@cjhyy/code-shell-core 0.8.8 → 0.8.10
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.js +49 -0
- package/dist/automation/store.d.ts +1 -1
- package/dist/automation/store.js +184 -10
- package/dist/cli/agent-server-stdio.js +7 -0
- package/dist/credentials/store.d.ts +14 -0
- package/dist/credentials/store.js +245 -42
- package/dist/engine/engine.js +81 -27
- package/dist/engine/file-history-hook.js +24 -5
- package/dist/engine/run-finalize.js +8 -6
- package/dist/engine/run-session-open.js +1 -1
- package/dist/engine/run-setup.d.ts +4 -0
- package/dist/engine/run-setup.js +5 -1
- package/dist/engine/run-tooling.js +7 -1
- package/dist/engine/run-types.d.ts +33 -0
- package/dist/engine/run-types.js +21 -0
- package/dist/engine/run-workspace.js +11 -5
- package/dist/engine/turn-loop.js +9 -8
- package/dist/goal/lifecycle.d.ts +2 -0
- package/dist/goal/lifecycle.js +56 -33
- package/dist/index.d.ts +2 -3
- package/dist/index.internal.d.ts +1 -0
- package/dist/index.internal.js +1 -0
- package/dist/index.js +2 -2
- package/dist/links/cli.d.ts +2 -0
- package/dist/links/cli.js +11 -4
- package/dist/model-catalog/index.js +19 -4
- package/dist/model-catalog/save-entry.js +122 -61
- package/dist/model-catalog/types.js +27 -23
- package/dist/panel-apps/installer.js +27 -14
- package/dist/panel-apps/manifest.d.ts +11 -11
- package/dist/panel-apps/manifest.js +3 -1
- package/dist/panel-apps/registry.js +60 -12
- package/dist/plugins/installedPlugins.d.ts +4 -0
- package/dist/plugins/installedPlugins.js +70 -30
- package/dist/plugins/installer/types.d.ts +12 -12
- package/dist/plugins/installer/update.js +37 -38
- package/dist/plugins/knownMarketplaces.d.ts +7 -3
- package/dist/plugins/knownMarketplaces.js +127 -23
- package/dist/plugins/pluginCatalog.js +18 -4
- package/dist/plugins/pluginHookApproval.js +56 -60
- package/dist/plugins/pluginMcpApproval.js +50 -52
- package/dist/profile/catalog-store.js +39 -4
- package/dist/profile/catalog.js +55 -15
- package/dist/profile/store.js +51 -21
- package/dist/prompt/composer.d.ts +8 -0
- package/dist/prompt/composer.js +8 -0
- package/dist/protocol/chat-session-manager.d.ts +9 -0
- package/dist/protocol/chat-session-manager.js +22 -0
- package/dist/protocol/chat-session.d.ts +5 -0
- package/dist/protocol/chat-session.js +1 -0
- package/dist/protocol/server.d.ts +2 -0
- package/dist/protocol/server.js +112 -29
- package/dist/protocol/types.d.ts +20 -0
- package/dist/run/FileRunStore.d.ts +2 -0
- package/dist/run/FileRunStore.js +153 -18
- package/dist/run/Heartbeat.js +63 -4
- package/dist/services/auto-dream.js +39 -17
- package/dist/services/session-memory.js +107 -8
- package/dist/session/file-history.d.ts +63 -2
- package/dist/session/file-history.js +593 -86
- package/dist/session/session-manager.d.ts +4 -1
- package/dist/session/session-manager.js +94 -47
- package/dist/session/transcript.js +33 -3
- package/dist/session/undo-target.d.ts +15 -6
- package/dist/session/undo-target.js +26 -9
- package/dist/settings/manager.d.ts +22 -3
- package/dist/settings/manager.js +185 -50
- package/dist/settings/schema.d.ts +3 -3
- package/dist/sources/adapters/local-files.js +49 -4
- package/dist/sources/catalog.js +64 -18
- package/dist/sources/types.d.ts +3 -3
- package/dist/sources/types.js +7 -4
- package/dist/themes/installer.js +192 -28
- package/dist/tool-system/builtin/add-marketplace.js +21 -1
- package/dist/tool-system/builtin/cron.d.ts +2 -1
- package/dist/tool-system/builtin/cron.js +20 -6
- package/dist/tool-system/builtin/index.js +48 -1
- package/dist/tool-system/builtin/install-capability.d.ts +52 -0
- package/dist/tool-system/builtin/install-capability.js +1057 -0
- package/dist/tool-system/builtin/skill.js +3 -1
- package/dist/tool-system/executor.js +1 -0
- package/dist/tool-system/path-policy.js +25 -33
- package/dist/tool-system/registry.js +5 -0
- package/dist/utils/file-mutex.d.ts +2 -0
- package/dist/utils/file-mutex.js +29 -4
- package/package.json +2 -1
|
@@ -32,6 +32,20 @@ export interface RunBehaviorProfile {
|
|
|
32
32
|
disablePlanMode?: boolean;
|
|
33
33
|
/** When true, MCP servers are neither connected nor exposed for the run. */
|
|
34
34
|
disableMcp?: boolean;
|
|
35
|
+
/** Skip repository/user instruction discovery for this run. */
|
|
36
|
+
disableInstructions?: boolean;
|
|
37
|
+
/** Skip persistent-memory injection for this run. */
|
|
38
|
+
disableMemoryContext?: boolean;
|
|
39
|
+
/** Skip capability-owned volatile context providers for this run. */
|
|
40
|
+
disableCapabilityContext?: boolean;
|
|
41
|
+
/** Skip bound-source metadata injection for this run. */
|
|
42
|
+
disableSourcesContext?: boolean;
|
|
43
|
+
/** Ignore the project's default digital-human profile for this run. */
|
|
44
|
+
disableWorkspaceProfile?: boolean;
|
|
45
|
+
/** Skip SessionStart/UserPromptSubmit/SessionEnd hooks for this run. */
|
|
46
|
+
disableHooks?: boolean;
|
|
47
|
+
/** Skip auxiliary title generation for this run. */
|
|
48
|
+
disableSessionTitle?: boolean;
|
|
35
49
|
/**
|
|
36
50
|
* When true, the injected persistent-memory index is trimmed to this run's
|
|
37
51
|
* project: global-layer project-type / dream-scope records tied to other
|
|
@@ -67,6 +81,10 @@ export interface RunBehaviorProfile {
|
|
|
67
81
|
export declare const QUICK_CHAT_RESTRICTED_SYSTEM_PROMPT = "# Side Conversation Boundary\n\nThis is a side conversation, not the main-thread task execution environment.\n- Treat all content before this boundary as reference history only. Do not proactively continue any earlier plan, task, or modification.\n- Default to answering the user's question directly. Use lightweight read-only exploration only when needed.\n- Do not modify files, git state, configuration, or permissions unless the user explicitly asks after this boundary (for example, \"Allow you to modify files, please help me...\" or \"Please directly edit...\"). When explicitly requested, use the normally available tools subject to the current permission and approval mode.\n- Sub-agents are disabled for this side conversation. Do not create or invoke sub-agents.";
|
|
68
82
|
/** The side-conversation restriction expressed as a generic behavior profile. */
|
|
69
83
|
export declare const QUICK_CHAT_RESTRICTED_PROFILE: RunBehaviorProfile;
|
|
84
|
+
export declare const ISOLATED_TASK_BEHAVIOR_MODE: "isolatedTask";
|
|
85
|
+
export declare const ISOLATED_TASK_SYSTEM_PROMPT = "# Isolated Task Boundary\n\nThis is a bounded Task, not a continuation of any user conversation.\n- Use only the explicit Task input, the visible tool surface, and the selected Skill when one is available.\n- Do not infer or search for prior Session history, persistent memory, unrelated Skills, or other projects.\n- Do not create sub-agents or start unrelated work.\n- Finish the requested outcome, then return a concise result suitable for the owning application.";
|
|
86
|
+
/** Minimal, process-local run profile used by host-owned Tasks. */
|
|
87
|
+
export declare const ISOLATED_TASK_PROFILE: RunBehaviorProfile;
|
|
70
88
|
export interface EngineRunOptions {
|
|
71
89
|
cwd?: string;
|
|
72
90
|
onStream?: StreamCallback;
|
|
@@ -80,9 +98,24 @@ export interface EngineRunOptions {
|
|
|
80
98
|
goal?: string | GoalConfig;
|
|
81
99
|
injected?: boolean;
|
|
82
100
|
clientMessageId?: string;
|
|
101
|
+
/**
|
|
102
|
+
* Host-requested history boundary to archive after this run's user message
|
|
103
|
+
* has been appended, but before the first model call. The current
|
|
104
|
+
* `clientMessageId` is the exclusive end anchor.
|
|
105
|
+
*/
|
|
106
|
+
archiveBeforeCurrentTurn?: {
|
|
107
|
+
fromClientMessageId?: string;
|
|
108
|
+
segmentId?: string;
|
|
109
|
+
};
|
|
83
110
|
attachments?: InputAttachmentMeta[];
|
|
84
111
|
/** Named per-run behavior profile supplied by interactive product surfaces. */
|
|
85
112
|
behaviorMode?: RunBehaviorMode;
|
|
113
|
+
/** Per-run hard tool allowlist. An empty list exposes no tools. */
|
|
114
|
+
toolAllowlist?: readonly string[];
|
|
115
|
+
/** Per-run hard Skill allowlist. An empty list exposes no Skills. */
|
|
116
|
+
skillAllowlist?: readonly string[];
|
|
117
|
+
/** Keep a fresh Session in process memory only and omit it from Session pickers. */
|
|
118
|
+
ephemeral?: boolean;
|
|
86
119
|
/**
|
|
87
120
|
* Generic per-run parameters consumed by the active behavior profile
|
|
88
121
|
* (createRunServices / buildVisibilityMeta / runtime-context injection).
|
package/dist/engine/run-types.js
CHANGED
|
@@ -10,3 +10,24 @@ export const QUICK_CHAT_RESTRICTED_PROFILE = {
|
|
|
10
10
|
id: "quickChatRestricted",
|
|
11
11
|
systemPromptAppend: QUICK_CHAT_RESTRICTED_SYSTEM_PROMPT,
|
|
12
12
|
};
|
|
13
|
+
export const ISOLATED_TASK_BEHAVIOR_MODE = "isolatedTask";
|
|
14
|
+
export const ISOLATED_TASK_SYSTEM_PROMPT = `# Isolated Task Boundary
|
|
15
|
+
|
|
16
|
+
This is a bounded Task, not a continuation of any user conversation.
|
|
17
|
+
- Use only the explicit Task input, the visible tool surface, and the selected Skill when one is available.
|
|
18
|
+
- Do not infer or search for prior Session history, persistent memory, unrelated Skills, or other projects.
|
|
19
|
+
- Do not create sub-agents or start unrelated work.
|
|
20
|
+
- Finish the requested outcome, then return a concise result suitable for the owning application.`;
|
|
21
|
+
/** Minimal, process-local run profile used by host-owned Tasks. */
|
|
22
|
+
export const ISOLATED_TASK_PROFILE = {
|
|
23
|
+
id: ISOLATED_TASK_BEHAVIOR_MODE,
|
|
24
|
+
systemPromptAppend: ISOLATED_TASK_SYSTEM_PROMPT,
|
|
25
|
+
disableMcp: true,
|
|
26
|
+
disableInstructions: true,
|
|
27
|
+
disableMemoryContext: true,
|
|
28
|
+
disableCapabilityContext: true,
|
|
29
|
+
disableSourcesContext: true,
|
|
30
|
+
disableWorkspaceProfile: true,
|
|
31
|
+
disableHooks: true,
|
|
32
|
+
disableSessionTitle: true,
|
|
33
|
+
};
|
|
@@ -93,11 +93,17 @@ export async function resolveRunWorkspace(args) {
|
|
|
93
93
|
configCwd: args.configCwd,
|
|
94
94
|
processCwd: args.processCwd,
|
|
95
95
|
});
|
|
96
|
-
const profileState =
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
const profileState = profile?.disableWorkspaceProfile
|
|
97
|
+
? {
|
|
98
|
+
workspaceProfile: undefined,
|
|
99
|
+
sessionProfileOverrides: undefined,
|
|
100
|
+
profileMemoryDir: undefined,
|
|
101
|
+
}
|
|
102
|
+
: resolveRunProfileState({
|
|
103
|
+
sessionWorkspaceProfile,
|
|
104
|
+
cwd,
|
|
105
|
+
settings: args.settings,
|
|
106
|
+
});
|
|
101
107
|
return {
|
|
102
108
|
ok: true,
|
|
103
109
|
resolution: {
|
package/dist/engine/turn-loop.js
CHANGED
|
@@ -22,7 +22,7 @@ import { COMPLETE_GOAL_TOOL_NAME } from "../tool-system/builtin/complete-goal.js
|
|
|
22
22
|
import { CANCEL_GOAL_TOOL_NAME } from "../tool-system/builtin/cancel-goal.js";
|
|
23
23
|
import { redactSensitiveToolResultsInMessages, SENSITIVE_TOOL_RESULT_PLACEHOLDER, toolResultForDisplay, toolResultTranscriptText, toolResultsForDisplay, } from "../tool-system/tool-result-redaction.js";
|
|
24
24
|
import { addTokenUsage, cacheHitRateFromUsage, cumulativeCacheHitRate, } from "../session/usage.js";
|
|
25
|
-
import { createGoalBudgetTracker, recordGoalUsage, goalBudgetTerminationReason, applyGoalExtension, limitProximity, isSameGoalVersion, normalizeGoal, GOAL_DEFAULT_MAX_STOP_BLOCKS, } from "../goal/lifecycle.js";
|
|
25
|
+
import { createGoalBudgetTracker, recordGoalUsage, goalBudgetTerminationReason, applyGoalExtension, extendGoalLimit, limitProximity, isSameGoalVersion, normalizeGoal, GOAL_DEFAULT_MAX_STOP_BLOCKS, } from "../goal/lifecycle.js";
|
|
26
26
|
/**
|
|
27
27
|
* 把一个 ToolResult 映射成发给 LLM 的 tool_result ContentBlock。
|
|
28
28
|
* 有 contentBlocks(view_image 的图片块)就原样用作 content;否则
|
|
@@ -148,6 +148,9 @@ export class TurnLoop {
|
|
|
148
148
|
* effective limits.
|
|
149
149
|
*/
|
|
150
150
|
extend(opts) {
|
|
151
|
+
const previousMaxTurns = this.config.maxTurns;
|
|
152
|
+
const previousTokenBudget = this.goalTracker?.goal.tokenBudget;
|
|
153
|
+
const previousTimeBudgetMs = this.goalTracker?.goal.timeBudgetMs;
|
|
151
154
|
const elapsedMs = this.goalTracker ? Date.now() - this.goalTracker.startedAtMs : 0;
|
|
152
155
|
const next = applyGoalExtension(this.config.maxTurns, this.goalTracker?.goal, this.goalTracker?.tokensUsed ?? 0, elapsedMs, opts);
|
|
153
156
|
this.config = { ...this.config, maxTurns: next.maxTurns };
|
|
@@ -166,18 +169,16 @@ export class TurnLoop {
|
|
|
166
169
|
// limit that actually bites, so an extend that only bumped maxTurns/budgets
|
|
167
170
|
// couldn't keep it going. Resolve the current cap the same way the loop does.
|
|
168
171
|
const curCap = this.config.maxStopBlocks ?? GOAL_DEFAULT_MAX_STOP_BLOCKS;
|
|
169
|
-
const nextCap =
|
|
170
|
-
? curCap + Math.floor(opts.addStopBlocks)
|
|
171
|
-
: curCap;
|
|
172
|
+
const nextCap = extendGoalLimit(curCap, opts.addStopBlocks);
|
|
172
173
|
this.config = { ...this.config, maxStopBlocks: nextCap };
|
|
173
174
|
// ANY extension resets the consecutive stop-block streak: the user just
|
|
174
175
|
// asked to keep going, so a goal that was repeatedly re-blocked shouldn't be
|
|
175
176
|
// immediately re-capped. (Previously only addTurns reset it, leaving a
|
|
176
177
|
// budget-only extension unable to un-stick a capped goal.)
|
|
177
|
-
const extended =
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
const extended = next.maxTurns !== previousMaxTurns ||
|
|
179
|
+
nextCap !== curCap ||
|
|
180
|
+
next.tokenBudget !== previousTokenBudget ||
|
|
181
|
+
next.timeBudgetMs !== previousTimeBudgetMs;
|
|
181
182
|
if (extended) {
|
|
182
183
|
this.stopBlockCount = 0;
|
|
183
184
|
// Let the next approach re-announce against the raised ceilings.
|
package/dist/goal/lifecycle.d.ts
CHANGED
|
@@ -83,6 +83,8 @@ export type GoalLifecycleV1 = (GoalLifecycleBaseV1 & {
|
|
|
83
83
|
};
|
|
84
84
|
});
|
|
85
85
|
export type GoalLifecyclePhase = GoalLifecycleV1["phase"];
|
|
86
|
+
/** Add a user-controlled positive integer delta without producing Infinity or an unsafe integer. */
|
|
87
|
+
export declare function extendGoalLimit(current: number, addition: unknown): number;
|
|
86
88
|
/** Build one canonical lifecycle record from the compatibility GoalConfig view. */
|
|
87
89
|
export declare function createGoalLifecycle(goal: GoalConfig, phase?: Exclude<GoalLifecyclePhase, "terminal" | "waiting">, nowMs?: number): GoalLifecycleV1;
|
|
88
90
|
/** Compatibility view consumed by Engine/protocol while persistence uses the union. */
|
package/dist/goal/lifecycle.js
CHANGED
|
@@ -11,6 +11,25 @@
|
|
|
11
11
|
* so an unattended goal run can't burn tokens or wall time without bound.
|
|
12
12
|
*/
|
|
13
13
|
import { createHash, randomUUID } from "node:crypto";
|
|
14
|
+
function positiveSafeNumber(value) {
|
|
15
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 && value <= Number.MAX_SAFE_INTEGER;
|
|
16
|
+
}
|
|
17
|
+
function positiveSafeInteger(value) {
|
|
18
|
+
if (!positiveSafeNumber(value))
|
|
19
|
+
return undefined;
|
|
20
|
+
const integer = Math.floor(value);
|
|
21
|
+
return integer >= 1 ? integer : undefined;
|
|
22
|
+
}
|
|
23
|
+
/** Add a user-controlled positive integer delta without producing Infinity or an unsafe integer. */
|
|
24
|
+
export function extendGoalLimit(current, addition) {
|
|
25
|
+
const delta = positiveSafeInteger(addition);
|
|
26
|
+
if (delta === undefined)
|
|
27
|
+
return current;
|
|
28
|
+
const safeCurrent = typeof current === "number" && Number.isFinite(current) && current >= 0
|
|
29
|
+
? Math.min(Number.MAX_SAFE_INTEGER, Math.floor(current))
|
|
30
|
+
: Number.MAX_SAFE_INTEGER;
|
|
31
|
+
return Math.min(Number.MAX_SAFE_INTEGER, safeCurrent + delta);
|
|
32
|
+
}
|
|
14
33
|
function goalLifecycleConfig(goal) {
|
|
15
34
|
const { goalId: _goalId, revision: _revision, paused: _paused, ...config } = goal;
|
|
16
35
|
return config;
|
|
@@ -22,7 +41,7 @@ export function createGoalLifecycle(goal, phase = goal.paused === true
|
|
|
22
41
|
const base = {
|
|
23
42
|
version: 1,
|
|
24
43
|
goalId: goal.goalId ?? randomUUID(),
|
|
25
|
-
revision:
|
|
44
|
+
revision: positiveSafeInteger(goal.revision) ?? 1,
|
|
26
45
|
config: goalLifecycleConfig(goal),
|
|
27
46
|
updatedAtMs: nowMs,
|
|
28
47
|
};
|
|
@@ -53,7 +72,7 @@ export function decodeGoalLifecycle(value) {
|
|
|
53
72
|
if (typeof candidate.goalId !== "string" || candidate.goalId.length === 0)
|
|
54
73
|
return undefined;
|
|
55
74
|
if (typeof candidate.revision !== "number" ||
|
|
56
|
-
!Number.
|
|
75
|
+
!Number.isSafeInteger(candidate.revision) ||
|
|
57
76
|
candidate.revision < 1) {
|
|
58
77
|
return undefined;
|
|
59
78
|
}
|
|
@@ -80,17 +99,19 @@ export function decodeGoalLifecycle(value) {
|
|
|
80
99
|
for (const key of ["tokenBudget", "timeBudgetMs", "maxTurns", "maxStopBlocks"]) {
|
|
81
100
|
const field = config[key];
|
|
82
101
|
if (field !== undefined &&
|
|
83
|
-
|
|
102
|
+
!positiveSafeNumber(field)) {
|
|
84
103
|
return undefined;
|
|
85
104
|
}
|
|
86
105
|
}
|
|
87
106
|
for (const key of ["maxTurns", "maxStopBlocks"]) {
|
|
88
107
|
const field = config[key];
|
|
89
|
-
if (field !== undefined && !Number.
|
|
108
|
+
if (field !== undefined && !Number.isSafeInteger(field))
|
|
90
109
|
return undefined;
|
|
91
110
|
}
|
|
92
111
|
if (config.setAtMs !== undefined &&
|
|
93
|
-
(typeof config.setAtMs !== "number" ||
|
|
112
|
+
(typeof config.setAtMs !== "number" ||
|
|
113
|
+
!Number.isSafeInteger(config.setAtMs) ||
|
|
114
|
+
config.setAtMs < 0)) {
|
|
94
115
|
return undefined;
|
|
95
116
|
}
|
|
96
117
|
const phase = candidate.phase;
|
|
@@ -309,11 +330,12 @@ export const INTERACTIVE_DEFAULT_MAX_STOP_BLOCKS = 8;
|
|
|
309
330
|
* Pure + injectable so engine and tests agree.
|
|
310
331
|
*/
|
|
311
332
|
export function resolveMaxStopBlocks(configMaxStopBlocks, goal) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
333
|
+
const configLimit = positiveSafeInteger(configMaxStopBlocks);
|
|
334
|
+
if (configLimit !== undefined)
|
|
335
|
+
return configLimit;
|
|
336
|
+
const goalLimit = positiveSafeInteger(goal?.maxStopBlocks);
|
|
337
|
+
if (goalLimit !== undefined)
|
|
338
|
+
return goalLimit;
|
|
317
339
|
return goal ? GOAL_DEFAULT_MAX_STOP_BLOCKS : INTERACTIVE_DEFAULT_MAX_STOP_BLOCKS;
|
|
318
340
|
}
|
|
319
341
|
/**
|
|
@@ -340,24 +362,25 @@ export function normalizeGoal(raw) {
|
|
|
340
362
|
const out = { objective };
|
|
341
363
|
if (typeof obj.goalId === "string" && obj.goalId.trim())
|
|
342
364
|
out.goalId = obj.goalId;
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
365
|
+
const revision = positiveSafeInteger(obj.revision);
|
|
366
|
+
if (revision !== undefined)
|
|
367
|
+
out.revision = revision;
|
|
346
368
|
if (obj.paused === true)
|
|
347
369
|
out.paused = true;
|
|
348
|
-
if (
|
|
370
|
+
if (positiveSafeNumber(obj.tokenBudget))
|
|
349
371
|
out.tokenBudget = obj.tokenBudget;
|
|
350
|
-
if (
|
|
372
|
+
if (positiveSafeNumber(obj.timeBudgetMs))
|
|
351
373
|
out.timeBudgetMs = obj.timeBudgetMs;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
374
|
+
const maxTurns = positiveSafeInteger(obj.maxTurns);
|
|
375
|
+
if (maxTurns !== undefined)
|
|
376
|
+
out.maxTurns = maxTurns;
|
|
377
|
+
const maxStopBlocks = positiveSafeInteger(obj.maxStopBlocks);
|
|
378
|
+
if (maxStopBlocks !== undefined)
|
|
379
|
+
out.maxStopBlocks = maxStopBlocks;
|
|
357
380
|
// Preserve a positive goal-set timestamp so a resumed/inherited goal keeps
|
|
358
381
|
// the anchor for relative deadlines. Non-positive/junk is dropped, matching
|
|
359
382
|
// the budget fields (a bogus anchor is worse than none).
|
|
360
|
-
if (
|
|
383
|
+
if (positiveSafeInteger(obj.setAtMs) !== undefined)
|
|
361
384
|
out.setAtMs = obj.setAtMs;
|
|
362
385
|
return out;
|
|
363
386
|
}
|
|
@@ -391,10 +414,11 @@ export const INTERACTIVE_DEFAULT_MAX_TURNS = 100;
|
|
|
391
414
|
* Pure + injectable so the engine and tests agree on the rule.
|
|
392
415
|
*/
|
|
393
416
|
export function resolveMaxTurns(configMaxTurns, goal) {
|
|
394
|
-
|
|
395
|
-
|
|
417
|
+
const configLimit = positiveSafeInteger(configMaxTurns);
|
|
418
|
+
if (configLimit !== undefined)
|
|
419
|
+
return configLimit;
|
|
396
420
|
if (goal)
|
|
397
|
-
return goal.maxTurns ?? GOAL_DEFAULT_MAX_TURNS;
|
|
421
|
+
return positiveSafeInteger(goal.maxTurns) ?? GOAL_DEFAULT_MAX_TURNS;
|
|
398
422
|
return INTERACTIVE_DEFAULT_MAX_TURNS;
|
|
399
423
|
}
|
|
400
424
|
/** Warn when this many turns (or fewer) remain before maxTurns. */
|
|
@@ -436,19 +460,17 @@ export function limitProximity(turnCount, maxTurns, stopBlockCount, maxStopBlock
|
|
|
436
460
|
*/
|
|
437
461
|
export function applyGoalExtension(currentMaxTurns, goal, tokensUsed, elapsedMs, ext) {
|
|
438
462
|
let maxTurns = currentMaxTurns;
|
|
439
|
-
|
|
440
|
-
maxTurns += Math.floor(ext.addTurns);
|
|
441
|
-
}
|
|
463
|
+
maxTurns = extendGoalLimit(maxTurns, ext.addTurns);
|
|
442
464
|
let tokenBudget = goal?.tokenBudget;
|
|
443
465
|
let timeBudgetMs = goal?.timeBudgetMs;
|
|
444
466
|
if (goal) {
|
|
445
|
-
if (
|
|
446
|
-
tokenBudget = (tokenBudget ?? tokensUsed
|
|
467
|
+
if (positiveSafeInteger(ext.addTokenBudget) !== undefined) {
|
|
468
|
+
tokenBudget = extendGoalLimit(tokenBudget ?? tokensUsed, ext.addTokenBudget);
|
|
447
469
|
}
|
|
448
|
-
if (
|
|
470
|
+
if (positiveSafeInteger(ext.addTimeBudgetMs) !== undefined) {
|
|
449
471
|
// Seed an unset cap from elapsed time (not 0), mirroring the token branch,
|
|
450
472
|
// so the new cap lands above current usage. Fixes B1.
|
|
451
|
-
timeBudgetMs = (timeBudgetMs ?? elapsedMs
|
|
473
|
+
timeBudgetMs = extendGoalLimit(timeBudgetMs ?? elapsedMs, ext.addTimeBudgetMs);
|
|
452
474
|
}
|
|
453
475
|
}
|
|
454
476
|
return { maxTurns, tokenBudget, timeBudgetMs };
|
|
@@ -459,8 +481,9 @@ export function createGoalBudgetTracker(goal, nowMs) {
|
|
|
459
481
|
}
|
|
460
482
|
/** Add this turn's token usage to the running total. */
|
|
461
483
|
export function recordGoalUsage(tracker, turnTokens) {
|
|
462
|
-
if (turnTokens
|
|
463
|
-
tracker.tokensUsed
|
|
484
|
+
if (positiveSafeInteger(turnTokens) !== undefined) {
|
|
485
|
+
tracker.tokensUsed = extendGoalLimit(tracker.tokensUsed, turnTokens);
|
|
486
|
+
}
|
|
464
487
|
}
|
|
465
488
|
/**
|
|
466
489
|
* Has the run exceeded any configured budget? `nowMs` is injected so callers
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Public API exports.
|
|
5
5
|
*/
|
|
6
|
-
export declare const VERSION = "0.8.
|
|
6
|
+
export declare const VERSION = "0.8.10";
|
|
7
7
|
export type { Message, ContentBlock, ToolDefinition, ToolCall, ToolResult, RegisteredTool, TranscriptEvent, TranscriptEventType, SessionState, SessionKind, SessionWorkspace, SessionForkLineage, ContextUsageAnchor, SessionStatus, TokenUsage, CompiledInput, PermissionDecision, PermissionMode, PermissionRule, TurnPhase, TurnResult, TerminalReason, TurnCompletionKind, StreamEvent, StreamCallback, LLMConfig, ClientDefaults, LLMResponse, Settings, MCPServerConfig, } from "./types.js";
|
|
8
8
|
export type { GoalConfig, GoalLifecycleConfig, GoalLifecyclePhase, GoalLifecycleTerminalReason, GoalLifecycleV1, } from "./goal/lifecycle.js";
|
|
9
9
|
export { FrameworkError, LLMError, LLMRateLimitError, ContextLimitError, ToolError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, PermissionDeniedError, SessionError, TranscriptError, ConfigError, SandboxUnavailableError, } from "./exceptions.js";
|
|
@@ -66,7 +66,7 @@ export { Methods, ErrorCodes, type RpcMessage, type RunResult, type ForkSessionP
|
|
|
66
66
|
export type * from "./protocol/mobile-remote-types.js";
|
|
67
67
|
export { Transcript } from "./session/transcript.js";
|
|
68
68
|
export { SessionManager, sessionMainRoot, codeShellHome, sessionsRoot, buildForkState, buildForkTranscript, type ForkSessionOptions, type ForkSessionResult, } from "./session/session-manager.js";
|
|
69
|
-
export { FileHistory } from "./session/file-history.js";
|
|
69
|
+
export { FileHistory, type FileSnapshot, type RedoRecord, type TurnUndoPlan, } from "./session/file-history.js";
|
|
70
70
|
export { latestUndoTarget, earliestSnapshotsPerFile, latestTurnUndoTargets, latestRedoTargets, } from "./session/undo-target.js";
|
|
71
71
|
export { diffLines, renderDiffPreview, type DiffLine } from "./session/simple-diff.js";
|
|
72
72
|
export { MemoryManager } from "./session/memory.js";
|
|
@@ -74,7 +74,6 @@ export type { MemoryEntry, MemoryOrigin, MemoryScope } from "./session/memory.js
|
|
|
74
74
|
export type { RouteSessionMessageInput, SessionMessageRouter, SessionMessageTarget, SessionMessageToolService, } from "./session/session-message.js";
|
|
75
75
|
export { runDreamConsolidation, type DreamConsolidationInput, type DreamConsolidationResult, } from "./services/dream-consolidation.js";
|
|
76
76
|
export { authorize, refreshToken, generatePKCE, createHardenedOAuthFetch, type OAuthConfig, type OAuthTokens, type OAuthAuthorizeOptions, type OAuthRefreshOptions, type HardenedOAuthFetchOptions, } from "./services/oauth.js";
|
|
77
|
-
export type { FileSnapshot, RedoRecord } from "./session/file-history.js";
|
|
78
77
|
export { PromptComposer } from "./prompt/composer.js";
|
|
79
78
|
export { SectionCache } from "./prompt/section-cache.js";
|
|
80
79
|
export { scanInstructions, combineInstructions } from "./prompt/instruction-scanner.js";
|
package/dist/index.internal.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export { execFileNoThrow } from "./utils/execFileNoThrow.js";
|
|
|
12
12
|
export { findExecutable, resolveExecutable, setGitPathOverride, resolveGit, isGitAvailable, resolveGitPath, } from "./utils/exec.js";
|
|
13
13
|
export { gte } from "./utils/semver.js";
|
|
14
14
|
export { lock, lockSync, unlock, check } from "./utils/lockfile.js";
|
|
15
|
+
export { acquireFileLock, acquireLockOnPath, mutateJsonFile } from "./utils/file-mutex.js";
|
|
15
16
|
export { logForDebugging } from "./utils/debug.js";
|
|
16
17
|
export { isEnvTruthy, isEnvDefinedFalsy, getClaudeConfigHomeDir, isBareMode, parseEnvVars, shouldMaintainProjectWorkingDir, getAWSRegion, getDefaultVertexRegion, getVertexRegionForModel, } from "./utils/envUtils.js";
|
|
17
18
|
export { startCapturingEarlyInput, stopCapturingEarlyInput, consumeEarlyInput, hasEarlyInput, seedEarlyInput, isCapturingEarlyInput, } from "./utils/earlyInput.js";
|
package/dist/index.internal.js
CHANGED
|
@@ -13,6 +13,7 @@ export { execFileNoThrow } from "./utils/execFileNoThrow.js";
|
|
|
13
13
|
export { findExecutable, resolveExecutable, setGitPathOverride, resolveGit, isGitAvailable, resolveGitPath, } from "./utils/exec.js";
|
|
14
14
|
export { gte } from "./utils/semver.js";
|
|
15
15
|
export { lock, lockSync, unlock, check } from "./utils/lockfile.js";
|
|
16
|
+
export { acquireFileLock, acquireLockOnPath, mutateJsonFile } from "./utils/file-mutex.js";
|
|
16
17
|
export { logForDebugging } from "./utils/debug.js";
|
|
17
18
|
export { isEnvTruthy, isEnvDefinedFalsy, getClaudeConfigHomeDir, isBareMode, parseEnvVars, shouldMaintainProjectWorkingDir, getAWSRegion, getDefaultVertexRegion, getVertexRegionForModel, } from "./utils/envUtils.js";
|
|
18
19
|
export { startCapturingEarlyInput, stopCapturingEarlyInput, consumeEarlyInput, hasEarlyInput, seedEarlyInput, isCapturingEarlyInput, } from "./utils/earlyInput.js";
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Public API exports.
|
|
5
5
|
*/
|
|
6
|
-
export const VERSION = "0.8.
|
|
6
|
+
export const VERSION = "0.8.10";
|
|
7
7
|
// ─── Exceptions ──────────────────────────────────────────────────
|
|
8
8
|
export { FrameworkError, LLMError, LLMRateLimitError, ContextLimitError, ToolError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, PermissionDeniedError, SessionError, TranscriptError, ConfigError, SandboxUnavailableError, } from "./exceptions.js";
|
|
9
9
|
// ─── Engine (primary API) ────────────────────────────────────────
|
|
@@ -55,7 +55,7 @@ export { Methods, ErrorCodes, } from "./protocol/types.js";
|
|
|
55
55
|
// ─── Session ─────────────────────────────────────────────────────
|
|
56
56
|
export { Transcript } from "./session/transcript.js";
|
|
57
57
|
export { SessionManager, sessionMainRoot, codeShellHome, sessionsRoot, buildForkState, buildForkTranscript, } from "./session/session-manager.js";
|
|
58
|
-
export { FileHistory } from "./session/file-history.js";
|
|
58
|
+
export { FileHistory, } from "./session/file-history.js";
|
|
59
59
|
export { latestUndoTarget, earliestSnapshotsPerFile, latestTurnUndoTargets, latestRedoTargets, } from "./session/undo-target.js";
|
|
60
60
|
export { diffLines, renderDiffPreview } from "./session/simple-diff.js";
|
|
61
61
|
export { MemoryManager } from "./session/memory.js";
|
package/dist/links/cli.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface CliLinkRunOptions {
|
|
|
17
17
|
signal?: AbortSignal;
|
|
18
18
|
timeoutMs: number;
|
|
19
19
|
input?: string;
|
|
20
|
+
/** Keep the pipe alive for browser/device login CLIs that inspect stdin. */
|
|
21
|
+
keepStdinOpen?: boolean;
|
|
20
22
|
}
|
|
21
23
|
export type CliLinkCommandRunner = (providerId: CliLinkProviderId, command: string, args: string[], options: CliLinkRunOptions) => Promise<CliLinkCommandResult>;
|
|
22
24
|
/** App-private CLI location. It never mutates the user's system PATH. */
|
package/dist/links/cli.js
CHANGED
|
@@ -103,10 +103,15 @@ export const runCliLinkCommand = (providerId, command, args, options) => new Pro
|
|
|
103
103
|
}
|
|
104
104
|
resolve({ stdout: String(stdout), stderr: String(stderr) });
|
|
105
105
|
});
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
|
|
106
|
+
if (options.input !== undefined)
|
|
107
|
+
child.stdin?.write(options.input);
|
|
108
|
+
// API calls consume a complete JSON body and must see EOF. Browser/device
|
|
109
|
+
// login is different: gh/glab prompt before opening the browser and some
|
|
110
|
+
// versions of Vercel first present a default login method. Feed one Enter
|
|
111
|
+
// below, but keep the pipe alive so those CLIs do not fail immediately with
|
|
112
|
+
// EOF while the browser authorization is still in progress.
|
|
113
|
+
if (!options.keepStdinOpen)
|
|
114
|
+
child.stdin?.end();
|
|
110
115
|
});
|
|
111
116
|
function parseJson(result, providerId) {
|
|
112
117
|
try {
|
|
@@ -289,6 +294,8 @@ export async function connectCliLink(providerId, options, run = runCliLinkComman
|
|
|
289
294
|
cwd: options.cwd,
|
|
290
295
|
signal: options.signal,
|
|
291
296
|
timeoutMs: 15 * 60_000,
|
|
297
|
+
input: "\n",
|
|
298
|
+
keepStdinOpen: true,
|
|
292
299
|
});
|
|
293
300
|
}
|
|
294
301
|
catch (error) {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* No remote source this version (design doc §5).
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
11
|
+
import { closeSync, constants, fstatSync, lstatSync, openSync, readFileSync } from "node:fs";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { userHome } from "../settings/manager.js";
|
|
14
14
|
import { logger } from "../logging/logger.js";
|
|
@@ -16,6 +16,7 @@ import { BUILTIN_CATALOG } from "./builtin.js";
|
|
|
16
16
|
import { userCatalogFileSchema } from "./types.js";
|
|
17
17
|
export { BUILTIN_CATALOG } from "./builtin.js";
|
|
18
18
|
export { saveCatalogEntry, deleteUserCatalogEntry } from "./save-entry.js";
|
|
19
|
+
const MAX_USER_CATALOG_BYTES = 4 * 1024 * 1024;
|
|
19
20
|
/** Path to the user catalog file (source B). */
|
|
20
21
|
export function userCatalogPath() {
|
|
21
22
|
return join(userHome(), ".code-shell", "model-catalog.user.json");
|
|
@@ -26,10 +27,18 @@ export function userCatalogPath() {
|
|
|
26
27
|
*/
|
|
27
28
|
export function loadUserCatalog() {
|
|
28
29
|
const path = userCatalogPath();
|
|
29
|
-
|
|
30
|
-
return [];
|
|
30
|
+
let descriptor;
|
|
31
31
|
try {
|
|
32
|
-
const
|
|
32
|
+
const metadata = lstatSync(path);
|
|
33
|
+
if (metadata.isSymbolicLink() || !metadata.isFile() || metadata.size > MAX_USER_CATALOG_BYTES) {
|
|
34
|
+
throw new Error("model catalog must be a bounded regular file");
|
|
35
|
+
}
|
|
36
|
+
descriptor = openSync(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
37
|
+
const opened = fstatSync(descriptor);
|
|
38
|
+
if (!opened.isFile() || opened.size > MAX_USER_CATALOG_BYTES) {
|
|
39
|
+
throw new Error("model catalog must be a bounded regular file");
|
|
40
|
+
}
|
|
41
|
+
const parsed = JSON.parse(readFileSync(descriptor, "utf-8"));
|
|
33
42
|
const result = userCatalogFileSchema.safeParse(parsed);
|
|
34
43
|
if (!result.success) {
|
|
35
44
|
logger.warn("model_catalog_user_invalid", { path, issues: result.error.issues.length });
|
|
@@ -38,9 +47,15 @@ export function loadUserCatalog() {
|
|
|
38
47
|
return result.data;
|
|
39
48
|
}
|
|
40
49
|
catch (err) {
|
|
50
|
+
if (err.code === "ENOENT")
|
|
51
|
+
return [];
|
|
41
52
|
logger.warn("model_catalog_user_read_failed", { path, error: err.message });
|
|
42
53
|
return [];
|
|
43
54
|
}
|
|
55
|
+
finally {
|
|
56
|
+
if (descriptor !== undefined)
|
|
57
|
+
closeSync(descriptor);
|
|
58
|
+
}
|
|
44
59
|
}
|
|
45
60
|
/**
|
|
46
61
|
* Merged catalog: built-in (A) ∪ user (B), deduped by `id` with user winning.
|