diff --git a/src/agent/ag-ui/encoder.test.ts b/src/agent/ag-ui/encoder.test.ts index 618a953420..672cbc78ad 100644 --- a/src/agent/ag-ui/encoder.test.ts +++ b/src/agent/ag-ui/encoder.test.ts @@ -273,9 +273,10 @@ describe("agent/ag-ui-encoder", () => { data: { runStartedAtUtc: "2026-07-19T07:30:00.000Z" }, }), [{ - event: "Custom", + event: "RuntimeEventRecorded", payload: { - name: "veryfront.runtime_context", + runtime: "veryfront", + kind: "runtime_context", value: { runStartedAtUtc: "2026-07-19T07:30:00.000Z" }, }, }], diff --git a/src/agent/ag-ui/native-run-events.test.ts b/src/agent/ag-ui/native-run-events.test.ts index 0c86da0f66..377acd4271 100644 --- a/src/agent/ag-ui/native-run-events.test.ts +++ b/src/agent/ag-ui/native-run-events.test.ts @@ -1,5 +1,5 @@ import "#veryfront/schemas/_test-setup.ts"; -import { assertEquals, assertExists } from "#veryfront/testing/assert.ts"; +import { assertEquals, assertExists, assertThrows } from "#veryfront/testing/assert.ts"; import { describe, it } from "#veryfront/testing/bdd.ts"; import { buildChildRunStatusChangedEvent, @@ -7,6 +7,7 @@ import { buildFileAttachedEvent, buildInputRequestLifecycleEvent, buildNativeRunEventFrame, + buildRuntimeEventRecordedEvent, buildToolCallStatusChangedEvent, buildUrlCitedEvent, isNativeRunEventName, @@ -57,6 +58,7 @@ describe("agent/ag-ui-native-run-events", () => { "UrlCited", "DocumentCited", "FileAttached", + "RuntimeEventRecorded", ]); assertEquals( Object.values(nativeRunEventTypes), @@ -69,7 +71,7 @@ describe("agent/ag-ui-native-run-events", () => { ); }); - it("accepts the six legacy custom names and rejects everything else", () => { + it("accepts the seven legacy custom names and rejects everything else", () => { for ( const name of [ "tool-call-status", @@ -78,6 +80,7 @@ describe("agent/ag-ui-native-run-events", () => { "source-url", "source-document", "file", + "veryfront.runtime_context", ] ) { assertEquals(isNativeRunEventName(name), true, name); @@ -423,6 +426,55 @@ describe("agent/ag-ui-native-run-events", () => { } }); + it("builds both emission shapes for a runtime event recorded", () => { + const runtimeContext = { + currentTimeUtc: "2026-09-09T00:00:00.000Z", + currentDateUtc: "2026-09-09", + runStartedAtUtc: "2026-09-09T00:00:00.000Z", + }; + assertEquals( + buildRuntimeEventRecordedEvent({ + runtime: "veryfront", + kind: "runtime_context", + value: runtimeContext, + }), + { + live: { + event: "RuntimeEventRecorded", + payload: { runtime: "veryfront", kind: "runtime_context", value: runtimeContext }, + }, + durable: { + runtime: "veryfront", + kind: "runtime_context", + value: runtimeContext, + type: "RUNTIME_EVENT_RECORDED", + }, + }, + ); + }); + + it("rejects a runtime event recorded with an invalid runtime, kind, or value", () => { + // `runtime`/`kind` are typed `string`, which accepts empty text, and + // `value` is typed `unknown`, which accepts `undefined`; but the API + // catalog's RUNTIME_EVENT_RECORDED variant requires non-empty strings + // and a JSON value. This builder has callers outside this module's own + // dispatcher (e.g. a future codex-runtime producer), so it must reject + // those on its own rather than trusting the caller's static types. + assertThrows(() => + buildRuntimeEventRecordedEvent({ runtime: "", kind: "runtime_context", value: {} }) + ); + assertThrows(() => + buildRuntimeEventRecordedEvent({ runtime: "veryfront", kind: "", value: {} }) + ); + assertThrows(() => + buildRuntimeEventRecordedEvent({ + runtime: "veryfront", + kind: "runtime_context", + value: undefined, + }) + ); + }); + it("routes every legacy name through the dispatcher", () => { assertEquals( buildNativeRunEventFrame({ @@ -461,6 +513,40 @@ describe("agent/ag-ui-native-run-events", () => { })?.live.event, "FileAttached", ); + assertEquals( + buildNativeRunEventFrame({ + name: "veryfront.runtime_context", + value: { + currentTimeUtc: "2026-09-09T00:00:00.000Z", + currentDateUtc: "2026-09-09", + runStartedAtUtc: "2026-09-09T00:00:00.000Z", + }, + }), + { + live: { + event: "RuntimeEventRecorded", + payload: { + runtime: "veryfront", + kind: "runtime_context", + value: { + currentTimeUtc: "2026-09-09T00:00:00.000Z", + currentDateUtc: "2026-09-09", + runStartedAtUtc: "2026-09-09T00:00:00.000Z", + }, + }, + }, + durable: { + runtime: "veryfront", + kind: "runtime_context", + value: { + currentTimeUtc: "2026-09-09T00:00:00.000Z", + currentDateUtc: "2026-09-09", + runStartedAtUtc: "2026-09-09T00:00:00.000Z", + }, + type: "RUNTIME_EVENT_RECORDED", + }, + }, + ); }); it("returns null for a name or value that has no native frame", () => { @@ -494,5 +580,10 @@ describe("agent/ag-ui-native-run-events", () => { null, "a file-change value is FILES_CHANGED on the legacy path, never FileAttached", ); + assertEquals( + buildNativeRunEventFrame({ name: "veryfront.runtime_context", value: null }), + null, + "a non-record value cannot become a RuntimeEventRecorded payload", + ); }); }); diff --git a/src/agent/ag-ui/native-run-events.ts b/src/agent/ag-ui/native-run-events.ts index e6b630cf99..3acd24f6d7 100644 --- a/src/agent/ag-ui/native-run-events.ts +++ b/src/agent/ag-ui/native-run-events.ts @@ -1,7 +1,9 @@ +import { getJsonValueSchema, getNonEmptyStringSchema } from "#veryfront/schemas/index.ts"; + /** * Native run event vocabulary shared by every emission path. * - * Veryfront Code used to wrap these seven occurrences in an AG-UI `Custom` + * Veryfront Code used to wrap these eight occurrences in an AG-UI `Custom` * frame and let the API translate the custom name back into a type. The API * now accepts the native names, so this module owns the list once: the wire * name a live SSE frame carries, the stored type a durable record carries, and @@ -45,6 +47,11 @@ export const NATIVE_RUN_EVENTS = [ legacyCustomName: "source-document", }, { wireName: "FileAttached", storedType: "FILE_ATTACHED", legacyCustomName: "file" }, + { + wireName: "RuntimeEventRecorded", + storedType: "RUNTIME_EVENT_RECORDED", + legacyCustomName: "veryfront.runtime_context", + }, ] as const satisfies readonly NativeRunEventDefinition[]; type NativeRunEventEntry = (typeof NATIVE_RUN_EVENTS)[number]; @@ -78,6 +85,7 @@ export const nativeRunEventTypes = { urlCited: "URL_CITED", documentCited: "DOCUMENT_CITED", fileAttached: "FILE_ATTACHED", + runtimeEventRecorded: "RUNTIME_EVENT_RECORDED", } as const; /** Stored type for each native wire name, for SSE readers. */ @@ -136,6 +144,7 @@ const CHILD_RUN_STATUS_CHANGED = NATIVE_RUN_EVENTS[3]; const URL_CITED = NATIVE_RUN_EVENTS[4]; const DOCUMENT_CITED = NATIVE_RUN_EVENTS[5]; const FILE_ATTACHED = NATIVE_RUN_EVENTS[6]; +const RUNTIME_EVENT_RECORDED = NATIVE_RUN_EVENTS[7]; // Keep application fields from overriding the native type or transport timing // when an open custom value becomes a flat native payload. @@ -330,6 +339,39 @@ export function buildFileAttachedEvent(source: Record): NativeR return toFrame(FILE_ATTACHED, payload); } +/** + * Fields the API catalog's `RUNTIME_EVENT_RECORDED` variant requires: + * `runtime` and `kind` non-empty strings, `value` any JSON value but never + * `undefined`. This is the catch-all diagnostics type for a runtime-native + * event with no AG-UI equivalent (the API catalog's own description + * mentions codex thread/session events as a future producer), so unlike the + * other seven builders this one does not derive its shape from a fixed + * source chunk -- the caller supplies the catalog fields directly. + */ +export interface RuntimeEventRecordedInput { + runtime: string; + kind: string; + value: unknown; +} + +/** + * Build the runtime event recorded frames. + * + * Validates against the catalog's own constraints -- `runtime`/`kind` non-empty + * strings, `value` a bounded JSON value -- rather than trusting the caller's + * static `string`/`unknown` types, since this builder (unlike the other seven) + * has callers outside this module's own dispatcher that supply the catalog + * fields directly. + */ +export function buildRuntimeEventRecordedEvent( + input: RuntimeEventRecordedInput, +): NativeRunEventFrame { + const runtime = getNonEmptyStringSchema().parse(input.runtime); + const kind = getNonEmptyStringSchema().parse(input.kind); + const value = getJsonValueSchema().parse(input.value); + return toFrame(RUNTIME_EVENT_RECORDED, { runtime, kind, value }); +} + /** Routing input for one custom event name and its value. */ export interface NativeRunEventRoutingInput { name: string; @@ -389,6 +431,18 @@ export function buildNativeRunEventFrame( readString(record.mediaType) ? buildFileAttachedEvent(record) : null; + case "veryfront.runtime_context": + // The one producer (runtime/index.ts's #streamWithinTurn) always sends + // the whole AgentRunRuntimeContext snapshot as the chunk's `data`, so + // `record` (already guarded non-null above) is the payload's `value` + // field wholesale; `runtime`/`kind` are this producer's own constants, + // not read off the value, since this legacy name only ever carried the + // context object itself. + return buildRuntimeEventRecordedEvent({ + runtime: "veryfront", + kind: "runtime_context", + value: record, + }); default: return null; } diff --git a/src/agent/conversation/legacy-run-read-adapter.test.ts b/src/agent/conversation/legacy-run-read-adapter.test.ts index 881d84dcdb..575fe66a0a 100644 --- a/src/agent/conversation/legacy-run-read-adapter.test.ts +++ b/src/agent/conversation/legacy-run-read-adapter.test.ts @@ -18,6 +18,7 @@ import { buildDocumentCitedEvent, buildFileAttachedEvent, buildInputRequestLifecycleEvent, + buildRuntimeEventRecordedEvent, buildToolCallStatusChangedEvent, buildUrlCitedEvent, NATIVE_RUN_EVENTS, @@ -1569,6 +1570,27 @@ describe("conversation run lifecycle read adapter", () => { value: { type: "file", mediaType: "text/plain", path: "notes.txt" }, }, }, + { + description: "RUNTIME_EVENT_RECORDED", + native: buildRuntimeEventRecordedEvent({ + runtime: "veryfront", + kind: "runtime_context", + value: { + currentTimeUtc: "2026-09-09T00:00:00.000Z", + currentDateUtc: "2026-09-09", + runStartedAtUtc: "2026-09-09T00:00:00.000Z", + }, + }).durable, + customTwin: { + type: "CUSTOM", + name: "veryfront.runtime_context", + value: { + currentTimeUtc: "2026-09-09T00:00:00.000Z", + currentDateUtc: "2026-09-09", + runStartedAtUtc: "2026-09-09T00:00:00.000Z", + }, + }, + }, ]; it("covers every native type with a legacy reconstruction case", () => { @@ -1597,6 +1619,28 @@ describe("conversation run lifecycle read adapter", () => { assertEquals(customFramesFor(1, native), customFramesFor(1, customTwin)); }); + it( + "does not reconstruct a non-veryfront RUNTIME_EVENT_RECORDED as the runtime_context twin", + () => { + // RUNTIME_EVENT_RECORDED is the API catalog's generic diagnostics + // shape and has other producers (e.g. the codex runtime) with other + // runtime/kind pairs. Only the exact veryfront/runtime_context pair + // may unwrap to the legacy `veryfront.runtime_context` CUSTOM twin; + // every other pair must surface as its own generic custom record + // instead of a false runtime_context. + const native = buildRuntimeEventRecordedEvent({ + runtime: "codex", + kind: "stderr", + value: { line: "boom" }, + }).durable; + + assertEquals( + customFramesFor(2, { ...native, ...v2Envelope(1, "codex-runtime-event") }), + [{ type: "custom", name: "codex.stderr", data: { line: "boom" } }], + ); + }, + ); + it("reads a TOOL_CALL_STATUS_CHANGED durable record as its CUSTOM twin on the version 1 reader", () => { const { native, customTwin } = cases.find((entry) => entry.description === "TOOL_CALL_STATUS_CHANGED" diff --git a/src/agent/conversation/legacy-run-read-adapter.ts b/src/agent/conversation/legacy-run-read-adapter.ts index 0284ba8504..b1827d86af 100644 --- a/src/agent/conversation/legacy-run-read-adapter.ts +++ b/src/agent/conversation/legacy-run-read-adapter.ts @@ -56,7 +56,7 @@ const DURABLE_ENVELOPE_KEYS = [ */ function readNativeAsLegacyCustom( event: Record, -): { name: string; value: Record } | null { +): { name: string; value: unknown } | null { const definition = typeof event.type === "string" ? NATIVE_STORED_TYPE_TO_LEGACY.get(event.type) : undefined; @@ -65,6 +65,22 @@ function readNativeAsLegacyCustom( for (const key of DURABLE_ENVELOPE_KEYS) { delete value[key]; } + if (definition.storedType === "RUNTIME_EVENT_RECORDED") { + // The legacy `veryfront.runtime_context` CUSTOM twin carried the bare + // AgentRunRuntimeContext object as its value, produced only for the + // veryfront/runtime_context pair; the native payload wraps that same + // object as `{ runtime, kind, value }` to match the API catalog's + // generic diagnostics shape (RUNTIME_EVENT_RECORDED has other producers + // with other runtimes/kinds, e.g. the codex runtime, so the wrapper is + // required there). Only that exact pair unwraps to the legacy twin here, + // the same way the citation/file case below restores a field the native + // payload dropped; any other runtime/kind becomes its own generic custom + // record instead of a false veryfront.runtime_context. + if (value.runtime === "veryfront" && value.kind === "runtime_context") { + return { name: definition.legacyCustomName, value: value.value }; + } + return { name: `${String(value.runtime)}.${String(value.kind)}`, value: value.value }; + } if ( definition.storedType === "INPUT_REQUEST_CREATED" || definition.storedType === "INPUT_REQUEST_UPDATED" diff --git a/src/agent/conversation/run-events.test.ts b/src/agent/conversation/run-events.test.ts index 825ee63004..3a24a14705 100644 --- a/src/agent/conversation/run-events.test.ts +++ b/src/agent/conversation/run-events.test.ts @@ -277,6 +277,32 @@ describe("agent/conversation-run-events", () => { ); }); + it("encodes native runtime event chunks as native durable records", () => { + const encoder = new ConversationRunEventEncoder(); + assertEquals( + encoder.encode({ + type: "data-veryfront.runtime_context", + data: { + currentTimeUtc: "2026-09-09T00:00:00.000Z", + currentDateUtc: "2026-09-09", + runStartedAtUtc: "2026-09-09T00:00:00.000Z", + }, + }), + [ + { + type: conversationRunEventTypes.runtimeEventRecorded, + runtime: "veryfront", + kind: "runtime_context", + value: { + currentTimeUtc: "2026-09-09T00:00:00.000Z", + currentDateUtc: "2026-09-09", + runStartedAtUtc: "2026-09-09T00:00:00.000Z", + }, + }, + ], + ); + }); + it("keeps state chunks and unknown data names custom", () => { const encoder = new ConversationRunEventEncoder(); assertEquals( diff --git a/src/chat/ag-ui.test.ts b/src/chat/ag-ui.test.ts index d55ccd538f..e3f59c748c 100644 --- a/src/chat/ag-ui.test.ts +++ b/src/chat/ag-ui.test.ts @@ -749,6 +749,8 @@ describe("chat/ag-ui", () => { 'event: InputRequestUpdated\ndata: {"inputRequest":{"id":"req-1"}}\n\n', 'event: ChildRunStatusChanged\ndata: {"toolCallId":"t","childRunId":"r",' + '"status":"running"}\n\n', + 'event: RuntimeEventRecorded\ndata: {"runtime":"veryfront","kind":"runtime_context",' + + '"value":{"currentTimeUtc":"2026-09-09T00:00:00.000Z"}}\n\n', ].join(""); assertEquals(decodeAgUiSseChunk(state, frames).events.flatMap((entry) => entry.chatEvents), [ @@ -771,6 +773,25 @@ describe("chat/ag-ui", () => { type: "data-veryfront.invoke_agent.lifecycle", data: { toolCallId: "t", childRunId: "r", status: "running" }, }, + { + type: "data-veryfront.runtime_context", + data: { currentTimeUtc: "2026-09-09T00:00:00.000Z" }, + }, + ]); + }); + + it("does not mistake a non-veryfront runtime event for runtime context", () => { + // RUNTIME_EVENT_RECORDED is the API catalog's generic diagnostics shape + // and accepts any non-empty runtime/kind pair (e.g. a codex thread or + // session event) -- only the exact veryfront/runtime_context pair may + // become the legacy `data-veryfront.runtime_context` chunk. + ensureTestSchemaValidator(); + const state = createAgUiChatEventDecoderState({ validationMode: "strict" }); + const frames = 'event: RuntimeEventRecorded\ndata: {"runtime":"codex","kind":"stderr",' + + '"value":{"line":"boom"}}\n\n'; + + assertEquals(decodeAgUiSseChunk(state, frames).events.flatMap((entry) => entry.chatEvents), [ + { type: "data-codex.stderr", data: { line: "boom" } }, ]); }); @@ -1162,10 +1183,10 @@ describe("chat/ag-ui", () => { it("decodes every native run event wire name NATIVE_RUN_EVENTS defines", () => { ensureTestSchemaValidator(); // NATIVE_RUN_EVENTS (src/agent/ag-ui/native-run-events.ts) is the - // producer's source of truth for the seven native wire names; this + // producer's source of truth for the eight native wire names; this // decoder keeps its own copy in AG_UI_WIRE_EVENT_NAMES rather than // importing that module, to keep the agent tree off the client bundle - // graph. Nothing else catches the two lists drifting apart: an eighth + // graph. Nothing else catches the two lists drifting apart: a ninth // native type added there would be silently dropped here, which is the // exact failure P9 exists to prevent. for (const { wireName } of NATIVE_RUN_EVENTS) { @@ -1294,9 +1315,9 @@ describe("chat/ag-ui without a registered SchemaValidator", () => { }); it("decodes native run event frames through the hand-rolled validator too", () => { - // The seven native arms in isValidAgUiPayload only run on this path, so + // The eight native arms in isValidAgUiPayload only run on this path, so // the zod-backed coverage above does not exercise them at all. Reuse the - // same seven-frame string the schema-validated twin-equality test uses. + // same eight-frame string the schema-validated twin-equality test uses. unregister("SchemaValidator"); try { const state = createAgUiChatEventDecoderState({ validationMode: "strict" }); @@ -1312,6 +1333,8 @@ describe("chat/ag-ui without a registered SchemaValidator", () => { 'event: InputRequestUpdated\ndata: {"inputRequest":{"id":"req-1"}}\n\n', 'event: ChildRunStatusChanged\ndata: {"toolCallId":"t","childRunId":"r",' + '"status":"running"}\n\n', + 'event: RuntimeEventRecorded\ndata: {"runtime":"veryfront","kind":"runtime_context",' + + '"value":{"currentTimeUtc":"2026-09-09T00:00:00.000Z"}}\n\n', ].join(""); assertEquals( @@ -1341,6 +1364,10 @@ describe("chat/ag-ui without a registered SchemaValidator", () => { type: "data-veryfront.invoke_agent.lifecycle", data: { toolCallId: "t", childRunId: "r", status: "running" }, }, + { + type: "data-veryfront.runtime_context", + data: { currentTimeUtc: "2026-09-09T00:00:00.000Z" }, + }, ], "the hand-rolled validator must decode native frames the same way the zod schema does", ); diff --git a/src/chat/ag-ui.ts b/src/chat/ag-ui.ts index c9e12dd9c4..ad1280cb02 100644 --- a/src/chat/ag-ui.ts +++ b/src/chat/ag-ui.ts @@ -129,6 +129,7 @@ const AG_UI_WIRE_EVENT_NAMES = [ "UrlCited", "DocumentCited", "FileAttached", + "RuntimeEventRecorded", "RunFinished", "RunError", ] as const; @@ -594,6 +595,14 @@ export const getAgUiWireEventSchema = defineSchema((v) => filename: v.unknown().optional(), }).passthrough(), }), + v.object({ + eventName: v.literal("RuntimeEventRecorded"), + payload: v.object({ + runtime: v.string().min(1), + kind: v.string().min(1), + value: v.unknown(), + }).passthrough(), + }), v.object({ eventName: v.literal("RunFinished"), payload: v.object({ metadata: getAgUiRunFinishedMetadataSchema().optional() }), @@ -725,6 +734,10 @@ function isValidAgUiPayload( case "FileAttached": return hasStringField(payload, "mediaType"); + case "RuntimeEventRecorded": + return hasStringField(payload, "runtime") && hasStringField(payload, "kind") && + "value" in payload; + case "ToolCallResult": return hasStringField(payload, "toolCallId") && (payload.messageId === undefined || hasStringField(payload, "messageId")) && @@ -1092,6 +1105,34 @@ function mapWireEventToChatEvents( }]; } + case "RuntimeEventRecorded": { + const { runtime, kind, value } = wireEvent.payload; + if (runtime === "veryfront" && kind === "runtime_context") { + // The Custom twin's data chunk carried the bare runtime context value, + // not the API's { runtime, kind, value } wrapper -- veryfront-code's + // one producer today (runtime/index.ts's #streamWithinTurn) always + // sent the whole snapshot object as `data`, so unwrap the same way + // legacy-run-read-adapter.ts's durable twin does. + return [{ + type: "data-veryfront.runtime_context", + data: value, + }]; + } + + // RUNTIME_EVENT_RECORDED is the API catalog's generic diagnostics + // shape and accepts any non-empty runtime/kind pair (the catalog's own + // description mentions codex thread/session events as a future + // producer), so any pair other than veryfront/runtime_context is not + // this repo's legacy twin. Expose it as its own generic chat chunk the + // same way the "Custom" case above falls back to `data-${name}` for an + // unrecognized custom name, instead of mislabeling it as Veryfront's + // runtime context. + return [{ + type: `data-${runtime}.${kind}`, + data: value, + }]; + } + case "RunFinished": state.toolCalls.clear(); state.activeFallbackReasoningPartId = null; diff --git a/src/internal-agents/ag-ui-sse.test.ts b/src/internal-agents/ag-ui-sse.test.ts index 95ee6ed811..a0f7ff0fd3 100644 --- a/src/internal-agents/ag-ui-sse.test.ts +++ b/src/internal-agents/ag-ui-sse.test.ts @@ -388,6 +388,27 @@ describe("internal-agents/ag-ui-sse", () => { ); }); + it("declares RuntimeEventRecorded in the payload allow-list with extra fields intact", () => { + // The eighth native wire name (see native-run-events.ts's P12 decision): + // an API-catalog diagnostics record with no fixed shape beyond + // runtime/kind/value, so this allow-list entry is `.passthrough()`-ed + // like its seven siblings rather than declaring extra fields. + const payload = new TextDecoder().decode( + formatAgUiEvent("RuntimeEventRecorded", { + runtime: "veryfront", + kind: "runtime_context", + value: { currentTimeUtc: "2026-09-09T00:00:00.000Z" }, + emittedAt: 8, + }), + ); + + assertEquals( + payload, + 'event: RuntimeEventRecorded\ndata: {"runtime":"veryfront","kind":"runtime_context",' + + '"value":{"currentTimeUtc":"2026-09-09T00:00:00.000Z"},"emittedAt":8}\n\n', + ); + }); + it("declares the seven native run event names in the payload allow-list with extra fields intact", () => { // M3: the seven native wire names used to have no schema entry, so they // took formatAgUiEvent's unvalidated pass-through branch instead of this diff --git a/src/internal-agents/ag-ui-sse.ts b/src/internal-agents/ag-ui-sse.ts index 705c832e6b..abfa33a60e 100644 --- a/src/internal-agents/ag-ui-sse.ts +++ b/src/internal-agents/ag-ui-sse.ts @@ -176,6 +176,15 @@ function buildAgUiEventPayloadSchemas(): Record): Uint8Array { const eventNameMatch = AG_UI_EVENT_NAME_PATTERN.exec(event); diff --git a/tests/fixtures/contracts/native-run-events.json b/tests/fixtures/contracts/native-run-events.json index 52b4fccc8e..498080f7a3 100644 --- a/tests/fixtures/contracts/native-run-events.json +++ b/tests/fixtures/contracts/native-run-events.json @@ -35,7 +35,13 @@ "requestedResponderType": "human", "title": "Choose a deployment target", "description": null, - "fields": [{ "type": "confirm", "name": "confirmed", "label": "Confirm?" }], + "fields": [ + { + "type": "confirm", + "name": "confirmed", + "label": "Confirm?" + } + ], "recommendations": null, "metadata": null, "createdAt": "2026-09-09T00:00:00.000Z", @@ -58,7 +64,13 @@ "requestedResponderType": "human", "title": "Choose a deployment target", "description": null, - "fields": [{ "type": "confirm", "name": "confirmed", "label": "Confirm?" }], + "fields": [ + { + "type": "confirm", + "name": "confirmed", + "label": "Confirm?" + } + ], "recommendations": null, "metadata": null, "createdAt": "2026-09-09T00:00:00.000Z", @@ -87,7 +99,13 @@ "requestedResponderType": "human", "title": "Choose a deployment target", "description": null, - "fields": [{ "type": "confirm", "name": "confirmed", "label": "Confirm?" }], + "fields": [ + { + "type": "confirm", + "name": "confirmed", + "label": "Confirm?" + } + ], "recommendations": null, "metadata": null, "createdAt": "2026-09-09T00:00:00.000Z", @@ -110,7 +128,13 @@ "requestedResponderType": "human", "title": "Choose a deployment target", "description": null, - "fields": [{ "type": "confirm", "name": "confirmed", "label": "Confirm?" }], + "fields": [ + { + "type": "confirm", + "name": "confirmed", + "label": "Confirm?" + } + ], "recommendations": null, "metadata": null, "createdAt": "2026-09-09T00:00:00.000Z", @@ -212,5 +236,31 @@ "filename": "report.pdf", "type": "FILE_ATTACHED" } + }, + { + "storedType": "RUNTIME_EVENT_RECORDED", + "legacyCustomName": "veryfront.runtime_context", + "live": { + "event": "RuntimeEventRecorded", + "payload": { + "runtime": "veryfront", + "kind": "runtime_context", + "value": { + "currentTimeUtc": "2026-09-09T00:00:00.000Z", + "currentDateUtc": "2026-09-09", + "runStartedAtUtc": "2026-09-09T00:00:00.000Z" + } + } + }, + "durable": { + "runtime": "veryfront", + "kind": "runtime_context", + "value": { + "currentTimeUtc": "2026-09-09T00:00:00.000Z", + "currentDateUtc": "2026-09-09", + "runStartedAtUtc": "2026-09-09T00:00:00.000Z" + }, + "type": "RUNTIME_EVENT_RECORDED" + } } ] diff --git a/tests/integration/semantic-unit-boundary/src/agent/ag-ui/native-run-events-contract.test.ts b/tests/integration/semantic-unit-boundary/src/agent/ag-ui/native-run-events-contract.test.ts index df5ad1d795..c4ecba5f36 100644 --- a/tests/integration/semantic-unit-boundary/src/agent/ag-ui/native-run-events-contract.test.ts +++ b/tests/integration/semantic-unit-boundary/src/agent/ag-ui/native-run-events-contract.test.ts @@ -9,6 +9,7 @@ import { buildDocumentCitedEvent, buildFileAttachedEvent, buildInputRequestLifecycleEvent, + buildRuntimeEventRecordedEvent, buildToolCallStatusChangedEvent, buildUrlCitedEvent, NATIVE_RUN_EVENTS, @@ -25,7 +26,7 @@ const FIXTURE_URL = new URL( // The veryfront-api copy of this file pins the same digest, which is what makes // the two repositories byte-identical rather than merely similar. const NATIVE_RUN_EVENTS_FIXTURE_SHA256 = - "a4e3e51168fd6d51d47b5baeb0579be892d4189c9f1231f34853b99744e41287"; + "864384ebc620f2f0894390b45bd31635f757a230992da92351986cb0d0e53f94"; const INPUT_REQUEST = { id: "8f2f1f52-0f2a-4a3a-9b0f-0f2a4a3a9b0f", @@ -97,6 +98,15 @@ function buildSamples(): NativeRunEventFrame[] { mediaType: "application/pdf", filename: "report.pdf", }), + buildRuntimeEventRecordedEvent({ + runtime: "veryfront", + kind: "runtime_context", + value: { + currentTimeUtc: "2026-09-09T00:00:00.000Z", + currentDateUtc: "2026-09-09", + runStartedAtUtc: "2026-09-09T00:00:00.000Z", + }, + }), ]; }