@hasna/loops 0.4.13 → 0.4.14
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/CHANGELOG.md +74 -3
- package/README.md +54 -12
- package/dist/api/index.d.ts +35 -0
- package/dist/api/index.js +695 -10
- package/dist/cli/index.js +1633 -366
- package/dist/cli/ui.d.ts +44 -0
- package/dist/daemon/index.js +948 -212
- package/dist/generated/storage-kit/health.d.ts +19 -0
- package/dist/generated/storage-kit/index.d.ts +7 -0
- package/dist/generated/storage-kit/migrations.d.ts +47 -0
- package/dist/generated/storage-kit/mode.d.ts +47 -0
- package/dist/generated/storage-kit/pool.d.ts +33 -0
- package/dist/generated/storage-kit/query.d.ts +35 -0
- package/dist/generated/storage-kit/tls.d.ts +25 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3567 -2010
- package/dist/lib/health.d.ts +83 -3
- package/dist/lib/mode.js +14 -2
- package/dist/lib/scheduler.d.ts +5 -2
- package/dist/lib/storage/index.d.ts +1 -0
- package/dist/lib/storage/index.js +1329 -211
- package/dist/lib/storage/pg-executor.d.ts +27 -0
- package/dist/lib/storage/pg-runner-claim.d.ts +40 -0
- package/dist/lib/storage/postgres-loop-storage.d.ts +114 -0
- package/dist/lib/storage/sqlite.js +336 -8
- package/dist/lib/store.d.ts +234 -0
- package/dist/lib/store.js +350 -8
- package/dist/mcp/index.js +1067 -306
- package/dist/runner/index.js +124 -25
- package/dist/sdk/http.d.ts +115 -0
- package/dist/sdk/http.js +145 -0
- package/dist/sdk/index.d.ts +5 -1
- package/dist/sdk/index.js +1052 -312
- package/dist/serve/index.d.ts +4 -0
- package/dist/serve/index.js +7619 -0
- package/dist/types.d.ts +3 -0
- package/docs/CUTOVER-RUNBOOK.md +61 -0
- package/docs/USAGE.md +50 -11
- package/package.json +14 -2
package/dist/lib/health.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import type { Loop, LoopRun } from "../types.js";
|
|
1
|
+
import type { Loop, LoopRun, LoopStatus } from "../types.js";
|
|
2
|
+
import type { DaemonStatus } from "../daemon/control.js";
|
|
3
|
+
import type { DoctorCheck, DoctorReport } from "./doctor.js";
|
|
2
4
|
import type { Store } from "./store.js";
|
|
3
|
-
export type RunFailureClassification = "rate_limit" | "auth" | "model_not_found" | "context_length" | "schema_response_format" | "node_init" | "preflight" | "route_functional" | "timeout" | "sigsegv" | "skipped_previous_active" | "circuit_breaker" | "unknown";
|
|
5
|
+
export type RunFailureClassification = "rate_limit" | "auth" | "provider_unavailable" | "model_not_found" | "context_length" | "schema_response_format" | "node_init" | "preflight" | "route_functional" | "timeout" | "sigsegv" | "restart_interrupted" | "skipped_previous_active" | "circuit_breaker" | "unknown";
|
|
4
6
|
export interface RunFailureSignal {
|
|
5
7
|
classification: RunFailureClassification;
|
|
6
8
|
fingerprint: string;
|
|
7
9
|
evidence: {
|
|
10
|
+
summary?: string;
|
|
8
11
|
error?: string;
|
|
9
12
|
stdout?: string;
|
|
10
13
|
stderr?: string;
|
|
@@ -31,7 +34,7 @@ export interface RecommendedTaskUpsert {
|
|
|
31
34
|
};
|
|
32
35
|
}
|
|
33
36
|
export interface LoopExpectationResult {
|
|
34
|
-
loop: Pick<Loop, "id" | "name" | "status" | "nextRunAt">;
|
|
37
|
+
loop: Pick<Loop, "id" | "name" | "status" | "nextRunAt" | "retryScheduledFor">;
|
|
35
38
|
ok: boolean;
|
|
36
39
|
check: {
|
|
37
40
|
id: "latest-run-succeeded" | "route-functional-health";
|
|
@@ -62,6 +65,81 @@ export interface LoopsHealthReport {
|
|
|
62
65
|
classifications: Record<RunFailureClassification, number>;
|
|
63
66
|
expectations: LoopExpectationResult[];
|
|
64
67
|
}
|
|
68
|
+
export type HealthScanStatus = "ok" | "degraded" | "critical";
|
|
69
|
+
export type HealthScanFindingKind = "daemon" | "doctor" | "preflight" | "latest-run" | "stale-running";
|
|
70
|
+
export type HealthScanFindingSeverity = "critical" | "high" | "medium" | "low";
|
|
71
|
+
export interface HealthScanSelfHealAction {
|
|
72
|
+
kind: "daemon-start";
|
|
73
|
+
attempted: boolean;
|
|
74
|
+
ok?: boolean;
|
|
75
|
+
reason: string;
|
|
76
|
+
result?: Record<string, unknown>;
|
|
77
|
+
}
|
|
78
|
+
export interface HealthScanFinding {
|
|
79
|
+
kind: HealthScanFindingKind;
|
|
80
|
+
severity: HealthScanFindingSeverity;
|
|
81
|
+
fingerprint: string;
|
|
82
|
+
title: string;
|
|
83
|
+
message: string;
|
|
84
|
+
loop?: Pick<Loop, "id" | "name" | "status" | "nextRunAt"> & {
|
|
85
|
+
leaseMs?: number;
|
|
86
|
+
};
|
|
87
|
+
run?: LoopRun;
|
|
88
|
+
route?: LoopExpectationResult["route"];
|
|
89
|
+
ageMs?: number;
|
|
90
|
+
staleThresholdMs?: number;
|
|
91
|
+
classification?: RunFailureClassification;
|
|
92
|
+
doctorCheck?: DoctorCheck;
|
|
93
|
+
recommendedTask?: RecommendedTaskUpsert;
|
|
94
|
+
}
|
|
95
|
+
export interface LoopsHealthScan {
|
|
96
|
+
ok: boolean;
|
|
97
|
+
status: HealthScanStatus;
|
|
98
|
+
generatedAt: string;
|
|
99
|
+
includedStatuses: LoopStatus[];
|
|
100
|
+
counts: {
|
|
101
|
+
loops: number;
|
|
102
|
+
active: number;
|
|
103
|
+
paused: number;
|
|
104
|
+
stopped: number;
|
|
105
|
+
expired: number;
|
|
106
|
+
latestRunFindings: number;
|
|
107
|
+
staleRunning: number;
|
|
108
|
+
daemonFindings: number;
|
|
109
|
+
doctorFindings: number;
|
|
110
|
+
preflightFindings: number;
|
|
111
|
+
findings: number;
|
|
112
|
+
reportedFindings: number;
|
|
113
|
+
truncatedFindings: number;
|
|
114
|
+
};
|
|
115
|
+
daemon?: Pick<DaemonStatus, "running" | "stale" | "pid" | "host" | "loops" | "runs" | "logPath">;
|
|
116
|
+
doctor?: DoctorReport;
|
|
117
|
+
health: LoopsHealthReport;
|
|
118
|
+
selfHeals: HealthScanSelfHealAction[];
|
|
119
|
+
findings: HealthScanFinding[];
|
|
120
|
+
reports?: {
|
|
121
|
+
dir: string;
|
|
122
|
+
json: string;
|
|
123
|
+
markdown: string;
|
|
124
|
+
};
|
|
125
|
+
todos?: Record<string, unknown>;
|
|
126
|
+
}
|
|
127
|
+
export interface BuildHealthScanOptions {
|
|
128
|
+
includeStatuses?: LoopStatus[];
|
|
129
|
+
includeArchived?: boolean;
|
|
130
|
+
limit?: number;
|
|
131
|
+
latestRun?: boolean;
|
|
132
|
+
doctor?: DoctorReport;
|
|
133
|
+
daemon?: DaemonStatus;
|
|
134
|
+
selfHeals?: HealthScanSelfHealAction[];
|
|
135
|
+
maxFindings?: number;
|
|
136
|
+
staleRunningMs?: number;
|
|
137
|
+
now?: Date;
|
|
138
|
+
}
|
|
139
|
+
export interface WriteHealthScanReportsOptions {
|
|
140
|
+
reportDir?: string;
|
|
141
|
+
}
|
|
142
|
+
export declare const RESTART_INTERRUPTED_RUN_PREFIX = "daemon restart interrupted active run";
|
|
65
143
|
export declare function classifyRunFailure(run: LoopRun): RunFailureSignal | undefined;
|
|
66
144
|
export declare function expectationForLoop(store: Store, loop: Loop): LoopExpectationResult;
|
|
67
145
|
export declare function buildHealthReport(store: Store, opts?: {
|
|
@@ -69,3 +147,5 @@ export declare function buildHealthReport(store: Store, opts?: {
|
|
|
69
147
|
includeInactive?: boolean;
|
|
70
148
|
limit?: number;
|
|
71
149
|
}): LoopsHealthReport;
|
|
150
|
+
export declare function buildHealthScan(store: Store, opts?: BuildHealthScanOptions): LoopsHealthScan;
|
|
151
|
+
export declare function writeHealthScanReports(scan: LoopsHealthScan, opts?: WriteHealthScanReportsOptions): LoopsHealthScan;
|
package/dist/lib/mode.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// package.json
|
|
3
3
|
var package_default = {
|
|
4
4
|
name: "@hasna/loops",
|
|
5
|
-
version: "0.4.
|
|
5
|
+
version: "0.4.14",
|
|
6
6
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
7
7
|
type: "module",
|
|
8
8
|
main: "dist/index.js",
|
|
@@ -11,6 +11,7 @@ var package_default = {
|
|
|
11
11
|
loops: "dist/cli/index.js",
|
|
12
12
|
"loops-daemon": "dist/daemon/index.js",
|
|
13
13
|
"loops-api": "dist/api/index.js",
|
|
14
|
+
"loops-serve": "dist/serve/index.js",
|
|
14
15
|
"loops-runner": "dist/runner/index.js",
|
|
15
16
|
"loops-mcp": "dist/mcp/index.js"
|
|
16
17
|
},
|
|
@@ -23,6 +24,14 @@ var package_default = {
|
|
|
23
24
|
types: "./dist/sdk/index.d.ts",
|
|
24
25
|
import: "./dist/sdk/index.js"
|
|
25
26
|
},
|
|
27
|
+
"./sdk/http": {
|
|
28
|
+
types: "./dist/sdk/http.d.ts",
|
|
29
|
+
import: "./dist/sdk/http.js"
|
|
30
|
+
},
|
|
31
|
+
"./serve": {
|
|
32
|
+
types: "./dist/serve/index.d.ts",
|
|
33
|
+
import: "./dist/serve/index.js"
|
|
34
|
+
},
|
|
26
35
|
"./mcp": {
|
|
27
36
|
types: "./dist/mcp/index.d.ts",
|
|
28
37
|
import: "./dist/mcp/index.js"
|
|
@@ -68,7 +77,7 @@ var package_default = {
|
|
|
68
77
|
"LICENSE"
|
|
69
78
|
],
|
|
70
79
|
scripts: {
|
|
71
|
-
build: "rm -rf dist && bun build src/cli/index.ts src/daemon/index.ts src/api/index.ts src/runner/index.ts src/mcp/index.ts src/index.ts src/sdk/index.ts src/lib/store.ts src/lib/mode.ts src/lib/storage/index.ts src/lib/storage/contract.ts src/lib/storage/sqlite.ts src/lib/storage/postgres.ts src/lib/storage/postgres-schema.ts --root src --outdir dist --target bun --packages external && chmod +x dist/cli/index.js dist/daemon/index.js dist/api/index.js dist/runner/index.js dist/mcp/index.js && tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist",
|
|
80
|
+
build: "rm -rf dist && bun build src/cli/index.ts src/daemon/index.ts src/api/index.ts src/serve/index.ts src/runner/index.ts src/mcp/index.ts src/index.ts src/sdk/index.ts src/sdk/http.ts src/lib/store.ts src/lib/mode.ts src/lib/storage/index.ts src/lib/storage/contract.ts src/lib/storage/sqlite.ts src/lib/storage/postgres.ts src/lib/storage/postgres-schema.ts --root src --outdir dist --target bun --packages external && chmod +x dist/cli/index.js dist/daemon/index.js dist/api/index.js dist/serve/index.js dist/runner/index.js dist/mcp/index.js && tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist",
|
|
72
81
|
"build:bin": "bun build src/cli/index.ts --compile --outfile dist/loops",
|
|
73
82
|
typecheck: "tsc --noEmit",
|
|
74
83
|
test: "bun test",
|
|
@@ -103,11 +112,13 @@ var package_default = {
|
|
|
103
112
|
bun: ">=1.0.0"
|
|
104
113
|
},
|
|
105
114
|
dependencies: {
|
|
115
|
+
"@hasna/contracts": "^0.4.1",
|
|
106
116
|
"@hasna/events": "^0.1.9",
|
|
107
117
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
108
118
|
"@openrouter/ai-sdk-provider": "2.9.1",
|
|
109
119
|
ai: "6.0.204",
|
|
110
120
|
commander: "^13.1.0",
|
|
121
|
+
pg: "^8.13.1",
|
|
111
122
|
zod: "4.4.3"
|
|
112
123
|
},
|
|
113
124
|
optionalDependencies: {
|
|
@@ -115,6 +126,7 @@ var package_default = {
|
|
|
115
126
|
},
|
|
116
127
|
devDependencies: {
|
|
117
128
|
"@types/bun": "latest",
|
|
129
|
+
"@types/pg": "^8.11.10",
|
|
118
130
|
typescript: "^5.7.3"
|
|
119
131
|
},
|
|
120
132
|
publishConfig: {
|
package/dist/lib/scheduler.d.ts
CHANGED
|
@@ -97,8 +97,8 @@ export declare const MAX_SKIPS_PER_LOOP_PER_TICK = 10;
|
|
|
97
97
|
/**
|
|
98
98
|
* Exponential retry backoff with jitter:
|
|
99
99
|
* delay = retryDelayMs * 2^(attempt-1) * (0.5 + random), capped at 6h.
|
|
100
|
-
* Failures classified as
|
|
101
|
-
* a throttled or
|
|
100
|
+
* Failures classified as provider-gated back off 4x harder, since hammering
|
|
101
|
+
* a throttled, misconfigured, or unavailable provider only makes things worse.
|
|
102
102
|
*/
|
|
103
103
|
export declare function retryBackoffDelayMs(loop: Loop, run: LoopRun, random?: () => number): number;
|
|
104
104
|
export interface AdvanceLoopOptions {
|
|
@@ -129,6 +129,9 @@ export declare function executeClaimedRun(deps: {
|
|
|
129
129
|
beforeFinalize?: (loop: Loop, run: LoopRun) => void;
|
|
130
130
|
daemonLeaseId?: string;
|
|
131
131
|
execute?: (loop: Loop, run: LoopRun) => Promise<ExecutorResult>;
|
|
132
|
+
finalizeResult?: (result: ExecutorResult, loop: Loop, run: LoopRun) => Omit<ExecutorResult, "status"> & {
|
|
133
|
+
status: LoopRun["status"];
|
|
134
|
+
};
|
|
132
135
|
onError?: (loop: Loop, error: unknown) => void;
|
|
133
136
|
}): Promise<LoopRun>;
|
|
134
137
|
export declare function claimDueRuns(deps: SchedulerDeps & {
|
|
@@ -2,5 +2,6 @@ export type { AppliedStorageMigration, AuditEventRecord, LoopStorageBackend, Loo
|
|
|
2
2
|
export { SqliteLoopStorage, createSqliteLoopStorage } from "./sqlite.js";
|
|
3
3
|
export { PostgresStorage, createPostgresStorage } from "./postgres.js";
|
|
4
4
|
export type { PostgresQueryExecutor } from "./postgres.js";
|
|
5
|
+
export { PostgresLoopStorage, createPostgresLoopStorage, NotImplementedError, } from "./postgres-loop-storage.js";
|
|
5
6
|
export { POSTGRES_MIGRATION_LEDGER_TABLE, POSTGRES_STORAGE_MIGRATIONS, checksumStorageSql, } from "./postgres-schema.js";
|
|
6
7
|
export { Store } from "../store.js";
|