odai-dsh-agent 0.2.5 → 0.2.7

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 +398 -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 +190 -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 +1140 -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 +93 -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 +15 -14
  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 +80 -0
  96. package/preset/odai/runtime/router.d.mts.map +1 -0
  97. package/preset/odai/runtime/router.mjs +509 -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 +64 -0
  104. package/preset/odai/runtime/routing-context.d.mts.map +1 -0
  105. package/preset/odai/runtime/routing-context.mjs +398 -277
  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 +237 -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,344 +1,366 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
- import {
3
- closeSync,
4
- existsSync,
5
- fsyncSync,
6
- ftruncateSync,
7
- mkdirSync,
8
- openSync,
9
- readFileSync,
10
- rmSync,
11
- statSync,
12
- writeSync,
13
- } from "node:fs";
2
+ import { closeSync, existsSync, fsyncSync, ftruncateSync, mkdirSync, openSync, readFileSync, rmSync, statSync, writeSync, } from "node:fs";
14
3
  import { dirname, resolve } from "node:path";
15
-
4
+ import { isUnknownRecord } from "./runtime-types.mjs";
16
5
  const STORE_SCHEMA_VERSION = 1;
17
6
  const GLOBAL_STATE_KEY = Symbol.for("odai.dsh.session-evidence.v2");
18
-
7
+ function isSessionEvidenceSharedState(value) {
8
+ return isUnknownRecord(value) && value.bySession instanceof WeakMap;
9
+ }
19
10
  function sharedState() {
20
- if (!globalThis[GLOBAL_STATE_KEY]) {
21
- Object.defineProperty(globalThis, GLOBAL_STATE_KEY, {
22
- configurable: false,
23
- enumerable: false,
24
- writable: false,
25
- value: { bySession: new WeakMap() },
11
+ const root = globalThis;
12
+ const existing = root[GLOBAL_STATE_KEY];
13
+ if (isSessionEvidenceSharedState(existing))
14
+ return existing;
15
+ const created = { bySession: new WeakMap() };
16
+ Object.defineProperty(root, GLOBAL_STATE_KEY, {
17
+ configurable: false,
18
+ enumerable: false,
19
+ writable: false,
20
+ value: created,
26
21
  });
27
- }
28
- return globalThis[GLOBAL_STATE_KEY];
22
+ return created;
29
23
  }
30
-
31
24
  function isOdaiEvent(event) {
32
- return typeof event?.type === "string" && event.type.startsWith("odai/");
25
+ return isUnknownRecord(event)
26
+ && typeof event.type === "string"
27
+ && event.type.startsWith("odai/")
28
+ && isUnknownRecord(event.data);
33
29
  }
34
-
35
30
  function sessionIdOf(agent) {
36
- const id = agent?.session?.header?.id;
37
- return typeof id === "string" && id !== "" ? id : undefined;
31
+ const id = agent?.session?.header?.id;
32
+ return typeof id === "string" && id !== "" ? id : undefined;
38
33
  }
39
-
40
34
  function evidencePath(root, sessionId) {
41
- const key = createHash("sha256").update(sessionId).digest("hex");
42
- return resolve(root, `${key}.jsonl`);
35
+ const key = createHash("sha256").update(sessionId).digest("hex");
36
+ return resolve(root, `${key}.jsonl`);
43
37
  }
44
-
45
38
  function stableJson(value) {
46
- if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
47
- if (value !== null && typeof value === "object") {
48
- return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
49
- }
50
- return JSON.stringify(value);
39
+ if (Array.isArray(value))
40
+ return `[${value.map(stableJson).join(",")}]`;
41
+ if (isUnknownRecord(value)) {
42
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
43
+ }
44
+ return JSON.stringify(value);
51
45
  }
52
-
53
46
  function evidenceId(type, data) {
54
- let identity;
55
- if (Number.isSafeInteger(data?.turn) && Number.isSafeInteger(data?.step)) {
56
- identity = `${type}:${data.turn}:${data.step}`;
57
- } else if (typeof data?.callId === "string" && data.callId !== "") {
58
- identity = `${type}:${data.callId}`;
59
- } else {
60
- identity = `${type}:${stableJson(data)}`;
61
- }
62
- return createHash("sha256").update(identity).digest("hex");
47
+ let identity;
48
+ if (Number.isSafeInteger(data?.turn) && Number.isSafeInteger(data?.step)) {
49
+ identity = `${type}:${data.turn}:${data.step}`;
50
+ }
51
+ else if (typeof data?.callId === "string" && data.callId !== "") {
52
+ identity = `${type}:${data.callId}`;
53
+ }
54
+ else {
55
+ identity = `${type}:${stableJson(data)}`;
56
+ }
57
+ return createHash("sha256").update(identity).digest("hex");
63
58
  }
64
-
65
59
  function snapshotData(data, type) {
66
- let encoded;
67
- try {
68
- encoded = JSON.stringify(data);
69
- } catch (error) {
70
- throw new Error(`odai evidence ${type} is not JSON-serializable`, { cause: error });
71
- }
72
- if (encoded === undefined) throw new Error(`odai evidence ${type} is not JSON-serializable`);
73
- return JSON.parse(encoded);
60
+ let encoded;
61
+ try {
62
+ encoded = JSON.stringify(data);
63
+ }
64
+ catch (error) {
65
+ throw new Error(`odai evidence ${type} is not JSON-serializable`, { cause: error });
66
+ }
67
+ if (encoded === undefined)
68
+ throw new Error(`odai evidence ${type} is not JSON-serializable`);
69
+ const snapshot = JSON.parse(encoded);
70
+ if (!isUnknownRecord(snapshot))
71
+ throw new Error(`odai evidence ${type} must be an object`);
72
+ return snapshot;
74
73
  }
75
-
76
74
  function readCompleteLines(path, strict) {
77
- if (!existsSync(path)) return [];
78
- const text = readFileSync(path, "utf8");
79
- if (strict && text !== "" && !text.endsWith("\n")) {
80
- throw new Error(`odai evidence log has an unterminated final record: ${path}`);
81
- }
82
- const completeLength = text.endsWith("\n") ? text.length : text.lastIndexOf("\n") + 1;
83
- if (completeLength <= 0) return [];
84
- return text.slice(0, completeLength).split("\n").filter(Boolean);
75
+ if (!existsSync(path))
76
+ return [];
77
+ const text = readFileSync(path, "utf8");
78
+ if (strict && text !== "" && !text.endsWith("\n")) {
79
+ throw new Error(`odai evidence log has an unterminated final record: ${path}`);
80
+ }
81
+ const completeLength = text.endsWith("\n") ? text.length : text.lastIndexOf("\n") + 1;
82
+ if (completeLength <= 0)
83
+ return [];
84
+ return text.slice(0, completeLength).split("\n").filter(Boolean);
85
+ }
86
+ function parseStoredRecord(value, sessionId) {
87
+ if (!isUnknownRecord(value)
88
+ || value.schemaVersion !== STORE_SCHEMA_VERSION
89
+ || value.sessionId !== sessionId
90
+ || !isOdaiEvent(value)
91
+ || typeof value.time !== "number"
92
+ || !Number.isSafeInteger(value.time)
93
+ || value.time < 0)
94
+ return undefined;
95
+ const id = typeof value.id === "string" && value.id !== "" ? value.id : evidenceId(value.type, value.data);
96
+ return {
97
+ schemaVersion: STORE_SCHEMA_VERSION,
98
+ id,
99
+ sessionId,
100
+ type: value.type,
101
+ time: value.time,
102
+ data: value.data,
103
+ };
85
104
  }
86
-
87
105
  function readStoredState(root, sessionId, logger, strict = false) {
88
- const path = evidencePath(root, sessionId);
89
- const events = [];
90
- const ids = new Set();
91
- for (const [index, line] of readCompleteLines(path, strict).entries()) {
92
- try {
93
- const record = JSON.parse(line);
94
- if (record?.schemaVersion !== STORE_SCHEMA_VERSION
95
- || record.sessionId !== sessionId
96
- || !isOdaiEvent(record)
97
- || !Number.isSafeInteger(record.time)
98
- || record.time < 0
99
- || record.data === undefined) {
100
- throw new Error("invalid evidence record");
101
- }
102
- const id = typeof record.id === "string" && record.id !== ""
103
- ? record.id
104
- : evidenceId(record.type, record.data);
105
- if (ids.has(id)) continue;
106
- ids.add(id);
107
- events.push(Object.freeze({ id, type: record.type, time: record.time, data: record.data }));
108
- } catch (error) {
109
- if (strict) throw new Error(`invalid odai evidence at ${path}:${index + 1}`, { cause: error });
110
- logger.warn(`ignored invalid odai evidence at ${path}:${index + 1}: ${error instanceof Error ? error.message : String(error)}`);
106
+ const path = evidencePath(root, sessionId);
107
+ const events = [];
108
+ const ids = new Set();
109
+ for (const [index, line] of readCompleteLines(path, strict).entries()) {
110
+ try {
111
+ const parsed = JSON.parse(line);
112
+ const record = parseStoredRecord(parsed, sessionId);
113
+ if (!record)
114
+ throw new Error("invalid evidence record");
115
+ if (ids.has(record.id))
116
+ continue;
117
+ ids.add(record.id);
118
+ events.push(Object.freeze({ id: record.id, type: record.type, time: record.time, data: record.data }));
119
+ }
120
+ catch (error) {
121
+ if (strict)
122
+ throw new Error(`invalid odai evidence at ${path}:${index + 1}`, { cause: error });
123
+ logger.warn(`ignored invalid odai evidence at ${path}:${index + 1}: ${error instanceof Error ? error.message : String(error)}`);
124
+ }
111
125
  }
112
- }
113
- return { events, ids };
126
+ return { events, ids, sessionId };
114
127
  }
115
-
116
128
  function mergeUnique(target, events) {
117
- for (const event of events) {
118
- const id = event.id ?? evidenceId(event.type, event.data);
119
- if (target.ids.has(id)) continue;
120
- target.ids.add(id);
121
- target.events.push(Object.freeze({ id, type: event.type, time: event.time, data: event.data }));
122
- }
129
+ for (const event of events) {
130
+ const id = event.id ?? evidenceId(event.type, event.data);
131
+ if (target.ids.has(id))
132
+ continue;
133
+ target.ids.add(id);
134
+ target.events.push(Object.freeze({ id, type: event.type, time: event.time, data: event.data }));
135
+ }
123
136
  }
124
-
125
137
  function stateFor(agent, root, logger) {
126
- const session = agent?.session;
127
- const owner = session && typeof session === "object"
128
- ? session
129
- : agent && typeof agent === "object"
130
- ? agent
131
- : undefined;
132
- if (!owner) return { events: [], ids: new Set(), sessionId: undefined };
133
-
134
- const shared = sharedState();
135
- let roots = shared.bySession.get(owner);
136
- if (!roots) {
137
- roots = new Map();
138
- shared.bySession.set(owner, roots);
139
- }
140
- const existing = roots.get(root);
141
- if (existing) return existing;
142
-
143
- const sessionId = sessionIdOf(agent);
144
- const state = { events: [], ids: new Set(), sessionId };
145
- const legacy = Array.isArray(session?.events) ? session.events.filter(isOdaiEvent) : [];
146
- mergeUnique(state, legacy);
147
- if (sessionId) mergeUnique(state, readStoredState(root, sessionId, logger).events);
148
- roots.set(root, state);
149
- return state;
138
+ const session = agent?.session;
139
+ const owner = session && typeof session === "object"
140
+ ? session
141
+ : agent && typeof agent === "object"
142
+ ? agent
143
+ : undefined;
144
+ if (!owner)
145
+ return { events: [], ids: new Set(), sessionId: undefined };
146
+ const shared = sharedState();
147
+ let roots = shared.bySession.get(owner);
148
+ if (!roots) {
149
+ roots = new Map();
150
+ shared.bySession.set(owner, roots);
151
+ }
152
+ const existing = roots.get(root);
153
+ if (existing)
154
+ return existing;
155
+ const sessionId = sessionIdOf(agent);
156
+ const state = { events: [], ids: new Set(), sessionId };
157
+ const legacy = Array.isArray(session?.events) ? session.events.filter(isOdaiEvent) : [];
158
+ mergeUnique(state, legacy);
159
+ if (sessionId)
160
+ mergeUnique(state, readStoredState(root, sessionId, logger).events);
161
+ roots.set(root, state);
162
+ return state;
163
+ }
164
+ function errorCode(error) {
165
+ if (error === null || typeof error !== "object" || !("code" in error))
166
+ return undefined;
167
+ return typeof error.code === "string" ? error.code : undefined;
150
168
  }
151
-
152
169
  function readLockOwner(lockPath) {
153
- try {
154
- const value = readFileSync(lockPath, "utf8").trim();
155
- const separator = value.indexOf(":");
156
- const pid = Number(separator < 0 ? "" : value.slice(0, separator));
157
- return Number.isSafeInteger(pid) && pid > 0 && separator < value.length - 1
158
- ? { value, pid }
159
- : undefined;
160
- } catch (error) {
161
- if (error?.code === "ENOENT") return undefined;
162
- throw error;
163
- }
170
+ try {
171
+ const value = readFileSync(lockPath, "utf8").trim();
172
+ const separator = value.indexOf(":");
173
+ const pid = Number(separator < 0 ? "" : value.slice(0, separator));
174
+ return Number.isSafeInteger(pid) && pid > 0 && separator < value.length - 1 ? { value, pid } : undefined;
175
+ }
176
+ catch (error) {
177
+ if (errorCode(error) === "ENOENT")
178
+ return undefined;
179
+ throw error;
180
+ }
164
181
  }
165
-
166
182
  function processIsAlive(pid) {
167
- try {
168
- process.kill(pid, 0);
169
- return true;
170
- } catch (error) {
171
- return error?.code !== "ESRCH";
172
- }
183
+ try {
184
+ process.kill(pid, 0);
185
+ return true;
186
+ }
187
+ catch (error) {
188
+ return errorCode(error) !== "ESRCH";
189
+ }
173
190
  }
174
-
175
191
  function createLock(lockPath, owner) {
176
- const handle = openSync(lockPath, "wx", 0o600);
177
- try {
178
- const content = Buffer.from(`${owner}\n`, "utf8");
179
- if (writeSync(handle, content, 0, content.length) !== content.length) {
180
- throw new Error(`short write while acquiring odai evidence lock: ${lockPath}`);
192
+ const handle = openSync(lockPath, "wx", 0o600);
193
+ try {
194
+ const content = Buffer.from(`${owner}\n`, "utf8");
195
+ if (writeSync(handle, content, 0, content.length) !== content.length) {
196
+ throw new Error(`short write while acquiring odai evidence lock: ${lockPath}`);
197
+ }
198
+ fsyncSync(handle);
199
+ return handle;
200
+ }
201
+ catch (error) {
202
+ closeSync(handle);
203
+ rmSync(lockPath, { force: true });
204
+ throw error;
181
205
  }
182
- fsyncSync(handle);
183
- return handle;
184
- } catch (error) {
185
- closeSync(handle);
186
- rmSync(lockPath, { force: true });
187
- throw error;
188
- }
189
206
  }
190
-
191
207
  function releaseLock(handle, lockPath, owner) {
192
- closeSync(handle);
193
- const current = readLockOwner(lockPath);
194
- if (current === undefined) throw new Error(`odai evidence lock disappeared before release: ${lockPath}`);
195
- if (current.value !== owner) throw new Error(`odai evidence lock ownership changed before release: ${lockPath}`);
196
- rmSync(lockPath);
208
+ closeSync(handle);
209
+ const current = readLockOwner(lockPath);
210
+ if (current === undefined)
211
+ throw new Error(`odai evidence lock disappeared before release: ${lockPath}`);
212
+ if (current.value !== owner)
213
+ throw new Error(`odai evidence lock ownership changed before release: ${lockPath}`);
214
+ rmSync(lockPath);
197
215
  }
198
-
199
216
  function acquireLock(path) {
200
- const lockPath = `${path}.lock`;
201
- const claimPath = `${lockPath}.claim`;
202
- const owner = `${process.pid}:${randomUUID()}`;
203
- const claimOwner = `${process.pid}:${randomUUID()}`;
204
- let claimHandle;
205
- try {
206
- claimHandle = createLock(claimPath, claimOwner);
207
- } catch (error) {
208
- if (error?.code === "EEXIST") {
209
- throw new Error(`odai evidence lock acquisition is already in progress: ${path}`);
210
- }
211
- throw error;
212
- }
213
-
214
- let handle;
215
- try {
217
+ const lockPath = `${path}.lock`;
218
+ const claimPath = `${lockPath}.claim`;
219
+ const owner = `${process.pid}:${randomUUID()}`;
220
+ const claimOwner = `${process.pid}:${randomUUID()}`;
221
+ let claimHandle;
216
222
  try {
217
- handle = createLock(lockPath, owner);
218
- } catch (error) {
219
- if (error?.code !== "EEXIST") throw error;
220
- const current = readLockOwner(lockPath);
221
- if (current === undefined || processIsAlive(current.pid)) {
222
- throw new Error(`odai evidence is being updated by another runtime: ${path}`);
223
- }
224
- rmSync(lockPath);
225
- handle = createLock(lockPath, owner);
223
+ claimHandle = createLock(claimPath, claimOwner);
224
+ }
225
+ catch (error) {
226
+ if (errorCode(error) === "EEXIST")
227
+ throw new Error(`odai evidence lock acquisition is already in progress: ${path}`);
228
+ throw error;
226
229
  }
227
- } finally {
230
+ let handle;
228
231
  try {
229
- releaseLock(claimHandle, claimPath, claimOwner);
230
- } catch (error) {
231
- if (handle !== undefined) {
232
232
  try {
233
- releaseLock(handle, lockPath, owner);
234
- } catch {
235
- // Preserve the claim failure; lock cleanup already failed closed.
233
+ handle = createLock(lockPath, owner);
234
+ }
235
+ catch (error) {
236
+ if (errorCode(error) !== "EEXIST")
237
+ throw error;
238
+ const current = readLockOwner(lockPath);
239
+ if (current === undefined || processIsAlive(current.pid))
240
+ throw new Error(`odai evidence is being updated by another runtime: ${path}`);
241
+ rmSync(lockPath);
242
+ handle = createLock(lockPath, owner);
243
+ }
244
+ }
245
+ finally {
246
+ try {
247
+ releaseLock(claimHandle, claimPath, claimOwner);
248
+ }
249
+ catch (error) {
250
+ if (handle !== undefined) {
251
+ try {
252
+ releaseLock(handle, lockPath, owner);
253
+ }
254
+ catch { }
255
+ handle = undefined;
256
+ }
257
+ throw error;
236
258
  }
237
- handle = undefined;
238
- }
239
- throw error;
240
259
  }
241
- }
242
-
243
- if (handle === undefined) throw new Error(`could not lock odai evidence: ${path}`);
244
- return () => releaseLock(handle, lockPath, owner);
260
+ if (handle === undefined)
261
+ throw new Error(`could not lock odai evidence: ${path}`);
262
+ const ownedHandle = handle;
263
+ return () => releaseLock(ownedHandle, lockPath, owner);
245
264
  }
246
-
247
265
  function syncDirectory(path) {
248
- let handle;
249
- try {
250
- handle = openSync(path, "r");
251
- fsyncSync(handle);
252
- } catch {
253
- // Windows does not expose directory fsync; the evidence file itself was synced.
254
- } finally {
255
- if (handle !== undefined) closeSync(handle);
256
- }
266
+ let handle;
267
+ try {
268
+ handle = openSync(path, "r");
269
+ fsyncSync(handle);
270
+ }
271
+ catch {
272
+ // Windows does not expose directory fsync; the evidence file itself was synced.
273
+ }
274
+ finally {
275
+ if (handle !== undefined)
276
+ closeSync(handle);
277
+ }
257
278
  }
258
-
259
279
  function persistRecord(root, sessionId, record, logger) {
260
- mkdirSync(root, { recursive: true, mode: 0o700 });
261
- const path = evidencePath(root, sessionId);
262
- const release = acquireLock(path);
263
- try {
264
- const stored = readStoredState(root, sessionId, logger, true);
265
- if (stored.ids.has(record.id)) return false;
266
- const encoded = Buffer.from(`${JSON.stringify(record)}\n`, "utf8");
267
- const originalSize = existsSync(path) ? statSync(path).size : 0;
268
- const handle = openSync(path, "a", 0o600);
280
+ mkdirSync(root, { recursive: true, mode: 0o700 });
281
+ const path = evidencePath(root, sessionId);
282
+ const release = acquireLock(path);
269
283
  try {
270
- let offset = 0;
271
- while (offset < encoded.length) {
272
- const written = writeSync(handle, encoded, offset, encoded.length - offset);
273
- if (written <= 0) throw new Error(`short write while persisting odai evidence: ${path}`);
274
- offset += written;
275
- }
276
- fsyncSync(handle);
277
- } catch (error) {
278
- ftruncateSync(handle, originalSize);
279
- fsyncSync(handle);
280
- throw error;
281
- } finally {
282
- closeSync(handle);
284
+ const stored = readStoredState(root, sessionId, logger, true);
285
+ if (stored.ids.has(record.id))
286
+ return false;
287
+ const encoded = Buffer.from(`${JSON.stringify(record)}\n`, "utf8");
288
+ const originalSize = existsSync(path) ? statSync(path).size : 0;
289
+ const handle = openSync(path, "a", 0o600);
290
+ try {
291
+ let offset = 0;
292
+ while (offset < encoded.length) {
293
+ const written = writeSync(handle, encoded, offset, encoded.length - offset);
294
+ if (written <= 0)
295
+ throw new Error(`short write while persisting odai evidence: ${path}`);
296
+ offset += written;
297
+ }
298
+ fsyncSync(handle);
299
+ }
300
+ catch (error) {
301
+ ftruncateSync(handle, originalSize);
302
+ fsyncSync(handle);
303
+ throw error;
304
+ }
305
+ finally {
306
+ closeSync(handle);
307
+ }
308
+ syncDirectory(root);
309
+ return true;
310
+ }
311
+ finally {
312
+ release();
283
313
  }
284
- syncDirectory(root);
285
- return true;
286
- } finally {
287
- release();
288
- }
289
314
  }
290
-
291
315
  export function resolveSessionEvidenceRoot(routingConfigPath) {
292
- return resolve(dirname(routingConfigPath), "session-evidence");
316
+ return resolve(dirname(routingConfigPath), "session-evidence");
293
317
  }
294
-
295
- export function readStoredSessionEvidence(root, sessionId, logger = { warn() {} }) {
296
- if (typeof sessionId !== "string" || sessionId === "") return [];
297
- return readStoredState(resolve(root), sessionId, logger).events;
318
+ export function readStoredSessionEvidence(root, sessionId, logger = { warn() { } }) {
319
+ if (typeof sessionId !== "string" || sessionId === "")
320
+ return [];
321
+ return readStoredState(resolve(root), sessionId, logger).events;
298
322
  }
299
-
300
323
  export function createSessionEvidence(options) {
301
- const root = resolve(options.root);
302
- const logger = options.logger ?? { warn() {} };
303
-
304
- const events = (agent) => stateFor(agent, root, logger).events.slice();
305
- const has = (agent, type, predicate = () => true) => stateFor(agent, root, logger)
306
- .events
307
- .some((event) => event.type === type && predicate(event.data));
308
- const append = (agent, type, data) => {
309
- if (typeof type !== "string" || !type.startsWith("odai/")) {
310
- throw new TypeError("odai evidence type must start with odai/");
311
- }
312
- const state = stateFor(agent, root, logger);
313
- const snapshot = snapshotData(data, type);
314
- const id = evidenceId(type, snapshot);
315
- const event = Object.freeze({ id, type, time: Date.now(), data: snapshot });
316
- if (state.ids.has(id)) return state.events.find((candidate) => candidate.id === id);
317
-
318
- if (!state.sessionId) {
319
- state.ids.add(id);
320
- state.events.push(event);
321
- try {
322
- agent?.session?.append?.(type, event.data);
323
- } catch (error) {
324
- logger.warn(`failed to append transient ${type}: ${error instanceof Error ? error.message : String(error)}`);
325
- }
326
- return event;
327
- }
328
-
329
- const record = {
330
- schemaVersion: STORE_SCHEMA_VERSION,
331
- id,
332
- sessionId: state.sessionId,
333
- type,
334
- time: event.time,
335
- data: event.data,
324
+ const root = resolve(options.root);
325
+ const logger = options.logger ?? { warn() { } };
326
+ const events = (agent) => stateFor(agent, root, logger).events.slice();
327
+ const has = (agent, type, predicate = () => true) => stateFor(agent, root, logger)
328
+ .events
329
+ .some((event) => event.type === type && predicate(event.data));
330
+ const append = (agent, type, data) => {
331
+ if (typeof type !== "string" || !type.startsWith("odai/")) {
332
+ throw new TypeError("odai evidence type must start with odai/");
333
+ }
334
+ const state = stateFor(agent, root, logger);
335
+ const snapshot = snapshotData(data, type);
336
+ const id = evidenceId(type, snapshot);
337
+ const event = Object.freeze({ id, type, time: Date.now(), data: snapshot });
338
+ if (state.ids.has(id))
339
+ return state.events.find((candidate) => candidate.id === id);
340
+ if (!state.sessionId) {
341
+ state.ids.add(id);
342
+ state.events.push(event);
343
+ try {
344
+ agent?.session?.append?.(type, event.data);
345
+ }
346
+ catch (error) {
347
+ logger.warn(`failed to append transient ${type}: ${error instanceof Error ? error.message : String(error)}`);
348
+ }
349
+ return event;
350
+ }
351
+ const record = {
352
+ schemaVersion: STORE_SCHEMA_VERSION,
353
+ id,
354
+ sessionId: state.sessionId,
355
+ type,
356
+ time: event.time,
357
+ data: event.data,
358
+ };
359
+ persistRecord(root, state.sessionId, record, logger);
360
+ state.ids.add(id);
361
+ state.events.push(event);
362
+ return event;
336
363
  };
337
- persistRecord(root, state.sessionId, record, logger);
338
- state.ids.add(id);
339
- state.events.push(event);
340
- return event;
341
- };
342
-
343
- return Object.freeze({ append, events, has, root });
364
+ return Object.freeze({ append, events, has, root });
344
365
  }
366
+ //# sourceMappingURL=session-evidence.mjs.map