@crowdedkingdoms/crowdyjs 11.0.0 → 12.0.0

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 (81) hide show
  1. package/MIGRATION.md +118 -0
  2. package/README.md +208 -0
  3. package/dist/crowdy-agent/browser-dispatcher.d.ts +41 -0
  4. package/dist/crowdy-agent/browser-dispatcher.d.ts.map +1 -0
  5. package/dist/crowdy-agent/browser-dispatcher.js +264 -0
  6. package/dist/crowdy-agent/controller.d.ts +144 -0
  7. package/dist/crowdy-agent/controller.d.ts.map +1 -0
  8. package/dist/crowdy-agent/controller.js +1239 -0
  9. package/dist/crowdy-agent/errors.d.ts +37 -0
  10. package/dist/crowdy-agent/errors.d.ts.map +1 -0
  11. package/dist/crowdy-agent/errors.js +107 -0
  12. package/dist/crowdy-agent/graphql-transport.d.ts +154 -0
  13. package/dist/crowdy-agent/graphql-transport.d.ts.map +1 -0
  14. package/dist/crowdy-agent/graphql-transport.js +854 -0
  15. package/dist/crowdy-agent/index.d.ts +16 -0
  16. package/dist/crowdy-agent/index.d.ts.map +1 -0
  17. package/dist/crowdy-agent/index.js +15 -0
  18. package/dist/crowdy-agent/registry.d.ts +29 -0
  19. package/dist/crowdy-agent/registry.d.ts.map +1 -0
  20. package/dist/crowdy-agent/registry.js +281 -0
  21. package/dist/crowdy-agent/schema.d.ts +72 -0
  22. package/dist/crowdy-agent/schema.d.ts.map +1 -0
  23. package/dist/crowdy-agent/schema.js +467 -0
  24. package/dist/crowdy-agent/studio-tools.d.ts +15 -0
  25. package/dist/crowdy-agent/studio-tools.d.ts.map +1 -0
  26. package/dist/crowdy-agent/studio-tools.js +280 -0
  27. package/dist/crowdy-agent/tool-descriptors.d.ts +4 -0
  28. package/dist/crowdy-agent/tool-descriptors.d.ts.map +1 -0
  29. package/dist/crowdy-agent/tool-descriptors.js +1049 -0
  30. package/dist/crowdy-agent/transport.d.ts +152 -0
  31. package/dist/crowdy-agent/transport.d.ts.map +1 -0
  32. package/dist/crowdy-agent/transport.js +27 -0
  33. package/dist/crowdy-agent/types.d.ts +328 -0
  34. package/dist/crowdy-agent/types.d.ts.map +1 -0
  35. package/dist/crowdy-agent/types.js +37 -0
  36. package/dist/crowdy-client.d.ts +3 -0
  37. package/dist/crowdy-client.d.ts.map +1 -1
  38. package/dist/crowdy-client.js +6 -0
  39. package/dist/crowdy-studio/agent-dom-shell.d.ts +40 -0
  40. package/dist/crowdy-studio/agent-dom-shell.d.ts.map +1 -0
  41. package/dist/crowdy-studio/agent-dom-shell.js +354 -0
  42. package/dist/crowdy-studio/controller.d.ts +74 -4
  43. package/dist/crowdy-studio/controller.d.ts.map +1 -1
  44. package/dist/crowdy-studio/controller.js +450 -17
  45. package/dist/crowdy-studio/dom-shell.d.ts +4 -1
  46. package/dist/crowdy-studio/dom-shell.d.ts.map +1 -1
  47. package/dist/crowdy-studio/dom-shell.js +6 -1
  48. package/dist/crowdy-studio/index.d.ts +6 -3
  49. package/dist/crowdy-studio/index.d.ts.map +1 -1
  50. package/dist/crowdy-studio/index.js +3 -0
  51. package/dist/crowdy-studio/models.d.ts +67 -0
  52. package/dist/crowdy-studio/models.d.ts.map +1 -1
  53. package/dist/crowdy-studio/mount.d.ts +22 -1
  54. package/dist/crowdy-studio/mount.d.ts.map +1 -1
  55. package/dist/crowdy-studio/mount.js +163 -2
  56. package/dist/crowdy-studio/styles.d.ts +1 -1
  57. package/dist/crowdy-studio/styles.d.ts.map +1 -1
  58. package/dist/crowdy-studio/styles.js +5 -3
  59. package/dist/generated/graphql.d.ts +3753 -1
  60. package/dist/generated/graphql.d.ts.map +1 -1
  61. package/dist/generated/graphql.js +296 -0
  62. package/dist/index.d.ts +1 -1
  63. package/dist/index.js +1 -1
  64. package/dist/kit/npcs.d.ts.map +1 -1
  65. package/dist/kit/social.d.ts.map +1 -1
  66. package/dist/player-host/index.d.ts +5 -0
  67. package/dist/player-host/index.d.ts.map +1 -0
  68. package/dist/player-host/index.js +3 -0
  69. package/dist/player-host/lease-manager.d.ts +66 -0
  70. package/dist/player-host/lease-manager.d.ts.map +1 -0
  71. package/dist/player-host/lease-manager.js +428 -0
  72. package/dist/player-host/schemas.d.ts +9 -0
  73. package/dist/player-host/schemas.d.ts.map +1 -0
  74. package/dist/player-host/schemas.js +347 -0
  75. package/dist/player-host/tools.d.ts +13 -0
  76. package/dist/player-host/tools.d.ts.map +1 -0
  77. package/dist/player-host/tools.js +79 -0
  78. package/dist/player-host/types.d.ts +203 -0
  79. package/dist/player-host/types.d.ts.map +1 -0
  80. package/dist/player-host/types.js +1 -0
  81. package/package.json +12 -2
@@ -0,0 +1,854 @@
1
+ import { createClient as createWsClient } from 'graphql-ws';
2
+ import { print } from 'graphql';
3
+ import { CrowdyGraphQLError, } from '../errors.js';
4
+ import { CrowdyStudioAgentAcknowledgeEventsDocument, CrowdyStudioAgentApproveToolDocument, CrowdyStudioAgentAttachClientDocument, CrowdyStudioAgentBudgetDocument, CrowdyStudioAgentCancelRunDocument, CrowdyStudioAgentCloseSessionDocument, CrowdyStudioAgentCreateSessionDocument, CrowdyStudioAgentEventsDocument, CrowdyStudioAgentGrantLeaseDocument, CrowdyStudioAgentHeartbeatDocument, CrowdyStudioAgentHistoryDocument, CrowdyStudioAgentPauseDocument, CrowdyStudioAgentRejectToolDocument, CrowdyStudioAgentResumeDocument, CrowdyStudioAgentRevokeLeaseDocument, CrowdyStudioAgentSendMessageDocument, CrowdyStudioAgentSessionDocument, CrowdyStudioAgentSessionsDocument, CrowdyStudioAgentSetModeDocument, CrowdyStudioAgentToolDescriptorsDocument, CrowdyStudioAgentToolResultDocument, } from '../generated/graphql.js';
5
+ import { CrowdyAgentError, } from './errors.js';
6
+ import { canonicalJson, digestCanonicalJson, } from './schema.js';
7
+ import { CrowdyAgentToolRegistry } from './registry.js';
8
+ /**
9
+ * Production generated-GraphQL adapter for `CrowdyStudioAgentTransportV1`.
10
+ * Every HTTP and subscription shape is selected by committed typed documents.
11
+ */
12
+ export class CrowdyAgentGraphQLTransport {
13
+ constructor(graphql, options) {
14
+ this.graphql = graphql;
15
+ this.options = options;
16
+ this.activeSubscriptions = new Set();
17
+ }
18
+ async getSession(sessionId) {
19
+ const data = await this.request(CrowdyStudioAgentSessionDocument, {
20
+ sessionId,
21
+ });
22
+ return mapSession(data.crowdyStudioAgentSession);
23
+ }
24
+ async listSessions(input) {
25
+ const data = await this.request(CrowdyStudioAgentSessionsDocument, {
26
+ appId: input.appId,
27
+ after: input.after ?? null,
28
+ first: input.first,
29
+ });
30
+ const connection = data.crowdyStudioAgentSessions;
31
+ const edges = connection.edges.map((edge) => ({
32
+ cursor: edge.cursor,
33
+ node: mapSession(edge.node),
34
+ }));
35
+ return {
36
+ edges,
37
+ pageInfo: {
38
+ hasNextPage: connection.pageInfo.hasNextPage,
39
+ ...(connection.pageInfo.endCursor
40
+ ? { endCursor: connection.pageInfo.endCursor }
41
+ : {}),
42
+ },
43
+ nodes: edges.map((edge) => edge.node),
44
+ ...(connection.endCursor ? { endCursor: connection.endCursor } : {}),
45
+ hasNextPage: connection.hasNextPage,
46
+ };
47
+ }
48
+ async history(input) {
49
+ const data = await this.request(CrowdyStudioAgentHistoryDocument, {
50
+ sessionId: input.sessionId,
51
+ afterSeq: input.afterSeq,
52
+ first: input.first,
53
+ });
54
+ const connection = data.crowdyStudioAgentHistory;
55
+ const edges = connection.edges.map((edge) => ({
56
+ cursor: edge.cursor,
57
+ node: mapEvent(edge.node),
58
+ }));
59
+ return {
60
+ edges,
61
+ pageInfo: {
62
+ hasNextPage: connection.pageInfo.hasNextPage,
63
+ ...(connection.pageInfo.endCursor
64
+ ? { endCursor: connection.pageInfo.endCursor }
65
+ : {}),
66
+ },
67
+ events: edges.map((edge) => edge.node),
68
+ hasMore: connection.hasMore,
69
+ };
70
+ }
71
+ async toolDescriptors(sessionId) {
72
+ const data = await this.request(CrowdyStudioAgentToolDescriptorsDocument, { sessionId });
73
+ const result = data.crowdyStudioAgentToolDescriptors;
74
+ const descriptors = result.tools.map(mapDescriptor);
75
+ const registry = new CrowdyAgentToolRegistry(descriptors);
76
+ if (registry.registryDigest !== result.registryDigest) {
77
+ throw new CrowdyAgentError('AGENT_CONTEXT_STALE', 'Game API descriptor registry digest does not match canonical descriptors');
78
+ }
79
+ return {
80
+ registryDigest: result.registryDigest,
81
+ tools: registry.list(),
82
+ };
83
+ }
84
+ async budget(sessionId) {
85
+ const data = await this.request(CrowdyStudioAgentBudgetDocument, {
86
+ sessionId,
87
+ });
88
+ return mapBudget(data.crowdyStudioAgentBudget);
89
+ }
90
+ async createSession(input) {
91
+ const data = await this.request(CrowdyStudioAgentCreateSessionDocument, {
92
+ input: {
93
+ idempotencyKey: input.idempotencyKey,
94
+ appId: input.appId,
95
+ mode: input.mode,
96
+ ...(input.projectId ? { projectId: input.projectId } : {}),
97
+ ...(input.gridId ? { gridId: input.gridId } : {}),
98
+ ...(input.requestedModel
99
+ ? { requestedModel: input.requestedModel }
100
+ : {}),
101
+ providerDataConsent: input.providerDataConsent ?? false,
102
+ },
103
+ });
104
+ return mapSession(data.crowdyStudioAgentCreateSession);
105
+ }
106
+ async attachClient(input) {
107
+ const data = await this.request(CrowdyStudioAgentAttachClientDocument, {
108
+ input: {
109
+ sessionId: input.sessionId,
110
+ idempotencyKey: input.idempotencyKey,
111
+ ...(input.clientInstanceId
112
+ ? { clientInstanceId: input.clientInstanceId }
113
+ : {}),
114
+ },
115
+ });
116
+ const attachment = data.crowdyStudioAgentAttachClient;
117
+ return {
118
+ session: mapSession(attachment.session),
119
+ clientEpoch: String(attachment.clientEpoch),
120
+ replayAfterSeq: String(attachment.replayAfterSeq),
121
+ };
122
+ }
123
+ async setMode(input) {
124
+ const data = await this.request(CrowdyStudioAgentSetModeDocument, {
125
+ input: {
126
+ ...input,
127
+ mode: input.mode,
128
+ },
129
+ });
130
+ return mapSession(data.crowdyStudioAgentSetMode);
131
+ }
132
+ async acknowledgeEvents(input) {
133
+ const data = await this.request(CrowdyStudioAgentAcknowledgeEventsDocument, { input });
134
+ return {
135
+ throughSeq: String(data.crowdyStudioAgentAcknowledgeEvents.throughSeq),
136
+ };
137
+ }
138
+ async heartbeat(input) {
139
+ const data = await this.request(CrowdyStudioAgentHeartbeatDocument, {
140
+ input,
141
+ });
142
+ const heartbeat = data.crowdyStudioAgentHeartbeat;
143
+ return {
144
+ serverTime: heartbeat.serverTime,
145
+ ...(heartbeat.playLeaseFreshUntil
146
+ ? { playLeaseFreshUntil: heartbeat.playLeaseFreshUntil }
147
+ : {}),
148
+ ...(heartbeat.workspaceLeaseExpiresAt
149
+ ? { workspaceLeaseExpiresAt: heartbeat.workspaceLeaseExpiresAt }
150
+ : {}),
151
+ };
152
+ }
153
+ async sendMessage(input) {
154
+ const data = await this.request(CrowdyStudioAgentSendMessageDocument, {
155
+ input: {
156
+ sessionId: input.sessionId,
157
+ clientEpoch: input.clientEpoch,
158
+ idempotencyKey: input.idempotencyKey,
159
+ content: input.message,
160
+ },
161
+ });
162
+ return mapRun(data.crowdyStudioAgentSendMessage);
163
+ }
164
+ async approveTool(input) {
165
+ const data = await this.request(CrowdyStudioAgentApproveToolDocument, {
166
+ input,
167
+ });
168
+ return mapApproval(data.crowdyStudioAgentApproveTool);
169
+ }
170
+ async rejectTool(input) {
171
+ const data = await this.request(CrowdyStudioAgentRejectToolDocument, {
172
+ input: {
173
+ sessionId: input.sessionId,
174
+ clientEpoch: input.clientEpoch,
175
+ idempotencyKey: input.idempotencyKey,
176
+ toolCallId: input.toolCallId,
177
+ argumentHash: input.argumentHash,
178
+ ...(input.reason ? { reason: input.reason } : {}),
179
+ },
180
+ });
181
+ return mapApproval(data.crowdyStudioAgentRejectTool);
182
+ }
183
+ async toolResult(input) {
184
+ const result = input.result;
185
+ const data = await this.request(CrowdyStudioAgentToolResultDocument, {
186
+ input: {
187
+ sessionId: input.sessionId,
188
+ clientEpoch: input.clientEpoch,
189
+ idempotencyKey: input.idempotencyKey,
190
+ result: {
191
+ protocolVersion: result.protocolVersion,
192
+ toolCallId: result.toolCallId,
193
+ status: result.status,
194
+ ...(result.output !== undefined
195
+ ? { outputJson: canonicalJson(result.output) }
196
+ : {}),
197
+ ...(result.error
198
+ ? {
199
+ errorCode: result.error.code,
200
+ errorMessage: result.error.message,
201
+ errorRetryable: result.error.retryable,
202
+ }
203
+ : {}),
204
+ observedContextVersion: result.observedContextVersion,
205
+ startedAt: result.startedAt,
206
+ finishedAt: result.finishedAt,
207
+ },
208
+ },
209
+ });
210
+ const call = data.crowdyStudioAgentToolResult;
211
+ return {
212
+ toolCallId: call.toolCallId,
213
+ toolName: call.toolName,
214
+ status: call.status,
215
+ argumentHash: call.argumentHash,
216
+ ...(call.error ? { error: mapError(call.error) } : {}),
217
+ accepted: call.accepted,
218
+ };
219
+ }
220
+ async grantLease(input) {
221
+ const data = await this.request(CrowdyStudioAgentGrantLeaseDocument, {
222
+ input: {
223
+ ...input,
224
+ scopes: [...input.scopes],
225
+ },
226
+ });
227
+ return mapLease(data.crowdyStudioAgentGrantLease);
228
+ }
229
+ async revokeLease(input) {
230
+ const data = await this.request(CrowdyStudioAgentRevokeLeaseDocument, {
231
+ input: {
232
+ ...input,
233
+ reason: input.reason,
234
+ },
235
+ });
236
+ return mapLease(data.crowdyStudioAgentRevokeLease);
237
+ }
238
+ async pause(input) {
239
+ const data = await this.request(CrowdyStudioAgentPauseDocument, { input });
240
+ return mapSession(data.crowdyStudioAgentPause);
241
+ }
242
+ async resume(input) {
243
+ const data = await this.request(CrowdyStudioAgentResumeDocument, { input });
244
+ return mapSession(data.crowdyStudioAgentResume);
245
+ }
246
+ async cancelRun(input) {
247
+ const data = await this.request(CrowdyStudioAgentCancelRunDocument, {
248
+ input,
249
+ });
250
+ return mapRun(data.crowdyStudioAgentCancelRun);
251
+ }
252
+ async closeSession(input) {
253
+ const data = await this.request(CrowdyStudioAgentCloseSessionDocument, {
254
+ input,
255
+ });
256
+ return mapSession(data.crowdyStudioAgentCloseSession);
257
+ }
258
+ subscribeEvents(input, handlers) {
259
+ const client = this.createSubscriptionClient();
260
+ let closed = false;
261
+ const dispose = client.subscribe({
262
+ query: print(CrowdyStudioAgentEventsDocument),
263
+ variables: input,
264
+ }, {
265
+ next: (message) => {
266
+ if (message.errors?.length) {
267
+ handlers.error(mapGraphQLErrors(message.errors));
268
+ return;
269
+ }
270
+ const data = message.data;
271
+ if (data?.crowdyStudioAgentEvents) {
272
+ try {
273
+ handlers.next(mapEvent(data.crowdyStudioAgentEvents));
274
+ }
275
+ catch (error) {
276
+ handlers.error(error);
277
+ }
278
+ }
279
+ },
280
+ error: (error) => handlers.error(mapTransportError(error)),
281
+ complete: () => handlers.complete(),
282
+ });
283
+ const subscription = {
284
+ close: () => {
285
+ if (closed)
286
+ return;
287
+ closed = true;
288
+ this.activeSubscriptions.delete(subscription);
289
+ dispose();
290
+ void client.dispose();
291
+ },
292
+ };
293
+ this.activeSubscriptions.add(subscription);
294
+ return subscription;
295
+ }
296
+ /** Close all agent event sockets owned by this adapter. */
297
+ close() {
298
+ for (const subscription of [...this.activeSubscriptions]) {
299
+ subscription.close();
300
+ }
301
+ }
302
+ createSubscriptionClient() {
303
+ if (this.options.subscriptionClientFactory) {
304
+ return this.options.subscriptionClientFactory();
305
+ }
306
+ if (!this.options.wsUrl) {
307
+ throw new CrowdyAgentError('AGENT_HOST_UNAVAILABLE', 'Crowdy agent events require a graphql-transport-ws endpoint');
308
+ }
309
+ return createWsClient({
310
+ url: this.options.wsUrl,
311
+ lazy: false,
312
+ retryAttempts: 0,
313
+ ...(this.options.webSocketImpl
314
+ ? { webSocketImpl: this.options.webSocketImpl }
315
+ : {}),
316
+ connectionParams: () => {
317
+ const token = this.options.getToken();
318
+ return token ? { Authorization: `Bearer ${token}` } : {};
319
+ },
320
+ });
321
+ }
322
+ async request(document, variables) {
323
+ try {
324
+ return await this.graphql.request(document, variables);
325
+ }
326
+ catch (error) {
327
+ throw mapTransportError(error);
328
+ }
329
+ }
330
+ }
331
+ function mapSession(value) {
332
+ return {
333
+ contractVersion: 'crowdy.studio-agent/1',
334
+ sessionId: value.sessionId,
335
+ appId: String(value.appId),
336
+ ...(value.projectId ? { projectId: value.projectId } : {}),
337
+ ...(value.gridId ? { gridId: String(value.gridId) } : {}),
338
+ mode: value.mode,
339
+ status: value.status,
340
+ requestedModel: value.requestedModel,
341
+ ...(value.model ? { model: value.model } : {}),
342
+ ...(value.resolvedModel ? { resolvedModel: value.resolvedModel } : {}),
343
+ providerDataConsent: value.providerDataConsent,
344
+ registryDigest: value.registryDigest,
345
+ providerPolicyVersion: value.providerPolicyVersion,
346
+ appPolicyVersion: value.appPolicyVersion,
347
+ contextVersion: value.contextVersion,
348
+ currentClientEpoch: String(value.currentClientEpoch),
349
+ ...(value.clientEpoch ? { clientEpoch: String(value.clientEpoch) } : {}),
350
+ lastEventSeq: String(value.lastEventSeq),
351
+ ...(value.currentRun ? { currentRun: mapRun(value.currentRun) } : {}),
352
+ activeLeases: value.activeLeases.map(mapLease),
353
+ ...(value.pendingApproval
354
+ ? { pendingApproval: mapApproval(value.pendingApproval) }
355
+ : {}),
356
+ createdAt: value.createdAt,
357
+ updatedAt: value.updatedAt,
358
+ ...(value.closedAt ? { closedAt: value.closedAt } : {}),
359
+ };
360
+ }
361
+ function mapRun(value) {
362
+ return {
363
+ runId: value.runId,
364
+ status: value.status,
365
+ providerRounds: value.providerRounds,
366
+ toolCalls: value.toolCalls,
367
+ ...(value.errorCode ? { errorCode: value.errorCode } : {}),
368
+ ...(value.terminalReason
369
+ ? { terminalReason: value.terminalReason }
370
+ : {}),
371
+ ...(value.reason ? { reason: value.reason } : {}),
372
+ ...(value.startedAt ? { startedAt: value.startedAt } : {}),
373
+ ...(value.finishedAt ? { finishedAt: value.finishedAt } : {}),
374
+ createdAt: value.createdAt,
375
+ cancelled: value.cancelled,
376
+ };
377
+ }
378
+ function mapApproval(value, reasons = []) {
379
+ return {
380
+ approvalId: value.approvalId,
381
+ toolCallId: value.toolCallId,
382
+ argumentHash: value.argumentHash,
383
+ status: value.status,
384
+ safeSummary: value.safeSummary,
385
+ reasons,
386
+ clientEpoch: String(value.clientEpoch),
387
+ expiresAt: value.expiresAt,
388
+ approved: value.approved,
389
+ rejected: value.rejected,
390
+ };
391
+ }
392
+ function mapLease(value) {
393
+ return {
394
+ leaseId: value.leaseId,
395
+ kind: value.kind,
396
+ status: value.status,
397
+ clientEpoch: String(value.clientEpoch),
398
+ scopes: value.scopes,
399
+ holder: value.holder,
400
+ ...(value.controlledEntityId
401
+ ? { controlledEntityId: value.controlledEntityId }
402
+ : {}),
403
+ ...(value.hostCapabilityRevision
404
+ ? { hostCapabilityRevision: value.hostCapabilityRevision }
405
+ : {}),
406
+ ...(value.expectedProjectRevision
407
+ ? { expectedProjectRevision: String(value.expectedProjectRevision) }
408
+ : {}),
409
+ contextVersion: value.contextVersion,
410
+ grantedAt: value.grantedAt,
411
+ expiresAt: value.expiresAt,
412
+ ...(value.revokedReason
413
+ ? {
414
+ revokedReason: mapPreemptionReason(value.revokedReason),
415
+ }
416
+ : {}),
417
+ };
418
+ }
419
+ function mapBudget(value) {
420
+ return {
421
+ dimensions: value.dimensions.map((dimension) => {
422
+ assertBudgetName(dimension.name);
423
+ assertBudgetScope(dimension.scope);
424
+ return {
425
+ name: dimension.name,
426
+ scope: dimension.scope,
427
+ limit: String(dimension.limit),
428
+ reserved: String(dimension.reserved),
429
+ consumed: String(dimension.consumed),
430
+ remaining: String(dimension.remaining),
431
+ unit: dimension.unit,
432
+ };
433
+ }),
434
+ ...(value.resetAt ? { resetAt: value.resetAt } : {}),
435
+ platformFunded: value.platformFunded,
436
+ payer: mapPayer(value.payer),
437
+ };
438
+ }
439
+ function mapDescriptor(value) {
440
+ const parsed = parseJsonObject(value.descriptorJson, 'descriptorJson');
441
+ if (canonicalJson(parsed) !== value.descriptorJson) {
442
+ throw new CrowdyAgentError('AGENT_CONTEXT_STALE', `${value.name} descriptor JSON is not canonical`);
443
+ }
444
+ if (digestCanonicalJson(parsed) !== value.descriptorDigest) {
445
+ throw new CrowdyAgentError('AGENT_CONTEXT_STALE', `${value.name} descriptor digest does not match its canonical JSON`);
446
+ }
447
+ const descriptor = parsed;
448
+ if (descriptor.name !== value.name ||
449
+ descriptor.version !== value.version ||
450
+ descriptor.wireName !== value.wireName ||
451
+ descriptor.schemaVersion !== value.schemaVersion ||
452
+ descriptor.summary !== value.summary ||
453
+ descriptor.executor !== value.executor ||
454
+ canonicalJson(descriptor.modes) !== canonicalJson(value.modes) ||
455
+ descriptor.risk.class !== value.risk ||
456
+ canonicalJson(descriptor.risk.effects) !==
457
+ canonicalJson(value.riskEffects) ||
458
+ descriptor.risk.reversible !== value.riskReversible ||
459
+ canonicalJson(descriptor.scopes.map((scope) => scope.scope)) !==
460
+ canonicalJson(value.scopes) ||
461
+ canonicalJson(descriptor.scopes) !== value.scopeRequirementsJson ||
462
+ (descriptor.approval.policy === 'REQUIRED') !==
463
+ value.approvalRequired ||
464
+ descriptor.approval.policy !== value.approvalPolicy ||
465
+ canonicalJson(descriptor.approval.reasons) !==
466
+ canonicalJson(value.approvalReasons) ||
467
+ descriptor.approval.maxTtlSeconds !== value.approvalMaxTtlSeconds ||
468
+ descriptor.idempotency.class !== value.idempotencyClass ||
469
+ descriptor.idempotency.keyScope !== value.idempotencyKeyScope ||
470
+ descriptor.timeoutMs !== value.timeoutMs ||
471
+ canonicalJson(descriptor.inputSchema) !== value.inputSchemaJson ||
472
+ canonicalJson(descriptor.outputSchema) !== value.outputSchemaJson ||
473
+ canonicalJson(descriptor.redaction.input) !== value.inputRedactionJson ||
474
+ canonicalJson(descriptor.redaction.output) !== value.outputRedactionJson ||
475
+ descriptor.redaction.maxPersistedBytes !== value.maxPersistedBytes) {
476
+ throw new CrowdyAgentError('AGENT_CONTEXT_STALE', `${value.name} descriptor fields diverge from descriptorJson`);
477
+ }
478
+ return descriptor;
479
+ }
480
+ function mapEvent(value) {
481
+ const base = {
482
+ protocolVersion: value.protocolVersion,
483
+ eventId: value.eventId,
484
+ sessionId: value.sessionId,
485
+ seq: String(value.seq),
486
+ createdAt: value.createdAt,
487
+ };
488
+ const type = value.type;
489
+ switch (value.__typename) {
490
+ case 'AgentLifecycleEvent':
491
+ if (type === 'MODE_SELECTED') {
492
+ if (!value.lifecycleMode) {
493
+ throw invalidEvent('MODE_SELECTED omitted mode');
494
+ }
495
+ return {
496
+ ...base,
497
+ type,
498
+ payload: { mode: value.lifecycleMode },
499
+ };
500
+ }
501
+ if (type === 'CONTEXT_CHANGED') {
502
+ if (!value.lifecycleContextVersion) {
503
+ throw invalidEvent('CONTEXT_CHANGED omitted contextVersion');
504
+ }
505
+ return {
506
+ ...base,
507
+ type,
508
+ payload: {
509
+ contextVersion: value.lifecycleContextVersion,
510
+ reason: value.lifecycleReason ?? 'CONTEXT_CHANGED',
511
+ },
512
+ };
513
+ }
514
+ if (type === 'CLIENT_ATTACHED' || type === 'CLIENT_DETACHED') {
515
+ return {
516
+ ...base,
517
+ type,
518
+ payload: {
519
+ ...(value.lifecycleClientEpoch
520
+ ? { clientEpoch: String(value.lifecycleClientEpoch) }
521
+ : {}),
522
+ ...(value.lifecycleReplayAfterSeq
523
+ ? { replayAfterSeq: String(value.lifecycleReplayAfterSeq) }
524
+ : {}),
525
+ ...(value.lifecycleReason
526
+ ? { reason: value.lifecycleReason }
527
+ : {}),
528
+ },
529
+ };
530
+ }
531
+ return {
532
+ ...base,
533
+ type,
534
+ payload: {
535
+ sessionId: value.sessionId,
536
+ ...(value.lifecycleReason
537
+ ? { reason: value.lifecycleReason }
538
+ : {}),
539
+ },
540
+ };
541
+ case 'AgentMessageEvent': {
542
+ const role = value.messageRole;
543
+ if (role !== 'USER' && role !== 'ASSISTANT') {
544
+ throw invalidEvent('Message event contains an unknown role');
545
+ }
546
+ if (type === 'ASSISTANT_CHUNK') {
547
+ return {
548
+ ...base,
549
+ type,
550
+ payload: {
551
+ runId: value.runId ?? '',
552
+ content: value.messageContent,
553
+ },
554
+ };
555
+ }
556
+ const message = {
557
+ messageId: value.messageEventId,
558
+ role,
559
+ content: value.messageContent,
560
+ ...(value.runId ? { runId: value.runId } : {}),
561
+ createdAt: value.createdAt,
562
+ };
563
+ return {
564
+ ...base,
565
+ type,
566
+ payload: { message },
567
+ };
568
+ }
569
+ case 'AgentRunEvent':
570
+ if (!value.runId)
571
+ throw invalidEvent('Run event omitted runId');
572
+ return {
573
+ ...base,
574
+ type,
575
+ payload: {
576
+ runId: value.runId,
577
+ status: value.runStatus,
578
+ ...(value.runCode ? { code: value.runCode } : {}),
579
+ ...(value.runReason ? { reason: value.runReason } : {}),
580
+ ...(value.runError ? { error: mapError(value.runError) } : {}),
581
+ },
582
+ };
583
+ case 'AgentToolEvent':
584
+ return {
585
+ ...base,
586
+ type,
587
+ payload: {
588
+ toolCallId: value.toolEventCallId,
589
+ name: value.toolEventName,
590
+ version: value.toolEventVersion,
591
+ status: value.toolStatus,
592
+ ...(value.toolSafeSummary
593
+ ? { safeSummary: value.toolSafeSummary }
594
+ : {}),
595
+ ...(value.toolArgumentHash
596
+ ? { argumentHash: value.toolArgumentHash }
597
+ : {}),
598
+ ...(value.toolInvocation
599
+ ? { invocation: mapInvocation(value.toolInvocation) }
600
+ : {}),
601
+ ...(value.toolResult
602
+ ? { result: mapResultEnvelope(value.toolResult) }
603
+ : {}),
604
+ ...(value.toolError ? { error: mapError(value.toolError) } : {}),
605
+ },
606
+ };
607
+ case 'AgentApprovalEvent':
608
+ return {
609
+ ...base,
610
+ type,
611
+ payload: {
612
+ approval: {
613
+ approvalId: value.approvalEventId,
614
+ toolCallId: value.approvalToolCallId,
615
+ argumentHash: value.approvalArgumentHash,
616
+ status: value.approvalStatus,
617
+ safeSummary: value.approvalSafeSummary,
618
+ reasons: value.approvalReasons,
619
+ expiresAt: value.approvalExpiresAt,
620
+ approved: value.approvalStatus === 'GRANTED' ||
621
+ value.approvalStatus === 'CONSUMED',
622
+ rejected: value.approvalStatus === 'DENIED',
623
+ },
624
+ },
625
+ };
626
+ case 'AgentLeaseEvent':
627
+ return {
628
+ ...base,
629
+ type,
630
+ payload: {
631
+ lease: {
632
+ leaseId: value.leaseEventId,
633
+ kind: value.leaseKind,
634
+ status: value.leaseStatus,
635
+ clientEpoch: String(value.leaseClientEpoch),
636
+ scopes: value.leaseScopes,
637
+ holder: value.leaseHolder,
638
+ contextVersion: value.leaseContextVersion,
639
+ ...(value.leaseControlledEntityId
640
+ ? { controlledEntityId: value.leaseControlledEntityId }
641
+ : {}),
642
+ ...(value.leaseHostCapabilityRevision
643
+ ? {
644
+ hostCapabilityRevision: value.leaseHostCapabilityRevision,
645
+ }
646
+ : {}),
647
+ ...(value.leaseExpectedProjectRevision
648
+ ? {
649
+ expectedProjectRevision: String(value.leaseExpectedProjectRevision),
650
+ }
651
+ : {}),
652
+ grantedAt: value.leaseGrantedAt,
653
+ expiresAt: value.leaseExpiresAt,
654
+ ...(value.leaseReason
655
+ ? {
656
+ revokedReason: mapPreemptionReason(value.leaseReason),
657
+ }
658
+ : {}),
659
+ },
660
+ },
661
+ };
662
+ case 'AgentCheckpointEvent':
663
+ if (value.checkpointReason !== 'AGENT_WRITE' &&
664
+ value.checkpointReason !== 'RESTORE_PREIMAGE' &&
665
+ value.checkpointReason !== 'MANUAL') {
666
+ throw invalidEvent(`Unknown checkpoint reason ${value.checkpointReason}`);
667
+ }
668
+ return {
669
+ ...base,
670
+ type,
671
+ payload: {
672
+ checkpoint: {
673
+ checkpointId: value.checkpointEventId,
674
+ projectRevision: String(value.checkpointProjectRevision),
675
+ contentHash: value.checkpointContentHash,
676
+ reason: value.checkpointReason,
677
+ files: value.checkpointFiles.map((file) => {
678
+ if (file.target !== 'SERVER' && file.target !== 'CLIENT') {
679
+ throw invalidEvent(`Unknown checkpoint file target ${file.target}`);
680
+ }
681
+ return {
682
+ target: file.target,
683
+ path: file.path,
684
+ contentHash: file.contentHash,
685
+ byteLength: file.byteLength,
686
+ };
687
+ }),
688
+ createdAt: value.createdAt,
689
+ ...(value.checkpointRestoredAt
690
+ ? { restoredAt: value.checkpointRestoredAt }
691
+ : {}),
692
+ },
693
+ },
694
+ };
695
+ case 'AgentBudgetEvent':
696
+ return {
697
+ ...base,
698
+ type,
699
+ payload: { budget: mapBudget(value.budgetSnapshot) },
700
+ };
701
+ }
702
+ }
703
+ function mapInvocation(value) {
704
+ return {
705
+ protocolVersion: value.protocolVersion,
706
+ sessionId: value.sessionId,
707
+ runId: value.runId,
708
+ toolCallId: value.toolCallId,
709
+ name: value.name,
710
+ version: value.version,
711
+ descriptorDigest: value.descriptorDigest,
712
+ arguments: parseJsonObject(value.argumentsJson, 'argumentsJson'),
713
+ argumentHash: value.argumentHash,
714
+ contextVersion: value.contextVersion,
715
+ ...(value.clientEpoch
716
+ ? { clientEpoch: String(value.clientEpoch) }
717
+ : {}),
718
+ ...(value.leaseId ? { leaseId: value.leaseId } : {}),
719
+ ...(value.approvalGrant
720
+ ? { approvalGrant: value.approvalGrant }
721
+ : {}),
722
+ ...(value.idempotencyKey
723
+ ? { idempotencyKey: value.idempotencyKey }
724
+ : {}),
725
+ deadline: value.deadline,
726
+ };
727
+ }
728
+ function mapResultEnvelope(value) {
729
+ return {
730
+ protocolVersion: value.protocolVersion,
731
+ toolCallId: value.toolCallId,
732
+ status: value.status,
733
+ ...(value.outputJson
734
+ ? { output: parseJsonObject(value.outputJson, 'outputJson') }
735
+ : {}),
736
+ ...(value.error ? { error: mapError(value.error) } : {}),
737
+ observedContextVersion: value.observedContextVersion,
738
+ startedAt: value.startedAt,
739
+ finishedAt: value.finishedAt,
740
+ };
741
+ }
742
+ function mapError(value) {
743
+ return {
744
+ code: value.code,
745
+ message: value.message,
746
+ retryable: value.retryable,
747
+ ...(value.remediation ? { remediation: value.remediation } : {}),
748
+ ...(value.field ? { field: value.field } : {}),
749
+ ...(value.requiredScope ? { requiredScope: value.requiredScope } : {}),
750
+ };
751
+ }
752
+ function parseJsonObject(value, field) {
753
+ try {
754
+ const parsed = JSON.parse(value);
755
+ if (parsed === null ||
756
+ typeof parsed !== 'object' ||
757
+ Array.isArray(parsed)) {
758
+ throw new Error('not object');
759
+ }
760
+ return parsed;
761
+ }
762
+ catch {
763
+ throw new CrowdyAgentError('AGENT_EVENT_CURSOR_INVALID', `${field} must contain one JSON object`, { field });
764
+ }
765
+ }
766
+ function assertBudgetName(value) {
767
+ if (![
768
+ 'REQUESTS',
769
+ 'INPUT_TOKENS',
770
+ 'OUTPUT_TOKENS',
771
+ 'REASONING_TOKENS',
772
+ 'PROVIDER_COST',
773
+ 'TOOL_ROUNDS',
774
+ 'WALL_CLOCK_MS',
775
+ 'TOOL_CALLS',
776
+ 'COMPILES',
777
+ ].includes(value)) {
778
+ throw invalidEvent(`Unknown budget dimension ${value}`);
779
+ }
780
+ }
781
+ function assertBudgetScope(value) {
782
+ if (!['TURN', 'SESSION', 'PLAYER_DAY'].includes(value)) {
783
+ throw invalidEvent(`Unknown budget scope ${value}`);
784
+ }
785
+ }
786
+ function mapPayer(value) {
787
+ if (value !== 'PLATFORM' && value !== 'APP' && value !== 'USER') {
788
+ throw invalidEvent(`Unknown agent budget payer ${value}`);
789
+ }
790
+ return value;
791
+ }
792
+ function mapPreemptionReason(value) {
793
+ const reasons = [
794
+ 'HUMAN_INPUT',
795
+ 'HUMAN_EDIT',
796
+ 'HUMAN_STOP',
797
+ 'ESCAPE',
798
+ 'DEATH',
799
+ 'CONTEXT_CHANGED',
800
+ 'PERMISSION_CHANGED',
801
+ 'ADMISSION_CHANGED',
802
+ 'CONTROL_TARGET_CHANGED',
803
+ 'DISCONNECTED',
804
+ 'CLIENT_REATTACHED',
805
+ 'QUOTA_FAILURE',
806
+ 'BUDGET_FAILURE',
807
+ 'OPERATOR_KILL',
808
+ 'LEASE_EXPIRED',
809
+ 'SESSION_CLOSED',
810
+ ];
811
+ if (!reasons.includes(value)) {
812
+ throw invalidEvent(`Unknown agent preemption reason ${value}`);
813
+ }
814
+ return value;
815
+ }
816
+ function invalidEvent(message) {
817
+ return new CrowdyAgentError('AGENT_EVENT_CURSOR_INVALID', message);
818
+ }
819
+ function mapGraphQLErrors(errors) {
820
+ const payloads = errors.filter((error) => error !== null &&
821
+ typeof error === 'object' &&
822
+ typeof error.message === 'string');
823
+ return mapTransportError(new CrowdyGraphQLError(payloads.length > 0
824
+ ? payloads
825
+ : [{ message: 'Agent subscription failed' }]));
826
+ }
827
+ function mapTransportError(error) {
828
+ if (error instanceof CrowdyAgentError)
829
+ return error;
830
+ if (error instanceof CrowdyGraphQLError) {
831
+ const code = error.code;
832
+ if (typeof code === 'string' &&
833
+ (code.startsWith('AGENT_') ||
834
+ code === 'CROWDY_STUDIO_REVISION_CONFLICT')) {
835
+ return new CrowdyAgentError(code, error.message, {
836
+ retryable: error.extensions?.retryable === true,
837
+ ...(typeof error.extensions?.remediation === 'string'
838
+ ? { remediation: error.extensions.remediation }
839
+ : {}),
840
+ ...(typeof error.extensions?.field === 'string'
841
+ ? { field: error.extensions.field }
842
+ : {}),
843
+ ...(typeof error.extensions?.requiredScope === 'string'
844
+ ? { requiredScope: error.extensions.requiredScope }
845
+ : {}),
846
+ cause: error,
847
+ });
848
+ }
849
+ return error;
850
+ }
851
+ if (Array.isArray(error))
852
+ return mapGraphQLErrors(error);
853
+ return new CrowdyAgentError('AGENT_DISCONNECTED', 'Crowdy agent GraphQL transport disconnected', { retryable: true, cause: error });
854
+ }