@zq-silk/yui 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/README.md +19 -3
  2. package/dist/artifacts/artifactCapability.js +39 -10
  3. package/dist/artifacts/imageMetadata.js +120 -0
  4. package/dist/artifacts/managedGit.js +7 -4
  5. package/dist/artifacts/taskArtifactRepository.js +6 -0
  6. package/dist/cli/commandCatalog.js +25 -11
  7. package/dist/cli/interactionPolicy.js +4 -0
  8. package/dist/cli/invocationAuthority.js +1 -1
  9. package/dist/commands/executionAuditCommands.js +5 -0
  10. package/dist/commands/globalRoleCommands.js +41 -3
  11. package/dist/commands/projectCommands.js +89 -94
  12. package/dist/commands/roleSkillValidation.js +4 -8
  13. package/dist/commands/taskCommands.js +30 -9
  14. package/dist/commands/taskIntegrationCommands.js +5 -16
  15. package/dist/commands/taskUsageCommand.js +38 -0
  16. package/dist/context/roleSessionContext.js +18 -2
  17. package/dist/context/runContextPack.js +5 -1
  18. package/dist/context/sessionBootstrapManifest.js +10 -1
  19. package/dist/context/skillCatalog.js +108 -0
  20. package/dist/context/skillPackage.js +161 -0
  21. package/dist/context/taskContext.js +3 -2
  22. package/dist/context/taskSearch.js +127 -0
  23. package/dist/controlPlaneCli.js +51 -39
  24. package/dist/controller/clientRuntime.js +3 -2
  25. package/dist/controller/fileSchedulerStoreAdapter.js +32 -6
  26. package/dist/controller/jobControl.js +2 -0
  27. package/dist/controller/nativeHostInput.js +37 -0
  28. package/dist/controller/runtime.js +93 -2
  29. package/dist/controller/runtimeEventInbox.js +2 -2
  30. package/dist/controller/runtimeEventProcessor.js +3 -3
  31. package/dist/doctor/doctor.js +10 -0
  32. package/dist/execution/reviewMainRun.js +1 -0
  33. package/dist/execution/workItemMainRun.js +1 -0
  34. package/dist/executor/codexConfigConflict.js +6 -4
  35. package/dist/executor/effectiveLaunch.js +24 -1
  36. package/dist/executor/fileRoleLaunchPlanner.js +13 -5
  37. package/dist/integration/gitIntegrationService.js +15 -79
  38. package/dist/integration/integrationSourceApplication.js +4 -2
  39. package/dist/job/httpPreview.js +61 -0
  40. package/dist/job/jobCleanupInspection.js +67 -0
  41. package/dist/job/jobRunner.js +12 -2
  42. package/dist/kernel/builtinCapabilities.js +3 -1
  43. package/dist/message/message.js +15 -1
  44. package/dist/output/rolePresentation.js +4 -1
  45. package/dist/repository/executionLaneGitSnapshot.js +2 -1
  46. package/dist/repository/gitCompatibility.js +62 -0
  47. package/dist/repository/gitWorkspace.js +86 -80
  48. package/dist/repository/gitWorktreeInventory.js +121 -0
  49. package/dist/repository/project.js +74 -25
  50. package/dist/repository/taskWorkspaceCoordinator.js +72 -145
  51. package/dist/repository/taskWorkspacePreparer.js +39 -43
  52. package/dist/repository/workspaceCleanupInspection.js +88 -7
  53. package/dist/resources/resourceDiscovery.js +2 -2
  54. package/dist/resources/resourceGc.js +7 -4
  55. package/dist/resources/resourceRegistrar.js +18 -11
  56. package/dist/runtime/agentEndpoint.js +8 -0
  57. package/dist/runtime/agentHost.js +170 -3
  58. package/dist/runtime/codexAppServerRuntime.js +1 -1
  59. package/dist/runtime/codexNativeAccess.js +175 -0
  60. package/dist/runtime/controlledCodexTui.js +116 -0
  61. package/dist/runtime/integrationRuntimeIsolation.js +21 -0
  62. package/dist/runtime/publicReply.js +23 -0
  63. package/dist/runtime/runtimeObservation.js +48 -0
  64. package/dist/runtime/structuredProviderHost.js +16 -7
  65. package/dist/runtime/taskCostMetrics.js +133 -0
  66. package/dist/runtime/taskRuntimeIsolation.js +91 -21
  67. package/dist/runtime/taskUsageMetrics.js +11 -2
  68. package/dist/runtime/taskUsageQuery.js +38 -0
  69. package/dist/scheduler/taskExecutionProjection.js +2 -0
  70. package/dist/scheduler/taskObservabilityProjection.js +9 -1
  71. package/dist/storage/homeLayout.js +13 -0
  72. package/dist/storage/sqliteSchema.js +44 -0
  73. package/dist/storage/sqliteStore.js +53 -0
  74. package/dist/storage/storageVersions.js +1 -1
  75. package/dist/storage/storeRpc.js +2 -0
  76. package/dist/storage/taskSearch.js +35 -0
  77. package/dist/storage/upgrade/projectKnowledgeMigration.js +52 -0
  78. package/dist/task/archiveDiagnostics.js +26 -8
  79. package/dist/task/archivePreflight.js +26 -9
  80. package/dist/task/leaderArchive.js +9 -15
  81. package/dist/task/ordinaryArchive.js +42 -0
  82. package/dist/tmux/tmuxManager.js +8 -8
  83. package/dist/web/assets/assetManifest.js +11 -51
  84. package/dist/web/assets/client/app/catalog.js +215 -0
  85. package/dist/web/assets/client/app/main.js +148 -0
  86. package/dist/web/assets/client/app/selection.js +139 -0
  87. package/dist/web/assets/client/app/settings.js +326 -0
  88. package/dist/web/assets/client/app/taskView.js +280 -0
  89. package/dist/web/assets/client/domain/context.js +96 -0
  90. package/dist/web/assets/client/domain/records.js +126 -0
  91. package/dist/web/assets/client/domain/runs.js +119 -0
  92. package/dist/web/assets/client/domain/taskForms.js +197 -0
  93. package/dist/web/assets/client/domain/vocab.js +69 -0
  94. package/dist/web/assets/client/domain/work.js +116 -0
  95. package/dist/web/assets/client/i18n/en.js +900 -0
  96. package/dist/web/assets/client/i18n/zh-CN.js +898 -0
  97. package/dist/web/assets/client/index.js +114 -0
  98. package/dist/web/assets/client/layout/resizer.js +37 -0
  99. package/dist/web/assets/client/layout/router.js +35 -0
  100. package/dist/web/assets/client/layout/shortcuts.js +45 -0
  101. package/dist/web/assets/client/layout/sizing.js +95 -0
  102. package/dist/web/assets/client/layout/workspace.js +247 -0
  103. package/dist/web/assets/client/{api.js → lib/api.js} +28 -4
  104. package/dist/web/assets/client/{dom.js → lib/dom.js} +24 -12
  105. package/dist/web/assets/client/{format.js → lib/format.js} +7 -0
  106. package/dist/web/assets/client/lib/i18n.js +54 -0
  107. package/dist/web/assets/client/lib/markdown.js +167 -0
  108. package/dist/web/assets/client/lib/prefs.js +28 -0
  109. package/dist/web/assets/client/lib/theme.js +68 -0
  110. package/dist/web/assets/client/ui/containers.js +101 -0
  111. package/dist/web/assets/client/ui/controls.js +51 -0
  112. package/dist/web/assets/client/ui/forms.js +48 -0
  113. package/dist/web/assets/client/ui/metrics.js +137 -0
  114. package/dist/web/assets/client/ui/primitives.js +92 -0
  115. package/dist/web/assets/client/ui/text.js +33 -0
  116. package/dist/web/assets/client/ui/toast.js +12 -0
  117. package/dist/web/assets/client/views/bodySearch.js +172 -0
  118. package/dist/web/assets/client/views/createTask.js +85 -0
  119. package/dist/web/assets/client/views/dock/conversation.js +612 -0
  120. package/dist/web/assets/client/views/dock/discussion.js +173 -0
  121. package/dist/web/assets/client/views/dock/preview.js +129 -0
  122. package/dist/web/assets/client/views/dock/terminal.js +199 -0
  123. package/dist/web/assets/client/views/globalInput.js +67 -0
  124. package/dist/web/assets/client/{overview.js → views/overview.js} +62 -56
  125. package/dist/web/assets/client/{sidebar.js → views/sidebar.js} +56 -62
  126. package/dist/web/assets/client/views/skills.js +152 -0
  127. package/dist/web/assets/client/views/task/actions.js +36 -0
  128. package/dist/web/assets/client/views/task/delivery.js +185 -0
  129. package/dist/web/assets/client/views/task/deliveryResults.js +159 -0
  130. package/dist/web/assets/client/views/task/files.js +272 -0
  131. package/dist/web/assets/client/views/task/observation.js +153 -0
  132. package/dist/web/assets/client/views/task/overview.js +161 -0
  133. package/dist/web/assets/client/views/task/page.js +131 -0
  134. package/dist/web/assets/client/views/task/records.js +163 -0
  135. package/dist/web/assets/client/views/task/runtime.js +130 -0
  136. package/dist/web/assets/client/views/task/timeline.js +190 -0
  137. package/dist/web/assets/shared/geometry.js +19 -0
  138. package/dist/web/assets/{icons.js → shared/icons.js} +4 -0
  139. package/dist/web/assets/shared/taskTabs.js +8 -0
  140. package/dist/web/assets/shared/themes.js +93 -0
  141. package/dist/web/assets/shell/dialogs.js +63 -0
  142. package/dist/web/assets/shell/dock.js +40 -0
  143. package/dist/web/assets/shell/index.js +40 -0
  144. package/dist/web/assets/shell/settings.js +34 -0
  145. package/dist/web/assets/shell/sidebar.js +45 -0
  146. package/dist/web/assets/styles/index.js +41 -0
  147. package/dist/web/assets/styles/{responsive.js → layout/responsive.js} +15 -13
  148. package/dist/web/assets/styles/{layout.js → layout/workspace.js} +17 -24
  149. package/dist/web/assets/styles/markdown.js +22 -3
  150. package/dist/web/assets/styles/tokens.js +10 -63
  151. package/dist/web/assets/styles/ui/controls.js +76 -0
  152. package/dist/web/assets/styles/ui/forms.js +46 -0
  153. package/dist/web/assets/styles/ui/surfaces.js +122 -0
  154. package/dist/web/assets/styles/views/delivery.js +29 -0
  155. package/dist/web/assets/styles/views/dock.js +71 -0
  156. package/dist/web/assets/styles/views/page.js +32 -0
  157. package/dist/web/assets/styles/views/records.js +70 -0
  158. package/dist/web/assets/styles/views/settings.js +26 -0
  159. package/dist/web/assets/styles/views/sidebar.js +74 -0
  160. package/dist/web/assets/styles/views/task.js +80 -0
  161. package/dist/web/assets/styles/views/terminal.js +38 -0
  162. package/dist/web/taskPreviews.js +205 -0
  163. package/dist/web/tmuxWebTerminal.js +16 -9
  164. package/dist/web/webConversation.js +373 -0
  165. package/dist/web/webMaterials.js +155 -0
  166. package/dist/web/webServer.js +306 -18
  167. package/dist/web/webSettings.js +331 -0
  168. package/dist/web/webSkills.js +86 -0
  169. package/dist/web/webSnapshot.js +40 -8
  170. package/dist/web/webTaskSurface.js +114 -9
  171. package/dist/workspace/gitChangeSetCapture.js +3 -1
  172. package/docs/managed-turn-and-session-runtime.md +9 -0
  173. package/docs/managed-turn-and-session-runtime.zh-CN.md +7 -0
  174. package/docs/observability/README.md +13 -3
  175. package/docs/observability/README.zh-CN.md +10 -3
  176. package/docs/release-workflow.md +53 -0
  177. package/docs/release-workflow.zh-CN.md +40 -0
  178. package/docs/storage-baseline.md +38 -1
  179. package/docs/storage-baseline.zh-CN.md +22 -0
  180. package/docs/task-delivery.md +36 -8
  181. package/docs/task-delivery.zh-CN.md +24 -7
  182. package/docs/task-discovery.md +28 -0
  183. package/docs/testing/verification-levels.md +2 -2
  184. package/i18n/README.zh-CN.md +4 -3
  185. package/package.json +1 -1
  186. package/skills/yui-leader/references/execution.md +2 -1
  187. package/skills/yui-leader/references/integration.md +20 -0
  188. package/skills/yui-operator/SKILL.md +4 -0
  189. package/skills/yui-reviewer/SKILL.md +3 -0
  190. package/skills/yui-runtime/SKILL.md +4 -0
  191. package/skills/yui-runtime/references/publication.md +20 -4
  192. package/skills/yui-worker/SKILL.md +3 -0
  193. package/dist/web/assets/client/app.js +0 -804
  194. package/dist/web/assets/client/components.js +0 -418
  195. package/dist/web/assets/client/detail.js +0 -333
  196. package/dist/web/assets/client/dock.js +0 -225
  197. package/dist/web/assets/client/evidence.js +0 -172
  198. package/dist/web/assets/client/forms.js +0 -230
  199. package/dist/web/assets/client/i18n.js +0 -1161
  200. package/dist/web/assets/client/markdown.js +0 -114
  201. package/dist/web/assets/client/records.js +0 -63
  202. package/dist/web/assets/client/sections.js +0 -313
  203. package/dist/web/assets/client/theme.js +0 -71
  204. package/dist/web/assets/shell.js +0 -161
  205. package/dist/web/assets/styles/components.js +0 -169
  206. package/dist/web/assets/styles/views.js +0 -292
@@ -0,0 +1,137 @@
1
+ export const METRICS_SCRIPT = String.raw `
2
+ // Metric tiles and the usage card. DOM is built with node() only, so the
3
+ // usage test can execute this module with node() and formatDateTime() alone.
4
+ import { node } from "/assets/js/lib/dom.js";
5
+ import { formatDateTime } from "/assets/js/lib/format.js";
6
+ import { requestJson } from "/assets/js/lib/api.js";
7
+
8
+ export function metricTile(labelText, value, options) {
9
+ const variant = options && options.tone ? " tone-" + options.tone : "";
10
+ const tile = node("div", "metric" + variant);
11
+ tile.append(node("span", "metric-label", labelText), node("strong", "metric-value", String(value)));
12
+ return tile;
13
+ }
14
+
15
+ function pageLocale() { return typeof document === "undefined" ? undefined : document.documentElement.lang || undefined; }
16
+
17
+ // Seconds as the largest whole unit plus the next one ("2d 22h", "2小时48分钟").
18
+ const DURATION_UNITS = [["day", 86400], ["hour", 3600], ["minute", 60], ["second", 1]];
19
+ function durationText(seconds) {
20
+ const total = Math.max(0, Math.round(seconds));
21
+ const found = DURATION_UNITS.findIndex(function (unit) { return total >= unit[1]; });
22
+ const first = found < 0 ? DURATION_UNITS.length - 1 : found;
23
+ return DURATION_UNITS.slice(first, first + 2).map(function (unit, index) {
24
+ const count = index ? Math.floor((total % DURATION_UNITS[first][1]) / unit[1]) : Math.floor(total / unit[1]);
25
+ return index && !count ? "" : new Intl.NumberFormat(pageLocale(), { style: "unit", unit: unit[0], unitDisplay: "narrow" }).format(count);
26
+ }).filter(Boolean).join(" ");
27
+ }
28
+
29
+ export function usageMetricText(metric, t, suffix) {
30
+ if (!metric || metric.value == null) return t("detail.unobserved");
31
+ const value = suffix === "s" ? durationText(metric.value) : metric.value + (suffix || "");
32
+ return value + (metric.status === "partial" ? " · " + t("detail.partial") : "");
33
+ }
34
+
35
+ function usageTile(labelText, metric, t, suffix) {
36
+ const tile = metricTile(labelText, usageMetricText(metric, t, suffix));
37
+ if (metric && metric.reasons && metric.reasons.length) {
38
+ tile.append(node("small", "metric-note", metric.reasons.map(function (reason) {
39
+ return t("usage.reason." + reason);
40
+ }).join("; ")));
41
+ }
42
+ return tile;
43
+ }
44
+
45
+ export function observabilityMetricCard(observability, t, viewState) {
46
+ if (!observability) return null;
47
+ const wrap = node("div", "usage");
48
+ const grid = node("div", "metric-grid");
49
+ const cost = observability.cost || {};
50
+ const context = observability.context || {};
51
+ grid.append(usageTile(t("usage.tokens"), cost.tokens, t));
52
+ grid.append(usageTile(t("usage.toolCalls"), cost.toolCalls, t));
53
+ grid.append(usageTile(t("usage.elapsed"), cost.elapsedSeconds, t, "s"));
54
+ grid.append(usageTile(t("usage.executionSum"), cost.executionSeconds, t, "s"));
55
+ ["actual", "estimated"].forEach(function (kind) {
56
+ const money = cost.costs && cost.costs[kind];
57
+ grid.append(metricTile(t("usage." + kind), !money || !money.amounts.length
58
+ ? t("detail.unobserved")
59
+ : money.amounts.map(function (amount) { return usageMetricText(amount, t) + " " + amount.currency; }).join("; ")));
60
+ });
61
+ grid.append(metricTile(t("usage.ready"), observability.readyCount === undefined
62
+ ? ((observability.dag && observability.dag.readyIds) || []).length : observability.readyCount));
63
+ grid.append(metricTile(t("usage.contextSnapshots"), context.snapshotCount == null ? t("detail.unobserved") : context.snapshotCount));
64
+ wrap.append(grid);
65
+ const meta = node("p", "usage-meta");
66
+ meta.append(node("span", "", t("usage.scope")));
67
+ meta.append(node("span", "", t("usage.observedThrough") + " "
68
+ + (cost.observedThrough ? formatDateTime(cost.observedThrough, pageLocale()) : t("detail.unobserved"))));
69
+ meta.append(node("span", "", t("usage.contextBytes") + " "
70
+ + (context.totalBytes == null ? t("detail.partial") : context.totalBytes + " B")));
71
+ wrap.append(meta);
72
+ wrap.append(node("p", "usage-meta", t("usage.notBill")));
73
+ if (cost.history && !cost.history.complete) wrap.append(node("p", "metric-note", t("usage.reason.usage-history-limited")));
74
+ if (cost.taskId && typeof document !== "undefined") {
75
+ const details = node("details", "usage-details");
76
+ details.dataset.viewKey = "usage-sources";
77
+ details.append(node("summary", "", t("usage.sources")));
78
+ const body = node("div", "stack");
79
+ const more = node("button", "", t("usage.loadDetails"));
80
+ more.type = "button";
81
+ const cache = viewState || {};
82
+ const state = cache.usageDetails || (cache.usageDetails = { pages: [], offset: 0, reading: false });
83
+ function drawPages() {
84
+ body.replaceChildren();
85
+ state.pages.forEach(function (page) {
86
+ body.append(node("p", "metric-note", t("usage.observedThrough") + " "
87
+ + (page.observedThrough ? formatDateTime(page.observedThrough, pageLocale()) : t("detail.unobserved"))));
88
+ ["actual", "estimated"].forEach(function (kind) {
89
+ page.costs[kind].evidence.forEach(function (entry) {
90
+ const basis = entry.basis;
91
+ body.append(node("p", "small mono-line", t("usage." + kind) + " · "
92
+ + usageMetricText(entry.amount, t) + " " + entry.currency + " · " + entry.source
93
+ + " · " + entry.roleName + "/" + entry.nativeSessionId + " · " + entry.semantics
94
+ + (basis ? " · " + basis.model + " · " + basis.source + "@" + basis.version
95
+ + " · " + basis.scope + " · " + t("usage.excluded") + ": " + basis.excluded.join(", ") : "")));
96
+ });
97
+ });
98
+ page.sessions.forEach(function (entry) {
99
+ body.append(node("p", "small mono-line", entry.identity.roleName + "/" + entry.identity.nativeSessionId
100
+ + " · " + usageMetricText(entry.tokens, t) + " · " + entry.sources.join(", ")
101
+ + " · " + entry.semantics.join(", ")));
102
+ });
103
+ if (!page.details.sessionTotal && !page.details.actualTotal && !page.details.estimatedTotal) {
104
+ body.append(node("p", "small", t("usage.unavailable")));
105
+ }
106
+ });
107
+ more.hidden = state.offset === null;
108
+ more.disabled = state.reading;
109
+ details.dataset.reading = String(state.reading);
110
+ }
111
+ async function load() {
112
+ if (state.reading || state.offset === null) return;
113
+ state.reading = true;
114
+ drawPages();
115
+ try {
116
+ const page = await requestJson("/api/tasks/" + encodeURIComponent(cost.taskId)
117
+ + "/usage?limit=20&offset=" + state.offset);
118
+ state.pages.push(page);
119
+ state.offset = page.details.nextOffset;
120
+ drawPages();
121
+ } catch (error) {
122
+ body.append(node("p", "metric-note", String(error.message || error)));
123
+ } finally {
124
+ state.reading = false;
125
+ more.disabled = false;
126
+ details.dataset.reading = "false";
127
+ }
128
+ }
129
+ drawPages();
130
+ more.addEventListener("click", load);
131
+ details.addEventListener("toggle", function () { if (details.open && !state.pages.length) load(); });
132
+ details.append(body, more);
133
+ wrap.append(details);
134
+ }
135
+ return wrap;
136
+ }
137
+ `;
@@ -0,0 +1,92 @@
1
+ export const PRIMITIVES_SCRIPT = String.raw `
2
+ // Leaf builders: data in, one small DOM element out. Nothing here reads
3
+ // application state, translates domain values or performs a request.
4
+ import { h, icon } from "/assets/js/lib/dom.js";
5
+ import { formatDateTime, relativeTime } from "/assets/js/lib/format.js";
6
+
7
+ export function badge(text, toneName, options) {
8
+ const element = h("span.badge.tone-" + (toneName || "idle"), null,
9
+ options && options.dot ? h("i.badge-dot", { "aria-hidden": "true" }) : null, text);
10
+ if (options && options.title) element.title = options.title;
11
+ return element;
12
+ }
13
+
14
+ export function dot(toneName, title) {
15
+ const element = h("span.dot.tone-" + (toneName || "idle"), { "aria-hidden": title ? null : "true" });
16
+ if (title) { element.title = title; element.setAttribute("role", "img"); element.setAttribute("aria-label", title); }
17
+ return element;
18
+ }
19
+
20
+ export function chip(text, extraClass) {
21
+ return h("span.chip" + (extraClass ? "." + extraClass : ""), null, text);
22
+ }
23
+
24
+ export function mono(text, extraClass) {
25
+ return h("code.id" + (extraClass ? "." + extraClass : ""), null, text);
26
+ }
27
+
28
+ export function timeTag(iso, locale, t, options) {
29
+ if (!iso) return null;
30
+ const element = h("time.when", { dateTime: iso }, options && options.absolute
31
+ ? formatDateTime(iso, locale) : relativeTime(iso, locale, t));
32
+ element.title = formatDateTime(iso, locale);
33
+ return element;
34
+ }
35
+
36
+ export function button(text, options) {
37
+ const opts = options || {};
38
+ const element = h("button.btn" + (opts.variant ? ".btn-" + opts.variant : ""), {
39
+ type: opts.type || "button", onclick: opts.onClick, disabled: !!opts.disabled, title: opts.title
40
+ }, opts.icon ? icon(opts.icon) : null, text ? h("span", null, text) : null);
41
+ if (opts.dataset) Object.assign(element.dataset, opts.dataset);
42
+ return element;
43
+ }
44
+
45
+ export function note(text, toneName) {
46
+ return h("p.note" + (toneName ? ".tone-" + toneName : ""), null, text);
47
+ }
48
+
49
+ export function emptyState(text, iconName, extra) {
50
+ return h("div.empty", null, iconName ? icon(iconName) : null, h("p", null, text), extra || null);
51
+ }
52
+
53
+ export function loadingBlock(text) {
54
+ return h("div.loading-block", null, h("span.spinner"), h("span", null, text));
55
+ }
56
+
57
+ export function kv(rows) {
58
+ const list = h("dl.kv");
59
+ rows.forEach(function (row) {
60
+ if (!row || row[1] === undefined || row[1] === null || row[1] === "") return;
61
+ list.append(h("dt", null, row[0]), h("dd", null, row[1]));
62
+ });
63
+ return list;
64
+ }
65
+
66
+ export function bulletList(items, className) {
67
+ const list = (items || []).filter(Boolean);
68
+ if (!list.length) return null;
69
+ return h("ul.bullets" + (className ? "." + className : ""), null, list.map(function (item) { return h("li", null, item); }));
70
+ }
71
+
72
+ // Verbatim text (a command, an exact record) in a scrollable block.
73
+ export function codeBlock(text) {
74
+ return h("pre.code-block", null, text);
75
+ }
76
+
77
+ export function jsonBlock(value) {
78
+ return codeBlock(JSON.stringify(value, null, 2));
79
+ }
80
+
81
+ // Only http(s) links without credentials leave the page.
82
+ export function safeUrl(value) {
83
+ try {
84
+ const url = new URL(value);
85
+ return ["http:", "https:"].includes(url.protocol) && !url.username && !url.password ? url.href : null;
86
+ } catch { return null; }
87
+ }
88
+
89
+ export function externalLink(href, text) {
90
+ return h("a.link", { href: href, rel: "noopener noreferrer", target: "_blank" }, text);
91
+ }
92
+ `;
@@ -0,0 +1,33 @@
1
+ export const TEXT_SCRIPT = String.raw `
2
+ // Agent and user prose: rendered Markdown that collapses when long.
3
+ import { h } from "/assets/js/lib/dom.js";
4
+ import { renderMarkdown } from "/assets/js/lib/markdown.js";
5
+
6
+ function shouldCollapse(text, threshold) {
7
+ const value = String(text);
8
+ if (value.length > (threshold || 700)) return true;
9
+ let lines = 0;
10
+ for (let index = 0; index < value.length; index += 1) if (value[index] === "\n") lines += 1;
11
+ return lines > 12;
12
+ }
13
+
14
+ export function richText(title, text, t, options) {
15
+ if (!text) return null;
16
+ const opts = options || {};
17
+ const block = h("div.prose-block" + (opts.className ? "." + opts.className : ""));
18
+ if (title) block.append(h("h4.prose-label", null, title));
19
+ const body = h("div.md" + (opts.muted ? ".muted" : ""));
20
+ body.innerHTML = renderMarkdown(text);
21
+ block.append(body);
22
+ if (shouldCollapse(text, opts.threshold)) {
23
+ block.classList.add("is-collapsed");
24
+ const toggle = h("button.link-btn", { type: "button" }, t("actions.showMore"));
25
+ toggle.addEventListener("click", function () {
26
+ const collapsed = block.classList.toggle("is-collapsed");
27
+ toggle.textContent = t(collapsed ? "actions.showMore" : "actions.showLess");
28
+ });
29
+ block.append(toggle);
30
+ }
31
+ return block;
32
+ }
33
+ `;
@@ -0,0 +1,12 @@
1
+ export const TOAST_SCRIPT = String.raw `
2
+ // A transient status message; a newer message replaces the current one.
3
+ export function createToast(element) {
4
+ let timer = null;
5
+ return function showToast(message) {
6
+ element.textContent = message;
7
+ element.classList.add("show");
8
+ window.clearTimeout(timer);
9
+ timer = window.setTimeout(function () { element.classList.remove("show"); }, 3200);
10
+ };
11
+ }
12
+ `;
@@ -0,0 +1,172 @@
1
+ export const BODY_SEARCH_SCRIPT = String.raw `
2
+ import { h } from "/assets/js/lib/dom.js";
3
+ import { button } from "/assets/js/ui/primitives.js";
4
+
5
+ // Textareas normalize CR/CRLF to LF. Map their UTF-16 selections back to the
6
+ // unchanged source before computing the search contract's code-point offset.
7
+ export function selectedSearchText(page, body) {
8
+ if (body.selectionStart === body.selectionEnd) return { excerpt: page.content, offset: page.offset };
9
+ let visible = 0, start, end;
10
+ for (let index = 0; index <= page.content.length;) {
11
+ if (visible === body.selectionStart) start = index;
12
+ if (visible === body.selectionEnd) { end = index; break; }
13
+ if (index === page.content.length) break;
14
+ const point = String.fromCodePoint(page.content.codePointAt(index));
15
+ index += point === "\r" && page.content[index + 1] === "\n" ? 2 : point.length;
16
+ visible += point.length;
17
+ }
18
+ if (start === undefined || end === undefined) throw new Error("Select complete characters before quoting.");
19
+ return { excerpt: page.content.slice(start, end),
20
+ offset: page.offset + Array.from(page.content.slice(0, start)).length };
21
+ }
22
+
23
+ // Search never sends input or starts a Task. The user/Operator chooses excerpts
24
+ // to reuse through the normal Task input path with its own authority.
25
+ export function bindBodySearch(options) {
26
+ const t = options.t, api = options.api;
27
+ const dialog = h("dialog.dialog.body-search-dialog", { "aria-labelledby": "body-search-title" });
28
+ const close = button(t("actions.close"));
29
+ close.dataset.i18n = "actions.close";
30
+ close.addEventListener("click", function () { dialog.close(); });
31
+ const query = h("input", { type: "search", required: true, maxLength: 256 });
32
+ const scope = h("select", { "aria-label": t("bodySearch.scope") },
33
+ h("option", { value: "" }, t("bodySearch.allTasks")),
34
+ h("option", { value: "current" }, t("bodySearch.currentTask")));
35
+ const kind = h("select");
36
+ ["", "brief", "message", "decision", "result", "completion"].forEach(function (value) {
37
+ const key = "bodySearch.kind." + (value || "all");
38
+ kind.append(h("option", { value: value, dataset: { i18n: key } }, t(key)));
39
+ });
40
+ const submit = button(t("bodySearch.search"), { variant: "primary" });
41
+ submit.dataset.i18n = "bodySearch.search";
42
+ submit.type = "submit";
43
+ const status = h("p.receipt", { role: "status" });
44
+ const destination = h("p.dialog-sub");
45
+ const results = h("div.row-stack");
46
+ const next = button(t("catalog.next"));
47
+ next.dataset.i18n = "catalog.next";
48
+ next.disabled = true;
49
+ const form = h("form.dialog-body", null,
50
+ h("header.dialog-head", null, h("h2", { id: "body-search-title", dataset: { i18n: "bodySearch.title" } }, t("bodySearch.title")), close),
51
+ h("p.dialog-sub", { dataset: { i18n: "bodySearch.help" } }, t("bodySearch.help")),
52
+ h("label.field", null, h("span", { dataset: { i18n: "bodySearch.query" } }, t("bodySearch.query")), query),
53
+ h("label.field", null, h("span", { dataset: { i18n: "bodySearch.kind" } }, t("bodySearch.kind")), kind),
54
+ h("label.field", null, h("span", null, t("bodySearch.scope")), scope),
55
+ h("div.dialog-actions", null, submit, next), destination, status, results);
56
+ dialog.append(form);
57
+ document.body.append(dialog);
58
+ let cursor = null, searched = null, generation = 0, target = null, currentTask = null;
59
+ function invalidate() {
60
+ generation += 1; cursor = null; searched = null; next.disabled = true;
61
+ submit.disabled = false; results.replaceChildren(); status.textContent = "";
62
+ }
63
+ query.addEventListener("input", invalidate);
64
+ kind.addEventListener("change", invalidate);
65
+ scope.addEventListener("change", invalidate);
66
+ dialog.addEventListener("close", invalidate);
67
+ async function load(more) {
68
+ const ticket = ++generation;
69
+ const parameters = more ? new URLSearchParams(searched) : new URLSearchParams({ query: query.value.trim(), limit: "20" });
70
+ if (!more && kind.value) parameters.set("kind", kind.value);
71
+ if (!more && scope.value === "current") {
72
+ if (!currentTask) { status.textContent = t("bodySearch.noTask"); return; }
73
+ parameters.set("task", currentTask);
74
+ }
75
+ if (more && cursor) parameters.set("cursor", cursor);
76
+ submit.disabled = true; next.disabled = true; status.textContent = t("bodySearch.loading");
77
+ // One page in view: browsing large histories never accumulates all bodies.
78
+ results.replaceChildren();
79
+ try {
80
+ const page = await api.searchBodies(parameters);
81
+ if (ticket !== generation) return;
82
+ cursor = page.nextCursor;
83
+ parameters.delete("cursor"); searched = parameters.toString();
84
+ status.textContent = page.items.length ? t("bodySearch.results") + " " + page.items.length : t("bodySearch.empty");
85
+ page.items.forEach(function (item) {
86
+ const source = item.taskId + " / " + t("bodySearch.kind." + item.kind) + " / " + item.ref.refId + " / " + item.field;
87
+ const original = button(t("bodySearch.original"));
88
+ original.dataset.i18n = "bodySearch.original";
89
+ const locate = button(t("bodySearch.locate"));
90
+ locate.dataset.i18n = "bodySearch.locate";
91
+ locate.addEventListener("click", function () { dialog.close(); options.selectTask(item.taskId); });
92
+ const quote = button(t("bodySearch.quoteSnippet"));
93
+ quote.disabled = !target;
94
+ const feedback = h("p.receipt", { role: "status" });
95
+ const detail = h("div");
96
+ const row = h("article.card", null, h("div.card-body", null,
97
+ h("h3.card-title", null, source), h("pre.code-block.search-snippet", null, item.snippet),
98
+ h("p.faint.small", null, item.ref.revision + " · " + item.ref.digest),
99
+ h("div.dialog-actions", null, original, locate, quote), feedback, detail));
100
+ async function insert(excerpt, offset, control) {
101
+ control.disabled = true;
102
+ try {
103
+ const page = await api.searchSource(item, offset);
104
+ if (ticket !== generation) return;
105
+ if (!page.content.startsWith(excerpt)) throw new Error(t("bodySearch.sourceChanged"));
106
+ const ref = JSON.stringify({ authority: "reference-only", taskId: item.taskId,
107
+ kind: item.kind, ref: item.ref, field: item.field, offset: offset,
108
+ length: Array.from(excerpt).length, offsetUnit: "unicode-code-points" });
109
+ options.insertReference(target, t("bodySearch.referenceOnly") + "\n" + ref
110
+ + "\n\n" + excerpt.split("\n").map(function (line) { return "> " + line; }).join("\n"));
111
+ feedback.textContent = t("bodySearch.inserted");
112
+ } catch (error) { if (ticket === generation) feedback.textContent = error.message; }
113
+ finally { if (ticket === generation) control.disabled = !target; }
114
+ }
115
+ quote.addEventListener("click", function () { insert(item.snippet, item.offset, quote); });
116
+ async function read(offset) {
117
+ original.disabled = true;
118
+ detail.textContent = t("bodySearch.loading");
119
+ try {
120
+ const page = await api.searchSource(item, offset);
121
+ if (ticket !== generation) return;
122
+ const body = h("textarea.search-source", { readOnly: true, rows: 12,
123
+ "aria-label": t("bodySearch.sourceText") });
124
+ body.value = page.content;
125
+ const stage = button(t("bodySearch.quoteSelection"));
126
+ stage.disabled = !target || !page.content;
127
+ stage.addEventListener("click", function () {
128
+ try {
129
+ const selection = selectedSearchText(page, body);
130
+ insert(selection.excerpt, selection.offset, stage);
131
+ } catch (error) { feedback.textContent = error.message; }
132
+ });
133
+ const previous = button(t("bodySearch.previous"));
134
+ previous.disabled = page.offset === 0;
135
+ previous.addEventListener("click", function () { read(Math.max(0, page.offset - 4000)); });
136
+ const more = button(t("catalog.next"));
137
+ more.disabled = page.nextOffset === null;
138
+ more.addEventListener("click", function () { read(page.nextOffset); });
139
+ detail.replaceChildren(h("p.faint.small", null,
140
+ page.offset + "–" + (page.offset + Array.from(page.content).length) + " / " + page.totalCharacters),
141
+ body, h("div.dialog-actions", null, previous, more, stage));
142
+ } catch (error) {
143
+ if (ticket === generation) detail.textContent = t("bodySearch.unavailable") + " " + error.message;
144
+ } finally { if (ticket === generation) original.disabled = false; }
145
+ }
146
+ original.addEventListener("click", function () { read(0); });
147
+ results.append(row);
148
+ });
149
+ next.disabled = !cursor;
150
+ } catch (error) {
151
+ if (ticket !== generation) return;
152
+ cursor = null; searched = null;
153
+ status.textContent = t("bodySearch.failed") + " " + error.message;
154
+ } finally { if (ticket === generation) submit.disabled = false; }
155
+ }
156
+ form.addEventListener("submit", function (event) { event.preventDefault(); if (!submit.disabled) load(false); });
157
+ next.addEventListener("click", function () { if (cursor && searched) load(true); });
158
+ document.querySelector("#body-search-open").addEventListener("click", function () {
159
+ invalidate();
160
+ target = options.captureDraftTarget();
161
+ currentTask = options.currentTask();
162
+ if (!target) destination.textContent = t("bodySearch.noTarget");
163
+ else {
164
+ const identity = JSON.parse(target);
165
+ destination.textContent = t("bodySearch.destination") + " "
166
+ + (identity[0].taskId || identity[0].scope) + " / " + identity[0].roleName + " / " + identity[1];
167
+ }
168
+ dialog.showModal(); query.focus();
169
+ });
170
+ return { dialog: dialog };
171
+ }
172
+ `;
@@ -0,0 +1,85 @@
1
+ export const CREATE_TASK_SCRIPT = String.raw `
2
+ import { h } from "/assets/js/lib/dom.js";
3
+ import { button } from "/assets/js/ui/primitives.js";
4
+ import { field, receiptLine, setReceipt, submitWrite } from "/assets/js/ui/forms.js";
5
+ import { renderSubmissionFacets } from "/assets/js/domain/taskForms.js";
6
+
7
+ export function bindCreateTask(options) {
8
+ const t = options.t;
9
+ let dialog = null;
10
+ window.addEventListener("beforeunload", function (event) {
11
+ if (dialog && dialog.querySelector('[data-unsent="true"]')) {
12
+ event.preventDefault(); event.returnValue = "";
13
+ }
14
+ });
15
+ document.querySelector("#create-task").addEventListener("click", async function () {
16
+ if (dialog && dialog.dataset.saved === "true") { dialog.remove(); dialog = null; }
17
+ if (dialog) { dialog.showModal(); return; }
18
+ dialog = h("dialog.dialog", { "aria-label": t("create.title") });
19
+ const creationDialog = dialog;
20
+ const form = h("form.dialog-body");
21
+ const title = h("input", { required: true, maxLength: 200 });
22
+ const requirements = h("textarea", { required: true, rows: 6, maxLength: 8000 });
23
+ const projects = h("fieldset.stack", null, h("legend", null, t("create.projects")));
24
+ const plan = h("input", { type: "checkbox", checked: true });
25
+ const receipt = receiptLine(t("receipt.notSubmitted"));
26
+ const facets = h("div");
27
+ const submit = button(t("create.plan"), { type: "submit", variant: "primary", disabled: true });
28
+ const close = button(t("actions.close"), { onClick: function () { dialog.close(); } });
29
+ form.append(h("h2", null, t("create.title")), field(t("create.name"), title),
30
+ field(t("goal.requirements"), requirements), projects, field(t("create.plan"), plan, t("create.hint")),
31
+ submit, close, receipt, facets);
32
+ dialog.append(form);
33
+ document.body.append(dialog);
34
+ dialog.showModal();
35
+ const choices = [];
36
+ let projectsReady = false;
37
+ let cursor;
38
+ const more = button(t("catalog.next"), { onClick: function () { loadProjects(); } });
39
+ projects.append(more);
40
+ async function loadProjects() {
41
+ more.disabled = true;
42
+ try {
43
+ const result = await options.api.projects(cursor);
44
+ result.projects.forEach(function (project) {
45
+ const control = h("input", { type: "checkbox", value: project.id, disabled: creationDialog.dataset.saved === "true" });
46
+ choices.push(control);
47
+ projects.insertBefore(field(project.name + " · " + project.id, control), more);
48
+ });
49
+ cursor = result.nextCursor;
50
+ more.hidden = !cursor;
51
+ more.disabled = creationDialog.dataset.saved === "true";
52
+ if (!projectsReady) { submit.disabled = false; projectsReady = true; }
53
+ } catch (error) { setReceipt(receipt, error.message, "bad"); more.disabled = creationDialog.dataset.saved === "true"; }
54
+ }
55
+ await loadProjects();
56
+ plan.addEventListener("change", function () { submit.textContent = t(plan.checked ? "create.plan" : "create.only"); });
57
+ form.addEventListener("input", function () { form.dataset.unsent = "true"; });
58
+ form.addEventListener("submit", function (event) {
59
+ event.preventDefault();
60
+ if (submit.disabled) return;
61
+ submitWrite({
62
+ control: submit, receipt: receipt, form: form, t: t,
63
+ send: function (requestId) {
64
+ return options.api.createTask({ title: title.value.trim(), requirements: requirements.value.trim(),
65
+ projectIds: choices.filter(function (control) { return control.checked; }).map(function (control) { return control.value; }),
66
+ plan: plan.checked, requestId: requestId });
67
+ },
68
+ saved: function (result) {
69
+ creationDialog.dataset.saved = "true";
70
+ [title, requirements, plan, more].concat(choices).forEach(function (control) { control.disabled = true; });
71
+ setReceipt(receipt, t("receipt.saved") + " · " + result.task.id, "ok");
72
+ renderSubmissionFacets(facets, result.submission, t);
73
+ facets.append(button(t("create.open"), { onClick: function () {
74
+ dialog.close(); dialog.remove(); dialog = null;
75
+ options.selectTask(result.task.id);
76
+ } }));
77
+ options.afterWrite();
78
+ return true;
79
+ },
80
+ rejectedKey: "receipt.notSubmittedBecause", unknownKey: "workbench.unknown"
81
+ });
82
+ });
83
+ });
84
+ }
85
+ `;