c0de-agent 0.0.0 → 1.1.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 +30 -0
- package/dist/cli/deps.js +70 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +155 -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 +100 -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 +85 -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 +35 -0
- package/dist/core/loop.js +915 -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 +113 -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 +116 -0
- package/dist/core/tool-exec.d.ts +20 -0
- package/dist/core/tool-exec.js +64 -0
- package/dist/core/types.d.ts +97 -0
- package/dist/core/types.js +1 -0
- package/dist/core/worktree.d.ts +24 -0
- package/dist/core/worktree.js +70 -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 +195 -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 +69 -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 +34 -0
- package/dist/server/agent-manager.js +62 -0
- package/dist/server/app.d.ts +5 -0
- package/dist/server/app.js +78 -0
- package/dist/server/context.d.ts +20 -0
- package/dist/server/context.js +49 -0
- package/dist/server/dev.d.ts +14 -0
- package/dist/server/dev.js +148 -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 +32 -0
- package/dist/server/permission/store.js +56 -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 +318 -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 +204 -0
- package/dist/server/routes/terminal.d.ts +5 -0
- package/dist/server/routes/terminal.js +66 -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 +56 -0
- package/dist/server/server.js +269 -0
- package/dist/server/terminal/pty-manager.d.ts +53 -0
- package/dist/server/terminal/pty-manager.js +160 -0
- package/dist/server/types.d.ts +76 -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 +33 -0
- package/dist/session/compaction.js +261 -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/shake.d.ts +66 -0
- package/dist/session/shake.js +304 -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 +229 -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 +114 -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 +123 -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 +137 -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 +108 -8
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
// src/server/server.ts
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { connect as tcpConnect } from 'node:net';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { serve } from '@hono/node-server';
|
|
6
|
+
import { WebSocketServer } from 'ws';
|
|
7
|
+
import { BUILTIN_AGENTS, createAgentRegistry } from '../core/agents/index.js';
|
|
8
|
+
import { loadConfig } from '../core/config.js';
|
|
9
|
+
import { decryptSecret } from '../core/secret.js';
|
|
10
|
+
import { createDB, migrateDB } from '../db/index.js';
|
|
11
|
+
import { createRegistry, registerProvider } from '../llm/registry.js';
|
|
12
|
+
import { initPlugins } from '../plugins/index.js';
|
|
13
|
+
import { createDefaultRegistry, createDefaultURLRegistry } from '../tools/index.js';
|
|
14
|
+
import { checkForUpdate, createHandoffServer, createUpdateScheduler, requestHandoff, restoreSessions, } from '../update/index.js';
|
|
15
|
+
import { createAgentManager } from './agent-manager.js';
|
|
16
|
+
import { PTYManager } from './terminal/pty-manager.js';
|
|
17
|
+
import { createApp } from './app.js';
|
|
18
|
+
import { createPermissionStore } from './permission/store.js';
|
|
19
|
+
/** 把 config.providers 注册到新建的 LLM registry(修复此前空 registry 的遗漏)。 */
|
|
20
|
+
function buildRegistryFromConfig(config) {
|
|
21
|
+
const registry = createRegistry();
|
|
22
|
+
for (const p of config.providers) {
|
|
23
|
+
registerProviderFromConfig(registry, p);
|
|
24
|
+
}
|
|
25
|
+
return registry;
|
|
26
|
+
}
|
|
27
|
+
function registerProviderFromConfig(registry, p) {
|
|
28
|
+
// 兼容 config.json 中以 _tag 标识 provider 的格式(name 缺失时回退到 _tag)
|
|
29
|
+
const name = p.name || p._tag;
|
|
30
|
+
if (!name || !p.baseURL)
|
|
31
|
+
return;
|
|
32
|
+
// baseURL 已含 /v1 时用 /chat/completions,避免 /v1/v1 双重前缀
|
|
33
|
+
const path = p.baseURL.replace(/\/+$/, '').endsWith('/v1') ? '/chat/completions' : undefined;
|
|
34
|
+
registerProvider(registry, {
|
|
35
|
+
name,
|
|
36
|
+
baseURL: p.baseURL,
|
|
37
|
+
apiKey: p.apiKey ? decryptSecret(p.apiKey) : p.apiKey,
|
|
38
|
+
...(path ? { path } : {}),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* config 变更后原地同步 registry(清空旧路由 + 重新注册),
|
|
43
|
+
* 使运行中的 ServerContext 立即生效,无需重启。
|
|
44
|
+
*/
|
|
45
|
+
function syncRegistryFromConfig(registry, config) {
|
|
46
|
+
registry.routes.clear();
|
|
47
|
+
registry.roles.clear();
|
|
48
|
+
for (const p of config.providers) {
|
|
49
|
+
registerProviderFromConfig(registry, p);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** 解析 PGLite 持久化数据目录:优先 C0DE_DB_DIR,否则 <cwd>/.c0de/pglite。 */
|
|
53
|
+
function resolveDbDir(cwd) {
|
|
54
|
+
const envDir = process.env.C0DE_DB_DIR;
|
|
55
|
+
if (envDir && envDir.trim() !== '')
|
|
56
|
+
return envDir;
|
|
57
|
+
return join(cwd, '.c0de', 'pglite');
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 围绕已有 DB handle 组装 ServerContext(不建/不关闭 DB)。
|
|
61
|
+
*
|
|
62
|
+
* dev 热重载重建复用此函数:PGLite 单写者约束下 DB handle 必须跨重载存活,
|
|
63
|
+
* 但 ctx 其余资源(agentManager/permissionStore/registries/plugins)全部重建为新实例。
|
|
64
|
+
* 返回的 dispose 清理 ctx 资源但**不 close db**——db 由调用方持有。
|
|
65
|
+
*/
|
|
66
|
+
async function buildServerContext(db, opts = {}) {
|
|
67
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
68
|
+
if (opts.restoreFrom) {
|
|
69
|
+
const snapshot = JSON.parse(readFileSync(opts.restoreFrom, 'utf8'));
|
|
70
|
+
await restoreSessions(db, snapshot);
|
|
71
|
+
}
|
|
72
|
+
const config = await loadConfig(cwd);
|
|
73
|
+
const toolRegistry = createDefaultRegistry(config);
|
|
74
|
+
const llmRegistry = buildRegistryFromConfig(config);
|
|
75
|
+
const urlRegistry = createDefaultURLRegistry();
|
|
76
|
+
const { pluginRegistry, hookRunner } = await initPlugins({
|
|
77
|
+
cwd,
|
|
78
|
+
config,
|
|
79
|
+
toolRegistry,
|
|
80
|
+
llmRegistry,
|
|
81
|
+
});
|
|
82
|
+
const ctx = {
|
|
83
|
+
db,
|
|
84
|
+
config,
|
|
85
|
+
toolRegistry,
|
|
86
|
+
llmRegistry,
|
|
87
|
+
urlRegistry,
|
|
88
|
+
hookRunner,
|
|
89
|
+
pluginRegistry,
|
|
90
|
+
agentManager: createAgentManager(),
|
|
91
|
+
permissionStore: createPermissionStore(),
|
|
92
|
+
permissionMode: config.permission.defaultMode,
|
|
93
|
+
// Agent 注册表:内置 4 个默认 agent;项目/用户自定义 agent 可在启动后补充加载。
|
|
94
|
+
agentRegistry: (() => {
|
|
95
|
+
const reg = createAgentRegistry();
|
|
96
|
+
for (const def of BUILTIN_AGENTS)
|
|
97
|
+
reg.register(def);
|
|
98
|
+
return reg;
|
|
99
|
+
})(),
|
|
100
|
+
// spec §18.1 后台版本检查调度器;config.update.enabled 控制是否启动。
|
|
101
|
+
updateScheduler: createUpdateScheduler({
|
|
102
|
+
checkFn: opts.checkForUpdateFn ?? checkForUpdate,
|
|
103
|
+
intervalMs: config.update.intervalMs,
|
|
104
|
+
initialDelayMs: config.update.initialDelayMs,
|
|
105
|
+
}),
|
|
106
|
+
cwd,
|
|
107
|
+
ptyManager: new PTYManager(),
|
|
108
|
+
};
|
|
109
|
+
// spec §18.3 handoff server:旧实例监听随机端口,收到 POST /handoff 时
|
|
110
|
+
// 序列化当前会话状态 + 优雅关闭,让新实例接管。config.update.enabled=false
|
|
111
|
+
// 或测试 skipHandoff 时跳过(减少后台资源占用)。
|
|
112
|
+
let handoffServer;
|
|
113
|
+
if (config.update.enabled && !opts.skipHandoff) {
|
|
114
|
+
const createHandoff = opts.createHandoffFn ?? createHandoffServer;
|
|
115
|
+
handoffServer = await createHandoff(async () => {
|
|
116
|
+
// 序列化在热更新主链路(performHotUpdate)已完成;handoff 回调只需释放资源。
|
|
117
|
+
// 这里提前停 scheduler 以避免 close 期间还在跑检查。
|
|
118
|
+
ctx.updateScheduler.stop();
|
|
119
|
+
});
|
|
120
|
+
ctx.handoff = { port: handoffServer.port, server: handoffServer };
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
ctx,
|
|
124
|
+
dispose: async () => {
|
|
125
|
+
// dev 重建前调用:中止活跃 run + settle pending permission +
|
|
126
|
+
// 停 scheduler + 关 handoff。**不 close db**(调用方持有)。
|
|
127
|
+
ctx.agentManager.dispose();
|
|
128
|
+
ctx.permissionStore.dispose();
|
|
129
|
+
ctx.updateScheduler.stop();
|
|
130
|
+
ctx.ptyManager.dispose();
|
|
131
|
+
if (handoffServer)
|
|
132
|
+
await handoffServer.close();
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/** dev 专用:创建 + migrate PGLite,跨热重载复用(单写者,只建一次)。 */
|
|
137
|
+
async function createDevDb(cwd) {
|
|
138
|
+
const dataDir = resolveDbDir(cwd);
|
|
139
|
+
if (!existsSync(dataDir))
|
|
140
|
+
mkdirSync(dataDir, { recursive: true });
|
|
141
|
+
const db = await createDB({ driver: 'pglite', dataDir });
|
|
142
|
+
// migrateDB 失败必须 close,否则 WASM 实例泄漏锁住 dataDir。
|
|
143
|
+
try {
|
|
144
|
+
await migrateDB(db);
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
await db.close().catch(() => { });
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
return db;
|
|
151
|
+
}
|
|
152
|
+
/** 初始化 DB + 配置 + 注册表,返回 ServerContext + 清理函数(dev 与独立后端共用)。 */
|
|
153
|
+
async function bootstrapServerContext(opts = {}) {
|
|
154
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
155
|
+
const ownsDb = !opts.db;
|
|
156
|
+
// 持久化 PGLite 数据:默认 <cwd>/.c0de/pglite(与 .c0de/cache、.c0de/config.json 同约定),
|
|
157
|
+
// 可用 C0DE_DB_DIR 覆盖。此前默认 in-memory,进程重启即丢全部会话/消息/调用详情。
|
|
158
|
+
// 测试注入 opts.db 时跳过(保持 in-memory 隔离)。
|
|
159
|
+
let db;
|
|
160
|
+
if (opts.db) {
|
|
161
|
+
db = opts.db;
|
|
162
|
+
// 注入 db 仍需 migrate(测试可能注入全新 in-memory db)。
|
|
163
|
+
// migrateDB 失败时若 ownsDb 才 close(注入的由调用方管)。
|
|
164
|
+
try {
|
|
165
|
+
await migrateDB(db);
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
if (ownsDb)
|
|
169
|
+
await db.close().catch(() => { });
|
|
170
|
+
throw err;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
db = await createDevDb(cwd);
|
|
175
|
+
}
|
|
176
|
+
const { ctx, dispose } = await buildServerContext(db, opts);
|
|
177
|
+
return {
|
|
178
|
+
ctx,
|
|
179
|
+
close: async () => {
|
|
180
|
+
await dispose();
|
|
181
|
+
if (ownsDb)
|
|
182
|
+
await db.close();
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 请求旧实例 handoff 后轮询端口释放(spec §18.3)。旧实例收到 /handoff 后
|
|
188
|
+
* 优雅退出释放端口;新实例轮询 TCP 连接直到被拒(端口已释放)或超时报错。
|
|
189
|
+
*/
|
|
190
|
+
async function requestHandoffWithRetry(port, maxAttempts = 30, delayMs = 100) {
|
|
191
|
+
try {
|
|
192
|
+
await requestHandoff(port);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// 旧实例未启 handoff 端点(update 未启用);直接尝试绑端口。
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
199
|
+
const released = await new Promise((resolve) => {
|
|
200
|
+
const sock = tcpConnect({ host: '127.0.0.1', port });
|
|
201
|
+
sock.once('connect', () => {
|
|
202
|
+
sock.destroy();
|
|
203
|
+
resolve(false); // 连上 → 旧实例还在
|
|
204
|
+
});
|
|
205
|
+
sock.once('error', () => resolve(true)); // 连接拒绝 → 端口已释放
|
|
206
|
+
});
|
|
207
|
+
if (released)
|
|
208
|
+
return;
|
|
209
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
210
|
+
}
|
|
211
|
+
throw new Error(`handoff: old instance on port ${port} did not release after ${maxAttempts} attempts`);
|
|
212
|
+
}
|
|
213
|
+
async function startServer(opts = {}) {
|
|
214
|
+
const port = opts.port ?? 3000;
|
|
215
|
+
// spec §18.3:新实例从 --handoff-port 拿到旧实例端口,请求优雅退出后绑端口。
|
|
216
|
+
if (opts.handoffPort && !opts.skipHandoff) {
|
|
217
|
+
await requestHandoffWithRetry(opts.handoffPort);
|
|
218
|
+
}
|
|
219
|
+
const { ctx, close: closeCtx } = await bootstrapServerContext(opts);
|
|
220
|
+
const app = createApp(ctx);
|
|
221
|
+
// config.update.enabled 时启动后台调度器。
|
|
222
|
+
if (ctx.config.update.enabled)
|
|
223
|
+
ctx.updateScheduler.start();
|
|
224
|
+
const server = serve({ fetch: app.fetch, port });
|
|
225
|
+
// WebSocket:终端双向流。Hono v2 无原生 WS,用 ws 包直接挂载到 HTTP server。
|
|
226
|
+
// 匹配 /api/terminal/:id/ws → ptyManager.attachWebSocket
|
|
227
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
228
|
+
const expectedToken = ctx.config.security.authEnabled ? ctx.config.security.token : undefined;
|
|
229
|
+
server.on('upgrade', (req, socket, head) => {
|
|
230
|
+
const url = new URL(req.url ?? '', `http://${req.headers.host ?? 'localhost'}`);
|
|
231
|
+
const match = url.pathname.match(/^\/api\/terminal\/([^/]+)\/ws$/);
|
|
232
|
+
if (!match) {
|
|
233
|
+
socket.destroy();
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
// 认证:token 通过 query 参数传递(浏览器 WS 无法设置 Authorization header)
|
|
237
|
+
if (expectedToken) {
|
|
238
|
+
const token = url.searchParams.get('token');
|
|
239
|
+
if (token !== expectedToken) {
|
|
240
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
|
241
|
+
socket.destroy();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const ptyId = match[1];
|
|
246
|
+
if (!ptyId) {
|
|
247
|
+
socket.destroy();
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
251
|
+
const attached = ctx.ptyManager.attachWebSocket(ptyId, ws);
|
|
252
|
+
if (!attached) {
|
|
253
|
+
ws.send(JSON.stringify({ type: 'error', message: 'Terminal not found' }));
|
|
254
|
+
ws.close(1008, 'Terminal not found');
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
let closed = false;
|
|
259
|
+
const close = async () => {
|
|
260
|
+
if (closed)
|
|
261
|
+
return;
|
|
262
|
+
closed = true;
|
|
263
|
+
wss.close();
|
|
264
|
+
server.close();
|
|
265
|
+
await closeCtx();
|
|
266
|
+
};
|
|
267
|
+
return { app, port, close };
|
|
268
|
+
}
|
|
269
|
+
export { bootstrapServerContext, buildRegistryFromConfig, buildServerContext, createDevDb, startServer, syncRegistryFromConfig, };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { WebSocket } from 'ws';
|
|
2
|
+
/** PTY 会话信息(返回给前端)。 */
|
|
3
|
+
export interface PTYInfo {
|
|
4
|
+
id: string;
|
|
5
|
+
pid: number;
|
|
6
|
+
title: string;
|
|
7
|
+
cols: number;
|
|
8
|
+
rows: number;
|
|
9
|
+
cwd: string;
|
|
10
|
+
/** shell 程序路径。 */
|
|
11
|
+
shell: string;
|
|
12
|
+
}
|
|
13
|
+
export interface CreatePTYOptions {
|
|
14
|
+
cwd: string;
|
|
15
|
+
cols?: number;
|
|
16
|
+
rows?: number;
|
|
17
|
+
title?: string;
|
|
18
|
+
/** 覆盖默认 shell;不传则自动检测。 */
|
|
19
|
+
shell?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* PTY 生命周期管理器。
|
|
23
|
+
*
|
|
24
|
+
* 负责 spawn / write / resize / kill 伪终端进程,并将 PTY 输出
|
|
25
|
+
* 通过 WebSocket 推送到前端。一个 PTY 可同时挂多个 WebSocket
|
|
26
|
+
* (多标签共享同一终端),任一 WS 断开不影响进程。
|
|
27
|
+
*/
|
|
28
|
+
export declare class PTYManager {
|
|
29
|
+
private entries;
|
|
30
|
+
/** 创建新 PTY 会话。 */
|
|
31
|
+
create(opts: CreatePTYOptions): PTYInfo;
|
|
32
|
+
/** 向 PTY stdin 写入数据。 */
|
|
33
|
+
write(id: string, data: string): void;
|
|
34
|
+
/** 调整 PTY 尺寸。 */
|
|
35
|
+
resize(id: string, cols: number, rows: number): void;
|
|
36
|
+
/** 更新 PTY 标题。 */
|
|
37
|
+
setTitle(id: string, title: string): void;
|
|
38
|
+
/** 终止 PTY 进程。 */
|
|
39
|
+
kill(id: string): void;
|
|
40
|
+
/** 获取 PTY 信息。 */
|
|
41
|
+
get(id: string): PTYInfo | undefined;
|
|
42
|
+
/** 列出所有活跃 PTY。 */
|
|
43
|
+
list(): PTYInfo[];
|
|
44
|
+
/**
|
|
45
|
+
* 将 WebSocket 挂载到 PTY,建立双向数据流:
|
|
46
|
+
* - PTY onData → WS send(终端输出)
|
|
47
|
+
* - WS onMessage → PTY write(用户输入)
|
|
48
|
+
* - WS onClose → 摘除连接(PTY 保持存活)
|
|
49
|
+
*/
|
|
50
|
+
attachWebSocket(id: string, ws: WebSocket): boolean;
|
|
51
|
+
/** 终止所有 PTY 进程(服务器关闭时调用)。 */
|
|
52
|
+
dispose(): void;
|
|
53
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// src/server/terminal/pty-manager.ts
|
|
2
|
+
import { spawn } from 'node-pty';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
/** 检测当前平台默认 shell。 */
|
|
5
|
+
function detectShell() {
|
|
6
|
+
if (process.platform === 'win32') {
|
|
7
|
+
return process.env.COMSPEC ?? 'cmd.exe';
|
|
8
|
+
}
|
|
9
|
+
return process.env.SHELL ?? '/bin/bash';
|
|
10
|
+
}
|
|
11
|
+
const DEFAULT_COLS = 80;
|
|
12
|
+
const DEFAULT_ROWS = 24;
|
|
13
|
+
const MAX_TITLE_LEN = 100;
|
|
14
|
+
function truncateTitle(title) {
|
|
15
|
+
const clean = title.replace(/[\r\n]/g, ' ').trim();
|
|
16
|
+
return clean.length > MAX_TITLE_LEN ? `${clean.slice(0, MAX_TITLE_LEN)}…` : clean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* PTY 生命周期管理器。
|
|
20
|
+
*
|
|
21
|
+
* 负责 spawn / write / resize / kill 伪终端进程,并将 PTY 输出
|
|
22
|
+
* 通过 WebSocket 推送到前端。一个 PTY 可同时挂多个 WebSocket
|
|
23
|
+
* (多标签共享同一终端),任一 WS 断开不影响进程。
|
|
24
|
+
*/
|
|
25
|
+
export class PTYManager {
|
|
26
|
+
entries = new Map();
|
|
27
|
+
/** 创建新 PTY 会话。 */
|
|
28
|
+
create(opts) {
|
|
29
|
+
const id = `pty_${randomUUID()}`;
|
|
30
|
+
const cols = opts.cols ?? DEFAULT_COLS;
|
|
31
|
+
const rows = opts.rows ?? DEFAULT_ROWS;
|
|
32
|
+
const shell = opts.shell ?? detectShell();
|
|
33
|
+
const pty = spawn(shell, [], {
|
|
34
|
+
name: 'xterm-256color',
|
|
35
|
+
cols,
|
|
36
|
+
rows,
|
|
37
|
+
cwd: opts.cwd,
|
|
38
|
+
env: {
|
|
39
|
+
...process.env,
|
|
40
|
+
TERM: 'xterm-256color',
|
|
41
|
+
COLORTERM: 'truecolor',
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
const info = {
|
|
45
|
+
id,
|
|
46
|
+
pid: pty.pid,
|
|
47
|
+
title: truncateTitle(opts.title ?? shell),
|
|
48
|
+
cols,
|
|
49
|
+
rows,
|
|
50
|
+
cwd: opts.cwd,
|
|
51
|
+
shell,
|
|
52
|
+
};
|
|
53
|
+
const entry = { pty, info, sockets: new Set() };
|
|
54
|
+
// PTY 输出 → 广播到所有挂载的 WebSocket
|
|
55
|
+
pty.onData((data) => {
|
|
56
|
+
for (const ws of entry.sockets) {
|
|
57
|
+
if (ws.readyState === ws.OPEN) {
|
|
58
|
+
ws.send(data);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
// PTY 退出 → 通知所有 WS 并清理
|
|
63
|
+
pty.onExit(({ exitCode }) => {
|
|
64
|
+
for (const ws of entry.sockets) {
|
|
65
|
+
if (ws.readyState === ws.OPEN) {
|
|
66
|
+
ws.send(JSON.stringify({ type: 'exit', exitCode }));
|
|
67
|
+
ws.close(1000, 'pty exited');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
this.entries.delete(id);
|
|
71
|
+
});
|
|
72
|
+
this.entries.set(id, entry);
|
|
73
|
+
return info;
|
|
74
|
+
}
|
|
75
|
+
/** 向 PTY stdin 写入数据。 */
|
|
76
|
+
write(id, data) {
|
|
77
|
+
const entry = this.entries.get(id);
|
|
78
|
+
if (!entry)
|
|
79
|
+
throw new Error(`PTY not found: ${id}`);
|
|
80
|
+
entry.pty.write(data);
|
|
81
|
+
}
|
|
82
|
+
/** 调整 PTY 尺寸。 */
|
|
83
|
+
resize(id, cols, rows) {
|
|
84
|
+
const entry = this.entries.get(id);
|
|
85
|
+
if (!entry)
|
|
86
|
+
throw new Error(`PTY not found: ${id}`);
|
|
87
|
+
entry.pty.resize(Math.max(1, cols), Math.max(1, rows));
|
|
88
|
+
entry.info.cols = cols;
|
|
89
|
+
entry.info.rows = rows;
|
|
90
|
+
}
|
|
91
|
+
/** 更新 PTY 标题。 */
|
|
92
|
+
setTitle(id, title) {
|
|
93
|
+
const entry = this.entries.get(id);
|
|
94
|
+
if (!entry)
|
|
95
|
+
throw new Error(`PTY not found: ${id}`);
|
|
96
|
+
entry.info.title = truncateTitle(title);
|
|
97
|
+
}
|
|
98
|
+
/** 终止 PTY 进程。 */
|
|
99
|
+
kill(id) {
|
|
100
|
+
const entry = this.entries.get(id);
|
|
101
|
+
if (!entry)
|
|
102
|
+
return;
|
|
103
|
+
// 通知所有 WS
|
|
104
|
+
for (const ws of entry.sockets) {
|
|
105
|
+
if (ws.readyState === ws.OPEN) {
|
|
106
|
+
ws.send(JSON.stringify({ type: 'exit', exitCode: 0 }));
|
|
107
|
+
ws.close(1000, 'pty killed');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
entry.pty.kill();
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// 进程可能已退出
|
|
115
|
+
}
|
|
116
|
+
this.entries.delete(id);
|
|
117
|
+
}
|
|
118
|
+
/** 获取 PTY 信息。 */
|
|
119
|
+
get(id) {
|
|
120
|
+
return this.entries.get(id)?.info;
|
|
121
|
+
}
|
|
122
|
+
/** 列出所有活跃 PTY。 */
|
|
123
|
+
list() {
|
|
124
|
+
return [...this.entries.values()].map((e) => ({ ...e.info }));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 将 WebSocket 挂载到 PTY,建立双向数据流:
|
|
128
|
+
* - PTY onData → WS send(终端输出)
|
|
129
|
+
* - WS onMessage → PTY write(用户输入)
|
|
130
|
+
* - WS onClose → 摘除连接(PTY 保持存活)
|
|
131
|
+
*/
|
|
132
|
+
attachWebSocket(id, ws) {
|
|
133
|
+
const entry = this.entries.get(id);
|
|
134
|
+
if (!entry)
|
|
135
|
+
return false;
|
|
136
|
+
entry.sockets.add(ws);
|
|
137
|
+
ws.on('message', (data) => {
|
|
138
|
+
const text = typeof data === 'string' ? data : data.toString('utf8');
|
|
139
|
+
try {
|
|
140
|
+
entry.pty.write(text);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// PTY 可能已退出
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
ws.on('close', () => {
|
|
147
|
+
entry.sockets.delete(ws);
|
|
148
|
+
});
|
|
149
|
+
ws.on('error', () => {
|
|
150
|
+
entry.sockets.delete(ws);
|
|
151
|
+
});
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
/** 终止所有 PTY 进程(服务器关闭时调用)。 */
|
|
155
|
+
dispose() {
|
|
156
|
+
for (const id of this.entries.keys()) {
|
|
157
|
+
this.kill(id);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { AgentRegistry } from '../core/agents/types.js';
|
|
2
|
+
import type { Config } from '../core/config.js';
|
|
3
|
+
import type { DB } from '../db/client.js';
|
|
4
|
+
import type { chatStream as chatStreamFn } from '../llm/provider.js';
|
|
5
|
+
import type { Registry } from '../llm/registry.js';
|
|
6
|
+
import type { HookRunner, PluginRegistry } from '../plugins/types.js';
|
|
7
|
+
import type { Config as SharedConfig } from '../shared/types/config.js';
|
|
8
|
+
import type { URLRegistry } from '../shared/types/tool.js';
|
|
9
|
+
import type { ToolRegistry } from '../tools/types.js';
|
|
10
|
+
import type { HandoffServer, UpdateScheduler } from '../update/index.js';
|
|
11
|
+
import type { PTYManager } from './terminal/pty-manager.js';
|
|
12
|
+
import type { AgentManager } from './agent-manager.js';
|
|
13
|
+
import type { PermissionStore } from './permission/store.js';
|
|
14
|
+
/** 持有所有服务依赖的不可变上下文(config 字段可变用于 PATCH 更新)。 */
|
|
15
|
+
type ServerContext = {
|
|
16
|
+
db: DB;
|
|
17
|
+
config: Config;
|
|
18
|
+
toolRegistry: ToolRegistry;
|
|
19
|
+
llmRegistry: Registry;
|
|
20
|
+
/** 内置 URL 解析器注册表(file://, skill://);注入 agent loop 的 ToolContext。 */
|
|
21
|
+
urlRegistry: URLRegistry;
|
|
22
|
+
/** 插件 hook runner;接入 agent loop 的 tool/provider/message hook。 */
|
|
23
|
+
hookRunner: HookRunner;
|
|
24
|
+
/** 插件注册表(builtin + 已加载外部插件);供插件管理 API 查询。 */
|
|
25
|
+
pluginRegistry: PluginRegistry;
|
|
26
|
+
agentManager: AgentManager;
|
|
27
|
+
/** 全局权限确认 store(单例),独立于 agent run 生命周期。 */
|
|
28
|
+
permissionStore: PermissionStore;
|
|
29
|
+
/** 全局授权模式(运行时可临时切换):'default' 逐个确认;'auto' 自动放行 ask 工具(YOLO)。启动时取 config.permission.defaultMode,PUT /api/permissions 临时切换不回写 config。 */
|
|
30
|
+
permissionMode: 'default' | 'auto';
|
|
31
|
+
/** Agent 类型注册表(spec: multi-agent-design)。注入 agent loop 的 runSubAgent。 */
|
|
32
|
+
agentRegistry: AgentRegistry;
|
|
33
|
+
/** 后台版本检查调度器(spec §18.1);/api/update 读取其缓存结果。 */
|
|
34
|
+
updateScheduler: UpdateScheduler;
|
|
35
|
+
/** Handoff HTTP 端点(spec §18.3);热更新时新实例 POST /handoff 触发优雅退出。 undefined 表示未启用。 */
|
|
36
|
+
handoff?: {
|
|
37
|
+
port: number;
|
|
38
|
+
server: HandoffServer;
|
|
39
|
+
};
|
|
40
|
+
cwd: string;
|
|
41
|
+
/** 终端 PTY 管理器(Web 终端面板用)。 */
|
|
42
|
+
ptyManager: PTYManager;
|
|
43
|
+
/** 测试注入:覆盖 LLM chat stream。生产环境为 undefined。 */
|
|
44
|
+
chatStream?: typeof chatStreamFn;
|
|
45
|
+
};
|
|
46
|
+
/** POST /api/chat 请求体。 */
|
|
47
|
+
type ChatRequest = {
|
|
48
|
+
sessionId: string;
|
|
49
|
+
message: string;
|
|
50
|
+
provider?: string;
|
|
51
|
+
model?: string;
|
|
52
|
+
tools?: string[];
|
|
53
|
+
};
|
|
54
|
+
/** Agent 控制请求(abort/pause/resume)。 */
|
|
55
|
+
type ControlRequest = {
|
|
56
|
+
sessionId: string;
|
|
57
|
+
};
|
|
58
|
+
/** POST /api/chat/steer 请求体。 */
|
|
59
|
+
type SteerRequest = {
|
|
60
|
+
sessionId: string;
|
|
61
|
+
message: string;
|
|
62
|
+
};
|
|
63
|
+
/** POST /api/tools/confirm 请求体。 */
|
|
64
|
+
type ConfirmRequest = {
|
|
65
|
+
toolCallId: string;
|
|
66
|
+
approved: boolean;
|
|
67
|
+
};
|
|
68
|
+
/** 统一错误响应体。 */
|
|
69
|
+
type APIErrorBody = {
|
|
70
|
+
error: {
|
|
71
|
+
code: string;
|
|
72
|
+
message: string;
|
|
73
|
+
details?: Record<string, unknown>;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
export type { APIErrorBody, ChatRequest, Config, ConfirmRequest, ControlRequest, HandoffServer, ServerContext, SharedConfig, SteerRequest, };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DB } from '../db/client.js';
|
|
2
|
+
import type { ArchiveRef, CompactionArchive, SessionEntry } from './types.js';
|
|
3
|
+
/** Archive original entries before compaction/squash. Returns the archive id. */
|
|
4
|
+
declare function archiveOriginalEntries(handle: DB, sessionId: string, entries: SessionEntry[], archiveType: 'compaction' | 'squash' | 'shake', summary: string, compactionId: string): Promise<string>;
|
|
5
|
+
/** Get an archive by id. Returns null for invalid or non-existent ids. */
|
|
6
|
+
declare function getArchive(handle: DB, id: string): Promise<CompactionArchive | null>;
|
|
7
|
+
/** Get the original entries stored in an archive. */
|
|
8
|
+
declare function getArchiveOriginalEntries(handle: DB, archiveId: string): Promise<SessionEntry[]>;
|
|
9
|
+
/** Search archives by keyword (case-insensitive substring on searchable text). */
|
|
10
|
+
declare function searchArchives(handle: DB, sessionId: string, query: string): Promise<CompactionArchive[]>;
|
|
11
|
+
/** Parse a `@[archive:<id>]` or `@[squash:<n>]` reference from text. Returns null if none. */
|
|
12
|
+
declare function parseArchiveReference(text: string): ArchiveRef | null;
|
|
13
|
+
/** Resolve an archive reference to displayable text. */
|
|
14
|
+
declare function resolveArchiveReference(handle: DB, sessionId: string, ref: ArchiveRef): Promise<string | null>;
|
|
15
|
+
export { archiveOriginalEntries, getArchive, getArchiveOriginalEntries, parseArchiveReference, resolveArchiveReference, searchArchives, };
|