@undefineds.co/linx 0.3.15 → 0.3.16

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.
@@ -0,0 +1,54 @@
1
+ const ABSOLUTE_IRI = /^[a-zA-Z][a-zA-Z\d+.-]*:/;
2
+ const AGENT_RESOURCE_ID = /^([A-Za-z0-9_.-]+)\/$/;
3
+ const AGENT_KEY = /^[A-Za-z0-9_.-]+$/;
4
+ export function asBaseRelativeResourceId(value, label = 'Resource id') {
5
+ if (typeof value !== 'string' || value.trim().length === 0) {
6
+ throw new Error(`${label} must be a non-empty base-relative resource id.`);
7
+ }
8
+ const normalized = value.trim();
9
+ if (ABSOLUTE_IRI.test(normalized) || normalized.startsWith('/') || normalized.startsWith('//')) {
10
+ throw new Error(`${label} must be a base-relative resource id.`);
11
+ }
12
+ return normalized;
13
+ }
14
+ export function asResourceIri(value, label = 'Resource IRI') {
15
+ if (typeof value !== 'string' || value.trim().length === 0) {
16
+ throw new Error(`${label} must be a non-empty resource IRI.`);
17
+ }
18
+ const normalized = value.trim();
19
+ if (!ABSOLUTE_IRI.test(normalized)) {
20
+ throw new Error(`${label} must be a full resource IRI.`);
21
+ }
22
+ return normalized;
23
+ }
24
+ export function requireRowResourceId(row, label = 'Pod row') {
25
+ if (!row || typeof row.id !== 'string' || row.id.trim().length === 0) {
26
+ throw new Error(`${label} row is missing row.id.`);
27
+ }
28
+ return asBaseRelativeResourceId(row.id, `${label} row.id`);
29
+ }
30
+ function defaultAgentKey() {
31
+ return `agent_${Math.random().toString(36).slice(2, 12)}`;
32
+ }
33
+ export function agentResourceId(key) {
34
+ const raw = typeof key === 'string' ? key.trim() : '';
35
+ const value = raw || defaultAgentKey();
36
+ if (AGENT_RESOURCE_ID.test(value)) {
37
+ return asBaseRelativeResourceId(value, 'Agent resource id');
38
+ }
39
+ if (!AGENT_KEY.test(value)) {
40
+ throw new Error('Agent key must use letters, numbers, dot, underscore, or dash.');
41
+ }
42
+ return asBaseRelativeResourceId(`${value}/`, 'Agent resource id');
43
+ }
44
+ export function agentKeyFromResourceId(resourceId) {
45
+ const id = asBaseRelativeResourceId(resourceId, 'Agent resource id');
46
+ const match = id.match(AGENT_RESOURCE_ID);
47
+ if (!match?.[1]) {
48
+ throw new Error('Agent resource id must use {agentKey}/.');
49
+ }
50
+ return match[1];
51
+ }
52
+ export function agentHomeDirFromResourceId(resourceId) {
53
+ return asBaseRelativeResourceId(agentResourceId(agentKeyFromResourceId(resourceId)), 'Agent home dir');
54
+ }
@@ -173,10 +173,10 @@ function selectWakeJobs(policy, event, placement, createdAt, randomId, client) {
173
173
  return [createSecretaryWakeJob(policy, event, placement, 'Symphony Delivery submissions wake Secretary for review or routing.', 'normal', createdAt, randomId)];
174
174
  }
175
175
  if (event.type === 'delivery.completed') {
176
- return [createSecretaryWakeJob(policy, event, placement, 'Symphony completion Delivery wakes Secretary for quality, acceptance, and follow-up extraction reconciliation.', 'high', createdAt, randomId)];
176
+ return [createSecretaryWakeJob(policy, event, placement, 'Symphony completion Delivery wakes Secretary for quality and acceptance reconciliation.', 'high', createdAt, randomId)];
177
177
  }
178
178
  if (event.type === 'delivery.failed') {
179
- return [createSecretaryWakeJob(policy, event, placement, 'Symphony failed Delivery wakes Secretary for feasibility, retry, scope change, or follow-up extraction reconciliation.', 'high', createdAt, randomId)];
179
+ return [createSecretaryWakeJob(policy, event, placement, 'Symphony failed Delivery wakes Secretary for feasibility, retry, or scope change reconciliation.', 'high', createdAt, randomId)];
180
180
  }
181
181
  if (event.type === 'issue.updated' || event.type === 'task.updated' || event.type === 'run.updated') {
182
182
  return [createSecretaryWakeJob(policy, event, placement, 'Symphony state changes wake Secretary to reconcile system state.', 'normal', createdAt, randomId)];
@@ -245,10 +245,10 @@ export function renderSymphonyRuntimePrompt(input) {
245
245
  ...(input.workspace.environment ? [`Workspace environment: ${formatSymphonyWorkerEnvironment(input.workspace.environment)}`] : []),
246
246
  '',
247
247
  '## Runtime Space Contract',
248
- '- Shared control space: Idea/Issue/Report/Evidence are file-primary Pod resources with structured meta; Task, Delivery, Session, Run, and RunStep are TTL control resources. Use the provided URIs as the common coordination surface with AI Secretary and product UI.',
248
+ '- Shared control space: Issue, Task, Delivery, Session, Run, and Evidence URIs are the common coordination surface with AI Secretary and product UI.',
249
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
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 plus file-primary Report/Evidence: return progress, blockers, implementation change requests, and verification so AI Secretary can persist structured control facts and Pod files without inlining long logs into TTL.',
251
+ '- Report through Delivery/Evidence: return progress, blockers, implementation change requests, and verification for AI Secretary to persist or route.',
252
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
253
  '- Environment-scoped identity: cross-environment file identity requires revision, artifact, patch, checksum, or evidence references.',
254
254
  '',
@@ -266,21 +266,20 @@ export function renderSymphonyRuntimePrompt(input) {
266
266
  '- Report blockers to AI Secretary instead of asking the user directly.',
267
267
  '- Do not read sibling worker transcripts unless Secretary explicitly includes them in a Delivery.',
268
268
  '- Preserve a concise report with changed files, commands run, and remaining risks.',
269
- '- In the final report, explicitly list follow-up candidates separately from assigned-work evidence: new defects, missing shared abstractions, app-local glue to move into shared models, storage, or adapter packages, live verification gaps, or deferred cleanup. Secretary classifies these; do not create or close Issues yourself.',
270
269
  '- If blocked by missing credentials, destructive actions, or unclear scope, report the blocker instead of guessing.',
271
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.',
272
271
  '- When reporting file work across environments, include repo-relative paths plus base revision, checksums/etags, patch or artifact references, and verification evidence.',
273
272
  '',
274
273
  '## Pod And Control Record Boundary',
275
274
  '- In LinX runtime, Pod control records are authoritative. Local files are mirrors, logs, or portable-runtime fallbacks.',
276
- '- If Pod/model tools are available, read only the assigned Issue document/meta, Task, Delivery, Run, source context, and existing Report/Evidence files needed for this task.',
277
- '- Write only execution facts for the assigned work: Run/RunStep progress, blockers, file-primary Evidence/Report, Delivery report metadata, or Implementation Change Request.',
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.',
278
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.',
279
278
  '- Use shared model/ORM surfaces when writing structured Pod data. Do not hand-patch business TTL or invent Pod paths.',
280
279
  '- If Pod access is unavailable, return the same facts as a structured report so AI Secretary can persist them.',
281
280
  '',
282
281
  '## Documentation Authority',
283
- '- Pod Issue files plus meta, Spec files, and Task control records are the authority for status, scope, acceptance, split, ownership, closure, and cross-client coordination.',
282
+ '- Pod Issue/Spec/Task records are the control authority for status, scope, acceptance, split, ownership, closure, and cross-client coordination.',
284
283
  '- Repository docs are the implementation authority for code-adjacent design, behavior notes, tests, examples, migration details, and file-level evidence.',
285
284
  '- When you edit repository docs, reference the Pod Issue/Spec/Task URI instead of creating a second Issue truth.',
286
285
  '- If repository findings contradict the Pod control record, write an Implementation Change Request instead of silently changing acceptance or scope.',
@@ -0,0 +1,33 @@
1
+ export type RuntimeWorkspaceKind = 'local-folder' | 'local-worktree' | 'pod-container';
2
+ export interface RuntimeWorkspaceInput {
3
+ workspaceKind?: unknown;
4
+ workspaceUri?: string | null;
5
+ repoPath?: string | null;
6
+ folderPath?: string | null;
7
+ baseRef?: string | null;
8
+ branch?: string | null;
9
+ }
10
+ export interface NormalizedRuntimeWorkspaceInput {
11
+ workspaceKind: RuntimeWorkspaceKind;
12
+ workspaceUri?: string;
13
+ repoPath?: string;
14
+ folderPath?: string;
15
+ baseRef: string;
16
+ branch?: string;
17
+ }
18
+ export interface NormalizeRuntimeWorkspaceOptions {
19
+ normalizeLocalPath?: (value?: string | null) => string;
20
+ defaultBaseRef?: string;
21
+ }
22
+ export interface RuntimeWorkspaceSessionLike {
23
+ cwd?: string | null;
24
+ repoPath?: string | null;
25
+ folderPath?: string | null;
26
+ workspaceUri?: string | null;
27
+ }
28
+ export declare function isRuntimeWorkspaceKind(value: unknown): value is RuntimeWorkspaceKind;
29
+ export declare function isHttpWorkspaceRef(value?: string | null): boolean;
30
+ export declare function inferRuntimeWorkspaceKind(input: RuntimeWorkspaceInput, options?: NormalizeRuntimeWorkspaceOptions): RuntimeWorkspaceKind;
31
+ export declare function normalizeRuntimeWorkspaceInput(input: RuntimeWorkspaceInput, options?: NormalizeRuntimeWorkspaceOptions): NormalizedRuntimeWorkspaceInput;
32
+ export declare function isRuntimeSessionInWorkspace(session: RuntimeWorkspaceSessionLike, workspacePath: string, options?: NormalizeRuntimeWorkspaceOptions): boolean;
33
+ export declare function filterRuntimeSessionsForWorkspace<T extends RuntimeWorkspaceSessionLike>(sessions: T[], workspacePath: string, options?: NormalizeRuntimeWorkspaceOptions): T[];
@@ -0,0 +1,80 @@
1
+ function trim(value) {
2
+ return value?.trim() || '';
3
+ }
4
+ function normalizeLocalPath(value) {
5
+ return trim(value);
6
+ }
7
+ export function isRuntimeWorkspaceKind(value) {
8
+ return value === 'local-folder' || value === 'local-worktree' || value === 'pod-container';
9
+ }
10
+ export function isHttpWorkspaceRef(value) {
11
+ const candidate = trim(value);
12
+ if (!candidate)
13
+ return false;
14
+ try {
15
+ const url = new URL(candidate);
16
+ return url.protocol === 'http:' || url.protocol === 'https:';
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ export function inferRuntimeWorkspaceKind(input, options = {}) {
23
+ if (isRuntimeWorkspaceKind(input.workspaceKind)) {
24
+ return input.workspaceKind;
25
+ }
26
+ const normalize = options.normalizeLocalPath ?? normalizeLocalPath;
27
+ const repoPath = normalize(input.repoPath);
28
+ if (isHttpWorkspaceRef(input.workspaceUri) && !repoPath) {
29
+ return 'pod-container';
30
+ }
31
+ const folderPath = normalize(input.folderPath);
32
+ return repoPath && folderPath && folderPath !== repoPath ? 'local-worktree' : 'local-folder';
33
+ }
34
+ export function normalizeRuntimeWorkspaceInput(input, options = {}) {
35
+ const normalize = options.normalizeLocalPath ?? normalizeLocalPath;
36
+ const workspaceKind = inferRuntimeWorkspaceKind(input, options);
37
+ const workspaceUri = trim(input.workspaceUri) || undefined;
38
+ const repoPath = normalize(input.repoPath) || undefined;
39
+ const folderPathInput = normalize(input.folderPath) || undefined;
40
+ const baseRef = trim(input.baseRef) || options.defaultBaseRef || 'HEAD';
41
+ const branch = trim(input.branch) || undefined;
42
+ if (workspaceKind === 'pod-container') {
43
+ if (!workspaceUri || !isHttpWorkspaceRef(workspaceUri)) {
44
+ throw new Error('Pod workspace session requires an http(s) workspaceUri.');
45
+ }
46
+ return {
47
+ workspaceKind,
48
+ workspaceUri,
49
+ repoPath,
50
+ folderPath: folderPathInput,
51
+ baseRef,
52
+ branch,
53
+ };
54
+ }
55
+ if (!repoPath) {
56
+ throw new Error('Local runtime session requires repoPath.');
57
+ }
58
+ const folderPath = folderPathInput || repoPath;
59
+ return {
60
+ workspaceKind: folderPath !== repoPath ? 'local-worktree' : 'local-folder',
61
+ workspaceUri,
62
+ repoPath,
63
+ folderPath,
64
+ baseRef,
65
+ branch,
66
+ };
67
+ }
68
+ export function isRuntimeSessionInWorkspace(session, workspacePath, options = {}) {
69
+ const normalize = options.normalizeLocalPath ?? normalizeLocalPath;
70
+ const expected = normalize(workspacePath);
71
+ if (!expected)
72
+ return false;
73
+ const candidates = [session.cwd, session.folderPath, session.repoPath]
74
+ .map((value) => normalize(value))
75
+ .filter(Boolean);
76
+ return candidates.some((candidate) => candidate === expected);
77
+ }
78
+ export function filterRuntimeSessionsForWorkspace(sessions, workspacePath, options = {}) {
79
+ return sessions.filter((session) => isRuntimeSessionInWorkspace(session, workspacePath, options));
80
+ }
@@ -9,12 +9,14 @@
9
9
  "./companion-model": "./dist/companion-model.js",
10
10
  "./control-plane": "./dist/control-plane.js",
11
11
  "./file-sync": "./dist/file-sync.js",
12
+ "./pod-resource-identity": "./dist/pod-resource-identity.js",
12
13
  "./reconciler": "./dist/reconciler.js",
13
14
  "./runtime": "./dist/runtime.js",
14
15
  "./symphony": "./dist/symphony.js",
15
16
  "./sync": "./dist/sync.js",
16
17
  "./thread-reconciler-controller": "./dist/thread-reconciler-controller.js",
17
18
  "./turn-controller": "./dist/turn-controller.js",
18
- "./wake-scheduler": "./dist/wake-scheduler.js"
19
+ "./wake-scheduler": "./dist/wake-scheduler.js",
20
+ "./workspace": "./dist/workspace.js"
19
21
  }
20
22
  }