@zq-silk/yui 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -1
- package/dist/artifacts/artifactCapability.js +39 -10
- package/dist/artifacts/imageMetadata.js +120 -0
- package/dist/artifacts/managedGit.js +7 -4
- package/dist/artifacts/taskArtifactRepository.js +6 -0
- package/dist/cli/commandCatalog.js +18 -9
- package/dist/cli/invocationAuthority.js +1 -1
- package/dist/commands/executionAuditCommands.js +5 -0
- package/dist/commands/globalRoleCommands.js +41 -3
- package/dist/commands/projectCommands.js +89 -94
- package/dist/commands/roleSkillValidation.js +4 -8
- package/dist/commands/taskCommands.js +65 -14
- package/dist/commands/taskUsageCommand.js +38 -0
- package/dist/context/roleSessionContext.js +18 -2
- package/dist/context/runContextPack.js +5 -1
- package/dist/context/sessionBootstrapManifest.js +18 -3
- package/dist/context/skillCatalog.js +108 -0
- package/dist/context/skillPackage.js +161 -0
- package/dist/context/taskContext.js +3 -2
- package/dist/context/taskSearch.js +127 -0
- package/dist/controlPlaneCli.js +25 -20
- package/dist/controller/fileSchedulerStoreAdapter.js +39 -8
- package/dist/controller/globalInputDelivery.js +2 -4
- package/dist/controller/jobControl.js +2 -0
- package/dist/controller/nativeHostInput.js +37 -0
- package/dist/controller/providerRetryDelivery.js +4 -2
- package/dist/controller/runtime.js +93 -2
- package/dist/controller/runtimeEventInbox.js +2 -2
- package/dist/controller/runtimeEventProcessor.js +3 -3
- package/dist/doctor/doctor.js +10 -0
- package/dist/execution/reviewMainRun.js +1 -0
- package/dist/execution/workItemMainRun.js +1 -0
- package/dist/executor/agentAdapter.js +3 -5
- package/dist/executor/codexConfigConflict.js +6 -4
- package/dist/executor/effectiveLaunch.js +24 -1
- package/dist/executor/fileRoleLaunchPlanner.js +15 -7
- package/dist/integration/gitIntegrationService.js +7 -12
- package/dist/integration/integrationSourceApplication.js +4 -2
- package/dist/interaction/notificationPresentation.js +85 -0
- package/dist/interaction/operatorPresentation.js +1 -2
- package/dist/job/httpPreview.js +61 -0
- package/dist/job/jobRunner.js +12 -2
- package/dist/kernel/builtinCapabilities.js +3 -1
- package/dist/message/message.js +15 -1
- package/dist/output/rolePresentation.js +4 -1
- package/dist/repository/executionLaneGitSnapshot.js +2 -1
- package/dist/repository/gitCompatibility.js +62 -0
- package/dist/repository/gitWorkspace.js +48 -71
- package/dist/repository/gitWorktreeInventory.js +121 -0
- package/dist/repository/project.js +74 -25
- package/dist/resources/resourceDiscovery.js +2 -2
- package/dist/resources/resourceGc.js +7 -4
- package/dist/resources/resourceRegistrar.js +2 -1
- package/dist/runtime/agentEndpoint.js +8 -0
- package/dist/runtime/agentHost.js +170 -3
- package/dist/runtime/codexAppServerRuntime.js +1 -1
- package/dist/runtime/codexNativeAccess.js +175 -0
- package/dist/runtime/controlledCodexTui.js +116 -0
- package/dist/runtime/publicReply.js +23 -0
- package/dist/runtime/runtimeObservation.js +48 -0
- package/dist/runtime/structuredProviderHost.js +16 -7
- package/dist/runtime/taskCostMetrics.js +133 -0
- package/dist/runtime/taskUsageMetrics.js +11 -2
- package/dist/runtime/taskUsageQuery.js +38 -0
- package/dist/scheduler/leaderWakeupProcessor.js +1 -9
- package/dist/scheduler/taskExecutionProjection.js +2 -0
- package/dist/scheduler/taskObservabilityProjection.js +9 -1
- package/dist/storage/homeLayout.js +13 -0
- package/dist/storage/sqliteSchema.js +44 -0
- package/dist/storage/sqliteStore.js +53 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +2 -0
- package/dist/storage/taskSearch.js +35 -0
- package/dist/storage/upgrade/projectKnowledgeMigration.js +52 -0
- package/dist/task/ordinaryArchive.js +42 -0
- package/dist/tmux/tmuxManager.js +8 -8
- package/dist/web/assets/assetManifest.js +11 -51
- package/dist/web/assets/client/app/catalog.js +215 -0
- package/dist/web/assets/client/app/main.js +148 -0
- package/dist/web/assets/client/app/selection.js +139 -0
- package/dist/web/assets/client/app/settings.js +326 -0
- package/dist/web/assets/client/app/taskView.js +280 -0
- package/dist/web/assets/client/domain/context.js +96 -0
- package/dist/web/assets/client/domain/records.js +126 -0
- package/dist/web/assets/client/domain/runs.js +119 -0
- package/dist/web/assets/client/domain/taskForms.js +197 -0
- package/dist/web/assets/client/domain/vocab.js +69 -0
- package/dist/web/assets/client/domain/work.js +116 -0
- package/dist/web/assets/client/i18n/en.js +900 -0
- package/dist/web/assets/client/i18n/zh-CN.js +898 -0
- package/dist/web/assets/client/index.js +114 -0
- package/dist/web/assets/client/layout/resizer.js +37 -0
- package/dist/web/assets/client/layout/router.js +35 -0
- package/dist/web/assets/client/layout/shortcuts.js +45 -0
- package/dist/web/assets/client/layout/sizing.js +95 -0
- package/dist/web/assets/client/layout/workspace.js +247 -0
- package/dist/web/assets/client/{api.js → lib/api.js} +20 -4
- package/dist/web/assets/client/{dom.js → lib/dom.js} +24 -12
- package/dist/web/assets/client/{format.js → lib/format.js} +7 -0
- package/dist/web/assets/client/lib/i18n.js +54 -0
- package/dist/web/assets/client/lib/markdown.js +167 -0
- package/dist/web/assets/client/lib/prefs.js +28 -0
- package/dist/web/assets/client/lib/theme.js +68 -0
- package/dist/web/assets/client/ui/containers.js +101 -0
- package/dist/web/assets/client/ui/controls.js +51 -0
- package/dist/web/assets/client/ui/forms.js +48 -0
- package/dist/web/assets/client/ui/metrics.js +137 -0
- package/dist/web/assets/client/ui/primitives.js +92 -0
- package/dist/web/assets/client/ui/text.js +33 -0
- package/dist/web/assets/client/ui/toast.js +12 -0
- package/dist/web/assets/client/views/bodySearch.js +172 -0
- package/dist/web/assets/client/views/createTask.js +85 -0
- package/dist/web/assets/client/views/dock/conversation.js +612 -0
- package/dist/web/assets/client/views/dock/discussion.js +173 -0
- package/dist/web/assets/client/views/dock/preview.js +129 -0
- package/dist/web/assets/client/views/dock/terminal.js +199 -0
- package/dist/web/assets/client/views/globalInput.js +67 -0
- package/dist/web/assets/client/{overview.js → views/overview.js} +61 -51
- package/dist/web/assets/client/{sidebar.js → views/sidebar.js} +56 -62
- package/dist/web/assets/client/views/skills.js +152 -0
- package/dist/web/assets/client/views/task/actions.js +36 -0
- package/dist/web/assets/client/views/task/delivery.js +185 -0
- package/dist/web/assets/client/views/task/deliveryResults.js +159 -0
- package/dist/web/assets/client/views/task/files.js +272 -0
- package/dist/web/assets/client/views/task/observation.js +153 -0
- package/dist/web/assets/client/views/task/overview.js +161 -0
- package/dist/web/assets/client/views/task/page.js +131 -0
- package/dist/web/assets/client/views/task/records.js +163 -0
- package/dist/web/assets/client/views/task/runtime.js +130 -0
- package/dist/web/assets/client/views/task/timeline.js +190 -0
- package/dist/web/assets/shared/geometry.js +19 -0
- package/dist/web/assets/shared/taskTabs.js +8 -0
- package/dist/web/assets/shared/themes.js +93 -0
- package/dist/web/assets/shell/dialogs.js +63 -0
- package/dist/web/assets/shell/dock.js +40 -0
- package/dist/web/assets/shell/index.js +40 -0
- package/dist/web/assets/shell/settings.js +34 -0
- package/dist/web/assets/shell/sidebar.js +45 -0
- package/dist/web/assets/styles/index.js +41 -0
- package/dist/web/assets/styles/{responsive.js → layout/responsive.js} +10 -10
- package/dist/web/assets/styles/{layout.js → layout/workspace.js} +17 -24
- package/dist/web/assets/styles/markdown.js +22 -3
- package/dist/web/assets/styles/tokens.js +10 -63
- package/dist/web/assets/styles/ui/controls.js +76 -0
- package/dist/web/assets/styles/ui/forms.js +46 -0
- package/dist/web/assets/styles/ui/surfaces.js +122 -0
- package/dist/web/assets/styles/views/delivery.js +29 -0
- package/dist/web/assets/styles/views/dock.js +71 -0
- package/dist/web/assets/styles/views/page.js +32 -0
- package/dist/web/assets/styles/views/records.js +70 -0
- package/dist/web/assets/styles/views/settings.js +26 -0
- package/dist/web/assets/styles/views/sidebar.js +74 -0
- package/dist/web/assets/styles/views/task.js +80 -0
- package/dist/web/assets/styles/views/terminal.js +38 -0
- package/dist/web/taskPreviews.js +205 -0
- package/dist/web/tmuxWebTerminal.js +16 -9
- package/dist/web/webConversation.js +373 -0
- package/dist/web/webMaterials.js +155 -0
- package/dist/web/webServer.js +295 -18
- package/dist/web/webSettings.js +331 -0
- package/dist/web/webSkills.js +86 -0
- package/dist/web/webSnapshot.js +40 -8
- package/dist/web/webTaskSurface.js +106 -8
- package/dist/workspace/gitChangeSetCapture.js +3 -1
- package/docs/cli-information-contract.md +1 -1
- package/docs/cli-information-contract.zh-CN.md +1 -1
- package/docs/managed-turn-and-session-runtime.md +31 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +22 -0
- package/docs/release-workflow.md +54 -0
- package/docs/release-workflow.zh-CN.md +39 -0
- package/docs/storage-baseline.md +38 -1
- package/docs/storage-baseline.zh-CN.md +22 -0
- package/docs/task-discovery.md +28 -0
- package/docs/testing/verification-levels.md +2 -2
- package/i18n/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -4
- package/skills/yui-leader/references/execution.md +2 -1
- package/skills/yui-operator/SKILL.md +3 -0
- package/skills/yui-reviewer/SKILL.md +4 -2
- package/skills/yui-runtime/SKILL.md +43 -2
- package/skills/yui-worker/SKILL.md +3 -2
- package/dist/web/assets/client/app.js +0 -808
- package/dist/web/assets/client/components.js +0 -477
- package/dist/web/assets/client/detail.js +0 -379
- package/dist/web/assets/client/dock.js +0 -309
- package/dist/web/assets/client/evidence.js +0 -364
- package/dist/web/assets/client/forms.js +0 -230
- package/dist/web/assets/client/i18n.js +0 -1277
- package/dist/web/assets/client/markdown.js +0 -114
- package/dist/web/assets/client/records.js +0 -198
- package/dist/web/assets/client/sections.js +0 -349
- package/dist/web/assets/client/theme.js +0 -71
- package/dist/web/assets/shell.js +0 -161
- package/dist/web/assets/styles/components.js +0 -216
- package/dist/web/assets/styles/views.js +0 -309
- /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, "<")
|
|
16
|
-
.replace(/>/g, ">")
|
|
17
|
-
.replace(/"/g, """);
|
|
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
|
-
`;
|