@cjhyy/code-shell-core 0.6.0-rc.9 → 0.7.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/THIRD_PARTY_NOTICES.md +206 -0
  2. package/dist/automation/scheduler.d.ts +13 -7
  3. package/dist/automation/scheduler.js +116 -37
  4. package/dist/capability-control/service.d.ts +2 -0
  5. package/dist/capability-control/service.js +4 -2
  6. package/dist/cc-orchestrator/agent-adapter.d.ts +2 -0
  7. package/dist/cc-orchestrator/agent-adapter.js +7 -1
  8. package/dist/cc-orchestrator/codex-session-history.js +1 -1
  9. package/dist/cc-orchestrator/cwd-normalize.d.ts +2 -0
  10. package/dist/cc-orchestrator/cwd-normalize.js +19 -0
  11. package/dist/cc-orchestrator/external-agent-bindings.d.ts +27 -0
  12. package/dist/cc-orchestrator/external-agent-bindings.js +150 -0
  13. package/dist/cc-orchestrator/external-agent-driver.d.ts +1 -0
  14. package/dist/cc-orchestrator/external-agent-driver.js +102 -51
  15. package/dist/cc-orchestrator/external-agent-session-store.d.ts +23 -0
  16. package/dist/cc-orchestrator/external-agent-session-store.js +146 -0
  17. package/dist/cc-orchestrator/session-history.js +2 -2
  18. package/dist/cli/agent-server-stdio.js +9 -2
  19. package/dist/context/compaction.d.ts +8 -1
  20. package/dist/context/compaction.js +49 -4
  21. package/dist/context/manager.d.ts +16 -2
  22. package/dist/context/manager.js +103 -19
  23. package/dist/credentials/access.d.ts +56 -0
  24. package/dist/credentials/access.js +183 -0
  25. package/dist/credentials/index.d.ts +1 -0
  26. package/dist/credentials/index.js +1 -0
  27. package/dist/credentials/inject-credential-tool.d.ts +3 -1
  28. package/dist/credentials/inject-credential-tool.js +30 -11
  29. package/dist/credentials/types.d.ts +2 -2
  30. package/dist/credentials/use-credential-tool.d.ts +9 -1
  31. package/dist/credentials/use-credential-tool.js +58 -45
  32. package/dist/engine/engine.d.ts +17 -1
  33. package/dist/engine/engine.js +247 -89
  34. package/dist/engine/image-policy.d.ts +6 -0
  35. package/dist/engine/image-policy.js +17 -6
  36. package/dist/engine/input-attachments.d.ts +13 -0
  37. package/dist/engine/input-attachments.js +255 -0
  38. package/dist/engine/model-facade.d.ts +5 -2
  39. package/dist/engine/model-facade.js +4 -4
  40. package/dist/engine/parse-task.d.ts +10 -0
  41. package/dist/engine/parse-task.js +5 -0
  42. package/dist/engine/streaming-tool-queue.d.ts +11 -7
  43. package/dist/engine/streaming-tool-queue.js +11 -7
  44. package/dist/engine/turn-loop.d.ts +7 -1
  45. package/dist/engine/turn-loop.js +117 -27
  46. package/dist/engine/types.d.ts +8 -0
  47. package/dist/git/worktree/crud.d.ts +69 -0
  48. package/dist/git/worktree/crud.js +206 -0
  49. package/dist/git/worktree/diff.d.ts +14 -0
  50. package/dist/git/worktree/diff.js +82 -0
  51. package/dist/git/worktree/git-exec.d.ts +7 -0
  52. package/dist/git/worktree/git-exec.js +51 -0
  53. package/dist/git/worktree/index.d.ts +5 -0
  54. package/dist/git/worktree/index.js +5 -0
  55. package/dist/git/worktree/query.d.ts +42 -0
  56. package/dist/git/worktree/query.js +121 -0
  57. package/dist/git/worktree/slug.d.ts +11 -0
  58. package/dist/git/worktree/slug.js +58 -0
  59. package/dist/git/worktree.d.ts +1 -84
  60. package/dist/git/worktree.js +5 -230
  61. package/dist/index.d.ts +31 -28
  62. package/dist/index.js +27 -25
  63. package/dist/logging/logger.js +6 -6
  64. package/dist/logging/sanitize-messages.d.ts +10 -2
  65. package/dist/logging/sanitize-messages.js +21 -6
  66. package/dist/plugins/installer/checkUpdate.d.ts +4 -1
  67. package/dist/plugins/installer/checkUpdate.js +4 -2
  68. package/dist/plugins/installer/install.js +2 -0
  69. package/dist/plugins/installer/installFromSource.js +9 -1
  70. package/dist/plugins/installer/parseSource.d.ts +4 -1
  71. package/dist/plugins/installer/parseSource.js +28 -10
  72. package/dist/plugins/installer/sourcePath.d.ts +9 -0
  73. package/dist/plugins/installer/sourcePath.js +50 -0
  74. package/dist/plugins/installer/update.d.ts +4 -1
  75. package/dist/plugins/installer/update.js +5 -3
  76. package/dist/plugins/parseMarketplaceInput.d.ts +4 -1
  77. package/dist/plugins/parseMarketplaceInput.js +4 -3
  78. package/dist/plugins/pluginInstaller.js +24 -22
  79. package/dist/preset/index.d.ts +1 -0
  80. package/dist/preset/index.js +16 -9
  81. package/dist/prompt/sections/base.md +1 -1
  82. package/dist/protocol/chat-session-manager.d.ts +2 -0
  83. package/dist/protocol/chat-session-manager.js +38 -2
  84. package/dist/protocol/chat-session.d.ts +3 -0
  85. package/dist/protocol/chat-session.js +1 -0
  86. package/dist/protocol/client.d.ts +9 -4
  87. package/dist/protocol/client.js +18 -1
  88. package/dist/protocol/server.d.ts +30 -0
  89. package/dist/protocol/server.js +246 -46
  90. package/dist/protocol/types.d.ts +47 -0
  91. package/dist/protocol/types.js +6 -0
  92. package/dist/run/FileRunStore.js +10 -1
  93. package/dist/run/Heartbeat.js +12 -0
  94. package/dist/run/RunApprovalBackend.d.ts +3 -0
  95. package/dist/run/RunApprovalBackend.js +41 -6
  96. package/dist/run/RunLock.js +2 -0
  97. package/dist/run/RunManager.d.ts +2 -0
  98. package/dist/run/RunManager.js +64 -24
  99. package/dist/run/ids.d.ts +2 -0
  100. package/dist/run/ids.js +23 -0
  101. package/dist/runtime/background-shell.d.ts +1 -0
  102. package/dist/runtime/background-shell.js +23 -13
  103. package/dist/runtime/spawn-common.js +10 -0
  104. package/dist/services/auto-dream.d.ts +15 -4
  105. package/dist/services/auto-dream.js +20 -20
  106. package/dist/services/dream-consolidation.d.ts +2 -3
  107. package/dist/services/dream-consolidation.js +65 -15
  108. package/dist/services/extract-memories.d.ts +14 -5
  109. package/dist/services/extract-memories.js +20 -3
  110. package/dist/services/global-dream-promotion.d.ts +23 -0
  111. package/dist/services/global-dream-promotion.js +112 -0
  112. package/dist/services/memory-orchestrator.js +347 -34
  113. package/dist/session/memory.d.ts +61 -18
  114. package/dist/session/memory.js +342 -79
  115. package/dist/session/session-manager.d.ts +35 -1
  116. package/dist/session/session-manager.js +190 -3
  117. package/dist/session/transcript.d.ts +1 -1
  118. package/dist/session/transcript.js +17 -5
  119. package/dist/settings/manager.d.ts +1 -0
  120. package/dist/settings/manager.js +87 -37
  121. package/dist/settings/schema-export.d.ts +2 -3
  122. package/dist/settings/schema-export.js +2 -3
  123. package/dist/settings/schema.d.ts +21 -0
  124. package/dist/settings/schema.js +12 -0
  125. package/dist/skills/scanner.d.ts +3 -2
  126. package/dist/skills/scanner.js +12 -9
  127. package/dist/tool-system/builtin/background-jobs.d.ts +10 -1
  128. package/dist/tool-system/builtin/background-jobs.js +12 -1
  129. package/dist/tool-system/builtin/background-work.d.ts +17 -18
  130. package/dist/tool-system/builtin/background-work.js +51 -5
  131. package/dist/tool-system/builtin/config.d.ts +2 -1
  132. package/dist/tool-system/builtin/config.js +16 -11
  133. package/dist/tool-system/builtin/drive-claude-code.d.ts +16 -2
  134. package/dist/tool-system/builtin/drive-claude-code.js +301 -47
  135. package/dist/tool-system/builtin/edit.js +5 -2
  136. package/dist/tool-system/builtin/generate-video.d.ts +1 -0
  137. package/dist/tool-system/builtin/generate-video.js +13 -4
  138. package/dist/tool-system/builtin/index.d.ts +8 -3
  139. package/dist/tool-system/builtin/index.js +37 -21
  140. package/dist/tool-system/builtin/lsp.d.ts +2 -1
  141. package/dist/tool-system/builtin/lsp.js +6 -3
  142. package/dist/tool-system/builtin/memory.js +40 -8
  143. package/dist/tool-system/builtin/notebook-edit.js +5 -2
  144. package/dist/tool-system/builtin/powershell.d.ts +5 -2
  145. package/dist/tool-system/builtin/powershell.js +11 -7
  146. package/dist/tool-system/builtin/read.js +118 -6
  147. package/dist/tool-system/builtin/view-image.d.ts +2 -2
  148. package/dist/tool-system/builtin/view-image.js +109 -19
  149. package/dist/tool-system/builtin/worktree.d.ts +4 -4
  150. package/dist/tool-system/builtin/worktree.js +297 -74
  151. package/dist/tool-system/builtin/write.js +5 -3
  152. package/dist/tool-system/context.d.ts +23 -1
  153. package/dist/tool-system/executor.d.ts +1 -5
  154. package/dist/tool-system/executor.js +94 -115
  155. package/dist/tool-system/mcp-manager.d.ts +18 -5
  156. package/dist/tool-system/mcp-manager.js +140 -74
  157. package/dist/tool-system/path-policy.d.ts +2 -0
  158. package/dist/tool-system/path-policy.js +41 -12
  159. package/dist/tool-system/permission.d.ts +28 -7
  160. package/dist/tool-system/permission.js +130 -49
  161. package/dist/tool-system/registry.js +11 -4
  162. package/dist/tool-system/tool-result-redaction.d.ts +7 -0
  163. package/dist/tool-system/tool-result-redaction.js +48 -0
  164. package/dist/tool-system/workspace-bridge.d.ts +11 -0
  165. package/dist/tool-system/workspace-bridge.js +1 -0
  166. package/dist/types.d.ts +55 -4
  167. package/dist/utils/toolDisplay.js +1 -1
  168. package/package.json +4 -3
@@ -10,9 +10,9 @@
10
10
  * 跨进程:实际的 restoreCookiesToBrowser 在 desktop main(core 够不到 Electron
11
11
  * session),经 `ctx.injectCredentialToBrowser` 回调(宿主注入,镜像 askUser)触发。
12
12
  */
13
- import { CredentialStore } from "./store.js";
14
13
  import { credentialUseGate, } from "./use-gate.js";
15
14
  import { SettingsManager } from "../settings/manager.js";
15
+ import { credentialAccessScope, getCredentialAccess } from "./access.js";
16
16
  const TOOL_NAME = "InjectCredential";
17
17
  const BASE_DESCRIPTION = "Inject a stored COOKIE credential into the built-in browser to restore its " +
18
18
  "login state, so you can then drive the page as that logged-in account with the " +
@@ -62,9 +62,14 @@ function sessionAllowFor(ctx) {
62
62
  }
63
63
  return set;
64
64
  }
65
- function readAutoApprove(cwd) {
65
+ // CredentialStore only distinguishes full user+project from project-only.
66
+ // Isolated engines must be at least as restrictive as project-scoped engines.
67
+ function credentialScope(scope) {
68
+ return credentialAccessScope(scope);
69
+ }
70
+ function readAutoApprove(cwd, scope) {
66
71
  try {
67
- const s = new SettingsManager(cwd, "full").get();
72
+ const s = new SettingsManager(cwd, scope === "full" ? "full" : "project").get();
68
73
  return s.credentialUse?.autoApprove === true;
69
74
  }
70
75
  catch {
@@ -72,9 +77,11 @@ function readAutoApprove(cwd) {
72
77
  }
73
78
  }
74
79
  /** 该工具仅在有 cookie 凭证 且 宿主接了注入回调时才可见(BUILTIN_TOOL_GUARDS)。 */
75
- export function isInjectCredentialAvailable(cwd) {
80
+ export function isInjectCredentialAvailable(cwd, settingsScope) {
76
81
  try {
77
- return new CredentialStore(cwd).listMasked().some((c) => c.type === "cookie");
82
+ return getCredentialAccess()
83
+ .listMasked(cwd, credentialScope(settingsScope))
84
+ .some((c) => c.type === "cookie");
78
85
  }
79
86
  catch {
80
87
  return false;
@@ -82,10 +89,14 @@ export function isInjectCredentialAvailable(cwd) {
82
89
  }
83
90
  export async function injectCredentialTool(args, ctx) {
84
91
  const cwd = ctx?.cwd ?? process.cwd();
92
+ const scope = credentialScope(ctx?.settingsScope);
85
93
  const id = typeof args.id === "string" ? args.id.trim() : "";
86
94
  const purpose = typeof args.purpose === "string" ? args.purpose : undefined;
87
95
  if (!id) {
88
- return json({ kind: "error", error: "缺少 id。先用 UseCredential(无参)列出凭证,再用 cookie 凭证的 id 调本工具。" });
96
+ return json({
97
+ kind: "error",
98
+ error: "缺少 id。先用 UseCredential(无参)列出凭证,再用 cookie 凭证的 id 调本工具。",
99
+ });
89
100
  }
90
101
  if (!ctx?.injectCredentialToBrowser) {
91
102
  return json({
@@ -93,17 +104,22 @@ export async function injectCredentialTool(args, ctx) {
93
104
  error: "当前环境无内置浏览器(headless/无面板),无法注入。请改用 UseCredential 取 cookie 走 HTTP 请求。",
94
105
  });
95
106
  }
96
- const cred = new CredentialStore(cwd).resolve(id);
107
+ const cred = getCredentialAccess().resolveMeta(cwd, id, scope);
97
108
  if (!cred) {
98
- return json({ kind: "error", error: `凭证不存在: "${id}"。调用 UseCredential(无参)可列出可用凭证。` });
109
+ return json({
110
+ kind: "error",
111
+ error: `凭证不存在: "${id}"。调用 UseCredential(无参)可列出可用凭证。`,
112
+ });
99
113
  }
100
114
  if (cred.type !== "cookie") {
101
115
  return json({ kind: "error", error: `凭证「${cred.label}」不是 cookie 类型,不能注入浏览器。` });
102
116
  }
103
117
  // 过门:复用三档,但用该凭证的 autoInjectByAI(不是 autoUseByAI)。
104
- const ask = ctx.askUser ? (q, opts) => ctx.askUser(q, opts) : undefined;
118
+ const ask = ctx.askUser
119
+ ? (q, opts) => ctx.askUser(q, opts)
120
+ : undefined;
105
121
  const decision = await credentialUseGate({ id: cred.id, label: cred.label, purpose }, {
106
- autoApprove: readAutoApprove(cwd),
122
+ autoApprove: readAutoApprove(cwd, scope),
107
123
  credentialAutoUse: cred.autoInjectByAI === true,
108
124
  sessionAllow: sessionAllowFor(ctx),
109
125
  ask,
@@ -115,7 +131,7 @@ export async function injectCredentialTool(args, ctx) {
115
131
  return json({ kind: "error", error: msg });
116
132
  }
117
133
  // 跨进程触发宿主注入。
118
- const res = await ctx.injectCredentialToBrowser(cred.id);
134
+ const res = await ctx.injectCredentialToBrowser(cred.id, scope);
119
135
  if (!res.ok) {
120
136
  return json({ kind: "error", error: res.error ?? "注入浏览器失败(宿主未返回成功)。" });
121
137
  }
@@ -128,3 +144,6 @@ function json(r) {
128
144
  export function __resetInjectCredentialSessionAllowForTests() {
129
145
  injectSessionAllowByEngine.clear();
130
146
  }
147
+ export function clearInjectCredentialSessionAllow(sessionId) {
148
+ injectSessionAllowByEngine.delete(sessionId);
149
+ }
@@ -39,8 +39,8 @@ export interface Credential {
39
39
  /**
40
40
  * link: 业务方 app 注册地址;cookie: 拓取所用平台与主域 + 抓取范围 + 切换策略。
41
41
  * - scope="all" 表示该 jar 是整分区全量抓的(切换时整包导回);缺省/"domain" = 仅该域。
42
- * - switchMode 决定「切换」时怎么写回浏览器:"clear"(默认)先清空整分区再注入(干净换号);
43
- * "merge" 只覆盖同名 cookie、保留分区里其他站(不踢掉别的登录态)。
42
+ * - switchMode 决定「切换」时怎么写回浏览器:"merge"(默认)只覆盖同名 cookie、保留
43
+ * 分区里其他站(不踢掉别的登录态);"clear" 先清空整分区再注入(干净换号,需显式选)。
44
44
  */
45
45
  meta?: {
46
46
  appUrl?: string;
@@ -7,7 +7,8 @@
7
7
  * - token/link → `{ kind: "value", value }`
8
8
  * - cookie → 就地写临时 cookies.txt(0600),返回 `{ kind: "cookie", cookiesFile, count }`
9
9
  *
10
- * 取值全部 core 直读 CredentialStore(cookie 值第二期已进库),无跨进程。
10
+ * desktop 下通过 host credential access IPC 按需解析 secret;headless/SDK
11
+ * 仍可使用本地 CredentialStore。
11
12
  * 集中在 core/src/credentials/ 下,只经 ToolDefinition 注册 + ToolContext.askUser 耦合 core,
12
13
  * 满足设计稿 §1「可整块外移」约束。
13
14
  */
@@ -25,5 +26,12 @@ export declare function useCredentialToolDefFor(cwd: string): ToolDefinition;
25
26
  */
26
27
  export declare function sweepStaleCredentialCookies(now?: number): void;
27
28
  export declare function useCredentialTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
29
+ export declare function useCredentialBuiltinTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string | {
30
+ result: string;
31
+ sensitive: true;
32
+ displayResult: string;
33
+ transcriptResult: string;
34
+ }>;
28
35
  /** 测试钩子:清空会话 allow 集(避免跨用例污染)。 */
29
36
  export declare function __resetCredentialSessionAllowForTests(): void;
37
+ export declare function clearCredentialSessionAllow(sessionId: string): void;
@@ -7,22 +7,17 @@
7
7
  * - token/link → `{ kind: "value", value }`
8
8
  * - cookie → 就地写临时 cookies.txt(0600),返回 `{ kind: "cookie", cookiesFile, count }`
9
9
  *
10
- * 取值全部 core 直读 CredentialStore(cookie 值第二期已进库),无跨进程。
10
+ * desktop 下通过 host credential access IPC 按需解析 secret;headless/SDK
11
+ * 仍可使用本地 CredentialStore。
11
12
  * 集中在 core/src/credentials/ 下,只经 ToolDefinition 注册 + ToolContext.askUser 耦合 core,
12
13
  * 满足设计稿 §1「可整块外移」约束。
13
14
  */
14
- import { writeFileSync, existsSync, readdirSync, statSync, rmSync } from "node:fs";
15
- import { join } from "node:path";
16
- import { tmpdir } from "node:os";
17
- import { randomUUID } from "node:crypto";
18
- import { CredentialStore } from "./store.js";
19
- import { formatNetscapeCookies, parseCookieJar } from "./cookie-jar.js";
15
+ import { SENSITIVE_TOOL_RESULT_PLACEHOLDER } from "../tool-system/tool-result-redaction.js";
20
16
  import { credentialUseGate, } from "./use-gate.js";
21
17
  import { SettingsManager } from "../settings/manager.js";
22
18
  import { logger } from "../logging/logger.js";
19
+ import { credentialAccessScope, getCredentialAccess, materializeCookieSecret, sweepStaleCredentialCookieFiles, } from "./access.js";
23
20
  const TOOL_NAME = "UseCredential";
24
- const COOKIE_FILE_PREFIX = "codeshell-cred-cookie-";
25
- const COOKIE_FILE_MAX_AGE_MS = 30 * 60 * 1000; // 30min 启动 sweep 上限
26
21
  const BASE_DESCRIPTION = "Use a stored credential (token / API key / login cookie) to run a command. " +
27
22
  "Call with NO arguments first to list available credentials (id + label + type); " +
28
23
  "then call again with `id` to fetch one. Token/link credentials return their secret " +
@@ -52,7 +47,7 @@ export const useCredentialToolDef = {
52
47
  */
53
48
  export function useCredentialToolDefFor(cwd) {
54
49
  try {
55
- const list = new CredentialStore(cwd).listMasked();
50
+ const list = getCredentialAccess().listMasked(cwd, "full");
56
51
  if (list.length === 0)
57
52
  return useCredentialToolDef;
58
53
  const names = list.map((c) => `${c.id} (${c.type})`).join(", ");
@@ -70,26 +65,7 @@ export function useCredentialToolDefFor(cwd) {
70
65
  * 不引入 lease 对象/定时器 —— 文件用完靠进程退出 + 这个轻量 sweep。
71
66
  */
72
67
  export function sweepStaleCredentialCookies(now = Date.now()) {
73
- const dir = tmpdir();
74
- try {
75
- if (!existsSync(dir))
76
- return;
77
- for (const f of readdirSync(dir)) {
78
- if (!f.startsWith(COOKIE_FILE_PREFIX))
79
- continue;
80
- const p = join(dir, f);
81
- try {
82
- if (now - statSync(p).mtimeMs > COOKIE_FILE_MAX_AGE_MS)
83
- rmSync(p, { force: true });
84
- }
85
- catch {
86
- /* skip */
87
- }
88
- }
89
- }
90
- catch {
91
- /* best-effort */
92
- }
68
+ sweepStaleCredentialCookieFiles(now);
93
69
  }
94
70
  /** 内存会话 allow 集:每个 Engine 一份(从 ctx.sessionId 键)。纯内存,关进程即忘。 */
95
71
  const sessionAllowByEngine = new Map();
@@ -112,7 +88,7 @@ function sessionAllowFor(ctx) {
112
88
  // (project-only). An "isolated" engine is at least as restrictive as project,
113
89
  // so it maps to "project" — it must never read the host user's ~/.code-shell.
114
90
  function credentialScope(scope) {
115
- return scope === "full" || scope === undefined ? "full" : "project";
91
+ return credentialAccessScope(scope);
116
92
  }
117
93
  function readAutoApprove(cwd, scope) {
118
94
  try {
@@ -128,19 +104,19 @@ function readAutoApprove(cwd, scope) {
128
104
  }
129
105
  export async function useCredentialTool(args, ctx) {
130
106
  const cwd = ctx?.cwd ?? process.cwd();
131
- const store = new CredentialStore(cwd);
107
+ const access = getCredentialAccess();
132
108
  const scope = credentialScope(ctx?.settingsScope);
133
109
  const id = typeof args.id === "string" ? args.id.trim() : "";
134
110
  const purpose = typeof args.purpose === "string" ? args.purpose : undefined;
135
111
  // 无 id → 清单(脱敏,权威实时源)。按 engine scope 过滤:project/isolated
136
112
  // 引擎不得列出宿主 user 层凭证。
137
113
  if (!id) {
138
- const credentials = store
139
- .listMasked(scope)
114
+ const credentials = access
115
+ .listMasked(cwd, scope)
140
116
  .map((c) => ({ id: c.id, label: c.label, type: c.type }));
141
117
  return json({ kind: "list", credentials });
142
118
  }
143
- const cred = store.resolve(id, scope);
119
+ const cred = access.resolveMeta(cwd, id, scope);
144
120
  if (!cred) {
145
121
  return json({ kind: "error", error: `凭证不存在: "${id}"。调用本工具(无参)可列出可用凭证。` });
146
122
  }
@@ -162,14 +138,20 @@ export async function useCredentialTool(args, ctx) {
162
138
  }
163
139
  // token/link → 直接返回值
164
140
  if (cred.type === "token" || cred.type === "link") {
165
- if (!cred.secret)
141
+ if (!cred.hasSecret || !access.resolveValue) {
142
+ return json({ kind: "error", error: `凭证「${cred.label}」没有可用的值。` });
143
+ }
144
+ try {
145
+ const value = await access.resolveValue({ cwd, id: cred.id, scope, purpose: "use" });
146
+ return json({ kind: "value", value });
147
+ }
148
+ catch {
166
149
  return json({ kind: "error", error: `凭证「${cred.label}」没有可用的值。` });
167
- return json({ kind: "value", value: cred.secret });
150
+ }
168
151
  }
169
152
  // cookie → 就地写临时 cookies.txt
170
153
  if (cred.type === "cookie") {
171
- const jar = parseCookieJar(cred.secret);
172
- if (jar.length === 0) {
154
+ if (!cred.hasSecret) {
173
155
  return json({
174
156
  kind: "error",
175
157
  error: `凭证「${cred.label}」的 cookie 为空或已失效,请在凭证页对该账号点「重拓」(重新登录后重新拓取)。`,
@@ -180,26 +162,57 @@ export async function useCredentialTool(args, ctx) {
180
162
  // same process — the second write would clobber the first and a caller could
181
163
  // read another account's cookies. A random component makes each file distinct.
182
164
  // Prefix is unchanged so the 30-min startup sweep still matches & cleans them.
183
- const file = join(tmpdir(), `${COOKIE_FILE_PREFIX}${safe(cred.id)}-${process.pid}-${randomUUID()}.txt`);
184
165
  try {
185
- writeFileSync(file, formatNetscapeCookies(jar), { mode: 0o600 });
166
+ const materialized = access.materializeCookie
167
+ ? await access.materializeCookie({ cwd, id: cred.id, scope })
168
+ : materializeCookieSecret(cred.id, await access.resolveValue({ cwd, id: cred.id, scope, purpose: "use" }));
169
+ return json({
170
+ kind: "cookie",
171
+ cookiesFile: materialized.cookiesFile,
172
+ count: materialized.count,
173
+ });
186
174
  }
187
175
  catch (e) {
176
+ if (String(e).includes("cookie jar is empty or invalid")) {
177
+ return json({
178
+ kind: "error",
179
+ error: `凭证「${cred.label}」的 cookie 为空或已失效,请在凭证页对该账号点「重拓」(重新登录后重新拓取)。`,
180
+ });
181
+ }
188
182
  logger.warn(`UseCredential: failed to write cookies.txt: ${String(e)}`);
189
183
  return json({ kind: "error", error: `写临时 cookie 文件失败: ${String(e)}` });
190
184
  }
191
- return json({ kind: "cookie", cookiesFile: file, count: jar.length });
192
185
  }
193
186
  return json({ kind: "error", error: `未知凭证类型: ${cred.type}` });
194
187
  }
195
- /** 文件名安全化(凭证 id 用了 `__`,这里只挡路径分隔符/控制字符) */
196
- function safe(s) {
197
- return s.replace(/[^a-zA-Z0-9_.-]/g, "_");
188
+ export async function useCredentialBuiltinTool(args, ctx) {
189
+ const result = await useCredentialTool(args, ctx);
190
+ if (!isValueResult(result))
191
+ return result;
192
+ const redacted = json({ kind: "value", value: SENSITIVE_TOOL_RESULT_PLACEHOLDER });
193
+ return {
194
+ result,
195
+ sensitive: true,
196
+ displayResult: redacted,
197
+ transcriptResult: redacted,
198
+ };
198
199
  }
199
200
  function json(r) {
200
201
  return JSON.stringify(r);
201
202
  }
203
+ function isValueResult(result) {
204
+ try {
205
+ const parsed = JSON.parse(result);
206
+ return parsed.kind === "value" && typeof parsed.value === "string";
207
+ }
208
+ catch {
209
+ return false;
210
+ }
211
+ }
202
212
  /** 测试钩子:清空会话 allow 集(避免跨用例污染)。 */
203
213
  export function __resetCredentialSessionAllowForTests() {
204
214
  sessionAllowByEngine.clear();
205
215
  }
216
+ export function clearCredentialSessionAllow(sessionId) {
217
+ sessionAllowByEngine.delete(sessionId);
218
+ }
@@ -12,9 +12,11 @@ import type { AskUserFn } from "../tool-system/builtin/ask-user.js";
12
12
  import type { CapabilityOverride } from "../settings/schema.js";
13
13
  import { type FeatureFlagName } from "../settings/feature-flags.js";
14
14
  import type { ToolContext } from "../tool-system/context.js";
15
+ import { type SandboxBackend } from "../tool-system/sandbox/index.js";
15
16
  import { ModelPool, type ModelEntry } from "../llm/model-pool.js";
16
17
  import { AgentDefinitionRegistry } from "../agent/agent-definition-registry.js";
17
18
  import { EngineRuntime } from "./runtime.js";
19
+ import type { InputAttachmentMeta } from "../protocol/types.js";
18
20
  /**
19
21
  * Build ScanOptions.compatFileNames from the user's instruction compat toggles.
20
22
  * Primary file name stays hard-wired to CODESHELL.md (not exposed). Turning a
@@ -53,7 +55,7 @@ export declare function resolveChildLlm(modelKey: string | undefined, pool: Mode
53
55
  * Names in `disabledAgents` are filtered out so the LLM never sees them.
54
56
  */
55
57
  /**
56
- * Resolve the working directory for a run. Precedence:
58
+ * Resolve the working directory for a run. Precedence for legacy sessions:
57
59
  * options.cwd > resumed session's state.cwd > config.cwd > process.cwd()
58
60
  *
59
61
  * The session-cwd tier is what stops a project-bound session from being
@@ -261,6 +263,8 @@ export declare class Engine {
261
263
  * clear "no browser panel" error.
262
264
  */
263
265
  setBrowserBridge(bridge: import("../tool-system/browser-bridge.js").BrowserBridge | undefined): void;
266
+ /** Inject the host-backed workspace bridge after construction. */
267
+ setWorkspaceBridge(bridge: import("../tool-system/workspace-bridge.js").WorkspaceBridge | undefined): void;
264
268
  /**
265
269
  * Queue a user message to be spliced into the in-flight run for `sessionId`
266
270
  * at the next turn-loop step boundary — the 不打断 steering path (vs cancel +
@@ -325,6 +329,8 @@ export declare class Engine {
325
329
  injected?: boolean;
326
330
  /** Stable id for this user-intent, used to make duplicate submits idempotent. */
327
331
  clientMessageId?: string;
332
+ /** Structured input attachments. Legacy `<codeshell-image>` blocks remain supported. */
333
+ attachments?: InputAttachmentMeta[];
328
334
  }): Promise<EngineResult>;
329
335
  /**
330
336
  * Run the end-of-session memory pipeline as a fire-and-forget background
@@ -460,6 +466,12 @@ export declare class Engine {
460
466
  * session with no active goal is a no-op returning false.
461
467
  */
462
468
  clearGoal(sessionId: string): boolean;
469
+ /**
470
+ * Reset a session's workspace pointer back to its main root. If the session is
471
+ * actively running, mutate that live SessionBundle first so the run's next
472
+ * saveState cannot resurrect a stale worktree pointer.
473
+ */
474
+ releaseSessionWorkspace(sessionId: string): import("../types.js").SessionWorkspace | null;
463
475
  injectContext(sessionId: string, content: string): void;
464
476
  /**
465
477
  * Force context compaction on a session.
@@ -570,6 +582,7 @@ export declare class Engine {
570
582
  * by tests that want a ToolContext without a full run() cycle.
571
583
  */
572
584
  private resolveSandboxWithoutRuntime;
585
+ private resolveSandboxConfigForCwd;
573
586
  /**
574
587
  * Build the shell env layered onto the Bash tool / background shells (see
575
588
  * mergeShellEnv). Three user-configured sources, merged lowest → highest:
@@ -622,6 +635,9 @@ export declare class Engine {
622
635
  linux?: string;
623
636
  windows?: string;
624
637
  } | undefined;
638
+ readWorktreeBranchPrefix(cwd?: string): string | undefined;
639
+ resolveWorktreeSetupSandbox(cwd: string): Promise<SandboxBackend | undefined>;
640
+ readWorktreeSetupShellEnv(cwd?: string): Record<string, string> | undefined;
625
641
  buildToolContext(): ToolContext;
626
642
  /**
627
643
  * Read settings.disabledSkills + settings.disabledPlugins in a single