@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,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama stats` 的汇总(纯函数):文件摘要 → 合计、分组、按来源的请求数、工具 Top N。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* 口径(与 `/session`、`get_session_stats` 一致):
|
|
5
|
+
* - 请求 = 每次模型请求(回合里的 assistant、保温、权限分类、压缩摘要…),按 kind 分开计;
|
|
6
|
+
* - 命中率 = cacheRead /(input + cacheRead + cacheWrite),只算「报告缓存」的端点:同一
|
|
7
|
+
* provider/model@channel 在扫描范围内出现过任何非零缓存读写才算;其余端点不进分母;
|
|
8
|
+
* - 费用只加有价请求;`unpriced` 是无价请求数(全无价时 cost 缺省,展示层只给 token)。
|
|
9
|
+
*/
|
|
10
|
+
export const STATS_GROUPS = [
|
|
11
|
+
"day",
|
|
12
|
+
"week",
|
|
13
|
+
"month",
|
|
14
|
+
"provider",
|
|
15
|
+
"channel",
|
|
16
|
+
"model",
|
|
17
|
+
"project",
|
|
18
|
+
];
|
|
19
|
+
const newAcc = () => ({
|
|
20
|
+
requests: 0,
|
|
21
|
+
turns: 0,
|
|
22
|
+
input: 0,
|
|
23
|
+
output: 0,
|
|
24
|
+
cacheRead: 0,
|
|
25
|
+
cacheWrite: 0,
|
|
26
|
+
cost: 0,
|
|
27
|
+
costed: 0,
|
|
28
|
+
hitRead: 0,
|
|
29
|
+
hitPrompt: 0,
|
|
30
|
+
errors: 0,
|
|
31
|
+
retries: 0,
|
|
32
|
+
turnMs: 0,
|
|
33
|
+
timedTurns: 0,
|
|
34
|
+
sessions: new Set(),
|
|
35
|
+
});
|
|
36
|
+
export function endpointOf(b) {
|
|
37
|
+
return `${b.provider}/${b.model}${b.channel !== undefined ? `@${b.channel}` : ""}`;
|
|
38
|
+
}
|
|
39
|
+
function add(acc, b, reported, session) {
|
|
40
|
+
acc.requests += b.requests;
|
|
41
|
+
acc.turns += b.turns;
|
|
42
|
+
acc.input += b.input;
|
|
43
|
+
acc.output += b.output;
|
|
44
|
+
acc.cacheRead += b.cacheRead;
|
|
45
|
+
acc.cacheWrite += b.cacheWrite;
|
|
46
|
+
acc.cost += b.cost;
|
|
47
|
+
acc.costed += b.costed;
|
|
48
|
+
if (reported) {
|
|
49
|
+
acc.hitRead += b.cacheRead;
|
|
50
|
+
acc.hitPrompt += b.input + b.cacheRead + b.cacheWrite;
|
|
51
|
+
}
|
|
52
|
+
acc.errors += b.errors;
|
|
53
|
+
acc.retries += b.retries;
|
|
54
|
+
acc.turnMs += b.turnMs;
|
|
55
|
+
acc.timedTurns += b.timedTurns;
|
|
56
|
+
acc.sessions.add(session);
|
|
57
|
+
}
|
|
58
|
+
function totals(acc) {
|
|
59
|
+
const out = {
|
|
60
|
+
requests: acc.requests,
|
|
61
|
+
turns: acc.turns,
|
|
62
|
+
input: acc.input,
|
|
63
|
+
output: acc.output,
|
|
64
|
+
cacheRead: acc.cacheRead,
|
|
65
|
+
cacheWrite: acc.cacheWrite,
|
|
66
|
+
unpriced: acc.requests - acc.costed,
|
|
67
|
+
errors: acc.errors,
|
|
68
|
+
retries: acc.retries,
|
|
69
|
+
};
|
|
70
|
+
if (acc.costed > 0)
|
|
71
|
+
out.cost = acc.cost;
|
|
72
|
+
if (acc.hitPrompt > 0)
|
|
73
|
+
out.hitRate = acc.hitRead / acc.hitPrompt;
|
|
74
|
+
if (acc.timedTurns > 0)
|
|
75
|
+
out.avgTurnMs = acc.turnMs / acc.timedTurns;
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/** 本地日期 → ISO 周 `2026-W40`。 */
|
|
79
|
+
export function isoWeek(day) {
|
|
80
|
+
const [y = 0, m = 1, d = 1] = day.split("-").map(Number);
|
|
81
|
+
const date = new Date(Date.UTC(y, m - 1, d));
|
|
82
|
+
const weekday = date.getUTCDay() || 7;
|
|
83
|
+
date.setUTCDate(date.getUTCDate() + 4 - weekday);
|
|
84
|
+
const yearStart = Date.UTC(date.getUTCFullYear(), 0, 1);
|
|
85
|
+
const week = Math.ceil(((date.getTime() - yearStart) / 86_400_000 + 1) / 7);
|
|
86
|
+
return `${date.getUTCFullYear()}-W${week < 10 ? `0${week}` : week}`;
|
|
87
|
+
}
|
|
88
|
+
function groupKey(by, b, cwd) {
|
|
89
|
+
switch (by) {
|
|
90
|
+
case "day":
|
|
91
|
+
return b.day;
|
|
92
|
+
case "week":
|
|
93
|
+
return isoWeek(b.day);
|
|
94
|
+
case "month":
|
|
95
|
+
return b.day.slice(0, 7);
|
|
96
|
+
case "provider":
|
|
97
|
+
return b.provider;
|
|
98
|
+
case "channel":
|
|
99
|
+
return endpointOf(b);
|
|
100
|
+
case "model":
|
|
101
|
+
return `${b.provider}/${b.model}`;
|
|
102
|
+
case "project":
|
|
103
|
+
return cwd;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export function aggregateStats(summaries, options = {}) {
|
|
107
|
+
const inRange = (day) => (options.since === undefined || day >= options.since) &&
|
|
108
|
+
(options.until === undefined || day <= options.until);
|
|
109
|
+
const reported = new Set();
|
|
110
|
+
const endpoints = new Set();
|
|
111
|
+
for (const s of summaries)
|
|
112
|
+
for (const b of s.buckets) {
|
|
113
|
+
endpoints.add(endpointOf(b));
|
|
114
|
+
if (b.cacheSeen)
|
|
115
|
+
reported.add(endpointOf(b));
|
|
116
|
+
}
|
|
117
|
+
const all = newAcc();
|
|
118
|
+
const groups = new Map();
|
|
119
|
+
const byKind = {};
|
|
120
|
+
const tools = new Map();
|
|
121
|
+
for (const s of summaries) {
|
|
122
|
+
for (const b of s.buckets) {
|
|
123
|
+
if (!inRange(b.day))
|
|
124
|
+
continue;
|
|
125
|
+
const isReported = reported.has(endpointOf(b));
|
|
126
|
+
add(all, b, isReported, s.id);
|
|
127
|
+
byKind[b.kind] = (byKind[b.kind] ?? 0) + b.requests;
|
|
128
|
+
if (options.by !== undefined) {
|
|
129
|
+
const key = groupKey(options.by, b, s.cwd);
|
|
130
|
+
let acc = groups.get(key);
|
|
131
|
+
if (acc === undefined)
|
|
132
|
+
groups.set(key, (acc = newAcc()));
|
|
133
|
+
add(acc, b, isReported, s.id);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
for (const [day, name, count] of s.tools)
|
|
137
|
+
if (inRange(day))
|
|
138
|
+
tools.set(name, (tools.get(name) ?? 0) + count);
|
|
139
|
+
}
|
|
140
|
+
const report = {
|
|
141
|
+
sessions: all.sessions.size,
|
|
142
|
+
totals: totals(all),
|
|
143
|
+
byKind,
|
|
144
|
+
tools: [...tools]
|
|
145
|
+
.map(([name, count]) => ({ name, count }))
|
|
146
|
+
.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name))
|
|
147
|
+
.slice(0, options.topTools ?? 10),
|
|
148
|
+
endpoints: { reported: reported.size, total: endpoints.size },
|
|
149
|
+
};
|
|
150
|
+
if (options.since !== undefined)
|
|
151
|
+
report.since = options.since;
|
|
152
|
+
if (options.until !== undefined)
|
|
153
|
+
report.until = options.until;
|
|
154
|
+
if (options.by !== undefined) {
|
|
155
|
+
const timeOrdered = options.by === "day" || options.by === "week" || options.by === "month";
|
|
156
|
+
report.groups = [...groups]
|
|
157
|
+
.map(([key, acc]) => ({ key, sessions: acc.sessions.size, ...totals(acc) }))
|
|
158
|
+
.sort((a, b) => timeOrdered
|
|
159
|
+
? a.key.localeCompare(b.key)
|
|
160
|
+
: b.requests - a.requests || a.key.localeCompare(b.key));
|
|
161
|
+
}
|
|
162
|
+
return report;
|
|
163
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama stats` 的增量索引缓存:`<数据目录>/stats-index.json`。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* - 每个会话文件一条:`{ mtimeMs, size, summary }`;mtime 或 size 变了就重扫,否则直接用摘要。
|
|
5
|
+
* - 摘要里的日期是本地日期,所以索引记下时区(`Intl` 的 timeZone 与当前 UTC 偏移);变了整份作废。
|
|
6
|
+
* - 只是缓存:读不到、版本不对、JSON 坏了都当没有;写入走临时文件 + rename,失败忽略。
|
|
7
|
+
* - 扫描全部目录时顺带删掉已不存在的文件的条目;只扫一个项目时保留别的条目。
|
|
8
|
+
*/
|
|
9
|
+
import { type FileStatsSummary } from "./stats-scan.js";
|
|
10
|
+
export declare const STATS_INDEX_FILE = "stats-index.json";
|
|
11
|
+
export declare function timeZoneKey(now?: Date): string;
|
|
12
|
+
export interface CollectResult {
|
|
13
|
+
summaries: FileStatsSummary[];
|
|
14
|
+
/** 本次重扫的文件数(其余命中缓存)。 */
|
|
15
|
+
scanned: number;
|
|
16
|
+
cached: number;
|
|
17
|
+
/** 无效 / 不可读的文件数。 */
|
|
18
|
+
invalid: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 取一批文件的摘要。`indexFile` 为 undefined 时不用缓存;`prune` 为 true 时删掉不在 `files` 里的条目。
|
|
22
|
+
*/
|
|
23
|
+
export declare function collectSummaries(files: readonly string[], options?: {
|
|
24
|
+
indexFile?: string;
|
|
25
|
+
prune?: boolean;
|
|
26
|
+
}): CollectResult;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama stats` 的增量索引缓存:`<数据目录>/stats-index.json`。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* - 每个会话文件一条:`{ mtimeMs, size, summary }`;mtime 或 size 变了就重扫,否则直接用摘要。
|
|
5
|
+
* - 摘要里的日期是本地日期,所以索引记下时区(`Intl` 的 timeZone 与当前 UTC 偏移);变了整份作废。
|
|
6
|
+
* - 只是缓存:读不到、版本不对、JSON 坏了都当没有;写入走临时文件 + rename,失败忽略。
|
|
7
|
+
* - 扫描全部目录时顺带删掉已不存在的文件的条目;只扫一个项目时保留别的条目。
|
|
8
|
+
*/
|
|
9
|
+
import { mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { dirname } from "node:path";
|
|
11
|
+
import { summarizeSessionFile } from "./stats-scan.js";
|
|
12
|
+
export const STATS_INDEX_FILE = "stats-index.json";
|
|
13
|
+
const INDEX_VERSION = 1;
|
|
14
|
+
export function timeZoneKey(now = new Date()) {
|
|
15
|
+
let zone = "";
|
|
16
|
+
try {
|
|
17
|
+
zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// 无 Intl 时只看偏移
|
|
21
|
+
}
|
|
22
|
+
return `${zone}|${now.getTimezoneOffset()}`;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 取一批文件的摘要。`indexFile` 为 undefined 时不用缓存;`prune` 为 true 时删掉不在 `files` 里的条目。
|
|
26
|
+
*/
|
|
27
|
+
export function collectSummaries(files, options = {}) {
|
|
28
|
+
const zone = timeZoneKey();
|
|
29
|
+
let index = { version: INDEX_VERSION, zone, files: {} };
|
|
30
|
+
if (options.indexFile !== undefined) {
|
|
31
|
+
try {
|
|
32
|
+
const loaded = JSON.parse(readFileSync(options.indexFile, "utf8"));
|
|
33
|
+
if (loaded.version === INDEX_VERSION &&
|
|
34
|
+
loaded.zone === zone &&
|
|
35
|
+
typeof loaded.files === "object")
|
|
36
|
+
index = loaded;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// 没有或坏了:重建
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
let dirty = false;
|
|
43
|
+
const result = { summaries: [], scanned: 0, cached: 0, invalid: 0 };
|
|
44
|
+
for (const file of files) {
|
|
45
|
+
let stat;
|
|
46
|
+
try {
|
|
47
|
+
stat = statSync(file);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
result.invalid++;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const hit = index.files[file];
|
|
54
|
+
let summary;
|
|
55
|
+
if (hit !== undefined && hit.mtimeMs === stat.mtimeMs && hit.size === stat.size) {
|
|
56
|
+
summary = hit.summary;
|
|
57
|
+
result.cached++;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
summary = summarizeSessionFile(file) ?? null;
|
|
61
|
+
result.scanned++;
|
|
62
|
+
index.files[file] = { mtimeMs: stat.mtimeMs, size: stat.size, summary };
|
|
63
|
+
dirty = true;
|
|
64
|
+
}
|
|
65
|
+
if (summary === null)
|
|
66
|
+
result.invalid++;
|
|
67
|
+
else
|
|
68
|
+
result.summaries.push(summary);
|
|
69
|
+
}
|
|
70
|
+
if (options.prune === true) {
|
|
71
|
+
const keep = new Set(files);
|
|
72
|
+
for (const key of Object.keys(index.files)) {
|
|
73
|
+
if (!keep.has(key)) {
|
|
74
|
+
delete index.files[key];
|
|
75
|
+
dirty = true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (dirty && options.indexFile !== undefined) {
|
|
80
|
+
try {
|
|
81
|
+
mkdirSync(dirname(options.indexFile), { recursive: true, mode: 0o700 });
|
|
82
|
+
const tmp = `${options.indexFile}.${process.pid}.tmp`;
|
|
83
|
+
writeFileSync(tmp, JSON.stringify(index), { mode: 0o600 });
|
|
84
|
+
renameSync(tmp, options.indexFile);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// 缓存写不进去不影响结果
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama stats` 的单文件摘要(只读、按行)。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* 一个会话文件 → 若干「桶」:按(本地日期、请求来源 kind、provider、model、channel)累加。
|
|
5
|
+
* - 请求来源:assistant 消息 = `turn`;`usage` 条目用自己的 `kind`(cache_warm、permission_classify…);
|
|
6
|
+
* `compaction` / `branch_summary` 带的 usage 记为同名 kind(模型取最近一条 assistant 的)。
|
|
7
|
+
* - token:input(不含缓存)/ output / cacheRead / cacheWrite;费用只累加有 `usage.cost` 的请求,
|
|
8
|
+
* `costed` 记有价请求数,展示层据此区分「有价」「部分无价」「全无价」。
|
|
9
|
+
* - 缓存三态:桶记下是否出现过 cacheRead > 0 或 cacheWrite > 0(`cacheSeen`);汇总时同一端点
|
|
10
|
+
* (provider/model@channel)任一桶出现过即算报告缓存,其余端点不进命中率分母(同会话层三态)。
|
|
11
|
+
* - 回合:非 `steer` 的用户消息开始一个回合,到下一个回合开始为止;回合计入首条 assistant 的桶,
|
|
12
|
+
* 耗时 = 最后一条 assistant 条目时间 − 用户消息条目时间(都是落盘时间戳)。没有 assistant 的回合不计。
|
|
13
|
+
* - 工具调用:assistant 内容里的 toolCall 块按名字计(codemode 内层调用不展开)。
|
|
14
|
+
* - 重试:`context_edit{reason:"retry"}` 计入当时最近一条 assistant 的桶;`stopReason:"error"` 计错误。
|
|
15
|
+
* - toolResult、custom、label 等行按前缀跳过、不解析;末尾半行忽略;中间坏行跳过并计数。
|
|
16
|
+
* - channel:最近一条 `model_change` 与该请求同 provider / model 时取它的 channel。
|
|
17
|
+
*/
|
|
18
|
+
export interface StatsBucket {
|
|
19
|
+
/** 本地日期 YYYY-MM-DD。 */
|
|
20
|
+
day: string;
|
|
21
|
+
kind: string;
|
|
22
|
+
provider: string;
|
|
23
|
+
model: string;
|
|
24
|
+
channel?: string;
|
|
25
|
+
requests: number;
|
|
26
|
+
input: number;
|
|
27
|
+
output: number;
|
|
28
|
+
cacheRead: number;
|
|
29
|
+
cacheWrite: number;
|
|
30
|
+
/** 有价请求的费用合计(美元)。 */
|
|
31
|
+
cost: number;
|
|
32
|
+
/** 有价请求数。 */
|
|
33
|
+
costed: number;
|
|
34
|
+
/** 出现过 cacheRead > 0 或 cacheWrite > 0。 */
|
|
35
|
+
cacheSeen: boolean;
|
|
36
|
+
errors: number;
|
|
37
|
+
retries: number;
|
|
38
|
+
turns: number;
|
|
39
|
+
/** 有耗时的回合的耗时合计与个数。 */
|
|
40
|
+
turnMs: number;
|
|
41
|
+
timedTurns: number;
|
|
42
|
+
}
|
|
43
|
+
export interface FileStatsSummary {
|
|
44
|
+
id: string;
|
|
45
|
+
cwd: string;
|
|
46
|
+
buckets: StatsBucket[];
|
|
47
|
+
/** `[日期, 工具名, 次数]`。 */
|
|
48
|
+
tools: Array<[string, string, number]>;
|
|
49
|
+
/** 跳过的坏行数。 */
|
|
50
|
+
badLines: number;
|
|
51
|
+
}
|
|
52
|
+
/** ISO → 本地日期;不可解析 → undefined。 */
|
|
53
|
+
export declare function localDay(iso: unknown): string | undefined;
|
|
54
|
+
export declare function summarizeSessionFile(file: string): FileStatsSummary | undefined;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama stats` 的单文件摘要(只读、按行)。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* 一个会话文件 → 若干「桶」:按(本地日期、请求来源 kind、provider、model、channel)累加。
|
|
5
|
+
* - 请求来源:assistant 消息 = `turn`;`usage` 条目用自己的 `kind`(cache_warm、permission_classify…);
|
|
6
|
+
* `compaction` / `branch_summary` 带的 usage 记为同名 kind(模型取最近一条 assistant 的)。
|
|
7
|
+
* - token:input(不含缓存)/ output / cacheRead / cacheWrite;费用只累加有 `usage.cost` 的请求,
|
|
8
|
+
* `costed` 记有价请求数,展示层据此区分「有价」「部分无价」「全无价」。
|
|
9
|
+
* - 缓存三态:桶记下是否出现过 cacheRead > 0 或 cacheWrite > 0(`cacheSeen`);汇总时同一端点
|
|
10
|
+
* (provider/model@channel)任一桶出现过即算报告缓存,其余端点不进命中率分母(同会话层三态)。
|
|
11
|
+
* - 回合:非 `steer` 的用户消息开始一个回合,到下一个回合开始为止;回合计入首条 assistant 的桶,
|
|
12
|
+
* 耗时 = 最后一条 assistant 条目时间 − 用户消息条目时间(都是落盘时间戳)。没有 assistant 的回合不计。
|
|
13
|
+
* - 工具调用:assistant 内容里的 toolCall 块按名字计(codemode 内层调用不展开)。
|
|
14
|
+
* - 重试:`context_edit{reason:"retry"}` 计入当时最近一条 assistant 的桶;`stopReason:"error"` 计错误。
|
|
15
|
+
* - toolResult、custom、label 等行按前缀跳过、不解析;末尾半行忽略;中间坏行跳过并计数。
|
|
16
|
+
* - channel:最近一条 `model_change` 与该请求同 provider / model 时取它的 channel。
|
|
17
|
+
*/
|
|
18
|
+
import { forEachLine, lineType } from "./scan.js";
|
|
19
|
+
const SKIP_TYPES = new Set([
|
|
20
|
+
"custom",
|
|
21
|
+
"custom_message",
|
|
22
|
+
"label",
|
|
23
|
+
"leaf",
|
|
24
|
+
"session_info",
|
|
25
|
+
"thinking_level_change",
|
|
26
|
+
]);
|
|
27
|
+
function pad(n) {
|
|
28
|
+
return n < 10 ? `0${n}` : String(n);
|
|
29
|
+
}
|
|
30
|
+
/** ISO → 本地日期;不可解析 → undefined。 */
|
|
31
|
+
export function localDay(iso) {
|
|
32
|
+
if (typeof iso !== "string")
|
|
33
|
+
return undefined;
|
|
34
|
+
const ms = Date.parse(iso);
|
|
35
|
+
if (!Number.isFinite(ms))
|
|
36
|
+
return undefined;
|
|
37
|
+
const d = new Date(ms);
|
|
38
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
|
39
|
+
}
|
|
40
|
+
const isRec = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
41
|
+
const num = (v) => (typeof v === "number" && Number.isFinite(v) ? v : 0);
|
|
42
|
+
export function summarizeSessionFile(file) {
|
|
43
|
+
let header;
|
|
44
|
+
const buckets = new Map();
|
|
45
|
+
const tools = new Map();
|
|
46
|
+
let badLines = 0;
|
|
47
|
+
let channel;
|
|
48
|
+
let lastAssistant;
|
|
49
|
+
let lastBucket;
|
|
50
|
+
let turn;
|
|
51
|
+
const bucketOf = (day, kind, ref) => {
|
|
52
|
+
const key = `${day}\u0000${kind}\u0000${ref.provider}\u0000${ref.model}\u0000${ref.channel ?? ""}`;
|
|
53
|
+
let bucket = buckets.get(key);
|
|
54
|
+
if (bucket === undefined) {
|
|
55
|
+
bucket = {
|
|
56
|
+
day,
|
|
57
|
+
kind,
|
|
58
|
+
provider: ref.provider,
|
|
59
|
+
model: ref.model,
|
|
60
|
+
requests: 0,
|
|
61
|
+
input: 0,
|
|
62
|
+
output: 0,
|
|
63
|
+
cacheRead: 0,
|
|
64
|
+
cacheWrite: 0,
|
|
65
|
+
cost: 0,
|
|
66
|
+
costed: 0,
|
|
67
|
+
cacheSeen: false,
|
|
68
|
+
errors: 0,
|
|
69
|
+
retries: 0,
|
|
70
|
+
turns: 0,
|
|
71
|
+
turnMs: 0,
|
|
72
|
+
timedTurns: 0,
|
|
73
|
+
};
|
|
74
|
+
if (ref.channel !== undefined)
|
|
75
|
+
bucket.channel = ref.channel;
|
|
76
|
+
buckets.set(key, bucket);
|
|
77
|
+
}
|
|
78
|
+
return bucket;
|
|
79
|
+
};
|
|
80
|
+
const addUsage = (bucket, usage) => {
|
|
81
|
+
bucket.requests++;
|
|
82
|
+
if (!isRec(usage))
|
|
83
|
+
return;
|
|
84
|
+
const u = usage;
|
|
85
|
+
bucket.input += num(u.input);
|
|
86
|
+
bucket.output += num(u.output);
|
|
87
|
+
bucket.cacheRead += num(u.cacheRead);
|
|
88
|
+
bucket.cacheWrite += num(u.cacheWrite);
|
|
89
|
+
if (num(u.cacheRead) > 0 || num(u.cacheWrite) > 0)
|
|
90
|
+
bucket.cacheSeen = true;
|
|
91
|
+
if (isRec(u.cost) && typeof u.cost["total"] === "number") {
|
|
92
|
+
bucket.cost += num(u.cost["total"]);
|
|
93
|
+
bucket.costed++;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const refFor = (provider, model) => {
|
|
97
|
+
const ref = { provider, model };
|
|
98
|
+
if (channel?.channel !== undefined &&
|
|
99
|
+
channel.provider === provider &&
|
|
100
|
+
channel.model === model) {
|
|
101
|
+
ref.channel = channel.channel;
|
|
102
|
+
}
|
|
103
|
+
return ref;
|
|
104
|
+
};
|
|
105
|
+
const closeTurn = () => {
|
|
106
|
+
if (turn?.bucket !== undefined) {
|
|
107
|
+
turn.bucket.turns++;
|
|
108
|
+
if (turn.end !== undefined && turn.end >= turn.start) {
|
|
109
|
+
turn.bucket.turnMs += turn.end - turn.start;
|
|
110
|
+
turn.bucket.timedTurns++;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
turn = undefined;
|
|
114
|
+
};
|
|
115
|
+
try {
|
|
116
|
+
forEachLine(file, (line, index, last) => {
|
|
117
|
+
if (index > 0) {
|
|
118
|
+
const quick = lineType(line);
|
|
119
|
+
if (quick !== undefined) {
|
|
120
|
+
if (SKIP_TYPES.has(quick.type))
|
|
121
|
+
return;
|
|
122
|
+
if (quick.type === "message" && quick.role === "toolResult")
|
|
123
|
+
return;
|
|
124
|
+
if (quick.type === "message" && quick.role === "system")
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
let value;
|
|
129
|
+
try {
|
|
130
|
+
value = JSON.parse(line);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
if (!last)
|
|
134
|
+
badLines++;
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!isRec(value))
|
|
138
|
+
return;
|
|
139
|
+
if (index === 0) {
|
|
140
|
+
if (value["type"] !== "session" || typeof value["cwd"] !== "string")
|
|
141
|
+
return false;
|
|
142
|
+
header = value;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const ts = value["timestamp"];
|
|
146
|
+
const day = localDay(ts);
|
|
147
|
+
if (day === undefined)
|
|
148
|
+
return;
|
|
149
|
+
switch (value["type"]) {
|
|
150
|
+
case "model_change": {
|
|
151
|
+
const ref = {
|
|
152
|
+
provider: String(value["provider"]),
|
|
153
|
+
model: String(value["modelId"]),
|
|
154
|
+
};
|
|
155
|
+
if (typeof value["channel"] === "string")
|
|
156
|
+
ref.channel = value["channel"];
|
|
157
|
+
channel = ref;
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
case "message": {
|
|
161
|
+
const message = value["message"];
|
|
162
|
+
if (!isRec(message))
|
|
163
|
+
return;
|
|
164
|
+
if (message["role"] === "user") {
|
|
165
|
+
if (message["origin"] === "steer" && turn !== undefined)
|
|
166
|
+
return;
|
|
167
|
+
closeTurn();
|
|
168
|
+
turn = { start: Date.parse(ts), day };
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (message["role"] !== "assistant")
|
|
172
|
+
return;
|
|
173
|
+
const ref = refFor(String(message["provider"]), String(message["model"]));
|
|
174
|
+
lastAssistant = ref;
|
|
175
|
+
const bucket = bucketOf(day, "turn", ref);
|
|
176
|
+
lastBucket = bucket;
|
|
177
|
+
addUsage(bucket, message["usage"]);
|
|
178
|
+
if (message["stopReason"] === "error")
|
|
179
|
+
bucket.errors++;
|
|
180
|
+
const content = message["content"];
|
|
181
|
+
if (Array.isArray(content)) {
|
|
182
|
+
for (const block of content) {
|
|
183
|
+
if (isRec(block) &&
|
|
184
|
+
block["type"] === "toolCall" &&
|
|
185
|
+
typeof block["name"] === "string") {
|
|
186
|
+
const key = `${day}\u0000${block["name"]}`;
|
|
187
|
+
tools.set(key, (tools.get(key) ?? 0) + 1);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (turn !== undefined) {
|
|
192
|
+
if (turn.bucket === undefined)
|
|
193
|
+
turn.bucket = bucketOf(turn.day, "turn", ref);
|
|
194
|
+
turn.end = Date.parse(ts);
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
case "usage": {
|
|
199
|
+
const ref = refFor(String(value["provider"]), String(value["model"]));
|
|
200
|
+
addUsage(bucketOf(day, String(value["kind"] ?? "usage"), ref), value["usage"]);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
case "compaction":
|
|
204
|
+
case "branch_summary": {
|
|
205
|
+
if (value["usage"] === undefined)
|
|
206
|
+
return;
|
|
207
|
+
const ref = lastAssistant ?? { provider: "?", model: "?" };
|
|
208
|
+
addUsage(bucketOf(day, value["type"], ref), value["usage"]);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
case "context_edit":
|
|
212
|
+
if (value["reason"] === "retry" && lastBucket !== undefined)
|
|
213
|
+
lastBucket.retries++;
|
|
214
|
+
return;
|
|
215
|
+
default:
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
closeTurn();
|
|
224
|
+
if (header === undefined)
|
|
225
|
+
return undefined;
|
|
226
|
+
return {
|
|
227
|
+
id: String(header["id"]),
|
|
228
|
+
cwd: String(header["cwd"]),
|
|
229
|
+
buckets: [...buckets.values()],
|
|
230
|
+
tools: [...tools].map(([key, count]) => {
|
|
231
|
+
const [day = "", name = ""] = key.split("\u0000");
|
|
232
|
+
return [day, name, count];
|
|
233
|
+
}),
|
|
234
|
+
badLines,
|
|
235
|
+
};
|
|
236
|
+
}
|
package/dist/tools/presets.d.ts
CHANGED
|
@@ -5,27 +5,52 @@
|
|
|
5
5
|
* | ------------- | ----------------------------------------- |
|
|
6
6
|
* | `default` | read、edit、write、bash、grep、glob |
|
|
7
7
|
* | `minimal` | read、edit、write、bash |
|
|
8
|
-
* | `codemode`
|
|
8
|
+
* | `codemode-only` | 只有 codemode(其余工具在脚本里调用) |
|
|
9
9
|
* | `coordinator` | read + 宿主注册的工具 |
|
|
10
10
|
*
|
|
11
11
|
* - 预设只管**内置**工具;宿主 / SDK 注册的工具(`canvas_*` 等)总在活动集里,除非用户
|
|
12
12
|
* `--tools a,b,c` 整组替换(之后只用列出的名字)。
|
|
13
13
|
* - `tools.default`:`+name` / `-name` 在预设上增减;不带前缀的名字整组替换预设的内置工具,
|
|
14
14
|
* 之后再应用带前缀的项。未注册的名字记 warning 并忽略。
|
|
15
|
-
* - codemode
|
|
16
|
-
* - codemode
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* - `codemode` 是 `codemode-only` 的旧名(0.3.0),配置合并与命令行解析时折成规范名。
|
|
16
|
+
* - codemode 开关跟随预设(`presetCodemodeMode`):`default` → `on`(只在沙箱 strict,即 Node ≥ 25 隔离
|
|
17
|
+
* 网络时;Node 22 / 24 → `off`),`codemode-only` → `only`,`minimal` / `coordinator` → `off`。
|
|
18
|
+
* `codemode.mode` 显式配置覆盖(缺省配置不写这个键,以后调整映射能惠及老用户)。
|
|
19
|
+
* - `coordinator` 预设即使显式 `on`,脚本里能调用的工具也只限活动集(read + 宿主工具),见
|
|
20
|
+
* `codemodeCallableFilter`:协调者「不写文件、不跑 bash」的约定不能经 codemode 绕过。
|
|
21
|
+
* - codemode 工具由 B10 经组装根的 `toolFactories` 注册(生效模式为 off 时不注册);不可用
|
|
22
|
+
* (例如 `codemode.requireStrict` 而运行时 Node 不隔离网络)时 codemode-only 预设**回退到 default 并
|
|
23
|
+
* warning**(不报错:零配置用户不应因此起不来),显式 `on` 同样忽略;跟随预设得到的 `on` 静默回退。
|
|
19
24
|
* - `only` 模式活动集独占:宿主 / SDK 工具也不直接暴露,只能在脚本里调用(`exclusive`)。
|
|
20
25
|
*/
|
|
21
|
-
import type
|
|
26
|
+
import { type AmaConfig, type CodemodeMode, type ToolsPreset } from "../config/types.js";
|
|
22
27
|
import { ToolRegistry } from "./registry.js";
|
|
23
28
|
import type { ToolDefinition, ToolSource } from "./types.js";
|
|
24
29
|
export declare const CODEMODE_TOOL = "codemode";
|
|
25
|
-
/** 各预设下模型直接看到的内置工具(codemode 预设见 `resolvePreset`)。 */
|
|
30
|
+
/** 各预设下模型直接看到的内置工具(codemode-only 预设见 `resolvePreset`)。 */
|
|
26
31
|
export declare const PRESET_TOOLS: Readonly<Record<ToolsPreset, readonly string[]>>;
|
|
32
|
+
/**
|
|
33
|
+
* 预设对应的 codemode 模式(没有显式 `codemode.mode` 时)。`default` 只在沙箱 strict(Node ≥ 25,
|
|
34
|
+
* 权限模型同时隔离网络)时开 `on`:Node 22 / 24 下 codemode 属 execute 类,每次都要审批,`-p` 下
|
|
35
|
+
* 直接被拒,所以缺省关闭(启动时提示一次,见 cli/codemode-notice.ts)。
|
|
36
|
+
*/
|
|
37
|
+
export declare function presetCodemodeMode(preset: ToolsPreset, strict: boolean): CodemodeMode;
|
|
38
|
+
export interface CodemodeModeResolution {
|
|
39
|
+
mode: CodemodeMode;
|
|
40
|
+
/** `config`:显式 `codemode.mode`;`preset`:跟随预设。 */
|
|
41
|
+
source: "config" | "preset";
|
|
42
|
+
preset: ToolsPreset;
|
|
43
|
+
strict: boolean;
|
|
44
|
+
}
|
|
45
|
+
/** 生效的 codemode 模式及其来源;`strict` 缺省按运行时探测(测试与组装根可注入)。 */
|
|
46
|
+
export declare function resolveCodemodeMode(config: Pick<AmaConfig, "tools" | "codemode">, strict?: boolean): CodemodeModeResolution;
|
|
27
47
|
/** 生效的 codemode 模式:显式配置优先,否则跟随预设。 */
|
|
28
|
-
export declare function effectiveCodemodeMode(config: Pick<AmaConfig, "tools" | "codemode"
|
|
48
|
+
export declare function effectiveCodemodeMode(config: Pick<AmaConfig, "tools" | "codemode">, strict?: boolean): CodemodeMode;
|
|
49
|
+
/**
|
|
50
|
+
* codemode 脚本里可调用工具的范围:`coordinator` 预设只能调活动集里的工具(`"active"`),其它预设
|
|
51
|
+
* 不限(`undefined`;`on` 模式下脚本还能调没直接暴露的 ls / task / todo 等)。
|
|
52
|
+
*/
|
|
53
|
+
export declare function codemodeCallableFilter(config: Pick<AmaConfig, "tools">): "active" | undefined;
|
|
29
54
|
/**
|
|
30
55
|
* `tools.default` 微调:不带前缀的名字整组替换 `base`,再依次应用 `+name` / `-name`。
|
|
31
56
|
* 返回去重后的名字(保持首次出现顺序)。
|
|
@@ -43,6 +68,8 @@ export declare function resolvePreset(input: {
|
|
|
43
68
|
config: Pick<AmaConfig, "tools" | "codemode">;
|
|
44
69
|
/** 该名字是否已注册(且未禁用)。 */
|
|
45
70
|
available(name: string): boolean;
|
|
71
|
+
/** 沙箱是否 strict;缺省按运行时探测。 */
|
|
72
|
+
strict?: boolean;
|
|
46
73
|
}): PresetResolution;
|
|
47
74
|
type RegisterListener = (tool: ToolDefinition, source: ToolSource) => void;
|
|
48
75
|
/**
|