@tagma/sdk 0.7.3 → 0.7.4

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 (92) hide show
  1. package/README.md +26 -5
  2. package/dist/adapters/stdin-approval.d.ts +1 -5
  3. package/dist/adapters/stdin-approval.d.ts.map +1 -1
  4. package/dist/adapters/stdin-approval.js +1 -89
  5. package/dist/adapters/stdin-approval.js.map +1 -1
  6. package/dist/adapters/websocket-approval.d.ts +1 -27
  7. package/dist/adapters/websocket-approval.d.ts.map +1 -1
  8. package/dist/adapters/websocket-approval.js +1 -146
  9. package/dist/adapters/websocket-approval.js.map +1 -1
  10. package/dist/approval.d.ts +2 -12
  11. package/dist/approval.d.ts.map +1 -1
  12. package/dist/approval.js +1 -90
  13. package/dist/approval.js.map +1 -1
  14. package/dist/bootstrap.d.ts +1 -1
  15. package/dist/bootstrap.d.ts.map +1 -1
  16. package/dist/core/task-executor.d.ts.map +1 -1
  17. package/dist/core/task-executor.js +13 -4
  18. package/dist/core/task-executor.js.map +1 -1
  19. package/dist/engine.d.ts +5 -56
  20. package/dist/engine.d.ts.map +1 -1
  21. package/dist/engine.js +7 -297
  22. package/dist/engine.js.map +1 -1
  23. package/dist/index.d.ts +4 -6
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +2 -4
  26. package/dist/index.js.map +1 -1
  27. package/dist/logger.d.ts +2 -60
  28. package/dist/logger.d.ts.map +1 -1
  29. package/dist/logger.js +1 -153
  30. package/dist/logger.js.map +1 -1
  31. package/dist/plugins.d.ts +2 -2
  32. package/dist/plugins.d.ts.map +1 -1
  33. package/dist/plugins.js +1 -1
  34. package/dist/plugins.js.map +1 -1
  35. package/dist/registry.d.ts +2 -66
  36. package/dist/registry.d.ts.map +1 -1
  37. package/dist/registry.js +1 -292
  38. package/dist/registry.js.map +1 -1
  39. package/dist/runner.d.ts +1 -35
  40. package/dist/runner.d.ts.map +1 -1
  41. package/dist/runner.js +1 -610
  42. package/dist/runner.js.map +1 -1
  43. package/dist/runtime/adapters/stdin-approval.d.ts +2 -0
  44. package/dist/runtime/adapters/stdin-approval.d.ts.map +1 -0
  45. package/dist/runtime/adapters/stdin-approval.js +2 -0
  46. package/dist/runtime/adapters/stdin-approval.js.map +1 -0
  47. package/dist/runtime/adapters/websocket-approval.d.ts +2 -0
  48. package/dist/runtime/adapters/websocket-approval.d.ts.map +1 -0
  49. package/dist/runtime/adapters/websocket-approval.js +2 -0
  50. package/dist/runtime/adapters/websocket-approval.js.map +1 -0
  51. package/dist/runtime/bun-process-runner.d.ts +2 -0
  52. package/dist/runtime/bun-process-runner.d.ts.map +1 -0
  53. package/dist/runtime/bun-process-runner.js +2 -0
  54. package/dist/runtime/bun-process-runner.js.map +1 -0
  55. package/dist/runtime.d.ts +2 -8
  56. package/dist/runtime.d.ts.map +1 -1
  57. package/dist/runtime.js +1 -7
  58. package/dist/runtime.js.map +1 -1
  59. package/dist/tagma.d.ts +3 -4
  60. package/dist/tagma.d.ts.map +1 -1
  61. package/dist/tagma.js +2 -3
  62. package/dist/tagma.js.map +1 -1
  63. package/dist/triggers/file.d.ts.map +1 -1
  64. package/dist/triggers/file.js +74 -107
  65. package/dist/triggers/file.js.map +1 -1
  66. package/package.json +15 -4
  67. package/src/adapters/stdin-approval.ts +1 -106
  68. package/src/adapters/websocket-approval.ts +1 -224
  69. package/src/approval.ts +5 -127
  70. package/src/bootstrap.ts +1 -1
  71. package/src/core/run-context.test.ts +35 -0
  72. package/src/core/task-executor.ts +13 -4
  73. package/src/engine-ports-mixed.test.ts +70 -44
  74. package/src/engine-ports.test.ts +77 -33
  75. package/src/engine.ts +18 -444
  76. package/src/index.ts +4 -6
  77. package/src/logger.ts +2 -182
  78. package/src/package-split.test.ts +15 -0
  79. package/src/pipeline-runner.test.ts +65 -12
  80. package/src/plugin-registry.test.ts +69 -3
  81. package/src/plugins.ts +2 -2
  82. package/src/registry.ts +7 -353
  83. package/src/runner.ts +1 -666
  84. package/src/runtime/adapters/stdin-approval.ts +1 -0
  85. package/src/runtime/adapters/websocket-approval.ts +1 -0
  86. package/src/runtime/bun-process-runner.ts +1 -0
  87. package/src/runtime-adapters.test.ts +10 -0
  88. package/src/runtime.ts +12 -20
  89. package/src/tagma.test.ts +162 -0
  90. package/src/tagma.ts +9 -4
  91. package/src/triggers/file.test.ts +79 -0
  92. package/src/triggers/file.ts +85 -118
package/README.md CHANGED
@@ -64,6 +64,8 @@ console.log(result.success ? 'Done' : 'Failed');
64
64
 
65
65
  The package root is intentionally small. Use explicit subpaths for YAML,
66
66
  config editing, plugin registry helpers, and low-level dataflow utilities.
67
+ The SDK composes `@tagma/core` with `@tagma/runtime-bun`; import those packages
68
+ directly only when you need lower-level package boundaries.
67
69
 
68
70
  ## Features
69
71
 
@@ -408,6 +410,18 @@ YAML `ports` has been replaced by typed `inputs` / `outputs`. `validateRaw` repo
408
410
  - placeholder substitution, binding resolution, output extraction, and internal prompt-contract inference
409
411
  - the subpath name is historical; YAML `ports` is rejected by validation
410
412
 
413
+ ### Runtime approval adapters
414
+
415
+ - `@tagma/sdk/runtime/adapters/stdin-approval`
416
+ - `@tagma/sdk/runtime/adapters/websocket-approval`
417
+ - the older `@tagma/sdk/adapters/*` subpaths remain thin compatibility re-exports
418
+
419
+ ### Split packages
420
+
421
+ - `@tagma/core` -- runtime-independent orchestration, registry, approval, logging, event/result types, and the `TagmaRuntime` interface
422
+ - `@tagma/runtime-bun` -- Bun process execution, file watching, log storage, `bunRuntime()`, and runtime approval adapters
423
+ - `@tagma/sdk` -- convenience package that composes core + Bun runtime + built-in plugins
424
+
411
425
  ### `bootstrapBuiltins(registry)`
412
426
 
413
427
  Registers all built-in plugins (opencode driver, file/manual triggers, completion checks, static-context middleware).
@@ -425,7 +439,7 @@ Options:
425
439
  - `registry` -- use an existing `PluginRegistry` instance
426
440
  - `builtins` -- register built-in plugins into the instance registry; defaults to `true`
427
441
  - `plugins` -- register package-level `TagmaPlugin` capability objects into the instance registry
428
- - `runtime` -- override command/driver process execution; defaults to `bunRuntime()`
442
+ - `runtime` -- override process execution, file watching, file existence checks, log storage, time, and sleep; defaults to `bunRuntime()`
429
443
 
430
444
  ### `createTagma().run(config, options): Promise<EngineResult>`
431
445
 
@@ -446,7 +460,7 @@ Options:
446
460
  - `maxLogRuns` -- number of per-run log directories to keep under `<workDir>/.tagma/logs/` (default: 20)
447
461
  - `skipPluginLoading` -- skip the engine's built-in `loadPlugins(config.plugins)` call. Set this when the host has already pre-loaded plugins from a custom resolution path (e.g. the editor loading from the user's workspace `node_modules`) so the engine doesn't re-resolve them via Node's default cwd-based import.
448
462
 
449
- > **stdout / stderr persistence.** The engine streams every task's stdout and stderr to disk under `<workDir>/.tagma/logs/<runId>/<taskId>.stdout` and `.stderr`. The `TaskResult.stdout` / `stderr` strings are bounded tails (8 MB / 4 MB by default) — long outputs are truncated from the head with a marker, and consumers that need the full bytes should read `TaskResult.stdoutPath` / `stderrPath`. Use `TaskResult.stdoutBytes` / `stderrBytes` to display "32 MB (truncated)" without re-stat'ing the file.
463
+ > **stdout / stderr persistence.** With the default Bun runtime, the engine streams every task's stdout and stderr to disk under `<workDir>/.tagma/logs/<runId>/<taskId>.stdout` and `.stderr`. Custom runtimes can relocate those artifacts by implementing `runtime.logStore.taskOutputPath()`. The `TaskResult.stdout` / `stderr` strings are bounded tails (8 MB / 4 MB by default) — long outputs are truncated from the head with a marker, and consumers that need the full bytes should read `TaskResult.stdoutPath` / `stderrPath`. Use `TaskResult.stdoutBytes` / `stderrBytes` to display "32 MB (truncated)" without re-stat'ing the file.
450
464
 
451
465
  ### `PipelineRunner`
452
466
 
@@ -534,6 +548,8 @@ export const HttpTrigger: TriggerPlugin = {
534
548
  },
535
549
  },
536
550
  async watch(config, ctx) {
551
+ // Trigger plugins should use ctx.runtime for file IO, watching, and
552
+ // timing so they can run under non-Bun test/runtime implementations.
537
553
  /* ... */
538
554
  },
539
555
  };
@@ -734,7 +750,9 @@ Use `buildDag` instead when you have a fully resolved `PipelineConfig` and need
734
750
  Dual-channel logger — console + file. Creates a per-run log file at `<workDir>/.tagma/logs/<runId>/pipeline.log`.
735
751
 
736
752
  ```ts
737
- const logger = new Logger(workDir, runId);
753
+ import { bunRuntime } from '@tagma/sdk';
754
+
755
+ const logger = new Logger(workDir, runId, bunRuntime().logStore);
738
756
  logger.info('[track]', 'message'); // console + file
739
757
  logger.warn('[track]', 'message'); // console + file
740
758
  logger.error('[track]', 'message'); // console + file
@@ -746,13 +764,14 @@ logger.dir; // run artifact directory
746
764
  logger.close(); // close the persistent file handle (called automatically when Tagma.run completes)
747
765
  ```
748
766
 
749
- Pass an optional third argument to stream every appended line out as a
767
+ Pass an optional fourth argument to stream every appended line out as a
750
768
  structured `LogRecord`; the engine uses this to emit `task_log` events:
751
769
 
752
770
  ```ts
771
+ import { bunRuntime } from '@tagma/sdk';
753
772
  import { Logger, type LogRecord } from '@tagma/sdk/logger';
754
773
 
755
- const logger = new Logger(workDir, runId, (record: LogRecord) => {
774
+ const logger = new Logger(workDir, runId, bunRuntime().logStore, (record: LogRecord) => {
756
775
  // record = { level, taskId, timestamp, text }
757
776
  // level = 'info' | 'warn' | 'error' | 'debug' | 'section' | 'quiet'
758
777
  // taskId is extracted from a '[task:<id>]' prefix, or null for untagged lines
@@ -791,6 +810,8 @@ Truncates `text` to at most `maxBytes` UTF-8 bytes (default 16 KB), appending a
791
810
  | Package | Description |
792
811
  | ---------------------------------------------------------------------------------------- | --------------------------------------------- |
793
812
  | [@tagma/types](https://www.npmjs.com/package/@tagma/types) | Shared TypeScript types |
813
+ | [@tagma/core](https://www.npmjs.com/package/@tagma/core) | Runtime-independent orchestration core |
814
+ | [@tagma/runtime-bun](https://www.npmjs.com/package/@tagma/runtime-bun) | Bun runtime implementation |
794
815
  | [@tagma/driver-codex](https://www.npmjs.com/package/@tagma/driver-codex) | Codex CLI driver plugin |
795
816
  | [@tagma/driver-claude-code](https://www.npmjs.com/package/@tagma/driver-claude-code) | Claude Code CLI driver plugin |
796
817
  | [@tagma/middleware-lightrag](https://www.npmjs.com/package/@tagma/middleware-lightrag) | LightRAG knowledge-graph retrieval middleware |
@@ -1,6 +1,2 @@
1
- import type { ApprovalGateway } from '../approval';
2
- export interface StdinApprovalAdapter {
3
- readonly detach: () => void;
4
- }
5
- export declare function attachStdinApprovalAdapter(gateway: ApprovalGateway): StdinApprovalAdapter;
1
+ export * from '../runtime/adapters/stdin-approval';
6
2
  //# sourceMappingURL=stdin-approval.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stdin-approval.d.ts","sourceRoot":"","sources":["../../src/adapters/stdin-approval.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,aAAa,CAAC;AAQpE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,eAAe,GAAG,oBAAoB,CA4FzF"}
1
+ {"version":3,"file":"stdin-approval.d.ts","sourceRoot":"","sources":["../../src/adapters/stdin-approval.ts"],"names":[],"mappings":"AAAA,cAAc,oCAAoC,CAAC"}
@@ -1,90 +1,2 @@
1
- import * as readline from 'readline';
2
- export function attachStdinApprovalAdapter(gateway) {
3
- const queue = [];
4
- let processing = false;
5
- let rl = null;
6
- function ensureReadline() {
7
- if (!rl) {
8
- rl = readline.createInterface({ input: process.stdin, terminal: false });
9
- }
10
- return rl;
11
- }
12
- function readOneLine() {
13
- return new Promise((resolvePromise) => {
14
- const reader = ensureReadline();
15
- const handler = (line) => {
16
- reader.off('line', handler);
17
- resolvePromise(line);
18
- };
19
- reader.on('line', handler);
20
- });
21
- }
22
- async function processNext() {
23
- if (processing)
24
- return;
25
- processing = true;
26
- try {
27
- while (queue.length > 0) {
28
- const req = queue.shift();
29
- // If the request was already resolved by another path while queued, skip it.
30
- if (!gateway.pending().some((p) => p.id === req.id))
31
- continue;
32
- process.stdout.write(`\n[APPROVAL REQUIRED] ${req.message}\n` +
33
- ` id: ${req.id}\n` +
34
- ` task: ${req.taskId}${req.trackId ? ` (track: ${req.trackId})` : ''}\n` +
35
- ` approve / reject > `);
36
- const input = (await readOneLine()).trim().toLowerCase();
37
- const approveAliases = new Set(['approve', 'yes', 'y', 'ok', 'true', '1']);
38
- const rejectAliases = new Set(['reject', 'no', 'n', 'deny', 'false', '0']);
39
- if (approveAliases.has(input)) {
40
- gateway.resolve(req.id, { outcome: 'approved', actor: 'cli' });
41
- }
42
- else if (rejectAliases.has(input)) {
43
- gateway.resolve(req.id, {
44
- outcome: 'rejected',
45
- actor: 'cli',
46
- reason: 'user rejected via CLI',
47
- });
48
- }
49
- else {
50
- process.stdout.write(` unrecognized input "${input}" — treating as rejection\n`);
51
- gateway.resolve(req.id, {
52
- outcome: 'rejected',
53
- actor: 'cli',
54
- reason: `unrecognized CLI input: ${input}`,
55
- });
56
- }
57
- }
58
- }
59
- finally {
60
- processing = false;
61
- }
62
- }
63
- const unsubscribe = gateway.subscribe((event) => {
64
- switch (event.type) {
65
- case 'requested':
66
- queue.push(event.request);
67
- void processNext();
68
- return;
69
- case 'resolved':
70
- case 'expired':
71
- case 'aborted': {
72
- // Drop from queue if it's still waiting its turn.
73
- const idx = queue.findIndex((r) => r.id === event.request.id);
74
- if (idx >= 0)
75
- queue.splice(idx, 1);
76
- return;
77
- }
78
- }
79
- });
80
- return {
81
- detach: () => {
82
- unsubscribe();
83
- if (rl) {
84
- rl.close();
85
- rl = null;
86
- }
87
- },
88
- };
89
- }
1
+ export * from '../runtime/adapters/stdin-approval';
90
2
  //# sourceMappingURL=stdin-approval.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stdin-approval.js","sourceRoot":"","sources":["../../src/adapters/stdin-approval.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stdin-approval.js","sourceRoot":"","sources":["../../src/adapters/stdin-approval.ts"],"names":[],"mappings":"AAAA,cAAc,oCAAoC,CAAC"}
@@ -1,28 +1,2 @@
1
- import type { ApprovalGateway } from '../approval';
2
- export interface WebSocketApprovalAdapterOptions {
3
- port?: number;
4
- hostname?: string;
5
- /**
6
- * M11: shared secret required from the client during the WebSocket
7
- * upgrade. The token can be supplied either as the `?token=` query
8
- * parameter or in the `x-tagma-token` request header. When set, any
9
- * upgrade request that fails the check is rejected with HTTP 401 and
10
- * never reaches the WebSocket layer (so a misconfigured client cannot
11
- * exhaust rate-limit slots either). Leave undefined for backward
12
- * compatibility with localhost-only deployments.
13
- */
14
- token?: string;
15
- /**
16
- * M11: opt-out of origin checking. Defaults to false, meaning Origin
17
- * headers are restricted to loopback hosts (localhost / 127.0.0.1 / ::1).
18
- * Requests without an Origin header are still allowed so non-browser local
19
- * clients can connect. Set true only for trusted reverse-proxy setups.
20
- */
21
- allowAnyOrigin?: boolean;
22
- }
23
- export interface WebSocketApprovalAdapter {
24
- readonly port: number;
25
- readonly detach: () => void;
26
- }
27
- export declare function attachWebSocketApprovalAdapter(gateway: ApprovalGateway, options?: WebSocketApprovalAdapterOptions): WebSocketApprovalAdapter;
1
+ export * from '../runtime/adapters/websocket-approval';
28
2
  //# sourceMappingURL=websocket-approval.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"websocket-approval.d.ts","sourceRoot":"","sources":["../../src/adapters/websocket-approval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,aAAa,CAAC;AAoBlE,MAAM,WAAW,+BAA+B;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;CAC7B;AAQD,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,eAAe,EACxB,OAAO,GAAE,+BAAoC,GAC5C,wBAAwB,CAmJ1B"}
1
+ {"version":3,"file":"websocket-approval.d.ts","sourceRoot":"","sources":["../../src/adapters/websocket-approval.ts"],"names":[],"mappings":"AAAA,cAAc,wCAAwC,CAAC"}
@@ -1,147 +1,2 @@
1
- // Maximum allowed message payload (bytes) to prevent DoS via oversized messages.
2
- const MAX_PAYLOAD_BYTES = 4_096;
3
- // Per-client rate limit: at most this many messages per window.
4
- const RATE_LIMIT_MAX = 10;
5
- const RATE_LIMIT_WINDOW_MS = 1_000;
6
- export function attachWebSocketApprovalAdapter(gateway, options = {}) {
7
- const port = options.port ?? 3000;
8
- const hostname = options.hostname ?? 'localhost';
9
- const requiredToken = options.token ?? null;
10
- const enforceOriginCheck = options.allowAnyOrigin !== true;
11
- function isLoopbackOrigin(origin) {
12
- try {
13
- const host = new URL(origin).hostname.toLowerCase();
14
- return host === 'localhost' || host === '127.0.0.1' || host === '::1' || host === '[::1]';
15
- }
16
- catch {
17
- return false;
18
- }
19
- }
20
- const clients = new Set();
21
- const clientRates = new Map();
22
- function broadcast(msg) {
23
- const text = JSON.stringify(msg);
24
- for (const ws of clients) {
25
- ws.send(text);
26
- }
27
- }
28
- const unsubscribe = gateway.subscribe((event) => {
29
- switch (event.type) {
30
- case 'requested':
31
- broadcast({ type: 'approval_requested', request: event.request });
32
- break;
33
- case 'resolved':
34
- broadcast({ type: 'approval_resolved', request: event.request, decision: event.decision });
35
- break;
36
- case 'expired':
37
- broadcast({ type: 'approval_expired', request: event.request });
38
- break;
39
- case 'aborted':
40
- broadcast({ type: 'approval_aborted', request: event.request, reason: event.reason });
41
- break;
42
- }
43
- });
44
- const server = Bun.serve({
45
- port,
46
- hostname,
47
- fetch(req, server) {
48
- if (enforceOriginCheck) {
49
- const origin = req.headers.get('origin');
50
- if (origin && !isLoopbackOrigin(origin)) {
51
- return new Response('forbidden origin', { status: 403 });
52
- }
53
- }
54
- // M11: enforce token before any upgrade so an unauthenticated client
55
- // can't even open a socket. Tokens may arrive via header or query.
56
- if (requiredToken !== null) {
57
- const headerToken = req.headers.get('x-tagma-token') ?? '';
58
- let queryToken = '';
59
- try {
60
- queryToken = new URL(req.url).searchParams.get('token') ?? '';
61
- }
62
- catch {
63
- /* malformed URL — leave queryToken empty */
64
- }
65
- const presented = headerToken || queryToken;
66
- if (presented !== requiredToken) {
67
- return new Response('unauthorized', { status: 401 });
68
- }
69
- }
70
- if (server.upgrade(req))
71
- return undefined;
72
- return new Response('tagma-sdk WebSocket approval endpoint', { status: 426 });
73
- },
74
- websocket: {
75
- open(ws) {
76
- clients.add(ws);
77
- // Sync current pending approvals to newly connected client.
78
- ws.send(JSON.stringify({ type: 'pending', requests: gateway.pending() }));
79
- },
80
- message(ws, raw) {
81
- const rawStr = typeof raw === 'string' ? raw : raw.toString();
82
- // Payload size guard — reject oversized messages before parsing.
83
- if (rawStr.length > MAX_PAYLOAD_BYTES) {
84
- ws.send(JSON.stringify({ type: 'error', message: 'message too large' }));
85
- return;
86
- }
87
- // Per-client rate limit.
88
- const now = Date.now();
89
- const rate = clientRates.get(ws) ?? { count: 0, resetAt: now + RATE_LIMIT_WINDOW_MS };
90
- if (now >= rate.resetAt) {
91
- rate.count = 0;
92
- rate.resetAt = now + RATE_LIMIT_WINDOW_MS;
93
- }
94
- rate.count++;
95
- clientRates.set(ws, rate);
96
- if (rate.count > RATE_LIMIT_MAX) {
97
- ws.send(JSON.stringify({ type: 'error', message: 'rate limit exceeded' }));
98
- return;
99
- }
100
- let msg;
101
- try {
102
- msg = JSON.parse(rawStr);
103
- }
104
- catch {
105
- ws.send(JSON.stringify({ type: 'error', message: 'invalid JSON' }));
106
- return;
107
- }
108
- if (!isResolveMessage(msg)) {
109
- ws.send(JSON.stringify({ type: 'error', message: 'unknown message type' }));
110
- return;
111
- }
112
- const ok = gateway.resolve(msg.approvalId, {
113
- outcome: msg.outcome,
114
- actor: msg.actor ?? 'websocket',
115
- reason: msg.reason,
116
- });
117
- if (!ok) {
118
- ws.send(JSON.stringify({
119
- type: 'error',
120
- message: `approval ${msg.approvalId} not found or already resolved`,
121
- }));
122
- }
123
- },
124
- close(ws) {
125
- clients.delete(ws);
126
- clientRates.delete(ws);
127
- },
128
- },
129
- });
130
- return {
131
- port: server.port,
132
- detach() {
133
- unsubscribe();
134
- clients.clear();
135
- server.stop(true);
136
- },
137
- };
138
- }
139
- function isResolveMessage(v) {
140
- if (typeof v !== 'object' || v === null)
141
- return false;
142
- const m = v;
143
- return (m['type'] === 'resolve' &&
144
- typeof m['approvalId'] === 'string' &&
145
- (m['outcome'] === 'approved' || m['outcome'] === 'rejected'));
146
- }
1
+ export * from '../runtime/adapters/websocket-approval';
147
2
  //# sourceMappingURL=websocket-approval.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"websocket-approval.js","sourceRoot":"","sources":["../../src/adapters/websocket-approval.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"websocket-approval.js","sourceRoot":"","sources":["../../src/adapters/websocket-approval.ts"],"names":[],"mappings":"AAAA,cAAc,wCAAwC,CAAC"}
@@ -1,13 +1,3 @@
1
- import type { ApprovalRequest, ApprovalDecision, ApprovalListener, ApprovalGateway } from './types';
2
- export type { ApprovalRequest, ApprovalDecision, ApprovalOutcome, ApprovalEvent, ApprovalListener, ApprovalGateway, } from './types';
3
- export declare class InMemoryApprovalGateway implements ApprovalGateway {
4
- private readonly pendingMap;
5
- private readonly listeners;
6
- request(req: Omit<ApprovalRequest, 'id' | 'createdAt'>): Promise<ApprovalDecision>;
7
- resolve(approvalId: string, decision: Omit<ApprovalDecision, 'approvalId' | 'decidedAt'>): boolean;
8
- pending(): readonly ApprovalRequest[];
9
- subscribe(listener: ApprovalListener): () => void;
10
- abortAll(reason: string): void;
11
- private emit;
12
- }
1
+ export { InMemoryApprovalGateway } from '@tagma/core';
2
+ export type { ApprovalDecision, ApprovalEvent, ApprovalGateway, ApprovalListener, ApprovalOutcome, ApprovalRequest, } from '@tagma/core';
13
3
  //# sourceMappingURL=approval.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAEhB,gBAAgB,EAChB,eAAe,EAChB,MAAM,SAAS,CAAC;AAGjB,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,eAAe,GAChB,MAAM,SAAS,CAAC;AAUjB,qBAAa,uBAAwB,YAAW,eAAe;IAC7D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA+B;IAEzD,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,WAAW,CAAC,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAkClF,OAAO,CACL,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,WAAW,CAAC,GAC3D,OAAO;IAkBV,OAAO,IAAI,SAAS,eAAe,EAAE;IAIrC,SAAS,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,IAAI;IAOjD,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAe9B,OAAO,CAAC,IAAI;CASb"}
1
+ {"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,aAAa,CAAC"}
package/dist/approval.js CHANGED
@@ -1,91 +1,2 @@
1
- import { randomUUID } from 'crypto';
2
- import { nowISO } from './utils';
3
- export class InMemoryApprovalGateway {
4
- pendingMap = new Map();
5
- listeners = new Set();
6
- request(req) {
7
- const full = {
8
- id: randomUUID(),
9
- createdAt: nowISO(),
10
- taskId: req.taskId,
11
- trackId: req.trackId,
12
- message: req.message,
13
- timeoutMs: req.timeoutMs,
14
- metadata: req.metadata,
15
- };
16
- return new Promise((resolvePromise) => {
17
- let timer = null;
18
- if (full.timeoutMs > 0) {
19
- timer = setTimeout(() => {
20
- const entry = this.pendingMap.get(full.id);
21
- if (!entry)
22
- return;
23
- this.pendingMap.delete(full.id);
24
- const decision = {
25
- approvalId: full.id,
26
- outcome: 'timeout',
27
- reason: `Approval timed out after ${full.timeoutMs}ms`,
28
- decidedAt: nowISO(),
29
- };
30
- this.emit({ type: 'expired', request: full });
31
- resolvePromise(decision);
32
- }, full.timeoutMs);
33
- }
34
- this.pendingMap.set(full.id, { request: full, settle: resolvePromise, timer });
35
- this.emit({ type: 'requested', request: full });
36
- });
37
- }
38
- resolve(approvalId, decision) {
39
- const entry = this.pendingMap.get(approvalId);
40
- if (!entry)
41
- return false;
42
- this.pendingMap.delete(approvalId);
43
- if (entry.timer)
44
- clearTimeout(entry.timer);
45
- const full = {
46
- approvalId,
47
- outcome: decision.outcome,
48
- actor: decision.actor,
49
- reason: decision.reason,
50
- decidedAt: nowISO(),
51
- };
52
- this.emit({ type: 'resolved', request: entry.request, decision: full });
53
- entry.settle(full);
54
- return true;
55
- }
56
- pending() {
57
- return Array.from(this.pendingMap.values()).map((e) => e.request);
58
- }
59
- subscribe(listener) {
60
- this.listeners.add(listener);
61
- return () => {
62
- this.listeners.delete(listener);
63
- };
64
- }
65
- abortAll(reason) {
66
- const entries = Array.from(this.pendingMap.entries());
67
- this.pendingMap.clear();
68
- for (const [id, entry] of entries) {
69
- if (entry.timer)
70
- clearTimeout(entry.timer);
71
- this.emit({ type: 'aborted', request: entry.request, reason });
72
- entry.settle({
73
- approvalId: id,
74
- outcome: 'aborted',
75
- reason,
76
- decidedAt: nowISO(),
77
- });
78
- }
79
- }
80
- emit(event) {
81
- for (const listener of this.listeners) {
82
- try {
83
- listener(event);
84
- }
85
- catch (err) {
86
- console.error('[approval gateway] listener error:', err);
87
- }
88
- }
89
- }
90
- }
1
+ export { InMemoryApprovalGateway } from '@tagma/core';
91
2
  //# sourceMappingURL=approval.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"approval.js","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"approval.js","sourceRoot":"","sources":["../src/approval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { PluginRegistry } from './registry';
1
+ import type { PluginRegistry } from '@tagma/core';
2
2
  export declare const BuiltinTagmaPlugin: {
3
3
  name: string;
4
4
  capabilities: {
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAsBjD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;CAmBR,CAAC;AAExB;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAE9D"}
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAsBlD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;CAmBR,CAAC;AAExB;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAE9D"}
@@ -1 +1 @@
1
- {"version":3,"file":"task-executor.d.ts","sourceRoot":"","sources":["../../src/core/task-executor.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAWlD,OAAO,EAAmB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAoBhD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;CAC3C;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgrB5E"}
1
+ {"version":3,"file":"task-executor.d.ts","sourceRoot":"","sources":["../../src/core/task-executor.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAWlD,OAAO,EAAmB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAoBhD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;CAC3C;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0rB5E"}
@@ -1,4 +1,3 @@
1
- import { resolve } from 'path';
2
1
  import { parseDuration, nowISO } from '../utils';
3
2
  import { promptDocumentFromString, serializePromptDocument, prependContext, renderInputsBlock, renderOutputSchemaBlock, } from '../prompt-doc';
4
3
  import { resolveTaskBindingInputs, resolveTaskInputs, substituteInputs } from '../ports';
@@ -88,6 +87,7 @@ export async function executeTask(options) {
88
87
  workDir: task.cwd ?? workDir,
89
88
  signal: ctx.abortController.signal,
90
89
  approvalGateway,
90
+ runtime: ctx.runtime,
91
91
  })
92
92
  .then((v) => {
93
93
  if (settled)
@@ -298,9 +298,18 @@ export async function executeTask(options) {
298
298
  // and keep only a bounded tail in the returned TaskResult. Filenames
299
299
  // mirror the existing `.stderr` naming — dots in task ids are replaced
300
300
  // so hierarchical ids (e.g. `track1.task2`) map cleanly to a flat dir.
301
- const fsSafeTaskId = taskId.replace(/\./g, '_');
302
- const stdoutPath = resolve(log.dir, `${fsSafeTaskId}.stdout`);
303
- const stderrPath = resolve(log.dir, `${fsSafeTaskId}.stderr`);
301
+ const stdoutPath = ctx.runtime.logStore.taskOutputPath({
302
+ workDir,
303
+ runId: ctx.runId,
304
+ taskId,
305
+ stream: 'stdout',
306
+ });
307
+ const stderrPath = ctx.runtime.logStore.taskOutputPath({
308
+ workDir,
309
+ runId: ctx.runId,
310
+ taskId,
311
+ stream: 'stderr',
312
+ });
304
313
  const runOpts = {
305
314
  timeoutMs,
306
315
  signal: ctx.abortController.signal,