@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
@@ -2,7 +2,7 @@
2
2
  * 组装根:会话存储(实施计划 §1.1 `sessions.*`)。[B6]
3
3
  *
4
4
  * 会话根目录 `sessionDir` 下按 cwd 分子目录(`sessionDirForCwd`)。
5
- * - `new` → 延迟落盘新建;`continue` → 本目录最近一条(没有则新建);
5
+ * - `new` → 延迟落盘新建;`memory`(`--no-session`)→ 内存会话,从不落盘;`continue` → 本目录最近一条(没有则新建);
6
6
  * - `resume{id}` / `fork{id}`:先在本目录、再在全部子目录里按 id 或唯一前缀找文件;找不到 →
7
7
  * `session_not_found`,前缀不唯一 → `invalid_arguments` 并列出候选;
8
8
  * - `session-id{id}`:找到则打开,否则以该 id 新建;
@@ -67,6 +67,8 @@ export function openSession(request, context) {
67
67
  switch (request.kind) {
68
68
  case "new":
69
69
  return SessionManager.create(dir, cwd);
70
+ case "memory":
71
+ return SessionManager.inMemory(cwd);
70
72
  case "continue":
71
73
  return SessionManager.continueRecent(dir, cwd);
72
74
  case "resume":
@@ -23,10 +23,11 @@
23
23
  * 需要会话时用 `ctx.session()`,嵌套调用其它工具用 `ToolContext.tools.executeTool`(走完整门禁)。
24
24
  */
25
25
  import type { ApiRegistry } from "../ai/apis/api.js";
26
- import type { ProviderData } from "../ai/types.js";
26
+ import type { ModelRef, ProviderData } from "../ai/types.js";
27
27
  import type { AgentSession } from "../agent/types.js";
28
28
  import type { AmaConfig } from "../config/types.js";
29
29
  import type { Rule } from "../permissions/types.js";
30
+ import { type SandboxCapability } from "../codemode/capability.js";
30
31
  import { PresetToolRegistry } from "../tools/presets.js";
31
32
  import type { ToolDefinition, ToolRegistryApi } from "../tools/types.js";
32
33
  import { type ComposeState, type LogFn } from "./compose-session.js";
@@ -46,7 +47,7 @@ export interface ToolFactoryContext {
46
47
  }
47
48
  /** 返回 undefined = 本次不注册(例如配置关闭)。 */
48
49
  export type ToolFactory = (ctx: ToolFactoryContext) => ToolDefinition | undefined;
49
- /** 缺省工具工厂:codemode(`codemode.mode` 生效值为 off 时不注册)。 */
50
+ /** 缺省工具工厂:codemode(生效模式为 off 时不注册;缺省跟随预设,见 tools/presets.ts)。 */
50
51
  export declare const DEFAULT_TOOL_FACTORIES: readonly ToolFactory[];
51
52
  export interface ComposeOptions {
52
53
  /** 追加 / 覆盖供应商(SDK)。 */
@@ -56,6 +57,11 @@ export interface ComposeOptions {
56
57
  extraTools?: ToolDefinition[];
57
58
  /** 缺省 DEFAULT_TOOL_FACTORIES。 */
58
59
  toolFactories?: readonly ToolFactory[];
60
+ /**
61
+ * 沙箱能力(缺省按运行时 Node 探测)。决定 default 预设是否开 codemode;给了且没给
62
+ * `toolFactories` 时 codemode 工厂也用它(测试据此不随 Node 版本变化)。
63
+ */
64
+ sandboxCapability?: SandboxCapability;
59
65
  /** 覆盖启动期问答;缺省见 `defaultStartupUi`。 */
60
66
  ui?: InteractiveUi;
61
67
  modes?: Partial<Record<RuntimeMode, ModeRunner>>;
@@ -73,15 +79,21 @@ export declare const DEFAULT_MODES: Readonly<Partial<Record<RuntimeMode, ModeRun
73
79
  /**
74
80
  * 第 12 步:内置工具 + 工厂 + extraTools,按预设定活动集;warning 留给组装会话时报告。
75
81
  *
76
- * 先跑工厂(产物暂不注册)再解析预设:codemode 是否可用决定预设与模式;`on` 模式下注册时给
77
- * 其它工具的描述追加 codemode 提示,`only` 模式活动集独占(codemode/modes.ts)。工厂拿到的
78
- * `registry` 在执行期才读,此时已登记完全部工具。
82
+ * 先跑工厂(产物暂不注册)再解析预设:codemode 是否可用决定预设与模式;`only` 模式活动集独占
83
+ * (codemode/modes.ts)。工厂拿到的 `registry` 在执行期才读,此时已登记完全部工具。default 预设
84
+ * 跟随预设而 codemode 缺省关闭时(非 strict 运行时)提示一次(cli/codemode-notice.ts)。
79
85
  */
80
86
  export declare function createTools(input: {
81
87
  config: AmaConfig;
82
88
  cwd: string;
83
89
  mode: RuntimeMode;
90
+ paths?: {
91
+ configDir: string;
92
+ dataDir: string;
93
+ } | undefined;
84
94
  }, options: ComposeOptions, state: ComposeState): PresetToolRegistry;
95
+ /** 当前模型是否收图片(read 工具用);查不到模型时按收(交给协议层过滤)。 */
96
+ export declare function modelAcceptsImages(state: ComposeState, model: ModelRef | undefined): boolean;
85
97
  /** 第 14 步前:内置 deny(builtinDeny 过滤)+ 用户规则;非法规则 warning 后跳过。 */
86
98
  export declare function buildRules(specs: readonly {
87
99
  effect: "allow" | "deny";
@@ -22,19 +22,22 @@
22
22
  * 第 12 步建注册表时调用,产物以 `builtin` 来源注册(受预设管理);工厂拿不到会话对象本身,执行期
23
23
  * 需要会话时用 `ctx.session()`,嵌套调用其它工具用 `ToolContext.tools.executeTool`(走完整门禁)。
24
24
  */
25
+ import { formatModelRef } from "../ai/providers/channels.js";
25
26
  import { PermissionPipeline } from "../permissions/pipeline.js";
26
27
  import { BUILTIN_DENY_RULES, parseRule } from "../permissions/rules.js";
27
28
  import { discoverSkills, skillSources } from "../skills/discover.js";
28
29
  import { discoverPromptTemplates, promptSources } from "../skills/templates.js";
29
- import { applyCodemodeMode, decorateForMode } from "../codemode/modes.js";
30
+ import { applyCodemodeMode } from "../codemode/modes.js";
31
+ import { detectSandboxCapability } from "../codemode/capability.js";
30
32
  import { codemodeToolFactory } from "../codemode/tool.js";
31
33
  import { PresetToolRegistry, resolvePreset } from "../tools/presets.js";
32
34
  import { builtinTools } from "../tools/registry.js";
35
+ import { takeCodemodeNotice } from "./codemode-notice.js";
33
36
  import { buildProviderRegistry } from "./compose-providers.js";
34
37
  import { composeSession, emptyComposeState, } from "./compose-session.js";
35
38
  import { createSessionStore } from "./compose-store.js";
36
39
  import { defaultSendUser } from "./startup-steps.js";
37
- /** 缺省工具工厂:codemode(`codemode.mode` 生效值为 off 时不注册)。 */
40
+ /** 缺省工具工厂:codemode(生效模式为 off 时不注册;缺省跟随预设,见 tools/presets.ts)。 */
38
41
  export const DEFAULT_TOOL_FACTORIES = [codemodeToolFactory()];
39
42
  export function lazyMode(load) {
40
43
  return async (runtime, context) => (await load())(runtime, context);
@@ -49,16 +52,24 @@ export const DEFAULT_MODES = {
49
52
  /**
50
53
  * 第 12 步:内置工具 + 工厂 + extraTools,按预设定活动集;warning 留给组装会话时报告。
51
54
  *
52
- * 先跑工厂(产物暂不注册)再解析预设:codemode 是否可用决定预设与模式;`on` 模式下注册时给
53
- * 其它工具的描述追加 codemode 提示,`only` 模式活动集独占(codemode/modes.ts)。工厂拿到的
54
- * `registry` 在执行期才读,此时已登记完全部工具。
55
+ * 先跑工厂(产物暂不注册)再解析预设:codemode 是否可用决定预设与模式;`only` 模式活动集独占
56
+ * (codemode/modes.ts)。工厂拿到的 `registry` 在执行期才读,此时已登记完全部工具。default 预设
57
+ * 跟随预设而 codemode 缺省关闭时(非 strict 运行时)提示一次(cli/codemode-notice.ts)。
55
58
  */
56
59
  export function createTools(input, options, state) {
57
60
  const registry = new PresetToolRegistry();
61
+ const capability = options.sandboxCapability ?? detectSandboxCapability();
62
+ const factories = options.toolFactories ??
63
+ (options.sandboxCapability !== undefined
64
+ ? [codemodeToolFactory({ capability })]
65
+ : DEFAULT_TOOL_FACTORIES);
58
66
  const bash = input.config.tools?.bashTimeoutMs !== undefined
59
67
  ? { defaultTimeoutMs: input.config.tools.bashTimeoutMs }
60
68
  : {};
61
- const builtins = builtinTools({ bash });
69
+ const builtins = builtinTools({
70
+ bash,
71
+ read: { supportsImages: (ctx) => modelAcceptsImages(state, ctx.model) },
72
+ });
62
73
  const ctx = {
63
74
  ...input,
64
75
  registry,
@@ -66,7 +77,7 @@ export function createTools(input, options, state) {
66
77
  warn: (message) => state.warnings.push(message),
67
78
  };
68
79
  const produced = [];
69
- for (const factory of options.toolFactories ?? DEFAULT_TOOL_FACTORIES) {
80
+ for (const factory of factories) {
70
81
  try {
71
82
  const tool = factory(ctx);
72
83
  if (tool !== undefined)
@@ -78,22 +89,38 @@ export function createTools(input, options, state) {
78
89
  }
79
90
  const extra = options.extraTools ?? [];
80
91
  const names = new Set([...builtins, ...produced, ...extra].map((tool) => tool.name));
81
- const preset = resolvePreset({ config: input.config, available: (name) => names.has(name) });
82
- const decorate = decorateForMode(preset.codemode);
92
+ const preset = resolvePreset({
93
+ config: input.config,
94
+ available: (name) => names.has(name),
95
+ strict: capability.strict,
96
+ });
83
97
  for (const tool of [...builtins, ...produced]) {
84
98
  try {
85
- registry.register(decorate(tool), "builtin");
99
+ registry.register(tool, "builtin");
86
100
  }
87
101
  catch (error) {
88
102
  state.warnings.push(`工具 ${tool.name} 注册失败:${error.message}`);
89
103
  }
90
104
  }
91
105
  for (const tool of extra)
92
- registry.register(decorate(tool), "sdk");
106
+ registry.register(tool, "sdk");
93
107
  applyCodemodeMode(registry, preset);
94
108
  state.warnings.push(...preset.warnings);
109
+ // 一次性提示只给有人看的界面(交互 / 行式);-p 与 RPC 的 stderr 常被脚本解析,不打扰。
110
+ if (input.paths !== undefined && (input.mode === "interactive" || input.mode === "line")) {
111
+ const notice = takeCodemodeNotice({ config: input.config, capability, ...input.paths });
112
+ if (notice !== undefined)
113
+ state.warnings.push(notice);
114
+ }
95
115
  return registry;
96
116
  }
117
+ /** 当前模型是否收图片(read 工具用);查不到模型时按收(交给协议层过滤)。 */
118
+ export function modelAcceptsImages(state, model) {
119
+ if (model === undefined || state.providers === undefined)
120
+ return true;
121
+ const found = state.providers.findModel(formatModelRef(model));
122
+ return found.ok ? found.model.input.includes("image") : true;
123
+ }
97
124
  /** 第 14 步前:内置 deny(builtinDeny 过滤)+ 用户规则;非法规则 warning 后跳过。 */
98
125
  export function buildRules(specs, builtinDeny, warn) {
99
126
  const rules = [];
@@ -142,14 +169,18 @@ export function createRuntimeDeps(options = {}) {
142
169
  const state = emptyComposeState();
143
170
  const deps = {
144
171
  providers: {
145
- create: (input) => buildProviderRegistry(input, {
146
- providers: options.providers,
147
- apis: options.apis,
148
- env: options.env,
149
- includeFake: options.includeFake,
150
- probeLocal: options.probeLocal,
151
- warn: (message) => state.warnings.push(message),
152
- }),
172
+ create: async (input) => {
173
+ const registry = await buildProviderRegistry(input, {
174
+ providers: options.providers,
175
+ apis: options.apis,
176
+ env: options.env,
177
+ includeFake: options.includeFake,
178
+ probeLocal: options.probeLocal,
179
+ warn: (message) => state.warnings.push(message),
180
+ });
181
+ state.providers = registry;
182
+ return registry;
183
+ },
153
184
  },
154
185
  sessions: createSessionStore(),
155
186
  tools: { create: (input) => createTools(input, options, state) },
@@ -158,6 +189,9 @@ export function createRuntimeDeps(options = {}) {
158
189
  mode: input.mode,
159
190
  rules: buildRules(input.rules, input.builtinDeny, (m) => state.warnings.push(m)),
160
191
  cwd: input.cwd,
192
+ ...(input.autoSafeCommands !== undefined
193
+ ? { autoSafeCommands: input.autoSafeCommands }
194
+ : {}),
161
195
  }),
162
196
  },
163
197
  resources: {
@@ -1,16 +1,53 @@
1
1
  /**
2
2
  * 零配置选模型(设计 §10.0、§11.1 第 11 步):没有 `--model`、续会话的 model_change 与
3
- * `config.defaultModel` 时,按内置供应商顺序(§3.3)选第一个**可用**供应商的缺省模型(目录首条)。
3
+ * `config.defaultModel` 时,按供应商顺序(内置 §3.3 在前,config 里的自定义供应商在后)选第一个
4
+ * **可用**供应商,再在它的模型里挑一个:
5
+ *
6
+ * - 内置供应商:目录首条(目录按推荐顺序整理过);
7
+ * - 自定义供应商(中转站,模型表是上游 /v1/models 的顺序):按 `pickByPrice` 的规则挑——
8
+ * models.dev 有价格(输入价 > 0)且支持工具调用、上下文 ≥ 64k 的模型里输入价最低的;同价取上下文
9
+ * 大的,再同取列表靠前的。没有满足条件的才退回列表首条。`ama providers add` 写 defaultModel 用同一规则。
4
10
  *
5
11
  * 可用 = 有 key(`requiresApiKey`)或本地服务已探测到模型(ollama / lmstudio,组装根在
6
12
  * 无 key 时探测)。跳过 `fake`(测试供应商只能显式 `--model fake/…`)与协议尚未实现的供应商。
7
13
  */
8
14
  import type { Model, ProviderData, ProviderRegistryApi } from "../ai/types.js";
15
+ /** 缺省模型的最小上下文(token)。 */
16
+ export declare const MIN_DEFAULT_CONTEXT = 64000;
17
+ export interface PriceCandidate {
18
+ id: string;
19
+ contextWindow?: number | undefined;
20
+ /** $/M 输入 token。 */
21
+ inputCost?: number | undefined;
22
+ /** models.dev 的 tool_call。 */
23
+ toolCall?: boolean | undefined;
24
+ }
25
+ export interface PricePick {
26
+ id: string;
27
+ /** 为什么选它(一句话)。 */
28
+ reason: string;
29
+ }
30
+ /** 规则说明(帮助与输出共用)。 */
31
+ export declare const PRICE_RULE = "\u652F\u6301\u5DE5\u5177\u8C03\u7528\u3001\u4E0A\u4E0B\u6587 \u2265 64k \u4E14\u6709\u4EF7\u683C\u7684\u6A21\u578B\u91CC\u8F93\u5165\u4EF7\u6700\u4F4E";
32
+ /** 按价格挑缺省模型;没有满足条件的返回 undefined(规则见文件头)。 */
33
+ export declare function pickByPrice(candidates: readonly PriceCandidate[]): PricePick | undefined;
9
34
  export interface DefaultModelChoice {
10
35
  model: Model;
11
36
  provider: ProviderData;
12
37
  /** 选中原因:`key`(有 key,来源见 keySource)或 `local`(本地服务)。 */
13
38
  via: "key" | "local";
14
39
  keySource?: string;
40
+ /** 为什么是这个模型(内置目录首条 / 价格规则 / 列表首条)。 */
41
+ rule: string;
15
42
  }
43
+ /** 供应商内挑模型:内置取目录首条;自定义按价格规则,挑不出再取首条。 */
44
+ export declare function pickProviderModel(registry: ProviderRegistryApi, provider: ProviderData): {
45
+ model: Model;
46
+ rule: string;
47
+ } | undefined;
16
48
  export declare function pickDefaultModel(registry: ProviderRegistryApi): Promise<DefaultModelChoice | undefined>;
49
+ /**
50
+ * 没有可用模型时的引导(一行,选择器的说明行与报错共用):列出前几个内置供应商的 key 环境变量,
51
+ * 以及 `ama auth set` / `ama providers add`。
52
+ */
53
+ export declare function noModelGuidance(registry: Pick<ProviderRegistryApi, "list">): string;
@@ -1,24 +1,111 @@
1
1
  /**
2
2
  * 零配置选模型(设计 §10.0、§11.1 第 11 步):没有 `--model`、续会话的 model_change 与
3
- * `config.defaultModel` 时,按内置供应商顺序(§3.3)选第一个**可用**供应商的缺省模型(目录首条)。
3
+ * `config.defaultModel` 时,按供应商顺序(内置 §3.3 在前,config 里的自定义供应商在后)选第一个
4
+ * **可用**供应商,再在它的模型里挑一个:
5
+ *
6
+ * - 内置供应商:目录首条(目录按推荐顺序整理过);
7
+ * - 自定义供应商(中转站,模型表是上游 /v1/models 的顺序):按 `pickByPrice` 的规则挑——
8
+ * models.dev 有价格(输入价 > 0)且支持工具调用、上下文 ≥ 64k 的模型里输入价最低的;同价取上下文
9
+ * 大的,再同取列表靠前的。没有满足条件的才退回列表首条。`ama providers add` 写 defaultModel 用同一规则。
4
10
  *
5
11
  * 可用 = 有 key(`requiresApiKey`)或本地服务已探测到模型(ollama / lmstudio,组装根在
6
12
  * 无 key 时探测)。跳过 `fake`(测试供应商只能显式 `--model fake/…`)与协议尚未实现的供应商。
7
13
  */
14
+ import { compactTokens, metadataOf } from "./subcommands/model-meta.js";
15
+ /** 缺省模型的最小上下文(token)。 */
16
+ export const MIN_DEFAULT_CONTEXT = 64_000;
17
+ /** 规则说明(帮助与输出共用)。 */
18
+ export const PRICE_RULE = "支持工具调用、上下文 ≥ 64k 且有价格的模型里输入价最低";
19
+ /** 按价格挑缺省模型;没有满足条件的返回 undefined(规则见文件头)。 */
20
+ export function pickByPrice(candidates) {
21
+ let best;
22
+ candidates.forEach((c, index) => {
23
+ if (c.toolCall !== true)
24
+ return;
25
+ if (c.inputCost === undefined || !(c.inputCost > 0))
26
+ return;
27
+ if ((c.contextWindow ?? 0) < MIN_DEFAULT_CONTEXT)
28
+ return;
29
+ if (best === undefined) {
30
+ best = { c, index };
31
+ return;
32
+ }
33
+ const a = best.c;
34
+ const cheaper = c.inputCost < a.inputCost;
35
+ const same = c.inputCost === a.inputCost;
36
+ if (cheaper || (same && (c.contextWindow ?? 0) > (a.contextWindow ?? 0)))
37
+ best = { c, index };
38
+ });
39
+ if (best === undefined)
40
+ return undefined;
41
+ const { c } = best;
42
+ return {
43
+ id: c.id,
44
+ reason: `${PRICE_RULE}($${c.inputCost}/M 输入,上下文 ${compactTokens(c.contextWindow)})`,
45
+ };
46
+ }
47
+ /** 供应商内挑模型:内置取目录首条;自定义按价格规则,挑不出再取首条。 */
48
+ export function pickProviderModel(registry, provider) {
49
+ const usable = provider.models.filter((m) => registry.getApi(m.api) !== undefined);
50
+ const first = usable[0];
51
+ if (first === undefined)
52
+ return undefined;
53
+ if (provider.builtin)
54
+ return { model: first, rule: "内置目录推荐的首个模型" };
55
+ const picked = pickByPrice(usable.map((m) => ({
56
+ id: m.id,
57
+ contextWindow: m.contextWindow,
58
+ inputCost: m.cost?.input,
59
+ toolCall: metadataOf(registry, provider.id, m.id)?.toolCall,
60
+ })));
61
+ const model = picked === undefined ? undefined : usable.find((m) => m.id === picked.id);
62
+ if (picked !== undefined && model !== undefined)
63
+ return { model, rule: picked.reason };
64
+ return {
65
+ model: first,
66
+ rule: "列表首个模型(没有同时支持工具调用、上下文 ≥ 64k 且有价格的模型)",
67
+ };
68
+ }
8
69
  export async function pickDefaultModel(registry) {
9
70
  for (const provider of registry.list()) {
10
71
  if (provider.id === "fake" && !provider.requiresApiKey)
11
72
  continue;
12
- const model = provider.models[0];
13
- if (model === undefined)
73
+ const head = provider.models[0];
74
+ if (head === undefined)
75
+ continue;
76
+ if (registry.getApi(head.api) === undefined)
14
77
  continue;
15
- if (registry.getApi(model.api) === undefined)
78
+ let via = "local";
79
+ let keySource;
80
+ if (provider.requiresApiKey) {
81
+ const key = await registry.resolveApiKey(provider.id);
82
+ if (key.apiKey === undefined)
83
+ continue;
84
+ via = "key";
85
+ keySource = key.source;
86
+ }
87
+ const picked = pickProviderModel(registry, provider);
88
+ if (picked === undefined)
16
89
  continue;
17
- if (!provider.requiresApiKey)
18
- return { model, provider, via: "local" };
19
- const key = await registry.resolveApiKey(provider.id);
20
- if (key.apiKey !== undefined)
21
- return { model, provider, via: "key", keySource: key.source };
90
+ const choice = { model: picked.model, provider, via, rule: picked.rule };
91
+ if (keySource !== undefined)
92
+ choice.keySource = keySource;
93
+ return choice;
22
94
  }
23
95
  return undefined;
24
96
  }
97
+ /**
98
+ * 没有可用模型时的引导(一行,选择器的说明行与报错共用):列出前几个内置供应商的 key 环境变量,
99
+ * 以及 `ama auth set` / `ama providers add`。
100
+ */
101
+ export function noModelGuidance(registry) {
102
+ const envs = registry
103
+ .list()
104
+ .filter((p) => p.builtin && p.requiresApiKey && p.id !== "fake")
105
+ .map((p) => p.envKeys[0])
106
+ .filter((name) => name !== undefined);
107
+ const shown = envs.slice(0, 3).join(" / ");
108
+ const more = envs.length > 3 ? " 等" : "";
109
+ return (`没有可用模型:设置 ${shown}${more}环境变量,或 \`ama auth set <provider>\` 保存 key,` +
110
+ "或 `ama providers add <id> --base-url <url>` 接入中转站(也可 --model 指定)");
111
+ }
@@ -37,6 +37,10 @@ export type SessionRequest = {
37
37
  } | {
38
38
  kind: "fork";
39
39
  id: string;
40
+ }
41
+ /** `--no-session`:内存会话,从不落盘。 */
42
+ | {
43
+ kind: "memory";
40
44
  };
41
45
  export interface ProviderBuildInput {
42
46
  config: AmaConfig;
@@ -51,6 +55,8 @@ export interface ProviderBuildInput {
51
55
  modelRef: string;
52
56
  provider?: string;
53
57
  };
58
+ /** 数据目录:读 models.dev 缓存(`<dataDir>/models-dev.json`,只读不联网);缺省不补元数据。 */
59
+ dataDir?: string;
54
60
  }
55
61
  export interface ResourceDiscoveryInput {
56
62
  cwd: string;
@@ -105,8 +111,22 @@ export interface SessionAssembly {
105
111
  sessionStartContext(): string | undefined;
106
112
  /** print:ask → deny。 */
107
113
  unattended: boolean;
114
+ /** 命令行对会话的覆盖(W4-C):`-p --max-turns`、`--system-prompt`、`--no-session`。 */
115
+ overrides?: SessionOverrides;
108
116
  warn(message: string): void;
109
117
  }
118
+ export interface SessionOverrides {
119
+ /** 一次运行(prompt)的回合上限。 */
120
+ maxTurns?: number;
121
+ /** `--system-prompt`:追加进 rules 节,或替换 preamble。 */
122
+ systemPrompt?: SystemPromptOverride;
123
+ /** `--no-session`:`/new` 等切换出的新会话也只在内存里。 */
124
+ noSession?: boolean;
125
+ }
126
+ export interface SystemPromptOverride {
127
+ text: string;
128
+ mode: "append" | "replace";
129
+ }
110
130
  export interface ModeContext {
111
131
  args: ParsedArgs;
112
132
  /** 位置参数拼成的首条提示。 */
@@ -164,6 +184,11 @@ export interface RuntimeDeps {
164
184
  config: AmaConfig;
165
185
  cwd: string;
166
186
  mode: RuntimeMode;
187
+ /** CLI 启动时给出(一次性提示记在数据目录);SDK 不给。 */
188
+ paths?: {
189
+ configDir: string;
190
+ dataDir: string;
191
+ };
167
192
  }): ToolRegistryApi;
168
193
  };
169
194
  /** B3:权限管线。 */
@@ -176,6 +201,8 @@ export interface RuntimeDeps {
176
201
  cwd: string;
177
202
  /** config `permission.builtinDeny`(只来自用户级 / profile);缺省 = 启用全部内置 deny。 */
178
203
  builtinDeny?: boolean | string[];
204
+ /** config `permission.autoSafeCommands`(auto 模式安全名单追加)。 */
205
+ autoSafeCommands?: string[];
179
206
  }): PermissionPipelineApi;
180
207
  };
181
208
  /** B3:skills / prompts 发现;缺省为空。 */
@@ -202,4 +229,15 @@ export interface CliIo {
202
229
  cwd: string;
203
230
  /** 读取 stdin 全部内容(auth set)。 */
204
231
  readStdin(): Promise<string>;
232
+ /**
233
+ * 等首字节至多 `timeoutMs`:期间没有任何字节 → undefined(停止读取,不再占着 stdin);收到首字节
234
+ * 后读到 EOF(不再超时);期间就 EOF → 已读到的内容。缺省实现见 cli/main.ts。
235
+ */
236
+ readStdinFirstByte?(timeoutMs: number): Promise<string | undefined>;
237
+ /**
238
+ * stdin 的类型(`-p` 据此决定读不读):`file` / `null`(/dev/null 等字符设备)读了不会卡;
239
+ * `fifo` 是 shell 管道;`socket` 多为父进程 spawn 时留的管道。未提供时按 `other` 处理(不读、不提示)。
240
+ */
241
+ stdinKind?(): StdinKind;
205
242
  }
243
+ export type StdinKind = "tty" | "file" | "null" | "fifo" | "socket" | "other";
@@ -16,6 +16,8 @@ export declare const ExitCode: {
16
16
  readonly Session: 5;
17
17
  /** 宿主 / Hook 加载或启动失败。 */
18
18
  readonly HostOrHook: 6;
19
+ /** `-p` 运行中有工具调用被拒(无人审批、deny 规则、plan 等),要求的动作没有完成。 */
20
+ readonly ToolDenied: 7;
19
21
  /** HOST_API_VERSION 不匹配。 */
20
22
  readonly HostVersion: 78;
21
23
  /** SIGINT 退出(两次 Ctrl+C)。 */
@@ -16,6 +16,8 @@ export const ExitCode = {
16
16
  Session: 5,
17
17
  /** 宿主 / Hook 加载或启动失败。 */
18
18
  HostOrHook: 6,
19
+ /** `-p` 运行中有工具调用被拒(无人审批、deny 规则、plan 等),要求的动作没有完成。 */
20
+ ToolDenied: 7,
19
21
  /** HOST_API_VERSION 不匹配。 */
20
22
  HostVersion: 78,
21
23
  /** SIGINT 退出(两次 Ctrl+C)。 */
@@ -31,6 +33,7 @@ export const EXIT_CODE_DESCRIPTIONS = {
31
33
  4: "无可用模型或密钥",
32
34
  5: "会话不存在 / 损坏 / cwd 不匹配",
33
35
  6: "宿主 / Hook 加载或启动失败",
36
+ 7: "-p 运行中有工具调用被拒(没有人审批)",
34
37
  78: "HOST_API_VERSION 不匹配",
35
38
  130: "SIGINT 退出(两次 Ctrl+C)",
36
39
  143: "SIGTERM",
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 测试供应商 `fake` 的可见性:零配置的模型选择器、`ama doctor`、`ama models list`、`ama providers list`、
3
+ * `ama config show` 缺省不列出它(实测零配置 TUI 的首项是 `fake › echo`,直接回车就用上了测试供应商)。
4
+ *
5
+ * - `AMA_SHOW_FAKE=1` 或设置了 `AMA_FAKE_SCRIPT` 时照常列出;
6
+ * - 只藏 `list()`:`--model fake/…`、`findModel`、`resolveApiKey` 等显式引用不受影响。
7
+ */
8
+ import type { ProviderRegistryApi } from "../ai/types.js";
9
+ export declare const SHOW_FAKE_ENV = "AMA_SHOW_FAKE";
10
+ export declare const FAKE_PROVIDER_ID = "fake";
11
+ export declare function fakeVisible(env: Readonly<Record<string, string | undefined>>): boolean;
12
+ /** 列表里藏起 fake 的注册表视图(其余方法原样转给原注册表);可见时原样返回。 */
13
+ export declare function hideFakeProvider<T extends ProviderRegistryApi>(registry: T, env: Readonly<Record<string, string | undefined>>): T;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * 测试供应商 `fake` 的可见性:零配置的模型选择器、`ama doctor`、`ama models list`、`ama providers list`、
3
+ * `ama config show` 缺省不列出它(实测零配置 TUI 的首项是 `fake › echo`,直接回车就用上了测试供应商)。
4
+ *
5
+ * - `AMA_SHOW_FAKE=1` 或设置了 `AMA_FAKE_SCRIPT` 时照常列出;
6
+ * - 只藏 `list()`:`--model fake/…`、`findModel`、`resolveApiKey` 等显式引用不受影响。
7
+ */
8
+ import { FAKE_SCRIPT_ENV } from "../ai/fake/fake-provider.js";
9
+ export const SHOW_FAKE_ENV = "AMA_SHOW_FAKE";
10
+ export const FAKE_PROVIDER_ID = "fake";
11
+ export function fakeVisible(env) {
12
+ return env[SHOW_FAKE_ENV] === "1" || (env[FAKE_SCRIPT_ENV] ?? "") !== "";
13
+ }
14
+ /** 列表里藏起 fake 的注册表视图(其余方法原样转给原注册表);可见时原样返回。 */
15
+ export function hideFakeProvider(registry, env) {
16
+ if (fakeVisible(env))
17
+ return registry;
18
+ return new Proxy(registry, {
19
+ get(target, prop) {
20
+ if (prop === "list")
21
+ return () => target.list().filter((provider) => provider.id !== FAKE_PROVIDER_ID);
22
+ const value = Reflect.get(target, prop, target);
23
+ return typeof value === "function" ? value.bind(target) : value;
24
+ },
25
+ });
26
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `--from <会话 id>[#编号]`:用旧会话里的一条用户消息作为新提示(docs/sessions.md「复用」)。[W4-D]
3
+ *
4
+ * - 文本:那条消息的文本;命令行另有位置参数时接在后面(空一行);
5
+ * - 图片:`-p` 时把图片块写进临时目录、追加到 `--image`(沿用 print 模式的图片校验:模型不收图片 →
6
+ * 用法错误),运行结束删掉;交互 / 行式界面只带文本,有图片时在 stderr 提示一行;
7
+ * - 交互模式经现有的「初始提示」参数传入(启动后直接提交,与 `ama "提示"` 相同);
8
+ * - `--mode rpc` 不支持(rpc 的提示来自宿主)。
9
+ * 会话目录用 bootstrap 解析后的 `paths.sessionDir`(含 `--session-dir` 与 profile),本目录优先找 id。
10
+ */
11
+ import { type ParsedArgs } from "./args.js";
12
+ import type { CliIo, ModeContext } from "./deps.js";
13
+ import type { Runtime } from "./runtime.js";
14
+ export interface FromContext {
15
+ context: ModeContext;
16
+ cleanup(): void;
17
+ }
18
+ export declare function applyFromOption(args: ParsedArgs, runtime: Pick<Runtime, "mode" | "paths">, io: CliIo): FromContext;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * `--from <会话 id>[#编号]`:用旧会话里的一条用户消息作为新提示(docs/sessions.md「复用」)。[W4-D]
3
+ *
4
+ * - 文本:那条消息的文本;命令行另有位置参数时接在后面(空一行);
5
+ * - 图片:`-p` 时把图片块写进临时目录、追加到 `--image`(沿用 print 模式的图片校验:模型不收图片 →
6
+ * 用法错误),运行结束删掉;交互 / 行式界面只带文本,有图片时在 stderr 提示一行;
7
+ * - 交互模式经现有的「初始提示」参数传入(启动后直接提交,与 `ama "提示"` 相同);
8
+ * - `--mode rpc` 不支持(rpc 的提示来自宿主)。
9
+ * 会话目录用 bootstrap 解析后的 `paths.sessionDir`(含 `--session-dir` 与 profile),本目录优先找 id。
10
+ */
11
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
14
+ import { resolveFromMessage } from "../session/reuse.js";
15
+ import { UsageError } from "./args.js";
16
+ const EXT = {
17
+ "image/png": "png",
18
+ "image/jpeg": "jpg",
19
+ "image/gif": "gif",
20
+ "image/webp": "webp",
21
+ };
22
+ export function applyFromOption(args, runtime, io) {
23
+ const spec = args.from;
24
+ const plain = { context: { args, prompt: args.prompt, io }, cleanup: () => { } };
25
+ if (spec === undefined)
26
+ return plain;
27
+ if (runtime.mode === "rpc")
28
+ throw new UsageError("--from 不用于 --mode rpc");
29
+ const picked = resolveFromMessage(runtime.paths.sessionDir, spec, runtime.paths.cwd);
30
+ const parts = [picked.text, args.prompt ?? ""].filter((p) => p.trim() !== "");
31
+ const prompt = parts.length > 0 ? parts.join("\n\n") : undefined;
32
+ if (picked.images.length === 0) {
33
+ return { context: { args, prompt, io }, cleanup: () => { } };
34
+ }
35
+ if (runtime.mode !== "print") {
36
+ io.stderr(`ama: --from 的消息带 ${picked.images.length} 张图片,交互界面只带文本(-p 会一并发送)\n`);
37
+ return { context: { args, prompt, io }, cleanup: () => { } };
38
+ }
39
+ const dir = mkdtempSync(join(tmpdir(), "ama-from-"));
40
+ const files = picked.images.map((image, i) => {
41
+ const file = join(dir, `${picked.n}-${i + 1}.${EXT[image.mimeType] ?? "png"}`);
42
+ writeFileSync(file, Buffer.from(image.data, "base64"), { mode: 0o600 });
43
+ return file;
44
+ });
45
+ return {
46
+ context: { args: { ...args, images: [...args.images, ...files] }, prompt, io },
47
+ cleanup: () => rmSync(dir, { recursive: true, force: true }),
48
+ };
49
+ }
@@ -4,14 +4,18 @@
4
4
  *
5
5
  * - 设 `AMA=1`、`AI_AGENT=ama`;直接执行时装 `uncaughtException` / `unhandledRejection` →
6
6
  * stderr 一行 + 退出码 1;SIGINT / SIGTERM 交给当前模式。
7
- * - `--version` / `--help` 短路;子命令 `auth / sessions / models / doctor / config` 分派后返回;
7
+ * - `--version` / `--help` 短路;子命令 `auth / sessions / models / providers / doctor / config /
8
+ * init` 分派后返回;进入对话的命令(与 `providers add`)启动前若配置目录不存在则静默初始化
9
+ * (`AMA_NO_INIT=1` 关闭),只读子命令(`config show`、`doctor`、`models list` 等)不写配置目录;
8
10
  * 其余交给 `runCli()`(bootstrap → 模式)。
11
+ * - 设了 HTTPS_PROXY / HTTP_PROXY 时启用 Node 内置的环境变量代理(cli/proxy.ts);不支持的 Node
12
+ * 版本在会联网的命令里提示一次。
9
13
  * - 运行时实现(RuntimeDeps):`MainOptions.deps` > `registerRuntimeDeps()` > 组装根
10
14
  * `createRuntimeDeps()`(cli/compose.ts,动态 import)。
11
15
  * - 签名 `main(argv): Promise<number>` 与「直接执行才自动运行」判定保持不变:
12
16
  * src/bundle.ts 显式调用 `main()`。
13
17
  */
14
- import type { CliIo, RuntimeDeps } from "./deps.js";
18
+ import type { CliIo, RuntimeDeps, StdinKind } from "./deps.js";
15
19
  /** 集成批次注入运行时实现(B1 / B2 / B3 / B6 / B7)。 */
16
20
  export declare function registerRuntimeDeps(deps: RuntimeDeps | undefined): void;
17
21
  export interface MainOptions {
@@ -22,5 +26,9 @@ export interface MainOptions {
22
26
  }
23
27
  /** 读 stdin 全部内容;TTY 下只读一行且不回显(auth set)。 */
24
28
  export declare function readStdinDefault(): Promise<string>;
29
+ /** fd 0 的类型;fstat 失败(已关闭)按 `null` 处理。 */
30
+ export declare function stdinKindDefault(): StdinKind;
31
+ /** 等 stdin 首字节至多 `timeoutMs`;超时则停止读取并返回 undefined,否则读到 EOF。 */
32
+ export declare function readStdinFirstByteDefault(timeoutMs: number): Promise<string | undefined>;
25
33
  export declare function defaultIo(): CliIo;
26
34
  export declare function main(argv: readonly string[], options?: MainOptions): Promise<number>;