odai-dsh-agent 0.2.3 → 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 -2441
  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 -257
  165. package/src/dsh-version.mjs +0 -24
  166. package/src/installer.mjs +0 -398
@@ -1,24 +1,9 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
- import {
3
- closeSync,
4
- constants,
5
- existsSync,
6
- fstatSync,
7
- fsyncSync,
8
- lstatSync,
9
- mkdirSync,
10
- openSync,
11
- readFileSync,
12
- realpathSync,
13
- renameSync,
14
- rmSync,
15
- writeFileSync,
16
- } from "node:fs";
2
+ import { closeSync, constants, existsSync, fstatSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync, } from "node:fs";
17
3
  import { homedir } from "node:os";
18
4
  import { basename, dirname, resolve } from "node:path";
19
-
20
5
  import { acquireOwnedStoreLock } from "./store-lock.mjs";
21
-
6
+ import { isUnknownRecord } from "./runtime-types.mjs";
22
7
  export const MEMORY_STORE_SCHEMA_VERSION = 1;
23
8
  export const MEMORY_EXTRACTOR_VERSION = 1;
24
9
  export const MEMORY_MODES = Object.freeze(["auto", "off"]);
@@ -28,425 +13,326 @@ export const DEFAULT_MEMORY_SETTINGS = Object.freeze({ mode: "auto" });
28
13
  export const MAX_MEMORY_ENTRIES = 1_000;
29
14
  export const MAX_MEMORY_PROVENANCE = 8;
30
15
  export const MAX_MEMORY_VALUE_CHARS = 600;
31
-
32
16
  const STORE_FIELDS = new Set(["schemaVersion", "settings", "entries"]);
33
17
  const SETTINGS_FIELDS = new Set(["mode"]);
34
18
  const ENTRY_FIELDS = new Set([
35
- "id",
36
- "scope",
37
- "category",
38
- "subject",
39
- "value",
40
- "status",
41
- "confidence",
42
- "supersedes",
43
- "conflictsWith",
44
- "provenance",
45
- "createdAt",
46
- "updatedAt",
47
- "occurrences",
19
+ "id", "scope", "category", "subject", "value", "status", "confidence", "supersedes", "conflictsWith",
20
+ "provenance", "createdAt", "updatedAt", "occurrences",
48
21
  ]);
49
22
  const SCOPE_FIELDS = new Set(["kind", "key", "label"]);
50
23
  const PROVENANCE_FIELDS = new Set([
51
- "sourceKind",
52
- "sessionHash",
53
- "messageHash",
54
- "turn",
55
- "eventSeq",
56
- "excerptSha256",
57
- "observedAt",
58
- "extraction",
59
- "extractorVersion",
24
+ "sourceKind", "sessionHash", "messageHash", "turn", "eventSeq", "excerptSha256", "observedAt", "extraction", "extractorVersion",
60
25
  ]);
61
-
62
- export class MemoryStoreValidationError extends Error {}
63
-
64
- function hash(value) {
65
- return createHash("sha256").update(value).digest("hex");
26
+ export class MemoryStoreValidationError extends Error {
66
27
  }
67
-
68
- function assertPlainObject(value, field) {
69
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
70
- throw new TypeError(`${field} must be an object`);
71
- }
72
- return value;
28
+ function hash(value) { return createHash("sha256").update(value).digest("hex"); }
29
+ function errorCode(error) {
30
+ if (error === null || typeof error !== "object" || !("code" in error))
31
+ return undefined;
32
+ return typeof error.code === "string" ? error.code : undefined;
73
33
  }
74
-
75
34
  function rejectUnknown(value, allowed, field) {
76
- const unknown = Object.keys(value).filter((name) => !allowed.has(name));
77
- if (unknown.length > 0) throw new TypeError(`${field} has unknown fields: ${unknown.join(", ")}`);
35
+ const unknownFields = Object.keys(value).filter((name) => !allowed.has(name));
36
+ if (unknownFields.length > 0)
37
+ throw new TypeError(`${field} has unknown fields: ${unknownFields.join(", ")}`);
78
38
  }
79
-
80
39
  function requiredString(value, field, max = 1_000) {
81
- if (typeof value !== "string" || value.length === 0 || value.length > max) {
82
- throw new TypeError(`${field} must be a non-empty string of at most ${max} characters`);
83
- }
84
- return value;
40
+ if (typeof value !== "string" || value.length === 0 || value.length > max) {
41
+ throw new TypeError(`${field} must be a non-empty string of at most ${max} characters`);
42
+ }
43
+ return value;
85
44
  }
86
-
87
45
  function safeInteger(value, field) {
88
- if (!Number.isSafeInteger(value) || value < 0) throw new TypeError(`${field} must be a non-negative integer`);
89
- return value;
46
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
47
+ throw new TypeError(`${field} must be a non-negative integer`);
48
+ return value;
90
49
  }
91
-
50
+ function isMemoryMode(value) { return typeof value === "string" && MEMORY_MODES.includes(value); }
51
+ function isMemoryCategory(value) { return typeof value === "string" && MEMORY_CATEGORIES.includes(value); }
52
+ function isMemoryStatus(value) { return typeof value === "string" && MEMORY_STATUSES.includes(value); }
92
53
  function validateMode(mode, field = "memory mode") {
93
- if (!MEMORY_MODES.includes(mode)) throw new TypeError(`${field} must be auto or off`);
94
- return mode;
54
+ if (!isMemoryMode(mode))
55
+ throw new TypeError(`${field} must be auto or off`);
56
+ return mode;
95
57
  }
96
-
97
58
  function validateScope(value, field) {
98
- const scope = assertPlainObject(value, field);
99
- rejectUnknown(scope, SCOPE_FIELDS, field);
100
- if (!["global", "project"].includes(scope.kind)) throw new TypeError(`${field}.kind must be global or project`);
101
- requiredString(scope.key, `${field}.key`, 64);
102
- if (scope.kind === "global" && scope.key !== "global") throw new TypeError(`${field}.key must be global for global scope`);
103
- if (scope.kind === "project" && !/^[a-f0-9]{64}$/u.test(scope.key)) {
104
- throw new TypeError(`${field}.key must be a sha256 project key`);
105
- }
106
- if (scope.label !== undefined) requiredString(scope.label, `${field}.label`, 120);
107
- return Object.freeze({
108
- kind: scope.kind,
109
- key: scope.key,
110
- ...(scope.label === undefined ? {} : { label: scope.label }),
111
- });
59
+ if (!isUnknownRecord(value))
60
+ throw new TypeError(`${field} must be an object`);
61
+ rejectUnknown(value, SCOPE_FIELDS, field);
62
+ if (value.kind !== "global" && value.kind !== "project")
63
+ throw new TypeError(`${field}.kind must be global or project`);
64
+ const key = requiredString(value.key, `${field}.key`, 64);
65
+ if (value.kind === "global" && key !== "global")
66
+ throw new TypeError(`${field}.key must be global for global scope`);
67
+ if (value.kind === "project" && !/^[a-f0-9]{64}$/u.test(key))
68
+ throw new TypeError(`${field}.key must be a sha256 project key`);
69
+ const label = value.label === undefined ? undefined : requiredString(value.label, `${field}.label`, 120);
70
+ return Object.freeze({ kind: value.kind, key, ...(label === undefined ? {} : { label }) });
112
71
  }
113
-
114
72
  function validateProvenance(value, field) {
115
- const provenance = assertPlainObject(value, field);
116
- rejectUnknown(provenance, PROVENANCE_FIELDS, field);
117
- if (provenance.sourceKind !== "direct-user") throw new TypeError(`${field}.sourceKind must be direct-user`);
118
- if (!/^[a-f0-9]{64}$/u.test(provenance.sessionHash)) throw new TypeError(`${field}.sessionHash must be sha256`);
119
- if (!/^[a-f0-9]{64}$/u.test(provenance.messageHash)) throw new TypeError(`${field}.messageHash must be sha256`);
120
- if (!/^[a-f0-9]{64}$/u.test(provenance.excerptSha256)) throw new TypeError(`${field}.excerptSha256 must be sha256`);
121
- safeInteger(provenance.turn, `${field}.turn`);
122
- if (provenance.eventSeq !== undefined) safeInteger(provenance.eventSeq, `${field}.eventSeq`);
123
- safeInteger(provenance.observedAt, `${field}.observedAt`);
124
- if (!["local-explicit", "tool-exact-excerpt"].includes(provenance.extraction)) {
125
- throw new TypeError(`${field}.extraction is unsupported`);
126
- }
127
- if (provenance.extractorVersion !== MEMORY_EXTRACTOR_VERSION) {
128
- throw new TypeError(`${field}.extractorVersion is unsupported`);
129
- }
130
- return Object.freeze({
131
- sourceKind: "direct-user",
132
- sessionHash: provenance.sessionHash,
133
- messageHash: provenance.messageHash,
134
- turn: provenance.turn,
135
- ...(provenance.eventSeq === undefined ? {} : { eventSeq: provenance.eventSeq }),
136
- excerptSha256: provenance.excerptSha256,
137
- observedAt: provenance.observedAt,
138
- extraction: provenance.extraction,
139
- extractorVersion: provenance.extractorVersion,
140
- });
73
+ if (!isUnknownRecord(value))
74
+ throw new TypeError(`${field} must be an object`);
75
+ rejectUnknown(value, PROVENANCE_FIELDS, field);
76
+ if (value.sourceKind !== "direct-user")
77
+ throw new TypeError(`${field}.sourceKind must be direct-user`);
78
+ const sessionHash = requiredString(value.sessionHash, `${field}.sessionHash`, 64);
79
+ const messageHash = requiredString(value.messageHash, `${field}.messageHash`, 64);
80
+ const excerptSha256 = requiredString(value.excerptSha256, `${field}.excerptSha256`, 64);
81
+ if (!/^[a-f0-9]{64}$/u.test(sessionHash))
82
+ throw new TypeError(`${field}.sessionHash must be sha256`);
83
+ if (!/^[a-f0-9]{64}$/u.test(messageHash))
84
+ throw new TypeError(`${field}.messageHash must be sha256`);
85
+ if (!/^[a-f0-9]{64}$/u.test(excerptSha256))
86
+ throw new TypeError(`${field}.excerptSha256 must be sha256`);
87
+ const turn = safeInteger(value.turn, `${field}.turn`);
88
+ const eventSeq = value.eventSeq === undefined ? undefined : safeInteger(value.eventSeq, `${field}.eventSeq`);
89
+ const observedAt = safeInteger(value.observedAt, `${field}.observedAt`);
90
+ if (value.extraction !== "local-explicit" && value.extraction !== "tool-exact-excerpt")
91
+ throw new TypeError(`${field}.extraction is unsupported`);
92
+ if (value.extractorVersion !== MEMORY_EXTRACTOR_VERSION)
93
+ throw new TypeError(`${field}.extractorVersion is unsupported`);
94
+ return Object.freeze({
95
+ sourceKind: "direct-user", sessionHash, messageHash, turn, ...(eventSeq === undefined ? {} : { eventSeq }),
96
+ excerptSha256, observedAt, extraction: value.extraction, extractorVersion: MEMORY_EXTRACTOR_VERSION,
97
+ });
141
98
  }
142
-
143
99
  function validateEntry(value, field) {
144
- const entry = assertPlainObject(value, field);
145
- rejectUnknown(entry, ENTRY_FIELDS, field);
146
- requiredString(entry.id, `${field}.id`, 80);
147
- const scope = validateScope(entry.scope, `${field}.scope`);
148
- if (!MEMORY_CATEGORIES.includes(entry.category)) throw new TypeError(`${field}.category is unsupported`);
149
- requiredString(entry.subject, `${field}.subject`, 64);
150
- requiredString(entry.value, `${field}.value`, MAX_MEMORY_VALUE_CHARS);
151
- if (!MEMORY_STATUSES.includes(entry.status)) throw new TypeError(`${field}.status is unsupported`);
152
- if (!["high", "medium"].includes(entry.confidence)) throw new TypeError(`${field}.confidence is unsupported`);
153
- if (!Array.isArray(entry.supersedes) || entry.supersedes.length > 64) {
154
- throw new TypeError(`${field}.supersedes must be an array with at most 64 entries`);
155
- }
156
- const supersedes = entry.supersedes.map((id, index) => requiredString(id, `${field}.supersedes[${index}]`, 80));
157
- if (!Array.isArray(entry.conflictsWith) || entry.conflictsWith.length > 64) {
158
- throw new TypeError(`${field}.conflictsWith must be an array with at most 64 entries`);
159
- }
160
- const conflictsWith = entry.conflictsWith.map((id, index) => requiredString(id, `${field}.conflictsWith[${index}]`, 80));
161
- if (!Array.isArray(entry.provenance)
162
- || entry.provenance.length === 0
163
- || entry.provenance.length > MAX_MEMORY_PROVENANCE) {
164
- throw new TypeError(`${field}.provenance must contain 1-${MAX_MEMORY_PROVENANCE} entries`);
165
- }
166
- const provenance = entry.provenance.map((item, index) => validateProvenance(item, `${field}.provenance[${index}]`));
167
- const createdAt = safeInteger(entry.createdAt, `${field}.createdAt`);
168
- const updatedAt = safeInteger(entry.updatedAt, `${field}.updatedAt`);
169
- if (updatedAt < createdAt) throw new TypeError(`${field}.updatedAt must not precede createdAt`);
170
- const occurrences = safeInteger(entry.occurrences, `${field}.occurrences`);
171
- if (occurrences < 1) throw new TypeError(`${field}.occurrences must be positive`);
172
- return Object.freeze({
173
- id: entry.id,
174
- scope,
175
- category: entry.category,
176
- subject: entry.subject,
177
- value: entry.value,
178
- status: entry.status,
179
- confidence: entry.confidence,
180
- supersedes: Object.freeze(supersedes),
181
- conflictsWith: Object.freeze(conflictsWith),
182
- provenance: Object.freeze(provenance),
183
- createdAt,
184
- updatedAt,
185
- occurrences,
186
- });
100
+ if (!isUnknownRecord(value))
101
+ throw new TypeError(`${field} must be an object`);
102
+ rejectUnknown(value, ENTRY_FIELDS, field);
103
+ const id = requiredString(value.id, `${field}.id`, 80);
104
+ const scope = validateScope(value.scope, `${field}.scope`);
105
+ if (!isMemoryCategory(value.category))
106
+ throw new TypeError(`${field}.category is unsupported`);
107
+ const subject = requiredString(value.subject, `${field}.subject`, 64);
108
+ const entryValue = requiredString(value.value, `${field}.value`, MAX_MEMORY_VALUE_CHARS);
109
+ if (!isMemoryStatus(value.status))
110
+ throw new TypeError(`${field}.status is unsupported`);
111
+ if (value.confidence !== "high" && value.confidence !== "medium")
112
+ throw new TypeError(`${field}.confidence is unsupported`);
113
+ if (!Array.isArray(value.supersedes) || value.supersedes.length > 64)
114
+ throw new TypeError(`${field}.supersedes must be an array with at most 64 entries`);
115
+ const supersedes = value.supersedes.map((entryId, index) => requiredString(entryId, `${field}.supersedes[${index}]`, 80));
116
+ if (!Array.isArray(value.conflictsWith) || value.conflictsWith.length > 64)
117
+ throw new TypeError(`${field}.conflictsWith must be an array with at most 64 entries`);
118
+ const conflictsWith = value.conflictsWith.map((entryId, index) => requiredString(entryId, `${field}.conflictsWith[${index}]`, 80));
119
+ if (!Array.isArray(value.provenance) || value.provenance.length === 0 || value.provenance.length > MAX_MEMORY_PROVENANCE) {
120
+ throw new TypeError(`${field}.provenance must contain 1-${MAX_MEMORY_PROVENANCE} entries`);
121
+ }
122
+ const provenance = value.provenance.map((item, index) => validateProvenance(item, `${field}.provenance[${index}]`));
123
+ const createdAt = safeInteger(value.createdAt, `${field}.createdAt`);
124
+ const updatedAt = safeInteger(value.updatedAt, `${field}.updatedAt`);
125
+ if (updatedAt < createdAt)
126
+ throw new TypeError(`${field}.updatedAt must not precede createdAt`);
127
+ const occurrences = safeInteger(value.occurrences, `${field}.occurrences`);
128
+ if (occurrences < 1)
129
+ throw new TypeError(`${field}.occurrences must be positive`);
130
+ return Object.freeze({ id, scope, category: value.category, subject, value: entryValue, status: value.status, confidence: value.confidence,
131
+ supersedes: Object.freeze(supersedes), conflictsWith: Object.freeze(conflictsWith), provenance: Object.freeze(provenance), createdAt, updatedAt, occurrences });
187
132
  }
188
-
189
133
  function validateStore(value, field) {
190
- const store = assertPlainObject(value, field);
191
- rejectUnknown(store, STORE_FIELDS, field);
192
- if (store.schemaVersion !== MEMORY_STORE_SCHEMA_VERSION) {
193
- throw new TypeError(`${field} has unsupported schemaVersion ${String(store.schemaVersion)}`);
194
- }
195
- const settingsValue = store.settings === undefined ? {} : assertPlainObject(store.settings, `${field}.settings`);
196
- rejectUnknown(settingsValue, SETTINGS_FIELDS, `${field}.settings`);
197
- const settings = Object.freeze({
198
- ...(settingsValue.mode === undefined ? {} : { mode: validateMode(settingsValue.mode, `${field}.settings.mode`) }),
199
- });
200
- if (!Array.isArray(store.entries) || store.entries.length > MAX_MEMORY_ENTRIES) {
201
- throw new TypeError(`${field}.entries must be an array with at most ${MAX_MEMORY_ENTRIES} records`);
202
- }
203
- const entries = store.entries.map((entry, index) => validateEntry(entry, `${field}.entries[${index}]`));
204
- const ids = new Set();
205
- for (const entry of entries) {
206
- if (ids.has(entry.id)) throw new TypeError(`${field}.entries contains duplicate id ${entry.id}`);
207
- ids.add(entry.id);
208
- }
209
- return Object.freeze({
210
- schemaVersion: MEMORY_STORE_SCHEMA_VERSION,
211
- settings,
212
- entries: Object.freeze(entries),
213
- });
214
- }
215
-
216
- function emptyStore() {
217
- return Object.freeze({
218
- schemaVersion: MEMORY_STORE_SCHEMA_VERSION,
219
- settings: Object.freeze({}),
220
- entries: Object.freeze([]),
221
- });
222
- }
223
-
224
- function assertNoSymlink(path, label) {
225
- if (!existsSync(path)) return;
226
- const stat = lstatSync(path);
227
- if (stat.isSymbolicLink()) throw new Error(`${label} must not be a symbolic link: ${path}`);
134
+ if (!isUnknownRecord(value))
135
+ throw new TypeError(`${field} must be an object`);
136
+ rejectUnknown(value, STORE_FIELDS, field);
137
+ if (value.schemaVersion !== MEMORY_STORE_SCHEMA_VERSION)
138
+ throw new TypeError(`${field} has unsupported schemaVersion ${String(value.schemaVersion)}`);
139
+ const settingsValue = value.settings === undefined ? {} : value.settings;
140
+ if (!isUnknownRecord(settingsValue))
141
+ throw new TypeError(`${field}.settings must be an object`);
142
+ rejectUnknown(settingsValue, SETTINGS_FIELDS, `${field}.settings`);
143
+ const settings = Object.freeze({ ...(settingsValue.mode === undefined ? {} : { mode: validateMode(settingsValue.mode, `${field}.settings.mode`) }) });
144
+ if (!Array.isArray(value.entries) || value.entries.length > MAX_MEMORY_ENTRIES)
145
+ throw new TypeError(`${field}.entries must be an array with at most ${MAX_MEMORY_ENTRIES} records`);
146
+ const entries = value.entries.map((entry, index) => validateEntry(entry, `${field}.entries[${index}]`));
147
+ const ids = new Set();
148
+ for (const entry of entries) {
149
+ if (ids.has(entry.id))
150
+ throw new TypeError(`${field}.entries contains duplicate id ${entry.id}`);
151
+ ids.add(entry.id);
152
+ }
153
+ return Object.freeze({ schemaVersion: MEMORY_STORE_SCHEMA_VERSION, settings, entries: Object.freeze(entries) });
228
154
  }
229
-
155
+ function emptyStore() { return Object.freeze({ schemaVersion: MEMORY_STORE_SCHEMA_VERSION, settings: Object.freeze({}), entries: Object.freeze([]) }); }
156
+ function assertNoSymlink(path, label) { if (existsSync(path) && lstatSync(path).isSymbolicLink())
157
+ throw new Error(`${label} must not be a symbolic link: ${path}`); }
230
158
  function assertSafeStorePath(storePath) {
231
- const parent = dirname(storePath);
232
- const grandparent = dirname(parent);
233
- assertNoSymlink(grandparent, "Odai memory parent");
234
- assertNoSymlink(parent, "Odai memory directory");
235
- assertNoSymlink(storePath, "Odai memory store");
236
- if (existsSync(parent) && !lstatSync(parent).isDirectory()) {
237
- throw new Error(`Odai memory directory is not a directory: ${parent}`);
238
- }
239
- if (existsSync(storePath) && !lstatSync(storePath).isFile()) {
240
- throw new Error(`Odai memory store is not a regular file: ${storePath}`);
241
- }
242
- return [grandparent, parent]
243
- .filter((path) => existsSync(path))
244
- .map((path) => {
245
- const stat = lstatSync(path);
246
- return Object.freeze({ path, dev: stat.dev, ino: stat.ino });
247
- });
159
+ const parent = dirname(storePath);
160
+ const grandparent = dirname(parent);
161
+ assertNoSymlink(grandparent, "Odai memory parent");
162
+ assertNoSymlink(parent, "Odai memory directory");
163
+ assertNoSymlink(storePath, "Odai memory store");
164
+ if (existsSync(parent) && !lstatSync(parent).isDirectory())
165
+ throw new Error(`Odai memory directory is not a directory: ${parent}`);
166
+ if (existsSync(storePath) && !lstatSync(storePath).isFile())
167
+ throw new Error(`Odai memory store is not a regular file: ${storePath}`);
168
+ return [grandparent, parent].filter((path) => existsSync(path)).map((path) => { const stat = lstatSync(path); return Object.freeze({ path, dev: stat.dev, ino: stat.ino }); });
248
169
  }
249
-
250
170
  function assertDirectoryIdentities(identities) {
251
- for (const identity of identities) {
252
- const stat = lstatSync(identity.path);
253
- if (stat.isSymbolicLink() || !stat.isDirectory() || stat.dev !== identity.dev || stat.ino !== identity.ino) {
254
- throw new Error(`Odai memory directory identity changed during an operation: ${identity.path}`);
171
+ for (const identity of identities) {
172
+ const stat = lstatSync(identity.path);
173
+ if (stat.isSymbolicLink() || !stat.isDirectory() || stat.dev !== identity.dev || stat.ino !== identity.ino)
174
+ throw new Error(`Odai memory directory identity changed during an operation: ${identity.path}`);
255
175
  }
256
- }
257
176
  }
258
-
259
177
  function readRegularFileNoFollow(path) {
260
- const noFollow = typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
261
- let fd;
262
- try {
263
- fd = openSync(path, constants.O_RDONLY | noFollow);
264
- const stat = fstatSync(fd);
265
- if (!stat.isFile()) throw new Error(`Odai memory store is not a regular file: ${path}`);
266
- return readFileSync(fd, "utf8");
267
- } finally {
268
- if (fd !== undefined) closeSync(fd);
269
- }
178
+ const noFollow = typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
179
+ let fd;
180
+ try {
181
+ fd = openSync(path, constants.O_RDONLY | noFollow);
182
+ const stat = fstatSync(fd);
183
+ if (!stat.isFile())
184
+ throw new Error(`Odai memory store is not a regular file: ${path}`);
185
+ return readFileSync(fd, "utf8");
186
+ }
187
+ finally {
188
+ if (fd !== undefined)
189
+ closeSync(fd);
190
+ }
270
191
  }
271
-
272
192
  export function resolveMemoryStorePath(configuredPath, env = process.env) {
273
- if (configuredPath !== undefined) {
274
- if (typeof configuredPath !== "string" || configuredPath.trim() === "") {
275
- throw new TypeError("config.memory.storePath must be a non-empty string");
193
+ if (configuredPath !== undefined) {
194
+ if (typeof configuredPath !== "string" || configuredPath.trim() === "")
195
+ throw new TypeError("config.memory.storePath must be a non-empty string");
196
+ return resolve(configuredPath.trim());
276
197
  }
277
- return resolve(configuredPath.trim());
278
- }
279
- const dshHome = typeof env.DSH_HOME === "string" && env.DSH_HOME.trim() !== ""
280
- ? resolve(env.DSH_HOME.trim())
281
- : resolve(homedir(), ".dsh");
282
- return resolve(dshHome, "odai", "memory", "store.json");
198
+ const dshHome = typeof env.DSH_HOME === "string" && env.DSH_HOME.trim() !== "" ? resolve(env.DSH_HOME.trim()) : resolve(homedir(), ".dsh");
199
+ return resolve(dshHome, "odai", "memory", "store.json");
283
200
  }
284
-
285
201
  export function readMemoryStore(storePath) {
286
- const identities = assertSafeStorePath(storePath);
287
- let text;
288
- try {
289
- text = readRegularFileNoFollow(storePath);
290
- } catch (error) {
202
+ const identities = assertSafeStorePath(storePath);
203
+ let text;
204
+ try {
205
+ text = readRegularFileNoFollow(storePath);
206
+ }
207
+ catch (error) {
208
+ assertDirectoryIdentities(identities);
209
+ if (errorCode(error) === "ENOENT")
210
+ return emptyStore();
211
+ throw new Error(`cannot read Odai semantic memory ${storePath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
212
+ }
291
213
  assertDirectoryIdentities(identities);
292
- if (error?.code === "ENOENT") return emptyStore();
293
- throw new Error(`cannot read Odai semantic memory ${storePath}: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
294
- }
295
- assertDirectoryIdentities(identities);
296
- let parsed;
297
- try {
298
- parsed = JSON.parse(text);
299
- } catch (error) {
300
- throw new MemoryStoreValidationError(`Odai semantic memory ${storePath} is not valid JSON`, { cause: error });
301
- }
302
- try {
303
- return validateStore(parsed, `Odai semantic memory ${storePath}`);
304
- } catch (error) {
305
- if (error instanceof MemoryStoreValidationError) throw error;
306
- throw new MemoryStoreValidationError(
307
- `Odai semantic memory ${storePath} failed validation: ${error instanceof Error ? error.message : String(error)}`,
308
- { cause: error },
309
- );
310
- }
214
+ let parsed;
215
+ try {
216
+ parsed = JSON.parse(text);
217
+ }
218
+ catch (error) {
219
+ throw new MemoryStoreValidationError(`Odai semantic memory ${storePath} is not valid JSON`, { cause: error });
220
+ }
221
+ try {
222
+ return validateStore(parsed, `Odai semantic memory ${storePath}`);
223
+ }
224
+ catch (error) {
225
+ if (error instanceof MemoryStoreValidationError)
226
+ throw error;
227
+ throw new MemoryStoreValidationError(`Odai semantic memory ${storePath} failed validation: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
228
+ }
311
229
  }
312
-
313
230
  function writeMemoryStore(storePath, store) {
314
- const value = validateStore(store, "Odai semantic memory write");
315
- const parent = dirname(storePath);
316
- assertSafeStorePath(storePath);
317
- mkdirSync(parent, { recursive: true, mode: 0o700 });
318
- const identities = assertSafeStorePath(storePath);
319
- const temporary = `${storePath}.tmp-${process.pid}-${randomUUID()}`;
320
- const noFollow = typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
321
- try {
322
- let fd;
231
+ const value = validateStore(store, "Odai semantic memory write");
232
+ const parent = dirname(storePath);
233
+ assertSafeStorePath(storePath);
234
+ mkdirSync(parent, { recursive: true, mode: 0o700 });
235
+ const identities = assertSafeStorePath(storePath);
236
+ const temporary = `${storePath}.tmp-${process.pid}-${randomUUID()}`;
237
+ const noFollow = typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
323
238
  try {
324
- fd = openSync(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | noFollow, 0o600);
325
- writeFileSync(fd, `${JSON.stringify(value, null, 2)}\n`, { encoding: "utf8" });
326
- fsyncSync(fd);
327
- } finally {
328
- if (fd !== undefined) closeSync(fd);
239
+ let fd;
240
+ try {
241
+ fd = openSync(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | noFollow, 0o600);
242
+ writeFileSync(fd, `${JSON.stringify(value, null, 2)}\n`, { encoding: "utf8" });
243
+ fsyncSync(fd);
244
+ }
245
+ finally {
246
+ if (fd !== undefined)
247
+ closeSync(fd);
248
+ }
249
+ assertSafeStorePath(storePath);
250
+ assertDirectoryIdentities(identities);
251
+ renameSync(temporary, storePath);
252
+ assertDirectoryIdentities(identities);
329
253
  }
330
- assertSafeStorePath(storePath);
331
- assertDirectoryIdentities(identities);
332
- renameSync(temporary, storePath);
333
- assertDirectoryIdentities(identities);
334
- } finally {
335
- rmSync(temporary, { force: true });
336
- }
337
- return value;
254
+ finally {
255
+ rmSync(temporary, { force: true });
256
+ }
257
+ return value;
338
258
  }
339
-
340
259
  export function mutateMemoryStore(storePath, mutate) {
341
- if (typeof mutate !== "function") throw new TypeError("memory mutator must be a function");
342
- const identities = assertSafeStorePath(storePath);
343
- const releaseLock = acquireOwnedStoreLock(storePath, "Odai semantic memory");
344
- try {
345
- assertDirectoryIdentities(identities);
346
- const current = readMemoryStore(storePath);
347
- const mutable = {
348
- schemaVersion: MEMORY_STORE_SCHEMA_VERSION,
349
- settings: { ...current.settings },
350
- entries: current.entries.map((entry) => ({
351
- ...entry,
352
- scope: { ...entry.scope },
353
- supersedes: [...entry.supersedes],
354
- conflictsWith: [...entry.conflictsWith],
355
- provenance: entry.provenance.map((source) => ({ ...source })),
356
- })),
357
- };
358
- const outcome = mutate(mutable);
359
- if (!outcome || outcome.changed !== true) return Object.freeze({ store: current, ...(outcome ?? {}) });
360
- const store = writeMemoryStore(storePath, mutable);
361
- return Object.freeze({ ...outcome, changed: true, store });
362
- } finally {
363
- releaseLock();
364
- }
260
+ if (typeof mutate !== "function")
261
+ throw new TypeError("memory mutator must be a function");
262
+ const identities = assertSafeStorePath(storePath);
263
+ const releaseLock = acquireOwnedStoreLock(storePath, "Odai semantic memory");
264
+ try {
265
+ assertDirectoryIdentities(identities);
266
+ const current = readMemoryStore(storePath);
267
+ const mutable = { schemaVersion: MEMORY_STORE_SCHEMA_VERSION, settings: { ...current.settings }, entries: current.entries.map((entry) => ({ ...entry, scope: { ...entry.scope }, supersedes: [...entry.supersedes], conflictsWith: [...entry.conflictsWith], provenance: entry.provenance.map((source) => ({ ...source })) })) };
268
+ const outcome = mutate(mutable);
269
+ if (!outcome || outcome.changed !== true)
270
+ return Object.freeze({ store: current, ...outcome });
271
+ const store = writeMemoryStore(storePath, mutable);
272
+ return Object.freeze({ ...outcome, changed: true, store });
273
+ }
274
+ finally {
275
+ releaseLock();
276
+ }
365
277
  }
366
-
367
278
  export function resetMemoryStore(storePath) {
368
- const identities = assertSafeStorePath(storePath);
369
- const releaseLock = acquireOwnedStoreLock(storePath, "Odai semantic memory");
370
- try {
371
- assertSafeStorePath(storePath);
372
- assertDirectoryIdentities(identities);
373
- rmSync(storePath, { force: true });
374
- assertDirectoryIdentities(identities);
375
- return writeMemoryStore(storePath, {
376
- schemaVersion: MEMORY_STORE_SCHEMA_VERSION,
377
- settings: {},
378
- entries: [],
379
- });
380
- } finally {
381
- releaseLock();
382
- }
279
+ const identities = assertSafeStorePath(storePath);
280
+ const releaseLock = acquireOwnedStoreLock(storePath, "Odai semantic memory");
281
+ try {
282
+ assertSafeStorePath(storePath);
283
+ assertDirectoryIdentities(identities);
284
+ rmSync(storePath, { force: true });
285
+ assertDirectoryIdentities(identities);
286
+ return writeMemoryStore(storePath, { schemaVersion: MEMORY_STORE_SCHEMA_VERSION, settings: {}, entries: [] });
287
+ }
288
+ finally {
289
+ releaseLock();
290
+ }
383
291
  }
384
-
385
292
  export function effectiveMemorySettings(storePath, configured = {}) {
386
- const configuredMode = validateMode(configured.mode ?? DEFAULT_MEMORY_SETTINGS.mode, "config.memory.mode");
387
- const store = readMemoryStore(storePath);
388
- if (configuredMode === "off") return Object.freeze({ mode: "off", source: "deployment-config" });
389
- return Object.freeze({
390
- mode: store.settings.mode ?? configuredMode,
391
- source: store.settings.mode === undefined ? "deployment-default" : "persisted",
392
- });
293
+ const configuredMode = validateMode(configured.mode ?? DEFAULT_MEMORY_SETTINGS.mode, "config.memory.mode");
294
+ const store = readMemoryStore(storePath);
295
+ if (configuredMode === "off")
296
+ return Object.freeze({ mode: "off", source: "deployment-config" });
297
+ return Object.freeze({ mode: store.settings.mode ?? configuredMode, source: store.settings.mode === undefined ? "deployment-default" : "persisted" });
393
298
  }
394
-
395
- export function globalMemoryScope() {
396
- return Object.freeze({ kind: "global", key: "global", label: "global" });
397
- }
398
-
299
+ export function globalMemoryScope() { return Object.freeze({ kind: "global", key: "global", label: "global" }); }
399
300
  export function projectMemoryScope(cwd) {
400
- if (typeof cwd !== "string" || cwd.trim() === "") return undefined;
401
- let canonical;
402
- try {
403
- canonical = realpathSync.native(resolve(cwd.trim()));
404
- if (!lstatSync(canonical).isDirectory()) return undefined;
405
- } catch {
406
- return undefined;
407
- }
408
- return Object.freeze({
409
- kind: "project",
410
- key: hash(canonical),
411
- label: basename(canonical) || "project",
412
- });
301
+ if (typeof cwd !== "string" || cwd.trim() === "")
302
+ return undefined;
303
+ let canonical;
304
+ try {
305
+ canonical = realpathSync.native(resolve(cwd.trim()));
306
+ if (!lstatSync(canonical).isDirectory())
307
+ return undefined;
308
+ }
309
+ catch {
310
+ return undefined;
311
+ }
312
+ return Object.freeze({ kind: "project", key: hash(canonical), label: basename(canonical) || "project" });
413
313
  }
414
-
415
314
  function eventSeqFor(agent, messageId) {
416
- const events = agent?.session?.events;
417
- if (!Array.isArray(events)) return undefined;
418
- for (let index = events.length - 1; index >= 0; index -= 1) {
419
- const event = events[index];
420
- if (event?.type === "user/message" && event.data?.id === messageId && Number.isSafeInteger(event.seq)) return event.seq;
421
- }
422
- return undefined;
423
- }
424
-
425
- export function directUserProvenance({ agent, message, turn, excerpt, extraction, now = Date.now() }) {
426
- const sessionId = agent?.session?.header?.id;
427
- const messageId = message?.id;
428
- if (typeof sessionId !== "string" || sessionId === "" || typeof messageId !== "string" || messageId === "") {
315
+ const events = agent?.session?.events;
316
+ if (!Array.isArray(events))
317
+ return undefined;
318
+ for (let index = events.length - 1; index >= 0; index -= 1) {
319
+ const event = events[index];
320
+ if (event?.type === "user/message" && event.data?.id === messageId && Number.isSafeInteger(event.seq))
321
+ return event.seq;
322
+ }
429
323
  return undefined;
430
- }
431
- if (!Number.isSafeInteger(turn) || turn < 0) return undefined;
432
- const eventSeq = eventSeqFor(agent, messageId);
433
- return Object.freeze({
434
- sourceKind: "direct-user",
435
- sessionHash: hash(sessionId),
436
- messageHash: hash(messageId),
437
- turn,
438
- ...(eventSeq === undefined ? {} : { eventSeq }),
439
- excerptSha256: hash(excerpt),
440
- observedAt: now,
441
- extraction,
442
- extractorVersion: MEMORY_EXTRACTOR_VERSION,
443
- });
444
- }
445
-
446
- export function memoryRecordId(scope, category, subject, value) {
447
- return `mem-${hash(JSON.stringify([scope.kind, scope.key, category, subject, value])).slice(0, 32)}`;
448
324
  }
449
-
450
- export function memoryValueDigest(value) {
451
- return hash(value);
325
+ export function directUserProvenance(options) {
326
+ const { agent, message, turn, excerpt, extraction, now = Date.now() } = options;
327
+ const sessionId = agent?.session?.header?.id;
328
+ const messageId = message?.id;
329
+ if (typeof sessionId !== "string" || sessionId === "" || typeof messageId !== "string" || messageId === "")
330
+ return undefined;
331
+ if (typeof turn !== "number" || !Number.isSafeInteger(turn) || turn < 0)
332
+ return undefined;
333
+ const eventSeq = eventSeqFor(agent, messageId);
334
+ return Object.freeze({ sourceKind: "direct-user", sessionHash: hash(sessionId), messageHash: hash(messageId), turn, ...(eventSeq === undefined ? {} : { eventSeq }), excerptSha256: hash(excerpt), observedAt: now, extraction, extractorVersion: MEMORY_EXTRACTOR_VERSION });
452
335
  }
336
+ export function memoryRecordId(scope, category, subject, value) { return `mem-${hash(JSON.stringify([scope.kind, scope.key, category, subject, value])).slice(0, 32)}`; }
337
+ export function memoryValueDigest(value) { return hash(value); }
338
+ //# sourceMappingURL=semantic-memory-store.mjs.map