Skip to content

Commit b27a24e

Browse files
committed
fix(core): track Antigravity execution metadata
1 parent 542ffc3 commit b27a24e

2 files changed

Lines changed: 78 additions & 13 deletions

File tree

packages/core/src/agy-request-metadata.test.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,22 @@ describe("agy request metadata", () => {
4343
expect(other.session.numericSessionId).toBe(first.session.numericSessionId)
4444
})
4545

46+
it("records a fresh execution ID only for known sessions", () => {
47+
const sessions = new AgyRequestSessionStore("file:///workspace", { now: () => 100 })
48+
const session = sessions.beginRequest("session-a").session
49+
50+
sessions.completeExecution("missing")
51+
expect(session.lastExecutionId).toBeUndefined()
52+
53+
sessions.completeExecution("session-a")
54+
const firstExecutionId = session.lastExecutionId
55+
expect(firstExecutionId).toMatch(/^[0-9a-f-]{36}$/)
56+
57+
sessions.completeExecution("session-a")
58+
expect(session.lastExecutionId).toMatch(/^[0-9a-f-]{36}$/)
59+
expect(session.lastExecutionId).not.toBe(firstExecutionId)
60+
})
61+
4662
it("creates stable session IDs with independently generated conversation and trajectory IDs", () => {
4763
expect(createAgyRequestSessionContext("file:///workspace", {
4864
conversationId: "conversation-id",
@@ -78,7 +94,7 @@ describe("agy request metadata", () => {
7894
])
7995
})
8096

81-
it("derives last_step_index from the number of content parts", () => {
97+
it("supports part- and content-based step counting", () => {
8298
const payload = {
8399
contents: [
84100
{ role: "user", parts: [{ text: "prompt" }] },
@@ -94,7 +110,8 @@ describe("agy request metadata", () => {
94110
}
95111

96112
expect(countAgyRequestSteps(payload)).toBe(4)
97-
expect(countAgyRequestSteps({ contents: [] })).toBe(1)
113+
expect(countAgyRequestSteps(payload, "contents")).toBe(3)
114+
expect(countAgyRequestSteps({ contents: [] }, "contents")).toBe(1)
98115
expect(countAgyRequestSteps({})).toBe(1)
99116
})
100117

@@ -132,6 +149,40 @@ describe("agy request metadata", () => {
132149
})
133150
})
134151

152+
it("matches the captured execution-aware step sequence", () => {
153+
const session = createAgyRequestSessionContext("", {
154+
conversationId: "conversation-id",
155+
trajectoryId: "trajectory-id",
156+
})
157+
const sequence = [
158+
{ contents: 1, step: 1, executionId: undefined },
159+
{ contents: 4, step: 5, executionId: "execution-1" },
160+
{ contents: 7, step: 8, executionId: "execution-2" },
161+
{ contents: 9, step: 10, executionId: "execution-2" },
162+
{ contents: 12, step: 13, executionId: "execution-3" },
163+
{ contents: 15, step: 16, executionId: "execution-4" },
164+
]
165+
166+
for (const [index, item] of sequence.entries()) {
167+
session.lastExecutionId = item.executionId
168+
const metadata = buildAgyAgentRequestMetadata(
169+
session,
170+
{ contents: Array.from({ length: item.contents }, () => ({ role: "user", parts: [] })) },
171+
"claude-opus-4-6-thinking",
172+
index + 1,
173+
{ stepCountMode: "contents" },
174+
)
175+
176+
expect(metadata.lastStepIndex).toBe(item.step)
177+
expect(metadata.requestId.endsWith(`/${item.step + 1}`)).toBe(true)
178+
if (item.executionId) {
179+
expect(metadata.labels.last_execution_id).toBe(item.executionId)
180+
} else {
181+
expect(metadata.labels).not.toHaveProperty("last_execution_id")
182+
}
183+
}
184+
})
185+
135186
it("matches every captured agy model enum fixture", () => {
136187
for (const fixture of MODEL_METADATA_FIXTURES) {
137188
for (const [model, expected] of Object.entries(fixture.models)) {

packages/core/src/agy-request-metadata.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export interface AgyRequestSessionContext {
3737
numericSessionId: string
3838
usedClaude?: boolean
3939
usedNonGeminiModel?: boolean
40+
lastExecutionId?: string
4041
}
4142

4243
interface StoredAgyRequestSession {
@@ -57,6 +58,7 @@ export interface AgyRequestScope {
5758
}
5859

5960
export interface AgyRequestLabels {
61+
last_execution_id?: string
6062
last_step_index: string
6163
model_enum?: string
6264
trajectory_id: string
@@ -72,6 +74,10 @@ export interface AgyAgentRequestMetadata {
7274
lastStepIndex: number
7375
}
7476

77+
export interface AgyAgentRequestMetadataOptions {
78+
stepCountMode?: "parts" | "contents"
79+
}
80+
7581
export function fnv1a64Signed(input: string): string {
7682
let hash = FNV1A_64_OFFSET_BASIS
7783
for (const byte of Buffer.from(input, "utf8")) {
@@ -133,6 +139,13 @@ export class AgyRequestSessionStore {
133139
return { session, timestamp }
134140
}
135141

142+
completeExecution(key: string): void {
143+
const stored = this.entries.get(key)
144+
if (stored) {
145+
stored.context.lastExecutionId = randomUUID()
146+
}
147+
}
148+
136149
has(key: string): boolean {
137150
return this.entries.has(key)
138151
}
@@ -194,21 +207,19 @@ export function orderAgyRequestPayloadInPlace(payload: Record<string, unknown>):
194207
Object.assign(payload, ordered)
195208
}
196209

197-
export function countAgyRequestSteps(payload: Record<string, unknown>): number {
210+
export function countAgyRequestSteps(
211+
payload: Record<string, unknown>,
212+
mode: "parts" | "contents" = "parts",
213+
): number {
198214
const contents = payload.contents
199-
if (!Array.isArray(contents)) {
200-
return 1
201-
}
215+
if (!Array.isArray(contents)) return 1
216+
if (mode === "contents") return Math.max(1, contents.length)
202217

203218
let partCount = 0
204219
for (const content of contents) {
205-
if (!content || typeof content !== "object" || Array.isArray(content)) {
206-
continue
207-
}
220+
if (!content || typeof content !== "object" || Array.isArray(content)) continue
208221
const parts = (content as Record<string, unknown>).parts
209-
if (Array.isArray(parts)) {
210-
partCount += parts.length
211-
}
222+
if (Array.isArray(parts)) partCount += parts.length
212223
}
213224
return Math.max(1, partCount)
214225
}
@@ -218,14 +229,17 @@ export function buildAgyAgentRequestMetadata(
218229
payload: Record<string, unknown>,
219230
model: string,
220231
timestamp = Date.now(),
232+
options: AgyAgentRequestMetadataOptions = {},
221233
): AgyAgentRequestMetadata {
222-
const lastStepIndex = countAgyRequestSteps(payload)
234+
const lastStepIndex = countAgyRequestSteps(payload, options.stepCountMode)
235+
+ (session.lastExecutionId ? 1 : 0)
223236
const isClaude = model.toLowerCase().startsWith("claude-")
224237
const isNonGemini = isClaude || model.toLowerCase().startsWith("gpt-")
225238
session.usedClaude = session.usedClaude === true || isClaude
226239
session.usedNonGeminiModel = session.usedNonGeminiModel === true || isNonGemini
227240
const modelEnum = getAgyModelEnum(model)
228241
const labels: AgyRequestLabels = {
242+
...(session.lastExecutionId ? { last_execution_id: session.lastExecutionId } : {}),
229243
last_step_index: String(lastStepIndex),
230244
...(modelEnum ? { model_enum: modelEnum } : {}),
231245
trajectory_id: session.trajectoryId,

0 commit comments

Comments
 (0)