@cjhyy/code-shell-core 0.6.0-rc.8 → 0.7.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD_PARTY_NOTICES.md +206 -0
- package/dist/automation/scheduler.d.ts +13 -7
- package/dist/automation/scheduler.js +116 -37
- package/dist/capability-control/service.d.ts +2 -0
- package/dist/capability-control/service.js +4 -2
- package/dist/cc-orchestrator/agent-adapter.d.ts +2 -0
- package/dist/cc-orchestrator/agent-adapter.js +7 -1
- package/dist/cc-orchestrator/codex-session-history.js +1 -1
- package/dist/cc-orchestrator/cwd-normalize.d.ts +2 -0
- package/dist/cc-orchestrator/cwd-normalize.js +19 -0
- package/dist/cc-orchestrator/external-agent-bindings.d.ts +27 -0
- package/dist/cc-orchestrator/external-agent-bindings.js +150 -0
- package/dist/cc-orchestrator/external-agent-driver.d.ts +1 -0
- package/dist/cc-orchestrator/external-agent-driver.js +102 -51
- package/dist/cc-orchestrator/external-agent-session-store.d.ts +23 -0
- package/dist/cc-orchestrator/external-agent-session-store.js +146 -0
- package/dist/cc-orchestrator/session-history.js +2 -2
- package/dist/cli/agent-server-stdio.js +9 -2
- package/dist/context/compaction.d.ts +38 -1
- package/dist/context/compaction.js +138 -0
- package/dist/context/manager.d.ts +34 -2
- package/dist/context/manager.js +254 -58
- package/dist/context/token-counter.js +13 -0
- package/dist/credentials/access.d.ts +56 -0
- package/dist/credentials/access.js +183 -0
- package/dist/credentials/index.d.ts +1 -0
- package/dist/credentials/index.js +1 -0
- package/dist/credentials/inject-credential-tool.d.ts +3 -1
- package/dist/credentials/inject-credential-tool.js +30 -11
- package/dist/credentials/types.d.ts +2 -2
- package/dist/credentials/use-credential-tool.d.ts +9 -1
- package/dist/credentials/use-credential-tool.js +58 -45
- package/dist/engine/engine.d.ts +38 -12
- package/dist/engine/engine.js +477 -163
- package/dist/engine/image-policy.d.ts +6 -0
- package/dist/engine/image-policy.js +17 -6
- package/dist/engine/input-attachments.d.ts +13 -0
- package/dist/engine/input-attachments.js +255 -0
- package/dist/engine/model-facade.d.ts +5 -2
- package/dist/engine/model-facade.js +6 -16
- package/dist/engine/parse-task.d.ts +10 -0
- package/dist/engine/parse-task.js +5 -0
- package/dist/engine/query.js +2 -0
- package/dist/engine/session-usage.d.ts +12 -0
- package/dist/engine/session-usage.js +56 -0
- package/dist/engine/steer-queue.d.ts +2 -1
- package/dist/engine/steer-queue.js +2 -2
- package/dist/engine/streaming-tool-queue.d.ts +11 -7
- package/dist/engine/streaming-tool-queue.js +11 -7
- package/dist/engine/turn-loop.d.ts +34 -2
- package/dist/engine/turn-loop.js +269 -52
- package/dist/engine/types.d.ts +8 -0
- package/dist/git/worktree/crud.d.ts +69 -0
- package/dist/git/worktree/crud.js +206 -0
- package/dist/git/worktree/diff.d.ts +14 -0
- package/dist/git/worktree/diff.js +82 -0
- package/dist/git/worktree/git-exec.d.ts +7 -0
- package/dist/git/worktree/git-exec.js +51 -0
- package/dist/git/worktree/index.d.ts +5 -0
- package/dist/git/worktree/index.js +5 -0
- package/dist/git/worktree/query.d.ts +42 -0
- package/dist/git/worktree/query.js +121 -0
- package/dist/git/worktree/slug.d.ts +11 -0
- package/dist/git/worktree/slug.js +58 -0
- package/dist/git/worktree.d.ts +1 -84
- package/dist/git/worktree.js +5 -230
- package/dist/index.d.ts +31 -28
- package/dist/index.js +27 -25
- package/dist/logging/logger.js +6 -6
- package/dist/logging/sanitize-messages.d.ts +10 -2
- package/dist/logging/sanitize-messages.js +21 -6
- package/dist/plugins/installer/checkUpdate.d.ts +4 -1
- package/dist/plugins/installer/checkUpdate.js +4 -2
- package/dist/plugins/installer/install.js +2 -0
- package/dist/plugins/installer/installFromSource.js +9 -1
- package/dist/plugins/installer/parseSource.d.ts +4 -1
- package/dist/plugins/installer/parseSource.js +28 -10
- package/dist/plugins/installer/sourcePath.d.ts +9 -0
- package/dist/plugins/installer/sourcePath.js +50 -0
- package/dist/plugins/installer/update.d.ts +4 -1
- package/dist/plugins/installer/update.js +5 -3
- package/dist/plugins/parseMarketplaceInput.d.ts +4 -1
- package/dist/plugins/parseMarketplaceInput.js +4 -3
- package/dist/plugins/pluginInstaller.js +24 -22
- package/dist/preset/index.d.ts +1 -0
- package/dist/preset/index.js +16 -9
- package/dist/prompt/sections/base.md +1 -1
- package/dist/protocol/chat-session-manager.d.ts +2 -0
- package/dist/protocol/chat-session-manager.js +38 -2
- package/dist/protocol/chat-session.d.ts +5 -0
- package/dist/protocol/chat-session.js +2 -0
- package/dist/protocol/client.d.ts +13 -5
- package/dist/protocol/client.js +26 -3
- package/dist/protocol/server.d.ts +43 -12
- package/dist/protocol/server.js +326 -104
- package/dist/protocol/types.d.ts +51 -0
- package/dist/protocol/types.js +6 -0
- package/dist/run/FileRunStore.js +10 -1
- package/dist/run/Heartbeat.js +12 -0
- package/dist/run/RunApprovalBackend.d.ts +3 -0
- package/dist/run/RunApprovalBackend.js +41 -6
- package/dist/run/RunLock.js +2 -0
- package/dist/run/RunManager.d.ts +2 -0
- package/dist/run/RunManager.js +64 -24
- package/dist/run/ids.d.ts +2 -0
- package/dist/run/ids.js +23 -0
- package/dist/runtime/background-shell.d.ts +1 -0
- package/dist/runtime/background-shell.js +23 -13
- package/dist/runtime/safe-spawn.js +74 -11
- package/dist/runtime/spawn-common.js +10 -0
- package/dist/services/auto-dream.d.ts +15 -4
- package/dist/services/auto-dream.js +20 -20
- package/dist/services/dream-consolidation.d.ts +2 -3
- package/dist/services/dream-consolidation.js +65 -15
- package/dist/services/extract-memories.d.ts +14 -5
- package/dist/services/extract-memories.js +20 -3
- package/dist/services/global-dream-promotion.d.ts +23 -0
- package/dist/services/global-dream-promotion.js +112 -0
- package/dist/services/memory-orchestrator.js +347 -34
- package/dist/session/memory.d.ts +61 -18
- package/dist/session/memory.js +342 -79
- package/dist/session/session-manager.d.ts +35 -1
- package/dist/session/session-manager.js +197 -4
- package/dist/session/transcript.d.ts +5 -1
- package/dist/session/transcript.js +38 -5
- package/dist/settings/manager.d.ts +1 -0
- package/dist/settings/manager.js +87 -37
- package/dist/settings/schema-export.d.ts +2 -3
- package/dist/settings/schema-export.js +2 -3
- package/dist/settings/schema.d.ts +21 -0
- package/dist/settings/schema.js +12 -0
- package/dist/skills/scanner.d.ts +3 -2
- package/dist/skills/scanner.js +12 -9
- package/dist/tool-system/builtin/background-jobs.d.ts +10 -1
- package/dist/tool-system/builtin/background-jobs.js +12 -1
- package/dist/tool-system/builtin/background-work.d.ts +17 -18
- package/dist/tool-system/builtin/background-work.js +51 -5
- package/dist/tool-system/builtin/config.d.ts +2 -1
- package/dist/tool-system/builtin/config.js +16 -11
- package/dist/tool-system/builtin/drive-claude-code.d.ts +16 -2
- package/dist/tool-system/builtin/drive-claude-code.js +301 -47
- package/dist/tool-system/builtin/edit.js +5 -2
- package/dist/tool-system/builtin/generate-video.d.ts +1 -0
- package/dist/tool-system/builtin/generate-video.js +13 -4
- package/dist/tool-system/builtin/index.d.ts +8 -3
- package/dist/tool-system/builtin/index.js +37 -21
- package/dist/tool-system/builtin/lsp.d.ts +2 -1
- package/dist/tool-system/builtin/lsp.js +6 -3
- package/dist/tool-system/builtin/memory.js +40 -8
- package/dist/tool-system/builtin/notebook-edit.js +5 -2
- package/dist/tool-system/builtin/powershell.d.ts +5 -2
- package/dist/tool-system/builtin/powershell.js +11 -7
- package/dist/tool-system/builtin/read.js +118 -6
- package/dist/tool-system/builtin/view-image.d.ts +2 -2
- package/dist/tool-system/builtin/view-image.js +109 -19
- package/dist/tool-system/builtin/worktree.d.ts +4 -4
- package/dist/tool-system/builtin/worktree.js +297 -74
- package/dist/tool-system/builtin/write.js +5 -3
- package/dist/tool-system/context.d.ts +23 -1
- package/dist/tool-system/executor.d.ts +1 -5
- package/dist/tool-system/executor.js +94 -115
- package/dist/tool-system/mcp-manager.d.ts +18 -5
- package/dist/tool-system/mcp-manager.js +157 -74
- package/dist/tool-system/mcp-stdio-diagnostics.d.ts +9 -0
- package/dist/tool-system/mcp-stdio-diagnostics.js +93 -0
- package/dist/tool-system/path-policy.d.ts +2 -0
- package/dist/tool-system/path-policy.js +41 -12
- package/dist/tool-system/permission.d.ts +28 -7
- package/dist/tool-system/permission.js +130 -49
- package/dist/tool-system/registry.js +11 -4
- package/dist/tool-system/tool-result-redaction.d.ts +7 -0
- package/dist/tool-system/tool-result-redaction.js +48 -0
- package/dist/tool-system/workspace-bridge.d.ts +11 -0
- package/dist/tool-system/workspace-bridge.js +1 -0
- package/dist/types.d.ts +86 -5
- package/dist/utils/toolDisplay.js +1 -1
- package/package.json +4 -3
|
@@ -21,6 +21,17 @@ export declare class AutoApprovalBackend implements ApprovalBackend {
|
|
|
21
21
|
requestApproval(req: ApprovalRequest): Promise<ApprovalResult>;
|
|
22
22
|
private isSafeOperation;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Closed-session tombstones are a bounded replay guard. A prompt that started
|
|
26
|
+
* before close is still protected by the state-object identity check in
|
|
27
|
+
* isActiveSessionState(): clearSession deletes the captured state, so the late
|
|
28
|
+
* result cannot write into a new bucket even if an old tombstone has aged out.
|
|
29
|
+
* The tombstone only blocks fresh post-close calls carrying a recently closed
|
|
30
|
+
* sessionId from creating an empty bucket. 4096 is 256x the default
|
|
31
|
+
* ChatSessionManager.maxSessions (16), which covers normal late-result bursts
|
|
32
|
+
* while keeping long-lived processes from retaining every historical id.
|
|
33
|
+
*/
|
|
34
|
+
export declare const CLOSED_SESSION_TOMBSTONE_LIMIT = 4096;
|
|
24
35
|
/**
|
|
25
36
|
* Interactive approval backend — prompts the user via a callback.
|
|
26
37
|
*
|
|
@@ -34,13 +45,11 @@ export declare class AutoApprovalBackend implements ApprovalBackend {
|
|
|
34
45
|
* the next time the user opens the project.
|
|
35
46
|
*/
|
|
36
47
|
export declare class InteractiveApprovalBackend implements ApprovalBackend {
|
|
37
|
-
private
|
|
38
|
-
private
|
|
48
|
+
private sessionStateById;
|
|
49
|
+
private closedSessionIds;
|
|
39
50
|
private promptFn;
|
|
40
|
-
private
|
|
41
|
-
private
|
|
42
|
-
private onProjectRules;
|
|
43
|
-
private promptTurn;
|
|
51
|
+
private legacyPromptTurn;
|
|
52
|
+
private legacyContext;
|
|
44
53
|
setPromptFn(fn: (request: ApprovalRequest) => Promise<ApprovalResult>): void;
|
|
45
54
|
/**
|
|
46
55
|
* Has someone installed a real prompt callback? Engine consults this
|
|
@@ -59,8 +68,18 @@ export declare class InteractiveApprovalBackend implements ApprovalBackend {
|
|
|
59
68
|
* earlier approvals.
|
|
60
69
|
*/
|
|
61
70
|
setOnProjectRules(fn: (rules: PermissionRule[]) => void): void;
|
|
71
|
+
setSessionContext(sessionId: string, context: {
|
|
72
|
+
cwd: string;
|
|
73
|
+
onProjectRules: (rules: PermissionRule[]) => void;
|
|
74
|
+
}): void;
|
|
75
|
+
openSession(sessionId: string): void;
|
|
76
|
+
clearSession(sessionId: string): void;
|
|
77
|
+
private rememberClosedSession;
|
|
78
|
+
private makeSessionState;
|
|
79
|
+
private getSessionState;
|
|
80
|
+
private isActiveSessionState;
|
|
62
81
|
requestApproval(req: ApprovalRequest): Promise<ApprovalResult>;
|
|
63
|
-
/** Session-rule lookup — operation-scoped (see
|
|
82
|
+
/** Session-rule lookup — operation-scoped (see sessionStateById doc). Deny
|
|
64
83
|
* wins over allow if both somehow match (conservative). Null = no rule. */
|
|
65
84
|
private checkSessionRules;
|
|
66
85
|
/** The actual interactive ask + rule recording (runs inside the prompt turn). */
|
|
@@ -89,6 +108,8 @@ export declare function pathRuleArgsPattern(absPath: string, scope: "file" | "di
|
|
|
89
108
|
*/
|
|
90
109
|
export declare function ruleMatches(rule: PermissionRule, toolName: string, args: Record<string, unknown>): boolean;
|
|
91
110
|
export declare function getInteractiveApprovalBackend(): InteractiveApprovalBackend;
|
|
111
|
+
export declare function openInteractiveApprovalSession(sessionId: string): void;
|
|
112
|
+
export declare function clearInteractiveApprovalSession(sessionId: string): void;
|
|
92
113
|
export declare function setInteractiveApprovalFn(fn: (request: ApprovalRequest) => Promise<ApprovalResult>): void;
|
|
93
114
|
type BashSafetyLevel = "safe-read" | "safe-write" | "unsafe" | "dangerous";
|
|
94
115
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Permission system — classifier + approval backend.
|
|
3
3
|
*/
|
|
4
4
|
import { resolve as resolvePath, dirname } from "node:path";
|
|
5
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from "node:fs";
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import { logger as rootPermLogger } from "../logging/logger.js";
|
|
8
8
|
import { READ_ONLY_TOOLS } from "./plan-mode-allowlist.js";
|
|
@@ -93,6 +93,17 @@ export class AutoApprovalBackend {
|
|
|
93
93
|
return false;
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Closed-session tombstones are a bounded replay guard. A prompt that started
|
|
98
|
+
* before close is still protected by the state-object identity check in
|
|
99
|
+
* isActiveSessionState(): clearSession deletes the captured state, so the late
|
|
100
|
+
* result cannot write into a new bucket even if an old tombstone has aged out.
|
|
101
|
+
* The tombstone only blocks fresh post-close calls carrying a recently closed
|
|
102
|
+
* sessionId from creating an empty bucket. 4096 is 256x the default
|
|
103
|
+
* ChatSessionManager.maxSessions (16), which covers normal late-result bursts
|
|
104
|
+
* while keeping long-lived processes from retaining every historical id.
|
|
105
|
+
*/
|
|
106
|
+
export const CLOSED_SESSION_TOMBSTONE_LIMIT = 4096;
|
|
96
107
|
/**
|
|
97
108
|
* Interactive approval backend — prompts the user via a callback.
|
|
98
109
|
*
|
|
@@ -106,29 +117,21 @@ export class AutoApprovalBackend {
|
|
|
106
117
|
* the next time the user opens the project.
|
|
107
118
|
*/
|
|
108
119
|
export class InteractiveApprovalBackend {
|
|
109
|
-
// Session-scoped grants
|
|
110
|
-
//
|
|
111
|
-
// the
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
120
|
+
// Session-scoped grants are bucketed by ApprovalRequest.sessionId and then
|
|
121
|
+
// keyed on the OPERATION (tool + narrowed argsPattern via buildProjectRule),
|
|
122
|
+
// NOT just the tool name. Approving `git status` for one session must not
|
|
123
|
+
// auto-allow either `rm -rf /` in that same session or `git ...` in another.
|
|
124
|
+
// Allow and deny are tracked separately so a one-off deny of `curl evil`
|
|
125
|
+
// never blocks an unrelated `git status`.
|
|
126
|
+
sessionStateById = new Map();
|
|
127
|
+
closedSessionIds = new Set();
|
|
116
128
|
promptFn = null;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
savedProjectRules = [];
|
|
124
|
-
onProjectRules = null;
|
|
125
|
-
// Serialize prompts so a burst of parallel tool calls doesn't queue N
|
|
126
|
-
// identical cards: while one ask is outstanding, later requests wait here
|
|
127
|
-
// and RE-CHECK the session rules when their turn comes — the first
|
|
128
|
-
// "本会话一直允许" answer then silently absorbs the queued duplicates.
|
|
129
|
-
// (All callers were already serialized at the UI anyway — the renderer
|
|
130
|
-
// shows one approval card at a time.)
|
|
131
|
-
promptTurn = Promise.resolve();
|
|
129
|
+
legacyPromptTurn = Promise.resolve();
|
|
130
|
+
legacyContext = {
|
|
131
|
+
cwd: null,
|
|
132
|
+
savedProjectRules: [],
|
|
133
|
+
onProjectRules: null,
|
|
134
|
+
};
|
|
132
135
|
setPromptFn(fn) {
|
|
133
136
|
this.promptFn = fn;
|
|
134
137
|
}
|
|
@@ -144,7 +147,7 @@ export class InteractiveApprovalBackend {
|
|
|
144
147
|
}
|
|
145
148
|
/** Inject the project root so persistence writes to the right settings file. */
|
|
146
149
|
setCwd(cwd) {
|
|
147
|
-
this.cwd = cwd;
|
|
150
|
+
this.legacyContext.cwd = cwd;
|
|
148
151
|
}
|
|
149
152
|
/**
|
|
150
153
|
* Inject a callback fired when the user approves "for this project". The
|
|
@@ -153,11 +156,68 @@ export class InteractiveApprovalBackend {
|
|
|
153
156
|
* earlier approvals.
|
|
154
157
|
*/
|
|
155
158
|
setOnProjectRules(fn) {
|
|
156
|
-
this.onProjectRules = fn;
|
|
159
|
+
this.legacyContext.onProjectRules = fn;
|
|
160
|
+
}
|
|
161
|
+
setSessionContext(sessionId, context) {
|
|
162
|
+
const state = this.getSessionState(sessionId, true);
|
|
163
|
+
if (!state)
|
|
164
|
+
return;
|
|
165
|
+
state.cwd = context.cwd;
|
|
166
|
+
state.onProjectRules = context.onProjectRules;
|
|
167
|
+
}
|
|
168
|
+
openSession(sessionId) {
|
|
169
|
+
if (!sessionId)
|
|
170
|
+
return;
|
|
171
|
+
this.closedSessionIds.delete(sessionId);
|
|
172
|
+
}
|
|
173
|
+
clearSession(sessionId) {
|
|
174
|
+
if (!sessionId)
|
|
175
|
+
return;
|
|
176
|
+
this.sessionStateById.delete(sessionId);
|
|
177
|
+
this.rememberClosedSession(sessionId);
|
|
178
|
+
}
|
|
179
|
+
rememberClosedSession(sessionId) {
|
|
180
|
+
this.closedSessionIds.delete(sessionId);
|
|
181
|
+
this.closedSessionIds.add(sessionId);
|
|
182
|
+
while (this.closedSessionIds.size > CLOSED_SESSION_TOMBSTONE_LIMIT) {
|
|
183
|
+
const oldest = this.closedSessionIds.values().next().value;
|
|
184
|
+
if (oldest === undefined)
|
|
185
|
+
break;
|
|
186
|
+
this.closedSessionIds.delete(oldest);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
makeSessionState() {
|
|
190
|
+
return {
|
|
191
|
+
allowRules: [],
|
|
192
|
+
denyRules: [],
|
|
193
|
+
promptTurn: Promise.resolve(),
|
|
194
|
+
cwd: null,
|
|
195
|
+
savedProjectRules: [],
|
|
196
|
+
onProjectRules: null,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
getSessionState(sessionId, create) {
|
|
200
|
+
if (!sessionId)
|
|
201
|
+
return null;
|
|
202
|
+
if (this.closedSessionIds.has(sessionId))
|
|
203
|
+
return null;
|
|
204
|
+
const existing = this.sessionStateById.get(sessionId);
|
|
205
|
+
if (existing || !create)
|
|
206
|
+
return existing ?? null;
|
|
207
|
+
const next = this.makeSessionState();
|
|
208
|
+
this.sessionStateById.set(sessionId, next);
|
|
209
|
+
return next;
|
|
210
|
+
}
|
|
211
|
+
isActiveSessionState(sessionId, state) {
|
|
212
|
+
return (!!sessionId &&
|
|
213
|
+
!!state &&
|
|
214
|
+
!this.closedSessionIds.has(sessionId) &&
|
|
215
|
+
this.sessionStateById.get(sessionId) === state);
|
|
157
216
|
}
|
|
158
217
|
async requestApproval(req) {
|
|
218
|
+
const state = this.getSessionState(req.sessionId, true);
|
|
159
219
|
// Fast path: the operation may already be covered by a session rule.
|
|
160
|
-
const cached = this.checkSessionRules(req);
|
|
220
|
+
const cached = state ? this.checkSessionRules(req, state) : null;
|
|
161
221
|
if (cached)
|
|
162
222
|
return cached;
|
|
163
223
|
if (!this.promptFn) {
|
|
@@ -167,51 +227,68 @@ export class InteractiveApprovalBackend {
|
|
|
167
227
|
// field doc): a burst of parallel tool calls all passes the fast path
|
|
168
228
|
// before the first decision lands; without the re-check the user had to
|
|
169
229
|
// answer one card per duplicate.
|
|
170
|
-
const prevTurn = this.
|
|
230
|
+
const prevTurn = state ? state.promptTurn : this.legacyPromptTurn;
|
|
171
231
|
let release;
|
|
172
|
-
|
|
232
|
+
const currentTurn = new Promise((r) => (release = r));
|
|
233
|
+
if (state) {
|
|
234
|
+
state.promptTurn = currentTurn;
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
this.legacyPromptTurn = currentTurn;
|
|
238
|
+
}
|
|
173
239
|
try {
|
|
174
240
|
await prevTurn;
|
|
175
|
-
const
|
|
241
|
+
const activeState = this.isActiveSessionState(req.sessionId, state) ? state : null;
|
|
242
|
+
const nowCached = activeState ? this.checkSessionRules(req, activeState) : null;
|
|
176
243
|
if (nowCached)
|
|
177
244
|
return nowCached;
|
|
178
|
-
return await this.promptAndRecord(req);
|
|
245
|
+
return await this.promptAndRecord(req, activeState);
|
|
179
246
|
}
|
|
180
247
|
finally {
|
|
181
248
|
release();
|
|
182
249
|
}
|
|
183
250
|
}
|
|
184
|
-
/** Session-rule lookup — operation-scoped (see
|
|
251
|
+
/** Session-rule lookup — operation-scoped (see sessionStateById doc). Deny
|
|
185
252
|
* wins over allow if both somehow match (conservative). Null = no rule. */
|
|
186
|
-
checkSessionRules(req) {
|
|
187
|
-
if (
|
|
253
|
+
checkSessionRules(req, state) {
|
|
254
|
+
if (state.denyRules.some((r) => ruleMatches(r, req.toolName, req.args))) {
|
|
188
255
|
return { approved: false };
|
|
189
256
|
}
|
|
190
|
-
if (
|
|
257
|
+
if (state.allowRules.some((r) => ruleMatches(r, req.toolName, req.args))) {
|
|
191
258
|
return { approved: true };
|
|
192
259
|
}
|
|
193
260
|
return null;
|
|
194
261
|
}
|
|
195
262
|
/** The actual interactive ask + rule recording (runs inside the prompt turn). */
|
|
196
|
-
async promptAndRecord(req) {
|
|
263
|
+
async promptAndRecord(req, state) {
|
|
197
264
|
if (!this.promptFn) {
|
|
198
265
|
return { approved: false, reason: "interactive approval backend has no prompt function" };
|
|
199
266
|
}
|
|
200
267
|
const result = await this.promptFn(req);
|
|
201
268
|
const scope = result.scope ?? (result.always ? "session" : "once");
|
|
269
|
+
const activeState = this.isActiveSessionState(req.sessionId, state) ? state : null;
|
|
270
|
+
const context = activeState ?? (!req.sessionId ? this.legacyContext : null);
|
|
202
271
|
// Path narrowing only rides on an APPROVE: a path-scoped deny is confusing
|
|
203
272
|
// (deny stays tool-wide). pathScope is ignored by buildProjectRule for
|
|
204
273
|
// non-file tools / when absent.
|
|
205
274
|
const ruleOpts = result.approved
|
|
206
|
-
? { pathScope: result.pathScope, cwd:
|
|
275
|
+
? { pathScope: result.pathScope, cwd: context?.cwd ?? undefined }
|
|
207
276
|
: undefined;
|
|
208
277
|
if (scope === "session" && result.always) {
|
|
278
|
+
if (!activeState) {
|
|
279
|
+
rootPermLogger.warn("permission.session_remember_ignored", {
|
|
280
|
+
cat: "permission",
|
|
281
|
+
tool: req.toolName,
|
|
282
|
+
reason: req.sessionId ? "session_closed" : "missing_session_id",
|
|
283
|
+
});
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
209
286
|
// Narrow to the operation (Bash → head command, file tools → path scope)
|
|
210
287
|
// so the session grant is scoped, not tool-wide. A rule with no
|
|
211
288
|
// argsPattern keeps the prior tool-granularity behavior.
|
|
212
289
|
const rule = buildProjectRule(req.toolName, req.args, ruleOpts);
|
|
213
290
|
if (rule) {
|
|
214
|
-
const target = result.approved ?
|
|
291
|
+
const target = result.approved ? activeState.allowRules : activeState.denyRules;
|
|
215
292
|
const dup = target.some((r) => r.tool === rule.tool &&
|
|
216
293
|
JSON.stringify(r.argsPattern) === JSON.stringify(rule.argsPattern));
|
|
217
294
|
if (!dup) {
|
|
@@ -225,23 +302,23 @@ export class InteractiveApprovalBackend {
|
|
|
225
302
|
// We only persist allow rules — denies stay session-only because a
|
|
226
303
|
// persisted deny is harder to recover from than a session deny.
|
|
227
304
|
const rule = buildProjectRule(req.toolName, req.args, ruleOpts);
|
|
228
|
-
if (rule &&
|
|
305
|
+
if (rule && context?.cwd) {
|
|
229
306
|
try {
|
|
230
|
-
persistProjectRule(
|
|
307
|
+
persistProjectRule(context.cwd, rule);
|
|
231
308
|
// Dedup against the in-memory list using the same equality used by
|
|
232
309
|
// persistProjectRule so re-prompts on the same tool/argsPattern
|
|
233
310
|
// don't bloat the live rule set.
|
|
234
|
-
const dup =
|
|
311
|
+
const dup = context.savedProjectRules.some((r) => r.tool === rule.tool &&
|
|
235
312
|
r.decision === rule.decision &&
|
|
236
313
|
JSON.stringify(r.argsPattern) === JSON.stringify(rule.argsPattern));
|
|
237
314
|
if (!dup)
|
|
238
|
-
|
|
239
|
-
|
|
315
|
+
context.savedProjectRules.push(rule);
|
|
316
|
+
context.onProjectRules?.(context.savedProjectRules);
|
|
240
317
|
rootPermLogger.info("permission.persist", {
|
|
241
318
|
cat: "permission",
|
|
242
319
|
tool: rule.tool,
|
|
243
320
|
decision: rule.decision,
|
|
244
|
-
totalProjectRules:
|
|
321
|
+
totalProjectRules: context.savedProjectRules.length,
|
|
245
322
|
duplicate: dup,
|
|
246
323
|
});
|
|
247
324
|
}
|
|
@@ -258,11 +335,11 @@ export class InteractiveApprovalBackend {
|
|
|
258
335
|
// Also seed the session allow list (operation-scoped) so the rest of
|
|
259
336
|
// this REPL session benefits even if the classifier path doesn't pick
|
|
260
337
|
// the rule up immediately.
|
|
261
|
-
if (rule) {
|
|
262
|
-
const dup =
|
|
338
|
+
if (rule && activeState) {
|
|
339
|
+
const dup = activeState.allowRules.some((r) => r.tool === rule.tool &&
|
|
263
340
|
JSON.stringify(r.argsPattern) === JSON.stringify(rule.argsPattern));
|
|
264
341
|
if (!dup)
|
|
265
|
-
|
|
342
|
+
activeState.allowRules.push(rule);
|
|
266
343
|
}
|
|
267
344
|
}
|
|
268
345
|
return result;
|
|
@@ -452,6 +529,12 @@ export function getInteractiveApprovalBackend() {
|
|
|
452
529
|
}
|
|
453
530
|
return _interactiveBackend;
|
|
454
531
|
}
|
|
532
|
+
export function openInteractiveApprovalSession(sessionId) {
|
|
533
|
+
getInteractiveApprovalBackend().openSession(sessionId);
|
|
534
|
+
}
|
|
535
|
+
export function clearInteractiveApprovalSession(sessionId) {
|
|
536
|
+
getInteractiveApprovalBackend().clearSession(sessionId);
|
|
537
|
+
}
|
|
455
538
|
export function setInteractiveApprovalFn(fn) {
|
|
456
539
|
getInteractiveApprovalBackend().setPromptFn(fn);
|
|
457
540
|
}
|
|
@@ -896,9 +979,7 @@ export class PermissionClassifier {
|
|
|
896
979
|
let result;
|
|
897
980
|
try {
|
|
898
981
|
const baseDescription = this.describeToolCall(toolName, args);
|
|
899
|
-
const description = reason
|
|
900
|
-
? `${baseDescription}\n\nReason (from pre_tool_use hook): ${reason}`
|
|
901
|
-
: baseDescription;
|
|
982
|
+
const description = reason ? `${baseDescription}\n\nReason:\n${reason}` : baseDescription;
|
|
902
983
|
result = await this.approvalBackend.requestApproval({
|
|
903
984
|
...(opts?.sessionId ? { sessionId: opts.sessionId } : {}),
|
|
904
985
|
toolName,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unified tool registry — built-in tools + MCP tools.
|
|
3
3
|
*/
|
|
4
|
-
import { ConfigError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError } from "../exceptions.js";
|
|
4
|
+
import { ConfigError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, } from "../exceptions.js";
|
|
5
5
|
import { BUILTIN_TOOLS } from "./builtin/index.js";
|
|
6
6
|
import { validateToolMetadata } from "./validate-tool-metadata.js";
|
|
7
7
|
/**
|
|
@@ -22,7 +22,9 @@ export class ToolRegistry {
|
|
|
22
22
|
if (selectedNames) {
|
|
23
23
|
const unknown = [...selectedNames].filter((name) => !availableNames.has(name));
|
|
24
24
|
if (unknown.length > 0) {
|
|
25
|
-
throw new ConfigError(`Unknown built-in tool(s): ${unknown.join(", ")}`, {
|
|
25
|
+
throw new ConfigError(`Unknown built-in tool(s): ${unknown.join(", ")}`, {
|
|
26
|
+
unknownBuiltinTools: unknown,
|
|
27
|
+
});
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
for (const tool of BUILTIN_TOOLS) {
|
|
@@ -120,12 +122,18 @@ export class ToolRegistry {
|
|
|
120
122
|
// 各取所有,缺的字段为 undefined。
|
|
121
123
|
const contentBlocks = "contentBlocks" in result ? result.contentBlocks : undefined;
|
|
122
124
|
const sandbox = "sandbox" in result ? result.sandbox : undefined;
|
|
125
|
+
const sensitive = "sensitive" in result ? result.sensitive : undefined;
|
|
126
|
+
const displayResult = "displayResult" in result ? result.displayResult : undefined;
|
|
127
|
+
const transcriptResult = "transcriptResult" in result ? result.transcriptResult : undefined;
|
|
123
128
|
return {
|
|
124
129
|
id,
|
|
125
130
|
toolName: name,
|
|
126
131
|
result: result.result ?? (contentBlocks ? "(image)" : ""),
|
|
127
132
|
contentBlocks,
|
|
128
133
|
sandbox,
|
|
134
|
+
sensitive,
|
|
135
|
+
displayResult,
|
|
136
|
+
transcriptResult,
|
|
129
137
|
};
|
|
130
138
|
}
|
|
131
139
|
catch (err) {
|
|
@@ -133,8 +141,7 @@ export class ToolRegistry {
|
|
|
133
141
|
parentSignal?.removeEventListener("abort", onParentAbort);
|
|
134
142
|
// Always return error as ToolResult, never throw
|
|
135
143
|
let errorMsg;
|
|
136
|
-
const isAbort = err?.name === "AbortError" ||
|
|
137
|
-
parentSignal?.aborted === true;
|
|
144
|
+
const isAbort = err?.name === "AbortError" || parentSignal?.aborted === true;
|
|
138
145
|
if (err instanceof ToolTimeoutError) {
|
|
139
146
|
errorMsg = `Tool timed out after ${timeout}ms: ${name}`;
|
|
140
147
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Message, ToolResult } from "../types.js";
|
|
2
|
+
export declare const SENSITIVE_TOOL_RESULT_PLACEHOLDER = "[credential value withheld]";
|
|
3
|
+
export declare function toolResultDisplayText(result: ToolResult): string | undefined;
|
|
4
|
+
export declare function toolResultTranscriptText(result: ToolResult): string | undefined;
|
|
5
|
+
export declare function toolResultForDisplay(result: ToolResult): ToolResult;
|
|
6
|
+
export declare function toolResultsForDisplay(results: ToolResult[]): ToolResult[];
|
|
7
|
+
export declare function redactSensitiveToolResultsInMessages(messages: Message[], redactions: ReadonlyMap<string, string>): Message[];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const SENSITIVE_TOOL_RESULT_PLACEHOLDER = "[credential value withheld]";
|
|
2
|
+
export function toolResultDisplayText(result) {
|
|
3
|
+
if (!result.sensitive)
|
|
4
|
+
return result.result;
|
|
5
|
+
return result.displayResult ?? result.transcriptResult ?? SENSITIVE_TOOL_RESULT_PLACEHOLDER;
|
|
6
|
+
}
|
|
7
|
+
export function toolResultTranscriptText(result) {
|
|
8
|
+
if (!result.sensitive)
|
|
9
|
+
return result.result;
|
|
10
|
+
return result.transcriptResult ?? result.displayResult ?? SENSITIVE_TOOL_RESULT_PLACEHOLDER;
|
|
11
|
+
}
|
|
12
|
+
export function toolResultForDisplay(result) {
|
|
13
|
+
if (!result.sensitive)
|
|
14
|
+
return result;
|
|
15
|
+
const { displayResult: _displayResult, transcriptResult: _transcriptResult, ...rest } = result;
|
|
16
|
+
return {
|
|
17
|
+
...rest,
|
|
18
|
+
result: toolResultDisplayText(result),
|
|
19
|
+
contentBlocks: undefined,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function toolResultsForDisplay(results) {
|
|
23
|
+
return results.map((result) => toolResultForDisplay(result));
|
|
24
|
+
}
|
|
25
|
+
export function redactSensitiveToolResultsInMessages(messages, redactions) {
|
|
26
|
+
if (redactions.size === 0)
|
|
27
|
+
return messages;
|
|
28
|
+
let changed = false;
|
|
29
|
+
const out = messages.map((message) => {
|
|
30
|
+
if (!Array.isArray(message.content))
|
|
31
|
+
return message;
|
|
32
|
+
let contentChanged = false;
|
|
33
|
+
const content = message.content.map((block) => {
|
|
34
|
+
if (block.type !== "tool_result")
|
|
35
|
+
return block;
|
|
36
|
+
const replacement = typeof block.tool_use_id === "string" ? redactions.get(block.tool_use_id) : undefined;
|
|
37
|
+
if (replacement === undefined)
|
|
38
|
+
return block;
|
|
39
|
+
contentChanged = true;
|
|
40
|
+
return { ...block, content: replacement };
|
|
41
|
+
});
|
|
42
|
+
if (!contentChanged)
|
|
43
|
+
return message;
|
|
44
|
+
changed = true;
|
|
45
|
+
return { ...message, content };
|
|
46
|
+
});
|
|
47
|
+
return changed ? out : messages;
|
|
48
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SessionWorkspace } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* WorkspaceBridge — host-backed session workspace switching.
|
|
4
|
+
*
|
|
5
|
+
* Desktop implements this through Electron main so model-initiated switches use
|
|
6
|
+
* the same service path as the UI workspace switcher. Undefined in headless or
|
|
7
|
+
* non-desktop contexts.
|
|
8
|
+
*/
|
|
9
|
+
export interface WorkspaceBridge {
|
|
10
|
+
switch(target: string): Promise<SessionWorkspace>;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types.d.ts
CHANGED
|
@@ -43,7 +43,15 @@ export interface ToolCall {
|
|
|
43
43
|
export interface ToolResult {
|
|
44
44
|
id: string;
|
|
45
45
|
toolName: string;
|
|
46
|
+
/**
|
|
47
|
+
* Sensitive tool results keep `result` as the model-facing value for the
|
|
48
|
+
* current model round only. Persisted/displayed/streamed observers must use
|
|
49
|
+
* `transcriptResult`/`displayResult` (or the standard placeholder) instead.
|
|
50
|
+
*/
|
|
51
|
+
sensitive?: boolean;
|
|
46
52
|
result?: string;
|
|
53
|
+
displayResult?: string;
|
|
54
|
+
transcriptResult?: string;
|
|
47
55
|
/**
|
|
48
56
|
* 结构化结果块(目前仅图片)。存在时优先于 `result` 用作发给 LLM 的
|
|
49
57
|
* tool_result content —— view_image 用它把本地图片以 image ContentBlock
|
|
@@ -54,13 +62,13 @@ export interface ToolResult {
|
|
|
54
62
|
error?: string;
|
|
55
63
|
isError?: boolean;
|
|
56
64
|
/**
|
|
57
|
-
* Set by tools
|
|
58
|
-
* worktree) so the UI can show whether THIS call was isolated. `backend`
|
|
65
|
+
* Set by command-executing tools with sandbox visibility (Bash / background
|
|
66
|
+
* shell / worktree / PowerShell) so the UI can show whether THIS call was isolated. `backend`
|
|
59
67
|
* "off" means the command ran un-sandboxed (we surface it explicitly so the
|
|
60
68
|
* user sees "未隔离" rather than guessing from an absent badge); "seatbelt"
|
|
61
69
|
* / "bwrap" mean OS-level isolation applied. `network` is the policy that
|
|
62
|
-
* was in force (absent when off). Tools
|
|
63
|
-
*
|
|
70
|
+
* was in force (absent when off). Tools with no sandbox visibility leave this
|
|
71
|
+
* undefined and the UI renders no badge.
|
|
64
72
|
*/
|
|
65
73
|
sandbox?: {
|
|
66
74
|
backend: "off" | "seatbelt" | "bwrap";
|
|
@@ -97,6 +105,12 @@ export interface RegisteredTool {
|
|
|
97
105
|
inputSchema: Record<string, unknown>;
|
|
98
106
|
source: ToolSource;
|
|
99
107
|
serverName?: string;
|
|
108
|
+
/**
|
|
109
|
+
* Declarative UI/metadata hint for hosts, docs, and capability listings.
|
|
110
|
+
* It is not an execution-policy input: PermissionClassifier does not read
|
|
111
|
+
* RegisteredTool and runtime decisions come from explicit rules, permission
|
|
112
|
+
* mode, approval backend, and tool-specific gates.
|
|
113
|
+
*/
|
|
100
114
|
permissionDefault: PermissionDecision;
|
|
101
115
|
isConcurrencySafe?: boolean;
|
|
102
116
|
isReadOnly?: boolean;
|
|
@@ -139,13 +153,51 @@ export type SessionStatus = "active" | "paused" | TerminalReason;
|
|
|
139
153
|
/** Which host/context created a session — used by the desktop disk-rebuild to
|
|
140
154
|
* filter the sidebar (only `desktop` + `automation` are shown). */
|
|
141
155
|
export type SessionOrigin = "desktop" | "tui" | "automation" | "subagent";
|
|
156
|
+
export interface SessionWorkspace {
|
|
157
|
+
root: string;
|
|
158
|
+
kind: "main" | "worktree";
|
|
159
|
+
worktree?: {
|
|
160
|
+
path: string;
|
|
161
|
+
branch: string;
|
|
162
|
+
baseRef: string;
|
|
163
|
+
createdBy: "codeshell";
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
export interface ContextUsageAnchor {
|
|
167
|
+
promptTokens: number;
|
|
168
|
+
messageCount: number;
|
|
169
|
+
estimateAtAnchor?: number;
|
|
170
|
+
recordedAt: number;
|
|
171
|
+
provider?: string;
|
|
172
|
+
model?: string;
|
|
173
|
+
}
|
|
142
174
|
export interface SessionState {
|
|
143
175
|
sessionId: string;
|
|
144
176
|
cwd: string;
|
|
177
|
+
/** Current session workspace pointer. Absent only on legacy state.json files. */
|
|
178
|
+
workspace?: SessionWorkspace;
|
|
145
179
|
startedAt: number;
|
|
146
180
|
model: string;
|
|
147
181
|
provider: string;
|
|
182
|
+
/**
|
|
183
|
+
* Legacy/model-scoped usage summary. Some flows reset this accounting window
|
|
184
|
+
* (for example model switches), so it must not drive whole-session metrics.
|
|
185
|
+
*/
|
|
148
186
|
tokenUsage: TokenUsage;
|
|
187
|
+
/**
|
|
188
|
+
* Last provider-reported prompt-token count for the current context window.
|
|
189
|
+
* Separate from cumulative tokenUsage; used only to seed context-size estimates
|
|
190
|
+
* before the next provider response.
|
|
191
|
+
*/
|
|
192
|
+
contextUsageAnchor?: ContextUsageAnchor;
|
|
193
|
+
/**
|
|
194
|
+
* Monotonic prompt-cache counters for the whole session. These only increase
|
|
195
|
+
* from session start and are separate from the resettable tokenUsage window.
|
|
196
|
+
* Optional on legacy state.json files; SessionManager normalizes them on load.
|
|
197
|
+
*/
|
|
198
|
+
cumulativePromptTokens?: number;
|
|
199
|
+
cumulativeCacheReadTokens?: number;
|
|
200
|
+
cumulativeCacheCreationTokens?: number;
|
|
149
201
|
turnCount: number;
|
|
150
202
|
/**
|
|
151
203
|
* Conversation-turn counter where ONE user message = one turn (incremented in
|
|
@@ -281,10 +333,14 @@ export interface TaskInfo {
|
|
|
281
333
|
activeForm?: string;
|
|
282
334
|
status: "pending" | "in_progress" | "completed" | "stopped";
|
|
283
335
|
}
|
|
336
|
+
export type PromptTokenSource = "provider_usage" | "anchor_delta" | "anchor_rescale" | "calibrated_estimate" | "heuristic_estimate" | "session_cumulative";
|
|
337
|
+
export type PromptTokenConfidence = "high" | "medium" | "low";
|
|
284
338
|
export type StreamEvent = {
|
|
285
339
|
type: "session_started";
|
|
286
340
|
sessionId: string;
|
|
287
341
|
promptTokens: number;
|
|
342
|
+
promptTokensSource?: PromptTokenSource;
|
|
343
|
+
promptTokensConfidence?: PromptTokenConfidence;
|
|
288
344
|
} | {
|
|
289
345
|
type: "session_title";
|
|
290
346
|
sessionId: string;
|
|
@@ -292,6 +348,7 @@ export type StreamEvent = {
|
|
|
292
348
|
} | {
|
|
293
349
|
type: "stream_request_start";
|
|
294
350
|
turnNumber: number;
|
|
351
|
+
messageId?: string;
|
|
295
352
|
agentId?: string;
|
|
296
353
|
} | {
|
|
297
354
|
type: "steer_injected";
|
|
@@ -318,6 +375,7 @@ export type StreamEvent = {
|
|
|
318
375
|
} | {
|
|
319
376
|
type: "assistant_message";
|
|
320
377
|
message: Message;
|
|
378
|
+
messageId?: string;
|
|
321
379
|
agentId?: string;
|
|
322
380
|
} | {
|
|
323
381
|
type: "turn_complete";
|
|
@@ -348,6 +406,7 @@ export type StreamEvent = {
|
|
|
348
406
|
} | {
|
|
349
407
|
type: "tombstone";
|
|
350
408
|
messageId: string;
|
|
409
|
+
agentId?: string;
|
|
351
410
|
} | {
|
|
352
411
|
type: "task_update";
|
|
353
412
|
tasks: TaskInfo[];
|
|
@@ -389,15 +448,19 @@ export type StreamEvent = {
|
|
|
389
448
|
} | {
|
|
390
449
|
type: "tool_summary";
|
|
391
450
|
summary: string;
|
|
451
|
+
toolCallIds?: string[];
|
|
452
|
+
agentId?: string;
|
|
392
453
|
} | {
|
|
393
454
|
type: "context_compact";
|
|
394
|
-
strategy: "micro" | "summary" | "window" | "snip" | "emergency";
|
|
455
|
+
strategy: "micro" | "summary" | "window" | "snip" | "emergency" | "compacted";
|
|
395
456
|
before: number;
|
|
396
457
|
after: number;
|
|
397
458
|
agentId?: string;
|
|
398
459
|
} | {
|
|
399
460
|
type: "usage_update";
|
|
400
461
|
promptTokens: number;
|
|
462
|
+
promptTokensSource?: PromptTokenSource;
|
|
463
|
+
promptTokensConfidence?: PromptTokenConfidence;
|
|
401
464
|
/**
|
|
402
465
|
* Provider-reported prompt-cache counts, forwarded so the UI can show a
|
|
403
466
|
* cache hit rate in the context-ring tooltip. Present only when the LLM
|
|
@@ -407,6 +470,24 @@ export type StreamEvent = {
|
|
|
407
470
|
*/
|
|
408
471
|
cacheReadTokens?: number;
|
|
409
472
|
cacheCreationTokens?: number;
|
|
473
|
+
/**
|
|
474
|
+
* Prompt-cache metrics for the current turn only. These are reset at the
|
|
475
|
+
* start of each turn-loop iteration and sum every LLM response in that
|
|
476
|
+
* turn, including max-token continuations.
|
|
477
|
+
*/
|
|
478
|
+
singleTurnPromptTokens?: number;
|
|
479
|
+
singleTurnCacheReadTokens?: number;
|
|
480
|
+
singleTurnCacheCreationTokens?: number;
|
|
481
|
+
singleTurnCacheHitRate?: number;
|
|
482
|
+
/**
|
|
483
|
+
* Whole-session monotonic prompt-cache counters/rate. Unlike the legacy
|
|
484
|
+
* session* fields below, these are not derived from the resettable
|
|
485
|
+
* tokenUsage accounting window.
|
|
486
|
+
*/
|
|
487
|
+
cumulativePromptTokens?: number;
|
|
488
|
+
cumulativeCacheReadTokens?: number;
|
|
489
|
+
cumulativeCacheCreationTokens?: number;
|
|
490
|
+
cumulativeCacheHitRate?: number;
|
|
410
491
|
/**
|
|
411
492
|
* SESSION-CUMULATIVE cache counts (sum across every LLM response this
|
|
412
493
|
* session, across runs and turns), emitted from the engine's turn
|