@junctionpanel/server 0.1.35 → 0.1.37

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 (53) hide show
  1. package/dist/server/client/daemon-client.d.ts +22 -6
  2. package/dist/server/client/daemon-client.d.ts.map +1 -1
  3. package/dist/server/client/daemon-client.js +42 -1
  4. package/dist/server/client/daemon-client.js.map +1 -1
  5. package/dist/server/server/agent/agent-manager.d.ts +24 -1
  6. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  7. package/dist/server/server/agent/agent-manager.js +23 -0
  8. package/dist/server/server/agent/agent-manager.js.map +1 -1
  9. package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
  10. package/dist/server/server/agent/agent-projections.js +11 -0
  11. package/dist/server/server/agent/agent-projections.js.map +1 -1
  12. package/dist/server/server/agent/agent-sdk-types.d.ts +20 -0
  13. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  14. package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
  15. package/dist/server/server/agent/agent-storage.d.ts +4 -4
  16. package/dist/server/server/agent/pending-plan-review.d.ts +12 -0
  17. package/dist/server/server/agent/pending-plan-review.d.ts.map +1 -0
  18. package/dist/server/server/agent/pending-plan-review.js +85 -0
  19. package/dist/server/server/agent/pending-plan-review.js.map +1 -0
  20. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  21. package/dist/server/server/agent-attention-policy.d.ts +2 -3
  22. package/dist/server/server/agent-attention-policy.d.ts.map +1 -1
  23. package/dist/server/server/agent-attention-policy.js +2 -2
  24. package/dist/server/server/agent-attention-policy.js.map +1 -1
  25. package/dist/server/server/notifications/relay-client.d.ts +9 -0
  26. package/dist/server/server/notifications/relay-client.d.ts.map +1 -0
  27. package/dist/server/server/notifications/relay-client.js +55 -0
  28. package/dist/server/server/notifications/relay-client.js.map +1 -0
  29. package/dist/server/server/notifications/relay-ownership.d.ts +5 -0
  30. package/dist/server/server/notifications/relay-ownership.d.ts.map +1 -0
  31. package/dist/server/server/notifications/relay-ownership.js +25 -0
  32. package/dist/server/server/notifications/relay-ownership.js.map +1 -0
  33. package/dist/server/server/notifications/relay-store.d.ts +22 -0
  34. package/dist/server/server/notifications/relay-store.d.ts.map +1 -0
  35. package/dist/server/server/notifications/relay-store.js +72 -0
  36. package/dist/server/server/notifications/relay-store.js.map +1 -0
  37. package/dist/server/server/session.d.ts +13 -7
  38. package/dist/server/server/session.d.ts.map +1 -1
  39. package/dist/server/server/session.js +135 -75
  40. package/dist/server/server/session.js.map +1 -1
  41. package/dist/server/server/websocket-server.d.ts +5 -0
  42. package/dist/server/server/websocket-server.d.ts.map +1 -1
  43. package/dist/server/server/websocket-server.js +81 -6
  44. package/dist/server/server/websocket-server.js.map +1 -1
  45. package/dist/server/shared/agent-attention-notification.d.ts +2 -0
  46. package/dist/server/shared/agent-attention-notification.d.ts.map +1 -1
  47. package/dist/server/shared/agent-attention-notification.js +23 -5
  48. package/dist/server/shared/agent-attention-notification.js.map +1 -1
  49. package/dist/server/shared/messages.d.ts +1488 -1148
  50. package/dist/server/shared/messages.d.ts.map +1 -1
  51. package/dist/server/shared/messages.js +47 -12
  52. package/dist/server/shared/messages.js.map +1 -1
  53. package/package.json +2 -2
@@ -33,6 +33,8 @@ import { deriveProjectGroupingKey, deriveProjectGroupingName } from '../shared/p
33
33
  import { DEFAULT_DAEMON_PACKAGE_NAME, resolveDaemonPackageVersion, } from './daemon-package-context.js';
34
34
  import { runDaemonDoctor } from './daemon-doctor.js';
35
35
  import { MANAGED_DAEMON_PROVIDERS, autoRouteProviderExecutable, loadDaemonProviderSettings, saveDaemonProviderExecutablePath, } from './daemon-provider-settings.js';
36
+ import { applyNotificationRelayOwnerLabel, preserveNotificationRelayOwnerLabel, } from './notifications/relay-ownership.js';
37
+ import { saveNotificationRelayConfig } from './notifications/relay-store.js';
36
38
  import { resolvePackageUpdateInfo } from './package-update.js';
37
39
  import { loadPersistedConfig } from './persisted-config.js';
38
40
  const execAsync = promisify(exec);
@@ -42,6 +44,7 @@ const READ_ONLY_GIT_ENV = {
42
44
  };
43
45
  const DEFAULT_STORED_TIMELINE_FETCH_LIMIT = 200;
44
46
  const pendingAgentInitializations = new Map();
47
+ const pendingAgentMessageExecutions = new Map();
45
48
  const DEFAULT_AGENT_PROVIDER = AGENT_PROVIDER_IDS[0];
46
49
  const CHECKOUT_DIFF_WATCH_DEBOUNCE_MS = 150;
47
50
  const CHECKOUT_DIFF_FALLBACK_REFRESH_MS = 5000;
@@ -121,13 +124,14 @@ export class Session {
121
124
  this.nextTerminalStreamId = 1;
122
125
  this.checkoutDiffSubscriptions = new Map();
123
126
  this.checkoutDiffTargets = new Map();
124
- const { clientId, userId, onMessage, onBinaryMessage, onLifecycleIntent, logger, downloadTokenStore, junctionHome, agentManager, agentStorage, createAgentMcpTransport, terminalManager, agentProviderRuntimeSettings, } = options;
127
+ const { clientId, userId, onMessage, onBinaryMessage, onLifecycleIntent, onClientActivityChange, logger, downloadTokenStore, junctionHome, agentManager, agentStorage, createAgentMcpTransport, terminalManager, agentProviderRuntimeSettings, } = options;
125
128
  this.clientId = clientId;
126
129
  this.userId = userId;
127
130
  this.sessionId = uuidv4();
128
131
  this.onMessage = onMessage;
129
132
  this.onBinaryMessage = onBinaryMessage ?? null;
130
133
  this.onLifecycleIntent = onLifecycleIntent ?? null;
134
+ this.onClientActivityChange = onClientActivityChange ?? null;
131
135
  this.downloadTokenStore = downloadTokenStore;
132
136
  this.junctionHome = junctionHome;
133
137
  this.agentManager = agentManager;
@@ -247,6 +251,61 @@ export class Session {
247
251
  },
248
252
  });
249
253
  }
254
+ enqueueAcceptedAgentMessageExecution(input) {
255
+ const previous = pendingAgentMessageExecutions.get(input.agentId) ?? Promise.resolve();
256
+ const execution = previous
257
+ .catch(() => undefined)
258
+ .then(async () => {
259
+ const startedAt = Date.now();
260
+ const phaseTimings = {
261
+ performSendMs: 0,
262
+ waitForRunStartMs: 0,
263
+ };
264
+ const performSendStartedAt = Date.now();
265
+ await this.performAgentMessageSend(input);
266
+ phaseTimings.performSendMs = Date.now() - performSendStartedAt;
267
+ const waitForRunStartStartedAt = Date.now();
268
+ await this.agentManager.waitForAgentRunStart(input.agentId);
269
+ phaseTimings.waitForRunStartMs = Date.now() - waitForRunStartStartedAt;
270
+ this.sessionLogger.debug({
271
+ agentId: input.agentId,
272
+ durationMs: Date.now() - startedAt,
273
+ ...phaseTimings,
274
+ }, 'Accepted send_agent_message_request execution started');
275
+ });
276
+ const trackedExecution = execution.finally(() => {
277
+ if (pendingAgentMessageExecutions.get(input.agentId) === trackedExecution) {
278
+ pendingAgentMessageExecutions.delete(input.agentId);
279
+ }
280
+ });
281
+ pendingAgentMessageExecutions.set(input.agentId, trackedExecution);
282
+ return trackedExecution;
283
+ }
284
+ async performAgentMessageSend(input) {
285
+ await this.ensureAgentLoaded(input.agentId);
286
+ await this.interruptAgentIfRunning(input.agentId);
287
+ try {
288
+ this.agentManager.recordUserMessage(input.agentId, input.text, {
289
+ messageId: input.messageId,
290
+ emitState: false,
291
+ images: input.images?.map((image) => ({
292
+ data: image.preview?.data ?? image.data,
293
+ mimeType: image.preview?.mimeType ?? image.mimeType,
294
+ ...(image.label ? { label: image.label } : {}),
295
+ ...(image.preview?.width != null ? { width: image.preview.width } : {}),
296
+ ...(image.preview?.height != null ? { height: image.preview.height } : {}),
297
+ })),
298
+ });
299
+ }
300
+ catch (error) {
301
+ this.sessionLogger.error({ err: error, agentId: input.agentId }, `Failed to record user message for agent ${input.agentId}`);
302
+ }
303
+ const prompt = this.buildAgentPrompt(input.text, input.images);
304
+ const started = this.startAgentStream(input.agentId, prompt, input.runOptions);
305
+ if (!started.ok) {
306
+ throw new Error(started.error);
307
+ }
308
+ }
250
309
  /**
251
310
  * Initialize Agent MCP client for this session using in-memory transport
252
311
  */
@@ -889,6 +948,9 @@ export class Session {
889
948
  case 'auto_route_provider_request':
890
949
  await this.handleAutoRouteProviderRequest(msg);
891
950
  break;
951
+ case 'update_notification_relay_config_request':
952
+ await this.handleUpdateNotificationRelayConfigRequest(msg);
953
+ break;
892
954
  case 'clear_agent_attention':
893
955
  await this.handleClearAgentAttention(msg.agentId);
894
956
  break;
@@ -1357,7 +1419,7 @@ export class Session {
1357
1419
  await this.agentManager.setTitle(agentId, normalizedName);
1358
1420
  }
1359
1421
  if (normalizedLabels) {
1360
- await this.agentManager.setLabels(agentId, normalizedLabels);
1422
+ await this.agentManager.setLabels(agentId, preserveNotificationRelayOwnerLabel(liveAgent.labels, normalizedLabels));
1361
1423
  }
1362
1424
  }
1363
1425
  else {
@@ -1368,7 +1430,11 @@ export class Session {
1368
1430
  await this.agentStorage.upsert({
1369
1431
  ...existing,
1370
1432
  ...(normalizedName ? { title: normalizedName } : {}),
1371
- ...(normalizedLabels ? { labels: { ...existing.labels, ...normalizedLabels } } : {}),
1433
+ ...(normalizedLabels
1434
+ ? {
1435
+ labels: preserveNotificationRelayOwnerLabel(existing.labels, normalizedLabels),
1436
+ }
1437
+ : {}),
1372
1438
  });
1373
1439
  }
1374
1440
  this.emit({
@@ -1409,30 +1475,17 @@ export class Session {
1409
1475
  return;
1410
1476
  }
1411
1477
  try {
1412
- await this.ensureAgentLoaded(agentId);
1413
- }
1414
- catch (error) {
1415
- this.handleAgentRunError(agentId, error, 'Failed to initialize agent before sending prompt');
1416
- return;
1417
- }
1418
- try {
1419
- await this.interruptAgentIfRunning(agentId);
1420
- }
1421
- catch (error) {
1422
- this.handleAgentRunError(agentId, error, 'Failed to interrupt running agent before sending prompt');
1423
- return;
1424
- }
1425
- const prompt = this.buildAgentPrompt(text, images);
1426
- try {
1427
- this.agentManager.recordUserMessage(agentId, text, {
1478
+ await this.performAgentMessageSend({
1479
+ agentId,
1480
+ text,
1428
1481
  messageId,
1429
- emitState: false,
1482
+ images,
1483
+ runOptions,
1430
1484
  });
1431
1485
  }
1432
1486
  catch (error) {
1433
- this.sessionLogger.error({ err: error, agentId }, `Failed to record user message for agent ${agentId}`);
1487
+ this.handleAgentRunError(agentId, error, 'Failed to send prompt to agent');
1434
1488
  }
1435
- this.startAgentStream(agentId, prompt, runOptions);
1436
1489
  }
1437
1490
  /**
1438
1491
  * Handle create agent request
@@ -1445,7 +1498,9 @@ export class Session {
1445
1498
  const mergedLabels = autoWorkspaceName
1446
1499
  ? { ...labels, 'junction:workspace': autoWorkspaceName }
1447
1500
  : labels;
1448
- const snapshot = await this.agentManager.createAgent(sessionConfig, undefined, { labels: mergedLabels });
1501
+ const snapshot = await this.agentManager.createAgent(sessionConfig, undefined, {
1502
+ labels: applyNotificationRelayOwnerLabel(mergedLabels, this.userId),
1503
+ });
1449
1504
  await this.forwardAgentUpdate(snapshot);
1450
1505
  if (requestId) {
1451
1506
  const agentPayload = await this.getAgentPayloadById(snapshot.id);
@@ -1941,6 +1996,46 @@ export class Session {
1941
1996
  });
1942
1997
  }
1943
1998
  }
1999
+ async handleUpdateNotificationRelayConfigRequest(msg) {
2000
+ try {
2001
+ const relayUrl = msg.relayUrl?.trim();
2002
+ const token = msg.token?.trim();
2003
+ const expiresAt = msg.expiresAt?.trim();
2004
+ const providedCount = [msg.relayUrl, msg.token, msg.expiresAt].filter((value) => value !== undefined).length;
2005
+ if (providedCount === 0) {
2006
+ saveNotificationRelayConfig(this.junctionHome, this.userId, null);
2007
+ }
2008
+ else if (providedCount !== 3 || !relayUrl || !token || !expiresAt) {
2009
+ throw new SessionRequestError('invalid_notification_relay_config', 'relayUrl, token, and expiresAt must be provided together');
2010
+ }
2011
+ else {
2012
+ saveNotificationRelayConfig(this.junctionHome, this.userId, {
2013
+ relayUrl,
2014
+ token,
2015
+ expiresAt,
2016
+ });
2017
+ }
2018
+ this.emit({
2019
+ type: 'update_notification_relay_config_response',
2020
+ payload: {
2021
+ success: true,
2022
+ error: null,
2023
+ requestId: msg.requestId,
2024
+ },
2025
+ });
2026
+ }
2027
+ catch (error) {
2028
+ this.sessionLogger.error({ err: error }, 'Failed to update notification relay config');
2029
+ this.emit({
2030
+ type: 'update_notification_relay_config_response',
2031
+ payload: {
2032
+ success: false,
2033
+ error: error instanceof Error ? error.message : String(error),
2034
+ requestId: msg.requestId,
2035
+ },
2036
+ });
2037
+ }
2038
+ }
1944
2039
  normalizeGitOptions(gitOptions, legacyWorktreeName) {
1945
2040
  const fallbackOptions = legacyWorktreeName
1946
2041
  ? {
@@ -2301,6 +2396,14 @@ export class Session {
2301
2396
  appVisible: msg.appVisible,
2302
2397
  appVisibilityChangedAt,
2303
2398
  };
2399
+ if (this.onClientActivityChange) {
2400
+ try {
2401
+ this.onClientActivityChange(this.clientActivity);
2402
+ }
2403
+ catch (error) {
2404
+ this.sessionLogger.warn({ err: error }, 'onClientActivityChange callback failed');
2405
+ }
2406
+ }
2304
2407
  }
2305
2408
  /**
2306
2409
  * Handle list commands request for an agent
@@ -4423,7 +4526,8 @@ export class Session {
4423
4526
  }
4424
4527
  try {
4425
4528
  const agentId = resolved.agentId;
4426
- const archivedAt = await this.getArchivedAt(agentId);
4529
+ const storedRecord = await this.agentStorage.get(agentId);
4530
+ const archivedAt = storedRecord?.archivedAt ?? null;
4427
4531
  if (archivedAt) {
4428
4532
  this.emit({
4429
4533
  type: 'send_agent_message_response',
@@ -4436,57 +4540,13 @@ export class Session {
4436
4540
  });
4437
4541
  return;
4438
4542
  }
4439
- await this.ensureAgentLoaded(agentId);
4440
- await this.interruptAgentIfRunning(agentId);
4441
- try {
4442
- this.agentManager.recordUserMessage(agentId, msg.text, {
4443
- messageId: msg.messageId,
4444
- emitState: false,
4445
- });
4446
- }
4447
- catch (error) {
4448
- this.sessionLogger.error({ err: error, agentId }, 'Failed to record user message for send_agent_message_request');
4449
- }
4450
- const prompt = this.buildAgentPrompt(msg.text, msg.images);
4451
- const started = this.startAgentStream(agentId, prompt, normalizeAgentRunOptions(msg.runOptions));
4452
- if (!started.ok) {
4453
- this.emit({
4454
- type: 'send_agent_message_response',
4455
- payload: {
4456
- requestId: msg.requestId,
4457
- agentId,
4458
- accepted: false,
4459
- error: started.error,
4460
- },
4461
- });
4462
- return;
4463
- }
4464
- const startAbort = new AbortController();
4465
- const startTimeoutMs = 15000;
4466
- const startTimeout = setTimeout(() => startAbort.abort('timeout'), startTimeoutMs);
4467
- try {
4468
- await this.agentManager.waitForAgentRunStart(agentId, { signal: startAbort.signal });
4469
- }
4470
- catch (error) {
4471
- const message = error instanceof Error
4472
- ? error.message
4473
- : typeof error === 'string'
4474
- ? error
4475
- : 'Unknown error';
4476
- this.emit({
4477
- type: 'send_agent_message_response',
4478
- payload: {
4479
- requestId: msg.requestId,
4480
- agentId,
4481
- accepted: false,
4482
- error: message,
4483
- },
4484
- });
4485
- return;
4486
- }
4487
- finally {
4488
- clearTimeout(startTimeout);
4489
- }
4543
+ await this.enqueueAcceptedAgentMessageExecution({
4544
+ agentId,
4545
+ text: msg.text,
4546
+ ...(msg.messageId ? { messageId: msg.messageId } : {}),
4547
+ ...(msg.images ? { images: msg.images } : {}),
4548
+ runOptions: normalizeAgentRunOptions(msg.runOptions),
4549
+ });
4490
4550
  this.emit({
4491
4551
  type: 'send_agent_message_response',
4492
4552
  payload: {