@zq-silk/yui 2.3.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 (183) 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 +17 -8
  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 +25 -2
  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 +10 -1
  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 +32 -6
  23. package/dist/controller/jobControl.js +2 -0
  24. package/dist/controller/nativeHostInput.js +37 -0
  25. package/dist/controller/runtime.js +93 -2
  26. package/dist/controller/runtimeEventInbox.js +2 -2
  27. package/dist/controller/runtimeEventProcessor.js +3 -3
  28. package/dist/doctor/doctor.js +10 -0
  29. package/dist/execution/reviewMainRun.js +1 -0
  30. package/dist/execution/workItemMainRun.js +1 -0
  31. package/dist/executor/codexConfigConflict.js +6 -4
  32. package/dist/executor/effectiveLaunch.js +24 -1
  33. package/dist/executor/fileRoleLaunchPlanner.js +13 -5
  34. package/dist/integration/gitIntegrationService.js +7 -12
  35. package/dist/integration/integrationSourceApplication.js +4 -2
  36. package/dist/job/httpPreview.js +61 -0
  37. package/dist/job/jobRunner.js +12 -2
  38. package/dist/kernel/builtinCapabilities.js +3 -1
  39. package/dist/message/message.js +15 -1
  40. package/dist/output/rolePresentation.js +4 -1
  41. package/dist/repository/executionLaneGitSnapshot.js +2 -1
  42. package/dist/repository/gitCompatibility.js +62 -0
  43. package/dist/repository/gitWorkspace.js +48 -71
  44. package/dist/repository/gitWorktreeInventory.js +121 -0
  45. package/dist/repository/project.js +74 -25
  46. package/dist/resources/resourceDiscovery.js +2 -2
  47. package/dist/resources/resourceGc.js +7 -4
  48. package/dist/resources/resourceRegistrar.js +2 -1
  49. package/dist/runtime/agentEndpoint.js +8 -0
  50. package/dist/runtime/agentHost.js +170 -3
  51. package/dist/runtime/codexAppServerRuntime.js +1 -1
  52. package/dist/runtime/codexNativeAccess.js +175 -0
  53. package/dist/runtime/controlledCodexTui.js +116 -0
  54. package/dist/runtime/publicReply.js +23 -0
  55. package/dist/runtime/runtimeObservation.js +48 -0
  56. package/dist/runtime/structuredProviderHost.js +16 -7
  57. package/dist/runtime/taskCostMetrics.js +133 -0
  58. package/dist/runtime/taskUsageMetrics.js +11 -2
  59. package/dist/runtime/taskUsageQuery.js +38 -0
  60. package/dist/scheduler/taskExecutionProjection.js +2 -0
  61. package/dist/scheduler/taskObservabilityProjection.js +9 -1
  62. package/dist/storage/homeLayout.js +13 -0
  63. package/dist/storage/sqliteSchema.js +44 -0
  64. package/dist/storage/sqliteStore.js +53 -0
  65. package/dist/storage/storageVersions.js +1 -1
  66. package/dist/storage/storeRpc.js +2 -0
  67. package/dist/storage/taskSearch.js +35 -0
  68. package/dist/storage/upgrade/projectKnowledgeMigration.js +52 -0
  69. package/dist/task/ordinaryArchive.js +42 -0
  70. package/dist/tmux/tmuxManager.js +8 -8
  71. package/dist/web/assets/assetManifest.js +11 -51
  72. package/dist/web/assets/client/app/catalog.js +215 -0
  73. package/dist/web/assets/client/app/main.js +148 -0
  74. package/dist/web/assets/client/app/selection.js +139 -0
  75. package/dist/web/assets/client/app/settings.js +326 -0
  76. package/dist/web/assets/client/app/taskView.js +280 -0
  77. package/dist/web/assets/client/domain/context.js +96 -0
  78. package/dist/web/assets/client/domain/records.js +126 -0
  79. package/dist/web/assets/client/domain/runs.js +119 -0
  80. package/dist/web/assets/client/domain/taskForms.js +197 -0
  81. package/dist/web/assets/client/domain/vocab.js +69 -0
  82. package/dist/web/assets/client/domain/work.js +116 -0
  83. package/dist/web/assets/client/i18n/en.js +900 -0
  84. package/dist/web/assets/client/i18n/zh-CN.js +898 -0
  85. package/dist/web/assets/client/index.js +114 -0
  86. package/dist/web/assets/client/layout/resizer.js +37 -0
  87. package/dist/web/assets/client/layout/router.js +35 -0
  88. package/dist/web/assets/client/layout/shortcuts.js +45 -0
  89. package/dist/web/assets/client/layout/sizing.js +95 -0
  90. package/dist/web/assets/client/layout/workspace.js +247 -0
  91. package/dist/web/assets/client/{api.js → lib/api.js} +20 -4
  92. package/dist/web/assets/client/{dom.js → lib/dom.js} +24 -12
  93. package/dist/web/assets/client/{format.js → lib/format.js} +7 -0
  94. package/dist/web/assets/client/lib/i18n.js +54 -0
  95. package/dist/web/assets/client/lib/markdown.js +167 -0
  96. package/dist/web/assets/client/lib/prefs.js +28 -0
  97. package/dist/web/assets/client/lib/theme.js +68 -0
  98. package/dist/web/assets/client/ui/containers.js +101 -0
  99. package/dist/web/assets/client/ui/controls.js +51 -0
  100. package/dist/web/assets/client/ui/forms.js +48 -0
  101. package/dist/web/assets/client/ui/metrics.js +137 -0
  102. package/dist/web/assets/client/ui/primitives.js +92 -0
  103. package/dist/web/assets/client/ui/text.js +33 -0
  104. package/dist/web/assets/client/ui/toast.js +12 -0
  105. package/dist/web/assets/client/views/bodySearch.js +172 -0
  106. package/dist/web/assets/client/views/createTask.js +85 -0
  107. package/dist/web/assets/client/views/dock/conversation.js +612 -0
  108. package/dist/web/assets/client/views/dock/discussion.js +173 -0
  109. package/dist/web/assets/client/views/dock/preview.js +129 -0
  110. package/dist/web/assets/client/views/dock/terminal.js +199 -0
  111. package/dist/web/assets/client/views/globalInput.js +67 -0
  112. package/dist/web/assets/client/{overview.js → views/overview.js} +61 -51
  113. package/dist/web/assets/client/{sidebar.js → views/sidebar.js} +56 -62
  114. package/dist/web/assets/client/views/skills.js +152 -0
  115. package/dist/web/assets/client/views/task/actions.js +36 -0
  116. package/dist/web/assets/client/views/task/delivery.js +185 -0
  117. package/dist/web/assets/client/views/task/deliveryResults.js +159 -0
  118. package/dist/web/assets/client/views/task/files.js +272 -0
  119. package/dist/web/assets/client/views/task/observation.js +153 -0
  120. package/dist/web/assets/client/views/task/overview.js +161 -0
  121. package/dist/web/assets/client/views/task/page.js +131 -0
  122. package/dist/web/assets/client/views/task/records.js +163 -0
  123. package/dist/web/assets/client/views/task/runtime.js +130 -0
  124. package/dist/web/assets/client/views/task/timeline.js +190 -0
  125. package/dist/web/assets/shared/geometry.js +19 -0
  126. package/dist/web/assets/shared/taskTabs.js +8 -0
  127. package/dist/web/assets/shared/themes.js +93 -0
  128. package/dist/web/assets/shell/dialogs.js +63 -0
  129. package/dist/web/assets/shell/dock.js +40 -0
  130. package/dist/web/assets/shell/index.js +40 -0
  131. package/dist/web/assets/shell/settings.js +34 -0
  132. package/dist/web/assets/shell/sidebar.js +45 -0
  133. package/dist/web/assets/styles/index.js +41 -0
  134. package/dist/web/assets/styles/{responsive.js → layout/responsive.js} +10 -10
  135. package/dist/web/assets/styles/{layout.js → layout/workspace.js} +17 -24
  136. package/dist/web/assets/styles/markdown.js +22 -3
  137. package/dist/web/assets/styles/tokens.js +10 -63
  138. package/dist/web/assets/styles/ui/controls.js +76 -0
  139. package/dist/web/assets/styles/ui/forms.js +46 -0
  140. package/dist/web/assets/styles/ui/surfaces.js +122 -0
  141. package/dist/web/assets/styles/views/delivery.js +29 -0
  142. package/dist/web/assets/styles/views/dock.js +71 -0
  143. package/dist/web/assets/styles/views/page.js +32 -0
  144. package/dist/web/assets/styles/views/records.js +70 -0
  145. package/dist/web/assets/styles/views/settings.js +26 -0
  146. package/dist/web/assets/styles/views/sidebar.js +74 -0
  147. package/dist/web/assets/styles/views/task.js +80 -0
  148. package/dist/web/assets/styles/views/terminal.js +38 -0
  149. package/dist/web/taskPreviews.js +205 -0
  150. package/dist/web/tmuxWebTerminal.js +16 -9
  151. package/dist/web/webConversation.js +373 -0
  152. package/dist/web/webMaterials.js +155 -0
  153. package/dist/web/webServer.js +295 -18
  154. package/dist/web/webSettings.js +331 -0
  155. package/dist/web/webSkills.js +86 -0
  156. package/dist/web/webSnapshot.js +40 -8
  157. package/dist/web/webTaskSurface.js +106 -8
  158. package/dist/workspace/gitChangeSetCapture.js +3 -1
  159. package/docs/managed-turn-and-session-runtime.md +9 -0
  160. package/docs/managed-turn-and-session-runtime.zh-CN.md +7 -0
  161. package/docs/release-workflow.md +28 -0
  162. package/docs/release-workflow.zh-CN.md +21 -0
  163. package/docs/storage-baseline.md +38 -1
  164. package/docs/storage-baseline.zh-CN.md +22 -0
  165. package/docs/task-discovery.md +28 -0
  166. package/docs/testing/verification-levels.md +2 -2
  167. package/i18n/README.zh-CN.md +1 -1
  168. package/package.json +1 -1
  169. package/dist/web/assets/client/app.js +0 -808
  170. package/dist/web/assets/client/components.js +0 -477
  171. package/dist/web/assets/client/detail.js +0 -379
  172. package/dist/web/assets/client/dock.js +0 -309
  173. package/dist/web/assets/client/evidence.js +0 -364
  174. package/dist/web/assets/client/forms.js +0 -230
  175. package/dist/web/assets/client/i18n.js +0 -1277
  176. package/dist/web/assets/client/markdown.js +0 -114
  177. package/dist/web/assets/client/records.js +0 -198
  178. package/dist/web/assets/client/sections.js +0 -349
  179. package/dist/web/assets/client/theme.js +0 -71
  180. package/dist/web/assets/shell.js +0 -161
  181. package/dist/web/assets/styles/components.js +0 -216
  182. package/dist/web/assets/styles/views.js +0 -309
  183. /package/dist/web/assets/{icons.js → shared/icons.js} +0 -0
@@ -1,114 +0,0 @@
1
- export const MARKDOWN_SCRIPT = `
2
- // Minimal, safe Markdown renderer.
3
- //
4
- // Agent-authored prose (focus, outcomes, reviews, messages) arrives as plain
5
- // text with informal Markdown. Rendering structure (headings, lists, code)
6
- // runs walls of text into scannable blocks.
7
- //
8
- // Safety rule: every byte of user text is HTML-escaped BEFORE any tag is
9
- // introduced; the transforms below only ever add our own elements. Links are
10
- // limited to http(s) URLs taken from already-escaped text, so no attribute
11
- // breakout is possible.
12
- export function escapeHtml(text) {
13
- return String(text)
14
- .replace(/&/g, "&")
15
- .replace(/</g, "&lt;")
16
- .replace(/>/g, "&gt;")
17
- .replace(/"/g, "&quot;");
18
- }
19
-
20
- // Inline spans: code, bold, auto-links. Expects HTML-escaped input.
21
- export function inlineMarkdown(escaped) {
22
- const codes = [];
23
- let out = escaped.replace(/\`([^\`\\n]+)\`/g, function (_match, code) {
24
- codes.push(code);
25
- return "\\u0000" + (codes.length - 1) + "\\u0000";
26
- });
27
- out = out.replace(/\\*\\*([^*\\n]+)\\*\\*/g, "<strong>$1</strong>");
28
- out = out.replace(/(https?:\\/\\/[^\\s<]+)/g, function (url) {
29
- return '<a href="' + url + '" target="_blank" rel="noreferrer">' + url + "</a>";
30
- });
31
- out = out.replace(/\\u0000(\\d+)\\u0000/g, function (_match, index) {
32
- return "<code>" + codes[Number(index)] + "</code>";
33
- });
34
- return out;
35
- }
36
-
37
- // Block structure: fenced code, ATX headings, unordered / ordered lists,
38
- // paragraphs with soft line breaks.
39
- export function renderMarkdown(text) {
40
- const lines = String(text).replace(/\\u0000/g, "").replace(/\\r\\n?/g, "\\n").split("\\n");
41
- const html = [];
42
- let paragraph = [];
43
- let list = null;
44
- let code = null;
45
- function flushParagraph() {
46
- if (!paragraph.length) return;
47
- html.push("<p>" + paragraph.map(inlineMarkdown).join("<br>") + "</p>");
48
- paragraph = [];
49
- }
50
- function flushList() {
51
- if (!list) return;
52
- html.push("<" + list.type + ">" + list.items.map(function (item) {
53
- return "<li>" + inlineMarkdown(item) + "</li>";
54
- }).join("") + "</" + list.type + ">");
55
- list = null;
56
- }
57
- lines.forEach(function (line) {
58
- if (code !== null) {
59
- if (/^\\s*\`\`\`/.test(line)) {
60
- html.push("<pre><code>" + code.join("\\n") + "</code></pre>");
61
- code = null;
62
- } else {
63
- code.push(line);
64
- }
65
- return;
66
- }
67
- if (/^\\s*\`\`\`/.test(line)) {
68
- flushParagraph();
69
- flushList();
70
- code = [];
71
- return;
72
- }
73
- const heading = line.match(/^\\s{0,3}(#{1,4})\\s+(.*)$/);
74
- if (heading) {
75
- flushParagraph();
76
- flushList();
77
- const level = Math.min(heading[1].length + 3, 6);
78
- html.push("<h" + level + ">" + inlineMarkdown(escapeHtml(heading[2].trim())) + "</h" + level + ">");
79
- return;
80
- }
81
- const unordered = line.match(/^\\s*[-*•]\\s+(.*)$/);
82
- if (unordered) {
83
- flushParagraph();
84
- if (!list || list.type !== "ul") {
85
- flushList();
86
- list = { type: "ul", items: [] };
87
- }
88
- list.items.push(escapeHtml(unordered[1]));
89
- return;
90
- }
91
- const ordered = line.match(/^\\s*\\d+[.)]\\s+(.*)$/);
92
- if (ordered) {
93
- flushParagraph();
94
- if (!list || list.type !== "ol") {
95
- flushList();
96
- list = { type: "ol", items: [] };
97
- }
98
- list.items.push(escapeHtml(ordered[1]));
99
- return;
100
- }
101
- if (/^\\s*$/.test(line)) {
102
- flushParagraph();
103
- flushList();
104
- return;
105
- }
106
- flushList();
107
- paragraph.push(escapeHtml(line));
108
- });
109
- flushParagraph();
110
- flushList();
111
- if (code !== null) html.push("<pre><code>" + code.join("\\n") + "</code></pre>");
112
- return html.join("");
113
- }
114
- `;
@@ -1,198 +0,0 @@
1
- export const RECORDS_SCRIPT = String.raw `
2
- // Context record access. Every business fact keeps its Context reference; an
3
- // expanded value is a current read, never a mutation of the snapshot.
4
- import { h, icon } from "/assets/js/dom.js";
5
- import { richText, mono, button, note, disclosure } from "/assets/js/components.js";
6
-
7
- export function entriesOf(core, store) {
8
- return core.records.filter(function (entry) { return entry.ref.store === store; });
9
- }
10
-
11
- export function valuesOf(core, store) {
12
- return entriesOf(core, store).filter(function (entry) { return !entry.omitted; }).map(function (entry) { return entry.value; });
13
- }
14
-
15
- // The family total from the same bounded read as its samples. The current
16
- // view counts current records only: open work, active runs, open questions.
17
- export function totalOf(core, store) {
18
- const row = (core.collections || []).find(function (item) { return item.store === store; });
19
- return row ? row.total : entriesOf(core, store).length;
20
- }
21
-
22
- // List and omitted-value summaries join known fields in a fixed order
23
- // ("title: …; status: …"). Split them for display only; the exact record
24
- // stays one read away.
25
- const SUMMARY_KEYS = ["title", "objective", "question", "summary", "body", "status", "type"];
26
- export function summaryFields(summary) {
27
- const fields = {};
28
- if (!summary) return fields;
29
- const pattern = new RegExp("(?:^|; )(" + SUMMARY_KEYS.join("|") + "): ", "g");
30
- const marks = [];
31
- let match;
32
- while ((match = pattern.exec(summary))) {
33
- const rank = SUMMARY_KEYS.indexOf(match[1]);
34
- if (marks.length && rank <= marks[marks.length - 1].rank) continue;
35
- marks.push({ key: match[1], rank: rank, start: match.index, from: match.index + match[0].length });
36
- }
37
- marks.forEach(function (mark, index) {
38
- fields[mark.key] = summary.slice(mark.from, index + 1 < marks.length ? marks[index + 1].start : summary.length);
39
- });
40
- return fields;
41
- }
42
-
43
- // --- Exact reads -------------------------------------------------------------
44
- // One inspect per reference digest, held in view state so a refresh with an
45
- // unchanged reference never reads again. A changed digest is a new read.
46
- export function readExact(entry, taskId, actions, cache) {
47
- const key = entry.ref.store + "/" + entry.ref.refId;
48
- const hit = cache[key];
49
- if (hit && hit.digest === entry.ref.digest) return hit.promise;
50
- const promise = actions.inspect(taskId, entry.ref);
51
- const slot = { digest: entry.ref.digest, promise: promise, result: null };
52
- cache[key] = slot;
53
- promise.then(function (result) { slot.result = result; }, function () { if (cache[key] === slot) delete cache[key]; });
54
- return promise;
55
- }
56
-
57
- // A row drawn from its exact value: synchronously when cached, otherwise the
58
- // honest summary card until the read returns and replaces it in place.
59
- export function exactRow(entry, taskId, t, actions, cache, render) {
60
- if (!entry.omitted) return render(entry.value, null);
61
- const hit = cache[entry.ref.store + "/" + entry.ref.refId];
62
- if (hit && hit.digest === entry.ref.digest && hit.result) return render(hit.result.value, hit.result);
63
- const placeholder = recordCard(entry, taskId, t, actions);
64
- placeholder.dataset.reading = "true";
65
- readExact(entry, taskId, actions, cache).then(function (result) {
66
- if (placeholder.isConnected) placeholder.replaceWith(render(result.value, result));
67
- }, function () { placeholder.dataset.reading = "false"; });
68
- return placeholder;
69
- }
70
-
71
- // A closed row drawn from a list summary; its exact value is read on first
72
- // open. Open rows are remembered in view state and reopen after a redraw.
73
- export function lazyRow(item, taskId, t, actions, options) {
74
- const opts = options || {};
75
- const key = item.ref.store + ":" + item.ref.refId;
76
- const element = h("details.lazy-row");
77
- if (item.status) element.dataset.status = item.status;
78
- const body = h("div.lazy-body");
79
- element.append(h("summary.lazy-head", null, icon("chevron", "disclosure-chevron"), opts.head), body);
80
- let state = "idle";
81
- function load() {
82
- if (state !== "idle") return;
83
- state = "reading";
84
- element.dataset.reading = "true";
85
- body.replaceChildren(h("p.faint.small", null, t("record.reading")));
86
- readExact({ ref: item.ref }, taskId, actions, opts.cache || {}).then(function (result) {
87
- state = "done";
88
- const raw = disclosure(t("record.raw"), null);
89
- raw.body.append(h("pre.code-block", null, JSON.stringify(result.value, null, 2)));
90
- body.replaceChildren();
91
- body.append(h("div.lazy-content", null, opts.render ? opts.render(result.value, result) : null), raw);
92
- }, function (error) {
93
- state = "idle";
94
- body.replaceChildren(note(t("record.changed") + " · " + error.message, "bad"));
95
- }).then(function () { element.dataset.reading = "false"; });
96
- }
97
- element.addEventListener("toggle", function () {
98
- if (opts.openRows) { if (element.open) opts.openRows[key] = true; else delete opts.openRows[key]; }
99
- if (element.open) load();
100
- });
101
- if (opts.openRows && opts.openRows[key]) element.open = true;
102
- return element;
103
- }
104
-
105
- // --- Paged family discovery --------------------------------------------------
106
- // Pages of one Context family for the current Context cursor. A continuation
107
- // never mixes versions: after the cursor changes, the first page is re-read.
108
- export function familyState(view, store) {
109
- view.families = view.families || {};
110
- if (!view.families[store]) view.families[store] = { items: [], total: null, nextCursor: null, pages: 0, key: null, error: null, pending: null };
111
- return view.families[store];
112
- }
113
-
114
- export function loadFamily(view, taskId, store, key, actions, options) {
115
- const state = familyState(view, store);
116
- const more = !!(options && options.more);
117
- if (state.pending) return state.pending;
118
- if (more ? !state.nextCursor : state.key === key && state.pages && !state.error) return Promise.resolve(state);
119
- state.pending = actions.list(taskId, store, { limit: (options && options.limit) || 40, cursor: more ? state.nextCursor : null })
120
- .then(function (page) {
121
- if (!more) { state.items = []; state.pages = 0; }
122
- state.items = state.items.concat(page.items);
123
- state.total = page.total;
124
- state.nextCursor = page.nextCursor;
125
- state.pages += 1;
126
- state.key = more ? state.key : key;
127
- state.error = null;
128
- state.errorKey = null;
129
- return state;
130
- }, function (error) { state.error = error.message; state.errorKey = more ? state.key : key; return state; })
131
- .then(function (result) { state.pending = null; return result; });
132
- return state.pending;
133
- }
134
-
135
- // --- Cards ---------------------------------------------------------------------
136
- // A generic, honest card for any record: omitted values say so and can be
137
- // read by reference; reading never acknowledges or accepts anything.
138
- export function recordCard(entry, taskId, t, actions, options) {
139
- const opts = options || {};
140
- const element = h("article.record" + (opts.compact ? ".is-compact" : ""));
141
- element.append(h("header.record-head", null,
142
- h("span.record-store", null, t("store." + entry.ref.store, entry.ref.store)),
143
- mono(entry.ref.refId),
144
- h("span.spacer"),
145
- h("span.faint.small", { title: t("record.revision") }, String(entry.ref.revision).slice(0, 24))));
146
- if (!entry.omitted && entry.value) {
147
- const value = entry.value;
148
- const text = value.content || value.summary || value.body || value.objective || value.title || value.leaderSummary;
149
- if (text) element.append(richText(null, text, t, { threshold: 360 }));
150
- if (entry.ref.store === "task-decision" && value.rationale) element.append(richText(t("record.rationale"), value.rationale, t, { muted: true }));
151
- const facts = [value.status, value.roleName].filter(Boolean);
152
- if (facts.length) element.append(h("p.faint.small", null, facts.join(" · ")));
153
- if (entry.ref.store === "job") element.append(note(t("job." + value.status + ".note", t("job.terminal.note")), value.status === "unknown-needs-attention" ? "warn" : ""));
154
- } else {
155
- const fields = summaryFields(entry.summary);
156
- const lead = fields.title || fields.question || fields.objective;
157
- if (lead) element.append(h("p.record-lead", null, lead));
158
- const text = fields.summary || fields.body || (lead === fields.objective ? null : fields.objective);
159
- if (text) element.append(h("p.faint", null, text));
160
- if (!lead && !text) element.append(h("p.faint", null, entry.summary || t("record.omitted")));
161
- }
162
- element.append(recordReader(entry, taskId, t, actions));
163
- return element;
164
- }
165
-
166
- // Shared source access for cards, history and discussion, including records
167
- // whose compact value is present but does not display every durable field.
168
- // With a renderer, the exact value is drawn richly and the raw record stays
169
- // available behind a disclosure.
170
- export function recordReader(entry, taskId, t, actions, render) {
171
- const element = h("div.stack");
172
- const expand = button(t("record.read"), { icon: "eye", variant: "link" });
173
- expand.addEventListener("click", async function () {
174
- expand.disabled = true;
175
- element.dataset.reading = "true";
176
- try {
177
- const result = await actions.inspect(taskId, entry.ref);
178
- const rich = render ? render(result.value, result) : null;
179
- if (rich) {
180
- const raw = disclosure(t("record.raw"), null);
181
- raw.body.append(h("pre.code-block", null, JSON.stringify(result.value, null, 2)));
182
- element.append(rich, raw);
183
- } else {
184
- if (result.result && result.result.output) element.append(richText(t("record.output"), result.result.output, t));
185
- if (result.result && result.result.diagnostic) element.append(richText(t("record.diagnostic"), result.result.diagnostic, t));
186
- element.append(h("pre.code-block", null, JSON.stringify(result.value, null, 2)));
187
- if (result.execution) element.append(h("pre.code-block", null, JSON.stringify(result.execution, null, 2)));
188
- }
189
- expand.remove();
190
- } catch {
191
- expand.disabled = false;
192
- expand.replaceChildren(icon("refresh"), h("span", null, t("record.changed")));
193
- } finally { element.dataset.reading = "false"; }
194
- });
195
- element.append(h("div.record-actions", null, expand));
196
- return element;
197
- }
198
- `;
@@ -1,349 +0,0 @@
1
- export const SECTIONS_SCRIPT = String.raw `
2
- // The Runtime and Records tabs. Runtime is what is live now (Roles and their
3
- // Sessions, open execution, open operations, usage, diagnostics); Records is
4
- // what happened (a paged timeline of decisions, milestones, executions and
5
- // questions) plus Task facts. Every #detail-* anchor stays addressable via
6
- // ?section=.
7
- import { h, icon, clear } from "/assets/js/dom.js";
8
- import { formatDateTime } from "/assets/js/format.js";
9
- import {
10
- card, listCard, filterChips, disclosure, emptyState, note, button, statusBadge, label, tone, dot, chip, mono,
11
- kv, richText, runCard, runBody, roleCard, timeTag, observabilityMetricCard
12
- } from "/assets/js/components.js";
13
- import {
14
- entriesOf, totalOf, summaryFields, exactRow, lazyRow, recordCard, familyState, loadFamily
15
- } from "/assets/js/records.js";
16
- import { controlForm, titleForm } from "/assets/js/forms.js";
17
-
18
- // --- Runtime ----------------------------------------------------------------------
19
- export function renderRuntime(panel, data, t, locale, ctx) {
20
- const core = data.core;
21
- const task = data.task;
22
- const view = data.viewState;
23
- view.exact = view.exact || {};
24
- const runtimeRoles = (data.runtime && data.runtime.roles) || [];
25
- // Active runs already read exactly (this view or an earlier render).
26
- const knownRuns = entriesOf(core, "run").map(function (entry) {
27
- const hit = view.exact["run/" + entry.ref.refId];
28
- return hit && hit.digest === entry.ref.digest && hit.result ? hit.result.value : null;
29
- }).filter(Boolean);
30
-
31
- const roleEntries = entriesOf(core, "role");
32
- const roles = listCard({
33
- id: "detail-roles", title: t("roles.title"), icon: "user", count: totalOf(core, "role"), hint: t("roles.hint"),
34
- actions: h("div.chip-row", { dataset: { slot: "session-counts" } })
35
- });
36
- roleEntries.forEach(function (entry) {
37
- if (entry.omitted) { roles.list.append(recordCard(entry, task.id, t, ctx)); return; }
38
- const role = entry.value;
39
- const active = knownRuns.find(function (run) { return run.roleName === role.name && run.status === "active"; });
40
- const runtimeRole = runtimeRoles.find(function (item) { return item.name === role.name; });
41
- const element = roleCard(role, t, locale, {
42
- runtimeRole: runtimeRole,
43
- retry: entry.providerRetry,
44
- launchDrift: active && active.effective && active.effective.sourceDesiredRevision !== role.launchRevision,
45
- onOpen: task.status === "archived" ? null : ctx.openSession
46
- });
47
- const head = element.querySelector(".role-head");
48
- head.append(h("span", { dataset: { roleStatus: role.name } }));
49
- head.after(h("div.role-session", { dataset: { roleSession: role.name } }));
50
- roles.list.append(element);
51
- });
52
- if (!roleEntries.length) roles.list.append(emptyState(t("records.none")));
53
- panel.append(roles);
54
-
55
- const redirect = disclosure(t("control.title"), "control", { className: "card-disclosure", meta: t("control.meta") });
56
- redirect.id = "detail-control";
57
- redirect.body.append(note(t("control.help")), controlForm(task, t, ctx, roleEntries.map(function (entry) { return entry.ref.refId; })));
58
- panel.append(redirect);
59
-
60
- const runEntries = entriesOf(core, "run");
61
- const runTotal = totalOf(core, "run");
62
- const runList = listCard({ id: "detail-exec", title: t("runs.title"), icon: "pulse", count: runTotal || null, hint: t("runs.hint") });
63
- if (!runEntries.length) runList.list.append(emptyState(t("runs.none"), "pulse"));
64
- runEntries.forEach(function (entry) {
65
- runList.list.append(exactRow(entry, task.id, t, ctx, view.exact, function (value, result) {
66
- return runCard({ ...value, execution: (result && result.execution) || entry.execution }, t, locale);
67
- }));
68
- });
69
- if (runTotal > runEntries.length) runList.list.append(note(t("runs.more").replace("{count}", String(runTotal - runEntries.length))));
70
- panel.append(runList);
71
-
72
- const jobs = entriesOf(core, "job");
73
- const jobTotal = totalOf(core, "job");
74
- const jobList = listCard({
75
- id: "detail-operations", title: t("jobs.title"), icon: "refresh", count: jobTotal || null,
76
- hint: jobTotal ? t("jobs.hint") : null
77
- });
78
- if (!jobs.length) jobList.list.append(emptyState(t("jobs.none")));
79
- jobs.forEach(function (entry) {
80
- jobList.list.append(exactRow(entry, task.id, t, ctx, view.exact, function (job) {
81
- return h("article.record", null,
82
- h("header.record-head", null, dot(tone("job", job.status)), mono(task.id + "/" + job.id), h("span.spacer"),
83
- statusBadge(t, "job", "job", job.status)),
84
- h("p.small", null, t("job." + job.status + ".note", t("job.terminal.note"))),
85
- h("p.faint.small", null, (job.steps || []).map(function (step) { return step.name; }).join(" → ")),
86
- timeTag(job.terminalAt || job.startedAt || job.createdAt, locale, t));
87
- }));
88
- });
89
- panel.append(jobList);
90
-
91
- const usage = card({ id: "detail-usage", title: t("usage.title"), icon: "clock" });
92
- const usageBody = h("div", { dataset: { slot: "usage" } });
93
- const metrics = data.runtime && observabilityMetricCard(data.runtime.observability, t);
94
- usageBody.append(metrics || emptyState(t("usage.unavailable")));
95
- usage.body.append(usageBody);
96
- panel.append(usage);
97
-
98
- const diagnostics = disclosure(t("diagnostics.title"), "diagnostics", { className: "card-disclosure", meta: t("diagnostics.meta") });
99
- diagnostics.id = "detail-diagnostics";
100
- diagnostics.body.append(note(t("observation.help")));
101
- (core.observations || []).forEach(function (observation) {
102
- diagnostics.body.append(h("p.small.mono-line", null,
103
- observation.source + " · " + observation.status + " · " + observation.coverage + " · " + observation.observedAt));
104
- });
105
- diagnostics.body.append(h("div.stack", { dataset: { slot: "session-facts" } }),
106
- h("p.faint.small", { dataset: { slot: "runtime-status" } }), h("pre.code-block", { dataset: { slot: "runtime-raw" } }));
107
- panel.append(diagnostics);
108
- }
109
-
110
- // --- Records ----------------------------------------------------------------------
111
- // One timeline over four Context families, read as paged summaries. Messages
112
- // live in the Discussion dock. Ascending families are read in full (bounded);
113
- // descending runs page on request, and older rows of other families are held
114
- // back until the run pages reach them so the merged order stays honest.
115
- const TIMELINE = [
116
- { kind: "decision", store: "task-decision", icon: "check", ascending: true },
117
- { kind: "milestone", store: "task-milestone", icon: "flag", ascending: true },
118
- { kind: "run", store: "run", icon: "pulse", ascending: false },
119
- { kind: "question", store: "input-request", icon: "inbox", ascending: true }
120
- ];
121
- const FULL_PAGES = 5;
122
-
123
- function at(item) { return Date.parse(item.createdAt || 0) || 0; }
124
-
125
- export function renderRecords(panel, data, t, locale, ctx) {
126
- const core = data.core;
127
- const task = data.task;
128
- const view = data.viewState;
129
- view.panels = view.panels || {};
130
- view.panels.records = panel;
131
- view.exact = view.exact || {};
132
- view.openRows = view.openRows || {};
133
- if (!TIMELINE.some(function (family) { return family.kind === view.recordsFilter; })) view.recordsFilter = "all";
134
- // Values the current snapshot already carries need no read.
135
- const known = {};
136
- core.records.forEach(function (entry) { if (!entry.omitted) known[entry.ref.store + "/" + entry.ref.refId] = entry; });
137
-
138
- const history = card({
139
- id: "detail-history", title: t("history.title"), icon: "history", hint: t("history.hint"),
140
- actions: filterChips(t("history.filter"), [{ value: "all", label: t("history.all") }].concat(TIMELINE.map(function (family) {
141
- return { value: family.kind, label: t("history." + family.kind) };
142
- })), view.recordsFilter, function (value) { view.recordsFilter = value; draw(); })
143
- });
144
- const list = h("ol.timeline");
145
- const foot = h("div.list-foot");
146
- history.body.append(list, foot);
147
-
148
- function families() {
149
- return TIMELINE.filter(function (family) { return view.recordsFilter === "all" || view.recordsFilter === family.kind; });
150
- }
151
- function draw() {
152
- clear(list);
153
- clear(foot);
154
- history.querySelectorAll(".chip-tab").forEach(function (chipButton) {
155
- chipButton.setAttribute("aria-pressed", String(chipButton.dataset.filter === view.recordsFilter));
156
- });
157
- const selected = families();
158
- const states = selected.map(function (family) { return { family: family, state: familyState(view, family.store) }; });
159
- let horizon = -Infinity;
160
- states.forEach(function (row) {
161
- if (row.state.nextCursor && !row.family.ascending && row.state.items.length) {
162
- horizon = Math.max(horizon, Math.min.apply(null, row.state.items.map(at)));
163
- }
164
- });
165
- const items = [];
166
- states.forEach(function (row) {
167
- row.state.items.forEach(function (item) { if (at(item) >= horizon) items.push({ family: row.family, item: item }); });
168
- });
169
- items.sort(function (a, b) { return at(b.item) - at(a.item); });
170
- const loading = states.some(function (row) { return row.state.pending && !row.state.pages; });
171
- const loaded = states.every(function (row) { return row.state.pages || row.state.error; });
172
- states.forEach(function (row) {
173
- if (row.state.error) list.append(h("li.timeline-empty", null, note(t("history." + row.family.kind) + " · " + t("records.unavailable") + " " + row.state.error, "bad")));
174
- });
175
- if (!items.length) {
176
- list.append(h("li.timeline-empty", null, emptyState(loading || !loaded ? t("history.loading") : t("records.none"), "history")));
177
- }
178
- items.forEach(function (row) { list.append(timelineItem(row.family, row.item)); });
179
- const total = states.reduce(function (sum, row) { return sum + (row.state.total || 0); }, 0);
180
- if (!loaded) return;
181
- foot.append(h("span.faint.small", null, t("history.loaded").replace("{shown}", String(items.length)).replace("{total}", String(total))));
182
- const stale = states.some(function (row) { return row.state.key && row.state.key !== core.coreCursor; });
183
- if (stale) {
184
- foot.append(button(t("history.stale"), { icon: "refresh", variant: "ghost", onClick: function () { show(true); } }));
185
- }
186
- if (states.some(function (row) { return row.state.nextCursor; })) {
187
- const more = button(t("history.more"), { icon: "chevron", variant: "ghost" });
188
- more.addEventListener("click", function () {
189
- more.disabled = true;
190
- Promise.all(states.filter(function (row) { return row.state.nextCursor; }).map(function (row) {
191
- return loadFamily(view, task.id, row.family.store, row.state.key, ctx, { more: true, limit: 40 });
192
- })).then(refresh);
193
- });
194
- foot.append(more);
195
- }
196
- }
197
-
198
- function timelineItem(family, item) {
199
- const fields = summaryFields(item.summary);
200
- const entry = known[item.ref.store + "/" + item.ref.refId];
201
- const value = entry && entry.ref.digest === item.ref.digest ? entry.value : null;
202
- const element = h("li.timeline-item.kind-" + family.kind);
203
- element.append(h("span.timeline-mark", { "aria-hidden": "true" }, icon(family.icon, "icon-sm")));
204
- const body = h("div.timeline-body");
205
- let title;
206
- if (family.kind === "run") title = [item.roleName || t("store.run"), item.purpose ? label(t, "run.purpose", item.purpose) : null].filter(Boolean).join(" · ");
207
- else if (family.kind === "question") title = fields.question || fields.title || item.ref.refId;
208
- else title = fields.title || item.ref.refId;
209
- const status = family.kind === "decision" ? statusBadge(t, "decision", "decision", item.status)
210
- : family.kind === "run" ? statusBadge(t, "run", "run", item.status)
211
- : family.kind === "question" ? statusBadge(t, "input", "inputStatus", item.status) : null;
212
- const head = h("div.timeline-summary", null, h("header.timeline-head", null, h("strong.timeline-title", null, title),
213
- family.kind === "run" ? mono(item.ref.refId) : null,
214
- item.workItemId ? chip(item.workItemId) : null,
215
- item.history === "retired" ? chip(t("history.retired")) : null,
216
- item.status ? status : null, h("span.spacer"), timeTag(item.createdAt, locale, t)));
217
- const preview = value && family.kind === "decision" ? value.rationale : fields.summary !== title ? fields.summary : null;
218
- if (preview) head.append(h("p.small.muted.timeline-preview", null, preview));
219
- // The row opens in place; its exact record is read on first open.
220
- body.append(lazyRow(item, task.id, t, ctx, { cache: view.exact, openRows: view.openRows, head: head, render: function (exact, result) {
221
- if (family.kind === "run") return h("div.stack", null, runBody({ ...exact, execution: result && result.execution }, t, locale));
222
- if (family.kind === "decision") return h("div.stack", null,
223
- exact.rationale ? richText(t("record.rationale"), exact.rationale, t, { muted: true }) : null,
224
- exact.supersededReason ? richText(t("history.superseded"), exact.supersededReason, t, { muted: true }) : null);
225
- if (family.kind === "milestone") return h("div.stack", null, exact.summary ? richText(null, exact.summary, t) : null);
226
- if (family.kind === "question") {
227
- const answer = exact.resolution && exact.resolution.answer;
228
- return h("div.stack", null, exact.question ? richText(null, exact.question, t) : null,
229
- answer ? richText(t("history.answer") + " · " + label(t, "answeredBy", exact.resolution.answeredBy),
230
- answer.text || answer.choiceKey, t, { muted: true }) : null,
231
- exact.cancellation ? note(exact.cancellation.reason) : null);
232
- }
233
- return null;
234
- } }));
235
- element.append(body);
236
- return element;
237
- }
238
-
239
- // Reads redraw whichever Records panel is current.
240
- function refresh() {
241
- const current = view.panels.records;
242
- if (current && current.isConnected) current.redraw();
243
- }
244
- function loadFull(family, key) {
245
- return loadFamily(view, task.id, family.store, key, ctx, { limit: 40 }).then(function (state) {
246
- if (!family.ascending || !state.nextCursor || state.pages >= FULL_PAGES || state.error) return state;
247
- return loadFamily(view, task.id, family.store, key, ctx, { more: true, limit: 40 }).then(function () { return loadFull(family, key); });
248
- });
249
- }
250
- // A stale first page re-reads silently; once the user paged further, the
251
- // read waits for an explicit refresh so their position is kept.
252
- function show(force) {
253
- const key = core.coreCursor;
254
- TIMELINE.forEach(function (family) {
255
- const state = familyState(view, family.store);
256
- if (state.pending || state.key === key || (!force && state.errorKey === key)) return;
257
- if (!force && state.key && !family.ascending && state.pages > 1) return;
258
- loadFull(family, key).then(refresh);
259
- });
260
- refresh();
261
- }
262
- panel.redraw = draw;
263
- panel.onShow = function () { show(false); };
264
- draw();
265
- panel.append(history);
266
-
267
- // --- Task information --------------------------------------------------------
268
- const facts = card({ id: "detail-facts", title: t("details.facts"), icon: "info" });
269
- facts.body.append(kv([
270
- [t("details.id"), mono(task.id)],
271
- [t("details.status"), statusBadge(t, "task", "status", task.status)],
272
- [t("details.priority"), task.priority ? label(t, "priority", task.priority) : null],
273
- [t("details.tags"), (task.tags || []).length ? h("span.chip-row", null, task.tags.map(function (tag) { return chip(tag); })) : null],
274
- [t("details.projects"), (task.projectBindings || []).length ? h("span.chip-row", null, task.projectBindings.map(function (b) { return chip(b.projectId); })) : null],
275
- [t("details.created"), formatDateTime(task.createdAt, locale)],
276
- [t("details.updated"), formatDateTime(task.updatedAt, locale)],
277
- [t("details.workspace"), task.cwd ? mono(task.cwd) : null]
278
- ]));
279
- facts.body.append(h("div.prose-block", null, h("h4.prose-label", null, t("details.title")), titleForm(task, t, ctx)));
280
- panel.append(facts);
281
-
282
- // --- Advanced -------------------------------------------------------------------
283
- const advanced = disclosure(t("advanced.title"), "advanced", { className: "card-disclosure", meta: t("advanced.meta") });
284
- advanced.id = "detail-advanced";
285
- advanced.body.append(h("h4.sub-head", null, t("details.context")), h("pre.code-block", null, JSON.stringify({
286
- coreCursor: core.coreCursor, throughCursor: core.throughCursor, count: core.count, collections: core.collections, omitted: core.omitted
287
- }, null, 2)), note(t("details.contextHelp")));
288
- const panels = h("div.stack");
289
- advanced.body.append(h("h4.sub-head", null, t("panels.title")), panels);
290
- let loading = false;
291
- let loadedPanels = false;
292
- advanced.addEventListener("toggle", async function () {
293
- if (!advanced.open || loading || loadedPanels) return;
294
- loading = true;
295
- clear(panels);
296
- panels.append(h("p.faint", null, t("panels.reading")));
297
- try {
298
- const current = await ctx.panels(task.id);
299
- clear(panels);
300
- panels.append(h("p.faint.small", null, t("panels.observed") + " " + formatDateTime(current.observedAt, locale)));
301
- if (!current.panels.length) panels.append(emptyState(t("panels.none")));
302
- current.panels.forEach(function (item) { panels.append(panelCard(item, task, t, ctx)); });
303
- loadedPanels = true;
304
- } catch (error) {
305
- clear(panels);
306
- panels.append(note(t("panels.unavailable") + " " + error.message, "bad"));
307
- } finally { loading = false; }
308
- });
309
- panel.append(advanced);
310
- }
311
-
312
- function panelCard(item, task, t, ctx) {
313
- const panel = item.panel;
314
- const element = h("article.record", null,
315
- h("header.record-head", null, h("strong", null, panel.title), h("span.spacer"), h("span.faint.small", null, item.capability + " · " + item.provider.id)));
316
- if (item.unavailable) { element.append(note(item.unavailable, "warn")); return element; }
317
- if (panel.kind === "text") element.append(h("p.small", null, panel.text));
318
- else if (panel.kind === "link") {
319
- try {
320
- const url = new URL(panel.href);
321
- if (["http:", "https:"].includes(url.protocol) && !url.username && !url.password) {
322
- element.append(h("a.link", { href: url.href, rel: "noopener noreferrer", target: "_blank" }, panel.title));
323
- }
324
- } catch {}
325
- } else if (panel.kind === "data" && panel.renderer === "json") {
326
- const input = h("textarea.mono", { rows: 3 });
327
- input.value = JSON.stringify({ taskId: task.id });
328
- const schema = disclosure(t("panels.schema"), null);
329
- schema.body.append(h("pre.code-block", null, JSON.stringify(item.inputSchema, null, 2)));
330
- const output = h("pre.code-block");
331
- output.hidden = true;
332
- const read = button(t("panels.read"), { icon: "eye", variant: "ghost" });
333
- read.addEventListener("click", async function () {
334
- if (read.disabled) return;
335
- read.disabled = true;
336
- output.hidden = false;
337
- try {
338
- const result = await ctx.readPanel(task.id, {
339
- capability: item.capability, contractVersion: item.contractVersion, provider: item.provider
340
- }, JSON.parse(input.value));
341
- output.textContent = JSON.stringify(result, null, 2);
342
- } catch (error) { output.textContent = t("panels.unavailable") + " " + error.message; }
343
- finally { read.disabled = false; }
344
- });
345
- element.append(h("label.field", null, h("span", null, t("panels.input")), input), schema, h("div.form-actions", null, read), output);
346
- }
347
- return element;
348
- }
349
- `;