@intent-systems/nexus 2026.1.5-4 → 2026.1.5-5

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 (123) hide show
  1. package/dist/agents/agent-id.js +41 -0
  2. package/dist/agents/auth-profiles.js +114 -25
  3. package/dist/agents/identity-state.js +79 -0
  4. package/dist/agents/model-auth.js +1 -0
  5. package/dist/agents/model-fallback.js +15 -9
  6. package/dist/agents/model-selection.js +1 -1
  7. package/dist/agents/models-config.js +17 -11
  8. package/dist/agents/pi-embedded-runner.js +101 -9
  9. package/dist/agents/sandbox.js +12 -3
  10. package/dist/agents/skill-runner.js +29 -4
  11. package/dist/agents/skill-usage.js +114 -11
  12. package/dist/agents/skills-status.js +4 -4
  13. package/dist/agents/skills.js +18 -7
  14. package/dist/agents/subagent-registry.js +25 -11
  15. package/dist/agents/system-prompt.js +16 -0
  16. package/dist/agents/tool-policy.js +19 -3
  17. package/dist/agents/tools/browser-tool.js +5 -2
  18. package/dist/agents/tools/image-tool.js +93 -8
  19. package/dist/agents/tools/sessions-announce-target.js +5 -1
  20. package/dist/agents/workspace.js +55 -46
  21. package/dist/auto-reply/command-detection.js +2 -1
  22. package/dist/auto-reply/reply/directive-handling.js +153 -28
  23. package/dist/auto-reply/reply/directives.js +17 -2
  24. package/dist/auto-reply/reply/model-selection.js +8 -3
  25. package/dist/auto-reply/reply/queue.js +2 -2
  26. package/dist/auto-reply/reply.js +1 -1
  27. package/dist/auto-reply/thinking.js +15 -0
  28. package/dist/browser/chrome.js +1 -1
  29. package/dist/browser/client.js +2 -0
  30. package/dist/browser/config.js +6 -2
  31. package/dist/browser/pw-tools-core.js +3 -0
  32. package/dist/browser/routes/agent.js +14 -0
  33. package/dist/canvas-host/server.js +1 -1
  34. package/dist/capabilities/detector.js +46 -15
  35. package/dist/capabilities/registry.js +2 -1
  36. package/dist/cli/cloud-cli.js +12 -7
  37. package/dist/cli/credential-cli.js +139 -17
  38. package/dist/cli/gateway-cli.js +1 -1
  39. package/dist/cli/log-cli.js +25 -0
  40. package/dist/cli/pairing-cli.js +1 -1
  41. package/dist/cli/program.js +58 -6
  42. package/dist/cli/run-main.js +1 -1
  43. package/dist/cli/skills-cli.js +144 -21
  44. package/dist/cli/skills-hub-cli.js +59 -29
  45. package/dist/cli/tool-connector-cli.js +99 -24
  46. package/dist/cli/upstream-sync-cli.js +253 -96
  47. package/dist/cli/usage-cli.js +14 -0
  48. package/dist/commands/auth-choice-options.js +6 -1
  49. package/dist/commands/auth-choice.js +157 -5
  50. package/dist/commands/bootstrap-preset.js +10 -6
  51. package/dist/commands/capabilities.js +33 -6
  52. package/dist/commands/claude-md.js +3 -2
  53. package/dist/commands/config-view.js +1 -1
  54. package/dist/commands/configure.js +4 -4
  55. package/dist/commands/credential.js +497 -36
  56. package/dist/commands/cursor-rules.js +39 -19
  57. package/dist/commands/doctor.js +5 -4
  58. package/dist/commands/identity.js +28 -31
  59. package/dist/commands/init.js +15 -18
  60. package/dist/commands/log.js +134 -0
  61. package/dist/commands/models/fallbacks.js +1 -1
  62. package/dist/commands/models/image-fallbacks.js +1 -1
  63. package/dist/commands/models/list.js +1 -1
  64. package/dist/commands/models/scan.js +1 -1
  65. package/dist/commands/onboard-auth.js +27 -2
  66. package/dist/commands/onboard-eve-identity.js +7 -8
  67. package/dist/commands/onboard-non-interactive.js +4 -2
  68. package/dist/commands/onboard-quickstart.js +18 -11
  69. package/dist/commands/quest-state.js +271 -0
  70. package/dist/commands/quest.js +53 -13
  71. package/dist/commands/reset.js +1 -1
  72. package/dist/commands/sessions-ingest.js +5 -4
  73. package/dist/commands/setup.js +4 -2
  74. package/dist/commands/skills-manifest.js +2 -2
  75. package/dist/commands/status.js +179 -61
  76. package/dist/commands/suggestions.js +1 -1
  77. package/dist/commands/usage-tracking.js +32 -0
  78. package/dist/commands/usage-upload.js +6 -1
  79. package/dist/config/defaults.js +1 -3
  80. package/dist/config/includes.js +5 -7
  81. package/dist/config/io.js +88 -16
  82. package/dist/config/legacy.js +4 -2
  83. package/dist/config/paths.js +16 -0
  84. package/dist/config/sessions.js +9 -5
  85. package/dist/config/zod-schema.js +4 -3
  86. package/dist/control-plane/broker/broker.js +131 -78
  87. package/dist/control-plane/compaction.js +3 -5
  88. package/dist/control-plane/factory.js +2 -2
  89. package/dist/control-plane/index.js +2 -2
  90. package/dist/control-plane/odu/agents.js +28 -23
  91. package/dist/control-plane/odu/interaction-tools.js +62 -50
  92. package/dist/control-plane/odu/prompt-loader.js +8 -8
  93. package/dist/control-plane/odu/runtime.js +87 -75
  94. package/dist/control-plane/odu-control-plane.js +14 -12
  95. package/dist/control-plane/single-agent.js +13 -13
  96. package/dist/credentials/store.js +133 -7
  97. package/dist/gateway/server-browser.js +5 -4
  98. package/dist/gateway/server-methods/cron.js +11 -1
  99. package/dist/gateway/server.js +14 -7
  100. package/dist/infra/bonjour.js +1 -1
  101. package/dist/infra/event-log.js +8 -2
  102. package/dist/infra/path-env.js +1 -2
  103. package/dist/infra/provider-usage.auth.js +5 -3
  104. package/dist/infra/provider-usage.fetch.claude.js +16 -6
  105. package/dist/infra/provider-usage.fetch.minimax.js +8 -3
  106. package/dist/infra/provider-usage.js +9 -5
  107. package/dist/infra/restart.js +2 -2
  108. package/dist/infra/usage-settings.js +78 -0
  109. package/dist/infra/usage-suggestions.js +17 -5
  110. package/dist/infra/usage-upload.js +38 -1
  111. package/dist/infra/voicewake.js +2 -2
  112. package/dist/media/image-ops.js +3 -1
  113. package/dist/memory/index.js +2 -381
  114. package/dist/pairing/pairing-store.js +24 -0
  115. package/dist/providers/github-copilot-auth.js +1 -1
  116. package/dist/routing/resolve-route.js +6 -6
  117. package/dist/routing/session-key.js +3 -1
  118. package/dist/sessions/send-policy.js +5 -5
  119. package/dist/slack/monitor.js +22 -1
  120. package/dist/telegram/reaction-level.js +2 -1
  121. package/dist/utils.js +4 -3
  122. package/dist/wizard/onboarding.js +29 -7
  123. package/package.json +1 -1
@@ -4,13 +4,13 @@
4
4
  * Convention-driven agent runtime that works for any ODU.
5
5
  * Adapted from magic-toolbox for Nexus.
6
6
  */
7
- import path from 'node:path';
8
- import { InteractionAgent, ExecutionAgent } from './agents.js';
9
- import { loadHistory, appendToHistory, } from '../../config/sessions.js';
10
- import { resolveUserPath } from '../../utils.js';
11
- import { DEFAULT_AGENT_ID } from '../../routing/session-key.js';
12
- import { loadPrompt } from './prompt-loader.js';
13
- import { DEFAULT_MODEL } from '../../agents/defaults.js';
7
+ import path from "node:path";
8
+ import { DEFAULT_MODEL } from "../../agents/defaults.js";
9
+ import { appendToHistory, loadHistory, } from "../../config/sessions.js";
10
+ import { DEFAULT_AGENT_ID } from "../../routing/session-key.js";
11
+ import { resolveUserPath } from "../../utils.js";
12
+ import { ExecutionAgent, InteractionAgent } from "./agents.js";
13
+ import { loadPrompt } from "./prompt-loader.js";
14
14
  /**
15
15
  * Unified Interaction Agent Runtime
16
16
  *
@@ -20,16 +20,21 @@ import { DEFAULT_MODEL } from '../../agents/defaults.js';
20
20
  export class ODUInteractionAgent extends InteractionAgent {
21
21
  // Static registry of all ODU IA singletons (fractal architecture support)
22
22
  static instances = new Map();
23
- conversationHistory = [];
24
- broker;
25
- constructor(config) {
26
- // Singleton key: oduPath + userId (supports nested ODUs)
23
+ static getOrCreate(config) {
27
24
  const key = `${config.oduPath}:${config.userId}`;
28
- // Return existing instance if already created
29
25
  const existing = ODUInteractionAgent.instances.get(key);
30
26
  if (existing) {
31
27
  return existing;
32
28
  }
29
+ const instance = new ODUInteractionAgent(config);
30
+ ODUInteractionAgent.instances.set(key, instance);
31
+ return instance;
32
+ }
33
+ conversationHistory = [];
34
+ broker;
35
+ constructor(config) {
36
+ // Singleton key: oduPath + userId (supports nested ODUs)
37
+ const key = `${config.oduPath}:${config.userId}`;
33
38
  // Create new instance
34
39
  super(config);
35
40
  this.oduConfig = config.oduConfig;
@@ -40,7 +45,7 @@ export class ODUInteractionAgent extends InteractionAgent {
40
45
  // (constructors can't be async, so we do this in the background)
41
46
  if (config.sessionId) {
42
47
  this.loadConversationHistory(config.sessionId).catch((error) => {
43
- this.log.warn('Failed to load conversation history', { error });
48
+ this.log.warn("Failed to load conversation history", { error });
44
49
  });
45
50
  }
46
51
  // Register ODU with broker
@@ -61,11 +66,11 @@ export class ODUInteractionAgent extends InteractionAgent {
61
66
  });
62
67
  };
63
68
  // Register ODU (EA factory) with broker
64
- this.broker.registerODU(this.oduConfig.name, path.join(resolveUserPath(this.oduPath), '../state'), agentFactory);
69
+ this.broker.registerODU(this.oduConfig.name, path.join(resolveUserPath(this.oduPath), "../state"), agentFactory);
65
70
  // Register THIS IA with broker so it can receive messages
66
71
  const iaId = `${this.oduConfig.name}-ia`;
67
72
  this.broker.registerIA(iaId, this);
68
- this.log.info('IA registered with broker', { iaId });
73
+ this.log.info("IA registered with broker", { iaId });
69
74
  }
70
75
  /**
71
76
  * Resolve the model to use for IA from config
@@ -77,12 +82,12 @@ export class ODUInteractionAgent extends InteractionAgent {
77
82
  // Check ODU-specific IA model first
78
83
  if (oduConfig?.iaModel?.primary) {
79
84
  // Extract just the model name from provider/model format
80
- const parts = oduConfig.iaModel.primary.split('/');
85
+ const parts = oduConfig.iaModel.primary.split("/");
81
86
  return parts.length > 1 ? parts[1] : parts[0];
82
87
  }
83
88
  // Fall back to agent.model
84
89
  if (agentConfig?.model?.primary) {
85
- const parts = agentConfig.model.primary.split('/');
90
+ const parts = agentConfig.model.primary.split("/");
86
91
  return parts.length > 1 ? parts[1] : parts[0];
87
92
  }
88
93
  // Default to opus
@@ -101,19 +106,20 @@ export class ODUInteractionAgent extends InteractionAgent {
101
106
  if (history && history.length > 0) {
102
107
  // Convert HistoryTurn[] to conversation history format
103
108
  this.conversationHistory = history.map((turn) => ({
109
+ turn_id: turn.turn_id,
104
110
  role: turn.role,
105
111
  content: turn.content,
106
112
  tool_calls: turn.tool_calls,
107
113
  tool_call_id: turn.tool_call_id,
108
114
  timestamp: turn.timestamp,
109
115
  }));
110
- this.log.info('Loaded conversation history', {
116
+ this.log.info("Loaded conversation history", {
111
117
  turns: this.conversationHistory.length,
112
118
  });
113
119
  }
114
120
  }
115
121
  catch (error) {
116
- this.log.error('Failed to load conversation history', { error });
122
+ this.log.error("Failed to load conversation history", { error });
117
123
  }
118
124
  }
119
125
  /**
@@ -122,7 +128,7 @@ export class ODUInteractionAgent extends InteractionAgent {
122
128
  async chatSync(userMessage) {
123
129
  // ALWAYS interrupt if currently streaming
124
130
  if (this.isStreaming) {
125
- this.log.info('New message received while streaming, interrupting');
131
+ this.log.info("New message received while streaming, interrupting");
126
132
  this.interrupt();
127
133
  // Wait a tiny bit for stream to stop
128
134
  await new Promise((resolve) => setTimeout(resolve, 50));
@@ -131,11 +137,11 @@ export class ODUInteractionAgent extends InteractionAgent {
131
137
  this.queueMessage(userMessage);
132
138
  // If already processing (but not streaming), return queued message
133
139
  if (this.isProcessing && !this.isStreaming) {
134
- return 'Message queued. Processing...';
140
+ return "Message queued. Processing...";
135
141
  }
136
142
  this.isProcessing = true;
137
143
  try {
138
- let finalResponse = '';
144
+ let finalResponse = "";
139
145
  // Process all queued messages
140
146
  while (this.messageQueue.length > 0) {
141
147
  // Dequeue all current messages
@@ -154,7 +160,9 @@ export class ODUInteractionAgent extends InteractionAgent {
154
160
  // Combine messages into single prompt
155
161
  const combinedMessage = messages.length === 1
156
162
  ? messages[0].content
157
- : messages.map((m, i) => `Message ${i + 1}:\n${m.content}`).join('\n\n---\n\n');
163
+ : messages
164
+ .map((m, i) => `Message ${i + 1}:\n${m.content}`)
165
+ .join("\n\n---\n\n");
158
166
  // Reset interrupt flag for new processing
159
167
  this.shouldInterrupt = false;
160
168
  // Process the combined message with sender context (with streaming + interrupt support)
@@ -174,20 +182,20 @@ export class ODUInteractionAgent extends InteractionAgent {
174
182
  */
175
183
  async processSingleMessage(userMessage, from) {
176
184
  // If message from another agent, prepend context so IA knows who to respond to
177
- if (from && from !== 'user' && from !== 'system') {
185
+ if (from && from !== "user" && from !== "system") {
178
186
  userMessage = `[Message from agent: ${from}]\n\n${userMessage}`;
179
187
  }
180
- this.log.info('Processing message', {
188
+ this.log.info("Processing message", {
181
189
  messageLength: userMessage.length,
182
190
  from,
183
191
  });
184
192
  // Import dependencies dynamically
185
- const { default: Anthropic } = await import('@anthropic-ai/sdk');
186
- const { createInteractionTools, handleToolInvocation } = await import('./interaction-tools.js');
193
+ const { default: Anthropic } = await import("@anthropic-ai/sdk");
194
+ const { createInteractionTools, handleToolInvocation } = await import("./interaction-tools.js");
187
195
  // Get API key from environment
188
196
  const apiKey = process.env.ANTHROPIC_API_KEY;
189
197
  if (!apiKey) {
190
- throw new Error('ANTHROPIC_API_KEY environment variable is required for ODU interaction agent');
198
+ throw new Error("ANTHROPIC_API_KEY environment variable is required for ODU interaction agent");
191
199
  }
192
200
  // Create Anthropic client
193
201
  const client = new Anthropic({ apiKey });
@@ -207,35 +215,32 @@ export class ODUInteractionAgent extends InteractionAgent {
207
215
  const messages = [];
208
216
  // Add conversation history
209
217
  for (const turn of this.conversationHistory) {
210
- if (turn.role === 'user') {
211
- messages.push({ role: 'user', content: turn.content });
218
+ if (turn.role === "user") {
219
+ messages.push({ role: "user", content: turn.content });
212
220
  }
213
- else if (turn.role === 'assistant') {
221
+ else if (turn.role === "assistant") {
214
222
  // Build assistant message with tool calls if present
215
223
  const content = [];
216
224
  if (turn.content) {
217
- content.push({ type: 'text', text: turn.content });
225
+ content.push({ type: "text", text: turn.content });
218
226
  }
219
227
  if (turn.tool_calls && turn.tool_calls.length > 0) {
220
228
  for (const toolCall of turn.tool_calls) {
221
229
  content.push({
222
- type: 'tool_use',
230
+ type: "tool_use",
223
231
  id: toolCall.id,
224
232
  name: toolCall.function.name,
225
233
  input: JSON.parse(toolCall.function.arguments),
226
234
  });
227
235
  }
228
236
  }
229
- messages.push({ role: 'assistant', content });
237
+ messages.push({ role: "assistant", content });
230
238
  }
231
- else if (turn.role === 'tool') {
232
- // Tool results must be in the previous assistant message's content
233
- // For now, we'll skip them as they should already be recorded
234
- continue;
239
+ else if (turn.role === "tool") {
235
240
  }
236
241
  }
237
242
  // Add current user message
238
- messages.push({ role: 'user', content: userMessage });
243
+ messages.push({ role: "user", content: userMessage });
239
244
  // Resolve model from config (IA uses controlPlane.odu.iaModel or agent.model)
240
245
  const iaModel = this.resolveIAModel();
241
246
  // Call Claude API with tool support
@@ -243,23 +248,24 @@ export class ODUInteractionAgent extends InteractionAgent {
243
248
  model: iaModel,
244
249
  max_tokens: 4096,
245
250
  system: systemPrompt,
246
- messages,
251
+ messages: messages,
247
252
  tools,
248
253
  });
249
254
  // Handle tool calls in a loop (agent may make multiple tool calls)
250
255
  let iterationCount = 0;
251
256
  const maxIterations = 10; // Prevent infinite loops
252
- while (response.stop_reason === 'tool_use' && iterationCount < maxIterations) {
257
+ while (response.stop_reason === "tool_use" &&
258
+ iterationCount < maxIterations) {
253
259
  iterationCount++;
254
260
  // Extract tool calls from response
255
- const toolUseBlocks = response.content.filter((block) => block.type === 'tool_use');
261
+ const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
256
262
  // Execute each tool and collect results
257
263
  const toolResults = [];
258
264
  for (const toolBlock of toolUseBlocks) {
259
265
  try {
260
266
  const result = await handleToolInvocation(toolBlock.name, toolBlock.input, toolsContext);
261
267
  toolResults.push({
262
- type: 'tool_result',
268
+ type: "tool_result",
263
269
  tool_use_id: toolBlock.id,
264
270
  content: result,
265
271
  });
@@ -267,7 +273,7 @@ export class ODUInteractionAgent extends InteractionAgent {
267
273
  catch (error) {
268
274
  const errorMessage = error instanceof Error ? error.message : String(error);
269
275
  toolResults.push({
270
- type: 'tool_result',
276
+ type: "tool_result",
271
277
  tool_use_id: toolBlock.id,
272
278
  content: `Error: ${errorMessage}`,
273
279
  is_error: true,
@@ -275,31 +281,31 @@ export class ODUInteractionAgent extends InteractionAgent {
275
281
  }
276
282
  }
277
283
  // Add assistant response to messages
278
- messages.push({ role: 'assistant', content: response.content });
284
+ messages.push({ role: "assistant", content: response.content });
279
285
  // Add tool results as user message
280
- messages.push({ role: 'user', content: toolResults });
286
+ messages.push({ role: "user", content: toolResults });
281
287
  // Continue conversation
282
288
  response = await client.messages.create({
283
- model: 'claude-sonnet-4-20250514',
289
+ model: "claude-sonnet-4-20250514",
284
290
  max_tokens: 4096,
285
291
  system: systemPrompt,
286
- messages,
292
+ messages: messages,
287
293
  tools,
288
294
  });
289
295
  }
290
296
  // Extract final text response
291
- const textBlocks = response.content.filter((block) => block.type === 'text');
292
- const finalResponse = textBlocks.map((block) => block.text).join('\n');
297
+ const textBlocks = response.content.filter((block) => block.type === "text");
298
+ const finalResponse = textBlocks.map((block) => block.text).join("\n");
293
299
  // Save conversation history (both in-memory and to disk)
294
300
  const userTurn = {
295
301
  turn_id: `turn-${Date.now()}-user`,
296
- role: 'user',
302
+ role: "user",
297
303
  content: userMessage,
298
304
  timestamp: new Date().toISOString(),
299
305
  };
300
306
  const assistantTurn = {
301
307
  turn_id: `turn-${Date.now()}-assistant`,
302
- role: 'assistant',
308
+ role: "assistant",
303
309
  content: finalResponse,
304
310
  timestamp: new Date().toISOString(),
305
311
  };
@@ -312,10 +318,13 @@ export class ODUInteractionAgent extends InteractionAgent {
312
318
  try {
313
319
  await appendToHistory(agentId, sessionKey, userTurn);
314
320
  await appendToHistory(agentId, sessionKey, assistantTurn);
315
- this.log.debug('Persisted conversation to history.jsonl', { sessionKey });
321
+ this.log.debug("Persisted conversation to history.jsonl", { sessionKey });
316
322
  }
317
323
  catch (error) {
318
- this.log.warn('Failed to persist conversation history', { error, sessionKey });
324
+ this.log.warn("Failed to persist conversation history", {
325
+ error,
326
+ sessionKey,
327
+ });
319
328
  }
320
329
  return finalResponse;
321
330
  }
@@ -324,7 +333,7 @@ export class ODUInteractionAgent extends InteractionAgent {
324
333
  */
325
334
  async buildSystemPrompt() {
326
335
  // Load InteractionAgent.md prompt with template variables
327
- const prompt = await loadPrompt('InteractionAgent.md', {
336
+ const prompt = await loadPrompt("InteractionAgent.md", {
328
337
  oduName: this.oduConfig.name,
329
338
  oduPurpose: this.oduConfig.purpose,
330
339
  oduPath: this.oduPath,
@@ -348,7 +357,7 @@ export class ODUExecutionAgent extends ExecutionAgent {
348
357
  }
349
358
  else {
350
359
  // Derive ODU name from path
351
- const oduName = path.basename(resolveUserPath(config.oduPath || '~/nexus/home'));
360
+ const oduName = path.basename(resolveUserPath(config.oduPath || "~/nexus/home"));
352
361
  this.oduConfig = {
353
362
  name: oduName,
354
363
  purpose: `Execute tasks for ${oduName} ODU`,
@@ -359,17 +368,17 @@ export class ODUExecutionAgent extends ExecutionAgent {
359
368
  * Execute the task using Nexus embedded agent
360
369
  */
361
370
  async execute() {
362
- this.log.info('Starting EA execution', {
371
+ this.log.info("Starting EA execution", {
363
372
  taskType: this.task.type,
364
- taskDescription: (this.task.description || '').substring(0, 200),
373
+ taskDescription: (this.task.description || "").substring(0, 200),
365
374
  historyLength: this.history.length,
366
375
  });
367
376
  try {
368
377
  // Import runEmbeddedPiAgent dynamically
369
- const { runEmbeddedPiAgent } = await import('../../agents/pi-embedded-runner.js');
370
- const { resolveNexusAgentDir } = await import('../../agents/agent-paths.js');
371
- const { randomUUID } = await import('node:crypto');
372
- const path = await import('node:path');
378
+ const { runEmbeddedPiAgent } = await import("../../agents/pi-embedded-runner.js");
379
+ const { resolveNexusAgentDir } = await import("../../agents/agent-paths.js");
380
+ const { randomUUID } = await import("node:crypto");
381
+ const path = await import("node:path");
373
382
  // Build initial task prompt
374
383
  const taskPrompt = this.buildInitialPrompt();
375
384
  // Resolve workspace directory for this EA
@@ -378,7 +387,7 @@ export class ODUExecutionAgent extends ExecutionAgent {
378
387
  const workspaceDir = resolveUserPath(this.oduPath);
379
388
  // Resolve session file path
380
389
  const agentDir = resolveNexusAgentDir();
381
- const sessionFile = path.join(workspaceDir, '.agent-session.json');
390
+ const sessionFile = path.join(workspaceDir, ".agent-session.json");
382
391
  // Build system prompt with EA template
383
392
  const systemPrompt = await this.buildSystemPrompt();
384
393
  // Run embedded pi-agent with full coding tools
@@ -400,8 +409,11 @@ export class ODUExecutionAgent extends ExecutionAgent {
400
409
  extraSystemPrompt: systemPrompt,
401
410
  });
402
411
  // Extract final response from payloads
403
- const finalResponse = result.payloads?.map((p) => p.text).filter(Boolean).join('\n\n') || 'Task completed';
404
- this.log.info('EA execution complete', {
412
+ const finalResponse = result.payloads
413
+ ?.map((p) => p.text)
414
+ .filter(Boolean)
415
+ .join("\n\n") || "Task completed";
416
+ this.log.info("EA execution complete", {
405
417
  responseLength: finalResponse.length,
406
418
  usage: result.meta.agentMeta?.usage,
407
419
  });
@@ -413,10 +425,10 @@ export class ODUExecutionAgent extends ExecutionAgent {
413
425
  from: this.agentId,
414
426
  to: iaId,
415
427
  content: finalResponse,
416
- priority: 'normal',
428
+ priority: "normal",
417
429
  timestamp: Date.now(),
418
430
  metadata: {
419
- source: 'ea',
431
+ source: "ea",
420
432
  completionResult: true,
421
433
  usage: result.meta.agentMeta?.usage,
422
434
  },
@@ -424,7 +436,7 @@ export class ODUExecutionAgent extends ExecutionAgent {
424
436
  return finalResponse;
425
437
  }
426
438
  catch (error) {
427
- this.log.error('EA execution failed', { error });
439
+ this.log.error("EA execution failed", { error });
428
440
  // Send error back to IA
429
441
  const oduName = this.oduConfig.name;
430
442
  const iaId = `${oduName}-ia`;
@@ -434,10 +446,10 @@ export class ODUExecutionAgent extends ExecutionAgent {
434
446
  from: this.agentId,
435
447
  to: iaId,
436
448
  content: `Task failed: ${errorMessage}`,
437
- priority: 'normal',
449
+ priority: "normal",
438
450
  timestamp: Date.now(),
439
451
  metadata: {
440
- source: 'ea',
452
+ source: "ea",
441
453
  completionResult: true,
442
454
  error: true,
443
455
  },
@@ -450,17 +462,17 @@ export class ODUExecutionAgent extends ExecutionAgent {
450
462
  */
451
463
  async buildSystemPrompt() {
452
464
  // Load ExecutionAgent.md prompt with template variables
453
- const prompt = await loadPrompt('ExecutionAgent.md', {
465
+ const prompt = await loadPrompt("ExecutionAgent.md", {
454
466
  oduName: this.oduConfig.name,
455
467
  oduPurpose: this.oduConfig.purpose,
456
468
  oduPath: this.oduPath,
457
- task: this.task.description || 'Execute the assigned task',
458
- taskName: this.task.taskName || 'task',
469
+ task: this.task.description || "Execute the assigned task",
470
+ taskName: this.task.taskName || "task",
459
471
  agentId: this.agentId,
460
472
  // For now, leave skills and capabilities empty
461
473
  // These will be populated when we implement skill/capability loading
462
- availableSkills: '(Skills will be listed here)',
463
- availableCapabilities: '(Capabilities will be listed here)',
474
+ availableSkills: "(Skills will be listed here)",
475
+ availableCapabilities: "(Capabilities will be listed here)",
464
476
  });
465
477
  return prompt;
466
478
  }
@@ -10,14 +10,14 @@
10
10
  * - EAs complete and respond → IA
11
11
  * - IA responds → User
12
12
  */
13
- import { loadSession, writeSessionMetadata, listAgentSessions, resolveSessionDir, } from "../config/sessions.js";
14
- import { normalizeAgentId, parseAgentSessionKey, DEFAULT_AGENT_ID, } from "../routing/session-key.js";
15
- import { ActiveMessageBroker } from "./broker/broker.js";
16
- import { ODUInteractionAgent } from "./odu/runtime.js";
17
- import { loadConfig } from "../config/config.js";
18
- import { createSubsystemLogger } from "../logging.js";
19
13
  import crypto from "node:crypto";
20
14
  import fs from "node:fs/promises";
15
+ import { loadConfig } from "../config/config.js";
16
+ import { listAgentSessions, loadSession, resolveSessionDir, writeSessionMetadata, } from "../config/sessions.js";
17
+ import { createSubsystemLogger } from "../logging.js";
18
+ import { DEFAULT_AGENT_ID, normalizeAgentId, parseAgentSessionKey, } from "../routing/session-key.js";
19
+ import { ActiveMessageBroker } from "./broker/broker.js";
20
+ import { ODUInteractionAgent } from "./odu/runtime.js";
21
21
  /**
22
22
  * ODUControlPlane implements AgentControlPlane using the broker pattern.
23
23
  *
@@ -57,7 +57,7 @@ export class ODUControlPlane {
57
57
  let ia = this.interactionAgents.get(userId);
58
58
  if (!ia) {
59
59
  this.logger.info("Creating new IA for user", { userId, sessionId });
60
- ia = new ODUInteractionAgent({
60
+ ia = ODUInteractionAgent.getOrCreate({
61
61
  userId,
62
62
  sessionId,
63
63
  oduPath: this.workspaceDir,
@@ -78,7 +78,7 @@ export class ODUControlPlane {
78
78
  return sessionKey;
79
79
  }
80
80
  async sendMessage(options) {
81
- const { sessionKey, message, agentId: optAgentId, thinkingLevel, verboseLevel, isHeartbeat, streaming, onChunk, onComplete, onError, } = options;
81
+ const { sessionKey, message, agentId: optAgentId, thinkingLevel, verboseLevel, isHeartbeat, streaming, onChunk: _onChunk, onComplete, onError, } = options;
82
82
  try {
83
83
  // Parse session key to get agentId
84
84
  const parsed = parseAgentSessionKey(sessionKey);
@@ -86,7 +86,7 @@ export class ODUControlPlane {
86
86
  // Convert session key to userId for ODU
87
87
  const userId = this.sessionKeyToUserId(sessionKey);
88
88
  // Load or create session
89
- let session = await loadSession(agentId, sessionKey);
89
+ const session = await loadSession(agentId, sessionKey);
90
90
  let sessionId;
91
91
  if (!session) {
92
92
  // Create new session
@@ -108,7 +108,7 @@ export class ODUControlPlane {
108
108
  await writeSessionMetadata(agentId, sessionKey, session.metadata);
109
109
  }
110
110
  // Get or create IA for this user
111
- const ia = this.getOrCreateIA(userId, sessionId);
111
+ const _ia = this.getOrCreateIA(userId, sessionId);
112
112
  // Build broker message
113
113
  const brokerMessage = {
114
114
  id: crypto.randomUUID(),
@@ -192,7 +192,8 @@ export class ODUControlPlane {
192
192
  return null;
193
193
  }
194
194
  // Session metadata has 'updated' (ISO string) from new format
195
- const updated = session.metadata.updated || new Date().toISOString();
195
+ const updated = session.metadata.updated ||
196
+ new Date().toISOString();
196
197
  return {
197
198
  key: sessionKey,
198
199
  sessionId: session.metadata.sessionId,
@@ -222,7 +223,8 @@ export class ODUControlPlane {
222
223
  const session = await loadSession(agentId, sessionKey);
223
224
  if (session) {
224
225
  // Session metadata has 'updated' (ISO string) from new format
225
- const updated = session.metadata.updated || new Date().toISOString();
226
+ const updated = session.metadata.updated ||
227
+ new Date().toISOString();
226
228
  sessions.push({
227
229
  key: sessionKey,
228
230
  sessionId: session.metadata.sessionId,
@@ -4,16 +4,16 @@
4
4
  * This implementation maintains the existing single-agent-per-session pattern.
5
5
  * No behavior changes - just adapting the existing code to the AgentControlPlane interface.
6
6
  */
7
- import { runEmbeddedPiAgent, } from "../agents/pi-embedded-runner.js";
8
- import { queueEmbeddedPiMessage } from "../agents/pi-embedded.js";
9
- import { loadConfig } from "../config/config.js";
10
- import { loadSession, writeSessionMetadata, listAgentSessions, resolveSessionDir, } from "../config/sessions.js";
11
- import { normalizeAgentId, parseAgentSessionKey, DEFAULT_AGENT_ID, } from "../routing/session-key.js";
12
- import path from "node:path";
13
7
  import { randomUUID } from "node:crypto";
14
- import { buildWorkspaceSkillSnapshot } from "../agents/skills.js";
15
- import { resolveNexusAgentDir } from "../agents/agent-paths.js";
16
8
  import fs from "node:fs";
9
+ import path from "node:path";
10
+ import { resolveNexusAgentDir } from "../agents/agent-paths.js";
11
+ import { queueEmbeddedPiMessage } from "../agents/pi-embedded.js";
12
+ import { runEmbeddedPiAgent, } from "../agents/pi-embedded-runner.js";
13
+ import { buildWorkspaceSkillSnapshot } from "../agents/skills.js";
14
+ import { loadConfig } from "../config/config.js";
15
+ import { listAgentSessions, loadSession, resolveSessionDir, writeSessionMetadata, } from "../config/sessions.js";
16
+ import { DEFAULT_AGENT_ID, normalizeAgentId, parseAgentSessionKey, } from "../routing/session-key.js";
17
17
  /**
18
18
  * SingleAgentControlPlane wraps the existing runEmbeddedPiAgent function
19
19
  * to implement the AgentControlPlane interface.
@@ -32,7 +32,7 @@ export class SingleAgentControlPlane {
32
32
  this.agentDir = options?.agentDir ?? resolveNexusAgentDir();
33
33
  }
34
34
  async sendMessage(options) {
35
- const { sessionKey, message, agentId: optAgentId, thinkingLevel, verboseLevel, isHeartbeat, streaming, onChunk, onComplete, onError, } = options;
35
+ const { sessionKey, message, agentId: optAgentId, thinkingLevel, verboseLevel, isHeartbeat: _isHeartbeat, streaming, onChunk, onComplete, onError, } = options;
36
36
  try {
37
37
  // Parse session key to get agentId and sessionId
38
38
  const parsed = parseAgentSessionKey(sessionKey);
@@ -72,11 +72,11 @@ export class SingleAgentControlPlane {
72
72
  };
73
73
  }
74
74
  // Load skills snapshot
75
- const skillsSnapshot = await buildWorkspaceSkillSnapshot(this.workspaceDir, {
75
+ const skillsSnapshot = buildWorkspaceSkillSnapshot(this.workspaceDir, {
76
76
  config: this.config,
77
77
  });
78
78
  // Build callbacks for streaming/chunks
79
- let partialPayloads = [];
79
+ const partialPayloads = [];
80
80
  const onPartialReply = async (payload) => {
81
81
  if (onChunk) {
82
82
  onChunk({
@@ -169,7 +169,7 @@ export class SingleAgentControlPlane {
169
169
  entry,
170
170
  };
171
171
  }
172
- catch (error) {
172
+ catch {
173
173
  return null;
174
174
  }
175
175
  }
@@ -203,7 +203,7 @@ export class SingleAgentControlPlane {
203
203
  sessions.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
204
204
  return sessions;
205
205
  }
206
- catch (error) {
206
+ catch {
207
207
  return [];
208
208
  }
209
209
  }