@armadra/agent 0.3.0 → 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.
- package/CHANGELOG.md +84 -0
- package/README.md +93 -44
- package/dist/agent/retry.d.ts +1 -1
- package/dist/agent/retry.js +2 -1
- package/dist/agent/session-cache.js +1 -1
- package/dist/agent/session-classifier.d.ts +18 -0
- package/dist/agent/session-classifier.js +103 -0
- package/dist/agent/session-core.d.ts +13 -0
- package/dist/agent/session-tools.js +24 -3
- package/dist/agent/session.d.ts +3 -0
- package/dist/agent/session.js +12 -3
- package/dist/agent/tool-runner.js +15 -2
- package/dist/agent/types.d.ts +9 -1
- package/dist/ai/apis/anthropic-messages.js +3 -2
- package/dist/ai/apis/google-generative-ai.js +3 -2
- package/dist/ai/apis/openai-completions.js +3 -2
- package/dist/ai/apis/openai-responses.js +3 -2
- package/dist/ai/http.d.ts +28 -6
- package/dist/ai/http.js +41 -8
- package/dist/ai/providers/registry.d.ts +8 -1
- package/dist/ai/providers/registry.js +30 -22
- package/dist/ai/providers/suggest.d.ts +18 -0
- package/dist/ai/providers/suggest.js +72 -0
- package/dist/ai/sse.d.ts +6 -2
- package/dist/ai/sse.js +22 -2
- package/dist/ai/types.d.ts +9 -2
- package/dist/bundle/ama.cjs +15279 -10151
- package/dist/cli/args.d.ts +19 -4
- package/dist/cli/args.js +83 -10
- package/dist/cli/bootstrap.js +27 -3
- package/dist/cli/codemode-notice.d.ts +20 -0
- package/dist/cli/codemode-notice.js +55 -0
- package/dist/cli/compose-session.d.ts +5 -0
- package/dist/cli/compose-session.js +32 -1
- package/dist/cli/compose-store.d.ts +1 -1
- package/dist/cli/compose-store.js +3 -1
- package/dist/cli/compose.d.ts +14 -4
- package/dist/cli/compose.js +29 -10
- package/dist/cli/default-model.d.ts +38 -1
- package/dist/cli/default-model.js +96 -9
- package/dist/cli/deps.d.ts +36 -0
- package/dist/cli/exit-codes.d.ts +2 -0
- package/dist/cli/exit-codes.js +3 -0
- package/dist/cli/fake-visibility.d.ts +13 -0
- package/dist/cli/fake-visibility.js +26 -0
- package/dist/cli/from-prompt.d.ts +18 -0
- package/dist/cli/from-prompt.js +49 -0
- package/dist/cli/main.d.ts +9 -2
- package/dist/cli/main.js +77 -3
- package/dist/cli/proxy.d.ts +51 -0
- package/dist/cli/proxy.js +135 -0
- package/dist/cli/startup-screen.d.ts +29 -0
- package/dist/cli/startup-screen.js +47 -0
- package/dist/cli/startup-steps.d.ts +1 -1
- package/dist/cli/startup-steps.js +14 -12
- package/dist/cli/subcommands/config.d.ts +19 -3
- package/dist/cli/subcommands/config.js +108 -21
- package/dist/cli/subcommands/context.js +4 -1
- package/dist/cli/subcommands/doctor.js +12 -1
- package/dist/cli/subcommands/init.js +2 -1
- package/dist/cli/subcommands/models-discover.d.ts +15 -9
- package/dist/cli/subcommands/models-discover.js +53 -46
- package/dist/cli/subcommands/probe-runner.d.ts +96 -0
- package/dist/cli/subcommands/probe-runner.js +264 -0
- package/dist/cli/subcommands/providers-probe.d.ts +34 -0
- package/dist/cli/subcommands/providers-probe.js +87 -0
- package/dist/cli/subcommands/providers.d.ts +3 -2
- package/dist/cli/subcommands/providers.js +48 -45
- package/dist/cli/subcommands/sessions-export.d.ts +10 -0
- package/dist/cli/subcommands/sessions-export.js +59 -0
- package/dist/cli/subcommands/sessions-search.d.ts +13 -0
- package/dist/cli/subcommands/sessions-search.js +103 -0
- package/dist/cli/subcommands/sessions.d.ts +3 -2
- package/dist/cli/subcommands/sessions.js +22 -1
- package/dist/cli/subcommands/stats.d.ts +17 -0
- package/dist/cli/subcommands/stats.js +198 -0
- package/dist/cli/system-prompt-arg.d.ts +11 -0
- package/dist/cli/system-prompt-arg.js +34 -0
- package/dist/codemode/modes.d.ts +4 -11
- package/dist/codemode/modes.js +5 -27
- package/dist/codemode/tool.d.ts +18 -14
- package/dist/codemode/tool.js +71 -28
- package/dist/config/init.d.ts +6 -2
- package/dist/config/init.js +12 -5
- package/dist/config/json-schema.d.ts +1 -0
- package/dist/config/json-schema.js +36 -3
- package/dist/config/key-docs.d.ts +22 -0
- package/dist/config/key-docs.js +105 -0
- package/dist/config/merge.d.ts +9 -8
- package/dist/config/merge.js +22 -7
- package/dist/config/schema.d.ts +1 -1
- package/dist/config/schema.js +24 -5
- package/dist/config/types.d.ts +34 -6
- package/dist/config/types.js +17 -1
- package/dist/modes/commands-core.js +6 -5
- package/dist/modes/interactive/approval-dialog.d.ts +21 -5
- package/dist/modes/interactive/approval-dialog.js +106 -27
- package/dist/modes/interactive/commands.d.ts +11 -2
- package/dist/modes/interactive/commands.js +56 -16
- package/dist/modes/interactive/interactive-mode.d.ts +2 -1
- package/dist/modes/interactive/interactive-mode.js +65 -81
- package/dist/modes/interactive/key-dispatch.d.ts +3 -1
- package/dist/modes/interactive/key-dispatch.js +5 -6
- package/dist/modes/interactive/line/line-mode.d.ts +1 -0
- package/dist/modes/interactive/line/line-mode.js +6 -4
- package/dist/modes/interactive/line/line-render.d.ts +4 -0
- package/dist/modes/interactive/line/line-render.js +28 -6
- package/dist/modes/interactive/message-view.d.ts +48 -9
- package/dist/modes/interactive/message-view.js +238 -44
- package/dist/modes/interactive/panels.d.ts +18 -0
- package/dist/modes/interactive/panels.js +143 -0
- package/dist/modes/interactive/pickers.d.ts +23 -2
- package/dist/modes/interactive/pickers.js +48 -15
- package/dist/modes/interactive/run-indicator.d.ts +51 -0
- package/dist/modes/interactive/run-indicator.js +189 -0
- package/dist/modes/interactive/startup-header.d.ts +40 -0
- package/dist/modes/interactive/startup-header.js +169 -0
- package/dist/modes/interactive/status-bar.d.ts +18 -15
- package/dist/modes/interactive/status-bar.js +98 -56
- package/dist/modes/interactive/tool-summary.d.ts +46 -0
- package/dist/modes/interactive/tool-summary.js +218 -0
- package/dist/modes/interactive/tool-view.d.ts +48 -15
- package/dist/modes/interactive/tool-view.js +203 -145
- package/dist/modes/print/print-mode.d.ts +31 -4
- package/dist/modes/print/print-mode.js +116 -7
- package/dist/modes/rpc/commands.js +5 -0
- package/dist/permissions/auto-safe.d.ts +60 -0
- package/dist/permissions/auto-safe.js +529 -0
- package/dist/permissions/classifier.d.ts +64 -0
- package/dist/permissions/classifier.js +184 -0
- package/dist/permissions/dangerous.d.ts +5 -0
- package/dist/permissions/dangerous.js +1 -1
- package/dist/permissions/modes.d.ts +30 -0
- package/dist/permissions/modes.js +78 -0
- package/dist/permissions/pipeline.d.ts +31 -4
- package/dist/permissions/pipeline.js +196 -6
- package/dist/permissions/protected.d.ts +19 -0
- package/dist/permissions/protected.js +74 -0
- package/dist/permissions/rules.js +3 -0
- package/dist/permissions/types.d.ts +50 -3
- package/dist/permissions/types.js +3 -0
- package/dist/sdk.d.ts +9 -3
- package/dist/sdk.js +10 -2
- package/dist/session/export.d.ts +32 -0
- package/dist/session/export.js +187 -0
- package/dist/session/redact.d.ts +15 -0
- package/dist/session/redact.js +55 -0
- package/dist/session/reuse.d.ts +33 -0
- package/dist/session/reuse.js +86 -0
- package/dist/session/scan.d.ts +34 -0
- package/dist/session/scan.js +140 -0
- package/dist/session/search.d.ts +52 -0
- package/dist/session/search.js +211 -0
- package/dist/session/stats-aggregate.d.ts +63 -0
- package/dist/session/stats-aggregate.js +163 -0
- package/dist/session/stats-index.d.ts +26 -0
- package/dist/session/stats-index.js +91 -0
- package/dist/session/stats-scan.d.ts +54 -0
- package/dist/session/stats-scan.js +236 -0
- package/dist/tools/presets.d.ts +35 -8
- package/dist/tools/presets.js +56 -17
- package/dist/tui/component.d.ts +8 -2
- package/dist/tui/component.js +3 -1
- package/dist/tui/components/box.d.ts +6 -1
- package/dist/tui/components/box.js +16 -6
- package/dist/tui/components/card.d.ts +23 -0
- package/dist/tui/components/card.js +37 -0
- package/dist/tui/components/editor-history.d.ts +6 -0
- package/dist/tui/components/editor-history.js +45 -0
- package/dist/tui/components/editor-paste.d.ts +1 -1
- package/dist/tui/components/editor-paste.js +4 -4
- package/dist/tui/components/editor.d.ts +11 -5
- package/dist/tui/components/editor.js +52 -58
- package/dist/tui/components/key-value.d.ts +3 -0
- package/dist/tui/components/key-value.js +16 -6
- package/dist/tui/components/loader.d.ts +37 -7
- package/dist/tui/components/loader.js +84 -21
- package/dist/tui/components/markdown.d.ts +5 -1
- package/dist/tui/components/markdown.js +45 -21
- package/dist/tui/components/meter.d.ts +3 -3
- package/dist/tui/components/meter.js +13 -11
- package/dist/tui/components/select-list.d.ts +23 -1
- package/dist/tui/components/select-list.js +76 -13
- package/dist/tui/glyphs.d.ts +68 -0
- package/dist/tui/glyphs.js +114 -0
- package/dist/tui/theme.d.ts +30 -6
- package/dist/tui/theme.js +103 -17
- package/dist/tui.d.ts +4 -2
- package/dist/tui.js +3 -1
- package/docs/codemode.md +23 -9
- package/docs/hooks.md +10 -10
- package/docs/permissions.md +148 -0
- package/docs/providers.md +33 -10
- package/docs/rpc.md +36 -36
- package/docs/session-format.md +2 -1
- package/docs/sessions.md +134 -0
- package/docs/tui.md +139 -62
- package/package.json +3 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama stats`:跨会话的用量统计(只读扫描会话 JSONL)。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* - 范围:缺省当前目录的会话;`--project <目录>` 指定项目;`--all` 全部(`--by project` 未指定范围时也看全部)。
|
|
5
|
+
* - 时间:`--since` / `--until` 接受 `7d`(含今天的最近 7 天)、`today`、`YYYY-MM-DD`(本地日期,含当天)。
|
|
6
|
+
* - 分组:`--by day|week|month|provider|channel|model|project`。
|
|
7
|
+
* - 缓存:`<数据目录>/stats-index.json`,按文件 mtime / size 失效;`--no-cache` 不读也不写。
|
|
8
|
+
* - 口径见 session/stats-aggregate.ts 与 docs/sessions.md「统计」。
|
|
9
|
+
*/
|
|
10
|
+
import { join, resolve } from "node:path";
|
|
11
|
+
import { expandHome, resolveDataDir } from "../../config/paths.js";
|
|
12
|
+
import { sessionFilesInScope } from "../../session/scan.js";
|
|
13
|
+
import { STATS_GROUPS, aggregateStats, } from "../../session/stats-aggregate.js";
|
|
14
|
+
import { STATS_INDEX_FILE, collectSummaries } from "../../session/stats-index.js";
|
|
15
|
+
import { localDay } from "../../session/stats-scan.js";
|
|
16
|
+
import { parseSubArgs, UsageError } from "../args.js";
|
|
17
|
+
import { ExitCode } from "../exit-codes.js";
|
|
18
|
+
export const STATS_USAGE = `用法:ama stats [--since 7d|30d|today|YYYY-MM-DD] [--until …]
|
|
19
|
+
[--by day|week|month|provider|channel|model|project]
|
|
20
|
+
[--project <目录> | --all] [--top N] [--json] [--no-cache] [--session-dir <目录>]
|
|
21
|
+
`;
|
|
22
|
+
/** `7d` / `today` / `YYYY-MM-DD` → 本地日期。 */
|
|
23
|
+
export function parseDaySpec(option, raw, now = new Date()) {
|
|
24
|
+
if (raw === "today")
|
|
25
|
+
return localDay(now.toISOString()) ?? "";
|
|
26
|
+
const rel = /^(\d+)d$/.exec(raw);
|
|
27
|
+
if (rel !== null) {
|
|
28
|
+
const days = Number(rel[1]);
|
|
29
|
+
if (days < 1)
|
|
30
|
+
throw new UsageError(`--${option} 的天数至少为 1(收到 ${raw})`);
|
|
31
|
+
const d = new Date(now);
|
|
32
|
+
d.setDate(d.getDate() - (days - 1));
|
|
33
|
+
return localDay(d.toISOString()) ?? "";
|
|
34
|
+
}
|
|
35
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(raw) && Number.isFinite(Date.parse(`${raw}T00:00:00`)))
|
|
36
|
+
return raw;
|
|
37
|
+
throw new UsageError(`--${option} 应为 7d、today 或 YYYY-MM-DD(收到 ${raw})`);
|
|
38
|
+
}
|
|
39
|
+
export function formatTokens(count) {
|
|
40
|
+
const trim = (text) => text.replace(/\.?0+$/, "");
|
|
41
|
+
if (count < 1000)
|
|
42
|
+
return String(Math.round(count));
|
|
43
|
+
if (count < 100_000)
|
|
44
|
+
return `${trim((count / 1000).toFixed(1))}k`;
|
|
45
|
+
if (count < 1_000_000)
|
|
46
|
+
return `${Math.round(count / 1000)}k`;
|
|
47
|
+
return `${trim((count / 1_000_000).toFixed(2))}M`;
|
|
48
|
+
}
|
|
49
|
+
const percent = (rate) => rate === undefined ? "—" : `${(rate * 100).toFixed(1)}%`;
|
|
50
|
+
const usd = (t) => {
|
|
51
|
+
if (t.cost === undefined)
|
|
52
|
+
return "—";
|
|
53
|
+
return t.cost >= 1 || t.cost === 0 ? `$${t.cost.toFixed(2)}` : `$${t.cost.toFixed(4)}`;
|
|
54
|
+
};
|
|
55
|
+
function seconds(ms) {
|
|
56
|
+
if (ms === undefined)
|
|
57
|
+
return "—";
|
|
58
|
+
return ms < 60_000 ? `${(ms / 1000).toFixed(1)}s` : `${(ms / 60_000).toFixed(1)}m`;
|
|
59
|
+
}
|
|
60
|
+
function width(text) {
|
|
61
|
+
let w = 0;
|
|
62
|
+
for (const ch of text)
|
|
63
|
+
w += (ch.codePointAt(0) ?? 0) > 0x2e7f ? 2 : 1;
|
|
64
|
+
return w;
|
|
65
|
+
}
|
|
66
|
+
function table(rows, rightAlign) {
|
|
67
|
+
const widths = [];
|
|
68
|
+
for (const row of rows)
|
|
69
|
+
row.forEach((cell, i) => (widths[i] = Math.max(widths[i] ?? 0, width(cell))));
|
|
70
|
+
return rows
|
|
71
|
+
.map((row) => row
|
|
72
|
+
.map((cell, i) => {
|
|
73
|
+
const padding = " ".repeat((widths[i] ?? 0) - width(cell));
|
|
74
|
+
return rightAlign(i) ? padding + cell : cell + padding;
|
|
75
|
+
})
|
|
76
|
+
.join(" ")
|
|
77
|
+
.trimEnd())
|
|
78
|
+
.join("\n");
|
|
79
|
+
}
|
|
80
|
+
export function renderStats(report, scope) {
|
|
81
|
+
const t = report.totals;
|
|
82
|
+
const range = report.since === undefined && report.until === undefined
|
|
83
|
+
? "全部时间"
|
|
84
|
+
: `${report.since ?? "最早"} – ${report.until ?? "今天"}`;
|
|
85
|
+
const lines = [`${range} · ${scope} · ${report.sessions} 个会话`];
|
|
86
|
+
if (t.requests === 0) {
|
|
87
|
+
lines.push("没有模型请求");
|
|
88
|
+
return lines.join("\n") + "\n";
|
|
89
|
+
}
|
|
90
|
+
const kinds = Object.entries(report.byKind)
|
|
91
|
+
.sort((a, b) => b[1] - a[1])
|
|
92
|
+
.map(([kind, n]) => `${kind === "turn" ? "对话" : kind} ${n}`)
|
|
93
|
+
.join(" · ");
|
|
94
|
+
const costLine = t.cost === undefined
|
|
95
|
+
? "— (所有请求的模型都无价格,见 token)"
|
|
96
|
+
: t.unpriced > 0
|
|
97
|
+
? `${usd(t)}(另有 ${t.unpriced} 次请求无价,未计入)`
|
|
98
|
+
: usd(t);
|
|
99
|
+
const rows = [
|
|
100
|
+
["请求", `${t.requests}(${kinds})`],
|
|
101
|
+
["回合", `${t.turns} · 平均耗时 ${seconds(t.avgTurnMs)}`],
|
|
102
|
+
[
|
|
103
|
+
"Token",
|
|
104
|
+
`输入 ${formatTokens(t.input)} · 输出 ${formatTokens(t.output)} · 缓存读 ${formatTokens(t.cacheRead)} · 缓存写 ${formatTokens(t.cacheWrite)}`,
|
|
105
|
+
],
|
|
106
|
+
[
|
|
107
|
+
"缓存命中率",
|
|
108
|
+
`${percent(t.hitRate)}(报告缓存的端点 ${report.endpoints.reported}/${report.endpoints.total},其余不进分母)`,
|
|
109
|
+
],
|
|
110
|
+
["费用", costLine],
|
|
111
|
+
["错误 / 重试", `${t.errors} / ${t.retries}`],
|
|
112
|
+
];
|
|
113
|
+
lines.push(table(rows, () => false));
|
|
114
|
+
if (report.groups !== undefined && report.groups.length > 0) {
|
|
115
|
+
const head = ["", "会话", "请求", "回合", "输入", "输出", "缓存读", "缓存写", "命中率", "费用"];
|
|
116
|
+
const body = report.groups.map((g) => [
|
|
117
|
+
g.key,
|
|
118
|
+
String(g.sessions),
|
|
119
|
+
String(g.requests),
|
|
120
|
+
String(g.turns),
|
|
121
|
+
formatTokens(g.input),
|
|
122
|
+
formatTokens(g.output),
|
|
123
|
+
formatTokens(g.cacheRead),
|
|
124
|
+
formatTokens(g.cacheWrite),
|
|
125
|
+
percent(g.hitRate),
|
|
126
|
+
usd(g) + (g.cost !== undefined && g.unpriced > 0 ? "+" : ""),
|
|
127
|
+
]);
|
|
128
|
+
lines.push("", table([head, ...body], (i) => i > 0));
|
|
129
|
+
}
|
|
130
|
+
if (report.tools.length > 0) {
|
|
131
|
+
lines.push("", `工具调用 Top ${report.tools.length}`);
|
|
132
|
+
lines.push(table(report.tools.map((tool) => [` ${tool.name}`, String(tool.count)]), (i) => i > 0));
|
|
133
|
+
}
|
|
134
|
+
return lines.join("\n") + "\n";
|
|
135
|
+
}
|
|
136
|
+
export async function runStats(argv, io) {
|
|
137
|
+
const { positionals, values, flags } = parseSubArgs(argv, ["since", "until", "by", "project", "session-dir", "top"], ["all", "json", "no-cache"]);
|
|
138
|
+
if (flags.has("help")) {
|
|
139
|
+
io.stdout(STATS_USAGE);
|
|
140
|
+
return ExitCode.Ok;
|
|
141
|
+
}
|
|
142
|
+
if (positionals.length > 0)
|
|
143
|
+
throw new UsageError(`ama stats 不接受位置参数:${positionals[0]}`);
|
|
144
|
+
const now = new Date();
|
|
145
|
+
const since = values.has("since") ? parseDaySpec("since", values.get("since"), now) : undefined;
|
|
146
|
+
const until = values.has("until") ? parseDaySpec("until", values.get("until"), now) : undefined;
|
|
147
|
+
const byRaw = values.get("by");
|
|
148
|
+
if (byRaw !== undefined && !STATS_GROUPS.includes(byRaw)) {
|
|
149
|
+
throw new UsageError(`--by 的取值应为 ${STATS_GROUPS.join(" | ")}(收到 ${byRaw})`);
|
|
150
|
+
}
|
|
151
|
+
const by = byRaw;
|
|
152
|
+
const top = Number(values.get("top") ?? "10");
|
|
153
|
+
if (!Number.isInteger(top) || top < 0)
|
|
154
|
+
throw new UsageError("--top 应为非负整数");
|
|
155
|
+
if (flags.has("all") && values.has("project")) {
|
|
156
|
+
throw new UsageError("--project 与 --all 不能同时使用");
|
|
157
|
+
}
|
|
158
|
+
const dataDir = resolveDataDir({ env: io.env });
|
|
159
|
+
const dirFlag = values.get("session-dir");
|
|
160
|
+
const root = dirFlag !== undefined
|
|
161
|
+
? resolve(io.cwd, expandHome(dirFlag, { env: io.env }))
|
|
162
|
+
: join(dataDir, "sessions");
|
|
163
|
+
const projectFlag = values.get("project");
|
|
164
|
+
const project = projectFlag !== undefined
|
|
165
|
+
? resolve(io.cwd, expandHome(projectFlag, { env: io.env }))
|
|
166
|
+
: flags.has("all") || by === "project"
|
|
167
|
+
? undefined
|
|
168
|
+
: io.cwd;
|
|
169
|
+
const files = sessionFilesInScope(root, project);
|
|
170
|
+
const useCache = !flags.has("no-cache");
|
|
171
|
+
const collected = collectSummaries(files, {
|
|
172
|
+
...(useCache ? { indexFile: join(dataDir, STATS_INDEX_FILE) } : {}),
|
|
173
|
+
prune: project === undefined,
|
|
174
|
+
});
|
|
175
|
+
const report = aggregateStats(collected.summaries, {
|
|
176
|
+
...(since !== undefined ? { since } : {}),
|
|
177
|
+
...(until !== undefined ? { until } : {}),
|
|
178
|
+
...(by !== undefined ? { by } : {}),
|
|
179
|
+
topTools: top,
|
|
180
|
+
});
|
|
181
|
+
if (flags.has("json")) {
|
|
182
|
+
io.stdout(JSON.stringify({
|
|
183
|
+
scope: project === undefined ? { all: true } : { project },
|
|
184
|
+
...report,
|
|
185
|
+
files: {
|
|
186
|
+
total: files.length,
|
|
187
|
+
scanned: collected.scanned,
|
|
188
|
+
cached: collected.cached,
|
|
189
|
+
invalid: collected.invalid,
|
|
190
|
+
},
|
|
191
|
+
}, null, 2) + "\n");
|
|
192
|
+
return ExitCode.Ok;
|
|
193
|
+
}
|
|
194
|
+
io.stdout(renderStats(report, project === undefined ? "全部项目" : `项目 ${project}`));
|
|
195
|
+
if (collected.invalid > 0)
|
|
196
|
+
io.stderr(`ama: 跳过 ${collected.invalid} 个无法读取的会话文件\n`);
|
|
197
|
+
return ExitCode.Ok;
|
|
198
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `--system-prompt <文本|@文件>` 与 `--system-prompt-mode append|replace`(W4-C)。
|
|
3
|
+
*
|
|
4
|
+
* - `append`(缺省):作为最后一条规则追加进系统提示的 rules 节——preamble 与工具表在它前面,
|
|
5
|
+
* 缓存前缀里最长的那段不受影响;同一段文本每次都逐字节相同。
|
|
6
|
+
* - `replace`:替换开头的 preamble(「You are ama…」那一段);工具表、规则、AGENTS.md、Skill 索引、
|
|
7
|
+
* cwd 等节照常保留,工具仍然可用。
|
|
8
|
+
* - `@文件`:相对当前目录读取(UTF-8);文件不存在或为空 → 配置错误(退出码 3,同 `--instructions`)。
|
|
9
|
+
*/
|
|
10
|
+
import type { SystemPromptOverride } from "./deps.js";
|
|
11
|
+
export declare function resolveSystemPromptArg(value: string | undefined, mode: "append" | "replace" | undefined, cwd: string): SystemPromptOverride | undefined;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `--system-prompt <文本|@文件>` 与 `--system-prompt-mode append|replace`(W4-C)。
|
|
3
|
+
*
|
|
4
|
+
* - `append`(缺省):作为最后一条规则追加进系统提示的 rules 节——preamble 与工具表在它前面,
|
|
5
|
+
* 缓存前缀里最长的那段不受影响;同一段文本每次都逐字节相同。
|
|
6
|
+
* - `replace`:替换开头的 preamble(「You are ama…」那一段);工具表、规则、AGENTS.md、Skill 索引、
|
|
7
|
+
* cwd 等节照常保留,工具仍然可用。
|
|
8
|
+
* - `@文件`:相对当前目录读取(UTF-8);文件不存在或为空 → 配置错误(退出码 3,同 `--instructions`)。
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, statSync } from "node:fs";
|
|
11
|
+
import { isAbsolute, resolve } from "node:path";
|
|
12
|
+
import { StartupError } from "../errors.js";
|
|
13
|
+
import { ExitCode } from "./exit-codes.js";
|
|
14
|
+
export function resolveSystemPromptArg(value, mode, cwd) {
|
|
15
|
+
if (value === undefined)
|
|
16
|
+
return undefined;
|
|
17
|
+
let text = value;
|
|
18
|
+
if (value.startsWith("@")) {
|
|
19
|
+
const raw = value.slice(1);
|
|
20
|
+
const path = isAbsolute(raw) ? raw : resolve(cwd, raw);
|
|
21
|
+
try {
|
|
22
|
+
if (!statSync(path).isFile())
|
|
23
|
+
throw new Error("not a file");
|
|
24
|
+
text = readFileSync(path, "utf8");
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new StartupError("config_invalid", `--system-prompt 文件不存在:${path}`, ExitCode.Config);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
text = text.trim();
|
|
31
|
+
if (text === "")
|
|
32
|
+
throw new StartupError("config_invalid", "--system-prompt 的内容为空", ExitCode.Config);
|
|
33
|
+
return { text, mode: mode ?? "append" };
|
|
34
|
+
}
|
package/dist/codemode/modes.d.ts
CHANGED
|
@@ -4,20 +4,13 @@
|
|
|
4
4
|
* | 模式 | 模型看到的工具 |
|
|
5
5
|
* | ------ | -------------------------------------------------------------------------- |
|
|
6
6
|
* | `off` | 不注册 codemode(工厂返回 undefined) |
|
|
7
|
-
* | `on` | 预设的工具 + codemode
|
|
7
|
+
* | `on` | 预设的工具 + codemode;codemode 描述一行列出可在脚本里调用的直接工具 |
|
|
8
8
|
* | `only` | 只有 codemode(宿主 / SDK 工具也不直接暴露);其它工具的声明列在它的描述里 |
|
|
9
9
|
*
|
|
10
|
-
* 活动集由 B6 的 `resolvePreset` 决定(`only` → `["codemode"]
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* 活动集由 B6 的 `resolvePreset` 决定(`only` → `["codemode"]`);这里落 `only` 模式的独占活动集
|
|
11
|
+
* (`PresetToolRegistry.setPresetTools(names, { exclusive: true })`)。`on` 模式不再给其它工具的描述
|
|
12
|
+
* 追加提示:直接工具的描述与 off 模式逐字节相同,codemode 描述里一行列出它们(codemode/tool.ts)。
|
|
13
13
|
*/
|
|
14
|
-
import type { CodemodeMode } from "../config/types.js";
|
|
15
14
|
import { type PresetResolution, type PresetToolRegistry } from "../tools/presets.js";
|
|
16
|
-
import type { ToolDefinition } from "../tools/types.js";
|
|
17
|
-
export declare function codemodeHint(name: string): string;
|
|
18
|
-
/** 复制工具(保留 getter 与方法),描述末尾追加一行 codemode 提示。 */
|
|
19
|
-
export declare function withCodemodeHint<I>(tool: ToolDefinition<I>): ToolDefinition<I>;
|
|
20
|
-
/** 注册前的包装:`on` 模式给 codemode 以外的工具加提示,其它模式原样返回。 */
|
|
21
|
-
export declare function decorateForMode(mode: CodemodeMode): <I>(tool: ToolDefinition<I>) => ToolDefinition<I>;
|
|
22
15
|
/** 把预设结果落到注册表:`only` 时活动集独占(宿主 / SDK 工具也只能在脚本里调用)。 */
|
|
23
16
|
export declare function applyCodemodeMode(registry: PresetToolRegistry, resolution: Pick<PresetResolution, "builtin" | "codemode">): void;
|
package/dist/codemode/modes.js
CHANGED
|
@@ -4,36 +4,14 @@
|
|
|
4
4
|
* | 模式 | 模型看到的工具 |
|
|
5
5
|
* | ------ | -------------------------------------------------------------------------- |
|
|
6
6
|
* | `off` | 不注册 codemode(工厂返回 undefined) |
|
|
7
|
-
* | `on` | 预设的工具 + codemode
|
|
7
|
+
* | `on` | 预设的工具 + codemode;codemode 描述一行列出可在脚本里调用的直接工具 |
|
|
8
8
|
* | `only` | 只有 codemode(宿主 / SDK 工具也不直接暴露);其它工具的声明列在它的描述里 |
|
|
9
9
|
*
|
|
10
|
-
* 活动集由 B6 的 `resolvePreset` 决定(`only` → `["codemode"]
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* 活动集由 B6 的 `resolvePreset` 决定(`only` → `["codemode"]`);这里落 `only` 模式的独占活动集
|
|
11
|
+
* (`PresetToolRegistry.setPresetTools(names, { exclusive: true })`)。`on` 模式不再给其它工具的描述
|
|
12
|
+
* 追加提示:直接工具的描述与 off 模式逐字节相同,codemode 描述里一行列出它们(codemode/tool.ts)。
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
15
|
-
export function codemodeHint(name) {
|
|
16
|
-
return `Also callable inside codemode scripts as tools.${name}(args).`;
|
|
17
|
-
}
|
|
18
|
-
/** 复制工具(保留 getter 与方法),描述末尾追加一行 codemode 提示。 */
|
|
19
|
-
export function withCodemodeHint(tool) {
|
|
20
|
-
const copy = Object.create(Object.getPrototypeOf(tool), {
|
|
21
|
-
...Object.getOwnPropertyDescriptors(tool),
|
|
22
|
-
});
|
|
23
|
-
Object.defineProperty(copy, "description", {
|
|
24
|
-
value: `${tool.description}\n${codemodeHint(tool.name)}`,
|
|
25
|
-
enumerable: true,
|
|
26
|
-
writable: false,
|
|
27
|
-
configurable: true,
|
|
28
|
-
});
|
|
29
|
-
return copy;
|
|
30
|
-
}
|
|
31
|
-
/** 注册前的包装:`on` 模式给 codemode 以外的工具加提示,其它模式原样返回。 */
|
|
32
|
-
export function decorateForMode(mode) {
|
|
33
|
-
if (mode !== "on")
|
|
34
|
-
return (tool) => tool;
|
|
35
|
-
return (tool) => (tool.name === CODEMODE_TOOL ? tool : withCodemodeHint(tool));
|
|
36
|
-
}
|
|
14
|
+
import {} from "../tools/presets.js";
|
|
37
15
|
/** 把预设结果落到注册表:`only` 时活动集独占(宿主 / SDK 工具也只能在脚本里调用)。 */
|
|
38
16
|
export function applyCodemodeMode(registry, resolution) {
|
|
39
17
|
registry.setPresetTools(resolution.builtin, { exclusive: resolution.codemode === "only" });
|
package/dist/codemode/tool.d.ts
CHANGED
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
* - store 只在脚本成功时提交(store.ts);
|
|
13
13
|
* - 描述(含工具声明)在第一次读取时确定并冻结:同一会话内字节稳定(设计 §9.1),之后注册的宿主
|
|
14
14
|
* 工具仍可在脚本里调用(`ALL_TOOLS` / `describeTool` 是执行时的清单)。
|
|
15
|
+
* - 描述分两种(`buildCodemodeDescription` 的 `layout`):`only` 模式按预算内联全部工具声明;`on`
|
|
16
|
+
* 模式不重复已直接暴露给模型的工具(它们的 schema 已在工具表里),只用一行列出名字,「仅脚本可
|
|
17
|
+
* 调用」的工具也只列名字(`describeTool(name)` 取签名),把 on 模式的前缀增量压在约 400 token。
|
|
15
18
|
*/
|
|
19
|
+
import type { AmaConfig } from "../config/types.js";
|
|
16
20
|
import type { ToolDefinition, ToolResult } from "../tools/types.js";
|
|
17
21
|
import { type SandboxCapability } from "./capability.js";
|
|
18
22
|
import { type DeclarableTool } from "./declarations.js";
|
|
@@ -25,6 +29,8 @@ export interface CallableTool {
|
|
|
25
29
|
tool: DeclarableTool;
|
|
26
30
|
/** 内置工具(非 bash)解析为文本;宿主 / SDK 工具可能返回结构化值。 */
|
|
27
31
|
textResult: boolean;
|
|
32
|
+
/** 模型也能直接调用(在活动集里);`on` 模式的描述只列名字。 */
|
|
33
|
+
direct?: boolean;
|
|
28
34
|
}
|
|
29
35
|
export interface CodemodeToolOptions {
|
|
30
36
|
/** 脚本里可调用的工具(不含 codemode 本身);描述冻结时与每次执行时各调用一次。 */
|
|
@@ -37,6 +43,8 @@ export interface CodemodeToolOptions {
|
|
|
37
43
|
maxResultChars?: number;
|
|
38
44
|
/** `only` 模式:其它工具不直接暴露,系统提示的工具行与规则写明只能在脚本里调用。 */
|
|
39
45
|
exclusive?: boolean;
|
|
46
|
+
/** 描述的写法:缺省 `only`(按预算内联声明);`on` 只列名字(见 `buildCodemodeDescription`)。 */
|
|
47
|
+
layout?: DescriptionLayout;
|
|
40
48
|
/** 子进程入口(测试 / 嵌入方覆盖)。 */
|
|
41
49
|
entry?: string;
|
|
42
50
|
nodePath?: string;
|
|
@@ -68,7 +76,8 @@ export declare const CODEMODE_EXAMPLE: readonly string[];
|
|
|
68
76
|
* (实测模型常见的两种错误)。其它错误原样返回。
|
|
69
77
|
*/
|
|
70
78
|
export declare function scriptErrorHint(error: string, toolNames: readonly string[]): string;
|
|
71
|
-
export
|
|
79
|
+
export type DescriptionLayout = "only" | "on";
|
|
80
|
+
export declare function buildCodemodeDescription(tools: readonly CallableTool[], inlineBudget: number, capability: SandboxCapability, layout?: DescriptionLayout): string;
|
|
72
81
|
/** 结果文本:状态行 + 输出(超限保留首尾)+ 失败原因。 */
|
|
73
82
|
export declare function formatCodemodeResult(run: Pick<SandboxRunResult, "ok" | "error" | "outputs" | "elapsedMs" | "droppedChars">, limitChars: number, saveFull: (text: string) => string | undefined): {
|
|
74
83
|
text: string;
|
|
@@ -79,27 +88,22 @@ export declare const CODEMODE_ONLY_GUIDELINE = "read, edit, write, bash and the
|
|
|
79
88
|
export declare function createCodemodeTool(options: CodemodeToolOptions): ToolDefinition<CodemodeInput>;
|
|
80
89
|
/** 组装根用:从 ToolFactoryContext 的形状取所需(避免 codemode → cli 的依赖)。 */
|
|
81
90
|
export interface CodemodeFactoryContext {
|
|
82
|
-
config:
|
|
83
|
-
codemode?: {
|
|
84
|
-
mode?: "off" | "on" | "only";
|
|
85
|
-
inlineBudget?: number;
|
|
86
|
-
requireStrict?: boolean;
|
|
87
|
-
};
|
|
88
|
-
tools?: {
|
|
89
|
-
preset?: string;
|
|
90
|
-
maxToolResultChars?: number;
|
|
91
|
-
};
|
|
92
|
-
};
|
|
91
|
+
config: Pick<AmaConfig, "tools" | "codemode">;
|
|
93
92
|
registry: {
|
|
94
93
|
list(): readonly string[];
|
|
95
94
|
get(name: string): ToolDefinition | undefined;
|
|
96
95
|
} & {
|
|
97
96
|
sourceOf?(name: string): string | undefined;
|
|
97
|
+
/** 活动集(coordinator 预设下脚本只能调这些)。 */
|
|
98
|
+
active?(): readonly {
|
|
99
|
+
name: string;
|
|
100
|
+
}[];
|
|
98
101
|
};
|
|
99
102
|
warn(message: string): void;
|
|
100
103
|
}
|
|
101
104
|
/**
|
|
102
|
-
* codemode
|
|
103
|
-
* → 不注册并 warning(预设随之回退到 default)。
|
|
105
|
+
* codemode 工具工厂:生效模式(显式 `codemode.mode`,否则跟随预设,见 tools/presets.ts)为 off →
|
|
106
|
+
* 不注册;`requireStrict` 而运行时不隔离网络 → 不注册并 warning(预设随之回退到 default)。
|
|
107
|
+
* `coordinator` 预设下脚本可调用的工具限于活动集(不含 codemode 本身)。
|
|
104
108
|
*/
|
|
105
109
|
export declare function codemodeToolFactory(overrides?: Partial<Pick<CodemodeToolOptions, "capability" | "entry" | "nodePath">>): (ctx: CodemodeFactoryContext) => ToolDefinition | undefined;
|
package/dist/codemode/tool.js
CHANGED
|
@@ -12,14 +12,17 @@
|
|
|
12
12
|
* - store 只在脚本成功时提交(store.ts);
|
|
13
13
|
* - 描述(含工具声明)在第一次读取时确定并冻结:同一会话内字节稳定(设计 §9.1),之后注册的宿主
|
|
14
14
|
* 工具仍可在脚本里调用(`ALL_TOOLS` / `describeTool` 是执行时的清单)。
|
|
15
|
+
* - 描述分两种(`buildCodemodeDescription` 的 `layout`):`only` 模式按预算内联全部工具声明;`on`
|
|
16
|
+
* 模式不重复已直接暴露给模型的工具(它们的 schema 已在工具表里),只用一行列出名字,「仅脚本可
|
|
17
|
+
* 调用」的工具也只列名字(`describeTool(name)` 取签名),把 on 模式的前缀增量压在约 400 token。
|
|
15
18
|
*/
|
|
16
19
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
17
20
|
import { tmpdir } from "node:os";
|
|
18
21
|
import { join } from "node:path";
|
|
22
|
+
import { codemodeCallableFilter, resolveCodemodeMode } from "../tools/presets.js";
|
|
19
23
|
import { codemodeAvailability, detectSandboxCapability, } from "./capability.js";
|
|
20
|
-
import { DEFAULT_INLINE_BUDGET, buildDeclarationBlock, toolDeclaration, } from "./declarations.js";
|
|
24
|
+
import { BASH_RESULT_DECLARATION, DEFAULT_INLINE_BUDGET, buildDeclarationBlock, toolDeclaration, } from "./declarations.js";
|
|
21
25
|
import { runSandbox } from "./host-side.js";
|
|
22
|
-
import { codemodeHint } from "./modes.js";
|
|
23
26
|
import { parseOptionsLine } from "./protocol.js";
|
|
24
27
|
import { commitStore, readStore } from "./store.js";
|
|
25
28
|
export const CODEMODE_TOOL_NAME = "codemode";
|
|
@@ -54,13 +57,6 @@ export function toScriptValue(name, result) {
|
|
|
54
57
|
throw new Error(textOf(result.content) || `Tool ${name} failed`);
|
|
55
58
|
return result.structured !== undefined ? result.structured : textOf(result.content);
|
|
56
59
|
}
|
|
57
|
-
/** on 模式下其它工具描述末尾的提示不进声明。 */
|
|
58
|
-
function declarable(tool) {
|
|
59
|
-
const suffix = `\n${codemodeHint(tool.name)}`;
|
|
60
|
-
if (!tool.description.endsWith(suffix))
|
|
61
|
-
return tool;
|
|
62
|
-
return { ...tool, description: tool.description.slice(0, -suffix.length) };
|
|
63
|
-
}
|
|
64
60
|
/** 描述里的示例脚本(6 行):并发两个 read、过滤、return。 */
|
|
65
61
|
export const CODEMODE_EXAMPLE = [
|
|
66
62
|
"const [a, b] = await Promise.all([",
|
|
@@ -86,7 +82,39 @@ export function scriptErrorHint(error, toolNames) {
|
|
|
86
82
|
}
|
|
87
83
|
return error;
|
|
88
84
|
}
|
|
89
|
-
|
|
85
|
+
/**
|
|
86
|
+
* `on` 模式的描述:规则压成三行 + 示例;已直接暴露的工具只列名字(参数同直接调用),仅脚本可调用的
|
|
87
|
+
* 工具也只列名字。不内联任何声明,字节只依赖工具名与沙箱能力。
|
|
88
|
+
*/
|
|
89
|
+
function buildOnDescription(tools, capability) {
|
|
90
|
+
const names = (list) => list
|
|
91
|
+
.map(({ tool }) => tool.name)
|
|
92
|
+
.sort()
|
|
93
|
+
.join(", ");
|
|
94
|
+
const direct = tools.filter((t) => t.direct === true);
|
|
95
|
+
const scriptOnly = tools.filter((t) => t.direct !== true);
|
|
96
|
+
const lines = [
|
|
97
|
+
"Run a JavaScript script that calls tools; only its output comes back to you. Use it to batch many tool calls (Promise.all), filter or summarize large results, or loop, in one step.",
|
|
98
|
+
"Input: raw JavaScript (not JSON, no code fence), the body of an async function (top-level await and return work). Only tools.<name>(args) exists: no require, import, process, fetch or timers.",
|
|
99
|
+
"Example:",
|
|
100
|
+
...CODEMODE_EXAMPLE,
|
|
101
|
+
"tools.<name>(args) returns a Promise that rejects when the call fails or is denied (Promise.allSettled keeps the rest); text(v) / console.log(...) append output; return v is text(v); store(key, v) / load(key) keep small JSON values across scripts; describeTool(name) returns a signature. At most 8 calls run at once, each through the normal permissions.",
|
|
102
|
+
];
|
|
103
|
+
if (!capability.strict)
|
|
104
|
+
lines.push(`Sandbox: ${capability.reason}.`);
|
|
105
|
+
if (direct.length > 0) {
|
|
106
|
+
const bash = direct.some(({ tool }) => tool.name === "bash");
|
|
107
|
+
lines.push(`Your direct tools are callable here too, same arguments: ${names(direct)}${bash ? " (tools.bash resolves to BashResult; the others to text)" : ""}.`);
|
|
108
|
+
if (bash)
|
|
109
|
+
lines.push(BASH_RESULT_DECLARATION);
|
|
110
|
+
}
|
|
111
|
+
if (scriptOnly.length > 0)
|
|
112
|
+
lines.push(`Callable only from scripts: ${names(scriptOnly)}.`);
|
|
113
|
+
return lines.join("\n");
|
|
114
|
+
}
|
|
115
|
+
export function buildCodemodeDescription(tools, inlineBudget, capability, layout = "only") {
|
|
116
|
+
if (layout === "on")
|
|
117
|
+
return buildOnDescription(tools, capability);
|
|
90
118
|
const lines = [
|
|
91
119
|
"Run a JavaScript script that calls tools; only the script's output comes back to you. Use it to batch many tool calls (Promise.all), filter or summarize large results, or loop, in one step.",
|
|
92
120
|
"Only tools.<name>(args) is available. There is no require, import, process, fetch or timers; do not call tools directly as functions.",
|
|
@@ -98,7 +126,7 @@ export function buildCodemodeDescription(tools, inlineBudget, capability) {
|
|
|
98
126
|
if (!capability.strict) {
|
|
99
127
|
lines.push(`Sandbox: ${capability.reason}.`);
|
|
100
128
|
}
|
|
101
|
-
const block = buildDeclarationBlock(tools.map(({ tool, textResult }) => ({ tool
|
|
129
|
+
const block = buildDeclarationBlock(tools.map(({ tool, textResult }) => ({ tool, textResult })), inlineBudget);
|
|
102
130
|
if (block.text !== "")
|
|
103
131
|
lines.push(block.text);
|
|
104
132
|
return lines.join("\n");
|
|
@@ -151,7 +179,7 @@ export function createCodemodeTool(options) {
|
|
|
151
179
|
name: CODEMODE_TOOL_NAME,
|
|
152
180
|
label: "Codemode",
|
|
153
181
|
get description() {
|
|
154
|
-
frozenDescription ??= buildCodemodeDescription(options.listTools(), inlineBudget, capability);
|
|
182
|
+
frozenDescription ??= buildCodemodeDescription(options.listTools(), inlineBudget, capability, options.layout);
|
|
155
183
|
return frozenDescription;
|
|
156
184
|
},
|
|
157
185
|
parameters: {
|
|
@@ -179,8 +207,10 @@ export function createCodemodeTool(options) {
|
|
|
179
207
|
}
|
|
180
208
|
const tools = options.listTools().map(({ tool: t, textResult }) => ({
|
|
181
209
|
name: t.name,
|
|
182
|
-
declaration: toolDeclaration(
|
|
210
|
+
declaration: toolDeclaration(t, textResult ? {} : { textResult: false }),
|
|
183
211
|
}));
|
|
212
|
+
// 只有清单里的工具可调(coordinator 预设的清单只含活动集);脚本拼出别的名字也不放行。
|
|
213
|
+
const callable = new Set(tools.map((t) => t.name));
|
|
184
214
|
let tail = "";
|
|
185
215
|
const run = await runSandbox({
|
|
186
216
|
script: input.script,
|
|
@@ -190,7 +220,11 @@ export function createCodemodeTool(options) {
|
|
|
190
220
|
signal: ctx.signal,
|
|
191
221
|
...(options.entry !== undefined ? { entry: options.entry } : {}),
|
|
192
222
|
...(options.nodePath !== undefined ? { nodePath: options.nodePath } : {}),
|
|
193
|
-
callTool: async (name, args, signal) =>
|
|
223
|
+
callTool: async (name, args, signal) => {
|
|
224
|
+
if (!callable.has(name))
|
|
225
|
+
throw new Error(`Tool ${name} is not available in codemode`);
|
|
226
|
+
return toScriptValue(name, await ctx.tools.executeTool(name, args, { signal }));
|
|
227
|
+
},
|
|
194
228
|
onOutput: (text) => {
|
|
195
229
|
tail = `${tail}${tail === "" ? "" : "\n"}${text}`.slice(-TAIL_CHARS);
|
|
196
230
|
ctx.onUpdate(tail);
|
|
@@ -240,33 +274,42 @@ export function createCodemodeTool(options) {
|
|
|
240
274
|
return tool;
|
|
241
275
|
}
|
|
242
276
|
/**
|
|
243
|
-
* codemode
|
|
244
|
-
* → 不注册并 warning(预设随之回退到 default)。
|
|
277
|
+
* codemode 工具工厂:生效模式(显式 `codemode.mode`,否则跟随预设,见 tools/presets.ts)为 off →
|
|
278
|
+
* 不注册;`requireStrict` 而运行时不隔离网络 → 不注册并 warning(预设随之回退到 default)。
|
|
279
|
+
* `coordinator` 预设下脚本可调用的工具限于活动集(不含 codemode 本身)。
|
|
245
280
|
*/
|
|
246
281
|
export function codemodeToolFactory(overrides = {}) {
|
|
247
282
|
return (ctx) => {
|
|
248
|
-
const
|
|
249
|
-
const mode =
|
|
283
|
+
const capability = overrides.capability ?? detectSandboxCapability();
|
|
284
|
+
const mode = resolveCodemodeMode(ctx.config, capability.strict).mode;
|
|
250
285
|
if (mode === "off")
|
|
251
286
|
return undefined;
|
|
252
|
-
const availability = codemodeAvailability(ctx.config.codemode?.requireStrict,
|
|
287
|
+
const availability = codemodeAvailability(ctx.config.codemode?.requireStrict, capability);
|
|
253
288
|
if (!availability.available) {
|
|
254
289
|
ctx.warn(availability.warning);
|
|
255
290
|
return undefined;
|
|
256
291
|
}
|
|
257
292
|
const registry = ctx.registry;
|
|
293
|
+
const onlyActive = codemodeCallableFilter(ctx.config) === "active";
|
|
294
|
+
const allowed = (name) => !onlyActive || (registry.active?.() ?? []).some((tool) => tool.name === name);
|
|
295
|
+
const direct = () => new Set(mode === "on" ? (registry.active?.() ?? []).map((tool) => tool.name) : []);
|
|
258
296
|
const options = {
|
|
259
297
|
capability: availability.capability,
|
|
260
298
|
exclusive: mode === "only",
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
299
|
+
layout: mode === "on" ? "on" : "only",
|
|
300
|
+
listTools: () => {
|
|
301
|
+
const active = direct();
|
|
302
|
+
return registry
|
|
303
|
+
.list()
|
|
304
|
+
.filter((name) => name !== CODEMODE_TOOL_NAME && allowed(name))
|
|
305
|
+
.map((name) => registry.get(name))
|
|
306
|
+
.filter((t) => t !== undefined)
|
|
307
|
+
.map((t) => ({
|
|
308
|
+
tool: t,
|
|
309
|
+
textResult: (registry.sourceOf?.(t.name) ?? "builtin") === "builtin",
|
|
310
|
+
...(active.has(t.name) ? { direct: true } : {}),
|
|
311
|
+
}));
|
|
312
|
+
},
|
|
270
313
|
};
|
|
271
314
|
const budget = ctx.config.codemode?.inlineBudget;
|
|
272
315
|
if (budget !== undefined)
|
package/dist/config/init.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 配置目录初始化(docs/providers.md「配置目录」):`ama init` 与 CLI 首次运行的自动初始化。
|
|
3
3
|
*
|
|
4
|
-
* - 目录 0700;`config.json` 只在不存在时写最小内容(`--force`
|
|
4
|
+
* - 目录 0700;`config.json` 只在不存在时写最小内容(`--force` 时先备份再重写):只有 `$schema`、
|
|
5
|
+
* `version` 与空 `providers`,不写死任何缺省值——缺省值以后调整(例如 codemode 跟随预设)对老用户
|
|
6
|
+
* 同样生效,`config show` 的来源也显示 default 而不是 user;
|
|
5
7
|
* - `config.schema.json` 不是用户文件,每次 init 都按当前版本重写(内容相同则不动);
|
|
6
8
|
* - 不创建 auth.json(只有 `ama auth set` / `ama providers add` 才写,0600)。
|
|
7
9
|
* - 自动初始化只在 CLI 里、配置目录不存在时触发,`AMA_NO_INIT=1` 关闭;SDK 与测试不走这里。
|
|
8
10
|
*/
|
|
9
11
|
import { type AmaConfig } from "./types.js";
|
|
10
12
|
export declare const NO_INIT_ENV = "AMA_NO_INIT";
|
|
11
|
-
/** 最小 config.json
|
|
13
|
+
/** 最小 config.json:只有 `$schema`、`version` 与空 `providers`(缺省值见 `ama config show`)。 */
|
|
12
14
|
export declare function minimalConfig(): AmaConfig & {
|
|
13
15
|
$schema: string;
|
|
14
16
|
};
|
|
@@ -27,4 +29,6 @@ export declare function initConfigDir(dir: string, options?: {
|
|
|
27
29
|
}): InitResult;
|
|
28
30
|
/** CLI 启动时:配置目录不存在就静默初始化;返回是否初始化了。失败不影响启动。 */
|
|
29
31
|
export declare function autoInitConfigDir(dir: string, env: Readonly<Record<string, string | undefined>>): boolean;
|
|
32
|
+
/** init 之后的下一步(配 key / 接中转站 / 自检)。 */
|
|
33
|
+
export declare const INIT_NEXT_STEPS: string[];
|
|
30
34
|
export declare function describeInit(result: InitResult): string;
|
package/dist/config/init.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 配置目录初始化(docs/providers.md「配置目录」):`ama init` 与 CLI 首次运行的自动初始化。
|
|
3
3
|
*
|
|
4
|
-
* - 目录 0700;`config.json` 只在不存在时写最小内容(`--force`
|
|
4
|
+
* - 目录 0700;`config.json` 只在不存在时写最小内容(`--force` 时先备份再重写):只有 `$schema`、
|
|
5
|
+
* `version` 与空 `providers`,不写死任何缺省值——缺省值以后调整(例如 codemode 跟随预设)对老用户
|
|
6
|
+
* 同样生效,`config show` 的来源也显示 default 而不是 user;
|
|
5
7
|
* - `config.schema.json` 不是用户文件,每次 init 都按当前版本重写(内容相同则不动);
|
|
6
8
|
* - 不创建 auth.json(只有 `ama auth set` / `ama providers add` 才写,0600)。
|
|
7
9
|
* - 自动初始化只在 CLI 里、配置目录不存在时触发,`AMA_NO_INIT=1` 关闭;SDK 与测试不走这里。
|
|
@@ -12,14 +14,11 @@ import { CONFIG_SCHEMA_FILE, configSchemaText } from "./json-schema.js";
|
|
|
12
14
|
import { AUTH_FILE, CONFIG_FILE } from "./paths.js";
|
|
13
15
|
import { CONFIG_FILE_VERSION } from "./types.js";
|
|
14
16
|
export const NO_INIT_ENV = "AMA_NO_INIT";
|
|
15
|
-
/** 最小 config.json
|
|
17
|
+
/** 最小 config.json:只有 `$schema`、`version` 与空 `providers`(缺省值见 `ama config show`)。 */
|
|
16
18
|
export function minimalConfig() {
|
|
17
19
|
return {
|
|
18
20
|
$schema: `./${CONFIG_SCHEMA_FILE}`,
|
|
19
21
|
version: CONFIG_FILE_VERSION,
|
|
20
|
-
thinkingLevel: "medium",
|
|
21
|
-
permission: { mode: "default" },
|
|
22
|
-
tools: { preset: "default" },
|
|
23
22
|
providers: {},
|
|
24
23
|
};
|
|
25
24
|
}
|
|
@@ -84,6 +83,14 @@ const STATUS_TEXT = {
|
|
|
84
83
|
unchanged: "已是当前版本",
|
|
85
84
|
overwritten: "已重写(原文件备份为 .bak)",
|
|
86
85
|
};
|
|
86
|
+
/** init 之后的下一步(配 key / 接中转站 / 自检)。 */
|
|
87
|
+
export const INIT_NEXT_STEPS = [
|
|
88
|
+
"下一步:",
|
|
89
|
+
" ama auth set anthropic 保存内置供应商的 key(或设置 ANTHROPIC_API_KEY 等环境变量)",
|
|
90
|
+
" ama providers add <id> --base-url <url> 接入中转站或自建服务",
|
|
91
|
+
" ama doctor 检查配置、key 与运行环境",
|
|
92
|
+
" ama config show 查看生效配置与每项来源",
|
|
93
|
+
];
|
|
87
94
|
export function describeInit(result) {
|
|
88
95
|
const lines = [`${result.dir}${result.dirCreated ? " 已创建(0700)" : " 已存在"}`];
|
|
89
96
|
for (const file of result.files)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* (docs/providers.md「配置目录」)。规则与 schema.ts 的 `validateConfig` 一一对应:未知字段在那里是
|
|
4
4
|
* warning、这里是 `additionalProperties: false`;渠道引用(模型 `channels` 指向已定义的渠道)这类跨字段
|
|
5
5
|
* 规则 JSON Schema 表达不了,只在 `validateConfig` 里查。一致性由 json-schema.test.ts 的正反例守住。
|
|
6
|
+
* 顶层与各段的键(供应商内部除外)的 description / default 来自 key-docs.ts(`annotate`)。
|
|
6
7
|
*/
|
|
7
8
|
type Schema = Record<string, unknown>;
|
|
8
9
|
export declare const CONFIG_SCHEMA_FILE = "config.schema.json";
|