@autonome-research/thread-phase 3.0.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.
- package/LICENSE +21 -0
- package/README.md +226 -0
- package/dist/agent/index.d.ts +28 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +28 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/openai-adapter.d.ts +15 -0
- package/dist/agent/openai-adapter.d.ts.map +1 -0
- package/dist/agent/openai-adapter.js +57 -0
- package/dist/agent/openai-adapter.js.map +1 -0
- package/dist/agent/parse-json.d.ts +12 -0
- package/dist/agent/parse-json.d.ts.map +1 -0
- package/dist/agent/parse-json.js +31 -0
- package/dist/agent/parse-json.js.map +1 -0
- package/dist/agent/retry.d.ts +15 -0
- package/dist/agent/retry.d.ts.map +1 -0
- package/dist/agent/retry.js +35 -0
- package/dist/agent/retry.js.map +1 -0
- package/dist/agent/runner.d.ts +25 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +270 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/agent/stream-consumer.d.ts +57 -0
- package/dist/agent/stream-consumer.d.ts.map +1 -0
- package/dist/agent/stream-consumer.js +126 -0
- package/dist/agent/stream-consumer.js.map +1 -0
- package/dist/agent/types.d.ts +135 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +9 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/agent-runner.d.ts +10 -0
- package/dist/agent-runner.d.ts.map +1 -0
- package/dist/agent-runner.js +10 -0
- package/dist/agent-runner.js.map +1 -0
- package/dist/agents/capability.d.ts +36 -0
- package/dist/agents/capability.d.ts.map +1 -0
- package/dist/agents/capability.js +51 -0
- package/dist/agents/capability.js.map +1 -0
- package/dist/agents/event-bus.d.ts +20 -0
- package/dist/agents/event-bus.d.ts.map +1 -0
- package/dist/agents/event-bus.js +40 -0
- package/dist/agents/event-bus.js.map +1 -0
- package/dist/agents/index.d.ts +23 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +33 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/inference-adapter.d.ts +52 -0
- package/dist/agents/inference-adapter.d.ts.map +1 -0
- package/dist/agents/inference-adapter.js +209 -0
- package/dist/agents/inference-adapter.js.map +1 -0
- package/dist/agents/job-store-bridge.d.ts +44 -0
- package/dist/agents/job-store-bridge.d.ts.map +1 -0
- package/dist/agents/job-store-bridge.js +58 -0
- package/dist/agents/job-store-bridge.js.map +1 -0
- package/dist/agents/memory.d.ts +40 -0
- package/dist/agents/memory.d.ts.map +1 -0
- package/dist/agents/memory.js +14 -0
- package/dist/agents/memory.js.map +1 -0
- package/dist/agents/protocol.d.ts +302 -0
- package/dist/agents/protocol.d.ts.map +1 -0
- package/dist/agents/protocol.js +36 -0
- package/dist/agents/protocol.js.map +1 -0
- package/dist/agents/run-helpers.d.ts +70 -0
- package/dist/agents/run-helpers.d.ts.map +1 -0
- package/dist/agents/run-helpers.js +131 -0
- package/dist/agents/run-helpers.js.map +1 -0
- package/dist/agents/serialize-error.d.ts +18 -0
- package/dist/agents/serialize-error.d.ts.map +1 -0
- package/dist/agents/serialize-error.js +27 -0
- package/dist/agents/serialize-error.js.map +1 -0
- package/dist/agents/structured-output.d.ts +90 -0
- package/dist/agents/structured-output.d.ts.map +1 -0
- package/dist/agents/structured-output.js +101 -0
- package/dist/agents/structured-output.js.map +1 -0
- package/dist/agents/test-utils/conformance.d.ts +59 -0
- package/dist/agents/test-utils/conformance.d.ts.map +1 -0
- package/dist/agents/test-utils/conformance.js +207 -0
- package/dist/agents/test-utils/conformance.js.map +1 -0
- package/dist/agents/test-utils/index.d.ts +12 -0
- package/dist/agents/test-utils/index.d.ts.map +1 -0
- package/dist/agents/test-utils/index.js +12 -0
- package/dist/agents/test-utils/index.js.map +1 -0
- package/dist/agents/test-utils/mock-agent.d.ts +66 -0
- package/dist/agents/test-utils/mock-agent.d.ts.map +1 -0
- package/dist/agents/test-utils/mock-agent.js +244 -0
- package/dist/agents/test-utils/mock-agent.js.map +1 -0
- package/dist/agents/thread.d.ts +57 -0
- package/dist/agents/thread.d.ts.map +1 -0
- package/dist/agents/thread.js +128 -0
- package/dist/agents/thread.js.map +1 -0
- package/dist/agents/turn-accumulator.d.ts +94 -0
- package/dist/agents/turn-accumulator.d.ts.map +1 -0
- package/dist/agents/turn-accumulator.js +150 -0
- package/dist/agents/turn-accumulator.js.map +1 -0
- package/dist/agents/with-memory.d.ts +55 -0
- package/dist/agents/with-memory.d.ts.map +1 -0
- package/dist/agents/with-memory.js +155 -0
- package/dist/agents/with-memory.js.map +1 -0
- package/dist/agents/with-thread.d.ts +45 -0
- package/dist/agents/with-thread.d.ts.map +1 -0
- package/dist/agents/with-thread.js +70 -0
- package/dist/agents/with-thread.js.map +1 -0
- package/dist/cache.d.ts +47 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +81 -0
- package/dist/cache.js.map +1 -0
- package/dist/context/compressor.d.ts +36 -0
- package/dist/context/compressor.d.ts.map +1 -0
- package/dist/context/compressor.js +158 -0
- package/dist/context/compressor.js.map +1 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +4 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/result-capper.d.ts +32 -0
- package/dist/context/result-capper.d.ts.map +1 -0
- package/dist/context/result-capper.js +50 -0
- package/dist/context/result-capper.js.map +1 -0
- package/dist/context/token-budget.d.ts +81 -0
- package/dist/context/token-budget.d.ts.map +1 -0
- package/dist/context/token-budget.js +99 -0
- package/dist/context/token-budget.js.map +1 -0
- package/dist/helpers/caller.d.ts +18 -0
- package/dist/helpers/caller.d.ts.map +1 -0
- package/dist/helpers/caller.js +40 -0
- package/dist/helpers/caller.js.map +1 -0
- package/dist/helpers/hook.d.ts +73 -0
- package/dist/helpers/hook.d.ts.map +1 -0
- package/dist/helpers/hook.js +244 -0
- package/dist/helpers/hook.js.map +1 -0
- package/dist/helpers/index.d.ts +12 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +11 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/one-shot.d.ts +27 -0
- package/dist/helpers/one-shot.d.ts.map +1 -0
- package/dist/helpers/one-shot.js +43 -0
- package/dist/helpers/one-shot.js.map +1 -0
- package/dist/helpers/schedule.d.ts +59 -0
- package/dist/helpers/schedule.d.ts.map +1 -0
- package/dist/helpers/schedule.js +118 -0
- package/dist/helpers/schedule.js.map +1 -0
- package/dist/helpers/types.d.ts +34 -0
- package/dist/helpers/types.d.ts.map +1 -0
- package/dist/helpers/types.js +11 -0
- package/dist/helpers/types.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/inference.d.ts +27 -0
- package/dist/inference.d.ts.map +1 -0
- package/dist/inference.js +34 -0
- package/dist/inference.js.map +1 -0
- package/dist/messages.d.ts +64 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +17 -0
- package/dist/messages.js.map +1 -0
- package/dist/orchestrator.d.ts +56 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +62 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/patterns/bounded-fanout-of.d.ts +61 -0
- package/dist/patterns/bounded-fanout-of.d.ts.map +1 -0
- package/dist/patterns/bounded-fanout-of.js +142 -0
- package/dist/patterns/bounded-fanout-of.js.map +1 -0
- package/dist/patterns/bounded-fanout.d.ts +111 -0
- package/dist/patterns/bounded-fanout.d.ts.map +1 -0
- package/dist/patterns/bounded-fanout.js +151 -0
- package/dist/patterns/bounded-fanout.js.map +1 -0
- package/dist/patterns/index.d.ts +14 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +13 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/patterns/intent-gate.d.ts +27 -0
- package/dist/patterns/intent-gate.d.ts.map +1 -0
- package/dist/patterns/intent-gate.js +32 -0
- package/dist/patterns/intent-gate.js.map +1 -0
- package/dist/patterns/match.d.ts +30 -0
- package/dist/patterns/match.d.ts.map +1 -0
- package/dist/patterns/match.js +58 -0
- package/dist/patterns/match.js.map +1 -0
- package/dist/patterns/parallel-fanout.d.ts +28 -0
- package/dist/patterns/parallel-fanout.d.ts.map +1 -0
- package/dist/patterns/parallel-fanout.js +24 -0
- package/dist/patterns/parallel-fanout.js.map +1 -0
- package/dist/patterns/parallel-phases.d.ts +27 -0
- package/dist/patterns/parallel-phases.d.ts.map +1 -0
- package/dist/patterns/parallel-phases.js +77 -0
- package/dist/patterns/parallel-phases.js.map +1 -0
- package/dist/patterns/preflight-confidence.d.ts +20 -0
- package/dist/patterns/preflight-confidence.d.ts.map +1 -0
- package/dist/patterns/preflight-confidence.js +38 -0
- package/dist/patterns/preflight-confidence.js.map +1 -0
- package/dist/patterns/spot-check.d.ts +19 -0
- package/dist/patterns/spot-check.d.ts.map +1 -0
- package/dist/patterns/spot-check.js +33 -0
- package/dist/patterns/spot-check.js.map +1 -0
- package/dist/patterns/sub-pipeline.d.ts +84 -0
- package/dist/patterns/sub-pipeline.d.ts.map +1 -0
- package/dist/patterns/sub-pipeline.js +90 -0
- package/dist/patterns/sub-pipeline.js.map +1 -0
- package/dist/patterns/synthesize-with-followup.d.ts +35 -0
- package/dist/patterns/synthesize-with-followup.d.ts.map +1 -0
- package/dist/patterns/synthesize-with-followup.js +45 -0
- package/dist/patterns/synthesize-with-followup.js.map +1 -0
- package/dist/patterns/while-condition.d.ts +31 -0
- package/dist/patterns/while-condition.d.ts.map +1 -0
- package/dist/patterns/while-condition.js +59 -0
- package/dist/patterns/while-condition.js.map +1 -0
- package/dist/patterns/with-retry.d.ts +37 -0
- package/dist/patterns/with-retry.d.ts.map +1 -0
- package/dist/patterns/with-retry.js +73 -0
- package/dist/patterns/with-retry.js.map +1 -0
- package/dist/phase.d.ts +78 -0
- package/dist/phase.d.ts.map +1 -0
- package/dist/phase.js +36 -0
- package/dist/phase.js.map +1 -0
- package/dist/session/index.d.ts +5 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +4 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/job-runner.d.ts +67 -0
- package/dist/session/job-runner.d.ts.map +1 -0
- package/dist/session/job-runner.js +131 -0
- package/dist/session/job-runner.js.map +1 -0
- package/dist/session/job-store.d.ts +98 -0
- package/dist/session/job-store.d.ts.map +1 -0
- package/dist/session/job-store.js +37 -0
- package/dist/session/job-store.js.map +1 -0
- package/dist/session/sqlite-job-store.d.ts +40 -0
- package/dist/session/sqlite-job-store.d.ts.map +1 -0
- package/dist/session/sqlite-job-store.js +200 -0
- package/dist/session/sqlite-job-store.js.map +1 -0
- package/dist/session/sse.d.ts +60 -0
- package/dist/session/sse.d.ts.map +1 -0
- package/dist/session/sse.js +97 -0
- package/dist/session/sse.js.map +1 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/registry.d.ts +44 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +74 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/triggers/index.d.ts +15 -0
- package/dist/triggers/index.d.ts.map +1 -0
- package/dist/triggers/index.js +14 -0
- package/dist/triggers/index.js.map +1 -0
- package/dist/triggers/run-trigger.d.ts +86 -0
- package/dist/triggers/run-trigger.d.ts.map +1 -0
- package/dist/triggers/run-trigger.js +146 -0
- package/dist/triggers/run-trigger.js.map +1 -0
- package/dist/triggers/timer-trigger.d.ts +46 -0
- package/dist/triggers/timer-trigger.d.ts.map +1 -0
- package/dist/triggers/timer-trigger.js +74 -0
- package/dist/triggers/timer-trigger.js.map +1 -0
- package/dist/triggers/types.d.ts +61 -0
- package/dist/triggers/types.d.ts.map +1 -0
- package/dist/triggers/types.js +23 -0
- package/dist/triggers/types.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sqlite-backed JobStore implementation — the bundled default.
|
|
3
|
+
*
|
|
4
|
+
* Two tables (job, event) on a single sqlite file. WAL journal for write
|
|
5
|
+
* concurrency, foreign keys enforced.
|
|
6
|
+
*
|
|
7
|
+
* The interface (`JobStore`) is async-by-default in v3.0.0; this
|
|
8
|
+
* implementation wraps its sync better-sqlite3 calls in `async` methods.
|
|
9
|
+
* better-sqlite3 stays sync internally — the only overhead is one
|
|
10
|
+
* microtask per call, which is negligible against sqlite's already
|
|
11
|
+
* sub-millisecond write cost. We pay this cost to keep one unified
|
|
12
|
+
* interface across all backends (Postgres, Redis, network stores)
|
|
13
|
+
* instead of two parallel sync + async hierarchies.
|
|
14
|
+
*/
|
|
15
|
+
import Database from 'better-sqlite3';
|
|
16
|
+
import { randomUUID } from 'crypto';
|
|
17
|
+
const MIGRATIONS = [
|
|
18
|
+
{
|
|
19
|
+
version: 1,
|
|
20
|
+
up: `
|
|
21
|
+
CREATE TABLE IF NOT EXISTS job (
|
|
22
|
+
id TEXT PRIMARY KEY,
|
|
23
|
+
name TEXT NOT NULL,
|
|
24
|
+
input TEXT NOT NULL,
|
|
25
|
+
status TEXT NOT NULL DEFAULT 'PENDING',
|
|
26
|
+
result TEXT,
|
|
27
|
+
error TEXT,
|
|
28
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
29
|
+
started_at TEXT,
|
|
30
|
+
completed_at TEXT
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_job_name_created
|
|
34
|
+
ON job (name, created_at DESC);
|
|
35
|
+
|
|
36
|
+
CREATE TABLE IF NOT EXISTS event (
|
|
37
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
38
|
+
job_id TEXT NOT NULL,
|
|
39
|
+
event_type TEXT NOT NULL,
|
|
40
|
+
data TEXT NOT NULL,
|
|
41
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
42
|
+
FOREIGN KEY (job_id) REFERENCES job(id)
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
CREATE INDEX IF NOT EXISTS idx_event_job_id ON event (job_id, id);
|
|
46
|
+
`,
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
function defaultDbPath() {
|
|
50
|
+
return process.env.THREAD_PHASE_DB ?? './thread-phase.db';
|
|
51
|
+
}
|
|
52
|
+
function parseDate(s) {
|
|
53
|
+
return s ? new Date(s + 'Z') : null;
|
|
54
|
+
}
|
|
55
|
+
export class SqliteJobStore {
|
|
56
|
+
db;
|
|
57
|
+
constructor(dbPath = defaultDbPath()) {
|
|
58
|
+
this.db = new Database(dbPath);
|
|
59
|
+
this.db.pragma('journal_mode = WAL');
|
|
60
|
+
this.db.pragma('foreign_keys = ON');
|
|
61
|
+
this.runMigrations();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Apply any unapplied migrations. Reads `PRAGMA user_version`, applies
|
|
65
|
+
* each entry from `MIGRATIONS` whose version is greater, inside a
|
|
66
|
+
* transaction per step, then bumps user_version.
|
|
67
|
+
*
|
|
68
|
+
* Idempotent: running twice is a no-op once the schema is current.
|
|
69
|
+
*/
|
|
70
|
+
runMigrations() {
|
|
71
|
+
const current = this.db.pragma('user_version', { simple: true }) ?? 0;
|
|
72
|
+
for (const m of MIGRATIONS) {
|
|
73
|
+
if (m.version <= current)
|
|
74
|
+
continue;
|
|
75
|
+
this.db.transaction(() => {
|
|
76
|
+
this.db.exec(m.up);
|
|
77
|
+
// user_version is an integer pragma; better-sqlite3 doesn't support
|
|
78
|
+
// parameter binding for pragmas, so interpolate the integer directly.
|
|
79
|
+
this.db.pragma(`user_version = ${m.version}`);
|
|
80
|
+
})();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// -------------------------------------------------------------------------
|
|
84
|
+
// Job lifecycle
|
|
85
|
+
// -------------------------------------------------------------------------
|
|
86
|
+
async createJob(name, input) {
|
|
87
|
+
const id = randomUUID();
|
|
88
|
+
this.db
|
|
89
|
+
.prepare(`INSERT INTO job (id, name, input) VALUES (?, ?, ?)`)
|
|
90
|
+
.run(id, name, JSON.stringify(input));
|
|
91
|
+
return id;
|
|
92
|
+
}
|
|
93
|
+
async acquireExclusive(name, input) {
|
|
94
|
+
// better-sqlite3's `db.transaction(fn)` wraps the callback in BEGIN…COMMIT
|
|
95
|
+
// (defaults to deferred, but the runtime upgrades to a write lock the
|
|
96
|
+
// moment we INSERT, which serializes concurrent acquireExclusive calls
|
|
97
|
+
// on the same DB file). The check + insert therefore happens atomically:
|
|
98
|
+
// a second caller racing on the same name will see the row we just
|
|
99
|
+
// inserted (status='RUNNING') and return null.
|
|
100
|
+
const tx = this.db.transaction((n, i) => {
|
|
101
|
+
const existing = this.db
|
|
102
|
+
.prepare(`SELECT id FROM job WHERE name = ? AND status = 'RUNNING' LIMIT 1`)
|
|
103
|
+
.get(n);
|
|
104
|
+
if (existing)
|
|
105
|
+
return null;
|
|
106
|
+
const id = randomUUID();
|
|
107
|
+
this.db
|
|
108
|
+
.prepare(`INSERT INTO job (id, name, input, status, started_at)
|
|
109
|
+
VALUES (?, ?, ?, 'RUNNING', datetime('now'))`)
|
|
110
|
+
.run(id, n, JSON.stringify(i));
|
|
111
|
+
return id;
|
|
112
|
+
});
|
|
113
|
+
return tx(name, input);
|
|
114
|
+
}
|
|
115
|
+
async setRunning(jobId) {
|
|
116
|
+
// COALESCE on started_at: idempotent w.r.t. acquireExclusive, which
|
|
117
|
+
// already sets status='RUNNING' and started_at at claim time.
|
|
118
|
+
this.db
|
|
119
|
+
.prepare(`UPDATE job SET status = 'RUNNING',
|
|
120
|
+
started_at = COALESCE(started_at, datetime('now'))
|
|
121
|
+
WHERE id = ?`)
|
|
122
|
+
.run(jobId);
|
|
123
|
+
}
|
|
124
|
+
async setCompleted(jobId, result) {
|
|
125
|
+
this.db
|
|
126
|
+
.prepare(`UPDATE job SET status = 'COMPLETED', result = ?, completed_at = datetime('now') WHERE id = ?`)
|
|
127
|
+
.run(JSON.stringify(result ?? null), jobId);
|
|
128
|
+
}
|
|
129
|
+
async setFailed(jobId, error) {
|
|
130
|
+
this.db
|
|
131
|
+
.prepare(`UPDATE job SET status = 'FAILED', error = ?, completed_at = datetime('now') WHERE id = ?`)
|
|
132
|
+
.run(error, jobId);
|
|
133
|
+
}
|
|
134
|
+
// -------------------------------------------------------------------------
|
|
135
|
+
// Job reads
|
|
136
|
+
// -------------------------------------------------------------------------
|
|
137
|
+
async getJob(jobId) {
|
|
138
|
+
const row = this.db
|
|
139
|
+
.prepare(`SELECT j.*, (SELECT COUNT(*) FROM event WHERE job_id = j.id) AS event_count
|
|
140
|
+
FROM job j WHERE j.id = ?`)
|
|
141
|
+
.get(jobId);
|
|
142
|
+
return row ? this.toJobRecord(row) : null;
|
|
143
|
+
}
|
|
144
|
+
async listJobs(options = {}) {
|
|
145
|
+
const limit = options.limit ?? 50;
|
|
146
|
+
// Build one parameterized query — `name IS NULL OR j.name = name` lets a
|
|
147
|
+
// null parameter act as a no-filter wildcard, removing the duplicated
|
|
148
|
+
// SELECT/ORDER/LIMIT clauses that were previously copied per branch.
|
|
149
|
+
const sql = `
|
|
150
|
+
SELECT j.*, (SELECT COUNT(*) FROM event WHERE job_id = j.id) AS event_count
|
|
151
|
+
FROM job j
|
|
152
|
+
WHERE (? IS NULL OR j.name = ?)
|
|
153
|
+
ORDER BY j.created_at DESC
|
|
154
|
+
LIMIT ?
|
|
155
|
+
`;
|
|
156
|
+
const name = options.name ?? null;
|
|
157
|
+
const rows = this.db.prepare(sql).all(name, name, limit);
|
|
158
|
+
return rows.map((r) => this.toJobRecord(r));
|
|
159
|
+
}
|
|
160
|
+
toJobRecord(row) {
|
|
161
|
+
return {
|
|
162
|
+
id: row.id,
|
|
163
|
+
name: row.name,
|
|
164
|
+
input: JSON.parse(row.input),
|
|
165
|
+
status: row.status,
|
|
166
|
+
result: row.result ? JSON.parse(row.result) : null,
|
|
167
|
+
error: row.error,
|
|
168
|
+
eventCount: row.event_count,
|
|
169
|
+
createdAt: parseDate(row.created_at),
|
|
170
|
+
startedAt: parseDate(row.started_at),
|
|
171
|
+
completedAt: parseDate(row.completed_at),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
// -------------------------------------------------------------------------
|
|
175
|
+
// Events — append-only log, resumable via afterId
|
|
176
|
+
// -------------------------------------------------------------------------
|
|
177
|
+
async appendEvent(jobId, event) {
|
|
178
|
+
const result = this.db
|
|
179
|
+
.prepare(`INSERT INTO event (job_id, event_type, data) VALUES (?, ?, ?)`)
|
|
180
|
+
.run(jobId, event.type, JSON.stringify(event));
|
|
181
|
+
return Number(result.lastInsertRowid);
|
|
182
|
+
}
|
|
183
|
+
async getEvents(jobId, afterId = 0) {
|
|
184
|
+
const rows = this.db
|
|
185
|
+
.prepare(`SELECT id, job_id, event_type, data, created_at
|
|
186
|
+
FROM event WHERE job_id = ? AND id > ? ORDER BY id ASC`)
|
|
187
|
+
.all(jobId, afterId);
|
|
188
|
+
return rows.map((r) => ({
|
|
189
|
+
id: r.id,
|
|
190
|
+
jobId: r.job_id,
|
|
191
|
+
eventType: r.event_type,
|
|
192
|
+
data: JSON.parse(r.data),
|
|
193
|
+
createdAt: parseDate(r.created_at),
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
close() {
|
|
197
|
+
this.db.close();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=sqlite-job-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-job-store.js","sourceRoot":"","sources":["../../src/session/sqlite-job-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-Sent Events helper — adapt a JobRunner live stream + replay log
|
|
3
|
+
* into an SSE wire format for HTTP consumers.
|
|
4
|
+
*
|
|
5
|
+
* Wire shape:
|
|
6
|
+
* - Each event is `id: <eventId>\nevent: <type>\ndata: <json>\n\n`.
|
|
7
|
+
* - The eventId is the JobStore's monotonic id, so disconnected clients
|
|
8
|
+
* can resume by reading `Last-Event-ID` and replaying via JobStore.
|
|
9
|
+
* - The connection closes after a `done` or `error` event.
|
|
10
|
+
*
|
|
11
|
+
* The helper is framework-agnostic: it writes to a minimal `SSEResponse`
|
|
12
|
+
* interface so it works with Node's `http.ServerResponse`, Express's
|
|
13
|
+
* `Response`, Fastify's reply (after .raw), etc.
|
|
14
|
+
*
|
|
15
|
+
* Typical usage:
|
|
16
|
+
*
|
|
17
|
+
* app.get('/jobs/:id/events', (req, res) => {
|
|
18
|
+
* const lastId = Number(req.headers['last-event-id'] ?? 0);
|
|
19
|
+
* res.writeHead(200, {
|
|
20
|
+
* 'Content-Type': 'text/event-stream',
|
|
21
|
+
* 'Cache-Control': 'no-cache',
|
|
22
|
+
* 'Connection': 'keep-alive',
|
|
23
|
+
* });
|
|
24
|
+
* streamToSSE({ runner, store, jobId: req.params.id, res, afterId: lastId });
|
|
25
|
+
* });
|
|
26
|
+
*/
|
|
27
|
+
import type { JobStore } from './job-store.js';
|
|
28
|
+
import type { JobRunner } from './job-runner.js';
|
|
29
|
+
/**
|
|
30
|
+
* Minimal response interface — `http.ServerResponse` and Express's `Response`
|
|
31
|
+
* both satisfy this without modification.
|
|
32
|
+
*/
|
|
33
|
+
export interface SSEResponse {
|
|
34
|
+
write(chunk: string): boolean;
|
|
35
|
+
end(): void;
|
|
36
|
+
on(event: 'close', listener: () => void): void;
|
|
37
|
+
}
|
|
38
|
+
export interface StreamToSSEOptions {
|
|
39
|
+
runner: JobRunner;
|
|
40
|
+
store: JobStore;
|
|
41
|
+
jobId: string;
|
|
42
|
+
res: SSEResponse;
|
|
43
|
+
/**
|
|
44
|
+
* Replay events with id > afterId before subscribing live. Use the value
|
|
45
|
+
* of the client's `Last-Event-ID` header to resume after disconnect.
|
|
46
|
+
*/
|
|
47
|
+
afterId?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Heartbeat interval in ms. Default 25s. Sends an SSE comment line to
|
|
50
|
+
* keep proxies and intermediaries from closing the connection. Set 0 to
|
|
51
|
+
* disable.
|
|
52
|
+
*/
|
|
53
|
+
heartbeatMs?: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Pump a job's events to an SSE response. Resolves when the connection
|
|
57
|
+
* closes (either because the job ended or the client disconnected).
|
|
58
|
+
*/
|
|
59
|
+
export declare function streamToSSE(options: StreamToSSEOptions): Promise<void>;
|
|
60
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/session/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,iBAAiB,CAAC;AAE5D;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,GAAG,IAAI,IAAI,CAAC;IACZ,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;CAChD;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,WAAW,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqE5E"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-Sent Events helper — adapt a JobRunner live stream + replay log
|
|
3
|
+
* into an SSE wire format for HTTP consumers.
|
|
4
|
+
*
|
|
5
|
+
* Wire shape:
|
|
6
|
+
* - Each event is `id: <eventId>\nevent: <type>\ndata: <json>\n\n`.
|
|
7
|
+
* - The eventId is the JobStore's monotonic id, so disconnected clients
|
|
8
|
+
* can resume by reading `Last-Event-ID` and replaying via JobStore.
|
|
9
|
+
* - The connection closes after a `done` or `error` event.
|
|
10
|
+
*
|
|
11
|
+
* The helper is framework-agnostic: it writes to a minimal `SSEResponse`
|
|
12
|
+
* interface so it works with Node's `http.ServerResponse`, Express's
|
|
13
|
+
* `Response`, Fastify's reply (after .raw), etc.
|
|
14
|
+
*
|
|
15
|
+
* Typical usage:
|
|
16
|
+
*
|
|
17
|
+
* app.get('/jobs/:id/events', (req, res) => {
|
|
18
|
+
* const lastId = Number(req.headers['last-event-id'] ?? 0);
|
|
19
|
+
* res.writeHead(200, {
|
|
20
|
+
* 'Content-Type': 'text/event-stream',
|
|
21
|
+
* 'Cache-Control': 'no-cache',
|
|
22
|
+
* 'Connection': 'keep-alive',
|
|
23
|
+
* });
|
|
24
|
+
* streamToSSE({ runner, store, jobId: req.params.id, res, afterId: lastId });
|
|
25
|
+
* });
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Pump a job's events to an SSE response. Resolves when the connection
|
|
29
|
+
* closes (either because the job ended or the client disconnected).
|
|
30
|
+
*/
|
|
31
|
+
export async function streamToSSE(options) {
|
|
32
|
+
const { runner, store, jobId, res, afterId = 0 } = options;
|
|
33
|
+
const heartbeatMs = options.heartbeatMs ?? 25_000;
|
|
34
|
+
let closed = false;
|
|
35
|
+
const close = () => {
|
|
36
|
+
if (closed)
|
|
37
|
+
return;
|
|
38
|
+
closed = true;
|
|
39
|
+
res.end();
|
|
40
|
+
};
|
|
41
|
+
res.on('close', () => {
|
|
42
|
+
closed = true;
|
|
43
|
+
});
|
|
44
|
+
const writeFrame = (id, type, data) => {
|
|
45
|
+
if (closed)
|
|
46
|
+
return false;
|
|
47
|
+
const payload = `id: ${id}\n` +
|
|
48
|
+
`event: ${type}\n` +
|
|
49
|
+
`data: ${JSON.stringify(data)}\n\n`;
|
|
50
|
+
return res.write(payload);
|
|
51
|
+
};
|
|
52
|
+
// Step 1: replay anything the client has missed.
|
|
53
|
+
let lastId = afterId;
|
|
54
|
+
const replay = await store.getEvents(jobId, afterId);
|
|
55
|
+
for (const evt of replay) {
|
|
56
|
+
writeFrame(evt.id, evt.eventType, evt.data);
|
|
57
|
+
lastId = Math.max(lastId, evt.id);
|
|
58
|
+
}
|
|
59
|
+
// If the job is already finished and replay covered everything, close.
|
|
60
|
+
const job = await store.getJob(jobId);
|
|
61
|
+
if (job && (job.status === 'COMPLETED' || job.status === 'FAILED')) {
|
|
62
|
+
close();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
// Step 2: subscribe to live events. Buffer events that arrived during
|
|
66
|
+
// replay if their id is past lastId (otherwise they're already sent).
|
|
67
|
+
const channel = `job:${jobId}`;
|
|
68
|
+
const onLive = (evt) => {
|
|
69
|
+
if (closed)
|
|
70
|
+
return;
|
|
71
|
+
if (evt.id <= lastId)
|
|
72
|
+
return;
|
|
73
|
+
lastId = evt.id;
|
|
74
|
+
writeFrame(evt.id, evt.eventType, evt.data);
|
|
75
|
+
if (evt.eventType === 'done' || evt.eventType === 'error') {
|
|
76
|
+
close();
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
runner.on(channel, onLive);
|
|
80
|
+
// Heartbeat: comment line every N seconds to keep proxies from idling out.
|
|
81
|
+
let heartbeat = null;
|
|
82
|
+
if (heartbeatMs > 0) {
|
|
83
|
+
heartbeat = setInterval(() => {
|
|
84
|
+
if (closed)
|
|
85
|
+
return;
|
|
86
|
+
res.write(`: keepalive ${Date.now()}\n\n`);
|
|
87
|
+
}, heartbeatMs);
|
|
88
|
+
}
|
|
89
|
+
// Wait until the connection closes.
|
|
90
|
+
await new Promise((resolve) => {
|
|
91
|
+
res.on('close', resolve);
|
|
92
|
+
});
|
|
93
|
+
if (heartbeat)
|
|
94
|
+
clearInterval(heartbeat);
|
|
95
|
+
runner.off(channel, onLive);
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=sse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/session/sse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,mBAAmB,GACzB,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,GAGb,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool registry — name → {definition, handler} dispatch with optional
|
|
3
|
+
* JSON-Schema validation of arguments.
|
|
4
|
+
*
|
|
5
|
+
* Implements `ToolExecutor` so it can be passed directly to
|
|
6
|
+
* `runAgentWithTools({ ..., toolExecutor: registry })`.
|
|
7
|
+
*
|
|
8
|
+
* Validation behaviour: when enabled (the default), arguments coming back
|
|
9
|
+
* from the model are checked against the tool's `inputSchema` before the
|
|
10
|
+
* handler runs. Failures return an error string to the agent rather than
|
|
11
|
+
* throwing — the model gets to read what went wrong and try again.
|
|
12
|
+
*
|
|
13
|
+
* Same policy for unknown tools and handler exceptions: errors become
|
|
14
|
+
* agent-readable strings, not thrown exceptions, so a single bad tool call
|
|
15
|
+
* doesn't kill the whole pipeline.
|
|
16
|
+
*/
|
|
17
|
+
import type { ToolDefinition, ToolExecutor, ToolResult } from '../messages.js';
|
|
18
|
+
/**
|
|
19
|
+
* Handler signature — receives parsed args plus optional context, returns the
|
|
20
|
+
* tool's result content as a string. Anything string-shaped works (JSON,
|
|
21
|
+
* markdown, plain text); the agent sees it raw.
|
|
22
|
+
*/
|
|
23
|
+
export type ToolHandler = (args: Record<string, unknown>, context: {
|
|
24
|
+
toolCallId: string;
|
|
25
|
+
}) => Promise<string>;
|
|
26
|
+
export interface ToolRegistryOptions {
|
|
27
|
+
/** When false, skip schema validation. Default: true. */
|
|
28
|
+
validate?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare class ToolRegistry implements ToolExecutor {
|
|
31
|
+
private tools;
|
|
32
|
+
private ajv;
|
|
33
|
+
constructor(options?: ToolRegistryOptions);
|
|
34
|
+
/**
|
|
35
|
+
* Register a tool. Throws on duplicate names — pipelines should be aware of
|
|
36
|
+
* what they expose, not silently overwrite.
|
|
37
|
+
*/
|
|
38
|
+
register(definition: ToolDefinition, handler: ToolHandler): this;
|
|
39
|
+
/** All registered tool definitions, in registration order. Hand to AgentConfig.tools. */
|
|
40
|
+
definitions(): ToolDefinition[];
|
|
41
|
+
has(name: string): boolean;
|
|
42
|
+
execute(name: string, toolCallId: string, args: Record<string, unknown>): Promise<ToolResult>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE/E;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,CACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,KAC5B,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AASD,qBAAa,YAAa,YAAW,YAAY;IAC/C,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,GAAG,CAAa;gBAEZ,OAAO,GAAE,mBAAwB;IAI7C;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAShE,yFAAyF;IACzF,WAAW,IAAI,cAAc,EAAE;IAI/B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpB,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,UAAU,CAAC;CA+BvB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool registry — name → {definition, handler} dispatch with optional
|
|
3
|
+
* JSON-Schema validation of arguments.
|
|
4
|
+
*
|
|
5
|
+
* Implements `ToolExecutor` so it can be passed directly to
|
|
6
|
+
* `runAgentWithTools({ ..., toolExecutor: registry })`.
|
|
7
|
+
*
|
|
8
|
+
* Validation behaviour: when enabled (the default), arguments coming back
|
|
9
|
+
* from the model are checked against the tool's `inputSchema` before the
|
|
10
|
+
* handler runs. Failures return an error string to the agent rather than
|
|
11
|
+
* throwing — the model gets to read what went wrong and try again.
|
|
12
|
+
*
|
|
13
|
+
* Same policy for unknown tools and handler exceptions: errors become
|
|
14
|
+
* agent-readable strings, not thrown exceptions, so a single bad tool call
|
|
15
|
+
* doesn't kill the whole pipeline.
|
|
16
|
+
*/
|
|
17
|
+
import { Ajv } from 'ajv';
|
|
18
|
+
export class ToolRegistry {
|
|
19
|
+
tools = new Map();
|
|
20
|
+
ajv;
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.ajv = options.validate === false ? null : new Ajv({ allErrors: true, strict: false });
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Register a tool. Throws on duplicate names — pipelines should be aware of
|
|
26
|
+
* what they expose, not silently overwrite.
|
|
27
|
+
*/
|
|
28
|
+
register(definition, handler) {
|
|
29
|
+
if (this.tools.has(definition.name)) {
|
|
30
|
+
throw new Error(`ToolRegistry: tool already registered: ${definition.name}`);
|
|
31
|
+
}
|
|
32
|
+
const validator = this.ajv ? this.ajv.compile(definition.inputSchema) : null;
|
|
33
|
+
this.tools.set(definition.name, { definition, handler, validator });
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
/** All registered tool definitions, in registration order. Hand to AgentConfig.tools. */
|
|
37
|
+
definitions() {
|
|
38
|
+
return [...this.tools.values()].map((e) => e.definition);
|
|
39
|
+
}
|
|
40
|
+
has(name) {
|
|
41
|
+
return this.tools.has(name);
|
|
42
|
+
}
|
|
43
|
+
async execute(name, toolCallId, args) {
|
|
44
|
+
const entry = this.tools.get(name);
|
|
45
|
+
if (!entry) {
|
|
46
|
+
return {
|
|
47
|
+
toolCallId,
|
|
48
|
+
content: `Error: unknown tool "${name}". Registered tools: ${[...this.tools.keys()].join(', ') || '(none)'}.`,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (entry.validator) {
|
|
52
|
+
const ok = entry.validator(args);
|
|
53
|
+
if (!ok) {
|
|
54
|
+
const errs = entry.validator.errors ?? [];
|
|
55
|
+
const detail = errs
|
|
56
|
+
.map((e) => `${e.instancePath || '/'} ${e.message ?? 'invalid'}`)
|
|
57
|
+
.join('; ');
|
|
58
|
+
return {
|
|
59
|
+
toolCallId,
|
|
60
|
+
content: `Error: invalid arguments for "${name}": ${detail}`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const content = await entry.handler(args, { toolCallId });
|
|
66
|
+
return { toolCallId, content };
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
70
|
+
return { toolCallId, content: `Error: tool "${name}" threw: ${message}` };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triggers — entry-point abstraction for thread-phase pipelines.
|
|
3
|
+
*
|
|
4
|
+
* The `Trigger` interface is the protocol every signal source implements:
|
|
5
|
+
* timers, webhooks, queue consumers, file watchers, message brokers.
|
|
6
|
+
* Core ships only `TimerTrigger`; HTTP/queue/file-watch adapters live in
|
|
7
|
+
* `examples/triggers/` as recipes.
|
|
8
|
+
*
|
|
9
|
+
* `runTrigger` is the canonical consumer — reads events, dispatches
|
|
10
|
+
* pipelines, optionally persists through a `JobRunner`.
|
|
11
|
+
*/
|
|
12
|
+
export type { Trigger, TriggerEvent } from './types.js';
|
|
13
|
+
export { TimerTrigger, type TimerTriggerOptions } from './timer-trigger.js';
|
|
14
|
+
export { runTrigger, type RunTriggerOptions, type RunTriggerHandle, } from './run-trigger.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/triggers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EACL,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triggers — entry-point abstraction for thread-phase pipelines.
|
|
3
|
+
*
|
|
4
|
+
* The `Trigger` interface is the protocol every signal source implements:
|
|
5
|
+
* timers, webhooks, queue consumers, file watchers, message brokers.
|
|
6
|
+
* Core ships only `TimerTrigger`; HTTP/queue/file-watch adapters live in
|
|
7
|
+
* `examples/triggers/` as recipes.
|
|
8
|
+
*
|
|
9
|
+
* `runTrigger` is the canonical consumer — reads events, dispatches
|
|
10
|
+
* pipelines, optionally persists through a `JobRunner`.
|
|
11
|
+
*/
|
|
12
|
+
export { TimerTrigger } from './timer-trigger.js';
|
|
13
|
+
export { runTrigger, } from './run-trigger.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/triggers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,YAAY,EAA4B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EACL,UAAU,GAGX,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runTrigger — the canonical Trigger consumer.
|
|
3
|
+
*
|
|
4
|
+
* Reads events from a `Trigger`, calls a user-supplied factory to produce
|
|
5
|
+
* `{ phases, ctx }` per event, and dispatches each pipeline. If a
|
|
6
|
+
* `JobRunner` is supplied, dispatch is persisted (job rows, event log,
|
|
7
|
+
* cancellation); otherwise the pipeline runs inline via
|
|
8
|
+
* `runPipelineToSummary` and events are discarded after dispatch.
|
|
9
|
+
*
|
|
10
|
+
* Resolves when the trigger generator exhausts, the abort signal fires,
|
|
11
|
+
* or `stop()` is called via the returned handle.
|
|
12
|
+
*
|
|
13
|
+
* Concurrency cap is a blocking semaphore. When `maxConcurrency` pipelines
|
|
14
|
+
* are in flight, the loop awaits a slot before pulling the next event
|
|
15
|
+
* from the trigger. The trigger's generator naturally pauses production
|
|
16
|
+
* — no events are dropped, no unbounded queue grows.
|
|
17
|
+
*
|
|
18
|
+
* Pipeline failures are isolated — one failing pipeline does not stop
|
|
19
|
+
* the trigger loop. Errors go through `onError` (or default stderr log)
|
|
20
|
+
* and the next event is still dispatched.
|
|
21
|
+
*
|
|
22
|
+
* Cancellation: each in-flight dispatch owns an `AbortController`. Call
|
|
23
|
+
* `handle.cancel(triggerEventId)` to abort that specific pipeline; the
|
|
24
|
+
* signal flows into `runPipelineToSummary` (inline) or `jobRunner.cancel`
|
|
25
|
+
* (persisted). Returns `true` if the pipeline was found and aborted,
|
|
26
|
+
* `false` if the event id is unknown or already completed.
|
|
27
|
+
*
|
|
28
|
+
* Observability: `onCapacityFull(event)` fires when an event arrives
|
|
29
|
+
* while the concurrency cap is full (the loop blocks on `Promise.race`
|
|
30
|
+
* before pulling the event, so this is the moment backpressure starts).
|
|
31
|
+
* `onDispatchStart(event)` fires immediately when a dispatch begins,
|
|
32
|
+
* before the user-supplied factory runs.
|
|
33
|
+
*/
|
|
34
|
+
import type { JobRunner } from '../session/index.js';
|
|
35
|
+
import type { BasePipelineContext, Phase } from '../phase.js';
|
|
36
|
+
import type { Trigger, TriggerEvent } from './types.js';
|
|
37
|
+
export interface RunTriggerOptions<TInput, TCtx extends BasePipelineContext> {
|
|
38
|
+
/**
|
|
39
|
+
* Optional JobRunner. If provided, each event creates a persisted job
|
|
40
|
+
* row and pipelines run through `runner.run()` (events go to the
|
|
41
|
+
* event log, cancellation works via `runner.cancel(jobId)`). If
|
|
42
|
+
* omitted, pipelines run inline.
|
|
43
|
+
*/
|
|
44
|
+
jobRunner?: JobRunner;
|
|
45
|
+
/** Name used for job rows when `jobRunner` is set. Default: `trigger.name`. */
|
|
46
|
+
pipelineName?: string;
|
|
47
|
+
/**
|
|
48
|
+
* Maximum concurrent in-flight pipelines from this trigger. When the
|
|
49
|
+
* cap is reached, the loop blocks before pulling the next event
|
|
50
|
+
* (backpressure flows back to the trigger). Default: 1.
|
|
51
|
+
*/
|
|
52
|
+
maxConcurrency?: number;
|
|
53
|
+
/** Abort the run loop and call `trigger.stop()`. Outstanding pipelines complete. */
|
|
54
|
+
signal?: AbortSignal;
|
|
55
|
+
/** Called when a pipeline is about to start, after dispatch picks it up. */
|
|
56
|
+
onStart?: (event: TriggerEvent<TInput>, jobId?: string) => void;
|
|
57
|
+
/** Called when a pipeline completes successfully. */
|
|
58
|
+
onComplete?: (event: TriggerEvent<TInput>, jobId?: string) => void;
|
|
59
|
+
/** Called when a pipeline throws. Default: log to stderr. */
|
|
60
|
+
onError?: (event: TriggerEvent<TInput>, error: Error, jobId?: string) => void;
|
|
61
|
+
/**
|
|
62
|
+
* Called when an event arrives while the concurrency cap is full —
|
|
63
|
+
* the moment backpressure begins. The dispatch still happens once a
|
|
64
|
+
* slot frees; this hook only signals the wait.
|
|
65
|
+
*/
|
|
66
|
+
onCapacityFull?: (event: TriggerEvent<TInput>) => void;
|
|
67
|
+
/** Called the moment dispatch starts, before the pipeline factory runs. */
|
|
68
|
+
onDispatchStart?: (event: TriggerEvent<TInput>) => void;
|
|
69
|
+
}
|
|
70
|
+
export interface RunTriggerHandle {
|
|
71
|
+
/** Resolves when the run loop has exited (trigger exhausted, signal fired, or `stop()` called). */
|
|
72
|
+
done: Promise<void>;
|
|
73
|
+
/** Stop the trigger and resolve `done` once outstanding pipelines complete. */
|
|
74
|
+
stop(): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Abort a specific in-flight pipeline by its trigger event id. Returns
|
|
77
|
+
* `true` if the pipeline was found and cancellation was initiated;
|
|
78
|
+
* `false` if the event id is unknown or already completed.
|
|
79
|
+
*/
|
|
80
|
+
cancel(triggerEventId: number): boolean;
|
|
81
|
+
}
|
|
82
|
+
export declare function runTrigger<TInput, TCtx extends BasePipelineContext>(trigger: Trigger<TInput>, pipelineFactory: (input: TInput, event: TriggerEvent<TInput>) => {
|
|
83
|
+
phases: ReadonlyArray<Phase<TCtx>>;
|
|
84
|
+
ctx: TCtx;
|
|
85
|
+
}, options?: RunTriggerOptions<TInput, TCtx>): RunTriggerHandle;
|
|
86
|
+
//# sourceMappingURL=run-trigger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-trigger.d.ts","sourceRoot":"","sources":["../../src/triggers/run-trigger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EACV,mBAAmB,EACnB,KAAK,EACN,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,iBAAiB,CAAC,MAAM,EAAE,IAAI,SAAS,mBAAmB;IACzE;;;;;OAKG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,qDAAqD;IACrD,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACnE,6DAA6D;IAC7D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9E;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACvD,2EAA2E;IAC3E,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;CACzD;AAED,MAAM,WAAW,gBAAgB;IAC/B,mGAAmG;IACnG,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,+EAA+E;IAC/E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB;;;;OAIG;IACH,MAAM,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;CACzC;AAQD,wBAAgB,UAAU,CAAC,MAAM,EAAE,IAAI,SAAS,mBAAmB,EACjE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,EACxB,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK;IAC/D,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACnC,GAAG,EAAE,IAAI,CAAC;CACX,EACD,OAAO,GAAE,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAM,GAC5C,gBAAgB,CA+HlB"}
|