@hachej/boring-agent 0.1.111 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +262 -51
  3. package/dist/agents.d.ts +75 -0
  4. package/dist/agents.js +91 -0
  5. package/dist/approval.d.ts +71 -0
  6. package/dist/approval.js +126 -0
  7. package/dist/artifacts.d.ts +53 -0
  8. package/dist/artifacts.js +129 -0
  9. package/dist/ask-user.d.ts +40 -0
  10. package/dist/ask-user.js +134 -0
  11. package/dist/attachment.d.ts +5 -0
  12. package/dist/attachment.js +4 -0
  13. package/dist/browser-task.d.ts +32 -0
  14. package/dist/browser-task.js +119 -0
  15. package/dist/chat-transport.d.ts +80 -0
  16. package/dist/chat-transport.js +306 -0
  17. package/dist/contracts.d.ts +40 -0
  18. package/dist/contracts.js +1 -0
  19. package/dist/conversations.d.ts +136 -0
  20. package/dist/conversations.js +366 -0
  21. package/dist/definitions.d.ts +37 -0
  22. package/dist/definitions.js +99 -0
  23. package/dist/delivery-input.d.ts +35 -0
  24. package/dist/delivery-input.js +56 -0
  25. package/dist/delivery.d.ts +33 -0
  26. package/dist/delivery.js +154 -0
  27. package/dist/durable-wait.d.ts +23 -0
  28. package/dist/durable-wait.js +79 -0
  29. package/dist/file-convert.d.ts +46 -0
  30. package/dist/file-convert.js +159 -0
  31. package/dist/file-guard.d.ts +50 -0
  32. package/dist/file-guard.js +177 -0
  33. package/dist/file-types.d.ts +17 -0
  34. package/dist/file-types.js +95 -0
  35. package/dist/gateway-provider.d.ts +24 -0
  36. package/dist/gateway-provider.js +42 -0
  37. package/dist/git.d.ts +34 -0
  38. package/dist/git.js +79 -0
  39. package/dist/harness-pool.d.ts +38 -0
  40. package/dist/harness-pool.js +134 -0
  41. package/dist/index.d.ts +4 -0
  42. package/dist/index.js +1 -0
  43. package/dist/json-schema-tool.d.ts +25 -0
  44. package/dist/json-schema-tool.js +19 -0
  45. package/dist/memory/optchat-core.d.ts +118 -0
  46. package/dist/memory/optchat-core.js +317 -0
  47. package/dist/memory/optchat.d.ts +156 -0
  48. package/dist/memory/optchat.js +432 -0
  49. package/dist/mentions.d.ts +56 -0
  50. package/dist/mentions.js +140 -0
  51. package/dist/metering.d.ts +183 -0
  52. package/dist/metering.js +443 -0
  53. package/dist/model-gateway.d.ts +86 -0
  54. package/dist/model-gateway.js +241 -0
  55. package/dist/native-version.d.ts +9 -0
  56. package/dist/native-version.js +7 -0
  57. package/dist/native.d.ts +4 -0
  58. package/dist/native.js +1 -0
  59. package/dist/presentation.d.ts +17 -0
  60. package/dist/presentation.js +39 -0
  61. package/dist/projection-format.d.ts +68 -0
  62. package/dist/projection-format.js +118 -0
  63. package/dist/projection-window.d.ts +9 -0
  64. package/dist/projection-window.js +93 -0
  65. package/dist/projection.d.ts +22 -0
  66. package/dist/projection.js +153 -0
  67. package/dist/question-response.d.ts +20 -0
  68. package/dist/question-response.js +75 -0
  69. package/dist/questions.d.ts +74 -0
  70. package/dist/questions.js +199 -0
  71. package/dist/self-evolving.d.ts +55 -0
  72. package/dist/self-evolving.js +239 -0
  73. package/dist/skills.d.ts +15 -0
  74. package/dist/skills.js +51 -0
  75. package/dist/tool-evidence.d.ts +14 -0
  76. package/dist/tool-evidence.js +54 -0
  77. package/dist/validation.d.ts +38 -0
  78. package/dist/validation.js +68 -0
  79. package/dist/workspaces.d.ts +107 -0
  80. package/dist/workspaces.js +212 -0
  81. package/package.json +145 -150
  82. package/dist/DebugDrawer-JNA43SVR.js +0 -8
  83. package/dist/admin-DORVQfLn.d.ts +0 -340
  84. package/dist/chunk-2BGLZZ7T.js +0 -10
  85. package/dist/chunk-42D6KAQV.js +0 -2882
  86. package/dist/chunk-4XOW57KJ.js +0 -343
  87. package/dist/chunk-72KT6VWV.js +0 -1338
  88. package/dist/chunk-7XAOVFSN.js +0 -28
  89. package/dist/chunk-KJIEMSQP.js +0 -198
  90. package/dist/chunk-KN72XWP7.js +0 -394
  91. package/dist/chunk-N6THTH7P.js +0 -110
  92. package/dist/chunk-V3H5A3BR.js +0 -22501
  93. package/dist/chunk-YGRQ4Q5P.js +0 -20
  94. package/dist/chunk-YTUZFKB3.js +0 -78
  95. package/dist/core/index.d.ts +0 -28
  96. package/dist/core/index.js +0 -617
  97. package/dist/createHarness-DOLOHA62.js +0 -18
  98. package/dist/eval/index.d.ts +0 -247
  99. package/dist/eval/index.js +0 -541
  100. package/dist/file-search-BUgMSKZE.d.ts +0 -289
  101. package/dist/front/artifacts.d.ts +0 -34
  102. package/dist/front/artifacts.js +0 -21
  103. package/dist/front/index.d.ts +0 -1082
  104. package/dist/front/index.js +0 -12912
  105. package/dist/front/styles.css +0 -5380
  106. package/dist/harness-qHlMJhZo.d.ts +0 -276
  107. package/dist/middleware-CaAEXpi5.d.ts +0 -115
  108. package/dist/piChatEvent-DpE0lIb9.d.ts +0 -450
  109. package/dist/piChatSessionService-CBct-hmz.d.ts +0 -86
  110. package/dist/sandbox-BMhut3kh.d.ts +0 -317
  111. package/dist/server/agent-host/testing/compositionRouteProof.d.ts +0 -8
  112. package/dist/server/agent-host/testing/compositionRouteProof.js +0 -78
  113. package/dist/server/agent-host/testing/gatewayConformance.d.ts +0 -43
  114. package/dist/server/agent-host/testing/gatewayConformance.js +0 -605
  115. package/dist/server/index.d.ts +0 -2278
  116. package/dist/server/index.js +0 -274
  117. package/dist/server/pi-session-readability.d.ts +0 -12
  118. package/dist/server/pi-session-readability.js +0 -23
  119. package/dist/server/worker/index.d.ts +0 -68
  120. package/dist/server/worker/index.js +0 -377
  121. package/dist/share-entry-CxS6UJ_L.d.ts +0 -330
  122. package/dist/shared/index.d.ts +0 -2111
  123. package/dist/shared/index.js +0 -262
  124. package/dist/types-DcWoQMn8.d.ts +0 -1377
  125. package/docs/ACCESSIBILITY.md +0 -52
  126. package/docs/AGENT_GATEWAY_V0.md +0 -212
  127. package/docs/AGENT_PACKAGES.md +0 -90
  128. package/docs/API.md +0 -123
  129. package/docs/AUTHORING.md +0 -68
  130. package/docs/CSP.md +0 -41
  131. package/docs/ERROR_CODES.md +0 -229
  132. package/docs/KNOWN_LIMITATIONS.md +0 -100
  133. package/docs/MIGRATION.md +0 -50
  134. package/docs/PERFORMANCE.md +0 -68
  135. package/docs/PLUGINS.md +0 -138
  136. package/docs/README.md +0 -98
  137. package/docs/RISKS-MULTI-TAB.md +0 -46
  138. package/docs/SESSION_IDENTITY.md +0 -91
  139. package/docs/STYLING.md +0 -73
  140. package/docs/UI-SHADCN.md +0 -56
  141. package/docs/VERCEL_COSTS.md +0 -103
  142. package/docs/plans/archive/AGENT_EVAL_FRAMEWORK.md +0 -466
  143. package/docs/plans/archive/agent-package-spec.md +0 -1777
  144. package/docs/plans/archive/harness-followup-capabilities.md +0 -440
  145. package/docs/plans/archive/harness-tool-ui-capabilities.md +0 -144
  146. package/docs/plans/archive/pi-followup-history-projection.md +0 -229
  147. package/docs/plans/archive/pi-tools-migration.md +0 -1061
  148. package/docs/plans/archive/reviews/pi-followup-history-codex-review.md +0 -11
  149. package/docs/plans/archive/reviews/pi-followup-history-gpt-review.md +0 -64
  150. package/docs/plans/archive/reviews/pi-followup-history-opus-review.md +0 -43
  151. package/docs/plans/archive/reviews/pi-followup-history-xai-review.md +0 -43
  152. package/docs/plans/archive/vercel-base-snapshot-template-plan.md +0 -527
  153. package/docs/plans/archive/vercel-persistent-sandbox-adapter.md +0 -553
  154. package/docs/runtime-provisioning.md +0 -157
  155. package/docs/runtime.md +0 -296
  156. package/docs/tools.md +0 -102
@@ -1,605 +0,0 @@
1
- import "../../../chunk-72KT6VWV.js";
2
- import {
3
- compareSessionOrder
4
- } from "../../../chunk-7XAOVFSN.js";
5
- import "../../../chunk-YTUZFKB3.js";
6
- import {
7
- AGENT_GATEWAY_ERROR_CODES,
8
- AgentGatewayError,
9
- AgentGatewayErrorCode
10
- } from "../../../chunk-KN72XWP7.js";
11
- import "../../../chunk-YGRQ4Q5P.js";
12
- import "../../../chunk-KJIEMSQP.js";
13
-
14
- // src/server/agent-host/testing/gatewayConformance.ts
15
- import { describe, expect, it } from "vitest";
16
- async function expectCode(operation, code) {
17
- await expect(operation).rejects.toMatchObject({ code });
18
- }
19
- async function createSession(fixture, scope, requestId, agentTypeId = "alpha") {
20
- return fixture.gateway.createSession({
21
- scope,
22
- agentTypeId,
23
- requestId,
24
- title: requestId
25
- });
26
- }
27
- function gatewayConformance(options) {
28
- describe(`AgentGateway conformance (replay ${options.replayLevel}, pagination ${options.paginationLevel})`, () => {
29
- it("requires issuer provenance and current membership on every operation", async () => {
30
- const fixture = await options.createFixture();
31
- const scope = fixture.issueScope();
32
- const ref = await createSession(fixture, scope, "create-valid");
33
- const foreign = fixture.issueScope({ issuer: "foreign" });
34
- const spread = { ...scope };
35
- const jsonCopy = JSON.parse(JSON.stringify(scope));
36
- const forged = {
37
- workspaceScopeId: scope.workspaceScopeId,
38
- authSubjectId: scope.authSubjectId
39
- };
40
- fixture.revoke(scope);
41
- for (const [index, denied] of [foreign, spread, jsonCopy, forged, scope].entries()) {
42
- await expectCode(fixture.gateway.listAgents({ scope: denied }), "AGENT_SCOPE_DENIED");
43
- await expectCode(fixture.gateway.listSessions({ scope: denied }), "AGENT_SCOPE_DENIED");
44
- await expectCode(fixture.gateway.createSession({ scope: denied, agentTypeId: "alpha", requestId: `denied-create-${index}` }), "AGENT_SCOPE_DENIED");
45
- await expectCode(fixture.gateway.connectSession({ scope: denied, ref }), "AGENT_SCOPE_DENIED");
46
- await expectCode(fixture.gateway.readSessionState({ scope: denied, ref }), "AGENT_SCOPE_DENIED");
47
- await expectCode(fixture.gateway.renameSession({ scope: denied, ref, requestId: `denied-rename-${index}`, title: "x" }), "AGENT_SCOPE_DENIED");
48
- await expectCode(fixture.gateway.setSessionArchived({ scope: denied, ref, requestId: `denied-archive-${index}`, archived: true }), "AGENT_SCOPE_DENIED");
49
- await expectCode(fixture.gateway.deleteSession({ scope: denied, ref, requestId: `denied-delete-${index}` }), "AGENT_SCOPE_DENIED");
50
- }
51
- });
52
- it("re-verifies membership for every command on an open connection", async () => {
53
- const fixture = await options.createFixture();
54
- const scope = fixture.issueScope();
55
- const ref = await createSession(fixture, scope, "open-command-session");
56
- const connection = await fixture.gateway.connectSession({ scope, ref });
57
- fixture.revoke(scope);
58
- await expectCode(connection.send({ kind: "prompt", requestId: "p", clientNonce: "n", content: "hello" }), "AGENT_SCOPE_DENIED");
59
- await expectCode(connection.interrupt({ requestId: "i" }), "AGENT_SCOPE_DENIED");
60
- await expectCode(connection.stop({ requestId: "s" }), "AGENT_SCOPE_DENIED");
61
- await expectCode(connection.clearQueue({ requestId: "q" }), "AGENT_SCOPE_DENIED");
62
- await connection.close();
63
- });
64
- it("exposes the complete closed v0 stable error mapping", () => {
65
- expect(AGENT_GATEWAY_ERROR_CODES).toEqual([
66
- "AGENT_TYPE_UNKNOWN",
67
- "AGENT_SESSION_NOT_FOUND",
68
- "AGENT_SCOPE_DENIED",
69
- "AGENT_ENTITLEMENT_REQUIRED",
70
- "AGENT_ACCESS_FORBIDDEN",
71
- "AGENT_ACCESS_POLICY_UNAVAILABLE",
72
- "AGENT_SESSION_REPLAY_GAP",
73
- "AGENT_SESSION_CURSOR_AHEAD",
74
- "AGENT_SESSION_CURSOR_EXPIRED",
75
- "AGENT_SESSION_CURSOR_INVALID",
76
- "AGENT_REQUEST_CONFLICT",
77
- "AGENT_REQUEST_IN_PROGRESS",
78
- "AGENT_REQUEST_OUTCOME_UNKNOWN",
79
- "AGENT_RUNTIME_RESTART_REQUIRED",
80
- "AGENT_COMMAND_INVALID_STATE",
81
- "AGENT_SHARED_ENVIRONMENT_UNAVAILABLE",
82
- "AGENT_GATEWAY_CLOSED"
83
- ]);
84
- expect(new AgentGatewayError(AgentGatewayErrorCode.AGENT_TYPE_UNKNOWN, "unknown").toJSON()).toEqual({
85
- code: AgentGatewayErrorCode.AGENT_TYPE_UNKNOWN,
86
- message: "unknown"
87
- });
88
- });
89
- it("applies admission before mutation with strong denial for every effect", async () => {
90
- const cases = [
91
- {
92
- effect: "session.create",
93
- run: async (fixture, scope, requestId) => {
94
- await expectCode(fixture.gateway.createSession({ scope, agentTypeId: "alpha", requestId, title: "denied" }), "AGENT_SCOPE_DENIED");
95
- expect(await fixture.gateway.listSessions({ scope })).toEqual({ sessions: [] });
96
- }
97
- },
98
- {
99
- effect: "session.rename",
100
- run: async (fixture, scope, requestId) => {
101
- const ref = await createSession(fixture, scope, `${requestId}-session`);
102
- await expectCode(fixture.gateway.renameSession({ scope, ref, requestId, title: "mutated" }), "AGENT_SCOPE_DENIED");
103
- expect((await fixture.gateway.readSessionState({ scope, ref })).summary.title).toBe(`${requestId}-session`);
104
- }
105
- },
106
- {
107
- effect: "session.archive",
108
- run: async (fixture, scope, requestId) => {
109
- const ref = await createSession(fixture, scope, `${requestId}-session`);
110
- await expectCode(fixture.gateway.setSessionArchived({ scope, ref, requestId, archived: true }), "AGENT_SCOPE_DENIED");
111
- expect((await fixture.gateway.listSessions({ scope, archived: "active" })).sessions).toContainEqual(expect.objectContaining({ ref }));
112
- }
113
- },
114
- {
115
- effect: "session.delete",
116
- run: async (fixture, scope, requestId) => {
117
- const ref = await createSession(fixture, scope, `${requestId}-session`);
118
- await expectCode(fixture.gateway.deleteSession({ scope, ref, requestId }), "AGENT_SCOPE_DENIED");
119
- await expect(fixture.gateway.readSessionState({ scope, ref })).resolves.toMatchObject({ ref });
120
- }
121
- },
122
- {
123
- effect: "session.prompt",
124
- run: async (fixture, scope, requestId) => {
125
- const ref = await createSession(fixture, scope, `${requestId}-session`);
126
- const connection = await fixture.gateway.connectSession({ scope, ref });
127
- await expectCode(connection.send({ kind: "prompt", requestId, clientNonce: requestId, content: "prompt" }), "AGENT_SCOPE_DENIED");
128
- const state = await fixture.gateway.readSessionState({ scope, ref });
129
- expect(state.summary.status).toBe("idle");
130
- expect(state.state.messages).toEqual([]);
131
- await connection.close();
132
- }
133
- },
134
- {
135
- effect: "session.followup",
136
- run: async (fixture, scope, requestId) => {
137
- const ref = await createSession(fixture, scope, `${requestId}-session`);
138
- const connection = await fixture.gateway.connectSession({ scope, ref });
139
- fixture.setActivity(ref, "running");
140
- await expectCode(connection.send({ kind: "followup", requestId, clientNonce: requestId, clientSeq: 1, content: "followup" }), "AGENT_SCOPE_DENIED");
141
- const state = await fixture.gateway.readSessionState({ scope, ref });
142
- expect(state.summary.status).toBe("running");
143
- expect(state.state.queue.followUps).toEqual([]);
144
- await connection.close();
145
- }
146
- },
147
- {
148
- effect: "session.interrupt",
149
- run: async (fixture, scope, requestId) => {
150
- const ref = await createSession(fixture, scope, `${requestId}-session`);
151
- const connection = await fixture.gateway.connectSession({ scope, ref });
152
- fixture.setActivity(ref, "running");
153
- await expectCode(connection.interrupt({ requestId }), "AGENT_SCOPE_DENIED");
154
- expect((await fixture.gateway.readSessionState({ scope, ref })).summary.status).toBe("running");
155
- await connection.close();
156
- }
157
- },
158
- {
159
- effect: "session.stop",
160
- run: async (fixture, scope, requestId) => {
161
- const ref = await createSession(fixture, scope, `${requestId}-session`);
162
- const connection = await fixture.gateway.connectSession({ scope, ref });
163
- fixture.setActivity(ref, "running");
164
- await connection.send({ kind: "followup", requestId: `${requestId}-queued`, clientNonce: `${requestId}-queued`, clientSeq: 1, content: "queued" });
165
- await expectCode(connection.stop({ requestId }), "AGENT_SCOPE_DENIED");
166
- const state = await fixture.gateway.readSessionState({ scope, ref });
167
- expect(state.summary.status).toBe("running");
168
- expect(state.state.queue.followUps).toHaveLength(1);
169
- await connection.close();
170
- }
171
- },
172
- {
173
- effect: "session.queue.clear",
174
- run: async (fixture, scope, requestId) => {
175
- const ref = await createSession(fixture, scope, `${requestId}-session`);
176
- const connection = await fixture.gateway.connectSession({ scope, ref });
177
- fixture.setActivity(ref, "running");
178
- await connection.send({ kind: "followup", requestId: `${requestId}-queued`, clientNonce: `${requestId}-queued`, clientSeq: 1, content: "queued" });
179
- await expectCode(connection.clearQueue({ requestId }), "AGENT_SCOPE_DENIED");
180
- const state = await fixture.gateway.readSessionState({ scope, ref });
181
- expect(state.summary.status).toBe("running");
182
- expect(state.state.queue.followUps).toHaveLength(1);
183
- await connection.close();
184
- }
185
- }
186
- ];
187
- for (const [index, testCase] of cases.entries()) {
188
- const fixture = await options.createFixture();
189
- const scope = fixture.issueScope();
190
- const requestId = `strong-reject-${index}`;
191
- fixture.queueAdmission(testCase.effect, "strong-reject");
192
- await testCase.run(fixture, scope, requestId);
193
- }
194
- });
195
- it("applies admission before mutation with retryable retry and digest conflict", async () => {
196
- const fixture = await options.createFixture();
197
- const scope = fixture.issueScope();
198
- fixture.queueAdmission("session.create", "strong-reject");
199
- await expectCode(fixture.gateway.createSession({
200
- scope,
201
- agentTypeId: "alpha",
202
- requestId: "strong-reject",
203
- title: "denied"
204
- }), "AGENT_SCOPE_DENIED");
205
- await expectCode(fixture.gateway.createSession({
206
- scope,
207
- agentTypeId: "alpha",
208
- requestId: "strong-reject",
209
- title: "denied"
210
- }), "AGENT_SCOPE_DENIED");
211
- await expectCode(fixture.gateway.createSession({
212
- scope,
213
- agentTypeId: "alpha",
214
- requestId: "strong-reject",
215
- title: "conflicting digest"
216
- }), "AGENT_REQUEST_CONFLICT");
217
- fixture.queueAdmission("session.create", "retryable");
218
- await expectCode(fixture.gateway.createSession({
219
- scope,
220
- agentTypeId: "alpha",
221
- requestId: "retryable"
222
- }), "AGENT_GATEWAY_CLOSED");
223
- await expect(fixture.gateway.listSessions({ scope })).resolves.toEqual({ sessions: [] });
224
- await expectCode(fixture.gateway.createSession({
225
- scope,
226
- agentTypeId: "alpha",
227
- requestId: "retryable",
228
- title: "conflicting retry"
229
- }), "AGENT_REQUEST_CONFLICT");
230
- const retry = {
231
- scope,
232
- agentTypeId: "alpha",
233
- requestId: "retryable"
234
- };
235
- const ref = await fixture.gateway.createSession(retry);
236
- await expect(fixture.gateway.createSession(retry)).resolves.toEqual(ref);
237
- await expect(fixture.gateway.listSessions({ scope })).resolves.toMatchObject({ sessions: [{ ref }] });
238
- });
239
- it("fails unknown agents and hidden cross-scope sessions with stable errors", async () => {
240
- const fixture = await options.createFixture();
241
- const scopeA = fixture.issueScope({ workspaceScopeId: "workspace-a" });
242
- const scopeB = fixture.issueScope({ workspaceScopeId: "workspace-b" });
243
- const ref = await createSession(fixture, scopeA, "scope-a-session");
244
- await expectCode(createSession(fixture, scopeA, "unknown-agent", "missing"), "AGENT_TYPE_UNKNOWN");
245
- await expectCode(fixture.gateway.readSessionState({ scope: scopeB, ref }), "AGENT_SESSION_NOT_FOUND");
246
- await expectCode(fixture.gateway.readSessionState({ scope: scopeA, ref: { agentTypeId: ref.agentTypeId, sessionId: "missing" } }), "AGENT_SESSION_NOT_FOUND");
247
- });
248
- it("keys idempotency by scope, effect, full target, request id, and digest", async () => {
249
- const fixture = await options.createFixture();
250
- const scopeA = fixture.issueScope({ workspaceScopeId: "workspace-a" });
251
- const scopeB = fixture.issueScope({ workspaceScopeId: "workspace-b" });
252
- const concurrent = await Promise.allSettled([
253
- createSession(fixture, scopeA, "same-id"),
254
- createSession(fixture, scopeA, "same-id")
255
- ]);
256
- const fulfilled = concurrent.filter((result) => result.status === "fulfilled");
257
- const first = fulfilled[0]?.value;
258
- expect(first).toBeDefined();
259
- expect(fulfilled.every((result) => JSON.stringify(result.value) === JSON.stringify(first))).toBe(true);
260
- const rejected = concurrent.find((result) => result.status === "rejected");
261
- if (rejected) expect(rejected).toMatchObject({ reason: { code: AgentGatewayErrorCode.AGENT_REQUEST_IN_PROGRESS } });
262
- expect(await createSession(fixture, scopeA, "same-id")).toEqual(first);
263
- await expectCode(
264
- fixture.gateway.createSession({ scope: scopeA, agentTypeId: "alpha", requestId: "same-id", title: "different" }),
265
- "AGENT_REQUEST_CONFLICT"
266
- );
267
- const otherScope = await createSession(fixture, scopeB, "same-id");
268
- const otherAgent = await createSession(fixture, scopeA, "same-id", "beta");
269
- expect(otherScope).not.toEqual(first);
270
- expect(otherAgent.agentTypeId).toBe("beta");
271
- const second = await createSession(fixture, scopeA, "second-target");
272
- const renamedFirst = await fixture.gateway.renameSession({ scope: scopeA, ref: first, requestId: "shared-target-id", title: "one" });
273
- expect(await fixture.gateway.renameSession({ scope: scopeA, ref: first, requestId: "shared-target-id", title: "one" })).toEqual(renamedFirst);
274
- await expectCode(
275
- fixture.gateway.renameSession({ scope: scopeA, ref: first, requestId: "shared-target-id", title: "changed" }),
276
- "AGENT_REQUEST_CONFLICT"
277
- );
278
- await expect(fixture.gateway.renameSession({ scope: scopeA, ref: second, requestId: "shared-target-id", title: "two" })).resolves.toMatchObject({ title: "two" });
279
- const archived = await fixture.gateway.setSessionArchived({
280
- scope: scopeA,
281
- ref: first,
282
- requestId: "archive-id",
283
- archived: true
284
- });
285
- expect(archived.archived).toBe(true);
286
- await expect(fixture.gateway.setSessionArchived({
287
- scope: scopeA,
288
- ref: first,
289
- requestId: "archive-id",
290
- archived: true
291
- })).resolves.toEqual(archived);
292
- await expectCode(fixture.gateway.setSessionArchived({
293
- scope: scopeA,
294
- ref: first,
295
- requestId: "archive-id",
296
- archived: false
297
- }), "AGENT_REQUEST_CONFLICT");
298
- await fixture.gateway.deleteSession({ scope: scopeA, ref: second, requestId: "delete-id" });
299
- await expect(fixture.gateway.deleteSession({ scope: scopeA, ref: second, requestId: "delete-id" })).resolves.toBeUndefined();
300
- });
301
- it("replays a completed archive receipt after the session is deleted", async () => {
302
- const fixture = await options.createFixture();
303
- const scope = fixture.issueScope();
304
- const ref = await createSession(fixture, scope, "archive-then-delete");
305
- const archiveRequest = {
306
- scope,
307
- ref,
308
- requestId: "archive-before-delete",
309
- archived: true
310
- };
311
- const archived = await fixture.gateway.setSessionArchived(archiveRequest);
312
- await fixture.gateway.deleteSession({ scope, ref, requestId: "delete-after-archive" });
313
- await expect(fixture.gateway.setSessionArchived(archiveRequest)).resolves.toEqual(archived);
314
- });
315
- it("enforces command states, queue controls, typed receipts, and command idempotency", async () => {
316
- const fixture = await options.createFixture();
317
- const scope = fixture.issueScope();
318
- const ref = await createSession(fixture, scope, "commands");
319
- const connection = await fixture.gateway.connectSession({ scope, ref });
320
- expect(await connection.interrupt({ requestId: "idle-interrupt" })).toMatchObject({ accepted: true });
321
- expect((await fixture.gateway.readSessionState({ scope, ref })).summary.status).toBe("idle");
322
- const prompt = await connection.send({ kind: "prompt", requestId: "prompt", clientNonce: "nonce-p", content: "start" });
323
- expect(prompt).toMatchObject({ accepted: true, disposition: "prompt", clientNonce: "nonce-p" });
324
- expect(await connection.send({ kind: "prompt", requestId: "prompt", clientNonce: "nonce-p", content: "start" })).toMatchObject({ duplicate: true });
325
- await expectCode(
326
- connection.send({ kind: "prompt", requestId: "prompt", clientNonce: "nonce-p", content: "different" }),
327
- "AGENT_REQUEST_CONFLICT"
328
- );
329
- await expectCode(
330
- connection.send({ kind: "prompt", requestId: "second-prompt", clientNonce: "nonce-p2", content: "invalid while running" }),
331
- "AGENT_COMMAND_INVALID_STATE"
332
- );
333
- const followup = await connection.send({ kind: "followup", requestId: "followup", clientNonce: "nonce-f", clientSeq: 4, content: "next" });
334
- expect(followup).toMatchObject({ accepted: true, disposition: "followup", clientNonce: "nonce-f", clientSeq: 4 });
335
- expect(await connection.interrupt({ requestId: "interrupt" })).toMatchObject({ accepted: true });
336
- expect(await connection.interrupt({ requestId: "interrupt" })).toMatchObject({ accepted: true });
337
- expect(await connection.interrupt({ requestId: "interrupt-aborting" })).toMatchObject({ accepted: true });
338
- await expectCode(
339
- connection.send({ kind: "prompt", requestId: "aborting-prompt", clientNonce: "nonce-a", content: "invalid while aborting" }),
340
- "AGENT_COMMAND_INVALID_STATE"
341
- );
342
- await expect(connection.send({ kind: "followup", requestId: "aborting-followup", clientNonce: "nonce-af", clientSeq: 5, content: "queued while aborting" })).resolves.toMatchObject({ disposition: "followup" });
343
- expect(await connection.clearQueue({ requestId: "clear" })).toMatchObject({ accepted: true, cleared: 2 });
344
- expect(await connection.clearQueue({ requestId: "clear" })).toMatchObject({ accepted: true, cleared: 2 });
345
- fixture.setActivity(ref, "error");
346
- await expect(connection.send({ kind: "prompt", requestId: "error-recovery", clientNonce: "nonce-e", content: "retry" })).resolves.toMatchObject({ disposition: "prompt" });
347
- const stop = await connection.stop({ requestId: "stop" });
348
- expect(stop).toMatchObject({ accepted: true, stopped: true, clearedQueue: [] });
349
- expect(await connection.stop({ requestId: "stop" })).toEqual(stop);
350
- await connection.close();
351
- });
352
- it("keeps strict-idle busy admission retryable and idempotent", async () => {
353
- const fixture = await options.createFixture();
354
- const scope = fixture.issueScope();
355
- const ref = await createSession(fixture, scope, "strict-idle-retry");
356
- const connection = await fixture.gateway.connectSession({ scope, ref });
357
- const input = {
358
- kind: "prompt",
359
- requestId: "strict-review",
360
- clientNonce: "strict-review",
361
- content: "model-facing review",
362
- displayContent: "Visible review",
363
- requireIdle: true
364
- };
365
- fixture.setActivity(ref, "running");
366
- await expectCode(connection.send(input), AgentGatewayErrorCode.AGENT_COMMAND_INVALID_STATE);
367
- fixture.setActivity(ref, "idle");
368
- const accepted = await connection.send(input);
369
- expect(accepted).toMatchObject({ accepted: true, disposition: "prompt" });
370
- await expect(connection.send(input)).resolves.toMatchObject({ ...accepted, duplicate: true });
371
- await expectCode(
372
- connection.send({ ...input, displayContent: "changed payload" }),
373
- AgentGatewayErrorCode.AGENT_REQUEST_CONFLICT
374
- );
375
- await connection.close();
376
- });
377
- it("admits exactly one of two concurrent strict-idle prompts", async () => {
378
- const fixture = await options.createFixture();
379
- const scope = fixture.issueScope();
380
- const ref = await createSession(fixture, scope, "strict-idle-race");
381
- const connection = await fixture.gateway.connectSession({ scope, ref });
382
- fixture.setActivity(ref, "idle");
383
- const outcome = async (requestId) => {
384
- try {
385
- await connection.send({
386
- kind: "prompt",
387
- requestId,
388
- clientNonce: requestId,
389
- content: requestId,
390
- requireIdle: true
391
- });
392
- return "accepted";
393
- } catch (error) {
394
- return error.code;
395
- }
396
- };
397
- const outcomes = await Promise.all([outcome("strict-a"), outcome("strict-b")]);
398
- expect(outcomes.sort()).toEqual([
399
- AgentGatewayErrorCode.AGENT_COMMAND_INVALID_STATE,
400
- "accepted"
401
- ].sort());
402
- await connection.close();
403
- });
404
- it("keeps ordinary error recovery while strict-idle prompts reject error state", async () => {
405
- const fixture = await options.createFixture();
406
- const scope = fixture.issueScope();
407
- const strictRef = await createSession(fixture, scope, "strict-error");
408
- const strictConnection = await fixture.gateway.connectSession({ scope, ref: strictRef });
409
- fixture.setActivity(strictRef, "error");
410
- await expectCode(strictConnection.send({
411
- kind: "prompt",
412
- requestId: "strict-error-prompt",
413
- clientNonce: "strict-error-prompt",
414
- content: "strict",
415
- requireIdle: true
416
- }), AgentGatewayErrorCode.AGENT_COMMAND_INVALID_STATE);
417
- await strictConnection.close();
418
- const recoveryRef = await createSession(fixture, scope, "ordinary-error");
419
- const recoveryConnection = await fixture.gateway.connectSession({ scope, ref: recoveryRef });
420
- fixture.setActivity(recoveryRef, "error");
421
- await expect(recoveryConnection.send({
422
- kind: "prompt",
423
- requestId: "ordinary-recovery",
424
- clientNonce: "ordinary-recovery",
425
- content: "recover"
426
- })).resolves.toMatchObject({ disposition: "prompt" });
427
- await recoveryConnection.close();
428
- });
429
- it("covers the complete command-state table and queue selector semantics", async () => {
430
- const fixture = await options.createFixture();
431
- const scope = fixture.issueScope();
432
- const states = ["idle", "running", "aborting", "error"];
433
- for (const [index, state] of states.entries()) {
434
- const ref = await createSession(fixture, scope, `state-${state}`);
435
- const connection = await fixture.gateway.connectSession({ scope, ref });
436
- fixture.setActivity(ref, state);
437
- const prompt = connection.send({
438
- kind: "prompt",
439
- requestId: `state-prompt-${index}`,
440
- clientNonce: `state-prompt-${index}`,
441
- content: "prompt"
442
- });
443
- if (state === "idle" || state === "error") {
444
- await expect(prompt).resolves.toMatchObject({ disposition: "prompt" });
445
- } else {
446
- await expectCode(prompt, "AGENT_COMMAND_INVALID_STATE");
447
- }
448
- fixture.setActivity(ref, state);
449
- const followupInput = {
450
- kind: "followup",
451
- requestId: `state-followup-${index}`,
452
- clientNonce: `state-followup-${index}`,
453
- clientSeq: index,
454
- content: "follow-up"
455
- };
456
- await expect(connection.send(followupInput)).resolves.toMatchObject({ disposition: "followup" });
457
- await expect(connection.send(followupInput)).resolves.toMatchObject({ duplicate: true });
458
- await expectCode(connection.send({ ...followupInput, content: "conflict" }), "AGENT_REQUEST_CONFLICT");
459
- await expectCode(connection.clearQueue({
460
- requestId: `selector-mismatch-${index}`,
461
- clientNonce: followupInput.clientNonce,
462
- clientSeq: index + 100
463
- }), "AGENT_REQUEST_CONFLICT");
464
- const beforeClear = (await fixture.gateway.readSessionState({ scope, ref })).summary.status;
465
- await expect(connection.clearQueue({ requestId: `state-clear-${index}` })).resolves.toMatchObject({ accepted: true });
466
- expect((await fixture.gateway.readSessionState({ scope, ref })).summary.status).toBe(beforeClear);
467
- fixture.setActivity(ref, state);
468
- await expect(connection.interrupt({ requestId: `state-interrupt-${index}` })).resolves.toMatchObject({ accepted: true });
469
- const afterInterrupt = (await fixture.gateway.readSessionState({ scope, ref })).summary.status;
470
- expect(afterInterrupt).toBe(state === "running" ? "aborting" : state);
471
- fixture.setActivity(ref, state);
472
- await expect(connection.stop({ requestId: `state-stop-${index}` })).resolves.toMatchObject({
473
- accepted: true,
474
- stopped: state === "running" || state === "aborting"
475
- });
476
- expect((await fixture.gateway.readSessionState({ scope, ref })).summary.status).toBe("idle");
477
- await connection.close();
478
- }
479
- });
480
- it("treats close as unsubscribe and keeps the session turn alive", async () => {
481
- const fixture = await options.createFixture();
482
- const scope = fixture.issueScope();
483
- const ref = await createSession(fixture, scope, "close-unsubscribe");
484
- const first = await fixture.gateway.connectSession({ scope, ref });
485
- const pendingEvent = first.events[Symbol.asyncIterator]().next();
486
- await first.close();
487
- await expect(pendingEvent).resolves.toMatchObject({ done: true });
488
- const second = await fixture.gateway.connectSession({ scope, ref });
489
- await second.send({ kind: "prompt", requestId: "run", clientNonce: "run", content: "continue" });
490
- await second.close();
491
- const snapshot = await fixture.gateway.readSessionState({ scope, ref });
492
- expect(snapshot.summary.status).toBe("running");
493
- const third = await fixture.gateway.connectSession({ scope, ref });
494
- await expect(third.interrupt({ requestId: "interrupt-after-close" })).resolves.toMatchObject({ accepted: true });
495
- await third.close();
496
- });
497
- it("provides monotonic single-sequence envelopes and consistent snapshots", async () => {
498
- const fixture = await options.createFixture();
499
- const scope = fixture.issueScope();
500
- const ref = await createSession(fixture, scope, "sequence");
501
- const connection = await fixture.gateway.connectSession({ scope, ref, cursor: 0 });
502
- await connection.send({ kind: "prompt", requestId: "event-1", clientNonce: "event-1", content: "one" });
503
- await connection.send({ kind: "followup", requestId: "event-2", clientNonce: "event-2", clientSeq: 1, content: "two" });
504
- const iterator = connection.events[Symbol.asyncIterator]();
505
- const first = await iterator.next();
506
- const second = await iterator.next();
507
- expect(first.done).toBe(false);
508
- expect(second.done).toBe(false);
509
- if (!first.done && !second.done) {
510
- expect(first.value.seq).toBe(first.value.event.seq);
511
- expect(second.value.seq).toBe(second.value.event.seq);
512
- expect(second.value.seq).toBeGreaterThan(first.value.seq);
513
- }
514
- const snapshot = await fixture.gateway.readSessionState({ scope, ref });
515
- expect(snapshot.seq).toBe(snapshot.state.seq);
516
- expect(snapshot.ref).toEqual(ref);
517
- await connection.close();
518
- });
519
- it("recovers a bounded replay gap through snapshot then reconnect", async () => {
520
- const fixture = await options.createFixture();
521
- const scope = fixture.issueScope();
522
- const ref = await createSession(fixture, scope, "replay");
523
- const connection = await fixture.gateway.connectSession({ scope, ref });
524
- for (let index = 0; index < 6; index += 1) {
525
- await connection.send({ kind: "followup", requestId: `replay-${index}`, clientNonce: `replay-${index}`, clientSeq: index, content: "event" });
526
- }
527
- await connection.close();
528
- await expectCode(fixture.gateway.connectSession({ scope, ref, cursor: 0 }), "AGENT_SESSION_REPLAY_GAP");
529
- const snapshot = await fixture.gateway.readSessionState({ scope, ref });
530
- await expect(fixture.gateway.connectSession({ scope, ref, cursor: snapshot.seq })).resolves.toMatchObject({ ref });
531
- await expectCode(fixture.gateway.connectSession({ scope, ref, cursor: snapshot.seq + 1 }), "AGENT_SESSION_CURSOR_AHEAD");
532
- });
533
- it("orders keyset pages and denies tampered or rebound cursors", async () => {
534
- const fixture = await options.createFixture();
535
- const scopeA = fixture.issueScope({ workspaceScopeId: "workspace-a" });
536
- const scopeB = fixture.issueScope({ workspaceScopeId: "workspace-b" });
537
- const alphaA = await createSession(fixture, scopeA, "a", "alpha");
538
- const beta = await createSession(fixture, scopeA, "b", "beta");
539
- const alphaC = await createSession(fixture, scopeA, "c", "alpha");
540
- fixture.moveSession(alphaA, 5e3);
541
- fixture.moveSession(beta, 5e3);
542
- fixture.moveSession(alphaC, 5e3);
543
- const page = await fixture.gateway.listSessions({ scope: scopeA, limit: 1 });
544
- expect(page.sessions).toHaveLength(1);
545
- expect(page.nextCursor).toBeDefined();
546
- const cursor = page.nextCursor;
547
- await expectCode(fixture.gateway.listSessions({ scope: scopeA, cursor: `${cursor}tampered`, limit: 1 }), "AGENT_SESSION_CURSOR_INVALID");
548
- await expectCode(fixture.gateway.listSessions({ scope: scopeB, cursor, limit: 1 }), "AGENT_SESSION_CURSOR_INVALID");
549
- await expectCode(fixture.gateway.listSessions({ scope: scopeA, cursor, limit: 2 }), "AGENT_SESSION_CURSOR_INVALID");
550
- await expectCode(fixture.gateway.listSessions({ scope: scopeA, cursor, agentTypeId: "alpha", limit: 1 }), "AGENT_SESSION_CURSOR_INVALID");
551
- await expectCode(fixture.gateway.listSessions({ scope: scopeA, cursor, archived: "active", limit: 1 }), "AGENT_SESSION_CURSOR_INVALID");
552
- const all = await fixture.gateway.listSessions({ scope: scopeA, limit: 20 });
553
- expect(all.sessions).toEqual([...all.sessions].sort((left, right) => compareSessionOrder(
554
- [left.updatedAt, left.ref.agentTypeId, left.ref.sessionId],
555
- [right.updatedAt, right.ref.agentTypeId, right.ref.sessionId]
556
- )));
557
- expect(all.sessions.map((session) => session.ref)).toEqual([alphaA, alphaC, beta]);
558
- });
559
- it("documents keyset mutation behavior: moved rows may shift and deleted rows disappear", async () => {
560
- const fixture = await options.createFixture();
561
- const scope = fixture.issueScope();
562
- const first = await createSession(fixture, scope, "mutation-a");
563
- const moved = await createSession(fixture, scope, "mutation-b");
564
- const deleted = await createSession(fixture, scope, "mutation-c");
565
- const page = await fixture.gateway.listSessions({ scope, limit: 1 });
566
- expect(page.nextCursor).toBeDefined();
567
- fixture.moveSession(moved, Date.now() + 1e5);
568
- await fixture.gateway.deleteSession({ scope, ref: deleted, requestId: "mutation-delete" });
569
- const continuation = await fixture.gateway.listSessions({ scope, limit: 1, cursor: page.nextCursor });
570
- expect(continuation.sessions).not.toContainEqual(expect.objectContaining({ ref: deleted }));
571
- expect(continuation.sessions).not.toContainEqual(expect.objectContaining({ ref: moved }));
572
- expect([first, moved, deleted]).toHaveLength(3);
573
- });
574
- it("allows a still-valid cursor to yield an empty page after mutation", async () => {
575
- const fixture = await options.createFixture();
576
- const scope = fixture.issueScope();
577
- const tail = await createSession(fixture, scope, "empty-a");
578
- await createSession(fixture, scope, "empty-b");
579
- const page = await fixture.gateway.listSessions({ scope, limit: 1 });
580
- expect(page.nextCursor).toBeDefined();
581
- await fixture.gateway.deleteSession({ scope, ref: tail, requestId: "empty-delete" });
582
- await expect(fixture.gateway.listSessions({ scope, limit: 1, cursor: page.nextCursor })).resolves.toEqual({ sessions: [] });
583
- });
584
- it("closes the facade idempotently and rejects subsequent operations", async () => {
585
- const fixture = await options.createFixture();
586
- const scope = fixture.issueScope();
587
- await fixture.gateway.close();
588
- await fixture.gateway.close();
589
- await expectCode(fixture.gateway.listAgents({ scope }), "AGENT_GATEWAY_CLOSED");
590
- });
591
- it.skip("Level D restart preserves sequence continuity [owner: streaming lane]", () => {
592
- });
593
- it.skip("Level D durable admission/effect crash matrix reconciles preserved unresolved work [owner: streaming lane]", () => {
594
- });
595
- it.skip("Level D durable activity index reconciles non-quiescent states at startup [owner: streaming lane]", () => {
596
- });
597
- it.skip("Level D/v2 immutable snapshot pagination is mutation-stable and expires [owner: #905 pool cursor]", () => {
598
- });
599
- it.skip("v2 remote wire validates JSON event leaves, paths, depth, and size [owner: #905 remote wire]", () => {
600
- });
601
- });
602
- }
603
- export {
604
- gatewayConformance
605
- };