@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.
- package/CHANGELOG.md +45 -0
- package/dist/builtin/intercom/CHANGELOG.md +18 -0
- package/dist/builtin/intercom/broker/broker.ts +31 -7
- package/dist/builtin/intercom/broker/client.ts +65 -0
- package/dist/builtin/intercom/index.bundle.mjs +227 -95
- package/dist/builtin/intercom/live-route-refusal.ts +20 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/skills/intercom/SKILL.md +10 -6
- package/dist/builtin/intercom/types.ts +20 -1
- package/dist/builtin/mcp/CHANGELOG.md +7 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +9 -0
- package/dist/builtin/subagents/README.md +8 -8
- package/dist/builtin/subagents/agents/worker.md +1 -1
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/skills/agent-browser/SKILL.md +1 -1
- package/dist/builtin/subagents/skills/cua-driver/EMBEDDING.md +21 -2
- package/dist/builtin/subagents/skills/cua-driver/LINUX.md +2 -2
- package/dist/builtin/subagents/skills/cua-driver/MACOS.md +342 -10
- package/dist/builtin/subagents/skills/cua-driver/README.md +4 -3
- package/dist/builtin/subagents/skills/cua-driver/RUNTIME.md +3 -1
- package/dist/builtin/subagents/skills/cua-driver/SKILL.md +7 -4
- package/dist/builtin/subagents/skills/cua-driver/WINDOWS.md +6 -5
- package/dist/builtin/subagents/skills/cua-driver/WORKFLOW.md +15 -0
- package/dist/builtin/subagents/skills/herdr/SKILL.md +22 -10
- package/dist/builtin/subagents/skills/subagent/SKILL.md +8 -5
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +6 -6
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +33 -0
- package/dist/builtin/workflows/README.md +1 -1
- package/dist/builtin/workflows/builtin/{chunk-frqxd4d1.js → chunk-k8d7ehjx.js} +22 -0
- package/dist/builtin/workflows/builtin/{chunk-3s95f7ry.js → chunk-qxpymnaf.js} +1 -1
- package/dist/builtin/workflows/builtin/{chunk-edhf30ef.js → chunk-zjecnjxx.js} +1 -1
- package/dist/builtin/workflows/builtin/goal.js +2 -2
- package/dist/builtin/workflows/builtin/index.js +3 -3
- package/dist/builtin/workflows/builtin/ralph.js +2 -2
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +1750 -598
- package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +1786 -600
- package/dist/builtin/workflows/src/index.js +1802 -650
- package/dist/builtin/workflows/src/local-worker-entry.mjs +1786 -600
- package/dist/cli/args.js +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +11 -2
- package/dist/core/exec.js.map +1 -1
- package/dist/core/package-manager-resolver.d.ts.map +1 -1
- package/dist/core/package-manager-resolver.js +25 -14
- package/dist/core/package-manager-resolver.js.map +1 -1
- package/dist/core/package-manager-types.d.ts +8 -0
- package/dist/core/package-manager-types.d.ts.map +1 -1
- package/dist/core/package-manager-types.js.map +1 -1
- package/dist/core/package-manager.d.ts +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/resource-loader-extension-warnings.d.ts +4 -1
- package/dist/core/resource-loader-extension-warnings.d.ts.map +1 -1
- package/dist/core/resource-loader-extension-warnings.js +15 -0
- package/dist/core/resource-loader-extension-warnings.js.map +1 -1
- package/dist/core/resource-loader-package-resources.d.ts.map +1 -1
- package/dist/core/resource-loader-package-resources.js +1 -0
- package/dist/core/resource-loader-package-resources.js.map +1 -1
- package/dist/core/resource-loader-reload.d.ts.map +1 -1
- package/dist/core/resource-loader-reload.js +5 -3
- package/dist/core/resource-loader-reload.js.map +1 -1
- package/dist/extensions/mcp/client/oauth/flow.d.ts.map +1 -1
- package/dist/extensions/mcp/client/oauth/flow.js +4 -1
- package/dist/extensions/mcp/client/oauth/flow.js.map +1 -1
- package/dist/extensions/mcp/client/oauth/types.d.ts +1 -0
- package/dist/extensions/mcp/client/oauth/types.d.ts.map +1 -1
- package/dist/extensions/mcp/client/oauth/types.js +8 -4
- package/dist/extensions/mcp/client/oauth/types.js.map +1 -1
- package/dist/extensions/mcp/client/transports/streamable-http.d.ts.map +1 -1
- package/dist/extensions/mcp/client/transports/streamable-http.js +2 -1
- package/dist/extensions/mcp/client/transports/streamable-http.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +20 -10
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +4 -2
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +4 -2
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +6 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +11 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/model-search.d.ts +10 -0
- package/dist/modes/interactive/model-search.d.ts.map +1 -1
- package/dist/modes/interactive/model-search.js +21 -0
- package/dist/modes/interactive/model-search.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +5 -0
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +30 -35
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/computer-use.md +5 -5
- package/docs/custom-provider/streaming.md +9 -14
- package/docs/environment-variables.md +1 -0
- package/docs/intercom.md +5 -1
- package/docs/packages.md +2 -0
- package/docs/settings.md +2 -2
- package/docs/skills.md +2 -2
- package/docs/subagents.md +38 -9
- package/docs/workflows/api-reference.md +1 -1
- package/docs/workflows/authoring.md +1 -1
- package/docs/workflows/operations.md +8 -6
- package/docs/workflows/reliable-design.md +23 -5
- package/examples/extensions/custom-provider-anthropic/index.ts +31 -31
- package/examples/extensions/todo.ts +7 -0
- package/npm-shrinkwrap.json +32 -32
- 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
|
-
|
|
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
|
|
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, () =>
|
|
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 (
|
|
8132
|
-
|
|
8133
|
-
state
|
|
8134
|
-
|
|
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 (
|
|
8196
|
+
if (connection.client === nextClient)
|
|
8139
8197
|
relayStickyLiveDelivered(notice);
|
|
8140
8198
|
});
|
|
8141
8199
|
nextClient.on("pending_stage_route_revoked", (notice) => {
|
|
8142
|
-
|
|
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
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
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
|
-
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
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 =
|
|
8326
|
+
const connect = async () => {
|
|
8202
8327
|
if (!pendingStageRouteClientIsCurrent(runId, state, contextAtStart, generationAtStart)) {
|
|
8203
8328
|
throw supersededPendingStageRouteError(contextAtStart, generationAtStart);
|
|
8204
8329
|
}
|
|
8205
|
-
|
|
8206
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
8379
|
+
await connection.client.disconnect();
|
|
8258
8380
|
} catch {}
|
|
8259
|
-
|
|
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
|
|
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
|
-
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
await
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
272
|
+
How a swarm works:
|
|
270
273
|
|
|
271
|
-
-
|
|
272
|
-
-
|
|
273
|
-
- Decide on evidence, not deference. A peer's approval, rejection, or claim is input to inspect, not a verdict to copy.
|
|
274
|
-
-
|
|
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
|