@zq-silk/yui 2.2.0 → 2.4.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 (206) hide show
  1. package/README.md +19 -3
  2. package/dist/artifacts/artifactCapability.js +39 -10
  3. package/dist/artifacts/imageMetadata.js +120 -0
  4. package/dist/artifacts/managedGit.js +7 -4
  5. package/dist/artifacts/taskArtifactRepository.js +6 -0
  6. package/dist/cli/commandCatalog.js +25 -11
  7. package/dist/cli/interactionPolicy.js +4 -0
  8. package/dist/cli/invocationAuthority.js +1 -1
  9. package/dist/commands/executionAuditCommands.js +5 -0
  10. package/dist/commands/globalRoleCommands.js +41 -3
  11. package/dist/commands/projectCommands.js +89 -94
  12. package/dist/commands/roleSkillValidation.js +4 -8
  13. package/dist/commands/taskCommands.js +30 -9
  14. package/dist/commands/taskIntegrationCommands.js +5 -16
  15. package/dist/commands/taskUsageCommand.js +38 -0
  16. package/dist/context/roleSessionContext.js +18 -2
  17. package/dist/context/runContextPack.js +5 -1
  18. package/dist/context/sessionBootstrapManifest.js +10 -1
  19. package/dist/context/skillCatalog.js +108 -0
  20. package/dist/context/skillPackage.js +161 -0
  21. package/dist/context/taskContext.js +3 -2
  22. package/dist/context/taskSearch.js +127 -0
  23. package/dist/controlPlaneCli.js +51 -39
  24. package/dist/controller/clientRuntime.js +3 -2
  25. package/dist/controller/fileSchedulerStoreAdapter.js +32 -6
  26. package/dist/controller/jobControl.js +2 -0
  27. package/dist/controller/nativeHostInput.js +37 -0
  28. package/dist/controller/runtime.js +93 -2
  29. package/dist/controller/runtimeEventInbox.js +2 -2
  30. package/dist/controller/runtimeEventProcessor.js +3 -3
  31. package/dist/doctor/doctor.js +10 -0
  32. package/dist/execution/reviewMainRun.js +1 -0
  33. package/dist/execution/workItemMainRun.js +1 -0
  34. package/dist/executor/codexConfigConflict.js +6 -4
  35. package/dist/executor/effectiveLaunch.js +24 -1
  36. package/dist/executor/fileRoleLaunchPlanner.js +13 -5
  37. package/dist/integration/gitIntegrationService.js +15 -79
  38. package/dist/integration/integrationSourceApplication.js +4 -2
  39. package/dist/job/httpPreview.js +61 -0
  40. package/dist/job/jobCleanupInspection.js +67 -0
  41. package/dist/job/jobRunner.js +12 -2
  42. package/dist/kernel/builtinCapabilities.js +3 -1
  43. package/dist/message/message.js +15 -1
  44. package/dist/output/rolePresentation.js +4 -1
  45. package/dist/repository/executionLaneGitSnapshot.js +2 -1
  46. package/dist/repository/gitCompatibility.js +62 -0
  47. package/dist/repository/gitWorkspace.js +86 -80
  48. package/dist/repository/gitWorktreeInventory.js +121 -0
  49. package/dist/repository/project.js +74 -25
  50. package/dist/repository/taskWorkspaceCoordinator.js +72 -145
  51. package/dist/repository/taskWorkspacePreparer.js +39 -43
  52. package/dist/repository/workspaceCleanupInspection.js +88 -7
  53. package/dist/resources/resourceDiscovery.js +2 -2
  54. package/dist/resources/resourceGc.js +7 -4
  55. package/dist/resources/resourceRegistrar.js +18 -11
  56. package/dist/runtime/agentEndpoint.js +8 -0
  57. package/dist/runtime/agentHost.js +170 -3
  58. package/dist/runtime/codexAppServerRuntime.js +1 -1
  59. package/dist/runtime/codexNativeAccess.js +175 -0
  60. package/dist/runtime/controlledCodexTui.js +116 -0
  61. package/dist/runtime/integrationRuntimeIsolation.js +21 -0
  62. package/dist/runtime/publicReply.js +23 -0
  63. package/dist/runtime/runtimeObservation.js +48 -0
  64. package/dist/runtime/structuredProviderHost.js +16 -7
  65. package/dist/runtime/taskCostMetrics.js +133 -0
  66. package/dist/runtime/taskRuntimeIsolation.js +91 -21
  67. package/dist/runtime/taskUsageMetrics.js +11 -2
  68. package/dist/runtime/taskUsageQuery.js +38 -0
  69. package/dist/scheduler/taskExecutionProjection.js +2 -0
  70. package/dist/scheduler/taskObservabilityProjection.js +9 -1
  71. package/dist/storage/homeLayout.js +13 -0
  72. package/dist/storage/sqliteSchema.js +44 -0
  73. package/dist/storage/sqliteStore.js +53 -0
  74. package/dist/storage/storageVersions.js +1 -1
  75. package/dist/storage/storeRpc.js +2 -0
  76. package/dist/storage/taskSearch.js +35 -0
  77. package/dist/storage/upgrade/projectKnowledgeMigration.js +52 -0
  78. package/dist/task/archiveDiagnostics.js +26 -8
  79. package/dist/task/archivePreflight.js +26 -9
  80. package/dist/task/leaderArchive.js +9 -15
  81. package/dist/task/ordinaryArchive.js +42 -0
  82. package/dist/tmux/tmuxManager.js +8 -8
  83. package/dist/web/assets/assetManifest.js +11 -51
  84. package/dist/web/assets/client/app/catalog.js +215 -0
  85. package/dist/web/assets/client/app/main.js +148 -0
  86. package/dist/web/assets/client/app/selection.js +139 -0
  87. package/dist/web/assets/client/app/settings.js +326 -0
  88. package/dist/web/assets/client/app/taskView.js +280 -0
  89. package/dist/web/assets/client/domain/context.js +96 -0
  90. package/dist/web/assets/client/domain/records.js +126 -0
  91. package/dist/web/assets/client/domain/runs.js +119 -0
  92. package/dist/web/assets/client/domain/taskForms.js +197 -0
  93. package/dist/web/assets/client/domain/vocab.js +69 -0
  94. package/dist/web/assets/client/domain/work.js +116 -0
  95. package/dist/web/assets/client/i18n/en.js +900 -0
  96. package/dist/web/assets/client/i18n/zh-CN.js +898 -0
  97. package/dist/web/assets/client/index.js +114 -0
  98. package/dist/web/assets/client/layout/resizer.js +37 -0
  99. package/dist/web/assets/client/layout/router.js +35 -0
  100. package/dist/web/assets/client/layout/shortcuts.js +45 -0
  101. package/dist/web/assets/client/layout/sizing.js +95 -0
  102. package/dist/web/assets/client/layout/workspace.js +247 -0
  103. package/dist/web/assets/client/{api.js → lib/api.js} +28 -4
  104. package/dist/web/assets/client/{dom.js → lib/dom.js} +24 -12
  105. package/dist/web/assets/client/{format.js → lib/format.js} +7 -0
  106. package/dist/web/assets/client/lib/i18n.js +54 -0
  107. package/dist/web/assets/client/lib/markdown.js +167 -0
  108. package/dist/web/assets/client/lib/prefs.js +28 -0
  109. package/dist/web/assets/client/lib/theme.js +68 -0
  110. package/dist/web/assets/client/ui/containers.js +101 -0
  111. package/dist/web/assets/client/ui/controls.js +51 -0
  112. package/dist/web/assets/client/ui/forms.js +48 -0
  113. package/dist/web/assets/client/ui/metrics.js +137 -0
  114. package/dist/web/assets/client/ui/primitives.js +92 -0
  115. package/dist/web/assets/client/ui/text.js +33 -0
  116. package/dist/web/assets/client/ui/toast.js +12 -0
  117. package/dist/web/assets/client/views/bodySearch.js +172 -0
  118. package/dist/web/assets/client/views/createTask.js +85 -0
  119. package/dist/web/assets/client/views/dock/conversation.js +612 -0
  120. package/dist/web/assets/client/views/dock/discussion.js +173 -0
  121. package/dist/web/assets/client/views/dock/preview.js +129 -0
  122. package/dist/web/assets/client/views/dock/terminal.js +199 -0
  123. package/dist/web/assets/client/views/globalInput.js +67 -0
  124. package/dist/web/assets/client/{overview.js → views/overview.js} +62 -56
  125. package/dist/web/assets/client/{sidebar.js → views/sidebar.js} +56 -62
  126. package/dist/web/assets/client/views/skills.js +152 -0
  127. package/dist/web/assets/client/views/task/actions.js +36 -0
  128. package/dist/web/assets/client/views/task/delivery.js +185 -0
  129. package/dist/web/assets/client/views/task/deliveryResults.js +159 -0
  130. package/dist/web/assets/client/views/task/files.js +272 -0
  131. package/dist/web/assets/client/views/task/observation.js +153 -0
  132. package/dist/web/assets/client/views/task/overview.js +161 -0
  133. package/dist/web/assets/client/views/task/page.js +131 -0
  134. package/dist/web/assets/client/views/task/records.js +163 -0
  135. package/dist/web/assets/client/views/task/runtime.js +130 -0
  136. package/dist/web/assets/client/views/task/timeline.js +190 -0
  137. package/dist/web/assets/shared/geometry.js +19 -0
  138. package/dist/web/assets/{icons.js → shared/icons.js} +4 -0
  139. package/dist/web/assets/shared/taskTabs.js +8 -0
  140. package/dist/web/assets/shared/themes.js +93 -0
  141. package/dist/web/assets/shell/dialogs.js +63 -0
  142. package/dist/web/assets/shell/dock.js +40 -0
  143. package/dist/web/assets/shell/index.js +40 -0
  144. package/dist/web/assets/shell/settings.js +34 -0
  145. package/dist/web/assets/shell/sidebar.js +45 -0
  146. package/dist/web/assets/styles/index.js +41 -0
  147. package/dist/web/assets/styles/{responsive.js → layout/responsive.js} +15 -13
  148. package/dist/web/assets/styles/{layout.js → layout/workspace.js} +17 -24
  149. package/dist/web/assets/styles/markdown.js +22 -3
  150. package/dist/web/assets/styles/tokens.js +10 -63
  151. package/dist/web/assets/styles/ui/controls.js +76 -0
  152. package/dist/web/assets/styles/ui/forms.js +46 -0
  153. package/dist/web/assets/styles/ui/surfaces.js +122 -0
  154. package/dist/web/assets/styles/views/delivery.js +29 -0
  155. package/dist/web/assets/styles/views/dock.js +71 -0
  156. package/dist/web/assets/styles/views/page.js +32 -0
  157. package/dist/web/assets/styles/views/records.js +70 -0
  158. package/dist/web/assets/styles/views/settings.js +26 -0
  159. package/dist/web/assets/styles/views/sidebar.js +74 -0
  160. package/dist/web/assets/styles/views/task.js +80 -0
  161. package/dist/web/assets/styles/views/terminal.js +38 -0
  162. package/dist/web/taskPreviews.js +205 -0
  163. package/dist/web/tmuxWebTerminal.js +16 -9
  164. package/dist/web/webConversation.js +373 -0
  165. package/dist/web/webMaterials.js +155 -0
  166. package/dist/web/webServer.js +306 -18
  167. package/dist/web/webSettings.js +331 -0
  168. package/dist/web/webSkills.js +86 -0
  169. package/dist/web/webSnapshot.js +40 -8
  170. package/dist/web/webTaskSurface.js +114 -9
  171. package/dist/workspace/gitChangeSetCapture.js +3 -1
  172. package/docs/managed-turn-and-session-runtime.md +9 -0
  173. package/docs/managed-turn-and-session-runtime.zh-CN.md +7 -0
  174. package/docs/observability/README.md +13 -3
  175. package/docs/observability/README.zh-CN.md +10 -3
  176. package/docs/release-workflow.md +53 -0
  177. package/docs/release-workflow.zh-CN.md +40 -0
  178. package/docs/storage-baseline.md +38 -1
  179. package/docs/storage-baseline.zh-CN.md +22 -0
  180. package/docs/task-delivery.md +36 -8
  181. package/docs/task-delivery.zh-CN.md +24 -7
  182. package/docs/task-discovery.md +28 -0
  183. package/docs/testing/verification-levels.md +2 -2
  184. package/i18n/README.zh-CN.md +4 -3
  185. package/package.json +1 -1
  186. package/skills/yui-leader/references/execution.md +2 -1
  187. package/skills/yui-leader/references/integration.md +20 -0
  188. package/skills/yui-operator/SKILL.md +4 -0
  189. package/skills/yui-reviewer/SKILL.md +3 -0
  190. package/skills/yui-runtime/SKILL.md +4 -0
  191. package/skills/yui-runtime/references/publication.md +20 -4
  192. package/skills/yui-worker/SKILL.md +3 -0
  193. package/dist/web/assets/client/app.js +0 -804
  194. package/dist/web/assets/client/components.js +0 -418
  195. package/dist/web/assets/client/detail.js +0 -333
  196. package/dist/web/assets/client/dock.js +0 -225
  197. package/dist/web/assets/client/evidence.js +0 -172
  198. package/dist/web/assets/client/forms.js +0 -230
  199. package/dist/web/assets/client/i18n.js +0 -1161
  200. package/dist/web/assets/client/markdown.js +0 -114
  201. package/dist/web/assets/client/records.js +0 -63
  202. package/dist/web/assets/client/sections.js +0 -313
  203. package/dist/web/assets/client/theme.js +0 -71
  204. package/dist/web/assets/shell.js +0 -161
  205. package/dist/web/assets/styles/components.js +0 -169
  206. package/dist/web/assets/styles/views.js +0 -292
@@ -0,0 +1,96 @@
1
+ export const CONTEXT_SCRIPT = String.raw `
2
+ // Context access. Every business fact keeps its Context reference; an
3
+ // expanded value is a current read, never a mutation of the snapshot.
4
+
5
+ export function entriesOf(core, store) {
6
+ return core.records.filter(function (entry) { return entry.ref.store === store; });
7
+ }
8
+
9
+ export function valuesOf(core, store) {
10
+ return entriesOf(core, store).filter(function (entry) { return !entry.omitted; }).map(function (entry) { return entry.value; });
11
+ }
12
+
13
+ // The family total from the same bounded read as its samples. The current
14
+ // view counts current records only: open work, active runs, open questions.
15
+ export function totalOf(core, store) {
16
+ const row = (core.collections || []).find(function (item) { return item.store === store; });
17
+ return row ? row.total : entriesOf(core, store).length;
18
+ }
19
+
20
+ // List and omitted-value summaries join known fields in a fixed order
21
+ // ("title: …; status: …"). Split them for display only; the exact record
22
+ // stays one read away.
23
+ const SUMMARY_KEYS = ["title", "scope", "expiresWhen", "version", "objective", "question", "summary", "body", "status", "type"];
24
+ export function summaryFields(summary) {
25
+ const fields = {};
26
+ if (!summary) return fields;
27
+ const pattern = new RegExp("(?:^|; )(" + SUMMARY_KEYS.join("|") + "): ", "g");
28
+ const marks = [];
29
+ let match;
30
+ while ((match = pattern.exec(summary))) {
31
+ const rank = SUMMARY_KEYS.indexOf(match[1]);
32
+ if (marks.length && rank <= marks[marks.length - 1].rank) continue;
33
+ marks.push({ key: match[1], rank: rank, start: match.index, from: match.index + match[0].length });
34
+ }
35
+ marks.forEach(function (mark, index) {
36
+ fields[mark.key] = summary.slice(mark.from, index + 1 < marks.length ? marks[index + 1].start : summary.length);
37
+ });
38
+ return fields;
39
+ }
40
+
41
+ // --- Exact reads -------------------------------------------------------------
42
+ // One inspect per reference digest, held in the Task's view state so a
43
+ // refresh with an unchanged reference never reads again. A changed digest is
44
+ // a new read.
45
+ export function exactCache(view) {
46
+ view.exact = view.exact || {};
47
+ return view.exact;
48
+ }
49
+
50
+ export function readExact(entry, taskId, actions, cache) {
51
+ const key = entry.ref.store + "/" + entry.ref.refId;
52
+ const hit = cache[key];
53
+ if (hit && hit.digest === entry.ref.digest) return hit.promise;
54
+ const promise = actions.inspect(taskId, entry.ref);
55
+ const slot = { digest: entry.ref.digest, promise: promise, result: null };
56
+ cache[key] = slot;
57
+ promise.then(function (result) { slot.result = result; }, function () { if (cache[key] === slot) delete cache[key]; });
58
+ return promise;
59
+ }
60
+
61
+ // The settled exact read for this exact reference, or null.
62
+ export function cachedExact(cache, entry) {
63
+ const hit = cache[entry.ref.store + "/" + entry.ref.refId];
64
+ return hit && hit.digest === entry.ref.digest && hit.result ? hit.result : null;
65
+ }
66
+
67
+ // --- Paged family discovery --------------------------------------------------
68
+ // Pages of one Context family for the current Context cursor. A continuation
69
+ // never mixes versions: after the cursor changes, the first page is re-read.
70
+ export function familyState(view, store) {
71
+ view.families = view.families || {};
72
+ if (!view.families[store]) view.families[store] = { items: [], total: null, nextCursor: null, pages: 0, key: null, error: null, pending: null };
73
+ return view.families[store];
74
+ }
75
+
76
+ export function loadFamily(view, taskId, store, key, actions, options) {
77
+ const state = familyState(view, store);
78
+ const more = !!(options && options.more);
79
+ if (state.pending) return state.pending;
80
+ if (more ? !state.nextCursor : state.key === key && state.pages && !state.error) return Promise.resolve(state);
81
+ state.pending = actions.list(taskId, store, { limit: (options && options.limit) || 40, cursor: more ? state.nextCursor : null })
82
+ .then(function (page) {
83
+ if (!more) { state.items = []; state.pages = 0; }
84
+ state.items = state.items.concat(page.items);
85
+ state.total = page.total;
86
+ state.nextCursor = page.nextCursor;
87
+ state.pages += 1;
88
+ state.key = more ? state.key : key;
89
+ state.error = null;
90
+ state.errorKey = null;
91
+ return state;
92
+ }, function (error) { state.error = error.message; state.errorKey = more ? state.key : key; return state; })
93
+ .then(function (result) { state.pending = null; return result; });
94
+ return state.pending;
95
+ }
96
+ `;
@@ -0,0 +1,126 @@
1
+ export const RECORDS_SCRIPT = String.raw `
2
+ // Record rows and cards. A row keeps its Context reference: an omitted value
3
+ // is shown as the honest summary until its exact read arrives, and reading
4
+ // never acknowledges or accepts anything.
5
+ import { h, icon } from "/assets/js/lib/dom.js";
6
+ import { button, jsonBlock, mono, note } from "/assets/js/ui/primitives.js";
7
+ import { rawDisclosure } from "/assets/js/ui/containers.js";
8
+ import { richText } from "/assets/js/ui/text.js";
9
+ import { cachedExact, readExact, summaryFields } from "/assets/js/domain/context.js";
10
+
11
+ // A row drawn from its exact value: synchronously when cached, otherwise the
12
+ // summary card until the read returns and replaces it in place.
13
+ export function exactRow(entry, taskId, t, actions, cache, render) {
14
+ if (!entry.omitted) return render(entry.value, null);
15
+ const hit = cachedExact(cache, entry);
16
+ if (hit) return render(hit.value, hit);
17
+ const placeholder = recordCard(entry, taskId, t, actions);
18
+ placeholder.dataset.reading = "true";
19
+ readExact(entry, taskId, actions, cache).then(function (result) {
20
+ if (placeholder.isConnected) placeholder.replaceWith(render(result.value, result));
21
+ }, function () { placeholder.dataset.reading = "false"; });
22
+ return placeholder;
23
+ }
24
+
25
+ // A closed row drawn from a list summary; its exact value is read on first
26
+ // open. Open rows are remembered in view state and reopen after a redraw.
27
+ export function lazyRow(item, taskId, t, actions, options) {
28
+ const opts = options || {};
29
+ const key = item.ref.store + ":" + item.ref.refId;
30
+ const element = h("details.lazy-row");
31
+ if (item.status) element.dataset.status = item.status;
32
+ const body = h("div.lazy-body");
33
+ element.append(h("summary.lazy-head", null, icon("chevron", "disclosure-chevron"), opts.head), body);
34
+ let state = "idle";
35
+ function load() {
36
+ if (state !== "idle") return;
37
+ state = "reading";
38
+ element.dataset.reading = "true";
39
+ body.replaceChildren(h("p.faint.small", null, t("record.reading")));
40
+ readExact({ ref: item.ref }, taskId, actions, opts.cache || {}).then(function (result) {
41
+ state = "done";
42
+ body.replaceChildren();
43
+ body.append(h("div.lazy-content", null, opts.render ? opts.render(result.value, result) : null), rawDisclosure(t, result.value));
44
+ }, function (error) {
45
+ state = "idle";
46
+ body.replaceChildren(note(t("record.changed") + " · " + error.message, "bad"));
47
+ }).then(function () { element.dataset.reading = "false"; });
48
+ }
49
+ element.addEventListener("toggle", function () {
50
+ if (opts.openRows) { if (element.open) opts.openRows[key] = true; else delete opts.openRows[key]; }
51
+ if (element.open) load();
52
+ });
53
+ if (opts.openRows && opts.openRows[key]) element.open = true;
54
+ return element;
55
+ }
56
+
57
+ // A generic card for any record: omitted values say so and can be read by
58
+ // reference.
59
+ export function recordCard(entry, taskId, t, actions, options) {
60
+ const opts = options || {};
61
+ const element = h("article.record" + (opts.compact ? ".is-compact" : ""));
62
+ element.append(h("header.record-head", null,
63
+ h("span.record-store", null, t("store." + entry.ref.store, entry.ref.store)),
64
+ mono(entry.ref.refId),
65
+ h("span.spacer"),
66
+ h("span.faint.small", { title: t("record.revision") }, String(entry.ref.revision).slice(0, 24))));
67
+ element.append.apply(element, !entry.omitted && entry.value ? valueLines(entry, t) : summaryLines(entry, t));
68
+ element.append(recordReader(entry, taskId, t, actions));
69
+ return element;
70
+ }
71
+
72
+ function valueLines(entry, t) {
73
+ const value = entry.value;
74
+ const lines = [];
75
+ const text = value.content || value.summary || value.body || value.objective || value.title || value.leaderSummary;
76
+ if (text) lines.push(richText(null, text, t, { threshold: 360 }));
77
+ if (entry.ref.store === "task-decision" && value.rationale) lines.push(richText(t("record.rationale"), value.rationale, t, { muted: true }));
78
+ const facts = [value.status, value.roleName].filter(Boolean);
79
+ if (facts.length) lines.push(h("p.faint.small", null, facts.join(" · ")));
80
+ if (entry.ref.store === "job") {
81
+ lines.push(note(t("job." + value.status + ".note", t("job.terminal.note")), value.status === "unknown-needs-attention" ? "warn" : ""));
82
+ }
83
+ return lines;
84
+ }
85
+
86
+ function summaryLines(entry, t) {
87
+ const fields = summaryFields(entry.summary);
88
+ const lead = fields.title || fields.question || fields.objective;
89
+ const text = fields.summary || fields.body || (lead === fields.objective ? null : fields.objective);
90
+ const lines = [];
91
+ if (lead) lines.push(h("p.record-lead", null, lead));
92
+ if (text) lines.push(h("p.faint", null, text));
93
+ if (!lead && !text) lines.push(h("p.faint", null, entry.summary || t("record.omitted")));
94
+ return lines;
95
+ }
96
+
97
+ // Shared source access for cards, history and discussion, including records
98
+ // whose compact value is present but does not display every durable field.
99
+ // With a renderer, the exact value is drawn richly and the raw record stays
100
+ // available behind a disclosure.
101
+ export function recordReader(entry, taskId, t, actions, render) {
102
+ const element = h("div.stack");
103
+ const expand = button(t("record.read"), { icon: "eye", variant: "link" });
104
+ expand.addEventListener("click", async function () {
105
+ expand.disabled = true;
106
+ element.dataset.reading = "true";
107
+ try {
108
+ const result = await actions.inspect(taskId, entry.ref);
109
+ const rich = render ? render(result.value, result) : null;
110
+ if (rich) element.append(rich, rawDisclosure(t, result.value));
111
+ else {
112
+ if (result.result && result.result.output) element.append(richText(t("record.output"), result.result.output, t));
113
+ if (result.result && result.result.diagnostic) element.append(richText(t("record.diagnostic"), result.result.diagnostic, t));
114
+ element.append(jsonBlock(result.value));
115
+ if (result.execution) element.append(jsonBlock(result.execution));
116
+ }
117
+ expand.remove();
118
+ } catch {
119
+ expand.disabled = false;
120
+ expand.replaceChildren(icon("refresh"), h("span", null, t("record.changed")));
121
+ } finally { element.dataset.reading = "false"; }
122
+ });
123
+ element.append(h("div.record-actions", null, expand));
124
+ return element;
125
+ }
126
+ `;
@@ -0,0 +1,119 @@
1
+ export const RUNS_SCRIPT = String.raw `
2
+ // Execution records, review rounds and Roles.
3
+ import { h } from "/assets/js/lib/dom.js";
4
+ import { formatDateTime } from "/assets/js/lib/format.js";
5
+ import { button, chip, dot, kv, mono, note, timeTag } from "/assets/js/ui/primitives.js";
6
+ import { richText } from "/assets/js/ui/text.js";
7
+ import { agentChips, label, statusBadge, tone } from "/assets/js/domain/vocab.js";
8
+
9
+ export function runCard(run, t, locale) {
10
+ const element = h("article.run-card");
11
+ element.dataset.status = run.status;
12
+ element.append(h("header.run-head", null,
13
+ dot(tone("run", run.status)),
14
+ h("span.run-role", null, run.roleName),
15
+ mono(run.id),
16
+ run.purpose ? chip(label(t, "run.purpose", run.purpose)) : null,
17
+ run.workItemId ? chip(run.workItemId) : null,
18
+ h("span.spacer"),
19
+ statusBadge(t, "run", "run", run.status)), runBody(run, t, locale));
20
+ return element;
21
+ }
22
+
23
+ // What one execution record says: delivery, instruction, report, failure.
24
+ export function runBody(run, t, locale) {
25
+ const parts = [];
26
+ const input = run.inputs && run.inputs.length ? run.inputs[0].input : null;
27
+ parts.push(h("div.meta-line", null,
28
+ h("span", null, t("run.delivery") + ": " + label(t, "run.delivery", (run.execution && run.execution.delivery) || "unobserved")),
29
+ run.mode ? h("span", null, label(t, "mode", run.mode)) : null,
30
+ timeTag((run.result && run.result.completedAt) || run.updatedAt, locale, t),
31
+ agentChips(run.effective || (run.agentId ? run : null))));
32
+ const directive = input && (input.directive || input.action);
33
+ if (directive) parts.push(richText(t("run.instruction"), directive, t, { threshold: 320 }));
34
+ if (run.result && run.result.output) parts.push(richText(t("run.output"), run.result.output, t, { threshold: 320, className: "callout" }));
35
+ if (run.result && run.result.diagnostic) parts.push(richText(t("run.failure"), run.result.diagnostic, t, { threshold: 320, className: "callout.tone-bad" }));
36
+ if (run.executionGroupId) {
37
+ parts.push(h("p.faint.mono-line", null, t("run.lineage") + " " + run.executionGroupId + (run.executionLaneId ? "/" + run.executionLaneId : "")));
38
+ }
39
+ parts.push(skillPackageDetails(run.effective && run.effective.skillPackages, t));
40
+ return parts.filter(Boolean);
41
+ }
42
+
43
+ function skillPackageDetails(packages, t) {
44
+ const details = h("details", null, h("summary", null, t("role.skills") + " · " + (packages ? packages.length : t("skills.unrecorded"))));
45
+ if (!packages) {
46
+ details.append(note(t("skills.legacyNote")));
47
+ return details;
48
+ }
49
+ packages.forEach(function (skill) {
50
+ details.append(h("div.record", null, h("strong", null, skill.id),
51
+ h("p.small.mono-line", null, skill.digest),
52
+ h("p.small", null, skill.source.kind + " · " + skill.fileCount + " " + t("skills.files") + " · " + skill.byteSize + " B"),
53
+ h("p.small.mono-line", null, t("skills.source") + ": " + skill.source.path),
54
+ h("p.small.mono-line", null, t("skills.snapshot") + ": " + skill.path),
55
+ h("p.small.mono-line", null, t("skills.inventory") + ": " + skill.manifestPath)));
56
+ });
57
+ return details;
58
+ }
59
+
60
+ export function reviewCard(round, t, locale) {
61
+ const element = h("article.review-card");
62
+ element.append(h("header.run-head", null,
63
+ dot(tone("review", round.status)),
64
+ h("span.run-role", null, round.reviewerRoleName || t("review.reviewer")),
65
+ mono(round.id),
66
+ round.scope ? chip(label(t, "review.scope", round.scope)) : null,
67
+ h("span.spacer"),
68
+ statusBadge(t, "review", "review", round.status)));
69
+ element.append(h("div.meta-line", null,
70
+ round.workItemId ? mono(round.workItemId + (round.candidateId ? " · " + round.candidateId : "")) : null,
71
+ round.taskCandidate && round.taskCandidate.projects ? round.taskCandidate.projects.map(function (project) {
72
+ return mono(project.projectId + " @ " + String(project.commit).slice(0, 12));
73
+ }) : null,
74
+ round.reviewBaseCommit ? h("span", null, t("review.base") + " ", mono(String(round.reviewBaseCommit).slice(0, 12))) : null,
75
+ round.reviewerRunId ? h("span", null, t("review.run") + " ", mono(round.reviewerRunId)) : null,
76
+ timeTag(round.createdAt, locale, t)));
77
+ if (round.failure) element.append(note(round.failure.kind + ": " + round.failure.message, "bad"));
78
+ return element;
79
+ }
80
+
81
+ export function roleCard(role, t, locale, options) {
82
+ const opts = options || {};
83
+ const element = h("article.role-card");
84
+ const binding = role.agentBindings && role.agentBindings[role.activeAgentId];
85
+ element.append(h("header.role-head", null,
86
+ h("span.avatar", { "aria-hidden": "true" }, String(role.name || "?").slice(0, 1).toUpperCase()),
87
+ h("div.role-title", null, h("strong", null, role.name),
88
+ h("span.faint", null, role.activeAgentId || "")),
89
+ opts.status ? statusBadge(t, "role", "role", opts.status) : null));
90
+ if (binding) element.append(agentChips({
91
+ adapterId: binding.adapterId, component: binding.component,
92
+ model: binding.config && binding.config.model, effort: binding.config && binding.config.effort
93
+ }));
94
+ if (role.description) element.append(richText(null, role.description, t, { muted: true, threshold: 280 }));
95
+ element.append(kv(roleFacts(role, t, locale, opts)));
96
+ if (opts.runtimeRole && opts.runtimeRole.runtimeSession) {
97
+ element.append(skillPackageDetails(opts.runtimeRole.runtimeSession.skillPackages, t));
98
+ }
99
+ if (opts.onOpen) {
100
+ element.append(h("div.role-actions", null, button(t("role.openSession"), {
101
+ icon: "terminal", variant: "ghost", onClick: function () { opts.onOpen(role.name); }
102
+ })));
103
+ }
104
+ return element;
105
+ }
106
+
107
+ function roleFacts(role, t, locale, opts) {
108
+ const session = opts.runtimeRole && opts.runtimeRole.runtimeSession;
109
+ const rows = [];
110
+ rows.push([t("role.session"), session && session.nativeSessionId ? mono(session.nativeSessionId) : t("role.noSession")]);
111
+ if (role.launchRevision !== undefined) rows.push([t("role.desired"), "r" + role.launchRevision + (opts.launchDrift ? " · " + t("role.drift") : "")]);
112
+ if (role.defaultAccess !== undefined) rows.push([t("role.access"), role.defaultAccess]);
113
+ if (opts.retry) rows.push([t("role.providerRetry"), label(t, "retry", opts.retry.status) + " · "
114
+ + opts.retry.attempts + "/" + opts.retry.limit
115
+ + (opts.retry.status === "waiting" && opts.retry.nextEligibleAt ? " · " + formatDateTime(opts.retry.nextEligibleAt, locale) : "")]);
116
+ if (role.skills && role.skills.length) rows.push([t("role.skills"), h("span.chip-row", null, role.skills.map(function (s) { return chip(s); }))]);
117
+ return rows;
118
+ }
119
+ `;
@@ -0,0 +1,197 @@
1
+ export const TASK_FORMS_SCRIPT = String.raw `
2
+ // Task write forms: the discussion composer, queue / steer / interrupt and
3
+ // the title editor. Submission plumbing (requestId, unsent flag, receipts,
4
+ // never replaying an unknown outcome) lives in ui/forms submitWrite().
5
+ import { h, icon, clear } from "/assets/js/lib/dom.js";
6
+ import { fill } from "/assets/js/lib/format.js";
7
+ import { button } from "/assets/js/ui/primitives.js";
8
+ import { segmented } from "/assets/js/ui/controls.js";
9
+ import { field, receiptLine, setReceipt, submitWrite } from "/assets/js/ui/forms.js";
10
+
11
+ function isOpen(task) {
12
+ return ["active", "draft"].includes(task.status);
13
+ }
14
+
15
+ // --- Discussion composer -----------------------------------------------------
16
+ export function messageComposer(task, t, actions) {
17
+ let intent = "discuss";
18
+ const form = h("form.composer");
19
+ const message = h("textarea.composer-input", {
20
+ required: true, maxLength: 8000, rows: 3, placeholder: t("composer.placeholder"),
21
+ "aria-label": t("composer.label")
22
+ });
23
+ const open = isOpen(task);
24
+ const send = h("button.btn.btn-primary.composer-send", { type: "submit", disabled: !open, title: t("composer.sendHint") },
25
+ icon("send"), h("span", null, t("composer.send")));
26
+ const intents = segmented("intent", ["discuss", "record", "develop"].map(function (value) {
27
+ return { value: value, label: t("intent." + value), title: t("intent." + value + ".hint") };
28
+ }), intent, function (value) { intent = value; });
29
+ const receipt = receiptLine(open ? t("receipt.notSubmitted") : t("composer.closed"));
30
+ const facets = h("div.facets");
31
+ form.append(h("div.composer-box", null, message, h("div.composer-bar", null, intents, h("span.spacer"), send)), receipt, facets);
32
+ form.addEventListener("input", function () { form.dataset.unsent = message.value ? "true" : "false"; });
33
+ message.addEventListener("keydown", function (event) {
34
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) { event.preventDefault(); form.requestSubmit(); }
35
+ });
36
+ form.addEventListener("submit", function (event) {
37
+ event.preventDefault();
38
+ if (send.disabled || !message.value.trim()) return;
39
+ clear(facets);
40
+ // requestId is the submission key: the same key retried returns the
41
+ // original Message and routing, never a second submission.
42
+ submitWrite({
43
+ control: send, receipt: receipt, form: form, t: t,
44
+ send: function (requestId) { return actions.sendMessage(task.id, message.value, requestId, intent); },
45
+ saved: function (result) {
46
+ setReceipt(receipt, t("receipt.saved") + " · " + result.record.id, "ok");
47
+ renderSubmissionFacets(facets, result.submission, t);
48
+ message.value = "";
49
+ },
50
+ rejectedKey: "receipt.notSubmittedBecause", unknownKey: "receipt.unknownMessage", afterWrite: actions.afterWrite
51
+ });
52
+ });
53
+ return form;
54
+ }
55
+
56
+ // Each submission facet is reported on its own line, never collapsed into a
57
+ // single "started". A missing facet block is a contract violation and is
58
+ // reported as such rather than reinterpreted.
59
+ export function renderSubmissionFacets(container, submission, t) {
60
+ clear(container);
61
+ if (!submission) { container.append(h("p.faint.small", null, t("facet.missing"))); return; }
62
+ const rows = [
63
+ ["facet.phase", "phase." + submission.phase, submission.phase],
64
+ ["facet.planning", "planningFacet." + submission.planning, submission.planning],
65
+ ["facet.activation", "activation." + submission.activation, submission.activation],
66
+ ["facet.delivery", "deliveryFacet." + submission.delivery, submission.delivery]
67
+ ];
68
+ container.append(h("dl.kv.kv-compact", null, rows.map(function (row) {
69
+ return [h("dt", null, t(row[0])), h("dd", null, t(row[1], row[2]))];
70
+ })));
71
+ const step = submission.nextStep;
72
+ const text = step ? nextStepText(step, t) : "";
73
+ if (text) container.append(h("p.next-step", { dataset: { nextStep: step.kind } }, icon("flag", "icon-sm"), h("span", null, text)));
74
+ }
75
+
76
+ function nextStepText(step, t) {
77
+ if (step.kind === "activate-manually") return fill(t("next.activateManually"), { task: step.taskId });
78
+ if (step.kind === "start-execution") return fill(t("next.startExecution"), { task: step.taskId });
79
+ if (step.kind === "await-pending-activation") return fill(t("next.awaitActivation"), { ref: step.activationRef });
80
+ if (step.kind === "resolve-failed-activation") return fill(t("next.resolveFailed"), { failure: step.failure, ref: step.activationRef });
81
+ return "";
82
+ }
83
+
84
+ // --- Queue / steer / interrupt ----------------------------------------------
85
+ // The same three application-layer actions the CLI drives. Steer and
86
+ // interrupt name an exact current Turn; a mismatch is a visible failure
87
+ // receipt, never a silent downgrade to a queue.
88
+ export function controlForm(task, t, actions, roleNames) {
89
+ let kind = "queue";
90
+ const form = h("form.stack");
91
+ const inputs = controlInputs(task, t, roleNames);
92
+ const fields = controlFields(inputs, t);
93
+ const submit = button(t("control.submit"), { type: "submit", variant: "primary", icon: "send", disabled: !isOpen(task) });
94
+ const receipt = receiptLine(t("receipt.notSubmitted"));
95
+ const apply = function () { applyControlKind(kind, inputs, fields); };
96
+ const actionsSeg = segmented("action", ["queue", "steer", "interrupt"].map(function (value) {
97
+ return { value: value, label: t("control." + value), title: t("control." + value + ".hint") };
98
+ }), kind, function (value) { kind = value; apply(); });
99
+ form.append(h("div.field", null, h("span", null, t("control.action")), actionsSeg),
100
+ h("div.field-row", null, fields.role, fields.workItem), inputs.roles, fields.body, fields.expected, fields.then,
101
+ h("div.form-actions", null, submit), receipt);
102
+ apply();
103
+ form.addEventListener("input", function () { form.dataset.unsent = "true"; });
104
+ form.addEventListener("submit", function (event) {
105
+ event.preventDefault();
106
+ if (submit.disabled) return;
107
+ submitWrite({
108
+ control: submit, receipt: receipt, form: form, t: t,
109
+ send: function (requestId) { return actions.control(task.id, controlPayload(kind, inputs, requestId)); },
110
+ // The receipt states exactly what settled; none of these states implies
111
+ // the Provider executed the input.
112
+ saved: function (result) { setReceipt(receipt, controlReceipt(result, t), "ok"); },
113
+ rejectedKey: "receipt.notSubmittedBecause", unknownKey: "receipt.unknownControl", afterWrite: actions.afterWrite
114
+ });
115
+ });
116
+ return form;
117
+ }
118
+
119
+ function controlInputs(task, t, roleNames) {
120
+ return {
121
+ role: h("input", { list: "control-roles-" + task.id, placeholder: t("control.rolePlaceholder") }),
122
+ roles: h("datalist", { id: "control-roles-" + task.id }, (roleNames || []).map(function (name) { return h("option", { value: name }); })),
123
+ body: h("textarea", { maxLength: 8000, rows: 3 }),
124
+ expected: h("input.mono", { placeholder: t("control.expectedPlaceholder") }),
125
+ workItem: h("input.mono"),
126
+ then: h("input.mono", { placeholder: t("control.thenPlaceholder") })
127
+ };
128
+ }
129
+
130
+ function controlFields(inputs, t) {
131
+ return {
132
+ role: field(t("control.role"), inputs.role),
133
+ body: field(t("control.message"), inputs.body),
134
+ expected: field(t("control.expectedTurn"), inputs.expected, t("control.expectedHint")),
135
+ workItem: field(t("control.workItem"), inputs.workItem),
136
+ then: field(t("control.then"), inputs.then)
137
+ };
138
+ }
139
+
140
+ function applyControlKind(kind, inputs, fields) {
141
+ fields.body.hidden = kind === "interrupt";
142
+ fields.expected.hidden = kind === "queue";
143
+ fields.then.hidden = kind !== "interrupt";
144
+ fields.workItem.hidden = kind === "interrupt";
145
+ inputs.role.required = kind !== "queue";
146
+ inputs.body.required = kind !== "interrupt";
147
+ inputs.expected.required = kind !== "queue";
148
+ }
149
+
150
+ function controlPayload(kind, inputs, requestId) {
151
+ const payload = { action: kind, requestId: requestId };
152
+ const role = inputs.role.value.trim();
153
+ if (kind === "interrupt") {
154
+ payload.role = role;
155
+ payload.expectedTarget = inputs.expected.value.trim();
156
+ if (inputs.then.value.trim()) payload.thenMessage = inputs.then.value.trim();
157
+ return payload;
158
+ }
159
+ payload.body = inputs.body.value;
160
+ if (kind === "steer") { payload.to = role; payload.expectedTarget = inputs.expected.value.trim(); }
161
+ else if (role) payload.to = role;
162
+ if (inputs.workItem.value.trim()) payload.workItem = inputs.workItem.value.trim();
163
+ return payload;
164
+ }
165
+
166
+ function controlReceipt(result, t) {
167
+ const settled = result.steer || result.interrupt || result.delivery || { state: result.disposition };
168
+ return t("receipt.receipt") + " " + result.action + " · " + settled.state
169
+ + (settled.outcome ? " · " + settled.outcome : "") + (settled.detail ? " · " + settled.detail : "")
170
+ + (result.record ? " · " + result.record.id : "");
171
+ }
172
+
173
+ // --- Title -------------------------------------------------------------------
174
+ export function titleForm(task, t, actions) {
175
+ const form = h("form.inline-form");
176
+ const archived = task.status === "archived";
177
+ const input = h("input", { value: task.title, required: true, maxLength: 500, disabled: archived, "aria-label": t("details.title") });
178
+ const save = button(t("actions.save"), { type: "submit", variant: "primary", disabled: archived });
179
+ const receipt = receiptLine(t("receipt.notSubmitted"));
180
+ form.append(h("div.inline-row", null, input, save), receipt);
181
+ form.addEventListener("input", function () { form.dataset.unsent = input.value !== task.title ? "true" : "false"; });
182
+ form.addEventListener("submit", function (event) {
183
+ event.preventDefault();
184
+ if (save.disabled) return;
185
+ submitWrite({
186
+ control: save, receipt: receipt, form: form, t: t,
187
+ send: function (requestId) { return actions.updateTask(task.id, { title: input.value }, requestId); },
188
+ saved: function (result) {
189
+ setReceipt(receipt, t("receipt.savedRevision") + " " + result.revision, "ok");
190
+ input.value = result.record.title;
191
+ },
192
+ rejectedKey: "receipt.notSavedBecause", unknownKey: "receipt.unknownSave", afterWrite: actions.afterWrite
193
+ });
194
+ });
195
+ return form;
196
+ }
197
+ `;
@@ -0,0 +1,69 @@
1
+ import { AGENT_ADAPTER_CATALOG } from "../../../../agent/adapterCatalog.js";
2
+ import { AGENT_EXECUTION_COMPONENT_CATALOG } from "../../../../agent/executionComponents.js";
3
+ export const VOCAB_SCRIPT = String.raw `
4
+ // Domain vocabulary: translated labels, semantic tones and the status badges
5
+ // built from them. Unknown values stay visible verbatim, never hidden or
6
+ // guessed.
7
+ import { h } from "/assets/js/lib/dom.js";
8
+ import { badge, chip } from "/assets/js/ui/primitives.js";
9
+
10
+ // Generated from the server catalogs, not a second product registry.
11
+ const adapterLabels = ${JSON.stringify(Object.fromEntries(AGENT_ADAPTER_CATALOG.map(({ id, label }) => [id, label])))};
12
+ const componentLabels = ${JSON.stringify(Object.fromEntries(AGENT_EXECUTION_COMPONENT_CATALOG.map(({ id, label }) => [id, label])))};
13
+ export function agentLabel(component, adapterId) {
14
+ return component ? componentLabels[component] || component : adapterLabels[adapterId] || adapterId;
15
+ }
16
+
17
+ export function label(t, prefix, value) {
18
+ if (value === undefined || value === null || value === "") return "—";
19
+ return t(prefix + "." + value, String(value).replace(/[-_]/g, " "));
20
+ }
21
+
22
+ const TONES = {
23
+ task: { draft: "warn", active: "info", completed: "ok", cancelled: "idle", archived: "idle" },
24
+ exec: {
25
+ "needs-leader-action": "accent", "waiting-on-agents": "info", "waiting-user": "warn",
26
+ recovering: "warn", attention: "bad", "progressing-with-attention": "warn", blocked: "bad",
27
+ conflicted: "warn", working: "info", completed: "ok", retired: "idle", cancelled: "idle",
28
+ accepted: "ok", open: "info", archived: "idle"
29
+ },
30
+ run: { active: "info", completed: "ok", failed: "bad" },
31
+ work: { open: "info", accepted: "ok", retired: "idle" },
32
+ review: { pending: "idle", running: "info", completed: "ok", failed: "bad" },
33
+ job: { queued: "idle", running: "info", succeeded: "ok", failed: "bad", "timed-out": "bad",
34
+ cancelled: "idle", "unknown-needs-attention": "warn" },
35
+ session: { active: "ok", waiting: "warn", quiet: "idle", diagnostic: "bad", unknown: "idle",
36
+ stopped: "bad", idle: "idle", background: "warn" },
37
+ role: { running: "ok", waiting: "warn", idle: "idle", unknown: "idle", failed: "bad", exited: "idle", detached: "idle" },
38
+ decision: { active: "ok", superseded: "idle" },
39
+ input: { open: "warn", answered: "ok", cancelled: "idle", "auto-resolved": "ok" },
40
+ integration: { running: "info", validating: "info", committed: "ok", conflicted: "warn", blocked: "bad",
41
+ failed: "bad", superseded: "idle" }
42
+ };
43
+ export function tone(kind, value) {
44
+ return (TONES[kind] && TONES[kind][value]) || "idle";
45
+ }
46
+
47
+ export function statusBadge(t, kind, prefix, value) {
48
+ const element = badge(label(t, prefix, value), tone(kind, value), { dot: true });
49
+ element.dataset.status = value;
50
+ return element;
51
+ }
52
+
53
+ // Native Session groups in display order, and one badge per non-empty group.
54
+ export const SESSION_GROUPS = ["active", "waiting", "background", "quiet", "diagnostic", "stopped", "unknown", "idle"];
55
+ export function sessionBadges(counts, t) {
56
+ return SESSION_GROUPS.filter(function (group) { return counts && counts[group]; }).map(function (group) {
57
+ return badge(counts[group] + " " + t("session." + group), tone("session", group), { dot: true });
58
+ });
59
+ }
60
+
61
+ export function agentChips(agent) {
62
+ if (!agent) return null;
63
+ const chips = [];
64
+ if (agent.adapterId || agent.component) chips.push(chip(agentLabel(agent.component, agent.adapterId), "chip-strong"));
65
+ if (agent.model) chips.push(chip(agent.model));
66
+ if (agent.effort) chips.push(chip(agent.effort));
67
+ return chips.length ? h("span.chip-row", null, chips) : null;
68
+ }
69
+ `;