@undefineds.co/linx 0.3.5 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/README.md +58 -23
  2. package/dist/generated/version.js +1 -1
  3. package/dist/generated/version.js.map +1 -1
  4. package/dist/index.js +336 -162
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/account-session.js +4 -8
  7. package/dist/lib/account-session.js.map +1 -1
  8. package/dist/lib/ai-command.js +228 -178
  9. package/dist/lib/ai-command.js.map +1 -1
  10. package/dist/lib/auto-mode/archive.js +38 -7
  11. package/dist/lib/auto-mode/archive.js.map +1 -1
  12. package/dist/lib/auto-mode/auth.js.map +1 -1
  13. package/dist/lib/auto-mode/display.js +71 -45
  14. package/dist/lib/auto-mode/display.js.map +1 -1
  15. package/dist/lib/auto-mode/format.js +9 -7
  16. package/dist/lib/auto-mode/format.js.map +1 -1
  17. package/dist/lib/auto-mode/hooks/claude.js +12 -2
  18. package/dist/lib/auto-mode/hooks/claude.js.map +1 -1
  19. package/dist/lib/auto-mode/hooks/codex.js +17 -7
  20. package/dist/lib/auto-mode/hooks/codex.js.map +1 -1
  21. package/dist/lib/auto-mode/hooks/index.js +28 -8
  22. package/dist/lib/auto-mode/hooks/index.js.map +1 -1
  23. package/dist/lib/auto-mode/pod-ai.js +20 -37
  24. package/dist/lib/auto-mode/pod-ai.js.map +1 -1
  25. package/dist/lib/auto-mode/pod-approval.js +124 -195
  26. package/dist/lib/auto-mode/pod-approval.js.map +1 -1
  27. package/dist/lib/auto-mode/pod-persistence.js +169 -90
  28. package/dist/lib/auto-mode/pod-persistence.js.map +1 -1
  29. package/dist/lib/auto-mode/runner.js +683 -81
  30. package/dist/lib/auto-mode/runner.js.map +1 -1
  31. package/dist/lib/auto-mode/secretary.js +186 -41
  32. package/dist/lib/auto-mode/secretary.js.map +1 -1
  33. package/dist/lib/auto-mode-command.js +32 -32
  34. package/dist/lib/auto-mode-command.js.map +1 -1
  35. package/dist/lib/chat-api.js +242 -50
  36. package/dist/lib/chat-api.js.map +1 -1
  37. package/dist/lib/codex-plugin/bridge.js +164 -17
  38. package/dist/lib/codex-plugin/bridge.js.map +1 -1
  39. package/dist/lib/codex-plugin/codex-native-proxy.js +370 -34
  40. package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
  41. package/dist/lib/credentials-store.js +33 -42
  42. package/dist/lib/credentials-store.js.map +1 -1
  43. package/dist/lib/linx-cloud-errors.js +61 -0
  44. package/dist/lib/linx-cloud-errors.js.map +1 -0
  45. package/dist/lib/linx-tui-contract.js +8 -5
  46. package/dist/lib/linx-tui-contract.js.map +1 -1
  47. package/dist/lib/login-command.js +9 -2
  48. package/dist/lib/login-command.js.map +1 -1
  49. package/dist/lib/models.js +3 -20
  50. package/dist/lib/models.js.map +1 -1
  51. package/dist/lib/oidc-auth.js +143 -17
  52. package/dist/lib/oidc-auth.js.map +1 -1
  53. package/dist/lib/oidc-session-storage.js +2 -6
  54. package/dist/lib/oidc-session-storage.js.map +1 -1
  55. package/dist/lib/pi-adapter/auto-input-controller.js +988 -0
  56. package/dist/lib/pi-adapter/auto-input-controller.js.map +1 -0
  57. package/dist/lib/pi-adapter/backend-command.js +2 -0
  58. package/dist/lib/pi-adapter/backend-command.js.map +1 -0
  59. package/dist/lib/pi-adapter/backend-credentials.js +80 -0
  60. package/dist/lib/pi-adapter/backend-credentials.js.map +1 -0
  61. package/dist/lib/pi-adapter/branding.js +246 -108
  62. package/dist/lib/pi-adapter/branding.js.map +1 -1
  63. package/dist/lib/pi-adapter/control-state.js +72 -0
  64. package/dist/lib/pi-adapter/control-state.js.map +1 -0
  65. package/dist/lib/pi-adapter/interactive.js +2634 -30
  66. package/dist/lib/pi-adapter/interactive.js.map +1 -1
  67. package/dist/lib/pi-adapter/pod-approval.js +382 -210
  68. package/dist/lib/pi-adapter/pod-approval.js.map +1 -1
  69. package/dist/lib/pi-adapter/pod-mirror-mapping.js +71 -17
  70. package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +1 -1
  71. package/dist/lib/pi-adapter/pod-mirror.js +531 -64
  72. package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
  73. package/dist/lib/pi-adapter/pod-native.js +81 -85
  74. package/dist/lib/pi-adapter/pod-native.js.map +1 -1
  75. package/dist/lib/pi-adapter/pod-status-output.js +54 -0
  76. package/dist/lib/pi-adapter/pod-status-output.js.map +1 -0
  77. package/dist/lib/pi-adapter/runtime.js +458 -228
  78. package/dist/lib/pi-adapter/runtime.js.map +1 -1
  79. package/dist/lib/pi-adapter/session-control.js +509 -0
  80. package/dist/lib/pi-adapter/session-control.js.map +1 -0
  81. package/dist/lib/pi-adapter/session.js +35 -22
  82. package/dist/lib/pi-adapter/session.js.map +1 -1
  83. package/dist/lib/pi-adapter/stream.js +89 -32
  84. package/dist/lib/pi-adapter/stream.js.map +1 -1
  85. package/dist/lib/pi-adapter/sync-recovery.js +89 -0
  86. package/dist/lib/pi-adapter/sync-recovery.js.map +1 -0
  87. package/dist/lib/pi-adapter/web-fetch.js +13 -14
  88. package/dist/lib/pi-adapter/web-fetch.js.map +1 -1
  89. package/dist/lib/pod-chat-store.js +254 -78
  90. package/dist/lib/pod-chat-store.js.map +1 -1
  91. package/dist/lib/pod-data-session.js +156 -35
  92. package/dist/lib/pod-data-session.js.map +1 -1
  93. package/dist/lib/solid-auth-store.js +27 -0
  94. package/dist/lib/solid-auth-store.js.map +1 -0
  95. package/dist/lib/solid-auth.js +2 -4
  96. package/dist/lib/solid-auth.js.map +1 -1
  97. package/dist/lib/solid-client-credentials-login.js +100 -0
  98. package/dist/lib/solid-client-credentials-login.js.map +1 -0
  99. package/dist/lib/solid-local-store.js +31 -0
  100. package/dist/lib/solid-local-store.js.map +1 -0
  101. package/dist/lib/symphony/archive.js +328 -18
  102. package/dist/lib/symphony/archive.js.map +1 -1
  103. package/dist/lib/symphony/pod-projection.js +2222 -0
  104. package/dist/lib/symphony/pod-projection.js.map +1 -0
  105. package/dist/lib/symphony-command.js +602 -178
  106. package/dist/lib/symphony-command.js.map +1 -1
  107. package/dist/lib/sync-checkpoint-store.js +74 -0
  108. package/dist/lib/sync-checkpoint-store.js.map +1 -0
  109. package/dist/skills/symphony/SKILL.md +665 -0
  110. package/package.json +15 -9
  111. package/vendor/agent-runtime/dist/agent-runtime.d.ts +137 -0
  112. package/vendor/agent-runtime/dist/agent-runtime.js +211 -0
  113. package/vendor/agent-runtime/dist/auto-mode.d.ts +78 -13
  114. package/vendor/agent-runtime/dist/auto-mode.js +288 -31
  115. package/vendor/agent-runtime/dist/control-plane.d.ts +28 -0
  116. package/vendor/agent-runtime/dist/control-plane.js +79 -0
  117. package/vendor/agent-runtime/dist/file-sync.d.ts +157 -0
  118. package/vendor/agent-runtime/dist/file-sync.js +314 -0
  119. package/vendor/agent-runtime/dist/index.d.ts +7 -0
  120. package/vendor/agent-runtime/dist/index.js +7 -0
  121. package/vendor/agent-runtime/dist/reconciler.d.ts +117 -0
  122. package/vendor/agent-runtime/dist/reconciler.js +361 -0
  123. package/vendor/agent-runtime/dist/symphony.d.ts +128 -8
  124. package/vendor/agent-runtime/dist/symphony.js +362 -57
  125. package/vendor/agent-runtime/dist/sync.d.ts +271 -0
  126. package/vendor/agent-runtime/dist/sync.js +550 -0
  127. package/vendor/agent-runtime/dist/thread-reconciler-controller.d.ts +58 -0
  128. package/vendor/agent-runtime/dist/thread-reconciler-controller.js +137 -0
  129. package/vendor/agent-runtime/dist/turn-controller.js +2 -2
  130. package/vendor/agent-runtime/dist/wake-scheduler.d.ts +67 -0
  131. package/vendor/agent-runtime/dist/wake-scheduler.js +194 -0
  132. package/vendor/agent-runtime/package.json +8 -1
  133. package/vendor/pi-web-access/CHANGELOG.md +387 -0
  134. package/vendor/pi-web-access/LICENSE +21 -0
  135. package/vendor/pi-web-access/README.md +352 -0
  136. package/vendor/pi-web-access/activity.ts +101 -0
  137. package/vendor/pi-web-access/banner.png +0 -0
  138. package/vendor/pi-web-access/chrome-cookies.ts +322 -0
  139. package/vendor/pi-web-access/code-search.ts +107 -0
  140. package/vendor/pi-web-access/curator-page.ts +3359 -0
  141. package/vendor/pi-web-access/curator-server.ts +605 -0
  142. package/vendor/pi-web-access/exa.ts +520 -0
  143. package/vendor/pi-web-access/extract.ts +641 -0
  144. package/vendor/pi-web-access/gemini-api.ts +112 -0
  145. package/vendor/pi-web-access/gemini-search.ts +361 -0
  146. package/vendor/pi-web-access/gemini-url-context.ts +126 -0
  147. package/vendor/pi-web-access/gemini-web-config.ts +52 -0
  148. package/vendor/pi-web-access/gemini-web.ts +396 -0
  149. package/vendor/pi-web-access/github-api.ts +196 -0
  150. package/vendor/pi-web-access/github-extract.ts +634 -0
  151. package/vendor/pi-web-access/index.ts +2346 -0
  152. package/vendor/pi-web-access/package.json +45 -0
  153. package/vendor/pi-web-access/pdf-extract.ts +192 -0
  154. package/vendor/pi-web-access/perplexity.ts +195 -0
  155. package/vendor/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
  156. package/vendor/pi-web-access/rsc-extract.ts +338 -0
  157. package/vendor/pi-web-access/skills/librarian/SKILL.md +195 -0
  158. package/vendor/pi-web-access/storage.ts +72 -0
  159. package/vendor/pi-web-access/summary-review.ts +276 -0
  160. package/vendor/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +41 -0
  161. package/vendor/pi-web-access/test/pdf-extract.test.mjs +95 -0
  162. package/vendor/pi-web-access/utils.ts +44 -0
  163. package/vendor/pi-web-access/video-extract.ts +378 -0
  164. package/vendor/pi-web-access/youtube-extract.ts +310 -0
  165. package/dist/lib/pi-adapter/auth.js +0 -68
  166. package/dist/lib/pi-adapter/auth.js.map +0 -1
  167. package/dist/lib/pi-adapter/pod-tools.js +0 -140
  168. package/dist/lib/pi-adapter/pod-tools.js.map +0 -1
  169. package/dist/skills/drizzle-solid/SKILL.md +0 -340
  170. package/dist/skills/pod-storage/SKILL.md +0 -100
  171. package/dist/skills/solid-modeling/SKILL.md +0 -274
  172. package/dist/skills/xpod-componentsjs/SKILL.md +0 -284
@@ -1,11 +1,20 @@
1
+ import { summarizeReconcileDecision } from './reconciler.js';
2
+ import { decideThreadControlEvent } from './thread-reconciler-controller.js';
1
3
  export const SYMPHONY_HOME_DIRNAME = 'symphony';
4
+ export const SYMPHONY_IDEAS_DIRNAME = 'ideas';
2
5
  export const SYMPHONY_ISSUES_DIRNAME = 'issues';
6
+ export const SYMPHONY_TASKS_DIRNAME = 'tasks';
3
7
  export const SYMPHONY_DELIVERIES_DIRNAME = 'deliveries';
4
8
  export const SYMPHONY_SESSIONS_DIRNAME = 'sessions';
9
+ export const SYMPHONY_IDEA_FILE_NAME = 'idea.json';
5
10
  export const SYMPHONY_ISSUE_FILE_NAME = 'issue.json';
11
+ export const SYMPHONY_TASK_FILE_NAME = 'task.json';
6
12
  export const SYMPHONY_DELIVERY_FILE_NAME = 'delivery.json';
7
13
  export const SYMPHONY_SESSION_FILE_NAME = 'session.json';
8
14
  const SYMPHONY_URI_PREFIX = 'urn:undefineds:linx';
15
+ export function createSymphonyIdeaUri(options = {}) {
16
+ return createSymphonyResourceUri('idea', options);
17
+ }
9
18
  export function createSymphonyIssueUri(options = {}) {
10
19
  return createSymphonyResourceUri('issue', options);
11
20
  }
@@ -20,16 +29,22 @@ export function createSymphonySessionUri(options = {}) {
20
29
  }
21
30
  export function getSymphonyArchiveRelativePaths(uri, kind) {
22
31
  const key = getSymphonyArchiveKey(uri);
23
- const dirName = kind === 'issue'
24
- ? SYMPHONY_ISSUES_DIRNAME
25
- : kind === 'delivery'
26
- ? SYMPHONY_DELIVERIES_DIRNAME
27
- : SYMPHONY_SESSIONS_DIRNAME;
28
- const fileName = kind === 'issue'
29
- ? SYMPHONY_ISSUE_FILE_NAME
30
- : kind === 'delivery'
31
- ? SYMPHONY_DELIVERY_FILE_NAME
32
- : SYMPHONY_SESSION_FILE_NAME;
32
+ const dirNameByKind = {
33
+ idea: SYMPHONY_IDEAS_DIRNAME,
34
+ issue: SYMPHONY_ISSUES_DIRNAME,
35
+ task: SYMPHONY_TASKS_DIRNAME,
36
+ delivery: SYMPHONY_DELIVERIES_DIRNAME,
37
+ session: SYMPHONY_SESSIONS_DIRNAME,
38
+ };
39
+ const fileNameByKind = {
40
+ idea: SYMPHONY_IDEA_FILE_NAME,
41
+ issue: SYMPHONY_ISSUE_FILE_NAME,
42
+ task: SYMPHONY_TASK_FILE_NAME,
43
+ delivery: SYMPHONY_DELIVERY_FILE_NAME,
44
+ session: SYMPHONY_SESSION_FILE_NAME,
45
+ };
46
+ const dirName = dirNameByKind[kind];
47
+ const fileName = fileNameByKind[kind];
33
48
  return {
34
49
  dir: `${dirName}/${key}`,
35
50
  file: `${dirName}/${key}/${fileName}`,
@@ -43,18 +58,26 @@ export function createRunPlan(input) {
43
58
  const objective = normalizeRequiredText(input.objective, 'objective');
44
59
  const title = normalizeOptionalText(input.title) ?? createSymphonyTitle(objective);
45
60
  const acceptanceCriteria = normalizeSymphonyAcceptanceCriteria(input.acceptanceCriteria);
46
- const chatThread = normalizeSymphonyChatThreadRef(input);
61
+ const issuer = normalizeSymphonyIssuer(input);
62
+ const workerSpecs = normalizeSymphonyWorkerSpecs(input);
63
+ const primaryTarget = workerSpecs[0].target;
64
+ const chatThread = normalizeSymphonyChatThreadRef({
65
+ chat: normalizeOptionalText(input.chat) ?? primaryTarget.chat,
66
+ thread: normalizeOptionalText(input.thread) ?? primaryTarget.thread,
67
+ messages: input.messages ?? primaryTarget.messages,
68
+ });
47
69
  const workspace = {
48
70
  path: normalizeRequiredText(input.workspacePath, 'workspacePath'),
49
71
  kind: input.workspaceKind ?? 'folder',
50
72
  ...(normalizeOptionalText(input.repository) ? { repository: normalizeOptionalText(input.repository) } : {}),
51
73
  ...(normalizeOptionalText(input.branch) ? { branch: normalizeOptionalText(input.branch) } : {}),
52
74
  ...(normalizeOptionalText(input.worktree) ? { worktree: normalizeOptionalText(input.worktree) } : {}),
75
+ ...(normalizeOptionalText(input.workspaceUri) ? { workspaceUri: normalizeOptionalText(input.workspaceUri) } : {}),
76
+ ...(normalizeOptionalText(input.baseRevision) ? { baseRevision: normalizeOptionalText(input.baseRevision) } : {}),
77
+ environment: normalizeSymphonyWorkerEnvironment(input.environment, input.backend),
53
78
  };
54
79
  const issueUri = createSymphonyIssueUri(uriOptions);
55
- const taskUri = createTaskUri(uriOptions);
56
- const deliveryUri = createSymphonyDeliveryUri(uriOptions);
57
- const sessionUri = createSymphonySessionUri(uriOptions);
80
+ const workerUris = workerSpecs.map((_, index) => createSymphonyWorkerUris(uriOptions, index));
58
81
  const issue = {
59
82
  uri: issueUri,
60
83
  title,
@@ -62,59 +85,139 @@ export function createRunPlan(input) {
62
85
  status: 'open',
63
86
  priority: 'medium',
64
87
  source: 'cli',
65
- tasks: [taskUri],
66
- ...chatThread,
67
- createdAt: timestamp,
68
- updatedAt: timestamp,
69
- };
70
- const session = {
71
- uri: sessionUri,
72
- issue: issueUri,
73
- task: taskUri,
74
- delivery: deliveryUri,
75
- backend: input.backend,
76
- mode: input.mode,
77
- status: 'planned',
78
- cwd: workspace.path,
79
- ...(normalizeOptionalText(input.model) ? { model: normalizeOptionalText(input.model) } : {}),
88
+ issuer,
89
+ tasks: workerUris.map((item) => item.task),
90
+ deliveries: workerUris.map((item) => item.delivery),
91
+ sessions: workerUris.map((item) => item.session),
80
92
  ...chatThread,
81
93
  createdAt: timestamp,
82
94
  updatedAt: timestamp,
83
95
  };
84
- const delivery = {
85
- uri: deliveryUri,
86
- issue: issueUri,
87
- task: taskUri,
88
- type: 'task_dispatch',
89
- status: 'pending',
90
- sourceAgent: 'ai-secretary',
91
- targetBackend: input.backend,
92
- targetAgent: `${input.backend}-worker`,
93
- projection: {
94
- runtimeRole: 'user',
95
- prompt: renderSymphonyRuntimePrompt({
96
- issue,
97
- task: taskUri,
98
- objective,
99
- acceptanceCriteria,
100
- workspace,
101
- backend: input.backend,
102
- mode: input.mode,
103
- session: sessionUri,
104
- }),
96
+ const workers = workerSpecs.map((spec, index) => {
97
+ const uris = workerUris[index];
98
+ const target = spec.target;
99
+ const workerWorkspace = normalizeSymphonyWorkerWorkspace(workspace, spec.workspace, target.backend);
100
+ const workerObjective = spec.objective;
101
+ const workerTitle = spec.title ?? createSymphonyTitle(workerObjective);
102
+ const workerAcceptanceCriteria = spec.acceptanceCriteria.length > 0
103
+ ? spec.acceptanceCriteria
104
+ : acceptanceCriteria;
105
+ const workerChatThread = normalizeSymphonyChatThreadRef({
106
+ chat: target.chat ?? chatThread.chat,
107
+ thread: target.thread ?? chatThread.thread,
108
+ messages: target.messages ?? chatThread.messages,
109
+ });
110
+ const targetAgent = target.agent ?? `${target.backend}-worker`;
111
+ const dispatchReconciler = createSymphonyDispatchReconcilerState({
112
+ issue: issueUri,
113
+ task: uris.task,
114
+ delivery: uris.delivery,
115
+ session: uris.session,
116
+ chat: workerChatThread.chat,
117
+ thread: workerChatThread.thread,
118
+ targetAgent,
119
+ now,
120
+ randomId: `${randomId}-w${index + 1}-dispatch`,
121
+ });
122
+ const taskRecord = {
123
+ uri: uris.task,
124
+ issue: issueUri,
125
+ title: workerTitle,
126
+ objective: workerObjective,
127
+ acceptanceCriteria: workerAcceptanceCriteria,
128
+ status: 'pending',
129
+ target,
130
+ backend: target.backend,
131
+ ...(target.agent ? { agent: target.agent } : {}),
132
+ delivery: uris.delivery,
133
+ session: uris.session,
134
+ reconciler: dispatchReconciler,
135
+ ...workerChatThread,
136
+ createdAt: timestamp,
137
+ updatedAt: timestamp,
138
+ };
139
+ const session = {
140
+ uri: uris.session,
141
+ issue: issueUri,
142
+ task: uris.task,
143
+ delivery: uris.delivery,
144
+ backend: target.backend,
145
+ mode: input.mode,
146
+ ...(input.secretaryAutoEnabled !== undefined ? { secretaryAutoEnabled: input.secretaryAutoEnabled } : {}),
147
+ status: 'planned',
148
+ cwd: workerWorkspace.path,
149
+ workspace: workerWorkspace,
150
+ target,
151
+ ...(spec.model ? { model: spec.model } : {}),
152
+ ...(spec.supervisor ? { supervisor: spec.supervisor } : {}),
153
+ reconciler: dispatchReconciler,
154
+ ...workerChatThread,
155
+ createdAt: timestamp,
156
+ updatedAt: timestamp,
157
+ };
158
+ const delivery = {
159
+ uri: uris.delivery,
160
+ issue: issueUri,
161
+ task: uris.task,
162
+ type: 'task_dispatch',
163
+ status: 'pending',
164
+ sourceAgent: '__secretary__',
165
+ targetBackend: target.backend,
166
+ targetAgent,
167
+ target,
168
+ projection: {
169
+ runtimeRole: 'user',
170
+ prompt: renderSymphonyRuntimePrompt({
171
+ issue,
172
+ task: uris.task,
173
+ objective: workerObjective,
174
+ acceptanceCriteria: workerAcceptanceCriteria,
175
+ workspace: workerWorkspace,
176
+ backend: target.backend,
177
+ mode: input.mode,
178
+ secretaryAutoEnabled: input.secretaryAutoEnabled,
179
+ session: uris.session,
180
+ target,
181
+ issuer,
182
+ workerIndex: index + 1,
183
+ workerCount: workerSpecs.length,
184
+ }),
185
+ },
186
+ session: uris.session,
187
+ reconciler: dispatchReconciler,
188
+ ...workerChatThread,
189
+ createdAt: timestamp,
190
+ updatedAt: timestamp,
191
+ };
192
+ return { task: uris.task, taskRecord, delivery, session };
193
+ });
194
+ const primary = workers[0];
195
+ return { issue, task: primary.task, taskRecord: primary.taskRecord, delivery: primary.delivery, session: primary.session, workers };
196
+ }
197
+ export function appendSymphonyReconcilerDecision(record, decision) {
198
+ const summary = isReconcileDecisionSummary(decision)
199
+ ? decision
200
+ : summarizeReconcileDecision(decision);
201
+ return {
202
+ ...record,
203
+ reconciler: {
204
+ decisions: [
205
+ ...(record.reconciler?.decisions ?? []),
206
+ summary,
207
+ ],
105
208
  },
106
- session: sessionUri,
107
- ...chatThread,
108
- createdAt: timestamp,
109
- updatedAt: timestamp,
110
209
  };
111
- return { issue, task: taskUri, delivery, session };
112
210
  }
113
211
  export function renderSymphonyRuntimePrompt(input) {
114
212
  const acceptanceCriteria = normalizeSymphonyAcceptanceCriteria(input.acceptanceCriteria);
115
213
  const criteria = acceptanceCriteria.length > 0
116
214
  ? acceptanceCriteria.map((item, index) => `${index + 1}. ${item}`).join('\n')
117
- : '1. Complete the objective and report concrete verification evidence.';
215
+ : [
216
+ '1. Infer concrete acceptance criteria from the delegated objective, source context, and repository state.',
217
+ '2. If the objective cannot be made testable without a user decision, report the blocker to AI Secretary instead of guessing.',
218
+ '3. Otherwise complete the objective and report concrete verification evidence.',
219
+ ].join('\n');
220
+ const workThread = normalizeOptionalText(input.target?.thread ?? input.issue?.thread ?? input.issuer?.thread);
118
221
  return [
119
222
  '# LinX Symphony Task',
120
223
  '',
@@ -122,9 +225,32 @@ export function renderSymphonyRuntimePrompt(input) {
122
225
  `Task URI: ${input.task}`,
123
226
  `Session URI: ${input.session}`,
124
227
  `Backend: ${input.backend}`,
125
- `Mode: ${input.mode}`,
228
+ `Worker mode: ${input.mode}`,
229
+ `Secretary auto: ${input.secretaryAutoEnabled === true ? 'on' : 'off'}`,
230
+ ...(input.issuer?.webId ? [`Issuer WebID: ${input.issuer.webId}`] : []),
231
+ ...(input.issuer?.agent ? [`Issuer agent: ${input.issuer.agent}`] : []),
232
+ ...(input.issuer?.chat ? [`Issuer chat: ${input.issuer.chat}`] : []),
233
+ ...(input.issuer?.thread ? [`Issuer thread: ${input.issuer.thread}`] : []),
234
+ ...(input.target?.chat ? [`Target chat: ${input.target.chat}`] : []),
235
+ ...(input.target?.thread ? [`Target thread: ${input.target.thread}`] : []),
236
+ ...(input.target?.agent ? [`Target agent: ${input.target.agent}`] : []),
237
+ ...(input.workerIndex && input.workerCount ? [`Worker: ${input.workerIndex}/${input.workerCount}`] : []),
238
+ ...(workThread ? [`Work thread: ${workThread}`] : []),
126
239
  `Workspace: ${input.workspace.path}`,
127
240
  `Workspace kind: ${input.workspace.kind}`,
241
+ ...(input.workspace.workspaceUri ? [`Workspace URI: ${input.workspace.workspaceUri}`] : []),
242
+ ...(input.workspace.repository ? [`Workspace repository: ${input.workspace.repository}`] : []),
243
+ ...(input.workspace.branch ? [`Workspace branch: ${input.workspace.branch}`] : []),
244
+ ...(input.workspace.baseRevision ? [`Workspace base revision: ${input.workspace.baseRevision}`] : []),
245
+ ...(input.workspace.environment ? [`Workspace environment: ${formatSymphonyWorkerEnvironment(input.workspace.environment)}`] : []),
246
+ '',
247
+ '## Runtime Space Contract',
248
+ '- Shared control space: Issue, Task, Delivery, Session, Run, and Evidence URIs are the common coordination surface with AI Secretary and product UI.',
249
+ '- Explicit session topology: you may be collaborating in the same room as Secretary or running in a runtime-projected worker session reached through control events. Follow the provided chat/thread/session targets; do not infer topology from workspace sharing.',
250
+ '- Thread reconciliation: messages, input/approval requests, blockers, schedule ticks, and Delivery submissions enter the Thread first; the Reconciler/Scheduler wakes Secretary or workers.',
251
+ '- Report through Delivery/Evidence: return progress, blockers, implementation change requests, and verification for AI Secretary to persist or route.',
252
+ '- Thread workspace: workers assigned to the same Thread in the same environment should normally share this workspace; independent Threads may use separate worktrees.',
253
+ '- Environment-scoped identity: cross-environment file identity requires revision, artifact, patch, checksum, or evidence references.',
128
254
  '',
129
255
  '## Objective',
130
256
  input.objective,
@@ -137,10 +263,62 @@ export function renderSymphonyRuntimePrompt(input) {
137
263
  '- Work only inside the workspace unless the task explicitly requires otherwise.',
138
264
  '- Treat this prompt as a delegated task from the user via AI Secretary.',
139
265
  '- Treat later Secretary messages in this session as Steer or follow-up Delivery updates, not as a replacement for the Goal.',
266
+ '- Report blockers to AI Secretary instead of asking the user directly.',
267
+ '- Do not read sibling worker transcripts unless Secretary explicitly includes them in a Delivery.',
140
268
  '- Preserve a concise report with changed files, commands run, and remaining risks.',
141
269
  '- If blocked by missing credentials, destructive actions, or unclear scope, report the blocker instead of guessing.',
270
+ '- Your workspace path is local to this worker environment. Same-Thread workers in this environment may share it, but do not assume Secretary, the user, or workers in other environments can access the same absolute path.',
271
+ '- When reporting file work across environments, include repo-relative paths plus base revision, checksums/etags, patch or artifact references, and verification evidence.',
272
+ '',
273
+ '## Pod And Control Record Boundary',
274
+ '- In LinX runtime, Pod control records are authoritative. Local files are mirrors, logs, or portable-runtime fallbacks.',
275
+ '- If Pod/model tools are available, read only the assigned Issue, Task, Delivery, Run, source context, and existing evidence needed for this task.',
276
+ '- Write only execution facts for the assigned work: Run/RunStep progress, blockers, Evidence, Delivery report, or Implementation Change Request.',
277
+ '- Do not close Issues, rewrite Spec/current truth, change acceptance criteria, change work split, alter release or roadmap state, create grants, or mutate sibling worker state.',
278
+ '- Use shared model/ORM surfaces when writing structured Pod data. Do not hand-patch business TTL or invent Pod paths.',
279
+ '- If Pod access is unavailable, return the same facts as a structured report so AI Secretary can persist them.',
280
+ '',
281
+ '## Documentation Authority',
282
+ '- Pod Issue/Spec/Task records are the control authority for status, scope, acceptance, split, ownership, closure, and cross-client coordination.',
283
+ '- Repository docs are the implementation authority for code-adjacent design, behavior notes, tests, examples, migration details, and file-level evidence.',
284
+ '- When you edit repository docs, reference the Pod Issue/Spec/Task URI instead of creating a second Issue truth.',
285
+ '- If repository findings contradict the Pod control record, write an Implementation Change Request instead of silently changing acceptance or scope.',
142
286
  ].join('\n');
143
287
  }
288
+ function createSymphonyDispatchReconcilerState(input) {
289
+ const { summary } = decideThreadControlEvent({
290
+ policy: {
291
+ kind: 'symphony',
292
+ assignedWorkerAgent: input.targetAgent,
293
+ secretaryAgent: '__secretary__',
294
+ },
295
+ event: {
296
+ type: 'delivery.submitted',
297
+ ...(input.chat ? { chat: input.chat } : {}),
298
+ ...(input.thread ? { thread: input.thread } : {}),
299
+ resource: input.delivery,
300
+ actor: {
301
+ id: '__secretary__',
302
+ role: 'secretary',
303
+ },
304
+ data: {
305
+ deliveryType: 'task_dispatch',
306
+ issue: input.issue,
307
+ task: input.task,
308
+ delivery: input.delivery,
309
+ session: input.session,
310
+ },
311
+ },
312
+ now: input.now,
313
+ randomId: input.randomId,
314
+ });
315
+ return {
316
+ decisions: [summary],
317
+ };
318
+ }
319
+ function isReconcileDecisionSummary(decision) {
320
+ return 'eventType' in decision;
321
+ }
144
322
  export function formatSymphonySessionSummary(session) {
145
323
  const linked = session.autoModeSessionId ? ` -> ${session.autoModeSessionId}` : '';
146
324
  return `${formatSymphonyUri(session.uri)} ${session.status} ${session.backend}/${session.mode}${linked} (${session.cwd})`;
@@ -203,6 +381,133 @@ function normalizeSymphonyChatThreadRef(input) {
203
381
  ...(messages.length > 0 ? { messages } : {}),
204
382
  };
205
383
  }
384
+ function createSymphonyWorkerUris(options, index) {
385
+ if (index === 0) {
386
+ return {
387
+ task: createTaskUri(options),
388
+ delivery: createSymphonyDeliveryUri(options),
389
+ session: createSymphonySessionUri(options),
390
+ };
391
+ }
392
+ const suffix = `w${index + 1}`;
393
+ return {
394
+ task: createTaskUri({ ...options, randomId: `${normalizeSymphonyRandomId(options.randomId)}-${suffix}` }),
395
+ delivery: createSymphonyDeliveryUri({ ...options, randomId: `${normalizeSymphonyRandomId(options.randomId)}-${suffix}` }),
396
+ session: createSymphonySessionUri({ ...options, randomId: `${normalizeSymphonyRandomId(options.randomId)}-${suffix}` }),
397
+ };
398
+ }
399
+ function normalizeSymphonyWorkerWorkspace(root, override, backend) {
400
+ const path = normalizeOptionalText(override?.path) ?? root.path;
401
+ return {
402
+ path,
403
+ kind: override?.kind ?? root.kind,
404
+ ...(normalizeOptionalText(override?.repository ?? root.repository) ? { repository: normalizeOptionalText(override?.repository ?? root.repository) } : {}),
405
+ ...(normalizeOptionalText(override?.branch ?? root.branch) ? { branch: normalizeOptionalText(override?.branch ?? root.branch) } : {}),
406
+ ...(normalizeOptionalText(override?.worktree ?? root.worktree) ? { worktree: normalizeOptionalText(override?.worktree ?? root.worktree) } : {}),
407
+ ...(normalizeOptionalText(override?.workspaceUri ?? root.workspaceUri) ? { workspaceUri: normalizeOptionalText(override?.workspaceUri ?? root.workspaceUri) } : {}),
408
+ ...(normalizeOptionalText(override?.baseRevision ?? root.baseRevision) ? { baseRevision: normalizeOptionalText(override?.baseRevision ?? root.baseRevision) } : {}),
409
+ environment: normalizeSymphonyWorkerEnvironment(override?.environment ?? root.environment, backend),
410
+ };
411
+ }
412
+ function normalizeSymphonyWorkerEnvironment(environment, backend) {
413
+ const kind = environment?.kind ?? (backend === 'codex' || backend === 'claude' || backend === 'codebuddy' ? 'backend-runtime' : 'local-shell');
414
+ return {
415
+ kind,
416
+ ...(normalizeOptionalText(environment?.id) ? { id: normalizeOptionalText(environment?.id) } : {}),
417
+ ...(normalizeOptionalText(environment?.label) ? { label: normalizeOptionalText(environment?.label) } : {}),
418
+ runtime: normalizeOptionalText(environment?.runtime) ?? backend,
419
+ };
420
+ }
421
+ function formatSymphonyWorkerEnvironment(environment) {
422
+ return [
423
+ environment.kind,
424
+ environment.runtime ? `runtime=${environment.runtime}` : undefined,
425
+ environment.id ? `id=${environment.id}` : undefined,
426
+ environment.label ? `label=${environment.label}` : undefined,
427
+ ].filter(Boolean).join(' ');
428
+ }
429
+ function normalizeSymphonyIssuer(input) {
430
+ const explicit = input.issuer ?? {};
431
+ const chatThread = normalizeSymphonyChatThreadRef({
432
+ chat: explicit.chat ?? input.chat,
433
+ thread: explicit.thread ?? input.thread,
434
+ messages: explicit.messages ?? input.messages,
435
+ });
436
+ return {
437
+ source: explicit.source ?? 'user',
438
+ ...(normalizeOptionalText(explicit.webId) ? { webId: normalizeOptionalText(explicit.webId) } : {}),
439
+ ...(normalizeOptionalText(explicit.agent) ? { agent: normalizeOptionalText(explicit.agent) } : {}),
440
+ ...(normalizeOptionalText(explicit.label) ? { label: normalizeOptionalText(explicit.label) } : {}),
441
+ ...chatThread,
442
+ };
443
+ }
444
+ function normalizeSymphonyWorkerSpecs(input) {
445
+ const workers = input.workers && input.workers.length > 0 ? input.workers : [input.target ?? {}];
446
+ const rootObjective = normalizeRequiredText(input.objective, 'objective');
447
+ const rootAcceptanceCriteria = normalizeSymphonyAcceptanceCriteria(input.acceptanceCriteria);
448
+ return workers.map((worker, index) => {
449
+ const target = normalizeSymphonyDelegationTarget({
450
+ ...input,
451
+ target: {
452
+ ...worker,
453
+ backend: worker.backend ?? input.backend,
454
+ },
455
+ });
456
+ const objective = normalizeOptionalText(worker.objective) ?? rootObjective;
457
+ const label = normalizeOptionalText(worker.label) ?? normalizeOptionalText(worker.agent);
458
+ const title = normalizeOptionalText(worker.title)
459
+ ?? (workers.length > 1 && label ? createSymphonyTitle(`${label}: ${objective}`) : undefined)
460
+ ?? (workers.length > 1 ? createSymphonyTitle(`Worker ${index + 1}: ${objective}`) : undefined);
461
+ const acceptanceCriteria = normalizeSymphonyAcceptanceCriteria(worker.acceptanceCriteria);
462
+ const model = normalizeOptionalText(worker.model) ?? normalizeOptionalText(input.workerModel) ?? normalizeOptionalText(input.model);
463
+ const supervisor = createSymphonySupervisorPolicy(worker.supervisorIntervalMs ?? input.workerSupervisorIntervalMs);
464
+ return {
465
+ target,
466
+ ...(title ? { title } : {}),
467
+ objective,
468
+ acceptanceCriteria: acceptanceCriteria.length > 0 ? acceptanceCriteria : rootAcceptanceCriteria,
469
+ ...(model ? { model } : {}),
470
+ ...(supervisor ? { supervisor } : {}),
471
+ ...(worker.workspace ? { workspace: worker.workspace } : {}),
472
+ };
473
+ });
474
+ }
475
+ function createSymphonySupervisorPolicy(intervalMs) {
476
+ const normalized = typeof intervalMs === 'number' && Number.isFinite(intervalMs)
477
+ ? Math.trunc(intervalMs)
478
+ : undefined;
479
+ if (!normalized || normalized <= 0) {
480
+ return undefined;
481
+ }
482
+ return {
483
+ strategy: 'interval',
484
+ intervalMs: normalized,
485
+ immediateWakeKinds: ['approval', 'question', 'blocked', 'failed', 'completed'],
486
+ };
487
+ }
488
+ function normalizeSymphonyDelegationTarget(input) {
489
+ const explicit = input.target ?? {};
490
+ const chatThread = normalizeSymphonyChatThreadRef({
491
+ chat: explicit.chat ?? input.chat,
492
+ thread: explicit.thread ?? input.thread,
493
+ messages: explicit.messages ?? input.messages,
494
+ });
495
+ const agent = normalizeOptionalText(explicit.agent) ?? `${input.backend}-worker`;
496
+ const label = normalizeOptionalText(explicit.label);
497
+ const source = explicit.source
498
+ ?? (chatThread.chat || chatThread.thread
499
+ ? 'active-session'
500
+ : input.target
501
+ ? 'explicit-backend'
502
+ : 'default');
503
+ return {
504
+ source,
505
+ backend: explicit.backend ?? input.backend,
506
+ agent,
507
+ ...(label ? { label } : {}),
508
+ ...chatThread,
509
+ };
510
+ }
206
511
  function formatSymphonyTimestamp(now = new Date()) {
207
512
  return now.toISOString().replace(/[:.]/gu, '-');
208
513
  }