agent-working-memory 0.7.16 → 0.8.5
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/README.md +156 -15
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +2 -16
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adapters/codex.d.ts.map +1 -1
- package/dist/adapters/codex.js +2 -11
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/common.d.ts +18 -0
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +254 -141
- package/dist/adapters/common.js.map +1 -1
- package/dist/adapters/cursor.d.ts.map +1 -1
- package/dist/adapters/cursor.js +2 -15
- package/dist/adapters/cursor.js.map +1 -1
- package/dist/adapters/http.d.ts.map +1 -1
- package/dist/adapters/http.js +28 -34
- package/dist/adapters/http.js.map +1 -1
- package/dist/api/routes.d.ts +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +280 -113
- package/dist/api/routes.js.map +1 -1
- package/dist/cli/migrate.d.ts +42 -0
- package/dist/cli/migrate.d.ts.map +1 -0
- package/dist/cli/migrate.js +268 -0
- package/dist/cli/migrate.js.map +1 -0
- package/dist/cli.js +45 -1
- package/dist/cli.js.map +1 -1
- package/dist/coordination/circuit-breaker.d.ts +24 -0
- package/dist/coordination/circuit-breaker.d.ts.map +1 -0
- package/dist/coordination/circuit-breaker.js +70 -0
- package/dist/coordination/circuit-breaker.js.map +1 -0
- package/dist/coordination/failure-modes.d.ts +21 -0
- package/dist/coordination/failure-modes.d.ts.map +1 -0
- package/dist/coordination/failure-modes.js +49 -0
- package/dist/coordination/failure-modes.js.map +1 -0
- package/dist/coordination/peer-decisions.js +7 -7
- package/dist/coordination/routes.d.ts.map +1 -1
- package/dist/coordination/routes.js +44 -1
- package/dist/coordination/routes.js.map +1 -1
- package/dist/coordination/schema.d.ts.map +1 -1
- package/dist/coordination/schema.js +18 -0
- package/dist/coordination/schema.js.map +1 -1
- package/dist/coordination/schemas.d.ts +9 -9
- package/dist/coordination/stale.d.ts +9 -1
- package/dist/coordination/stale.d.ts.map +1 -1
- package/dist/coordination/stale.js +53 -9
- package/dist/coordination/stale.js.map +1 -1
- package/dist/core/embeddings.d.ts +16 -10
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +54 -38
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/ml-worker-entry.d.ts +17 -0
- package/dist/core/ml-worker-entry.d.ts.map +1 -0
- package/dist/core/ml-worker-entry.js +193 -0
- package/dist/core/ml-worker-entry.js.map +1 -0
- package/dist/core/ml-worker.d.ts +59 -0
- package/dist/core/ml-worker.d.ts.map +1 -0
- package/dist/core/ml-worker.js +253 -0
- package/dist/core/ml-worker.js.map +1 -0
- package/dist/core/query-expander.d.ts +13 -15
- package/dist/core/query-expander.d.ts.map +1 -1
- package/dist/core/query-expander.js +48 -54
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/reranker.d.ts +9 -19
- package/dist/core/reranker.d.ts.map +1 -1
- package/dist/core/reranker.js +33 -35
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/salience.d.ts +32 -5
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +201 -28
- package/dist/core/salience.js.map +1 -1
- package/dist/core/write-pipeline.d.ts +128 -0
- package/dist/core/write-pipeline.d.ts.map +1 -0
- package/dist/core/write-pipeline.js +405 -0
- package/dist/core/write-pipeline.js.map +1 -0
- package/dist/engine/activation.d.ts +2 -2
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +238 -115
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/confidence.d.ts +62 -0
- package/dist/engine/confidence.d.ts.map +1 -0
- package/dist/engine/confidence.js +100 -0
- package/dist/engine/confidence.js.map +1 -0
- package/dist/engine/connections.d.ts +47 -8
- package/dist/engine/connections.d.ts.map +1 -1
- package/dist/engine/connections.js +83 -22
- package/dist/engine/connections.js.map +1 -1
- package/dist/engine/consolidation-scheduler.d.ts +46 -10
- package/dist/engine/consolidation-scheduler.d.ts.map +1 -1
- package/dist/engine/consolidation-scheduler.js +170 -50
- package/dist/engine/consolidation-scheduler.js.map +1 -1
- package/dist/engine/consolidation.d.ts +16 -2
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +143 -44
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/engine/eval.d.ts +2 -2
- package/dist/engine/eval.d.ts.map +1 -1
- package/dist/engine/eval.js +9 -9
- package/dist/engine/eval.js.map +1 -1
- package/dist/engine/eviction.d.ts +4 -4
- package/dist/engine/eviction.d.ts.map +1 -1
- package/dist/engine/eviction.js +15 -15
- package/dist/engine/eviction.js.map +1 -1
- package/dist/engine/retraction.d.ts +93 -5
- package/dist/engine/retraction.d.ts.map +1 -1
- package/dist/engine/retraction.js +238 -19
- package/dist/engine/retraction.js.map +1 -1
- package/dist/engine/staging.d.ts +1 -1
- package/dist/engine/staging.d.ts.map +1 -1
- package/dist/engine/staging.js +3 -3
- package/dist/engine/staging.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +1 -1
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +4 -4
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/index.js +48 -31
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +110 -155
- package/dist/mcp.js.map +1 -1
- package/dist/storage/factory.d.ts +55 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/factory.js +135 -0
- package/dist/storage/factory.js.map +1 -0
- package/dist/storage/pglite-schema.d.ts +20 -0
- package/dist/storage/pglite-schema.d.ts.map +1 -0
- package/dist/storage/pglite-schema.js +165 -0
- package/dist/storage/pglite-schema.js.map +1 -0
- package/dist/storage/pglite.d.ts +187 -0
- package/dist/storage/pglite.d.ts.map +1 -0
- package/dist/storage/pglite.js +1114 -0
- package/dist/storage/pglite.js.map +1 -0
- package/dist/storage/sqlite.d.ts +117 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +357 -8
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/storage/store.d.ts +63 -0
- package/dist/storage/store.d.ts.map +1 -0
- package/dist/storage/store.js +27 -0
- package/dist/storage/store.js.map +1 -0
- package/dist/types/engram.d.ts +92 -2
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js.map +1 -1
- package/package.json +3 -1
- package/src/adapters/claude-code.ts +218 -234
- package/src/adapters/codex.ts +252 -262
- package/src/adapters/common.ts +411 -284
- package/src/adapters/cursor.ts +135 -150
- package/src/adapters/http.ts +93 -100
- package/src/adapters/index.ts +31 -31
- package/src/adapters/types.ts +75 -75
- package/src/api/index.ts +3 -3
- package/src/api/routes.ts +367 -120
- package/src/cli/migrate.ts +307 -0
- package/src/cli.ts +37 -1
- package/src/coordination/circuit-breaker.ts +83 -0
- package/src/coordination/events.ts +90 -90
- package/src/coordination/failure-modes.ts +50 -0
- package/src/coordination/peer-decisions.ts +105 -105
- package/src/coordination/plugin-loader.ts +60 -60
- package/src/coordination/plugin.ts +44 -44
- package/src/coordination/routes.ts +56 -1
- package/src/coordination/schema.ts +13 -0
- package/src/coordination/stale.ts +80 -11
- package/src/coordination/types.ts +311 -311
- package/src/coordination/write-mutex.ts +69 -69
- package/src/core/auto-tagger.ts +168 -168
- package/src/core/decay.ts +63 -63
- package/src/core/embeddings.ts +110 -93
- package/src/core/index.ts +5 -5
- package/src/core/logger.ts +36 -36
- package/src/core/ml-worker-entry.ts +194 -0
- package/src/core/ml-worker.ts +281 -0
- package/src/core/query-expander.ts +122 -128
- package/src/core/reranker.ts +119 -125
- package/src/core/salience.ts +200 -33
- package/src/core/write-pipeline.ts +521 -0
- package/src/engine/activation.ts +235 -109
- package/src/engine/confidence.ts +120 -0
- package/src/engine/connections.ts +162 -103
- package/src/engine/consolidation-scheduler.ts +242 -125
- package/src/engine/consolidation.ts +138 -45
- package/src/engine/eval.ts +102 -102
- package/src/engine/eviction.ts +101 -101
- package/src/engine/index.ts +8 -8
- package/src/engine/retraction.ts +366 -116
- package/src/engine/staging.ts +74 -74
- package/src/hooks/sidecar.ts +5 -5
- package/src/index.ts +42 -28
- package/src/mcp.ts +105 -152
- package/src/storage/factory.ts +147 -0
- package/src/storage/index.ts +3 -3
- package/src/storage/pglite-schema.ts +166 -0
- package/src/storage/pglite.ts +1363 -0
- package/src/storage/sqlite.ts +402 -7
- package/src/storage/store.ts +80 -0
- package/src/types/agent.ts +67 -67
- package/src/types/checkpoint.ts +46 -46
- package/src/types/engram.ts +101 -3
- package/src/types/eval.ts +100 -100
- package/src/types/index.ts +6 -6
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-worker CircuitBreaker for the coordination control layer.
|
|
3
|
+
* Prevents chronically-stale workers from poisoning the assignment queue.
|
|
4
|
+
* Part of AWM 0.8.1 — additive, no breaking changes.
|
|
5
|
+
*
|
|
6
|
+
* States:
|
|
7
|
+
* closed — normal operation
|
|
8
|
+
* open — worker blocked after FAILURE_THRESHOLD consecutive failures
|
|
9
|
+
* half_open — probe window (30s after open), allows one assignment attempt
|
|
10
|
+
*/
|
|
11
|
+
import type Database from 'better-sqlite3';
|
|
12
|
+
export type CircuitState = 'closed' | 'open' | 'half_open';
|
|
13
|
+
/** Record a worker failure. Opens the circuit when consecutive failures hit the threshold. */
|
|
14
|
+
export declare function recordFailure(db: Database.Database, agentId: string): void;
|
|
15
|
+
/** Record a worker success. Resets to closed regardless of prior state. */
|
|
16
|
+
export declare function recordSuccess(db: Database.Database, agentId: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Get current circuit state for a worker.
|
|
19
|
+
* If the circuit has been open for >30s, auto-transitions to half_open.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getState(db: Database.Database, agentId: string): CircuitState;
|
|
22
|
+
/** Returns true when the worker is eligible to receive an assignment. */
|
|
23
|
+
export declare function isAvailable(db: Database.Database, agentId: string): boolean;
|
|
24
|
+
//# sourceMappingURL=circuit-breaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/coordination/circuit-breaker.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAK3D,8FAA8F;AAC9F,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAiB1E;AAED,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAU1E;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAoB7E;AAED,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAE3E"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Per-worker CircuitBreaker for the coordination control layer.
|
|
5
|
+
* Prevents chronically-stale workers from poisoning the assignment queue.
|
|
6
|
+
* Part of AWM 0.8.1 — additive, no breaking changes.
|
|
7
|
+
*
|
|
8
|
+
* States:
|
|
9
|
+
* closed — normal operation
|
|
10
|
+
* open — worker blocked after FAILURE_THRESHOLD consecutive failures
|
|
11
|
+
* half_open — probe window (30s after open), allows one assignment attempt
|
|
12
|
+
*/
|
|
13
|
+
const FAILURE_THRESHOLD = 5;
|
|
14
|
+
const HALF_OPEN_DELAY_MS = 30_000;
|
|
15
|
+
/** Record a worker failure. Opens the circuit when consecutive failures hit the threshold. */
|
|
16
|
+
export function recordFailure(db, agentId) {
|
|
17
|
+
db.prepare(`
|
|
18
|
+
INSERT INTO coord_circuit_state (agent_id, consecutive_failures, last_transition_at)
|
|
19
|
+
VALUES (?, 1, datetime('now'))
|
|
20
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
21
|
+
consecutive_failures = consecutive_failures + 1,
|
|
22
|
+
state = CASE
|
|
23
|
+
WHEN consecutive_failures + 1 >= ${FAILURE_THRESHOLD} THEN 'open'
|
|
24
|
+
ELSE state
|
|
25
|
+
END,
|
|
26
|
+
opened_at = CASE
|
|
27
|
+
WHEN consecutive_failures + 1 >= ${FAILURE_THRESHOLD} AND (state != 'open' OR opened_at IS NULL)
|
|
28
|
+
THEN datetime('now')
|
|
29
|
+
ELSE opened_at
|
|
30
|
+
END,
|
|
31
|
+
last_transition_at = datetime('now')
|
|
32
|
+
`).run(agentId);
|
|
33
|
+
}
|
|
34
|
+
/** Record a worker success. Resets to closed regardless of prior state. */
|
|
35
|
+
export function recordSuccess(db, agentId) {
|
|
36
|
+
db.prepare(`
|
|
37
|
+
INSERT INTO coord_circuit_state (agent_id, state, consecutive_failures, last_transition_at)
|
|
38
|
+
VALUES (?, 'closed', 0, datetime('now'))
|
|
39
|
+
ON CONFLICT(agent_id) DO UPDATE SET
|
|
40
|
+
state = 'closed',
|
|
41
|
+
consecutive_failures = 0,
|
|
42
|
+
opened_at = NULL,
|
|
43
|
+
last_transition_at = datetime('now')
|
|
44
|
+
`).run(agentId);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get current circuit state for a worker.
|
|
48
|
+
* If the circuit has been open for >30s, auto-transitions to half_open.
|
|
49
|
+
*/
|
|
50
|
+
export function getState(db, agentId) {
|
|
51
|
+
const row = db.prepare(`SELECT state, opened_at FROM coord_circuit_state WHERE agent_id = ?`).get(agentId);
|
|
52
|
+
if (!row || row.state === 'closed')
|
|
53
|
+
return 'closed';
|
|
54
|
+
if (row.state === 'half_open')
|
|
55
|
+
return 'half_open';
|
|
56
|
+
// open — check if half-open window has elapsed
|
|
57
|
+
if (row.state === 'open' && row.opened_at) {
|
|
58
|
+
const openedAt = new Date(row.opened_at.endsWith('Z') ? row.opened_at : row.opened_at + 'Z').getTime();
|
|
59
|
+
if (Date.now() - openedAt > HALF_OPEN_DELAY_MS) {
|
|
60
|
+
db.prepare(`UPDATE coord_circuit_state SET state = 'half_open', last_transition_at = datetime('now') WHERE agent_id = ?`).run(agentId);
|
|
61
|
+
return 'half_open';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return 'open';
|
|
65
|
+
}
|
|
66
|
+
/** Returns true when the worker is eligible to receive an assignment. */
|
|
67
|
+
export function isAvailable(db, agentId) {
|
|
68
|
+
return getState(db, agentId) !== 'open';
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../src/coordination/circuit-breaker.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,sCAAsC;AACtC;;;;;;;;;GASG;AAMH,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAC5B,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,8FAA8F;AAC9F,MAAM,UAAU,aAAa,CAAC,EAAqB,EAAE,OAAe;IAClE,EAAE,CAAC,OAAO,CAAC;;;;;;2CAM8B,iBAAiB;;;;2CAIjB,iBAAiB;;;;;GAKzD,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAClB,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,aAAa,CAAC,EAAqB,EAAE,OAAe;IAClE,EAAE,CAAC,OAAO,CAAC;;;;;;;;GAQV,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAqB,EAAE,OAAe;IAC7D,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CACpB,qEAAqE,CACtE,CAAC,GAAG,CAAC,OAAO,CAA4D,CAAC;IAE1E,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IACpD,IAAI,GAAG,CAAC,KAAK,KAAK,WAAW;QAAE,OAAO,WAAW,CAAC;IAElD,+CAA+C;IAC/C,IAAI,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;QACvG,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,GAAG,kBAAkB,EAAE,CAAC;YAC/C,EAAE,CAAC,OAAO,CACR,6GAA6G,CAC9G,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACf,OAAO,WAAW,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,WAAW,CAAC,EAAqB,EAAE,OAAe;IAChE,OAAO,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FailureMode taxonomy and mutation-hint map for the coordination control layer.
|
|
3
|
+
* Part of AWM 0.8.1 — additive, no breaking changes.
|
|
4
|
+
*/
|
|
5
|
+
export declare enum FailureMode {
|
|
6
|
+
AGENT_STALE = "agent_stale",
|
|
7
|
+
TIMEOUT = "timeout",
|
|
8
|
+
OUTPUT_INVALID = "output_invalid",
|
|
9
|
+
TEST_FAIL = "test_fail",
|
|
10
|
+
LINT_FAIL = "lint_fail",
|
|
11
|
+
MERGE_CONFLICT = "merge_conflict",
|
|
12
|
+
UNKNOWN = "unknown"
|
|
13
|
+
}
|
|
14
|
+
/** Classify a failure result string into one of the known modes. */
|
|
15
|
+
export declare function classifyFailure(result: string | null): FailureMode;
|
|
16
|
+
/**
|
|
17
|
+
* Corrective guidance injected into the task description on retry.
|
|
18
|
+
* Each hint is written in the vocabulary the next worker will read.
|
|
19
|
+
*/
|
|
20
|
+
export declare const MUTATION_HINTS: Record<FailureMode, string>;
|
|
21
|
+
//# sourceMappingURL=failure-modes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure-modes.d.ts","sourceRoot":"","sources":["../../src/coordination/failure-modes.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,oBAAY,WAAW;IACrB,WAAW,gBAAmB;IAC9B,OAAO,YAAmB;IAC1B,cAAc,mBAAmB;IACjC,SAAS,cAAmB;IAC5B,SAAS,cAAmB;IAC5B,cAAc,mBAAmB;IACjC,OAAO,YAAmB;CAC3B;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,CAUlE;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAetD,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* FailureMode taxonomy and mutation-hint map for the coordination control layer.
|
|
5
|
+
* Part of AWM 0.8.1 — additive, no breaking changes.
|
|
6
|
+
*/
|
|
7
|
+
export var FailureMode;
|
|
8
|
+
(function (FailureMode) {
|
|
9
|
+
FailureMode["AGENT_STALE"] = "agent_stale";
|
|
10
|
+
FailureMode["TIMEOUT"] = "timeout";
|
|
11
|
+
FailureMode["OUTPUT_INVALID"] = "output_invalid";
|
|
12
|
+
FailureMode["TEST_FAIL"] = "test_fail";
|
|
13
|
+
FailureMode["LINT_FAIL"] = "lint_fail";
|
|
14
|
+
FailureMode["MERGE_CONFLICT"] = "merge_conflict";
|
|
15
|
+
FailureMode["UNKNOWN"] = "unknown";
|
|
16
|
+
})(FailureMode || (FailureMode = {}));
|
|
17
|
+
/** Classify a failure result string into one of the known modes. */
|
|
18
|
+
export function classifyFailure(result) {
|
|
19
|
+
if (!result)
|
|
20
|
+
return FailureMode.UNKNOWN;
|
|
21
|
+
const r = result.toLowerCase();
|
|
22
|
+
if (r.includes('stale') || r.includes('disconnected'))
|
|
23
|
+
return FailureMode.AGENT_STALE;
|
|
24
|
+
if (r.includes('timeout') || r.includes('timed out'))
|
|
25
|
+
return FailureMode.TIMEOUT;
|
|
26
|
+
if (r.includes('json') || r.includes('schema') || r.includes('parse'))
|
|
27
|
+
return FailureMode.OUTPUT_INVALID;
|
|
28
|
+
if (r.includes('test fail') || r.includes('vitest') || r.includes('jest'))
|
|
29
|
+
return FailureMode.TEST_FAIL;
|
|
30
|
+
if (r.includes('lint') || r.includes('eslint') || r.includes('typecheck'))
|
|
31
|
+
return FailureMode.LINT_FAIL;
|
|
32
|
+
if (r.includes('conflict'))
|
|
33
|
+
return FailureMode.MERGE_CONFLICT;
|
|
34
|
+
return FailureMode.UNKNOWN;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Corrective guidance injected into the task description on retry.
|
|
38
|
+
* Each hint is written in the vocabulary the next worker will read.
|
|
39
|
+
*/
|
|
40
|
+
export const MUTATION_HINTS = {
|
|
41
|
+
[FailureMode.AGENT_STALE]: 'Previous worker disconnected before completion. Resume from last known state; check git status before re-running destructive commands.',
|
|
42
|
+
[FailureMode.TIMEOUT]: 'Previous attempt timed out. Break work into smaller commits; report progress every 5 minutes.',
|
|
43
|
+
[FailureMode.OUTPUT_INVALID]: 'Previous output failed validation. Return a single fenced code block; verify JSON parses before submitting.',
|
|
44
|
+
[FailureMode.TEST_FAIL]: 'Previous attempt left tests failing. Run vitest before completion; do NOT mark complete if any test fails.',
|
|
45
|
+
[FailureMode.LINT_FAIL]: 'Previous attempt had lint/typecheck errors. Run pnpm typecheck and pnpm lint before completion.',
|
|
46
|
+
[FailureMode.MERGE_CONFLICT]: 'Previous attempt left merge conflicts unresolved. git pull --rebase, resolve, then re-attempt.',
|
|
47
|
+
[FailureMode.UNKNOWN]: 'Previous attempt failed for an unclassified reason. Investigate the prior result before re-running.',
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=failure-modes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"failure-modes.js","sourceRoot":"","sources":["../../src/coordination/failure-modes.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,sCAAsC;AACtC;;;GAGG;AAEH,MAAM,CAAN,IAAY,WAQX;AARD,WAAY,WAAW;IACrB,0CAA8B,CAAA;IAC9B,kCAA0B,CAAA;IAC1B,gDAAiC,CAAA;IACjC,sCAA4B,CAAA;IAC5B,sCAA4B,CAAA;IAC5B,gDAAiC,CAAA;IACjC,kCAA0B,CAAA;AAC5B,CAAC,EARW,WAAW,KAAX,WAAW,QAQtB;AAED,oEAAoE;AACpE,MAAM,UAAU,eAAe,CAAC,MAAqB;IACnD,IAAI,CAAC,MAAM;QAAE,OAAO,WAAW,CAAC,OAAO,CAAC;IACxC,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IAC/B,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,OAAO,WAAW,CAAC,WAAW,CAAC;IACtF,IAAI,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAG,OAAO,WAAW,CAAC,OAAO,CAAC;IAClF,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,WAAW,CAAC,cAAc,CAAC;IACzG,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,WAAW,CAAC,SAAS,CAAC;IACxG,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,WAAW,CAAC,SAAS,CAAC;IACxG,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;QAA8B,OAAO,WAAW,CAAC,cAAc,CAAC;IAC1F,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAgC;IACzD,CAAC,WAAW,CAAC,WAAW,CAAC,EACvB,wIAAwI;IAC1I,CAAC,WAAW,CAAC,OAAO,CAAC,EACnB,+FAA+F;IACjG,CAAC,WAAW,CAAC,cAAc,CAAC,EAC1B,6GAA6G;IAC/G,CAAC,WAAW,CAAC,SAAS,CAAC,EACrB,4GAA4G;IAC9G,CAAC,WAAW,CAAC,SAAS,CAAC,EACrB,iGAAiG;IACnG,CAAC,WAAW,CAAC,cAAc,CAAC,EAC1B,gGAAgG;IAClG,CAAC,WAAW,CAAC,OAAO,CAAC,EACnB,qGAAqG;CACxG,CAAC"}
|
|
@@ -44,14 +44,14 @@ export function queryPeerDecisions(db, selfName, query, windowHours = 1, limit =
|
|
|
44
44
|
const safeHours = Math.max(0.1, Math.min(windowHours, 168)); // 6min – 1 week
|
|
45
45
|
const safeLimit = Math.max(1, Math.min(limit, 50));
|
|
46
46
|
const keywords = extractKeywords(query);
|
|
47
|
-
const baseWhere = `
|
|
48
|
-
d.created_at >= datetime('now', '-${safeHours} hours')
|
|
49
|
-
AND a.name != ?
|
|
47
|
+
const baseWhere = `
|
|
48
|
+
d.created_at >= datetime('now', '-${safeHours} hours')
|
|
49
|
+
AND a.name != ?
|
|
50
50
|
`;
|
|
51
|
-
const select = `
|
|
52
|
-
SELECT d.id, a.name AS author_name, d.assignment_id, d.tags, d.summary, d.created_at
|
|
53
|
-
FROM coord_decisions d
|
|
54
|
-
JOIN coord_agents a ON d.author_id = a.id
|
|
51
|
+
const select = `
|
|
52
|
+
SELECT d.id, a.name AS author_name, d.assignment_id, d.tags, d.summary, d.created_at
|
|
53
|
+
FROM coord_decisions d
|
|
54
|
+
JOIN coord_agents a ON d.author_id = a.id
|
|
55
55
|
`;
|
|
56
56
|
if (keywords.length === 0) {
|
|
57
57
|
// No useful keywords — return the N most recent decisions from other agents
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/coordination/routes.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/coordination/routes.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AA+ExD,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,eAAe,EAAE,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,OAAO,aAAa,EAAE,oBAAoB,GAAG,IAAI,CA4gExK"}
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { randomUUID } from 'node:crypto';
|
|
9
9
|
import { checkinSchema, checkoutSchema, pulseSchema, nextSchema, assignCreateSchema, assignmentQuerySchema, assignmentClaimSchema, assignmentUpdateSchema, assignmentIdParamSchema, assignmentsListSchema, reassignSchema, lockAcquireSchema, lockReleaseSchema, commandCreateSchema, commandWaitQuerySchema, findingCreateSchema, findingsQuerySchema, findingIdParamSchema, findingUpdateSchema, decisionsQuerySchema, decisionCreateSchema, eventsQuerySchema, staleQuerySchema, workersQuerySchema, agentIdParamSchema, timelineQuerySchema, channelRegisterSchema, channelDeregisterSchema, channelPushSchema, } from './schemas.js';
|
|
10
|
-
import { detectStale, cleanupStale } from './stale.js';
|
|
10
|
+
import { detectStale, cleanupStale, retryOrFailAssignment } from './stale.js';
|
|
11
|
+
import { recordSuccess, recordFailure as circuitRecordFailure, isAvailable } from './circuit-breaker.js';
|
|
11
12
|
/** Pretty timestamp for coordination logs. */
|
|
12
13
|
function ts() {
|
|
13
14
|
return new Date().toLocaleTimeString('en-GB', { hour12: false });
|
|
@@ -315,6 +316,10 @@ export function registerCoordinationRoutes(app, db, store, eventBus) {
|
|
|
315
316
|
const pending = reserved ?? (agentWorkspace
|
|
316
317
|
? db.prepare(`SELECT * FROM coord_assignments WHERE status = 'pending' AND agent_id IS NULL AND (workspace = ? OR workspace IS NULL) ${blockedFilter} ORDER BY priority DESC, created_at ASC LIMIT 1`).get(agentWorkspace)
|
|
317
318
|
: db.prepare(`SELECT * FROM coord_assignments WHERE status = 'pending' AND agent_id IS NULL ${blockedFilter} ORDER BY priority DESC, created_at ASC LIMIT 1`).get());
|
|
319
|
+
// Circuit breaker: refuse assignment if worker is in open state
|
|
320
|
+
if (pending && !isAvailable(db, agentId)) {
|
|
321
|
+
return reply.code(423).send({ status: 'idle', assignment: null, circuit_open: true, reason: 'circuit_open' });
|
|
322
|
+
}
|
|
318
323
|
if (pending) {
|
|
319
324
|
const claimed = db.prepare(`UPDATE coord_assignments SET agent_id = ?, status = 'assigned', started_at = datetime('now') WHERE id = ? AND status = 'pending'`).run(agentId, pending.id);
|
|
320
325
|
if (claimed.changes > 0) {
|
|
@@ -516,6 +521,10 @@ export function registerCoordinationRoutes(app, db, store, eventBus) {
|
|
|
516
521
|
const pending = reserved ?? (agentWorkspace
|
|
517
522
|
? db.prepare(`SELECT * FROM coord_assignments WHERE status = 'pending' AND agent_id IS NULL AND (workspace = ? OR workspace IS NULL) ${blockedFilter} ORDER BY priority DESC, created_at ASC LIMIT 1`).get(agentWorkspace)
|
|
518
523
|
: db.prepare(`SELECT * FROM coord_assignments WHERE status = 'pending' AND agent_id IS NULL ${blockedFilter} ORDER BY priority DESC, created_at ASC LIMIT 1`).get());
|
|
524
|
+
// Circuit breaker: refuse assignment if worker is in open state
|
|
525
|
+
if (pending && !isAvailable(db, agentId)) {
|
|
526
|
+
return reply.code(423).send({ assignment: null, circuit_open: true, reason: 'circuit_open' });
|
|
527
|
+
}
|
|
519
528
|
if (pending) {
|
|
520
529
|
const claimed = db.prepare(`UPDATE coord_assignments SET agent_id = ?, status = 'assigned', started_at = datetime('now') WHERE id = ? AND status = 'pending'`).run(agentId, pending.id);
|
|
521
530
|
if (claimed.changes > 0) {
|
|
@@ -598,6 +607,13 @@ export function registerCoordinationRoutes(app, db, store, eventBus) {
|
|
|
598
607
|
if (['completed', 'failed'].includes(status)) {
|
|
599
608
|
coordLog(`${assignInfo?.agent_name ?? 'unknown'} ${status}: ${assignInfo?.task?.slice(0, 80) ?? id}`);
|
|
600
609
|
}
|
|
610
|
+
// Circuit breaker: track success/failure per worker
|
|
611
|
+
if (assignInfo?.agent_id) {
|
|
612
|
+
if (status === 'completed')
|
|
613
|
+
recordSuccess(db, assignInfo.agent_id);
|
|
614
|
+
else if (status === 'failed')
|
|
615
|
+
circuitRecordFailure(db, assignInfo.agent_id);
|
|
616
|
+
}
|
|
601
617
|
// Emit events
|
|
602
618
|
eventBus?.emit('assignment.updated', { assignmentId: id, agentId: assignInfo?.agent_id ?? null, status, result });
|
|
603
619
|
if (status === 'completed') {
|
|
@@ -722,6 +738,29 @@ export function registerCoordinationRoutes(app, db, store, eventBus) {
|
|
|
722
738
|
return reply.code(400).send({ error: gate.error });
|
|
723
739
|
return reply.send({ ok: true });
|
|
724
740
|
});
|
|
741
|
+
/** POST /assignment/:id/fail — Worker voluntarily fails an assignment with retry logic.
|
|
742
|
+
* Body: { result: string, mode?: FailureMode }
|
|
743
|
+
* Returns: { outcome: 'retried' | 'failed', attempt_count, last_failure_mode }
|
|
744
|
+
*/
|
|
745
|
+
app.post('/assignment/:id/fail', async (req, reply) => {
|
|
746
|
+
const { id } = assignmentIdParamSchema.parse(req.params);
|
|
747
|
+
const body = req.body;
|
|
748
|
+
const result = body?.result ?? 'worker-initiated failure';
|
|
749
|
+
const mode = body?.mode;
|
|
750
|
+
const row = db.prepare(`SELECT id, agent_id, status FROM coord_assignments WHERE id = ?`).get(id);
|
|
751
|
+
if (!row)
|
|
752
|
+
return reply.code(404).send({ error: 'assignment not found' });
|
|
753
|
+
if (['completed', 'failed'].includes(row.status)) {
|
|
754
|
+
return reply.code(400).send({ error: `cannot fail a ${row.status} assignment` });
|
|
755
|
+
}
|
|
756
|
+
const agentId = row.agent_id ?? 'unknown';
|
|
757
|
+
const outcome = retryOrFailAssignment(db, id, agentId, result, mode);
|
|
758
|
+
// Circuit breaker: record failure on voluntary fail
|
|
759
|
+
if (agentId !== 'unknown')
|
|
760
|
+
circuitRecordFailure(db, agentId);
|
|
761
|
+
const updated = db.prepare(`SELECT attempt_count, last_failure_mode FROM coord_assignments WHERE id = ?`).get(id);
|
|
762
|
+
return reply.send({ ok: true, outcome, attempt_count: updated?.attempt_count ?? 0, last_failure_mode: updated?.last_failure_mode ?? null });
|
|
763
|
+
});
|
|
725
764
|
app.put('/assignment/:id', async (req, reply) => {
|
|
726
765
|
const { id } = assignmentIdParamSchema.parse(req.params);
|
|
727
766
|
const parsed = assignmentUpdateSchema.safeParse(req.body);
|
|
@@ -1423,6 +1462,10 @@ export function registerCoordinationRoutes(app, db, store, eventBus) {
|
|
|
1423
1462
|
const agent = db.prepare(`SELECT name, workspace FROM coord_agents WHERE id = ?`).get(agentId);
|
|
1424
1463
|
if (!agent)
|
|
1425
1464
|
return reply.status(404).send({ error: 'Agent not found' });
|
|
1465
|
+
// Circuit breaker: refuse push to open-circuit workers
|
|
1466
|
+
if (!isAvailable(db, agentId)) {
|
|
1467
|
+
return reply.status(423).send({ error: 'circuit_open', reason: 'Worker circuit is open — too many consecutive failures. Try again after 30s or after a successful assignment.' });
|
|
1468
|
+
}
|
|
1426
1469
|
// Try live channel delivery first
|
|
1427
1470
|
const session = db.prepare(`SELECT agent_id, channel_id FROM coord_channel_sessions WHERE agent_id = ? AND status = 'connected'`).get(agentId);
|
|
1428
1471
|
if (session) {
|