holo-codex 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/CONTRIBUTING.md +54 -0
  3. package/LICENSE +21 -0
  4. package/README.md +215 -0
  5. package/README.zh-CN.md +215 -0
  6. package/SECURITY.md +39 -0
  7. package/assets/brand/README.md +35 -0
  8. package/assets/brand/holo-codex-icon.svg +28 -0
  9. package/assets/brand/holo-codex-lockup.svg +49 -0
  10. package/assets/brand/holo-codex-mark.svg +33 -0
  11. package/assets/brand/holo-codex-plugin-card.png +0 -0
  12. package/assets/brand/holo-codex-plugin-card.svg +81 -0
  13. package/assets/brand/holo-codex-readme-hero.png +0 -0
  14. package/assets/brand/holo-codex-readme-hero.svg +140 -0
  15. package/assets/brand/holo-codex-social-preview.png +0 -0
  16. package/assets/brand/holo-codex-social-preview.svg +130 -0
  17. package/assets/brand/holo-codex-wordmark-options.svg +52 -0
  18. package/docs/checklists/agent-loop-first-delivery-audit.md +129 -0
  19. package/docs/examples/generic-loop-repo-hygiene.md +168 -0
  20. package/docs/install.md +190 -0
  21. package/docs/local-release-readiness.md +206 -0
  22. package/docs/release-checklist.md +144 -0
  23. package/docs/self-bootstrap.md +150 -0
  24. package/docs/trust-and-safety.md +45 -0
  25. package/package.json +83 -0
  26. package/plugins/autonomous-pr-loop/.codex-plugin/plugin.json +17 -0
  27. package/plugins/autonomous-pr-loop/.mcp.json +13 -0
  28. package/plugins/autonomous-pr-loop/bin/agent-loop.mjs +31 -0
  29. package/plugins/autonomous-pr-loop/core/artifacts.ts +164 -0
  30. package/plugins/autonomous-pr-loop/core/autonomy-policy.ts +206 -0
  31. package/plugins/autonomous-pr-loop/core/ci.ts +131 -0
  32. package/plugins/autonomous-pr-loop/core/cli-i18n.ts +123 -0
  33. package/plugins/autonomous-pr-loop/core/cli.ts +1413 -0
  34. package/plugins/autonomous-pr-loop/core/command-runner.ts +446 -0
  35. package/plugins/autonomous-pr-loop/core/command.ts +47 -0
  36. package/plugins/autonomous-pr-loop/core/config-editor.ts +140 -0
  37. package/plugins/autonomous-pr-loop/core/config.ts +293 -0
  38. package/plugins/autonomous-pr-loop/core/controller-host.ts +19 -0
  39. package/plugins/autonomous-pr-loop/core/dashboard-server.ts +536 -0
  40. package/plugins/autonomous-pr-loop/core/delivery-work-item.ts +217 -0
  41. package/plugins/autonomous-pr-loop/core/doctor.ts +335 -0
  42. package/plugins/autonomous-pr-loop/core/errors.ts +82 -0
  43. package/plugins/autonomous-pr-loop/core/gate-recovery.ts +176 -0
  44. package/plugins/autonomous-pr-loop/core/gates.ts +26 -0
  45. package/plugins/autonomous-pr-loop/core/generic-lifecycle.ts +399 -0
  46. package/plugins/autonomous-pr-loop/core/git.ts +213 -0
  47. package/plugins/autonomous-pr-loop/core/github.ts +269 -0
  48. package/plugins/autonomous-pr-loop/core/gitnexus.ts +90 -0
  49. package/plugins/autonomous-pr-loop/core/happy.ts +42 -0
  50. package/plugins/autonomous-pr-loop/core/hook-capture.ts +115 -0
  51. package/plugins/autonomous-pr-loop/core/hook-events.ts +22 -0
  52. package/plugins/autonomous-pr-loop/core/hook-installation.ts +85 -0
  53. package/plugins/autonomous-pr-loop/core/hook-observer.ts +84 -0
  54. package/plugins/autonomous-pr-loop/core/hook-policy.ts +423 -0
  55. package/plugins/autonomous-pr-loop/core/hook-router.ts +452 -0
  56. package/plugins/autonomous-pr-loop/core/index.ts +32 -0
  57. package/plugins/autonomous-pr-loop/core/local-install.ts +778 -0
  58. package/plugins/autonomous-pr-loop/core/locale.ts +60 -0
  59. package/plugins/autonomous-pr-loop/core/loop-shapes.ts +190 -0
  60. package/plugins/autonomous-pr-loop/core/mcp-controller.ts +1479 -0
  61. package/plugins/autonomous-pr-loop/core/notification-feed.ts +263 -0
  62. package/plugins/autonomous-pr-loop/core/plan-parser.ts +206 -0
  63. package/plugins/autonomous-pr-loop/core/plugin-paths.ts +32 -0
  64. package/plugins/autonomous-pr-loop/core/policy.ts +65 -0
  65. package/plugins/autonomous-pr-loop/core/pr-lifecycle.ts +464 -0
  66. package/plugins/autonomous-pr-loop/core/pr-selector.ts +284 -0
  67. package/plugins/autonomous-pr-loop/core/profiles.ts +439 -0
  68. package/plugins/autonomous-pr-loop/core/redaction.ts +17 -0
  69. package/plugins/autonomous-pr-loop/core/repo-root.ts +22 -0
  70. package/plugins/autonomous-pr-loop/core/review-comments.ts +77 -0
  71. package/plugins/autonomous-pr-loop/core/scope-guard.ts +179 -0
  72. package/plugins/autonomous-pr-loop/core/state-machine.ts +828 -0
  73. package/plugins/autonomous-pr-loop/core/state-types.ts +130 -0
  74. package/plugins/autonomous-pr-loop/core/storage.ts +2527 -0
  75. package/plugins/autonomous-pr-loop/core/types.ts +567 -0
  76. package/plugins/autonomous-pr-loop/core/worker-events.ts +412 -0
  77. package/plugins/autonomous-pr-loop/core/worker-policy.ts +72 -0
  78. package/plugins/autonomous-pr-loop/core/worker-prompts.ts +182 -0
  79. package/plugins/autonomous-pr-loop/core/worker.ts +809 -0
  80. package/plugins/autonomous-pr-loop/core/workflow-board.ts +1515 -0
  81. package/plugins/autonomous-pr-loop/hooks/dist/permission-request.js +2462 -0
  82. package/plugins/autonomous-pr-loop/hooks/dist/post-compact.js +2462 -0
  83. package/plugins/autonomous-pr-loop/hooks/dist/post-tool-use.js +2462 -0
  84. package/plugins/autonomous-pr-loop/hooks/dist/pre-compact.js +2462 -0
  85. package/plugins/autonomous-pr-loop/hooks/dist/pre-tool-use.js +3460 -0
  86. package/plugins/autonomous-pr-loop/hooks/dist/session-start.js +2462 -0
  87. package/plugins/autonomous-pr-loop/hooks/dist/stop.js +2462 -0
  88. package/plugins/autonomous-pr-loop/hooks/dist/user-prompt-submit.js +2462 -0
  89. package/plugins/autonomous-pr-loop/hooks/hooks.json +106 -0
  90. package/plugins/autonomous-pr-loop/hooks/observe-runner.ts +25 -0
  91. package/plugins/autonomous-pr-loop/hooks/permission-request.ts +4 -0
  92. package/plugins/autonomous-pr-loop/hooks/post-compact.ts +4 -0
  93. package/plugins/autonomous-pr-loop/hooks/post-tool-use.ts +4 -0
  94. package/plugins/autonomous-pr-loop/hooks/pre-compact.ts +4 -0
  95. package/plugins/autonomous-pr-loop/hooks/pre-tool-use.ts +44 -0
  96. package/plugins/autonomous-pr-loop/hooks/session-start.ts +4 -0
  97. package/plugins/autonomous-pr-loop/hooks/stop.ts +4 -0
  98. package/plugins/autonomous-pr-loop/hooks/user-prompt-submit.ts +4 -0
  99. package/plugins/autonomous-pr-loop/mcp-server/src/index.ts +87 -0
  100. package/plugins/autonomous-pr-loop/mcp-server/src/tools.ts +205 -0
  101. package/plugins/autonomous-pr-loop/package.json +9 -0
  102. package/plugins/autonomous-pr-loop/schemas/config.schema.json +74 -0
  103. package/plugins/autonomous-pr-loop/schemas/marketplace.schema.json +46 -0
  104. package/plugins/autonomous-pr-loop/schemas/plugin.schema.json +32 -0
  105. package/plugins/autonomous-pr-loop/schemas/state.schema.json +19 -0
  106. package/plugins/autonomous-pr-loop/schemas/worker-event.schema.json +19 -0
  107. package/plugins/autonomous-pr-loop/schemas/worker-result.schema.json +58 -0
  108. package/plugins/autonomous-pr-loop/scripts/agent-loop.ts +44 -0
  109. package/plugins/autonomous-pr-loop/skills/autonomous-pr-loop/SKILL.md +26 -0
  110. package/plugins/autonomous-pr-loop/skills/autonomous-pr-loop/agents/openai.yaml +6 -0
  111. package/plugins/autonomous-pr-loop/ui/index.html +26 -0
  112. package/plugins/autonomous-pr-loop/ui/public/favicon.svg +7 -0
  113. package/plugins/autonomous-pr-loop/ui/src/api.ts +639 -0
  114. package/plugins/autonomous-pr-loop/ui/src/app.tsx +238 -0
  115. package/plugins/autonomous-pr-loop/ui/src/components/ActivityBadge.tsx +31 -0
  116. package/plugins/autonomous-pr-loop/ui/src/components/BrandMark.tsx +36 -0
  117. package/plugins/autonomous-pr-loop/ui/src/components/Collapsible.tsx +6 -0
  118. package/plugins/autonomous-pr-loop/ui/src/components/CommandPreview.tsx +15 -0
  119. package/plugins/autonomous-pr-loop/ui/src/components/ConfigEditor.tsx +389 -0
  120. package/plugins/autonomous-pr-loop/ui/src/components/EmptyState.tsx +10 -0
  121. package/plugins/autonomous-pr-loop/ui/src/components/ErrorState.tsx +12 -0
  122. package/plugins/autonomous-pr-loop/ui/src/components/List.tsx +7 -0
  123. package/plugins/autonomous-pr-loop/ui/src/components/MetricRow.tsx +6 -0
  124. package/plugins/autonomous-pr-loop/ui/src/components/ResponsiveTable.tsx +65 -0
  125. package/plugins/autonomous-pr-loop/ui/src/components/RiskBadge.tsx +10 -0
  126. package/plugins/autonomous-pr-loop/ui/src/components/StatusBadge.tsx +29 -0
  127. package/plugins/autonomous-pr-loop/ui/src/components/TopMetric.tsx +10 -0
  128. package/plugins/autonomous-pr-loop/ui/src/fixtures.ts +1152 -0
  129. package/plugins/autonomous-pr-loop/ui/src/i18n.ts +1105 -0
  130. package/plugins/autonomous-pr-loop/ui/src/main.tsx +14 -0
  131. package/plugins/autonomous-pr-loop/ui/src/pages/CommandCenter.tsx +470 -0
  132. package/plugins/autonomous-pr-loop/ui/src/pages/CommandCenterParts.tsx +276 -0
  133. package/plugins/autonomous-pr-loop/ui/src/pages/agent-timeline/AgentTimelineView.tsx +73 -0
  134. package/plugins/autonomous-pr-loop/ui/src/pages/artifact-viewer/ArtifactViewer.tsx +44 -0
  135. package/plugins/autonomous-pr-loop/ui/src/pages/dry-run-preview/DryRunPreview.tsx +66 -0
  136. package/plugins/autonomous-pr-loop/ui/src/pages/event-ledger/EventLedger.tsx +17 -0
  137. package/plugins/autonomous-pr-loop/ui/src/pages/gate-center/GateCenter.tsx +34 -0
  138. package/plugins/autonomous-pr-loop/ui/src/pages/mission-control/MissionControl.tsx +104 -0
  139. package/plugins/autonomous-pr-loop/ui/src/pages/mission-control/WorkflowBoard.tsx +577 -0
  140. package/plugins/autonomous-pr-loop/ui/src/pages/notifications/NotificationsView.tsx +30 -0
  141. package/plugins/autonomous-pr-loop/ui/src/pages/plan-navigator/PlanNavigator.tsx +19 -0
  142. package/plugins/autonomous-pr-loop/ui/src/pages/policy-config/PolicyConfig.tsx +22 -0
  143. package/plugins/autonomous-pr-loop/ui/src/pages/pr-inbox/PrInbox.tsx +26 -0
  144. package/plugins/autonomous-pr-loop/ui/src/pages/recovery-center/RecoveryCenter.tsx +125 -0
  145. package/plugins/autonomous-pr-loop/ui/src/pages/scope-guard/ScopeGuard.tsx +16 -0
  146. package/plugins/autonomous-pr-loop/ui/src/pages/worker-runs/WorkerRuns.tsx +39 -0
  147. package/plugins/autonomous-pr-loop/ui/src/styles.css +2673 -0
  148. package/plugins/autonomous-pr-loop/ui/src/theme.ts +57 -0
  149. package/tsconfig.json +18 -0
@@ -0,0 +1,26 @@
1
+ import type { JSX } from "react";
2
+ import type { MissionControlData } from "../../api.js";
3
+ import { Collapsible } from "../../components/Collapsible.js";
4
+ import { MetricRow } from "../../components/MetricRow.js";
5
+ import { ResponsiveTable } from "../../components/ResponsiveTable.js";
6
+ import { StatusBadge, toneForStatus } from "../../components/StatusBadge.js";
7
+ import { displayValueLabel, t } from "../../i18n.js";
8
+ import { formatTime, type EffectiveLocale } from "../CommandCenterParts.js";
9
+
10
+ export function PrInbox({ data, locale }: { data: MissionControlData; locale: EffectiveLocale }): JSX.Element {
11
+ return (
12
+ <div className="two-stack">
13
+ <section className="pr-summary">
14
+ <MetricRow label={t(locale, "tablePullRequest")} value={data.pr ? `#${data.pr.prNumber} ${data.pr.state}` : t(locale, "none")} tone={data.pr ? "blue" : "muted"} />
15
+ <MetricRow label={t(locale, "tableDraft")} value={data.pr?.draft ? t(locale, "draft") : data.pr ? t(locale, "ready") : t(locale, "unknown")} tone={data.pr?.draft ? "yellow" : data.pr ? "green" : "muted"} />
16
+ <MetricRow label={t(locale, "tableBranch")} value={data.pr?.branch ?? t(locale, "notLinked")} tone={data.pr ? "blue" : "muted"} />
17
+ </section>
18
+ <Collapsible title={t(locale, "reviewComments")} chip={t(locale, "commentsChip", { count: data.reviewComments.length })} defaultOpen>
19
+ <ResponsiveTable columns={[t(locale, "tableComment"), t(locale, "tableAuthor"), t(locale, "tablePath"), t(locale, "tableStatus")]} rows={data.reviewComments.map((comment) => ({ key: comment.id, cells: [comment.body, comment.author, comment.path, <StatusBadge key={comment.id} value={displayValueLabel(locale, comment.status)} tone={toneForStatus(comment.status)} />], cardTitle: comment.body, cardMeta: comment.path, cardSummary: comment.author }))} empty={t(locale, "noReviewComments")} />
20
+ </Collapsible>
21
+ <Collapsible title={t(locale, "ciChecks")} chip={t(locale, "checksChip", { count: data.ci.length })} defaultOpen>
22
+ <ResponsiveTable columns={[t(locale, "tableCheck"), t(locale, "tableStatus"), t(locale, "tableConclusion"), t(locale, "tableObserved")]} rows={data.ci.map((check) => ({ key: check.id, cells: [check.name, check.status, check.conclusion ?? t(locale, "pending"), formatTime(check.observedAt)], cardTitle: check.name, cardMeta: formatTime(check.observedAt), cardSummary: check.conclusion ?? check.status }))} empty={t(locale, "noCiChecks")} />
23
+ </Collapsible>
24
+ </div>
25
+ );
26
+ }
@@ -0,0 +1,125 @@
1
+ import type { JSX } from "react";
2
+ import { useState } from "react";
3
+ import type { DashboardResult, GateReevaluationData, GateReevaluationResult, MissionControlData } from "../../api.js";
4
+ import { ActivityBadge, activityReasonLabel } from "../../components/ActivityBadge.js";
5
+ import { Collapsible } from "../../components/Collapsible.js";
6
+ import { List } from "../../components/List.js";
7
+ import { MetricRow } from "../../components/MetricRow.js";
8
+ import { t } from "../../i18n.js";
9
+ import { RawMessageDetails, summarizeRawMessage, workerScopeSummary, type EffectiveLocale } from "../CommandCenterParts.js";
10
+
11
+ export function RecoveryCenter({
12
+ data,
13
+ stale,
14
+ onRecover,
15
+ onReevaluateGate,
16
+ onMarkGateHandled,
17
+ locale
18
+ }: {
19
+ data: MissionControlData;
20
+ stale: boolean;
21
+ onRecover: () => void;
22
+ onReevaluateGate: (gateId: string) => Promise<DashboardResult<GateReevaluationData>>;
23
+ onMarkGateHandled: (gateId: string) => void;
24
+ locale: EffectiveLocale;
25
+ }): JSX.Element {
26
+ const run = data.current.run;
27
+ const historicalGates = data.gates.filter((gate) => gate.activity === "historical");
28
+ const staleWorkers = data.workers.filter((worker) => worker.activityReason === "stale_worker_failure");
29
+ const [results, setResults] = useState<Record<string, { result?: GateReevaluationResult; error?: string }>>({});
30
+
31
+ const reEvaluateGate = async (gateId: string): Promise<void> => {
32
+ const response = await onReevaluateGate(gateId);
33
+ const result = response.data?.result;
34
+ setResults((current) => ({
35
+ ...current,
36
+ [gateId]: response.ok && isGateReevaluationResult(result)
37
+ ? { result }
38
+ : { error: response.error?.message ?? t(locale, "reevaluateUnknownError") }
39
+ }));
40
+ };
41
+
42
+ return (
43
+ <div className="two-stack">
44
+ <section className="recovery-panel">
45
+ <MetricRow label={t(locale, "storageRun")} value={run?.id ?? t(locale, "none")} tone={run ? "blue" : "muted"} />
46
+ <MetricRow label={t(locale, "gitBranch")} value={run?.branch ?? t(locale, "unknown")} tone="blue" />
47
+ <MetricRow label={t(locale, "worktree")} value={run?.worktreeClean === false ? t(locale, "dirty") : t(locale, "clean")} tone={run?.worktreeClean === false ? "red" : "green"} />
48
+ <MetricRow label={t(locale, "freshness")} value={stale ? t(locale, "staleData") : t(locale, "fresh")} tone={stale ? "yellow" : "green"} />
49
+ </section>
50
+ <Collapsible title={t(locale, "recoveryWhyStopped")} chip={data.current.gate ? t(locale, "activityActive") : t(locale, "activityHistorical") } defaultOpen>
51
+ <List items={[recoveryExplanation(data, locale)]} locale={locale} />
52
+ </Collapsible>
53
+ {historicalGates.length ? (
54
+ <Collapsible title={t(locale, "historicalGates")} chip={t(locale, "gatesChip", { count: historicalGates.length })} defaultOpen>
55
+ <div className="compact-card-list">
56
+ {historicalGates.map((gate) => (
57
+ <article className="compact-data-card" key={gate.id}>
58
+ <div className="compact-data-card__head">
59
+ <strong>{gate.kind}</strong>
60
+ <ActivityBadge activity={gate.activity} reason={gate.activityReason} locale={locale} />
61
+ </div>
62
+ <p>{summarizeRawMessage(gate.message, locale)}</p>
63
+ <dl>
64
+ <div><dt>{t(locale, "gateId")}</dt><dd>{gate.id}</dd></div>
65
+ <div><dt>{t(locale, "reason")}</dt><dd>{activityReasonLabel(locale, gate.activityReason)}</dd></div>
66
+ <div><dt>{t(locale, "tableRawMessage")}</dt><dd><RawMessageDetails message={gate.message} locale={locale} /></dd></div>
67
+ </dl>
68
+ {results[gate.id] ? (
69
+ <div className={results[gate.id]?.error ? "action-result action-result--error" : "action-result"}>
70
+ <strong>{t(locale, "reevaluateResultTitle")}</strong>
71
+ <span>{results[gate.id]?.error ?? t(locale, `reevaluateResult.${results[gate.id]?.result}`)}</span>
72
+ </div>
73
+ ) : null}
74
+ <div className="button-row">
75
+ <button className="ghost-button" type="button" onClick={() => void reEvaluateGate(gate.id)}>{t(locale, "actionReevaluate")}</button>
76
+ {gate.status === "open" ? (
77
+ <button className="ghost-button" type="button" onClick={() => onMarkGateHandled(gate.id)}>{t(locale, "actionMarkHandled")}</button>
78
+ ) : null}
79
+ </div>
80
+ </article>
81
+ ))}
82
+ </div>
83
+ </Collapsible>
84
+ ) : null}
85
+ {staleWorkers.length ? (
86
+ <Collapsible title={t(locale, "staleWorkerFailures")} chip={workerScopeSummary(data.workers, locale)}>
87
+ <div className="compact-card-list compact-card-list--always">
88
+ {staleWorkers.map((worker) => (
89
+ <article className="compact-data-card" key={worker.id}>
90
+ <div className="compact-data-card__head">
91
+ <strong>{worker.id}</strong>
92
+ <ActivityBadge activity={worker.activity} reason={worker.activityReason} locale={locale} />
93
+ </div>
94
+ <p>{summarizeRawMessage(worker.error, locale) ?? activityReasonLabel(locale, worker.activityReason)}</p>
95
+ {worker.error ? <RawMessageDetails message={worker.error} locale={locale} /> : null}
96
+ </article>
97
+ ))}
98
+ </div>
99
+ </Collapsible>
100
+ ) : null}
101
+ {data.recoveryWarnings?.length ? <Collapsible title={t(locale, "recoveryWarnings")} chip={t(locale, "warningsChip", { count: data.recoveryWarnings.length })} defaultOpen><List items={data.recoveryWarnings} locale={locale} /></Collapsible> : null}
102
+ <button className="ghost-button" type="button" onClick={onRecover}>{t(locale, "actionRunRecovery")}</button>
103
+ </div>
104
+ );
105
+ }
106
+
107
+ function recoveryExplanation(data: MissionControlData, locale: EffectiveLocale): string {
108
+ if (data.current.gate) {
109
+ return t(locale, "recoveryExplanationActive", { gate: data.current.gate.kind });
110
+ }
111
+ if (data.gates.some((gate) => gate.activity === "historical")) {
112
+ return t(locale, "recoveryExplanationHistorical");
113
+ }
114
+ if (data.current.status === "STOPPED") {
115
+ return t(locale, "recoveryExplanationStopped");
116
+ }
117
+ return t(locale, "recoveryExplanationClear");
118
+ }
119
+
120
+ function isGateReevaluationResult(value: unknown): value is GateReevaluationResult {
121
+ return value === "still_historical" ||
122
+ value === "overridden_by_current_reality" ||
123
+ value === "active_again" ||
124
+ value === "manually_handled";
125
+ }
@@ -0,0 +1,16 @@
1
+ import type { JSX } from "react";
2
+ import type { MissionControlData } from "../../api.js";
3
+ import { ResponsiveTable } from "../../components/ResponsiveTable.js";
4
+ import { t } from "../../i18n.js";
5
+ import type { EffectiveLocale } from "../CommandCenterParts.js";
6
+
7
+ export function ScopeGuard({ data, locale }: { data: MissionControlData; locale: EffectiveLocale }): JSX.Element {
8
+ const scopeEvents = data.events.filter((event) => event.kind.includes("scope") || event.kind.includes("gitnexus") || event.kind.includes("policy"));
9
+ return (
10
+ <ResponsiveTable
11
+ columns={[t(locale, "tableSeq"), t(locale, "tableEvent"), t(locale, "tableMessage"), t(locale, "tableState")]}
12
+ rows={scopeEvents.map((event) => ({ key: event.id, cells: [String(event.seq), event.kind, event.message, event.stateAfter ?? "-"], cardTitle: event.kind, cardMeta: `#${event.seq}`, cardSummary: event.message }))}
13
+ empty={t(locale, "noScopeEvidence")}
14
+ />
15
+ );
16
+ }
@@ -0,0 +1,39 @@
1
+ import type { JSX } from "react";
2
+ import type { DashboardApi, WorkerSummary } from "../../api.js";
3
+ import { ActivityBadge, activityReasonLabel } from "../../components/ActivityBadge.js";
4
+ import { ResponsiveTable } from "../../components/ResponsiveTable.js";
5
+ import { StatusBadge, toneForStatus } from "../../components/StatusBadge.js";
6
+ import { displayValueLabel, t } from "../../i18n.js";
7
+ import { RawMessageDetails, WorkerEventDetails, formatTime, summarizeRawMessage, workerScopeSummary, type EffectiveLocale } from "../CommandCenterParts.js";
8
+
9
+ export function WorkerRuns({ workers, api, locale, scopeNote, hideScopeNote = false }: { workers: WorkerSummary[]; api: DashboardApi; locale: EffectiveLocale; scopeNote?: string; hideScopeNote?: boolean }): JSX.Element {
10
+ return (
11
+ <div className="two-stack">
12
+ {hideScopeNote ? null : <p className="scope-note">{scopeNote ?? workerScopeSummary(workers, locale)}</p>}
13
+ <ResponsiveTable
14
+ columns={[t(locale, "tableWorker"), t(locale, "tableActivity"), t(locale, "tableRole"), t(locale, "tableStatus"), t(locale, "tableStarted"), t(locale, "tableRawResultError")]}
15
+ rows={workers.map((worker) => {
16
+ const { error, ...workerForEvents } = worker;
17
+ return {
18
+ key: worker.id,
19
+ cells: [
20
+ worker.id.slice(0, 8),
21
+ <ActivityBadge key={`${worker.id}-activity`} activity={worker.activity} reason={worker.activityReason} locale={locale} />,
22
+ worker.type,
23
+ <StatusBadge key={worker.id} value={displayValueLabel(locale, worker.status)} tone={toneForStatus(worker.status)} />,
24
+ formatTime(worker.startedAt),
25
+ <>
26
+ {error ? <RawMessageDetails message={error} locale={locale} /> : null}
27
+ <WorkerEventDetails worker={workerForEvents} api={api} locale={locale} />
28
+ </>
29
+ ],
30
+ cardTitle: `${worker.type} / ${worker.status}`,
31
+ cardMeta: `${worker.id} / ${activityReasonLabel(locale, worker.activityReason)} / ${formatTime(worker.startedAt)}`,
32
+ cardSummary: summarizeRawMessage(error, locale) ?? worker.resultArtifactId ?? t(locale, "workerEvents")
33
+ };
34
+ })}
35
+ empty={t(locale, "noWorkerRuns")}
36
+ />
37
+ </div>
38
+ );
39
+ }