alaska-ai 0.1.0

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 (74) hide show
  1. package/README.md +56 -0
  2. package/dist/adapters/slack.d.ts +130 -0
  3. package/dist/adapters/slack.js +1484 -0
  4. package/dist/adapters/slack.js.map +1 -0
  5. package/dist/backends/claude.d.ts +78 -0
  6. package/dist/backends/claude.js +452 -0
  7. package/dist/backends/claude.js.map +1 -0
  8. package/dist/backends/codex.d.ts +53 -0
  9. package/dist/backends/codex.js +324 -0
  10. package/dist/backends/codex.js.map +1 -0
  11. package/dist/cli/init.d.ts +50 -0
  12. package/dist/cli/init.js +386 -0
  13. package/dist/cli/init.js.map +1 -0
  14. package/dist/cli/prompt.d.ts +31 -0
  15. package/dist/cli/prompt.js +145 -0
  16. package/dist/cli/prompt.js.map +1 -0
  17. package/dist/cli/start.d.ts +28 -0
  18. package/dist/cli/start.js +522 -0
  19. package/dist/cli/start.js.map +1 -0
  20. package/dist/cli.d.ts +2 -0
  21. package/dist/cli.js +65 -0
  22. package/dist/cli.js.map +1 -0
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.js +8 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/mcp/callbacks.d.ts +32 -0
  27. package/dist/mcp/callbacks.js +181 -0
  28. package/dist/mcp/callbacks.js.map +1 -0
  29. package/dist/mcp/config.d.ts +15 -0
  30. package/dist/mcp/config.js +22 -0
  31. package/dist/mcp/config.js.map +1 -0
  32. package/dist/mcp/entry.d.ts +13 -0
  33. package/dist/mcp/entry.js +119 -0
  34. package/dist/mcp/entry.js.map +1 -0
  35. package/dist/mcp/file-browser.d.ts +15 -0
  36. package/dist/mcp/file-browser.js +135 -0
  37. package/dist/mcp/file-browser.js.map +1 -0
  38. package/dist/mcp/ipc-server.d.ts +64 -0
  39. package/dist/mcp/ipc-server.js +380 -0
  40. package/dist/mcp/ipc-server.js.map +1 -0
  41. package/dist/mcp/preview-server.d.ts +33 -0
  42. package/dist/mcp/preview-server.js +254 -0
  43. package/dist/mcp/preview-server.js.map +1 -0
  44. package/dist/mcp/server.d.ts +51 -0
  45. package/dist/mcp/server.js +257 -0
  46. package/dist/mcp/server.js.map +1 -0
  47. package/dist/mcp/tunnel.d.ts +17 -0
  48. package/dist/mcp/tunnel.js +154 -0
  49. package/dist/mcp/tunnel.js.map +1 -0
  50. package/dist/router.d.ts +113 -0
  51. package/dist/router.js +511 -0
  52. package/dist/router.js.map +1 -0
  53. package/dist/sandbox-policy.d.ts +6 -0
  54. package/dist/sandbox-policy.js +46 -0
  55. package/dist/sandbox-policy.js.map +1 -0
  56. package/dist/scheduler.d.ts +42 -0
  57. package/dist/scheduler.js +169 -0
  58. package/dist/scheduler.js.map +1 -0
  59. package/dist/store.d.ts +95 -0
  60. package/dist/store.js +353 -0
  61. package/dist/store.js.map +1 -0
  62. package/dist/types/adapter.d.ts +50 -0
  63. package/dist/types/adapter.js +9 -0
  64. package/dist/types/adapter.js.map +1 -0
  65. package/dist/types/backend.d.ts +73 -0
  66. package/dist/types/backend.js +8 -0
  67. package/dist/types/backend.js.map +1 -0
  68. package/dist/types/events.d.ts +47 -0
  69. package/dist/types/events.js +9 -0
  70. package/dist/types/events.js.map +1 -0
  71. package/dist/utils.d.ts +59 -0
  72. package/dist/utils.js +272 -0
  73. package/dist/utils.js.map +1 -0
  74. package/package.json +50 -0
package/dist/router.js ADDED
@@ -0,0 +1,511 @@
1
+ /**
2
+ * Router for Alaska.
3
+ *
4
+ * Maps channel/thread pairs to projects and sessions, manages backend
5
+ * lifecycle, and enforces session state machine transitions.
6
+ */
7
+ import { cleanupStagingFiles } from './utils.js';
8
+ import { assertProjectDirAllowed, requireSandboxRoot } from './sandbox-policy.js';
9
+ /** Default timeout for backend.send() in milliseconds (0 = disabled). */
10
+ const DEFAULT_TIMEOUT_MS = 0;
11
+ /** Default idle timeout for persistent backends in ms (5 minutes). */
12
+ const DEFAULT_IDLE_TIMEOUT_MS = 5 * 60 * 1000;
13
+ /** How often to check for idle backends (60 seconds). */
14
+ const IDLE_CHECK_INTERVAL_MS = 60 * 1000;
15
+ export class Router {
16
+ store;
17
+ backendFactory;
18
+ activeBackends = new Map();
19
+ timeoutMs;
20
+ mcpConfigFactory;
21
+ ipc;
22
+ hookScriptDir;
23
+ /** Per-thread lock to serialize concurrent sends/responds to the same thread. */
24
+ threadLocks = new Map();
25
+ /** Last activity timestamp per thread for idle timeout cleanup. */
26
+ lastActivity = new Map();
27
+ /** Idle cleanup interval handle. */
28
+ idleCleanupInterval = null;
29
+ constructor(store, backendFactory, options) {
30
+ this.store = store;
31
+ this.backendFactory = backendFactory;
32
+ this.timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
33
+ this.mcpConfigFactory = options?.mcpConfigFactory;
34
+ this.ipc = options?.ipc;
35
+ this.hookScriptDir = options?.hookScriptDir;
36
+ // Start periodic idle cleanup
37
+ this.idleCleanupInterval = setInterval(() => this.cleanupIdleBackends(), IDLE_CHECK_INTERVAL_MS);
38
+ // Don't prevent process exit
39
+ if (this.idleCleanupInterval.unref) {
40
+ this.idleCleanupInterval.unref();
41
+ }
42
+ }
43
+ /**
44
+ * Clean up backends that have been idle for longer than the timeout.
45
+ */
46
+ cleanupIdleBackends() {
47
+ const now = Date.now();
48
+ for (const [threadId, lastTime] of this.lastActivity) {
49
+ if (now - lastTime > DEFAULT_IDLE_TIMEOUT_MS) {
50
+ const backend = this.activeBackends.get(threadId);
51
+ if (backend) {
52
+ console.log(`[router] cleaning up idle backend for thread ${threadId}`);
53
+ backend.stop().catch(() => { });
54
+ this.activeBackends.delete(threadId);
55
+ }
56
+ this.lastActivity.delete(threadId);
57
+ }
58
+ }
59
+ }
60
+ /**
61
+ * Get an existing alive backend for a thread, or create and start a new one.
62
+ * Returns the backend ready for send().
63
+ */
64
+ async getOrCreateBackend(threadId, project, channelId) {
65
+ const sandboxRoot = await requireSandboxRoot(this.store);
66
+ const allowedProjectDir = assertProjectDirAllowed(project.project_dir, sandboxRoot);
67
+ // Check for existing alive backend
68
+ const existing = this.activeBackends.get(threadId);
69
+ if (existing && existing.isAlive()) {
70
+ console.log(`[router] reusing persistent backend for thread ${threadId}`);
71
+ return existing;
72
+ }
73
+ // Clean up dead backend if present
74
+ if (existing) {
75
+ this.activeBackends.delete(threadId);
76
+ }
77
+ // Create and initialize a new backend
78
+ const backend = this.backendFactory(project.backend_name);
79
+ // Set session ID before start() so SDK backends can resume the conversation
80
+ const storedSession = await this.store.getSessionByThreadId(threadId);
81
+ if (storedSession?.backend_session_id) {
82
+ backend.setSessionId(storedSession.backend_session_id);
83
+ console.log(`[router] restoring session ${storedSession.backend_session_id} for thread ${threadId}`);
84
+ }
85
+ const mcpConfig = this.mcpConfigFactory?.({
86
+ channelId,
87
+ threadId,
88
+ projectDir: allowedProjectDir,
89
+ platform: project.platform,
90
+ });
91
+ await backend.start({
92
+ projectDir: allowedProjectDir,
93
+ mcpConfig,
94
+ ipc: this.ipc,
95
+ channelId,
96
+ threadId,
97
+ platform: project.platform,
98
+ hookScriptDir: this.hookScriptDir,
99
+ permissionMode: project.permission_mode,
100
+ sandboxMode: project.sandbox_mode,
101
+ });
102
+ this.activeBackends.set(threadId, backend);
103
+ return backend;
104
+ }
105
+ /**
106
+ * Acquire a per-thread lock. If another operation is in progress for this
107
+ * thread, the returned promise won't resolve until it finishes.
108
+ * Returns a release function to call when the operation completes.
109
+ */
110
+ acquireThreadLock(threadId) {
111
+ const existing = this.threadLocks.get(threadId);
112
+ let release;
113
+ const newLock = new Promise((resolve) => {
114
+ release = resolve;
115
+ });
116
+ this.threadLocks.set(threadId, newLock);
117
+ const acquire = async () => {
118
+ if (existing) {
119
+ console.log(`[router] thread ${threadId} is busy — queuing message`);
120
+ await existing;
121
+ }
122
+ return () => {
123
+ // Only clear if this is still the active lock (another may have queued)
124
+ if (this.threadLocks.get(threadId) === newLock) {
125
+ this.threadLocks.delete(threadId);
126
+ }
127
+ release();
128
+ };
129
+ };
130
+ return acquire();
131
+ }
132
+ /** Send with timeout — races backend.send() against a timeout. */
133
+ async sendWithTimeout(backend, text, files) {
134
+ if (this.timeoutMs <= 0) {
135
+ return backend.send(text, files);
136
+ }
137
+ let timer;
138
+ const result = await Promise.race([
139
+ backend.send(text, files).then((r) => {
140
+ clearTimeout(timer);
141
+ return r;
142
+ }),
143
+ new Promise((_, reject) => {
144
+ timer = setTimeout(async () => {
145
+ // Kill the orphaned backend process
146
+ try {
147
+ await backend.stop();
148
+ }
149
+ catch {
150
+ // Ignore stop errors during timeout cleanup
151
+ }
152
+ reject(new Error(`Backend timed out after ${Math.round(this.timeoutMs / 1000)} seconds. ` +
153
+ 'The operation took too long and was killed.'));
154
+ }, this.timeoutMs);
155
+ }),
156
+ ]);
157
+ return result;
158
+ }
159
+ /**
160
+ * Resolve a channel + thread to a project and session.
161
+ * Returns null if the channel is not bound to a project.
162
+ * Creates a new session if the thread is unknown in a bound channel.
163
+ */
164
+ async resolve(channelId, threadId) {
165
+ const project = await this.store.getProjectByChannelId(channelId);
166
+ if (!project) {
167
+ return null;
168
+ }
169
+ let session = await this.store.getSessionByThreadId(threadId);
170
+ if (!session) {
171
+ session = await this.store.createSession(threadId, project.id);
172
+ console.log(`[router] created new session for thread ${threadId} in project ${project.id}`);
173
+ }
174
+ return { project, session };
175
+ }
176
+ /** Prepend context metadata (time, source platform) so the backend can reason about relative dates and message origin. */
177
+ prependContext(text, platform) {
178
+ const now = new Date().toLocaleString('en-US', {
179
+ weekday: 'long',
180
+ year: 'numeric',
181
+ month: 'long',
182
+ day: 'numeric',
183
+ hour: 'numeric',
184
+ minute: '2-digit',
185
+ hour12: true,
186
+ });
187
+ return `[Current time: ${now}]\n[Source: ${platform}]\n[You are responding in a chat thread. Keep your final text responses succinct and scannable.]\n\n${text}`;
188
+ }
189
+ /** Augment prompt text with upload info for files that have staging metadata. */
190
+ augmentTextWithUploadInfo(text, files) {
191
+ if (!files || files.length === 0)
192
+ return text;
193
+ const uploadLines = [];
194
+ for (const f of files) {
195
+ if (f.uploadId && f.filename) {
196
+ uploadLines.push(`[Uploaded file: ${f.filename} (upload_id: ${f.uploadId}). ` +
197
+ `To save this file to the project, use the save_uploaded_file tool.]`);
198
+ }
199
+ }
200
+ if (uploadLines.length === 0)
201
+ return text;
202
+ return `${text}\n\n${uploadLines.join('\n')}`;
203
+ }
204
+ /** Clean up staging files from file attachments. */
205
+ cleanupFileStaging(files) {
206
+ if (!files)
207
+ return;
208
+ const paths = files
209
+ .map((f) => f.stagingPath)
210
+ .filter((p) => !!p);
211
+ if (paths.length > 0) {
212
+ cleanupStagingFiles(paths);
213
+ }
214
+ }
215
+ /**
216
+ * Send a message through the backend and return normalized events.
217
+ * Manages session state transitions and persists backend session ID.
218
+ */
219
+ async send(channelId, threadId, text, files) {
220
+ const release = await this.acquireThreadLock(threadId);
221
+ try {
222
+ return await this._send(channelId, threadId, text, files);
223
+ }
224
+ finally {
225
+ release();
226
+ }
227
+ }
228
+ async _send(channelId, threadId, text, files) {
229
+ const resolved = await this.resolve(channelId, threadId);
230
+ if (!resolved) {
231
+ throw new Error(`Channel ${channelId} is not connected to a project`);
232
+ }
233
+ const { project, session } = resolved;
234
+ // If session crashed previously, auto-recover: dead → idle, clear backend session
235
+ if (session.state === 'dead') {
236
+ await this.store.updateSessionState(session.id, 'idle');
237
+ await this.store.updateBackendSessionId(session.id, null);
238
+ console.log(`[router] auto-recovered dead session ${session.id} — starting fresh`);
239
+ }
240
+ // Transition to running
241
+ await this.store.updateSessionState(session.id, 'running');
242
+ // Get or create a backend for this thread (persistent pool)
243
+ const backend = await this.getOrCreateBackend(threadId, project, channelId);
244
+ // Load accumulated allowed tools from the store (P12.6)
245
+ const accumulatedTools = (await this.store.getAllowedTools(project.id)).map(t => t.tool_pattern);
246
+ if (accumulatedTools.length > 0) {
247
+ backend.setAllowedTools(accumulatedTools);
248
+ }
249
+ // Prepend context (time + source platform) so backend can reason about dates and message origin
250
+ const timedText = this.prependContext(text, project.platform);
251
+ // Augment prompt with upload info so backend knows about staged files
252
+ const augmentedText = this.augmentTextWithUploadInfo(timedText, files);
253
+ // Track activity for idle timeout
254
+ this.lastActivity.set(threadId, Date.now());
255
+ let result;
256
+ try {
257
+ result = await this.sendWithTimeout(backend, augmentedText, files);
258
+ }
259
+ catch (err) {
260
+ // Backend crashed or timed out — clean up and transition to dead
261
+ // But if cancelBackend already cleaned up, skip state transition
262
+ try {
263
+ await backend.stop();
264
+ }
265
+ catch { /* ignore stop errors */ }
266
+ this.activeBackends.delete(session.thread_id);
267
+ this.lastActivity.delete(session.thread_id);
268
+ // TODO: Re-enable staging cleanup once per-session TTL is implemented
269
+ // this.cleanupFileStaging(files);
270
+ const currentState = (await this.store.getSessionById(session.id))?.state;
271
+ if (currentState === 'running') {
272
+ await this.store.updateSessionState(session.id, 'dead');
273
+ }
274
+ console.log(`[router] backend failed for session ${session.id}:`, err);
275
+ throw err;
276
+ }
277
+ // Update activity timestamp
278
+ this.lastActivity.set(threadId, Date.now());
279
+ // If the backend died after send (CLI backends exit after each call),
280
+ // clean up so the next send creates a fresh one.
281
+ if (!backend.isAlive()) {
282
+ this.activeBackends.delete(session.thread_id);
283
+ this.lastActivity.delete(session.thread_id);
284
+ }
285
+ // If cancelBackend already intervened, the session is no longer running.
286
+ // Skip state transitions and just return the partial events.
287
+ const postSendSession = await this.store.getSessionById(session.id);
288
+ if (!postSendSession || postSendSession.state !== 'running') {
289
+ console.log(`[router] session ${session.id} already transitioned (state: ${postSendSession?.state}), skipping post-send transition`);
290
+ return { events: result.events, session: postSendSession ?? session };
291
+ }
292
+ // Store the backend session ID for future resume
293
+ if (result.sessionId) {
294
+ await this.store.updateBackendSessionId(session.id, result.sessionId);
295
+ }
296
+ // Check if any events are PermissionDenied
297
+ const hasPermissionDenied = result.events.some((e) => e.type === 'permission_denied');
298
+ if (hasPermissionDenied) {
299
+ // Transition to waiting_for_input
300
+ await this.store.updateSessionState(session.id, 'waiting_for_input');
301
+ console.log(`[router] session ${session.id} waiting for input (permission denied)`);
302
+ }
303
+ else {
304
+ // Transition back to idle
305
+ await this.store.updateSessionState(session.id, 'idle');
306
+ }
307
+ // TODO: Re-enable staging cleanup once per-session TTL is implemented
308
+ // this.cleanupFileStaging(files);
309
+ // Fetch the updated session
310
+ const updatedSession = await this.store.getSessionById(session.id);
311
+ if (!updatedSession) {
312
+ throw new Error(`Session ${session.id} not found after send`);
313
+ }
314
+ return {
315
+ events: result.events,
316
+ session: updatedSession,
317
+ };
318
+ }
319
+ /**
320
+ * Handle a user response when a session is waiting_for_input.
321
+ * This resumes the backend with the user's response text.
322
+ * When allowedTools is provided, the backend will auto-approve those tools.
323
+ */
324
+ async respond(channelId, threadId, text, allowedTools) {
325
+ const release = await this.acquireThreadLock(threadId);
326
+ try {
327
+ return await this._respond(channelId, threadId, text, allowedTools);
328
+ }
329
+ finally {
330
+ release();
331
+ }
332
+ }
333
+ async _respond(channelId, threadId, text, allowedTools) {
334
+ const resolved = await this.resolve(channelId, threadId);
335
+ if (!resolved) {
336
+ throw new Error(`Channel ${channelId} is not connected to a project`);
337
+ }
338
+ const { project, session } = resolved;
339
+ if (session.state !== 'waiting_for_input') {
340
+ throw new Error(`Session ${session.id} is not waiting for input (state: ${session.state})`);
341
+ }
342
+ // Transition to running
343
+ await this.store.updateSessionState(session.id, 'running');
344
+ // Get or create a backend for this thread (persistent pool)
345
+ const backend = await this.getOrCreateBackend(threadId, project, channelId);
346
+ // Merge one-shot allowed tools with accumulated tools from the store (P12.6)
347
+ const accumulatedTools = (await this.store.getAllowedTools(project.id)).map(t => t.tool_pattern);
348
+ const mergedTools = [...new Set([...(allowedTools ?? []), ...accumulatedTools])];
349
+ if (mergedTools.length > 0) {
350
+ backend.setAllowedTools(mergedTools);
351
+ }
352
+ // Prepend context (time + source platform) so backend can reason about dates and message origin
353
+ const timedText = this.prependContext(text, project.platform);
354
+ // Track activity for idle timeout
355
+ this.lastActivity.set(threadId, Date.now());
356
+ let result;
357
+ try {
358
+ result = await this.sendWithTimeout(backend, timedText);
359
+ }
360
+ catch (err) {
361
+ try {
362
+ await backend.stop();
363
+ }
364
+ catch { /* ignore stop errors */ }
365
+ this.activeBackends.delete(session.thread_id);
366
+ this.lastActivity.delete(session.thread_id);
367
+ const currentState = (await this.store.getSessionById(session.id))?.state;
368
+ if (currentState === 'running') {
369
+ await this.store.updateSessionState(session.id, 'dead');
370
+ }
371
+ throw err;
372
+ }
373
+ // Update activity timestamp
374
+ this.lastActivity.set(threadId, Date.now());
375
+ // If the backend died after send, clean up
376
+ if (!backend.isAlive()) {
377
+ this.activeBackends.delete(session.thread_id);
378
+ this.lastActivity.delete(session.thread_id);
379
+ }
380
+ // If cancelBackend already intervened, skip state transitions
381
+ const postRespondSession = await this.store.getSessionById(session.id);
382
+ if (!postRespondSession || postRespondSession.state !== 'running') {
383
+ console.log(`[router] session ${session.id} already transitioned (state: ${postRespondSession?.state}), skipping post-respond transition`);
384
+ return { events: result.events, session: postRespondSession ?? session };
385
+ }
386
+ // Store updated session ID
387
+ if (result.sessionId) {
388
+ await this.store.updateBackendSessionId(session.id, result.sessionId);
389
+ }
390
+ // Check for another permission denied
391
+ const hasPermissionDenied = result.events.some((e) => e.type === 'permission_denied');
392
+ if (hasPermissionDenied) {
393
+ await this.store.updateSessionState(session.id, 'waiting_for_input');
394
+ }
395
+ else {
396
+ await this.store.updateSessionState(session.id, 'idle');
397
+ }
398
+ const updatedSession = await this.store.getSessionById(session.id);
399
+ if (!updatedSession) {
400
+ throw new Error(`Session ${session.id} not found after respond`);
401
+ }
402
+ return {
403
+ events: result.events,
404
+ session: updatedSession,
405
+ };
406
+ }
407
+ /**
408
+ * Reset a session — clear the backend session ID so next message starts fresh.
409
+ */
410
+ async resetSession(channelId, threadId) {
411
+ const resolved = await this.resolve(channelId, threadId);
412
+ if (!resolved) {
413
+ throw new Error(`Channel ${channelId} is not connected to a project`);
414
+ }
415
+ const { session } = resolved;
416
+ // Stop any persistent backend for this thread
417
+ const backend = this.activeBackends.get(threadId);
418
+ if (backend) {
419
+ backend.stop().catch(() => { });
420
+ this.activeBackends.delete(threadId);
421
+ this.lastActivity.delete(threadId);
422
+ }
423
+ // If session is in a non-idle state, transition to idle
424
+ if (session.state === 'running' || session.state === 'waiting_for_input') {
425
+ // Force to dead first, then idle
426
+ if (session.state === 'running') {
427
+ await this.store.updateSessionState(session.id, 'dead');
428
+ await this.store.updateSessionState(session.id, 'idle');
429
+ }
430
+ else {
431
+ // waiting_for_input → running → dead → idle
432
+ await this.store.updateSessionState(session.id, 'running');
433
+ await this.store.updateSessionState(session.id, 'dead');
434
+ await this.store.updateSessionState(session.id, 'idle');
435
+ }
436
+ }
437
+ else if (session.state === 'dead') {
438
+ await this.store.updateSessionState(session.id, 'idle');
439
+ }
440
+ // If already idle, nothing to do for state
441
+ // Clear backend session ID so next send starts fresh
442
+ await this.store.updateBackendSessionId(session.id, null);
443
+ const updatedSession = await this.store.getSessionById(session.id);
444
+ if (!updatedSession) {
445
+ throw new Error(`Session ${session.id} not found after reset`);
446
+ }
447
+ console.log(`[router] session ${session.id} reset`);
448
+ return updatedSession;
449
+ }
450
+ /**
451
+ * Cancel a running backend for a thread. Uses interrupt() for clean
452
+ * cancellation, then stop() to terminate. Transitions the session back to idle.
453
+ * Returns true if a backend was cancelled, false if nothing was running.
454
+ */
455
+ async cancelBackend(channelId, threadId) {
456
+ const backend = this.activeBackends.get(threadId);
457
+ if (!backend) {
458
+ return false;
459
+ }
460
+ console.log(`[router] cancelling backend for thread ${threadId}`);
461
+ try {
462
+ // Try clean interrupt first
463
+ await backend.interrupt();
464
+ }
465
+ catch {
466
+ // Fall through to stop
467
+ }
468
+ try {
469
+ await backend.stop();
470
+ }
471
+ catch {
472
+ // Ignore stop errors during cancel
473
+ }
474
+ this.activeBackends.delete(threadId);
475
+ this.lastActivity.delete(threadId);
476
+ // Transition session back to idle
477
+ const resolved = await this.resolve(channelId, threadId);
478
+ if (resolved) {
479
+ const { session } = resolved;
480
+ if (session.state === 'running') {
481
+ await this.store.updateSessionState(session.id, 'dead');
482
+ await this.store.updateSessionState(session.id, 'idle');
483
+ console.log(`[router] session ${session.id} cancelled and reset to idle`);
484
+ }
485
+ }
486
+ return true;
487
+ }
488
+ /**
489
+ * Graceful shutdown — stop all active backend sessions and clean up.
490
+ */
491
+ async shutdown() {
492
+ // Stop idle cleanup
493
+ if (this.idleCleanupInterval) {
494
+ clearInterval(this.idleCleanupInterval);
495
+ this.idleCleanupInterval = null;
496
+ }
497
+ console.log(`[router] shutting down ${this.activeBackends.size} active backend(s)`);
498
+ for (const [threadId, backend] of this.activeBackends) {
499
+ try {
500
+ await backend.stop();
501
+ console.log(`[router] stopped backend for thread ${threadId}`);
502
+ }
503
+ catch (err) {
504
+ console.error(`[router] error stopping backend for thread ${threadId}:`, err);
505
+ }
506
+ }
507
+ this.activeBackends.clear();
508
+ this.lastActivity.clear();
509
+ }
510
+ }
511
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { Store } from './store.js';
2
+ export declare function isPathInsideRoot(candidatePath: string, sandboxRoot: string): boolean;
3
+ export declare function validateSandboxRoot(rootPath: string): string;
4
+ export declare function resolveWithinSandbox(inputPath: string, sandboxRoot: string): string;
5
+ export declare function assertProjectDirAllowed(projectDir: string, sandboxRoot: string): string;
6
+ export declare function requireSandboxRoot(store: Store): Promise<string>;
@@ -0,0 +1,46 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ function normalizeForCompare(p) {
4
+ const resolved = path.resolve(p);
5
+ return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
6
+ }
7
+ export function isPathInsideRoot(candidatePath, sandboxRoot) {
8
+ const candidate = normalizeForCompare(candidatePath);
9
+ const root = normalizeForCompare(sandboxRoot);
10
+ const relative = path.relative(root, candidate);
11
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
12
+ }
13
+ export function validateSandboxRoot(rootPath) {
14
+ const resolved = path.resolve(rootPath);
15
+ if (!path.isAbsolute(resolved)) {
16
+ throw new Error('Projects root must be an absolute path.');
17
+ }
18
+ if (!fs.existsSync(resolved)) {
19
+ throw new Error(`Projects root does not exist: ${resolved}`);
20
+ }
21
+ if (!fs.statSync(resolved).isDirectory()) {
22
+ throw new Error(`Projects root is not a directory: ${resolved}`);
23
+ }
24
+ return resolved;
25
+ }
26
+ export function resolveWithinSandbox(inputPath, sandboxRoot) {
27
+ const root = validateSandboxRoot(sandboxRoot);
28
+ const resolved = path.isAbsolute(inputPath)
29
+ ? path.resolve(inputPath)
30
+ : path.resolve(root, inputPath);
31
+ if (!isPathInsideRoot(resolved, root)) {
32
+ throw new Error(`Path is outside sandbox root (${root}): ${inputPath}`);
33
+ }
34
+ return resolved;
35
+ }
36
+ export function assertProjectDirAllowed(projectDir, sandboxRoot) {
37
+ return resolveWithinSandbox(projectDir, sandboxRoot);
38
+ }
39
+ export async function requireSandboxRoot(store) {
40
+ const root = await store.getSetting('projects_root');
41
+ if (!root) {
42
+ throw new Error('Projects root is not configured. Run `alaska-ai init` or `/settings root /absolute/path`.');
43
+ }
44
+ return validateSandboxRoot(root);
45
+ }
46
+ //# sourceMappingURL=sandbox-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-policy.js","sourceRoot":"","sources":["../src/sandbox-policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,SAAS,mBAAmB,CAAC,CAAS;IACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACjC,OAAO,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,aAAqB,EAAE,WAAmB;IACzE,MAAM,SAAS,GAAG,mBAAmB,CAAC,aAAa,CAAC,CAAC;IACrD,MAAM,IAAI,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAChD,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IACD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,iCAAiC,QAAQ,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,qCAAqC,QAAQ,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,SAAiB,EAAE,WAAmB;IACzE,MAAM,IAAI,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QACzC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;QACzB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAElC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,MAAM,SAAS,EAAE,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,UAAkB,EAAE,WAAmB;IAC7E,OAAO,oBAAoB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAY;IACnD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;IACrD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,2FAA2F,CAAC,CAAC;IAC/G,CAAC;IACD,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Scheduler for Alaska scheduled sessions.
3
+ *
4
+ * Runs a periodic check loop that fires due schedules by creating
5
+ * threads and running full backend sessions via the router.
6
+ */
7
+ import type { Store } from './store.js';
8
+ import type { Router } from './router.js';
9
+ import type { Adapter } from './types/adapter.js';
10
+ /** Default scheduler tick interval in milliseconds (30 seconds). */
11
+ export declare const DEFAULT_TICK_INTERVAL_MS = 30000;
12
+ /** System timezone detected at startup, e.g. "America/New_York". */
13
+ export declare const SYSTEM_TIMEZONE: string;
14
+ /**
15
+ * Compute the next run time from a cron expression in the system timezone.
16
+ * Returns an ISO 8601 datetime string (UTC).
17
+ * Throws if the cron expression is invalid.
18
+ */
19
+ export declare function computeNextRun(cronExpression: string): string;
20
+ export interface SchedulerOptions {
21
+ /** Tick interval in milliseconds. Defaults to DEFAULT_TICK_INTERVAL_MS (60s). */
22
+ tickIntervalMs?: number;
23
+ }
24
+ export declare class Scheduler {
25
+ private store;
26
+ private router;
27
+ private adapters;
28
+ private timer;
29
+ private tickIntervalMs;
30
+ private ticking;
31
+ constructor(store: Store, router: Router, adapters: Map<string, Adapter>, options?: SchedulerOptions);
32
+ /** Start the scheduler loop. Runs an immediate tick, then repeats at the configured interval. */
33
+ start(): void;
34
+ /** Stop the scheduler loop. */
35
+ stop(): void;
36
+ /** Check for due schedules and fire them. */
37
+ tick(): Promise<void>;
38
+ /** Fire a single schedule: create thread, run session, handle aftermath. */
39
+ private fire;
40
+ /** Run a session in a thread and post the results. Throws on failure. */
41
+ private runSession;
42
+ }