@zq-silk/yui 0.8.3 → 0.8.6

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 (60) hide show
  1. package/ARCHITECTURE.md +40 -22
  2. package/README.md +46 -16
  3. package/dist/cli/commandCatalog.js +23 -11
  4. package/dist/cli/operatorWizard.js +10 -20
  5. package/dist/cli.js +154 -16
  6. package/dist/commands/executionAuditCommands.js +30 -0
  7. package/dist/commands/operatorCommands.js +42 -1
  8. package/dist/commands/taskCommands.js +386 -138
  9. package/dist/commands/taskCompletionGate.js +36 -24
  10. package/dist/commands/taskContextCommand.js +6 -1
  11. package/dist/commands/taskInputCommands.js +48 -10
  12. package/dist/commands/taskNextActionCommand.js +36 -3
  13. package/dist/context/sessionBootstrapManifest.js +82 -1
  14. package/dist/controller/clientRuntime.js +7 -7
  15. package/dist/controller/controller.js +16 -8
  16. package/dist/controller/fileSchedulerStoreAdapter.js +64 -5
  17. package/dist/controller/handoverCandidate.js +10 -3
  18. package/dist/controller/sessionNotify.js +4 -22
  19. package/dist/executor/agentAdapter.js +2 -2
  20. package/dist/executor/agentExecutor.js +25 -5
  21. package/dist/executor/fileRoleLaunchPlanner.js +16 -11
  22. package/dist/integration/gitIntegrationService.js +50 -2
  23. package/dist/integration/integrationCheckEvidenceReuse.js +53 -0
  24. package/dist/observability/executionAudit.js +47 -1
  25. package/dist/observability/faultClassification.js +6 -4
  26. package/dist/observability/orchestrationMetrics.js +196 -0
  27. package/dist/operator/operatorSessionHistory.js +36 -0
  28. package/dist/release/releaseHandover.js +7 -5
  29. package/dist/release/runtimeRelease.js +15 -0
  30. package/dist/repository/taskWorkspaceCoordinator.js +13 -10
  31. package/dist/review/deltaRecheck.js +3 -2
  32. package/dist/review/reviewFindingLedger.js +5 -4
  33. package/dist/review/reviewOutcomeClassifier.js +252 -54
  34. package/dist/review/taskFinalReviewContractEvent.js +1 -0
  35. package/dist/review/taskFinalReviewContractRebind.js +350 -0
  36. package/dist/run/runIdentity.js +10 -70
  37. package/dist/runtime/agentHost.js +3 -4
  38. package/dist/runtime/codexAppServerRuntime.js +6 -0
  39. package/dist/runtime/firstProgressStopLoss.js +52 -0
  40. package/dist/runtime/launchBroker.js +10 -2
  41. package/dist/runtime/runtimeDeadlines.js +14 -0
  42. package/dist/runtime/sessionTitle.js +24 -12
  43. package/dist/runtime/structuredProviderHost.js +7 -1
  44. package/dist/runtime/tmuxAdapters.js +10 -3
  45. package/dist/scheduler/activeRoleRunDelivery.js +20 -18
  46. package/dist/scheduler/leaderWakeupProcessor.js +33 -2
  47. package/dist/scheduler/wakeReason.js +1 -0
  48. package/dist/storage/sqliteStore.js +8 -1
  49. package/dist/storage/taskStore.js +10 -1
  50. package/dist/task/completionReadiness.js +48 -19
  51. package/dist/task/deliveryGuard.js +3 -1
  52. package/dist/task/nextAction.js +145 -52
  53. package/dist/task/repairWave.js +14 -1
  54. package/dist/task/task.js +10 -0
  55. package/dist/web/webSnapshot.js +7 -1
  56. package/i18n/README.zh-CN.md +28 -8
  57. package/package.json +1 -1
  58. package/skills/yui-leader/SKILL.md +73 -31
  59. package/skills/yui-operator/SKILL.md +51 -10
  60. package/skills/yui-reviewer/SKILL.md +23 -0
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync } from "node:fs";
3
- import { resolve } from "node:path";
3
+ import { join, resolve } from "node:path";
4
4
  import { createInterface } from "node:readline/promises";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { isDeepStrictEqual } from "node:util";
@@ -36,8 +36,9 @@ import { runResourcesCommand } from "./commands/resourcesCommands.js";
36
36
  import { applyOperatorSessionControl, runOperatorCommand } from "./commands/operatorCommands.js";
37
37
  import { runProjectCommand } from "./commands/projectCommands.js";
38
38
  import { runProfileCommand } from "./commands/profileCommands.js";
39
- import { dispatchPreparedReviewRound, failPendingReviewRound, RESUMED_PENDING_FINAL_REVIEW, TERMINALIZED_LEADER_BEFORE_FINAL_REVIEW, TaskFinalReviewDispatchDriftError, preserveReviewRoundWorkspace, parseTaskCompletionRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, validateTaskArchiveRequest } from "./commands/taskCommands.js";
39
+ import { dispatchPreparedReviewRound, failPendingReviewRound, RESUMED_PENDING_FINAL_REVIEW, TERMINALIZED_LEADER_BEFORE_FINAL_REVIEW, TaskFinalReviewDispatchDriftError, preserveReviewRoundWorkspace, parseTaskCompletionRequest, parseTaskFinalReviewContractRebindRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, validateTaskArchiveRequest } from "./commands/taskCommands.js";
40
40
  import { taskActor } from "./commands/taskActor.js";
41
+ import { isCurrentGlobalOperator } from "./commands/taskInputCommands.js";
41
42
  import { runTaskIntegrationCommand } from "./commands/taskIntegrationCommands.js";
42
43
  import { runTaskChangeSetCommand } from "./commands/taskChangeSetCommands.js";
43
44
  import { runTaskOverlapCommand } from "./commands/taskOverlapCommands.js";
@@ -46,7 +47,7 @@ import { runTaskWorkspaceCommand } from "./commands/taskWorkspaceCommands.js";
46
47
  import { runWorkflowCommandAsync } from "./commands/workflowCommands.js";
47
48
  import { createUpdatePorts } from "./cli/updatePorts.js";
48
49
  import { createReleaseWorkflowPorts } from "./release/releaseWorkflowPorts.js";
49
- import { readRuntimeIdentity } from "./release/runtimeRelease.js";
50
+ import { acquireHandoverLock, readRuntimeIdentity } from "./release/runtimeRelease.js";
50
51
  import { renderReleaseActivateResult, renderReleaseInstallResult, renderReleaseList, runReleaseActivate, runReleaseInstall, runReleaseList } from "./commands/releaseCommands.js";
51
52
  import { reconcileTaskRemoteBaselines, verifyTaskCompletionPublishedTree } from "./commands/taskCompletionGate.js";
52
53
  import { runTaskBaseStatusCommand } from "./commands/taskBaseCommands.js";
@@ -81,8 +82,10 @@ import { TmuxWebTerminalService } from "./web/tmuxWebTerminal.js";
81
82
  import { listOperatorSessions, operatorSessionRef } from "./operator/operatorSessionHistory.js";
82
83
  import { YUI_VERSION, yuiVersionIdentity } from "./version.js";
83
84
  import { YUI_CONTROL_PLANE_DESCRIPTOR, YUI_TASK_RUNTIME_DESCRIPTOR, assertExactControlPlanePreflight, assertExactTaskRuntimeEnvironment, assertExactTaskRuntimeState, exactControlPlaneDigest, extractExactControlArgument, parseExactControlPlaneDescriptor } from "./runtime/exactControlPlane.js";
85
+ import { readSessionBootstrapManifest } from "./context/sessionBootstrapManifest.js";
84
86
  import { builtinAgentDriverRegistry } from "./runtime/builtinAgentDrivers.js";
85
87
  import { createTaskFinalReviewContract, extractTaskFinalReviewRequest } from "./review/taskFinalReviewContract.js";
88
+ import { prepareTaskFinalReviewContractRebindProof, resolveRecordedTaskFinalReviewContract } from "./review/taskFinalReviewContractRebind.js";
86
89
  import { assessDeltaRecheck } from "./review/deltaRecheck.js";
87
90
  import { deltaRecheckEnabled } from "./review/reviewConfig.js";
88
91
  import { NodeGitWorkspace } from "./repository/gitWorkspace.js";
@@ -109,7 +112,7 @@ void main().catch((error) => {
109
112
  process.exitCode = 5;
110
113
  });
111
114
  export async function main() {
112
- const { contract: taskFinalReviewContract, verifiedStore } = await preflightManagedTaskControlPlane();
115
+ const { contract: taskFinalReviewContract, verifiedStore, controlPlane: exactCurrentControlPlane } = await preflightManagedTaskControlPlane();
113
116
  if (args.length === 0) {
114
117
  emit(renderCommandHelp((await import("./cli/commandCatalog.js")).ROOT_COMMAND, VERSION));
115
118
  return;
@@ -988,6 +991,59 @@ export async function main() {
988
991
  }
989
992
  }
990
993
  }
994
+ let taskFinalReviewRebindProof;
995
+ let taskFinalReviewRebindRequest;
996
+ let releaseTaskFinalReviewHandoverLock;
997
+ if (resolved[1] === "review" && resolved[2] === "rebind") {
998
+ if (!isCurrentGlobalOperator(store, process.env)) {
999
+ throw usageError("Task-final Review contract rebind requires the authenticated global Operator session.");
1000
+ }
1001
+ if (exactCurrentControlPlane === undefined) {
1002
+ throw usageError("Task-final Review contract rebind requires the exact current global Session CLI.");
1003
+ }
1004
+ taskFinalReviewRebindRequest = parseTaskFinalReviewContractRebindRequest(resolved.slice(3));
1005
+ }
1006
+ if ((resolved[1] === "review"
1007
+ && ["request", "force-fresh", "retry", "rebind"].includes(resolved[2] ?? ""))
1008
+ || resolved[1] === "complete") {
1009
+ const handoverLock = acquireHandoverLock(home);
1010
+ releaseTaskFinalReviewHandoverLock = handoverLock.release;
1011
+ }
1012
+ if (taskFinalReviewRebindRequest !== undefined) {
1013
+ const request = taskFinalReviewRebindRequest;
1014
+ try {
1015
+ taskFinalReviewRebindProof = prepareTaskFinalReviewContractRebindProof({
1016
+ home,
1017
+ taskId: request.taskId,
1018
+ fromControlPlaneDigest: request.fromControlPlaneDigest,
1019
+ toControlPlaneDigest: request.toControlPlaneDigest,
1020
+ fromReleaseId: request.fromReleaseId,
1021
+ toReleaseId: request.toReleaseId,
1022
+ currentControlPlane: exactCurrentControlPlane.descriptor
1023
+ });
1024
+ const workItems = store.listWorkItems(request.taskId);
1025
+ const reviewRounds = store.listReviewRounds(request.taskId);
1026
+ const resolution = resolveRecordedTaskFinalReviewContract(request.taskId, workItems, reviewRounds, store.listEvents(request.taskId));
1027
+ const activeRound = reviewRounds.find((round) => ((round.scope ?? "work-item") === "task"
1028
+ && (round.status === "pending" || round.status === "running")));
1029
+ // An exact replay is a true no-op, even when the target Leader is live.
1030
+ // Invalid/stale source tuples also reach the command unchanged so its
1031
+ // transactional diagnosis cannot stop a healthy target runtime.
1032
+ if (resolution?.effective.controlPlaneDigest === request.fromControlPlaneDigest
1033
+ && activeRound === undefined) {
1034
+ // The contract event and wake must never target a Provider
1035
+ // Conversation launched by the source release. Active Runs fail
1036
+ // closed; otherwise stop and retire that runtime under the handover
1037
+ // lock before the transactional event+wake commit.
1038
+ await workspaceCoordinator.cleanupTaskRoleRuntime(request.taskId, "leader");
1039
+ }
1040
+ }
1041
+ catch (error) {
1042
+ releaseTaskFinalReviewHandoverLock();
1043
+ releaseTaskFinalReviewHandoverLock = undefined;
1044
+ throw error;
1045
+ }
1046
+ }
991
1047
  let candidateMaterialization;
992
1048
  let candidateMaterializationCommitted = false;
993
1049
  try {
@@ -1002,7 +1058,7 @@ export async function main() {
1002
1058
  ? await candidateSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract)
1003
1059
  : candidateMaterialization.snapshot;
1004
1060
  const directTaskMainSnapshot = await directTaskMainSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract);
1005
- const actualTaskReviewCandidate = await actualTaskReviewCandidateForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract);
1061
+ const actualTaskReviewCandidate = await actualTaskReviewCandidateForTaskCommand(resolved, store, workspacePreparer, process.env);
1006
1062
  const deltaRecheckPreflight = await deltaRecheckPreflightForTaskCommand(resolved.slice(1), store, actualTaskReviewCandidate);
1007
1063
  const reviewWorkspaceResult = await reviewWorkspaceResultForTaskCommand(resolved, store, workspacePreparer, process.env);
1008
1064
  const executionLaneGitSnapshot = await executionLaneGitSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env);
@@ -1016,6 +1072,9 @@ export async function main() {
1016
1072
  ...(taskFinalReviewContract === undefined
1017
1073
  ? {}
1018
1074
  : { taskFinalReviewContract }),
1075
+ ...(taskFinalReviewRebindProof === undefined
1076
+ ? {}
1077
+ : { taskFinalReviewRebindProof }),
1019
1078
  ...(completionSummary === undefined ? {} : { completionSummary }),
1020
1079
  ...(completionPublishedTreeProof === undefined
1021
1080
  ? {}
@@ -1260,6 +1319,12 @@ export async function main() {
1260
1319
  }
1261
1320
  throw error;
1262
1321
  }
1322
+ finally {
1323
+ if (releaseTaskFinalReviewHandoverLock !== undefined) {
1324
+ releaseTaskFinalReviewHandoverLock();
1325
+ releaseTaskFinalReviewHandoverLock = undefined;
1326
+ }
1327
+ }
1263
1328
  }
1264
1329
  if (resolved[0] === "jobs") {
1265
1330
  emit(runJobCommand(resolved.slice(1), store, { runtime }));
@@ -1300,12 +1365,12 @@ async function preflightManagedTaskControlPlane() {
1300
1365
  }
1301
1366
  if (!exactRuntime) {
1302
1367
  if (exactControlInvocation.digest !== undefined) {
1303
- throw new Error("Exact Task control-plane invocation requires its frozen runtime descriptors.");
1368
+ return await preflightManagedGlobalControlPlane(exactControlInvocation.digest);
1304
1369
  }
1305
1370
  if (taskFinalReviewInvocation.request !== undefined) {
1306
1371
  throw new Error("Task final-review contract requires a verified exact Task control-plane invocation.");
1307
1372
  }
1308
- return { contract: undefined, verifiedStore: undefined };
1373
+ return { contract: undefined, controlPlane: undefined, verifiedStore: undefined };
1309
1374
  }
1310
1375
  if (process.env.YUI_SESSION_SCOPE !== "task") {
1311
1376
  throw new Error("Exact Task control-plane invocation requires a managed Task runtime.");
@@ -1349,8 +1414,13 @@ async function preflightManagedTaskControlPlane() {
1349
1414
  }
1350
1415
  : {});
1351
1416
  const request = taskFinalReviewInvocation.request;
1352
- if (request === undefined)
1353
- return { contract: undefined, verifiedStore };
1417
+ if (request === undefined) {
1418
+ return {
1419
+ contract: undefined,
1420
+ controlPlane: { digest, descriptor: control },
1421
+ verifiedStore
1422
+ };
1423
+ }
1354
1424
  if (runtime.roleName !== "leader") {
1355
1425
  throw new Error("Only the exact Task Leader invocation may establish a final-review contract.");
1356
1426
  }
@@ -1363,9 +1433,50 @@ async function preflightManagedTaskControlPlane() {
1363
1433
  reviewerRoleName: request.reviewerRoleName,
1364
1434
  controlPlaneDigest: digest
1365
1435
  }),
1436
+ controlPlane: { digest, descriptor: control },
1366
1437
  verifiedStore
1367
1438
  };
1368
1439
  }
1440
+ async function preflightManagedGlobalControlPlane(digest) {
1441
+ if (process.env.YUI_SESSION_SCOPE !== "global") {
1442
+ throw new Error("Exact Task control-plane invocation requires its frozen runtime descriptors.");
1443
+ }
1444
+ if (taskFinalReviewInvocation.request !== undefined) {
1445
+ throw new Error("Task final-review contract establishment requires a verified exact Task control-plane invocation.");
1446
+ }
1447
+ const manifestPath = process.env.YUI_SESSION_MANIFEST;
1448
+ const sessionCliPath = process.env.YUI_SESSION_CLI;
1449
+ if (manifestPath === undefined || sessionCliPath === undefined) {
1450
+ throw new Error("Exact global control-plane invocation requires its Session Manifest and CLI.");
1451
+ }
1452
+ const manifest = readSessionBootstrapManifest(manifestPath);
1453
+ const expectedRoleKind = process.env.YUI_ROLE === "operator" ? "operator" : "global";
1454
+ if (manifest.owner.scope !== "global"
1455
+ || manifest.roleKind !== expectedRoleKind
1456
+ || resolve(manifest.controlPlane.sessionCliPath) !== resolve(sessionCliPath)
1457
+ || manifest.controlPlane.digest !== digest) {
1458
+ throw new Error("Exact global control-plane invocation does not match its Session Manifest.");
1459
+ }
1460
+ const serializedDescriptor = readFileSync(manifest.controlPlane.descriptorPath, "utf8");
1461
+ const descriptor = parseExactControlPlaneDescriptor(serializedDescriptor);
1462
+ if (exactControlPlaneDigest(descriptor) !== digest
1463
+ || resolve(manifest.controlPlane.descriptorPath) !== resolve(join(descriptor.yuiHome, "runtime", "control-plane", `${digest}.json`))
1464
+ || resolve(manifestPath) !== resolve(join(descriptor.yuiHome, "runtime", "session-manifests", `${manifest.digest}.json`))) {
1465
+ throw new Error("Exact global Session Manifest does not match its control-plane descriptor.");
1466
+ }
1467
+ await assertExactControlPlanePreflight({
1468
+ serializedDescriptor,
1469
+ digest,
1470
+ actualExecutable: process.execPath,
1471
+ actualCliEntry: fileURLToPath(import.meta.url),
1472
+ actualHome: resolveYuiHome(process.env)
1473
+ });
1474
+ return {
1475
+ contract: undefined,
1476
+ controlPlane: { digest, descriptor },
1477
+ verifiedStore: openCompatibleFileTaskStore(descriptor.yuiHome)
1478
+ };
1479
+ }
1369
1480
  function cleanupCliError(error, fallbackResource) {
1370
1481
  if (error instanceof WorkspaceCleanupBlockedError) {
1371
1482
  return usageError(error.message, undefined, cleanupBlockedDetails(error.reason, error.resource, error.retryable));
@@ -1630,6 +1741,36 @@ async function prepareReviewLaneWorkspaces(taskId, reviewRoundId, store, prepare
1630
1741
  return map;
1631
1742
  }
1632
1743
  async function directTaskMainSnapshotForTaskCommand(args, store, preparer, environment, taskFinalReviewContract) {
1744
+ const deliveryOption = args.indexOf("--delivery", 3);
1745
+ const deliveryPromotion = args[0] === "task"
1746
+ && args[1] === "update"
1747
+ && args[2] !== undefined
1748
+ && (args.includes("--require-integration", 3)
1749
+ || (deliveryOption >= 0 && args[deliveryOption + 1] === "integrated"));
1750
+ if (deliveryPromotion) {
1751
+ const task = store.getTask(args[2]);
1752
+ if (task === null
1753
+ || (task.status !== "active" && task.status !== "draft")
1754
+ || task.requireIntegration === true
1755
+ || task.projectBindings.length === 0) {
1756
+ return undefined;
1757
+ }
1758
+ const workspace = store.getTaskWorkspace(task.id);
1759
+ if (task.status === "draft" && workspace === null)
1760
+ return undefined;
1761
+ if (workspace === null
1762
+ || workspace.owner.type !== "task"
1763
+ || workspace.owner.taskId !== task.id) {
1764
+ throw usageError(`Task has no authoritative main workspace: ${task.id}.`);
1765
+ }
1766
+ try {
1767
+ return await preparer.snapshotDirectTaskMain(workspace, task.projectBindings.map(({ projectId }) => projectId));
1768
+ }
1769
+ catch (error) {
1770
+ throw usageError(`Delivery promotion Task-main verification failed for ${task.id}: `
1771
+ + `${error instanceof Error ? error.message : String(error)}`);
1772
+ }
1773
+ }
1633
1774
  if (taskFinalReviewContract === undefined
1634
1775
  || args[0] !== "task"
1635
1776
  || args[1] !== "work"
@@ -1660,7 +1801,7 @@ async function directTaskMainSnapshotForTaskCommand(args, store, preparer, envir
1660
1801
  throw usageError(error instanceof Error ? error.message : String(error));
1661
1802
  }
1662
1803
  }
1663
- async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, environment, taskFinalReviewContract) {
1804
+ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, environment) {
1664
1805
  if (args[0] !== "task")
1665
1806
  return undefined;
1666
1807
  let taskId;
@@ -1669,12 +1810,9 @@ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, en
1669
1810
  if (task === null || task.status !== "active" || task.projectBindings.length === 0) {
1670
1811
  return undefined;
1671
1812
  }
1672
- const establishedFinalRound = store.listReviewRounds(task.id).some((round) => ((round.scope ?? "work-item") === "task"));
1673
- if (taskFinalReviewContract === undefined
1674
- && store.getReviewConfig()?.trigger !== "final"
1675
- && !establishedFinalRound) {
1676
- return undefined;
1677
- }
1813
+ // Every Project-backed completion, including direct delivery, must freeze
1814
+ // a clean committed Task-main snapshot. Review policy only decides whether
1815
+ // that head also needs an independent ReviewRound.
1678
1816
  taskId = task.id;
1679
1817
  }
1680
1818
  else if (args[1] === "review"
@@ -213,6 +213,36 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
213
213
  else {
214
214
  lines.push("", ...sectionError("workItems", report));
215
215
  }
216
+ if (report.orchestration.status === "ok" && report.orchestration.data !== undefined) {
217
+ const orchestration = report.orchestration.data;
218
+ lines.push("", `Orchestration: ${orchestration.tasks.length} Task(s) · ${orchestration.advisoryCount} advisory finding(s)`);
219
+ if (orchestration.tasks.length > 0) {
220
+ lines.push(renderTable("Task orchestration metrics", [
221
+ { header: "Task", minWidth: 7, maxWidth: 14 },
222
+ { header: "Delivery", minWidth: 8, maxWidth: 10 },
223
+ { header: "Runs", minWidth: 4, maxWidth: 6 },
224
+ { header: "WIs", minWidth: 3, maxWidth: 5 },
225
+ { header: "Review F/D/N", minWidth: 12, maxWidth: 16 },
226
+ { header: "Integration A/F/R", minWidth: 17, maxWidth: 20 },
227
+ { header: "Pre-progress gen", minWidth: 12, maxWidth: 16 },
228
+ { header: "Terminal ws", minWidth: 10, maxWidth: 12 },
229
+ { header: "Advice", minWidth: 6, maxWidth: 8 }
230
+ ], orchestration.tasks.map((task) => [
231
+ task.taskId,
232
+ task.deliveryPath,
233
+ String(task.runs.total),
234
+ String(task.workItems),
235
+ `${task.reviews.full}/${task.reviews.delta}/${task.reviews.nonSemantic}`,
236
+ `${task.integrations.attempts}/${task.integrations.failed}/${task.integrations.repeatedIdentities}`,
237
+ String(task.providerGenerationsBeforeFirstProgress),
238
+ String(task.terminalWorkspaceCount),
239
+ String(task.advisories.length)
240
+ ]), width));
241
+ }
242
+ }
243
+ else {
244
+ lines.push("", ...sectionError("orchestration", report));
245
+ }
216
246
  if (report.storage.status === "ok" && report.storage.data !== undefined) {
217
247
  const storage = report.storage.data;
218
248
  lines.push("", `Storage: backend ${storage.backend} · state.json ${formatBytes(storage.stateJsonBytes)} · yui.db ${formatBytes(storage.databaseBytes)}`
@@ -1,6 +1,6 @@
1
1
  import { usageError } from "../errors/cliError.js";
2
2
  import { createRoleSessionSet } from "../executor/agentExecutor.js";
3
- import { listOperatorSessions, prepareOperatorNewSession, prepareOperatorResumeSession } from "../operator/operatorSessionHistory.js";
3
+ import { listOperatorSessions, projectOperatorStatus, prepareOperatorNewSession, prepareOperatorResumeSession } from "../operator/operatorSessionHistory.js";
4
4
  import { defaultTableWidth, renderTable } from "../output/table.js";
5
5
  import { formatRelativeTimestamp } from "../output/timePresentation.js";
6
6
  import { updateGlobalRole } from "../role/role.js";
@@ -11,6 +11,7 @@ export function runOperatorCommand(args, store, options = {}) {
11
11
  const [command, ...rest] = args;
12
12
  switch (command) {
13
13
  case "list": return listSessions(rest, store, options);
14
+ case "status": return status(rest, store, options);
14
15
  case "new": return newSession(rest, store);
15
16
  case "resume": return resumeSession(rest, store);
16
17
  case "submit": return submit(rest, store, options);
@@ -20,6 +21,46 @@ export function runOperatorCommand(args, store, options = {}) {
20
21
  : `Unknown command: operator ${command}`);
21
22
  }
22
23
  }
24
+ function status(args, store, options) {
25
+ if (args.length !== 0)
26
+ throw usageError("Operator status usage: yui operator status.");
27
+ const role = requireOperator(store);
28
+ const activeBinding = role.agentBindings[role.activeAgentId];
29
+ if (activeBinding === undefined) {
30
+ throw usageError(`Operator active Agent is not bound: ${role.activeAgentId}.`);
31
+ }
32
+ const projection = projectOperatorStatus(store.getGlobalRoleSessionSet(role.name), role.activeAgentId, activeBinding.adapterId);
33
+ const writer = projection.writer;
34
+ const writerLines = [
35
+ "Operator status",
36
+ "Active writer:",
37
+ ` State: ${writer.state}`,
38
+ ` Agent: ${writer.agentId}`,
39
+ ` Adapter: ${adapterLabel(writer.adapterId)}`,
40
+ ` Conversation: ${writer.sessionRef ?? "-"}`,
41
+ ` Native session: ${writer.nativeSessionId ?? "-"}`,
42
+ ` Session state: ${writer.sessionStatus ?? "-"}`
43
+ ];
44
+ const history = projection.historicalConversations;
45
+ const historyOutput = history.length === 0
46
+ ? "Historical conversations: none"
47
+ : renderTable("Historical conversations", [
48
+ { header: "Updated", minWidth: 7, maxWidth: 12 },
49
+ { header: "Agent", minWidth: 6, maxWidth: 16 },
50
+ { header: "Conversation", minWidth: 20, maxWidth: 72 },
51
+ { header: "State", minWidth: 7, maxWidth: 10 }
52
+ ], history.map((session) => [
53
+ formatRelativeTimestamp(session.updatedAt, options.now?.() ?? new Date()),
54
+ adapterLabel(session.adapterId),
55
+ session.displayTitle,
56
+ session.state
57
+ ]), options.width ?? defaultTableWidth());
58
+ return {
59
+ kind: "output",
60
+ output: `${writerLines.join("\n")}\n\n${historyOutput}\n`,
61
+ data: projection
62
+ };
63
+ }
23
64
  function submit(rest, store, options) {
24
65
  const usage = "Operator submit usage: yui operator submit (<body>|--body-file <path|->) [--task <id>].";
25
66
  const positionals = [];