@zq-silk/yui 2.3.0 → 2.5.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 (197) hide show
  1. package/README.md +15 -1
  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 +18 -9
  7. package/dist/cli/invocationAuthority.js +1 -1
  8. package/dist/commands/executionAuditCommands.js +5 -0
  9. package/dist/commands/globalRoleCommands.js +41 -3
  10. package/dist/commands/projectCommands.js +89 -94
  11. package/dist/commands/roleSkillValidation.js +4 -8
  12. package/dist/commands/taskCommands.js +65 -14
  13. package/dist/commands/taskUsageCommand.js +38 -0
  14. package/dist/context/roleSessionContext.js +18 -2
  15. package/dist/context/runContextPack.js +5 -1
  16. package/dist/context/sessionBootstrapManifest.js +18 -3
  17. package/dist/context/skillCatalog.js +108 -0
  18. package/dist/context/skillPackage.js +161 -0
  19. package/dist/context/taskContext.js +3 -2
  20. package/dist/context/taskSearch.js +127 -0
  21. package/dist/controlPlaneCli.js +25 -20
  22. package/dist/controller/fileSchedulerStoreAdapter.js +39 -8
  23. package/dist/controller/globalInputDelivery.js +2 -4
  24. package/dist/controller/jobControl.js +2 -0
  25. package/dist/controller/nativeHostInput.js +37 -0
  26. package/dist/controller/providerRetryDelivery.js +4 -2
  27. package/dist/controller/runtime.js +93 -2
  28. package/dist/controller/runtimeEventInbox.js +2 -2
  29. package/dist/controller/runtimeEventProcessor.js +3 -3
  30. package/dist/doctor/doctor.js +10 -0
  31. package/dist/execution/reviewMainRun.js +1 -0
  32. package/dist/execution/workItemMainRun.js +1 -0
  33. package/dist/executor/agentAdapter.js +3 -5
  34. package/dist/executor/codexConfigConflict.js +6 -4
  35. package/dist/executor/effectiveLaunch.js +24 -1
  36. package/dist/executor/fileRoleLaunchPlanner.js +15 -7
  37. package/dist/integration/gitIntegrationService.js +7 -12
  38. package/dist/integration/integrationSourceApplication.js +4 -2
  39. package/dist/interaction/notificationPresentation.js +85 -0
  40. package/dist/interaction/operatorPresentation.js +1 -2
  41. package/dist/job/httpPreview.js +61 -0
  42. package/dist/job/jobRunner.js +12 -2
  43. package/dist/kernel/builtinCapabilities.js +3 -1
  44. package/dist/message/message.js +15 -1
  45. package/dist/output/rolePresentation.js +4 -1
  46. package/dist/repository/executionLaneGitSnapshot.js +2 -1
  47. package/dist/repository/gitCompatibility.js +62 -0
  48. package/dist/repository/gitWorkspace.js +48 -71
  49. package/dist/repository/gitWorktreeInventory.js +121 -0
  50. package/dist/repository/project.js +74 -25
  51. package/dist/resources/resourceDiscovery.js +2 -2
  52. package/dist/resources/resourceGc.js +7 -4
  53. package/dist/resources/resourceRegistrar.js +2 -1
  54. package/dist/runtime/agentEndpoint.js +8 -0
  55. package/dist/runtime/agentHost.js +170 -3
  56. package/dist/runtime/codexAppServerRuntime.js +1 -1
  57. package/dist/runtime/codexNativeAccess.js +175 -0
  58. package/dist/runtime/controlledCodexTui.js +116 -0
  59. package/dist/runtime/publicReply.js +23 -0
  60. package/dist/runtime/runtimeObservation.js +48 -0
  61. package/dist/runtime/structuredProviderHost.js +16 -7
  62. package/dist/runtime/taskCostMetrics.js +133 -0
  63. package/dist/runtime/taskUsageMetrics.js +11 -2
  64. package/dist/runtime/taskUsageQuery.js +38 -0
  65. package/dist/scheduler/leaderWakeupProcessor.js +1 -9
  66. package/dist/scheduler/taskExecutionProjection.js +2 -0
  67. package/dist/scheduler/taskObservabilityProjection.js +9 -1
  68. package/dist/storage/homeLayout.js +13 -0
  69. package/dist/storage/sqliteSchema.js +44 -0
  70. package/dist/storage/sqliteStore.js +53 -0
  71. package/dist/storage/storageVersions.js +1 -1
  72. package/dist/storage/storeRpc.js +2 -0
  73. package/dist/storage/taskSearch.js +35 -0
  74. package/dist/storage/upgrade/projectKnowledgeMigration.js +52 -0
  75. package/dist/task/ordinaryArchive.js +42 -0
  76. package/dist/tmux/tmuxManager.js +8 -8
  77. package/dist/web/assets/assetManifest.js +11 -51
  78. package/dist/web/assets/client/app/catalog.js +215 -0
  79. package/dist/web/assets/client/app/main.js +148 -0
  80. package/dist/web/assets/client/app/selection.js +139 -0
  81. package/dist/web/assets/client/app/settings.js +326 -0
  82. package/dist/web/assets/client/app/taskView.js +280 -0
  83. package/dist/web/assets/client/domain/context.js +96 -0
  84. package/dist/web/assets/client/domain/records.js +126 -0
  85. package/dist/web/assets/client/domain/runs.js +119 -0
  86. package/dist/web/assets/client/domain/taskForms.js +197 -0
  87. package/dist/web/assets/client/domain/vocab.js +69 -0
  88. package/dist/web/assets/client/domain/work.js +116 -0
  89. package/dist/web/assets/client/i18n/en.js +900 -0
  90. package/dist/web/assets/client/i18n/zh-CN.js +898 -0
  91. package/dist/web/assets/client/index.js +114 -0
  92. package/dist/web/assets/client/layout/resizer.js +37 -0
  93. package/dist/web/assets/client/layout/router.js +35 -0
  94. package/dist/web/assets/client/layout/shortcuts.js +45 -0
  95. package/dist/web/assets/client/layout/sizing.js +95 -0
  96. package/dist/web/assets/client/layout/workspace.js +247 -0
  97. package/dist/web/assets/client/{api.js → lib/api.js} +20 -4
  98. package/dist/web/assets/client/{dom.js → lib/dom.js} +24 -12
  99. package/dist/web/assets/client/{format.js → lib/format.js} +7 -0
  100. package/dist/web/assets/client/lib/i18n.js +54 -0
  101. package/dist/web/assets/client/lib/markdown.js +167 -0
  102. package/dist/web/assets/client/lib/prefs.js +28 -0
  103. package/dist/web/assets/client/lib/theme.js +68 -0
  104. package/dist/web/assets/client/ui/containers.js +101 -0
  105. package/dist/web/assets/client/ui/controls.js +51 -0
  106. package/dist/web/assets/client/ui/forms.js +48 -0
  107. package/dist/web/assets/client/ui/metrics.js +137 -0
  108. package/dist/web/assets/client/ui/primitives.js +92 -0
  109. package/dist/web/assets/client/ui/text.js +33 -0
  110. package/dist/web/assets/client/ui/toast.js +12 -0
  111. package/dist/web/assets/client/views/bodySearch.js +172 -0
  112. package/dist/web/assets/client/views/createTask.js +85 -0
  113. package/dist/web/assets/client/views/dock/conversation.js +612 -0
  114. package/dist/web/assets/client/views/dock/discussion.js +173 -0
  115. package/dist/web/assets/client/views/dock/preview.js +129 -0
  116. package/dist/web/assets/client/views/dock/terminal.js +199 -0
  117. package/dist/web/assets/client/views/globalInput.js +67 -0
  118. package/dist/web/assets/client/{overview.js → views/overview.js} +61 -51
  119. package/dist/web/assets/client/{sidebar.js → views/sidebar.js} +56 -62
  120. package/dist/web/assets/client/views/skills.js +152 -0
  121. package/dist/web/assets/client/views/task/actions.js +36 -0
  122. package/dist/web/assets/client/views/task/delivery.js +185 -0
  123. package/dist/web/assets/client/views/task/deliveryResults.js +159 -0
  124. package/dist/web/assets/client/views/task/files.js +272 -0
  125. package/dist/web/assets/client/views/task/observation.js +153 -0
  126. package/dist/web/assets/client/views/task/overview.js +161 -0
  127. package/dist/web/assets/client/views/task/page.js +131 -0
  128. package/dist/web/assets/client/views/task/records.js +163 -0
  129. package/dist/web/assets/client/views/task/runtime.js +130 -0
  130. package/dist/web/assets/client/views/task/timeline.js +190 -0
  131. package/dist/web/assets/shared/geometry.js +19 -0
  132. package/dist/web/assets/shared/taskTabs.js +8 -0
  133. package/dist/web/assets/shared/themes.js +93 -0
  134. package/dist/web/assets/shell/dialogs.js +63 -0
  135. package/dist/web/assets/shell/dock.js +40 -0
  136. package/dist/web/assets/shell/index.js +40 -0
  137. package/dist/web/assets/shell/settings.js +34 -0
  138. package/dist/web/assets/shell/sidebar.js +45 -0
  139. package/dist/web/assets/styles/index.js +41 -0
  140. package/dist/web/assets/styles/{responsive.js → layout/responsive.js} +10 -10
  141. package/dist/web/assets/styles/{layout.js → layout/workspace.js} +17 -24
  142. package/dist/web/assets/styles/markdown.js +22 -3
  143. package/dist/web/assets/styles/tokens.js +10 -63
  144. package/dist/web/assets/styles/ui/controls.js +76 -0
  145. package/dist/web/assets/styles/ui/forms.js +46 -0
  146. package/dist/web/assets/styles/ui/surfaces.js +122 -0
  147. package/dist/web/assets/styles/views/delivery.js +29 -0
  148. package/dist/web/assets/styles/views/dock.js +71 -0
  149. package/dist/web/assets/styles/views/page.js +32 -0
  150. package/dist/web/assets/styles/views/records.js +70 -0
  151. package/dist/web/assets/styles/views/settings.js +26 -0
  152. package/dist/web/assets/styles/views/sidebar.js +74 -0
  153. package/dist/web/assets/styles/views/task.js +80 -0
  154. package/dist/web/assets/styles/views/terminal.js +38 -0
  155. package/dist/web/taskPreviews.js +205 -0
  156. package/dist/web/tmuxWebTerminal.js +16 -9
  157. package/dist/web/webConversation.js +373 -0
  158. package/dist/web/webMaterials.js +155 -0
  159. package/dist/web/webServer.js +295 -18
  160. package/dist/web/webSettings.js +331 -0
  161. package/dist/web/webSkills.js +86 -0
  162. package/dist/web/webSnapshot.js +40 -8
  163. package/dist/web/webTaskSurface.js +106 -8
  164. package/dist/workspace/gitChangeSetCapture.js +3 -1
  165. package/docs/cli-information-contract.md +1 -1
  166. package/docs/cli-information-contract.zh-CN.md +1 -1
  167. package/docs/managed-turn-and-session-runtime.md +31 -0
  168. package/docs/managed-turn-and-session-runtime.zh-CN.md +22 -0
  169. package/docs/release-workflow.md +54 -0
  170. package/docs/release-workflow.zh-CN.md +39 -0
  171. package/docs/storage-baseline.md +38 -1
  172. package/docs/storage-baseline.zh-CN.md +22 -0
  173. package/docs/task-discovery.md +28 -0
  174. package/docs/testing/verification-levels.md +2 -2
  175. package/i18n/README.zh-CN.md +1 -1
  176. package/package.json +1 -1
  177. package/skills/yui-leader/SKILL.md +5 -4
  178. package/skills/yui-leader/references/execution.md +2 -1
  179. package/skills/yui-operator/SKILL.md +3 -0
  180. package/skills/yui-reviewer/SKILL.md +4 -2
  181. package/skills/yui-runtime/SKILL.md +43 -2
  182. package/skills/yui-worker/SKILL.md +3 -2
  183. package/dist/web/assets/client/app.js +0 -808
  184. package/dist/web/assets/client/components.js +0 -477
  185. package/dist/web/assets/client/detail.js +0 -379
  186. package/dist/web/assets/client/dock.js +0 -309
  187. package/dist/web/assets/client/evidence.js +0 -364
  188. package/dist/web/assets/client/forms.js +0 -230
  189. package/dist/web/assets/client/i18n.js +0 -1277
  190. package/dist/web/assets/client/markdown.js +0 -114
  191. package/dist/web/assets/client/records.js +0 -198
  192. package/dist/web/assets/client/sections.js +0 -349
  193. package/dist/web/assets/client/theme.js +0 -71
  194. package/dist/web/assets/shell.js +0 -161
  195. package/dist/web/assets/styles/components.js +0 -216
  196. package/dist/web/assets/styles/views.js +0 -309
  197. /package/dist/web/assets/{icons.js → shared/icons.js} +0 -0
@@ -0,0 +1,280 @@
1
+ export const TASK_VIEW_SCRIPT = String.raw `
2
+ // The selected Task on screen: the center page, the dock discussion and its
3
+ // Session targets, and the section (tab) the reader is on. This module draws
4
+ // state.detail; app/selection loads it. A re-render is skipped while the
5
+ // reader has an unsent draft, an in-flight read or focus in the center. The
6
+ // only state field written here is state.activeTab.
7
+ import { h, isEditing } from "/assets/js/lib/dom.js";
8
+ import { releaseMutation } from "/assets/js/lib/api.js";
9
+ import { loadingBlock } from "/assets/js/ui/primitives.js";
10
+ import { entriesOf } from "/assets/js/domain/context.js";
11
+ import { renderOverview } from "/assets/js/views/overview.js";
12
+ import { renderTaskDetail, selectTab, TABS, TAB_ALIASES } from "/assets/js/views/task/page.js";
13
+ import { updateObservation } from "/assets/js/views/task/observation.js";
14
+ import { renderDiscussion, discussionHasUnsent } from "/assets/js/views/dock/discussion.js";
15
+ import { sameTarget } from "/assets/js/views/dock/terminal.js";
16
+ import { setSectionParam } from "/assets/js/layout/router.js";
17
+
18
+ // Only the observed identity participates: observation timestamps advance on
19
+ // every poll and would otherwise re-render continuously.
20
+ function runtimeSignature(detail) {
21
+ const runtime = detail.runtime;
22
+ return detail.runtimeStatus + "|" + JSON.stringify(runtime ? [
23
+ runtime.sessions && runtime.sessions.sessions, runtime.remoteDelivery,
24
+ runtime.execution && runtime.execution.attention, runtime.execution && runtime.execution.blockers,
25
+ runtime.execution && runtime.execution.next, runtime.execution && runtime.execution.status
26
+ ] : null) + "|" + ((runtime && runtime.roles) || []).map(function (role) {
27
+ return role.name + ":" + (role.runtimeSession ? role.runtimeSession.nativeSessionId + "/" + role.runtimeSession.status : "-") + ":" + role.status;
28
+ }).join(",");
29
+ }
30
+
31
+ // deps: { el, state, t, locale, api, toast, terminal, workspace,
32
+ // selectTask, clearSelection, pickAttention, readPageSessions, refreshCatalog }
33
+ // el #detail, #center and the dock discussion
34
+ // state reads selected, detail, detailKey and the catalog fields the
35
+ // overview shows; writes activeTab
36
+ // api Task reads for lazy blocks and the Task page's writes
37
+ // terminal the dock's native Session (views/dock/terminal)
38
+ // workspace layout/workspace: dock toggles and opening Sessions
39
+ // selectTask(taskId) an overview row was chosen (app/selection)
40
+ // clearSelection() the Task page's back button (app/selection)
41
+ // pickAttention(kind), readPageSessions() overview actions (app/catalog)
42
+ // refreshCatalog() quiet catalog refresh after a write; resolves when done
43
+ export function createTaskView(deps) {
44
+ const view = {
45
+ deps: deps,
46
+ // The render keys of what is on screen; an unchanged key skips the redraw.
47
+ rendered: { detail: null, discussion: null },
48
+ controller: null
49
+ };
50
+ view.controller = {
51
+ enterTask: function () { enterTask(view); },
52
+ leaveTask: function () { clearDiscussion(deps.el); },
53
+ showLoading: function () { showLoading(deps); },
54
+ showDetailError: function () { showDetailError(deps); },
55
+ hasUnsent: function () { return hasUnsent(deps.el); },
56
+ render: function (force) { render(view, force); },
57
+ observationArrived: function (detail) { observationArrived(view, detail); },
58
+ relabel: function () { relabel(view); },
59
+ showOverview: function () { showOverview(deps); },
60
+ updateSessionTargets: function () { updateSessionTargets(deps); },
61
+ switchTab: function (tab, options) { switchTab(deps, tab, options); },
62
+ applySection: function (section) { applySection(deps, section); }
63
+ };
64
+ return view.controller;
65
+ }
66
+
67
+ // A Task was just selected: start on its overview tab with nothing drawn.
68
+ function enterTask(view) {
69
+ view.deps.state.activeTab = "overview";
70
+ view.rendered.detail = null;
71
+ view.rendered.discussion = null;
72
+ clearDiscussion(view.deps.el);
73
+ }
74
+
75
+ function clearDiscussion(el) {
76
+ el.discussion.replaceChildren();
77
+ delete el.discussion.dataset.taskId;
78
+ }
79
+
80
+ function showLoading(deps) {
81
+ const el = deps.el;
82
+ el.detail.replaceChildren(h("div.page", null, loadingBlock(deps.t("loading.detail"))));
83
+ delete el.detail.dataset.taskId;
84
+ el.center.scrollTop = 0;
85
+ }
86
+
87
+ function showDetailError(deps) {
88
+ deps.el.detail.replaceChildren(h("div.page", null, h("div.list-empty.is-error", null, h("p", null, deps.t("errors.detail")))));
89
+ }
90
+
91
+ // An unsent draft in the Task page or the dock discussion.
92
+ function hasUnsent(el) {
93
+ return !!(el.detail.querySelector('[data-unsent="true"]') || discussionHasUnsent(el.discussion));
94
+ }
95
+
96
+ // The locale changed: redraw everything that carries text.
97
+ function relabel(view) {
98
+ view.rendered.discussion = null;
99
+ if (view.deps.state.detail) render(view, true); else showOverview(view.deps);
100
+ }
101
+
102
+ function render(view, force) {
103
+ const deps = view.deps;
104
+ const state = deps.state;
105
+ if (!state.detail) {
106
+ if (!state.selected) { view.rendered.detail = null; showOverview(deps); }
107
+ return;
108
+ }
109
+ renderDock(view);
110
+ // Polling must not replace an unsent draft or an in-flight form, including
111
+ // after focus moved. This is view state, never Task state.
112
+ if (centerIsBusy(deps)) return;
113
+ const key = deps.locale() + "|" + state.detailKey + "|" + runtimeSignature(state.detail);
114
+ if (!force && key === view.rendered.detail) return;
115
+ drawDetail(deps, detailContext(view));
116
+ view.rendered.detail = key;
117
+ deps.workspace.syncDockButtons();
118
+ }
119
+
120
+ function renderDock(view) {
121
+ const deps = view.deps;
122
+ const detail = deps.state.detail;
123
+ const el = deps.el;
124
+ const key = deps.locale() + "|" + detail.task.id + "|" + deps.state.detailKey + "|" + detail.task.status;
125
+ if (key !== view.rendered.discussion || el.discussion.dataset.taskId !== detail.task.id) {
126
+ renderDiscussion(el.discussion, detail, deps.t, deps.locale(), dockActions(deps));
127
+ view.rendered.discussion = key;
128
+ }
129
+ updateSessionTargets(deps);
130
+ }
131
+
132
+ function observationArrived(view, detail) {
133
+ const deps = view.deps;
134
+ if (deps.el.detail.dataset.taskId === detail.task.id) updateObservation(deps.el.detail, detail, deps.t, deps.locale(), detailContext(view));
135
+ // Patching slots is not enough when the observed identity changed facts the
136
+ // detail drew earlier; the render key decides whether a re-render is due.
137
+ if (deps.state.detail === detail) render(view);
138
+ }
139
+
140
+ // Everything the Task page (views/task/*) may call back into.
141
+ function detailContext(view) {
142
+ const deps = view.deps;
143
+ const api = deps.api;
144
+ return {
145
+ activeTab: deps.state.activeTab,
146
+ onTab: view.controller.switchTab,
147
+ onBack: deps.clearSelection,
148
+ showDock: deps.workspace.toggleDock,
149
+ openConversation: function (materials) {
150
+ deps.workspace.openConversation({ scope: "task", taskId: deps.state.selected, roleName: "leader" }, materials);
151
+ },
152
+ openSession: function (roleName) { deps.workspace.openSession({ scope: "task", taskId: deps.state.selected, roleName: roleName }); },
153
+ answerInput: function (input, answer, control) { return answerInput(deps, input, answer, control); },
154
+ taskAction: deps.api.taskAction,
155
+ inspect: api.inspect,
156
+ list: api.list,
157
+ artifacts: api.artifacts,
158
+ readArtifact: api.artifact,
159
+ evidence: api.evidence,
160
+ panels: api.panels,
161
+ readPanel: api.readPanel,
162
+ control: api.control,
163
+ updateTask: api.updateTask,
164
+ afterWrite: function () { deps.refreshCatalog(); }
165
+ };
166
+ }
167
+
168
+ // Everything the dock discussion (views/dock/discussion) may call back into.
169
+ function dockActions(deps) {
170
+ return {
171
+ sendMessage: deps.api.sendMessage,
172
+ inspect: deps.api.inspect,
173
+ list: deps.api.list,
174
+ afterWrite: function () { deps.refreshCatalog(); }
175
+ };
176
+ }
177
+
178
+ async function answerInput(deps, input, answer, control) {
179
+ if (!deps.state.detail) return;
180
+ const taskId = deps.state.detail.task.id;
181
+ const card = control && control.closest(".input-card");
182
+ const controls = card ? Array.from(card.querySelectorAll("button, input")) : control ? [control] : [];
183
+ const receipt = card && card.querySelector("[data-input-receipt]");
184
+ if (card) card.dataset.unsent = "true";
185
+ controls.forEach(function (item) { item.disabled = true; });
186
+ if (receipt) receipt.textContent = deps.t("receipt.waiting");
187
+ try {
188
+ const result = await deps.api.answerInput(taskId, input.id, answer);
189
+ releaseMutation(taskId + "/input/" + input.id);
190
+ const text = result.request.id + " · " + result.request.status + " · "
191
+ + (result.request.resolution ? result.request.resolution.answer.text : "");
192
+ if (receipt) receipt.textContent = text;
193
+ if (card) card.dataset.unsent = "false";
194
+ deps.toast(text);
195
+ const form = control && control.closest("form");
196
+ if (form) form.dataset.unsent = "false";
197
+ await deps.refreshCatalog();
198
+ } catch (error) {
199
+ controls.forEach(function (item) { item.disabled = error.disposition !== "not-submitted"; });
200
+ if (card && error.disposition === "not-submitted") card.dataset.unsent = "false";
201
+ const text = error.disposition === "not-submitted" ? error.message : deps.t("input.unknown");
202
+ if (receipt) receipt.textContent = text;
203
+ deps.toast(text);
204
+ }
205
+ }
206
+
207
+ function centerIsBusy(deps) {
208
+ const el = deps.el;
209
+ const active = document.activeElement;
210
+ return el.detail.dataset.taskId === (deps.state.detail && deps.state.detail.task.id)
211
+ && (el.detail.querySelector('[data-unsent="true"]') || el.detail.querySelector('[data-reading="true"]')
212
+ || isEditing(active) && el.center.contains(active));
213
+ }
214
+
215
+ // Re-render the page, restoring what the reader had open, the focused tab and
216
+ // the scroll position.
217
+ function drawDetail(deps, ctx) {
218
+ const el = deps.el;
219
+ const detail = deps.state.detail;
220
+ const openSections = Array.from(el.detail.querySelectorAll("details[data-view-key][open]")).map(function (d) { return d.dataset.viewKey; });
221
+ const focusedTab = el.detail.querySelector(".tab:focus");
222
+ const focusedTabId = focusedTab && focusedTab.id;
223
+ const scroll = el.detail.dataset.taskId === detail.task.id ? el.center.scrollTop : 0;
224
+ renderTaskDetail(el.detail, detail, deps.t, deps.locale(), ctx);
225
+ el.detail.querySelectorAll("details[data-view-key]").forEach(function (d) { d.open = openSections.includes(d.dataset.viewKey); });
226
+ if (focusedTabId) el.detail.querySelector("#" + CSS.escape(focusedTabId)).focus({ preventScroll: true });
227
+ el.center.scrollTop = scroll;
228
+ }
229
+
230
+ // The dock's Session chips: every role of the selected Task plus the global
231
+ // operator, keeping the Session currently open even when it left the list.
232
+ function updateSessionTargets(deps) {
233
+ const detail = deps.state.detail;
234
+ const targets = [];
235
+ if (detail) {
236
+ const runtimeRoles = (detail.runtime && detail.runtime.roles) || [];
237
+ entriesOf(detail.core, "role").forEach(function (entry) {
238
+ const runtimeRole = runtimeRoles.find(function (role) { return role.name === entry.ref.refId; });
239
+ targets.push({ scope: "task", taskId: detail.task.id, roleName: entry.ref.refId,
240
+ live: !!(runtimeRole && runtimeRole.runtimeSession && runtimeRole.runtimeSession.nativeSessionId) });
241
+ });
242
+ }
243
+ targets.push({ scope: "global", roleName: "operator" });
244
+ const current = deps.terminal.current();
245
+ if (current && !targets.some(function (target) { return sameTarget(target, current); })) targets.unshift(current);
246
+ deps.terminal.setTargets(targets);
247
+ }
248
+
249
+ function showOverview(deps) {
250
+ delete deps.el.detail.dataset.taskId;
251
+ renderOverview(deps.el.detail, deps.state, deps.t, deps.locale(), {
252
+ select: deps.selectTask,
253
+ filterAttention: deps.pickAttention,
254
+ readSessions: deps.readPageSessions,
255
+ openOperator: deps.workspace.openOperator
256
+ });
257
+ }
258
+
259
+ function switchTab(deps, tab, options) {
260
+ if (!TABS.includes(tab)) return;
261
+ deps.state.activeTab = tab;
262
+ selectTab(deps.el.detail, tab);
263
+ if (!(options && options.keepScroll)) deps.el.center.scrollTop = 0;
264
+ setSectionParam(deps.state, tab);
265
+ }
266
+
267
+ // A section may name a tab, a retired tab name, or any anchored block inside
268
+ // one (for example "reviews" → the Delivery tab, scrolled to #detail-reviews).
269
+ function applySection(deps, section) {
270
+ const tab = (section && TAB_ALIASES[section]) || section;
271
+ if (!tab || TABS.includes(tab)) { switchTab(deps, tab || "overview"); return; }
272
+ const anchor = deps.el.detail.querySelector("#detail-" + CSS.escape(tab));
273
+ const panel = anchor && anchor.closest(".tab-panel");
274
+ if (!panel) { switchTab(deps, "overview"); return; }
275
+ deps.state.activeTab = panel.id.replace(/^panel-/, "");
276
+ selectTab(deps.el.detail, deps.state.activeTab);
277
+ if (anchor.tagName === "DETAILS") anchor.open = true;
278
+ anchor.scrollIntoView({ block: "start" });
279
+ }
280
+ `;
@@ -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
+ `;