c0de-agent 0.0.0 → 1.0.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/LICENSE +21 -0
- package/README.md +101 -0
- package/dist/cli/commands/acp.d.ts +19 -0
- package/dist/cli/commands/acp.js +50 -0
- package/dist/cli/commands/chat.d.ts +13 -0
- package/dist/cli/commands/chat.js +26 -0
- package/dist/cli/commands/config.d.ts +17 -0
- package/dist/cli/commands/config.js +77 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.js +14 -0
- package/dist/cli/commands/plugin.d.ts +11 -0
- package/dist/cli/commands/plugin.js +31 -0
- package/dist/cli/commands/serve.d.ts +18 -0
- package/dist/cli/commands/serve.js +35 -0
- package/dist/cli/commands/update.d.ts +24 -0
- package/dist/cli/commands/update.js +40 -0
- package/dist/cli/deps.d.ts +19 -0
- package/dist/cli/deps.js +57 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +149 -0
- package/dist/cli/modes/acp.d.ts +18 -0
- package/dist/cli/modes/acp.js +57 -0
- package/dist/cli/modes/print.d.ts +16 -0
- package/dist/cli/modes/print.js +30 -0
- package/dist/cli/parser.d.ts +19 -0
- package/dist/cli/parser.js +28 -0
- package/dist/cli/utils/output.d.ts +11 -0
- package/dist/cli/utils/output.js +23 -0
- package/dist/cli/utils/prompt.d.ts +12 -0
- package/dist/cli/utils/prompt.js +18 -0
- package/dist/core/agent.d.ts +12 -0
- package/dist/core/agent.js +95 -0
- package/dist/core/agents/builtin.d.ts +6 -0
- package/dist/core/agents/builtin.js +76 -0
- package/dist/core/agents/discovery.d.ts +6 -0
- package/dist/core/agents/discovery.js +96 -0
- package/dist/core/agents/index.d.ts +6 -0
- package/dist/core/agents/index.js +4 -0
- package/dist/core/agents/parallel.d.ts +13 -0
- package/dist/core/agents/parallel.js +37 -0
- package/dist/core/agents/registry.d.ts +4 -0
- package/dist/core/agents/registry.js +23 -0
- package/dist/core/agents/types.d.ts +37 -0
- package/dist/core/agents/types.js +1 -0
- package/dist/core/compact.d.ts +11 -0
- package/dist/core/compact.js +31 -0
- package/dist/core/config.d.ts +7 -0
- package/dist/core/config.js +84 -0
- package/dist/core/context.d.ts +23 -0
- package/dist/core/context.js +93 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.js +11 -0
- package/dist/core/loop.d.ts +25 -0
- package/dist/core/loop.js +651 -0
- package/dist/core/metrics.d.ts +27 -0
- package/dist/core/metrics.js +94 -0
- package/dist/core/prompt-registry.d.ts +21 -0
- package/dist/core/prompt-registry.js +200 -0
- package/dist/core/prompt.d.ts +11 -0
- package/dist/core/prompt.js +13 -0
- package/dist/core/secret.d.ts +12 -0
- package/dist/core/secret.js +51 -0
- package/dist/core/slash.d.ts +15 -0
- package/dist/core/slash.js +114 -0
- package/dist/core/steering.d.ts +8 -0
- package/dist/core/steering.js +15 -0
- package/dist/core/title.d.ts +42 -0
- package/dist/core/title.js +115 -0
- package/dist/core/tool-exec.d.ts +20 -0
- package/dist/core/tool-exec.js +64 -0
- package/dist/core/types.d.ts +95 -0
- package/dist/core/types.js +1 -0
- package/dist/core/worktree.d.ts +24 -0
- package/dist/core/worktree.js +68 -0
- package/dist/dap/index.d.ts +6 -0
- package/dist/dap/index.js +4 -0
- package/dist/dap/protocol.d.ts +40 -0
- package/dist/dap/protocol.js +132 -0
- package/dist/dap/session.d.ts +23 -0
- package/dist/dap/session.js +130 -0
- package/dist/dap/transport.d.ts +8 -0
- package/dist/dap/transport.js +30 -0
- package/dist/dap/types.d.ts +50 -0
- package/dist/dap/types.js +2 -0
- package/dist/db/client.d.ts +22 -0
- package/dist/db/client.js +39 -0
- package/dist/db/index.d.ts +6 -0
- package/dist/db/index.js +4 -0
- package/dist/db/migrate.d.ts +9 -0
- package/dist/db/migrate.js +15 -0
- package/dist/db/schema.d.ts +965 -0
- package/dist/db/schema.js +112 -0
- package/dist/db/types.d.ts +11 -0
- package/dist/db/types.js +1 -0
- package/dist/llm/index.d.ts +13 -0
- package/dist/llm/index.js +8 -0
- package/dist/llm/protocols/openai-compat.d.ts +139 -0
- package/dist/llm/protocols/openai-compat.js +214 -0
- package/dist/llm/protocols/utils/index.d.ts +2 -0
- package/dist/llm/protocols/utils/index.js +2 -0
- package/dist/llm/protocols/utils/lifecycle.d.ts +23 -0
- package/dist/llm/protocols/utils/lifecycle.js +46 -0
- package/dist/llm/protocols/utils/tool-stream.d.ts +46 -0
- package/dist/llm/protocols/utils/tool-stream.js +94 -0
- package/dist/llm/provider-error.d.ts +5 -0
- package/dist/llm/provider-error.js +33 -0
- package/dist/llm/provider.d.ts +32 -0
- package/dist/llm/provider.js +191 -0
- package/dist/llm/registry.d.ts +44 -0
- package/dist/llm/registry.js +107 -0
- package/dist/llm/retry.d.ts +37 -0
- package/dist/llm/retry.js +82 -0
- package/dist/llm/routing.d.ts +36 -0
- package/dist/llm/routing.js +53 -0
- package/dist/llm/schema/errors.d.ts +88 -0
- package/dist/llm/schema/errors.js +33 -0
- package/dist/llm/schema/events.d.ts +135 -0
- package/dist/llm/schema/events.js +35 -0
- package/dist/llm/schema/ids.d.ts +22 -0
- package/dist/llm/schema/ids.js +3 -0
- package/dist/llm/schema/index.d.ts +5 -0
- package/dist/llm/schema/index.js +5 -0
- package/dist/llm/schema/messages.d.ts +97 -0
- package/dist/llm/schema/messages.js +18 -0
- package/dist/llm/schema/options.d.ts +54 -0
- package/dist/llm/schema/options.js +54 -0
- package/dist/llm/token.d.ts +2 -0
- package/dist/llm/token.js +13 -0
- package/dist/llm/transport.d.ts +31 -0
- package/dist/llm/transport.js +165 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.js +3 -0
- package/dist/plugins/builtin.d.ts +6 -0
- package/dist/plugins/builtin.js +54 -0
- package/dist/plugins/hooks.d.ts +3 -0
- package/dist/plugins/hooks.js +79 -0
- package/dist/plugins/index.d.ts +9 -0
- package/dist/plugins/index.js +8 -0
- package/dist/plugins/init.d.ts +23 -0
- package/dist/plugins/init.js +30 -0
- package/dist/plugins/lifecycle.d.ts +6 -0
- package/dist/plugins/lifecycle.js +74 -0
- package/dist/plugins/loader.d.ts +16 -0
- package/dist/plugins/loader.js +68 -0
- package/dist/plugins/logger.d.ts +4 -0
- package/dist/plugins/logger.js +31 -0
- package/dist/plugins/registry.d.ts +7 -0
- package/dist/plugins/registry.js +24 -0
- package/dist/plugins/types.d.ts +113 -0
- package/dist/plugins/types.js +1 -0
- package/dist/project/detect.d.ts +4 -0
- package/dist/project/detect.js +131 -0
- package/dist/project/index.d.ts +4 -0
- package/dist/project/index.js +2 -0
- package/dist/project/project.d.ts +23 -0
- package/dist/project/project.js +87 -0
- package/dist/project/resolve.d.ts +8 -0
- package/dist/project/resolve.js +81 -0
- package/dist/server/agent-manager.d.ts +32 -0
- package/dist/server/agent-manager.js +54 -0
- package/dist/server/app.d.ts +5 -0
- package/dist/server/app.js +75 -0
- package/dist/server/context.d.ts +20 -0
- package/dist/server/context.js +47 -0
- package/dist/server/dev.d.ts +11 -0
- package/dist/server/dev.js +83 -0
- package/dist/server/index.d.ts +11 -0
- package/dist/server/index.js +7 -0
- package/dist/server/middleware/auth.d.ts +15 -0
- package/dist/server/middleware/auth.js +29 -0
- package/dist/server/middleware/cors.d.ts +16 -0
- package/dist/server/middleware/cors.js +44 -0
- package/dist/server/middleware/error.d.ts +15 -0
- package/dist/server/middleware/error.js +11 -0
- package/dist/server/permission/interactive.d.ts +24 -0
- package/dist/server/permission/interactive.js +56 -0
- package/dist/server/permission/store.d.ts +30 -0
- package/dist/server/permission/store.js +47 -0
- package/dist/server/routes/agent.d.ts +4 -0
- package/dist/server/routes/agent.js +19 -0
- package/dist/server/routes/catalog.d.ts +42 -0
- package/dist/server/routes/catalog.js +194 -0
- package/dist/server/routes/chat.d.ts +8 -0
- package/dist/server/routes/chat.js +282 -0
- package/dist/server/routes/commands.d.ts +5 -0
- package/dist/server/routes/commands.js +17 -0
- package/dist/server/routes/config.d.ts +4 -0
- package/dist/server/routes/config.js +27 -0
- package/dist/server/routes/files.d.ts +4 -0
- package/dist/server/routes/files.js +183 -0
- package/dist/server/routes/filesystem.d.ts +29 -0
- package/dist/server/routes/filesystem.js +108 -0
- package/dist/server/routes/health.d.ts +3 -0
- package/dist/server/routes/health.js +13 -0
- package/dist/server/routes/permissions.d.ts +5 -0
- package/dist/server/routes/permissions.js +23 -0
- package/dist/server/routes/project.d.ts +4 -0
- package/dist/server/routes/project.js +51 -0
- package/dist/server/routes/provider.d.ts +16 -0
- package/dist/server/routes/provider.js +66 -0
- package/dist/server/routes/session.d.ts +4 -0
- package/dist/server/routes/session.js +130 -0
- package/dist/server/routes/tool.d.ts +4 -0
- package/dist/server/routes/tool.js +28 -0
- package/dist/server/routes/update.d.ts +14 -0
- package/dist/server/routes/update.js +49 -0
- package/dist/server/server.d.ts +43 -0
- package/dist/server/server.js +197 -0
- package/dist/server/types.d.ts +73 -0
- package/dist/server/types.js +2 -0
- package/dist/server/util/safe-path.d.ts +7 -0
- package/dist/server/util/safe-path.js +7 -0
- package/dist/session/archive.d.ts +15 -0
- package/dist/session/archive.js +129 -0
- package/dist/session/branch.d.ts +9 -0
- package/dist/session/branch.js +63 -0
- package/dist/session/compaction.d.ts +27 -0
- package/dist/session/compaction.js +192 -0
- package/dist/session/context.d.ts +16 -0
- package/dist/session/context.js +173 -0
- package/dist/session/index.d.ts +10 -0
- package/dist/session/index.js +10 -0
- package/dist/session/message.d.ts +22 -0
- package/dist/session/message.js +145 -0
- package/dist/session/session.d.ts +37 -0
- package/dist/session/session.js +144 -0
- package/dist/session/snapshot.d.ts +11 -0
- package/dist/session/snapshot.js +60 -0
- package/dist/session/squash.d.ts +9 -0
- package/dist/session/squash.js +88 -0
- package/dist/session/token.d.ts +10 -0
- package/dist/session/token.js +33 -0
- package/dist/session/types.d.ts +122 -0
- package/dist/session/types.js +1 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +1 -0
- package/dist/shared/types/agent.d.ts +202 -0
- package/dist/shared/types/agent.js +1 -0
- package/dist/shared/types/base.d.ts +32 -0
- package/dist/shared/types/base.js +1 -0
- package/dist/shared/types/config.d.ts +104 -0
- package/dist/shared/types/config.js +1 -0
- package/dist/shared/types/index.d.ts +6 -0
- package/dist/shared/types/index.js +1 -0
- package/dist/shared/types/llm.d.ts +122 -0
- package/dist/shared/types/llm.js +1 -0
- package/dist/shared/types/message.d.ts +71 -0
- package/dist/shared/types/message.js +1 -0
- package/dist/shared/types/tool.d.ts +134 -0
- package/dist/shared/types/tool.js +1 -0
- package/dist/shared/utils/path.d.ts +15 -0
- package/dist/shared/utils/path.js +23 -0
- package/dist/shared/utils.d.ts +5 -0
- package/dist/shared/utils.js +10 -0
- package/dist/tools/builtin/bash.d.ts +12 -0
- package/dist/tools/builtin/bash.js +121 -0
- package/dist/tools/builtin/dap.d.ts +23 -0
- package/dist/tools/builtin/dap.js +231 -0
- package/dist/tools/builtin/edit.d.ts +12 -0
- package/dist/tools/builtin/edit.js +151 -0
- package/dist/tools/builtin/glob.d.ts +11 -0
- package/dist/tools/builtin/glob.js +120 -0
- package/dist/tools/builtin/grep.d.ts +6 -0
- package/dist/tools/builtin/grep.js +141 -0
- package/dist/tools/builtin/read.d.ts +6 -0
- package/dist/tools/builtin/read.js +73 -0
- package/dist/tools/builtin/resolvers.d.ts +12 -0
- package/dist/tools/builtin/resolvers.js +79 -0
- package/dist/tools/builtin/task.d.ts +9 -0
- package/dist/tools/builtin/task.js +100 -0
- package/dist/tools/builtin/write.d.ts +6 -0
- package/dist/tools/builtin/write.js +36 -0
- package/dist/tools/builtin/yield.d.ts +9 -0
- package/dist/tools/builtin/yield.js +40 -0
- package/dist/tools/executor.d.ts +9 -0
- package/dist/tools/executor.js +54 -0
- package/dist/tools/hashline/index.d.ts +2 -0
- package/dist/tools/hashline/index.js +3 -0
- package/dist/tools/hashline/patch.d.ts +48 -0
- package/dist/tools/hashline/patch.js +201 -0
- package/dist/tools/index.d.ts +30 -0
- package/dist/tools/index.js +55 -0
- package/dist/tools/permission.d.ts +13 -0
- package/dist/tools/permission.js +39 -0
- package/dist/tools/registry.d.ts +15 -0
- package/dist/tools/registry.js +54 -0
- package/dist/tools/resolver.d.ts +15 -0
- package/dist/tools/resolver.js +47 -0
- package/dist/tools/truncate.d.ts +8 -0
- package/dist/tools/truncate.js +50 -0
- package/dist/tools/types.d.ts +101 -0
- package/dist/tools/types.js +1 -0
- package/dist/tools/validate.d.ts +8 -0
- package/dist/tools/validate.js +130 -0
- package/dist/tools/websearch/fetch.d.ts +11 -0
- package/dist/tools/websearch/fetch.js +21 -0
- package/dist/tools/websearch/index.d.ts +19 -0
- package/dist/tools/websearch/index.js +78 -0
- package/dist/tools/websearch/providers/brave.d.ts +5 -0
- package/dist/tools/websearch/providers/brave.js +53 -0
- package/dist/tools/websearch/providers/duckduckgo.d.ts +3 -0
- package/dist/tools/websearch/providers/duckduckgo.js +75 -0
- package/dist/tools/websearch/providers/tavily.d.ts +11 -0
- package/dist/tools/websearch/providers/tavily.js +89 -0
- package/dist/tools/websearch/types.d.ts +51 -0
- package/dist/tools/websearch/types.js +18 -0
- package/dist/tools/websearch/websearch.d.ts +10 -0
- package/dist/tools/websearch/websearch.js +48 -0
- package/dist/update/index.d.ts +10 -0
- package/dist/update/index.js +6 -0
- package/dist/update/ipc.d.ts +15 -0
- package/dist/update/ipc.js +41 -0
- package/dist/update/scheduler.d.ts +34 -0
- package/dist/update/scheduler.js +57 -0
- package/dist/update/snapshot.d.ts +39 -0
- package/dist/update/snapshot.js +99 -0
- package/dist/update/updater.d.ts +48 -0
- package/dist/update/updater.js +83 -0
- package/dist/update/version.d.ts +18 -0
- package/dist/update/version.js +49 -0
- package/drizzle/0000_magical_nightshade.sql +55 -0
- package/drizzle/0001_chemical_doctor_faustus.sql +13 -0
- package/drizzle/0002_amusing_micromacro.sql +12 -0
- package/drizzle/0003_regular_purifiers.sql +2 -0
- package/drizzle/meta/0000_snapshot.json +448 -0
- package/drizzle/meta/0001_snapshot.json +539 -0
- package/drizzle/meta/0002_snapshot.json +633 -0
- package/drizzle/meta/0003_snapshot.json +645 -0
- package/drizzle/meta/_journal.json +34 -0
- package/package.json +100 -8
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { and, eq } from 'drizzle-orm';
|
|
2
|
+
import { toolMetrics } from '../db/schema.js';
|
|
3
|
+
/** 非 edit 工具的占位模式(这些工具只有一种实现,无需选择)。 */
|
|
4
|
+
export const DEFAULT_TOOL_MODE = 'default';
|
|
5
|
+
/** edit 工具在数据不足或历史不达标时使用的保守默认(spec §16.5)。 */
|
|
6
|
+
export const DEFAULT_EDIT_MODE = 'diff';
|
|
7
|
+
/** 推断某次工具调用使用的模式。
|
|
8
|
+
* - edit:入参含 `patch` → hashline;否则 diff(search/replace)。
|
|
9
|
+
* - 其他工具:固定 default(单实现,不参与模式选择)。 */
|
|
10
|
+
export function inferToolMode(tool, input) {
|
|
11
|
+
if (tool === 'edit') {
|
|
12
|
+
if (input !== null && typeof input === 'object' && 'patch' in input)
|
|
13
|
+
return 'hashline';
|
|
14
|
+
return 'diff';
|
|
15
|
+
}
|
|
16
|
+
return DEFAULT_TOOL_MODE;
|
|
17
|
+
}
|
|
18
|
+
/** DB 行(Date 时间戳)→ 共享 ModelToolMetrics(number 时间戳)。 */
|
|
19
|
+
function rowToMetrics(row) {
|
|
20
|
+
const lastUsed = row.lastUsed instanceof Date ? row.lastUsed.getTime() : new Date(row.lastUsed).getTime();
|
|
21
|
+
return {
|
|
22
|
+
model: row.model,
|
|
23
|
+
tool: row.tool,
|
|
24
|
+
mode: row.mode,
|
|
25
|
+
attempts: row.attempts,
|
|
26
|
+
successes: row.successes,
|
|
27
|
+
failures: row.failures,
|
|
28
|
+
avgLatencyMs: row.avgLatencyMs,
|
|
29
|
+
lastUsed,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** 查某 (model, tool) 所有已记录模式的 metrics。 */
|
|
33
|
+
export async function getToolMetrics(handle, model, tool) {
|
|
34
|
+
const rows = await handle.db
|
|
35
|
+
.select()
|
|
36
|
+
.from(toolMetrics)
|
|
37
|
+
.where(and(eq(toolMetrics.model, model), eq(toolMetrics.tool, tool)));
|
|
38
|
+
return rows.map(rowToMetrics);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 记录一次工具调用的结果(upsert)。avgLatencyMs 用增量滚动平均更新。
|
|
42
|
+
* PGLite 单进程,select-then-update 无并发竞争。
|
|
43
|
+
*/
|
|
44
|
+
export async function recordToolMetrics(handle, model, tool, mode, success, latencyMs) {
|
|
45
|
+
const whereMode = and(eq(toolMetrics.model, model), eq(toolMetrics.tool, tool), eq(toolMetrics.mode, mode));
|
|
46
|
+
const existing = await handle.db.select().from(toolMetrics).where(whereMode).limit(1);
|
|
47
|
+
if (existing.length === 0) {
|
|
48
|
+
await handle.db.insert(toolMetrics).values({
|
|
49
|
+
model,
|
|
50
|
+
tool,
|
|
51
|
+
mode,
|
|
52
|
+
attempts: 1,
|
|
53
|
+
successes: success ? 1 : 0,
|
|
54
|
+
failures: success ? 0 : 1,
|
|
55
|
+
avgLatencyMs: latencyMs,
|
|
56
|
+
lastUsed: new Date(),
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const row = existing[0];
|
|
61
|
+
if (!row)
|
|
62
|
+
return;
|
|
63
|
+
const attempts = row.attempts + 1;
|
|
64
|
+
const successes = row.successes + (success ? 1 : 0);
|
|
65
|
+
const failures = row.failures + (success ? 0 : 1);
|
|
66
|
+
// 滚动平均:新均值 = 旧均值 + (新值 - 旧均值) / n
|
|
67
|
+
const avgLatencyMs = row.avgLatencyMs + (latencyMs - row.avgLatencyMs) / attempts;
|
|
68
|
+
await handle.db
|
|
69
|
+
.update(toolMetrics)
|
|
70
|
+
.set({ attempts, successes, failures, avgLatencyMs, lastUsed: new Date() })
|
|
71
|
+
.where(whereMode);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 根据历史成功率选择最优工具模式(spec §16.5)。
|
|
75
|
+
* - 样本不足(attempts < minSamples)→ defaultMode
|
|
76
|
+
* - 成功率 >= threshold 的模式中取成功率最高(并列取样本最多者)
|
|
77
|
+
* - 无达标模式 → defaultMode
|
|
78
|
+
*/
|
|
79
|
+
export function selectBestMode(metrics, defaultMode, opts = {}) {
|
|
80
|
+
const threshold = opts.threshold ?? 0.8;
|
|
81
|
+
const minSamples = opts.minSamples ?? 5;
|
|
82
|
+
const eligible = metrics.filter((x) => x.attempts >= minSamples);
|
|
83
|
+
if (eligible.length === 0)
|
|
84
|
+
return defaultMode;
|
|
85
|
+
const good = eligible.filter((x) => x.successes / x.attempts >= threshold);
|
|
86
|
+
if (good.length === 0)
|
|
87
|
+
return defaultMode;
|
|
88
|
+
good.sort((a, b) => {
|
|
89
|
+
const ra = a.successes / a.attempts;
|
|
90
|
+
const rb = b.successes / b.attempts;
|
|
91
|
+
return rb - ra || b.attempts - a.attempts;
|
|
92
|
+
});
|
|
93
|
+
return good[0]?.mode ?? defaultMode;
|
|
94
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PromptBuildContext, PromptRegistry, PromptSection } from './types.js';
|
|
2
|
+
declare const ROLE_DESCRIPTION = "You are c0de-agent, an open-source AI coding assistant the team trusts with load-bearing changes. You help developers write, debug, and understand code across multiple languages and frameworks.\n\nYou operate autonomously: assume the user wants real changes and carry work through to completion end-to-end. Do not stop at analysis or partial fixes. The only times to pause and confirm are: before destructive git operations (commit, reset --hard, amend), before deleting code you did not write, and when a request is genuinely ambiguous between materially different approaches.\n\nPrioritize technical accuracy and truthfulness over validating the user's beliefs. Investigate before confirming; disagree respectfully when necessary. Objective guidance is more valuable than false agreement.";
|
|
3
|
+
declare const ENGINEERING_PRINCIPLES = "# Engineering Principles\n- Optimize for correctness first, then for the next maintainer six months out.\n- You have agency and taste: delete code that isn't pulling its weight, refuse unnecessary abstractions, prefer boring when it's called for.\n- Consider what code compiles to. Never allocate avoidably; no needless copies or computation.\n- You are not alone in this repo. Treat unexpected changes as the user's work and adapt \u2014 never revert edits you did not make unless explicitly asked.";
|
|
4
|
+
declare const TOOL_USAGE = "# Tool Usage\nYou have dedicated tools \u2014 prefer them over shell commands for file operations:\n- Reading a file or listing a directory \u2192 `read` (NOT `cat`, `head`, `tail`).\n- Finding files by name or pattern \u2192 `glob` (NOT `find`, `ls -R`).\n- Searching file contents \u2192 `grep` (NOT shell `grep`/`rg`/`ack`, NOT `awk`/`sed`).\n- Modifying files \u2192 `edit`/`write` (NOT `sed`, `echo >`, heredocs).\n\nReserve `bash` for genuine command execution: builds, tests, git, or short pipelines that compute a fact (`wc -l`, `git status`, `diff`, a checksum). Never explore a codebase with `find`/`ls`/`cat` when `read`/`glob`/`grep` can.\n\nBatch independent tool calls in a single response \u2014 parallelize file reads and independent lookups. Only sequence when one call's result informs the next.\n\nWhen referencing code, use the `file_path:line_number` pattern so the user can navigate directly.";
|
|
5
|
+
declare const CODEBASE = "# Working with the Codebase\nBefore changing files, understand the existing conventions. Mimic code style, reuse existing utilities, and follow established patterns.\n- NEVER assume a library is available, even if well known. Check package.json (or equivalent) and neighboring files first.\n- When creating a new component, look at existing ones first for naming, typing, and framework conventions.\n- When editing, read the surrounding context (especially imports) to make the change idiomatic. Never introduce code that exposes or logs secrets.";
|
|
6
|
+
declare const EXECUTION_WORKFLOW = "# Execution Workflow\n1. Scope \u2014 plan before touching files; research existing code and conventions.\n2. Research \u2014 read sections, not snippets. Reuse existing patterns; a second convention beside an existing one is prohibited.\n3. Decompose \u2014 break multi-step work into steps and track them; skip for trivial requests. Plan only what makes the request work.\n4. Implement \u2014 fix problems at the source. Remove obsolete code \u2014 no leftover comments, aliases, or re-exports. Prefer editing existing files over new ones.\n5. Verify \u2014 never yield non-trivial work without proof: run the relevant tests. Prefer testing behavior, not plumbing. Don't test defaults.\n6. Cleanup \u2014 changelog, tests, docs, and removing scaffolding are the LAST phase, gated on the request demonstrably working. Never pre-plan cleanup before the request works.";
|
|
7
|
+
declare const VERIFICATION = "# Verification & Evidence\n- Never yield non-trivial work without proof: tests, builds, or QA.\n- Run lint and typecheck after changes if the project provides them.\n- Every claim about code, tools, or tests must be grounded. Mark anything not directly observed as [INFERENCE].\n- Verification claims must match what was actually exercised. A passing typecheck does not prove an integration.";
|
|
8
|
+
declare const DELIVERY_CONTRACT = "# Delivery Contract\n- \"Done\" means the deliverable behaves as specified end-to-end \u2014 not that a scaffold compiles.\n- Never yield unless complete. A phase boundary is never a yield point.\n- Never suppress tests to make code pass. Never fabricate outputs.\n- Never substitute an easier problem: don't infer extra scope, don't treat the symptom unless asked.\n- Never ship stubs, placeholders, mocks, no-ops, or fake fallbacks as finished work.\n- Default to clean cutover: migrate every caller; leave no shims or aliases.";
|
|
9
|
+
declare const GIT_SAFETY = "# Git & Safety\n- NEVER commit unless the user explicitly asks. Committing is too proactive.\n- NEVER use `git reset --hard` or `git checkout --` unless explicitly approved.\n- Do not amend a commit unless asked.\n- You may be in a dirty worktree. NEVER revert changes you did not make; if unrelated changes conflict with your task, stop and ask.";
|
|
10
|
+
declare const TONE = "# Tone & Output\n- Be concise and direct. Lead with the conclusion, then the evidence.\n- No preamble or postamble (\"The answer is\u2026\", \"Here is what I'll do next\u2026\").\n- Use GitHub-flavored markdown. Only use emojis if explicitly asked.\n- Don't hide uncertainty: state it at the specific claim, name the tradeoff.\n- For a simple question, a one-liner is best.";
|
|
11
|
+
export { CODEBASE, DELIVERY_CONTRACT, ENGINEERING_PRINCIPLES, EXECUTION_WORKFLOW, GIT_SAFETY, ROLE_DESCRIPTION, TONE, TOOL_USAGE, VERIFICATION, };
|
|
12
|
+
/** Built-in section ids in priority order. */
|
|
13
|
+
export declare const BUILTIN_SECTION_IDS: readonly ["role", "systemPrompt", "engineering", "tool-usage", "codebase", "constraints", "execution-workflow", "verification", "delivery-contract", "git-safety", "tone", "project", "skills", "agents", "slash-commands"];
|
|
14
|
+
/** Create a registry pre-loaded with the built-in sections. */
|
|
15
|
+
declare function createPromptRegistry(): PromptRegistry;
|
|
16
|
+
/** Register a section. A later registration with the same id overrides the earlier
|
|
17
|
+
* one (lets plugins replace builtins). */
|
|
18
|
+
declare function registerPromptSection(registry: PromptRegistry, section: PromptSection): void;
|
|
19
|
+
/** Assemble the system prompt: filter by condition, sort by priority, render. */
|
|
20
|
+
declare function buildDynamicPrompt(registry: PromptRegistry, ctx: PromptBuildContext): string;
|
|
21
|
+
export { buildDynamicPrompt, createPromptRegistry, registerPromptSection };
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { builtinCommands } from './slash.js';
|
|
2
|
+
// ── Static section bodies (shared with the legacy buildSystemPrompt) ──
|
|
3
|
+
const ROLE_DESCRIPTION = `You are c0de-agent, an open-source AI coding assistant the team trusts with load-bearing changes. You help developers write, debug, and understand code across multiple languages and frameworks.
|
|
4
|
+
|
|
5
|
+
You operate autonomously: assume the user wants real changes and carry work through to completion end-to-end. Do not stop at analysis or partial fixes. The only times to pause and confirm are: before destructive git operations (commit, reset --hard, amend), before deleting code you did not write, and when a request is genuinely ambiguous between materially different approaches.
|
|
6
|
+
|
|
7
|
+
Prioritize technical accuracy and truthfulness over validating the user's beliefs. Investigate before confirming; disagree respectfully when necessary. Objective guidance is more valuable than false agreement.`;
|
|
8
|
+
const ENGINEERING_PRINCIPLES = `# Engineering Principles
|
|
9
|
+
- Optimize for correctness first, then for the next maintainer six months out.
|
|
10
|
+
- You have agency and taste: delete code that isn't pulling its weight, refuse unnecessary abstractions, prefer boring when it's called for.
|
|
11
|
+
- Consider what code compiles to. Never allocate avoidably; no needless copies or computation.
|
|
12
|
+
- You are not alone in this repo. Treat unexpected changes as the user's work and adapt — never revert edits you did not make unless explicitly asked.`;
|
|
13
|
+
const TOOL_USAGE = `# Tool Usage
|
|
14
|
+
You have dedicated tools — prefer them over shell commands for file operations:
|
|
15
|
+
- Reading a file or listing a directory → \`read\` (NOT \`cat\`, \`head\`, \`tail\`).
|
|
16
|
+
- Finding files by name or pattern → \`glob\` (NOT \`find\`, \`ls -R\`).
|
|
17
|
+
- Searching file contents → \`grep\` (NOT shell \`grep\`/\`rg\`/\`ack\`, NOT \`awk\`/\`sed\`).
|
|
18
|
+
- Modifying files → \`edit\`/\`write\` (NOT \`sed\`, \`echo >\`, heredocs).
|
|
19
|
+
|
|
20
|
+
Reserve \`bash\` for genuine command execution: builds, tests, git, or short pipelines that compute a fact (\`wc -l\`, \`git status\`, \`diff\`, a checksum). Never explore a codebase with \`find\`/\`ls\`/\`cat\` when \`read\`/\`glob\`/\`grep\` can.
|
|
21
|
+
|
|
22
|
+
Batch independent tool calls in a single response — parallelize file reads and independent lookups. Only sequence when one call's result informs the next.
|
|
23
|
+
|
|
24
|
+
When referencing code, use the \`file_path:line_number\` pattern so the user can navigate directly.`;
|
|
25
|
+
const CODEBASE = `# Working with the Codebase
|
|
26
|
+
Before changing files, understand the existing conventions. Mimic code style, reuse existing utilities, and follow established patterns.
|
|
27
|
+
- NEVER assume a library is available, even if well known. Check package.json (or equivalent) and neighboring files first.
|
|
28
|
+
- When creating a new component, look at existing ones first for naming, typing, and framework conventions.
|
|
29
|
+
- When editing, read the surrounding context (especially imports) to make the change idiomatic. Never introduce code that exposes or logs secrets.`;
|
|
30
|
+
const PARADIGM_CONSTRAINTS = `# Coding Paradigm
|
|
31
|
+
This project follows a strict data + functions paradigm:
|
|
32
|
+
- Use \`type\` (not \`interface\`) for type definitions.
|
|
33
|
+
- Use discriminated unions with \`_tag\` fields for variant types.
|
|
34
|
+
- Use plain functions \`export function foo(ctx, ...)\` with context-first argument.
|
|
35
|
+
- No classes; prefer factory functions and pure data transformation.
|
|
36
|
+
- Prefer \`import type\` for type-only imports.`;
|
|
37
|
+
const EXECUTION_WORKFLOW = `# Execution Workflow
|
|
38
|
+
1. Scope — plan before touching files; research existing code and conventions.
|
|
39
|
+
2. Research — read sections, not snippets. Reuse existing patterns; a second convention beside an existing one is prohibited.
|
|
40
|
+
3. Decompose — break multi-step work into steps and track them; skip for trivial requests. Plan only what makes the request work.
|
|
41
|
+
4. Implement — fix problems at the source. Remove obsolete code — no leftover comments, aliases, or re-exports. Prefer editing existing files over new ones.
|
|
42
|
+
5. Verify — never yield non-trivial work without proof: run the relevant tests. Prefer testing behavior, not plumbing. Don't test defaults.
|
|
43
|
+
6. Cleanup — changelog, tests, docs, and removing scaffolding are the LAST phase, gated on the request demonstrably working. Never pre-plan cleanup before the request works.`;
|
|
44
|
+
const VERIFICATION = `# Verification & Evidence
|
|
45
|
+
- Never yield non-trivial work without proof: tests, builds, or QA.
|
|
46
|
+
- Run lint and typecheck after changes if the project provides them.
|
|
47
|
+
- Every claim about code, tools, or tests must be grounded. Mark anything not directly observed as [INFERENCE].
|
|
48
|
+
- Verification claims must match what was actually exercised. A passing typecheck does not prove an integration.`;
|
|
49
|
+
const DELIVERY_CONTRACT = `# Delivery Contract
|
|
50
|
+
- "Done" means the deliverable behaves as specified end-to-end — not that a scaffold compiles.
|
|
51
|
+
- Never yield unless complete. A phase boundary is never a yield point.
|
|
52
|
+
- Never suppress tests to make code pass. Never fabricate outputs.
|
|
53
|
+
- Never substitute an easier problem: don't infer extra scope, don't treat the symptom unless asked.
|
|
54
|
+
- Never ship stubs, placeholders, mocks, no-ops, or fake fallbacks as finished work.
|
|
55
|
+
- Default to clean cutover: migrate every caller; leave no shims or aliases.`;
|
|
56
|
+
const GIT_SAFETY = `# Git & Safety
|
|
57
|
+
- NEVER commit unless the user explicitly asks. Committing is too proactive.
|
|
58
|
+
- NEVER use \`git reset --hard\` or \`git checkout --\` unless explicitly approved.
|
|
59
|
+
- Do not amend a commit unless asked.
|
|
60
|
+
- You may be in a dirty worktree. NEVER revert changes you did not make; if unrelated changes conflict with your task, stop and ask.`;
|
|
61
|
+
const TONE = `# Tone & Output
|
|
62
|
+
- Be concise and direct. Lead with the conclusion, then the evidence.
|
|
63
|
+
- No preamble or postamble ("The answer is…", "Here is what I'll do next…").
|
|
64
|
+
- Use GitHub-flavored markdown. Only use emojis if explicitly asked.
|
|
65
|
+
- Don't hide uncertainty: state it at the specific claim, name the tradeoff.
|
|
66
|
+
- For a simple question, a one-liner is best.`;
|
|
67
|
+
export { CODEBASE, DELIVERY_CONTRACT, ENGINEERING_PRINCIPLES, EXECUTION_WORKFLOW, GIT_SAFETY, ROLE_DESCRIPTION, TONE, TOOL_USAGE, VERIFICATION, };
|
|
68
|
+
// ── Registry ──────────────────────────────────────────────
|
|
69
|
+
/** Built-in section ids in priority order. */
|
|
70
|
+
export const BUILTIN_SECTION_IDS = [
|
|
71
|
+
'role',
|
|
72
|
+
'systemPrompt',
|
|
73
|
+
'engineering',
|
|
74
|
+
'tool-usage',
|
|
75
|
+
'codebase',
|
|
76
|
+
'constraints',
|
|
77
|
+
'execution-workflow',
|
|
78
|
+
'verification',
|
|
79
|
+
'delivery-contract',
|
|
80
|
+
'git-safety',
|
|
81
|
+
'tone',
|
|
82
|
+
'project',
|
|
83
|
+
'skills',
|
|
84
|
+
'agents',
|
|
85
|
+
'slash-commands',
|
|
86
|
+
];
|
|
87
|
+
/** Built-in sections. Dynamic bodies (tool list, project info, skills) use `render`. */
|
|
88
|
+
function builtinSections() {
|
|
89
|
+
return [
|
|
90
|
+
{ id: 'role', content: ROLE_DESCRIPTION, priority: 0 },
|
|
91
|
+
{
|
|
92
|
+
id: 'systemPrompt',
|
|
93
|
+
content: '',
|
|
94
|
+
priority: 5,
|
|
95
|
+
condition: (c) => Boolean(c.config.systemPrompt),
|
|
96
|
+
render: (c) => c.config.systemPrompt ?? '',
|
|
97
|
+
},
|
|
98
|
+
{ id: 'engineering', content: ENGINEERING_PRINCIPLES, priority: 10 },
|
|
99
|
+
{
|
|
100
|
+
id: 'tool-usage',
|
|
101
|
+
content: '',
|
|
102
|
+
priority: 20,
|
|
103
|
+
condition: (c) => c.tools.length > 0,
|
|
104
|
+
render: (c) => {
|
|
105
|
+
const lines = ['## Available Tools'];
|
|
106
|
+
for (const tool of c.tools)
|
|
107
|
+
lines.push(`- **${tool.name}**: ${tool.description}`);
|
|
108
|
+
lines.push(TOOL_USAGE);
|
|
109
|
+
return lines.join('\n');
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{ id: 'codebase', content: CODEBASE, priority: 30 },
|
|
113
|
+
{ id: 'constraints', content: PARADIGM_CONSTRAINTS, priority: 40 },
|
|
114
|
+
{ id: 'execution-workflow', content: EXECUTION_WORKFLOW, priority: 50 },
|
|
115
|
+
{ id: 'verification', content: VERIFICATION, priority: 60 },
|
|
116
|
+
{ id: 'delivery-contract', content: DELIVERY_CONTRACT, priority: 70 },
|
|
117
|
+
{ id: 'git-safety', content: GIT_SAFETY, priority: 80 },
|
|
118
|
+
{ id: 'tone', content: TONE, priority: 90 },
|
|
119
|
+
{
|
|
120
|
+
id: 'project',
|
|
121
|
+
content: '',
|
|
122
|
+
priority: 100,
|
|
123
|
+
render: (c) => {
|
|
124
|
+
const lines = ['## Project Context'];
|
|
125
|
+
lines.push(`- Name: ${c.projectInfo.name}`);
|
|
126
|
+
lines.push(`- Language: ${c.projectInfo.language}`);
|
|
127
|
+
if (c.projectInfo.framework)
|
|
128
|
+
lines.push(`- Framework: ${c.projectInfo.framework}`);
|
|
129
|
+
lines.push(`- Root: ${c.projectInfo.rootDir}`);
|
|
130
|
+
if (c.projectInfo.gitBranch)
|
|
131
|
+
lines.push(`- Git Branch: ${c.projectInfo.gitBranch}`);
|
|
132
|
+
return lines.join('\n');
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
id: 'skills',
|
|
137
|
+
content: '',
|
|
138
|
+
priority: 110,
|
|
139
|
+
condition: (c) => Boolean(c.skills && c.skills.length > 0),
|
|
140
|
+
render: (c) => {
|
|
141
|
+
const lines = ['## Loaded Skills'];
|
|
142
|
+
for (const skill of c.skills ?? [])
|
|
143
|
+
lines.push(`- ${skill}`);
|
|
144
|
+
return lines.join('\n');
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
// spec §17.2:有子 agent 时列出可委托 agent(插件/配置注入)。
|
|
148
|
+
{
|
|
149
|
+
id: 'agents',
|
|
150
|
+
content: '',
|
|
151
|
+
priority: 115,
|
|
152
|
+
condition: (c) => Boolean(c.agents && c.agents.length > 0),
|
|
153
|
+
render: (c) => {
|
|
154
|
+
const lines = ['## Available Agents'];
|
|
155
|
+
for (const agent of c.agents ?? [])
|
|
156
|
+
lines.push(`- ${agent}`);
|
|
157
|
+
return lines.join('\n');
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
// spec §17.2:始终列出可用 slash 命令,让模型知道可调用哪些命令。
|
|
161
|
+
{
|
|
162
|
+
id: 'slash-commands',
|
|
163
|
+
content: '',
|
|
164
|
+
priority: 120,
|
|
165
|
+
render: () => {
|
|
166
|
+
const lines = ['## Slash Commands'];
|
|
167
|
+
for (const cmd of builtinCommands)
|
|
168
|
+
lines.push(`- /${cmd.name}: ${cmd.description}`);
|
|
169
|
+
return lines.join('\n');
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
/** Create a registry pre-loaded with the built-in sections. */
|
|
175
|
+
function createPromptRegistry() {
|
|
176
|
+
const registry = { sections: new Map() };
|
|
177
|
+
for (const section of builtinSections()) {
|
|
178
|
+
registry.sections.set(section.id, section);
|
|
179
|
+
}
|
|
180
|
+
return registry;
|
|
181
|
+
}
|
|
182
|
+
/** Register a section. A later registration with the same id overrides the earlier
|
|
183
|
+
* one (lets plugins replace builtins). */
|
|
184
|
+
function registerPromptSection(registry, section) {
|
|
185
|
+
registry.sections.set(section.id, section);
|
|
186
|
+
}
|
|
187
|
+
/** Assemble the system prompt: filter by condition, sort by priority, render. */
|
|
188
|
+
function buildDynamicPrompt(registry, ctx) {
|
|
189
|
+
const sections = Array.from(registry.sections.values())
|
|
190
|
+
.filter((s) => !s.condition || s.condition(ctx))
|
|
191
|
+
.sort((a, b) => a.priority - b.priority);
|
|
192
|
+
const parts = [];
|
|
193
|
+
for (const section of sections) {
|
|
194
|
+
const body = section.render ? section.render(ctx) : section.content;
|
|
195
|
+
if (body.length > 0)
|
|
196
|
+
parts.push(body);
|
|
197
|
+
}
|
|
198
|
+
return parts.join('\n\n');
|
|
199
|
+
}
|
|
200
|
+
export { buildDynamicPrompt, createPromptRegistry, registerPromptSection };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PromptContext } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build the system prompt for an agent run.
|
|
4
|
+
*
|
|
5
|
+
* Legacy entry point kept for backward compatibility with `agentLoop` and
|
|
6
|
+
* existing call sites. Internally assembles via the dynamic prompt registry
|
|
7
|
+
* (spec §17): a default registry seeded with the built-in sections, so plugins
|
|
8
|
+
* that register custom sections on the same registry are reflected.
|
|
9
|
+
*/
|
|
10
|
+
declare function buildSystemPrompt(ctx: PromptContext): string;
|
|
11
|
+
export { buildSystemPrompt };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { buildDynamicPrompt, createPromptRegistry } from './prompt-registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build the system prompt for an agent run.
|
|
4
|
+
*
|
|
5
|
+
* Legacy entry point kept for backward compatibility with `agentLoop` and
|
|
6
|
+
* existing call sites. Internally assembles via the dynamic prompt registry
|
|
7
|
+
* (spec §17): a default registry seeded with the built-in sections, so plugins
|
|
8
|
+
* that register custom sections on the same registry are reflected.
|
|
9
|
+
*/
|
|
10
|
+
function buildSystemPrompt(ctx) {
|
|
11
|
+
return buildDynamicPrompt(createPromptRegistry(), ctx);
|
|
12
|
+
}
|
|
13
|
+
export { buildSystemPrompt };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** 判断存储值是否已加密(带 enc: 前缀)。 */
|
|
2
|
+
export declare function isEncryptedSecret(stored: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* 加密一个敏感值(如 provider apiKey),返回 `enc:` 前缀的 base64 串。
|
|
5
|
+
* 使用 AES-256-GCM + 随机 IV + 机器绑定密钥(spec §24.2「keyring 或加密文件」)。
|
|
6
|
+
* 机器绑定意味着密文不可跨机器解密——apiKey 本就该在各机器重新配置。
|
|
7
|
+
*/
|
|
8
|
+
export declare function encryptSecret(plaintext: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* 解密存储值。带 `enc:` 前缀则解密;否则原样返回(明文兼容,平滑迁移)。
|
|
11
|
+
*/
|
|
12
|
+
export declare function decryptSecret(stored: string): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';
|
|
2
|
+
import { hostname, userInfo } from 'node:os';
|
|
3
|
+
/** 加密值的存储前缀。无此前缀的值视为明文(向后兼容)。 */
|
|
4
|
+
const PREFIX = 'enc:';
|
|
5
|
+
/** 密钥派生盐(固定;机器绑定由 hostname+username 提供)。 */
|
|
6
|
+
const SALT = 'c0de-agent-apikey-v1';
|
|
7
|
+
/** 机器标识,用于派生加密密钥。userInfo 失败时回退到环境变量。 */
|
|
8
|
+
function machineId() {
|
|
9
|
+
try {
|
|
10
|
+
return `${hostname()}:${userInfo().username}`;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return `${hostname()}:${process.env.USER ?? 'anonymous'}`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** 派生 256 位 AES 密钥(机器绑定)。 */
|
|
17
|
+
function deriveKey() {
|
|
18
|
+
return scryptSync(machineId(), SALT, 32);
|
|
19
|
+
}
|
|
20
|
+
/** 判断存储值是否已加密(带 enc: 前缀)。 */
|
|
21
|
+
export function isEncryptedSecret(stored) {
|
|
22
|
+
return stored.startsWith(PREFIX);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 加密一个敏感值(如 provider apiKey),返回 `enc:` 前缀的 base64 串。
|
|
26
|
+
* 使用 AES-256-GCM + 随机 IV + 机器绑定密钥(spec §24.2「keyring 或加密文件」)。
|
|
27
|
+
* 机器绑定意味着密文不可跨机器解密——apiKey 本就该在各机器重新配置。
|
|
28
|
+
*/
|
|
29
|
+
export function encryptSecret(plaintext) {
|
|
30
|
+
const key = deriveKey();
|
|
31
|
+
const iv = randomBytes(12);
|
|
32
|
+
const cipher = createCipheriv('aes-256-gcm', key, iv);
|
|
33
|
+
const enc = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
|
34
|
+
const tag = cipher.getAuthTag();
|
|
35
|
+
return PREFIX + Buffer.concat([iv, tag, enc]).toString('base64');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 解密存储值。带 `enc:` 前缀则解密;否则原样返回(明文兼容,平滑迁移)。
|
|
39
|
+
*/
|
|
40
|
+
export function decryptSecret(stored) {
|
|
41
|
+
if (!stored.startsWith(PREFIX))
|
|
42
|
+
return stored;
|
|
43
|
+
const buf = Buffer.from(stored.slice(PREFIX.length), 'base64');
|
|
44
|
+
const iv = buf.subarray(0, 12);
|
|
45
|
+
const tag = buf.subarray(12, 28);
|
|
46
|
+
const enc = buf.subarray(28);
|
|
47
|
+
const key = deriveKey();
|
|
48
|
+
const decipher = createDecipheriv('aes-256-gcm', key, iv);
|
|
49
|
+
decipher.setAuthTag(tag);
|
|
50
|
+
return Buffer.concat([decipher.update(enc), decipher.final()]).toString('utf8');
|
|
51
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SlashCommand } from './types.js';
|
|
2
|
+
declare function parseSlashInput(input: string): {
|
|
3
|
+
name: string;
|
|
4
|
+
args: string;
|
|
5
|
+
} | null;
|
|
6
|
+
type SlashRegistry = {
|
|
7
|
+
has: (name: string) => boolean;
|
|
8
|
+
get: (name: string) => SlashCommand | undefined;
|
|
9
|
+
list: () => SlashCommand[];
|
|
10
|
+
register: (cmd: SlashCommand) => void;
|
|
11
|
+
};
|
|
12
|
+
declare function createSlashRegistry(): SlashRegistry;
|
|
13
|
+
declare const builtinCommands: SlashCommand[];
|
|
14
|
+
export type { SlashRegistry };
|
|
15
|
+
export { builtinCommands, createSlashRegistry, parseSlashInput };
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
function parseSlashInput(input) {
|
|
2
|
+
const trimmed = input.trim();
|
|
3
|
+
if (!trimmed.startsWith('/'))
|
|
4
|
+
return null;
|
|
5
|
+
const spaceIdx = trimmed.indexOf(' ');
|
|
6
|
+
if (spaceIdx === -1) {
|
|
7
|
+
return { name: trimmed.slice(1), args: '' };
|
|
8
|
+
}
|
|
9
|
+
return { name: trimmed.slice(1, spaceIdx), args: trimmed.slice(spaceIdx + 1).trim() };
|
|
10
|
+
}
|
|
11
|
+
function createSlashRegistry() {
|
|
12
|
+
const commands = new Map();
|
|
13
|
+
for (const cmd of builtinCommands) {
|
|
14
|
+
commands.set(cmd.name, cmd);
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
has: (name) => commands.has(name),
|
|
18
|
+
get: (name) => commands.get(name),
|
|
19
|
+
list: () => Array.from(commands.values()),
|
|
20
|
+
register: (cmd) => commands.set(cmd.name, cmd),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const helpCommand = {
|
|
24
|
+
name: 'help',
|
|
25
|
+
description: 'List available slash commands',
|
|
26
|
+
execute: async () => {
|
|
27
|
+
const lines = [
|
|
28
|
+
'Available commands:',
|
|
29
|
+
' /compact Manually trigger context compaction',
|
|
30
|
+
' /model <name> Switch the current session model',
|
|
31
|
+
' /clear Clear session messages',
|
|
32
|
+
' /help Show this help',
|
|
33
|
+
' /fork [index] Fork session from a message',
|
|
34
|
+
' /config [k][v] View or set configuration',
|
|
35
|
+
];
|
|
36
|
+
return { _tag: 'text', text: lines.join('\n') };
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
const compactCommand = {
|
|
40
|
+
name: 'compact',
|
|
41
|
+
description: 'Manually trigger context compaction',
|
|
42
|
+
execute: async () => {
|
|
43
|
+
return {
|
|
44
|
+
_tag: 'success',
|
|
45
|
+
message: 'Compaction queued. Use the agent API to trigger with a summarizer.',
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
const modelCommand = {
|
|
50
|
+
name: 'model',
|
|
51
|
+
description: 'Switch the current session model',
|
|
52
|
+
argsHint: '<model-name>',
|
|
53
|
+
execute: async (args) => {
|
|
54
|
+
if (!args)
|
|
55
|
+
return { _tag: 'error', message: 'Usage: /model <model-name>' };
|
|
56
|
+
return { _tag: 'success', message: `Model set to ${args} (takes effect next turn)` };
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
const clearCommand = {
|
|
60
|
+
name: 'clear',
|
|
61
|
+
description: 'Clear session messages',
|
|
62
|
+
execute: async (args, ctx) => {
|
|
63
|
+
if (!args)
|
|
64
|
+
return { _tag: 'error', message: 'Usage: /clear <session-id>' };
|
|
65
|
+
const { deleteEntriesByIds, getEntries } = await import('../session/message.js');
|
|
66
|
+
const entries = await getEntries(ctx.deps.db, args);
|
|
67
|
+
const ids = entries.map((e) => ('id' in e ? e.id : '')).filter(Boolean);
|
|
68
|
+
if (ids.length > 0) {
|
|
69
|
+
await deleteEntriesByIds(ctx.deps.db, ids);
|
|
70
|
+
}
|
|
71
|
+
return { _tag: 'success', message: `Cleared ${ids.length} entries` };
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
const forkCommand = {
|
|
75
|
+
name: 'fork',
|
|
76
|
+
description: 'Fork session from a message index',
|
|
77
|
+
argsHint: '<session-id> [message-index]',
|
|
78
|
+
execute: async (args, ctx) => {
|
|
79
|
+
if (!args)
|
|
80
|
+
return { _tag: 'error', message: 'Usage: /fork <session-id> [message-index]' };
|
|
81
|
+
const parts = args.split(/\s+/);
|
|
82
|
+
const sessionId = parts[0] ?? '';
|
|
83
|
+
const messageIndex = parts[1] !== undefined ? Number.parseInt(parts[1], 10) : 0;
|
|
84
|
+
const { forkSession } = await import('../session/branch.js');
|
|
85
|
+
const forked = await forkSession(ctx.deps.db, sessionId, messageIndex);
|
|
86
|
+
return { _tag: 'success', message: `Forked to new session: ${forked.id}` };
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
const configCommand = {
|
|
90
|
+
name: 'config',
|
|
91
|
+
description: 'View or set configuration',
|
|
92
|
+
argsHint: '[key] [value]',
|
|
93
|
+
execute: async (args, ctx) => {
|
|
94
|
+
if (!args) {
|
|
95
|
+
return { _tag: 'text', text: JSON.stringify(ctx.config, null, 2) };
|
|
96
|
+
}
|
|
97
|
+
const parts = args.split(/\s+/);
|
|
98
|
+
const key = parts[0] ?? '';
|
|
99
|
+
if (parts.length === 1) {
|
|
100
|
+
const value = ctx.config[key];
|
|
101
|
+
return { _tag: 'text', text: `${key}: ${JSON.stringify(value)}` };
|
|
102
|
+
}
|
|
103
|
+
return { _tag: 'success', message: 'Config updates are handled via the config API' };
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
const builtinCommands = [
|
|
107
|
+
helpCommand,
|
|
108
|
+
compactCommand,
|
|
109
|
+
modelCommand,
|
|
110
|
+
clearCommand,
|
|
111
|
+
forkCommand,
|
|
112
|
+
configCommand,
|
|
113
|
+
];
|
|
114
|
+
export { builtinCommands, createSlashRegistry, parseSlashInput };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AgentState } from '../shared/types/agent.js';
|
|
2
|
+
/** Add a steering message to the queue. Takes effect before the next LLM turn. */
|
|
3
|
+
declare function injectSteering(state: AgentState, message: string): void;
|
|
4
|
+
/** Remove and return all queued steering messages (FIFO order). Clears the queue. */
|
|
5
|
+
declare function drainSteering(state: AgentState): string[];
|
|
6
|
+
/** Discard all queued steering messages without returning them. */
|
|
7
|
+
declare function clearSteering(state: AgentState): void;
|
|
8
|
+
export { clearSteering, drainSteering, injectSteering };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Add a steering message to the queue. Takes effect before the next LLM turn. */
|
|
2
|
+
function injectSteering(state, message) {
|
|
3
|
+
state.steeringQueue.push(message);
|
|
4
|
+
}
|
|
5
|
+
/** Remove and return all queued steering messages (FIFO order). Clears the queue. */
|
|
6
|
+
function drainSteering(state) {
|
|
7
|
+
const messages = [...state.steeringQueue];
|
|
8
|
+
state.steeringQueue.length = 0;
|
|
9
|
+
return messages;
|
|
10
|
+
}
|
|
11
|
+
/** Discard all queued steering messages without returning them. */
|
|
12
|
+
function clearSteering(state) {
|
|
13
|
+
state.steeringQueue.length = 0;
|
|
14
|
+
}
|
|
15
|
+
export { clearSteering, drainSteering, injectSteering };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话标题自动生成。
|
|
3
|
+
*
|
|
4
|
+
* 设计参考 opencode 的 `ensureTitle`:在第一条用户消息后,用一个 smol/快速
|
|
5
|
+
* 模型为会话生成简短标题。fire-and-forget,失败仅吞掉,绝不阻塞主对话流。
|
|
6
|
+
*
|
|
7
|
+
* 模型解析遵循 chat 路由的同款策略(见 server/routes/chat.ts):优先
|
|
8
|
+
* `config.roleRouting.smol`,未配置则回退 `defaultProvider/defaultModel`。
|
|
9
|
+
* 不走 registry.resolveModelByRole——因为运行时 buildRegistryFromConfig 只
|
|
10
|
+
* 注册 provider 路由、并未把 roleRouting 绑定到 registry.roles,角色解析会
|
|
11
|
+
* 直接抛 NoRoute。
|
|
12
|
+
*/
|
|
13
|
+
import type { DB } from '../db/client.js';
|
|
14
|
+
import { chat as defaultChat } from '../llm/provider.js';
|
|
15
|
+
import type { Registry } from '../llm/registry.js';
|
|
16
|
+
import type { Config } from './config.js';
|
|
17
|
+
/** 会话默认占位标题;仍是该值时才有资格自动生成。 */
|
|
18
|
+
declare const DEFAULT_SESSION_TITLE = "New Session";
|
|
19
|
+
/** 注入点:可覆盖的非流式 chat 函数(测试用)。 */
|
|
20
|
+
type ChatFn = typeof defaultChat;
|
|
21
|
+
/** 标题生成所需的最小依赖(AgentDependencies 的结构子集)。 */
|
|
22
|
+
type TitleDeps = {
|
|
23
|
+
db: DB;
|
|
24
|
+
llmRegistry: Registry;
|
|
25
|
+
config: Config;
|
|
26
|
+
/** 可选注入 chat 实现;默认走真实 llm/provider。 */
|
|
27
|
+
chatFn?: ChatFn;
|
|
28
|
+
};
|
|
29
|
+
/** 会话标题是否仍是默认占位值。 */
|
|
30
|
+
declare function isDefaultTitle(title: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* 为会话生成标题并持久化。fire-and-forget 语义:任何错误都被吞掉,
|
|
33
|
+
* 绝不向调用方抛出,绝不覆盖一个已存在的非默认标题。
|
|
34
|
+
*
|
|
35
|
+
* @param deps 最小依赖(含可注入的 chatFn)
|
|
36
|
+
* @param sessionId 目标会话 id
|
|
37
|
+
* @param firstMessage 第一条用户消息原文
|
|
38
|
+
* @param chatProvider 会话实际使用的 LLM provider(AgentConfig.provider)
|
|
39
|
+
* @param chatModel 会话实际使用的 LLM model(AgentConfig.model)
|
|
40
|
+
*/
|
|
41
|
+
declare function generateSessionTitle(deps: TitleDeps, sessionId: string, firstMessage: string, chatProvider: string, chatModel: string): Promise<void>;
|
|
42
|
+
export { DEFAULT_SESSION_TITLE, generateSessionTitle, isDefaultTitle };
|