condukt 0.26.0 → 0.27.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.
- package/dist/runtimes/claude/bridge-cache.d.ts +58 -0
- package/dist/runtimes/claude/bridge-cache.d.ts.map +1 -0
- package/dist/runtimes/claude/bridge-cache.js +257 -0
- package/dist/runtimes/claude/bridge-cache.js.map +1 -0
- package/dist/runtimes/claude/claude-runtime.d.ts +30 -0
- package/dist/runtimes/claude/claude-runtime.d.ts.map +1 -0
- package/dist/runtimes/claude/claude-runtime.js +88 -0
- package/dist/runtimes/claude/claude-runtime.js.map +1 -0
- package/dist/runtimes/claude/claude-session.d.ts +154 -0
- package/dist/runtimes/claude/claude-session.d.ts.map +1 -0
- package/dist/runtimes/claude/claude-session.js +851 -0
- package/dist/runtimes/claude/claude-session.js.map +1 -0
- package/dist/runtimes/claude/config-mapper.d.ts +69 -0
- package/dist/runtimes/claude/config-mapper.d.ts.map +1 -0
- package/dist/runtimes/claude/config-mapper.js +458 -0
- package/dist/runtimes/claude/config-mapper.js.map +1 -0
- package/dist/runtimes/claude/epoch-store.d.ts +28 -0
- package/dist/runtimes/claude/epoch-store.d.ts.map +1 -0
- package/dist/runtimes/claude/epoch-store.js +199 -0
- package/dist/runtimes/claude/epoch-store.js.map +1 -0
- package/dist/runtimes/claude/fallback-runtime.d.ts +14 -0
- package/dist/runtimes/claude/fallback-runtime.d.ts.map +1 -0
- package/dist/runtimes/claude/fallback-runtime.js +64 -0
- package/dist/runtimes/claude/fallback-runtime.js.map +1 -0
- package/dist/runtimes/claude/index.d.ts +24 -0
- package/dist/runtimes/claude/index.d.ts.map +1 -0
- package/dist/runtimes/claude/index.js +61 -0
- package/dist/runtimes/claude/index.js.map +1 -0
- package/dist/runtimes/claude/message-mapper.d.ts +135 -0
- package/dist/runtimes/claude/message-mapper.d.ts.map +1 -0
- package/dist/runtimes/claude/message-mapper.js +504 -0
- package/dist/runtimes/claude/message-mapper.js.map +1 -0
- package/dist/runtimes/claude/one-shot.d.ts +48 -0
- package/dist/runtimes/claude/one-shot.d.ts.map +1 -0
- package/dist/runtimes/claude/one-shot.js +186 -0
- package/dist/runtimes/claude/one-shot.js.map +1 -0
- package/dist/runtimes/claude/protocol-bridge.d.ts +138 -0
- package/dist/runtimes/claude/protocol-bridge.d.ts.map +1 -0
- package/dist/runtimes/claude/protocol-bridge.js +1170 -0
- package/dist/runtimes/claude/protocol-bridge.js.map +1 -0
- package/dist/runtimes/claude/protocol-router.d.ts +113 -0
- package/dist/runtimes/claude/protocol-router.d.ts.map +1 -0
- package/dist/runtimes/claude/protocol-router.js +514 -0
- package/dist/runtimes/claude/protocol-router.js.map +1 -0
- package/dist/runtimes/claude/session-epoch.d.ts +156 -0
- package/dist/runtimes/claude/session-epoch.d.ts.map +1 -0
- package/dist/runtimes/claude/session-epoch.js +365 -0
- package/dist/runtimes/claude/session-epoch.js.map +1 -0
- package/dist/runtimes/claude/session-recovery.d.ts +78 -0
- package/dist/runtimes/claude/session-recovery.d.ts.map +1 -0
- package/dist/runtimes/claude/session-recovery.js +259 -0
- package/dist/runtimes/claude/session-recovery.js.map +1 -0
- package/dist/runtimes/claude/sse.d.ts +16 -0
- package/dist/runtimes/claude/sse.d.ts.map +1 -0
- package/dist/runtimes/claude/sse.js +46 -0
- package/dist/runtimes/claude/sse.js.map +1 -0
- package/dist/runtimes/claude/translate-chat.d.ts +106 -0
- package/dist/runtimes/claude/translate-chat.d.ts.map +1 -0
- package/dist/runtimes/claude/translate-chat.js +857 -0
- package/dist/runtimes/claude/translate-chat.js.map +1 -0
- package/dist/runtimes/claude/translate-responses.d.ts +76 -0
- package/dist/runtimes/claude/translate-responses.d.ts.map +1 -0
- package/dist/runtimes/claude/translate-responses.js +625 -0
- package/dist/runtimes/claude/translate-responses.js.map +1 -0
- package/dist/runtimes/claude/usage-ledger.d.ts +108 -0
- package/dist/runtimes/claude/usage-ledger.d.ts.map +1 -0
- package/dist/runtimes/claude/usage-ledger.js +189 -0
- package/dist/runtimes/claude/usage-ledger.js.map +1 -0
- package/dist/runtimes/copilot/sdk-backend.d.ts.map +1 -1
- package/dist/runtimes/copilot/sdk-backend.js +15 -104
- package/dist/runtimes/copilot/sdk-backend.js.map +1 -1
- package/dist/runtimes/mcp-env.d.ts +24 -0
- package/dist/runtimes/mcp-env.d.ts.map +1 -0
- package/dist/runtimes/mcp-env.js +81 -0
- package/dist/runtimes/mcp-env.js.map +1 -0
- package/dist/src/session-recovery-policy.d.ts +19 -0
- package/dist/src/session-recovery-policy.d.ts.map +1 -0
- package/dist/src/session-recovery-policy.js +43 -0
- package/dist/src/session-recovery-policy.js.map +1 -0
- package/dist/src/types.d.ts +45 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/package.json +20 -4
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token accounting for the Claude Agent SDK.
|
|
3
|
+
*
|
|
4
|
+
* The SDK exposes three usage surfaces, and only one is authoritative:
|
|
5
|
+
*
|
|
6
|
+
* - `assistant.message.usage` — the API's non-final, per-block snapshot. It
|
|
7
|
+
* must never be summed (doing so double-counts every streamed turn) and it
|
|
8
|
+
* carries no subagent attribution.
|
|
9
|
+
* - `result.usage` — the main agent loop only, excluding subagents, sidechains,
|
|
10
|
+
* compaction and Workflow calls.
|
|
11
|
+
* - `result.modelUsage` — per-model totals for every call made through the
|
|
12
|
+
* query pipeline, subagents included. These totals are **cumulative**: each
|
|
13
|
+
* result carries the running total for the session so far, and a resumed or
|
|
14
|
+
* forked session continues from the totals its transcript saved.
|
|
15
|
+
*
|
|
16
|
+
* condukt's usage events are per-turn charges (the scheduler sums them), so
|
|
17
|
+
* this ledger differences successive cumulative snapshots. It also seeds a
|
|
18
|
+
* baseline when the first result after a resume arrives with no earlier
|
|
19
|
+
* snapshot in this process — the transcript, not this process, holds the
|
|
20
|
+
* earlier turns, and reporting the whole cumulative total as one turn would
|
|
21
|
+
* overstate the node by the entire session.
|
|
22
|
+
*/
|
|
23
|
+
import type { ModelUsage } from '@anthropic-ai/claude-agent-sdk';
|
|
24
|
+
/** Per-model token counts in condukt's vocabulary. */
|
|
25
|
+
export interface TokenCounts {
|
|
26
|
+
/** Prompt tokens, including cache reads and cache writes. */
|
|
27
|
+
readonly inputTokens: number;
|
|
28
|
+
readonly outputTokens: number;
|
|
29
|
+
readonly cacheReadTokens: number;
|
|
30
|
+
readonly cacheWriteTokens: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* One `node:usage` payload. Extends the emitter's `Record<string, unknown>`
|
|
34
|
+
* contract so `agent.ts` can consume it without a cast.
|
|
35
|
+
*/
|
|
36
|
+
export interface ClaudeUsageRecord extends Record<string, unknown> {
|
|
37
|
+
readonly inputTokens: number;
|
|
38
|
+
readonly outputTokens: number;
|
|
39
|
+
readonly totalTokens: number;
|
|
40
|
+
readonly cacheReadTokens: number;
|
|
41
|
+
readonly cacheWriteTokens: number;
|
|
42
|
+
readonly model: string;
|
|
43
|
+
readonly apiCallId: string;
|
|
44
|
+
/** Wall-clock duration of the whole turn, in milliseconds (result-level). */
|
|
45
|
+
readonly duration?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface ModelUsageObservation {
|
|
48
|
+
/** `result.uuid`; combined with the model for a stable dedupe key. */
|
|
49
|
+
readonly uuid: string;
|
|
50
|
+
/** `result.duration_ms`. The SDK does not break duration down per model. */
|
|
51
|
+
readonly durationMs?: number;
|
|
52
|
+
/** `result.modelUsage` (camelCase, cumulative). */
|
|
53
|
+
readonly modelUsage: Readonly<Record<string, Partial<ModelUsage>>> | undefined;
|
|
54
|
+
/** `result.usage` (snake_case, main loop only, per turn). */
|
|
55
|
+
readonly mainLoopUsage: Readonly<Record<string, unknown>> | undefined;
|
|
56
|
+
/** Model of the latest assistant frame, when one was seen. */
|
|
57
|
+
readonly mainLoopModel?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface ModelUsageObservationContext {
|
|
60
|
+
/**
|
|
61
|
+
* Whether this turn continued an existing session (`resume`). Only used for
|
|
62
|
+
* the very first observation, where it decides between "this snapshot is the
|
|
63
|
+
* turn" and "this snapshot is the session baseline".
|
|
64
|
+
*/
|
|
65
|
+
readonly resumed: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Differences the SDK's cumulative `modelUsage` snapshots into per-turn usage.
|
|
69
|
+
*
|
|
70
|
+
* One ledger lives for the whole session: turn N's delta is turn N's snapshot
|
|
71
|
+
* minus turn N-1's. A zeroed/empty error result never overwrites the baseline,
|
|
72
|
+
* so a crash cannot resurrect already-billed tokens on the next turn.
|
|
73
|
+
*/
|
|
74
|
+
export declare class ModelUsageLedger {
|
|
75
|
+
private snapshot;
|
|
76
|
+
/**
|
|
77
|
+
* Turns that billed each model. The session cannot derive this from the
|
|
78
|
+
* snapshot alone, and reporting the session's total turn count against
|
|
79
|
+
* every model would claim requests a model never served.
|
|
80
|
+
*/
|
|
81
|
+
private readonly requestCounts;
|
|
82
|
+
private readonly fallbackModel;
|
|
83
|
+
constructor(options?: {
|
|
84
|
+
readonly fallbackModel?: string;
|
|
85
|
+
});
|
|
86
|
+
/**
|
|
87
|
+
* Records one result and returns the usage records for the turn it closed.
|
|
88
|
+
*
|
|
89
|
+
* - Fresh session, first result: the snapshot *is* this turn.
|
|
90
|
+
* - Resumed session with no earlier snapshot (process restart): seed the
|
|
91
|
+
* baseline from the snapshot and report only the main-loop usage, which is
|
|
92
|
+
* per-turn. Subagent tokens from this one turn stay unattributed until the
|
|
93
|
+
* next result's delta; the plan records this gap rather than inventing a
|
|
94
|
+
* number.
|
|
95
|
+
* - Later results: per-model deltas, clamped at zero so a mid-session
|
|
96
|
+
* `/clear` (which resets the running total) cannot produce negative usage.
|
|
97
|
+
*/
|
|
98
|
+
observe(observation: ModelUsageObservation, context: ModelUsageObservationContext): ClaudeUsageRecord[];
|
|
99
|
+
/** Cumulative per-model totals as of the last non-zeroed result. */
|
|
100
|
+
getSnapshot(): ReadonlyMap<string, TokenCounts>;
|
|
101
|
+
/** Turns that produced usage for each model. */
|
|
102
|
+
getRequestCounts(): ReadonlyMap<string, number>;
|
|
103
|
+
/** True once at least one result with real usage has been observed. */
|
|
104
|
+
hasUsage(): boolean;
|
|
105
|
+
private record;
|
|
106
|
+
private attributeMainLoop;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=usage-ledger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-ledger.d.ts","sourceRoot":"","sources":["../../../runtimes/claude/usage-ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAEjE,sDAAsD;AACtD,MAAM,WAAW,WAAW;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAkB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAChE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,qBAAqB;IACpC,sEAAsE;IACtE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAC/E,6DAA6D;IAC7D,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IACtE,8DAA8D;IAC9D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AA0ED;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAyC;IACzD;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE;IAIzD;;;;;;;;;;;OAWG;IACH,OAAO,CACL,WAAW,EAAE,qBAAqB,EAClC,OAAO,EAAE,4BAA4B,GACpC,iBAAiB,EAAE;IA2DtB,oEAAoE;IACpE,WAAW,IAAI,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC;IAI/C,gDAAgD;IAChD,gBAAgB,IAAI,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;IAI/C,uEAAuE;IACvE,QAAQ,IAAI,OAAO;IAInB,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,iBAAiB;CAW1B"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Token accounting for the Claude Agent SDK.
|
|
4
|
+
*
|
|
5
|
+
* The SDK exposes three usage surfaces, and only one is authoritative:
|
|
6
|
+
*
|
|
7
|
+
* - `assistant.message.usage` — the API's non-final, per-block snapshot. It
|
|
8
|
+
* must never be summed (doing so double-counts every streamed turn) and it
|
|
9
|
+
* carries no subagent attribution.
|
|
10
|
+
* - `result.usage` — the main agent loop only, excluding subagents, sidechains,
|
|
11
|
+
* compaction and Workflow calls.
|
|
12
|
+
* - `result.modelUsage` — per-model totals for every call made through the
|
|
13
|
+
* query pipeline, subagents included. These totals are **cumulative**: each
|
|
14
|
+
* result carries the running total for the session so far, and a resumed or
|
|
15
|
+
* forked session continues from the totals its transcript saved.
|
|
16
|
+
*
|
|
17
|
+
* condukt's usage events are per-turn charges (the scheduler sums them), so
|
|
18
|
+
* this ledger differences successive cumulative snapshots. It also seeds a
|
|
19
|
+
* baseline when the first result after a resume arrives with no earlier
|
|
20
|
+
* snapshot in this process — the transcript, not this process, holds the
|
|
21
|
+
* earlier turns, and reporting the whole cumulative total as one turn would
|
|
22
|
+
* overstate the node by the entire session.
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.ModelUsageLedger = void 0;
|
|
26
|
+
const ZERO = Object.freeze({
|
|
27
|
+
inputTokens: 0,
|
|
28
|
+
outputTokens: 0,
|
|
29
|
+
cacheReadTokens: 0,
|
|
30
|
+
cacheWriteTokens: 0,
|
|
31
|
+
});
|
|
32
|
+
let fallbackSequence = 0;
|
|
33
|
+
function count(value) {
|
|
34
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : 0;
|
|
35
|
+
}
|
|
36
|
+
function totalOf(counts) {
|
|
37
|
+
return counts.inputTokens + counts.outputTokens;
|
|
38
|
+
}
|
|
39
|
+
function countsFromModelUsage(usage) {
|
|
40
|
+
return {
|
|
41
|
+
// ModelUsage.inputTokens excludes cached prompt tokens; condukt's
|
|
42
|
+
// `inputTokens` is the whole prompt.
|
|
43
|
+
inputTokens: count(usage?.inputTokens)
|
|
44
|
+
+ count(usage?.cacheReadInputTokens)
|
|
45
|
+
+ count(usage?.cacheCreationInputTokens),
|
|
46
|
+
outputTokens: count(usage?.outputTokens),
|
|
47
|
+
cacheReadTokens: count(usage?.cacheReadInputTokens),
|
|
48
|
+
cacheWriteTokens: count(usage?.cacheCreationInputTokens),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** Reads the main-loop `result.usage`, accepting either spelling. */
|
|
52
|
+
function countsFromMainLoop(usage) {
|
|
53
|
+
if (usage === undefined)
|
|
54
|
+
return ZERO;
|
|
55
|
+
const cacheRead = count(usage['cache_read_input_tokens'] ?? usage['cacheReadTokens']);
|
|
56
|
+
const cacheWrite = count(usage['cache_creation_input_tokens'] ?? usage['cacheWriteTokens']);
|
|
57
|
+
return {
|
|
58
|
+
inputTokens: count(usage['input_tokens'] ?? usage['inputTokens']) + cacheRead + cacheWrite,
|
|
59
|
+
outputTokens: count(usage['output_tokens'] ?? usage['outputTokens']),
|
|
60
|
+
cacheReadTokens: cacheRead,
|
|
61
|
+
cacheWriteTokens: cacheWrite,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function subtractClamped(cumulative, previous) {
|
|
65
|
+
return {
|
|
66
|
+
inputTokens: Math.max(0, cumulative.inputTokens - previous.inputTokens),
|
|
67
|
+
outputTokens: Math.max(0, cumulative.outputTokens - previous.outputTokens),
|
|
68
|
+
cacheReadTokens: Math.max(0, cumulative.cacheReadTokens - previous.cacheReadTokens),
|
|
69
|
+
cacheWriteTokens: Math.max(0, cumulative.cacheWriteTokens - previous.cacheWriteTokens),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function toRecord(model, counts, uuid, durationMs) {
|
|
73
|
+
const callId = uuid.length > 0 ? `${uuid}:${model}` : `claude-${Date.now().toString(36)}-${++fallbackSequence}`;
|
|
74
|
+
return {
|
|
75
|
+
inputTokens: counts.inputTokens,
|
|
76
|
+
outputTokens: counts.outputTokens,
|
|
77
|
+
totalTokens: counts.inputTokens + counts.outputTokens,
|
|
78
|
+
cacheReadTokens: counts.cacheReadTokens,
|
|
79
|
+
cacheWriteTokens: counts.cacheWriteTokens,
|
|
80
|
+
model,
|
|
81
|
+
apiCallId: callId,
|
|
82
|
+
...(durationMs !== undefined ? { duration: durationMs } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Differences the SDK's cumulative `modelUsage` snapshots into per-turn usage.
|
|
87
|
+
*
|
|
88
|
+
* One ledger lives for the whole session: turn N's delta is turn N's snapshot
|
|
89
|
+
* minus turn N-1's. A zeroed/empty error result never overwrites the baseline,
|
|
90
|
+
* so a crash cannot resurrect already-billed tokens on the next turn.
|
|
91
|
+
*/
|
|
92
|
+
class ModelUsageLedger {
|
|
93
|
+
snapshot = null;
|
|
94
|
+
/**
|
|
95
|
+
* Turns that billed each model. The session cannot derive this from the
|
|
96
|
+
* snapshot alone, and reporting the session's total turn count against
|
|
97
|
+
* every model would claim requests a model never served.
|
|
98
|
+
*/
|
|
99
|
+
requestCounts = new Map();
|
|
100
|
+
fallbackModel;
|
|
101
|
+
constructor(options) {
|
|
102
|
+
this.fallbackModel = options?.fallbackModel ?? 'claude';
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Records one result and returns the usage records for the turn it closed.
|
|
106
|
+
*
|
|
107
|
+
* - Fresh session, first result: the snapshot *is* this turn.
|
|
108
|
+
* - Resumed session with no earlier snapshot (process restart): seed the
|
|
109
|
+
* baseline from the snapshot and report only the main-loop usage, which is
|
|
110
|
+
* per-turn. Subagent tokens from this one turn stay unattributed until the
|
|
111
|
+
* next result's delta; the plan records this gap rather than inventing a
|
|
112
|
+
* number.
|
|
113
|
+
* - Later results: per-model deltas, clamped at zero so a mid-session
|
|
114
|
+
* `/clear` (which resets the running total) cannot produce negative usage.
|
|
115
|
+
*/
|
|
116
|
+
observe(observation, context) {
|
|
117
|
+
const cumulative = new Map();
|
|
118
|
+
for (const [model, usage] of Object.entries(observation.modelUsage ?? {})) {
|
|
119
|
+
cumulative.set(model, countsFromModelUsage(usage));
|
|
120
|
+
}
|
|
121
|
+
const snapshotHasUsage = [...cumulative.values()].some((counts) => totalOf(counts) > 0);
|
|
122
|
+
const mainLoop = countsFromMainLoop(observation.mainLoopUsage);
|
|
123
|
+
const mainLoopHasUsage = totalOf(mainLoop) > 0;
|
|
124
|
+
if (this.snapshot === null) {
|
|
125
|
+
if (snapshotHasUsage) {
|
|
126
|
+
this.snapshot = cumulative;
|
|
127
|
+
if (context.resumed) {
|
|
128
|
+
return mainLoopHasUsage
|
|
129
|
+
? [this.record(this.attributeMainLoop(observation.mainLoopModel, cumulative), mainLoop, observation.uuid, observation.durationMs)]
|
|
130
|
+
: [];
|
|
131
|
+
}
|
|
132
|
+
return [...cumulative.entries()]
|
|
133
|
+
.filter(([, counts]) => totalOf(counts) > 0)
|
|
134
|
+
.map(([model, counts]) => this.record(model, counts, observation.uuid, observation.durationMs));
|
|
135
|
+
}
|
|
136
|
+
// No per-model snapshot at all: the main loop is the only accounting
|
|
137
|
+
// left. Do not seed anything — the next result's cumulative total will
|
|
138
|
+
// still be baselined on its own first observation.
|
|
139
|
+
return mainLoopHasUsage
|
|
140
|
+
? [this.record(this.attributeMainLoop(observation.mainLoopModel, cumulative), mainLoop, observation.uuid, observation.durationMs)]
|
|
141
|
+
: [];
|
|
142
|
+
}
|
|
143
|
+
if (!snapshotHasUsage) {
|
|
144
|
+
// Crash / startup-error results may carry a zeroed snapshot. Treating it
|
|
145
|
+
// as real would reset the baseline and re-bill every earlier turn.
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
const records = [];
|
|
149
|
+
for (const [model, counts] of cumulative) {
|
|
150
|
+
const previous = this.snapshot.get(model) ?? ZERO;
|
|
151
|
+
this.snapshot.set(model, counts);
|
|
152
|
+
const delta = subtractClamped(counts, previous);
|
|
153
|
+
if (totalOf(delta) > 0) {
|
|
154
|
+
records.push(this.record(model, delta, observation.uuid, observation.durationMs));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Models absent from this snapshot keep their last-seen totals: they may
|
|
158
|
+
// return in a later turn, and their earlier usage is already accounted.
|
|
159
|
+
return records;
|
|
160
|
+
}
|
|
161
|
+
/** Cumulative per-model totals as of the last non-zeroed result. */
|
|
162
|
+
getSnapshot() {
|
|
163
|
+
return this.snapshot ?? new Map();
|
|
164
|
+
}
|
|
165
|
+
/** Turns that produced usage for each model. */
|
|
166
|
+
getRequestCounts() {
|
|
167
|
+
return this.requestCounts;
|
|
168
|
+
}
|
|
169
|
+
/** True once at least one result with real usage has been observed. */
|
|
170
|
+
hasUsage() {
|
|
171
|
+
return this.snapshot !== null && [...this.snapshot.values()].some((counts) => totalOf(counts) > 0);
|
|
172
|
+
}
|
|
173
|
+
record(model, counts, uuid, durationMs) {
|
|
174
|
+
this.requestCounts.set(model, (this.requestCounts.get(model) ?? 0) + 1);
|
|
175
|
+
return toRecord(model, counts, uuid, durationMs);
|
|
176
|
+
}
|
|
177
|
+
attributeMainLoop(observedModel, cumulative) {
|
|
178
|
+
if (observedModel !== undefined && observedModel.length > 0)
|
|
179
|
+
return observedModel;
|
|
180
|
+
if (cumulative.size === 1) {
|
|
181
|
+
const only = cumulative.keys().next();
|
|
182
|
+
if (!only.done)
|
|
183
|
+
return only.value;
|
|
184
|
+
}
|
|
185
|
+
return this.fallbackModel;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
exports.ModelUsageLedger = ModelUsageLedger;
|
|
189
|
+
//# sourceMappingURL=usage-ledger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-ledger.js","sourceRoot":"","sources":["../../../runtimes/claude/usage-ledger.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk-backend.d.ts","sourceRoot":"","sources":["../../../runtimes/copilot/sdk-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAmBH,OAAO,KAAK,EAEV,MAAM,EAKP,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"sdk-backend.d.ts","sourceRoot":"","sources":["../../../runtimes/copilot/sdk-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAmBH,OAAO,KAAK,EAEV,MAAM,EAKP,MAAM,iBAAiB,CAAC;AAWzB,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,aAAa,EACb,sBAAsB,EAQvB,MAAM,mBAAmB,CAAC;AAU3B,OAAO,KAAK,EACV,wBAAwB,EACxB,cAAc,EACd,cAAc,EACf,MAAM,aAAa,CAAC;AAMrB,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;AACvF,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAmC9D,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACvD,uFAAuF;IACvF,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,0DAA0D;IAC1D,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,gFAAgF;IAChF,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,kFAAkF;IAClF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,0GAA0G;IAC1G,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,GAAG,KAAK,CAAC;IACjD,0FAA0F;IAC1F,QAAQ,CAAC,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IAC7D,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAC5C,qFAAqF;IACrF,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CAC9C;AA4nCD;;;;;;GAMG;AACH,qBAAa,UAAW,YAAW,cAAc;IAC/C,QAAQ,CAAC,IAAI,SAAS;IACtB,QAAQ,CAAC,YAAY;;;;OAIT;IACZ,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqC;IACpE,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAA2B;IACpE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAqB;IAC/D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA+B;IAChE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,GAAE,iBAAsB;IAgBrC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAY/B,aAAa,CACjB,MAAM,EAAE,aAAa,EACrB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,cAAc,CAAC;CAoB3B"}
|
|
@@ -51,6 +51,8 @@ const fs = __importStar(require("fs"));
|
|
|
51
51
|
const path = __importStar(require("path"));
|
|
52
52
|
const node_crypto_1 = require("node:crypto");
|
|
53
53
|
const types_1 = require("../../src/types");
|
|
54
|
+
const session_recovery_policy_1 = require("../../src/session-recovery-policy");
|
|
55
|
+
const mcp_env_1 = require("../mcp-env");
|
|
54
56
|
const lifecycle_events_1 = require("./lifecycle-events");
|
|
55
57
|
const subagents_1 = require("./subagents");
|
|
56
58
|
const SEVERITY_RANK = {
|
|
@@ -81,36 +83,6 @@ function writeDiagnosticStderr(severity, message, configuredLevel) {
|
|
|
81
83
|
/* closed stream */
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
|
-
const MAX_SESSION_RECOVERY_CONTINUATIONS = 23;
|
|
85
|
-
function positiveIntegerOr(value, fallback) {
|
|
86
|
-
return value !== undefined && Number.isFinite(value) && value > 0
|
|
87
|
-
? Math.trunc(value)
|
|
88
|
-
: fallback;
|
|
89
|
-
}
|
|
90
|
-
function nonNegativeOr(value, fallback) {
|
|
91
|
-
return value !== undefined && Number.isFinite(value) && value >= 0
|
|
92
|
-
? value
|
|
93
|
-
: fallback;
|
|
94
|
-
}
|
|
95
|
-
function effectiveSessionRecoveryPolicy(policy) {
|
|
96
|
-
if (policy === false)
|
|
97
|
-
return undefined;
|
|
98
|
-
const configured = policy ?? {};
|
|
99
|
-
return {
|
|
100
|
-
maxContinuations: Math.min(MAX_SESSION_RECOVERY_CONTINUATIONS, positiveIntegerOr(configured.maxContinuations, types_1.DEFAULT_SESSION_RECOVERY_POLICY.maxContinuations)),
|
|
101
|
-
budgetMs: nonNegativeOr(configured.budgetMs, types_1.DEFAULT_SESSION_RECOVERY_POLICY.budgetMs),
|
|
102
|
-
backoffBaseMs: positiveIntegerOr(configured.backoffBaseMs, types_1.DEFAULT_SESSION_RECOVERY_POLICY.backoffBaseMs),
|
|
103
|
-
backoffMaxMs: positiveIntegerOr(configured.backoffMaxMs, types_1.DEFAULT_SESSION_RECOVERY_POLICY.backoffMaxMs),
|
|
104
|
-
jitter: configured.jitter ?? types_1.DEFAULT_SESSION_RECOVERY_POLICY.jitter,
|
|
105
|
-
nativeRetryGraceMs: nonNegativeOr(configured.nativeRetryGraceMs, types_1.DEFAULT_SESSION_RECOVERY_POLICY.nativeRetryGraceMs),
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
function recoveryDelayMs(policy, failedContinuation) {
|
|
109
|
-
const exponent = Math.min(52, Math.max(0, failedContinuation));
|
|
110
|
-
const capped = Math.min(policy.backoffMaxMs, policy.backoffBaseMs * (2 ** exponent));
|
|
111
|
-
const delayed = policy.jitter ? Math.random() * capped : capped;
|
|
112
|
-
return Math.max(100, Number.isFinite(delayed) ? delayed : 100);
|
|
113
|
-
}
|
|
114
86
|
/**
|
|
115
87
|
* A failed reconnect step, tagged with the stage that threw so an `exhausted`
|
|
116
88
|
* event can name it.
|
|
@@ -975,72 +947,6 @@ function parseMcpConfig(configPath, mcpServerWorkingDirectory, terminalLogLevel)
|
|
|
975
947
|
return null;
|
|
976
948
|
}
|
|
977
949
|
}
|
|
978
|
-
const ENV_REFERENCE = /^\$\{([A-Z_][A-Z0-9_]*(?:\|[A-Z_][A-Z0-9_]*)*)\}$/;
|
|
979
|
-
/**
|
|
980
|
-
* Resolves ${NAME} and ${NAME|FALLBACK|...}, taking the first variable that is set.
|
|
981
|
-
*
|
|
982
|
-
* The fallback chain exists because there is no single conventional name for a GitHub
|
|
983
|
-
* token: the Copilot CLI itself reads COPILOT_GITHUB_TOKEN, then GH_TOKEN, then
|
|
984
|
-
* GITHUB_TOKEN. A default that insisted on one spelling would silently ship an
|
|
985
|
-
* unauthenticated server in any environment that chose a different one.
|
|
986
|
-
*/
|
|
987
|
-
function resolveMcpEnvironmentValue(value) {
|
|
988
|
-
const match = ENV_REFERENCE.exec(value);
|
|
989
|
-
if (!match)
|
|
990
|
-
return value;
|
|
991
|
-
for (const name of match[1].split('|')) {
|
|
992
|
-
const resolved = process.env[name];
|
|
993
|
-
if (resolved !== undefined && resolved !== '')
|
|
994
|
-
return resolved;
|
|
995
|
-
}
|
|
996
|
-
return undefined;
|
|
997
|
-
}
|
|
998
|
-
function resolveMcpEnvironmentRecord(values) {
|
|
999
|
-
if (values === undefined)
|
|
1000
|
-
return undefined;
|
|
1001
|
-
const resolved = {};
|
|
1002
|
-
for (const [key, value] of Object.entries(values)) {
|
|
1003
|
-
const environmentValue = resolveMcpEnvironmentValue(value);
|
|
1004
|
-
if (environmentValue !== undefined)
|
|
1005
|
-
resolved[key] = environmentValue;
|
|
1006
|
-
}
|
|
1007
|
-
return Object.keys(resolved).length > 0 ? resolved : undefined;
|
|
1008
|
-
}
|
|
1009
|
-
// Embedded references, so `Bearer ${A|B}` resolves rather than only a bare `${A|B}`. The
|
|
1010
|
-
// fallback alternatives must be part of the pattern: a reference matcher without them finds
|
|
1011
|
-
// nothing in `${A|B}`, leaves the value untouched, and emits the placeholder as a literal
|
|
1012
|
-
// header — an unauthenticated server that reports no error and simply returns no tools.
|
|
1013
|
-
const HEADER_ENV_REFERENCE = /\$\{([A-Z_][A-Z0-9_]*(?:\|[A-Z_][A-Z0-9_]*)*)\}/g;
|
|
1014
|
-
function resolveMcpHeaders(headers, terminalLogLevel) {
|
|
1015
|
-
if (headers === undefined)
|
|
1016
|
-
return undefined;
|
|
1017
|
-
const resolved = {};
|
|
1018
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
1019
|
-
const references = [...value.matchAll(HEADER_ENV_REFERENCE)];
|
|
1020
|
-
let resolvedValue = value;
|
|
1021
|
-
let complete = true;
|
|
1022
|
-
for (const reference of references) {
|
|
1023
|
-
const names = reference[1].split('|');
|
|
1024
|
-
const environmentValue = names
|
|
1025
|
-
.map((name) => process.env[name])
|
|
1026
|
-
.find((candidate) => candidate !== undefined && candidate !== '');
|
|
1027
|
-
if (environmentValue === undefined) {
|
|
1028
|
-
// Dropping the header is deliberate — a missing credential must not abort a run — but
|
|
1029
|
-
// it is invisible at the server, which just answers with nothing.
|
|
1030
|
-
try {
|
|
1031
|
-
writeDiagnosticStderr('warning', `[SdkBackend] MCP header ${key} dropped: none of ${names.join(', ')} is set\n`, terminalLogLevel);
|
|
1032
|
-
}
|
|
1033
|
-
catch { /* diagnostics must not break configuration */ }
|
|
1034
|
-
complete = false;
|
|
1035
|
-
break;
|
|
1036
|
-
}
|
|
1037
|
-
resolvedValue = resolvedValue.replace(reference[0], environmentValue);
|
|
1038
|
-
}
|
|
1039
|
-
if (complete)
|
|
1040
|
-
resolved[key] = resolvedValue;
|
|
1041
|
-
}
|
|
1042
|
-
return Object.keys(resolved).length > 0 ? resolved : undefined;
|
|
1043
|
-
}
|
|
1044
950
|
function toSdkMcpServers(servers, terminalLogLevel) {
|
|
1045
951
|
if (servers === undefined || servers === false)
|
|
1046
952
|
return servers;
|
|
@@ -1050,14 +956,19 @@ function toSdkMcpServers(servers, terminalLogLevel) {
|
|
|
1050
956
|
if (!parsed)
|
|
1051
957
|
continue;
|
|
1052
958
|
if ('url' in parsed) {
|
|
1053
|
-
const headers = resolveMcpHeaders(parsed.headers,
|
|
959
|
+
const headers = (0, mcp_env_1.resolveMcpHeaders)(parsed.headers, ({ header, names }) => {
|
|
960
|
+
try {
|
|
961
|
+
writeDiagnosticStderr('warning', `[SdkBackend] MCP header ${header} dropped: none of ${names.join(', ')} is set\n`, terminalLogLevel);
|
|
962
|
+
}
|
|
963
|
+
catch { /* diagnostics must not break configuration */ }
|
|
964
|
+
});
|
|
1054
965
|
converted[name] = {
|
|
1055
966
|
...parsed,
|
|
1056
967
|
...(headers !== undefined ? { headers } : { headers: undefined }),
|
|
1057
968
|
};
|
|
1058
969
|
}
|
|
1059
970
|
else {
|
|
1060
|
-
const env = resolveMcpEnvironmentRecord(parsed.env);
|
|
971
|
+
const env = (0, mcp_env_1.resolveMcpEnvironmentRecord)(parsed.env);
|
|
1061
972
|
converted[name] = {
|
|
1062
973
|
...parsed,
|
|
1063
974
|
...(env !== undefined ? { env } : { env: undefined }),
|
|
@@ -2036,7 +1947,7 @@ class SdkSession {
|
|
|
2036
1947
|
});
|
|
2037
1948
|
}
|
|
2038
1949
|
scheduleRecovery(failedHandle, error, data, eventId, options = {}) {
|
|
2039
|
-
const policy = effectiveSessionRecoveryPolicy(this.config.sessionRecovery);
|
|
1950
|
+
const policy = (0, session_recovery_policy_1.effectiveSessionRecoveryPolicy)(this.config.sessionRecovery);
|
|
2040
1951
|
if (!policy) {
|
|
2041
1952
|
const eventType = data?.failureKind === 'session-progress-timeout'
|
|
2042
1953
|
? 'session.progress_timeout'
|
|
@@ -2068,7 +1979,7 @@ class SdkSession {
|
|
|
2068
1979
|
}
|
|
2069
1980
|
this.recoveryStartedAt ??= Date.now();
|
|
2070
1981
|
this.recoveryDeadlineMs ??= Math.min(this.sessionDeadlineMs ?? Number.POSITIVE_INFINITY, this.recoveryStartedAt + policy.budgetMs);
|
|
2071
|
-
const delayMs = recoveryDelayMs(policy, this.recoveryContinuation);
|
|
1982
|
+
const delayMs = (0, session_recovery_policy_1.recoveryDelayMs)(policy, this.recoveryContinuation);
|
|
2072
1983
|
this.emitRecovery('scheduled', policy, this.recoveryContinuation + 1, sessionId, {
|
|
2073
1984
|
delayMs,
|
|
2074
1985
|
...this.recoveryMetadata(data),
|
|
@@ -2137,7 +2048,7 @@ class SdkSession {
|
|
|
2137
2048
|
this.exhaustRecovery(policy, sessionId, originalError, failed.message, data, failed.stage);
|
|
2138
2049
|
return;
|
|
2139
2050
|
}
|
|
2140
|
-
const retryDelayMs = recoveryDelayMs(policy, this.recoveryContinuation);
|
|
2051
|
+
const retryDelayMs = (0, session_recovery_policy_1.recoveryDelayMs)(policy, this.recoveryContinuation);
|
|
2141
2052
|
this.emitRecovery('scheduled', policy, this.recoveryContinuation + 1, sessionId, {
|
|
2142
2053
|
delayMs: retryDelayMs,
|
|
2143
2054
|
...this.recoveryMetadata(data),
|
|
@@ -2550,7 +2461,7 @@ class SdkSession {
|
|
|
2550
2461
|
// rather than the attempt's first turn, so the recovery bookkeeping (and
|
|
2551
2462
|
// its `recovered` event) does not apply to it.
|
|
2552
2463
|
const isContinuation = this.settleWaiters.length > 0;
|
|
2553
|
-
const policy = effectiveSessionRecoveryPolicy(this.config.sessionRecovery);
|
|
2464
|
+
const policy = (0, session_recovery_policy_1.effectiveSessionRecoveryPolicy)(this.config.sessionRecovery);
|
|
2554
2465
|
if (!isContinuation && policy && this.recoveryContinuation > 0 && this.sessionId) {
|
|
2555
2466
|
this.emitRecovery('recovered', policy, this.recoveryContinuation, this.sessionId);
|
|
2556
2467
|
}
|
|
@@ -2707,7 +2618,7 @@ class SdkSession {
|
|
|
2707
2618
|
if (!e.agentId && this.nativeRetryGraceTimer) {
|
|
2708
2619
|
clearTimeout(this.nativeRetryGraceTimer);
|
|
2709
2620
|
this.nativeRetryGraceTimer = null;
|
|
2710
|
-
const policy = effectiveSessionRecoveryPolicy(this.config.sessionRecovery);
|
|
2621
|
+
const policy = (0, session_recovery_policy_1.effectiveSessionRecoveryPolicy)(this.config.sessionRecovery);
|
|
2711
2622
|
if (policy && this.sessionId) {
|
|
2712
2623
|
this.emitRecovery('native-retry', policy, this.recoveryContinuation, this.sessionId);
|
|
2713
2624
|
}
|
|
@@ -2954,7 +2865,7 @@ class SdkSession {
|
|
|
2954
2865
|
// genuinely running subagent read is abandoned and re-issued by the model.
|
|
2955
2866
|
// For the read-only work recovery is opted into for, that is the right
|
|
2956
2867
|
// trade against failing the node; a mutating node opts out of recovery.
|
|
2957
|
-
const policy = effectiveSessionRecoveryPolicy(this.config.sessionRecovery);
|
|
2868
|
+
const policy = (0, session_recovery_policy_1.effectiveSessionRecoveryPolicy)(this.config.sessionRecovery);
|
|
2958
2869
|
const hasPendingWork = this.activeToolCalls.size > 0 || this.pendingExternalRequests.size > 0;
|
|
2959
2870
|
if (!policy) {
|
|
2960
2871
|
if (hasPendingWork) {
|