@ours.network/fleet 1.0.5 → 1.1.0-nightly.10

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 (159) hide show
  1. package/README.md +122 -133
  2. package/dist/agent-recovery-gate.d.ts +17 -0
  3. package/dist/agent-recovery-gate.js +135 -0
  4. package/dist/application/capabilities.d.ts +0 -1
  5. package/dist/application/capabilities.js +3 -13
  6. package/dist/application/fleet-query-service.d.ts +0 -3
  7. package/dist/application/fleet-query-service.js +14 -25
  8. package/dist/application/role-creation-service.d.ts +5 -24
  9. package/dist/application/role-creation-service.js +27 -67
  10. package/dist/application/role-removal-service.js +6 -3
  11. package/dist/application/role-repository.d.ts +1 -2
  12. package/dist/application/role-repository.js +7 -7
  13. package/dist/application/session-control.d.ts +0 -12
  14. package/dist/application/session-control.js +0 -42
  15. package/dist/application/session-mutations.d.ts +5 -5
  16. package/dist/application/task-room-service.d.ts +1 -1
  17. package/dist/application/task-room-service.js +154 -16
  18. package/dist/application/types.d.ts +1 -8
  19. package/dist/briefing.js +12 -11
  20. package/dist/build-info.json +5 -5
  21. package/dist/cli.js +143 -63
  22. package/dist/config-yaml.d.ts +1 -1
  23. package/dist/config.d.ts +66 -6
  24. package/dist/config.js +398 -43
  25. package/dist/creation.d.ts +1 -1
  26. package/dist/creation.js +2 -2
  27. package/dist/daemon-recovery.d.ts +93 -0
  28. package/dist/daemon-recovery.js +328 -0
  29. package/dist/docs.d.ts +1 -1
  30. package/dist/docs.js +103 -127
  31. package/dist/doctor.js +18 -11
  32. package/dist/fleet-command-audit.d.ts +160 -0
  33. package/dist/fleet-command-audit.js +433 -0
  34. package/dist/fleet-proxy.d.ts +3 -1
  35. package/dist/fleet-proxy.js +30 -8
  36. package/dist/generated-agent-source.d.ts +9 -0
  37. package/dist/generated-agent-source.js +53 -0
  38. package/dist/harness/acp-session-transport.d.ts +8 -0
  39. package/dist/harness/acp-session-transport.js +3 -0
  40. package/dist/harness/agent-session.d.ts +51 -0
  41. package/dist/harness/agent-session.js +1 -0
  42. package/dist/harness/claude-code-session.d.ts +24 -0
  43. package/dist/harness/claude-code-session.js +36 -0
  44. package/dist/harness/claude-code.d.ts +2 -1
  45. package/dist/harness/claude-code.js +41 -81
  46. package/dist/harness/codex-session.d.ts +24 -0
  47. package/dist/harness/codex-session.js +36 -0
  48. package/dist/harness/codex.d.ts +2 -1
  49. package/dist/harness/codex.js +55 -45
  50. package/dist/harness/registry.js +4 -0
  51. package/dist/harness/types.d.ts +6 -38
  52. package/dist/index.d.ts +4 -6
  53. package/dist/index.js +2 -4
  54. package/dist/init-guidance.d.ts +1 -0
  55. package/dist/init-guidance.js +6 -0
  56. package/dist/isolation/resources.d.ts +3 -7
  57. package/dist/isolation/resources.js +3 -7
  58. package/dist/model-env.d.ts +4 -6
  59. package/dist/model-env.js +5 -8
  60. package/dist/monitor.d.ts +1 -24
  61. package/dist/monitor.js +5 -140
  62. package/dist/ops.js +4 -2
  63. package/dist/owner-channel/channel.d.ts +58 -2
  64. package/dist/owner-channel/channel.js +269 -29
  65. package/dist/owner-channel/commands.d.ts +2 -0
  66. package/dist/owner-channel/commands.js +31 -13
  67. package/dist/owner-channel/state.d.ts +2 -0
  68. package/dist/owner-channel/state.js +8 -2
  69. package/dist/preset-bootstrap.d.ts +10 -0
  70. package/dist/preset-bootstrap.js +85 -0
  71. package/dist/reports/artifact.d.ts +10 -0
  72. package/dist/reports/artifact.js +32 -0
  73. package/dist/reports/index.d.ts +5 -0
  74. package/dist/reports/index.js +5 -0
  75. package/dist/reports/render.d.ts +6 -0
  76. package/dist/reports/render.js +240 -0
  77. package/dist/reports/service.d.ts +42 -0
  78. package/dist/reports/service.js +57 -0
  79. package/dist/reports/tasks.d.ts +52 -0
  80. package/dist/reports/tasks.js +177 -0
  81. package/dist/reports/types.d.ts +315 -0
  82. package/dist/reports/types.js +6 -0
  83. package/dist/resolved-plan.d.ts +3 -2
  84. package/dist/resolved-plan.js +21 -5
  85. package/dist/rooms-tasks/cli.js +225 -40
  86. package/dist/rooms-tasks/config.js +24 -13
  87. package/dist/rooms-tasks/external-worker.d.ts +1 -0
  88. package/dist/rooms-tasks/external-worker.js +14 -9
  89. package/dist/rooms-tasks/provision.js +98 -36
  90. package/dist/rooms-tasks/templates.d.ts +0 -1
  91. package/dist/rooms-tasks/templates.js +9 -76
  92. package/dist/rooms-tasks/types.d.ts +10 -16
  93. package/dist/rooms-tasks/types.js +1 -4
  94. package/dist/runner.d.ts +13 -16
  95. package/dist/runner.js +110 -122
  96. package/dist/sensitive-config.d.ts +2 -0
  97. package/dist/sensitive-config.js +4 -0
  98. package/dist/session/acp.d.ts +7 -2
  99. package/dist/session/acp.js +29 -6
  100. package/dist/session/activity.d.ts +2 -3
  101. package/dist/session/activity.js +2 -3
  102. package/dist/session/arbiter.d.ts +4 -4
  103. package/dist/session/control.d.ts +17 -4
  104. package/dist/session/control.js +35 -0
  105. package/dist/session/types.d.ts +11 -5
  106. package/dist/spawn.d.ts +8 -33
  107. package/dist/spawn.js +53 -213
  108. package/dist/supervisor/none.d.ts +1 -1
  109. package/dist/supervisor/none.js +2 -2
  110. package/dist/temp-lifecycle.d.ts +1 -1
  111. package/dist/tmux.d.ts +1 -14
  112. package/dist/tmux.js +1 -51
  113. package/dist/watchdog/config.d.ts +10 -7
  114. package/dist/watchdog/config.js +30 -18
  115. package/dist/watchdog/run.js +12 -36
  116. package/dist/web/auth.d.ts +1 -1
  117. package/dist/web/fleet-config-service.d.ts +11 -8
  118. package/dist/web/fleet-config-service.js +384 -183
  119. package/dist/web/runtime.js +4 -15
  120. package/dist/web/server.d.ts +1 -3
  121. package/dist/web/server.js +3 -14
  122. package/dist/web/topology-promote.js +26 -19
  123. package/dist/web/topology.js +2 -2
  124. package/dist/web/yaml-document-edit.js +2 -0
  125. package/dist/web-app/assets/index-59GF-gsZ.css +1 -0
  126. package/dist/web-app/assets/index-BbTc5KtZ.js +10 -0
  127. package/dist/web-app/index.html +2 -2
  128. package/examples/fleet/agents/Alice.yaml +11 -0
  129. package/examples/fleet/agents/FleetCoordinator.yaml +7 -0
  130. package/examples/fleet/brains/claude-default.yaml +5 -0
  131. package/examples/fleet/roles/coordinator.yaml +5 -0
  132. package/examples/fleet/roles/developer.yaml +3 -0
  133. package/examples/fleet.yaml +74 -0
  134. package/package.json +5 -8
  135. package/presets/fleet/agents/Agent.yaml +3 -0
  136. package/presets/fleet/agents/Architect.yaml +3 -0
  137. package/presets/fleet/agents/Critic.yaml +3 -0
  138. package/presets/fleet/agents/Developer.yaml +3 -0
  139. package/presets/fleet/agents/Secretary.yaml +3 -0
  140. package/presets/fleet/agents/Tester.yaml +3 -0
  141. package/presets/fleet/brains/claude-default.yaml +5 -0
  142. package/presets/fleet/roles/Agent.yaml +6 -0
  143. package/presets/fleet/roles/Architect.yaml +6 -0
  144. package/presets/fleet/roles/Critic.yaml +6 -0
  145. package/presets/fleet/roles/Developer.yaml +6 -0
  146. package/presets/fleet/roles/Secretary.yaml +6 -0
  147. package/presets/fleet/roles/Tester.yaml +6 -0
  148. package/presets/fleet/room_templates/pair.yaml +11 -0
  149. package/presets/fleet/room_templates/single.yaml +8 -0
  150. package/presets/fleet/room_templates/team.yaml +12 -0
  151. package/presets/fleet.yaml +7 -0
  152. package/presets/manifest.json +5 -0
  153. package/dist/session/tmux.d.ts +0 -28
  154. package/dist/session/tmux.js +0 -80
  155. package/dist/web/terminal/bridge.d.ts +0 -27
  156. package/dist/web/terminal/bridge.js +0 -317
  157. package/dist/web-app/assets/TerminalView-C_G1ID2P.js +0 -9
  158. package/dist/web-app/assets/index-BCBK78hw.js +0 -10
  159. package/dist/web-app/assets/index-DuC-xnX4.css +0 -1
@@ -0,0 +1,177 @@
1
+ import { FleetReportService, ReportRegistry } from './service.js';
2
+ import { createHash } from 'node:crypto';
3
+ export const TASK_REPORT_MAX_RECORDS = 200;
4
+ export const TASK_REPORT_MAX_TEXT = 2_000;
5
+ const TASK_REPORT_BRIEF_PREVIEW = 240;
6
+ const STATUS_LABEL = {
7
+ backlog: 'Backlog', provisioning: 'Provisioning', active: 'Active', review: 'Review',
8
+ done: 'Done', cancelled: 'Cancelled', failed: 'Failed',
9
+ };
10
+ const STATUS_TONE = {
11
+ backlog: 'neutral', provisioning: 'warning', active: 'good', review: 'warning',
12
+ done: 'good', cancelled: 'neutral', failed: 'bad',
13
+ };
14
+ const bounded = (value, limit = TASK_REPORT_MAX_TEXT) => {
15
+ if (value === undefined)
16
+ return null;
17
+ const points = [...value];
18
+ return points.length <= limit ? value
19
+ : `${points.slice(0, Math.max(0, limit - 34)).join('')}… [truncated from ${points.length} chars]`;
20
+ };
21
+ const briefPreview = (value) => {
22
+ const first = value?.split(/\r?\n/u).map(line => line.trim()).find(Boolean);
23
+ return bounded(first, TASK_REPORT_BRIEF_PREVIEW) ?? 'No brief supplied.';
24
+ };
25
+ const field = (label, value, tone, multiline = false) => ({
26
+ label, value, ...(tone ? { tone } : {}), ...(multiline ? { multiline: true } : {}),
27
+ });
28
+ const allowlistList = (list) => ({
29
+ list_id: list.list_id, name: bounded(list.name) ?? 'Unnamed list', built_in: list.built_in,
30
+ });
31
+ const allowlistTask = (task) => ({
32
+ task_id: task.task_id, list_id: task.list_id, list_name: bounded(task.list_name) ?? 'default',
33
+ title: bounded(task.title) ?? 'Untitled task', ...(task.brief === undefined ? {} : { brief: bounded(task.brief) ?? '' }),
34
+ state: task.state, ...(task.blocked ? { blocked: { reason: bounded(task.blocked.reason) ?? 'Blocked' } } : {}),
35
+ ...(task.room_id ? { room_id: bounded(task.room_id) ?? undefined } : {}),
36
+ ...(task.template ? { template: { name: bounded(task.template.name) ?? 'Unknown', version: task.template.version } } : {}),
37
+ member_roles: task.member_roles.map(member => ({ name: bounded(member.name) ?? 'Unknown', slot: bounded(member.slot) ?? 'Unknown', cowork_role: bounded(member.cowork_role) ?? 'Unknown' })),
38
+ origin: { type: task.origin.type }, created_at: task.created_at,
39
+ ...(task.started_at ? { started_at: task.started_at } : {}), ...(task.ended_at ? { ended_at: task.ended_at } : {}),
40
+ ...(task.outcome ? { outcome: { summary: bounded(task.outcome.summary) ?? undefined } } : {}),
41
+ });
42
+ function safeTask(task) {
43
+ const status = STATUS_LABEL[task.state];
44
+ const title = bounded(task.title) ?? 'Untitled task';
45
+ const brief = briefPreview(task.brief);
46
+ const groups = [
47
+ { title: '1. Identity and lifecycle', values: [
48
+ field('Task ID', task.task_id), field('Title', title),
49
+ field('Lifecycle status', status, STATUS_TONE[task.state]), field('List', task.list_name),
50
+ field('Blocked indicator', task.blocked ? bounded(task.blocked.reason) : 'No — unblocked', task.blocked ? 'warning' : undefined),
51
+ ] },
52
+ { title: '2. Task content', values: [
53
+ field('Brief', brief, undefined, true),
54
+ field('Detailed description', bounded(task.brief), undefined, true),
55
+ ] },
56
+ { title: '3. Work context', values: [
57
+ field('Room', task.room_id ?? null),
58
+ field('Template', task.template ? `${task.template.name}@${task.template.version}` : null),
59
+ field('Members and roles', task.member_roles.length
60
+ ? bounded(task.member_roles.map(member => `${member.name} — ${member.slot} — ${member.cowork_role}`).join('\n')) : null, undefined, true),
61
+ field('Origin', task.origin.type),
62
+ ] },
63
+ { title: '4. Timeline and outcome', values: [
64
+ field('Created', task.created_at), field('Started', task.started_at ?? null), field('Ended', task.ended_at ?? null),
65
+ field('Terminal outcome', bounded(task.outcome?.summary), undefined, true),
66
+ ] },
67
+ ];
68
+ return {
69
+ id: task.task_id, title, brief, status,
70
+ ...(task.blocked ? { blocked: bounded(task.blocked.reason) ?? 'Blocked' } : {}), updated: task.ended_at ?? task.started_at ?? task.created_at,
71
+ groups,
72
+ };
73
+ }
74
+ function presentTasksReport(input) {
75
+ const maxRecords = input.maxRecords ?? TASK_REPORT_MAX_RECORDS;
76
+ const includedLists = input.selectedList
77
+ ? input.lists.filter(list => list.name === input.selectedList) : input.lists;
78
+ let remaining = maxRecords;
79
+ const panels = includedLists.map(list => {
80
+ const all = input.tasks.filter(task => task.list_id === list.list_id);
81
+ const total = input.totalsByList?.[list.list_id] ?? all.length;
82
+ const shown = all.slice(0, remaining);
83
+ remaining -= shown.length;
84
+ return {
85
+ id: list.list_id, name: list.name, total, shown: shown.length,
86
+ description: shown.length === total ? `All ${total} tasks are included in source order.`
87
+ : `${shown.length} of ${total} tasks are included by the report bound; source order is preserved.`,
88
+ tasks: shown.map(safeTask),
89
+ };
90
+ });
91
+ const defaultList = includedLists.find(list => list.name === input.selectedList)?.list_id
92
+ ?? includedLists.find(list => list.built_in)?.list_id ?? includedLists[0]?.list_id ?? 'default';
93
+ const section = {
94
+ kind: 'task-navigator', id: 'task-lists', title: 'Fleet task lists',
95
+ description: 'One read-only task report shared by task and list commands.',
96
+ lists: panels.map(panel => ({ id: panel.id, name: panel.name, count: panel.total,
97
+ blocked: panel.tasks.filter(task => task.blocked).length })), defaultList, panels,
98
+ };
99
+ return {
100
+ schemaVersion: 1, reportKind: 'tasks', title: 'Fleet task lists',
101
+ description: section.description, generatedAt: input.generatedAt,
102
+ source: input.source ?? { name: 'ours-fleet', version: '1' },
103
+ filters: { ...(input.selectedList ? { list: input.selectedList } : {}), ...(input.state ? { state: input.state } : {}) },
104
+ observedAt: input.observedAt ? { tasks: input.observedAt } : {}, unavailable: [], sections: [section],
105
+ };
106
+ }
107
+ export function buildTasksReport(input) {
108
+ return presentTasksReport({ ...input, lists: input.lists.map(allowlistList), tasks: input.tasks.map(allowlistTask) });
109
+ }
110
+ export async function createTasksReport(input) {
111
+ const request = {
112
+ kind: 'tasks', viewer: input.viewer,
113
+ ...(input.state ? { state: input.state } : {}),
114
+ ...(input.selectedList ? { list: input.selectedList } : {}),
115
+ };
116
+ const registry = new ReportRegistry().register({
117
+ kind: 'tasks', maxRecords: TASK_REPORT_MAX_RECORDS,
118
+ validate: (candidate) => candidate.kind === 'tasks',
119
+ resourceId: () => undefined,
120
+ present: (candidate, snapshot, generatedAt) => presentTasksReport({
121
+ ...snapshot.data, generatedAt, observedAt: snapshot.observedAt.tasks, source: input.source,
122
+ selectedList: candidate.list, state: candidate.state,
123
+ maxRecords: TASK_REPORT_MAX_RECORDS,
124
+ }),
125
+ });
126
+ const provider = {
127
+ surface: input.viewer.surface,
128
+ collect: async (_request, limits) => {
129
+ const data = await input.collect(limits);
130
+ const observedAt = new Date().toISOString();
131
+ const total = data.tasks.length;
132
+ const tasks = data.tasks.slice(0, limits.maxRecords).map(allowlistTask);
133
+ const totalsByList = data.tasks.reduce((totals, task) => {
134
+ totals[task.list_id] = (totals[task.list_id] ?? 0) + 1;
135
+ return totals;
136
+ }, {});
137
+ return { data: { lists: data.lists.map(allowlistList), tasks, totalsByList }, observedAt: { tasks: observedAt }, unavailable: [], stale: [],
138
+ bounds: { shown: tasks.length, total, truncated: total > tasks.length } };
139
+ },
140
+ };
141
+ return new FleetReportService(registry, input.maxBytes)
142
+ .create(request, { provider, generatedAt: input.generatedAt ?? new Date().toISOString() });
143
+ }
144
+ export async function createTaskReport(input) {
145
+ const request = { kind: 'task', viewer: input.viewer, taskId: input.taskId };
146
+ const resourceSlug = input.taskId.toLowerCase().replace(/[^a-z0-9_-]+/gu, '-').replace(/^-+|-+$/gu, '').slice(0, 68) || 'task';
147
+ const resourceName = `${resourceSlug}-${createHash('sha256').update(input.taskId).digest('hex').slice(0, 10)}`;
148
+ const registry = new ReportRegistry().register({
149
+ kind: 'task', maxRecords: 1,
150
+ validate: (candidate) => candidate.kind === 'task'
151
+ && typeof candidate.taskId === 'string' && candidate.taskId.length > 0 && candidate.taskId.length <= 200,
152
+ resourceId: () => resourceName,
153
+ present: (candidate, snapshot, generatedAt) => {
154
+ const task = snapshot.data;
155
+ const model = presentTasksReport({
156
+ lists: [{ list_id: task.list_id, name: task.list_name, built_in: task.list_id === 'default' }],
157
+ tasks: [task], generatedAt, observedAt: snapshot.observedAt.tasks, source: input.source,
158
+ selectedList: task.list_name, maxRecords: 1,
159
+ });
160
+ return { ...model, reportKind: 'task', title: `Task — ${task.title}`,
161
+ description: `Focused read-only task report for ${candidate.taskId}.`, filters: { task: candidate.taskId } };
162
+ },
163
+ });
164
+ const provider = {
165
+ surface: input.viewer.surface,
166
+ collect: async () => {
167
+ const collected = await input.collect();
168
+ if (collected.task_id !== input.taskId)
169
+ throw new Error('authorized task provider returned a mismatched resource');
170
+ const task = allowlistTask(collected);
171
+ return { data: task, observedAt: { tasks: new Date().toISOString() }, unavailable: [], stale: [],
172
+ bounds: { shown: 1, total: 1, truncated: false } };
173
+ },
174
+ };
175
+ return new FleetReportService(registry, input.maxBytes)
176
+ .create(request, { provider, generatedAt: input.generatedAt ?? new Date().toISOString() });
177
+ }
@@ -0,0 +1,315 @@
1
+ export declare const REPORT_KINDS: readonly ["overview", "tasks", "task-lists", "task", "rooms", "room", "room-members", "agents", "agent-status", "templates", "template", "template-validation", "loops", "loop-status", "loop-validation", "watchdog", "watchdog-runs", "version", "config", "doctor", "manual"];
2
+ export type ReportKind = typeof REPORT_KINDS[number];
3
+ export type ReportTone = 'neutral' | 'good' | 'warning' | 'bad' | 'unknown';
4
+ export interface ReportValue {
5
+ label: string;
6
+ value: string | number | boolean | null;
7
+ tone?: ReportTone;
8
+ /** Internal artifact link only; the renderer derives the fragment. */
9
+ target?: {
10
+ section: string;
11
+ id?: string;
12
+ };
13
+ multiline?: boolean;
14
+ }
15
+ export type ReportViewer = {
16
+ surface: 'cli';
17
+ authority: 'local-owner';
18
+ } | {
19
+ surface: 'rest';
20
+ sessionId: string;
21
+ roomCids: string[];
22
+ } | {
23
+ surface: 'messenger';
24
+ authenticatedCid: string;
25
+ roomCids: string[];
26
+ };
27
+ export type ReportRequest = {
28
+ kind: 'overview';
29
+ viewer: ReportViewer;
30
+ } | {
31
+ kind: 'tasks';
32
+ viewer: ReportViewer;
33
+ state?: string;
34
+ list?: string;
35
+ groupByList?: boolean;
36
+ } | {
37
+ kind: 'task-lists';
38
+ viewer: ReportViewer;
39
+ } | {
40
+ kind: 'task';
41
+ viewer: ReportViewer;
42
+ taskId: string;
43
+ } | {
44
+ kind: 'rooms';
45
+ viewer: ReportViewer;
46
+ state?: string;
47
+ } | {
48
+ kind: 'room';
49
+ viewer: ReportViewer;
50
+ roomId: string;
51
+ } | {
52
+ kind: 'room-members';
53
+ viewer: ReportViewer;
54
+ roomId: string;
55
+ } | {
56
+ kind: 'agents';
57
+ viewer: ReportViewer;
58
+ } | {
59
+ kind: 'agent-status';
60
+ viewer: ReportViewer;
61
+ role: string;
62
+ } | {
63
+ kind: 'templates' | 'template-validation';
64
+ viewer: ReportViewer;
65
+ } | {
66
+ kind: 'template';
67
+ viewer: ReportViewer;
68
+ name: string;
69
+ } | {
70
+ kind: 'loops' | 'loop-validation';
71
+ viewer: ReportViewer;
72
+ role?: string;
73
+ } | {
74
+ kind: 'loop-status';
75
+ viewer: ReportViewer;
76
+ role?: string;
77
+ loop?: string;
78
+ } | {
79
+ kind: 'watchdog';
80
+ viewer: ReportViewer;
81
+ watchdog: string;
82
+ runId?: string;
83
+ } | {
84
+ kind: 'watchdog-runs';
85
+ viewer: ReportViewer;
86
+ watchdog: string;
87
+ } | {
88
+ kind: 'version' | 'config' | 'doctor' | 'manual';
89
+ viewer: ReportViewer;
90
+ };
91
+ export interface ReportTable {
92
+ kind: 'table';
93
+ id: string;
94
+ title: string;
95
+ description?: string;
96
+ columns: string[];
97
+ rows: Array<{
98
+ id?: string;
99
+ cells: ReportValue[];
100
+ }>;
101
+ empty?: string;
102
+ truncated?: {
103
+ shown: number;
104
+ total: number;
105
+ };
106
+ }
107
+ export interface ReportDetails {
108
+ kind: 'details';
109
+ id: string;
110
+ title: string;
111
+ description?: string;
112
+ values: ReportValue[];
113
+ paragraphs?: string[];
114
+ }
115
+ export interface ReportNotice {
116
+ kind: 'notice';
117
+ id: string;
118
+ title: string;
119
+ tone: ReportTone;
120
+ text: string;
121
+ }
122
+ export interface ReportCards {
123
+ kind: 'cards';
124
+ id: string;
125
+ title: string;
126
+ description?: string;
127
+ items: Array<{
128
+ id: string;
129
+ title: string;
130
+ subtitle?: string;
131
+ tone?: ReportTone;
132
+ values: ReportValue[];
133
+ paragraphs?: string[];
134
+ open?: boolean;
135
+ }>;
136
+ empty?: string;
137
+ truncated?: {
138
+ shown: number;
139
+ total: number;
140
+ };
141
+ }
142
+ export interface ReportRecords {
143
+ kind: 'records';
144
+ id: string;
145
+ title: string;
146
+ description?: string;
147
+ items: Array<{
148
+ id: string;
149
+ title: string;
150
+ subtitle: string;
151
+ tone?: ReportTone;
152
+ open?: boolean;
153
+ groups: Array<{
154
+ title: string;
155
+ values: ReportValue[];
156
+ }>;
157
+ }>;
158
+ empty?: string;
159
+ truncated?: {
160
+ shown: number;
161
+ total: number;
162
+ };
163
+ }
164
+ export interface ReportListBoard {
165
+ kind: 'list-board';
166
+ id: string;
167
+ title: string;
168
+ description?: string;
169
+ items: Array<{
170
+ id: string;
171
+ name: string;
172
+ builtIn: boolean;
173
+ createdAt: string | null;
174
+ counts: {
175
+ total: number;
176
+ active: number;
177
+ blocked: number;
178
+ terminal: number;
179
+ };
180
+ taskTarget: {
181
+ section: string;
182
+ };
183
+ recent: ReportValue[];
184
+ recentTotal: number;
185
+ }>;
186
+ truncated?: {
187
+ shown: number;
188
+ total: number;
189
+ };
190
+ }
191
+ export interface ReportInbox {
192
+ kind: 'inbox';
193
+ id: string;
194
+ title: string;
195
+ description: string;
196
+ lists: Array<{
197
+ id: string;
198
+ name: string;
199
+ count: number;
200
+ blocked: number;
201
+ selected: boolean;
202
+ }>;
203
+ selected: {
204
+ name: string;
205
+ total: number;
206
+ shown: number;
207
+ };
208
+ attention: Array<{
209
+ id: string;
210
+ title: string;
211
+ brief: string;
212
+ status: string;
213
+ blocker: string;
214
+ updated: string;
215
+ groups: Array<{
216
+ title: string;
217
+ values: ReportValue[];
218
+ }>;
219
+ }>;
220
+ active: Array<{
221
+ id: string;
222
+ title: string;
223
+ brief: string;
224
+ status: string;
225
+ updated: string;
226
+ groups: Array<{
227
+ title: string;
228
+ values: ReportValue[];
229
+ }>;
230
+ }>;
231
+ terminal: Array<{
232
+ id: string;
233
+ title: string;
234
+ brief: string;
235
+ status: string;
236
+ updated: string;
237
+ outcome: string;
238
+ }>;
239
+ }
240
+ export interface ReportTaskNavigator {
241
+ kind: 'task-navigator';
242
+ id: string;
243
+ title: string;
244
+ description: string;
245
+ lists: Array<{
246
+ id: string;
247
+ name: string;
248
+ count: number;
249
+ blocked: number;
250
+ }>;
251
+ defaultList: string;
252
+ panels: Array<{
253
+ id: string;
254
+ name: string;
255
+ total: number;
256
+ shown: number;
257
+ description: string;
258
+ tasks: Array<{
259
+ id: string;
260
+ title: string;
261
+ brief: string;
262
+ status: string;
263
+ blocked?: string;
264
+ updated: string;
265
+ groups: Array<{
266
+ title: string;
267
+ values: ReportValue[];
268
+ }>;
269
+ }>;
270
+ }>;
271
+ }
272
+ export type ReportSection = ReportTable | ReportDetails | ReportNotice | ReportCards | ReportRecords | ReportListBoard | ReportInbox | ReportTaskNavigator;
273
+ export interface ReportViewModel {
274
+ schemaVersion: 1;
275
+ reportKind: ReportKind;
276
+ title: string;
277
+ description?: string;
278
+ generatedAt: string;
279
+ source: {
280
+ name: string;
281
+ version: string;
282
+ buildId?: string;
283
+ };
284
+ filters?: Record<string, string>;
285
+ observedAt?: Record<string, string>;
286
+ summary?: ReportValue[];
287
+ sections: ReportSection[];
288
+ unavailable?: string[];
289
+ }
290
+ export interface ReportArtifactMetadata {
291
+ schemaVersion: 1;
292
+ reportKind: ReportKind;
293
+ filename: string;
294
+ mediaType: 'text/html; charset=utf-8';
295
+ byteSize: number;
296
+ source: {
297
+ name: string;
298
+ version: string;
299
+ buildId?: string;
300
+ };
301
+ generatedAt: string;
302
+ filters: Record<string, string>;
303
+ truncated: boolean;
304
+ unavailable: string[];
305
+ observedAt: Record<string, string>;
306
+ truncation: Array<{
307
+ section: string;
308
+ shown: number;
309
+ total: number;
310
+ }>;
311
+ }
312
+ export interface ReportArtifact {
313
+ metadata: ReportArtifactMetadata;
314
+ html: string;
315
+ }
@@ -0,0 +1,6 @@
1
+ export const REPORT_KINDS = [
2
+ 'overview', 'tasks', 'task-lists', 'task', 'rooms', 'room', 'room-members',
3
+ 'agents', 'agent-status', 'templates', 'template', 'template-validation',
4
+ 'loops', 'loop-status', 'loop-validation', 'watchdog', 'watchdog-runs',
5
+ 'version', 'config', 'doctor', 'manual',
6
+ ];
@@ -1,5 +1,6 @@
1
1
  import type { FleetConfig, ResolvedRole } from './config.js';
2
- export declare const RESOLVED_PLAN_SCHEMA_VERSION = 1;
2
+ export declare const RESOLVED_PLAN_SCHEMA_VERSION = 2;
3
+ export declare const redactSensitive: (value: unknown, key?: string) => unknown;
3
4
  /** Stable, secret-safe machine contract consumed by config JSON and spawn dry-run. */
4
5
  export declare function resolvedPlan(cfg: FleetConfig): Record<string, unknown>;
5
- export declare function resolvedRolePlan(role: ResolvedRole): Record<string, unknown>;
6
+ export declare function resolvedRolePlan(role: ResolvedRole, manifestDir?: string): Record<string, unknown>;
@@ -1,8 +1,10 @@
1
1
  import { analyzeRolePermissions } from './permissions.js';
2
- export const RESOLVED_PLAN_SCHEMA_VERSION = 1;
2
+ import { dirname, relative } from 'node:path';
3
+ import { isSensitiveConfigKey } from './sensitive-config.js';
4
+ export const RESOLVED_PLAN_SCHEMA_VERSION = 2;
3
5
  const sortedObject = (value) => Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)));
4
- const redactSensitive = (value, key = '') => {
5
- if (/(?:secret|token|password|authorization|api[_-]?key)/i.test(key))
6
+ export const redactSensitive = (value, key = '') => {
7
+ if (isSensitiveConfigKey(key))
6
8
  return '<redacted>';
7
9
  if (Array.isArray(value))
8
10
  return value.map(item => redactSensitive(item));
@@ -14,12 +16,17 @@ const redactSensitive = (value, key = '') => {
14
16
  };
15
17
  /** Stable, secret-safe machine contract consumed by config JSON and spawn dry-run. */
16
18
  export function resolvedPlan(cfg) {
19
+ const manifestDir = dirname(cfg.files[0] ?? '.');
17
20
  return {
18
21
  schemaVersion: RESOLVED_PLAN_SCHEMA_VERSION,
22
+ configMode: cfg.configMode ?? 'split-v2',
19
23
  sourceFiles: [...cfg.files],
24
+ sourceDocuments: (cfg.sourceDocuments ?? []).map(document => ({
25
+ ...document, path: relative(manifestDir, document.path) || document.path,
26
+ })),
20
27
  startStaggerMs: cfg.startStaggerMs,
21
28
  diagnostics: cfg.diagnostics.map(diagnostic => ({ ...diagnostic })),
22
- roles: cfg.roles.map(resolvedRolePlan),
29
+ roles: cfg.roles.map(role => resolvedRolePlan(role, manifestDir)),
23
30
  loops: cfg.loops.map(loop => sortedObject({
24
31
  name: loop.name, selectors: [...loop.selectors], roles: [...loop.roleNames],
25
32
  intervalMs: loop.intervalMs, initialDelayMs: loop.initialDelayMs, jitterMs: loop.jitterMs,
@@ -36,16 +43,25 @@ export function resolvedPlan(cfg) {
36
43
  })),
37
44
  };
38
45
  }
39
- export function resolvedRolePlan(role) {
46
+ export function resolvedRolePlan(role, manifestDir = dirname(role.sourceFile)) {
40
47
  const analysis = analyzeRolePermissions(role);
41
48
  return {
42
49
  name: role.name,
43
50
  sourceFile: role.sourceFile,
51
+ references: Object.values(role.provenance ?? {}).flatMap(item => item.viaReference ? [item.viaReference] : [])
52
+ .filter((item, index, all) => all.findIndex(other => JSON.stringify(other) === JSON.stringify(item)) === index)
53
+ .sort((a, b) => a.kind.localeCompare(b.kind) || a.id.localeCompare(b.id)
54
+ || a.sourcePointer.localeCompare(b.sourcePointer)),
55
+ provenance: sortedObject(Object.fromEntries(Object.entries(role.provenance ?? {}).map(([field, item]) => [
56
+ field, { ...item, sourceFile: item.sourceKind === 'built-in'
57
+ ? item.sourceFile : relative(manifestDir, item.sourceFile) || item.sourceFile },
58
+ ]))),
44
59
  identity: role.identity,
45
60
  harness: role.harness,
46
61
  session: role.session,
47
62
  sessionOptions: role.session_options ?? null,
48
63
  model: role.model ?? null,
64
+ effort: role.effort ?? null,
49
65
  modelChain: role.model_chain ?? null,
50
66
  cwd: role.cwd ?? null,
51
67
  coordinator: role.coordinator ?? null,