@zq-silk/yui 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +17 -8
- 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 +25 -2
- 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 +10 -1
- 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 +32 -6
- package/dist/controller/jobControl.js +2 -0
- package/dist/controller/nativeHostInput.js +37 -0
- 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/codexConfigConflict.js +6 -4
- package/dist/executor/effectiveLaunch.js +24 -1
- package/dist/executor/fileRoleLaunchPlanner.js +13 -5
- package/dist/integration/gitIntegrationService.js +7 -12
- package/dist/integration/integrationSourceApplication.js +4 -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/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/managed-turn-and-session-runtime.md +9 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +7 -0
- package/docs/release-workflow.md +28 -0
- package/docs/release-workflow.zh-CN.md +21 -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/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
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export const CONTEXT_SCRIPT = String.raw `
|
|
2
|
+
// Context access. Every business fact keeps its Context reference; an
|
|
3
|
+
// expanded value is a current read, never a mutation of the snapshot.
|
|
4
|
+
|
|
5
|
+
export function entriesOf(core, store) {
|
|
6
|
+
return core.records.filter(function (entry) { return entry.ref.store === store; });
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function valuesOf(core, store) {
|
|
10
|
+
return entriesOf(core, store).filter(function (entry) { return !entry.omitted; }).map(function (entry) { return entry.value; });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// The family total from the same bounded read as its samples. The current
|
|
14
|
+
// view counts current records only: open work, active runs, open questions.
|
|
15
|
+
export function totalOf(core, store) {
|
|
16
|
+
const row = (core.collections || []).find(function (item) { return item.store === store; });
|
|
17
|
+
return row ? row.total : entriesOf(core, store).length;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// List and omitted-value summaries join known fields in a fixed order
|
|
21
|
+
// ("title: …; status: …"). Split them for display only; the exact record
|
|
22
|
+
// stays one read away.
|
|
23
|
+
const SUMMARY_KEYS = ["title", "scope", "expiresWhen", "version", "objective", "question", "summary", "body", "status", "type"];
|
|
24
|
+
export function summaryFields(summary) {
|
|
25
|
+
const fields = {};
|
|
26
|
+
if (!summary) return fields;
|
|
27
|
+
const pattern = new RegExp("(?:^|; )(" + SUMMARY_KEYS.join("|") + "): ", "g");
|
|
28
|
+
const marks = [];
|
|
29
|
+
let match;
|
|
30
|
+
while ((match = pattern.exec(summary))) {
|
|
31
|
+
const rank = SUMMARY_KEYS.indexOf(match[1]);
|
|
32
|
+
if (marks.length && rank <= marks[marks.length - 1].rank) continue;
|
|
33
|
+
marks.push({ key: match[1], rank: rank, start: match.index, from: match.index + match[0].length });
|
|
34
|
+
}
|
|
35
|
+
marks.forEach(function (mark, index) {
|
|
36
|
+
fields[mark.key] = summary.slice(mark.from, index + 1 < marks.length ? marks[index + 1].start : summary.length);
|
|
37
|
+
});
|
|
38
|
+
return fields;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// --- Exact reads -------------------------------------------------------------
|
|
42
|
+
// One inspect per reference digest, held in the Task's view state so a
|
|
43
|
+
// refresh with an unchanged reference never reads again. A changed digest is
|
|
44
|
+
// a new read.
|
|
45
|
+
export function exactCache(view) {
|
|
46
|
+
view.exact = view.exact || {};
|
|
47
|
+
return view.exact;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function readExact(entry, taskId, actions, cache) {
|
|
51
|
+
const key = entry.ref.store + "/" + entry.ref.refId;
|
|
52
|
+
const hit = cache[key];
|
|
53
|
+
if (hit && hit.digest === entry.ref.digest) return hit.promise;
|
|
54
|
+
const promise = actions.inspect(taskId, entry.ref);
|
|
55
|
+
const slot = { digest: entry.ref.digest, promise: promise, result: null };
|
|
56
|
+
cache[key] = slot;
|
|
57
|
+
promise.then(function (result) { slot.result = result; }, function () { if (cache[key] === slot) delete cache[key]; });
|
|
58
|
+
return promise;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The settled exact read for this exact reference, or null.
|
|
62
|
+
export function cachedExact(cache, entry) {
|
|
63
|
+
const hit = cache[entry.ref.store + "/" + entry.ref.refId];
|
|
64
|
+
return hit && hit.digest === entry.ref.digest && hit.result ? hit.result : null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// --- Paged family discovery --------------------------------------------------
|
|
68
|
+
// Pages of one Context family for the current Context cursor. A continuation
|
|
69
|
+
// never mixes versions: after the cursor changes, the first page is re-read.
|
|
70
|
+
export function familyState(view, store) {
|
|
71
|
+
view.families = view.families || {};
|
|
72
|
+
if (!view.families[store]) view.families[store] = { items: [], total: null, nextCursor: null, pages: 0, key: null, error: null, pending: null };
|
|
73
|
+
return view.families[store];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function loadFamily(view, taskId, store, key, actions, options) {
|
|
77
|
+
const state = familyState(view, store);
|
|
78
|
+
const more = !!(options && options.more);
|
|
79
|
+
if (state.pending) return state.pending;
|
|
80
|
+
if (more ? !state.nextCursor : state.key === key && state.pages && !state.error) return Promise.resolve(state);
|
|
81
|
+
state.pending = actions.list(taskId, store, { limit: (options && options.limit) || 40, cursor: more ? state.nextCursor : null })
|
|
82
|
+
.then(function (page) {
|
|
83
|
+
if (!more) { state.items = []; state.pages = 0; }
|
|
84
|
+
state.items = state.items.concat(page.items);
|
|
85
|
+
state.total = page.total;
|
|
86
|
+
state.nextCursor = page.nextCursor;
|
|
87
|
+
state.pages += 1;
|
|
88
|
+
state.key = more ? state.key : key;
|
|
89
|
+
state.error = null;
|
|
90
|
+
state.errorKey = null;
|
|
91
|
+
return state;
|
|
92
|
+
}, function (error) { state.error = error.message; state.errorKey = more ? state.key : key; return state; })
|
|
93
|
+
.then(function (result) { state.pending = null; return result; });
|
|
94
|
+
return state.pending;
|
|
95
|
+
}
|
|
96
|
+
`;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export const RECORDS_SCRIPT = String.raw `
|
|
2
|
+
// Record rows and cards. A row keeps its Context reference: an omitted value
|
|
3
|
+
// is shown as the honest summary until its exact read arrives, and reading
|
|
4
|
+
// never acknowledges or accepts anything.
|
|
5
|
+
import { h, icon } from "/assets/js/lib/dom.js";
|
|
6
|
+
import { button, jsonBlock, mono, note } from "/assets/js/ui/primitives.js";
|
|
7
|
+
import { rawDisclosure } from "/assets/js/ui/containers.js";
|
|
8
|
+
import { richText } from "/assets/js/ui/text.js";
|
|
9
|
+
import { cachedExact, readExact, summaryFields } from "/assets/js/domain/context.js";
|
|
10
|
+
|
|
11
|
+
// A row drawn from its exact value: synchronously when cached, otherwise the
|
|
12
|
+
// summary card until the read returns and replaces it in place.
|
|
13
|
+
export function exactRow(entry, taskId, t, actions, cache, render) {
|
|
14
|
+
if (!entry.omitted) return render(entry.value, null);
|
|
15
|
+
const hit = cachedExact(cache, entry);
|
|
16
|
+
if (hit) return render(hit.value, hit);
|
|
17
|
+
const placeholder = recordCard(entry, taskId, t, actions);
|
|
18
|
+
placeholder.dataset.reading = "true";
|
|
19
|
+
readExact(entry, taskId, actions, cache).then(function (result) {
|
|
20
|
+
if (placeholder.isConnected) placeholder.replaceWith(render(result.value, result));
|
|
21
|
+
}, function () { placeholder.dataset.reading = "false"; });
|
|
22
|
+
return placeholder;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// A closed row drawn from a list summary; its exact value is read on first
|
|
26
|
+
// open. Open rows are remembered in view state and reopen after a redraw.
|
|
27
|
+
export function lazyRow(item, taskId, t, actions, options) {
|
|
28
|
+
const opts = options || {};
|
|
29
|
+
const key = item.ref.store + ":" + item.ref.refId;
|
|
30
|
+
const element = h("details.lazy-row");
|
|
31
|
+
if (item.status) element.dataset.status = item.status;
|
|
32
|
+
const body = h("div.lazy-body");
|
|
33
|
+
element.append(h("summary.lazy-head", null, icon("chevron", "disclosure-chevron"), opts.head), body);
|
|
34
|
+
let state = "idle";
|
|
35
|
+
function load() {
|
|
36
|
+
if (state !== "idle") return;
|
|
37
|
+
state = "reading";
|
|
38
|
+
element.dataset.reading = "true";
|
|
39
|
+
body.replaceChildren(h("p.faint.small", null, t("record.reading")));
|
|
40
|
+
readExact({ ref: item.ref }, taskId, actions, opts.cache || {}).then(function (result) {
|
|
41
|
+
state = "done";
|
|
42
|
+
body.replaceChildren();
|
|
43
|
+
body.append(h("div.lazy-content", null, opts.render ? opts.render(result.value, result) : null), rawDisclosure(t, result.value));
|
|
44
|
+
}, function (error) {
|
|
45
|
+
state = "idle";
|
|
46
|
+
body.replaceChildren(note(t("record.changed") + " · " + error.message, "bad"));
|
|
47
|
+
}).then(function () { element.dataset.reading = "false"; });
|
|
48
|
+
}
|
|
49
|
+
element.addEventListener("toggle", function () {
|
|
50
|
+
if (opts.openRows) { if (element.open) opts.openRows[key] = true; else delete opts.openRows[key]; }
|
|
51
|
+
if (element.open) load();
|
|
52
|
+
});
|
|
53
|
+
if (opts.openRows && opts.openRows[key]) element.open = true;
|
|
54
|
+
return element;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// A generic card for any record: omitted values say so and can be read by
|
|
58
|
+
// reference.
|
|
59
|
+
export function recordCard(entry, taskId, t, actions, options) {
|
|
60
|
+
const opts = options || {};
|
|
61
|
+
const element = h("article.record" + (opts.compact ? ".is-compact" : ""));
|
|
62
|
+
element.append(h("header.record-head", null,
|
|
63
|
+
h("span.record-store", null, t("store." + entry.ref.store, entry.ref.store)),
|
|
64
|
+
mono(entry.ref.refId),
|
|
65
|
+
h("span.spacer"),
|
|
66
|
+
h("span.faint.small", { title: t("record.revision") }, String(entry.ref.revision).slice(0, 24))));
|
|
67
|
+
element.append.apply(element, !entry.omitted && entry.value ? valueLines(entry, t) : summaryLines(entry, t));
|
|
68
|
+
element.append(recordReader(entry, taskId, t, actions));
|
|
69
|
+
return element;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function valueLines(entry, t) {
|
|
73
|
+
const value = entry.value;
|
|
74
|
+
const lines = [];
|
|
75
|
+
const text = value.content || value.summary || value.body || value.objective || value.title || value.leaderSummary;
|
|
76
|
+
if (text) lines.push(richText(null, text, t, { threshold: 360 }));
|
|
77
|
+
if (entry.ref.store === "task-decision" && value.rationale) lines.push(richText(t("record.rationale"), value.rationale, t, { muted: true }));
|
|
78
|
+
const facts = [value.status, value.roleName].filter(Boolean);
|
|
79
|
+
if (facts.length) lines.push(h("p.faint.small", null, facts.join(" · ")));
|
|
80
|
+
if (entry.ref.store === "job") {
|
|
81
|
+
lines.push(note(t("job." + value.status + ".note", t("job.terminal.note")), value.status === "unknown-needs-attention" ? "warn" : ""));
|
|
82
|
+
}
|
|
83
|
+
return lines;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function summaryLines(entry, t) {
|
|
87
|
+
const fields = summaryFields(entry.summary);
|
|
88
|
+
const lead = fields.title || fields.question || fields.objective;
|
|
89
|
+
const text = fields.summary || fields.body || (lead === fields.objective ? null : fields.objective);
|
|
90
|
+
const lines = [];
|
|
91
|
+
if (lead) lines.push(h("p.record-lead", null, lead));
|
|
92
|
+
if (text) lines.push(h("p.faint", null, text));
|
|
93
|
+
if (!lead && !text) lines.push(h("p.faint", null, entry.summary || t("record.omitted")));
|
|
94
|
+
return lines;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Shared source access for cards, history and discussion, including records
|
|
98
|
+
// whose compact value is present but does not display every durable field.
|
|
99
|
+
// With a renderer, the exact value is drawn richly and the raw record stays
|
|
100
|
+
// available behind a disclosure.
|
|
101
|
+
export function recordReader(entry, taskId, t, actions, render) {
|
|
102
|
+
const element = h("div.stack");
|
|
103
|
+
const expand = button(t("record.read"), { icon: "eye", variant: "link" });
|
|
104
|
+
expand.addEventListener("click", async function () {
|
|
105
|
+
expand.disabled = true;
|
|
106
|
+
element.dataset.reading = "true";
|
|
107
|
+
try {
|
|
108
|
+
const result = await actions.inspect(taskId, entry.ref);
|
|
109
|
+
const rich = render ? render(result.value, result) : null;
|
|
110
|
+
if (rich) element.append(rich, rawDisclosure(t, result.value));
|
|
111
|
+
else {
|
|
112
|
+
if (result.result && result.result.output) element.append(richText(t("record.output"), result.result.output, t));
|
|
113
|
+
if (result.result && result.result.diagnostic) element.append(richText(t("record.diagnostic"), result.result.diagnostic, t));
|
|
114
|
+
element.append(jsonBlock(result.value));
|
|
115
|
+
if (result.execution) element.append(jsonBlock(result.execution));
|
|
116
|
+
}
|
|
117
|
+
expand.remove();
|
|
118
|
+
} catch {
|
|
119
|
+
expand.disabled = false;
|
|
120
|
+
expand.replaceChildren(icon("refresh"), h("span", null, t("record.changed")));
|
|
121
|
+
} finally { element.dataset.reading = "false"; }
|
|
122
|
+
});
|
|
123
|
+
element.append(h("div.record-actions", null, expand));
|
|
124
|
+
return element;
|
|
125
|
+
}
|
|
126
|
+
`;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
export const RUNS_SCRIPT = String.raw `
|
|
2
|
+
// Execution records, review rounds and Roles.
|
|
3
|
+
import { h } from "/assets/js/lib/dom.js";
|
|
4
|
+
import { formatDateTime } from "/assets/js/lib/format.js";
|
|
5
|
+
import { button, chip, dot, kv, mono, note, timeTag } from "/assets/js/ui/primitives.js";
|
|
6
|
+
import { richText } from "/assets/js/ui/text.js";
|
|
7
|
+
import { agentChips, label, statusBadge, tone } from "/assets/js/domain/vocab.js";
|
|
8
|
+
|
|
9
|
+
export function runCard(run, t, locale) {
|
|
10
|
+
const element = h("article.run-card");
|
|
11
|
+
element.dataset.status = run.status;
|
|
12
|
+
element.append(h("header.run-head", null,
|
|
13
|
+
dot(tone("run", run.status)),
|
|
14
|
+
h("span.run-role", null, run.roleName),
|
|
15
|
+
mono(run.id),
|
|
16
|
+
run.purpose ? chip(label(t, "run.purpose", run.purpose)) : null,
|
|
17
|
+
run.workItemId ? chip(run.workItemId) : null,
|
|
18
|
+
h("span.spacer"),
|
|
19
|
+
statusBadge(t, "run", "run", run.status)), runBody(run, t, locale));
|
|
20
|
+
return element;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// What one execution record says: delivery, instruction, report, failure.
|
|
24
|
+
export function runBody(run, t, locale) {
|
|
25
|
+
const parts = [];
|
|
26
|
+
const input = run.inputs && run.inputs.length ? run.inputs[0].input : null;
|
|
27
|
+
parts.push(h("div.meta-line", null,
|
|
28
|
+
h("span", null, t("run.delivery") + ": " + label(t, "run.delivery", (run.execution && run.execution.delivery) || "unobserved")),
|
|
29
|
+
run.mode ? h("span", null, label(t, "mode", run.mode)) : null,
|
|
30
|
+
timeTag((run.result && run.result.completedAt) || run.updatedAt, locale, t),
|
|
31
|
+
agentChips(run.effective || (run.agentId ? run : null))));
|
|
32
|
+
const directive = input && (input.directive || input.action);
|
|
33
|
+
if (directive) parts.push(richText(t("run.instruction"), directive, t, { threshold: 320 }));
|
|
34
|
+
if (run.result && run.result.output) parts.push(richText(t("run.output"), run.result.output, t, { threshold: 320, className: "callout" }));
|
|
35
|
+
if (run.result && run.result.diagnostic) parts.push(richText(t("run.failure"), run.result.diagnostic, t, { threshold: 320, className: "callout.tone-bad" }));
|
|
36
|
+
if (run.executionGroupId) {
|
|
37
|
+
parts.push(h("p.faint.mono-line", null, t("run.lineage") + " " + run.executionGroupId + (run.executionLaneId ? "/" + run.executionLaneId : "")));
|
|
38
|
+
}
|
|
39
|
+
parts.push(skillPackageDetails(run.effective && run.effective.skillPackages, t));
|
|
40
|
+
return parts.filter(Boolean);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function skillPackageDetails(packages, t) {
|
|
44
|
+
const details = h("details", null, h("summary", null, t("role.skills") + " · " + (packages ? packages.length : t("skills.unrecorded"))));
|
|
45
|
+
if (!packages) {
|
|
46
|
+
details.append(note(t("skills.legacyNote")));
|
|
47
|
+
return details;
|
|
48
|
+
}
|
|
49
|
+
packages.forEach(function (skill) {
|
|
50
|
+
details.append(h("div.record", null, h("strong", null, skill.id),
|
|
51
|
+
h("p.small.mono-line", null, skill.digest),
|
|
52
|
+
h("p.small", null, skill.source.kind + " · " + skill.fileCount + " " + t("skills.files") + " · " + skill.byteSize + " B"),
|
|
53
|
+
h("p.small.mono-line", null, t("skills.source") + ": " + skill.source.path),
|
|
54
|
+
h("p.small.mono-line", null, t("skills.snapshot") + ": " + skill.path),
|
|
55
|
+
h("p.small.mono-line", null, t("skills.inventory") + ": " + skill.manifestPath)));
|
|
56
|
+
});
|
|
57
|
+
return details;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function reviewCard(round, t, locale) {
|
|
61
|
+
const element = h("article.review-card");
|
|
62
|
+
element.append(h("header.run-head", null,
|
|
63
|
+
dot(tone("review", round.status)),
|
|
64
|
+
h("span.run-role", null, round.reviewerRoleName || t("review.reviewer")),
|
|
65
|
+
mono(round.id),
|
|
66
|
+
round.scope ? chip(label(t, "review.scope", round.scope)) : null,
|
|
67
|
+
h("span.spacer"),
|
|
68
|
+
statusBadge(t, "review", "review", round.status)));
|
|
69
|
+
element.append(h("div.meta-line", null,
|
|
70
|
+
round.workItemId ? mono(round.workItemId + (round.candidateId ? " · " + round.candidateId : "")) : null,
|
|
71
|
+
round.taskCandidate && round.taskCandidate.projects ? round.taskCandidate.projects.map(function (project) {
|
|
72
|
+
return mono(project.projectId + " @ " + String(project.commit).slice(0, 12));
|
|
73
|
+
}) : null,
|
|
74
|
+
round.reviewBaseCommit ? h("span", null, t("review.base") + " ", mono(String(round.reviewBaseCommit).slice(0, 12))) : null,
|
|
75
|
+
round.reviewerRunId ? h("span", null, t("review.run") + " ", mono(round.reviewerRunId)) : null,
|
|
76
|
+
timeTag(round.createdAt, locale, t)));
|
|
77
|
+
if (round.failure) element.append(note(round.failure.kind + ": " + round.failure.message, "bad"));
|
|
78
|
+
return element;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function roleCard(role, t, locale, options) {
|
|
82
|
+
const opts = options || {};
|
|
83
|
+
const element = h("article.role-card");
|
|
84
|
+
const binding = role.agentBindings && role.agentBindings[role.activeAgentId];
|
|
85
|
+
element.append(h("header.role-head", null,
|
|
86
|
+
h("span.avatar", { "aria-hidden": "true" }, String(role.name || "?").slice(0, 1).toUpperCase()),
|
|
87
|
+
h("div.role-title", null, h("strong", null, role.name),
|
|
88
|
+
h("span.faint", null, role.activeAgentId || "")),
|
|
89
|
+
opts.status ? statusBadge(t, "role", "role", opts.status) : null));
|
|
90
|
+
if (binding) element.append(agentChips({
|
|
91
|
+
adapterId: binding.adapterId, component: binding.component,
|
|
92
|
+
model: binding.config && binding.config.model, effort: binding.config && binding.config.effort
|
|
93
|
+
}));
|
|
94
|
+
if (role.description) element.append(richText(null, role.description, t, { muted: true, threshold: 280 }));
|
|
95
|
+
element.append(kv(roleFacts(role, t, locale, opts)));
|
|
96
|
+
if (opts.runtimeRole && opts.runtimeRole.runtimeSession) {
|
|
97
|
+
element.append(skillPackageDetails(opts.runtimeRole.runtimeSession.skillPackages, t));
|
|
98
|
+
}
|
|
99
|
+
if (opts.onOpen) {
|
|
100
|
+
element.append(h("div.role-actions", null, button(t("role.openSession"), {
|
|
101
|
+
icon: "terminal", variant: "ghost", onClick: function () { opts.onOpen(role.name); }
|
|
102
|
+
})));
|
|
103
|
+
}
|
|
104
|
+
return element;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function roleFacts(role, t, locale, opts) {
|
|
108
|
+
const session = opts.runtimeRole && opts.runtimeRole.runtimeSession;
|
|
109
|
+
const rows = [];
|
|
110
|
+
rows.push([t("role.session"), session && session.nativeSessionId ? mono(session.nativeSessionId) : t("role.noSession")]);
|
|
111
|
+
if (role.launchRevision !== undefined) rows.push([t("role.desired"), "r" + role.launchRevision + (opts.launchDrift ? " · " + t("role.drift") : "")]);
|
|
112
|
+
if (role.defaultAccess !== undefined) rows.push([t("role.access"), role.defaultAccess]);
|
|
113
|
+
if (opts.retry) rows.push([t("role.providerRetry"), label(t, "retry", opts.retry.status) + " · "
|
|
114
|
+
+ opts.retry.attempts + "/" + opts.retry.limit
|
|
115
|
+
+ (opts.retry.status === "waiting" && opts.retry.nextEligibleAt ? " · " + formatDateTime(opts.retry.nextEligibleAt, locale) : "")]);
|
|
116
|
+
if (role.skills && role.skills.length) rows.push([t("role.skills"), h("span.chip-row", null, role.skills.map(function (s) { return chip(s); }))]);
|
|
117
|
+
return rows;
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
export const TASK_FORMS_SCRIPT = String.raw `
|
|
2
|
+
// Task write forms: the discussion composer, queue / steer / interrupt and
|
|
3
|
+
// the title editor. Submission plumbing (requestId, unsent flag, receipts,
|
|
4
|
+
// never replaying an unknown outcome) lives in ui/forms submitWrite().
|
|
5
|
+
import { h, icon, clear } from "/assets/js/lib/dom.js";
|
|
6
|
+
import { fill } from "/assets/js/lib/format.js";
|
|
7
|
+
import { button } from "/assets/js/ui/primitives.js";
|
|
8
|
+
import { segmented } from "/assets/js/ui/controls.js";
|
|
9
|
+
import { field, receiptLine, setReceipt, submitWrite } from "/assets/js/ui/forms.js";
|
|
10
|
+
|
|
11
|
+
function isOpen(task) {
|
|
12
|
+
return ["active", "draft"].includes(task.status);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// --- Discussion composer -----------------------------------------------------
|
|
16
|
+
export function messageComposer(task, t, actions) {
|
|
17
|
+
let intent = "discuss";
|
|
18
|
+
const form = h("form.composer");
|
|
19
|
+
const message = h("textarea.composer-input", {
|
|
20
|
+
required: true, maxLength: 8000, rows: 3, placeholder: t("composer.placeholder"),
|
|
21
|
+
"aria-label": t("composer.label")
|
|
22
|
+
});
|
|
23
|
+
const open = isOpen(task);
|
|
24
|
+
const send = h("button.btn.btn-primary.composer-send", { type: "submit", disabled: !open, title: t("composer.sendHint") },
|
|
25
|
+
icon("send"), h("span", null, t("composer.send")));
|
|
26
|
+
const intents = segmented("intent", ["discuss", "record", "develop"].map(function (value) {
|
|
27
|
+
return { value: value, label: t("intent." + value), title: t("intent." + value + ".hint") };
|
|
28
|
+
}), intent, function (value) { intent = value; });
|
|
29
|
+
const receipt = receiptLine(open ? t("receipt.notSubmitted") : t("composer.closed"));
|
|
30
|
+
const facets = h("div.facets");
|
|
31
|
+
form.append(h("div.composer-box", null, message, h("div.composer-bar", null, intents, h("span.spacer"), send)), receipt, facets);
|
|
32
|
+
form.addEventListener("input", function () { form.dataset.unsent = message.value ? "true" : "false"; });
|
|
33
|
+
message.addEventListener("keydown", function (event) {
|
|
34
|
+
if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) { event.preventDefault(); form.requestSubmit(); }
|
|
35
|
+
});
|
|
36
|
+
form.addEventListener("submit", function (event) {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
if (send.disabled || !message.value.trim()) return;
|
|
39
|
+
clear(facets);
|
|
40
|
+
// requestId is the submission key: the same key retried returns the
|
|
41
|
+
// original Message and routing, never a second submission.
|
|
42
|
+
submitWrite({
|
|
43
|
+
control: send, receipt: receipt, form: form, t: t,
|
|
44
|
+
send: function (requestId) { return actions.sendMessage(task.id, message.value, requestId, intent); },
|
|
45
|
+
saved: function (result) {
|
|
46
|
+
setReceipt(receipt, t("receipt.saved") + " · " + result.record.id, "ok");
|
|
47
|
+
renderSubmissionFacets(facets, result.submission, t);
|
|
48
|
+
message.value = "";
|
|
49
|
+
},
|
|
50
|
+
rejectedKey: "receipt.notSubmittedBecause", unknownKey: "receipt.unknownMessage", afterWrite: actions.afterWrite
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
return form;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Each submission facet is reported on its own line, never collapsed into a
|
|
57
|
+
// single "started". A missing facet block is a contract violation and is
|
|
58
|
+
// reported as such rather than reinterpreted.
|
|
59
|
+
export function renderSubmissionFacets(container, submission, t) {
|
|
60
|
+
clear(container);
|
|
61
|
+
if (!submission) { container.append(h("p.faint.small", null, t("facet.missing"))); return; }
|
|
62
|
+
const rows = [
|
|
63
|
+
["facet.phase", "phase." + submission.phase, submission.phase],
|
|
64
|
+
["facet.planning", "planningFacet." + submission.planning, submission.planning],
|
|
65
|
+
["facet.activation", "activation." + submission.activation, submission.activation],
|
|
66
|
+
["facet.delivery", "deliveryFacet." + submission.delivery, submission.delivery]
|
|
67
|
+
];
|
|
68
|
+
container.append(h("dl.kv.kv-compact", null, rows.map(function (row) {
|
|
69
|
+
return [h("dt", null, t(row[0])), h("dd", null, t(row[1], row[2]))];
|
|
70
|
+
})));
|
|
71
|
+
const step = submission.nextStep;
|
|
72
|
+
const text = step ? nextStepText(step, t) : "";
|
|
73
|
+
if (text) container.append(h("p.next-step", { dataset: { nextStep: step.kind } }, icon("flag", "icon-sm"), h("span", null, text)));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function nextStepText(step, t) {
|
|
77
|
+
if (step.kind === "activate-manually") return fill(t("next.activateManually"), { task: step.taskId });
|
|
78
|
+
if (step.kind === "start-execution") return fill(t("next.startExecution"), { task: step.taskId });
|
|
79
|
+
if (step.kind === "await-pending-activation") return fill(t("next.awaitActivation"), { ref: step.activationRef });
|
|
80
|
+
if (step.kind === "resolve-failed-activation") return fill(t("next.resolveFailed"), { failure: step.failure, ref: step.activationRef });
|
|
81
|
+
return "";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// --- Queue / steer / interrupt ----------------------------------------------
|
|
85
|
+
// The same three application-layer actions the CLI drives. Steer and
|
|
86
|
+
// interrupt name an exact current Turn; a mismatch is a visible failure
|
|
87
|
+
// receipt, never a silent downgrade to a queue.
|
|
88
|
+
export function controlForm(task, t, actions, roleNames) {
|
|
89
|
+
let kind = "queue";
|
|
90
|
+
const form = h("form.stack");
|
|
91
|
+
const inputs = controlInputs(task, t, roleNames);
|
|
92
|
+
const fields = controlFields(inputs, t);
|
|
93
|
+
const submit = button(t("control.submit"), { type: "submit", variant: "primary", icon: "send", disabled: !isOpen(task) });
|
|
94
|
+
const receipt = receiptLine(t("receipt.notSubmitted"));
|
|
95
|
+
const apply = function () { applyControlKind(kind, inputs, fields); };
|
|
96
|
+
const actionsSeg = segmented("action", ["queue", "steer", "interrupt"].map(function (value) {
|
|
97
|
+
return { value: value, label: t("control." + value), title: t("control." + value + ".hint") };
|
|
98
|
+
}), kind, function (value) { kind = value; apply(); });
|
|
99
|
+
form.append(h("div.field", null, h("span", null, t("control.action")), actionsSeg),
|
|
100
|
+
h("div.field-row", null, fields.role, fields.workItem), inputs.roles, fields.body, fields.expected, fields.then,
|
|
101
|
+
h("div.form-actions", null, submit), receipt);
|
|
102
|
+
apply();
|
|
103
|
+
form.addEventListener("input", function () { form.dataset.unsent = "true"; });
|
|
104
|
+
form.addEventListener("submit", function (event) {
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
if (submit.disabled) return;
|
|
107
|
+
submitWrite({
|
|
108
|
+
control: submit, receipt: receipt, form: form, t: t,
|
|
109
|
+
send: function (requestId) { return actions.control(task.id, controlPayload(kind, inputs, requestId)); },
|
|
110
|
+
// The receipt states exactly what settled; none of these states implies
|
|
111
|
+
// the Provider executed the input.
|
|
112
|
+
saved: function (result) { setReceipt(receipt, controlReceipt(result, t), "ok"); },
|
|
113
|
+
rejectedKey: "receipt.notSubmittedBecause", unknownKey: "receipt.unknownControl", afterWrite: actions.afterWrite
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
return form;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function controlInputs(task, t, roleNames) {
|
|
120
|
+
return {
|
|
121
|
+
role: h("input", { list: "control-roles-" + task.id, placeholder: t("control.rolePlaceholder") }),
|
|
122
|
+
roles: h("datalist", { id: "control-roles-" + task.id }, (roleNames || []).map(function (name) { return h("option", { value: name }); })),
|
|
123
|
+
body: h("textarea", { maxLength: 8000, rows: 3 }),
|
|
124
|
+
expected: h("input.mono", { placeholder: t("control.expectedPlaceholder") }),
|
|
125
|
+
workItem: h("input.mono"),
|
|
126
|
+
then: h("input.mono", { placeholder: t("control.thenPlaceholder") })
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function controlFields(inputs, t) {
|
|
131
|
+
return {
|
|
132
|
+
role: field(t("control.role"), inputs.role),
|
|
133
|
+
body: field(t("control.message"), inputs.body),
|
|
134
|
+
expected: field(t("control.expectedTurn"), inputs.expected, t("control.expectedHint")),
|
|
135
|
+
workItem: field(t("control.workItem"), inputs.workItem),
|
|
136
|
+
then: field(t("control.then"), inputs.then)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function applyControlKind(kind, inputs, fields) {
|
|
141
|
+
fields.body.hidden = kind === "interrupt";
|
|
142
|
+
fields.expected.hidden = kind === "queue";
|
|
143
|
+
fields.then.hidden = kind !== "interrupt";
|
|
144
|
+
fields.workItem.hidden = kind === "interrupt";
|
|
145
|
+
inputs.role.required = kind !== "queue";
|
|
146
|
+
inputs.body.required = kind !== "interrupt";
|
|
147
|
+
inputs.expected.required = kind !== "queue";
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function controlPayload(kind, inputs, requestId) {
|
|
151
|
+
const payload = { action: kind, requestId: requestId };
|
|
152
|
+
const role = inputs.role.value.trim();
|
|
153
|
+
if (kind === "interrupt") {
|
|
154
|
+
payload.role = role;
|
|
155
|
+
payload.expectedTarget = inputs.expected.value.trim();
|
|
156
|
+
if (inputs.then.value.trim()) payload.thenMessage = inputs.then.value.trim();
|
|
157
|
+
return payload;
|
|
158
|
+
}
|
|
159
|
+
payload.body = inputs.body.value;
|
|
160
|
+
if (kind === "steer") { payload.to = role; payload.expectedTarget = inputs.expected.value.trim(); }
|
|
161
|
+
else if (role) payload.to = role;
|
|
162
|
+
if (inputs.workItem.value.trim()) payload.workItem = inputs.workItem.value.trim();
|
|
163
|
+
return payload;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function controlReceipt(result, t) {
|
|
167
|
+
const settled = result.steer || result.interrupt || result.delivery || { state: result.disposition };
|
|
168
|
+
return t("receipt.receipt") + " " + result.action + " · " + settled.state
|
|
169
|
+
+ (settled.outcome ? " · " + settled.outcome : "") + (settled.detail ? " · " + settled.detail : "")
|
|
170
|
+
+ (result.record ? " · " + result.record.id : "");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// --- Title -------------------------------------------------------------------
|
|
174
|
+
export function titleForm(task, t, actions) {
|
|
175
|
+
const form = h("form.inline-form");
|
|
176
|
+
const archived = task.status === "archived";
|
|
177
|
+
const input = h("input", { value: task.title, required: true, maxLength: 500, disabled: archived, "aria-label": t("details.title") });
|
|
178
|
+
const save = button(t("actions.save"), { type: "submit", variant: "primary", disabled: archived });
|
|
179
|
+
const receipt = receiptLine(t("receipt.notSubmitted"));
|
|
180
|
+
form.append(h("div.inline-row", null, input, save), receipt);
|
|
181
|
+
form.addEventListener("input", function () { form.dataset.unsent = input.value !== task.title ? "true" : "false"; });
|
|
182
|
+
form.addEventListener("submit", function (event) {
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
if (save.disabled) return;
|
|
185
|
+
submitWrite({
|
|
186
|
+
control: save, receipt: receipt, form: form, t: t,
|
|
187
|
+
send: function (requestId) { return actions.updateTask(task.id, { title: input.value }, requestId); },
|
|
188
|
+
saved: function (result) {
|
|
189
|
+
setReceipt(receipt, t("receipt.savedRevision") + " " + result.revision, "ok");
|
|
190
|
+
input.value = result.record.title;
|
|
191
|
+
},
|
|
192
|
+
rejectedKey: "receipt.notSavedBecause", unknownKey: "receipt.unknownSave", afterWrite: actions.afterWrite
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
return form;
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { AGENT_ADAPTER_CATALOG } from "../../../../agent/adapterCatalog.js";
|
|
2
|
+
import { AGENT_EXECUTION_COMPONENT_CATALOG } from "../../../../agent/executionComponents.js";
|
|
3
|
+
export const VOCAB_SCRIPT = String.raw `
|
|
4
|
+
// Domain vocabulary: translated labels, semantic tones and the status badges
|
|
5
|
+
// built from them. Unknown values stay visible verbatim, never hidden or
|
|
6
|
+
// guessed.
|
|
7
|
+
import { h } from "/assets/js/lib/dom.js";
|
|
8
|
+
import { badge, chip } from "/assets/js/ui/primitives.js";
|
|
9
|
+
|
|
10
|
+
// Generated from the server catalogs, not a second product registry.
|
|
11
|
+
const adapterLabels = ${JSON.stringify(Object.fromEntries(AGENT_ADAPTER_CATALOG.map(({ id, label }) => [id, label])))};
|
|
12
|
+
const componentLabels = ${JSON.stringify(Object.fromEntries(AGENT_EXECUTION_COMPONENT_CATALOG.map(({ id, label }) => [id, label])))};
|
|
13
|
+
export function agentLabel(component, adapterId) {
|
|
14
|
+
return component ? componentLabels[component] || component : adapterLabels[adapterId] || adapterId;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function label(t, prefix, value) {
|
|
18
|
+
if (value === undefined || value === null || value === "") return "—";
|
|
19
|
+
return t(prefix + "." + value, String(value).replace(/[-_]/g, " "));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const TONES = {
|
|
23
|
+
task: { draft: "warn", active: "info", completed: "ok", cancelled: "idle", archived: "idle" },
|
|
24
|
+
exec: {
|
|
25
|
+
"needs-leader-action": "accent", "waiting-on-agents": "info", "waiting-user": "warn",
|
|
26
|
+
recovering: "warn", attention: "bad", "progressing-with-attention": "warn", blocked: "bad",
|
|
27
|
+
conflicted: "warn", working: "info", completed: "ok", retired: "idle", cancelled: "idle",
|
|
28
|
+
accepted: "ok", open: "info", archived: "idle"
|
|
29
|
+
},
|
|
30
|
+
run: { active: "info", completed: "ok", failed: "bad" },
|
|
31
|
+
work: { open: "info", accepted: "ok", retired: "idle" },
|
|
32
|
+
review: { pending: "idle", running: "info", completed: "ok", failed: "bad" },
|
|
33
|
+
job: { queued: "idle", running: "info", succeeded: "ok", failed: "bad", "timed-out": "bad",
|
|
34
|
+
cancelled: "idle", "unknown-needs-attention": "warn" },
|
|
35
|
+
session: { active: "ok", waiting: "warn", quiet: "idle", diagnostic: "bad", unknown: "idle",
|
|
36
|
+
stopped: "bad", idle: "idle", background: "warn" },
|
|
37
|
+
role: { running: "ok", waiting: "warn", idle: "idle", unknown: "idle", failed: "bad", exited: "idle", detached: "idle" },
|
|
38
|
+
decision: { active: "ok", superseded: "idle" },
|
|
39
|
+
input: { open: "warn", answered: "ok", cancelled: "idle", "auto-resolved": "ok" },
|
|
40
|
+
integration: { running: "info", validating: "info", committed: "ok", conflicted: "warn", blocked: "bad",
|
|
41
|
+
failed: "bad", superseded: "idle" }
|
|
42
|
+
};
|
|
43
|
+
export function tone(kind, value) {
|
|
44
|
+
return (TONES[kind] && TONES[kind][value]) || "idle";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function statusBadge(t, kind, prefix, value) {
|
|
48
|
+
const element = badge(label(t, prefix, value), tone(kind, value), { dot: true });
|
|
49
|
+
element.dataset.status = value;
|
|
50
|
+
return element;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Native Session groups in display order, and one badge per non-empty group.
|
|
54
|
+
export const SESSION_GROUPS = ["active", "waiting", "background", "quiet", "diagnostic", "stopped", "unknown", "idle"];
|
|
55
|
+
export function sessionBadges(counts, t) {
|
|
56
|
+
return SESSION_GROUPS.filter(function (group) { return counts && counts[group]; }).map(function (group) {
|
|
57
|
+
return badge(counts[group] + " " + t("session." + group), tone("session", group), { dot: true });
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function agentChips(agent) {
|
|
62
|
+
if (!agent) return null;
|
|
63
|
+
const chips = [];
|
|
64
|
+
if (agent.adapterId || agent.component) chips.push(chip(agentLabel(agent.component, agent.adapterId), "chip-strong"));
|
|
65
|
+
if (agent.model) chips.push(chip(agent.model));
|
|
66
|
+
if (agent.effort) chips.push(chip(agent.effort));
|
|
67
|
+
return chips.length ? h("span.chip-row", null, chips) : null;
|
|
68
|
+
}
|
|
69
|
+
`;
|