@parall/parall 1.62.0 → 1.64.0

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.
@@ -52286,6 +52286,41 @@ function buildForkResultPrefix(results) {
52286
52286
  return blocks.join("\n\n") + "\n\n---\n\n";
52287
52287
  }
52288
52288
 
52289
+ // ../agent-core/dist/dispatch-recovery.js
52290
+ var DispatchRecovery = class {
52291
+ scan;
52292
+ stopped;
52293
+ onError;
52294
+ running = false;
52295
+ requested = false;
52296
+ constructor(scan, stopped, onError) {
52297
+ this.scan = scan;
52298
+ this.stopped = stopped;
52299
+ this.onError = onError;
52300
+ }
52301
+ request = () => {
52302
+ if (this.stopped())
52303
+ return;
52304
+ this.requested = true;
52305
+ if (this.running)
52306
+ return;
52307
+ this.running = true;
52308
+ void this.run();
52309
+ };
52310
+ async run() {
52311
+ try {
52312
+ while (this.requested && !this.stopped()) {
52313
+ this.requested = false;
52314
+ await this.scan();
52315
+ }
52316
+ } catch (err) {
52317
+ this.onError(err);
52318
+ } finally {
52319
+ this.running = false;
52320
+ }
52321
+ }
52322
+ };
52323
+
52289
52324
  // ../agent-core/dist/gateway-base.js
52290
52325
  import * as os from "node:os";
52291
52326
 
@@ -52323,6 +52358,14 @@ function browserViewerRequestOptions(command, opts) {
52323
52358
  };
52324
52359
  }
52325
52360
 
52361
+ // ../sdk/dist/clip-connection-endpoints.js
52362
+ var clipConnectionEndpoints = {
52363
+ ORG_CLIP_MCP_OAUTH_ATTEMPT: (orgId, clipId, attemptId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-oauth-attempts/${attemptId}`,
52364
+ CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
52365
+ CLIP_CONNECTION_ACCESS: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}/access`,
52366
+ CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`
52367
+ };
52368
+
52326
52369
  // ../sdk/dist/attachment-endpoints.js
52327
52370
  function attachmentEndpoints(apiBase) {
52328
52371
  return {
@@ -52336,6 +52379,7 @@ function attachmentEndpoints(apiBase) {
52336
52379
  // ../sdk/dist/edge-endpoints.js
52337
52380
  var API_BASE = "/api/v1";
52338
52381
  var edgeEndpoints = {
52382
+ ORG_EDGE_PROFILE_ACCESS: (orgId, edgeId, profileName) => `${API_BASE}/orgs/${orgId}/edge/${edgeId}/profiles/${encodeURIComponent(profileName)}/access`,
52339
52383
  ORG_EDGE: (orgId) => `${API_BASE}/orgs/${orgId}/edge`,
52340
52384
  ORG_EDGE_CONNECT: (orgId) => `${API_BASE}/orgs/${orgId}/edge/connect`,
52341
52385
  ORG_EDGE_DEVICES: (orgId) => `${API_BASE}/orgs/${orgId}/edge/devices`,
@@ -52527,6 +52571,7 @@ var ENDPOINTS = {
52527
52571
  MACHINES_ME_AGENTS: `${API_BASE2}/machines/me/agents`,
52528
52572
  MACHINES_ME_HEALTH: `${API_BASE2}/machines/me/health`,
52529
52573
  MACHINES_ME_RUNTIME_REPORT: `${API_BASE2}/machines/me/runtime-report`,
52574
+ MACHINES_ME_DIAGNOSTICS: `${API_BASE2}/machines/me/diagnostics`,
52530
52575
  MACHINES_ME_CLIPS: `${API_BASE2}/machines/me/clips`,
52531
52576
  MACHINES_ME_BROWSER_PROFILES: `${API_BASE2}/machines/me/browser-profiles`,
52532
52577
  MACHINES_ME_BROWSER_PROFILE_STATUS: (profileId) => `${API_BASE2}/machines/me/browser-profiles/${profileId}/status`,
@@ -52616,6 +52661,7 @@ var ENDPOINTS = {
52616
52661
  TRIGGER: (orgId, triggerId) => `${API_BASE2}/orgs/${orgId}/external-triggers/${triggerId}`,
52617
52662
  /** @deprecated Use TRIGGER. Same compatibility path. */
52618
52663
  EXTERNAL_TRIGGER: (orgId, triggerId) => `${API_BASE2}/orgs/${orgId}/external-triggers/${triggerId}`,
52664
+ PLATFORM_TRIGGER_SCHEMA: (orgId) => `${API_BASE2}/orgs/${orgId}/platform-trigger-schema`,
52619
52665
  TRIGGER_RUNS: (orgId) => `${API_BASE2}/orgs/${orgId}/external-trigger-runs`,
52620
52666
  /** @deprecated Use TRIGGER_RUNS. Same compatibility path. */
52621
52667
  EXTERNAL_TRIGGER_RUNS: (orgId) => `${API_BASE2}/orgs/${orgId}/external-trigger-runs`,
@@ -52713,6 +52759,7 @@ var ENDPOINTS = {
52713
52759
  // Wiki History (commits, file commits, blame)
52714
52760
  WIKI_COMMITS: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/commits`,
52715
52761
  WIKI_FILE_COMMITS: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/file-commits`,
52762
+ WIKI_PATH_ACTIVITY: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/path-activity`,
52716
52763
  WIKI_BLAME: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/blame`,
52717
52764
  // Wiki Operations (audit log)
52718
52765
  WIKI_OPERATIONS: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/operations`,
@@ -52792,6 +52839,8 @@ var ENDPOINTS = {
52792
52839
  BILLING_CHECKOUT: (orgId) => `${API_BASE2}/orgs/${orgId}/billing/checkout`,
52793
52840
  BILLING_AUTO_RELOAD: (orgId) => `${API_BASE2}/orgs/${orgId}/billing/auto-reload`,
52794
52841
  BILLING_SETUP_INTENT: (orgId) => `${API_BASE2}/orgs/${orgId}/billing/setup-intent`,
52842
+ BILLING_USAGE_DAILY: (orgId) => `${API_BASE2}/orgs/${orgId}/billing/usage-daily`,
52843
+ BILLING_USAGE_HEATMAP: (orgId) => `${API_BASE2}/orgs/${orgId}/billing/usage-heatmap`,
52795
52844
  COMPUTE_PRICING: () => `${API_BASE2}/billing/compute-pricing`,
52796
52845
  // Runtime capability table (public, no auth) — SSOT for the create/settings
52797
52846
  // interlock: per-runtime compute modes, native model family, cross-family
@@ -52826,16 +52875,6 @@ var ENDPOINTS = {
52826
52875
  BROWSER_PROFILE_VIEWER_COMMAND: (orgId, profileId) => `${API_BASE2}/orgs/${orgId}/browser-profiles/${profileId}/viewer/command`,
52827
52876
  // Edge device endpoints
52828
52877
  ...edgeEndpoints,
52829
- ORG_BROWSER_ALIASES: (orgId) => `/api/v1/orgs/${orgId}/browser-aliases`,
52830
- ORG_BROWSER_ALIAS: (orgId, aliasId) => `/api/v1/orgs/${orgId}/browser-aliases/${aliasId}`,
52831
- ORG_BROWSER_ALIAS_READINESS_CHECKS: (orgId, aliasId) => `/api/v1/orgs/${orgId}/browser-aliases/${aliasId}/readiness-checks`,
52832
- ORG_BROWSER_ALIAS_READINESS_CHECK: (orgId, aliasId, checkId) => `/api/v1/orgs/${orgId}/browser-aliases/${aliasId}/readiness-checks/${checkId}`,
52833
- ORG_API_KEY_BROWSER_ALIAS_GRANTS: (orgId, keyId) => `/api/v1/orgs/${orgId}/api-keys/${keyId}/browser-alias-grants`,
52834
- ORG_API_KEY_BROWSER_ALIAS_GRANT: (orgId, keyId, aliasId) => `/api/v1/orgs/${orgId}/api-keys/${keyId}/browser-alias-grants/${aliasId}`,
52835
- ORG_MANAGED_ALIASES: (orgId) => `/api/v1/orgs/${orgId}/aliases`,
52836
- ORG_MANAGED_ALIAS: (orgId, aliasName) => `/api/v1/orgs/${orgId}/aliases/${encodeURIComponent(aliasName)}`,
52837
- ORG_BROWSER_ALIAS_EXECUTIONS: (orgId) => `/api/v1/orgs/${orgId}/executions`,
52838
- ORG_BROWSER_ALIAS_EXECUTION: (orgId, requestId) => `/api/v1/orgs/${orgId}/executions/${encodeURIComponent(requestId)}`,
52839
52878
  ORG_BROWSER_USE_OPERATIONS: (orgId) => `/api/v1/orgs/${orgId}/browser-use/operations`,
52840
52879
  ORG_BROWSER_USE_OPERATION: (orgId, operationId) => `/api/v1/orgs/${orgId}/browser-use/operations/${encodeURIComponent(operationId)}`,
52841
52880
  // Per-profile egress proxy (hosted Cloud Profiles; manager-only, human-only).
@@ -52848,8 +52887,7 @@ var ENDPOINTS = {
52848
52887
  // Same request/reply shape as the v2 browser-profile viewer, on the v3 edge
52849
52888
  // pipe. Additive: does NOT replace BROWSER_PROFILE_VIEWER_COMMAND.
52850
52889
  ORG_EDGE_VIEWER_COMMAND: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/viewer/command`,
52851
- CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
52852
- CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`,
52890
+ ...clipConnectionEndpoints,
52853
52891
  // Clip registry (v3, org-scoped, served by api-server — `crg_` entries)
52854
52892
  ORG_CLIP_REGISTRY: (orgId) => `/api/v1/orgs/${orgId}/clip-registry`,
52855
52893
  ORG_CLIP_REGISTRY_RESOLVE: (orgId) => `/api/v1/orgs/${orgId}/clip-registry/resolve`,
@@ -53363,6 +53401,91 @@ var ChannelConversationClient = class extends SlackFilesClient {
53363
53401
  }
53364
53402
  };
53365
53403
 
53404
+ // ../sdk/dist/clip-connection-client.js
53405
+ var ClipConnectionClient = class extends ChannelConversationClient {
53406
+ // ---- Clip connections ----
53407
+ async listClipConnections(orgId, clipId) {
53408
+ return this.request("GET", ENDPOINTS.CLIP_CONNECTIONS(orgId, clipId));
53409
+ }
53410
+ async createClipConnection(orgId, clipId, input) {
53411
+ return this.request("POST", ENDPOINTS.CLIP_CONNECTIONS(orgId, clipId), input);
53412
+ }
53413
+ async deleteClipConnection(orgId, connId) {
53414
+ return this.request("DELETE", ENDPOINTS.CLIP_CONNECTION(orgId, connId));
53415
+ }
53416
+ /** Organization members may read; personal connections are owner-only. */
53417
+ async getClipConnectionAccess(orgId, connId) {
53418
+ return this.request("GET", ENDPOINTS.CLIP_CONNECTION_ACCESS(orgId, connId));
53419
+ }
53420
+ /** Human org owner/admin; accepts JWT or personal key. */
53421
+ async putClipConnectionAccess(orgId, connId, input) {
53422
+ return this.request("PUT", ENDPOINTS.CLIP_CONNECTION_ACCESS(orgId, connId), input);
53423
+ }
53424
+ /** Start OAuth using the reviewed platform app or provider registration ladder.
53425
+ * Allow discovery (30s), persistence (10s), and network overhead to finish. */
53426
+ async createClipMCPOAuthConnection(orgId, clipId, serverUrl, alias, oauthClient) {
53427
+ return this.request("POST", ENDPOINTS.ORG_CLIP_MCP_CONFIGS(orgId, clipId), {
53428
+ server_url: serverUrl,
53429
+ auth_type: "oauth",
53430
+ ...alias ? { alias } : {},
53431
+ ...oauthClient ? { oauth_client: oauthClient } : {}
53432
+ }, void 0, false, { timeoutMs: 5e4 });
53433
+ }
53434
+ /** Cancel exactly one pending attempt; requires its initiator or an org admin. */
53435
+ async cancelClipMCPOAuthAttempt(orgId, clipId, attemptId) {
53436
+ return this.request("DELETE", ENDPOINTS.ORG_CLIP_MCP_OAUTH_ATTEMPT(orgId, clipId, attemptId));
53437
+ }
53438
+ };
53439
+
53440
+ // ../sdk/dist/browser-profile-client.js
53441
+ var BrowserProfileClient = class extends ClipConnectionClient {
53442
+ /** Metadata-only compatibility discovery. Management scope errors propagate;
53443
+ * callers with a known Profile can execute without this discovery chain. */
53444
+ async listBrowserUseProfiles(orgId, profileId) {
53445
+ const me = await this.request("GET", ENDPOINTS.USERS_ME);
53446
+ const devices = await this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
53447
+ const data = [];
53448
+ for (const device of devices.data) {
53449
+ const personal = device.managed_by === "member";
53450
+ if (personal && device.owner_user_id !== me.id)
53451
+ continue;
53452
+ const profiles = await this.request("GET", ENDPOINTS.ORG_EDGE_PROFILES(orgId, device.id));
53453
+ for (const profile of profiles) {
53454
+ if (profileId && profile.id !== profileId)
53455
+ continue;
53456
+ const access = personal ? null : await this.getBrowserProfileAccess(orgId, device.id, profile.name);
53457
+ if (!personal && !access?.org_public && !(access?.grants ?? []).some((g) => g.user_id === me.id && g.grants.length > 0))
53458
+ continue;
53459
+ data.push({
53460
+ profile_id: profile.id,
53461
+ name: profile.name,
53462
+ device_name: device.name,
53463
+ placement: device.placement ?? "byoc",
53464
+ lifecycle_state: profile.lifecycle_state,
53465
+ can_access: true,
53466
+ availability: device.placement === "hosted" ? device.hosted_state ?? "unknown" : device.status
53467
+ });
53468
+ }
53469
+ }
53470
+ return { data };
53471
+ }
53472
+ /** Submit once: even refresh-enabled clients must not replay browser actions. */
53473
+ async createBrowserUseOperation(orgId, request3) {
53474
+ return this.request("POST", ENDPOINTS.ORG_BROWSER_USE_OPERATIONS(orgId), request3, void 0, true);
53475
+ }
53476
+ /** Recover proof/data without creating a new browser operation. */
53477
+ async getBrowserUseOperation(orgId, operationId, opts) {
53478
+ return this.request("GET", ENDPOINTS.ORG_BROWSER_USE_OPERATION(orgId, operationId), void 0, void 0, false, opts);
53479
+ }
53480
+ async getBrowserProfileAccess(orgId, edgeId, profileName) {
53481
+ return this.request("GET", ENDPOINTS.ORG_EDGE_PROFILE_ACCESS(orgId, edgeId, profileName));
53482
+ }
53483
+ /** Replace the complete access policy; requires a human org owner/admin. */
53484
+ async putBrowserProfileAccess(orgId, edgeId, profileName, access) {
53485
+ return this.request("PUT", ENDPOINTS.ORG_EDGE_PROFILE_ACCESS(orgId, edgeId, profileName), access);
53486
+ }
53487
+ };
53488
+
53366
53489
  // ../sdk/dist/wiki-upload.js
53367
53490
  function createWikiUploadFormData(params) {
53368
53491
  const form = new FormData();
@@ -53499,7 +53622,7 @@ function legacyHeartbeatFromReport(report) {
53499
53622
  detectedRuntimes
53500
53623
  };
53501
53624
  }
53502
- var ParallClient = class _ParallClient extends ChannelConversationClient {
53625
+ var ParallClient = class _ParallClient extends BrowserProfileClient {
53503
53626
  baseUrl;
53504
53627
  wikiBaseUrl;
53505
53628
  token;
@@ -53797,8 +53920,8 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
53797
53920
  return this.request("POST", ENDPOINTS.AUTH_OAUTH_EXCHANGE, { code });
53798
53921
  }
53799
53922
  // ---- WebSocket ----
53800
- async getWsTicket() {
53801
- return this.request("POST", ENDPOINTS.WS_TICKET);
53923
+ async getWsTicket(req) {
53924
+ return this.request("POST", ENDPOINTS.WS_TICKET, req);
53802
53925
  }
53803
53926
  // ---- Users ----
53804
53927
  async getMe() {
@@ -54533,6 +54656,17 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
54533
54656
  await this.postMachineHeartbeat(legacyHeartbeatFromReport(report));
54534
54657
  return "legacy";
54535
54658
  }
54659
+ /**
54660
+ * Upload a diagnostics bundle from the machine itself (`mck_` bearer):
54661
+ * a gzip body the daemon assembled and redacted locally. Multipart so the
54662
+ * meta travels beside the file; the server indexes it for platform staff.
54663
+ */
54664
+ async uploadMachineDiagnostics(bundle, meta2 = {}) {
54665
+ const form = new FormData();
54666
+ form.append("meta", JSON.stringify(meta2));
54667
+ form.append("bundle", new Blob([new Uint8Array(bundle)], { type: "application/gzip" }), "diagnostics.json.gz");
54668
+ return this.multipartRequest("POST", ENDPOINTS.MACHINES_ME_DIAGNOSTICS, form, false, { timeoutMs: 2 * 60 * 1e3 });
54669
+ }
54536
54670
  /**
54537
54671
  * @deprecated Legacy form of `putMachineRuntimeReport`, kept for servers
54538
54672
  * that predate it. `POST /machines/me/health` — report daemon state. All
@@ -54774,8 +54908,8 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
54774
54908
  * no_action sweep otherwise. Idempotent.
54775
54909
  *
54776
54910
  * @deprecated Legacy ok-only alias — use {@link completeDispatch} with the
54777
- * `sources` form, which also carries `turn_outcome`. The endpoint retires
54778
- * at S3b (dispatch-convergence-design.md §3).
54911
+ * `sources` form, which also carries `turn_outcome`. Retirement conditions:
54912
+ * agent-dispatch-idempotency-design.md#compatibility.
54779
54913
  */
54780
54914
  async completeDispatchSources(orgId, req) {
54781
54915
  return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE_SOURCES(orgId), req);
@@ -55018,6 +55152,9 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
55018
55152
  async getTriggerSchema(orgId, connectionId) {
55019
55153
  return this.request("GET", ENDPOINTS.TRIGGER_SCHEMA(orgId, connectionId));
55020
55154
  }
55155
+ async getPlatformTriggerSchema(orgId, eventTypes) {
55156
+ return this.request("GET", ENDPOINTS.PLATFORM_TRIGGER_SCHEMA(orgId), void 0, eventTypes && eventTypes.length > 0 ? { event_types: eventTypes.join(",") } : void 0);
55157
+ }
55021
55158
  async listTriggerIngressEvents(orgId, filters) {
55022
55159
  return this.request("GET", ENDPOINTS.TRIGGER_INGRESS_EVENTS(orgId), void 0, filters);
55023
55160
  }
@@ -55119,8 +55256,8 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
55119
55256
  return url;
55120
55257
  return `${this.baseUrlFor(url)}${url}`;
55121
55258
  }
55122
- async getWikiBlob(orgId, wikiId, params) {
55123
- const blob = await this.request("GET", ENDPOINTS.WIKI_BLOB(orgId, wikiId), void 0, params);
55259
+ async getWikiBlob(orgId, wikiId, params, opts) {
55260
+ const blob = await this.request("GET", ENDPOINTS.WIKI_BLOB(orgId, wikiId), void 0, params, false, opts);
55124
55261
  if (blob.signed_url)
55125
55262
  blob.signed_url = this.absoluteMediaUrl(blob.signed_url);
55126
55263
  return blob;
@@ -55314,6 +55451,10 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
55314
55451
  ...params
55315
55452
  });
55316
55453
  }
55454
+ /** Bounded, ACL-safe activity for one Wiki file path. */
55455
+ async getWikiPathActivity(orgId, wikiId, path11, opts) {
55456
+ return this.request("GET", ENDPOINTS.WIKI_PATH_ACTIVITY(orgId, wikiId), void 0, { path: path11 }, false, opts);
55457
+ }
55317
55458
  async getWikiBlame(orgId, wikiId, path11, ref) {
55318
55459
  return this.request("GET", ENDPOINTS.WIKI_BLAME(orgId, wikiId), void 0, { path: path11, ref });
55319
55460
  }
@@ -55489,6 +55630,21 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
55489
55630
  async getBilling(orgId) {
55490
55631
  return this.request("GET", ENDPOINTS.BILLING(orgId));
55491
55632
  }
55633
+ async getUsageDaily(orgId, opts) {
55634
+ const params = new URLSearchParams();
55635
+ params.set("start_date", opts.start_date);
55636
+ params.set("end_date", opts.end_date);
55637
+ return this.request("GET", `${ENDPOINTS.BILLING_USAGE_DAILY(orgId)}?${params}`);
55638
+ }
55639
+ async getUsageHeatmap(orgId, opts) {
55640
+ const params = new URLSearchParams();
55641
+ params.set("start_time", opts.start_time);
55642
+ if (opts.limit != null)
55643
+ params.set("limit", String(opts.limit));
55644
+ if (opts.offset != null)
55645
+ params.set("offset", String(opts.offset));
55646
+ return this.request("GET", `${ENDPOINTS.BILLING_USAGE_HEATMAP(orgId)}?${params}`);
55647
+ }
55492
55648
  async listBillingTransactions(orgId, opts) {
55493
55649
  const params = new URLSearchParams();
55494
55650
  if (opts?.cursor)
@@ -55658,12 +55814,14 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
55658
55814
  }
55659
55815
  // ---- Clip registry (v3, api-server org registry — `crg_` entries) ----
55660
55816
  /**
55661
- * List registry clips visible to the org: its own plus public+approved
55662
- * cross-org entries. This is the surface `installRegistryClip` and clip
55663
- * connections operate on.
55817
+ * List one page of registry clips visible to the org: its own plus
55818
+ * public+approved cross-org entries, used by installs and connections.
55664
55819
  */
55665
- async listOrgRegistryClips(orgId) {
55666
- const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?limit=100`);
55820
+ async listOrgRegistryClips(orgId, opts) {
55821
+ const params = new URLSearchParams({ limit: String(opts?.limit ?? 100) });
55822
+ if (opts?.offset !== void 0)
55823
+ params.set("offset", String(opts.offset));
55824
+ const resp = await this.request("GET", `${ENDPOINTS.ORG_CLIP_REGISTRY(orgId)}?${params}`);
55667
55825
  return resp ?? [];
55668
55826
  }
55669
55827
  /**
@@ -55744,7 +55902,6 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
55744
55902
  /**
55745
55903
  * Read a clip's per-org exec access (by user id — humans and agents alike).
55746
55904
  * Org-member readable, so a denied caller can learn why exec answered
55747
- * `CLIP_NOT_ALLOWED` / `CLIP_CONNECTION_NOT_ALLOWED` (agents:
55748
55905
  * `CLIP_AGENT_NOT_ALLOWED` / `CLIP_AGENT_CONNECTION_NOT_ALLOWED`). Writing
55749
55906
  * it (`putClipAgentExecAccess`) is an org owner/admin's act.
55750
55907
  */
@@ -55784,6 +55941,19 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
55784
55941
  async deleteEdgeDevice(orgId, edgeId) {
55785
55942
  return this.request("DELETE", ENDPOINTS.ORG_EDGE_DEVICE(orgId, edgeId));
55786
55943
  }
55944
+ /** Move the human caller's OWN BYOC device between the org layer and their
55945
+ * personal layer (ADR-020). `member` takes the device — and every clip
55946
+ * connection bound to it — out of the org's sight and into the owner's sole
55947
+ * custody; `org` shares it back, after which an org admin binds clips to it
55948
+ * and the clip's access list governs exec. Idempotent. Refused for hosted
55949
+ * profiles (`EDGE_NOT_PERSONALIZABLE`), for anyone but the owner
55950
+ * (`FORBIDDEN`; someone else's personal device is `NOT_FOUND`), and for
55951
+ * agents. */
55952
+ async setEdgeManagedBy(orgId, edgeId, managedBy) {
55953
+ return this.request("PATCH", ENDPOINTS.ORG_EDGE_DEVICE(orgId, edgeId), {
55954
+ managed_by: managedBy
55955
+ });
55956
+ }
55787
55957
  /** Idempotently remove the human caller's own offline BYOC registration.
55788
55958
  * A live connection returns `EDGE_ONLINE`. */
55789
55959
  async unregisterEdgeDevice(orgId, edgeId) {
@@ -55812,69 +55982,6 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
55812
55982
  async getEdgeProfileOperation(orgId, edgeId, operationId) {
55813
55983
  return this.request("GET", ENDPOINTS.ORG_EDGE_PROFILE_OPERATION(orgId, edgeId, operationId));
55814
55984
  }
55815
- /** Create and atomically materialize one Local BYOC Browser Alias. A replay
55816
- * with the same idempotency key and body returns the original stable ccn_. */
55817
- async createBrowserAlias(orgId, request3) {
55818
- return this.request("POST", ENDPOINTS.ORG_BROWSER_ALIASES(orgId), request3);
55819
- }
55820
- async listBrowserAliases(orgId) {
55821
- return this.request("GET", ENDPOINTS.ORG_BROWSER_ALIASES(orgId));
55822
- }
55823
- async getBrowserAlias(orgId, aliasId) {
55824
- return this.request("GET", ENDPOINTS.ORG_BROWSER_ALIAS(orgId, aliasId));
55825
- }
55826
- async renameBrowserAlias(orgId, aliasId, request3) {
55827
- return this.request("PATCH", ENDPOINTS.ORG_BROWSER_ALIAS(orgId, aliasId), request3);
55828
- }
55829
- async deleteBrowserAlias(orgId, aliasId, request3) {
55830
- return this.request("DELETE", ENDPOINTS.ORG_BROWSER_ALIAS(orgId, aliasId), request3);
55831
- }
55832
- /** Start one durable readiness check. The key is carried as the standard
55833
- * Idempotency-Key header; the request body is intentionally empty. */
55834
- async createBrowserAliasReadinessCheck(orgId, aliasId, request3) {
55835
- return this.request("POST", ENDPOINTS.ORG_BROWSER_ALIAS_READINESS_CHECKS(orgId, aliasId), void 0, void 0, false, { headers: { "Idempotency-Key": request3.idempotency_key } });
55836
- }
55837
- /** Sole durable recovery surface for one readiness request. */
55838
- async getBrowserAliasReadinessCheck(orgId, aliasId, checkId) {
55839
- return this.request("GET", ENDPOINTS.ORG_BROWSER_ALIAS_READINESS_CHECK(orgId, aliasId, checkId));
55840
- }
55841
- /** Organization Owner view. Hashes, reviewed snapshots and deltas are
55842
- * intentionally available only on this human-JWT management surface. */
55843
- async listBrowserAliasGrants(orgId, keyId) {
55844
- return this.request("GET", ENDPOINTS.ORG_API_KEY_BROWSER_ALIAS_GRANTS(orgId, keyId));
55845
- }
55846
- async getBrowserAliasGrant(orgId, keyId, aliasId) {
55847
- return this.request("GET", ENDPOINTS.ORG_API_KEY_BROWSER_ALIAS_GRANT(orgId, keyId, aliasId));
55848
- }
55849
- async replaceBrowserAliasGrant(orgId, keyId, aliasId, request3) {
55850
- return this.request("PUT", ENDPOINTS.ORG_API_KEY_BROWSER_ALIAS_GRANT(orgId, keyId, aliasId), request3);
55851
- }
55852
- /** Managed-key discovery. The response is already grant-filtered and never
55853
- * carries contract hashes, review snapshots, Edge IDs or Profile IDs. */
55854
- async listManagedBrowserAliases(orgId) {
55855
- return this.request("GET", ENDPOINTS.ORG_MANAGED_ALIASES(orgId));
55856
- }
55857
- async getManagedBrowserAlias(orgId, aliasName) {
55858
- return this.request("GET", ENDPOINTS.ORG_MANAGED_ALIAS(orgId, aliasName));
55859
- }
55860
- /** Accept and dispatch one @alias command. request_id is generated by the
55861
- * caller before local validation and is also the sole durable recovery key. */
55862
- async createBrowserAliasExecution(orgId, request3) {
55863
- return this.request("POST", ENDPOINTS.ORG_BROWSER_ALIAS_EXECUTIONS(orgId), request3);
55864
- }
55865
- async getBrowserAliasExecution(orgId, requestId) {
55866
- return this.request("GET", ENDPOINTS.ORG_BROWSER_ALIAS_EXECUTION(orgId, requestId));
55867
- }
55868
- /** Accept one idempotent Browser Use operation for an explicit Profile.
55869
- * The Server resolves Profile→Edge; callers never select an Edge directly. */
55870
- async createBrowserUseOperation(orgId, request3) {
55871
- return this.request("POST", ENDPOINTS.ORG_BROWSER_USE_OPERATIONS(orgId), request3);
55872
- }
55873
- /** Recover the durable receipt. Successful data is present only inside the
55874
- * Server's short encrypted result TTL; terminal proof remains afterwards. */
55875
- async getBrowserUseOperation(orgId, operationId) {
55876
- return this.request("GET", ENDPOINTS.ORG_BROWSER_USE_OPERATION(orgId, operationId));
55877
- }
55878
55985
  /**
55879
55986
  * Read a hosted Cloud Profile's egress-proxy status (manager-only: hosted
55880
55987
  * human maintainer or org admin). Sanitized — the password never comes back.
@@ -55983,9 +56090,8 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
55983
56090
  * - `EDGE_DEADLINE_EXCEEDED` 504 — arrived late, provably NOT executed.
55984
56091
  * - `EDGE_HOSTED_DISABLED_FOR_ORG` 403, `EDGE_REPAIR` 503 (operator-held),
55985
56092
  * `EDGE_RUNTIME_UNAVAILABLE` 503 (deployment has no hosted runtime).
55986
- * - Access errors (decided before any side effect): `CLIP_NOT_ALLOWED`
55987
- * (no grant for this clip), `CLIP_CONNECTION_NOT_ALLOWED` (grant does
55988
- * not cover this connection) — agent principals receive the historical
56093
+ * - Access errors (before side effects): human `CONNECTION_NOT_ALLOWED`.
56094
+ * Agent principals receive the historical
55989
56095
  * `CLIP_AGENT_NOT_ALLOWED` / `CLIP_AGENT_CONNECTION_NOT_ALLOWED` for one
55990
56096
  * release. An org owner/admin can widen access in the clip's settings.
55991
56097
  * - Routing errors: `CONNECTION_REQUIRED`, `CONNECTION_NOT_FOUND`,
@@ -56007,16 +56113,6 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
56007
56113
  throw err;
56008
56114
  }
56009
56115
  }
56010
- // ---- Clip connections ----
56011
- async listClipConnections(orgId, clipId) {
56012
- return this.request("GET", ENDPOINTS.CLIP_CONNECTIONS(orgId, clipId));
56013
- }
56014
- async createClipConnection(orgId, clipId, input) {
56015
- return this.request("POST", ENDPOINTS.CLIP_CONNECTIONS(orgId, clipId), input);
56016
- }
56017
- async deleteClipConnection(orgId, connId) {
56018
- return this.request("DELETE", ENDPOINTS.CLIP_CONNECTION(orgId, connId));
56019
- }
56020
56116
  // ---- MCP clip server config (same-org or reviewed Official OAuth) ----
56021
56117
  /**
56022
56118
  * Read the redacted MCP config. 404 NOT_FOUND when the clip has none yet —
@@ -56078,17 +56174,6 @@ var ParallClient = class _ParallClient extends ChannelConversationClient {
56078
56174
  timeoutMs: 4e4
56079
56175
  });
56080
56176
  }
56081
- /** Start a new Official OAuth MCP connection with the platform-owned app.
56082
- * Omitting oauth_client is load-bearing: the server resolves the exact
56083
- * reviewed-version binding and never falls back to DCR/CIMD cross-org. */
56084
- async createClipMCPOAuthConnection(orgId, clipId, serverUrl, alias, oauthClient) {
56085
- return this.request("POST", ENDPOINTS.ORG_CLIP_MCP_CONFIGS(orgId, clipId), {
56086
- server_url: serverUrl,
56087
- auth_type: "oauth",
56088
- ...alias ? { alias } : {},
56089
- ...oauthClient ? { oauth_client: oauthClient } : {}
56090
- });
56091
- }
56092
56177
  /** Re-authorize an existing MCP credential slot. */
56093
56178
  async initiateClipMCPOAuthById(orgId, clipId, configId, serverUrl, expectedVersion, oauthClient) {
56094
56179
  const req = {
@@ -56277,6 +56362,11 @@ function describeWsStateDetail(detail) {
56277
56362
  function isRetryableNetworkError(err) {
56278
56363
  return err instanceof Error && err.name === "ApiError" && "status" in err && err.status === 0;
56279
56364
  }
56365
+ function authRejectionFrom(err) {
56366
+ if (!(err instanceof ApiError) || err.status !== 401)
56367
+ return null;
56368
+ return { status: err.status, code: err.code, message: err.message };
56369
+ }
56280
56370
  function isBrowserRuntime() {
56281
56371
  return typeof window !== "undefined";
56282
56372
  }
@@ -56285,9 +56375,12 @@ var ParallWs = class {
56285
56375
  options;
56286
56376
  listeners = /* @__PURE__ */ new Map();
56287
56377
  stateListeners = /* @__PURE__ */ new Set();
56378
+ authInvalidListeners = /* @__PURE__ */ new Set();
56288
56379
  heartbeatTimer = null;
56289
56380
  reconnectTimer = null;
56290
56381
  reconnectAttempts = 0;
56382
+ /** Ticket attempts in the current auth-invalid episode (reset on success). */
56383
+ authAttempts = 0;
56291
56384
  lastSeq = 0;
56292
56385
  _state = "disconnected";
56293
56386
  intentionalClose = false;
@@ -56300,6 +56393,7 @@ var ParallWs = class {
56300
56393
  reconnect: true,
56301
56394
  reconnectInterval: 1e3,
56302
56395
  maxReconnectInterval: 3e4,
56396
+ maxAuthAttempts: 3,
56303
56397
  ...options
56304
56398
  };
56305
56399
  this.lastSeq = options.lastSeq ?? 0;
@@ -56309,12 +56403,25 @@ var ParallWs = class {
56309
56403
  }
56310
56404
  async connect() {
56311
56405
  this.intentionalClose = false;
56406
+ if (this._state === "auth-invalid")
56407
+ this.authAttempts = 0;
56312
56408
  this.setupBrowserListeners();
56313
56409
  this.setState("connecting");
56314
56410
  let ticket;
56315
56411
  try {
56316
56412
  ticket = await this.options.getTicket();
56317
56413
  } catch (err) {
56414
+ const authRejection = authRejectionFrom(err);
56415
+ if (authRejection) {
56416
+ this.authAttempts += 1;
56417
+ if (this.authAttempts < this.options.maxAuthAttempts && this.options.reconnect) {
56418
+ console.warn(`WS ticket auth rejected (attempt ${this.authAttempts}/${this.options.maxAuthAttempts}):`, err);
56419
+ this.scheduleReconnect({ cause: "ticket_failed" });
56420
+ } else {
56421
+ this.enterAuthInvalid(authRejection);
56422
+ }
56423
+ return;
56424
+ }
56318
56425
  if (isBrowserRuntime() && isRetryableNetworkError(err)) {
56319
56426
  console.warn("Failed to get WS ticket:", err);
56320
56427
  } else {
@@ -56361,6 +56468,7 @@ var ParallWs = class {
56361
56468
  this.ws.onopen = () => {
56362
56469
  clearTimeout(connectTimeout);
56363
56470
  this.reconnectAttempts = 0;
56471
+ this.authAttempts = 0;
56364
56472
  this.lastReceivedAt = Date.now();
56365
56473
  this.setState("connected");
56366
56474
  };
@@ -56448,6 +56556,20 @@ var ParallWs = class {
56448
56556
  this.stateListeners.delete(handler);
56449
56557
  };
56450
56558
  }
56559
+ /**
56560
+ * Subscribe to the auth-invalid transition. Fired exactly once when the
56561
+ * client enters the terminal 'auth-invalid' state (a second episode after
56562
+ * a recovered reconnect fires again — one event per transition edge).
56563
+ * Consumers: runtime bridges exit for supervisor re-mint (see
56564
+ * @parall/agent-core gateway-base), the daemon logs recovery guidance for
56565
+ * its machine key, UIs surface a re-authenticate action.
56566
+ */
56567
+ onAuthInvalid(handler) {
56568
+ this.authInvalidListeners.add(handler);
56569
+ return () => {
56570
+ this.authInvalidListeners.delete(handler);
56571
+ };
56572
+ }
56451
56573
  // ---- Internal ----
56452
56574
  send(frame) {
56453
56575
  if (this.ws?.readyState === WebSocket.OPEN) {
@@ -56493,6 +56615,8 @@ var ParallWs = class {
56493
56615
  }
56494
56616
  }
56495
56617
  scheduleReconnect(detail) {
56618
+ if (this._state === "auth-invalid")
56619
+ return;
56496
56620
  this.setState("reconnecting", { ...detail, attempt: this.reconnectAttempts + 1 });
56497
56621
  this.clearReconnect();
56498
56622
  const base = Math.min(this.options.reconnectInterval * Math.pow(2, this.reconnectAttempts), this.options.maxReconnectInterval);
@@ -56508,6 +56632,20 @@ var ParallWs = class {
56508
56632
  this.reconnectTimer = null;
56509
56633
  }
56510
56634
  }
56635
+ /**
56636
+ * Enter the terminal auth-invalid state and notify consumers once. Loud on
56637
+ * purpose: before this branch existed, a revoked key produced an infinite
56638
+ * silent connecting→reconnecting loop with no indication that the
56639
+ * credential — not the network — was the problem.
56640
+ */
56641
+ enterAuthInvalid(event) {
56642
+ this.clearReconnect();
56643
+ this.setState("auth-invalid");
56644
+ console.error(`WS auth-invalid: server rejected this credential (status ${event.status}${event.code ? `, code ${event.code}` : ""}) \u2014 ${event.message}. The key is invalid, expired, or revoked; retrying it cannot succeed, so reconnects are stopped. Recover by refreshing credentials (re-login, or issue a new API key), then connect again.`);
56645
+ for (const listener of this.authInvalidListeners) {
56646
+ listener(event);
56647
+ }
56648
+ }
56511
56649
  /** Force-close a dead/stale connection and trigger reconnect. */
56512
56650
  forceReconnect(cause) {
56513
56651
  this.stopHeartbeat();
@@ -56612,6 +56750,16 @@ var ParallWs = class {
56612
56750
  }
56613
56751
  };
56614
56752
 
56753
+ // ../sdk/dist/browser-use-contract.generated.js
56754
+ var browserUseContract = { "actions": { "read": { "grant": "browser.read", "target": "optional_tab", "params": { "required": [], "properties": { "url": { "type": "string", "min_length": 1, "max_length": 8192, "schemes": ["http", "https", "about:blank"] } }, "rules": ["exactly one of params.url or target is required", "params.url creates a hidden Profile-bound tab and closes it before terminal success or failure"], "additional_properties": false }, "success": { "base": null, "fields": { "profile_id": "ebp_identifier", "url": "string", "title": "string", "article": "nullable_article" } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "PAGE_LOAD_FAILED"] }, "tabs": { "grant": "browser.read", "target": "none", "params": { "required": [], "properties": {}, "additional_properties": false }, "success": { "base": null, "fields": { "tabs": "tab_summary_array" } }, "errors": ["TAB_GONE"] }, "wait": { "grant": "browser.read", "target": "tab", "params": { "required": [], "properties": { "selector": { "type": "string", "min_length": 1, "max_length": 4096 }, "state": { "enum": ["attached", "detached", "visible", "hidden", "load"], "default": "attached" }, "timeout_ms": { "type": "integer", "minimum": 1, "maximum": 3e4, "default": 1e4 } }, "rules": ["selector is required unless state=load", "state=load forbids selector"], "additional_properties": false }, "success": { "base": "tab_state", "fields": { "matched": { "const": true } } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "PAGE_LOAD_FAILED"] }, "snapshot": { "grant": "browser.read", "target": "tab", "params": { "required": [], "properties": { "interactive": { "type": "boolean", "default": false }, "compact": { "type": "boolean", "default": false }, "depth": { "type": "integer", "minimum": 0, "maximum": 64 } }, "additional_properties": false }, "success": { "base": "tab_state", "fields": { "snapshot": "string", "snapshot_id": "identifier" } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "PAGE_LOAD_FAILED"] }, "get": { "grant": "browser.read", "target": "tab_or_ref", "params": { "required": ["prop"], "properties": { "selector": { "type": "string", "min_length": 1, "max_length": 4096 }, "prop": { "enum": ["text", "html", "value", "attr", "title", "url"] }, "name": { "type": "string", "min_length": 1, "max_length": 256 } }, "rules": ["title/url forbid locator and name", "text/html/value require exactly one locator", "attr requires exactly one locator and name"], "additional_properties": false }, "success": { "base": "tab_state", "fields": { "value": "nullable_string" } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "STALE_REF", "PAGE_LOAD_FAILED"] }, "screenshot": { "grant": "browser.read", "target": "tab", "params": { "required": [], "properties": {}, "additional_properties": false }, "success": { "base": "tab_state", "fields": { "format": { "const": "png" }, "base64": "canonical_base64" } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "PAGE_LOAD_FAILED"] }, "open": { "grant": "browser.interact", "target": "none", "params": { "required": ["url"], "properties": { "url": { "type": "string", "min_length": 1, "max_length": 8192, "schemes": ["http", "https", "about:blank"] }, "hidden": { "type": "boolean", "default": false }, "viewport": { "type": "viewport" } }, "additional_properties": false }, "success": { "base": "tab_state", "fields": {} }, "errors": ["PAGE_LOAD_FAILED"] }, "navigate": { "grant": "browser.interact", "target": "tab", "params": { "required": ["url"], "properties": { "url": { "type": "string", "min_length": 1, "max_length": 8192, "schemes": ["http", "https", "about:blank"] } }, "additional_properties": false }, "success": { "base": "tab_state", "fields": {} }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "PAGE_LOAD_FAILED"] }, "viewport": { "grant": "browser.interact", "target": "tab", "params": { "required": ["width", "height"], "properties": { "width": { "type": "integer", "minimum": 320, "maximum": 3840 }, "height": { "type": "integer", "minimum": 240, "maximum": 2160 } }, "additional_properties": false }, "success": { "base": "tab_state", "fields": { "viewport": "viewport" } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE"] }, "click": { "grant": "browser.interact", "target": "tab_or_ref", "params": { "required": [], "properties": { "selector": "selector" }, "rules": ["exactly one of params.selector or target.ref"], "additional_properties": false }, "success": { "base": "tab_state", "fields": { "clicked": { "const": true } } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "STALE_REF", "PAGE_LOAD_FAILED"] }, "fill": { "grant": "browser.interact", "target": "tab_or_ref", "params": { "required": ["text"], "properties": { "selector": "selector", "text": { "type": "string", "allow_empty": true } }, "rules": ["exactly one of params.selector or target.ref"], "additional_properties": false }, "success": { "base": "tab_state", "fields": { "filled": { "const": true }, "characters": "non_negative_integer" } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "STALE_REF", "PAGE_LOAD_FAILED"] }, "type": { "grant": "browser.interact", "target": "tab_or_ref", "params": { "required": ["text"], "properties": { "selector": "selector", "text": { "type": "string", "min_length": 1 } }, "rules": ["exactly one of params.selector or target.ref"], "additional_properties": false }, "success": { "base": "tab_state", "fields": { "typed": { "const": true }, "characters": "non_negative_integer" } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "STALE_REF", "PAGE_LOAD_FAILED"] }, "press": { "grant": "browser.interact", "target": "tab", "params": { "required": ["key"], "properties": { "key": "single_unicode_character_or_named_key" }, "additional_properties": false }, "success": { "base": "tab_state", "fields": { "pressed": { "const": true } } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "PAGE_LOAD_FAILED"] }, "scroll": { "grant": "browser.interact", "target": "tab", "params": { "required": [], "properties": { "direction": { "enum": ["up", "down", "left", "right"], "default": "down" }, "pixels": { "type": "integer", "minimum": 1, "maximum": 1e5, "default": 500 } }, "additional_properties": false }, "success": { "base": "tab_state", "fields": { "scrolled": { "const": true } } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "PAGE_LOAD_FAILED"] }, "close": { "grant": "browser.interact", "target": "tab", "params": { "required": [], "properties": {}, "additional_properties": false }, "success": { "base": "tab_state", "fields": { "closed": { "const": true } } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE"] }, "recover": { "grant": "browser.interact", "target": "tab", "params": { "required": [], "properties": {}, "additional_properties": false }, "success": { "base": "tab_state", "fields": { "recovered_from_tab_id": "identifier" } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "PAGE_LOAD_FAILED"] }, "eval": { "grant": "browser.advanced", "target": "tab", "params": { "required": ["expression"], "properties": { "expression": { "type": "string", "min_length": 1 }, "timeout_ms": { "type": "integer", "minimum": 1, "maximum": 15e3, "default": 15e3 } }, "additional_properties": false }, "success": { "base": "tab_state", "fields": { "value": "json_value_max_256_kib" } }, "errors": ["TAB_NOT_FOUND", "TAB_GONE", "PAGE_LOAD_FAILED"] } }, "successShapes": { "tab_state": { "required": ["tab_id", "profile_id", "browser_generation", "document_generation", "url", "title"], "fields": { "tab_id": "identifier", "profile_id": "ebp_identifier", "browser_generation": "positive_integer", "document_generation": "positive_integer", "url": "string", "title": "string" }, "additional_properties": false }, "tab_summary": { "base": "tab_state", "required_additions": ["loading"], "fields": { "loading": "boolean" }, "additional_properties": false }, "article": { "required": ["title", "byline", "excerpt", "text", "published_time", "site_name", "length"], "fields": { "title": "nullable_string", "byline": "nullable_string", "excerpt": "nullable_string", "text": "string", "published_time": "nullable_string", "site_name": "nullable_string", "length": "non_negative_integer" }, "additional_properties": false } } };
56755
+
56756
+ // ../sdk/dist/browser-use-validation.js
56757
+ var BROWSER_USE_ACTION_SCHEMAS = browserUseContract.actions;
56758
+ var BROWSER_USE_ACTIONS = Object.freeze(Object.keys(BROWSER_USE_ACTION_SCHEMAS));
56759
+
56760
+ // ../sdk/dist/browser-use-receipt.js
56761
+ var KEYS = "contract_version operation_id request_id profile_id profile_version profile_config_revision action required_grant phase dispatched receipt_id receipt_digest occurred_at tab_id browser_generation duration_ms result_digest result_available result_expires_at data error deadline accepted_at started_at terminal_at updated_at".split(" ");
56762
+
56615
56763
  // ../agent-core/dist/fork-state.js
56616
56764
  var FORK_CONTINUATION_RETRY_CAP = 256;
56617
56765
  function forkContinuationRetryKey(event) {
@@ -56664,12 +56812,15 @@ function createActiveForkState(fork, targetId, continuationPrefix) {
56664
56812
 
56665
56813
  // ../agent-core/dist/redact.js
56666
56814
  function redactSecrets(s, knownValues = []) {
56815
+ return maskKnownValues(s, knownValues).replace(/\b(agk|mck|cpk)_[A-Za-z0-9_-]+/g, "$1_***").replace(/\b(sk|pk|rk)-[A-Za-z0-9_-]{8,}/g, "$1-***").replace(/\bAKIA[0-9A-Z]{16}\b/g, "AKIA***").replace(/\b((?:bearer|basic)\s+)[A-Za-z0-9._~+/=-]+/gi, "$1***").replace(/(\b(?:[\w-]*[_-])?(?:api[_-]?key|key|token|secret|password|passwd|authorization)["']?\s*[=:]\s*)(?:"(?:\\.|[^"\\\r\n])*"|'(?:\\.|[^'\\\r\n])*'|[^\s&;,}\]"']+)/gi, "$1***").replace(/[A-Za-z0-9_-]{32,}/g, "***");
56816
+ }
56817
+ function maskKnownValues(s, knownValues) {
56667
56818
  let out = s;
56668
56819
  for (const v of knownValues) {
56669
56820
  if (typeof v === "string" && v.length >= 6)
56670
56821
  out = out.split(v).join("***");
56671
56822
  }
56672
- return out.replace(/\b(agk|mck|cpk)_[A-Za-z0-9_-]+/g, "$1_***").replace(/\b(sk|pk|rk)-[A-Za-z0-9_-]{8,}/g, "$1-***").replace(/\bAKIA[0-9A-Z]{16}\b/g, "AKIA***").replace(/\b(bearer\s+)[A-Za-z0-9._~+/=-]{8,}/gi, "$1***").replace(/[A-Za-z0-9_-]{32,}/g, "***");
56823
+ return out;
56673
56824
  }
56674
56825
  function redactTurnOutcome(event, knownValues) {
56675
56826
  const redacted = { ...event };
@@ -56690,6 +56841,16 @@ function describeTurnOutcomeFailure(outcome) {
56690
56841
  stepMessage: `LLM turn ${outcome.outcome}${retryNote}${outcome.detail ? `: ${outcome.detail}` : ""}`
56691
56842
  };
56692
56843
  }
56844
+ function redactLogger(log, knownValues) {
56845
+ if (!log)
56846
+ return void 0;
56847
+ return {
56848
+ info: (message) => log.info(redactSecrets(message, knownValues)),
56849
+ warn: (message) => log.warn(redactSecrets(message, knownValues)),
56850
+ error: (message) => log.error(redactSecrets(message, knownValues)),
56851
+ ...log.child ? { child: (name) => redactLogger(log.child(name), knownValues) } : {}
56852
+ };
56853
+ }
56693
56854
 
56694
56855
  // ../agent-core/dist/lane-ledger.js
56695
56856
  import * as fs2 from "node:fs";
@@ -56730,12 +56891,209 @@ function isTypedEvent(event) {
56730
56891
  return event.type !== "message" && laneTargetUri(event) === void 0;
56731
56892
  }
56732
56893
 
56733
- // ../agent-core/dist/lane-ledger.js
56894
+ // ../agent-core/dist/lane-outcome.js
56734
56895
  function errorWireClass(cls) {
56735
56896
  if (!cls || cls === "ok" || cls === "usage_limit")
56736
56897
  return void 0;
56737
56898
  return cls;
56738
56899
  }
56900
+
56901
+ // ../agent-core/dist/runtime-instance.js
56902
+ function isTurnToken(token) {
56903
+ return token.startsWith("turn_");
56904
+ }
56905
+
56906
+ // ../agent-core/dist/lane-input-receipts.js
56907
+ var LaneInputReceipts = class {
56908
+ opts;
56909
+ confirmations;
56910
+ constructor(opts, confirmations) {
56911
+ this.opts = opts;
56912
+ this.confirmations = confirmations;
56913
+ }
56914
+ lifecycle(lane, messageIds) {
56915
+ const explicit = lane.coverageMode === "explicit";
56916
+ const unique = [...new Set(messageIds)];
56917
+ const dispatchEventIds = unique.map((messageId) => lane.folded.get(messageId)).filter((id) => Boolean(id));
56918
+ if (explicit && dispatchEventIds.length !== unique.length) {
56919
+ throw new Error(`explicit lane ${lane.lane} is missing a folded WorkItem mapping`);
56920
+ }
56921
+ if (!explicit && dispatchEventIds.length === 0)
56922
+ return void 0;
56923
+ return {
56924
+ deliveryKey: dispatchEventIds.join(","),
56925
+ dispatchEventIds,
56926
+ update: explicit ? (state, failure) => this.updateInputState(lane, dispatchEventIds, state, failure) : async () => void 0
56927
+ };
56928
+ }
56929
+ async updateInputState(lane, dispatchEventIds, state, failure) {
56930
+ lane.observedInputs ??= /* @__PURE__ */ new Map();
56931
+ for (const id of dispatchEventIds)
56932
+ lane.observedInputs.set(id, state);
56933
+ let confirmed;
56934
+ const ids = [...dispatchEventIds];
56935
+ const failed = new Set(state === "failed" ? ids : []);
56936
+ const sourceIds = [...lane.folded].filter(([, id]) => failed.has(id)).map(([id]) => id);
56937
+ const request3 = {
56938
+ lane: lane.lane,
56939
+ target_uri: lane.targetUri,
56940
+ thread_root_id: lane.threadRootId,
56941
+ dispatch_event_ids: ids,
56942
+ state,
56943
+ session_id: lane.sessionAmbiguous ? void 0 : lane.sessionId,
56944
+ ...state === "failed" && failure ? {
56945
+ outcome_class: errorWireClass(failure.outcomeClass),
56946
+ detail: failure.detail ? redactSecrets(failure.detail, this.opts.knownSecrets).slice(0, 500) : void 0
56947
+ } : {}
56948
+ };
56949
+ await this.confirmations.submit(lane.lane, {
56950
+ label: `input ${state}`,
56951
+ send: async () => {
56952
+ const restore = sourceIds.length ? this.opts.releaseLocalClaims?.(sourceIds) : void 0;
56953
+ let result;
56954
+ try {
56955
+ result = await this.opts.client.updateDispatchInputState(this.opts.orgId, request3);
56956
+ } catch (err) {
56957
+ restore?.();
56958
+ throw err;
56959
+ }
56960
+ if (result.recognized !== ids.length && !isTurnToken(lane.lane)) {
56961
+ restore?.();
56962
+ throw new ApiError(409, "input acknowledgement scope changed", "INPUT_SCOPE_CHANGED");
56963
+ }
56964
+ if (state === "failed") {
56965
+ for (const sourceId of sourceIds) {
56966
+ lane.folded.delete(sourceId);
56967
+ lane.seen.delete(sourceId);
56968
+ }
56969
+ }
56970
+ confirmed = { retry: state === "failed" && result.released > 0 };
56971
+ }
56972
+ });
56973
+ return confirmed ?? { retry: false, pending: true };
56974
+ }
56975
+ async syncAttribution(lane) {
56976
+ if (lane.settlementMode !== "receipt" || lane.settling || lane.sessionAmbiguous || !lane.sessionId || !lane.observedInputs?.size)
56977
+ return;
56978
+ const sessionId = lane.sessionId;
56979
+ const groups = /* @__PURE__ */ new Map();
56980
+ for (const [id, state] of lane.observedInputs) {
56981
+ const ids = groups.get(state) ?? [];
56982
+ ids.push(id);
56983
+ groups.set(state, ids);
56984
+ }
56985
+ for (const [state, ids] of groups) {
56986
+ await this.confirmations.submit(lane.lane, {
56987
+ label: "input session",
56988
+ send: async () => {
56989
+ await this.opts.client.updateDispatchInputState(this.opts.orgId, {
56990
+ lane: lane.lane,
56991
+ target_uri: lane.targetUri,
56992
+ thread_root_id: lane.threadRootId,
56993
+ dispatch_event_ids: ids,
56994
+ state,
56995
+ session_id: sessionId
56996
+ });
56997
+ }
56998
+ });
56999
+ }
57000
+ }
57001
+ };
57002
+
57003
+ // ../agent-core/dist/lane-confirmations.js
57004
+ var LaneConfirmations = class {
57005
+ opts;
57006
+ pending = /* @__PURE__ */ new Map();
57007
+ disposed = false;
57008
+ constructor(opts) {
57009
+ this.opts = opts;
57010
+ }
57011
+ /** Try once inline; a retained write returns without blocking native execution. */
57012
+ async submit(token, write) {
57013
+ if (this.disposed)
57014
+ return;
57015
+ let pending = this.pending.get(token);
57016
+ if (pending) {
57017
+ if (pending.blocked)
57018
+ return;
57019
+ pending.writes.push(write);
57020
+ return;
57021
+ }
57022
+ pending = { writes: [write], failures: 0, blocked: false };
57023
+ this.pending.set(token, pending);
57024
+ await this.run(token, pending);
57025
+ }
57026
+ async run(token, pending) {
57027
+ if (pending.running || pending.blocked || this.pending.get(token) !== pending)
57028
+ return;
57029
+ const recovered = pending.failures > 0;
57030
+ pending.running = Promise.resolve().then(async () => {
57031
+ while (pending.writes.length && this.pending.get(token) === pending) {
57032
+ const write = pending.writes[0];
57033
+ try {
57034
+ await write.send();
57035
+ pending.writes.shift();
57036
+ } catch (err) {
57037
+ if (this.pending.get(token) !== pending)
57038
+ return;
57039
+ if (err instanceof ApiError && err.status === 409 && err.code === "STALE_LANE") {
57040
+ this.pending.delete(token);
57041
+ this.opts.onStale(token);
57042
+ this.opts.onRecovered?.();
57043
+ return;
57044
+ }
57045
+ const transient = !(err instanceof ApiError) || err.status === 0 || err.status >= 500 || err.status === 408 || err.status === 429;
57046
+ if (!transient) {
57047
+ pending.blocked = true;
57048
+ this.opts.log?.error(`lane ${write.label} rejected for ${token}; confirmation retained: ${String(err)}`);
57049
+ return;
57050
+ }
57051
+ pending.failures += 1;
57052
+ this.opts.log?.warn(`lane ${write.label} unconfirmed for ${token}; retry ${pending.failures}: ${String(err)}`);
57053
+ const delays = this.opts.delaysMs?.length ? this.opts.delaysMs : [2e3, 1e4, 3e4, 6e4, 12e4];
57054
+ const delay = delays[Math.min(pending.failures - 1, delays.length - 1)];
57055
+ pending.timer = setTimeout(() => {
57056
+ pending.timer = void 0;
57057
+ void this.run(token, pending);
57058
+ }, delay);
57059
+ pending.timer.unref?.();
57060
+ return;
57061
+ }
57062
+ }
57063
+ if (this.pending.get(token) === pending)
57064
+ this.pending.delete(token);
57065
+ if (recovered)
57066
+ this.opts.onRecovered?.();
57067
+ });
57068
+ try {
57069
+ await pending.running;
57070
+ } finally {
57071
+ pending.running = void 0;
57072
+ }
57073
+ }
57074
+ /** Reconnect/shutdown may accelerate retries; permanent rejections stay visible. */
57075
+ async retryNow() {
57076
+ await Promise.all([...this.pending].map(async ([token, pending]) => {
57077
+ if (pending.timer)
57078
+ clearTimeout(pending.timer);
57079
+ pending.timer = void 0;
57080
+ await this.run(token, pending);
57081
+ }));
57082
+ }
57083
+ forget(token) {
57084
+ const pending = this.pending.get(token);
57085
+ if (pending?.timer)
57086
+ clearTimeout(pending.timer);
57087
+ this.pending.delete(token);
57088
+ }
57089
+ dispose() {
57090
+ this.disposed = true;
57091
+ for (const token of this.pending.keys())
57092
+ this.forget(token);
57093
+ }
57094
+ };
57095
+
57096
+ // ../agent-core/dist/lane-ledger.js
56739
57097
  var LedgerUnsupportedError = class extends Error {
56740
57098
  };
56741
57099
  function bindLaneSession(lane, agentSessionId) {
@@ -56761,8 +57119,24 @@ function isEndpointMissing2(err) {
56761
57119
  var LaneLedger = class {
56762
57120
  opts;
56763
57121
  lanes = /* @__PURE__ */ new Map();
57122
+ confirmations;
57123
+ inputs;
57124
+ claimResponses = /* @__PURE__ */ new Map();
57125
+ claims = /* @__PURE__ */ new Map();
56764
57126
  constructor(opts) {
56765
57127
  this.opts = opts;
57128
+ this.confirmations = new LaneConfirmations({
57129
+ delaysMs: opts.terminalRetryDelaysMs,
57130
+ log: opts.log,
57131
+ onRecovered: opts.onRecovered,
57132
+ onStale: (token) => {
57133
+ for (const lane of this.lanes.values()) {
57134
+ if (lane.lane === token)
57135
+ this.forgetLane(lane);
57136
+ }
57137
+ }
57138
+ });
57139
+ this.inputs = new LaneInputReceipts(opts, this.confirmations);
56766
57140
  }
56767
57141
  get contextDir() {
56768
57142
  return this.opts.contextDir;
@@ -56861,7 +57235,16 @@ ${frame}` : frame;
56861
57235
  * incumbent completes.
56862
57236
  */
56863
57237
  async ensureLane(events) {
56864
- return this.ensureLaneAttempt(events, false);
57238
+ const key = this.laneKeyFor(events[events.length - 1]);
57239
+ const prior = this.claims.get(key);
57240
+ const claim = (prior ? prior.catch(() => null) : Promise.resolve()).then(() => this.ensureLaneAttempt(events, false));
57241
+ this.claims.set(key, claim);
57242
+ try {
57243
+ return await claim;
57244
+ } finally {
57245
+ if (this.claims.get(key) === claim)
57246
+ this.claims.delete(key);
57247
+ }
56865
57248
  }
56866
57249
  /**
56867
57250
  * One ensureLane pass. `reclaimed` marks the arbitration retry: STALE_LANE
@@ -56879,22 +57262,22 @@ ${frame}` : frame;
56879
57262
  const trigger = events[events.length - 1];
56880
57263
  const laneKey = this.laneKeyFor(trigger);
56881
57264
  let lane = this.lanes.get(laneKey);
57265
+ if (lane?.settling)
57266
+ return null;
56882
57267
  const reused = lane != null;
56883
57268
  if (!lane) {
56884
57269
  const targetUri = laneTargetUri(trigger) ?? `prll://${trigger.targetId}`;
56885
- let res;
56886
- try {
56887
- res = await this.opts.client.claimDispatch(this.opts.orgId, {
56888
- target_uri: targetUri,
56889
- thread_root_id: trigger.threadRootId,
56890
- limit: 100,
56891
- coverage_mode: this.opts.coverageMode ?? "implicit"
56892
- });
56893
- } catch (err) {
56894
- if (isEndpointMissing2(err))
56895
- throw new LedgerUnsupportedError("claim endpoint unavailable");
56896
- throw err;
56897
- }
57270
+ const res = await this.claimRecoverably(laneKey, {
57271
+ target_uri: targetUri,
57272
+ thread_root_id: trigger.threadRootId,
57273
+ limit: 100,
57274
+ coverage_mode: this.opts.coverageMode ?? "implicit",
57275
+ settlement_mode: this.opts.instanceId ? this.opts.settlementMode : void 0,
57276
+ resume_received: this.opts.instanceId ? true : void 0,
57277
+ instance_id: this.opts.instanceId
57278
+ });
57279
+ if (!res)
57280
+ return null;
56898
57281
  if (!res.claimed || !res.lane) {
56899
57282
  if (res.reason === "empty") {
56900
57283
  this.opts.log?.warn(`claim for ${targetUri} came back empty \u2014 nothing foldable; leaving to the reconciler`);
@@ -56917,6 +57300,7 @@ ${frame}` : frame;
56917
57300
  targetUri,
56918
57301
  threadRootId: trigger.threadRootId,
56919
57302
  coverageMode: actualCoverage,
57303
+ settlementMode: res.settlement_mode ?? "legacy",
56920
57304
  folded: /* @__PURE__ */ new Map(),
56921
57305
  framed: [],
56922
57306
  seen: /* @__PURE__ */ new Set(),
@@ -56951,9 +57335,8 @@ ${frame}` : frame;
56951
57335
  }
56952
57336
  return null;
56953
57337
  }
56954
- this.opts.log?.warn(`steer fold failed for ${ev.messageId} \u2014 failing closed, releasing lane: ${String(err)}`);
56955
- await this.release(laneKey, "steer_failed");
56956
- return null;
57338
+ this.opts.log?.warn(`steer fold failed for ${ev.messageId}; preserving prior members: ${String(err)}`);
57339
+ continue;
56957
57340
  }
56958
57341
  }
56959
57342
  return lane;
@@ -56967,7 +57350,7 @@ ${frame}` : frame;
56967
57350
  async steerLive(event) {
56968
57351
  const laneKey = this.laneKeyFor(event);
56969
57352
  const lane = this.lanes.get(laneKey);
56970
- if (!lane)
57353
+ if (!lane || lane.settling)
56971
57354
  return null;
56972
57355
  if (lane.seen.has(event.messageId))
56973
57356
  return null;
@@ -57013,64 +57396,9 @@ ${frame}` : frame;
57013
57396
  * prompt or injection actually delivers (frame coverage ∪ buffered group).
57014
57397
  */
57015
57398
  inputLifecycleFor(lane, messageIds) {
57016
- const explicit = lane.coverageMode === "explicit";
57017
- const unique = [...new Set(messageIds)];
57018
- const dispatchEventIds = unique.map((messageId) => lane.folded.get(messageId)).filter((id) => Boolean(id));
57019
- if (explicit && dispatchEventIds.length !== unique.length) {
57020
- throw new Error(`explicit lane ${lane.lane} is missing a folded WorkItem mapping`);
57021
- }
57022
- if (!explicit && dispatchEventIds.length === 0)
57023
- return void 0;
57024
- return {
57025
- deliveryKey: dispatchEventIds.join(","),
57026
- dispatchEventIds,
57027
- update: explicit ? (state) => this.updateInputState(lane, dispatchEventIds, state) : async () => void 0
57028
- };
57399
+ return this.inputs.lifecycle(lane, messageIds);
57029
57400
  }
57030
- async updateInputState(lane, dispatchEventIds, state) {
57031
- const failed = new Set(state === "failed" ? dispatchEventIds : []);
57032
- const failedSourceIds = [...lane.folded].filter(([, dispatchEventId]) => failed.has(dispatchEventId)).map(([sourceId]) => sourceId);
57033
- const restoreLocalClaims = failedSourceIds.length > 0 ? this.opts.releaseLocalClaims?.(failedSourceIds) : void 0;
57034
- try {
57035
- const result = await this.opts.client.updateDispatchInputState(this.opts.orgId, {
57036
- lane: lane.lane,
57037
- target_uri: lane.targetUri,
57038
- thread_root_id: lane.threadRootId,
57039
- dispatch_event_ids: dispatchEventIds,
57040
- state
57041
- });
57042
- if (result.recognized !== dispatchEventIds.length) {
57043
- throw new Error(`input lifecycle ${state} recognized ${result.recognized}/${dispatchEventIds.length} WorkItems`);
57044
- }
57045
- if (state === "failed" && result.released === 0) {
57046
- restoreLocalClaims?.();
57047
- return { retry: false };
57048
- }
57049
- } catch (err) {
57050
- restoreLocalClaims?.();
57051
- throw err;
57052
- }
57053
- if (state === "failed") {
57054
- for (const [sourceId, dispatchEventId] of lane.folded) {
57055
- if (failed.has(dispatchEventId))
57056
- lane.folded.delete(sourceId);
57057
- }
57058
- }
57059
- return { retry: state === "failed" };
57060
- }
57061
- /**
57062
- * Complete the lane when no local work remains for it: the server sweeps
57063
- * still-leased members as no_action, releases the occupancy row, and
57064
- * re-drives any same-target pending work. A STALE_LANE answer means a
57065
- * takeover already owns the resource — local state is dropped either way.
57066
- */
57067
- /**
57068
- * Record that the turn on this lane surfaced a runtime error. The flow
57069
- * settles an errored lane immediately (dispatchLaneGroup returns 'failed'
57070
- * after a forced complete), so the bit normally lives for one turn only —
57071
- * it is the transport between the gateway's per-session error signal and
57072
- * this lane's complete request.
57073
- */
57401
+ /** Carry the runtime error into the lane's forced completion. */
57074
57402
  markTurnError(laneKey, info = {}) {
57075
57403
  const lane = this.lanes.get(laneKey);
57076
57404
  if (lane)
@@ -57097,62 +57425,62 @@ ${frame}` : frame;
57097
57425
  const lane = this.lanes.get(laneKey);
57098
57426
  if (!lane || hasMoreLocal)
57099
57427
  return;
57100
- this.lanes.delete(laneKey);
57101
- this.removeLaneContext(lane);
57428
+ if (lane.settling)
57429
+ return;
57430
+ lane.settling = true;
57102
57431
  const deferred = !lane.turnError && !this.deferredUnsupported ? lane.turnDeferred : void 0;
57103
57432
  const outcome = lane.turnError ? "error" : lane.turnDeferred ? this.deferredUnsupported ? "error" : "deferred" : "ok";
57104
- try {
57105
- const res = await this.opts.client.completeDispatch(this.opts.orgId, {
57106
- lane: lane.lane,
57107
- target_uri: lane.targetUri,
57108
- thread_root_id: lane.threadRootId,
57109
- // An error turn releases its members for retry; a deferred turn
57110
- // re-delivers them at retry_at without burning redrive budget
57111
- // (ignored by older servers, which 400 on the unknown enum — see the
57112
- // fallback below).
57113
- turn_outcome: outcome,
57114
- ...deferred ? {
57115
- outcome_class: deferred.outcomeClass,
57116
- ...deferred.retryAt ? { retry_at: deferred.retryAt } : {}
57117
- } : {},
57118
- // An error complete carries the bridge's classification and redacted
57119
- // evidence so the server can persist WHY on the released row
57120
- // (agent-health-alerting-design.md §4.5). usage_limit never travels
57121
- // with error: it is the deferred contract, and a server that rejected
57122
- // deferred (deferredUnsupported) also predates the class field.
57123
- ...outcome === "error" && lane.turnError ? {
57124
- ...errorWireClass(lane.turnError.outcomeClass) ? { outcome_class: errorWireClass(lane.turnError.outcomeClass) } : {},
57125
- ...lane.turnError.detail ? { detail: lane.turnError.detail.slice(0, 500) } : {}
57126
- } : {},
57127
- session_id: lane.sessionAmbiguous ? void 0 : lane.sessionId
57128
- });
57129
- if (res.swept_no_action > 0 || res.redriven) {
57130
- this.opts.log?.info(`lane complete for ${lane.targetUri}: swept ${res.swept_no_action} no_action, redriven=${res.redriven}`);
57131
- }
57132
- } catch (err) {
57133
- if (isStaleLane(err)) {
57134
- this.opts.log?.info(`lane complete skipped for ${lane.targetUri} \u2014 taken over`);
57135
- return;
57136
- }
57137
- if (outcome === "deferred" && err instanceof ApiError && err.status === 400) {
57138
- this.deferredUnsupported = true;
57139
- this.opts.log?.warn(`server rejected turn_outcome=deferred for ${lane.targetUri} \u2014 falling back to error completes`);
57433
+ const request3 = {
57434
+ lane: lane.lane,
57435
+ target_uri: lane.targetUri,
57436
+ thread_root_id: lane.threadRootId,
57437
+ // An error turn releases its members for retry; a deferred turn
57438
+ // re-delivers them at retry_at without burning redrive budget
57439
+ // (ignored by older servers, which 400 on the unknown enum — see the
57440
+ // fallback below).
57441
+ turn_outcome: outcome,
57442
+ ...deferred ? {
57443
+ outcome_class: deferred.outcomeClass,
57444
+ ...deferred.retryAt ? { retry_at: deferred.retryAt } : {}
57445
+ } : {},
57446
+ // An error complete carries the bridge's classification and redacted
57447
+ // evidence so the server can persist WHY on the released row
57448
+ // (agent-health-alerting-design.md §4.5). usage_limit never travels
57449
+ // with error: it is the deferred contract, and a server that rejected
57450
+ // deferred (deferredUnsupported) also predates the class field.
57451
+ ...outcome === "error" && lane.turnError ? {
57452
+ ...errorWireClass(lane.turnError.outcomeClass) ? { outcome_class: errorWireClass(lane.turnError.outcomeClass) } : {},
57453
+ ...lane.turnError.detail ? { detail: lane.turnError.detail.slice(0, 500) } : {}
57454
+ } : {},
57455
+ session_id: lane.sessionAmbiguous ? void 0 : lane.sessionId
57456
+ };
57457
+ let fallback = false;
57458
+ await this.confirmations.submit(lane.lane, {
57459
+ label: "complete",
57460
+ send: async () => {
57140
57461
  try {
57141
- await this.opts.client.completeDispatch(this.opts.orgId, {
57142
- lane: lane.lane,
57143
- target_uri: lane.targetUri,
57144
- thread_root_id: lane.threadRootId,
57462
+ await this.opts.client.completeDispatch(this.opts.orgId, fallback ? {
57463
+ lane: request3.lane,
57464
+ target_uri: request3.target_uri,
57465
+ thread_root_id: request3.thread_root_id,
57145
57466
  turn_outcome: "error"
57146
- });
57147
- } catch (fallbackErr) {
57148
- if (isStaleLane(fallbackErr))
57149
- return;
57150
- this.opts.log?.warn(`lane complete (deferred fallback) failed for ${lane.targetUri}: ${String(fallbackErr)}`);
57467
+ } : request3);
57468
+ } catch (err) {
57469
+ if (!fallback && outcome === "deferred" && err instanceof ApiError && err.status === 400) {
57470
+ this.deferredUnsupported = true;
57471
+ fallback = true;
57472
+ await this.opts.client.completeDispatch(this.opts.orgId, {
57473
+ lane: request3.lane,
57474
+ target_uri: request3.target_uri,
57475
+ thread_root_id: request3.thread_root_id,
57476
+ turn_outcome: "error"
57477
+ });
57478
+ } else
57479
+ throw err;
57151
57480
  }
57152
- return;
57481
+ this.forgetLane(lane);
57153
57482
  }
57154
- this.opts.log?.warn(`lane complete failed for ${lane.targetUri}: ${String(err)}`);
57155
- }
57483
+ });
57156
57484
  }
57157
57485
  /**
57158
57486
  * Renew one lane by its key — the external runtime-activity hook for
@@ -57174,6 +57502,8 @@ ${frame}` : frame;
57174
57502
  * write's incumbency check anyway.
57175
57503
  */
57176
57504
  maybeRenew(lane) {
57505
+ if (isTurnToken(lane.lane))
57506
+ return;
57177
57507
  const now = Date.now();
57178
57508
  const ttl = lane.leaseTtlMs ?? 10 * 6e4;
57179
57509
  const until = lane.leaseUntilMs ?? now;
@@ -57204,26 +57534,40 @@ ${frame}` : frame;
57204
57534
  */
57205
57535
  async release(laneKey, reason = "unspecified", info) {
57206
57536
  const lane = this.lanes.get(laneKey);
57207
- if (!lane)
57537
+ if (!lane || lane.settling)
57208
57538
  return;
57209
- this.lanes.delete(laneKey);
57210
- this.removeLaneContext(lane);
57211
- try {
57212
- const wireClass = errorWireClass(info?.outcomeClass);
57213
- const outcome = info && (wireClass || info.detail) ? {
57214
- ...wireClass ? { outcome_class: wireClass } : {},
57215
- ...info.detail ? { detail: info.detail } : {}
57216
- } : void 0;
57217
- await this.opts.client.releaseDispatchLane(this.opts.orgId, lane.lane, reason, outcome);
57218
- } catch (err) {
57219
- this.opts.log?.warn(`lane release (${reason}) failed for ${lane.targetUri}: ${String(err)}`);
57220
- }
57539
+ lane.settling = true;
57540
+ const wireClass = errorWireClass(info?.outcomeClass);
57541
+ const outcome = info && (wireClass || info.detail) ? {
57542
+ ...wireClass ? { outcome_class: wireClass } : {},
57543
+ ...info.detail ? { detail: redactSecrets(info.detail, this.opts.knownSecrets).slice(0, 500) } : {}
57544
+ } : void 0;
57545
+ await this.confirmations.submit(lane.lane, {
57546
+ label: "release",
57547
+ send: async () => {
57548
+ await this.opts.client.releaseDispatchLane(this.opts.orgId, lane.lane, reason, outcome);
57549
+ this.forgetLane(lane);
57550
+ }
57551
+ });
57221
57552
  }
57222
57553
  async releaseAll(reason = "shutdown") {
57554
+ await this.confirmations.retryNow();
57223
57555
  const keys = [...this.lanes.keys()];
57224
57556
  for (const key of keys) {
57225
57557
  await this.release(key, reason);
57226
57558
  }
57559
+ for (const [key, claim] of this.claimResponses) {
57560
+ if (!claim.response?.lane)
57561
+ continue;
57562
+ const token = claim.response.lane;
57563
+ await this.confirmations.submit(token, {
57564
+ label: "unconsumed claim release",
57565
+ send: async () => {
57566
+ await this.opts.client.releaseDispatchLane(this.opts.orgId, token, reason);
57567
+ this.claimResponses.delete(key);
57568
+ }
57569
+ });
57570
+ }
57227
57571
  }
57228
57572
  /** True when any lane is currently active (used by shutdown logging). */
57229
57573
  get activeCount() {
@@ -57238,18 +57582,57 @@ ${frame}` : frame;
57238
57582
  * — the caller must skip processing.
57239
57583
  */
57240
57584
  async claimTyped(ref) {
57241
- let res;
57585
+ const key = `typed:${ref.dispatchEventId ?? `${ref.sourceType}:${ref.sourceId}`}`;
57586
+ if (this.claims.has(key))
57587
+ return null;
57588
+ if (ref.dispatchEventId && this.lanes.has(laneKeyForTarget(`dsp:${ref.dispatchEventId}`)))
57589
+ return null;
57590
+ const claim = this.claimTypedAttempt(ref);
57591
+ this.claims.set(key, claim);
57242
57592
  try {
57243
- res = await this.opts.client.claimDispatch(this.opts.orgId, {
57244
- dispatch_event_id: ref.dispatchEventId,
57245
- source_type: ref.dispatchEventId ? void 0 : ref.sourceType,
57246
- source_id: ref.dispatchEventId ? void 0 : ref.sourceId
57593
+ return await claim;
57594
+ } finally {
57595
+ this.claims.delete(key);
57596
+ }
57597
+ }
57598
+ async claimRecoverably(key, request3) {
57599
+ let claim = this.claimResponses.get(key);
57600
+ if (!claim) {
57601
+ claim = { request: request3 };
57602
+ this.claimResponses.set(key, claim);
57603
+ const captured = claim;
57604
+ await this.confirmations.submit(`claim:${key}`, {
57605
+ label: "claim",
57606
+ send: async () => {
57607
+ try {
57608
+ captured.response = await this.opts.client.claimDispatch(this.opts.orgId, request3);
57609
+ } catch (err) {
57610
+ if (isEndpointMissing2(err))
57611
+ captured.unsupported = true;
57612
+ else
57613
+ throw err;
57614
+ }
57615
+ }
57247
57616
  });
57248
- } catch (err) {
57249
- if (isEndpointMissing2(err))
57250
- throw new LedgerUnsupportedError("claim endpoint unavailable");
57251
- throw err;
57252
57617
  }
57618
+ if (claim.unsupported) {
57619
+ this.claimResponses.delete(key);
57620
+ throw new LedgerUnsupportedError("claim endpoint unavailable");
57621
+ }
57622
+ if (!claim.response)
57623
+ return null;
57624
+ this.claimResponses.delete(key);
57625
+ return claim.response;
57626
+ }
57627
+ async claimTypedAttempt(ref) {
57628
+ const res = await this.claimRecoverably(`typed:${ref.dispatchEventId ?? `${ref.sourceType}:${ref.sourceId}`}`, {
57629
+ dispatch_event_id: ref.dispatchEventId,
57630
+ source_type: ref.dispatchEventId ? void 0 : ref.sourceType,
57631
+ source_id: ref.dispatchEventId ? void 0 : ref.sourceId,
57632
+ instance_id: this.opts.instanceId
57633
+ });
57634
+ if (!res)
57635
+ return null;
57253
57636
  if (!res.claimed || !res.lane || !res.events?.length) {
57254
57637
  if (res.reason === "stale") {
57255
57638
  this.opts.log?.info(`typed dispatch ${ref.dispatchEventId ?? `${ref.sourceType}:${ref.sourceId}`} resolved by the server as stale (${res.stale_reason ?? "unspecified"})`);
@@ -57261,6 +57644,8 @@ ${frame}` : frame;
57261
57644
  }
57262
57645
  const workItem = res.events[0];
57263
57646
  const targetUri = `dsp:${workItem.id}`;
57647
+ if (this.lanes.has(laneKeyForTarget(targetUri)))
57648
+ return null;
57264
57649
  const leaseUntilMs = Date.parse(res.lease_until ?? "");
57265
57650
  const lane = {
57266
57651
  laneKey: laneKeyForTarget(targetUri),
@@ -57289,7 +57674,34 @@ ${frame}` : frame;
57289
57674
  const lane = this.lanes.get(laneKey);
57290
57675
  if (!lane)
57291
57676
  return;
57292
- this.lanes.delete(laneKey);
57677
+ this.confirmations.forget(lane.lane);
57678
+ this.forgetLane(lane);
57679
+ }
57680
+ syncInputAttribution(lane) {
57681
+ return this.inputs.syncAttribution(lane);
57682
+ }
57683
+ async confirmTyped(lane, send) {
57684
+ if (lane.settling)
57685
+ return;
57686
+ lane.settling = true;
57687
+ await this.confirmations.submit(lane.lane, {
57688
+ label: "typed complete",
57689
+ send: async () => {
57690
+ await send();
57691
+ this.forgetLane(lane);
57692
+ }
57693
+ });
57694
+ }
57695
+ retryConfirmations() {
57696
+ return this.confirmations.retryNow();
57697
+ }
57698
+ dispose() {
57699
+ this.confirmations.dispose();
57700
+ }
57701
+ forgetLane(lane) {
57702
+ if (this.lanes.get(lane.laneKey) !== lane)
57703
+ return;
57704
+ this.lanes.delete(lane.laneKey);
57293
57705
  this.removeLaneContext(lane);
57294
57706
  }
57295
57707
  /**
@@ -58232,7 +58644,7 @@ async function dispatchLaneGroup(host, opts) {
58232
58644
  }
58233
58645
  async function dispatchLaneGroupInner(host, opts) {
58234
58646
  const ledger = host.laneLedger;
58235
- const event = opts.events[opts.events.length - 1];
58647
+ let event = opts.events[opts.events.length - 1];
58236
58648
  let lane;
58237
58649
  try {
58238
58650
  lane = await runInPhaseSpan("claim", () => ledger.ensureLane(opts.events));
@@ -58250,18 +58662,51 @@ async function dispatchLaneGroupInner(host, opts) {
58250
58662
  }
58251
58663
  return "foreign";
58252
58664
  }
58665
+ const foldedEvents = opts.events.filter((ev) => lane.folded.has(ev.messageId));
58666
+ for (const ev of opts.events) {
58667
+ if (!lane.folded.has(ev.messageId))
58668
+ host.dispatchedMessages.delete(ev.messageId);
58669
+ }
58670
+ if (foldedEvents.length === 0) {
58671
+ await ledger.completeIfIdle(lane.laneKey, unsettledInjections(host, opts.sessionKey) || opts.hasMoreLocal());
58672
+ return "foreign";
58673
+ }
58674
+ opts = { ...opts, events: foldedEvents, earlier: foldedEvents.slice(0, -1) };
58675
+ event = foldedEvents[foldedEvents.length - 1];
58253
58676
  const pending = ledger.takeFrame(lane);
58254
58677
  const frame = pending.frame;
58255
58678
  if (!frame && opts.events.every((ev) => lane.seen.has(ev.messageId))) {
58256
58679
  host.opts.log?.info(`lane group for ${event.messageId} already rendered by the server frame \u2014 no turn`);
58257
58680
  const acknowledge = host.opts.dispatchAdapter.acknowledgeDiscardedInjection?.bind(host.opts.dispatchAdapter);
58681
+ const withdrawn = [];
58258
58682
  if (acknowledge) {
58259
- for (const ev of opts.events) {
58260
- const deliveryKey = lane.folded.get(ev.messageId);
58261
- if (deliveryKey)
58262
- acknowledge(opts.sessionKey, deliveryKey);
58263
- }
58683
+ try {
58684
+ for (const ev of opts.events) {
58685
+ const deliveryKey = lane.folded.get(ev.messageId);
58686
+ if (!deliveryKey)
58687
+ continue;
58688
+ if (await acknowledge(opts.sessionKey, deliveryKey) === "cancelled")
58689
+ withdrawn.push(ev);
58690
+ }
58691
+ } catch (err) {
58692
+ host.opts.log?.warn(`discarded input acknowledgement failed for ${event.messageId}: ${String(err)}`);
58693
+ }
58694
+ }
58695
+ if (withdrawn.length > 0 && !opts.withdrawnRetry) {
58696
+ for (const ev of withdrawn)
58697
+ host.dispatchedMessages.delete(ev.messageId);
58698
+ await ledger.completeIfIdle(lane.laneKey, unsettledInjections(host, opts.sessionKey));
58699
+ if (ledger.getForEvent(event))
58700
+ return "no_turn";
58701
+ return dispatchLaneGroupInner(host, {
58702
+ ...opts,
58703
+ events: withdrawn,
58704
+ earlier: withdrawn.slice(0, -1),
58705
+ withdrawnRetry: true
58706
+ });
58264
58707
  }
58708
+ for (const ev of opts.events)
58709
+ host.dispatchedMessages.delete(ev.messageId);
58265
58710
  await runInPhaseSpan("settle", () => ledger.completeIfIdle(lane.laneKey, unsettledInjections(host, opts.sessionKey) || opts.hasMoreLocal()));
58266
58711
  return "no_turn";
58267
58712
  }
@@ -58447,24 +58892,35 @@ async function consumeTypedDispatch(host, ref, run, hooks) {
58447
58892
  viaLegacyAck = true;
58448
58893
  resolved = settleAck(await runInPhaseSpan("settle", () => hooks.legacyAck(claimed.typedDispatchEventId)));
58449
58894
  } else {
58450
- const outcome = await runInPhaseSpan("settle", () => resolveDispatchByID(host, claimed.typedDispatchEventId, claimed.lane));
58451
- if (outcome === "unsupported") {
58452
- host.typedByIdCompleteUnsupported = true;
58453
- host.opts.log?.warn("server predates the by-id dispatch complete \u2014 falling back to the legacy typed ack");
58454
- viaLegacyAck = true;
58455
- resolved = settleAck(await runInPhaseSpan("settle", () => hooks.legacyAck(claimed.typedDispatchEventId)));
58456
- } else if (outcome === "stale") {
58457
- resolved = true;
58458
- } else {
58459
- resolved = outcome === "ok";
58460
- }
58895
+ await runInPhaseSpan("settle", () => ledger.confirmTyped(claimed, async () => {
58896
+ try {
58897
+ await host.opts.client.completeDispatch(host.opts.config.org_id, {
58898
+ dispatch_event_id: claimed.typedDispatchEventId,
58899
+ lane: claimed.lane,
58900
+ turn_outcome: "ok"
58901
+ });
58902
+ } catch (err) {
58903
+ if (!isByIDCompleteUnsupported(err))
58904
+ throw err;
58905
+ host.typedByIdCompleteUnsupported = true;
58906
+ if (!settleAck(await hooks.legacyAck(claimed.typedDispatchEventId))) {
58907
+ throw new Error("legacy typed acknowledgement unconfirmed");
58908
+ }
58909
+ await host.opts.client.releaseDispatchLane(host.opts.config.org_id, claimed.lane);
58910
+ }
58911
+ }));
58912
+ resolved = true;
58461
58913
  }
58462
58914
  }
58915
+ } catch (err) {
58916
+ ledger.markTurnError(claimed.laneKey, releaseErrorInfo(err, [host.opts.config.api_key]));
58917
+ throw err;
58463
58918
  } finally {
58464
58919
  settle(resolved);
58465
58920
  span.setAttribute("dispatch.lane_result", resolved ? "dispatched" : "released");
58466
58921
  if (resolved && !viaLegacyAck) {
58467
- ledger.dropLocal(claimed.laneKey);
58922
+ if (!claimed.settling)
58923
+ ledger.dropLocal(claimed.laneKey);
58468
58924
  } else {
58469
58925
  await runInPhaseSpan("settle", () => ledger.completeIfIdle(claimed.laneKey, false).catch(() => {
58470
58926
  }));
@@ -58904,6 +59360,19 @@ var DispatchInactivityDeadline = class {
58904
59360
  this.onExpire();
58905
59361
  }, delayMs);
58906
59362
  }
59363
+ /**
59364
+ * Re-arm after an expiry that was only reported, not acted on (the
59365
+ * no-abort default: a long silent turn is logged at intervals, never
59366
+ * killed — attached-turn-ownership).
59367
+ */
59368
+ rearm() {
59369
+ if (this.disposed || this.timeoutMs <= 0)
59370
+ return;
59371
+ this.expired = false;
59372
+ this.lastActivityAt = Date.now();
59373
+ if (!this.timer)
59374
+ this.arm(this.timeoutMs);
59375
+ }
58907
59376
  dispose() {
58908
59377
  if (this.disposed)
58909
59378
  return;
@@ -59813,6 +60282,7 @@ function recordToolCall(sessionKey) {
59813
60282
  }
59814
60283
 
59815
60284
  // ../agent-core/dist/gateway-base.js
60285
+ var AUTH_INVALID_EXIT_CODE = 86;
59816
60286
  var TYPED_EVENT_KINDS = {
59817
60287
  task_assign: { type: "task", ackSourceType: "task_activity" },
59818
60288
  task_update: { type: "task", ackSourceType: "task_activity" },
@@ -59982,14 +60452,9 @@ var ParallAgentGateway = class {
59982
60452
  pendingRestartNotification = null;
59983
60453
  laneLedger;
59984
60454
  stepPersister;
59985
- // Single entry point for session active/idle writes — serialized
59986
- // desired-state reconciler (see session-lifecycle.ts). The gateway only
59987
- // declares turn boundaries; ordering, retries and stale-finish rejection
59988
- // live in the coordinator.
60455
+ // Session lifecycle owns ordering, retries and stale-finish rejection.
59989
60456
  sessionLifecycle;
59990
- // Normal fork teardown use case: seal → drain → close → release. The
59991
- // gateway only triggers it; ordering and ownership live in the finalizer
59992
- // (see fork-session-finalizer.ts).
60457
+ // ForkSessionFinalizer owns seal → drain → close → release.
59993
60458
  forkFinalizer;
59994
60459
  // Sticky fallback: flipped when the server predates the ledger (claim
59995
60460
  // endpoint 404) so every subsequent dispatch uses the legacy flow.
@@ -60005,21 +60470,30 @@ var ParallAgentGateway = class {
60005
60470
  SHUTDOWN_DEADLINE_MS;
60006
60471
  FORK_DEADLINE_MS;
60007
60472
  DISPATCH_DEADLINE_MS;
60473
+ INACTIVITY_WARN_MS;
60008
60474
  constructor(opts) {
60009
60475
  this.opts = opts;
60476
+ opts = this.opts = { ...opts, log: redactLogger(opts.log, [opts.config.api_key]) };
60010
60477
  if (opts.dispatchContextDir) {
60478
+ const recovery = new DispatchRecovery(() => this.catchUpFromDispatch(), () => this.shuttingDown, (err) => opts.log?.warn(`dispatch recovery failed: ${String(err)}`));
60011
60479
  this.laneLedger = new LaneLedger({
60012
60480
  client: opts.client,
60013
60481
  orgId: opts.config.org_id,
60014
60482
  contextDir: opts.dispatchContextDir,
60015
60483
  log: opts.log,
60484
+ knownSecrets: [opts.config.api_key],
60016
60485
  coverageMode: opts.dispatchAdapter.inputLifecycleMode ?? "implicit",
60486
+ settlementMode: opts.dispatchAdapter.inputSettlementMode,
60487
+ instanceId: opts.instanceId,
60488
+ terminalRetryDelaysMs: opts.laneTerminalRetryDelaysMs,
60489
+ onRecovered: recovery.request,
60017
60490
  releaseLocalClaims: (sourceIds) => releaseLocalMessageClaims(this.dispatchedMessages, sourceIds)
60018
60491
  });
60019
60492
  }
60020
60493
  this.SHUTDOWN_DEADLINE_MS = opts.shutdownDeadlineMs ?? 6e4;
60021
- this.FORK_DEADLINE_MS = opts.forkDeadlineMs ?? 2 * 60 * 6e4;
60022
- this.DISPATCH_DEADLINE_MS = opts.dispatchDeadlineMs ?? 20 * 6e4;
60494
+ this.FORK_DEADLINE_MS = opts.forkDeadlineMs ?? 0;
60495
+ this.DISPATCH_DEADLINE_MS = opts.dispatchDeadlineMs ?? 0;
60496
+ this.INACTIVITY_WARN_MS = opts.inactivityWarnMs ?? 30 * 6e4;
60023
60497
  this.unsubscribeRuntimeActivity = opts.dispatchAdapter.subscribeRuntimeActivity?.((event) => this.handleRuntimeActivity(event));
60024
60498
  this.stepPersister = new StepPersister({
60025
60499
  client: opts.client,
@@ -60097,12 +60571,34 @@ var ParallAgentGateway = class {
60097
60571
  }
60098
60572
  });
60099
60573
  this.opts.log?.info(`connecting to ${this.opts.connectionLabel ?? "Parall WS"}...`);
60574
+ let settled = false;
60575
+ let onSettled = null;
60576
+ const finish = async (opts) => {
60577
+ if (settled)
60578
+ return;
60579
+ settled = true;
60580
+ await this.shutdown(opts);
60581
+ onSettled?.();
60582
+ };
60583
+ ws.onAuthInvalid((event) => {
60584
+ const exitCode = this.opts.authInvalidExitCode === void 0 ? AUTH_INVALID_EXIT_CODE : this.opts.authInvalidExitCode;
60585
+ log?.error(`AUTH-INVALID agent=${this.opts.agentUserId} status=${event.status}${event.code ? ` code=${event.code}` : ""} \u2014 the server rejected this runtime's credential (${event.message}); the key is invalid, expired, or revoked.`);
60586
+ if (exitCode === null) {
60587
+ log?.error("AUTH-INVALID: reconnects are stopped and this account gateway has shut down. Refresh this account\u2019s credential and start it again; other accounts in this process are unaffected.");
60588
+ } else {
60589
+ log?.error(`AUTH-INVALID: reconnects are stopped. Exiting with code ${exitCode} so the supervisor can re-mint the launch credential and restart this agent; if no supervisor manages this process, replace its PRLL_API_KEY and start it again.`);
60590
+ process.exitCode = exitCode;
60591
+ }
60592
+ void finish({ skipDrain: true });
60593
+ });
60100
60594
  await ws.connect();
60101
60595
  return new Promise((resolve3) => {
60102
- abortSignal.addEventListener("abort", async () => {
60103
- await this.shutdown();
60596
+ if (settled) {
60104
60597
  resolve3();
60105
- });
60598
+ return;
60599
+ }
60600
+ onSettled = resolve3;
60601
+ abortSignal.addEventListener("abort", () => void finish());
60106
60602
  });
60107
60603
  }
60108
60604
  tryClaimMessage(id) {
@@ -60183,12 +60679,6 @@ var ParallAgentGateway = class {
60183
60679
  * this safe: every bridge emits its error events before the turn-boundary
60184
60680
  * turn_outcome.
60185
60681
  */
60186
- /**
60187
- * Redaction for every runtime-error text that gets persisted or shown: the
60188
- * turn-outcome detail, the runtime error step and the dispatch-failure
60189
- * step all surface in the product, and a runtime error can echo the agent
60190
- * key or a provider credential.
60191
- */
60192
60682
  redactRuntimeText(text) {
60193
60683
  return redactSecrets(text, [this.opts.config.api_key]).slice(0, 2e3);
60194
60684
  }
@@ -60526,6 +61016,7 @@ var ParallAgentGateway = class {
60526
61016
  step_id: null,
60527
61017
  dispatch_event_id: activeLane?.typedDispatchEventId ?? activeLane?.folded.get(event.messageId) ?? null,
60528
61018
  lane: activeLane?.lane ?? null,
61019
+ settlement_mode: activeLane?.settlementMode ?? "legacy",
60529
61020
  target_uri: activeLane?.targetUri ?? null,
60530
61021
  thread_root_id: activeLane?.threadRootId ?? null,
60531
61022
  // Typed binding hint for the CLI: which task this dispatch is about
@@ -60545,13 +61036,19 @@ var ParallAgentGateway = class {
60545
61036
  threadRootId: contextBody.thread_root_id
60546
61037
  });
60547
61038
  this.inFlightDispatches++;
60548
- const dispatchDeadline = this.dispatchInactivityDeadlines.start(sessionKey, this.DISPATCH_DEADLINE_MS, () => {
60549
- this.opts.log?.warn(`dispatch inactivity deadline exceeded (${this.DISPATCH_DEADLINE_MS}ms) for ${event.messageId} on ${sessionKey}; aborting`);
60550
- try {
60551
- this.opts.dispatchAdapter.abortDispatch?.(sessionKey);
60552
- } catch (err) {
60553
- this.opts.log?.warn(`abortDispatch threw for ${sessionKey}: ${String(err)}`);
61039
+ const abortAfterMs = this.DISPATCH_DEADLINE_MS;
61040
+ const dispatchDeadline = this.dispatchInactivityDeadlines.start(sessionKey, abortAfterMs > 0 ? abortAfterMs : this.INACTIVITY_WARN_MS, () => {
61041
+ if (abortAfterMs > 0) {
61042
+ this.opts.log?.warn(`dispatch inactivity deadline exceeded (${abortAfterMs}ms) for ${event.messageId} on ${sessionKey}; aborting`);
61043
+ try {
61044
+ this.opts.dispatchAdapter.abortDispatch?.(sessionKey);
61045
+ } catch (err) {
61046
+ this.opts.log?.warn(`abortDispatch threw for ${sessionKey}: ${String(err)}`);
61047
+ }
61048
+ return;
60554
61049
  }
61050
+ this.opts.log?.warn(`no runtime output for ${Math.round(this.INACTIVITY_WARN_MS / 6e4)} min on ${sessionKey} (${event.messageId}); the turn stays open \u2014 only the runtime finishing or a process restart ends it`);
61051
+ dispatchDeadline.rearm();
60555
61052
  });
60556
61053
  let binding = this.sessionBindings.get(sessionKey);
60557
61054
  let inputStepsCreated = false;
@@ -60582,6 +61079,7 @@ var ParallAgentGateway = class {
60582
61079
  binding = await runInPhaseSpan("session", () => this.bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath, laneContextFilePath2));
60583
61080
  if (activeLane) {
60584
61081
  bindLaneSession(activeLane, binding.agentSessionId);
61082
+ await this.laneLedger?.syncInputAttribution(activeLane);
60585
61083
  }
60586
61084
  if (event.targetType === "channel_conversation" && binding.agentSessionId !== priorAgentSessionId) {
60587
61085
  try {
@@ -61221,9 +61719,11 @@ var ParallAgentGateway = class {
61221
61719
  const activeFork = createActiveForkState(fork, event.targetId, continuationPrefix);
61222
61720
  this.forkStates.set(event.targetId, activeFork);
61223
61721
  this.dispatchState.activeForks.set(event.targetId, fork.sessionKey);
61224
- activeFork.deadlineTimer = setTimeout(() => {
61225
- this.abortFork(event.targetId, `deadline exceeded (${this.FORK_DEADLINE_MS}ms)`);
61226
- }, this.FORK_DEADLINE_MS);
61722
+ if (this.FORK_DEADLINE_MS > 0) {
61723
+ activeFork.deadlineTimer = setTimeout(() => {
61724
+ this.abortFork(event.targetId, `deadline exceeded (${this.FORK_DEADLINE_MS}ms)`);
61725
+ }, this.FORK_DEADLINE_MS);
61726
+ }
61227
61727
  const firstEventPromise = new Promise((resolve3) => {
61228
61728
  activeFork.queue.push({ event, resolve: resolve3 });
61229
61729
  });
@@ -61422,12 +61922,16 @@ ${fullSummary}` : fullSummary;
61422
61922
  this.sessionId = data.session_id ?? "";
61423
61923
  if (this.forkStates.size > 0) {
61424
61924
  const targetIds = [...this.forkStates.keys()];
61425
- log?.info(`aborting ${targetIds.length} active fork(s) on reconnect`);
61426
- for (const targetId of targetIds) {
61427
- this.abortFork(targetId, "ws reconnect");
61925
+ if (this.opts.instanceId) {
61926
+ log?.info(`${targetIds.length} active fork(s) continue across the reconnect`);
61927
+ } else {
61928
+ log?.info(`aborting ${targetIds.length} active fork(s) on reconnect`);
61929
+ for (const targetId of targetIds) {
61930
+ this.abortFork(targetId, "ws reconnect");
61931
+ }
61428
61932
  }
61429
61933
  }
61430
- if (this.laneLedger && this.inFlightDispatches === 0 && this.laneLedger.activeCount > 0) {
61934
+ if (this.laneLedger && !this.opts.instanceId && this.inFlightDispatches === 0 && this.laneLedger.activeCount > 0) {
61431
61935
  log?.info(`releasing ${this.laneLedger.activeCount} stale lane(s) on reconnect`);
61432
61936
  await this.laneLedger.releaseAll();
61433
61937
  }
@@ -61474,6 +61978,7 @@ ${fullSummary}` : fullSummary;
61474
61978
  uptime: os.uptime()
61475
61979
  });
61476
61980
  }, intervalSec * 1e3);
61981
+ await this.laneLedger?.retryConfirmations();
61477
61982
  this.catchUpFromDispatch().catch((err) => {
61478
61983
  log?.warn(`dispatch catch-up failed: ${String(err)}`);
61479
61984
  });
@@ -61559,10 +62064,12 @@ ${fullSummary}` : fullSummary;
61559
62064
  notifyDrainWaiters() {
61560
62065
  this.drainGate.notify();
61561
62066
  }
61562
- async shutdown() {
62067
+ async shutdown(opts = { skipDrain: false }) {
61563
62068
  this.shuttingDown = true;
61564
62069
  this.idleCompact.abort?.();
61565
- if (!this.isDrained()) {
62070
+ if (opts.skipDrain && !this.isDrained()) {
62071
+ this.opts.log?.warn(`skipping drain (credential rejected): ${this.inFlightDispatches} dispatch(es), ${this.inFlightRuntimeTurns} runtime-initiated turn(s) abandoned to server redelivery`);
62072
+ } else if (!this.isDrained()) {
61566
62073
  this.opts.log?.info(`draining ${this.inFlightDispatches} in-flight dispatch(es), ${this.inFlightRuntimeTurns} runtime-initiated turn(s), runtime busy=${this.adapterBusy()}, deadline ${this.SHUTDOWN_DEADLINE_MS}ms`);
61567
62074
  await this.drainGate.wait(this.SHUTDOWN_DEADLINE_MS);
61568
62075
  if (!this.isDrained()) {
@@ -61573,7 +62080,7 @@ ${fullSummary}` : fullSummary;
61573
62080
  }
61574
62081
  if (this.heartbeatTimer)
61575
62082
  clearInterval(this.heartbeatTimer);
61576
- if (this.laneLedger && this.laneLedger.activeCount > 0) {
62083
+ if (this.laneLedger) {
61577
62084
  this.opts.log?.info(`releasing ${this.laneLedger.activeCount} lane(s) on shutdown`);
61578
62085
  await this.laneLedger.releaseAll();
61579
62086
  }
@@ -61593,6 +62100,8 @@ ${fullSummary}` : fullSummary;
61593
62100
  this.opts.log?.warn(`${lifecycleRemaining} session lifecycle write(s) unreconciled at shutdown`);
61594
62101
  }
61595
62102
  this.sessionLifecycle.dispose();
62103
+ await this.laneLedger?.retryConfirmations();
62104
+ this.laneLedger?.dispose();
61596
62105
  this.opts.ws.disconnect();
61597
62106
  this.unsubscribeRuntimeActivity?.();
61598
62107
  this.opts.log?.info(`disconnected`);
@@ -63365,13 +63874,18 @@ var parallGateway = {
63365
63874
  runtimeKey: orchestratorKey,
63366
63875
  runtimeRef: { hostname: os2.hostname(), pid: process.pid },
63367
63876
  dispatchAdapter,
63877
+ // One OpenClaw host embeds many account gateways in one process: a
63878
+ // rejected credential stops THIS account's gateway only — it must
63879
+ // not set the host-wide process exit code or claim the process is
63880
+ // exiting.
63881
+ authInvalidExitCode: null,
63368
63882
  // Opts openclaw into the dispatch ledger (claim/fold/complete +
63369
63883
  // idempotent reply effects) — the same shared-gateway machinery
63370
63884
  // claude/codex ride; openclaw stays buffer-only (no mid-turn steer),
63371
63885
  // which the ledger does not require. Replies already flow through
63372
63886
  // @parall/cli, which reads PRLL_CONTEXT_DIR (injected in hooks.ts)
63373
63887
  // to bind dispatch_lane + reply effect keys.
63374
- // Design: docs/engineering-design/dispatch-convergence-design.md §7 (S2).
63888
+ // Design: docs/engineering-design/agent-dispatch-idempotency-design.md#effects.
63375
63889
  dispatchContextDir: dispatchLaneContextDir(stateDir),
63376
63890
  // Per-session context file (PRLL_CONTEXT_FILE contract) — the CLI's
63377
63891
  // TYPED dispatch binding (parall tasks update → task_update effect)