@zq-silk/yui 1.1.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/ARCHITECTURE.md +2 -1
  2. package/ARCHITECTURE.zh-CN.md +2 -1
  3. package/README.md +2 -0
  4. package/dist/cli/commandCatalog.js +344 -324
  5. package/dist/cli/commandDiscovery.js +69 -0
  6. package/dist/cli/completion.js +22 -227
  7. package/dist/cli/dynamicCompletion.js +12 -6
  8. package/dist/cli/invocationAuthority.js +2 -2
  9. package/dist/cli/invocationRouter.js +18 -7
  10. package/dist/cli/managedDiagnostics.js +2 -1
  11. package/dist/cli/updatePorts.js +0 -5
  12. package/dist/cli.js +184 -130
  13. package/dist/commands/capabilityCommands.js +5 -5
  14. package/dist/commands/controllerCommands.js +2 -1
  15. package/dist/commands/globalRoleCommands.js +86 -44
  16. package/dist/commands/grantCommands.js +48 -8
  17. package/dist/commands/taskCommands.js +157 -196
  18. package/dist/commands/taskContextCommand.js +29 -6
  19. package/dist/commands/taskFactCommands.js +10 -69
  20. package/dist/commands/taskInputCommands.js +26 -35
  21. package/dist/commands/taskPublicationCommands.js +2 -35
  22. package/dist/commands/taskPublicationVerifyCommand.js +1 -2
  23. package/dist/commands/taskUpstreamCommands.js +2 -1
  24. package/dist/context/runContextPack.js +8 -29
  25. package/dist/context/sessionBootstrapManifest.js +5 -101
  26. package/dist/context/taskContext.js +104 -41
  27. package/dist/controller/clientRuntime.js +16 -8
  28. package/dist/controller/fileSchedulerStoreAdapter.js +24 -0
  29. package/dist/controller/runtime.js +6 -0
  30. package/dist/core/controllerClient.js +46 -21
  31. package/dist/core/protocol.js +12 -7
  32. package/dist/doctor/doctor.js +4 -3
  33. package/dist/errors/cliError.js +4 -4
  34. package/dist/errors/cliFailure.js +213 -0
  35. package/dist/executor/fileRoleLaunchPlanner.js +1 -4
  36. package/dist/grant/capabilityGrant.js +13 -0
  37. package/dist/grant/taskAuthorization.js +127 -0
  38. package/dist/kernel/accessAssessment.js +1 -0
  39. package/dist/kernel/builtinCapabilities.js +88 -9
  40. package/dist/kernel/capabilityRegistry.js +64 -19
  41. package/dist/kernel/kernelPorts.js +2 -2
  42. package/dist/nativeAgent/agent.js +173 -0
  43. package/dist/nativeAgent/contracts.js +1 -0
  44. package/dist/nativeAgent/demo.js +29 -0
  45. package/dist/nativeAgent/index.js +3 -0
  46. package/dist/nativeAgent/mockProvider.js +43 -0
  47. package/dist/nativeAgent/textTools.js +156 -0
  48. package/dist/nativeAgent/validation.js +95 -0
  49. package/dist/output/boundedRead.js +118 -0
  50. package/dist/plugins/pluginService.js +98 -8
  51. package/dist/release/releaseWorkflowEngine.js +8 -1
  52. package/dist/resources/projectResourceService.js +23 -1
  53. package/dist/runtime/managedCaller.js +2 -2
  54. package/dist/runtime/managedIdentity.js +6 -0
  55. package/dist/runtime/runtimeCoherence.js +10 -8
  56. package/dist/storage/sqliteSchema.js +10 -0
  57. package/dist/storage/storageVersions.js +1 -1
  58. package/dist/storage/taskStore.js +3 -0
  59. package/dist/surface/surfaceContributions.js +3 -0
  60. package/dist/task/leaderArchive.js +111 -0
  61. package/dist/task/leaderArchiveAuthority.js +24 -0
  62. package/dist/task/taskAuthority.js +5 -10
  63. package/dist/tmux/commandExecutor.js +7 -5
  64. package/dist/web/assets/assetManifest.js +38 -22
  65. package/dist/web/assets/client/api.js +115 -0
  66. package/dist/web/assets/client/app.js +555 -863
  67. package/dist/web/assets/client/components.js +323 -897
  68. package/dist/web/assets/client/detail.js +333 -0
  69. package/dist/web/assets/client/dock.js +225 -0
  70. package/dist/web/assets/client/dom.js +55 -3
  71. package/dist/web/assets/client/evidence.js +172 -0
  72. package/dist/web/assets/client/format.js +25 -14
  73. package/dist/web/assets/client/forms.js +230 -0
  74. package/dist/web/assets/client/i18n.js +1020 -791
  75. package/dist/web/assets/client/overview.js +115 -0
  76. package/dist/web/assets/client/records.js +63 -0
  77. package/dist/web/assets/client/sections.js +313 -0
  78. package/dist/web/assets/client/sidebar.js +117 -0
  79. package/dist/web/assets/client/theme.js +61 -22
  80. package/dist/web/assets/icons.js +39 -0
  81. package/dist/web/assets/shell.js +124 -118
  82. package/dist/web/assets/styles/base.js +41 -0
  83. package/dist/web/assets/styles/components.js +167 -0
  84. package/dist/web/assets/styles/layout.js +49 -77
  85. package/dist/web/assets/styles/markdown.js +16 -24
  86. package/dist/web/assets/styles/responsive.js +33 -44
  87. package/dist/web/assets/styles/tokens.js +65 -89
  88. package/dist/web/assets/styles/views.js +289 -0
  89. package/dist/web/webServer.js +2 -1
  90. package/docs/architecture/README.md +1 -0
  91. package/docs/architecture/README.zh-CN.md +1 -0
  92. package/docs/cli-information-contract.md +107 -0
  93. package/docs/cli-information-contract.zh-CN.md +81 -0
  94. package/docs/managed-turn-and-session-runtime.md +9 -0
  95. package/docs/managed-turn-and-session-runtime.zh-CN.md +6 -0
  96. package/docs/plugin-sdk.md +7 -3
  97. package/docs/plugin-sdk.zh-CN.md +5 -2
  98. package/docs/release-workflow.md +42 -9
  99. package/docs/release-workflow.zh-CN.md +30 -7
  100. package/docs/storage-baseline.md +5 -0
  101. package/docs/testing/verification-levels.md +2 -2
  102. package/i18n/README.zh-CN.md +2 -0
  103. package/package.json +1 -1
  104. package/skills/yui-leader/SKILL.md +8 -1
  105. package/skills/yui-leader/references/authorization.md +64 -0
  106. package/skills/yui-leader/references/execution.md +18 -4
  107. package/skills/yui-leader/references/task-plugins.md +4 -2
  108. package/skills/yui-operator/SKILL.md +9 -2
  109. package/skills/yui-runtime/SKILL.md +77 -9
  110. package/skills/yui-runtime/references/publication.md +3 -1
  111. package/dist/web/assets/client/taskSummary.js +0 -350
  112. package/dist/web/assets/client/taskSurface.js +0 -616
  113. package/dist/web/assets/client/view.js +0 -608
  114. package/dist/web/assets/styles/cards.js +0 -247
  115. package/dist/web/assets/styles/widgets.js +0 -168
@@ -1,16 +1,28 @@
1
1
  import { usageError } from "../errors/cliError.js";
2
- import { inspectTaskContext, readTaskContext, readTaskContextDelta } from "../context/taskContext.js";
2
+ import { inspectTaskContext, listTaskContext, readTaskContext, readTaskContextDelta } from "../context/taskContext.js";
3
+ import { readOptions } from "../output/boundedRead.js";
4
+ export function taskRecordList(args, store, family, environment = {}) {
5
+ const parsed = readOptions(args, ["--cursor", "--limit", "--status", "--after", "--work-item"]);
6
+ if (parsed.positionals.length !== 1)
7
+ throw usageError("Record list requires one Task and optional --limit/--cursor/--status/--after/--work-item.");
8
+ const data = listTaskContext(store, parsed.positionals[0], family, {
9
+ ...parsed, status: parsed.values.get("--status"), after: parsed.values.get("--after"),
10
+ workItemId: parsed.values.get("--work-item")
11
+ }, environment);
12
+ return { kind: "output", output: `${JSON.stringify(data)}\n`, data };
13
+ }
3
14
  /** CLI parsing only; typed callers and capability ingress share the read model. */
4
15
  export function runTaskContextCommand(args, store, environment = {}) {
5
- const explicit = ["read", "delta", "inspect"].includes(args[0] ?? "");
16
+ const explicit = ["read", "delta", "inspect", "list"].includes(args[0] ?? "");
6
17
  const action = explicit ? args[0] : "read";
7
18
  const taskId = args[explicit ? 1 : 0];
8
19
  if (!taskId || taskId.startsWith("--"))
9
- throw usageError("Task context usage: yui task context [read|delta|inspect] <task> [--after <cursor>] [--continuation <cursor>] [--limit <1..100>] [--store <store> --ref <id> --digest <digest>].");
20
+ throw usageError("Task context usage: yui task context [read|list|delta|inspect] <task> [--after <cursor|timestamp>] [--continuation <cursor>] [--cursor <cursor>] [--limit <1..100>] [--store <store> --ref <id> --digest <digest>] [--status <status>] [--work-item <id>].");
10
21
  const options = new Map();
11
22
  const rest = args.slice(explicit ? 2 : 1);
12
23
  const allowed = action === "read" ? [] : action === "delta"
13
- ? ["--after", "--continuation", "--limit"] : ["--store", "--ref", "--digest"];
24
+ ? ["--after", "--continuation", "--limit"] : action === "list"
25
+ ? ["--store", "--cursor", "--limit", "--status", "--after", "--work-item"] : ["--store", "--ref", "--digest", "--cursor"];
14
26
  for (let i = 0; i < rest.length; i += 2) {
15
27
  const flag = rest[i];
16
28
  const value = rest[i + 1];
@@ -22,6 +34,16 @@ export function runTaskContextCommand(args, store, environment = {}) {
22
34
  let data;
23
35
  if (action === "read")
24
36
  data = readTaskContext(store, taskId, environment);
37
+ else if (action === "list") {
38
+ const family = options.get("--store");
39
+ if (family === undefined)
40
+ throw usageError("Context list requires --store.");
41
+ data = listTaskContext(store, taskId, family, {
42
+ cursor: options.get("--cursor"),
43
+ status: options.get("--status"), after: options.get("--after"), workItemId: options.get("--work-item"),
44
+ ...(options.has("--limit") ? { limit: Number(options.get("--limit")) } : {})
45
+ }, environment);
46
+ }
25
47
  else if (action === "delta") {
26
48
  const after = options.get("--after");
27
49
  if (after === undefined)
@@ -36,7 +58,8 @@ export function runTaskContextCommand(args, store, environment = {}) {
36
58
  const refId = options.get("--ref");
37
59
  if (family === undefined || refId === undefined)
38
60
  throw usageError("Context inspect requires --store and --ref.");
39
- data = inspectTaskContext(store, taskId, { store: family, refId, digest: options.get("--digest") }, environment);
61
+ data = inspectTaskContext(store, taskId, { store: family, refId, digest: options.get("--digest"),
62
+ cursor: options.get("--cursor") }, environment);
40
63
  }
41
- return { kind: "output", output: `${JSON.stringify(data, null, 2)}\n`, data };
64
+ return { kind: "output", output: `${JSON.stringify(data)}\n`, data };
42
65
  }
@@ -1,11 +1,12 @@
1
1
  import { createTaskBrief, updateTaskBrief } from "../brief/taskBrief.js";
2
- import { assertContextRecordReadable, contextRecordReader } from "../context/taskContext.js";
2
+ import { assertContextRecordReadable } from "../context/taskContext.js";
3
+ import { taskRecordList } from "./taskContextCommand.js";
4
+ import { materialize } from "../context/taskContext.js";
3
5
  import { enqueueWork } from "../coordination/workMailboxQueue.js";
4
6
  import { createDecision, supersedeDecision } from "../decision/decision.js";
5
7
  import { dataError, usageError } from "../errors/cliError.js";
6
8
  import { createMilestone } from "../milestone/milestone.js";
7
- import { defaultTableWidth, renderTable } from "../output/table.js";
8
- import { assertTaskOpen, clock, exactPositionals, leaderActionEventPayload, leaderMailbox, notifyMailbox, optionalNonEmptyOption, output, parseMultiValueTail, parseTail, presentTime, recordTaskEvent, requiredOption, requiredText, requireTask, taskActor, taskMailbox, taskRef } from "./taskCommandSupport.js";
9
+ import { assertTaskOpen, clock, exactPositionals, leaderActionEventPayload, leaderMailbox, notifyMailbox, output, parseMultiValueTail, parseTail, presentTime, recordTaskEvent, requiredOption, requiredText, requireTask, taskActor, taskMailbox, taskRef } from "./taskCommandSupport.js";
9
10
  import { failureCapabilitiesCommand } from "../runtime/agentFailureContext.js";
10
11
  export function taskBriefCommand(args, store, options) {
11
12
  const [command, ...rest] = args;
@@ -87,7 +88,9 @@ export function taskBriefCommand(args, store, options) {
87
88
  if (result.task.status === "active") {
88
89
  notifyMailbox(options.runtime, leaderMailbox(result.task.id));
89
90
  }
90
- return output(`Updated brief for ${result.task.id}\n`, { taskId: result.task.id, brief: result.brief });
91
+ return output(`Updated brief for ${result.task.id}\n`, {
92
+ taskId: result.task.id, effect: "saved", ref: materialize("task-brief", result.task.id, result.brief).ref
93
+ });
91
94
  }
92
95
  throw usageError(command === undefined
93
96
  ? "Task brief command is required."
@@ -125,29 +128,7 @@ export function taskDecisionCommand(args, store, options) {
125
128
  return output(`Recorded decision ${result.decision.id} for ${result.task.id}\n`);
126
129
  }
127
130
  if (command === "list") {
128
- const usage = "Task decision list usage: yui task decision list <task> [--status active|superseded].";
129
- const parsed = parseTail(rest, new Set(["--status"]), usage);
130
- exactPositionals(parsed.positionals, 1, usage);
131
- const task = requireTask(store, parsed.positionals[0]);
132
- const readable = contextRecordReader(store, task.id, options.environment);
133
- let decisions = store.listDecisions(task.id).filter(record => readable("task-decision", record.id));
134
- const status = parsed.options.get("--status");
135
- if (status !== undefined) {
136
- if (status !== "active" && status !== "superseded") {
137
- throw usageError("--status must be active or superseded.", usage);
138
- }
139
- decisions = decisions.filter((d) => d.status === status);
140
- }
141
- if (decisions.length === 0) {
142
- return output(`No decisions found for ${task.id}.\n`, { taskId: task.id, decisions: [] });
143
- }
144
- const timeZone = store.getConfig().timeZone;
145
- return output(`${renderTable(`Decisions: ${task.id}`, [
146
- { header: "Decision", minWidth: 8, maxWidth: 18 },
147
- { header: "Status", minWidth: 6, maxWidth: 12 },
148
- { header: "Title", minWidth: 8, maxWidth: 64 },
149
- { header: "Created", minWidth: 10, maxWidth: 28 }
150
- ], decisions.map((d) => [d.id, d.status, d.title, presentTime(d.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, decisions });
131
+ return taskRecordList(rest, store, "task-decision", options.environment);
151
132
  }
152
133
  if (command === "show") {
153
134
  exactPositionals(rest, 2, "Task decision show usage: yui task decision show <task> <decision>.");
@@ -231,19 +212,7 @@ export function taskMilestoneCommand(args, store, options) {
231
212
  return output(`Added milestone ${result.milestone.id} for ${result.task.id}\n`);
232
213
  }
233
214
  if (command === "list") {
234
- exactPositionals(rest, 1, "Task milestone list usage: yui task milestone list <task>.");
235
- const task = requireTask(store, rest[0]);
236
- const readable = contextRecordReader(store, task.id, options.environment);
237
- const milestones = store.listMilestones(task.id).filter(record => readable("task-milestone", record.id));
238
- if (milestones.length === 0) {
239
- return output(`No milestones found for ${task.id}.\n`, { taskId: task.id, milestones: [] });
240
- }
241
- const timeZone = store.getConfig().timeZone;
242
- return output(`${renderTable(`Milestones: ${task.id}`, [
243
- { header: "Milestone", minWidth: 9, maxWidth: 18 },
244
- { header: "Title", minWidth: 8, maxWidth: 64 },
245
- { header: "Created", minWidth: 10, maxWidth: 28 }
246
- ], milestones.map((m) => [m.id, m.title, presentTime(m.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, milestones });
215
+ return taskRecordList(rest, store, "task-milestone", options.environment);
247
216
  }
248
217
  if (command === "show") {
249
218
  exactPositionals(rest, 2, "Task milestone show usage: yui task milestone show <task> <milestone>.");
@@ -269,35 +238,7 @@ export function taskMilestoneCommand(args, store, options) {
269
238
  export function taskEventCommand(args, store, options = {}) {
270
239
  const [command, ...rest] = args;
271
240
  if (command === "list") {
272
- const eventListUsage = "Task event list usage: yui task event list <task> [--after <timestamp>] [--limit <n>].";
273
- const parsed = parseTail(rest, new Set(["--after", "--limit"]), eventListUsage);
274
- exactPositionals(parsed.positionals, 1, eventListUsage);
275
- const task = requireTask(store, parsed.positionals[0]);
276
- const readable = contextRecordReader(store, task.id, options.environment);
277
- let events = store.listEvents(task.id).filter(record => readable("task-event", record.id));
278
- const after = optionalNonEmptyOption(parsed.options, "--after");
279
- if (after !== undefined) {
280
- const afterMs = Date.parse(after);
281
- if (!Number.isFinite(afterMs))
282
- throw usageError("--after must be a valid timestamp.", eventListUsage);
283
- events = events.filter((e) => Date.parse(e.createdAt) > afterMs);
284
- }
285
- const limit = optionalNonEmptyOption(parsed.options, "--limit");
286
- if (limit !== undefined) {
287
- const n = Number(limit);
288
- if (!Number.isSafeInteger(n) || n <= 0)
289
- throw usageError("--limit must be a positive integer.", eventListUsage);
290
- events = events.slice(-n);
291
- }
292
- if (events.length === 0) {
293
- return output(`No events found for ${task.id}.\n`, { taskId: task.id, events: [] });
294
- }
295
- const timeZone = store.getConfig().timeZone;
296
- return output(`${renderTable(`Events: ${task.id}`, [
297
- { header: "Event", minWidth: 8, maxWidth: 18 },
298
- { header: "Type", minWidth: 8, maxWidth: 28 },
299
- { header: "Created", minWidth: 10, maxWidth: 28 }
300
- ], events.map((e) => [e.id, e.type, presentTime(e.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, events });
241
+ return taskRecordList(rest, store, "task-event", options.environment);
301
242
  }
302
243
  if (command === "show") {
303
244
  exactPositionals(rest, 2, "Task event show usage: yui task event show <task> <event>.");
@@ -2,7 +2,9 @@ import { enqueueWork } from "../coordination/workMailboxQueue.js";
2
2
  import { dataError, roleNotFound, taskNotFound, usageError } from "../errors/cliError.js";
3
3
  import { createTaskEvent } from "../event/taskEvent.js";
4
4
  import { answerInputRequest, cancelInputRequest, createInputRequest } from "../input/inputRequest.js";
5
- import { defaultTableWidth, renderTable } from "../output/table.js";
5
+ import { boundedDocument, recordPage, readOptions } from "../output/boundedRead.js";
6
+ import { materialize, resolveContextReader } from "../context/taskContext.js";
7
+ import { taskRecordList } from "./taskContextCommand.js";
6
8
  import { formatTimestamp } from "../output/timePresentation.js";
7
9
  import { requireManagedTaskCaller, requireManagedGlobalCaller } from "../runtime/managedCaller.js";
8
10
  import { isRoleRunStalled, RUN_RECOVERED_EVENT } from "../scheduler/roleRunStall.js";
@@ -12,7 +14,7 @@ export function runTaskInputCommand(args, store, options) {
12
14
  const [command, ...rest] = args;
13
15
  switch (command) {
14
16
  case "request": return createRequest(rest, store, options);
15
- case "list": return listRequests(rest, store);
17
+ case "list": return listRequests(rest, store, options);
16
18
  case "show": return showRequest(rest, store, options);
17
19
  case "answer": return answerRequest(rest, store, options);
18
20
  case "cancel": return cancelRequest(rest, store, options);
@@ -86,51 +88,40 @@ function createRequest(args, store, options) {
86
88
  notifyMailbox(options, { kind: "operator" });
87
89
  return output(`Created input request ${request.id} for ${request.taskId}\n`, { request });
88
90
  }
89
- function listRequests(args, store) {
90
- const usage = "Task input list usage: yui task input list [task] [--all].";
91
- const parsed = parseTail(args, new Set(), usage, new Set(["--all"]));
91
+ function listRequests(args, store, options) {
92
+ const usage = "Task input list usage: yui task input list [task] [--all] [--limit <n>] [--cursor <cursor>].";
93
+ const all = args.includes("--all");
94
+ if (args.filter(arg => arg === "--all").length > 1)
95
+ throw usageError(usage);
96
+ const parsed = readOptions(args.filter(arg => arg !== "--all"));
92
97
  if (parsed.positionals.length > 1)
93
98
  throw usageError(usage);
94
- const taskId = parsed.positionals[0];
95
- if (taskId !== undefined)
96
- requireTask(store, taskId);
97
- const all = taskId === undefined
98
- ? store.listAllInputRequests()
99
- : store.listInputRequests(taskId);
100
- const requests = parsed.options.has("--all")
101
- ? all
102
- : all.filter((request) => request.status === "open");
103
- let rendered = "No input requests found.\n";
104
- if (requests.length > 0) {
105
- const timeZone = store.getConfig().timeZone;
106
- rendered = `${renderTable(taskId === undefined ? "Input inbox" : `Input requests: ${taskId}`, [
107
- { header: "Input", minWidth: 6, maxWidth: 18 },
108
- { header: "Task", minWidth: 6, maxWidth: 18 },
109
- { header: "Status", minWidth: 6, maxWidth: 10 },
110
- { header: "Policy", minWidth: 8, maxWidth: 12 },
111
- { header: "Question", minWidth: 8, maxWidth: 72 },
112
- { header: "Created", minWidth: 10, maxWidth: 28 }
113
- ], requests.map((request) => [
114
- taskId === undefined ? `${request.taskId}/${request.id}` : request.id,
115
- request.taskId,
116
- request.status,
117
- request.policy.kind,
118
- request.question,
119
- formatTimestamp(request.createdAt, timeZone)
120
- ]), defaultTableWidth())}\n`;
99
+ const caller = resolveContextReader(store, options.environment ?? {});
100
+ const taskId = parsed.positionals[0] ?? caller?.taskId;
101
+ if (taskId !== undefined) {
102
+ return taskRecordList([taskId, ...(all ? [] : ["--status", "open"]),
103
+ ...(parsed.cursor === undefined ? [] : ["--cursor", parsed.cursor]),
104
+ ...(parsed.limit === undefined ? [] : ["--limit", String(parsed.limit)])], store, "input-request", options.environment);
121
105
  }
122
- return output(rendered, { requests });
106
+ const requests = store.listAllInputRequests().filter(request => all || request.status === "open");
107
+ const data = recordPage(requests.map(request => ({
108
+ taskId: request.taskId, ref: materialize("input-request", request.id, request).ref,
109
+ status: request.status, summary: request.question.slice(0, 400), policy: request.policy.kind
110
+ })), `input-inbox:${all ? "all" : "open"}`, parsed);
111
+ return output(`${JSON.stringify(data)}\n`, data);
123
112
  }
124
113
  function showRequest(args, store, options) {
125
114
  const usage = "Task input show usage: yui task input show (<task>/<input> | <input> --task <task>).";
126
- const parsed = parseTail(args, new Set(["--task"]), usage);
115
+ const parsed = parseTail(args, new Set(["--task", "--cursor"]), usage);
127
116
  exactPositionals(parsed.positionals, 1, usage);
128
117
  const reference = inputRequestReference(store, parsed.positionals[0], optionalNonEmptyOption(parsed.options, "--task"), options.environment);
129
118
  const request = store.getInputRequest(reference.taskId, reference.localId);
130
119
  if (request === null) {
131
120
  throw dataError(`Input request not found: ${reference.taskId}/${reference.localId}.`);
132
121
  }
133
- return output(renderInputRequest(request, store.getConfig().timeZone), { request });
122
+ const data = boundedDocument({ request }, `input:${reference.taskId}/${reference.localId}`, parsed.options.get("--cursor"));
123
+ return output("contentPage" in data ? `${JSON.stringify(data)}\n`
124
+ : renderInputRequest(request, store.getConfig().timeZone), data);
134
125
  }
135
126
  function answerRequest(args, store, options) {
136
127
  const usage = "Task input answer usage: yui task input answer (<task>/<input> | <input> --task <task>) (--choice <key> | --text <text>).";
@@ -1,7 +1,7 @@
1
1
  import { isDeepStrictEqual } from "node:util";
2
2
  import { dataError, taskNotFound, usageError } from "../errors/cliError.js";
3
3
  import { createTaskEvent } from "../event/taskEvent.js";
4
- import { defaultTableWidth, renderTable } from "../output/table.js";
4
+ import { taskRecordList } from "./taskContextCommand.js";
5
5
  import { formatTimestamp } from "../output/timePresentation.js";
6
6
  import { resolveProject } from "../repository/project.js";
7
7
  import { createPublicationReference } from "../task/publicationReference.js";
@@ -26,7 +26,7 @@ export function runPublicationCommand(args, store, options) {
26
26
  const [command, ...rest] = args;
27
27
  switch (command) {
28
28
  case "upsert": return upsertPublication(rest, store, options);
29
- case "list": return listPublications(rest, store);
29
+ case "list": return taskRecordList(rest, store, "publication", options.environment);
30
30
  case "show": return showPublication(rest, store);
31
31
  default:
32
32
  throw usageError(command === undefined
@@ -125,34 +125,6 @@ export function upsertTaskPublication(store, task, input, actor, now) {
125
125
  recordPublicationEvent(store, reference, now);
126
126
  return { reference, idempotent: false };
127
127
  }
128
- function listPublications(args, store) {
129
- const usage = "Task publication list usage: yui task publication list <task>.";
130
- const parsed = parseTail(args, new Set(), usage);
131
- exactPositionals(parsed.positionals, 1, usage);
132
- const task = requireTask(store, parsed.positionals[0]);
133
- const references = store.listPublicationReferences(task.id);
134
- if (references.length === 0) {
135
- return output("No publication references found.\n", []);
136
- }
137
- const rendered = `${renderTable(`Publication references: ${task.id}`, [
138
- { header: "ID", minWidth: 12, maxWidth: 22 },
139
- { header: "External", minWidth: 16, maxWidth: 40 },
140
- { header: "Title", minWidth: 16, maxWidth: 40 },
141
- { header: "State", minWidth: 7, maxWidth: 10 },
142
- { header: "Verification", minWidth: 11, maxWidth: 14 },
143
- { header: "Lineage", minWidth: 18, maxWidth: 58 },
144
- { header: "Supersedes", minWidth: 12, maxWidth: 22 }
145
- ], references.map((reference) => [
146
- reference.id,
147
- `${reference.provider}/${reference.repository}/${reference.externalId}`,
148
- reference.title ?? "-",
149
- reference.state,
150
- reference.verification,
151
- lineageSummary(reference),
152
- reference.supersedes ?? "-"
153
- ]), defaultTableWidth())}\n`;
154
- return output(rendered, references);
155
- }
156
128
  function showPublication(args, store) {
157
129
  const usage = "Task publication show usage: yui task publication show (<task>/<publication-id> | <task> <publication-id>).";
158
130
  const parsed = parseTail(args, new Set(), usage);
@@ -197,11 +169,6 @@ function renderPublication(reference, timeZone) {
197
169
  `Created: ${formatTimestamp(reference.createdAt, timeZone)}`
198
170
  ].join("\n").concat("\n");
199
171
  }
200
- function lineageSummary(reference) {
201
- const local = reference.localCommit ?? "unknown";
202
- const remote = reference.remoteCommit ?? "unknown";
203
- return `${local.slice(0, 12)} -> ${reference.externalId} -> ${remote.slice(0, 12)}`;
204
- }
205
172
  function parseVerification(parsed, usage) {
206
173
  if (parsed.flags.has("--reported") && parsed.flags.has("--verified")) {
207
174
  throw usageError("--reported and --verified are mutually exclusive.", usage);
@@ -36,8 +36,7 @@ export async function runTaskPublicationVerifyCommand(args, store, options) {
36
36
  if (error instanceof CliError)
37
37
  throw error;
38
38
  throw runtimeError(`Publication verification failed for ${request.publication.provider}/`
39
- + `${request.publication.repository}/${request.publication.externalId}: `
40
- + `${error instanceof Error ? error.message : String(error)}`);
39
+ + `${request.publication.repository}/${request.publication.externalId}.`, { cause: error });
41
40
  }
42
41
  assertVerificationObservation(request, observation);
43
42
  const result = commitPublicationVerification(store, request, observation, options.now?.() ?? new Date());
@@ -1,5 +1,6 @@
1
1
  import { parseRepeatable } from "../cli/parseRepeatable.js";
2
2
  import { usageError } from "../errors/cliError.js";
3
+ import { describeCliFailure } from "../errors/cliFailure.js";
3
4
  import { GitIntegrationService } from "../integration/gitIntegrationService.js";
4
5
  import { createIntegrationAttempt } from "../integration/integrationAttempt.js";
5
6
  import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
@@ -128,7 +129,7 @@ async function integrateUpstream(args, store, home, options) {
128
129
  failure = {
129
130
  projectId: binding.projectId,
130
131
  phase,
131
- message: error instanceof Error ? error.message : String(error),
132
+ message: describeCliFailure(error, { environment: options.environment }).message,
132
133
  ...(integrationId === undefined ? {} : { integrationId }),
133
134
  effect: integrationId === undefined ? "not-started" : "inspect-required"
134
135
  };
@@ -10,9 +10,6 @@ import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
10
10
  import { contextContentDigest, contextSnapshotRef, createContextSnapshot, validateContextSnapshot } from "./contextSnapshot.js";
11
11
  import { MAX_SYNTHESIS_SOURCE_RUNS, sourceRunContextValue } from "./sourceRunContext.js";
12
12
  export const RUN_CONTEXT_PACK_SCHEMA_VERSION = 1;
13
- export const RUN_CONTEXT_PACK_MAX_REFS = 256;
14
- export const RUN_CONTEXT_PACK_MAX_BYTES = 8 * 1024 * 1024;
15
- export const RUN_CONTEXT_EXPAND_MAX_BYTES = 4 * 1024 * 1024;
16
13
  export function freezeRunContextSnapshot(store, run, now, frozenBy = "controller", baselineRef, sourceRunIds) {
17
14
  if (baselineRef !== undefined) {
18
15
  const baseline = store.getContextSnapshot(run.taskId, baselineRef.id);
@@ -212,18 +209,9 @@ export function buildRunContextPack(store, taskId, runId) {
212
209
  const run = requireExactRun(store, taskId, runId);
213
210
  const snapshot = readRunContextSnapshot(store, run);
214
211
  const pointers = snapshot.refs;
215
- if (pointers.length > RUN_CONTEXT_PACK_MAX_REFS) {
216
- throw new Error(`AgentRun Context exceeds ${RUN_CONTEXT_PACK_MAX_REFS} authorized refs.`);
217
- }
218
212
  const view = contextView(run);
219
213
  const writableProjectIds = run.effective.executionAuthority === "planning"
220
214
  ? [] : run.effective.writeProjectIds;
221
- const summaries = pointers.map((ref) => Object.freeze({
222
- refId: ref.refId,
223
- store: ref.store,
224
- summary: ref.summary ?? `${ref.store} ${ref.refId}`,
225
- digest: ref.digest
226
- }));
227
215
  const body = {
228
216
  schemaVersion: RUN_CONTEXT_PACK_SCHEMA_VERSION,
229
217
  identity: Object.freeze({
@@ -237,10 +225,10 @@ export function buildRunContextPack(store, taskId, runId) {
237
225
  }),
238
226
  snapshot: contextSnapshotRef(snapshot),
239
227
  input: run.inputs[0].input,
240
- authority: Object.freeze({ view, readableRefs: pointers, writableProjectIds }),
228
+ authority: Object.freeze({ view, writableProjectIds }),
241
229
  pointers,
242
- summaries,
243
- deltas: pointers.filter((ref) => run.inputs[0].input.deltaRefIds.includes(ref.refId)),
230
+ deltaRefs: pointers.filter((ref) => run.inputs[0].input.deltaRefIds.includes(ref.refId))
231
+ .map(({ store, refId }) => ({ store, refId })),
244
232
  completion: Object.freeze({
245
233
  allowedActions: completionActions(view, run.effective.executionAuthority),
246
234
  exactRunRef: `${taskId}/${runId}`
@@ -248,17 +236,12 @@ export function buildRunContextPack(store, taskId, runId) {
248
236
  };
249
237
  const digest = contextContentDigest(body);
250
238
  const preliminaryBytes = Buffer.byteLength(JSON.stringify({ ...body, digest }), "utf8");
251
- if (preliminaryBytes > RUN_CONTEXT_PACK_MAX_BYTES) {
252
- throw new Error(`AgentRun Context Pack exceeds ${RUN_CONTEXT_PACK_MAX_BYTES} bytes.`);
253
- }
254
239
  const pack = Object.freeze({
255
240
  ...body,
256
- budget: Object.freeze({
257
- maxRefs: RUN_CONTEXT_PACK_MAX_REFS,
258
- returnedRefs: pointers.length,
259
- maxBytes: RUN_CONTEXT_PACK_MAX_BYTES,
260
- returnedBytes: preliminaryBytes,
261
- truncated: false
241
+ inventory: Object.freeze({
242
+ refs: pointers.length,
243
+ frozenBytes: preliminaryBytes,
244
+ complete: true
262
245
  }),
263
246
  digest,
264
247
  liveTaskState: readLiveTaskState(store, taskId)
@@ -303,10 +286,6 @@ export function expandRunContextRef(store, taskId, runId, refId, refStore) {
303
286
  if (materialized === undefined || materialized.ref.digest !== authorized[0].digest) {
304
287
  throw new Error(`AgentRun Context ref is unavailable or drifted: ${selector}.`);
305
288
  }
306
- const bytes = Buffer.byteLength(JSON.stringify(materialized.value), "utf8");
307
- if (bytes > RUN_CONTEXT_EXPAND_MAX_BYTES) {
308
- throw new Error(`AgentRun Context expansion exceeds ${RUN_CONTEXT_EXPAND_MAX_BYTES} bytes.`);
309
- }
310
289
  return Object.freeze({
311
290
  ref: materialized.ref,
312
291
  value: materialized.value,
@@ -330,7 +309,7 @@ export function buildRunContextDelta(store, taskId, runId, after) {
330
309
  schemaVersion: 1,
331
310
  after,
332
311
  cursor: pack.digest,
333
- refs: pack.deltas
312
+ refs: pack.pointers.filter(ref => pack.deltaRefs.some(delta => delta.store === ref.store && delta.refId === ref.refId))
334
313
  });
335
314
  }
336
315
  function requireExactRun(store, taskId, runId) {
@@ -1,6 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
- import { chmodSync, existsSync, readFileSync, readdirSync } from "node:fs";
3
- import { dirname, join, resolve } from "node:path";
2
+ import { chmodSync, readFileSync } from "node:fs";
3
+ import { join, resolve } from "node:path";
4
4
  import { writeTextFileAtomically } from "../storage/durableFile.js";
5
5
  import { SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION, SESSION_CONTEXT_PROTOCOL, sessionManifestCompatibilityDigest } from "./sessionProtocolIdentity.js";
6
6
  export { SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION, SESSION_CONTEXT_PROTOCOL, sessionManifestCompatibilityDigest } from "./sessionProtocolIdentity.js";
@@ -13,27 +13,10 @@ export function withSessionContextPointer(text, environment) {
13
13
  return [
14
14
  `Read the Yui Session Manifest at ${manifest}.`,
15
15
  "Follow its referenced Role Skills and role profile. It gives the exact Context load command.",
16
- ...(environment.YUI_SESSION_CLI === undefined ? [] : [
17
- `Use this absolute CLI entry for Yui commands: ${environment.YUI_SESSION_CLI}`
18
- ]),
19
16
  "",
20
17
  text
21
18
  ].join("\n");
22
19
  }
23
- /**
24
- * A managed Session wrapper answers exactly one question: which installation
25
- * runs this command. It therefore carries only the resolved entry point, never
26
- * a package or build identity. Current Sessions may survive a compatible
27
- * installation relocation; this is not a cross-version runtime adapter.
28
- */
29
- function renderSessionCli(entryPoint) {
30
- return [
31
- "#!/bin/sh",
32
- `exec ${quoteShellWord(entryPoint.executable)} `
33
- + `${quoteShellWord(entryPoint.cliEntry)} \"$@\"`,
34
- ""
35
- ].join("\n");
36
- }
37
20
  /** Read back one immutable Session Manifest and verify its content digest. */
38
21
  export function readSessionBootstrapManifest(path) {
39
22
  const source = resolve(path);
@@ -67,8 +50,6 @@ export function readSessionBootstrapManifest(path) {
67
50
  && record.roleKind !== "leader"
68
51
  && record.roleKind !== "worker"
69
52
  && record.roleKind !== "reviewer")
70
- || record.controlPlane === null
71
- || typeof record.controlPlane !== "object"
72
53
  || !Array.isArray(record.skills)
73
54
  || record.roleProfileRef === null
74
55
  || typeof record.roleProfileRef !== "object"
@@ -80,8 +61,6 @@ export function readSessionBootstrapManifest(path) {
80
61
  if (owner.scope === "task" && typeof owner.taskId !== "string") {
81
62
  throw new Error("Task Session Manifest owner is invalid.");
82
63
  }
83
- const control = record.controlPlane;
84
- requireText(control.sessionCliPath, "Session Manifest CLI path");
85
64
  for (const skill of record.skills) {
86
65
  if (skill === null || typeof skill !== "object") {
87
66
  throw new Error("Session Manifest Skill entry is invalid.");
@@ -103,16 +82,6 @@ export function readSessionBootstrapManifest(path) {
103
82
  }
104
83
  export function materializeSessionBootstrap(input) {
105
84
  const home = resolve(input.yuiHome);
106
- // Provider command runners may rebuild PATH independently of the managed
107
- // process environment, so a bare `yui` could resolve to another install or
108
- // Home. The wrapper pins the resolved entry point instead. Package identity
109
- // stays out of it: current Session authority and the CLI/Controller protocol
110
- // boundary authorize the command.
111
- const sessionCliContent = renderSessionCli(input.entryPoint);
112
- const sessionCliDigest = digest(sessionCliContent);
113
- const sessionCliPath = resolve(join(home, "runtime", "session-cli", `yui-${sessionCliDigest}.sh`));
114
- writeImmutableText(sessionCliPath, sessionCliContent);
115
- chmodSync(sessionCliPath, 0o700);
116
85
  const roleProfile = {
117
86
  roleName: input.role.name,
118
87
  roleKind: input.roleKind,
@@ -134,9 +103,6 @@ export function materializeSessionBootstrap(input) {
134
103
  effectiveRevision: input.role.launchRevision,
135
104
  roleKind: input.roleKind,
136
105
  compatibilityDigest: sessionManifestCompatibilityDigest(input.role.name, input.roleKind, input.role),
137
- controlPlane: {
138
- sessionCliPath
139
- },
140
106
  skills: input.skills.map((skill) => Object.freeze({
141
107
  id: skill.id,
142
108
  path: skill.path,
@@ -151,10 +117,10 @@ export function materializeSessionBootstrap(input) {
151
117
  loadCommand: renderGlobalContextCommand(input.entryPoint, home, input.role.name)
152
118
  }
153
119
  : {
154
- loadCommand: "\"$YUI_SESSION_CLI\" task run context \"$YUI_TASK_ID/<run-id>\" --json",
155
- expandCommand: "\"$YUI_SESSION_CLI\" task run context expand \"$YUI_TASK_ID/<run-id>\" <ref-id> --store <store> --mode full --json",
120
+ loadCommand: `yui task run context ${quoteShellWord(`${input.owner.taskId}/<run-id>`)} --json`,
121
+ expandCommand: `yui task run context expand ${quoteShellWord(`${input.owner.taskId}/<run-id>`)} <ref-id> --store <store> --mode full --json`,
156
122
  ...(input.role.name === "leader" ? {
157
- currentCommand: "\"$YUI_SESSION_CLI\" task context \"$YUI_TASK_ID\" --json"
123
+ currentCommand: `yui task context ${quoteShellWord(input.owner.taskId)} --json`
158
124
  } : {})
159
125
  }
160
126
  };
@@ -164,7 +130,6 @@ export function materializeSessionBootstrap(input) {
164
130
  return Object.freeze({
165
131
  manifest,
166
132
  manifestPath,
167
- sessionCliPath,
168
133
  roleProfilePath
169
134
  });
170
135
  }
@@ -182,67 +147,6 @@ function renderGlobalContextCommand(entryPoint, home, roleName) {
182
147
  function quoteShellWord(value) {
183
148
  return `'${value.replaceAll("'", "'\"'\"'")}'`;
184
149
  }
185
- /**
186
- * Retargets current managed wrappers to the resolved installation entry point.
187
- * Only a valid Session Manifest may nominate a wrapper, and only the exact
188
- * two-argument shell form generated here is changed.
189
- */
190
- export function refreshManagedSessionCliWrappers(homeInput, entryPoint) {
191
- const home = resolve(homeInput);
192
- const currentSessionCli = renderSessionCli(entryPoint);
193
- const manifestDirectory = resolve(join(home, "runtime", "session-manifests"));
194
- const sessionCliDirectory = resolve(join(home, "runtime", "session-cli"));
195
- if (!existsSync(manifestDirectory)) {
196
- return Object.freeze({ refreshed: 0, current: 0, skipped: 0 });
197
- }
198
- const wrapperPaths = new Set();
199
- let skipped = 0;
200
- for (const name of readdirSync(manifestDirectory).filter((entry) => entry.endsWith(".json"))) {
201
- const manifestPath = resolve(join(manifestDirectory, name));
202
- try {
203
- const manifest = readSessionBootstrapManifest(manifestPath);
204
- if (manifestPath !== resolve(join(manifestDirectory, `${manifest.digest}.json`))) {
205
- skipped += 1;
206
- continue;
207
- }
208
- const wrapperPath = resolve(manifest.controlPlane.sessionCliPath);
209
- if (dirname(wrapperPath) !== sessionCliDirectory) {
210
- skipped += 1;
211
- continue;
212
- }
213
- wrapperPaths.add(wrapperPath);
214
- }
215
- catch {
216
- // An invalid Manifest cannot nominate a wrapper for mutation.
217
- skipped += 1;
218
- }
219
- }
220
- let refreshed = 0;
221
- let current = 0;
222
- for (const wrapperPath of wrapperPaths) {
223
- if (!existsSync(wrapperPath)) {
224
- skipped += 1;
225
- continue;
226
- }
227
- const content = readFileSync(wrapperPath, "utf8");
228
- if (content === currentSessionCli) {
229
- current += 1;
230
- continue;
231
- }
232
- if (!isManagedSessionCli(content)) {
233
- skipped += 1;
234
- continue;
235
- }
236
- writeTextFileAtomically(wrapperPath, currentSessionCli);
237
- chmodSync(wrapperPath, 0o700);
238
- refreshed += 1;
239
- }
240
- return Object.freeze({ refreshed, current, skipped });
241
- }
242
- /** Match exactly the two shell-quoted words emitted by renderSessionCli. */
243
- function isManagedSessionCli(content) {
244
- return /^#!\/bin\/sh\nexec '(?:[^'\n]|'"'"')*' '(?:[^'\n]|'"'"')*' "\$@"\n$/u.test(content);
245
- }
246
150
  function writeImmutableText(path, content) {
247
151
  writeTextFileAtomically(path, content);
248
152
  chmodSync(path, 0o600);