@zq-silk/yui 1.1.2 → 2.1.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 (115) hide show
  1. package/ARCHITECTURE.md +2 -1
  2. package/ARCHITECTURE.zh-CN.md +2 -1
  3. package/README.md +2 -0
  4. package/dist/cli/commandCatalog.js +344 -324
  5. package/dist/cli/commandDiscovery.js +69 -0
  6. package/dist/cli/completion.js +22 -227
  7. package/dist/cli/dynamicCompletion.js +12 -6
  8. package/dist/cli/invocationAuthority.js +2 -2
  9. package/dist/cli/invocationRouter.js +18 -7
  10. package/dist/cli/managedDiagnostics.js +2 -1
  11. package/dist/cli/updatePorts.js +0 -5
  12. package/dist/cli.js +184 -130
  13. package/dist/commands/capabilityCommands.js +5 -5
  14. package/dist/commands/controllerCommands.js +2 -1
  15. package/dist/commands/globalRoleCommands.js +86 -44
  16. package/dist/commands/grantCommands.js +48 -8
  17. package/dist/commands/taskCommands.js +157 -196
  18. package/dist/commands/taskContextCommand.js +29 -6
  19. package/dist/commands/taskFactCommands.js +10 -69
  20. package/dist/commands/taskInputCommands.js +26 -35
  21. package/dist/commands/taskPublicationCommands.js +2 -35
  22. package/dist/commands/taskPublicationVerifyCommand.js +1 -2
  23. package/dist/commands/taskUpstreamCommands.js +2 -1
  24. package/dist/context/runContextPack.js +8 -29
  25. package/dist/context/sessionBootstrapManifest.js +5 -101
  26. package/dist/context/taskContext.js +104 -41
  27. package/dist/controller/clientRuntime.js +16 -8
  28. package/dist/controller/fileSchedulerStoreAdapter.js +24 -0
  29. package/dist/controller/runtime.js +6 -0
  30. package/dist/core/controllerClient.js +46 -21
  31. package/dist/core/protocol.js +12 -7
  32. package/dist/doctor/doctor.js +4 -3
  33. package/dist/errors/cliError.js +4 -4
  34. package/dist/errors/cliFailure.js +213 -0
  35. package/dist/executor/fileRoleLaunchPlanner.js +1 -4
  36. package/dist/grant/capabilityGrant.js +13 -0
  37. package/dist/grant/taskAuthorization.js +127 -0
  38. package/dist/kernel/accessAssessment.js +1 -0
  39. package/dist/kernel/builtinCapabilities.js +88 -9
  40. package/dist/kernel/capabilityRegistry.js +64 -19
  41. package/dist/kernel/kernelPorts.js +2 -2
  42. package/dist/nativeAgent/agent.js +173 -0
  43. package/dist/nativeAgent/contracts.js +1 -0
  44. package/dist/nativeAgent/demo.js +29 -0
  45. package/dist/nativeAgent/index.js +3 -0
  46. package/dist/nativeAgent/mockProvider.js +43 -0
  47. package/dist/nativeAgent/textTools.js +156 -0
  48. package/dist/nativeAgent/validation.js +95 -0
  49. package/dist/output/boundedRead.js +118 -0
  50. package/dist/plugins/pluginService.js +98 -8
  51. package/dist/release/releaseWorkflowEngine.js +8 -1
  52. package/dist/resources/projectResourceService.js +23 -1
  53. package/dist/runtime/managedCaller.js +2 -2
  54. package/dist/runtime/managedIdentity.js +6 -0
  55. package/dist/runtime/runtimeCoherence.js +10 -8
  56. package/dist/storage/sqliteSchema.js +10 -0
  57. package/dist/storage/storageVersions.js +1 -1
  58. package/dist/storage/taskStore.js +3 -0
  59. package/dist/surface/surfaceContributions.js +3 -0
  60. package/dist/task/leaderArchive.js +111 -0
  61. package/dist/task/leaderArchiveAuthority.js +24 -0
  62. package/dist/task/taskAuthority.js +5 -10
  63. package/dist/tmux/commandExecutor.js +7 -5
  64. package/dist/web/assets/assetManifest.js +38 -22
  65. package/dist/web/assets/client/api.js +115 -0
  66. package/dist/web/assets/client/app.js +555 -863
  67. package/dist/web/assets/client/components.js +323 -897
  68. package/dist/web/assets/client/detail.js +333 -0
  69. package/dist/web/assets/client/dock.js +225 -0
  70. package/dist/web/assets/client/dom.js +55 -3
  71. package/dist/web/assets/client/evidence.js +172 -0
  72. package/dist/web/assets/client/format.js +25 -14
  73. package/dist/web/assets/client/forms.js +230 -0
  74. package/dist/web/assets/client/i18n.js +1020 -791
  75. package/dist/web/assets/client/overview.js +115 -0
  76. package/dist/web/assets/client/records.js +63 -0
  77. package/dist/web/assets/client/sections.js +313 -0
  78. package/dist/web/assets/client/sidebar.js +117 -0
  79. package/dist/web/assets/client/theme.js +61 -22
  80. package/dist/web/assets/icons.js +39 -0
  81. package/dist/web/assets/shell.js +124 -118
  82. package/dist/web/assets/styles/base.js +41 -0
  83. package/dist/web/assets/styles/components.js +167 -0
  84. package/dist/web/assets/styles/layout.js +49 -77
  85. package/dist/web/assets/styles/markdown.js +16 -24
  86. package/dist/web/assets/styles/responsive.js +33 -44
  87. package/dist/web/assets/styles/tokens.js +65 -89
  88. package/dist/web/assets/styles/views.js +289 -0
  89. package/dist/web/webServer.js +2 -1
  90. package/docs/architecture/README.md +1 -0
  91. package/docs/architecture/README.zh-CN.md +1 -0
  92. package/docs/cli-information-contract.md +107 -0
  93. package/docs/cli-information-contract.zh-CN.md +81 -0
  94. package/docs/managed-turn-and-session-runtime.md +9 -0
  95. package/docs/managed-turn-and-session-runtime.zh-CN.md +6 -0
  96. package/docs/plugin-sdk.md +7 -3
  97. package/docs/plugin-sdk.zh-CN.md +5 -2
  98. package/docs/release-workflow.md +42 -9
  99. package/docs/release-workflow.zh-CN.md +30 -7
  100. package/docs/storage-baseline.md +5 -0
  101. package/docs/testing/verification-levels.md +2 -2
  102. package/i18n/README.zh-CN.md +2 -0
  103. package/package.json +1 -1
  104. package/skills/yui-leader/SKILL.md +8 -1
  105. package/skills/yui-leader/references/authorization.md +64 -0
  106. package/skills/yui-leader/references/execution.md +18 -4
  107. package/skills/yui-leader/references/task-plugins.md +4 -2
  108. package/skills/yui-operator/SKILL.md +9 -2
  109. package/skills/yui-runtime/SKILL.md +77 -9
  110. package/skills/yui-runtime/references/publication.md +3 -1
  111. package/dist/web/assets/client/taskSummary.js +0 -350
  112. package/dist/web/assets/client/taskSurface.js +0 -616
  113. package/dist/web/assets/client/view.js +0 -608
  114. package/dist/web/assets/styles/cards.js +0 -247
  115. package/dist/web/assets/styles/widgets.js +0 -168
@@ -1,13 +1,15 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { isDeepStrictEqual } from "node:util";
3
3
  import { agentExecutionComponentLabel } from "../agent/executionComponents.js";
4
+ import { assertLeaderArchiveAdmission } from "../task/leaderArchiveAuthority.js";
4
5
  import { createRun, runExecutionObservation, runPurposeAdmitsTaskState } from "../agentRun/agentRun.js";
5
6
  import { isGitArtifactRefString, parseGitArtifactRef } from "../artifacts/gitArtifactRef.js";
6
7
  import { contextSnapshotRef } from "../context/contextSnapshot.js";
7
8
  import { buildRunContextDelta, buildRunContextPack, contextSnapshotDeltaRefIds, expandRunContextRef, freezeReviewStageContextSnapshot, freezeRunContextSnapshot, freezeWorkItemExecutionAssignmentContextSnapshot, readRunContextSnapshot, synthesisSourceRunIds } from "../context/runContextPack.js";
8
9
  import { createRunInput } from "../context/runInputContract.js";
9
10
  import { parseTaskCatalogOptions, readTaskCatalog, renderTaskCatalog } from "../context/taskCatalog.js";
10
- import { listContextMessages } from "../context/taskContext.js";
11
+ import { assertContextRecordReadable } from "../context/taskContext.js";
12
+ import { boundedDocument, readOptions, messageReceipt, recordPage } from "../output/boundedRead.js";
11
13
  import { referencedWakeRunIds } from "../context/wakeRunReferences.js";
12
14
  import { completeProcessing, releaseProcessing } from "../coordination/workMailbox.js";
13
15
  import { enqueueRoleRunDispatch, enqueueWork, settleExactWorkExecution } from "../coordination/workMailboxQueue.js";
@@ -36,7 +38,7 @@ import { projectReviewerAvailability } from "../review/reviewerAvailability.js";
36
38
  import { attachReviewExecutionGroup, createReviewRound, createTaskDeltaReviewRound, createTaskReviewRound, finishReviewRound, recordReviewWorkspaceDisposition, retryReviewRound, retryRunningReviewExecutionLane, retryTaskReviewRound, startReplicatedReviewRound, startReviewRound, updateReviewExecutionGroup, validateTaskReviewCandidate } from "../review/reviewRound.js";
37
39
  import { sameTaskFinalReviewContract, taskFinalReviewConfig, validateTaskFinalReviewContract } from "../review/taskFinalReviewContract.js";
38
40
  import { resolveRecordedTaskFinalReviewContract } from "../review/taskFinalReviewContractResolution.js";
39
- import { copyGlobalRoleToTaskRole, createRole, createRoleAgentBinding, switchActiveRoleAgent, unbindRoleAgent, updateRole } from "../role/role.js";
41
+ import { copyGlobalRoleToTaskRole, activeRoleAgentBinding, createRole, createRoleAgentBinding, switchActiveRoleAgent, unbindRoleAgent, updateRole } from "../role/role.js";
40
42
  import { roleLaunchEventPayload, saveTaskRoleUpdate } from "../role/taskRoleUpdate.js";
41
43
  import { RUNTIME_SESSION_REPLACE_REQUIRED_REASON, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
42
44
  import { currentManagedRuntime, resolveManagedTaskReader } from "../runtime/managedCaller.js";
@@ -70,10 +72,12 @@ import { runGrantCommand } from "./grantCommands.js";
70
72
  import { hasAgentConfigOptions, hasNoRoleMutation, parseRoleOptions, patchRoleAgentBinding, roleOptionSpecs, roleProfilePatch } from "./roleConfiguration.js";
71
73
  import { assertRoleRuntimeMutationAllowed } from "./roleRuntimeGuard.js";
72
74
  import { hasRoleLaunchContextOptions, validateConfiguredRoleSkills } from "./roleSkillValidation.js";
75
+ import { requireManagedGlobalCaller } from "../runtime/managedCaller.js";
76
+ import { hasManagedIdentity } from "../runtime/managedIdentity.js";
73
77
  import { nonLeaderActivationIdentity, runTaskActivationCommand } from "./taskActivationCommands.js";
74
78
  import { assertTaskOpen, clock, exactPositionals, leaderActionEventPayload, leaderMailbox, notifyMailbox, optionalNonEmptyOption, output, parseMultiValueTail, parseTail, presentTime, recordTaskEvent, recordTaskEventRecord, requiredOption, requiredText, requireTask, roleMailbox, taskActor, taskMailbox, taskRef, trimmed } from "./taskCommandSupport.js";
75
79
  import { assertTaskCompletionPublishedTreeProof } from "./taskCompletionGate.js";
76
- import { runTaskContextCommand } from "./taskContextCommand.js";
80
+ import { runTaskContextCommand, taskRecordList } from "./taskContextCommand.js";
77
81
  import { taskBriefCommand, taskDecisionCommand, taskEventCommand, taskMilestoneCommand } from "./taskFactCommands.js";
78
82
  import { openInputRequestCount, runTaskInputCommand } from "./taskInputCommands.js";
79
83
  import { runTaskNextActionCommand } from "./taskNextActionCommand.js";
@@ -300,6 +304,29 @@ function formatCompletionBlockers(taskId, blockers) {
300
304
  return `Task ${taskId} cannot complete: ${blockers.length} blocker(s) remain.\n${lines.join("\n")}`;
301
305
  }
302
306
  export function runTaskCommand(args, store, options = {}) {
307
+ // Read-only detail documents share one bounded transport. This is not a
308
+ // mutation replay mechanism: only the explicit read paths below accept a
309
+ // document cursor; authorization still runs inside each domain command.
310
+ const detail = ["show", "next-action", "remote-delivery"].includes(args[0] ?? "")
311
+ || (["brief", "decision", "milestone", "event", "work", "wake", "review", "change-set"].includes(args[0] ?? "")
312
+ && args[1] === "show")
313
+ || (args[0] === "role" && (["show", "status"].includes(args[1] ?? "")
314
+ || args[1] === "session" && args[2] === "inspect"));
315
+ if (!detail)
316
+ return executeTaskCommand(args, store, options);
317
+ const cursorIndex = args.indexOf("--cursor");
318
+ const cursor = cursorIndex < 0 ? undefined : args[cursorIndex + 1];
319
+ if (cursorIndex >= 0 && (cursor === undefined || cursor.startsWith("--")
320
+ || args.lastIndexOf("--cursor") !== cursorIndex))
321
+ throw usageError("Detail read requires one --cursor value.");
322
+ const command = cursorIndex < 0 ? args : [...args.slice(0, cursorIndex), ...args.slice(cursorIndex + 2)];
323
+ const result = executeTaskCommand(command, store, options);
324
+ if (result.kind !== "output" || result.data === undefined)
325
+ return result;
326
+ const data = boundedDocument(result.data, `task-read:${JSON.stringify(command)}`, cursor);
327
+ return data === result.data ? result : output(`${JSON.stringify(data, null, 2)}\n`, data);
328
+ }
329
+ function executeTaskCommand(args, store, options = {}) {
303
330
  const [command, ...rest] = args;
304
331
  const delivery = command === "complete"
305
332
  || (command === "work" && ["dispatch", "synthesize", "review", "accept"].includes(rest[0] ?? ""))
@@ -766,6 +793,15 @@ function parseSubmissionIntentOption(raw, usage) {
766
793
  throw usageError(`--intent must be one of ${TASK_SUBMISSION_INTENTS.join(", ")}: ${raw}.`, usage);
767
794
  }
768
795
  export function submitOperatorMessage(body, taskId, store, options = {}, intent, submissionKey) {
796
+ // Public terminals and ordinary unmanaged Agents retain the submission
797
+ // route. Managed callers cannot turn a Task Assignment into Operator input.
798
+ const callerEnvironment = options.environment ?? {};
799
+ if (hasManagedIdentity(callerEnvironment)) {
800
+ if (callerEnvironment.YUI_SESSION_SCOPE !== "global" || callerEnvironment.YUI_ROLE !== "operator") {
801
+ throw usageError("Operator submission requires a public caller or the current global Operator.");
802
+ }
803
+ requireManagedGlobalCaller(store, callerEnvironment);
804
+ }
769
805
  const now = clock(options);
770
806
  const effectiveIntent = normalizeSubmissionIntent(intent);
771
807
  const result = store.transaction((tx) => {
@@ -1249,7 +1285,8 @@ function archiveTaskCommand(args, store, options) {
1249
1285
  const now = clock(options);
1250
1286
  const result = store.transaction((tx) => {
1251
1287
  const task = requireTask(tx, request.taskId);
1252
- const actor = taskActor(tx, options, task.id);
1288
+ const actor = options.archiveLeaderAdmission === undefined ? taskActor(tx, options, task.id)
1289
+ : (assertLeaderArchiveAdmission(tx, task.id, options.archiveLeaderAdmission), "leader");
1253
1290
  if (task.status === "archived")
1254
1291
  return { task, changed: false };
1255
1292
  if (task.status !== "completed"
@@ -1309,6 +1346,9 @@ function archiveTaskCommand(args, store, options) {
1309
1346
  })));
1310
1347
  recordTaskEvent(tx, task.id, "task.archived", {
1311
1348
  by: actor,
1349
+ ...(options.archiveLeaderAdmission === undefined ? {} : {
1350
+ authorizationSource: JSON.stringify(options.archiveLeaderAdmission.source)
1351
+ }),
1312
1352
  workspaceDisposition: request.disposition,
1313
1353
  ...(request.force ? {
1314
1354
  force: "true", cleanup: "pending",
@@ -1496,7 +1536,22 @@ export function parseTaskArchiveArguments(args, command = "archive") {
1496
1536
  const usage = "Task archive usage: "
1497
1537
  + `yui task ${command} <id> (--integrated|--abandon) [--force].`;
1498
1538
  const taskId = args[0]?.trim();
1499
- const flags = args.slice(1);
1539
+ const flags = [];
1540
+ const values = new Map();
1541
+ for (let index = 1; index < args.length; index++) {
1542
+ const arg = args[index];
1543
+ if (["--source-message", "--purpose", "--request-id"].includes(arg)) {
1544
+ if (command !== "archive" || values.has(arg) || !args[index + 1]?.trim()
1545
+ || args[index + 1].startsWith("--"))
1546
+ throw usageError(usage);
1547
+ values.set(arg, args[++index]);
1548
+ }
1549
+ else
1550
+ flags.push(arg);
1551
+ }
1552
+ if (values.size !== 0 && values.size !== 3) {
1553
+ throw usageError("Leader archive requires --source-message, --purpose and --request-id together.");
1554
+ }
1500
1555
  if (taskId === undefined
1501
1556
  || taskId.length === 0
1502
1557
  || flags.some((flag, index) => (!["--integrated", "--abandon", "--force"].includes(flag)
@@ -1512,7 +1567,11 @@ export function parseTaskArchiveArguments(args, command = "archive") {
1512
1567
  return {
1513
1568
  taskId,
1514
1569
  disposition: integrated ? "integrated" : "abandoned",
1515
- force
1570
+ force,
1571
+ ...(values.size === 0 ? {} : {
1572
+ sourceMessage: values.get("--source-message"), purpose: values.get("--purpose"),
1573
+ requestId: values.get("--request-id")
1574
+ })
1516
1575
  };
1517
1576
  }
1518
1577
  function formatProjectCommits(projects) {
@@ -1522,9 +1581,18 @@ function formatProjectCommits(projects) {
1522
1581
  export function validateTaskArchiveRequest(args, store, options = {}) {
1523
1582
  const request = parseTaskArchiveArguments(args);
1524
1583
  const task = requireTask(store, request.taskId);
1525
- const archiveActor = taskActor(store, options, task.id);
1526
- if (archiveActor === "leader") {
1527
- throw usageError("Task archive requires independent user or Operator authorization.");
1584
+ if (options.archiveLeaderAdmission !== undefined) {
1585
+ assertLeaderArchiveAdmission(store, task.id, options.archiveLeaderAdmission);
1586
+ if (request.force || request.disposition !== "integrated") {
1587
+ throw usageError("Leader ordinary archive admission never authorizes force or abandonment.");
1588
+ }
1589
+ }
1590
+ const archiveActor = options.archiveLeaderAdmission === undefined ? taskActor(store, options, task.id) : "leader";
1591
+ if (archiveActor === "leader" && options.archiveLeaderAdmission === undefined) {
1592
+ if (request.force || request.disposition === "abandoned") {
1593
+ throw usageError("Force or abandon archive requires independent user or Operator authorization.");
1594
+ }
1595
+ throw usageError("Leader archive requires a source-authorized Controller operation; use task archive with --source-message, --purpose and --request-id.");
1528
1596
  }
1529
1597
  if (task.status !== "archived"
1530
1598
  && task.status !== "completed"
@@ -1581,17 +1649,21 @@ function taskMessageCommand(args, store, options) {
1581
1649
  }
1582
1650
  if (command === "show") {
1583
1651
  const usage = "Task message show usage: yui task message show <task/message>.";
1584
- exactPositionals(rest, 1, usage);
1585
- const ref = taskRecordReference(rest[0], "message", "Message reference", options);
1586
- const message = listContextMessages(store, ref.taskId, options.environment)
1587
- .find((entry) => entry.id === ref.localId);
1652
+ const parsed = readOptions(rest, ["--cursor"]);
1653
+ exactPositionals(parsed.positionals, 1, usage);
1654
+ const ref = taskRecordReference(parsed.positionals[0], "message", "Message reference", options);
1655
+ assertContextRecordReadable(store, ref.taskId, "task-message", ref.localId, options.environment);
1656
+ const message = store.queryContextRecords(ref.taskId, {
1657
+ family: "task-message", ids: [ref.localId], limit: 1
1658
+ }).records[0]?.value;
1588
1659
  if (message === undefined)
1589
1660
  throw dataError("Message is unavailable in the caller's scope.");
1590
1661
  const continuationRun = message.continuation?.runId === undefined
1591
1662
  ? null : store.getRun(ref.taskId, message.continuation.runId);
1592
1663
  const expanded = { ...expandTaskMessageResult(message, (task, run) => store.getRun(task, run)),
1593
1664
  ...(continuationRun === null ? {} : { execution: runExecutionObservation(continuationRun, store.getTaskRoleSessionSet(ref.taskId, continuationRun.roleName)?.providerBinding, store.listEvents(ref.taskId)) }) };
1594
- return { kind: "output", output: `${JSON.stringify(expanded, null, 2)}\n`, data: expanded };
1665
+ const data = boundedDocument(expanded, `task:${ref.taskId}/message/${ref.localId}`, parsed.cursor);
1666
+ return { kind: "output", output: `${JSON.stringify(data, null, 2)}\n`, data };
1595
1667
  }
1596
1668
  if (command === "send") {
1597
1669
  const usage = "Task message send usage: yui task message send <id> (<body>|--body-file <path|->) [--intent record|discuss|develop] [--request-id <key>] [--to <role> --work-item <id>|--review-round <id>].";
@@ -1613,60 +1685,20 @@ function taskMessageCommand(args, store, options) {
1613
1685
  // A user/operator submission returns the unified §2.5 feedback; render each
1614
1686
  // facet on its own line and expose the structure to non-text callers.
1615
1687
  if (result.feedback !== undefined) {
1616
- return output(renderSubmissionFeedback(result.feedback), { taskId: result.task.id, message: result.message, submission: result.feedback });
1688
+ return output(renderSubmissionFeedback(result.feedback), { taskId: result.task.id, message: messageReceipt(result.message), submission: result.feedback });
1617
1689
  }
1618
1690
  const reason = result.message.continuation?.notDeliveredReason;
1619
1691
  const delivery = reason !== undefined ? { state: "not-delivered", reason }
1620
1692
  : recipientRole !== undefined || result.queuedForLeader
1621
1693
  ? { state: "queued" } : { state: "saved" };
1622
- return output(`Saved message ${result.message.id} to ${result.task.id} (${delivery.state}${reason === undefined ? "" : `: ${reason}`}).\n`, { taskId: result.task.id, message: result.message, delivery });
1694
+ return output(`Saved message ${result.message.id} to ${result.task.id} (${delivery.state}${reason === undefined ? "" : `: ${reason}`}).\n`, { taskId: result.task.id, message: messageReceipt(result.message), delivery });
1623
1695
  }
1624
1696
  if (command === "queue")
1625
1697
  return queueTaskMessage(rest, store, options);
1626
1698
  if (command === "steer")
1627
1699
  return steerTaskMessage(rest, store, options);
1628
1700
  if (command === "list") {
1629
- const messageListUsage = "Task message list usage: yui task message list <id> [--after <timestamp>] [--limit <n>].";
1630
- const parsed = parseTail(rest, new Set(["--after", "--limit"]), messageListUsage);
1631
- exactPositionals(parsed.positionals, 1, messageListUsage);
1632
- const task = requireTask(store, parsed.positionals[0]);
1633
- let messages = listContextMessages(store, task.id, options.environment);
1634
- const after = optionalNonEmptyOption(parsed.options, "--after");
1635
- if (after !== undefined) {
1636
- const afterMs = Date.parse(after);
1637
- if (!Number.isFinite(afterMs))
1638
- throw usageError("--after must be a valid timestamp.", messageListUsage);
1639
- messages = messages.filter((m) => Date.parse(m.createdAt) > afterMs);
1640
- }
1641
- const limit = optionalNonEmptyOption(parsed.options, "--limit");
1642
- if (limit !== undefined) {
1643
- const n = Number(limit);
1644
- if (!Number.isSafeInteger(n) || n <= 0)
1645
- throw usageError("--limit must be a positive integer.", messageListUsage);
1646
- messages = messages.slice(-n);
1647
- }
1648
- if (messages.length === 0)
1649
- return "No messages found.\n";
1650
- const retirements = new Map(store.listEvents(task.id).flatMap((event) => {
1651
- const retirement = taskRecordRetirement(event);
1652
- return retirement?.recordKind === "message"
1653
- ? [[retirement.recordId, retirement]]
1654
- : [];
1655
- }));
1656
- const timeZone = store.getConfig().timeZone;
1657
- return `${renderTable(`Task messages: ${task.id}`, [
1658
- { header: "Message", minWidth: 7, maxWidth: 18 },
1659
- { header: "Status", minWidth: 6, maxWidth: 9 },
1660
- { header: "Author", minWidth: 6, maxWidth: 18 },
1661
- { header: "Created", minWidth: 10, maxWidth: 28 },
1662
- { header: "Body", minWidth: 8, maxWidth: 72 }
1663
- ], messages.map((message) => [
1664
- message.id,
1665
- retirements.has(message.id) ? "retired" : "active",
1666
- taskMessageAuthorLabel(message.author),
1667
- presentTime(message.createdAt, timeZone),
1668
- message.body
1669
- ]), defaultTableWidth())}\n`;
1701
+ return taskRecordList(rest, store, "task-message", options.environment);
1670
1702
  }
1671
1703
  if (command === "update") {
1672
1704
  return updateMessage(rest, store, options);
@@ -1719,7 +1751,7 @@ function queueTaskInput(taskId, input, store, options) {
1719
1751
  const delivery = result.idempotentReplay ? { state: "idempotent-replay" }
1720
1752
  : reason !== undefined ? { state: "not-delivered", reason }
1721
1753
  : recipientRole !== undefined || result.actor !== "leader" ? { state: "queued" } : { state: "saved" };
1722
- return output(`Queued message ${result.message.id} to ${result.task.id} (${delivery.state}${reason === undefined ? "" : `: ${reason}`}).\n`, { taskId: result.task.id, message: result.message, delivery });
1754
+ return output(`Queued message ${result.message.id} to ${result.task.id} (${delivery.state}${reason === undefined ? "" : `: ${reason}`}).\n`, { taskId: result.task.id, message: messageReceipt(result.message), delivery });
1723
1755
  }
1724
1756
  /**
1725
1757
  * `steer` targets only the exact current native Turn. It always persists the
@@ -1772,7 +1804,7 @@ function steerTaskInput(taskId, input, store, options) {
1772
1804
  // unknown steer is visibly not-submitted/pending/accepted/rejected/
1773
1805
  // delivery-unknown rather than silently re-attempted (message-5 gap D).
1774
1806
  const controlState = taskMessageInputControlState(result.message);
1775
- return output(`Steer message ${result.message.id} already recorded (idempotent-replay${controlState === undefined ? "" : `; control ${controlState}`}).\n`, { taskId: result.task.id, message: result.message,
1807
+ return output(`Steer message ${result.message.id} already recorded (idempotent-replay${controlState === undefined ? "" : `; control ${controlState}`}).\n`, { taskId: result.task.id, message: messageReceipt(result.message),
1776
1808
  steer: { state: "idempotent-replay",
1777
1809
  ...(controlState === undefined ? {} : { control: controlState }) } });
1778
1810
  }
@@ -1780,7 +1812,7 @@ function steerTaskInput(taskId, input, store, options) {
1780
1812
  // match, and writer fence, all from durable state without a live Endpoint.
1781
1813
  const resolution = resolveTaskInputControl(store, result.task.id, roleName, "steer", expectedTarget);
1782
1814
  if (resolution.outcome !== "ready") {
1783
- return output(`Steer message ${result.message.id} saved but not delivered (${resolution.code}: ${resolution.detail}).\n`, { taskId: result.task.id, message: result.message,
1815
+ return output(`Steer message ${result.message.id} saved but not delivered (${resolution.code}: ${resolution.detail}).\n`, { taskId: result.task.id, message: messageReceipt(result.message),
1784
1816
  steer: { state: "not-steered", code: resolution.code, detail: resolution.detail } });
1785
1817
  }
1786
1818
  if (!leaderTarget) {
@@ -1792,7 +1824,7 @@ function steerTaskInput(taskId, input, store, options) {
1792
1824
  || recipient.workItemId !== current.workItemId
1793
1825
  || recipient.reviewRoundId !== current.reviewRoundId
1794
1826
  || messageContinuationBlocker(store, result.message) !== undefined) {
1795
- return output(`Steer message ${result.message.id} saved but not delivered (TARGET_CHANGED: active Assignment differs).\n`, { taskId: result.task.id, message: result.message,
1827
+ return output(`Steer message ${result.message.id} saved but not delivered (TARGET_CHANGED: active Assignment differs).\n`, { taskId: result.task.id, message: messageReceipt(result.message),
1796
1828
  steer: { state: "not-steered", code: "TARGET_CHANGED", detail: "The input does not belong to the active Turn's Assignment." } });
1797
1829
  }
1798
1830
  }
@@ -2057,7 +2089,7 @@ function updateMessage(args, store, options) {
2057
2089
  ? leaderMailbox(result.task.id) : taskMailbox(result.task.id));
2058
2090
  }
2059
2091
  return output(`${result.changed ? "Updated" : "Unchanged"} Task Message ${result.task.id}/${result.message.id}\n`, {
2060
- message: result.message
2092
+ message: messageReceipt(result.message)
2061
2093
  });
2062
2094
  }
2063
2095
  function retireMessage(args, store, options) {
@@ -2481,10 +2513,16 @@ function taskRoleSessionCommand(args, store, options) {
2481
2513
  ].join("\n") + "\n"
2482
2514
  + `\n${renderRoleLaunchComparison(role, active.effective)}\n`
2483
2515
  + (runConfigurationDetail === "" ? "" : `\n${runConfigurationDetail}\n`), {
2484
- task,
2485
- role,
2516
+ taskId: task.id,
2517
+ roleName: role.name,
2518
+ desired: { launchRevision: role.launchRevision, activeAgentId: role.activeAgentId,
2519
+ binding: activeRoleAgentBinding(role) },
2486
2520
  session: active,
2487
- providerBinding: binding,
2521
+ providerBinding: binding === null ? null : {
2522
+ providerNamespace: binding.providerNamespace, accountScope: binding.accountScope,
2523
+ currentConversation: currentProviderConversation(binding),
2524
+ authority: binding.authority, run: binding.run, goal: binding.goal
2525
+ },
2488
2526
  retry: providerRetryProjection(binding),
2489
2527
  ...(host === undefined ? {} : { host }),
2490
2528
  ...(runConfiguration === undefined ? {} : { runConfiguration })
@@ -2652,31 +2690,20 @@ function addTaskRole(args, store, options) {
2652
2690
  ].join("\n").concat("\n");
2653
2691
  }
2654
2692
  function listTaskRoles(args, store, options) {
2655
- exactPositionals(args, 1, "Task role list usage: yui task role list <task>.");
2656
- const task = requireTask(store, args[0]);
2693
+ const parsed = readOptions(args);
2694
+ exactPositionals(parsed.positionals, 1, "Task role list usage: yui task role list <task> [--limit <n>] [--cursor <cursor>].");
2695
+ const task = requireTask(store, parsed.positionals[0]);
2657
2696
  const roles = store.listRoles(task.id);
2658
- const statuses = inspectTaskRoleRuntimeStatuses(task.id, roles, store, options.runtime?.inspectTaskRolePanes?.(task.id) ?? [], options.now?.() ?? new Date()).map(status => withTaskRoleHostObservation(status, options.liveHostObservations?.[status.roleName]));
2659
- if (statuses.length === 0)
2660
- return output("No roles assigned.\n", { roles: statuses });
2661
- return output(`${renderTable(`Task roles: ${task.id}`, [
2662
- { header: "Role", minWidth: 4, maxWidth: 24 },
2663
- { header: "Agent", minWidth: 5, maxWidth: 20 },
2664
- { header: "Health", minWidth: 6, maxWidth: 15 },
2665
- { header: "Open input", minWidth: 5, maxWidth: 10 },
2666
- { header: "Active work", minWidth: 10, maxWidth: 34 },
2667
- { header: "Last turn", minWidth: 10, maxWidth: 28 },
2668
- { header: "Native session", minWidth: 10, maxWidth: 28 },
2669
- { header: "tmux", minWidth: 6, maxWidth: 22 }
2670
- ], statuses.map((status) => [
2671
- status.roleName,
2672
- status.agentId,
2673
- status.health,
2674
- taskRoleOpenInputLabel(status),
2675
- taskRoleActiveWorkLabel(status),
2676
- taskRoleLastRunLabel(status),
2677
- taskRoleNativeSessionLabel(status),
2678
- taskRoleTmuxLabel(status)
2679
- ]), defaultTableWidth())}\n`, { roles: statuses });
2697
+ const statuses = inspectTaskRoleRuntimeStatuses(task.id, roles, store, options.runtime?.inspectTaskRolePanes?.(task.id) ?? [], options.now?.() ?? new Date());
2698
+ const data = recordPage(statuses.map(status => ({
2699
+ roleName: status.roleName, agentId: status.agentId, recordedHealth: status.health,
2700
+ hostObservation: "not-requested",
2701
+ openInput: taskRoleOpenInputLabel(status), activeWork: taskRoleActiveWorkLabel(status),
2702
+ lastRun: taskRoleLastRunLabel(status), nativeSession: taskRoleNativeSessionLabel(status),
2703
+ tmux: taskRoleTmuxLabel(status),
2704
+ read: `task role status ${task.id} ${status.roleName}`
2705
+ })), `task:${task.id}/role-status`, parsed);
2706
+ return output(`${JSON.stringify(data)}\n`, data);
2680
2707
  }
2681
2708
  function taskRoleStatus(args, store, options) {
2682
2709
  exactPositionals(args, 2, "Task role status usage: yui task role status <task> <role>.");
@@ -3011,7 +3038,7 @@ function taskWorkCommand(args, store, options) {
3011
3038
  if (command === "create")
3012
3039
  return createWork(rest, store, options);
3013
3040
  if (command === "list")
3014
- return listWork(rest, store);
3041
+ return taskRecordList(rest, store, "work-item", options.environment);
3015
3042
  if (command === "show")
3016
3043
  return showWork(rest, store, options);
3017
3044
  if (command === "edit")
@@ -3882,36 +3909,6 @@ function retireWork(args, store, options) {
3882
3909
  options.runtime?.notifyMailboxChanged({ kind: "task", taskId: retired.taskId });
3883
3910
  return output(`Retired Work Item ${retired.id}\n`, { workItem: retired });
3884
3911
  }
3885
- function listWork(args, store) {
3886
- exactPositionals(args, 1, "Task work list usage: yui task work list <task>.");
3887
- const task = requireTask(store, args[0]);
3888
- const items = store.listWorkItems(task.id);
3889
- const runs = store.listRuns(task.id);
3890
- const sessionSets = store.listRoleSessionSets(task.id);
3891
- const executions = items.map((item) => projectWorkItemExecution(item, runs, sessionSets, store));
3892
- const rendered = items.length === 0
3893
- ? "No work items found.\n"
3894
- : `${renderTable(`Task work: ${task.id}`, [
3895
- { header: "Work", minWidth: 6, maxWidth: 20 },
3896
- { header: "Status", minWidth: 6, maxWidth: 12 },
3897
- { header: "Role", minWidth: 4, maxWidth: 18 },
3898
- { header: "Shape", minWidth: 6, maxWidth: 10 },
3899
- { header: "Execution", minWidth: 12, maxWidth: 42 },
3900
- { header: "Next / Owner", minWidth: 12, maxWidth: 36 },
3901
- { header: "Title", minWidth: 8, maxWidth: 64 },
3902
- { header: "Outcome", minWidth: 8, maxWidth: 40 }
3903
- ], items.map((item, index) => [
3904
- item.id,
3905
- presentWorkStatus(item.status),
3906
- item.assignee ?? "Leader",
3907
- executions[index].shape,
3908
- compactWorkItemExecution(executions[index]),
3909
- `${executions[index].nextAction.kind} / ${executions[index].nextAction.owners.join(",") || "none"}`,
3910
- item.title,
3911
- item.outcome ?? "-"
3912
- ]), defaultTableWidth())}\n`;
3913
- return output(rendered, { workItems: items, executions });
3914
- }
3915
3912
  function showWork(args, store, options) {
3916
3913
  exactPositionals(args, 1, "Task work show usage: yui task work show <work>.");
3917
3914
  const item = requireWorkItem(store, args[0], options);
@@ -3934,12 +3931,6 @@ function showWork(args, store, options) {
3934
3931
  ].join("\n");
3935
3932
  return output(`${rendered}\n`, { workItem: item, execution });
3936
3933
  }
3937
- function compactWorkItemExecution(projection) {
3938
- if (projection.shape === "direct")
3939
- return `main=${projection.mainRun.status}`;
3940
- const counts = projection.laneCounts;
3941
- return `lanes ${counts.running}/${counts.succeeded}/${counts.needsAttention}/${counts.failed}/${counts.unknown}; ${projection.synthesis.status}`;
3942
- }
3943
3934
  function renderWorkItemExecutionProjection(projection) {
3944
3935
  return [
3945
3936
  `Execution Shape: ${projection.shape}`,
@@ -4402,7 +4393,7 @@ function retryFailedTaskReviewRound(args, store, options) {
4402
4393
  function taskRunCommand(args, store, options) {
4403
4394
  const [command, ...rest] = args;
4404
4395
  if (command === "list")
4405
- return output(listRuns(rest, store, options));
4396
+ return listRuns(rest, store, options);
4406
4397
  if (command === "show")
4407
4398
  return showRun(rest, store, options);
4408
4399
  if (command === "context")
@@ -4673,7 +4664,7 @@ function runContextCommand(args, store, options) {
4673
4664
  const [first, ...rest] = args;
4674
4665
  if (first === "expand") {
4675
4666
  const usage = "Task run context expand usage: yui task run context expand <task>/<run> <ref-id> --store <store> [--mode full].";
4676
- const parsed = parseTail(rest, new Set(["--store", "--mode"]), usage);
4667
+ const parsed = parseTail(rest, new Set(["--store", "--mode", "--cursor"]), usage);
4677
4668
  exactPositionals(parsed.positionals, 2, usage);
4678
4669
  const refStore = requiredOption(parsed.options, "--store");
4679
4670
  const mode = parsed.options.get("--mode");
@@ -4683,24 +4674,29 @@ function runContextCommand(args, store, options) {
4683
4674
  const { taskId, runId } = parseRunContextReference(parsed.positionals[0]);
4684
4675
  authorizeRunContext(store, taskId, runId, options.environment);
4685
4676
  const expanded = store.transaction((tx) => expandRunContextRef(tx, taskId, runId, parsed.positionals[1], refStore));
4686
- return output(`${JSON.stringify(expanded, null, 2)}\n`, { context: expanded });
4677
+ const data = boundedDocument(expanded, `run:${taskId}/${runId}/${refStore}/${parsed.positionals[1]}`, parsed.options.get("--cursor"));
4678
+ return output(`${JSON.stringify(data, null, 2)}\n`, { context: data });
4687
4679
  }
4688
4680
  if (first === "delta") {
4689
- if (rest.length !== 3 || rest[1] !== "--after") {
4690
- throw usageError("Task run context delta usage: yui task run context delta <task>/<run> --after <cursor>.");
4681
+ const parsed = readOptions(rest, ["--after", "--cursor"]);
4682
+ if (parsed.positionals.length !== 1 || !parsed.values.has("--after")) {
4683
+ throw usageError("Task run context delta usage: yui task run context delta <task>/<run> --after <digest> [--cursor <cursor>].");
4691
4684
  }
4692
- const { taskId, runId } = parseRunContextReference(rest[0]);
4685
+ const { taskId, runId } = parseRunContextReference(parsed.positionals[0]);
4693
4686
  authorizeRunContext(store, taskId, runId, options.environment);
4694
- const delta = store.transaction((tx) => (buildRunContextDelta(tx, taskId, runId, rest[2])));
4695
- return output(`${JSON.stringify(delta, null, 2)}\n`, { contextDelta: delta });
4687
+ const delta = store.transaction((tx) => (buildRunContextDelta(tx, taskId, runId, parsed.values.get("--after"))));
4688
+ const data = boundedDocument(delta, `run:${taskId}/${runId}/delta/${parsed.values.get("--after")}`, parsed.cursor);
4689
+ return output(`${JSON.stringify(data)}\n`, { contextDelta: data });
4696
4690
  }
4697
- if (first === undefined || rest.length !== 0) {
4691
+ const parsed = readOptions(rest, ["--cursor"]);
4692
+ if (first === undefined || parsed.positionals.length !== 0) {
4698
4693
  throw usageError("Task run context usage: yui task run context <task>/<run>.");
4699
4694
  }
4700
4695
  const { taskId, runId } = parseRunContextReference(first);
4701
4696
  authorizeRunContext(store, taskId, runId, options.environment);
4702
4697
  const pack = store.transaction((tx) => buildRunContextPack(tx, taskId, runId));
4703
- return output(`${JSON.stringify(pack, null, 2)}\n`, { context: pack });
4698
+ const data = boundedDocument(pack, `run:${taskId}/${runId}/context`, parsed.cursor);
4699
+ return output(`${JSON.stringify(data)}\n`, { context: data });
4704
4700
  }
4705
4701
  function parseRunContextReference(value) {
4706
4702
  const [taskId, runId, extra] = value.split("/");
@@ -4736,41 +4732,12 @@ function authorizeRunContext(store, taskId, runId, environment) {
4736
4732
  }
4737
4733
  }
4738
4734
  function listRuns(args, store, options) {
4739
- const usage = "Task run list usage: yui task run list <task|task/work>.";
4740
- exactPositionals(args, 1, usage);
4741
4735
  const reference = args[0];
4742
- const task = store.getTask(reference);
4743
- const item = task === null ? requireWorkItem(store, reference, options) : null;
4744
- const taskId = task?.id ?? item.taskId;
4745
- const runs = store.listRuns(taskId).filter((run) => (item === null || run.workItemId === item.id));
4746
- if (runs.length === 0)
4747
- return "No AgentRuns found.\n";
4748
- const events = store.listEvents(taskId);
4749
- return `${renderTable(`AgentRuns: ${item?.id ?? taskId}`, [
4750
- { header: "AgentRun", minWidth: 6, maxWidth: 20 },
4751
- { header: "Role", minWidth: 4, maxWidth: 22 },
4752
- { header: "Subject", minWidth: 7, maxWidth: 24 },
4753
- { header: "Purpose", minWidth: 6, maxWidth: 10 },
4754
- { header: "Mode", minWidth: 4, maxWidth: 8 },
4755
- { header: "Effective", minWidth: 10, maxWidth: 30 },
4756
- { header: "Profile", minWidth: 7, maxWidth: 8 },
4757
- { header: "Permission", minWidth: 8, maxWidth: 16 },
4758
- { header: "Status", minWidth: 6, maxWidth: 12 },
4759
- { header: "History", minWidth: 7, maxWidth: 9 },
4760
- { header: "Summary", minWidth: 8, maxWidth: 58 }
4761
- ], runs.map((run) => [
4762
- run.id,
4763
- run.roleName,
4764
- run.workItemId ?? (run.reviewRoundId === undefined ? "task" : `review:${run.reviewRoundId}`),
4765
- run.purpose,
4766
- run.mode,
4767
- `${run.effective.agentId}/${run.effective.adapterId} r${run.effective.sourceDesiredRevision}`,
4768
- run.effective.profileAccess,
4769
- run.effective.permission.strategy,
4770
- run.status,
4771
- isTaskRecordRetired(events, "run", run.id) ? "retired" : "active",
4772
- run.result?.output ?? run.result?.diagnostic ?? "-"
4773
- ]), defaultTableWidth())}\n`;
4736
+ if (reference?.includes("/")) {
4737
+ const item = requireWorkItem(store, reference, options);
4738
+ return taskRecordList([item.taskId, "--work-item", item.id, ...args.slice(1)], store, "run", options.environment);
4739
+ }
4740
+ return taskRecordList(args, store, "run", options.environment);
4774
4741
  }
4775
4742
  /**
4776
4743
  * Settles only the known bootstrap split where a failed Task-final AgentRun
@@ -5683,8 +5650,8 @@ function retryFailedReviewRun(previous, store, options, now) {
5683
5650
  /** AgentRun details are retained audit evidence; continuation uses durable Task state. */
5684
5651
  function showRun(args, store, options) {
5685
5652
  const usage = "Task run show usage: yui task run show <task>/<run> [--json].";
5686
- const asJson = args.includes("--json");
5687
- const positionals = args.filter((arg) => arg !== "--json");
5653
+ const parsed = readOptions(args.filter((arg) => arg !== "--json"), ["--cursor"]);
5654
+ const positionals = parsed.positionals;
5688
5655
  exactPositionals(positionals, 1, usage);
5689
5656
  const data = store.transaction((tx) => {
5690
5657
  const run = requireRun(tx, positionals[0], options);
@@ -5693,9 +5660,9 @@ function showRun(args, store, options) {
5693
5660
  .find((entry) => entry?.recordKind === "run" && entry.recordId === run.id) ?? null;
5694
5661
  return { run: run, retirement, execution: runExecutionObservation(run, tx.getTaskRoleSessionSet(run.taskId, run.roleName)?.providerBinding, tx.listEvents(run.taskId)) };
5695
5662
  });
5696
- if (asJson) {
5697
- return { kind: "output", output: `${JSON.stringify(data, null, 2)}\n`, data };
5698
- }
5663
+ const bounded = boundedDocument(data, `run:${data.run.taskId}/${data.run.id}`, parsed.cursor);
5664
+ if ("contentPage" in bounded)
5665
+ return output(`${JSON.stringify(bounded, null, 2)}\n`, bounded);
5699
5666
  return {
5700
5667
  kind: "output",
5701
5668
  output: `Delivery observation: ${data.execution.delivery}\n` + renderRunShow(data.run, data.retirement),
@@ -6874,25 +6841,16 @@ function taskWakeDispatch(args, store, options) {
6874
6841
  function taskWakeInspectionCommand(args, store) {
6875
6842
  const [command, ...rest] = args;
6876
6843
  if (command === "list") {
6877
- const usage = "Task wake list usage: yui task wake list <task>.";
6878
- exactPositionals(rest, 1, usage);
6879
- const task = requireTask(store, rest[0]);
6844
+ const usage = "Task wake list usage: yui task wake list <task> [--limit <n>] [--cursor <cursor>].";
6845
+ const parsed = readOptions(rest);
6846
+ exactPositionals(parsed.positionals, 1, usage);
6847
+ const task = requireTask(store, parsed.positionals[0]);
6880
6848
  const wakes = store.listTaskWakes(task.id);
6881
- if (wakes.length === 0) {
6882
- return output(`No wakes recorded for ${task.id}.\n`, { taskId: task.id, wakes: [] });
6883
- }
6884
- const timeZone = store.getConfig().timeZone;
6885
- return output(`${renderTable(`Wakes: ${task.id}`, [
6886
- { header: "Wake", minWidth: 8, maxWidth: 18 },
6887
- { header: "Status", minWidth: 8, maxWidth: 12 },
6888
- { header: "Reasons", minWidth: 10, maxWidth: 40 },
6889
- { header: "Dispatched", minWidth: 10, maxWidth: 28 }
6890
- ], wakes.map((wake) => [
6891
- wake.id,
6892
- wake.status,
6893
- wake.reasons.map(renderWakeReason).join(", "),
6894
- presentTime(wake.createdAt, timeZone)
6895
- ]), defaultTableWidth())}\n`, { taskId: task.id, wakes });
6849
+ const data = recordPage(wakes.map(wake => ({
6850
+ id: wake.id, status: wake.status, fromCursor: wake.fromCursor, toCursor: wake.toCursor,
6851
+ createdAt: wake.createdAt, read: `task wake show ${task.id} ${wake.id}`
6852
+ })), `task:${task.id}/wake`, parsed);
6853
+ return output(`${JSON.stringify(data)}\n`, data);
6896
6854
  }
6897
6855
  if (command === "show") {
6898
6856
  const usage = "Task wake show usage: yui task wake show <task> <wake>.";
@@ -6962,9 +6920,12 @@ function taskWakeInspectionCommand(args, store) {
6962
6920
  taskId: task.id,
6963
6921
  deliveryEvents,
6964
6922
  wake,
6965
- events,
6966
- messages,
6967
- runs
6923
+ events: events.map(event => ({ id: event.id, type: event.type, createdAt: event.createdAt,
6924
+ read: `task event show ${task.id} ${event.id}` })),
6925
+ messages: messages.map(message => ({ id: message.id, kind: message.kind, author: message.author,
6926
+ resultRef: message.resultRef, read: `task message show ${task.id}/${message.id}` })),
6927
+ runs: runs.map(run => ({ id: run.id, status: run.status, purpose: run.purpose, roleName: run.roleName,
6928
+ read: `task run show ${task.id}/${run.id}` }))
6968
6929
  });
6969
6930
  }
6970
6931
  throw usageError(command === undefined