@cjhyy/code-shell-core 0.6.0-rc.14 → 0.6.0-rc.15
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/capability-control/service.d.ts +2 -0
- package/dist/capability-control/service.js +4 -2
- package/dist/cc-orchestrator/codex-session-history.js +1 -1
- package/dist/cc-orchestrator/external-agent-bindings.js +1 -1
- package/dist/cc-orchestrator/external-agent-session-store.js +3 -1
- package/dist/cc-orchestrator/session-history.js +2 -2
- package/dist/cli/agent-server-stdio.js +7 -2
- package/dist/context/manager.d.ts +11 -2
- package/dist/context/manager.js +64 -3
- package/dist/credentials/inject-credential-tool.d.ts +3 -1
- package/dist/credentials/inject-credential-tool.js +29 -10
- package/dist/credentials/use-credential-tool.d.ts +1 -0
- package/dist/credentials/use-credential-tool.js +3 -0
- package/dist/engine/engine.d.ts +9 -0
- package/dist/engine/engine.js +96 -17
- package/dist/engine/turn-loop.d.ts +3 -1
- package/dist/engine/turn-loop.js +3 -1
- 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 -127
- package/dist/git/worktree.js +5 -464
- package/dist/index.d.ts +28 -27
- package/dist/index.js +24 -24
- 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/parseSource.d.ts +4 -1
- package/dist/plugins/installer/parseSource.js +28 -10
- 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/preset/index.d.ts +1 -0
- package/dist/preset/index.js +6 -0
- package/dist/prompt/sections/base.md +1 -1
- package/dist/protocol/chat-session-manager.js +7 -0
- package/dist/protocol/server.d.ts +10 -0
- package/dist/protocol/server.js +88 -4
- package/dist/protocol/types.d.ts +6 -0
- package/dist/protocol/types.js +2 -0
- package/dist/runtime/background-shell.js +2 -3
- 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 +56 -17
- package/dist/session/memory.js +337 -78
- package/dist/session/session-manager.d.ts +1 -1
- package/dist/session/session-manager.js +6 -6
- package/dist/settings/manager.js +43 -12
- package/dist/settings/schema.d.ts +21 -0
- package/dist/settings/schema.js +12 -0
- package/dist/tool-system/builtin/index.js +18 -8
- package/dist/tool-system/builtin/memory.js +40 -8
- package/dist/tool-system/builtin/worktree.d.ts +2 -0
- package/dist/tool-system/builtin/worktree.js +59 -11
- package/dist/tool-system/context.d.ts +11 -1
- package/dist/tool-system/mcp-manager.d.ts +8 -5
- package/dist/tool-system/mcp-manager.js +85 -55
- package/dist/tool-system/path-policy.d.ts +2 -0
- package/dist/tool-system/path-policy.js +28 -12
- package/dist/tool-system/workspace-bridge.d.ts +11 -0
- package/dist/tool-system/workspace-bridge.js +1 -0
- package/dist/types.d.ts +14 -0
- package/package.json +1 -1
|
@@ -9,9 +9,10 @@ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/
|
|
|
9
9
|
import { logger } from "../logging/logger.js";
|
|
10
10
|
import { CredentialStore } from "../credentials/index.js";
|
|
11
11
|
import { ENV_ALLOWLIST } from "../runtime/spawn-common.js";
|
|
12
|
-
import {
|
|
12
|
+
import { mkdir, readdir, unlink, writeFile } from "node:fs/promises";
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import { diagnoseMcpStdioMissingCommand, previewPath } from "./mcp-stdio-diagnostics.js";
|
|
15
|
+
import { codeShellHome } from "../session/session-manager.js";
|
|
15
16
|
/**
|
|
16
17
|
* Read a required secret from `process.env` by NAME (Codex-style env-secret
|
|
17
18
|
* handling — the value is never persisted in MCP config). A referenced env var
|
|
@@ -118,7 +119,29 @@ export function inferTransportType(config) {
|
|
|
118
119
|
* spill itself is bounded by the byte budget below; this cap is just
|
|
119
120
|
* to keep ls(~/.code-shell/mcp_images) tractable.
|
|
120
121
|
*/
|
|
122
|
+
const MAX_MCP_IMAGE_SPILLS_PER_TOOL = 50;
|
|
121
123
|
const MAX_MCP_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
124
|
+
function spillTimestamp(filename, prefix) {
|
|
125
|
+
const suffix = filename.slice(prefix.length);
|
|
126
|
+
const dot = suffix.lastIndexOf(".");
|
|
127
|
+
const raw = dot === -1 ? suffix : suffix.slice(0, dot);
|
|
128
|
+
const timestamp = Number(raw);
|
|
129
|
+
return Number.isFinite(timestamp) ? timestamp : 0;
|
|
130
|
+
}
|
|
131
|
+
async function pruneMcpImageSpills(baseDir, prefix) {
|
|
132
|
+
const entries = await readdir(baseDir, { withFileTypes: true });
|
|
133
|
+
const spills = entries
|
|
134
|
+
.filter((entry) => entry.isFile() && entry.name.startsWith(prefix))
|
|
135
|
+
.map((entry) => ({
|
|
136
|
+
name: entry.name,
|
|
137
|
+
timestamp: spillTimestamp(entry.name, prefix),
|
|
138
|
+
}))
|
|
139
|
+
.sort((a, b) => a.timestamp - b.timestamp || a.name.localeCompare(b.name));
|
|
140
|
+
const excess = spills.length - MAX_MCP_IMAGE_SPILLS_PER_TOOL;
|
|
141
|
+
if (excess <= 0)
|
|
142
|
+
return;
|
|
143
|
+
await Promise.all(spills.slice(0, excess).map((entry) => unlink(join(baseDir, entry.name)).catch(() => { })));
|
|
144
|
+
}
|
|
122
145
|
/**
|
|
123
146
|
* Persist an MCP-returned image to disk and return the textual
|
|
124
147
|
* reference the LLM should see. Images larger than
|
|
@@ -130,9 +153,7 @@ const MAX_MCP_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
|
130
153
|
* [mcp-image] server=playwright tool=screenshot saved=/abs/path.png (123 KB)
|
|
131
154
|
*/
|
|
132
155
|
export async function spillMcpImage(serverName, toolName, base64, mimeType, opts) {
|
|
133
|
-
const
|
|
134
|
-
const baseDir = opts?.baseDir ??
|
|
135
|
-
(home ? join(home, ".code-shell", "mcp_images") : join("/tmp", "code-shell-mcp-images"));
|
|
156
|
+
const baseDir = opts?.baseDir ?? join(codeShellHome(), "mcp_images");
|
|
136
157
|
const now = opts?.now ?? Date.now;
|
|
137
158
|
const decodedBytes = Math.floor((base64.length * 3) / 4);
|
|
138
159
|
if (decodedBytes > MAX_MCP_IMAGE_BYTES) {
|
|
@@ -147,11 +168,13 @@ export async function spillMcpImage(serverName, toolName, base64, mimeType, opts
|
|
|
147
168
|
: "png";
|
|
148
169
|
const safeServer = serverName.replace(/[^\w.-]+/g, "_");
|
|
149
170
|
const safeTool = toolName.replace(/[^\w.-]+/g, "_");
|
|
150
|
-
const
|
|
171
|
+
const prefix = `${safeServer}-${safeTool}-`;
|
|
172
|
+
const filename = `${prefix}${now()}.${ext}`;
|
|
151
173
|
const filePath = join(baseDir, filename);
|
|
152
174
|
try {
|
|
153
175
|
await mkdir(baseDir, { recursive: true });
|
|
154
176
|
await writeFile(filePath, Buffer.from(base64, "base64"));
|
|
177
|
+
await pruneMcpImageSpills(baseDir, prefix);
|
|
155
178
|
}
|
|
156
179
|
catch (err) {
|
|
157
180
|
logger.warn("mcp.image_spill_failed", {
|
|
@@ -448,59 +471,65 @@ export class MCPManager {
|
|
|
448
471
|
* Discover tools from an MCP server and register them.
|
|
449
472
|
*/
|
|
450
473
|
async discoverTools(serverName, client) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
474
|
+
try {
|
|
475
|
+
const result = await client.listTools();
|
|
476
|
+
for (const tool of result.tools) {
|
|
477
|
+
const registered = buildRegisteredTool(serverName, tool);
|
|
478
|
+
// Register with an executor that calls the MCP server
|
|
479
|
+
this.toolRegistry.registerTool(registered, async (args, ctx) => {
|
|
480
|
+
const callResult = await client.callTool({
|
|
481
|
+
name: tool.name,
|
|
482
|
+
arguments: stripInternalToolArgs(args),
|
|
483
|
+
}, undefined, ctx?.signal ? { signal: ctx.signal } : undefined);
|
|
484
|
+
// Extract text + image content from the result. Image blobs
|
|
485
|
+
// are spilled to ~/.code-shell/mcp_images/ so they don't bloat
|
|
486
|
+
// the LLM message tree — same pattern as the GenerateImage
|
|
487
|
+
// tool — and the model sees a textual reference it can Read
|
|
488
|
+
// on a later turn if it needs the pixels. This sidesteps the
|
|
489
|
+
// "MCP returned a 5MB screenshot → token budget exploded"
|
|
490
|
+
// failure mode that bit Codex (issue #11845); we never put
|
|
491
|
+
// raw base64 image data in the result text. See
|
|
492
|
+
// TODO-week.md #9c + docs/research-cc-vs-codex-image-handling.md §B.
|
|
493
|
+
const parts = [];
|
|
494
|
+
if (Array.isArray(callResult.content)) {
|
|
495
|
+
for (const item of callResult.content) {
|
|
496
|
+
if (typeof item === "string") {
|
|
497
|
+
parts.push(item);
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (typeof item !== "object" || item === null)
|
|
501
|
+
continue;
|
|
502
|
+
if ("text" in item) {
|
|
503
|
+
parts.push(String(item.text));
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
if (item.type === "image") {
|
|
507
|
+
const block = item;
|
|
508
|
+
if (typeof block.data === "string" && block.data.length > 0) {
|
|
509
|
+
const note = await spillMcpImage(serverName, tool.name, block.data, block.mimeType ?? "image/png");
|
|
510
|
+
parts.push(note);
|
|
511
|
+
}
|
|
487
512
|
}
|
|
488
513
|
}
|
|
489
514
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
515
|
+
const body = parts.join("\n") || "(no output)";
|
|
516
|
+
// Trust boundary: MCP output is external content. Wrap it so the
|
|
517
|
+
// model sees an explicit reminder that the body comes from an
|
|
518
|
+
// untrusted server and any instructions inside are content, not
|
|
519
|
+
// commands. The marker is intentionally short so it doesn't bloat
|
|
520
|
+
// every tool result, but distinct enough that prompt-injected
|
|
521
|
+
// strings can't fake their way out.
|
|
522
|
+
return wrapMcpOutput(serverName, tool.name, body);
|
|
523
|
+
});
|
|
524
|
+
const set = this.registeredToolsByServer.get(serverName) ?? new Set();
|
|
525
|
+
set.add(registered.name);
|
|
526
|
+
this.registeredToolsByServer.set(serverName, set);
|
|
527
|
+
logger.info("mcp.tool_registered", { server: serverName, tool: registered.name });
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
catch (err) {
|
|
531
|
+
await this.disconnect(serverName);
|
|
532
|
+
throw err;
|
|
504
533
|
}
|
|
505
534
|
}
|
|
506
535
|
/**
|
|
@@ -583,6 +612,7 @@ export class MCPManager {
|
|
|
583
612
|
uri: r.uri,
|
|
584
613
|
name: r.name ?? r.uri,
|
|
585
614
|
description: r.description,
|
|
615
|
+
serverName: name,
|
|
586
616
|
});
|
|
587
617
|
}
|
|
588
618
|
}
|
|
@@ -52,6 +52,8 @@ export interface ClassifyOptions {
|
|
|
52
52
|
export type PathApprovalScope = "once" | "session" | "project";
|
|
53
53
|
/** Test seam: clear the in-memory session grants. */
|
|
54
54
|
export declare function _resetSessionPathGrants(): void;
|
|
55
|
+
export declare function openSessionPathApprovals(sessionId: string): void;
|
|
56
|
+
export declare function clearSessionPathApprovals(sessionId: string): void;
|
|
55
57
|
/**
|
|
56
58
|
* Classify a file path against the workspace + sensitive-path policy.
|
|
57
59
|
*
|
|
@@ -105,6 +105,10 @@ function expandTilde(p) {
|
|
|
105
105
|
}
|
|
106
106
|
/** sessionId → set of approved directory grants (prefix + operation). */
|
|
107
107
|
const sessionPathGrants = new Map();
|
|
108
|
+
/** Session ids explicitly closed by ChatSessionManager; late approvals must not recreate grants. */
|
|
109
|
+
const closedPathApprovalSessions = new Set();
|
|
110
|
+
/** Per-session prompt chains for enforcePathPolicyWithApproval (see comment there). */
|
|
111
|
+
const askChains = new Map();
|
|
108
112
|
/** True if a grant for `grantOp` authorizes an operation of `wantOp`. */
|
|
109
113
|
function grantCoversOp(grantOp, wantOp) {
|
|
110
114
|
// write ⊇ {read, write}; read ⊇ {read}.
|
|
@@ -198,6 +202,8 @@ function storedOp(g) {
|
|
|
198
202
|
function recordPathApproval(scope, resolved, operation, cwd, sessionId) {
|
|
199
203
|
if (scope === "once")
|
|
200
204
|
return;
|
|
205
|
+
if (sessionId && closedPathApprovalSessions.has(sessionId))
|
|
206
|
+
return;
|
|
201
207
|
const prefix = dirPrefix(dirname(resolved));
|
|
202
208
|
if (scope === "session") {
|
|
203
209
|
if (!sessionId)
|
|
@@ -271,6 +277,16 @@ function recordPathApproval(scope, resolved, operation, cwd, sessionId) {
|
|
|
271
277
|
/** Test seam: clear the in-memory session grants. */
|
|
272
278
|
export function _resetSessionPathGrants() {
|
|
273
279
|
sessionPathGrants.clear();
|
|
280
|
+
closedPathApprovalSessions.clear();
|
|
281
|
+
askChains.clear();
|
|
282
|
+
}
|
|
283
|
+
export function openSessionPathApprovals(sessionId) {
|
|
284
|
+
closedPathApprovalSessions.delete(sessionId);
|
|
285
|
+
}
|
|
286
|
+
export function clearSessionPathApprovals(sessionId) {
|
|
287
|
+
sessionPathGrants.delete(sessionId);
|
|
288
|
+
closedPathApprovalSessions.add(sessionId);
|
|
289
|
+
askChains.delete(sessionId);
|
|
274
290
|
}
|
|
275
291
|
/**
|
|
276
292
|
* Best-effort resolution. realpath fails when the path doesn't exist yet —
|
|
@@ -461,8 +477,8 @@ export function enforcePathPolicy(filePath, operation, workspaceRoot) {
|
|
|
461
477
|
}
|
|
462
478
|
// ask — MVP refuses with explanatory message until askUser plumbing
|
|
463
479
|
// lands. The conservative bias matches the plan's leaning answer for Q1.
|
|
464
|
-
return `Error: path requires approval — ${c.reason}. Path: ${c.resolvedPath}. ` +
|
|
465
|
-
`Set CODESHELL_PATH_POLICY=off to disable enforcement during a rollback
|
|
480
|
+
return (`Error: path requires approval — ${c.reason}. Path: ${c.resolvedPath}. ` +
|
|
481
|
+
`Set CODESHELL_PATH_POLICY=off to disable enforcement during a rollback.`);
|
|
466
482
|
}
|
|
467
483
|
export async function enforcePathPolicyWithApproval(filePath, operation, ctx) {
|
|
468
484
|
if (ctx?.cwd === undefined)
|
|
@@ -481,8 +497,8 @@ export async function enforcePathPolicyWithApproval(filePath, operation, ctx) {
|
|
|
481
497
|
return `Error: blocked by path policy — ${c.reason}. Path: ${c.resolvedPath}`;
|
|
482
498
|
}
|
|
483
499
|
if (operation === "write" && ctx.planMode) {
|
|
484
|
-
return `Error: blocked by path policy — ${c.reason}. Path: ${c.resolvedPath}. ` +
|
|
485
|
-
`Plan mode does not allow file writes
|
|
500
|
+
return (`Error: blocked by path policy — ${c.reason}. Path: ${c.resolvedPath}. ` +
|
|
501
|
+
`Plan mode does not allow file writes.`);
|
|
486
502
|
}
|
|
487
503
|
// Already approved this directory for this OPERATION (this session or
|
|
488
504
|
// persisted for the project)? Proceed without re-prompting — this is the fix
|
|
@@ -491,8 +507,8 @@ export async function enforcePathPolicyWithApproval(filePath, operation, ctx) {
|
|
|
491
507
|
if (isPathPreApproved(c.resolvedPath, operation, ctx.cwd, ctx.sessionId))
|
|
492
508
|
return null;
|
|
493
509
|
if (!ctx.askUser) {
|
|
494
|
-
return `Error: path requires approval — ${c.reason}. Path: ${c.resolvedPath}. ` +
|
|
495
|
-
`No interactive approval UI is available in this run
|
|
510
|
+
return (`Error: path requires approval — ${c.reason}. Path: ${c.resolvedPath}. ` +
|
|
511
|
+
`No interactive approval UI is available in this run.`);
|
|
496
512
|
}
|
|
497
513
|
// Serialize concurrent asks (per session) and RE-CHECK grants when our turn
|
|
498
514
|
// comes. Parallel tools hitting the same not-yet-approved directory all pass
|
|
@@ -502,7 +518,8 @@ export async function enforcePathPolicyWithApproval(filePath, operation, ctx) {
|
|
|
502
518
|
const chainKey = ctx.sessionId ?? "__global__";
|
|
503
519
|
const prevTurn = askChains.get(chainKey) ?? Promise.resolve();
|
|
504
520
|
let release;
|
|
505
|
-
|
|
521
|
+
const currentTurn = new Promise((r) => (release = r));
|
|
522
|
+
askChains.set(chainKey, currentTurn);
|
|
506
523
|
try {
|
|
507
524
|
await prevTurn;
|
|
508
525
|
if (isPathPreApproved(c.resolvedPath, operation, ctx.cwd, ctx.sessionId))
|
|
@@ -511,6 +528,9 @@ export async function enforcePathPolicyWithApproval(filePath, operation, ctx) {
|
|
|
511
528
|
}
|
|
512
529
|
finally {
|
|
513
530
|
release();
|
|
531
|
+
if (askChains.get(chainKey) === currentTurn) {
|
|
532
|
+
askChains.delete(chainKey);
|
|
533
|
+
}
|
|
514
534
|
}
|
|
515
535
|
}
|
|
516
536
|
/** The actual interactive ask — split out so the serialized section reads flat.
|
|
@@ -521,9 +541,7 @@ async function promptForPathApproval(c, operation, ctx) {
|
|
|
521
541
|
// "工作区外路径" header was misleading for sensitive-path asks.
|
|
522
542
|
const isSensitive = c.reason.startsWith("sensitive");
|
|
523
543
|
const what = operation === "read" ? "读取" : "写入";
|
|
524
|
-
const title = isSensitive
|
|
525
|
-
? `工具想${what}敏感文件`
|
|
526
|
-
: `工具想${what}工作区外路径`;
|
|
544
|
+
const title = isSensitive ? `工具想${what}敏感文件` : `工具想${what}工作区外路径`;
|
|
527
545
|
const header = isSensitive ? "敏感文件权限" : "路径权限";
|
|
528
546
|
// Scope options carry remembered grants for the directory of this path, so
|
|
529
547
|
// the same folder isn't re-prompted. Labels are matched by exact string
|
|
@@ -566,8 +584,6 @@ async function promptForPathApproval(c, operation, ctx) {
|
|
|
566
584
|
}
|
|
567
585
|
return `Error: path approval denied by user — ${c.reason}. Path: ${c.resolvedPath}`;
|
|
568
586
|
}
|
|
569
|
-
/** Per-session prompt chains for enforcePathPolicyWithApproval (see comment there). */
|
|
570
|
-
const askChains = new Map();
|
|
571
587
|
/**
|
|
572
588
|
* Internal: reset the "disabled warning" latch. Tests flip the env var
|
|
573
589
|
* between cases and need each one to be able to re-trigger the warning.
|
|
@@ -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
|
@@ -149,6 +149,14 @@ export interface SessionWorkspace {
|
|
|
149
149
|
createdBy: "codeshell";
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
|
+
export interface ContextUsageAnchor {
|
|
153
|
+
promptTokens: number;
|
|
154
|
+
messageCount: number;
|
|
155
|
+
estimateAtAnchor?: number;
|
|
156
|
+
recordedAt: number;
|
|
157
|
+
provider?: string;
|
|
158
|
+
model?: string;
|
|
159
|
+
}
|
|
152
160
|
export interface SessionState {
|
|
153
161
|
sessionId: string;
|
|
154
162
|
cwd: string;
|
|
@@ -162,6 +170,12 @@ export interface SessionState {
|
|
|
162
170
|
* (for example model switches), so it must not drive whole-session metrics.
|
|
163
171
|
*/
|
|
164
172
|
tokenUsage: TokenUsage;
|
|
173
|
+
/**
|
|
174
|
+
* Last provider-reported prompt-token count for the current context window.
|
|
175
|
+
* Separate from cumulative tokenUsage; used only to seed context-size estimates
|
|
176
|
+
* before the next provider response.
|
|
177
|
+
*/
|
|
178
|
+
contextUsageAnchor?: ContextUsageAnchor;
|
|
165
179
|
/**
|
|
166
180
|
* Monotonic prompt-cache counters for the whole session. These only increase
|
|
167
181
|
* from session start and are separate from the resettable tokenUsage window.
|
package/package.json
CHANGED