@tangle-network/chatgpt-agents-kit 0.1.0 → 0.1.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.
@@ -3,10 +3,12 @@ name: operate-existing-agent
3
3
  description: Connect an existing Tangle agent, hand over reviewed work, and retrieve actual outputs without changing its identity or workspace.
4
4
  ---
5
5
 
6
- Call agent_profile and agent_capabilities before doing work. Treat tools/list as
7
- the authority for this app and this account. Do not invent an endpoint or fall
8
- back to a broader credential when an action is missing. Never request API keys,
9
- cookies, bearer tokens or provider credentials in chat.
6
+ Call agent_profile and agent_capabilities before doing work.
7
+ Treat tools/list and its schemas as the tool discovery authority.
8
+ agent_capabilities describes permissions available under the current grant.
9
+ An action omitted there can still be advertised for additional consent.
10
+ Tools absent from tools/list stay unavailable.
11
+ Never invent endpoints or request API keys, cookies, bearer tokens, or provider credentials in chat.
10
12
 
11
13
  Offer “Connect my agent”. List accessible agents, then use the exact existing
12
14
  workspaceId and threadId the user selects. Reconnect using those same IDs; do not
@@ -36,6 +38,17 @@ list delegate_task use that name and its brief argument. Do not offer both as
36
38
  separate ways to run an agent. Use the saved target and a fresh UUID turnId for a
37
39
  new instruction; pass the connect_my_agent contextVersion when supported. Do not
38
40
  force a file handoff for an ordinary prompt or a retained text answer.
41
+
42
+ For an explicitly requested task, invoke the advertised prompt tool with the approved target and input, following normal tool confirmation.
43
+ Do this even when agent_capabilities omits it because the current grant lacks its advertised scopes.
44
+ An mcp/www_authenticate challenge means permission is required; it is not task admission.
45
+ Present the client's normal reconnect or consent flow and explain that permission to run the agent is needed.
46
+ Wait for the user's consent; never approve it or substitute credentials.
47
+ If consent is declined, fails, or remains pending, retain the task intent and report that state.
48
+ After consent, refresh agent_profile and agent_capabilities, then reconnect the same target and verify its native identity.
49
+ Continue only when the refreshed authority permits the requested action.
50
+ Preserve the original target, turnId, and instruction through consent; the client may already have retried the tool.
51
+
39
52
  Reuse that UUID only for a retry of the identical prompt. Check get_task before
40
53
  retrying uncertain admission. A queued, accepted, working or input-required
41
54
  receipt is not completed work. Wait for retained terminal evidence; report
@@ -0,0 +1,18 @@
1
+ export declare const cardOutputSchemas: {
2
+ readonly list_my_agents: {
3
+ type: string;
4
+ anyOf: object[];
5
+ };
6
+ readonly connect_my_agent: {
7
+ type: string;
8
+ anyOf: object[];
9
+ };
10
+ readonly prompt_agent: {
11
+ type: string;
12
+ anyOf: object[];
13
+ };
14
+ readonly get_task: {
15
+ type: string;
16
+ anyOf: object[];
17
+ };
18
+ };
@@ -0,0 +1,43 @@
1
+ // Exact successful shapes from this kit's existing native tool dispatcher.
2
+ // Errors keep their existing structuredContent envelope and are described too.
3
+ const string = { type: 'string' };
4
+ const integer = { type: 'integer' };
5
+ const boolean = { type: 'boolean' };
6
+ const target = { workspaceId: string, threadId: string };
7
+ const environment = { type: 'object', properties: { kind: { const: 'persistent-sandbox' }, sandboxId: string,
8
+ sessionId: string, instanceKey: string, generation: integer, profileVersion: { type: ['string', 'null'] },
9
+ filesystemIncarnationId: string }, required: ['kind', 'sandboxId', 'sessionId'], additionalProperties: false };
10
+ const file = { type: 'object', properties: { path: string, content: string, revision: string, mediaType: string,
11
+ sha256: string, bytes: integer }, required: ['path', 'content', 'revision', 'mediaType', 'sha256', 'bytes'], additionalProperties: false };
12
+ const response = { type: 'object', properties: { content: string, mediaType: string, sha256: string, bytes: integer },
13
+ required: ['content', 'mediaType', 'sha256', 'bytes'], additionalProperties: false };
14
+ const continuation = { type: 'object', properties: { tool: { const: 'get_task' }, arguments: { type: 'object',
15
+ properties: { ...target, turnId: string }, required: ['workspaceId', 'threadId', 'turnId'], additionalProperties: false },
16
+ resubmit: { const: false } }, required: ['tool', 'arguments', 'resubmit'], additionalProperties: false };
17
+ const notification = { type: 'object', properties: { state: { const: 'not_subscribed' }, name: { const: 'agent.task_updated' },
18
+ arguments: { type: 'object', properties: { ...target, turnId: string },
19
+ required: ['workspaceId', 'threadId', 'turnId'], additionalProperties: false } },
20
+ required: ['state', 'name', 'arguments'], additionalProperties: false };
21
+ const failure = { type: 'object', properties: { error: { type: 'object', properties: { code: string, status: integer,
22
+ admission: { const: 'not-started' }, recovery: { type: 'object', properties: { tool: { const: 'get_task' },
23
+ arguments: { type: 'object', properties: { ...target, turnId: string },
24
+ required: ['workspaceId', 'threadId', 'turnId'], additionalProperties: false }, resubmit: { const: false } },
25
+ required: ['tool', 'arguments', 'resubmit'], additionalProperties: false } },
26
+ required: ['code', 'status'], additionalProperties: false } }, required: ['error'], additionalProperties: false };
27
+ const tool = (success) => ({ type: 'object', anyOf: [success, failure] });
28
+ const task = { type: 'object', properties: { ...target, turnId: string, executionId: string,
29
+ state: { enum: ['queued', 'working', 'input-required', 'succeeded', 'failed', 'cancelled', 'unknown'] },
30
+ source: string, environment, completionVerified: boolean, reason: string, completedAt: string,
31
+ response, outputs: { type: 'array', items: file }, continuation, notification },
32
+ required: ['workspaceId', 'threadId', 'turnId', 'state', 'source', 'completionVerified'], additionalProperties: false };
33
+ export const cardOutputSchemas = {
34
+ list_my_agents: tool({ type: 'object', properties: { appName: string, identityId: string,
35
+ agents: { type: 'array', items: { type: 'object', properties: { workspaceId: string, name: string, threadId: string },
36
+ required: ['workspaceId', 'name'], additionalProperties: false } } },
37
+ required: ['appName', 'identityId', 'agents'], additionalProperties: false }),
38
+ connect_my_agent: tool({ type: 'object', properties: { appName: string, identityId: string, ...target,
39
+ agentRef: string, contextVersion: string, continuity: { enum: ['persistent-sandbox', 'host-defined'] }, environment },
40
+ required: ['appName', 'identityId', 'workspaceId', 'threadId', 'agentRef', 'contextVersion', 'continuity'], additionalProperties: false }),
41
+ prompt_agent: tool(task),
42
+ get_task: tool(task),
43
+ };
@@ -0,0 +1,9 @@
1
+ /** Optional, prebundled MCP Apps resource. No tokens or native clients belong here. */
2
+ export interface AgentCardsResource {
3
+ html: string;
4
+ /** A dedicated HTTPS origin, required for hosted ChatGPT submissions. */
5
+ domain?: string;
6
+ }
7
+ export declare const AGENT_CARDS_URI = "ui://tangle-agents/cards.html";
8
+ export declare const AGENT_CARDS_MIME = "text/html;profile=mcp-app";
9
+ export declare const AGENT_CARD_TOOLS: readonly ["list_my_agents", "connect_my_agent", "prompt_agent", "get_task"];
@@ -0,0 +1,3 @@
1
+ export const AGENT_CARDS_URI = 'ui://tangle-agents/cards.html';
2
+ export const AGENT_CARDS_MIME = 'text/html;profile=mcp-app';
3
+ export const AGENT_CARD_TOOLS = ['list_my_agents', 'connect_my_agent', 'prompt_agent', 'get_task'];
package/dist/src/kit.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type AgentCardsResource } from './cards.ts';
1
2
  import { type AgentAppDescription } from './app-metadata.ts';
2
3
  import type { NativeBinding, NativeMcpFactory, OAuthResource, Principal } from './contracts.ts';
3
4
  import type { InspectionBinding } from './inspection.ts';
@@ -17,5 +18,7 @@ export declare function createAgentsHandler(options: {
17
18
  taskWait?: TaskWaitPolicy;
18
19
  /** Optional durable event adapter, bound to this host's native grant store and worker. */
19
20
  events?: TaskEvents;
21
+ /** Optional MCP Apps renderer. Omit to retain the tools-only protocol. */
22
+ cards?: AgentCardsResource;
20
23
  allowedOrigins?: readonly string[];
21
24
  }): (request: Request) => Promise<Response>;
package/dist/src/kit.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { AGENT_CARDS_URI, AGENT_CARDS_MIME, AGENT_CARD_TOOLS } from "./cards.js";
2
+ import { cardOutputSchemas } from "./card-output-schemas.js";
1
3
  import { agentAppDiscovery, defineAgentAppMetadata } from "./app-metadata.js";
2
4
  import { AgentFailure } from "./contracts.js";
3
5
  import { ACTION_TOOLS as names, HANDOFF_ACTIONS, describeAgentWorkflows } from "./workflows.js";
@@ -6,6 +8,12 @@ import { observeTask, taskWaitPolicy, validateNativeTask } from "./task-wait.js"
6
8
  import { describeConnection, digest, projectEnvironment } from "./connection.js";
7
9
  export { digest } from "./connection.js";
8
10
  const failureMarker = Symbol('native-tool-failure');
11
+ /** Only kit checks completed before native admission may permit reconnect and retry. */
12
+ class PreAdmissionFailure extends AgentFailure {
13
+ constructor(error) {
14
+ super(error instanceof AgentFailure ? error.message : 'native_operation_failed', error instanceof AgentFailure ? error.status : 503);
15
+ }
16
+ }
9
17
  /** An error after entering native admission must retain its reconciliation handle. */
10
18
  class AdmissionFailure extends AgentFailure {
11
19
  recovery;
@@ -184,24 +192,29 @@ async function run(action, args, binding, revalidate, signal, policy, canReadTas
184
192
  break;
185
193
  }
186
194
  case 'delegate': {
187
- if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(args.turnId))
188
- throw new AgentFailure('turn_id_must_be_uuid');
189
- // Validate observation authorization BEFORE admitting work. A denied wait must not create a task.
190
- if (Math.min(args.waitMs ?? policy.defaultMs, policy.maxMs) > 0) {
191
- if (!canReadTask)
192
- throw new AgentFailure('task_read_scope_required', 403);
193
- await binding.authorize(target, 'task');
194
- await revalidate();
195
+ try {
196
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(args.turnId))
197
+ throw new AgentFailure('turn_id_must_be_uuid');
198
+ // Validate observation authorization BEFORE admitting work. A denied wait must not create a task.
199
+ if (Math.min(args.waitMs ?? policy.defaultMs, policy.maxMs) > 0) {
200
+ if (!canReadTask)
201
+ throw new AgentFailure('task_read_scope_required', 403);
202
+ await binding.authorize(target, 'task');
203
+ await revalidate();
204
+ }
205
+ if (args.contextVersion !== undefined) {
206
+ if (!canConnect)
207
+ throw new AgentFailure('connect_scope_required', 403);
208
+ await binding.authorize(target, 'connect');
209
+ await revalidate();
210
+ if ((await describeConnection(await s.connect(target))).contextVersion !== args.contextVersion)
211
+ throw new AgentFailure('agent_context_changed_reconnect_required', 409);
212
+ await binding.authorize(target, 'delegate');
213
+ await revalidate();
214
+ }
195
215
  }
196
- if (args.contextVersion !== undefined) {
197
- if (!canConnect)
198
- throw new AgentFailure('connect_scope_required', 403);
199
- await binding.authorize(target, 'connect');
200
- await revalidate();
201
- if ((await describeConnection(await s.connect(target))).contextVersion !== args.contextVersion)
202
- throw new AgentFailure('agent_context_changed_reconnect_required', 409);
203
- await binding.authorize(target, 'delegate');
204
- await revalidate();
216
+ catch (error) {
217
+ throw new PreAdmissionFailure(error);
205
218
  }
206
219
  try {
207
220
  const task = await s.startTask(target, args.turnId, args.prompt, args.contextVersion === undefined ? undefined : { contextVersion: args.contextVersion });
@@ -422,7 +435,7 @@ export function createAgentsHandler(options) {
422
435
  || (typeof params.name === 'string' && request.headers.get('mcp-name') !== params.name))
423
436
  return rpcResult({ error: { code: -32600, message: 'MCP routing headers do not match request' } });
424
437
  if (parsed.method === 'server/discover')
425
- return rpcResult({ result: { resultType: 'complete', supportedVersions: ['2026-07-28'], serverInfo: { name: metadata.name, version: '0.1.0' }, capabilities: { tools: {}, ...(eventsEnabled ? { events: {} } : {}) } } });
438
+ return rpcResult({ result: { resultType: 'complete', supportedVersions: ['2026-07-28'], serverInfo: { name: metadata.name, version: '0.1.0' }, capabilities: { tools: {}, ...(options.cards ? { resources: {} } : {}), ...(eventsEnabled ? { events: {} } : {}) } } });
426
439
  }
427
440
  if (parsed.method.startsWith('events/')) {
428
441
  if (!v2 || !eventsEnabled)
@@ -436,6 +449,16 @@ export function createAgentsHandler(options) {
436
449
  }
437
450
  return rpcResult(result);
438
451
  }
452
+ if (options.cards && (parsed.method === 'resources/list' || parsed.method === 'resources/read')) {
453
+ const descriptor = { uri: AGENT_CARDS_URI, name: 'agent-cards', title: `${metadata.displayName} agents`, mimeType: AGENT_CARDS_MIME };
454
+ if (parsed.method === 'resources/list')
455
+ return rpcResult({ result: { resources: [descriptor], ...(v2 ? { resultType: 'complete' } : {}) } });
456
+ if (!record(parsed.params) || parsed.params.uri !== AGENT_CARDS_URI)
457
+ return rpcResult({ error: { code: -32002, message: 'Resource not found' } });
458
+ return rpcResult({ result: { contents: [{ ...descriptor, text: options.cards.html,
459
+ _meta: { ui: { prefersBorder: true, csp: { connectDomains: [], resourceDomains: [] }, ...(options.cards.domain ? { domain: options.cards.domain } : {}) } },
460
+ }], ...(v2 ? { resultType: 'complete' } : {}) } });
461
+ }
439
462
  const workflows = describeAgentWorkflows(actions);
440
463
  const handoff = workflows.some(workflow => workflow.id === 'handoff-to-agent') ? createHandoffTool(binding, revalidate) : null;
441
464
  const profileSchema = { type: 'object', properties: { id: { type: 'string', minLength: 1 }, name: { type: 'string' } }, required: ['id'], additionalProperties: false };
@@ -443,7 +466,12 @@ export function createAgentsHandler(options) {
443
466
  { name: 'agent_profile', description: 'Stable application identity of the authenticated account.', inputSchema: { type: 'object', properties: {}, additionalProperties: false }, run: async (args) => { validate(args, {}); return { id: binding.applicationUserId, ...(binding.name ? { name: binding.name } : {}) }; } },
444
467
  { name: 'agent_capabilities', description: 'Discover actions actually supported for this identity, app and grant now.', inputSchema: { type: 'object', properties: {}, additionalProperties: false }, run: async (args) => { validate(args, {}); return { appName: binding.appName, actions: [...actions.map(a => names[a]), ...(handoff ? [handoff.name] : [])], workflows, inspection: inspection?.tools.map(tool => tool.name) ?? [], media: ['text/plain; charset=utf-8'], compatibilityAliases: actions.includes('delegate') ? { delegate_task: names.delegate } : {}, preferredPromptTool: actions.includes('delegate') ? names.delegate : null, resultKinds: canReadOutput ? [...(binding.services.readFile ? ['files'] : []), ...(binding.services.readResponse ? ['response'] : [])] : [], execution: 'native-application', stateStore: 'native-application', taskWait: { defaultMs: waitPolicy.defaultMs, maxMs: waitPolicy.maxMs }, events: eventsEnabled ? { name: 'agent.task_updated', protocolVersion: '2026-07-28', delivery: ['webhook'], replay: false } : null }; } },
445
468
  ...advertisedActions.map(action => ({ name: names[action], description: descriptions[action], inputSchema: { type: 'object', properties: Object.fromEntries(Object.entries(schemas[action]).map(([key, value]) => { const { optional: _optional, ...schema } = value; return [key, schema]; })), required: Object.keys(schemas[action]).filter(key => !schemas[action][key].optional), additionalProperties: false }, run: async (args) => {
446
- validate(args, schemas[action]);
469
+ try {
470
+ validate(args, schemas[action]);
471
+ }
472
+ catch (error) {
473
+ throw action === 'delegate' ? new PreAdmissionFailure(error) : error;
474
+ }
447
475
  if (!actions.includes(action))
448
476
  throw new AgentFailure('insufficient_scope', 403);
449
477
  if (action === 'delegate' && args.contextVersion !== undefined && !actions.includes('connect'))
@@ -469,14 +497,14 @@ export function createAgentsHandler(options) {
469
497
  return await tool.run(args, {});
470
498
  }
471
499
  catch (error) {
472
- return { [failureMarker]: { code: error instanceof AgentFailure ? error.message : 'native_operation_failed', status: error instanceof AgentFailure ? error.status : 503, ...(error instanceof AdmissionFailure ? { recovery: error.recovery } : {}) } };
500
+ return { [failureMarker]: { code: error instanceof AgentFailure ? error.message : 'native_operation_failed', status: error instanceof AgentFailure ? error.status : 503, ...(error instanceof PreAdmissionFailure ? { admission: 'not-started' } : {}), ...(error instanceof AdmissionFailure ? { recovery: error.recovery } : {}) } };
473
501
  } } })),
474
502
  buildEnv: () => ({}),
475
- formatResult: result => {
503
+ formatResult: (result, tool) => {
476
504
  const failed = result && typeof result === 'object' && failureMarker in result ? result[failureMarker] : null;
477
505
  return failed
478
506
  ? { isError: true, content: [{ type: 'text', text: failed.code }], structuredContent: { error: failed } }
479
- : { content: [{ type: 'text', text: JSON.stringify(result) }], structuredContent: result };
507
+ : { content: [{ type: 'text', text: JSON.stringify(result) }], structuredContent: result, ...(options.cards && AGENT_CARD_TOOLS.includes(tool.name) ? { _meta: { agentCards: { displayName: metadata.displayName, tools: actions.map(action => names[action]) } } } : {}) };
480
508
  },
481
509
  });
482
510
  if (parsed.method === 'tools/call' && record(parsed.params) && parsed.params.name === 'delegate_task') {
@@ -499,7 +527,7 @@ export function createAgentsHandler(options) {
499
527
  ? [...new Set(HANDOFF_ACTIONS.flatMap(a => binding.requiredScopes[a] ?? []))]
500
528
  : [];
501
529
  const securitySchemes = [{ type: 'oauth2', scopes: inspectionTool ? inspectionScopes : action ? binding.requiredScopes[action] : handoffScopes }];
502
- Object.assign(tool, { annotations: { readOnlyHint: readOnly, destructiveHint: !readOnly, idempotentHint: readOnly, openWorldHint: action === 'delegate' || action === 'decide' || action === 'channel' }, securitySchemes, _meta: { securitySchemes, ...(tool.name === 'agent_profile' ? { 'openai/profile': true } : {}) }, ...(tool.name === 'agent_profile' ? { outputSchema: profileSchema } : {}) });
530
+ Object.assign(tool, { annotations: { readOnlyHint: readOnly, destructiveHint: !readOnly, idempotentHint: readOnly, openWorldHint: action === 'delegate' || action === 'decide' || action === 'channel' }, securitySchemes, _meta: { securitySchemes, ...(tool.name === 'agent_profile' ? { 'openai/profile': true } : {}), ...(options.cards && AGENT_CARD_TOOLS.includes(String(tool.name)) ? { ui: { resourceUri: AGENT_CARDS_URI, visibility: ['model', 'app'] } } : {}) }, ...(tool.name === 'agent_profile' ? { outputSchema: profileSchema } : {}), ...(Object.hasOwn(cardOutputSchemas, String(tool.name)) ? { outputSchema: cardOutputSchemas[tool.name] } : {}) });
503
531
  }
504
532
  if (v2 && envelope.result) {
505
533
  envelope.result.tools?.sort((a, b) => String(a.name).localeCompare(String(b.name)));
@@ -507,6 +535,12 @@ export function createAgentsHandler(options) {
507
535
  }
508
536
  return Response.json(envelope, { headers: { 'cache-control': 'no-store' } });
509
537
  }
538
+ if (options.cards && parsed.method === 'initialize' && response.ok) {
539
+ const envelope = await response.json();
540
+ if (envelope.result)
541
+ envelope.result.capabilities = { ...envelope.result.capabilities, resources: {} };
542
+ return Response.json(envelope, { headers: { 'cache-control': 'no-store' } });
543
+ }
510
544
  if (v2 && response.ok && response.status !== 204) {
511
545
  const envelope = await response.json();
512
546
  if (envelope.result)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/chatgpt-agents-kit",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/tangle-network/chatgpt-plugins.git",
@@ -28,21 +28,29 @@
28
28
  "./events-node": {
29
29
  "types": "./dist/src/events-node.d.ts",
30
30
  "import": "./dist/src/events-node.js"
31
+ },
32
+ "./cards": {
33
+ "types": "./dist/cards/server.d.ts",
34
+ "import": "./dist/cards/server.js"
31
35
  }
32
36
  },
33
37
  "files": [
34
38
  "dist",
35
39
  "README.md",
36
- "SETUP.md"
40
+ "SETUP.md",
41
+ "CARDS.md"
37
42
  ],
38
43
  "scripts": {
39
44
  "check": "tsc --noEmit",
40
- "build": "npm run check && tsc -p tsconfig.build.json && node scripts/build-assets.mjs",
41
- "test": "node --experimental-strip-types --test tests/kit.test.ts tests/workflows.test.ts tests/task-turn.test.ts tests/persistent-agent.test.ts tests/distribution.test.ts tests/setup.test.ts && npm run test:package",
45
+ "build": "npm run check && tsc -p tsconfig.build.json && node scripts/build-assets.mjs && node scripts/build-cards.mjs",
46
+ "test": "node --experimental-strip-types --test tests/kit.test.ts tests/workflows.test.ts tests/task-turn.test.ts tests/persistent-agent.test.ts tests/distribution.test.ts tests/setup.test.ts tests/cards.test.ts && npm run test:package",
42
47
  "test:package": "node --experimental-strip-types --test tests/packaged-generator.test.mjs",
43
48
  "acceptance": "node --experimental-strip-types acceptance/run.ts",
44
49
  "build:plugin": "node dist/src/cli.js",
45
- "verify:turn": "npm run build && node scripts/prove-turn.mjs"
50
+ "verify:turn": "npm run build && node scripts/prove-turn.mjs",
51
+ "test:cards": "node --experimental-strip-types --test tests/cards.test.ts",
52
+ "proof:cards": "node cards/proof.mjs",
53
+ "proof:cards:lifecycle": "node cards/proof-lifecycle.mjs"
46
54
  },
47
55
  "dependencies": {
48
56
  "@modelcontextprotocol/sdk": "1.31.0",
@@ -51,8 +59,19 @@
51
59
  "zod": "4.6.5"
52
60
  },
53
61
  "devDependencies": {
62
+ "@axe-core/playwright": "4.11.1",
63
+ "@modelcontextprotocol/ext-apps": "2.0.3",
64
+ "@playwright/test": "1.58.2",
54
65
  "@tangle-network/agent-app": "0.50.11",
66
+ "@tangle-network/brand": "1.10.0",
67
+ "@tangle-network/ui": "11.13.0",
55
68
  "@types/node": "22.15.30",
69
+ "@types/react": "19.2.14",
70
+ "@types/react-dom": "19.2.3",
71
+ "ajv": "8.18.0",
72
+ "esbuild": "0.27.4",
73
+ "react": "19.2.4",
74
+ "react-dom": "19.2.4",
56
75
  "typescript": "5.9.3"
57
76
  }
58
77
  }