@tea-agent/loop-agent 0.28.2-beta.1 → 0.28.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.
Files changed (67) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +25 -0
  3. package/README.md +11 -1
  4. package/dist/cli/command-definitions.js +2 -1
  5. package/dist/commands/client-recovery.js +111 -8
  6. package/dist/commands/dag-init-hybrid.js +1 -1
  7. package/dist/commands/init-upgrade.js +2479 -0
  8. package/dist/commands/init.js +120 -9
  9. package/dist/governance/manifest-types.js +65 -0
  10. package/dist/shared/operator/capabilities.js +350 -2
  11. package/dist/task/worktree.js +256 -39
  12. package/dist/worker/cli.js +22 -12
  13. package/dist/worker/console/chat/workspace-landing.js +16 -6
  14. package/dist/worker/console/observe-health-match.js +2 -0
  15. package/dist/worker/console/observe-link.js +4 -0
  16. package/dist/worker/console/operator-actions.js +183 -4
  17. package/dist/worker/console/operator-selection.js +13 -0
  18. package/dist/worker/console/static/assets/index-BfRgtLF4.js +29 -0
  19. package/dist/worker/console/static/index.html +1 -1
  20. package/dist/worker/observe/health.js +1 -0
  21. package/dist/worker/observe/night-jobs.js +104 -0
  22. package/dist/worker/observe/routes.js +48 -0
  23. package/dist/worker/observe/static/app.js +3 -0
  24. package/dist/worker/observe/static/constants.js +1 -0
  25. package/dist/worker/observe/static/index.html +47 -0
  26. package/dist/worker/observe/static/router.js +10 -0
  27. package/dist/worker/observe/static/shell-chrome.js +1 -0
  28. package/dist/worker/observe/static/views/night.js +201 -0
  29. package/dist/worker/report/morning-report.js +56 -16
  30. package/dist/worker/run-task/execute-prepared-task.js +153 -0
  31. package/dist/worker/runner/single-task-attempt.js +147 -0
  32. package/dist/worker/scheduler/admission.js +536 -0
  33. package/dist/worker/scheduler/auto-followup.js +99 -0
  34. package/dist/worker/scheduler/cli.js +539 -0
  35. package/dist/worker/scheduler/dispatcher.js +503 -0
  36. package/dist/worker/scheduler/doctor.js +346 -0
  37. package/dist/worker/scheduler/evidence.js +170 -0
  38. package/dist/worker/scheduler/git-base.js +52 -0
  39. package/dist/worker/scheduler/index.js +23 -0
  40. package/dist/worker/scheduler/lease.js +114 -0
  41. package/dist/worker/scheduler/lifecycle.js +348 -0
  42. package/dist/worker/scheduler/lock.js +80 -0
  43. package/dist/worker/scheduler/morning-window.js +161 -0
  44. package/dist/worker/scheduler/night-git-finalizer.js +88 -0
  45. package/dist/worker/scheduler/night-harvest.js +421 -0
  46. package/dist/worker/scheduler/paths.js +84 -0
  47. package/dist/worker/scheduler/prepared-attempt-recovery.js +471 -0
  48. package/dist/worker/scheduler/recovery.js +277 -0
  49. package/dist/worker/scheduler/reservation.js +146 -0
  50. package/dist/worker/scheduler/retry.js +199 -0
  51. package/dist/worker/scheduler/scheduler-loop.js +272 -0
  52. package/dist/worker/scheduler/store.js +275 -0
  53. package/dist/worker/scheduler/traceability.js +54 -0
  54. package/dist/worker/scheduler/trigger.js +258 -0
  55. package/dist/worker/scheduler/types.js +369 -0
  56. package/dist/worker/scheduler/workspace-adapter.js +91 -0
  57. package/dist/workflows/dag/frontend-implementation-contract.js +2 -102
  58. package/docs/architecture/runtime-boundaries.md +9 -0
  59. package/docs/init-surface.manifest.json +9 -2
  60. package/docs/templates/harness.schema.json +107 -0
  61. package/docs/templates/init-managed-agents.md +18 -8
  62. package/harness.json +22 -0
  63. package/package.json +1 -1
  64. package/skills/loop-agent/SKILL.md +28 -36
  65. package/skills/loop-agent/references/command-reference.md +40 -16
  66. package/skills/loop-agent/references/hybrid-dag.md +1 -1
  67. package/dist/worker/console/static/assets/index-CNO7n6qB.js +0 -29
@@ -23,6 +23,11 @@ import { issueHumanGateToken, mutationGatePayloadHash, verifyHumanGateToken, } f
23
23
  const MUTATION_GATE_ACTIONS = new Set([
24
24
  "standaloneTaskRerun",
25
25
  "workerTaskRetry",
26
+ "workerAdmissionPrepare",
27
+ "workerSchedulerAdd",
28
+ "workerSchedulerCancel",
29
+ "workerSchedulerHarvest",
30
+ "workerSchedulerDiscard",
26
31
  ]);
27
32
  const MUTATION_OR_LONG = new Set([
28
33
  "newTask",
@@ -382,7 +387,13 @@ export async function dispatchOperatorAction(ctx, req) {
382
387
  const notFound = body.ok === true && snap.exists === false;
383
388
  return {
384
389
  kind: "sync",
385
- status: notFound ? 404 : body.ok ? 200 : body.outcome === "not-found" ? 404 : 400,
390
+ status: notFound
391
+ ? 404
392
+ : body.ok
393
+ ? 200
394
+ : body.outcome === "not-found"
395
+ ? 404
396
+ : 400,
386
397
  body: reshaped,
387
398
  };
388
399
  }
@@ -893,9 +904,8 @@ export async function dispatchOperatorAction(ctx, req) {
893
904
  cliArgs: ["task", "advance", taskId, title, "--json"],
894
905
  });
895
906
  }
896
- case "bootstrapFromPrd": {
907
+ case "bootstrapFromPrd":
897
908
  return dispatchBootstrapFromPrd(ctx, p);
898
- }
899
909
  case "importPrd": {
900
910
  const taskId = str(p.taskId);
901
911
  const content = str(p.content) ?? str(p.fileText);
@@ -1252,6 +1262,175 @@ export async function dispatchOperatorAction(ctx, req) {
1252
1262
  await finalizeMutationGateReceipt(ctx, gated.receiptId, accepted);
1253
1263
  return accepted;
1254
1264
  }
1265
+ case "workerAdmissionPrepare": {
1266
+ const featureDir = str(p.featureDir);
1267
+ const taskId = str(p.taskId);
1268
+ const at = str(p.at);
1269
+ const tz = str(p.tz);
1270
+ const taskCard = str(p.taskCard);
1271
+ const clientRequestId = str(req.clientRequestId);
1272
+ if (!featureDir || !taskId || !at || !clientRequestId) {
1273
+ return invalid(action, "featureDir, taskId, at, and clientRequestId are required");
1274
+ }
1275
+ const gated = await consumeMutationGateReceipt(ctx, action, p);
1276
+ if (gated.kind === "error")
1277
+ return gated;
1278
+ if (gated.kind === "idempotent")
1279
+ return gated.result;
1280
+ const args = [
1281
+ "admission",
1282
+ "prepare",
1283
+ "--repo",
1284
+ ctx.repoRoot,
1285
+ "--feature-dir",
1286
+ featureDir,
1287
+ "--task-id",
1288
+ taskId,
1289
+ "--at",
1290
+ at,
1291
+ "--json",
1292
+ ];
1293
+ if (tz)
1294
+ args.push("--tz", tz);
1295
+ if (taskCard)
1296
+ args.push("--task-card", taskCard);
1297
+ const accepted = await acceptOperation(ctx, {
1298
+ action,
1299
+ actionParams: p,
1300
+ clientRequestId,
1301
+ taskId,
1302
+ cliArgs: args,
1303
+ externalCommand: resolveSiblingAgentWorkerBin(),
1304
+ });
1305
+ await finalizeMutationGateReceipt(ctx, gated.receiptId, accepted);
1306
+ return accepted;
1307
+ }
1308
+ case "workerSchedulerAdd": {
1309
+ const scheduleId = str(p.scheduleId);
1310
+ const approveGate = str(p.approveGate);
1311
+ const clientRequestId = str(req.clientRequestId);
1312
+ if (!scheduleId || !approveGate || !clientRequestId) {
1313
+ return invalid(action, "scheduleId, approveGate, and clientRequestId are required");
1314
+ }
1315
+ const gated = await consumeMutationGateReceipt(ctx, action, p);
1316
+ if (gated.kind === "error")
1317
+ return gated;
1318
+ if (gated.kind === "idempotent")
1319
+ return gated.result;
1320
+ const accepted = await acceptOperation(ctx, {
1321
+ action,
1322
+ actionParams: p,
1323
+ clientRequestId,
1324
+ cliArgs: [
1325
+ "scheduler",
1326
+ "add",
1327
+ scheduleId,
1328
+ "--repo",
1329
+ ctx.repoRoot,
1330
+ "--approve-gate",
1331
+ approveGate,
1332
+ "--json",
1333
+ ],
1334
+ externalCommand: resolveSiblingAgentWorkerBin(),
1335
+ });
1336
+ await finalizeMutationGateReceipt(ctx, gated.receiptId, accepted);
1337
+ return accepted;
1338
+ }
1339
+ case "workerSchedulerCancel": {
1340
+ const scheduleId = str(p.scheduleId);
1341
+ const reason = str(p.reason);
1342
+ const clientRequestId = str(req.clientRequestId);
1343
+ if (!scheduleId || !clientRequestId) {
1344
+ return invalid(action, "scheduleId and clientRequestId are required");
1345
+ }
1346
+ const gated = await consumeMutationGateReceipt(ctx, action, p);
1347
+ if (gated.kind === "error")
1348
+ return gated;
1349
+ if (gated.kind === "idempotent")
1350
+ return gated.result;
1351
+ const args = [
1352
+ "scheduler",
1353
+ "cancel",
1354
+ scheduleId,
1355
+ "--repo",
1356
+ ctx.repoRoot,
1357
+ "--json",
1358
+ ];
1359
+ if (reason)
1360
+ args.push("--reason", reason);
1361
+ const accepted = await acceptOperation(ctx, {
1362
+ action,
1363
+ actionParams: p,
1364
+ clientRequestId,
1365
+ cliArgs: args,
1366
+ externalCommand: resolveSiblingAgentWorkerBin(),
1367
+ });
1368
+ await finalizeMutationGateReceipt(ctx, gated.receiptId, accepted);
1369
+ return accepted;
1370
+ }
1371
+ case "workerSchedulerHarvest": {
1372
+ const scheduleId = str(p.scheduleId);
1373
+ const clientRequestId = str(req.clientRequestId);
1374
+ if (!scheduleId || !clientRequestId) {
1375
+ return invalid(action, "scheduleId and clientRequestId are required");
1376
+ }
1377
+ const gated = await consumeMutationGateReceipt(ctx, action, p);
1378
+ if (gated.kind === "error")
1379
+ return gated;
1380
+ if (gated.kind === "idempotent")
1381
+ return gated.result;
1382
+ const accepted = await acceptOperation(ctx, {
1383
+ action,
1384
+ actionParams: p,
1385
+ clientRequestId,
1386
+ cliArgs: [
1387
+ "scheduler",
1388
+ "harvest",
1389
+ scheduleId,
1390
+ "--repo",
1391
+ ctx.repoRoot,
1392
+ "--json",
1393
+ ],
1394
+ externalCommand: resolveSiblingAgentWorkerBin(),
1395
+ });
1396
+ await finalizeMutationGateReceipt(ctx, gated.receiptId, accepted);
1397
+ return accepted;
1398
+ }
1399
+ case "workerSchedulerDiscard": {
1400
+ const scheduleId = str(p.scheduleId);
1401
+ const reason = str(p.reason);
1402
+ const force = p.force === true;
1403
+ const clientRequestId = str(req.clientRequestId);
1404
+ if (!scheduleId || !reason || !clientRequestId) {
1405
+ return invalid(action, "scheduleId, reason, and clientRequestId are required");
1406
+ }
1407
+ const gated = await consumeMutationGateReceipt(ctx, action, p);
1408
+ if (gated.kind === "error")
1409
+ return gated;
1410
+ if (gated.kind === "idempotent")
1411
+ return gated.result;
1412
+ const args = [
1413
+ "scheduler",
1414
+ "discard",
1415
+ scheduleId,
1416
+ "--repo",
1417
+ ctx.repoRoot,
1418
+ "--reason",
1419
+ reason,
1420
+ "--json",
1421
+ ];
1422
+ if (force)
1423
+ args.push("--force");
1424
+ const accepted = await acceptOperation(ctx, {
1425
+ action,
1426
+ actionParams: p,
1427
+ clientRequestId,
1428
+ cliArgs: args,
1429
+ externalCommand: resolveSiblingAgentWorkerBin(),
1430
+ });
1431
+ await finalizeMutationGateReceipt(ctx, gated.receiptId, accepted);
1432
+ return accepted;
1433
+ }
1255
1434
  case "prepareMutationGate": {
1256
1435
  const targetAction = str(p.action);
1257
1436
  const actionParams = p.actionParams &&
@@ -1263,7 +1442,7 @@ export async function dispatchOperatorAction(ctx, req) {
1263
1442
  return invalid(action, "action and actionParams are required");
1264
1443
  }
1265
1444
  if (!MUTATION_GATE_ACTIONS.has(targetAction)) {
1266
- return invalid(action, "action must be standaloneTaskRerun or workerTaskRetry");
1445
+ return invalid(action, "action must be a mutation-gate target (standaloneTaskRerun | workerTaskRetry | workerAdmissionPrepare | workerSchedulerAdd | workerSchedulerCancel | workerSchedulerHarvest | workerSchedulerDiscard)");
1267
1446
  }
1268
1447
  const secret = ctx.humanGateSecret;
1269
1448
  if (!secret) {
@@ -20,6 +20,10 @@ export function encodeInspectHash(selection) {
20
20
  return `#/run/${segment(selection.workerRunId)}`;
21
21
  case "batch":
22
22
  return `#/batch/${segment(selection.batchRunId)}`;
23
+ case "night-jobs":
24
+ return `#/night`;
25
+ case "night-job":
26
+ return `#/night/${segment(selection.scheduleId)}`;
23
27
  }
24
28
  }
25
29
  export function buildInspectPath(selection) {
@@ -48,6 +52,9 @@ export function decodeInspectHash(hash) {
48
52
  if (parts.length === 0)
49
53
  return null;
50
54
  const [kind, first, third, fourth] = parts;
55
+ // Collection views without an id segment (e.g. #/night).
56
+ if (kind === "night" && parts.length === 1)
57
+ return { kind: "night-jobs" };
51
58
  const id = decodeSegment(first);
52
59
  if (!id)
53
60
  return null;
@@ -66,6 +73,8 @@ export function decodeInspectHash(hash) {
66
73
  return { kind: "worker-run", workerRunId: id };
67
74
  if (kind === "batch" && parts.length === 2)
68
75
  return { kind: "batch", batchRunId: id };
76
+ if (kind === "night" && parts.length === 2)
77
+ return { kind: "night-job", scheduleId: id };
69
78
  return null;
70
79
  }
71
80
  export function selectionFromObserveLinkTarget(target) {
@@ -80,6 +89,10 @@ export function selectionFromObserveLinkTarget(target) {
80
89
  return { kind: "worker-run", workerRunId: target.workerRunId };
81
90
  case "batch":
82
91
  return { kind: "batch", batchRunId: target.batchRunId };
92
+ case "night":
93
+ return target.scheduleId
94
+ ? { kind: "night-job", scheduleId: target.scheduleId }
95
+ : { kind: "night-jobs" };
83
96
  }
84
97
  }
85
98
  /** Canonicalize only the historical Console root hash shape. */