@tea-agent/loop-agent 0.35.4-beta.0 → 0.36.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 (55) hide show
  1. package/AGENTS.md +4 -2
  2. package/CHANGELOG.md +55 -7
  3. package/dist/application/task-lifecycle/advance.js +1 -0
  4. package/dist/application/task-lifecycle/observe.js +15 -0
  5. package/dist/application/task-lifecycle/plan-transitions.js +15 -0
  6. package/dist/build-stamp.json +3 -3
  7. package/dist/commands/init-upgrade.js +351 -19
  8. package/dist/commands/init.js +14 -67
  9. package/dist/commands/run-dag-progress.js +14 -0
  10. package/dist/commands/task-advance.js +33 -3
  11. package/dist/executors/dag-pi-executor.js +3 -1
  12. package/dist/shared/operator/capabilities.js +125 -11
  13. package/dist/task/source-prepare/completeness.js +17 -0
  14. package/dist/task/source-prepare/parse-intent.js +15 -1
  15. package/dist/worker/console/chat/artifact-card.js +8 -1
  16. package/dist/worker/console/chat/chat-event-store.js +61 -0
  17. package/dist/worker/console/chat/human-gate-card.js +9 -1
  18. package/dist/worker/console/chat/operation-card.js +71 -2
  19. package/dist/worker/console/chat/pi-runtime.js +69 -30
  20. package/dist/worker/console/chat/routes.js +27 -4
  21. package/dist/worker/console/chat/semantic-activity.js +465 -0
  22. package/dist/worker/console/chat/turn-process.js +31 -12
  23. package/dist/worker/console/operation-run-facts.js +190 -0
  24. package/dist/worker/console/operation-runner.js +107 -6
  25. package/dist/worker/console/operation-wait.js +314 -0
  26. package/dist/worker/console/operator-actions.js +153 -2
  27. package/dist/worker/console/static/assets/index-2OeZODxk.js +57 -0
  28. package/dist/worker/console/static/assets/index-DVJlUL8X.css +1 -0
  29. package/dist/worker/console/static/index.html +2 -2
  30. package/dist/worker/console/static-src/operator-chat/activity-journey.js +125 -0
  31. package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +73 -0
  32. package/dist/worker/console/static-src/operator-chat/activity-references.js +20 -0
  33. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +69 -31
  34. package/dist/worker/console/static-src/operator-chat/slash-palette-layout.js +24 -0
  35. package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +141 -0
  36. package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +2 -1
  37. package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +59 -0
  38. package/dist/worker/console/static-src/operator-chat/useChatSessions.js +16 -5
  39. package/dist/worker/console/static-src/operator-chat/useComposer.js +30 -7
  40. package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +7 -3
  41. package/dist/worker/observe/static/operator-chrome.js +1 -1
  42. package/dist/workflows/dag/failure-routing.js +4 -9
  43. package/dist/workflows/dag/init-hybrid.js +74 -2
  44. package/dist/workflows/dag/lifecycle.js +0 -4
  45. package/dist/workflows/dag/report.js +0 -6
  46. package/docs/README.md +3 -3
  47. package/docs/architecture/evolution.md +102 -67
  48. package/docs/templates/frontend-task-constraints.md +7 -13
  49. package/docs/templates/init-managed-agents.md +5 -2
  50. package/harness.json +2 -2
  51. package/package.json +1 -1
  52. package/skills/loop-agent/SKILL.md +1 -0
  53. package/skills/loop-agent/references/command-reference.md +2 -0
  54. package/dist/worker/console/static/assets/index-DRqZiQ7J.css +0 -1
  55. package/dist/worker/console/static/assets/index-DuVLjCIT.js +0 -57
@@ -22,6 +22,7 @@ import { clearSemanticIntakeAttempt } from "../../task/source-prepare/semantic-i
22
22
  import nodePath from "node:path";
23
23
  import { resolveSiblingAgentWorkerBin } from "./sibling-controller.js";
24
24
  import { projectOperationEventSummary, projectOperationForChat, } from "./chat/chat-event-store.js";
25
+ import { OperationWaitError, waitForOperationChange, } from "./operation-wait.js";
25
26
  import { MutationGateReceiptStore } from "./mutation-gate-receipt-store.js";
26
27
  import { assessAutonomousExecutionEligibility, deriveEligibilityFactsFromDagSpec, } from "./dag-execution-receipt.js";
27
28
  import { issueHumanGateToken, mutationGatePayloadHash, verifyHumanGateToken, } from "./human-gate-token.js";
@@ -42,6 +43,7 @@ const MUTATION_OR_LONG = new Set([
42
43
  "contractApply",
43
44
  "contractAdopt",
44
45
  "contractRecover",
46
+ "taskAdvance",
45
47
  "dagRunTask",
46
48
  "runDag",
47
49
  "dagRerun",
@@ -55,6 +57,13 @@ function paramsOf(req) {
55
57
  function str(v) {
56
58
  return typeof v === "string" && v.trim() ? v.trim() : undefined;
57
59
  }
60
+ const TASK_ID_PLACEHOLDER_RE = /[<>]|^(?:id|task-?id|your-task-id|todo|tbd)$/i;
61
+ const TASK_ADVANCE_ALIAS_ACTIONS = new Set(["promoteRun", "closeoutTask"]);
62
+ function taskIdInputError(taskId) {
63
+ if (!TASK_ID_PLACEHOLDER_RE.test(taskId))
64
+ return undefined;
65
+ return `taskId must be a concrete canonical id; placeholder values are not allowed: ${JSON.stringify(taskId)}`;
66
+ }
58
67
  /** Extract write-set gate token from task advance operator JSON. */
59
68
  function extractWriteSetGateTokenFromCliJson(json, taskId) {
60
69
  if (!json || typeof json !== "object")
@@ -135,7 +144,21 @@ function gateError(action, code, message, details) {
135
144
  */
136
145
  async function assertAutonomousRerunEligible(ctx, runId, taskId) {
137
146
  const report = await runReadCli(ctx, ["dag", "report", "--run-id", runId, "--json"], "dag report");
138
- const reportResult = (report.ok ? report.result : undefined);
147
+ // `dag report --json` emits { schemaVersion, runs: [...] } — the run facts
148
+ // (primaryFailure / primaryRecovery) live on each entry of `runs`, not on
149
+ // the envelope top level. Reading them from the top level made every
150
+ // standaloneTaskRerun call fail closed with "cannot read dagReport run
151
+ // facts" even though the evidence was present (2026-08-15 parse drift).
152
+ const reportEnvelope = (report.ok ? report.result : undefined);
153
+ const reportRuns = reportEnvelope?.runs ?? [];
154
+ const matchedRun = reportRuns.find((entry) => entry.runId === runId) ?? reportRuns[0];
155
+ const reportResult = matchedRun ??
156
+ (reportEnvelope?.primaryFailure
157
+ ? {
158
+ primaryFailure: reportEnvelope.primaryFailure,
159
+ primaryRecovery: reportEnvelope.primaryRecovery,
160
+ }
161
+ : undefined);
139
162
  const recovery = reportResult?.primaryRecovery;
140
163
  if (!report.ok || !reportResult?.primaryFailure || !recovery) {
141
164
  return gateError("standaloneTaskRerun", "INVALID_INPUT", "cannot read dagReport run facts; standaloneTaskRerun requires server-side run evidence", { runId });
@@ -519,6 +542,66 @@ export async function dispatchOperatorAction(ctx, req) {
519
542
  }),
520
543
  };
521
544
  }
545
+ case "operationWait": {
546
+ const operationId = str(p.operationId);
547
+ if (!operationId)
548
+ return invalid(action, "operationId is required");
549
+ const rawAfterSeq = p.afterSeq ?? 0;
550
+ const afterSeq = typeof rawAfterSeq === "number" ? rawAfterSeq : Number(rawAfterSeq);
551
+ const rawMaxWaitMs = p.maxWaitMs;
552
+ const maxWaitMs = typeof rawMaxWaitMs === "number"
553
+ ? rawMaxWaitMs
554
+ : rawMaxWaitMs === undefined
555
+ ? undefined
556
+ : Number(rawMaxWaitMs);
557
+ const rawWakeOn = str(p.wakeOn);
558
+ const wakeOn = rawWakeOn === "all" || rawWakeOn === "meaningful" ? rawWakeOn : undefined;
559
+ try {
560
+ const result = await waitForOperationChange({
561
+ operations: ctx.operations,
562
+ events: ctx.events,
563
+ operationId,
564
+ afterSeq,
565
+ maxWaitMs,
566
+ ...(wakeOn ? { wakeOn } : {}),
567
+ });
568
+ return {
569
+ kind: "sync",
570
+ status: 200,
571
+ body: operatorSucceeded(action, result),
572
+ };
573
+ }
574
+ catch (error) {
575
+ if (error instanceof OperationWaitError) {
576
+ if (error.code === "NOT_FOUND") {
577
+ return {
578
+ kind: "error",
579
+ status: 404,
580
+ body: operatorFailed({
581
+ command: action,
582
+ outcome: "not-found",
583
+ code: "NOT_FOUND",
584
+ message: error.message,
585
+ }),
586
+ };
587
+ }
588
+ if (error.code === "EVENT_CURSOR_EXPIRED") {
589
+ return {
590
+ kind: "error",
591
+ status: 410,
592
+ body: operatorFailed({
593
+ command: action,
594
+ outcome: "blocked",
595
+ code: "EVENT_CURSOR_EXPIRED",
596
+ message: error.message,
597
+ }),
598
+ };
599
+ }
600
+ return invalid(action, error.message);
601
+ }
602
+ throw error;
603
+ }
604
+ }
522
605
  case "inspect": {
523
606
  const body = await runReadCli(ctx, ["inspect", "--json"], "inspect");
524
607
  return { kind: "sync", status: body.ok ? 200 : 400, body };
@@ -1186,8 +1269,24 @@ export async function dispatchOperatorAction(ctx, req) {
1186
1269
  };
1187
1270
  }
1188
1271
  const reviewPacket = extractReviewPacketFromCliJson(gen.json) ?? {};
1272
+ let parsedDagSpec;
1273
+ try {
1274
+ parsedDagSpec = JSON.parse(dagText);
1275
+ }
1276
+ catch (error) {
1277
+ return {
1278
+ kind: "error",
1279
+ status: 409,
1280
+ body: operatorFailed({
1281
+ command: action,
1282
+ outcome: "blocked",
1283
+ code: "DAG_GENERATION_FAILED",
1284
+ message: `generated DAG is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
1285
+ }),
1286
+ };
1287
+ }
1189
1288
  const derivedFacts = deriveEligibilityFactsFromDagSpec({
1190
- dagSpec: JSON.parse(dagText),
1289
+ dagSpec: parsedDagSpec,
1191
1290
  allowedPaths,
1192
1291
  forbiddenPaths,
1193
1292
  });
@@ -1349,6 +1448,9 @@ export async function dispatchOperatorAction(ctx, req) {
1349
1448
  if (!taskId || !clientRequestId) {
1350
1449
  return invalid(action, "taskId and clientRequestId are required");
1351
1450
  }
1451
+ const taskIdError = taskIdInputError(taskId);
1452
+ if (taskIdError)
1453
+ return invalid(action, taskIdError);
1352
1454
  return acceptOperation(ctx, {
1353
1455
  action,
1354
1456
  actionParams: p,
@@ -1357,6 +1459,38 @@ export async function dispatchOperatorAction(ctx, req) {
1357
1459
  cliArgs: ["task", "advance", taskId, title, "--json"],
1358
1460
  });
1359
1461
  }
1462
+ case "taskAdvance": {
1463
+ const taskId = str(p.taskId);
1464
+ const clientRequestId = str(req.clientRequestId);
1465
+ if (!taskId || !clientRequestId) {
1466
+ return invalid(action, "taskId and clientRequestId are required");
1467
+ }
1468
+ const taskIdError = taskIdInputError(taskId);
1469
+ if (taskIdError)
1470
+ return invalid(action, taskIdError);
1471
+ const args = ["task", "advance", taskId];
1472
+ const title = str(p.title);
1473
+ if (title)
1474
+ args.push(title);
1475
+ const taskKind = str(p.taskKind);
1476
+ if (taskKind)
1477
+ args.push("--task-kind", taskKind);
1478
+ const featureId = str(p.featureId);
1479
+ if (featureId)
1480
+ args.push("--feature-id", featureId);
1481
+ const profile = str(p.profile);
1482
+ if (profile)
1483
+ args.push("--profile", profile);
1484
+ appendEngineeringCliArgs(args, parseEngineeringBoundaryFromParams(p));
1485
+ args.push("--json");
1486
+ return acceptOperation(ctx, {
1487
+ action,
1488
+ actionParams: p,
1489
+ clientRequestId,
1490
+ taskId,
1491
+ cliArgs: args,
1492
+ });
1493
+ }
1360
1494
  case "bootstrapFromPrd":
1361
1495
  return dispatchBootstrapFromPrd(ctx, p);
1362
1496
  case "importPrd": {
@@ -1367,6 +1501,9 @@ export async function dispatchOperatorAction(ctx, req) {
1367
1501
  if (!taskId || !content) {
1368
1502
  return invalid(action, "taskId and content are required");
1369
1503
  }
1504
+ const taskIdError = taskIdInputError(taskId);
1505
+ if (taskIdError)
1506
+ return invalid(action, taskIdError);
1370
1507
  const taskKindRaw = str(p.taskKind)?.trim();
1371
1508
  const taskKind = normalizeConsoleWorkflowKind(taskKindRaw, "standard");
1372
1509
  const taskKindExplicit = Boolean(taskKindRaw);
@@ -1561,6 +1698,9 @@ export async function dispatchOperatorAction(ctx, req) {
1561
1698
  if (!taskId || !clientRequestId) {
1562
1699
  return invalid(action, "taskId and clientRequestId are required");
1563
1700
  }
1701
+ const taskIdError = taskIdInputError(taskId);
1702
+ if (taskIdError)
1703
+ return invalid(action, taskIdError);
1564
1704
  const out = await stageUtf8Text(ctx.appData, "", { extension: ".json" });
1565
1705
  return acceptOperation(ctx, {
1566
1706
  action,
@@ -2195,6 +2335,14 @@ export async function dispatchOperatorAction(ctx, req) {
2195
2335
  rawArgs.some((value) => typeof value !== "string"))) {
2196
2336
  return invalid(action, "args must be an array of strings");
2197
2337
  }
2338
+ if (TASK_ADVANCE_ALIAS_ACTIONS.has(action) && Array.isArray(rawArgs)) {
2339
+ const aliasTaskId = str(rawArgs[0]);
2340
+ const taskIdError = aliasTaskId
2341
+ ? taskIdInputError(aliasTaskId)
2342
+ : undefined;
2343
+ if (taskIdError)
2344
+ return invalid(action, taskIdError);
2345
+ }
2198
2346
  if (capability.humanConfirmation === "required") {
2199
2347
  const confirmationId = str(p.confirmationId);
2200
2348
  if (!confirmationId)
@@ -2274,6 +2422,9 @@ export async function dispatchOperatorAction(ctx, req) {
2274
2422
  .replace(/^(loop-agent|agent-worker)\s+/, "")
2275
2423
  .split(/\s+/);
2276
2424
  const args = [...prefix, ...(rawArgs ?? [])];
2425
+ if (TASK_ADVANCE_ALIAS_ACTIONS.has(action) && !args.includes("--json")) {
2426
+ args.push("--json");
2427
+ }
2277
2428
  if (capability.kind === "read") {
2278
2429
  if (!args.includes("--json"))
2279
2430
  args.push("--json");