@undefineds.co/linx 0.3.20 → 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 +53 -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/linx-cloud-errors.js +0 -5
- package/dist/lib/linx-cloud-errors.js.map +1 -1
- package/dist/lib/linx-status-line.js +1 -8
- package/dist/lib/linx-status-line.js.map +1 -1
- 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/branding.js +34 -103
- package/dist/lib/pi-adapter/branding.js.map +1 -1
- package/dist/lib/pi-adapter/interactive.js +35 -49
- package/dist/lib/pi-adapter/interactive.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror.js +38 -107
- package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
- package/dist/lib/pi-adapter/pod-native.js +2 -0
- package/dist/lib/pi-adapter/pod-native.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/runtime.js +2 -12
- package/dist/lib/pi-adapter/runtime.js.map +1 -1
- 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/status-line-command.js +2 -2
- package/dist/lib/status-line-command.js.map +1 -1
- package/dist/lib/symphony/archive.js +15 -37
- package/dist/lib/symphony/archive.js.map +1 -1
- package/dist/lib/symphony/pod-projection.js +189 -1346
- package/dist/lib/symphony/pod-projection.js.map +1 -1
- package/dist/lib/symphony-command.js +209 -109
- 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 +7 -0
- package/dist/skills/xpod-cli/SKILL.md +2 -13
- 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 -1
- package/vendor/agent-runtime/dist/index.js +4 -1
- 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.d.ts +0 -11
- package/vendor/agent-runtime/dist/reconciler.js +5 -43
- package/vendor/agent-runtime/dist/symphony.d.ts +272 -27
- package/vendor/agent-runtime/dist/symphony.js +1268 -21
- 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
- package/dist/lib/capture/persistence.js +0 -377
- package/dist/lib/capture/persistence.js.map +0 -1
- package/dist/lib/capture/tool.js +0 -242
- package/dist/lib/capture/tool.js.map +0 -1
- package/dist/skills/basic/SKILL.md +0 -46
- package/dist/skills/capture/SKILL.md +0 -165
- package/vendor/agent-runtime/dist/coordination.d.ts +0 -93
- package/vendor/agent-runtime/dist/coordination.js +0 -145
|
@@ -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,147 +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
|
-
status,
|
|
1030
|
-
tool: `symphony:${worker.session.backend}`,
|
|
1031
|
-
tokenUsage: 0,
|
|
1032
|
-
messages: worker.session.messages,
|
|
1033
|
-
policyVersion: SYMPHONY_POLICY_VERSION,
|
|
1034
|
-
metadata: {
|
|
1035
|
-
kind: 'symphony-run',
|
|
1036
|
-
surface: 'symphony',
|
|
1037
|
-
issue: plan.issue.uri,
|
|
1038
|
-
task: worker.task,
|
|
1039
|
-
delivery: worker.delivery.uri,
|
|
1040
|
-
session: worker.session.uri,
|
|
1041
|
-
issuer: plan.issue.issuer,
|
|
1042
|
-
worker: workerSummary,
|
|
1043
|
-
workers: [workerSummary],
|
|
1044
|
-
backend: worker.session.backend,
|
|
1045
|
-
mode: worker.session.mode,
|
|
1046
|
-
model: worker.session.model,
|
|
1047
|
-
workspacePath: worker.session.cwd,
|
|
1048
|
-
workspace: buildSymphonyWorkspaceMetadata(plan, worker),
|
|
1049
|
-
reconciler: buildSymphonyReconcilerMetadata(worker),
|
|
1050
|
-
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1051
|
-
exitCode: worker.session.exitCode,
|
|
1052
|
-
dryRun: worker.session.dryRun,
|
|
1053
|
-
error: worker.session.error ?? worker.delivery.error ?? plan.issue.error,
|
|
1054
|
-
target: worker.session.target,
|
|
1055
|
-
},
|
|
1056
|
-
createdAt,
|
|
1057
|
-
updatedAt,
|
|
1058
|
-
...(status === 'completed' || status === 'error' ? { archivedAt: updatedAt } : {}),
|
|
1059
|
-
};
|
|
1060
|
-
}
|
|
1061
|
-
function buildSymphonyIssueRow(plan, webId) {
|
|
1062
|
-
const createdAt = safeDate(plan.issue.createdAt);
|
|
1063
|
-
const updatedAt = safeDate(plan.issue.updatedAt);
|
|
1064
|
-
const secretaryContact = buildSecretaryContactIri(webId);
|
|
1065
|
-
return {
|
|
1066
|
-
id: buildSymphonyIssueId(plan.issue),
|
|
1067
|
-
// File-primary: title remains a compact index label for existing Issue schemas.
|
|
1068
|
-
// The full problem statement and acceptance narrative live in issue.md.
|
|
1069
|
-
title: plan.issue.title,
|
|
1070
|
-
document: podFileUrlFromWebId(webId, buildSymphonyIssueDocumentPath(plan.issue)),
|
|
1071
|
-
description: undefined,
|
|
1072
|
-
status: plan.issue.status,
|
|
1073
|
-
priority: plan.issue.priority,
|
|
1074
|
-
labels: ['symphony'],
|
|
1075
|
-
chat: selectTargetChatIri(plan.session.target?.chat, webId, plan),
|
|
1076
|
-
thread: selectTargetThreadIri(plan.session.target?.thread, webId, plan),
|
|
1077
|
-
tasks: Array.from(new Set((plan.issue.tasks?.length ? plan.issue.tasks : plan.workers.map((worker) => worker.task))
|
|
1078
|
-
.map((task) => normalizeSymphonyTaskIri(webId, task)))),
|
|
1079
|
-
createdBy: plan.issue.issuer.webId ?? webId,
|
|
1080
|
-
assignedTo: secretaryContact,
|
|
1081
|
-
createdAt,
|
|
1082
|
-
updatedAt,
|
|
1083
|
-
...(plan.issue.closedAt ? { closedAt: safeDate(plan.issue.closedAt) } : {}),
|
|
1084
|
-
};
|
|
1085
|
-
}
|
|
1086
680
|
function buildSymphonyIdeaRow(idea, webId) {
|
|
1087
681
|
const createdAt = safeDate(idea.createdAt);
|
|
1088
682
|
const updatedAt = safeDate(idea.updatedAt);
|
|
1089
683
|
return {
|
|
1090
684
|
id: getSymphonyArchiveKey(idea.uri),
|
|
1091
685
|
summary: idea.summary,
|
|
1092
|
-
|
|
1093
|
-
// File-primary: the source text lives in idea.md; TTL keeps only routing/index facts.
|
|
1094
|
-
input: undefined,
|
|
686
|
+
input: idea.input,
|
|
1095
687
|
status: idea.status,
|
|
1096
688
|
commitment: idea.commitment,
|
|
1097
689
|
affectedArea: idea.affectedArea,
|
|
1098
|
-
currentUnderstanding:
|
|
1099
|
-
openQuestions:
|
|
690
|
+
currentUnderstanding: idea.currentUnderstanding,
|
|
691
|
+
openQuestions: idea.openQuestions,
|
|
1100
692
|
related: idea.relatedRecords,
|
|
1101
|
-
conflicts:
|
|
1102
|
-
nextStep:
|
|
693
|
+
conflicts: idea.conflicts,
|
|
694
|
+
nextStep: idea.nextStep,
|
|
1103
695
|
promotedTo: idea.promotedTo,
|
|
1104
696
|
chat: idea.chat,
|
|
1105
697
|
thread: idea.thread,
|
|
@@ -1107,277 +699,25 @@ function buildSymphonyIdeaRow(idea, webId) {
|
|
|
1107
699
|
createdBy: webId,
|
|
1108
700
|
metadata: {
|
|
1109
701
|
surface: 'symphony',
|
|
1110
|
-
filePrimary: true,
|
|
1111
|
-
documentPath: buildSymphonyIdeaDocumentPath(idea),
|
|
1112
702
|
...buildSymphonyArchiveMetadata({ idea: idea.uri }),
|
|
1113
703
|
},
|
|
1114
704
|
createdAt,
|
|
1115
705
|
updatedAt,
|
|
1116
706
|
};
|
|
1117
707
|
}
|
|
1118
|
-
function mapSymphonyTaskStatus(status) {
|
|
1119
|
-
if (status === 'running')
|
|
1120
|
-
return 'active';
|
|
1121
|
-
if (status === 'pending')
|
|
1122
|
-
return 'open';
|
|
1123
|
-
return status;
|
|
1124
|
-
}
|
|
1125
|
-
function mapSymphonyRunStatus(status) {
|
|
1126
|
-
if (status === 'planned')
|
|
1127
|
-
return 'queued';
|
|
1128
|
-
if (status === 'running')
|
|
1129
|
-
return 'running';
|
|
1130
|
-
if (status === 'completed')
|
|
1131
|
-
return 'completed';
|
|
1132
|
-
if (status === 'failed')
|
|
1133
|
-
return 'failed';
|
|
1134
|
-
return 'queued';
|
|
1135
|
-
}
|
|
1136
|
-
function buildSymphonyTaskRow(plan, webId, worker) {
|
|
1137
|
-
const createdAt = safeDate(worker.taskRecord.createdAt);
|
|
1138
|
-
const updatedAt = safeDate(worker.taskRecord.updatedAt);
|
|
1139
|
-
const workerContact = buildWorkerContactIri(webId, worker);
|
|
1140
|
-
const workerAgent = buildWorkerAgentIri(webId, worker);
|
|
1141
|
-
return {
|
|
1142
|
-
id: taskResource.buildId({ id: buildSymphonyTaskKey(worker.task) }),
|
|
1143
|
-
title: worker.taskRecord.title,
|
|
1144
|
-
instruction: worker.taskRecord.objective,
|
|
1145
|
-
prompt: worker.delivery.projection.prompt,
|
|
1146
|
-
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1147
|
-
message: plan.issue.messages?.at(-1),
|
|
1148
|
-
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1149
|
-
workspace: pathToWorkspaceUri(worker.session.cwd) ?? pathToWorkspaceUri(plan.session.cwd) ?? 'file:///',
|
|
1150
|
-
status: mapSymphonyTaskStatus(worker.taskRecord.status),
|
|
1151
|
-
priority: plan.issue.priority,
|
|
1152
|
-
assignedTo: workerContact,
|
|
1153
|
-
source: buildSymphonyIssueIri(webId, plan.issue),
|
|
1154
|
-
metadata: {
|
|
1155
|
-
surface: 'symphony',
|
|
1156
|
-
...buildSymphonyArchiveMetadata({ task: worker.taskRecord.uri }),
|
|
1157
|
-
acceptanceCriteria: worker.taskRecord.acceptanceCriteria,
|
|
1158
|
-
backend: worker.session.backend,
|
|
1159
|
-
target: worker.session.target,
|
|
1160
|
-
assignedContact: workerContact,
|
|
1161
|
-
assignedAgent: workerAgent,
|
|
1162
|
-
workspace: buildSymphonyWorkspaceMetadata(plan, worker),
|
|
1163
|
-
spaceContract: buildSymphonySpaceContract(plan, webId, worker),
|
|
1164
|
-
podAccessPolicy: buildSymphonyWorkerPodAccessPolicy(plan, webId, worker),
|
|
1165
|
-
reconciler: buildSymphonyReconcilerMetadata(worker),
|
|
1166
|
-
},
|
|
1167
|
-
createdAt,
|
|
1168
|
-
updatedAt,
|
|
1169
|
-
};
|
|
1170
|
-
}
|
|
1171
|
-
function buildSymphonyDeliveryRow(plan, webId, worker) {
|
|
1172
|
-
const createdAt = safeDate(worker.delivery.createdAt);
|
|
1173
|
-
const updatedAt = safeDate(worker.delivery.updatedAt);
|
|
1174
|
-
const secretaryAgent = agentResource.buildIri(webId, { id: SYMPHONY_SECRETARY_AGENT_ID });
|
|
1175
|
-
const secretaryContact = buildSecretaryContactIri(webId);
|
|
1176
|
-
const workerAgent = buildWorkerAgentIri(webId, worker);
|
|
1177
|
-
const workerContact = buildWorkerContactIri(webId, worker);
|
|
1178
|
-
return {
|
|
1179
|
-
id: deliveryResource.buildId({
|
|
1180
|
-
id: getSymphonyArchiveKey(worker.delivery.uri),
|
|
1181
|
-
task: buildSymphonyTaskIri(webId, worker.task),
|
|
1182
|
-
createdAt,
|
|
1183
|
-
}),
|
|
1184
|
-
kind: worker.delivery.type,
|
|
1185
|
-
status: worker.delivery.status,
|
|
1186
|
-
task: buildSymphonyTaskIri(webId, worker.task),
|
|
1187
|
-
source: secretaryContact,
|
|
1188
|
-
target: workerContact,
|
|
1189
|
-
chat: selectWorkerChatIri(plan, webId, worker),
|
|
1190
|
-
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1191
|
-
targetThread: selectWorkerThreadIri(plan, webId, worker),
|
|
1192
|
-
targetSession: worker.session.uri,
|
|
1193
|
-
actor: secretaryAgent,
|
|
1194
|
-
object: buildSymphonyTaskIri(webId, worker.task),
|
|
1195
|
-
objective: worker.taskRecord.objective,
|
|
1196
|
-
payload: {
|
|
1197
|
-
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1198
|
-
acceptanceCriteria: worker.taskRecord.acceptanceCriteria,
|
|
1199
|
-
backend: worker.session.backend,
|
|
1200
|
-
mode: worker.session.mode,
|
|
1201
|
-
target: worker.session.target,
|
|
1202
|
-
targetContact: workerContact,
|
|
1203
|
-
targetAgent: workerAgent,
|
|
1204
|
-
workspace: buildSymphonyWorkspaceMetadata(plan, worker),
|
|
1205
|
-
spaceContract: buildSymphonySpaceContract(plan, webId, worker),
|
|
1206
|
-
podAccessPolicy: buildSymphonyWorkerPodAccessPolicy(plan, webId, worker),
|
|
1207
|
-
reconciler: buildSymphonyReconcilerMetadata(worker),
|
|
1208
|
-
},
|
|
1209
|
-
projection: worker.delivery.projection,
|
|
1210
|
-
projectedRole: worker.delivery.projection.runtimeRole,
|
|
1211
|
-
metadata: {
|
|
1212
|
-
surface: 'symphony',
|
|
1213
|
-
...buildSymphonyArchiveMetadata({
|
|
1214
|
-
issue: plan.issue.uri,
|
|
1215
|
-
task: worker.task,
|
|
1216
|
-
delivery: worker.delivery.uri,
|
|
1217
|
-
session: worker.session.uri,
|
|
1218
|
-
}),
|
|
1219
|
-
autoModeSessionId: worker.delivery.autoModeSessionId,
|
|
1220
|
-
sourceAgent: secretaryAgent,
|
|
1221
|
-
targetContact: workerContact,
|
|
1222
|
-
targetAgent: workerAgent,
|
|
1223
|
-
workspace: buildSymphonyWorkspaceMetadata(plan, worker),
|
|
1224
|
-
spaceContract: buildSymphonySpaceContract(plan, webId, worker),
|
|
1225
|
-
podAccessPolicy: buildSymphonyWorkerPodAccessPolicy(plan, webId, worker),
|
|
1226
|
-
reconciler: buildSymphonyReconcilerMetadata(worker),
|
|
1227
|
-
},
|
|
1228
|
-
error: worker.delivery.error,
|
|
1229
|
-
createdAt,
|
|
1230
|
-
dispatchedAt: worker.delivery.status === 'dispatched' || worker.delivery.status === 'completed'
|
|
1231
|
-
? updatedAt
|
|
1232
|
-
: undefined,
|
|
1233
|
-
completedAt: worker.delivery.completedAt ? safeDate(worker.delivery.completedAt) : undefined,
|
|
1234
|
-
updatedAt,
|
|
1235
|
-
};
|
|
1236
|
-
}
|
|
1237
|
-
function buildSymphonyReportRow(plan, webId, worker, stage) {
|
|
1238
|
-
const completedAt = safeDate(worker.session.completedAt ?? worker.session.updatedAt);
|
|
1239
|
-
const workerAgent = buildWorkerAgentIri(webId, worker);
|
|
1240
|
-
const run = buildSymphonyRunIri(webId, worker);
|
|
1241
|
-
const task = buildSymphonyTaskIri(webId, worker.task);
|
|
1242
|
-
const status = worker.session.status === 'failed' || stage === 'failed' ? 'failed' : 'completed';
|
|
1243
|
-
const summary = status === 'completed'
|
|
1244
|
-
? `${worker.taskRecord.title} completed.`
|
|
1245
|
-
: `${worker.taskRecord.title} failed: ${worker.session.error ?? worker.delivery.error ?? 'worker did not complete successfully.'}`;
|
|
1246
|
-
const evidence = buildSymphonyEvidenceIri(webId, worker, stage);
|
|
1247
|
-
return {
|
|
1248
|
-
id: reportResource.buildId({
|
|
1249
|
-
id: `${getSymphonyArchiveKey(worker.session.uri)}-report`,
|
|
1250
|
-
task,
|
|
1251
|
-
createdAt: completedAt,
|
|
1252
|
-
}),
|
|
1253
|
-
reportKind: 'handoff',
|
|
1254
|
-
status: 'published',
|
|
1255
|
-
outcome: status === 'completed' ? 'accepted' : 'blocked',
|
|
1256
|
-
about: run,
|
|
1257
|
-
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1258
|
-
task,
|
|
1259
|
-
delivery: buildSymphonyDeliveryIri(webId, worker),
|
|
1260
|
-
run,
|
|
1261
|
-
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1262
|
-
evidence: [
|
|
1263
|
-
evidence,
|
|
1264
|
-
],
|
|
1265
|
-
summary,
|
|
1266
|
-
actor: workerAgent,
|
|
1267
|
-
source: podFileUrlFromWebId(webId, buildSymphonyReportDocumentPath(worker)),
|
|
1268
|
-
metricFacts: {
|
|
1269
|
-
backend: worker.session.backend,
|
|
1270
|
-
agent: worker.session.target.agent,
|
|
1271
|
-
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1272
|
-
exitCode: worker.session.exitCode,
|
|
1273
|
-
},
|
|
1274
|
-
metadata: {
|
|
1275
|
-
surface: 'symphony',
|
|
1276
|
-
filePrimary: true,
|
|
1277
|
-
reportFile: buildSymphonyReportDocumentPath(worker),
|
|
1278
|
-
reportDelivery: buildSymphonyReportDeliveryIri(webId, worker),
|
|
1279
|
-
postRunReconciliation: buildPostRunReconciliationMetadata(plan, webId, worker, stage),
|
|
1280
|
-
...buildSymphonyArchiveMetadata({
|
|
1281
|
-
issue: plan.issue.uri,
|
|
1282
|
-
task: worker.task,
|
|
1283
|
-
delivery: worker.delivery.uri,
|
|
1284
|
-
session: worker.session.uri,
|
|
1285
|
-
}),
|
|
1286
|
-
},
|
|
1287
|
-
createdAt: completedAt,
|
|
1288
|
-
publishedAt: completedAt,
|
|
1289
|
-
updatedAt: completedAt,
|
|
1290
|
-
};
|
|
1291
|
-
}
|
|
1292
|
-
function buildSymphonyEvidenceRow(plan, webId, worker, stage) {
|
|
1293
|
-
const createdAt = safeDate(worker.session.completedAt ?? worker.session.updatedAt);
|
|
1294
|
-
const status = worker.session.status === 'failed' || stage === 'failed' ? 'failed' : 'completed';
|
|
1295
|
-
const run = buildSymphonyRunIri(webId, worker);
|
|
1296
|
-
const task = buildSymphonyTaskIri(webId, worker.task);
|
|
1297
|
-
const delivery = buildSymphonyDeliveryIri(webId, worker);
|
|
1298
|
-
const runStep = buildSymphonyRunStepIri(webId, worker, stage);
|
|
1299
|
-
const workerAgent = buildWorkerAgentIri(webId, worker);
|
|
1300
|
-
return {
|
|
1301
|
-
id: evidenceResource.buildId({
|
|
1302
|
-
id: `${getSymphonyArchiveKey(worker.session.uri)}-${stage}`,
|
|
1303
|
-
createdAt,
|
|
1304
|
-
}),
|
|
1305
|
-
evidenceKind: EvidenceKind.RUNTIME_LOG,
|
|
1306
|
-
about: run,
|
|
1307
|
-
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1308
|
-
task,
|
|
1309
|
-
delivery,
|
|
1310
|
-
run,
|
|
1311
|
-
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1312
|
-
summary: status === 'completed'
|
|
1313
|
-
? `${worker.taskRecord.title} completed with runtime status ${worker.session.status}.`
|
|
1314
|
-
: `${worker.taskRecord.title} failed: ${worker.session.error ?? worker.delivery.error ?? worker.taskRecord.error ?? 'worker did not complete successfully.'}`,
|
|
1315
|
-
source: podFileUrlFromWebId(webId, buildSymphonyEvidenceDocumentPath(worker, stage)),
|
|
1316
|
-
actor: workerAgent,
|
|
1317
|
-
outcome: status,
|
|
1318
|
-
metadata: {
|
|
1319
|
-
surface: 'symphony',
|
|
1320
|
-
filePrimary: true,
|
|
1321
|
-
evidenceFile: buildSymphonyEvidenceDocumentPath(worker, stage),
|
|
1322
|
-
sourceRunStep: runStep,
|
|
1323
|
-
report: buildSymphonyReportIri(webId, worker),
|
|
1324
|
-
reportDelivery: buildSymphonyReportDeliveryIri(webId, worker),
|
|
1325
|
-
postRunReconciliation: buildPostRunReconciliationMetadata(plan, webId, worker, stage),
|
|
1326
|
-
runtime: {
|
|
1327
|
-
backend: worker.session.backend,
|
|
1328
|
-
model: worker.session.model,
|
|
1329
|
-
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1330
|
-
exitCode: worker.session.exitCode,
|
|
1331
|
-
status: worker.session.status,
|
|
1332
|
-
},
|
|
1333
|
-
...buildSymphonyArchiveMetadata({
|
|
1334
|
-
issue: plan.issue.uri,
|
|
1335
|
-
task: worker.task,
|
|
1336
|
-
delivery: worker.delivery.uri,
|
|
1337
|
-
session: worker.session.uri,
|
|
1338
|
-
}),
|
|
1339
|
-
},
|
|
1340
|
-
createdAt,
|
|
1341
|
-
};
|
|
1342
|
-
}
|
|
1343
|
-
function buildPostRunReconciliationMetadata(plan, webId, worker, stage) {
|
|
1344
|
-
return {
|
|
1345
|
-
required: true,
|
|
1346
|
-
status: 'pending_secretary_review',
|
|
1347
|
-
owner: buildSecretaryContactIri(webId),
|
|
1348
|
-
sourceIssue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1349
|
-
sourceTask: buildSymphonyTaskIri(webId, worker.task),
|
|
1350
|
-
sourceDelivery: buildSymphonyDeliveryIri(webId, worker),
|
|
1351
|
-
sourceRun: buildSymphonyRunIri(webId, worker),
|
|
1352
|
-
sourceRunStep: buildSymphonyRunStepIri(webId, worker, stage),
|
|
1353
|
-
sourceEvidence: buildSymphonyEvidenceIri(webId, worker, stage),
|
|
1354
|
-
sourceReport: buildSymphonyReportIri(webId, worker),
|
|
1355
|
-
classifications: [
|
|
1356
|
-
'same_issue_task',
|
|
1357
|
-
'new_issue',
|
|
1358
|
-
'idea',
|
|
1359
|
-
'evidence_only',
|
|
1360
|
-
'ask_user',
|
|
1361
|
-
],
|
|
1362
|
-
rule: 'Secretary must reconcile acceptance and follow-up before task closure.',
|
|
1363
|
-
};
|
|
1364
|
-
}
|
|
1365
708
|
function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
1366
709
|
const completedAt = safeDate(worker.session.completedAt ?? worker.session.updatedAt);
|
|
1367
|
-
const workerAgent =
|
|
1368
|
-
|
|
710
|
+
const workerAgent = agentResource.buildIri(webId, {
|
|
711
|
+
id: buildWorkerAgentId(worker.session.backend, worker.session.target.agent),
|
|
712
|
+
});
|
|
1369
713
|
const secretaryAgent = agentResource.buildIri(webId, { id: SYMPHONY_SECRETARY_AGENT_ID });
|
|
1370
|
-
const secretaryContact = buildSecretaryContactIri(webId);
|
|
1371
714
|
const run = buildSymphonyRunIri(webId, worker);
|
|
1372
|
-
const report = buildSymphonyReportIri(webId, worker);
|
|
1373
715
|
const task = buildSymphonyTaskIri(webId, worker.task);
|
|
1374
716
|
const originalDelivery = buildSymphonyDeliveryIri(webId, worker);
|
|
1375
717
|
const status = worker.session.status === 'failed' || stage === 'failed' ? 'failed' : 'completed';
|
|
1376
718
|
const summary = status === 'completed'
|
|
1377
719
|
? `${worker.taskRecord.title} completed.`
|
|
1378
720
|
: `${worker.taskRecord.title} failed: ${worker.session.error ?? worker.delivery.error ?? 'worker did not complete successfully.'}`;
|
|
1379
|
-
const evidence = buildSymphonyEvidenceIri(webId, worker, stage);
|
|
1380
|
-
const sourceRunStep = buildSymphonyRunStepIri(webId, worker, stage);
|
|
1381
721
|
return {
|
|
1382
722
|
id: deliveryResource.buildId({
|
|
1383
723
|
id: `${getSymphonyArchiveKey(worker.session.uri)}-report`,
|
|
@@ -1387,14 +727,14 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
1387
727
|
kind: 'report',
|
|
1388
728
|
status: 'completed',
|
|
1389
729
|
task,
|
|
1390
|
-
source:
|
|
1391
|
-
target:
|
|
730
|
+
source: workerAgent,
|
|
731
|
+
target: secretaryAgent,
|
|
1392
732
|
chat: selectWorkerChatIri(plan, webId, worker),
|
|
1393
733
|
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1394
734
|
targetThread: selectTargetThreadIri(plan.issue.thread ?? worker.session.target?.thread, webId, plan),
|
|
1395
735
|
targetSession: buildSymphonyControlSessionUri(webId, plan),
|
|
1396
736
|
actor: workerAgent,
|
|
1397
|
-
object:
|
|
737
|
+
object: run,
|
|
1398
738
|
objective: summary,
|
|
1399
739
|
payload: {
|
|
1400
740
|
kind: 'symphony_report',
|
|
@@ -1403,25 +743,18 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
1403
743
|
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1404
744
|
task,
|
|
1405
745
|
delivery: originalDelivery,
|
|
1406
|
-
report,
|
|
1407
746
|
reportDelivery: buildSymphonyReportDeliveryIri(webId, worker),
|
|
1408
747
|
session: buildSymphonyControlSessionUri(webId, plan),
|
|
1409
748
|
run,
|
|
1410
749
|
backend: worker.session.backend,
|
|
1411
750
|
agent: worker.session.target.agent,
|
|
1412
|
-
contact: worker.session.target.contact ?? buildWorkerContactId(worker),
|
|
1413
|
-
sourceAgent: workerAgent,
|
|
1414
|
-
sourceContact: workerContact,
|
|
1415
751
|
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1416
752
|
exitCode: worker.session.exitCode,
|
|
1417
753
|
error: worker.session.error ?? worker.delivery.error ?? worker.taskRecord.error,
|
|
1418
|
-
|
|
1419
|
-
evidenceFile: buildSymphonyEvidenceDocumentPath(worker, stage),
|
|
1420
|
-
postRunReconciliation: buildPostRunReconciliationMetadata(plan, webId, worker, stage),
|
|
754
|
+
acceptanceReview: worker.delivery.acceptanceReview ?? worker.taskRecord.acceptanceReview ?? worker.session.acceptanceReview,
|
|
1421
755
|
evidence: {
|
|
1422
|
-
statusMessage:
|
|
1423
|
-
|
|
1424
|
-
evidence,
|
|
756
|
+
statusMessage: buildSharedSymphonyMessageIri(webId, plan, buildSharedSymphonyStatusMessageRow(plan, webId, stage)),
|
|
757
|
+
runStep: buildSymphonyRunStepIri(webId, worker, stage),
|
|
1425
758
|
},
|
|
1426
759
|
},
|
|
1427
760
|
projection: {
|
|
@@ -1438,11 +771,7 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
1438
771
|
session: worker.session.uri,
|
|
1439
772
|
}),
|
|
1440
773
|
reportKind: 'worker-completion',
|
|
1441
|
-
|
|
1442
|
-
reportFile: buildSymphonyReportDocumentPath(worker),
|
|
1443
|
-
evidence,
|
|
1444
|
-
evidenceFile: buildSymphonyEvidenceDocumentPath(worker, stage),
|
|
1445
|
-
postRunReconciliation: buildPostRunReconciliationMetadata(plan, webId, worker, stage),
|
|
774
|
+
acceptanceReview: worker.delivery.acceptanceReview ?? worker.taskRecord.acceptanceReview ?? worker.session.acceptanceReview,
|
|
1446
775
|
},
|
|
1447
776
|
error: status === 'failed' ? worker.session.error ?? worker.delivery.error ?? worker.taskRecord.error : undefined,
|
|
1448
777
|
createdAt: completedAt,
|
|
@@ -1452,101 +781,13 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
1452
781
|
updatedAt: completedAt,
|
|
1453
782
|
};
|
|
1454
783
|
}
|
|
1455
|
-
function
|
|
1456
|
-
const
|
|
1457
|
-
const updatedAt = safeDate(worker.session.updatedAt);
|
|
1458
|
-
return {
|
|
1459
|
-
id: runResource.buildId({
|
|
1460
|
-
id: getSymphonyArchiveKey(worker.session.uri),
|
|
1461
|
-
task: buildSymphonyTaskIri(webId, worker.task),
|
|
1462
|
-
createdAt,
|
|
1463
|
-
}),
|
|
1464
|
-
task: buildSymphonyTaskIri(webId, worker.task),
|
|
1465
|
-
delivery: buildSymphonyDeliveryIri(webId, worker),
|
|
1466
|
-
trigger: plan.issue.messages?.at(-1) ?? buildSymphonyIssueIri(webId, plan.issue),
|
|
1467
|
-
input: buildSymphonyDeliveryIri(webId, worker),
|
|
1468
|
-
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1469
|
-
workspace: pathToWorkspaceUri(worker.session.cwd) ?? pathToWorkspaceUri(plan.session.cwd) ?? 'file:///',
|
|
1470
|
-
status: mapSymphonyRunStatus(worker.session.status),
|
|
1471
|
-
runner: worker.session.backend,
|
|
1472
|
-
prompt: worker.delivery.projection.prompt,
|
|
1473
|
-
externalRunId: worker.session.autoModeSessionId,
|
|
1474
|
-
error: worker.session.error,
|
|
1475
|
-
metadata: {
|
|
1476
|
-
surface: 'symphony',
|
|
1477
|
-
...buildSymphonyArchiveMetadata({ session: worker.session.uri }),
|
|
1478
|
-
mode: worker.session.mode,
|
|
1479
|
-
model: worker.session.model,
|
|
1480
|
-
target: worker.session.target,
|
|
1481
|
-
workspace: buildSymphonyWorkspaceMetadata(plan, worker),
|
|
1482
|
-
spaceContract: buildSymphonySpaceContract(plan, webId, worker),
|
|
1483
|
-
podAccessPolicy: buildSymphonyWorkerPodAccessPolicy(plan, webId, worker),
|
|
1484
|
-
reconciler: buildSymphonyReconcilerMetadata(worker),
|
|
1485
|
-
exitCode: worker.session.exitCode,
|
|
1486
|
-
dryRun: worker.session.dryRun,
|
|
1487
|
-
},
|
|
1488
|
-
createdAt,
|
|
1489
|
-
startedAt: worker.session.status === 'running' || worker.session.status === 'completed' || worker.session.status === 'failed'
|
|
1490
|
-
? updatedAt
|
|
1491
|
-
: undefined,
|
|
1492
|
-
completedAt: worker.session.completedAt ? safeDate(worker.session.completedAt) : undefined,
|
|
1493
|
-
updatedAt,
|
|
1494
|
-
};
|
|
1495
|
-
}
|
|
1496
|
-
function buildSymphonyRunStepRow(plan, webId, worker, stage) {
|
|
1497
|
-
const run = buildSymphonyRunIri(webId, worker);
|
|
1498
|
-
const createdAt = stage === 'planned' ? safeDate(worker.session.createdAt) : safeDate(worker.session.updatedAt);
|
|
1499
|
-
const stepType = stage === 'planned'
|
|
1500
|
-
? 'run.created'
|
|
1501
|
-
: stage === 'running'
|
|
1502
|
-
? 'run.started'
|
|
1503
|
-
: stage === 'completed'
|
|
1504
|
-
? 'run.completed'
|
|
1505
|
-
: 'run.failed';
|
|
1506
|
-
return {
|
|
1507
|
-
id: runStepResource.buildId({
|
|
1508
|
-
id: `${getSymphonyArchiveKey(worker.session.uri)}-${stage}`,
|
|
1509
|
-
run,
|
|
1510
|
-
}),
|
|
1511
|
-
run,
|
|
1512
|
-
stepType,
|
|
1513
|
-
message: buildStatusContent(plan, stage),
|
|
1514
|
-
data: {
|
|
1515
|
-
surface: 'symphony',
|
|
1516
|
-
stage,
|
|
1517
|
-
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1518
|
-
task: buildSymphonyTaskIri(webId, worker.task),
|
|
1519
|
-
delivery: buildSymphonyDeliveryIri(webId, worker),
|
|
1520
|
-
archive: buildSymphonyArchiveRefs({ session: worker.session.uri }),
|
|
1521
|
-
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1522
|
-
},
|
|
1523
|
-
createdAt,
|
|
1524
|
-
};
|
|
1525
|
-
}
|
|
1526
|
-
function normalizeSymphonyActorKey(value, fallback) {
|
|
1527
|
-
return (value ?? fallback)
|
|
784
|
+
function buildWorkerAgentId(backend, agent) {
|
|
785
|
+
const suffix = (agent ?? `${backend}-worker`)
|
|
1528
786
|
.trim()
|
|
1529
787
|
.replace(/[^a-zA-Z0-9._-]/gu, '-')
|
|
1530
788
|
.replace(/-+/gu, '-')
|
|
1531
|
-
.replace(/^-|-$/gu, '')
|
|
1532
|
-
|
|
1533
|
-
}
|
|
1534
|
-
function buildWorkerContactId(worker) {
|
|
1535
|
-
return normalizeSymphonyActorKey(worker.session.target.contact ?? worker.session.target.agent ?? worker.delivery.targetAgent, worker.session.backend);
|
|
1536
|
-
}
|
|
1537
|
-
function buildWorkerAgentId(backend, agent, contact) {
|
|
1538
|
-
return normalizeSymphonyActorKey(agent ?? contact, backend);
|
|
1539
|
-
}
|
|
1540
|
-
function buildSecretaryContactIri(webId) {
|
|
1541
|
-
return contactResource.buildIri(webId, { id: SYMPHONY_CONTACT_ID });
|
|
1542
|
-
}
|
|
1543
|
-
function buildWorkerAgentIri(webId, worker) {
|
|
1544
|
-
return agentResource.buildIri(webId, {
|
|
1545
|
-
id: buildWorkerAgentId(worker.session.backend, worker.session.target.agent, worker.session.target.contact),
|
|
1546
|
-
});
|
|
1547
|
-
}
|
|
1548
|
-
function buildWorkerContactIri(webId, worker) {
|
|
1549
|
-
return contactResource.buildIri(webId, { id: buildWorkerContactId(worker) });
|
|
789
|
+
.replace(/^-|-$/gu, '');
|
|
790
|
+
return `symphony-${suffix || `${backend}-worker`}`;
|
|
1550
791
|
}
|
|
1551
792
|
function buildSymphonyAgents(plan) {
|
|
1552
793
|
const now = safeDate(plan.session.updatedAt);
|
|
@@ -1563,7 +804,7 @@ function buildSymphonyAgents(plan) {
|
|
|
1563
804
|
];
|
|
1564
805
|
const seen = new Set(agents.map((agent) => agent.id));
|
|
1565
806
|
for (const worker of plan.workers) {
|
|
1566
|
-
const id = buildWorkerAgentId(worker.session.backend, worker.session.target.agent
|
|
807
|
+
const id = buildWorkerAgentId(worker.session.backend, worker.session.target.agent);
|
|
1567
808
|
if (seen.has(id)) {
|
|
1568
809
|
continue;
|
|
1569
810
|
}
|
|
@@ -1580,146 +821,6 @@ function buildSymphonyAgents(plan) {
|
|
|
1580
821
|
}
|
|
1581
822
|
return agents;
|
|
1582
823
|
}
|
|
1583
|
-
function buildSymphonyContacts(plan, webId) {
|
|
1584
|
-
const now = safeDate(plan.session.updatedAt);
|
|
1585
|
-
const contacts = [
|
|
1586
|
-
{
|
|
1587
|
-
id: SYMPHONY_CONTACT_ID,
|
|
1588
|
-
name: 'AI Secretary',
|
|
1589
|
-
entity: agentResource.buildIri(webId, { id: SYMPHONY_SECRETARY_AGENT_ID }),
|
|
1590
|
-
rdfType: ContactClass.AGENT,
|
|
1591
|
-
contactType: ContactType.AGENT,
|
|
1592
|
-
createdAt: now,
|
|
1593
|
-
updatedAt: now,
|
|
1594
|
-
},
|
|
1595
|
-
];
|
|
1596
|
-
const seen = new Set(contacts.map((contact) => contact.id));
|
|
1597
|
-
for (const worker of plan.workers) {
|
|
1598
|
-
const id = buildWorkerContactId(worker);
|
|
1599
|
-
if (seen.has(id)) {
|
|
1600
|
-
continue;
|
|
1601
|
-
}
|
|
1602
|
-
seen.add(id);
|
|
1603
|
-
contacts.push({
|
|
1604
|
-
id,
|
|
1605
|
-
name: worker.session.target.label ?? worker.session.target.contact ?? worker.session.target.agent ?? backendDisplayName(worker.session.backend),
|
|
1606
|
-
entity: buildWorkerAgentIri(webId, worker),
|
|
1607
|
-
rdfType: ContactClass.AGENT,
|
|
1608
|
-
contactType: ContactType.AGENT,
|
|
1609
|
-
createdAt: now,
|
|
1610
|
-
updatedAt: now,
|
|
1611
|
-
});
|
|
1612
|
-
}
|
|
1613
|
-
return contacts;
|
|
1614
|
-
}
|
|
1615
|
-
function buildProgressBlock(plan, stage) {
|
|
1616
|
-
const statusByStage = {
|
|
1617
|
-
planned: 'pending',
|
|
1618
|
-
running: 'running',
|
|
1619
|
-
completed: 'done',
|
|
1620
|
-
failed: 'error',
|
|
1621
|
-
};
|
|
1622
|
-
const workerSteps = plan.workers.map((worker, index) => ({
|
|
1623
|
-
id: `${buildSymphonyThreadId(plan)}-worker-${index + 1}`,
|
|
1624
|
-
label: `${worker.session.target.label ?? worker.session.target.agent ?? backendDisplayName(worker.session.backend)} worker`,
|
|
1625
|
-
status: worker.session.status === 'completed'
|
|
1626
|
-
? 'done'
|
|
1627
|
-
: worker.session.status === 'failed'
|
|
1628
|
-
? 'error'
|
|
1629
|
-
: worker.session.status === 'running'
|
|
1630
|
-
? 'running'
|
|
1631
|
-
: statusByStage[stage],
|
|
1632
|
-
detail: worker.session.autoModeSessionId ?? worker.session.uri,
|
|
1633
|
-
}));
|
|
1634
|
-
return {
|
|
1635
|
-
type: 'task_progress',
|
|
1636
|
-
task: plan.task,
|
|
1637
|
-
title: plan.issue.title,
|
|
1638
|
-
steps: [
|
|
1639
|
-
{
|
|
1640
|
-
id: `${buildSymphonyThreadId(plan)}-plan`,
|
|
1641
|
-
label: 'Secretary created task projection',
|
|
1642
|
-
status: stage === 'planned' ? 'running' : 'done',
|
|
1643
|
-
detail: plan.issue.uri,
|
|
1644
|
-
},
|
|
1645
|
-
...workerSteps,
|
|
1646
|
-
{
|
|
1647
|
-
id: `${buildSymphonyThreadId(plan)}-finish`,
|
|
1648
|
-
label: 'Archive Symphony result',
|
|
1649
|
-
status: stage === 'completed' ? 'done' : stage === 'failed' ? 'error' : 'pending',
|
|
1650
|
-
detail: plan.issue.error ?? plan.session.error ?? `${plan.workers.length} worker${plan.workers.length === 1 ? '' : 's'}`,
|
|
1651
|
-
},
|
|
1652
|
-
],
|
|
1653
|
-
currentStep: stage === 'planned' ? 1 : stage === 'running' ? 2 : workerSteps.length + 2,
|
|
1654
|
-
totalSteps: workerSteps.length + 2,
|
|
1655
|
-
};
|
|
1656
|
-
}
|
|
1657
|
-
function buildStatusContent(plan, stage) {
|
|
1658
|
-
if (stage === 'planned') {
|
|
1659
|
-
return `I created a Symphony issue with ${plan.workers.length} worker${plan.workers.length === 1 ? '' : 's'}.\n\n${plan.issue.description ?? plan.issue.title}`;
|
|
1660
|
-
}
|
|
1661
|
-
if (stage === 'running') {
|
|
1662
|
-
const running = plan.workers
|
|
1663
|
-
.filter((worker) => worker.session.status === 'running')
|
|
1664
|
-
.map((worker) => worker.session.target.label ?? worker.session.target.agent ?? backendDisplayName(worker.session.backend));
|
|
1665
|
-
return `Symphony workers are active: ${running.length > 0 ? running.join(', ') : plan.workers.length}.\n\nIssue: ${plan.issue.uri}`;
|
|
1666
|
-
}
|
|
1667
|
-
if (stage === 'completed') {
|
|
1668
|
-
return `Symphony issue completed.\n\nWorkers: ${plan.workers.length}`;
|
|
1669
|
-
}
|
|
1670
|
-
return `Symphony issue failed.\n\n${plan.issue.error ?? plan.session.error ?? plan.delivery.error ?? 'Backend did not complete successfully.'}`;
|
|
1671
|
-
}
|
|
1672
|
-
function buildStatusMessageRow(plan, webId, stage) {
|
|
1673
|
-
const createdAt = stage === 'planned'
|
|
1674
|
-
? safeDate(plan.issue.createdAt)
|
|
1675
|
-
: safeDate(plan.session.updatedAt);
|
|
1676
|
-
const content = buildStatusContent(plan, stage);
|
|
1677
|
-
const secretaryAgent = agentResource.buildIri(webId, { id: SYMPHONY_SECRETARY_AGENT_ID });
|
|
1678
|
-
const routeTargetAgent = agentResource.buildIri(webId, {
|
|
1679
|
-
id: buildWorkerAgentId(plan.session.backend, plan.session.target?.agent, plan.session.target?.contact),
|
|
1680
|
-
});
|
|
1681
|
-
return {
|
|
1682
|
-
id: `${buildSymphonyThreadId(plan)}-${stage}`,
|
|
1683
|
-
chat: selectTargetChatIri(plan.session.target?.chat, webId, plan),
|
|
1684
|
-
thread: selectTargetThreadIri(plan.session.target?.thread, webId, plan),
|
|
1685
|
-
maker: secretaryAgent,
|
|
1686
|
-
role: 'assistant',
|
|
1687
|
-
content,
|
|
1688
|
-
richContent: JSON.stringify({
|
|
1689
|
-
blocks: [buildProgressBlock(plan, stage)],
|
|
1690
|
-
symphony: {
|
|
1691
|
-
stage,
|
|
1692
|
-
issue: plan.issue.uri,
|
|
1693
|
-
task: plan.task,
|
|
1694
|
-
delivery: plan.delivery.uri,
|
|
1695
|
-
session: plan.session.uri,
|
|
1696
|
-
issuer: plan.issue.issuer,
|
|
1697
|
-
workers: plan.workers.map((worker) => ({
|
|
1698
|
-
task: worker.task,
|
|
1699
|
-
title: worker.taskRecord.title,
|
|
1700
|
-
objective: worker.taskRecord.objective,
|
|
1701
|
-
acceptanceCriteria: worker.taskRecord.acceptanceCriteria,
|
|
1702
|
-
taskStatus: worker.taskRecord.status,
|
|
1703
|
-
delivery: worker.delivery.uri,
|
|
1704
|
-
session: worker.session.uri,
|
|
1705
|
-
backend: worker.session.backend,
|
|
1706
|
-
agent: worker.session.target.agent,
|
|
1707
|
-
contact: worker.session.target.contact ?? buildWorkerContactId(worker),
|
|
1708
|
-
status: worker.session.status,
|
|
1709
|
-
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1710
|
-
})),
|
|
1711
|
-
autoModeSessionId: plan.session.autoModeSessionId,
|
|
1712
|
-
},
|
|
1713
|
-
}),
|
|
1714
|
-
status: stage === 'failed' ? 'error' : 'sent',
|
|
1715
|
-
senderName: 'AI Secretary',
|
|
1716
|
-
routedBy: secretaryAgent,
|
|
1717
|
-
routeTargetAgent,
|
|
1718
|
-
coordinationId: plan.session.uri,
|
|
1719
|
-
createdAt,
|
|
1720
|
-
updatedAt: createdAt,
|
|
1721
|
-
};
|
|
1722
|
-
}
|
|
1723
824
|
function auditActionForStage(stage) {
|
|
1724
825
|
if (stage === 'running')
|
|
1725
826
|
return 'symphony.dispatched';
|
|
@@ -1742,13 +843,15 @@ function buildSymphonyReportInboxNotificationRow(webId, worker) {
|
|
|
1742
843
|
const createdAt = safeDate(worker.session.completedAt ?? worker.session.updatedAt);
|
|
1743
844
|
return {
|
|
1744
845
|
id: stableReportInboxNotificationId(worker),
|
|
1745
|
-
actor:
|
|
846
|
+
actor: agentResource.buildIri(webId, {
|
|
847
|
+
id: buildWorkerAgentId(worker.session.backend, worker.session.target.agent),
|
|
848
|
+
}),
|
|
1746
849
|
object: buildSymphonyReportDeliveryIri(webId, worker),
|
|
1747
850
|
createdAt,
|
|
1748
851
|
};
|
|
1749
852
|
}
|
|
1750
853
|
function buildSymphonyAuditRow(plan, webId, stage) {
|
|
1751
|
-
const message =
|
|
854
|
+
const message = buildSharedSymphonyStatusMessageRow(plan, webId, stage);
|
|
1752
855
|
const createdAt = safeDate(message.createdAt);
|
|
1753
856
|
return {
|
|
1754
857
|
id: stableAuditId(plan, stage),
|
|
@@ -1757,7 +860,7 @@ function buildSymphonyAuditRow(plan, webId, stage) {
|
|
|
1757
860
|
actorRole: 'secretary',
|
|
1758
861
|
onBehalfOf: webId,
|
|
1759
862
|
session: buildSymphonyControlSessionUri(webId, plan),
|
|
1760
|
-
entry:
|
|
863
|
+
entry: buildSharedSymphonyMessageIri(webId, plan, message),
|
|
1761
864
|
policyVersion: SYMPHONY_POLICY_VERSION,
|
|
1762
865
|
createdAt,
|
|
1763
866
|
};
|
|
@@ -1794,6 +897,7 @@ async function upsertMessage(db, runtime, row) {
|
|
|
1794
897
|
routedBy: row.routedBy,
|
|
1795
898
|
routeTargetAgent: row.routeTargetAgent,
|
|
1796
899
|
coordinationId: row.coordinationId,
|
|
900
|
+
metadata: row.metadata,
|
|
1797
901
|
updatedAt: row.updatedAt,
|
|
1798
902
|
});
|
|
1799
903
|
}
|
|
@@ -1805,6 +909,7 @@ async function upsertSession(db, runtime, row) {
|
|
|
1805
909
|
owner: row.owner,
|
|
1806
910
|
chat: row.chat,
|
|
1807
911
|
thread: row.thread,
|
|
912
|
+
sessionType: row.sessionType,
|
|
1808
913
|
status: row.status,
|
|
1809
914
|
tool: row.tool,
|
|
1810
915
|
tokenUsage: row.tokenUsage,
|
|
@@ -1819,12 +924,12 @@ async function upsertIssue(db, runtime, row) {
|
|
|
1819
924
|
await upsertExactRecord(db, runtime.issueResource, { id: row.id }, row, {
|
|
1820
925
|
title: row.title,
|
|
1821
926
|
description: row.description,
|
|
1822
|
-
document: row.document,
|
|
1823
927
|
status: row.status,
|
|
1824
928
|
priority: row.priority,
|
|
1825
929
|
labels: row.labels,
|
|
1826
930
|
chat: row.chat,
|
|
1827
931
|
thread: row.thread,
|
|
932
|
+
parentIssue: row.parentIssue,
|
|
1828
933
|
tasks: row.tasks,
|
|
1829
934
|
assignedTo: row.assignedTo,
|
|
1830
935
|
updatedAt: row.updatedAt,
|
|
@@ -1834,7 +939,6 @@ async function upsertIssue(db, runtime, row) {
|
|
|
1834
939
|
async function upsertIdea(db, runtime, row) {
|
|
1835
940
|
await upsertExactRecord(db, runtime.ideaResource, { id: row.id }, row, {
|
|
1836
941
|
summary: row.summary,
|
|
1837
|
-
document: row.document,
|
|
1838
942
|
input: row.input,
|
|
1839
943
|
status: row.status,
|
|
1840
944
|
commitment: row.commitment,
|
|
@@ -1869,51 +973,6 @@ async function upsertTask(db, runtime, row) {
|
|
|
1869
973
|
updatedAt: row.updatedAt,
|
|
1870
974
|
});
|
|
1871
975
|
}
|
|
1872
|
-
async function upsertReport(db, runtime, row) {
|
|
1873
|
-
await upsertExactRecord(db, runtime.reportResource, {
|
|
1874
|
-
id: row.id,
|
|
1875
|
-
task: row.task,
|
|
1876
|
-
createdAt: row.createdAt,
|
|
1877
|
-
}, row, {
|
|
1878
|
-
reportKind: row.reportKind,
|
|
1879
|
-
status: row.status,
|
|
1880
|
-
outcome: row.outcome,
|
|
1881
|
-
about: row.about,
|
|
1882
|
-
issue: row.issue,
|
|
1883
|
-
task: row.task,
|
|
1884
|
-
delivery: row.delivery,
|
|
1885
|
-
run: row.run,
|
|
1886
|
-
thread: row.thread,
|
|
1887
|
-
evidence: row.evidence,
|
|
1888
|
-
summary: row.summary,
|
|
1889
|
-
reviewer: row.reviewer,
|
|
1890
|
-
actor: row.actor,
|
|
1891
|
-
source: row.source,
|
|
1892
|
-
metricFacts: row.metricFacts,
|
|
1893
|
-
metadata: row.metadata,
|
|
1894
|
-
publishedAt: row.publishedAt,
|
|
1895
|
-
updatedAt: row.updatedAt,
|
|
1896
|
-
});
|
|
1897
|
-
}
|
|
1898
|
-
async function upsertEvidence(db, runtime, row) {
|
|
1899
|
-
await upsertExactRecord(db, runtime.evidenceResource, {
|
|
1900
|
-
id: row.id,
|
|
1901
|
-
createdAt: row.createdAt,
|
|
1902
|
-
}, row, {
|
|
1903
|
-
evidenceKind: row.evidenceKind,
|
|
1904
|
-
about: row.about,
|
|
1905
|
-
issue: row.issue,
|
|
1906
|
-
task: row.task,
|
|
1907
|
-
delivery: row.delivery,
|
|
1908
|
-
run: row.run,
|
|
1909
|
-
thread: row.thread,
|
|
1910
|
-
summary: row.summary,
|
|
1911
|
-
source: row.source,
|
|
1912
|
-
actor: row.actor,
|
|
1913
|
-
outcome: row.outcome,
|
|
1914
|
-
metadata: row.metadata,
|
|
1915
|
-
});
|
|
1916
|
-
}
|
|
1917
976
|
async function upsertDelivery(db, runtime, row) {
|
|
1918
977
|
await upsertExactRecord(db, runtime.deliveryResource, { id: row.id }, row, {
|
|
1919
978
|
kind: row.kind,
|
|
@@ -1960,6 +1019,31 @@ async function upsertRun(db, runtime, row) {
|
|
|
1960
1019
|
async function insertRunStepOnce(db, runtime, row) {
|
|
1961
1020
|
await insertExactRecordOnce(db, runtime.runStepResource, String(row.id), row);
|
|
1962
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
|
+
}
|
|
1963
1047
|
async function upsertAgent(db, runtime, row) {
|
|
1964
1048
|
const target = { id: row.id };
|
|
1965
1049
|
const agentResourceWithId = runtime.agentResource;
|
|
@@ -1979,7 +1063,7 @@ async function upsertAgent(db, runtime, row) {
|
|
|
1979
1063
|
async function upsertContact(db, runtime, row) {
|
|
1980
1064
|
await upsertExactRecord(db, runtime.contactResource, { id: row.id }, row, {
|
|
1981
1065
|
name: row.name,
|
|
1982
|
-
|
|
1066
|
+
about: row.about,
|
|
1983
1067
|
rdfType: row.rdfType,
|
|
1984
1068
|
contactType: row.contactType,
|
|
1985
1069
|
updatedAt: row.updatedAt,
|
|
@@ -1998,7 +1082,7 @@ async function insertInboxNotificationOnce(db, runtime, row) {
|
|
|
1998
1082
|
await insertExactRecordOnce(db, runtime.inboxNotificationResource, String(row.id), row);
|
|
1999
1083
|
}
|
|
2000
1084
|
function collectMessageUris(webId, plan, stages) {
|
|
2001
|
-
return Array.from(new Set(stages.map((stage) =>
|
|
1085
|
+
return Array.from(new Set(stages.map((stage) => buildSharedSymphonyMessageIri(webId, plan, buildSharedSymphonyStatusMessageRow(plan, webId, stage)))));
|
|
2002
1086
|
}
|
|
2003
1087
|
function collectSymphonyProjectionResources(webId, plan, stages) {
|
|
2004
1088
|
const resources = [];
|
|
@@ -2013,13 +1097,16 @@ function collectSymphonyProjectionResources(webId, plan, stages) {
|
|
|
2013
1097
|
};
|
|
2014
1098
|
add('chat', selectTargetChatIri(plan.session.target?.chat, webId, plan));
|
|
2015
1099
|
add('issue', buildSymphonyIssueIri(webId, plan.issue));
|
|
1100
|
+
for (const issue of plan.followUpIssues ?? []) {
|
|
1101
|
+
add('issue', buildSymphonyIssueIri(webId, issue));
|
|
1102
|
+
}
|
|
2016
1103
|
for (const message of collectMessageUris(webId, plan, stages)) {
|
|
2017
1104
|
add('message', message);
|
|
2018
1105
|
}
|
|
2019
1106
|
for (const agent of buildSymphonyAgents(plan)) {
|
|
2020
1107
|
add('agent', agentResource.buildIri(webId, { id: agent.id }));
|
|
2021
1108
|
}
|
|
2022
|
-
for (const contact of
|
|
1109
|
+
for (const contact of buildSharedSymphonyContactRows(plan, webId)) {
|
|
2023
1110
|
add('contact', contactResource.buildIri(webId, { id: contact.id }));
|
|
2024
1111
|
}
|
|
2025
1112
|
for (const worker of plan.workers) {
|
|
@@ -2032,10 +1119,10 @@ function collectSymphonyProjectionResources(webId, plan, stages) {
|
|
|
2032
1119
|
for (const stage of stages) {
|
|
2033
1120
|
add('runStep', buildSymphonyRunStepIri(webId, worker, stage));
|
|
2034
1121
|
}
|
|
1122
|
+
for (const step of worker.runSteps ?? []) {
|
|
1123
|
+
add('runStep', buildSymphonyRuntimeRunStepIri(webId, worker, step));
|
|
1124
|
+
}
|
|
2035
1125
|
if (worker.session.status === 'completed' || worker.session.status === 'failed') {
|
|
2036
|
-
const terminalStage = worker.session.status === 'failed' ? 'failed' : 'completed';
|
|
2037
|
-
add('evidence', buildSymphonyEvidenceIri(webId, worker, terminalStage));
|
|
2038
|
-
add('report', buildSymphonyReportIri(webId, worker));
|
|
2039
1126
|
add('delivery', buildSymphonyReportDeliveryIri(webId, worker));
|
|
2040
1127
|
}
|
|
2041
1128
|
}
|
|
@@ -2072,7 +1159,6 @@ export async function persistSymphonyControlStateToPod(plan, options = {}) {
|
|
|
2072
1159
|
};
|
|
2073
1160
|
const projected = withTargetRefs(normalizedPlan, refs, podSession.webId);
|
|
2074
1161
|
const resources = collectSymphonyProjectionResources(podSession.webId, projected, stages);
|
|
2075
|
-
const latestMessage = buildStatusMessageRow(projected, podSession.webId, stage);
|
|
2076
1162
|
const controlWrite = createLinxPodSyncScope({
|
|
2077
1163
|
source: 'symphony-control-state',
|
|
2078
1164
|
plane: 'control-plane',
|
|
@@ -2108,9 +1194,7 @@ export async function persistSymphonyControlStateToPod(plan, options = {}) {
|
|
|
2108
1194
|
webId: podSession.webId,
|
|
2109
1195
|
stage,
|
|
2110
1196
|
stages,
|
|
2111
|
-
latestMessage,
|
|
2112
1197
|
shouldUpsertChat: !normalizedPlan.session.target?.chat,
|
|
2113
|
-
podSession,
|
|
2114
1198
|
}),
|
|
2115
1199
|
});
|
|
2116
1200
|
return {
|
|
@@ -2184,10 +1268,6 @@ function resolveProjectionResourceModel(runtime, kind) {
|
|
|
2184
1268
|
return runtime.taskResource;
|
|
2185
1269
|
if (kind === 'delivery')
|
|
2186
1270
|
return runtime.deliveryResource;
|
|
2187
|
-
if (kind === 'evidence')
|
|
2188
|
-
return runtime.evidenceResource;
|
|
2189
|
-
if (kind === 'report')
|
|
2190
|
-
return runtime.reportResource;
|
|
2191
1271
|
if (kind === 'run')
|
|
2192
1272
|
return runtime.runResource;
|
|
2193
1273
|
if (kind === 'runStep')
|
|
@@ -2202,6 +1282,14 @@ function resolveProjectionResourceModel(runtime, kind) {
|
|
|
2202
1282
|
return runtime.inboxNotificationResource ?? null;
|
|
2203
1283
|
return null;
|
|
2204
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
|
+
}
|
|
2205
1293
|
function serializeOrmRowAsJsonLd(model, iri, row) {
|
|
2206
1294
|
const context = {};
|
|
2207
1295
|
const document = {
|
|
@@ -2325,18 +1413,7 @@ export async function persistSymphonyIdeaToPod(idea, options = {}) {
|
|
|
2325
1413
|
},
|
|
2326
1414
|
},
|
|
2327
1415
|
{
|
|
2328
|
-
id: 'symphony.idea.
|
|
2329
|
-
kind: 'upsert',
|
|
2330
|
-
apply: async () => {
|
|
2331
|
-
await runtime.writePodFile?.(podSession, {
|
|
2332
|
-
path: buildSymphonyIdeaDocumentPath(idea),
|
|
2333
|
-
content: renderSymphonyIdeaMarkdown(idea),
|
|
2334
|
-
contentType: 'text/markdown; charset=utf-8',
|
|
2335
|
-
});
|
|
2336
|
-
},
|
|
2337
|
-
},
|
|
2338
|
-
{
|
|
2339
|
-
id: 'symphony.idea.upsert-meta',
|
|
1416
|
+
id: 'symphony.idea.upsert',
|
|
2340
1417
|
kind: 'upsert',
|
|
2341
1418
|
apply: () => upsertIdea(db, runtime, buildSymphonyIdeaRow(idea, podSession.webId)),
|
|
2342
1419
|
},
|
|
@@ -2352,13 +1429,10 @@ export async function listOpenSymphonyIssuesFromPod(options = {}) {
|
|
|
2352
1429
|
}
|
|
2353
1430
|
const db = runtime.createDb(podSession);
|
|
2354
1431
|
try {
|
|
2355
|
-
await
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
.filter((issue) => issue !== null)
|
|
2360
|
-
.filter((issue) => !isClosedIssueStatus(issue.status))
|
|
2361
|
-
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
1432
|
+
return await listOpenSymphonyIssuesFromControlState({
|
|
1433
|
+
db,
|
|
1434
|
+
webId: podSession.webId,
|
|
1435
|
+
});
|
|
2362
1436
|
}
|
|
2363
1437
|
catch {
|
|
2364
1438
|
return null;
|
|
@@ -2372,13 +1446,7 @@ export async function listRunningSymphonyWorkersFromPod(options = {}) {
|
|
|
2372
1446
|
}
|
|
2373
1447
|
const db = runtime.createDb(podSession);
|
|
2374
1448
|
try {
|
|
2375
|
-
await
|
|
2376
|
-
const rows = await db.select().from(runtime.sessionResource).execute();
|
|
2377
|
-
return rows
|
|
2378
|
-
.filter(isSymphonySessionRow)
|
|
2379
|
-
.flatMap((row) => extractRunningSymphonyWorkersFromSession(row))
|
|
2380
|
-
.sort(compareWorkerStatusUpdatedAt)
|
|
2381
|
-
.map(({ updatedAt: _updatedAt, ...worker }) => worker);
|
|
1449
|
+
return await listRunningSymphonyWorkersFromControlState({ db });
|
|
2382
1450
|
}
|
|
2383
1451
|
catch {
|
|
2384
1452
|
return null;
|
|
@@ -2392,217 +1460,22 @@ export async function listRecentSymphonyReportsFromPod(options = {}) {
|
|
|
2392
1460
|
}
|
|
2393
1461
|
const db = runtime.createDb(podSession);
|
|
2394
1462
|
try {
|
|
2395
|
-
await
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
.filter((report) => report !== null)
|
|
2400
|
-
.sort((left, right) => right.sortAt - left.sortAt)
|
|
2401
|
-
.slice(0, options.limit ?? 5)
|
|
2402
|
-
.map(({ sortAt: _sortAt, ...report }) => report);
|
|
1463
|
+
return await listRecentSymphonyReportsFromControlState({
|
|
1464
|
+
db,
|
|
1465
|
+
limit: options.limit ?? 5,
|
|
1466
|
+
});
|
|
2403
1467
|
}
|
|
2404
1468
|
catch {
|
|
2405
1469
|
return null;
|
|
2406
1470
|
}
|
|
2407
1471
|
}
|
|
2408
|
-
function issueRowToSymphonyIssueRecord(row, webId) {
|
|
2409
|
-
const record = asRecord(row);
|
|
2410
|
-
const id = normalizeString(record?.id);
|
|
2411
|
-
const title = normalizeString(record?.title);
|
|
2412
|
-
if (!record || !id || !title) {
|
|
2413
|
-
return null;
|
|
2414
|
-
}
|
|
2415
|
-
const status = normalizeIssueStatus(record.status);
|
|
2416
|
-
const priority = normalizeIssuePriority(record.priority);
|
|
2417
|
-
const tasks = Array.isArray(record.tasks)
|
|
2418
|
-
? record.tasks.map((item) => normalizeString(item)).filter((item) => Boolean(item))
|
|
2419
|
-
: [];
|
|
2420
|
-
const createdAt = toIsoDate(record.createdAt);
|
|
2421
|
-
const updatedAt = toIsoDate(record.updatedAt) ?? createdAt;
|
|
2422
|
-
return {
|
|
2423
|
-
uri: symphonyIssueUriFromResourceId(id),
|
|
2424
|
-
title,
|
|
2425
|
-
description: normalizeString(record.description),
|
|
2426
|
-
status,
|
|
2427
|
-
priority,
|
|
2428
|
-
source: 'cli',
|
|
2429
|
-
issuer: {
|
|
2430
|
-
source: 'user',
|
|
2431
|
-
webId: normalizeString(record.createdBy) ?? webId,
|
|
2432
|
-
...(normalizeString(record.chat) ? { chat: normalizeString(record.chat) } : {}),
|
|
2433
|
-
...(normalizeString(record.thread) ? { thread: normalizeString(record.thread) } : {}),
|
|
2434
|
-
},
|
|
2435
|
-
tasks,
|
|
2436
|
-
deliveries: [],
|
|
2437
|
-
sessions: [],
|
|
2438
|
-
...(normalizeString(record.chat) ? { chat: normalizeString(record.chat) } : {}),
|
|
2439
|
-
...(normalizeString(record.thread) ? { thread: normalizeString(record.thread) } : {}),
|
|
2440
|
-
createdAt,
|
|
2441
|
-
updatedAt,
|
|
2442
|
-
...(record.closedAt ? { closedAt: toIsoDate(record.closedAt) ?? updatedAt } : {}),
|
|
2443
|
-
};
|
|
2444
|
-
}
|
|
2445
|
-
function symphonyIssueUriFromResourceId(id) {
|
|
2446
|
-
const normalized = resolvePodResourceTemplateValue(issueResource, id) ?? id;
|
|
2447
|
-
return `urn:undefineds:linx:issue:${normalized}`;
|
|
2448
|
-
}
|
|
2449
|
-
function normalizeIssueStatus(value) {
|
|
2450
|
-
const normalized = normalizeString(value);
|
|
2451
|
-
if (normalized === 'open'
|
|
2452
|
-
|| normalized === 'triaging'
|
|
2453
|
-
|| normalized === 'in_progress'
|
|
2454
|
-
|| normalized === 'blocked'
|
|
2455
|
-
|| normalized === 'resolved'
|
|
2456
|
-
|| normalized === 'closed') {
|
|
2457
|
-
return normalized;
|
|
2458
|
-
}
|
|
2459
|
-
return 'open';
|
|
2460
|
-
}
|
|
2461
|
-
function normalizeIssuePriority(value) {
|
|
2462
|
-
const normalized = normalizeString(value);
|
|
2463
|
-
if (normalized === 'low' || normalized === 'medium' || normalized === 'high' || normalized === 'urgent') {
|
|
2464
|
-
return normalized;
|
|
2465
|
-
}
|
|
2466
|
-
return 'medium';
|
|
2467
|
-
}
|
|
2468
|
-
function isClosedIssueStatus(status) {
|
|
2469
|
-
return status === 'closed' || status === 'resolved';
|
|
2470
|
-
}
|
|
2471
|
-
function isSymphonySessionRow(row) {
|
|
2472
|
-
if (!row || typeof row !== 'object') {
|
|
2473
|
-
return false;
|
|
2474
|
-
}
|
|
2475
|
-
const record = row;
|
|
2476
|
-
const metadata = asRecord(record.metadata);
|
|
2477
|
-
return metadata?.kind === 'symphony-run'
|
|
2478
|
-
|| record.policyVersion === SYMPHONY_POLICY_VERSION
|
|
2479
|
-
|| (typeof record.tool === 'string' && record.tool.startsWith('symphony:'));
|
|
2480
|
-
}
|
|
2481
|
-
function extractRunningSymphonyWorkersFromSession(row) {
|
|
2482
|
-
const metadata = asRecord(row.metadata) ?? {};
|
|
2483
|
-
const sessionStatus = normalizePodSymphonySessionStatus(metadata.status ?? row.status);
|
|
2484
|
-
const workers = Array.isArray(metadata.workers) ? metadata.workers : [];
|
|
2485
|
-
const updatedAt = safeOptionalDate(row.updatedAt);
|
|
2486
|
-
if (workers.length === 0) {
|
|
2487
|
-
if (sessionStatus !== 'running') {
|
|
2488
|
-
return [];
|
|
2489
|
-
}
|
|
2490
|
-
return [{
|
|
2491
|
-
status: sessionStatus,
|
|
2492
|
-
backend: normalizeString(metadata.backend) ?? parseBackendFromTool(row.tool) ?? 'unknown',
|
|
2493
|
-
mode: normalizeString(metadata.mode) ?? 'auto',
|
|
2494
|
-
cwd: normalizeString(metadata.workspacePath),
|
|
2495
|
-
autoModeSessionId: normalizeString(metadata.autoModeSessionId),
|
|
2496
|
-
target: normalizeSymphonyWorkerTarget(asRecord(metadata.target)),
|
|
2497
|
-
updatedAt,
|
|
2498
|
-
}];
|
|
2499
|
-
}
|
|
2500
|
-
return workers
|
|
2501
|
-
.map((item) => asRecord(item))
|
|
2502
|
-
.filter((item) => item !== null)
|
|
2503
|
-
.map((worker) => ({
|
|
2504
|
-
status: normalizePodSymphonySessionStatus(worker.status ?? worker.taskStatus ?? sessionStatus),
|
|
2505
|
-
backend: normalizeString(worker.backend) ?? normalizeString(metadata.backend) ?? parseBackendFromTool(row.tool) ?? 'unknown',
|
|
2506
|
-
mode: normalizeString(worker.mode) ?? normalizeString(metadata.mode) ?? 'auto',
|
|
2507
|
-
cwd: normalizeString(worker.workspacePath) ?? normalizeString(metadata.workspacePath),
|
|
2508
|
-
autoModeSessionId: normalizeString(worker.autoModeSessionId) ?? normalizeString(metadata.autoModeSessionId),
|
|
2509
|
-
target: normalizeSymphonyWorkerTarget(asRecord(worker.target), worker, asRecord(metadata.target)),
|
|
2510
|
-
updatedAt,
|
|
2511
|
-
}))
|
|
2512
|
-
.filter((worker) => worker.status === 'running');
|
|
2513
|
-
}
|
|
2514
|
-
function deliveryRowToSymphonyReportStatus(row) {
|
|
2515
|
-
const record = asRecord(row);
|
|
2516
|
-
if (!record) {
|
|
2517
|
-
return null;
|
|
2518
|
-
}
|
|
2519
|
-
const metadata = asRecord(record.metadata);
|
|
2520
|
-
const payload = asRecord(record.payload);
|
|
2521
|
-
if (record.kind !== 'report' && metadata?.reportKind !== 'worker-completion' && payload?.kind !== 'symphony_report') {
|
|
2522
|
-
return null;
|
|
2523
|
-
}
|
|
2524
|
-
const completedAt = safeOptionalDate(record.completedAt);
|
|
2525
|
-
const updatedAt = safeOptionalDate(record.updatedAt);
|
|
2526
|
-
const createdAt = safeOptionalDate(record.createdAt);
|
|
2527
|
-
const sortAt = completedAt?.getTime() ?? updatedAt?.getTime() ?? createdAt?.getTime() ?? 0;
|
|
2528
|
-
const agent = normalizeString(payload?.agent);
|
|
2529
|
-
const title = normalizeString(record.objective);
|
|
2530
|
-
const summary = normalizeString(payload?.summary);
|
|
2531
|
-
const task = normalizeString(record.task);
|
|
2532
|
-
const archive = asRecord(metadata?.archive);
|
|
2533
|
-
const delivery = normalizeString(payload?.delivery) ?? normalizeString(archive?.delivery);
|
|
2534
|
-
const reportDelivery = normalizeString(payload?.reportDelivery) ?? normalizeString(record.id);
|
|
2535
|
-
const run = normalizeString(payload?.run) ?? normalizeString(record.object);
|
|
2536
|
-
const chat = normalizeString(record.chat);
|
|
2537
|
-
const thread = normalizeString(record.thread);
|
|
2538
|
-
const autoModeSessionId = normalizeString(payload?.autoModeSessionId);
|
|
2539
|
-
const error = normalizeString(payload?.error) ?? normalizeString(record.error);
|
|
2540
|
-
return {
|
|
2541
|
-
status: normalizeString(payload?.outcome) ?? normalizeString(record.status) ?? 'completed',
|
|
2542
|
-
backend: normalizeString(payload?.backend) ?? 'unknown',
|
|
2543
|
-
...(agent ? { agent } : {}),
|
|
2544
|
-
...(title ? { title } : {}),
|
|
2545
|
-
...(summary ? { summary } : {}),
|
|
2546
|
-
...(task ? { task } : {}),
|
|
2547
|
-
...(delivery ? { delivery } : {}),
|
|
2548
|
-
...(reportDelivery ? { reportDelivery } : {}),
|
|
2549
|
-
...(run ? { run } : {}),
|
|
2550
|
-
...(chat ? { chat } : {}),
|
|
2551
|
-
...(thread ? { thread } : {}),
|
|
2552
|
-
...(autoModeSessionId ? { autoModeSessionId } : {}),
|
|
2553
|
-
...(error ? { error } : {}),
|
|
2554
|
-
...(completedAt ? { completedAt: completedAt.toISOString() } : {}),
|
|
2555
|
-
...(updatedAt ? { updatedAt: updatedAt.toISOString() } : {}),
|
|
2556
|
-
sortAt,
|
|
2557
|
-
};
|
|
2558
|
-
}
|
|
2559
|
-
function normalizeSymphonyWorkerTarget(target, worker = {}, fallback = null) {
|
|
2560
|
-
const normalized = {
|
|
2561
|
-
label: normalizeString(target?.label) ?? normalizeString(worker.title) ?? normalizeString(fallback?.label),
|
|
2562
|
-
agent: normalizeString(target?.agent) ?? normalizeString(worker.agent) ?? normalizeString(fallback?.agent),
|
|
2563
|
-
chat: normalizeString(target?.chat) ?? normalizeString(worker.chat) ?? normalizeString(fallback?.chat),
|
|
2564
|
-
};
|
|
2565
|
-
return Object.values(normalized).some(Boolean) ? normalized : undefined;
|
|
2566
|
-
}
|
|
2567
|
-
function compareWorkerStatusUpdatedAt(left, right) {
|
|
2568
|
-
return (right.updatedAt?.getTime() ?? 0) - (left.updatedAt?.getTime() ?? 0);
|
|
2569
|
-
}
|
|
2570
|
-
function normalizePodSymphonySessionStatus(value) {
|
|
2571
|
-
const normalized = normalizeString(value);
|
|
2572
|
-
if (normalized === 'active')
|
|
2573
|
-
return 'running';
|
|
2574
|
-
if (normalized === 'error')
|
|
2575
|
-
return 'failed';
|
|
2576
|
-
if (normalized === 'queued')
|
|
2577
|
-
return 'planned';
|
|
2578
|
-
return normalized ?? 'planned';
|
|
2579
|
-
}
|
|
2580
|
-
function parseBackendFromTool(value) {
|
|
2581
|
-
const tool = normalizeString(value);
|
|
2582
|
-
if (!tool?.startsWith('symphony:')) {
|
|
2583
|
-
return undefined;
|
|
2584
|
-
}
|
|
2585
|
-
return tool.slice('symphony:'.length) || undefined;
|
|
2586
|
-
}
|
|
2587
|
-
function asRecord(value) {
|
|
2588
|
-
return value && typeof value === 'object' && !Array.isArray(value)
|
|
2589
|
-
? value
|
|
2590
|
-
: null;
|
|
2591
|
-
}
|
|
2592
|
-
function normalizeString(value) {
|
|
2593
|
-
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
2594
|
-
}
|
|
2595
|
-
function safeOptionalDate(value) {
|
|
2596
|
-
if (!value) {
|
|
2597
|
-
return undefined;
|
|
2598
|
-
}
|
|
2599
|
-
const date = value instanceof Date ? value : new Date(String(value));
|
|
2600
|
-
return Number.isFinite(date.getTime()) ? date : undefined;
|
|
2601
|
-
}
|
|
2602
|
-
function toIsoDate(value) {
|
|
2603
|
-
return (safeOptionalDate(value) ?? new Date()).toISOString();
|
|
2604
|
-
}
|
|
2605
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
|
+
});
|
|
2606
1479
|
return [
|
|
2607
1480
|
{
|
|
2608
1481
|
id: 'symphony.prepare-resources',
|
|
@@ -2616,10 +1489,10 @@ function buildSymphonyProjectionOperations(input) {
|
|
|
2616
1489
|
input.runtime.issueResource,
|
|
2617
1490
|
input.runtime.taskResource,
|
|
2618
1491
|
input.runtime.deliveryResource,
|
|
2619
|
-
input.runtime.evidenceResource,
|
|
2620
|
-
input.runtime.reportResource,
|
|
2621
1492
|
input.runtime.runResource,
|
|
2622
1493
|
input.runtime.runStepResource,
|
|
1494
|
+
input.runtime.evidenceResource,
|
|
1495
|
+
input.runtime.reportResource,
|
|
2623
1496
|
input.runtime.agentResource,
|
|
2624
1497
|
input.runtime.contactResource,
|
|
2625
1498
|
input.runtime.auditResource,
|
|
@@ -2628,82 +1501,84 @@ function buildSymphonyProjectionOperations(input) {
|
|
|
2628
1501
|
},
|
|
2629
1502
|
},
|
|
2630
1503
|
{
|
|
2631
|
-
id: 'symphony.
|
|
1504
|
+
id: 'symphony.upsert-issue',
|
|
2632
1505
|
kind: 'upsert',
|
|
2633
|
-
apply:
|
|
2634
|
-
await input.runtime.writePodFile?.(input.podSession, {
|
|
2635
|
-
path: buildSymphonyIssueDocumentPath(input.plan.issue),
|
|
2636
|
-
content: renderSymphonyIssueMarkdown(input.plan),
|
|
2637
|
-
contentType: 'text/markdown; charset=utf-8',
|
|
2638
|
-
});
|
|
2639
|
-
},
|
|
1506
|
+
apply: () => upsertIssue(input.db, input.runtime, controlRows.issue),
|
|
2640
1507
|
},
|
|
2641
|
-
{
|
|
2642
|
-
id:
|
|
1508
|
+
...controlRows.issues.slice(1).map((row, index) => ({
|
|
1509
|
+
id: `symphony.upsert-follow-up-issue:${index + 1}`,
|
|
2643
1510
|
kind: 'upsert',
|
|
2644
|
-
apply: () => upsertIssue(input.db, input.runtime,
|
|
2645
|
-
},
|
|
2646
|
-
...
|
|
2647
|
-
{
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
{
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
{
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
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
|
+
})),
|
|
2668
1543
|
...buildSymphonyReportOperations(input),
|
|
2669
1544
|
...buildSymphonyAgents(input.plan).map((agent) => ({
|
|
2670
1545
|
id: `symphony.upsert-agent:${agent.id}`,
|
|
2671
1546
|
kind: 'upsert',
|
|
2672
1547
|
apply: () => upsertAgent(input.db, input.runtime, agent),
|
|
2673
1548
|
})),
|
|
2674
|
-
...
|
|
1549
|
+
...controlRows.contacts.map((contact) => ({
|
|
2675
1550
|
id: `symphony.upsert-contact:${contact.id}`,
|
|
2676
1551
|
kind: 'upsert',
|
|
2677
1552
|
apply: () => upsertContact(input.db, input.runtime, contact),
|
|
2678
1553
|
})),
|
|
2679
|
-
{
|
|
1554
|
+
...controlRows.chats.map((row, index) => ({
|
|
2680
1555
|
id: 'symphony.upsert-chat',
|
|
2681
1556
|
kind: 'upsert',
|
|
2682
1557
|
shouldRun: () => input.shouldUpsertChat,
|
|
2683
|
-
apply: () => upsertChat(input.db, input.runtime,
|
|
2684
|
-
},
|
|
2685
|
-
...
|
|
1558
|
+
apply: () => upsertChat(input.db, input.runtime, row),
|
|
1559
|
+
})),
|
|
1560
|
+
...controlRows.threads.map((row, index) => ({
|
|
2686
1561
|
id: `symphony.upsert-thread:${index + 1}`,
|
|
2687
1562
|
kind: 'upsert',
|
|
2688
1563
|
apply: () => upsertThread(input.db, input.runtime, row),
|
|
2689
1564
|
})),
|
|
2690
|
-
...
|
|
1565
|
+
...controlRows.sessions.map((row, index) => ({
|
|
2691
1566
|
id: `symphony.upsert-session:${index + 1}`,
|
|
2692
1567
|
kind: 'upsert',
|
|
2693
|
-
apply: () => upsertSession(input.db, input.runtime,
|
|
1568
|
+
apply: () => upsertSession(input.db, input.runtime, row),
|
|
2694
1569
|
})),
|
|
2695
|
-
...
|
|
1570
|
+
...controlRows.messages.flatMap((row, index) => [
|
|
2696
1571
|
{
|
|
2697
|
-
id: `symphony.upsert-message:${
|
|
1572
|
+
id: `symphony.upsert-message:${input.stages[index] ?? index + 1}`,
|
|
2698
1573
|
kind: 'upsert',
|
|
2699
|
-
apply: () => upsertMessage(input.db, input.runtime,
|
|
2700
|
-
},
|
|
2701
|
-
{
|
|
2702
|
-
id: `symphony.insert-audit:${stage}`,
|
|
2703
|
-
kind: 'insert',
|
|
2704
|
-
apply: () => insertAuditOnce(input.db, input.runtime, buildSymphonyAuditRow(input.plan, input.webId, stage)),
|
|
1574
|
+
apply: () => upsertMessage(input.db, input.runtime, row),
|
|
2705
1575
|
},
|
|
2706
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
|
+
})),
|
|
2707
1582
|
];
|
|
2708
1583
|
}
|
|
2709
1584
|
function buildSymphonyReportOperations(input) {
|
|
@@ -2712,38 +1587,6 @@ function buildSymphonyReportOperations(input) {
|
|
|
2712
1587
|
}
|
|
2713
1588
|
const terminalStage = input.stage;
|
|
2714
1589
|
return input.plan.workers.flatMap((worker, index) => [
|
|
2715
|
-
{
|
|
2716
|
-
id: `symphony.write-evidence-file:${index + 1}`,
|
|
2717
|
-
kind: 'upsert',
|
|
2718
|
-
apply: async () => {
|
|
2719
|
-
await input.runtime.writePodFile?.(input.podSession, {
|
|
2720
|
-
path: buildSymphonyEvidenceDocumentPath(worker, terminalStage),
|
|
2721
|
-
content: renderSymphonyEvidenceMarkdown(input.plan, input.webId, worker, terminalStage),
|
|
2722
|
-
contentType: 'text/markdown; charset=utf-8',
|
|
2723
|
-
});
|
|
2724
|
-
},
|
|
2725
|
-
},
|
|
2726
|
-
{
|
|
2727
|
-
id: `symphony.upsert-evidence:${index + 1}`,
|
|
2728
|
-
kind: 'upsert',
|
|
2729
|
-
apply: () => upsertEvidence(input.db, input.runtime, buildSymphonyEvidenceRow(input.plan, input.webId, worker, terminalStage)),
|
|
2730
|
-
},
|
|
2731
|
-
{
|
|
2732
|
-
id: `symphony.write-report-file:${index + 1}`,
|
|
2733
|
-
kind: 'upsert',
|
|
2734
|
-
apply: async () => {
|
|
2735
|
-
await input.runtime.writePodFile?.(input.podSession, {
|
|
2736
|
-
path: buildSymphonyReportDocumentPath(worker),
|
|
2737
|
-
content: renderSymphonyReportMarkdown(input.plan, worker, terminalStage),
|
|
2738
|
-
contentType: 'text/markdown; charset=utf-8',
|
|
2739
|
-
});
|
|
2740
|
-
},
|
|
2741
|
-
},
|
|
2742
|
-
{
|
|
2743
|
-
id: `symphony.upsert-report:${index + 1}`,
|
|
2744
|
-
kind: 'upsert',
|
|
2745
|
-
apply: () => upsertReport(input.db, input.runtime, buildSymphonyReportRow(input.plan, input.webId, worker, terminalStage)),
|
|
2746
|
-
},
|
|
2747
1590
|
{
|
|
2748
1591
|
id: `symphony.upsert-report-delivery:${index + 1}`,
|
|
2749
1592
|
kind: 'upsert',
|
|
@@ -2769,21 +1612,21 @@ export const __symphonyPodProjectionInternal = {
|
|
|
2769
1612
|
buildSymphonyMessageUri,
|
|
2770
1613
|
auditActionForStage,
|
|
2771
1614
|
buildSymphonyAuditRow,
|
|
2772
|
-
buildStatusMessageRow,
|
|
2773
|
-
buildSymphonyChatRow,
|
|
2774
|
-
buildSymphonyThreadRow,
|
|
2775
|
-
buildSymphonySessionRow,
|
|
2776
|
-
buildSymphonyIssueRow,
|
|
1615
|
+
buildStatusMessageRow: buildSharedSymphonyStatusMessageRow,
|
|
1616
|
+
buildSymphonyChatRow: buildSharedSymphonyChatRow,
|
|
1617
|
+
buildSymphonyThreadRow: buildSharedSymphonyThreadRow,
|
|
1618
|
+
buildSymphonySessionRow: buildSharedSymphonySessionRow,
|
|
1619
|
+
buildSymphonyIssueRow: buildSharedSymphonyIssueRow,
|
|
2777
1620
|
buildSymphonyIdeaRow,
|
|
2778
|
-
buildSymphonyTaskRow,
|
|
2779
|
-
buildSymphonyDeliveryRow,
|
|
2780
|
-
buildSymphonyEvidenceRow,
|
|
1621
|
+
buildSymphonyTaskRow: buildSharedSymphonyTaskRow,
|
|
1622
|
+
buildSymphonyDeliveryRow: buildSharedSymphonyDeliveryRow,
|
|
2781
1623
|
buildSymphonyReportDeliveryRow,
|
|
2782
1624
|
buildSymphonyReportInboxNotificationRow,
|
|
2783
|
-
buildSymphonyRunRow,
|
|
2784
|
-
buildSymphonyRunStepRow,
|
|
1625
|
+
buildSymphonyRunRow: buildSharedSymphonyRunRow,
|
|
1626
|
+
buildSymphonyRunStepRow: buildSharedSymphonyRunStepRow,
|
|
1627
|
+
buildSymphonyRuntimeRunStepRow: buildSharedSymphonyRuntimeRunStepRow,
|
|
2785
1628
|
buildSymphonyAgents,
|
|
2786
|
-
buildSymphonyContacts,
|
|
1629
|
+
buildSymphonyContacts: buildSharedSymphonyContactRows,
|
|
2787
1630
|
withChatThreadRefs,
|
|
2788
1631
|
normalizeSymphonyRunPlan,
|
|
2789
1632
|
};
|