odai-dsh-agent 0.2.5 → 0.2.6

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 (166) hide show
  1. package/README.md +3 -3
  2. package/build/bin/odai-dsh-agent.d.mts +3 -0
  3. package/build/bin/odai-dsh-agent.d.mts.map +1 -0
  4. package/build/bin/odai-dsh-agent.mjs +114 -0
  5. package/build/bin/odai-dsh-agent.mjs.map +1 -0
  6. package/build/src/dsh-version.d.mts +16 -0
  7. package/build/src/dsh-version.d.mts.map +1 -0
  8. package/build/src/dsh-version.mjs +20 -0
  9. package/build/src/dsh-version.mjs.map +1 -0
  10. package/build/src/installer.d.mts +46 -0
  11. package/build/src/installer.d.mts.map +1 -0
  12. package/build/src/installer.mjs +397 -0
  13. package/build/src/installer.mjs.map +1 -0
  14. package/package.json +13 -12
  15. package/preset/odai/runtime/compaction-config.d.mts +66 -0
  16. package/preset/odai/runtime/compaction-config.d.mts.map +1 -0
  17. package/preset/odai/runtime/compaction-config.mjs +320 -342
  18. package/preset/odai/runtime/compaction-config.mjs.map +1 -0
  19. package/preset/odai/runtime/compaction-runtime.d.mts +68 -0
  20. package/preset/odai/runtime/compaction-runtime.d.mts.map +1 -0
  21. package/preset/odai/runtime/compaction-runtime.mjs +157 -0
  22. package/preset/odai/runtime/compaction-runtime.mjs.map +1 -0
  23. package/preset/odai/runtime/context-activation.d.mts +31 -0
  24. package/preset/odai/runtime/context-activation.d.mts.map +1 -0
  25. package/preset/odai/runtime/context-activation.mjs +55 -49
  26. package/preset/odai/runtime/context-activation.mjs.map +1 -0
  27. package/preset/odai/runtime/context-capability.d.mts +16 -0
  28. package/preset/odai/runtime/context-capability.d.mts.map +1 -0
  29. package/preset/odai/runtime/context-capability.mjs +79 -74
  30. package/preset/odai/runtime/context-capability.mjs.map +1 -0
  31. package/preset/odai/runtime/governance.d.mts +30 -0
  32. package/preset/odai/runtime/governance.d.mts.map +1 -0
  33. package/preset/odai/runtime/governance.mjs +126 -113
  34. package/preset/odai/runtime/governance.mjs.map +1 -0
  35. package/preset/odai/runtime/human-care.d.mts +13 -0
  36. package/preset/odai/runtime/human-care.d.mts.map +1 -0
  37. package/preset/odai/runtime/human-care.mjs +45 -42
  38. package/preset/odai/runtime/human-care.mjs.map +1 -0
  39. package/preset/odai/runtime/human-safety-continuity-store.d.mts +43 -0
  40. package/preset/odai/runtime/human-safety-continuity-store.d.mts.map +1 -0
  41. package/preset/odai/runtime/human-safety-continuity-store.mjs +193 -215
  42. package/preset/odai/runtime/human-safety-continuity-store.mjs.map +1 -0
  43. package/preset/odai/runtime/human-safety-continuity.d.mts +27 -0
  44. package/preset/odai/runtime/human-safety-continuity.d.mts.map +1 -0
  45. package/preset/odai/runtime/human-safety-continuity.mjs +205 -216
  46. package/preset/odai/runtime/human-safety-continuity.mjs.map +1 -0
  47. package/preset/odai/runtime/human-safety.d.mts +14 -0
  48. package/preset/odai/runtime/human-safety.d.mts.map +1 -0
  49. package/preset/odai/runtime/human-safety.mjs +50 -47
  50. package/preset/odai/runtime/human-safety.mjs.map +1 -0
  51. package/preset/odai/runtime/index.d.mts +8 -0
  52. package/preset/odai/runtime/index.d.mts.map +1 -0
  53. package/preset/odai/runtime/index.mjs +161 -2473
  54. package/preset/odai/runtime/index.mjs.map +1 -0
  55. package/preset/odai/runtime/lifecycle-runtime.d.mts +83 -0
  56. package/preset/odai/runtime/lifecycle-runtime.d.mts.map +1 -0
  57. package/preset/odai/runtime/lifecycle-runtime.mjs +1081 -0
  58. package/preset/odai/runtime/lifecycle-runtime.mjs.map +1 -0
  59. package/preset/odai/runtime/model-route.d.mts +22 -0
  60. package/preset/odai/runtime/model-route.d.mts.map +1 -0
  61. package/preset/odai/runtime/model-route.mjs +68 -60
  62. package/preset/odai/runtime/model-route.mjs.map +1 -0
  63. package/preset/odai/runtime/output-config.d.mts +54 -0
  64. package/preset/odai/runtime/output-config.d.mts.map +1 -0
  65. package/preset/odai/runtime/output-config.mjs +289 -315
  66. package/preset/odai/runtime/output-config.mjs.map +1 -0
  67. package/preset/odai/runtime/output-policy-state.d.mts +3 -0
  68. package/preset/odai/runtime/output-policy-state.d.mts.map +1 -0
  69. package/preset/odai/runtime/output-policy-state.mjs +41 -29
  70. package/preset/odai/runtime/output-policy-state.mjs.map +1 -0
  71. package/preset/odai/runtime/prompt-runtime.d.mts +55 -0
  72. package/preset/odai/runtime/prompt-runtime.d.mts.map +1 -0
  73. package/preset/odai/runtime/prompt-runtime.mjs +303 -0
  74. package/preset/odai/runtime/prompt-runtime.mjs.map +1 -0
  75. package/preset/odai/runtime/research-packet.d.mts +30 -0
  76. package/preset/odai/runtime/research-packet.d.mts.map +1 -0
  77. package/preset/odai/runtime/research-packet.mjs +116 -117
  78. package/preset/odai/runtime/research-packet.mjs.map +1 -0
  79. package/preset/odai/runtime/responsibility-gap.d.mts +26 -0
  80. package/preset/odai/runtime/responsibility-gap.d.mts.map +1 -0
  81. package/preset/odai/runtime/responsibility-gap.mjs +91 -85
  82. package/preset/odai/runtime/responsibility-gap.mjs.map +1 -0
  83. package/preset/odai/runtime/responsibility-scope.d.mts +69 -0
  84. package/preset/odai/runtime/responsibility-scope.d.mts.map +1 -0
  85. package/preset/odai/runtime/responsibility-scope.mjs +199 -171
  86. package/preset/odai/runtime/responsibility-scope.mjs.map +1 -0
  87. package/preset/odai/runtime/role-overlays.d.mts +5 -0
  88. package/preset/odai/runtime/role-overlays.d.mts.map +1 -0
  89. package/preset/odai/runtime/role-overlays.mjs +14 -13
  90. package/preset/odai/runtime/role-overlays.mjs.map +1 -0
  91. package/preset/odai/runtime/route-card.d.mts +42 -0
  92. package/preset/odai/runtime/route-card.d.mts.map +1 -0
  93. package/preset/odai/runtime/route-card.mjs +158 -146
  94. package/preset/odai/runtime/route-card.mjs.map +1 -0
  95. package/preset/odai/runtime/router.d.mts +79 -0
  96. package/preset/odai/runtime/router.d.mts.map +1 -0
  97. package/preset/odai/runtime/router.mjs +491 -559
  98. package/preset/odai/runtime/router.mjs.map +1 -0
  99. package/preset/odai/runtime/routing-config.d.mts +76 -0
  100. package/preset/odai/runtime/routing-config.d.mts.map +1 -0
  101. package/preset/odai/runtime/routing-config.mjs +373 -438
  102. package/preset/odai/runtime/routing-config.mjs.map +1 -0
  103. package/preset/odai/runtime/routing-context.d.mts +47 -0
  104. package/preset/odai/runtime/routing-context.d.mts.map +1 -0
  105. package/preset/odai/runtime/routing-context.mjs +238 -273
  106. package/preset/odai/runtime/routing-context.mjs.map +1 -0
  107. package/preset/odai/runtime/runtime-config.d.mts +24 -0
  108. package/preset/odai/runtime/runtime-config.d.mts.map +1 -0
  109. package/preset/odai/runtime/runtime-config.mjs +181 -0
  110. package/preset/odai/runtime/runtime-config.mjs.map +1 -0
  111. package/preset/odai/runtime/runtime-support.d.mts +91 -0
  112. package/preset/odai/runtime/runtime-support.d.mts.map +1 -0
  113. package/preset/odai/runtime/runtime-support.mjs +371 -0
  114. package/preset/odai/runtime/runtime-support.mjs.map +1 -0
  115. package/preset/odai/runtime/runtime-types.d.mts +235 -0
  116. package/preset/odai/runtime/runtime-types.d.mts.map +1 -0
  117. package/preset/odai/runtime/runtime-types.mjs +4 -0
  118. package/preset/odai/runtime/runtime-types.mjs.map +1 -0
  119. package/preset/odai/runtime/semantic-memory-store.d.mts +110 -0
  120. package/preset/odai/runtime/semantic-memory-store.d.mts.map +1 -0
  121. package/preset/odai/runtime/semantic-memory-store.mjs +271 -385
  122. package/preset/odai/runtime/semantic-memory-store.mjs.map +1 -0
  123. package/preset/odai/runtime/semantic-memory.d.mts +88 -0
  124. package/preset/odai/runtime/semantic-memory.d.mts.map +1 -0
  125. package/preset/odai/runtime/semantic-memory.mjs +790 -750
  126. package/preset/odai/runtime/semantic-memory.mjs.map +1 -0
  127. package/preset/odai/runtime/session-compat.d.mts +31 -0
  128. package/preset/odai/runtime/session-compat.d.mts.map +1 -0
  129. package/preset/odai/runtime/session-compat.mjs +301 -328
  130. package/preset/odai/runtime/session-compat.mjs.map +1 -0
  131. package/preset/odai/runtime/session-evidence.d.mts +25 -0
  132. package/preset/odai/runtime/session-evidence.d.mts.map +1 -0
  133. package/preset/odai/runtime/session-evidence.mjs +309 -287
  134. package/preset/odai/runtime/session-evidence.mjs.map +1 -0
  135. package/preset/odai/runtime/skill-bundle.d.mts +53 -0
  136. package/preset/odai/runtime/skill-bundle.d.mts.map +1 -0
  137. package/preset/odai/runtime/skill-bundle.mjs +286 -276
  138. package/preset/odai/runtime/skill-bundle.mjs.map +1 -0
  139. package/preset/odai/runtime/skill-evolution.d.mts +112 -0
  140. package/preset/odai/runtime/skill-evolution.d.mts.map +1 -0
  141. package/preset/odai/runtime/skill-evolution.mjs +1287 -1261
  142. package/preset/odai/runtime/skill-evolution.mjs.map +1 -0
  143. package/preset/odai/runtime/skill-selection-state.d.mts +5 -0
  144. package/preset/odai/runtime/skill-selection-state.d.mts.map +1 -0
  145. package/preset/odai/runtime/skill-selection-state.mjs +59 -43
  146. package/preset/odai/runtime/skill-selection-state.mjs.map +1 -0
  147. package/preset/odai/runtime/skill-selector.d.mts +28 -0
  148. package/preset/odai/runtime/skill-selector.d.mts.map +1 -0
  149. package/preset/odai/runtime/skill-selector.mjs +100 -109
  150. package/preset/odai/runtime/skill-selector.mjs.map +1 -0
  151. package/preset/odai/runtime/skill-source-config.d.mts +32 -0
  152. package/preset/odai/runtime/skill-source-config.d.mts.map +1 -0
  153. package/preset/odai/runtime/skill-source-config.mjs +200 -210
  154. package/preset/odai/runtime/skill-source-config.mjs.map +1 -0
  155. package/preset/odai/runtime/store-lock.d.mts +2 -0
  156. package/preset/odai/runtime/store-lock.d.mts.map +1 -0
  157. package/preset/odai/runtime/store-lock.mjs +92 -87
  158. package/preset/odai/runtime/store-lock.mjs.map +1 -0
  159. package/preset/odai/runtime/tool-runtime.d.mts +43 -0
  160. package/preset/odai/runtime/tool-runtime.d.mts.map +1 -0
  161. package/preset/odai/runtime/tool-runtime.mjs +221 -0
  162. package/preset/odai/runtime/tool-runtime.mjs.map +1 -0
  163. package/bin/odai-dsh-agent.mjs +0 -108
  164. package/scripts/verify-agent-load.mjs +0 -260
  165. package/src/dsh-version.mjs +0 -24
  166. package/src/installer.mjs +0 -385
@@ -1,389 +1,362 @@
1
1
  import { execFileSync } from "node:child_process";
2
2
  import { createHash, randomUUID } from "node:crypto";
3
- import {
4
- closeSync,
5
- constants as fsConstants,
6
- copyFileSync,
7
- existsSync,
8
- fsyncSync,
9
- openSync,
10
- readFileSync,
11
- readdirSync,
12
- renameSync,
13
- rmSync,
14
- statSync,
15
- writeSync,
16
- } from "node:fs";
3
+ import { closeSync, constants as fsConstants, copyFileSync, existsSync, fsyncSync, openSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeSync, } from "node:fs";
17
4
  import { homedir } from "node:os";
18
5
  import { dirname, resolve, sep } from "node:path";
19
6
  import * as zlib from "node:zlib";
20
-
7
+ import { isUnknownRecord } from "./runtime-types.mjs";
21
8
  const ZSTD_MAGIC = 4247762216;
22
9
  const ODAI_EVENT_PREFIX = "odai/";
23
10
  const LEGACY_ODAI_AUDIT_EVENT_TYPES = new Set([
24
- "odai/governance-denied",
25
- "odai/research-decided",
26
- "odai/research-result",
27
- "odai/route-config-missing",
28
- "odai/route-decided",
29
- "odai/route-protection",
30
- "odai/route-result",
31
- "odai/route-upgrade",
32
- "odai/routing-configured",
33
- "odai/tool-observed",
11
+ "odai/governance-denied", "odai/research-decided", "odai/research-result", "odai/route-config-missing",
12
+ "odai/route-decided", "odai/route-protection", "odai/route-result", "odai/route-upgrade",
13
+ "odai/routing-configured", "odai/tool-observed",
34
14
  ]);
35
-
36
15
  export function resolveDshHome(configured, env = process.env) {
37
- const value = configured ?? env.DSH_HOME ?? resolve(homedir(), ".dsh");
38
- if (typeof value !== "string" || value.trim() === "") {
39
- throw new TypeError("DSH home must be a non-empty path");
40
- }
41
- const trimmed = value.trim();
42
- if (trimmed === "~") return homedir();
43
- if (trimmed.startsWith(`~${sep}`)) return resolve(homedir(), trimmed.slice(2));
44
- return resolve(trimmed);
16
+ const value = configured ?? env.DSH_HOME ?? resolve(homedir(), ".dsh");
17
+ if (typeof value !== "string" || value.trim() === "")
18
+ throw new TypeError("DSH home must be a non-empty path");
19
+ const trimmed = value.trim();
20
+ if (trimmed === "~")
21
+ return homedir();
22
+ if (trimmed.startsWith(`~${sep}`))
23
+ return resolve(homedir(), trimmed.slice(2));
24
+ return resolve(trimmed);
45
25
  }
46
-
47
26
  function isDshCommand(commandLine) {
48
- if (typeof commandLine !== "string") return false;
49
- return /@deepseek-ai[\\/]dsh(?:[\\/]|$)/iu.test(commandLine)
50
- || /(?:^|[\s"'])[^\s"']*[\\/]dsh(?:\.cmd|\.mjs|\.js)?(?=$|[\s"'])/iu.test(commandLine)
51
- || /(?:^|[\s"'])dsh(?:\.cmd)?(?=$|[\s"'])/iu.test(commandLine);
27
+ if (typeof commandLine !== "string")
28
+ return false;
29
+ return /@deepseek-ai[\\/]dsh(?:[\\/]|$)/iu.test(commandLine)
30
+ || /(?:^|[\s"'])[^\s"']*[\\/]dsh(?:\.cmd|\.mjs|\.js)?(?=$|[\s"'])/iu.test(commandLine)
31
+ || /(?:^|[\s"'])dsh(?:\.cmd)?(?=$|[\s"'])/iu.test(commandLine);
52
32
  }
53
-
54
33
  function listWindowsProcesses() {
55
- const command = "Get-CimInstance Win32_Process | Select-Object ProcessId,Name,CommandLine | ConvertTo-Json -Compress";
56
- const output = execFileSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command], {
57
- encoding: "utf8",
58
- windowsHide: true,
59
- }).trim();
60
- if (output === "") return [];
61
- const parsed = JSON.parse(output);
62
- return (Array.isArray(parsed) ? parsed : [parsed]).map((entry) => ({
63
- pid: Number(entry.ProcessId),
64
- name: typeof entry.Name === "string" ? entry.Name : "unknown",
65
- commandLine: entry.CommandLine,
66
- }));
34
+ const command = "Get-CimInstance Win32_Process | Select-Object ProcessId,Name,CommandLine | ConvertTo-Json -Compress";
35
+ const output = execFileSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", command], {
36
+ encoding: "utf8", windowsHide: true,
37
+ }).trim();
38
+ if (output === "")
39
+ return [];
40
+ const parsed = JSON.parse(output);
41
+ return (Array.isArray(parsed) ? parsed : [parsed]).flatMap((entry) => {
42
+ if (!isUnknownRecord(entry))
43
+ return [];
44
+ return [{
45
+ pid: Number(entry.ProcessId),
46
+ name: typeof entry.Name === "string" ? entry.Name : "unknown",
47
+ commandLine: entry.CommandLine,
48
+ }];
49
+ });
67
50
  }
68
-
69
51
  function listPosixProcesses() {
70
- const output = execFileSync("ps", ["-axo", "pid=,comm=,args="], { encoding: "utf8" });
71
- return output.split(/\r?\n/u).flatMap((line) => {
72
- const matched = /^\s*(\d+)\s+(\S+)\s+(.*)$/u.exec(line);
73
- return matched ? [{ pid: Number(matched[1]), name: matched[2], commandLine: matched[3] }] : [];
74
- });
52
+ const output = execFileSync("ps", ["-axo", "pid=,comm=,args="], { encoding: "utf8" });
53
+ return output.split(/\r?\n/u).flatMap((line) => {
54
+ const matched = /^\s*(\d+)\s+(\S+)\s+(.*)$/u.exec(line);
55
+ return matched ? [{ pid: Number(matched[1]), name: matched[2], commandLine: matched[3] }] : [];
56
+ });
75
57
  }
76
-
77
58
  export function listActiveDshProcesses(platform = process.platform) {
78
- const processes = platform === "win32" ? listWindowsProcesses() : listPosixProcesses();
79
- return processes
80
- .filter((entry) => Number.isSafeInteger(entry.pid) && entry.pid > 0 && entry.pid !== process.pid)
81
- .filter((entry) => isDshCommand(entry.commandLine))
82
- .map(({ pid, name }) => Object.freeze({ pid, name }));
59
+ const processes = platform === "win32" ? listWindowsProcesses() : listPosixProcesses();
60
+ return processes
61
+ .filter((entry) => Number.isSafeInteger(entry.pid) && entry.pid > 0 && entry.pid !== process.pid)
62
+ .filter((entry) => isDshCommand(entry.commandLine))
63
+ .map(({ pid, name }) => Object.freeze({ pid, name }));
83
64
  }
84
-
85
65
  function assertDshStopped(options) {
86
- let active;
87
- try {
88
- const scanner = options.processScanner ?? listActiveDshProcesses;
89
- active = scanner();
90
- } catch {
91
- throw new Error("cannot verify that DSH is stopped; run the repair from a normal terminal with permission to inspect local process command lines");
92
- }
93
- if (!Array.isArray(active)) throw new Error("DSH process scanner returned an invalid result");
94
- if (active.length > 0) {
95
- const owners = active.map((entry) => `${entry.name ?? "dsh"} (pid ${entry.pid ?? "unknown"})`).join(", ");
96
- throw new Error(`refusing to rewrite session artifacts while DSH is running: ${owners}`);
97
- }
66
+ let active;
67
+ try {
68
+ const scanner = options.processScanner ?? listActiveDshProcesses;
69
+ active = scanner();
70
+ }
71
+ catch {
72
+ throw new Error("cannot verify that DSH is stopped; run the repair from a normal terminal with permission to inspect local process command lines");
73
+ }
74
+ if (!Array.isArray(active))
75
+ throw new Error("DSH process scanner returned an invalid result");
76
+ if (active.length > 0) {
77
+ const owners = active.map((entry) => `${entry.name ?? "dsh"} (pid ${entry.pid ?? "unknown"})`).join(", ");
78
+ throw new Error(`refusing to rewrite session artifacts while DSH is running: ${owners}`);
79
+ }
98
80
  }
99
-
100
81
  function listSessionArtifacts(root) {
101
- if (!existsSync(root)) return [];
102
- const found = [];
103
- const pending = [root];
104
- while (pending.length > 0) {
105
- const current = pending.pop();
106
- for (const entry of readdirSync(current, { withFileTypes: true })) {
107
- const path = resolve(current, entry.name);
108
- if (entry.isDirectory()) pending.push(path);
109
- else if (entry.isFile() && (entry.name === "session.jsonl" || entry.name === "session.jsonl.zstd")) {
110
- found.push(path);
111
- }
82
+ if (!existsSync(root))
83
+ return [];
84
+ const found = [];
85
+ const pending = [root];
86
+ while (pending.length > 0) {
87
+ const current = pending.pop();
88
+ if (!current)
89
+ break;
90
+ for (const entry of readdirSync(current, { withFileTypes: true })) {
91
+ const path = resolve(current, entry.name);
92
+ if (entry.isDirectory())
93
+ pending.push(path);
94
+ else if (entry.isFile() && (entry.name === "session.jsonl" || entry.name === "session.jsonl.zstd"))
95
+ found.push(path);
96
+ }
112
97
  }
113
- }
114
- return found.sort();
98
+ return found.sort();
115
99
  }
116
-
117
100
  function transformLine(line) {
118
- if (line === "") return { line, repaired: 0 };
119
- const record = JSON.parse(line);
120
- if (typeof record?.type !== "string" || !record.type.startsWith(ODAI_EVENT_PREFIX) || record.ignorable === true) {
121
- return { line, repaired: 0 };
122
- }
123
- if (!LEGACY_ODAI_AUDIT_EVENT_TYPES.has(record.type)) {
124
- throw new Error(`refusing unknown Odai session event type ${JSON.stringify(record.type)}; use a compatible Odai repair version`);
125
- }
126
- return { line: JSON.stringify({ ...record, ignorable: true }), repaired: 1 };
101
+ if (line === "")
102
+ return { line, repaired: 0 };
103
+ const parsed = JSON.parse(line);
104
+ if (!isUnknownRecord(parsed)
105
+ || typeof parsed.type !== "string"
106
+ || !parsed.type.startsWith(ODAI_EVENT_PREFIX)
107
+ || parsed.ignorable === true)
108
+ return { line, repaired: 0 };
109
+ if (!LEGACY_ODAI_AUDIT_EVENT_TYPES.has(parsed.type)) {
110
+ throw new Error(`refusing unknown Odai session event type ${JSON.stringify(parsed.type)}; use a compatible Odai repair version`);
111
+ }
112
+ return { line: JSON.stringify({ ...parsed, ignorable: true }), repaired: 1 };
127
113
  }
128
-
129
114
  function transformCompleteJsonl(text) {
130
- const output = [];
131
- let repaired = 0;
132
- let offset = 0;
133
- for (;;) {
134
- const newline = text.indexOf("\n", offset);
135
- if (newline < 0) break;
136
- const raw = text.slice(offset, newline);
137
- const carriageReturn = raw.endsWith("\r");
138
- const line = carriageReturn ? raw.slice(0, -1) : raw;
139
- const transformed = transformLine(line);
140
- output.push(transformed.line, carriageReturn ? "\r\n" : "\n");
141
- repaired += transformed.repaired;
142
- offset = newline + 1;
143
- }
144
- const tornTail = text.slice(offset);
145
- output.push(tornTail);
146
- return { text: output.join(""), repaired, torn: tornTail.length > 0 };
147
- }
148
-
149
- function scanZstdFrames(buffer) {
150
- const frames = [];
151
- let offset = 0;
152
- while (offset < buffer.length) {
153
- const start = offset;
154
- if (buffer.length - offset < 4) return { frames, tornStart: start };
155
- if (buffer.readUInt32LE(offset) !== ZSTD_MAGIC) {
156
- throw new Error(`invalid Zstandard frame magic at byte ${offset}`);
157
- }
158
- offset += 4;
159
- if (offset === buffer.length) return { frames, tornStart: start };
160
- const descriptor = buffer.readUInt8(offset);
161
- offset += 1;
162
- if ((descriptor & 24) !== 0) throw new Error(`reserved Zstandard frame-header bit at byte ${offset - 1}`);
163
- const contentSizeFlag = descriptor >>> 6;
164
- const singleSegment = (descriptor & 32) !== 0;
165
- const checksum = (descriptor & 4) !== 0;
166
- const dictionaryFlag = descriptor & 3;
167
- const dictionaryBytes = dictionaryFlag === 3 ? 4 : dictionaryFlag;
168
- const contentSizeBytes = contentSizeFlag === 0 ? (singleSegment ? 1 : 0) : 1 << contentSizeFlag;
169
- const remainingHeaderBytes = (singleSegment ? 0 : 1) + dictionaryBytes + contentSizeBytes;
170
- if (buffer.length - offset < remainingHeaderBytes) return { frames, tornStart: start };
171
- offset += remainingHeaderBytes;
115
+ const output = [];
116
+ let repaired = 0;
117
+ let offset = 0;
172
118
  for (;;) {
173
- if (buffer.length - offset < 3) return { frames, tornStart: start };
174
- const blockHeader = buffer.readUIntLE(offset, 3);
175
- offset += 3;
176
- const lastBlock = (blockHeader & 1) !== 0;
177
- const blockType = (blockHeader >>> 1) & 3;
178
- const blockSize = blockHeader >>> 3;
179
- if (blockType === 3) throw new Error(`reserved Zstandard block type at byte ${offset - 3}`);
180
- const payloadBytes = blockType === 1 ? 1 : blockSize;
181
- if (buffer.length - offset < payloadBytes) return { frames, tornStart: start };
182
- offset += payloadBytes;
183
- if (lastBlock) break;
119
+ const newline = text.indexOf("\n", offset);
120
+ if (newline < 0)
121
+ break;
122
+ const raw = text.slice(offset, newline);
123
+ const carriageReturn = raw.endsWith("\r");
124
+ const line = carriageReturn ? raw.slice(0, -1) : raw;
125
+ const transformed = transformLine(line);
126
+ output.push(transformed.line, carriageReturn ? "\r\n" : "\n");
127
+ repaired += transformed.repaired;
128
+ offset = newline + 1;
184
129
  }
185
- if (checksum) {
186
- if (buffer.length - offset < 4) return { frames, tornStart: start };
187
- offset += 4;
130
+ const tornTail = text.slice(offset);
131
+ output.push(tornTail);
132
+ return { text: output.join(""), repaired, torn: tornTail.length > 0 };
133
+ }
134
+ function scanZstdFrames(buffer) {
135
+ const frames = [];
136
+ let offset = 0;
137
+ while (offset < buffer.length) {
138
+ const start = offset;
139
+ if (buffer.length - offset < 4)
140
+ return { frames, tornStart: start };
141
+ if (buffer.readUInt32LE(offset) !== ZSTD_MAGIC)
142
+ throw new Error(`invalid Zstandard frame magic at byte ${offset}`);
143
+ offset += 4;
144
+ if (offset === buffer.length)
145
+ return { frames, tornStart: start };
146
+ const descriptor = buffer.readUInt8(offset);
147
+ offset += 1;
148
+ if ((descriptor & 24) !== 0)
149
+ throw new Error(`reserved Zstandard frame-header bit at byte ${offset - 1}`);
150
+ const contentSizeFlag = descriptor >>> 6;
151
+ const singleSegment = (descriptor & 32) !== 0;
152
+ const checksum = (descriptor & 4) !== 0;
153
+ const dictionaryFlag = descriptor & 3;
154
+ const dictionaryBytes = dictionaryFlag === 3 ? 4 : dictionaryFlag;
155
+ const contentSizeBytes = contentSizeFlag === 0 ? (singleSegment ? 1 : 0) : 1 << contentSizeFlag;
156
+ const remainingHeaderBytes = (singleSegment ? 0 : 1) + dictionaryBytes + contentSizeBytes;
157
+ if (buffer.length - offset < remainingHeaderBytes)
158
+ return { frames, tornStart: start };
159
+ offset += remainingHeaderBytes;
160
+ for (;;) {
161
+ if (buffer.length - offset < 3)
162
+ return { frames, tornStart: start };
163
+ const blockHeader = buffer.readUIntLE(offset, 3);
164
+ offset += 3;
165
+ const lastBlock = (blockHeader & 1) !== 0;
166
+ const blockType = (blockHeader >>> 1) & 3;
167
+ const blockSize = blockHeader >>> 3;
168
+ if (blockType === 3)
169
+ throw new Error(`reserved Zstandard block type at byte ${offset - 3}`);
170
+ const payloadBytes = blockType === 1 ? 1 : blockSize;
171
+ if (buffer.length - offset < payloadBytes)
172
+ return { frames, tornStart: start };
173
+ offset += payloadBytes;
174
+ if (lastBlock)
175
+ break;
176
+ }
177
+ if (checksum) {
178
+ if (buffer.length - offset < 4)
179
+ return { frames, tornStart: start };
180
+ offset += 4;
181
+ }
182
+ frames.push({ start, end: offset });
188
183
  }
189
- frames.push({ start, end: offset });
190
- }
191
- return { frames };
184
+ return { frames };
192
185
  }
193
-
194
186
  function assertZstdAvailable() {
195
- if (typeof zlib.zstdCompressSync !== "function" || typeof zlib.zstdDecompressSync !== "function") {
196
- throw new Error("this Node.js build cannot migrate Zstandard session logs");
197
- }
187
+ if (typeof zlib.zstdCompressSync !== "function" || typeof zlib.zstdDecompressSync !== "function") {
188
+ throw new Error("this Node.js build cannot migrate Zstandard session logs");
189
+ }
198
190
  }
199
-
200
191
  function compressZstdFrame(plaintext) {
201
- return zlib.zstdCompressSync(plaintext, {
202
- params: { [zlib.constants.ZSTD_c_checksumFlag]: 1 },
203
- });
192
+ return zlib.zstdCompressSync(plaintext, { params: { [zlib.constants.ZSTD_c_checksumFlag]: 1 } });
204
193
  }
205
-
206
194
  function transformZstd(source) {
207
- assertZstdAvailable();
208
- const { frames, tornStart } = scanZstdFrames(source);
209
- if (frames.length === 0) throw new Error("Zstandard session log has no complete header frame");
210
- const output = [];
211
- let repaired = 0;
212
- for (const [index, frame] of frames.entries()) {
213
- const encoded = source.subarray(frame.start, frame.end);
214
- if (index === 0) {
215
- const header = zlib.zstdDecompressSync(encoded).toString("utf8");
216
- if (!header.endsWith("\n") || JSON.parse(header.trimEnd())?.type !== "session") {
217
- throw new Error("Zstandard session log has an invalid header frame");
218
- }
219
- output.push(encoded);
220
- continue;
221
- }
222
- const plaintext = zlib.zstdDecompressSync(encoded);
223
- const transformed = transformCompleteJsonl(plaintext.toString("utf8"));
224
- if (transformed.torn) throw new Error("complete Zstandard event frame has an unterminated JSONL record");
225
- repaired += transformed.repaired;
226
- if (transformed.repaired === 0) {
227
- output.push(encoded);
228
- continue;
195
+ assertZstdAvailable();
196
+ const { frames, tornStart } = scanZstdFrames(source);
197
+ if (frames.length === 0)
198
+ throw new Error("Zstandard session log has no complete header frame");
199
+ const output = [];
200
+ let repaired = 0;
201
+ for (const [index, frame] of frames.entries()) {
202
+ const encoded = source.subarray(frame.start, frame.end);
203
+ if (index === 0) {
204
+ const header = zlib.zstdDecompressSync(encoded).toString("utf8");
205
+ const headerValue = JSON.parse(header.trimEnd());
206
+ if (!header.endsWith("\n") || !isUnknownRecord(headerValue) || headerValue.type !== "session") {
207
+ throw new Error("Zstandard session log has an invalid header frame");
208
+ }
209
+ output.push(encoded);
210
+ continue;
211
+ }
212
+ const plaintext = zlib.zstdDecompressSync(encoded);
213
+ const transformed = transformCompleteJsonl(plaintext.toString("utf8"));
214
+ if (transformed.torn)
215
+ throw new Error("complete Zstandard event frame has an unterminated JSONL record");
216
+ repaired += transformed.repaired;
217
+ if (transformed.repaired === 0) {
218
+ output.push(encoded);
219
+ continue;
220
+ }
221
+ const replacementPlaintext = Buffer.from(transformed.text, "utf8");
222
+ const replacement = compressZstdFrame(replacementPlaintext);
223
+ if (!zlib.zstdDecompressSync(replacement).equals(replacementPlaintext))
224
+ throw new Error("Zstandard migration verification failed");
225
+ output.push(replacement);
229
226
  }
230
- const replacementPlaintext = Buffer.from(transformed.text, "utf8");
231
- const replacement = compressZstdFrame(replacementPlaintext);
232
- if (!zlib.zstdDecompressSync(replacement).equals(replacementPlaintext)) {
233
- throw new Error("Zstandard migration verification failed");
234
- }
235
- output.push(replacement);
236
- }
237
- if (tornStart !== undefined) output.push(source.subarray(tornStart));
238
- return {
239
- buffer: repaired > 0 ? Buffer.concat(output) : source,
240
- repaired,
241
- torn: tornStart !== undefined,
242
- };
227
+ if (tornStart !== undefined)
228
+ output.push(source.subarray(tornStart));
229
+ return { buffer: repaired > 0 ? Buffer.concat(output) : source, repaired, torn: tornStart !== undefined };
243
230
  }
244
-
245
231
  function transformArtifact(path) {
246
- const source = readFileSync(path);
247
- if (path.endsWith(".zstd")) return { source, ...transformZstd(source) };
248
- const transformed = transformCompleteJsonl(source.toString("utf8"));
249
- return {
250
- source,
251
- buffer: transformed.repaired > 0 ? Buffer.from(transformed.text, "utf8") : source,
252
- repaired: transformed.repaired,
253
- torn: transformed.torn,
254
- };
232
+ const source = readFileSync(path);
233
+ if (path.endsWith(".zstd"))
234
+ return { source, ...transformZstd(source) };
235
+ const transformed = transformCompleteJsonl(source.toString("utf8"));
236
+ return { source, buffer: transformed.repaired > 0 ? Buffer.from(transformed.text, "utf8") : source, repaired: transformed.repaired, torn: transformed.torn };
255
237
  }
256
-
257
238
  function writeDurably(path, content, mode) {
258
- const handle = openSync(path, "wx", mode);
259
- try {
260
- let offset = 0;
261
- while (offset < content.length) {
262
- const written = writeSync(handle, content, offset, content.length - offset);
263
- if (written <= 0) throw new Error(`short write while preparing session artifact: ${path}`);
264
- offset += written;
239
+ const handle = openSync(path, "wx", mode);
240
+ try {
241
+ let offset = 0;
242
+ while (offset < content.length) {
243
+ const written = writeSync(handle, content, offset, content.length - offset);
244
+ if (written <= 0)
245
+ throw new Error(`short write while preparing session artifact: ${path}`);
246
+ offset += written;
247
+ }
248
+ fsyncSync(handle);
249
+ }
250
+ finally {
251
+ closeSync(handle);
265
252
  }
266
- fsyncSync(handle);
267
- } finally {
268
- closeSync(handle);
269
- }
270
253
  }
271
-
272
254
  function syncDirectory(path) {
273
- let handle;
274
- try {
275
- handle = openSync(path, "r");
276
- fsyncSync(handle);
277
- } catch {
278
- // Windows does not expose directory fsync; the file itself was already synced.
279
- } finally {
280
- if (handle !== undefined) closeSync(handle);
281
- }
255
+ let handle;
256
+ try {
257
+ handle = openSync(path, "r");
258
+ fsyncSync(handle);
259
+ }
260
+ catch { }
261
+ finally {
262
+ if (handle !== undefined)
263
+ closeSync(handle);
264
+ }
282
265
  }
283
-
284
266
  function backupPathFor(path, source) {
285
- const digest = createHash("sha256").update(source).digest("hex").slice(0, 16);
286
- return `${path}.odai-compat-${digest}.bak`;
267
+ const digest = createHash("sha256").update(source).digest("hex").slice(0, 16);
268
+ return `${path}.odai-compat-${digest}.bak`;
287
269
  }
288
-
289
270
  function createVerifiedBackup(path, source) {
290
- const backupPath = backupPathFor(path, source);
291
- if (!existsSync(backupPath)) {
292
- copyFileSync(path, backupPath, fsConstants.COPYFILE_EXCL);
293
- const handle = openSync(backupPath, "r+");
294
- try {
295
- fsyncSync(handle);
296
- } finally {
297
- closeSync(handle);
271
+ const backupPath = backupPathFor(path, source);
272
+ if (!existsSync(backupPath)) {
273
+ copyFileSync(path, backupPath, fsConstants.COPYFILE_EXCL);
274
+ const handle = openSync(backupPath, "r+");
275
+ try {
276
+ fsyncSync(handle);
277
+ }
278
+ finally {
279
+ closeSync(handle);
280
+ }
281
+ syncDirectory(dirname(path));
298
282
  }
299
- syncDirectory(dirname(path));
300
- }
301
- if (!readFileSync(backupPath).equals(source)) {
302
- throw new Error(`session backup does not match the migration source: ${backupPath}`);
303
- }
304
- return backupPath;
283
+ if (!readFileSync(backupPath).equals(source))
284
+ throw new Error(`session backup does not match the migration source: ${backupPath}`);
285
+ return backupPath;
305
286
  }
306
-
307
287
  function replaceAtomically(path, source, replacement, backupPath) {
308
- if (!readFileSync(path).equals(source)) {
309
- throw new Error("session artifact changed while compatibility migration was preparing it; stop every DSH process and retry");
310
- }
311
- const temporary = `${path}.odai-compat-${process.pid}-${randomUUID()}.tmp`;
312
- const mode = statSync(path).mode & 0o777;
313
- let published = false;
314
- try {
315
- writeDurably(temporary, replacement, mode);
316
- renameSync(temporary, path);
317
- published = true;
318
- syncDirectory(dirname(path));
319
- if (!readFileSync(path).equals(replacement)) throw new Error("published session artifact failed byte verification");
320
- const verified = transformArtifact(path);
321
- if (verified.repaired !== 0) throw new Error("published session artifact still contains required Odai events");
322
- } catch (error) {
323
- if (published) {
324
- const restore = `${path}.odai-restore-${process.pid}-${randomUUID()}.tmp`;
325
- try {
326
- writeDurably(restore, readFileSync(backupPath), mode);
327
- renameSync(restore, path);
288
+ if (!readFileSync(path).equals(source))
289
+ throw new Error("session artifact changed while compatibility migration was preparing it; stop every DSH process and retry");
290
+ const temporary = `${path}.odai-compat-${process.pid}-${randomUUID()}.tmp`;
291
+ const mode = statSync(path).mode & 0o777;
292
+ let published = false;
293
+ try {
294
+ writeDurably(temporary, replacement, mode);
295
+ renameSync(temporary, path);
296
+ published = true;
328
297
  syncDirectory(dirname(path));
329
- } finally {
330
- rmSync(restore, { force: true });
331
- }
298
+ if (!readFileSync(path).equals(replacement))
299
+ throw new Error("published session artifact failed byte verification");
300
+ const verified = transformArtifact(path);
301
+ if (verified.repaired !== 0)
302
+ throw new Error("published session artifact still contains required Odai events");
303
+ }
304
+ catch (error) {
305
+ if (published) {
306
+ const restore = `${path}.odai-restore-${process.pid}-${randomUUID()}.tmp`;
307
+ try {
308
+ writeDurably(restore, readFileSync(backupPath), mode);
309
+ renameSync(restore, path);
310
+ syncDirectory(dirname(path));
311
+ }
312
+ finally {
313
+ rmSync(restore, { force: true });
314
+ }
315
+ }
316
+ throw error;
317
+ }
318
+ finally {
319
+ rmSync(temporary, { force: true });
332
320
  }
333
- throw error;
334
- } finally {
335
- rmSync(temporary, { force: true });
336
- }
337
321
  }
338
-
339
322
  function processLegacySessionLogs(options, writeChanges) {
340
- const dshHome = resolveDshHome(options.dshHome);
341
- const sessionRoot = resolve(options.sessionRoot ?? resolve(dshHome, "sessions"));
342
- const artifacts = listSessionArtifacts(sessionRoot);
343
- const result = {
344
- dshHome,
345
- sessionRoot,
346
- scannedArtifacts: artifacts.length,
347
- matchedArtifacts: 0,
348
- matchedEvents: 0,
349
- repairedArtifacts: 0,
350
- repairedEvents: 0,
351
- backupPaths: [],
352
- tornArtifacts: [],
353
- failures: [],
354
- };
355
-
356
- for (const path of artifacts) {
357
- try {
358
- const transformed = transformArtifact(path);
359
- if (transformed.torn) result.tornArtifacts.push(path);
360
- if (transformed.repaired === 0) continue;
361
- result.matchedArtifacts += 1;
362
- result.matchedEvents += transformed.repaired;
363
- if (!writeChanges) continue;
364
- const backupPath = createVerifiedBackup(path, transformed.source);
365
- replaceAtomically(path, transformed.source, transformed.buffer, backupPath);
366
- result.backupPaths.push(backupPath);
367
- result.repairedArtifacts += 1;
368
- result.repairedEvents += transformed.repaired;
369
- } catch (error) {
370
- result.failures.push({
371
- path,
372
- error: error instanceof Error ? error.message : String(error),
373
- });
323
+ const dshHome = resolveDshHome(options.dshHome);
324
+ const sessionRoot = resolve(options.sessionRoot ?? resolve(dshHome, "sessions"));
325
+ const artifacts = listSessionArtifacts(sessionRoot);
326
+ const result = {
327
+ dshHome, sessionRoot, scannedArtifacts: artifacts.length, matchedArtifacts: 0, matchedEvents: 0,
328
+ repairedArtifacts: 0, repairedEvents: 0, backupPaths: [], tornArtifacts: [], failures: [],
329
+ };
330
+ for (const path of artifacts) {
331
+ try {
332
+ const transformed = transformArtifact(path);
333
+ if (transformed.torn)
334
+ result.tornArtifacts.push(path);
335
+ if (transformed.repaired === 0)
336
+ continue;
337
+ result.matchedArtifacts += 1;
338
+ result.matchedEvents += transformed.repaired;
339
+ if (!writeChanges)
340
+ continue;
341
+ const backupPath = createVerifiedBackup(path, transformed.source);
342
+ replaceAtomically(path, transformed.source, transformed.buffer, backupPath);
343
+ result.backupPaths.push(backupPath);
344
+ result.repairedArtifacts += 1;
345
+ result.repairedEvents += transformed.repaired;
346
+ }
347
+ catch (error) {
348
+ result.failures.push({ path, error: error instanceof Error ? error.message : String(error) });
349
+ }
374
350
  }
375
- }
376
- return result;
351
+ return result;
377
352
  }
378
-
379
353
  export function inspectLegacySessionLogs(options = {}) {
380
- return processLegacySessionLogs(options, false);
354
+ return processLegacySessionLogs(options, false);
381
355
  }
382
-
383
356
  export function repairLegacySessionLogs(options = {}) {
384
- if (options.confirmDshStopped !== true) {
385
- throw new Error("refusing to rewrite session artifacts until confirmDshStopped is true; stop every DSH process first");
386
- }
387
- assertDshStopped(options);
388
- return processLegacySessionLogs(options, true);
357
+ if (options.confirmDshStopped !== true)
358
+ throw new Error("refusing to rewrite session artifacts until confirmDshStopped is true; stop every DSH process first");
359
+ assertDshStopped(options);
360
+ return processLegacySessionLogs(options, true);
389
361
  }
362
+ //# sourceMappingURL=session-compat.mjs.map