refactor(core): rename system context to instructions (#35583)

This commit is contained in:
Kit Langton 2026-07-06 14:29:29 -04:00 committed by GitHub
commit 91f1815732
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 1482 additions and 1005 deletions

View file

@ -2350,12 +2350,12 @@
"summary": "Get session context"
}
},
"/api/session/{sessionID}/context-entry": {
"/api/session/{sessionID}/instructions/entries": {
"get": {
"tags": [
"sessions"
],
"operationId": "v2.session.context.entry.list",
"operationId": "v2.session.instructions.entry.list",
"parameters": [
{
"name": "sessionID",
@ -2383,7 +2383,7 @@
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SessionContextEntry.Info"
"$ref": "#/components/schemas/InstructionEntry.Info"
}
}
},
@ -2433,16 +2433,16 @@
}
}
},
"description": "List API-managed context entries attached to the session's system context.",
"summary": "List context entries"
"description": "List API-managed instruction entries attached to the session.",
"summary": "List instruction entries"
}
},
"/api/session/{sessionID}/context-entry/{key}": {
"/api/session/{sessionID}/instructions/entries/{key}": {
"put": {
"tags": [
"sessions"
],
"operationId": "v2.session.context.entry.put",
"operationId": "v2.session.instructions.entry.put",
"parameters": [
{
"name": "sessionID",
@ -2461,7 +2461,7 @@
"name": "key",
"in": "path",
"schema": {
"$ref": "#/components/schemas/SessionContextEntry.Key"
"$ref": "#/components/schemas/InstructionEntry.Key"
},
"required": true
}
@ -2509,8 +2509,8 @@
}
}
},
"description": "Attach or replace one durable context entry. The value is rendered into the session's system context; changes announce as updates at the next turn boundary.",
"summary": "Put context entry",
"description": "Attach or replace one durable instruction entry. Changes announce as updates at the next step boundary.",
"summary": "Put instruction entry",
"requestBody": {
"content": {
"application/json": {
@ -2533,7 +2533,7 @@
"tags": [
"sessions"
],
"operationId": "v2.session.context.entry.remove",
"operationId": "v2.session.instructions.entry.remove",
"parameters": [
{
"name": "sessionID",
@ -2552,7 +2552,7 @@
"name": "key",
"in": "path",
"schema": {
"$ref": "#/components/schemas/SessionContextEntry.Key"
"$ref": "#/components/schemas/InstructionEntry.Key"
},
"required": true
}
@ -2600,8 +2600,8 @@
}
}
},
"description": "Remove one context entry; the removal is announced to the model at the next turn boundary.",
"summary": "Remove context entry"
"description": "Remove one instruction entry; the removal is announced to the model at the next step boundary.",
"summary": "Remove instruction entry"
}
},
"/api/session/{sessionID}/log": {
@ -11692,7 +11692,7 @@
}
]
},
"SessionContextEntry.Key": {
"InstructionEntry.Key": {
"type": "string",
"allOf": [
{
@ -11701,11 +11701,11 @@
}
]
},
"SessionContextEntry.Info": {
"InstructionEntry.Info": {
"type": "object",
"properties": {
"key": {
"$ref": "#/components/schemas/SessionContextEntry.Key"
"$ref": "#/components/schemas/InstructionEntry.Key"
},
"value": {}
},
@ -12385,7 +12385,7 @@
],
"additionalProperties": false
},
"session.next.context.updated": {
"session.next.instructions.updated": {
"type": "object",
"properties": {
"id": {
@ -12402,7 +12402,7 @@
"type": {
"type": "string",
"enum": [
"session.next.context.updated"
"session.next.instructions.updated"
]
},
"durable": {
@ -14917,7 +14917,7 @@
"$ref": "#/components/schemas/session.next.prompt.admitted"
},
{
"$ref": "#/components/schemas/session.next.context.updated"
"$ref": "#/components/schemas/session.next.instructions.updated"
},
{
"$ref": "#/components/schemas/session.next.synthetic"
@ -26060,7 +26060,7 @@
"$ref": "#/components/schemas/session.next.execution.settled"
},
{
"$ref": "#/components/schemas/session.next.context.updated"
"$ref": "#/components/schemas/session.next.instructions.updated"
},
{
"$ref": "#/components/schemas/session.next.synthetic"

View file

@ -54,7 +54,9 @@ const json = (value: unknown, status = 200) =>
const singleOperation = (operation: Record<string, unknown>, method = "get"): Document => ({
openapi: "3.1.0",
paths: { "/test": { [method]: { operationId: "test", responses: { 200: { description: "Success" } }, ...operation } } },
paths: {
"/test": { [method]: { operationId: "test", responses: { 200: { description: "Success" } }, ...operation } },
},
})
describe("OpenAPI.fromSpec", () => {
@ -94,7 +96,12 @@ describe("OpenAPI.fromSpec", () => {
const remove = toolAt(api.tools, "users.remove")
expect(api.skipped).toEqual([])
if (!Tool.isDefinition(get) || !Tool.isDefinition(create) || !Tool.isDefinition(search) || !Tool.isDefinition(remove)) {
if (
!Tool.isDefinition(get) ||
!Tool.isDefinition(create) ||
!Tool.isDefinition(search) ||
!Tool.isDefinition(remove)
) {
throw new Error("happy-path fixture did not generate every operation")
}
expect(inputTypeScript(get)).toBe(
@ -110,7 +117,8 @@ describe("OpenAPI.fromSpec", () => {
const result = await Effect.runPromise(
CodeMode.make({ tools: { api: api.tools } })
.execute(`
.execute(
`
const user = await tools.api.users.get({
userId: "user-1",
include: ["profile", "permissions"],
@ -128,7 +136,8 @@ describe("OpenAPI.fromSpec", () => {
})
const removed = await tools.api.users.remove({ userId: "user-1" })
return { user, created, summary, removed }
`)
`,
)
.pipe(Effect.provide(client.layer)),
)
@ -196,11 +205,11 @@ describe("OpenAPI.fromSpec", () => {
if (!Tool.isDefinition(switchAgent)) throw new Error("v2.session.switchAgent was not generated")
expect(inputTypeScript(switchAgent)).toBe("{ sessionID: string; agent: string }")
const contextEntryPut = toolAt(result.tools, "v2.session.contextEntry.put")
expect(Tool.isDefinition(contextEntryPut)).toBe(true)
if (!Tool.isDefinition(contextEntryPut)) throw new Error("v2.session.contextEntry.put was not generated")
expect(inputTypeScript(contextEntryPut)).toBe("{ sessionID: string; key: string; value: unknown }")
expect(toolAt(result.tools, "v2_session_context_entry_put_2")).toBeUndefined()
const instructionPut = toolAt(result.tools, "v2.session.instructions.entry.put")
expect(Tool.isDefinition(instructionPut)).toBe(true)
if (!Tool.isDefinition(instructionPut)) throw new Error("v2.session.instructions.entry.put was not generated")
expect(inputTypeScript(instructionPut)).toBe("{ sessionID: string; key: string; value: unknown }")
expect(toolAt(result.tools, "v2_session_instructions_entry_put_2")).toBeUndefined()
expect(toolAt(result.tools, "v2.pty.connect")).toBeUndefined()
expect(toolAt(result.tools, "v2.session.log")).toBeUndefined()
expect(toolAt(result.tools, "v2.event.subscribe")).toBeUndefined()
@ -481,9 +490,9 @@ describe("OpenAPI.fromSpec", () => {
expect(url.searchParams.get("nullable")).toBe("null")
expect(url.searchParams.get("constructor")).toBe("safe")
expect(client.requests[0]!.headers.meta).toBe("a=b,c=d")
await expect(
Effect.runPromise(tool.run({ keys: [undefined] }).pipe(Effect.provide(client.layer))),
).rejects.toThrow("unsupported nested value")
await expect(Effect.runPromise(tool.run({ keys: [undefined] }).pipe(Effect.provide(client.layer)))).rejects.toThrow(
"unsupported nested value",
)
})
test("skips unsupported parameter encodings and malformed security", () => {
@ -585,7 +594,10 @@ describe("OpenAPI.fromSpec", () => {
test("applies authentication carriers without prototype or collision loss", async () => {
const client = recordingClient(() => json({ ok: true }))
const authenticated = (security: ReadonlyArray<Record<string, ReadonlyArray<string>>>, schemes: Record<string, unknown>) =>
const authenticated = (
security: ReadonlyArray<Record<string, ReadonlyArray<string>>>,
schemes: Record<string, unknown>,
) =>
OpenAPI.fromSpec({
baseUrl,
spec: { ...singleOperation({}), security, components: { securitySchemes: schemes } },
@ -601,13 +613,10 @@ describe("OpenAPI.fromSpec", () => {
expect(new URL(client.requests[0]!.url).searchParams.get("__proto__")).toBe("secret")
const duplicate = toolAt(
authenticated(
[{ first: [], second: [] }],
{
first: { type: "apiKey", in: "header", name: "x-key" },
second: { type: "apiKey", in: "header", name: "x-key" },
},
).tools,
authenticated([{ first: [], second: [] }], {
first: { type: "apiKey", in: "header", name: "x-key" },
second: { type: "apiKey", in: "header", name: "x-key" },
}).tools,
"test",
)
if (!Tool.isDefinition(duplicate)) throw new Error("duplicate auth tool was not generated")
@ -632,8 +641,7 @@ describe("OpenAPI.fromSpec", () => {
},
},
auth: {
resolve: ({ name }) =>
Effect.succeed(name === "bearer" ? { type: "bearer", token: "secret" } : undefined),
resolve: ({ name }) => Effect.succeed(name === "bearer" ? { type: "bearer", token: "secret" } : undefined),
},
})
const alternativeTool = toolAt(alternative.tools, "test")
@ -765,9 +773,7 @@ describe("OpenAPI.fromSpec", () => {
const oversized = recordingClient(
() => new Response(null, { headers: { "content-length": String(50 * 1024 * 1024 + 1) } }),
)
const malformed = recordingClient(
() => new Response("{", { headers: { "content-type": "application/json" } }),
)
const malformed = recordingClient(() => new Response("{", { headers: { "content-type": "application/json" } }))
const chunked = recordingClient(() => new Response(new Uint8Array(50 * 1024 * 1024 + 1)))
await expect(Effect.runPromise(tool.run({}).pipe(Effect.provide(oversized.layer)))).rejects.toThrow(