@zq-silk/yui 2.6.0 → 2.8.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 (126) hide show
  1. package/README.md +23 -0
  2. package/dist/cli/commandCatalog.js +34 -7
  3. package/dist/cli/updateCommand.js +3 -0
  4. package/dist/commands/configCommands.js +1 -0
  5. package/dist/commands/globalRoleCommands.js +62 -35
  6. package/dist/commands/inboxCommands.js +372 -0
  7. package/dist/commands/roleSkillValidation.js +2 -0
  8. package/dist/commands/taskCommands.js +39 -7
  9. package/dist/commands/taskRoleRuntimeStatus.js +2 -0
  10. package/dist/context/currentSkills.js +48 -0
  11. package/dist/context/roleSessionContext.js +13 -16
  12. package/dist/context/runContextPack.js +50 -3
  13. package/dist/context/runInputContract.js +1 -1
  14. package/dist/context/sessionBootstrapManifest.js +27 -4
  15. package/dist/context/skillCatalog.js +2 -3
  16. package/dist/context/taskContext.js +38 -12
  17. package/dist/controlPlaneCli.js +58 -7
  18. package/dist/controller/controller.js +8 -2
  19. package/dist/controller/fileSchedulerStoreAdapter.js +26 -6
  20. package/dist/controller/globalInputDelivery.js +86 -16
  21. package/dist/controller/globalRuntimeAttention.js +136 -6
  22. package/dist/controller/operatorNotification.js +6 -1
  23. package/dist/controller/providerRetryDelivery.js +3 -5
  24. package/dist/controller/runtime.js +6 -2
  25. package/dist/controller/structuredProviderObservation.js +51 -15
  26. package/dist/controller/taskHostTerminalRecovery.js +33 -0
  27. package/dist/doctor/doctor.js +25 -2
  28. package/dist/executor/effectiveLaunch.js +2 -2
  29. package/dist/executor/fileRoleLaunchPlanner.js +8 -15
  30. package/dist/interaction/notificationPresentation.js +2 -2
  31. package/dist/interaction/operatorPresentation.js +1 -1
  32. package/dist/message/globalInputBacklog.js +69 -0
  33. package/dist/message/globalMessageQuery.js +1 -0
  34. package/dist/message/inboxReceipt.js +62 -0
  35. package/dist/message/message.js +6 -1
  36. package/dist/message/messageContinuation.js +34 -13
  37. package/dist/message/taskInboxOwnership.js +43 -0
  38. package/dist/models/modelManagement.js +469 -0
  39. package/dist/models/modelManagementConfig.js +46 -0
  40. package/dist/models/modelManagementFiles.js +90 -0
  41. package/dist/models/modelManagementHttp.js +90 -0
  42. package/dist/models/modelManagementTypes.js +1 -0
  43. package/dist/nativeAgent/session/backends.js +1 -1
  44. package/dist/observability/runtimeIdentity.js +1 -1
  45. package/dist/output/boundedRead.js +43 -14
  46. package/dist/resources/sqliteResourceRegistry.js +1 -1
  47. package/dist/runtime/agentEndpointIdentity.js +8 -4
  48. package/dist/runtime/agentEndpointOwnership.js +5 -6
  49. package/dist/runtime/agentHost.js +165 -24
  50. package/dist/runtime/agentHostImplementation.js +18 -0
  51. package/dist/runtime/managedCaller.js +25 -1
  52. package/dist/runtime/managedIdentity.js +1 -1
  53. package/dist/runtime/providerRetry.js +19 -1
  54. package/dist/runtime/sessionCaller.js +53 -0
  55. package/dist/scheduler/operatorEvent.js +49 -0
  56. package/dist/scheduler/operatorInputNotificationProcessor.js +5 -4
  57. package/dist/scheduler/taskWake.js +5 -0
  58. package/dist/sqlite/driver.js +108 -0
  59. package/dist/storage/contextRecords.js +28 -0
  60. package/dist/storage/sqliteSchema.js +21 -1
  61. package/dist/storage/sqliteStore.js +64 -1
  62. package/dist/storage/storageSchema.js +1 -1
  63. package/dist/storage/storageVersions.js +1 -1
  64. package/dist/storage/storeRpc.js +1 -0
  65. package/dist/storage/taskStore.js +4 -0
  66. package/dist/storage/upgrade/upgradeOrchestrator.js +1 -1
  67. package/dist/task/completionReadiness.js +13 -6
  68. package/dist/telemetry/sqliteTelemetryStore.js +1 -1
  69. package/dist/web/assets/assetManifest.js +28 -2
  70. package/dist/web/assets/client/app/manage.js +6 -0
  71. package/dist/web/assets/client/app/models.js +20 -0
  72. package/dist/web/assets/client/app/settings.js +41 -9
  73. package/dist/web/assets/client/i18n/en.js +180 -2
  74. package/dist/web/assets/client/i18n/zh-CN.js +178 -2
  75. package/dist/web/assets/client/index.js +8 -0
  76. package/dist/web/assets/client/layout/manageShell.js +94 -0
  77. package/dist/web/assets/client/lib/api.js +13 -1
  78. package/dist/web/assets/client/lib/i18n.js +5 -3
  79. package/dist/web/assets/client/views/dock/conversation.js +231 -80
  80. package/dist/web/assets/client/views/models.js +777 -0
  81. package/dist/web/assets/client/views/skills.js +22 -15
  82. package/dist/web/assets/shared/icons.js +2 -0
  83. package/dist/web/assets/shared/manageNav.js +13 -0
  84. package/dist/web/assets/shell/manage.js +54 -0
  85. package/dist/web/assets/shell/models.js +7 -0
  86. package/dist/web/assets/shell/settings.js +1 -35
  87. package/dist/web/assets/shell/settingsContent.js +30 -0
  88. package/dist/web/assets/shell/sidebar.js +1 -0
  89. package/dist/web/assets/styles/index.js +6 -0
  90. package/dist/web/assets/styles/views/conversation.js +133 -0
  91. package/dist/web/assets/styles/views/manage.js +71 -0
  92. package/dist/web/assets/styles/views/models.js +129 -0
  93. package/dist/web/assets/styles/views/settings.js +28 -14
  94. package/dist/web/assets/styles/views/sidebar.js +1 -0
  95. package/dist/web/webServer.js +59 -9
  96. package/docs/cli-information-contract.md +42 -11
  97. package/docs/cli-information-contract.zh-CN.md +28 -7
  98. package/docs/managed-turn-and-session-runtime.md +127 -5
  99. package/docs/managed-turn-and-session-runtime.zh-CN.md +83 -3
  100. package/docs/release-workflow.md +105 -9
  101. package/docs/release-workflow.zh-CN.md +73 -5
  102. package/docs/storage-baseline.md +22 -7
  103. package/docs/storage-baseline.zh-CN.md +14 -5
  104. package/docs/task-delivery.md +17 -8
  105. package/docs/task-delivery.zh-CN.md +11 -5
  106. package/docs/testing/review-evaluation.md +148 -0
  107. package/docs/testing/review-evaluation.zh-CN.md +117 -0
  108. package/docs/testing/verification-levels.md +26 -4
  109. package/docs/testing/verification-levels.zh-CN.md +11 -1
  110. package/i18n/README.zh-CN.md +19 -0
  111. package/package.json +5 -2
  112. package/skills/yui-leader/SKILL.md +28 -11
  113. package/skills/yui-leader/VERSION +1 -0
  114. package/skills/yui-leader/references/execution.md +29 -0
  115. package/skills/yui-leader/references/replicated-execution.md +7 -0
  116. package/skills/yui-operator/SKILL.md +15 -3
  117. package/skills/yui-operator/VERSION +1 -0
  118. package/skills/yui-operator/references/task-delivery.md +2 -0
  119. package/skills/yui-reviewer/SKILL.md +43 -1
  120. package/skills/yui-reviewer/VERSION +1 -0
  121. package/skills/yui-runtime/SKILL.md +144 -15
  122. package/skills/yui-runtime/VERSION +1 -0
  123. package/skills/yui-runtime/references/publication.md +16 -5
  124. package/skills/yui-runtime/references/recovery.md +24 -5
  125. package/skills/yui-worker/SKILL.md +8 -1
  126. package/skills/yui-worker/VERSION +1 -0
package/README.md CHANGED
@@ -67,6 +67,29 @@ Native first-run confirmations may still require your input.
67
67
  npm install -g @zq-silk/yui
68
68
  ```
69
69
 
70
+ SQLite is selected lazily before opening your Home: Yui probes a disposable
71
+ in-memory connection with `sqlite-modern` (`better-sqlite3@13.0.3`, N-API) first,
72
+ then tries `sqlite-compat` (`better-sqlite3@10.1.0`) only for identified missing
73
+ native bindings, Node ABI or system-library incompatibility. Each process keeps
74
+ its successful selection. Home permissions, corruption, locks, SQL and migration
75
+ errors never trigger a driver switch. `yui doctor` reports the driver version,
76
+ actual SQLite engine and fallback reason separately from Home health.
77
+
78
+ The N-API driver avoids the native `ObjectWrap` garbage-collection abort seen
79
+ with the earlier driver on Node 24.19/24.20. It requires Node-API 10;
80
+ runtimes with an older API (including Node 20 and Node 22.9) skip it before
81
+ native loading and use the compatibility candidate. No global Node change
82
+ or Home migration is needed for this dependency correction.
83
+
84
+ npm attempts **both** fixed optional aliases during installation; it does not
85
+ install the older one only after the newer one fails. This increases download
86
+ size and installation/build time. Optional installation failures may leave npm
87
+ successful, but Yui cannot run without a usable candidate; do not use
88
+ `--omit=optional`. The compatibility driver uses older SQLite (3.46.0), without
89
+ later fixes, and is not guaranteed on every old glibc/toolchain or newer Node
90
+ ABI. The supported Node range above is unchanged; an OS/toolchain upgrade may
91
+ still be necessary. Selection does not downgrade or migrate Home data.
92
+
70
93
  ### 2. Set up, yourself or with an Agent
71
94
 
72
95
  Run the interactive setup:
@@ -190,6 +190,12 @@ const roleChildren = [
190
190
  { name: "unbind", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Unbind a dormant Agent from a global Role.", usage: "yui config role unbind <role> <agent-id>" }
191
191
  ];
192
192
  const globalSessionChildren = [
193
+ {
194
+ name: "skills", discovery: { surface: "managed", audiences: ["operator", "leader", "assignment", "global", "unbound"] },
195
+ summary: "Check current Skill paths, versions and availability for this managed Session without reading bodies.",
196
+ usage: "yui session skills [--cursor <cursor>]",
197
+ options: ["--cursor"]
198
+ },
193
199
  { name: "enter", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Enter a global Role's native session.", usage: "yui session enter <role>" },
194
200
  {
195
201
  name: "context", discovery: { surface: "managed", audiences: ["operator", "global", "unbound"] },
@@ -366,7 +372,7 @@ const taskChildren = [
366
372
  }
367
373
  ]
368
374
  },
369
- { name: "reopen", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Explicitly reopen completed or cancelled intent without replaying historical inputs.", usage: "yui task reopen <id>" },
375
+ { name: "reopen", discovery: { surface: "public", audiences: ["public", "operator", "leader"] }, summary: "Reopen a completed Task without replaying historical inputs; a current Leader is limited to its own Task. Restoring cancelled intent requires user or Operator authority; an independent execution stop is preserved.", usage: "yui task reopen <id>" },
370
376
  {
371
377
  name: "cancel", discovery: { surface: "public", audiences: ["public", "operator", "leader"] },
372
378
  summary: "Stop pursuing a Task without claiming its processes stopped.",
@@ -906,9 +912,9 @@ const taskChildren = [
906
912
  },
907
913
  {
908
914
  name: "context", discovery: { surface: "managed", audiences: ["operator", "leader", "assignment"] },
909
- summary: "Load the exact authorized AgentRun context.",
910
- usage: "yui task run context <task>/<run> [--cursor <cursor>] [--json]",
911
- options: ["--cursor", "--json"],
915
+ summary: "Load bounded AgentRun entry facts; discover refs or read the full authorized pack explicitly.",
916
+ usage: "yui task run context <task>/<run> [--view current|refs|input|full] [--cursor <cursor>] [--json]",
917
+ options: ["--view", "--cursor", "--json"],
912
918
  executable: true,
913
919
  hidden: true,
914
920
  sections: [{ id: "load", title: "Commands", entries: ["expand", "delta"] }],
@@ -1128,7 +1134,7 @@ export const ROOT_COMMAND = buildNode({
1128
1134
  { id: "general", title: "General", entries: [
1129
1135
  "help", "version", "update", "upgrade", "setup", "start", "doctor"
1130
1136
  ] },
1131
- { id: "workflow", title: "Workflow", entries: ["agent", "operator", "role", "project", "task"] },
1137
+ { id: "workflow", title: "Workflow", entries: ["agent", "operator", "role", "inbox", "project", "task"] },
1132
1138
  { id: "configuration", title: "Configuration", entries: ["config"] },
1133
1139
  { id: "operations", title: "Operations", entries: ["web", "controller", "session", "execution", "capability", "job", "jobs", "telemetry", "release"] },
1134
1140
  { id: "resources", title: "Resources", entries: ["resources"] },
@@ -1154,6 +1160,26 @@ export const ROOT_COMMAND = buildNode({
1154
1160
  options: catalogCommandOptions(name) }))
1155
1161
  ]
1156
1162
  },
1163
+ {
1164
+ name: "inbox",
1165
+ summary: "Read and explicitly handle the current managed Agent's own inbox.",
1166
+ sections: [{ id: "commands", title: "Commands", entries: ["list", "show", "claim", "ack"] }],
1167
+ children: [
1168
+ { name: "list", discovery: { surface: "managed", audiences: ["operator", "global", "leader", "assignment"] },
1169
+ summary: "Discover unhandled own-address entries, including accepted delivery, with current identity and separate handling receipts.",
1170
+ usage: "yui inbox list [--limit <1..100>] [--cursor <cursor>]", options: ["--limit", "--cursor"] },
1171
+ { name: "show", discovery: { surface: "managed", audiences: ["operator", "global", "leader", "assignment"] },
1172
+ summary: "Read an exact original or fixed notification window without acknowledgement.",
1173
+ usage: "yui inbox show <entry> [--cursor <cursor>]", options: ["--cursor"] },
1174
+ { name: "claim", discovery: { surface: "managed", audiences: ["operator", "global", "leader", "assignment"] },
1175
+ summary: "Reserve 1..16 exact entries for handling in this turn; never starts a model turn.",
1176
+ usage: "yui inbox claim <entry> [<entry> ...] --request-id <id>", options: ["--request-id"] },
1177
+ { name: "ack", discovery: { surface: "managed", audiences: ["operator", "global", "leader", "assignment"] },
1178
+ summary: "Confirm handling of claimed entries with an auditable disposition, not Provider acceptance.",
1179
+ usage: "yui inbox ack <entry> [<entry> ...] --request-id <id> --reason <disposition>",
1180
+ options: ["--request-id", "--reason"] }
1181
+ ]
1182
+ },
1157
1183
  {
1158
1184
  name: "role", discovery: { surface: "public", audiences: ["public", "operator"] },
1159
1185
  summary: "Queue input or control an exact current Global Role Turn.",
@@ -1165,8 +1191,8 @@ export const ROOT_COMMAND = buildNode({
1165
1191
  children: [
1166
1192
  {
1167
1193
  name: "list", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Discover Global Messages without consuming input.",
1168
- usage: "yui role message list <role> [--pending] [--limit <1..100>] [--cursor <cursor>]",
1169
- options: ["--pending", "--limit", "--cursor"]
1194
+ usage: "yui role message list <role> [--pending|--diagnostics|--diagnostic <key>] [--limit <1..100>] [--cursor <cursor>]",
1195
+ options: ["--pending", "--diagnostics", "--diagnostic", "--limit", "--cursor"]
1170
1196
  },
1171
1197
  {
1172
1198
  name: "show", discovery: { surface: "public", audiences: ["public", "operator"] }, summary: "Read one exact Global Message; follow contentPage.nextCursor for long content.",
@@ -1584,6 +1610,7 @@ export const ROOT_COMMAND = buildNode({
1584
1610
  "yui session reconcile --report"
1585
1611
  ],
1586
1612
  sections: [
1613
+ { id: "instructions", title: "Managed instructions", entries: ["skills"] },
1587
1614
  { id: "global", title: "Global Role sessions", entries: ["context", "enter", "record", "replace"] },
1588
1615
  { id: "maintenance", title: "Maintenance", entries: ["stop"] },
1589
1616
  { id: "recovery", title: "Recovery", entries: ["reconcile", "retry"] }
@@ -65,6 +65,9 @@ export function renderUpdateResult(result) {
65
65
  })();
66
66
  return [
67
67
  rendered,
68
+ ...(result.outcome === "updated" ? [
69
+ "Runtime: existing Hosts are retained, not upgraded in place. Host control capabilities and event delivery are checked separately; accepted input is never replayed."
70
+ ] : []),
68
71
  ...(result.controllerReconciliation === undefined ? [] : [
69
72
  `Controller reconciliation: ${JSON.stringify(result.controllerReconciliation)}`
70
73
  ]),
@@ -56,6 +56,7 @@ export function effectiveConfigData(config, domain) {
56
56
  const health = resolveRuntimeHealth(config.runtimeHealth);
57
57
  const all = {
58
58
  schemaVersion: config.schemaVersion,
59
+ modelManagement: config.modelManagement ?? null,
59
60
  defaultAgent: config.defaultAgent ?? null,
60
61
  defaultWorkspace: config.defaultWorkspace ?? null,
61
62
  timeZone: resolveTimeZone(config.timeZone),
@@ -1,8 +1,14 @@
1
1
  import { roleNotFound, usageError } from "../errors/cliError.js";
2
2
  import { hasManagedIdentity } from "../runtime/managedIdentity.js";
3
- import { boundedDocument, recordPage, readOptions, readDigest, messageReceipt } from "../output/boundedRead.js";
4
- import { controlProviderRetry, providerRetryProjection, renderProviderRetry } from "../runtime/providerRetry.js";
3
+ import { requireManagedGlobalCaller } from "../runtime/managedCaller.js";
4
+ import { boundedDocument, recordPage, readOptions, readDigest, messageReceipt, summaryText, summaryMetadata } from "../output/boundedRead.js";
5
+ import { controlProviderRetry, providerRetryProjection, providerRetrySummary, renderProviderRetry } from "../runtime/providerRetry.js";
6
+ import { pendingGlobalInboxMessages, inboxHandlingState, globalInboxDeliveryState } from "../message/inboxReceipt.js";
5
7
  import { settleGlobalRetryInput } from "../message/globalProviderRetry.js";
8
+ import { globalInputBacklog } from "../message/globalInputBacklog.js";
9
+ import { checkCurrentSkills, SKILL_REUSE_RULE } from "../context/currentSkills.js";
10
+ import { runtimeDiagnosticGroups, runtimeDiagnosticEvidence } from "../controller/globalRuntimeAttention.js";
11
+ import { roleSkillIds } from "../context/roleSessionContext.js";
6
12
  import { createRoleSessionSet, recordRoleAgentSession } from "../executor/agentExecutor.js";
7
13
  import { resolveEffectiveLaunch } from "../executor/effectiveLaunch.js";
8
14
  import { managedGlobalRoleWorkspace } from "../storage/homeLayout.js";
@@ -73,13 +79,7 @@ function assertGlobalInputAuthority(store, options, target) {
73
79
  throw usageError("Global input mutation is outside the exact Session authority.");
74
80
  }
75
81
  if (env.YUI_SESSION_SCOPE === "global") {
76
- const caller = store.getGlobalRoleSessionSet(env.YUI_ROLE ?? "");
77
- const session = caller?.sessions[caller.activeAgentId];
78
- if (session === undefined || session.status !== "active" || env.YUI_AGENT_ID !== session.agentId
79
- || env.YUI_ADAPTER_ID !== session.adapterId
80
- || (env.CODEX_THREAD_ID ?? env.YUI_NATIVE_SESSION_ID) !== session.nativeSessionId) {
81
- throw usageError("Global input mutation requires the current native caller Session.");
82
- }
82
+ requireManagedGlobalCaller(store, env);
83
83
  }
84
84
  else if (hasManagedIdentity(env)) {
85
85
  throw usageError("Incomplete managed identity cannot acquire user authority.");
@@ -120,22 +120,26 @@ function roleContext(args, store, options) {
120
120
  if (environment.YUI_SESSION_SCOPE !== "global" || environment.YUI_ROLE !== name) {
121
121
  throw usageError("Managed GlobalRole context is outside the exact Session authority.");
122
122
  }
123
- if (environment.YUI_SESSION_MANIFEST === undefined) {
124
- throw usageError("Managed GlobalRole context requires its Session Manifest.");
125
- }
126
123
  }
127
124
  const role = requireRole(name, store);
128
125
  const binding = activeRoleAgentBinding(role);
129
126
  const effective = resolveEffectiveLaunch({ role, purpose: "execution" });
130
127
  const sessions = store.getGlobalRoleSessionSet(name);
131
128
  const session = sessions?.sessions[role.activeAgentId];
132
- // Only the controlled Endpoint's accepted receipt consumes a queued Message.
129
+ const currentSkills = checkCurrentSkills(options.yuiHome ?? environment.YUI_HOME, roleSkillIds(session?.effective.context ?? role, role.name === "operator" ? "operator" : "global"));
130
+ // Native delivery and explicit Agent handling settle the same queued input
131
+ // through distinct receipts; a context read does neither.
133
132
  const messages = store.listGlobalRoleMessages(name);
134
133
  const queued = messages.filter((message) => message.delivery === undefined
134
+ && message.inbox?.ack === undefined
135
135
  && message.notDelivered === undefined
136
136
  && (message.interruptThen !== undefined || message.inputControl?.action === "queue"));
137
- const pending = recordPage(queued.map(globalMessageSummary), `global:${name}/messages/pending`, { limit: 8 });
138
- const recent = recordPage([...messages].reverse().map(globalMessageSummary), `global:${name}/messages/all`, { limit: 8 });
137
+ const pending = recordPage(pendingGlobalInboxMessages(messages).map(globalMessageSummary), `global:${name}/messages/pending`, { limit: 8 });
138
+ const observedAt = new Date();
139
+ const { oldestAgeMs, messages: _backlogMessages, ...backlog } = globalInputBacklog(queued, sessions, observedAt, {
140
+ attention: name === "operator" ? store.getWorkMailbox({ kind: "operator" }) : null,
141
+ lifecycle: store.getWorkMailbox({ kind: "global-role-runtime", roleName: name })
142
+ });
139
143
  const context = Object.freeze({
140
144
  schemaVersion: 1,
141
145
  protocol: "yui-managed-context/v1",
@@ -152,10 +156,7 @@ function roleContext(args, store, options) {
152
156
  : { nativeSessionId: session.nativeSessionId })
153
157
  },
154
158
  profile: {
155
- description: role.description,
156
- responsibilities: role.responsibilities ?? [],
157
- constraints: role.constraints ?? [],
158
- expectedOutput: role.expectedOutput,
159
+ read: "Read roleProfileRef from the current Session Manifest; it is not repeated at each entry.",
159
160
  skillIds: [
160
161
  "yui-runtime",
161
162
  ...(role.name === "operator" ? ["yui-operator"] : []),
@@ -172,21 +173,33 @@ function roleContext(args, store, options) {
172
173
  },
173
174
  // Reading pending input is neither delivery nor implementation.
174
175
  pending,
175
- recent,
176
+ backlog,
176
177
  reads: {
178
+ ownInbox: "yui inbox list --json",
177
179
  messages: `role message list ${name}`,
178
180
  pending: `role message list ${name} --pending`,
179
- detail: `role message show ${name} <message-id>`
181
+ detail: `role message show ${name} <message-id>`,
182
+ diagnostics: `role message list ${name} --diagnostics`,
183
+ retry: `session retry ${name} show`
180
184
  },
181
- nativeTurn: sessions?.providerBinding?.run ?? null,
182
- retry: providerRetryProjection(sessions?.providerBinding),
185
+ nativeTurn: sessions?.providerBinding?.run == null ? null : {
186
+ attemptId: sessions.providerBinding.run.attemptId,
187
+ nativeTurnId: sessions.providerBinding.run.nativeTurnId,
188
+ status: sessions.providerBinding.run.status
189
+ },
190
+ retry: providerRetrySummary(sessions?.providerBinding),
183
191
  // Historical interrupt receipts are available through Session inspection;
184
192
  // only this Turn's exact identity is required at entry.
185
193
  sessionManifestPath: environment.YUI_SESSION_MANIFEST,
186
- skillPackages: session?.effective.skillPackages ?? null,
194
+ skillCheckCommand: "yui session skills --json",
195
+ currentSkills,
196
+ skillLoadingRule: SKILL_REUSE_RULE,
187
197
  cliCommand: "yui"
188
198
  });
189
- const data = boundedDocument(context, `global:${name}/context`, parsed.cursor);
199
+ // Elapsed wall-clock time is live display metadata, not a changing source
200
+ // fact: keep it outside the digest so a long Context remains pageable.
201
+ const data = { ...boundedDocument(context, `global:${name}/context`, parsed.cursor),
202
+ backlogObservation: { observedAt: observedAt.toISOString(), oldestAgeMs } };
190
203
  return { kind: "output", data, output: [
191
204
  `Role Context: ${role.name}`,
192
205
  `Scope: global (${context.identity.roleKind})`,
@@ -195,7 +208,7 @@ function roleContext(args, store, options) {
195
208
  `Effective revision: ${role.launchRevision}`,
196
209
  `Skills: ${context.profile.skillIds.join(", ") || "none"}`,
197
210
  `Authority: ${context.authority.view}; Task implementation: no`,
198
- `Pending messages: ${pending.total}; recent messages: ${recent.total}`,
211
+ `Pending messages: ${pending.total}; history is available on demand`,
199
212
  JSON.stringify(data),
200
213
  ""
201
214
  ].join("\n") };
@@ -203,12 +216,15 @@ function roleContext(args, store, options) {
203
216
  function globalMessageSummary(message) {
204
217
  return {
205
218
  id: message.id, createdAt: message.createdAt, kind: message.kind,
206
- digest: readDigest(message), summary: message.body.slice(0, 240),
219
+ digest: readDigest(message), summary: summaryText(message.body),
220
+ read: `role message show ${message.roleName} ${message.id}`,
207
221
  delivery: message.delivery === undefined ? undefined : { deliveredAt: message.delivery.deliveredAt },
208
- notDelivered: message.notDelivered === undefined ? undefined : { summary: message.notDelivered.reason.slice(0, 240) },
222
+ notDelivered: message.notDelivered === undefined ? undefined : { summary: summaryText(message.notDelivered.reason) },
209
223
  control: message.control === undefined ? undefined : { outcome: message.control.outcome, observedAt: message.control.observedAt },
210
224
  inputAction: message.inputControl?.action,
211
- deliveryTarget: message.deliveryTarget,
225
+ handling: inboxHandlingState(message.inbox),
226
+ deliveryState: globalInboxDeliveryState(message),
227
+ deliveryTarget: message.deliveryTarget === undefined ? undefined : summaryMetadata(message.deliveryTarget),
212
228
  interruptThen: message.interruptThen === undefined ? undefined : true
213
229
  };
214
230
  }
@@ -610,8 +626,13 @@ function globalRoleMessage(args, store, options) {
610
626
  }
611
627
  requireRole(name, store);
612
628
  const pending = tail.includes("--pending");
613
- const parsed = readOptions(tail.filter(arg => arg !== "--pending"), action === "list"
614
- ? ["--cursor", "--limit"] : ["--cursor"]);
629
+ const diagnostics = tail.includes("--diagnostics");
630
+ const parsed = readOptions(tail.filter(arg => arg !== "--pending" && arg !== "--diagnostics"), action === "list"
631
+ ? ["--cursor", "--limit", "--diagnostic"] : ["--cursor"]);
632
+ const diagnostic = parsed.values.get("--diagnostic");
633
+ if (Number(pending) + Number(diagnostics) + Number(diagnostic !== undefined) > 1
634
+ || action === "show" && diagnostics)
635
+ throw usageError("Choose one Message discovery view.");
615
636
  const messages = store.listGlobalRoleMessages(name);
616
637
  let data;
617
638
  if (action === "show") {
@@ -625,10 +646,16 @@ function globalRoleMessage(args, store, options) {
625
646
  else {
626
647
  if (parsed.positionals.length !== 0)
627
648
  throw usageError("Role message list <role> [--pending] [--limit <n>] [--cursor <cursor>].");
628
- const selected = pending ? messages.filter(message => message.delivery === undefined
629
- && message.notDelivered === undefined
630
- && (message.interruptThen !== undefined || message.inputControl?.action === "queue")) : [...messages].reverse();
631
- data = recordPage(selected.map(globalMessageSummary), `global:${name}/messages/${pending ? "pending" : "all"}`, parsed);
649
+ if (diagnostics) {
650
+ data = recordPage(runtimeDiagnosticGroups(messages).reverse().map(group => ({
651
+ ...group, source: summaryMetadata(group.source),
652
+ read: `role message show ${name} ${group.latestEvidence}`
653
+ })), `global:${name}/diagnostics`, parsed);
654
+ return { kind: "output", output: `${JSON.stringify(data)}\n`, data };
655
+ }
656
+ const selected = diagnostic !== undefined ? runtimeDiagnosticEvidence(messages, diagnostic)
657
+ : pending ? pendingGlobalInboxMessages(messages) : [...messages].reverse();
658
+ data = recordPage(selected.map(globalMessageSummary), `global:${name}/messages/${diagnostic === undefined ? pending ? "pending" : "all" : `diagnostic/${diagnostic}`}`, parsed);
632
659
  }
633
660
  return { kind: "output", output: `${JSON.stringify(data, null, 2)}\n`, data };
634
661
  }