@ilikexiaoni/pi-task-list 0.4.0 → 0.4.2

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.
@@ -0,0 +1,116 @@
1
+ import {
2
+ applyTaskAction,
3
+ cloneState,
4
+ cloneTaskCommand,
5
+ createEmptyState,
6
+ isPersistedTaskOperation,
7
+ isPersistedTaskState,
8
+ normalizeState,
9
+ type TaskOperation,
10
+ type TaskState,
11
+ } from "./task-list-core.ts";
12
+
13
+ export const TASK_LIST_TOOL_NAME = "task_list";
14
+ export const TASK_LIST_STATE_ENTRY_TYPE = "task-list-state";
15
+
16
+ interface RecordLike {
17
+ [key: string]: unknown;
18
+ }
19
+
20
+ interface BranchMessageEntry {
21
+ type?: unknown;
22
+ message?: unknown;
23
+ customType?: unknown;
24
+ data?: unknown;
25
+ }
26
+
27
+ function isRecord(value: unknown): value is RecordLike {
28
+ return typeof value === "object" && value !== null;
29
+ }
30
+
31
+ function snapshotFromValue(value: unknown): TaskState | undefined {
32
+ if (!isRecord(value)) return undefined;
33
+ if (isPersistedTaskState(value.state)) return normalizeState(value.state);
34
+ if (isPersistedTaskState(value)) return normalizeState(value);
35
+ return undefined;
36
+ }
37
+
38
+ function toolDetails(entry: unknown): unknown {
39
+ if (!isRecord(entry) || entry.type !== "message" || !isRecord(entry.message)) return undefined;
40
+ const message = entry.message;
41
+ if (message.role !== "toolResult" || message.toolName !== TASK_LIST_TOOL_NAME) return undefined;
42
+ return message.details;
43
+ }
44
+
45
+ function snapshotFromEntry(entry: BranchMessageEntry): TaskState | undefined {
46
+ if (entry.type === "custom" && entry.customType === TASK_LIST_STATE_ENTRY_TYPE) {
47
+ return snapshotFromValue(entry.data);
48
+ }
49
+ return snapshotFromValue(toolDetails(entry));
50
+ }
51
+
52
+ function operationFromValue(value: unknown): TaskOperation | undefined {
53
+ if (!isRecord(value)) return undefined;
54
+ const operation = value.operation;
55
+ if (!isPersistedTaskOperation(operation)) return undefined;
56
+ return {
57
+ version: 1,
58
+ command: cloneTaskCommand(operation.command),
59
+ at: operation.at.slice(0, 64),
60
+ };
61
+ }
62
+
63
+ function operationFromEntry(entry: BranchMessageEntry): TaskOperation | undefined {
64
+ return operationFromValue(toolDetails(entry));
65
+ }
66
+
67
+ /**
68
+ * Apply a compact task-list operation without allowing a malformed historical
69
+ * entry to prevent restoration of later valid session data.
70
+ */
71
+ export function replayTaskOperation(state: TaskState, operation: TaskOperation): TaskState | undefined {
72
+ try {
73
+ return applyTaskAction(state, operation.command, operation.at).state;
74
+ } catch {
75
+ return undefined;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Restore the current branch from the most recent usable snapshot and replay
81
+ * only later mutations. Full snapshots from releases before operation-based
82
+ * persistence remain supported for backward compatibility.
83
+ */
84
+ export function restoreTaskListState(branch: readonly unknown[]): TaskState {
85
+ let snapshot: TaskState | undefined;
86
+ let snapshotIndex = -1;
87
+
88
+ for (let index = branch.length - 1; index >= 0; index -= 1) {
89
+ const candidate = snapshotFromEntry(branch[index] as BranchMessageEntry);
90
+ if (!candidate) continue;
91
+ snapshot = candidate;
92
+ snapshotIndex = index;
93
+ break;
94
+ }
95
+
96
+ let state = snapshot ? cloneState(snapshot) : createEmptyState();
97
+ for (let index = snapshotIndex + 1; index < branch.length; index += 1) {
98
+ const operation = operationFromEntry(branch[index] as BranchMessageEntry);
99
+ if (!operation) continue;
100
+ const replayed = replayTaskOperation(state, operation);
101
+ if (replayed) state = replayed;
102
+ }
103
+
104
+ return normalizeState(state);
105
+ }
106
+
107
+ /**
108
+ * Update an already restored in-memory state from a task_list tool result.
109
+ * New releases use operations; older sessions expose a full `state` snapshot.
110
+ */
111
+ export function applyTaskListToolDetails(current: TaskState, details: unknown): TaskState | undefined {
112
+ const snapshot = snapshotFromValue(details);
113
+ if (snapshot) return snapshot;
114
+ const operation = operationFromValue(details);
115
+ return operation ? replayTaskOperation(current, operation) : undefined;
116
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ilikexiaoni/pi-task-list",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "type": "module",
5
5
  "description": "A generic structured task list extension for Pi with dependencies, evidence, and visible progress.",
6
6
  "keywords": [