@duetso/agent 0.1.189 → 0.1.191
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 +27 -0
- package/dist/package.json +2 -2
- package/dist/src/cli/help.d.ts +1 -0
- package/dist/src/cli/help.d.ts.map +1 -1
- package/dist/src/cli/help.js +43 -0
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/memory-db.d.ts +6 -1
- package/dist/src/cli/memory-db.d.ts.map +1 -1
- package/dist/src/cli/memory-db.js +8 -1
- package/dist/src/cli/memory-db.js.map +1 -1
- package/dist/src/cli/memory-reflect.js +1 -1
- package/dist/src/cli/memory-reflect.js.map +1 -1
- package/dist/src/cli/train.d.ts +23 -0
- package/dist/src/cli/train.d.ts.map +1 -0
- package/dist/src/cli/train.js +356 -0
- package/dist/src/cli/train.js.map +1 -0
- package/dist/src/cli.d.ts +1 -0
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +6 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/memory/context-pack.d.ts.map +1 -1
- package/dist/src/memory/context-pack.js +1 -0
- package/dist/src/memory/context-pack.js.map +1 -1
- package/dist/src/memory/embedding-worker.d.ts +26 -6
- package/dist/src/memory/embedding-worker.d.ts.map +1 -1
- package/dist/src/memory/embedding-worker.js +138 -61
- package/dist/src/memory/embedding-worker.js.map +1 -1
- package/dist/src/memory/loader.d.ts +4 -1
- package/dist/src/memory/loader.d.ts.map +1 -1
- package/dist/src/memory/loader.js +7 -3
- package/dist/src/memory/loader.js.map +1 -1
- package/dist/src/memory/observational.d.ts +3 -0
- package/dist/src/memory/observational.d.ts.map +1 -1
- package/dist/src/memory/observational.js +16 -0
- package/dist/src/memory/observational.js.map +1 -1
- package/dist/src/memory/session.d.ts +16 -0
- package/dist/src/memory/session.d.ts.map +1 -1
- package/dist/src/memory/session.js +24 -0
- package/dist/src/memory/session.js.map +1 -1
- package/dist/src/memory/storage.d.ts.map +1 -1
- package/dist/src/memory/storage.js +23 -8
- package/dist/src/memory/storage.js.map +1 -1
- package/dist/src/train/archive.d.ts +20 -0
- package/dist/src/train/archive.d.ts.map +1 -0
- package/dist/src/train/archive.js +84 -0
- package/dist/src/train/archive.js.map +1 -0
- package/dist/src/train/types.d.ts +30 -0
- package/dist/src/train/types.d.ts.map +1 -0
- package/dist/src/train/types.js +2 -0
- package/dist/src/train/types.js.map +1 -0
- package/dist/src/types/memory.d.ts +25 -9
- package/dist/src/types/memory.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -353,6 +353,33 @@ The interactive TUI accepts image attachments (PNG, JPEG, GIF, WebP):
|
|
|
353
353
|
|
|
354
354
|
</details>
|
|
355
355
|
|
|
356
|
+
<details>
|
|
357
|
+
<summary><b>Train a folder of docs into durable memory</b></summary>
|
|
358
|
+
|
|
359
|
+
`duet train <folder>` launches a sub-agent inside the folder, lets it read whatever is there with its native tools (markdown, CSVs, PDFs, spreadsheets, screenshots, source — anything the agent can open), and produces two artifacts:
|
|
360
|
+
|
|
361
|
+
- **One high-priority manual (user-curated) row** in `~/.duet/memory.db`, tagged `train` and `train:<slug>`. Fresh `duet` sessions load it into their initial memory pack with a ranking boost (via `manualBias`), and writing it as a `manual` row means `duet memory reflect` never compacts it away.
|
|
362
|
+
- **`~/.duet/train/<memory-id>/`** — a hidden archive: a copy of every source file plus a `manifest.json`. Provenance only; the DB row is the source of truth. Deleting the memory via `duet memory` removes the archive too.
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
duet train ./snowflake-notes # writes to ~/.duet/memory.db
|
|
366
|
+
duet train ./snowflake-notes --slug snow # custom slug (default: folder basename)
|
|
367
|
+
duet train ./snowflake-notes --model opus-4.8
|
|
368
|
+
duet train ./snowflake-notes --db /tmp/scratch.db # write to a throwaway DB instead
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Re-running `train` on the same slug **replaces** the prior row and its archive — there is at most one `train:<slug>` observation at a time.
|
|
372
|
+
|
|
373
|
+
Three things to know:
|
|
374
|
+
|
|
375
|
+
- **The current session won't see the new memory.** Memory loads at session start. Start a fresh `duet` session to test recall.
|
|
376
|
+
- **Synthesis needs an API key.** `DUET_API_KEY` (recommended), or any of the provider keys covered above.
|
|
377
|
+
- **The corpus folder's `.env` is loaded** (so a project folder can supply its own provider credentials). Check folders you didn't author before training them.
|
|
378
|
+
|
|
379
|
+
The CLI also prints the observation content to stdout, so it's pipeable: `duet train ./docs | tee notes.txt`.
|
|
380
|
+
|
|
381
|
+
</details>
|
|
382
|
+
|
|
356
383
|
<details>
|
|
357
384
|
<summary><b>CLI Login</b></summary>
|
|
358
385
|
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duetso/agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.191",
|
|
4
4
|
"description": "An opinionated full-stack agent turn runner with native memories, interrupts, and multi-agent orchestration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"check-types": "tsc --noEmit",
|
|
44
44
|
"cli": "bun src/cli.ts",
|
|
45
45
|
"dev": "tsc --watch",
|
|
46
|
-
"eval": "docker run --rm -v \"$PWD:/src:ro\" -w /work -e HOME=/tmp/home -e DUET_TEST_IN_DOCKER=1 oven/bun:1.3.11 sh -lc 'cp -R /src/. /work && bun install --frozen-lockfile && bun test ./evals/*.eval.ts'",
|
|
46
|
+
"eval": "docker run --rm -v \"$PWD:/src:ro\" -w /work -e HOME=/tmp/home -e DUET_TEST_IN_DOCKER=1 -e DUET_API_KEY -e ANTHROPIC_API_KEY -e OPENAI_API_KEY -e EVAL_MODEL -e JUDGE_MODEL -e COVERAGE_THRESHOLD oven/bun:1.3.11 sh -lc 'cp -R /src/. /work && bun install --frozen-lockfile && bun test ./evals/*.eval.ts'",
|
|
47
47
|
"format": "oxfmt --write --no-error-on-unmatched-pattern",
|
|
48
48
|
"format:check": "oxfmt --check --no-error-on-unmatched-pattern",
|
|
49
49
|
"lint": "oxlint src/",
|
package/dist/src/cli/help.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare function printEnvHelp(): void;
|
|
|
11
11
|
export declare function printSkillsHelp(): void;
|
|
12
12
|
export declare function printMemoryHelp(): void;
|
|
13
13
|
export declare function printMemoryReflectHelp(): void;
|
|
14
|
+
export declare function printTrainHelp(): void;
|
|
14
15
|
export declare function printSendFeedbackHelp(): void;
|
|
15
16
|
export declare function printUpgradeHelp(packageName: string): void;
|
|
16
17
|
//# sourceMappingURL=help.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../src/cli/help.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAEjD,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../src/cli/help.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,IAAI,CAAC;AAEjD,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAqFtD;AAED,wBAAgB,cAAc,IAAI,IAAI,CA0BrC;AAED,wBAAgB,YAAY,IAAI,IAAI,CAsBnC;AAED,wBAAgB,eAAe,IAAI,IAAI,CAqBtC;AAED,wBAAgB,eAAe,IAAI,IAAI,CA2BtC;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAuC7C;AAED,wBAAgB,cAAc,IAAI,IAAI,CAyCrC;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAiB5C;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAe1D"}
|
package/dist/src/cli/help.js
CHANGED
|
@@ -87,6 +87,7 @@ EXAMPLES
|
|
|
87
87
|
duet login
|
|
88
88
|
duet env
|
|
89
89
|
duet memory
|
|
90
|
+
duet train ./docs/my-project
|
|
90
91
|
duet send-feedback "the TUI flickers when..."
|
|
91
92
|
duet upgrade
|
|
92
93
|
`);
|
|
@@ -231,6 +232,48 @@ OPTIONS
|
|
|
231
232
|
-h, --help Show this help
|
|
232
233
|
`);
|
|
233
234
|
}
|
|
235
|
+
export function printTrainHelp() {
|
|
236
|
+
console.log(`
|
|
237
|
+
duet train — Ingest a project corpus into one durable memory observation
|
|
238
|
+
|
|
239
|
+
USAGE
|
|
240
|
+
duet train <folder> [--slug <name>] [--model <name>] [--db <path>] [--wait <seconds>]
|
|
241
|
+
|
|
242
|
+
DESCRIPTION
|
|
243
|
+
Launches a duet agent with the corpus folder as its working directory.
|
|
244
|
+
The agent reads the corpus using its native file-reading tools (any
|
|
245
|
+
format the duet agent normally reads — markdown, plain text, CSVs, PDFs,
|
|
246
|
+
spreadsheets, source code) and writes a single handoff file at the
|
|
247
|
+
corpus root:
|
|
248
|
+
|
|
249
|
+
.duet-train.json — structured handoff with headline + observation.
|
|
250
|
+
|
|
251
|
+
'train' then persists the synthesis into ~/.duet/memory.db as a manual
|
|
252
|
+
(user-curated) row (tagged 'train' and 'train:<slug>'), archives the
|
|
253
|
+
corpus under ~/.duet/train/<memory-id>/, removes the handoff file, and
|
|
254
|
+
prints the observation content to stdout so what you see is what landed
|
|
255
|
+
in memory.
|
|
256
|
+
|
|
257
|
+
Subsequent runs against the same slug replace the prior row in place,
|
|
258
|
+
so the memory pool does not bloat. Writing the row as a manual row
|
|
259
|
+
earns it a ranking boost in the memory pack (via manualBias) and exempts
|
|
260
|
+
it from 'duet memory reflect' compaction; deleting it via 'duet memory'
|
|
261
|
+
also removes its archive. Note: train loads the corpus folder's .env (for provider
|
|
262
|
+
credentials), so check folders you did not author before training.
|
|
263
|
+
|
|
264
|
+
OPTIONS
|
|
265
|
+
--slug <name> Override the corpus slug (default: sanitized folder basename)
|
|
266
|
+
--model <name> Model used by the synthesis sub-agent (default: same resolution as 'duet run')
|
|
267
|
+
--db <path> Memory database path (default: ~/.duet/memory.db)
|
|
268
|
+
--wait <seconds> Seconds to wait for the cross-process open-lock
|
|
269
|
+
(default: 30; 0 fails immediately)
|
|
270
|
+
-h, --help Show this help
|
|
271
|
+
|
|
272
|
+
EXAMPLES
|
|
273
|
+
duet train ./docs/my-project
|
|
274
|
+
duet train ./research --slug acme-research --model sonnet-4.6
|
|
275
|
+
`);
|
|
276
|
+
}
|
|
234
277
|
export function printSendFeedbackHelp() {
|
|
235
278
|
console.log(`
|
|
236
279
|
duet send-feedback — Send free-form markdown feedback to the Duet team
|
package/dist/src/cli/help.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../../src/cli/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAExE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC;AAEjD,MAAM,UAAU,YAAY,CAAC,WAAmB;IAC9C,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;gDAmBkC,WAAW;;;;;;;;;;;;;gHAaqD,+BAA+B;;;;;oFAK3D,qBAAqB
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../../src/cli/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAExE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAAC,CAAC;AAEjD,MAAM,UAAU,YAAY,CAAC,WAAmB;IAC9C,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;gDAmBkC,WAAW;;;;;;;;;;;;;gHAaqD,+BAA+B;;;;;oFAK3D,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8CxG,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;wEAW0D,qBAAqB;;;;;;;;;;;;;CAa5F,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;yDAa2C,qBAAqB;;;;;;IAM1E,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;CAChC,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;CAmBb,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;CAyBb,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCb,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuCb,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;CAeb,CAAC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,OAAO,CAAC,GAAG,CAAC;oCACsB,WAAW;;;;;;;;;;;;CAY9C,CAAC,CAAC;AACH,CAAC"}
|
|
@@ -52,7 +52,12 @@ export declare class MemoryDb {
|
|
|
52
52
|
}): Promise<Observation[]>;
|
|
53
53
|
/** Replace just the `content` of an observation, preserving everything else. */
|
|
54
54
|
updateContent(id: string, content: string): Promise<void>;
|
|
55
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Permanently remove an observation. There is no undo. Rows written by
|
|
57
|
+
* `duet train` keep a corpus archive under `~/.duet/train/<id>/` that
|
|
58
|
+
* shares the row's lifecycle, so it is removed too (a no-op for every
|
|
59
|
+
* other row — there is simply no directory to delete).
|
|
60
|
+
*/
|
|
56
61
|
delete(id: string): Promise<void>;
|
|
57
62
|
close(): Promise<void>;
|
|
58
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-db.d.ts","sourceRoot":"","sources":["../../../src/cli/memory-db.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"memory-db.d.ts","sourceRoot":"","sources":["../../../src/cli/memory-db.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,sEAAsE;AACtE,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CAK3F;AAED;;;;GAIG;AACH,qBAAa,QAAQ;IACC,OAAO,CAAC,QAAQ,CAAC,EAAE;IAAvC,OAAO;IAEP;;;;;;;;;OASG;WACU,IAAI,CACf,IAAI,EAAE,MAAM,EACZ,EAAE,YAA+C,EAAE,GAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAA;KAAO,GAClF,OAAO,CAAC,QAAQ,CAAC;IAiBpB,yDAAyD;IACnD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAO9B;;;;;;;;;;;;OAYG;IACG,UAAU,CAAC,EACf,KAAK,EACL,MAAM,EACN,GAAgB,GACjB,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IA6B1B,gFAAgF;IAC1E,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/D;;;;;OAKG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -2,6 +2,7 @@ import { observationScore, PRIORITY_WEIGHT } from "../memory/loader.js";
|
|
|
2
2
|
import { runMigrations } from "../memory/migrations.js";
|
|
3
3
|
import { DEFAULT_RECENCY_HALF_LIFE_MS, DEFAULT_REFLECTION_BIAS } from "../memory/observational.js";
|
|
4
4
|
import { DEFAULT_OPEN_LOCK_WAIT_BUDGET_MS, openPGliteWaitingForLock } from "../memory/pglite.js";
|
|
5
|
+
import { removeArchive } from "../train/archive.js";
|
|
5
6
|
/** Rows fetched per page by the ranked, lazily-paginated TUI list. */
|
|
6
7
|
export const MEMORY_PAGE_SIZE = 25;
|
|
7
8
|
/**
|
|
@@ -96,9 +97,15 @@ export class MemoryDb {
|
|
|
96
97
|
async updateContent(id, content) {
|
|
97
98
|
await this.db.query(`UPDATE observations SET content = $1 WHERE id = $2`, [content, id]);
|
|
98
99
|
}
|
|
99
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* Permanently remove an observation. There is no undo. Rows written by
|
|
102
|
+
* `duet train` keep a corpus archive under `~/.duet/train/<id>/` that
|
|
103
|
+
* shares the row's lifecycle, so it is removed too (a no-op for every
|
|
104
|
+
* other row — there is simply no directory to delete).
|
|
105
|
+
*/
|
|
100
106
|
async delete(id) {
|
|
101
107
|
await this.db.query(`DELETE FROM observations WHERE id = $1`, [id]);
|
|
108
|
+
await removeArchive(id);
|
|
102
109
|
}
|
|
103
110
|
async close() {
|
|
104
111
|
await this.db.close();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-db.js","sourceRoot":"","sources":["../../../src/cli/memory-db.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACnG,OAAO,EAAE,gCAAgC,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-db.js","sourceRoot":"","sources":["../../../src/cli/memory-db.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,7 +25,7 @@ export async function runMemoryReflectCommand(args, io = { stdout: process.stdou
|
|
|
25
25
|
await runMigrations(db);
|
|
26
26
|
},
|
|
27
27
|
},
|
|
28
|
-
...(options.waitBudgetMs !== undefined ? {
|
|
28
|
+
...(options.waitBudgetMs !== undefined ? { lockWaitBudgetMs: options.waitBudgetMs } : {}),
|
|
29
29
|
idleCloseMs: 60_000,
|
|
30
30
|
});
|
|
31
31
|
const removeShutdownHandlers = installShutdownHandlers(() => session.dispose());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-reflect.js","sourceRoot":"","sources":["../../../src/cli/memory-reflect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EACL,yBAAyB,EACzB,mCAAmC,EACnC,sBAAsB,EACtB,kCAAkC,GACnC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAiBxD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,IAAc,EACd,KAAuB,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;IAEzE,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,OAAO;QAAE,OAAO;IAErB,MAAM,OAAO,GAAG,IAAI,aAAa,CAAC;QAChC,IAAI,EAAE,OAAO,CAAC,MAAM;QACpB,WAAW,EAAE;YACX,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;gBACjB,MAAM,aAAa,CAAC,EAAE,CAAC,CAAC;YAC1B,CAAC;SACF;QACD,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"memory-reflect.js","sourceRoot":"","sources":["../../../src/cli/memory-reflect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface TrainCommandOptions {
|
|
2
|
+
folder: string;
|
|
3
|
+
slug: string;
|
|
4
|
+
/** Undefined means "resolve at run time via resolveCliModel". */
|
|
5
|
+
model: string | undefined;
|
|
6
|
+
dbPath: string;
|
|
7
|
+
waitBudgetMs?: number;
|
|
8
|
+
}
|
|
9
|
+
interface TrainCommandIO {
|
|
10
|
+
stdout: NodeJS.WritableStream;
|
|
11
|
+
stderr: NodeJS.WritableStream;
|
|
12
|
+
}
|
|
13
|
+
export declare function parseTrainArgs(args: string[]): TrainCommandOptions | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Run `duet train <folder>` — launch a sub-agent that reads the corpus
|
|
16
|
+
* and writes a structured handoff file (.duet-train.json), persist the
|
|
17
|
+
* synthesized observation into durable memory (replacing any prior
|
|
18
|
+
* `train:<slug>` row), archive the corpus under `~/.duet/train/<memoryId>/`,
|
|
19
|
+
* and verify both side effects landed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function runTrainCommand(args: string[], io?: TrainCommandIO): Promise<void>;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=train.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"train.d.ts","sourceRoot":"","sources":["../../../src/cli/train.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;CAC/B;AAcD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,mBAAmB,GAAG,SAAS,CA0D9E;AA8ID;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EAAE,EACd,EAAE,GAAE,cAAmE,GACtE,OAAO,CAAC,IAAI,CAAC,CAyJf"}
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { readFile, rm, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import dedent from "dedent";
|
|
4
|
+
import { runMigrations } from "../memory/migrations.js";
|
|
5
|
+
import { MemoryLockTimeoutError } from "../memory/pglite.js";
|
|
6
|
+
import { MemorySession } from "../memory/session.js";
|
|
7
|
+
import { appendObservation } from "../memory/storage.js";
|
|
8
|
+
import { resolveCliModel } from "../model-resolution/resolver.js";
|
|
9
|
+
import { DEFAULT_MEMORY_DB_PATH, SessionManager } from "../session/session-manager.js";
|
|
10
|
+
import { collectArchiveFiles, removeArchive, writeArchive } from "../train/archive.js";
|
|
11
|
+
import { printTrainHelp } from "./help.js";
|
|
12
|
+
import { fail, loadCliEnvFiles, resolveUserPath } from "./shared.js";
|
|
13
|
+
import { installShutdownHandlers } from "./shutdown.js";
|
|
14
|
+
/**
|
|
15
|
+
* Lowercase a base name and collapse anything that isn't a-z/0-9 into
|
|
16
|
+
* single dashes so the slug can serve as a tag (`train:<slug>`) and a
|
|
17
|
+
* folder name without quoting.
|
|
18
|
+
*/
|
|
19
|
+
function sanitizeSlug(name) {
|
|
20
|
+
return name
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
23
|
+
.replace(/^-+|-+$/g, "");
|
|
24
|
+
}
|
|
25
|
+
export function parseTrainArgs(args) {
|
|
26
|
+
let folder;
|
|
27
|
+
let slugOverride;
|
|
28
|
+
// Default model is resolved at run time via `resolveCliModel`, mirroring
|
|
29
|
+
// `duet run`'s actor model selection. Env var `DUET_MODEL` is honored by
|
|
30
|
+
// the resolver itself; we keep `--model` as the user's explicit override.
|
|
31
|
+
let model;
|
|
32
|
+
let dbPath = DEFAULT_MEMORY_DB_PATH;
|
|
33
|
+
let waitBudgetMs;
|
|
34
|
+
for (let i = 0; i < args.length; i++) {
|
|
35
|
+
const arg = args[i];
|
|
36
|
+
switch (arg) {
|
|
37
|
+
case "--slug":
|
|
38
|
+
if (!args[i + 1] || args[i + 1]?.startsWith("-"))
|
|
39
|
+
fail(`Missing value for ${arg}`);
|
|
40
|
+
slugOverride = args[++i];
|
|
41
|
+
break;
|
|
42
|
+
case "--model":
|
|
43
|
+
if (!args[i + 1] || args[i + 1]?.startsWith("-"))
|
|
44
|
+
fail(`Missing value for ${arg}`);
|
|
45
|
+
model = args[++i];
|
|
46
|
+
break;
|
|
47
|
+
case "--db":
|
|
48
|
+
if (!args[i + 1] || args[i + 1]?.startsWith("-"))
|
|
49
|
+
fail(`Missing value for ${arg}`);
|
|
50
|
+
dbPath = resolveUserPath(args[++i]);
|
|
51
|
+
break;
|
|
52
|
+
case "--wait": {
|
|
53
|
+
const raw = args[++i];
|
|
54
|
+
const seconds = Number(raw);
|
|
55
|
+
if (!Number.isFinite(seconds) || seconds < 0) {
|
|
56
|
+
fail(`Invalid --wait value: ${raw} (expected non-negative number of seconds)`);
|
|
57
|
+
}
|
|
58
|
+
waitBudgetMs = Math.round(seconds * 1000);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case "--help":
|
|
62
|
+
case "-h":
|
|
63
|
+
printTrainHelp();
|
|
64
|
+
return undefined;
|
|
65
|
+
default:
|
|
66
|
+
if (arg.startsWith("-"))
|
|
67
|
+
fail(`Unknown train option: ${arg}`);
|
|
68
|
+
if (folder !== undefined)
|
|
69
|
+
fail(`Unexpected extra argument: ${arg}`);
|
|
70
|
+
folder = arg;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!folder)
|
|
74
|
+
fail("duet train requires a <folder> argument");
|
|
75
|
+
const resolved = path.resolve(folder);
|
|
76
|
+
const slug = sanitizeSlug(slugOverride ?? path.basename(resolved));
|
|
77
|
+
if (slug.length === 0) {
|
|
78
|
+
fail(`Could not derive a slug from "${folder}"; pass --slug <name>`);
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
folder: resolved,
|
|
82
|
+
slug,
|
|
83
|
+
model,
|
|
84
|
+
dbPath,
|
|
85
|
+
...(waitBudgetMs !== undefined ? { waitBudgetMs } : {}),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Verbatim system prompt for the synthesis sub-agent. The agent's cwd is
|
|
90
|
+
* the corpus folder, so file-reading tools resolve relative paths against
|
|
91
|
+
* it directly.
|
|
92
|
+
*/
|
|
93
|
+
const TRAIN_SYSTEM_PROMPT = dedent `
|
|
94
|
+
You are synthesizing a project corpus into a single durable memory
|
|
95
|
+
observation.
|
|
96
|
+
|
|
97
|
+
The working directory IS the corpus. Read every file that looks
|
|
98
|
+
substantive — markdown, plain text, CSVs, PDFs, spreadsheets, images,
|
|
99
|
+
source code. Skip lockfiles, build artifacts, node_modules, .git, and
|
|
100
|
+
other obvious noise. Use your file-reading tools (read, ls, grep) to
|
|
101
|
+
explore until you understand what this corpus is about.
|
|
102
|
+
|
|
103
|
+
Extract EXHAUSTIVELY, not as a summary. A weaker reader of this corpus
|
|
104
|
+
tends to keep the headline claim and silently drop the specifics fused
|
|
105
|
+
to it — that is the failure to avoid. As you read, preserve EVERY
|
|
106
|
+
load-bearing concrete fact:
|
|
107
|
+
- Every number, quantity, percentage, version, and date — including
|
|
108
|
+
the second and third value in any enumeration (if a fact says
|
|
109
|
+
"X across A, B, and C", keep A, B, AND C, never just A).
|
|
110
|
+
- Every named feature, product, option, tool, attribute, and API —
|
|
111
|
+
by its exact name, not a generic paraphrase.
|
|
112
|
+
- Every member of a list, and the qualifier attached to each member
|
|
113
|
+
(if every option "supports both X and Y", state that for the set).
|
|
114
|
+
- Exact syntax, identifiers, code values, and config snippets as
|
|
115
|
+
written (e.g. a property name and its literal example value).
|
|
116
|
+
Never collapse an enumeration to its first item and never generalize a
|
|
117
|
+
specific into a category. When in doubt, include the detail.
|
|
118
|
+
|
|
119
|
+
When you have read enough to write an accurate synthesis, produce
|
|
120
|
+
EXACTLY one file at the cwd root and nothing else:
|
|
121
|
+
|
|
122
|
+
\`.duet-train.json\` — a JSON object with exactly two string fields:
|
|
123
|
+
{
|
|
124
|
+
"headline": "<short title for this corpus, under 120 characters, no trailing punctuation>",
|
|
125
|
+
"observationContent": "<dense, concrete, durable, high-priority knowledge an agent would need to act on this material. Use as much length as it takes to retain every load-bearing fact above — completeness beats brevity — but keep it a single standalone memory entry of tight prose, not a document dump. No preamble, no 'this corpus contains' framing.>"
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
Do NOT write \`AGENTS.md\` or any other file. The JSON handoff is the
|
|
129
|
+
only deliverable. Neither field may be empty. After writing the file,
|
|
130
|
+
stop. You do not need to say anything else.
|
|
131
|
+
`;
|
|
132
|
+
/**
|
|
133
|
+
* Launch the duet agent inside the corpus folder and let it produce a
|
|
134
|
+
* `.duet-train.json` handoff file. Returns the parsed synthesis result.
|
|
135
|
+
* The handoff file is removed before launch (so a leftover from a failed
|
|
136
|
+
* run can't be mistaken for fresh output) and after a successful read
|
|
137
|
+
* (so it doesn't leak into the corpus or the archive).
|
|
138
|
+
*/
|
|
139
|
+
async function runAgentSynthesis(options, io) {
|
|
140
|
+
const handoffPath = path.join(options.folder, ".duet-train.json");
|
|
141
|
+
await rm(handoffPath, { force: true });
|
|
142
|
+
const config = {
|
|
143
|
+
cwd: options.folder,
|
|
144
|
+
model: options.model,
|
|
145
|
+
// Don't write to the durable memory DB during synthesis; the train
|
|
146
|
+
// command owns the single observation row that lands at the end.
|
|
147
|
+
memoryDbPath: false,
|
|
148
|
+
// Synthesis must not be steered by the corpus's own AGENTS.md (if
|
|
149
|
+
// present) — the agent reads files as data, not as instructions.
|
|
150
|
+
systemPromptFiles: [],
|
|
151
|
+
// Avoid local user-skill drift influencing what the sub-agent does.
|
|
152
|
+
skillDiscovery: { includeDefaults: false },
|
|
153
|
+
systemInstructions: TRAIN_SYSTEM_PROMPT,
|
|
154
|
+
mode: "agent",
|
|
155
|
+
};
|
|
156
|
+
const manager = new SessionManager(config);
|
|
157
|
+
// Quietly surface what the sub-agent is doing so a long synthesis run
|
|
158
|
+
// doesn't look frozen. We only relay tool calls (the visible work) and
|
|
159
|
+
// any system-level messages; assistant text and reasoning are skipped.
|
|
160
|
+
const unsubscribe = manager.subscribe(({ event }) => {
|
|
161
|
+
if (event.type === "step") {
|
|
162
|
+
const step = event.step;
|
|
163
|
+
if (step.type === "tool_call" && step.status === "running") {
|
|
164
|
+
io.stderr.write(`[agent] ${step.toolName}\n`);
|
|
165
|
+
}
|
|
166
|
+
else if (step.type === "system") {
|
|
167
|
+
io.stderr.write(`[agent] ${step.message}\n`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
else if (event.type === "system") {
|
|
171
|
+
io.stderr.write(`[agent] ${event.message}\n`);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
try {
|
|
175
|
+
const session = manager.create({
|
|
176
|
+
prompt: `Synthesize the corpus in this directory. Slug: ${options.slug}.`,
|
|
177
|
+
});
|
|
178
|
+
const terminal = await session.waitForTerminal();
|
|
179
|
+
if (terminal.type !== "complete") {
|
|
180
|
+
throw new Error(`train: agent terminated with type=${terminal.type}; expected "complete"`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
finally {
|
|
184
|
+
unsubscribe();
|
|
185
|
+
await manager.dispose();
|
|
186
|
+
}
|
|
187
|
+
const raw = await readFile(handoffPath, "utf8").catch(() => {
|
|
188
|
+
throw new Error(`train: agent did not produce ${handoffPath}. Re-run with --model <stronger model> if the model is too small.`);
|
|
189
|
+
});
|
|
190
|
+
await rm(handoffPath, { force: true });
|
|
191
|
+
let parsed;
|
|
192
|
+
try {
|
|
193
|
+
parsed = JSON.parse(raw);
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
throw new Error(`train: .duet-train.json was not valid JSON: ${err.message}`);
|
|
197
|
+
}
|
|
198
|
+
if (!parsed || typeof parsed !== "object") {
|
|
199
|
+
throw new Error(`train: .duet-train.json must be a JSON object, got ${typeof parsed}`);
|
|
200
|
+
}
|
|
201
|
+
const { headline, observationContent } = parsed;
|
|
202
|
+
if (typeof headline !== "string" || headline.trim().length === 0) {
|
|
203
|
+
throw new Error(`train: .duet-train.json missing non-empty string "headline"`);
|
|
204
|
+
}
|
|
205
|
+
if (typeof observationContent !== "string" || observationContent.trim().length === 0) {
|
|
206
|
+
throw new Error(`train: .duet-train.json missing non-empty string "observationContent"`);
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
headline: headline.trim(),
|
|
210
|
+
observationContent: observationContent.trim(),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Run `duet train <folder>` — launch a sub-agent that reads the corpus
|
|
215
|
+
* and writes a structured handoff file (.duet-train.json), persist the
|
|
216
|
+
* synthesized observation into durable memory (replacing any prior
|
|
217
|
+
* `train:<slug>` row), archive the corpus under `~/.duet/train/<memoryId>/`,
|
|
218
|
+
* and verify both side effects landed.
|
|
219
|
+
*/
|
|
220
|
+
export async function runTrainCommand(args, io = { stdout: process.stdout, stderr: process.stderr }) {
|
|
221
|
+
const options = parseTrainArgs(args);
|
|
222
|
+
if (!options)
|
|
223
|
+
return;
|
|
224
|
+
// Post-parse failures throw instead of calling `fail()`: the CLI entry
|
|
225
|
+
// (`runCli`) prints them identically, while in-process callers (the
|
|
226
|
+
// eval harness, the model sweep) can catch them and run their cleanup.
|
|
227
|
+
const folderStat = await stat(options.folder).catch(() => undefined);
|
|
228
|
+
if (!folderStat || !folderStat.isDirectory()) {
|
|
229
|
+
throw new Error(`Folder not found or not a directory: ${options.folder}`);
|
|
230
|
+
}
|
|
231
|
+
// Resolve the model the same way `duet run` does so shorthands and
|
|
232
|
+
// provider routing behave identically. Env files in the corpus folder
|
|
233
|
+
// can supply provider credentials; load them before resolution.
|
|
234
|
+
const dotenvKeys = loadCliEnvFiles(options.folder);
|
|
235
|
+
const modelResolution = resolveCliModel(options.model, dotenvKeys);
|
|
236
|
+
const resolvedModel = modelResolution.modelName;
|
|
237
|
+
const session = new MemorySession({
|
|
238
|
+
path: options.dbPath,
|
|
239
|
+
openOptions: {
|
|
240
|
+
init: async (db) => {
|
|
241
|
+
await runMigrations(db);
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
...(options.waitBudgetMs !== undefined ? { lockWaitBudgetMs: options.waitBudgetMs } : {}),
|
|
245
|
+
idleCloseMs: 60_000,
|
|
246
|
+
});
|
|
247
|
+
const removeShutdownHandlers = installShutdownHandlers(() => session.dispose());
|
|
248
|
+
try {
|
|
249
|
+
await session.withDb(async () => { });
|
|
250
|
+
io.stderr.write(`[synthesize] model=${resolvedModel}\n`);
|
|
251
|
+
const synthesis = await runAgentSynthesis({ folder: options.folder, slug: options.slug, model: resolvedModel }, io);
|
|
252
|
+
// Insert the new row BEFORE deleting priors: if the process dies
|
|
253
|
+
// between the two steps the worst case is a duplicate `train:<slug>`
|
|
254
|
+
// row (cleaned up by the next train run), never zero rows. The row is
|
|
255
|
+
// written as `kind: "manual"` so the loader's manualBias ranks it at
|
|
256
|
+
// the top of the global pack (a manual row with no sessionId never
|
|
257
|
+
// matches a live session, so it lands in the global pack via the
|
|
258
|
+
// loader's NULL-session handling), and `duet memory reflect`'s prune
|
|
259
|
+
// preserves it by kind regardless of age.
|
|
260
|
+
const trainTag = `train:${options.slug}`;
|
|
261
|
+
const observedDate = new Date().toISOString().slice(0, 10);
|
|
262
|
+
const observation = await appendObservation(session, {
|
|
263
|
+
kind: "manual",
|
|
264
|
+
priority: "high",
|
|
265
|
+
source: { kind: "system" },
|
|
266
|
+
content: synthesis.observationContent,
|
|
267
|
+
tags: ["train", trainTag],
|
|
268
|
+
observedDate,
|
|
269
|
+
});
|
|
270
|
+
if (!observation) {
|
|
271
|
+
throw new Error(`Could not write training memory to ${options.dbPath} (lock contention).`);
|
|
272
|
+
}
|
|
273
|
+
// SELECT + DELETE share one `withDb` so the replace step is atomic
|
|
274
|
+
// against peer writers and lock failure surfaces in one place.
|
|
275
|
+
// Invariant after this block: exactly one `train:<slug>` row — the
|
|
276
|
+
// one written above.
|
|
277
|
+
const priorIds = await session.withDb(async (db) => {
|
|
278
|
+
const result = await db.query("SELECT id, tags_json FROM observations");
|
|
279
|
+
const ids = [];
|
|
280
|
+
for (const row of result.rows) {
|
|
281
|
+
if (row.id === observation.id)
|
|
282
|
+
continue;
|
|
283
|
+
let tags;
|
|
284
|
+
try {
|
|
285
|
+
tags = JSON.parse(row.tags_json);
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (Array.isArray(tags) && tags.includes(trainTag)) {
|
|
291
|
+
ids.push(row.id);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
for (const id of ids) {
|
|
295
|
+
await db.query("DELETE FROM observations WHERE id = $1", [id]);
|
|
296
|
+
}
|
|
297
|
+
return ids;
|
|
298
|
+
});
|
|
299
|
+
if (priorIds === undefined) {
|
|
300
|
+
throw new Error(`Could not remove prior training rows from ${options.dbPath} (lock contention); ` +
|
|
301
|
+
`the new row ${observation.id} was written. Re-run train to clean up the duplicate.`);
|
|
302
|
+
}
|
|
303
|
+
for (const id of priorIds) {
|
|
304
|
+
await removeArchive(id);
|
|
305
|
+
}
|
|
306
|
+
const replacedSuffix = priorIds.length > 0 ? ` (replaced ${priorIds.length} prior row(s))` : "";
|
|
307
|
+
io.stderr.write(`[persist] memory id=${observation.id}${replacedSuffix}\n`);
|
|
308
|
+
// `runAgentSynthesis` already removed `.duet-train.json`; the
|
|
309
|
+
// walker's skip list is defense in depth.
|
|
310
|
+
const archivedFiles = await collectArchiveFiles(options.folder);
|
|
311
|
+
const manifest = {
|
|
312
|
+
memoryId: observation.id,
|
|
313
|
+
slug: options.slug,
|
|
314
|
+
createdAt: observation.createdAt,
|
|
315
|
+
sourceFolder: options.folder,
|
|
316
|
+
model: resolvedModel,
|
|
317
|
+
headline: synthesis.headline,
|
|
318
|
+
files: archivedFiles.map((file) => ({
|
|
319
|
+
relPath: file.relPath,
|
|
320
|
+
bytes: file.bytes,
|
|
321
|
+
sha256: file.sha256,
|
|
322
|
+
})),
|
|
323
|
+
};
|
|
324
|
+
const archivePath = await writeArchive({
|
|
325
|
+
memoryId: observation.id,
|
|
326
|
+
files: archivedFiles,
|
|
327
|
+
manifest,
|
|
328
|
+
});
|
|
329
|
+
const verifyRows = await session.withDb(async (db) => db.query("SELECT content FROM observations WHERE id = $1", [
|
|
330
|
+
observation.id,
|
|
331
|
+
]));
|
|
332
|
+
if (verifyRows === undefined) {
|
|
333
|
+
throw new Error(`Could not verify training memory at ${options.dbPath} (lock contention).`);
|
|
334
|
+
}
|
|
335
|
+
if (verifyRows.rows[0]?.content !== synthesis.observationContent) {
|
|
336
|
+
throw new Error(`Verification failed: observation ${observation.id} did not round-trip through the DB.`);
|
|
337
|
+
}
|
|
338
|
+
io.stdout.write(`Trained "${synthesis.headline}"\n`);
|
|
339
|
+
io.stdout.write(` memory id : ${observation.id}\n`);
|
|
340
|
+
io.stdout.write(` archive : ${archivePath}\n`);
|
|
341
|
+
io.stdout.write(` files : ${archivedFiles.length} file(s)\n`);
|
|
342
|
+
io.stdout.write(` model : ${resolvedModel}\n`);
|
|
343
|
+
io.stdout.write(`\n${synthesis.observationContent}\n`);
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
if (error instanceof MemoryLockTimeoutError) {
|
|
347
|
+
throw new Error(`Memory database at ${error.dataDir} is still locked by duet pid ${error.holderPid} after ${error.budgetMs / 1000}s. Stop that process (or pass --wait <seconds> to wait longer) and retry.`);
|
|
348
|
+
}
|
|
349
|
+
throw error;
|
|
350
|
+
}
|
|
351
|
+
finally {
|
|
352
|
+
removeShutdownHandlers();
|
|
353
|
+
await session.dispose();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
//# sourceMappingURL=train.js.map
|