@zq-silk/yui 2.0.0 → 2.2.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 (138) hide show
  1. package/README.md +4 -0
  2. package/dist/cli/commandCatalog.js +318 -289
  3. package/dist/cli/commandDiscovery.js +72 -0
  4. package/dist/cli/completion.js +22 -227
  5. package/dist/cli/dynamicCompletion.js +12 -6
  6. package/dist/cli/invocationAuthority.js +2 -2
  7. package/dist/cli/invocationRouter.js +18 -7
  8. package/dist/cli.js +23 -3030
  9. package/dist/commands/capabilityCommands.js +5 -5
  10. package/dist/commands/globalRoleCommands.js +2 -1
  11. package/dist/commands/taskCommands.js +90 -21
  12. package/dist/commands/taskIntegrationCommands.js +3 -1
  13. package/dist/commands/taskUpstreamCommands.js +3 -1
  14. package/dist/context/runContextPack.js +132 -5
  15. package/dist/context/sourceRunContext.js +4 -2
  16. package/dist/context/taskContext.js +12 -3
  17. package/dist/controlPlaneCli.js +3092 -0
  18. package/dist/controller/fileSchedulerStoreAdapter.js +19 -6
  19. package/dist/controller/jobControl.js +21 -0
  20. package/dist/executor/agentExecutor.js +1 -1
  21. package/dist/executor/effectiveLaunch.js +18 -5
  22. package/dist/executor/fileRoleLaunchPlanner.js +15 -19
  23. package/dist/integration/gitIntegrationService.js +50 -15
  24. package/dist/kernel/accessAssessment.js +1 -0
  25. package/dist/kernel/builtinCapabilities.js +28 -1
  26. package/dist/kernel/capabilityRegistry.js +64 -19
  27. package/dist/message/messageContinuation.js +7 -2
  28. package/dist/nativeAgent/agent.js +275 -0
  29. package/dist/nativeAgent/cliDemo.js +37 -0
  30. package/dist/nativeAgent/codingTools.js +11 -0
  31. package/dist/nativeAgent/commandTool.js +215 -0
  32. package/dist/nativeAgent/compactionDemo.js +158 -0
  33. package/dist/nativeAgent/composition.js +71 -0
  34. package/dist/nativeAgent/context/budget.js +44 -0
  35. package/dist/nativeAgent/context/index.js +339 -0
  36. package/dist/nativeAgent/context/providerCompressor.js +103 -0
  37. package/dist/nativeAgent/contracts.js +1 -0
  38. package/dist/nativeAgent/demo.js +40 -0
  39. package/dist/nativeAgent/evaluation/cases.js +38 -0
  40. package/dist/nativeAgent/evaluation/checks.js +91 -0
  41. package/dist/nativeAgent/evaluation/demo.js +19 -0
  42. package/dist/nativeAgent/evaluation/files.js +54 -0
  43. package/dist/nativeAgent/evaluation/fixture.js +36 -0
  44. package/dist/nativeAgent/evaluation/index.js +239 -0
  45. package/dist/nativeAgent/executionOwner.js +209 -0
  46. package/dist/nativeAgent/filePatterns.js +170 -0
  47. package/dist/nativeAgent/fileToolsSupport.js +202 -0
  48. package/dist/nativeAgent/index.js +16 -0
  49. package/dist/nativeAgent/interaction/cli.js +358 -0
  50. package/dist/nativeAgent/interaction/contracts.js +1 -0
  51. package/dist/nativeAgent/interaction/index.js +3 -0
  52. package/dist/nativeAgent/interaction/memoryDemo.js +115 -0
  53. package/dist/nativeAgent/interaction/renderer.js +34 -0
  54. package/dist/nativeAgent/localSafety.js +258 -0
  55. package/dist/nativeAgent/mockProvider.js +43 -0
  56. package/dist/nativeAgent/model/anthropicMessages.js +204 -0
  57. package/dist/nativeAgent/model/chatCompletions.js +210 -0
  58. package/dist/nativeAgent/model/errors.js +47 -0
  59. package/dist/nativeAgent/model/gateway.js +423 -0
  60. package/dist/nativeAgent/model/index.js +7 -0
  61. package/dist/nativeAgent/model/observationAdapter.js +21 -0
  62. package/dist/nativeAgent/model/protocols.js +19 -0
  63. package/dist/nativeAgent/model/responses.js +263 -0
  64. package/dist/nativeAgent/model/types.js +1 -0
  65. package/dist/nativeAgent/model/wire.js +73 -0
  66. package/dist/nativeAgent/observability/index.js +220 -0
  67. package/dist/nativeAgent/product/catalog.js +82 -0
  68. package/dist/nativeAgent/product/config.js +295 -0
  69. package/dist/nativeAgent/product/facts.js +30 -0
  70. package/dist/nativeAgent/product/index.js +62 -0
  71. package/dist/nativeAgent/product/location.js +44 -0
  72. package/dist/nativeAgent/product/runtime.js +276 -0
  73. package/dist/nativeAgent/product/storage.js +49 -0
  74. package/dist/nativeAgent/product/tools.js +47 -0
  75. package/dist/nativeAgent/product/transport.js +54 -0
  76. package/dist/nativeAgent/projectGuidance/index.js +425 -0
  77. package/dist/nativeAgent/searchTools.js +305 -0
  78. package/dist/nativeAgent/session/backends.js +293 -0
  79. package/dist/nativeAgent/session/catalog.js +97 -0
  80. package/dist/nativeAgent/session/catalogDemo.js +87 -0
  81. package/dist/nativeAgent/session/contracts.js +1 -0
  82. package/dist/nativeAgent/session/format.js +269 -0
  83. package/dist/nativeAgent/session/index.js +5 -0
  84. package/dist/nativeAgent/session/location.js +36 -0
  85. package/dist/nativeAgent/session/sqliteFormat.js +134 -0
  86. package/dist/nativeAgent/session/store.js +248 -0
  87. package/dist/nativeAgent/textTools.js +293 -0
  88. package/dist/nativeAgent/toolManager/executor.js +290 -0
  89. package/dist/nativeAgent/toolManager/index.js +4 -0
  90. package/dist/nativeAgent/validation.js +95 -0
  91. package/dist/plugins/pluginService.js +98 -8
  92. package/dist/resources/projectResourceService.js +23 -1
  93. package/dist/runtime/managedIdentity.js +6 -0
  94. package/dist/surface/surfaceContributions.js +3 -0
  95. package/dist/task/taskAuthority.js +61 -10
  96. package/dist/web/assets/assetManifest.js +38 -22
  97. package/dist/web/assets/client/api.js +115 -0
  98. package/dist/web/assets/client/app.js +615 -862
  99. package/dist/web/assets/client/components.js +323 -897
  100. package/dist/web/assets/client/detail.js +333 -0
  101. package/dist/web/assets/client/dock.js +225 -0
  102. package/dist/web/assets/client/dom.js +55 -3
  103. package/dist/web/assets/client/evidence.js +172 -0
  104. package/dist/web/assets/client/format.js +25 -14
  105. package/dist/web/assets/client/forms.js +230 -0
  106. package/dist/web/assets/client/i18n.js +1024 -791
  107. package/dist/web/assets/client/overview.js +118 -0
  108. package/dist/web/assets/client/records.js +63 -0
  109. package/dist/web/assets/client/sections.js +313 -0
  110. package/dist/web/assets/client/sidebar.js +146 -0
  111. package/dist/web/assets/client/theme.js +61 -22
  112. package/dist/web/assets/icons.js +39 -0
  113. package/dist/web/assets/shell.js +125 -118
  114. package/dist/web/assets/styles/base.js +41 -0
  115. package/dist/web/assets/styles/components.js +169 -0
  116. package/dist/web/assets/styles/layout.js +56 -77
  117. package/dist/web/assets/styles/markdown.js +16 -24
  118. package/dist/web/assets/styles/responsive.js +41 -44
  119. package/dist/web/assets/styles/tokens.js +65 -89
  120. package/dist/web/assets/styles/views.js +292 -0
  121. package/dist/web/webServer.js +2 -1
  122. package/docs/agent-result-consumption.md +16 -0
  123. package/docs/agent-result-consumption.zh-CN.md +13 -0
  124. package/docs/examples/agent-offline.mjs +194 -0
  125. package/docs/native-agent.md +283 -0
  126. package/docs/release-workflow.md +61 -0
  127. package/docs/release-workflow.zh-CN.md +46 -0
  128. package/docs/roles-and-configuration.md +32 -0
  129. package/docs/roles-and-configuration.zh-CN.md +26 -0
  130. package/package.json +1 -1
  131. package/skills/yui-leader/SKILL.md +9 -0
  132. package/skills/yui-reviewer/SKILL.md +5 -0
  133. package/skills/yui-runtime/SKILL.md +33 -0
  134. package/dist/web/assets/client/taskSummary.js +0 -350
  135. package/dist/web/assets/client/taskSurface.js +0 -616
  136. package/dist/web/assets/client/view.js +0 -608
  137. package/dist/web/assets/styles/cards.js +0 -247
  138. package/dist/web/assets/styles/widgets.js +0 -168
@@ -0,0 +1,118 @@
1
+ export const OVERVIEW_SCRIPT = String.raw `
2
+ // Workspace overview, shown in the center while no Task is selected.
3
+ import { h, icon, clear } from "/assets/js/dom.js";
4
+ import { byNewest, formatDateTime, relativeTime } from "/assets/js/format.js";
5
+ import { card, dot, tone, label, metricTile, emptyState, button, badge, note } from "/assets/js/components.js";
6
+ import { ATTENTION_KINDS } from "/assets/js/sidebar.js";
7
+
8
+ const SESSION_GROUPS = ["active", "waiting", "background", "quiet", "diagnostic", "stopped", "unknown", "idle"];
9
+
10
+ export function renderOverview(container, state, t, locale, actions) {
11
+ clear(container);
12
+ const counts = state.counts;
13
+ const page = h("div.page.overview");
14
+ page.append(h("header.page-head", null,
15
+ h("div.page-head-main", null,
16
+ h("p.kicker", null, t("overview.kicker")),
17
+ h("h1.page-title", null, t("overview.title")),
18
+ h("p.page-sub", null, counts
19
+ ? t("overview.sub").replace("{total}", String(counts.total)).replace("{active}", String(counts.active || 0))
20
+ : t("loading.dashboard"))),
21
+ h("div.page-head-actions", null,
22
+ button(t("actions.operator"), { icon: "terminal", variant: "ghost", onClick: actions.openOperator }))));
23
+
24
+ page.append(h("div.metric-strip", null,
25
+ metricTile(t("metrics.inputs"), counts ? counts.openInputs : "—", { tone: counts && counts.openInputs ? "warn" : "" }),
26
+ metricTile(t("metrics.active"), counts ? counts.active : "—", { tone: "info" }),
27
+ metricTile(t("metrics.draft"), counts ? counts.draft : "—"),
28
+ metricTile(t("metrics.completed"), counts ? counts.completed : "—", { tone: "ok" }),
29
+ metricTile(t("metrics.total"), counts ? counts.total : "—")));
30
+
31
+ // Two independent column stacks instead of paired rows: each card keeps its
32
+ // natural height, so a short card never leaves a gap beside a long one.
33
+ // Main column: the work path (answer, in progress, recent). Rail: catalog-wide
34
+ // signals and the explicit Session read.
35
+ const main = h("div.overview-col.overview-main");
36
+ const rail = h("div.overview-col.overview-rail");
37
+ // Open questions: the catalog names the affected Tasks, never the questions.
38
+ const inbox = card({ title: t("overview.inbox"), icon: "inbox", count: counts ? counts.openInputs : null });
39
+ const items = state.attention || [];
40
+ if (!items.length) {
41
+ inbox.body.append(emptyState(counts && counts.openInputs > 0 ? t("overview.inboxElsewhere") : t("overview.inboxEmpty"), "check"));
42
+ } else {
43
+ inbox.body.append(h("div.row-list", null, items.slice(0, 8).map(function (item) {
44
+ return h("button.list-row", { type: "button", onclick: function () { actions.select(item.taskId); } },
45
+ dot("warn"), h("span.list-row-title", null, item.taskTitle), h("code.id", null, item.taskId),
46
+ h("span.list-row-go", null, t("overview.answer"), icon("chevron", "icon-sm")));
47
+ })));
48
+ }
49
+ main.append(inbox);
50
+
51
+ const catalog = card({ title: t("overview.catalog"), icon: "pulse", hint: t("overview.catalogHint") });
52
+ if (state.catalogAttention) {
53
+ catalog.body.append(h("div.row-list", null, ATTENTION_KINDS.map(function (kind) {
54
+ const value = state.catalogAttention[kind];
55
+ if (!value) return null;
56
+ return h("button.list-row", {
57
+ type: "button", disabled: value.taskCount === 0,
58
+ "aria-pressed": String(state.attentionFilter === kind),
59
+ onclick: function () { actions.filterAttention(kind); }
60
+ },
61
+ h("span.list-row-title", null, t("catalog." + kind)),
62
+ h("span.list-row-count", null, String(value.count)),
63
+ h("span.faint", null, value.taskCount + " " + t("overview.tasks")));
64
+ })));
65
+ catalog.body.append(note(t("overview.signalsHelp")));
66
+ } else catalog.body.append(emptyState(t("loading.dashboard")));
67
+ rail.append(catalog);
68
+
69
+ const active = (state.tasks || []).filter(function (task) { return task.status === "active"; });
70
+ const recent = (state.tasks || []).filter(function (task) { return task.status !== "archived"; }).slice().sort(byNewest).slice(0, 8);
71
+ main.append(taskListCard(t("overview.activeNow"), "pulse", active, t("overview.activeEmpty"), t, locale, actions));
72
+ main.append(taskListCard(t("overview.recent"), "history", recent, t("sidebar.empty"), t, locale, actions));
73
+ rail.append(sessionCard(state, t, locale, actions));
74
+ page.append(h("div.overview-layout", null, main, rail));
75
+ page.append(h("p.page-foot", null, t("overview.pageScope")));
76
+ container.append(page);
77
+ }
78
+
79
+ function taskListCard(title, iconName, tasks, empty, t, locale, actions) {
80
+ const element = card({ title: title, icon: iconName, count: tasks.length, hint: t("overview.thisPage") });
81
+ if (!tasks.length) { element.body.append(emptyState(empty)); return element; }
82
+ element.body.append(h("div.row-list", null, tasks.map(function (task) {
83
+ return h("button.list-row", { type: "button", title: task.summary || task.title, onclick: function () { actions.select(task.id); } },
84
+ dot(tone("task", task.status), label(t, "status", task.status)),
85
+ h("span.list-row-title", null, task.title),
86
+ task.attention && task.attention.openInputs ? badge(String(task.attention.openInputs), "warn") : null,
87
+ h("time.faint", { dateTime: task.updatedAt }, relativeTime(task.updatedAt, locale, t)));
88
+ })));
89
+ return element;
90
+ }
91
+
92
+ // Native Session observation is an explicit read of exactly this catalog page.
93
+ function sessionCard(state, t, locale, actions) {
94
+ const element = card({
95
+ title: t("overview.sessions"), icon: "terminal", hint: t("overview.sessionsHint"),
96
+ actions: button(state.sessionOverview ? t("overview.sessionsReread") : t("overview.sessionsRead"), {
97
+ icon: "eye", variant: "ghost", disabled: state.sessionLoading || !(state.tasks || []).length,
98
+ onClick: actions.readSessions
99
+ })
100
+ });
101
+ if (state.sessionError) element.body.append(note(state.sessionError, "bad"));
102
+ const overview = state.sessionOverview;
103
+ if (!overview) {
104
+ element.body.append(emptyState(state.sessionLoading ? t("overview.sessionsReading") : t("overview.sessionsNotRead")));
105
+ return element;
106
+ }
107
+ element.body.append(h("p.faint.small", null, t("overview.readAt") + " " + formatDateTime(overview.readAt, locale)));
108
+ element.body.append(h("div.row-list", null, overview.tasks.map(function (task) {
109
+ const chips = SESSION_GROUPS.filter(function (group) { return task.counts && task.counts[group]; }).map(function (group) {
110
+ return badge(task.counts[group] + " " + t("session." + group), tone("session", group), { dot: true });
111
+ });
112
+ return h("button.list-row.list-row-wrap", { type: "button", onclick: function () { actions.select(task.taskId); } },
113
+ h("span.list-row-title", null, task.title), h("code.id", null, task.taskId),
114
+ h("span.chip-row", null, chips.length ? chips : h("span.faint", null, t("session.noneRecorded"))));
115
+ })));
116
+ return element;
117
+ }
118
+ `;
@@ -0,0 +1,63 @@
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 } 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
+ // A generic, honest card for any record: omitted values say so and can be
16
+ // read by reference; reading never acknowledges or accepts anything.
17
+ export function recordCard(entry, taskId, t, actions, options) {
18
+ const opts = options || {};
19
+ const element = h("article.record" + (opts.compact ? ".is-compact" : ""));
20
+ element.append(h("header.record-head", null,
21
+ h("span.record-store", null, t("store." + entry.ref.store, entry.ref.store)),
22
+ mono(entry.ref.refId),
23
+ h("span.spacer"),
24
+ h("span.faint.small", { title: t("record.revision") }, String(entry.ref.revision).slice(0, 24))));
25
+ if (!entry.omitted && entry.value) {
26
+ const value = entry.value;
27
+ const text = value.content || value.summary || value.body || value.objective || value.title || value.leaderSummary;
28
+ if (text) element.append(richText(null, text, t, { threshold: 360 }));
29
+ if (entry.ref.store === "task-decision" && value.rationale) element.append(richText(t("record.rationale"), value.rationale, t, { muted: true }));
30
+ const facts = [value.status, value.roleName].filter(Boolean);
31
+ if (facts.length) element.append(h("p.faint.small", null, facts.join(" · ")));
32
+ if (entry.ref.store === "job") element.append(note(t("job." + value.status + ".note", t("job.terminal.note")), value.status === "unknown-needs-attention" ? "warn" : ""));
33
+ } else {
34
+ element.append(h("p.faint", null, entry.summary || t("record.omitted")));
35
+ }
36
+ element.append(recordReader(entry, taskId, t, actions));
37
+ return element;
38
+ }
39
+
40
+ // Shared source access for cards, history and discussion, including records
41
+ // whose compact value is present but does not display every durable field.
42
+ export function recordReader(entry, taskId, t, actions) {
43
+ const element = h("div.stack");
44
+ const expand = button(t("record.read"), { icon: "eye", variant: "link" });
45
+ expand.addEventListener("click", async function () {
46
+ expand.disabled = true;
47
+ element.dataset.reading = "true";
48
+ try {
49
+ const result = await actions.inspect(taskId, entry.ref);
50
+ if (result.result && result.result.output) element.append(richText(t("record.output"), result.result.output, t));
51
+ if (result.result && result.result.diagnostic) element.append(richText(t("record.diagnostic"), result.result.diagnostic, t));
52
+ element.append(h("pre.code-block", null, JSON.stringify(result.value, null, 2)));
53
+ if (result.execution) element.append(h("pre.code-block", null, JSON.stringify(result.execution, null, 2)));
54
+ expand.remove();
55
+ } catch {
56
+ expand.disabled = false;
57
+ expand.replaceChildren(icon("refresh"), h("span", null, t("record.changed")));
58
+ } finally { element.dataset.reading = "false"; }
59
+ });
60
+ element.append(h("div.record-actions", null, expand));
61
+ return element;
62
+ }
63
+ `;
@@ -0,0 +1,313 @@
1
+ export const SECTIONS_SCRIPT = String.raw `
2
+ // Task sections behind the Work, Execution, History and Details tabs.
3
+ import { h, icon, clear } from "/assets/js/dom.js";
4
+ import { formatDateTime } from "/assets/js/format.js";
5
+ import {
6
+ sectionTitle, card, disclosure, emptyState, note, button, statusBadge, label, tone, dot, chip, mono,
7
+ kv, richText, pagedList, workItemCard, runCard, reviewCard, roleCard, timeTag, observabilityMetricCard
8
+ } from "/assets/js/components.js";
9
+ import { entriesOf, valuesOf, recordCard, recordReader } from "/assets/js/records.js";
10
+ import { renderEvidence } from "/assets/js/evidence.js";
11
+ import { controlForm, titleForm } from "/assets/js/forms.js";
12
+
13
+ function omittedNote(core, t) {
14
+ return core.omitted.records ? t("records.notIncluded") : t("records.none");
15
+ }
16
+
17
+ // --- Work -------------------------------------------------------------------------
18
+ export function renderWork(panel, data, t, locale, ctx) {
19
+ const core = data.core;
20
+ const items = entriesOf(core, "work-item");
21
+ const titles = {};
22
+ valuesOf(core, "work-item").forEach(function (item) { titles[item.id] = item.title; });
23
+ const rank = function (entry) { return entry.omitted ? 1 : entry.value.status === "open" ? 0 : 2; };
24
+ const sorted = items.slice().sort(function (a, b) {
25
+ return rank(a) - rank(b) || Date.parse((b.value && b.value.updatedAt) || 0) - Date.parse((a.value && a.value.updatedAt) || 0);
26
+ });
27
+ const work = h("section.section", { id: "detail-work" }, sectionTitle(t("work.title"), items.length));
28
+ if (!sorted.length) work.append(emptyState(omittedNote(core, t), "layers"));
29
+ const list = h("div.stack");
30
+ sorted.forEach(function (entry) {
31
+ list.append(entry.omitted ? recordCard(entry, data.task.id, t, ctx) : workItemCard(entry.value, t, locale, titles));
32
+ });
33
+ work.append(list);
34
+ panel.append(work);
35
+
36
+ const results = h("section.section", { id: "detail-results" }, sectionTitle(t("evidence.title")));
37
+ const resultsBody = h("div.card-grid.card-grid-1");
38
+ renderEvidence(resultsBody, data, t, locale, ctx);
39
+ results.append(resultsBody);
40
+ panel.append(results);
41
+
42
+ const reviews = entriesOf(core, "review-round");
43
+ const reviewSection = h("section.section", { id: "detail-reviews" }, sectionTitle(t("reviews.title"), reviews.length));
44
+ if (!reviews.length) reviewSection.append(emptyState(omittedNote(core, t)));
45
+ else {
46
+ reviewSection.append(note(t("reviews.hint")));
47
+ const reviewList = h("div.stack");
48
+ reviews.forEach(function (entry) {
49
+ reviewList.append(entry.omitted ? recordCard(entry, data.task.id, t, ctx) : reviewCard(entry.value, t, locale));
50
+ });
51
+ reviewSection.append(reviewList);
52
+ }
53
+ panel.append(reviewSection);
54
+ }
55
+
56
+ // --- Execution --------------------------------------------------------------------
57
+ export function renderExecution(panel, data, t, locale, ctx) {
58
+ const core = data.core;
59
+ const task = data.task;
60
+ const runtimeRoles = (data.runtime && data.runtime.roles) || [];
61
+ const runs = valuesOf(core, "run");
62
+
63
+ const roleEntries = entriesOf(core, "role");
64
+ const roles = h("section.section", { id: "detail-roles" }, sectionTitle(t("roles.title"), roleEntries.length));
65
+ const roleGrid = h("div.role-grid");
66
+ roleEntries.forEach(function (entry) {
67
+ if (entry.omitted) { roleGrid.append(recordCard(entry, task.id, t, ctx)); return; }
68
+ const role = entry.value;
69
+ const active = runs.find(function (run) { return run.roleName === role.name && run.status === "active"; });
70
+ const runtimeRole = runtimeRoles.find(function (item) { return item.name === role.name; });
71
+ const element = roleCard(role, t, locale, {
72
+ runtimeRole: runtimeRole,
73
+ retry: entry.providerRetry,
74
+ launchDrift: active && active.effective && active.effective.sourceDesiredRevision !== role.launchRevision,
75
+ onOpen: task.status === "archived" ? null : ctx.openSession
76
+ });
77
+ const statusSlot = h("span", { dataset: { roleStatus: role.name } });
78
+ element.querySelector(".role-head").append(statusSlot);
79
+ if (!active) element.append(h("p.faint.small", null, t("roles.noActiveRun")));
80
+ roleGrid.append(element);
81
+ });
82
+ if (!roleEntries.length) roleGrid.append(emptyState(omittedNote(core, t)));
83
+ roles.append(roleGrid);
84
+ panel.append(roles);
85
+
86
+ const redirect = disclosure(t("control.title"), "control", { className: "card-disclosure", meta: t("control.meta") });
87
+ redirect.body.append(note(t("control.help")), controlForm(task, t, ctx, roleEntries.map(function (entry) { return entry.ref.refId; })));
88
+ panel.append(h("section.section", { id: "detail-control" }, redirect));
89
+
90
+ const runEntries = entriesOf(core, "run");
91
+ const runSection = h("section.section", { id: "detail-exec" });
92
+ const filters = ["all", "active", "completed", "failed"];
93
+ let filter = "all";
94
+ const filterRow = h("div.chip-tabs", { role: "group", "aria-label": t("runs.filter") });
95
+ const list = h("div.stack");
96
+ function draw() {
97
+ clear(list);
98
+ const visible = runEntries.filter(function (entry) { return filter === "all" || (!entry.omitted && entry.value.status === filter); });
99
+ if (!visible.length) { list.append(emptyState(omittedNote(core, t), "pulse")); return; }
100
+ pagedList(list, visible, 12, function (entry) {
101
+ return entry.omitted ? recordCard(entry, task.id, t, ctx) : runCard({ ...entry.value, execution: entry.execution }, t, locale);
102
+ }, t);
103
+ }
104
+ filters.forEach(function (status) {
105
+ const count = status === "all" ? runEntries.length : runs.filter(function (run) { return run.status === status; }).length;
106
+ if (status !== "all" && !count) return;
107
+ filterRow.append(h("button.chip-tab", {
108
+ type: "button", "aria-pressed": String(status === filter), dataset: { status },
109
+ onclick: function () {
110
+ filter = status;
111
+ filterRow.querySelectorAll(".chip-tab").forEach(function (b) { b.setAttribute("aria-pressed", String(b.dataset.status === status)); });
112
+ draw();
113
+ }
114
+ }, status === "all" ? t("filter.all") : label(t, "run", status), h("span.count", null, String(count))));
115
+ });
116
+ runSection.append(sectionTitle(t("runs.title"), runEntries.length), note(t("runs.hint")), filterRow, list);
117
+ draw();
118
+ panel.append(runSection);
119
+
120
+ const jobs = entriesOf(core, "job");
121
+ const jobSection = h("section.section", { id: "detail-operations" }, sectionTitle(t("jobs.title"), jobs.length));
122
+ if (!jobs.length) jobSection.append(emptyState(omittedNote(core, t)));
123
+ else {
124
+ jobSection.append(note(t("jobs.hint")));
125
+ const jobList = h("div.stack");
126
+ jobs.forEach(function (entry) {
127
+ if (entry.omitted) { jobList.append(recordCard(entry, task.id, t, ctx)); return; }
128
+ const job = entry.value;
129
+ jobList.append(h("article.record", null,
130
+ h("header.record-head", null, dot(tone("job", job.status)), mono(task.id + "/" + job.id), h("span.spacer"),
131
+ statusBadge(t, "job", "job", job.status)),
132
+ h("p.small", null, t("job." + job.status + ".note", t("job.terminal.note"))),
133
+ h("p.faint.small", null, (job.steps || []).map(function (step) { return step.name; }).join(" → ")),
134
+ timeTag(job.terminalAt || job.startedAt || job.createdAt, locale, t)));
135
+ });
136
+ jobSection.append(jobList);
137
+ }
138
+ panel.append(jobSection);
139
+
140
+ const usage = h("section.section", { id: "detail-usage" }, sectionTitle(t("usage.title")));
141
+ const usageBody = h("div", { dataset: { slot: "usage" } });
142
+ const metrics = data.runtime && observabilityMetricCard(data.runtime.observability, t);
143
+ usageBody.append(metrics || emptyState(t("usage.unavailable")));
144
+ usage.append(usageBody);
145
+ panel.append(usage);
146
+
147
+ const observations = disclosure(t("observation.title"), "execution", { className: "card-disclosure" });
148
+ observations.body.append(note(t("observation.help")));
149
+ (core.observations || []).forEach(function (observation) {
150
+ observations.body.append(h("p.small.mono-line", null,
151
+ observation.source + " · " + observation.status + " · " + observation.coverage + " · " + observation.observedAt));
152
+ });
153
+ observations.body.append(h("p.faint.small", { dataset: { slot: "runtime-status" } }), h("pre.code-block", { dataset: { slot: "runtime-raw" } }));
154
+ panel.append(h("section.section", null, observations));
155
+ }
156
+
157
+ // --- History ----------------------------------------------------------------------
158
+ export function renderHistory(panel, data, t, locale, ctx) {
159
+ const core = data.core;
160
+ const task = data.task;
161
+ const events = [];
162
+ entriesOf(core, "task-decision").forEach(function (entry) { events.push({ kind: "decision", entry }); });
163
+ entriesOf(core, "task-milestone").forEach(function (entry) { events.push({ kind: "milestone", entry }); });
164
+ entriesOf(core, "task-message").forEach(function (entry) { events.push({ kind: "message", entry }); });
165
+ const at = function (event) {
166
+ const value = event.entry.value || {};
167
+ return Date.parse(value.createdAt || value.updatedAt || 0) || 0;
168
+ };
169
+ events.sort(function (a, b) { return at(b) - at(a); });
170
+ const section = h("section.section", { id: "detail-history" }, sectionTitle(t("history.title"), events.length));
171
+ const kinds = ["all", "decision", "milestone", "message"];
172
+ let kind = "all";
173
+ const filterRow = h("div.chip-tabs", { role: "group", "aria-label": t("history.filter") });
174
+ const list = h("ol.timeline");
175
+ function draw() {
176
+ clear(list);
177
+ const visible = events.filter(function (event) { return kind === "all" || event.kind === kind; });
178
+ if (!visible.length) { list.append(h("li.timeline-empty", null, emptyState(omittedNote(core, t), "history"))); return; }
179
+ pagedList(list, visible, 20, function (event) { return timelineItem(event, task.id, t, locale, ctx); }, t);
180
+ }
181
+ kinds.forEach(function (value) {
182
+ const count = value === "all" ? events.length : events.filter(function (event) { return event.kind === value; }).length;
183
+ filterRow.append(h("button.chip-tab", {
184
+ type: "button", "aria-pressed": String(value === kind), dataset: { kind: value },
185
+ onclick: function () {
186
+ kind = value;
187
+ filterRow.querySelectorAll(".chip-tab").forEach(function (b) { b.setAttribute("aria-pressed", String(b.dataset.kind === value)); });
188
+ draw();
189
+ }
190
+ }, t("history." + value), h("span.count", null, String(count))));
191
+ });
192
+ section.append(filterRow, list);
193
+ if (core.omitted.records) section.append(note(t("history.omitted")));
194
+ draw();
195
+ panel.append(section);
196
+ }
197
+
198
+ function timelineItem(event, taskId, t, locale, ctx) {
199
+ const entry = event.entry;
200
+ const item = h("li.timeline-item.kind-" + event.kind);
201
+ item.append(h("span.timeline-mark", { "aria-hidden": "true" }, icon(event.kind === "decision" ? "check" : event.kind === "milestone" ? "flag" : "chat", "icon-sm")));
202
+ if (entry.omitted) { item.append(recordCard(entry, taskId, t, ctx, { compact: true })); return item; }
203
+ const value = entry.value;
204
+ const body = h("div.timeline-body");
205
+ if (event.kind === "message") {
206
+ const author = (value.author && value.author.roleName) || label(t, "author", (value.author && value.author.type) || "system");
207
+ body.append(h("header.timeline-head", null, h("strong", null, author),
208
+ value.kind ? chip(label(t, "messageKind", value.kind)) : null,
209
+ value.intent ? chip(label(t, "intent", value.intent)) : null,
210
+ h("span.spacer"), timeTag(value.createdAt, locale, t)));
211
+ body.append(richText(null, value.body || "", t, { threshold: 480 }));
212
+ if (value.resultRef) body.append(h("p.faint.small", null, t("history.resultOf") + " ", mono(value.resultRef.runId)));
213
+ } else if (event.kind === "decision") {
214
+ body.append(h("header.timeline-head", null, h("strong", null, value.title),
215
+ value.status ? statusBadge(t, "decision", "decision", value.status) : null, h("span.spacer"), timeTag(value.createdAt, locale, t)));
216
+ if (value.rationale) body.append(richText(null, value.rationale, t, { muted: true }));
217
+ if (value.supersededReason) body.append(richText(t("history.superseded"), value.supersededReason, t, { muted: true }));
218
+ } else {
219
+ body.append(h("header.timeline-head", null, h("strong", null, value.title), h("span.spacer"), timeTag(value.createdAt, locale, t)));
220
+ if (value.summary) body.append(richText(null, value.summary, t, { muted: true }));
221
+ }
222
+ body.append(h("p.faint.small", null, mono(entry.ref.refId)));
223
+ body.append(recordReader(entry, taskId, t, ctx));
224
+ item.append(body);
225
+ return item;
226
+ }
227
+
228
+ // --- Details ----------------------------------------------------------------------
229
+ export function renderDetails(panel, data, t, locale, ctx) {
230
+ const task = data.task;
231
+ const core = data.core;
232
+ const facts = card({ title: t("details.facts"), icon: "file" });
233
+ facts.body.append(kv([
234
+ [t("details.id"), mono(task.id)],
235
+ [t("details.status"), statusBadge(t, "task", "status", task.status)],
236
+ [t("details.priority"), task.priority ? label(t, "priority", task.priority) : null],
237
+ [t("details.tags"), (task.tags || []).length ? h("span.chip-row", null, task.tags.map(function (tag) { return chip(tag); })) : null],
238
+ [t("details.projects"), (task.projectBindings || []).length ? h("span.chip-row", null, task.projectBindings.map(function (b) { return chip(b.projectId); })) : null],
239
+ [t("details.created"), formatDateTime(task.createdAt, locale)],
240
+ [t("details.updated"), formatDateTime(task.updatedAt, locale)],
241
+ [t("details.workspace"), task.cwd ? mono(task.cwd) : null]
242
+ ]));
243
+ const edit = card({ title: t("details.title"), icon: "sparkle" });
244
+ edit.body.append(titleForm(task, t, ctx));
245
+ const grid = h("div.card-grid");
246
+ grid.append(facts, edit);
247
+ panel.append(h("section.section", { id: "detail-facts" }, grid));
248
+
249
+ const context = disclosure(t("details.context"), "context", { className: "card-disclosure" });
250
+ context.body.append(h("pre.code-block", null, JSON.stringify({
251
+ coreCursor: core.coreCursor, throughCursor: core.throughCursor, count: core.count, omitted: core.omitted
252
+ }, null, 2)), note(t("details.contextHelp")));
253
+ panel.append(h("section.section", null, context));
254
+
255
+ const panels = disclosure(t("panels.title"), "panels", { className: "card-disclosure" });
256
+ let loading = false;
257
+ panels.addEventListener("toggle", async function () {
258
+ if (!panels.open || loading) return;
259
+ loading = true;
260
+ clear(panels.body);
261
+ panels.body.append(h("p.faint", null, t("panels.reading")));
262
+ try {
263
+ const current = await ctx.panels(task.id);
264
+ clear(panels.body);
265
+ panels.body.append(h("p.faint.small", null, t("panels.observed") + " " + formatDateTime(current.observedAt, locale)));
266
+ if (!current.panels.length) panels.body.append(emptyState(t("panels.none")));
267
+ current.panels.forEach(function (item) { panels.body.append(panelCard(item, task, t, ctx)); });
268
+ } catch (error) {
269
+ clear(panels.body);
270
+ panels.body.append(note(t("panels.unavailable") + " " + error.message, "bad"));
271
+ } finally { loading = false; }
272
+ });
273
+ panel.append(h("section.section", null, panels));
274
+ }
275
+
276
+ function panelCard(item, task, t, ctx) {
277
+ const panel = item.panel;
278
+ const element = h("article.record", null,
279
+ h("header.record-head", null, h("strong", null, panel.title), h("span.spacer"), h("span.faint.small", null, item.capability + " · " + item.provider.id)));
280
+ if (item.unavailable) { element.append(note(item.unavailable, "warn")); return element; }
281
+ if (panel.kind === "text") element.append(h("p.small", null, panel.text));
282
+ else if (panel.kind === "link") {
283
+ try {
284
+ const url = new URL(panel.href);
285
+ if (["http:", "https:"].includes(url.protocol) && !url.username && !url.password) {
286
+ element.append(h("a.link", { href: url.href, rel: "noopener noreferrer", target: "_blank" }, panel.title));
287
+ }
288
+ } catch {}
289
+ } else if (panel.kind === "data" && panel.renderer === "json") {
290
+ const input = h("textarea.mono", { rows: 3 });
291
+ input.value = JSON.stringify({ taskId: task.id });
292
+ const schema = disclosure(t("panels.schema"), null);
293
+ schema.body.append(h("pre.code-block", null, JSON.stringify(item.inputSchema, null, 2)));
294
+ const output = h("pre.code-block");
295
+ output.hidden = true;
296
+ const read = button(t("panels.read"), { icon: "eye", variant: "ghost" });
297
+ read.addEventListener("click", async function () {
298
+ if (read.disabled) return;
299
+ read.disabled = true;
300
+ output.hidden = false;
301
+ try {
302
+ const result = await ctx.readPanel(task.id, {
303
+ capability: item.capability, contractVersion: item.contractVersion, provider: item.provider
304
+ }, JSON.parse(input.value));
305
+ output.textContent = JSON.stringify(result, null, 2);
306
+ } catch (error) { output.textContent = t("panels.unavailable") + " " + error.message; }
307
+ finally { read.disabled = false; }
308
+ });
309
+ element.append(h("label.field", null, h("span", null, t("panels.input")), input), schema, h("div.form-actions", null, read), output);
310
+ }
311
+ return element;
312
+ }
313
+ `;
@@ -0,0 +1,146 @@
1
+ export const SIDEBAR_SCRIPT = String.raw `
2
+ // Task index: attention shortcuts, status tabs and the grouped task list.
3
+ import { h, icon, clear } from "/assets/js/dom.js";
4
+ import { relativeTime } from "/assets/js/format.js";
5
+ import { dot, tone, label } from "/assets/js/components.js";
6
+
7
+ export const STATUS_FILTERS = ["all", "active", "draft", "completed", "cancelled", "archived"];
8
+ export const ATTENTION_KINDS = ["openInputs", "unknownOperations", "pendingOperations", "executionSignals"];
9
+ const ATTENTION_ICON = { openInputs: "inbox", unknownOperations: "alert", pendingOperations: "clock", executionSignals: "pulse" };
10
+ const ATTENTION_TONE = { openInputs: "warn", unknownOperations: "bad", pendingOperations: "info", executionSignals: "idle" };
11
+
12
+ export function renderAttentionBar(container, state, t, onPick) {
13
+ clear(container);
14
+ const attention = state.catalogAttention;
15
+ if (!attention) return;
16
+ const kinds = ATTENTION_KINDS.filter(function (kind) { return attention[kind] && attention[kind].taskCount > 0; });
17
+ if (!kinds.length) {
18
+ container.append(h("p.attention-clear", null, icon("check", "icon-sm"), t("sidebar.allClear")));
19
+ return;
20
+ }
21
+ container.append(h("div.attention-list", null, kinds.map(function (kind) {
22
+ const value = attention[kind];
23
+ return h("button.attention-pill.tone-" + ATTENTION_TONE[kind], {
24
+ type: "button",
25
+ "aria-pressed": String(state.attentionFilter === kind),
26
+ title: t("catalog.help." + kind),
27
+ onclick: function () { onPick(state.attentionFilter === kind ? null : kind); }
28
+ }, icon(ATTENTION_ICON[kind], "icon-sm"), h("span", null, t("catalog." + kind)), h("b", null, String(value.count)));
29
+ })));
30
+ }
31
+
32
+ // The tab row is created once and updated in place so its horizontal scroll
33
+ // position survives every refresh. When the sidebar is too narrow for every
34
+ // tab, the clipped edge fades out, the wheel scrolls the row sideways and a
35
+ // newly selected tab is scrolled into view.
36
+ function syncTabsOverflow(row) {
37
+ const max = row.scrollWidth - row.clientWidth;
38
+ const before = row.scrollLeft > 1;
39
+ const after = row.scrollLeft < max - 1;
40
+ row.dataset.fade = before && after ? "both" : after ? "end" : before ? "start" : "none";
41
+ }
42
+
43
+ function revealTab(row, tab) {
44
+ const margin = 28;
45
+ const left = tab.offsetLeft - margin;
46
+ const right = tab.offsetLeft + tab.offsetWidth + margin;
47
+ if (left < row.scrollLeft) row.scrollLeft = Math.max(0, left);
48
+ else if (right > row.scrollLeft + row.clientWidth) row.scrollLeft = right - row.clientWidth;
49
+ }
50
+
51
+ export function renderFilters(container, state, t, onFilter) {
52
+ const counts = state.counts || {};
53
+ let row = container.querySelector(":scope > .tabs-row");
54
+ if (!row) {
55
+ row = h("div.tabs-row");
56
+ STATUS_FILTERS.forEach(function (status) {
57
+ row.append(h("button.status-tab", { type: "button", dataset: { status }, onclick: function () { onFilter(status); } },
58
+ h("span.status-tab-label"), h("span.status-tab-count")));
59
+ });
60
+ container.append(row);
61
+ row.addEventListener("scroll", function () { syncTabsOverflow(row); }, { passive: true });
62
+ row.addEventListener("wheel", function (event) {
63
+ if (Math.abs(event.deltaY) <= Math.abs(event.deltaX) || row.scrollWidth <= row.clientWidth) return;
64
+ event.preventDefault();
65
+ row.scrollLeft += event.deltaY;
66
+ }, { passive: false });
67
+ new ResizeObserver(function () { syncTabsOverflow(row); }).observe(row);
68
+ }
69
+ STATUS_FILTERS.forEach(function (status) {
70
+ const tab = row.querySelector('[data-status="' + status + '"]');
71
+ tab.setAttribute("aria-pressed", String(state.filter === status));
72
+ tab.querySelector(".status-tab-label").textContent = status === "all"
73
+ ? t(state.catalogAll ? "filter.everything" : "filter.unarchived") : label(t, "status", status);
74
+ const count = status === "all"
75
+ ? (counts.total === undefined ? undefined : counts.total - (state.catalogAll ? 0 : counts.archived || 0))
76
+ : counts[status];
77
+ const badge = tab.querySelector(".status-tab-count");
78
+ const hide = count === undefined || count === null
79
+ || (status === "archived" && state.catalogScope && state.catalogScope.archived === "excluded");
80
+ badge.hidden = hide;
81
+ if (!hide) badge.textContent = String(count);
82
+ });
83
+ if (row.dataset.selected !== state.filter) {
84
+ row.dataset.selected = state.filter;
85
+ revealTab(row, row.querySelector('[data-status="' + state.filter + '"]'));
86
+ }
87
+ syncTabsOverflow(row);
88
+ }
89
+
90
+ function groupOf(task) {
91
+ const attention = task.attention || {};
92
+ if (attention.openInputs > 0 || attention.unknownOperations > 0) return "attention";
93
+ if (task.status === "completed") return "completed";
94
+ return task.status;
95
+ }
96
+
97
+ const GROUP_ORDER = ["attention", "active", "draft", "completed", "cancelled", "archived"];
98
+
99
+ export function renderTasks(container, state, t, locale, onSelect) {
100
+ clear(container);
101
+ const groups = {};
102
+ (state.tasks || []).forEach(function (task) {
103
+ if (state.filter === "all" && !state.catalogAll && task.status === "archived") return;
104
+ if (state.filter !== "all" && task.status !== state.filter) return;
105
+ const group = groupOf(task);
106
+ (groups[group] = groups[group] || []).push(task);
107
+ });
108
+ const present = GROUP_ORDER.filter(function (group) { return groups[group] && groups[group].length; });
109
+ if (!present.length) {
110
+ container.append(h("div.list-empty", null, icon("search"), h("p", null, t(state.query || state.attentionFilter ? "sidebar.noMatch" : "sidebar.empty"))));
111
+ return;
112
+ }
113
+ present.forEach(function (group) {
114
+ container.append(h("section.task-group", null,
115
+ h("h3.task-group-head", null, h("span", null, t("group." + group)), h("span.count", null, String(groups[group].length))),
116
+ groups[group].map(function (task) { return taskRow(task, state, t, locale, onSelect); })));
117
+ });
118
+ }
119
+
120
+ function taskRow(task, state, t, locale, onSelect) {
121
+ const attention = task.attention || {};
122
+ const counts = task.counts || {};
123
+ const signals = h("span.task-signals");
124
+ if (attention.openInputs > 0) signals.append(signal("inbox", attention.openInputs, "warn", t("catalog.openInputs")));
125
+ if (attention.unknownOperations > 0) signals.append(signal("alert", attention.unknownOperations, "bad", t("catalog.unknownOperations")));
126
+ if (counts.activeRuns > 0) signals.append(signal("pulse", counts.activeRuns, "info", t("sidebar.activeRuns")));
127
+ const row = h("button.task-row", {
128
+ type: "button",
129
+ dataset: { id: task.id },
130
+ "aria-current": String(state.selected === task.id),
131
+ title: task.summary || task.title,
132
+ onclick: function () { onSelect(task.id); }
133
+ },
134
+ dot(tone("task", task.status), label(t, "status", task.status)),
135
+ h("span.task-body", null,
136
+ h("span.task-line", null, h("span.task-name", null, task.title), h("time.task-time", { dateTime: task.updatedAt }, relativeTime(task.updatedAt, locale, t))),
137
+ h("span.task-line.task-sub", null, h("code.task-id", null, task.id),
138
+ counts.workItems > 0 ? h("span.task-count", { title: t("sidebar.workItems") }, icon("layers", "icon-sm"), String(counts.workItems)) : null,
139
+ signals)));
140
+ return row;
141
+ }
142
+
143
+ function signal(iconName, count, toneName, title) {
144
+ return h("span.signal.tone-" + toneName, { title: title }, icon(iconName, "icon-sm"), String(count));
145
+ }
146
+ `;