@zq-silk/yui 2.21.0 → 2.22.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 (125) hide show
  1. package/README.md +5 -0
  2. package/dist/cli/commandCatalog.js +85 -25
  3. package/dist/cli/dynamicCompletion.js +4 -1
  4. package/dist/cli/helpRenderer.js +12 -1
  5. package/dist/cli/interactionPolicy.js +2 -0
  6. package/dist/cli/invocationRouter.js +5 -5
  7. package/dist/cli/roleWizard.js +5 -1
  8. package/dist/commands/globalRoleCommands.js +33 -8
  9. package/dist/commands/inboxCommands.js +39 -12
  10. package/dist/commands/operatorCommands.js +2 -5
  11. package/dist/commands/releaseCommands.js +16 -2
  12. package/dist/commands/roleConfiguration.js +25 -2
  13. package/dist/commands/sessionCommands.js +5 -2
  14. package/dist/commands/taskCommands.js +217 -64
  15. package/dist/commands/taskContextCommand.js +26 -4
  16. package/dist/commands/taskFactCommands.js +15 -2
  17. package/dist/commands/taskIntegrationCommands.js +3 -3
  18. package/dist/commands/taskRoleRuntimeStatus.js +15 -5
  19. package/dist/commands/textInput.js +2 -2
  20. package/dist/context/contextFamilies.js +10 -0
  21. package/dist/context/currentSkills.js +6 -4
  22. package/dist/context/runContextPack.js +44 -5
  23. package/dist/context/runInputContract.js +1 -1
  24. package/dist/context/sessionBootstrapManifest.js +7 -5
  25. package/dist/context/sessionSkillNotice.js +53 -0
  26. package/dist/context/taskContext.js +100 -16
  27. package/dist/controlPlaneCli.js +17 -7
  28. package/dist/controller/agentRuntimeObserver.js +57 -12
  29. package/dist/controller/clientRuntime.js +37 -8
  30. package/dist/controller/controller.js +3 -1
  31. package/dist/controller/diagnosticJournal.js +10 -2
  32. package/dist/controller/fileSchedulerStoreAdapter.js +72 -24
  33. package/dist/controller/messageHealth.js +4 -3
  34. package/dist/controller/providerRetryAdmission.js +15 -1
  35. package/dist/controller/providerRetryDelivery.js +18 -2
  36. package/dist/controller/runtime.js +15 -5
  37. package/dist/controller/runtimeEventInbox.js +107 -13
  38. package/dist/controller/runtimeEventProcessor.js +3 -3
  39. package/dist/controller/taskRunRecovery.js +153 -0
  40. package/dist/core/controllerClient.js +16 -2
  41. package/dist/core/controllerServer.js +14 -5
  42. package/dist/core/controllerTelemetry.js +51 -5
  43. package/dist/errors/cliFailure.js +14 -5
  44. package/dist/executor/agentAdapter.js +32 -4
  45. package/dist/executor/agentConfigurationFields.js +2 -0
  46. package/dist/integration/gitIntegrationService.js +3 -2
  47. package/dist/integration/integrationSourceApplication.js +12 -1
  48. package/dist/interaction/notificationPresentation.js +11 -16
  49. package/dist/interaction/operatorPresentation.js +1 -1
  50. package/dist/message/messageContinuation.js +3 -1
  51. package/dist/output/boundedRead.js +36 -7
  52. package/dist/output/rolePresentation.js +3 -1
  53. package/dist/runtime/agentHost.js +12 -3
  54. package/dist/runtime/codexAppServerRuntime.js +110 -24
  55. package/dist/runtime/codexInteractiveHost.js +46 -0
  56. package/dist/runtime/providerRetry.js +5 -2
  57. package/dist/runtime/providerRuntimeIdentity.js +1 -1
  58. package/dist/runtime/structuredProviderHost.js +32 -34
  59. package/dist/scheduler/wakeupQueue.js +10 -4
  60. package/dist/storage/sqliteSchema.js +9 -0
  61. package/dist/storage/storageVersions.js +1 -1
  62. package/dist/tmux/tmuxManager.js +4 -2
  63. package/dist/web/webSettings.js +2 -0
  64. package/docs/authorization.md +18 -3
  65. package/docs/cli-information-contract.md +228 -6
  66. package/docs/cli-information-contract.zh-CN.md +95 -5
  67. package/docs/managed-turn-and-session-runtime.md +82 -21
  68. package/docs/managed-turn-and-session-runtime.zh-CN.md +56 -13
  69. package/docs/provider-retry.md +11 -0
  70. package/docs/release-workflow.md +37 -0
  71. package/docs/release-workflow.zh-CN.md +28 -0
  72. package/docs/roles-and-configuration.md +28 -0
  73. package/docs/roles-and-configuration.zh-CN.md +21 -0
  74. package/docs/storage-baseline.md +4 -1
  75. package/docs/storage-baseline.zh-CN.md +1 -1
  76. package/docs/task-delivery.md +7 -0
  77. package/docs/task-delivery.zh-CN.md +5 -0
  78. package/docs/testing/review-evaluation.md +7 -7
  79. package/docs/testing/review-evaluation.zh-CN.md +7 -7
  80. package/docs/testing/verification-levels.md +45 -2
  81. package/docs/testing/verification-levels.zh-CN.md +21 -2
  82. package/i18n/README.zh-CN.md +6 -0
  83. package/package.json +1 -1
  84. package/skills/yui-leader/SKILL.md +98 -193
  85. package/skills/yui-leader/VERSION +1 -1
  86. package/skills/yui-leader/references/artifacts.md +41 -0
  87. package/skills/yui-leader/references/assignment-execution.md +140 -0
  88. package/skills/yui-leader/references/completion.md +79 -0
  89. package/skills/yui-leader/references/delivery-scope.md +26 -0
  90. package/skills/yui-leader/references/execution.md +16 -487
  91. package/skills/yui-leader/references/implementation.md +81 -0
  92. package/skills/yui-leader/references/planning.md +7 -45
  93. package/skills/yui-leader/references/review.md +90 -0
  94. package/skills/yui-leader/references/task-progress.md +23 -0
  95. package/skills/yui-leader/references/work-allocation.md +118 -0
  96. package/skills/yui-operator/SKILL.md +77 -333
  97. package/skills/yui-operator/VERSION +1 -1
  98. package/skills/yui-operator/references/body-transport.md +62 -0
  99. package/skills/yui-operator/references/configuration.md +47 -0
  100. package/skills/yui-operator/references/lifecycle.md +41 -0
  101. package/skills/yui-operator/references/progress.md +39 -0
  102. package/skills/yui-operator/references/routing.md +57 -0
  103. package/skills/yui-operator/references/submission.md +56 -0
  104. package/skills/yui-operator/references/task-delivery.md +1 -1
  105. package/skills/yui-operator/references/web-lifecycle.md +19 -0
  106. package/skills/yui-operator/scripts/verify-submission.mjs +37 -0
  107. package/skills/yui-runtime/SKILL.md +177 -531
  108. package/skills/yui-runtime/VERSION +1 -1
  109. package/skills/yui-runtime/references/archive.md +51 -0
  110. package/skills/yui-runtime/references/command-discovery.md +41 -0
  111. package/skills/yui-runtime/references/completed-followup.md +65 -0
  112. package/skills/yui-runtime/references/context-ordering.md +3 -34
  113. package/skills/yui-runtime/references/context-reads.md +61 -0
  114. package/skills/yui-runtime/references/dispatched-run.md +61 -0
  115. package/skills/yui-runtime/references/execution-failure.md +105 -0
  116. package/skills/yui-runtime/references/global-context.md +18 -0
  117. package/skills/yui-runtime/references/global-input-controls.md +44 -0
  118. package/skills/yui-runtime/references/host-custody.md +30 -0
  119. package/skills/yui-runtime/references/inbox-recovery.md +77 -0
  120. package/skills/yui-runtime/references/incremental-context.md +40 -0
  121. package/skills/yui-runtime/references/publication.md +4 -111
  122. package/skills/yui-runtime/references/recovery.md +32 -192
  123. package/skills/yui-runtime/references/session-identity.md +18 -0
  124. package/skills/yui-runtime/references/session-replacement.md +45 -0
  125. package/skills/yui-runtime/references/uncertain-execution.md +29 -0
@@ -31,7 +31,7 @@ export async function startStructuredProviderSession(payload, input = {}) {
31
31
  if (control.adapterId === "codex" && control.transport === "codex-app-server-proxy") {
32
32
  const proxy = await connectCodexProxy(payload, mirror, true);
33
33
  try {
34
- const opened = await CodexStructuredProviderSession.open(proxy.child, proxy.exit, proxy.processInstanceId, payload, control, proxy.channel, input.onStarted, input.onTerminal, input.onGoal, input.onInput, input.onActivity, input.onDiagnostic, mirror);
34
+ const opened = await CodexStructuredProviderSession.open(proxy.child, proxy.exit, proxy.processInstanceId, payload, control, proxy.channel, input.onStarted, input.onTerminal, input.onGoal, input.onInput, input.onActivity, mirror);
35
35
  return Object.freeze({
36
36
  session: opened.session,
37
37
  ...(opened.recoveredTerminal === undefined
@@ -238,7 +238,10 @@ class CodexProxyWebSocketChannel {
238
238
  this.#pending.delete(id);
239
239
  reject(new Error(`Provider request timed out: ${method}.`));
240
240
  }, PROVIDER_ACCEPT_TIMEOUT_MS);
241
- this.#pending.set(id, { resolve: resolvePromise, reject, timer });
241
+ this.#pending.set(id, {
242
+ resolve: resolvePromise, reject, timer, method,
243
+ requestBytes: Buffer.byteLength(JSON.stringify({ id, method, params }), "utf8")
244
+ });
242
245
  });
243
246
  try {
244
247
  await this.send({ id, method, params });
@@ -282,7 +285,7 @@ class CodexProxyWebSocketChannel {
282
285
  if (isBinary || encoded.byteLength > PROVIDER_MESSAGE_MAX_BYTES) {
283
286
  this.#close(new Error(isBinary
284
287
  ? "Provider returned an unsupported binary WebSocket message."
285
- : "Provider response message exceeds its bound."));
288
+ : `Provider response message exceeds its bound (receivedBytes=${encoded.byteLength}).`));
286
289
  terminateProcessGroup(this.child, "SIGTERM");
287
290
  return;
288
291
  }
@@ -320,6 +323,15 @@ class CodexProxyWebSocketChannel {
320
323
  #close(error) {
321
324
  if (this.#closedError !== undefined)
322
325
  return;
326
+ // A rejected WebSocket frame has not reached #receive: its full size and
327
+ // response/notification identity are unknown. Record pending request
328
+ // metadata, not parameters or transcript bytes, and never blame a specific
329
+ // response just because that request was in flight.
330
+ const pendingMetadata = [...this.#pending].slice(0, 8).map(([id, pending]) => ({
331
+ id, method: pending.method, requestBytes: pending.requestBytes
332
+ }));
333
+ error = new Error(`Codex proxy transport closed: ${error.message}; messageLimitBytes=${PROVIDER_MESSAGE_MAX_BYTES}; `
334
+ + `pendingRequests=${JSON.stringify(pendingMetadata)}; pendingCount=${this.#pending.size}.`, { cause: error });
323
335
  this.#closedError = error;
324
336
  if (!this.#ready)
325
337
  this.#rejectReady(error);
@@ -410,7 +422,7 @@ class CodexStructuredProviderSession {
410
422
  this.mirror = mirror;
411
423
  this.nativeAccountHome = nativeAccountHome;
412
424
  }
413
- static async open(child, exit, processInstanceId, payload, control, channel, onStarted, onTerminal, onGoal, onInput, onActivity, onDiagnostic, mirror) {
425
+ static async open(child, exit, processInstanceId, payload, control, channel, onStarted, onTerminal, onGoal, onInput, onActivity, mirror) {
414
426
  const openingMessages = [];
415
427
  const stopOpeningBuffer = channel.onMessage((message) => openingMessages.push(message));
416
428
  const initialized = await channel.request("initialize", codexClientInitialization());
@@ -419,7 +431,6 @@ class CodexStructuredProviderSession {
419
431
  let conversationId;
420
432
  let nativeConfiguration;
421
433
  let resumedActiveTurnId;
422
- let resumedTurns = [];
423
434
  if (control.mode === "new") {
424
435
  const opened = await runtime.openConversation({
425
436
  cwd: payload.cwd,
@@ -437,7 +448,6 @@ class CodexStructuredProviderSession {
437
448
  conversationId = resumed.threadId;
438
449
  nativeConfiguration = resumed.raw;
439
450
  resumedActiveTurnId = resumed.activeTurnId;
440
- resumedTurns = resumed.turns;
441
451
  }
442
452
  catch (error) {
443
453
  if (codexAppServerErrorIsMissing(error)) {
@@ -446,37 +456,25 @@ class CodexStructuredProviderSession {
446
456
  throw error;
447
457
  }
448
458
  }
449
- const threadMetadata = nativeConfiguration.thread;
450
- const nativeTitle = typeof threadMetadata === "object" && threadMetadata !== null
451
- && !Array.isArray(threadMetadata) ? threadMetadata.name : undefined;
452
- // A native rename belongs to the user. Missing metadata is not evidence
453
- // that an old title is still ours; leave it alone on resume.
454
- let appliedGeneratedTitle;
455
- if (control.sessionTitle !== undefined && (control.mode === "new"
456
- || (control.previousSessionTitle !== undefined && nativeTitle === control.previousSessionTitle))) {
457
- try {
458
- await runtime.setConversationName({
459
- conversationId,
460
- name: control.sessionTitle
461
- });
462
- appliedGeneratedTitle = control.sessionTitle;
463
- }
464
- catch (error) {
465
- onDiagnostic?.({
466
- nativeSessionId: conversationId,
467
- failure: providerDeliveryFailureFrom(error, {
468
- phase: "session-start",
469
- inputDisposition: "not-accepted",
470
- sessionDisposition: "recoverable"
471
- })
472
- });
473
- }
459
+ if (control.sessionTitle !== undefined) {
460
+ // The native title mutation can persist without replying and stall later
461
+ // Goal/Turn requests, even through a separate metadata connection. Keep
462
+ // Yui's launch title, but do not put that optional mutation on a managed
463
+ // Session's execution path. Reconnect must not replay it either.
464
+ // This is an intentional omission, not a failed delivery or unavailable
465
+ // observer. Keep it in the Host log without waking an Agent to recover
466
+ // an operation that was never attempted.
467
+ mirror("stderr", "Native title synchronization was not submitted; Yui retains its Session title without blocking native Goal/Turn control.\n");
474
468
  }
469
+ const ownedTurn = control.kind === "restore" ? control.ownedTurn : undefined;
470
+ // Keep notifications buffered during paginated recovery. Otherwise a live
471
+ // terminal can be emitted before open returns and then returned again as a
472
+ // synthetic recovered terminal, or a newer start can be overwritten.
473
+ const recoveredOwnedTurn = ownedTurn !== undefined && resumedActiveTurnId !== ownedTurn.turnId
474
+ ? await runtime.readTurn(conversationId, ownedTurn.turnId) : undefined;
475
475
  const session = new CodexStructuredProviderSession(child, exit, processInstanceId, conversationId, runtime, onStarted, onTerminal, onGoal, onInput, onActivity, mirror, optionalId(initialized.codexHome));
476
476
  session.nativeAccess = createCodexNativeAccess(conversationId, initialized, channel, openingMessages, nativeConfiguration);
477
- session.appliedGeneratedTitle = appliedGeneratedTitle;
478
477
  session.#activeTurnId = resumedActiveTurnId;
479
- const ownedTurn = control.kind === "restore" ? control.ownedTurn : undefined;
480
478
  session.#clientOwnedTurnId = ownedTurn?.turnId;
481
479
  session.#clientOwnedAttemptId = ownedTurn?.attemptId;
482
480
  if (ownedTurn !== undefined)
@@ -493,7 +491,7 @@ class CodexStructuredProviderSession {
493
491
  });
494
492
  if (ownedTurn !== undefined && recoveredTerminal === undefined
495
493
  && session.#activeTurnId !== ownedTurn.turnId) {
496
- const recovered = resumedTurns.find((turn) => turn.turnId === ownedTurn.turnId);
494
+ const recovered = recoveredOwnedTurn;
497
495
  if (recovered?.status === "completed"
498
496
  || recovered?.status === "interrupted"
499
497
  || recovered?.status === "failed") {
@@ -1,9 +1,15 @@
1
- import { mergePendingWakeup } from "./pendingWakeup.js";
1
+ import { enqueueWork } from "../coordination/workMailboxQueue.js";
2
2
  /** The caller's TaskStore transaction owns this read/merge/write operation. */
3
3
  export function queueLeaderWakeup(store, taskId, reason, now) {
4
4
  if (store.getTask(taskId)?.status === "archived")
5
5
  return null;
6
- const pending = mergePendingWakeup(taskId, reason, now, store.getPendingWakeup(taskId));
7
- store.savePendingWakeup(pending);
8
- return pending;
6
+ // The scheduler's pending-wakeup projection may expose an already-owned
7
+ // processing prefix. Append to the actual suffix, never write that read
8
+ // projection back over independent inputs.
9
+ const pending = enqueueWork(store, { kind: "role", taskId, roleName: "leader" }, reason, now).pending;
10
+ return {
11
+ schemaVersion: 1, taskId, reasons: [...pending.reasons],
12
+ requestCount: pending.requestCount,
13
+ firstRequestedAt: pending.firstQueuedAt, lastRequestedAt: pending.lastQueuedAt
14
+ };
9
15
  }
@@ -286,6 +286,15 @@ const MINOR_UPGRADES = Object.freeze([{
286
286
  // Optional Role presentation and generated-title provenance. Never infer
287
287
  // ownership of historical titles, rewrite frozen profiles, or fill defaults.
288
288
  sql: ""
289
+ }, {
290
+ fromVersion: "1.25",
291
+ toVersion: "1.26",
292
+ name: "codex-workspace-temp-exclusions",
293
+ introducedIn: "next",
294
+ sourceChecksum: CURRENT_SCHEMA_CHECKSUM,
295
+ targetChecksum: CURRENT_SCHEMA_CHECKSUM,
296
+ // Optional workspace-write permission fields; preserve all existing payloads.
297
+ sql: ""
289
298
  }]);
290
299
  export function storageMinorUpgradePlan(from) {
291
300
  if (from === CURRENT_STORAGE_VERSION)
@@ -1,4 +1,4 @@
1
- export const CURRENT_STORAGE_VERSION = "1.25";
1
+ export const CURRENT_STORAGE_VERSION = "1.26";
2
2
  export const MIN_SUPPORTED_STORAGE_VERSION = "1.0";
3
3
  export const STORAGE_FORMAT = "yui-home";
4
4
  export function isStorageVersion(value) {
@@ -62,7 +62,9 @@ export class TmuxManager {
62
62
  this.#yuiHome = options.yuiHome ?? process.env.YUI_HOME ?? process.cwd();
63
63
  this.#serverName = yuiTmuxServerName(this.#yuiHome);
64
64
  this.#onRoleTargetRecorded = options.onRoleTargetRecorded;
65
- this.#terminalInput = options.terminalInput ?? process.stdin;
65
+ // Creating Node's stdin stream can make piped fd 0 nonblocking. Commands
66
+ // may still need to read that pipe synchronously before any attach.
67
+ this.#terminalInput = options.terminalInput;
66
68
  this.#terminalType = usableInteractiveTerminal(options.terminalType ?? process.env.TERM);
67
69
  this.#closeInteractiveInput = options.closeInteractiveInput ?? (() => { });
68
70
  this.#readinessTimeoutMs = positiveInteger(options.readinessTimeoutMs, DEFAULT_READINESS_TIMEOUT_MS, "readiness timeout");
@@ -228,7 +230,7 @@ export class TmuxManager {
228
230
  }
229
231
  if (resolvedAccess === "read-write")
230
232
  options.revalidateWritableAttach?.();
231
- handoffTerminal(this.#terminalInput, this.#closeInteractiveInput);
233
+ handoffTerminal(this.#terminalInput ?? process.stdin, this.#closeInteractiveInput);
232
234
  this.run([
233
235
  "attach-session",
234
236
  ...(resolvedAccess === "read-only" ? ["-r"] : []),
@@ -276,6 +276,8 @@ const ROLE_FIELDS = {
276
276
  model: { set: "--model", clear: "--clear-model" }, effort: { set: "--effort", clear: "--clear-effort" },
277
277
  "permission.strategy": { set: "--permission-strategy" }, "permission.sandbox": { set: "--sandbox" },
278
278
  "permission.approval": { set: "--approval" }, "permission.mode": { set: "--permission-mode" },
279
+ "permission.excludeTmpdirEnvVar": { set: "--exclude-tmpdir-env-var", clear: "--clear-exclude-tmpdir-env-var" },
280
+ "permission.excludeSlashTmp": { set: "--exclude-slash-tmp", clear: "--clear-exclude-slash-tmp" },
279
281
  "permission.allowedTools": { set: "--allowed-tool", clear: "--clear-allowed-tools", many: true },
280
282
  "permission.disallowedTools": { set: "--disallowed-tool", clear: "--clear-disallowed-tools", many: true },
281
283
  search: { set: "--search", clear: "--clear-search" },
@@ -20,9 +20,24 @@ authorization for any external effect.
20
20
  | May it access an additional resource or irreversible effect? | `CapabilityGrant`, `grant/taskAuthorization.ts`, resource/plugin/release consumers | Task activation does not authorize arbitrary resources |
21
21
 
22
22
  The full command inventory is generated from the catalog, not maintained as a
23
- second per-command permissions table: use `yui --json help` and scoped
24
- `yui --json help task`, or traverse `describeCommandTree(discoveryCommandTree(env))`
25
- in offline tooling. Complete role hints retain an offline template even without
23
+ second per-command permissions table. `yui --json help` and scoped
24
+ `yui --json help task` return the selected command's full details and all its
25
+ immediate child **summaries**, not a recursively expanded tree. The `discovery`
26
+ field labels this boundary and gives the detail and full-tree commands. Follow
27
+ each child's `path` with `yui --json help <command ...>` for full usage, options,
28
+ values and examples, or use `yui --json help [command ...] --recursive` for the
29
+ complete caller-filtered tree in one call.
30
+
31
+ This changes the default JSON help shape: consumers that previously traversed
32
+ nested `children` must request `--recursive`; absence of child details in the
33
+ default view is not proof that the child has no options or descendants.
34
+ Focused leaf details are unchanged, with additive discovery metadata.
35
+ Text help still shows immediate children; `--recursive` requires `--json`.
36
+ `config describe --json` and the offline
37
+ `describeCommandTree(discoveryCommandTree(env))` API retain full recursive
38
+ descriptions. Native `yui agent` product help is a separate surface and unchanged.
39
+
40
+ Complete role hints retain an offline template even without
26
41
  a running Home; that is not authentication. Native-ID-only callers are resolved
27
42
  from read-only local storage for help/completion; if that read fails the CLI shows
28
43
  the restricted `unbound` template. No Controller or provider is started for that
@@ -21,7 +21,7 @@ declared storage 1.10 → 1.11 transition; historical sources are never invented
21
21
  | What changed? | `task context delta <task> --after <cursor>` | Immutable events with a fixed upper bound, not a snapshot of current mutable state |
22
22
  | Which Global input? | `session context <role>` | Identity, Profile read pointer, authority, current native Turn/retry facts and a bounded pending Message page; history is opt-in |
23
23
  | Read Global input | `role message list <role> [--pending]`, `role message show <role> <id>` | Scoped discovery, then exact original; queue acceptance never erases history |
24
- | What arrived in this wake? | `task wake show <task> <wake>` | Fixed-window inputs/results, attributed optional summaries and exact original reads; `--history` expands diagnostics |
24
+ | What arrived in this wake? | `task wake show <task> <wake>` | Fixed-window inputs/results, bounded complete ordinary inputs and exact original reads; `--history` expands diagnostics |
25
25
  | What was assigned? | `task run context <task>/<run>` | Frozen authority, up to 16 `pointers`, sampled changed identities and live observations with totals; `--view refs` pages references, `--view full` reads the full pack |
26
26
  | Which repeated failure? | `role message list <role> --diagnostics` | Derived classified groups with count, first/latest and recovery evidence; `--diagnostic <key>` pages original occurrences |
27
27
  | Which configuration is real? | `task role session inspect <task> <role>` | Task/Role identity, desired binding, frozen Session, current Provider binding, retry and explicit Host observation; no full Task/Role copies or Provider conversation history |
@@ -32,11 +32,35 @@ Task Role-status and wake-history lists also page, with explicit detail commands
32
32
  Role discovery reports `recordedHealth` and `hostObservation: "not-requested"`,
33
33
  not live Host health. Even a normal recorded status cannot rule out a failed Host
34
34
  or unacknowledged terminal; follow the row's `task role status` read to inspect it.
35
+ `task role status <task> <role>` defaults to `--view current`: the same computed
36
+ health, Host, delivery/retry, cleanup and drift observations, with Run/WorkItem
37
+ identities and exact detail commands instead of their input/report/history bodies.
38
+ Retry diagnostics remain available through the current retry read. Desired and
39
+ effective configuration remain visible; unknown evidence is not upgraded to success.
40
+ This changes the default nested JSON contract. Consumers needing the previous
41
+ complete records use `--view full` and retain that option on pagination.
42
+ The underlying runtime status projection and Web payload are unchanged.
35
43
  Context lists additionally expose candidates, reviews, jobs, Project Knowledge,
36
44
  workspaces and other authorized record families. `--status`, `--after` and
37
45
  `--work-item` narrow Context discovery; every continuation must retain filters.
38
46
  Run listing accepts a Task or `task/work` target.
39
47
 
48
+ `task run show <task>/<run> --view status` reads identity, record status,
49
+ delivery/retry observations, retirement and result availability without copying
50
+ input, launch or report bodies. It uses the same authorization as the default
51
+ `--view full`, which retains complete audit evidence and document pagination.
52
+ Its retry observation is the same current summary used in Task Context, with
53
+ an exact Session retry command for raw diagnostics and history, not their bodies.
54
+ `--view result` returns the complete original result (including failure/provider
55
+ fields), Run identity/status, retirement and execution observation without the
56
+ submitted input or launch configuration. An absent result is `null`, not success.
57
+ Long reports retain document pagination; continue with the same view. The full
58
+ audit command remains available in `reads.full`, and the default is still full.
59
+ Read the original result through its Message, result or full Run view before disposition;
60
+ status is neither a report nor acceptance. Once that exact original is available
61
+ in current context, use status for additional execution facts instead of loading
62
+ the report twice. No read receipt or model-memory cache is introduced.
63
+
40
64
  Current Context's `attention.messages` excludes explicit acknowledgements and
41
65
  retirements; delivery alone is not handling. The exact wake receipt and latest
42
66
  Message revision determine coverage. `--view current` continues that view;
@@ -45,6 +69,28 @@ explicitly describe incomplete discovery. Samples are not proof that no older pe
45
69
  relevant input exists. Read the relevant fixed wake and original Messages;
46
70
  use family discovery when a broader requirement/history audit is needed.
47
71
 
72
+ Current Context is a working index, not a repeated configuration or requirements
73
+ dump. Task/Brief, Role, Project policy, workspace, delivery and decision records
74
+ return summaries and exact references even when their bodies are small. Lifecycle,
75
+ execution authority, attention counts and active execution/retry observations
76
+ remain inline. Read a relevant original through `context inspect` before acting
77
+ on its requirements or authority; `omitted: true` is not an empty original.
78
+ Fresh reads still compute current identities and enforce scope. There is no
79
+ model-memory cache, inferred handling receipt or automatic reuse of stale state.
80
+ Within the current workspace sample, the exact Run workspace overlay (when
81
+ authorized) precedes Task main, then other workspaces. Historical integration
82
+ directories cannot crowd Task main out of that sample. Full family discovery
83
+ retains its existing ordering, counts and original records; this priority neither
84
+ changes workspace authority nor grants access to another Assignment.
85
+
86
+ Message discovery can reuse an existing producer-authored `Yui-Summary:` from
87
+ the Message body or its matching terminal Run result. It labels the source as
88
+ an **author summary**, not a verified outcome or handling decision. The source
89
+ label counts toward the 512-byte summary budget; an otherwise valid summary
90
+ that no longer fits is omitted whole with an original-read hint, not clipped.
91
+ Missing, oversized or mismatched result summaries do not hide the Message or
92
+ change its exact original, digest, authorization or handling state.
93
+
48
94
  ## Budgets and continuations
49
95
 
50
96
  - Discovery pages default to 20 items, accept 1–100, and enforce a 32 KiB
@@ -58,6 +104,11 @@ use family discovery when a broader requirement/history audit is needed.
58
104
  uncertain ones. Historical missing receipts are a separate count and explicit
59
105
  `inbox list --view history` read; absence of ACK is not proof of unexecuted work.
60
106
  Continue its pending page with `role message list <role> --current --cursor`.
107
+ A managed self-read additionally includes `currentOriginals`: complete records
108
+ correlated to this exact Turn and present in that pending page, at most 4 KiB
109
+ each and 8 KiB combined. Available future inputs, history and oversized records
110
+ remain discovery-only; their exact reads are unchanged. Inspection callers get
111
+ no inline originals. This does not claim, deliver or acknowledge input.
61
112
  It does not repeat recent history, Profile bodies, frozen Skill
62
113
  packages or accumulated retry details. The Manifest is the Profile pointer;
63
114
  explicit Message and Session retry reads retain history and original evidence.
@@ -75,14 +126,25 @@ use family discovery when a broader requirement/history audit is needed.
75
126
  document continuation uses `--view full`. Neither changes the frozen authority.
76
127
  Read `--view input` before acting when `input.directiveOmitted` is true: the
77
128
  entry's bounded directive summary is not the assigned instruction body.
129
+ New frozen Task-message pointers include a literal `Excerpt:` preview in their
130
+ existing summary (at most 160 UTF-8 bytes), alongside the original revision
131
+ and digest. Use it to select relevant originals, not as their complete body or
132
+ authority. Later message edits cannot change that Run's frozen preview.
133
+ Existing snapshots are not regenerated.
134
+ When pointers are present, `reads.expand` exposes the existing selected-original
135
+ batch syntax directly. Replace the placeholders with relevant frozen pointers;
136
+ this is not a preselected read-all command. The existing 1–8 selection limit,
137
+ exact snapshot checks, complete original bodies and page budget are unchanged.
78
138
  - `session skills [--cursor ...]` checks only current metadata/readability. It
79
139
  does not read Skill bodies or historical packages, and uses document pagination
80
140
  only when the configured metadata exceeds the inline budget.
81
141
  - Detail documents up to 16 KiB remain ordinary JSON. Larger documents return
82
142
  `contentPage`: exact source, SHA-256 digest, JSON-character offset, total bytes
83
- and characters, text, completeness and a next cursor. Chunks contain at most
84
- 4096 UTF-16 code units without splitting surrogate pairs, keeping escaped
85
- wire output below 32 KiB. There is no 4 MiB cutoff that makes a legal original
143
+ and characters, text, completeness and a next cursor. Chunk lengths adapt to
144
+ the complete encoded 32 KiB response budget, including identity and cursors,
145
+ with 256 bytes reserved for small command wrappers. Chunks never split
146
+ surrogate pairs; offsets remain UTF-16 units and previously issued cursors
147
+ remain readable. There is no 4 MiB cutoff that makes a legal original
86
148
  permanently unreadable.
87
149
  - Task metadata/next-action/remote-delivery, Brief, WorkItem, Message, Run,
88
150
  decision/milestone/event, Role show/status/Session inspection, wake detail,
@@ -111,6 +173,93 @@ Use `--json` and read the top-level `data`. Run context and expansion use
111
173
  the same source and digest, and parse the combined JSON once. Do not parse
112
174
  chunks individually or treat a first chunk as the complete report.
113
175
 
176
+ ### Copyable detail-reading example
177
+
178
+ For 1–8 already-known independent originals in the same Task, use
179
+ `task context inspect <task> --refs '<JSON array of discovery ref objects>'`.
180
+ For hand-written commands, repeated `--context-ref <store/refId@digest>` flags
181
+ avoid JSON quoting; they use the same exact-reference spelling as dispatch.
182
+ Do not combine the two selection formats or single-inspect selector flags.
183
+ Each object requires `store`, `refId`, and `digest`; optional `revision` is
184
+ discovery metadata, not a second freshness guard. The response contains
185
+ `{taskId, items}`, with each item identical to a single inspect result.
186
+ The entire batch shares the existing 32 KiB budget, not one budget per item.
187
+ Follow `contentPage` using the same ordered references and `--cursor`.
188
+ A stale, duplicate, unavailable or forbidden reference rejects the whole read.
189
+ Each original is independently digest-pinned; this does not create an atomic
190
+ cross-record snapshot, select sources, claim inputs, or grant broader access.
191
+ Use single inspect unchanged when only one original is needed.
192
+
193
+ For `task context inspect`, the following Node.js example consumes the outer
194
+ `data` envelope. Supply the authorized CLI path and exact discovery arguments;
195
+ keep the current Session environment. It only reads that record, does not claim
196
+ or acknowledge input, and does not prove that an Agent has read its contents.
197
+ Save as a local `.mjs` file and invoke
198
+ `node reader.mjs /absolute/path/to/yui task-1 task-message message-1 <digest>`.
199
+ The final stdout is the reconstructed record; capture it in your owned scratch
200
+ directory and read it in bounded portions if it exceeds the tool output limit.
201
+ Do not re-fetch earlier pages just to inspect metadata.
202
+
203
+ ```js
204
+ import { spawnSync } from "node:child_process";
205
+
206
+ function readDocument(read) {
207
+ let cursor, source, digest, total, offset = 0;
208
+ const chunks = [];
209
+ for (;;) {
210
+ const data = read(cursor);
211
+ const page = data.contentPage;
212
+ if (!page) {
213
+ if (chunks.length) throw Error("Detail changed shape during pagination");
214
+ return data;
215
+ }
216
+ if (!chunks.length) {
217
+ ({ source, digest, totalCharacters: total } = page);
218
+ }
219
+ if (page.source !== source || page.digest !== digest ||
220
+ page.totalCharacters !== total || page.offset !== offset ||
221
+ typeof page.text !== "string" || !page.text.length) {
222
+ throw Error(`Detail identity/offset mismatch at ${offset}`);
223
+ }
224
+ chunks.push(page.text);
225
+ offset += page.text.length; // UTF-16 units, not UTF-8 bytes.
226
+ if (page.complete === true && page.nextCursor === null && offset === total) {
227
+ try { return JSON.parse(chunks.join("")); }
228
+ catch { throw Error(`Invalid completed detail at ${offset}`); }
229
+ }
230
+ if (page.complete !== false || offset >= total ||
231
+ typeof page.nextCursor !== "string" || !page.nextCursor.length) {
232
+ throw Error(`Invalid continuation at ${offset}`);
233
+ }
234
+ cursor = page.nextCursor;
235
+ }
236
+ }
237
+
238
+ const [cli, task, store, ref, digest] = process.argv.slice(2);
239
+ if (![cli, task, store, ref, digest].every(Boolean)) throw Error("Five arguments required");
240
+ const args = ["task", "context", "inspect", task, "--store", store,
241
+ "--ref", ref, "--digest", digest, "--json"];
242
+ const record = readDocument(cursor => {
243
+ const result = spawnSync(cli, [...args, ...(cursor ? ["--cursor", cursor] : [])],
244
+ { encoding: "utf8", maxBuffer: 1024 * 1024 });
245
+ if (result.error || result.status !== 0) throw Error("Detail command failed; stop without replay");
246
+ let reply;
247
+ try { reply = JSON.parse(result.stdout); }
248
+ catch { throw Error("Invalid CLI envelope"); }
249
+ if (reply.ok !== true || !reply.data) throw Error("Detail read refused");
250
+ return reply.data;
251
+ });
252
+ console.log(JSON.stringify(record));
253
+ ```
254
+
255
+ This is a detail consumer, not a list paginator or a mutation retry helper.
256
+ On a changed source or refusal, stop and reassess the original read; do not
257
+ silently restart or mix versions. Errors deliberately exclude document text:
258
+ passing a partial `contentPage.text` to `jq fromjson` can echo the entire
259
+ fragment on failure. To inspect one already-fetched page without parsing it,
260
+ use `.data.contentPage | {offset,len:(.text|length),complete,nextCursor}`.
261
+ Two pages are not a completion condition; only the returned metadata is.
262
+
114
263
  List responses carry `items`, `total`, `complete`, and `nextCursor`. Counts,
115
264
  items and cursor validation occur inside the same authorized scope. Cursors
116
265
  are opaque positions, not access tokens: every page reauthorizes. A changed
@@ -163,6 +312,16 @@ timestamps overlap; `--history` retains timestamp-window discovery.
163
312
 
164
313
  ## Runtime diagnostics
165
314
 
315
+ CLI JSON failures keep `ok`, `code`, `message` and `details`. `message` contains
316
+ the primary error, effect uncertainty, safe recovery guidance and focused help;
317
+ `details.diagnostic` retains the complete sanitized cause chain, operation,
318
+ Home, CLI entry, targets and any Controller request/delivery/timing facts.
319
+ Those identity lines and nested causes are not repeated as prose in `message`.
320
+ Consumers needing them must read `details.diagnostic`, not parse the human
321
+ message. Text-mode diagnostics remain unchanged. This is presentation only:
322
+ exit codes, redaction, partial receipts and no-replay/recovery boundaries do
323
+ not change; no probe, retry or repair is performed.
324
+
166
325
  Global failure evidence remains ordinary record-only Messages; no aggregate
167
326
  table, Message envelope change, memory store or recovery state machine is added.
168
327
  Grouping is a derived view of new evidence documents. Older documents remain
@@ -183,6 +342,42 @@ Discovery and every evidence page retain the existing Role read boundary.
183
342
 
184
343
  Message send/queue/steer receipts preserve identity, digest, body byte count and
185
344
  the original submission/delivery/control states without echoing the body.
345
+ `bodySha256` is the lowercase SHA-256 of the saved body's raw UTF-8 bytes;
346
+ `bodyBytes` counts those same bytes. Callers may compare them with the exact
347
+ submitted file to verify transport without fetching the body again. Do not trim
348
+ or JSON-encode the body before hashing. The separate `digest` covers the entire
349
+ record and is not a body checksum. These fields are derived from the saved
350
+ Message, including idempotent replay; they neither add persistent state nor
351
+ replace required original reads, authorization, ACK or downstream acceptance.
352
+ `operator submit --json` uses that same structured shape:
353
+ `data.taskId`, `data.message` (the bounded Message receipt), and `data.submission`
354
+ (the recorded save, phase, planning, activation, delivery and next-step facts).
355
+ It no longer returns only an `output` string for JSON consumers; ordinary text
356
+ output is unchanged. A same-key retry reproduces the saved submission feedback,
357
+ not a fresh interpretation of the Task's later state. The receipt is not the
358
+ original body or proof that the Leader finished.
359
+ Message `--body-file <path|->` inputs preserve their UTF-8 text, including leading
360
+ and trailing whitespace, through Operator submission and Task/Global message
361
+ send, update, queue and steer paths. Blank-only or NUL-containing input remains
362
+ invalid. Inline text and summary/note file normalization are unchanged. This fixes
363
+ the earlier file-input trim; an old request saved after trimming must be inspected,
364
+ not replayed with newly different bytes under the same request ID.
365
+ Operator's optional `scripts/verify-submission.mjs` compares a local receipt with
366
+ the payload and expected Task/intent/request ID. It performs no Yui operation,
367
+ write, cleanup or ACK; a missing checksum is a failed local check, never success.
368
+ `task decision record|supersede --json` also returns a bounded save receipt:
369
+ `data.taskId`, `effect: "saved"`, `data.decision` (identity, state, timestamps,
370
+ complete-record digest and rationale byte count). When the complete
371
+ saved record is at most 2048 UTF-8 bytes in JSON, `decision` also contains all
372
+ its fields and `contentOmitted` is false: callers can inspect the actual short
373
+ saved value directly, without a `read` follow-up pointer. Larger records set
374
+ `contentOmitted: true`, include an exact `read` command, and omit title,
375
+ rationale and supersede-reason bodies rather than truncate them. The digest
376
+ always covers the complete original record, excluding receipt-added fields;
377
+ `decision show` remains the complete read when needed. Human text is unchanged.
378
+ JSON callers must consume `data`
379
+ instead of parsing the previous `output` string. This confirms the saved state,
380
+ not semantic acceptance; a missing response never authorizes repeating a record.
186
381
  Brief updates return a saved reference. No new wait, retry, acknowledgement or
187
382
  approval phase is added. Repeating a read cursor must never repeat a mutation.
188
383
 
@@ -201,8 +396,35 @@ document does not claim a universal 32 KiB cap for them. Full-detail Web
201
396
  projections are separate consumers, not silently replaced with CLI summaries.
202
397
  Use targeted Context discovery for large Project Knowledge and Task evidence.
203
398
 
204
- For a typical notification, read current Context once, read the fixed wake once,
205
- then read each relevant original once (or its complete document pages).
399
+ For a typical notification, read current Context and the fixed wake, then obtain
400
+ only relevant originals not already returned in full (including their complete pages).
401
+ The focused wake JSON includes complete ordinary Message values up to 4 KiB
402
+ each and 8 KiB combined, marked `contentOmitted: false` with their current
403
+ versioned `ref`. Use those bodies directly. Larger messages and result reports
404
+ retain explicit reads; a completion notice never substitutes for its report.
405
+ For an inline complete value, `refreshRead` is an optional current-source refresh,
406
+ not a required next read; `read` is present only when the original is omitted.
407
+ Consumers that previously followed every `messages[].read` must first inspect
408
+ `contentOmitted` and use `value` for complete originals. Text output still links
409
+ to the original because it presents only a summary, not the inline JSON value.
410
+ The fixed window selects identities, not a historical snapshot of mutable
411
+ message bodies. Later revisions remain distinguishable by their exact reference.
412
+ History mode does not inline bodies. This changes no claim or ACK semantics.
413
+ The current Leader's `inbox show <wake>` also returns that focused view as
414
+ `originalContext`, with the existing wake metadata at the top level. It uses
415
+ the same identity selection, inline limits and exact original pointers as
416
+ `task wake show`; reuse the included complete bodies instead of making a second
417
+ read. Own-inbox authorization still applies, and the outer read retains
418
+ `contentPage` pagination. An unclaimed `pending-*` window has no fixed wake yet;
419
+ its claim receipt still supplies the durable wake and original-read command.
420
+ Claim/ACK mutations themselves are unchanged and do not perform extra reads.
421
+ For mutations addressed to an existing `wake-*`, the receipt now calls that
422
+ navigation `refreshOriginals`, not `originals`: use it when the focused originals
423
+ are missing, incomplete or need refreshing, not merely because a claim occurred.
424
+ Mutations addressed to `pending-*` retain `originals`, including idempotent claim
425
+ retries, because their initial inspection did not provide fixed originals.
426
+ This is a JSON navigation-field change; consumers must handle both entry types.
427
+ It records no read history and does not waive complete relevant-original reads.
206
428
  Do not reread an aggregate to obtain a detail it intentionally omits.
207
429
  Full-original reading costs additional calls only when the original exceeds
208
430
  the inline budget; it is never replaced by a summary.