@memgrafter/flatagents 0.9.0 → 2.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/MACHINES.md +78 -1
- package/dist/index.d.mts +49 -23
- package/dist/index.d.ts +49 -23
- package/dist/index.js +187 -142
- package/dist/index.mjs +186 -142
- package/package.json +1 -1
- package/schemas/flatagent.d.ts +11 -1
- package/schemas/flatagent.schema.json +38 -0
- package/schemas/flatagent.slim.d.ts +10 -1
- package/schemas/flatagents-runtime.d.ts +338 -47
- package/schemas/flatagents-runtime.schema.json +73 -14
- package/schemas/flatagents-runtime.slim.d.ts +102 -7
- package/schemas/flatmachine.d.ts +95 -32
- package/schemas/flatmachine.schema.json +141 -20
- package/schemas/flatmachine.slim.d.ts +30 -8
- package/schemas/profiles.d.ts +3 -1
- package/schemas/profiles.schema.json +3 -0
- package/schemas/profiles.slim.d.ts +2 -1
|
@@ -15,10 +15,12 @@
|
|
|
15
15
|
* - MachineHooks: Base interface (MUST)
|
|
16
16
|
* - RegistrationBackend: SQLiteRegistrationBackend (MUST), MemoryRegistrationBackend (SHOULD)
|
|
17
17
|
* - WorkBackend: SQLiteWorkBackend (MUST), MemoryWorkBackend (SHOULD)
|
|
18
|
+
* - SignalBackend: MemorySignalBackend (MUST), SQLiteSignalBackend (SHOULD)
|
|
19
|
+
* - TriggerBackend: NoOpTrigger (MUST), FileTrigger (SHOULD)
|
|
18
20
|
*
|
|
19
21
|
* OPTIONAL IMPLEMENTATIONS:
|
|
20
22
|
* -------------------------
|
|
21
|
-
* - Distributed backends (Redis, Postgres, etc.)
|
|
23
|
+
* - Distributed backends (Redis, Postgres, DynamoDB, etc.)
|
|
22
24
|
* - LLMBackend (SDK may use native provider SDKs)
|
|
23
25
|
*
|
|
24
26
|
* EXECUTION LOCKING:
|
|
@@ -68,10 +70,30 @@
|
|
|
68
70
|
* Extension points for machine execution.
|
|
69
71
|
* All methods are optional and can be sync or async.
|
|
70
72
|
*
|
|
73
|
+
* SDKs MUST provide:
|
|
74
|
+
* - HooksRegistry: Name-based registry for resolving hooks from config
|
|
75
|
+
*
|
|
71
76
|
* SDKs SHOULD provide:
|
|
72
77
|
* - WebhookHooks: Send events to HTTP endpoint
|
|
73
78
|
* - CompositeHooks: Combine multiple hook implementations
|
|
74
79
|
*
|
|
80
|
+
* HOOKS REGISTRY:
|
|
81
|
+
* ---------------
|
|
82
|
+
* Maps hook names (strings) to implementations. Machine configs reference
|
|
83
|
+
* hooks by name; the SDK's HooksRegistry resolves them at runtime.
|
|
84
|
+
*
|
|
85
|
+
* This decouples machine configs from any specific language or file path,
|
|
86
|
+
* enabling the same YAML to work across Python, JavaScript, Rust, etc.
|
|
87
|
+
*
|
|
88
|
+
* SDKs MUST provide:
|
|
89
|
+
* - HooksRegistry with register(), resolve(), has()
|
|
90
|
+
*
|
|
91
|
+
* Built-in registrations (when available in the SDK):
|
|
92
|
+
* - "logging": LoggingHooks
|
|
93
|
+
* - "webhook": WebhookHooks (args: { url, timeout?, api_key? })
|
|
94
|
+
* - "metrics": MetricsHooks
|
|
95
|
+
* - "distributed-worker": DistributedWorkerHooks
|
|
96
|
+
*
|
|
75
97
|
* LLM BACKEND (OPTIONAL):
|
|
76
98
|
* -----------------------
|
|
77
99
|
* Abstraction over LLM providers.
|
|
@@ -87,6 +109,83 @@
|
|
|
87
109
|
* Interface for invoking peer machines.
|
|
88
110
|
* Used internally by FlatMachine for `machine:` and `launch:` states.
|
|
89
111
|
*
|
|
112
|
+
* AGENT RESULT:
|
|
113
|
+
* --------------
|
|
114
|
+
* Universal result contract for agent execution across all backends.
|
|
115
|
+
* All fields are optional and use structured data (no classes) for
|
|
116
|
+
* cross-language and cross-process/network compatibility.
|
|
117
|
+
*
|
|
118
|
+
* REGISTRATION BACKEND:
|
|
119
|
+
* ---------------------
|
|
120
|
+
* Worker lifecycle management for distributed execution.
|
|
121
|
+
*
|
|
122
|
+
* SDKs MUST provide:
|
|
123
|
+
* - SQLiteRegistrationBackend: For local deployments
|
|
124
|
+
*
|
|
125
|
+
* SDKs SHOULD provide:
|
|
126
|
+
* - MemoryRegistrationBackend: For testing
|
|
127
|
+
*
|
|
128
|
+
* Implementation notes:
|
|
129
|
+
* - Time units: Python reference SDK uses seconds for all interval values
|
|
130
|
+
* - Stale threshold: SDKs SHOULD default to 2× heartbeat_interval if not specified
|
|
131
|
+
*
|
|
132
|
+
* WORK BACKEND:
|
|
133
|
+
* -------------
|
|
134
|
+
* Work distribution via named pools with atomic claim.
|
|
135
|
+
*
|
|
136
|
+
* SDKs MUST provide:
|
|
137
|
+
* - SQLiteWorkBackend: For local deployments
|
|
138
|
+
*
|
|
139
|
+
* SDKs SHOULD provide:
|
|
140
|
+
* - MemoryWorkBackend: For testing
|
|
141
|
+
*
|
|
142
|
+
* Implementation notes:
|
|
143
|
+
* - Atomic claim: SDKs MUST ensure no two workers can claim the same job
|
|
144
|
+
* - Test requirements: Include concurrent claim race condition tests
|
|
145
|
+
*
|
|
146
|
+
* SIGNAL BACKEND:
|
|
147
|
+
* ---------------
|
|
148
|
+
* Durable signal storage for cross-process machine activation.
|
|
149
|
+
*
|
|
150
|
+
* Signals are named-channel messages that wake checkpointed machines.
|
|
151
|
+
* A machine paused at a `wait_for` state checkpoints with a `waiting_channel`
|
|
152
|
+
* tag and exits (no process running). When a signal arrives on that channel,
|
|
153
|
+
* a dispatcher finds matching checkpoints and resumes them.
|
|
154
|
+
*
|
|
155
|
+
* Signal data becomes the `wait_for` state's output — accessible via
|
|
156
|
+
* output.* in output_to_context templates. No new template syntax needed.
|
|
157
|
+
*
|
|
158
|
+
* Channel semantics:
|
|
159
|
+
* - Addressed: "approval/task-001" → one waiting machine
|
|
160
|
+
* - Broadcast: "quota/openai" → N waiting machines (dispatcher controls limit)
|
|
161
|
+
*
|
|
162
|
+
* SDKs MUST provide:
|
|
163
|
+
* - MemorySignalBackend: For testing and single-process use
|
|
164
|
+
*
|
|
165
|
+
* SDKs SHOULD provide:
|
|
166
|
+
* - SQLiteSignalBackend: For durable local use
|
|
167
|
+
*
|
|
168
|
+
* TRIGGER BACKEND:
|
|
169
|
+
* ----------------
|
|
170
|
+
* Process activation when signals or work arrive.
|
|
171
|
+
* The OS-native equivalent of Lambda triggers / DynamoDB Streams.
|
|
172
|
+
*
|
|
173
|
+
* Called after SignalBackend.send() or WorkPool.push() to wake
|
|
174
|
+
* a dispatcher process. The dispatcher then queries for matching
|
|
175
|
+
* checkpoints and resumes machines.
|
|
176
|
+
*
|
|
177
|
+
* Deployment mapping:
|
|
178
|
+
* - NoOpTrigger: Consumer already running (polling/in-process)
|
|
179
|
+
* - FileTrigger: Touch file → launchd WatchPaths (macOS) / systemd PathChanged (Linux)
|
|
180
|
+
* - SocketTrigger: UDS notify → in-host low-latency wake (optional)
|
|
181
|
+
* - DynamoDB: Implicit via Streams → Lambda (no application code)
|
|
182
|
+
*
|
|
183
|
+
* SDKs MUST provide:
|
|
184
|
+
* - NoOpTrigger: For in-process and polling consumers
|
|
185
|
+
*
|
|
186
|
+
* SDKs SHOULD provide:
|
|
187
|
+
* - FileTrigger: For OS-level activation with zero running processes
|
|
188
|
+
*
|
|
90
189
|
* BACKEND CONFIGURATION:
|
|
91
190
|
* ----------------------
|
|
92
191
|
* Backend configuration for machine settings.
|
|
@@ -146,6 +245,24 @@ export interface PersistenceBackend {
|
|
|
146
245
|
* @returns Array of matching keys, sorted lexicographically
|
|
147
246
|
*/
|
|
148
247
|
list(prefix: string): Promise<string[]>;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* List execution IDs, optionally filtered by latest checkpoint state.
|
|
251
|
+
*
|
|
252
|
+
* @param options.event - Filter by latest checkpoint event (e.g., "machine_end")
|
|
253
|
+
* @param options.waiting_channel - Filter by waiting_channel metadata
|
|
254
|
+
* @returns Array of matching execution IDs
|
|
255
|
+
*/
|
|
256
|
+
listExecutionIds?(options?: {
|
|
257
|
+
event?: string;
|
|
258
|
+
waiting_channel?: string;
|
|
259
|
+
}): Promise<string[]>;
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Delete all checkpoint data for an execution.
|
|
263
|
+
* Safe to call if execution doesn't exist.
|
|
264
|
+
*/
|
|
265
|
+
deleteExecution?(execution_id: string): Promise<void>;
|
|
149
266
|
}
|
|
150
267
|
|
|
151
268
|
export interface ResultBackend {
|
|
@@ -181,14 +298,126 @@ export interface ResultBackend {
|
|
|
181
298
|
delete(uri: string): Promise<void>;
|
|
182
299
|
}
|
|
183
300
|
|
|
301
|
+
export interface AgentResult {
|
|
302
|
+
// Content
|
|
303
|
+
output?: Record<string, any> | null;
|
|
304
|
+
content?: string | null;
|
|
305
|
+
raw?: any; // In-process only, not serialized across boundaries
|
|
306
|
+
|
|
307
|
+
// Metrics
|
|
308
|
+
usage?: UsageInfo | null;
|
|
309
|
+
cost?: CostInfo | number | null; // number for backwards compatibility
|
|
310
|
+
metadata?: Record<string, any> | null;
|
|
311
|
+
|
|
312
|
+
// Completion status
|
|
313
|
+
/** Known values: "stop", "length", "tool_use", "error", "content_filter", "aborted" */
|
|
314
|
+
finish_reason?: string | null;
|
|
315
|
+
|
|
316
|
+
// Error info (null/undefined = success)
|
|
317
|
+
error?: AgentError | null;
|
|
318
|
+
|
|
319
|
+
// Rate limit state (normalized for orchestration)
|
|
320
|
+
rate_limit?: RateLimitState | null;
|
|
321
|
+
|
|
322
|
+
// Provider-specific data (includes raw_headers when available)
|
|
323
|
+
provider_data?: ProviderData | null;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export interface UsageInfo {
|
|
327
|
+
input_tokens?: number;
|
|
328
|
+
output_tokens?: number;
|
|
329
|
+
total_tokens?: number;
|
|
330
|
+
cache_read_tokens?: number;
|
|
331
|
+
cache_write_tokens?: number;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export interface CostInfo {
|
|
335
|
+
input?: number;
|
|
336
|
+
output?: number;
|
|
337
|
+
cache_read?: number;
|
|
338
|
+
cache_write?: number;
|
|
339
|
+
total?: number;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export interface AgentError {
|
|
343
|
+
/**
|
|
344
|
+
* Known values: "rate_limit", "timeout", "server_error", "invalid_request",
|
|
345
|
+
* "auth_error", "content_filter", "context_length", "model_unavailable"
|
|
346
|
+
* Custom codes allowed for extension.
|
|
347
|
+
*/
|
|
348
|
+
code?: string;
|
|
349
|
+
|
|
350
|
+
/** Original error type name (e.g., "RateLimitError", "TimeoutError") */
|
|
351
|
+
type?: string;
|
|
352
|
+
|
|
353
|
+
/** Human-readable error message */
|
|
354
|
+
message: string;
|
|
355
|
+
|
|
356
|
+
/** HTTP status code if applicable */
|
|
357
|
+
status_code?: number;
|
|
358
|
+
|
|
359
|
+
/** Whether retry might succeed */
|
|
360
|
+
retryable?: boolean;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export interface RateLimitState {
|
|
364
|
+
/** Is any limit exhausted? */
|
|
365
|
+
limited: boolean;
|
|
366
|
+
|
|
367
|
+
/** Recommended wait time in seconds */
|
|
368
|
+
retry_after?: number;
|
|
369
|
+
|
|
370
|
+
/** Per-window breakdown for smart orchestration */
|
|
371
|
+
windows?: RateLimitWindow[];
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export interface RateLimitWindow {
|
|
375
|
+
/** Identifier: "requests_per_minute", "tokens_per_day", etc. */
|
|
376
|
+
name: string;
|
|
377
|
+
|
|
378
|
+
/** Known values: "requests", "tokens", "input_tokens", "output_tokens" */
|
|
379
|
+
resource: string;
|
|
380
|
+
|
|
381
|
+
/** Current remaining in this window */
|
|
382
|
+
remaining?: number;
|
|
383
|
+
|
|
384
|
+
/** Maximum for this window */
|
|
385
|
+
limit?: number;
|
|
386
|
+
|
|
387
|
+
/** Seconds until this window resets */
|
|
388
|
+
resets_in?: number;
|
|
389
|
+
|
|
390
|
+
/** Unix timestamp when this window resets */
|
|
391
|
+
reset_at?: number;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export interface ProviderData {
|
|
395
|
+
/** Provider name (e.g., "openai", "anthropic", "cerebras") */
|
|
396
|
+
provider?: string;
|
|
397
|
+
|
|
398
|
+
/** Model identifier */
|
|
399
|
+
model?: string;
|
|
400
|
+
|
|
401
|
+
/** Provider's request ID for debugging/support */
|
|
402
|
+
request_id?: string;
|
|
403
|
+
|
|
404
|
+
/** Raw HTTP headers from response (when backend exposes them) */
|
|
405
|
+
raw_headers?: Record<string, string>;
|
|
406
|
+
|
|
407
|
+
/** Any other provider-specific data */
|
|
408
|
+
[key: string]: any;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export interface AgentExecutor {
|
|
412
|
+
execute(input: Record<string, any>, context?: Record<string, any>): Promise<AgentResult>;
|
|
413
|
+
metadata?: Record<string, any>;
|
|
414
|
+
}
|
|
415
|
+
|
|
184
416
|
export interface ExecutionType {
|
|
185
417
|
/**
|
|
186
|
-
* Execute
|
|
187
|
-
*
|
|
188
|
-
* @param fn - The async function to execute (typically agent.call)
|
|
189
|
-
* @returns The result(s) according to strategy
|
|
418
|
+
* Execute an agent with this strategy.
|
|
190
419
|
*/
|
|
191
|
-
execute
|
|
420
|
+
execute(executor: AgentExecutor, input: Record<string, any>, context?: Record<string, any>): Promise<AgentResult>;
|
|
192
421
|
}
|
|
193
422
|
|
|
194
423
|
export interface ExecutionConfig {
|
|
@@ -227,6 +456,37 @@ export interface MachineHooks {
|
|
|
227
456
|
onAction?(action: string, context: Record<string, any>): Record<string, any> | Promise<Record<string, any>>;
|
|
228
457
|
}
|
|
229
458
|
|
|
459
|
+
export interface HooksRegistry {
|
|
460
|
+
/**
|
|
461
|
+
* Register a hooks implementation by name.
|
|
462
|
+
* @param name - Hook name referenced in machine config (e.g., "my-hooks")
|
|
463
|
+
* @param factory - Class constructor or factory function
|
|
464
|
+
*/
|
|
465
|
+
register(name: string, factory: HooksFactory): void;
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Resolve a HooksRef from machine config into a MachineHooks instance.
|
|
469
|
+
* - String: lookup by name, no args
|
|
470
|
+
* - HooksRefConfig: lookup by name, pass args to factory
|
|
471
|
+
* - Array: resolve each entry, combine with CompositeHooks
|
|
472
|
+
* @throws Error if name is not registered
|
|
473
|
+
*/
|
|
474
|
+
resolve(ref: HooksRef): MachineHooks;
|
|
475
|
+
|
|
476
|
+
/** Check if a name is registered. */
|
|
477
|
+
has(name: string): boolean;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Factory for creating MachineHooks instances.
|
|
482
|
+
* Can be a class constructor or a function.
|
|
483
|
+
*/
|
|
484
|
+
export type HooksFactory =
|
|
485
|
+
| { new (args?: Record<string, any>): MachineHooks }
|
|
486
|
+
| ((args?: Record<string, any>) => MachineHooks);
|
|
487
|
+
|
|
488
|
+
import { HooksRef, HooksRefConfig } from "./flatmachine";
|
|
489
|
+
|
|
230
490
|
export interface LLMBackend {
|
|
231
491
|
/** Total cost accumulated across all calls. */
|
|
232
492
|
totalCost: number;
|
|
@@ -307,6 +567,7 @@ export interface MachineSnapshot {
|
|
|
307
567
|
total_cost?: number;
|
|
308
568
|
parent_execution_id?: string;
|
|
309
569
|
pending_launches?: LaunchIntent[];
|
|
570
|
+
waiting_channel?: string;
|
|
310
571
|
}
|
|
311
572
|
|
|
312
573
|
export interface LaunchIntent {
|
|
@@ -316,21 +577,6 @@ export interface LaunchIntent {
|
|
|
316
577
|
launched: boolean;
|
|
317
578
|
}
|
|
318
579
|
|
|
319
|
-
/**
|
|
320
|
-
* REGISTRATION BACKEND:
|
|
321
|
-
* ---------------------
|
|
322
|
-
* Worker lifecycle management for distributed execution.
|
|
323
|
-
*
|
|
324
|
-
* SDKs MUST provide:
|
|
325
|
-
* - SQLiteRegistrationBackend: For local deployments
|
|
326
|
-
*
|
|
327
|
-
* SDKs SHOULD provide:
|
|
328
|
-
* - MemoryRegistrationBackend: For testing
|
|
329
|
-
*
|
|
330
|
-
* Implementation notes:
|
|
331
|
-
* - Time units: Python reference SDK uses seconds for all interval values
|
|
332
|
-
* - Stale threshold: SDKs SHOULD default to 2× heartbeat_interval if not specified
|
|
333
|
-
*/
|
|
334
580
|
export interface RegistrationBackend {
|
|
335
581
|
/**
|
|
336
582
|
* Register a new worker.
|
|
@@ -388,21 +634,6 @@ export interface WorkerFilter {
|
|
|
388
634
|
stale_threshold_seconds?: number; // Filter workers with old heartbeats
|
|
389
635
|
}
|
|
390
636
|
|
|
391
|
-
/**
|
|
392
|
-
* WORK BACKEND:
|
|
393
|
-
* -------------
|
|
394
|
-
* Work distribution via named pools with atomic claim.
|
|
395
|
-
*
|
|
396
|
-
* SDKs MUST provide:
|
|
397
|
-
* - SQLiteWorkBackend: For local deployments
|
|
398
|
-
*
|
|
399
|
-
* SDKs SHOULD provide:
|
|
400
|
-
* - MemoryWorkBackend: For testing
|
|
401
|
-
*
|
|
402
|
-
* Implementation notes:
|
|
403
|
-
* - Atomic claim: SDKs MUST ensure no two workers can claim the same job
|
|
404
|
-
* - Test requirements: Include concurrent claim race condition tests
|
|
405
|
-
*/
|
|
406
637
|
export interface WorkBackend {
|
|
407
638
|
/**
|
|
408
639
|
* Get a named work pool.
|
|
@@ -468,32 +699,89 @@ export interface WorkItem {
|
|
|
468
699
|
// - "done": Successfully completed
|
|
469
700
|
// - "poisoned": Failed max_retries times, will not be retried
|
|
470
701
|
|
|
702
|
+
export interface SignalBackend {
|
|
703
|
+
/**
|
|
704
|
+
* Send a signal to a named channel.
|
|
705
|
+
* @param channel - Channel name (e.g., "approval/task-001", "quota/openai")
|
|
706
|
+
* @param data - Signal payload (JSON-serializable)
|
|
707
|
+
* @returns Signal ID
|
|
708
|
+
*/
|
|
709
|
+
send(channel: string, data: any): Promise<string>;
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Atomically consume the next signal on a channel.
|
|
713
|
+
* Removes the signal from storage.
|
|
714
|
+
* @returns Signal or null if none pending
|
|
715
|
+
*/
|
|
716
|
+
consume(channel: string): Promise<Signal | null>;
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Peek at pending signals without consuming.
|
|
720
|
+
* @returns Array of pending signals on this channel
|
|
721
|
+
*/
|
|
722
|
+
peek(channel: string): Promise<Signal[]>;
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* List channels that have pending signals.
|
|
726
|
+
* Used by dispatcher to find actionable channels.
|
|
727
|
+
*/
|
|
728
|
+
channels(): Promise<string[]>;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
export interface Signal {
|
|
732
|
+
id: string;
|
|
733
|
+
channel: string;
|
|
734
|
+
data: any;
|
|
735
|
+
created_at: string;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
export interface TriggerBackend {
|
|
739
|
+
/**
|
|
740
|
+
* Signal that activity occurred on a channel.
|
|
741
|
+
* Implementation touches a file, writes to a socket, or no-ops.
|
|
742
|
+
*
|
|
743
|
+
* @param channel - Channel name
|
|
744
|
+
*/
|
|
745
|
+
notify(channel: string): Promise<void>;
|
|
746
|
+
}
|
|
747
|
+
|
|
471
748
|
export interface BackendConfig {
|
|
472
749
|
/** Checkpoint storage. Default: memory */
|
|
473
|
-
persistence?: "memory" | "local" | "redis" | "postgres" | "s3";
|
|
750
|
+
persistence?: "memory" | "local" | "sqlite" | "redis" | "postgres" | "s3" | "dynamodb";
|
|
474
751
|
|
|
475
752
|
/** Execution locking. Default: none */
|
|
476
|
-
locking?: "none" | "local" | "redis" | "consul";
|
|
753
|
+
locking?: "none" | "local" | "sqlite" | "redis" | "consul" | "dynamodb";
|
|
477
754
|
|
|
478
755
|
/** Inter-machine results. Default: memory */
|
|
479
|
-
results?: "memory" | "redis";
|
|
756
|
+
results?: "memory" | "redis" | "dynamodb";
|
|
480
757
|
|
|
481
758
|
/** Worker registration. Default: memory */
|
|
482
|
-
registration?: "memory" | "sqlite" | "redis";
|
|
759
|
+
registration?: "memory" | "sqlite" | "redis" | "dynamodb";
|
|
483
760
|
|
|
484
761
|
/** Work pool. Default: memory */
|
|
485
|
-
work?: "memory" | "sqlite" | "redis";
|
|
762
|
+
work?: "memory" | "sqlite" | "redis" | "dynamodb";
|
|
763
|
+
|
|
764
|
+
/** Signal storage. Default: memory */
|
|
765
|
+
signal?: "memory" | "sqlite" | "redis" | "dynamodb";
|
|
486
766
|
|
|
487
|
-
/**
|
|
767
|
+
/** Trigger activation. Default: none */
|
|
768
|
+
trigger?: "none" | "file" | "socket";
|
|
769
|
+
|
|
770
|
+
/** Path for sqlite backends (registration, work, and signals share this) */
|
|
488
771
|
sqlite_path?: string;
|
|
772
|
+
|
|
773
|
+
/** Base path for file triggers (default: /tmp/flatmachines) */
|
|
774
|
+
trigger_path?: string;
|
|
775
|
+
|
|
776
|
+
/** DynamoDB table name (single-table design) */
|
|
777
|
+
dynamodb_table?: string;
|
|
778
|
+
|
|
779
|
+
/** AWS region for DynamoDB */
|
|
780
|
+
aws_region?: string;
|
|
489
781
|
}
|
|
490
782
|
|
|
491
|
-
export const SPEC_VERSION = "0.
|
|
783
|
+
export const SPEC_VERSION = "2.0.0";
|
|
492
784
|
|
|
493
|
-
/**
|
|
494
|
-
* Wrapper interface for JSON schema generation.
|
|
495
|
-
* Groups all runtime interfaces that SDKs must implement.
|
|
496
|
-
*/
|
|
497
785
|
export interface SDKRuntimeWrapper {
|
|
498
786
|
spec: "flatagents-runtime";
|
|
499
787
|
spec_version: typeof SPEC_VERSION;
|
|
@@ -502,11 +790,14 @@ export interface SDKRuntimeWrapper {
|
|
|
502
790
|
result_backend?: ResultBackend;
|
|
503
791
|
execution_config?: ExecutionConfig;
|
|
504
792
|
machine_hooks?: MachineHooks;
|
|
793
|
+
hooks_registry?: HooksRegistry;
|
|
505
794
|
llm_backend?: LLMBackend;
|
|
506
795
|
machine_invoker?: MachineInvoker;
|
|
507
796
|
backend_config?: BackendConfig;
|
|
508
797
|
machine_snapshot?: MachineSnapshot;
|
|
509
798
|
registration_backend?: RegistrationBackend;
|
|
510
799
|
work_backend?: WorkBackend;
|
|
800
|
+
signal_backend?: SignalBackend;
|
|
801
|
+
trigger_backend?: TriggerBackend;
|
|
511
802
|
}
|
|
512
803
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"spec_version": {
|
|
13
13
|
"type": "string",
|
|
14
|
-
"const": "0.
|
|
14
|
+
"const": "2.0.0"
|
|
15
15
|
},
|
|
16
16
|
"execution_lock": {
|
|
17
17
|
"$ref": "#/definitions/ExecutionLock"
|
|
@@ -28,6 +28,9 @@
|
|
|
28
28
|
"machine_hooks": {
|
|
29
29
|
"$ref": "#/definitions/MachineHooks"
|
|
30
30
|
},
|
|
31
|
+
"hooks_registry": {
|
|
32
|
+
"$ref": "#/definitions/HooksRegistry"
|
|
33
|
+
},
|
|
31
34
|
"llm_backend": {
|
|
32
35
|
"$ref": "#/definitions/LLMBackend"
|
|
33
36
|
},
|
|
@@ -45,19 +48,24 @@
|
|
|
45
48
|
},
|
|
46
49
|
"work_backend": {
|
|
47
50
|
"$ref": "#/definitions/WorkBackend"
|
|
51
|
+
},
|
|
52
|
+
"signal_backend": {
|
|
53
|
+
"$ref": "#/definitions/SignalBackend"
|
|
54
|
+
},
|
|
55
|
+
"trigger_backend": {
|
|
56
|
+
"$ref": "#/definitions/TriggerBackend"
|
|
48
57
|
}
|
|
49
58
|
},
|
|
50
59
|
"required": [
|
|
51
60
|
"spec",
|
|
52
61
|
"spec_version"
|
|
53
62
|
],
|
|
54
|
-
"additionalProperties": false
|
|
55
|
-
"description": "Wrapper interface for JSON schema generation. Groups all runtime interfaces that SDKs must implement."
|
|
63
|
+
"additionalProperties": false
|
|
56
64
|
},
|
|
57
65
|
"ExecutionLock": {
|
|
58
66
|
"type": "object",
|
|
59
67
|
"additionalProperties": false,
|
|
60
|
-
"description": "FlatAgents Runtime Interface Spec ==================================\n\nThis file defines the runtime interfaces that SDKs MUST implement to be considered compliant. These are NOT configuration schemas (see flatagent.d.ts and flatmachine.d.ts for those).\n\nREQUIRED IMPLEMENTATIONS:\n------------------------- - ExecutionLock: NoOpLock (MUST), LocalFileLock (SHOULD) - PersistenceBackend: MemoryBackend (MUST), LocalFileBackend (SHOULD) - ResultBackend: InMemoryResultBackend (MUST) - ExecutionType: Default, Retry, Parallel, MDAPVoting (MUST) - MachineHooks: Base interface (MUST) - RegistrationBackend: SQLiteRegistrationBackend (MUST), MemoryRegistrationBackend (SHOULD) - WorkBackend: SQLiteWorkBackend (MUST), MemoryWorkBackend (SHOULD)\n\nOPTIONAL IMPLEMENTATIONS:\n------------------------- - Distributed backends (Redis, Postgres, etc.) - LLMBackend (SDK may use native provider SDKs)\n\nEXECUTION LOCKING:\n------------------ Prevents concurrent execution of the same machine instance.\n\nSDKs MUST provide: - NoOpLock: For when locking is handled externally or disabled\n\nSDKs SHOULD provide: - LocalFileLock: For single-node deployments using fcntl/flock\n\nDistributed deployments should implement Redis/Consul/etcd locks.\n\nPERSISTENCE BACKEND:\n-------------------- Storage backend for machine checkpoints.\n\nSDKs MUST provide: - MemoryBackend: For testing and ephemeral runs\n\nSDKs SHOULD provide: - LocalFileBackend: For durable local storage with atomic writes\n\nRESULT BACKEND:\n--------------- Inter-machine communication via URI-addressed results.\n\nURI format: flatagents://{execution_id}/{path} - path is typically \"result\" or \"checkpoint\"\n\nSDKs MUST provide: - InMemoryResultBackend: For single-process execution\n\nEXECUTION TYPES:\n---------------- Execution strategy for agent calls.\n\nSDKs MUST implement all four types: - default: Single call, no retry - retry: Configurable backoffs with jitter - parallel: Run N samples, return all successes - mdap_voting: Multi-sample with consensus voting\n\nMACHINE HOOKS:\n-------------- Extension points for machine execution. All methods are optional and can be sync or async.\n\nSDKs SHOULD provide: - WebhookHooks: Send events to HTTP endpoint - CompositeHooks: Combine multiple hook implementations\n\nLLM BACKEND (OPTIONAL):\n----------------------- Abstraction over LLM providers.\n\nThis interface is OPTIONAL - SDKs may use provider SDKs directly. Useful for: - Unified retry/monitoring across providers - Provider-agnostic code - Testing with mock backends\n\nMACHINE INVOKER:\n---------------- Interface for invoking peer machines. Used internally by FlatMachine for `machine:` and `launch:` states.\n\nBACKEND CONFIGURATION:\n---------------------- Backend configuration for machine settings.\n\nExample in YAML: settings: backends: persistence: local locking: none results: memory"
|
|
68
|
+
"description": "FlatAgents Runtime Interface Spec ==================================\n\nThis file defines the runtime interfaces that SDKs MUST implement to be considered compliant. These are NOT configuration schemas (see flatagent.d.ts and flatmachine.d.ts for those).\n\nREQUIRED IMPLEMENTATIONS:\n------------------------- - ExecutionLock: NoOpLock (MUST), LocalFileLock (SHOULD) - PersistenceBackend: MemoryBackend (MUST), LocalFileBackend (SHOULD) - ResultBackend: InMemoryResultBackend (MUST) - ExecutionType: Default, Retry, Parallel, MDAPVoting (MUST) - MachineHooks: Base interface (MUST) - RegistrationBackend: SQLiteRegistrationBackend (MUST), MemoryRegistrationBackend (SHOULD) - WorkBackend: SQLiteWorkBackend (MUST), MemoryWorkBackend (SHOULD) - SignalBackend: MemorySignalBackend (MUST), SQLiteSignalBackend (SHOULD) - TriggerBackend: NoOpTrigger (MUST), FileTrigger (SHOULD)\n\nOPTIONAL IMPLEMENTATIONS:\n------------------------- - Distributed backends (Redis, Postgres, DynamoDB, etc.) - LLMBackend (SDK may use native provider SDKs)\n\nEXECUTION LOCKING:\n------------------ Prevents concurrent execution of the same machine instance.\n\nSDKs MUST provide: - NoOpLock: For when locking is handled externally or disabled\n\nSDKs SHOULD provide: - LocalFileLock: For single-node deployments using fcntl/flock\n\nDistributed deployments should implement Redis/Consul/etcd locks.\n\nPERSISTENCE BACKEND:\n-------------------- Storage backend for machine checkpoints.\n\nSDKs MUST provide: - MemoryBackend: For testing and ephemeral runs\n\nSDKs SHOULD provide: - LocalFileBackend: For durable local storage with atomic writes\n\nRESULT BACKEND:\n--------------- Inter-machine communication via URI-addressed results.\n\nURI format: flatagents://{execution_id}/{path} - path is typically \"result\" or \"checkpoint\"\n\nSDKs MUST provide: - InMemoryResultBackend: For single-process execution\n\nEXECUTION TYPES:\n---------------- Execution strategy for agent calls.\n\nSDKs MUST implement all four types: - default: Single call, no retry - retry: Configurable backoffs with jitter - parallel: Run N samples, return all successes - mdap_voting: Multi-sample with consensus voting\n\nMACHINE HOOKS:\n-------------- Extension points for machine execution. All methods are optional and can be sync or async.\n\nSDKs MUST provide: - HooksRegistry: Name-based registry for resolving hooks from config\n\nSDKs SHOULD provide: - WebhookHooks: Send events to HTTP endpoint - CompositeHooks: Combine multiple hook implementations\n\nHOOKS REGISTRY:\n--------------- Maps hook names (strings) to implementations. Machine configs reference hooks by name; the SDK's HooksRegistry resolves them at runtime.\n\nThis decouples machine configs from any specific language or file path, enabling the same YAML to work across Python, JavaScript, Rust, etc.\n\nSDKs MUST provide: - HooksRegistry with register(), resolve(), has()\n\nBuilt-in registrations (when available in the SDK): - \"logging\": LoggingHooks - \"webhook\": WebhookHooks (args: { url, timeout?, api_key? }) - \"metrics\": MetricsHooks - \"distributed-worker\": DistributedWorkerHooks\n\nLLM BACKEND (OPTIONAL):\n----------------------- Abstraction over LLM providers.\n\nThis interface is OPTIONAL - SDKs may use provider SDKs directly. Useful for: - Unified retry/monitoring across providers - Provider-agnostic code - Testing with mock backends\n\nMACHINE INVOKER:\n---------------- Interface for invoking peer machines. Used internally by FlatMachine for `machine:` and `launch:` states.\n\nAGENT RESULT:\n-------------- Universal result contract for agent execution across all backends. All fields are optional and use structured data (no classes) for cross-language and cross-process/network compatibility.\n\nREGISTRATION BACKEND:\n--------------------- Worker lifecycle management for distributed execution.\n\nSDKs MUST provide: - SQLiteRegistrationBackend: For local deployments\n\nSDKs SHOULD provide: - MemoryRegistrationBackend: For testing\n\nImplementation notes: - Time units: Python reference SDK uses seconds for all interval values - Stale threshold: SDKs SHOULD default to 2× heartbeat_interval if not specified\n\nWORK BACKEND:\n------------- Work distribution via named pools with atomic claim.\n\nSDKs MUST provide: - SQLiteWorkBackend: For local deployments\n\nSDKs SHOULD provide: - MemoryWorkBackend: For testing\n\nImplementation notes: - Atomic claim: SDKs MUST ensure no two workers can claim the same job - Test requirements: Include concurrent claim race condition tests\n\nSIGNAL BACKEND:\n--------------- Durable signal storage for cross-process machine activation.\n\nSignals are named-channel messages that wake checkpointed machines. A machine paused at a `wait_for` state checkpoints with a `waiting_channel` tag and exits (no process running). When a signal arrives on that channel, a dispatcher finds matching checkpoints and resumes them.\n\nSignal data becomes the `wait_for` state's output — accessible via output.* in output_to_context templates. No new template syntax needed.\n\nChannel semantics: - Addressed: \"approval/task-001\" → one waiting machine - Broadcast: \"quota/openai\" → N waiting machines (dispatcher controls limit)\n\nSDKs MUST provide: - MemorySignalBackend: For testing and single-process use\n\nSDKs SHOULD provide: - SQLiteSignalBackend: For durable local use\n\nTRIGGER BACKEND:\n---------------- Process activation when signals or work arrive. The OS-native equivalent of Lambda triggers / DynamoDB Streams.\n\nCalled after SignalBackend.send() or WorkPool.push() to wake a dispatcher process. The dispatcher then queries for matching checkpoints and resumes machines.\n\nDeployment mapping: - NoOpTrigger: Consumer already running (polling/in-process) - FileTrigger: Touch file → launchd WatchPaths (macOS) / systemd PathChanged (Linux) - SocketTrigger: UDS notify → in-host low-latency wake (optional) - DynamoDB: Implicit via Streams → Lambda (no application code)\n\nSDKs MUST provide: - NoOpTrigger: For in-process and polling consumers\n\nSDKs SHOULD provide: - FileTrigger: For OS-level activation with zero running processes\n\nBACKEND CONFIGURATION:\n---------------------- Backend configuration for machine settings.\n\nExample in YAML: settings: backends: persistence: local locking: none results: memory"
|
|
61
69
|
},
|
|
62
70
|
"PersistenceBackend": {
|
|
63
71
|
"type": "object",
|
|
@@ -107,6 +115,10 @@
|
|
|
107
115
|
"type": "object",
|
|
108
116
|
"additionalProperties": false
|
|
109
117
|
},
|
|
118
|
+
"HooksRegistry": {
|
|
119
|
+
"type": "object",
|
|
120
|
+
"additionalProperties": false
|
|
121
|
+
},
|
|
110
122
|
"LLMBackend": {
|
|
111
123
|
"type": "object",
|
|
112
124
|
"properties": {
|
|
@@ -137,9 +149,11 @@
|
|
|
137
149
|
"enum": [
|
|
138
150
|
"memory",
|
|
139
151
|
"local",
|
|
152
|
+
"sqlite",
|
|
140
153
|
"redis",
|
|
141
154
|
"postgres",
|
|
142
|
-
"s3"
|
|
155
|
+
"s3",
|
|
156
|
+
"dynamodb"
|
|
143
157
|
],
|
|
144
158
|
"description": "Checkpoint storage. Default: memory"
|
|
145
159
|
},
|
|
@@ -148,8 +162,10 @@
|
|
|
148
162
|
"enum": [
|
|
149
163
|
"none",
|
|
150
164
|
"local",
|
|
165
|
+
"sqlite",
|
|
151
166
|
"redis",
|
|
152
|
-
"consul"
|
|
167
|
+
"consul",
|
|
168
|
+
"dynamodb"
|
|
153
169
|
],
|
|
154
170
|
"description": "Execution locking. Default: none"
|
|
155
171
|
},
|
|
@@ -157,7 +173,8 @@
|
|
|
157
173
|
"type": "string",
|
|
158
174
|
"enum": [
|
|
159
175
|
"memory",
|
|
160
|
-
"redis"
|
|
176
|
+
"redis",
|
|
177
|
+
"dynamodb"
|
|
161
178
|
],
|
|
162
179
|
"description": "Inter-machine results. Default: memory"
|
|
163
180
|
},
|
|
@@ -166,7 +183,8 @@
|
|
|
166
183
|
"enum": [
|
|
167
184
|
"memory",
|
|
168
185
|
"sqlite",
|
|
169
|
-
"redis"
|
|
186
|
+
"redis",
|
|
187
|
+
"dynamodb"
|
|
170
188
|
],
|
|
171
189
|
"description": "Worker registration. Default: memory"
|
|
172
190
|
},
|
|
@@ -175,13 +193,45 @@
|
|
|
175
193
|
"enum": [
|
|
176
194
|
"memory",
|
|
177
195
|
"sqlite",
|
|
178
|
-
"redis"
|
|
196
|
+
"redis",
|
|
197
|
+
"dynamodb"
|
|
179
198
|
],
|
|
180
199
|
"description": "Work pool. Default: memory"
|
|
181
200
|
},
|
|
201
|
+
"signal": {
|
|
202
|
+
"type": "string",
|
|
203
|
+
"enum": [
|
|
204
|
+
"memory",
|
|
205
|
+
"sqlite",
|
|
206
|
+
"redis",
|
|
207
|
+
"dynamodb"
|
|
208
|
+
],
|
|
209
|
+
"description": "Signal storage. Default: memory"
|
|
210
|
+
},
|
|
211
|
+
"trigger": {
|
|
212
|
+
"type": "string",
|
|
213
|
+
"enum": [
|
|
214
|
+
"none",
|
|
215
|
+
"file",
|
|
216
|
+
"socket"
|
|
217
|
+
],
|
|
218
|
+
"description": "Trigger activation. Default: none"
|
|
219
|
+
},
|
|
182
220
|
"sqlite_path": {
|
|
183
221
|
"type": "string",
|
|
184
|
-
"description": "Path for sqlite backends (registration and
|
|
222
|
+
"description": "Path for sqlite backends (registration, work, and signals share this)"
|
|
223
|
+
},
|
|
224
|
+
"trigger_path": {
|
|
225
|
+
"type": "string",
|
|
226
|
+
"description": "Base path for file triggers (default: /tmp/flatmachines)"
|
|
227
|
+
},
|
|
228
|
+
"dynamodb_table": {
|
|
229
|
+
"type": "string",
|
|
230
|
+
"description": "DynamoDB table name (single-table design)"
|
|
231
|
+
},
|
|
232
|
+
"aws_region": {
|
|
233
|
+
"type": "string",
|
|
234
|
+
"description": "AWS region for DynamoDB"
|
|
185
235
|
}
|
|
186
236
|
},
|
|
187
237
|
"additionalProperties": false
|
|
@@ -230,6 +280,9 @@
|
|
|
230
280
|
"items": {
|
|
231
281
|
"$ref": "#/definitions/LaunchIntent"
|
|
232
282
|
}
|
|
283
|
+
},
|
|
284
|
+
"waiting_channel": {
|
|
285
|
+
"type": "string"
|
|
233
286
|
}
|
|
234
287
|
},
|
|
235
288
|
"required": [
|
|
@@ -269,13 +322,19 @@
|
|
|
269
322
|
},
|
|
270
323
|
"RegistrationBackend": {
|
|
271
324
|
"type": "object",
|
|
272
|
-
"additionalProperties": false
|
|
273
|
-
"description": "REGISTRATION BACKEND:\n--------------------- Worker lifecycle management for distributed execution.\n\nSDKs MUST provide: - SQLiteRegistrationBackend: For local deployments\n\nSDKs SHOULD provide: - MemoryRegistrationBackend: For testing\n\nImplementation notes: - Time units: Python reference SDK uses seconds for all interval values - Stale threshold: SDKs SHOULD default to 2× heartbeat_interval if not specified"
|
|
325
|
+
"additionalProperties": false
|
|
274
326
|
},
|
|
275
327
|
"WorkBackend": {
|
|
276
328
|
"type": "object",
|
|
277
|
-
"additionalProperties": false
|
|
278
|
-
|
|
329
|
+
"additionalProperties": false
|
|
330
|
+
},
|
|
331
|
+
"SignalBackend": {
|
|
332
|
+
"type": "object",
|
|
333
|
+
"additionalProperties": false
|
|
334
|
+
},
|
|
335
|
+
"TriggerBackend": {
|
|
336
|
+
"type": "object",
|
|
337
|
+
"additionalProperties": false
|
|
279
338
|
}
|
|
280
339
|
}
|
|
281
340
|
}
|