@zhin.js/agent 1.1.39 → 1.1.41
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.
- package/lib/assistant/job-storage.d.ts +238 -0
- package/lib/assistant/job-storage.js +143 -0
- package/lib/assistant/job-storage.js.map +1 -0
- package/lib/assistant/job-store.d.ts +7 -0
- package/lib/assistant/job-store.js +41 -3
- package/lib/assistant/job-store.js.map +1 -1
- package/lib/config/perception-config.d.ts +9 -0
- package/lib/config/perception-config.js +27 -0
- package/lib/config/perception-config.js.map +1 -1
- package/lib/context/context-system.d.ts +1 -0
- package/lib/context/context-system.js +8 -8
- package/lib/context/context-system.js.map +1 -1
- package/lib/context/im-turn-context-adapter.js +2 -0
- package/lib/context/im-turn-context-adapter.js.map +1 -1
- package/lib/context/skill-context.d.ts +3 -0
- package/lib/context/skill-context.js +6 -0
- package/lib/context/skill-context.js.map +1 -0
- package/lib/context/turn-envelope.d.ts +4 -0
- package/lib/context/turn-envelope.js +24 -7
- package/lib/context/turn-envelope.js.map +1 -1
- package/lib/context/turn-user-message.d.ts +3 -1
- package/lib/context/turn-user-message.js +14 -1
- package/lib/context/turn-user-message.js.map +1 -1
- package/lib/core/agent-core-run.d.ts +1 -0
- package/lib/core/agent-core-run.js +47 -20
- package/lib/core/agent-core-run.js.map +1 -1
- package/lib/core/structured-reply-recovery.d.ts +6 -2
- package/lib/core/structured-reply-recovery.js +22 -8
- package/lib/core/structured-reply-recovery.js.map +1 -1
- package/lib/core/turn-completion-usage.d.ts +2 -1
- package/lib/core/turn-completion-usage.js +14 -5
- package/lib/core/turn-completion-usage.js.map +1 -1
- package/lib/core/turn-event-mapper.d.ts +1 -1
- package/lib/core/turn-event-mapper.js +8 -5
- package/lib/core/turn-event-mapper.js.map +1 -1
- package/lib/decision/config.d.ts +1 -1
- package/lib/decision/config.js +1 -1
- package/lib/decision/config.js.map +1 -1
- package/lib/decision/types.d.ts +1 -1
- package/lib/decision/types.js.map +1 -1
- package/lib/event/turn-event.d.ts +7 -1
- package/lib/event/turn-event.js.map +1 -1
- package/lib/index.d.ts +4 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/media/perception.d.ts +3 -4
- package/lib/media/perception.js +59 -37
- package/lib/media/perception.js.map +1 -1
- package/lib/memory/compaction-runtime.d.ts +3 -2
- package/lib/memory/compaction-runtime.js +22 -14
- package/lib/memory/compaction-runtime.js.map +1 -1
- package/lib/outbound/agent-output.js +2 -0
- package/lib/outbound/agent-output.js.map +1 -1
- package/lib/outbound/chat-failure.d.ts +27 -0
- package/lib/outbound/chat-failure.js +53 -0
- package/lib/outbound/chat-failure.js.map +1 -0
- package/lib/outbound/structured-reply.d.ts +7 -0
- package/lib/outbound/structured-reply.js +52 -16
- package/lib/outbound/structured-reply.js.map +1 -1
- package/lib/plugin-runtime/agent-runtime.js +49 -8
- package/lib/plugin-runtime/agent-runtime.js.map +1 -1
- package/lib/plugin-runtime/ai-completion.d.ts +4 -3
- package/lib/plugin-runtime/ai-completion.js +64 -10
- package/lib/plugin-runtime/ai-completion.js.map +1 -1
- package/lib/plugin-runtime/capability-ingress.js +3 -1
- package/lib/plugin-runtime/capability-ingress.js.map +1 -1
- package/lib/plugin-runtime/decision-runtime.d.ts +1 -0
- package/lib/plugin-runtime/deferred-capability-plan.d.ts +4 -1
- package/lib/plugin-runtime/deferred-capability-plan.js +25 -9
- package/lib/plugin-runtime/deferred-capability-plan.js.map +1 -1
- package/lib/plugin-runtime/full-agent-turn-engine.js +1 -0
- package/lib/plugin-runtime/full-agent-turn-engine.js.map +1 -1
- package/lib/plugin-runtime/index.d.ts +2 -0
- package/lib/plugin-runtime/index.js +2 -0
- package/lib/plugin-runtime/index.js.map +1 -1
- package/lib/prompt/assembly.js +0 -9
- package/lib/prompt/assembly.js.map +1 -1
- package/lib/prompt/git-context.js +1 -1
- package/lib/prompt/git-context.js.map +1 -1
- package/lib/prompt/rules/chat-rules.js +8 -9
- package/lib/prompt/rules/chat-rules.js.map +1 -1
- package/lib/prompt/rules/critical-rules.js +10 -10
- package/lib/prompt/rules/critical-rules.js.map +1 -1
- package/lib/prompt/system-prompt.js +4 -5
- package/lib/prompt/system-prompt.js.map +1 -1
- package/lib/security/file-role-policy.js +6 -6
- package/lib/security/file-role-policy.js.map +1 -1
- package/lib/security/policy-facade.js +1 -1
- package/lib/security/policy-facade.js.map +1 -1
- package/lib/security/usage-quota.d.ts +4 -0
- package/lib/security/usage-quota.js +23 -2
- package/lib/security/usage-quota.js.map +1 -1
- package/lib/session/approval-port.d.ts +2 -0
- package/lib/session/approval-port.js.map +1 -1
- package/lib/session/approval-review-agent.d.ts +1 -1
- package/lib/session/approval-review-agent.js +19 -4
- package/lib/session/approval-review-agent.js.map +1 -1
- package/lib/session/turn-ingress-session.js +7 -1
- package/lib/session/turn-ingress-session.js.map +1 -1
- package/lib/tool/tool-approval-gate.js +4 -2
- package/lib/tool/tool-approval-gate.js.map +1 -1
- package/lib/tool/turn-tool-runtime.js +24 -5
- package/lib/tool/turn-tool-runtime.js.map +1 -1
- package/lib/tool-catalog/deferred-turn-controller.js +1 -4
- package/lib/tool-catalog/deferred-turn-controller.js.map +1 -1
- package/lib/trace/context-budget.d.ts +4 -3
- package/lib/trace/context-budget.js +28 -8
- package/lib/trace/context-budget.js.map +1 -1
- package/lib/trace/turn-trace-format.js +4 -1
- package/lib/trace/turn-trace-format.js.map +1 -1
- package/lib/trace/turn-trace.d.ts +6 -0
- package/lib/trace/turn-trace.js +8 -2
- package/lib/trace/turn-trace.js.map +1 -1
- package/lib/trace/turn-usage-log.d.ts +14 -0
- package/lib/trace/turn-usage-log.js +83 -0
- package/lib/trace/turn-usage-log.js.map +1 -0
- package/lib/usage/usage-store.d.ts +199 -0
- package/lib/usage/usage-store.js +121 -0
- package/lib/usage/usage-store.js.map +1 -0
- package/package.json +12 -12
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { formatCompact } from '@zhin.js/logger';
|
|
2
|
+
const FIELDS = {
|
|
3
|
+
input: 'prompt_tokens', output: 'completion_tokens', totalTokens: 'total_tokens',
|
|
4
|
+
cacheRead: 'cache_read_tokens', cacheWrite: 'cache_write_tokens', reasoning: 'reasoning_tokens',
|
|
5
|
+
};
|
|
6
|
+
// Metadata can contain controls too; preserve one physical line and field boundaries.
|
|
7
|
+
function safe(value) {
|
|
8
|
+
return value.replace(/[\p{Cc}\u2028\u2029|]/gu, ch => `\\u${ch.charCodeAt(0).toString(16).padStart(4, '0')}`).slice(0, 240);
|
|
9
|
+
}
|
|
10
|
+
function known(call, field) {
|
|
11
|
+
const value = call[FIELDS[field]];
|
|
12
|
+
if (value === undefined || !Number.isFinite(value) || value < 0)
|
|
13
|
+
return undefined;
|
|
14
|
+
// Old records lack availability. Positive usage is evidence; normalized zero isn't.
|
|
15
|
+
const reported = call.reported?.[field] ?? (field === 'cacheRead' || field === 'cacheWrite' || field === 'reasoning'
|
|
16
|
+
? true : value > 0);
|
|
17
|
+
return reported ? value : undefined;
|
|
18
|
+
}
|
|
19
|
+
/** A partial sum is explicitly a lower bound, never an apparently complete zero. */
|
|
20
|
+
function sum(calls, field) {
|
|
21
|
+
if (!calls.length)
|
|
22
|
+
return 0;
|
|
23
|
+
const values = calls.map(call => known(call, field));
|
|
24
|
+
const present = values.filter((v) => v !== undefined);
|
|
25
|
+
if (!present.length)
|
|
26
|
+
return '?';
|
|
27
|
+
const total = present.reduce((a, b) => a + b, 0);
|
|
28
|
+
return present.length === calls.length ? total : `>=${total}`;
|
|
29
|
+
}
|
|
30
|
+
function tree(trace) {
|
|
31
|
+
return [trace, ...trace.subagents.flatMap(tree)];
|
|
32
|
+
}
|
|
33
|
+
function tokens(calls) {
|
|
34
|
+
return { input: sum(calls, 'input'), output: sum(calls, 'output'),
|
|
35
|
+
cache_read: sum(calls, 'cacheRead'), cache_write: sum(calls, 'cacheWrite'), reasoning: sum(calls, 'reasoning') };
|
|
36
|
+
}
|
|
37
|
+
export function formatTurnUsageLogs(trace, context, includeDetails = true) {
|
|
38
|
+
const traces = tree(trace);
|
|
39
|
+
const calls = traces.flatMap(item => item.model_calls);
|
|
40
|
+
const identity = { trace: safe(context.traceId), turn: safe(trace.turn_id) };
|
|
41
|
+
const models = [...new Set(calls.map(call => `${call.provider ?? '?'}/${call.model ?? '?'}`))];
|
|
42
|
+
const summary = formatCompact({ op: 'turn', ...identity, scene: safe(context.scene),
|
|
43
|
+
status: context.status ?? trace.termination.status,
|
|
44
|
+
model: safe(models.join(',') || context.selectedModel || '?'),
|
|
45
|
+
calls: calls.length, ...tokens(calls),
|
|
46
|
+
ms: trace.duration_ms === undefined ? '?' : Math.round(trace.duration_ms),
|
|
47
|
+
...(traces.length > 1 ? { subagent_calls: calls.length - trace.model_calls.length } : {}),
|
|
48
|
+
...((context.status ?? trace.termination.status) !== 'completed'
|
|
49
|
+
|| traces.some(item => item.dropped > 0 || item.termination.status !== 'completed')
|
|
50
|
+
? { partial: true } : {}),
|
|
51
|
+
});
|
|
52
|
+
const details = includeDetails ? traces.flatMap(item => {
|
|
53
|
+
const ids = { trace: identity.trace, turn: safe(item.turn_id) };
|
|
54
|
+
const lines = item.model_calls.map(call => formatCompact({ op: 'call', ...ids, index: call.index,
|
|
55
|
+
model: safe(`${call.provider ?? '?'}/${call.model ?? '?'}`), status: safe(call.stop_reason ?? '?'),
|
|
56
|
+
...tokens([call]), ms: call.duration_ms === undefined ? '?' : Math.round(call.duration_ms) }));
|
|
57
|
+
const budget = item.context_budget;
|
|
58
|
+
if (budget)
|
|
59
|
+
lines.push(formatCompact({ op: 'context', ...ids, request: 'first', unit: 'estimated_tokens',
|
|
60
|
+
total: budget.total_tokens, fixed: budget.fixed_tokens,
|
|
61
|
+
segments: Object.entries(budget.segments).map(([key, value]) => `${safe(key)}=${value}`).join(' '),
|
|
62
|
+
loaded_tools: budget.tools.loaded, deferred_tools: budget.tools.deferred }));
|
|
63
|
+
return lines;
|
|
64
|
+
}) : [];
|
|
65
|
+
return { summary, details };
|
|
66
|
+
}
|
|
67
|
+
/** Logging is a projection of the existing trace, never execution authority. */
|
|
68
|
+
export function logTurnUsage(logger, trace, context) {
|
|
69
|
+
try {
|
|
70
|
+
if (!logger.isLevelEnabled('info'))
|
|
71
|
+
return;
|
|
72
|
+
const debug = logger.isLevelEnabled('debug');
|
|
73
|
+
const lines = formatTurnUsageLogs(trace, context, debug);
|
|
74
|
+
logger.info(lines.summary);
|
|
75
|
+
if (debug)
|
|
76
|
+
for (const line of lines.details)
|
|
77
|
+
logger.debug(line);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// A transport/formatter failure must not change the outcome or skip trace persistence.
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=turn-usage-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-usage-log.js","sourceRoot":"","sources":["../../src/trace/turn-usage-log.ts"],"names":[],"mappings":"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","sourcesContent":["import { formatCompact, type Logger } from '@zhin.js/logger';\nimport type { TurnTrace, TurnTraceModelCall } from './turn-trace.js';\n\nexport interface UsageLogContext {\n traceId: string;\n scene: string;\n selectedModel?: string;\n status?: string;\n}\n\ntype Field = 'input' | 'output' | 'totalTokens' | 'cacheRead' | 'cacheWrite' | 'reasoning';\nconst FIELDS = {\n input: 'prompt_tokens', output: 'completion_tokens', totalTokens: 'total_tokens',\n cacheRead: 'cache_read_tokens', cacheWrite: 'cache_write_tokens', reasoning: 'reasoning_tokens',\n} as const;\n\n// Metadata can contain controls too; preserve one physical line and field boundaries.\nfunction safe(value: string): string {\n return value.replace(/[\\p{Cc}\\u2028\\u2029|]/gu, ch => `\\\\u${ch.charCodeAt(0).toString(16).padStart(4, '0')}`).slice(0, 240);\n}\n\nfunction known(call: TurnTraceModelCall, field: Field): number | undefined {\n const value = call[FIELDS[field]];\n if (value === undefined || !Number.isFinite(value) || value < 0) return undefined;\n // Old records lack availability. Positive usage is evidence; normalized zero isn't.\n const reported = call.reported?.[field] ?? (field === 'cacheRead' || field === 'cacheWrite' || field === 'reasoning'\n ? true : value > 0);\n return reported ? value : undefined;\n}\n\n/** A partial sum is explicitly a lower bound, never an apparently complete zero. */\nfunction sum(calls: readonly TurnTraceModelCall[], field: Field): number | string {\n if (!calls.length) return 0;\n const values = calls.map(call => known(call, field));\n const present = values.filter((v): v is number => v !== undefined);\n if (!present.length) return '?';\n const total = present.reduce((a, b) => a + b, 0);\n return present.length === calls.length ? total : `>=${total}`;\n}\n\nfunction tree(trace: TurnTrace): TurnTrace[] {\n return [trace, ...trace.subagents.flatMap(tree)];\n}\n\nfunction tokens(calls: readonly TurnTraceModelCall[]) {\n return { input: sum(calls, 'input'), output: sum(calls, 'output'),\n cache_read: sum(calls, 'cacheRead'), cache_write: sum(calls, 'cacheWrite'), reasoning: sum(calls, 'reasoning') };\n}\n\nexport function formatTurnUsageLogs(trace: TurnTrace, context: UsageLogContext, includeDetails = true): { summary: string; details: string[] } {\n const traces = tree(trace);\n const calls = traces.flatMap(item => item.model_calls);\n const identity = { trace: safe(context.traceId), turn: safe(trace.turn_id) };\n const models = [...new Set(calls.map(call => `${call.provider ?? '?'}/${call.model ?? '?'}`))];\n const summary = formatCompact({ op: 'turn', ...identity, scene: safe(context.scene),\n status: context.status ?? trace.termination.status,\n model: safe(models.join(',') || context.selectedModel || '?'),\n calls: calls.length, ...tokens(calls),\n ms: trace.duration_ms === undefined ? '?' : Math.round(trace.duration_ms),\n ...(traces.length > 1 ? { subagent_calls: calls.length - trace.model_calls.length } : {}),\n ...((context.status ?? trace.termination.status) !== 'completed'\n || traces.some(item => item.dropped > 0 || item.termination.status !== 'completed')\n ? { partial: true } : {}),\n });\n const details = includeDetails ? traces.flatMap(item => {\n const ids = { trace: identity.trace, turn: safe(item.turn_id) };\n const lines = item.model_calls.map(call => formatCompact({ op: 'call', ...ids, index: call.index,\n model: safe(`${call.provider ?? '?'}/${call.model ?? '?'}`), status: safe(call.stop_reason ?? '?'),\n ...tokens([call]), ms: call.duration_ms === undefined ? '?' : Math.round(call.duration_ms) }));\n const budget = item.context_budget;\n if (budget) lines.push(formatCompact({ op: 'context', ...ids, request: 'first', unit: 'estimated_tokens',\n total: budget.total_tokens, fixed: budget.fixed_tokens,\n segments: Object.entries(budget.segments).map(([key, value]) => `${safe(key)}=${value}`).join(' '),\n loaded_tools: budget.tools.loaded, deferred_tools: budget.tools.deferred }));\n return lines;\n }) : [];\n return { summary, details };\n}\n\n/** Logging is a projection of the existing trace, never execution authority. */\nexport function logTurnUsage(logger: Pick<Logger, 'info' | 'debug' | 'isLevelEnabled'>, trace: TurnTrace, context: UsageLogContext): void {\n try {\n if (!logger.isLevelEnabled('info')) return;\n const debug = logger.isLevelEnabled('debug');\n const lines = formatTurnUsageLogs(trace, context, debug);\n logger.info(lines.summary);\n if (debug) for (const line of lines.details) logger.debug(line);\n } catch {\n // A transport/formatter failure must not change the outcome or skip trace persistence.\n }\n}\n"]}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { type DatabaseHost } from '@zhin.js/plugin-runtime';
|
|
2
|
+
import type { TurnEvent } from '../event/turn-event.js';
|
|
3
|
+
import type { TurnRequest } from '../turn/turn-ingress.js';
|
|
4
|
+
export declare const AGENT_USAGE_TURN_MODEL: {
|
|
5
|
+
readonly id: {
|
|
6
|
+
readonly type: "text";
|
|
7
|
+
readonly length: 64;
|
|
8
|
+
readonly primary: true;
|
|
9
|
+
};
|
|
10
|
+
readonly fingerprint: {
|
|
11
|
+
readonly type: "text";
|
|
12
|
+
readonly length: 64;
|
|
13
|
+
readonly nullable: false;
|
|
14
|
+
};
|
|
15
|
+
readonly owner: {
|
|
16
|
+
readonly type: "text";
|
|
17
|
+
readonly nullable: true;
|
|
18
|
+
};
|
|
19
|
+
readonly trace_id: {
|
|
20
|
+
readonly type: "text";
|
|
21
|
+
readonly nullable: true;
|
|
22
|
+
};
|
|
23
|
+
readonly turn_id: {
|
|
24
|
+
readonly type: "text";
|
|
25
|
+
readonly nullable: true;
|
|
26
|
+
};
|
|
27
|
+
readonly session_key: {
|
|
28
|
+
readonly type: "text";
|
|
29
|
+
readonly nullable: true;
|
|
30
|
+
};
|
|
31
|
+
readonly principal_id: {
|
|
32
|
+
readonly type: "text";
|
|
33
|
+
readonly nullable: true;
|
|
34
|
+
};
|
|
35
|
+
readonly origin_kind: {
|
|
36
|
+
readonly type: "text";
|
|
37
|
+
readonly nullable: true;
|
|
38
|
+
};
|
|
39
|
+
readonly endpoint_adapter: {
|
|
40
|
+
readonly type: "text";
|
|
41
|
+
readonly nullable: true;
|
|
42
|
+
};
|
|
43
|
+
readonly endpoint_id: {
|
|
44
|
+
readonly type: "text";
|
|
45
|
+
readonly nullable: true;
|
|
46
|
+
};
|
|
47
|
+
readonly conversation_kind: {
|
|
48
|
+
readonly type: "text";
|
|
49
|
+
readonly nullable: true;
|
|
50
|
+
};
|
|
51
|
+
readonly conversation_id: {
|
|
52
|
+
readonly type: "text";
|
|
53
|
+
readonly nullable: true;
|
|
54
|
+
};
|
|
55
|
+
readonly parent_kind: {
|
|
56
|
+
readonly type: "text";
|
|
57
|
+
readonly nullable: true;
|
|
58
|
+
};
|
|
59
|
+
readonly parent_id: {
|
|
60
|
+
readonly type: "text";
|
|
61
|
+
readonly nullable: true;
|
|
62
|
+
};
|
|
63
|
+
readonly thread_id: {
|
|
64
|
+
readonly type: "text";
|
|
65
|
+
readonly nullable: true;
|
|
66
|
+
};
|
|
67
|
+
readonly source_id: {
|
|
68
|
+
readonly type: "text";
|
|
69
|
+
readonly nullable: true;
|
|
70
|
+
};
|
|
71
|
+
readonly selected_provider: {
|
|
72
|
+
readonly type: "text";
|
|
73
|
+
readonly nullable: true;
|
|
74
|
+
};
|
|
75
|
+
readonly selected_model: {
|
|
76
|
+
readonly type: "text";
|
|
77
|
+
readonly nullable: true;
|
|
78
|
+
};
|
|
79
|
+
readonly status: {
|
|
80
|
+
readonly type: "text";
|
|
81
|
+
readonly nullable: true;
|
|
82
|
+
};
|
|
83
|
+
readonly started_at: {
|
|
84
|
+
readonly type: "bigint";
|
|
85
|
+
readonly nullable: true;
|
|
86
|
+
};
|
|
87
|
+
readonly ended_at: {
|
|
88
|
+
readonly type: "bigint";
|
|
89
|
+
readonly nullable: true;
|
|
90
|
+
};
|
|
91
|
+
readonly duration_ms: {
|
|
92
|
+
readonly type: "bigint";
|
|
93
|
+
readonly nullable: true;
|
|
94
|
+
};
|
|
95
|
+
readonly call_count: {
|
|
96
|
+
readonly type: "bigint";
|
|
97
|
+
readonly nullable: true;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
export declare const AGENT_USAGE_CALL_MODEL: {
|
|
101
|
+
readonly id: {
|
|
102
|
+
readonly type: "text";
|
|
103
|
+
readonly length: 64;
|
|
104
|
+
readonly primary: true;
|
|
105
|
+
};
|
|
106
|
+
readonly root_id: {
|
|
107
|
+
readonly type: "text";
|
|
108
|
+
readonly length: 64;
|
|
109
|
+
readonly nullable: false;
|
|
110
|
+
};
|
|
111
|
+
readonly turn_id: {
|
|
112
|
+
readonly type: "text";
|
|
113
|
+
readonly nullable: true;
|
|
114
|
+
};
|
|
115
|
+
readonly call_index: {
|
|
116
|
+
readonly type: "integer";
|
|
117
|
+
readonly nullable: false;
|
|
118
|
+
};
|
|
119
|
+
readonly provider: {
|
|
120
|
+
readonly type: "text";
|
|
121
|
+
readonly nullable: true;
|
|
122
|
+
};
|
|
123
|
+
readonly model: {
|
|
124
|
+
readonly type: "text";
|
|
125
|
+
readonly nullable: true;
|
|
126
|
+
};
|
|
127
|
+
readonly stop_reason: {
|
|
128
|
+
readonly type: "text";
|
|
129
|
+
readonly nullable: true;
|
|
130
|
+
};
|
|
131
|
+
readonly duration_ms: {
|
|
132
|
+
readonly type: "bigint";
|
|
133
|
+
readonly nullable: true;
|
|
134
|
+
};
|
|
135
|
+
readonly input_tokens: {
|
|
136
|
+
readonly type: "bigint";
|
|
137
|
+
readonly nullable: true;
|
|
138
|
+
};
|
|
139
|
+
readonly output_tokens: {
|
|
140
|
+
readonly type: "bigint";
|
|
141
|
+
readonly nullable: true;
|
|
142
|
+
};
|
|
143
|
+
readonly total_tokens: {
|
|
144
|
+
readonly type: "bigint";
|
|
145
|
+
readonly nullable: true;
|
|
146
|
+
};
|
|
147
|
+
readonly cache_read_tokens: {
|
|
148
|
+
readonly type: "bigint";
|
|
149
|
+
readonly nullable: true;
|
|
150
|
+
};
|
|
151
|
+
readonly cache_write_tokens: {
|
|
152
|
+
readonly type: "bigint";
|
|
153
|
+
readonly nullable: true;
|
|
154
|
+
};
|
|
155
|
+
readonly reasoning_tokens: {
|
|
156
|
+
readonly type: "bigint";
|
|
157
|
+
readonly nullable: true;
|
|
158
|
+
};
|
|
159
|
+
readonly ended_at: {
|
|
160
|
+
readonly type: "bigint";
|
|
161
|
+
readonly nullable: true;
|
|
162
|
+
};
|
|
163
|
+
readonly root_ended_at: {
|
|
164
|
+
readonly type: "bigint";
|
|
165
|
+
readonly nullable: true;
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
export type UsageRow = Record<string, string | number | null>;
|
|
169
|
+
export interface AgentUsageRecord {
|
|
170
|
+
readonly turn: UsageRow;
|
|
171
|
+
readonly calls: readonly UsageRow[];
|
|
172
|
+
}
|
|
173
|
+
export interface AgentUsageStore {
|
|
174
|
+
save(record: AgentUsageRecord): Promise<void>;
|
|
175
|
+
prune(): Promise<void>;
|
|
176
|
+
}
|
|
177
|
+
export declare const agentUsageStoreToken: import("@zhin.js/plugin-runtime").Token<AgentUsageStore>;
|
|
178
|
+
/** Minimal numeric facts, independent of log level and trace archive size limits. */
|
|
179
|
+
export declare class AgentUsageCollector {
|
|
180
|
+
#private;
|
|
181
|
+
private readonly owner;
|
|
182
|
+
private readonly request;
|
|
183
|
+
private readonly selected;
|
|
184
|
+
constructor(owner: string, request: TurnRequest, selected: {
|
|
185
|
+
provider: string;
|
|
186
|
+
model: string;
|
|
187
|
+
});
|
|
188
|
+
record(event: TurnEvent): void;
|
|
189
|
+
finish(status: string): AgentUsageRecord;
|
|
190
|
+
}
|
|
191
|
+
/** Tables contain no payload JSON, chat content, prompt, tool arguments or error text. */
|
|
192
|
+
export declare class DatabaseAgentUsageStore implements AgentUsageStore {
|
|
193
|
+
private readonly database;
|
|
194
|
+
readonly retentionDays: number;
|
|
195
|
+
private readonly now;
|
|
196
|
+
constructor(database: DatabaseHost, retentionDays?: number, now?: () => number);
|
|
197
|
+
save(record: AgentUsageRecord): Promise<void>;
|
|
198
|
+
prune(): Promise<void>;
|
|
199
|
+
}
|
|
@@ -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.
|
|
3
|
+
"version": "1.1.41",
|
|
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.
|
|
94
|
-
"@zhin.js/agent-feature": "1.1.
|
|
95
|
-
"@zhin.js/ai": "1.1.
|
|
96
|
-
"@zhin.js/core": "1.1.
|
|
97
|
-
"@zhin.js/im-contract": "1.1.
|
|
93
|
+
"@zhin.js/adapter": "1.1.22",
|
|
94
|
+
"@zhin.js/agent-feature": "1.1.9",
|
|
95
|
+
"@zhin.js/ai": "1.1.45",
|
|
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.
|
|
101
|
-
"@zhin.js/permission": "1.1.
|
|
102
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
103
|
-
"@zhin.js/prompt-section": "1.1.
|
|
104
|
-
"@zhin.js/skill": "1.1.
|
|
105
|
-
"@zhin.js/tool": "1.1.
|
|
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",
|