@undefineds.co/linx 0.3.19 → 0.3.22
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/dist/generated/version.js +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/auto-mode/pod-persistence.js +51 -3
- package/dist/lib/auto-mode/pod-persistence.js.map +1 -1
- package/dist/lib/auto-mode/secretary.js +2 -2
- package/dist/lib/auto-mode/secretary.js.map +1 -1
- package/dist/lib/chat-api.js +23 -61
- package/dist/lib/chat-api.js.map +1 -1
- package/dist/lib/codex-plugin/index.js +1 -0
- package/dist/lib/codex-plugin/index.js.map +1 -1
- package/dist/lib/codex-plugin/symphony-mcp.js +335 -0
- package/dist/lib/codex-plugin/symphony-mcp.js.map +1 -0
- package/dist/lib/models.js +3 -2
- package/dist/lib/models.js.map +1 -1
- package/dist/lib/pi-adapter/auth.js +68 -0
- package/dist/lib/pi-adapter/auth.js.map +1 -0
- package/dist/lib/pi-adapter/interactive.js +12 -17
- package/dist/lib/pi-adapter/interactive.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror.js +34 -5
- package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
- package/dist/lib/pi-adapter/pod-tools.js +140 -0
- package/dist/lib/pi-adapter/pod-tools.js.map +1 -0
- package/dist/lib/pi-adapter/session.js +13 -17
- package/dist/lib/pi-adapter/session.js.map +1 -1
- package/dist/lib/pi-adapter/stream.js +2 -20
- package/dist/lib/pi-adapter/stream.js.map +1 -1
- package/dist/lib/pod-chat-store.js +53 -4
- package/dist/lib/pod-chat-store.js.map +1 -1
- package/dist/lib/resource-identity.js +2 -0
- package/dist/lib/resource-identity.js.map +1 -0
- package/dist/lib/symphony/archive.js +15 -37
- package/dist/lib/symphony/archive.js.map +1 -1
- package/dist/lib/symphony/pod-projection.js +188 -1347
- package/dist/lib/symphony/pod-projection.js.map +1 -1
- package/dist/lib/symphony-command.js +208 -108
- package/dist/lib/symphony-command.js.map +1 -1
- package/dist/plugins/linx-symphony-codex/.codex-plugin/plugin.json +38 -0
- package/dist/plugins/linx-symphony-codex/.mcp.json +10 -0
- package/dist/plugins/linx-symphony-codex/README.md +9 -0
- package/dist/plugins/linx-symphony-codex/hooks.json +60 -0
- package/dist/plugins/linx-symphony-codex/scripts/symphony-hook-events.mjs +119 -0
- package/dist/plugins/linx-symphony-codex/scripts/symphony-mcp.mjs +335 -0
- package/dist/plugins/linx-symphony-codex/skills/symphony/SKILL.md +791 -0
- package/dist/skills/symphony/SKILL.md +15 -4
- package/package.json +4 -4
- package/vendor/agent-runtime/dist/chat-reconciler.d.ts +33 -0
- package/vendor/agent-runtime/dist/chat-reconciler.js +108 -0
- package/vendor/agent-runtime/dist/index.d.ts +4 -0
- package/vendor/agent-runtime/dist/index.js +4 -0
- package/vendor/agent-runtime/dist/matrix-client.d.ts +149 -0
- package/vendor/agent-runtime/dist/matrix-client.js +220 -0
- package/vendor/agent-runtime/dist/pod-resource-identity.d.ts +17 -0
- package/vendor/agent-runtime/dist/pod-resource-identity.js +54 -0
- package/vendor/agent-runtime/dist/reconciler.js +2 -2
- package/vendor/agent-runtime/dist/symphony.d.ts +273 -28
- package/vendor/agent-runtime/dist/symphony.js +1267 -20
- package/vendor/agent-runtime/dist/workspace.d.ts +61 -0
- package/vendor/agent-runtime/dist/workspace.js +81 -0
- package/vendor/agent-runtime/package.json +5 -1
- package/vendor/stores/dist/current-pod-base.d.ts +2 -0
- package/vendor/stores/dist/current-pod-base.js +14 -0
- package/vendor/stores/dist/exact-records.d.ts +7 -0
- package/vendor/stores/dist/exact-records.js +87 -0
- package/vendor/stores/dist/index.d.ts +1 -0
- package/vendor/stores/dist/index.js +1 -0
- package/vendor/stores/dist/login.d.ts +51 -0
- package/vendor/stores/dist/login.js +195 -0
- package/vendor/stores/dist/pod-collection.d.ts +28 -0
- package/vendor/stores/dist/pod-collection.js +194 -0
- package/vendor/stores/dist/pod-write-guard.d.ts +5 -0
- package/vendor/stores/dist/pod-write-guard.js +133 -0
- package/vendor/stores/dist/symphony-control.d.ts +245 -0
- package/vendor/stores/dist/symphony-control.js +2175 -0
- package/vendor/stores/package.json +14 -0
|
@@ -5,10 +5,10 @@ import { getSymphonyArchiveKey } from '../../../vendor/agent-runtime/dist/sympho
|
|
|
5
5
|
import { DEFAULT_AGENT_RUNTIME_COMPANION_MODEL_ID } from '../../../vendor/agent-runtime/dist/companion-model.js';
|
|
6
6
|
import { decideThreadControlEvent } from '../../../vendor/agent-runtime/dist/thread-reconciler-controller.js';
|
|
7
7
|
import { createLinxPodSyncScope } from '../../../vendor/agent-runtime/dist/sync.js';
|
|
8
|
-
import {
|
|
8
|
+
import { buildSymphonyChatRow as buildSharedSymphonyChatRow, buildSymphonyContactRows as buildSharedSymphonyContactRows, buildSymphonyControlRows, buildSymphonyDeliveryRow as buildSharedSymphonyDeliveryRow, buildSymphonyIssueRow as buildSharedSymphonyIssueRow, buildSymphonyMessageIri as buildSharedSymphonyMessageIri, buildSymphonyRunRow as buildSharedSymphonyRunRow, buildSymphonyRunStepRow as buildSharedSymphonyRunStepRow, buildSymphonyRuntimeRunStepRow as buildSharedSymphonyRuntimeRunStepRow, buildSymphonySessionRow as buildSharedSymphonySessionRow, buildSymphonyStatusMessageRow as buildSharedSymphonyStatusMessageRow, buildSymphonyTaskRow as buildSharedSymphonyTaskRow, buildSymphonyThreadRow as buildSharedSymphonyThreadRow, listOpenSymphonyIssuesFromControlState, listRecentSymphonyReportsFromControlState, listRunningSymphonyWorkersFromControlState, } from '../../../vendor/stores/dist/symphony-control.js';
|
|
9
|
+
import { insertExactRecordOnce, upsertExactRecord, } from '@undefineds.co/drizzle-solid';
|
|
9
10
|
import { getDefaultPodDataSession } from '../pod-data-session.js';
|
|
10
|
-
import {
|
|
11
|
-
import { pathToWorkspaceUri } from '../pi-adapter/pod-mirror-mapping.js';
|
|
11
|
+
import { chatRepository, agentResource, contactResource, deliveryResource, ideaResource, issueResource, messageResource, runResource, runStepResource, sessionResource, taskResource, threadRepository, } from '../models.js';
|
|
12
12
|
import { getSymphonyHome } from './archive.js';
|
|
13
13
|
const SYMPHONY_CHAT_ID = 'symphony';
|
|
14
14
|
const SYMPHONY_SECRETARY_AGENT_ID = '__secretary__';
|
|
@@ -16,240 +16,6 @@ const SYMPHONY_CONTACT_ID = 'symphony';
|
|
|
16
16
|
const SYMPHONY_POLICY_VERSION = 'linx-symphony-session/v1';
|
|
17
17
|
const SYMPHONY_WORKER_POD_ACCESS_POLICY_VERSION = 'linx-symphony-worker-pod-access/v1';
|
|
18
18
|
const SYMPHONY_ARCHIVE_PROVENANCE_VERSION = 'linx-symphony-archive/v1';
|
|
19
|
-
function ensureTrailingSlash(value) {
|
|
20
|
-
return value.endsWith('/') ? value : `${value}/`;
|
|
21
|
-
}
|
|
22
|
-
function podBaseUrlFromWebId(webId) {
|
|
23
|
-
const marker = '/profile/card#me';
|
|
24
|
-
if (webId.includes(marker)) {
|
|
25
|
-
return `${webId.slice(0, webId.indexOf(marker) + 1)}`;
|
|
26
|
-
}
|
|
27
|
-
const url = new URL(webId);
|
|
28
|
-
return `${url.origin}/`;
|
|
29
|
-
}
|
|
30
|
-
function podFileUrlFromWebId(webId, path) {
|
|
31
|
-
return new URL(path.replace(/^\/+/, ''), podBaseUrlFromWebId(webId)).toString();
|
|
32
|
-
}
|
|
33
|
-
function podFileUrl(podSession, path) {
|
|
34
|
-
return new URL(path.replace(/^\/+/, ''), ensureTrailingSlash(podSession.podUrl)).toString();
|
|
35
|
-
}
|
|
36
|
-
async function writePodFileToSession(session, file) {
|
|
37
|
-
const url = podFileUrl(session, file.path);
|
|
38
|
-
await ensurePodResourceContainers(session.fetch, url);
|
|
39
|
-
const response = await session.fetch(url, {
|
|
40
|
-
method: 'PUT',
|
|
41
|
-
headers: { 'Content-Type': file.contentType },
|
|
42
|
-
body: file.content.endsWith('\n') ? file.content : `${file.content}\n`,
|
|
43
|
-
});
|
|
44
|
-
if (!response.ok) {
|
|
45
|
-
const details = await response.text().catch(() => '');
|
|
46
|
-
const suffix = details.trim() ? ` - ${details.trim().slice(0, 500)}` : '';
|
|
47
|
-
throw new Error(`Failed to write Symphony Pod file ${url}: ${response.status} ${response.statusText}${suffix}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
async function ensurePodResourceContainers(fetcher, resourceUrl) {
|
|
51
|
-
for (const containerUrl of containerUrlsForResource(resourceUrl)) {
|
|
52
|
-
const existing = await fetcher(containerUrl, { method: 'HEAD' }).catch(() => null);
|
|
53
|
-
if (existing?.ok)
|
|
54
|
-
continue;
|
|
55
|
-
if (existing && existing.status !== 404 && existing.status !== 405)
|
|
56
|
-
continue;
|
|
57
|
-
const response = await fetcher(containerUrl, {
|
|
58
|
-
method: 'PUT',
|
|
59
|
-
headers: {
|
|
60
|
-
Link: '<http://www.w3.org/ns/ldp#BasicContainer>; rel="type"',
|
|
61
|
-
'Content-Type': 'text/turtle; charset=utf-8',
|
|
62
|
-
},
|
|
63
|
-
body: '',
|
|
64
|
-
});
|
|
65
|
-
if (!response.ok && response.status !== 409) {
|
|
66
|
-
throw new Error(`Failed to ensure Symphony Pod container ${containerUrl}: ${response.status} ${response.statusText}`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function containerUrlsForResource(resourceUrl) {
|
|
71
|
-
const url = new URL(resourceUrl);
|
|
72
|
-
const parts = url.pathname.split('/').filter(Boolean);
|
|
73
|
-
const containers = [];
|
|
74
|
-
let path = '/';
|
|
75
|
-
for (let index = 0; index < parts.length - 1; index += 1) {
|
|
76
|
-
path += `${parts[index]}/`;
|
|
77
|
-
containers.push(new URL(path, url.origin).toString());
|
|
78
|
-
}
|
|
79
|
-
return containers;
|
|
80
|
-
}
|
|
81
|
-
function datePathParts(input) {
|
|
82
|
-
const date = safeDate(input);
|
|
83
|
-
return {
|
|
84
|
-
yyyy: String(date.getUTCFullYear()),
|
|
85
|
-
MM: String(date.getUTCMonth() + 1).padStart(2, '0'),
|
|
86
|
-
dd: String(date.getUTCDate()).padStart(2, '0'),
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
function buildSymphonyIssueDocumentPath(issue) {
|
|
90
|
-
return `/.data/issues/${buildSymphonyIssueId(issue)}/issue.md`;
|
|
91
|
-
}
|
|
92
|
-
function buildSymphonyIdeaDocumentPath(idea) {
|
|
93
|
-
const { yyyy, MM, dd } = datePathParts(idea.createdAt);
|
|
94
|
-
return `/.data/ideas/${yyyy}/${MM}/${dd}/${getSymphonyArchiveKey(idea.uri)}/idea.md`;
|
|
95
|
-
}
|
|
96
|
-
function buildSymphonyReportDocumentPath(worker) {
|
|
97
|
-
const { yyyy, MM, dd } = datePathParts(worker.session.completedAt ?? worker.session.updatedAt);
|
|
98
|
-
return `/.data/reports/${yyyy}/${MM}/${dd}/${getSymphonyArchiveKey(worker.session.uri)}-report.md`;
|
|
99
|
-
}
|
|
100
|
-
function buildSymphonyEvidenceDocumentPath(worker, stage) {
|
|
101
|
-
const { yyyy, MM, dd } = datePathParts(worker.session.completedAt ?? worker.session.updatedAt);
|
|
102
|
-
return `/.data/evidence/${yyyy}/${MM}/${dd}/${getSymphonyArchiveKey(worker.session.uri)}-${stage}-evidence.md`;
|
|
103
|
-
}
|
|
104
|
-
function renderMarkdownList(items) {
|
|
105
|
-
const values = (items ?? []).map((item) => item.trim()).filter(Boolean);
|
|
106
|
-
return values.length > 0 ? values.map((item) => `- ${item}`).join('\n') : '- None recorded.';
|
|
107
|
-
}
|
|
108
|
-
function renderSymphonyIssueMarkdown(plan) {
|
|
109
|
-
const issue = plan.issue;
|
|
110
|
-
const sections = [
|
|
111
|
-
`# ${issue.title || buildSymphonyIssueId(issue)}`,
|
|
112
|
-
'',
|
|
113
|
-
'## Summary',
|
|
114
|
-
issue.description?.trim() || issue.title || 'No summary recorded.',
|
|
115
|
-
'',
|
|
116
|
-
'## Status',
|
|
117
|
-
`- Status: ${issue.status}`,
|
|
118
|
-
`- Priority: ${issue.priority}`,
|
|
119
|
-
`- Source: ${issue.source}`,
|
|
120
|
-
'',
|
|
121
|
-
'## Acceptance Criteria',
|
|
122
|
-
renderMarkdownList(plan.workers.flatMap((worker) => worker.taskRecord.acceptanceCriteria ?? [])),
|
|
123
|
-
'',
|
|
124
|
-
'## Tasks',
|
|
125
|
-
renderMarkdownList(plan.workers.map((worker) => `${worker.taskRecord.title}: ${worker.taskRecord.objective}`)),
|
|
126
|
-
'',
|
|
127
|
-
'## Source Context',
|
|
128
|
-
`- Chat: ${issue.chat ?? plan.session.chat ?? 'not recorded'}`,
|
|
129
|
-
`- Thread: ${issue.thread ?? plan.session.thread ?? 'not recorded'}`,
|
|
130
|
-
`- Messages: ${(issue.messages ?? []).join(', ') || 'not recorded'}`,
|
|
131
|
-
'',
|
|
132
|
-
'## Control Records',
|
|
133
|
-
`- Issue: ${issue.uri}`,
|
|
134
|
-
...plan.workers.flatMap((worker) => [
|
|
135
|
-
`- Task: ${worker.task}`,
|
|
136
|
-
`- Delivery: ${worker.delivery.uri}`,
|
|
137
|
-
`- Session: ${worker.session.uri}`,
|
|
138
|
-
]),
|
|
139
|
-
];
|
|
140
|
-
return sections.join('\n');
|
|
141
|
-
}
|
|
142
|
-
function renderSymphonyIdeaMarkdown(idea) {
|
|
143
|
-
return [
|
|
144
|
-
`# ${idea.summary || getSymphonyArchiveKey(idea.uri)}`,
|
|
145
|
-
'',
|
|
146
|
-
'## Input',
|
|
147
|
-
idea.input?.trim() || idea.summary || 'No input recorded.',
|
|
148
|
-
'',
|
|
149
|
-
'## Current Understanding',
|
|
150
|
-
idea.currentUnderstanding?.trim() || 'No current understanding recorded.',
|
|
151
|
-
'',
|
|
152
|
-
'## Open Questions',
|
|
153
|
-
renderMarkdownList(idea.openQuestions),
|
|
154
|
-
'',
|
|
155
|
-
'## Conflicts',
|
|
156
|
-
renderMarkdownList(idea.conflicts),
|
|
157
|
-
'',
|
|
158
|
-
'## Next Step',
|
|
159
|
-
idea.nextStep?.trim() || 'No next step recorded.',
|
|
160
|
-
'',
|
|
161
|
-
'## Source Context',
|
|
162
|
-
`- Status: ${idea.status}`,
|
|
163
|
-
`- Commitment: ${idea.commitment}`,
|
|
164
|
-
`- Chat: ${idea.chat ?? 'not recorded'}`,
|
|
165
|
-
`- Thread: ${idea.thread ?? 'not recorded'}`,
|
|
166
|
-
`- Messages: ${(idea.messages ?? []).join(', ') || 'not recorded'}`,
|
|
167
|
-
`- Idea: ${idea.uri}`,
|
|
168
|
-
].join('\n');
|
|
169
|
-
}
|
|
170
|
-
function renderSymphonyReportMarkdown(plan, worker, stage) {
|
|
171
|
-
const status = worker.session.status === 'failed' || stage === 'failed' ? 'failed' : 'completed';
|
|
172
|
-
const summary = status === 'completed'
|
|
173
|
-
? `${worker.taskRecord.title} completed.`
|
|
174
|
-
: `${worker.taskRecord.title} failed: ${worker.session.error ?? worker.delivery.error ?? 'worker did not complete successfully.'}`;
|
|
175
|
-
return [
|
|
176
|
-
`# ${worker.taskRecord.title} — ${status}`,
|
|
177
|
-
'',
|
|
178
|
-
'## Summary',
|
|
179
|
-
summary,
|
|
180
|
-
'',
|
|
181
|
-
'## Outcome',
|
|
182
|
-
`- Status: ${status}`,
|
|
183
|
-
`- Backend: ${worker.session.backend}`,
|
|
184
|
-
`- Agent: ${worker.session.target.agent ?? worker.delivery.targetAgent}`,
|
|
185
|
-
`- Auto mode session: ${worker.session.autoModeSessionId ?? 'not recorded'}`,
|
|
186
|
-
`- Exit code: ${worker.session.exitCode ?? 'not recorded'}`,
|
|
187
|
-
'',
|
|
188
|
-
'## Task',
|
|
189
|
-
worker.taskRecord.objective,
|
|
190
|
-
'',
|
|
191
|
-
'## Acceptance Criteria',
|
|
192
|
-
renderMarkdownList(worker.taskRecord.acceptanceCriteria),
|
|
193
|
-
'',
|
|
194
|
-
'## Linked Control Records',
|
|
195
|
-
`- Issue: ${buildSymphonyIssueId(plan.issue)}`,
|
|
196
|
-
`- Task: ${worker.task}`,
|
|
197
|
-
`- Delivery: ${worker.delivery.uri}`,
|
|
198
|
-
`- Session: ${worker.session.uri}`,
|
|
199
|
-
`- Run status: ${worker.session.status}`,
|
|
200
|
-
'',
|
|
201
|
-
'## Post-Run Reconciliation',
|
|
202
|
-
'- Secretary must review this report and linked Evidence before closing the task.',
|
|
203
|
-
'- Classify any follow-up as same_issue_task, new_issue, idea, evidence_only, or ask_user.',
|
|
204
|
-
'',
|
|
205
|
-
...(worker.session.error || worker.delivery.error || worker.taskRecord.error ? [
|
|
206
|
-
'## Error',
|
|
207
|
-
worker.session.error ?? worker.delivery.error ?? worker.taskRecord.error ?? '',
|
|
208
|
-
'',
|
|
209
|
-
] : []),
|
|
210
|
-
].join('\n');
|
|
211
|
-
}
|
|
212
|
-
function renderSymphonyEvidenceMarkdown(plan, webId, worker, stage) {
|
|
213
|
-
const status = worker.session.status === 'failed' || stage === 'failed' ? 'failed' : 'completed';
|
|
214
|
-
const summary = status === 'completed'
|
|
215
|
-
? `${worker.taskRecord.title} completed with runtime status ${worker.session.status}.`
|
|
216
|
-
: `${worker.taskRecord.title} failed: ${worker.session.error ?? worker.delivery.error ?? worker.taskRecord.error ?? 'worker did not complete successfully.'}`;
|
|
217
|
-
const run = buildSymphonyRunIri(webId, worker);
|
|
218
|
-
const runStep = buildSymphonyRunStepIri(webId, worker, stage);
|
|
219
|
-
return [
|
|
220
|
-
`# ${worker.taskRecord.title} — ${status} evidence`,
|
|
221
|
-
'',
|
|
222
|
-
'## Summary',
|
|
223
|
-
summary,
|
|
224
|
-
'',
|
|
225
|
-
'## Runtime Facts',
|
|
226
|
-
`- Backend: ${worker.session.backend}`,
|
|
227
|
-
`- Agent: ${worker.session.target.agent ?? worker.delivery.targetAgent}`,
|
|
228
|
-
`- Model: ${worker.session.model ?? 'not recorded'}`,
|
|
229
|
-
`- Auto mode session: ${worker.session.autoModeSessionId ?? 'not recorded'}`,
|
|
230
|
-
`- Exit code: ${worker.session.exitCode ?? 'not recorded'}`,
|
|
231
|
-
`- Run status: ${worker.session.status}`,
|
|
232
|
-
'',
|
|
233
|
-
'## Acceptance Criteria',
|
|
234
|
-
renderMarkdownList(worker.taskRecord.acceptanceCriteria),
|
|
235
|
-
'',
|
|
236
|
-
'## Linked Control Records',
|
|
237
|
-
`- Issue: ${buildSymphonyIssueIri(webId, plan.issue)}`,
|
|
238
|
-
`- Task: ${buildSymphonyTaskIri(webId, worker.task)}`,
|
|
239
|
-
`- Delivery: ${buildSymphonyDeliveryIri(webId, worker)}`,
|
|
240
|
-
`- Run: ${run}`,
|
|
241
|
-
`- Source RunStep: ${runStep}`,
|
|
242
|
-
`- Worker Thread: ${selectWorkerThreadIri(plan, webId, worker)}`,
|
|
243
|
-
'',
|
|
244
|
-
...(worker.session.error || worker.delivery.error || worker.taskRecord.error ? [
|
|
245
|
-
'## Error',
|
|
246
|
-
worker.session.error ?? worker.delivery.error ?? worker.taskRecord.error ?? '',
|
|
247
|
-
'',
|
|
248
|
-
] : []),
|
|
249
|
-
'## Secretary Follow-Up Review',
|
|
250
|
-
'This Evidence is append-only proof/finding material. Secretary must use it with the Report and RunSteps to decide acceptance and whether follow-up work should be captured.',
|
|
251
|
-
].join('\n');
|
|
252
|
-
}
|
|
253
19
|
function normalizeSymphonyRunPlan(plan) {
|
|
254
20
|
const workers = Array.isArray(plan.workers) && plan.workers.length > 0
|
|
255
21
|
? plan.workers
|
|
@@ -326,6 +92,7 @@ function normalizeSymphonyRunPlan(plan) {
|
|
|
326
92
|
delivery: primary.delivery,
|
|
327
93
|
session: primary.session,
|
|
328
94
|
workers: normalizedWorkers,
|
|
95
|
+
...(plan.followUpIssues?.length ? { followUpIssues: plan.followUpIssues } : {}),
|
|
329
96
|
};
|
|
330
97
|
}
|
|
331
98
|
async function dynamicImport(specifier) {
|
|
@@ -353,15 +120,14 @@ async function createDefaultRuntime() {
|
|
|
353
120
|
issueResource: models.issueResource,
|
|
354
121
|
taskResource: models.taskResource,
|
|
355
122
|
deliveryResource: models.deliveryResource,
|
|
356
|
-
evidenceResource: models.evidenceResource,
|
|
357
|
-
reportResource: models.reportResource,
|
|
358
123
|
runResource: models.runResource,
|
|
359
124
|
runStepResource: models.runStepResource,
|
|
125
|
+
evidenceResource: models.evidenceResource,
|
|
126
|
+
reportResource: models.reportResource,
|
|
360
127
|
agentResource: models.agentResource,
|
|
361
128
|
contactResource: models.contactResource,
|
|
362
129
|
auditResource: models.auditResource,
|
|
363
130
|
inboxNotificationResource: models.inboxNotificationResource,
|
|
364
|
-
writePodFile: writePodFileToSession,
|
|
365
131
|
};
|
|
366
132
|
}
|
|
367
133
|
function selectTargetChatIri(value, webId, plan) {
|
|
@@ -422,6 +188,24 @@ function buildSymphonyIssueId(issue) {
|
|
|
422
188
|
function buildSymphonyIssueIri(webId, issue) {
|
|
423
189
|
return issueResource.buildIri(webId, { id: buildSymphonyIssueId(issue) });
|
|
424
190
|
}
|
|
191
|
+
function normalizeSymphonyIssueIri(webId, issue) {
|
|
192
|
+
if (/^https?:\/\//u.test(issue)) {
|
|
193
|
+
return issue;
|
|
194
|
+
}
|
|
195
|
+
return buildSymphonyIssueIri(webId, {
|
|
196
|
+
uri: issue,
|
|
197
|
+
title: '',
|
|
198
|
+
status: 'open',
|
|
199
|
+
priority: 'medium',
|
|
200
|
+
source: 'cli',
|
|
201
|
+
issuer: { source: 'system' },
|
|
202
|
+
tasks: [],
|
|
203
|
+
deliveries: [],
|
|
204
|
+
sessions: [],
|
|
205
|
+
createdAt: new Date().toISOString(),
|
|
206
|
+
updatedAt: new Date().toISOString(),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
425
209
|
function buildSymphonyTaskKey(task) {
|
|
426
210
|
return getSymphonyArchiveKey(task);
|
|
427
211
|
}
|
|
@@ -448,19 +232,6 @@ function buildSymphonyReportDeliveryIri(webId, worker) {
|
|
|
448
232
|
createdAt: safeDate(worker.session.completedAt ?? worker.session.updatedAt),
|
|
449
233
|
});
|
|
450
234
|
}
|
|
451
|
-
function buildSymphonyReportIri(webId, worker) {
|
|
452
|
-
return reportResource.buildIri(webId, {
|
|
453
|
-
id: `${getSymphonyArchiveKey(worker.session.uri)}-report`,
|
|
454
|
-
task: buildSymphonyTaskIri(webId, worker.task),
|
|
455
|
-
createdAt: safeDate(worker.session.completedAt ?? worker.session.updatedAt),
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
function buildSymphonyEvidenceIri(webId, worker, stage) {
|
|
459
|
-
return evidenceResource.buildIri(webId, {
|
|
460
|
-
id: `${getSymphonyArchiveKey(worker.session.uri)}-${stage}`,
|
|
461
|
-
createdAt: safeDate(worker.session.completedAt ?? worker.session.updatedAt),
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
235
|
function buildSymphonyRunIri(webId, worker) {
|
|
465
236
|
return runResource.buildIri(webId, {
|
|
466
237
|
id: getSymphonyArchiveKey(worker.session.uri),
|
|
@@ -474,6 +245,12 @@ function buildSymphonyRunStepIri(webId, worker, stage) {
|
|
|
474
245
|
run: buildSymphonyRunIri(webId, worker),
|
|
475
246
|
});
|
|
476
247
|
}
|
|
248
|
+
function buildSymphonyRuntimeRunStepIri(webId, worker, step) {
|
|
249
|
+
return runStepResource.buildIri(webId, {
|
|
250
|
+
id: getSymphonyArchiveKey(step.uri),
|
|
251
|
+
run: buildSymphonyRunIri(webId, worker),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
477
254
|
function buildSymphonyWorkerPodAccessPolicy(plan, webId, worker) {
|
|
478
255
|
return {
|
|
479
256
|
version: SYMPHONY_WORKER_POD_ACCESS_POLICY_VERSION,
|
|
@@ -592,11 +369,11 @@ function resolveSymphonyRuntimeSessionRelation(plan, webId, worker) {
|
|
|
592
369
|
return 'runtime-projected-worker-session';
|
|
593
370
|
}
|
|
594
371
|
function buildSymphonyWorkspaceMetadata(plan, worker) {
|
|
595
|
-
const workspace =
|
|
372
|
+
const workspace = normalizeWorkerWorkspace(worker.session.workspace ?? plan.session.workspace, worker.session.cwd ?? plan.session.cwd);
|
|
596
373
|
return {
|
|
597
374
|
path: workspace.path,
|
|
598
375
|
kind: workspace.kind,
|
|
599
|
-
...(workspace.
|
|
376
|
+
...(workspace.container ? { container: workspace.container } : {}),
|
|
600
377
|
...(workspace.repository ? { repository: workspace.repository } : {}),
|
|
601
378
|
...(workspace.branch ? { branch: workspace.branch } : {}),
|
|
602
379
|
...(workspace.worktree ? { worktree: workspace.worktree } : {}),
|
|
@@ -609,14 +386,14 @@ function buildSymphonyWorkspaceMetadata(plan, worker) {
|
|
|
609
386
|
equivalenceRequires: ['baseRevision', 'checksum-or-etag-or-artifact-uri'],
|
|
610
387
|
};
|
|
611
388
|
}
|
|
612
|
-
function
|
|
389
|
+
function normalizeWorkerWorkspace(workspace, fallbackPath) {
|
|
613
390
|
return {
|
|
614
391
|
path: workspace?.path ?? fallbackPath,
|
|
615
392
|
kind: workspace?.kind ?? 'folder',
|
|
616
393
|
...(workspace?.repository ? { repository: workspace.repository } : {}),
|
|
617
394
|
...(workspace?.branch ? { branch: workspace.branch } : {}),
|
|
618
395
|
...(workspace?.worktree ? { worktree: workspace.worktree } : {}),
|
|
619
|
-
...(workspace?.
|
|
396
|
+
...(workspace?.container ? { container: workspace.container } : {}),
|
|
620
397
|
...(workspace?.baseRevision ? { baseRevision: workspace.baseRevision } : {}),
|
|
621
398
|
...(workspace?.environment ? { environment: workspace.environment } : {}),
|
|
622
399
|
};
|
|
@@ -719,6 +496,7 @@ function withChatThreadRefs(plan, refs) {
|
|
|
719
496
|
thread: refs.thread,
|
|
720
497
|
messages: refs.messages,
|
|
721
498
|
},
|
|
499
|
+
...(worker.runSteps?.length ? { runSteps: worker.runSteps } : {}),
|
|
722
500
|
}));
|
|
723
501
|
const primary = workers[0] ?? {
|
|
724
502
|
task: plan.task,
|
|
@@ -753,6 +531,7 @@ function withChatThreadRefs(plan, refs) {
|
|
|
753
531
|
delivery: primary.delivery,
|
|
754
532
|
session: primary.session,
|
|
755
533
|
workers,
|
|
534
|
+
...(plan.followUpIssues?.length ? { followUpIssues: plan.followUpIssues } : {}),
|
|
756
535
|
};
|
|
757
536
|
}
|
|
758
537
|
function withTargetRefs(plan, refs, webId) {
|
|
@@ -795,6 +574,7 @@ function withTargetRefs(plan, refs, webId) {
|
|
|
795
574
|
messages: workerRefs.messages,
|
|
796
575
|
target,
|
|
797
576
|
},
|
|
577
|
+
...(worker.runSteps?.length ? { runSteps: worker.runSteps } : {}),
|
|
798
578
|
};
|
|
799
579
|
});
|
|
800
580
|
const primary = workers[0] ?? {
|
|
@@ -830,69 +610,9 @@ function withTargetRefs(plan, refs, webId) {
|
|
|
830
610
|
delivery: primary.delivery,
|
|
831
611
|
session: primary.session,
|
|
832
612
|
workers,
|
|
613
|
+
...(plan.followUpIssues?.length ? { followUpIssues: plan.followUpIssues } : {}),
|
|
833
614
|
};
|
|
834
615
|
}
|
|
835
|
-
function buildSymphonyChatRow(plan, webId, stage, lastPreview) {
|
|
836
|
-
const createdAt = safeDate(plan.issue.createdAt);
|
|
837
|
-
const updatedAt = safeDate(plan.session.updatedAt);
|
|
838
|
-
const secretaryAgent = agentResource.buildIri(webId, { id: SYMPHONY_SECRETARY_AGENT_ID });
|
|
839
|
-
const secretaryContact = buildSecretaryContactIri(webId);
|
|
840
|
-
const workerMembers = plan.workers.map((worker) => ({
|
|
841
|
-
contact: buildWorkerContactIri(webId, worker),
|
|
842
|
-
agent: buildWorkerAgentIri(webId, worker),
|
|
843
|
-
label: worker.session.target.label ?? worker.session.target.contact ?? worker.session.target.agent ?? backendDisplayName(worker.session.backend),
|
|
844
|
-
}));
|
|
845
|
-
const participants = Array.from(new Set([webId, secretaryContact, ...workerMembers.map((member) => member.contact)]));
|
|
846
|
-
const targetChat = selectTargetChatIri(plan.session.target?.chat, webId, plan);
|
|
847
|
-
return {
|
|
848
|
-
id: buildTargetChatId(plan, webId),
|
|
849
|
-
title: plan.session.target?.label ?? (targetChat === buildSymphonyChatUri(webId) ? 'AI Secretary · Symphony' : 'Symphony Delegation'),
|
|
850
|
-
participants,
|
|
851
|
-
metadata: {
|
|
852
|
-
kind: targetChat === buildSymphonyChatUri(webId) ? 'symphony-control-room' : 'symphony-target-room',
|
|
853
|
-
surface: 'symphony',
|
|
854
|
-
secretaryAgent,
|
|
855
|
-
secretaryContact,
|
|
856
|
-
currentBackend: plan.session.backend,
|
|
857
|
-
target: plan.session.target,
|
|
858
|
-
currentStage: stage,
|
|
859
|
-
memberRoles: Object.fromEntries([
|
|
860
|
-
[webId, 'owner'],
|
|
861
|
-
[secretaryContact, 'admin'],
|
|
862
|
-
...workerMembers.map((member) => [member.contact, 'member']),
|
|
863
|
-
]),
|
|
864
|
-
members: [
|
|
865
|
-
{ uri: webId, role: 'user', label: 'User' },
|
|
866
|
-
{ uri: secretaryContact, agent: secretaryAgent, role: 'secretary', label: 'AI Secretary' },
|
|
867
|
-
...workerMembers.map((member) => ({
|
|
868
|
-
uri: member.contact,
|
|
869
|
-
agent: member.agent,
|
|
870
|
-
role: 'worker',
|
|
871
|
-
label: member.label,
|
|
872
|
-
})),
|
|
873
|
-
],
|
|
874
|
-
},
|
|
875
|
-
lastActiveAt: updatedAt,
|
|
876
|
-
lastMessagePreview: lastPreview ? normalizeTitle(lastPreview, 100) : undefined,
|
|
877
|
-
createdAt,
|
|
878
|
-
updatedAt,
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
function collectSymphonyThreadProjectionGroups(plan, webId) {
|
|
882
|
-
const groups = new Map();
|
|
883
|
-
for (const worker of plan.workers) {
|
|
884
|
-
const chat = selectWorkerChatIri(plan, webId, worker);
|
|
885
|
-
const thread = selectWorkerThreadIri(plan, webId, worker);
|
|
886
|
-
const key = `${chat}\0${thread}`;
|
|
887
|
-
const existing = groups.get(key);
|
|
888
|
-
if (existing) {
|
|
889
|
-
existing.workers.push(worker);
|
|
890
|
-
continue;
|
|
891
|
-
}
|
|
892
|
-
groups.set(key, { chat, thread, workers: [worker] });
|
|
893
|
-
}
|
|
894
|
-
return Array.from(groups.values());
|
|
895
|
-
}
|
|
896
616
|
function buildSymphonyWorkerSummary(plan, webId, worker) {
|
|
897
617
|
return {
|
|
898
618
|
task: worker.task,
|
|
@@ -905,9 +625,6 @@ function buildSymphonyWorkerSummary(plan, webId, worker) {
|
|
|
905
625
|
sessionResource: buildSymphonyWorkerSessionUri(webId, worker),
|
|
906
626
|
backend: worker.session.backend,
|
|
907
627
|
agent: worker.session.target.agent,
|
|
908
|
-
contact: worker.session.target.contact ?? buildWorkerContactId(worker),
|
|
909
|
-
contactResource: buildWorkerContactIri(webId, worker),
|
|
910
|
-
agentResource: buildWorkerAgentIri(webId, worker),
|
|
911
628
|
status: worker.session.status,
|
|
912
629
|
autoModeSessionId: worker.session.autoModeSessionId,
|
|
913
630
|
target: worker.session.target,
|
|
@@ -915,6 +632,7 @@ function buildSymphonyWorkerSummary(plan, webId, worker) {
|
|
|
915
632
|
workspace: buildSymphonyWorkspaceMetadata(plan, worker),
|
|
916
633
|
podAccessPolicy: buildSymphonyWorkerPodAccessPolicy(plan, webId, worker),
|
|
917
634
|
reconciler: buildSymphonyReconcilerMetadata(worker),
|
|
635
|
+
acceptanceReview: worker.taskRecord.acceptanceReview ?? worker.delivery.acceptanceReview ?? worker.session.acceptanceReview,
|
|
918
636
|
};
|
|
919
637
|
}
|
|
920
638
|
function buildSymphonyReconcilerMetadata(worker) {
|
|
@@ -959,148 +677,21 @@ function createFallbackSymphonyDispatchDecision(worker) {
|
|
|
959
677
|
randomId: `${worker.delivery.uri}-dispatch`,
|
|
960
678
|
}).summary;
|
|
961
679
|
}
|
|
962
|
-
function buildSymphonyThreadRows(plan, webId, stage) {
|
|
963
|
-
return collectSymphonyThreadProjectionGroups(plan, webId)
|
|
964
|
-
.map((group) => buildSymphonyThreadRow(plan, webId, stage, group));
|
|
965
|
-
}
|
|
966
|
-
function buildSymphonyThreadRow(plan, webId, stage, group) {
|
|
967
|
-
const workers = group?.workers ?? plan.workers;
|
|
968
|
-
const primaryWorker = workers[0] ?? {
|
|
969
|
-
task: plan.task,
|
|
970
|
-
taskRecord: plan.taskRecord,
|
|
971
|
-
delivery: plan.delivery,
|
|
972
|
-
session: plan.session,
|
|
973
|
-
};
|
|
974
|
-
const createdAt = safeDate(primaryWorker.session.createdAt);
|
|
975
|
-
const updatedAt = safeDate(primaryWorker.session.updatedAt);
|
|
976
|
-
const parent = group?.chat ?? selectTargetChatIri(plan.session.target?.chat, webId, plan);
|
|
977
|
-
const thread = group?.thread ?? selectTargetThreadIri(plan.session.target?.thread, webId, plan);
|
|
978
|
-
const workspace = pathToWorkspaceUri(primaryWorker.session.cwd) ?? pathToWorkspaceUri(plan.session.cwd);
|
|
979
|
-
return {
|
|
980
|
-
id: threadRepository.idForChat(parent, thread),
|
|
981
|
-
parent,
|
|
982
|
-
title: normalizeTitle(plan.issue.title || plan.issue.description || 'Symphony Task'),
|
|
983
|
-
...(workspace ? { workspace } : {}),
|
|
984
|
-
metadata: {
|
|
985
|
-
kind: 'symphony-run',
|
|
986
|
-
surface: 'symphony',
|
|
987
|
-
stage,
|
|
988
|
-
status: plan.session.status,
|
|
989
|
-
issue: plan.issue.uri,
|
|
990
|
-
task: primaryWorker.task,
|
|
991
|
-
delivery: primaryWorker.delivery.uri,
|
|
992
|
-
session: primaryWorker.session.uri,
|
|
993
|
-
issuer: plan.issue.issuer,
|
|
994
|
-
workers: workers.map((worker) => buildSymphonyWorkerSummary(plan, webId, worker)),
|
|
995
|
-
backend: primaryWorker.session.backend,
|
|
996
|
-
mode: primaryWorker.session.mode,
|
|
997
|
-
model: primaryWorker.session.model,
|
|
998
|
-
workspacePath: primaryWorker.session.cwd,
|
|
999
|
-
workspace: buildSymphonyWorkspaceMetadata(plan, primaryWorker),
|
|
1000
|
-
reconciler: buildSymphonyReconcilerMetadata(primaryWorker),
|
|
1001
|
-
autoModeSessionId: primaryWorker.session.autoModeSessionId,
|
|
1002
|
-
exitCode: primaryWorker.session.exitCode,
|
|
1003
|
-
error: primaryWorker.session.error ?? primaryWorker.delivery.error ?? plan.issue.error,
|
|
1004
|
-
target: primaryWorker.session.target,
|
|
1005
|
-
},
|
|
1006
|
-
createdAt,
|
|
1007
|
-
updatedAt,
|
|
1008
|
-
};
|
|
1009
|
-
}
|
|
1010
|
-
function buildSymphonySessionRow(plan, webId, worker = plan.workers[0] ?? {
|
|
1011
|
-
task: plan.task,
|
|
1012
|
-
taskRecord: plan.taskRecord,
|
|
1013
|
-
delivery: plan.delivery,
|
|
1014
|
-
session: plan.session,
|
|
1015
|
-
}) {
|
|
1016
|
-
const createdAt = safeDate(worker.session.createdAt);
|
|
1017
|
-
const updatedAt = safeDate(worker.session.updatedAt);
|
|
1018
|
-
const status = worker.session.status === 'completed'
|
|
1019
|
-
? 'completed'
|
|
1020
|
-
: worker.session.status === 'failed'
|
|
1021
|
-
? 'error'
|
|
1022
|
-
: 'active';
|
|
1023
|
-
const workerSummary = buildSymphonyWorkerSummary(plan, webId, worker);
|
|
1024
|
-
return {
|
|
1025
|
-
id: buildSymphonySessionRecordId(worker.session),
|
|
1026
|
-
owner: webId,
|
|
1027
|
-
chat: selectWorkerChatIri(plan, webId, worker),
|
|
1028
|
-
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1029
|
-
sessionType: 'group',
|
|
1030
|
-
status,
|
|
1031
|
-
tool: `symphony:${worker.session.backend}`,
|
|
1032
|
-
tokenUsage: 0,
|
|
1033
|
-
messages: worker.session.messages,
|
|
1034
|
-
policyVersion: SYMPHONY_POLICY_VERSION,
|
|
1035
|
-
metadata: {
|
|
1036
|
-
kind: 'symphony-run',
|
|
1037
|
-
surface: 'symphony',
|
|
1038
|
-
issue: plan.issue.uri,
|
|
1039
|
-
task: worker.task,
|
|
1040
|
-
delivery: worker.delivery.uri,
|
|
1041
|
-
session: worker.session.uri,
|
|
1042
|
-
issuer: plan.issue.issuer,
|
|
1043
|
-
worker: workerSummary,
|
|
1044
|
-
workers: [workerSummary],
|
|
1045
|
-
backend: worker.session.backend,
|
|
1046
|
-
mode: worker.session.mode,
|
|
1047
|
-
model: worker.session.model,
|
|
1048
|
-
workspacePath: worker.session.cwd,
|
|
1049
|
-
workspace: buildSymphonyWorkspaceMetadata(plan, worker),
|
|
1050
|
-
reconciler: buildSymphonyReconcilerMetadata(worker),
|
|
1051
|
-
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1052
|
-
exitCode: worker.session.exitCode,
|
|
1053
|
-
dryRun: worker.session.dryRun,
|
|
1054
|
-
error: worker.session.error ?? worker.delivery.error ?? plan.issue.error,
|
|
1055
|
-
target: worker.session.target,
|
|
1056
|
-
},
|
|
1057
|
-
createdAt,
|
|
1058
|
-
updatedAt,
|
|
1059
|
-
...(status === 'completed' || status === 'error' ? { archivedAt: updatedAt } : {}),
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
function buildSymphonyIssueRow(plan, webId) {
|
|
1063
|
-
const createdAt = safeDate(plan.issue.createdAt);
|
|
1064
|
-
const updatedAt = safeDate(plan.issue.updatedAt);
|
|
1065
|
-
const secretaryContact = buildSecretaryContactIri(webId);
|
|
1066
|
-
return {
|
|
1067
|
-
id: buildSymphonyIssueId(plan.issue),
|
|
1068
|
-
// File-primary: title remains a compact index label for existing Issue schemas.
|
|
1069
|
-
// The full problem statement and acceptance narrative live in issue.md.
|
|
1070
|
-
title: plan.issue.title,
|
|
1071
|
-
document: podFileUrlFromWebId(webId, buildSymphonyIssueDocumentPath(plan.issue)),
|
|
1072
|
-
description: undefined,
|
|
1073
|
-
status: plan.issue.status,
|
|
1074
|
-
priority: plan.issue.priority,
|
|
1075
|
-
labels: ['symphony'],
|
|
1076
|
-
chat: selectTargetChatIri(plan.session.target?.chat, webId, plan),
|
|
1077
|
-
thread: selectTargetThreadIri(plan.session.target?.thread, webId, plan),
|
|
1078
|
-
tasks: Array.from(new Set((plan.issue.tasks?.length ? plan.issue.tasks : plan.workers.map((worker) => worker.task))
|
|
1079
|
-
.map((task) => normalizeSymphonyTaskIri(webId, task)))),
|
|
1080
|
-
createdBy: plan.issue.issuer.webId ?? webId,
|
|
1081
|
-
assignedTo: secretaryContact,
|
|
1082
|
-
createdAt,
|
|
1083
|
-
updatedAt,
|
|
1084
|
-
...(plan.issue.closedAt ? { closedAt: safeDate(plan.issue.closedAt) } : {}),
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
680
|
function buildSymphonyIdeaRow(idea, webId) {
|
|
1088
681
|
const createdAt = safeDate(idea.createdAt);
|
|
1089
682
|
const updatedAt = safeDate(idea.updatedAt);
|
|
1090
683
|
return {
|
|
1091
684
|
id: getSymphonyArchiveKey(idea.uri),
|
|
1092
685
|
summary: idea.summary,
|
|
1093
|
-
|
|
1094
|
-
// File-primary: the source text lives in idea.md; TTL keeps only routing/index facts.
|
|
1095
|
-
input: undefined,
|
|
686
|
+
input: idea.input,
|
|
1096
687
|
status: idea.status,
|
|
1097
688
|
commitment: idea.commitment,
|
|
1098
689
|
affectedArea: idea.affectedArea,
|
|
1099
|
-
currentUnderstanding:
|
|
1100
|
-
openQuestions:
|
|
690
|
+
currentUnderstanding: idea.currentUnderstanding,
|
|
691
|
+
openQuestions: idea.openQuestions,
|
|
1101
692
|
related: idea.relatedRecords,
|
|
1102
|
-
conflicts:
|
|
1103
|
-
nextStep:
|
|
693
|
+
conflicts: idea.conflicts,
|
|
694
|
+
nextStep: idea.nextStep,
|
|
1104
695
|
promotedTo: idea.promotedTo,
|
|
1105
696
|
chat: idea.chat,
|
|
1106
697
|
thread: idea.thread,
|
|
@@ -1108,277 +699,25 @@ function buildSymphonyIdeaRow(idea, webId) {
|
|
|
1108
699
|
createdBy: webId,
|
|
1109
700
|
metadata: {
|
|
1110
701
|
surface: 'symphony',
|
|
1111
|
-
filePrimary: true,
|
|
1112
|
-
documentPath: buildSymphonyIdeaDocumentPath(idea),
|
|
1113
702
|
...buildSymphonyArchiveMetadata({ idea: idea.uri }),
|
|
1114
703
|
},
|
|
1115
704
|
createdAt,
|
|
1116
705
|
updatedAt,
|
|
1117
706
|
};
|
|
1118
707
|
}
|
|
1119
|
-
function mapSymphonyTaskStatus(status) {
|
|
1120
|
-
if (status === 'running')
|
|
1121
|
-
return 'active';
|
|
1122
|
-
if (status === 'pending')
|
|
1123
|
-
return 'open';
|
|
1124
|
-
return status;
|
|
1125
|
-
}
|
|
1126
|
-
function mapSymphonyRunStatus(status) {
|
|
1127
|
-
if (status === 'planned')
|
|
1128
|
-
return 'queued';
|
|
1129
|
-
if (status === 'running')
|
|
1130
|
-
return 'running';
|
|
1131
|
-
if (status === 'completed')
|
|
1132
|
-
return 'completed';
|
|
1133
|
-
if (status === 'failed')
|
|
1134
|
-
return 'failed';
|
|
1135
|
-
return 'queued';
|
|
1136
|
-
}
|
|
1137
|
-
function buildSymphonyTaskRow(plan, webId, worker) {
|
|
1138
|
-
const createdAt = safeDate(worker.taskRecord.createdAt);
|
|
1139
|
-
const updatedAt = safeDate(worker.taskRecord.updatedAt);
|
|
1140
|
-
const workerContact = buildWorkerContactIri(webId, worker);
|
|
1141
|
-
const workerAgent = buildWorkerAgentIri(webId, worker);
|
|
1142
|
-
return {
|
|
1143
|
-
id: taskResource.buildId({ id: buildSymphonyTaskKey(worker.task) }),
|
|
1144
|
-
title: worker.taskRecord.title,
|
|
1145
|
-
instruction: worker.taskRecord.objective,
|
|
1146
|
-
prompt: worker.delivery.projection.prompt,
|
|
1147
|
-
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1148
|
-
message: plan.issue.messages?.at(-1),
|
|
1149
|
-
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1150
|
-
workspace: pathToWorkspaceUri(worker.session.cwd) ?? pathToWorkspaceUri(plan.session.cwd) ?? 'file:///',
|
|
1151
|
-
status: mapSymphonyTaskStatus(worker.taskRecord.status),
|
|
1152
|
-
priority: plan.issue.priority,
|
|
1153
|
-
assignedTo: workerContact,
|
|
1154
|
-
source: buildSymphonyIssueIri(webId, plan.issue),
|
|
1155
|
-
metadata: {
|
|
1156
|
-
surface: 'symphony',
|
|
1157
|
-
...buildSymphonyArchiveMetadata({ task: worker.taskRecord.uri }),
|
|
1158
|
-
acceptanceCriteria: worker.taskRecord.acceptanceCriteria,
|
|
1159
|
-
backend: worker.session.backend,
|
|
1160
|
-
target: worker.session.target,
|
|
1161
|
-
assignedContact: workerContact,
|
|
1162
|
-
assignedAgent: workerAgent,
|
|
1163
|
-
workspace: buildSymphonyWorkspaceMetadata(plan, worker),
|
|
1164
|
-
spaceContract: buildSymphonySpaceContract(plan, webId, worker),
|
|
1165
|
-
podAccessPolicy: buildSymphonyWorkerPodAccessPolicy(plan, webId, worker),
|
|
1166
|
-
reconciler: buildSymphonyReconcilerMetadata(worker),
|
|
1167
|
-
},
|
|
1168
|
-
createdAt,
|
|
1169
|
-
updatedAt,
|
|
1170
|
-
};
|
|
1171
|
-
}
|
|
1172
|
-
function buildSymphonyDeliveryRow(plan, webId, worker) {
|
|
1173
|
-
const createdAt = safeDate(worker.delivery.createdAt);
|
|
1174
|
-
const updatedAt = safeDate(worker.delivery.updatedAt);
|
|
1175
|
-
const secretaryAgent = agentResource.buildIri(webId, { id: SYMPHONY_SECRETARY_AGENT_ID });
|
|
1176
|
-
const secretaryContact = buildSecretaryContactIri(webId);
|
|
1177
|
-
const workerAgent = buildWorkerAgentIri(webId, worker);
|
|
1178
|
-
const workerContact = buildWorkerContactIri(webId, worker);
|
|
1179
|
-
return {
|
|
1180
|
-
id: deliveryResource.buildId({
|
|
1181
|
-
id: getSymphonyArchiveKey(worker.delivery.uri),
|
|
1182
|
-
task: buildSymphonyTaskIri(webId, worker.task),
|
|
1183
|
-
createdAt,
|
|
1184
|
-
}),
|
|
1185
|
-
kind: worker.delivery.type,
|
|
1186
|
-
status: worker.delivery.status,
|
|
1187
|
-
task: buildSymphonyTaskIri(webId, worker.task),
|
|
1188
|
-
source: secretaryContact,
|
|
1189
|
-
target: workerContact,
|
|
1190
|
-
chat: selectWorkerChatIri(plan, webId, worker),
|
|
1191
|
-
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1192
|
-
targetThread: selectWorkerThreadIri(plan, webId, worker),
|
|
1193
|
-
targetSession: worker.session.uri,
|
|
1194
|
-
actor: secretaryAgent,
|
|
1195
|
-
object: buildSymphonyTaskIri(webId, worker.task),
|
|
1196
|
-
objective: worker.taskRecord.objective,
|
|
1197
|
-
payload: {
|
|
1198
|
-
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1199
|
-
acceptanceCriteria: worker.taskRecord.acceptanceCriteria,
|
|
1200
|
-
backend: worker.session.backend,
|
|
1201
|
-
mode: worker.session.mode,
|
|
1202
|
-
target: worker.session.target,
|
|
1203
|
-
targetContact: workerContact,
|
|
1204
|
-
targetAgent: workerAgent,
|
|
1205
|
-
workspace: buildSymphonyWorkspaceMetadata(plan, worker),
|
|
1206
|
-
spaceContract: buildSymphonySpaceContract(plan, webId, worker),
|
|
1207
|
-
podAccessPolicy: buildSymphonyWorkerPodAccessPolicy(plan, webId, worker),
|
|
1208
|
-
reconciler: buildSymphonyReconcilerMetadata(worker),
|
|
1209
|
-
},
|
|
1210
|
-
projection: worker.delivery.projection,
|
|
1211
|
-
projectedRole: worker.delivery.projection.runtimeRole,
|
|
1212
|
-
metadata: {
|
|
1213
|
-
surface: 'symphony',
|
|
1214
|
-
...buildSymphonyArchiveMetadata({
|
|
1215
|
-
issue: plan.issue.uri,
|
|
1216
|
-
task: worker.task,
|
|
1217
|
-
delivery: worker.delivery.uri,
|
|
1218
|
-
session: worker.session.uri,
|
|
1219
|
-
}),
|
|
1220
|
-
autoModeSessionId: worker.delivery.autoModeSessionId,
|
|
1221
|
-
sourceAgent: secretaryAgent,
|
|
1222
|
-
targetContact: workerContact,
|
|
1223
|
-
targetAgent: workerAgent,
|
|
1224
|
-
workspace: buildSymphonyWorkspaceMetadata(plan, worker),
|
|
1225
|
-
spaceContract: buildSymphonySpaceContract(plan, webId, worker),
|
|
1226
|
-
podAccessPolicy: buildSymphonyWorkerPodAccessPolicy(plan, webId, worker),
|
|
1227
|
-
reconciler: buildSymphonyReconcilerMetadata(worker),
|
|
1228
|
-
},
|
|
1229
|
-
error: worker.delivery.error,
|
|
1230
|
-
createdAt,
|
|
1231
|
-
dispatchedAt: worker.delivery.status === 'dispatched' || worker.delivery.status === 'completed'
|
|
1232
|
-
? updatedAt
|
|
1233
|
-
: undefined,
|
|
1234
|
-
completedAt: worker.delivery.completedAt ? safeDate(worker.delivery.completedAt) : undefined,
|
|
1235
|
-
updatedAt,
|
|
1236
|
-
};
|
|
1237
|
-
}
|
|
1238
|
-
function buildSymphonyReportRow(plan, webId, worker, stage) {
|
|
1239
|
-
const completedAt = safeDate(worker.session.completedAt ?? worker.session.updatedAt);
|
|
1240
|
-
const workerAgent = buildWorkerAgentIri(webId, worker);
|
|
1241
|
-
const run = buildSymphonyRunIri(webId, worker);
|
|
1242
|
-
const task = buildSymphonyTaskIri(webId, worker.task);
|
|
1243
|
-
const status = worker.session.status === 'failed' || stage === 'failed' ? 'failed' : 'completed';
|
|
1244
|
-
const summary = status === 'completed'
|
|
1245
|
-
? `${worker.taskRecord.title} completed.`
|
|
1246
|
-
: `${worker.taskRecord.title} failed: ${worker.session.error ?? worker.delivery.error ?? 'worker did not complete successfully.'}`;
|
|
1247
|
-
const evidence = buildSymphonyEvidenceIri(webId, worker, stage);
|
|
1248
|
-
return {
|
|
1249
|
-
id: reportResource.buildId({
|
|
1250
|
-
id: `${getSymphonyArchiveKey(worker.session.uri)}-report`,
|
|
1251
|
-
task,
|
|
1252
|
-
createdAt: completedAt,
|
|
1253
|
-
}),
|
|
1254
|
-
reportKind: 'handoff',
|
|
1255
|
-
status: 'published',
|
|
1256
|
-
outcome: status === 'completed' ? 'accepted' : 'blocked',
|
|
1257
|
-
about: run,
|
|
1258
|
-
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1259
|
-
task,
|
|
1260
|
-
delivery: buildSymphonyDeliveryIri(webId, worker),
|
|
1261
|
-
run,
|
|
1262
|
-
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1263
|
-
evidence: [
|
|
1264
|
-
evidence,
|
|
1265
|
-
],
|
|
1266
|
-
summary,
|
|
1267
|
-
actor: workerAgent,
|
|
1268
|
-
source: podFileUrlFromWebId(webId, buildSymphonyReportDocumentPath(worker)),
|
|
1269
|
-
metricFacts: {
|
|
1270
|
-
backend: worker.session.backend,
|
|
1271
|
-
agent: worker.session.target.agent,
|
|
1272
|
-
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1273
|
-
exitCode: worker.session.exitCode,
|
|
1274
|
-
},
|
|
1275
|
-
metadata: {
|
|
1276
|
-
surface: 'symphony',
|
|
1277
|
-
filePrimary: true,
|
|
1278
|
-
reportFile: buildSymphonyReportDocumentPath(worker),
|
|
1279
|
-
reportDelivery: buildSymphonyReportDeliveryIri(webId, worker),
|
|
1280
|
-
postRunReconciliation: buildPostRunReconciliationMetadata(plan, webId, worker, stage),
|
|
1281
|
-
...buildSymphonyArchiveMetadata({
|
|
1282
|
-
issue: plan.issue.uri,
|
|
1283
|
-
task: worker.task,
|
|
1284
|
-
delivery: worker.delivery.uri,
|
|
1285
|
-
session: worker.session.uri,
|
|
1286
|
-
}),
|
|
1287
|
-
},
|
|
1288
|
-
createdAt: completedAt,
|
|
1289
|
-
publishedAt: completedAt,
|
|
1290
|
-
updatedAt: completedAt,
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
|
-
function buildSymphonyEvidenceRow(plan, webId, worker, stage) {
|
|
1294
|
-
const createdAt = safeDate(worker.session.completedAt ?? worker.session.updatedAt);
|
|
1295
|
-
const status = worker.session.status === 'failed' || stage === 'failed' ? 'failed' : 'completed';
|
|
1296
|
-
const run = buildSymphonyRunIri(webId, worker);
|
|
1297
|
-
const task = buildSymphonyTaskIri(webId, worker.task);
|
|
1298
|
-
const delivery = buildSymphonyDeliveryIri(webId, worker);
|
|
1299
|
-
const runStep = buildSymphonyRunStepIri(webId, worker, stage);
|
|
1300
|
-
const workerAgent = buildWorkerAgentIri(webId, worker);
|
|
1301
|
-
return {
|
|
1302
|
-
id: evidenceResource.buildId({
|
|
1303
|
-
id: `${getSymphonyArchiveKey(worker.session.uri)}-${stage}`,
|
|
1304
|
-
createdAt,
|
|
1305
|
-
}),
|
|
1306
|
-
evidenceKind: EvidenceKind.RUNTIME_LOG,
|
|
1307
|
-
about: run,
|
|
1308
|
-
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1309
|
-
task,
|
|
1310
|
-
delivery,
|
|
1311
|
-
run,
|
|
1312
|
-
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1313
|
-
summary: status === 'completed'
|
|
1314
|
-
? `${worker.taskRecord.title} completed with runtime status ${worker.session.status}.`
|
|
1315
|
-
: `${worker.taskRecord.title} failed: ${worker.session.error ?? worker.delivery.error ?? worker.taskRecord.error ?? 'worker did not complete successfully.'}`,
|
|
1316
|
-
source: podFileUrlFromWebId(webId, buildSymphonyEvidenceDocumentPath(worker, stage)),
|
|
1317
|
-
actor: workerAgent,
|
|
1318
|
-
outcome: status,
|
|
1319
|
-
metadata: {
|
|
1320
|
-
surface: 'symphony',
|
|
1321
|
-
filePrimary: true,
|
|
1322
|
-
evidenceFile: buildSymphonyEvidenceDocumentPath(worker, stage),
|
|
1323
|
-
sourceRunStep: runStep,
|
|
1324
|
-
report: buildSymphonyReportIri(webId, worker),
|
|
1325
|
-
reportDelivery: buildSymphonyReportDeliveryIri(webId, worker),
|
|
1326
|
-
postRunReconciliation: buildPostRunReconciliationMetadata(plan, webId, worker, stage),
|
|
1327
|
-
runtime: {
|
|
1328
|
-
backend: worker.session.backend,
|
|
1329
|
-
model: worker.session.model,
|
|
1330
|
-
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1331
|
-
exitCode: worker.session.exitCode,
|
|
1332
|
-
status: worker.session.status,
|
|
1333
|
-
},
|
|
1334
|
-
...buildSymphonyArchiveMetadata({
|
|
1335
|
-
issue: plan.issue.uri,
|
|
1336
|
-
task: worker.task,
|
|
1337
|
-
delivery: worker.delivery.uri,
|
|
1338
|
-
session: worker.session.uri,
|
|
1339
|
-
}),
|
|
1340
|
-
},
|
|
1341
|
-
createdAt,
|
|
1342
|
-
};
|
|
1343
|
-
}
|
|
1344
|
-
function buildPostRunReconciliationMetadata(plan, webId, worker, stage) {
|
|
1345
|
-
return {
|
|
1346
|
-
required: true,
|
|
1347
|
-
status: 'pending_secretary_review',
|
|
1348
|
-
owner: buildSecretaryContactIri(webId),
|
|
1349
|
-
sourceIssue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1350
|
-
sourceTask: buildSymphonyTaskIri(webId, worker.task),
|
|
1351
|
-
sourceDelivery: buildSymphonyDeliveryIri(webId, worker),
|
|
1352
|
-
sourceRun: buildSymphonyRunIri(webId, worker),
|
|
1353
|
-
sourceRunStep: buildSymphonyRunStepIri(webId, worker, stage),
|
|
1354
|
-
sourceEvidence: buildSymphonyEvidenceIri(webId, worker, stage),
|
|
1355
|
-
sourceReport: buildSymphonyReportIri(webId, worker),
|
|
1356
|
-
classifications: [
|
|
1357
|
-
'same_issue_task',
|
|
1358
|
-
'new_issue',
|
|
1359
|
-
'idea',
|
|
1360
|
-
'evidence_only',
|
|
1361
|
-
'ask_user',
|
|
1362
|
-
],
|
|
1363
|
-
rule: 'Secretary must reconcile acceptance and follow-up before task closure.',
|
|
1364
|
-
};
|
|
1365
|
-
}
|
|
1366
708
|
function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
1367
709
|
const completedAt = safeDate(worker.session.completedAt ?? worker.session.updatedAt);
|
|
1368
|
-
const workerAgent =
|
|
1369
|
-
|
|
710
|
+
const workerAgent = agentResource.buildIri(webId, {
|
|
711
|
+
id: buildWorkerAgentId(worker.session.backend, worker.session.target.agent),
|
|
712
|
+
});
|
|
1370
713
|
const secretaryAgent = agentResource.buildIri(webId, { id: SYMPHONY_SECRETARY_AGENT_ID });
|
|
1371
|
-
const secretaryContact = buildSecretaryContactIri(webId);
|
|
1372
714
|
const run = buildSymphonyRunIri(webId, worker);
|
|
1373
|
-
const report = buildSymphonyReportIri(webId, worker);
|
|
1374
715
|
const task = buildSymphonyTaskIri(webId, worker.task);
|
|
1375
716
|
const originalDelivery = buildSymphonyDeliveryIri(webId, worker);
|
|
1376
717
|
const status = worker.session.status === 'failed' || stage === 'failed' ? 'failed' : 'completed';
|
|
1377
718
|
const summary = status === 'completed'
|
|
1378
719
|
? `${worker.taskRecord.title} completed.`
|
|
1379
720
|
: `${worker.taskRecord.title} failed: ${worker.session.error ?? worker.delivery.error ?? 'worker did not complete successfully.'}`;
|
|
1380
|
-
const evidence = buildSymphonyEvidenceIri(webId, worker, stage);
|
|
1381
|
-
const sourceRunStep = buildSymphonyRunStepIri(webId, worker, stage);
|
|
1382
721
|
return {
|
|
1383
722
|
id: deliveryResource.buildId({
|
|
1384
723
|
id: `${getSymphonyArchiveKey(worker.session.uri)}-report`,
|
|
@@ -1388,14 +727,14 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
1388
727
|
kind: 'report',
|
|
1389
728
|
status: 'completed',
|
|
1390
729
|
task,
|
|
1391
|
-
source:
|
|
1392
|
-
target:
|
|
730
|
+
source: workerAgent,
|
|
731
|
+
target: secretaryAgent,
|
|
1393
732
|
chat: selectWorkerChatIri(plan, webId, worker),
|
|
1394
733
|
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1395
734
|
targetThread: selectTargetThreadIri(plan.issue.thread ?? worker.session.target?.thread, webId, plan),
|
|
1396
735
|
targetSession: buildSymphonyControlSessionUri(webId, plan),
|
|
1397
736
|
actor: workerAgent,
|
|
1398
|
-
object:
|
|
737
|
+
object: run,
|
|
1399
738
|
objective: summary,
|
|
1400
739
|
payload: {
|
|
1401
740
|
kind: 'symphony_report',
|
|
@@ -1404,25 +743,18 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
1404
743
|
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1405
744
|
task,
|
|
1406
745
|
delivery: originalDelivery,
|
|
1407
|
-
report,
|
|
1408
746
|
reportDelivery: buildSymphonyReportDeliveryIri(webId, worker),
|
|
1409
747
|
session: buildSymphonyControlSessionUri(webId, plan),
|
|
1410
748
|
run,
|
|
1411
749
|
backend: worker.session.backend,
|
|
1412
750
|
agent: worker.session.target.agent,
|
|
1413
|
-
contact: worker.session.target.contact ?? buildWorkerContactId(worker),
|
|
1414
|
-
sourceAgent: workerAgent,
|
|
1415
|
-
sourceContact: workerContact,
|
|
1416
751
|
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1417
752
|
exitCode: worker.session.exitCode,
|
|
1418
753
|
error: worker.session.error ?? worker.delivery.error ?? worker.taskRecord.error,
|
|
1419
|
-
|
|
1420
|
-
evidenceFile: buildSymphonyEvidenceDocumentPath(worker, stage),
|
|
1421
|
-
postRunReconciliation: buildPostRunReconciliationMetadata(plan, webId, worker, stage),
|
|
754
|
+
acceptanceReview: worker.delivery.acceptanceReview ?? worker.taskRecord.acceptanceReview ?? worker.session.acceptanceReview,
|
|
1422
755
|
evidence: {
|
|
1423
|
-
statusMessage:
|
|
1424
|
-
|
|
1425
|
-
evidence,
|
|
756
|
+
statusMessage: buildSharedSymphonyMessageIri(webId, plan, buildSharedSymphonyStatusMessageRow(plan, webId, stage)),
|
|
757
|
+
runStep: buildSymphonyRunStepIri(webId, worker, stage),
|
|
1426
758
|
},
|
|
1427
759
|
},
|
|
1428
760
|
projection: {
|
|
@@ -1439,11 +771,7 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
1439
771
|
session: worker.session.uri,
|
|
1440
772
|
}),
|
|
1441
773
|
reportKind: 'worker-completion',
|
|
1442
|
-
|
|
1443
|
-
reportFile: buildSymphonyReportDocumentPath(worker),
|
|
1444
|
-
evidence,
|
|
1445
|
-
evidenceFile: buildSymphonyEvidenceDocumentPath(worker, stage),
|
|
1446
|
-
postRunReconciliation: buildPostRunReconciliationMetadata(plan, webId, worker, stage),
|
|
774
|
+
acceptanceReview: worker.delivery.acceptanceReview ?? worker.taskRecord.acceptanceReview ?? worker.session.acceptanceReview,
|
|
1447
775
|
},
|
|
1448
776
|
error: status === 'failed' ? worker.session.error ?? worker.delivery.error ?? worker.taskRecord.error : undefined,
|
|
1449
777
|
createdAt: completedAt,
|
|
@@ -1453,101 +781,13 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
1453
781
|
updatedAt: completedAt,
|
|
1454
782
|
};
|
|
1455
783
|
}
|
|
1456
|
-
function
|
|
1457
|
-
const
|
|
1458
|
-
const updatedAt = safeDate(worker.session.updatedAt);
|
|
1459
|
-
return {
|
|
1460
|
-
id: runResource.buildId({
|
|
1461
|
-
id: getSymphonyArchiveKey(worker.session.uri),
|
|
1462
|
-
task: buildSymphonyTaskIri(webId, worker.task),
|
|
1463
|
-
createdAt,
|
|
1464
|
-
}),
|
|
1465
|
-
task: buildSymphonyTaskIri(webId, worker.task),
|
|
1466
|
-
delivery: buildSymphonyDeliveryIri(webId, worker),
|
|
1467
|
-
trigger: plan.issue.messages?.at(-1) ?? buildSymphonyIssueIri(webId, plan.issue),
|
|
1468
|
-
input: buildSymphonyDeliveryIri(webId, worker),
|
|
1469
|
-
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1470
|
-
workspace: pathToWorkspaceUri(worker.session.cwd) ?? pathToWorkspaceUri(plan.session.cwd) ?? 'file:///',
|
|
1471
|
-
status: mapSymphonyRunStatus(worker.session.status),
|
|
1472
|
-
runner: worker.session.backend,
|
|
1473
|
-
prompt: worker.delivery.projection.prompt,
|
|
1474
|
-
externalRunId: worker.session.autoModeSessionId,
|
|
1475
|
-
error: worker.session.error,
|
|
1476
|
-
metadata: {
|
|
1477
|
-
surface: 'symphony',
|
|
1478
|
-
...buildSymphonyArchiveMetadata({ session: worker.session.uri }),
|
|
1479
|
-
mode: worker.session.mode,
|
|
1480
|
-
model: worker.session.model,
|
|
1481
|
-
target: worker.session.target,
|
|
1482
|
-
workspace: buildSymphonyWorkspaceMetadata(plan, worker),
|
|
1483
|
-
spaceContract: buildSymphonySpaceContract(plan, webId, worker),
|
|
1484
|
-
podAccessPolicy: buildSymphonyWorkerPodAccessPolicy(plan, webId, worker),
|
|
1485
|
-
reconciler: buildSymphonyReconcilerMetadata(worker),
|
|
1486
|
-
exitCode: worker.session.exitCode,
|
|
1487
|
-
dryRun: worker.session.dryRun,
|
|
1488
|
-
},
|
|
1489
|
-
createdAt,
|
|
1490
|
-
startedAt: worker.session.status === 'running' || worker.session.status === 'completed' || worker.session.status === 'failed'
|
|
1491
|
-
? updatedAt
|
|
1492
|
-
: undefined,
|
|
1493
|
-
completedAt: worker.session.completedAt ? safeDate(worker.session.completedAt) : undefined,
|
|
1494
|
-
updatedAt,
|
|
1495
|
-
};
|
|
1496
|
-
}
|
|
1497
|
-
function buildSymphonyRunStepRow(plan, webId, worker, stage) {
|
|
1498
|
-
const run = buildSymphonyRunIri(webId, worker);
|
|
1499
|
-
const createdAt = stage === 'planned' ? safeDate(worker.session.createdAt) : safeDate(worker.session.updatedAt);
|
|
1500
|
-
const stepType = stage === 'planned'
|
|
1501
|
-
? 'run.created'
|
|
1502
|
-
: stage === 'running'
|
|
1503
|
-
? 'run.started'
|
|
1504
|
-
: stage === 'completed'
|
|
1505
|
-
? 'run.completed'
|
|
1506
|
-
: 'run.failed';
|
|
1507
|
-
return {
|
|
1508
|
-
id: runStepResource.buildId({
|
|
1509
|
-
id: `${getSymphonyArchiveKey(worker.session.uri)}-${stage}`,
|
|
1510
|
-
run,
|
|
1511
|
-
}),
|
|
1512
|
-
run,
|
|
1513
|
-
stepType,
|
|
1514
|
-
message: buildStatusContent(plan, stage),
|
|
1515
|
-
data: {
|
|
1516
|
-
surface: 'symphony',
|
|
1517
|
-
stage,
|
|
1518
|
-
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1519
|
-
task: buildSymphonyTaskIri(webId, worker.task),
|
|
1520
|
-
delivery: buildSymphonyDeliveryIri(webId, worker),
|
|
1521
|
-
archive: buildSymphonyArchiveRefs({ session: worker.session.uri }),
|
|
1522
|
-
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1523
|
-
},
|
|
1524
|
-
createdAt,
|
|
1525
|
-
};
|
|
1526
|
-
}
|
|
1527
|
-
function normalizeSymphonyActorKey(value, fallback) {
|
|
1528
|
-
return (value ?? fallback)
|
|
784
|
+
function buildWorkerAgentId(backend, agent) {
|
|
785
|
+
const suffix = (agent ?? `${backend}-worker`)
|
|
1529
786
|
.trim()
|
|
1530
787
|
.replace(/[^a-zA-Z0-9._-]/gu, '-')
|
|
1531
788
|
.replace(/-+/gu, '-')
|
|
1532
|
-
.replace(/^-|-$/gu, '')
|
|
1533
|
-
|
|
1534
|
-
}
|
|
1535
|
-
function buildWorkerContactId(worker) {
|
|
1536
|
-
return normalizeSymphonyActorKey(worker.session.target.contact ?? worker.session.target.agent ?? worker.delivery.targetAgent, worker.session.backend);
|
|
1537
|
-
}
|
|
1538
|
-
function buildWorkerAgentId(backend, agent, contact) {
|
|
1539
|
-
return normalizeSymphonyActorKey(agent ?? contact, backend);
|
|
1540
|
-
}
|
|
1541
|
-
function buildSecretaryContactIri(webId) {
|
|
1542
|
-
return contactResource.buildIri(webId, { id: SYMPHONY_CONTACT_ID });
|
|
1543
|
-
}
|
|
1544
|
-
function buildWorkerAgentIri(webId, worker) {
|
|
1545
|
-
return agentResource.buildIri(webId, {
|
|
1546
|
-
id: buildWorkerAgentId(worker.session.backend, worker.session.target.agent, worker.session.target.contact),
|
|
1547
|
-
});
|
|
1548
|
-
}
|
|
1549
|
-
function buildWorkerContactIri(webId, worker) {
|
|
1550
|
-
return contactResource.buildIri(webId, { id: buildWorkerContactId(worker) });
|
|
789
|
+
.replace(/^-|-$/gu, '');
|
|
790
|
+
return `symphony-${suffix || `${backend}-worker`}`;
|
|
1551
791
|
}
|
|
1552
792
|
function buildSymphonyAgents(plan) {
|
|
1553
793
|
const now = safeDate(plan.session.updatedAt);
|
|
@@ -1564,7 +804,7 @@ function buildSymphonyAgents(plan) {
|
|
|
1564
804
|
];
|
|
1565
805
|
const seen = new Set(agents.map((agent) => agent.id));
|
|
1566
806
|
for (const worker of plan.workers) {
|
|
1567
|
-
const id = buildWorkerAgentId(worker.session.backend, worker.session.target.agent
|
|
807
|
+
const id = buildWorkerAgentId(worker.session.backend, worker.session.target.agent);
|
|
1568
808
|
if (seen.has(id)) {
|
|
1569
809
|
continue;
|
|
1570
810
|
}
|
|
@@ -1581,146 +821,6 @@ function buildSymphonyAgents(plan) {
|
|
|
1581
821
|
}
|
|
1582
822
|
return agents;
|
|
1583
823
|
}
|
|
1584
|
-
function buildSymphonyContacts(plan, webId) {
|
|
1585
|
-
const now = safeDate(plan.session.updatedAt);
|
|
1586
|
-
const contacts = [
|
|
1587
|
-
{
|
|
1588
|
-
id: SYMPHONY_CONTACT_ID,
|
|
1589
|
-
name: 'AI Secretary',
|
|
1590
|
-
entity: agentResource.buildIri(webId, { id: SYMPHONY_SECRETARY_AGENT_ID }),
|
|
1591
|
-
rdfType: ContactClass.AGENT,
|
|
1592
|
-
contactType: ContactType.AGENT,
|
|
1593
|
-
createdAt: now,
|
|
1594
|
-
updatedAt: now,
|
|
1595
|
-
},
|
|
1596
|
-
];
|
|
1597
|
-
const seen = new Set(contacts.map((contact) => contact.id));
|
|
1598
|
-
for (const worker of plan.workers) {
|
|
1599
|
-
const id = buildWorkerContactId(worker);
|
|
1600
|
-
if (seen.has(id)) {
|
|
1601
|
-
continue;
|
|
1602
|
-
}
|
|
1603
|
-
seen.add(id);
|
|
1604
|
-
contacts.push({
|
|
1605
|
-
id,
|
|
1606
|
-
name: worker.session.target.label ?? worker.session.target.contact ?? worker.session.target.agent ?? backendDisplayName(worker.session.backend),
|
|
1607
|
-
entity: buildWorkerAgentIri(webId, worker),
|
|
1608
|
-
rdfType: ContactClass.AGENT,
|
|
1609
|
-
contactType: ContactType.AGENT,
|
|
1610
|
-
createdAt: now,
|
|
1611
|
-
updatedAt: now,
|
|
1612
|
-
});
|
|
1613
|
-
}
|
|
1614
|
-
return contacts;
|
|
1615
|
-
}
|
|
1616
|
-
function buildProgressBlock(plan, stage) {
|
|
1617
|
-
const statusByStage = {
|
|
1618
|
-
planned: 'pending',
|
|
1619
|
-
running: 'running',
|
|
1620
|
-
completed: 'done',
|
|
1621
|
-
failed: 'error',
|
|
1622
|
-
};
|
|
1623
|
-
const workerSteps = plan.workers.map((worker, index) => ({
|
|
1624
|
-
id: `${buildSymphonyThreadId(plan)}-worker-${index + 1}`,
|
|
1625
|
-
label: `${worker.session.target.label ?? worker.session.target.agent ?? backendDisplayName(worker.session.backend)} worker`,
|
|
1626
|
-
status: worker.session.status === 'completed'
|
|
1627
|
-
? 'done'
|
|
1628
|
-
: worker.session.status === 'failed'
|
|
1629
|
-
? 'error'
|
|
1630
|
-
: worker.session.status === 'running'
|
|
1631
|
-
? 'running'
|
|
1632
|
-
: statusByStage[stage],
|
|
1633
|
-
detail: worker.session.autoModeSessionId ?? worker.session.uri,
|
|
1634
|
-
}));
|
|
1635
|
-
return {
|
|
1636
|
-
type: 'task_progress',
|
|
1637
|
-
task: plan.task,
|
|
1638
|
-
title: plan.issue.title,
|
|
1639
|
-
steps: [
|
|
1640
|
-
{
|
|
1641
|
-
id: `${buildSymphonyThreadId(plan)}-plan`,
|
|
1642
|
-
label: 'Secretary created task projection',
|
|
1643
|
-
status: stage === 'planned' ? 'running' : 'done',
|
|
1644
|
-
detail: plan.issue.uri,
|
|
1645
|
-
},
|
|
1646
|
-
...workerSteps,
|
|
1647
|
-
{
|
|
1648
|
-
id: `${buildSymphonyThreadId(plan)}-finish`,
|
|
1649
|
-
label: 'Archive Symphony result',
|
|
1650
|
-
status: stage === 'completed' ? 'done' : stage === 'failed' ? 'error' : 'pending',
|
|
1651
|
-
detail: plan.issue.error ?? plan.session.error ?? `${plan.workers.length} worker${plan.workers.length === 1 ? '' : 's'}`,
|
|
1652
|
-
},
|
|
1653
|
-
],
|
|
1654
|
-
currentStep: stage === 'planned' ? 1 : stage === 'running' ? 2 : workerSteps.length + 2,
|
|
1655
|
-
totalSteps: workerSteps.length + 2,
|
|
1656
|
-
};
|
|
1657
|
-
}
|
|
1658
|
-
function buildStatusContent(plan, stage) {
|
|
1659
|
-
if (stage === 'planned') {
|
|
1660
|
-
return `I created a Symphony issue with ${plan.workers.length} worker${plan.workers.length === 1 ? '' : 's'}.\n\n${plan.issue.description ?? plan.issue.title}`;
|
|
1661
|
-
}
|
|
1662
|
-
if (stage === 'running') {
|
|
1663
|
-
const running = plan.workers
|
|
1664
|
-
.filter((worker) => worker.session.status === 'running')
|
|
1665
|
-
.map((worker) => worker.session.target.label ?? worker.session.target.agent ?? backendDisplayName(worker.session.backend));
|
|
1666
|
-
return `Symphony workers are active: ${running.length > 0 ? running.join(', ') : plan.workers.length}.\n\nIssue: ${plan.issue.uri}`;
|
|
1667
|
-
}
|
|
1668
|
-
if (stage === 'completed') {
|
|
1669
|
-
return `Symphony issue completed.\n\nWorkers: ${plan.workers.length}`;
|
|
1670
|
-
}
|
|
1671
|
-
return `Symphony issue failed.\n\n${plan.issue.error ?? plan.session.error ?? plan.delivery.error ?? 'Backend did not complete successfully.'}`;
|
|
1672
|
-
}
|
|
1673
|
-
function buildStatusMessageRow(plan, webId, stage) {
|
|
1674
|
-
const createdAt = stage === 'planned'
|
|
1675
|
-
? safeDate(plan.issue.createdAt)
|
|
1676
|
-
: safeDate(plan.session.updatedAt);
|
|
1677
|
-
const content = buildStatusContent(plan, stage);
|
|
1678
|
-
const secretaryAgent = agentResource.buildIri(webId, { id: SYMPHONY_SECRETARY_AGENT_ID });
|
|
1679
|
-
const routeTargetAgent = agentResource.buildIri(webId, {
|
|
1680
|
-
id: buildWorkerAgentId(plan.session.backend, plan.session.target?.agent, plan.session.target?.contact),
|
|
1681
|
-
});
|
|
1682
|
-
return {
|
|
1683
|
-
id: `${buildSymphonyThreadId(plan)}-${stage}`,
|
|
1684
|
-
chat: selectTargetChatIri(plan.session.target?.chat, webId, plan),
|
|
1685
|
-
thread: selectTargetThreadIri(plan.session.target?.thread, webId, plan),
|
|
1686
|
-
maker: secretaryAgent,
|
|
1687
|
-
role: 'assistant',
|
|
1688
|
-
content,
|
|
1689
|
-
richContent: JSON.stringify({
|
|
1690
|
-
blocks: [buildProgressBlock(plan, stage)],
|
|
1691
|
-
symphony: {
|
|
1692
|
-
stage,
|
|
1693
|
-
issue: plan.issue.uri,
|
|
1694
|
-
task: plan.task,
|
|
1695
|
-
delivery: plan.delivery.uri,
|
|
1696
|
-
session: plan.session.uri,
|
|
1697
|
-
issuer: plan.issue.issuer,
|
|
1698
|
-
workers: plan.workers.map((worker) => ({
|
|
1699
|
-
task: worker.task,
|
|
1700
|
-
title: worker.taskRecord.title,
|
|
1701
|
-
objective: worker.taskRecord.objective,
|
|
1702
|
-
acceptanceCriteria: worker.taskRecord.acceptanceCriteria,
|
|
1703
|
-
taskStatus: worker.taskRecord.status,
|
|
1704
|
-
delivery: worker.delivery.uri,
|
|
1705
|
-
session: worker.session.uri,
|
|
1706
|
-
backend: worker.session.backend,
|
|
1707
|
-
agent: worker.session.target.agent,
|
|
1708
|
-
contact: worker.session.target.contact ?? buildWorkerContactId(worker),
|
|
1709
|
-
status: worker.session.status,
|
|
1710
|
-
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1711
|
-
})),
|
|
1712
|
-
autoModeSessionId: plan.session.autoModeSessionId,
|
|
1713
|
-
},
|
|
1714
|
-
}),
|
|
1715
|
-
status: stage === 'failed' ? 'error' : 'sent',
|
|
1716
|
-
senderName: 'AI Secretary',
|
|
1717
|
-
routedBy: secretaryAgent,
|
|
1718
|
-
routeTargetAgent,
|
|
1719
|
-
coordinationId: plan.session.uri,
|
|
1720
|
-
createdAt,
|
|
1721
|
-
updatedAt: createdAt,
|
|
1722
|
-
};
|
|
1723
|
-
}
|
|
1724
824
|
function auditActionForStage(stage) {
|
|
1725
825
|
if (stage === 'running')
|
|
1726
826
|
return 'symphony.dispatched';
|
|
@@ -1743,13 +843,15 @@ function buildSymphonyReportInboxNotificationRow(webId, worker) {
|
|
|
1743
843
|
const createdAt = safeDate(worker.session.completedAt ?? worker.session.updatedAt);
|
|
1744
844
|
return {
|
|
1745
845
|
id: stableReportInboxNotificationId(worker),
|
|
1746
|
-
actor:
|
|
846
|
+
actor: agentResource.buildIri(webId, {
|
|
847
|
+
id: buildWorkerAgentId(worker.session.backend, worker.session.target.agent),
|
|
848
|
+
}),
|
|
1747
849
|
object: buildSymphonyReportDeliveryIri(webId, worker),
|
|
1748
850
|
createdAt,
|
|
1749
851
|
};
|
|
1750
852
|
}
|
|
1751
853
|
function buildSymphonyAuditRow(plan, webId, stage) {
|
|
1752
|
-
const message =
|
|
854
|
+
const message = buildSharedSymphonyStatusMessageRow(plan, webId, stage);
|
|
1753
855
|
const createdAt = safeDate(message.createdAt);
|
|
1754
856
|
return {
|
|
1755
857
|
id: stableAuditId(plan, stage),
|
|
@@ -1758,7 +860,7 @@ function buildSymphonyAuditRow(plan, webId, stage) {
|
|
|
1758
860
|
actorRole: 'secretary',
|
|
1759
861
|
onBehalfOf: webId,
|
|
1760
862
|
session: buildSymphonyControlSessionUri(webId, plan),
|
|
1761
|
-
entry:
|
|
863
|
+
entry: buildSharedSymphonyMessageIri(webId, plan, message),
|
|
1762
864
|
policyVersion: SYMPHONY_POLICY_VERSION,
|
|
1763
865
|
createdAt,
|
|
1764
866
|
};
|
|
@@ -1795,6 +897,7 @@ async function upsertMessage(db, runtime, row) {
|
|
|
1795
897
|
routedBy: row.routedBy,
|
|
1796
898
|
routeTargetAgent: row.routeTargetAgent,
|
|
1797
899
|
coordinationId: row.coordinationId,
|
|
900
|
+
metadata: row.metadata,
|
|
1798
901
|
updatedAt: row.updatedAt,
|
|
1799
902
|
});
|
|
1800
903
|
}
|
|
@@ -1821,12 +924,12 @@ async function upsertIssue(db, runtime, row) {
|
|
|
1821
924
|
await upsertExactRecord(db, runtime.issueResource, { id: row.id }, row, {
|
|
1822
925
|
title: row.title,
|
|
1823
926
|
description: row.description,
|
|
1824
|
-
document: row.document,
|
|
1825
927
|
status: row.status,
|
|
1826
928
|
priority: row.priority,
|
|
1827
929
|
labels: row.labels,
|
|
1828
930
|
chat: row.chat,
|
|
1829
931
|
thread: row.thread,
|
|
932
|
+
parentIssue: row.parentIssue,
|
|
1830
933
|
tasks: row.tasks,
|
|
1831
934
|
assignedTo: row.assignedTo,
|
|
1832
935
|
updatedAt: row.updatedAt,
|
|
@@ -1836,7 +939,6 @@ async function upsertIssue(db, runtime, row) {
|
|
|
1836
939
|
async function upsertIdea(db, runtime, row) {
|
|
1837
940
|
await upsertExactRecord(db, runtime.ideaResource, { id: row.id }, row, {
|
|
1838
941
|
summary: row.summary,
|
|
1839
|
-
document: row.document,
|
|
1840
942
|
input: row.input,
|
|
1841
943
|
status: row.status,
|
|
1842
944
|
commitment: row.commitment,
|
|
@@ -1871,51 +973,6 @@ async function upsertTask(db, runtime, row) {
|
|
|
1871
973
|
updatedAt: row.updatedAt,
|
|
1872
974
|
});
|
|
1873
975
|
}
|
|
1874
|
-
async function upsertReport(db, runtime, row) {
|
|
1875
|
-
await upsertExactRecord(db, runtime.reportResource, {
|
|
1876
|
-
id: row.id,
|
|
1877
|
-
task: row.task,
|
|
1878
|
-
createdAt: row.createdAt,
|
|
1879
|
-
}, row, {
|
|
1880
|
-
reportKind: row.reportKind,
|
|
1881
|
-
status: row.status,
|
|
1882
|
-
outcome: row.outcome,
|
|
1883
|
-
about: row.about,
|
|
1884
|
-
issue: row.issue,
|
|
1885
|
-
task: row.task,
|
|
1886
|
-
delivery: row.delivery,
|
|
1887
|
-
run: row.run,
|
|
1888
|
-
thread: row.thread,
|
|
1889
|
-
evidence: row.evidence,
|
|
1890
|
-
summary: row.summary,
|
|
1891
|
-
reviewer: row.reviewer,
|
|
1892
|
-
actor: row.actor,
|
|
1893
|
-
source: row.source,
|
|
1894
|
-
metricFacts: row.metricFacts,
|
|
1895
|
-
metadata: row.metadata,
|
|
1896
|
-
publishedAt: row.publishedAt,
|
|
1897
|
-
updatedAt: row.updatedAt,
|
|
1898
|
-
});
|
|
1899
|
-
}
|
|
1900
|
-
async function upsertEvidence(db, runtime, row) {
|
|
1901
|
-
await upsertExactRecord(db, runtime.evidenceResource, {
|
|
1902
|
-
id: row.id,
|
|
1903
|
-
createdAt: row.createdAt,
|
|
1904
|
-
}, row, {
|
|
1905
|
-
evidenceKind: row.evidenceKind,
|
|
1906
|
-
about: row.about,
|
|
1907
|
-
issue: row.issue,
|
|
1908
|
-
task: row.task,
|
|
1909
|
-
delivery: row.delivery,
|
|
1910
|
-
run: row.run,
|
|
1911
|
-
thread: row.thread,
|
|
1912
|
-
summary: row.summary,
|
|
1913
|
-
source: row.source,
|
|
1914
|
-
actor: row.actor,
|
|
1915
|
-
outcome: row.outcome,
|
|
1916
|
-
metadata: row.metadata,
|
|
1917
|
-
});
|
|
1918
|
-
}
|
|
1919
976
|
async function upsertDelivery(db, runtime, row) {
|
|
1920
977
|
await upsertExactRecord(db, runtime.deliveryResource, { id: row.id }, row, {
|
|
1921
978
|
kind: row.kind,
|
|
@@ -1962,6 +1019,31 @@ async function upsertRun(db, runtime, row) {
|
|
|
1962
1019
|
async function insertRunStepOnce(db, runtime, row) {
|
|
1963
1020
|
await insertExactRecordOnce(db, runtime.runStepResource, String(row.id), row);
|
|
1964
1021
|
}
|
|
1022
|
+
async function insertEvidenceOnce(db, runtime, row) {
|
|
1023
|
+
await insertExactRecordOnce(db, runtime.evidenceResource, String(row.id), row);
|
|
1024
|
+
}
|
|
1025
|
+
async function upsertReport(db, runtime, row) {
|
|
1026
|
+
await upsertExactRecord(db, runtime.reportResource, { id: row.id }, row, {
|
|
1027
|
+
reportKind: row.reportKind,
|
|
1028
|
+
status: row.status,
|
|
1029
|
+
outcome: row.outcome,
|
|
1030
|
+
about: row.about,
|
|
1031
|
+
issue: row.issue,
|
|
1032
|
+
task: row.task,
|
|
1033
|
+
delivery: row.delivery,
|
|
1034
|
+
run: row.run,
|
|
1035
|
+
thread: row.thread,
|
|
1036
|
+
evidence: row.evidence,
|
|
1037
|
+
summary: row.summary,
|
|
1038
|
+
reviewer: row.reviewer,
|
|
1039
|
+
actor: row.actor,
|
|
1040
|
+
source: row.source,
|
|
1041
|
+
metricFacts: row.metricFacts,
|
|
1042
|
+
metadata: row.metadata,
|
|
1043
|
+
publishedAt: row.publishedAt,
|
|
1044
|
+
updatedAt: row.updatedAt,
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1965
1047
|
async function upsertAgent(db, runtime, row) {
|
|
1966
1048
|
const target = { id: row.id };
|
|
1967
1049
|
const agentResourceWithId = runtime.agentResource;
|
|
@@ -1981,7 +1063,7 @@ async function upsertAgent(db, runtime, row) {
|
|
|
1981
1063
|
async function upsertContact(db, runtime, row) {
|
|
1982
1064
|
await upsertExactRecord(db, runtime.contactResource, { id: row.id }, row, {
|
|
1983
1065
|
name: row.name,
|
|
1984
|
-
|
|
1066
|
+
about: row.about,
|
|
1985
1067
|
rdfType: row.rdfType,
|
|
1986
1068
|
contactType: row.contactType,
|
|
1987
1069
|
updatedAt: row.updatedAt,
|
|
@@ -2000,7 +1082,7 @@ async function insertInboxNotificationOnce(db, runtime, row) {
|
|
|
2000
1082
|
await insertExactRecordOnce(db, runtime.inboxNotificationResource, String(row.id), row);
|
|
2001
1083
|
}
|
|
2002
1084
|
function collectMessageUris(webId, plan, stages) {
|
|
2003
|
-
return Array.from(new Set(stages.map((stage) =>
|
|
1085
|
+
return Array.from(new Set(stages.map((stage) => buildSharedSymphonyMessageIri(webId, plan, buildSharedSymphonyStatusMessageRow(plan, webId, stage)))));
|
|
2004
1086
|
}
|
|
2005
1087
|
function collectSymphonyProjectionResources(webId, plan, stages) {
|
|
2006
1088
|
const resources = [];
|
|
@@ -2015,13 +1097,16 @@ function collectSymphonyProjectionResources(webId, plan, stages) {
|
|
|
2015
1097
|
};
|
|
2016
1098
|
add('chat', selectTargetChatIri(plan.session.target?.chat, webId, plan));
|
|
2017
1099
|
add('issue', buildSymphonyIssueIri(webId, plan.issue));
|
|
1100
|
+
for (const issue of plan.followUpIssues ?? []) {
|
|
1101
|
+
add('issue', buildSymphonyIssueIri(webId, issue));
|
|
1102
|
+
}
|
|
2018
1103
|
for (const message of collectMessageUris(webId, plan, stages)) {
|
|
2019
1104
|
add('message', message);
|
|
2020
1105
|
}
|
|
2021
1106
|
for (const agent of buildSymphonyAgents(plan)) {
|
|
2022
1107
|
add('agent', agentResource.buildIri(webId, { id: agent.id }));
|
|
2023
1108
|
}
|
|
2024
|
-
for (const contact of
|
|
1109
|
+
for (const contact of buildSharedSymphonyContactRows(plan, webId)) {
|
|
2025
1110
|
add('contact', contactResource.buildIri(webId, { id: contact.id }));
|
|
2026
1111
|
}
|
|
2027
1112
|
for (const worker of plan.workers) {
|
|
@@ -2034,10 +1119,10 @@ function collectSymphonyProjectionResources(webId, plan, stages) {
|
|
|
2034
1119
|
for (const stage of stages) {
|
|
2035
1120
|
add('runStep', buildSymphonyRunStepIri(webId, worker, stage));
|
|
2036
1121
|
}
|
|
1122
|
+
for (const step of worker.runSteps ?? []) {
|
|
1123
|
+
add('runStep', buildSymphonyRuntimeRunStepIri(webId, worker, step));
|
|
1124
|
+
}
|
|
2037
1125
|
if (worker.session.status === 'completed' || worker.session.status === 'failed') {
|
|
2038
|
-
const terminalStage = worker.session.status === 'failed' ? 'failed' : 'completed';
|
|
2039
|
-
add('evidence', buildSymphonyEvidenceIri(webId, worker, terminalStage));
|
|
2040
|
-
add('report', buildSymphonyReportIri(webId, worker));
|
|
2041
1126
|
add('delivery', buildSymphonyReportDeliveryIri(webId, worker));
|
|
2042
1127
|
}
|
|
2043
1128
|
}
|
|
@@ -2074,7 +1159,6 @@ export async function persistSymphonyControlStateToPod(plan, options = {}) {
|
|
|
2074
1159
|
};
|
|
2075
1160
|
const projected = withTargetRefs(normalizedPlan, refs, podSession.webId);
|
|
2076
1161
|
const resources = collectSymphonyProjectionResources(podSession.webId, projected, stages);
|
|
2077
|
-
const latestMessage = buildStatusMessageRow(projected, podSession.webId, stage);
|
|
2078
1162
|
const controlWrite = createLinxPodSyncScope({
|
|
2079
1163
|
source: 'symphony-control-state',
|
|
2080
1164
|
plane: 'control-plane',
|
|
@@ -2110,9 +1194,7 @@ export async function persistSymphonyControlStateToPod(plan, options = {}) {
|
|
|
2110
1194
|
webId: podSession.webId,
|
|
2111
1195
|
stage,
|
|
2112
1196
|
stages,
|
|
2113
|
-
latestMessage,
|
|
2114
1197
|
shouldUpsertChat: !normalizedPlan.session.target?.chat,
|
|
2115
|
-
podSession,
|
|
2116
1198
|
}),
|
|
2117
1199
|
});
|
|
2118
1200
|
return {
|
|
@@ -2186,10 +1268,6 @@ function resolveProjectionResourceModel(runtime, kind) {
|
|
|
2186
1268
|
return runtime.taskResource;
|
|
2187
1269
|
if (kind === 'delivery')
|
|
2188
1270
|
return runtime.deliveryResource;
|
|
2189
|
-
if (kind === 'evidence')
|
|
2190
|
-
return runtime.evidenceResource;
|
|
2191
|
-
if (kind === 'report')
|
|
2192
|
-
return runtime.reportResource;
|
|
2193
1271
|
if (kind === 'run')
|
|
2194
1272
|
return runtime.runResource;
|
|
2195
1273
|
if (kind === 'runStep')
|
|
@@ -2204,6 +1282,14 @@ function resolveProjectionResourceModel(runtime, kind) {
|
|
|
2204
1282
|
return runtime.inboxNotificationResource ?? null;
|
|
2205
1283
|
return null;
|
|
2206
1284
|
}
|
|
1285
|
+
function asRecord(value) {
|
|
1286
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
1287
|
+
? value
|
|
1288
|
+
: null;
|
|
1289
|
+
}
|
|
1290
|
+
function normalizeString(value) {
|
|
1291
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
1292
|
+
}
|
|
2207
1293
|
function serializeOrmRowAsJsonLd(model, iri, row) {
|
|
2208
1294
|
const context = {};
|
|
2209
1295
|
const document = {
|
|
@@ -2327,18 +1413,7 @@ export async function persistSymphonyIdeaToPod(idea, options = {}) {
|
|
|
2327
1413
|
},
|
|
2328
1414
|
},
|
|
2329
1415
|
{
|
|
2330
|
-
id: 'symphony.idea.
|
|
2331
|
-
kind: 'upsert',
|
|
2332
|
-
apply: async () => {
|
|
2333
|
-
await runtime.writePodFile?.(podSession, {
|
|
2334
|
-
path: buildSymphonyIdeaDocumentPath(idea),
|
|
2335
|
-
content: renderSymphonyIdeaMarkdown(idea),
|
|
2336
|
-
contentType: 'text/markdown; charset=utf-8',
|
|
2337
|
-
});
|
|
2338
|
-
},
|
|
2339
|
-
},
|
|
2340
|
-
{
|
|
2341
|
-
id: 'symphony.idea.upsert-meta',
|
|
1416
|
+
id: 'symphony.idea.upsert',
|
|
2342
1417
|
kind: 'upsert',
|
|
2343
1418
|
apply: () => upsertIdea(db, runtime, buildSymphonyIdeaRow(idea, podSession.webId)),
|
|
2344
1419
|
},
|
|
@@ -2354,13 +1429,10 @@ export async function listOpenSymphonyIssuesFromPod(options = {}) {
|
|
|
2354
1429
|
}
|
|
2355
1430
|
const db = runtime.createDb(podSession);
|
|
2356
1431
|
try {
|
|
2357
|
-
await
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
.filter((issue) => issue !== null)
|
|
2362
|
-
.filter((issue) => !isClosedIssueStatus(issue.status))
|
|
2363
|
-
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
1432
|
+
return await listOpenSymphonyIssuesFromControlState({
|
|
1433
|
+
db,
|
|
1434
|
+
webId: podSession.webId,
|
|
1435
|
+
});
|
|
2364
1436
|
}
|
|
2365
1437
|
catch {
|
|
2366
1438
|
return null;
|
|
@@ -2374,13 +1446,7 @@ export async function listRunningSymphonyWorkersFromPod(options = {}) {
|
|
|
2374
1446
|
}
|
|
2375
1447
|
const db = runtime.createDb(podSession);
|
|
2376
1448
|
try {
|
|
2377
|
-
await
|
|
2378
|
-
const rows = await db.select().from(runtime.sessionResource).execute();
|
|
2379
|
-
return rows
|
|
2380
|
-
.filter(isSymphonySessionRow)
|
|
2381
|
-
.flatMap((row) => extractRunningSymphonyWorkersFromSession(row))
|
|
2382
|
-
.sort(compareWorkerStatusUpdatedAt)
|
|
2383
|
-
.map(({ updatedAt: _updatedAt, ...worker }) => worker);
|
|
1449
|
+
return await listRunningSymphonyWorkersFromControlState({ db });
|
|
2384
1450
|
}
|
|
2385
1451
|
catch {
|
|
2386
1452
|
return null;
|
|
@@ -2394,217 +1460,22 @@ export async function listRecentSymphonyReportsFromPod(options = {}) {
|
|
|
2394
1460
|
}
|
|
2395
1461
|
const db = runtime.createDb(podSession);
|
|
2396
1462
|
try {
|
|
2397
|
-
await
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
.filter((report) => report !== null)
|
|
2402
|
-
.sort((left, right) => right.sortAt - left.sortAt)
|
|
2403
|
-
.slice(0, options.limit ?? 5)
|
|
2404
|
-
.map(({ sortAt: _sortAt, ...report }) => report);
|
|
1463
|
+
return await listRecentSymphonyReportsFromControlState({
|
|
1464
|
+
db,
|
|
1465
|
+
limit: options.limit ?? 5,
|
|
1466
|
+
});
|
|
2405
1467
|
}
|
|
2406
1468
|
catch {
|
|
2407
1469
|
return null;
|
|
2408
1470
|
}
|
|
2409
1471
|
}
|
|
2410
|
-
function issueRowToSymphonyIssueRecord(row, webId) {
|
|
2411
|
-
const record = asRecord(row);
|
|
2412
|
-
const id = normalizeString(record?.id);
|
|
2413
|
-
const title = normalizeString(record?.title);
|
|
2414
|
-
if (!record || !id || !title) {
|
|
2415
|
-
return null;
|
|
2416
|
-
}
|
|
2417
|
-
const status = normalizeIssueStatus(record.status);
|
|
2418
|
-
const priority = normalizeIssuePriority(record.priority);
|
|
2419
|
-
const tasks = Array.isArray(record.tasks)
|
|
2420
|
-
? record.tasks.map((item) => normalizeString(item)).filter((item) => Boolean(item))
|
|
2421
|
-
: [];
|
|
2422
|
-
const createdAt = toIsoDate(record.createdAt);
|
|
2423
|
-
const updatedAt = toIsoDate(record.updatedAt) ?? createdAt;
|
|
2424
|
-
return {
|
|
2425
|
-
uri: symphonyIssueUriFromResourceId(id),
|
|
2426
|
-
title,
|
|
2427
|
-
description: normalizeString(record.description),
|
|
2428
|
-
status,
|
|
2429
|
-
priority,
|
|
2430
|
-
source: 'cli',
|
|
2431
|
-
issuer: {
|
|
2432
|
-
source: 'user',
|
|
2433
|
-
webId: normalizeString(record.createdBy) ?? webId,
|
|
2434
|
-
...(normalizeString(record.chat) ? { chat: normalizeString(record.chat) } : {}),
|
|
2435
|
-
...(normalizeString(record.thread) ? { thread: normalizeString(record.thread) } : {}),
|
|
2436
|
-
},
|
|
2437
|
-
tasks,
|
|
2438
|
-
deliveries: [],
|
|
2439
|
-
sessions: [],
|
|
2440
|
-
...(normalizeString(record.chat) ? { chat: normalizeString(record.chat) } : {}),
|
|
2441
|
-
...(normalizeString(record.thread) ? { thread: normalizeString(record.thread) } : {}),
|
|
2442
|
-
createdAt,
|
|
2443
|
-
updatedAt,
|
|
2444
|
-
...(record.closedAt ? { closedAt: toIsoDate(record.closedAt) ?? updatedAt } : {}),
|
|
2445
|
-
};
|
|
2446
|
-
}
|
|
2447
|
-
function symphonyIssueUriFromResourceId(id) {
|
|
2448
|
-
const normalized = resolvePodResourceTemplateValue(issueResource, id) ?? id;
|
|
2449
|
-
return `urn:undefineds:linx:issue:${normalized}`;
|
|
2450
|
-
}
|
|
2451
|
-
function normalizeIssueStatus(value) {
|
|
2452
|
-
const normalized = normalizeString(value);
|
|
2453
|
-
if (normalized === 'open'
|
|
2454
|
-
|| normalized === 'triaging'
|
|
2455
|
-
|| normalized === 'in_progress'
|
|
2456
|
-
|| normalized === 'blocked'
|
|
2457
|
-
|| normalized === 'resolved'
|
|
2458
|
-
|| normalized === 'closed') {
|
|
2459
|
-
return normalized;
|
|
2460
|
-
}
|
|
2461
|
-
return 'open';
|
|
2462
|
-
}
|
|
2463
|
-
function normalizeIssuePriority(value) {
|
|
2464
|
-
const normalized = normalizeString(value);
|
|
2465
|
-
if (normalized === 'low' || normalized === 'medium' || normalized === 'high' || normalized === 'urgent') {
|
|
2466
|
-
return normalized;
|
|
2467
|
-
}
|
|
2468
|
-
return 'medium';
|
|
2469
|
-
}
|
|
2470
|
-
function isClosedIssueStatus(status) {
|
|
2471
|
-
return status === 'closed' || status === 'resolved';
|
|
2472
|
-
}
|
|
2473
|
-
function isSymphonySessionRow(row) {
|
|
2474
|
-
if (!row || typeof row !== 'object') {
|
|
2475
|
-
return false;
|
|
2476
|
-
}
|
|
2477
|
-
const record = row;
|
|
2478
|
-
const metadata = asRecord(record.metadata);
|
|
2479
|
-
return metadata?.kind === 'symphony-run'
|
|
2480
|
-
|| record.policyVersion === SYMPHONY_POLICY_VERSION
|
|
2481
|
-
|| (typeof record.tool === 'string' && record.tool.startsWith('symphony:'));
|
|
2482
|
-
}
|
|
2483
|
-
function extractRunningSymphonyWorkersFromSession(row) {
|
|
2484
|
-
const metadata = asRecord(row.metadata) ?? {};
|
|
2485
|
-
const sessionStatus = normalizePodSymphonySessionStatus(metadata.status ?? row.status);
|
|
2486
|
-
const workers = Array.isArray(metadata.workers) ? metadata.workers : [];
|
|
2487
|
-
const updatedAt = safeOptionalDate(row.updatedAt);
|
|
2488
|
-
if (workers.length === 0) {
|
|
2489
|
-
if (sessionStatus !== 'running') {
|
|
2490
|
-
return [];
|
|
2491
|
-
}
|
|
2492
|
-
return [{
|
|
2493
|
-
status: sessionStatus,
|
|
2494
|
-
backend: normalizeString(metadata.backend) ?? parseBackendFromTool(row.tool) ?? 'unknown',
|
|
2495
|
-
mode: normalizeString(metadata.mode) ?? 'auto',
|
|
2496
|
-
cwd: normalizeString(metadata.workspacePath),
|
|
2497
|
-
autoModeSessionId: normalizeString(metadata.autoModeSessionId),
|
|
2498
|
-
target: normalizeSymphonyWorkerTarget(asRecord(metadata.target)),
|
|
2499
|
-
updatedAt,
|
|
2500
|
-
}];
|
|
2501
|
-
}
|
|
2502
|
-
return workers
|
|
2503
|
-
.map((item) => asRecord(item))
|
|
2504
|
-
.filter((item) => item !== null)
|
|
2505
|
-
.map((worker) => ({
|
|
2506
|
-
status: normalizePodSymphonySessionStatus(worker.status ?? worker.taskStatus ?? sessionStatus),
|
|
2507
|
-
backend: normalizeString(worker.backend) ?? normalizeString(metadata.backend) ?? parseBackendFromTool(row.tool) ?? 'unknown',
|
|
2508
|
-
mode: normalizeString(worker.mode) ?? normalizeString(metadata.mode) ?? 'auto',
|
|
2509
|
-
cwd: normalizeString(worker.workspacePath) ?? normalizeString(metadata.workspacePath),
|
|
2510
|
-
autoModeSessionId: normalizeString(worker.autoModeSessionId) ?? normalizeString(metadata.autoModeSessionId),
|
|
2511
|
-
target: normalizeSymphonyWorkerTarget(asRecord(worker.target), worker, asRecord(metadata.target)),
|
|
2512
|
-
updatedAt,
|
|
2513
|
-
}))
|
|
2514
|
-
.filter((worker) => worker.status === 'running');
|
|
2515
|
-
}
|
|
2516
|
-
function deliveryRowToSymphonyReportStatus(row) {
|
|
2517
|
-
const record = asRecord(row);
|
|
2518
|
-
if (!record) {
|
|
2519
|
-
return null;
|
|
2520
|
-
}
|
|
2521
|
-
const metadata = asRecord(record.metadata);
|
|
2522
|
-
const payload = asRecord(record.payload);
|
|
2523
|
-
if (record.kind !== 'report' && metadata?.reportKind !== 'worker-completion' && payload?.kind !== 'symphony_report') {
|
|
2524
|
-
return null;
|
|
2525
|
-
}
|
|
2526
|
-
const completedAt = safeOptionalDate(record.completedAt);
|
|
2527
|
-
const updatedAt = safeOptionalDate(record.updatedAt);
|
|
2528
|
-
const createdAt = safeOptionalDate(record.createdAt);
|
|
2529
|
-
const sortAt = completedAt?.getTime() ?? updatedAt?.getTime() ?? createdAt?.getTime() ?? 0;
|
|
2530
|
-
const agent = normalizeString(payload?.agent);
|
|
2531
|
-
const title = normalizeString(record.objective);
|
|
2532
|
-
const summary = normalizeString(payload?.summary);
|
|
2533
|
-
const task = normalizeString(record.task);
|
|
2534
|
-
const archive = asRecord(metadata?.archive);
|
|
2535
|
-
const delivery = normalizeString(payload?.delivery) ?? normalizeString(archive?.delivery);
|
|
2536
|
-
const reportDelivery = normalizeString(payload?.reportDelivery) ?? normalizeString(record.id);
|
|
2537
|
-
const run = normalizeString(payload?.run) ?? normalizeString(record.object);
|
|
2538
|
-
const chat = normalizeString(record.chat);
|
|
2539
|
-
const thread = normalizeString(record.thread);
|
|
2540
|
-
const autoModeSessionId = normalizeString(payload?.autoModeSessionId);
|
|
2541
|
-
const error = normalizeString(payload?.error) ?? normalizeString(record.error);
|
|
2542
|
-
return {
|
|
2543
|
-
status: normalizeString(payload?.outcome) ?? normalizeString(record.status) ?? 'completed',
|
|
2544
|
-
backend: normalizeString(payload?.backend) ?? 'unknown',
|
|
2545
|
-
...(agent ? { agent } : {}),
|
|
2546
|
-
...(title ? { title } : {}),
|
|
2547
|
-
...(summary ? { summary } : {}),
|
|
2548
|
-
...(task ? { task } : {}),
|
|
2549
|
-
...(delivery ? { delivery } : {}),
|
|
2550
|
-
...(reportDelivery ? { reportDelivery } : {}),
|
|
2551
|
-
...(run ? { run } : {}),
|
|
2552
|
-
...(chat ? { chat } : {}),
|
|
2553
|
-
...(thread ? { thread } : {}),
|
|
2554
|
-
...(autoModeSessionId ? { autoModeSessionId } : {}),
|
|
2555
|
-
...(error ? { error } : {}),
|
|
2556
|
-
...(completedAt ? { completedAt: completedAt.toISOString() } : {}),
|
|
2557
|
-
...(updatedAt ? { updatedAt: updatedAt.toISOString() } : {}),
|
|
2558
|
-
sortAt,
|
|
2559
|
-
};
|
|
2560
|
-
}
|
|
2561
|
-
function normalizeSymphonyWorkerTarget(target, worker = {}, fallback = null) {
|
|
2562
|
-
const normalized = {
|
|
2563
|
-
label: normalizeString(target?.label) ?? normalizeString(worker.title) ?? normalizeString(fallback?.label),
|
|
2564
|
-
agent: normalizeString(target?.agent) ?? normalizeString(worker.agent) ?? normalizeString(fallback?.agent),
|
|
2565
|
-
chat: normalizeString(target?.chat) ?? normalizeString(worker.chat) ?? normalizeString(fallback?.chat),
|
|
2566
|
-
};
|
|
2567
|
-
return Object.values(normalized).some(Boolean) ? normalized : undefined;
|
|
2568
|
-
}
|
|
2569
|
-
function compareWorkerStatusUpdatedAt(left, right) {
|
|
2570
|
-
return (right.updatedAt?.getTime() ?? 0) - (left.updatedAt?.getTime() ?? 0);
|
|
2571
|
-
}
|
|
2572
|
-
function normalizePodSymphonySessionStatus(value) {
|
|
2573
|
-
const normalized = normalizeString(value);
|
|
2574
|
-
if (normalized === 'active')
|
|
2575
|
-
return 'running';
|
|
2576
|
-
if (normalized === 'error')
|
|
2577
|
-
return 'failed';
|
|
2578
|
-
if (normalized === 'queued')
|
|
2579
|
-
return 'planned';
|
|
2580
|
-
return normalized ?? 'planned';
|
|
2581
|
-
}
|
|
2582
|
-
function parseBackendFromTool(value) {
|
|
2583
|
-
const tool = normalizeString(value);
|
|
2584
|
-
if (!tool?.startsWith('symphony:')) {
|
|
2585
|
-
return undefined;
|
|
2586
|
-
}
|
|
2587
|
-
return tool.slice('symphony:'.length) || undefined;
|
|
2588
|
-
}
|
|
2589
|
-
function asRecord(value) {
|
|
2590
|
-
return value && typeof value === 'object' && !Array.isArray(value)
|
|
2591
|
-
? value
|
|
2592
|
-
: null;
|
|
2593
|
-
}
|
|
2594
|
-
function normalizeString(value) {
|
|
2595
|
-
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
2596
|
-
}
|
|
2597
|
-
function safeOptionalDate(value) {
|
|
2598
|
-
if (!value) {
|
|
2599
|
-
return undefined;
|
|
2600
|
-
}
|
|
2601
|
-
const date = value instanceof Date ? value : new Date(String(value));
|
|
2602
|
-
return Number.isFinite(date.getTime()) ? date : undefined;
|
|
2603
|
-
}
|
|
2604
|
-
function toIsoDate(value) {
|
|
2605
|
-
return (safeOptionalDate(value) ?? new Date()).toISOString();
|
|
2606
|
-
}
|
|
2607
1472
|
function buildSymphonyProjectionOperations(input) {
|
|
1473
|
+
const controlRows = buildSymphonyControlRows({
|
|
1474
|
+
plan: input.plan,
|
|
1475
|
+
webId: input.webId,
|
|
1476
|
+
stage: input.stage,
|
|
1477
|
+
stages: input.stages,
|
|
1478
|
+
});
|
|
2608
1479
|
return [
|
|
2609
1480
|
{
|
|
2610
1481
|
id: 'symphony.prepare-resources',
|
|
@@ -2618,10 +1489,10 @@ function buildSymphonyProjectionOperations(input) {
|
|
|
2618
1489
|
input.runtime.issueResource,
|
|
2619
1490
|
input.runtime.taskResource,
|
|
2620
1491
|
input.runtime.deliveryResource,
|
|
2621
|
-
input.runtime.evidenceResource,
|
|
2622
|
-
input.runtime.reportResource,
|
|
2623
1492
|
input.runtime.runResource,
|
|
2624
1493
|
input.runtime.runStepResource,
|
|
1494
|
+
input.runtime.evidenceResource,
|
|
1495
|
+
input.runtime.reportResource,
|
|
2625
1496
|
input.runtime.agentResource,
|
|
2626
1497
|
input.runtime.contactResource,
|
|
2627
1498
|
input.runtime.auditResource,
|
|
@@ -2630,82 +1501,84 @@ function buildSymphonyProjectionOperations(input) {
|
|
|
2630
1501
|
},
|
|
2631
1502
|
},
|
|
2632
1503
|
{
|
|
2633
|
-
id: 'symphony.
|
|
1504
|
+
id: 'symphony.upsert-issue',
|
|
2634
1505
|
kind: 'upsert',
|
|
2635
|
-
apply:
|
|
2636
|
-
await input.runtime.writePodFile?.(input.podSession, {
|
|
2637
|
-
path: buildSymphonyIssueDocumentPath(input.plan.issue),
|
|
2638
|
-
content: renderSymphonyIssueMarkdown(input.plan),
|
|
2639
|
-
contentType: 'text/markdown; charset=utf-8',
|
|
2640
|
-
});
|
|
2641
|
-
},
|
|
1506
|
+
apply: () => upsertIssue(input.db, input.runtime, controlRows.issue),
|
|
2642
1507
|
},
|
|
2643
|
-
{
|
|
2644
|
-
id:
|
|
1508
|
+
...controlRows.issues.slice(1).map((row, index) => ({
|
|
1509
|
+
id: `symphony.upsert-follow-up-issue:${index + 1}`,
|
|
2645
1510
|
kind: 'upsert',
|
|
2646
|
-
apply: () => upsertIssue(input.db, input.runtime,
|
|
2647
|
-
},
|
|
2648
|
-
...
|
|
2649
|
-
{
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
{
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
{
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
1511
|
+
apply: () => upsertIssue(input.db, input.runtime, row),
|
|
1512
|
+
})),
|
|
1513
|
+
...controlRows.tasks.map((row, index) => ({
|
|
1514
|
+
id: `symphony.upsert-task:${index + 1}`,
|
|
1515
|
+
kind: 'upsert',
|
|
1516
|
+
apply: () => upsertTask(input.db, input.runtime, row),
|
|
1517
|
+
})),
|
|
1518
|
+
...controlRows.deliveries.map((row, index) => ({
|
|
1519
|
+
id: `symphony.upsert-delivery:${index + 1}`,
|
|
1520
|
+
kind: 'upsert',
|
|
1521
|
+
apply: () => upsertDelivery(input.db, input.runtime, row),
|
|
1522
|
+
})),
|
|
1523
|
+
...controlRows.runs.map((row, index) => ({
|
|
1524
|
+
id: `symphony.upsert-run:${index + 1}`,
|
|
1525
|
+
kind: 'upsert',
|
|
1526
|
+
apply: () => upsertRun(input.db, input.runtime, row),
|
|
1527
|
+
})),
|
|
1528
|
+
...controlRows.runSteps.map((row, index) => ({
|
|
1529
|
+
id: `symphony.insert-run-step:${index + 1}`,
|
|
1530
|
+
kind: 'insert',
|
|
1531
|
+
apply: () => insertRunStepOnce(input.db, input.runtime, row),
|
|
1532
|
+
})),
|
|
1533
|
+
...controlRows.evidence.map((row, index) => ({
|
|
1534
|
+
id: `symphony.insert-evidence:${index + 1}`,
|
|
1535
|
+
kind: 'insert',
|
|
1536
|
+
apply: () => insertEvidenceOnce(input.db, input.runtime, row),
|
|
1537
|
+
})),
|
|
1538
|
+
...controlRows.reports.map((row, index) => ({
|
|
1539
|
+
id: `symphony.upsert-report:${index + 1}`,
|
|
1540
|
+
kind: 'upsert',
|
|
1541
|
+
apply: () => upsertReport(input.db, input.runtime, row),
|
|
1542
|
+
})),
|
|
2670
1543
|
...buildSymphonyReportOperations(input),
|
|
2671
1544
|
...buildSymphonyAgents(input.plan).map((agent) => ({
|
|
2672
1545
|
id: `symphony.upsert-agent:${agent.id}`,
|
|
2673
1546
|
kind: 'upsert',
|
|
2674
1547
|
apply: () => upsertAgent(input.db, input.runtime, agent),
|
|
2675
1548
|
})),
|
|
2676
|
-
...
|
|
1549
|
+
...controlRows.contacts.map((contact) => ({
|
|
2677
1550
|
id: `symphony.upsert-contact:${contact.id}`,
|
|
2678
1551
|
kind: 'upsert',
|
|
2679
1552
|
apply: () => upsertContact(input.db, input.runtime, contact),
|
|
2680
1553
|
})),
|
|
2681
|
-
{
|
|
1554
|
+
...controlRows.chats.map((row, index) => ({
|
|
2682
1555
|
id: 'symphony.upsert-chat',
|
|
2683
1556
|
kind: 'upsert',
|
|
2684
1557
|
shouldRun: () => input.shouldUpsertChat,
|
|
2685
|
-
apply: () => upsertChat(input.db, input.runtime,
|
|
2686
|
-
},
|
|
2687
|
-
...
|
|
1558
|
+
apply: () => upsertChat(input.db, input.runtime, row),
|
|
1559
|
+
})),
|
|
1560
|
+
...controlRows.threads.map((row, index) => ({
|
|
2688
1561
|
id: `symphony.upsert-thread:${index + 1}`,
|
|
2689
1562
|
kind: 'upsert',
|
|
2690
1563
|
apply: () => upsertThread(input.db, input.runtime, row),
|
|
2691
1564
|
})),
|
|
2692
|
-
...
|
|
1565
|
+
...controlRows.sessions.map((row, index) => ({
|
|
2693
1566
|
id: `symphony.upsert-session:${index + 1}`,
|
|
2694
1567
|
kind: 'upsert',
|
|
2695
|
-
apply: () => upsertSession(input.db, input.runtime,
|
|
1568
|
+
apply: () => upsertSession(input.db, input.runtime, row),
|
|
2696
1569
|
})),
|
|
2697
|
-
...
|
|
1570
|
+
...controlRows.messages.flatMap((row, index) => [
|
|
2698
1571
|
{
|
|
2699
|
-
id: `symphony.upsert-message:${
|
|
1572
|
+
id: `symphony.upsert-message:${input.stages[index] ?? index + 1}`,
|
|
2700
1573
|
kind: 'upsert',
|
|
2701
|
-
apply: () => upsertMessage(input.db, input.runtime,
|
|
2702
|
-
},
|
|
2703
|
-
{
|
|
2704
|
-
id: `symphony.insert-audit:${stage}`,
|
|
2705
|
-
kind: 'insert',
|
|
2706
|
-
apply: () => insertAuditOnce(input.db, input.runtime, buildSymphonyAuditRow(input.plan, input.webId, stage)),
|
|
1574
|
+
apply: () => upsertMessage(input.db, input.runtime, row),
|
|
2707
1575
|
},
|
|
2708
1576
|
]),
|
|
1577
|
+
...input.stages.map((stage) => ({
|
|
1578
|
+
id: `symphony.insert-audit:${stage}`,
|
|
1579
|
+
kind: 'insert',
|
|
1580
|
+
apply: () => insertAuditOnce(input.db, input.runtime, buildSymphonyAuditRow(input.plan, input.webId, stage)),
|
|
1581
|
+
})),
|
|
2709
1582
|
];
|
|
2710
1583
|
}
|
|
2711
1584
|
function buildSymphonyReportOperations(input) {
|
|
@@ -2714,38 +1587,6 @@ function buildSymphonyReportOperations(input) {
|
|
|
2714
1587
|
}
|
|
2715
1588
|
const terminalStage = input.stage;
|
|
2716
1589
|
return input.plan.workers.flatMap((worker, index) => [
|
|
2717
|
-
{
|
|
2718
|
-
id: `symphony.write-evidence-file:${index + 1}`,
|
|
2719
|
-
kind: 'upsert',
|
|
2720
|
-
apply: async () => {
|
|
2721
|
-
await input.runtime.writePodFile?.(input.podSession, {
|
|
2722
|
-
path: buildSymphonyEvidenceDocumentPath(worker, terminalStage),
|
|
2723
|
-
content: renderSymphonyEvidenceMarkdown(input.plan, input.webId, worker, terminalStage),
|
|
2724
|
-
contentType: 'text/markdown; charset=utf-8',
|
|
2725
|
-
});
|
|
2726
|
-
},
|
|
2727
|
-
},
|
|
2728
|
-
{
|
|
2729
|
-
id: `symphony.upsert-evidence:${index + 1}`,
|
|
2730
|
-
kind: 'upsert',
|
|
2731
|
-
apply: () => upsertEvidence(input.db, input.runtime, buildSymphonyEvidenceRow(input.plan, input.webId, worker, terminalStage)),
|
|
2732
|
-
},
|
|
2733
|
-
{
|
|
2734
|
-
id: `symphony.write-report-file:${index + 1}`,
|
|
2735
|
-
kind: 'upsert',
|
|
2736
|
-
apply: async () => {
|
|
2737
|
-
await input.runtime.writePodFile?.(input.podSession, {
|
|
2738
|
-
path: buildSymphonyReportDocumentPath(worker),
|
|
2739
|
-
content: renderSymphonyReportMarkdown(input.plan, worker, terminalStage),
|
|
2740
|
-
contentType: 'text/markdown; charset=utf-8',
|
|
2741
|
-
});
|
|
2742
|
-
},
|
|
2743
|
-
},
|
|
2744
|
-
{
|
|
2745
|
-
id: `symphony.upsert-report:${index + 1}`,
|
|
2746
|
-
kind: 'upsert',
|
|
2747
|
-
apply: () => upsertReport(input.db, input.runtime, buildSymphonyReportRow(input.plan, input.webId, worker, terminalStage)),
|
|
2748
|
-
},
|
|
2749
1590
|
{
|
|
2750
1591
|
id: `symphony.upsert-report-delivery:${index + 1}`,
|
|
2751
1592
|
kind: 'upsert',
|
|
@@ -2771,21 +1612,21 @@ export const __symphonyPodProjectionInternal = {
|
|
|
2771
1612
|
buildSymphonyMessageUri,
|
|
2772
1613
|
auditActionForStage,
|
|
2773
1614
|
buildSymphonyAuditRow,
|
|
2774
|
-
buildStatusMessageRow,
|
|
2775
|
-
buildSymphonyChatRow,
|
|
2776
|
-
buildSymphonyThreadRow,
|
|
2777
|
-
buildSymphonySessionRow,
|
|
2778
|
-
buildSymphonyIssueRow,
|
|
1615
|
+
buildStatusMessageRow: buildSharedSymphonyStatusMessageRow,
|
|
1616
|
+
buildSymphonyChatRow: buildSharedSymphonyChatRow,
|
|
1617
|
+
buildSymphonyThreadRow: buildSharedSymphonyThreadRow,
|
|
1618
|
+
buildSymphonySessionRow: buildSharedSymphonySessionRow,
|
|
1619
|
+
buildSymphonyIssueRow: buildSharedSymphonyIssueRow,
|
|
2779
1620
|
buildSymphonyIdeaRow,
|
|
2780
|
-
buildSymphonyTaskRow,
|
|
2781
|
-
buildSymphonyDeliveryRow,
|
|
2782
|
-
buildSymphonyEvidenceRow,
|
|
1621
|
+
buildSymphonyTaskRow: buildSharedSymphonyTaskRow,
|
|
1622
|
+
buildSymphonyDeliveryRow: buildSharedSymphonyDeliveryRow,
|
|
2783
1623
|
buildSymphonyReportDeliveryRow,
|
|
2784
1624
|
buildSymphonyReportInboxNotificationRow,
|
|
2785
|
-
buildSymphonyRunRow,
|
|
2786
|
-
buildSymphonyRunStepRow,
|
|
1625
|
+
buildSymphonyRunRow: buildSharedSymphonyRunRow,
|
|
1626
|
+
buildSymphonyRunStepRow: buildSharedSymphonyRunStepRow,
|
|
1627
|
+
buildSymphonyRuntimeRunStepRow: buildSharedSymphonyRuntimeRunStepRow,
|
|
2787
1628
|
buildSymphonyAgents,
|
|
2788
|
-
buildSymphonyContacts,
|
|
1629
|
+
buildSymphonyContacts: buildSharedSymphonyContactRows,
|
|
2789
1630
|
withChatThreadRefs,
|
|
2790
1631
|
normalizeSymphonyRunPlan,
|
|
2791
1632
|
};
|