@parall/daemon 1.37.0 → 1.39.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.
- package/bundle/manifest.json +11 -11
- package/bundle/parall-browser-pod.js +72 -1
- package/bundle/parall-claude-agent.js +887 -224
- package/bundle/parall-codex-agent.js +894 -233
- package/bundle/parall-daemon.js +100 -1
- package/package.json +6 -6
|
@@ -17718,9 +17718,9 @@ var require_getMachineId_linux = __commonJS({
|
|
|
17718
17718
|
var api_1 = (init_esm(), __toCommonJS(esm_exports));
|
|
17719
17719
|
async function getMachineId() {
|
|
17720
17720
|
const paths = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
|
|
17721
|
-
for (const
|
|
17721
|
+
for (const path9 of paths) {
|
|
17722
17722
|
try {
|
|
17723
|
-
const result = await fs_1.promises.readFile(
|
|
17723
|
+
const result = await fs_1.promises.readFile(path9, { encoding: "utf8" });
|
|
17724
17724
|
return result.trim();
|
|
17725
17725
|
} catch (e) {
|
|
17726
17726
|
api_1.diag.debug(`error reading machine id: ${e}`);
|
|
@@ -21123,7 +21123,7 @@ function appendRootPathToUrlIfNeeded(url) {
|
|
|
21123
21123
|
return void 0;
|
|
21124
21124
|
}
|
|
21125
21125
|
}
|
|
21126
|
-
function appendResourcePathToUrl(url,
|
|
21126
|
+
function appendResourcePathToUrl(url, path9) {
|
|
21127
21127
|
try {
|
|
21128
21128
|
new URL(url);
|
|
21129
21129
|
} catch (_a) {
|
|
@@ -21133,11 +21133,11 @@ function appendResourcePathToUrl(url, path8) {
|
|
|
21133
21133
|
if (!url.endsWith("/")) {
|
|
21134
21134
|
url = url + "/";
|
|
21135
21135
|
}
|
|
21136
|
-
url +=
|
|
21136
|
+
url += path9;
|
|
21137
21137
|
try {
|
|
21138
21138
|
new URL(url);
|
|
21139
21139
|
} catch (_b) {
|
|
21140
|
-
diag2.warn("Configuration: Provided URL appended with '" +
|
|
21140
|
+
diag2.warn("Configuration: Provided URL appended with '" + path9 + "' is not a valid URL, using 'undefined' instead of '" + url + "'");
|
|
21141
21141
|
return void 0;
|
|
21142
21142
|
}
|
|
21143
21143
|
return url;
|
|
@@ -26474,6 +26474,19 @@ function parseProviderConfig(env) {
|
|
|
26474
26474
|
}
|
|
26475
26475
|
}
|
|
26476
26476
|
|
|
26477
|
+
// ts/agent-core/dist/lane-key.js
|
|
26478
|
+
import * as path from "node:path";
|
|
26479
|
+
function laneKeyForTarget(targetUri, threadRootId) {
|
|
26480
|
+
return Buffer.from(`${targetUri}
|
|
26481
|
+
${threadRootId ?? ""}`, "utf8").toString("base64url");
|
|
26482
|
+
}
|
|
26483
|
+
function dispatchLaneContextDir(stateDir) {
|
|
26484
|
+
return path.join(stateDir, "dispatch-lane-context");
|
|
26485
|
+
}
|
|
26486
|
+
function laneContextFilePath(contextDir, targetUri, threadRootId) {
|
|
26487
|
+
return path.join(contextDir, `${laneKeyForTarget(targetUri, threadRootId)}.json`);
|
|
26488
|
+
}
|
|
26489
|
+
|
|
26477
26490
|
// ts/agent-core/dist/session-state.js
|
|
26478
26491
|
function normalizeSessionKey(sessionKey) {
|
|
26479
26492
|
return sessionKey.toLowerCase();
|
|
@@ -27102,8 +27115,8 @@ function childLogger(logger, sub) {
|
|
|
27102
27115
|
|
|
27103
27116
|
// ts/agent-core/dist/gateway-base.js
|
|
27104
27117
|
import * as os from "node:os";
|
|
27105
|
-
import * as
|
|
27106
|
-
import * as
|
|
27118
|
+
import * as fs2 from "node:fs";
|
|
27119
|
+
import * as path2 from "node:path";
|
|
27107
27120
|
|
|
27108
27121
|
// ts/sdk/dist/types.js
|
|
27109
27122
|
var MENTION_ALL_USER_ID = "all";
|
|
@@ -27378,6 +27391,11 @@ var ENDPOINTS = {
|
|
|
27378
27391
|
DISPATCH_ACK_BY_ID: (orgId, id) => `${API_BASE}/orgs/${orgId}/dispatch/${id}/ack`,
|
|
27379
27392
|
DISPATCH_EXPIRE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/expire`,
|
|
27380
27393
|
DISPATCH_BY_MESSAGES: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/by-messages`,
|
|
27394
|
+
DISPATCH_CLAIM: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/claim`,
|
|
27395
|
+
DISPATCH_STEER: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/steer`,
|
|
27396
|
+
DISPATCH_COMPLETE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/complete`,
|
|
27397
|
+
DISPATCH_RELEASE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/release`,
|
|
27398
|
+
DISPATCH_HEARTBEAT: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/heartbeat`,
|
|
27381
27399
|
// Unread
|
|
27382
27400
|
UNREAD: `${API_BASE}/me/unread`,
|
|
27383
27401
|
ORG_UNREAD: (orgId) => `${API_BASE}/orgs/${orgId}/unread`,
|
|
@@ -27439,7 +27457,13 @@ var ENDPOINTS = {
|
|
|
27439
27457
|
// the machine control-plane request/reply bridge that lives in api-server.
|
|
27440
27458
|
BROWSER_PROFILE_VIEWER_COMMAND: (orgId, profileId) => `${API_BASE}/orgs/${orgId}/browser-profiles/${profileId}/viewer/command`,
|
|
27441
27459
|
// Clip registry (global, served by clip-service → Pinix Hub proxy)
|
|
27442
|
-
CLIP_REGISTRY: () => `${CLIP_BASE}/registry/clips
|
|
27460
|
+
CLIP_REGISTRY: () => `${CLIP_BASE}/registry/clips`,
|
|
27461
|
+
// Edge device endpoints
|
|
27462
|
+
ORG_EDGE_DEVICES: (orgId) => `/api/v1/orgs/${orgId}/edge/devices`,
|
|
27463
|
+
ORG_EDGE_ONBOARDING: (orgId) => `/api/v1/orgs/${orgId}/edge/onboarding`,
|
|
27464
|
+
ORG_EDGE_PROFILES: (orgId, edgeId) => `/api/v1/orgs/${orgId}/edge/${edgeId}/profiles`,
|
|
27465
|
+
CLIP_CONNECTIONS: (orgId, clipId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/connections`,
|
|
27466
|
+
CLIP_CONNECTION: (orgId, connId) => `/api/v1/orgs/${orgId}/clip-connections/${connId}`
|
|
27443
27467
|
};
|
|
27444
27468
|
var WS_EVENTS = {
|
|
27445
27469
|
// Client -> Server
|
|
@@ -27498,6 +27522,7 @@ var WS_EVENTS = {
|
|
|
27498
27522
|
READ_POSITION_UPDATED: "read_position.updated",
|
|
27499
27523
|
DISPATCH_NEW: "dispatch.new",
|
|
27500
27524
|
DISPATCH_RECEIVED: "dispatch.received",
|
|
27525
|
+
DISPATCH_RESOLVED: "dispatch.resolved",
|
|
27501
27526
|
SCHEDULE_CREATED: "schedule.created",
|
|
27502
27527
|
SCHEDULE_UPDATED: "schedule.updated",
|
|
27503
27528
|
SCHEDULE_DELETED: "schedule.deleted",
|
|
@@ -27593,8 +27618,8 @@ var ParallClient = class _ParallClient {
|
|
|
27593
27618
|
* is authoritative, so wiki vs api routing can't drift from how a caller
|
|
27594
27619
|
* happens to invoke the client.
|
|
27595
27620
|
*/
|
|
27596
|
-
baseUrlFor(
|
|
27597
|
-
return
|
|
27621
|
+
baseUrlFor(path9) {
|
|
27622
|
+
return path9.startsWith(WIKI_BASE) ? this.wikiBaseUrl : this.baseUrl;
|
|
27598
27623
|
}
|
|
27599
27624
|
setToken(token) {
|
|
27600
27625
|
this.token = token;
|
|
@@ -27621,10 +27646,10 @@ var ParallClient = class _ParallClient {
|
|
|
27621
27646
|
* REFRESH_THRESHOLD_S, refresh it **before** sending the request.
|
|
27622
27647
|
* No-op when the token is still fresh, missing, or un-parseable.
|
|
27623
27648
|
*/
|
|
27624
|
-
async ensureFreshToken(
|
|
27649
|
+
async ensureFreshToken(path9) {
|
|
27625
27650
|
if (!this.token || !this.getRefreshToken)
|
|
27626
27651
|
return;
|
|
27627
|
-
const pathSuffix =
|
|
27652
|
+
const pathSuffix = path9.replace(/^\/api\/v1/, "");
|
|
27628
27653
|
if (_ParallClient.AUTH_PATHS.has(pathSuffix))
|
|
27629
27654
|
return;
|
|
27630
27655
|
const exp = _ParallClient.decodeJwtExp(this.token);
|
|
@@ -27656,11 +27681,11 @@ var ParallClient = class _ParallClient {
|
|
|
27656
27681
|
this.refreshPromise = null;
|
|
27657
27682
|
}
|
|
27658
27683
|
}
|
|
27659
|
-
async request(method,
|
|
27684
|
+
async request(method, path9, body, query, retried = false, opts) {
|
|
27660
27685
|
if (!retried) {
|
|
27661
|
-
await this.ensureFreshToken(
|
|
27686
|
+
await this.ensureFreshToken(path9);
|
|
27662
27687
|
}
|
|
27663
|
-
let url = `${this.baseUrlFor(
|
|
27688
|
+
let url = `${this.baseUrlFor(path9)}${path9}`;
|
|
27664
27689
|
if (query) {
|
|
27665
27690
|
const params = new URLSearchParams();
|
|
27666
27691
|
for (const [key, value] of Object.entries(query)) {
|
|
@@ -27690,12 +27715,12 @@ var ParallClient = class _ParallClient {
|
|
|
27690
27715
|
throw _ParallClient.normalizeFetchError(err);
|
|
27691
27716
|
}
|
|
27692
27717
|
if (res.status === 401) {
|
|
27693
|
-
const pathSuffix =
|
|
27718
|
+
const pathSuffix = path9.replace(/^\/api\/v1/, "");
|
|
27694
27719
|
const isAuthPath = _ParallClient.AUTH_PATHS.has(pathSuffix);
|
|
27695
27720
|
if (!retried && !isAuthPath && this.getRefreshToken) {
|
|
27696
27721
|
const refreshed = await this.tryRefresh();
|
|
27697
27722
|
if (refreshed) {
|
|
27698
|
-
return this.request(method,
|
|
27723
|
+
return this.request(method, path9, body, query, true, opts);
|
|
27699
27724
|
}
|
|
27700
27725
|
}
|
|
27701
27726
|
if (this.onTokenExpired && !isAuthPath) {
|
|
@@ -27706,6 +27731,7 @@ var ParallClient = class _ParallClient {
|
|
|
27706
27731
|
const rawErrorBody = await res.json().catch(() => ({}));
|
|
27707
27732
|
throw buildApiError(res, rawErrorBody);
|
|
27708
27733
|
}
|
|
27734
|
+
opts?.onStatus?.(res.status);
|
|
27709
27735
|
if (res.status === 204)
|
|
27710
27736
|
return void 0;
|
|
27711
27737
|
if (res.status === 202) {
|
|
@@ -27724,15 +27750,15 @@ var ParallClient = class _ParallClient {
|
|
|
27724
27750
|
* hit the 100 MiB cap, so a longer 5-minute timeout is used so a
|
|
27725
27751
|
* 50 MiB blob on a slow connection doesn't get chopped at 15 s.
|
|
27726
27752
|
*/
|
|
27727
|
-
async multipartRequest(method,
|
|
27753
|
+
async multipartRequest(method, path9, body, retried = false) {
|
|
27728
27754
|
if (!retried) {
|
|
27729
|
-
await this.ensureFreshToken(
|
|
27755
|
+
await this.ensureFreshToken(path9);
|
|
27730
27756
|
}
|
|
27731
27757
|
const { "Content-Type": _drop, ...headers } = this.buildHeaders();
|
|
27732
27758
|
void _drop;
|
|
27733
27759
|
let res;
|
|
27734
27760
|
try {
|
|
27735
|
-
res = await fetch(`${this.baseUrlFor(
|
|
27761
|
+
res = await fetch(`${this.baseUrlFor(path9)}${path9}`, {
|
|
27736
27762
|
method,
|
|
27737
27763
|
headers,
|
|
27738
27764
|
body,
|
|
@@ -27742,12 +27768,12 @@ var ParallClient = class _ParallClient {
|
|
|
27742
27768
|
throw _ParallClient.normalizeFetchError(err);
|
|
27743
27769
|
}
|
|
27744
27770
|
if (res.status === 401) {
|
|
27745
|
-
const pathSuffix =
|
|
27771
|
+
const pathSuffix = path9.replace(/^\/api\/v1/, "");
|
|
27746
27772
|
const isAuthPath = _ParallClient.AUTH_PATHS.has(pathSuffix);
|
|
27747
27773
|
if (!retried && !isAuthPath && this.getRefreshToken) {
|
|
27748
27774
|
const refreshed = await this.tryRefresh();
|
|
27749
27775
|
if (refreshed) {
|
|
27750
|
-
return this.multipartRequest(method,
|
|
27776
|
+
return this.multipartRequest(method, path9, body, true);
|
|
27751
27777
|
}
|
|
27752
27778
|
}
|
|
27753
27779
|
if (this.onTokenExpired && !isAuthPath) {
|
|
@@ -28039,6 +28065,21 @@ var ParallClient = class _ParallClient {
|
|
|
28039
28065
|
async sendMessage(orgId, chatId, req) {
|
|
28040
28066
|
return this.request("POST", ENDPOINTS.CHAT_MESSAGES(orgId, chatId), req);
|
|
28041
28067
|
}
|
|
28068
|
+
/**
|
|
28069
|
+
* sendMessage variant that also reports whether the server answered with an
|
|
28070
|
+
* idempotent replay (HTTP 200 — the message already existed for this
|
|
28071
|
+
* idempotency/effect key) instead of a fresh create (201). Used by the CLI
|
|
28072
|
+
* to surface "already sent by a previous run (deduplicated)".
|
|
28073
|
+
*/
|
|
28074
|
+
async sendMessageDetailed(orgId, chatId, req) {
|
|
28075
|
+
let status = 0;
|
|
28076
|
+
const message = await this.request("POST", ENDPOINTS.CHAT_MESSAGES(orgId, chatId), req, void 0, false, {
|
|
28077
|
+
onStatus: (s) => {
|
|
28078
|
+
status = s;
|
|
28079
|
+
}
|
|
28080
|
+
});
|
|
28081
|
+
return { message, deduplicated: status === 200 };
|
|
28082
|
+
}
|
|
28042
28083
|
async getMessages(orgId, chatId, params) {
|
|
28043
28084
|
return this.request("GET", ENDPOINTS.CHAT_MESSAGES(orgId, chatId), void 0, params);
|
|
28044
28085
|
}
|
|
@@ -28426,8 +28467,8 @@ var ParallClient = class _ParallClient {
|
|
|
28426
28467
|
async requestMachineUpdate(orgId, machineId, mandatory = false) {
|
|
28427
28468
|
await this.request("POST", ENDPOINTS.MACHINE_REQUEST_UPDATE(orgId, machineId), { mandatory });
|
|
28428
28469
|
}
|
|
28429
|
-
async browseMachineFilesystem(orgId, machineId,
|
|
28430
|
-
return this.request("POST", ENDPOINTS.MACHINE_BROWSE(orgId, machineId), { path:
|
|
28470
|
+
async browseMachineFilesystem(orgId, machineId, path9) {
|
|
28471
|
+
return this.request("POST", ENDPOINTS.MACHINE_BROWSE(orgId, machineId), { path: path9 }, void 0, false, { timeoutMs: 15e3 });
|
|
28431
28472
|
}
|
|
28432
28473
|
/** Create a new machine key. Returns the raw key string (shown once) + metadata. */
|
|
28433
28474
|
async createMachineKey(orgId, machineId, name) {
|
|
@@ -28500,6 +28541,30 @@ var ParallClient = class _ParallClient {
|
|
|
28500
28541
|
const params = maxAgeHours !== void 0 ? { max_age_hours: String(maxAgeHours) } : void 0;
|
|
28501
28542
|
return this.request("POST", ENDPOINTS.DISPATCH_EXPIRE(orgId), void 0, params);
|
|
28502
28543
|
}
|
|
28544
|
+
/**
|
|
28545
|
+
* Claim a dispatch lane (explicit consume endpoint). Occupies the
|
|
28546
|
+
* (agent, target, thread) lane and folds claimable WorkItems into it;
|
|
28547
|
+
* `claimed: false` means a healthy incumbent holds the lane.
|
|
28548
|
+
*/
|
|
28549
|
+
async claimDispatch(orgId, req) {
|
|
28550
|
+
return this.request("POST", ENDPOINTS.DISPATCH_CLAIM(orgId), req);
|
|
28551
|
+
}
|
|
28552
|
+
/** Fold a pending same-target WorkItem into a live lane (409 STALE_LANE when dethroned). */
|
|
28553
|
+
async steerDispatch(orgId, req) {
|
|
28554
|
+
return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
|
|
28555
|
+
}
|
|
28556
|
+
/** End a turn: no_action sweep of the lane's members + lane release + re-drive check. */
|
|
28557
|
+
async completeDispatch(orgId, req) {
|
|
28558
|
+
return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
|
|
28559
|
+
}
|
|
28560
|
+
/** Release a lane on graceful shutdown — members return to pending immediately. */
|
|
28561
|
+
async releaseDispatchLane(orgId, lane) {
|
|
28562
|
+
return this.request("POST", ENDPOINTS.DISPATCH_RELEASE(orgId), { lane });
|
|
28563
|
+
}
|
|
28564
|
+
/** Renew a live lane's lease (long-turn keepalive). 409 STALE_LANE when dethroned. */
|
|
28565
|
+
async heartbeatDispatchLane(orgId, req) {
|
|
28566
|
+
return this.request("POST", ENDPOINTS.DISPATCH_HEARTBEAT(orgId), req);
|
|
28567
|
+
}
|
|
28503
28568
|
async getDispatchByMessages(orgId, chatId, messageIds) {
|
|
28504
28569
|
const params = { chat_id: chatId, message_ids: messageIds.join(",") };
|
|
28505
28570
|
return this.request("GET", ENDPOINTS.DISPATCH_BY_MESSAGES(orgId), void 0, params);
|
|
@@ -28964,8 +29029,8 @@ var ParallClient = class _ParallClient {
|
|
|
28964
29029
|
async deleteWikiRestriction(orgId, wikiId, restrictionId) {
|
|
28965
29030
|
await this.request("DELETE", ENDPOINTS.WIKI_RESTRICTION(orgId, wikiId, restrictionId));
|
|
28966
29031
|
}
|
|
28967
|
-
async getWikiAccessStatus(orgId, wikiId,
|
|
28968
|
-
return this.request("GET", ENDPOINTS.WIKI_ACCESS_STATUS(orgId, wikiId), void 0,
|
|
29032
|
+
async getWikiAccessStatus(orgId, wikiId, path9) {
|
|
29033
|
+
return this.request("GET", ENDPOINTS.WIKI_ACCESS_STATUS(orgId, wikiId), void 0, path9 ? { path: path9 } : void 0);
|
|
28969
29034
|
}
|
|
28970
29035
|
async createWikiAccessRequest(orgId, wikiId, data) {
|
|
28971
29036
|
await this.request("POST", ENDPOINTS.WIKI_ACCESS_REQUESTS(orgId, wikiId), data);
|
|
@@ -28974,14 +29039,14 @@ var ParallClient = class _ParallClient {
|
|
|
28974
29039
|
async getWikiCommits(orgId, wikiId, params) {
|
|
28975
29040
|
return this.request("GET", ENDPOINTS.WIKI_COMMITS(orgId, wikiId), void 0, params);
|
|
28976
29041
|
}
|
|
28977
|
-
async getWikiFileCommits(orgId, wikiId,
|
|
29042
|
+
async getWikiFileCommits(orgId, wikiId, path9, params) {
|
|
28978
29043
|
return this.request("GET", ENDPOINTS.WIKI_FILE_COMMITS(orgId, wikiId), void 0, {
|
|
28979
|
-
path:
|
|
29044
|
+
path: path9,
|
|
28980
29045
|
...params
|
|
28981
29046
|
});
|
|
28982
29047
|
}
|
|
28983
|
-
async getWikiBlame(orgId, wikiId,
|
|
28984
|
-
return this.request("GET", ENDPOINTS.WIKI_BLAME(orgId, wikiId), void 0, { path:
|
|
29048
|
+
async getWikiBlame(orgId, wikiId, path9, ref) {
|
|
29049
|
+
return this.request("GET", ENDPOINTS.WIKI_BLAME(orgId, wikiId), void 0, { path: path9, ref });
|
|
28985
29050
|
}
|
|
28986
29051
|
// ---- Wiki Operations (audit log) ----
|
|
28987
29052
|
async getWikiOperations(orgId, wikiId, params) {
|
|
@@ -29227,6 +29292,26 @@ var ParallClient = class _ParallClient {
|
|
|
29227
29292
|
const resp = await this.request("GET", url);
|
|
29228
29293
|
return resp.data;
|
|
29229
29294
|
}
|
|
29295
|
+
// ---- Edge devices ----
|
|
29296
|
+
async listEdgeDevices(orgId) {
|
|
29297
|
+
return this.request("GET", ENDPOINTS.ORG_EDGE_DEVICES(orgId));
|
|
29298
|
+
}
|
|
29299
|
+
async getEdgeOnboarding(orgId) {
|
|
29300
|
+
return this.request("GET", ENDPOINTS.ORG_EDGE_ONBOARDING(orgId));
|
|
29301
|
+
}
|
|
29302
|
+
async listEdgeProfiles(orgId, edgeId) {
|
|
29303
|
+
return this.request("GET", ENDPOINTS.ORG_EDGE_PROFILES(orgId, edgeId));
|
|
29304
|
+
}
|
|
29305
|
+
// ---- Clip connections ----
|
|
29306
|
+
async listClipConnections(orgId, clipId) {
|
|
29307
|
+
return this.request("GET", ENDPOINTS.CLIP_CONNECTIONS(orgId, clipId));
|
|
29308
|
+
}
|
|
29309
|
+
async createClipConnection(orgId, clipId, input) {
|
|
29310
|
+
return this.request("POST", ENDPOINTS.CLIP_CONNECTIONS(orgId, clipId), input);
|
|
29311
|
+
}
|
|
29312
|
+
async deleteClipConnection(orgId, connId) {
|
|
29313
|
+
return this.request("DELETE", ENDPOINTS.CLIP_CONNECTION(orgId, connId));
|
|
29314
|
+
}
|
|
29230
29315
|
};
|
|
29231
29316
|
function normalizeWikiChangeset(changeset) {
|
|
29232
29317
|
return {
|
|
@@ -29613,6 +29698,404 @@ var ParallWs = class {
|
|
|
29613
29698
|
}
|
|
29614
29699
|
};
|
|
29615
29700
|
|
|
29701
|
+
// ts/agent-core/dist/lane-ledger.js
|
|
29702
|
+
import * as fs from "node:fs";
|
|
29703
|
+
var LedgerUnsupportedError = class extends Error {
|
|
29704
|
+
};
|
|
29705
|
+
function isStaleLane(err) {
|
|
29706
|
+
return err instanceof ApiError && err.status === 409 && err.code === "STALE_LANE";
|
|
29707
|
+
}
|
|
29708
|
+
function isEndpointMissing(err) {
|
|
29709
|
+
return err instanceof ApiError && err.status === 404 && !err.code;
|
|
29710
|
+
}
|
|
29711
|
+
var LaneLedger = class {
|
|
29712
|
+
opts;
|
|
29713
|
+
lanes = /* @__PURE__ */ new Map();
|
|
29714
|
+
constructor(opts) {
|
|
29715
|
+
this.opts = opts;
|
|
29716
|
+
}
|
|
29717
|
+
get contextDir() {
|
|
29718
|
+
return this.opts.contextDir;
|
|
29719
|
+
}
|
|
29720
|
+
/** Only chat message events ride the lane ledger; typed events stay on the legacy ack path. */
|
|
29721
|
+
handles(event) {
|
|
29722
|
+
return event.type === "message" && event.targetId.startsWith("cht_");
|
|
29723
|
+
}
|
|
29724
|
+
laneKeyFor(event) {
|
|
29725
|
+
if (event.type !== "message" && event.dispatchEventId) {
|
|
29726
|
+
return laneKeyForTarget(`dsp:${event.dispatchEventId}`);
|
|
29727
|
+
}
|
|
29728
|
+
return laneKeyForTarget(`prll://${event.targetId}`, event.threadRootId);
|
|
29729
|
+
}
|
|
29730
|
+
getForEvent(event) {
|
|
29731
|
+
return this.lanes.get(this.laneKeyFor(event));
|
|
29732
|
+
}
|
|
29733
|
+
laneContextPath(lane) {
|
|
29734
|
+
return laneContextFilePath(this.opts.contextDir, lane.targetUri, lane.threadRootId);
|
|
29735
|
+
}
|
|
29736
|
+
/**
|
|
29737
|
+
* Claim (or reuse) the lane for a group of same-lane message events and
|
|
29738
|
+
* fold every group member into it. Returns 'foreign' when a healthy
|
|
29739
|
+
* incumbent (another pod) holds the resource — the caller must not
|
|
29740
|
+
* dispatch; the events stay pending server-side and re-drive after the
|
|
29741
|
+
* incumbent completes.
|
|
29742
|
+
*/
|
|
29743
|
+
async ensureLane(events) {
|
|
29744
|
+
const trigger = events[events.length - 1];
|
|
29745
|
+
const laneKey = this.laneKeyFor(trigger);
|
|
29746
|
+
let lane = this.lanes.get(laneKey);
|
|
29747
|
+
if (!lane) {
|
|
29748
|
+
const targetUri = `prll://${trigger.targetId}`;
|
|
29749
|
+
let res;
|
|
29750
|
+
try {
|
|
29751
|
+
res = await this.opts.client.claimDispatch(this.opts.orgId, {
|
|
29752
|
+
target_uri: targetUri,
|
|
29753
|
+
thread_root_id: trigger.threadRootId,
|
|
29754
|
+
limit: 100
|
|
29755
|
+
});
|
|
29756
|
+
} catch (err) {
|
|
29757
|
+
if (isEndpointMissing(err))
|
|
29758
|
+
throw new LedgerUnsupportedError("claim endpoint unavailable");
|
|
29759
|
+
throw err;
|
|
29760
|
+
}
|
|
29761
|
+
if (!res.claimed || !res.lane) {
|
|
29762
|
+
this.opts.log?.info(`lane for ${targetUri} held by a healthy incumbent \u2014 leaving events pending for re-drive`);
|
|
29763
|
+
return null;
|
|
29764
|
+
}
|
|
29765
|
+
const leaseUntilMs = Date.parse(res.lease_until ?? "");
|
|
29766
|
+
lane = {
|
|
29767
|
+
laneKey,
|
|
29768
|
+
lane: res.lane,
|
|
29769
|
+
targetUri,
|
|
29770
|
+
threadRootId: trigger.threadRootId,
|
|
29771
|
+
folded: /* @__PURE__ */ new Map(),
|
|
29772
|
+
...Number.isNaN(leaseUntilMs) ? {} : { leaseUntilMs, leaseTtlMs: Math.max(leaseUntilMs - Date.now(), 6e4) }
|
|
29773
|
+
};
|
|
29774
|
+
for (const ev of res.events ?? []) {
|
|
29775
|
+
lane.folded.set(ev.source_id, ev.id);
|
|
29776
|
+
}
|
|
29777
|
+
this.lanes.set(laneKey, lane);
|
|
29778
|
+
}
|
|
29779
|
+
for (const ev of events) {
|
|
29780
|
+
if (lane.folded.has(ev.messageId))
|
|
29781
|
+
continue;
|
|
29782
|
+
try {
|
|
29783
|
+
const res = await this.opts.client.steerDispatch(this.opts.orgId, {
|
|
29784
|
+
lane: lane.lane,
|
|
29785
|
+
target_uri: lane.targetUri,
|
|
29786
|
+
thread_root_id: lane.threadRootId,
|
|
29787
|
+
...ev.dispatchEventId ? { dispatch_event_id: ev.dispatchEventId } : { source_type: "message", source_id: ev.messageId }
|
|
29788
|
+
});
|
|
29789
|
+
lane.folded.set(ev.messageId, res.dispatch_event_id);
|
|
29790
|
+
} catch (err) {
|
|
29791
|
+
if (isStaleLane(err)) {
|
|
29792
|
+
this.lanes.delete(laneKey);
|
|
29793
|
+
return null;
|
|
29794
|
+
}
|
|
29795
|
+
this.opts.log?.warn(`steer fold failed for ${ev.messageId} \u2014 failing closed, releasing lane: ${String(err)}`);
|
|
29796
|
+
await this.release(laneKey);
|
|
29797
|
+
return null;
|
|
29798
|
+
}
|
|
29799
|
+
}
|
|
29800
|
+
return lane;
|
|
29801
|
+
}
|
|
29802
|
+
/**
|
|
29803
|
+
* Fold a live mid-turn message into its active lane BEFORE injecting it
|
|
29804
|
+
* into the running turn. Injection without a successful fold is forbidden —
|
|
29805
|
+
* an un-folded injected message would be re-driven after complete and the
|
|
29806
|
+
* model would handle it twice.
|
|
29807
|
+
*/
|
|
29808
|
+
async steerLive(event) {
|
|
29809
|
+
const laneKey = this.laneKeyFor(event);
|
|
29810
|
+
const lane = this.lanes.get(laneKey);
|
|
29811
|
+
if (!lane)
|
|
29812
|
+
return false;
|
|
29813
|
+
if (lane.folded.has(event.messageId))
|
|
29814
|
+
return true;
|
|
29815
|
+
try {
|
|
29816
|
+
const res = await this.opts.client.steerDispatch(this.opts.orgId, {
|
|
29817
|
+
lane: lane.lane,
|
|
29818
|
+
target_uri: lane.targetUri,
|
|
29819
|
+
thread_root_id: lane.threadRootId,
|
|
29820
|
+
...event.dispatchEventId ? { dispatch_event_id: event.dispatchEventId } : { source_type: "message", source_id: event.messageId }
|
|
29821
|
+
});
|
|
29822
|
+
lane.folded.set(event.messageId, res.dispatch_event_id);
|
|
29823
|
+
return true;
|
|
29824
|
+
} catch (err) {
|
|
29825
|
+
if (isStaleLane(err)) {
|
|
29826
|
+
this.lanes.delete(laneKey);
|
|
29827
|
+
} else {
|
|
29828
|
+
this.opts.log?.warn(`live steer failed for ${event.messageId}: ${String(err)}`);
|
|
29829
|
+
}
|
|
29830
|
+
return false;
|
|
29831
|
+
}
|
|
29832
|
+
}
|
|
29833
|
+
/**
|
|
29834
|
+
* Complete the lane when no local work remains for it: the server sweeps
|
|
29835
|
+
* still-leased members as no_action, releases the occupancy row, and
|
|
29836
|
+
* re-drives any same-target pending work. A STALE_LANE answer means a
|
|
29837
|
+
* takeover already owns the resource — local state is dropped either way.
|
|
29838
|
+
*/
|
|
29839
|
+
async completeIfIdle(laneKey, hasMoreLocal) {
|
|
29840
|
+
const lane = this.lanes.get(laneKey);
|
|
29841
|
+
if (!lane || hasMoreLocal)
|
|
29842
|
+
return;
|
|
29843
|
+
this.lanes.delete(laneKey);
|
|
29844
|
+
this.removeLaneContext(lane);
|
|
29845
|
+
try {
|
|
29846
|
+
const res = await this.opts.client.completeDispatch(this.opts.orgId, {
|
|
29847
|
+
lane: lane.lane,
|
|
29848
|
+
target_uri: lane.targetUri,
|
|
29849
|
+
thread_root_id: lane.threadRootId
|
|
29850
|
+
});
|
|
29851
|
+
if (res.swept_no_action > 0 || res.redriven) {
|
|
29852
|
+
this.opts.log?.info(`lane complete for ${lane.targetUri}: swept ${res.swept_no_action} no_action, redriven=${res.redriven}`);
|
|
29853
|
+
}
|
|
29854
|
+
} catch (err) {
|
|
29855
|
+
if (isStaleLane(err)) {
|
|
29856
|
+
this.opts.log?.info(`lane complete skipped for ${lane.targetUri} \u2014 taken over`);
|
|
29857
|
+
return;
|
|
29858
|
+
}
|
|
29859
|
+
this.opts.log?.warn(`lane complete failed for ${lane.targetUri}: ${String(err)}`);
|
|
29860
|
+
}
|
|
29861
|
+
}
|
|
29862
|
+
/**
|
|
29863
|
+
* Long-turn keepalive: renew the lane's lease on runtime activity, throttled
|
|
29864
|
+
* so a chatty turn doesn't spam the server. Without this, a legitimately
|
|
29865
|
+
* long turn (> lane TTL) would be dethroned mid-flight and every subsequent
|
|
29866
|
+
* write misfired with STALE_LANE — the design doc's "long turns renew via
|
|
29867
|
+
* step writes". Fire-and-forget: a failed renewal is surfaced by the next
|
|
29868
|
+
* write's incumbency check anyway.
|
|
29869
|
+
*/
|
|
29870
|
+
maybeRenew(lane) {
|
|
29871
|
+
const now = Date.now();
|
|
29872
|
+
const ttl = lane.leaseTtlMs ?? 10 * 6e4;
|
|
29873
|
+
const until = lane.leaseUntilMs ?? now;
|
|
29874
|
+
if (until - now > ttl / 2)
|
|
29875
|
+
return;
|
|
29876
|
+
lane.leaseUntilMs = now + ttl;
|
|
29877
|
+
void this.opts.client.heartbeatDispatchLane(this.opts.orgId, {
|
|
29878
|
+
lane: lane.lane,
|
|
29879
|
+
target_uri: lane.targetUri,
|
|
29880
|
+
thread_root_id: lane.threadRootId
|
|
29881
|
+
}).then((res) => {
|
|
29882
|
+
const until2 = Date.parse(res?.lease_until ?? "");
|
|
29883
|
+
if (!Number.isNaN(until2))
|
|
29884
|
+
lane.leaseUntilMs = until2;
|
|
29885
|
+
}).catch((err) => {
|
|
29886
|
+
if (isStaleLane(err)) {
|
|
29887
|
+
this.lanes.delete(lane.laneKey);
|
|
29888
|
+
this.opts.log?.warn(`lane ${lane.targetUri} was taken over during the turn`);
|
|
29889
|
+
return;
|
|
29890
|
+
}
|
|
29891
|
+
this.opts.log?.warn(`lane heartbeat failed for ${lane.targetUri}: ${String(err)}`);
|
|
29892
|
+
});
|
|
29893
|
+
}
|
|
29894
|
+
/**
|
|
29895
|
+
* Release a lane's unresolved members back to pending (dispatch error /
|
|
29896
|
+
* shutdown) so the next pod re-claims immediately instead of waiting out
|
|
29897
|
+
* the lease.
|
|
29898
|
+
*/
|
|
29899
|
+
async release(laneKey) {
|
|
29900
|
+
const lane = this.lanes.get(laneKey);
|
|
29901
|
+
if (!lane)
|
|
29902
|
+
return;
|
|
29903
|
+
this.lanes.delete(laneKey);
|
|
29904
|
+
this.removeLaneContext(lane);
|
|
29905
|
+
try {
|
|
29906
|
+
await this.opts.client.releaseDispatchLane(this.opts.orgId, lane.lane);
|
|
29907
|
+
} catch (err) {
|
|
29908
|
+
this.opts.log?.warn(`lane release failed for ${lane.targetUri}: ${String(err)}`);
|
|
29909
|
+
}
|
|
29910
|
+
}
|
|
29911
|
+
async releaseAll() {
|
|
29912
|
+
const keys = [...this.lanes.keys()];
|
|
29913
|
+
for (const key of keys) {
|
|
29914
|
+
await this.release(key);
|
|
29915
|
+
}
|
|
29916
|
+
}
|
|
29917
|
+
/** True when any lane is currently active (used by shutdown logging). */
|
|
29918
|
+
get activeCount() {
|
|
29919
|
+
return this.lanes.size;
|
|
29920
|
+
}
|
|
29921
|
+
/**
|
|
29922
|
+
* Claim the single-member lane of one typed WorkItem (resource = dsp:<id>),
|
|
29923
|
+
* by WorkItem id or by source identity (the live task.assigned event has no
|
|
29924
|
+
* WorkItem id). Returns null when a healthy incumbent (another pod) holds
|
|
29925
|
+
* it or the WorkItem is already resolved — the caller must skip processing.
|
|
29926
|
+
*/
|
|
29927
|
+
async claimTyped(ref) {
|
|
29928
|
+
let res;
|
|
29929
|
+
try {
|
|
29930
|
+
res = await this.opts.client.claimDispatch(this.opts.orgId, {
|
|
29931
|
+
dispatch_event_id: ref.dispatchEventId,
|
|
29932
|
+
source_type: ref.dispatchEventId ? void 0 : ref.sourceType,
|
|
29933
|
+
source_id: ref.dispatchEventId ? void 0 : ref.sourceId
|
|
29934
|
+
});
|
|
29935
|
+
} catch (err) {
|
|
29936
|
+
if (isEndpointMissing(err))
|
|
29937
|
+
throw new LedgerUnsupportedError("claim endpoint unavailable");
|
|
29938
|
+
throw err;
|
|
29939
|
+
}
|
|
29940
|
+
if (!res.claimed || !res.lane || !res.events?.length)
|
|
29941
|
+
return null;
|
|
29942
|
+
const workItem = res.events[0];
|
|
29943
|
+
const targetUri = `dsp:${workItem.id}`;
|
|
29944
|
+
const leaseUntilMs = Date.parse(res.lease_until ?? "");
|
|
29945
|
+
const lane = {
|
|
29946
|
+
laneKey: laneKeyForTarget(targetUri),
|
|
29947
|
+
lane: res.lane,
|
|
29948
|
+
targetUri,
|
|
29949
|
+
folded: /* @__PURE__ */ new Map([[workItem.source_id, workItem.id]]),
|
|
29950
|
+
typedDispatchEventId: workItem.id,
|
|
29951
|
+
...Number.isNaN(leaseUntilMs) ? {} : { leaseUntilMs, leaseTtlMs: Math.max(leaseUntilMs - Date.now(), 6e4) }
|
|
29952
|
+
};
|
|
29953
|
+
this.lanes.set(lane.laneKey, lane);
|
|
29954
|
+
return lane;
|
|
29955
|
+
}
|
|
29956
|
+
/**
|
|
29957
|
+
* Remove the per-lane context file (and its CLI sidecar) when the lane
|
|
29958
|
+
* ends. A leftover file would make a later cross-context send to the same
|
|
29959
|
+
* target bind a dead lane token and misfire with STALE_LANE instead of
|
|
29960
|
+
* taking the plain non-ledger path.
|
|
29961
|
+
*/
|
|
29962
|
+
removeLaneContext(lane) {
|
|
29963
|
+
const contextPath = this.laneContextPath(lane);
|
|
29964
|
+
for (const p of [contextPath, contextPath.replace(/\.json$/, ".reply-state.json")]) {
|
|
29965
|
+
try {
|
|
29966
|
+
fs.rmSync(p, { force: true });
|
|
29967
|
+
} catch {
|
|
29968
|
+
}
|
|
29969
|
+
}
|
|
29970
|
+
}
|
|
29971
|
+
};
|
|
29972
|
+
|
|
29973
|
+
// ts/agent-core/dist/gateway-lane-flow.js
|
|
29974
|
+
async function dispatchLaneGroup(host, opts) {
|
|
29975
|
+
const ledger = host.laneLedger;
|
|
29976
|
+
const event = opts.events[opts.events.length - 1];
|
|
29977
|
+
let lane;
|
|
29978
|
+
try {
|
|
29979
|
+
lane = await ledger.ensureLane(opts.events);
|
|
29980
|
+
} catch (err) {
|
|
29981
|
+
if (!(err instanceof LedgerUnsupportedError))
|
|
29982
|
+
throw err;
|
|
29983
|
+
host.disableLedger("claim endpoint missing");
|
|
29984
|
+
await host.emitDispatchReceived(event);
|
|
29985
|
+
const dispatched2 = await host.runDispatch(event, opts.sessionKey, opts.body, opts.earlier, opts.captureText);
|
|
29986
|
+
if (!dispatched2)
|
|
29987
|
+
return "shutdown";
|
|
29988
|
+
for (const ev of opts.events) {
|
|
29989
|
+
host.opts.client.ackDispatch(host.opts.config.org_id, {
|
|
29990
|
+
source_type: ev.ackSourceType ?? "message",
|
|
29991
|
+
source_id: ev.ackSourceId ?? ev.messageId
|
|
29992
|
+
}).catch(() => {
|
|
29993
|
+
});
|
|
29994
|
+
}
|
|
29995
|
+
return "dispatched";
|
|
29996
|
+
}
|
|
29997
|
+
if (!lane) {
|
|
29998
|
+
for (const ev of opts.events) {
|
|
29999
|
+
host.dispatchedMessages.delete(ev.messageId);
|
|
30000
|
+
}
|
|
30001
|
+
return "foreign";
|
|
30002
|
+
}
|
|
30003
|
+
let dispatched = false;
|
|
30004
|
+
try {
|
|
30005
|
+
dispatched = await host.runDispatch(event, opts.sessionKey, opts.body, opts.earlier, opts.captureText);
|
|
30006
|
+
} catch (err) {
|
|
30007
|
+
await ledger.release(lane.laneKey).catch(() => {
|
|
30008
|
+
});
|
|
30009
|
+
throw err;
|
|
30010
|
+
}
|
|
30011
|
+
if (!dispatched) {
|
|
30012
|
+
return "shutdown";
|
|
30013
|
+
}
|
|
30014
|
+
const pendingInjections = host.opts.dispatchAdapter.hasPendingInjections?.(opts.sessionKey) ?? false;
|
|
30015
|
+
await ledger.completeIfIdle(lane.laneKey, pendingInjections || opts.hasMoreLocal());
|
|
30016
|
+
return "dispatched";
|
|
30017
|
+
}
|
|
30018
|
+
async function consumeTypedDispatch(host, ref, run, ack) {
|
|
30019
|
+
if (!host.laneLedger || host.ledgerDisabled) {
|
|
30020
|
+
if (await run(ref.dispatchEventId))
|
|
30021
|
+
ack(ref.dispatchEventId);
|
|
30022
|
+
return;
|
|
30023
|
+
}
|
|
30024
|
+
let lane;
|
|
30025
|
+
try {
|
|
30026
|
+
lane = await host.laneLedger.claimTyped(ref);
|
|
30027
|
+
} catch (err) {
|
|
30028
|
+
if (err instanceof LedgerUnsupportedError) {
|
|
30029
|
+
host.disableLedger("claim endpoint missing");
|
|
30030
|
+
if (await run(ref.dispatchEventId))
|
|
30031
|
+
ack(ref.dispatchEventId);
|
|
30032
|
+
return;
|
|
30033
|
+
}
|
|
30034
|
+
throw err;
|
|
30035
|
+
}
|
|
30036
|
+
if (!lane) {
|
|
30037
|
+
host.opts.log?.info(`typed dispatch ${ref.dispatchEventId ?? `${ref.sourceType}:${ref.sourceId}`} not claimable (held elsewhere or already resolved) \u2014 skipping`);
|
|
30038
|
+
return;
|
|
30039
|
+
}
|
|
30040
|
+
try {
|
|
30041
|
+
if (await run(lane.typedDispatchEventId))
|
|
30042
|
+
ack(lane.typedDispatchEventId);
|
|
30043
|
+
} finally {
|
|
30044
|
+
await host.laneLedger.completeIfIdle(lane.laneKey, false).catch(() => {
|
|
30045
|
+
});
|
|
30046
|
+
}
|
|
30047
|
+
}
|
|
30048
|
+
async function consumeMessageWorkItem(host, item) {
|
|
30049
|
+
if (host.shuttingDown)
|
|
30050
|
+
return;
|
|
30051
|
+
if (!host.tryClaimMessage(item.source_id))
|
|
30052
|
+
return;
|
|
30053
|
+
const ackItem = () => {
|
|
30054
|
+
host.opts.client.ackDispatchByID(host.opts.config.org_id, item.id).catch(() => {
|
|
30055
|
+
});
|
|
30056
|
+
};
|
|
30057
|
+
let msg = null;
|
|
30058
|
+
try {
|
|
30059
|
+
msg = await host.opts.client.getMessage(item.source_id);
|
|
30060
|
+
} catch (err) {
|
|
30061
|
+
const status = err?.status;
|
|
30062
|
+
if (status !== 404) {
|
|
30063
|
+
host.opts.log?.warn(`message fetch failed for ${item.source_id}, leaving pending: ${String(err)}`);
|
|
30064
|
+
host.dispatchedMessages.delete(item.source_id);
|
|
30065
|
+
return;
|
|
30066
|
+
}
|
|
30067
|
+
}
|
|
30068
|
+
if (!msg || msg.sender_id === host.opts.agentUserId) {
|
|
30069
|
+
host.dispatchedMessages.delete(item.source_id);
|
|
30070
|
+
ackItem();
|
|
30071
|
+
return;
|
|
30072
|
+
}
|
|
30073
|
+
const decision = await host.buildMessageDispatchDecision(item.chat_id, msg);
|
|
30074
|
+
if (decision.action === "retry") {
|
|
30075
|
+
host.dispatchedMessages.delete(item.source_id);
|
|
30076
|
+
return;
|
|
30077
|
+
}
|
|
30078
|
+
if (decision.action === "skip") {
|
|
30079
|
+
host.dispatchedMessages.delete(item.source_id);
|
|
30080
|
+
ackItem();
|
|
30081
|
+
return;
|
|
30082
|
+
}
|
|
30083
|
+
decision.event.dispatchEventId = item.id;
|
|
30084
|
+
const laneResolved = host.usesLaneLedger(decision.event);
|
|
30085
|
+
try {
|
|
30086
|
+
const dispatched = await host.handleInboundEvent(decision.event);
|
|
30087
|
+
if (dispatched) {
|
|
30088
|
+
if (!laneResolved)
|
|
30089
|
+
ackItem();
|
|
30090
|
+
} else {
|
|
30091
|
+
host.dispatchedMessages.delete(item.source_id);
|
|
30092
|
+
}
|
|
30093
|
+
} catch (err) {
|
|
30094
|
+
host.dispatchedMessages.delete(item.source_id);
|
|
30095
|
+
throw err;
|
|
30096
|
+
}
|
|
30097
|
+
}
|
|
30098
|
+
|
|
29616
30099
|
// ts/agent-core/dist/telemetry.js
|
|
29617
30100
|
init_esm();
|
|
29618
30101
|
var import_api_logs = __toESM(require_src(), 1);
|
|
@@ -29914,6 +30397,14 @@ var ParallAgentGateway = class {
|
|
|
29914
30397
|
inFlightDispatches = 0;
|
|
29915
30398
|
drainResolvers = [];
|
|
29916
30399
|
pendingRestartNotification = null;
|
|
30400
|
+
laneLedger;
|
|
30401
|
+
// Sticky fallback: flipped when the server predates the ledger (claim
|
|
30402
|
+
// endpoint 404) so every subsequent dispatch uses the legacy flow.
|
|
30403
|
+
ledgerDisabled = false;
|
|
30404
|
+
// Group key of the group currently being dispatched on main — lane-aware
|
|
30405
|
+
// (targetId + thread), unlike mainCurrentTargetId which stays chat-level
|
|
30406
|
+
// for fork routing decisions.
|
|
30407
|
+
mainCurrentGroupKey;
|
|
29917
30408
|
DISPATCHED_MESSAGES_CAP = 5e3;
|
|
29918
30409
|
// SHUTDOWN_DEADLINE_MS is read by waitForDrain via the configured value
|
|
29919
30410
|
// below — kept as instance state so per-runtime configs can override it
|
|
@@ -29923,6 +30414,14 @@ var ParallAgentGateway = class {
|
|
|
29923
30414
|
DISPATCH_DEADLINE_MS;
|
|
29924
30415
|
constructor(opts) {
|
|
29925
30416
|
this.opts = opts;
|
|
30417
|
+
if (opts.dispatchContextDir) {
|
|
30418
|
+
this.laneLedger = new LaneLedger({
|
|
30419
|
+
client: opts.client,
|
|
30420
|
+
orgId: opts.config.org_id,
|
|
30421
|
+
contextDir: opts.dispatchContextDir,
|
|
30422
|
+
log: opts.log
|
|
30423
|
+
});
|
|
30424
|
+
}
|
|
29926
30425
|
this.SHUTDOWN_DEADLINE_MS = opts.shutdownDeadlineMs ?? 6e4;
|
|
29927
30426
|
this.FORK_DEADLINE_MS = opts.forkDeadlineMs ?? 2 * 60 * 6e4;
|
|
29928
30427
|
this.DISPATCH_DEADLINE_MS = opts.dispatchDeadlineMs ?? 20 * 6e4;
|
|
@@ -29992,14 +30491,18 @@ var ParallAgentGateway = class {
|
|
|
29992
30491
|
if (data.status !== "todo" && data.status !== "in_progress")
|
|
29993
30492
|
return;
|
|
29994
30493
|
try {
|
|
29995
|
-
|
|
29996
|
-
|
|
30494
|
+
await this.consumeTypedDispatch(data.dispatch_event_id ? { dispatchEventId: data.dispatch_event_id } : { sourceType: "task_activity", sourceId: data.id }, (dispatchEventId) => this.handleTaskAssignment(data, data.id, dispatchEventId), (dispatchEventId) => {
|
|
30495
|
+
if (dispatchEventId) {
|
|
30496
|
+
this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).catch(() => {
|
|
30497
|
+
});
|
|
30498
|
+
return;
|
|
30499
|
+
}
|
|
29997
30500
|
this.opts.client.ackDispatch(this.opts.config.org_id, {
|
|
29998
30501
|
source_type: "task_activity",
|
|
29999
30502
|
source_id: data.id
|
|
30000
30503
|
}).catch(() => {
|
|
30001
30504
|
});
|
|
30002
|
-
}
|
|
30505
|
+
});
|
|
30003
30506
|
} catch (err) {
|
|
30004
30507
|
this.opts.log?.error(`task dispatch failed for ${data.id}: ${String(err)}`);
|
|
30005
30508
|
}
|
|
@@ -30009,11 +30512,10 @@ var ParallAgentGateway = class {
|
|
|
30009
30512
|
if (!data.source_id || !data.task_id)
|
|
30010
30513
|
return;
|
|
30011
30514
|
try {
|
|
30012
|
-
|
|
30013
|
-
if (dispatched) {
|
|
30515
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleTaskComment(data.source_id, data.task_id ?? "", data.actor_id, data.delivery_reason), () => {
|
|
30014
30516
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
30015
30517
|
});
|
|
30016
|
-
}
|
|
30518
|
+
});
|
|
30017
30519
|
} catch (err) {
|
|
30018
30520
|
this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30019
30521
|
}
|
|
@@ -30021,11 +30523,10 @@ var ParallAgentGateway = class {
|
|
|
30021
30523
|
if (!data.source_id)
|
|
30022
30524
|
return;
|
|
30023
30525
|
try {
|
|
30024
|
-
|
|
30025
|
-
if (dispatched) {
|
|
30526
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason), () => {
|
|
30026
30527
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
30027
30528
|
});
|
|
30028
|
-
}
|
|
30529
|
+
});
|
|
30029
30530
|
} catch (err) {
|
|
30030
30531
|
this.opts.log?.error(`wiki comment dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30031
30532
|
}
|
|
@@ -30033,11 +30534,13 @@ var ParallAgentGateway = class {
|
|
|
30033
30534
|
if (!data.task_id)
|
|
30034
30535
|
return;
|
|
30035
30536
|
try {
|
|
30036
|
-
|
|
30037
|
-
|
|
30537
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleTaskDispatch(data.task_id ?? "", data.source_id ?? data.task_id ?? "", {
|
|
30538
|
+
allowCreator: true,
|
|
30539
|
+
dispatchEventId
|
|
30540
|
+
}), () => {
|
|
30038
30541
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
30039
30542
|
});
|
|
30040
|
-
}
|
|
30543
|
+
});
|
|
30041
30544
|
} catch (err) {
|
|
30042
30545
|
this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
|
|
30043
30546
|
}
|
|
@@ -30045,11 +30548,10 @@ var ParallAgentGateway = class {
|
|
|
30045
30548
|
if (!data.source_id)
|
|
30046
30549
|
return;
|
|
30047
30550
|
try {
|
|
30048
|
-
|
|
30049
|
-
if (dispatched) {
|
|
30551
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id), () => {
|
|
30050
30552
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
30051
30553
|
});
|
|
30052
|
-
}
|
|
30554
|
+
});
|
|
30053
30555
|
} catch (err) {
|
|
30054
30556
|
this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30055
30557
|
}
|
|
@@ -30057,11 +30559,10 @@ var ParallAgentGateway = class {
|
|
|
30057
30559
|
if (!data.source_id)
|
|
30058
30560
|
return;
|
|
30059
30561
|
try {
|
|
30060
|
-
|
|
30061
|
-
if (dispatched) {
|
|
30562
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id), () => {
|
|
30062
30563
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
30063
30564
|
});
|
|
30064
|
-
}
|
|
30565
|
+
});
|
|
30065
30566
|
} catch (err) {
|
|
30066
30567
|
this.opts.log?.error(`external trigger dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30067
30568
|
}
|
|
@@ -30069,11 +30570,10 @@ var ParallAgentGateway = class {
|
|
|
30069
30570
|
if (!data.source_id)
|
|
30070
30571
|
return;
|
|
30071
30572
|
try {
|
|
30072
|
-
|
|
30073
|
-
if (dispatched) {
|
|
30573
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id), () => {
|
|
30074
30574
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
30075
30575
|
});
|
|
30076
|
-
}
|
|
30576
|
+
});
|
|
30077
30577
|
} catch (err) {
|
|
30078
30578
|
this.opts.log?.error(`channel message dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30079
30579
|
}
|
|
@@ -30081,14 +30581,19 @@ var ParallAgentGateway = class {
|
|
|
30081
30581
|
if (!data.source_id)
|
|
30082
30582
|
return;
|
|
30083
30583
|
try {
|
|
30084
|
-
|
|
30085
|
-
if (dispatched) {
|
|
30584
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null), () => {
|
|
30086
30585
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
30087
30586
|
});
|
|
30088
|
-
}
|
|
30587
|
+
});
|
|
30089
30588
|
} catch (err) {
|
|
30090
30589
|
this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30091
30590
|
}
|
|
30591
|
+
} else if (data.event_type === "message" && this.laneLedger && data.source_id && data.chat_id) {
|
|
30592
|
+
try {
|
|
30593
|
+
await this.handleMessageRedrive(data);
|
|
30594
|
+
} catch (err) {
|
|
30595
|
+
this.opts.log?.error(`message re-drive failed for ${data.source_id}: ${String(err)}`);
|
|
30596
|
+
}
|
|
30092
30597
|
} else if (data.event_type !== "message" && data.event_type !== "task_assign") {
|
|
30093
30598
|
this.opts.log?.info(`dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) \u2014 no-op`);
|
|
30094
30599
|
}
|
|
@@ -30124,6 +30629,39 @@ var ParallAgentGateway = class {
|
|
|
30124
30629
|
source_id: sourceId
|
|
30125
30630
|
});
|
|
30126
30631
|
}
|
|
30632
|
+
/** True when this event's lifecycle is owned by the dispatch lane ledger. */
|
|
30633
|
+
usesLaneLedger(event) {
|
|
30634
|
+
return this.laneLedger != null && !this.ledgerDisabled && this.laneLedger.handles(event);
|
|
30635
|
+
}
|
|
30636
|
+
disableLedger(reason) {
|
|
30637
|
+
if (this.ledgerDisabled)
|
|
30638
|
+
return;
|
|
30639
|
+
this.ledgerDisabled = true;
|
|
30640
|
+
this.opts.log?.warn(`dispatch ledger unavailable (${reason}) \u2014 falling back to legacy received/ack flow`);
|
|
30641
|
+
}
|
|
30642
|
+
/**
|
|
30643
|
+
* Buffer grouping key. Lane-ledger message events group by full lane
|
|
30644
|
+
* identity (chat + thread) so a channel lane and a thread lane in the same
|
|
30645
|
+
* chat dispatch as separate turns with separate claims; everything else
|
|
30646
|
+
* keeps the historical chat-level grouping.
|
|
30647
|
+
*/
|
|
30648
|
+
dispatchGroupKey(event) {
|
|
30649
|
+
if (this.usesLaneLedger(event)) {
|
|
30650
|
+
return this.laneLedger.laneKeyFor(event);
|
|
30651
|
+
}
|
|
30652
|
+
return event.targetId;
|
|
30653
|
+
}
|
|
30654
|
+
// Lane-flow protocols live in gateway-lane-flow.ts; these thin delegates
|
|
30655
|
+
// keep call sites and tests on the class surface.
|
|
30656
|
+
laneFlowHost() {
|
|
30657
|
+
return this;
|
|
30658
|
+
}
|
|
30659
|
+
dispatchLaneGroup(opts) {
|
|
30660
|
+
return dispatchLaneGroup(this.laneFlowHost(), opts);
|
|
30661
|
+
}
|
|
30662
|
+
consumeTypedDispatch(ref, run, ack) {
|
|
30663
|
+
return consumeTypedDispatch(this.laneFlowHost(), ref, run, ack);
|
|
30664
|
+
}
|
|
30127
30665
|
buildDispatchContext(event, sessionKey) {
|
|
30128
30666
|
const binding = this.sessionBindings.get(sessionKey);
|
|
30129
30667
|
return {
|
|
@@ -30140,6 +30678,7 @@ var ParallAgentGateway = class {
|
|
|
30140
30678
|
noReply: event.noReply ?? false,
|
|
30141
30679
|
contextFilePath: this.opts.contextFilePathForSession?.(sessionKey),
|
|
30142
30680
|
stepIdFilePath: this.opts.stepIdFilePathForSession?.(sessionKey),
|
|
30681
|
+
contextDirPath: this.opts.dispatchContextDir,
|
|
30143
30682
|
client: this.opts.client,
|
|
30144
30683
|
log: this.opts.log
|
|
30145
30684
|
};
|
|
@@ -30179,7 +30718,7 @@ var ParallAgentGateway = class {
|
|
|
30179
30718
|
this.opts.log?.warn(`failed to create input step: ${String(err)}`);
|
|
30180
30719
|
}
|
|
30181
30720
|
}
|
|
30182
|
-
async createRuntimeStep(sessionId, event, runtimeEvent, stepIdFilePath, contextFilePath) {
|
|
30721
|
+
async createRuntimeStep(sessionId, event, runtimeEvent, stepIdFilePath, contextFilePath, laneContextFilePath2) {
|
|
30183
30722
|
const target = resolveStepTarget(event);
|
|
30184
30723
|
try {
|
|
30185
30724
|
switch (runtimeEvent.type) {
|
|
@@ -30225,6 +30764,9 @@ var ParallAgentGateway = class {
|
|
|
30225
30764
|
} else if (stepIdFilePath) {
|
|
30226
30765
|
this.writeStepIdFile(stepIdFilePath, step.id);
|
|
30227
30766
|
}
|
|
30767
|
+
if (laneContextFilePath2) {
|
|
30768
|
+
this.updateContextFileStepId(laneContextFilePath2, step.id);
|
|
30769
|
+
}
|
|
30228
30770
|
break;
|
|
30229
30771
|
}
|
|
30230
30772
|
case "tool_result":
|
|
@@ -30247,6 +30789,9 @@ var ParallAgentGateway = class {
|
|
|
30247
30789
|
} else if (stepIdFilePath) {
|
|
30248
30790
|
this.clearStepIdFile(stepIdFilePath);
|
|
30249
30791
|
}
|
|
30792
|
+
if (laneContextFilePath2) {
|
|
30793
|
+
this.updateContextFileStepId(laneContextFilePath2, null);
|
|
30794
|
+
}
|
|
30250
30795
|
break;
|
|
30251
30796
|
case "error":
|
|
30252
30797
|
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
@@ -30266,28 +30811,28 @@ var ParallAgentGateway = class {
|
|
|
30266
30811
|
}
|
|
30267
30812
|
writeContextFile(filePath, ctx) {
|
|
30268
30813
|
try {
|
|
30269
|
-
|
|
30270
|
-
|
|
30814
|
+
fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
|
|
30815
|
+
fs2.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
30271
30816
|
} catch (err) {
|
|
30272
30817
|
this.opts.log?.warn(`failed to write context file ${filePath}: ${String(err)}`);
|
|
30273
30818
|
}
|
|
30274
30819
|
}
|
|
30275
30820
|
updateContextFileStepId(filePath, stepId) {
|
|
30276
30821
|
try {
|
|
30277
|
-
const raw =
|
|
30822
|
+
const raw = fs2.readFileSync(filePath, "utf8");
|
|
30278
30823
|
const ctx = JSON.parse(raw);
|
|
30279
30824
|
ctx.step_id = stepId;
|
|
30280
|
-
|
|
30825
|
+
fs2.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
30281
30826
|
} catch (err) {
|
|
30282
30827
|
this.opts.log?.warn(`failed to update context file step_id ${filePath}: ${String(err)}`);
|
|
30283
30828
|
}
|
|
30284
30829
|
}
|
|
30285
30830
|
updateContextFileSessionId(filePath, sessionId) {
|
|
30286
30831
|
try {
|
|
30287
|
-
const raw =
|
|
30832
|
+
const raw = fs2.readFileSync(filePath, "utf8");
|
|
30288
30833
|
const ctx = JSON.parse(raw);
|
|
30289
30834
|
ctx.session_id = sessionId;
|
|
30290
|
-
|
|
30835
|
+
fs2.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
30291
30836
|
} catch (err) {
|
|
30292
30837
|
this.opts.log?.warn(`failed to update context file session_id ${filePath}: ${String(err)}`);
|
|
30293
30838
|
}
|
|
@@ -30295,8 +30840,8 @@ var ParallAgentGateway = class {
|
|
|
30295
30840
|
/** @deprecated Use writeContextFile / updateContextFileStepId. */
|
|
30296
30841
|
writeStepIdFile(filePath, stepId) {
|
|
30297
30842
|
try {
|
|
30298
|
-
|
|
30299
|
-
|
|
30843
|
+
fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
|
|
30844
|
+
fs2.writeFileSync(filePath, stepId, "utf8");
|
|
30300
30845
|
} catch (err) {
|
|
30301
30846
|
this.opts.log?.warn(`failed to write step id file ${filePath}: ${String(err)}`);
|
|
30302
30847
|
}
|
|
@@ -30304,7 +30849,7 @@ var ParallAgentGateway = class {
|
|
|
30304
30849
|
/** @deprecated Use writeContextFile / updateContextFileStepId. */
|
|
30305
30850
|
clearStepIdFile(filePath) {
|
|
30306
30851
|
try {
|
|
30307
|
-
|
|
30852
|
+
fs2.writeFileSync(filePath, "", "utf8");
|
|
30308
30853
|
} catch {
|
|
30309
30854
|
}
|
|
30310
30855
|
}
|
|
@@ -30313,7 +30858,7 @@ var ParallAgentGateway = class {
|
|
|
30313
30858
|
await this.createInputStep(sessionId, event);
|
|
30314
30859
|
}
|
|
30315
30860
|
}
|
|
30316
|
-
async bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath) {
|
|
30861
|
+
async bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath, laneContextFilePath2) {
|
|
30317
30862
|
const runtimeLaneKey = runtimeEvent.runtimeLaneKey || sessionKey;
|
|
30318
30863
|
const existing = this.sessionBindings.get(sessionKey);
|
|
30319
30864
|
if (existing && existing.runtimeLaneKey === runtimeLaneKey && existing.runtimeSessionId === runtimeEvent.runtimeSessionId) {
|
|
@@ -30357,6 +30902,9 @@ var ParallAgentGateway = class {
|
|
|
30357
30902
|
if (contextFilePath) {
|
|
30358
30903
|
this.updateContextFileSessionId(contextFilePath, session.id);
|
|
30359
30904
|
}
|
|
30905
|
+
if (laneContextFilePath2) {
|
|
30906
|
+
this.updateContextFileSessionId(laneContextFilePath2, session.id);
|
|
30907
|
+
}
|
|
30360
30908
|
await this.opts.onSessionBinding?.(binding);
|
|
30361
30909
|
return binding;
|
|
30362
30910
|
}
|
|
@@ -30383,14 +30931,27 @@ var ParallAgentGateway = class {
|
|
|
30383
30931
|
const dispatchContext = this.buildDispatchContext(event, sessionKey);
|
|
30384
30932
|
const contextFilePath = dispatchContext.contextFilePath;
|
|
30385
30933
|
const stepIdFilePath = dispatchContext.stepIdFilePath;
|
|
30934
|
+
const activeLane = this.ledgerDisabled ? void 0 : this.laneLedger?.getForEvent(event);
|
|
30935
|
+
const laneContextFilePath2 = activeLane ? this.laneLedger?.laneContextPath(activeLane) : void 0;
|
|
30936
|
+
const contextBody = {
|
|
30937
|
+
session_id: dispatchContext.sessionId ?? null,
|
|
30938
|
+
chat_id: dispatchContext.chatId ?? null,
|
|
30939
|
+
trigger_message_id: dispatchContext.triggerMessageId ?? null,
|
|
30940
|
+
no_reply: dispatchContext.noReply,
|
|
30941
|
+
step_id: null,
|
|
30942
|
+
dispatch_event_id: activeLane?.typedDispatchEventId ?? activeLane?.folded.get(event.messageId) ?? null,
|
|
30943
|
+
lane: activeLane?.lane ?? null,
|
|
30944
|
+
target_uri: activeLane?.targetUri ?? null,
|
|
30945
|
+
thread_root_id: activeLane?.threadRootId ?? null,
|
|
30946
|
+
// Typed binding hint for the CLI: which task this dispatch is about
|
|
30947
|
+
// (parall task update attaches the typed effect only on a match).
|
|
30948
|
+
task_id: event.type === "task" ? event.targetId : null
|
|
30949
|
+
};
|
|
30386
30950
|
if (contextFilePath) {
|
|
30387
|
-
this.writeContextFile(contextFilePath,
|
|
30388
|
-
|
|
30389
|
-
|
|
30390
|
-
|
|
30391
|
-
no_reply: dispatchContext.noReply,
|
|
30392
|
-
step_id: null
|
|
30393
|
-
});
|
|
30951
|
+
this.writeContextFile(contextFilePath, contextBody);
|
|
30952
|
+
}
|
|
30953
|
+
if (laneContextFilePath2) {
|
|
30954
|
+
this.writeContextFile(laneContextFilePath2, contextBody);
|
|
30394
30955
|
}
|
|
30395
30956
|
this.inFlightDispatches++;
|
|
30396
30957
|
const deadlineTimer = this.DISPATCH_DEADLINE_MS > 0 ? setTimeout(() => {
|
|
@@ -30417,7 +30978,7 @@ var ParallAgentGateway = class {
|
|
|
30417
30978
|
})) {
|
|
30418
30979
|
if (runtimeEvent.type === "runtime_session") {
|
|
30419
30980
|
const priorAgentSessionId = binding?.agentSessionId;
|
|
30420
|
-
binding = await this.bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath);
|
|
30981
|
+
binding = await this.bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath, laneContextFilePath2);
|
|
30421
30982
|
if (event.targetType === "channel_conversation" && binding.agentSessionId !== priorAgentSessionId) {
|
|
30422
30983
|
try {
|
|
30423
30984
|
await this.opts.client.setChannelConversationSession(this.opts.config.org_id, event.targetId, binding.agentSessionId);
|
|
@@ -30449,6 +31010,9 @@ var ParallAgentGateway = class {
|
|
|
30449
31010
|
await this.createInputStep(binding.agentSessionId, event);
|
|
30450
31011
|
inputStepsCreated = true;
|
|
30451
31012
|
}
|
|
31013
|
+
if (activeLane && !this.ledgerDisabled) {
|
|
31014
|
+
this.laneLedger?.maybeRenew(activeLane);
|
|
31015
|
+
}
|
|
30452
31016
|
if (captureText && runtimeEvent.type === "text" && runtimeEvent.text) {
|
|
30453
31017
|
captureText.push(runtimeEvent.text);
|
|
30454
31018
|
}
|
|
@@ -30465,7 +31029,7 @@ var ParallAgentGateway = class {
|
|
|
30465
31029
|
} else if (runtimeEvent.type === "tool_result" && pendingSendCallIds.delete(runtimeEvent.callId)) {
|
|
30466
31030
|
recordMessageSend(sessionKey, !runtimeEvent.error);
|
|
30467
31031
|
}
|
|
30468
|
-
await this.createRuntimeStep(binding.agentSessionId, event, runtimeEvent, stepIdFilePath, contextFilePath);
|
|
31032
|
+
await this.createRuntimeStep(binding.agentSessionId, event, runtimeEvent, stepIdFilePath, contextFilePath, laneContextFilePath2);
|
|
30469
31033
|
}
|
|
30470
31034
|
if (!binding) {
|
|
30471
31035
|
binding = this.sessionBindings.get(sessionKey);
|
|
@@ -30487,7 +31051,7 @@ var ParallAgentGateway = class {
|
|
|
30487
31051
|
await this.createRuntimeStep(binding.agentSessionId, event, {
|
|
30488
31052
|
type: "error",
|
|
30489
31053
|
message: `Dispatch failed: ${String(err)}`
|
|
30490
|
-
}, stepIdFilePath, contextFilePath);
|
|
31054
|
+
}, stepIdFilePath, contextFilePath, laneContextFilePath2);
|
|
30491
31055
|
} catch (stepErr) {
|
|
30492
31056
|
if (this.isSessionNotLiveError(stepErr))
|
|
30493
31057
|
staleDetected = true;
|
|
@@ -30530,6 +31094,9 @@ var ParallAgentGateway = class {
|
|
|
30530
31094
|
} else if (stepIdFilePath) {
|
|
30531
31095
|
this.clearStepIdFile(stepIdFilePath);
|
|
30532
31096
|
}
|
|
31097
|
+
if (laneContextFilePath2) {
|
|
31098
|
+
this.updateContextFileStepId(laneContextFilePath2, null);
|
|
31099
|
+
}
|
|
30533
31100
|
this.inFlightDispatches--;
|
|
30534
31101
|
if (this.inFlightDispatches === 0 && this.drainResolvers.length > 0) {
|
|
30535
31102
|
const resolvers = this.drainResolvers.splice(0);
|
|
@@ -30587,13 +31154,39 @@ var ParallAgentGateway = class {
|
|
|
30587
31154
|
item.resolve(false);
|
|
30588
31155
|
break;
|
|
30589
31156
|
}
|
|
30590
|
-
|
|
31157
|
+
let items;
|
|
31158
|
+
const head = fork.queue[0];
|
|
31159
|
+
if (head && this.usesLaneLedger(head.event)) {
|
|
31160
|
+
const headKey = this.dispatchGroupKey(head.event);
|
|
31161
|
+
const splitAt = fork.queue.findIndex((it) => this.dispatchGroupKey(it.event) !== headKey);
|
|
31162
|
+
items = splitAt === -1 ? fork.queue.splice(0) : fork.queue.splice(0, splitAt);
|
|
31163
|
+
} else {
|
|
31164
|
+
items = fork.queue.splice(0);
|
|
31165
|
+
}
|
|
30591
31166
|
const events = items.map((item) => item.event);
|
|
30592
31167
|
const last = events[events.length - 1];
|
|
30593
31168
|
const earlier = events.slice(0, -1);
|
|
30594
31169
|
try {
|
|
30595
31170
|
const batchText = [];
|
|
30596
|
-
|
|
31171
|
+
let dispatched;
|
|
31172
|
+
if (this.usesLaneLedger(last)) {
|
|
31173
|
+
const outcome = await this.dispatchLaneGroup({
|
|
31174
|
+
events,
|
|
31175
|
+
sessionKey: fork.fork.sessionKey,
|
|
31176
|
+
body: buildForkScopePrefix(last) + buildEventBody(last),
|
|
31177
|
+
earlier,
|
|
31178
|
+
captureText: batchText,
|
|
31179
|
+
hasMoreLocal: () => fork.queue.length > 0
|
|
31180
|
+
});
|
|
31181
|
+
if (outcome === "foreign") {
|
|
31182
|
+
for (const item of items)
|
|
31183
|
+
item.resolve(false);
|
|
31184
|
+
break;
|
|
31185
|
+
}
|
|
31186
|
+
dispatched = outcome === "dispatched";
|
|
31187
|
+
} else {
|
|
31188
|
+
dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
|
|
31189
|
+
}
|
|
30597
31190
|
if (!dispatched) {
|
|
30598
31191
|
for (const item of items) {
|
|
30599
31192
|
item.resolve(false);
|
|
@@ -30696,9 +31289,9 @@ var ParallAgentGateway = class {
|
|
|
30696
31289
|
this.opts.log?.info(`drainMainBuffer halted (shutting down) \u2014 ${this.dispatchState.mainBuffer.length} buffered, ${this.dispatchState.pendingForkResults.length} pending fork results left for catch-up`);
|
|
30697
31290
|
break;
|
|
30698
31291
|
}
|
|
30699
|
-
const
|
|
31292
|
+
const groupKey = this.dispatchGroupKey(this.dispatchState.mainBuffer[0]);
|
|
30700
31293
|
const events = [];
|
|
30701
|
-
while (this.dispatchState.mainBuffer[0]
|
|
31294
|
+
while (this.dispatchState.mainBuffer[0] && this.dispatchGroupKey(this.dispatchState.mainBuffer[0]) === groupKey) {
|
|
30702
31295
|
events.push(this.dispatchState.mainBuffer.shift());
|
|
30703
31296
|
}
|
|
30704
31297
|
const event = events[events.length - 1];
|
|
@@ -30707,7 +31300,36 @@ var ParallAgentGateway = class {
|
|
|
30707
31300
|
const pendingFork = hasPendingInjections ? [] : this.dispatchState.pendingForkResults.splice(0);
|
|
30708
31301
|
const forkPrefix = buildForkResultPrefix(pendingFork);
|
|
30709
31302
|
this.dispatchState.mainCurrentTargetId = event.targetId;
|
|
31303
|
+
this.mainCurrentGroupKey = groupKey;
|
|
30710
31304
|
this.dispatchState.mainPreDispatchBranchPoint = this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
|
|
31305
|
+
if (this.usesLaneLedger(event)) {
|
|
31306
|
+
let outcome;
|
|
31307
|
+
try {
|
|
31308
|
+
outcome = await this.dispatchLaneGroup({
|
|
31309
|
+
events,
|
|
31310
|
+
sessionKey: this.opts.runtimeKey,
|
|
31311
|
+
body: forkPrefix + buildEventBody(event),
|
|
31312
|
+
earlier,
|
|
31313
|
+
hasMoreLocal: () => this.dispatchState.mainBuffer.some((e) => this.dispatchGroupKey(e) === groupKey)
|
|
31314
|
+
});
|
|
31315
|
+
} catch (err) {
|
|
31316
|
+
this.opts.log?.error(`lane dispatch failed for ${event.messageId}: ${String(err)}`);
|
|
31317
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31318
|
+
for (const ev of events)
|
|
31319
|
+
this.dispatchedMessages.delete(ev.messageId);
|
|
31320
|
+
continue;
|
|
31321
|
+
}
|
|
31322
|
+
if (outcome === "shutdown") {
|
|
31323
|
+
this.dispatchState.mainBuffer.unshift(...events);
|
|
31324
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31325
|
+
break;
|
|
31326
|
+
}
|
|
31327
|
+
if (outcome === "foreign") {
|
|
31328
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31329
|
+
continue;
|
|
31330
|
+
}
|
|
31331
|
+
continue;
|
|
31332
|
+
}
|
|
30711
31333
|
try {
|
|
30712
31334
|
await this.emitDispatchReceived(event);
|
|
30713
31335
|
} catch (err) {
|
|
@@ -30736,6 +31358,7 @@ var ParallAgentGateway = class {
|
|
|
30736
31358
|
this.draining = false;
|
|
30737
31359
|
this.dispatchState.mainDispatching = false;
|
|
30738
31360
|
this.dispatchState.mainCurrentTargetId = void 0;
|
|
31361
|
+
this.mainCurrentGroupKey = void 0;
|
|
30739
31362
|
this.dispatchState.mainPreDispatchBranchPoint = void 0;
|
|
30740
31363
|
if (!this.shuttingDown && this.dispatchState.mainBuffer.length > 0) {
|
|
30741
31364
|
setTimeout(() => {
|
|
@@ -30755,13 +31378,38 @@ var ParallAgentGateway = class {
|
|
|
30755
31378
|
const forkPrefix = buildForkResultPrefix(pendingFork);
|
|
30756
31379
|
this.dispatchState.mainDispatching = true;
|
|
30757
31380
|
this.dispatchState.mainCurrentTargetId = event.targetId;
|
|
31381
|
+
this.mainCurrentGroupKey = this.dispatchGroupKey(event);
|
|
30758
31382
|
this.dispatchState.mainPreDispatchBranchPoint = this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
|
|
31383
|
+
if (this.usesLaneLedger(event)) {
|
|
31384
|
+
let outcome = "shutdown";
|
|
31385
|
+
try {
|
|
31386
|
+
try {
|
|
31387
|
+
outcome = await this.dispatchLaneGroup({
|
|
31388
|
+
events: [event],
|
|
31389
|
+
sessionKey: this.opts.runtimeKey,
|
|
31390
|
+
body: forkPrefix + buildEventBody(event),
|
|
31391
|
+
earlier: [],
|
|
31392
|
+
hasMoreLocal: () => this.dispatchState.mainBuffer.some((e) => this.dispatchGroupKey(e) === this.dispatchGroupKey(event))
|
|
31393
|
+
});
|
|
31394
|
+
} catch (err) {
|
|
31395
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31396
|
+
throw err;
|
|
31397
|
+
}
|
|
31398
|
+
if (outcome !== "dispatched") {
|
|
31399
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31400
|
+
}
|
|
31401
|
+
} finally {
|
|
31402
|
+
await this.drainMainBuffer();
|
|
31403
|
+
}
|
|
31404
|
+
return outcome === "dispatched";
|
|
31405
|
+
}
|
|
30759
31406
|
try {
|
|
30760
31407
|
await this.emitDispatchReceived(event);
|
|
30761
31408
|
} catch (err) {
|
|
30762
31409
|
this.opts.log?.warn?.(`mark-received failed, leaving unacked for retry: ${String(err)}`);
|
|
30763
31410
|
this.dispatchState.mainDispatching = false;
|
|
30764
31411
|
this.dispatchState.mainCurrentTargetId = void 0;
|
|
31412
|
+
this.mainCurrentGroupKey = void 0;
|
|
30765
31413
|
this.dispatchState.mainPreDispatchBranchPoint = void 0;
|
|
30766
31414
|
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
30767
31415
|
return false;
|
|
@@ -30782,7 +31430,11 @@ var ParallAgentGateway = class {
|
|
|
30782
31430
|
return false;
|
|
30783
31431
|
}
|
|
30784
31432
|
this.dispatchState.mainBuffer.push(event);
|
|
30785
|
-
if (this.
|
|
31433
|
+
if (this.usesLaneLedger(event)) {
|
|
31434
|
+
if (this.mainCurrentGroupKey === this.dispatchGroupKey(event) && await this.laneLedger?.steerLive(event) && await this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))) {
|
|
31435
|
+
this.opts.log?.info(`steer folded+injected for ${event.messageId} (will drain for bookkeeping)`);
|
|
31436
|
+
}
|
|
31437
|
+
} else if (this.dispatchState.mainCurrentTargetId === event.targetId && await this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))) {
|
|
30786
31438
|
this.opts.log?.info(`steer injected for ${event.messageId} (will drain for bookkeeping)`);
|
|
30787
31439
|
}
|
|
30788
31440
|
if (!this.dispatchState.mainDispatching && !this.draining && this.dispatchState.mainBuffer.length > 0) {
|
|
@@ -30953,8 +31605,10 @@ var ParallAgentGateway = class {
|
|
|
30953
31605
|
try {
|
|
30954
31606
|
const dispatched = await this.handleInboundEvent(event);
|
|
30955
31607
|
if (dispatched) {
|
|
30956
|
-
|
|
30957
|
-
|
|
31608
|
+
if (!this.usesLaneLedger(event)) {
|
|
31609
|
+
this.opts.client.ackDispatch(this.opts.config.org_id, { source_type: "message", source_id: data.id }).catch(() => {
|
|
31610
|
+
});
|
|
31611
|
+
}
|
|
30958
31612
|
} else {
|
|
30959
31613
|
this.dispatchedMessages.delete(data.id);
|
|
30960
31614
|
}
|
|
@@ -30963,7 +31617,21 @@ var ParallAgentGateway = class {
|
|
|
30963
31617
|
this.dispatchedMessages.delete(data.id);
|
|
30964
31618
|
}
|
|
30965
31619
|
}
|
|
30966
|
-
|
|
31620
|
+
// Ledger re-drive consumption: dispatch.new message hints re-enter the
|
|
31621
|
+
// shared WorkItem consumption path (same protocol as catch-up).
|
|
31622
|
+
async handleMessageRedrive(item) {
|
|
31623
|
+
if (!item.chat_id || !item.source_id)
|
|
31624
|
+
return;
|
|
31625
|
+
await this.consumeMessageWorkItem({
|
|
31626
|
+
id: item.id,
|
|
31627
|
+
source_id: item.source_id,
|
|
31628
|
+
chat_id: item.chat_id
|
|
31629
|
+
});
|
|
31630
|
+
}
|
|
31631
|
+
consumeMessageWorkItem(item) {
|
|
31632
|
+
return consumeMessageWorkItem(this.laneFlowHost(), item);
|
|
31633
|
+
}
|
|
31634
|
+
async handleTaskAssignment(task, ackSourceId, dispatchEventId) {
|
|
30967
31635
|
if (this.shuttingDown)
|
|
30968
31636
|
return false;
|
|
30969
31637
|
const dedupeKey = `${task.id}:${task.updated_at}`;
|
|
@@ -30992,7 +31660,8 @@ var ParallAgentGateway = class {
|
|
|
30992
31660
|
body: parts.join("\n"),
|
|
30993
31661
|
sentAt: task.updated_at ?? task.created_at,
|
|
30994
31662
|
ackSourceType: "task_activity",
|
|
30995
|
-
ackSourceId
|
|
31663
|
+
ackSourceId,
|
|
31664
|
+
dispatchEventId
|
|
30996
31665
|
};
|
|
30997
31666
|
const dispatched = await this.handleInboundEvent(event);
|
|
30998
31667
|
if (!dispatched) {
|
|
@@ -31016,7 +31685,7 @@ var ParallAgentGateway = class {
|
|
|
31016
31685
|
this.opts.log?.info(`skipping stale task dispatch ${ackSourceId ?? taskId} \u2014 assigned to ${task.assignee_id}, creator ${task.creator_id}`);
|
|
31017
31686
|
return true;
|
|
31018
31687
|
}
|
|
31019
|
-
return this.handleTaskAssignment(task, ackSourceId);
|
|
31688
|
+
return this.handleTaskAssignment(task, ackSourceId, opts.dispatchEventId);
|
|
31020
31689
|
}
|
|
31021
31690
|
async handleTaskComment(commentId, taskId, actorId, deliveryReason) {
|
|
31022
31691
|
if (this.shuttingDown)
|
|
@@ -31424,74 +32093,46 @@ var ParallAgentGateway = class {
|
|
|
31424
32093
|
}
|
|
31425
32094
|
processed++;
|
|
31426
32095
|
try {
|
|
31427
|
-
|
|
32096
|
+
const ackItem = () => {
|
|
32097
|
+
this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => {
|
|
32098
|
+
});
|
|
32099
|
+
};
|
|
31428
32100
|
if (item.event_type === "task_assign" && item.task_id) {
|
|
31429
32101
|
try {
|
|
31430
|
-
|
|
32102
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
|
|
32103
|
+
dispatchEventId
|
|
32104
|
+
}), ackItem);
|
|
31431
32105
|
} catch (err) {
|
|
31432
32106
|
this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
|
|
31433
32107
|
continue;
|
|
31434
32108
|
}
|
|
31435
32109
|
} else if (item.event_type === "task_update" && item.task_id) {
|
|
31436
32110
|
try {
|
|
31437
|
-
|
|
32111
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
|
|
32112
|
+
allowCreator: true,
|
|
32113
|
+
dispatchEventId
|
|
32114
|
+
}), ackItem);
|
|
31438
32115
|
} catch (err) {
|
|
31439
32116
|
this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
|
|
31440
32117
|
continue;
|
|
31441
32118
|
}
|
|
31442
32119
|
} else if (item.event_type === "task_comment" && item.source_id && item.task_id) {
|
|
31443
|
-
|
|
32120
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.handleTaskComment(item.source_id, item.task_id ?? "", item.actor_id, item.delivery_reason), ackItem);
|
|
31444
32121
|
} else if (item.event_type === "wiki_comment" && item.source_id) {
|
|
31445
|
-
|
|
32122
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.handleWikiComment(item.source_id, item.actor_id, item.delivery_reason), ackItem);
|
|
31446
32123
|
} else if (item.event_type === "schedule.fire" && item.source_id) {
|
|
31447
|
-
|
|
32124
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleScheduleFire(item.source_id, item.actor_id), ackItem);
|
|
31448
32125
|
} else if (item.event_type === "external_trigger" && item.source_id) {
|
|
31449
|
-
|
|
32126
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleExternalTriggerRun(item.source_id), ackItem);
|
|
31450
32127
|
} else if (item.event_type === "channel_message" && item.source_id) {
|
|
31451
|
-
|
|
32128
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleChannelMessage(item.source_id), ackItem);
|
|
31452
32129
|
} else if (item.event_type === "approval_decided" && item.source_id) {
|
|
31453
|
-
|
|
32130
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleApprovalDecided(item.source_id, item.actor_id, item.chat_id ?? null), ackItem);
|
|
31454
32131
|
} else if (item.event_type === "message" && item.source_id && item.chat_id) {
|
|
31455
|
-
|
|
31456
|
-
|
|
31457
|
-
|
|
31458
|
-
|
|
31459
|
-
try {
|
|
31460
|
-
msg = await this.opts.client.getMessage(item.source_id);
|
|
31461
|
-
} catch (err) {
|
|
31462
|
-
const status = err?.status;
|
|
31463
|
-
if (status === 404) {
|
|
31464
|
-
msg = null;
|
|
31465
|
-
} else {
|
|
31466
|
-
msgFetchFailed = true;
|
|
31467
|
-
this.opts.log?.warn(`catch-up message fetch failed for ${item.source_id}, leaving pending: ${String(err)}`);
|
|
31468
|
-
}
|
|
31469
|
-
}
|
|
31470
|
-
if (msgFetchFailed) {
|
|
31471
|
-
this.dispatchedMessages.delete(item.source_id);
|
|
31472
|
-
continue;
|
|
31473
|
-
}
|
|
31474
|
-
if (!msg || msg.sender_id === this.opts.agentUserId) {
|
|
31475
|
-
this.dispatchedMessages.delete(item.source_id);
|
|
31476
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => {
|
|
31477
|
-
});
|
|
31478
|
-
continue;
|
|
31479
|
-
}
|
|
31480
|
-
const decision = await this.buildMessageDispatchDecision(item.chat_id, msg);
|
|
31481
|
-
if (decision.action === "retry") {
|
|
31482
|
-
this.dispatchedMessages.delete(item.source_id);
|
|
31483
|
-
continue;
|
|
31484
|
-
}
|
|
31485
|
-
if (decision.action === "skip") {
|
|
31486
|
-
this.dispatchedMessages.delete(item.source_id);
|
|
31487
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => {
|
|
31488
|
-
});
|
|
31489
|
-
continue;
|
|
31490
|
-
}
|
|
31491
|
-
dispatched = await this.handleInboundEvent(decision.event);
|
|
31492
|
-
}
|
|
31493
|
-
if (dispatched) {
|
|
31494
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => {
|
|
32132
|
+
await this.consumeMessageWorkItem({
|
|
32133
|
+
id: item.id,
|
|
32134
|
+
source_id: item.source_id,
|
|
32135
|
+
chat_id: item.chat_id
|
|
31495
32136
|
});
|
|
31496
32137
|
}
|
|
31497
32138
|
} catch (err) {
|
|
@@ -31557,6 +32198,14 @@ ${fullSummary}` : fullSummary;
|
|
|
31557
32198
|
this.abortFork(targetId, "ws reconnect");
|
|
31558
32199
|
}
|
|
31559
32200
|
}
|
|
32201
|
+
if (this.laneLedger && this.inFlightDispatches === 0 && this.laneLedger.activeCount > 0) {
|
|
32202
|
+
log2?.info(`releasing ${this.laneLedger.activeCount} stale lane(s) on reconnect`);
|
|
32203
|
+
await this.laneLedger.releaseAll();
|
|
32204
|
+
}
|
|
32205
|
+
if (this.laneLedger && this.ledgerDisabled) {
|
|
32206
|
+
log2?.info("re-probing dispatch ledger after reconnect (was disabled)");
|
|
32207
|
+
this.ledgerDisabled = false;
|
|
32208
|
+
}
|
|
31560
32209
|
const intervalSec = data.heartbeat_interval > 0 ? data.heartbeat_interval : 30;
|
|
31561
32210
|
try {
|
|
31562
32211
|
const count = await fetchAllChats(client, config.org_id, this.chatInfoMap);
|
|
@@ -31627,6 +32276,10 @@ ${fullSummary}` : fullSummary;
|
|
|
31627
32276
|
}
|
|
31628
32277
|
if (this.heartbeatTimer)
|
|
31629
32278
|
clearInterval(this.heartbeatTimer);
|
|
32279
|
+
if (this.laneLedger && this.laneLedger.activeCount > 0) {
|
|
32280
|
+
this.opts.log?.info(`releasing ${this.laneLedger.activeCount} lane(s) on shutdown`);
|
|
32281
|
+
await this.laneLedger.releaseAll();
|
|
32282
|
+
}
|
|
31630
32283
|
await this.opts.onBeforeDisconnect?.();
|
|
31631
32284
|
this.opts.ws.disconnect();
|
|
31632
32285
|
this.opts.log?.info(`disconnected`);
|
|
@@ -31634,8 +32287,8 @@ ${fullSummary}` : fullSummary;
|
|
|
31634
32287
|
};
|
|
31635
32288
|
|
|
31636
32289
|
// ts/agent-core/dist/platform-config.js
|
|
31637
|
-
import * as
|
|
31638
|
-
import * as
|
|
32290
|
+
import * as fs3 from "node:fs";
|
|
32291
|
+
import * as path3 from "node:path";
|
|
31639
32292
|
function isPlatformManagedProfile(profile) {
|
|
31640
32293
|
return profile?.machine_id != null || profile?.model_management === "platform";
|
|
31641
32294
|
}
|
|
@@ -31652,11 +32305,11 @@ function deriveModelIsPin(defaults, profile) {
|
|
|
31652
32305
|
var CACHE_FILENAME = "parall-platform-config.json";
|
|
31653
32306
|
var SUPPORTED_SCHEMA_VERSION = 1;
|
|
31654
32307
|
function cachePath(stateDir) {
|
|
31655
|
-
return
|
|
32308
|
+
return path3.join(stateDir, CACHE_FILENAME);
|
|
31656
32309
|
}
|
|
31657
32310
|
function loadCache(stateDir) {
|
|
31658
32311
|
try {
|
|
31659
|
-
const raw =
|
|
32312
|
+
const raw = fs3.readFileSync(cachePath(stateDir), "utf-8");
|
|
31660
32313
|
return JSON.parse(raw);
|
|
31661
32314
|
} catch {
|
|
31662
32315
|
return null;
|
|
@@ -31671,9 +32324,9 @@ function saveCache(stateDir, response) {
|
|
|
31671
32324
|
};
|
|
31672
32325
|
const filePath = cachePath(stateDir);
|
|
31673
32326
|
const tmpPath = `${filePath}.tmp`;
|
|
31674
|
-
|
|
31675
|
-
|
|
31676
|
-
|
|
32327
|
+
fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
|
|
32328
|
+
fs3.writeFileSync(tmpPath, JSON.stringify(cached, null, 2), "utf-8");
|
|
32329
|
+
fs3.renameSync(tmpPath, filePath);
|
|
31677
32330
|
}
|
|
31678
32331
|
function runtimeModelName(canonicalModel, runtimeType, config) {
|
|
31679
32332
|
const runtime = runtimeType?.trim();
|
|
@@ -31764,8 +32417,8 @@ function createPlatformConfigManager(opts) {
|
|
|
31764
32417
|
}
|
|
31765
32418
|
|
|
31766
32419
|
// ts/agent-core/dist/skills/index.js
|
|
31767
|
-
import * as
|
|
31768
|
-
import * as
|
|
32420
|
+
import * as fs4 from "node:fs";
|
|
32421
|
+
import * as path4 from "node:path";
|
|
31769
32422
|
|
|
31770
32423
|
// ts/agent-core/dist/skills/parall-platform.js
|
|
31771
32424
|
var PARALL_PLATFORM_SKILL = `# Parall Platform
|
|
@@ -32538,13 +33191,13 @@ var SKILLS = [
|
|
|
32538
33191
|
}
|
|
32539
33192
|
];
|
|
32540
33193
|
function writeSkillFiles(targetDir) {
|
|
32541
|
-
|
|
33194
|
+
fs4.mkdirSync(targetDir, { recursive: true });
|
|
32542
33195
|
for (const skill of SKILLS) {
|
|
32543
|
-
|
|
33196
|
+
fs4.writeFileSync(path4.join(targetDir, `${skill.name}.md`), skill.content, "utf8");
|
|
32544
33197
|
}
|
|
32545
33198
|
}
|
|
32546
33199
|
function buildSkillReferences(workspaceDir) {
|
|
32547
|
-
const dir =
|
|
33200
|
+
const dir = path4.join(workspaceDir, ".parall", "skills");
|
|
32548
33201
|
const lines = SKILLS.map((s) => `- ${s.description.split(":")[0]}: \`${dir}/${s.name}.md\``);
|
|
32549
33202
|
return `## Platform Skills (read on demand)
|
|
32550
33203
|
|
|
@@ -32554,7 +33207,7 @@ ${lines.join("\n")}
|
|
|
32554
33207
|
|
|
32555
33208
|
// ts/codex-agent/dist/config.js
|
|
32556
33209
|
import * as os2 from "node:os";
|
|
32557
|
-
import * as
|
|
33210
|
+
import * as path5 from "node:path";
|
|
32558
33211
|
function requireEnv(env, name) {
|
|
32559
33212
|
const value = env[name]?.trim();
|
|
32560
33213
|
if (!value) {
|
|
@@ -32563,15 +33216,15 @@ function requireEnv(env, name) {
|
|
|
32563
33216
|
return value;
|
|
32564
33217
|
}
|
|
32565
33218
|
function resolvePath(value) {
|
|
32566
|
-
return
|
|
33219
|
+
return path5.isAbsolute(value) ? value : path5.resolve(process.cwd(), value);
|
|
32567
33220
|
}
|
|
32568
33221
|
function resolveCodexAgentConfig(env = process.env) {
|
|
32569
33222
|
const apiUrl = requireEnv(env, "PRLL_API_URL");
|
|
32570
33223
|
const apiKey = requireEnv(env, "PRLL_API_KEY");
|
|
32571
33224
|
const orgId = requireEnv(env, "PRLL_ORG_ID");
|
|
32572
|
-
const codexHome = resolvePath(env.PRLL_CODEX_HOME?.trim() || env.CODEX_HOME?.trim() ||
|
|
32573
|
-
const stateDir = resolvePath(env.PRLL_STATE_DIR?.trim() ||
|
|
32574
|
-
const workspaceDir = resolvePath(env.PRLL_WORKSPACE_DIR?.trim() ||
|
|
33225
|
+
const codexHome = resolvePath(env.PRLL_CODEX_HOME?.trim() || env.CODEX_HOME?.trim() || path5.join(env.HOME || os2.homedir(), ".codex"));
|
|
33226
|
+
const stateDir = resolvePath(env.PRLL_STATE_DIR?.trim() || path5.join(env.HOME || os2.homedir(), ".parall-agent"));
|
|
33227
|
+
const workspaceDir = resolvePath(env.PRLL_WORKSPACE_DIR?.trim() || path5.join(stateDir, "workspace"));
|
|
32575
33228
|
return {
|
|
32576
33229
|
apiUrl,
|
|
32577
33230
|
apiKey,
|
|
@@ -32625,28 +33278,31 @@ function buildCodexRuntimeKey(agentUserId) {
|
|
|
32625
33278
|
}
|
|
32626
33279
|
function sessionStateFilePathForRuntime(stateDir, runtimeKey) {
|
|
32627
33280
|
const fileName = Buffer.from(runtimeKey).toString("base64url");
|
|
32628
|
-
return
|
|
33281
|
+
return path5.join(stateDir, "threads", `${fileName}.json`);
|
|
32629
33282
|
}
|
|
32630
33283
|
function contextFilePathForSession(stateDir, sessionKey) {
|
|
32631
33284
|
const fileName = Buffer.from(sessionKey).toString("base64url");
|
|
32632
|
-
return
|
|
33285
|
+
return path5.join(stateDir, "dispatch-context", `${fileName}.json`);
|
|
33286
|
+
}
|
|
33287
|
+
function dispatchContextDirPath(stateDir) {
|
|
33288
|
+
return dispatchLaneContextDir(stateDir);
|
|
32633
33289
|
}
|
|
32634
33290
|
function stepIdFilePathForSession(stateDir, sessionKey) {
|
|
32635
33291
|
const fileName = Buffer.from(sessionKey).toString("base64url");
|
|
32636
|
-
return
|
|
33292
|
+
return path5.join(stateDir, "step-ids", `${fileName}.txt`);
|
|
32637
33293
|
}
|
|
32638
33294
|
|
|
32639
33295
|
// ts/codex-agent/dist/dispatch.js
|
|
32640
33296
|
import { execSync as execSync2, spawn } from "node:child_process";
|
|
32641
33297
|
import { randomUUID } from "node:crypto";
|
|
32642
|
-
import * as
|
|
33298
|
+
import * as fs6 from "node:fs";
|
|
32643
33299
|
|
|
32644
33300
|
// ts/agent-core/dist/internal/attachment-input.js
|
|
32645
33301
|
import { execSync } from "node:child_process";
|
|
32646
33302
|
import { constants } from "node:fs";
|
|
32647
33303
|
import * as fsSync from "node:fs";
|
|
32648
|
-
import * as
|
|
32649
|
-
import * as
|
|
33304
|
+
import * as fs5 from "node:fs/promises";
|
|
33305
|
+
import * as path6 from "node:path";
|
|
32650
33306
|
var DEFAULT_MAX_TOTAL_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
32651
33307
|
var DEFAULT_ATTACHMENT_CACHE_MAX_BYTES = 512 * 1024 * 1024;
|
|
32652
33308
|
var DEFAULT_ATTACHMENT_DOWNLOAD_TIMEOUT_MS = 3e4;
|
|
@@ -32676,11 +33332,11 @@ async function prepareLocalImageAttachments(event, context2, opts) {
|
|
|
32676
33332
|
};
|
|
32677
33333
|
}
|
|
32678
33334
|
const rootDir = await ensureAttachmentRootDir(opts.workspaceDir);
|
|
32679
|
-
const messageDir =
|
|
33335
|
+
const messageDir = path6.join(rootDir, sanitizePathSegment(event.messageId));
|
|
32680
33336
|
await ensurePathIsNotSymlink(messageDir);
|
|
32681
|
-
await
|
|
33337
|
+
await fs5.mkdir(messageDir, { recursive: true });
|
|
32682
33338
|
await ensurePathIsNotSymlink(messageDir);
|
|
32683
|
-
const activeMessageDir =
|
|
33339
|
+
const activeMessageDir = path6.resolve(messageDir);
|
|
32684
33340
|
activeAttachmentDirs.add(activeMessageDir);
|
|
32685
33341
|
const maintenanceCooldownMs = opts.maintenanceCooldownMs ?? DEFAULT_MAINTENANCE_COOLDOWN_MS;
|
|
32686
33342
|
const maintenancePromise = scheduleAttachmentMaintenance(rootDir, {
|
|
@@ -32697,7 +33353,7 @@ async function prepareLocalImageAttachments(event, context2, opts) {
|
|
|
32697
33353
|
const notes = [];
|
|
32698
33354
|
let downloadedBytes = 0;
|
|
32699
33355
|
for (const att of imageAttachments) {
|
|
32700
|
-
const localPath =
|
|
33356
|
+
const localPath = path6.join(messageDir, localFileName(att.id, att.fileName, att.mimeType));
|
|
32701
33357
|
const downloadTimeoutMs = opts.downloadTimeoutMs ?? DEFAULT_ATTACHMENT_DOWNLOAD_TIMEOUT_MS;
|
|
32702
33358
|
const fetchFresh = async () => {
|
|
32703
33359
|
const fileInfo = await withTimeout(context2.client.getFileUrl(att.id), downloadTimeoutMs, `file URL lookup timed out after ${downloadTimeoutMs}ms`);
|
|
@@ -32754,7 +33410,7 @@ async function appendPreparedLocalAttachmentRefs(body, event, context2, opts) {
|
|
|
32754
33410
|
return { body: appendLocalAttachmentRefs(body, attachments), attachments };
|
|
32755
33411
|
}
|
|
32756
33412
|
function pinLocalAttachmentPaths(images) {
|
|
32757
|
-
const dirs = new Set(images.map((image) =>
|
|
33413
|
+
const dirs = new Set(images.map((image) => path6.resolve(path6.dirname(image.localPath))));
|
|
32758
33414
|
for (const dir of dirs) {
|
|
32759
33415
|
activeAttachmentDirs.add(dir);
|
|
32760
33416
|
}
|
|
@@ -32769,7 +33425,7 @@ function pinLocalAttachmentPaths(images) {
|
|
|
32769
33425
|
};
|
|
32770
33426
|
}
|
|
32771
33427
|
function attachmentRootDir(workspaceDir) {
|
|
32772
|
-
return
|
|
33428
|
+
return path6.join(path6.resolve(workspaceDir), ".parall", "attachments");
|
|
32773
33429
|
}
|
|
32774
33430
|
function ensureLocalAttachmentGitExclude(workingDirectory) {
|
|
32775
33431
|
try {
|
|
@@ -32778,8 +33434,8 @@ function ensureLocalAttachmentGitExclude(workingDirectory) {
|
|
|
32778
33434
|
encoding: "utf8",
|
|
32779
33435
|
stdio: ["ignore", "pipe", "ignore"]
|
|
32780
33436
|
}).trim();
|
|
32781
|
-
const excludePath =
|
|
32782
|
-
fsSync.mkdirSync(
|
|
33437
|
+
const excludePath = path6.isAbsolute(rel) ? rel : path6.join(workingDirectory, rel);
|
|
33438
|
+
fsSync.mkdirSync(path6.dirname(excludePath), { recursive: true });
|
|
32783
33439
|
const existing = fsSync.existsSync(excludePath) ? fsSync.readFileSync(excludePath, "utf8") : "";
|
|
32784
33440
|
if (existing.split(/\r?\n/).some((line) => line.trim() === ".parall/"))
|
|
32785
33441
|
return;
|
|
@@ -32815,18 +33471,18 @@ function scheduleAttachmentMaintenance(rootDir, opts) {
|
|
|
32815
33471
|
return run;
|
|
32816
33472
|
}
|
|
32817
33473
|
async function ensureAttachmentRootDir(workspaceDir) {
|
|
32818
|
-
const workspaceRoot =
|
|
32819
|
-
const parallDir =
|
|
33474
|
+
const workspaceRoot = path6.resolve(workspaceDir);
|
|
33475
|
+
const parallDir = path6.join(workspaceRoot, ".parall");
|
|
32820
33476
|
const rootDir = attachmentRootDir(workspaceRoot);
|
|
32821
|
-
await
|
|
33477
|
+
await fs5.mkdir(workspaceRoot, { recursive: true });
|
|
32822
33478
|
await ensurePathIsNotSymlink(parallDir);
|
|
32823
|
-
await
|
|
33479
|
+
await fs5.mkdir(parallDir, { recursive: true, mode: 448 });
|
|
32824
33480
|
await ensurePathIsNotSymlink(parallDir);
|
|
32825
33481
|
await ensurePathIsNotSymlink(rootDir);
|
|
32826
|
-
await
|
|
33482
|
+
await fs5.mkdir(rootDir, { recursive: true, mode: 448 });
|
|
32827
33483
|
await ensurePathIsNotSymlink(rootDir);
|
|
32828
|
-
const realWorkspace = await
|
|
32829
|
-
const realRoot = await
|
|
33484
|
+
const realWorkspace = await fs5.realpath(workspaceRoot);
|
|
33485
|
+
const realRoot = await fs5.realpath(rootDir);
|
|
32830
33486
|
if (!isPathInside(realRoot, realWorkspace)) {
|
|
32831
33487
|
throw new Error(`attachment root escapes workspace: ${rootDir}`);
|
|
32832
33488
|
}
|
|
@@ -32834,7 +33490,7 @@ async function ensureAttachmentRootDir(workspaceDir) {
|
|
|
32834
33490
|
}
|
|
32835
33491
|
async function ensurePathIsNotSymlink(filePath) {
|
|
32836
33492
|
try {
|
|
32837
|
-
const stat = await
|
|
33493
|
+
const stat = await fs5.lstat(filePath);
|
|
32838
33494
|
if (stat.isSymbolicLink()) {
|
|
32839
33495
|
throw new Error(`refusing to use symlinked attachment path ${filePath}`);
|
|
32840
33496
|
}
|
|
@@ -32845,8 +33501,8 @@ async function ensurePathIsNotSymlink(filePath) {
|
|
|
32845
33501
|
}
|
|
32846
33502
|
}
|
|
32847
33503
|
function isPathInside(childPath, parentPath) {
|
|
32848
|
-
const rel =
|
|
32849
|
-
return rel === "" || !!rel && !rel.startsWith("..") && !
|
|
33504
|
+
const rel = path6.relative(parentPath, childPath);
|
|
33505
|
+
return rel === "" || !!rel && !rel.startsWith("..") && !path6.isAbsolute(rel);
|
|
32850
33506
|
}
|
|
32851
33507
|
async function existingUsableFile(filePath, expectedSize, rootDir) {
|
|
32852
33508
|
try {
|
|
@@ -32857,30 +33513,30 @@ async function existingUsableFile(filePath, expectedSize, rootDir) {
|
|
|
32857
33513
|
}
|
|
32858
33514
|
}
|
|
32859
33515
|
async function localFileStatInsideRoot(filePath, rootDir) {
|
|
32860
|
-
const stat = await
|
|
33516
|
+
const stat = await fs5.lstat(filePath);
|
|
32861
33517
|
if (stat.isSymbolicLink()) {
|
|
32862
33518
|
throw new Error(`refusing to use symlinked attachment file ${filePath}`);
|
|
32863
33519
|
}
|
|
32864
33520
|
if (!stat.isFile()) {
|
|
32865
33521
|
throw new Error(`attachment path is not a file ${filePath}`);
|
|
32866
33522
|
}
|
|
32867
|
-
const realRoot = await
|
|
32868
|
-
const realFile = await
|
|
33523
|
+
const realRoot = await fs5.realpath(rootDir);
|
|
33524
|
+
const realFile = await fs5.realpath(filePath);
|
|
32869
33525
|
if (!isPathInside(realFile, realRoot)) {
|
|
32870
33526
|
throw new Error(`attachment file escapes workspace: ${filePath}`);
|
|
32871
33527
|
}
|
|
32872
33528
|
return stat;
|
|
32873
33529
|
}
|
|
32874
33530
|
async function localDirectoryStatInsideRoot(dirPath, rootDir) {
|
|
32875
|
-
const stat = await
|
|
33531
|
+
const stat = await fs5.lstat(dirPath);
|
|
32876
33532
|
if (stat.isSymbolicLink()) {
|
|
32877
33533
|
throw new Error(`refusing to use symlinked attachment directory ${dirPath}`);
|
|
32878
33534
|
}
|
|
32879
33535
|
if (!stat.isDirectory()) {
|
|
32880
33536
|
throw new Error(`attachment path is not a directory ${dirPath}`);
|
|
32881
33537
|
}
|
|
32882
|
-
const realRoot = await
|
|
32883
|
-
const realDir = await
|
|
33538
|
+
const realRoot = await fs5.realpath(rootDir);
|
|
33539
|
+
const realDir = await fs5.realpath(dirPath);
|
|
32884
33540
|
if (!isPathInside(realDir, realRoot)) {
|
|
32885
33541
|
throw new Error(`attachment directory escapes workspace: ${dirPath}`);
|
|
32886
33542
|
}
|
|
@@ -32888,7 +33544,7 @@ async function localDirectoryStatInsideRoot(dirPath, rootDir) {
|
|
|
32888
33544
|
}
|
|
32889
33545
|
async function openLocalFileInsideRoot(filePath, rootDir) {
|
|
32890
33546
|
const checkedStat = await localFileStatInsideRoot(filePath, rootDir);
|
|
32891
|
-
const file = await
|
|
33547
|
+
const file = await fs5.open(filePath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
32892
33548
|
let keepOpen = false;
|
|
32893
33549
|
try {
|
|
32894
33550
|
const openedStat = await file.stat();
|
|
@@ -32904,8 +33560,8 @@ async function openLocalFileInsideRoot(filePath, rootDir) {
|
|
|
32904
33560
|
}
|
|
32905
33561
|
}
|
|
32906
33562
|
async function openLocalTempFileInsideRoot(filePath, rootDir) {
|
|
32907
|
-
await localDirectoryStatInsideRoot(
|
|
32908
|
-
const file = await
|
|
33563
|
+
await localDirectoryStatInsideRoot(path6.dirname(filePath), rootDir);
|
|
33564
|
+
const file = await fs5.open(filePath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
|
|
32909
33565
|
let keepOpen = false;
|
|
32910
33566
|
try {
|
|
32911
33567
|
const checkedStat = await localFileStatInsideRoot(filePath, rootDir);
|
|
@@ -32930,7 +33586,7 @@ async function assertLocalFileIdentity(filePath, rootDir, expected) {
|
|
|
32930
33586
|
async function removeLocalFileIfInside(filePath, rootDir) {
|
|
32931
33587
|
try {
|
|
32932
33588
|
await localFileStatInsideRoot(filePath, rootDir);
|
|
32933
|
-
await
|
|
33589
|
+
await fs5.rm(filePath, { force: true });
|
|
32934
33590
|
} catch {
|
|
32935
33591
|
}
|
|
32936
33592
|
}
|
|
@@ -32943,7 +33599,7 @@ function sameFile(a, b) {
|
|
|
32943
33599
|
async function cleanupOldAttachmentFiles(rootDir, ttlMs, log2, preserveDirs) {
|
|
32944
33600
|
let entries;
|
|
32945
33601
|
try {
|
|
32946
|
-
entries = await
|
|
33602
|
+
entries = await fs5.readdir(rootDir, { withFileTypes: true });
|
|
32947
33603
|
} catch {
|
|
32948
33604
|
return;
|
|
32949
33605
|
}
|
|
@@ -32951,15 +33607,15 @@ async function cleanupOldAttachmentFiles(rootDir, ttlMs, log2, preserveDirs) {
|
|
|
32951
33607
|
await Promise.all(entries.map(async (entry) => {
|
|
32952
33608
|
if (!entry.isDirectory())
|
|
32953
33609
|
return;
|
|
32954
|
-
const fullPath =
|
|
33610
|
+
const fullPath = path6.join(rootDir, entry.name);
|
|
32955
33611
|
try {
|
|
32956
|
-
if (preserveDirs?.has(
|
|
33612
|
+
if (preserveDirs?.has(path6.resolve(fullPath)))
|
|
32957
33613
|
return;
|
|
32958
|
-
const stat = await
|
|
33614
|
+
const stat = await fs5.lstat(fullPath);
|
|
32959
33615
|
if (!stat.isDirectory())
|
|
32960
33616
|
return;
|
|
32961
33617
|
if (stat.mtimeMs < cutoff) {
|
|
32962
|
-
await
|
|
33618
|
+
await fs5.rm(fullPath, { recursive: true, force: true });
|
|
32963
33619
|
}
|
|
32964
33620
|
} catch (err) {
|
|
32965
33621
|
log2?.warn?.(`agent-core: failed to clean attachment temp dir ${fullPath}: ${String(err)}`);
|
|
@@ -32971,7 +33627,7 @@ async function pruneAttachmentCache(rootDir, maxBytes, log2, preserveDirs) {
|
|
|
32971
33627
|
return;
|
|
32972
33628
|
let entries;
|
|
32973
33629
|
try {
|
|
32974
|
-
entries = await
|
|
33630
|
+
entries = await fs5.readdir(rootDir, { withFileTypes: true });
|
|
32975
33631
|
} catch {
|
|
32976
33632
|
return;
|
|
32977
33633
|
}
|
|
@@ -32980,9 +33636,9 @@ async function pruneAttachmentCache(rootDir, maxBytes, log2, preserveDirs) {
|
|
|
32980
33636
|
for (const entry of entries) {
|
|
32981
33637
|
if (!entry.isDirectory())
|
|
32982
33638
|
continue;
|
|
32983
|
-
const fullPath =
|
|
33639
|
+
const fullPath = path6.join(rootDir, entry.name);
|
|
32984
33640
|
try {
|
|
32985
|
-
const stat = await
|
|
33641
|
+
const stat = await fs5.lstat(fullPath);
|
|
32986
33642
|
if (!stat.isDirectory())
|
|
32987
33643
|
continue;
|
|
32988
33644
|
const size = await directorySize(fullPath);
|
|
@@ -32998,10 +33654,10 @@ async function pruneAttachmentCache(rootDir, maxBytes, log2, preserveDirs) {
|
|
|
32998
33654
|
for (const dir of dirs) {
|
|
32999
33655
|
if (total <= maxBytes)
|
|
33000
33656
|
break;
|
|
33001
|
-
if (preserveDirs?.has(
|
|
33657
|
+
if (preserveDirs?.has(path6.resolve(dir.path)))
|
|
33002
33658
|
continue;
|
|
33003
33659
|
try {
|
|
33004
|
-
await
|
|
33660
|
+
await fs5.rm(dir.path, { recursive: true, force: true });
|
|
33005
33661
|
total -= dir.size;
|
|
33006
33662
|
} catch (err) {
|
|
33007
33663
|
log2?.warn?.(`agent-core: failed to prune attachment cache dir ${dir.path}: ${String(err)}`);
|
|
@@ -33010,12 +33666,12 @@ async function pruneAttachmentCache(rootDir, maxBytes, log2, preserveDirs) {
|
|
|
33010
33666
|
}
|
|
33011
33667
|
async function directorySize(dirPath) {
|
|
33012
33668
|
let total = 0;
|
|
33013
|
-
const entries = await
|
|
33669
|
+
const entries = await fs5.readdir(dirPath, { withFileTypes: true });
|
|
33014
33670
|
for (const entry of entries) {
|
|
33015
|
-
const fullPath =
|
|
33671
|
+
const fullPath = path6.join(dirPath, entry.name);
|
|
33016
33672
|
let stat;
|
|
33017
33673
|
try {
|
|
33018
|
-
stat = await
|
|
33674
|
+
stat = await fs5.lstat(fullPath);
|
|
33019
33675
|
} catch {
|
|
33020
33676
|
continue;
|
|
33021
33677
|
}
|
|
@@ -33030,10 +33686,10 @@ async function directorySize(dirPath) {
|
|
|
33030
33686
|
return total;
|
|
33031
33687
|
}
|
|
33032
33688
|
function activeDirsForRoot(rootDir) {
|
|
33033
|
-
const root =
|
|
33689
|
+
const root = path6.resolve(rootDir);
|
|
33034
33690
|
const dirs = /* @__PURE__ */ new Set();
|
|
33035
33691
|
for (const dir of activeAttachmentDirs) {
|
|
33036
|
-
if (dir === root || dir.startsWith(`${root}${
|
|
33692
|
+
if (dir === root || dir.startsWith(`${root}${path6.sep}`)) {
|
|
33037
33693
|
dirs.add(dir);
|
|
33038
33694
|
}
|
|
33039
33695
|
}
|
|
@@ -33130,9 +33786,9 @@ async function writeResponseToFileWithLimit(res, filePath, maxBytes, rootDir) {
|
|
|
33130
33786
|
}
|
|
33131
33787
|
writtenStat = await file.stat();
|
|
33132
33788
|
await closeFile();
|
|
33133
|
-
await localDirectoryStatInsideRoot(
|
|
33789
|
+
await localDirectoryStatInsideRoot(path6.dirname(filePath), rootDir);
|
|
33134
33790
|
await assertLocalFileIdentity(tmpPath, rootDir, writtenStat);
|
|
33135
|
-
await
|
|
33791
|
+
await fs5.rename(tmpPath, filePath);
|
|
33136
33792
|
completed = true;
|
|
33137
33793
|
return written;
|
|
33138
33794
|
} finally {
|
|
@@ -33148,9 +33804,9 @@ async function writeResponseToFileWithLimit(res, filePath, maxBytes, rootDir) {
|
|
|
33148
33804
|
}
|
|
33149
33805
|
}
|
|
33150
33806
|
function localFileName(attachmentId, fileName, mimeType) {
|
|
33151
|
-
const safeName = sanitizePathSegment(
|
|
33152
|
-
const ext =
|
|
33153
|
-
const stem =
|
|
33807
|
+
const safeName = sanitizePathSegment(path6.basename(fileName || attachmentId));
|
|
33808
|
+
const ext = path6.extname(safeName) || extensionForMime(mimeType);
|
|
33809
|
+
const stem = path6.basename(safeName, path6.extname(safeName)) || attachmentId;
|
|
33154
33810
|
return `${sanitizePathSegment(attachmentId)}-${stem}${ext}`;
|
|
33155
33811
|
}
|
|
33156
33812
|
function extensionForMime(mimeType) {
|
|
@@ -33933,6 +34589,9 @@ var CodexAppServerAdapter = class {
|
|
|
33933
34589
|
if (this.opts.contextFilePath) {
|
|
33934
34590
|
env.PRLL_CONTEXT_FILE = this.opts.contextFilePath;
|
|
33935
34591
|
}
|
|
34592
|
+
if (this.opts.contextDirPath) {
|
|
34593
|
+
env.PRLL_CONTEXT_DIR = this.opts.contextDirPath;
|
|
34594
|
+
}
|
|
33936
34595
|
if (!env.PRLL_WIKI_MOUNT_ROOT?.trim()) {
|
|
33937
34596
|
env.PRLL_WIKI_MOUNT_ROOT = this.opts.workspaceDir;
|
|
33938
34597
|
}
|
|
@@ -34078,7 +34737,7 @@ var TurnSink = class {
|
|
|
34078
34737
|
}
|
|
34079
34738
|
};
|
|
34080
34739
|
function ensureGitRepo(workingDirectory) {
|
|
34081
|
-
|
|
34740
|
+
fs6.mkdirSync(workingDirectory, { recursive: true });
|
|
34082
34741
|
try {
|
|
34083
34742
|
execSync2("git rev-parse --is-inside-work-tree", { cwd: workingDirectory, stdio: "pipe" });
|
|
34084
34743
|
ensureLocalAttachmentGitExclude(workingDirectory);
|
|
@@ -34148,8 +34807,8 @@ function errToString(err) {
|
|
|
34148
34807
|
}
|
|
34149
34808
|
|
|
34150
34809
|
// ts/codex-agent/dist/session-manager.js
|
|
34151
|
-
import * as
|
|
34152
|
-
import * as
|
|
34810
|
+
import * as fs7 from "node:fs";
|
|
34811
|
+
import * as path7 from "node:path";
|
|
34153
34812
|
var CodexSessionManager = class {
|
|
34154
34813
|
mainSessionKey;
|
|
34155
34814
|
stateFilePath;
|
|
@@ -34186,14 +34845,14 @@ var CodexSessionManager = class {
|
|
|
34186
34845
|
clearMainThread() {
|
|
34187
34846
|
this.threadIds.delete(this.mainSessionKey);
|
|
34188
34847
|
try {
|
|
34189
|
-
|
|
34848
|
+
fs7.rmSync(this.stateFilePath, { force: true });
|
|
34190
34849
|
} catch (error) {
|
|
34191
34850
|
this.logger?.warn(`codex-agent: failed to clear main thread state at ${this.stateFilePath}: ${String(error)}`);
|
|
34192
34851
|
}
|
|
34193
34852
|
}
|
|
34194
34853
|
restore() {
|
|
34195
34854
|
try {
|
|
34196
|
-
const raw =
|
|
34855
|
+
const raw = fs7.readFileSync(this.stateFilePath, "utf8");
|
|
34197
34856
|
const parsed = JSON.parse(raw);
|
|
34198
34857
|
if (parsed.runtimeKey === this.mainSessionKey && typeof parsed.threadId === "string" && parsed.threadId.trim()) {
|
|
34199
34858
|
this.threadIds.set(this.mainSessionKey, parsed.threadId.trim());
|
|
@@ -34206,10 +34865,10 @@ var CodexSessionManager = class {
|
|
|
34206
34865
|
}
|
|
34207
34866
|
persist(threadId) {
|
|
34208
34867
|
try {
|
|
34209
|
-
|
|
34868
|
+
fs7.mkdirSync(path7.dirname(this.stateFilePath), { recursive: true });
|
|
34210
34869
|
const tmpPath = `${this.stateFilePath}.tmp`;
|
|
34211
|
-
|
|
34212
|
-
|
|
34870
|
+
fs7.writeFileSync(tmpPath, JSON.stringify({ runtimeKey: this.mainSessionKey, threadId }, null, 2));
|
|
34871
|
+
fs7.renameSync(tmpPath, this.stateFilePath);
|
|
34213
34872
|
} catch (error) {
|
|
34214
34873
|
this.logger?.warn(`codex-agent: failed to persist thread state at ${this.stateFilePath}: ${String(error)}`);
|
|
34215
34874
|
}
|
|
@@ -34217,8 +34876,8 @@ var CodexSessionManager = class {
|
|
|
34217
34876
|
};
|
|
34218
34877
|
|
|
34219
34878
|
// ts/codex-agent/dist/workspace.js
|
|
34220
|
-
import * as
|
|
34221
|
-
import * as
|
|
34879
|
+
import * as fs8 from "node:fs";
|
|
34880
|
+
import * as path8 from "node:path";
|
|
34222
34881
|
|
|
34223
34882
|
// ts/node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/error.js
|
|
34224
34883
|
function getLineColFromPtr(string, ptr) {
|
|
@@ -34912,12 +35571,12 @@ function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
|
34912
35571
|
|
|
34913
35572
|
// ts/codex-agent/dist/workspace.js
|
|
34914
35573
|
function ensureWorkspaceTrusted(codexHome, workspaceDir, log2) {
|
|
34915
|
-
const configPath =
|
|
34916
|
-
const normalizedPath =
|
|
35574
|
+
const configPath = path8.join(codexHome, "config.toml");
|
|
35575
|
+
const normalizedPath = path8.resolve(workspaceDir);
|
|
34917
35576
|
try {
|
|
34918
35577
|
let content = "";
|
|
34919
35578
|
try {
|
|
34920
|
-
content =
|
|
35579
|
+
content = fs8.readFileSync(configPath, "utf8");
|
|
34921
35580
|
} catch (err) {
|
|
34922
35581
|
if (err.code !== "ENOENT")
|
|
34923
35582
|
throw err;
|
|
@@ -34957,14 +35616,14 @@ ${trustLine}
|
|
|
34957
35616
|
content = content.substring(0, afterHeader) + trustLine + "\n" + content.substring(afterHeader);
|
|
34958
35617
|
}
|
|
34959
35618
|
}
|
|
34960
|
-
|
|
35619
|
+
fs8.writeFileSync(configPath, content, "utf8");
|
|
34961
35620
|
} else if (projects?.[normalizedPath] !== void 0) {
|
|
34962
35621
|
log2?.warn(`Codex config.toml has non-canonical header for ${normalizedPath}; skipping trust write`);
|
|
34963
35622
|
} else if (projects && !content.includes("[projects.")) {
|
|
34964
35623
|
log2?.warn(`Codex config.toml uses inline table for projects; skipping trust write for ${normalizedPath}`);
|
|
34965
35624
|
} else {
|
|
34966
|
-
|
|
34967
|
-
|
|
35625
|
+
fs8.mkdirSync(codexHome, { recursive: true });
|
|
35626
|
+
fs8.appendFileSync(configPath, `
|
|
34968
35627
|
${sectionHeader}
|
|
34969
35628
|
${trustLine}
|
|
34970
35629
|
`, "utf8");
|
|
@@ -34981,12 +35640,12 @@ function isParallProxyMode(env = process.env) {
|
|
|
34981
35640
|
return baseUrl === apiUrl || baseUrl.startsWith(`${apiUrl}/`);
|
|
34982
35641
|
}
|
|
34983
35642
|
function ensureParallProvider(codexHome, apiUrl, log2) {
|
|
34984
|
-
const configPath =
|
|
35643
|
+
const configPath = path8.join(codexHome, "config.toml");
|
|
34985
35644
|
const baseUrl = apiUrl.replace(/\/$/, "") + "/api/llm/v1";
|
|
34986
35645
|
try {
|
|
34987
35646
|
let content = "";
|
|
34988
35647
|
try {
|
|
34989
|
-
content =
|
|
35648
|
+
content = fs8.readFileSync(configPath, "utf8");
|
|
34990
35649
|
} catch (err) {
|
|
34991
35650
|
if (err.code !== "ENOENT")
|
|
34992
35651
|
throw err;
|
|
@@ -35017,8 +35676,8 @@ function ensureParallProvider(codexHome, apiUrl, log2) {
|
|
|
35017
35676
|
} else {
|
|
35018
35677
|
content = content.trimEnd() + "\n\n" + providerBlock + "\n";
|
|
35019
35678
|
}
|
|
35020
|
-
|
|
35021
|
-
|
|
35679
|
+
fs8.mkdirSync(codexHome, { recursive: true });
|
|
35680
|
+
fs8.writeFileSync(configPath, content, "utf8");
|
|
35022
35681
|
} catch (err) {
|
|
35023
35682
|
throw new Error(`failed to write Parall provider config: ${String(err)}`);
|
|
35024
35683
|
}
|
|
@@ -35035,18 +35694,18 @@ function ensureCodexWorkspace(workspaceDir, log2, agentIdentity) {
|
|
|
35035
35694
|
PRLL_REFERENCE_GUIDE,
|
|
35036
35695
|
buildSkillReferences(workspaceDir)
|
|
35037
35696
|
].join("\n\n");
|
|
35038
|
-
|
|
35039
|
-
const parallDir =
|
|
35040
|
-
|
|
35041
|
-
|
|
35042
|
-
writeSkillFiles(
|
|
35043
|
-
const codexConfigDir =
|
|
35044
|
-
|
|
35697
|
+
fs8.mkdirSync(workspaceDir, { recursive: true });
|
|
35698
|
+
const parallDir = path8.join(workspaceDir, ".parall");
|
|
35699
|
+
fs8.mkdirSync(parallDir, { recursive: true });
|
|
35700
|
+
fs8.writeFileSync(path8.join(parallDir, "system-prompt.md"), systemPrompt, "utf8");
|
|
35701
|
+
writeSkillFiles(path8.join(parallDir, "skills"));
|
|
35702
|
+
const codexConfigDir = path8.join(workspaceDir, ".codex");
|
|
35703
|
+
fs8.mkdirSync(codexConfigDir, { recursive: true });
|
|
35045
35704
|
const toml = `developer_instructions = """
|
|
35046
35705
|
${systemPrompt.replace(/\\/g, "\\\\").replace(/"""/g, '\\"""')}
|
|
35047
35706
|
"""
|
|
35048
35707
|
`;
|
|
35049
|
-
|
|
35708
|
+
fs8.writeFileSync(path8.join(codexConfigDir, "config.toml"), toml, "utf8");
|
|
35050
35709
|
}
|
|
35051
35710
|
|
|
35052
35711
|
// ts/codex-agent/dist/index.js
|
|
@@ -35139,6 +35798,7 @@ async function main() {
|
|
|
35139
35798
|
sessionManager,
|
|
35140
35799
|
log: agentLog,
|
|
35141
35800
|
contextFilePath: mainContextFilePath,
|
|
35801
|
+
contextDirPath: dispatchContextDirPath(config.stateDir),
|
|
35142
35802
|
useParallProvider
|
|
35143
35803
|
});
|
|
35144
35804
|
const refreshPlatformConfig = async () => {
|
|
@@ -35184,6 +35844,7 @@ async function main() {
|
|
|
35184
35844
|
dispatchDeadlineMs: parseDispatchDeadlineMs(process.env.PRLL_DISPATCH_DEADLINE_MS),
|
|
35185
35845
|
contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
|
|
35186
35846
|
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
35847
|
+
dispatchContextDir: dispatchContextDirPath(config.stateDir),
|
|
35187
35848
|
onConfigUpdate: refreshPlatformConfig,
|
|
35188
35849
|
onSessionReady: refreshPlatformConfig,
|
|
35189
35850
|
onNewSession: () => {
|