@zhushanwen/extension-protocol 0.25.0 → 0.27.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 +2 -1
- package/dist/background-task-entry.d.ts +20 -11
- package/dist/background-task-entry.js +27 -4
- package/dist/chunk-TFO4NW5X.js +23 -0
- package/dist/index.d.ts +160 -160
- package/dist/index.js +58 -122
- package/dist/scheduler-store-entry.d.ts +57 -0
- package/dist/scheduler-store-entry.js +102 -0
- package/dist/types-DcAmuFZ2.d.ts +70 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ pi extension 跨层契约包:类型 + helper 函数 + 共享行为原语,零
|
|
|
8
8
|
- `extensions/` —— 有运行时定制逻辑的 extension(marker + helper)
|
|
9
9
|
- `ask-user/` —— 富交互(select 通道 + marker)
|
|
10
10
|
- `plan/` —— plan 生命周期状态机 + 审阅回传值域契约 + 旧 entry legacy 读取(纯函数零 pi 依赖,扩展 / runtime / renderer 三层共用)
|
|
11
|
-
- `scheduler/` —— scheduler 任务条目契约 +
|
|
11
|
+
- `scheduler/` —— scheduler 任务条目契约 + 时间格式化(纯契约与纯函数,零 pi/node 依赖);任务库快照文件契约 `store.ts`(含 node:fs/node:path,走独立子出口)
|
|
12
12
|
- `scheduler-create/` —— scheduler 创建确认共享资产(ScheduleDraft / ScheduleFormResult 类型 + 形状与时间折叠检查 + marker)
|
|
13
13
|
- `session-manager/` —— agent-managed session 嵌套 `{action, params}` 契约(select 通道 + marker)
|
|
14
14
|
- `subagent-engine/` —— 引擎可发现性(`engines.json` 状态文件 + 引擎配置视图)
|
|
@@ -19,6 +19,7 @@ pi extension 跨层契约包:类型 + helper 函数 + 共享行为原语,零
|
|
|
19
19
|
- `pending-entries` —— pending 事件流差集核心(register 去重 + unregister 抵消,纯算法)
|
|
20
20
|
- `background-task` —— base-tool-enhance 后台任务 `registry.json` 文件契约
|
|
21
21
|
- 子出口 `@zhushanwen/extension-protocol/background-task` —— 后台任务行为原语(进程处置 / registry 文件 IO / output tail,含 node 内建依赖,不进 index 桶出口)
|
|
22
|
+
- 子出口 `@zhushanwen/extension-protocol/scheduler-store` —— scheduler 任务库快照文件契约(路径推导 `schedulerStoreFilePath` + 读改写原语,含 node 内建依赖,不进 index 桶出口)
|
|
22
23
|
|
|
23
24
|
## 设计原则
|
|
24
25
|
|
|
@@ -5,18 +5,21 @@ import { B as BackgroundTaskState, a as BackgroundTaskRegistryEntry } from './ba
|
|
|
5
5
|
*
|
|
6
6
|
* 契约两端(此前各持一份逐字同构实现,对齐手段是注释,已归一到本模块):
|
|
7
7
|
* - extension 侧:@zhushanwen/pi-base-tool-enhance(bash_kill 前校验 / 后台 timeout
|
|
8
|
-
* 到点校验 / 对账判死),原实现在其 kill-tree.ts
|
|
8
|
+
* 到点校验 / 对账判死),原实现在其 kill-tree.ts(归一前原仓库文件,本仓不可考)
|
|
9
9
|
* - runtime 侧:taiji background-task-reaper(孤儿收殓三分支判定),原实现在
|
|
10
|
-
* background-task-reaper.ts
|
|
10
|
+
* background-task-reaper.ts 原语区(原语已归一迁出至本模块,该文件余部仍在 runtime)
|
|
11
11
|
*
|
|
12
12
|
* 分支语义(与 pi 实装对齐,勿单侧擅改):Windows `taskkill /F /T`;POSIX 按进程组终止
|
|
13
|
-
* `kill -- -<pgid>`(后台任务 detached spawn 自成进程组,pgid = pid
|
|
14
|
-
*
|
|
13
|
+
* `kill -- -<pgid>`(后台任务 detached spawn 自成进程组,pgid = pid);快路径落空时解析
|
|
14
|
+
* 目标真实 pgid(`ps -o pgid=`)按组杀——转后台任务组成员非组长形态(ADR-0184,组长
|
|
15
|
+
* 死后组仍有效);解析出的组等于调用者自身进程组时跳过组杀(防自杀)落回退;均不可达
|
|
16
|
+
* 才回退单 pid + `pgrep -P` 递归杀残留子进程(先孙后子最后目标,防 reparent 逃逸枚举)。
|
|
15
17
|
*
|
|
16
18
|
* 日志通道:本模块零日志依赖(protocol 零运行时依赖纪律)——回退路径的诊断经可选
|
|
17
19
|
* 回调 `onFallback(step, err)` 上报,extension 侧注入 logger.debug 适配、runtime 侧
|
|
18
20
|
* 注入 console.debug 适配。step 为稳定短标识符:
|
|
19
21
|
* - 'process-group-kill-missed' POSIX 进程组 kill 失败(组长已死),走回退
|
|
22
|
+
* - 'resolved-group-kill-missed' 解析真实 pgid 的组 kill 失败,走回退(ADR-0184)
|
|
20
23
|
* - 'single-pid-kill-missed' 单 pid kill 失败(目标已死,幂等语义)
|
|
21
24
|
* - 'taskkill-failed' Windows taskkill 失败
|
|
22
25
|
* - 'descendant-enumeration-failed' pgrep 不可用/失败,放弃子孙枚举
|
|
@@ -31,10 +34,13 @@ type ProcessFallbackLogger = (step: string, err: unknown) => void;
|
|
|
31
34
|
declare function isPidAlive(pid: number): boolean;
|
|
32
35
|
/**
|
|
33
36
|
* 杀整棵进程树(同步;收殓路径在 process.on("exit") 里跑,必须同步)。
|
|
34
|
-
* POSIX
|
|
35
|
-
*
|
|
36
|
-
* reparent
|
|
37
|
-
*
|
|
37
|
+
* POSIX:优先按进程组终止——detached spawn 形态(显式后台任务)pgid = pid,kill(-pid)
|
|
38
|
+
* 一次覆盖全部子孙;pid 非组长(前台转后台任务复用前台进程链,pgid ≠ pid)时解析真实
|
|
39
|
+
* pgid 按组杀(reparent 不换组,组长死后组仍在);均不可达时回退单 pid + pgrep -P
|
|
40
|
+
* 递归清理(先枚举杀孙辈再杀子辈、最后杀目标本身——防目标死后子进程被 reparent 逃逸
|
|
41
|
+
* 枚举)。解析出的 pgid 等于调用者自身进程组时跳过组杀(防自杀),走回退。
|
|
42
|
+
* Windows taskkill /F /T。幂等:目标已死时静默成功(回退路径的诊断经 onFallback
|
|
43
|
+
* 上报,无回调则静默)。
|
|
38
44
|
*/
|
|
39
45
|
declare function killProcessTree(pid: number, onFallback?: ProcessFallbackLogger): void;
|
|
40
46
|
/**
|
|
@@ -95,9 +101,12 @@ declare function corruptPathFor(registryPath: string): string;
|
|
|
95
101
|
* 读取 registry 全量条目 + 损坏标记(runtime 现形态为超集;extension 侧 Map 形态
|
|
96
102
|
* 由各侧薄壳派生)。UI 读侧需要区分「真的空表」与「损坏被隔离的空表」。
|
|
97
103
|
*
|
|
98
|
-
* 文件不存在
|
|
99
|
-
*
|
|
100
|
-
*
|
|
104
|
+
* 文件不存在 → 空表 + corrupted:false(合法空态);读失败(权限等,文件存在但不可读)
|
|
105
|
+
* → onLog warn + 抛错(禁止折叠成空表——读改写按空表合并写回会以空表重建唯一登记
|
|
106
|
+
* 文件,静默丢失全部既有条目;降级决策归调用方:写侧锁壳 {success:false} 留待重试、
|
|
107
|
+
* 收殓器跳过本目录下轮重试、读侧工具上抛为显式失败);解析失败 → 重命名 .corrupt
|
|
108
|
+
* 保留现场 + onLog warn + 空表 + corrupted:true(「空表重建」由后续写入自然完成,
|
|
109
|
+
* 不立即写空文件)。
|
|
101
110
|
*/
|
|
102
111
|
declare function readRegistry(registryPath: string, onLog?: RegistryFileLogFn): {
|
|
103
112
|
entries: BackgroundTaskRegistryEntry[];
|
|
@@ -29,12 +29,35 @@ function killProcessTree(pid, onFallback) {
|
|
|
29
29
|
} catch (err) {
|
|
30
30
|
onFallback?.("process-group-kill-missed", err);
|
|
31
31
|
}
|
|
32
|
+
const ownPgid = readPgid(process.pid);
|
|
33
|
+
const pgid = readPgid(pid);
|
|
34
|
+
if (pgid !== null && pgid > 1 && pgid !== ownPgid) {
|
|
35
|
+
try {
|
|
36
|
+
process.kill(-pgid, "SIGKILL");
|
|
37
|
+
return;
|
|
38
|
+
} catch (err) {
|
|
39
|
+
onFallback?.("resolved-group-kill-missed", err);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
killDescendantsRecursive(pid, onFallback);
|
|
32
43
|
try {
|
|
33
44
|
process.kill(pid, "SIGKILL");
|
|
34
45
|
} catch (err) {
|
|
35
46
|
onFallback?.("single-pid-kill-missed", err);
|
|
36
47
|
}
|
|
37
|
-
|
|
48
|
+
}
|
|
49
|
+
function readPgid(pid) {
|
|
50
|
+
try {
|
|
51
|
+
const result = spawnSync("ps", ["-o", "pgid=", "-p", String(pid)], {
|
|
52
|
+
encoding: "utf8",
|
|
53
|
+
timeout: PS_TIMEOUT_MS
|
|
54
|
+
});
|
|
55
|
+
if (result.error || result.status !== 0 || !result.stdout) return null;
|
|
56
|
+
const pgid = Number.parseInt(result.stdout.trim(), 10);
|
|
57
|
+
return Number.isInteger(pgid) && pgid > 0 ? pgid : null;
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
38
61
|
}
|
|
39
62
|
function killProcessTreeWindows(pid, onFallback) {
|
|
40
63
|
try {
|
|
@@ -50,7 +73,7 @@ function killProcessTreeWindows(pid, onFallback) {
|
|
|
50
73
|
function killDescendantsRecursive(pid, onFallback) {
|
|
51
74
|
let stdout;
|
|
52
75
|
try {
|
|
53
|
-
const result = spawnSync("pgrep", ["-P", String(pid)], { encoding: "utf8" });
|
|
76
|
+
const result = spawnSync("pgrep", ["-P", String(pid)], { encoding: "utf8", timeout: PS_TIMEOUT_MS });
|
|
54
77
|
if (result.error || result.status !== 0 || !result.stdout) return;
|
|
55
78
|
stdout = result.stdout;
|
|
56
79
|
} catch (err) {
|
|
@@ -116,8 +139,8 @@ function readRegistry(registryPath, onLog) {
|
|
|
116
139
|
try {
|
|
117
140
|
raw = readFileSync(registryPath, "utf8");
|
|
118
141
|
} catch (err) {
|
|
119
|
-
onLog?.("warn", "registry read failed
|
|
120
|
-
|
|
142
|
+
onLog?.("warn", "registry read failed; propagating to caller (not treated as empty)", { path: registryPath, err });
|
|
143
|
+
throw err;
|
|
121
144
|
}
|
|
122
145
|
const parsed = parseRegistryContent(raw);
|
|
123
146
|
if (parsed === void 0) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// src/extensions/scheduler/types.ts
|
|
2
|
+
var SCHEDULER_STORE_SCHEMA_VERSION = 2;
|
|
3
|
+
function isSchedulerStoreFile(data) {
|
|
4
|
+
if (!data || typeof data !== "object") return false;
|
|
5
|
+
const record = data;
|
|
6
|
+
if (record.schemaVersion !== SCHEDULER_STORE_SCHEMA_VERSION) return false;
|
|
7
|
+
if (!Array.isArray(record.tasks)) return false;
|
|
8
|
+
return record.tasks.every((t) => t !== null && typeof t === "object");
|
|
9
|
+
}
|
|
10
|
+
var HISTORY_LIMIT = 20;
|
|
11
|
+
function appendExecutionRecord(task, at, status) {
|
|
12
|
+
task.history.push({ at, status });
|
|
13
|
+
if (task.history.length > HISTORY_LIMIT) task.history.shift();
|
|
14
|
+
}
|
|
15
|
+
var SCHEDULER_MODAL_VIEW_ID = "modal-scheduler-manager-scheduler-manager.panel";
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
SCHEDULER_STORE_SCHEMA_VERSION,
|
|
19
|
+
isSchedulerStoreFile,
|
|
20
|
+
HISTORY_LIMIT,
|
|
21
|
+
appendExecutionRecord,
|
|
22
|
+
SCHEDULER_MODAL_VIEW_ID
|
|
23
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { S as ScheduleSpec, T as TaskKind } from './types-DcAmuFZ2.js';
|
|
2
|
+
export { E as ExecutionRecord, H as HISTORY_LIMIT, a as SCHEDULER_MODAL_VIEW_ID, b as SCHEDULER_STORE_SCHEMA_VERSION, c as ScheduledTask, d as SchedulerStoreFile, e as TaskStatus, f as appendExecutionRecord, i as isSchedulerStoreFile } from './types-DcAmuFZ2.js';
|
|
1
3
|
export { b as BACKGROUND_TASK_ID_PREFIX, c as BACKGROUND_TASK_REGISTRY_FILENAME, d as BACKGROUND_TASK_REGISTRY_VERSION, e as BASE_TOOL_ENHANCE_DIRNAME, f as BackgroundTaskEndReason, a as BackgroundTaskRegistryEntry, g as BackgroundTaskRegistryFile, B as BackgroundTaskState, M as MAX_TERMINAL_REGISTRY_ENTRIES, i as isActiveBackgroundTaskState, h as isBackgroundTaskRegistryEntry, j as isTerminalBackgroundTaskState } from './background-task-B53rQ-4Q.js';
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -98,6 +100,12 @@ interface GuiComponentProps {
|
|
|
98
100
|
commandId?: string;
|
|
99
101
|
/** 只许标量:args 原样传给插件 handler,禁嵌套对象防无 schema 结构化旁路(设计 AP-3 校验) */
|
|
100
102
|
args?: Record<string, string | number | boolean>;
|
|
103
|
+
/**
|
|
104
|
+
* 宿主容器导航(容器扩展点统一 §6.4 导航原语):声明后点击请求宿主打开
|
|
105
|
+
* 对应容器(浮层/抽屉)的 tabId 标签页,不经命令链;与 commandId 互为两条
|
|
106
|
+
* 执行路径(commandId 优先走既有命令链,宿主执行器裁决)。
|
|
107
|
+
*/
|
|
108
|
+
hostAction?: HostContainerAction;
|
|
101
109
|
}[];
|
|
102
110
|
};
|
|
103
111
|
/**
|
|
@@ -180,8 +188,35 @@ interface TreeItem {
|
|
|
180
188
|
status?: 'running' | 'done' | 'failed';
|
|
181
189
|
depth?: number;
|
|
182
190
|
children?: TreeItem[];
|
|
191
|
+
/**
|
|
192
|
+
* 行点击动作(容器扩展点统一 §6.4 导航原语;此前 TreeItem 完全不可点)。
|
|
193
|
+
* commandId 走既有命令链,hostAction 走宿主容器导航执行器——两条路径汇入
|
|
194
|
+
* 同一个执行器,缺省 = 纯展示行(维持旧行为)。
|
|
195
|
+
*/
|
|
196
|
+
action?: {
|
|
197
|
+
/** 命令链命令 id(无参形态执行;带参命令用 hostAction.params 语义或后续按需扩展) */
|
|
198
|
+
commandId?: string;
|
|
199
|
+
/** 宿主容器导航(打开浮层/抽屉的某个标签页) */
|
|
200
|
+
hostAction?: HostContainerAction;
|
|
201
|
+
};
|
|
183
202
|
}
|
|
184
203
|
type TreeItemIcon = 'arrow' | 'check' | 'cross' | 'circle' | 'dot' | 'pause' | 'branch';
|
|
204
|
+
/**
|
|
205
|
+
* 宿主容器导航声明(§6.4 导航原语的公共形状):请求宿主打开某个容器的某个标签页。
|
|
206
|
+
* 插件不接触宿主内部状态,只表达「打开哪个容器的哪个 tab、带什么参数」。
|
|
207
|
+
*/
|
|
208
|
+
interface HostContainerAction {
|
|
209
|
+
/** 容器位置族:overlay = workflow-viz 浮层,drawer = 右抽屉 */
|
|
210
|
+
kind: 'overlay' | 'drawer';
|
|
211
|
+
/** 目标标签页 id(全局 id 空间解析,须带插件命名空间前缀);目标不存在时点击无动作 + 一行 warning */
|
|
212
|
+
tabId: string;
|
|
213
|
+
/**
|
|
214
|
+
* 透传给目标容器选项字段的参数:键 = 目标容器选项的字段名(显式登记为对外契约),
|
|
215
|
+
* 值只许标量——禁嵌套对象防无 schema 结构化旁路(与 action-bar args 同一校验口径);
|
|
216
|
+
* 未知键被忽略 + 一行 warning,不阻断动作。会话上下文由宿主注入(插件不传)。
|
|
217
|
+
*/
|
|
218
|
+
params?: Record<string, string | number | boolean>;
|
|
219
|
+
}
|
|
185
220
|
/**
|
|
186
221
|
* 计划产物文档元数据——agent 调 register-doc 登记的一份产物。
|
|
187
222
|
* version 由 extension 侧维护:修订后重写文档须重调 register-doc(version+1),
|
|
@@ -312,7 +347,8 @@ interface GuiContext {
|
|
|
312
347
|
declare function isGuiCapable(ctx: GuiContext): boolean;
|
|
313
348
|
/**
|
|
314
349
|
* 构造 GuiRenderResult。当前主用途是 guiSetWidget 的载荷(composer 任务托盘的协议 widget 区);
|
|
315
|
-
* details.__gui__(tool result
|
|
350
|
+
* details.__gui__(tool result 通道)为 workflow / subagent_batch 等工具的在产通道(经
|
|
351
|
+
* subagent-workflow 侧 tool/tool-shared.ts 的 withGuiAttach 单点构造,RPC 模式门控)。
|
|
316
352
|
* stripUndefined 确保序列化不含 undefined(JSON.stringify 会丢弃 undefined 字段)。
|
|
317
353
|
*/
|
|
318
354
|
declare function guiResult(component: GuiComponent, meta?: WidgetMeta): GuiRenderResult;
|
|
@@ -597,164 +633,6 @@ declare function getAskUserOther(answers: AskUserAnswers, question: AskUserQuest
|
|
|
597
633
|
*/
|
|
598
634
|
declare function isAskUserQuestion(value: unknown): value is AskUserQuestion;
|
|
599
635
|
|
|
600
|
-
type ScheduleSpec = {
|
|
601
|
-
mode: 'cron';
|
|
602
|
-
cronExpression: string;
|
|
603
|
-
} | {
|
|
604
|
-
mode: 'interval';
|
|
605
|
-
intervalMs: number;
|
|
606
|
-
};
|
|
607
|
-
type TaskKind = 'once' | 'recurring';
|
|
608
|
-
type TaskStatus = 'success' | 'failed';
|
|
609
|
-
interface ScheduledTask {
|
|
610
|
-
id: string;
|
|
611
|
-
name: string;
|
|
612
|
-
prompt: string;
|
|
613
|
-
kind: TaskKind;
|
|
614
|
-
schedule: ScheduleSpec;
|
|
615
|
-
model?: string;
|
|
616
|
-
enabled: boolean;
|
|
617
|
-
createdAt: number;
|
|
618
|
-
nextRunAt: number;
|
|
619
|
-
expiresAt?: number;
|
|
620
|
-
runCount: number;
|
|
621
|
-
lastRunAt?: number;
|
|
622
|
-
lastStatus?: TaskStatus;
|
|
623
|
-
lastError?: string;
|
|
624
|
-
history: ExecutionRecord[];
|
|
625
|
-
ownerSessionFile?: string;
|
|
626
|
-
pending?: boolean;
|
|
627
|
-
}
|
|
628
|
-
interface ExecutionRecord {
|
|
629
|
-
at: number;
|
|
630
|
-
status: TaskStatus;
|
|
631
|
-
}
|
|
632
|
-
/**
|
|
633
|
-
* history 裁剪上限(ext-simplify-08 L5 单点):runtime 侧 dispatch 累积
|
|
634
|
-
* (onDispatchSuccess)与 replay 侧 advance 折叠共用同一上限——
|
|
635
|
-
* 双处各持字面量会漂移(曾 runtime.ts / replay.ts 各写一份 20),收敛到本文件单点导出。
|
|
636
|
-
*/
|
|
637
|
-
declare const HISTORY_LIMIT = 20;
|
|
638
|
-
/**
|
|
639
|
-
* 追加执行记录并按 HISTORY_LIMIT 裁剪(ext-simplify-17 B4 单点):runtime 侧 dispatch
|
|
640
|
-
* 累积与 replay 侧 advance 折叠共用的 push+trim——三处内联时与上限字面量同源存在
|
|
641
|
-
* 「漏改一处」漂移风险,随上限一并收敛。
|
|
642
|
-
*/
|
|
643
|
-
declare function appendExecutionRecord(task: ScheduledTask, at: number, status: TaskStatus): void;
|
|
644
|
-
/**
|
|
645
|
-
* pi.appendEntry 写入的 custom entry customType 标识(ext-simplify-17 B1 单点):
|
|
646
|
-
* backend.appendEntry 写入 / replay 折叠过滤 / importer 旧 store 导入三处共用——
|
|
647
|
-
* 各持字面量时「改一处漏两处」会使写入与重放识别脱节(写入新标识、重放不认,任务静默丢失)。
|
|
648
|
-
*/
|
|
649
|
-
declare const TASK_ENTRY_TYPE = "pi-scheduler:task";
|
|
650
|
-
/**
|
|
651
|
-
* upsert op 携带的全量任务快照(不含 ownerSessionFile / pending)。
|
|
652
|
-
* ownerSessionFile 放在 op 顶层(见 SchedulerEntryOp.upsert),
|
|
653
|
-
* pending 是运行时标记不持久化。其余字段与 ScheduledTask 对齐。
|
|
654
|
-
*/
|
|
655
|
-
interface TaskSnapshot {
|
|
656
|
-
id: string;
|
|
657
|
-
name: string;
|
|
658
|
-
prompt: string;
|
|
659
|
-
kind: TaskKind;
|
|
660
|
-
schedule: ScheduleSpec;
|
|
661
|
-
model?: string;
|
|
662
|
-
enabled: boolean;
|
|
663
|
-
createdAt: number;
|
|
664
|
-
nextRunAt: number;
|
|
665
|
-
expiresAt?: number;
|
|
666
|
-
runCount: number;
|
|
667
|
-
lastRunAt?: number;
|
|
668
|
-
lastStatus?: TaskStatus;
|
|
669
|
-
lastError?: string;
|
|
670
|
-
history: ExecutionRecord[];
|
|
671
|
-
}
|
|
672
|
-
/**
|
|
673
|
-
* pi.appendEntry(TASK_ENTRY_TYPE, SchedulerEntryOp) 写入 session JSONL 的 op 联合类型。
|
|
674
|
-
*
|
|
675
|
-
* advance.status 固定为 'success'(CL8):对齐现有 TaskStatus='success'|'failed',
|
|
676
|
-
* 而非继承的 DM-SCHEDULER-OP 词表 'ok'。唯一 emit 值——按 CL7,advance 仅在 dispatch
|
|
677
|
-
* 成功(nextRunAt 实际推进)时 append,失败 dispatch 不 append(transient 失败重试语义)。
|
|
678
|
-
* fold 的 `task.lastStatus = entry.status` 直接赋值合法('success' 属 TaskStatus)。
|
|
679
|
-
*/
|
|
680
|
-
type SchedulerEntryOp = {
|
|
681
|
-
op: 'upsert';
|
|
682
|
-
taskId: string;
|
|
683
|
-
ownerSessionFile: string;
|
|
684
|
-
task: TaskSnapshot;
|
|
685
|
-
} | {
|
|
686
|
-
op: 'advance';
|
|
687
|
-
taskId: string;
|
|
688
|
-
nextRunAt: number;
|
|
689
|
-
at: number;
|
|
690
|
-
status: 'success';
|
|
691
|
-
} | {
|
|
692
|
-
op: 'toggle';
|
|
693
|
-
taskId: string;
|
|
694
|
-
enabled: boolean;
|
|
695
|
-
nextRunAt?: number;
|
|
696
|
-
} | {
|
|
697
|
-
op: 'delete';
|
|
698
|
-
taskId: string;
|
|
699
|
-
};
|
|
700
|
-
/**
|
|
701
|
-
* TaskSnapshot → ScheduledTask(replay 侧重建):pending 不持久化故不恢复,
|
|
702
|
-
* ownerSessionFile 由调用方(replay applyUpsert)从 op 顶层补回。history 逐项深拷贝——
|
|
703
|
-
* 避免恢复出的运行时 task 与快照共享数组/元素引用(task 后续被 mutate 时不污染快照)。
|
|
704
|
-
*/
|
|
705
|
-
declare function snapshotToTask(snapshot: TaskSnapshot): ScheduledTask;
|
|
706
|
-
/**
|
|
707
|
-
* scheduler-manager 插件推树的 overlay 定时任务 tab ViewHost 分区键(历史命名
|
|
708
|
-
* modal-<pluginId>-<modalId>,modal 链退役后原值保留):插件 views.update 推树与
|
|
709
|
-
* taiji renderer ViewHost 消费跨包共用本单源——双端各持字面量时任一端漂移即
|
|
710
|
-
* ViewHost per-session 分区静默 miss(tab 空白无报错),收敛到本文件单点导出。
|
|
711
|
-
*/
|
|
712
|
-
declare const SCHEDULER_MODAL_VIEW_ID = "modal-scheduler-manager-scheduler-manager.panel";
|
|
713
|
-
|
|
714
|
-
/**
|
|
715
|
-
* 折叠期降级日志注入点:包内不持有任何 logger 依赖(零 pi 依赖收敛),由调用方注入。
|
|
716
|
-
* 省略时降级路径静默(折叠行为不受影响——返回值与注入无关)。
|
|
717
|
-
*/
|
|
718
|
-
interface ReplayFoldOptions {
|
|
719
|
-
warn?: (message: string, context?: unknown) => void;
|
|
720
|
-
}
|
|
721
|
-
/**
|
|
722
|
-
* CustomEntry 的最小可识别形状(duck-typed,不依赖 @earendil-works/pi-coding-agent 的
|
|
723
|
-
* 具体 SessionEntry 类型)。与 pending-notifications/state.ts、goal/ports.ts 同款:
|
|
724
|
-
* 纯函数可独立单测,调用方传入真实 SessionEntry[] 时按结构兼容。
|
|
725
|
-
*/
|
|
726
|
-
interface SchedulerEntryLike {
|
|
727
|
-
type: string;
|
|
728
|
-
customType?: string;
|
|
729
|
-
data?: unknown;
|
|
730
|
-
}
|
|
731
|
-
/**
|
|
732
|
-
* 从 CustomEntry 序列折叠恢复 per-task 末态(event sourcing)。
|
|
733
|
-
*
|
|
734
|
-
* 两类调用方:
|
|
735
|
-
* - pi 扩展 session_start 重放:PiSchedulerBackend.loadTasks() 委托本函数,把当前 session 的
|
|
736
|
-
* TASK_ENTRY_TYPE custom entries 折叠成运行时任务 Map。
|
|
737
|
-
* - taiji plugin 条目镜像:对 readEntries 拉取的累计 entries 调用本函数折叠出任务表
|
|
738
|
-
* (与扩展同一实现,插件与扩展折叠结果构造性一致)。
|
|
739
|
-
*
|
|
740
|
-
* 两步:
|
|
741
|
-
* ① 全量折叠(foldEntries)——遍历 customType===TASK_ENTRY_TYPE 的 entries,按 op 类型折叠:
|
|
742
|
-
* - upsert → 用快照重建任务(ownerSessionFile 来自 op 顶层)
|
|
743
|
-
* - advance → nextRunAt=新值 / lastRunAt=at / runCount++ / history.push(裁20) / lastStatus=status(gap2)
|
|
744
|
-
* - toggle → enabled=新值
|
|
745
|
-
* - delete → 从 Map 移除(后续同 taskId 的 advance/toggle 对不存在 taskId 为 no-op,安全)
|
|
746
|
-
* advance/toggle/delete 对不存在的 taskId 为 no-op,因此 fork 继承的完整序列必须先全量折叠,
|
|
747
|
-
* 不能在处理 upsert 时按 owner 跳过(否则后续无 owner 字段的 op 会作用于「不存在的 taskId」丢失)。
|
|
748
|
-
* ② 折叠完成后按 task.ownerSessionFile !== currentSessionFile 整体过滤(gap1,filterNonOwnerTasks):
|
|
749
|
-
* 移除 fork 继承的非 owner 任务——fork 出的 session B 继承了 owner=A 的 upsert+advance 序列,
|
|
750
|
-
* 但 B 不应拥有 A 的任务,重放后整体过滤掉。
|
|
751
|
-
*
|
|
752
|
-
* append-only 时序保证 nextRunAt 不回退(D1):advance entry 按写入顺序折叠,自然取到最后推进值。
|
|
753
|
-
*
|
|
754
|
-
* getEntries 解析/迭代异常 → options.warn + 返回空 Map(gap4):降级为无任务而非崩溃调用方。
|
|
755
|
-
*/
|
|
756
|
-
declare function replayFoldEntries(entries: Iterable<SchedulerEntryLike>, currentSessionFile: string | undefined, options?: ReplayFoldOptions): Map<string, ScheduledTask>;
|
|
757
|
-
|
|
758
636
|
declare const MS_PER_DAY = 86400000;
|
|
759
637
|
declare const MS_PER_HOUR = 3600000;
|
|
760
638
|
declare const MS_PER_MINUTE = 60000;
|
|
@@ -1699,6 +1577,34 @@ declare const SUBAGENT_BG_NOTIFY_CUSTOM_TYPE: "subagent-bg-notify";
|
|
|
1699
1577
|
* 上下文 + renderer 定向气泡)。shared parseSubagentDirective / runtime display 覆写消费。 */
|
|
1700
1578
|
declare const SUBAGENT_DIRECTIVE_CUSTOM_TYPE: "subagent-directive";
|
|
1701
1579
|
|
|
1580
|
+
/**
|
|
1581
|
+
* smart-context 扩展通知通道的 custom_message customType 单源(subagent-notify 词表同款跨层契约)。
|
|
1582
|
+
*
|
|
1583
|
+
* 契约两端(值逐字节依赖,改名 = 跨侧破坏性变更):
|
|
1584
|
+
* - 写侧:@zhushanwen/pi-smart-context extension(阈值/模型/压缩结果可见通知 +
|
|
1585
|
+
* 后台压缩落盘信号,均经 pi.sendMessage 落 custom_message entry 进 session JSONL);
|
|
1586
|
+
* - 读侧:taiji runtime event-interpreter(落盘信号分诊:customType + details.source
|
|
1587
|
+
* 双判据,命中后合成分界行帧)。
|
|
1588
|
+
*
|
|
1589
|
+
* 历史上 customType 常量只在扩展本地定义(runtime 分诊引入前无跨层消费者),收敛本单源
|
|
1590
|
+
* 后两侧 import 同一常量,值漂移结构性不可能(扩展侧 re-export 保持既有 import 路径不变)。
|
|
1591
|
+
*/
|
|
1592
|
+
/** smart-context 通知的送达 customType(对话流可见性由 display 字段区分,非 customType)。 */
|
|
1593
|
+
declare const SMART_CONTEXT_NOTICE_CUSTOM_TYPE: "smart-context";
|
|
1594
|
+
/** 后台压缩落盘信号的 details.source 取值:compaction entry 已 append 进 session 文件。 */
|
|
1595
|
+
declare const COMPACTION_ENTRY_APPENDED_SOURCE: "compaction-entry-appended";
|
|
1596
|
+
/** 落盘信号的判读结果(compactionId 缺失时 undefined,消费方走尾部兜底取最近 compaction entry)。 */
|
|
1597
|
+
interface CompactionPersistSignal {
|
|
1598
|
+
compactionId?: string;
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* 防御性解析后台压缩落盘信号的 details(runtime 分诊判据,parseBgNotifyDetails 同款范式)。
|
|
1602
|
+
*
|
|
1603
|
+
* 命中 = source 为落盘信号词(customType 判定在消费方先行,本函数只裁 details)。
|
|
1604
|
+
* 非对象形态 / source 不符 → null(非目标信号);compactionId 非串 → undefined(降级尾部兜底)。
|
|
1605
|
+
*/
|
|
1606
|
+
declare function parseCompactionPersistSignal(details: unknown): CompactionPersistSignal | null;
|
|
1607
|
+
|
|
1702
1608
|
/**
|
|
1703
1609
|
* 子代理引擎契约(U7 引擎可发现性 + GUI 引擎选择)。
|
|
1704
1610
|
*
|
|
@@ -1751,6 +1657,57 @@ interface SubagentEngineConfigView {
|
|
|
1751
1657
|
engineDefaults?: Record<string, string>;
|
|
1752
1658
|
}
|
|
1753
1659
|
|
|
1660
|
+
/**
|
|
1661
|
+
* subagent 投影判据(占用谓词 / 失败轮 / 失败分类展示词)——插件侧托盘 widget 与
|
|
1662
|
+
* 宿主 renderer 的共享判据单点(dmg-r1-4 判据下沉:此前两侧手抄镜像、靠「两侧单测
|
|
1663
|
+
* 各自锚定」的人工纪律同步,无对拍)。
|
|
1664
|
+
*
|
|
1665
|
+
* 契约两端(同判据同实现,结构性不漂移):
|
|
1666
|
+
* - 插件侧:@zhushanwen/pi-subagent-workflow interface/gui/tray-widget.ts(托盘
|
|
1667
|
+
* widget 树的行状态映射 / 分桶 / meta 状态点);
|
|
1668
|
+
* - 宿主侧:packages/renderer lib/subagent-bucket.ts(badge 计数 / streaming 判定
|
|
1669
|
+
* / SubagentTab 失败展示),经 re-export 同源消费。
|
|
1670
|
+
*
|
|
1671
|
+
* 入参类型 = 两侧 SubagentRecord 的共持形状子集(@taiji/shared 与
|
|
1672
|
+
* @zhushanwen/subagent-core 各持一份 record 类型,本包零运行时依赖、不 import 任何
|
|
1673
|
+
* 一侧词表);status/stopReason 词表纪律(扩值评估、编译锁)仍归两侧 record 类型
|
|
1674
|
+
* 所在包,本模块只锚判据逻辑本体。
|
|
1675
|
+
*/
|
|
1676
|
+
/**
|
|
1677
|
+
* 失败分类的最小消费形状(两侧 FailureCause 的共持子集):判据只读 class/kind 二
|
|
1678
|
+
* 字段。宽入参是刻意的——sdk 判别联合(class×kind 矩阵)对两侧各自持有,本包不
|
|
1679
|
+
* 复制词表防漂移。
|
|
1680
|
+
*/
|
|
1681
|
+
interface SubagentFailureLike {
|
|
1682
|
+
class: string;
|
|
1683
|
+
kind: string;
|
|
1684
|
+
}
|
|
1685
|
+
/** 三判据消费的最小字段面(两侧 SubagentRecord 形状子集;字段语义见判据注释)。 */
|
|
1686
|
+
interface SubagentProjectionRecord {
|
|
1687
|
+
status: string;
|
|
1688
|
+
stopReason?: string;
|
|
1689
|
+
lastRound?: {
|
|
1690
|
+
failure?: SubagentFailureLike;
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
/**
|
|
1694
|
+
* 占用谓词(「正在跑」严格口径):running 且无轮收条停因(「轮收条已写 = 本轮不在途」
|
|
1695
|
+
* 的等价信号——轮始清点族在翻 running 时清 stopReason,markRoundIdle 轮终写入停因,
|
|
1696
|
+
* 信号由 stopReason 字段承载)。带停因的 running(轮收条残留)不算占用。
|
|
1697
|
+
*/
|
|
1698
|
+
declare function isRunningProjection(record: SubagentProjectionRecord): boolean;
|
|
1699
|
+
/**
|
|
1700
|
+
* 失败轮判据:lastRound.failure 存在且非 user.cancelled(FailureCause 存在即失败轮;
|
|
1701
|
+
* user.cancelled 是用户主动中止非失败)。返回该 lastRound 供展示消费(泛型保留两侧
|
|
1702
|
+
* 各自的 lastRound 载荷类型);非失败轮返回 undefined。
|
|
1703
|
+
*/
|
|
1704
|
+
declare function failedRoundOf<T extends SubagentProjectionRecord>(record: T): T['lastRound'] | undefined;
|
|
1705
|
+
/**
|
|
1706
|
+
* 失败分类展示词:`"<class>.<kind>"` 点拼(与 run 终局 failedMembers[].cause 的
|
|
1707
|
+
* 序列化形态同形)。
|
|
1708
|
+
*/
|
|
1709
|
+
declare function failureCauseLabel(failure: SubagentFailureLike): string;
|
|
1710
|
+
|
|
1754
1711
|
/**
|
|
1755
1712
|
* problem-solve-canvas 契约模块 —— marker 与 action 词表(U1 SSOT)。
|
|
1756
1713
|
*
|
|
@@ -2602,4 +2559,47 @@ type ResolvedSettlementReason = SettlementReason | 'unknown';
|
|
|
2602
2559
|
*/
|
|
2603
2560
|
declare function resolveSettlementReason(reason: string): ResolvedSettlementReason;
|
|
2604
2561
|
|
|
2605
|
-
|
|
2562
|
+
/**
|
|
2563
|
+
* base-tool-enhance 空闲转后台阈值(idleBackgroundSec)跨层契约(跨层 SSOT)。
|
|
2564
|
+
*
|
|
2565
|
+
* 契约两端:
|
|
2566
|
+
* - 配置权威侧:@zhushanwen/pi-base-tool-enhance extension(config.ts
|
|
2567
|
+
* loadBaseToolEnhanceConfig;键级 warn/文案属扩展配置系统 UX,留扩展侧——
|
|
2568
|
+
* 本文件只承载值语义,anomaly 字段供扩展侧生成 warn)。
|
|
2569
|
+
* - 消费侧 1:包装脚本 --idle 参数(bash 文本协议,只接受非负整数秒——小数放行会
|
|
2570
|
+
* 使配置层值空间与执行层可接受值空间出现差集,差集配置下全部前台命令 exit 64,
|
|
2571
|
+
* 故归一化收敛为整数且 floor 后 <1 抬到 1:--idle 0 语义是「首轮监控即转后台」,
|
|
2572
|
+
* 不是亚秒阈值的合理近似)。
|
|
2573
|
+
* - 消费侧 2:taiji runtime 静默判定矩阵 T 公式(T = idleBackgroundSec + 300s,
|
|
2574
|
+
* packages/runtime event-interpreter-silence)——runtime 侧直 import 本实现,
|
|
2575
|
+
* 禁止镜像副本(历史事故:两侧手抄归一化无机器绑定,超大值 clamp 语义漂移)。
|
|
2576
|
+
*
|
|
2577
|
+
* 显式 null 与 undefined 分流的理由:undefined = 未配置(默认开启 600);显式 null =
|
|
2578
|
+
* 用户关闭(透传 null,下游据此回落 pi 官方原生无限等待);坏值 = 回退默认而非关闭
|
|
2579
|
+
* (显式 null 是唯一关闭通道,坏值不得意外关闭机制)。
|
|
2580
|
+
*/
|
|
2581
|
+
/**
|
|
2582
|
+
* idleBackgroundSec 默认值(秒):正常工具调用分钟级、编译静默常见 1-5 分钟,10 分钟
|
|
2583
|
+
* 输出静默在交互会话已是强异常信号。
|
|
2584
|
+
*/
|
|
2585
|
+
declare const DEFAULT_IDLE_BACKGROUND_SEC = 600;
|
|
2586
|
+
/**
|
|
2587
|
+
* 归一化结果:value 恒为可直接消费的生效值(合法输入 = 非负整数秒;显式 null = null;
|
|
2588
|
+
* undefined/坏值 = 默认 600);anomaly 非空表示输入异常(调用方据此落 warn,不据此
|
|
2589
|
+
* 改值——value 已是回退/截断后的生效值)。
|
|
2590
|
+
* - 'invalid':类型错/负数/0/非有限 → 回退默认(value = DEFAULT_IDLE_BACKGROUND_SEC)
|
|
2591
|
+
* - 'clamped':换算毫秒超 int32 上限 → 截断(value = floor(int32 ms 对应秒数))
|
|
2592
|
+
*/
|
|
2593
|
+
interface IdleBackgroundSecNormalization {
|
|
2594
|
+
value: number | null;
|
|
2595
|
+
anomaly: 'invalid' | 'clamped' | null;
|
|
2596
|
+
}
|
|
2597
|
+
/**
|
|
2598
|
+
* idleBackgroundSec 归一化(跨层唯一实现,禁镜像):
|
|
2599
|
+
* undefined → 默认 600(未配置);null → null(显式关闭);正有限数 → floor ≥ 1 的
|
|
2600
|
+
* 整数秒(换算毫秒超 int32 上限先截断,anomaly = 'clamped');其余坏值 → 默认 600
|
|
2601
|
+
* (anomaly = 'invalid')。
|
|
2602
|
+
*/
|
|
2603
|
+
declare function normalizeIdleBackgroundSec(raw: unknown): IdleBackgroundSecNormalization;
|
|
2604
|
+
|
|
2605
|
+
export { ACTION_EVENT_MAP, ASK_USER_MARKER, type ArgumentMateriality, type ArgumentPolarity, type AskUserAnswers, type AskUserOption, type AskUserQuestion, CANVAS_ACTIONS, CANVAS_ACTOR_MATRIX, CANVAS_ERROR_CODES, CANVAS_ERROR_RECOVERY, CANVAS_EVENT_TYPES, CANVAS_MARKER, CANVAS_MARKER_ACTIONS, CANVAS_TOOL_NAMES, COMPACTION_ENTRY_APPENDED_SOURCE, type CanvasAction, type CanvasActionParams, type CanvasActor, type CanvasComment, type CanvasCommentParams, type CanvasCommentRespondParams, type CanvasConvergeCheckParams, type CanvasCreateParams, type CanvasDecideDirectionParams, type CanvasDirectionDecision, type CanvasDispatchParams, type CanvasEdge, type CanvasEdgeType, type CanvasEnterParams, type CanvasError, type CanvasErrorCode, type CanvasEventType, type CanvasExportParams, type CanvasExportTarget, type CanvasGrowItem, type CanvasGrowParams, type CanvasListEntry, type CanvasListParams, type CanvasListReply, type CanvasMarkerAction, type CanvasMarkerParams, type CanvasMarkerRequest, type CanvasMutateReply, type CanvasMutateRequest, type CanvasNode, type CanvasNodeLifecycle, type CanvasNodeType, type CanvasPatchParams, type CanvasReadParams, type CanvasReadReply, type CanvasReviewAppendParams, type CanvasReviewEntryInput, type CanvasReviewRequestParams, type CanvasReviewResolveParams, type CanvasSelectParams, type CanvasState, type CanvasToolName, type CanvasTransitionParams, type ChannelErrorResult, type ChoiceQuestion, type CollectPendingIdsOptions, type CompactionPersistSignal, DEFAULT_IDLE_BACKGROUND_SEC, type DualWidgetContent, type FormAnswers, type FormOption, type FormQuestion, GUI_WIDGET_MARKER, type GuiComponent, type GuiComponentProps, type GuiComponentType, type GuiContext, type GuiRenderResult, type HostContainerAction, INFLIGHT_REPORT_ACK, type IdleBackgroundSecNormalization, JOURNAL_REPORT_ACK, MAPPED_PENDING_STATUSES, MS_PER_DAY, MS_PER_HOUR, MS_PER_MINUTE, MS_PER_SECOND, type MappedPendingStatus, type MarkerRpcOptions, type MarkerRpcResult, NON_TOOL_ACTIONS, PENDING_REGISTER_ENTRY_TYPE, PENDING_UNREGISTER_ENTRY_TYPE, PERMISSION_APPROVAL_TITLE, PLAN_LIFECYCLE_EVENTS, PLAN_LIFECYCLE_STATES, PLAN_REVIEW_MARKER, PLAN_SELF_REVIEW_MAX_BYTES, PLAN_STATE_CUSTOM_TYPE, PROTOCOL_VERSION, type PendingEntriesScan, type PendingFieldsScan, type PlanDocMeta, type PlanLifecycleEvent, type PlanLifecycleState, type PlanPhase, type PlanReviewComment, type PlanReviewDecision, type PlanReviewRequest, type PlanReviewResponse, type PlanReviewResponseEnvelope, type PlanTransitionResult, type ResolvedSettlementReason, type ReviewDimension, type ReviewEntry, type ReviewReport, type ReviewReportStatus, type ReviewResolutionDisposition, type ReviewSeverity, SCHEDULE_CREATE_MARKER, SESSION_MANAGER_ACTIONS, SESSION_MANAGER_MARKER, SETTLEMENT_REASONS, SMART_CONTEXT_NOTICE_CUSTOM_TYPE, SUBAGENTS_ENGINES_FILENAME, SUBAGENT_BG_NOTIFY_CUSTOM_TYPE, SUBAGENT_DIRECTIVE_CUSTOM_TYPE, SUBAGENT_INFLIGHT_MARKER, SUBAGENT_JOURNAL_MARKER, type ScheduleDraft, type ScheduleFormResult, type ScheduleKind, type ScheduleQuestion, ScheduleSpec, type SessionManagerAbortParams, type SessionManagerAbortResult, type SessionManagerAction, type SessionManagerArchiveParams, type SessionManagerArchiveResult, type SessionManagerCreateParams, type SessionManagerCreateResult, type SessionManagerDeleteParams, type SessionManagerDeleteResult, type SessionManagerErrorResult, type SessionManagerHistoryParams, type SessionManagerHistoryResult, type SessionManagerListParams, type SessionManagerListResult, type SessionManagerParams, type SessionManagerSendParams, type SessionManagerSendResult, type SessionManagerSessionSummary, type SessionManagerStatusParams, type SessionManagerStatusResult, type SessionManagerWatchParams, type SessionManagerWatchReason, type SessionManagerWatchRespondPayload, type SettlementReason, type StatItem, type SubagentEngineConfigView, type SubagentEngineModelInfo, type SubagentEnginesFile, type SubagentFailureLike, type SubagentInFlightReport, type SubagentJournalDomain, type SubagentJournalEvent, type SubagentJournalReport, type SubagentProjectionRecord, type SupportRating, TOOL_ACTION_MAP, TaskKind, type TextQuestion, type TreeItem, type TreeItemIcon, UI_FORM_MARKER, type UiFormInteractOptions, type UiFormInteractResult, WORKFLOW_RESULT_CUSTOM_TYPE, type WidgetIconPathsRejection, type WidgetIconPathsValidation, type WidgetMeta, allCanvasActions, allToolActions, applyPendingDiff, callMarkerRpc, canvasError, collectActivePendingIds, dateToOnceCron, derivePhase, extractGui, failedRoundOf, failureCauseLabel, firstContentText, firstLine, formatChannelErrorText, formatDuration, formatRelativeTime, formatSchedule, getAskUserAnswer, getAskUserOther, guiComponent, guiResult, guiSetWidget, isActionAllowed, isArgumentMateriality, isArgumentPolarity, isAskUserQuestion, isCanvasCommentParams, isCanvasCommentRespondParams, isCanvasConvergeCheckParams, isCanvasCreateParams, isCanvasDecideDirectionParams, isCanvasDispatchParams, isCanvasEnterParams, isCanvasExportParams, isCanvasGrowParams, isCanvasListParams, isCanvasMarkerRequest, isCanvasPatchParams, isCanvasReadParams, isCanvasReviewAppendParams, isCanvasReviewRequestParams, isCanvasReviewResolveParams, isCanvasSelectParams, isCanvasTransitionParams, isChannelErrorResult, isFormAnswers, isFormQuestion, isGuiCapable, isGuiComponent, isGuiRenderResult, isInFlightReportAck, isJournalReportAck, isPlanReviewRequest, isPlanReviewResponse, isRunningProjection, isScheduleDraft, isScheduleFormResult, isSessionManagerAbortParams, isSessionManagerArchiveParams, isSessionManagerCreateParams, isSessionManagerDeleteParams, isSessionManagerHistoryParams, isSessionManagerListParams, isSessionManagerNotifyId, isSessionManagerSendParams, isSessionManagerStatusParams, isSessionManagerWatchParams, isSessionManagerWatchRespondPayload, isSubagentInFlightReport, isSubagentJournalReport, mapReasonToStatus, normalizeIdleBackgroundSec, onceCronToDate, parseCompactionPersistSignal, parsePlanReviewResponse, readDetailsSummary, readLifecycleState, readResumeHint, resolveSettlementReason, resultContentText, scanPendingEntries, scanPendingFields, setWidgetDual, toolOfAction, transition, truncateSelfReview, uiFormInteract, validateWidgetIconPaths };
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,13 @@ import {
|
|
|
8
8
|
isBackgroundTaskRegistryEntry,
|
|
9
9
|
isTerminalBackgroundTaskState
|
|
10
10
|
} from "./chunk-NJ3E4ISR.js";
|
|
11
|
+
import {
|
|
12
|
+
HISTORY_LIMIT,
|
|
13
|
+
SCHEDULER_MODAL_VIEW_ID,
|
|
14
|
+
SCHEDULER_STORE_SCHEMA_VERSION,
|
|
15
|
+
appendExecutionRecord,
|
|
16
|
+
isSchedulerStoreFile
|
|
17
|
+
} from "./chunk-TFO4NW5X.js";
|
|
11
18
|
|
|
12
19
|
// src/core/types.ts
|
|
13
20
|
var PROTOCOL_VERSION = 1;
|
|
@@ -206,125 +213,6 @@ function isAskUserQuestion(value) {
|
|
|
206
213
|
return typeof q.question === "string" && (q.header === void 0 || typeof q.header === "string") && (q.options === void 0 || Array.isArray(q.options)) && (q.multiSelect === void 0 || typeof q.multiSelect === "boolean") && (q.allowOther === void 0 || typeof q.allowOther === "boolean");
|
|
207
214
|
}
|
|
208
215
|
|
|
209
|
-
// src/extensions/scheduler/types.ts
|
|
210
|
-
var HISTORY_LIMIT = 20;
|
|
211
|
-
function appendExecutionRecord(task, at, status) {
|
|
212
|
-
task.history.push({ at, status });
|
|
213
|
-
if (task.history.length > HISTORY_LIMIT) task.history.shift();
|
|
214
|
-
}
|
|
215
|
-
var TASK_ENTRY_TYPE = "pi-scheduler:task";
|
|
216
|
-
function snapshotToTask(snapshot) {
|
|
217
|
-
return {
|
|
218
|
-
id: snapshot.id,
|
|
219
|
-
name: snapshot.name,
|
|
220
|
-
prompt: snapshot.prompt,
|
|
221
|
-
kind: snapshot.kind,
|
|
222
|
-
schedule: snapshot.schedule,
|
|
223
|
-
model: snapshot.model,
|
|
224
|
-
enabled: snapshot.enabled,
|
|
225
|
-
createdAt: snapshot.createdAt,
|
|
226
|
-
nextRunAt: snapshot.nextRunAt,
|
|
227
|
-
expiresAt: snapshot.expiresAt,
|
|
228
|
-
runCount: snapshot.runCount,
|
|
229
|
-
lastRunAt: snapshot.lastRunAt,
|
|
230
|
-
lastStatus: snapshot.lastStatus,
|
|
231
|
-
lastError: snapshot.lastError,
|
|
232
|
-
history: snapshot.history.map((h) => ({ ...h }))
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
var SCHEDULER_MODAL_VIEW_ID = "modal-scheduler-manager-scheduler-manager.panel";
|
|
236
|
-
|
|
237
|
-
// src/extensions/scheduler/replay.ts
|
|
238
|
-
function toErrorMessage(e) {
|
|
239
|
-
return e instanceof Error ? e.message : String(e);
|
|
240
|
-
}
|
|
241
|
-
function replayFoldEntries(entries, currentSessionFile, options) {
|
|
242
|
-
try {
|
|
243
|
-
const tasks = /* @__PURE__ */ new Map();
|
|
244
|
-
foldEntries(entries, tasks, options);
|
|
245
|
-
filterNonOwnerTasks(tasks, currentSessionFile);
|
|
246
|
-
return tasks;
|
|
247
|
-
} catch (err) {
|
|
248
|
-
options?.warn?.("replayFoldEntries failed", { error: toErrorMessage(err) });
|
|
249
|
-
return /* @__PURE__ */ new Map();
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
function foldEntries(entries, tasks, options) {
|
|
253
|
-
for (const entry of entries) {
|
|
254
|
-
if (entry.type !== "custom" || entry.customType !== TASK_ENTRY_TYPE) continue;
|
|
255
|
-
if (!isSchedulerEntryOp(entry.data)) continue;
|
|
256
|
-
const op = entry.data;
|
|
257
|
-
try {
|
|
258
|
-
applyOp(tasks, op);
|
|
259
|
-
} catch (err) {
|
|
260
|
-
options?.warn?.("skipping corrupted scheduler entry", { error: toErrorMessage(err) });
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
function applyOp(tasks, op) {
|
|
266
|
-
switch (op.op) {
|
|
267
|
-
case "upsert":
|
|
268
|
-
applyUpsert(tasks, op);
|
|
269
|
-
break;
|
|
270
|
-
case "advance":
|
|
271
|
-
applyAdvance(tasks, op);
|
|
272
|
-
break;
|
|
273
|
-
case "toggle":
|
|
274
|
-
applyToggle(tasks, op);
|
|
275
|
-
break;
|
|
276
|
-
case "delete":
|
|
277
|
-
applyDelete(tasks, op);
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
function applyUpsert(tasks, op) {
|
|
282
|
-
tasks.set(op.taskId, { ...snapshotToTask(op.task), ownerSessionFile: op.ownerSessionFile });
|
|
283
|
-
}
|
|
284
|
-
function applyAdvance(tasks, op) {
|
|
285
|
-
const task = tasks.get(op.taskId);
|
|
286
|
-
if (!task) return;
|
|
287
|
-
task.nextRunAt = op.nextRunAt;
|
|
288
|
-
task.lastRunAt = op.at;
|
|
289
|
-
task.runCount += 1;
|
|
290
|
-
appendExecutionRecord(task, op.at, op.status);
|
|
291
|
-
task.lastStatus = op.status;
|
|
292
|
-
}
|
|
293
|
-
function applyToggle(tasks, op) {
|
|
294
|
-
const task = tasks.get(op.taskId);
|
|
295
|
-
if (!task) return;
|
|
296
|
-
task.enabled = op.enabled;
|
|
297
|
-
if (op.nextRunAt !== void 0) task.nextRunAt = op.nextRunAt;
|
|
298
|
-
}
|
|
299
|
-
function applyDelete(tasks, op) {
|
|
300
|
-
tasks.delete(op.taskId);
|
|
301
|
-
}
|
|
302
|
-
function filterNonOwnerTasks(tasks, currentSessionFile) {
|
|
303
|
-
for (const [id, task] of tasks) {
|
|
304
|
-
if (task.ownerSessionFile !== currentSessionFile) {
|
|
305
|
-
tasks.delete(id);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
function isSchedulerEntryOp(data) {
|
|
310
|
-
if (!data || typeof data !== "object") return false;
|
|
311
|
-
const record = data;
|
|
312
|
-
const op = record.op;
|
|
313
|
-
if (op === "upsert") {
|
|
314
|
-
return typeof record.taskId === "string" && typeof record.task === "object" && record.task !== null;
|
|
315
|
-
}
|
|
316
|
-
if (op === "advance") {
|
|
317
|
-
return typeof record.taskId === "string" && typeof record.nextRunAt === "number" && typeof record.at === "number" && record.status === "success";
|
|
318
|
-
}
|
|
319
|
-
if (op === "toggle") {
|
|
320
|
-
return typeof record.taskId === "string" && typeof record.enabled === "boolean" && (record.nextRunAt === void 0 || typeof record.nextRunAt === "number");
|
|
321
|
-
}
|
|
322
|
-
if (op === "delete") {
|
|
323
|
-
return typeof record.taskId === "string";
|
|
324
|
-
}
|
|
325
|
-
return false;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
216
|
// src/extensions/scheduler/format.ts
|
|
329
217
|
var MS_PER_DAY = 864e5;
|
|
330
218
|
var MS_PER_HOUR = 36e5;
|
|
@@ -851,9 +739,33 @@ var WORKFLOW_RESULT_CUSTOM_TYPE = "workflow-result";
|
|
|
851
739
|
var SUBAGENT_BG_NOTIFY_CUSTOM_TYPE = "subagent-bg-notify";
|
|
852
740
|
var SUBAGENT_DIRECTIVE_CUSTOM_TYPE = "subagent-directive";
|
|
853
741
|
|
|
742
|
+
// src/extensions/smart-context/notify-types.ts
|
|
743
|
+
var SMART_CONTEXT_NOTICE_CUSTOM_TYPE = "smart-context";
|
|
744
|
+
var COMPACTION_ENTRY_APPENDED_SOURCE = "compaction-entry-appended";
|
|
745
|
+
function parseCompactionPersistSignal(details) {
|
|
746
|
+
if (!details || typeof details !== "object" || Array.isArray(details)) return null;
|
|
747
|
+
const d = details;
|
|
748
|
+
if (d.source !== COMPACTION_ENTRY_APPENDED_SOURCE) return null;
|
|
749
|
+
return { compactionId: typeof d.compactionId === "string" ? d.compactionId : void 0 };
|
|
750
|
+
}
|
|
751
|
+
|
|
854
752
|
// src/extensions/subagent-engine/contract.ts
|
|
855
753
|
var SUBAGENTS_ENGINES_FILENAME = "engines.json";
|
|
856
754
|
|
|
755
|
+
// src/extensions/subagent-projection/predicates.ts
|
|
756
|
+
function isRunningProjection(record) {
|
|
757
|
+
return record.status === "running" && record.stopReason === void 0;
|
|
758
|
+
}
|
|
759
|
+
function failedRoundOf(record) {
|
|
760
|
+
const failure = record.lastRound?.failure;
|
|
761
|
+
if (failure === void 0) return void 0;
|
|
762
|
+
if (failure.class === "user" && failure.kind === "cancelled") return void 0;
|
|
763
|
+
return record.lastRound;
|
|
764
|
+
}
|
|
765
|
+
function failureCauseLabel(failure) {
|
|
766
|
+
return `${failure.class}.${failure.kind}`;
|
|
767
|
+
}
|
|
768
|
+
|
|
857
769
|
// src/extensions/canvas/marker.ts
|
|
858
770
|
var CANVAS_MARKER = "\0TAIJI_CANVAS";
|
|
859
771
|
var CANVAS_ACTIONS = [
|
|
@@ -1299,6 +1211,23 @@ function resolveSettlementReason(reason) {
|
|
|
1299
1211
|
return SETTLEMENT_REASON_MAP[reason] ?? "unknown";
|
|
1300
1212
|
}
|
|
1301
1213
|
var SETTLEMENT_REASON_KEYS = new Set(Object.keys(SETTLEMENT_REASON_MAP));
|
|
1214
|
+
|
|
1215
|
+
// src/background-idle-config.ts
|
|
1216
|
+
var INT32_MAX_MS = 2147483647;
|
|
1217
|
+
var MS_PER_SECOND2 = 1e3;
|
|
1218
|
+
var DEFAULT_IDLE_BACKGROUND_SEC = 600;
|
|
1219
|
+
function normalizeIdleBackgroundSec(raw) {
|
|
1220
|
+
if (raw === null) return { value: null, anomaly: null };
|
|
1221
|
+
if (typeof raw === "number" && Number.isFinite(raw) && raw > 0) {
|
|
1222
|
+
const ms = raw * MS_PER_SECOND2;
|
|
1223
|
+
if (ms > INT32_MAX_MS) {
|
|
1224
|
+
return { value: Math.floor(INT32_MAX_MS / MS_PER_SECOND2), anomaly: "clamped" };
|
|
1225
|
+
}
|
|
1226
|
+
return { value: Math.max(1, Math.floor(raw)), anomaly: null };
|
|
1227
|
+
}
|
|
1228
|
+
if (raw !== void 0) return { value: DEFAULT_IDLE_BACKGROUND_SEC, anomaly: "invalid" };
|
|
1229
|
+
return { value: DEFAULT_IDLE_BACKGROUND_SEC, anomaly: null };
|
|
1230
|
+
}
|
|
1302
1231
|
export {
|
|
1303
1232
|
ACTION_EVENT_MAP,
|
|
1304
1233
|
ASK_USER_MARKER,
|
|
@@ -1314,6 +1243,8 @@ export {
|
|
|
1314
1243
|
CANVAS_MARKER,
|
|
1315
1244
|
CANVAS_MARKER_ACTIONS,
|
|
1316
1245
|
CANVAS_TOOL_NAMES,
|
|
1246
|
+
COMPACTION_ENTRY_APPENDED_SOURCE,
|
|
1247
|
+
DEFAULT_IDLE_BACKGROUND_SEC,
|
|
1317
1248
|
GUI_WIDGET_MARKER,
|
|
1318
1249
|
HISTORY_LIMIT,
|
|
1319
1250
|
INFLIGHT_REPORT_ACK,
|
|
@@ -1335,16 +1266,17 @@ export {
|
|
|
1335
1266
|
PLAN_STATE_CUSTOM_TYPE,
|
|
1336
1267
|
PROTOCOL_VERSION,
|
|
1337
1268
|
SCHEDULER_MODAL_VIEW_ID,
|
|
1269
|
+
SCHEDULER_STORE_SCHEMA_VERSION,
|
|
1338
1270
|
SCHEDULE_CREATE_MARKER,
|
|
1339
1271
|
SESSION_MANAGER_ACTIONS,
|
|
1340
1272
|
SESSION_MANAGER_MARKER,
|
|
1341
1273
|
SETTLEMENT_REASONS,
|
|
1274
|
+
SMART_CONTEXT_NOTICE_CUSTOM_TYPE,
|
|
1342
1275
|
SUBAGENTS_ENGINES_FILENAME,
|
|
1343
1276
|
SUBAGENT_BG_NOTIFY_CUSTOM_TYPE,
|
|
1344
1277
|
SUBAGENT_DIRECTIVE_CUSTOM_TYPE,
|
|
1345
1278
|
SUBAGENT_INFLIGHT_MARKER,
|
|
1346
1279
|
SUBAGENT_JOURNAL_MARKER,
|
|
1347
|
-
TASK_ENTRY_TYPE,
|
|
1348
1280
|
TOOL_ACTION_MAP,
|
|
1349
1281
|
UI_FORM_MARKER,
|
|
1350
1282
|
WORKFLOW_RESULT_CUSTOM_TYPE,
|
|
@@ -1358,6 +1290,8 @@ export {
|
|
|
1358
1290
|
dateToOnceCron,
|
|
1359
1291
|
derivePhase,
|
|
1360
1292
|
extractGui,
|
|
1293
|
+
failedRoundOf,
|
|
1294
|
+
failureCauseLabel,
|
|
1361
1295
|
firstContentText,
|
|
1362
1296
|
firstLine,
|
|
1363
1297
|
formatChannelErrorText,
|
|
@@ -1403,8 +1337,10 @@ export {
|
|
|
1403
1337
|
isJournalReportAck,
|
|
1404
1338
|
isPlanReviewRequest,
|
|
1405
1339
|
isPlanReviewResponse,
|
|
1340
|
+
isRunningProjection,
|
|
1406
1341
|
isScheduleDraft,
|
|
1407
1342
|
isScheduleFormResult,
|
|
1343
|
+
isSchedulerStoreFile,
|
|
1408
1344
|
isSessionManagerAbortParams,
|
|
1409
1345
|
isSessionManagerArchiveParams,
|
|
1410
1346
|
isSessionManagerCreateParams,
|
|
@@ -1420,18 +1356,18 @@ export {
|
|
|
1420
1356
|
isSubagentJournalReport,
|
|
1421
1357
|
isTerminalBackgroundTaskState,
|
|
1422
1358
|
mapReasonToStatus,
|
|
1359
|
+
normalizeIdleBackgroundSec,
|
|
1423
1360
|
onceCronToDate,
|
|
1361
|
+
parseCompactionPersistSignal,
|
|
1424
1362
|
parsePlanReviewResponse,
|
|
1425
1363
|
readDetailsSummary,
|
|
1426
1364
|
readLifecycleState,
|
|
1427
1365
|
readResumeHint,
|
|
1428
|
-
replayFoldEntries,
|
|
1429
1366
|
resolveSettlementReason,
|
|
1430
1367
|
resultContentText,
|
|
1431
1368
|
scanPendingEntries,
|
|
1432
1369
|
scanPendingFields,
|
|
1433
1370
|
setWidgetDual,
|
|
1434
|
-
snapshotToTask,
|
|
1435
1371
|
toolOfAction,
|
|
1436
1372
|
transition,
|
|
1437
1373
|
truncateSelfReview,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { c as ScheduledTask, d as SchedulerStoreFile } from './types-DcAmuFZ2.js';
|
|
2
|
+
export { b as SCHEDULER_STORE_SCHEMA_VERSION, i as isSchedulerStoreFile } from './types-DcAmuFZ2.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 读结果三分支:
|
|
6
|
+
* - `ok`:解析成功,tasks 为任务集(可为空数组)
|
|
7
|
+
* - `missing`:文件不存在 = 空任务集(**正常路径而非异常**——新 session / fork / 从未建任务)
|
|
8
|
+
* - `corrupt`:文件存在但不可解析 → 已改名 `.corrupt-<ts>` 留证,调用方按空任务集启动
|
|
9
|
+
*/
|
|
10
|
+
type SchedulerStoreRead = {
|
|
11
|
+
kind: 'ok';
|
|
12
|
+
tasks: ScheduledTask[];
|
|
13
|
+
} | {
|
|
14
|
+
kind: 'missing';
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'corrupt';
|
|
17
|
+
renamedTo: string;
|
|
18
|
+
};
|
|
19
|
+
/** 读写选项:降级日志 warn 注入点(包内零 logger 依赖;同构先例 = replay 折叠链的
|
|
20
|
+
* ReplayFoldOptions.warn——该链已随方案 B 落地整体退役,见 commit 73073b169 删除的 replay.ts)。 */
|
|
21
|
+
type SchedulerStoreOptions = {
|
|
22
|
+
warn?: (message: string, context?: unknown) => void;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* 任务库分片文件路径:`<agentDir>/scheduler/<root>/<segments>/<sessionId>.json`。
|
|
26
|
+
*
|
|
27
|
+
* - `agentDir`:pi 的 getAgentDir() 值(扩展侧 pi 官方导出;taiji 面板侧 = shared
|
|
28
|
+
* getPiAgentDir(),同一目录——runtime 注入 pi 进程的 PI_CODING_AGENT_DIR 即它)
|
|
29
|
+
* - `cwd`:owner session 工作目录(workspace 路径隔离键)
|
|
30
|
+
* - `sessionId`:pi SessionManager.getSessionId()(--no-session 等空值由调用方归一,
|
|
31
|
+
* 本函数对空/非法值 fail-fast——静默拼出错误路径会让任务「写到别处」不可见)
|
|
32
|
+
*
|
|
33
|
+
* 旧 store 文件(`scheduler.json`,v1)与新分片(`<sessionId>.json`)同目录不同名,
|
|
34
|
+
* 互不冲突;旧文件按用户裁决不迁移、留盘为孤儿。
|
|
35
|
+
*/
|
|
36
|
+
declare function schedulerStoreFilePath(agentDir: string, cwd: string, sessionId: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* 读快照:一次 JSON.parse 读整个文件重建任务集(无重放折叠——重放折叠 / fork owner
|
|
39
|
+
* 过滤 / 活跃路径裁剪三件套已随 event sourcing 退役)。
|
|
40
|
+
*
|
|
41
|
+
* 失败语义:
|
|
42
|
+
* - ENOENT → `missing`(空任务集正常路径)
|
|
43
|
+
* - 解析/形状失败 → 改名 `.corrupt-<ts>` 留证 + 返回 `corrupt`(调用方按空任务集继续,
|
|
44
|
+
* 调度不中断——与旧「恢复失败显式降级」语义一致);改名自身失败(只读卷等)不抛,
|
|
45
|
+
* 仍返回 `corrupt` + warn——读侧永远不因留证失败而中断启动
|
|
46
|
+
* - 其他读错误(EACCES 等)→ 上抛,由调用方决定降级(扩展装配点 catch 显式降级)
|
|
47
|
+
*/
|
|
48
|
+
declare function readSchedulerStore(filePath: string, options?: SchedulerStoreOptions): SchedulerStoreRead;
|
|
49
|
+
/**
|
|
50
|
+
* 写快照:全量序列化 + 原子替换(tmp 文件 → fsync → rename,POSIX rename 原子性)。
|
|
51
|
+
* 崩溃时要么旧文件完整、要么新文件完整,无半写形态。
|
|
52
|
+
* 目录不存在则递归创建(首写建目录)。写失败 throw——调用方(扩展 runtime 的
|
|
53
|
+
* persistTasks catch)负责 warn + 下写点重试,内存态照常推进。
|
|
54
|
+
*/
|
|
55
|
+
declare function writeSchedulerStore(filePath: string, file: Omit<SchedulerStoreFile, 'schemaVersion'>, options?: SchedulerStoreOptions): void;
|
|
56
|
+
|
|
57
|
+
export { SchedulerStoreFile, type SchedulerStoreOptions, type SchedulerStoreRead, readSchedulerStore, schedulerStoreFilePath, writeSchedulerStore };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SCHEDULER_STORE_SCHEMA_VERSION,
|
|
3
|
+
isSchedulerStoreFile
|
|
4
|
+
} from "./chunk-TFO4NW5X.js";
|
|
5
|
+
|
|
6
|
+
// src/extensions/scheduler/store.ts
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
function schedulerStoreFilePath(agentDir, cwd, sessionId) {
|
|
10
|
+
if (!/^[A-Za-z0-9_-]+$/.test(sessionId)) {
|
|
11
|
+
throw new Error(`Invalid sessionId for scheduler store path: ${JSON.stringify(sessionId)}`);
|
|
12
|
+
}
|
|
13
|
+
const resolved = path.resolve(cwd);
|
|
14
|
+
const parsed = path.parse(resolved);
|
|
15
|
+
const segments = resolved.slice(parsed.root.length).split(path.sep).filter(Boolean);
|
|
16
|
+
const root = parsed.root.replaceAll(/[^a-zA-Z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "").toLowerCase() || "root";
|
|
17
|
+
return path.join(agentDir, "scheduler", root, ...segments, `${sessionId}.json`);
|
|
18
|
+
}
|
|
19
|
+
function toStoreTask(task) {
|
|
20
|
+
const { pending: _p, history, ...rest } = task;
|
|
21
|
+
return { ...rest, history: history.map((h) => ({ ...h })) };
|
|
22
|
+
}
|
|
23
|
+
function readSchedulerStore(filePath, options) {
|
|
24
|
+
let content;
|
|
25
|
+
try {
|
|
26
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
27
|
+
} catch (err) {
|
|
28
|
+
if (isEnoent(err)) return { kind: "missing" };
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
const corrupted = (renamedTo) => {
|
|
32
|
+
options?.warn?.("scheduler store corrupted; renamed for inspection, starting with empty task set", {
|
|
33
|
+
filePath,
|
|
34
|
+
renamedTo
|
|
35
|
+
});
|
|
36
|
+
return { kind: "corrupt", renamedTo };
|
|
37
|
+
};
|
|
38
|
+
let data;
|
|
39
|
+
try {
|
|
40
|
+
data = JSON.parse(content);
|
|
41
|
+
} catch {
|
|
42
|
+
return corrupted(renameCorrupt(filePath));
|
|
43
|
+
}
|
|
44
|
+
if (!isSchedulerStoreFile(data)) {
|
|
45
|
+
return corrupted(renameCorrupt(filePath));
|
|
46
|
+
}
|
|
47
|
+
return { kind: "ok", tasks: data.tasks };
|
|
48
|
+
}
|
|
49
|
+
var SNAPSHOT_INDENT = 2;
|
|
50
|
+
function writeSchedulerStore(filePath, file, options) {
|
|
51
|
+
const payload = {
|
|
52
|
+
schemaVersion: SCHEDULER_STORE_SCHEMA_VERSION,
|
|
53
|
+
...file.sessionFile !== void 0 && { sessionFile: file.sessionFile },
|
|
54
|
+
tasks: file.tasks.map(toStoreTask)
|
|
55
|
+
};
|
|
56
|
+
const dir = path.dirname(filePath);
|
|
57
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
58
|
+
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
59
|
+
try {
|
|
60
|
+
const fd = fs.openSync(tmp, "w");
|
|
61
|
+
try {
|
|
62
|
+
fs.writeFileSync(fd, JSON.stringify(payload, null, SNAPSHOT_INDENT) + "\n", "utf-8");
|
|
63
|
+
fs.fsyncSync(fd);
|
|
64
|
+
} finally {
|
|
65
|
+
fs.closeSync(fd);
|
|
66
|
+
}
|
|
67
|
+
fs.renameSync(tmp, filePath);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
try {
|
|
70
|
+
fs.unlinkSync(tmp);
|
|
71
|
+
} catch (cleanupErr) {
|
|
72
|
+
if (!isEnoent(cleanupErr)) {
|
|
73
|
+
options?.warn?.("scheduler store tmp cleanup failed after write error", {
|
|
74
|
+
tmp,
|
|
75
|
+
error: cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function renameCorrupt(filePath) {
|
|
83
|
+
const renamedTo = `${filePath}.corrupt-${Date.now()}`;
|
|
84
|
+
try {
|
|
85
|
+
fs.renameSync(filePath, renamedTo);
|
|
86
|
+
return renamedTo;
|
|
87
|
+
} catch {
|
|
88
|
+
return filePath;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function isEnoent(err) {
|
|
92
|
+
const code = err?.code;
|
|
93
|
+
if (code === "ENOENT") return true;
|
|
94
|
+
return err instanceof Error && /ENOENT/.test(err.message);
|
|
95
|
+
}
|
|
96
|
+
export {
|
|
97
|
+
SCHEDULER_STORE_SCHEMA_VERSION,
|
|
98
|
+
isSchedulerStoreFile,
|
|
99
|
+
readSchedulerStore,
|
|
100
|
+
schedulerStoreFilePath,
|
|
101
|
+
writeSchedulerStore
|
|
102
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** 快照 schema 版本(v2 = 任务库分离形态;v1 是 npm 0.1.1 旧 store,文件名不同不互读)。 */
|
|
2
|
+
declare const SCHEDULER_STORE_SCHEMA_VERSION = 2;
|
|
3
|
+
/**
|
|
4
|
+
* 快照文件形状(v2 任务库分离 schema;方案 B 设计原文未归档,裁决内容与背景
|
|
5
|
+
* 以 commit 73073b169 message 为溯源锚——同 store.ts 头注口径)。
|
|
6
|
+
* tasks 为内存任务集的全量快照:写入口统一剥离运行时标记(pending)并拷贝 history,
|
|
7
|
+
* 读方拿到的即干净的持久化形态。
|
|
8
|
+
*/
|
|
9
|
+
type SchedulerStoreFile = {
|
|
10
|
+
schemaVersion: typeof SCHEDULER_STORE_SCHEMA_VERSION;
|
|
11
|
+
/** owner session 的 JSONL 路径(冗余记录,供排障与对账;--no-session 时可缺省) */
|
|
12
|
+
sessionFile?: string;
|
|
13
|
+
tasks: ScheduledTask[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* 快照文件形状校验(M0 契约期由 @taiji/shared/execution 的 scheduler 小域契约单源
|
|
17
|
+
* re-export,U6 存量导入消费):顶层 schemaVersion=2 + tasks 数组且逐项为对象
|
|
18
|
+
* (字段级信任唯一写者,不逐字段校验)。
|
|
19
|
+
*/
|
|
20
|
+
declare function isSchedulerStoreFile(data: unknown): data is SchedulerStoreFile;
|
|
21
|
+
type ScheduleSpec = {
|
|
22
|
+
mode: 'cron';
|
|
23
|
+
cronExpression: string;
|
|
24
|
+
} | {
|
|
25
|
+
mode: 'interval';
|
|
26
|
+
intervalMs: number;
|
|
27
|
+
};
|
|
28
|
+
type TaskKind = 'once' | 'recurring';
|
|
29
|
+
type TaskStatus = 'success' | 'failed';
|
|
30
|
+
type ScheduledTask = {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
prompt: string;
|
|
34
|
+
kind: TaskKind;
|
|
35
|
+
schedule: ScheduleSpec;
|
|
36
|
+
model?: string;
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
createdAt: number;
|
|
39
|
+
nextRunAt: number;
|
|
40
|
+
expiresAt?: number;
|
|
41
|
+
runCount: number;
|
|
42
|
+
lastRunAt?: number;
|
|
43
|
+
lastStatus?: TaskStatus;
|
|
44
|
+
lastError?: string;
|
|
45
|
+
history: ExecutionRecord[];
|
|
46
|
+
pending?: boolean;
|
|
47
|
+
};
|
|
48
|
+
type ExecutionRecord = {
|
|
49
|
+
at: number;
|
|
50
|
+
status: TaskStatus;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* history 裁剪上限(单点):由 `appendExecutionRecord` 在 runtime 记账侧执行裁剪;
|
|
54
|
+
* 快照写入口不裁剪(只逐项拷贝)——上限字面量只此一处,多处各持会漂移。
|
|
55
|
+
*/
|
|
56
|
+
declare const HISTORY_LIMIT = 20;
|
|
57
|
+
/**
|
|
58
|
+
* 追加执行记录并按 HISTORY_LIMIT 裁剪(单点):runtime 侧 dispatch 累积的
|
|
59
|
+
* push+trim——内联时与上限字面量同源存在「漏改一处」漂移风险,随上限一并收敛。
|
|
60
|
+
*/
|
|
61
|
+
declare function appendExecutionRecord(task: ScheduledTask, at: number, status: TaskStatus): void;
|
|
62
|
+
/**
|
|
63
|
+
* scheduler-manager 插件推树的 overlay 定时任务 tab ViewHost 分区键(历史命名
|
|
64
|
+
* modal-<pluginId>-<modalId>,modal 链退役后原值保留):插件 views.update 推树与
|
|
65
|
+
* taiji renderer ViewHost 消费跨包共用本单源——双端各持字面量时任一端漂移即
|
|
66
|
+
* ViewHost per-session 分区静默 miss(tab 空白无报错),收敛到本文件单点导出。
|
|
67
|
+
*/
|
|
68
|
+
declare const SCHEDULER_MODAL_VIEW_ID = "modal-scheduler-manager-scheduler-manager.panel";
|
|
69
|
+
|
|
70
|
+
export { type ExecutionRecord as E, HISTORY_LIMIT as H, type ScheduleSpec as S, type TaskKind as T, SCHEDULER_MODAL_VIEW_ID as a, SCHEDULER_STORE_SCHEMA_VERSION as b, type ScheduledTask as c, type SchedulerStoreFile as d, type TaskStatus as e, appendExecutionRecord as f, isSchedulerStoreFile as i };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhushanwen/extension-protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Cross-layer pi extension contract package: dual-mode (TUI/GUI) rendering helpers, session/background-task/pending-entries protocols, and shared behavior primitives",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
"./background-task": {
|
|
15
15
|
"types": "./dist/background-task-entry.d.ts",
|
|
16
16
|
"import": "./dist/background-task-entry.js"
|
|
17
|
+
},
|
|
18
|
+
"./scheduler-store": {
|
|
19
|
+
"types": "./dist/scheduler-store-entry.d.ts",
|
|
20
|
+
"import": "./dist/scheduler-store-entry.js"
|
|
17
21
|
}
|
|
18
22
|
},
|
|
19
23
|
"files": [
|
|
@@ -22,7 +26,7 @@
|
|
|
22
26
|
],
|
|
23
27
|
"license": "MIT",
|
|
24
28
|
"dependencies": {
|
|
25
|
-
"@zhushanwen/state-machine": "0.2.
|
|
29
|
+
"@zhushanwen/state-machine": "0.2.1"
|
|
26
30
|
},
|
|
27
31
|
"devDependencies": {
|
|
28
32
|
"@vitest/coverage-v8": "^4.1.9",
|