@hasna/loops 0.4.13 → 0.4.22

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 (62) hide show
  1. package/CHANGELOG.md +99 -3
  2. package/README.md +171 -39
  3. package/dist/api/index.d.ts +36 -0
  4. package/dist/api/index.js +1518 -15
  5. package/dist/cli/index.js +7280 -3213
  6. package/dist/cli/ui.d.ts +44 -0
  7. package/dist/daemon/index.js +1433 -303
  8. package/dist/generated/storage-kit/health.d.ts +19 -0
  9. package/dist/generated/storage-kit/index.d.ts +7 -0
  10. package/dist/generated/storage-kit/migrations.d.ts +47 -0
  11. package/dist/generated/storage-kit/mode.d.ts +47 -0
  12. package/dist/generated/storage-kit/pool.d.ts +33 -0
  13. package/dist/generated/storage-kit/query.d.ts +35 -0
  14. package/dist/generated/storage-kit/tls.d.ts +25 -0
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +8689 -4837
  17. package/dist/lib/cloud/mode.d.ts +17 -0
  18. package/dist/lib/cloud/resolve.d.ts +16 -0
  19. package/dist/lib/cloud/storage.d.ts +82 -0
  20. package/dist/lib/cloud/transport.d.ts +148 -0
  21. package/dist/lib/format.d.ts +2 -1
  22. package/dist/lib/health.d.ts +83 -3
  23. package/dist/lib/migration.d.ts +40 -0
  24. package/dist/lib/mode.js +15 -3
  25. package/dist/lib/route/index.d.ts +2 -0
  26. package/dist/lib/route/policies.d.ts +51 -0
  27. package/dist/lib/route/provider-admission.d.ts +37 -0
  28. package/dist/lib/route/throttle.d.ts +4 -0
  29. package/dist/lib/route/types.d.ts +8 -0
  30. package/dist/lib/run-receipts.d.ts +14 -0
  31. package/dist/lib/scheduler.d.ts +5 -2
  32. package/dist/lib/storage/contract.d.ts +7 -1
  33. package/dist/lib/storage/index.d.ts +1 -0
  34. package/dist/lib/storage/index.js +2028 -231
  35. package/dist/lib/storage/pg-executor.d.ts +27 -0
  36. package/dist/lib/storage/pg-runner-claim.d.ts +40 -0
  37. package/dist/lib/storage/postgres-loop-storage.d.ts +120 -0
  38. package/dist/lib/storage/postgres-schema.js +29 -0
  39. package/dist/lib/storage/postgres.js +29 -0
  40. package/dist/lib/storage/sqlite.d.ts +6 -0
  41. package/dist/lib/storage/sqlite.js +748 -26
  42. package/dist/lib/store/index.d.ts +268 -0
  43. package/dist/lib/store.d.ts +282 -1
  44. package/dist/lib/store.js +746 -26
  45. package/dist/lib/template-kit.d.ts +25 -0
  46. package/dist/lib/templates.d.ts +16 -1
  47. package/dist/mcp/http.d.ts +16 -0
  48. package/dist/mcp/index.js +2885 -634
  49. package/dist/runner/index.js +198 -32
  50. package/dist/sdk/http.d.ts +182 -0
  51. package/dist/sdk/http.js +166 -0
  52. package/dist/sdk/index.d.ts +55 -18
  53. package/dist/sdk/index.js +2734 -617
  54. package/dist/serve/index.d.ts +4 -0
  55. package/dist/serve/index.js +9095 -0
  56. package/dist/types.d.ts +52 -0
  57. package/docs/AUTOMATION_RUNTIME_DESIGN.md +442 -1
  58. package/docs/CUTOVER-RUNBOOK.md +78 -0
  59. package/docs/DEPLOYMENT_MODES.md +35 -18
  60. package/docs/RUNTIME_BOUNDARY.md +203 -0
  61. package/docs/USAGE.md +195 -38
  62. package/package.json +15 -3
@@ -0,0 +1,268 @@
1
+ import type { CreateLoopInput, CreateWorkflowInput, Loop, LoopRun, LoopStatus, RunReceipt, RunStatus, WorkflowEvent, WorkflowInvocation, WorkflowRun, WorkflowSpec, WorkflowStepRun, WorkflowWorkItem, WorkflowWorkItemStatus, WriteRunReceiptInput } from "../../types.js";
2
+ import type { Goal, GoalPlanNode, GoalRun, GoalStatus } from "../goal/types.js";
3
+ import type { HasnaStorageClient } from "../cloud/storage.js";
4
+ import type { Env } from "../cloud/mode.js";
5
+ import { Store } from "../store.js";
6
+ import type { PruneHistoryOptions, PruneHistorySummary } from "../store.js";
7
+ /** Which transport backs a resolved store (for banners/diagnostics only). */
8
+ export type StoreTransport = "local" | "cloud-http";
9
+ /**
10
+ * Thrown when a command routed through the cloud ApiStore hits an operation that
11
+ * the hosted `/v1` API does not yet expose. Failing loudly here is deliberate:
12
+ * the alternative (silently reading/writing the on-box sqlite island while the
13
+ * machine is flipped to cloud) is exactly the split-brain bug we are removing.
14
+ */
15
+ export declare class CloudUnsupportedError extends Error {
16
+ constructor(operation: string);
17
+ }
18
+ /**
19
+ * The single data interface for the OpenLoops client. Both {@link LocalStore}
20
+ * and {@link ApiStore} implement it; callers hold a `LoopStore` and never know
21
+ * (or branch on) which one. Every method is async so the two transports share
22
+ * one surface.
23
+ */
24
+ export interface LoopStore {
25
+ readonly transport: StoreTransport;
26
+ close(): Promise<void>;
27
+ createLoop(input: CreateLoopInput, from?: Date): Promise<Loop>;
28
+ getLoop(id: string): Promise<Loop | undefined>;
29
+ findLoopByName(name: string): Promise<Loop | undefined>;
30
+ requireLoop(idOrName: string): Promise<Loop>;
31
+ requireUniqueLoop(idOrName: string): Promise<Loop>;
32
+ listLoops(opts?: {
33
+ status?: LoopStatus;
34
+ limit?: number;
35
+ offset?: number;
36
+ archived?: boolean;
37
+ includeArchived?: boolean;
38
+ name?: string;
39
+ }): Promise<Loop[]>;
40
+ countLoops(status?: LoopStatus, opts?: {
41
+ archived?: boolean;
42
+ includeArchived?: boolean;
43
+ }): Promise<number>;
44
+ updateLoop(id: string, patch: Partial<Pick<Loop, "status" | "nextRunAt" | "retryScheduledFor" | "expiresAt">>): Promise<Loop>;
45
+ renameLoop(id: string, name: string): Promise<Loop>;
46
+ archiveLoop(idOrName: string): Promise<Loop>;
47
+ unarchiveLoop(idOrName: string): Promise<Loop>;
48
+ deleteLoop(idOrName: string): Promise<boolean>;
49
+ createWorkflow(input: CreateWorkflowInput): Promise<WorkflowSpec>;
50
+ getWorkflow(id: string): Promise<WorkflowSpec | undefined>;
51
+ findWorkflowByName(name: string): Promise<WorkflowSpec | undefined>;
52
+ requireWorkflow(idOrName: string): Promise<WorkflowSpec>;
53
+ listWorkflows(opts?: {
54
+ status?: WorkflowSpec["status"];
55
+ limit?: number;
56
+ offset?: number;
57
+ }): Promise<WorkflowSpec[]>;
58
+ countWorkflows(opts?: {
59
+ status?: WorkflowSpec["status"];
60
+ }): Promise<number>;
61
+ archiveWorkflow(idOrName: string): Promise<WorkflowSpec>;
62
+ getWorkflowRun(id: string): Promise<WorkflowRun | undefined>;
63
+ requireWorkflowRun(id: string): Promise<WorkflowRun>;
64
+ listWorkflowRuns(opts?: {
65
+ workflowId?: string;
66
+ loopRunId?: string;
67
+ limit?: number;
68
+ }): Promise<WorkflowRun[]>;
69
+ listWorkflowStepRuns(workflowRunId: string): Promise<WorkflowStepRun[]>;
70
+ listWorkflowEvents(workflowRunId: string, limit?: number): Promise<WorkflowEvent[]>;
71
+ recoverWorkflowRun(workflowRunId: string, reason?: string): Promise<{
72
+ run: WorkflowRun;
73
+ recoveredSteps: WorkflowStepRun[];
74
+ }>;
75
+ cancelWorkflowRun(workflowRunId: string, reason?: string): Promise<WorkflowRun>;
76
+ listWorkflowWorkItems(opts?: {
77
+ status?: WorkflowWorkItemStatus;
78
+ routeKey?: string;
79
+ limit?: number;
80
+ }): Promise<WorkflowWorkItem[]>;
81
+ getWorkflowWorkItem(id: string): Promise<WorkflowWorkItem | undefined>;
82
+ requeueWorkflowWorkItem(id: string, patch?: {
83
+ reason?: string;
84
+ }): Promise<WorkflowWorkItem>;
85
+ listWorkflowInvocations(opts?: {
86
+ limit?: number;
87
+ }): Promise<WorkflowInvocation[]>;
88
+ getWorkflowInvocation(id: string): Promise<WorkflowInvocation | undefined>;
89
+ getGoal(id: string): Promise<Goal | undefined>;
90
+ findGoalByLoop(idOrName: string): Promise<Goal | undefined>;
91
+ findGoalByRunId(id: string): Promise<Goal | undefined>;
92
+ listGoals(opts?: {
93
+ status?: GoalStatus;
94
+ limit?: number;
95
+ }): Promise<Goal[]>;
96
+ listGoalPlanNodes(goalIdOrPlanId: string): Promise<GoalPlanNode[]>;
97
+ listGoalRuns(opts?: {
98
+ goalId?: string;
99
+ runId?: string;
100
+ limit?: number;
101
+ }): Promise<GoalRun[]>;
102
+ listRuns(opts?: {
103
+ loopId?: string;
104
+ status?: RunStatus;
105
+ limit?: number;
106
+ offset?: number;
107
+ }): Promise<LoopRun[]>;
108
+ getRun(id: string): Promise<LoopRun | undefined>;
109
+ writeRunReceipt(input: WriteRunReceiptInput): Promise<RunReceipt>;
110
+ getRunReceipt(runId: string): Promise<RunReceipt | undefined>;
111
+ listRunReceipts(opts?: {
112
+ loopId?: string;
113
+ repo?: string;
114
+ taskId?: string;
115
+ knowledgeId?: string;
116
+ status?: string;
117
+ limit?: number;
118
+ }): Promise<RunReceipt[]>;
119
+ pruneHistory(opts: PruneHistoryOptions): Promise<PruneHistorySummary>;
120
+ }
121
+ /**
122
+ * On-box SQLite transport. Wraps a single {@link Store} and awaits its
123
+ * synchronous methods so callers see the async {@link LoopStore} surface.
124
+ */
125
+ export declare class LocalStore implements LoopStore {
126
+ readonly transport: "local";
127
+ private readonly store;
128
+ constructor(store?: Store);
129
+ /**
130
+ * The underlying on-box sqlite {@link Store}. Exposed ONLY for genuinely
131
+ * local-runtime operations that cannot route over HTTP — the scheduler
132
+ * (tick/run-now inline execution), migration import/export, and local
133
+ * diagnostics (doctor/health/daemon status). Callers must gate on
134
+ * `transport === "local"` first; the hosted ApiStore has no `raw` store, so
135
+ * these operations fail loudly in cloud mode instead of touching an island.
136
+ */
137
+ get raw(): Store;
138
+ close(): Promise<void>;
139
+ createLoop(input: CreateLoopInput, from?: Date): Promise<Loop>;
140
+ getLoop(id: string): Promise<Loop | undefined>;
141
+ findLoopByName(name: string): Promise<Loop | undefined>;
142
+ requireLoop(idOrName: string): Promise<Loop>;
143
+ requireUniqueLoop(idOrName: string): Promise<Loop>;
144
+ listLoops(opts?: Parameters<Store["listLoops"]>[0]): Promise<Loop[]>;
145
+ countLoops(status?: LoopStatus, opts?: {
146
+ archived?: boolean;
147
+ includeArchived?: boolean;
148
+ }): Promise<number>;
149
+ updateLoop(id: string, patch: Partial<Pick<Loop, "status" | "nextRunAt" | "retryScheduledFor" | "expiresAt">>): Promise<Loop>;
150
+ renameLoop(id: string, name: string): Promise<Loop>;
151
+ archiveLoop(idOrName: string): Promise<Loop>;
152
+ unarchiveLoop(idOrName: string): Promise<Loop>;
153
+ deleteLoop(idOrName: string): Promise<boolean>;
154
+ createWorkflow(input: CreateWorkflowInput): Promise<WorkflowSpec>;
155
+ getWorkflow(id: string): Promise<WorkflowSpec | undefined>;
156
+ findWorkflowByName(name: string): Promise<WorkflowSpec | undefined>;
157
+ requireWorkflow(idOrName: string): Promise<WorkflowSpec>;
158
+ listWorkflows(opts?: Parameters<Store["listWorkflows"]>[0]): Promise<WorkflowSpec[]>;
159
+ countWorkflows(opts?: {
160
+ status?: WorkflowSpec["status"];
161
+ }): Promise<number>;
162
+ archiveWorkflow(idOrName: string): Promise<WorkflowSpec>;
163
+ getWorkflowRun(id: string): Promise<WorkflowRun | undefined>;
164
+ requireWorkflowRun(id: string): Promise<WorkflowRun>;
165
+ listWorkflowRuns(opts?: Parameters<Store["listWorkflowRuns"]>[0]): Promise<WorkflowRun[]>;
166
+ listWorkflowStepRuns(workflowRunId: string): Promise<WorkflowStepRun[]>;
167
+ listWorkflowEvents(workflowRunId: string, limit?: number): Promise<WorkflowEvent[]>;
168
+ recoverWorkflowRun(workflowRunId: string, reason?: string): Promise<{
169
+ run: WorkflowRun;
170
+ recoveredSteps: WorkflowStepRun[];
171
+ }>;
172
+ cancelWorkflowRun(workflowRunId: string, reason?: string): Promise<WorkflowRun>;
173
+ listWorkflowWorkItems(opts?: Parameters<Store["listWorkflowWorkItems"]>[0]): Promise<WorkflowWorkItem[]>;
174
+ getWorkflowWorkItem(id: string): Promise<WorkflowWorkItem | undefined>;
175
+ requeueWorkflowWorkItem(id: string, patch?: {
176
+ reason?: string;
177
+ }): Promise<WorkflowWorkItem>;
178
+ listWorkflowInvocations(opts?: Parameters<Store["listWorkflowInvocations"]>[0]): Promise<WorkflowInvocation[]>;
179
+ getWorkflowInvocation(id: string): Promise<WorkflowInvocation | undefined>;
180
+ getGoal(id: string): Promise<Goal | undefined>;
181
+ findGoalByLoop(idOrName: string): Promise<Goal | undefined>;
182
+ findGoalByRunId(id: string): Promise<Goal | undefined>;
183
+ listGoals(opts?: Parameters<Store["listGoals"]>[0]): Promise<Goal[]>;
184
+ listGoalPlanNodes(goalIdOrPlanId: string): Promise<GoalPlanNode[]>;
185
+ listGoalRuns(opts?: Parameters<Store["listGoalRuns"]>[0]): Promise<GoalRun[]>;
186
+ listRuns(opts?: Parameters<Store["listRuns"]>[0]): Promise<LoopRun[]>;
187
+ getRun(id: string): Promise<LoopRun | undefined>;
188
+ writeRunReceipt(input: WriteRunReceiptInput): Promise<RunReceipt>;
189
+ getRunReceipt(runId: string): Promise<RunReceipt | undefined>;
190
+ listRunReceipts(opts?: Parameters<Store["listRunReceipts"]>[0]): Promise<RunReceipt[]>;
191
+ pruneHistory(opts: PruneHistoryOptions): Promise<PruneHistorySummary>;
192
+ }
193
+ /**
194
+ * Self_hosted / cloud transport: routes every read+write through the hosted
195
+ * `/v1` API. Loop/name resolution (requireLoop/requireUniqueLoop/find*) is done
196
+ * client-side by listing and matching, mirroring the local Store behaviour, so a
197
+ * server that ignores a name filter can never return the wrong row.
198
+ */
199
+ export declare class ApiStore implements LoopStore {
200
+ private readonly client;
201
+ readonly baseUrl: string;
202
+ readonly transport: "cloud-http";
203
+ constructor(client: HasnaStorageClient, baseUrl: string);
204
+ private get t();
205
+ close(): Promise<void>;
206
+ createLoop(input: CreateLoopInput): Promise<Loop>;
207
+ getLoop(id: string): Promise<Loop | undefined>;
208
+ findLoopByName(name: string): Promise<Loop | undefined>;
209
+ requireLoop(idOrName: string): Promise<Loop>;
210
+ requireUniqueLoop(idOrName: string): Promise<Loop>;
211
+ private resolveLoop;
212
+ listLoops(opts?: Parameters<LoopStore["listLoops"]>[0]): Promise<Loop[]>;
213
+ countLoops(status?: LoopStatus, opts?: {
214
+ archived?: boolean;
215
+ includeArchived?: boolean;
216
+ }): Promise<number>;
217
+ updateLoop(id: string, patch: Partial<Pick<Loop, "status" | "nextRunAt" | "retryScheduledFor" | "expiresAt">>): Promise<Loop>;
218
+ renameLoop(id: string, name: string): Promise<Loop>;
219
+ archiveLoop(idOrName: string): Promise<Loop>;
220
+ unarchiveLoop(idOrName: string): Promise<Loop>;
221
+ deleteLoop(idOrName: string): Promise<boolean>;
222
+ createWorkflow(input: CreateWorkflowInput): Promise<WorkflowSpec>;
223
+ getWorkflow(id: string): Promise<WorkflowSpec | undefined>;
224
+ findWorkflowByName(name: string): Promise<WorkflowSpec | undefined>;
225
+ requireWorkflow(idOrName: string): Promise<WorkflowSpec>;
226
+ listWorkflows(opts?: Parameters<LoopStore["listWorkflows"]>[0]): Promise<WorkflowSpec[]>;
227
+ countWorkflows(opts?: {
228
+ status?: WorkflowSpec["status"];
229
+ }): Promise<number>;
230
+ archiveWorkflow(idOrName: string): Promise<WorkflowSpec>;
231
+ getWorkflowRun(id: string): Promise<WorkflowRun | undefined>;
232
+ requireWorkflowRun(id: string): Promise<WorkflowRun>;
233
+ listWorkflowRuns(opts?: Parameters<LoopStore["listWorkflowRuns"]>[0]): Promise<WorkflowRun[]>;
234
+ listWorkflowStepRuns(workflowRunId: string): Promise<WorkflowStepRun[]>;
235
+ listWorkflowEvents(workflowRunId: string, limit?: number): Promise<WorkflowEvent[]>;
236
+ recoverWorkflowRun(workflowRunId: string, reason?: string): Promise<{
237
+ run: WorkflowRun;
238
+ recoveredSteps: WorkflowStepRun[];
239
+ }>;
240
+ cancelWorkflowRun(_workflowRunId: string, _reason?: string): Promise<WorkflowRun>;
241
+ listWorkflowWorkItems(opts?: Parameters<LoopStore["listWorkflowWorkItems"]>[0]): Promise<WorkflowWorkItem[]>;
242
+ getWorkflowWorkItem(id: string): Promise<WorkflowWorkItem | undefined>;
243
+ requeueWorkflowWorkItem(_id: string, _patch?: {
244
+ reason?: string;
245
+ }): Promise<WorkflowWorkItem>;
246
+ listWorkflowInvocations(opts?: Parameters<LoopStore["listWorkflowInvocations"]>[0]): Promise<WorkflowInvocation[]>;
247
+ getWorkflowInvocation(id: string): Promise<WorkflowInvocation | undefined>;
248
+ getGoal(id: string): Promise<Goal | undefined>;
249
+ findGoalByLoop(idOrName: string): Promise<Goal | undefined>;
250
+ findGoalByRunId(id: string): Promise<Goal | undefined>;
251
+ listGoals(opts?: Parameters<LoopStore["listGoals"]>[0]): Promise<Goal[]>;
252
+ listGoalPlanNodes(goalIdOrPlanId: string): Promise<GoalPlanNode[]>;
253
+ listGoalRuns(opts?: Parameters<LoopStore["listGoalRuns"]>[0]): Promise<GoalRun[]>;
254
+ listRuns(opts?: Parameters<LoopStore["listRuns"]>[0]): Promise<LoopRun[]>;
255
+ getRun(id: string): Promise<LoopRun | undefined>;
256
+ writeRunReceipt(input: WriteRunReceiptInput): Promise<RunReceipt>;
257
+ getRunReceipt(runId: string): Promise<RunReceipt | undefined>;
258
+ listRunReceipts(opts?: Parameters<LoopStore["listRunReceipts"]>[0]): Promise<RunReceipt[]>;
259
+ pruneHistory(opts: PruneHistoryOptions): Promise<PruneHistorySummary>;
260
+ }
261
+ /**
262
+ * Resolve the client store for the current environment: an {@link ApiStore} when
263
+ * the client-flip contract resolves to cloud-http (self_hosted/cloud), else a
264
+ * {@link LocalStore}. Callers hold a {@link LoopStore} and never branch on mode.
265
+ */
266
+ export declare function getStore(env?: Env): LoopStore;
267
+ /** True when the resolved store is the hosted HTTP transport (cloud/self_hosted). */
268
+ export declare function isCloudStore(env?: Env): boolean;
@@ -1,9 +1,232 @@
1
- import type { CreateLoopInput, CreateWorkflowInvocationInput, CreateWorkflowInput, Goal, GoalAutoExecute, GoalPlanNode, GoalRun, GoalStatus, Loop, LoopRun, LoopStatus, RunStatus, TimeoutMs, WorkflowEvent, WorkflowInvocation, WorkflowRun, WorkflowRunStatus, WorkflowSpec, WorkflowStepRun, WorkflowWorkItem, WorkflowWorkItemStatus, UpsertWorkflowWorkItemInput } from "../types.js";
1
+ import type { CreateLoopInput, CreateWorkflowInvocationInput, CreateWorkflowInput, Goal, GoalAutoExecute, GoalPlanNode, GoalRun, GoalStatus, Loop, LoopRun, LoopStatus, RunReceipt, RunStatus, TimeoutMs, WriteRunReceiptInput, WorkflowEvent, WorkflowInvocation, WorkflowRun, WorkflowRunStatus, WorkflowSpec, WorkflowStepRun, WorkflowWorkItem, WorkflowWorkItemStatus, UpsertWorkflowWorkItemInput } from "../types.js";
2
2
  interface DaemonLeaseFence {
3
3
  daemonLeaseId?: string;
4
4
  now?: Date;
5
5
  claimToken?: string;
6
6
  }
7
+ export interface LoopRow {
8
+ id: string;
9
+ name: string;
10
+ description: string | null;
11
+ status: string;
12
+ archived_at: string | null;
13
+ archived_from_status: string | null;
14
+ schedule_json: string;
15
+ target_json: string;
16
+ goal_json: string | null;
17
+ machine_json: string | null;
18
+ next_run_at: string | null;
19
+ retry_scheduled_for: string | null;
20
+ catch_up: string;
21
+ catch_up_limit: number;
22
+ overlap: string;
23
+ max_attempts: number;
24
+ retry_delay_ms: number;
25
+ lease_ms: number;
26
+ expires_at: string | null;
27
+ created_at: string;
28
+ updated_at: string;
29
+ }
30
+ export interface RunRow {
31
+ id: string;
32
+ loop_id: string;
33
+ loop_name: string;
34
+ scheduled_for: string;
35
+ attempt: number;
36
+ status: string;
37
+ started_at: string | null;
38
+ finished_at: string | null;
39
+ claimed_by: string | null;
40
+ claim_token: string | null;
41
+ lease_expires_at: string | null;
42
+ pid: number | null;
43
+ pgid: number | null;
44
+ process_started_at: string | null;
45
+ exit_code: number | null;
46
+ duration_ms: number | null;
47
+ stdout: string | null;
48
+ stderr: string | null;
49
+ error: string | null;
50
+ goal_run_id: string | null;
51
+ created_at: string;
52
+ updated_at: string;
53
+ }
54
+ export interface RunReceiptRow {
55
+ loop_id: string;
56
+ run_id: string;
57
+ machine_json: string;
58
+ repo: string;
59
+ task_ids_json: string;
60
+ knowledge_ids_json: string;
61
+ digest_id: string;
62
+ started_at: string | null;
63
+ finished_at: string | null;
64
+ status: string;
65
+ exit_code: number | null;
66
+ summary_json: string;
67
+ evidence_paths_json: string;
68
+ created_at: string;
69
+ updated_at: string;
70
+ }
71
+ export interface WorkflowRow {
72
+ id: string;
73
+ name: string;
74
+ description: string | null;
75
+ version: number;
76
+ status: string;
77
+ goal_json: string | null;
78
+ steps_json: string;
79
+ created_at: string;
80
+ updated_at: string;
81
+ }
82
+ export interface WorkflowRunRow {
83
+ id: string;
84
+ workflow_id: string;
85
+ workflow_name: string;
86
+ loop_id: string | null;
87
+ loop_run_id: string | null;
88
+ invocation_id: string | null;
89
+ work_item_id: string | null;
90
+ scheduled_for: string | null;
91
+ idempotency_key: string | null;
92
+ manifest_path: string | null;
93
+ status: string;
94
+ started_at: string | null;
95
+ finished_at: string | null;
96
+ duration_ms: number | null;
97
+ error: string | null;
98
+ goal_run_id: string | null;
99
+ created_at: string;
100
+ updated_at: string;
101
+ }
102
+ export interface WorkflowInvocationRow {
103
+ id: string;
104
+ workflow_id: string | null;
105
+ template_id: string | null;
106
+ source_kind: string;
107
+ source_id: string | null;
108
+ source_dedupe_key: string | null;
109
+ source_json: string;
110
+ subject_kind: string;
111
+ subject_id: string | null;
112
+ subject_path: string | null;
113
+ subject_url: string | null;
114
+ subject_json: string;
115
+ intent: string;
116
+ scope_json: string | null;
117
+ output_policy_json: string | null;
118
+ created_at: string;
119
+ updated_at: string;
120
+ }
121
+ export interface WorkflowWorkItemRow {
122
+ id: string;
123
+ route_key: string;
124
+ idempotency_key: string;
125
+ invocation_id: string;
126
+ source_type: string;
127
+ source_ref: string;
128
+ subject_ref: string;
129
+ project_key: string | null;
130
+ project_group: string | null;
131
+ machine_id: string | null;
132
+ route_scope: string | null;
133
+ priority: number;
134
+ status: string;
135
+ attempts: number;
136
+ next_attempt_at: string | null;
137
+ lease_expires_at: string | null;
138
+ workflow_id: string | null;
139
+ loop_id: string | null;
140
+ workflow_run_id: string | null;
141
+ last_reason: string | null;
142
+ created_at: string;
143
+ updated_at: string;
144
+ }
145
+ export interface WorkflowStepRunRow {
146
+ id: string;
147
+ workflow_run_id: string;
148
+ step_id: string;
149
+ sequence: number;
150
+ status: string;
151
+ started_at: string | null;
152
+ finished_at: string | null;
153
+ exit_code: number | null;
154
+ pid: number | null;
155
+ duration_ms: number | null;
156
+ stdout: string | null;
157
+ stderr: string | null;
158
+ error: string | null;
159
+ account_profile: string | null;
160
+ account_tool: string | null;
161
+ goal_run_id: string | null;
162
+ created_at: string;
163
+ updated_at: string;
164
+ }
165
+ export interface WorkflowEventRow {
166
+ id: string;
167
+ workflow_run_id: string;
168
+ sequence: number;
169
+ event_type: string;
170
+ step_id: string | null;
171
+ payload_json: string | null;
172
+ created_at: string;
173
+ }
174
+ export interface GoalRow {
175
+ id: string;
176
+ plan_id: string;
177
+ objective: string;
178
+ status: string;
179
+ token_budget: number | null;
180
+ tokens_used: number;
181
+ time_used_seconds: number;
182
+ auto_execute: string;
183
+ max_tokens: number | null;
184
+ source_type: string | null;
185
+ source_id: string | null;
186
+ loop_id: string | null;
187
+ loop_run_id: string | null;
188
+ workflow_id: string | null;
189
+ workflow_run_id: string | null;
190
+ workflow_step_id: string | null;
191
+ created_at: string;
192
+ updated_at: string;
193
+ }
194
+ export interface GoalPlanNodeRow {
195
+ id: string;
196
+ goal_id: string;
197
+ plan_id: string;
198
+ key: string;
199
+ sequence: number;
200
+ priority: number;
201
+ objective: string;
202
+ status: string;
203
+ ready: number;
204
+ token_budget: number | null;
205
+ tokens_used: number;
206
+ time_used_seconds: number;
207
+ depends_on_json: string;
208
+ created_at: string;
209
+ updated_at: string;
210
+ }
211
+ export interface GoalRunRow {
212
+ id: string;
213
+ goal_id: string;
214
+ plan_id: string;
215
+ loop_id: string | null;
216
+ loop_run_id: string | null;
217
+ workflow_id: string | null;
218
+ workflow_run_id: string | null;
219
+ workflow_step_id: string | null;
220
+ turn: number;
221
+ phase: string;
222
+ status: string;
223
+ node_key: string | null;
224
+ tokens_used: number;
225
+ evidence_json: string | null;
226
+ raw_response_json: string | null;
227
+ created_at: string;
228
+ updated_at: string;
229
+ }
7
230
  export interface DaemonLease {
8
231
  id: string;
9
232
  pid: number;
@@ -13,6 +236,28 @@ export interface DaemonLease {
13
236
  createdAt: string;
14
237
  updatedAt: string;
15
238
  }
239
+ export interface LeaseRow {
240
+ id: string;
241
+ pid: number;
242
+ hostname: string;
243
+ heartbeat_at: string;
244
+ expires_at: string;
245
+ created_at: string;
246
+ updated_at: string;
247
+ }
248
+ export declare function rowToLoop(row: LoopRow): Loop;
249
+ export declare function rowToRun(row: RunRow): LoopRun;
250
+ export declare function rowToRunReceipt(row: RunReceiptRow): RunReceipt;
251
+ export declare function rowToWorkflow(row: WorkflowRow): WorkflowSpec;
252
+ export declare function rowToWorkflowRun(row: WorkflowRunRow): WorkflowRun;
253
+ export declare function rowToWorkflowInvocation(row: WorkflowInvocationRow): WorkflowInvocation;
254
+ export declare function rowToWorkflowWorkItem(row: WorkflowWorkItemRow): WorkflowWorkItem;
255
+ export declare function rowToWorkflowStepRun(row: WorkflowStepRunRow): WorkflowStepRun;
256
+ export declare function rowToGoal(row: GoalRow): Goal;
257
+ export declare function rowToGoalPlanNode(row: GoalPlanNodeRow): GoalPlanNode;
258
+ export declare function rowToGoalRun(row: GoalRunRow): GoalRun;
259
+ export declare function rowToWorkflowEvent(row: WorkflowEventRow): WorkflowEvent;
260
+ export declare function rowToLease(row: LeaseRow): DaemonLease;
16
261
  export interface ClaimRunResult {
17
262
  run: LoopRun;
18
263
  loop: Loop;
@@ -122,6 +367,10 @@ export interface RecordGoalEventInput {
122
367
  evidence?: Record<string, unknown>;
123
368
  rawResponse?: unknown;
124
369
  }
370
+ export declare function workItemStatusForLoopRun(status: RunStatus, attempt: number, maxAttempts: number | undefined): WorkflowWorkItemStatus | undefined;
371
+ export declare function scrubbedOrNull(value: string | undefined | null): string | null;
372
+ /** Scrub secrets then bound size before persisting run stdout/stderr. */
373
+ export declare function persistedRunOutput(value: string | undefined | null): string | null;
125
374
  export declare class Store {
126
375
  private db;
127
376
  private rootDir;
@@ -131,6 +380,7 @@ export declare class Store {
131
380
  private migrate;
132
381
  private migrations;
133
382
  private createBaseSchema;
383
+ private createRunReceiptsSchema;
134
384
  /**
135
385
  * Add a column only if it does not already exist. Idempotent — avoids the
136
386
  * "duplicate column name" error that SQLite logs (via libsqlite3, before any
@@ -152,9 +402,12 @@ export declare class Store {
152
402
  listLoops(opts?: {
153
403
  status?: LoopStatus;
154
404
  limit?: number;
405
+ offset?: number;
155
406
  archived?: boolean;
156
407
  includeArchived?: boolean;
408
+ name?: string;
157
409
  }): Loop[];
410
+ private withLatestRunSummaries;
158
411
  dueLoops(now: Date, limit?: number): Loop[];
159
412
  updateLoop(id: string, patch: Partial<Pick<Loop, "status" | "nextRunAt" | "retryScheduledFor" | "expiresAt">>, opts?: DaemonLeaseFence): Loop;
160
413
  private activeLoopReferenceCount;
@@ -162,6 +415,7 @@ export declare class Store {
162
415
  retargetWorkflowLoop(idOrName: string, workflowId: string, opts?: DaemonLeaseFence & {
163
416
  workflowTimeoutMs?: TimeoutMs;
164
417
  }): Loop;
418
+ updateAgentLoopTimeout(idOrName: string, timeoutMs: TimeoutMs, opts?: DaemonLeaseFence): Loop;
165
419
  createAndRetargetWorkflowLoop(idOrName: string, workflowInput: CreateWorkflowInput, opts?: DaemonLeaseFence & {
166
420
  workflowTimeoutMs?: TimeoutMs;
167
421
  archiveOld?: boolean;
@@ -201,6 +455,8 @@ export declare class Store {
201
455
  private generatedRouteArchiveContext;
202
456
  private maybeArchiveGeneratedRouteWorkflow;
203
457
  private maybeArchiveTerminalGeneratedRouteWorkflow;
458
+ private taskLifecycleTodosPointerContext;
459
+ private syncSuccessfulTaskLifecycleTodosPointers;
204
460
  createWorkflowInvocation(input: CreateWorkflowInvocationInput): WorkflowInvocation;
205
461
  refreshWorkflowInvocationForWorkItem(workItemId: string, input: CreateWorkflowInvocationInput): WorkflowInvocation;
206
462
  getWorkflowInvocation(id: string): WorkflowInvocation | undefined;
@@ -309,6 +565,7 @@ export declare class Store {
309
565
  listWorkflowEvents(workflowRunId: string, limit?: number): WorkflowEvent[];
310
566
  hasRunningRun(loopId: string): boolean;
311
567
  hasRunningRunForSlot(loopId: string, scheduledFor: string): boolean;
568
+ private hasBlockingRunningRunForOtherSlot;
312
569
  markRunPid(id: string, pid: number, claimedBy?: string, opts?: DaemonLeaseFence): LoopRun | undefined;
313
570
  /**
314
571
  * Record the spawned child's process identity (pid, process group id, start
@@ -332,7 +589,20 @@ export declare class Store {
332
589
  loopId?: string;
333
590
  status?: RunStatus;
334
591
  limit?: number;
592
+ offset?: number;
335
593
  }): LoopRun[];
594
+ writeRunReceipt(input: WriteRunReceiptInput, opts?: {
595
+ now?: Date;
596
+ }): RunReceipt;
597
+ getRunReceipt(runId: string): RunReceipt | undefined;
598
+ listRunReceipts(opts?: {
599
+ loopId?: string;
600
+ repo?: string;
601
+ taskId?: string;
602
+ knowledgeId?: string;
603
+ status?: string;
604
+ limit?: number;
605
+ }): RunReceipt[];
336
606
  private deferLiveExpiredRun;
337
607
  recoverExpiredRunLeases(now?: Date, opts?: DaemonLeaseFence & {
338
608
  limit?: number;
@@ -355,6 +625,17 @@ export declare class Store {
355
625
  }): number;
356
626
  countRuns(status?: RunStatus): number;
357
627
  exportMigrationRows(opts?: StoreMigrationRowsOptions): StoreMigrationRows;
628
+ /**
629
+ * Page through loop_runs for a streaming export. A full `exportMigrationRows`
630
+ * loads every run's stdout/stderr into memory at once (hundreds of MB on a
631
+ * busy host); a self-hosted backfill instead pulls stable ordered pages so
632
+ * peak memory stays bounded. Order is deterministic (created_at, id) so
633
+ * offset paging over an immutable snapshot never skips or repeats a row.
634
+ */
635
+ exportMigrationRunPage(opts: {
636
+ limit: number;
637
+ offset: number;
638
+ }): LoopRun[];
358
639
  private countTable;
359
640
  private migrationChecks;
360
641
  upsertMigrationWorkflow(workflow: WorkflowSpec, opts?: StoreMigrationUpsertOptions): WorkflowSpec;