@hasna/mementos 0.15.2 → 0.17.0

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 (130) hide show
  1. package/README.md +83 -22
  2. package/dist/audit-contract.d.ts +87 -0
  3. package/dist/audit-contract.d.ts.map +1 -0
  4. package/dist/cli/commands/agent.d.ts.map +1 -1
  5. package/dist/cli/commands/consolidation.d.ts.map +1 -1
  6. package/dist/cli/commands/info-context.d.ts.map +1 -1
  7. package/dist/cli/commands/info-history.d.ts.map +1 -1
  8. package/dist/cli/commands/io-backup.d.ts.map +1 -1
  9. package/dist/cli/commands/io-export.d.ts.map +1 -1
  10. package/dist/cli/commands/io-restore.d.ts.map +1 -1
  11. package/dist/cli/commands/memory-cmd-crud.d.ts.map +1 -1
  12. package/dist/cli/commands/memory-cmd-list.d.ts +25 -0
  13. package/dist/cli/commands/memory-cmd-list.d.ts.map +1 -1
  14. package/dist/cli/commands/memory-cmd-recall.d.ts.map +1 -1
  15. package/dist/cli/commands/memory-cmd-remove.d.ts.map +1 -1
  16. package/dist/cli/commands/memory-cmd-search.d.ts.map +1 -1
  17. package/dist/cli/commands/memory-cmd-view.d.ts.map +1 -1
  18. package/dist/cli/commands/memory-cmd-when-to-use.d.ts.map +1 -1
  19. package/dist/cli/commands/project.d.ts.map +1 -1
  20. package/dist/cli/commands/system-session.d.ts.map +1 -1
  21. package/dist/cli/commands/system-status.d.ts.map +1 -1
  22. package/dist/cli/commands/system-synthesized-profile.d.ts.map +1 -1
  23. package/dist/cli/helpers.d.ts +3 -1
  24. package/dist/cli/helpers.d.ts.map +1 -1
  25. package/dist/cli/index.js +2482 -823
  26. package/dist/cli/structured-json.d.ts +54 -0
  27. package/dist/cli/structured-json.d.ts.map +1 -0
  28. package/dist/db/api-mode.d.ts +10 -0
  29. package/dist/db/api-mode.d.ts.map +1 -1
  30. package/dist/db/api-response-contract.d.ts +23 -0
  31. package/dist/db/api-response-contract.d.ts.map +1 -0
  32. package/dist/db/audit.d.ts +34 -45
  33. package/dist/db/audit.d.ts.map +1 -1
  34. package/dist/db/database.d.ts +1 -0
  35. package/dist/db/database.d.ts.map +1 -1
  36. package/dist/db/locks.d.ts.map +1 -1
  37. package/dist/db/machines.d.ts +50 -13
  38. package/dist/db/machines.d.ts.map +1 -1
  39. package/dist/db/memories.d.ts.map +1 -1
  40. package/dist/db/migrations.d.ts.map +1 -1
  41. package/dist/db/pg-migrate.d.ts.map +1 -1
  42. package/dist/db/pg-migrations.d.ts.map +1 -1
  43. package/dist/db/session-jobs.d.ts +2 -0
  44. package/dist/db/session-jobs.d.ts.map +1 -1
  45. package/dist/diagnostics/historical-project-registration-receipt.js +215 -1
  46. package/dist/index.d.ts +4 -2
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +1523 -206
  49. package/dist/lib/built-in-hooks.d.ts.map +1 -1
  50. package/dist/lib/conversations-transport.d.ts +21 -0
  51. package/dist/lib/conversations-transport.d.ts.map +1 -0
  52. package/dist/lib/export-v1.d.ts +2 -0
  53. package/dist/lib/export-v1.d.ts.map +1 -1
  54. package/dist/lib/injection-project.d.ts +10 -0
  55. package/dist/lib/injection-project.d.ts.map +1 -0
  56. package/dist/lib/injector.d.ts +1 -0
  57. package/dist/lib/injector.d.ts.map +1 -1
  58. package/dist/lib/machine-visibility.d.ts.map +1 -1
  59. package/dist/lib/memory-lock.d.ts.map +1 -1
  60. package/dist/lib/profile-synthesizer.d.ts +13 -2
  61. package/dist/lib/profile-synthesizer.d.ts.map +1 -1
  62. package/dist/lib/session-queue.d.ts.map +1 -1
  63. package/dist/lib/session-registry.d.ts +38 -1
  64. package/dist/lib/session-registry.d.ts.map +1 -1
  65. package/dist/lib/storage-sync.d.ts +5 -0
  66. package/dist/lib/storage-sync.d.ts.map +1 -1
  67. package/dist/lib/synthesis/index.d.ts.map +1 -1
  68. package/dist/mcp/index.d.ts +1 -1
  69. package/dist/mcp/index.d.ts.map +1 -1
  70. package/dist/mcp/index.js +3857 -1600
  71. package/dist/mcp/memory-broadcast.d.ts +6 -8
  72. package/dist/mcp/memory-broadcast.d.ts.map +1 -1
  73. package/dist/mcp/profile.d.ts +23 -0
  74. package/dist/mcp/profile.d.ts.map +1 -0
  75. package/dist/mcp/tools/bounded-output.d.ts +27 -0
  76. package/dist/mcp/tools/bounded-output.d.ts.map +1 -0
  77. package/dist/mcp/tools/lock-tools.d.ts.map +1 -1
  78. package/dist/mcp/tools/memory-audit.d.ts +1 -1
  79. package/dist/mcp/tools/memory-audit.d.ts.map +1 -1
  80. package/dist/mcp/tools/memory-crud.d.ts.map +1 -1
  81. package/dist/mcp/tools/memory-inject.d.ts.map +1 -1
  82. package/dist/mcp/tools/memory-io.d.ts.map +1 -1
  83. package/dist/mcp/tools/project-tools.d.ts.map +1 -1
  84. package/dist/mcp/tools/session-tools.d.ts.map +1 -1
  85. package/dist/mcp/tools/tool-registry.d.ts +22 -19
  86. package/dist/mcp/tools/tool-registry.d.ts.map +1 -1
  87. package/dist/mcp/tools/utility-tools.d.ts +2 -1
  88. package/dist/mcp/tools/utility-tools.d.ts.map +1 -1
  89. package/dist/project-registration.js +276 -20
  90. package/dist/sdk/index.d.ts +110 -7
  91. package/dist/sdk/index.d.ts.map +1 -1
  92. package/dist/sdk/index.js +746 -28
  93. package/dist/server/index.d.ts +2 -0
  94. package/dist/server/index.d.ts.map +1 -1
  95. package/dist/server/index.js +3195 -1134
  96. package/dist/server/openapi.d.ts.map +1 -1
  97. package/dist/server/routes/audit.d.ts +2 -0
  98. package/dist/server/routes/audit.d.ts.map +1 -0
  99. package/dist/server/routes/machines.d.ts +2 -0
  100. package/dist/server/routes/machines.d.ts.map +1 -0
  101. package/dist/server/routes/system-sessions.d.ts.map +1 -1
  102. package/dist/server/routes/system-synthesis.d.ts.map +1 -1
  103. package/dist/types/index.d.ts +3 -0
  104. package/dist/types/index.d.ts.map +1 -1
  105. package/hasna.contract.json +2 -2
  106. package/package.json +8 -5
  107. package/dist/cli/__fixtures__/clean-fallback-stub-server.d.ts +0 -2
  108. package/dist/cli/__fixtures__/clean-fallback-stub-server.d.ts.map +0 -1
  109. package/dist/cli/__fixtures__/io-restore-stub-server.d.ts +0 -2
  110. package/dist/cli/__fixtures__/io-restore-stub-server.d.ts.map +0 -1
  111. package/dist/db/__fixtures__/fail-closed-stub-server.d.ts +0 -2
  112. package/dist/db/__fixtures__/fail-closed-stub-server.d.ts.map +0 -1
  113. package/dist/db/__fixtures__/list-filter-capture-server.d.ts +0 -2
  114. package/dist/db/__fixtures__/list-filter-capture-server.d.ts.map +0 -1
  115. package/dist/db/__fixtures__/list-filter-client-runner.d.ts +0 -2
  116. package/dist/db/__fixtures__/list-filter-client-runner.d.ts.map +0 -1
  117. package/dist/test-support/memories-page-stub-server.d.ts +0 -2
  118. package/dist/test-support/memories-page-stub-server.d.ts.map +0 -1
  119. package/dist/test-support/memories-page-stub.d.ts +0 -28
  120. package/dist/test-support/memories-page-stub.d.ts.map +0 -1
  121. package/dist/test-support/pg-sync-stub-worker.d.ts +0 -2
  122. package/dist/test-support/pg-sync-stub-worker.d.ts.map +0 -1
  123. package/dist/test-support/preload-local-store.d.ts +0 -2
  124. package/dist/test-support/preload-local-store.d.ts.map +0 -1
  125. package/dist/test-support/project-authority-identity.d.ts +0 -8
  126. package/dist/test-support/project-authority-identity.d.ts.map +0 -1
  127. package/dist/test-support/store-isolation.d.ts +0 -147
  128. package/dist/test-support/store-isolation.d.ts.map +0 -1
  129. package/dist/test-support/strip-ansi.d.ts +0 -3
  130. package/dist/test-support/strip-ansi.d.ts.map +0 -1
package/dist/sdk/index.js CHANGED
@@ -1,5 +1,274 @@
1
1
  // @bun
2
- // ../contracts/dist/client/transport.js
2
+ // src/audit-contract.ts
3
+ var AUDIT_TRAIL_CONTRACT = "mementos.audit.trail.v1";
4
+ var AUDIT_EXPORT_CONTRACT = "mementos.audit.export.v1";
5
+ var AUDIT_STATS_CONTRACT = "mementos.audit.stats.v1";
6
+ var AUDIT_DEFAULT_LIMIT = 50;
7
+ var AUDIT_OPERATIONS = [
8
+ "create",
9
+ "update",
10
+ "delete",
11
+ "archive",
12
+ "restore",
13
+ "read"
14
+ ];
15
+
16
+ class AuditContractError extends Error {
17
+ code = "MEMENTOS_AUDIT_CONTRACT";
18
+ constructor(message) {
19
+ super(message);
20
+ this.name = "AuditContractError";
21
+ }
22
+ }
23
+ var ISO_UTC = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
24
+ var MD5_HEX = /^[0-9a-f]{32}$/;
25
+ var ENTRY_KEYS = new Set([
26
+ "id",
27
+ "memory_id",
28
+ "memory_key",
29
+ "operation",
30
+ "agent_id",
31
+ "old_value_hash",
32
+ "new_value_hash",
33
+ "changes",
34
+ "created_at"
35
+ ]);
36
+ var PAGE_KEYS = new Set([
37
+ "contract",
38
+ "entries",
39
+ "count",
40
+ "total",
41
+ "limit",
42
+ "cursor",
43
+ "next_cursor",
44
+ "consumed",
45
+ "has_more",
46
+ "complete",
47
+ "snapshot_at",
48
+ "filters",
49
+ "sort"
50
+ ]);
51
+ function object(value, label) {
52
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
53
+ throw new AuditContractError(`${label} must be a JSON object`);
54
+ }
55
+ return value;
56
+ }
57
+ function exactKeys(value, allowed, label) {
58
+ const unexpected = Object.keys(value).filter((key) => !allowed.has(key));
59
+ if (unexpected.length)
60
+ throw new AuditContractError(`${label} has unexpected field '${unexpected[0]}'`);
61
+ }
62
+ function string(value, label, options = {}) {
63
+ if (options.nullable && value === null)
64
+ return null;
65
+ if (typeof value !== "string" || value.length === 0 || value.length > (options.max ?? 2048) || /[\u0000-\u001f\u007f]/.test(value)) {
66
+ throw new AuditContractError(`${label} must be a non-empty printable string of at most ${options.max ?? 2048} characters`);
67
+ }
68
+ return value;
69
+ }
70
+ function cursorString(value, label) {
71
+ if (value === null)
72
+ return null;
73
+ if (typeof value !== "string" || !value || value.length > 4096 || !/^[A-Za-z0-9_-]+$/.test(value)) {
74
+ throw new AuditContractError(`${label} must be a bounded base64url cursor or null`);
75
+ }
76
+ return value;
77
+ }
78
+ function integer(value, label, minimum = 0) {
79
+ if (!Number.isSafeInteger(value) || value < minimum) {
80
+ throw new AuditContractError(`${label} must be a safe integer >= ${minimum}`);
81
+ }
82
+ return value;
83
+ }
84
+ function boolean(value, label) {
85
+ if (typeof value !== "boolean")
86
+ throw new AuditContractError(`${label} must be a boolean`);
87
+ return value;
88
+ }
89
+ function canonicalAuditTimestamp(value, label) {
90
+ if (value instanceof Date)
91
+ value = value.toISOString();
92
+ if (typeof value !== "string")
93
+ throw new AuditContractError(`${label} must be a canonical UTC timestamp`);
94
+ const candidate = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d{1,3})?$/.test(value) ? `${value.replace(" ", "T")}${value.includes(".") ? "" : ".000"}Z` : value;
95
+ if (!ISO_UTC.test(candidate))
96
+ throw new AuditContractError(`${label} must use YYYY-MM-DDTHH:mm:ss.sssZ`);
97
+ const parsed = new Date(candidate);
98
+ if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== candidate) {
99
+ throw new AuditContractError(`${label} must be a real calendar timestamp`);
100
+ }
101
+ return candidate;
102
+ }
103
+ function auditOperation(value, label = "operation") {
104
+ if (typeof value !== "string" || !AUDIT_OPERATIONS.includes(value)) {
105
+ throw new AuditContractError(`${label} must be one of: ${AUDIT_OPERATIONS.join(", ")}`);
106
+ }
107
+ return value;
108
+ }
109
+ function nullableHash(value, label) {
110
+ if (value === null)
111
+ return null;
112
+ if (typeof value !== "string" || !MD5_HEX.test(value)) {
113
+ throw new AuditContractError(`${label} must be a lowercase md5 hex digest or null`);
114
+ }
115
+ return value;
116
+ }
117
+ function record(value, label) {
118
+ return object(value, label);
119
+ }
120
+ function validateAuditEntry(value, label = "audit entry") {
121
+ const row = object(value, label);
122
+ exactKeys(row, ENTRY_KEYS, label);
123
+ return {
124
+ id: string(row.id, `${label}.id`, { max: 512 }),
125
+ memory_id: string(row.memory_id, `${label}.memory_id`, { max: 512 }),
126
+ memory_key: string(row.memory_key, `${label}.memory_key`, { nullable: true, max: 4096 }),
127
+ operation: auditOperation(row.operation, `${label}.operation`),
128
+ agent_id: string(row.agent_id, `${label}.agent_id`, { nullable: true, max: 512 }),
129
+ old_value_hash: nullableHash(row.old_value_hash, `${label}.old_value_hash`),
130
+ new_value_hash: nullableHash(row.new_value_hash, `${label}.new_value_hash`),
131
+ changes: record(row.changes, `${label}.changes`),
132
+ created_at: canonicalAuditTimestamp(row.created_at, `${label}.created_at`)
133
+ };
134
+ }
135
+ function nullableTimestamp(value, label) {
136
+ return value === null ? null : canonicalAuditTimestamp(value, label);
137
+ }
138
+ function validateFilters(value) {
139
+ const filters = object(value, "audit page filters");
140
+ const expected = new Set(["memory_id", "since", "until", "operation", "agent_id"]);
141
+ exactKeys(filters, expected, "audit page filters");
142
+ const result = {
143
+ memory_id: string(filters.memory_id, "filters.memory_id", { nullable: true, max: 512 }),
144
+ since: nullableTimestamp(filters.since, "filters.since"),
145
+ until: nullableTimestamp(filters.until, "filters.until"),
146
+ operation: filters.operation === null ? null : auditOperation(filters.operation, "filters.operation"),
147
+ agent_id: string(filters.agent_id, "filters.agent_id", { nullable: true, max: 512 })
148
+ };
149
+ if (result.since && result.until && result.since > result.until) {
150
+ throw new AuditContractError("filters.since must not be after filters.until");
151
+ }
152
+ return result;
153
+ }
154
+ function compareEntryOrder(left, right) {
155
+ if (left.created_at !== right.created_at)
156
+ return left.created_at > right.created_at ? -1 : 1;
157
+ return left.id > right.id ? -1 : left.id < right.id ? 1 : 0;
158
+ }
159
+ function validateAuditPage(value, expected) {
160
+ const page = object(value, "audit page");
161
+ exactKeys(page, PAGE_KEYS, "audit page");
162
+ if (page.contract !== expected.contract)
163
+ throw new AuditContractError(`expected contract '${expected.contract}'`);
164
+ const entries = Array.isArray(page.entries) ? page.entries.map((entry, index) => validateAuditEntry(entry, `entries[${index}]`)) : (() => {
165
+ throw new AuditContractError("entries must be an array");
166
+ })();
167
+ const count = integer(page.count, "count");
168
+ const total = integer(page.total, "total");
169
+ const limit = integer(page.limit, "limit", 1);
170
+ const requestedLimit = integer(expected.limit, "requested limit", 1);
171
+ if (limit !== requestedLimit)
172
+ throw new AuditContractError("limit receipt does not match the request");
173
+ const consumed = integer(page.consumed, "consumed");
174
+ const cursor = cursorString(page.cursor, "cursor");
175
+ const nextCursor = cursorString(page.next_cursor, "next_cursor");
176
+ const hasMore = boolean(page.has_more, "has_more");
177
+ const complete = boolean(page.complete, "complete");
178
+ const snapshotAt = canonicalAuditTimestamp(page.snapshot_at, "snapshot_at");
179
+ const filters = validateFilters(page.filters);
180
+ const sort = object(page.sort, "sort");
181
+ exactKeys(sort, new Set(["field", "direction", "tie_breaker"]), "sort");
182
+ if (sort.field !== "created_at" || sort.direction !== "desc" || sort.tie_breaker !== "id") {
183
+ throw new AuditContractError("sort must be created_at desc with id tie-breaker");
184
+ }
185
+ if (cursor !== expected.cursor)
186
+ throw new AuditContractError("cursor receipt does not match the request");
187
+ for (const key of ["memory_id", "since", "until", "operation", "agent_id"]) {
188
+ if (filters[key] !== expected.filters[key]) {
189
+ throw new AuditContractError(`filters.${key} receipt does not match the request`);
190
+ }
191
+ }
192
+ if (count !== entries.length || count > limit || total < count || consumed < count || consumed > total) {
193
+ throw new AuditContractError("count/limit/consumed/total fields are inconsistent");
194
+ }
195
+ if (hasMore !== consumed < total)
196
+ throw new AuditContractError("has_more does not match consumed/total");
197
+ if (hasMore !== (nextCursor !== null))
198
+ throw new AuditContractError("next_cursor does not match has_more");
199
+ if (total > 0 && count === 0)
200
+ throw new AuditContractError("non-empty audit result must make progress on every page");
201
+ if (hasMore && nextCursor === cursor)
202
+ throw new AuditContractError("next_cursor must advance beyond the request cursor");
203
+ const shouldBeComplete = cursor === null && !hasMore && consumed === total;
204
+ if (complete !== shouldBeComplete)
205
+ throw new AuditContractError("complete is not truthful for this page");
206
+ if (cursor === null && consumed !== count)
207
+ throw new AuditContractError("initial page consumed must equal count");
208
+ for (let index = 1;index < entries.length; index++) {
209
+ if (compareEntryOrder(entries[index - 1], entries[index]) >= 0) {
210
+ throw new AuditContractError("entries are not strictly ordered by created_at desc, id desc");
211
+ }
212
+ }
213
+ const ids = new Set;
214
+ for (const entry of entries) {
215
+ if (ids.has(entry.id))
216
+ throw new AuditContractError(`duplicate audit entry id '${entry.id}'`);
217
+ ids.add(entry.id);
218
+ if (filters.memory_id && entry.memory_id !== filters.memory_id)
219
+ throw new AuditContractError("entry violates memory_id filter");
220
+ if (filters.operation && entry.operation !== filters.operation)
221
+ throw new AuditContractError("entry violates operation filter");
222
+ if (filters.agent_id && entry.agent_id !== filters.agent_id)
223
+ throw new AuditContractError("entry violates agent_id filter");
224
+ if (filters.since && entry.created_at < filters.since)
225
+ throw new AuditContractError("entry violates since filter");
226
+ if (filters.until && entry.created_at > filters.until)
227
+ throw new AuditContractError("entry violates until filter");
228
+ if (entry.created_at > snapshotAt)
229
+ throw new AuditContractError("entry is newer than the snapshot boundary");
230
+ }
231
+ return {
232
+ contract: expected.contract,
233
+ entries,
234
+ count,
235
+ total,
236
+ limit,
237
+ cursor,
238
+ next_cursor: nextCursor,
239
+ consumed,
240
+ has_more: hasMore,
241
+ complete,
242
+ snapshot_at: snapshotAt,
243
+ filters,
244
+ sort: { field: "created_at", direction: "desc", tie_breaker: "id" }
245
+ };
246
+ }
247
+ function validateAuditStats(value) {
248
+ const stats = object(value, "audit stats");
249
+ exactKeys(stats, new Set(["contract", "total_entries", "by_operation", "recent_24h", "snapshot_at"]), "audit stats");
250
+ if (stats.contract !== AUDIT_STATS_CONTRACT)
251
+ throw new AuditContractError(`expected contract '${AUDIT_STATS_CONTRACT}'`);
252
+ const total = integer(stats.total_entries, "total_entries");
253
+ const recent = integer(stats.recent_24h, "recent_24h");
254
+ const byOperationObject = object(stats.by_operation, "by_operation");
255
+ exactKeys(byOperationObject, new Set(AUDIT_OPERATIONS), "by_operation");
256
+ const byOperation = Object.fromEntries(AUDIT_OPERATIONS.map((operation) => [operation, integer(byOperationObject[operation], `by_operation.${operation}`)]));
257
+ if (Object.values(byOperation).reduce((sum, count) => sum + count, 0) !== total) {
258
+ throw new AuditContractError("by_operation counts do not sum to total_entries");
259
+ }
260
+ if (recent > total)
261
+ throw new AuditContractError("recent_24h exceeds total_entries");
262
+ return {
263
+ contract: AUDIT_STATS_CONTRACT,
264
+ total_entries: total,
265
+ by_operation: byOperation,
266
+ recent_24h: recent,
267
+ snapshot_at: canonicalAuditTimestamp(stats.snapshot_at, "snapshot_at")
268
+ };
269
+ }
270
+
271
+ // ../../node_modules/.bun/@hasna+contracts@1.0.2+e8014c875821e0be/node_modules/@hasna/contracts/dist/client/transport.js
3
272
  import { isIP } from "net";
4
273
  import { spawnSync } from "child_process";
5
274
  import { closeSync, fstatSync, openSync, readFileSync } from "fs";
@@ -798,6 +1067,11 @@ function mementosResolverInputs(env, credentials = {}) {
798
1067
  }
799
1068
 
800
1069
  // src/sdk/index.ts
1070
+ var MEMENTOS_MACHINE_REGISTRATION_CONTRACT = "mementos.machine-registration.v1";
1071
+ var MEMENTOS_MACHINE_LIST_CONTRACT = "mementos.machines.v1";
1072
+ var MEMENTOS_MACHINE_MUTATION_CONTRACT = "mementos.machine-mutation.v1";
1073
+ var MEMENTOS_MACHINE_TOUCH_CONTRACT = "mementos.machine-touch.v1";
1074
+
801
1075
  class MementosError extends Error {
802
1076
  status;
803
1077
  details;
@@ -816,9 +1090,297 @@ class MementosConfigError extends Error {
816
1090
  this.name = "MementosConfigError";
817
1091
  }
818
1092
  }
1093
+ var SESSION_JOBS_PAGE_CONTRACT = "mementos.sessions.jobs.v2";
1094
+ function sdkProtocolError(operation, detail) {
1095
+ return new MementosError(`mementos ${operation} returned a malformed 2xx response: ${detail}`, 502);
1096
+ }
1097
+ function sessionProtocolError(detail) {
1098
+ return sdkProtocolError("session jobs", detail);
1099
+ }
1100
+ function auditSdkError(operation, error) {
1101
+ if (error instanceof MementosError)
1102
+ throw error;
1103
+ const detail = error instanceof Error ? error.message : String(error);
1104
+ throw sdkProtocolError(operation, detail);
1105
+ }
1106
+ function sdkAuditIdentifier(value, label) {
1107
+ if (value === undefined)
1108
+ return null;
1109
+ if (!value || value.length > 512 || /[\u0000-\u001f\u007f]/.test(value)) {
1110
+ throw new AuditContractError(`${label} must be a non-empty printable string of at most 512 characters`);
1111
+ }
1112
+ return value;
1113
+ }
1114
+ function sdkAuditFilters(input) {
1115
+ const filters = {
1116
+ memory_id: sdkAuditIdentifier(input.memory_id, "memory_id"),
1117
+ since: input.since === undefined ? null : canonicalAuditTimestamp(input.since, "since"),
1118
+ until: input.until === undefined ? null : canonicalAuditTimestamp(input.until, "until"),
1119
+ operation: input.operation === undefined ? null : auditOperation(input.operation),
1120
+ agent_id: sdkAuditIdentifier(input.agent_id, "agent_id")
1121
+ };
1122
+ if (filters.since && filters.until && filters.since > filters.until) {
1123
+ throw new AuditContractError("since must not be after until");
1124
+ }
1125
+ return filters;
1126
+ }
1127
+ function sdkAuditCursor(cursor) {
1128
+ if (cursor === undefined)
1129
+ return;
1130
+ if (!cursor || cursor.length > 4096 || !/^[A-Za-z0-9_-]+$/.test(cursor)) {
1131
+ throw new AuditContractError("cursor must be a bounded base64url audit cursor");
1132
+ }
1133
+ return cursor;
1134
+ }
1135
+ function sdkAuditLimit(limit) {
1136
+ if (limit === undefined)
1137
+ return;
1138
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1000) {
1139
+ throw new AuditContractError("limit must be an integer between 1 and 1000");
1140
+ }
1141
+ return limit;
1142
+ }
1143
+ function sdkObject(value, operation, field = "response") {
1144
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1145
+ throw sdkProtocolError(operation, `expected ${field} to be a JSON object`);
1146
+ }
1147
+ return value;
1148
+ }
1149
+ var SDK_MACHINE_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
1150
+ function sdkMachineHostname(value, operation) {
1151
+ const normalized = value.trim().replace(/\.+$/, "").toLowerCase();
1152
+ if (!normalized || normalized.length > 253 || /[\u0000-\u001f\u007f/\\\s]/.test(normalized) || normalized !== value) {
1153
+ throw sdkProtocolError(operation, "machine.hostname is not canonical");
1154
+ }
1155
+ return normalized;
1156
+ }
1157
+ function sdkMachinePlatform(value, operation) {
1158
+ const normalized = value.trim().toLowerCase();
1159
+ if (!normalized || normalized.length > 64 || !/^[a-z0-9._-]+$/.test(normalized) || normalized !== value) {
1160
+ throw sdkProtocolError(operation, "machine.platform is not canonical");
1161
+ }
1162
+ return normalized;
1163
+ }
1164
+ function sdkMachineName(value, operation) {
1165
+ if (typeof value !== "string" || !value || value.length > 128 || /[\u0000-\u001f\u007f]/.test(value)) {
1166
+ throw sdkProtocolError(operation, "machine.name is outside the public contract");
1167
+ }
1168
+ return value;
1169
+ }
1170
+ function sdkMachineTimestamp(value, operation, field) {
1171
+ if (typeof value !== "string" || !SDK_MACHINE_TIMESTAMP.test(value)) {
1172
+ throw sdkProtocolError(operation, `machine.${field} is not a canonical UTC timestamp`);
1173
+ }
1174
+ const parsed = new Date(value);
1175
+ if (!Number.isFinite(parsed.getTime()) || parsed.toISOString() !== value) {
1176
+ throw sdkProtocolError(operation, `machine.${field} is not a real calendar timestamp`);
1177
+ }
1178
+ return value;
1179
+ }
1180
+ function sdkMachine(value, operation) {
1181
+ const machine = sdkObject(value, operation, "machine");
1182
+ if (typeof machine["id"] !== "string" || !machine["id"]) {
1183
+ throw sdkProtocolError(operation, "expected machine.id to be a non-empty string");
1184
+ }
1185
+ if (typeof machine["hostname"] !== "string" || typeof machine["platform"] !== "string") {
1186
+ throw sdkProtocolError(operation, "expected machine hostname/platform strings");
1187
+ }
1188
+ if (typeof machine["is_primary"] !== "boolean") {
1189
+ throw sdkProtocolError(operation, "expected machine.is_primary to be a boolean");
1190
+ }
1191
+ const createdAt = sdkMachineTimestamp(machine["created_at"], operation, "created_at");
1192
+ const lastSeenAt = sdkMachineTimestamp(machine["last_seen_at"], operation, "last_seen_at");
1193
+ if (lastSeenAt < createdAt)
1194
+ throw sdkProtocolError(operation, "machine.last_seen_at precedes created_at");
1195
+ return {
1196
+ id: machine["id"],
1197
+ name: sdkMachineName(machine["name"], operation),
1198
+ hostname: sdkMachineHostname(machine["hostname"], operation),
1199
+ platform: sdkMachinePlatform(machine["platform"], operation),
1200
+ is_primary: machine["is_primary"],
1201
+ created_at: createdAt,
1202
+ last_seen_at: lastSeenAt
1203
+ };
1204
+ }
1205
+ function sdkMachineMutation(value, operation, expectedId) {
1206
+ const response = sdkObject(value, operation);
1207
+ if (response["contract"] !== MEMENTOS_MACHINE_MUTATION_CONTRACT) {
1208
+ throw sdkProtocolError(operation, `expected contract '${MEMENTOS_MACHINE_MUTATION_CONTRACT}'`);
1209
+ }
1210
+ const machine = sdkMachine(response["machine"], operation);
1211
+ if (machine.id !== expectedId) {
1212
+ throw sdkProtocolError(operation, "returned machine id does not match the requested stable id");
1213
+ }
1214
+ return machine;
1215
+ }
1216
+ function sessionObject(value, field = "response") {
1217
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1218
+ throw sessionProtocolError(`expected ${field} to be a JSON object`);
1219
+ }
1220
+ return value;
1221
+ }
1222
+ function sessionString(object2, field, nullable = false) {
1223
+ const value = object2[field];
1224
+ if (nullable && value === null)
1225
+ return null;
1226
+ if (typeof value !== "string" || !nullable && value.length === 0) {
1227
+ throw sessionProtocolError(`expected '${field}' to be ${nullable ? "a string or null" : "a non-empty string"}`);
1228
+ }
1229
+ return value;
1230
+ }
1231
+ function sessionCount(object2, field) {
1232
+ const value = object2[field];
1233
+ if (!Number.isSafeInteger(value) || value < 0) {
1234
+ throw sessionProtocolError(`expected '${field}' to be a non-negative safe integer`);
1235
+ }
1236
+ return value;
1237
+ }
1238
+ function decodeSessionJob(value, index) {
1239
+ const job = sessionObject(value, `jobs[${index}]`);
1240
+ const source = sessionString(job, "source");
1241
+ if (!["claude-code", "codex", "manual", "open-sessions"].includes(source)) {
1242
+ throw sessionProtocolError(`jobs[${index}] has unsupported 'source'`);
1243
+ }
1244
+ const status = sessionString(job, "status");
1245
+ if (!["pending", "processing", "completed", "failed"].includes(status)) {
1246
+ throw sessionProtocolError(`jobs[${index}] has unsupported 'status'`);
1247
+ }
1248
+ const metadata = sessionObject(job["metadata"], `jobs[${index}].metadata`);
1249
+ return {
1250
+ id: sessionString(job, "id"),
1251
+ session_id: sessionString(job, "session_id"),
1252
+ agent_id: sessionString(job, "agent_id", true),
1253
+ project_id: sessionString(job, "project_id", true),
1254
+ source,
1255
+ status,
1256
+ transcript: typeof job["transcript"] === "string" ? job["transcript"] : (() => {
1257
+ throw sessionProtocolError(`jobs[${index}] expected 'transcript' to be a string`);
1258
+ })(),
1259
+ chunk_count: sessionCount(job, "chunk_count"),
1260
+ memories_extracted: sessionCount(job, "memories_extracted"),
1261
+ error: sessionString(job, "error", true),
1262
+ metadata,
1263
+ created_at: sessionString(job, "created_at"),
1264
+ started_at: sessionString(job, "started_at", true),
1265
+ completed_at: sessionString(job, "completed_at", true)
1266
+ };
1267
+ }
1268
+ function decodeSessionJobsPage(value) {
1269
+ const page = sessionObject(value);
1270
+ if (page["contract"] !== SESSION_JOBS_PAGE_CONTRACT) {
1271
+ throw sessionProtocolError(`expected contract '${SESSION_JOBS_PAGE_CONTRACT}'`);
1272
+ }
1273
+ if (!Array.isArray(page["jobs"]))
1274
+ throw sessionProtocolError("expected 'jobs' to be an array");
1275
+ const jobs = page["jobs"].map(decodeSessionJob);
1276
+ const count = sessionCount(page, "count");
1277
+ const limit = sessionCount(page, "limit");
1278
+ const offset = sessionCount(page, "offset");
1279
+ if (limit < 1)
1280
+ throw sessionProtocolError("expected 'limit' to be positive");
1281
+ if (count !== jobs.length || count > limit) {
1282
+ throw sessionProtocolError("page count is inconsistent with jobs/limit");
1283
+ }
1284
+ if (typeof page["has_more"] !== "boolean")
1285
+ throw sessionProtocolError("expected 'has_more' to be a boolean");
1286
+ const nextOffset = page["next_offset"];
1287
+ if (nextOffset !== null && (!Number.isSafeInteger(nextOffset) || nextOffset < 0)) {
1288
+ throw sessionProtocolError("expected 'next_offset' to be a non-negative safe integer or null");
1289
+ }
1290
+ if (page["has_more"] === true && nextOffset !== offset + jobs.length) {
1291
+ throw sessionProtocolError("'has_more' requires the exact next offset");
1292
+ }
1293
+ if (page["has_more"] === false && nextOffset !== null) {
1294
+ throw sessionProtocolError("terminal page must set 'next_offset' to null");
1295
+ }
1296
+ return {
1297
+ contract: SESSION_JOBS_PAGE_CONTRACT,
1298
+ jobs,
1299
+ count,
1300
+ limit,
1301
+ offset,
1302
+ has_more: page["has_more"],
1303
+ next_offset: nextOffset
1304
+ };
1305
+ }
1306
+ function decodeSessionIngestReceipt(value, transcript, expectedSessionId) {
1307
+ const receipt = sessionObject(value);
1308
+ if (receipt["contract"] !== "mementos.sessions.ingest.v2") {
1309
+ throw sdkProtocolError("session ingest", "expected contract 'mementos.sessions.ingest.v2'");
1310
+ }
1311
+ const jobId = sessionString(receipt, "job_id");
1312
+ if (receipt["status"] !== "queued")
1313
+ throw sdkProtocolError("session ingest", "expected status 'queued'");
1314
+ const message = sessionString(receipt, "message");
1315
+ const rawJob = sessionObject(receipt["job"], "job");
1316
+ const job = decodeSessionJob({ ...rawJob, transcript }, 0);
1317
+ if (job.id !== jobId || job.session_id !== expectedSessionId) {
1318
+ throw sdkProtocolError("session ingest", "job receipt identity does not match the request");
1319
+ }
1320
+ return {
1321
+ contract: "mementos.sessions.ingest.v2",
1322
+ job_id: jobId,
1323
+ status: "queued",
1324
+ message,
1325
+ job
1326
+ };
1327
+ }
1328
+ function decodeQueueStats(value) {
1329
+ const stats = sessionObject(value);
1330
+ return {
1331
+ pending: sessionCount(stats, "pending"),
1332
+ processing: sessionCount(stats, "processing"),
1333
+ completed: sessionCount(stats, "completed"),
1334
+ failed: sessionCount(stats, "failed")
1335
+ };
1336
+ }
1337
+ var SDK_RESOURCE_TYPES = new Set(["project", "memory", "entity", "agent", "connector", "file"]);
1338
+ var SDK_LOCK_TYPES = new Set(["advisory", "exclusive"]);
1339
+ function decodeResourceLock(value, operation) {
1340
+ const lock = sessionObject(value, "lock");
1341
+ const resourceType = sessionString(lock, "resource_type");
1342
+ const lockType = sessionString(lock, "lock_type");
1343
+ if (!SDK_RESOURCE_TYPES.has(resourceType)) {
1344
+ throw sdkProtocolError(operation, "unsupported 'resource_type'");
1345
+ }
1346
+ if (!SDK_LOCK_TYPES.has(lockType)) {
1347
+ throw sdkProtocolError(operation, "unsupported 'lock_type'");
1348
+ }
1349
+ return {
1350
+ id: sessionString(lock, "id"),
1351
+ resource_type: resourceType,
1352
+ resource_id: sessionString(lock, "resource_id"),
1353
+ agent_id: sessionString(lock, "agent_id"),
1354
+ lock_type: lockType,
1355
+ locked_at: sessionString(lock, "locked_at"),
1356
+ expires_at: sessionString(lock, "expires_at")
1357
+ };
1358
+ }
1359
+ function decodeResourceLocks(value, operation) {
1360
+ if (!Array.isArray(value))
1361
+ throw sdkProtocolError(operation, "expected a JSON array");
1362
+ return value.map((lock) => decodeResourceLock(lock, operation));
1363
+ }
1364
+ function decodeBooleanReceipt(value, field, operation) {
1365
+ const receipt = sessionObject(value);
1366
+ if (typeof receipt[field] !== "boolean")
1367
+ throw sdkProtocolError(operation, `expected '${field}' to be a boolean`);
1368
+ return { [field]: receipt[field] };
1369
+ }
1370
+ function decodeCountReceipt(value, field, operation) {
1371
+ const receipt = sessionObject(value);
1372
+ const count = receipt[field];
1373
+ if (!Number.isSafeInteger(count) || count < 0) {
1374
+ throw sdkProtocolError(operation, `expected '${field}' to be a non-negative safe integer`);
1375
+ }
1376
+ return { [field]: count };
1377
+ }
819
1378
  var MEMENTOS_DEFAULT_BASE_URL = "http://localhost:19428";
820
1379
  function resolveMementosApiBase(rawBaseUrl, explicitPrefix) {
821
- const trimmed = (rawBaseUrl ?? MEMENTOS_DEFAULT_BASE_URL).trim().replace(/\/+$/, "") || MEMENTOS_DEFAULT_BASE_URL;
1380
+ if (rawBaseUrl === undefined || rawBaseUrl.trim() === "") {
1381
+ throw new Error("mementos base URL is required for this explicit-base helper; ordinary SDK clients must resolve the hosted authority through the credential chain or opt into local mode");
1382
+ }
1383
+ const trimmed = rawBaseUrl.trim().replace(/\/+$/, "");
822
1384
  let url;
823
1385
  try {
824
1386
  url = new URL(trimmed);
@@ -927,10 +1489,10 @@ class MementosClient {
927
1489
  _resolveOptions;
928
1490
  _pinnedAuthority = null;
929
1491
  constructor(config = {}) {
930
- const resolved = resolveMementosApiBase(config.baseUrl, config.prefix);
1492
+ const prefix = config.baseUrl !== undefined ? resolveMementosApiBase(config.baseUrl, config.prefix).prefix : config.prefix?.replace(/\/+$/, "") || "/v1";
931
1493
  this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);
932
1494
  this.apiKey = config.apiKey;
933
- this.prefix = resolved.prefix;
1495
+ this.prefix = prefix;
934
1496
  this._resolveOptions = {
935
1497
  ...config.baseUrl !== undefined ? { baseUrl: config.baseUrl } : {},
936
1498
  ...config.apiKey !== undefined ? { apiKey: config.apiKey } : {},
@@ -1044,6 +1606,8 @@ class MementosClient {
1044
1606
  q["agent_id"] = filter.agent_id;
1045
1607
  if (filter.project_id)
1046
1608
  q["project_id"] = filter.project_id;
1609
+ if (filter.project_id && filter.include_unassigned_project)
1610
+ q["include_unassigned_project"] = true;
1047
1611
  if (filter.session_id)
1048
1612
  q["session_id"] = filter.session_id;
1049
1613
  if (filter.namespace)
@@ -1202,6 +1766,126 @@ class MementosClient {
1202
1766
  listAgentsByProject(projectId) {
1203
1767
  return this.get(`/api/agents`, { project_id: projectId });
1204
1768
  }
1769
+ async getMemoryAuditTrail(memoryId, options = {}) {
1770
+ const operation = "GET /v1/memories/:id/audit-trail";
1771
+ try {
1772
+ const filters = sdkAuditFilters({ memory_id: memoryId });
1773
+ const requestedLimit = sdkAuditLimit(options.limit);
1774
+ const limit = requestedLimit ?? AUDIT_DEFAULT_LIMIT;
1775
+ const requestedCursor = sdkAuditCursor(options.cursor);
1776
+ const cursor = requestedCursor ?? null;
1777
+ const response = await this.get(`/api/memories/${encodeURIComponent(filters.memory_id)}/audit-trail`, { limit: requestedLimit, cursor: requestedCursor });
1778
+ return validateAuditPage(response, { contract: AUDIT_TRAIL_CONTRACT, cursor, filters, limit });
1779
+ } catch (error) {
1780
+ return auditSdkError(operation, error);
1781
+ }
1782
+ }
1783
+ async exportAuditLog(options = {}) {
1784
+ const operation = "GET /v1/audit/export";
1785
+ try {
1786
+ const filters = sdkAuditFilters(options);
1787
+ const requestedLimit = sdkAuditLimit(options.limit);
1788
+ const limit = requestedLimit ?? AUDIT_DEFAULT_LIMIT;
1789
+ const requestedCursor = sdkAuditCursor(options.cursor);
1790
+ const cursor = requestedCursor ?? null;
1791
+ const response = await this.get("/api/audit/export", {
1792
+ since: filters.since ?? undefined,
1793
+ until: filters.until ?? undefined,
1794
+ operation: filters.operation ?? undefined,
1795
+ agent_id: filters.agent_id ?? undefined,
1796
+ limit: requestedLimit,
1797
+ cursor: requestedCursor
1798
+ });
1799
+ return validateAuditPage(response, { contract: AUDIT_EXPORT_CONTRACT, cursor, filters, limit });
1800
+ } catch (error) {
1801
+ return auditSdkError(operation, error);
1802
+ }
1803
+ }
1804
+ async getAuditStats() {
1805
+ const operation = "GET /v1/audit/stats";
1806
+ try {
1807
+ return validateAuditStats(await this.get("/api/audit/stats"));
1808
+ } catch (error) {
1809
+ return auditSdkError(operation, error);
1810
+ }
1811
+ }
1812
+ async listMachines() {
1813
+ const operation = "GET /v1/machines";
1814
+ const response = sdkObject(await this.get("/api/machines"), operation);
1815
+ if (response["contract"] !== MEMENTOS_MACHINE_LIST_CONTRACT || response["complete"] !== true || !Array.isArray(response["machines"])) {
1816
+ throw sdkProtocolError(operation, `expected contract '${MEMENTOS_MACHINE_LIST_CONTRACT}', complete=true, and a machines array`);
1817
+ }
1818
+ const machines = response["machines"].map((entry, index) => sdkMachine(entry, `${operation} machines[${index}]`));
1819
+ if (!Number.isSafeInteger(response["count"]) || response["count"] !== machines.length) {
1820
+ throw sdkProtocolError(operation, "count does not match machines.length");
1821
+ }
1822
+ if (new Set(machines.map((machine) => machine.id)).size !== machines.length || new Set(machines.map((machine) => machine.hostname)).size !== machines.length || new Set(machines.map((machine) => machine.name)).size !== machines.length) {
1823
+ throw sdkProtocolError(operation, "machine ids, hostnames, or names are duplicated");
1824
+ }
1825
+ if (machines.filter((machine) => machine.is_primary).length > 1) {
1826
+ throw sdkProtocolError(operation, "more than one machine is primary");
1827
+ }
1828
+ return { machines, count: machines.length, complete: true };
1829
+ }
1830
+ async registerMachine(input) {
1831
+ const operation = "POST /v1/machines";
1832
+ const response = sdkObject(await this.post("/api/machines", input), operation);
1833
+ if (response["contract"] !== MEMENTOS_MACHINE_REGISTRATION_CONTRACT || typeof response["created"] !== "boolean") {
1834
+ throw sdkProtocolError(operation, `expected contract '${MEMENTOS_MACHINE_REGISTRATION_CONTRACT}' and a boolean created receipt`);
1835
+ }
1836
+ const machine = sdkMachine(response["machine"], operation);
1837
+ const identity = sdkObject(response["identity"], operation, "identity");
1838
+ if (identity["idempotency_key"] !== "normalized_hostname" || identity["stable_id"] !== machine.id) {
1839
+ throw sdkProtocolError(operation, "identity receipt does not match the stable machine id");
1840
+ }
1841
+ const expectedHostname = sdkMachineHostname(input.hostname.trim().replace(/\.+$/, "").toLowerCase(), operation);
1842
+ const expectedPlatform = sdkMachinePlatform(input.platform.trim().toLowerCase(), operation);
1843
+ if (machine.hostname !== expectedHostname || machine.platform !== expectedPlatform) {
1844
+ throw sdkProtocolError(operation, "registration receipt does not match the requested hostname/platform");
1845
+ }
1846
+ if (response["created"] === true) {
1847
+ const expectedName = input.name?.trim() || expectedHostname;
1848
+ if (machine.name !== expectedName)
1849
+ throw sdkProtocolError(operation, "created registration receipt does not match the requested name");
1850
+ }
1851
+ return machine;
1852
+ }
1853
+ async getMachine(id) {
1854
+ return sdkMachineMutation(await this.get(`/api/machines/${encodeURIComponent(id)}`), "GET /v1/machines/:id", id);
1855
+ }
1856
+ async renameMachine(id, name) {
1857
+ const normalizedName = name.trim();
1858
+ const machine = sdkMachineMutation(await this.patch(`/api/machines/${encodeURIComponent(id)}`, { name: normalizedName }), "PATCH /v1/machines/:id", id);
1859
+ if (machine.name !== normalizedName)
1860
+ throw sdkProtocolError("PATCH /v1/machines/:id", "returned name does not match the requested rename");
1861
+ return machine;
1862
+ }
1863
+ async setPrimaryMachine(id) {
1864
+ const machine = sdkMachineMutation(await this.post(`/api/machines/${encodeURIComponent(id)}/primary`), "POST /v1/machines/:id/primary", id);
1865
+ if (!machine.is_primary)
1866
+ throw sdkProtocolError("POST /v1/machines/:id/primary", "returned machine is not primary");
1867
+ return machine;
1868
+ }
1869
+ async touchMachine(id) {
1870
+ const operation = "POST /v1/machines/:id/touch";
1871
+ const response = sdkObject(await this.post(`/api/machines/${encodeURIComponent(id)}/touch`), operation);
1872
+ if (response["contract"] !== MEMENTOS_MACHINE_TOUCH_CONTRACT || response["touched"] !== true || response["id"] !== id) {
1873
+ throw sdkProtocolError(operation, "expected touched=true for the requested stable id");
1874
+ }
1875
+ const machine = sdkMachine(response["machine"], operation);
1876
+ if (machine.id !== id || response["touched_at"] !== machine.last_seen_at) {
1877
+ throw sdkProtocolError(operation, "touch receipt does not match the returned machine");
1878
+ }
1879
+ return machine;
1880
+ }
1881
+ async deleteMachine(id) {
1882
+ const operation = "DELETE /v1/machines/:id";
1883
+ const response = sdkObject(await this.delete(`/api/machines/${encodeURIComponent(id)}`), operation);
1884
+ if (response["contract"] !== MEMENTOS_MACHINE_MUTATION_CONTRACT || response["deleted"] !== true || response["id"] !== id) {
1885
+ throw sdkProtocolError(operation, "expected deleted=true for the requested stable id");
1886
+ }
1887
+ return { deleted: true, id };
1888
+ }
1205
1889
  listProjects() {
1206
1890
  return this.get("/api/projects");
1207
1891
  }
@@ -1422,30 +2106,30 @@ class MementosClient {
1422
2106
  }
1423
2107
  async acquireLock(input) {
1424
2108
  try {
1425
- return await this.post("/api/locks", input);
1426
- } catch (e) {
1427
- if (e instanceof Error && e.message.includes("409"))
2109
+ return decodeResourceLock(await this.post("/api/locks", input), "lock acquire");
2110
+ } catch (error) {
2111
+ if (error instanceof MementosError && error.status === 409)
1428
2112
  return null;
1429
- throw e;
2113
+ throw error;
1430
2114
  }
1431
2115
  }
1432
- checkLock(resourceType, resourceId, lockType) {
2116
+ async checkLock(resourceType, resourceId, lockType) {
1433
2117
  const params = { resource_type: resourceType, resource_id: resourceId };
1434
2118
  if (lockType)
1435
2119
  params["lock_type"] = lockType;
1436
- return this.get("/api/locks", params);
2120
+ return decodeResourceLocks(await this.get("/api/locks", params), "lock list");
1437
2121
  }
1438
- releaseLock(lockId, agentId) {
1439
- return this.request("DELETE", `/api/locks/${lockId}`, { agent_id: agentId });
2122
+ async releaseLock(lockId, agentId) {
2123
+ return decodeBooleanReceipt(await this.request("DELETE", `/api/locks/${encodeURIComponent(lockId)}`, { agent_id: agentId }), "released", "lock release");
1440
2124
  }
1441
- listAgentLocks(agentId) {
1442
- return this.get(`/api/agents/${agentId}/locks`);
2125
+ async listAgentLocks(agentId) {
2126
+ return decodeResourceLocks(await this.get(`/api/agents/${encodeURIComponent(agentId)}/locks`), "agent lock list");
1443
2127
  }
1444
- releaseAllAgentLocks(agentId) {
1445
- return this.request("DELETE", `/api/agents/${agentId}/locks`);
2128
+ async releaseAllAgentLocks(agentId) {
2129
+ return decodeCountReceipt(await this.request("DELETE", `/api/agents/${encodeURIComponent(agentId)}/locks`), "released", "agent lock release");
1446
2130
  }
1447
- cleanExpiredLocks() {
1448
- return this.post("/api/locks/clean", {});
2131
+ async cleanExpiredLocks() {
2132
+ return decodeCountReceipt(await this.post("/api/locks/clean", {}), "cleaned", "lock cleanup");
1449
2133
  }
1450
2134
  createTask(input) {
1451
2135
  return this.post("/api/tasks", input);
@@ -1567,27 +2251,53 @@ class MementosClient {
1567
2251
  rollbackSynthesis(runId) {
1568
2252
  return this.post(`/api/synthesis/rollback/${runId}`, {});
1569
2253
  }
1570
- ingestSession(input) {
1571
- return this.post("/api/sessions/ingest", input);
2254
+ async ingestSession(input) {
2255
+ return decodeSessionIngestReceipt(await this.post("/api/sessions/ingest", input), input.transcript, input.session_id);
1572
2256
  }
1573
- getSessionJob(jobId) {
1574
- return this.get(`/api/sessions/jobs/${jobId}`);
2257
+ async getSessionJob(jobId) {
2258
+ return decodeSessionJob(await this.get(`/api/sessions/jobs/${encodeURIComponent(jobId)}`), 0);
1575
2259
  }
1576
- listSessionJobs(filter) {
2260
+ async listSessionJobs(filter) {
1577
2261
  const params = new URLSearchParams;
1578
2262
  if (filter?.agent_id)
1579
2263
  params.set("agent_id", filter.agent_id);
1580
2264
  if (filter?.project_id)
1581
2265
  params.set("project_id", filter.project_id);
2266
+ if (filter?.session_id)
2267
+ params.set("session_id", filter.session_id);
1582
2268
  if (filter?.status)
1583
2269
  params.set("status", filter.status);
1584
- if (filter?.limit)
2270
+ if (filter?.limit !== undefined)
1585
2271
  params.set("limit", String(filter.limit));
2272
+ if (filter?.offset !== undefined)
2273
+ params.set("offset", String(filter.offset));
1586
2274
  const qs = params.toString() ? `?${params.toString()}` : "";
1587
- return this.get(`/api/sessions/jobs${qs}`);
2275
+ const response = await this.get(`/api/sessions/jobs${qs}`);
2276
+ const page = decodeSessionJobsPage(response);
2277
+ if (filter?.limit !== undefined && page.limit !== filter.limit) {
2278
+ throw sessionProtocolError("server did not preserve requested 'limit'");
2279
+ }
2280
+ if (filter?.offset !== undefined && page.offset !== filter.offset) {
2281
+ throw sessionProtocolError("server did not preserve requested 'offset'");
2282
+ }
2283
+ for (const job of page.jobs) {
2284
+ if (filter?.agent_id !== undefined && job.agent_id !== filter.agent_id) {
2285
+ throw sessionProtocolError("server did not preserve requested 'agent_id'");
2286
+ }
2287
+ if (filter?.project_id !== undefined && job.project_id !== filter.project_id) {
2288
+ throw sessionProtocolError("server did not preserve requested 'project_id'");
2289
+ }
2290
+ if (filter?.session_id !== undefined && job.session_id !== filter.session_id) {
2291
+ throw sessionProtocolError("server did not preserve requested 'session_id'");
2292
+ }
2293
+ if (filter?.status !== undefined && job.status !== filter.status) {
2294
+ throw sessionProtocolError("server did not preserve requested 'status'");
2295
+ }
2296
+ }
2297
+ return page;
1588
2298
  }
1589
- getSessionQueueStats() {
1590
- return this.get("/api/sessions/queue/stats");
2299
+ async getSessionQueueStats() {
2300
+ return decodeQueueStats(await this.get("/api/sessions/queue/stats"));
1591
2301
  }
1592
2302
  }
1593
2303
  var sdk_default = MementosClient;
@@ -1596,8 +2306,16 @@ export {
1596
2306
  resolveMementosApiBase,
1597
2307
  sdk_default as default,
1598
2308
  __resetMementosSdkLocalNotice,
2309
+ SESSION_JOBS_PAGE_CONTRACT,
1599
2310
  MementosError,
1600
2311
  MementosConfigError,
1601
2312
  MementosClient,
1602
- MEMENTOS_DEFAULT_BASE_URL
2313
+ MEMENTOS_MACHINE_TOUCH_CONTRACT,
2314
+ MEMENTOS_MACHINE_REGISTRATION_CONTRACT,
2315
+ MEMENTOS_MACHINE_MUTATION_CONTRACT,
2316
+ MEMENTOS_MACHINE_LIST_CONTRACT,
2317
+ MEMENTOS_DEFAULT_BASE_URL,
2318
+ AUDIT_TRAIL_CONTRACT as MEMENTOS_AUDIT_TRAIL_CONTRACT,
2319
+ AUDIT_STATS_CONTRACT as MEMENTOS_AUDIT_STATS_CONTRACT,
2320
+ AUDIT_EXPORT_CONTRACT as MEMENTOS_AUDIT_EXPORT_CONTRACT
1603
2321
  };