@william2000/dsh-nova-ui-task-board 0.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/README.md +86 -0
- package/cordis.patch.yml +13 -0
- package/lib/client.js +5641 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +4873 -0
- package/lib/types/client/NovaTaskBoardSettingsCard.d.ts +17 -0
- package/lib/types/client/PlaceholderBoard.d.ts +9 -0
- package/lib/types/client/apply-guard.d.ts +25 -0
- package/lib/types/client/board/ConfirmDialog.d.ts +12 -0
- package/lib/types/client/board/ConfirmTaskDialog.d.ts +8 -0
- package/lib/types/client/board/ConvertTaskModal.d.ts +17 -0
- package/lib/types/client/board/NewTaskModal.d.ts +6 -0
- package/lib/types/client/board/RequirementSplitModal.d.ts +14 -0
- package/lib/types/client/board/SplitConfirmDialog.d.ts +9 -0
- package/lib/types/client/board/TagEditor.d.ts +6 -0
- package/lib/types/client/board/TaskBoard.d.ts +9 -0
- package/lib/types/client/board/TaskCard.d.ts +52 -0
- package/lib/types/client/board/TaskDetail.d.ts +7 -0
- package/lib/types/client/board/status-key.d.ts +8 -0
- package/lib/types/client/board-controller.d.ts +274 -0
- package/lib/types/client/board-mount.d.ts +10 -0
- package/lib/types/client/chat-integration.d.ts +99 -0
- package/lib/types/client/drag-utils.d.ts +78 -0
- package/lib/types/client/filter.d.ts +42 -0
- package/lib/types/client/grouping.d.ts +54 -0
- package/lib/types/client/host-api.d.ts +62 -0
- package/lib/types/client/index.d.ts +46 -0
- package/lib/types/client/legacy-store.d.ts +28 -0
- package/lib/types/client/locales.d.ts +202 -0
- package/lib/types/client/schedule-presets.d.ts +20 -0
- package/lib/types/client/sidebar-entry-core.d.ts +54 -0
- package/lib/types/client/sidebar-entry.d.ts +19 -0
- package/lib/types/core/context.d.ts +139 -0
- package/lib/types/core/cron.d.ts +54 -0
- package/lib/types/core/migrate.d.ts +39 -0
- package/lib/types/core/model.d.ts +446 -0
- package/lib/types/core/recovery.d.ts +25 -0
- package/lib/types/core/split.d.ts +81 -0
- package/lib/types/core/transitions.d.ts +436 -0
- package/lib/types/dsh-home.d.ts +21 -0
- package/lib/types/host-automation.d.ts +59 -0
- package/lib/types/host-ledger.d.ts +146 -0
- package/lib/types/host-routes.d.ts +65 -0
- package/lib/types/host-runner.d.ts +177 -0
- package/lib/types/host-service.d.ts +291 -0
- package/lib/types/index.d.ts +90 -0
- package/lib/types/loopback.d.ts +23 -0
- package/lib/types/mount-once.d.ts +9 -0
- package/lib/types/protocol.d.ts +210 -0
- package/package.json +89 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host 权威账本(DSH-REQ-001 §8 架构 / §9.1 / §17 可靠性)。
|
|
3
|
+
*
|
|
4
|
+
* 「Host 权威、浏览器只是异步视图」的数据底座:所有任务状态持久化到
|
|
5
|
+
* `$DSH_HOME/nova-ui/task-board/ledger-v2.json`(0600,原子写),保证重启不丢、
|
|
6
|
+
* 并发一致、损坏可隔离。T003(协议)/T005(执行)/T006(调度)等在此之上工作。
|
|
7
|
+
*
|
|
8
|
+
* 可靠性机制(§17 / §19-7 数据部分):
|
|
9
|
+
* - **原子写**:临时文件 + fsync + 原子 rename + 目录 fsync;任何失败清理临时
|
|
10
|
+
* 文件并重新抛出,绝不产生半写文件;
|
|
11
|
+
* - **损坏隔离**:schema 不兼容/不可解析的账本 rename 为 `ledger-v2.json.corrupt-*`
|
|
12
|
+
* (保留原字节)并以空账本重建,不崩溃;
|
|
13
|
+
* - **单写者锁**:`ledger-v2.lock`(0600,`wx` 排他创建)记录 PID + token +
|
|
14
|
+
* 进程启动时间探测;PID 复用通过启动时间不匹配识别为陈旧锁并接管(防误判),
|
|
15
|
+
* 僵尸/死亡进程(Z/X)不阻塞启动;
|
|
16
|
+
* - **幂等缓存**:`recentRequests`(最近 256 条 requestId→SHA-256 指纹)随账本
|
|
17
|
+
* 原子持久化,Host 重启后浏览器重试仍安全(§11.3);
|
|
18
|
+
* - **v1 迁移**:`importSource` 按 sourceId 一次性导入、按 id 合并,导入 marker
|
|
19
|
+
* 随原子落账持久化(Host 确认后浏览器才清除 v1 原值,见 core/migrate.ts)。
|
|
20
|
+
*
|
|
21
|
+
* 串行性:本类全部方法为同步实现(无 await),单进程内天然串行;跨进程互斥
|
|
22
|
+
* 由锁保证。`revision` 每次原子落账严格 +1(scheduler 心跳类补丁除外,见
|
|
23
|
+
* `setScheduler`)。
|
|
24
|
+
*/
|
|
25
|
+
import type { AutomationRule, LedgerDocument, LedgerScheduler, LedgerState, SchedulerSnapshot, TaskRecord } from './core/model.ts';
|
|
26
|
+
/**
|
|
27
|
+
* 尽力而为的单字母进程状态('R','S','D','Z',...),平台不支持时返回 undefined。
|
|
28
|
+
* Linux 直接读 /proc(无子进程);其他 POSIX 用 `ps -o stat=`;Windows 无僵尸
|
|
29
|
+
* 态,返回 undefined(此时 kill(0) 探测为唯一权威)。
|
|
30
|
+
*/
|
|
31
|
+
export declare function processState(pid: number): string | undefined;
|
|
32
|
+
/** PID 是否存活(僵尸/死亡态视为不存活)。 */
|
|
33
|
+
export declare function processIsAlive(pid: number): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* 尽力而为的进程启动时间(Unix epoch ms),用于证明锁记录里的 PID 是否真的是
|
|
36
|
+
* 之前那个进程(PID 复用会被识别为陈旧锁而不是永久阻塞启动)。平台探测不可用
|
|
37
|
+
* 时返回 undefined,调用方 fail closed。
|
|
38
|
+
*/
|
|
39
|
+
export declare function processStartTimeMs(pid: number): number | undefined;
|
|
40
|
+
/** 记录的启动时间是否证明记录的 PID 已是另一个进程(PID 复用探测核心)。 */
|
|
41
|
+
export declare function startTimeMismatch(recorded: number, actual: number, exact: boolean): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* 进程探测面(§17 可测试性:Host 编排用结构面注入 fake)。生产默认用真实
|
|
44
|
+
* 平台探测;测试可注入假实现来覆盖 PID 复用接管等分支(真实探测在部分
|
|
45
|
+
* 沙箱/系统上不可用——如 macOS 限制 ps——会 fail closed 而非误判)。
|
|
46
|
+
*/
|
|
47
|
+
export interface ProcessProbes {
|
|
48
|
+
isAlive(pid: number): boolean;
|
|
49
|
+
/** 进程启动时间(Unix epoch ms);平台探测不可用时返回 undefined。 */
|
|
50
|
+
startTimeMs(pid: number): number | undefined;
|
|
51
|
+
/** 本进程启动时间(带缓存)。 */
|
|
52
|
+
ownStartTimeMs(): number | undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Host 权威账本。构造即获取单写者锁并加载/恢复账本;进程内所有写入经
|
|
56
|
+
* `applyRequest`/`importSource`/`mutate`/`setScheduler` 串行原子落账。
|
|
57
|
+
*/
|
|
58
|
+
export declare class HostLedger {
|
|
59
|
+
private readonly now;
|
|
60
|
+
/** 账本文件绝对路径。 */
|
|
61
|
+
readonly file: string;
|
|
62
|
+
/** 锁文件绝对路径。 */
|
|
63
|
+
readonly lockFile: string;
|
|
64
|
+
/** 账本目录。 */
|
|
65
|
+
readonly dir: string;
|
|
66
|
+
private document;
|
|
67
|
+
private readonly listeners;
|
|
68
|
+
private readonly requestCache;
|
|
69
|
+
private readonly lockToken;
|
|
70
|
+
private lockFd;
|
|
71
|
+
private readonly probes;
|
|
72
|
+
/**
|
|
73
|
+
* @param dir - 账本目录(默认 `$DSH_HOME/nova-ui/task-board/`,见 AGENTS.md D2)。
|
|
74
|
+
* @param now - 时间源(测试可注入)。
|
|
75
|
+
* @param probes - 进程探测面(默认真实探测;测试可注入 fake,见 §17 可测试性)。
|
|
76
|
+
*/
|
|
77
|
+
constructor(dir?: string, now?: () => number, probes?: ProcessProbes);
|
|
78
|
+
/** revision + scheduler(不克隆任务),供 SSE 事件帧使用。 */
|
|
79
|
+
summary(): {
|
|
80
|
+
revision: number;
|
|
81
|
+
scheduler: SchedulerSnapshot;
|
|
82
|
+
};
|
|
83
|
+
/** 全量状态快照(任务/规则深拷贝,调用方修改不影响账本)。 */
|
|
84
|
+
state(): LedgerState;
|
|
85
|
+
/**
|
|
86
|
+
* 只读任务数组(不克隆,O(1))。仅供服务层扫描/计数(如 power 快照的
|
|
87
|
+
* runningSessions/armedSchedules);**绝不可修改**——任何变更必须走
|
|
88
|
+
* `applyRequest`/`mutate` 的原子落账路径。
|
|
89
|
+
*/
|
|
90
|
+
peekTasks(): readonly TaskRecord[];
|
|
91
|
+
/** 只读自动化规则数组(不克隆;变更必须走 applyRequest/mutate 原子落账)。 */
|
|
92
|
+
peekAutomationRules(): readonly AutomationRule[];
|
|
93
|
+
/** 订阅账本变更(每次原子落账后触发)。 */
|
|
94
|
+
subscribe(listener: () => void): () => void;
|
|
95
|
+
/** 释放锁(仅当锁仍属本实例时删除锁文件,避免误删外部替换的锁)。 */
|
|
96
|
+
dispose(): void;
|
|
97
|
+
/**
|
|
98
|
+
* 幂等 action 应用(§11.3)。调用方(T003 协议层)提供序列化的 action(仅
|
|
99
|
+
* 用于 SHA-256 指纹)与变更闭包;闭包返回 true 表示文档有实际变更。
|
|
100
|
+
*
|
|
101
|
+
* - 重复 requestId + 相同指纹 → 直接返回当前 state(不重新应用、不 bump);
|
|
102
|
+
* - 同 requestId + 不同动作 → 抛错(`request id was reused with a different action`);
|
|
103
|
+
* - 首次请求 → 指纹先行入缓存,随后应用;变更闭包抛错则回滚缓存条目;
|
|
104
|
+
* - 成功且 changed → 原子落账(revision+1),指纹随账本一并持久化——
|
|
105
|
+
* Host 重启后浏览器重试仍安全。
|
|
106
|
+
*
|
|
107
|
+
* @param requestId - 浏览器侧 uuid。
|
|
108
|
+
* @param action - 序列化 action(指纹源;必须可 JSON 序列化)。
|
|
109
|
+
* @param apply - 变更闭包,就地修改文档;返回 true 表示发生变更(需落账)。
|
|
110
|
+
*/
|
|
111
|
+
applyRequest(requestId: string, action: unknown, apply: (doc: LedgerDocument) => boolean): LedgerState;
|
|
112
|
+
/**
|
|
113
|
+
* v1 迁移导入(§11.2 `import` / §9.5):按 sourceId 一次性导入、按 id 合并、
|
|
114
|
+
* 写入 importedSources marker(原子落账确认后才写入)。重复 sourceId 幂等
|
|
115
|
+
* no-op(不落账不 bump)。浏览器以成功响应 + 记住 ledgerId 作为确认凭证
|
|
116
|
+
* (T003/T004 接线;marker 不进 state/summary,见 `summary`)。
|
|
117
|
+
*/
|
|
118
|
+
importSource(requestId: string, sourceId: string, tasks: readonly TaskRecord[]): LedgerState;
|
|
119
|
+
/**
|
|
120
|
+
* 通用原子变更(Host 编排用,T005/T006 的 runner/scheduler 也走这里)。
|
|
121
|
+
* `apply` 返回 true 才落账(revision+1);返回 false 保持 revision 不变。
|
|
122
|
+
*/
|
|
123
|
+
mutate(apply: (doc: LedgerDocument) => boolean): LedgerState;
|
|
124
|
+
/**
|
|
125
|
+
* 更新 scheduler 补丁(心跳 lastTickAt、错误可见性等,§9.1)。
|
|
126
|
+
* 不 bump revision(避免 30s 心跳驱动客户端全量重拉);无实际变更不写盘。
|
|
127
|
+
*/
|
|
128
|
+
setScheduler(patch: Partial<LedgerScheduler>): void;
|
|
129
|
+
private syncRecentRequests;
|
|
130
|
+
/**
|
|
131
|
+
* 原子落账:临时文件 + fsync + 原子 rename + 目录 fsync(0600)。
|
|
132
|
+
* 任一步失败清理临时文件并重新抛出——绝不产生半写文件。
|
|
133
|
+
*/
|
|
134
|
+
private commit;
|
|
135
|
+
/** 加载账本;损坏/schema 不兼容 → 隔离原文件并以空账本重建(§17 损坏隔离)。 */
|
|
136
|
+
private load;
|
|
137
|
+
/** 损坏隔离:原文件 rename 为 `ledger-v2.json.corrupt-<ts>-<pid>-<uuid>`,原字节保留。 */
|
|
138
|
+
private quarantine;
|
|
139
|
+
private notify;
|
|
140
|
+
/**
|
|
141
|
+
* 获取单写者锁:`wx` 排他创建(已存在则 EEXIST 走锁主判定)。
|
|
142
|
+
* 锁记录 PID + token + 启动时间探测;PID 复用(启动时间不匹配)识别为陈旧锁
|
|
143
|
+
* 并接管;锁不可读时 fail closed(提示人工清理)。
|
|
144
|
+
*/
|
|
145
|
+
private acquireLock;
|
|
146
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 路由层(DSH-REQ-001 §11.1 三端点 + §16 安全栅栏)。
|
|
3
|
+
*
|
|
4
|
+
* 端点(同源,前缀 `/api/nova-task-board`):
|
|
5
|
+
* - `GET /api/nova-task-board/state` → 全量快照 `{schemaVersion, revision,
|
|
6
|
+
* tasks, scheduler, power, automationRules}`;
|
|
7
|
+
* - `POST /api/nova-task-board/action` → 幂等信封 `{requestId, action}`,返回
|
|
8
|
+
* 新 snapshot;
|
|
9
|
+
* - `GET /api/nova-task-board/events` → SSE 推送 `{revision, scheduler,
|
|
10
|
+
* power}`(15s 心跳注释帧),revision 变化时客户端重拉 state;
|
|
11
|
+
* - `POST /api/nova-task-board/webhooks/github` → GitHub `issues` 事件接收
|
|
12
|
+
* (T010 §14.1):HMAC-SHA256 签名校验(`X-Hub-Signature-256`,密钥来自
|
|
13
|
+
* 环境变量,不落配置);未配置密钥 → 503;签名不匹配 → 401。该端点不走
|
|
14
|
+
* 浏览器同源栅栏(GitHub 无法携带浏览器信号),HMAC 即权威边界。
|
|
15
|
+
*
|
|
16
|
+
* 安全栅栏(§16.1/16.2/16.3):
|
|
17
|
+
* - 浏览器同源标记(sec-fetch-site/Origin)为触发器而非权威校验——裸命令行
|
|
18
|
+
* 请求(curl 无任何浏览器信号)直接 403;权威边界是回环 socket + Host +
|
|
19
|
+
* origin 等价校验(loopback.ts + isTrustedTaskBoardRequest);
|
|
20
|
+
* - 同机认证反代必须白名单(trustedProxyHosts 规范化 authority)+ 服务端注入
|
|
21
|
+
* token(`x-dsh-task-board-proxy-token`,timingSafeEqual 比较);
|
|
22
|
+
* - POST 必须 application/json(否则 415);普通 action ≤ 64 KiB、import /
|
|
23
|
+
* propose-batch / start-split ≤ 2 MiB(超限 413);非法信封/未知 action → 400
|
|
24
|
+
* invalid-action;状态机守卫失败 → 400 + 错误信息。
|
|
25
|
+
*/
|
|
26
|
+
import type { IncomingMessage } from 'node:http';
|
|
27
|
+
import type { WebRoute } from '@deepseek-ai/dsh-host-webserver';
|
|
28
|
+
import type { TaskBoardHostService } from './host-service.ts';
|
|
29
|
+
/** 普通 action 载荷上限(§11.1)。 */
|
|
30
|
+
export declare const ACTION_LIMIT: number;
|
|
31
|
+
/** import(批量候选同限)载荷上限(§11.1)。 */
|
|
32
|
+
export declare const IMPORT_LIMIT: number;
|
|
33
|
+
/** SSE 心跳间隔(§17 性能:SSE 心跳 15s)。 */
|
|
34
|
+
export declare const HEARTBEAT_MS = 15000;
|
|
35
|
+
/** 同机认证反代在自身认证后替换注入的服务端 token 头(§16.1)。 */
|
|
36
|
+
export declare const TASK_BOARD_PROXY_TOKEN_HEADER = "x-dsh-task-board-proxy-token";
|
|
37
|
+
/** GitHub Webhook 签名头(HMAC-SHA256,`sha256=<hex>`,§14.1)。 */
|
|
38
|
+
export declare const GITHUB_SIGNATURE_HEADER = "x-hub-signature-256";
|
|
39
|
+
/** 路由访问面:默认仅回环 + 浏览器同源标记;反代须白名单 + token。 */
|
|
40
|
+
export interface TaskBoardRouteAccess {
|
|
41
|
+
trustedProxyHosts?: readonly string[];
|
|
42
|
+
proxyToken?: string;
|
|
43
|
+
/** GitHub Webhook HMAC 密钥(env 解析,不落配置;缺省 = Webhook 端点不可用)。 */
|
|
44
|
+
webhookSecret?: string;
|
|
45
|
+
}
|
|
46
|
+
interface ResolvedRouteAccess {
|
|
47
|
+
trustedProxyHosts: ReadonlySet<string>;
|
|
48
|
+
proxyToken?: string;
|
|
49
|
+
webhookSecret?: string;
|
|
50
|
+
}
|
|
51
|
+
/** 解析访问面并校验白名单条目的规范化形态(配置错误直接 throw,插件加载失败)。 */
|
|
52
|
+
export declare function resolveAccess(access: TaskBoardRouteAccess): ResolvedRouteAccess;
|
|
53
|
+
/**
|
|
54
|
+
* GitHub Webhook 签名校验(§14.1):`X-Hub-Signature-256: sha256=<hex>`,
|
|
55
|
+
* 对原始请求体做 HMAC-SHA256 并恒定时间比较。签名头缺失/畸形/不匹配 → false。
|
|
56
|
+
*/
|
|
57
|
+
export declare function verifyGithubSignature(rawBody: string, signature: string | string[] | undefined, secret: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* 请求级信任判定:浏览器同源标记 + (回环请求 | 白名单反代 + token)。
|
|
60
|
+
* 回环判定包含 socket 地址、Host 头与 origin 等价校验(loopback.ts)。
|
|
61
|
+
*/
|
|
62
|
+
export declare function isTrustedTaskBoardRequest(req: IncomingMessage, access: ResolvedRouteAccess): boolean;
|
|
63
|
+
/** 组装三个端点路由(宿主的 webServer.register 对重复 (kind, path) 直接 throw,勿重复注册)。 */
|
|
64
|
+
export declare function makeTaskBoardRoutes(service: TaskBoardHostService, access?: TaskBoardRouteAccess): WebRoute[];
|
|
65
|
+
export {};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 真实执行与结算(DSH-REQ-001 §13,T005)。
|
|
3
|
+
*
|
|
4
|
+
* HostExecutionRunner 是「点击执行 → 独立 DSH 会话 → 按 turn 结算」的执行驱动:
|
|
5
|
+
*
|
|
6
|
+
* - **launch(§13.2)**:fail-closed 钉住目标——工作区必须存在于 workspace.list、
|
|
7
|
+
* agent 预设必须存在且未损坏,任一失败直接抛错(服务层结算 failed,绝不发送
|
|
8
|
+
* 任务 Prompt);随后创建独立会话(sessions.create,携带钉住的 workspaceId/
|
|
9
|
+
* agentPreset)→ rename 为任务标题(**可失败但仅装饰**,§13.2)→ 钉住权限经
|
|
10
|
+
* `/permission <id>` 命令应用(被拒/未匹配 → 失败)→ `sessions.prompt(mode:
|
|
11
|
+
* 'queue', text = prompt || title)`。会话创建后的失败包装为
|
|
12
|
+
* `SessionLaunchError`(携带 sessionId,服务层先回填再结算,保留跳转线索)。
|
|
13
|
+
* - **listRunning(§13.3)**:轮询会话列表(会话列表 RPC 失败 → known:false,
|
|
14
|
+
* 服务层据此标记 sessionStateKnown=false)。
|
|
15
|
+
* - **inspect(§13.3)**:单条执行的结算判定——会话消失 → cancelled;仍在运行
|
|
16
|
+
* → pending;已结束 → 翻页读历史,取时间 ≥ startedAt 的首条 `turn/end`
|
|
17
|
+
* 判定:`reason.kind === 'error'` → failed,否则 → succeeded。
|
|
18
|
+
*
|
|
19
|
+
* 依赖面(§17 可测试性):本类只依赖 `ctx.apiProxy` 的窄子集(ExecutionApi)与
|
|
20
|
+
* 命令调度器(ExecutionCommandDispatcher,`/permission` 走宿主 commands
|
|
21
|
+
* 注册表、不经模型),测试注入 fake 即可脱离宿主单测;生产接线在 index.ts。
|
|
22
|
+
*
|
|
23
|
+
* 执行隔离(§16.4):任务 Prompt 只作为发给 agent 的数据;权限只能经
|
|
24
|
+
* `/permission` 命令应用,Prompt 不能提权。
|
|
25
|
+
*/
|
|
26
|
+
import type { HistoryEntry, PromptContentPart, RpcRequest, RpcResponse } from '@deepseek-ai/dsh-host-apiproxy/api';
|
|
27
|
+
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
28
|
+
import type { TaskRecord } from './core/model.ts';
|
|
29
|
+
/** `/permission <id>` 命令的超时(对齐参考实现 30s)。 */
|
|
30
|
+
export declare const PERMISSION_COMMAND_TIMEOUT_MS = 30000;
|
|
31
|
+
/** inspect 历史翻页上限(对齐参考实现)。 */
|
|
32
|
+
export declare const MAX_HISTORY_PAGES = 100;
|
|
33
|
+
/** inspect 每页事件数(对齐参考实现)。 */
|
|
34
|
+
export declare const HISTORY_PAGE_SIZE = 100;
|
|
35
|
+
/** 会话列表行(inspect 所需的最小面;SDK SessionSummary 是其超集)。 */
|
|
36
|
+
export interface ExecutionSessionRow {
|
|
37
|
+
sessionId: string;
|
|
38
|
+
running: boolean;
|
|
39
|
+
}
|
|
40
|
+
/** 5s 轮询的会话列表状态(§13.3):RPC 失败时 known:false(服务层据此降级)。 */
|
|
41
|
+
export type ExecutionSessionState = {
|
|
42
|
+
known: true;
|
|
43
|
+
count: number;
|
|
44
|
+
items: readonly ExecutionSessionRow[];
|
|
45
|
+
} | {
|
|
46
|
+
known: false;
|
|
47
|
+
};
|
|
48
|
+
/** 单条执行的结算判定(§13.3)。 */
|
|
49
|
+
export type ExecutionOutcome = 'pending' | 'cancelled' | 'failed' | 'succeeded';
|
|
50
|
+
export interface ExecutionInspection {
|
|
51
|
+
outcome: ExecutionOutcome;
|
|
52
|
+
error?: string;
|
|
53
|
+
/** 本次执行(startedAt 之后)最近的 assistant 消息文本(B5:Host 侧摘取摘要源)。 */
|
|
54
|
+
lastAssistantText?: string;
|
|
55
|
+
}
|
|
56
|
+
/** `/permission <id>` 命令调度面(index.ts 接线到宿主 ctx.commands,见 §16.4)。 */
|
|
57
|
+
export interface ExecutionCommandDispatcher {
|
|
58
|
+
/**
|
|
59
|
+
* 在会话上执行一条斜杠命令(不经模型)。undefined = 未被承认(命令名不匹配/
|
|
60
|
+
* 语法未解析);kind:'error' = 被拒/未匹配。
|
|
61
|
+
*/
|
|
62
|
+
execute(sessionId: string, line: string, signal: AbortSignal): Promise<{
|
|
63
|
+
kind: 'success';
|
|
64
|
+
text?: string;
|
|
65
|
+
} | {
|
|
66
|
+
kind: 'error';
|
|
67
|
+
text: string;
|
|
68
|
+
} | undefined>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* HostExecutionRunner 依赖的 API 窄面(ctx.apiProxy 的结构子集)。只声明
|
|
72
|
+
* launch/inspect 用到的域方法;生产传完整 ApiProxy(结构兼容),测试传 fake。
|
|
73
|
+
*/
|
|
74
|
+
export interface ExecutionApi {
|
|
75
|
+
workspace: {
|
|
76
|
+
list(request: RpcRequest<{}>): Promise<RpcResponse<{
|
|
77
|
+
items: Array<{
|
|
78
|
+
workspaceId: string;
|
|
79
|
+
}>;
|
|
80
|
+
}>>;
|
|
81
|
+
};
|
|
82
|
+
agentPresets: {
|
|
83
|
+
list(request: RpcRequest<{}>): Promise<RpcResponse<{
|
|
84
|
+
presets: readonly {
|
|
85
|
+
id: string;
|
|
86
|
+
broken?: string;
|
|
87
|
+
}[];
|
|
88
|
+
}>>;
|
|
89
|
+
};
|
|
90
|
+
sessions: {
|
|
91
|
+
create(request: RpcRequest<{
|
|
92
|
+
workspaceId?: string;
|
|
93
|
+
agentPreset?: string;
|
|
94
|
+
}>): Promise<RpcResponse<{
|
|
95
|
+
sessionId: SessionId;
|
|
96
|
+
}>>;
|
|
97
|
+
rename(request: RpcRequest<{
|
|
98
|
+
sessionId: string;
|
|
99
|
+
title: string;
|
|
100
|
+
}>): Promise<RpcResponse<{
|
|
101
|
+
title: string;
|
|
102
|
+
seq: number;
|
|
103
|
+
}>>;
|
|
104
|
+
prompt(request: RpcRequest<{
|
|
105
|
+
sessionId: SessionId;
|
|
106
|
+
mode: 'queue' | 'steer';
|
|
107
|
+
content: PromptContentPart[];
|
|
108
|
+
clientTimeZone?: string;
|
|
109
|
+
}>): Promise<RpcResponse<{
|
|
110
|
+
accepted: true;
|
|
111
|
+
}>>;
|
|
112
|
+
list(request: RpcRequest<{}>): Promise<RpcResponse<{
|
|
113
|
+
items: readonly ExecutionSessionRow[];
|
|
114
|
+
}>>;
|
|
115
|
+
history(request: RpcRequest<{
|
|
116
|
+
sessionId: string;
|
|
117
|
+
beforeSeq?: number;
|
|
118
|
+
maxMessages?: number;
|
|
119
|
+
}>): Promise<RpcResponse<{
|
|
120
|
+
events: HistoryEntry[];
|
|
121
|
+
hasMore: boolean;
|
|
122
|
+
}>>;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** 会话创建后的启动失败:仍标识会话(服务层先回填 sessionId 再结算 failed)。 */
|
|
126
|
+
export declare class SessionLaunchError extends Error {
|
|
127
|
+
readonly sessionId: string;
|
|
128
|
+
constructor(sessionId: string, cause: unknown);
|
|
129
|
+
}
|
|
130
|
+
/** 会话 transcript 全文读取上限(T012/B8:拆分会话结果解析;防超大会话内存放大)。 */
|
|
131
|
+
export declare const TRANSCRIPT_TEXT_LIMIT = 200000;
|
|
132
|
+
/**
|
|
133
|
+
* 真实执行与结算驱动(§13.2/§13.3)。无状态、无计时器——轮询调度由
|
|
134
|
+
* TaskBoardHostService 持有(5s 间隔);本类只做单次 launch/inspect。
|
|
135
|
+
*/
|
|
136
|
+
export declare class HostExecutionRunner {
|
|
137
|
+
private readonly api;
|
|
138
|
+
private readonly commands;
|
|
139
|
+
/** @param api - apiProxy 窄面(workspace/agentPresets/sessions 的读写子集)。 */
|
|
140
|
+
constructor(api: ExecutionApi, commands: ExecutionCommandDispatcher | undefined);
|
|
141
|
+
/**
|
|
142
|
+
* 启动一次执行(§13.2 流程):
|
|
143
|
+
* 1. fail-closed 校验钉住目标(工作区存在、预设存在且未损坏)——任一失败抛
|
|
144
|
+
* 错,调用方结算 failed,**不发送任务 Prompt**;
|
|
145
|
+
* 2. `sessions.create` 创建独立会话(携带钉住的 workspaceId/agentPreset);
|
|
146
|
+
* 3. rename 为任务标题(可失败但仅装饰);权限经 `/permission` 命令应用
|
|
147
|
+
* (被拒/未匹配 → 失败);
|
|
148
|
+
* 4. `sessions.prompt(mode:'queue', text = prompt || title)`。
|
|
149
|
+
*
|
|
150
|
+
* 会话创建之后的任何失败抛 `SessionLaunchError`(携带 sessionId)。
|
|
151
|
+
* @returns 创建的会话 id。
|
|
152
|
+
*/
|
|
153
|
+
launch(task: TaskRecord): Promise<string>;
|
|
154
|
+
/**
|
|
155
|
+
* 轮询会话列表(§13.3,5s 一次由服务层调度)。RPC 失败/不可用 → known:false
|
|
156
|
+
* (服务层据此把 sessionStateKnown 置 false,不据此结算)。
|
|
157
|
+
*/
|
|
158
|
+
listRunning(): Promise<ExecutionSessionState>;
|
|
159
|
+
/**
|
|
160
|
+
* 单条执行的结算判定(§13.3):
|
|
161
|
+
* - 会话不在列表中 → cancelled(`execution session no longer exists`);
|
|
162
|
+
* - 会话仍在运行 → pending;
|
|
163
|
+
* - 已结束 → 翻页读历史直到越过 startedAt 边界,取时间 ≥ startedAt 的首条
|
|
164
|
+
* `turn/end`:reason.kind==='error' → failed,否则 → succeeded;边界内无
|
|
165
|
+
* turn/end(如 prompt 从未被消费)→ pending(保持观察)。
|
|
166
|
+
* @param sessions - 本次轮询已取的会话列表(避免 1 + E 次 list RPC,对齐参考
|
|
167
|
+
* 实现);缺省时自行拉取。
|
|
168
|
+
*/
|
|
169
|
+
inspect(sessionId: string, startedAt: number, sessions?: readonly ExecutionSessionRow[]): Promise<ExecutionInspection>;
|
|
170
|
+
/**
|
|
171
|
+
* 读取会话 transcript 全文(T012/B8:拆分会话结果解析用)。翻页读取历史事件,
|
|
172
|
+
* 拼接全部 assistant 消息文本(顺序拼接、换行分隔;总长封顶
|
|
173
|
+
* TRANSCRIPT_TEXT_LIMIT)。会话不存在/历史不可读 → undefined(调用方按失败
|
|
174
|
+
* 处理)。拆分会话是独立专用会话,全文即拆分结果(无需按 startedAt 截取)。
|
|
175
|
+
*/
|
|
176
|
+
readTranscriptText(sessionId: string): Promise<string | undefined>;
|
|
177
|
+
}
|