pi-harness-runtime 1.1.407 → 1.1.410

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.
Files changed (207) hide show
  1. package/harness/key-file-audit.ts +2 -2
  2. package/index.ts +78 -0
  3. package/package.json +1 -1
  4. package/packages/a2a-adapter/package.json +1 -1
  5. package/packages/agent-policy/package.json +1 -1
  6. package/packages/architecture-generator/package.json +1 -1
  7. package/packages/auth/package.json +1 -1
  8. package/packages/autonomous-refactor/package.json +1 -1
  9. package/packages/autonomous-runtime/package.json +1 -1
  10. package/packages/cache-strategy/package.json +1 -1
  11. package/packages/capability-registry/package.json +1 -1
  12. package/packages/checkpoint/package.json +1 -1
  13. package/packages/cli-plugin-sdk/package.json +1 -1
  14. package/packages/clipboard/package.json +1 -1
  15. package/packages/clipboard-plugin/package.json +1 -1
  16. package/packages/code-generation/package.json +1 -1
  17. package/packages/code-review/package.json +1 -1
  18. package/packages/codex-adapter/package.json +1 -1
  19. package/packages/codex-watcher/package.json +1 -1
  20. package/packages/config-capture/package.json +1 -1
  21. package/packages/connection-runtime/package.json +1 -1
  22. package/packages/context-compiler/package.json +1 -1
  23. package/packages/context-discovery/package.json +1 -1
  24. package/packages/context-manager/package.json +1 -1
  25. package/packages/contracts/package.json +1 -1
  26. package/packages/cookie-sanitizer/package.json +1 -1
  27. package/packages/cost-optimizer/package.json +1 -1
  28. package/packages/dependency-analyzer/package.json +1 -1
  29. package/packages/django-plugin/package.json +1 -1
  30. package/packages/doc-generator/package.json +1 -1
  31. package/packages/durable-review/package.json +1 -1
  32. package/packages/e2e-tester/package.json +1 -1
  33. package/packages/evaluation-engine/package.json +1 -1
  34. package/packages/evaluation-runner/package.json +1 -1
  35. package/packages/event-bus/package.json +1 -1
  36. package/packages/event-sink/package.json +1 -1
  37. package/packages/event-store/package.json +1 -1
  38. package/packages/experience-replay/package.json +1 -1
  39. package/packages/feedback-collector/package.json +1 -1
  40. package/packages/file-copy-helper/package.json +1 -1
  41. package/packages/framework-detector/package.json +1 -1
  42. package/packages/framework-plugin-sdk/package.json +1 -1
  43. package/packages/frappe-plugin/package.json +1 -1
  44. package/packages/generic-web-plugin/package.json +1 -1
  45. package/packages/health-monitor/package.json +1 -1
  46. package/packages/intent-analyzer/package.json +1 -1
  47. package/packages/jev-judge/package.json +1 -1
  48. package/packages/knowledge-graph/package.json +1 -1
  49. package/packages/knowledge-retrieval/package.json +1 -1
  50. package/packages/laravel-plugin/package.json +1 -1
  51. package/packages/learning-engine/package.json +1 -1
  52. package/packages/logging/package.json +1 -1
  53. package/packages/mcp-adapter/package.json +1 -1
  54. package/packages/memory-engine/package.json +1 -1
  55. package/packages/milestone-manager/package.json +1 -1
  56. package/packages/model-registry/package.json +1 -1
  57. package/packages/nextjs-plugin/package.json +1 -1
  58. package/packages/notification/package.json +1 -1
  59. package/packages/observability/package.json +1 -1
  60. package/packages/okf-indexer/package.json +1 -1
  61. package/packages/paperclip-integration/package.json +1 -1
  62. package/packages/performance-optimizer/package.json +1 -1
  63. package/packages/permission-mode/package.json +1 -1
  64. package/packages/privilege-broker/package.json +1 -1
  65. package/packages/project-analyzer/package.json +1 -1
  66. package/packages/project-bootstrap/package.json +1 -1
  67. package/packages/projection-engine/package.json +1 -1
  68. package/packages/prompt-compiler/package.json +1 -1
  69. package/packages/prompt-versioning/package.json +1 -1
  70. package/packages/provider-adapter-sdk/package.json +1 -1
  71. package/packages/provider-router/package.json +1 -1
  72. package/packages/provider-selector/package.json +1 -1
  73. package/packages/providers/dist/adapters.d.ts +1 -1
  74. package/packages/providers/dist/adapters.d.ts.map +1 -1
  75. package/packages/providers/dist/deepagents-adapter.d.ts +22 -1
  76. package/packages/providers/dist/deepagents-adapter.d.ts.map +1 -1
  77. package/packages/providers/dist/deepagents-adapter.js +3 -8
  78. package/packages/providers/dist/deepagents-adapter.js.map +1 -1
  79. package/packages/providers/package.json +1 -1
  80. package/packages/providers/src/adapters.ts +1 -1
  81. package/packages/providers/src/deepagents-adapter.ts +28 -31
  82. package/packages/qdrant-skills/dist/embedder.d.ts +5 -1
  83. package/packages/qdrant-skills/dist/embedder.d.ts.map +1 -1
  84. package/packages/qdrant-skills/dist/embedder.js +52 -37
  85. package/packages/qdrant-skills/dist/embedder.js.map +1 -1
  86. package/packages/qdrant-skills/package.json +1 -1
  87. package/packages/qdrant-skills/src/embedder.ts +56 -40
  88. package/packages/qdrant-skills/test/embedder.test.ts +97 -0
  89. package/packages/quota-manager/dist/quota-manager.d.ts +1 -1
  90. package/packages/quota-manager/dist/quota-manager.d.ts.map +1 -1
  91. package/packages/quota-manager/dist/quota-manager.js +10 -8
  92. package/packages/quota-manager/dist/quota-manager.js.map +1 -1
  93. package/packages/quota-manager/dist/tui-usage-monitor.d.ts +5 -3
  94. package/packages/quota-manager/dist/tui-usage-monitor.d.ts.map +1 -1
  95. package/packages/quota-manager/dist/tui-usage-monitor.js +30 -10
  96. package/packages/quota-manager/dist/tui-usage-monitor.js.map +1 -1
  97. package/packages/quota-manager/package.json +1 -1
  98. package/packages/quota-manager/src/quota-manager.ts +10 -9
  99. package/packages/quota-manager/src/tui-usage-monitor.ts +40 -17
  100. package/packages/rate-limiter/package.json +1 -1
  101. package/packages/react-vite-plugin/package.json +1 -1
  102. package/packages/relay/package.json +1 -1
  103. package/packages/release-manager/package.json +1 -1
  104. package/packages/requirement-compiler/package.json +1 -1
  105. package/packages/runtime/package.json +1 -1
  106. package/packages/scheduler/package.json +1 -1
  107. package/packages/scheduler-adapter/package.json +1 -1
  108. package/packages/session/package.json +1 -1
  109. package/packages/session-api/package.json +1 -1
  110. package/packages/session-export/package.json +1 -1
  111. package/packages/shared-context/package.json +1 -1
  112. package/packages/skill-mcp-client/package.json +1 -1
  113. package/packages/skill-registry/package.json +1 -1
  114. package/packages/skills/dist/loader.d.ts.map +1 -1
  115. package/packages/skills/dist/loader.js +9 -6
  116. package/packages/skills/dist/loader.js.map +1 -1
  117. package/packages/skills/package.json +1 -1
  118. package/packages/skills/src/loader.ts +13 -6
  119. package/packages/sprint-planner/package.json +1 -1
  120. package/packages/subscription-engine/package.json +1 -1
  121. package/packages/task-compiler/package.json +1 -1
  122. package/packages/telegram/package.json +1 -1
  123. package/packages/tencentdb-memory/package.json +1 -1
  124. package/packages/tencentdb-sync/package.json +1 -1
  125. package/packages/test-data-generator/package.json +1 -1
  126. package/packages/test-generator/package.json +1 -1
  127. package/packages/todo-bd-sync/package.json +1 -1
  128. package/packages/token-estimation/package.json +1 -1
  129. package/packages/token-optimizer/package.json +1 -1
  130. package/packages/tooling-preference/package.json +1 -1
  131. package/packages/trajectory/package.json +1 -1
  132. package/packages/tui/package.json +1 -1
  133. package/packages/two-phase-review/dist/classify.d.ts +56 -0
  134. package/packages/two-phase-review/dist/classify.d.ts.map +1 -0
  135. package/packages/two-phase-review/dist/classify.js +108 -0
  136. package/packages/two-phase-review/dist/classify.js.map +1 -0
  137. package/packages/two-phase-review/dist/index.d.ts +20 -0
  138. package/packages/two-phase-review/dist/index.d.ts.map +1 -0
  139. package/packages/two-phase-review/dist/index.js +14 -0
  140. package/packages/two-phase-review/dist/index.js.map +1 -0
  141. package/packages/two-phase-review/dist/rules.d.ts +81 -0
  142. package/packages/two-phase-review/dist/rules.d.ts.map +1 -0
  143. package/packages/two-phase-review/dist/rules.js +195 -0
  144. package/packages/two-phase-review/dist/rules.js.map +1 -0
  145. package/packages/two-phase-review/dist/runtime.d.ts +70 -0
  146. package/packages/two-phase-review/dist/runtime.d.ts.map +1 -0
  147. package/packages/two-phase-review/dist/runtime.js +298 -0
  148. package/packages/two-phase-review/dist/runtime.js.map +1 -0
  149. package/packages/two-phase-review/dist/scope.d.ts +68 -0
  150. package/packages/two-phase-review/dist/scope.d.ts.map +1 -0
  151. package/packages/two-phase-review/dist/scope.js +205 -0
  152. package/packages/two-phase-review/dist/scope.js.map +1 -0
  153. package/packages/two-phase-review/dist/signals.d.ts +74 -0
  154. package/packages/two-phase-review/dist/signals.d.ts.map +1 -0
  155. package/packages/two-phase-review/dist/signals.js +297 -0
  156. package/packages/two-phase-review/dist/signals.js.map +1 -0
  157. package/packages/two-phase-review/dist/state.d.ts +72 -0
  158. package/packages/two-phase-review/dist/state.d.ts.map +1 -0
  159. package/packages/two-phase-review/dist/state.js +238 -0
  160. package/packages/two-phase-review/dist/state.js.map +1 -0
  161. package/packages/two-phase-review/dist/types.d.ts +62 -0
  162. package/packages/two-phase-review/dist/types.d.ts.map +1 -0
  163. package/packages/two-phase-review/dist/types.js +9 -0
  164. package/packages/two-phase-review/dist/types.js.map +1 -0
  165. package/packages/two-phase-review/package.json +21 -0
  166. package/packages/two-phase-review/src/classify.ts +129 -0
  167. package/packages/two-phase-review/src/index.ts +110 -0
  168. package/packages/two-phase-review/src/rules.ts +236 -0
  169. package/packages/two-phase-review/src/runtime.ts +392 -0
  170. package/packages/two-phase-review/src/scope.ts +247 -0
  171. package/packages/two-phase-review/src/signals.ts +340 -0
  172. package/packages/two-phase-review/src/state.ts +280 -0
  173. package/packages/two-phase-review/src/types.ts +72 -0
  174. package/packages/two-phase-review/test/classify.test.ts +102 -0
  175. package/packages/two-phase-review/test/helpers.ts +72 -0
  176. package/packages/two-phase-review/test/rules.test.ts +150 -0
  177. package/packages/two-phase-review/test/runtime.test.ts +362 -0
  178. package/packages/two-phase-review/test/scope.test.ts +186 -0
  179. package/packages/two-phase-review/test/signals.test.ts +183 -0
  180. package/packages/two-phase-review/test/state.test.ts +201 -0
  181. package/packages/types/package.json +1 -1
  182. package/packages/voice-api/package.json +1 -1
  183. package/packages/webmcp-tester/package.json +1 -1
  184. package/packages/wiki/package.json +1 -1
  185. package/packages/workflow-events/package.json +1 -1
  186. package/packages/workspace-scanner/package.json +1 -1
  187. package/packages/worktree/dist/worktree.d.ts +32 -1
  188. package/packages/worktree/dist/worktree.d.ts.map +1 -1
  189. package/packages/worktree/dist/worktree.js +21 -8
  190. package/packages/worktree/dist/worktree.js.map +1 -1
  191. package/packages/worktree/package.json +1 -1
  192. package/packages/worktree/src/worktree.ts +50 -9
  193. package/packages/worktree/test/worktree.test.ts +27 -27
  194. package/packages/write-review/package.json +1 -1
  195. package/scripts/typecheck-packages.mjs +3 -3
  196. package/packages/providers/adapters.ts +0 -362
  197. package/packages/providers/deepagents-adapter.ts +0 -458
  198. package/packages/providers/glm-usage.ts +0 -248
  199. package/packages/providers/index.ts +0 -85
  200. package/packages/providers/openai-usage.ts +0 -263
  201. package/packages/trajectory/dist/test/store.test.d.ts +0 -2
  202. package/packages/trajectory/dist/test/store.test.d.ts.map +0 -1
  203. package/packages/trajectory/dist/test/store.test.js +0 -228
  204. package/packages/trajectory/dist/test/store.test.js.map +0 -1
  205. package/packages/types/src/agent-worker-types.d.ts +0 -206
  206. package/packages/types/src/agent-worker-types.js +0 -41
  207. package/packages/worktree/worktree.ts +0 -293
@@ -0,0 +1,238 @@
1
+ /**
2
+ * The persisted state of the two-phase workflow.
3
+ *
4
+ * The state survives a restart and a compaction, so the phase, the task start
5
+ * revision, the review scope, the finished chunks and the open findings are
6
+ * never lost.
7
+ */
8
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
9
+ import { homedir } from "node:os";
10
+ import { join } from "node:path";
11
+ /** The directory the state files live in. */
12
+ export const TWO_PHASE_STATE_DIR = join(homedir(), ".pi-harness-runtime", "two-phase-review");
13
+ const PHASE_ORDER = {
14
+ implementation: 0,
15
+ review_pending: 1,
16
+ review: 2,
17
+ review_fixes: 3,
18
+ complete: 4,
19
+ };
20
+ /** Makes the name of a temporary state file unique. */
21
+ let writeCounter = 0;
22
+ /** A fresh state for a task. */
23
+ export function createState(taskId, startRevision = null) {
24
+ return {
25
+ phase: "implementation",
26
+ taskId,
27
+ startRevision,
28
+ scopeFiles: [],
29
+ chunks: [],
30
+ signals: [],
31
+ findings: [],
32
+ updatedAt: new Date().toISOString(),
33
+ };
34
+ }
35
+ /** Make a file name safe for the file system. */
36
+ export function safeFileName(taskId) {
37
+ return (taskId || "default").replace(/[^A-Za-z0-9._-]+/g, "_");
38
+ }
39
+ /** The entries of a directory, for a readable error message. */
40
+ function listDir(dir) {
41
+ try {
42
+ return readdirSync(dir).join(",");
43
+ }
44
+ catch {
45
+ return "<unreadable>";
46
+ }
47
+ }
48
+ function isPhase(value) {
49
+ return (typeof value === "string" &&
50
+ ["implementation", "review_pending", "review", "review_fixes", "complete"].includes(value));
51
+ }
52
+ /** Repair a state that was read from disk. */
53
+ export function normalizeState(value, taskId) {
54
+ const raw = (value ?? {});
55
+ return {
56
+ phase: isPhase(raw.phase) ? raw.phase : "implementation",
57
+ taskId: typeof raw.taskId === "string" && raw.taskId ? raw.taskId : taskId,
58
+ startRevision: typeof raw.startRevision === "string" ? raw.startRevision : null,
59
+ scopeFiles: Array.isArray(raw.scopeFiles) ? raw.scopeFiles.filter((f) => typeof f === "string") : [],
60
+ chunks: Array.isArray(raw.chunks) ? raw.chunks.filter(isChunk) : [],
61
+ signals: Array.isArray(raw.signals) ? raw.signals.filter(isSignal) : [],
62
+ findings: Array.isArray(raw.findings) ? raw.findings.filter(isFinding) : [],
63
+ reviewTodoSubject: typeof raw.reviewTodoSubject === "string" ? raw.reviewTodoSubject : undefined,
64
+ reviewTodoDescription: typeof raw.reviewTodoDescription === "string" ? raw.reviewTodoDescription : undefined,
65
+ updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : new Date().toISOString(),
66
+ };
67
+ }
68
+ /** Is the chunk of a persisted state usable? */
69
+ function isChunk(value) {
70
+ if (!value || typeof value !== "object")
71
+ return false;
72
+ const chunk = value;
73
+ return (typeof chunk.id === "string" &&
74
+ typeof chunk.label === "string" &&
75
+ Array.isArray(chunk.files) &&
76
+ chunk.files.every((file) => typeof file === "string") &&
77
+ (chunk.status === "pending" || chunk.status === "done"));
78
+ }
79
+ /** Is the signal of a persisted state usable? */
80
+ function isSignal(value) {
81
+ if (!value || typeof value !== "object")
82
+ return false;
83
+ const signal = value;
84
+ return (typeof signal.source === "string" &&
85
+ typeof signal.reason === "string" &&
86
+ Array.isArray(signal.files) &&
87
+ signal.files.every((file) => typeof file === "string"));
88
+ }
89
+ /** Is the finding of a persisted state usable? */
90
+ function isFinding(value) {
91
+ if (!value || typeof value !== "object")
92
+ return false;
93
+ const finding = value;
94
+ return typeof finding.id === "string" && typeof finding.summary === "string";
95
+ }
96
+ /** The state of one task on disk. */
97
+ export class TwoPhaseStateStore {
98
+ dir;
99
+ constructor(dir = TWO_PHASE_STATE_DIR) {
100
+ this.dir = dir;
101
+ }
102
+ /** The file the state of the task lives in. */
103
+ path(taskId) {
104
+ return join(this.dir, `${safeFileName(taskId)}.json`);
105
+ }
106
+ /**
107
+ * Read the state of the task.
108
+ *
109
+ * `null` means that there is no state. A state that exists but cannot be
110
+ * read is an error: it must not be mistaken for a fresh task, because that
111
+ * would lose the revision the task started at and the progress of the
112
+ * review.
113
+ */
114
+ read(taskId) {
115
+ const path = this.path(taskId);
116
+ if (!existsSync(path))
117
+ return null;
118
+ let content;
119
+ try {
120
+ content = readFileSync(path, "utf8");
121
+ }
122
+ catch (error) {
123
+ throw new Error(`the state file could not be read: ${error.message}; path=${path}`, { cause: error });
124
+ }
125
+ let parsed;
126
+ try {
127
+ parsed = JSON.parse(content);
128
+ }
129
+ catch (error) {
130
+ throw new Error(`the state file is not valid JSON: ${error.message}; path=${path}`, { cause: error });
131
+ }
132
+ if (!parsed || typeof parsed !== "object") {
133
+ throw new Error(`the state file does not contain a state: path=${path}`);
134
+ }
135
+ return normalizeState(parsed, taskId);
136
+ }
137
+ /**
138
+ * Write the state of the task.
139
+ *
140
+ * A failing write is reported to the caller: the runtime decides whether
141
+ * the session may continue without the state, the test sees the reason.
142
+ */
143
+ write(state) {
144
+ const next = { ...state, updatedAt: new Date().toISOString() };
145
+ mkdirSync(this.dir, { recursive: true });
146
+ const path = this.path(next.taskId);
147
+ // Every writer uses its own temporary file, so two writers cannot
148
+ // rename the content of each other.
149
+ const temporary = `${path}.${process.pid}.${writeCounter++}.tmp`;
150
+ try {
151
+ writeFileSync(temporary, JSON.stringify(next, null, 2), "utf8");
152
+ renameSync(temporary, path);
153
+ }
154
+ catch (error) {
155
+ throw new Error(`the state file could not be written: ${error.message}; dir=${this.dir}; entries=${listDir(this.dir)}`, { cause: error });
156
+ }
157
+ return next;
158
+ }
159
+ /** Remove the state of the task. */
160
+ clear(taskId) {
161
+ try {
162
+ rmSync(this.path(taskId), { force: true });
163
+ }
164
+ catch {
165
+ // Nothing to remove.
166
+ }
167
+ }
168
+ }
169
+ /** Move to a phase, never backwards. */
170
+ export function moveToPhase(state, phase) {
171
+ if (PHASE_ORDER[phase] <= PHASE_ORDER[state.phase])
172
+ return state;
173
+ return { ...state, phase };
174
+ }
175
+ /** Record a review signal. The phase does not change. */
176
+ export function recordSignal(state, signal) {
177
+ const key = `${signal.source}::${[...signal.files].sort().join(",")}::${signal.reason
178
+ .trim()
179
+ .toLowerCase()}`;
180
+ if (state.signals.some((entry) => `${entry.source}::${[...entry.files]
181
+ .sort()
182
+ .join(",")}::${entry.reason.trim().toLowerCase()}` === key)) {
183
+ return state;
184
+ }
185
+ return { ...state, signals: [...state.signals, signal] };
186
+ }
187
+ /** The implementation is done: the review waits for its scope. */
188
+ export function transitionToReviewPending(state, scopeFiles) {
189
+ return { ...moveToPhase(state, "review_pending"), scopeFiles };
190
+ }
191
+ /** The review starts with the given chunks. */
192
+ export function startReview(state, chunks) {
193
+ return { ...moveToPhase(state, "review"), chunks };
194
+ }
195
+ /** Mark a review chunk as finished. */
196
+ export function markChunkDone(state, chunkId) {
197
+ const chunks = state.chunks.map((chunk) => chunk.id === chunkId ? { ...chunk, status: "done" } : chunk);
198
+ return { ...state, chunks };
199
+ }
200
+ /** Record a confirmed finding. */
201
+ export function recordFinding(state, finding) {
202
+ return { ...state, findings: [...state.findings, finding] };
203
+ }
204
+ /**
205
+ * The review found something: the fixes are now the work.
206
+ *
207
+ * The fixes start when the review is finished: a fix phase before the review
208
+ * ran would hide the work that is still open.
209
+ */
210
+ export function transitionToReviewFixes(state) {
211
+ if (state.phase !== "review")
212
+ return state;
213
+ if (!allChunksDone(state))
214
+ return state;
215
+ return moveToPhase(state, "review_fixes");
216
+ }
217
+ /** Every chunk is finished. */
218
+ export function allChunksDone(state) {
219
+ return state.chunks.length > 0 && state.chunks.every((chunk) => chunk.status === "done");
220
+ }
221
+ /**
222
+ * The work is finished.
223
+ *
224
+ * The task is only complete when the review ran over every chunk. The
225
+ * implementation phase never completes the task on its own.
226
+ */
227
+ export function markComplete(state) {
228
+ if (state.phase === "implementation" || state.phase === "review_pending")
229
+ return state;
230
+ if (!allChunksDone(state))
231
+ return state;
232
+ return moveToPhase(state, "complete");
233
+ }
234
+ /** Is a review queued? */
235
+ export function hasQueuedReview(state) {
236
+ return state.signals.length > 0;
237
+ }
238
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Types of the two-phase implementation and review workflow.
3
+ *
4
+ * The workflow keeps two phases apart: the implementation of the requested
5
+ * work (Phase 1) and the comprehensive review of that work (Phase 2). A
6
+ * review signal that arrives during Phase 1 is recorded, never executed.
7
+ */
8
+ /** The phase the task currently is in. */
9
+ export type TwoPhasePhase = "implementation" | "review_pending" | "review" | "review_fixes" | "complete";
10
+ /** What a todo item represents. */
11
+ export type TodoKind = "implement" | "review" | "review_fix" | "other";
12
+ /** A "review now" signal that arrived while the implementation was running. */
13
+ export interface ReviewSignal {
14
+ /** Who raised the signal (tool name, agent name, command). */
15
+ source: string;
16
+ /** The files the signal points at (best effort). */
17
+ files: string[];
18
+ /** Why the signal was raised, in one short line. */
19
+ reason: string;
20
+ /** ISO timestamp of the signal. */
21
+ at: string;
22
+ }
23
+ /** One review unit: a logical scope of the task diff. */
24
+ export interface ReviewChunk {
25
+ id: string;
26
+ label: string;
27
+ files: string[];
28
+ status: "pending" | "done";
29
+ }
30
+ /** A confirmed finding of the review. */
31
+ export interface ReviewFinding {
32
+ id: string;
33
+ severity: "low" | "medium" | "high";
34
+ summary: string;
35
+ chunkId?: string;
36
+ /** A finding the user accepted as a known issue. */
37
+ accepted?: boolean;
38
+ at: string;
39
+ }
40
+ /** The persisted state of the workflow for one task. */
41
+ export interface TwoPhaseState {
42
+ phase: TwoPhasePhase;
43
+ /** The task the state belongs to (the session id). */
44
+ taskId: string;
45
+ /** The revision the task started at: the review scope is the diff since then. */
46
+ startRevision: string | null;
47
+ /** The files the review covers. */
48
+ scopeFiles: string[];
49
+ /** The review chunks the scope was split into. */
50
+ chunks: ReviewChunk[];
51
+ /** Every review signal that was recorded (deduplicated). */
52
+ signals: ReviewSignal[];
53
+ /** The findings the review produced. */
54
+ findings: ReviewFinding[];
55
+ /** The subject of the queued review todo (one item, not many). */
56
+ reviewTodoSubject?: string;
57
+ /** The description of the queued review todo. */
58
+ reviewTodoDescription?: string;
59
+ /** ISO timestamp of the last write. */
60
+ updatedAt: string;
61
+ }
62
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,0CAA0C;AAC1C,MAAM,MAAM,aAAa,GACtB,gBAAgB,GAChB,gBAAgB,GAChB,QAAQ,GACR,cAAc,GACd,UAAU,CAAC;AAEd,mCAAmC;AACnC,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,YAAY,GAAG,OAAO,CAAC;AAEvE,+EAA+E;AAC/E,MAAM,WAAW,YAAY;IAC5B,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,EAAE,EAAE,MAAM,CAAC;CACX;AAED,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,MAAM,CAAC;CAC3B;AAED,yCAAyC;AACzC,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;CACX;AAED,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,aAAa,CAAC;IACrB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mCAAmC;IACnC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,kDAAkD;IAClD,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,4DAA4D;IAC5D,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,wCAAwC;IACxC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,kEAAkE;IAClE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iDAAiD;IACjD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;CAClB"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Types of the two-phase implementation and review workflow.
3
+ *
4
+ * The workflow keeps two phases apart: the implementation of the requested
5
+ * work (Phase 1) and the comprehensive review of that work (Phase 2). A
6
+ * review signal that arrives during Phase 1 is recorded, never executed.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@pi-harness/two-phase-review",
3
+ "version": "1.1.410",
4
+ "description": "pi-harness-runtime — two-phase implementation and review workflow (queue review signals, gate the phase, scope the diff)",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "test": "bun test"
20
+ }
21
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Todo classification for the two-phase workflow.
3
+ *
4
+ * Implementation work and queued review work must never be confused. The
5
+ * workflow therefore classifies every todo item before it decides whether
6
+ * the implementation phase is over.
7
+ */
8
+
9
+ import type { TodoKind } from "./types.js";
10
+
11
+ /** Prefix of a todo item that is implementation work. */
12
+ export const IMPLEMENT_PREFIX = "[IMPLEMENT]";
13
+ /** Prefix of a todo item that is the queued review. */
14
+ export const REVIEW_PREFIX = "[REVIEW]";
15
+ /** Prefix of a todo item that fixes a confirmed review finding. */
16
+ export const REVIEW_FIX_PREFIX = "[REVIEW-FIX]";
17
+
18
+ /** The part of a todo item the workflow needs. */
19
+ export interface TodoLike {
20
+ id?: number;
21
+ subject?: string;
22
+ status?: string;
23
+ metadata?: Record<string, unknown>;
24
+ }
25
+
26
+ const KIND_BY_NAME: Record<string, TodoKind> = {
27
+ implement: "implement",
28
+ implementation: "implement",
29
+ impl: "implement",
30
+ review: "review",
31
+ review_pending: "review",
32
+ "review-pending": "review",
33
+ review_fix: "review_fix",
34
+ "review-fix": "review_fix",
35
+ reviewfix: "review_fix",
36
+ other: "other",
37
+ };
38
+
39
+ /**
40
+ * Classify a todo item.
41
+ *
42
+ * The metadata wins over the title: an explicit kind is more reliable than a
43
+ * prefix. A `[REVIEW-FIX]` title is a fix item, not a review item, so the fix
44
+ * prefix is checked first.
45
+ */
46
+ export function classifyTodo(todo: TodoLike | null | undefined): TodoKind {
47
+ if (!todo) return "other";
48
+
49
+ const metadata = todo.metadata ?? {};
50
+ const declared = metadata.kind ?? metadata.todoKind;
51
+ if (typeof declared === "string") {
52
+ const key = declared.toLowerCase().trim().replace(/[\s-]+/g, "_");
53
+ const mapped = KIND_BY_NAME[key];
54
+ if (mapped) return mapped;
55
+ }
56
+
57
+ const subject = (todo.subject ?? "").trim().toUpperCase();
58
+ if (subject.startsWith(REVIEW_FIX_PREFIX)) return "review_fix";
59
+ if (subject.startsWith(IMPLEMENT_PREFIX)) return "implement";
60
+ if (subject.startsWith(REVIEW_PREFIX)) return "review";
61
+ return "other";
62
+ }
63
+
64
+ /** Is the todo item still open? */
65
+ export function isOpenTodo(todo: TodoLike): boolean {
66
+ return todo.status === "pending" || todo.status === "in_progress";
67
+ }
68
+
69
+ /** Every open todo item. */
70
+ export function openTodos(todos: TodoLike[]): TodoLike[] {
71
+ return todos.filter((todo) => isOpenTodo(todo));
72
+ }
73
+
74
+ /**
75
+ * The open todo items that are still implementation work.
76
+ *
77
+ * A queued review item is not implementation work: it must never block the
78
+ * implementation. An unclassified item counts as implementation work, because
79
+ * a task that is tracked without a prefix is still work that has to finish.
80
+ */
81
+ export function implementationTodos(todos: TodoLike[]): TodoLike[] {
82
+ return openTodos(todos).filter((todo) => {
83
+ const kind = classifyTodo(todo);
84
+ return kind === "implement" || kind === "other";
85
+ });
86
+ }
87
+
88
+ /** The open todo items that represent the queued review. */
89
+ export function reviewTodos(todos: TodoLike[]): TodoLike[] {
90
+ return openTodos(todos).filter((todo) => classifyTodo(todo) === "review");
91
+ }
92
+
93
+ /** The open todo items that fix a confirmed review finding. */
94
+ export function reviewFixTodos(todos: TodoLike[]): TodoLike[] {
95
+ return openTodos(todos).filter((todo) => classifyTodo(todo) === "review_fix");
96
+ }
97
+
98
+ /** Does the task still have implementation work? */
99
+ export function hasOpenImplementationWork(todos: TodoLike[]): boolean {
100
+ return implementationTodos(todos).length > 0;
101
+ }
102
+
103
+ /** How many todo items of each kind are open. */
104
+ export function summarizeTodos(todos: TodoLike[]): {
105
+ implementation: number;
106
+ review: number;
107
+ reviewFix: number;
108
+ } {
109
+ return {
110
+ implementation: implementationTodos(todos).length,
111
+ review: reviewTodos(todos).length,
112
+ reviewFix: reviewFixTodos(todos).length,
113
+ };
114
+ }
115
+
116
+ const PREFIX_BY_KIND: Record<TodoKind, string> = {
117
+ implement: IMPLEMENT_PREFIX,
118
+ review: REVIEW_PREFIX,
119
+ review_fix: REVIEW_FIX_PREFIX,
120
+ other: "",
121
+ };
122
+
123
+ /** Prefix a title with the kind of its todo item. */
124
+ export function buildTodoSubject(kind: TodoKind, text: string): string {
125
+ const prefix = PREFIX_BY_KIND[kind];
126
+ const clean = text.trim();
127
+ if (!prefix) return clean;
128
+ return clean.toUpperCase().startsWith(prefix) ? clean : `${prefix} ${clean}`;
129
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Two-phase implementation and review workflow.
3
+ *
4
+ * Phase 1 finishes the implementation todos of a request. Phase 2 reviews the
5
+ * diff of that request. A review signal that arrives during Phase 1 is
6
+ * recorded and deferred, never executed between two implementation todos.
7
+ */
8
+
9
+ export type {
10
+ TwoPhasePhase,
11
+ TodoKind,
12
+ ReviewSignal,
13
+ ReviewChunk,
14
+ ReviewFinding,
15
+ TwoPhaseState,
16
+ } from "./types.js";
17
+
18
+ export {
19
+ IMPLEMENT_PREFIX,
20
+ REVIEW_PREFIX,
21
+ REVIEW_FIX_PREFIX,
22
+ classifyTodo,
23
+ isOpenTodo,
24
+ openTodos,
25
+ implementationTodos,
26
+ reviewTodos,
27
+ reviewFixTodos,
28
+ hasOpenImplementationWork,
29
+ summarizeTodos,
30
+ buildTodoSubject,
31
+ } from "./classify.js";
32
+ export type { TodoLike } from "./classify.js";
33
+
34
+ export {
35
+ DEFAULT_REVIEW_TOOL_PATTERNS,
36
+ DEFAULT_REVIEW_COMMAND_PATTERNS,
37
+ parseExtraPatterns,
38
+ isComprehensiveReviewTool,
39
+ isReviewCommand,
40
+ dispatchAgentName,
41
+ detectReviewInvocation,
42
+ explicitReviewRequest,
43
+ extractFiles,
44
+ signalKey,
45
+ mergeSignals,
46
+ summarizeSignals,
47
+ } from "./signals.js";
48
+ export type { ReviewInvocation, SignalSummary } from "./signals.js";
49
+
50
+ export {
51
+ EXCLUDED_SCOPE_PREFIXES,
52
+ DEFAULT_CHUNK_SIZE,
53
+ isReviewablePath,
54
+ isGitRepository,
55
+ currentRevision,
56
+ revisionExists,
57
+ moduleKey,
58
+ countModules,
59
+ parseNumstat,
60
+ collectScope,
61
+ chunkScope,
62
+ shouldChunkScope,
63
+ } from "./scope.js";
64
+ export type { ScopeMetrics, ScopeResult } from "./scope.js";
65
+
66
+ export {
67
+ TWO_PHASE_STATE_DIR,
68
+ createState,
69
+ safeFileName,
70
+ normalizeState,
71
+ TwoPhaseStateStore,
72
+ moveToPhase,
73
+ recordSignal,
74
+ transitionToReviewPending,
75
+ startReview,
76
+ markChunkDone,
77
+ recordFinding,
78
+ transitionToReviewFixes,
79
+ allChunksDone,
80
+ markComplete,
81
+ hasQueuedReview,
82
+ } from "./state.js";
83
+
84
+ export {
85
+ TWO_PHASE_CONTRACT,
86
+ buildPhaseView,
87
+ buildPhaseInstruction,
88
+ buildReviewTodoSubject,
89
+ buildReviewTodoDescription,
90
+ buildChunkTodoSubject,
91
+ buildChunkTodoDescription,
92
+ isChunkTodo,
93
+ syncChunkStatus,
94
+ formatPhaseStatus,
95
+ buildDeferralReason,
96
+ } from "./rules.js";
97
+ export type { PhaseView } from "./rules.js";
98
+
99
+ export {
100
+ HARNESS_MESSAGE_MARKER,
101
+ initTwoPhaseReview,
102
+ } from "./runtime.js";
103
+ export type {
104
+ TwoPhaseExtensionApi,
105
+ TwoPhaseExtensionContext,
106
+ TwoPhaseRuntime,
107
+ TwoPhaseRuntimeOptions,
108
+ TodoReadResult,
109
+ TodoReader,
110
+ } from "./runtime.js";