@inf-minds/jobs 0.0.1

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 (76) hide show
  1. package/dist/artifact-cleanup.d.ts +30 -0
  2. package/dist/artifact-cleanup.d.ts.map +1 -0
  3. package/dist/artifact-cleanup.js +44 -0
  4. package/dist/artifact-cleanup.js.map +1 -0
  5. package/dist/artifact-manager.d.ts +41 -0
  6. package/dist/artifact-manager.d.ts.map +1 -0
  7. package/dist/artifact-manager.js +254 -0
  8. package/dist/artifact-manager.js.map +1 -0
  9. package/dist/artifact-materializer.d.ts +21 -0
  10. package/dist/artifact-materializer.d.ts.map +1 -0
  11. package/dist/artifact-materializer.js +33 -0
  12. package/dist/artifact-materializer.js.map +1 -0
  13. package/dist/artifact-router.d.ts +131 -0
  14. package/dist/artifact-router.d.ts.map +1 -0
  15. package/dist/artifact-router.js +4 -0
  16. package/dist/artifact-router.js.map +1 -0
  17. package/dist/default-artifact-router.d.ts +26 -0
  18. package/dist/default-artifact-router.d.ts.map +1 -0
  19. package/dist/default-artifact-router.js +125 -0
  20. package/dist/default-artifact-router.js.map +1 -0
  21. package/dist/event-appender.d.ts +23 -0
  22. package/dist/event-appender.d.ts.map +1 -0
  23. package/dist/event-appender.js +120 -0
  24. package/dist/event-appender.js.map +1 -0
  25. package/dist/index.d.ts +16 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +28 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/job-context.d.ts +58 -0
  30. package/dist/job-context.d.ts.map +1 -0
  31. package/dist/job-context.js +133 -0
  32. package/dist/job-context.js.map +1 -0
  33. package/dist/job-manager.d.ts +23 -0
  34. package/dist/job-manager.d.ts.map +1 -0
  35. package/dist/job-manager.js +145 -0
  36. package/dist/job-manager.js.map +1 -0
  37. package/dist/job-runner.d.ts +32 -0
  38. package/dist/job-runner.d.ts.map +1 -0
  39. package/dist/job-runner.js +187 -0
  40. package/dist/job-runner.js.map +1 -0
  41. package/dist/job-worker.d.ts +79 -0
  42. package/dist/job-worker.d.ts.map +1 -0
  43. package/dist/job-worker.js +134 -0
  44. package/dist/job-worker.js.map +1 -0
  45. package/dist/mind-worker.d.ts +63 -0
  46. package/dist/mind-worker.d.ts.map +1 -0
  47. package/dist/mind-worker.js +99 -0
  48. package/dist/mind-worker.js.map +1 -0
  49. package/dist/schema.d.ts +1143 -0
  50. package/dist/schema.d.ts.map +1 -0
  51. package/dist/schema.js +225 -0
  52. package/dist/schema.js.map +1 -0
  53. package/dist/types.d.ts +434 -0
  54. package/dist/types.d.ts.map +1 -0
  55. package/dist/types.js +24 -0
  56. package/dist/types.js.map +1 -0
  57. package/drizzle/0001_create_jobs_tables.sql +70 -0
  58. package/drizzle/0002_coordinator_tables.sql +78 -0
  59. package/drizzle/0003_artifacts.sql +24 -0
  60. package/drizzle/0004_kernel.sql +28 -0
  61. package/drizzle/0005_artifact_routing.sql +29 -0
  62. package/package.json +48 -0
  63. package/src/artifact-cleanup.ts +85 -0
  64. package/src/artifact-manager.ts +346 -0
  65. package/src/artifact-materializer.ts +64 -0
  66. package/src/artifact-router.ts +151 -0
  67. package/src/default-artifact-router.ts +186 -0
  68. package/src/event-appender.ts +158 -0
  69. package/src/index.ts +136 -0
  70. package/src/job-context.ts +195 -0
  71. package/src/job-manager.ts +179 -0
  72. package/src/job-runner.ts +260 -0
  73. package/src/job-worker.ts +252 -0
  74. package/src/mind-worker.ts +152 -0
  75. package/src/schema.ts +290 -0
  76. package/src/types.ts +542 -0
@@ -0,0 +1,346 @@
1
+ // ABOUTME: ArtifactManager for job artifact CRUD operations
2
+ // ABOUTME: Routes artifacts to appropriate storage backend
3
+
4
+ import type { StorageProvider } from '@inf-minds/storage';
5
+ import { detectMimeType, getStorageType } from '@inf-minds/storage';
6
+ import type { ArtifactRef, ArtifactContent, SaveArtifactOptions } from './types.js';
7
+ import { jobArtifacts, type JobArtifact, type NewJobArtifact } from './schema.js';
8
+ import { and, eq, sql } from 'drizzle-orm';
9
+
10
+ export interface ArtifactManagerDb {
11
+ insert(table: unknown): {
12
+ values(record: unknown): {
13
+ returning(): Promise<unknown[]>;
14
+ };
15
+ };
16
+ select(): {
17
+ from(table: unknown): {
18
+ where(condition: unknown): Promise<unknown[]>;
19
+ };
20
+ };
21
+ delete(table: unknown): {
22
+ where(condition: unknown): Promise<unknown>;
23
+ };
24
+ }
25
+
26
+ export interface ArtifactManagerOptions {
27
+ db: ArtifactManagerDb;
28
+ postgresStorage: StorageProvider;
29
+ externalStorage: StorageProvider;
30
+ table?: unknown;
31
+ }
32
+
33
+ export interface ArtifactManager {
34
+ save(jobId: string, name: string, content: string | Buffer, options: SaveArtifactOptions): Promise<ArtifactRef>;
35
+ load(jobId: string, name: string): Promise<ArtifactContent | null>;
36
+ list(jobId: string): Promise<ArtifactRef[]>;
37
+ getSignedUrl(jobId: string, name: string, expiresIn?: number): Promise<string | null>;
38
+ delete(jobId: string, name: string): Promise<void>;
39
+ deleteAll(jobId: string): Promise<void>;
40
+
41
+ // Session/visibility queries for kernel orchestration
42
+ /** List all artifacts for a kernel session */
43
+ listBySession(sessionId: string): Promise<ArtifactRef[]>;
44
+ /** List artifacts for a specific node in a session */
45
+ listByNode(sessionId: string, nodeId: string): Promise<ArtifactRef[]>;
46
+ /** List artifacts by visibility scope in a session */
47
+ listByVisibility(sessionId: string, visibility: string): Promise<ArtifactRef[]>;
48
+ /** List artifacts published to specific channels in a session */
49
+ listByChannels(sessionId: string, channels: string[]): Promise<ArtifactRef[]>;
50
+ }
51
+
52
+ export function createArtifactManager(options: ArtifactManagerOptions): ArtifactManager {
53
+ const { db, postgresStorage, externalStorage } = options;
54
+ const table = options.table ?? jobArtifacts;
55
+
56
+ return {
57
+ async save(jobId: string, name: string, content: string | Buffer, options: SaveArtifactOptions): Promise<ArtifactRef> {
58
+ // Determine MIME type
59
+ const mimeType = options.mimeType ?? detectMimeType(name);
60
+
61
+ // Determine storage type based on MIME type
62
+ const storageType = getStorageType(mimeType);
63
+
64
+ // Select appropriate storage backend
65
+ const storage = storageType === 'postgres' ? postgresStorage : externalStorage;
66
+
67
+ // Build storage key
68
+ const storageKey = storageType === 'postgres' ? name : `${jobId}/${name}`;
69
+
70
+ // Store content in appropriate backend
71
+ const putResult = await storage.put(storageKey, content, {
72
+ mimeType,
73
+ metadata: options.metadata,
74
+ });
75
+
76
+ // Calculate expiry timestamp
77
+ let expiresAt: Date | null = null;
78
+ if (options.expiresIn) {
79
+ expiresAt = new Date(Date.now() + options.expiresIn * 1000);
80
+ }
81
+
82
+ // Build artifact record
83
+ const artifactRecord: NewJobArtifact = {
84
+ jobId,
85
+ name,
86
+ mimeType,
87
+ storageType,
88
+ content: storageType === 'postgres' ? (typeof content === 'string' ? { text: content } : content) : null,
89
+ externalKey: storageType === 'external' ? putResult.key : null,
90
+ sizeBytes: putResult.sizeBytes,
91
+ metadata: options.metadata ?? {},
92
+ expiresAt,
93
+ };
94
+
95
+ // Insert into database
96
+ const [inserted] = await db
97
+ .insert(table)
98
+ .values(artifactRecord)
99
+ .returning();
100
+
101
+ if (!inserted) {
102
+ throw new Error('Failed to insert artifact record');
103
+ }
104
+
105
+ const artifact = inserted as JobArtifact;
106
+
107
+ // Return artifact reference
108
+ return {
109
+ name: artifact.name,
110
+ mimeType: artifact.mimeType,
111
+ sizeBytes: artifact.sizeBytes,
112
+ storageType: artifact.storageType as 'postgres' | 'external',
113
+ metadata: (artifact.metadata as Record<string, unknown>) ?? {},
114
+ createdAt: artifact.createdAt,
115
+ };
116
+ },
117
+
118
+ async load(jobId: string, name: string): Promise<ArtifactContent | null> {
119
+ // Query database for artifact record
120
+ const results = await db
121
+ .select()
122
+ .from(table)
123
+ .where(and(eq(jobArtifacts.jobId, jobId), eq(jobArtifacts.name, name)));
124
+
125
+ if (results.length === 0) {
126
+ return null;
127
+ }
128
+
129
+ const artifact = results[0] as JobArtifact;
130
+
131
+ // Fetch content from appropriate storage
132
+ let content: string | Buffer;
133
+
134
+ if (artifact.storageType === 'postgres') {
135
+ const storageResult = await postgresStorage.get(artifact.name);
136
+ if (!storageResult) {
137
+ throw new Error(`Artifact content not found in postgres storage: ${artifact.name}`);
138
+ }
139
+ content = storageResult.content;
140
+ } else {
141
+ if (!artifact.externalKey) {
142
+ throw new Error(`External key missing for artifact: ${artifact.name}`);
143
+ }
144
+ const storageResult = await externalStorage.get(artifact.externalKey);
145
+ if (!storageResult) {
146
+ throw new Error(`Artifact content not found in external storage: ${artifact.externalKey}`);
147
+ }
148
+ content = storageResult.content;
149
+ }
150
+
151
+ // Return artifact with content
152
+ return {
153
+ name: artifact.name,
154
+ mimeType: artifact.mimeType,
155
+ sizeBytes: artifact.sizeBytes,
156
+ storageType: artifact.storageType as 'postgres' | 'external',
157
+ metadata: (artifact.metadata as Record<string, unknown>) ?? {},
158
+ createdAt: artifact.createdAt,
159
+ content,
160
+ };
161
+ },
162
+
163
+ async list(jobId: string): Promise<ArtifactRef[]> {
164
+ // Query database for all artifacts for this job
165
+ const results = await db
166
+ .select()
167
+ .from(table)
168
+ .where(eq(jobArtifacts.jobId, jobId));
169
+
170
+ // Map to artifact references
171
+ return results.map((artifact) => {
172
+ const a = artifact as JobArtifact;
173
+ return {
174
+ name: a.name,
175
+ mimeType: a.mimeType,
176
+ sizeBytes: a.sizeBytes,
177
+ storageType: a.storageType as 'postgres' | 'external',
178
+ metadata: (a.metadata as Record<string, unknown>) ?? {},
179
+ createdAt: a.createdAt,
180
+ };
181
+ });
182
+ },
183
+
184
+ async getSignedUrl(jobId: string, name: string, expiresIn?: number): Promise<string | null> {
185
+ // Query database for artifact record
186
+ const results = await db
187
+ .select()
188
+ .from(table)
189
+ .where(and(eq(jobArtifacts.jobId, jobId), eq(jobArtifacts.name, name)));
190
+
191
+ if (results.length === 0) {
192
+ return null;
193
+ }
194
+
195
+ const artifact = results[0] as JobArtifact;
196
+
197
+ // Only external artifacts can have signed URLs
198
+ if (artifact.storageType !== 'external' || !artifact.externalKey) {
199
+ return null;
200
+ }
201
+
202
+ // Get signed URL from external storage
203
+ return await externalStorage.getSignedUrl(artifact.externalKey, expiresIn);
204
+ },
205
+
206
+ async delete(jobId: string, name: string): Promise<void> {
207
+ // Query database for artifact record
208
+ const results = await db
209
+ .select()
210
+ .from(table)
211
+ .where(and(eq(jobArtifacts.jobId, jobId), eq(jobArtifacts.name, name)));
212
+
213
+ if (results.length === 0) {
214
+ return;
215
+ }
216
+
217
+ const artifact = results[0] as JobArtifact;
218
+
219
+ // Delete from appropriate storage
220
+ if (artifact.storageType === 'postgres') {
221
+ await postgresStorage.delete(artifact.name);
222
+ } else if (artifact.externalKey) {
223
+ await externalStorage.delete(artifact.externalKey);
224
+ }
225
+
226
+ // Delete database record
227
+ await db
228
+ .delete(table)
229
+ .where(and(eq(jobArtifacts.jobId, jobId), eq(jobArtifacts.name, name)));
230
+ },
231
+
232
+ async deleteAll(jobId: string): Promise<void> {
233
+ // Get all artifacts for this job
234
+ const artifacts = await db
235
+ .select()
236
+ .from(table)
237
+ .where(eq(jobArtifacts.jobId, jobId));
238
+
239
+ // Delete from storage backends
240
+ for (const artifact of artifacts) {
241
+ const a = artifact as JobArtifact;
242
+ if (a.storageType === 'postgres') {
243
+ await postgresStorage.delete(a.name);
244
+ } else if (a.externalKey) {
245
+ await externalStorage.delete(a.externalKey);
246
+ }
247
+ }
248
+
249
+ // Delete all database records
250
+ await db
251
+ .delete(table)
252
+ .where(eq(jobArtifacts.jobId, jobId));
253
+ },
254
+
255
+ // Session/visibility query implementations
256
+
257
+ async listBySession(sessionId: string): Promise<ArtifactRef[]> {
258
+ const results = await db
259
+ .select()
260
+ .from(table)
261
+ .where(eq(jobArtifacts.sessionId, sessionId));
262
+
263
+ return results.map((artifact) => {
264
+ const a = artifact as JobArtifact;
265
+ return {
266
+ name: a.name,
267
+ mimeType: a.mimeType,
268
+ sizeBytes: a.sizeBytes,
269
+ storageType: a.storageType as 'postgres' | 'external',
270
+ metadata: (a.metadata as Record<string, unknown>) ?? {},
271
+ createdAt: a.createdAt,
272
+ };
273
+ });
274
+ },
275
+
276
+ async listByNode(sessionId: string, nodeId: string): Promise<ArtifactRef[]> {
277
+ const results = await db
278
+ .select()
279
+ .from(table)
280
+ .where(and(eq(jobArtifacts.sessionId, sessionId), eq(jobArtifacts.nodeId, nodeId)));
281
+
282
+ return results.map((artifact) => {
283
+ const a = artifact as JobArtifact;
284
+ return {
285
+ name: a.name,
286
+ mimeType: a.mimeType,
287
+ sizeBytes: a.sizeBytes,
288
+ storageType: a.storageType as 'postgres' | 'external',
289
+ metadata: (a.metadata as Record<string, unknown>) ?? {},
290
+ createdAt: a.createdAt,
291
+ };
292
+ });
293
+ },
294
+
295
+ async listByVisibility(sessionId: string, visibility: string): Promise<ArtifactRef[]> {
296
+ const results = await db
297
+ .select()
298
+ .from(table)
299
+ .where(and(eq(jobArtifacts.sessionId, sessionId), eq(jobArtifacts.visibility, visibility)));
300
+
301
+ return results.map((artifact) => {
302
+ const a = artifact as JobArtifact;
303
+ return {
304
+ name: a.name,
305
+ mimeType: a.mimeType,
306
+ sizeBytes: a.sizeBytes,
307
+ storageType: a.storageType as 'postgres' | 'external',
308
+ metadata: (a.metadata as Record<string, unknown>) ?? {},
309
+ createdAt: a.createdAt,
310
+ };
311
+ });
312
+ },
313
+
314
+ async listByChannels(sessionId: string, channels: string[]): Promise<ArtifactRef[]> {
315
+ if (channels.length === 0) {
316
+ return [];
317
+ }
318
+
319
+ // Query for artifacts where channels array overlaps with requested channels
320
+ const results = await db
321
+ .select()
322
+ .from(table)
323
+ .where(
324
+ and(
325
+ eq(jobArtifacts.sessionId, sessionId),
326
+ sql`${jobArtifacts.channels} && ARRAY[${sql.join(
327
+ channels.map((c) => sql`${c}`),
328
+ sql`, `
329
+ )}]::text[]`
330
+ )
331
+ );
332
+
333
+ return results.map((artifact) => {
334
+ const a = artifact as JobArtifact;
335
+ return {
336
+ name: a.name,
337
+ mimeType: a.mimeType,
338
+ sizeBytes: a.sizeBytes,
339
+ storageType: a.storageType as 'postgres' | 'external',
340
+ metadata: (a.metadata as Record<string, unknown>) ?? {},
341
+ createdAt: a.createdAt,
342
+ };
343
+ });
344
+ },
345
+ };
346
+ }
@@ -0,0 +1,64 @@
1
+ // ABOUTME: Materializes artifacts from job dependencies
2
+ // ABOUTME: Makes parent job artifacts available via deps/{jobId}/{name} paths
3
+
4
+ import type { ArtifactManager } from './artifact-manager.js';
5
+ import type { ArtifactContent } from './types.js';
6
+ import type { MaterializedArtifacts } from './job-context.js';
7
+
8
+ export interface ArtifactMaterializerDb {
9
+ select(): {
10
+ from(table: unknown): {
11
+ where(condition: unknown): Promise<{ dependsOnJobId: string }[]>;
12
+ };
13
+ };
14
+ }
15
+
16
+ export interface ArtifactMaterializerOptions {
17
+ db: ArtifactMaterializerDb;
18
+ artifactManager: Pick<ArtifactManager, 'list' | 'load'>;
19
+ dependenciesTable?: unknown;
20
+ }
21
+
22
+ export interface ArtifactMaterializer {
23
+ materialize(jobId: string): Promise<MaterializedArtifacts>;
24
+ }
25
+
26
+ export function createArtifactMaterializer(
27
+ options: ArtifactMaterializerOptions
28
+ ): ArtifactMaterializer {
29
+ const { db, artifactManager } = options;
30
+
31
+ return {
32
+ async materialize(jobId: string): Promise<MaterializedArtifacts> {
33
+ const artifacts = new Map<string, ArtifactContent>();
34
+
35
+ // Get job dependencies
36
+ const dependencies = await (db.select as any)()
37
+ .from(null)
38
+ .where({ jobId });
39
+
40
+ if (dependencies.length === 0) {
41
+ return { artifacts };
42
+ }
43
+
44
+ // Load artifacts from each dependency
45
+ for (const dep of dependencies) {
46
+ const parentJobId = dep.dependsOnJobId;
47
+
48
+ // List artifacts for parent job
49
+ const parentArtifacts = await artifactManager.list(parentJobId);
50
+
51
+ // Load each artifact
52
+ for (const ref of parentArtifacts) {
53
+ const content = await artifactManager.load(parentJobId, ref.name);
54
+ if (content) {
55
+ const path = `deps/${parentJobId}/${ref.name}`;
56
+ artifacts.set(path, content);
57
+ }
58
+ }
59
+ }
60
+
61
+ return { artifacts };
62
+ },
63
+ };
64
+ }
@@ -0,0 +1,151 @@
1
+ // ABOUTME: ArtifactRouter interface for kernel artifact visibility and routing
2
+ // ABOUTME: Determines which artifacts nodes can access and how outputs are routed
3
+
4
+ import type { ArtifactRef, GraphState } from './types.js';
5
+ import type { ArtifactVisibility } from './schema.js';
6
+
7
+ /**
8
+ * Graph edge definition (simplified for routing context).
9
+ */
10
+ export interface GraphEdge {
11
+ /** Source node ID */
12
+ source: string;
13
+ /** Target node ID */
14
+ target: string;
15
+ /** Named channel for artifact routing (optional) */
16
+ channel?: string;
17
+ }
18
+
19
+ /**
20
+ * Context provided for artifact routing decisions.
21
+ */
22
+ export interface RoutingContext {
23
+ /** Kernel session ID (the parent job ID) */
24
+ sessionId: string;
25
+ /** Current node ID in the graph */
26
+ nodeId: string;
27
+ /** Mind ID being executed (e.g., 'researcher', 'coordinator') */
28
+ mindId: string;
29
+ /** The incoming edge that triggered this node (optional for start nodes) */
30
+ edge?: GraphEdge;
31
+ /** Current graph execution state */
32
+ graphState: GraphState;
33
+ /** Account ID for the session */
34
+ accountId: string;
35
+ }
36
+
37
+ /**
38
+ * Hints provided by minds when saving artifacts.
39
+ */
40
+ export interface VisibilityHints {
41
+ /** Explicit visibility scope override */
42
+ scope?: ArtifactVisibility;
43
+ /** Specific recipient node IDs */
44
+ recipients?: string[];
45
+ /** Named channels to publish to */
46
+ channels?: string[];
47
+ /** Whether this artifact should persist beyond session */
48
+ persistent?: boolean;
49
+ }
50
+
51
+ /**
52
+ * Artifact with routing metadata.
53
+ */
54
+ export interface RoutedArtifact extends ArtifactRef {
55
+ /** Resolved visibility scope */
56
+ visibility: ArtifactVisibility;
57
+ /** Session this artifact belongs to */
58
+ sessionId: string;
59
+ /** Node that created this artifact */
60
+ nodeId: string;
61
+ /** Channels this artifact is published to */
62
+ channels: string[];
63
+ }
64
+
65
+ /**
66
+ * Options for resolving input artifacts.
67
+ */
68
+ export interface ResolveInputOptions {
69
+ /** Filter by visibility scopes */
70
+ visibilities?: ArtifactVisibility[];
71
+ /** Filter by specific channels */
72
+ channels?: string[];
73
+ /** Filter by source node IDs */
74
+ sourceNodes?: string[];
75
+ /** Include artifacts from all completed predecessor nodes */
76
+ includePredecessors?: boolean;
77
+ }
78
+
79
+ /**
80
+ * Options for routing output artifacts.
81
+ */
82
+ export interface RouteOutputOptions {
83
+ /** Artifact reference to route */
84
+ artifact: ArtifactRef;
85
+ /** Visibility hints from the mind */
86
+ hints: VisibilityHints;
87
+ }
88
+
89
+ /**
90
+ * Interface for artifact routing in kernel sessions.
91
+ *
92
+ * The router is responsible for:
93
+ * 1. Determining which artifacts a node can access as inputs
94
+ * 2. Routing output artifacts with appropriate visibility
95
+ *
96
+ * Implementation is decoupled from storage - the router only
97
+ * handles visibility and routing logic, not persistence.
98
+ */
99
+ export interface ArtifactRouter {
100
+ /**
101
+ * Resolve which artifacts are available as inputs for a node.
102
+ *
103
+ * This considers:
104
+ * - Artifacts from predecessor nodes (via edges)
105
+ * - Session-visible artifacts
106
+ * - Channel subscriptions
107
+ *
108
+ * @param context - Current routing context
109
+ * @param options - Optional filtering options
110
+ * @returns List of accessible artifact references
111
+ */
112
+ resolveInputArtifacts(
113
+ context: RoutingContext,
114
+ options?: ResolveInputOptions
115
+ ): Promise<ArtifactRef[]>;
116
+
117
+ /**
118
+ * Route output artifacts with visibility and channel assignments.
119
+ *
120
+ * This determines:
121
+ * - Final visibility scope (from hints or defaults)
122
+ * - Channel assignments
123
+ * - Session and node attribution
124
+ *
125
+ * @param outputs - Artifacts with visibility hints
126
+ * @param context - Current routing context
127
+ * @returns Routed artifacts with resolved metadata
128
+ */
129
+ routeOutputArtifacts(
130
+ outputs: RouteOutputOptions[],
131
+ context: RoutingContext
132
+ ): Promise<RoutedArtifact[]>;
133
+
134
+ /**
135
+ * Get default visibility for a given artifact path.
136
+ *
137
+ * Visibility can be inferred from path patterns:
138
+ * - `/sessions/{s}/nodes/{n}/private/**` → private
139
+ * - `/sessions/{s}/nodes/{n}/outputs/**` → session
140
+ * - `/sessions/{s}/shared/**` → session
141
+ * - `/persistent/**` → persistent
142
+ *
143
+ * @param path - Artifact path
144
+ * @param context - Routing context
145
+ * @returns Default visibility scope
146
+ */
147
+ getDefaultVisibility(
148
+ path: string,
149
+ context: RoutingContext
150
+ ): ArtifactVisibility;
151
+ }