@zhushanwen/extension-protocol 0.26.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 +74 -159
- package/dist/index.js +41 -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
|
/**
|
|
@@ -631,164 +633,6 @@ declare function getAskUserOther(answers: AskUserAnswers, question: AskUserQuest
|
|
|
631
633
|
*/
|
|
632
634
|
declare function isAskUserQuestion(value: unknown): value is AskUserQuestion;
|
|
633
635
|
|
|
634
|
-
type ScheduleSpec = {
|
|
635
|
-
mode: 'cron';
|
|
636
|
-
cronExpression: string;
|
|
637
|
-
} | {
|
|
638
|
-
mode: 'interval';
|
|
639
|
-
intervalMs: number;
|
|
640
|
-
};
|
|
641
|
-
type TaskKind = 'once' | 'recurring';
|
|
642
|
-
type TaskStatus = 'success' | 'failed';
|
|
643
|
-
interface ScheduledTask {
|
|
644
|
-
id: string;
|
|
645
|
-
name: string;
|
|
646
|
-
prompt: string;
|
|
647
|
-
kind: TaskKind;
|
|
648
|
-
schedule: ScheduleSpec;
|
|
649
|
-
model?: string;
|
|
650
|
-
enabled: boolean;
|
|
651
|
-
createdAt: number;
|
|
652
|
-
nextRunAt: number;
|
|
653
|
-
expiresAt?: number;
|
|
654
|
-
runCount: number;
|
|
655
|
-
lastRunAt?: number;
|
|
656
|
-
lastStatus?: TaskStatus;
|
|
657
|
-
lastError?: string;
|
|
658
|
-
history: ExecutionRecord[];
|
|
659
|
-
ownerSessionFile?: string;
|
|
660
|
-
pending?: boolean;
|
|
661
|
-
}
|
|
662
|
-
interface ExecutionRecord {
|
|
663
|
-
at: number;
|
|
664
|
-
status: TaskStatus;
|
|
665
|
-
}
|
|
666
|
-
/**
|
|
667
|
-
* history 裁剪上限(ext-simplify-08 L5 单点):runtime 侧 dispatch 累积
|
|
668
|
-
* (onDispatchSuccess)与 replay 侧 advance 折叠共用同一上限——
|
|
669
|
-
* 双处各持字面量会漂移(曾 runtime.ts / replay.ts 各写一份 20),收敛到本文件单点导出。
|
|
670
|
-
*/
|
|
671
|
-
declare const HISTORY_LIMIT = 20;
|
|
672
|
-
/**
|
|
673
|
-
* 追加执行记录并按 HISTORY_LIMIT 裁剪(ext-simplify-17 B4 单点):runtime 侧 dispatch
|
|
674
|
-
* 累积与 replay 侧 advance 折叠共用的 push+trim——三处内联时与上限字面量同源存在
|
|
675
|
-
* 「漏改一处」漂移风险,随上限一并收敛。
|
|
676
|
-
*/
|
|
677
|
-
declare function appendExecutionRecord(task: ScheduledTask, at: number, status: TaskStatus): void;
|
|
678
|
-
/**
|
|
679
|
-
* pi.appendEntry 写入的 custom entry customType 标识(ext-simplify-17 B1 单点):
|
|
680
|
-
* backend.appendEntry 写入 / replay 折叠过滤 / importer 旧 store 导入三处共用——
|
|
681
|
-
* 各持字面量时「改一处漏两处」会使写入与重放识别脱节(写入新标识、重放不认,任务静默丢失)。
|
|
682
|
-
*/
|
|
683
|
-
declare const TASK_ENTRY_TYPE = "pi-scheduler:task";
|
|
684
|
-
/**
|
|
685
|
-
* upsert op 携带的全量任务快照(不含 ownerSessionFile / pending)。
|
|
686
|
-
* ownerSessionFile 放在 op 顶层(见 SchedulerEntryOp.upsert),
|
|
687
|
-
* pending 是运行时标记不持久化。其余字段与 ScheduledTask 对齐。
|
|
688
|
-
*/
|
|
689
|
-
interface TaskSnapshot {
|
|
690
|
-
id: string;
|
|
691
|
-
name: string;
|
|
692
|
-
prompt: string;
|
|
693
|
-
kind: TaskKind;
|
|
694
|
-
schedule: ScheduleSpec;
|
|
695
|
-
model?: string;
|
|
696
|
-
enabled: boolean;
|
|
697
|
-
createdAt: number;
|
|
698
|
-
nextRunAt: number;
|
|
699
|
-
expiresAt?: number;
|
|
700
|
-
runCount: number;
|
|
701
|
-
lastRunAt?: number;
|
|
702
|
-
lastStatus?: TaskStatus;
|
|
703
|
-
lastError?: string;
|
|
704
|
-
history: ExecutionRecord[];
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* pi.appendEntry(TASK_ENTRY_TYPE, SchedulerEntryOp) 写入 session JSONL 的 op 联合类型。
|
|
708
|
-
*
|
|
709
|
-
* advance.status 固定为 'success'(CL8):对齐现有 TaskStatus='success'|'failed',
|
|
710
|
-
* 而非继承的 DM-SCHEDULER-OP 词表 'ok'。唯一 emit 值——按 CL7,advance 仅在 dispatch
|
|
711
|
-
* 成功(nextRunAt 实际推进)时 append,失败 dispatch 不 append(transient 失败重试语义)。
|
|
712
|
-
* fold 的 `task.lastStatus = entry.status` 直接赋值合法('success' 属 TaskStatus)。
|
|
713
|
-
*/
|
|
714
|
-
type SchedulerEntryOp = {
|
|
715
|
-
op: 'upsert';
|
|
716
|
-
taskId: string;
|
|
717
|
-
ownerSessionFile: string;
|
|
718
|
-
task: TaskSnapshot;
|
|
719
|
-
} | {
|
|
720
|
-
op: 'advance';
|
|
721
|
-
taskId: string;
|
|
722
|
-
nextRunAt: number;
|
|
723
|
-
at: number;
|
|
724
|
-
status: 'success';
|
|
725
|
-
} | {
|
|
726
|
-
op: 'toggle';
|
|
727
|
-
taskId: string;
|
|
728
|
-
enabled: boolean;
|
|
729
|
-
nextRunAt?: number;
|
|
730
|
-
} | {
|
|
731
|
-
op: 'delete';
|
|
732
|
-
taskId: string;
|
|
733
|
-
};
|
|
734
|
-
/**
|
|
735
|
-
* TaskSnapshot → ScheduledTask(replay 侧重建):pending 不持久化故不恢复,
|
|
736
|
-
* ownerSessionFile 由调用方(replay applyUpsert)从 op 顶层补回。history 逐项深拷贝——
|
|
737
|
-
* 避免恢复出的运行时 task 与快照共享数组/元素引用(task 后续被 mutate 时不污染快照)。
|
|
738
|
-
*/
|
|
739
|
-
declare function snapshotToTask(snapshot: TaskSnapshot): ScheduledTask;
|
|
740
|
-
/**
|
|
741
|
-
* scheduler-manager 插件推树的 overlay 定时任务 tab ViewHost 分区键(历史命名
|
|
742
|
-
* modal-<pluginId>-<modalId>,modal 链退役后原值保留):插件 views.update 推树与
|
|
743
|
-
* taiji renderer ViewHost 消费跨包共用本单源——双端各持字面量时任一端漂移即
|
|
744
|
-
* ViewHost per-session 分区静默 miss(tab 空白无报错),收敛到本文件单点导出。
|
|
745
|
-
*/
|
|
746
|
-
declare const SCHEDULER_MODAL_VIEW_ID = "modal-scheduler-manager-scheduler-manager.panel";
|
|
747
|
-
|
|
748
|
-
/**
|
|
749
|
-
* 折叠期降级日志注入点:包内不持有任何 logger 依赖(零 pi 依赖收敛),由调用方注入。
|
|
750
|
-
* 省略时降级路径静默(折叠行为不受影响——返回值与注入无关)。
|
|
751
|
-
*/
|
|
752
|
-
interface ReplayFoldOptions {
|
|
753
|
-
warn?: (message: string, context?: unknown) => void;
|
|
754
|
-
}
|
|
755
|
-
/**
|
|
756
|
-
* CustomEntry 的最小可识别形状(duck-typed,不依赖 @earendil-works/pi-coding-agent 的
|
|
757
|
-
* 具体 SessionEntry 类型)。与 pending-notifications/state.ts、goal/ports.ts 同款:
|
|
758
|
-
* 纯函数可独立单测,调用方传入真实 SessionEntry[] 时按结构兼容。
|
|
759
|
-
*/
|
|
760
|
-
interface SchedulerEntryLike {
|
|
761
|
-
type: string;
|
|
762
|
-
customType?: string;
|
|
763
|
-
data?: unknown;
|
|
764
|
-
}
|
|
765
|
-
/**
|
|
766
|
-
* 从 CustomEntry 序列折叠恢复 per-task 末态(event sourcing)。
|
|
767
|
-
*
|
|
768
|
-
* 两类调用方:
|
|
769
|
-
* - pi 扩展 session_start 重放:PiSchedulerBackend.loadTasks() 委托本函数,把当前 session 的
|
|
770
|
-
* TASK_ENTRY_TYPE custom entries 折叠成运行时任务 Map。
|
|
771
|
-
* - taiji plugin 条目镜像:对 readEntries 拉取的累计 entries 调用本函数折叠出任务表
|
|
772
|
-
* (与扩展同一实现,插件与扩展折叠结果构造性一致)。
|
|
773
|
-
*
|
|
774
|
-
* 两步:
|
|
775
|
-
* ① 全量折叠(foldEntries)——遍历 customType===TASK_ENTRY_TYPE 的 entries,按 op 类型折叠:
|
|
776
|
-
* - upsert → 用快照重建任务(ownerSessionFile 来自 op 顶层)
|
|
777
|
-
* - advance → nextRunAt=新值 / lastRunAt=at / runCount++ / history.push(裁20) / lastStatus=status(gap2)
|
|
778
|
-
* - toggle → enabled=新值
|
|
779
|
-
* - delete → 从 Map 移除(后续同 taskId 的 advance/toggle 对不存在 taskId 为 no-op,安全)
|
|
780
|
-
* advance/toggle/delete 对不存在的 taskId 为 no-op,因此 fork 继承的完整序列必须先全量折叠,
|
|
781
|
-
* 不能在处理 upsert 时按 owner 跳过(否则后续无 owner 字段的 op 会作用于「不存在的 taskId」丢失)。
|
|
782
|
-
* ② 折叠完成后按 task.ownerSessionFile !== currentSessionFile 整体过滤(gap1,filterNonOwnerTasks):
|
|
783
|
-
* 移除 fork 继承的非 owner 任务——fork 出的 session B 继承了 owner=A 的 upsert+advance 序列,
|
|
784
|
-
* 但 B 不应拥有 A 的任务,重放后整体过滤掉。
|
|
785
|
-
*
|
|
786
|
-
* append-only 时序保证 nextRunAt 不回退(D1):advance entry 按写入顺序折叠,自然取到最后推进值。
|
|
787
|
-
*
|
|
788
|
-
* getEntries 解析/迭代异常 → options.warn + 返回空 Map(gap4):降级为无任务而非崩溃调用方。
|
|
789
|
-
*/
|
|
790
|
-
declare function replayFoldEntries(entries: Iterable<SchedulerEntryLike>, currentSessionFile: string | undefined, options?: ReplayFoldOptions): Map<string, ScheduledTask>;
|
|
791
|
-
|
|
792
636
|
declare const MS_PER_DAY = 86400000;
|
|
793
637
|
declare const MS_PER_HOUR = 3600000;
|
|
794
638
|
declare const MS_PER_MINUTE = 60000;
|
|
@@ -1733,6 +1577,34 @@ declare const SUBAGENT_BG_NOTIFY_CUSTOM_TYPE: "subagent-bg-notify";
|
|
|
1733
1577
|
* 上下文 + renderer 定向气泡)。shared parseSubagentDirective / runtime display 覆写消费。 */
|
|
1734
1578
|
declare const SUBAGENT_DIRECTIVE_CUSTOM_TYPE: "subagent-directive";
|
|
1735
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
|
+
|
|
1736
1608
|
/**
|
|
1737
1609
|
* 子代理引擎契约(U7 引擎可发现性 + GUI 引擎选择)。
|
|
1738
1610
|
*
|
|
@@ -2687,4 +2559,47 @@ type ResolvedSettlementReason = SettlementReason | 'unknown';
|
|
|
2687
2559
|
*/
|
|
2688
2560
|
declare function resolveSettlementReason(reason: string): ResolvedSettlementReason;
|
|
2689
2561
|
|
|
2690
|
-
|
|
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,6 +739,16 @@ 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
|
|
|
@@ -1313,6 +1211,23 @@ function resolveSettlementReason(reason) {
|
|
|
1313
1211
|
return SETTLEMENT_REASON_MAP[reason] ?? "unknown";
|
|
1314
1212
|
}
|
|
1315
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
|
+
}
|
|
1316
1231
|
export {
|
|
1317
1232
|
ACTION_EVENT_MAP,
|
|
1318
1233
|
ASK_USER_MARKER,
|
|
@@ -1328,6 +1243,8 @@ export {
|
|
|
1328
1243
|
CANVAS_MARKER,
|
|
1329
1244
|
CANVAS_MARKER_ACTIONS,
|
|
1330
1245
|
CANVAS_TOOL_NAMES,
|
|
1246
|
+
COMPACTION_ENTRY_APPENDED_SOURCE,
|
|
1247
|
+
DEFAULT_IDLE_BACKGROUND_SEC,
|
|
1331
1248
|
GUI_WIDGET_MARKER,
|
|
1332
1249
|
HISTORY_LIMIT,
|
|
1333
1250
|
INFLIGHT_REPORT_ACK,
|
|
@@ -1349,16 +1266,17 @@ export {
|
|
|
1349
1266
|
PLAN_STATE_CUSTOM_TYPE,
|
|
1350
1267
|
PROTOCOL_VERSION,
|
|
1351
1268
|
SCHEDULER_MODAL_VIEW_ID,
|
|
1269
|
+
SCHEDULER_STORE_SCHEMA_VERSION,
|
|
1352
1270
|
SCHEDULE_CREATE_MARKER,
|
|
1353
1271
|
SESSION_MANAGER_ACTIONS,
|
|
1354
1272
|
SESSION_MANAGER_MARKER,
|
|
1355
1273
|
SETTLEMENT_REASONS,
|
|
1274
|
+
SMART_CONTEXT_NOTICE_CUSTOM_TYPE,
|
|
1356
1275
|
SUBAGENTS_ENGINES_FILENAME,
|
|
1357
1276
|
SUBAGENT_BG_NOTIFY_CUSTOM_TYPE,
|
|
1358
1277
|
SUBAGENT_DIRECTIVE_CUSTOM_TYPE,
|
|
1359
1278
|
SUBAGENT_INFLIGHT_MARKER,
|
|
1360
1279
|
SUBAGENT_JOURNAL_MARKER,
|
|
1361
|
-
TASK_ENTRY_TYPE,
|
|
1362
1280
|
TOOL_ACTION_MAP,
|
|
1363
1281
|
UI_FORM_MARKER,
|
|
1364
1282
|
WORKFLOW_RESULT_CUSTOM_TYPE,
|
|
@@ -1422,6 +1340,7 @@ export {
|
|
|
1422
1340
|
isRunningProjection,
|
|
1423
1341
|
isScheduleDraft,
|
|
1424
1342
|
isScheduleFormResult,
|
|
1343
|
+
isSchedulerStoreFile,
|
|
1425
1344
|
isSessionManagerAbortParams,
|
|
1426
1345
|
isSessionManagerArchiveParams,
|
|
1427
1346
|
isSessionManagerCreateParams,
|
|
@@ -1437,18 +1356,18 @@ export {
|
|
|
1437
1356
|
isSubagentJournalReport,
|
|
1438
1357
|
isTerminalBackgroundTaskState,
|
|
1439
1358
|
mapReasonToStatus,
|
|
1359
|
+
normalizeIdleBackgroundSec,
|
|
1440
1360
|
onceCronToDate,
|
|
1361
|
+
parseCompactionPersistSignal,
|
|
1441
1362
|
parsePlanReviewResponse,
|
|
1442
1363
|
readDetailsSummary,
|
|
1443
1364
|
readLifecycleState,
|
|
1444
1365
|
readResumeHint,
|
|
1445
|
-
replayFoldEntries,
|
|
1446
1366
|
resolveSettlementReason,
|
|
1447
1367
|
resultContentText,
|
|
1448
1368
|
scanPendingEntries,
|
|
1449
1369
|
scanPendingFields,
|
|
1450
1370
|
setWidgetDual,
|
|
1451
|
-
snapshotToTask,
|
|
1452
1371
|
toolOfAction,
|
|
1453
1372
|
transition,
|
|
1454
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",
|