@armadra/agent 0.2.1 → 0.4.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 (232) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/README.md +142 -49
  3. package/dist/agent/retry.d.ts +1 -1
  4. package/dist/agent/retry.js +2 -1
  5. package/dist/agent/session-cache.js +6 -3
  6. package/dist/agent/session-classifier.d.ts +18 -0
  7. package/dist/agent/session-classifier.js +103 -0
  8. package/dist/agent/session-core.d.ts +13 -0
  9. package/dist/agent/session-state.js +2 -1
  10. package/dist/agent/session-sync.js +9 -2
  11. package/dist/agent/session-tools.js +24 -3
  12. package/dist/agent/session.d.ts +3 -0
  13. package/dist/agent/session.js +20 -10
  14. package/dist/agent/tool-runner.js +15 -2
  15. package/dist/agent/types.d.ts +9 -1
  16. package/dist/ai/apis/anthropic-messages.js +3 -2
  17. package/dist/ai/apis/google-generative-ai.js +3 -2
  18. package/dist/ai/apis/openai-completions.js +3 -2
  19. package/dist/ai/apis/openai-responses.js +5 -3
  20. package/dist/ai/cache/fingerprint.d.ts +1 -1
  21. package/dist/ai/cache/fingerprint.js +1 -1
  22. package/dist/ai/cache/reporting.js +2 -1
  23. package/dist/ai/http.d.ts +28 -6
  24. package/dist/ai/http.js +41 -8
  25. package/dist/ai/providers/channels.d.ts +51 -0
  26. package/dist/ai/providers/channels.js +96 -0
  27. package/dist/ai/providers/enrich.d.ts +34 -0
  28. package/dist/ai/providers/enrich.js +86 -0
  29. package/dist/ai/providers/models-dev-cache.d.ts +53 -0
  30. package/dist/ai/providers/models-dev-cache.js +147 -0
  31. package/dist/ai/providers/models-dev.d.ts +99 -0
  32. package/dist/ai/providers/models-dev.js +315 -0
  33. package/dist/ai/providers/registry.d.ts +42 -4
  34. package/dist/ai/providers/registry.js +195 -34
  35. package/dist/ai/providers/suggest.d.ts +18 -0
  36. package/dist/ai/providers/suggest.js +72 -0
  37. package/dist/ai/sse.d.ts +6 -2
  38. package/dist/ai/sse.js +22 -2
  39. package/dist/ai/types.d.ts +30 -3
  40. package/dist/bundle/ama.cjs +17893 -10698
  41. package/dist/cli/args.d.ts +23 -5
  42. package/dist/cli/args.js +113 -13
  43. package/dist/cli/bootstrap.js +28 -3
  44. package/dist/cli/codemode-notice.d.ts +20 -0
  45. package/dist/cli/codemode-notice.js +55 -0
  46. package/dist/cli/compose-providers.js +3 -0
  47. package/dist/cli/compose-session.d.ts +8 -0
  48. package/dist/cli/compose-session.js +32 -1
  49. package/dist/cli/compose-store.d.ts +1 -1
  50. package/dist/cli/compose-store.js +3 -1
  51. package/dist/cli/compose.d.ts +17 -5
  52. package/dist/cli/compose.js +53 -19
  53. package/dist/cli/default-model.d.ts +38 -1
  54. package/dist/cli/default-model.js +96 -9
  55. package/dist/cli/deps.d.ts +38 -0
  56. package/dist/cli/exit-codes.d.ts +2 -0
  57. package/dist/cli/exit-codes.js +3 -0
  58. package/dist/cli/fake-visibility.d.ts +13 -0
  59. package/dist/cli/fake-visibility.js +26 -0
  60. package/dist/cli/from-prompt.d.ts +18 -0
  61. package/dist/cli/from-prompt.js +49 -0
  62. package/dist/cli/main.d.ts +10 -2
  63. package/dist/cli/main.js +88 -2
  64. package/dist/cli/proxy.d.ts +51 -0
  65. package/dist/cli/proxy.js +135 -0
  66. package/dist/cli/startup-screen.d.ts +29 -0
  67. package/dist/cli/startup-screen.js +47 -0
  68. package/dist/cli/startup-steps.d.ts +1 -1
  69. package/dist/cli/startup-steps.js +20 -13
  70. package/dist/cli/subcommands/config.d.ts +29 -4
  71. package/dist/cli/subcommands/config.js +188 -25
  72. package/dist/cli/subcommands/context.js +5 -1
  73. package/dist/cli/subcommands/doctor.js +12 -1
  74. package/dist/cli/subcommands/init.d.ts +6 -0
  75. package/dist/cli/subcommands/init.js +23 -0
  76. package/dist/cli/subcommands/model-meta.d.ts +16 -0
  77. package/dist/cli/subcommands/model-meta.js +54 -0
  78. package/dist/cli/subcommands/models-cache-probe.js +1 -1
  79. package/dist/cli/subcommands/models-discover.d.ts +18 -6
  80. package/dist/cli/subcommands/models-discover.js +87 -52
  81. package/dist/cli/subcommands/models.js +26 -18
  82. package/dist/cli/subcommands/probe-runner.d.ts +96 -0
  83. package/dist/cli/subcommands/probe-runner.js +264 -0
  84. package/dist/cli/subcommands/providers-list.d.ts +8 -0
  85. package/dist/cli/subcommands/providers-list.js +97 -0
  86. package/dist/cli/subcommands/providers-plan.d.ts +79 -0
  87. package/dist/cli/subcommands/providers-plan.js +215 -0
  88. package/dist/cli/subcommands/providers-probe.d.ts +34 -0
  89. package/dist/cli/subcommands/providers-probe.js +87 -0
  90. package/dist/cli/subcommands/providers.d.ts +28 -0
  91. package/dist/cli/subcommands/providers.js +436 -0
  92. package/dist/cli/subcommands/sessions-export.d.ts +10 -0
  93. package/dist/cli/subcommands/sessions-export.js +59 -0
  94. package/dist/cli/subcommands/sessions-search.d.ts +13 -0
  95. package/dist/cli/subcommands/sessions-search.js +103 -0
  96. package/dist/cli/subcommands/sessions.d.ts +3 -2
  97. package/dist/cli/subcommands/sessions.js +22 -1
  98. package/dist/cli/subcommands/stats.d.ts +17 -0
  99. package/dist/cli/subcommands/stats.js +198 -0
  100. package/dist/cli/system-prompt-arg.d.ts +11 -0
  101. package/dist/cli/system-prompt-arg.js +34 -0
  102. package/dist/codemode/modes.d.ts +4 -11
  103. package/dist/codemode/modes.js +5 -27
  104. package/dist/codemode/tool.d.ts +18 -14
  105. package/dist/codemode/tool.js +71 -28
  106. package/dist/config/init.d.ts +34 -0
  107. package/dist/config/init.js +99 -0
  108. package/dist/config/json-schema.d.ts +15 -0
  109. package/dist/config/json-schema.js +215 -0
  110. package/dist/config/key-docs.d.ts +22 -0
  111. package/dist/config/key-docs.js +105 -0
  112. package/dist/config/merge.d.ts +9 -8
  113. package/dist/config/merge.js +22 -7
  114. package/dist/config/schema.d.ts +1 -1
  115. package/dist/config/schema.js +97 -15
  116. package/dist/config/types.d.ts +57 -11
  117. package/dist/config/types.js +19 -1
  118. package/dist/modes/commands-core.js +6 -5
  119. package/dist/modes/image-input.d.ts +27 -0
  120. package/dist/modes/image-input.js +78 -0
  121. package/dist/modes/interactive/approval-dialog.d.ts +21 -5
  122. package/dist/modes/interactive/approval-dialog.js +106 -27
  123. package/dist/modes/interactive/commands.d.ts +11 -2
  124. package/dist/modes/interactive/commands.js +58 -19
  125. package/dist/modes/interactive/interactive-mode.d.ts +2 -1
  126. package/dist/modes/interactive/interactive-mode.js +70 -82
  127. package/dist/modes/interactive/key-dispatch.d.ts +3 -1
  128. package/dist/modes/interactive/key-dispatch.js +5 -6
  129. package/dist/modes/interactive/line/line-mode.d.ts +1 -0
  130. package/dist/modes/interactive/line/line-mode.js +9 -5
  131. package/dist/modes/interactive/line/line-render.d.ts +4 -0
  132. package/dist/modes/interactive/line/line-render.js +28 -6
  133. package/dist/modes/interactive/message-view.d.ts +48 -9
  134. package/dist/modes/interactive/message-view.js +238 -44
  135. package/dist/modes/interactive/panels.d.ts +18 -0
  136. package/dist/modes/interactive/panels.js +143 -0
  137. package/dist/modes/interactive/pickers.d.ts +23 -2
  138. package/dist/modes/interactive/pickers.js +48 -15
  139. package/dist/modes/interactive/run-indicator.d.ts +51 -0
  140. package/dist/modes/interactive/run-indicator.js +189 -0
  141. package/dist/modes/interactive/startup-header.d.ts +40 -0
  142. package/dist/modes/interactive/startup-header.js +169 -0
  143. package/dist/modes/interactive/startup-ui.d.ts +7 -2
  144. package/dist/modes/interactive/startup-ui.js +41 -11
  145. package/dist/modes/interactive/status-bar.d.ts +18 -15
  146. package/dist/modes/interactive/status-bar.js +98 -56
  147. package/dist/modes/interactive/tool-summary.d.ts +46 -0
  148. package/dist/modes/interactive/tool-summary.js +218 -0
  149. package/dist/modes/interactive/tool-view.d.ts +48 -15
  150. package/dist/modes/interactive/tool-view.js +203 -145
  151. package/dist/modes/print/print-mode.d.ts +33 -4
  152. package/dist/modes/print/print-mode.js +128 -8
  153. package/dist/modes/rpc/commands.js +12 -2
  154. package/dist/permissions/auto-safe.d.ts +60 -0
  155. package/dist/permissions/auto-safe.js +529 -0
  156. package/dist/permissions/classifier.d.ts +64 -0
  157. package/dist/permissions/classifier.js +184 -0
  158. package/dist/permissions/dangerous.d.ts +5 -0
  159. package/dist/permissions/dangerous.js +1 -1
  160. package/dist/permissions/modes.d.ts +30 -0
  161. package/dist/permissions/modes.js +78 -0
  162. package/dist/permissions/pipeline.d.ts +31 -4
  163. package/dist/permissions/pipeline.js +196 -6
  164. package/dist/permissions/protected.d.ts +19 -0
  165. package/dist/permissions/protected.js +74 -0
  166. package/dist/permissions/rules.js +3 -0
  167. package/dist/permissions/types.d.ts +50 -3
  168. package/dist/permissions/types.js +3 -0
  169. package/dist/rpc.d.ts +2 -0
  170. package/dist/sdk.d.ts +9 -3
  171. package/dist/sdk.js +10 -2
  172. package/dist/session/export.d.ts +32 -0
  173. package/dist/session/export.js +187 -0
  174. package/dist/session/projection.js +5 -1
  175. package/dist/session/redact.d.ts +15 -0
  176. package/dist/session/redact.js +55 -0
  177. package/dist/session/reuse.d.ts +33 -0
  178. package/dist/session/reuse.js +86 -0
  179. package/dist/session/scan.d.ts +34 -0
  180. package/dist/session/scan.js +140 -0
  181. package/dist/session/search.d.ts +52 -0
  182. package/dist/session/search.js +211 -0
  183. package/dist/session/stats-aggregate.d.ts +63 -0
  184. package/dist/session/stats-aggregate.js +163 -0
  185. package/dist/session/stats-index.d.ts +26 -0
  186. package/dist/session/stats-index.js +91 -0
  187. package/dist/session/stats-scan.d.ts +54 -0
  188. package/dist/session/stats-scan.js +236 -0
  189. package/dist/session/types.d.ts +2 -0
  190. package/dist/tools/image-file.d.ts +31 -0
  191. package/dist/tools/image-file.js +114 -0
  192. package/dist/tools/presets.d.ts +35 -8
  193. package/dist/tools/presets.js +56 -17
  194. package/dist/tools/read.d.ts +4 -8
  195. package/dist/tools/read.js +15 -64
  196. package/dist/tui/component.d.ts +8 -2
  197. package/dist/tui/component.js +3 -1
  198. package/dist/tui/components/box.d.ts +6 -1
  199. package/dist/tui/components/box.js +16 -6
  200. package/dist/tui/components/card.d.ts +23 -0
  201. package/dist/tui/components/card.js +37 -0
  202. package/dist/tui/components/editor-history.d.ts +6 -0
  203. package/dist/tui/components/editor-history.js +45 -0
  204. package/dist/tui/components/editor-paste.d.ts +1 -1
  205. package/dist/tui/components/editor-paste.js +4 -4
  206. package/dist/tui/components/editor.d.ts +11 -5
  207. package/dist/tui/components/editor.js +52 -58
  208. package/dist/tui/components/key-value.d.ts +3 -0
  209. package/dist/tui/components/key-value.js +16 -6
  210. package/dist/tui/components/loader.d.ts +37 -7
  211. package/dist/tui/components/loader.js +84 -21
  212. package/dist/tui/components/markdown.d.ts +5 -1
  213. package/dist/tui/components/markdown.js +45 -21
  214. package/dist/tui/components/meter.d.ts +3 -3
  215. package/dist/tui/components/meter.js +13 -11
  216. package/dist/tui/components/select-list.d.ts +23 -1
  217. package/dist/tui/components/select-list.js +76 -13
  218. package/dist/tui/glyphs.d.ts +68 -0
  219. package/dist/tui/glyphs.js +114 -0
  220. package/dist/tui/theme.d.ts +30 -6
  221. package/dist/tui/theme.js +103 -17
  222. package/dist/tui.d.ts +4 -2
  223. package/dist/tui.js +3 -1
  224. package/docs/codemode.md +23 -9
  225. package/docs/hooks.md +10 -10
  226. package/docs/permissions.md +148 -0
  227. package/docs/providers.md +241 -3
  228. package/docs/rpc.md +38 -38
  229. package/docs/session-format.md +3 -2
  230. package/docs/sessions.md +134 -0
  231. package/docs/tui.md +140 -61
  232. package/package.json +3 -1
@@ -0,0 +1,19 @@
1
+ /**
2
+ * auto 模式规则层的受保护路径(§7.4,docs/permissions.md「规则层」)。
3
+ *
4
+ * - **机密路径**(读写都询问):`.env` / `.env.*`(`.example` / `.sample` / `.template` / `.dist` 除外)、
5
+ * `*.env`、`.ssh/`、`.gnupg/`、`.aws/`、`.kube/config`、`.docker/config.json`、`.config/gcloud/`、
6
+ * `.netrc`、`.pgpass`、`.git-credentials`、私钥(`id_rsa` 等、`*.pem`、`*.key`、`*.p12`、`*.pfx`、
7
+ * `*.jks`、`*.keystore`、`*.ppk`)、ama 自己的 `ama/auth.json`。
8
+ * - **受保护写入**(只对写询问):机密路径,`.git` 目录内部,项目里的 `.ama/`(Hook 与配置可能借此放宽),
9
+ * 项目目录外的任何路径(`/dev/null`、`/dev/stdout`、`/dev/stderr`、`/dev/tty` 除外)。
10
+ *
11
+ * 判断按词法路径(不跟随符号链接,与规则匹配一致)。返回英文短说明(进审计与 tool_result),
12
+ * 不受保护返回 undefined。
13
+ */
14
+ /** 机密文件或目录:读与写都要询问。 */
15
+ export declare function secretPathReason(absPath: string): string | undefined;
16
+ /** 写入受保护的原因:机密、`.git` 内部、项目 `.ama/`、项目外;可以写返回 undefined。 */
17
+ export declare function writeProtectionReason(absPath: string, projectRoot: string): string | undefined;
18
+ /** 是否在项目目录内(含项目根本身)。 */
19
+ export declare function insideProject(absPath: string, projectRoot: string): boolean;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * auto 模式规则层的受保护路径(§7.4,docs/permissions.md「规则层」)。
3
+ *
4
+ * - **机密路径**(读写都询问):`.env` / `.env.*`(`.example` / `.sample` / `.template` / `.dist` 除外)、
5
+ * `*.env`、`.ssh/`、`.gnupg/`、`.aws/`、`.kube/config`、`.docker/config.json`、`.config/gcloud/`、
6
+ * `.netrc`、`.pgpass`、`.git-credentials`、私钥(`id_rsa` 等、`*.pem`、`*.key`、`*.p12`、`*.pfx`、
7
+ * `*.jks`、`*.keystore`、`*.ppk`)、ama 自己的 `ama/auth.json`。
8
+ * - **受保护写入**(只对写询问):机密路径,`.git` 目录内部,项目里的 `.ama/`(Hook 与配置可能借此放宽),
9
+ * 项目目录外的任何路径(`/dev/null`、`/dev/stdout`、`/dev/stderr`、`/dev/tty` 除外)。
10
+ *
11
+ * 判断按词法路径(不跟随符号链接,与规则匹配一致)。返回英文短说明(进审计与 tool_result),
12
+ * 不受保护返回 undefined。
13
+ */
14
+ import { basename, relative } from "node:path";
15
+ import { isWithin, toPosix } from "../tools/paths.js";
16
+ const ENV_EXEMPT = /\.(example|sample|template|dist)$/i;
17
+ const KEY_FILE = /^id_(rsa|dsa|ecdsa|ed25519)(_sk)?$/;
18
+ const KEY_EXT = /\.(pem|key|p12|pfx|jks|keystore|ppk)$/i;
19
+ const SECRET_DIRS = new Set([".ssh", ".gnupg", ".aws"]);
20
+ const SECRET_FILES = new Set([".netrc", ".pgpass", ".git-credentials"]);
21
+ const DEVICE_SINKS = new Set(["/dev/null", "/dev/stdout", "/dev/stderr", "/dev/tty"]);
22
+ function segments(absPath) {
23
+ return toPosix(absPath).split("/").filter(Boolean);
24
+ }
25
+ /** 机密文件或目录:读与写都要询问。 */
26
+ export function secretPathReason(absPath) {
27
+ const parts = segments(absPath);
28
+ const name = basename(absPath);
29
+ if ((/^\.env(\..+)?$/.test(name) || /\.env$/.test(name)) && !ENV_EXEMPT.test(name)) {
30
+ return `secret file ${name}`;
31
+ }
32
+ if (KEY_FILE.test(name) || KEY_EXT.test(name))
33
+ return `private key or certificate ${name}`;
34
+ if (SECRET_FILES.has(name))
35
+ return `credentials file ${name}`;
36
+ const dir = parts.slice(0, -1);
37
+ const secretDir = parts.find((p) => SECRET_DIRS.has(p));
38
+ if (secretDir !== undefined)
39
+ return `credentials directory ${secretDir}/`;
40
+ for (let i = 0; i < dir.length; i++) {
41
+ const a = dir[i];
42
+ const b = parts[i + 1];
43
+ if (a === ".kube" && b === "config")
44
+ return "kubeconfig";
45
+ if (a === ".docker" && b === "config.json")
46
+ return "docker credentials";
47
+ if (a === ".config" && b === "gcloud")
48
+ return "gcloud credentials";
49
+ if ((a === "ama" || a === ".ama") && b === "auth.json" && i + 2 === parts.length) {
50
+ return "ama API key file";
51
+ }
52
+ }
53
+ return undefined;
54
+ }
55
+ /** 写入受保护的原因:机密、`.git` 内部、项目 `.ama/`、项目外;可以写返回 undefined。 */
56
+ export function writeProtectionReason(absPath, projectRoot) {
57
+ if (DEVICE_SINKS.has(toPosix(absPath)))
58
+ return undefined;
59
+ const secret = secretPathReason(absPath);
60
+ if (secret !== undefined)
61
+ return secret;
62
+ if (segments(absPath).includes(".git"))
63
+ return "writes inside .git";
64
+ if (!isWithin(projectRoot, absPath))
65
+ return "writes outside the project directory";
66
+ const rel = toPosix(relative(projectRoot, absPath));
67
+ if (rel === ".ama" || rel.startsWith(".ama/"))
68
+ return "writes to the project .ama/ config";
69
+ return undefined;
70
+ }
71
+ /** 是否在项目目录内(含项目根本身)。 */
72
+ export function insideProject(absPath, projectRoot) {
73
+ return isWithin(projectRoot, absPath);
74
+ }
@@ -229,6 +229,9 @@ export function resolvePermissionLayers(layers, options = {}) {
229
229
  if (!PERMISSION_MODES_STRICT_FIRST.includes(layer.mode)) {
230
230
  warnings.push(`${layer.source}: unknown permission mode "${String(layer.mode)}"`);
231
231
  }
232
+ else if (restricted && (layer.mode === "auto" || layer.mode === "full-auto")) {
233
+ warnings.push(`project config cannot set permission mode "${layer.mode}"; ignored`);
234
+ }
232
235
  else if (restricted && !isAtLeastAsStrict(layer.mode, mode)) {
233
236
  warnings.push(`project config cannot relax permission mode from "${mode}" to "${layer.mode}"; ignored`);
234
237
  }
@@ -11,9 +11,32 @@
11
11
  * 「覆盖未读过的文件」。`previewAction()` 实现归 W3-B9a-2。
12
12
  */
13
13
  import type { ToolPermission } from "../tools/types.js";
14
- /** 严格程度:plan > default > auto-edit > full-auto(项目级只能往左收紧,§7.2)。 */
15
- export type PermissionMode = "plan" | "default" | "auto-edit" | "full-auto";
14
+ /**
15
+ * 六种模式(§7.4)。严格程度从严到宽:plan < allowlist < default < auto-edit < auto < full-auto
16
+ * (项目级只能往左收紧,且不能设 auto / full-auto,§7.2)。
17
+ * `allowlist` 放行只读工具与 allow 规则命中的调用,其余拒绝、从不询问:放行集合是 plan 的超集、
18
+ * default 放行集合的子集,所以排在两者之间。
19
+ */
20
+ export type PermissionMode = "plan" | "allowlist" | "default" | "auto-edit" | "auto" | "full-auto";
16
21
  export declare const PERMISSION_MODES_STRICT_FIRST: readonly PermissionMode[];
22
+ /** auto 判定发生在哪一层(§7.4):规则层、静态判定、模型分类器。 */
23
+ export type AutoLayer = "rule" | "static" | "classifier";
24
+ /** 一次 auto 判定(审计用;线上形状,可 JSON 序列化)。 */
25
+ export interface AutoDecision {
26
+ layer: AutoLayer;
27
+ /** 这一层的结论;ask 在无人值守时最终按 deny 执行。 */
28
+ decision: Decision;
29
+ reason: string;
30
+ /** 分类器结果来自会话内缓存。 */
31
+ cached?: boolean;
32
+ }
33
+ /** `/permissions` 列出的最近判定。 */
34
+ export interface AutoAuditEntry extends AutoDecision {
35
+ at: number;
36
+ toolName: string;
37
+ /** 命令或路径摘要(单行、截断)。 */
38
+ summary: string;
39
+ }
17
40
  export type Decision = "allow" | "ask" | "deny";
18
41
  export type RuleSource = "builtin" | "user" | "profile" | "project" | "cli" | "sdk";
19
42
  /** `bash(git push*)`、`write(src/**)`、`read(**)`、`canvas_*` 解析结果。 */
@@ -36,6 +59,8 @@ export interface ApprovalRequest {
36
59
  hookReason?: string;
37
60
  /** [W3-C0] 执行前预览(只读、有上限);预览超时或未实现时缺省。 */
38
61
  preview?: ActionPreview;
62
+ /** auto 模式下为什么要询问(规则层或分类器的结论)。 */
63
+ autoDecision?: AutoDecision;
39
64
  /**
40
65
  * 发起方上下文:`depth > 0` 表示来自 `task` 子 Agent(对话框标 `[task]`);codemode 内层
41
66
  * 调用带外层调用的 `parentToolCallId`。由子会话包装的 broker 填入,缺省视为主会话。
@@ -80,7 +105,15 @@ export interface PermissionCheckInput {
80
105
  unattended: boolean;
81
106
  }
82
107
  /** 管线哪一步做出了最终决定。 */
83
- export type PermissionStep = "deny-rule" | "hook-deny" | "dangerous" | "mode" | "allow-rule" | "hook" | "session";
108
+ export type PermissionStep = "deny-rule" | "hook-deny" | "dangerous" | "mode" | "allow-rule" | "hook" | "session"
109
+ /** auto 规则层:受保护路径、网络、删除类、项目外写入。 */
110
+ | "auto-rule"
111
+ /** auto 静态判定放行。 */
112
+ | "auto-static"
113
+ /** auto 留给模型分类器(verdict.classify 为真)。 */
114
+ | "auto-classify"
115
+ /** allowlist 模式下不在允许名单。 */
116
+ | "allowlist";
84
117
  export interface PermissionVerdict {
85
118
  decision: Decision;
86
119
  step: PermissionStep;
@@ -89,6 +122,13 @@ export interface PermissionVerdict {
89
122
  rule?: Rule;
90
123
  /** 给模型 / 用户看的说明(deny 时写进 tool_result)。 */
91
124
  message?: string;
125
+ /** auto 模式的判定记录(layer / reason);其它模式缺省。 */
126
+ auto?: AutoDecision;
127
+ /**
128
+ * auto 模式下规则层与静态判定都没决定:调用方可以问模型分类器,allow → 放行;否则按本 verdict
129
+ * 的 decision(ask,无人值守时已是 deny)。不认识这个字段的调用方得到的就是保守结论。
130
+ */
131
+ classify?: boolean;
92
132
  }
93
133
  export interface PermissionPipelineApi {
94
134
  readonly mode: PermissionMode;
@@ -97,4 +137,11 @@ export interface PermissionPipelineApi {
97
137
  check(input: PermissionCheckInput): PermissionVerdict;
98
138
  /** allow_session:内存记住 toolName + 归一化输入前缀,不落盘。 */
99
139
  rememberForSession(toolName: string, input: unknown): void;
140
+ /** auto 判定审计:记一条(最多保留最近 {@link AUTO_AUDIT_LIMIT} 条)。 */
141
+ recordAutoDecision?(toolName: string, input: unknown, decision: AutoDecision): void;
142
+ /** 最近的 auto 判定,旧的在前。 */
143
+ autoDecisions?(): readonly AutoAuditEntry[];
144
+ /** auto:项目根(分类器输入),缺省 = 会话 cwd。 */
145
+ readonly projectRoot?: string;
100
146
  }
147
+ export declare const AUTO_AUDIT_LIMIT = 20;
@@ -12,7 +12,10 @@
12
12
  */
13
13
  export const PERMISSION_MODES_STRICT_FIRST = [
14
14
  "plan",
15
+ "allowlist",
15
16
  "default",
16
17
  "auto-edit",
18
+ "auto",
17
19
  "full-auto",
18
20
  ];
21
+ export const AUTO_AUDIT_LIMIT = 20;
package/dist/rpc.d.ts CHANGED
@@ -45,9 +45,11 @@ export interface RpcCommandMap {
45
45
  get_messages: NoParams;
46
46
  get_last_assistant_text: NoParams;
47
47
  get_session_stats: NoParams;
48
+ /** `channel`:多渠道供应商的渠道名(缺省走模型的首选渠道)。 */
48
49
  set_model: {
49
50
  provider: string;
50
51
  modelId: string;
52
+ channel?: string;
51
53
  };
52
54
  get_available_models: NoParams;
53
55
  set_thinking_level: {
package/dist/sdk.d.ts CHANGED
@@ -16,7 +16,7 @@ import type { AgentSessionImpl } from "./agent/session.js";
16
16
  import { type LogFn } from "./cli/compose-session.js";
17
17
  import { type ComposeOptions } from "./cli/compose.js";
18
18
  import type { Runtime } from "./cli/runtime.js";
19
- import type { AmaConfig, ToolsPreset } from "./config/types.js";
19
+ import { type AmaConfig, type ToolsPresetInput } from "./config/types.js";
20
20
  import type { HookConfig } from "./hooks/types.js";
21
21
  import type { InstructionSource } from "./host/types.js";
22
22
  import type { ApprovalDecision, ApprovalRequest, PermissionMode } from "./permissions/types.js";
@@ -32,7 +32,8 @@ export interface RuntimeOptions {
32
32
  model?: string;
33
33
  thinkingLevel?: ModelThinkingLevel;
34
34
  permissionMode?: PermissionMode;
35
- toolsPreset?: ToolsPreset;
35
+ /** `codemode` 是 `codemode-only` 的别名。 */
36
+ toolsPreset?: ToolsPresetInput;
36
37
  /** 信任项目(项目级 Hook / Skill / 提示模板)。 */
37
38
  trust?: boolean;
38
39
  sessionDir?: string;
@@ -73,7 +74,8 @@ export interface CreateSessionOptions {
73
74
  sessionManager?: SessionManager;
74
75
  /** `default`(缺省,按预设)/ `none` / 指定工具全集。 */
75
76
  tools?: "default" | "none" | ToolDefinition[];
76
- toolsPreset?: ToolsPreset;
77
+ /** `codemode` 是 `codemode-only` 的别名。 */
78
+ toolsPreset?: ToolsPresetInput;
77
79
  extraTools?: ToolDefinition[];
78
80
  disableTools?: string[];
79
81
  /** 进系统提示 host 节。 */
@@ -85,6 +87,10 @@ export interface CreateSessionOptions {
85
87
  mode?: PermissionMode;
86
88
  allow?: string[];
87
89
  deny?: string[];
90
+ /** auto 模式分类器的模型(`provider/model`);缺省 config `permission.autoModel`,再缺省当前模型。 */
91
+ autoModel?: string;
92
+ /** auto 模式安全名单追加(与 config `permission.autoSafeCommands` 合并)。 */
93
+ autoSafeCommands?: string[];
88
94
  /** 审批回调;缺省无人作答 → deny。 */
89
95
  ask?(request: ApprovalRequest, signal: AbortSignal): Promise<ApprovalDecision>;
90
96
  };
package/dist/sdk.js CHANGED
@@ -21,6 +21,7 @@ import { pickDefaultModel } from "./cli/default-model.js";
21
21
  import { findContextFiles } from "./config/context-files.js";
22
22
  import { DEFAULT_CONFIG, mergeConfig } from "./config/merge.js";
23
23
  import { resolveConfigDir, resolveDataDir } from "./config/paths.js";
24
+ import { canonicalPreset } from "./config/types.js";
24
25
  import { AmaError } from "./errors.js";
25
26
  import { hooksFromConfig } from "./hooks/config.js";
26
27
  import { HookDispatcher } from "./hooks/dispatcher.js";
@@ -41,7 +42,7 @@ export async function createRuntime(options = {}) {
41
42
  if (options.permissionMode !== undefined)
42
43
  args.permissionMode = options.permissionMode;
43
44
  if (options.toolsPreset !== undefined)
44
- args.toolsPreset = options.toolsPreset;
45
+ args.toolsPreset = canonicalPreset(options.toolsPreset);
45
46
  if (options.trust !== undefined)
46
47
  args.trust = options.trust;
47
48
  if (options.sessionDir !== undefined)
@@ -131,7 +132,14 @@ export async function createAgentSession(options = {}) {
131
132
  source: "sdk",
132
133
  })),
133
134
  ], config.permission?.builtinDeny, warn);
134
- const permission = new PermissionPipeline({ mode, rules, cwd });
135
+ const autoSafeCommands = [
136
+ ...(config.permission?.autoSafeCommands ?? []),
137
+ ...(options.permission?.autoSafeCommands ?? []),
138
+ ];
139
+ const permission = new PermissionPipeline({ mode, rules, cwd, autoSafeCommands });
140
+ const autoModel = options.permission?.autoModel;
141
+ if (autoModel !== undefined)
142
+ config.permission = { ...config.permission, autoModel };
135
143
  const manager = options.sessionManager ?? SessionManager.inMemory(cwd);
136
144
  let session;
137
145
  const hooks = new HookDispatcher({
@@ -0,0 +1,32 @@
1
+ /**
2
+ * 会话导出(`ama sessions export`):md(给人读)/ json(结构化)/ jsonl(原格式子集)。[W4-D]
3
+ *
4
+ * - 范围:`leaf` = 根 → 当前叶子的分支(同 `/tree` 当前位置),`all` = 文件里全部条目(文件顺序)。
5
+ * - 三种格式都先脱敏(redact.ts):json / jsonl 对每个字符串值做(图片 base64 保留),md 对成文做。
6
+ * - md:用户 / 助手消息、工具调用(参数 JSON 截到 500 字符)与结果(截到 2000 字符)、压缩与分支摘要、
7
+ * 末尾用量合计;思考块与 system 消息不写;图片写成占位 `[图片 image/png]`。
8
+ * - jsonl:头 + 所选条目,形状与会话文件相同(可以再被 ama 读),不带 leaf 行。
9
+ */
10
+ import type { SessionEntry, SessionHeader } from "./types.js";
11
+ export type ExportFormat = "md" | "json" | "jsonl";
12
+ export type ExportBranch = "leaf" | "all";
13
+ export interface ExportInput {
14
+ header: SessionHeader;
15
+ entries: readonly SessionEntry[];
16
+ leaf: string | null;
17
+ }
18
+ export interface UsageTotals {
19
+ requests: number;
20
+ input: number;
21
+ output: number;
22
+ cacheRead: number;
23
+ cacheWrite: number;
24
+ /** 有价请求的费用;没有有价请求时缺省。 */
25
+ cost?: number;
26
+ }
27
+ export declare function selectEntries(input: ExportInput, branch: ExportBranch): SessionEntry[];
28
+ export declare function usageTotals(entries: readonly SessionEntry[]): UsageTotals;
29
+ export declare function renderMarkdown(input: ExportInput, branch: ExportBranch): string;
30
+ export declare function renderJson(input: ExportInput, branch: ExportBranch): string;
31
+ export declare function renderJsonl(input: ExportInput, branch: ExportBranch): string;
32
+ export declare function renderExport(input: ExportInput, format: ExportFormat, branch: ExportBranch): string;
@@ -0,0 +1,187 @@
1
+ /**
2
+ * 会话导出(`ama sessions export`):md(给人读)/ json(结构化)/ jsonl(原格式子集)。[W4-D]
3
+ *
4
+ * - 范围:`leaf` = 根 → 当前叶子的分支(同 `/tree` 当前位置),`all` = 文件里全部条目(文件顺序)。
5
+ * - 三种格式都先脱敏(redact.ts):json / jsonl 对每个字符串值做(图片 base64 保留),md 对成文做。
6
+ * - md:用户 / 助手消息、工具调用(参数 JSON 截到 500 字符)与结果(截到 2000 字符)、压缩与分支摘要、
7
+ * 末尾用量合计;思考块与 system 消息不写;图片写成占位 `[图片 image/png]`。
8
+ * - jsonl:头 + 所选条目,形状与会话文件相同(可以再被 ama 读),不带 leaf 行。
9
+ */
10
+ import { contentImages, contentText, numberUserMessages } from "./reuse.js";
11
+ import { redactSecrets, redactValue } from "./redact.js";
12
+ import { indexEntries, pathToRoot } from "./tree.js";
13
+ export function selectEntries(input, branch) {
14
+ return branch === "all"
15
+ ? [...input.entries]
16
+ : pathToRoot(indexEntries(input.entries), input.leaf);
17
+ }
18
+ export function usageTotals(entries) {
19
+ const totals = { requests: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
20
+ let cost = 0;
21
+ let costed = 0;
22
+ const add = (usage) => {
23
+ if (usage === undefined)
24
+ return;
25
+ totals.requests++;
26
+ totals.input += usage.input;
27
+ totals.output += usage.output;
28
+ totals.cacheRead += usage.cacheRead;
29
+ totals.cacheWrite += usage.cacheWrite;
30
+ if (usage.cost !== undefined) {
31
+ cost += usage.cost.total;
32
+ costed++;
33
+ }
34
+ };
35
+ for (const entry of entries) {
36
+ if (entry.type === "message" && entry.message.role === "assistant")
37
+ add(entry.message.usage);
38
+ else if (entry.type === "usage")
39
+ add(entry.usage);
40
+ else if (entry.type === "compaction" || entry.type === "branch_summary")
41
+ add(entry.usage);
42
+ }
43
+ if (costed > 0)
44
+ totals.cost = cost;
45
+ return totals;
46
+ }
47
+ function sessionName(entries) {
48
+ let name;
49
+ for (const entry of entries)
50
+ if (entry.type === "session_info")
51
+ name = entry.name;
52
+ return name;
53
+ }
54
+ function truncate(text, max) {
55
+ return text.length > max ? `${text.slice(0, max)}\n…(截断,原长 ${text.length} 字符)` : text;
56
+ }
57
+ function fence(text, lang = "") {
58
+ let ticks = "```";
59
+ while (text.includes(ticks))
60
+ ticks += "`";
61
+ return `${ticks}${lang}\n${text}\n${ticks}`;
62
+ }
63
+ function time(iso) {
64
+ return iso.replace("T", " ").replace(/\.\d+Z$|Z$/, "");
65
+ }
66
+ function imagesNote(content) {
67
+ return contentImages(content)
68
+ .map((image) => `[图片 ${image.mimeType}]`)
69
+ .join(" ");
70
+ }
71
+ export function renderMarkdown(input, branch) {
72
+ const entries = selectEntries(input, branch);
73
+ const numbers = new Map(numberUserMessages(input.entries).map((u) => [u.entryId, u.n]));
74
+ const name = sessionName(input.entries);
75
+ const out = [
76
+ `# ${name ?? `会话 ${input.header.id.slice(0, 8)}`}`,
77
+ "",
78
+ `- id:${input.header.id}`,
79
+ `- 目录:${input.header.cwd}`,
80
+ `- 创建:${time(input.header.timestamp)}`,
81
+ `- 范围:${branch === "all" ? "全部条目" : "当前分支"}(${entries.length} 条条目)`,
82
+ ...(input.header.parentSession !== undefined
83
+ ? [`- 来源会话:${input.header.parentSession}`]
84
+ : []),
85
+ "",
86
+ ];
87
+ for (const entry of entries) {
88
+ switch (entry.type) {
89
+ case "message": {
90
+ const m = entry.message;
91
+ if (m.role === "user") {
92
+ const n = numbers.get(entry.id);
93
+ const origin = m.origin;
94
+ out.push(`## 用户${n !== undefined ? ` #${n}` : ""}${origin !== undefined ? `(${origin})` : ""} · ${time(entry.timestamp)}`, "");
95
+ const text = contentText(m.content);
96
+ if (text !== "")
97
+ out.push(text, "");
98
+ const images = imagesNote(m.content);
99
+ if (images !== "")
100
+ out.push(images, "");
101
+ }
102
+ else if (m.role === "assistant") {
103
+ out.push(`## 助手 · ${m.provider}/${m.model} · ${time(entry.timestamp)}`, "");
104
+ for (const block of m.content) {
105
+ if (block.type === "text" && block.text.trim() !== "")
106
+ out.push(block.text, "");
107
+ if (block.type === "toolCall") {
108
+ out.push(`**工具调用** \`${block.name}\``, "", fence(truncate(JSON.stringify(block.arguments, null, 2), 500), "json"), "");
109
+ }
110
+ }
111
+ if (m.stopReason === "error" || m.stopReason === "aborted") {
112
+ out.push(`> 停止:${m.stopReason}${m.errorMessage !== undefined ? ` · ${m.errorMessage}` : ""}`, "");
113
+ }
114
+ }
115
+ else if (m.role === "toolResult") {
116
+ out.push(`**结果** \`${m.toolName}\`${m.isError ? "(出错)" : ""}`, "", fence(truncate(contentText(m.content), 2000)), "");
117
+ const images = imagesNote(m.content);
118
+ if (images !== "")
119
+ out.push(images, "");
120
+ }
121
+ break;
122
+ }
123
+ case "compaction":
124
+ out.push(`## 压缩摘要 · ${time(entry.timestamp)}`, "", entry.summary, "");
125
+ break;
126
+ case "branch_summary":
127
+ out.push(`## 分支摘要 · ${time(entry.timestamp)}`, "", entry.summary, "");
128
+ break;
129
+ case "custom_message":
130
+ if (entry.display) {
131
+ out.push(`## ${entry.customType} · ${time(entry.timestamp)}`, "");
132
+ out.push(contentText(entry.content), "");
133
+ }
134
+ break;
135
+ case "model_change":
136
+ out.push(`_切换模型:${entry.provider}/${entry.modelId}${entry.channel !== undefined ? `@${entry.channel}` : ""}_`, "");
137
+ break;
138
+ default:
139
+ break;
140
+ }
141
+ }
142
+ const usage = usageTotals(entries);
143
+ out.push("## 用量", "", "| 请求 | 输入 | 输出 | 缓存读 | 缓存写 | 费用 |", "| ---: | ---: | ---: | ---: | ---: | ---: |", `| ${usage.requests} | ${usage.input} | ${usage.output} | ${usage.cacheRead} | ${usage.cacheWrite} | ${usage.cost !== undefined ? `$${usage.cost.toFixed(4)}` : "—"} |`, "");
144
+ return redactSecrets(out.join("\n"));
145
+ }
146
+ export function renderJson(input, branch) {
147
+ const entries = selectEntries(input, branch);
148
+ const selected = new Set(entries.map((e) => e.id));
149
+ const name = sessionName(input.entries);
150
+ const session = {
151
+ id: input.header.id,
152
+ cwd: input.header.cwd,
153
+ createdAt: input.header.timestamp,
154
+ agent: input.header.agent,
155
+ };
156
+ if (name !== undefined)
157
+ session["name"] = name;
158
+ if (input.header.parentSession !== undefined)
159
+ session["parentSession"] = input.header.parentSession;
160
+ const doc = {
161
+ format: "ama.session-export",
162
+ version: 1,
163
+ session,
164
+ branch,
165
+ leafId: input.leaf,
166
+ userMessages: numberUserMessages(input.entries)
167
+ .filter((u) => selected.has(u.entryId))
168
+ .map((u) => ({ n: u.n, entryId: u.entryId })),
169
+ usage: usageTotals(entries),
170
+ entries,
171
+ };
172
+ return JSON.stringify(redactValue(doc), null, 2) + "\n";
173
+ }
174
+ export function renderJsonl(input, branch) {
175
+ const lines = [input.header, ...selectEntries(input, branch)].map((line) => JSON.stringify(redactValue(line)));
176
+ return lines.join("\n") + "\n";
177
+ }
178
+ export function renderExport(input, format, branch) {
179
+ switch (format) {
180
+ case "md":
181
+ return renderMarkdown(input, branch);
182
+ case "json":
183
+ return renderJson(input, branch);
184
+ case "jsonl":
185
+ return renderJsonl(input, branch);
186
+ }
187
+ }
@@ -172,7 +172,11 @@ export function buildContext(branch) {
172
172
  let thinkingLevel;
173
173
  for (const entry of branch) {
174
174
  if (entry.type === "model_change")
175
- model = { provider: entry.provider, id: entry.modelId };
175
+ model = {
176
+ provider: entry.provider,
177
+ id: entry.modelId,
178
+ ...(entry.channel !== undefined ? { channel: entry.channel } : {}),
179
+ };
176
180
  else if (entry.type === "thinking_level_change")
177
181
  thinkingLevel = entry.thinkingLevel;
178
182
  }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * 导出前的机密脱敏(`ama sessions export`)。[W4-D]
3
+ *
4
+ * 只认「形态」:常见 key / token 前缀(sk-…、sk-ant-…、ghp_…、github_pat_…、xox?-…、AIza…、
5
+ * AKIA…、npm_…)、JWT、`Bearer` / `Basic` 凭据、PEM 私钥块,以及 `apiKey` / `secret` / `token` /
6
+ * `password` / `authorization` 之后紧跟 `:` 或 `=` 的值。命中的部分换成 `[REDACTED]`
7
+ * (键名与 Bearer 之类的前缀保留,便于读者知道那里原来有什么)。宁可多遮,不做熵判断。
8
+ */
9
+ export declare const REDACTED = "[REDACTED]";
10
+ export declare function redactSecrets(text: string): string;
11
+ /**
12
+ * 深拷贝并对全部字符串值脱敏;键名像机密(如 `apiKey`、`authToken`)的字符串值整段遮掉;
13
+ * 图片块的 base64 `data` 原样保留(不会是机密,替换会弄坏图片)。
14
+ */
15
+ export declare function redactValue<T>(value: T): T;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * 导出前的机密脱敏(`ama sessions export`)。[W4-D]
3
+ *
4
+ * 只认「形态」:常见 key / token 前缀(sk-…、sk-ant-…、ghp_…、github_pat_…、xox?-…、AIza…、
5
+ * AKIA…、npm_…)、JWT、`Bearer` / `Basic` 凭据、PEM 私钥块,以及 `apiKey` / `secret` / `token` /
6
+ * `password` / `authorization` 之后紧跟 `:` 或 `=` 的值。命中的部分换成 `[REDACTED]`
7
+ * (键名与 Bearer 之类的前缀保留,便于读者知道那里原来有什么)。宁可多遮,不做熵判断。
8
+ */
9
+ export const REDACTED = "[REDACTED]";
10
+ const TOKEN_PATTERNS = [
11
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
12
+ /\bsk-[A-Za-z0-9_-]{16,}/g,
13
+ /\b(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,})/g,
14
+ /\bxox[abprs]-[A-Za-z0-9-]{10,}/g,
15
+ /\bAIza[0-9A-Za-z_-]{30,}/g,
16
+ /\bAKIA[0-9A-Z]{16}\b/g,
17
+ /\bnpm_[A-Za-z0-9]{30,}/g,
18
+ /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
19
+ ];
20
+ const SCHEME = /\b(Bearer|Basic)(\s+)[A-Za-z0-9._~+/=-]{12,}/g;
21
+ const ASSIGNMENT = /\b([A-Za-z0-9_-]*(?:api[_-]?key|apikey|secret|token|password|passwd|authorization))(["']?\s*[:=]\s*["']?)([^\s"',;}{)]{8,})/gi;
22
+ export function redactSecrets(text) {
23
+ let out = text;
24
+ for (const pattern of TOKEN_PATTERNS)
25
+ out = out.replace(pattern, REDACTED);
26
+ out = out.replace(SCHEME, (_m, scheme, space) => `${scheme}${space}${REDACTED}`);
27
+ out = out.replace(ASSIGNMENT, (match, key, sep, value) => value === REDACTED || value.startsWith(REDACTED) ? match : `${key}${sep}${REDACTED}`);
28
+ return out;
29
+ }
30
+ const SECRET_KEY = /(?:api[_-]?key|apikey|secret|token|password|passwd|authorization)$/i;
31
+ /**
32
+ * 深拷贝并对全部字符串值脱敏;键名像机密(如 `apiKey`、`authToken`)的字符串值整段遮掉;
33
+ * 图片块的 base64 `data` 原样保留(不会是机密,替换会弄坏图片)。
34
+ */
35
+ export function redactValue(value) {
36
+ if (typeof value === "string")
37
+ return redactSecrets(value);
38
+ if (Array.isArray(value))
39
+ return value.map((item) => redactValue(item));
40
+ if (typeof value === "object" && value !== null) {
41
+ const record = value;
42
+ const image = record["type"] === "image";
43
+ const out = {};
44
+ for (const [key, item] of Object.entries(record)) {
45
+ if (image && key === "data")
46
+ out[key] = item;
47
+ else if (typeof item === "string" && item.length >= 8 && SECRET_KEY.test(key))
48
+ out[key] = REDACTED;
49
+ else
50
+ out[key] = redactValue(item);
51
+ }
52
+ return out;
53
+ }
54
+ return value;
55
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 会话复用:用户消息编号与 `--from <id>[#n]`。[W4-D]
3
+ *
4
+ * - 编号 = 文件里第几条 `role: "user"` 的消息(1 起,含 steer / followUp / host 注入),与分支无关、
5
+ * 只追加所以稳定;`ama sessions show` 列出,`ama sessions search` 的命中也给出同样的编号。
6
+ * - `--from <id>`(不带 `#n`)取文件里最后一条用户消息。
7
+ * - 取出的是那条消息的文本(多个文本块用空行连接)与图片块。
8
+ */
9
+ import type { ImageBlock } from "../ai/types.js";
10
+ import type { SessionEntry } from "./types.js";
11
+ export interface NumberedUserMessage {
12
+ n: number;
13
+ entryId: string;
14
+ timestamp: string;
15
+ text: string;
16
+ images: ImageBlock[];
17
+ origin?: string;
18
+ }
19
+ /** 内容 → 纯文本(文本块用空行连接;不含图片)。 */
20
+ export declare function contentText(content: unknown): string;
21
+ export declare function contentImages(content: unknown): ImageBlock[];
22
+ export declare function numberUserMessages(entries: readonly SessionEntry[]): NumberedUserMessage[];
23
+ /** `abc123#4` → `{ id: "abc123", n: 4 }`;`abc123` → `{ id }`。 */
24
+ export declare function parseFromSpec(spec: string): {
25
+ id: string;
26
+ n?: number;
27
+ };
28
+ export interface FromMessage extends NumberedUserMessage {
29
+ sessionId: string;
30
+ file: string;
31
+ }
32
+ /** 在会话目录里找 `--from` 指向的用户消息;找不到会话 → session_not_found,编号越界 → 用法错误。 */
33
+ export declare function resolveFromMessage(root: string, spec: string, cwd?: string): FromMessage;