@bastani/atomic 0.9.43-alpha.1 → 0.9.44-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +18 -0
  3. package/dist/builtin/intercom/broker/broker.ts +31 -7
  4. package/dist/builtin/intercom/broker/client.ts +65 -0
  5. package/dist/builtin/intercom/index.bundle.mjs +227 -95
  6. package/dist/builtin/intercom/live-route-refusal.ts +20 -0
  7. package/dist/builtin/intercom/package.json +1 -1
  8. package/dist/builtin/intercom/skills/intercom/SKILL.md +10 -6
  9. package/dist/builtin/intercom/types.ts +20 -1
  10. package/dist/builtin/mcp/CHANGELOG.md +7 -0
  11. package/dist/builtin/mcp/package.json +1 -1
  12. package/dist/builtin/subagents/CHANGELOG.md +9 -0
  13. package/dist/builtin/subagents/README.md +8 -8
  14. package/dist/builtin/subagents/agents/worker.md +1 -1
  15. package/dist/builtin/subagents/package.json +1 -1
  16. package/dist/builtin/subagents/skills/agent-browser/SKILL.md +1 -1
  17. package/dist/builtin/subagents/skills/cua-driver/EMBEDDING.md +21 -2
  18. package/dist/builtin/subagents/skills/cua-driver/LINUX.md +2 -2
  19. package/dist/builtin/subagents/skills/cua-driver/MACOS.md +342 -10
  20. package/dist/builtin/subagents/skills/cua-driver/README.md +4 -3
  21. package/dist/builtin/subagents/skills/cua-driver/RUNTIME.md +3 -1
  22. package/dist/builtin/subagents/skills/cua-driver/SKILL.md +7 -4
  23. package/dist/builtin/subagents/skills/cua-driver/WINDOWS.md +6 -5
  24. package/dist/builtin/subagents/skills/cua-driver/WORKFLOW.md +15 -0
  25. package/dist/builtin/subagents/skills/herdr/SKILL.md +22 -10
  26. package/dist/builtin/subagents/skills/subagent/SKILL.md +8 -5
  27. package/dist/builtin/subagents/src/extension/index.bundle.mjs +6 -6
  28. package/dist/builtin/web-access/package.json +1 -1
  29. package/dist/builtin/workflows/CHANGELOG.md +33 -0
  30. package/dist/builtin/workflows/README.md +1 -1
  31. package/dist/builtin/workflows/builtin/{chunk-frqxd4d1.js → chunk-k8d7ehjx.js} +22 -0
  32. package/dist/builtin/workflows/builtin/{chunk-3s95f7ry.js → chunk-qxpymnaf.js} +1 -1
  33. package/dist/builtin/workflows/builtin/{chunk-edhf30ef.js → chunk-zjecnjxx.js} +1 -1
  34. package/dist/builtin/workflows/builtin/goal.js +2 -2
  35. package/dist/builtin/workflows/builtin/index.js +3 -3
  36. package/dist/builtin/workflows/builtin/ralph.js +2 -2
  37. package/dist/builtin/workflows/package.json +1 -1
  38. package/dist/builtin/workflows/src/extension/index.bundle.mjs +1750 -598
  39. package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +1786 -600
  40. package/dist/builtin/workflows/src/index.js +1802 -650
  41. package/dist/builtin/workflows/src/local-worker-entry.mjs +1786 -600
  42. package/dist/cli/args.js +1 -1
  43. package/dist/cli/args.js.map +1 -1
  44. package/dist/core/exec.d.ts.map +1 -1
  45. package/dist/core/exec.js +11 -2
  46. package/dist/core/exec.js.map +1 -1
  47. package/dist/core/package-manager-resolver.d.ts.map +1 -1
  48. package/dist/core/package-manager-resolver.js +25 -14
  49. package/dist/core/package-manager-resolver.js.map +1 -1
  50. package/dist/core/package-manager-types.d.ts +8 -0
  51. package/dist/core/package-manager-types.d.ts.map +1 -1
  52. package/dist/core/package-manager-types.js.map +1 -1
  53. package/dist/core/package-manager.d.ts +1 -1
  54. package/dist/core/package-manager.d.ts.map +1 -1
  55. package/dist/core/package-manager.js.map +1 -1
  56. package/dist/core/resource-loader-extension-warnings.d.ts +4 -1
  57. package/dist/core/resource-loader-extension-warnings.d.ts.map +1 -1
  58. package/dist/core/resource-loader-extension-warnings.js +15 -0
  59. package/dist/core/resource-loader-extension-warnings.js.map +1 -1
  60. package/dist/core/resource-loader-package-resources.d.ts.map +1 -1
  61. package/dist/core/resource-loader-package-resources.js +1 -0
  62. package/dist/core/resource-loader-package-resources.js.map +1 -1
  63. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  64. package/dist/core/resource-loader-reload.js +5 -3
  65. package/dist/core/resource-loader-reload.js.map +1 -1
  66. package/dist/extensions/mcp/client/oauth/flow.d.ts.map +1 -1
  67. package/dist/extensions/mcp/client/oauth/flow.js +4 -1
  68. package/dist/extensions/mcp/client/oauth/flow.js.map +1 -1
  69. package/dist/extensions/mcp/client/oauth/types.d.ts +1 -0
  70. package/dist/extensions/mcp/client/oauth/types.d.ts.map +1 -1
  71. package/dist/extensions/mcp/client/oauth/types.js +8 -4
  72. package/dist/extensions/mcp/client/oauth/types.js.map +1 -1
  73. package/dist/extensions/mcp/client/transports/streamable-http.d.ts.map +1 -1
  74. package/dist/extensions/mcp/client/transports/streamable-http.js +2 -1
  75. package/dist/extensions/mcp/client/transports/streamable-http.js.map +1 -1
  76. package/dist/index.d.ts +1 -1
  77. package/dist/index.d.ts.map +1 -1
  78. package/dist/index.js.map +1 -1
  79. package/dist/main.d.ts.map +1 -1
  80. package/dist/main.js +20 -10
  81. package/dist/main.js.map +1 -1
  82. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  83. package/dist/modes/interactive/components/model-selector.js +4 -2
  84. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  85. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  86. package/dist/modes/interactive/components/scoped-models-selector.js +4 -2
  87. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  88. package/dist/modes/interactive/components/tool-execution.d.ts +6 -0
  89. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  90. package/dist/modes/interactive/components/tool-execution.js +11 -1
  91. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  92. package/dist/modes/interactive/model-search.d.ts +10 -0
  93. package/dist/modes/interactive/model-search.d.ts.map +1 -1
  94. package/dist/modes/interactive/model-search.js +21 -0
  95. package/dist/modes/interactive/model-search.js.map +1 -1
  96. package/dist/package-manager-cli.d.ts.map +1 -1
  97. package/dist/package-manager-cli.js +5 -0
  98. package/dist/package-manager-cli.js.map +1 -1
  99. package/dist/utils/tools-manager.d.ts.map +1 -1
  100. package/dist/utils/tools-manager.js +30 -35
  101. package/dist/utils/tools-manager.js.map +1 -1
  102. package/docs/computer-use.md +5 -5
  103. package/docs/custom-provider/streaming.md +9 -14
  104. package/docs/environment-variables.md +1 -0
  105. package/docs/intercom.md +5 -1
  106. package/docs/packages.md +2 -0
  107. package/docs/settings.md +2 -2
  108. package/docs/skills.md +2 -2
  109. package/docs/subagents.md +38 -9
  110. package/docs/workflows/api-reference.md +1 -1
  111. package/docs/workflows/authoring.md +1 -1
  112. package/docs/workflows/operations.md +8 -6
  113. package/docs/workflows/reliable-design.md +23 -5
  114. package/examples/extensions/custom-provider-anthropic/index.ts +31 -31
  115. package/examples/extensions/todo.ts +7 -0
  116. package/npm-shrinkwrap.json +32 -32
  117. package/package.json +3 -3
@@ -99,7 +99,13 @@ function isLiveWorkflowStageRouteRefusalCode(value) {
99
99
  function isTransientLiveWorkflowStageRouteRefusal(code) {
100
100
  return TRANSIENT_REFUSAL_CODES.has(code);
101
101
  }
102
- var LIVE_WORKFLOW_STAGE_ROUTE_REFUSAL_CODES, LIVE_WORKFLOW_STAGE_ROUTE_REFUSAL_REASONS, TRANSIENT_REFUSAL_CODES, IntercomLiveRouteRefusedError;
102
+ function liveRouteOwnerRetryDelayMs(attempt) {
103
+ return Math.min(LIVE_ROUTE_OWNER_RETRY_FIRST_MS * 2 ** attempt, LIVE_ROUTE_OWNER_RETRY_MAX_MS);
104
+ }
105
+ function isOwnerMissingRefusal(error) {
106
+ return error instanceof IntercomLiveRouteRefusedError && error.code === "owner_missing";
107
+ }
108
+ var LIVE_WORKFLOW_STAGE_ROUTE_REFUSAL_CODES, LIVE_WORKFLOW_STAGE_ROUTE_REFUSAL_REASONS, TRANSIENT_REFUSAL_CODES, IntercomLiveRouteRefusedError, LIVE_ROUTE_OWNER_WAIT_MS = 120000, LIVE_ROUTE_OWNER_RETRY_FIRST_MS = 250, LIVE_ROUTE_OWNER_RETRY_MAX_MS = 2000;
103
109
  var init_live_route_refusal = __esm(() => {
104
110
  LIVE_WORKFLOW_STAGE_ROUTE_REFUSAL_CODES = [
105
111
  "owner_missing",
@@ -908,7 +914,7 @@ function isWorkflowFutureStageRosterEntries(value) {
908
914
  function isWorkflowStageRosterEntries(value) {
909
915
  return Array.isArray(value) && value.every((entry) => typeof entry === "object" && entry !== null && entry.kind === "workflow-stage" && typeof entry.runId === "string" && typeof entry.stageId === "string" && typeof entry.stageName === "string" && typeof entry.target === "string" && (entry.lifecycle === "pending" || entry.lifecycle === "running") && typeof entry.group === "string" && (entry.sessionId === undefined || typeof entry.sessionId === "string"));
910
916
  }
911
- var BROKER_SOCKET, BROKER_CONNECT_TIMEOUT_MS = 1e4, PENDING_STAGE_ROUTE_ACK_TIMEOUT_MS = 1e4, GROUP_REQUEST_TIMEOUT_MS = 5000, PRESENCE_ACK_TIMEOUT_MS = 5000, RESTORING_RECIPIENT_REQUEST_TIMEOUT_MS = 5000, HELD_SEND_GRACE_MS = 5000, IntercomClient;
917
+ var BROKER_SOCKET, BROKER_CONNECT_TIMEOUT_MS = 1e4, PENDING_STAGE_ROUTE_ACK_TIMEOUT_MS = 1e4, GROUP_REQUEST_TIMEOUT_MS = 5000, PRESENCE_ACK_TIMEOUT_MS = 5000, RESTORING_RECIPIENT_REQUEST_TIMEOUT_MS = 5000, HELD_SEND_GRACE_MS = 5000, IntercomPendingStageRouteRefusedError, PENDING_STAGE_ROUTE_REFUSAL_CODES, IntercomClient;
912
918
  var init_client = __esm(() => {
913
919
  init_paths();
914
920
  init_pending_send_registry();
@@ -917,6 +923,21 @@ var init_client = __esm(() => {
917
923
  init_live_route_refusal();
918
924
  init_retry_policy();
919
925
  BROKER_SOCKET = getBrokerSocketPath();
926
+ IntercomPendingStageRouteRefusedError = class IntercomPendingStageRouteRefusedError extends Error {
927
+ connectionKept;
928
+ code;
929
+ constructor(reason, refusal = { connectionKept: false }) {
930
+ super(reason);
931
+ this.name = "IntercomPendingStageRouteRefusedError";
932
+ this.connectionKept = refusal.connectionKept;
933
+ this.code = refusal.code;
934
+ }
935
+ };
936
+ PENDING_STAGE_ROUTE_REFUSAL_CODES = new Set([
937
+ "not_authorized",
938
+ "invalid_stage_roster",
939
+ "invalid_possible_stage_roster"
940
+ ]);
920
941
  IntercomClient = class IntercomClient extends EventEmitter {
921
942
  returnAddress;
922
943
  preferredSessionId;
@@ -924,6 +945,8 @@ var init_client = __esm(() => {
924
945
  connectTimeoutMs;
925
946
  brokerAcknowledgesReceipt = false;
926
947
  brokerAcknowledgesPendingStageRoutes = false;
948
+ multiplexesPendingStageRoutes;
949
+ brokerRefusesPendingStageRoutesInPlace = false;
927
950
  brokerAnswersStatus = false;
928
951
  socket = null;
929
952
  _sessionId = null;
@@ -989,6 +1012,7 @@ var init_client = __esm(() => {
989
1012
  this.preferredSessionId = options.preferredSessionId;
990
1013
  this.acknowledgesReceipt = options.acknowledgesReceipt === true;
991
1014
  this.connectTimeoutMs = options.connectTimeoutMs ?? BROKER_CONNECT_TIMEOUT_MS;
1015
+ this.multiplexesPendingStageRoutes = options.multiplexesPendingStageRoutes === true;
992
1016
  }
993
1017
  failPending(error) {
994
1018
  this.pendingSends.rejectAll(error);
@@ -1101,6 +1125,7 @@ var init_client = __esm(() => {
1101
1125
  this._supervisorSessionId = null;
1102
1126
  this.brokerAcknowledgesReceipt = false;
1103
1127
  this.brokerAcknowledgesPendingStageRoutes = false;
1128
+ this.brokerRefusesPendingStageRoutesInPlace = false;
1104
1129
  this.brokerAnswersStatus = false;
1105
1130
  this._messageSource = undefined;
1106
1131
  this.disconnectError = null;
@@ -1179,7 +1204,7 @@ var init_client = __esm(() => {
1179
1204
  }
1180
1205
  switch (brokerMessage.type) {
1181
1206
  case "registered": {
1182
- if (typeof brokerMessage.sessionId !== "string" || brokerMessage.supervisorSessionId !== undefined && typeof brokerMessage.supervisorSessionId !== "string" || brokerMessage.acknowledgesReceipt !== undefined && brokerMessage.acknowledgesReceipt !== true || brokerMessage.acknowledgesPendingStageRoutes !== undefined && brokerMessage.acknowledgesPendingStageRoutes !== true || brokerMessage.brokerStatus !== undefined && brokerMessage.brokerStatus !== true) {
1207
+ if (typeof brokerMessage.sessionId !== "string" || brokerMessage.supervisorSessionId !== undefined && typeof brokerMessage.supervisorSessionId !== "string" || brokerMessage.acknowledgesReceipt !== undefined && brokerMessage.acknowledgesReceipt !== true || brokerMessage.acknowledgesPendingStageRoutes !== undefined && brokerMessage.acknowledgesPendingStageRoutes !== true || brokerMessage.refusesPendingStageRoutesInPlace !== undefined && brokerMessage.refusesPendingStageRoutesInPlace !== true || brokerMessage.brokerStatus !== undefined && brokerMessage.brokerStatus !== true) {
1183
1208
  throw new Error("Invalid registered message");
1184
1209
  }
1185
1210
  if (this._sessionId !== null) {
@@ -1189,6 +1214,7 @@ var init_client = __esm(() => {
1189
1214
  this._supervisorSessionId = typeof brokerMessage.supervisorSessionId === "string" ? brokerMessage.supervisorSessionId : null;
1190
1215
  this.brokerAcknowledgesReceipt = this.acknowledgesReceipt && brokerMessage.acknowledgesReceipt === true;
1191
1216
  this.brokerAcknowledgesPendingStageRoutes = brokerMessage.acknowledgesPendingStageRoutes === true;
1217
+ this.brokerRefusesPendingStageRoutesInPlace = brokerMessage.refusesPendingStageRoutesInPlace === true;
1192
1218
  this.brokerAnswersStatus = brokerMessage.brokerStatus === true;
1193
1219
  this.emit("_registered", { type: "registered", sessionId: brokerMessage.sessionId });
1194
1220
  break;
@@ -1201,6 +1227,12 @@ var init_client = __esm(() => {
1201
1227
  this.emit("_registration_failed", refusal);
1202
1228
  break;
1203
1229
  }
1230
+ const refusedRouteId = typeof brokerMessage.requestId === "string" ? brokerMessage.requestId : "";
1231
+ const refusedRoute = this.pendingStageRouteRegistrations.get(refusedRouteId);
1232
+ if (refusedRoute !== undefined) {
1233
+ this.pendingStageRouteRegistrations.delete(refusedRouteId);
1234
+ refusedRoute.reject(new IntercomPendingStageRouteRefusedError(brokerMessage.reason));
1235
+ }
1204
1236
  this.disconnectError ??= refusal;
1205
1237
  this.failPending(this.disconnectError);
1206
1238
  this.socket?.destroy();
@@ -1326,6 +1358,21 @@ var init_client = __esm(() => {
1326
1358
  this.emit("pending_stage_route_revoked", { runId });
1327
1359
  break;
1328
1360
  }
1361
+ case "pending_stage_route_refused": {
1362
+ const { requestId, runId, code, reason } = brokerMessage;
1363
+ if (typeof requestId !== "string" || typeof runId !== "string" || typeof reason !== "string" || typeof code !== "string") {
1364
+ throw new Error("Invalid pending-stage route refusal");
1365
+ }
1366
+ const pending = this.pendingStageRouteRegistrations.get(requestId);
1367
+ if (!pending)
1368
+ return;
1369
+ this.pendingStageRouteRegistrations.delete(requestId);
1370
+ pending.reject(new IntercomPendingStageRouteRefusedError(reason, {
1371
+ connectionKept: true,
1372
+ ...PENDING_STAGE_ROUTE_REFUSAL_CODES.has(code) ? { code } : {}
1373
+ }));
1374
+ break;
1375
+ }
1329
1376
  case "pending_stage_route_registered": {
1330
1377
  const { requestId } = brokerMessage;
1331
1378
  if (typeof requestId !== "string")
@@ -1829,7 +1876,8 @@ var init_client = __esm(() => {
1829
1876
  stages,
1830
1877
  possibleStages,
1831
1878
  ...parent === undefined ? {} : { parent },
1832
- ...dormant === true ? { dormant: true } : {}
1879
+ ...dormant === true ? { dormant: true } : {},
1880
+ ...this.multiplexesPendingStageRoutes && this.brokerRefusesPendingStageRoutesInPlace ? { refusalKeepsConnection: true } : {}
1833
1881
  });
1834
1882
  } catch (error) {
1835
1883
  clearTimeout(timeout);
@@ -7472,6 +7520,7 @@ __export(exports_index_heavy, {
7472
7520
  });
7473
7521
  import { APP_NAME, sessionScopedExtensionState } from "@bastani/atomic";
7474
7522
  import { appendFileSync as appendFileSync2 } from "node:fs";
7523
+ import { setTimeout as delay3 } from "node:timers/promises";
7475
7524
  function isPendingStageUndeliverableEvent(value) {
7476
7525
  if (typeof value !== "object" || value === null)
7477
7526
  return false;
@@ -7543,6 +7592,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
7543
7592
  const inboundDeliveries = new InboundMessageAdmission;
7544
7593
  const pendingStageRoutes = new Map;
7545
7594
  const pendingStageRouteClients = new Map;
7595
+ const pendingStageRouteConnections = new Map;
7546
7596
  const routeClientConnectGate = createConcurrencyGate(ROUTE_CLIENT_CONNECT_CONCURRENCY);
7547
7597
  const supervisorAuthorizations = new SupervisorAuthorizationRegistry;
7548
7598
  const releasedStagePeers = new ReleasedStagePeers({
@@ -8123,42 +8173,105 @@ function piIntercomExtension(pi, testOverrides = {}) {
8123
8173
  });
8124
8174
  }, jitteredReconnectDelayMs(state.reconnectAttempt));
8125
8175
  }
8126
- function attachPendingStageRouteClientHandlers(runId, state, nextClient) {
8176
+ function pendingStageRouteRunsOn(nextClient) {
8177
+ return [...pendingStageRouteClients].filter(([, state]) => state.client === nextClient);
8178
+ }
8179
+ function attachPendingStageRouteConnectionHandlers(connection, nextClient) {
8127
8180
  nextClient.on("pending_stage_message", (request) => {
8128
- handlePendingStageMessage(nextClient, request, () => state.client === nextClient && pendingStageRouteClientIsCurrent(runId, state));
8181
+ handlePendingStageMessage(nextClient, request, () => {
8182
+ const state = pendingStageRouteClients.get(request.runId);
8183
+ return state?.client === nextClient && pendingStageRouteClientIsCurrent(request.runId, state);
8184
+ });
8129
8185
  });
8130
8186
  nextClient.on("disconnected", () => {
8131
- if (state.client !== nextClient)
8132
- return;
8133
- state.client = null;
8134
- schedulePendingStageRouteReconnect(runId, state);
8187
+ if (connection.client === nextClient)
8188
+ connection.client = null;
8189
+ for (const [runId, state] of pendingStageRouteRunsOn(nextClient)) {
8190
+ state.client = null;
8191
+ schedulePendingStageRouteReconnect(runId, state);
8192
+ }
8135
8193
  });
8136
8194
  nextClient.on("error", () => {});
8137
8195
  nextClient.on("sticky_live_delivered", (notice) => {
8138
- if (state.client === nextClient)
8196
+ if (connection.client === nextClient)
8139
8197
  relayStickyLiveDelivered(notice);
8140
8198
  });
8141
8199
  nextClient.on("pending_stage_route_revoked", (notice) => {
8142
- if (notice.runId !== runId || state.client !== nextClient)
8200
+ const state = pendingStageRouteClients.get(notice.runId);
8201
+ if (state === undefined || state.client !== nextClient)
8143
8202
  return;
8144
8203
  state.client = null;
8145
8204
  endPendingStageRouteBackoff(state, new IntercomClientDisconnectedError);
8146
- if (pendingStageRouteClients.get(runId) === state)
8147
- pendingStageRouteClients.delete(runId);
8148
- if (samePendingStageRoute(pendingStageRoutes.get(runId), state.route))
8149
- pendingStageRoutes.delete(runId);
8150
- nextClient.disconnect().catch(() => {});
8151
- pi.events.emit(PENDING_STAGE_ROUTE_REVOKED_EVENT, { runId, capability: state.route.capability });
8205
+ pendingStageRouteClients.delete(notice.runId);
8206
+ if (samePendingStageRoute(pendingStageRoutes.get(notice.runId), state.route))
8207
+ pendingStageRoutes.delete(notice.runId);
8208
+ if (pendingStageRouteRunsOn(nextClient).length === 0)
8209
+ closePendingStageRouteConnection(nextClient);
8210
+ pi.events.emit(PENDING_STAGE_ROUTE_REVOKED_EVENT, { runId: notice.runId, capability: state.route.capability });
8152
8211
  });
8153
8212
  }
8213
+ function closePendingStageRouteConnection(nextClient) {
8214
+ for (const [key, connection] of pendingStageRouteConnections) {
8215
+ if (connection.client !== nextClient)
8216
+ continue;
8217
+ connection.client = null;
8218
+ if (connection.promise === null)
8219
+ pendingStageRouteConnections.delete(key);
8220
+ }
8221
+ nextClient.disconnect().catch(() => {});
8222
+ }
8223
+ function connectPendingStageRouteConnection(key, group) {
8224
+ let connection = pendingStageRouteConnections.get(key);
8225
+ if (connection === undefined) {
8226
+ connection = { client: null, promise: null };
8227
+ pendingStageRouteConnections.set(key, connection);
8228
+ }
8229
+ if (connection.client?.isConnected())
8230
+ return Promise.resolve(connection.client);
8231
+ if (connection.promise !== null)
8232
+ return connection.promise;
8233
+ const owner = connection;
8234
+ const promise = routeClientConnectGate.run(async () => {
8235
+ await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs);
8236
+ if (pendingStageRouteConnections.get(key) !== owner)
8237
+ throw new IntercomClientDisconnectedError;
8238
+ const routeReturnAddress = `${currentSessionId}:pending-stage-route:${key}`;
8239
+ const nextClient = new IntercomClient(routeReturnAddress, {
8240
+ connectTimeoutMs: testOverrides.connectTimeoutMs?.(routeReturnAddress),
8241
+ multiplexesPendingStageRoutes: true
8242
+ });
8243
+ owner.client = nextClient;
8244
+ attachPendingStageRouteConnectionHandlers(owner, nextClient);
8245
+ try {
8246
+ await nextClient.connect({ ...buildRegistration(), recipientPurpose: "control", name: undefined, groups: [group], group }, undefined, undefined, readSubagentMessageSource(runtimeContext?.subagentPolicy));
8247
+ } catch (error) {
8248
+ if (owner.client === nextClient)
8249
+ owner.client = null;
8250
+ try {
8251
+ await nextClient.disconnect();
8252
+ } catch {}
8253
+ throw error;
8254
+ }
8255
+ return nextClient;
8256
+ }).finally(() => {
8257
+ if (owner.promise === promise)
8258
+ owner.promise = null;
8259
+ });
8260
+ owner.promise = promise;
8261
+ return promise;
8262
+ }
8154
8263
  async function retirePendingStageRouteClient(state) {
8155
8264
  try {
8156
8265
  await state.promise;
8157
8266
  } catch {}
8158
- if (state.client) {
8159
- try {
8160
- await state.client.disconnect();
8161
- } catch {}
8267
+ const retiredClient = state.client;
8268
+ state.client = null;
8269
+ if (retiredClient === null)
8270
+ return;
8271
+ const group = normalizeGroup(state.route.group);
8272
+ const groupInUse = [...pendingStageRoutes.values()].some((route) => !registersOnMainClient(route) && normalizeGroup(route.group) === group);
8273
+ if ((state.isolated || !groupInUse) && pendingStageRouteRunsOn(retiredClient).length === 0) {
8274
+ closePendingStageRouteConnection(retiredClient);
8162
8275
  }
8163
8276
  }
8164
8277
  function supersededPendingStageRouteError(context, generation) {
@@ -8171,6 +8284,7 @@ function piIntercomExtension(pi, testOverrides = {}) {
8171
8284
  throw new Error("Intercom runtime not initialized");
8172
8285
  let existing = pendingStageRouteClients.get(runId);
8173
8286
  let retired;
8287
+ const isolated = existing?.isolated === true && normalizeGroup(existing.route.group) === normalizeGroup(route.group);
8174
8288
  if (existing && !samePendingStageRoute(existing.route, route)) {
8175
8289
  pendingStageRouteClients.delete(runId);
8176
8290
  endPendingStageRouteBackoff(existing, new IntercomClientDisconnectedError);
@@ -8183,13 +8297,24 @@ function piIntercomExtension(pi, testOverrides = {}) {
8183
8297
  promise: null,
8184
8298
  reconnectTimer: null,
8185
8299
  reconnectAttempt: 0,
8186
- retryWaiters: new RouteRetryWaiters
8300
+ retryWaiters: new RouteRetryWaiters,
8301
+ isolated
8187
8302
  };
8188
8303
  if (!existing)
8189
8304
  pendingStageRouteClients.set(runId, state);
8190
8305
  state.route = route;
8191
8306
  if (state.client?.isConnected()) {
8192
- await state.client.registerPendingStageRouteAcknowledged(runId, normalizeGroup(route.group), route.capability, route.stages, route.possibleStages, route.parent, route.dormant === true);
8307
+ await state.client.registerPendingStageRouteAcknowledged(runId, normalizeGroup(route.group), route.capability, route.stages, route.possibleStages, route.parent, route.dormant === true).catch((error) => {
8308
+ if (error instanceof IntercomPendingStageRouteRefusedError) {
8309
+ if (!error.connectionKept)
8310
+ state.isolated = true;
8311
+ else if (state.client !== null) {
8312
+ state.client = null;
8313
+ schedulePendingStageRouteReconnect(runId, state);
8314
+ }
8315
+ }
8316
+ throw error;
8317
+ });
8193
8318
  return;
8194
8319
  }
8195
8320
  if (state.promise) {
@@ -8198,42 +8323,31 @@ function piIntercomExtension(pi, testOverrides = {}) {
8198
8323
  }
8199
8324
  if (state.reconnectTimer !== null && options.bypassBackoff !== true)
8200
8325
  return state.retryWaiters.join();
8201
- const connect = () => routeClientConnectGate.run(async () => {
8326
+ const connect = async () => {
8202
8327
  if (!pendingStageRouteClientIsCurrent(runId, state, contextAtStart, generationAtStart)) {
8203
8328
  throw supersededPendingStageRouteError(contextAtStart, generationAtStart);
8204
8329
  }
8205
- await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs);
8206
- const routeReturnAddress = `${currentSessionId}:pending-stage-route:${runId}`;
8207
- const nextClient = new IntercomClient(routeReturnAddress, {
8208
- connectTimeoutMs: testOverrides.connectTimeoutMs?.(routeReturnAddress)
8209
- });
8210
- state.client = nextClient;
8211
- attachPendingStageRouteClientHandlers(runId, state, nextClient);
8212
- await nextClient.connect({ ...buildRegistration(), recipientPurpose: "control", name: undefined, groups: [normalizeGroup(route.group)], group: normalizeGroup(route.group) }, undefined, undefined, readSubagentMessageSource(runtimeContext?.subagentPolicy));
8330
+ const group = normalizeGroup(route.group);
8331
+ const nextClient = await connectPendingStageRouteConnection(state.isolated ? runId : group, group);
8213
8332
  if (!pendingStageRouteClientIsCurrent(runId, state, contextAtStart, generationAtStart)) {
8214
- await nextClient.disconnect();
8215
8333
  throw supersededPendingStageRouteError(contextAtStart, generationAtStart);
8216
8334
  }
8335
+ state.client = nextClient;
8217
8336
  await nextClient.registerPendingStageRouteAcknowledged(runId, normalizeGroup(route.group), route.capability, route.stages, route.possibleStages, route.parent, route.dormant === true);
8218
8337
  if (!pendingStageRouteClientIsCurrent(runId, state, contextAtStart, generationAtStart)) {
8219
- await nextClient.disconnect();
8220
8338
  throw supersededPendingStageRouteError(contextAtStart, generationAtStart);
8221
8339
  }
8222
8340
  state.reconnectAttempt = 0;
8223
8341
  endPendingStageRouteBackoff(state);
8224
- });
8342
+ };
8225
8343
  const promise = retired === undefined ? connect() : retired.then(connect);
8226
8344
  state.promise = promise;
8227
8345
  try {
8228
8346
  await promise;
8229
8347
  } catch (error) {
8230
- const failedClient = state.client;
8348
+ if (error instanceof IntercomPendingStageRouteRefusedError && !error.connectionKept)
8349
+ state.isolated = true;
8231
8350
  state.client = null;
8232
- if (failedClient) {
8233
- try {
8234
- await failedClient.disconnect();
8235
- } catch {}
8236
- }
8237
8351
  if (state.promise === promise)
8238
8352
  state.promise = null;
8239
8353
  schedulePendingStageRouteReconnect(runId, state);
@@ -8246,17 +8360,25 @@ function piIntercomExtension(pi, testOverrides = {}) {
8246
8360
  async function disconnectPendingStageRouteClients() {
8247
8361
  const states = [...pendingStageRouteClients.values()];
8248
8362
  pendingStageRouteClients.clear();
8363
+ const connections = [...pendingStageRouteConnections.values()];
8364
+ pendingStageRouteConnections.clear();
8249
8365
  for (const state of states) {
8250
8366
  endPendingStageRouteBackoff(state, new IntercomClientDisconnectedError);
8251
8367
  try {
8252
8368
  await state.promise;
8253
8369
  } catch {}
8254
- if (!state.client)
8370
+ state.client = null;
8371
+ }
8372
+ for (const connection of connections) {
8373
+ try {
8374
+ await connection.promise;
8375
+ } catch {}
8376
+ if (!connection.client)
8255
8377
  continue;
8256
8378
  try {
8257
- await state.client.disconnect();
8379
+ await connection.client.disconnect();
8258
8380
  } catch {}
8259
- state.client = null;
8381
+ connection.client = null;
8260
8382
  }
8261
8383
  }
8262
8384
  async function registerPendingStageRoute(activeClient, runId, route) {
@@ -8306,59 +8428,68 @@ function piIntercomExtension(pi, testOverrides = {}) {
8306
8428
  const nextReconnectPromise = (async () => {
8307
8429
  let connectFailed = false;
8308
8430
  await ownershipRecorded.promise;
8309
- const nextClient = new IntercomClient(sessionIdAtStart, {
8310
- preferredSessionId,
8311
- acknowledgesReceipt: true,
8312
- connectTimeoutMs: testOverrides.connectTimeoutMs?.(sessionIdAtStart)
8313
- });
8314
- client = nextClient;
8315
- attachClientHandlers(nextClient);
8431
+ const ownerWaitDeadline = Date.now() + LIVE_ROUTE_OWNER_WAIT_MS;
8316
8432
  try {
8317
- await testOverrides.beforeConnectAttempt?.(reason);
8318
- await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs);
8319
- const childMetadata = currentChildOrchestratorMetadata();
8320
- await nextClient.connect(buildRegistration(), childMetadata?.supervisor, supervisorAuthorizations.ownerToken, readSubagentMessageSource(runtimeContext?.subagentPolicy), resolveSessionHomeGroup());
8321
- clientRegistrationGroup = resolveSessionHomeGroup();
8322
- if (replyCapability() === "terminal")
8323
- nextClient.updatePresence({ replyCapability: "terminal" });
8324
- await supervisorAuthorizations.restore(nextClient);
8325
- for (const [runId, route] of pendingStageRoutes) {
8326
- if (registersOnMainClient(route)) {
8327
- await registerPendingStageRoute(nextClient, runId, route);
8328
- continue;
8329
- }
8330
- ensurePendingStageRouteClient(runId, { ...route, group: normalizeGroup(route.group) }).catch(() => {});
8331
- }
8332
- const orchestration = contextAtStart.orchestrationContext;
8333
- if (orchestration?.kind === "workflow-stage" && orchestration.pendingStageDelivery !== undefined) {
8334
- const liveStageKeys = [orchestration.workflowStageId, orchestration.workflowStageName].filter((stageKey) => !stageKey.includes("/") && !stageKey.includes("*"));
8335
- if (liveStageKeys.length > 0) {
8336
- await nextClient.registerLiveWorkflowStageRoute(orchestration.workflowRunId, liveStageKeys, orchestration.pendingStageDelivery.routeCapability);
8433
+ for (let ownerWait = 0;; ownerWait += 1) {
8434
+ const nextClient = new IntercomClient(sessionIdAtStart, {
8435
+ preferredSessionId,
8436
+ acknowledgesReceipt: true,
8437
+ connectTimeoutMs: testOverrides.connectTimeoutMs?.(sessionIdAtStart)
8438
+ });
8439
+ client = nextClient;
8440
+ attachClientHandlers(nextClient);
8441
+ try {
8442
+ await testOverrides.beforeConnectAttempt?.(reason);
8443
+ await spawnBrokerIfNeeded(config.brokerCommand, config.brokerArgs);
8444
+ const childMetadata = currentChildOrchestratorMetadata();
8445
+ await nextClient.connect(buildRegistration(), childMetadata?.supervisor, supervisorAuthorizations.ownerToken, readSubagentMessageSource(runtimeContext?.subagentPolicy), resolveSessionHomeGroup());
8446
+ clientRegistrationGroup = resolveSessionHomeGroup();
8447
+ if (replyCapability() === "terminal")
8448
+ nextClient.updatePresence({ replyCapability: "terminal" });
8449
+ await supervisorAuthorizations.restore(nextClient);
8450
+ for (const [runId, route] of pendingStageRoutes) {
8451
+ if (registersOnMainClient(route)) {
8452
+ await registerPendingStageRoute(nextClient, runId, route);
8453
+ continue;
8454
+ }
8455
+ ensurePendingStageRouteClient(runId, { ...route, group: normalizeGroup(route.group) }).catch(() => {});
8456
+ }
8457
+ const orchestration = contextAtStart.orchestrationContext;
8458
+ if (orchestration?.kind === "workflow-stage" && orchestration.pendingStageDelivery !== undefined) {
8459
+ const liveStageKeys = [orchestration.workflowStageId, orchestration.workflowStageName].filter((stageKey) => !stageKey.includes("/") && !stageKey.includes("*"));
8460
+ if (liveStageKeys.length > 0) {
8461
+ await nextClient.registerLiveWorkflowStageRoute(orchestration.workflowRunId, liveStageKeys, orchestration.pendingStageDelivery.routeCapability);
8462
+ }
8463
+ }
8464
+ if (!getLiveContext(contextAtStart, generationAtStart)) {
8465
+ await nextClient.disconnect();
8466
+ throw new Error("Intercom runtime no longer active");
8467
+ }
8468
+ client = nextClient;
8469
+ if (nextClient.sessionId) {
8470
+ process.env[INTERCOM_SESSION_ID_ENV] = nextClient.sessionId;
8471
+ }
8472
+ reconnectAttempt = 0;
8473
+ if (nextClient.sessionId && nextClient.sessionId !== preferredSessionId) {
8474
+ await recordIntercomSessionIdentity(sessionIdAtStart, nextClient.sessionId);
8475
+ }
8476
+ return nextClient;
8477
+ } catch (error) {
8478
+ if (client === nextClient) {
8479
+ client = null;
8480
+ clientRegistrationGroup = null;
8481
+ }
8482
+ try {
8483
+ await nextClient.disconnect();
8484
+ } catch {}
8485
+ if (isOwnerMissingRefusal(error) && Date.now() < ownerWaitDeadline && !disposed && !shuttingDown && getLiveContext(contextAtStart, generationAtStart)) {
8486
+ await delay3(liveRouteOwnerRetryDelayMs(ownerWait));
8487
+ continue;
8488
+ }
8489
+ connectFailed = true;
8490
+ throw toError3(error);
8337
8491
  }
8338
8492
  }
8339
- if (!getLiveContext(contextAtStart, generationAtStart)) {
8340
- await nextClient.disconnect();
8341
- throw new Error("Intercom runtime no longer active");
8342
- }
8343
- client = nextClient;
8344
- if (nextClient.sessionId) {
8345
- process.env[INTERCOM_SESSION_ID_ENV] = nextClient.sessionId;
8346
- }
8347
- reconnectAttempt = 0;
8348
- if (nextClient.sessionId && nextClient.sessionId !== preferredSessionId) {
8349
- await recordIntercomSessionIdentity(sessionIdAtStart, nextClient.sessionId);
8350
- }
8351
- return nextClient;
8352
- } catch (error) {
8353
- if (client === nextClient) {
8354
- client = null;
8355
- clientRegistrationGroup = null;
8356
- }
8357
- connectFailed = true;
8358
- try {
8359
- await nextClient.disconnect();
8360
- } catch {}
8361
- throw toError3(error);
8362
8493
  } finally {
8363
8494
  if (reconnectPromiseGeneration === generationAtStart) {
8364
8495
  reconnectPromise = null;
@@ -8652,6 +8783,7 @@ var init_index_heavy = __esm(() => {
8652
8783
  init_runtime_group();
8653
8784
  init_reconnect_backoff();
8654
8785
  init_recoverable_disconnect();
8786
+ init_live_route_refusal();
8655
8787
  init_route_retry_waiters();
8656
8788
  init_released_stage_peers();
8657
8789
  init_supervisor_authorization_registry();
@@ -67,3 +67,23 @@ export class IntercomLiveRouteRefusedError extends Error {
67
67
  this.transient = isTransientLiveWorkflowStageRouteRefusal(code);
68
68
  }
69
69
  }
70
+
71
+ /**
72
+ * How long a starting stage keeps waiting out an `owner_missing` refusal. The run's owner
73
+ * registers its route over its own broker connection, which may still be connecting, backing
74
+ * off (up to 30 s) or waiting out 10 s connect and acknowledgement timeouts when the stage
75
+ * starts, so a few quick retries would turn ordinary registration latency into a failed stage
76
+ * (#3904). Past this bound the refusal surfaces as before.
77
+ */
78
+ export const LIVE_ROUTE_OWNER_WAIT_MS = 120_000;
79
+ const LIVE_ROUTE_OWNER_RETRY_FIRST_MS = 250;
80
+ const LIVE_ROUTE_OWNER_RETRY_MAX_MS = 2_000;
81
+
82
+ /** Delay before the next registration attempt while the owner is missing: doubling, capped at 2 s. */
83
+ export function liveRouteOwnerRetryDelayMs(attempt: number): number {
84
+ return Math.min(LIVE_ROUTE_OWNER_RETRY_FIRST_MS * 2 ** attempt, LIVE_ROUTE_OWNER_RETRY_MAX_MS);
85
+ }
86
+
87
+ export function isOwnerMissingRefusal(error: unknown): boolean {
88
+ return error instanceof IntercomLiveRouteRefusedError && error.code === "owner_missing";
89
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/intercom",
3
- "version": "0.9.43-alpha.1",
3
+ "version": "0.9.44-alpha.1",
4
4
  "private": true,
5
5
  "description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
6
6
  "contributors": [
@@ -247,12 +247,15 @@ In Atomic workflows, each invocation has its own Intercom group, and parallel st
247
247
 
248
248
  ### Pattern 8: Peer Coordination Between Subagents and Workflow Stages
249
249
 
250
- Communication inside a delegation is not only vertical. Children launched in one parallel set (or one explicit `group`) share an Intercom group, and workflow stages share their invocation group `workflow:<rootRunId>`; delegated subagents inside a stage inherit it. Any of them can list live siblings and message them directly. `contact_supervisor` is reserved for the supervisor; peer traffic uses ordinary `intercom`.
250
+ Peer-to-peer (swarm) is the default topology inside a delegation. There are no limits on communication between agents; it is encouraged whenever you need to. The supervisor is not a message relay: relaying adds latency, loses detail, and fills the supervisor's context, so siblings exchange evidence directly and self-organize. Children launched in one parallel set (or one explicit `group`) share an Intercom group, and workflow stages share their invocation group `workflow:<rootRunId>`; delegated subagents inside a stage inherit it. Any of them can list live siblings and message them directly. `contact_supervisor` is reserved for the supervisor; peer traffic uses ordinary `intercom`.
251
251
 
252
252
  ```typescript
253
253
  // Any child or stage: discover who is live in your group
254
254
  intercom({ action: "list" })
255
255
 
256
+ // Broadcast: share a finding with every live sibling in the workflow
257
+ intercom({ action: "send", to: "workflow:<rootRunId>/**", message: "Root cause confirmed: cache key omits tenant id (src/cache.ts:52). Adjust your angle." })
258
+
256
259
  // Connect: pass what you found to the sibling who needs it
257
260
  intercom({ action: "send", to: "codebase-analyzer-2", message: "Null handling lives in src/auth/session.ts:40-118 and src/auth/refresh.ts:12-60. Start there." })
258
261
 
@@ -266,14 +269,15 @@ intercom({ action: "ask", to: "debugger-1", message: "What exact command and env
266
269
  intercom({ action: "ask", to: "reviewer-2", message: "You called the retry loop unbounded. client.ts:88 caps attempts at 5 — which path bypasses it?" })
267
270
  ```
268
271
 
269
- Rules that keep peer exchange useful:
272
+ How a swarm works:
270
273
 
271
- - Peers do not coordinate spontaneously. The launcher's prompt should name the peers (or how to discover them with `list`) and what they are expected to exchange.
272
- - Bound the exchange: one evidence round for reviewers, one ownership claim per shared step, one ask per fact you need. Then return your own complete result.
273
- - Decide on evidence, not deference. A peer's approval, rejection, or claim is input to inspect, not a verdict to copy.
274
- - Scope, product, architecture, and acceptance decisions stay with the supervisor (`contact_supervisor`) or a workflow gate.
274
+ - Coordinate proactively. Discover peers with `list` as soon as you start and again whenever new siblings may have joined, without waiting for the launcher to name them.
275
+ - Broadcast findings, claim files or expensive steps, hand work along a chain, ask and answer each other, and debate disagreements for as long as it is useful.
276
+ - Decide on evidence, not deference. A peer's approval, rejection, or claim is input to inspect, not a verdict to copy, and each agent still returns its own result.
277
+ - `contact_supervisor` is for supervisor decisions; explicit user approval gates and authorization for destructive or external actions still apply.
275
278
  - Inside a workflow, `send` to a known pending sibling queues until it starts, and `ask` to a completed sibling that retains a valid conversation reopens it for a post-mortem turn. Named stage subgroups isolate their members from sibling subgroups.
276
279
  - Peer asks and one blocking supervisor request may wait concurrently in the same child; mutual asks work when both sides process inbound work.
280
+ - Scale with the task. For very complex work or requested heavy parallelism, hundreds or thousands of agents self-organize into teams (named groups, per-stage subagent sets, child workflows) and coordinate peer-to-peer like an organization. Groups have no member limit; team members reach their lead with `contact_supervisor`, and leads talk to each other in their shared group.
277
281
 
278
282
  ## Key Differences
279
283
 
@@ -221,6 +221,12 @@ export type ClientMessage =
221
221
  parent?: WorkflowRunParentAnnouncement;
222
222
  /** Interrupted run queued by its restarted owner before resume; yields to the process that resumes it (#3760). */
223
223
  dormant?: true;
224
+ /**
225
+ * #3906: the connection carries the routes of many runs, so a refusal of this one is answered with
226
+ * `pending_stage_route_refused` and leaves the connection open. Honoured only with a `requestId`, and sent only to
227
+ * a broker that advertised `refusesPendingStageRoutesInPlace`; an older broker ignores it and ends the connection.
228
+ */
229
+ refusalKeepsConnection?: true;
224
230
  }
225
231
  | {
226
232
  type: "register_live_workflow_stage_route";
@@ -254,6 +260,9 @@ export type ClientMessage =
254
260
  /** Allowed before `register`: shut the broker down; its sessions reconnect to a new broker. */
255
261
  | { type: "shutdown"; requestId: string };
256
262
 
263
+ /** Why the broker refused a pending-stage route registration (#3906). Authorization decides these, not the client. */
264
+ export type PendingStageRouteRefusalCode = "not_authorized" | "invalid_stage_roster" | "invalid_possible_stage_roster";
265
+
257
266
  export type BrokerMessage =
258
267
  | { type: "health"; requestId: string; instanceId: string }
259
268
  | {
@@ -264,10 +273,12 @@ export type BrokerMessage =
264
273
  acknowledgesReceipt?: true;
265
274
  /** This broker acknowledges a `register_pending_stage_route` that carries a `requestId`; absent on older brokers. */
266
275
  acknowledgesPendingStageRoutes?: true;
276
+ /** #3906: this broker honours `refusalKeepsConnection`; absent on older brokers, which end the connection. */
277
+ refusesPendingStageRoutesInPlace?: true;
267
278
  /** This broker answers `broker_status`; absent on older brokers, which drop a client that sends it. */
268
279
  brokerStatus?: true;
269
280
  }
270
- | { type: "registration_failed"; reason: string; code?: LiveWorkflowStageRouteRefusalCode }
281
+ | { type: "registration_failed"; reason: string; code?: LiveWorkflowStageRouteRefusalCode; requestId?: string }
271
282
  | { type: "question_target"; messageId: string; attemptId: string; sessionId: string }
272
283
  | { type: "sessions"; requestId: string; sessions: SessionInfo[]; workflowStages?: WorkflowStageRosterEntry[]; workflowFutureStages?: WorkflowFutureStageRosterEntry[]; restricted?: true }
273
284
  | { type: "groups"; requestId: string; groups: GroupSummary[] }
@@ -288,6 +299,14 @@ export type BrokerMessage =
288
299
  }
289
300
  | { type: "live_workflow_stage_route_registered"; requestId: string }
290
301
  | { type: "pending_stage_route_registered"; requestId: string }
302
+ /** #3906: the registration `requestId` of run `runId` was refused; the connection and its other routes are kept. */
303
+ | {
304
+ type: "pending_stage_route_refused";
305
+ requestId: string;
306
+ runId: string;
307
+ code: PendingStageRouteRefusalCode;
308
+ reason: string;
309
+ }
291
310
  /** A dormant route's run was resumed by another process, which now owns the run's routes (#3760). */
292
311
  | { type: "pending_stage_route_revoked"; runId: string }
293
312
  | { type: "presence_update"; session: SessionInfo }
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.9.44-alpha.1] - 2026-10-24
11
+
12
+ ### Fixed
13
+
14
+ - MCP sign-in now accepts only `http:` and `https:` authorization endpoints and other OAuth metadata URLs. Before, a malicious MCP server could supply a `file:` or custom protocol authorization endpoint (for example `search-ms:`), which the platform URL handler would launch on sign-in ([pi 9ffd27cf](https://github.com/earendil-works/pi/commit/9ffd27cfaaeed5c1ecf9369f393c3222975729b4))
15
+ - The excerpt of an HTTP error body in Streamable HTTP errors no longer keeps the whole response body alive ([pi #9265](https://github.com/earendil-works/pi/issues/9265))
16
+
10
17
  ## [0.9.35] - 2026-10-13
11
18
 
12
19
  ### Fixed