codeksei 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/LICENSE +661 -661
  2. package/README.en.md +109 -47
  3. package/README.md +79 -58
  4. package/bin/cyberboss.js +1 -1
  5. package/package.json +86 -86
  6. package/scripts/open_shared_wechat_thread.sh +77 -77
  7. package/scripts/open_wechat_thread.sh +108 -108
  8. package/scripts/shared-common.js +144 -144
  9. package/scripts/shared-open.js +14 -14
  10. package/scripts/shared-start.js +5 -5
  11. package/scripts/shared-status.js +27 -27
  12. package/scripts/show_shared_status.sh +45 -45
  13. package/scripts/start_shared_app_server.sh +52 -52
  14. package/scripts/start_shared_wechat.sh +94 -94
  15. package/scripts/timeline-screenshot.sh +14 -14
  16. package/src/adapters/channel/weixin/account-store.js +99 -99
  17. package/src/adapters/channel/weixin/api-v2.js +50 -50
  18. package/src/adapters/channel/weixin/api.js +169 -169
  19. package/src/adapters/channel/weixin/context-token-store.js +84 -84
  20. package/src/adapters/channel/weixin/index.js +618 -604
  21. package/src/adapters/channel/weixin/legacy.js +579 -566
  22. package/src/adapters/channel/weixin/media-mime.js +22 -22
  23. package/src/adapters/channel/weixin/media-receive.js +370 -370
  24. package/src/adapters/channel/weixin/media-send.js +102 -102
  25. package/src/adapters/channel/weixin/message-utils-v2.js +282 -282
  26. package/src/adapters/channel/weixin/message-utils.js +199 -199
  27. package/src/adapters/channel/weixin/redact.js +41 -41
  28. package/src/adapters/channel/weixin/reminder-queue-store.js +101 -101
  29. package/src/adapters/channel/weixin/sync-buffer-store.js +35 -35
  30. package/src/adapters/runtime/codex/events.js +215 -215
  31. package/src/adapters/runtime/codex/index.js +109 -104
  32. package/src/adapters/runtime/codex/message-utils.js +95 -95
  33. package/src/adapters/runtime/codex/model-catalog.js +106 -106
  34. package/src/adapters/runtime/codex/protocol-leak-monitor.js +75 -75
  35. package/src/adapters/runtime/codex/rpc-client.js +339 -339
  36. package/src/adapters/runtime/codex/session-store.js +286 -286
  37. package/src/app/channel-send-file-cli.js +57 -57
  38. package/src/app/diary-write-cli.js +236 -88
  39. package/src/app/note-sync-cli.js +2 -2
  40. package/src/app/reminder-write-cli.js +215 -210
  41. package/src/app/review-cli.js +7 -5
  42. package/src/app/system-checkin-poller.js +64 -64
  43. package/src/app/system-send-cli.js +129 -129
  44. package/src/app/timeline-event-cli.js +28 -25
  45. package/src/app/timeline-screenshot-cli.js +103 -100
  46. package/src/core/app.js +1763 -1763
  47. package/src/core/branding.js +2 -1
  48. package/src/core/command-registry.js +381 -369
  49. package/src/core/config.js +30 -14
  50. package/src/core/default-targets.js +163 -163
  51. package/src/core/durable-note-schema.js +9 -8
  52. package/src/core/instructions-template.js +17 -16
  53. package/src/core/note-sync.js +8 -7
  54. package/src/core/path-utils.js +54 -0
  55. package/src/core/project-radar.js +11 -10
  56. package/src/core/review.js +48 -50
  57. package/src/core/stream-delivery.js +1162 -983
  58. package/src/core/system-message-dispatcher.js +68 -68
  59. package/src/core/system-message-queue-store.js +128 -128
  60. package/src/core/thread-state-store.js +96 -96
  61. package/src/core/timeline-screenshot-queue-store.js +134 -134
  62. package/src/core/timezone.js +436 -0
  63. package/src/core/workspace-bootstrap.js +9 -1
  64. package/src/index.js +148 -146
  65. package/src/integrations/timeline/index.js +130 -74
  66. package/src/integrations/timeline/state-sync.js +240 -0
  67. package/templates/weixin-instructions.md +12 -38
  68. package/templates/weixin-operations.md +29 -31
@@ -1,77 +1,77 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const { normalizeModelCatalog } = require("./model-catalog");
4
-
5
- class SessionStore {
6
- constructor({ filePath }) {
7
- this.filePath = filePath;
8
- this.state = createEmptyState();
9
- this.ensureParentDirectory();
10
- this.load();
11
- }
12
-
13
- ensureParentDirectory() {
14
- fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
15
- }
16
-
17
- load() {
18
- try {
19
- const raw = fs.readFileSync(this.filePath, "utf8");
20
- const parsed = JSON.parse(raw);
21
- if (parsed && typeof parsed === "object" && parsed.bindings) {
22
- this.state = {
23
- ...createEmptyState(),
24
- ...parsed,
25
- bindings: parsed.bindings || {},
26
- approvalCommandAllowlistByWorkspaceRoot: parsed.approvalCommandAllowlistByWorkspaceRoot || {},
27
- approvalPromptStateByThreadId: parsed.approvalPromptStateByThreadId || {},
28
- availableModelCatalog: parsed.availableModelCatalog || {
29
- models: [],
30
- updatedAt: "",
31
- },
32
- };
33
- }
34
- } catch {
35
- this.state = createEmptyState();
36
- }
37
- }
38
-
39
- save() {
40
- fs.writeFileSync(this.filePath, JSON.stringify(this.state, null, 2));
41
- }
42
-
43
- getBinding(bindingKey) {
44
- return this.state.bindings[bindingKey] || null;
45
- }
46
-
47
- listBindings() {
48
- return Object.entries(this.state.bindings || {}).map(([bindingKey, binding]) => ({
49
- bindingKey,
50
- ...(binding || {}),
51
- }));
52
- }
53
-
54
- getActiveWorkspaceRoot(bindingKey) {
55
- return normalizeValue(this.state.bindings[bindingKey]?.activeWorkspaceRoot);
56
- }
57
-
58
- updateBinding(bindingKey, nextBinding) {
59
- this.state.bindings[bindingKey] = {
60
- ...(this.state.bindings[bindingKey] || {}),
61
- ...(nextBinding || {}),
62
- };
63
- this.save();
64
- return this.state.bindings[bindingKey];
65
- }
66
-
67
- getThreadIdForWorkspace(bindingKey, workspaceRoot) {
68
- const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
69
- if (!normalizedWorkspaceRoot) {
70
- return "";
71
- }
72
- return this.state.bindings[bindingKey]?.threadIdByWorkspaceRoot?.[normalizedWorkspaceRoot] || "";
73
- }
74
-
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { normalizeModelCatalog } = require("./model-catalog");
4
+
5
+ class SessionStore {
6
+ constructor({ filePath }) {
7
+ this.filePath = filePath;
8
+ this.state = createEmptyState();
9
+ this.ensureParentDirectory();
10
+ this.load();
11
+ }
12
+
13
+ ensureParentDirectory() {
14
+ fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
15
+ }
16
+
17
+ load() {
18
+ try {
19
+ const raw = fs.readFileSync(this.filePath, "utf8");
20
+ const parsed = JSON.parse(raw);
21
+ if (parsed && typeof parsed === "object" && parsed.bindings) {
22
+ this.state = {
23
+ ...createEmptyState(),
24
+ ...parsed,
25
+ bindings: parsed.bindings || {},
26
+ approvalCommandAllowlistByWorkspaceRoot: parsed.approvalCommandAllowlistByWorkspaceRoot || {},
27
+ approvalPromptStateByThreadId: parsed.approvalPromptStateByThreadId || {},
28
+ availableModelCatalog: parsed.availableModelCatalog || {
29
+ models: [],
30
+ updatedAt: "",
31
+ },
32
+ };
33
+ }
34
+ } catch {
35
+ this.state = createEmptyState();
36
+ }
37
+ }
38
+
39
+ save() {
40
+ fs.writeFileSync(this.filePath, JSON.stringify(this.state, null, 2));
41
+ }
42
+
43
+ getBinding(bindingKey) {
44
+ return this.state.bindings[bindingKey] || null;
45
+ }
46
+
47
+ listBindings() {
48
+ return Object.entries(this.state.bindings || {}).map(([bindingKey, binding]) => ({
49
+ bindingKey,
50
+ ...(binding || {}),
51
+ }));
52
+ }
53
+
54
+ getActiveWorkspaceRoot(bindingKey) {
55
+ return normalizeValue(this.state.bindings[bindingKey]?.activeWorkspaceRoot);
56
+ }
57
+
58
+ updateBinding(bindingKey, nextBinding) {
59
+ this.state.bindings[bindingKey] = {
60
+ ...(this.state.bindings[bindingKey] || {}),
61
+ ...(nextBinding || {}),
62
+ };
63
+ this.save();
64
+ return this.state.bindings[bindingKey];
65
+ }
66
+
67
+ getThreadIdForWorkspace(bindingKey, workspaceRoot) {
68
+ const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
69
+ if (!normalizedWorkspaceRoot) {
70
+ return "";
71
+ }
72
+ return this.state.bindings[bindingKey]?.threadIdByWorkspaceRoot?.[normalizedWorkspaceRoot] || "";
73
+ }
74
+
75
75
  setThreadIdForWorkspace(bindingKey, workspaceRoot, threadId, extra = {}) {
76
76
  const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
77
77
  const normalizedThreadId = normalizeValue(threadId);
@@ -100,38 +100,38 @@ class SessionStore {
100
100
  workspaceBootstrapThreadIdByWorkspaceRoot,
101
101
  });
102
102
  }
103
-
104
- getCodexParamsForWorkspace(bindingKey, workspaceRoot) {
105
- const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
106
- if (!normalizedWorkspaceRoot) {
107
- return { model: "" };
108
- }
109
- const current = this.getBinding(bindingKey) || {};
110
- const codexParamsByWorkspaceRoot = getCodexParamsMap(current);
111
- const entry = codexParamsByWorkspaceRoot[normalizedWorkspaceRoot];
112
- return {
113
- model: normalizeValue(entry?.model),
114
- };
115
- }
116
-
117
- setCodexParamsForWorkspace(bindingKey, workspaceRoot, { model = "" }) {
118
- const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
119
- if (!normalizedWorkspaceRoot) {
120
- return this.getBinding(bindingKey);
121
- }
122
- const current = this.getBinding(bindingKey) || {};
123
- const codexParamsByWorkspaceRoot = {
124
- ...getCodexParamsMap(current),
125
- [normalizedWorkspaceRoot]: {
126
- model: normalizeValue(model),
127
- },
128
- };
129
- return this.updateBinding(bindingKey, {
130
- ...current,
131
- codexParamsByWorkspaceRoot,
132
- });
133
- }
134
-
103
+
104
+ getCodexParamsForWorkspace(bindingKey, workspaceRoot) {
105
+ const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
106
+ if (!normalizedWorkspaceRoot) {
107
+ return { model: "" };
108
+ }
109
+ const current = this.getBinding(bindingKey) || {};
110
+ const codexParamsByWorkspaceRoot = getCodexParamsMap(current);
111
+ const entry = codexParamsByWorkspaceRoot[normalizedWorkspaceRoot];
112
+ return {
113
+ model: normalizeValue(entry?.model),
114
+ };
115
+ }
116
+
117
+ setCodexParamsForWorkspace(bindingKey, workspaceRoot, { model = "" }) {
118
+ const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
119
+ if (!normalizedWorkspaceRoot) {
120
+ return this.getBinding(bindingKey);
121
+ }
122
+ const current = this.getBinding(bindingKey) || {};
123
+ const codexParamsByWorkspaceRoot = {
124
+ ...getCodexParamsMap(current),
125
+ [normalizedWorkspaceRoot]: {
126
+ model: normalizeValue(model),
127
+ },
128
+ };
129
+ return this.updateBinding(bindingKey, {
130
+ ...current,
131
+ codexParamsByWorkspaceRoot,
132
+ });
133
+ }
134
+
135
135
  clearThreadIdForWorkspace(bindingKey, workspaceRoot) {
136
136
  const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
137
137
  if (!normalizedWorkspaceRoot) {
@@ -152,37 +152,37 @@ class SessionStore {
152
152
  workspaceBootstrapThreadIdByWorkspaceRoot,
153
153
  });
154
154
  }
155
-
156
- setActiveWorkspaceRoot(bindingKey, workspaceRoot) {
157
- const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
158
- if (!normalizedWorkspaceRoot) {
159
- return this.getBinding(bindingKey);
160
- }
161
- return this.updateBinding(bindingKey, {
162
- activeWorkspaceRoot: normalizedWorkspaceRoot,
163
- });
164
- }
165
-
166
- listWorkspaceRoots(bindingKey) {
167
- const current = this.getBinding(bindingKey) || {};
168
- return Object.keys(getThreadMap(current));
169
- }
170
-
155
+
156
+ setActiveWorkspaceRoot(bindingKey, workspaceRoot) {
157
+ const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
158
+ if (!normalizedWorkspaceRoot) {
159
+ return this.getBinding(bindingKey);
160
+ }
161
+ return this.updateBinding(bindingKey, {
162
+ activeWorkspaceRoot: normalizedWorkspaceRoot,
163
+ });
164
+ }
165
+
166
+ listWorkspaceRoots(bindingKey) {
167
+ const current = this.getBinding(bindingKey) || {};
168
+ return Object.keys(getThreadMap(current));
169
+ }
170
+
171
171
  findBindingForThreadId(threadId) {
172
- const normalizedThreadId = normalizeValue(threadId);
173
- if (!normalizedThreadId) {
174
- return null;
175
- }
176
- for (const [bindingKey, binding] of Object.entries(this.state.bindings || {})) {
177
- for (const [workspaceRoot, candidateThreadId] of Object.entries(getThreadMap(binding))) {
178
- if (normalizeValue(candidateThreadId) === normalizedThreadId) {
179
- return {
180
- bindingKey,
181
- workspaceRoot: normalizeValue(workspaceRoot),
182
- };
183
- }
184
- }
185
- }
172
+ const normalizedThreadId = normalizeValue(threadId);
173
+ if (!normalizedThreadId) {
174
+ return null;
175
+ }
176
+ for (const [bindingKey, binding] of Object.entries(this.state.bindings || {})) {
177
+ for (const [workspaceRoot, candidateThreadId] of Object.entries(getThreadMap(binding))) {
178
+ if (normalizeValue(candidateThreadId) === normalizedThreadId) {
179
+ return {
180
+ bindingKey,
181
+ workspaceRoot: normalizeValue(workspaceRoot),
182
+ };
183
+ }
184
+ }
185
+ }
186
186
  return null;
187
187
  }
188
188
 
@@ -212,141 +212,141 @@ class SessionStore {
212
212
  workspaceBootstrapThreadIdByWorkspaceRoot,
213
213
  });
214
214
  }
215
-
216
- getApprovalCommandAllowlistForWorkspace(workspaceRoot) {
217
- const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
218
- if (!normalizedWorkspaceRoot) {
219
- return [];
220
- }
221
- const raw = this.state.approvalCommandAllowlistByWorkspaceRoot?.[normalizedWorkspaceRoot];
222
- if (!Array.isArray(raw)) {
223
- return [];
224
- }
225
- return raw
226
- .filter((entry) => Array.isArray(entry))
227
- .map((entry) => entry.map((part) => normalizeValue(part)).filter(Boolean))
228
- .filter((entry) => entry.length);
229
- }
230
-
231
- rememberApprovalPrefixForWorkspace(workspaceRoot, commandTokens) {
232
- const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
233
- const normalizedTokens = normalizeCommandTokens(commandTokens);
234
- if (!normalizedWorkspaceRoot || !normalizedTokens.length) {
235
- return this.getApprovalCommandAllowlistForWorkspace(workspaceRoot);
236
- }
237
- const current = this.getApprovalCommandAllowlistForWorkspace(normalizedWorkspaceRoot);
238
- if (!current.some((entry) => isSameTokenList(entry, normalizedTokens))) {
239
- current.push(normalizedTokens);
240
- this.state.approvalCommandAllowlistByWorkspaceRoot = {
241
- ...(this.state.approvalCommandAllowlistByWorkspaceRoot || {}),
242
- [normalizedWorkspaceRoot]: current,
243
- };
244
- this.save();
245
- }
246
- return current;
247
- }
248
-
249
- getApprovalPromptState(threadId) {
250
- const normalizedThreadId = normalizeValue(threadId);
251
- if (!normalizedThreadId) {
252
- return null;
253
- }
254
- const raw = this.state.approvalPromptStateByThreadId?.[normalizedThreadId];
255
- if (!raw || typeof raw !== "object") {
256
- return null;
257
- }
258
- return {
259
- requestId: normalizeValue(raw.requestId),
260
- signature: normalizeValue(raw.signature),
261
- promptedAt: normalizeValue(raw.promptedAt),
262
- };
263
- }
264
-
265
- rememberApprovalPrompt(threadId, requestId, signature = "") {
266
- const normalizedThreadId = normalizeValue(threadId);
267
- const normalizedRequestId = normalizeValue(requestId);
268
- const normalizedSignature = normalizeValue(signature);
269
- if (!normalizedThreadId || !normalizedRequestId) {
270
- return null;
271
- }
272
- this.state.approvalPromptStateByThreadId = {
273
- ...(this.state.approvalPromptStateByThreadId || {}),
274
- [normalizedThreadId]: {
275
- requestId: normalizedRequestId,
276
- signature: normalizedSignature,
277
- promptedAt: new Date().toISOString(),
278
- },
279
- };
280
- this.save();
281
- return this.getApprovalPromptState(normalizedThreadId);
282
- }
283
-
284
- clearApprovalPrompt(threadId) {
285
- const normalizedThreadId = normalizeValue(threadId);
286
- if (!normalizedThreadId || !this.state.approvalPromptStateByThreadId?.[normalizedThreadId]) {
287
- return;
288
- }
289
- const next = {
290
- ...(this.state.approvalPromptStateByThreadId || {}),
291
- };
292
- delete next[normalizedThreadId];
293
- this.state.approvalPromptStateByThreadId = next;
294
- this.save();
295
- }
296
-
297
- getAvailableModelCatalog() {
298
- const raw = this.state.availableModelCatalog;
299
- if (!raw || typeof raw !== "object") {
300
- return null;
301
- }
302
- const models = normalizeModelCatalog(raw.models);
303
- if (!models.length) {
304
- return null;
305
- }
306
- const updatedAt = normalizeValue(raw.updatedAt);
307
- return { models, updatedAt };
308
- }
309
-
310
- setAvailableModelCatalog(models) {
311
- const normalizedModels = normalizeModelCatalog(models);
312
- if (!normalizedModels.length) {
313
- return null;
314
- }
315
- this.state.availableModelCatalog = {
316
- models: normalizedModels,
317
- updatedAt: new Date().toISOString(),
318
- };
319
- this.save();
320
- return this.state.availableModelCatalog;
321
- }
322
-
323
- buildBindingKey({ workspaceId, accountId, senderId }) {
324
- return `${normalizeValue(workspaceId)}:${normalizeValue(accountId)}:${normalizeValue(senderId)}`;
325
- }
326
- }
327
-
328
- function createEmptyState() {
329
- return {
330
- bindings: {},
331
- approvalCommandAllowlistByWorkspaceRoot: {},
332
- approvalPromptStateByThreadId: {},
333
- availableModelCatalog: {
334
- models: [],
335
- updatedAt: "",
336
- },
337
- };
338
- }
339
-
340
- function normalizeValue(value) {
341
- return typeof value === "string" ? value.trim() : "";
342
- }
343
-
344
- function getThreadMap(binding) {
345
- return binding?.threadIdByWorkspaceRoot && typeof binding.threadIdByWorkspaceRoot === "object"
346
- ? binding.threadIdByWorkspaceRoot
347
- : {};
348
- }
349
-
215
+
216
+ getApprovalCommandAllowlistForWorkspace(workspaceRoot) {
217
+ const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
218
+ if (!normalizedWorkspaceRoot) {
219
+ return [];
220
+ }
221
+ const raw = this.state.approvalCommandAllowlistByWorkspaceRoot?.[normalizedWorkspaceRoot];
222
+ if (!Array.isArray(raw)) {
223
+ return [];
224
+ }
225
+ return raw
226
+ .filter((entry) => Array.isArray(entry))
227
+ .map((entry) => entry.map((part) => normalizeValue(part)).filter(Boolean))
228
+ .filter((entry) => entry.length);
229
+ }
230
+
231
+ rememberApprovalPrefixForWorkspace(workspaceRoot, commandTokens) {
232
+ const normalizedWorkspaceRoot = normalizeValue(workspaceRoot);
233
+ const normalizedTokens = normalizeCommandTokens(commandTokens);
234
+ if (!normalizedWorkspaceRoot || !normalizedTokens.length) {
235
+ return this.getApprovalCommandAllowlistForWorkspace(workspaceRoot);
236
+ }
237
+ const current = this.getApprovalCommandAllowlistForWorkspace(normalizedWorkspaceRoot);
238
+ if (!current.some((entry) => isSameTokenList(entry, normalizedTokens))) {
239
+ current.push(normalizedTokens);
240
+ this.state.approvalCommandAllowlistByWorkspaceRoot = {
241
+ ...(this.state.approvalCommandAllowlistByWorkspaceRoot || {}),
242
+ [normalizedWorkspaceRoot]: current,
243
+ };
244
+ this.save();
245
+ }
246
+ return current;
247
+ }
248
+
249
+ getApprovalPromptState(threadId) {
250
+ const normalizedThreadId = normalizeValue(threadId);
251
+ if (!normalizedThreadId) {
252
+ return null;
253
+ }
254
+ const raw = this.state.approvalPromptStateByThreadId?.[normalizedThreadId];
255
+ if (!raw || typeof raw !== "object") {
256
+ return null;
257
+ }
258
+ return {
259
+ requestId: normalizeValue(raw.requestId),
260
+ signature: normalizeValue(raw.signature),
261
+ promptedAt: normalizeValue(raw.promptedAt),
262
+ };
263
+ }
264
+
265
+ rememberApprovalPrompt(threadId, requestId, signature = "") {
266
+ const normalizedThreadId = normalizeValue(threadId);
267
+ const normalizedRequestId = normalizeValue(requestId);
268
+ const normalizedSignature = normalizeValue(signature);
269
+ if (!normalizedThreadId || !normalizedRequestId) {
270
+ return null;
271
+ }
272
+ this.state.approvalPromptStateByThreadId = {
273
+ ...(this.state.approvalPromptStateByThreadId || {}),
274
+ [normalizedThreadId]: {
275
+ requestId: normalizedRequestId,
276
+ signature: normalizedSignature,
277
+ promptedAt: new Date().toISOString(),
278
+ },
279
+ };
280
+ this.save();
281
+ return this.getApprovalPromptState(normalizedThreadId);
282
+ }
283
+
284
+ clearApprovalPrompt(threadId) {
285
+ const normalizedThreadId = normalizeValue(threadId);
286
+ if (!normalizedThreadId || !this.state.approvalPromptStateByThreadId?.[normalizedThreadId]) {
287
+ return;
288
+ }
289
+ const next = {
290
+ ...(this.state.approvalPromptStateByThreadId || {}),
291
+ };
292
+ delete next[normalizedThreadId];
293
+ this.state.approvalPromptStateByThreadId = next;
294
+ this.save();
295
+ }
296
+
297
+ getAvailableModelCatalog() {
298
+ const raw = this.state.availableModelCatalog;
299
+ if (!raw || typeof raw !== "object") {
300
+ return null;
301
+ }
302
+ const models = normalizeModelCatalog(raw.models);
303
+ if (!models.length) {
304
+ return null;
305
+ }
306
+ const updatedAt = normalizeValue(raw.updatedAt);
307
+ return { models, updatedAt };
308
+ }
309
+
310
+ setAvailableModelCatalog(models) {
311
+ const normalizedModels = normalizeModelCatalog(models);
312
+ if (!normalizedModels.length) {
313
+ return null;
314
+ }
315
+ this.state.availableModelCatalog = {
316
+ models: normalizedModels,
317
+ updatedAt: new Date().toISOString(),
318
+ };
319
+ this.save();
320
+ return this.state.availableModelCatalog;
321
+ }
322
+
323
+ buildBindingKey({ workspaceId, accountId, senderId }) {
324
+ return `${normalizeValue(workspaceId)}:${normalizeValue(accountId)}:${normalizeValue(senderId)}`;
325
+ }
326
+ }
327
+
328
+ function createEmptyState() {
329
+ return {
330
+ bindings: {},
331
+ approvalCommandAllowlistByWorkspaceRoot: {},
332
+ approvalPromptStateByThreadId: {},
333
+ availableModelCatalog: {
334
+ models: [],
335
+ updatedAt: "",
336
+ },
337
+ };
338
+ }
339
+
340
+ function normalizeValue(value) {
341
+ return typeof value === "string" ? value.trim() : "";
342
+ }
343
+
344
+ function getThreadMap(binding) {
345
+ return binding?.threadIdByWorkspaceRoot && typeof binding.threadIdByWorkspaceRoot === "object"
346
+ ? binding.threadIdByWorkspaceRoot
347
+ : {};
348
+ }
349
+
350
350
  function getCodexParamsMap(binding) {
351
351
  return binding?.codexParamsByWorkspaceRoot && typeof binding.codexParamsByWorkspaceRoot === "object"
352
352
  ? binding.codexParamsByWorkspaceRoot
@@ -359,18 +359,18 @@ function getWorkspaceBootstrapThreadMap(binding) {
359
359
  ? binding.workspaceBootstrapThreadIdByWorkspaceRoot
360
360
  : {};
361
361
  }
362
-
363
- function normalizeCommandTokens(tokens) {
364
- return Array.isArray(tokens)
365
- ? tokens.map((part) => normalizeValue(part)).filter(Boolean)
366
- : [];
367
- }
368
-
369
- function isSameTokenList(left, right) {
370
- if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
371
- return false;
372
- }
373
- return left.every((value, index) => value === right[index]);
374
- }
375
-
376
- module.exports = { SessionStore };
362
+
363
+ function normalizeCommandTokens(tokens) {
364
+ return Array.isArray(tokens)
365
+ ? tokens.map((part) => normalizeValue(part)).filter(Boolean)
366
+ : [];
367
+ }
368
+
369
+ function isSameTokenList(left, right) {
370
+ if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
371
+ return false;
372
+ }
373
+ return left.every((value, index) => value === right[index]);
374
+ }
375
+
376
+ module.exports = { SessionStore };