@zhin.js/agent 1.1.39 → 1.1.40

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 (114) hide show
  1. package/lib/assistant/job-storage.d.ts +238 -0
  2. package/lib/assistant/job-storage.js +143 -0
  3. package/lib/assistant/job-storage.js.map +1 -0
  4. package/lib/assistant/job-store.d.ts +7 -0
  5. package/lib/assistant/job-store.js +41 -3
  6. package/lib/assistant/job-store.js.map +1 -1
  7. package/lib/config/perception-config.d.ts +9 -0
  8. package/lib/config/perception-config.js +27 -0
  9. package/lib/config/perception-config.js.map +1 -1
  10. package/lib/context/context-system.d.ts +1 -0
  11. package/lib/context/context-system.js +8 -8
  12. package/lib/context/context-system.js.map +1 -1
  13. package/lib/context/im-turn-context-adapter.js +2 -0
  14. package/lib/context/im-turn-context-adapter.js.map +1 -1
  15. package/lib/context/skill-context.d.ts +3 -0
  16. package/lib/context/skill-context.js +6 -0
  17. package/lib/context/skill-context.js.map +1 -0
  18. package/lib/context/turn-envelope.d.ts +4 -0
  19. package/lib/context/turn-envelope.js +24 -7
  20. package/lib/context/turn-envelope.js.map +1 -1
  21. package/lib/context/turn-user-message.d.ts +3 -1
  22. package/lib/context/turn-user-message.js +14 -1
  23. package/lib/context/turn-user-message.js.map +1 -1
  24. package/lib/core/agent-core-run.d.ts +1 -0
  25. package/lib/core/agent-core-run.js +47 -20
  26. package/lib/core/agent-core-run.js.map +1 -1
  27. package/lib/core/structured-reply-recovery.d.ts +6 -2
  28. package/lib/core/structured-reply-recovery.js +22 -8
  29. package/lib/core/structured-reply-recovery.js.map +1 -1
  30. package/lib/core/turn-completion-usage.d.ts +2 -1
  31. package/lib/core/turn-completion-usage.js +14 -5
  32. package/lib/core/turn-completion-usage.js.map +1 -1
  33. package/lib/core/turn-event-mapper.d.ts +1 -1
  34. package/lib/core/turn-event-mapper.js +8 -5
  35. package/lib/core/turn-event-mapper.js.map +1 -1
  36. package/lib/event/turn-event.d.ts +7 -1
  37. package/lib/event/turn-event.js.map +1 -1
  38. package/lib/index.d.ts +4 -1
  39. package/lib/index.js +2 -0
  40. package/lib/index.js.map +1 -1
  41. package/lib/media/perception.d.ts +3 -4
  42. package/lib/media/perception.js +59 -37
  43. package/lib/media/perception.js.map +1 -1
  44. package/lib/memory/compaction-runtime.d.ts +3 -2
  45. package/lib/memory/compaction-runtime.js +22 -14
  46. package/lib/memory/compaction-runtime.js.map +1 -1
  47. package/lib/outbound/agent-output.js +2 -0
  48. package/lib/outbound/agent-output.js.map +1 -1
  49. package/lib/outbound/chat-failure.d.ts +27 -0
  50. package/lib/outbound/chat-failure.js +53 -0
  51. package/lib/outbound/chat-failure.js.map +1 -0
  52. package/lib/outbound/structured-reply.d.ts +7 -0
  53. package/lib/outbound/structured-reply.js +52 -16
  54. package/lib/outbound/structured-reply.js.map +1 -1
  55. package/lib/plugin-runtime/agent-runtime.js +49 -8
  56. package/lib/plugin-runtime/agent-runtime.js.map +1 -1
  57. package/lib/plugin-runtime/ai-completion.d.ts +4 -3
  58. package/lib/plugin-runtime/ai-completion.js +64 -10
  59. package/lib/plugin-runtime/ai-completion.js.map +1 -1
  60. package/lib/plugin-runtime/capability-ingress.js +3 -1
  61. package/lib/plugin-runtime/capability-ingress.js.map +1 -1
  62. package/lib/plugin-runtime/deferred-capability-plan.d.ts +4 -1
  63. package/lib/plugin-runtime/deferred-capability-plan.js +25 -9
  64. package/lib/plugin-runtime/deferred-capability-plan.js.map +1 -1
  65. package/lib/plugin-runtime/full-agent-turn-engine.js +1 -0
  66. package/lib/plugin-runtime/full-agent-turn-engine.js.map +1 -1
  67. package/lib/plugin-runtime/index.d.ts +2 -0
  68. package/lib/plugin-runtime/index.js +2 -0
  69. package/lib/plugin-runtime/index.js.map +1 -1
  70. package/lib/prompt/assembly.js +0 -9
  71. package/lib/prompt/assembly.js.map +1 -1
  72. package/lib/prompt/git-context.js +1 -1
  73. package/lib/prompt/git-context.js.map +1 -1
  74. package/lib/prompt/rules/chat-rules.js +8 -9
  75. package/lib/prompt/rules/chat-rules.js.map +1 -1
  76. package/lib/prompt/rules/critical-rules.js +10 -10
  77. package/lib/prompt/rules/critical-rules.js.map +1 -1
  78. package/lib/prompt/system-prompt.js +4 -5
  79. package/lib/prompt/system-prompt.js.map +1 -1
  80. package/lib/security/file-role-policy.js +6 -6
  81. package/lib/security/file-role-policy.js.map +1 -1
  82. package/lib/security/policy-facade.js +1 -1
  83. package/lib/security/policy-facade.js.map +1 -1
  84. package/lib/security/usage-quota.d.ts +4 -0
  85. package/lib/security/usage-quota.js +23 -2
  86. package/lib/security/usage-quota.js.map +1 -1
  87. package/lib/session/approval-port.d.ts +2 -0
  88. package/lib/session/approval-port.js.map +1 -1
  89. package/lib/session/approval-review-agent.d.ts +1 -1
  90. package/lib/session/approval-review-agent.js +19 -4
  91. package/lib/session/approval-review-agent.js.map +1 -1
  92. package/lib/session/turn-ingress-session.js +7 -1
  93. package/lib/session/turn-ingress-session.js.map +1 -1
  94. package/lib/tool/tool-approval-gate.js +4 -2
  95. package/lib/tool/tool-approval-gate.js.map +1 -1
  96. package/lib/tool/turn-tool-runtime.js +24 -5
  97. package/lib/tool/turn-tool-runtime.js.map +1 -1
  98. package/lib/tool-catalog/deferred-turn-controller.js +1 -4
  99. package/lib/tool-catalog/deferred-turn-controller.js.map +1 -1
  100. package/lib/trace/context-budget.d.ts +4 -3
  101. package/lib/trace/context-budget.js +28 -8
  102. package/lib/trace/context-budget.js.map +1 -1
  103. package/lib/trace/turn-trace-format.js +4 -1
  104. package/lib/trace/turn-trace-format.js.map +1 -1
  105. package/lib/trace/turn-trace.d.ts +6 -0
  106. package/lib/trace/turn-trace.js +8 -2
  107. package/lib/trace/turn-trace.js.map +1 -1
  108. package/lib/trace/turn-usage-log.d.ts +14 -0
  109. package/lib/trace/turn-usage-log.js +83 -0
  110. package/lib/trace/turn-usage-log.js.map +1 -0
  111. package/lib/usage/usage-store.d.ts +199 -0
  112. package/lib/usage/usage-store.js +121 -0
  113. package/lib/usage/usage-store.js.map +1 -0
  114. package/package.json +12 -12
@@ -0,0 +1,121 @@
1
+ import { createRootPrivateToken, encodeStorageIdentity } from '@zhin.js/plugin-runtime';
2
+ import { createHash } from 'node:crypto';
3
+ import { traceScopeTurnId } from '../trace/turn-trace-registry.js';
4
+ const text = { type: 'text', nullable: true };
5
+ const count = { type: 'bigint', nullable: true };
6
+ export const AGENT_USAGE_TURN_MODEL = {
7
+ id: { type: 'text', length: 64, primary: true }, fingerprint: { type: 'text', length: 64, nullable: false },
8
+ owner: text, trace_id: text, turn_id: text, session_key: text, principal_id: text,
9
+ origin_kind: text, endpoint_adapter: text, endpoint_id: text, conversation_kind: text, conversation_id: text,
10
+ parent_kind: text, parent_id: text, thread_id: text, source_id: text,
11
+ selected_provider: text, selected_model: text, status: text,
12
+ started_at: count, ended_at: count, duration_ms: count, call_count: count,
13
+ };
14
+ export const AGENT_USAGE_CALL_MODEL = {
15
+ id: { type: 'text', length: 64, primary: true }, root_id: { type: 'text', length: 64, nullable: false },
16
+ turn_id: text, call_index: { type: 'integer', nullable: false }, provider: text, model: text, stop_reason: text,
17
+ duration_ms: count, input_tokens: count, output_tokens: count, total_tokens: count,
18
+ cache_read_tokens: count, cache_write_tokens: count, reasoning_tokens: count, ended_at: count, root_ended_at: count,
19
+ };
20
+ export const agentUsageStoreToken = createRootPrivateToken('zhin.agent.usage-store', 'Generation-owned structured usage persistence');
21
+ const tokenFields = {
22
+ input_tokens: ['promptTokens', 'input'], output_tokens: ['completionTokens', 'output'], total_tokens: ['totalTokens', 'totalTokens'],
23
+ cache_read_tokens: ['cacheReadTokens', 'cacheRead'], cache_write_tokens: ['cacheWriteTokens', 'cacheWrite'], reasoning_tokens: ['reasoningTokens', 'reasoning'],
24
+ };
25
+ function nonnegative(value) { return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : null; }
26
+ const identity = (value) => value === undefined ? null : encodeStorageIdentity(value);
27
+ const digest = (value) => createHash('sha256').update(JSON.stringify(value)).digest('hex');
28
+ /** Minimal numeric facts, independent of log level and trace archive size limits. */
29
+ export class AgentUsageCollector {
30
+ owner;
31
+ request;
32
+ selected;
33
+ #calls = [];
34
+ #indexes = new Map();
35
+ #startedAt = Date.now();
36
+ #id;
37
+ constructor(owner, request, selected) {
38
+ this.owner = owner;
39
+ this.request = request;
40
+ this.selected = selected;
41
+ this.#id = digest([owner, request.identity.turnId]);
42
+ }
43
+ record(event) {
44
+ if (event.type !== 'usage')
45
+ return;
46
+ const turnId = traceScopeTurnId(event) ?? this.request.identity.turnId;
47
+ const index = (this.#indexes.get(turnId) ?? 0) + 1;
48
+ this.#indexes.set(turnId, index);
49
+ const row = { id: digest([this.#id, turnId, index]), root_id: this.#id, turn_id: identity(turnId), call_index: index,
50
+ provider: event.provider ?? null, model: event.model ?? null, stop_reason: event.stopReason ?? null,
51
+ duration_ms: typeof event.durationMs === 'number' ? nonnegative(Math.round(event.durationMs)) : null, ended_at: Date.now() };
52
+ for (const [column, [source, availability]] of Object.entries(tokenFields)) {
53
+ const value = nonnegative(event.usage[source]);
54
+ const reported = event.usage.reported?.[availability]
55
+ ?? (availability === 'input' || availability === 'output' || availability === 'totalTokens' ? value !== null && value > 0 : value !== null);
56
+ row[column] = reported ? value : null;
57
+ }
58
+ this.#calls.push(row);
59
+ }
60
+ finish(status) {
61
+ const endedAt = Date.now(), origin = this.request.origin, im = origin.kind === 'im' ? origin : undefined;
62
+ const turn = { id: this.#id, owner: identity(this.owner), trace_id: identity(this.request.identity.traceId), turn_id: identity(this.request.identity.turnId),
63
+ session_key: identity(this.request.session.key), principal_id: identity(this.request.principal.subjectId),
64
+ origin_kind: origin.kind, endpoint_adapter: identity(im?.conversation?.endpoint.adapter ?? im?.platform), endpoint_id: identity(im?.conversation?.endpoint.id ?? im?.endpoint),
65
+ conversation_kind: im?.conversation?.kind ?? im?.scope ?? null, conversation_id: identity(im?.conversation?.id ?? im?.sceneId), parent_kind: im?.conversation?.parent?.kind ?? null,
66
+ parent_id: identity(im?.conversation?.parent?.id), thread_id: identity(im?.conversation?.threadId ?? im?.threadId),
67
+ source_id: identity(origin.kind === 'schedule' ? origin.jobId : origin.kind === 'a2a' ? origin.taskId : origin.kind === 'http' ? origin.sessionId : origin.kind === 'internal' ? origin.source : undefined),
68
+ selected_provider: this.selected.provider, selected_model: this.selected.model, status,
69
+ started_at: this.#startedAt, ended_at: endedAt, duration_ms: endedAt - this.#startedAt, call_count: this.#calls.length,
70
+ };
71
+ const calls = this.#calls.map(row => ({ ...row, root_ended_at: endedAt }));
72
+ turn.fingerprint = digest([turn, calls]);
73
+ return { turn, calls };
74
+ }
75
+ }
76
+ /** Tables contain no payload JSON, chat content, prompt, tool arguments or error text. */
77
+ export class DatabaseAgentUsageStore {
78
+ database;
79
+ retentionDays;
80
+ now;
81
+ constructor(database, retentionDays = 30, now = Date.now) {
82
+ this.database = database;
83
+ this.retentionDays = retentionDays;
84
+ this.now = now;
85
+ if (!Number.isSafeInteger(retentionDays) || retentionDays <= 0)
86
+ throw new TypeError('ai.usage.retentionDays must be a positive integer');
87
+ if (!database.transaction)
88
+ throw new Error('Agent usage requires transactional database storage');
89
+ }
90
+ async save(record) {
91
+ try {
92
+ await this.database.transaction(async (transaction) => {
93
+ const turns = transaction.model('agent_usage_turns');
94
+ const existing = (await turns.select('fingerprint').where({ id: record.turn.id }))[0];
95
+ if (existing) {
96
+ if (existing.fingerprint !== record.turn.fingerprint)
97
+ throw new Error('Conflicting usage record for the same turn');
98
+ return;
99
+ }
100
+ await turns.insert(record.turn);
101
+ for (const call of record.calls)
102
+ await transaction.model('agent_usage_calls').insert(call);
103
+ });
104
+ }
105
+ catch (error) {
106
+ // A concurrent writer may have committed the same complete record.
107
+ const existing = (await this.database.models.get('agent_usage_turns').select('fingerprint').where({ id: record.turn.id }).limit(1))[0];
108
+ if (existing?.fingerprint === record.turn.fingerprint)
109
+ return;
110
+ throw error;
111
+ }
112
+ }
113
+ async prune() {
114
+ const cutoff = this.now() - this.retentionDays * 86_400_000;
115
+ await this.database.transaction(async (transaction) => {
116
+ await transaction.model('agent_usage_calls').delete().where({ root_ended_at: { $lt: cutoff } });
117
+ await transaction.model('agent_usage_turns').delete().where({ ended_at: { $lt: cutoff } });
118
+ });
119
+ }
120
+ }
121
+ //# sourceMappingURL=usage-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-store.js","sourceRoot":"","sources":["../../src/usage/usage-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { createRootPrivateToken, encodeStorageIdentity, type DatabaseHost } from '@zhin.js/plugin-runtime';\nimport { createHash } from 'node:crypto';\nimport type { TurnEvent } from '../event/turn-event.js';\nimport type { TurnRequest } from '../turn/turn-ingress.js';\nimport { traceScopeTurnId } from '../trace/turn-trace-registry.js';\n\nconst text = { type: 'text', nullable: true } as const;\nconst count = { type: 'bigint', nullable: true } as const;\nexport const AGENT_USAGE_TURN_MODEL = {\n id: { type: 'text', length: 64, primary: true }, fingerprint: { type: 'text', length: 64, nullable: false },\n owner: text, trace_id: text, turn_id: text, session_key: text, principal_id: text,\n origin_kind: text, endpoint_adapter: text, endpoint_id: text, conversation_kind: text, conversation_id: text,\n parent_kind: text, parent_id: text, thread_id: text, source_id: text,\n selected_provider: text, selected_model: text, status: text,\n started_at: count, ended_at: count, duration_ms: count, call_count: count,\n} as const;\nexport const AGENT_USAGE_CALL_MODEL = {\n id: { type: 'text', length: 64, primary: true }, root_id: { type: 'text', length: 64, nullable: false },\n turn_id: text, call_index: { type: 'integer', nullable: false }, provider: text, model: text, stop_reason: text,\n duration_ms: count, input_tokens: count, output_tokens: count, total_tokens: count,\n cache_read_tokens: count, cache_write_tokens: count, reasoning_tokens: count, ended_at: count, root_ended_at: count,\n} as const;\nexport type UsageRow = Record<string, string | number | null>;\nexport interface AgentUsageRecord { readonly turn: UsageRow; readonly calls: readonly UsageRow[] }\nexport interface AgentUsageStore { save(record: AgentUsageRecord): Promise<void>; prune(): Promise<void> }\nexport const agentUsageStoreToken = createRootPrivateToken<AgentUsageStore>('zhin.agent.usage-store', 'Generation-owned structured usage persistence');\n\nconst tokenFields = {\n input_tokens: ['promptTokens', 'input'], output_tokens: ['completionTokens', 'output'], total_tokens: ['totalTokens', 'totalTokens'],\n cache_read_tokens: ['cacheReadTokens', 'cacheRead'], cache_write_tokens: ['cacheWriteTokens', 'cacheWrite'], reasoning_tokens: ['reasoningTokens', 'reasoning'],\n} as const;\nfunction nonnegative(value: unknown): number | null { return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : null; }\nconst identity = (value: string | undefined) => value === undefined ? null : encodeStorageIdentity(value);\nconst digest = (value: unknown) => createHash('sha256').update(JSON.stringify(value)).digest('hex');\n\n/** Minimal numeric facts, independent of log level and trace archive size limits. */\nexport class AgentUsageCollector {\n readonly #calls: UsageRow[] = [];\n readonly #indexes = new Map<string, number>();\n readonly #startedAt = Date.now();\n readonly #id: string;\n constructor(private readonly owner: string, private readonly request: TurnRequest,\n private readonly selected: { provider: string; model: string }) {\n this.#id = digest([owner, request.identity.turnId]);\n }\n record(event: TurnEvent): void {\n if (event.type !== 'usage') return;\n const turnId = traceScopeTurnId(event) ?? this.request.identity.turnId;\n const index = (this.#indexes.get(turnId) ?? 0) + 1;\n this.#indexes.set(turnId, index);\n const row: UsageRow = { id: digest([this.#id, turnId, index]), root_id: this.#id, turn_id: identity(turnId), call_index: index,\n provider: event.provider ?? null, model: event.model ?? null, stop_reason: event.stopReason ?? null,\n duration_ms: typeof event.durationMs === 'number' ? nonnegative(Math.round(event.durationMs)) : null, ended_at: Date.now() };\n for (const [column, [source, availability]] of Object.entries(tokenFields)) {\n const value = nonnegative(event.usage[source]);\n const reported = event.usage.reported?.[availability]\n ?? (availability === 'input' || availability === 'output' || availability === 'totalTokens' ? value !== null && value > 0 : value !== null);\n row[column] = reported ? value : null;\n }\n this.#calls.push(row);\n }\n finish(status: string): AgentUsageRecord {\n const endedAt = Date.now(), origin = this.request.origin, im = origin.kind === 'im' ? origin : undefined;\n const turn: UsageRow = { id: this.#id, owner: identity(this.owner), trace_id: identity(this.request.identity.traceId), turn_id: identity(this.request.identity.turnId),\n session_key: identity(this.request.session.key), principal_id: identity(this.request.principal.subjectId),\n origin_kind: origin.kind, endpoint_adapter: identity(im?.conversation?.endpoint.adapter ?? im?.platform), endpoint_id: identity(im?.conversation?.endpoint.id ?? im?.endpoint),\n conversation_kind: im?.conversation?.kind ?? im?.scope ?? null, conversation_id: identity(im?.conversation?.id ?? im?.sceneId), parent_kind: im?.conversation?.parent?.kind ?? null,\n parent_id: identity(im?.conversation?.parent?.id), thread_id: identity(im?.conversation?.threadId ?? im?.threadId),\n source_id: identity(origin.kind === 'schedule' ? origin.jobId : origin.kind === 'a2a' ? origin.taskId : origin.kind === 'http' ? origin.sessionId : origin.kind === 'internal' ? origin.source : undefined),\n selected_provider: this.selected.provider, selected_model: this.selected.model, status,\n started_at: this.#startedAt, ended_at: endedAt, duration_ms: endedAt - this.#startedAt, call_count: this.#calls.length,\n };\n const calls = this.#calls.map(row => ({ ...row, root_ended_at: endedAt }));\n turn.fingerprint = digest([turn, calls]);\n return { turn, calls };\n }\n}\n\n/** Tables contain no payload JSON, chat content, prompt, tool arguments or error text. */\nexport class DatabaseAgentUsageStore implements AgentUsageStore {\n constructor(private readonly database: DatabaseHost, readonly retentionDays = 30, private readonly now = Date.now) {\n if (!Number.isSafeInteger(retentionDays) || retentionDays <= 0) throw new TypeError('ai.usage.retentionDays must be a positive integer');\n if (!database.transaction) throw new Error('Agent usage requires transactional database storage');\n }\n async save(record: AgentUsageRecord): Promise<void> {\n try {\n await this.database.transaction!(async transaction => {\n const turns = transaction.model('agent_usage_turns');\n const existing = (await turns.select('fingerprint').where({ id: record.turn.id }))[0];\n if (existing) {\n if (existing.fingerprint !== record.turn.fingerprint) throw new Error('Conflicting usage record for the same turn');\n return;\n }\n await turns.insert(record.turn);\n for (const call of record.calls) await transaction.model('agent_usage_calls').insert(call);\n });\n } catch (error) {\n // A concurrent writer may have committed the same complete record.\n const existing = (await this.database.models.get('agent_usage_turns')!.select('fingerprint').where({ id: record.turn.id }).limit(1))[0];\n if (existing?.fingerprint === record.turn.fingerprint) return;\n throw error;\n }\n }\n async prune(): Promise<void> {\n const cutoff = this.now() - this.retentionDays * 86_400_000;\n await this.database.transaction!(async transaction => {\n await transaction.model('agent_usage_calls').delete().where({ root_ended_at: { $lt: cutoff } });\n await transaction.model('agent_usage_turns').delete().where({ ended_at: { $lt: cutoff } });\n });\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/agent",
3
- "version": "1.1.39",
3
+ "version": "1.1.40",
4
4
  "description": "Zhin AI Agent — session, ZhinAgent, init; composes @zhin.js/core providers and tools",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -90,19 +90,19 @@
90
90
  "ajv": "^8.18.0",
91
91
  "he": "^1.2.0",
92
92
  "js-yaml": "^5.2.2",
93
- "@zhin.js/adapter": "1.1.21",
94
- "@zhin.js/agent-feature": "1.1.8",
95
- "@zhin.js/ai": "1.1.43",
96
- "@zhin.js/core": "1.1.49",
97
- "@zhin.js/im-contract": "1.1.5",
93
+ "@zhin.js/adapter": "1.1.22",
94
+ "@zhin.js/agent-feature": "1.1.9",
95
+ "@zhin.js/ai": "1.1.44",
96
+ "@zhin.js/core": "1.1.50",
97
+ "@zhin.js/im-contract": "1.1.6",
98
98
  "@zhin.js/kernel": "1.1.5",
99
99
  "@zhin.js/logger": "1.1.2",
100
- "@zhin.js/mcp-feature": "1.1.8",
101
- "@zhin.js/permission": "1.1.7",
102
- "@zhin.js/plugin-runtime": "1.1.15",
103
- "@zhin.js/prompt-section": "1.1.7",
104
- "@zhin.js/skill": "1.1.8",
105
- "@zhin.js/tool": "1.1.8"
100
+ "@zhin.js/mcp-feature": "1.1.9",
101
+ "@zhin.js/permission": "1.1.8",
102
+ "@zhin.js/plugin-runtime": "1.1.16",
103
+ "@zhin.js/prompt-section": "1.1.8",
104
+ "@zhin.js/skill": "1.1.9",
105
+ "@zhin.js/tool": "1.1.9"
106
106
  },
107
107
  "peerDependencies": {
108
108
  "@modelcontextprotocol/sdk": "^1.29.0",