@undefineds.co/linx 0.3.19 → 0.3.20

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 (39) hide show
  1. package/dist/lib/auto-mode/pod-persistence.js +0 -2
  2. package/dist/lib/auto-mode/pod-persistence.js.map +1 -1
  3. package/dist/lib/capture/persistence.js +377 -0
  4. package/dist/lib/capture/persistence.js.map +1 -0
  5. package/dist/lib/capture/tool.js +242 -0
  6. package/dist/lib/capture/tool.js.map +1 -0
  7. package/dist/lib/linx-cloud-errors.js +5 -0
  8. package/dist/lib/linx-cloud-errors.js.map +1 -1
  9. package/dist/lib/linx-status-line.js +8 -1
  10. package/dist/lib/linx-status-line.js.map +1 -1
  11. package/dist/lib/pi-adapter/branding.js +103 -34
  12. package/dist/lib/pi-adapter/branding.js.map +1 -1
  13. package/dist/lib/pi-adapter/interactive.js +32 -23
  14. package/dist/lib/pi-adapter/interactive.js.map +1 -1
  15. package/dist/lib/pi-adapter/pod-mirror.js +102 -4
  16. package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
  17. package/dist/lib/pi-adapter/pod-native.js +0 -2
  18. package/dist/lib/pi-adapter/pod-native.js.map +1 -1
  19. package/dist/lib/pi-adapter/runtime.js +12 -2
  20. package/dist/lib/pi-adapter/runtime.js.map +1 -1
  21. package/dist/lib/status-line-command.js +2 -2
  22. package/dist/lib/status-line-command.js.map +1 -1
  23. package/dist/lib/symphony/pod-projection.js +4 -6
  24. package/dist/lib/symphony/pod-projection.js.map +1 -1
  25. package/dist/lib/symphony-command.js +1 -1
  26. package/dist/lib/symphony-command.js.map +1 -1
  27. package/dist/skills/basic/SKILL.md +46 -0
  28. package/dist/skills/capture/SKILL.md +165 -0
  29. package/dist/skills/symphony/SKILL.md +8 -4
  30. package/dist/skills/xpod-cli/SKILL.md +13 -2
  31. package/package.json +2 -2
  32. package/vendor/agent-runtime/dist/coordination.d.ts +93 -0
  33. package/vendor/agent-runtime/dist/coordination.js +145 -0
  34. package/vendor/agent-runtime/dist/index.d.ts +1 -0
  35. package/vendor/agent-runtime/dist/index.js +1 -0
  36. package/vendor/agent-runtime/dist/reconciler.d.ts +11 -0
  37. package/vendor/agent-runtime/dist/reconciler.js +41 -3
  38. package/vendor/agent-runtime/dist/symphony.d.ts +9 -9
  39. package/vendor/agent-runtime/dist/symphony.js +4 -4
@@ -4,6 +4,7 @@ export * from './auto-mode.js';
4
4
  export * from './companion-model.js';
5
5
  export * from './client-inbox-subscription.js';
6
6
  export * from './control-plane.js';
7
+ export * from './coordination.js';
7
8
  export * from './file-sync.js';
8
9
  export * from './reconciler.js';
9
10
  export * from './runtime.js';
@@ -4,6 +4,7 @@ export * from './auto-mode.js';
4
4
  export * from './companion-model.js';
5
5
  export * from './client-inbox-subscription.js';
6
6
  export * from './control-plane.js';
7
+ export * from './coordination.js';
7
8
  export * from './file-sync.js';
8
9
  export * from './reconciler.js';
9
10
  export * from './runtime.js';
@@ -1,4 +1,5 @@
1
1
  import type { AgentParticipantRole } from './turn-controller.js';
2
+ import { type ClientReconcilerLease, type ReconcilerOwner } from './coordination.js';
2
3
  export type ThreadPolicyKind = 'direct' | 'auto' | 'symphony' | 'open_group' | 'review';
3
4
  export type ThreadKind = 'main' | 'control' | 'worker' | 'review' | 'schedule' | 'schedule_run';
4
5
  export type ReconcilerEventType = 'message.appended' | 'input.required' | 'approval.required' | 'inbox.notification.created' | 'inbox.notification.updated' | 'delivery.submitted' | 'delivery.completed' | 'delivery.failed' | 'schedule.tick' | 'worker.blocked' | 'change.requested' | 'issue.updated' | 'task.updated' | 'run.updated' | (string & {});
@@ -62,6 +63,9 @@ export interface ThreadControlEvent<TData extends Record<string, unknown> = Reco
62
63
  }
63
64
  export interface ThreadPolicy {
64
65
  kind: ThreadPolicyKind;
66
+ reconcilerOwner?: ReconcilerOwner;
67
+ humanAuthorities?: string[];
68
+ humanAuthorityCount?: number;
65
69
  secretaryAgent?: string;
66
70
  defaultAssistantAgent?: string;
67
71
  assignedWorkerAgent?: string;
@@ -114,6 +118,7 @@ export interface ReconcilerNotificationEvent {
114
118
  export interface ReconcileDecision {
115
119
  id: string;
116
120
  policyKind: ThreadPolicyKind;
121
+ reconcilerOwner: ReconcilerOwner;
117
122
  event: ThreadControlEvent;
118
123
  placement: ThreadPlacement;
119
124
  wakeJobs: WakeJob[];
@@ -140,6 +145,7 @@ export interface WakeJobSummary {
140
145
  export interface ReconcileDecisionSummary {
141
146
  id: string;
142
147
  policyKind: ThreadPolicyKind;
148
+ reconcilerOwner: ReconcilerOwner;
143
149
  eventType: ReconcilerEventType;
144
150
  thread: string;
145
151
  chat?: string;
@@ -151,6 +157,11 @@ export interface ReconcileDecisionSummary {
151
157
  export interface ReconcileThreadEventInput {
152
158
  policy: ThreadPolicyKind | ThreadPolicy;
153
159
  event: ThreadControlEvent;
160
+ reconcilerOwner?: ReconcilerOwner;
161
+ humanAuthorities?: string[];
162
+ humanAuthorityCount?: number;
163
+ clientReconcilerLease?: ClientReconcilerLease | null;
164
+ requireClientReconcilerLease?: boolean;
154
165
  chat?: string;
155
166
  thread?: string;
156
167
  now?: Date;
@@ -1,3 +1,4 @@
1
+ import { canClientCoordinateThread, resolveReconcilerOwnership, } from './coordination.js';
1
2
  const DEFAULT_SECRETARY_AGENT = '__secretary__';
2
3
  const DEFAULT_ASSISTANT_AGENT = 'primary-agent';
3
4
  const DEFAULT_REVIEWER_AGENT = 'ai-reviewer';
@@ -24,12 +25,27 @@ export function reconcileThreadEvent(input) {
24
25
  thread: input.thread,
25
26
  randomId: input.randomId,
26
27
  });
27
- const jobs = selectWakeJobs(policy, event, placement, createdAt, input.randomId, input.client);
28
+ const ownership = resolveReconcilerOwnership({
29
+ policyKind: policy.kind,
30
+ humanAuthorities: input.humanAuthorities ?? policy.humanAuthorities,
31
+ humanAuthorityCount: input.humanAuthorityCount ?? policy.humanAuthorityCount,
32
+ reconcilerOwner: input.reconcilerOwner ?? policy.reconcilerOwner,
33
+ });
34
+ const clientCoordinationSkip = resolveClientCoordinationSkip({
35
+ ownership,
36
+ input,
37
+ placement,
38
+ createdAt,
39
+ });
40
+ const jobs = clientCoordinationSkip
41
+ ? []
42
+ : selectWakeJobs(policy, event, placement, createdAt, input.randomId, input.client);
28
43
  const notificationEvents = selectNotificationEvents(event, placement, createdAt, input.randomId);
29
- const skippedReason = jobs.length === 0 ? skipReasonFor(policy, event, input.client) : undefined;
44
+ const skippedReason = clientCoordinationSkip ?? (jobs.length === 0 ? skipReasonFor(policy, event, input.client) : undefined);
30
45
  return {
31
46
  id: createReconcilerId('decision', input.randomId),
32
47
  policyKind: policy.kind,
48
+ reconcilerOwner: ownership.reconcilerOwner,
33
49
  event,
34
50
  placement,
35
51
  wakeJobs: jobs,
@@ -42,6 +58,7 @@ export function summarizeReconcileDecision(decision) {
42
58
  return {
43
59
  id: decision.id,
44
60
  policyKind: decision.policyKind,
61
+ reconcilerOwner: decision.reconcilerOwner,
45
62
  eventType: decision.event.type,
46
63
  thread: decision.placement.thread,
47
64
  ...(decision.placement.chat ? { chat: decision.placement.chat } : {}),
@@ -120,6 +137,27 @@ export function resolveThreadPlacement(input) {
120
137
  kind: 'control',
121
138
  };
122
139
  }
140
+ function resolveClientCoordinationSkip(input) {
141
+ if (input.ownership.reconcilerOwner !== 'client') {
142
+ return undefined;
143
+ }
144
+ if (!input.input.requireClientReconcilerLease && !input.input.clientReconcilerLease) {
145
+ return undefined;
146
+ }
147
+ const clientId = input.input.client?.id;
148
+ if (canClientCoordinateThread({
149
+ clientId,
150
+ thread: input.placement.thread,
151
+ lease: input.input.clientReconcilerLease,
152
+ now: input.createdAt,
153
+ })) {
154
+ return undefined;
155
+ }
156
+ const owner = input.input.clientReconcilerLease?.ownerClientId;
157
+ return owner
158
+ ? `Client-owned Reconciler is leased by ${owner}; client ${clientId ?? 'unknown'} must not reconcile ${input.placement.thread}.`
159
+ : `Client-owned Reconciler requires an active client coordinator lease for ${input.placement.thread}.`;
160
+ }
123
161
  function selectWakeJobs(policy, event, placement, createdAt, randomId, client) {
124
162
  if (policy.kind === 'direct') {
125
163
  return isUserMessage(event)
@@ -127,7 +165,7 @@ function selectWakeJobs(policy, event, placement, createdAt, randomId, client) {
127
165
  targetAgent: policy.defaultAssistantAgent ?? DEFAULT_ASSISTANT_AGENT,
128
166
  targetRole: 'primary-agent',
129
167
  priority: 'normal',
130
- reason: 'Direct thread routes user messages to the default assistant.',
168
+ reason: 'Single-human assistant surfaces route user messages to the default assistant.',
131
169
  createdAt,
132
170
  randomId,
133
171
  })]
@@ -11,7 +11,7 @@ export declare const SYMPHONY_ISSUE_FILE_NAME = "issue.json";
11
11
  export declare const SYMPHONY_TASK_FILE_NAME = "task.json";
12
12
  export declare const SYMPHONY_DELIVERY_FILE_NAME = "delivery.json";
13
13
  export declare const SYMPHONY_SESSION_FILE_NAME = "session.json";
14
- export type SymphonyWorkspaceKind = 'git' | 'folder';
14
+ export type WorkerWorkspaceKind = 'git' | 'folder';
15
15
  export type SymphonyIdeaStatus = 'captured' | 'exploring' | 'candidate' | 'promoted' | 'deferred' | 'rejected' | 'superseded';
16
16
  export type SymphonyIdeaCommitment = 'thought' | 'direction' | 'tentative_decision' | 'committed';
17
17
  export type SymphonyIssueStatus = 'open' | 'triaging' | 'in_progress' | 'blocked' | 'resolved' | 'closed';
@@ -23,13 +23,13 @@ export type SymphonyResourceKind = 'idea' | 'issue' | 'task' | 'delivery' | 'ses
23
23
  export interface SymphonyReconcilerState {
24
24
  decisions: ReconcileDecisionSummary[];
25
25
  }
26
- export interface SymphonyWorkspaceRef {
26
+ export interface WorkerWorkspaceRef {
27
27
  path: string;
28
- kind: SymphonyWorkspaceKind;
28
+ kind: WorkerWorkspaceKind;
29
29
  repository?: string;
30
30
  branch?: string;
31
31
  worktree?: string;
32
- workspaceUri?: string;
32
+ workspace?: string;
33
33
  baseRevision?: string;
34
34
  environment?: SymphonyWorkerEnvironmentRef;
35
35
  }
@@ -147,7 +147,7 @@ export interface SymphonySessionRecord extends SymphonyChatThreadRef {
147
147
  secretaryAutoEnabled?: boolean;
148
148
  status: SymphonySessionStatus;
149
149
  cwd: string;
150
- workspace?: SymphonyWorkspaceRef;
150
+ workspaceRef?: WorkerWorkspaceRef;
151
151
  target: SymphonyDelegationTarget;
152
152
  model?: string;
153
153
  supervisor?: SymphonySupervisorPolicy;
@@ -180,18 +180,18 @@ export interface SymphonyWorkerSpec extends Partial<SymphonyDelegationTarget> {
180
180
  acceptanceCriteria?: string[];
181
181
  model?: string;
182
182
  supervisorIntervalMs?: number;
183
- workspace?: Partial<SymphonyWorkspaceRef>;
183
+ workspace?: Partial<WorkerWorkspaceRef>;
184
184
  }
185
185
  export interface CreateSymphonyRunPlanInput {
186
186
  objective: string;
187
187
  title?: string;
188
188
  acceptanceCriteria?: string[];
189
189
  workspacePath: string;
190
- workspaceKind?: SymphonyWorkspaceKind;
190
+ workspaceKind?: WorkerWorkspaceKind;
191
191
  repository?: string;
192
192
  branch?: string;
193
193
  worktree?: string;
194
- workspaceUri?: string;
194
+ workspace?: string;
195
195
  baseRevision?: string;
196
196
  environment?: Partial<SymphonyWorkerEnvironmentRef>;
197
197
  backend: AutoModeWorkerBackend;
@@ -242,7 +242,7 @@ export declare function renderSymphonyRuntimePrompt(input: {
242
242
  task: string;
243
243
  objective: string;
244
244
  acceptanceCriteria?: string[];
245
- workspace: SymphonyWorkspaceRef;
245
+ workspace: WorkerWorkspaceRef;
246
246
  backend: AutoModeWorkerBackend;
247
247
  mode: AutoModeMode;
248
248
  secretaryAutoEnabled?: boolean;
@@ -72,7 +72,7 @@ export function createRunPlan(input) {
72
72
  ...(normalizeOptionalText(input.repository) ? { repository: normalizeOptionalText(input.repository) } : {}),
73
73
  ...(normalizeOptionalText(input.branch) ? { branch: normalizeOptionalText(input.branch) } : {}),
74
74
  ...(normalizeOptionalText(input.worktree) ? { worktree: normalizeOptionalText(input.worktree) } : {}),
75
- ...(normalizeOptionalText(input.workspaceUri) ? { workspaceUri: normalizeOptionalText(input.workspaceUri) } : {}),
75
+ ...(normalizeOptionalText(input.workspace) ? { workspace: normalizeOptionalText(input.workspace) } : {}),
76
76
  ...(normalizeOptionalText(input.baseRevision) ? { baseRevision: normalizeOptionalText(input.baseRevision) } : {}),
77
77
  environment: normalizeSymphonyWorkerEnvironment(input.environment, input.backend),
78
78
  };
@@ -146,7 +146,7 @@ export function createRunPlan(input) {
146
146
  ...(input.secretaryAutoEnabled !== undefined ? { secretaryAutoEnabled: input.secretaryAutoEnabled } : {}),
147
147
  status: 'planned',
148
148
  cwd: workerWorkspace.path,
149
- workspace: workerWorkspace,
149
+ workspaceRef: workerWorkspace,
150
150
  target,
151
151
  ...(spec.model ? { model: spec.model } : {}),
152
152
  ...(spec.supervisor ? { supervisor: spec.supervisor } : {}),
@@ -239,7 +239,7 @@ export function renderSymphonyRuntimePrompt(input) {
239
239
  ...(workThread ? [`Work thread: ${workThread}`] : []),
240
240
  `Workspace: ${input.workspace.path}`,
241
241
  `Workspace kind: ${input.workspace.kind}`,
242
- ...(input.workspace.workspaceUri ? [`Workspace URI: ${input.workspace.workspaceUri}`] : []),
242
+ ...(input.workspace.workspace ? [`Workspace resource: ${input.workspace.workspace}`] : []),
243
243
  ...(input.workspace.repository ? [`Workspace repository: ${input.workspace.repository}`] : []),
244
244
  ...(input.workspace.branch ? [`Workspace branch: ${input.workspace.branch}`] : []),
245
245
  ...(input.workspace.baseRevision ? [`Workspace base revision: ${input.workspace.baseRevision}`] : []),
@@ -406,7 +406,7 @@ function normalizeSymphonyWorkerWorkspace(root, override, backend) {
406
406
  ...(normalizeOptionalText(override?.repository ?? root.repository) ? { repository: normalizeOptionalText(override?.repository ?? root.repository) } : {}),
407
407
  ...(normalizeOptionalText(override?.branch ?? root.branch) ? { branch: normalizeOptionalText(override?.branch ?? root.branch) } : {}),
408
408
  ...(normalizeOptionalText(override?.worktree ?? root.worktree) ? { worktree: normalizeOptionalText(override?.worktree ?? root.worktree) } : {}),
409
- ...(normalizeOptionalText(override?.workspaceUri ?? root.workspaceUri) ? { workspaceUri: normalizeOptionalText(override?.workspaceUri ?? root.workspaceUri) } : {}),
409
+ ...(normalizeOptionalText(override?.workspace ?? root.workspace) ? { workspace: normalizeOptionalText(override?.workspace ?? root.workspace) } : {}),
410
410
  ...(normalizeOptionalText(override?.baseRevision ?? root.baseRevision) ? { baseRevision: normalizeOptionalText(override?.baseRevision ?? root.baseRevision) } : {}),
411
411
  environment: normalizeSymphonyWorkerEnvironment(override?.environment ?? root.environment, backend),
412
412
  };