workspacecord 1.0.2

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.
@@ -0,0 +1,1224 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ config
4
+ } from "./chunk-2LBNM64L.js";
5
+ import {
6
+ Store,
7
+ bindProjectCategory,
8
+ getProjectByCategoryId,
9
+ getProjectByName,
10
+ loadRegistry,
11
+ setProjectControlChannel,
12
+ setProjectHistoryChannel,
13
+ updateProject
14
+ } from "./chunk-NIXZJTOZ.js";
15
+ import {
16
+ isAbortError,
17
+ resolvePath,
18
+ sanitizeName
19
+ } from "./chunk-WE4X3JB3.js";
20
+
21
+ // src/thread-manager.ts
22
+ import { existsSync as existsSync2 } from "fs";
23
+ import { sep } from "path";
24
+
25
+ // src/providers/claude-provider.ts
26
+ import { query } from "@anthropic-ai/claude-agent-sdk";
27
+ var TASK_TOOLS = /* @__PURE__ */ new Set(["TaskCreate", "TaskUpdate", "TaskList", "TaskGet"]);
28
+ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".bmp"]);
29
+ function extractImagePath(toolName, toolInput) {
30
+ try {
31
+ const data = JSON.parse(toolInput);
32
+ if (toolName === "Write" || toolName === "Read") {
33
+ const filePath = data.file_path;
34
+ if (filePath && IMAGE_EXTENSIONS.has(filePath.slice(filePath.lastIndexOf(".")).toLowerCase())) {
35
+ return filePath;
36
+ }
37
+ }
38
+ } catch {
39
+ }
40
+ return null;
41
+ }
42
+ function buildClaudeSystemPrompt(parts) {
43
+ if (parts.length > 0) {
44
+ return { type: "preset", preset: "claude_code", append: parts.join("\n\n") };
45
+ }
46
+ return { type: "preset", preset: "claude_code" };
47
+ }
48
+ var ClaudeProvider = class {
49
+ name = "claude";
50
+ supports(feature) {
51
+ return [
52
+ "resume_from_terminal",
53
+ "plugins",
54
+ "ask_user_question",
55
+ "mode_switching",
56
+ "continue"
57
+ ].includes(feature);
58
+ }
59
+ async *sendPrompt(prompt, options) {
60
+ const systemPrompt = buildClaudeSystemPrompt(options.systemPromptParts);
61
+ const isBypass = options.claudePermissionMode === "bypass";
62
+ function buildQueryPrompt() {
63
+ if (typeof prompt === "string") return prompt;
64
+ const claudeBlocks = prompt.filter((b) => b.type !== "local_image");
65
+ const userMessage = {
66
+ type: "user",
67
+ message: { role: "user", content: claudeBlocks },
68
+ parent_tool_use_id: null,
69
+ session_id: ""
70
+ };
71
+ return (async function* () {
72
+ yield userMessage;
73
+ })();
74
+ }
75
+ let retried = false;
76
+ let resumeId = options.providerSessionId;
77
+ while (true) {
78
+ let failed = false;
79
+ const stream = query({
80
+ prompt: buildQueryPrompt(),
81
+ options: {
82
+ cwd: options.directory,
83
+ resume: resumeId,
84
+ abortController: options.abortController,
85
+ permissionMode: isBypass ? "bypassPermissions" : "default",
86
+ ...isBypass ? { allowDangerouslySkipPermissions: true } : {},
87
+ model: options.model,
88
+ systemPrompt,
89
+ canUseTool: options.canUseTool,
90
+ includePartialMessages: true,
91
+ settingSources: ["user", "project", "local"]
92
+ }
93
+ });
94
+ yield* this.translateStream(stream, resumeId, retried, (f, r) => {
95
+ failed = f;
96
+ retried = r;
97
+ });
98
+ if (failed && !retried) {
99
+ retried = true;
100
+ resumeId = void 0;
101
+ yield { type: "session_init", providerSessionId: "" };
102
+ continue;
103
+ }
104
+ break;
105
+ }
106
+ }
107
+ async *continueSession(options) {
108
+ const systemPrompt = buildClaudeSystemPrompt(options.systemPromptParts);
109
+ const isBypass = options.claudePermissionMode === "bypass";
110
+ let retried = false;
111
+ let resumeId = options.providerSessionId;
112
+ while (true) {
113
+ let failed = false;
114
+ const stream = query({
115
+ prompt: "",
116
+ options: {
117
+ cwd: options.directory,
118
+ ...resumeId ? { continue: true, resume: resumeId } : {},
119
+ abortController: options.abortController,
120
+ permissionMode: isBypass ? "bypassPermissions" : "default",
121
+ ...isBypass ? { allowDangerouslySkipPermissions: true } : {},
122
+ model: options.model,
123
+ systemPrompt,
124
+ canUseTool: options.canUseTool,
125
+ includePartialMessages: true,
126
+ settingSources: ["user", "project", "local"]
127
+ }
128
+ });
129
+ yield* this.translateStream(stream, resumeId, retried, (f, r) => {
130
+ failed = f;
131
+ retried = r;
132
+ });
133
+ if (failed && !retried) {
134
+ retried = true;
135
+ resumeId = void 0;
136
+ yield { type: "session_init", providerSessionId: "" };
137
+ continue;
138
+ }
139
+ break;
140
+ }
141
+ }
142
+ async *translateStream(stream, resumeId, alreadyRetried, setRetry) {
143
+ let currentToolName = null;
144
+ let currentToolInput = "";
145
+ for await (const message of stream) {
146
+ if (message.type === "system" && "subtype" in message && message.subtype === "init") {
147
+ const initMessage = message;
148
+ yield { type: "session_init", providerSessionId: initMessage.session_id || "" };
149
+ }
150
+ if (message.type === "stream_event") {
151
+ const event = message.event;
152
+ if (event?.type === "content_block_start") {
153
+ if (event.content_block?.type === "tool_use") {
154
+ currentToolName = event.content_block.name || "tool";
155
+ currentToolInput = "";
156
+ }
157
+ }
158
+ if (event?.type === "content_block_delta") {
159
+ if (event.delta?.type === "text_delta" && event.delta.text) {
160
+ yield { type: "text_delta", text: event.delta.text };
161
+ }
162
+ if (event.delta?.type === "input_json_delta" && event.delta.partial_json) {
163
+ currentToolInput += event.delta.partial_json;
164
+ }
165
+ }
166
+ if (event?.type === "content_block_stop") {
167
+ if (currentToolName) {
168
+ if (currentToolName === "AskUserQuestion") {
169
+ yield { type: "ask_user", questionsJson: currentToolInput };
170
+ } else if (TASK_TOOLS.has(currentToolName)) {
171
+ yield { type: "task", action: currentToolName, dataJson: currentToolInput };
172
+ } else {
173
+ yield { type: "tool_start", toolName: currentToolName, toolInput: currentToolInput };
174
+ }
175
+ const imagePath = extractImagePath(currentToolName, currentToolInput);
176
+ if (imagePath) {
177
+ yield { type: "image_file", filePath: imagePath };
178
+ }
179
+ currentToolName = null;
180
+ currentToolInput = "";
181
+ }
182
+ }
183
+ }
184
+ if (message.type === "user") {
185
+ const content = message.message?.content;
186
+ let resultText = "";
187
+ const toolName = "";
188
+ if (Array.isArray(content)) {
189
+ for (const block of content) {
190
+ if (block.type === "tool_result" && block.content) {
191
+ if (typeof block.content === "string") {
192
+ resultText += block.content;
193
+ } else if (Array.isArray(block.content)) {
194
+ for (const sub of block.content) {
195
+ if (sub.type === "text") resultText += sub.text;
196
+ }
197
+ }
198
+ }
199
+ }
200
+ }
201
+ if (resultText) {
202
+ yield { type: "tool_result", toolName, result: resultText };
203
+ }
204
+ }
205
+ if (message.type === "system") {
206
+ const systemMessage = message;
207
+ const sub = systemMessage.subtype;
208
+ if (sub === "task_started") {
209
+ yield {
210
+ type: "task_started",
211
+ taskId: systemMessage.task_id || "",
212
+ description: systemMessage.description || ""
213
+ };
214
+ } else if (sub === "task_progress") {
215
+ yield {
216
+ type: "task_progress",
217
+ taskId: systemMessage.task_id || "",
218
+ description: systemMessage.description || "",
219
+ lastToolName: systemMessage.last_tool_name,
220
+ summary: systemMessage.summary
221
+ };
222
+ } else if (sub === "task_notification") {
223
+ yield {
224
+ type: "task_done",
225
+ taskId: systemMessage.task_id || "",
226
+ status: systemMessage.status || "completed",
227
+ summary: systemMessage.summary || ""
228
+ };
229
+ }
230
+ }
231
+ if (message.type === "result") {
232
+ const r = message;
233
+ if (r.subtype !== "success" && !alreadyRetried && resumeId) {
234
+ setRetry(true, false);
235
+ break;
236
+ }
237
+ yield {
238
+ type: "result",
239
+ success: r.subtype === "success",
240
+ costUsd: r.total_cost_usd ?? 0,
241
+ durationMs: r.duration_ms ?? 0,
242
+ numTurns: r.num_turns ?? 0,
243
+ errors: r.errors ?? []
244
+ };
245
+ }
246
+ }
247
+ }
248
+ };
249
+
250
+ // src/providers/index.ts
251
+ var providers = /* @__PURE__ */ new Map();
252
+ providers.set("claude", new ClaudeProvider());
253
+ var codexLoadAttempted = false;
254
+ async function loadCodexProvider() {
255
+ try {
256
+ const { CodexProvider } = await import("./codex-provider-7CI5W34X.js");
257
+ providers.set("codex", new CodexProvider());
258
+ codexLoadAttempted = true;
259
+ } catch (err) {
260
+ codexLoadAttempted = true;
261
+ throw new Error(
262
+ `Codex provider is unavailable. Install @openai/codex-sdk manually. Root cause: ${err.message}`
263
+ );
264
+ }
265
+ }
266
+ async function ensureProvider(name) {
267
+ if (providers.has(name)) return providers.get(name);
268
+ if (name === "codex" && !codexLoadAttempted) {
269
+ await loadCodexProvider();
270
+ return providers.get("codex");
271
+ }
272
+ if (name === "codex") {
273
+ throw new Error(
274
+ "Codex provider is unavailable. Install @openai/codex-sdk and restart workspacecord."
275
+ );
276
+ }
277
+ throw new Error(`Unknown provider: ${name}`);
278
+ }
279
+
280
+ // src/agents.ts
281
+ var agents = [
282
+ {
283
+ name: "code-reviewer",
284
+ emoji: "\u{1F50D}",
285
+ description: "Code quality, bugs, best practices",
286
+ systemPrompt: `You are a senior code reviewer. Focus on:
287
+ - Code quality and readability
288
+ - Potential bugs and edge cases
289
+ - Security vulnerabilities
290
+ - Performance concerns
291
+ - Best practices and design patterns
292
+ Be specific, cite line numbers, and suggest concrete improvements.`
293
+ },
294
+ {
295
+ name: "architect",
296
+ emoji: "\u{1F3D7}\uFE0F",
297
+ description: "System design, patterns, scalability",
298
+ systemPrompt: `You are a software architect. Focus on:
299
+ - System design and architecture patterns
300
+ - Scalability and maintainability
301
+ - Component boundaries and interfaces
302
+ - Data flow and state management
303
+ - Trade-offs between different approaches
304
+ Think in terms of systems, not just code.`
305
+ },
306
+ {
307
+ name: "debugger",
308
+ emoji: "\u{1F41B}",
309
+ description: "Root cause analysis, debugging strategies",
310
+ systemPrompt: `You are a debugging specialist. Focus on:
311
+ - Root cause analysis over symptoms
312
+ - Systematic debugging strategies
313
+ - Reproducing issues reliably
314
+ - Tracing data flow to find where things break
315
+ - Suggesting targeted fixes with minimal side effects
316
+ Think methodically and follow the evidence.`
317
+ },
318
+ {
319
+ name: "security",
320
+ emoji: "\u{1F512}",
321
+ description: "Vulnerabilities, OWASP, secure coding",
322
+ systemPrompt: `You are a security analyst. Focus on:
323
+ - OWASP Top 10 vulnerabilities
324
+ - Input validation and sanitization
325
+ - Authentication and authorization flaws
326
+ - Injection attacks (SQL, XSS, command)
327
+ - Secure defaults and least privilege
328
+ Flag issues with severity ratings and remediation steps.`
329
+ },
330
+ {
331
+ name: "performance",
332
+ emoji: "\u{1F680}",
333
+ description: "Optimization, profiling, bottlenecks",
334
+ systemPrompt: `You are a performance engineer. Focus on:
335
+ - Identifying bottlenecks and hot paths
336
+ - Algorithm and data structure choices
337
+ - Memory allocation and GC pressure
338
+ - I/O optimization and caching strategies
339
+ - Benchmarking and profiling recommendations
340
+ Quantify impact where possible.`
341
+ },
342
+ {
343
+ name: "devops",
344
+ emoji: "\u2699\uFE0F",
345
+ description: "CI/CD, Docker, infrastructure",
346
+ systemPrompt: `You are a DevOps engineer. Focus on:
347
+ - CI/CD pipeline design and optimization
348
+ - Container and orchestration best practices
349
+ - Infrastructure as code
350
+ - Monitoring, logging, and observability
351
+ - Deployment strategies and rollback plans
352
+ Prioritize reliability and automation.`
353
+ },
354
+ {
355
+ name: "general",
356
+ emoji: "\u{1F9E0}",
357
+ description: "Default \u2014 no specialized focus",
358
+ systemPrompt: ""
359
+ }
360
+ ];
361
+ function getAgent(name) {
362
+ return agents.find((a) => a.name === name);
363
+ }
364
+
365
+ // src/project-manager.ts
366
+ import { readFile, writeFile } from "fs/promises";
367
+ import { existsSync } from "fs";
368
+ import { join } from "path";
369
+ async function loadProjects() {
370
+ await loadRegistry();
371
+ }
372
+ function getProject(categoryId) {
373
+ const project = getProjectByCategoryId(categoryId);
374
+ if (!project) return void 0;
375
+ return {
376
+ categoryId: project.discordCategoryId ?? categoryId,
377
+ historyChannelId: project.historyChannelId,
378
+ controlChannelId: project.controlChannelId,
379
+ name: project.name,
380
+ directory: project.path,
381
+ personality: project.personality,
382
+ skills: Object.entries(project.skills).map(([name, prompt]) => ({ name, prompt })),
383
+ mcpServers: project.mcpServers,
384
+ createdAt: project.createdAt
385
+ };
386
+ }
387
+ async function bindMountedProjectToCategory(projectName, categoryId, categoryName) {
388
+ const project = getProjectByName(projectName);
389
+ if (!project) throw new Error(`Mounted project not found: ${projectName}`);
390
+ if (project.discordCategoryId && project.discordCategoryId !== categoryId) {
391
+ throw new Error(`Project "${projectName}" is already bound to another Discord category`);
392
+ }
393
+ await bindProjectCategory(projectName, categoryId, categoryName);
394
+ return getProject(categoryId);
395
+ }
396
+ function setHistoryChannelId(categoryId, channelId) {
397
+ const project = getProjectByCategoryId(categoryId);
398
+ if (!project) return;
399
+ void setProjectHistoryChannel(project.name, channelId);
400
+ }
401
+ function setControlChannelId(categoryId, channelId) {
402
+ const project = getProjectByCategoryId(categoryId);
403
+ if (!project) return;
404
+ void setProjectControlChannel(project.name, channelId);
405
+ }
406
+ function setPersonality(categoryId, personality) {
407
+ const project = getProjectByCategoryId(categoryId);
408
+ if (!project) return;
409
+ project.personality = personality;
410
+ void updateProject(project);
411
+ }
412
+ function getPersonality(categoryId) {
413
+ return getProjectByCategoryId(categoryId)?.personality;
414
+ }
415
+ function clearPersonality(categoryId) {
416
+ const project = getProjectByCategoryId(categoryId);
417
+ if (!project) return;
418
+ delete project.personality;
419
+ void updateProject(project);
420
+ }
421
+ function addSkill(categoryId, name, prompt) {
422
+ const project = getProjectByCategoryId(categoryId);
423
+ if (!project) return;
424
+ project.skills[name] = prompt;
425
+ void updateProject(project);
426
+ }
427
+ function removeSkill(categoryId, name) {
428
+ const project = getProjectByCategoryId(categoryId);
429
+ if (!project || !project.skills[name]) return false;
430
+ delete project.skills[name];
431
+ void updateProject(project);
432
+ return true;
433
+ }
434
+ function getSkills(categoryId) {
435
+ const project = getProjectByCategoryId(categoryId);
436
+ if (!project) return [];
437
+ return Object.entries(project.skills).map(([name, prompt]) => ({ name, prompt }));
438
+ }
439
+ function executeSkill(categoryId, name, input) {
440
+ const project = getProjectByCategoryId(categoryId);
441
+ if (!project) return null;
442
+ const template = project.skills[name];
443
+ if (!template) return null;
444
+ return input ? template.replace(/\{input\}/g, input) : template.replace(/\{input\}/g, "");
445
+ }
446
+ async function addMcpServer(categoryId, serverName, command, args) {
447
+ const project = getProjectByCategoryId(categoryId);
448
+ if (!project) return;
449
+ const existing = project.mcpServers.findIndex((server2) => server2.name === serverName);
450
+ const server = {
451
+ name: serverName,
452
+ command,
453
+ ...args?.length ? { args } : {}
454
+ };
455
+ if (existing >= 0) {
456
+ project.mcpServers[existing] = server;
457
+ } else {
458
+ project.mcpServers.push(server);
459
+ }
460
+ await updateProject(project);
461
+ await writeMcpJson(project.path, project.mcpServers);
462
+ }
463
+ async function removeMcpServer(categoryId, serverName) {
464
+ const project = getProjectByCategoryId(categoryId);
465
+ if (!project) return false;
466
+ const index = project.mcpServers.findIndex((server) => server.name === serverName);
467
+ if (index < 0) return false;
468
+ project.mcpServers.splice(index, 1);
469
+ await updateProject(project);
470
+ await writeMcpJson(project.path, project.mcpServers);
471
+ return true;
472
+ }
473
+ function getMcpServers(categoryId) {
474
+ return getProjectByCategoryId(categoryId)?.mcpServers || [];
475
+ }
476
+ async function writeMcpJson(projectDir, servers) {
477
+ const mcpPath = join(projectDir, ".mcp.json");
478
+ let mcpConfig = {};
479
+ try {
480
+ if (existsSync(mcpPath)) {
481
+ const existing = await readFile(mcpPath, "utf-8");
482
+ mcpConfig = JSON.parse(existing);
483
+ }
484
+ } catch {
485
+ }
486
+ const mcpServers = {};
487
+ for (const server of servers) {
488
+ mcpServers[server.name] = {
489
+ command: server.command,
490
+ ...server.args?.length ? { args: server.args } : {}
491
+ };
492
+ }
493
+ mcpConfig.mcpServers = mcpServers;
494
+ await writeFile(mcpPath, JSON.stringify(mcpConfig, null, 2), "utf-8");
495
+ }
496
+
497
+ // src/thread-manager.ts
498
+ var MODE_PROMPTS = {
499
+ auto: "",
500
+ plan: "You MUST use EnterPlanMode at the start of every task. Present your plan for user approval before making any code changes. Do not write or edit files until the user approves the plan.",
501
+ normal: "Before performing destructive or significant operations (deleting files, running dangerous commands, making large refactors, writing to many files), use AskUserQuestion to confirm with the user first. Ask for explicit approval before proceeding with changes.",
502
+ monitor: "This session is running in monitored autonomy mode. Treat the active user request as the task objective and keep working until it is fully satisfied. Do not stop at a partial implementation or ask the user for follow-up direction unless you are truly blocked by missing permissions, credentials, or required external information that you cannot obtain yourself. When you believe the task is complete, explain concisely what was finished and why it satisfies the request."
503
+ };
504
+ var MONITOR_SYSTEM_PROMPT = `You are a monitor agent supervising another coding agent.
505
+
506
+ Your job is to judge progress against the user's original request and decide whether the worker should continue.
507
+
508
+ Return JSON only in this schema:
509
+ {
510
+ "status": "complete" | "continue" | "blocked",
511
+ "confidence": "high" | "medium" | "low",
512
+ "rationale": "Short explanation tied to the original request",
513
+ "steering": "Concrete next instructions for the worker. Empty string only when status is complete.",
514
+ "completionSummary": "Short summary of what is complete. Empty string unless status is complete."
515
+ }
516
+
517
+ Rules:
518
+ - Favor continuing unless the task clearly satisfies the original request.
519
+ - Judge against robustness, completeness, and the user's stated quality bar, not just whether some code changed.
520
+ - If the worker stopped early, ask for the next concrete step instead of accepting the output.
521
+ - Use "blocked" only for true blockers the worker cannot resolve autonomously.
522
+ - Never ask the human for optional next steps.
523
+ - Output valid JSON and nothing else.`;
524
+ var sessionStore = new Store("sessions.json");
525
+ var sessions = /* @__PURE__ */ new Map();
526
+ var idToChannelId = /* @__PURE__ */ new Map();
527
+ var sessionsByCategory = /* @__PURE__ */ new Map();
528
+ var sessionControllers = /* @__PURE__ */ new Map();
529
+ var sessionAbortReasons = /* @__PURE__ */ new Map();
530
+ var saveQueue = Promise.resolve();
531
+ var saveTimer = null;
532
+ function createDefaultWorkflowState() {
533
+ return {
534
+ status: "idle",
535
+ iteration: 0,
536
+ updatedAt: Date.now()
537
+ };
538
+ }
539
+ async function loadSessions() {
540
+ const data = await sessionStore.read();
541
+ if (!data) return;
542
+ let cleaned = false;
543
+ for (const s of data) {
544
+ if (!s.categoryId) {
545
+ cleaned = true;
546
+ console.warn(`Skipping invalid persisted session "${s.id}" (missing categoryId).`);
547
+ continue;
548
+ }
549
+ if (!s.channelId) {
550
+ cleaned = true;
551
+ console.warn(`Skipping invalid persisted session "${s.id}" (missing channelId).`);
552
+ continue;
553
+ }
554
+ if (sessions.has(s.channelId)) {
555
+ cleaned = true;
556
+ console.warn(
557
+ `Skipping duplicate persisted session "${s.id}" (channelId ${s.channelId} already loaded).`
558
+ );
559
+ continue;
560
+ }
561
+ const provider = s.provider ?? "claude";
562
+ sessions.set(s.channelId, {
563
+ ...s,
564
+ provider,
565
+ verbose: s.verbose ?? false,
566
+ mode: s.mode ?? "auto",
567
+ subagentDepth: s.subagentDepth ?? 0,
568
+ type: s.type ?? "persistent",
569
+ workflowState: s.workflowState ?? createDefaultWorkflowState(),
570
+ currentTurn: s.currentTurn ?? 0,
571
+ humanResolved: s.humanResolved ?? false,
572
+ currentInteractionMessageId: s.currentInteractionMessageId,
573
+ statusCardMessageId: s.statusCardMessageId,
574
+ discoverySource: s.discoverySource ?? "discord",
575
+ lastObservedState: s.lastObservedState,
576
+ lastObservedEventKey: s.lastObservedEventKey,
577
+ lastObservedAt: s.lastObservedAt,
578
+ lastObservedCwd: s.lastObservedCwd,
579
+ remoteHumanControl: s.remoteHumanControl,
580
+ activeHumanGateId: s.activeHumanGateId,
581
+ isGenerating: false
582
+ });
583
+ idToChannelId.set(s.id, s.channelId);
584
+ if (!sessionsByCategory.has(s.categoryId)) {
585
+ sessionsByCategory.set(s.categoryId, /* @__PURE__ */ new Set());
586
+ }
587
+ sessionsByCategory.get(s.categoryId).add(s.channelId);
588
+ }
589
+ if (cleaned) {
590
+ await saveSessions();
591
+ }
592
+ console.log(`[session-manager] Restored ${sessions.size} session(s)`);
593
+ }
594
+ async function persistSessionsNow() {
595
+ const data = [];
596
+ for (const [, s] of sessions) {
597
+ data.push({
598
+ id: s.id,
599
+ channelId: s.channelId,
600
+ categoryId: s.categoryId,
601
+ projectName: s.projectName,
602
+ agentLabel: s.agentLabel,
603
+ provider: s.provider,
604
+ providerSessionId: s.providerSessionId,
605
+ model: s.model,
606
+ type: s.type,
607
+ parentChannelId: s.parentChannelId,
608
+ subagentDepth: s.subagentDepth,
609
+ directory: s.directory,
610
+ mode: s.mode,
611
+ agentPersona: s.agentPersona,
612
+ verbose: s.verbose || false,
613
+ claudePermissionMode: s.claudePermissionMode,
614
+ monitorGoal: s.monitorGoal,
615
+ monitorProviderSessionId: s.monitorProviderSessionId,
616
+ workflowState: s.workflowState,
617
+ createdAt: s.createdAt,
618
+ lastActivity: s.lastActivity,
619
+ messageCount: s.messageCount,
620
+ totalCost: s.totalCost,
621
+ currentTurn: s.currentTurn,
622
+ humanResolved: s.humanResolved,
623
+ currentInteractionMessageId: s.currentInteractionMessageId,
624
+ statusCardMessageId: s.statusCardMessageId,
625
+ discoverySource: s.discoverySource,
626
+ lastObservedState: s.lastObservedState,
627
+ lastObservedEventKey: s.lastObservedEventKey,
628
+ lastObservedAt: s.lastObservedAt,
629
+ lastObservedCwd: s.lastObservedCwd,
630
+ remoteHumanControl: s.remoteHumanControl,
631
+ activeHumanGateId: s.activeHumanGateId
632
+ });
633
+ }
634
+ await sessionStore.write(data);
635
+ }
636
+ function saveSessions() {
637
+ saveQueue = saveQueue.catch(() => {
638
+ }).then(async () => {
639
+ try {
640
+ await persistSessionsNow();
641
+ } catch (err) {
642
+ console.error(`[session-manager] Failed to persist sessions: ${err.message}`);
643
+ }
644
+ });
645
+ return saveQueue;
646
+ }
647
+ function debouncedSave() {
648
+ if (saveTimer) clearTimeout(saveTimer);
649
+ saveTimer = setTimeout(() => {
650
+ saveTimer = null;
651
+ saveSessions();
652
+ }, 1e3);
653
+ }
654
+ function saveSessionsImmediate() {
655
+ if (saveTimer) {
656
+ clearTimeout(saveTimer);
657
+ saveTimer = null;
658
+ }
659
+ return saveSessions();
660
+ }
661
+ async function createSession(params) {
662
+ const {
663
+ channelId,
664
+ categoryId,
665
+ projectName,
666
+ agentLabel,
667
+ provider,
668
+ providerSessionId,
669
+ model,
670
+ type,
671
+ parentChannelId,
672
+ subagentDepth = 0,
673
+ mode = config.defaultMode,
674
+ claudePermissionMode,
675
+ discoverySource = "discord",
676
+ remoteHumanControl
677
+ } = params;
678
+ const resolvedDir = resolvePath(params.directory);
679
+ if (!existsSync2(resolvedDir)) {
680
+ throw new Error(`Directory does not exist: ${resolvedDir}`);
681
+ }
682
+ await ensureProvider(provider);
683
+ if (sessions.has(channelId)) {
684
+ throw new Error(`Session for channelId "${channelId}" already exists`);
685
+ }
686
+ let id = sanitizeName(agentLabel);
687
+ let suffix = 1;
688
+ while (idToChannelId.has(id)) {
689
+ suffix++;
690
+ id = sanitizeName(`${agentLabel}-${suffix}`);
691
+ }
692
+ const session = {
693
+ id,
694
+ channelId,
695
+ categoryId,
696
+ projectName,
697
+ agentLabel,
698
+ provider,
699
+ providerSessionId,
700
+ model,
701
+ type,
702
+ parentChannelId,
703
+ subagentDepth,
704
+ directory: resolvedDir,
705
+ mode,
706
+ agentPersona: void 0,
707
+ verbose: false,
708
+ claudePermissionMode,
709
+ monitorGoal: void 0,
710
+ monitorProviderSessionId: void 0,
711
+ workflowState: createDefaultWorkflowState(),
712
+ isGenerating: false,
713
+ createdAt: Date.now(),
714
+ lastActivity: Date.now(),
715
+ messageCount: 0,
716
+ totalCost: 0,
717
+ currentTurn: 0,
718
+ humanResolved: false,
719
+ currentInteractionMessageId: void 0,
720
+ statusCardMessageId: void 0,
721
+ discoverySource,
722
+ remoteHumanControl
723
+ };
724
+ sessions.set(channelId, session);
725
+ idToChannelId.set(id, channelId);
726
+ if (!sessionsByCategory.has(categoryId)) {
727
+ sessionsByCategory.set(categoryId, /* @__PURE__ */ new Set());
728
+ }
729
+ sessionsByCategory.get(categoryId).add(channelId);
730
+ await saveSessionsImmediate();
731
+ return session;
732
+ }
733
+ function getSession(id) {
734
+ const channelId = idToChannelId.get(id);
735
+ return channelId ? sessions.get(channelId) : void 0;
736
+ }
737
+ function getSessionByChannel(channelId) {
738
+ return sessions.get(channelId);
739
+ }
740
+ var getSessionByThread = getSessionByChannel;
741
+ function getSessionByCodexId(codexSessionId) {
742
+ for (const session of sessions.values()) {
743
+ if (session.provider === "codex" && session.providerSessionId === codexSessionId) {
744
+ return session;
745
+ }
746
+ }
747
+ return void 0;
748
+ }
749
+ function getSessionByProviderSession(provider, providerSessionId) {
750
+ if (!providerSessionId) return void 0;
751
+ for (const session of sessions.values()) {
752
+ if (session.provider !== provider) continue;
753
+ if (session.providerSessionId === providerSessionId) return session;
754
+ }
755
+ return void 0;
756
+ }
757
+ function getSessionsByCategory(categoryId) {
758
+ const channelIds = sessionsByCategory.get(categoryId);
759
+ if (!channelIds) return [];
760
+ const result = [];
761
+ for (const channelId of channelIds) {
762
+ const session = sessions.get(channelId);
763
+ if (session) result.push(session);
764
+ }
765
+ return result;
766
+ }
767
+ function getAllSessions() {
768
+ return Array.from(sessions.values());
769
+ }
770
+ function getSessionByProviderSessionId(provider, providerSessionId) {
771
+ for (const session of sessions.values()) {
772
+ if (session.provider !== provider) continue;
773
+ if (!session.providerSessionId) continue;
774
+ if (session.providerSessionId === providerSessionId) return session;
775
+ }
776
+ return void 0;
777
+ }
778
+ function findCodexSessionForMonitor(providerSessionId, cwd) {
779
+ if (providerSessionId) {
780
+ const byProviderId = getSessionByProviderSessionId("codex", providerSessionId);
781
+ if (byProviderId) return byProviderId;
782
+ }
783
+ if (!cwd) return void 0;
784
+ const normalizedCwd = resolvePath(cwd);
785
+ let matched;
786
+ let matchedLen = -1;
787
+ for (const session of sessions.values()) {
788
+ if (session.provider !== "codex") continue;
789
+ const sessionDir = resolvePath(session.directory);
790
+ if (normalizedCwd !== sessionDir && !normalizedCwd.startsWith(`${sessionDir}/`)) continue;
791
+ if (sessionDir.length > matchedLen) {
792
+ matched = session;
793
+ matchedLen = sessionDir.length;
794
+ }
795
+ }
796
+ return matched;
797
+ }
798
+ function stripCodexMonitorPrefix(sessionId) {
799
+ return sessionId.startsWith("codex:") ? sessionId.slice("codex:".length) : sessionId;
800
+ }
801
+ function findCodexSessionByProviderSessionId(providerSessionId) {
802
+ const normalized = stripCodexMonitorPrefix(providerSessionId);
803
+ for (const session of sessions.values()) {
804
+ if (session.provider !== "codex") continue;
805
+ if (!session.providerSessionId) continue;
806
+ if (session.providerSessionId === normalized || session.providerSessionId === providerSessionId) {
807
+ return session;
808
+ }
809
+ }
810
+ return void 0;
811
+ }
812
+ function findCodexSessionByCwd(cwd) {
813
+ const normalizedCwd = resolvePath(cwd);
814
+ let best;
815
+ let bestLen = -1;
816
+ for (const session of sessions.values()) {
817
+ if (session.provider !== "codex") continue;
818
+ const dir = resolvePath(session.directory);
819
+ const isMatch = normalizedCwd === dir || normalizedCwd.startsWith(`${dir}${sep}`);
820
+ if (!isMatch) continue;
821
+ if (dir.length > bestLen) {
822
+ best = session;
823
+ bestLen = dir.length;
824
+ }
825
+ }
826
+ return best;
827
+ }
828
+ function resolveCodexSessionFromMonitor(monitorSessionId, cwd) {
829
+ const byProviderSessionId = findCodexSessionByProviderSessionId(monitorSessionId);
830
+ if (byProviderSessionId) return byProviderSessionId;
831
+ if (cwd) return findCodexSessionByCwd(cwd);
832
+ return void 0;
833
+ }
834
+ function updateSession(sessionId, patch) {
835
+ const session = getSession(sessionId);
836
+ if (!session) return;
837
+ Object.assign(session, patch);
838
+ debouncedSave();
839
+ }
840
+ function setStatusCardBinding(sessionId, binding) {
841
+ const session = getSession(sessionId);
842
+ if (!session) return;
843
+ session.statusCardMessageId = binding.messageId;
844
+ debouncedSave();
845
+ }
846
+ function setCurrentInteractionMessage(sessionId, messageId) {
847
+ const session = getSession(sessionId);
848
+ if (!session) return;
849
+ session.currentInteractionMessageId = messageId;
850
+ debouncedSave();
851
+ }
852
+ async function endSession(id) {
853
+ const session = getSession(id);
854
+ if (!session) throw new Error(`Session "${id}" not found`);
855
+ const controller = sessionControllers.get(session.id);
856
+ if (controller && session.isGenerating) {
857
+ controller.abort();
858
+ }
859
+ sessionControllers.delete(session.id);
860
+ sessionAbortReasons.delete(session.id);
861
+ idToChannelId.delete(session.id);
862
+ sessions.delete(session.channelId);
863
+ const categorySet = sessionsByCategory.get(session.categoryId);
864
+ if (categorySet) {
865
+ categorySet.delete(session.channelId);
866
+ if (categorySet.size === 0) {
867
+ sessionsByCategory.delete(session.categoryId);
868
+ }
869
+ }
870
+ await saveSessionsImmediate();
871
+ }
872
+ function setMode(sessionId, mode) {
873
+ const session = getSession(sessionId);
874
+ if (session) {
875
+ session.mode = mode;
876
+ if (mode === "monitor") {
877
+ session.monitorProviderSessionId = void 0;
878
+ }
879
+ session.workflowState = createDefaultWorkflowState();
880
+ debouncedSave();
881
+ }
882
+ }
883
+ function setVerbose(sessionId, verbose) {
884
+ const session = getSession(sessionId);
885
+ if (session) {
886
+ session.verbose = verbose;
887
+ debouncedSave();
888
+ }
889
+ }
890
+ function setModel(sessionId, model) {
891
+ const session = getSession(sessionId);
892
+ if (session) {
893
+ session.model = model;
894
+ debouncedSave();
895
+ }
896
+ }
897
+ function setAgentPersona(sessionId, persona) {
898
+ const session = getSession(sessionId);
899
+ if (session) {
900
+ session.agentPersona = persona;
901
+ debouncedSave();
902
+ }
903
+ }
904
+ function setMonitorGoal(sessionId, goal) {
905
+ const session = getSession(sessionId);
906
+ if (session) {
907
+ session.monitorGoal = goal;
908
+ if (!goal) {
909
+ session.monitorProviderSessionId = void 0;
910
+ }
911
+ session.workflowState = createDefaultWorkflowState();
912
+ debouncedSave();
913
+ }
914
+ }
915
+ function updateWorkflowState(sessionId, patch) {
916
+ const session = getSession(sessionId);
917
+ if (!session) return;
918
+ const next = typeof patch === "function" ? patch(session.workflowState) : { ...session.workflowState, ...patch };
919
+ session.workflowState = {
920
+ ...next,
921
+ updatedAt: Date.now()
922
+ };
923
+ debouncedSave();
924
+ }
925
+ function resetWorkflowState(sessionId) {
926
+ const session = getSession(sessionId);
927
+ if (!session) return;
928
+ session.workflowState = createDefaultWorkflowState();
929
+ debouncedSave();
930
+ }
931
+ function buildSystemPromptParts(session) {
932
+ const parts = [];
933
+ const personality = getPersonality(session.categoryId);
934
+ if (personality) parts.push(personality);
935
+ if (session.agentPersona) {
936
+ const agent = getAgent(session.agentPersona);
937
+ if (agent?.systemPrompt) parts.push(agent.systemPrompt);
938
+ }
939
+ const modePrompt = MODE_PROMPTS[session.mode];
940
+ if (modePrompt) parts.push(modePrompt);
941
+ return parts;
942
+ }
943
+ function buildMonitorSystemPromptParts(session) {
944
+ const parts = [];
945
+ const personality = getPersonality(session.categoryId);
946
+ if (personality) parts.push(personality);
947
+ parts.push(MONITOR_SYSTEM_PROMPT);
948
+ return parts;
949
+ }
950
+ function buildProviderOptions(session, controller, isMonitor = false, runtimeOverrides = {}) {
951
+ const isAutoMode = session.mode === "auto";
952
+ return {
953
+ directory: session.directory,
954
+ providerSessionId: isMonitor ? session.monitorProviderSessionId : session.providerSessionId,
955
+ model: session.model,
956
+ sandboxMode: config.codexSandboxMode,
957
+ approvalPolicy: isAutoMode ? "never" : config.codexApprovalPolicy,
958
+ networkAccessEnabled: config.codexNetworkAccessEnabled,
959
+ webSearchMode: config.codexWebSearchMode,
960
+ modelReasoningEffort: config.codexReasoningEffort || void 0,
961
+ claudePermissionMode: isAutoMode ? "bypass" : session.claudePermissionMode ?? config.claudePermissionMode,
962
+ systemPromptParts: isMonitor ? buildMonitorSystemPromptParts(session) : buildSystemPromptParts(session),
963
+ abortController: controller,
964
+ canUseTool: runtimeOverrides.canUseTool
965
+ };
966
+ }
967
+ async function* sendPrompt(sessionId, prompt, runtimeOverrides = {}) {
968
+ const session = getSession(sessionId);
969
+ if (!session) throw new Error(`Session "${sessionId}" not found`);
970
+ if (session.isGenerating) throw new Error("Session is already generating");
971
+ const controller = new AbortController();
972
+ sessionControllers.set(session.id, controller);
973
+ session.isGenerating = true;
974
+ session.lastActivity = Date.now();
975
+ const provider = await ensureProvider(session.provider);
976
+ try {
977
+ const stream = provider.sendPrompt(prompt, buildProviderOptions(session, controller, false, runtimeOverrides));
978
+ for await (const event of stream) {
979
+ if (event.type === "session_init") {
980
+ session.providerSessionId = event.providerSessionId || void 0;
981
+ debouncedSave();
982
+ }
983
+ if (event.type === "result") {
984
+ session.totalCost += event.costUsd;
985
+ }
986
+ yield event;
987
+ }
988
+ session.messageCount++;
989
+ } catch (err) {
990
+ if (isAbortError(err)) {
991
+ } else {
992
+ throw err;
993
+ }
994
+ } finally {
995
+ session.isGenerating = false;
996
+ session.lastActivity = Date.now();
997
+ sessionControllers.delete(session.id);
998
+ await saveSessionsImmediate();
999
+ }
1000
+ }
1001
+ async function* continueSession(sessionId) {
1002
+ yield* continueSessionWithOverrides(sessionId);
1003
+ }
1004
+ async function* continueSessionWithOverrides(sessionId, runtimeOverrides = {}) {
1005
+ const session = getSession(sessionId);
1006
+ if (!session) throw new Error(`Session "${sessionId}" not found`);
1007
+ if (session.isGenerating) throw new Error("Session is already generating");
1008
+ const controller = new AbortController();
1009
+ sessionControllers.set(session.id, controller);
1010
+ session.isGenerating = true;
1011
+ session.lastActivity = Date.now();
1012
+ const provider = await ensureProvider(session.provider);
1013
+ try {
1014
+ const stream = provider.continueSession(
1015
+ buildProviderOptions(session, controller, false, runtimeOverrides)
1016
+ );
1017
+ for await (const event of stream) {
1018
+ if (event.type === "session_init") {
1019
+ session.providerSessionId = event.providerSessionId || void 0;
1020
+ debouncedSave();
1021
+ }
1022
+ if (event.type === "result") {
1023
+ session.totalCost += event.costUsd;
1024
+ }
1025
+ yield event;
1026
+ }
1027
+ session.messageCount++;
1028
+ } catch (err) {
1029
+ if (isAbortError(err)) {
1030
+ } else {
1031
+ throw err;
1032
+ }
1033
+ } finally {
1034
+ session.isGenerating = false;
1035
+ session.lastActivity = Date.now();
1036
+ sessionControllers.delete(session.id);
1037
+ await saveSessionsImmediate();
1038
+ }
1039
+ }
1040
+ async function* sendMonitorPrompt(sessionId, prompt) {
1041
+ const session = getSession(sessionId);
1042
+ if (!session) throw new Error(`Session "${sessionId}" not found`);
1043
+ const provider = await ensureProvider(session.provider);
1044
+ session.lastActivity = Date.now();
1045
+ const controller = new AbortController();
1046
+ const stream = provider.sendPrompt(prompt, buildProviderOptions(session, controller, true));
1047
+ for await (const event of stream) {
1048
+ if (event.type === "session_init") {
1049
+ session.monitorProviderSessionId = event.providerSessionId || void 0;
1050
+ debouncedSave();
1051
+ }
1052
+ if (event.type === "result") {
1053
+ session.totalCost += event.costUsd;
1054
+ }
1055
+ yield event;
1056
+ }
1057
+ session.lastActivity = Date.now();
1058
+ debouncedSave();
1059
+ }
1060
+ function abortSession(sessionId) {
1061
+ return abortSessionWithReason(sessionId, "user");
1062
+ }
1063
+ function abortSessionWithReason(sessionId, reason) {
1064
+ const session = getSession(sessionId);
1065
+ if (!session) return false;
1066
+ const controller = sessionControllers.get(session.id);
1067
+ sessionAbortReasons.set(session.id, reason);
1068
+ if (controller) {
1069
+ controller.abort();
1070
+ }
1071
+ if (session.isGenerating) {
1072
+ session.isGenerating = false;
1073
+ sessionControllers.delete(session.id);
1074
+ debouncedSave();
1075
+ return true;
1076
+ }
1077
+ return !!controller;
1078
+ }
1079
+ function consumeAbortReason(sessionId) {
1080
+ const session = getSession(sessionId);
1081
+ if (!session) return void 0;
1082
+ const reason = sessionAbortReasons.get(session.id);
1083
+ sessionAbortReasons.delete(session.id);
1084
+ return reason;
1085
+ }
1086
+ function updateLocalObservation(sessionId, patch) {
1087
+ const observationPatch = {
1088
+ discoverySource: patch.discoverySource,
1089
+ lastObservedAt: Date.now(),
1090
+ lastObservedCwd: resolvePath(patch.cwd)
1091
+ };
1092
+ if (patch.remoteHumanControl !== void 0) {
1093
+ observationPatch.remoteHumanControl = patch.remoteHumanControl;
1094
+ }
1095
+ updateSession(sessionId, observationPatch);
1096
+ }
1097
+ async function registerLocalSession(params, guild) {
1098
+ const { provider, providerSessionId, cwd, discoverySource, labelHint, remoteHumanControl } = params;
1099
+ const existing = getSessionByProviderSessionId(provider, providerSessionId);
1100
+ if (existing) {
1101
+ updateLocalObservation(existing.id, {
1102
+ discoverySource,
1103
+ cwd,
1104
+ remoteHumanControl
1105
+ });
1106
+ return { session: existing, isNewlyCreated: false };
1107
+ }
1108
+ const { getProjectByPath, getAllRegisteredProjects: getAllRegisteredProjects2 } = await import("./project-registry-DQT5ORUU.js");
1109
+ const { resolvePath: resolvePath2 } = await import("./utils-72GMT2X5.js");
1110
+ const normalizedCwd = resolvePath2(cwd);
1111
+ let project = getProjectByPath(normalizedCwd);
1112
+ if (!project) {
1113
+ const allProjects = getAllRegisteredProjects2();
1114
+ let bestMatch;
1115
+ let bestMatchPathLength = -1;
1116
+ for (const p of allProjects) {
1117
+ const projectPath = resolvePath2(p.path);
1118
+ if (normalizedCwd.startsWith(projectPath + sep) && projectPath.length > bestMatchPathLength) {
1119
+ bestMatch = p;
1120
+ bestMatchPathLength = projectPath.length;
1121
+ }
1122
+ }
1123
+ project = bestMatch;
1124
+ }
1125
+ if (!project || !project.discordCategoryId) {
1126
+ console.warn(
1127
+ `[registerLocalSession] Cannot register ${provider} session ${providerSessionId}: cwd "${cwd}" does not belong to any mounted project`
1128
+ );
1129
+ return null;
1130
+ }
1131
+ const { ChannelType } = await import("discord.js");
1132
+ const category = guild.channels.cache.get(project.discordCategoryId);
1133
+ if (!category || category.type !== ChannelType.GuildCategory) {
1134
+ console.warn(
1135
+ `[registerLocalSession] Cannot register ${provider} session ${providerSessionId}: category ${project.discordCategoryId} not found`
1136
+ );
1137
+ return null;
1138
+ }
1139
+ const base = labelHint ? labelHint.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 60) : providerSessionId.slice(0, 12);
1140
+ const channelName = `${provider}-${base}`.slice(0, 100);
1141
+ let channel = category.children.cache.find(
1142
+ (ch) => ch.type === ChannelType.GuildText && typeof ch.topic === "string" && ch.topic.includes(`Provider Session: ${providerSessionId}`)
1143
+ );
1144
+ if (!channel) {
1145
+ channel = await guild.channels.create({
1146
+ name: channelName,
1147
+ type: ChannelType.GuildText,
1148
+ parent: category.id,
1149
+ topic: `${provider} session (local) | Provider Session: ${providerSessionId}`
1150
+ });
1151
+ }
1152
+ const session = await createSession({
1153
+ channelId: channel.id,
1154
+ categoryId: project.discordCategoryId,
1155
+ projectName: project.name,
1156
+ agentLabel: labelHint || providerSessionId.slice(0, 12),
1157
+ provider,
1158
+ providerSessionId,
1159
+ directory: normalizedCwd,
1160
+ type: "persistent",
1161
+ discoverySource,
1162
+ remoteHumanControl: remoteHumanControl ?? false
1163
+ });
1164
+ updateLocalObservation(session.id, {
1165
+ discoverySource,
1166
+ cwd: normalizedCwd,
1167
+ remoteHumanControl: remoteHumanControl ?? false
1168
+ });
1169
+ console.log(
1170
+ `[registerLocalSession] Registered ${provider} session ${providerSessionId} (source: ${discoverySource}, channel: ${channel.id})`
1171
+ );
1172
+ return { session, isNewlyCreated: true };
1173
+ }
1174
+
1175
+ export {
1176
+ loadProjects,
1177
+ getProject,
1178
+ bindMountedProjectToCategory,
1179
+ setHistoryChannelId,
1180
+ setControlChannelId,
1181
+ setPersonality,
1182
+ clearPersonality,
1183
+ addSkill,
1184
+ removeSkill,
1185
+ getSkills,
1186
+ executeSkill,
1187
+ addMcpServer,
1188
+ removeMcpServer,
1189
+ getMcpServers,
1190
+ loadSessions,
1191
+ createSession,
1192
+ getSession,
1193
+ getSessionByChannel,
1194
+ getSessionByThread,
1195
+ getSessionByCodexId,
1196
+ getSessionByProviderSession,
1197
+ getSessionsByCategory,
1198
+ getAllSessions,
1199
+ getSessionByProviderSessionId,
1200
+ findCodexSessionForMonitor,
1201
+ findCodexSessionByProviderSessionId,
1202
+ findCodexSessionByCwd,
1203
+ resolveCodexSessionFromMonitor,
1204
+ updateSession,
1205
+ setStatusCardBinding,
1206
+ setCurrentInteractionMessage,
1207
+ endSession,
1208
+ setMode,
1209
+ setVerbose,
1210
+ setModel,
1211
+ setAgentPersona,
1212
+ setMonitorGoal,
1213
+ updateWorkflowState,
1214
+ resetWorkflowState,
1215
+ sendPrompt,
1216
+ continueSession,
1217
+ continueSessionWithOverrides,
1218
+ sendMonitorPrompt,
1219
+ abortSession,
1220
+ abortSessionWithReason,
1221
+ consumeAbortReason,
1222
+ updateLocalObservation,
1223
+ registerLocalSession
1224
+ };