@cjhyy/code-shell-core 0.9.5 → 0.9.7
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/automation/scheduler.d.ts +3 -0
- package/dist/automation/scheduler.js +21 -0
- package/dist/context/manager.d.ts +8 -2
- package/dist/context/manager.js +20 -4
- package/dist/context/notes.d.ts +39 -0
- package/dist/context/notes.js +314 -0
- package/dist/credentials/access.d.ts +9 -1
- package/dist/credentials/access.js +15 -4
- package/dist/credentials/store.d.ts +11 -0
- package/dist/credentials/store.js +44 -9
- package/dist/credentials/types.d.ts +4 -0
- package/dist/credentials/types.js +4 -0
- package/dist/credentials/use-credential-tool.js +12 -2
- package/dist/engine/engine-workspace-authority.js +10 -3
- package/dist/engine/engine.d.ts +8 -4
- package/dist/engine/engine.js +115 -15
- package/dist/engine/prompt-cache-diagnostics.js +10 -1
- package/dist/engine/run-goal.js +7 -3
- package/dist/engine/run-tooling.d.ts +3 -0
- package/dist/engine/run-tooling.js +25 -23
- package/dist/engine/run-types.d.ts +20 -0
- package/dist/engine/run-workspace.js +5 -21
- package/dist/engine/subagent-spawner.d.ts +3 -0
- package/dist/engine/subagent-spawner.js +48 -17
- package/dist/engine/turn-loop.d.ts +14 -4
- package/dist/engine/turn-loop.js +120 -28
- package/dist/engine/types.d.ts +19 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/links/index.d.ts +1 -0
- package/dist/links/index.js +1 -0
- package/dist/links/link-action-tool.d.ts +2 -1
- package/dist/links/link-action-tool.js +80 -44
- package/dist/links/status.d.ts +53 -0
- package/dist/links/status.js +175 -0
- package/dist/llm/prompt-cache.d.ts +35 -3
- package/dist/llm/prompt-cache.js +63 -3
- package/dist/llm/providers/openai.d.ts +3 -0
- package/dist/llm/providers/openai.js +57 -14
- package/dist/prompt/section-loader.js +1 -0
- package/dist/prompt/sections/browser.md +4 -2
- package/dist/prompt/sections/context-notes.md +9 -0
- package/dist/protocol/background-result-wakeup.d.ts +8 -1
- package/dist/protocol/background-result-wakeup.js +76 -38
- package/dist/protocol/chat-session-manager.d.ts +7 -1
- package/dist/protocol/chat-session-manager.js +44 -8
- package/dist/protocol/chat-session.d.ts +2 -0
- package/dist/protocol/chat-session.js +4 -1
- package/dist/protocol/server.d.ts +4 -0
- package/dist/protocol/server.js +355 -62
- package/dist/protocol/session-message-result.d.ts +12 -0
- package/dist/protocol/session-message-result.js +42 -0
- package/dist/protocol/session-message-workspace.d.ts +21 -0
- package/dist/protocol/session-message-workspace.js +57 -0
- package/dist/protocol/types.d.ts +4 -0
- package/dist/session/session-manager.js +8 -7
- package/dist/session/session-message.d.ts +17 -2
- package/dist/session/transcript.d.ts +17 -0
- package/dist/session/transcript.js +271 -16
- package/dist/settings/schema.d.ts +9 -0
- package/dist/settings/schema.js +4 -0
- package/dist/themes/paths.js +20 -1
- package/dist/tool-system/browser-bridge.d.ts +21 -1
- package/dist/tool-system/browser-discovery.d.ts +6 -0
- package/dist/tool-system/browser-discovery.js +17 -0
- package/dist/tool-system/builtin/agent.js +23 -9
- package/dist/tool-system/builtin/browser-tools.js +30 -9
- package/dist/tool-system/builtin/context-notes.d.ts +12 -0
- package/dist/tool-system/builtin/context-notes.js +188 -0
- package/dist/tool-system/builtin/index.js +50 -3
- package/dist/tool-system/builtin/mcp-tools.d.ts +5 -3
- package/dist/tool-system/builtin/mcp-tools.js +10 -10
- package/dist/tool-system/builtin/send-message-to-session.js +19 -3
- package/dist/tool-system/builtin/tool-search.js +15 -3
- package/dist/tool-system/context.d.ts +9 -0
- package/dist/tool-system/executor.js +5 -3
- package/dist/tool-system/mcp-compat.d.ts +3 -0
- package/dist/tool-system/mcp-compat.js +51 -0
- package/dist/tool-system/mcp-manager.d.ts +27 -26
- package/dist/tool-system/mcp-manager.js +273 -111
- package/dist/tool-system/mcp-workspace.d.ts +18 -0
- package/dist/tool-system/mcp-workspace.js +56 -0
- package/dist/tool-system/permission.d.ts +6 -0
- package/dist/tool-system/permission.js +45 -9
- package/dist/tool-system/plan-mode-allowlist.js +5 -0
- package/dist/tool-system/sandbox/seatbelt.js +71 -2
- package/dist/tool-system/session-tool-host.js +9 -1
- package/dist/types.d.ts +4 -2
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { closeSync, constants, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { credentialAllowsEnvExposure, credentialSecretHint, } from "./types.js";
|
|
4
|
+
import { credentialAllowsEnvExposure, credentialSecretHint, isCredentialSecretAvailable, } from "./types.js";
|
|
5
5
|
import { getDefaultCredentialCipher } from "./cipher.js";
|
|
6
6
|
import { logger } from "../logging/logger.js";
|
|
7
7
|
import { summarizeOAuthCredentialSecret } from "./oauth.js";
|
|
@@ -329,6 +329,34 @@ export class CredentialStore {
|
|
|
329
329
|
return true;
|
|
330
330
|
});
|
|
331
331
|
}
|
|
332
|
+
/**
|
|
333
|
+
* Update an existing record while holding the store lock. The callback sees
|
|
334
|
+
* the latest decrypted record and may return undefined to decline the write.
|
|
335
|
+
* Unlike save(), this never recreates a deleted credential.
|
|
336
|
+
*/
|
|
337
|
+
updateExisting(scope, id, update) {
|
|
338
|
+
if (typeof id !== "string" || !id || id.length > MAX_CREDENTIAL_ID_CHARS || id.includes("\0")) {
|
|
339
|
+
throw new Error("invalid credential id");
|
|
340
|
+
}
|
|
341
|
+
let changed = false;
|
|
342
|
+
this.mutate(scope, (file) => {
|
|
343
|
+
const idx = file.credentials.findIndex((credential) => credential.id === id);
|
|
344
|
+
if (idx < 0)
|
|
345
|
+
return false;
|
|
346
|
+
const next = update(normalizeCredential(file.credentials[idx], true));
|
|
347
|
+
if (!next)
|
|
348
|
+
return false;
|
|
349
|
+
const normalized = normalizeCredential(next, true);
|
|
350
|
+
if (normalized.id !== id)
|
|
351
|
+
throw new Error("credential update cannot change id");
|
|
352
|
+
file.credentials[idx] = credentialAllowsEnvExposure(normalized.type)
|
|
353
|
+
? normalized
|
|
354
|
+
: { ...normalized, exposeAsEnv: undefined };
|
|
355
|
+
changed = true;
|
|
356
|
+
return true;
|
|
357
|
+
});
|
|
358
|
+
return changed;
|
|
359
|
+
}
|
|
332
360
|
remove(scope, id) {
|
|
333
361
|
if (typeof id !== "string" || !id || id.length > MAX_CREDENTIAL_ID_CHARS || id.includes("\0")) {
|
|
334
362
|
throw new Error("invalid credential id");
|
|
@@ -350,14 +378,20 @@ export class CredentialStore {
|
|
|
350
378
|
* same host-isolation contract as {@link envExposures} and top-level env.
|
|
351
379
|
*/
|
|
352
380
|
list(scope = "full") {
|
|
381
|
+
return this.listWithStatus(scope).credentials;
|
|
382
|
+
}
|
|
383
|
+
/** Preserve read failures so diagnostics can distinguish unknown state from an empty store. */
|
|
384
|
+
listWithStatus(scope = "full") {
|
|
353
385
|
const byId = new Map();
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
386
|
+
let readable = true;
|
|
387
|
+
const scopes = scope === "full" ? ["user", "project"] : ["project"];
|
|
388
|
+
for (const layer of scopes) {
|
|
389
|
+
const result = this.readGuarded(layer);
|
|
390
|
+
readable = readable && result.readable;
|
|
391
|
+
for (const credential of result.file.credentials)
|
|
392
|
+
byId.set(credential.id, credential);
|
|
357
393
|
}
|
|
358
|
-
|
|
359
|
-
byId.set(c.id, c); // project wins
|
|
360
|
-
return [...byId.values()];
|
|
394
|
+
return { credentials: [...byId.values()], readable };
|
|
361
395
|
}
|
|
362
396
|
resolve(id, scope = "full") {
|
|
363
397
|
return this.list(scope).find((c) => c.id === id);
|
|
@@ -397,11 +431,12 @@ export class CredentialStore {
|
|
|
397
431
|
listMasked(scope = "full") {
|
|
398
432
|
return this.list(scope).map((c) => {
|
|
399
433
|
const { secret, ...rest } = c;
|
|
434
|
+
const available = isCredentialSecretAvailable(secret);
|
|
400
435
|
return {
|
|
401
436
|
...rest,
|
|
402
437
|
...(credentialAllowsEnvExposure(c.type) ? {} : { exposeAsEnv: undefined }),
|
|
403
|
-
hasSecret:
|
|
404
|
-
secretHint: credentialSecretHint(c.type, secret),
|
|
438
|
+
hasSecret: available,
|
|
439
|
+
secretHint: available ? credentialSecretHint(c.type, secret) : undefined,
|
|
405
440
|
...(c.type === "oauth" || isBrowserOAuthLinkCredential(c)
|
|
406
441
|
? { oauthStatus: summarizeOAuthCredentialSecret(secret) }
|
|
407
442
|
: {}),
|
|
@@ -10,6 +10,8 @@ export type CredentialType = "token" | "link" | "cookie" | "oauth";
|
|
|
10
10
|
* values and must remain behind the credential resolver boundary.
|
|
11
11
|
*/
|
|
12
12
|
export declare function credentialAllowsEnvExposure(type: CredentialType): boolean;
|
|
13
|
+
/** An unreadable encrypted value is preserved on disk, but is not a usable secret. */
|
|
14
|
+
export declare function isCredentialSecretAvailable(secret: string | undefined): secret is string;
|
|
13
15
|
/** Build a renderer-safe hint without deriving bytes from structured JSON. */
|
|
14
16
|
export declare function credentialSecretHint(type: CredentialType, secret: string | undefined): string | undefined;
|
|
15
17
|
export interface OAuthCredentialSecret {
|
|
@@ -112,6 +114,8 @@ export interface Credential {
|
|
|
112
114
|
domain?: string;
|
|
113
115
|
scope?: "domain" | "all";
|
|
114
116
|
switchMode?: "clear" | "merge";
|
|
117
|
+
/** Opt in to writing browser-side cookie rotations back to this saved credential. */
|
|
118
|
+
autoRefreshFromBrowser?: boolean;
|
|
115
119
|
/** Link provider id for credentials owned by a local/server app connection. */
|
|
116
120
|
linkProvider?: string;
|
|
117
121
|
/** Provider-specific connection method, e.g. fine-grained-pat or github-app. */
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
export function credentialAllowsEnvExposure(type) {
|
|
7
7
|
return type === "token" || type === "link";
|
|
8
8
|
}
|
|
9
|
+
/** An unreadable encrypted value is preserved on disk, but is not a usable secret. */
|
|
10
|
+
export function isCredentialSecretAvailable(secret) {
|
|
11
|
+
return typeof secret === "string" && secret.length > 0 && !secret.startsWith("enc:");
|
|
12
|
+
}
|
|
9
13
|
/** Build a renderer-safe hint without deriving bytes from structured JSON. */
|
|
10
14
|
export function credentialSecretHint(type, secret) {
|
|
11
15
|
if (!secret)
|
|
@@ -23,7 +23,10 @@ const BASE_DESCRIPTION = "Use a stored credential (token / API key / login cooki
|
|
|
23
23
|
"or manually exporting credentials. Before `--cookies-from-browser` or retrying an " +
|
|
24
24
|
"authentication failure, check stored credentials here. If `Currently available` names a " +
|
|
25
25
|
"matching id, fetch it directly; otherwise call with NO arguments first to list available " +
|
|
26
|
-
"credentials (id + label + type), then call again with `id`.
|
|
26
|
+
"credentials (id + label + type), then call again with `id`. Provider-owned Link connections " +
|
|
27
|
+
"and existing CLI sessions are not listed here. Call LinkAction with provider only to check " +
|
|
28
|
+
"those before concluding " +
|
|
29
|
+
"a service is disconnected or asking the user to log in. Exposable token credentials return their secret " +
|
|
27
30
|
"value; cookie credentials are materialized to a temporary Netscape cookies.txt file " +
|
|
28
31
|
"(use it as `yt-dlp --cookies <cookiesFile>` / `curl -b <cookiesFile>`). " +
|
|
29
32
|
"Each use is gated by a quick user approval unless auto-approve is on.";
|
|
@@ -125,7 +128,14 @@ export async function useCredentialTool(args, ctx) {
|
|
|
125
128
|
.listMasked(cwd, scope)
|
|
126
129
|
.filter(isAgentExposableCredential)
|
|
127
130
|
.map((c) => ({ id: c.id, label: c.label, type: c.type }));
|
|
128
|
-
return json({
|
|
131
|
+
return json({
|
|
132
|
+
kind: "list",
|
|
133
|
+
credentials,
|
|
134
|
+
notice: "This list excludes provider-owned Link credentials and CLI login sessions. " +
|
|
135
|
+
'Call LinkAction with provider only, for example LinkAction({provider: "github"}), ' +
|
|
136
|
+
"to check saved connections and current CLI authentication; " +
|
|
137
|
+
"an absent credential here does not mean the service is signed out.",
|
|
138
|
+
});
|
|
129
139
|
}
|
|
130
140
|
const cred = access.resolveMeta(cwd, id, scope);
|
|
131
141
|
if (!cred) {
|
|
@@ -104,7 +104,8 @@ export function createAuthorizedSessionMessageService(options) {
|
|
|
104
104
|
const targets = catalog.filter((target) => target.sessionId !== sourceSessionId);
|
|
105
105
|
return {
|
|
106
106
|
targets,
|
|
107
|
-
send: async ({ targetSessionId, message }) => {
|
|
107
|
+
send: async ({ targetSessionId, message, signal }) => {
|
|
108
|
+
signal?.throwIfAborted();
|
|
108
109
|
const target = targets.find((candidate) => candidate.sessionId === targetSessionId);
|
|
109
110
|
if (!target)
|
|
110
111
|
throw new Error("target Session is not in the host-authorized project list");
|
|
@@ -112,8 +113,14 @@ export function createAuthorizedSessionMessageService(options) {
|
|
|
112
113
|
throw new Error("message is required");
|
|
113
114
|
if (message.length > 48_000)
|
|
114
115
|
throw new Error("message exceeds 48000 characters");
|
|
115
|
-
await router({
|
|
116
|
-
|
|
116
|
+
const receipt = await router({
|
|
117
|
+
sourceSessionId,
|
|
118
|
+
target,
|
|
119
|
+
message,
|
|
120
|
+
catalog,
|
|
121
|
+
...(signal ? { signal } : {}),
|
|
122
|
+
});
|
|
123
|
+
return receipt ? { ...target, receipt } : target;
|
|
117
124
|
},
|
|
118
125
|
};
|
|
119
126
|
}
|
package/dist/engine/engine.d.ts
CHANGED
|
@@ -158,6 +158,8 @@ export declare class Engine {
|
|
|
158
158
|
* running goal's turn/budget ceilings mid-run (TODO 3.1). Null when idle.
|
|
159
159
|
*/
|
|
160
160
|
private activeTurnLoop;
|
|
161
|
+
/** Frozen behavior-profile ceiling for the live run's Goal controls. */
|
|
162
|
+
private activeProfileMaxTurns;
|
|
161
163
|
/**
|
|
162
164
|
* The goal-stop hook of the in-flight goal run, exposed so clearGoal() can
|
|
163
165
|
* unregister it mid-run (the closure holds the now-cleared goal and would
|
|
@@ -279,6 +281,7 @@ export declare class Engine {
|
|
|
279
281
|
* clear "no browser panel" error.
|
|
280
282
|
*/
|
|
281
283
|
setBrowserBridge(bridge: import("../tool-system/browser-bridge.js").BrowserBridge | undefined): void;
|
|
284
|
+
setChildHostBindings(factory: EngineConfig["createChildHostBindings"]): void;
|
|
282
285
|
/** Inject the host-backed workspace bridge after construction. */
|
|
283
286
|
setWorkspaceBridge(bridge: import("../tool-system/workspace-bridge.js").WorkspaceBridge | undefined): void;
|
|
284
287
|
/** Inject the host-backed panel discovery/focus bridge after construction. */
|
|
@@ -364,6 +367,7 @@ export declare class Engine {
|
|
|
364
367
|
* permission-boundary bypass.
|
|
365
368
|
*/
|
|
366
369
|
private resolveBehaviorProfile;
|
|
370
|
+
private resolveContextStrategy;
|
|
367
371
|
private runExclusive;
|
|
368
372
|
/** A receipt write failure must not turn an already finalized model result
|
|
369
373
|
* into a second synthetic failure (or reject the Engine.run contract). */
|
|
@@ -459,6 +463,7 @@ export declare class Engine {
|
|
|
459
463
|
* published through the `publishGoalJudgeContext` callback.
|
|
460
464
|
*/
|
|
461
465
|
private buildTurnLoop;
|
|
466
|
+
private resolveRunMaxTurns;
|
|
462
467
|
private buildSummarizeFn;
|
|
463
468
|
private resolveAuxClient;
|
|
464
469
|
private runMemoryPipeline;
|
|
@@ -611,10 +616,9 @@ export declare class Engine {
|
|
|
611
616
|
migrateSessionMainRoot(sessionId: string, project: import("../types.js").SessionProjectBinding, mainRoot: string): SessionWorkspace;
|
|
612
617
|
releaseSessionWorkspace(sessionId: string): SessionWorkspace | null;
|
|
613
618
|
injectContext(sessionId: string, content: string): void;
|
|
614
|
-
/**
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
*/
|
|
619
|
+
/** Attempt the durable note path without issuing a separate model request. */
|
|
620
|
+
private tryNotesRollover;
|
|
621
|
+
/** Force context compaction and return token stats before/after. */
|
|
618
622
|
forceCompact(sessionId?: string): Promise<{
|
|
619
623
|
before: number;
|
|
620
624
|
after: number;
|
package/dist/engine/engine.js
CHANGED
|
@@ -20,6 +20,8 @@ import { loadPluginHooks } from "../plugins/loadPluginHooks.js";
|
|
|
20
20
|
import { pluginAgentDirs } from "../plugins/installer/loadPluginAgents.js";
|
|
21
21
|
import { runShellHook, shellHookMatches } from "../hooks/shell-runner.js";
|
|
22
22
|
import { ContextManager } from "../context/manager.js";
|
|
23
|
+
import { SessionContextNotes } from "../context/notes.js";
|
|
24
|
+
import { loadSection } from "../prompt/section-loader.js";
|
|
23
25
|
import { CONTEXT_PACKAGE_MAX_OUTPUT_TOKENS, buildAnchoredSummaryMessage, buildContextPackagePromptFromSerialized, estimateTokens, groupMessagesByApiRound, serializeContextPackageMessages, clampContextRatios as clampContextRatiosImpl, } from "../context/compaction.js";
|
|
24
26
|
import { PromptComposer } from "../prompt/composer.js";
|
|
25
27
|
import { SessionManager, isEphemeralSessionState, sessionsRoot, } from "../session/session-manager.js";
|
|
@@ -232,6 +234,8 @@ export class Engine {
|
|
|
232
234
|
* running goal's turn/budget ceilings mid-run (TODO 3.1). Null when idle.
|
|
233
235
|
*/
|
|
234
236
|
activeTurnLoop = null;
|
|
237
|
+
/** Frozen behavior-profile ceiling for the live run's Goal controls. */
|
|
238
|
+
activeProfileMaxTurns;
|
|
235
239
|
/**
|
|
236
240
|
* The goal-stop hook of the in-flight goal run, exposed so clearGoal() can
|
|
237
241
|
* unregister it mid-run (the closure holds the now-cleared goal and would
|
|
@@ -697,6 +701,9 @@ export class Engine {
|
|
|
697
701
|
setBrowserBridge(bridge) {
|
|
698
702
|
this.config.browserBridge = bridge;
|
|
699
703
|
}
|
|
704
|
+
setChildHostBindings(factory) {
|
|
705
|
+
this.config.createChildHostBindings = factory;
|
|
706
|
+
}
|
|
700
707
|
/** Inject the host-backed workspace bridge after construction. */
|
|
701
708
|
setWorkspaceBridge(bridge) {
|
|
702
709
|
this.config.workspaceBridge = bridge;
|
|
@@ -992,6 +999,12 @@ export class Engine {
|
|
|
992
999
|
}
|
|
993
1000
|
return sessionProfile ?? explicitProfile;
|
|
994
1001
|
}
|
|
1002
|
+
resolveContextStrategy(profile) {
|
|
1003
|
+
return (this.config.contextStrategy ??
|
|
1004
|
+
this.getSettingsManager().get().context?.strategy ??
|
|
1005
|
+
profile?.contextStrategy ??
|
|
1006
|
+
"summary");
|
|
1007
|
+
}
|
|
995
1008
|
async runExclusive(task, options) {
|
|
996
1009
|
// Freeze permission context once, before the first await. Per-turn protocol
|
|
997
1010
|
// overrides live only for this run; persistent setPermissionMode/setPlanMode
|
|
@@ -1123,6 +1136,9 @@ export class Engine {
|
|
|
1123
1136
|
const { messages: openedMessages, freshImageMessage, resumedFromDisk, claimClientMessageId, releaseClientMessageId, } = openedResult.opened;
|
|
1124
1137
|
const session = openedResult.opened.session;
|
|
1125
1138
|
let messages = openedMessages;
|
|
1139
|
+
toolCtx.contextStrategy = this.resolveContextStrategy(profile);
|
|
1140
|
+
const contextNotes = toolCtx.contextStrategy === "notes" ? new SessionContextNotes(session.transcript) : undefined;
|
|
1141
|
+
toolCtx.contextNotes = contextNotes;
|
|
1126
1142
|
// A host-owned time/topic boundary must be applied only after the current
|
|
1127
1143
|
// user message exists in the transcript (it is the exclusive end anchor),
|
|
1128
1144
|
// yet before this turn assembles prompts or calls the model. Doing this as
|
|
@@ -1143,12 +1159,21 @@ export class Engine {
|
|
|
1143
1159
|
: {}),
|
|
1144
1160
|
};
|
|
1145
1161
|
const before = estimateTokens(messages);
|
|
1162
|
+
let archiveStrategy = "range";
|
|
1146
1163
|
if (options.archiveBeforeCurrentTurn.summary) {
|
|
1147
1164
|
await this.appendArchiveMarker(session.state.sessionId, {
|
|
1148
1165
|
...anchors,
|
|
1149
1166
|
summary: options.archiveBeforeCurrentTurn.summary,
|
|
1150
1167
|
});
|
|
1151
1168
|
}
|
|
1169
|
+
else if (!freshImageMessage &&
|
|
1170
|
+
contextNotes &&
|
|
1171
|
+
this.tryNotesRollover(contextNotes, messages)) {
|
|
1172
|
+
// The note covers its own cursor; any newer messages remain verbatim.
|
|
1173
|
+
this.compactedMessagesBySession.delete(session.state.sessionId);
|
|
1174
|
+
session.state.contextUsageAnchor = undefined;
|
|
1175
|
+
archiveStrategy = "notes";
|
|
1176
|
+
}
|
|
1152
1177
|
else {
|
|
1153
1178
|
await this.archiveTurnRange(session.state.sessionId, { start: 0, end: 0 }, anchors);
|
|
1154
1179
|
}
|
|
@@ -1162,7 +1187,7 @@ export class Engine {
|
|
|
1162
1187
|
if (archived.before > archived.after) {
|
|
1163
1188
|
options.onStream?.({
|
|
1164
1189
|
type: "context_compact",
|
|
1165
|
-
strategy:
|
|
1190
|
+
strategy: archiveStrategy,
|
|
1166
1191
|
before: archived.before,
|
|
1167
1192
|
after: archived.after,
|
|
1168
1193
|
});
|
|
@@ -1217,7 +1242,7 @@ export class Engine {
|
|
|
1217
1242
|
sessionProfileOverrides,
|
|
1218
1243
|
runPlanMode,
|
|
1219
1244
|
});
|
|
1220
|
-
const { llmClient, fullSystemPrompt, dynamicContextMsg, userContextMsg } = await this.assembleRunPrompts({
|
|
1245
|
+
const { llmClient, fullSystemPrompt, dynamicContextMsg, userContextMsg, retainedContextMessages, } = await this.assembleRunPrompts({
|
|
1221
1246
|
session,
|
|
1222
1247
|
messages,
|
|
1223
1248
|
hookMessages,
|
|
@@ -1251,6 +1276,11 @@ export class Engine {
|
|
|
1251
1276
|
task,
|
|
1252
1277
|
cwd,
|
|
1253
1278
|
options,
|
|
1279
|
+
profileMaxTurns: typeof profile?.maxTurns === "number" &&
|
|
1280
|
+
Number.isSafeInteger(profile.maxTurns) &&
|
|
1281
|
+
profile.maxTurns > 0
|
|
1282
|
+
? profile.maxTurns
|
|
1283
|
+
: undefined,
|
|
1254
1284
|
toolCtx,
|
|
1255
1285
|
toolExecutor,
|
|
1256
1286
|
contextManager,
|
|
@@ -1262,6 +1292,7 @@ export class Engine {
|
|
|
1262
1292
|
releaseClientMessageId,
|
|
1263
1293
|
freshImageMessage,
|
|
1264
1294
|
dynamicContextMsg,
|
|
1295
|
+
retainedContextMessages,
|
|
1265
1296
|
});
|
|
1266
1297
|
let result;
|
|
1267
1298
|
let firstGoalTermination;
|
|
@@ -1285,8 +1316,10 @@ export class Engine {
|
|
|
1285
1316
|
this.activeRuntimeGoal = null;
|
|
1286
1317
|
this.activePersistedRunGoal = null;
|
|
1287
1318
|
}
|
|
1288
|
-
if (this.activeTurnLoop === turnLoop)
|
|
1319
|
+
if (this.activeTurnLoop === turnLoop) {
|
|
1289
1320
|
this.activeTurnLoop = null;
|
|
1321
|
+
this.activeProfileMaxTurns = undefined;
|
|
1322
|
+
}
|
|
1290
1323
|
if (this.activeRunSession === session)
|
|
1291
1324
|
this.activeRunSession = null;
|
|
1292
1325
|
// Run-scoped too: this handler is re-registered every run(), so it must be
|
|
@@ -1515,17 +1548,21 @@ export class Engine {
|
|
|
1515
1548
|
promptComposer.buildDynamicContextMessage(),
|
|
1516
1549
|
]);
|
|
1517
1550
|
const fullSystemPrompt = composeRunSystemPrompt({
|
|
1518
|
-
baseSystemPrompt
|
|
1551
|
+
baseSystemPrompt: toolDefs.some((tool) => tool.name === "SaveContextNote")
|
|
1552
|
+
? `${baseSystemPrompt}\n\n${loadSection("context-notes")}`
|
|
1553
|
+
: baseSystemPrompt,
|
|
1519
1554
|
profile,
|
|
1520
1555
|
profileParams,
|
|
1521
1556
|
});
|
|
1522
1557
|
const userContextMsg = promptComposer.buildUserContextMessage();
|
|
1558
|
+
const priorMessages = new Set(messages);
|
|
1523
1559
|
assembleRunMessages({
|
|
1524
1560
|
messages,
|
|
1525
1561
|
userContextMsg,
|
|
1526
1562
|
hookMessages,
|
|
1527
1563
|
dynamicContextMsg,
|
|
1528
1564
|
});
|
|
1565
|
+
const retainedContextMessages = messages.filter((message) => !priorMessages.has(message) && message !== dynamicContextMsg);
|
|
1529
1566
|
this.lastSessionId = session.state.sessionId;
|
|
1530
1567
|
this.lastMessages = messages;
|
|
1531
1568
|
// Wire up LLM summarization for context compaction
|
|
@@ -1541,7 +1578,13 @@ export class Engine {
|
|
|
1541
1578
|
contextManager.initReplacementStateFromMessages(messages);
|
|
1542
1579
|
// Two summarizers with DIFFERENT quality needs (see the run loop wiring for
|
|
1543
1580
|
// the aux-model summarizer set up alongside the ModelFacade).
|
|
1544
|
-
return {
|
|
1581
|
+
return {
|
|
1582
|
+
llmClient,
|
|
1583
|
+
fullSystemPrompt,
|
|
1584
|
+
dynamicContextMsg,
|
|
1585
|
+
userContextMsg,
|
|
1586
|
+
retainedContextMessages,
|
|
1587
|
+
};
|
|
1545
1588
|
}
|
|
1546
1589
|
/**
|
|
1547
1590
|
* Seed the run's {@link ContextManager}, emit the early `session_started`
|
|
@@ -1642,6 +1685,7 @@ export class Engine {
|
|
|
1642
1685
|
// built-ins and product capabilities.
|
|
1643
1686
|
const subAgentSpawner = createSubAgentSpawner({
|
|
1644
1687
|
parentConfig: this.config,
|
|
1688
|
+
getParentSessionId: () => getSession().state.sessionId,
|
|
1645
1689
|
parentSandbox: sandboxConfig,
|
|
1646
1690
|
presetName: this.preset.name,
|
|
1647
1691
|
cwd,
|
|
@@ -1658,7 +1702,12 @@ export class Engine {
|
|
|
1658
1702
|
createChild: (config) => new Engine(config),
|
|
1659
1703
|
runChild: async (config, childTask, childOptions) => {
|
|
1660
1704
|
const child = new Engine(config);
|
|
1661
|
-
|
|
1705
|
+
try {
|
|
1706
|
+
return await child.run(childTask, childOptions);
|
|
1707
|
+
}
|
|
1708
|
+
finally {
|
|
1709
|
+
await child.dispose();
|
|
1710
|
+
}
|
|
1662
1711
|
},
|
|
1663
1712
|
},
|
|
1664
1713
|
});
|
|
@@ -1714,7 +1763,7 @@ export class Engine {
|
|
|
1714
1763
|
* phases consume cross back out.
|
|
1715
1764
|
*/
|
|
1716
1765
|
async wireRunLoop(args) {
|
|
1717
|
-
const { session, sid, task, cwd, options, toolCtx, toolExecutor, contextManager, llmClient, auxSummaryClient, fullSystemPrompt, toolDefs, claimClientMessageId, releaseClientMessageId, freshImageMessage, dynamicContextMsg, } = args;
|
|
1766
|
+
const { session, sid, task, cwd, options, profileMaxTurns, toolCtx, toolExecutor, contextManager, llmClient, auxSummaryClient, fullSystemPrompt, toolDefs, claimClientMessageId, releaseClientMessageId, freshImageMessage, dynamicContextMsg, retainedContextMessages, } = args;
|
|
1718
1767
|
// eslint-disable-next-line prefer-const
|
|
1719
1768
|
let turnLoop;
|
|
1720
1769
|
const accounting = createRunUsageAccounting({
|
|
@@ -1827,6 +1876,7 @@ export class Engine {
|
|
|
1827
1876
|
toolDefs,
|
|
1828
1877
|
sid,
|
|
1829
1878
|
options,
|
|
1879
|
+
profileMaxTurns,
|
|
1830
1880
|
cwd,
|
|
1831
1881
|
claimClientMessageId,
|
|
1832
1882
|
releaseClientMessageId,
|
|
@@ -1836,6 +1886,7 @@ export class Engine {
|
|
|
1836
1886
|
normalizedGoal,
|
|
1837
1887
|
freshImageMessage,
|
|
1838
1888
|
dynamicContextMsg,
|
|
1889
|
+
retainedContextMessages,
|
|
1839
1890
|
usageBaseline,
|
|
1840
1891
|
getRunUsage,
|
|
1841
1892
|
recordCumulativeUsage,
|
|
@@ -1846,8 +1897,10 @@ export class Engine {
|
|
|
1846
1897
|
toolCtx.recordBilledUsage = recordExternalBilledUsage;
|
|
1847
1898
|
// Expose this run's loop for mid-run extension (TODO 3.1). Top-level only —
|
|
1848
1899
|
// a sub-agent's loop is its own concern and isn't user-extendable.
|
|
1849
|
-
if (this.config.isSubAgent !== true)
|
|
1900
|
+
if (this.config.isSubAgent !== true) {
|
|
1850
1901
|
this.activeTurnLoop = turnLoop;
|
|
1902
|
+
this.activeProfileMaxTurns = profileMaxTurns;
|
|
1903
|
+
}
|
|
1851
1904
|
// Expose this run's session bundle so a mid-run clearGoal() wipes the goal
|
|
1852
1905
|
// on the very instance this loop keeps saving (see field doc). Top-level
|
|
1853
1906
|
// only — sub-agents don't carry user-clearable persistent goals.
|
|
@@ -2019,6 +2072,7 @@ export class Engine {
|
|
|
2019
2072
|
const mcpServers = this.config.mcpServers ?? {};
|
|
2020
2073
|
const mcpDisabled = profile?.disableMcp === true;
|
|
2021
2074
|
await connectRunMcp({
|
|
2075
|
+
toolContext: toolCtx,
|
|
2022
2076
|
mcpServers,
|
|
2023
2077
|
mcpDisabled,
|
|
2024
2078
|
getManager: () => this.mcpManager,
|
|
@@ -2082,13 +2136,17 @@ export class Engine {
|
|
|
2082
2136
|
* published through the `publishGoalJudgeContext` callback.
|
|
2083
2137
|
*/
|
|
2084
2138
|
buildTurnLoop(args) {
|
|
2085
|
-
const { modelFacade, toolExecutor, contextManager, session, fullSystemPrompt, toolDefs, sid, options, cwd, claimClientMessageId, releaseClientMessageId, toolCtx, persistedRunGoal, goalHookHandler, normalizedGoal, freshImageMessage, dynamicContextMsg, usageBaseline, getRunUsage, recordCumulativeUsage, publishGoalJudgeContext, } = args;
|
|
2139
|
+
const { modelFacade, toolExecutor, contextManager, session, fullSystemPrompt, toolDefs, sid, options, profileMaxTurns, cwd, claimClientMessageId, releaseClientMessageId, toolCtx, persistedRunGoal, goalHookHandler, normalizedGoal, freshImageMessage, dynamicContextMsg, retainedContextMessages, usageBaseline, getRunUsage, recordCumulativeUsage, publishGoalJudgeContext, } = args;
|
|
2086
2140
|
// Surface compaction events to the UI so the user knows when context was trimmed.
|
|
2087
2141
|
// Buffer the most recent event so TurnLoop can drain it and emit the
|
|
2088
2142
|
// post_compact hook on the next turn (ContextManager itself doesn't
|
|
2089
2143
|
// know about HookRegistry — the buffer is the seam).
|
|
2090
2144
|
let pendingCompactInfo = null;
|
|
2091
2145
|
contextManager.setOnCompact((info) => {
|
|
2146
|
+
if (info.strategy === "notes") {
|
|
2147
|
+
session.state.contextUsageAnchor = undefined;
|
|
2148
|
+
this.compactedMessagesBySession.delete(sid);
|
|
2149
|
+
}
|
|
2092
2150
|
pendingCompactInfo = info;
|
|
2093
2151
|
options?.onStream?.({ type: "context_compact", ...info });
|
|
2094
2152
|
});
|
|
@@ -2097,6 +2155,7 @@ export class Engine {
|
|
|
2097
2155
|
model: modelFacade,
|
|
2098
2156
|
toolExecutor,
|
|
2099
2157
|
contextManager,
|
|
2158
|
+
contextNotes: toolCtx.contextNotes instanceof SessionContextNotes ? toolCtx.contextNotes : undefined,
|
|
2100
2159
|
hooks: this.hooks,
|
|
2101
2160
|
transcript: session.transcript,
|
|
2102
2161
|
systemPrompt: fullSystemPrompt,
|
|
@@ -2146,6 +2205,9 @@ export class Engine {
|
|
|
2146
2205
|
releaseClientMessageId,
|
|
2147
2206
|
setOriginClientMessageId: (clientMessageId) => {
|
|
2148
2207
|
toolCtx.originClientMessageId = clientMessageId;
|
|
2208
|
+
const profileId = toolCtx.toolVisibility?.behaviorProfile;
|
|
2209
|
+
const profile = profileId ? this.behaviorProfiles.get(profileId) : undefined;
|
|
2210
|
+
profile?.onUserInputChanged?.(toolCtx.runScopedServices ?? {});
|
|
2149
2211
|
},
|
|
2150
2212
|
recordCumulativeUsage,
|
|
2151
2213
|
onAgentUsage: (usage) => options?.onAgentProgress?.({ type: "usage", usage }),
|
|
@@ -2199,7 +2261,7 @@ export class Engine {
|
|
|
2199
2261
|
// getting re-blocked by the stop-hook until it's done, and the 100
|
|
2200
2262
|
// interactive default would silently truncate a long objective. The
|
|
2201
2263
|
// real backstops are the goal token/time budgets + maxStopBlocks.
|
|
2202
|
-
maxTurns:
|
|
2264
|
+
maxTurns: this.resolveRunMaxTurns(normalizedGoal, profileMaxTurns),
|
|
2203
2265
|
// Consecutive stop-block cap: config override > goal.maxStopBlocks >
|
|
2204
2266
|
// GOAL_DEFAULT_MAX_STOP_BLOCKS(25). The old hardcoded 8 was too tight
|
|
2205
2267
|
// for complex goals that legitimately get re-blocked while advancing.
|
|
@@ -2213,6 +2275,7 @@ export class Engine {
|
|
|
2213
2275
|
signal: options?.signal,
|
|
2214
2276
|
freshImageMessages: freshImageMessage ? [freshImageMessage] : undefined,
|
|
2215
2277
|
volatileContextMessages: dynamicContextMsg ? [dynamicContextMsg] : undefined,
|
|
2278
|
+
retainedContextMessages,
|
|
2216
2279
|
// Goal mode: the active goal is surfaced to the on_stop handler via
|
|
2217
2280
|
// ctx.data.goal; the GoalStopHook (registered above) judges it.
|
|
2218
2281
|
goal: normalizedGoal,
|
|
@@ -2253,6 +2316,10 @@ export class Engine {
|
|
|
2253
2316
|
});
|
|
2254
2317
|
return turnLoop;
|
|
2255
2318
|
}
|
|
2319
|
+
resolveRunMaxTurns(goal, profileMaxTurns) {
|
|
2320
|
+
const resolved = resolveMaxTurns(this.config.maxTurns, goal);
|
|
2321
|
+
return profileMaxTurns === undefined ? resolved : Math.min(resolved, profileMaxTurns);
|
|
2322
|
+
}
|
|
2256
2323
|
buildSummarizeFn(auxSummaryClient, recordCumulativeUsage) {
|
|
2257
2324
|
return this.auxiliaryPipeline.buildSummarizeFn(auxSummaryClient, recordCumulativeUsage);
|
|
2258
2325
|
}
|
|
@@ -2717,7 +2784,7 @@ export class Engine {
|
|
|
2717
2784
|
: resumed
|
|
2718
2785
|
? `目标已恢复:${next.objective}`
|
|
2719
2786
|
: undefined, {
|
|
2720
|
-
maxTurns:
|
|
2787
|
+
maxTurns: this.resolveRunMaxTurns(next, this.activeProfileMaxTurns),
|
|
2721
2788
|
maxStopBlocks: resolveMaxStopBlocks(this.config.maxStopBlocks, next),
|
|
2722
2789
|
});
|
|
2723
2790
|
if (this.activeGoalHook && !this.activeGoalHookAttached) {
|
|
@@ -2830,10 +2897,17 @@ export class Engine {
|
|
|
2830
2897
|
cached.push({ role: "assistant", content });
|
|
2831
2898
|
}
|
|
2832
2899
|
}
|
|
2833
|
-
/**
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2900
|
+
/** Attempt the durable note path without issuing a separate model request. */
|
|
2901
|
+
tryNotesRollover(notes, messages) {
|
|
2902
|
+
try {
|
|
2903
|
+
notes.requestRollover();
|
|
2904
|
+
return notes.applyRollover(messages);
|
|
2905
|
+
}
|
|
2906
|
+
catch {
|
|
2907
|
+
return undefined;
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
/** Force context compaction and return token stats before/after. */
|
|
2837
2911
|
async forceCompact(sessionId) {
|
|
2838
2912
|
const effectiveSessionId = sessionId ?? this.lastSessionId;
|
|
2839
2913
|
if (!effectiveSessionId) {
|
|
@@ -2842,6 +2916,19 @@ export class Engine {
|
|
|
2842
2916
|
const session = this.sessionManager.resume(effectiveSessionId);
|
|
2843
2917
|
const sourceMessages = this.compactedMessagesBySession.get(effectiveSessionId) ?? session.transcript.toMessages();
|
|
2844
2918
|
const before = estimateTokens(sourceMessages);
|
|
2919
|
+
const profile = [...this.behaviorProfiles.values()].find((candidate) => candidate.activateForSessionKinds?.includes(session.state.kind ?? "work"));
|
|
2920
|
+
if (this.resolveContextStrategy(profile) === "notes") {
|
|
2921
|
+
const compacted = this.tryNotesRollover(new SessionContextNotes(session.transcript), sourceMessages);
|
|
2922
|
+
if (compacted) {
|
|
2923
|
+
this.compactedMessagesBySession.set(effectiveSessionId, compacted);
|
|
2924
|
+
session.state.contextUsageAnchor = undefined;
|
|
2925
|
+
this.lastContextManager = undefined;
|
|
2926
|
+
this.persistRunProgress(session.state);
|
|
2927
|
+
this.lastSessionId = effectiveSessionId;
|
|
2928
|
+
this.lastMessages = compacted;
|
|
2929
|
+
return { before, after: estimateTokens(compacted), strategy: "notes" };
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2845
2932
|
const contextManager = await this.prepareContextManagerForSession(effectiveSessionId, session, sourceMessages, "force_compact");
|
|
2846
2933
|
// Manual /compact emits its UI boundary at the protocol layer from the
|
|
2847
2934
|
// final before/after result. Capture the tier here, but avoid reusing a
|
|
@@ -3134,6 +3221,19 @@ export class Engine {
|
|
|
3134
3221
|
extendGoalRun(opts) {
|
|
3135
3222
|
if (!this.activeTurnLoop)
|
|
3136
3223
|
return null;
|
|
3224
|
+
if (this.activeProfileMaxTurns !== undefined &&
|
|
3225
|
+
typeof opts.addTurns === "number" &&
|
|
3226
|
+
Number.isFinite(opts.addTurns) &&
|
|
3227
|
+
opts.addTurns > 0 &&
|
|
3228
|
+
opts.addTurns <= Number.MAX_SAFE_INTEGER) {
|
|
3229
|
+
// An empty extension is a no-op that returns the live ceilings. Goal
|
|
3230
|
+
// budgets may still grow, but a behavior-profile ceiling cannot.
|
|
3231
|
+
const currentMaxTurns = this.activeTurnLoop.extend({}).maxTurns;
|
|
3232
|
+
return this.activeTurnLoop.extend({
|
|
3233
|
+
...opts,
|
|
3234
|
+
addTurns: Math.min(opts.addTurns, this.activeProfileMaxTurns - currentMaxTurns),
|
|
3235
|
+
});
|
|
3236
|
+
}
|
|
3137
3237
|
return this.activeTurnLoop.extend(opts);
|
|
3138
3238
|
}
|
|
3139
3239
|
/**
|
|
@@ -4,6 +4,12 @@ const DEFAULT_MAX_SESSIONS = 256;
|
|
|
4
4
|
const DROP_MIN_PREVIOUS_TOKENS = 100;
|
|
5
5
|
const DROP_MAX_CURRENT_TOKENS = 64;
|
|
6
6
|
const DROP_RATIO = 0.1;
|
|
7
|
+
// Partial cache invalidation can leave thousands of tokens cached. Require
|
|
8
|
+
// both a halving and a meaningful absolute loss to avoid reporting small
|
|
9
|
+
// fluctuations. Compare cache reads, not hit rates: appending uncached input
|
|
10
|
+
// can lower the hit rate while the reusable prefix remains fully cached.
|
|
11
|
+
const PARTIAL_DROP_MAX_RETAINED_RATIO = 0.5;
|
|
12
|
+
const PARTIAL_DROP_MIN_LOST_TOKENS = 4096;
|
|
7
13
|
/**
|
|
8
14
|
* Session stickiness audit. Semantic, capability, and security switches must
|
|
9
15
|
* take effect on the next request even when they invalidate a cache prefix.
|
|
@@ -150,7 +156,10 @@ export class PromptCacheDiagnosticRecorder {
|
|
|
150
156
|
if (previous.cacheReadTokens < DROP_MIN_PREVIOUS_TOKENS)
|
|
151
157
|
return { kind: "updated" };
|
|
152
158
|
const dropRatio = previous.cacheReadTokens > 0 ? cacheReadTokens / previous.cacheReadTokens : 1;
|
|
153
|
-
|
|
159
|
+
const nearColdDrop = cacheReadTokens <= DROP_MAX_CURRENT_TOKENS && dropRatio <= DROP_RATIO;
|
|
160
|
+
const partialDrop = dropRatio <= PARTIAL_DROP_MAX_RETAINED_RATIO &&
|
|
161
|
+
previous.cacheReadTokens - cacheReadTokens >= PARTIAL_DROP_MIN_LOST_TOKENS;
|
|
162
|
+
if (!nearColdDrop && !partialDrop) {
|
|
154
163
|
return { kind: "updated" };
|
|
155
164
|
}
|
|
156
165
|
return {
|
package/dist/engine/run-goal.js
CHANGED
|
@@ -10,9 +10,13 @@ import { logger } from "../logging/logger.js";
|
|
|
10
10
|
/** engine.ts:explicit/stored/fallback 归一化 + 持久化 + goal_set 事件。 */
|
|
11
11
|
export function resolveRunGoal(args) {
|
|
12
12
|
const { options, session, sessionManager, configGoal, isSubAgent, sid, onStream } = args;
|
|
13
|
-
const
|
|
13
|
+
const goalDisabled = options?.disableGoal === true;
|
|
14
|
+
const explicitGoal = goalDisabled ? undefined : normalizeGoal(options?.goal);
|
|
14
15
|
const storedLifecycle = session.state.goalLifecycle;
|
|
15
|
-
const storedGoal =
|
|
16
|
+
const storedGoal = !goalDisabled &&
|
|
17
|
+
isSubAgent !== true &&
|
|
18
|
+
storedLifecycle &&
|
|
19
|
+
isGoalLifecycleCurrent(storedLifecycle)
|
|
16
20
|
? goalConfigFromLifecycle(storedLifecycle)
|
|
17
21
|
: undefined;
|
|
18
22
|
if (storedGoal &&
|
|
@@ -48,7 +52,7 @@ export function resolveRunGoal(args) {
|
|
|
48
52
|
replaced,
|
|
49
53
|
});
|
|
50
54
|
}
|
|
51
|
-
const fallbackGoal = normalizeGoal(configGoal);
|
|
55
|
+
const fallbackGoal = goalDisabled ? undefined : normalizeGoal(configGoal);
|
|
52
56
|
if (fallbackGoal && !fallbackGoal.goalId)
|
|
53
57
|
fallbackGoal.goalId = randomUUID();
|
|
54
58
|
if (fallbackGoal && !fallbackGoal.revision)
|
|
@@ -64,6 +64,7 @@ export declare function connectRunMcp(args: {
|
|
|
64
64
|
runtimePool: MCPManager | undefined;
|
|
65
65
|
toolRegistry: ToolRegistry;
|
|
66
66
|
engineForConnect: Parameters<MCPManager["connectAll"]>[1];
|
|
67
|
+
toolContext?: Parameters<MCPManager["connectAll"]>[3];
|
|
67
68
|
emitNotificationHook: (payload: Record<string, unknown>) => void;
|
|
68
69
|
}): Promise<void>;
|
|
69
70
|
/**
|
|
@@ -74,12 +75,14 @@ export declare function connectRunMcp(args: {
|
|
|
74
75
|
*/
|
|
75
76
|
export interface ToolVisibilityInputs {
|
|
76
77
|
cwd: string;
|
|
78
|
+
contextStrategy?: "summary" | "notes";
|
|
77
79
|
workspace?: ToolContext["workspace"];
|
|
78
80
|
hasGoal: boolean;
|
|
79
81
|
sessionId?: string;
|
|
80
82
|
settingsScope?: SettingsScope;
|
|
81
83
|
/** Host identity a guard may require, e.g. Panel needs `"desktop"`. */
|
|
82
84
|
host?: string;
|
|
85
|
+
hasBrowserAutomation?: boolean;
|
|
83
86
|
isSubAgent?: boolean;
|
|
84
87
|
behaviorProfile?: string;
|
|
85
88
|
sessionMessageTargets?: readonly import("../session/session-message.js").SessionMessageTarget[];
|