@zq-silk/yui 0.2.0 → 0.4.2

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 (208) hide show
  1. package/ARCHITECTURE.md +603 -133
  2. package/README.md +806 -31
  3. package/dist/agent/agent.js +2 -1
  4. package/dist/agent/argumentPolicy.js +3 -1
  5. package/dist/agent/launchEnvironment.js +106 -0
  6. package/dist/agent/managedRuntimeEnvironment.js +34 -0
  7. package/dist/brief/taskBrief.js +11 -1
  8. package/dist/cli/agentConfigurationPicker.js +287 -0
  9. package/dist/cli/commandCatalog.js +488 -60
  10. package/dist/cli/completion.js +146 -22
  11. package/dist/cli/helpRenderer.js +3 -1
  12. package/dist/cli/interactionCandidates.js +53 -15
  13. package/dist/cli/interactionPolicy.js +267 -30
  14. package/dist/cli/interactiveSelection.js +6 -2
  15. package/dist/cli/invocationRouter.js +5 -1
  16. package/dist/cli/operatorWizard.js +87 -0
  17. package/dist/cli/roleOptionCatalog.js +1 -0
  18. package/dist/cli/roleWizard.js +185 -21
  19. package/dist/cli/updateCommand.js +62 -19
  20. package/dist/cli/updateOrchestrator.js +539 -0
  21. package/dist/cli/updatePorts.js +1119 -0
  22. package/dist/cli/upgradeCommand.js +112 -0
  23. package/dist/cli.js +1420 -86
  24. package/dist/commands/agentCommands.js +146 -3
  25. package/dist/commands/configCommands.js +126 -0
  26. package/dist/commands/controllerCommands.js +365 -0
  27. package/dist/commands/globalRoleCommands.js +168 -126
  28. package/dist/commands/jobCommands.js +18 -8
  29. package/dist/commands/operatorCommands.js +159 -9
  30. package/dist/commands/profileCommands.js +203 -0
  31. package/dist/commands/projectCommands.js +650 -0
  32. package/dist/commands/roleConfiguration.js +85 -24
  33. package/dist/commands/roleRuntimeGuard.js +12 -0
  34. package/dist/commands/roleSkillValidation.js +47 -0
  35. package/dist/commands/taskActor.js +127 -0
  36. package/dist/commands/taskCommands.js +4201 -313
  37. package/dist/commands/taskCompletionGate.js +131 -0
  38. package/dist/commands/taskContextCommand.js +244 -30
  39. package/dist/commands/taskInputCommands.js +177 -59
  40. package/dist/commands/taskIntegrationCommands.js +303 -0
  41. package/dist/commands/taskOverviewCommand.js +363 -0
  42. package/dist/commands/taskRoleRuntimeStatus.js +125 -19
  43. package/dist/commands/textInput.js +15 -0
  44. package/dist/completion/completionInstaller.js +26 -22
  45. package/dist/config/yuiConfig.js +4 -3
  46. package/dist/context/dispatchContext.js +90 -38
  47. package/dist/context/roleSessionContext.js +119 -0
  48. package/dist/controller/claudeLifecycleHook.js +203 -0
  49. package/dist/controller/clientRuntime.js +408 -56
  50. package/dist/controller/codexLifecycleHook.js +108 -0
  51. package/dist/controller/controller.js +1089 -32
  52. package/dist/controller/domainIdentity.js +505 -0
  53. package/dist/controller/ephemeralResourceReaper.js +131 -0
  54. package/dist/controller/fileSchedulerStoreAdapter.js +2153 -103
  55. package/dist/controller/providerHookRunFence.js +127 -0
  56. package/dist/controller/resourceCleanupLinux.js +286 -0
  57. package/dist/controller/resourceInventory.js +531 -0
  58. package/dist/controller/resourceInventoryLinux.js +610 -0
  59. package/dist/controller/runtime.js +629 -10
  60. package/dist/controller/runtimeEventInbox.js +564 -0
  61. package/dist/controller/runtimeEventProcessor.js +248 -0
  62. package/dist/controller/runtimeLaunchCoordinator.js +477 -0
  63. package/dist/controller/sessionNotify.js +121 -78
  64. package/dist/coordination/deadlineScheduler.js +15 -0
  65. package/dist/coordination/mailboxScheduler.js +108 -0
  66. package/dist/coordination/workMailbox.js +329 -0
  67. package/dist/coordination/workMailboxQueue.js +86 -0
  68. package/dist/core/controllerClient.js +19 -5
  69. package/dist/core/controllerEndpoint.js +37 -0
  70. package/dist/core/controllerServer.js +218 -10
  71. package/dist/core/protocol.js +6 -2
  72. package/dist/decision/decision.js +2 -1
  73. package/dist/doctor/doctor.js +681 -32
  74. package/dist/domain/validation.js +53 -0
  75. package/dist/errors/cliError.js +5 -3
  76. package/dist/event/taskEvent.js +7 -3
  77. package/dist/execution/codexThreadNaming.js +160 -0
  78. package/dist/execution/executionGroup.js +579 -0
  79. package/dist/executor/agentAdapter.js +255 -40
  80. package/dist/executor/agentConfigurationCatalog.js +326 -0
  81. package/dist/executor/agentConfigurationProbe.js +506 -0
  82. package/dist/executor/agentExecutor.js +625 -10
  83. package/dist/executor/codexConfigConflict.js +290 -0
  84. package/dist/executor/effectiveLaunch.js +340 -0
  85. package/dist/executor/executorRegistry.js +238 -36
  86. package/dist/executor/fileRoleLaunchPlanner.js +550 -40
  87. package/dist/executor/turnCompletion.js +126 -0
  88. package/dist/input/inputRequest.js +30 -9
  89. package/dist/integration/changeSet.js +36 -0
  90. package/dist/integration/checkResult.js +24 -0
  91. package/dist/integration/gitIntegrationService.js +695 -0
  92. package/dist/integration/integrationAttempt.js +142 -0
  93. package/dist/interaction/operatorPresentation.js +96 -0
  94. package/dist/lifecycle/canonicalLifecycleEvent.js +342 -0
  95. package/dist/lifecycle/exactRunTerminalization.js +572 -0
  96. package/dist/lifecycle/providerLifecycleMapping.js +190 -0
  97. package/dist/lifecycle/taskRoleSessionReset.js +124 -0
  98. package/dist/message/message.js +23 -7
  99. package/dist/milestone/milestone.js +2 -1
  100. package/dist/operator/operatorSessionHistory.js +124 -0
  101. package/dist/output/agentConfigurationPresentation.js +43 -0
  102. package/dist/output/rolePresentation.js +34 -10
  103. package/dist/output/terminal.js +8 -0
  104. package/dist/output/timePresentation.js +55 -0
  105. package/dist/profile/agentProfile.js +128 -0
  106. package/dist/repository/gitWorkspace.js +578 -24
  107. package/dist/repository/project.js +213 -0
  108. package/dist/repository/taskWorkspaceCoordinator.js +392 -0
  109. package/dist/repository/taskWorkspacePreparer.js +1688 -191
  110. package/dist/review/reviewConfig.js +11 -0
  111. package/dist/review/reviewRound.js +399 -0
  112. package/dist/review/taskFinalReviewContract.js +90 -0
  113. package/dist/role/role.js +124 -23
  114. package/dist/run/agentRun.js +155 -12
  115. package/dist/run/runIdentity.js +82 -0
  116. package/dist/runtime/exactControlPlane.js +472 -0
  117. package/dist/runtime/index.js +8 -0
  118. package/dist/runtime/lifecycleReservation.js +38 -0
  119. package/dist/runtime/ports.js +11 -0
  120. package/dist/runtime/preallocatedNativeSession.js +13 -0
  121. package/dist/runtime/promptEnvelope.js +30 -0
  122. package/dist/runtime/runtimeBinding.js +31 -0
  123. package/dist/runtime/runtimeOwner.js +14 -0
  124. package/dist/runtime/sessionLaunchRequest.js +62 -0
  125. package/dist/runtime/sessionTitle.js +54 -0
  126. package/dist/runtime/taskRuntimeIsolation.js +643 -0
  127. package/dist/runtime/tmuxAdapters.js +315 -0
  128. package/dist/runtime/turnCompletion.js +3 -0
  129. package/dist/runtime/validation.js +23 -0
  130. package/dist/scheduler/activeRoleRunDelivery.js +342 -32
  131. package/dist/scheduler/activeTaskProgress.js +63 -0
  132. package/dist/scheduler/leaderFailure.js +2 -1
  133. package/dist/scheduler/leaderWakeupProcessor.js +307 -66
  134. package/dist/scheduler/operatorInputNotificationProcessor.js +109 -46
  135. package/dist/scheduler/operatorNotification.js +44 -2
  136. package/dist/scheduler/ports.js +28 -1
  137. package/dist/scheduler/roleRunLiveness.js +131 -25
  138. package/dist/scheduler/roleRunStall.js +951 -0
  139. package/dist/scheduler/taskExecutionProjection.js +544 -0
  140. package/dist/scheduler/wakeupQueue.js +3 -0
  141. package/dist/setup/setupCommand.js +302 -52
  142. package/dist/storage/compatibleTaskStore.js +102 -0
  143. package/dist/storage/migration/baseline.js +78 -0
  144. package/dist/storage/migration/classifier.js +51 -0
  145. package/dist/storage/migration/compatibleCodec.js +53 -0
  146. package/dist/storage/migration/engine.js +147 -0
  147. package/dist/storage/migration/index.js +33 -0
  148. package/dist/storage/migration/planner.js +154 -0
  149. package/dist/storage/migration/productionRegistry.js +486 -0
  150. package/dist/storage/migration/registry.js +169 -0
  151. package/dist/storage/migration/report.js +54 -0
  152. package/dist/storage/migration/types.js +31 -0
  153. package/dist/storage/storageSchema.js +147 -123
  154. package/dist/storage/storageVersions.js +11 -0
  155. package/dist/storage/taskStore.js +1793 -197
  156. package/dist/storage/upgrade/homeClassification.js +156 -0
  157. package/dist/storage/upgrade/homeMigrationTarget.js +595 -0
  158. package/dist/storage/upgrade/offlineUpgradeInventory.js +315 -0
  159. package/dist/storage/upgrade/productionMigrationRegistry.js +6 -0
  160. package/dist/storage/upgrade/recordVersionScan.js +176 -0
  161. package/dist/storage/upgrade/recordVersions.js +159 -0
  162. package/dist/storage/upgrade/switchProgress.js +80 -0
  163. package/dist/storage/upgrade/upgradeOrchestrator.js +948 -0
  164. package/dist/storage/upgrade/upgradeReceipt.js +161 -0
  165. package/dist/storage/upgradeCoordination.js +186 -0
  166. package/dist/storage/upgradeFence.js +366 -0
  167. package/dist/task/task.js +132 -26
  168. package/dist/task/taskRecordReference.js +66 -0
  169. package/dist/tmux/commandExecutor.js +75 -2
  170. package/dist/tmux/tmuxManager.js +747 -49
  171. package/dist/version.js +23 -0
  172. package/dist/web/assets/assetManifest.js +62 -0
  173. package/dist/web/assets/client/app.js +631 -0
  174. package/dist/web/assets/client/components.js +605 -0
  175. package/dist/web/assets/client/dom.js +14 -0
  176. package/dist/web/assets/client/format.js +28 -0
  177. package/dist/web/assets/client/i18n.js +494 -0
  178. package/dist/web/assets/client/markdown.js +114 -0
  179. package/dist/web/assets/client/theme.js +32 -0
  180. package/dist/web/assets/client/view.js +458 -0
  181. package/dist/web/assets/fontData.js +12 -0
  182. package/dist/web/assets/fonts.js +12 -0
  183. package/dist/web/assets/shell.js +114 -0
  184. package/dist/web/assets/styles/cards.js +135 -0
  185. package/dist/web/assets/styles/layout.js +47 -0
  186. package/dist/web/assets/styles/markdown.js +29 -0
  187. package/dist/web/assets/styles/responsive.js +39 -0
  188. package/dist/web/assets/styles/tokens.js +101 -0
  189. package/dist/web/assets/styles/widgets.js +147 -0
  190. package/dist/web/tmuxWebTerminal.js +158 -0
  191. package/dist/web/webServer.js +463 -0
  192. package/dist/web/webSnapshot.js +148 -0
  193. package/dist/workItem/workItem.js +642 -23
  194. package/dist/workspace/gitChangeSetCapture.js +86 -0
  195. package/dist/workspace/workItemChangeSetManager.js +445 -0
  196. package/dist/worktree/managedWorkspace.js +202 -0
  197. package/docs/task-local-identity.md +62 -0
  198. package/i18n/README.zh-CN.md +406 -31
  199. package/package.json +10 -2
  200. package/skills/yui-leader/SKILL.md +601 -39
  201. package/skills/yui-operator/SKILL.md +255 -34
  202. package/skills/yui-reviewer/SKILL.md +57 -0
  203. package/skills/yui-worker/SKILL.md +214 -17
  204. package/dist/commands/repositoryCommands.js +0 -86
  205. package/dist/operator/operatorContext.js +0 -66
  206. package/dist/repository/repository.js +0 -55
  207. package/dist/scheduler/archivedTaskRuntime.js +0 -12
  208. package/dist/worktree/roleWorkspace.js +0 -62
@@ -0,0 +1,605 @@
1
+ export const COMPONENTS_SCRIPT = `
2
+ // Reusable widgets and record cards. Everything here is a pure builder:
3
+ // data + i18n in, DOM out. Page composition lives in view.js.
4
+ import { node } from "/assets/js/dom.js";
5
+ import { formatDateTime, relativeTime } from "/assets/js/format.js";
6
+ import { escapeHtml, inlineMarkdown, renderMarkdown } from "/assets/js/markdown.js";
7
+
8
+ export function translatedStatus(t, prefix, status) {
9
+ return t(prefix + "." + status);
10
+ }
11
+
12
+ // Mirrors src/agent/adapterCatalog.ts to avoid a runtime import loop.
13
+ const adapterLabels = { codex: "Codex", claude: "Claude" };
14
+ function adapterLabel(adapterId) {
15
+ return adapterLabels[adapterId] || adapterId;
16
+ }
17
+
18
+ // --- Small widgets -----------------------------------------------------------
19
+ export function metaItem(label, value) {
20
+ const item = node("span", "detail-meta-item");
21
+ item.append(node("small", "", label), node("span", "", value));
22
+ return item;
23
+ }
24
+
25
+ export function pathMetaItem(label, value) {
26
+ const item = node("span", "detail-meta-item");
27
+ const path = node("span", "meta-path", value);
28
+ path.title = value;
29
+ item.append(node("small", "", label), path);
30
+ return item;
31
+ }
32
+
33
+ export function chip(text, extraClass) {
34
+ return node("span", "chip" + (extraClass ? " " + extraClass : ""), text);
35
+ }
36
+
37
+ export function agentBadge(agent) {
38
+ if (!agent) return null;
39
+ const badge = node("span", "agent-badge");
40
+ if (agent.adapterId) badge.append(chip(adapterLabel(agent.adapterId), "is-adapter"));
41
+ if (agent.model) badge.append(chip(agent.model));
42
+ if (agent.effort) badge.append(chip(agent.effort));
43
+ return badge.childNodes.length ? badge : null;
44
+ }
45
+
46
+ export function chipRow(label, values, activeValue) {
47
+ const list = (values || []).filter(function (value) { return value !== undefined && value !== null && value !== ""; });
48
+ if (!list.length) return null;
49
+ const block = node("div", "record-block");
50
+ block.append(node("small", "", label));
51
+ const row = node("div", "chip-row");
52
+ list.forEach(function (value) {
53
+ row.append(chip(String(value), activeValue !== undefined && value === activeValue ? "is-active" : ""));
54
+ });
55
+ block.append(row);
56
+ return block;
57
+ }
58
+
59
+ export function pill(t, namespace, status) {
60
+ const element = node("span", "pill", translatedStatus(t, namespace, status));
61
+ element.dataset.status = status;
62
+ return element;
63
+ }
64
+
65
+ export function statusDot(status) {
66
+ const dot = node("span", "status-dot " + status);
67
+ dot.setAttribute("aria-hidden", "true");
68
+ return dot;
69
+ }
70
+
71
+ export function emptyRow(t, key) {
72
+ return node("div", "row is-empty", t(key || "empty.none"));
73
+ }
74
+
75
+ export function sectionHead(label, options) {
76
+ const head = node("div", "section-head");
77
+ head.append(node("h3", "", label));
78
+ if (options && options.count !== undefined) head.append(node("span", "section-count", String(options.count)));
79
+ if (options && options.kicker) head.append(node("span", "section-kicker", options.kicker));
80
+ if (options && options.right) head.append(node("span", "section-label", options.right));
81
+ return head;
82
+ }
83
+
84
+ export function anchorSection(id, head, body) {
85
+ const section = node("section", "detail-section anchor");
86
+ section.id = id;
87
+ section.append(head, body);
88
+ return section;
89
+ }
90
+
91
+ // --- Progressive disclosure --------------------------------------------------
92
+ // Re-render is cheap and happens whenever the underlying data changes, so all
93
+ // disclosure state (collapsed blocks, visible pages) is deliberately
94
+ // deterministic per render: long content starts collapsed and expanding is a
95
+ // pure local toggle.
96
+ function shouldCollapse(text, threshold) {
97
+ const limit = threshold || 700;
98
+ const value = String(text);
99
+ if (value.length > limit) return true;
100
+ let lines = 0;
101
+ for (let i = 0; i < value.length; i += 1) {
102
+ if (value[i] === "\\n") lines += 1;
103
+ }
104
+ return lines > 12;
105
+ }
106
+
107
+ function collapseToggle(block, t) {
108
+ block.classList.add("is-collapsible", "is-collapsed");
109
+ const toggle = node("button", "md-toggle", t("actions.showMore"));
110
+ toggle.type = "button";
111
+ toggle.addEventListener("click", function () {
112
+ const collapsed = block.classList.toggle("is-collapsed");
113
+ toggle.textContent = collapsed ? t("actions.showMore") : t("actions.showLess");
114
+ });
115
+ block.append(toggle);
116
+ }
117
+
118
+ // Rich text block: small label + Markdown body, auto-collapsed when long.
119
+ export function richText(label, text, t, options) {
120
+ if (!text) return null;
121
+ const baseClass = (options && options.className) || "record-block";
122
+ const extra = options && options.extraClass ? " " + options.extraClass : "";
123
+ const block = node("div", baseClass + extra);
124
+ if (label) block.append(node("small", "", label));
125
+ const body = node("div", "md" + (options && options.muted ? " muted" : ""));
126
+ body.innerHTML = renderMarkdown(text);
127
+ block.append(body);
128
+ if (shouldCollapse(text, options && options.threshold)) collapseToggle(block, t);
129
+ return block;
130
+ }
131
+
132
+ // Renders the first page of a long list plus a button that reveals the rest
133
+ // in place, one page at a time.
134
+ export function pagedList(container, items, pageSize, renderItem, t) {
135
+ let shown = 0;
136
+ let more = null;
137
+ function renderChunk() {
138
+ const chunk = items.slice(shown, shown + pageSize);
139
+ chunk.forEach(function (item) {
140
+ container.insertBefore(renderItem(item), more);
141
+ });
142
+ shown += chunk.length;
143
+ if (more) {
144
+ const remaining = items.length - shown;
145
+ if (remaining <= 0) {
146
+ more.remove();
147
+ more = null;
148
+ } else {
149
+ more.textContent = t("actions.showRemaining").replace("{count}", String(remaining));
150
+ }
151
+ }
152
+ }
153
+ if (items.length > pageSize) {
154
+ more = node("button", "show-more", "");
155
+ more.type = "button";
156
+ more.addEventListener("click", renderChunk);
157
+ container.append(more);
158
+ }
159
+ renderChunk();
160
+ }
161
+
162
+ export function criteriaList(label, items, t) {
163
+ const list = (items || []).filter(Boolean);
164
+ if (!list.length) return null;
165
+ const block = node("div", "record-block is-wide criteria-block");
166
+ block.append(node("small", "", label));
167
+ const listElement = node("ul", "criteria-list");
168
+ list.forEach(function (item) {
169
+ const li = node("li", "");
170
+ li.innerHTML = inlineMarkdown(escapeHtml(item));
171
+ listElement.append(li);
172
+ });
173
+ block.append(listElement);
174
+ if (list.length > 6) collapseToggle(block, t);
175
+ return block;
176
+ }
177
+
178
+ // --- Authors ------------------------------------------------------------------
179
+ function messageAuthor(message, t) {
180
+ return message.author.type === "role"
181
+ ? message.author.roleName
182
+ : t("author." + message.author.type);
183
+ }
184
+
185
+ function authorName(t, who) {
186
+ const key = "author." + who;
187
+ const label = t(key);
188
+ return label === key ? who : label;
189
+ }
190
+
191
+ // --- Input (attention) cards ---------------------------------------------------
192
+ function answerActions(input, actions, t) {
193
+ const answers = node("div", "input-actions");
194
+ if (input.choices && input.choices.length) {
195
+ input.choices.forEach(function (choice) {
196
+ const button = node("button", "input-answer", choice.label);
197
+ button.type = "button";
198
+ button.addEventListener("click", function () {
199
+ if (actions.answerInput) actions.answerInput(input, { choiceKey: choice.key });
200
+ });
201
+ answers.append(button);
202
+ });
203
+ } else {
204
+ const form = node("form", "input-form");
205
+ const field = node("input", "");
206
+ field.type = "text";
207
+ field.required = true;
208
+ field.placeholder = t("input.freeText");
209
+ const submit = node("button", "input-answer", t("actions.answer"));
210
+ submit.type = "submit";
211
+ form.append(field, submit);
212
+ form.addEventListener("submit", function (event) {
213
+ event.preventDefault();
214
+ const text = field.value.trim();
215
+ if (text && actions.answerInput) actions.answerInput(input, { text: text });
216
+ });
217
+ answers.append(form);
218
+ }
219
+ return answers;
220
+ }
221
+
222
+ export function inputCard(input, _options, t, locale, actions) {
223
+ const card = node("article", "input-card");
224
+ const top = node("div", "input-card-top");
225
+ top.append(node("small", "", t("detail.openInput")));
226
+ const question = node("p", "input-question", input.question);
227
+ top.append(question);
228
+ const context = node("div", "input-context");
229
+ if (input.policy) {
230
+ if (input.policy.kind === "recommended") {
231
+ context.append(pill(t, "input", "recommended"));
232
+ if (input.policy.timeoutAt) {
233
+ context.append(node("span", "", t("input.timeoutAt") + " · " + relativeTime(input.policy.timeoutAt, locale, t)));
234
+ }
235
+ } else {
236
+ context.append(pill(t, "input", "required"));
237
+ }
238
+ }
239
+ context.append(node("time", "", formatDateTime(input.createdAt, locale)));
240
+ top.append(context);
241
+ if (input.blockedRefs && input.blockedRefs.length) {
242
+ const blocked = input.blockedRefs.map(function (ref) { return ref.type + "·" + ref.id; }).join(" ");
243
+ top.append(node("span", "input-blocked", blocked));
244
+ }
245
+ card.append(top);
246
+ card.append(answerActions(input, actions, t));
247
+ return card;
248
+ }
249
+
250
+ // --- Conclusion band ------------------------------------------------------------
251
+ export function conclusionMeta(task, t, locale, kind) {
252
+ const meta = node("div", "conclusion-meta");
253
+ const actor = kind === "archived" ? task.archivedBy
254
+ : kind === "retired" ? task.retiredBy
255
+ : task.completedBy;
256
+ const at = kind === "archived" ? task.archivedAt
257
+ : kind === "retired" ? task.retiredAt
258
+ : task.completedAt;
259
+ const label = kind === "archived" ? t("detail.archivedBy")
260
+ : kind === "retired" ? t("detail.retiredBy")
261
+ : t("detail.completedBy");
262
+ if (actor) meta.append(node("span", "", label + " · " + authorName(t, actor)));
263
+ if (at) meta.append(node("time", "", formatDateTime(at, locale)));
264
+ return meta;
265
+ }
266
+
267
+ // --- Record cards -----------------------------------------------------------------
268
+ // WorkItems that no longer need action render collapsed; everything actionable
269
+ // (pending, running, awaiting acceptance, failed) stays expanded.
270
+ const WORK_ITEM_OPEN_STATUSES = ["pending", "running", "awaiting_acceptance", "failed"];
271
+
272
+ export function workItemCard(item, titles, t, locale, actions, taskId) {
273
+ const collapsible = WORK_ITEM_OPEN_STATUSES.indexOf(item.status) === -1;
274
+ const card = node(collapsible ? "details" : "article", "record-card work-item-card");
275
+ const head = node(collapsible ? "summary" : "div", "record-head");
276
+ const titleRow = node("div", "record-title-row");
277
+ titleRow.append(statusDot(item.status), node("strong", "record-title", item.title));
278
+ const pills = node("div", "record-pills");
279
+ pills.append(pill(t, "work", item.status));
280
+ if (item.workspaceDisposition) pills.append(pill(t, "disposition", item.workspaceDisposition));
281
+ head.append(titleRow, pills);
282
+ card.append(head);
283
+
284
+ const body = node("div", "work-item-body");
285
+ const meta = node("div", "record-meta");
286
+ if (item.assignee) meta.append(node("span", "meta-name", item.assignee));
287
+ meta.append(node("span", "mono", item.id));
288
+ meta.append(node("time", "", formatDateTime(item.updatedAt, locale)));
289
+ body.append(meta);
290
+
291
+ if (item.objective && item.objective !== item.title) {
292
+ body.append(richText(t("detail.objective"), item.objective, t));
293
+ }
294
+ if (item.acceptance && item.acceptance.length) {
295
+ body.append(criteriaList(t("detail.acceptance"), item.acceptance, t));
296
+ }
297
+
298
+ // Short chip rows (dependencies, writable projects) sit side by side; long
299
+ // text blocks above and below stay full-width so the card reads top-to-bottom
300
+ // like an issue rather than a mismatched column grid.
301
+ const chipCols = node("div", "work-item-chips");
302
+ if (item.dependsOn && item.dependsOn.length) {
303
+ chipCols.append(chipRow(t("detail.dependsOn"), item.dependsOn.map(function (id) { return titles[id] || id; })));
304
+ }
305
+ if (item.writeProjectIds && item.writeProjectIds.length) {
306
+ chipCols.append(chipRow(t("detail.writeProjects"), item.writeProjectIds));
307
+ }
308
+ if (chipCols.childNodes.length) body.append(chipCols);
309
+
310
+ if (item.outcome) {
311
+ body.append(richText(t("detail.outcome"), item.outcome, t, { extraClass: "outcome-callout", muted: true }));
312
+ }
313
+ card.append(body);
314
+ return card;
315
+ }
316
+
317
+ export function runCard(run, t, locale) {
318
+ const card = node("article", "execute-card");
319
+ card.dataset.status = run.status;
320
+
321
+ const idRow = node("div", "execute-id");
322
+ idRow.append(statusDot(run.status));
323
+ idRow.append(node("span", "role", run.roleName));
324
+ idRow.append(node("span", "", run.id));
325
+ if (run.workItemId) idRow.append(node("span", "", t("detail.workItem") + " · " + run.workItemId));
326
+ idRow.append(node("time", "", formatDateTime(run.updatedAt, locale)));
327
+ card.append(idRow);
328
+
329
+ card.append(richText(t("detail.instruction"), run.input, t, { className: "execute-io", threshold: 320 }));
330
+ if (run.summary) {
331
+ card.append(richText(t("detail.outcome"), run.summary, t, { className: "execute-io outcome", threshold: 320 }));
332
+ }
333
+
334
+ const foot = node("div", "execute-foot");
335
+ const tags = node("div", "execute-tags");
336
+ tags.append(chip(t("mode." + run.mode)));
337
+ const deliveryKey = run.deliveredAt
338
+ ? "delivery.delivered"
339
+ : run.pushedAt
340
+ ? "delivery.pushed"
341
+ : "delivery.pending";
342
+ tags.append(chip(t(deliveryKey)));
343
+ const badge = run.effective ? agentBadge(run.effective) : (run.agentId ? agentBadge(run) : null);
344
+ if (badge) tags.append(badge);
345
+ foot.append(tags);
346
+ foot.append(pill(t, "run", run.status));
347
+ card.append(foot);
348
+
349
+ if (run.effective) {
350
+ const eff = node("div", "record-meta");
351
+ eff.append(node("span", "", t("detail.effective") + " · r" + run.effective.sourceDesiredRevision));
352
+ eff.append(node("span", "", t("detail.profileIntent") + " · " + run.effective.profileAccess));
353
+ eff.append(node("span", "", t("detail.permission") + " · " + run.effective.permission.strategy));
354
+ card.append(eff);
355
+ }
356
+
357
+ return card;
358
+ }
359
+
360
+ export function reviewCard(round, t, locale) {
361
+ const card = node("article", "record-card");
362
+ const head = node("div", "record-head");
363
+ const titleRow = node("div", "record-title-row");
364
+ titleRow.append(node("strong", "record-title", round.id));
365
+ head.append(titleRow);
366
+ head.append(pill(t, "review", round.status));
367
+ card.append(head);
368
+ const meta = node("div", "record-meta");
369
+ meta.append(node("span", "meta-name", round.reviewerRoleName));
370
+ meta.append(node("span", "mono", round.workItemId + " · " + round.candidateId));
371
+ if (round.createdAt) meta.append(node("time", "", formatDateTime(round.createdAt, locale)));
372
+ meta.append(node("span", "", t("detail.reviewBase") + " · " + round.reviewBaseCommit));
373
+ if (round.workspace && round.workspace.root) {
374
+ meta.append(pathMetaItem(t("detail.workspace"), round.workspace.root));
375
+ }
376
+ if (round.evidenceCommit) {
377
+ meta.append(node("span", "", t("detail.evidence") + " · " + round.evidenceCommit));
378
+ }
379
+ card.append(meta);
380
+ if (round.checks && round.checks.length) {
381
+ card.append(richText(t("detail.checks"), round.checks.map(function (check) {
382
+ return check.name + "=" + check.outcome;
383
+ }).join(", "), t));
384
+ }
385
+ if (round.summary) card.append(richText(null, round.summary, t));
386
+ return card;
387
+ }
388
+
389
+ export function roleCard(role, task, t, locale, actions) {
390
+ const card = node("article", "record-card");
391
+ const head = node("div", "record-head");
392
+ head.append(node("strong", "record-title", role.name));
393
+ const headRight = node("div", "record-pills");
394
+ headRight.append(pill(t, "role", role.status));
395
+ const open = node("button", "record-open", "");
396
+ open.type = "button";
397
+ open.append(node("span", "", t("actions.openRun")), node("span", "arrow", "→"));
398
+ open.addEventListener("click", function () {
399
+ if (actions.openTerminal) actions.openTerminal({ scope: "task", taskId: task.id, roleName: role.name });
400
+ });
401
+ headRight.append(open);
402
+ head.append(headRight);
403
+ card.append(head);
404
+
405
+ const meta = node("div", "record-meta");
406
+ meta.append(node("span", "meta-name", role.activeAgentId));
407
+ const activeBinding = role.agentBindings && role.agentBindings[role.activeAgentId];
408
+ if (activeBinding) {
409
+ const badge = agentBadge({
410
+ adapterId: activeBinding.adapterId,
411
+ model: activeBinding.config && activeBinding.config.model,
412
+ effort: activeBinding.config && activeBinding.config.effort
413
+ });
414
+ if (badge) meta.append(badge);
415
+ }
416
+ if (role.launchRevision !== undefined) {
417
+ meta.append(node("span", "", t("detail.desired") + " · r" + role.launchRevision));
418
+ }
419
+ if (role.defaultAccess !== undefined) {
420
+ meta.append(node("span", "", t("detail.profileIntent") + " · " + role.defaultAccess));
421
+ }
422
+ if (role.updatedAt) meta.append(node("time", "", formatDateTime(role.updatedAt, locale)));
423
+ card.append(meta);
424
+
425
+ if (role.effectiveLaunch) {
426
+ const eff = node("div", "record-meta");
427
+ eff.append(node("span", "", t("detail.effectiveAgent") + " · " + role.effectiveLaunch.agentId));
428
+ const effBadge = agentBadge(role.effectiveLaunch);
429
+ if (effBadge) eff.append(effBadge);
430
+ if (role.effectiveLaunch.sourceDesiredRevision !== undefined) {
431
+ eff.append(node("span", "", t("detail.effective") + " · r" + role.effectiveLaunch.sourceDesiredRevision));
432
+ }
433
+ if (role.effectiveLaunch.profileAccess !== undefined) {
434
+ eff.append(node("span", "", t("detail.profileIntent") + " · " + role.effectiveLaunch.profileAccess));
435
+ }
436
+ if (role.effectiveLaunch.permission && role.effectiveLaunch.permission.strategy !== undefined) {
437
+ eff.append(node("span", "", t("detail.permission") + " · " + role.effectiveLaunch.permission.strategy));
438
+ }
439
+ eff.append(node("span", "", role.launchDrift ? t("launch.drift") : t("launch.current")));
440
+ card.append(eff);
441
+ }
442
+
443
+ if (role.description) card.append(richText(null, role.description, t, { muted: true }));
444
+
445
+ const cols = node("div", "record-cols");
446
+ const bindingIds = role.agentBindings ? Object.keys(role.agentBindings) : [];
447
+ if (bindingIds.length > 1) {
448
+ const bindings = bindingIds.map(function (id) {
449
+ const binding = role.agentBindings[id];
450
+ const model = binding.config && binding.config.model;
451
+ return adapterLabel(binding.adapterId) + (model ? " · " + model : "");
452
+ });
453
+ const activeBindingLabel = activeBinding
454
+ ? adapterLabel(activeBinding.adapterId) + (activeBinding.config && activeBinding.config.model ? " · " + activeBinding.config.model : "")
455
+ : undefined;
456
+ cols.append(chipRow(t("detail.agents"), bindings, activeBindingLabel));
457
+ }
458
+ if (role.skills && role.skills.length) cols.append(chipRow(t("detail.skills"), role.skills));
459
+ if (role.responsibilities && role.responsibilities.length) cols.append(criteriaList(t("detail.responsibilities"), role.responsibilities, t));
460
+ if (role.constraints && role.constraints.length) cols.append(criteriaList(t("detail.constraints"), role.constraints, t));
461
+ if (role.expectedOutput) cols.append(richText(t("detail.expectedOutput"), role.expectedOutput, t, { muted: true }));
462
+ if (cols.childNodes.length) card.append(cols);
463
+
464
+ if (role.workspace) {
465
+ const workspaceMeta = node("div", "record-meta");
466
+ workspaceMeta.append(pathMetaItem(t("detail.workspace"), role.workspace));
467
+ card.append(workspaceMeta);
468
+ }
469
+ return card;
470
+ }
471
+
472
+ export function historyEventRow(event, t, locale) {
473
+ if (event.kind === "milestone") {
474
+ const milestone = event.item;
475
+ const card = node("article", "record-card");
476
+ const head = node("div", "record-head");
477
+ const titleRow = node("div", "record-title-row");
478
+ titleRow.append(node("strong", "record-title", milestone.title));
479
+ head.append(titleRow);
480
+ head.append(pill(t, "history", "milestone"));
481
+ card.append(head);
482
+ const meta = node("div", "record-meta");
483
+ meta.append(node("span", "mono", milestone.id));
484
+ meta.append(node("time", "", formatDateTime(milestone.createdAt, locale)));
485
+ card.append(meta);
486
+ if (milestone.summary) card.append(richText(null, milestone.summary, t, { muted: true }));
487
+ return card;
488
+ }
489
+ const decision = event.item;
490
+ const card = node("article", "record-card");
491
+ const head = node("div", "record-head");
492
+ const titleRow = node("div", "record-title-row");
493
+ titleRow.append(node("strong", "record-title", decision.title));
494
+ head.append(titleRow);
495
+ head.append(pill(t, "history", "decision"));
496
+ card.append(head);
497
+ const meta = node("div", "record-meta");
498
+ meta.append(node("span", "mono", decision.id));
499
+ if (decision.createdAt) meta.append(node("time", "", formatDateTime(decision.createdAt, locale)));
500
+ if (decision.status) meta.append(pill(t, "decision", decision.status));
501
+ card.append(meta);
502
+ if (decision.rationale) card.append(richText(null, decision.rationale, t, { muted: true }));
503
+ if (decision.supersededReason) card.append(richText(null, decision.supersededReason, t, { muted: true }));
504
+ return card;
505
+ }
506
+
507
+ export function messageCard(message, t, locale) {
508
+ const card = node("article", "record-card");
509
+ const head = node("div", "record-head");
510
+ const titleRow = node("div", "record-title-row");
511
+ titleRow.append(node("strong", "record-title", messageAuthor(message, t)));
512
+ head.append(titleRow);
513
+ if (message.kind) head.append(pill(t, "messageKind", message.kind));
514
+ card.append(head);
515
+
516
+ const meta = node("div", "record-meta");
517
+ meta.append(node("span", "mono", message.id));
518
+ meta.append(node("time", "", formatDateTime(message.createdAt, locale)));
519
+ if (message.runId) meta.append(node("span", "mono", message.runId + (message.workItemId ? " · " + message.workItemId : "")));
520
+ card.append(meta);
521
+
522
+ card.append(richText(null, message.body, t));
523
+ return card;
524
+ }
525
+
526
+ // --- Overview + sidebar rows --------------------------------------------------
527
+ export function metricTile(label, value, options) {
528
+ const variant = options && options.hot ? " is-hot" : options && options.warning ? " is-warning" : "";
529
+ const tile = node("article", "metric" + variant);
530
+ tile.append(node("span", "metric-label", label), node("strong", "metric-value", String(value)));
531
+ return tile;
532
+ }
533
+
534
+ export function attentionRow(item, t, locale, onSelect) {
535
+ const row = node("button", "inbox-row");
536
+ row.type = "button";
537
+ const request = item.request || {};
538
+ const kind = request.policy && request.policy.kind === "required" ? "required"
539
+ : request.policy && request.policy.kind === "recommended" ? "recommended"
540
+ : null;
541
+
542
+ const lead = node("span", "inbox-lead");
543
+ lead.append(node("span", "inbox-dot"));
544
+ const head = node("span", "inbox-head");
545
+ head.append(node("span", "inbox-task", item.taskTitle));
546
+ if (kind) head.append(pill(t, "input", kind));
547
+ lead.append(head);
548
+ row.append(lead);
549
+
550
+ if (request.question) {
551
+ const question = node("span", "inbox-question", request.question);
552
+ question.title = request.question;
553
+ row.append(question);
554
+ }
555
+
556
+ const foot = node("span", "inbox-foot");
557
+ if (request.createdAt) foot.append(node("time", "", relativeTime(request.createdAt, locale, t)));
558
+ foot.append(node("span", "inbox-go", t("actions.answer") + " →"));
559
+ row.append(foot);
560
+
561
+ row.addEventListener("click", function () { onSelect(item.taskId); });
562
+ return row;
563
+ }
564
+
565
+ export function overviewRow(task, t, locale, onSelect) {
566
+ const hasInputs = task.openInputCount > 0;
567
+ const row = node("button", "overview-row" + (hasInputs ? " has-inputs" : ""));
568
+ row.type = "button";
569
+ row.append(statusDot(task.status));
570
+ const main = node("span", "overview-row-title", task.title);
571
+ main.title = task.title;
572
+ row.append(main);
573
+ row.append(node("span", "overview-row-time", relativeTime(task.updatedAt, locale, t)));
574
+ row.addEventListener("click", function () { onSelect(task.id); });
575
+ return row;
576
+ }
577
+
578
+ export function taskCard(task, hasAttention, state, t, locale, onSelect) {
579
+ const button = node("button", "task");
580
+ button.type = "button";
581
+ button.dataset.id = task.id;
582
+ button.setAttribute("aria-current", String(state.selected === task.id));
583
+
584
+ button.append(statusDot(task.status));
585
+
586
+ const main = node("span", "task-main");
587
+ main.append(node("strong", "task-title", task.title));
588
+ main.append(node("span", "task-meta", relativeTime(task.updatedAt, locale, t)));
589
+ button.append(main);
590
+
591
+ // One signal only: an open input (needs the user) outranks a running count.
592
+ if (hasAttention) {
593
+ const badge = node("span", "task-signal is-input", String(task.openInputCount || 1));
594
+ badge.title = t("stats.inputs");
595
+ button.append(badge);
596
+ } else if (task.workItems && task.workItems.running > 0) {
597
+ const badge = node("span", "task-signal is-running", String(task.workItems.running));
598
+ badge.title = t("stats.running");
599
+ button.append(badge);
600
+ }
601
+
602
+ button.addEventListener("click", function () { onSelect(task.id); });
603
+ return button;
604
+ }
605
+ `;
@@ -0,0 +1,14 @@
1
+ export const DOM_SCRIPT = `
2
+ // Tiny DOM factory shared by every client module. Kept dependency-free so it
3
+ // can be imported by components and views alike.
4
+ export function node(tagName, className, textContent) {
5
+ const element = document.createElement(tagName);
6
+ if (className) element.className = className;
7
+ if (textContent !== undefined && textContent !== null) element.textContent = String(textContent);
8
+ return element;
9
+ }
10
+
11
+ export function clear(element) {
12
+ element.replaceChildren();
13
+ }
14
+ `;
@@ -0,0 +1,28 @@
1
+ export const FORMAT_SCRIPT = `
2
+ // Date / time formatting helpers shared by cards and views.
3
+ export function relativeTime(iso, locale, t) {
4
+ const seconds = Math.round((Date.parse(iso) - Date.now()) / 1000);
5
+ if (!Number.isFinite(seconds) || Math.abs(seconds) < 45) return t("time.justNow");
6
+ const units = [
7
+ ["year", 31536000],
8
+ ["month", 2592000],
9
+ ["day", 86400],
10
+ ["hour", 3600],
11
+ ["minute", 60]
12
+ ];
13
+ const selected = units.find(function (entry) { return Math.abs(seconds) >= entry[1]; }) || ["second", 1];
14
+ return new Intl.RelativeTimeFormat(locale, { numeric: "auto" }).format(
15
+ Math.round(seconds / selected[1]),
16
+ selected[0]
17
+ );
18
+ }
19
+
20
+ export function formatDateTime(iso, locale) {
21
+ if (!iso || !Number.isFinite(Date.parse(iso))) return "—";
22
+ return new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "short" }).format(new Date(iso));
23
+ }
24
+
25
+ export function byNewest(left, right) {
26
+ return Date.parse(right.updatedAt || right.createdAt) - Date.parse(left.updatedAt || left.createdAt);
27
+ }
28
+ `;