@cjhyy/code-shell-core 0.8.13 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/automation/desktop-authority-client.d.ts +9 -0
  2. package/dist/automation/desktop-authority-client.js +47 -0
  3. package/dist/automation/scheduler.d.ts +8 -0
  4. package/dist/automation/scheduler.js +37 -1
  5. package/dist/automation/store.js +14 -1
  6. package/dist/capabilities/index.d.ts +1 -0
  7. package/dist/cli/agent-server-stdio.js +8 -4
  8. package/dist/engine/engine-workspace-authority.d.ts +35 -0
  9. package/dist/engine/engine-workspace-authority.js +136 -0
  10. package/dist/engine/engine.d.ts +5 -10
  11. package/dist/engine/engine.js +108 -112
  12. package/dist/engine/input-attachments.d.ts +1 -0
  13. package/dist/engine/input-attachments.js +6 -2
  14. package/dist/engine/run-environment.d.ts +8 -3
  15. package/dist/engine/run-environment.js +33 -8
  16. package/dist/engine/run-image-input.d.ts +1 -0
  17. package/dist/engine/run-image-input.js +1 -0
  18. package/dist/engine/run-session-open.d.ts +2 -1
  19. package/dist/engine/run-session-open.js +7 -1
  20. package/dist/engine/run-setup.d.ts +1 -0
  21. package/dist/engine/run-setup.js +2 -1
  22. package/dist/engine/run-tooling.d.ts +2 -0
  23. package/dist/engine/run-tooling.js +3 -0
  24. package/dist/engine/run-types.d.ts +4 -0
  25. package/dist/engine/run-workspace.d.ts +6 -1
  26. package/dist/engine/run-workspace.js +47 -0
  27. package/dist/engine/subagent-spawner.d.ts +1 -0
  28. package/dist/engine/subagent-spawner.js +1 -0
  29. package/dist/engine/turn-loop.js +17 -0
  30. package/dist/engine/types.d.ts +2 -0
  31. package/dist/index.d.ts +4 -4
  32. package/dist/index.extension.d.ts +1 -1
  33. package/dist/index.internal.d.ts +3 -2
  34. package/dist/index.internal.js +2 -0
  35. package/dist/index.js +2 -2
  36. package/dist/llm/client-base.d.ts +2 -1
  37. package/dist/llm/client-base.js +3 -1
  38. package/dist/llm/providers/anthropic.js +24 -25
  39. package/dist/llm/providers/openai.d.ts +4 -1
  40. package/dist/llm/providers/openai.js +76 -13
  41. package/dist/panel-apps/index.d.ts +1 -1
  42. package/dist/panel-apps/index.js +1 -1
  43. package/dist/panel-apps/installer.d.ts +29 -0
  44. package/dist/panel-apps/installer.js +124 -15
  45. package/dist/plugins/pluginAutomationTemplates.d.ts +2 -0
  46. package/dist/plugins/pluginAutomationTemplates.js +4 -0
  47. package/dist/plugins/pluginCatalog.d.ts +6 -0
  48. package/dist/plugins/pluginCatalog.js +7 -2
  49. package/dist/plugins/pluginContent.d.ts +1 -1
  50. package/dist/plugins/pluginContent.js +2 -10
  51. package/dist/prompt/composer.d.ts +4 -1
  52. package/dist/prompt/composer.js +11 -3
  53. package/dist/protocol/chat-session-manager.d.ts +42 -1
  54. package/dist/protocol/chat-session-manager.js +167 -4
  55. package/dist/protocol/chat-session.d.ts +11 -1
  56. package/dist/protocol/chat-session.js +20 -2
  57. package/dist/protocol/mobile-remote-types.d.ts +15 -0
  58. package/dist/protocol/server.d.ts +1 -2
  59. package/dist/protocol/server.js +28 -51
  60. package/dist/protocol/session-workspace-rpc.d.ts +23 -0
  61. package/dist/protocol/session-workspace-rpc.js +171 -0
  62. package/dist/protocol/types.d.ts +47 -1
  63. package/dist/protocol/types.js +4 -0
  64. package/dist/session/session-manager.d.ts +25 -0
  65. package/dist/session/session-manager.js +106 -6
  66. package/dist/session/transcript.d.ts +9 -0
  67. package/dist/session/transcript.js +81 -0
  68. package/dist/settings/manager.d.ts +12 -0
  69. package/dist/settings/manager.js +31 -0
  70. package/dist/tool-system/builtin/configure-model-connection.d.ts +36 -0
  71. package/dist/tool-system/builtin/configure-model-connection.js +396 -0
  72. package/dist/tool-system/builtin/cron.d.ts +11 -0
  73. package/dist/tool-system/builtin/cron.js +27 -2
  74. package/dist/tool-system/builtin/edit-model-catalog.d.ts +4 -6
  75. package/dist/tool-system/builtin/edit-model-catalog.js +5 -8
  76. package/dist/tool-system/builtin/edit.js +5 -3
  77. package/dist/tool-system/builtin/index.js +14 -0
  78. package/dist/tool-system/builtin/install-capability.js +22 -8
  79. package/dist/tool-system/builtin/settings-changed.d.ts +9 -0
  80. package/dist/tool-system/builtin/settings-changed.js +18 -0
  81. package/dist/tool-system/builtin/write.js +5 -3
  82. package/dist/tool-system/context.d.ts +12 -3
  83. package/dist/tool-system/executor.js +1 -1
  84. package/dist/tool-system/path-policy.d.ts +11 -3
  85. package/dist/tool-system/path-policy.js +56 -36
  86. package/dist/types.d.ts +11 -0
  87. package/dist/workspace/canonical-key.d.ts +7 -0
  88. package/dist/workspace/canonical-key.js +39 -0
  89. package/dist/workspace/workspace-context.d.ts +26 -0
  90. package/dist/workspace/workspace-context.js +112 -0
  91. package/package.json +1 -1
@@ -28,6 +28,7 @@ export function buildRunToolContext(args) {
28
28
  // properties and survive the spread. Off keeps network undefined.
29
29
  sandbox: args.sandbox,
30
30
  cwd: args.cwd,
31
+ workspace: args.workspace,
31
32
  shellEnv: args.shellEnv,
32
33
  // TodoWrite reads this to push task_update events independently
33
34
  // of its return value, so the UI's pinned task panel refreshes
@@ -166,6 +167,7 @@ export async function connectRunMcp(args) {
166
167
  export function buildToolVisibility(inputs) {
167
168
  return {
168
169
  cwd: inputs.cwd,
170
+ ...(inputs.workspace ? { workspace: inputs.workspace } : {}),
169
171
  hasGoal: inputs.hasGoal,
170
172
  ...(inputs.sessionId ? { sessionId: inputs.sessionId } : {}),
171
173
  ...(inputs.settingsScope ? { settingsScope: inputs.settingsScope } : {}),
@@ -186,6 +188,7 @@ export function assembleRunToolDefs(args) {
186
188
  const guardCwd = args.guardCwd;
187
189
  const toolVisibility = buildToolVisibility({
188
190
  cwd: guardCwd,
191
+ workspace: toolCtx.workspace,
189
192
  hasGoal: args.hasRunnableGoal,
190
193
  sessionId: toolCtx.sessionId,
191
194
  settingsScope: args.settingsScope,
@@ -87,6 +87,8 @@ export declare const ISOLATED_TASK_SYSTEM_PROMPT = "# Isolated Task Boundary\n\n
87
87
  export declare const ISOLATED_TASK_PROFILE: RunBehaviorProfile;
88
88
  export interface EngineRunOptions {
89
89
  cwd?: string;
90
+ /** Trusted run-scoped root authorization. Protocol hosts must strip untrusted values. */
91
+ workspaceContext?: import("../workspace/workspace-context.js").WorkspaceContext;
90
92
  onStream?: StreamCallback;
91
93
  signal?: AbortSignal;
92
94
  sessionId?: string;
@@ -106,6 +108,8 @@ export interface EngineRunOptions {
106
108
  archiveBeforeCurrentTurn?: {
107
109
  fromClientMessageId?: string;
108
110
  segmentId?: string;
111
+ /** Host-authored replacement for the archived span; skips the summarizer. */
112
+ summary?: string;
109
113
  };
110
114
  attachments?: InputAttachmentMeta[];
111
115
  /** Named per-run behavior profile supplied by interactive product surfaces. */
@@ -11,6 +11,7 @@ import type { SettingsManager } from "../settings/manager.js";
11
11
  import type { EngineConfig, EngineResult } from "./types.js";
12
12
  import type { EngineRunOptions, RunBehaviorProfile } from "./run-types.js";
13
13
  import { type RunProfileState } from "./run-setup.js";
14
+ import { type WorkspaceContext } from "../workspace/workspace-context.js";
14
15
  /**
15
16
  * Resolve the working directory for a run. Precedence for legacy sessions:
16
17
  * options.cwd > resumed session's state.cwd > config.cwd > process.cwd()
@@ -30,6 +31,9 @@ export interface RunWorkspaceResolution {
30
31
  runPermissionMode: NonNullable<EngineConfig["permissionMode"]>;
31
32
  runPlanMode: boolean;
32
33
  cwd: string;
34
+ workspaceContext: WorkspaceContext;
35
+ /** False only for the compatibility context synthesized from a lone cwd. */
36
+ authoritativeWorkspaceContext: boolean;
33
37
  profileState: RunProfileState;
34
38
  }
35
39
  export type ResolveRunWorkspaceResult = {
@@ -41,10 +45,11 @@ export type ResolveRunWorkspaceResult = {
41
45
  };
42
46
  export declare function resolveRunWorkspace(args: {
43
47
  options: EngineRunOptions | undefined;
44
- sessionManager: Pick<SessionManager, "exists" | "readSessionKind" | "readSessionWorkspaceProfile" | "resolveSessionWorkspaceForResume" | "readSessionMainRoot">;
48
+ sessionManager: Pick<SessionManager, "exists" | "readSessionKind" | "readSessionWorkspaceProfile" | "resolveSessionWorkspaceForResume" | "readSessionMainRoot" | "readSessionProjectBinding">;
45
49
  resolveBehaviorProfile: (sessionKind: string, behaviorMode: string | undefined) => RunBehaviorProfile | undefined;
46
50
  configPermissionMode: EngineConfig["permissionMode"];
47
51
  configCwd: string | undefined;
52
+ configWorkspaceContext: WorkspaceContext | undefined;
48
53
  settings: SettingsManager;
49
54
  processCwd: string;
50
55
  }): Promise<ResolveRunWorkspaceResult>;
@@ -1,4 +1,6 @@
1
1
  import { resolveRunProfileState } from "./run-setup.js";
2
+ import { canonicalKey } from "../workspace/canonical-key.js";
3
+ import { legacySingleRootWorkspace, validateWorkspaceContext, workspacePrimaryRoot, } from "../workspace/workspace-context.js";
2
4
  /**
3
5
  * Resolve the working directory for a run. Precedence for legacy sessions:
4
6
  * options.cwd > resumed session's state.cwd > config.cwd > process.cwd()
@@ -93,6 +95,37 @@ export async function resolveRunWorkspace(args) {
93
95
  configCwd: args.configCwd,
94
96
  processCwd: args.processCwd,
95
97
  });
98
+ const rawWorkspaceContext = options?.workspaceContext ?? args.configWorkspaceContext;
99
+ let workspaceContext;
100
+ try {
101
+ workspaceContext = rawWorkspaceContext
102
+ ? validateWorkspaceContext(rawWorkspaceContext)
103
+ : legacySingleRootWorkspace(cwd);
104
+ }
105
+ catch (error) {
106
+ return workspaceError(options?.sessionId, error instanceof Error ? error.message : String(error));
107
+ }
108
+ const authoritativeWorkspaceContext = rawWorkspaceContext !== undefined;
109
+ const primary = workspacePrimaryRoot(workspaceContext);
110
+ if (canonicalKey(primary.path) !== canonicalKey(cwd)) {
111
+ return workspaceError(options?.sessionId, `WorkspaceContext primary does not match effective cwd: ${primary.path} != ${cwd}`);
112
+ }
113
+ if (workspaceResume?.ok &&
114
+ workspaceResume.reason !== "legacy" &&
115
+ canonicalKey(workspaceResume.workspace.root) !== canonicalKey(primary.path)) {
116
+ return workspaceError(options?.sessionId, "WorkspaceContext primary does not match SessionWorkspace");
117
+ }
118
+ const binding = options?.sessionId
119
+ ? args.sessionManager.readSessionProjectBinding(options.sessionId)
120
+ : undefined;
121
+ if (binding && !authoritativeWorkspaceContext) {
122
+ return workspaceError(options?.sessionId, "bound Session requires an authoritative WorkspaceContext");
123
+ }
124
+ if (binding &&
125
+ (binding.projectId !== workspaceContext.projectId ||
126
+ binding.mainRootId !== workspaceContext.sessionMainRootId)) {
127
+ return workspaceError(options?.sessionId, "WorkspaceContext does not match persisted project binding");
128
+ }
96
129
  const profileState = profile?.disableWorkspaceProfile
97
130
  ? {
98
131
  workspaceProfile: undefined,
@@ -114,7 +147,21 @@ export async function resolveRunWorkspace(args) {
114
147
  runPermissionMode,
115
148
  runPlanMode,
116
149
  cwd,
150
+ workspaceContext,
151
+ authoritativeWorkspaceContext,
117
152
  profileState,
118
153
  },
119
154
  };
120
155
  }
156
+ function workspaceError(sessionId, message) {
157
+ return {
158
+ ok: false,
159
+ result: {
160
+ text: `ERROR: ${message}`,
161
+ reason: "completed",
162
+ sessionId: sessionId ?? "workspace-invalid",
163
+ turnCount: 0,
164
+ usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
165
+ },
166
+ };
167
+ }
@@ -21,6 +21,7 @@ export interface CreateSubAgentSpawnerDeps {
21
21
  parentSandbox: SandboxConfig;
22
22
  presetName: AgentPresetName;
23
23
  cwd: string;
24
+ workspaceContext?: import("../workspace/workspace-context.js").WorkspaceContext;
24
25
  permissionMode: NonNullable<EngineConfig["permissionMode"]>;
25
26
  modelPool?: ModelPool;
26
27
  appendParentSubagent: (agentId: string, description: string) => void;
@@ -233,6 +233,7 @@ export function createSubAgentSpawner(deps) {
233
233
  retryMaxAttempts: 2,
234
234
  },
235
235
  cwd: deps.cwd,
236
+ workspaceContext: deps.workspaceContext ?? deps.parentConfig.workspaceContext,
236
237
  permissionMode: deps.permissionMode,
237
238
  preset: deps.presetName,
238
239
  enabledBuiltinTools: scope.enabled,
@@ -836,6 +836,23 @@ export class TurnLoop {
836
836
  if (response.text) {
837
837
  messages.push({ role: "assistant", content: response.text });
838
838
  }
839
+ // Anthropic streams tool_use_start as soon as the content block
840
+ // opens. If max_tokens cuts the JSON arguments off, we deliberately
841
+ // do not execute that call, but the renderer still needs a terminal
842
+ // event for the already-open card. Without it Claude tool cards stay
843
+ // "working" forever (often until a later context compaction hides
844
+ // them), even though the loop has moved on to a retry.
845
+ for (const toolCall of response.toolCalls) {
846
+ this.config.onStream?.({
847
+ type: "tool_result",
848
+ result: {
849
+ id: toolCall.id,
850
+ toolName: toolCall.toolName,
851
+ error: "Tool call was not executed because its arguments were truncated.",
852
+ isError: true,
853
+ },
854
+ });
855
+ }
839
856
  messages.push({
840
857
  role: "user",
841
858
  content: "<system-reminder>Your previous response was truncated by the max output token limit before the tool call finished, so its arguments are incomplete. Do not assume it ran. Either retry with a smaller/more focused tool call (e.g. write the file in sections via Edit), or raise this model's maxOutputTokens.</system-reminder>",
@@ -33,6 +33,8 @@ export interface EngineConfig {
33
33
  */
34
34
  clientDefaults?: ClientDefaults;
35
35
  cwd?: string;
36
+ /** Trusted host-provided root authorization for runs created by this Engine. */
37
+ workspaceContext?: import("../workspace/workspace-context.js").WorkspaceContext;
36
38
  maxTurns?: number;
37
39
  /**
38
40
  * Override the goal-mode consecutive-stop-block cap (TODO 3.1). Falls back to
package/dist/index.d.ts CHANGED
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Public API exports.
5
5
  */
6
- export declare const VERSION = "0.8.13";
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";
6
+ export declare const VERSION = "0.9.0";
7
+ export type { Message, ContentBlock, ToolDefinition, ToolCall, ToolResult, RegisteredTool, TranscriptEvent, TranscriptEventType, SessionState, SessionKind, SessionWorkspace, SessionProjectBinding, 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, CompositionError, SandboxUnavailableError, } from "./exceptions.js";
10
10
  export { compileComposition, toCompositionSnapshot, computeCompositionDigest, compositionToolCatalog, resolvePresetFromComposition, } from "./composition/index.js";
@@ -62,7 +62,7 @@ export { AgentClient, type BackgroundAgentCompletedHandler } from "./protocol/cl
62
62
  export { createInProcessTransport, StdioTransport, type Transport } from "./protocol/transport.js";
63
63
  export { SocketTransport, listenTcp, type TcpListenResult } from "./protocol/tcp-transport.js";
64
64
  export { createServer, createClient, type CreateServerOptions, type CreateClientOptions, type ServerHandle, } from "./protocol/factories.js";
65
- export { Methods, ErrorCodes, type RpcMessage, type RunResult, type ForkSessionParams, type ForkSessionResult as ProtocolForkSessionResult, } from "./protocol/types.js";
65
+ export { Methods, ErrorCodes, type RpcMessage, type RunResult, type ForkSessionParams, type ForkSessionResult as ProtocolForkSessionResult, type MigrateSessionMainRootResult, } from "./protocol/types.js";
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";
@@ -98,7 +98,7 @@ export { mergePluginMcpServers, readPluginMcp } from "./plugins/installer/loadPl
98
98
  export { approvePluginMcp, listPluginMcpTrust, revokePluginMcp, type PluginMcpApprovalResult, type PluginMcpTrustEntry, } from "./plugins/pluginMcpApproval.js";
99
99
  export { pluginMcpApprovalState, type PluginMcpApprovalState, } from "./plugins/pluginMcpIntegrity.js";
100
100
  export { pluginsRoot } from "./plugins/installer/paths.js";
101
- export { PANEL_APP_ICONS, PANEL_APP_MANIFEST_FILE, PANEL_APP_PERMISSIONS, PanelAppManifest, PanelAppAlreadyInstalledError, PanelAppInstallError, PanelAppReviewChangedError, assertSafePanelAppId, installReviewedLocalPanelApp, installReviewedPanelAppUpdate, listInstalledPanelApps, panelAppInstallDir, panelAppsRegistryPath, panelAppsRoot, previewLocalPanelApp, previewInstalledPanelAppUpdate, isPanelAppBound, resolvePanelAppBindingPolicy, resolvePanelAppBindingProjectPath, uninstallPanelApp, type InstalledPanelApp, type InstalledPanelAppSource, type GitPanelAppSourceInput, type LocalPanelAppSourceInput, type PanelAppSourceInput, type PanelAppManifestData, type PanelAppBindingPolicy, type PanelAppPreview, } from "./panel-apps/index.js";
101
+ export { PANEL_APP_ICONS, PANEL_APP_MANIFEST_FILE, PANEL_APP_PERMISSIONS, PanelAppManifest, PanelAppAlreadyInstalledError, PanelAppInstallError, PanelAppReviewChangedError, assertSafePanelAppId, discoverGitPanelApps, installReviewedLocalPanelApp, installReviewedPanelAppUpdate, listInstalledPanelApps, panelAppInstallDir, panelAppsRegistryPath, panelAppsRoot, previewLocalPanelApp, previewInstalledPanelAppUpdate, isPanelAppBound, resolvePanelAppBindingPolicy, resolvePanelAppBindingProjectPath, uninstallPanelApp, type InstalledPanelApp, type InstalledPanelAppSource, type GitPanelAppDiscovery, type GitPanelAppDiscoveryCandidate, type GitPanelAppDiscoveryIssue, type GitPanelAppSourceInput, type LocalPanelAppSourceInput, type PanelAppSourceInput, type PanelAppManifestData, type PanelAppBindingPolicy, type PanelAppPreview, } from "./panel-apps/index.js";
102
102
  export { previewLocalTheme, installReviewedLocalTheme, listInstalledThemes, uninstallTheme, themesRoot, themeInstallDir, assertSafeThemeName, ThemeInstallError, ThemeReviewChangedError, THEME_ASSET_DIR, THEME_VAR_NAMES, detectThemeImage, type ThemePreview, type InstalledTheme, type ThemeManifest, } from "./themes/index.js";
103
103
  export { resolveSafePluginPath } from "./plugins/pluginInstaller.js";
104
104
  export { listPluginHooks, pluginHookKey, type PluginHookEntry } from "./plugins/loadPluginHooks.js";
@@ -26,7 +26,7 @@ export type { ExposureRationale } from "./tool-system/external-tool-exposure.js"
26
26
  export { webSearchTool } from "./tool-system/builtin/web-search.js";
27
27
  export { webFetchTool } from "./tool-system/builtin/web-fetch.js";
28
28
  export { extractJSON, extractJSONArray } from "./utils/json.js";
29
- export type { ExtensionQueryHandler, ExtensionTool, } from "./tool-system/capability-module.js";
29
+ export type { ExtensionQueryHandler, ExtensionTool } from "./tool-system/capability-module.js";
30
30
  export type { SessionWorkspace } from "./types.js";
31
31
  export type { CapabilityArtifactDetector, CapabilityDynamicContextProvider, CapabilityToolServiceHost, } from "./capabilities/index.js";
32
32
  export type { AgentModule, AgentEngineContributions, AgentProtocolContributions, AgentModuleToolContribution, ResolvedComposition, } from "./composition/types.js";
@@ -7,6 +7,7 @@
7
7
  */
8
8
  export { getGraphemeSegmenter, firstGrapheme, lastGrapheme, getWordSegmenter, getRelativeTimeFormat, getTimeZone, getSystemLocaleLanguage, } from "./utils/intl.js";
9
9
  export { env } from "./utils/env.js";
10
+ export { canonicalKey, canonicalPath, computeWorkspaceRootsDigest, createWorkspaceContext, legacySingleRootWorkspace, validateWorkspaceContext, workspacePrimaryRoot, type ProjectId, type ProjectRootContext, type ProjectRootId, type WorkspaceContext, type WorkspaceContextInput, } from "./workspace/workspace-context.js";
10
11
  export { default as sliceAnsi } from "./utils/sliceAnsi.js";
11
12
  export { execFileNoThrow } from "./utils/execFileNoThrow.js";
12
13
  export { findExecutable, resolveExecutable, setGitPathOverride, resolveGit, isGitAvailable, resolveGitPath, } from "./utils/exec.js";
@@ -26,7 +27,7 @@ export { recordUIEvent } from "./logging/session-recorder.js";
26
27
  export { getInteractiveApprovalBackend } from "./tool-system/permission.js";
27
28
  export { defaultSandboxConfig, type SandboxConfig } from "./tool-system/sandbox/index.js";
28
29
  export { buildNotificationMessage, buildNotificationSummary, notificationQueue, agentNotificationBus, notificationItemToStreamEvent, type NotificationItem, } from "./tool-system/builtin/agent-notifications.js";
29
- export type { BackgroundAgentCompletedEvent } from "./types.js";
30
+ export { backgroundJobRegistry } from "./tool-system/builtin/background-jobs.js";
30
31
  export { startAutomation, type StartAutomationDeps, type AutomationHandle, CronScheduler, cronScheduler, type CronExecutionOutcome, type CronJob, type CronJobLifecycleEvent, type CronPermissionLevel, type CronTemplateSource, type CreateJobOptions, type UpdateJobPatch, CronStore, defaultCronStorePath, bindCronToEngine, bindCronToRunManager, type CronRunner, type CronRunRequest, type CronRunResult, type RunSubmitter, isCronExpression, parseCronExpression, nextCronTime, validateSchedule, type ParsedCron, resolveWritePolicy, wrapUntrustedInput, type WritePolicy, runWriteJobInWorktree, type WriteJobGitOps, type RunWriteJobInput, type RunWriteJobResult, } from "./automation/index.js";
31
32
  export { asyncAgentRegistry, type AsyncAgentEntry } from "./tool-system/builtin/agent-registry.js";
32
33
  export { backgroundShellManager, BackgroundShellManager, type BgShell, type BgShellStatus, } from "./runtime/background-shell.js";
@@ -45,7 +46,7 @@ export { capabilitiesFor, type Capability } from "./llm/capabilities/index.js";
45
46
  export { reasoningControlFor, type ReasoningControl, } from "./llm/capabilities/reasoning-control.js";
46
47
  export { REASONING_EFFORTS, type ReasoningSetting } from "./llm/reasoning-setting.js";
47
48
  export { type ProviderConfig } from "./llm/provider-catalog.js";
48
- export type { ApprovalRequest, ApprovalResult, ApprovalScope, TaskInfo } from "./types.js";
49
+ export type { ApprovalRequest, ApprovalResult, ApprovalScope, BackgroundAgentCompletedEvent, TaskInfo, } from "./types.js";
49
50
  export { fileCache } from "./tool-system/builtin/file-cache.js";
50
51
  export { validateToolArgs } from "./tool-system/validation.js";
51
52
  export { createOffBackend } from "./tool-system/sandbox/off.js";
@@ -8,6 +8,7 @@
8
8
  // ─── Utils (shared primitives used by TUI) ───────────────────────
9
9
  export { getGraphemeSegmenter, firstGrapheme, lastGrapheme, getWordSegmenter, getRelativeTimeFormat, getTimeZone, getSystemLocaleLanguage, } from "./utils/intl.js";
10
10
  export { env } from "./utils/env.js";
11
+ export { canonicalKey, canonicalPath, computeWorkspaceRootsDigest, createWorkspaceContext, legacySingleRootWorkspace, validateWorkspaceContext, workspacePrimaryRoot, } from "./workspace/workspace-context.js";
11
12
  export { default as sliceAnsi } from "./utils/sliceAnsi.js";
12
13
  export { execFileNoThrow } from "./utils/execFileNoThrow.js";
13
14
  export { findExecutable, resolveExecutable, setGitPathOverride, resolveGit, isGitAvailable, resolveGitPath, } from "./utils/exec.js";
@@ -29,6 +30,7 @@ export { recordUIEvent } from "./logging/session-recorder.js";
29
30
  export { getInteractiveApprovalBackend } from "./tool-system/permission.js";
30
31
  export { defaultSandboxConfig } from "./tool-system/sandbox/index.js";
31
32
  export { buildNotificationMessage, buildNotificationSummary, notificationQueue, agentNotificationBus, notificationItemToStreamEvent, } from "./tool-system/builtin/agent-notifications.js";
33
+ export { backgroundJobRegistry } from "./tool-system/builtin/background-jobs.js";
32
34
  export { startAutomation, CronScheduler, cronScheduler, CronStore, defaultCronStorePath, bindCronToEngine, bindCronToRunManager, isCronExpression, parseCronExpression, nextCronTime, validateSchedule, resolveWritePolicy, wrapUntrustedInput, runWriteJobInWorktree, } from "./automation/index.js";
33
35
  export { asyncAgentRegistry } from "./tool-system/builtin/agent-registry.js";
34
36
  export { backgroundShellManager, BackgroundShellManager, } from "./runtime/background-shell.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.13";
6
+ export const VERSION = "0.9.0";
7
7
  // ─── Exceptions ──────────────────────────────────────────────────
8
8
  export { FrameworkError, LLMError, LLMRateLimitError, ContextLimitError, ToolError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, PermissionDeniedError, SessionError, TranscriptError, ConfigError, CompositionError, SandboxUnavailableError, } from "./exceptions.js";
9
9
  // ─── Composition (AgentModule / ResolvedComposition) ─────────────
@@ -82,7 +82,7 @@ export { mergePluginMcpServers, readPluginMcp } from "./plugins/installer/loadPl
82
82
  export { approvePluginMcp, listPluginMcpTrust, revokePluginMcp, } from "./plugins/pluginMcpApproval.js";
83
83
  export { pluginMcpApprovalState, } from "./plugins/pluginMcpIntegrity.js";
84
84
  export { pluginsRoot } from "./plugins/installer/paths.js";
85
- export { PANEL_APP_ICONS, PANEL_APP_MANIFEST_FILE, PANEL_APP_PERMISSIONS, PanelAppManifest, PanelAppAlreadyInstalledError, PanelAppInstallError, PanelAppReviewChangedError, assertSafePanelAppId, installReviewedLocalPanelApp, installReviewedPanelAppUpdate, listInstalledPanelApps, panelAppInstallDir, panelAppsRegistryPath, panelAppsRoot, previewLocalPanelApp, previewInstalledPanelAppUpdate, isPanelAppBound, resolvePanelAppBindingPolicy, resolvePanelAppBindingProjectPath, uninstallPanelApp, } from "./panel-apps/index.js";
85
+ export { PANEL_APP_ICONS, PANEL_APP_MANIFEST_FILE, PANEL_APP_PERMISSIONS, PanelAppManifest, PanelAppAlreadyInstalledError, PanelAppInstallError, PanelAppReviewChangedError, assertSafePanelAppId, discoverGitPanelApps, installReviewedLocalPanelApp, installReviewedPanelAppUpdate, listInstalledPanelApps, panelAppInstallDir, panelAppsRegistryPath, panelAppsRoot, previewLocalPanelApp, previewInstalledPanelAppUpdate, isPanelAppBound, resolvePanelAppBindingPolicy, resolvePanelAppBindingProjectPath, uninstallPanelApp, } from "./panel-apps/index.js";
86
86
  export { previewLocalTheme, installReviewedLocalTheme, listInstalledThemes, uninstallTheme, themesRoot, themeInstallDir, assertSafeThemeName, ThemeInstallError, ThemeReviewChangedError, THEME_ASSET_DIR, THEME_VAR_NAMES, detectThemeImage, } from "./themes/index.js";
87
87
  export { resolveSafePluginPath } from "./plugins/pluginInstaller.js";
88
88
  export { listPluginHooks, pluginHookKey } from "./plugins/loadPluginHooks.js";
@@ -12,6 +12,7 @@ export declare abstract class LLMClientBase {
12
12
  readonly timeout: number;
13
13
  readonly retryMaxAttempts: number;
14
14
  readonly imageDetail?: ClientDefaults["imageDetail"];
15
+ protected readonly fetch?: ClientDefaults["fetch"];
15
16
  /**
16
17
  * Process-wide hook fired on every LLM response. The CLI installs this in
17
18
  * main.ts to feed the cost tracker; lives on the base class so every code
@@ -23,7 +24,7 @@ export declare abstract class LLMClientBase {
23
24
  /**
24
25
  * `config` carries model identity (provider/model/apiKey/baseUrl/maxTokens/
25
26
  * thinking/providerKind). `defaults` carries cross-model runtime knobs
26
- * (temperature/timeout/retryMaxAttempts/imageDetail) — those are owned by
27
+ * (temperature/timeout/retryMaxAttempts/imageDetail/fetch) — those are owned by
27
28
  * the Engine and stay stable across hot model switches.
28
29
  */
29
30
  constructor(config: LLMConfig, defaults?: ClientDefaults);
@@ -23,6 +23,7 @@ export class LLMClientBase {
23
23
  timeout;
24
24
  retryMaxAttempts;
25
25
  imageDetail;
26
+ fetch;
26
27
  /**
27
28
  * Process-wide hook fired on every LLM response. The CLI installs this in
28
29
  * main.ts to feed the cost tracker; lives on the base class so every code
@@ -34,7 +35,7 @@ export class LLMClientBase {
34
35
  /**
35
36
  * `config` carries model identity (provider/model/apiKey/baseUrl/maxTokens/
36
37
  * thinking/providerKind). `defaults` carries cross-model runtime knobs
37
- * (temperature/timeout/retryMaxAttempts/imageDetail) — those are owned by
38
+ * (temperature/timeout/retryMaxAttempts/imageDetail/fetch) — those are owned by
38
39
  * the Engine and stay stable across hot model switches.
39
40
  */
40
41
  constructor(config, defaults) {
@@ -51,6 +52,7 @@ export class LLMClientBase {
51
52
  this.timeout = defaults?.timeout ?? 120_000;
52
53
  this.retryMaxAttempts = defaults?.retryMaxAttempts ?? 3;
53
54
  this.imageDetail = defaults?.imageDetail;
55
+ this.fetch = defaults?.fetch;
54
56
  this.initClient();
55
57
  }
56
58
  recordUsage(usage, options) {
@@ -21,6 +21,25 @@ const ANTHROPIC_FALLBACK_MAX_TOKENS = 4096;
21
21
  * no explicit token budget was given. Clamped up to the model's minimum.
22
22
  */
23
23
  const ANTHROPIC_DEFAULT_THINKING_BUDGET = 4096;
24
+ /**
25
+ * Anthropic reports uncached input, cache writes, and cache reads as three
26
+ * disjoint counters. `TokenUsage.promptTokens` is the whole prompt throughout
27
+ * CodeShell (OpenAI includes cached tokens in prompt_tokens), so normalize the
28
+ * Anthropic shape before context accounting, compaction, cache-hit math, and
29
+ * cost tracking consume it.
30
+ */
31
+ function tokenUsageFromAnthropic(usage) {
32
+ const cacheReadTokens = usage.cache_read_input_tokens ?? 0;
33
+ const cacheCreationTokens = usage.cache_creation_input_tokens ?? 0;
34
+ const promptTokens = usage.input_tokens + cacheReadTokens + cacheCreationTokens;
35
+ return {
36
+ promptTokens,
37
+ completionTokens: usage.output_tokens,
38
+ totalTokens: promptTokens + usage.output_tokens,
39
+ ...(usage.cache_read_input_tokens != null ? { cacheReadTokens } : {}),
40
+ ...(usage.cache_creation_input_tokens != null ? { cacheCreationTokens } : {}),
41
+ };
42
+ }
24
43
  export class AnthropicClient extends LLMClientBase {
25
44
  _client = null;
26
45
  constructor(config, defaults) {
@@ -37,6 +56,7 @@ export class AnthropicClient extends LLMClientBase {
37
56
  apiKey: resolveApiKey(this.config, process.env.ANTHROPIC_API_KEY),
38
57
  ...(this.config.baseUrl ? { baseURL: this.config.baseUrl } : {}),
39
58
  ...(Object.keys(headers).length > 0 ? { defaultHeaders: headers } : {}),
59
+ ...(this.fetch ? { fetch: this.fetch } : {}),
40
60
  timeout: this.timeout,
41
61
  });
42
62
  }
@@ -106,9 +126,7 @@ export class AnthropicClient extends LLMClientBase {
106
126
  if (ceiling < min) {
107
127
  return undefined;
108
128
  }
109
- let budget = reasoning.mode === "budget"
110
- ? reasoning.budgetTokens
111
- : ANTHROPIC_DEFAULT_THINKING_BUDGET; // "on" or "effort" → default budget
129
+ let budget = reasoning.mode === "budget" ? reasoning.budgetTokens : ANTHROPIC_DEFAULT_THINKING_BUDGET; // "on" or "effort" → default budget
112
130
  // Clamp into [min, ceiling] — both bounds are now guaranteed ≥ min.
113
131
  budget = Math.min(Math.max(budget, min), ceiling);
114
132
  return { type: "enabled", budget_tokens: budget };
@@ -172,13 +190,7 @@ export class AnthropicClient extends LLMClientBase {
172
190
  ? { temperature: options.temperature }
173
191
  : { temperature: this.temperature }),
174
192
  }, { signal: requestSignal ?? options.signal });
175
- const usage = {
176
- promptTokens: response.usage.input_tokens,
177
- completionTokens: response.usage.output_tokens,
178
- totalTokens: response.usage.input_tokens + response.usage.output_tokens,
179
- cacheReadTokens: response.usage.cache_read_input_tokens,
180
- cacheCreationTokens: response.usage.cache_creation_input_tokens,
181
- };
193
+ const usage = tokenUsageFromAnthropic(response.usage);
182
194
  this.recordUsage(usage, options);
183
195
  return this.processResponse(response, usage);
184
196
  }
@@ -209,10 +221,8 @@ export class AnthropicClient extends LLMClientBase {
209
221
  ? { temperature: options.temperature }
210
222
  : { temperature: this.temperature }),
211
223
  }, { signal: requestSignal ?? options.signal });
212
- let currentText = "";
213
224
  let currentToolName = "";
214
225
  let currentToolId = "";
215
- let currentToolInput = "";
216
226
  // Abort-guarded emit: once the turn is cancelled, stop forwarding chunks
217
227
  // to the UI. The SDK's event emitter can keep firing buffered text/
218
228
  // contentBlock/inputJson events after abort() until its HTTP stream tears
@@ -249,14 +259,12 @@ export class AnthropicClient extends LLMClientBase {
249
259
  ttft_ms: Date.now() - streamStartedAt,
250
260
  });
251
261
  }
252
- currentText += text;
253
262
  emit({ type: "text", text, tokens: countTokens(text) });
254
263
  });
255
264
  stream.on("contentBlock", (block) => {
256
265
  if (block.type === "tool_use") {
257
266
  currentToolName = block.name;
258
267
  currentToolId = block.id;
259
- currentToolInput = "";
260
268
  emit({
261
269
  type: "tool_use_start",
262
270
  toolCall: { id: block.id, toolName: block.name, args: {} },
@@ -264,7 +272,6 @@ export class AnthropicClient extends LLMClientBase {
264
272
  }
265
273
  });
266
274
  stream.on("inputJson", (_delta, snapshot) => {
267
- currentToolInput = JSON.stringify(snapshot);
268
275
  if (currentToolId) {
269
276
  emit({
270
277
  type: "tool_use_delta",
@@ -277,13 +284,7 @@ export class AnthropicClient extends LLMClientBase {
277
284
  }
278
285
  });
279
286
  const finalMessage = await stream.finalMessage();
280
- const usage = {
281
- promptTokens: finalMessage.usage.input_tokens,
282
- completionTokens: finalMessage.usage.output_tokens,
283
- totalTokens: finalMessage.usage.input_tokens + finalMessage.usage.output_tokens,
284
- cacheReadTokens: finalMessage.usage.cache_read_input_tokens,
285
- cacheCreationTokens: finalMessage.usage.cache_creation_input_tokens,
286
- };
287
+ const usage = tokenUsageFromAnthropic(finalMessage.usage);
287
288
  this.recordUsage(usage, options);
288
289
  options.onChunk?.({ type: "stop", stopReason: finalMessage.stop_reason ?? undefined });
289
290
  return this.processResponse(finalMessage, usage);
@@ -415,9 +416,7 @@ export class AnthropicClient extends LLMClientBase {
415
416
  // marking them would 400. buildMessages never emits them today (thinking
416
417
  // is a top-level request field, not history content), but guard anyway
417
418
  // so a future block type can't silently break the request.
418
- if (lastBlock &&
419
- lastBlock.type !== "thinking" &&
420
- lastBlock.type !== "redacted_thinking") {
419
+ if (lastBlock && lastBlock.type !== "thinking" && lastBlock.type !== "redacted_thinking") {
421
420
  lastBlock.cache_control = { type: "ephemeral" };
422
421
  }
423
422
  }
@@ -49,9 +49,12 @@ interface RunStreamOpts {
49
49
  export declare function runStreamWithWatchdog<T = any>(stream: AsyncIterable<T>, opts?: RunStreamOpts): Promise<string>;
50
50
  export declare class OpenAIClient extends LLMClientBase {
51
51
  private _client;
52
+ private readonly dangerouslyAllowBrowser;
52
53
  private _forceMaxCompletionTokens;
53
54
  private _dropReasoningEffort;
54
- constructor(config: LLMConfig, defaults?: ClientDefaults);
55
+ constructor(config: LLMConfig, defaults?: ClientDefaults, runtimeOptions?: {
56
+ dangerouslyAllowBrowser?: boolean;
57
+ });
55
58
  protected initClient(): void;
56
59
  private get client();
57
60
  /**