@snoai/memory 1.1.0 → 1.2.1

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 CHANGED
@@ -28,7 +28,7 @@ Who it is for: you do not use this package directly. Follow the
28
28
  plugins use to reach the service.
29
29
 
30
30
  ```bash
31
- npm install @snoai/memory@1.1.0
31
+ npm install @snoai/memory@1.2.1
32
32
  ```
33
33
 
34
34
  Needs Node.js 22.22.3+, 24.15.0+ or 25.9.0+. Licensed under Apache-2.0.
@@ -0,0 +1,223 @@
1
+ import "./internal/engine/telemetry/observe-ledger.js";
2
+ import "./internal/config/skill-categories.js";
3
+ import "./internal/config/skin-defaults.js";
4
+ import { z } from "zod";
5
+ import { basename } from "node:path";
6
+ import { createReadStream } from "node:fs";
7
+ //#region config/session-activity.ts
8
+ /** A gap between two records of at most this long is working time; a longer one ends the run. */
9
+ const ACTIVITY_GAP_MS = 9e5;
10
+ /** A run counts in runs_over_12h once, in the send where it reaches this length, ended or not. */
11
+ const ACTIVITY_LONG_RUN_MS = 432e5;
12
+ /**
13
+ * Text markers of messages the person did not write, seen in real transcripts on the owner's
14
+ * machine (Claude Code 2.1.247 and Codex 0.160, 2026-08 to 2026-10). Heuristics, matched on text.
15
+ */
16
+ /** Reach's ring and mailbox doorbell, typed into the agent's terminal (apps/reach/lib/reach-ring line 426); recorded as a human prompt by Claude Code and Codex. */
17
+ const REACH_RING_MARKER = "typed by the mail transport, not by the owner";
18
+ /** A heartbeat tick line, "<UTC time> [label] tick=N ..." (apps/heartbeat/bin/heartbeat line 507); Claude Code delivers it inside a Monitor <task-notification>. */
19
+ const HEARTBEAT_TICK = /\d{4}-\d\d-\d\dT\d\d:\d\d:\d\dZ \[[^\]\n]+\] tick=\d+/;
20
+ /** The first prompt Reach gives a seat it starts in a terminal (apps/reach/lib/reach-spawn line 19). */
21
+ const REACH_SEAT_START_MARKER = "No work is assigned by this startup message";
22
+ /** True when the text of a user message came from another agent's tooling: a Reach ring or seat start, or a heartbeat wake. */
23
+ function isAgentText(text) {
24
+ return text.includes(REACH_RING_MARKER) || text.includes(REACH_SEAT_START_MARKER) || HEARTBEAT_TICK.test(text);
25
+ }
26
+ const activityCursorSchema = z.object({
27
+ offset: z.number().int().nonnegative(),
28
+ lastTs: z.number().int().nonnegative(),
29
+ runStart: z.number().int().nonnegative(),
30
+ agentDriven: z.boolean(),
31
+ counted: z.boolean(),
32
+ path: z.string().min(1).optional()
33
+ });
34
+ const EMPTY_ACTIVITY_CURSOR = {
35
+ offset: 0,
36
+ lastTs: 0,
37
+ runStart: 0,
38
+ agentDriven: false,
39
+ counted: false
40
+ };
41
+ /**
42
+ * Folds the records after the cursor into one window. A gap belongs to the window of the record
43
+ * that ends it, so splitting a transcript at any line gives the same sums. A run is counted in
44
+ * runs_over_12h once, in the send where it reaches 12 hours, whether or not it has ended. longest_run_ms
45
+ * is the longest run of any length seen in the window, including the length so far of the run still
46
+ * open. `closeRun` ends the run still open at the last record (the session is over); otherwise it
47
+ * stays open in the cursor.
48
+ */
49
+ function foldActivity(cursor, records, closeRun) {
50
+ let { lastTs, runStart, agentDriven, counted } = cursor;
51
+ let windowStart = runStart > 0 ? lastTs : 0;
52
+ let active = 0, team = 0, human = 0, runs = 0, longest = 0;
53
+ const seeRun = (end) => {
54
+ const length = end - runStart;
55
+ longest = Math.max(longest, length);
56
+ if (!counted && length >= ACTIVITY_LONG_RUN_MS) {
57
+ runs++;
58
+ counted = true;
59
+ }
60
+ };
61
+ for (const record of records) {
62
+ if (windowStart === 0) windowStart = record.ts;
63
+ if (runStart === 0) runStart = record.ts;
64
+ else if (record.ts > lastTs) {
65
+ const gap = record.ts - lastTs;
66
+ if (gap <= ACTIVITY_GAP_MS) {
67
+ active += gap;
68
+ if (agentDriven) team += gap;
69
+ } else {
70
+ seeRun(lastTs);
71
+ runStart = record.ts;
72
+ counted = false;
73
+ }
74
+ }
75
+ lastTs = Math.max(lastTs, record.ts);
76
+ if (runStart > 0) seeRun(lastTs);
77
+ if (record.incoming === "human") {
78
+ human++;
79
+ agentDriven = false;
80
+ } else if (record.incoming === "agent") agentDriven = true;
81
+ }
82
+ const closing = closeRun && runStart > 0;
83
+ if (closing) {
84
+ runStart = 0;
85
+ counted = false;
86
+ }
87
+ const next = {
88
+ ...cursor,
89
+ lastTs,
90
+ runStart,
91
+ agentDriven,
92
+ counted
93
+ };
94
+ if (records.length === 0 && !closing) return {
95
+ payload: void 0,
96
+ cursor: next
97
+ };
98
+ return {
99
+ payload: {
100
+ window_start_ms: windowStart > 0 ? windowStart : lastTs,
101
+ window_end_ms: lastTs,
102
+ active_ms: active,
103
+ team_driven_ms: team,
104
+ runs_over_12h: runs,
105
+ longest_run_ms: longest,
106
+ human_messages: human
107
+ },
108
+ cursor: next
109
+ };
110
+ }
111
+ /**
112
+ * Calls `onLine` for each complete line from `offset` on and returns the offset after the last
113
+ * complete line, and whether the end of the file was reached on a line boundary. Stops reading once `budgetMs` has passed: real transcripts reach 1.65 GB while a
114
+ * hook has seconds, so a long backlog is taken over several sends instead of failing every time.
115
+ */
116
+ async function readNewLines(path, offset, budgetMs, onLine) {
117
+ const deadline = Date.now() + budgetMs;
118
+ const stream = createReadStream(path, { start: offset });
119
+ let pending = Buffer.alloc(0);
120
+ let consumed = offset;
121
+ let done = true;
122
+ try {
123
+ for await (const chunk of stream) {
124
+ pending = pending.length === 0 ? chunk : Buffer.concat([pending, chunk]);
125
+ let from = 0;
126
+ for (let end = pending.indexOf(10, from); end !== -1; end = pending.indexOf(10, from)) {
127
+ if (end > from) onLine(pending.toString("utf8", from, end));
128
+ from = end + 1;
129
+ }
130
+ consumed += from;
131
+ pending = pending.subarray(from);
132
+ if (Date.now() > deadline) {
133
+ done = false;
134
+ break;
135
+ }
136
+ }
137
+ } finally {
138
+ stream.destroy();
139
+ }
140
+ if (done && pending.length > 0) {
141
+ const tail = pending.toString("utf8");
142
+ try {
143
+ JSON.parse(tail);
144
+ onLine(tail);
145
+ consumed += pending.length;
146
+ } catch {
147
+ done = false;
148
+ }
149
+ }
150
+ return {
151
+ offset: consumed,
152
+ done
153
+ };
154
+ }
155
+ //#endregion
156
+ //#region config/coding-skin.ts
157
+ const CODING_SKIN_CHILD_DEADLINE_MS = 11e4;
158
+ const CODING_SKIN_WORKER_LIFETIME_MS = 54e4;
159
+ const CODING_SKIN_RETRY_DELAYS_MS = [2e3, 1e4];
160
+ const CODING_SKIN_MAX_ATTEMPTS = 3;
161
+ const CODING_SKIN_IMPORT_INTERVAL_MS = 2010;
162
+ const CODING_SKIN_CHILD_MAX_BUFFER_BYTES = 8388608;
163
+ const CODING_SKIN_MANUAL_SESSION_ID = "manual";
164
+ const CODING_SKIN_SESSION_QUERY = "standing decisions, open tasks, conventions and known pitfalls for this repository";
165
+ const CODING_SKIN_LEDGER_MAX_CHARS = 12e3;
166
+ const CODING_SKIN_RECALL_SENTENCE_MAX_CHARS = 200;
167
+ const CODING_SKIN_RECALL_ITEM_MAX_CHARS = 240;
168
+ const CODING_SKIN_HOOKS = {
169
+ SessionStart: {
170
+ eventName: "session_start",
171
+ subcommand: "session-start",
172
+ timeout: 15
173
+ },
174
+ UserPromptSubmit: {
175
+ eventName: "user_prompt_submit",
176
+ subcommand: "user-prompt-submit",
177
+ timeout: 8
178
+ },
179
+ Stop: {
180
+ eventName: "stop",
181
+ subcommand: "stop",
182
+ timeout: 5
183
+ },
184
+ SessionEnd: {
185
+ eventName: "session_end",
186
+ subcommand: "session-end",
187
+ timeout: 8
188
+ },
189
+ PreToolUse: {
190
+ eventName: "pre_tool_use",
191
+ subcommand: "pre-tool-use",
192
+ timeout: 5
193
+ },
194
+ PostToolUse: {
195
+ eventName: "post_tool_use",
196
+ subcommand: "post-tool-use",
197
+ timeout: 8
198
+ }
199
+ };
200
+ const CODING_SKIN_MODEL_COMMANDS = [
201
+ "recall",
202
+ "get",
203
+ "remember",
204
+ "correct"
205
+ ];
206
+ function shellQuote(value) {
207
+ return `'${value.replaceAll("'", "'\\''")}'`;
208
+ }
209
+ /** The command an agent hook runs: the `sno` binary that ran setup, then `memory hook <event> --harness <name>`. */
210
+ function codingSkinHookCommand(snoPath, subcommand, harness) {
211
+ return `${shellQuote(snoPath)} memory hook ${subcommand} --harness ${harness}`;
212
+ }
213
+ /** True for a hook command of that shape, whatever the path to `sno` is. */
214
+ function isCodingSkinHookCommand(command, subcommand, harness) {
215
+ const suffix = ` memory hook ${subcommand} --harness ${harness}`;
216
+ const trimmed = command.trim();
217
+ if (!trimmed.endsWith(suffix)) return false;
218
+ const encoded = trimmed.slice(0, -suffix.length);
219
+ const program = encoded.startsWith("'") && encoded.endsWith("'") ? encoded.slice(1, -1).replaceAll("'\\''", "'") : encoded;
220
+ return basename(program) === "sno";
221
+ }
222
+ //#endregion
223
+ export { EMPTY_ACTIVITY_CURSOR as _, CODING_SKIN_LEDGER_MAX_CHARS as a, isAgentText as b, CODING_SKIN_MODEL_COMMANDS as c, CODING_SKIN_RETRY_DELAYS_MS as d, CODING_SKIN_SESSION_QUERY as f, shellQuote as g, isCodingSkinHookCommand as h, CODING_SKIN_IMPORT_INTERVAL_MS as i, CODING_SKIN_RECALL_ITEM_MAX_CHARS as l, codingSkinHookCommand as m, CODING_SKIN_CHILD_MAX_BUFFER_BYTES as n, CODING_SKIN_MANUAL_SESSION_ID as o, CODING_SKIN_WORKER_LIFETIME_MS as p, CODING_SKIN_HOOKS as r, CODING_SKIN_MAX_ATTEMPTS as s, CODING_SKIN_CHILD_DEADLINE_MS as t, CODING_SKIN_RECALL_SENTENCE_MAX_CHARS as u, activityCursorSchema as v, readNewLines as x, foldActivity as y };
@@ -1,6 +1,61 @@
1
1
  import { ObserveLedgerRow, appendObserveLedgerRows } from "./internal/engine/telemetry/observe-ledger.js";
2
2
  import { SKILL_CATEGORIES, skillVersionFor } from "./internal/config/skill-categories.js";
3
3
  import { HOST_MODEL_CALLBACK_HOST, HOST_MODEL_CALLBACK_PATH } from "./internal/config/skin-defaults.js";
4
+ import { z } from "zod";
5
+ //#region config/session-activity.d.ts
6
+ /** True when the text of a user message came from another agent's tooling: a Reach ring or seat start, or a heartbeat wake. */
7
+ export declare function isAgentText(text: string): boolean;
8
+ /** One transcript record: its time, and who sent the incoming message it carries, if it carries one. */
9
+ interface ActivityRecord {
10
+ ts: number;
11
+ incoming?: "human" | "agent";
12
+ }
13
+ /** Where the previous send stopped: a byte offset into the transcript and the run still open there. */
14
+ interface ActivityCursor {
15
+ offset: number;
16
+ lastTs: number;
17
+ /** Start of the continuous run still open at lastTs; 0 when no run is open. */
18
+ runStart: number;
19
+ /** The latest incoming message came from another agent. */
20
+ agentDriven: boolean;
21
+ /** The run still open was already counted in runs_over_12h. */
22
+ counted: boolean;
23
+ /** Codex only: the rollout file found by its first send. */
24
+ path?: string;
25
+ }
26
+ export declare const activityCursorSchema: z.ZodType<ActivityCursor>;
27
+ interface SessionActivityPayload {
28
+ window_start_ms: number;
29
+ window_end_ms: number;
30
+ active_ms: number;
31
+ team_driven_ms: number;
32
+ runs_over_12h: number;
33
+ longest_run_ms: number;
34
+ human_messages: number;
35
+ }
36
+ export declare const EMPTY_ACTIVITY_CURSOR: ActivityCursor;
37
+ /**
38
+ * Folds the records after the cursor into one window. A gap belongs to the window of the record
39
+ * that ends it, so splitting a transcript at any line gives the same sums. A run is counted in
40
+ * runs_over_12h once, in the send where it reaches 12 hours, whether or not it has ended. longest_run_ms
41
+ * is the longest run of any length seen in the window, including the length so far of the run still
42
+ * open. `closeRun` ends the run still open at the last record (the session is over); otherwise it
43
+ * stays open in the cursor.
44
+ */
45
+ export declare function foldActivity(cursor: ActivityCursor, records: readonly ActivityRecord[], closeRun: boolean): {
46
+ payload: SessionActivityPayload | undefined;
47
+ cursor: ActivityCursor;
48
+ };
49
+ /**
50
+ * Calls `onLine` for each complete line from `offset` on and returns the offset after the last
51
+ * complete line, and whether the end of the file was reached on a line boundary. Stops reading once `budgetMs` has passed: real transcripts reach 1.65 GB while a
52
+ * hook has seconds, so a long backlog is taken over several sends instead of failing every time.
53
+ */
54
+ export declare function readNewLines(path: string, offset: number, budgetMs: number, onLine: (line: string) => void): Promise<{
55
+ offset: number;
56
+ done: boolean;
57
+ }>;
58
+ //#endregion
4
59
  //#region config/coding-skin.d.ts
5
60
  export declare const CODING_SKIN_CHILD_DEADLINE_MS = 11e4;
6
61
  export declare const CODING_SKIN_WORKER_LIFETIME_MS: number;
@@ -54,4 +109,4 @@ export declare function codingSkinHookCommand(snoPath: string, subcommand: strin
54
109
  /** True for a hook command of that shape, whatever the path to `sno` is. */
55
110
  export declare function isCodingSkinHookCommand(command: string, subcommand: string, harness: CodingSkinHarness): boolean;
56
111
  //#endregion
57
- export { HOST_MODEL_CALLBACK_HOST, HOST_MODEL_CALLBACK_PATH, type ObserveLedgerRow, SKILL_CATEGORIES, appendObserveLedgerRows, skillVersionFor };
112
+ export { type ActivityCursor, type ActivityRecord, HOST_MODEL_CALLBACK_HOST, HOST_MODEL_CALLBACK_PATH, type ObserveLedgerRow, SKILL_CATEGORIES, type SessionActivityPayload, appendObserveLedgerRows, skillVersionFor };
@@ -1,72 +1,5 @@
1
1
  import { appendObserveLedgerRows } from "./internal/engine/telemetry/observe-ledger.js";
2
2
  import { SKILL_CATEGORIES, skillVersionFor } from "./internal/config/skill-categories.js";
3
3
  import { HOST_MODEL_CALLBACK_HOST, HOST_MODEL_CALLBACK_PATH } from "./internal/config/skin-defaults.js";
4
- import { basename } from "node:path";
5
- //#region config/coding-skin.ts
6
- const CODING_SKIN_CHILD_DEADLINE_MS = 11e4;
7
- const CODING_SKIN_WORKER_LIFETIME_MS = 54e4;
8
- const CODING_SKIN_RETRY_DELAYS_MS = [2e3, 1e4];
9
- const CODING_SKIN_MAX_ATTEMPTS = 3;
10
- const CODING_SKIN_IMPORT_INTERVAL_MS = 2010;
11
- const CODING_SKIN_CHILD_MAX_BUFFER_BYTES = 8388608;
12
- const CODING_SKIN_MANUAL_SESSION_ID = "manual";
13
- const CODING_SKIN_SESSION_QUERY = "standing decisions, open tasks, conventions and known pitfalls for this repository";
14
- const CODING_SKIN_LEDGER_MAX_CHARS = 12e3;
15
- const CODING_SKIN_RECALL_SENTENCE_MAX_CHARS = 200;
16
- const CODING_SKIN_RECALL_ITEM_MAX_CHARS = 240;
17
- const CODING_SKIN_HOOKS = {
18
- SessionStart: {
19
- eventName: "session_start",
20
- subcommand: "session-start",
21
- timeout: 15
22
- },
23
- UserPromptSubmit: {
24
- eventName: "user_prompt_submit",
25
- subcommand: "user-prompt-submit",
26
- timeout: 8
27
- },
28
- Stop: {
29
- eventName: "stop",
30
- subcommand: "stop",
31
- timeout: 5
32
- },
33
- SessionEnd: {
34
- eventName: "session_end",
35
- subcommand: "session-end",
36
- timeout: 8
37
- },
38
- PreToolUse: {
39
- eventName: "pre_tool_use",
40
- subcommand: "pre-tool-use",
41
- timeout: 5
42
- },
43
- PostToolUse: {
44
- eventName: "post_tool_use",
45
- subcommand: "post-tool-use",
46
- timeout: 8
47
- }
48
- };
49
- const CODING_SKIN_MODEL_COMMANDS = [
50
- "recall",
51
- "get",
52
- "remember",
53
- "correct"
54
- ];
55
- function shellQuote(value) {
56
- return `'${value.replaceAll("'", "'\\''")}'`;
57
- }
58
- /** The command an agent hook runs: the `sno` binary that ran setup, then `memory hook <event> --harness <name>`. */
59
- function codingSkinHookCommand(snoPath, subcommand, harness) {
60
- return `${shellQuote(snoPath)} memory hook ${subcommand} --harness ${harness}`;
61
- }
62
- /** True for a hook command of that shape, whatever the path to `sno` is. */
63
- function isCodingSkinHookCommand(command, subcommand, harness) {
64
- const suffix = ` memory hook ${subcommand} --harness ${harness}`;
65
- const trimmed = command.trim();
66
- if (!trimmed.endsWith(suffix)) return false;
67
- const encoded = trimmed.slice(0, -suffix.length);
68
- const program = encoded.startsWith("'") && encoded.endsWith("'") ? encoded.slice(1, -1).replaceAll("'\\''", "'") : encoded;
69
- return basename(program) === "sno";
70
- }
71
- //#endregion
72
- export { CODING_SKIN_CHILD_DEADLINE_MS, CODING_SKIN_CHILD_MAX_BUFFER_BYTES, CODING_SKIN_HOOKS, CODING_SKIN_IMPORT_INTERVAL_MS, CODING_SKIN_LEDGER_MAX_CHARS, CODING_SKIN_MANUAL_SESSION_ID, CODING_SKIN_MAX_ATTEMPTS, CODING_SKIN_MODEL_COMMANDS, CODING_SKIN_RECALL_ITEM_MAX_CHARS, CODING_SKIN_RECALL_SENTENCE_MAX_CHARS, CODING_SKIN_RETRY_DELAYS_MS, CODING_SKIN_SESSION_QUERY, CODING_SKIN_WORKER_LIFETIME_MS, HOST_MODEL_CALLBACK_HOST, HOST_MODEL_CALLBACK_PATH, SKILL_CATEGORIES, appendObserveLedgerRows, codingSkinHookCommand, isCodingSkinHookCommand, shellQuote, skillVersionFor };
4
+ import { _ as EMPTY_ACTIVITY_CURSOR, a as CODING_SKIN_LEDGER_MAX_CHARS, b as isAgentText, c as CODING_SKIN_MODEL_COMMANDS, d as CODING_SKIN_RETRY_DELAYS_MS, f as CODING_SKIN_SESSION_QUERY, g as shellQuote, h as isCodingSkinHookCommand, i as CODING_SKIN_IMPORT_INTERVAL_MS, l as CODING_SKIN_RECALL_ITEM_MAX_CHARS, m as codingSkinHookCommand, n as CODING_SKIN_CHILD_MAX_BUFFER_BYTES, o as CODING_SKIN_MANUAL_SESSION_ID, p as CODING_SKIN_WORKER_LIFETIME_MS, r as CODING_SKIN_HOOKS, s as CODING_SKIN_MAX_ATTEMPTS, t as CODING_SKIN_CHILD_DEADLINE_MS, u as CODING_SKIN_RECALL_SENTENCE_MAX_CHARS, v as activityCursorSchema, x as readNewLines, y as foldActivity } from "./coding-skin-C7gwOKsc.js";
5
+ export { CODING_SKIN_CHILD_DEADLINE_MS, CODING_SKIN_CHILD_MAX_BUFFER_BYTES, CODING_SKIN_HOOKS, CODING_SKIN_IMPORT_INTERVAL_MS, CODING_SKIN_LEDGER_MAX_CHARS, CODING_SKIN_MANUAL_SESSION_ID, CODING_SKIN_MAX_ATTEMPTS, CODING_SKIN_MODEL_COMMANDS, CODING_SKIN_RECALL_ITEM_MAX_CHARS, CODING_SKIN_RECALL_SENTENCE_MAX_CHARS, CODING_SKIN_RETRY_DELAYS_MS, CODING_SKIN_SESSION_QUERY, CODING_SKIN_WORKER_LIFETIME_MS, EMPTY_ACTIVITY_CURSOR, HOST_MODEL_CALLBACK_HOST, HOST_MODEL_CALLBACK_PATH, SKILL_CATEGORIES, activityCursorSchema, appendObserveLedgerRows, codingSkinHookCommand, foldActivity, isAgentText, isCodingSkinHookCommand, readNewLines, shellQuote, skillVersionFor };
@@ -1,4 +1,4 @@
1
- import { n as version, t as logSiteCatalog } from "./log-site-catalog.generated-CylmA5IF.js";
1
+ import { n as version, t as logSiteCatalog } from "./log-site-catalog.generated-GhNOEd6q.js";
2
2
  import { diagnosticProcessInstanceId, encodeDiagnostic as encodeDiagnostic$1, writeEmergencyDiagnostic as writeEmergencyDiagnostic$1 } from "@snoai/utils/log-encoder";
3
3
  //#region src/engine/observability/early-diagnostics.ts
4
4
  function bindApplication(input) {
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import { MEMORY_BODY_LIMIT_BYTES, MEMORY_DEFAULT_SKIN_ID, MEMORY_ERROR_STATUS, M
7
7
  import { a as readTrustedUserId, c as inspectProviderProjects, o as resolveProviderIdentity, s as SnoStationMemProviderSearchManager, t as createMemoryRuntime } from "./provider-registration-qLSA9CQl.js";
8
8
  import { s as withMemoryOperation, t as checkMemoryOperation } from "./operation-cancellation-CTKWwNHk.js";
9
9
  import { pruneOldestEntries, setLruEntry } from "./internal/engine/shared/lru.js";
10
- import { CODING_SKIN_LEDGER_MAX_CHARS, CODING_SKIN_SESSION_QUERY } from "./coding-skin.js";
10
+ import { a as CODING_SKIN_LEDGER_MAX_CHARS, f as CODING_SKIN_SESSION_QUERY } from "./coding-skin-C7gwOKsc.js";
11
11
  import { defaultSettings } from "./internal/config/settings.js";
12
12
  import { i as createLlmClient } from "./llm-client-BhkzlTnH.js";
13
13
  import { pickLlmRoutingConfig } from "./internal/model/llm-mode-routing.js";
@@ -9,7 +9,7 @@ const SKILL_CATEGORIES = {
9
9
  "pr-review": "J",
10
10
  "first-principles-review": "J",
11
11
  "e2e": "J",
12
- "e2e-environment-preflight": "J",
12
+ "e2e-environment-preflight": "M",
13
13
  "e2e-red-triage": "J",
14
14
  "heartbeat": "S",
15
15
  "subscription-quota-check": "S",
@@ -52,7 +52,24 @@ const SKILL_CATEGORIES = {
52
52
  "rem-reflect": "R",
53
53
  "bro": "T",
54
54
  "managed-worktree": "T",
55
- "lh-english-writer": "T"
55
+ "lh-english-writer": "T",
56
+ "less-is-more": "J",
57
+ "medic": "M",
58
+ "sno-cli": "M",
59
+ "pl": "T",
60
+ "pl-analyze": "R",
61
+ "pl-audit": "T",
62
+ "pl-dispatch": "T",
63
+ "pl-env": "T",
64
+ "pl-watch": "T",
65
+ "cos": "T",
66
+ "cos-evolve": "R",
67
+ "cos-review": "T",
68
+ "cos-watch": "T",
69
+ "away-brief": "H",
70
+ "catch-report": "H",
71
+ "charter": "T",
72
+ "deliver": "T"
56
73
  };
57
74
  const publishedSchema = z.object({ source: z.object({ commit: z.string().min(1) }) });
58
75
  function skillVersionFor(skillDir) {
@@ -4,7 +4,7 @@ import { n as ATOMIC_EXTRACTION_SKILL_HASH } from "../../../atomic-extraction-sk
4
4
  import { getStateDir } from "../../contract/profile.js";
5
5
  import { r as ATOMIC_EXTRACTION_RESPONSE_JSON_SCHEMA } from "../../../atomic-extraction-reply-BJ9XV_9s.js";
6
6
  import { r as attribute_dictionary_default } from "../../../relation-dictionary-Cev1kyLe.js";
7
- import { n as version, t as logSiteCatalog } from "../../../log-site-catalog.generated-CylmA5IF.js";
7
+ import { n as version, t as logSiteCatalog } from "../../../log-site-catalog.generated-GhNOEd6q.js";
8
8
  import { configureLogger, createLogger, effectiveLogLevel, loggerFileStatus } from "@snoai/utils/logger";
9
9
  import { createHash } from "node:crypto";
10
10
  //#region src/engine/observability/runtime-diagnostics.ts
@@ -27,4 +27,9 @@ export declare function forwardObserveLedger(options: {
27
27
  observe: LedgerObserve;
28
28
  batchSize?: number;
29
29
  }): Promise<ForwardResult>;
30
+ /** Forwards batch after batch until the ledger is read to its end, so a session that wrote many rows (one session.activity row per turn) leaves no backlog for the next one. A batch that forwards nothing ends the loop. */
31
+ export declare function forwardObserveLedgerUntilIdle(options: {
32
+ profileDir: string;
33
+ observe: LedgerObserve;
34
+ }): Promise<void>;
30
35
  //#endregion
@@ -107,5 +107,9 @@ async function forwardObserveLedger(options) {
107
107
  offset
108
108
  };
109
109
  }
110
+ /** Forwards batch after batch until the ledger is read to its end, so a session that wrote many rows (one session.activity row per turn) leaves no backlog for the next one. A batch that forwards nothing ends the loop. */
111
+ async function forwardObserveLedgerUntilIdle(options) {
112
+ for (;;) if ((await forwardObserveLedger(options)).status !== "forwarded") return;
113
+ }
110
114
  //#endregion
111
- export { appendObserveLedgerRows, forwardObserveLedger, observeLedgerPath, observeLedgerSyncedPath };
115
+ export { appendObserveLedgerRows, forwardObserveLedger, forwardObserveLedgerUntilIdle, observeLedgerPath, observeLedgerSyncedPath };
@@ -1,7 +1,7 @@
1
1
  import { BACKUP_INTERVAL_MS, BACKUP_MIN_FREE_DISK_RATIO, FTS_MERGE_INTERVAL_MS, INTEGRITY_CHECK_INTERVAL_MS, MAINTENANCE_TICK_MS, PLANNER_STATISTICS_INTERVAL_MS, REM_TRIGGER_CHECK_INTERVAL_MS, USAGE_EVENT_RETENTION_INTERVAL_MS, USAGE_OUTBOX_INTERVAL_MS } from "../config/index.js";
2
2
  import { REM_SIDECAR_TOKEN_HEADER } from "../contract/routes.js";
3
3
  import "../engine/shared/utils.js";
4
- import "../../coding-skin.js";
4
+ import "../../coding-skin-C7gwOKsc.js";
5
5
  import "../config/settings.js";
6
6
  import "../../llm-client-BhkzlTnH.js";
7
7
  import "../model/llm-mode-routing.js";
@@ -1,9 +1,9 @@
1
1
  //#region package.json
2
- var version = "1.1.0";
2
+ var version = "1.2.1";
3
3
  //#endregion
4
4
  //#region src/engine/observability/log-site-catalog.generated.ts
5
5
  const logSiteCatalog = {
6
- "build_id": "7dd4401db498bffcd68d9bd555bd59d26b6ddd627eb10524e20dc14620dd5548",
6
+ "build_id": "17c40497dbd06f5f417622c54c1db540856d7d5f024e7fa3719bdfd77f680c3c",
7
7
  "sites": {
8
8
  "memory.recall.budget.checked": {
9
9
  "file": "packages/memory/src/engine/bindings/memory-recall-tool.ts",
@@ -1431,7 +1431,7 @@ const logSiteCatalog = {
1431
1431
  "function": "forwardObserveLedger",
1432
1432
  "line": 58,
1433
1433
  "column": 7,
1434
- "file_hash": "d55aceca369305bdba14768ebdeb98c0654b3d67817e01884d82d754f11294a8"
1434
+ "file_hash": "7fd4a98768b857e018a442e875e35014cbe82951e3a23ef60a5da32bc8b22b1f"
1435
1435
  },
1436
1436
  "shared.llm-client.resolveRequestTransport.8f631601af": {
1437
1437
  "file": "packages/memory/src/model/llm-client.ts",
@@ -1585,28 +1585,28 @@ const logSiteCatalog = {
1585
1585
  "function": "engineLog",
1586
1586
  "line": 61,
1587
1587
  "column": 2,
1588
- "file_hash": "0d430d0388646ffdb1b74e3ca447b78ffb63c80adbf431c8f585ff68aa6a94c8"
1588
+ "file_hash": "b5753215445046395099acc9500ccf6e19989fc339646d31ec52cce75935c947"
1589
1589
  },
1590
1590
  "memory.sidecar.model.prepare.failed": {
1591
1591
  "file": "packages/memory/src/sidecar/memory-runtime.ts",
1592
1592
  "function": "prepareModel",
1593
1593
  "line": 118,
1594
1594
  "column": 4,
1595
- "file_hash": "0d430d0388646ffdb1b74e3ca447b78ffb63c80adbf431c8f585ff68aa6a94c8"
1595
+ "file_hash": "b5753215445046395099acc9500ccf6e19989fc339646d31ec52cce75935c947"
1596
1596
  },
1597
1597
  "memory.sidecar.pending_capture.failed": {
1598
1598
  "file": "packages/memory/src/sidecar/memory-runtime.ts",
1599
1599
  "function": "replayPendingCaptures",
1600
1600
  "line": 135,
1601
1601
  "column": 5,
1602
- "file_hash": "0d430d0388646ffdb1b74e3ca447b78ffb63c80adbf431c8f585ff68aa6a94c8"
1602
+ "file_hash": "b5753215445046395099acc9500ccf6e19989fc339646d31ec52cce75935c947"
1603
1603
  },
1604
1604
  "memory.sidecar.usage.failed": {
1605
1605
  "file": "packages/memory/src/sidecar/memory-runtime.ts",
1606
1606
  "function": "<anonymous callback>",
1607
1607
  "line": 420,
1608
1608
  "column": 5,
1609
- "file_hash": "0d430d0388646ffdb1b74e3ca447b78ffb63c80adbf431c8f585ff68aa6a94c8"
1609
+ "file_hash": "b5753215445046395099acc9500ccf6e19989fc339646d31ec52cce75935c947"
1610
1610
  },
1611
1611
  "rem-batch-executor.onStageError.d5f24d50d3": {
1612
1612
  "file": "packages/memory/src/sidecar/rem-batch-executor.ts",