@undefineds.co/linx 0.3.13 → 0.3.15
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/lib/models.js +2 -2
- package/dist/lib/models.js.map +1 -1
- package/dist/lib/symphony/pod-projection.js +342 -10
- package/dist/lib/symphony/pod-projection.js.map +1 -1
- package/dist/skills/symphony/SKILL.md +50 -0
- package/package.json +2 -2
- package/vendor/agent-runtime/dist/reconciler.js +2 -2
- package/vendor/agent-runtime/dist/symphony.js +6 -5
- package/dist/lib/pi-adapter/auth.js +0 -68
- package/dist/lib/pi-adapter/auth.js.map +0 -1
- package/dist/lib/pi-adapter/pod-tools.js +0 -140
- package/dist/lib/pi-adapter/pod-tools.js.map +0 -1
package/dist/lib/models.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getBuiltinModels as getSharedBuiltinModels, } from '@undefineds.co/models/discovery';
|
|
2
|
-
import { aiConfigProviderRef, aiConfigModelUri, aiConfigProviderUri, aiConfigRepository, aiModelResource, aiModelTable, aiProviderResource, aiProviderTable, approvalResource, approvalTable, auditTable, buildAIConfigDisconnectPlan, buildAIConfigMutationPlan, buildAIConfigProviderStateMap, claimApprovalRequest, claimInputRequest, chatRepository, credentialResource, credentialTable, ContactClass, ContactType, auditResource, agentResource, agentTable, applySolidComunicaPatches, chatResource, chatTable, contactResource, contactTable, drizzle, eq, getAIConfigProviderFamilyIds, getAIConfigProviderIdsForBackend, getAIConfigProviderCatalog, getAIConfigProviderMetadata, getDefaultAIConfigCredentialId, grantResource, grantTable, ideaResource, inboxNotificationResource, inboxNotificationTable, inputRequestResource, inputRequestTable, initSolidResources, initSolidTables, extractSessionIdFromSessionRef, solidResources, solidSchema, messageResource, messageTable, issueResource, issueTable, taskResource, taskTable, deliveryResource, deliveryTable, runResource, runStepResource, runTable, runStepTable, sessionResource, sessionTable, skillResource, skillTable, normalizeAIConfigProviderId, normalizeAIConfigResourceId, sameAIConfigProviderFamily, selectAIConfigCredential, selectAIConfigCredentialForBackend, sessionRepository, threadRepository, threadResource, threadTable, } from '@undefineds.co/models';
|
|
3
|
-
export { ContactClass, ContactType, agentResource, agentTable, applySolidComunicaPatches, aiConfigModelUri, aiConfigProviderRef, aiConfigProviderUri, aiConfigRepository, aiModelResource, aiModelTable, aiProviderResource, aiProviderTable, approvalResource, approvalTable, auditTable, buildAIConfigDisconnectPlan, buildAIConfigMutationPlan, buildAIConfigProviderStateMap, claimApprovalRequest, claimInputRequest, chatRepository, chatResource, chatTable, contactResource, contactTable, credentialResource, credentialTable, auditResource, drizzle, eq, getAIConfigProviderFamilyIds, getAIConfigProviderIdsForBackend, getAIConfigProviderCatalog, getAIConfigProviderMetadata, getDefaultAIConfigCredentialId, grantResource, grantTable, ideaResource, inboxNotificationResource, inboxNotificationTable, inputRequestResource, inputRequestTable, extractSessionIdFromSessionRef, solidSchema, solidResources, initSolidResources, initSolidTables, messageResource, messageTable, issueResource, issueTable, taskResource, taskTable, deliveryResource, deliveryTable, runResource, runStepResource, runTable, runStepTable, sessionResource, sessionTable, skillResource, skillTable, normalizeAIConfigProviderId, normalizeAIConfigResourceId, sameAIConfigProviderFamily, selectAIConfigCredential, selectAIConfigCredentialForBackend, sessionRepository, threadRepository, threadResource, threadTable, };
|
|
2
|
+
import { aiConfigProviderRef, aiConfigModelUri, aiConfigProviderUri, aiConfigRepository, aiModelResource, aiModelTable, aiProviderResource, aiProviderTable, approvalResource, approvalTable, auditTable, buildAIConfigDisconnectPlan, buildAIConfigMutationPlan, buildAIConfigProviderStateMap, claimApprovalRequest, claimInputRequest, chatRepository, credentialResource, credentialTable, ContactClass, ContactType, auditResource, agentResource, agentTable, applySolidComunicaPatches, chatResource, chatTable, contactResource, contactTable, drizzle, eq, getAIConfigProviderFamilyIds, getAIConfigProviderIdsForBackend, getAIConfigProviderCatalog, getAIConfigProviderMetadata, getDefaultAIConfigCredentialId, grantResource, grantTable, ideaResource, inboxNotificationResource, inboxNotificationTable, inputRequestResource, inputRequestTable, initSolidResources, initSolidTables, extractSessionIdFromSessionRef, solidResources, solidSchema, messageResource, messageTable, issueResource, issueTable, taskResource, taskTable, deliveryResource, deliveryTable, reportResource, runResource, runStepResource, runTable, runStepTable, sessionResource, sessionTable, skillResource, skillTable, normalizeAIConfigProviderId, normalizeAIConfigResourceId, sameAIConfigProviderFamily, selectAIConfigCredential, selectAIConfigCredentialForBackend, sessionRepository, threadRepository, threadResource, threadTable, } from '@undefineds.co/models';
|
|
3
|
+
export { ContactClass, ContactType, agentResource, agentTable, applySolidComunicaPatches, aiConfigModelUri, aiConfigProviderRef, aiConfigProviderUri, aiConfigRepository, aiModelResource, aiModelTable, aiProviderResource, aiProviderTable, approvalResource, approvalTable, auditTable, buildAIConfigDisconnectPlan, buildAIConfigMutationPlan, buildAIConfigProviderStateMap, claimApprovalRequest, claimInputRequest, chatRepository, chatResource, chatTable, contactResource, contactTable, credentialResource, credentialTable, auditResource, drizzle, eq, getAIConfigProviderFamilyIds, getAIConfigProviderIdsForBackend, getAIConfigProviderCatalog, getAIConfigProviderMetadata, getDefaultAIConfigCredentialId, grantResource, grantTable, ideaResource, inboxNotificationResource, inboxNotificationTable, inputRequestResource, inputRequestTable, extractSessionIdFromSessionRef, solidSchema, solidResources, initSolidResources, initSolidTables, messageResource, messageTable, issueResource, issueTable, taskResource, taskTable, deliveryResource, deliveryTable, reportResource, runResource, runStepResource, runTable, runStepTable, sessionResource, sessionTable, skillResource, skillTable, normalizeAIConfigProviderId, normalizeAIConfigResourceId, sameAIConfigProviderFamily, selectAIConfigCredential, selectAIConfigCredentialForBackend, sessionRepository, threadRepository, threadResource, threadTable, };
|
|
4
4
|
export function getBuiltinModels() {
|
|
5
5
|
return getSharedBuiltinModels();
|
|
6
6
|
}
|
package/dist/lib/models.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/lib/models.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,IAAI,sBAAsB,GAC3C,MAAM,iCAAiC,CAAA;AAExC,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,2BAA2B,EAC3B,yBAAyB,EACzB,6BAA6B,EAC7B,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,WAAW,EACX,aAAa,EACb,aAAa,EACb,UAAU,EACV,yBAAyB,EACzB,YAAY,EACZ,SAAS,EACT,eAAe,EACf,YAAY,EACZ,OAAO,EACP,EAAE,EACF,4BAA4B,EAC5B,gCAAgC,EAChC,0BAA0B,EAC1B,2BAA2B,EAC3B,8BAA8B,EAC9B,aAAa,EACb,UAAU,EACV,YAAY,EACZ,yBAAyB,EACzB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,8BAA8B,EAC9B,cAAc,EACd,WAAW,EACX,eAAe,EACf,YAAY,EACZ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,aAAa,EACb,UAAU,EACV,2BAA2B,EAC3B,2BAA2B,EAC3B,0BAA0B,EAC1B,wBAAwB,EACxB,kCAAkC,EAClC,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,WAAW,
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/lib/models.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,IAAI,sBAAsB,GAC3C,MAAM,iCAAiC,CAAA;AAExC,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,2BAA2B,EAC3B,yBAAyB,EACzB,6BAA6B,EAC7B,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,WAAW,EACX,aAAa,EACb,aAAa,EACb,UAAU,EACV,yBAAyB,EACzB,YAAY,EACZ,SAAS,EACT,eAAe,EACf,YAAY,EACZ,OAAO,EACP,EAAE,EACF,4BAA4B,EAC5B,gCAAgC,EAChC,0BAA0B,EAC1B,2BAA2B,EAC3B,8BAA8B,EAC9B,aAAa,EACb,UAAU,EACV,YAAY,EACZ,yBAAyB,EACzB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,8BAA8B,EAC9B,cAAc,EACd,WAAW,EACX,eAAe,EACf,YAAY,EACZ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,WAAW,EACX,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,aAAa,EACb,UAAU,EACV,2BAA2B,EAC3B,2BAA2B,EAC3B,0BAA0B,EAC1B,wBAAwB,EACxB,kCAAkC,EAClC,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,WAAW,GAoCZ,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EACL,YAAY,EACZ,WAAW,EACX,aAAa,EACb,UAAU,EACV,yBAAyB,EACzB,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,2BAA2B,EAC3B,yBAAyB,EACzB,6BAA6B,EAC7B,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,SAAS,EACT,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,OAAO,EACP,EAAE,EACF,4BAA4B,EAC5B,gCAAgC,EAChC,0BAA0B,EAC1B,2BAA2B,EAC3B,8BAA8B,EAC9B,aAAa,EACb,UAAU,EACV,YAAY,EACZ,yBAAyB,EACzB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,8BAA8B,EAC9B,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,eAAe,EACf,YAAY,EACZ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,WAAW,EACX,eAAe,EACf,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,aAAa,EACb,UAAU,EACV,2BAA2B,EAC3B,2BAA2B,EAC3B,0BAA0B,EAC1B,wBAAwB,EACxB,kCAAkC,EAClC,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,WAAW,GACZ,CAAA;AAuCD,MAAM,UAAU,gBAAgB;IAC9B,OAAO,sBAAsB,EAAE,CAAA;AACjC,CAAC"}
|
|
@@ -7,7 +7,7 @@ import { decideThreadControlEvent } from '../../../vendor/agent-runtime/dist/thr
|
|
|
7
7
|
import { createLinxPodSyncScope } from '../../../vendor/agent-runtime/dist/sync.js';
|
|
8
8
|
import { insertExactRecordOnce, resolvePodResourceTemplateValue, upsertExactRecord, } from '@undefineds.co/drizzle-solid';
|
|
9
9
|
import { getDefaultPodDataSession } from '../pod-data-session.js';
|
|
10
|
-
import { ContactClass, ContactType, chatRepository, agentResource, contactResource, deliveryResource, ideaResource, issueResource, messageResource, runResource, runStepResource, sessionResource, taskResource, threadRepository, } from '../models.js';
|
|
10
|
+
import { ContactClass, ContactType, chatRepository, agentResource, contactResource, deliveryResource, ideaResource, issueResource, messageResource, reportResource, runResource, runStepResource, sessionResource, taskResource, threadRepository, } from '../models.js';
|
|
11
11
|
import { pathToWorkspaceUri } from '../pi-adapter/pod-mirror-mapping.js';
|
|
12
12
|
import { getSymphonyHome } from './archive.js';
|
|
13
13
|
const SYMPHONY_CHAT_ID = 'symphony';
|
|
@@ -16,6 +16,191 @@ 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 renderMarkdownList(items) {
|
|
101
|
+
const values = (items ?? []).map((item) => item.trim()).filter(Boolean);
|
|
102
|
+
return values.length > 0 ? values.map((item) => `- ${item}`).join('\n') : '- None recorded.';
|
|
103
|
+
}
|
|
104
|
+
function renderSymphonyIssueMarkdown(plan) {
|
|
105
|
+
const issue = plan.issue;
|
|
106
|
+
const sections = [
|
|
107
|
+
`# ${issue.title || buildSymphonyIssueId(issue)}`,
|
|
108
|
+
'',
|
|
109
|
+
'## Summary',
|
|
110
|
+
issue.description?.trim() || issue.title || 'No summary recorded.',
|
|
111
|
+
'',
|
|
112
|
+
'## Status',
|
|
113
|
+
`- Status: ${issue.status}`,
|
|
114
|
+
`- Priority: ${issue.priority}`,
|
|
115
|
+
`- Source: ${issue.source}`,
|
|
116
|
+
'',
|
|
117
|
+
'## Acceptance Criteria',
|
|
118
|
+
renderMarkdownList(plan.workers.flatMap((worker) => worker.taskRecord.acceptanceCriteria ?? [])),
|
|
119
|
+
'',
|
|
120
|
+
'## Tasks',
|
|
121
|
+
renderMarkdownList(plan.workers.map((worker) => `${worker.taskRecord.title}: ${worker.taskRecord.objective}`)),
|
|
122
|
+
'',
|
|
123
|
+
'## Source Context',
|
|
124
|
+
`- Chat: ${issue.chat ?? plan.session.chat ?? 'not recorded'}`,
|
|
125
|
+
`- Thread: ${issue.thread ?? plan.session.thread ?? 'not recorded'}`,
|
|
126
|
+
`- Messages: ${(issue.messages ?? []).join(', ') || 'not recorded'}`,
|
|
127
|
+
'',
|
|
128
|
+
'## Control Records',
|
|
129
|
+
`- Issue: ${issue.uri}`,
|
|
130
|
+
...plan.workers.flatMap((worker) => [
|
|
131
|
+
`- Task: ${worker.task}`,
|
|
132
|
+
`- Delivery: ${worker.delivery.uri}`,
|
|
133
|
+
`- Session: ${worker.session.uri}`,
|
|
134
|
+
]),
|
|
135
|
+
];
|
|
136
|
+
return sections.join('\n');
|
|
137
|
+
}
|
|
138
|
+
function renderSymphonyIdeaMarkdown(idea) {
|
|
139
|
+
return [
|
|
140
|
+
`# ${idea.summary || getSymphonyArchiveKey(idea.uri)}`,
|
|
141
|
+
'',
|
|
142
|
+
'## Input',
|
|
143
|
+
idea.input?.trim() || idea.summary || 'No input recorded.',
|
|
144
|
+
'',
|
|
145
|
+
'## Current Understanding',
|
|
146
|
+
idea.currentUnderstanding?.trim() || 'No current understanding recorded.',
|
|
147
|
+
'',
|
|
148
|
+
'## Open Questions',
|
|
149
|
+
renderMarkdownList(idea.openQuestions),
|
|
150
|
+
'',
|
|
151
|
+
'## Conflicts',
|
|
152
|
+
renderMarkdownList(idea.conflicts),
|
|
153
|
+
'',
|
|
154
|
+
'## Next Step',
|
|
155
|
+
idea.nextStep?.trim() || 'No next step recorded.',
|
|
156
|
+
'',
|
|
157
|
+
'## Source Context',
|
|
158
|
+
`- Status: ${idea.status}`,
|
|
159
|
+
`- Commitment: ${idea.commitment}`,
|
|
160
|
+
`- Chat: ${idea.chat ?? 'not recorded'}`,
|
|
161
|
+
`- Thread: ${idea.thread ?? 'not recorded'}`,
|
|
162
|
+
`- Messages: ${(idea.messages ?? []).join(', ') || 'not recorded'}`,
|
|
163
|
+
`- Idea: ${idea.uri}`,
|
|
164
|
+
].join('\n');
|
|
165
|
+
}
|
|
166
|
+
function renderSymphonyReportMarkdown(plan, worker, stage) {
|
|
167
|
+
const status = worker.session.status === 'failed' || stage === 'failed' ? 'failed' : 'completed';
|
|
168
|
+
const summary = status === 'completed'
|
|
169
|
+
? `${worker.taskRecord.title} completed.`
|
|
170
|
+
: `${worker.taskRecord.title} failed: ${worker.session.error ?? worker.delivery.error ?? 'worker did not complete successfully.'}`;
|
|
171
|
+
return [
|
|
172
|
+
`# ${worker.taskRecord.title} — ${status}`,
|
|
173
|
+
'',
|
|
174
|
+
'## Summary',
|
|
175
|
+
summary,
|
|
176
|
+
'',
|
|
177
|
+
'## Outcome',
|
|
178
|
+
`- Status: ${status}`,
|
|
179
|
+
`- Backend: ${worker.session.backend}`,
|
|
180
|
+
`- Agent: ${worker.session.target.agent ?? worker.delivery.targetAgent}`,
|
|
181
|
+
`- Auto mode session: ${worker.session.autoModeSessionId ?? 'not recorded'}`,
|
|
182
|
+
`- Exit code: ${worker.session.exitCode ?? 'not recorded'}`,
|
|
183
|
+
'',
|
|
184
|
+
'## Task',
|
|
185
|
+
worker.taskRecord.objective,
|
|
186
|
+
'',
|
|
187
|
+
'## Acceptance Criteria',
|
|
188
|
+
renderMarkdownList(worker.taskRecord.acceptanceCriteria),
|
|
189
|
+
'',
|
|
190
|
+
'## Linked Control Records',
|
|
191
|
+
`- Issue: ${buildSymphonyIssueId(plan.issue)}`,
|
|
192
|
+
`- Task: ${worker.task}`,
|
|
193
|
+
`- Delivery: ${worker.delivery.uri}`,
|
|
194
|
+
`- Session: ${worker.session.uri}`,
|
|
195
|
+
`- Run status: ${worker.session.status}`,
|
|
196
|
+
'',
|
|
197
|
+
...(worker.session.error || worker.delivery.error || worker.taskRecord.error ? [
|
|
198
|
+
'## Error',
|
|
199
|
+
worker.session.error ?? worker.delivery.error ?? worker.taskRecord.error ?? '',
|
|
200
|
+
'',
|
|
201
|
+
] : []),
|
|
202
|
+
].join('\n');
|
|
203
|
+
}
|
|
19
204
|
function normalizeSymphonyRunPlan(plan) {
|
|
20
205
|
const workers = Array.isArray(plan.workers) && plan.workers.length > 0
|
|
21
206
|
? plan.workers
|
|
@@ -119,12 +304,14 @@ async function createDefaultRuntime() {
|
|
|
119
304
|
issueResource: models.issueResource,
|
|
120
305
|
taskResource: models.taskResource,
|
|
121
306
|
deliveryResource: models.deliveryResource,
|
|
307
|
+
reportResource: models.reportResource,
|
|
122
308
|
runResource: models.runResource,
|
|
123
309
|
runStepResource: models.runStepResource,
|
|
124
310
|
agentResource: models.agentResource,
|
|
125
311
|
contactResource: models.contactTable,
|
|
126
312
|
auditResource: models.auditResource,
|
|
127
313
|
inboxNotificationResource: models.inboxNotificationResource,
|
|
314
|
+
writePodFile: writePodFileToSession,
|
|
128
315
|
};
|
|
129
316
|
}
|
|
130
317
|
function selectTargetChatIri(value, webId, plan) {
|
|
@@ -211,6 +398,13 @@ function buildSymphonyReportDeliveryIri(webId, worker) {
|
|
|
211
398
|
createdAt: safeDate(worker.session.completedAt ?? worker.session.updatedAt),
|
|
212
399
|
});
|
|
213
400
|
}
|
|
401
|
+
function buildSymphonyReportIri(webId, worker) {
|
|
402
|
+
return reportResource.buildIri(webId, {
|
|
403
|
+
id: `${getSymphonyArchiveKey(worker.session.uri)}-report`,
|
|
404
|
+
task: buildSymphonyTaskIri(webId, worker.task),
|
|
405
|
+
createdAt: safeDate(worker.session.completedAt ?? worker.session.updatedAt),
|
|
406
|
+
});
|
|
407
|
+
}
|
|
214
408
|
function buildSymphonyRunIri(webId, worker) {
|
|
215
409
|
return runResource.buildIri(webId, {
|
|
216
410
|
id: getSymphonyArchiveKey(worker.session.uri),
|
|
@@ -808,8 +1002,11 @@ function buildSymphonyIssueRow(plan, webId) {
|
|
|
808
1002
|
const updatedAt = safeDate(plan.issue.updatedAt);
|
|
809
1003
|
return {
|
|
810
1004
|
id: buildSymphonyIssueId(plan.issue),
|
|
1005
|
+
// File-primary: title remains a compact index label for existing Issue schemas.
|
|
1006
|
+
// The full problem statement and acceptance narrative live in issue.md.
|
|
811
1007
|
title: plan.issue.title,
|
|
812
|
-
|
|
1008
|
+
document: podFileUrlFromWebId(webId, buildSymphonyIssueDocumentPath(plan.issue)),
|
|
1009
|
+
description: undefined,
|
|
813
1010
|
status: plan.issue.status,
|
|
814
1011
|
priority: plan.issue.priority,
|
|
815
1012
|
labels: ['symphony'],
|
|
@@ -830,15 +1027,17 @@ function buildSymphonyIdeaRow(idea, webId) {
|
|
|
830
1027
|
return {
|
|
831
1028
|
id: getSymphonyArchiveKey(idea.uri),
|
|
832
1029
|
summary: idea.summary,
|
|
833
|
-
|
|
1030
|
+
document: podFileUrlFromWebId(webId, buildSymphonyIdeaDocumentPath(idea)),
|
|
1031
|
+
// File-primary: the source text lives in idea.md; TTL keeps only routing/index facts.
|
|
1032
|
+
input: undefined,
|
|
834
1033
|
status: idea.status,
|
|
835
1034
|
commitment: idea.commitment,
|
|
836
1035
|
affectedArea: idea.affectedArea,
|
|
837
|
-
currentUnderstanding:
|
|
838
|
-
openQuestions:
|
|
1036
|
+
currentUnderstanding: undefined,
|
|
1037
|
+
openQuestions: undefined,
|
|
839
1038
|
related: idea.relatedRecords,
|
|
840
|
-
conflicts:
|
|
841
|
-
nextStep:
|
|
1039
|
+
conflicts: undefined,
|
|
1040
|
+
nextStep: undefined,
|
|
842
1041
|
promotedTo: idea.promotedTo,
|
|
843
1042
|
chat: idea.chat,
|
|
844
1043
|
thread: idea.thread,
|
|
@@ -846,6 +1045,8 @@ function buildSymphonyIdeaRow(idea, webId) {
|
|
|
846
1045
|
createdBy: webId,
|
|
847
1046
|
metadata: {
|
|
848
1047
|
surface: 'symphony',
|
|
1048
|
+
filePrimary: true,
|
|
1049
|
+
documentPath: buildSymphonyIdeaDocumentPath(idea),
|
|
849
1050
|
...buildSymphonyArchiveMetadata({ idea: idea.uri }),
|
|
850
1051
|
},
|
|
851
1052
|
createdAt,
|
|
@@ -965,6 +1166,61 @@ function buildSymphonyDeliveryRow(plan, webId, worker) {
|
|
|
965
1166
|
updatedAt,
|
|
966
1167
|
};
|
|
967
1168
|
}
|
|
1169
|
+
function buildSymphonyReportRow(plan, webId, worker, stage) {
|
|
1170
|
+
const completedAt = safeDate(worker.session.completedAt ?? worker.session.updatedAt);
|
|
1171
|
+
const workerAgent = agentResource.buildIri(webId, {
|
|
1172
|
+
id: buildWorkerAgentId(worker.session.backend, worker.session.target.agent),
|
|
1173
|
+
});
|
|
1174
|
+
const run = buildSymphonyRunIri(webId, worker);
|
|
1175
|
+
const task = buildSymphonyTaskIri(webId, worker.task);
|
|
1176
|
+
const status = worker.session.status === 'failed' || stage === 'failed' ? 'failed' : 'completed';
|
|
1177
|
+
const summary = status === 'completed'
|
|
1178
|
+
? `${worker.taskRecord.title} completed.`
|
|
1179
|
+
: `${worker.taskRecord.title} failed: ${worker.session.error ?? worker.delivery.error ?? 'worker did not complete successfully.'}`;
|
|
1180
|
+
return {
|
|
1181
|
+
id: reportResource.buildId({
|
|
1182
|
+
id: `${getSymphonyArchiveKey(worker.session.uri)}-report`,
|
|
1183
|
+
task,
|
|
1184
|
+
createdAt: completedAt,
|
|
1185
|
+
}),
|
|
1186
|
+
reportKind: 'handoff',
|
|
1187
|
+
status: 'published',
|
|
1188
|
+
outcome: status === 'completed' ? 'accepted' : 'blocked',
|
|
1189
|
+
about: run,
|
|
1190
|
+
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1191
|
+
task,
|
|
1192
|
+
delivery: buildSymphonyDeliveryIri(webId, worker),
|
|
1193
|
+
run,
|
|
1194
|
+
thread: selectWorkerThreadIri(plan, webId, worker),
|
|
1195
|
+
evidence: [
|
|
1196
|
+
buildSymphonyRunStepIri(webId, worker, stage),
|
|
1197
|
+
],
|
|
1198
|
+
summary,
|
|
1199
|
+
actor: workerAgent,
|
|
1200
|
+
source: podFileUrlFromWebId(webId, buildSymphonyReportDocumentPath(worker)),
|
|
1201
|
+
metricFacts: {
|
|
1202
|
+
backend: worker.session.backend,
|
|
1203
|
+
agent: worker.session.target.agent,
|
|
1204
|
+
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1205
|
+
exitCode: worker.session.exitCode,
|
|
1206
|
+
},
|
|
1207
|
+
metadata: {
|
|
1208
|
+
surface: 'symphony',
|
|
1209
|
+
filePrimary: true,
|
|
1210
|
+
reportFile: buildSymphonyReportDocumentPath(worker),
|
|
1211
|
+
reportDelivery: buildSymphonyReportDeliveryIri(webId, worker),
|
|
1212
|
+
...buildSymphonyArchiveMetadata({
|
|
1213
|
+
issue: plan.issue.uri,
|
|
1214
|
+
task: worker.task,
|
|
1215
|
+
delivery: worker.delivery.uri,
|
|
1216
|
+
session: worker.session.uri,
|
|
1217
|
+
}),
|
|
1218
|
+
},
|
|
1219
|
+
createdAt: completedAt,
|
|
1220
|
+
publishedAt: completedAt,
|
|
1221
|
+
updatedAt: completedAt,
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
968
1224
|
function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
969
1225
|
const completedAt = safeDate(worker.session.completedAt ?? worker.session.updatedAt);
|
|
970
1226
|
const workerAgent = agentResource.buildIri(webId, {
|
|
@@ -972,6 +1228,7 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
972
1228
|
});
|
|
973
1229
|
const secretaryAgent = agentResource.buildIri(webId, { id: SYMPHONY_SECRETARY_AGENT_ID });
|
|
974
1230
|
const run = buildSymphonyRunIri(webId, worker);
|
|
1231
|
+
const report = buildSymphonyReportIri(webId, worker);
|
|
975
1232
|
const task = buildSymphonyTaskIri(webId, worker.task);
|
|
976
1233
|
const originalDelivery = buildSymphonyDeliveryIri(webId, worker);
|
|
977
1234
|
const status = worker.session.status === 'failed' || stage === 'failed' ? 'failed' : 'completed';
|
|
@@ -994,7 +1251,7 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
994
1251
|
targetThread: selectTargetThreadIri(plan.issue.thread ?? worker.session.target?.thread, webId, plan),
|
|
995
1252
|
targetSession: buildSymphonyControlSessionUri(webId, plan),
|
|
996
1253
|
actor: workerAgent,
|
|
997
|
-
object:
|
|
1254
|
+
object: report,
|
|
998
1255
|
objective: summary,
|
|
999
1256
|
payload: {
|
|
1000
1257
|
kind: 'symphony_report',
|
|
@@ -1003,6 +1260,7 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
1003
1260
|
issue: buildSymphonyIssueIri(webId, plan.issue),
|
|
1004
1261
|
task,
|
|
1005
1262
|
delivery: originalDelivery,
|
|
1263
|
+
report,
|
|
1006
1264
|
reportDelivery: buildSymphonyReportDeliveryIri(webId, worker),
|
|
1007
1265
|
session: buildSymphonyControlSessionUri(webId, plan),
|
|
1008
1266
|
run,
|
|
@@ -1011,6 +1269,7 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
1011
1269
|
autoModeSessionId: worker.session.autoModeSessionId,
|
|
1012
1270
|
exitCode: worker.session.exitCode,
|
|
1013
1271
|
error: worker.session.error ?? worker.delivery.error ?? worker.taskRecord.error,
|
|
1272
|
+
reportFile: buildSymphonyReportDocumentPath(worker),
|
|
1014
1273
|
evidence: {
|
|
1015
1274
|
statusMessage: buildSymphonyMessageUri(webId, plan, buildStatusMessageRow(plan, webId, stage)),
|
|
1016
1275
|
runStep: buildSymphonyRunStepIri(webId, worker, stage),
|
|
@@ -1030,6 +1289,8 @@ function buildSymphonyReportDeliveryRow(plan, webId, worker, stage) {
|
|
|
1030
1289
|
session: worker.session.uri,
|
|
1031
1290
|
}),
|
|
1032
1291
|
reportKind: 'worker-completion',
|
|
1292
|
+
filePrimary: true,
|
|
1293
|
+
reportFile: buildSymphonyReportDocumentPath(worker),
|
|
1033
1294
|
},
|
|
1034
1295
|
error: status === 'failed' ? worker.session.error ?? worker.delivery.error ?? worker.taskRecord.error : undefined,
|
|
1035
1296
|
createdAt: completedAt,
|
|
@@ -1371,6 +1632,7 @@ async function upsertIssue(db, runtime, row) {
|
|
|
1371
1632
|
await upsertExactRecord(db, runtime.issueResource, { id: row.id }, row, {
|
|
1372
1633
|
title: row.title,
|
|
1373
1634
|
description: row.description,
|
|
1635
|
+
document: row.document,
|
|
1374
1636
|
status: row.status,
|
|
1375
1637
|
priority: row.priority,
|
|
1376
1638
|
labels: row.labels,
|
|
@@ -1385,6 +1647,7 @@ async function upsertIssue(db, runtime, row) {
|
|
|
1385
1647
|
async function upsertIdea(db, runtime, row) {
|
|
1386
1648
|
await upsertExactRecord(db, runtime.ideaResource, { id: row.id }, row, {
|
|
1387
1649
|
summary: row.summary,
|
|
1650
|
+
document: row.document,
|
|
1388
1651
|
input: row.input,
|
|
1389
1652
|
status: row.status,
|
|
1390
1653
|
commitment: row.commitment,
|
|
@@ -1419,6 +1682,32 @@ async function upsertTask(db, runtime, row) {
|
|
|
1419
1682
|
updatedAt: row.updatedAt,
|
|
1420
1683
|
});
|
|
1421
1684
|
}
|
|
1685
|
+
async function upsertReport(db, runtime, row) {
|
|
1686
|
+
await upsertExactRecord(db, runtime.reportResource, {
|
|
1687
|
+
id: row.id,
|
|
1688
|
+
task: row.task,
|
|
1689
|
+
createdAt: row.createdAt,
|
|
1690
|
+
}, row, {
|
|
1691
|
+
reportKind: row.reportKind,
|
|
1692
|
+
status: row.status,
|
|
1693
|
+
outcome: row.outcome,
|
|
1694
|
+
about: row.about,
|
|
1695
|
+
issue: row.issue,
|
|
1696
|
+
task: row.task,
|
|
1697
|
+
delivery: row.delivery,
|
|
1698
|
+
run: row.run,
|
|
1699
|
+
thread: row.thread,
|
|
1700
|
+
evidence: row.evidence,
|
|
1701
|
+
summary: row.summary,
|
|
1702
|
+
reviewer: row.reviewer,
|
|
1703
|
+
actor: row.actor,
|
|
1704
|
+
source: row.source,
|
|
1705
|
+
metricFacts: row.metricFacts,
|
|
1706
|
+
metadata: row.metadata,
|
|
1707
|
+
publishedAt: row.publishedAt,
|
|
1708
|
+
updatedAt: row.updatedAt,
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1422
1711
|
async function upsertDelivery(db, runtime, row) {
|
|
1423
1712
|
await upsertExactRecord(db, runtime.deliveryResource, { id: row.id }, row, {
|
|
1424
1713
|
kind: row.kind,
|
|
@@ -1538,6 +1827,7 @@ function collectSymphonyProjectionResources(webId, plan, stages) {
|
|
|
1538
1827
|
add('runStep', buildSymphonyRunStepIri(webId, worker, stage));
|
|
1539
1828
|
}
|
|
1540
1829
|
if (worker.session.status === 'completed' || worker.session.status === 'failed') {
|
|
1830
|
+
add('report', buildSymphonyReportIri(webId, worker));
|
|
1541
1831
|
add('delivery', buildSymphonyReportDeliveryIri(webId, worker));
|
|
1542
1832
|
}
|
|
1543
1833
|
}
|
|
@@ -1612,6 +1902,7 @@ export async function persistSymphonyControlStateToPod(plan, options = {}) {
|
|
|
1612
1902
|
stages,
|
|
1613
1903
|
latestMessage,
|
|
1614
1904
|
shouldUpsertChat: !normalizedPlan.session.target?.chat,
|
|
1905
|
+
podSession,
|
|
1615
1906
|
}),
|
|
1616
1907
|
});
|
|
1617
1908
|
return {
|
|
@@ -1685,6 +1976,8 @@ function resolveProjectionResourceModel(runtime, kind) {
|
|
|
1685
1976
|
return runtime.taskResource;
|
|
1686
1977
|
if (kind === 'delivery')
|
|
1687
1978
|
return runtime.deliveryResource;
|
|
1979
|
+
if (kind === 'report')
|
|
1980
|
+
return runtime.reportResource;
|
|
1688
1981
|
if (kind === 'run')
|
|
1689
1982
|
return runtime.runResource;
|
|
1690
1983
|
if (kind === 'runStep')
|
|
@@ -1822,7 +2115,18 @@ export async function persistSymphonyIdeaToPod(idea, options = {}) {
|
|
|
1822
2115
|
},
|
|
1823
2116
|
},
|
|
1824
2117
|
{
|
|
1825
|
-
id: 'symphony.idea.
|
|
2118
|
+
id: 'symphony.idea.write-file-primary-document',
|
|
2119
|
+
kind: 'upsert',
|
|
2120
|
+
apply: async () => {
|
|
2121
|
+
await runtime.writePodFile?.(podSession, {
|
|
2122
|
+
path: buildSymphonyIdeaDocumentPath(idea),
|
|
2123
|
+
content: renderSymphonyIdeaMarkdown(idea),
|
|
2124
|
+
contentType: 'text/markdown; charset=utf-8',
|
|
2125
|
+
});
|
|
2126
|
+
},
|
|
2127
|
+
},
|
|
2128
|
+
{
|
|
2129
|
+
id: 'symphony.idea.upsert-meta',
|
|
1826
2130
|
kind: 'upsert',
|
|
1827
2131
|
apply: () => upsertIdea(db, runtime, buildSymphonyIdeaRow(idea, podSession.webId)),
|
|
1828
2132
|
},
|
|
@@ -2102,6 +2406,7 @@ function buildSymphonyProjectionOperations(input) {
|
|
|
2102
2406
|
input.runtime.issueResource,
|
|
2103
2407
|
input.runtime.taskResource,
|
|
2104
2408
|
input.runtime.deliveryResource,
|
|
2409
|
+
input.runtime.reportResource,
|
|
2105
2410
|
input.runtime.runResource,
|
|
2106
2411
|
input.runtime.runStepResource,
|
|
2107
2412
|
input.runtime.agentResource,
|
|
@@ -2112,7 +2417,18 @@ function buildSymphonyProjectionOperations(input) {
|
|
|
2112
2417
|
},
|
|
2113
2418
|
},
|
|
2114
2419
|
{
|
|
2115
|
-
id: 'symphony.
|
|
2420
|
+
id: 'symphony.write-file-primary-documents',
|
|
2421
|
+
kind: 'upsert',
|
|
2422
|
+
apply: async () => {
|
|
2423
|
+
await input.runtime.writePodFile?.(input.podSession, {
|
|
2424
|
+
path: buildSymphonyIssueDocumentPath(input.plan.issue),
|
|
2425
|
+
content: renderSymphonyIssueMarkdown(input.plan),
|
|
2426
|
+
contentType: 'text/markdown; charset=utf-8',
|
|
2427
|
+
});
|
|
2428
|
+
},
|
|
2429
|
+
},
|
|
2430
|
+
{
|
|
2431
|
+
id: 'symphony.upsert-issue-meta',
|
|
2116
2432
|
kind: 'upsert',
|
|
2117
2433
|
apply: () => upsertIssue(input.db, input.runtime, buildSymphonyIssueRow(input.plan, input.webId)),
|
|
2118
2434
|
},
|
|
@@ -2185,6 +2501,22 @@ function buildSymphonyReportOperations(input) {
|
|
|
2185
2501
|
}
|
|
2186
2502
|
const terminalStage = input.stage;
|
|
2187
2503
|
return input.plan.workers.flatMap((worker, index) => [
|
|
2504
|
+
{
|
|
2505
|
+
id: `symphony.write-report-file:${index + 1}`,
|
|
2506
|
+
kind: 'upsert',
|
|
2507
|
+
apply: async () => {
|
|
2508
|
+
await input.runtime.writePodFile?.(input.podSession, {
|
|
2509
|
+
path: buildSymphonyReportDocumentPath(worker),
|
|
2510
|
+
content: renderSymphonyReportMarkdown(input.plan, worker, terminalStage),
|
|
2511
|
+
contentType: 'text/markdown; charset=utf-8',
|
|
2512
|
+
});
|
|
2513
|
+
},
|
|
2514
|
+
},
|
|
2515
|
+
{
|
|
2516
|
+
id: `symphony.upsert-report:${index + 1}`,
|
|
2517
|
+
kind: 'upsert',
|
|
2518
|
+
apply: () => upsertReport(input.db, input.runtime, buildSymphonyReportRow(input.plan, input.webId, worker, terminalStage)),
|
|
2519
|
+
},
|
|
2188
2520
|
{
|
|
2189
2521
|
id: `symphony.upsert-report-delivery:${index + 1}`,
|
|
2190
2522
|
kind: 'upsert',
|