@tea-agent/loop-agent 0.33.6 → 0.33.7-beta.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +23 -23
  2. package/dist/application/task-lifecycle/advance.js +4 -254
  3. package/dist/application/task-lifecycle/gates.js +0 -50
  4. package/dist/application/task-lifecycle/observe.js +2 -11
  5. package/dist/commands/init-upgrade.js +1 -32
  6. package/dist/commands/init.js +3 -94
  7. package/dist/executors/shell-executor.js +91 -4
  8. package/dist/executors/shell-write-guard.js +8 -26
  9. package/dist/shared/operator/capabilities.js +42 -72
  10. package/dist/task/source-prepare/index.js +0 -2
  11. package/dist/task/source-prepare/parse-intent.js +10 -58
  12. package/dist/task/source-prepare/prepare.js +16 -180
  13. package/dist/task/source-prepare/reference-integrity.js +2 -18
  14. package/dist/worker/console/app-data.js +0 -2
  15. package/dist/worker/console/chat/chat-event-store.js +25 -190
  16. package/dist/worker/console/chat/instruction-skills.js +217 -0
  17. package/dist/worker/console/chat/pi-console-config.js +32 -250
  18. package/dist/worker/console/chat/pi-runtime.js +71 -625
  19. package/dist/worker/console/chat/resource-loader.js +4 -5
  20. package/dist/worker/console/chat/routes.js +146 -324
  21. package/dist/worker/console/chat/runtime-context.js +12 -48
  22. package/dist/worker/console/chat/runtime-selection.js +0 -59
  23. package/dist/worker/console/chat/shortcuts.js +0 -1
  24. package/dist/worker/console/chat/tool-adapter.js +3 -9
  25. package/dist/worker/console/chat/tools.js +1 -5
  26. package/dist/worker/console/operator-actions.js +68 -559
  27. package/dist/worker/console/server.js +15 -8
  28. package/dist/worker/console/static/assets/index-CnUXAqxG.css +1 -0
  29. package/dist/worker/console/static/assets/index-CteJFFL2.js +29 -0
  30. package/dist/worker/console/static/index.html +2 -2
  31. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +8 -45
  32. package/dist/worker/console/static-src/operator-chat/refs.js +0 -9
  33. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +0 -16
  34. package/dist/worker/console/static-src/operator-chat/useChatStream.js +184 -210
  35. package/dist/worker/console/static-src/operator-chat/useChatThread.js +5 -49
  36. package/dist/worker/console/static-src/operator-chat/useComposer.js +0 -17
  37. package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +74 -225
  38. package/dist/worker/delivery/final-verification.js +5 -13
  39. package/dist/worker/delivery/package.js +19 -31
  40. package/dist/worker/delivery/verification-bundle.js +4 -6
  41. package/dist/worker/observe/static/operator-chrome.css +2 -5
  42. package/dist/worker/observe/static/operator-chrome.js +1 -6
  43. package/dist/worker/observe/static/styles.css +9 -39
  44. package/dist/workflows/dag/backend-test-case-coverage-analysis.js +462 -33
  45. package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
  46. package/dist/workflows/dag/backend-test-markdown-workflow.js +25 -1
  47. package/dist/workflows/dag/backend-test-module-stem.js +5 -0
  48. package/dist/workflows/dag/backend-test-pytest-collection.js +345 -24
  49. package/dist/workflows/dag/backend-test-scenario-param.js +269 -82
  50. package/dist/workflows/dag/backend-test-writer-completeness.js +47 -16
  51. package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
  52. package/dist/workflows/dag/frontend-worktree-diff.js +27 -12
  53. package/dist/workflows/dag/init-hybrid.js +46 -34
  54. package/dist/workflows/dag/types.js +7 -0
  55. package/dist/workflows/dag/workspace-checkpoint.js +27 -8
  56. package/docs/templates/backend-test-dag.json +32 -29
  57. package/harness.json +1 -1
  58. package/package.json +1 -1
  59. package/skills/loop-agent/references/command-reference.md +1 -3
  60. package/skills/loop-agent/references/source-and-plan-practice.md +0 -13
  61. package/skills/loop-agent/references/task-workflow.md +0 -4
  62. package/dist/shared/resilient-git.js +0 -133
  63. package/dist/task/source-prepare/artifact-meta.js +0 -137
  64. package/dist/task/source-prepare/semantic-intake.js +0 -404
  65. package/dist/worker/console/dag-execution-receipt.js +0 -380
  66. package/dist/worker/console/static/assets/index-BUOLppPr.js +0 -28
  67. package/dist/worker/console/static/assets/index-C1KzazY5.css +0 -1
  68. package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +0 -257
  69. package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +0 -196
@@ -1,380 +0,0 @@
1
- /**
2
- * DAG execution receipt — session-bound, TTL, single-use autonomous DAG
3
- * execution (2026-08-11 design §5 / §8.3).
4
- *
5
- * `prepareDagExecution` issues an execution receipt ONLY when the server-side
6
- * G2 autonomous-execution assessment passes (bounded writeSet ⊆ allowedPaths,
7
- * no forbidden overlap, no broad/destructive risk, structured verification
8
- * present, valid bindings). The receipt carries no human-gate token — the
9
- * browser Human Gate is not part of this flow.
10
- *
11
- * `runDag` consumes the receipt exactly once. Consumption re-reads the staged
12
- * DAG bytes/hash and re-validates every binding (dag hash, source binding,
13
- * task contract binding, controller fingerprint, validation result); any
14
- * expiry, cross-session use, replay, or drift fails closed. Error codes reuse
15
- * the existing confirmation lifecycle codes (CONFIRMATION_* /
16
- * HUMAN_CONFIRMATION_REQUIRED / NOT_FOUND) because registry error codes are
17
- * immutable in this task.
18
- */
19
- import { randomBytes } from "node:crypto";
20
- import { readFile } from "node:fs/promises";
21
- import path from "node:path";
22
- import { pathMatchesPattern } from "../../shared/git-progress.js";
23
- import { readJsonIfExists, sha256Utf8, writeSecureJson, } from "./app-data.js";
24
- import { hashDagBytes, } from "./dag-confirmation.js";
25
- export const DEFAULT_EXECUTION_RECEIPT_TTL_MS = 30 * 60 * 1000;
26
- function normalizePathEntry(entry) {
27
- return entry.trim().replace(/\\/g, "/").replace(/^\.\//, "");
28
- }
29
- function isBroadWriteSetEntry(entry) {
30
- const normalized = normalizePathEntry(entry);
31
- if (!normalized || normalized === "." || normalized === "./")
32
- return true;
33
- if (normalized === "**")
34
- return true;
35
- // Placeholder marker (`REPLACE/WITH`) means the writeSet is not concrete yet.
36
- return normalized.includes("REPLACE/WITH");
37
- }
38
- function isContainedInAllowed(candidate, allowedPaths) {
39
- const normalized = normalizePathEntry(candidate);
40
- if (!normalized)
41
- return false;
42
- return allowedPaths.some((allowed) => {
43
- const pattern = normalizePathEntry(allowed);
44
- if (!pattern || pattern === "." || pattern === "./")
45
- return false;
46
- return pathMatchesPattern(normalized, pattern);
47
- });
48
- }
49
- function overlapsForbidden(candidate, forbiddenPaths) {
50
- const normalized = normalizePathEntry(candidate);
51
- if (!normalized)
52
- return true;
53
- return forbiddenPaths.some((forbidden) => {
54
- const pattern = normalizePathEntry(forbidden);
55
- if (!pattern)
56
- return false;
57
- return (pathMatchesPattern(normalized, pattern) ||
58
- pathMatchesPattern(pattern, normalized));
59
- });
60
- }
61
- /**
62
- * G2 deterministic autonomous-execution assessment (design §5.2). Pure and
63
- * exported so the dispatcher and unit tests share the exact same code path.
64
- * Every condition must pass — any missing fact fails closed.
65
- */
66
- export function assessAutonomousExecutionEligibility(input) {
67
- const dangerous = input.dangerousMarkers ?? {};
68
- if (dangerous.credentials || dangerous.destructive || dangerous.irreversible) {
69
- return {
70
- ok: false,
71
- reason: "DAG involves credentials, destructive operations, or irreversible migration; human authorization required",
72
- };
73
- }
74
- if (!input.hasStructuredVerification) {
75
- return {
76
- ok: false,
77
- reason: "DAG has no structured shell verification; autonomous execution not permitted",
78
- };
79
- }
80
- if (input.broadWriteSetRisk) {
81
- return {
82
- ok: false,
83
- reason: "writeSet contains broad/placeholder entries; autonomous execution not permitted",
84
- };
85
- }
86
- if (input.forbiddenOverlapRisk) {
87
- return {
88
- ok: false,
89
- reason: "writeSet overlaps forbidden paths; autonomous execution not permitted",
90
- };
91
- }
92
- if (!Array.isArray(input.allowedPaths) || !Array.isArray(input.forbiddenPaths)) {
93
- return {
94
- ok: false,
95
- reason: "task boundary (allowedPaths/forbiddenPaths) unavailable; autonomous execution not permitted",
96
- };
97
- }
98
- for (const writer of input.writersWriteSets ?? []) {
99
- if (!Array.isArray(writer.writeSet))
100
- continue;
101
- for (const entry of writer.writeSet) {
102
- if (!isContainedInAllowed(entry, input.allowedPaths)) {
103
- return {
104
- ok: false,
105
- reason: `writer ${writer.nodeId} writeSet entry "${entry}" is outside allowedPaths`,
106
- };
107
- }
108
- if (overlapsForbidden(entry, input.forbiddenPaths)) {
109
- return {
110
- ok: false,
111
- reason: `writer ${writer.nodeId} writeSet entry "${entry}" overlaps forbiddenPaths`,
112
- };
113
- }
114
- }
115
- }
116
- return { ok: true };
117
- }
118
- /**
119
- * Extract the deterministic G2 facts from a generated DAG spec (staged bytes).
120
- * Mirrors the CLI review-packet semantics (generate-task-dag.ts) so the
121
- * console never trusts client-supplied claims. Missing facts fail closed.
122
- */
123
- export function deriveEligibilityFactsFromDagSpec(input) {
124
- const spec = (input.dagSpec ?? {});
125
- const tasks = Array.isArray(spec.tasks) ? spec.tasks : [];
126
- const defaultWritePolicy = spec.defaults?.writePolicy;
127
- const resolvePolicy = (task) => task.writePolicy ?? defaultWritePolicy ?? "none";
128
- const stringList = (value) => Array.isArray(value)
129
- ? value.filter((v) => typeof v === "string")
130
- : [];
131
- const writers = tasks
132
- .filter((task) => resolvePolicy(task) === "exclusive" &&
133
- stringList(task.writeSet).length > 0)
134
- .map((task) => ({
135
- nodeId: task.id ?? "unknown",
136
- writeSet: stringList(task.writeSet),
137
- }));
138
- const allWriteSetEntries = writers.flatMap((writer) => writer.writeSet);
139
- const broadWriteSetRisk = allWriteSetEntries.some(isBroadWriteSetEntry);
140
- const forbiddenOverlapRisk = tasks.some((task) => stringList(task.writeSet).some((entry) => [...stringList(task.forbiddenPaths), ...input.forbiddenPaths].some((forbidden) => pathMatchesPattern(entry, forbidden) ||
141
- pathMatchesPattern(forbidden, entry))));
142
- const hasStructuredVerification = tasks.some((task) => {
143
- if (task.executor !== "shell" || !task.shell)
144
- return false;
145
- if (/verify|verification/i.test(task.id ?? ""))
146
- return true;
147
- const commands = stringList(task.shell.commands);
148
- return commands.some((command) => /(vitest|npm run (lint|typecheck|test)|check-repo\.sh|loop-agent-standard-verify)/.test(command));
149
- });
150
- return {
151
- writersWriteSets: writers,
152
- allowedPaths: input.allowedPaths,
153
- forbiddenPaths: input.forbiddenPaths,
154
- broadWriteSetRisk,
155
- forbiddenOverlapRisk,
156
- hasStructuredVerification,
157
- };
158
- }
159
- function receiptPath(appData, executionId) {
160
- const dir = appData.dagReceipts ??
161
- path.join(path.dirname(appData.confirmations), "dag-receipts");
162
- return path.join(dir, `${executionId}.json`);
163
- }
164
- export function newExecutionId() {
165
- return `exec_${randomBytes(12).toString("hex")}`;
166
- }
167
- export class DagExecutionReceiptStore {
168
- appData;
169
- constructor(appData) {
170
- this.appData = appData;
171
- }
172
- async get(executionId) {
173
- const rec = await readJsonIfExists(receiptPath(this.appData, executionId));
174
- if (!rec)
175
- return undefined;
176
- return this.materializeExpiry(rec);
177
- }
178
- async materializeExpiry(rec, now = Date.now()) {
179
- if (rec.state === "prepared" && Date.parse(rec.expiresAt) <= now) {
180
- const expired = {
181
- ...rec,
182
- state: "expired",
183
- staleReason: rec.staleReason ?? "ttl exceeded (30min default)",
184
- };
185
- await writeSecureJson(receiptPath(this.appData, rec.executionId), expired);
186
- return expired;
187
- }
188
- return rec;
189
- }
190
- /**
191
- * Issue a single-use execution receipt. G2 eligibility is re-checked here
192
- * (fail closed) in addition to the dispatcher's pre-check.
193
- */
194
- async prepare(input) {
195
- const assessment = assessAutonomousExecutionEligibility(input.eligibility);
196
- if (!assessment.ok) {
197
- return {
198
- ok: false,
199
- code: "HUMAN_CONFIRMATION_REQUIRED",
200
- reason: assessment.reason,
201
- };
202
- }
203
- if (!input.dagBytes ||
204
- !input.dagBytesPath ||
205
- !input.sourceBindingSha256 ||
206
- !input.controllerFingerprint ||
207
- !input.validationResultSha256 ||
208
- !input.taskContractBinding?.taskId) {
209
- return {
210
- ok: false,
211
- code: "INVALID_INPUT",
212
- reason: "missing server-derived DAG/binding facts for execution receipt",
213
- };
214
- }
215
- const now = input.now ?? new Date();
216
- const ttl = input.ttlMs ?? DEFAULT_EXECUTION_RECEIPT_TTL_MS;
217
- const executionId = newExecutionId();
218
- const rec = {
219
- schemaVersion: 1,
220
- executionId,
221
- state: "prepared",
222
- preparedAt: now.toISOString(),
223
- expiresAt: new Date(now.getTime() + ttl).toISOString(),
224
- operatorSessionId: input.operatorSessionId,
225
- taskId: input.taskId,
226
- dagHandle: input.dagHandle,
227
- dagSha256: hashDagBytes(input.dagBytes),
228
- dagBytesPath: input.dagBytesPath,
229
- sourceBindingSha256: input.sourceBindingSha256,
230
- taskContractBinding: input.taskContractBinding,
231
- controllerFingerprint: input.controllerFingerprint,
232
- validationResultSha256: input.validationResultSha256,
233
- reviewPacket: input.reviewPacket,
234
- };
235
- await writeSecureJson(receiptPath(this.appData, executionId), rec);
236
- return { ok: true, receipt: rec };
237
- }
238
- /**
239
- * Atomic single-use consume for runDag. Re-reads the staged DAG bytes and
240
- * re-validates every binding; any drift/expiry/session/replay fails closed.
241
- */
242
- async consume(input) {
243
- const rec = await this.get(input.executionId);
244
- if (!rec) {
245
- return {
246
- ok: false,
247
- code: "NOT_FOUND",
248
- message: `execution receipt not found: ${input.executionId}`,
249
- };
250
- }
251
- if (rec.state === "expired") {
252
- return {
253
- ok: false,
254
- code: "CONFIRMATION_EXPIRED",
255
- message: "execution receipt expired",
256
- receipt: rec,
257
- };
258
- }
259
- if (rec.state === "consumed") {
260
- return {
261
- ok: false,
262
- code: "CONFIRMATION_CONSUMED",
263
- message: "execution receipt already consumed (single-use)",
264
- receipt: rec,
265
- };
266
- }
267
- if (rec.state === "stale") {
268
- return {
269
- ok: false,
270
- code: "CONFIRMATION_STALE",
271
- message: rec.staleReason ?? "execution receipt is stale",
272
- receipt: rec,
273
- };
274
- }
275
- if (rec.state !== "prepared") {
276
- return {
277
- ok: false,
278
- code: "HUMAN_CONFIRMATION_REQUIRED",
279
- message: `execution receipt is ${rec.state}; re-prepare required`,
280
- receipt: rec,
281
- };
282
- }
283
- if (rec.operatorSessionId !== input.operatorSessionId) {
284
- return {
285
- ok: false,
286
- code: "HUMAN_CONFIRMATION_REQUIRED",
287
- message: "session mismatch; execution receipt cannot be consumed across operator sessions",
288
- receipt: rec,
289
- };
290
- }
291
- // Re-read staged DAG bytes and re-hash (fail closed on any drift).
292
- let dagBytes;
293
- try {
294
- dagBytes = await readFile(rec.dagBytesPath, "utf8");
295
- }
296
- catch (error) {
297
- const stale = await this.markStale(rec.executionId, `cannot re-read staged DAG bytes: ${error instanceof Error ? error.message : String(error)}`);
298
- return {
299
- ok: false,
300
- code: "CONFIRMATION_STALE",
301
- message: "cannot re-read staged DAG bytes",
302
- receipt: stale,
303
- };
304
- }
305
- const dagSha256 = hashDagBytes(dagBytes);
306
- if (dagSha256 !== rec.dagSha256) {
307
- const stale = await this.markStale(rec.executionId, "staged DAG bytes drift (hash mismatch on consume)");
308
- return {
309
- ok: false,
310
- code: "CONFIRMATION_STALE",
311
- message: "staged DAG bytes drift (hash mismatch on consume)",
312
- receipt: stale,
313
- };
314
- }
315
- const drift = detectReceiptBindingDrift(rec, {
316
- sourceBindingSha256: input.sourceBindingSha256,
317
- taskContractBinding: input.taskContractBinding,
318
- controllerFingerprint: input.controllerFingerprint,
319
- validationResultSha256: input.validationResultSha256,
320
- });
321
- if (drift) {
322
- const stale = await this.markStale(rec.executionId, drift);
323
- return {
324
- ok: false,
325
- code: "CONFIRMATION_STALE",
326
- message: drift,
327
- receipt: stale,
328
- };
329
- }
330
- const now = input.now ?? new Date();
331
- const consumed = {
332
- ...rec,
333
- state: "consumed",
334
- consumedAt: now.toISOString(),
335
- };
336
- await writeSecureJson(receiptPath(this.appData, rec.executionId), consumed);
337
- return { ok: true, receipt: consumed };
338
- }
339
- /** Record the operation id that consumed this receipt (post-accept). */
340
- async markDispatched(executionId, operationId) {
341
- const rec = await this.get(executionId);
342
- if (!rec || rec.state !== "consumed")
343
- return rec;
344
- const dispatched = {
345
- ...rec,
346
- consumedOperationId: operationId,
347
- };
348
- await writeSecureJson(receiptPath(this.appData, executionId), dispatched);
349
- return dispatched;
350
- }
351
- async markStale(executionId, reason) {
352
- const rec = await this.get(executionId);
353
- if (!rec) {
354
- throw new Error(`execution receipt not found: ${executionId}`);
355
- }
356
- const stale = {
357
- ...rec,
358
- state: "stale",
359
- staleReason: reason,
360
- };
361
- await writeSecureJson(receiptPath(this.appData, executionId), stale);
362
- return stale;
363
- }
364
- }
365
- function detectReceiptBindingDrift(rec, current) {
366
- if (current.sourceBindingSha256 !== rec.sourceBindingSha256) {
367
- return "sourceBindingSha256 binding drift";
368
- }
369
- if (current.controllerFingerprint !== rec.controllerFingerprint) {
370
- return "controllerFingerprint binding drift";
371
- }
372
- if (current.validationResultSha256 !== rec.validationResultSha256) {
373
- return "validationResultSha256 binding drift";
374
- }
375
- const a = sha256Utf8(JSON.stringify(rec.taskContractBinding));
376
- const b = sha256Utf8(JSON.stringify(current.taskContractBinding));
377
- if (a !== b)
378
- return "taskContractBinding drift";
379
- return undefined;
380
- }