@duetso/agent 0.1.53 → 0.1.55

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 (106) hide show
  1. package/README.md +13 -2
  2. package/dist/package.json +1 -1
  3. package/dist/src/cli/help.js +1 -1
  4. package/dist/src/cli/memory-db.d.ts.map +1 -1
  5. package/dist/src/cli/memory-db.js +14 -5
  6. package/dist/src/cli/memory-db.js.map +1 -1
  7. package/dist/src/cli/memory-tui.js +1 -1
  8. package/dist/src/cli/memory-tui.js.map +1 -1
  9. package/dist/src/cli/resume-hint.d.ts +1 -1
  10. package/dist/src/cli/resume-hint.d.ts.map +1 -1
  11. package/dist/src/cli/resume-hint.js +2 -2
  12. package/dist/src/cli/resume-hint.js.map +1 -1
  13. package/dist/src/cli/run.d.ts +1 -1
  14. package/dist/src/cli/run.d.ts.map +1 -1
  15. package/dist/src/cli/run.js +7 -6
  16. package/dist/src/cli/run.js.map +1 -1
  17. package/dist/src/memory/context-pack.d.ts +29 -0
  18. package/dist/src/memory/context-pack.d.ts.map +1 -0
  19. package/dist/src/memory/context-pack.js +40 -0
  20. package/dist/src/memory/context-pack.js.map +1 -0
  21. package/dist/src/memory/embedding-worker.d.ts +26 -0
  22. package/dist/src/memory/embedding-worker.d.ts.map +1 -0
  23. package/dist/src/memory/embedding-worker.js +145 -0
  24. package/dist/src/memory/embedding-worker.js.map +1 -0
  25. package/dist/src/memory/embedding.d.ts +59 -0
  26. package/dist/src/memory/embedding.d.ts.map +1 -0
  27. package/dist/src/memory/embedding.js +126 -0
  28. package/dist/src/memory/embedding.js.map +1 -0
  29. package/dist/src/memory/loader.d.ts +81 -0
  30. package/dist/src/memory/loader.d.ts.map +1 -0
  31. package/dist/src/memory/loader.js +162 -0
  32. package/dist/src/memory/loader.js.map +1 -0
  33. package/dist/src/memory/migrations.d.ts +54 -0
  34. package/dist/src/memory/migrations.d.ts.map +1 -0
  35. package/dist/src/memory/migrations.js +199 -0
  36. package/dist/src/memory/migrations.js.map +1 -0
  37. package/dist/src/memory/observational.d.ts +18 -0
  38. package/dist/src/memory/observational.d.ts.map +1 -1
  39. package/dist/src/memory/observational.js +63 -14
  40. package/dist/src/memory/observational.js.map +1 -1
  41. package/dist/src/memory/pglite.d.ts +5 -5
  42. package/dist/src/memory/pglite.d.ts.map +1 -1
  43. package/dist/src/memory/pglite.js +20 -6
  44. package/dist/src/memory/pglite.js.map +1 -1
  45. package/dist/src/memory/recall.d.ts +49 -0
  46. package/dist/src/memory/recall.d.ts.map +1 -0
  47. package/dist/src/memory/recall.js +185 -0
  48. package/dist/src/memory/recall.js.map +1 -0
  49. package/dist/src/memory/storage.d.ts +36 -1
  50. package/dist/src/memory/storage.d.ts.map +1 -1
  51. package/dist/src/memory/storage.js +68 -14
  52. package/dist/src/memory/storage.js.map +1 -1
  53. package/dist/src/memory/store.d.ts +33 -1
  54. package/dist/src/memory/store.d.ts.map +1 -1
  55. package/dist/src/memory/store.js +22 -16
  56. package/dist/src/memory/store.js.map +1 -1
  57. package/dist/src/session/session.d.ts.map +1 -1
  58. package/dist/src/session/session.js +5 -1
  59. package/dist/src/session/session.js.map +1 -1
  60. package/dist/src/tui/app.d.ts.map +1 -1
  61. package/dist/src/tui/app.js +361 -30
  62. package/dist/src/tui/app.js.map +1 -1
  63. package/dist/src/tui/autocomplete.d.ts.map +1 -1
  64. package/dist/src/tui/autocomplete.js +5 -0
  65. package/dist/src/tui/autocomplete.js.map +1 -1
  66. package/dist/src/tui/clipboard.d.ts +28 -0
  67. package/dist/src/tui/clipboard.d.ts.map +1 -0
  68. package/dist/src/tui/clipboard.js +95 -0
  69. package/dist/src/tui/clipboard.js.map +1 -0
  70. package/dist/src/tui/history.d.ts +14 -0
  71. package/dist/src/tui/history.d.ts.map +1 -1
  72. package/dist/src/tui/history.js +21 -0
  73. package/dist/src/tui/history.js.map +1 -1
  74. package/dist/src/tui/recent-sessions.d.ts +51 -0
  75. package/dist/src/tui/recent-sessions.d.ts.map +1 -0
  76. package/dist/src/tui/recent-sessions.js +150 -0
  77. package/dist/src/tui/recent-sessions.js.map +1 -0
  78. package/dist/src/tui/starters.d.ts +92 -0
  79. package/dist/src/tui/starters.d.ts.map +1 -0
  80. package/dist/src/tui/starters.js +196 -0
  81. package/dist/src/tui/starters.js.map +1 -0
  82. package/dist/src/tui/theme.d.ts +2 -2
  83. package/dist/src/tui/theme.d.ts.map +1 -1
  84. package/dist/src/tui/theme.js +2 -2
  85. package/dist/src/tui/theme.js.map +1 -1
  86. package/dist/src/tui/transcript-log.d.ts +45 -0
  87. package/dist/src/tui/transcript-log.d.ts.map +1 -0
  88. package/dist/src/tui/transcript-log.js +76 -0
  89. package/dist/src/tui/transcript-log.js.map +1 -0
  90. package/dist/src/turn-runner/tools.d.ts +28 -1
  91. package/dist/src/turn-runner/tools.d.ts.map +1 -1
  92. package/dist/src/turn-runner/tools.js +135 -3
  93. package/dist/src/turn-runner/tools.js.map +1 -1
  94. package/dist/src/turn-runner/turn-runner.d.ts +1 -0
  95. package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
  96. package/dist/src/turn-runner/turn-runner.js +70 -3
  97. package/dist/src/turn-runner/turn-runner.js.map +1 -1
  98. package/dist/src/types/config.d.ts +12 -0
  99. package/dist/src/types/config.d.ts.map +1 -1
  100. package/dist/src/types/memory.d.ts +59 -21
  101. package/dist/src/types/memory.d.ts.map +1 -1
  102. package/package.json +1 -1
  103. package/dist/src/memory/schema.d.ts +0 -7
  104. package/dist/src/memory/schema.d.ts.map +0 -1
  105. package/dist/src/memory/schema.js +0 -21
  106. package/dist/src/memory/schema.js.map +0 -1
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Client for the Duet embedding endpoint.
3
+ *
4
+ * Routes through the Duet public API (`POST <app>/api/v1/embed`) gated
5
+ * by the user's existing `DUET_API_KEY`. Logged-in users get embeddings
6
+ * for free as a CLI perk; we deliberately do not expose a way to plug
7
+ * in a different provider key here so the runtime stays single-path.
8
+ *
9
+ * Behavior:
10
+ * - Inputs are batched up to `EMBEDDING_BATCH_LIMIT` per HTTP call so
11
+ * a single backfill batch never goes over the request body cap.
12
+ * - 5xx responses and network errors retry with exponential backoff;
13
+ * 4xx responses (auth, malformed) fail fast since retrying will not
14
+ * change the outcome.
15
+ * - When `DUET_API_KEY` is missing the function throws a typed error
16
+ * so callers can degrade to keyword-only retrieval (the recall_memory
17
+ * tool catches it and logs once; the backfill worker logs and
18
+ * sleeps until the user logs in).
19
+ */
20
+ /** Default embedding model identifier sent with every request. */
21
+ export declare const DEFAULT_EMBEDDING_MODEL = "openai/text-embedding-3-small";
22
+ /** Maximum input strings sent in a single embedding request. */
23
+ export declare const EMBEDDING_BATCH_LIMIT = 100;
24
+ /**
25
+ * Callable shape consumed by the backfill worker and the recall_memory
26
+ * tool. Inputs map 1:1 to output vectors.
27
+ */
28
+ export type EmbedFn = (inputs: string[]) => Promise<number[][]>;
29
+ export declare class EmbeddingUnavailableError extends Error {
30
+ constructor(message: string);
31
+ }
32
+ export interface CreateEmbeddingClientOptions {
33
+ /** Embedding model identifier sent with each request. */
34
+ model?: string;
35
+ /**
36
+ * Override the API key resolver. Defaults to reading `DUET_API_KEY`
37
+ * from `process.env`. Tests inject a fixed value to exercise the
38
+ * client without touching environment state.
39
+ */
40
+ apiKey?: string | (() => string | undefined);
41
+ /**
42
+ * Override the base URL. Defaults to `resolveDuetAppBaseUrl()` so the
43
+ * client follows the same staging/production routing as the rest of
44
+ * the CLI.
45
+ */
46
+ baseUrl?: string;
47
+ /**
48
+ * Override the fetch implementation. Tests use this to assert request
49
+ * shape and stub responses without standing up a real server.
50
+ */
51
+ fetch?: typeof fetch;
52
+ }
53
+ /**
54
+ * Build an `EmbedFn` bound to the Duet embedding endpoint. Constructed
55
+ * once per process and reused for every backfill batch and every
56
+ * `recall_memory` query so connection reuse can amortize TLS setup.
57
+ */
58
+ export declare function createEmbeddingClient(options?: CreateEmbeddingClientOptions): EmbedFn;
59
+ //# sourceMappingURL=embedding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedding.d.ts","sourceRoot":"","sources":["../../../src/memory/embedding.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,kEAAkE;AAClE,eAAO,MAAM,uBAAuB,kCAAkC,CAAC;AACvE,gEAAgE;AAChE,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAMzC;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAEhE,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,4BAA4B;IAC3C,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7C;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAmCzF"}
@@ -0,0 +1,126 @@
1
+ import { resolveDuetAppBaseUrl } from "../lib/duet-app-url.js";
2
+ /**
3
+ * Client for the Duet embedding endpoint.
4
+ *
5
+ * Routes through the Duet public API (`POST <app>/api/v1/embed`) gated
6
+ * by the user's existing `DUET_API_KEY`. Logged-in users get embeddings
7
+ * for free as a CLI perk; we deliberately do not expose a way to plug
8
+ * in a different provider key here so the runtime stays single-path.
9
+ *
10
+ * Behavior:
11
+ * - Inputs are batched up to `EMBEDDING_BATCH_LIMIT` per HTTP call so
12
+ * a single backfill batch never goes over the request body cap.
13
+ * - 5xx responses and network errors retry with exponential backoff;
14
+ * 4xx responses (auth, malformed) fail fast since retrying will not
15
+ * change the outcome.
16
+ * - When `DUET_API_KEY` is missing the function throws a typed error
17
+ * so callers can degrade to keyword-only retrieval (the recall_memory
18
+ * tool catches it and logs once; the backfill worker logs and
19
+ * sleeps until the user logs in).
20
+ */
21
+ /** Default embedding model identifier sent with every request. */
22
+ export const DEFAULT_EMBEDDING_MODEL = "openai/text-embedding-3-small";
23
+ /** Maximum input strings sent in a single embedding request. */
24
+ export const EMBEDDING_BATCH_LIMIT = 100;
25
+ const ENDPOINT_PATH = "/api/v1/embed";
26
+ const MAX_RETRIES = 3;
27
+ const BASE_BACKOFF_MS = 500;
28
+ export class EmbeddingUnavailableError extends Error {
29
+ constructor(message) {
30
+ super(message);
31
+ this.name = "EmbeddingUnavailableError";
32
+ }
33
+ }
34
+ /**
35
+ * Build an `EmbedFn` bound to the Duet embedding endpoint. Constructed
36
+ * once per process and reused for every backfill batch and every
37
+ * `recall_memory` query so connection reuse can amortize TLS setup.
38
+ */
39
+ export function createEmbeddingClient(options = {}) {
40
+ const model = options.model ?? DEFAULT_EMBEDDING_MODEL;
41
+ const baseUrl = options.baseUrl ?? resolveDuetAppBaseUrl();
42
+ const fetchImpl = options.fetch ?? fetch;
43
+ return async (inputs) => {
44
+ if (inputs.length === 0)
45
+ return [];
46
+ const apiKey = resolveApiKey(options.apiKey);
47
+ if (!apiKey) {
48
+ // Throwing a typed error lets callers branch on graceful
49
+ // degradation without string-matching the message.
50
+ throw new EmbeddingUnavailableError("DUET_API_KEY is not set; embeddings unavailable. Run `duet login` to enable hybrid memory retrieval.");
51
+ }
52
+ const results = [];
53
+ for (let offset = 0; offset < inputs.length; offset += EMBEDDING_BATCH_LIMIT) {
54
+ const slice = inputs.slice(offset, offset + EMBEDDING_BATCH_LIMIT);
55
+ const vectors = await postBatch({
56
+ url: `${baseUrl}${ENDPOINT_PATH}`,
57
+ apiKey,
58
+ model,
59
+ inputs: slice,
60
+ fetchImpl,
61
+ });
62
+ if (vectors.length !== slice.length) {
63
+ throw new Error(`Embedding response length (${vectors.length}) did not match request size (${slice.length})`);
64
+ }
65
+ results.push(...vectors);
66
+ }
67
+ return results;
68
+ };
69
+ }
70
+ async function postBatch(options) {
71
+ let lastError;
72
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
73
+ try {
74
+ const response = await options.fetchImpl(options.url, {
75
+ method: "POST",
76
+ headers: {
77
+ authorization: `Bearer ${options.apiKey}`,
78
+ "content-type": "application/json",
79
+ },
80
+ body: JSON.stringify({ input: options.inputs, model: options.model }),
81
+ });
82
+ if (response.ok) {
83
+ const body = (await response.json());
84
+ return body.embeddings;
85
+ }
86
+ // 4xx errors (auth, malformed input) will not improve on retry;
87
+ // surface them immediately so callers see the real cause.
88
+ if (response.status >= 400 && response.status < 500) {
89
+ const detail = await safeReadText(response);
90
+ throw new EmbeddingUnavailableError(`Embedding endpoint returned ${response.status}: ${detail}`);
91
+ }
92
+ lastError = new Error(`Embedding endpoint returned ${response.status}`);
93
+ }
94
+ catch (error) {
95
+ // Re-throw typed errors immediately; only retry on transport-level failures.
96
+ if (error instanceof EmbeddingUnavailableError)
97
+ throw error;
98
+ lastError = error;
99
+ }
100
+ if (attempt < MAX_RETRIES - 1) {
101
+ await sleep(BASE_BACKOFF_MS * 2 ** attempt);
102
+ }
103
+ }
104
+ throw lastError instanceof Error
105
+ ? lastError
106
+ : new Error(`Embedding request failed after ${MAX_RETRIES} attempts`);
107
+ }
108
+ function resolveApiKey(override) {
109
+ if (typeof override === "function")
110
+ return override();
111
+ if (typeof override === "string")
112
+ return override;
113
+ return process.env.DUET_API_KEY;
114
+ }
115
+ async function safeReadText(response) {
116
+ try {
117
+ return await response.text();
118
+ }
119
+ catch {
120
+ return "<unreadable response body>";
121
+ }
122
+ }
123
+ function sleep(ms) {
124
+ return new Promise((resolve) => setTimeout(resolve, ms));
125
+ }
126
+ //# sourceMappingURL=embedding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedding.js","sourceRoot":"","sources":["../../../src/memory/embedding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ import type { PGlite } from "@electric-sql/pglite";
2
+ import type { Observation } from "../types/memory.js";
3
+ export interface ScoreInputs {
4
+ /** Wall-clock time at which scoring is happening. Same value across one ranking pass. */
5
+ now: number;
6
+ /** Half-life for the recency exponential decay. Same units as createdAt (milliseconds). */
7
+ halfLifeMs: number;
8
+ /** Multiplier applied when an observation is a reflection rather than a raw observation. */
9
+ reflectionBias: number;
10
+ }
11
+ /**
12
+ * Combined relevance score used to rank the global layer.
13
+ *
14
+ * score = priorityWeight * recencyDecay * kindBias
15
+ *
16
+ * - priorityWeight: 3 / 2 / 1 for high / medium / low.
17
+ * - recencyDecay: `0.5 ^ (ageMs / halfLifeMs)`. Equals 1.0 at age 0,
18
+ * 0.5 at one half-life, ~0.0625 at four half-lives. Smooth and
19
+ * parameterizable; no cliffs.
20
+ * - kindBias: `reflectionBias` for reflections, 1.0 for raw
21
+ * observations. Default 1.3 keeps reflections preferred at matched
22
+ * priority/recency without shutting raw observations out.
23
+ *
24
+ * Examples at the default 7d half-life and 1.3 reflection bias:
25
+ * - high reflection, 0d: 3 * 1.0 * 1.3 = 3.9
26
+ * - high observation, 0d: 3 * 1.0 * 1.0 = 3.0
27
+ * - medium reflection, 0d: 2 * 1.0 * 1.3 = 2.6
28
+ * - high reflection, 7d: 3 * 0.5 * 1.3 = 1.95
29
+ * - low observation, 30d: 1 * 0.0508 * 1.0 = 0.05 (effectively pruned)
30
+ */
31
+ export declare function score(observation: Observation, inputs: ScoreInputs): number;
32
+ export interface LoadGlobalPackOptions {
33
+ /**
34
+ * Session whose rows are excluded from the global pack. The current
35
+ * runner's session lives in the local pack at full fidelity, so
36
+ * counting its rows in the global ranking would double-count and
37
+ * push out cross-session signal. Pass `undefined` to include every
38
+ * row (e.g. for the `recall_memory` tool when the user wants
39
+ * unrestricted search).
40
+ */
41
+ excludeSessionId?: string;
42
+ /** Maximum tokens of `content` packed into the result. Defaults to 8000. */
43
+ tokenBudget?: number;
44
+ /** Recency half-life in milliseconds. */
45
+ recencyHalfLifeMs: number;
46
+ /** Reflection bias multiplier. */
47
+ reflectionBias: number;
48
+ /**
49
+ * Override for `Date.now()`, used by tests so fixture data with
50
+ * fixed `created_at` values produces deterministic scores.
51
+ */
52
+ now?: number;
53
+ }
54
+ /**
55
+ * Load the highest-scoring rows from every session except the caller's,
56
+ * up to the token budget. Returns rows ordered by descending score so
57
+ * the renderer can preserve the ranking visually.
58
+ *
59
+ * Implementation note: the database query orders by `created_at DESC`
60
+ * (cheap with the `idx_obs_kind_priority_created` index) and a generous
61
+ * over-fetch limit; the actual ranking happens in JS so the recency
62
+ * decay term can be applied without trying to express `0.5 ^ x` in
63
+ * portable SQL. With a 8k-token budget and ~250-token rows this means
64
+ * a worst case of ~32 rows packed; the over-fetch keeps the planner
65
+ * cheap even when the budget is set higher.
66
+ */
67
+ export declare function loadGlobalPack(db: PGlite, options: LoadGlobalPackOptions): Promise<Observation[]>;
68
+ export interface LoadLocalPackOptions {
69
+ sessionId: string;
70
+ }
71
+ /**
72
+ * Load every row this session produced, ordered chronologically. The
73
+ * local pack represents the session's own compaction summary — the
74
+ * observations and reflections that replaced earlier transcript — so
75
+ * it is rendered whole regardless of size. Bounding the local layer
76
+ * is the existing observer/reflector pipeline's job: their thresholds
77
+ * keep this set from unbounded growth, and a reflection condenses it
78
+ * back down when it gets too large.
79
+ */
80
+ export declare function loadLocalPack(db: PGlite, options: LoadLocalPackOptions): Promise<Observation[]>;
81
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/memory/loader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAwC,MAAM,oBAAoB,CAAC;AAmC5F,MAAM,WAAW,WAAW;IAC1B,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAC;IACZ,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;IACnB,4FAA4F;IAC5F,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,KAAK,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAM3E;AAED,MAAM,WAAW,qBAAqB;IACpC;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yCAAyC;IACzC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,WAAW,EAAE,CAAC,CA6DxB;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,WAAW,EAAE,CAAC,CAUxB"}
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Loads ranked memory packs for rendering above the message tail.
3
+ *
4
+ * The loader splits durable memory into two layers along a single axis —
5
+ * does the row's session_id match the current runner's session?
6
+ *
7
+ * - Local layer: rows whose session_id equals the current session. The
8
+ * current session's compacted view of itself (observations + the
9
+ * reflection that may have replaced them). Rendered chronologically
10
+ * and unranked because it represents the conversation's own
11
+ * compaction summary; reordering it would lose the time signal.
12
+ *
13
+ * - Global layer: every other session's rows, ranked by
14
+ * `priority * recencyDecay * kindBias`. Reflections rank higher than
15
+ * raw observations through `reflectionBias`. Greedy-fitted to a
16
+ * fixed token budget so the prompt prefix stays bounded regardless
17
+ * of how big the durable memory database has grown.
18
+ *
19
+ * The two layers are intentionally rendered with separate headings so
20
+ * the model can tell what comes from this conversation versus what
21
+ * comes from accumulated cross-session knowledge. The render order is
22
+ * `system prompt -> global -> local -> message history`: the global
23
+ * pack is the most stable prefix (rebuilt only on compaction), local
24
+ * is medium-stability (replaced on each reflection), and the message
25
+ * tail is the only part that grows turn over turn.
26
+ */
27
+ const PRIORITY_WEIGHT = {
28
+ high: 3,
29
+ medium: 2,
30
+ low: 1,
31
+ };
32
+ /**
33
+ * Combined relevance score used to rank the global layer.
34
+ *
35
+ * score = priorityWeight * recencyDecay * kindBias
36
+ *
37
+ * - priorityWeight: 3 / 2 / 1 for high / medium / low.
38
+ * - recencyDecay: `0.5 ^ (ageMs / halfLifeMs)`. Equals 1.0 at age 0,
39
+ * 0.5 at one half-life, ~0.0625 at four half-lives. Smooth and
40
+ * parameterizable; no cliffs.
41
+ * - kindBias: `reflectionBias` for reflections, 1.0 for raw
42
+ * observations. Default 1.3 keeps reflections preferred at matched
43
+ * priority/recency without shutting raw observations out.
44
+ *
45
+ * Examples at the default 7d half-life and 1.3 reflection bias:
46
+ * - high reflection, 0d: 3 * 1.0 * 1.3 = 3.9
47
+ * - high observation, 0d: 3 * 1.0 * 1.0 = 3.0
48
+ * - medium reflection, 0d: 2 * 1.0 * 1.3 = 2.6
49
+ * - high reflection, 7d: 3 * 0.5 * 1.3 = 1.95
50
+ * - low observation, 30d: 1 * 0.0508 * 1.0 = 0.05 (effectively pruned)
51
+ */
52
+ export function score(observation, inputs) {
53
+ const priorityWeight = PRIORITY_WEIGHT[observation.priority];
54
+ const ageMs = Math.max(0, inputs.now - observation.createdAt);
55
+ const recencyDecay = Math.pow(0.5, ageMs / inputs.halfLifeMs);
56
+ const kindBias = observation.kind === "reflection" ? inputs.reflectionBias : 1.0;
57
+ return priorityWeight * recencyDecay * kindBias;
58
+ }
59
+ /**
60
+ * Load the highest-scoring rows from every session except the caller's,
61
+ * up to the token budget. Returns rows ordered by descending score so
62
+ * the renderer can preserve the ranking visually.
63
+ *
64
+ * Implementation note: the database query orders by `created_at DESC`
65
+ * (cheap with the `idx_obs_kind_priority_created` index) and a generous
66
+ * over-fetch limit; the actual ranking happens in JS so the recency
67
+ * decay term can be applied without trying to express `0.5 ^ x` in
68
+ * portable SQL. With a 8k-token budget and ~250-token rows this means
69
+ * a worst case of ~32 rows packed; the over-fetch keeps the planner
70
+ * cheap even when the budget is set higher.
71
+ */
72
+ export async function loadGlobalPack(db, options) {
73
+ const tokenBudget = options.tokenBudget ?? 8000;
74
+ const now = options.now ?? Date.now();
75
+ // Over-fetch by a comfortable factor so the JS-side scorer has enough
76
+ // candidates to find the top-N regardless of priority distribution.
77
+ // 4x the typical row count fitting in the budget keeps the SQL cheap
78
+ // while leaving headroom for a long tail of low-priority rows.
79
+ const overFetchLimit = Math.max(200, Math.ceil(tokenBudget / 50));
80
+ let result;
81
+ if (options.excludeSessionId === undefined) {
82
+ result = await db.query(`SELECT id, created_at, session_id, kind, observed_date, referenced_date,
83
+ relative_date, time_of_day, priority, source_json, content, tags_json
84
+ FROM observations
85
+ ORDER BY created_at DESC
86
+ LIMIT $1`, [overFetchLimit]);
87
+ }
88
+ else {
89
+ // NULL session_id rows (legacy, pre-sessionId tracking) are kept in
90
+ // the global pool because they cannot match any current session.
91
+ // Without `IS DISTINCT FROM` Postgres would treat NULL = X as NULL,
92
+ // which filters those legacy rows out of every query.
93
+ result = await db.query(`SELECT id, created_at, session_id, kind, observed_date, referenced_date,
94
+ relative_date, time_of_day, priority, source_json, content, tags_json
95
+ FROM observations
96
+ WHERE session_id IS DISTINCT FROM $1
97
+ ORDER BY created_at DESC
98
+ LIMIT $2`, [options.excludeSessionId, overFetchLimit]);
99
+ }
100
+ const candidates = result.rows.map(rowToObservation);
101
+ const scoreInputs = {
102
+ now,
103
+ halfLifeMs: options.recencyHalfLifeMs,
104
+ reflectionBias: options.reflectionBias,
105
+ };
106
+ const ranked = candidates
107
+ .map((observation) => ({ observation, score: score(observation, scoreInputs) }))
108
+ .sort((a, b) => b.score - a.score);
109
+ const packed = [];
110
+ let usedTokens = 0;
111
+ for (const entry of ranked) {
112
+ const tokens = estimateTokens(entry.observation.content);
113
+ if (usedTokens + tokens > tokenBudget) {
114
+ // Skip oversize rows but keep iterating: a smaller row later in
115
+ // the ranking may still fit, which avoids leaving the budget
116
+ // half-empty just because one high-scoring row was a long
117
+ // reflection.
118
+ continue;
119
+ }
120
+ packed.push(entry.observation);
121
+ usedTokens += tokens;
122
+ }
123
+ return packed;
124
+ }
125
+ /**
126
+ * Load every row this session produced, ordered chronologically. The
127
+ * local pack represents the session's own compaction summary — the
128
+ * observations and reflections that replaced earlier transcript — so
129
+ * it is rendered whole regardless of size. Bounding the local layer
130
+ * is the existing observer/reflector pipeline's job: their thresholds
131
+ * keep this set from unbounded growth, and a reflection condenses it
132
+ * back down when it gets too large.
133
+ */
134
+ export async function loadLocalPack(db, options) {
135
+ const result = await db.query(`SELECT id, created_at, session_id, kind, observed_date, referenced_date,
136
+ relative_date, time_of_day, priority, source_json, content, tags_json
137
+ FROM observations
138
+ WHERE session_id = $1
139
+ ORDER BY created_at ASC`, [options.sessionId]);
140
+ return result.rows.map(rowToObservation);
141
+ }
142
+ function rowToObservation(row) {
143
+ return {
144
+ id: row.id,
145
+ createdAt: row.created_at,
146
+ ...(row.session_id !== null ? { sessionId: row.session_id } : {}),
147
+ kind: row.kind,
148
+ observedDate: row.observed_date,
149
+ ...(row.referenced_date !== null ? { referencedDate: row.referenced_date } : {}),
150
+ ...(row.relative_date !== null ? { relativeDate: row.relative_date } : {}),
151
+ ...(row.time_of_day !== null ? { timeOfDay: row.time_of_day } : {}),
152
+ priority: row.priority,
153
+ source: JSON.parse(row.source_json),
154
+ content: row.content,
155
+ tags: JSON.parse(row.tags_json),
156
+ };
157
+ }
158
+ /** Rough char-to-token estimate matching the rest of the memory subsystem. */
159
+ function estimateTokens(text) {
160
+ return Math.ceil(text.length / 4);
161
+ }
162
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/memory/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import type { PGlite, Transaction } from "@electric-sql/pglite";
2
+ /**
3
+ * Forward-only schema migrations for the observational memory database.
4
+ *
5
+ * Why a real migration framework instead of bare `CREATE TABLE IF NOT
6
+ * EXISTS`: the memory schema is changing shape (adding `session_id` and
7
+ * `kind`, dropping `scope`, reshaping `source_json`) and a user upgrading
8
+ * Duet must have their existing rows transformed in place — dropping the
9
+ * column without rewriting the data would lose information, and there is
10
+ * no `duet memory migrate` CLI step we want to require.
11
+ *
12
+ * Design notes:
13
+ * - Migrations run inside `loadStoredMemory()` and `MemoryDb.open()` so
14
+ * both the runner and the `duet memory` CLI see the same upgraded
15
+ * schema. Failure aborts startup with a clear message; the underlying
16
+ * PGlite directory is also covered by `quarantineDataDirectory()` for
17
+ * unreadable cases (see `pglite.ts`).
18
+ * - Each migration runs inside a single transaction. Postgres DDL is
19
+ * transactional, so a partial failure rolls back to the prior version.
20
+ * - The `schema_version` table records every applied migration with its
21
+ * wall-clock timestamp. This is read once on open to find the current
22
+ * version; we never go backwards.
23
+ * - Forward-only by design. A bad migration is fixed by writing the next
24
+ * migration, not by reverting. This matches gbrain's pattern after
25
+ * they hit "upgrade-wedge" bugs across six schema versions.
26
+ */
27
+ export interface Migration {
28
+ /** Monotonic version number. Migrations are applied strictly in ascending order. */
29
+ version: number;
30
+ /** One-line summary stored in `schema_version.description` for audit. */
31
+ description: string;
32
+ /** Forward step. Receives a transaction so DDL + DML run atomically. */
33
+ up: (tx: Transaction) => Promise<void>;
34
+ }
35
+ export interface MigrationResult {
36
+ /** Schema version the database was at before `runMigrations` was called. */
37
+ fromVersion: number;
38
+ /** Schema version the database is at after `runMigrations` returns. */
39
+ toVersion: number;
40
+ /** Versions actually applied this call (empty when already up to date). */
41
+ applied: number[];
42
+ }
43
+ /**
44
+ * Bring the database up to the latest known schema version. Idempotent;
45
+ * safe to call on every open. Throws on the first failure with the
46
+ * triggering migration's version in the message so users have a place to
47
+ * look without grepping.
48
+ */
49
+ export declare function runMigrations(db: PGlite): Promise<MigrationResult>;
50
+ /** Latest version known to this build. Exported for tests. */
51
+ export declare const LATEST_SCHEMA_VERSION: number;
52
+ /** All registered migrations. Exported for tests. */
53
+ export declare const REGISTERED_MIGRATIONS: readonly Migration[];
54
+ //# sourceMappingURL=migrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../../src/memory/migrations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,SAAS;IACxB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,EAAE,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC;AAiLD,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAwCxE;AAED,8DAA8D;AAC9D,eAAO,MAAM,qBAAqB,EAAE,MAAwC,CAAC;AAE7E,qDAAqD;AACrD,eAAO,MAAM,qBAAqB,EAAE,SAAS,SAAS,EAAe,CAAC"}