@zq-silk/yui 2.20.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 (142) hide show
  1. package/README.md +5 -0
  2. package/dist/cli/commandCatalog.js +88 -28
  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/invocationAuthority.js +2 -6
  7. package/dist/cli/invocationRouter.js +5 -5
  8. package/dist/cli/managedDiagnostics.js +24 -16
  9. package/dist/cli/roleWizard.js +5 -1
  10. package/dist/commands/durableJobCommands.js +2 -3
  11. package/dist/commands/globalRoleCommands.js +33 -8
  12. package/dist/commands/inboxCommands.js +39 -12
  13. package/dist/commands/operatorCommands.js +2 -5
  14. package/dist/commands/releaseCommands.js +16 -2
  15. package/dist/commands/roleConfiguration.js +25 -2
  16. package/dist/commands/sessionCommands.js +5 -2
  17. package/dist/commands/taskCommands.js +225 -99
  18. package/dist/commands/taskContextCommand.js +26 -4
  19. package/dist/commands/taskFactCommands.js +20 -7
  20. package/dist/commands/taskIntegrationCommands.js +3 -3
  21. package/dist/commands/taskRoleRuntimeStatus.js +15 -5
  22. package/dist/commands/taskUsageCommand.js +2 -6
  23. package/dist/commands/textInput.js +2 -2
  24. package/dist/context/contextFamilies.js +10 -0
  25. package/dist/context/currentSkills.js +6 -4
  26. package/dist/context/runContextPack.js +44 -5
  27. package/dist/context/runInputContract.js +1 -1
  28. package/dist/context/sessionBootstrapManifest.js +7 -5
  29. package/dist/context/sessionSkillNotice.js +53 -0
  30. package/dist/context/taskCatalog.js +4 -13
  31. package/dist/context/taskContext.js +135 -103
  32. package/dist/context/taskSearch.js +3 -7
  33. package/dist/controlPlaneCli.js +23 -25
  34. package/dist/controller/agentRuntimeObserver.js +57 -12
  35. package/dist/controller/clientRuntime.js +37 -8
  36. package/dist/controller/controller.js +3 -1
  37. package/dist/controller/diagnosticJournal.js +10 -2
  38. package/dist/controller/fileSchedulerStoreAdapter.js +72 -24
  39. package/dist/controller/jobClient.js +2 -2
  40. package/dist/controller/jobControl.js +2 -2
  41. package/dist/controller/jobDefinitionControl.js +5 -1
  42. package/dist/controller/messageHealth.js +4 -3
  43. package/dist/controller/providerRetryAdmission.js +15 -1
  44. package/dist/controller/providerRetryDelivery.js +18 -2
  45. package/dist/controller/runtime.js +15 -5
  46. package/dist/controller/runtimeEventInbox.js +107 -13
  47. package/dist/controller/runtimeEventProcessor.js +3 -3
  48. package/dist/controller/taskRunRecovery.js +153 -0
  49. package/dist/core/controllerClient.js +16 -2
  50. package/dist/core/controllerServer.js +14 -5
  51. package/dist/core/controllerTelemetry.js +51 -5
  52. package/dist/errors/cliFailure.js +14 -5
  53. package/dist/executor/agentAdapter.js +32 -4
  54. package/dist/executor/agentConfigurationFields.js +2 -0
  55. package/dist/executor/taskAgentCapabilities.js +2 -2
  56. package/dist/integration/gitIntegrationService.js +3 -2
  57. package/dist/integration/integrationSourceApplication.js +12 -1
  58. package/dist/interaction/notificationPresentation.js +11 -16
  59. package/dist/interaction/operatorPresentation.js +1 -1
  60. package/dist/message/messageContinuation.js +3 -1
  61. package/dist/output/boundedRead.js +36 -7
  62. package/dist/output/rolePresentation.js +3 -1
  63. package/dist/runtime/agentHost.js +12 -3
  64. package/dist/runtime/codexAppServerRuntime.js +110 -24
  65. package/dist/runtime/codexInteractiveHost.js +46 -0
  66. package/dist/runtime/providerRetry.js +5 -2
  67. package/dist/runtime/providerRuntimeIdentity.js +1 -1
  68. package/dist/runtime/structuredProviderHost.js +32 -34
  69. package/dist/scheduler/wakeupQueue.js +10 -4
  70. package/dist/storage/sqliteSchema.js +9 -0
  71. package/dist/storage/storageVersions.js +1 -1
  72. package/dist/task/taskAuthority.js +7 -3
  73. package/dist/tmux/tmuxManager.js +4 -2
  74. package/dist/web/webSettings.js +2 -0
  75. package/docs/authorization.md +18 -3
  76. package/docs/cli-information-contract.md +245 -6
  77. package/docs/cli-information-contract.zh-CN.md +107 -5
  78. package/docs/managed-turn-and-session-runtime.md +82 -21
  79. package/docs/managed-turn-and-session-runtime.zh-CN.md +56 -13
  80. package/docs/provider-retry.md +11 -0
  81. package/docs/release-workflow.md +62 -0
  82. package/docs/release-workflow.zh-CN.md +46 -0
  83. package/docs/roles-and-configuration.md +28 -0
  84. package/docs/roles-and-configuration.zh-CN.md +21 -0
  85. package/docs/storage-baseline.md +4 -1
  86. package/docs/storage-baseline.zh-CN.md +1 -1
  87. package/docs/task-delivery.md +7 -0
  88. package/docs/task-delivery.zh-CN.md +5 -0
  89. package/docs/task-discovery.md +5 -5
  90. package/docs/task-discovery.zh-CN.md +4 -4
  91. package/docs/testing/review-evaluation.md +7 -7
  92. package/docs/testing/review-evaluation.zh-CN.md +7 -7
  93. package/docs/testing/verification-levels.md +45 -2
  94. package/docs/testing/verification-levels.zh-CN.md +21 -2
  95. package/i18n/README.zh-CN.md +6 -0
  96. package/package.json +1 -1
  97. package/skills/yui-leader/SKILL.md +98 -189
  98. package/skills/yui-leader/VERSION +1 -1
  99. package/skills/yui-leader/references/artifacts.md +41 -0
  100. package/skills/yui-leader/references/assignment-execution.md +140 -0
  101. package/skills/yui-leader/references/completion.md +79 -0
  102. package/skills/yui-leader/references/delivery-scope.md +26 -0
  103. package/skills/yui-leader/references/execution.md +16 -487
  104. package/skills/yui-leader/references/implementation.md +81 -0
  105. package/skills/yui-leader/references/planning.md +7 -45
  106. package/skills/yui-leader/references/review.md +90 -0
  107. package/skills/yui-leader/references/task-progress.md +23 -0
  108. package/skills/yui-leader/references/work-allocation.md +118 -0
  109. package/skills/yui-operator/SKILL.md +77 -331
  110. package/skills/yui-operator/VERSION +1 -1
  111. package/skills/yui-operator/references/body-transport.md +62 -0
  112. package/skills/yui-operator/references/configuration.md +47 -0
  113. package/skills/yui-operator/references/lifecycle.md +41 -0
  114. package/skills/yui-operator/references/progress.md +39 -0
  115. package/skills/yui-operator/references/routing.md +57 -0
  116. package/skills/yui-operator/references/submission.md +56 -0
  117. package/skills/yui-operator/references/task-delivery.md +5 -2
  118. package/skills/yui-operator/references/web-lifecycle.md +19 -0
  119. package/skills/yui-operator/scripts/verify-submission.mjs +37 -0
  120. package/skills/yui-reviewer/SKILL.md +4 -2
  121. package/skills/yui-reviewer/VERSION +1 -1
  122. package/skills/yui-runtime/SKILL.md +177 -505
  123. package/skills/yui-runtime/VERSION +1 -1
  124. package/skills/yui-runtime/references/archive.md +51 -0
  125. package/skills/yui-runtime/references/command-discovery.md +41 -0
  126. package/skills/yui-runtime/references/completed-followup.md +65 -0
  127. package/skills/yui-runtime/references/context-ordering.md +3 -34
  128. package/skills/yui-runtime/references/context-reads.md +61 -0
  129. package/skills/yui-runtime/references/dispatched-run.md +61 -0
  130. package/skills/yui-runtime/references/execution-failure.md +105 -0
  131. package/skills/yui-runtime/references/global-context.md +18 -0
  132. package/skills/yui-runtime/references/global-input-controls.md +44 -0
  133. package/skills/yui-runtime/references/host-custody.md +30 -0
  134. package/skills/yui-runtime/references/inbox-recovery.md +77 -0
  135. package/skills/yui-runtime/references/incremental-context.md +40 -0
  136. package/skills/yui-runtime/references/publication.md +4 -104
  137. package/skills/yui-runtime/references/recovery.md +32 -192
  138. package/skills/yui-runtime/references/session-identity.md +18 -0
  139. package/skills/yui-runtime/references/session-replacement.md +45 -0
  140. package/skills/yui-runtime/references/uncertain-execution.md +29 -0
  141. package/skills/yui-worker/SKILL.md +7 -4
  142. package/skills/yui-worker/VERSION +1 -1
@@ -4,6 +4,7 @@ import { once } from "node:events";
4
4
  import WebSocket, { WebSocketServer } from "ws";
5
5
  import { AGENT_HOST_CONTROL_PROTOCOL } from "./agentHostProtocol.js";
6
6
  import { openCodexInteractiveConnection } from "./structuredProviderHost.js";
7
+ import { createSessionSkillNotice } from "../context/sessionSkillNotice.js";
7
8
  /**
8
9
  * Keep the native TUI and its transparent App Server attachment in one pane.
9
10
  * The existing Host acknowledgement carries the ID from that TUI's exact
@@ -15,6 +16,8 @@ import { openCodexInteractiveConnection } from "./structuredProviderHost.js";
15
16
  */
16
17
  export async function runCodexInteractiveHost(home, payload, openAgentHostControl) {
17
18
  const environment = payload.environment;
19
+ const skillNotice = createSessionSkillNotice(home);
20
+ const pendingNotices = new Map();
18
21
  if (environment.YUI_SESSION_SCOPE !== "global" || environment.YUI_ADAPTER_ID !== "codex"
19
22
  || payload.providerControl !== undefined) {
20
23
  throw new Error("Interactive Codex Host requires a global native TUI launch.");
@@ -106,6 +109,23 @@ export async function runCodexInteractiveHost(home, payload, openAgentHostContro
106
109
  startupRequestId = message.id;
107
110
  message.params = codexInteractiveStartupParameters(payload, params);
108
111
  }
112
+ if (message.method === "turn/start" || message.method === "turn/steer") {
113
+ if (snapshot.nativeSessionId === undefined)
114
+ throw new Error("Skill check requires the reserved native Thread.");
115
+ if (typeof message.id !== "string" && typeof message.id !== "number"
116
+ || pendingNotices.has(message.id))
117
+ throw new Error("Turn input requires a unique correlation id.");
118
+ const params = jsonObject(message.params);
119
+ if (params.threadId !== snapshot.nativeSessionId)
120
+ throw new Error("Turn input targets another Thread.");
121
+ const prepared = codexInteractiveSkillInput(skillNotice, environment, snapshot.nativeSessionId, params);
122
+ message.params = prepared.params;
123
+ pendingNotices.set(message.id, {
124
+ commit: prepared.commit,
125
+ ...(message.method === "turn/steer" && typeof params.expectedTurnId === "string"
126
+ ? { expectedTurnId: params.expectedTurnId } : {})
127
+ });
128
+ }
109
129
  void connection.send(message).catch(fail);
110
130
  }
111
131
  catch (error) {
@@ -115,6 +135,17 @@ export async function runCodexInteractiveHost(home, payload, openAgentHostContro
115
135
  });
116
136
  connection.onMessage((message) => {
117
137
  try {
138
+ if (message.method === undefined && (typeof message.id === "string" || typeof message.id === "number")) {
139
+ const pending = pendingNotices.get(message.id);
140
+ if (pending !== undefined) {
141
+ pendingNotices.delete(message.id);
142
+ const result = message.result;
143
+ const turnId = result?.turn?.id ?? result?.turnId;
144
+ if (message.error === undefined && typeof turnId === "string" && turnId.length > 0
145
+ && (pending.expectedTurnId === undefined || pending.expectedTurnId === turnId))
146
+ pending.commit();
147
+ }
148
+ }
118
149
  let nativeSessionId;
119
150
  if (startupRequestId !== undefined && message.id === startupRequestId
120
151
  && message.method === undefined) {
@@ -183,6 +214,21 @@ export async function runCodexInteractiveHost(home, payload, openAgentHostContro
183
214
  await control.close();
184
215
  }
185
216
  }
217
+ /** Preserve all native content parts (including images), adding only Host metadata. */
218
+ export function codexInteractiveSkillInput(notice, environment, nativeSessionId, params) {
219
+ if (!Array.isArray(params.input))
220
+ throw new Error("Turn input must be an array.");
221
+ const prepared = notice.prepare("", environment, nativeSessionId);
222
+ return {
223
+ params: {
224
+ ...params,
225
+ input: prepared.text.length === 0 ? params.input : [
226
+ { type: "text", text: prepared.text.trimEnd(), text_elements: [] }, ...params.input
227
+ ]
228
+ },
229
+ commit: prepared.commit
230
+ };
231
+ }
186
232
  /** The remote TUI can omit local launch configuration. Apply the reserved
187
233
  * workspace and managed identity on its actual startup, never by starting a
188
234
  * second Thread or changing daemon-wide configuration. */
@@ -211,7 +211,7 @@ export function providerRetryProjection(binding) {
211
211
  }
212
212
  /** Entry-point facts, never accumulated retry history or raw diagnostics.
213
213
  * Exact Session retry/show and failureRef retain the original evidence. */
214
- export function providerRetrySummary(binding) {
214
+ export function providerRetrySummary(binding, detailCommand) {
215
215
  const view = providerRetryProjection(binding);
216
216
  if (view === null)
217
217
  return null;
@@ -224,7 +224,10 @@ export function providerRetrySummary(binding) {
224
224
  currentTurn: currentTurn === null ? null : {
225
225
  attemptId: currentTurn.attemptId, status: currentTurn.status, nativeTurnId: currentTurn.nativeTurnId
226
226
  },
227
- historyCount: history.length, detailOmitted: true
227
+ historyCount: history.length, detailOmitted: true,
228
+ // This reads current Session evidence, not a frozen Run snapshot. The
229
+ // returned chain/attempt identities must still be compared by the caller.
230
+ ...(detailCommand === undefined ? {} : { currentDetailCommand: detailCommand })
228
231
  };
229
232
  }
230
233
  export function controlProviderRetry(binding, action, at) {
@@ -462,7 +462,7 @@ function orderedRunTimestamp(run, value, label) {
462
462
  }
463
463
  return normalized;
464
464
  }
465
- function providerTurnIsActive(run) {
465
+ export function providerTurnIsActive(run) {
466
466
  return run !== null && ["submitting", "accepted", "delivery-unknown"]
467
467
  .includes(run.status);
468
468
  }
@@ -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) {
@@ -146,10 +146,14 @@ export function projectActor(environment) {
146
146
  }
147
147
  return "user";
148
148
  }
149
+ /** Read-only Job requests carry the original identity, not the record's owner.
150
+ * The Controller authenticates it; mutations keep resolveJobCaller's target check. */
151
+ export function resolveJobReadCaller(environment, taskId) {
152
+ return resolveJobCaller(environment, environment?.YUI_SESSION_SCOPE === "task" ? environment.YUI_TASK_ID ?? "" : taskId);
153
+ }
149
154
  /**
150
- * Resolve the caller's Task, Role and native Session identity. The Controller
151
- * verifies it against durable state and supplies the current AgentRun. A Task
152
- * caller cannot control another Task; an incomplete identity is not a user.
155
+ * Resolve execution identity. The Controller verifies it against durable state;
156
+ * a Task caller cannot control another Task or infer user authority.
153
157
  */
154
158
  export function resolveJobCaller(environment, taskId) {
155
159
  const env = environment ?? {};
@@ -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