@parall/daemon 1.36.1 → 1.38.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 +352 -27
- package/bundle/parall-claude-agent.js +1219 -269
- package/bundle/parall-codex-agent.js +1225 -276
- package/bundle/parall-daemon.js +33552 -31712
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/clip-runtime/browser-daemon-env.d.ts +29 -0
- package/dist/clip-runtime/browser-daemon-env.d.ts.map +1 -0
- package/dist/clip-runtime/browser-daemon-env.js +70 -0
- package/dist/clip-runtime/browser-profile-manager.d.ts +18 -5
- package/dist/clip-runtime/browser-profile-manager.d.ts.map +1 -1
- package/dist/clip-runtime/browser-profile-manager.js +149 -24
- package/dist/clip-runtime/browser-profile-pool.d.ts +250 -0
- package/dist/clip-runtime/browser-profile-pool.d.ts.map +1 -0
- package/dist/clip-runtime/browser-profile-pool.js +581 -0
- package/dist/clip-runtime/index.d.ts +2 -2
- package/dist/clip-runtime/index.d.ts.map +1 -1
- package/dist/clip-runtime/index.js +2 -1
- package/dist/clip-runtime/process-manager.d.ts +5 -3
- package/dist/clip-runtime/process-manager.d.ts.map +1 -1
- package/dist/clip-runtime/subprocess.d.ts +14 -0
- package/dist/clip-runtime/subprocess.d.ts.map +1 -1
- package/dist/clip-runtime/subprocess.js +49 -0
- package/dist/config.d.ts +14 -15
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -26
- package/dist/daemon-main.d.ts +8 -0
- package/dist/daemon-main.d.ts.map +1 -0
- package/dist/daemon-main.js +165 -0
- package/dist/daemon-paths.d.ts +14 -0
- package/dist/daemon-paths.d.ts.map +1 -0
- package/dist/daemon-paths.js +26 -0
- package/dist/daemon-update-mode.d.ts +8 -0
- package/dist/daemon-update-mode.d.ts.map +1 -0
- package/dist/daemon-update-mode.js +18 -0
- package/dist/index.js +41 -167
- package/dist/runtime-bin-resolver.d.ts +4 -0
- package/dist/runtime-bin-resolver.d.ts.map +1 -1
- package/dist/runtime-bin-resolver.js +40 -7
- package/dist/runtime-detector.d.ts +30 -0
- package/dist/runtime-detector.d.ts.map +1 -0
- package/dist/runtime-detector.js +100 -0
- package/dist/supervisor.d.ts +53 -2
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +604 -67
- package/dist/update-health-gate.d.ts +66 -0
- package/dist/update-health-gate.d.ts.map +1 -0
- package/dist/update-health-gate.js +93 -0
- package/dist/updater-manifest.d.ts +2 -1
- package/dist/updater-manifest.d.ts.map +1 -1
- package/dist/updater-manifest.js +38 -7
- package/dist/updater.d.ts +13 -2
- package/dist/updater.d.ts.map +1 -1
- package/dist/updater.js +126 -17
- 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();
|
|
@@ -26647,6 +26660,17 @@ function buildEventBody(event) {
|
|
|
26647
26660
|
if (event.attachedUri)
|
|
26648
26661
|
lines.push(`[Attached: ${sanitizeMeta(event.attachedUri)}]`);
|
|
26649
26662
|
lines.push("", event.body);
|
|
26663
|
+
} else if (event.type === "channel_message") {
|
|
26664
|
+
lines.push(`[Event: channel.message]`);
|
|
26665
|
+
const providerLabel = sanitizeMeta(event.channelProvider ?? "external IM");
|
|
26666
|
+
const convLabel = event.channelExternalConversationId ? `${sanitizeMeta(event.channelExternalConversationId)} (${sanitizeMeta(event.channelConversationType ?? "conversation")})` : sanitizeMeta(event.channelConversationType ?? "conversation");
|
|
26667
|
+
lines.push(`[Channel: ${providerLabel} | conversation: ${convLabel}]`);
|
|
26668
|
+
lines.push(`[From: ${sanitizeMeta(event.senderName)} (external user, not a Parall member)]`);
|
|
26669
|
+
if (event.channelExternalMessageId) {
|
|
26670
|
+
lines.push(`[External message ID: ${sanitizeMeta(event.channelExternalMessageId)}]`);
|
|
26671
|
+
}
|
|
26672
|
+
lines.push(`[Audience: this conversation lives on ${providerLabel}, OUTSIDE Parall. Readers cannot open prll:// links, Parall cards, or internal attachments \u2014 never include them in replies. Write plain conversational text.]`);
|
|
26673
|
+
lines.push("", event.body);
|
|
26650
26674
|
} else if (event.type === "external_trigger") {
|
|
26651
26675
|
lines.push(`[Event: external.trigger]`);
|
|
26652
26676
|
lines.push(`[Trigger: prll://${event.targetId}]`);
|
|
@@ -26688,7 +26712,7 @@ function buildSendMessageHint(event) {
|
|
|
26688
26712
|
}
|
|
26689
26713
|
if (event.targetId.startsWith("cht_")) {
|
|
26690
26714
|
return `
|
|
26691
|
-
<system-reminder>To reply, run
|
|
26715
|
+
<system-reminder>To reply, run \`parall messages send prll://${event.targetId} --text-file - <<'EOF'\` \u2026 \`EOF\` \u2014 the quoted heredoc keeps \`$\`, backticks and apostrophes literal (plain \`--text "$1,000"\` sends \`,000\`). Your plain text output is not delivered to the chat.</system-reminder>`;
|
|
26692
26716
|
}
|
|
26693
26717
|
if (event.targetId.startsWith("tsk_")) {
|
|
26694
26718
|
return `
|
|
@@ -26697,6 +26721,14 @@ function buildSendMessageHint(event) {
|
|
|
26697
26721
|
if (event.targetId.startsWith("sch_")) {
|
|
26698
26722
|
return `
|
|
26699
26723
|
<system-reminder>To communicate, use the CLI: \`parall messages send\` / \`parall dm\`. Your plain text output is not delivered.</system-reminder>`;
|
|
26724
|
+
}
|
|
26725
|
+
if (event.type === "channel_message") {
|
|
26726
|
+
const clipLabel = event.channelProvider ? `the \`${event.channelProvider}\` clip's` : "your channel provider clip's";
|
|
26727
|
+
const apiLabel = event.channelProvider ?? "external platform";
|
|
26728
|
+
const target = event.channelExternalConversationId ? `{"chat_id": "${event.channelExternalConversationId}", "text": "..."}` : `{"chat_id": "<conversation id>", "text": "..."}`;
|
|
26729
|
+
const threadAlt = event.channelExternalMessageId ? ` To reply in-thread to this specific message, use {"message_id": "${event.channelExternalMessageId}", "text": "..."} instead.` : "";
|
|
26730
|
+
return `
|
|
26731
|
+
<system-reminder>To reply, invoke ${clipLabel} \`send_message\` command with ${target} \u2014 your plain text output is NOT delivered to the external conversation.${threadAlt} The same clip's \`call\` command reaches the wider ${apiLabel} API when needed.</system-reminder>`;
|
|
26700
26732
|
}
|
|
26701
26733
|
if (event.type === "external_trigger" || event.targetId.startsWith("xtr_")) {
|
|
26702
26734
|
return `
|
|
@@ -26904,6 +26936,8 @@ Or upload first and reuse across chats:
|
|
|
26904
26936
|
parall messages send prll://cht_aaa --attachment att_yyy --text "Report"
|
|
26905
26937
|
parall messages send prll://cht_bbb --attachment att_yyy --text "FYI"
|
|
26906
26938
|
|
|
26939
|
+
The \`--text\` captions above are safe short literals. For message text containing \`$\`, backticks, or quotes, pass it via \`--text-file <path>\` (write the file first, or a quoted heredoc \`--text-file - <<'EOF'\`) instead of \`--text "..."\` \u2014 inside double quotes the shell turns \`$1,000\` into \`,000\` and executes \`$(...)\`.
|
|
26940
|
+
|
|
26907
26941
|
### When to reference
|
|
26908
26942
|
|
|
26909
26943
|
- **Origin** \u2014 always link the message or task that triggered your work
|
|
@@ -26954,11 +26988,21 @@ To say something in a chat, you **must** invoke the Parall CLI via your shell/ex
|
|
|
26954
26988
|
|
|
26955
26989
|
All outbound interactions go through the \`parall\` CLI. Credentials are pre-injected as environment variables \u2014 no setup needed. If \`parall\` is not on PATH, use \`npx --yes @parall/cli@latest\` instead.
|
|
26956
26990
|
|
|
26957
|
-
- \`parall messages send prll://cht_xxx --text
|
|
26958
|
-
- \`parall dm prll://usr_xxx --text
|
|
26991
|
+
- \`parall messages send prll://cht_xxx --text-file -\` \u2014 reply into the triggering chat (pipe the body via a quoted heredoc; see Shell-safety below)
|
|
26992
|
+
- \`parall dm prll://usr_xxx --text-file - [--no-reply]\` \u2014 direct message another user
|
|
26959
26993
|
- \`parall tasks update prll://tsk_xxx --status in_progress\` \u2014 task state
|
|
26960
26994
|
- \`parall no-reply [--reason "..."]\` \u2014 explicitly declare this turn silent (audit signal; not required for silence, just clarifies intent)
|
|
26961
26995
|
|
|
26996
|
+
**Shell-safety \u2014 never wrap real message content in double quotes.** Your command runs in a shell, which expands \`$\`, backticks, and \`$(...)\` inside \`"..."\` before the CLI sees them: \`--text "That costs $1,000"\` sends \`That costs ,000\`, and \`--text "$(cmd)"\` executes \`cmd\`. Pass message bodies via \`--text-file <path>\` (write the file first \u2014 no shell touches it) or a quoted heredoc that disables expansion:
|
|
26997
|
+
|
|
26998
|
+
\`\`\`bash
|
|
26999
|
+
parall messages send prll://cht_xxx --text-file - <<'EOF'
|
|
27000
|
+
That costs $1,000, and $(whoami) stays literal. I'm on it.
|
|
27001
|
+
EOF
|
|
27002
|
+
\`\`\`
|
|
27003
|
+
|
|
27004
|
+
Keep \`--text "..."\` for short literals with no \`$\`, backtick, or apostrophe.
|
|
27005
|
+
|
|
26962
27006
|
The bridge injects Parall context via environment variables. The static credentials \`PRLL_API_URL\`, \`PRLL_API_KEY\`, and \`PRLL_ORG_ID\` are always set. \`PRLL_CONTEXT_FILE\` points to a per-session JSON file that the gateway updates each dispatch with \`session_id\`, \`chat_id\`, \`trigger_message_id\`, \`no_reply\`, and \`step_id\` (updated per tool call). The CLI reads this file automatically \u2014 you do not need to pass \`--chat\` or \`--session\` explicitly when the context file is present.
|
|
26963
27007
|
|
|
26964
27008
|
## Attachments
|
|
@@ -27003,7 +27047,7 @@ Messages may arrive with a \`[Thread: prll://msg_xxx]\` line in the event block,
|
|
|
27003
27047
|
|
|
27004
27048
|
### CLI usage
|
|
27005
27049
|
- The \`msg_xxx\` from the \`[Thread: prll://msg_xxx]\` line is the thread root ID \u2014 pass it as \`--thread-root-id\`
|
|
27006
|
-
- Reply in a thread: \`parall messages send <chatId> --text
|
|
27050
|
+
- Reply in a thread: \`parall messages send <chatId> --text-file /tmp/reply.md --thread-root-id <msgId>\`
|
|
27007
27051
|
- Read thread replies: \`parall messages list <chatId> --thread-root-id <msgId>\`
|
|
27008
27052
|
|
|
27009
27053
|
### Thread etiquette
|
|
@@ -27071,8 +27115,8 @@ function childLogger(logger, sub) {
|
|
|
27071
27115
|
|
|
27072
27116
|
// ts/agent-core/dist/gateway-base.js
|
|
27073
27117
|
import * as os from "node:os";
|
|
27074
|
-
import * as
|
|
27075
|
-
import * as
|
|
27118
|
+
import * as fs2 from "node:fs";
|
|
27119
|
+
import * as path2 from "node:path";
|
|
27076
27120
|
|
|
27077
27121
|
// ts/sdk/dist/types.js
|
|
27078
27122
|
var MENTION_ALL_USER_ID = "all";
|
|
@@ -27104,6 +27148,7 @@ var ENDPOINTS = {
|
|
|
27104
27148
|
// Org-scoped
|
|
27105
27149
|
ORG: (orgId) => `${API_BASE}/orgs/${orgId}`,
|
|
27106
27150
|
ORG_MEMBERS: (orgId) => `${API_BASE}/orgs/${orgId}/members`,
|
|
27151
|
+
ORG_MEMBERS_FORMER: (orgId) => `${API_BASE}/orgs/${orgId}/members/former`,
|
|
27107
27152
|
TEAMS: (orgId) => `${API_BASE}/orgs/${orgId}/teams`,
|
|
27108
27153
|
ORG_MEMBERS_ONLINE: (orgId) => `${API_BASE}/orgs/${orgId}/members/online`,
|
|
27109
27154
|
ORG_MEMBER: (orgId, userId) => `${API_BASE}/orgs/${orgId}/members/${userId}`,
|
|
@@ -27253,6 +27298,19 @@ var ENDPOINTS = {
|
|
|
27253
27298
|
EXTERNAL_TRIGGER: (orgId, triggerId) => `${API_BASE}/orgs/${orgId}/external-triggers/${triggerId}`,
|
|
27254
27299
|
EXTERNAL_TRIGGER_RUNS: (orgId) => `${API_BASE}/orgs/${orgId}/external-trigger-runs`,
|
|
27255
27300
|
EXTERNAL_TRIGGER_RUN: (orgId, runId) => `${API_BASE}/orgs/${orgId}/external-trigger-runs/${runId}`,
|
|
27301
|
+
// External IM channel (org-scoped, platform-mediated Feishu/Slack)
|
|
27302
|
+
CHANNEL_CONNECTIONS: (orgId) => `${API_BASE}/orgs/${orgId}/channel-connections`,
|
|
27303
|
+
CHANNEL_CONNECTION: (orgId, connectionId) => `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}`,
|
|
27304
|
+
CHANNEL_CONNECTION_CREDENTIALS: (orgId, connectionId) => `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}/credentials`,
|
|
27305
|
+
CHANNEL_CONNECTION_INGRESS_TOKEN_REGENERATE: (orgId, connectionId) => `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}/ingress-token/regenerate`,
|
|
27306
|
+
CHANNEL_CONNECTION_CONVERSATIONS: (orgId, connectionId) => `${API_BASE}/orgs/${orgId}/channel-connections/${connectionId}/conversations`,
|
|
27307
|
+
CHANNEL_CONVERSATION: (orgId, conversationId) => `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}`,
|
|
27308
|
+
CHANNEL_CONVERSATION_MESSAGES: (orgId, conversationId) => `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}/messages`,
|
|
27309
|
+
CHANNEL_CONVERSATION_SESSION: (orgId, conversationId) => `${API_BASE}/orgs/${orgId}/channel-conversations/${conversationId}/session`,
|
|
27310
|
+
CHANNEL_MESSAGE: (orgId, messageId) => `${API_BASE}/orgs/${orgId}/channel-messages/${messageId}`,
|
|
27311
|
+
CHANNEL_PROVISIONING: (orgId) => `${API_BASE}/orgs/${orgId}/channel-provisioning`,
|
|
27312
|
+
CHANNEL_PROVISIONING_SESSION: (orgId, sessionId) => `${API_BASE}/orgs/${orgId}/channel-provisioning/${sessionId}`,
|
|
27313
|
+
CHANNEL_PROVISIONING_CANCEL: (orgId, sessionId) => `${API_BASE}/orgs/${orgId}/channel-provisioning/${sessionId}/cancel`,
|
|
27256
27314
|
// Invitations (org-scoped, admin)
|
|
27257
27315
|
ORG_INVITATIONS: (orgId) => `${API_BASE}/orgs/${orgId}/invitations`,
|
|
27258
27316
|
ORG_INVITATION: (orgId, invId) => `${API_BASE}/orgs/${orgId}/invitations/${invId}`,
|
|
@@ -27270,9 +27328,16 @@ var ENDPOINTS = {
|
|
|
27270
27328
|
INVITE_LINK_JOIN: `${API_BASE}/invite-link/join`,
|
|
27271
27329
|
// Wikis (org-scoped, served by wiki-service)
|
|
27272
27330
|
WIKIS: (orgId) => `${WIKI_BASE}/orgs/${orgId}/wikis`,
|
|
27331
|
+
// Recycle bin — soft-deleted wikis (owner only). Must precede WIKI in the
|
|
27332
|
+
// backend router so `deleted` isn't captured as a {wikiId}; the SDK builder
|
|
27333
|
+
// is just a string.
|
|
27334
|
+
WIKIS_DELETED: (orgId) => `${WIKI_BASE}/orgs/${orgId}/wikis/deleted`,
|
|
27335
|
+
// Detail URL — also reused for DELETE (soft-delete) and `${WIKI}/restore`.
|
|
27273
27336
|
WIKI: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}`,
|
|
27337
|
+
WIKI_RESTORE: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/restore`,
|
|
27274
27338
|
WIKI_TREE: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/tree`,
|
|
27275
27339
|
WIKI_BLOB: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/blob`,
|
|
27340
|
+
WIKI_COPY: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/copy`,
|
|
27276
27341
|
WIKI_NODE_SECTIONS: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/node-sections`,
|
|
27277
27342
|
WIKI_SEARCH: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/search`,
|
|
27278
27343
|
WIKI_PAGE_INDEX: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}/page-index`,
|
|
@@ -27326,6 +27391,11 @@ var ENDPOINTS = {
|
|
|
27326
27391
|
DISPATCH_ACK_BY_ID: (orgId, id) => `${API_BASE}/orgs/${orgId}/dispatch/${id}/ack`,
|
|
27327
27392
|
DISPATCH_EXPIRE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/expire`,
|
|
27328
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`,
|
|
27329
27399
|
// Unread
|
|
27330
27400
|
UNREAD: `${API_BASE}/me/unread`,
|
|
27331
27401
|
ORG_UNREAD: (orgId) => `${API_BASE}/orgs/${orgId}/unread`,
|
|
@@ -27355,6 +27425,14 @@ var ENDPOINTS = {
|
|
|
27355
27425
|
BILLING_AUTO_RELOAD: (orgId) => `${API_BASE}/orgs/${orgId}/billing/auto-reload`,
|
|
27356
27426
|
BILLING_SETUP_INTENT: (orgId) => `${API_BASE}/orgs/${orgId}/billing/setup-intent`,
|
|
27357
27427
|
COMPUTE_PRICING: () => `${API_BASE}/billing/compute-pricing`,
|
|
27428
|
+
// Runtime capability table (public, no auth) — SSOT for the create/settings
|
|
27429
|
+
// interlock: per-runtime compute modes, native model family, cross-family
|
|
27430
|
+
// availability, and recommended model.
|
|
27431
|
+
RUNTIMES: () => `${API_BASE}/runtimes`,
|
|
27432
|
+
// Model catalog (public, no auth) — served from the server's DB-backed
|
|
27433
|
+
// snapshot. Optional include_model keeps an agent's pinned hidden legacy
|
|
27434
|
+
// model representable in settings pickers.
|
|
27435
|
+
MODELS: (includeModel) => includeModel ? `${API_BASE}/models?include_model=${encodeURIComponent(includeModel)}` : `${API_BASE}/models`,
|
|
27358
27436
|
// ADMIN_GRANTS intentionally NOT exported here — it sits under the
|
|
27359
27437
|
// unauthenticated `/internal/admin/*` surface and must not bleed into the
|
|
27360
27438
|
// public SDK. Admin dashboard hits the URL directly from its own client.
|
|
@@ -27367,6 +27445,7 @@ var ENDPOINTS = {
|
|
|
27367
27445
|
AGENT_CLIP: (orgId, agentId, clipId) => `${CLIP_BASE}/orgs/${orgId}/agents/${agentId}/clips/${clipId}`,
|
|
27368
27446
|
CLIP_INVOKE: (orgId) => `${CLIP_BASE}/orgs/${orgId}/invoke`,
|
|
27369
27447
|
CLIP_ONLINE: (orgId) => `${CLIP_BASE}/orgs/${orgId}/online`,
|
|
27448
|
+
BROWSER_RUNTIME_STATUS: (orgId) => `${CLIP_BASE}/orgs/${orgId}/browser-runtime/status`,
|
|
27370
27449
|
BROWSER_PROFILES: (orgId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles`,
|
|
27371
27450
|
BROWSER_PROFILE: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}`,
|
|
27372
27451
|
BROWSER_PROFILE_OPEN: (orgId, profileId) => `${CLIP_BASE}/orgs/${orgId}/browser-profiles/${profileId}/open`,
|
|
@@ -27420,10 +27499,13 @@ var WS_EVENTS = {
|
|
|
27420
27499
|
INVITATION_REVOKED: "invitation.revoked",
|
|
27421
27500
|
ORG_JOIN_REQUEST_NEW: "org.join_request.new",
|
|
27422
27501
|
ORG_INVITE_LINK_JOINED: "org.invite_link.joined",
|
|
27502
|
+
ORG_MEMBER_REMOVED: "org.member.removed",
|
|
27423
27503
|
AGENT_CONFIG_UPDATE: "agent_config.update",
|
|
27424
27504
|
PRESENCE_UPDATE: "presence.update",
|
|
27425
27505
|
WIKI_CHANGESET_CREATED: "wiki.changeset.created",
|
|
27426
27506
|
WIKI_CHANGESET_UPDATED: "wiki.changeset.updated",
|
|
27507
|
+
WIKI_DELETED: "wiki.deleted",
|
|
27508
|
+
WIKI_RESTORED: "wiki.restored",
|
|
27427
27509
|
COMMENT_CREATED: "comment.created",
|
|
27428
27510
|
COMMENT_UPDATED: "comment.updated",
|
|
27429
27511
|
COMMENT_DELETED: "comment.deleted",
|
|
@@ -27434,6 +27516,7 @@ var WS_EVENTS = {
|
|
|
27434
27516
|
READ_POSITION_UPDATED: "read_position.updated",
|
|
27435
27517
|
DISPATCH_NEW: "dispatch.new",
|
|
27436
27518
|
DISPATCH_RECEIVED: "dispatch.received",
|
|
27519
|
+
DISPATCH_RESOLVED: "dispatch.resolved",
|
|
27437
27520
|
SCHEDULE_CREATED: "schedule.created",
|
|
27438
27521
|
SCHEDULE_UPDATED: "schedule.updated",
|
|
27439
27522
|
SCHEDULE_DELETED: "schedule.deleted",
|
|
@@ -27451,6 +27534,7 @@ var WS_EVENTS = {
|
|
|
27451
27534
|
MACHINE_FILESYSTEM_BROWSE: "machine.filesystem.browse",
|
|
27452
27535
|
MACHINE_UPDATE: "machine.update",
|
|
27453
27536
|
MACHINE_CONFIG_UPDATED: "machine.config.updated",
|
|
27537
|
+
MACHINE_AGENT_CONFIG_UPDATED: "machine.agent_config.updated",
|
|
27454
27538
|
MACHINE_CLIP_SYNC: "machine.clip.sync",
|
|
27455
27539
|
MACHINE_BROWSER_PROFILE_LIFECYCLE: "machine.browser_profile.lifecycle",
|
|
27456
27540
|
MACHINE_BROWSER_PROFILE_VIEWER: "machine.browser_profile.viewer",
|
|
@@ -27528,8 +27612,8 @@ var ParallClient = class _ParallClient {
|
|
|
27528
27612
|
* is authoritative, so wiki vs api routing can't drift from how a caller
|
|
27529
27613
|
* happens to invoke the client.
|
|
27530
27614
|
*/
|
|
27531
|
-
baseUrlFor(
|
|
27532
|
-
return
|
|
27615
|
+
baseUrlFor(path9) {
|
|
27616
|
+
return path9.startsWith(WIKI_BASE) ? this.wikiBaseUrl : this.baseUrl;
|
|
27533
27617
|
}
|
|
27534
27618
|
setToken(token) {
|
|
27535
27619
|
this.token = token;
|
|
@@ -27556,10 +27640,10 @@ var ParallClient = class _ParallClient {
|
|
|
27556
27640
|
* REFRESH_THRESHOLD_S, refresh it **before** sending the request.
|
|
27557
27641
|
* No-op when the token is still fresh, missing, or un-parseable.
|
|
27558
27642
|
*/
|
|
27559
|
-
async ensureFreshToken(
|
|
27643
|
+
async ensureFreshToken(path9) {
|
|
27560
27644
|
if (!this.token || !this.getRefreshToken)
|
|
27561
27645
|
return;
|
|
27562
|
-
const pathSuffix =
|
|
27646
|
+
const pathSuffix = path9.replace(/^\/api\/v1/, "");
|
|
27563
27647
|
if (_ParallClient.AUTH_PATHS.has(pathSuffix))
|
|
27564
27648
|
return;
|
|
27565
27649
|
const exp = _ParallClient.decodeJwtExp(this.token);
|
|
@@ -27591,11 +27675,11 @@ var ParallClient = class _ParallClient {
|
|
|
27591
27675
|
this.refreshPromise = null;
|
|
27592
27676
|
}
|
|
27593
27677
|
}
|
|
27594
|
-
async request(method,
|
|
27678
|
+
async request(method, path9, body, query, retried = false, opts) {
|
|
27595
27679
|
if (!retried) {
|
|
27596
|
-
await this.ensureFreshToken(
|
|
27680
|
+
await this.ensureFreshToken(path9);
|
|
27597
27681
|
}
|
|
27598
|
-
let url = `${this.baseUrlFor(
|
|
27682
|
+
let url = `${this.baseUrlFor(path9)}${path9}`;
|
|
27599
27683
|
if (query) {
|
|
27600
27684
|
const params = new URLSearchParams();
|
|
27601
27685
|
for (const [key, value] of Object.entries(query)) {
|
|
@@ -27625,12 +27709,12 @@ var ParallClient = class _ParallClient {
|
|
|
27625
27709
|
throw _ParallClient.normalizeFetchError(err);
|
|
27626
27710
|
}
|
|
27627
27711
|
if (res.status === 401) {
|
|
27628
|
-
const pathSuffix =
|
|
27712
|
+
const pathSuffix = path9.replace(/^\/api\/v1/, "");
|
|
27629
27713
|
const isAuthPath = _ParallClient.AUTH_PATHS.has(pathSuffix);
|
|
27630
27714
|
if (!retried && !isAuthPath && this.getRefreshToken) {
|
|
27631
27715
|
const refreshed = await this.tryRefresh();
|
|
27632
27716
|
if (refreshed) {
|
|
27633
|
-
return this.request(method,
|
|
27717
|
+
return this.request(method, path9, body, query, true, opts);
|
|
27634
27718
|
}
|
|
27635
27719
|
}
|
|
27636
27720
|
if (this.onTokenExpired && !isAuthPath) {
|
|
@@ -27641,6 +27725,7 @@ var ParallClient = class _ParallClient {
|
|
|
27641
27725
|
const rawErrorBody = await res.json().catch(() => ({}));
|
|
27642
27726
|
throw buildApiError(res, rawErrorBody);
|
|
27643
27727
|
}
|
|
27728
|
+
opts?.onStatus?.(res.status);
|
|
27644
27729
|
if (res.status === 204)
|
|
27645
27730
|
return void 0;
|
|
27646
27731
|
if (res.status === 202) {
|
|
@@ -27659,15 +27744,15 @@ var ParallClient = class _ParallClient {
|
|
|
27659
27744
|
* hit the 100 MiB cap, so a longer 5-minute timeout is used so a
|
|
27660
27745
|
* 50 MiB blob on a slow connection doesn't get chopped at 15 s.
|
|
27661
27746
|
*/
|
|
27662
|
-
async multipartRequest(method,
|
|
27747
|
+
async multipartRequest(method, path9, body, retried = false) {
|
|
27663
27748
|
if (!retried) {
|
|
27664
|
-
await this.ensureFreshToken(
|
|
27749
|
+
await this.ensureFreshToken(path9);
|
|
27665
27750
|
}
|
|
27666
27751
|
const { "Content-Type": _drop, ...headers } = this.buildHeaders();
|
|
27667
27752
|
void _drop;
|
|
27668
27753
|
let res;
|
|
27669
27754
|
try {
|
|
27670
|
-
res = await fetch(`${this.baseUrlFor(
|
|
27755
|
+
res = await fetch(`${this.baseUrlFor(path9)}${path9}`, {
|
|
27671
27756
|
method,
|
|
27672
27757
|
headers,
|
|
27673
27758
|
body,
|
|
@@ -27677,12 +27762,12 @@ var ParallClient = class _ParallClient {
|
|
|
27677
27762
|
throw _ParallClient.normalizeFetchError(err);
|
|
27678
27763
|
}
|
|
27679
27764
|
if (res.status === 401) {
|
|
27680
|
-
const pathSuffix =
|
|
27765
|
+
const pathSuffix = path9.replace(/^\/api\/v1/, "");
|
|
27681
27766
|
const isAuthPath = _ParallClient.AUTH_PATHS.has(pathSuffix);
|
|
27682
27767
|
if (!retried && !isAuthPath && this.getRefreshToken) {
|
|
27683
27768
|
const refreshed = await this.tryRefresh();
|
|
27684
27769
|
if (refreshed) {
|
|
27685
|
-
return this.multipartRequest(method,
|
|
27770
|
+
return this.multipartRequest(method, path9, body, true);
|
|
27686
27771
|
}
|
|
27687
27772
|
}
|
|
27688
27773
|
if (this.onTokenExpired && !isAuthPath) {
|
|
@@ -27790,6 +27875,13 @@ var ParallClient = class _ParallClient {
|
|
|
27790
27875
|
const res = await this.request("GET", ENDPOINTS.ORG_MEMBERS(orgId));
|
|
27791
27876
|
return res.data;
|
|
27792
27877
|
}
|
|
27878
|
+
/** User IDs of soft-removed (former) org members — for marking their avatar /
|
|
27879
|
+
* name as "left" where dormant relations still surface them (DMs, task
|
|
27880
|
+
* assignees, message history). */
|
|
27881
|
+
async getFormerMemberIds(orgId) {
|
|
27882
|
+
const res = await this.request("GET", ENDPOINTS.ORG_MEMBERS_FORMER(orgId));
|
|
27883
|
+
return res.user_ids ?? [];
|
|
27884
|
+
}
|
|
27793
27885
|
async getTeams(orgId) {
|
|
27794
27886
|
const res = await this.request("GET", ENDPOINTS.TEAMS(orgId));
|
|
27795
27887
|
return res.data;
|
|
@@ -27967,6 +28059,21 @@ var ParallClient = class _ParallClient {
|
|
|
27967
28059
|
async sendMessage(orgId, chatId, req) {
|
|
27968
28060
|
return this.request("POST", ENDPOINTS.CHAT_MESSAGES(orgId, chatId), req);
|
|
27969
28061
|
}
|
|
28062
|
+
/**
|
|
28063
|
+
* sendMessage variant that also reports whether the server answered with an
|
|
28064
|
+
* idempotent replay (HTTP 200 — the message already existed for this
|
|
28065
|
+
* idempotency/effect key) instead of a fresh create (201). Used by the CLI
|
|
28066
|
+
* to surface "already sent by a previous run (deduplicated)".
|
|
28067
|
+
*/
|
|
28068
|
+
async sendMessageDetailed(orgId, chatId, req) {
|
|
28069
|
+
let status = 0;
|
|
28070
|
+
const message = await this.request("POST", ENDPOINTS.CHAT_MESSAGES(orgId, chatId), req, void 0, false, {
|
|
28071
|
+
onStatus: (s) => {
|
|
28072
|
+
status = s;
|
|
28073
|
+
}
|
|
28074
|
+
});
|
|
28075
|
+
return { message, deduplicated: status === 200 };
|
|
28076
|
+
}
|
|
27970
28077
|
async getMessages(orgId, chatId, params) {
|
|
27971
28078
|
return this.request("GET", ENDPOINTS.CHAT_MESSAGES(orgId, chatId), void 0, params);
|
|
27972
28079
|
}
|
|
@@ -28278,20 +28385,25 @@ var ParallClient = class _ParallClient {
|
|
|
28278
28385
|
return res.data;
|
|
28279
28386
|
}
|
|
28280
28387
|
/** `POST /machines/me/browser-profiles/{profileId}/status` — report runtime status. */
|
|
28281
|
-
async reportBrowserProfileStatus(profileId, status, errorMsg) {
|
|
28388
|
+
async reportBrowserProfileStatus(profileId, status, errorMsg, generation) {
|
|
28282
28389
|
await this.request("POST", ENDPOINTS.MACHINES_ME_BROWSER_PROFILE_STATUS(profileId), {
|
|
28283
28390
|
status,
|
|
28284
|
-
...errorMsg ? { error_msg: errorMsg } : {}
|
|
28391
|
+
...errorMsg ? { error_msg: errorMsg } : {},
|
|
28392
|
+
...generation !== void 0 ? { generation } : {}
|
|
28285
28393
|
});
|
|
28286
28394
|
}
|
|
28287
28395
|
/**
|
|
28288
28396
|
* `POST /machines/me/health` — bump the Machine's `updated_at` to now and
|
|
28289
|
-
* report daemon state. The daemon
|
|
28290
|
-
*
|
|
28397
|
+
* report daemon state. The daemon calls this on startup and again whenever
|
|
28398
|
+
* periodic runtime re-detection produces a CHANGED result (there is no
|
|
28399
|
+
* fixed-cadence keepalive loop today — steady state posts nothing). All
|
|
28291
28400
|
* fields are optional and only persisted when changed:
|
|
28292
28401
|
* - `daemonVersion` — the running bundle/launcher version.
|
|
28293
28402
|
* - `selfUpdateCapable` — whether the daemon can act on a machine.update
|
|
28294
28403
|
* signal (true under a service manager, false for bare `npx` foreground).
|
|
28404
|
+
* - `detectedRuntimes` — runtime CLIs found on the host. Omitted = no
|
|
28405
|
+
* report this beat (server keeps the stored value); [] = detection ran
|
|
28406
|
+
* and found nothing (server clears to empty).
|
|
28295
28407
|
*/
|
|
28296
28408
|
async postMachineHeartbeat(opts) {
|
|
28297
28409
|
const body = {};
|
|
@@ -28299,6 +28411,8 @@ var ParallClient = class _ParallClient {
|
|
|
28299
28411
|
body.daemon_version = opts.daemonVersion;
|
|
28300
28412
|
if (opts?.selfUpdateCapable !== void 0)
|
|
28301
28413
|
body.self_update_capable = opts.selfUpdateCapable;
|
|
28414
|
+
if (opts?.detectedRuntimes !== void 0)
|
|
28415
|
+
body.detected_runtimes = opts.detectedRuntimes;
|
|
28302
28416
|
return this.request("POST", ENDPOINTS.MACHINES_ME_HEALTH, Object.keys(body).length > 0 ? body : void 0);
|
|
28303
28417
|
}
|
|
28304
28418
|
async reportAgentWorkspaceState(agentId, state) {
|
|
@@ -28347,8 +28461,8 @@ var ParallClient = class _ParallClient {
|
|
|
28347
28461
|
async requestMachineUpdate(orgId, machineId, mandatory = false) {
|
|
28348
28462
|
await this.request("POST", ENDPOINTS.MACHINE_REQUEST_UPDATE(orgId, machineId), { mandatory });
|
|
28349
28463
|
}
|
|
28350
|
-
async browseMachineFilesystem(orgId, machineId,
|
|
28351
|
-
return this.request("POST", ENDPOINTS.MACHINE_BROWSE(orgId, machineId), { path:
|
|
28464
|
+
async browseMachineFilesystem(orgId, machineId, path9) {
|
|
28465
|
+
return this.request("POST", ENDPOINTS.MACHINE_BROWSE(orgId, machineId), { path: path9 }, void 0, false, { timeoutMs: 15e3 });
|
|
28352
28466
|
}
|
|
28353
28467
|
/** Create a new machine key. Returns the raw key string (shown once) + metadata. */
|
|
28354
28468
|
async createMachineKey(orgId, machineId, name) {
|
|
@@ -28421,6 +28535,30 @@ var ParallClient = class _ParallClient {
|
|
|
28421
28535
|
const params = maxAgeHours !== void 0 ? { max_age_hours: String(maxAgeHours) } : void 0;
|
|
28422
28536
|
return this.request("POST", ENDPOINTS.DISPATCH_EXPIRE(orgId), void 0, params);
|
|
28423
28537
|
}
|
|
28538
|
+
/**
|
|
28539
|
+
* Claim a dispatch lane (explicit consume endpoint). Occupies the
|
|
28540
|
+
* (agent, target, thread) lane and folds claimable WorkItems into it;
|
|
28541
|
+
* `claimed: false` means a healthy incumbent holds the lane.
|
|
28542
|
+
*/
|
|
28543
|
+
async claimDispatch(orgId, req) {
|
|
28544
|
+
return this.request("POST", ENDPOINTS.DISPATCH_CLAIM(orgId), req);
|
|
28545
|
+
}
|
|
28546
|
+
/** Fold a pending same-target WorkItem into a live lane (409 STALE_LANE when dethroned). */
|
|
28547
|
+
async steerDispatch(orgId, req) {
|
|
28548
|
+
return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
|
|
28549
|
+
}
|
|
28550
|
+
/** End a turn: no_action sweep of the lane's members + lane release + re-drive check. */
|
|
28551
|
+
async completeDispatch(orgId, req) {
|
|
28552
|
+
return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
|
|
28553
|
+
}
|
|
28554
|
+
/** Release a lane on graceful shutdown — members return to pending immediately. */
|
|
28555
|
+
async releaseDispatchLane(orgId, lane) {
|
|
28556
|
+
return this.request("POST", ENDPOINTS.DISPATCH_RELEASE(orgId), { lane });
|
|
28557
|
+
}
|
|
28558
|
+
/** Renew a live lane's lease (long-turn keepalive). 409 STALE_LANE when dethroned. */
|
|
28559
|
+
async heartbeatDispatchLane(orgId, req) {
|
|
28560
|
+
return this.request("POST", ENDPOINTS.DISPATCH_HEARTBEAT(orgId), req);
|
|
28561
|
+
}
|
|
28424
28562
|
async getDispatchByMessages(orgId, chatId, messageIds) {
|
|
28425
28563
|
const params = { chat_id: chatId, message_ids: messageIds.join(",") };
|
|
28426
28564
|
return this.request("GET", ENDPOINTS.DISPATCH_BY_MESSAGES(orgId), void 0, params);
|
|
@@ -28617,6 +28755,59 @@ var ParallClient = class _ParallClient {
|
|
|
28617
28755
|
async deleteExternalConnection(orgId, connectionId) {
|
|
28618
28756
|
return this.request("DELETE", ENDPOINTS.EXTERNAL_CONNECTION(orgId, connectionId));
|
|
28619
28757
|
}
|
|
28758
|
+
// ---- External IM channel (org-scoped) ----
|
|
28759
|
+
async createChannelConnection(orgId, input) {
|
|
28760
|
+
return this.request("POST", ENDPOINTS.CHANNEL_CONNECTIONS(orgId), input);
|
|
28761
|
+
}
|
|
28762
|
+
async listChannelConnections(orgId) {
|
|
28763
|
+
return this.request("GET", ENDPOINTS.CHANNEL_CONNECTIONS(orgId));
|
|
28764
|
+
}
|
|
28765
|
+
async getChannelConnection(orgId, connectionId) {
|
|
28766
|
+
return this.request("GET", ENDPOINTS.CHANNEL_CONNECTION(orgId, connectionId));
|
|
28767
|
+
}
|
|
28768
|
+
async updateChannelConnection(orgId, connectionId, patch) {
|
|
28769
|
+
return this.request("PATCH", ENDPOINTS.CHANNEL_CONNECTION(orgId, connectionId), patch);
|
|
28770
|
+
}
|
|
28771
|
+
async archiveChannelConnection(orgId, connectionId) {
|
|
28772
|
+
return this.request("DELETE", ENDPOINTS.CHANNEL_CONNECTION(orgId, connectionId));
|
|
28773
|
+
}
|
|
28774
|
+
async deliverChannelCredentials(orgId, connectionId, credentials) {
|
|
28775
|
+
return this.request("POST", ENDPOINTS.CHANNEL_CONNECTION_CREDENTIALS(orgId, connectionId), credentials);
|
|
28776
|
+
}
|
|
28777
|
+
async regenerateChannelIngressToken(orgId, connectionId) {
|
|
28778
|
+
return this.request("POST", ENDPOINTS.CHANNEL_CONNECTION_INGRESS_TOKEN_REGENERATE(orgId, connectionId));
|
|
28779
|
+
}
|
|
28780
|
+
/** Start a Feishu one-click provisioning session (device flow QR). */
|
|
28781
|
+
async initiateChannelProvisioning(orgId, input) {
|
|
28782
|
+
return this.request("POST", ENDPOINTS.CHANNEL_PROVISIONING(orgId), input);
|
|
28783
|
+
}
|
|
28784
|
+
/**
|
|
28785
|
+
* Lazy status poll — server-side this may forward one provider poll, so
|
|
28786
|
+
* call it at the session's `poll_interval_seconds` cadence, not faster.
|
|
28787
|
+
*/
|
|
28788
|
+
async getChannelProvisioningSession(orgId, sessionId) {
|
|
28789
|
+
return this.request("GET", ENDPOINTS.CHANNEL_PROVISIONING_SESSION(orgId, sessionId));
|
|
28790
|
+
}
|
|
28791
|
+
async cancelChannelProvisioning(orgId, sessionId) {
|
|
28792
|
+
return this.request("POST", ENDPOINTS.CHANNEL_PROVISIONING_CANCEL(orgId, sessionId));
|
|
28793
|
+
}
|
|
28794
|
+
async listChannelConversations(orgId, connectionId) {
|
|
28795
|
+
return this.request("GET", ENDPOINTS.CHANNEL_CONNECTION_CONVERSATIONS(orgId, connectionId));
|
|
28796
|
+
}
|
|
28797
|
+
async getChannelConversation(orgId, conversationId) {
|
|
28798
|
+
return this.request("GET", ENDPOINTS.CHANNEL_CONVERSATION(orgId, conversationId));
|
|
28799
|
+
}
|
|
28800
|
+
async listChannelConversationMessages(orgId, conversationId, limit) {
|
|
28801
|
+
return this.request("GET", ENDPOINTS.CHANNEL_CONVERSATION_MESSAGES(orgId, conversationId), void 0, { limit });
|
|
28802
|
+
}
|
|
28803
|
+
async setChannelConversationSession(orgId, conversationId, agentSessionId) {
|
|
28804
|
+
return this.request("PATCH", ENDPOINTS.CHANNEL_CONVERSATION_SESSION(orgId, conversationId), {
|
|
28805
|
+
agent_session_id: agentSessionId
|
|
28806
|
+
});
|
|
28807
|
+
}
|
|
28808
|
+
async getChannelMessage(orgId, messageId) {
|
|
28809
|
+
return this.request("GET", ENDPOINTS.CHANNEL_MESSAGE(orgId, messageId));
|
|
28810
|
+
}
|
|
28620
28811
|
async getExternalTriggerSchema(orgId, connectionId) {
|
|
28621
28812
|
return this.request("GET", ENDPOINTS.EXTERNAL_TRIGGER_SCHEMA(orgId, connectionId));
|
|
28622
28813
|
}
|
|
@@ -28658,6 +28849,23 @@ var ParallClient = class _ParallClient {
|
|
|
28658
28849
|
async getWiki(orgId, wikiId) {
|
|
28659
28850
|
return this.request("GET", ENDPOINTS.WIKI(orgId, wikiId));
|
|
28660
28851
|
}
|
|
28852
|
+
/** Soft-delete an entire wiki (org owner only; the default wiki is
|
|
28853
|
+
* protected → 403 WIKI_PROTECTED). Returns the deleted wiki. Recoverable
|
|
28854
|
+
* via restoreWiki within the retention window. */
|
|
28855
|
+
async deleteWiki(orgId, wikiId) {
|
|
28856
|
+
return this.request("DELETE", ENDPOINTS.WIKI(orgId, wikiId));
|
|
28857
|
+
}
|
|
28858
|
+
/** Restore a soft-deleted wiki (org owner only). 409 WIKI_PURGE_STARTED once
|
|
28859
|
+
* purge has been claimed (no longer restorable), or 404 once the row is gone.
|
|
28860
|
+
* Returns the restored wiki. */
|
|
28861
|
+
async restoreWiki(orgId, wikiId) {
|
|
28862
|
+
return this.request("POST", ENDPOINTS.WIKI_RESTORE(orgId, wikiId));
|
|
28863
|
+
}
|
|
28864
|
+
/** Recycle bin — list soft-deleted wikis for the org (owner only). */
|
|
28865
|
+
async getDeletedWikis(orgId) {
|
|
28866
|
+
const res = await this.request("GET", ENDPOINTS.WIKIS_DELETED(orgId));
|
|
28867
|
+
return res.data;
|
|
28868
|
+
}
|
|
28661
28869
|
async getWikiTree(orgId, wikiId, params) {
|
|
28662
28870
|
return this.request("GET", ENDPOINTS.WIKI_TREE(orgId, wikiId), void 0, params);
|
|
28663
28871
|
}
|
|
@@ -28691,6 +28899,15 @@ var ParallClient = class _ParallClient {
|
|
|
28691
28899
|
blob.signed_url = this.absoluteMediaUrl(blob.signed_url);
|
|
28692
28900
|
return blob;
|
|
28693
28901
|
}
|
|
28902
|
+
/**
|
|
28903
|
+
* Copy one wiki file to another path as an independent snapshot (no sync).
|
|
28904
|
+
* Used to "share a private file out": copy a file from the caller's personal
|
|
28905
|
+
* namespace (`users/{userId}/…`) into the public wiki, leaving the original
|
|
28906
|
+
* untouched. 409 `FILE_EXISTS` if `dest_path` already exists.
|
|
28907
|
+
*/
|
|
28908
|
+
async copyWikiFile(orgId, wikiId, req) {
|
|
28909
|
+
return this.request("POST", ENDPOINTS.WIKI_COPY(orgId, wikiId), req);
|
|
28910
|
+
}
|
|
28694
28911
|
async getWikiNodeSections(orgId, wikiId, params) {
|
|
28695
28912
|
return this.request("GET", ENDPOINTS.WIKI_NODE_SECTIONS(orgId, wikiId), void 0, params);
|
|
28696
28913
|
}
|
|
@@ -28806,8 +29023,8 @@ var ParallClient = class _ParallClient {
|
|
|
28806
29023
|
async deleteWikiRestriction(orgId, wikiId, restrictionId) {
|
|
28807
29024
|
await this.request("DELETE", ENDPOINTS.WIKI_RESTRICTION(orgId, wikiId, restrictionId));
|
|
28808
29025
|
}
|
|
28809
|
-
async getWikiAccessStatus(orgId, wikiId,
|
|
28810
|
-
return this.request("GET", ENDPOINTS.WIKI_ACCESS_STATUS(orgId, wikiId), void 0,
|
|
29026
|
+
async getWikiAccessStatus(orgId, wikiId, path9) {
|
|
29027
|
+
return this.request("GET", ENDPOINTS.WIKI_ACCESS_STATUS(orgId, wikiId), void 0, path9 ? { path: path9 } : void 0);
|
|
28811
29028
|
}
|
|
28812
29029
|
async createWikiAccessRequest(orgId, wikiId, data) {
|
|
28813
29030
|
await this.request("POST", ENDPOINTS.WIKI_ACCESS_REQUESTS(orgId, wikiId), data);
|
|
@@ -28816,14 +29033,14 @@ var ParallClient = class _ParallClient {
|
|
|
28816
29033
|
async getWikiCommits(orgId, wikiId, params) {
|
|
28817
29034
|
return this.request("GET", ENDPOINTS.WIKI_COMMITS(orgId, wikiId), void 0, params);
|
|
28818
29035
|
}
|
|
28819
|
-
async getWikiFileCommits(orgId, wikiId,
|
|
29036
|
+
async getWikiFileCommits(orgId, wikiId, path9, params) {
|
|
28820
29037
|
return this.request("GET", ENDPOINTS.WIKI_FILE_COMMITS(orgId, wikiId), void 0, {
|
|
28821
|
-
path:
|
|
29038
|
+
path: path9,
|
|
28822
29039
|
...params
|
|
28823
29040
|
});
|
|
28824
29041
|
}
|
|
28825
|
-
async getWikiBlame(orgId, wikiId,
|
|
28826
|
-
return this.request("GET", ENDPOINTS.WIKI_BLAME(orgId, wikiId), void 0, { path:
|
|
29042
|
+
async getWikiBlame(orgId, wikiId, path9, ref) {
|
|
29043
|
+
return this.request("GET", ENDPOINTS.WIKI_BLAME(orgId, wikiId), void 0, { path: path9, ref });
|
|
28827
29044
|
}
|
|
28828
29045
|
// ---- Wiki Operations (audit log) ----
|
|
28829
29046
|
async getWikiOperations(orgId, wikiId, params) {
|
|
@@ -28964,6 +29181,19 @@ var ParallClient = class _ParallClient {
|
|
|
28964
29181
|
const resp = await this.request("GET", ENDPOINTS.COMPUTE_PRICING());
|
|
28965
29182
|
return resp.data;
|
|
28966
29183
|
}
|
|
29184
|
+
/** Runtime capability table (public) — SSOT for the create/settings interlock. */
|
|
29185
|
+
async getRuntimes() {
|
|
29186
|
+
const resp = await this.request("GET", ENDPOINTS.RUNTIMES());
|
|
29187
|
+
return resp.data;
|
|
29188
|
+
}
|
|
29189
|
+
/**
|
|
29190
|
+
* Platform model catalog (public) — DB-backed, replaces the compile-time
|
|
29191
|
+
* PLATFORM_MODELS constant. Pass includeModel to keep an agent's pinned
|
|
29192
|
+
* hidden legacy model representable (settings picker).
|
|
29193
|
+
*/
|
|
29194
|
+
async getModels(includeModel) {
|
|
29195
|
+
return this.request("GET", ENDPOINTS.MODELS(includeModel));
|
|
29196
|
+
}
|
|
28967
29197
|
// ---- Clips ----
|
|
28968
29198
|
async listClips(orgId) {
|
|
28969
29199
|
const resp = await this.request("GET", ENDPOINTS.CLIPS(orgId));
|
|
@@ -29008,6 +29238,11 @@ var ParallClient = class _ParallClient {
|
|
|
29008
29238
|
const resp = await this.request("GET", ENDPOINTS.CLIP_ONLINE(orgId));
|
|
29009
29239
|
return resp.data;
|
|
29010
29240
|
}
|
|
29241
|
+
/** Deployment-wide hosted browser runtime availability — drives whether the
|
|
29242
|
+
* create UI offers the platform-hosted placement. Fail-closed server-side. */
|
|
29243
|
+
async getBrowserRuntimeStatus(orgId) {
|
|
29244
|
+
return this.request("GET", ENDPOINTS.BROWSER_RUNTIME_STATUS(orgId));
|
|
29245
|
+
}
|
|
29011
29246
|
/** Org-wide browser-profile discovery list. Returns the sanitized
|
|
29012
29247
|
* {@link BrowserProfileListItem} shape (not the full domain model), each row
|
|
29013
29248
|
* carrying a per-viewer `can_open` control hint. */
|
|
@@ -29437,6 +29672,404 @@ var ParallWs = class {
|
|
|
29437
29672
|
}
|
|
29438
29673
|
};
|
|
29439
29674
|
|
|
29675
|
+
// ts/agent-core/dist/lane-ledger.js
|
|
29676
|
+
import * as fs from "node:fs";
|
|
29677
|
+
var LedgerUnsupportedError = class extends Error {
|
|
29678
|
+
};
|
|
29679
|
+
function isStaleLane(err) {
|
|
29680
|
+
return err instanceof ApiError && err.status === 409 && err.code === "STALE_LANE";
|
|
29681
|
+
}
|
|
29682
|
+
function isEndpointMissing(err) {
|
|
29683
|
+
return err instanceof ApiError && err.status === 404 && !err.code;
|
|
29684
|
+
}
|
|
29685
|
+
var LaneLedger = class {
|
|
29686
|
+
opts;
|
|
29687
|
+
lanes = /* @__PURE__ */ new Map();
|
|
29688
|
+
constructor(opts) {
|
|
29689
|
+
this.opts = opts;
|
|
29690
|
+
}
|
|
29691
|
+
get contextDir() {
|
|
29692
|
+
return this.opts.contextDir;
|
|
29693
|
+
}
|
|
29694
|
+
/** Only chat message events ride the lane ledger; typed events stay on the legacy ack path. */
|
|
29695
|
+
handles(event) {
|
|
29696
|
+
return event.type === "message" && event.targetId.startsWith("cht_");
|
|
29697
|
+
}
|
|
29698
|
+
laneKeyFor(event) {
|
|
29699
|
+
if (event.type !== "message" && event.dispatchEventId) {
|
|
29700
|
+
return laneKeyForTarget(`dsp:${event.dispatchEventId}`);
|
|
29701
|
+
}
|
|
29702
|
+
return laneKeyForTarget(`prll://${event.targetId}`, event.threadRootId);
|
|
29703
|
+
}
|
|
29704
|
+
getForEvent(event) {
|
|
29705
|
+
return this.lanes.get(this.laneKeyFor(event));
|
|
29706
|
+
}
|
|
29707
|
+
laneContextPath(lane) {
|
|
29708
|
+
return laneContextFilePath(this.opts.contextDir, lane.targetUri, lane.threadRootId);
|
|
29709
|
+
}
|
|
29710
|
+
/**
|
|
29711
|
+
* Claim (or reuse) the lane for a group of same-lane message events and
|
|
29712
|
+
* fold every group member into it. Returns 'foreign' when a healthy
|
|
29713
|
+
* incumbent (another pod) holds the resource — the caller must not
|
|
29714
|
+
* dispatch; the events stay pending server-side and re-drive after the
|
|
29715
|
+
* incumbent completes.
|
|
29716
|
+
*/
|
|
29717
|
+
async ensureLane(events) {
|
|
29718
|
+
const trigger = events[events.length - 1];
|
|
29719
|
+
const laneKey = this.laneKeyFor(trigger);
|
|
29720
|
+
let lane = this.lanes.get(laneKey);
|
|
29721
|
+
if (!lane) {
|
|
29722
|
+
const targetUri = `prll://${trigger.targetId}`;
|
|
29723
|
+
let res;
|
|
29724
|
+
try {
|
|
29725
|
+
res = await this.opts.client.claimDispatch(this.opts.orgId, {
|
|
29726
|
+
target_uri: targetUri,
|
|
29727
|
+
thread_root_id: trigger.threadRootId,
|
|
29728
|
+
limit: 100
|
|
29729
|
+
});
|
|
29730
|
+
} catch (err) {
|
|
29731
|
+
if (isEndpointMissing(err))
|
|
29732
|
+
throw new LedgerUnsupportedError("claim endpoint unavailable");
|
|
29733
|
+
throw err;
|
|
29734
|
+
}
|
|
29735
|
+
if (!res.claimed || !res.lane) {
|
|
29736
|
+
this.opts.log?.info(`lane for ${targetUri} held by a healthy incumbent \u2014 leaving events pending for re-drive`);
|
|
29737
|
+
return null;
|
|
29738
|
+
}
|
|
29739
|
+
const leaseUntilMs = Date.parse(res.lease_until ?? "");
|
|
29740
|
+
lane = {
|
|
29741
|
+
laneKey,
|
|
29742
|
+
lane: res.lane,
|
|
29743
|
+
targetUri,
|
|
29744
|
+
threadRootId: trigger.threadRootId,
|
|
29745
|
+
folded: /* @__PURE__ */ new Map(),
|
|
29746
|
+
...Number.isNaN(leaseUntilMs) ? {} : { leaseUntilMs, leaseTtlMs: Math.max(leaseUntilMs - Date.now(), 6e4) }
|
|
29747
|
+
};
|
|
29748
|
+
for (const ev of res.events ?? []) {
|
|
29749
|
+
lane.folded.set(ev.source_id, ev.id);
|
|
29750
|
+
}
|
|
29751
|
+
this.lanes.set(laneKey, lane);
|
|
29752
|
+
}
|
|
29753
|
+
for (const ev of events) {
|
|
29754
|
+
if (lane.folded.has(ev.messageId))
|
|
29755
|
+
continue;
|
|
29756
|
+
try {
|
|
29757
|
+
const res = await this.opts.client.steerDispatch(this.opts.orgId, {
|
|
29758
|
+
lane: lane.lane,
|
|
29759
|
+
target_uri: lane.targetUri,
|
|
29760
|
+
thread_root_id: lane.threadRootId,
|
|
29761
|
+
...ev.dispatchEventId ? { dispatch_event_id: ev.dispatchEventId } : { source_type: "message", source_id: ev.messageId }
|
|
29762
|
+
});
|
|
29763
|
+
lane.folded.set(ev.messageId, res.dispatch_event_id);
|
|
29764
|
+
} catch (err) {
|
|
29765
|
+
if (isStaleLane(err)) {
|
|
29766
|
+
this.lanes.delete(laneKey);
|
|
29767
|
+
return null;
|
|
29768
|
+
}
|
|
29769
|
+
this.opts.log?.warn(`steer fold failed for ${ev.messageId} \u2014 failing closed, releasing lane: ${String(err)}`);
|
|
29770
|
+
await this.release(laneKey);
|
|
29771
|
+
return null;
|
|
29772
|
+
}
|
|
29773
|
+
}
|
|
29774
|
+
return lane;
|
|
29775
|
+
}
|
|
29776
|
+
/**
|
|
29777
|
+
* Fold a live mid-turn message into its active lane BEFORE injecting it
|
|
29778
|
+
* into the running turn. Injection without a successful fold is forbidden —
|
|
29779
|
+
* an un-folded injected message would be re-driven after complete and the
|
|
29780
|
+
* model would handle it twice.
|
|
29781
|
+
*/
|
|
29782
|
+
async steerLive(event) {
|
|
29783
|
+
const laneKey = this.laneKeyFor(event);
|
|
29784
|
+
const lane = this.lanes.get(laneKey);
|
|
29785
|
+
if (!lane)
|
|
29786
|
+
return false;
|
|
29787
|
+
if (lane.folded.has(event.messageId))
|
|
29788
|
+
return true;
|
|
29789
|
+
try {
|
|
29790
|
+
const res = await this.opts.client.steerDispatch(this.opts.orgId, {
|
|
29791
|
+
lane: lane.lane,
|
|
29792
|
+
target_uri: lane.targetUri,
|
|
29793
|
+
thread_root_id: lane.threadRootId,
|
|
29794
|
+
...event.dispatchEventId ? { dispatch_event_id: event.dispatchEventId } : { source_type: "message", source_id: event.messageId }
|
|
29795
|
+
});
|
|
29796
|
+
lane.folded.set(event.messageId, res.dispatch_event_id);
|
|
29797
|
+
return true;
|
|
29798
|
+
} catch (err) {
|
|
29799
|
+
if (isStaleLane(err)) {
|
|
29800
|
+
this.lanes.delete(laneKey);
|
|
29801
|
+
} else {
|
|
29802
|
+
this.opts.log?.warn(`live steer failed for ${event.messageId}: ${String(err)}`);
|
|
29803
|
+
}
|
|
29804
|
+
return false;
|
|
29805
|
+
}
|
|
29806
|
+
}
|
|
29807
|
+
/**
|
|
29808
|
+
* Complete the lane when no local work remains for it: the server sweeps
|
|
29809
|
+
* still-leased members as no_action, releases the occupancy row, and
|
|
29810
|
+
* re-drives any same-target pending work. A STALE_LANE answer means a
|
|
29811
|
+
* takeover already owns the resource — local state is dropped either way.
|
|
29812
|
+
*/
|
|
29813
|
+
async completeIfIdle(laneKey, hasMoreLocal) {
|
|
29814
|
+
const lane = this.lanes.get(laneKey);
|
|
29815
|
+
if (!lane || hasMoreLocal)
|
|
29816
|
+
return;
|
|
29817
|
+
this.lanes.delete(laneKey);
|
|
29818
|
+
this.removeLaneContext(lane);
|
|
29819
|
+
try {
|
|
29820
|
+
const res = await this.opts.client.completeDispatch(this.opts.orgId, {
|
|
29821
|
+
lane: lane.lane,
|
|
29822
|
+
target_uri: lane.targetUri,
|
|
29823
|
+
thread_root_id: lane.threadRootId
|
|
29824
|
+
});
|
|
29825
|
+
if (res.swept_no_action > 0 || res.redriven) {
|
|
29826
|
+
this.opts.log?.info(`lane complete for ${lane.targetUri}: swept ${res.swept_no_action} no_action, redriven=${res.redriven}`);
|
|
29827
|
+
}
|
|
29828
|
+
} catch (err) {
|
|
29829
|
+
if (isStaleLane(err)) {
|
|
29830
|
+
this.opts.log?.info(`lane complete skipped for ${lane.targetUri} \u2014 taken over`);
|
|
29831
|
+
return;
|
|
29832
|
+
}
|
|
29833
|
+
this.opts.log?.warn(`lane complete failed for ${lane.targetUri}: ${String(err)}`);
|
|
29834
|
+
}
|
|
29835
|
+
}
|
|
29836
|
+
/**
|
|
29837
|
+
* Long-turn keepalive: renew the lane's lease on runtime activity, throttled
|
|
29838
|
+
* so a chatty turn doesn't spam the server. Without this, a legitimately
|
|
29839
|
+
* long turn (> lane TTL) would be dethroned mid-flight and every subsequent
|
|
29840
|
+
* write misfired with STALE_LANE — the design doc's "long turns renew via
|
|
29841
|
+
* step writes". Fire-and-forget: a failed renewal is surfaced by the next
|
|
29842
|
+
* write's incumbency check anyway.
|
|
29843
|
+
*/
|
|
29844
|
+
maybeRenew(lane) {
|
|
29845
|
+
const now = Date.now();
|
|
29846
|
+
const ttl = lane.leaseTtlMs ?? 10 * 6e4;
|
|
29847
|
+
const until = lane.leaseUntilMs ?? now;
|
|
29848
|
+
if (until - now > ttl / 2)
|
|
29849
|
+
return;
|
|
29850
|
+
lane.leaseUntilMs = now + ttl;
|
|
29851
|
+
void this.opts.client.heartbeatDispatchLane(this.opts.orgId, {
|
|
29852
|
+
lane: lane.lane,
|
|
29853
|
+
target_uri: lane.targetUri,
|
|
29854
|
+
thread_root_id: lane.threadRootId
|
|
29855
|
+
}).then((res) => {
|
|
29856
|
+
const until2 = Date.parse(res?.lease_until ?? "");
|
|
29857
|
+
if (!Number.isNaN(until2))
|
|
29858
|
+
lane.leaseUntilMs = until2;
|
|
29859
|
+
}).catch((err) => {
|
|
29860
|
+
if (isStaleLane(err)) {
|
|
29861
|
+
this.lanes.delete(lane.laneKey);
|
|
29862
|
+
this.opts.log?.warn(`lane ${lane.targetUri} was taken over during the turn`);
|
|
29863
|
+
return;
|
|
29864
|
+
}
|
|
29865
|
+
this.opts.log?.warn(`lane heartbeat failed for ${lane.targetUri}: ${String(err)}`);
|
|
29866
|
+
});
|
|
29867
|
+
}
|
|
29868
|
+
/**
|
|
29869
|
+
* Release a lane's unresolved members back to pending (dispatch error /
|
|
29870
|
+
* shutdown) so the next pod re-claims immediately instead of waiting out
|
|
29871
|
+
* the lease.
|
|
29872
|
+
*/
|
|
29873
|
+
async release(laneKey) {
|
|
29874
|
+
const lane = this.lanes.get(laneKey);
|
|
29875
|
+
if (!lane)
|
|
29876
|
+
return;
|
|
29877
|
+
this.lanes.delete(laneKey);
|
|
29878
|
+
this.removeLaneContext(lane);
|
|
29879
|
+
try {
|
|
29880
|
+
await this.opts.client.releaseDispatchLane(this.opts.orgId, lane.lane);
|
|
29881
|
+
} catch (err) {
|
|
29882
|
+
this.opts.log?.warn(`lane release failed for ${lane.targetUri}: ${String(err)}`);
|
|
29883
|
+
}
|
|
29884
|
+
}
|
|
29885
|
+
async releaseAll() {
|
|
29886
|
+
const keys = [...this.lanes.keys()];
|
|
29887
|
+
for (const key of keys) {
|
|
29888
|
+
await this.release(key);
|
|
29889
|
+
}
|
|
29890
|
+
}
|
|
29891
|
+
/** True when any lane is currently active (used by shutdown logging). */
|
|
29892
|
+
get activeCount() {
|
|
29893
|
+
return this.lanes.size;
|
|
29894
|
+
}
|
|
29895
|
+
/**
|
|
29896
|
+
* Claim the single-member lane of one typed WorkItem (resource = dsp:<id>),
|
|
29897
|
+
* by WorkItem id or by source identity (the live task.assigned event has no
|
|
29898
|
+
* WorkItem id). Returns null when a healthy incumbent (another pod) holds
|
|
29899
|
+
* it or the WorkItem is already resolved — the caller must skip processing.
|
|
29900
|
+
*/
|
|
29901
|
+
async claimTyped(ref) {
|
|
29902
|
+
let res;
|
|
29903
|
+
try {
|
|
29904
|
+
res = await this.opts.client.claimDispatch(this.opts.orgId, {
|
|
29905
|
+
dispatch_event_id: ref.dispatchEventId,
|
|
29906
|
+
source_type: ref.dispatchEventId ? void 0 : ref.sourceType,
|
|
29907
|
+
source_id: ref.dispatchEventId ? void 0 : ref.sourceId
|
|
29908
|
+
});
|
|
29909
|
+
} catch (err) {
|
|
29910
|
+
if (isEndpointMissing(err))
|
|
29911
|
+
throw new LedgerUnsupportedError("claim endpoint unavailable");
|
|
29912
|
+
throw err;
|
|
29913
|
+
}
|
|
29914
|
+
if (!res.claimed || !res.lane || !res.events?.length)
|
|
29915
|
+
return null;
|
|
29916
|
+
const workItem = res.events[0];
|
|
29917
|
+
const targetUri = `dsp:${workItem.id}`;
|
|
29918
|
+
const leaseUntilMs = Date.parse(res.lease_until ?? "");
|
|
29919
|
+
const lane = {
|
|
29920
|
+
laneKey: laneKeyForTarget(targetUri),
|
|
29921
|
+
lane: res.lane,
|
|
29922
|
+
targetUri,
|
|
29923
|
+
folded: /* @__PURE__ */ new Map([[workItem.source_id, workItem.id]]),
|
|
29924
|
+
typedDispatchEventId: workItem.id,
|
|
29925
|
+
...Number.isNaN(leaseUntilMs) ? {} : { leaseUntilMs, leaseTtlMs: Math.max(leaseUntilMs - Date.now(), 6e4) }
|
|
29926
|
+
};
|
|
29927
|
+
this.lanes.set(lane.laneKey, lane);
|
|
29928
|
+
return lane;
|
|
29929
|
+
}
|
|
29930
|
+
/**
|
|
29931
|
+
* Remove the per-lane context file (and its CLI sidecar) when the lane
|
|
29932
|
+
* ends. A leftover file would make a later cross-context send to the same
|
|
29933
|
+
* target bind a dead lane token and misfire with STALE_LANE instead of
|
|
29934
|
+
* taking the plain non-ledger path.
|
|
29935
|
+
*/
|
|
29936
|
+
removeLaneContext(lane) {
|
|
29937
|
+
const contextPath = this.laneContextPath(lane);
|
|
29938
|
+
for (const p of [contextPath, contextPath.replace(/\.json$/, ".reply-state.json")]) {
|
|
29939
|
+
try {
|
|
29940
|
+
fs.rmSync(p, { force: true });
|
|
29941
|
+
} catch {
|
|
29942
|
+
}
|
|
29943
|
+
}
|
|
29944
|
+
}
|
|
29945
|
+
};
|
|
29946
|
+
|
|
29947
|
+
// ts/agent-core/dist/gateway-lane-flow.js
|
|
29948
|
+
async function dispatchLaneGroup(host, opts) {
|
|
29949
|
+
const ledger = host.laneLedger;
|
|
29950
|
+
const event = opts.events[opts.events.length - 1];
|
|
29951
|
+
let lane;
|
|
29952
|
+
try {
|
|
29953
|
+
lane = await ledger.ensureLane(opts.events);
|
|
29954
|
+
} catch (err) {
|
|
29955
|
+
if (!(err instanceof LedgerUnsupportedError))
|
|
29956
|
+
throw err;
|
|
29957
|
+
host.disableLedger("claim endpoint missing");
|
|
29958
|
+
await host.emitDispatchReceived(event);
|
|
29959
|
+
const dispatched2 = await host.runDispatch(event, opts.sessionKey, opts.body, opts.earlier, opts.captureText);
|
|
29960
|
+
if (!dispatched2)
|
|
29961
|
+
return "shutdown";
|
|
29962
|
+
for (const ev of opts.events) {
|
|
29963
|
+
host.opts.client.ackDispatch(host.opts.config.org_id, {
|
|
29964
|
+
source_type: ev.ackSourceType ?? "message",
|
|
29965
|
+
source_id: ev.ackSourceId ?? ev.messageId
|
|
29966
|
+
}).catch(() => {
|
|
29967
|
+
});
|
|
29968
|
+
}
|
|
29969
|
+
return "dispatched";
|
|
29970
|
+
}
|
|
29971
|
+
if (!lane) {
|
|
29972
|
+
for (const ev of opts.events) {
|
|
29973
|
+
host.dispatchedMessages.delete(ev.messageId);
|
|
29974
|
+
}
|
|
29975
|
+
return "foreign";
|
|
29976
|
+
}
|
|
29977
|
+
let dispatched = false;
|
|
29978
|
+
try {
|
|
29979
|
+
dispatched = await host.runDispatch(event, opts.sessionKey, opts.body, opts.earlier, opts.captureText);
|
|
29980
|
+
} catch (err) {
|
|
29981
|
+
await ledger.release(lane.laneKey).catch(() => {
|
|
29982
|
+
});
|
|
29983
|
+
throw err;
|
|
29984
|
+
}
|
|
29985
|
+
if (!dispatched) {
|
|
29986
|
+
return "shutdown";
|
|
29987
|
+
}
|
|
29988
|
+
const pendingInjections = host.opts.dispatchAdapter.hasPendingInjections?.(opts.sessionKey) ?? false;
|
|
29989
|
+
await ledger.completeIfIdle(lane.laneKey, pendingInjections || opts.hasMoreLocal());
|
|
29990
|
+
return "dispatched";
|
|
29991
|
+
}
|
|
29992
|
+
async function consumeTypedDispatch(host, ref, run, ack) {
|
|
29993
|
+
if (!host.laneLedger || host.ledgerDisabled) {
|
|
29994
|
+
if (await run(ref.dispatchEventId))
|
|
29995
|
+
ack(ref.dispatchEventId);
|
|
29996
|
+
return;
|
|
29997
|
+
}
|
|
29998
|
+
let lane;
|
|
29999
|
+
try {
|
|
30000
|
+
lane = await host.laneLedger.claimTyped(ref);
|
|
30001
|
+
} catch (err) {
|
|
30002
|
+
if (err instanceof LedgerUnsupportedError) {
|
|
30003
|
+
host.disableLedger("claim endpoint missing");
|
|
30004
|
+
if (await run(ref.dispatchEventId))
|
|
30005
|
+
ack(ref.dispatchEventId);
|
|
30006
|
+
return;
|
|
30007
|
+
}
|
|
30008
|
+
throw err;
|
|
30009
|
+
}
|
|
30010
|
+
if (!lane) {
|
|
30011
|
+
host.opts.log?.info(`typed dispatch ${ref.dispatchEventId ?? `${ref.sourceType}:${ref.sourceId}`} not claimable (held elsewhere or already resolved) \u2014 skipping`);
|
|
30012
|
+
return;
|
|
30013
|
+
}
|
|
30014
|
+
try {
|
|
30015
|
+
if (await run(lane.typedDispatchEventId))
|
|
30016
|
+
ack(lane.typedDispatchEventId);
|
|
30017
|
+
} finally {
|
|
30018
|
+
await host.laneLedger.completeIfIdle(lane.laneKey, false).catch(() => {
|
|
30019
|
+
});
|
|
30020
|
+
}
|
|
30021
|
+
}
|
|
30022
|
+
async function consumeMessageWorkItem(host, item) {
|
|
30023
|
+
if (host.shuttingDown)
|
|
30024
|
+
return;
|
|
30025
|
+
if (!host.tryClaimMessage(item.source_id))
|
|
30026
|
+
return;
|
|
30027
|
+
const ackItem = () => {
|
|
30028
|
+
host.opts.client.ackDispatchByID(host.opts.config.org_id, item.id).catch(() => {
|
|
30029
|
+
});
|
|
30030
|
+
};
|
|
30031
|
+
let msg = null;
|
|
30032
|
+
try {
|
|
30033
|
+
msg = await host.opts.client.getMessage(item.source_id);
|
|
30034
|
+
} catch (err) {
|
|
30035
|
+
const status = err?.status;
|
|
30036
|
+
if (status !== 404) {
|
|
30037
|
+
host.opts.log?.warn(`message fetch failed for ${item.source_id}, leaving pending: ${String(err)}`);
|
|
30038
|
+
host.dispatchedMessages.delete(item.source_id);
|
|
30039
|
+
return;
|
|
30040
|
+
}
|
|
30041
|
+
}
|
|
30042
|
+
if (!msg || msg.sender_id === host.opts.agentUserId) {
|
|
30043
|
+
host.dispatchedMessages.delete(item.source_id);
|
|
30044
|
+
ackItem();
|
|
30045
|
+
return;
|
|
30046
|
+
}
|
|
30047
|
+
const decision = await host.buildMessageDispatchDecision(item.chat_id, msg);
|
|
30048
|
+
if (decision.action === "retry") {
|
|
30049
|
+
host.dispatchedMessages.delete(item.source_id);
|
|
30050
|
+
return;
|
|
30051
|
+
}
|
|
30052
|
+
if (decision.action === "skip") {
|
|
30053
|
+
host.dispatchedMessages.delete(item.source_id);
|
|
30054
|
+
ackItem();
|
|
30055
|
+
return;
|
|
30056
|
+
}
|
|
30057
|
+
decision.event.dispatchEventId = item.id;
|
|
30058
|
+
const laneResolved = host.usesLaneLedger(decision.event);
|
|
30059
|
+
try {
|
|
30060
|
+
const dispatched = await host.handleInboundEvent(decision.event);
|
|
30061
|
+
if (dispatched) {
|
|
30062
|
+
if (!laneResolved)
|
|
30063
|
+
ackItem();
|
|
30064
|
+
} else {
|
|
30065
|
+
host.dispatchedMessages.delete(item.source_id);
|
|
30066
|
+
}
|
|
30067
|
+
} catch (err) {
|
|
30068
|
+
host.dispatchedMessages.delete(item.source_id);
|
|
30069
|
+
throw err;
|
|
30070
|
+
}
|
|
30071
|
+
}
|
|
30072
|
+
|
|
29440
30073
|
// ts/agent-core/dist/telemetry.js
|
|
29441
30074
|
init_esm();
|
|
29442
30075
|
var import_api_logs = __toESM(require_src(), 1);
|
|
@@ -29667,6 +30300,9 @@ function resolveStepTarget(event) {
|
|
|
29667
30300
|
if (event.type === "external_trigger" || event.targetId.startsWith("xtr_")) {
|
|
29668
30301
|
return { target_type: "external_trigger", target_id: event.targetId };
|
|
29669
30302
|
}
|
|
30303
|
+
if (event.type === "channel_message" || event.targetId.startsWith("chv_")) {
|
|
30304
|
+
return { target_type: "channel_conversation", target_id: event.targetId };
|
|
30305
|
+
}
|
|
29670
30306
|
if (event.type === "wiki_comment") {
|
|
29671
30307
|
return { target_type: "wiki", target_id: event.targetId || void 0 };
|
|
29672
30308
|
}
|
|
@@ -29710,6 +30346,9 @@ var ParallAgentGateway = class {
|
|
|
29710
30346
|
opts;
|
|
29711
30347
|
chatInfoMap = /* @__PURE__ */ new Map();
|
|
29712
30348
|
dispatchedTasks = /* @__PURE__ */ new Set();
|
|
30349
|
+
// connection id → provider alias, for channel_message prompt labeling
|
|
30350
|
+
// (stable mapping; avoids one connection fetch per inbound message).
|
|
30351
|
+
channelConnectionProviders = /* @__PURE__ */ new Map();
|
|
29713
30352
|
dispatchedMessages = /* @__PURE__ */ new Set();
|
|
29714
30353
|
forkStates = /* @__PURE__ */ new Map();
|
|
29715
30354
|
dispatchState = {
|
|
@@ -29732,6 +30371,14 @@ var ParallAgentGateway = class {
|
|
|
29732
30371
|
inFlightDispatches = 0;
|
|
29733
30372
|
drainResolvers = [];
|
|
29734
30373
|
pendingRestartNotification = null;
|
|
30374
|
+
laneLedger;
|
|
30375
|
+
// Sticky fallback: flipped when the server predates the ledger (claim
|
|
30376
|
+
// endpoint 404) so every subsequent dispatch uses the legacy flow.
|
|
30377
|
+
ledgerDisabled = false;
|
|
30378
|
+
// Group key of the group currently being dispatched on main — lane-aware
|
|
30379
|
+
// (targetId + thread), unlike mainCurrentTargetId which stays chat-level
|
|
30380
|
+
// for fork routing decisions.
|
|
30381
|
+
mainCurrentGroupKey;
|
|
29735
30382
|
DISPATCHED_MESSAGES_CAP = 5e3;
|
|
29736
30383
|
// SHUTDOWN_DEADLINE_MS is read by waitForDrain via the configured value
|
|
29737
30384
|
// below — kept as instance state so per-runtime configs can override it
|
|
@@ -29741,6 +30388,14 @@ var ParallAgentGateway = class {
|
|
|
29741
30388
|
DISPATCH_DEADLINE_MS;
|
|
29742
30389
|
constructor(opts) {
|
|
29743
30390
|
this.opts = opts;
|
|
30391
|
+
if (opts.dispatchContextDir) {
|
|
30392
|
+
this.laneLedger = new LaneLedger({
|
|
30393
|
+
client: opts.client,
|
|
30394
|
+
orgId: opts.config.org_id,
|
|
30395
|
+
contextDir: opts.dispatchContextDir,
|
|
30396
|
+
log: opts.log
|
|
30397
|
+
});
|
|
30398
|
+
}
|
|
29744
30399
|
this.SHUTDOWN_DEADLINE_MS = opts.shutdownDeadlineMs ?? 6e4;
|
|
29745
30400
|
this.FORK_DEADLINE_MS = opts.forkDeadlineMs ?? 2 * 60 * 6e4;
|
|
29746
30401
|
this.DISPATCH_DEADLINE_MS = opts.dispatchDeadlineMs ?? 20 * 6e4;
|
|
@@ -29810,14 +30465,18 @@ var ParallAgentGateway = class {
|
|
|
29810
30465
|
if (data.status !== "todo" && data.status !== "in_progress")
|
|
29811
30466
|
return;
|
|
29812
30467
|
try {
|
|
29813
|
-
|
|
29814
|
-
|
|
30468
|
+
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) => {
|
|
30469
|
+
if (dispatchEventId) {
|
|
30470
|
+
this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).catch(() => {
|
|
30471
|
+
});
|
|
30472
|
+
return;
|
|
30473
|
+
}
|
|
29815
30474
|
this.opts.client.ackDispatch(this.opts.config.org_id, {
|
|
29816
30475
|
source_type: "task_activity",
|
|
29817
30476
|
source_id: data.id
|
|
29818
30477
|
}).catch(() => {
|
|
29819
30478
|
});
|
|
29820
|
-
}
|
|
30479
|
+
});
|
|
29821
30480
|
} catch (err) {
|
|
29822
30481
|
this.opts.log?.error(`task dispatch failed for ${data.id}: ${String(err)}`);
|
|
29823
30482
|
}
|
|
@@ -29827,11 +30486,10 @@ var ParallAgentGateway = class {
|
|
|
29827
30486
|
if (!data.source_id || !data.task_id)
|
|
29828
30487
|
return;
|
|
29829
30488
|
try {
|
|
29830
|
-
|
|
29831
|
-
if (dispatched) {
|
|
30489
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleTaskComment(data.source_id, data.task_id ?? "", data.actor_id, data.delivery_reason), () => {
|
|
29832
30490
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
29833
30491
|
});
|
|
29834
|
-
}
|
|
30492
|
+
});
|
|
29835
30493
|
} catch (err) {
|
|
29836
30494
|
this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
29837
30495
|
}
|
|
@@ -29839,11 +30497,10 @@ var ParallAgentGateway = class {
|
|
|
29839
30497
|
if (!data.source_id)
|
|
29840
30498
|
return;
|
|
29841
30499
|
try {
|
|
29842
|
-
|
|
29843
|
-
if (dispatched) {
|
|
30500
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason), () => {
|
|
29844
30501
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
29845
30502
|
});
|
|
29846
|
-
}
|
|
30503
|
+
});
|
|
29847
30504
|
} catch (err) {
|
|
29848
30505
|
this.opts.log?.error(`wiki comment dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
29849
30506
|
}
|
|
@@ -29851,11 +30508,13 @@ var ParallAgentGateway = class {
|
|
|
29851
30508
|
if (!data.task_id)
|
|
29852
30509
|
return;
|
|
29853
30510
|
try {
|
|
29854
|
-
|
|
29855
|
-
|
|
30511
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleTaskDispatch(data.task_id ?? "", data.source_id ?? data.task_id ?? "", {
|
|
30512
|
+
allowCreator: true,
|
|
30513
|
+
dispatchEventId
|
|
30514
|
+
}), () => {
|
|
29856
30515
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
29857
30516
|
});
|
|
29858
|
-
}
|
|
30517
|
+
});
|
|
29859
30518
|
} catch (err) {
|
|
29860
30519
|
this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
|
|
29861
30520
|
}
|
|
@@ -29863,11 +30522,10 @@ var ParallAgentGateway = class {
|
|
|
29863
30522
|
if (!data.source_id)
|
|
29864
30523
|
return;
|
|
29865
30524
|
try {
|
|
29866
|
-
|
|
29867
|
-
if (dispatched) {
|
|
30525
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id), () => {
|
|
29868
30526
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
29869
30527
|
});
|
|
29870
|
-
}
|
|
30528
|
+
});
|
|
29871
30529
|
} catch (err) {
|
|
29872
30530
|
this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
29873
30531
|
}
|
|
@@ -29875,26 +30533,41 @@ var ParallAgentGateway = class {
|
|
|
29875
30533
|
if (!data.source_id)
|
|
29876
30534
|
return;
|
|
29877
30535
|
try {
|
|
29878
|
-
|
|
29879
|
-
if (dispatched) {
|
|
30536
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id), () => {
|
|
29880
30537
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
29881
30538
|
});
|
|
29882
|
-
}
|
|
30539
|
+
});
|
|
29883
30540
|
} catch (err) {
|
|
29884
30541
|
this.opts.log?.error(`external trigger dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
29885
30542
|
}
|
|
30543
|
+
} else if (data.event_type === "channel_message") {
|
|
30544
|
+
if (!data.source_id)
|
|
30545
|
+
return;
|
|
30546
|
+
try {
|
|
30547
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id), () => {
|
|
30548
|
+
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
30549
|
+
});
|
|
30550
|
+
});
|
|
30551
|
+
} catch (err) {
|
|
30552
|
+
this.opts.log?.error(`channel message dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30553
|
+
}
|
|
29886
30554
|
} else if (data.event_type === "approval_decided") {
|
|
29887
30555
|
if (!data.source_id)
|
|
29888
30556
|
return;
|
|
29889
30557
|
try {
|
|
29890
|
-
|
|
29891
|
-
if (dispatched) {
|
|
30558
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null), () => {
|
|
29892
30559
|
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
|
|
29893
30560
|
});
|
|
29894
|
-
}
|
|
30561
|
+
});
|
|
29895
30562
|
} catch (err) {
|
|
29896
30563
|
this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
29897
30564
|
}
|
|
30565
|
+
} else if (data.event_type === "message" && this.laneLedger && data.source_id && data.chat_id) {
|
|
30566
|
+
try {
|
|
30567
|
+
await this.handleMessageRedrive(data);
|
|
30568
|
+
} catch (err) {
|
|
30569
|
+
this.opts.log?.error(`message re-drive failed for ${data.source_id}: ${String(err)}`);
|
|
30570
|
+
}
|
|
29898
30571
|
} else if (data.event_type !== "message" && data.event_type !== "task_assign") {
|
|
29899
30572
|
this.opts.log?.info(`dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) \u2014 no-op`);
|
|
29900
30573
|
}
|
|
@@ -29930,6 +30603,39 @@ var ParallAgentGateway = class {
|
|
|
29930
30603
|
source_id: sourceId
|
|
29931
30604
|
});
|
|
29932
30605
|
}
|
|
30606
|
+
/** True when this event's lifecycle is owned by the dispatch lane ledger. */
|
|
30607
|
+
usesLaneLedger(event) {
|
|
30608
|
+
return this.laneLedger != null && !this.ledgerDisabled && this.laneLedger.handles(event);
|
|
30609
|
+
}
|
|
30610
|
+
disableLedger(reason) {
|
|
30611
|
+
if (this.ledgerDisabled)
|
|
30612
|
+
return;
|
|
30613
|
+
this.ledgerDisabled = true;
|
|
30614
|
+
this.opts.log?.warn(`dispatch ledger unavailable (${reason}) \u2014 falling back to legacy received/ack flow`);
|
|
30615
|
+
}
|
|
30616
|
+
/**
|
|
30617
|
+
* Buffer grouping key. Lane-ledger message events group by full lane
|
|
30618
|
+
* identity (chat + thread) so a channel lane and a thread lane in the same
|
|
30619
|
+
* chat dispatch as separate turns with separate claims; everything else
|
|
30620
|
+
* keeps the historical chat-level grouping.
|
|
30621
|
+
*/
|
|
30622
|
+
dispatchGroupKey(event) {
|
|
30623
|
+
if (this.usesLaneLedger(event)) {
|
|
30624
|
+
return this.laneLedger.laneKeyFor(event);
|
|
30625
|
+
}
|
|
30626
|
+
return event.targetId;
|
|
30627
|
+
}
|
|
30628
|
+
// Lane-flow protocols live in gateway-lane-flow.ts; these thin delegates
|
|
30629
|
+
// keep call sites and tests on the class surface.
|
|
30630
|
+
laneFlowHost() {
|
|
30631
|
+
return this;
|
|
30632
|
+
}
|
|
30633
|
+
dispatchLaneGroup(opts) {
|
|
30634
|
+
return dispatchLaneGroup(this.laneFlowHost(), opts);
|
|
30635
|
+
}
|
|
30636
|
+
consumeTypedDispatch(ref, run, ack) {
|
|
30637
|
+
return consumeTypedDispatch(this.laneFlowHost(), ref, run, ack);
|
|
30638
|
+
}
|
|
29933
30639
|
buildDispatchContext(event, sessionKey) {
|
|
29934
30640
|
const binding = this.sessionBindings.get(sessionKey);
|
|
29935
30641
|
return {
|
|
@@ -29946,6 +30652,7 @@ var ParallAgentGateway = class {
|
|
|
29946
30652
|
noReply: event.noReply ?? false,
|
|
29947
30653
|
contextFilePath: this.opts.contextFilePathForSession?.(sessionKey),
|
|
29948
30654
|
stepIdFilePath: this.opts.stepIdFilePathForSession?.(sessionKey),
|
|
30655
|
+
contextDirPath: this.opts.dispatchContextDir,
|
|
29949
30656
|
client: this.opts.client,
|
|
29950
30657
|
log: this.opts.log
|
|
29951
30658
|
};
|
|
@@ -29961,12 +30668,17 @@ var ParallAgentGateway = class {
|
|
|
29961
30668
|
target_type: target.target_type,
|
|
29962
30669
|
target_id: target.target_id,
|
|
29963
30670
|
content: {
|
|
29964
|
-
trigger_type: event.type === "task" ? "task_assign" : event.type === "task_comment" ? "task_comment" : event.type === "wiki_comment" ? "wiki_comment" : event.type === "schedule" ? "schedule_fire" : event.type === "external_trigger" ? "external_trigger" : event.type === "approval" ? "approval_decided" : "mention",
|
|
30671
|
+
trigger_type: event.type === "task" ? "task_assign" : event.type === "task_comment" ? "task_comment" : event.type === "wiki_comment" ? "wiki_comment" : event.type === "schedule" ? "schedule_fire" : event.type === "external_trigger" ? "external_trigger" : event.type === "channel_message" ? "channel_message" : event.type === "approval" ? "approval_decided" : "mention",
|
|
29965
30672
|
trigger_ref: event.type === "task" ? { task_id: event.targetId } : event.type === "task_comment" ? { comment_id: event.messageId, task_id: event.targetId } : event.type === "wiki_comment" ? { comment_id: event.messageId, target_uri: event.replyTargetUri } : event.type === "schedule" ? { schedule_id: event.targetId, run_id: event.messageId } : event.type === "external_trigger" ? {
|
|
29966
30673
|
trigger_id: event.targetId,
|
|
29967
30674
|
run_id: event.messageId,
|
|
29968
30675
|
connection_id: event.externalConnectionId,
|
|
29969
30676
|
ingress_event_id: event.externalIngressEventId
|
|
30677
|
+
} : event.type === "channel_message" ? {
|
|
30678
|
+
conversation_id: event.targetId,
|
|
30679
|
+
channel_message_id: event.messageId,
|
|
30680
|
+
provider: event.channelProvider,
|
|
30681
|
+
external_conversation_id: event.channelExternalConversationId
|
|
29970
30682
|
} : event.type === "approval" ? { approval_id: event.messageId } : { message_id: event.messageId },
|
|
29971
30683
|
sender_id: event.senderId,
|
|
29972
30684
|
sender_name: event.senderName,
|
|
@@ -29980,7 +30692,7 @@ var ParallAgentGateway = class {
|
|
|
29980
30692
|
this.opts.log?.warn(`failed to create input step: ${String(err)}`);
|
|
29981
30693
|
}
|
|
29982
30694
|
}
|
|
29983
|
-
async createRuntimeStep(sessionId, event, runtimeEvent, stepIdFilePath, contextFilePath) {
|
|
30695
|
+
async createRuntimeStep(sessionId, event, runtimeEvent, stepIdFilePath, contextFilePath, laneContextFilePath2) {
|
|
29984
30696
|
const target = resolveStepTarget(event);
|
|
29985
30697
|
try {
|
|
29986
30698
|
switch (runtimeEvent.type) {
|
|
@@ -30026,6 +30738,9 @@ var ParallAgentGateway = class {
|
|
|
30026
30738
|
} else if (stepIdFilePath) {
|
|
30027
30739
|
this.writeStepIdFile(stepIdFilePath, step.id);
|
|
30028
30740
|
}
|
|
30741
|
+
if (laneContextFilePath2) {
|
|
30742
|
+
this.updateContextFileStepId(laneContextFilePath2, step.id);
|
|
30743
|
+
}
|
|
30029
30744
|
break;
|
|
30030
30745
|
}
|
|
30031
30746
|
case "tool_result":
|
|
@@ -30048,6 +30763,9 @@ var ParallAgentGateway = class {
|
|
|
30048
30763
|
} else if (stepIdFilePath) {
|
|
30049
30764
|
this.clearStepIdFile(stepIdFilePath);
|
|
30050
30765
|
}
|
|
30766
|
+
if (laneContextFilePath2) {
|
|
30767
|
+
this.updateContextFileStepId(laneContextFilePath2, null);
|
|
30768
|
+
}
|
|
30051
30769
|
break;
|
|
30052
30770
|
case "error":
|
|
30053
30771
|
await this.opts.client.createAgentStep(this.opts.config.org_id, this.opts.agentUserId, sessionId, {
|
|
@@ -30067,28 +30785,28 @@ var ParallAgentGateway = class {
|
|
|
30067
30785
|
}
|
|
30068
30786
|
writeContextFile(filePath, ctx) {
|
|
30069
30787
|
try {
|
|
30070
|
-
|
|
30071
|
-
|
|
30788
|
+
fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
|
|
30789
|
+
fs2.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
30072
30790
|
} catch (err) {
|
|
30073
30791
|
this.opts.log?.warn(`failed to write context file ${filePath}: ${String(err)}`);
|
|
30074
30792
|
}
|
|
30075
30793
|
}
|
|
30076
30794
|
updateContextFileStepId(filePath, stepId) {
|
|
30077
30795
|
try {
|
|
30078
|
-
const raw =
|
|
30796
|
+
const raw = fs2.readFileSync(filePath, "utf8");
|
|
30079
30797
|
const ctx = JSON.parse(raw);
|
|
30080
30798
|
ctx.step_id = stepId;
|
|
30081
|
-
|
|
30799
|
+
fs2.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
30082
30800
|
} catch (err) {
|
|
30083
30801
|
this.opts.log?.warn(`failed to update context file step_id ${filePath}: ${String(err)}`);
|
|
30084
30802
|
}
|
|
30085
30803
|
}
|
|
30086
30804
|
updateContextFileSessionId(filePath, sessionId) {
|
|
30087
30805
|
try {
|
|
30088
|
-
const raw =
|
|
30806
|
+
const raw = fs2.readFileSync(filePath, "utf8");
|
|
30089
30807
|
const ctx = JSON.parse(raw);
|
|
30090
30808
|
ctx.session_id = sessionId;
|
|
30091
|
-
|
|
30809
|
+
fs2.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
30092
30810
|
} catch (err) {
|
|
30093
30811
|
this.opts.log?.warn(`failed to update context file session_id ${filePath}: ${String(err)}`);
|
|
30094
30812
|
}
|
|
@@ -30096,8 +30814,8 @@ var ParallAgentGateway = class {
|
|
|
30096
30814
|
/** @deprecated Use writeContextFile / updateContextFileStepId. */
|
|
30097
30815
|
writeStepIdFile(filePath, stepId) {
|
|
30098
30816
|
try {
|
|
30099
|
-
|
|
30100
|
-
|
|
30817
|
+
fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
|
|
30818
|
+
fs2.writeFileSync(filePath, stepId, "utf8");
|
|
30101
30819
|
} catch (err) {
|
|
30102
30820
|
this.opts.log?.warn(`failed to write step id file ${filePath}: ${String(err)}`);
|
|
30103
30821
|
}
|
|
@@ -30105,7 +30823,7 @@ var ParallAgentGateway = class {
|
|
|
30105
30823
|
/** @deprecated Use writeContextFile / updateContextFileStepId. */
|
|
30106
30824
|
clearStepIdFile(filePath) {
|
|
30107
30825
|
try {
|
|
30108
|
-
|
|
30826
|
+
fs2.writeFileSync(filePath, "", "utf8");
|
|
30109
30827
|
} catch {
|
|
30110
30828
|
}
|
|
30111
30829
|
}
|
|
@@ -30114,7 +30832,7 @@ var ParallAgentGateway = class {
|
|
|
30114
30832
|
await this.createInputStep(sessionId, event);
|
|
30115
30833
|
}
|
|
30116
30834
|
}
|
|
30117
|
-
async bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath) {
|
|
30835
|
+
async bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath, laneContextFilePath2) {
|
|
30118
30836
|
const runtimeLaneKey = runtimeEvent.runtimeLaneKey || sessionKey;
|
|
30119
30837
|
const existing = this.sessionBindings.get(sessionKey);
|
|
30120
30838
|
if (existing && existing.runtimeLaneKey === runtimeLaneKey && existing.runtimeSessionId === runtimeEvent.runtimeSessionId) {
|
|
@@ -30158,6 +30876,9 @@ var ParallAgentGateway = class {
|
|
|
30158
30876
|
if (contextFilePath) {
|
|
30159
30877
|
this.updateContextFileSessionId(contextFilePath, session.id);
|
|
30160
30878
|
}
|
|
30879
|
+
if (laneContextFilePath2) {
|
|
30880
|
+
this.updateContextFileSessionId(laneContextFilePath2, session.id);
|
|
30881
|
+
}
|
|
30161
30882
|
await this.opts.onSessionBinding?.(binding);
|
|
30162
30883
|
return binding;
|
|
30163
30884
|
}
|
|
@@ -30184,14 +30905,27 @@ var ParallAgentGateway = class {
|
|
|
30184
30905
|
const dispatchContext = this.buildDispatchContext(event, sessionKey);
|
|
30185
30906
|
const contextFilePath = dispatchContext.contextFilePath;
|
|
30186
30907
|
const stepIdFilePath = dispatchContext.stepIdFilePath;
|
|
30908
|
+
const activeLane = this.ledgerDisabled ? void 0 : this.laneLedger?.getForEvent(event);
|
|
30909
|
+
const laneContextFilePath2 = activeLane ? this.laneLedger?.laneContextPath(activeLane) : void 0;
|
|
30910
|
+
const contextBody = {
|
|
30911
|
+
session_id: dispatchContext.sessionId ?? null,
|
|
30912
|
+
chat_id: dispatchContext.chatId ?? null,
|
|
30913
|
+
trigger_message_id: dispatchContext.triggerMessageId ?? null,
|
|
30914
|
+
no_reply: dispatchContext.noReply,
|
|
30915
|
+
step_id: null,
|
|
30916
|
+
dispatch_event_id: activeLane?.typedDispatchEventId ?? activeLane?.folded.get(event.messageId) ?? null,
|
|
30917
|
+
lane: activeLane?.lane ?? null,
|
|
30918
|
+
target_uri: activeLane?.targetUri ?? null,
|
|
30919
|
+
thread_root_id: activeLane?.threadRootId ?? null,
|
|
30920
|
+
// Typed binding hint for the CLI: which task this dispatch is about
|
|
30921
|
+
// (parall task update attaches the typed effect only on a match).
|
|
30922
|
+
task_id: event.type === "task" ? event.targetId : null
|
|
30923
|
+
};
|
|
30187
30924
|
if (contextFilePath) {
|
|
30188
|
-
this.writeContextFile(contextFilePath,
|
|
30189
|
-
|
|
30190
|
-
|
|
30191
|
-
|
|
30192
|
-
no_reply: dispatchContext.noReply,
|
|
30193
|
-
step_id: null
|
|
30194
|
-
});
|
|
30925
|
+
this.writeContextFile(contextFilePath, contextBody);
|
|
30926
|
+
}
|
|
30927
|
+
if (laneContextFilePath2) {
|
|
30928
|
+
this.writeContextFile(laneContextFilePath2, contextBody);
|
|
30195
30929
|
}
|
|
30196
30930
|
this.inFlightDispatches++;
|
|
30197
30931
|
const deadlineTimer = this.DISPATCH_DEADLINE_MS > 0 ? setTimeout(() => {
|
|
@@ -30217,7 +30951,15 @@ var ParallAgentGateway = class {
|
|
|
30217
30951
|
context: dispatchContext
|
|
30218
30952
|
})) {
|
|
30219
30953
|
if (runtimeEvent.type === "runtime_session") {
|
|
30220
|
-
|
|
30954
|
+
const priorAgentSessionId = binding?.agentSessionId;
|
|
30955
|
+
binding = await this.bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath, laneContextFilePath2);
|
|
30956
|
+
if (event.targetType === "channel_conversation" && binding.agentSessionId !== priorAgentSessionId) {
|
|
30957
|
+
try {
|
|
30958
|
+
await this.opts.client.setChannelConversationSession(this.opts.config.org_id, event.targetId, binding.agentSessionId);
|
|
30959
|
+
} catch (err) {
|
|
30960
|
+
this.opts.log?.warn(`failed to record session mapping for channel conversation ${event.targetId}: ${String(err)}`);
|
|
30961
|
+
}
|
|
30962
|
+
}
|
|
30221
30963
|
if (!inputStepsCreated) {
|
|
30222
30964
|
if (earlierEvents.length > 0) {
|
|
30223
30965
|
await this.createInputStepsForEarlierEvents(binding.agentSessionId, earlierEvents);
|
|
@@ -30242,6 +30984,9 @@ var ParallAgentGateway = class {
|
|
|
30242
30984
|
await this.createInputStep(binding.agentSessionId, event);
|
|
30243
30985
|
inputStepsCreated = true;
|
|
30244
30986
|
}
|
|
30987
|
+
if (activeLane && !this.ledgerDisabled) {
|
|
30988
|
+
this.laneLedger?.maybeRenew(activeLane);
|
|
30989
|
+
}
|
|
30245
30990
|
if (captureText && runtimeEvent.type === "text" && runtimeEvent.text) {
|
|
30246
30991
|
captureText.push(runtimeEvent.text);
|
|
30247
30992
|
}
|
|
@@ -30258,7 +31003,7 @@ var ParallAgentGateway = class {
|
|
|
30258
31003
|
} else if (runtimeEvent.type === "tool_result" && pendingSendCallIds.delete(runtimeEvent.callId)) {
|
|
30259
31004
|
recordMessageSend(sessionKey, !runtimeEvent.error);
|
|
30260
31005
|
}
|
|
30261
|
-
await this.createRuntimeStep(binding.agentSessionId, event, runtimeEvent, stepIdFilePath, contextFilePath);
|
|
31006
|
+
await this.createRuntimeStep(binding.agentSessionId, event, runtimeEvent, stepIdFilePath, contextFilePath, laneContextFilePath2);
|
|
30262
31007
|
}
|
|
30263
31008
|
if (!binding) {
|
|
30264
31009
|
binding = this.sessionBindings.get(sessionKey);
|
|
@@ -30280,7 +31025,7 @@ var ParallAgentGateway = class {
|
|
|
30280
31025
|
await this.createRuntimeStep(binding.agentSessionId, event, {
|
|
30281
31026
|
type: "error",
|
|
30282
31027
|
message: `Dispatch failed: ${String(err)}`
|
|
30283
|
-
}, stepIdFilePath, contextFilePath);
|
|
31028
|
+
}, stepIdFilePath, contextFilePath, laneContextFilePath2);
|
|
30284
31029
|
} catch (stepErr) {
|
|
30285
31030
|
if (this.isSessionNotLiveError(stepErr))
|
|
30286
31031
|
staleDetected = true;
|
|
@@ -30323,6 +31068,9 @@ var ParallAgentGateway = class {
|
|
|
30323
31068
|
} else if (stepIdFilePath) {
|
|
30324
31069
|
this.clearStepIdFile(stepIdFilePath);
|
|
30325
31070
|
}
|
|
31071
|
+
if (laneContextFilePath2) {
|
|
31072
|
+
this.updateContextFileStepId(laneContextFilePath2, null);
|
|
31073
|
+
}
|
|
30326
31074
|
this.inFlightDispatches--;
|
|
30327
31075
|
if (this.inFlightDispatches === 0 && this.drainResolvers.length > 0) {
|
|
30328
31076
|
const resolvers = this.drainResolvers.splice(0);
|
|
@@ -30380,13 +31128,39 @@ var ParallAgentGateway = class {
|
|
|
30380
31128
|
item.resolve(false);
|
|
30381
31129
|
break;
|
|
30382
31130
|
}
|
|
30383
|
-
|
|
31131
|
+
let items;
|
|
31132
|
+
const head = fork.queue[0];
|
|
31133
|
+
if (head && this.usesLaneLedger(head.event)) {
|
|
31134
|
+
const headKey = this.dispatchGroupKey(head.event);
|
|
31135
|
+
const splitAt = fork.queue.findIndex((it) => this.dispatchGroupKey(it.event) !== headKey);
|
|
31136
|
+
items = splitAt === -1 ? fork.queue.splice(0) : fork.queue.splice(0, splitAt);
|
|
31137
|
+
} else {
|
|
31138
|
+
items = fork.queue.splice(0);
|
|
31139
|
+
}
|
|
30384
31140
|
const events = items.map((item) => item.event);
|
|
30385
31141
|
const last = events[events.length - 1];
|
|
30386
31142
|
const earlier = events.slice(0, -1);
|
|
30387
31143
|
try {
|
|
30388
31144
|
const batchText = [];
|
|
30389
|
-
|
|
31145
|
+
let dispatched;
|
|
31146
|
+
if (this.usesLaneLedger(last)) {
|
|
31147
|
+
const outcome = await this.dispatchLaneGroup({
|
|
31148
|
+
events,
|
|
31149
|
+
sessionKey: fork.fork.sessionKey,
|
|
31150
|
+
body: buildForkScopePrefix(last) + buildEventBody(last),
|
|
31151
|
+
earlier,
|
|
31152
|
+
captureText: batchText,
|
|
31153
|
+
hasMoreLocal: () => fork.queue.length > 0
|
|
31154
|
+
});
|
|
31155
|
+
if (outcome === "foreign") {
|
|
31156
|
+
for (const item of items)
|
|
31157
|
+
item.resolve(false);
|
|
31158
|
+
break;
|
|
31159
|
+
}
|
|
31160
|
+
dispatched = outcome === "dispatched";
|
|
31161
|
+
} else {
|
|
31162
|
+
dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
|
|
31163
|
+
}
|
|
30390
31164
|
if (!dispatched) {
|
|
30391
31165
|
for (const item of items) {
|
|
30392
31166
|
item.resolve(false);
|
|
@@ -30489,9 +31263,9 @@ var ParallAgentGateway = class {
|
|
|
30489
31263
|
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`);
|
|
30490
31264
|
break;
|
|
30491
31265
|
}
|
|
30492
|
-
const
|
|
31266
|
+
const groupKey = this.dispatchGroupKey(this.dispatchState.mainBuffer[0]);
|
|
30493
31267
|
const events = [];
|
|
30494
|
-
while (this.dispatchState.mainBuffer[0]
|
|
31268
|
+
while (this.dispatchState.mainBuffer[0] && this.dispatchGroupKey(this.dispatchState.mainBuffer[0]) === groupKey) {
|
|
30495
31269
|
events.push(this.dispatchState.mainBuffer.shift());
|
|
30496
31270
|
}
|
|
30497
31271
|
const event = events[events.length - 1];
|
|
@@ -30500,7 +31274,36 @@ var ParallAgentGateway = class {
|
|
|
30500
31274
|
const pendingFork = hasPendingInjections ? [] : this.dispatchState.pendingForkResults.splice(0);
|
|
30501
31275
|
const forkPrefix = buildForkResultPrefix(pendingFork);
|
|
30502
31276
|
this.dispatchState.mainCurrentTargetId = event.targetId;
|
|
31277
|
+
this.mainCurrentGroupKey = groupKey;
|
|
30503
31278
|
this.dispatchState.mainPreDispatchBranchPoint = this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
|
|
31279
|
+
if (this.usesLaneLedger(event)) {
|
|
31280
|
+
let outcome;
|
|
31281
|
+
try {
|
|
31282
|
+
outcome = await this.dispatchLaneGroup({
|
|
31283
|
+
events,
|
|
31284
|
+
sessionKey: this.opts.runtimeKey,
|
|
31285
|
+
body: forkPrefix + buildEventBody(event),
|
|
31286
|
+
earlier,
|
|
31287
|
+
hasMoreLocal: () => this.dispatchState.mainBuffer.some((e) => this.dispatchGroupKey(e) === groupKey)
|
|
31288
|
+
});
|
|
31289
|
+
} catch (err) {
|
|
31290
|
+
this.opts.log?.error(`lane dispatch failed for ${event.messageId}: ${String(err)}`);
|
|
31291
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31292
|
+
for (const ev of events)
|
|
31293
|
+
this.dispatchedMessages.delete(ev.messageId);
|
|
31294
|
+
continue;
|
|
31295
|
+
}
|
|
31296
|
+
if (outcome === "shutdown") {
|
|
31297
|
+
this.dispatchState.mainBuffer.unshift(...events);
|
|
31298
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31299
|
+
break;
|
|
31300
|
+
}
|
|
31301
|
+
if (outcome === "foreign") {
|
|
31302
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31303
|
+
continue;
|
|
31304
|
+
}
|
|
31305
|
+
continue;
|
|
31306
|
+
}
|
|
30504
31307
|
try {
|
|
30505
31308
|
await this.emitDispatchReceived(event);
|
|
30506
31309
|
} catch (err) {
|
|
@@ -30529,6 +31332,7 @@ var ParallAgentGateway = class {
|
|
|
30529
31332
|
this.draining = false;
|
|
30530
31333
|
this.dispatchState.mainDispatching = false;
|
|
30531
31334
|
this.dispatchState.mainCurrentTargetId = void 0;
|
|
31335
|
+
this.mainCurrentGroupKey = void 0;
|
|
30532
31336
|
this.dispatchState.mainPreDispatchBranchPoint = void 0;
|
|
30533
31337
|
if (!this.shuttingDown && this.dispatchState.mainBuffer.length > 0) {
|
|
30534
31338
|
setTimeout(() => {
|
|
@@ -30548,13 +31352,38 @@ var ParallAgentGateway = class {
|
|
|
30548
31352
|
const forkPrefix = buildForkResultPrefix(pendingFork);
|
|
30549
31353
|
this.dispatchState.mainDispatching = true;
|
|
30550
31354
|
this.dispatchState.mainCurrentTargetId = event.targetId;
|
|
31355
|
+
this.mainCurrentGroupKey = this.dispatchGroupKey(event);
|
|
30551
31356
|
this.dispatchState.mainPreDispatchBranchPoint = this.opts.dispatchAdapter.getBranchPoint?.(this.opts.runtimeKey);
|
|
31357
|
+
if (this.usesLaneLedger(event)) {
|
|
31358
|
+
let outcome = "shutdown";
|
|
31359
|
+
try {
|
|
31360
|
+
try {
|
|
31361
|
+
outcome = await this.dispatchLaneGroup({
|
|
31362
|
+
events: [event],
|
|
31363
|
+
sessionKey: this.opts.runtimeKey,
|
|
31364
|
+
body: forkPrefix + buildEventBody(event),
|
|
31365
|
+
earlier: [],
|
|
31366
|
+
hasMoreLocal: () => this.dispatchState.mainBuffer.some((e) => this.dispatchGroupKey(e) === this.dispatchGroupKey(event))
|
|
31367
|
+
});
|
|
31368
|
+
} catch (err) {
|
|
31369
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31370
|
+
throw err;
|
|
31371
|
+
}
|
|
31372
|
+
if (outcome !== "dispatched") {
|
|
31373
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31374
|
+
}
|
|
31375
|
+
} finally {
|
|
31376
|
+
await this.drainMainBuffer();
|
|
31377
|
+
}
|
|
31378
|
+
return outcome === "dispatched";
|
|
31379
|
+
}
|
|
30552
31380
|
try {
|
|
30553
31381
|
await this.emitDispatchReceived(event);
|
|
30554
31382
|
} catch (err) {
|
|
30555
31383
|
this.opts.log?.warn?.(`mark-received failed, leaving unacked for retry: ${String(err)}`);
|
|
30556
31384
|
this.dispatchState.mainDispatching = false;
|
|
30557
31385
|
this.dispatchState.mainCurrentTargetId = void 0;
|
|
31386
|
+
this.mainCurrentGroupKey = void 0;
|
|
30558
31387
|
this.dispatchState.mainPreDispatchBranchPoint = void 0;
|
|
30559
31388
|
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
30560
31389
|
return false;
|
|
@@ -30575,7 +31404,11 @@ var ParallAgentGateway = class {
|
|
|
30575
31404
|
return false;
|
|
30576
31405
|
}
|
|
30577
31406
|
this.dispatchState.mainBuffer.push(event);
|
|
30578
|
-
if (this.
|
|
31407
|
+
if (this.usesLaneLedger(event)) {
|
|
31408
|
+
if (this.mainCurrentGroupKey === this.dispatchGroupKey(event) && await this.laneLedger?.steerLive(event) && await this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))) {
|
|
31409
|
+
this.opts.log?.info(`steer folded+injected for ${event.messageId} (will drain for bookkeeping)`);
|
|
31410
|
+
}
|
|
31411
|
+
} else if (this.dispatchState.mainCurrentTargetId === event.targetId && await this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))) {
|
|
30579
31412
|
this.opts.log?.info(`steer injected for ${event.messageId} (will drain for bookkeeping)`);
|
|
30580
31413
|
}
|
|
30581
31414
|
if (!this.dispatchState.mainDispatching && !this.draining && this.dispatchState.mainBuffer.length > 0) {
|
|
@@ -30746,8 +31579,10 @@ var ParallAgentGateway = class {
|
|
|
30746
31579
|
try {
|
|
30747
31580
|
const dispatched = await this.handleInboundEvent(event);
|
|
30748
31581
|
if (dispatched) {
|
|
30749
|
-
|
|
30750
|
-
|
|
31582
|
+
if (!this.usesLaneLedger(event)) {
|
|
31583
|
+
this.opts.client.ackDispatch(this.opts.config.org_id, { source_type: "message", source_id: data.id }).catch(() => {
|
|
31584
|
+
});
|
|
31585
|
+
}
|
|
30751
31586
|
} else {
|
|
30752
31587
|
this.dispatchedMessages.delete(data.id);
|
|
30753
31588
|
}
|
|
@@ -30756,7 +31591,21 @@ var ParallAgentGateway = class {
|
|
|
30756
31591
|
this.dispatchedMessages.delete(data.id);
|
|
30757
31592
|
}
|
|
30758
31593
|
}
|
|
30759
|
-
|
|
31594
|
+
// Ledger re-drive consumption: dispatch.new message hints re-enter the
|
|
31595
|
+
// shared WorkItem consumption path (same protocol as catch-up).
|
|
31596
|
+
async handleMessageRedrive(item) {
|
|
31597
|
+
if (!item.chat_id || !item.source_id)
|
|
31598
|
+
return;
|
|
31599
|
+
await this.consumeMessageWorkItem({
|
|
31600
|
+
id: item.id,
|
|
31601
|
+
source_id: item.source_id,
|
|
31602
|
+
chat_id: item.chat_id
|
|
31603
|
+
});
|
|
31604
|
+
}
|
|
31605
|
+
consumeMessageWorkItem(item) {
|
|
31606
|
+
return consumeMessageWorkItem(this.laneFlowHost(), item);
|
|
31607
|
+
}
|
|
31608
|
+
async handleTaskAssignment(task, ackSourceId, dispatchEventId) {
|
|
30760
31609
|
if (this.shuttingDown)
|
|
30761
31610
|
return false;
|
|
30762
31611
|
const dedupeKey = `${task.id}:${task.updated_at}`;
|
|
@@ -30785,7 +31634,8 @@ var ParallAgentGateway = class {
|
|
|
30785
31634
|
body: parts.join("\n"),
|
|
30786
31635
|
sentAt: task.updated_at ?? task.created_at,
|
|
30787
31636
|
ackSourceType: "task_activity",
|
|
30788
|
-
ackSourceId
|
|
31637
|
+
ackSourceId,
|
|
31638
|
+
dispatchEventId
|
|
30789
31639
|
};
|
|
30790
31640
|
const dispatched = await this.handleInboundEvent(event);
|
|
30791
31641
|
if (!dispatched) {
|
|
@@ -30809,7 +31659,7 @@ var ParallAgentGateway = class {
|
|
|
30809
31659
|
this.opts.log?.info(`skipping stale task dispatch ${ackSourceId ?? taskId} \u2014 assigned to ${task.assignee_id}, creator ${task.creator_id}`);
|
|
30810
31660
|
return true;
|
|
30811
31661
|
}
|
|
30812
|
-
return this.handleTaskAssignment(task, ackSourceId);
|
|
31662
|
+
return this.handleTaskAssignment(task, ackSourceId, opts.dispatchEventId);
|
|
30813
31663
|
}
|
|
30814
31664
|
async handleTaskComment(commentId, taskId, actorId, deliveryReason) {
|
|
30815
31665
|
if (this.shuttingDown)
|
|
@@ -31020,6 +31870,75 @@ var ParallAgentGateway = class {
|
|
|
31020
31870
|
return true;
|
|
31021
31871
|
return this.handleExternalTriggerRun(run);
|
|
31022
31872
|
}
|
|
31873
|
+
// fetchAndHandleChannelMessage resolves a channel_message dispatch to its
|
|
31874
|
+
// durable ChannelMessage + conversation and hands it to the inbound
|
|
31875
|
+
// pipeline. targetId = the ChannelConversation id, so per-conversation
|
|
31876
|
+
// multi-turn continuity rides the same per-target session mechanics as
|
|
31877
|
+
// chats. Design: docs/engineering-design/external-im-channel-design.md.
|
|
31878
|
+
async fetchAndHandleChannelMessage(messageId) {
|
|
31879
|
+
if (this.shuttingDown)
|
|
31880
|
+
return false;
|
|
31881
|
+
const claimKey = `channel_message:${messageId}`;
|
|
31882
|
+
if (!this.tryClaimMessage(claimKey))
|
|
31883
|
+
return false;
|
|
31884
|
+
let msg = null;
|
|
31885
|
+
let conv = null;
|
|
31886
|
+
try {
|
|
31887
|
+
msg = await this.opts.client.getChannelMessage(this.opts.config.org_id, messageId);
|
|
31888
|
+
conv = await this.opts.client.getChannelConversation(this.opts.config.org_id, msg.conversation_id);
|
|
31889
|
+
} catch (err) {
|
|
31890
|
+
const status = err?.status;
|
|
31891
|
+
if (status === 404) {
|
|
31892
|
+
this.opts.log?.warn(`channel message ${messageId} not accessible (404), acking stale dispatch`);
|
|
31893
|
+
return true;
|
|
31894
|
+
}
|
|
31895
|
+
this.dispatchedMessages.delete(claimKey);
|
|
31896
|
+
this.opts.log?.warn(`channel message fetch failed for ${messageId}, leaving pending: ${String(err)}`);
|
|
31897
|
+
return false;
|
|
31898
|
+
}
|
|
31899
|
+
if (!msg || !conv) {
|
|
31900
|
+
return true;
|
|
31901
|
+
}
|
|
31902
|
+
this.opts.log?.info(`channel message: ${msg.id} (conversation ${conv.id})`);
|
|
31903
|
+
let provider = this.channelConnectionProviders.get(conv.connection_id);
|
|
31904
|
+
if (!provider) {
|
|
31905
|
+
try {
|
|
31906
|
+
const connection = await this.opts.client.getChannelConnection(this.opts.config.org_id, conv.connection_id);
|
|
31907
|
+
provider = connection.provider;
|
|
31908
|
+
this.channelConnectionProviders.set(conv.connection_id, provider);
|
|
31909
|
+
} catch {
|
|
31910
|
+
provider = void 0;
|
|
31911
|
+
}
|
|
31912
|
+
}
|
|
31913
|
+
const event = {
|
|
31914
|
+
type: "channel_message",
|
|
31915
|
+
targetId: conv.id,
|
|
31916
|
+
targetName: conv.external_user_name || conv.external_conversation_id,
|
|
31917
|
+
targetType: "channel_conversation",
|
|
31918
|
+
senderId: msg.external_user_id || "external",
|
|
31919
|
+
senderName: msg.external_user_name || msg.external_user_id || "external user",
|
|
31920
|
+
messageId: msg.id,
|
|
31921
|
+
body: msg.text,
|
|
31922
|
+
sentAt: msg.received_at,
|
|
31923
|
+
channelProvider: provider,
|
|
31924
|
+
channelConversationType: conv.conversation_type || void 0,
|
|
31925
|
+
channelExternalConversationId: conv.external_conversation_id,
|
|
31926
|
+
channelExternalMessageId: msg.external_message_id,
|
|
31927
|
+
ackSourceType: "channel_message",
|
|
31928
|
+
ackSourceId: msg.id
|
|
31929
|
+
};
|
|
31930
|
+
let dispatched;
|
|
31931
|
+
try {
|
|
31932
|
+
dispatched = await this.handleInboundEvent(event);
|
|
31933
|
+
} catch (err) {
|
|
31934
|
+
this.dispatchedMessages.delete(claimKey);
|
|
31935
|
+
throw err;
|
|
31936
|
+
}
|
|
31937
|
+
if (!dispatched) {
|
|
31938
|
+
this.dispatchedMessages.delete(claimKey);
|
|
31939
|
+
}
|
|
31940
|
+
return dispatched;
|
|
31941
|
+
}
|
|
31023
31942
|
async handleExternalTriggerRun(run) {
|
|
31024
31943
|
if (this.shuttingDown)
|
|
31025
31944
|
return false;
|
|
@@ -31148,72 +32067,46 @@ var ParallAgentGateway = class {
|
|
|
31148
32067
|
}
|
|
31149
32068
|
processed++;
|
|
31150
32069
|
try {
|
|
31151
|
-
|
|
32070
|
+
const ackItem = () => {
|
|
32071
|
+
this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => {
|
|
32072
|
+
});
|
|
32073
|
+
};
|
|
31152
32074
|
if (item.event_type === "task_assign" && item.task_id) {
|
|
31153
32075
|
try {
|
|
31154
|
-
|
|
32076
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
|
|
32077
|
+
dispatchEventId
|
|
32078
|
+
}), ackItem);
|
|
31155
32079
|
} catch (err) {
|
|
31156
32080
|
this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
|
|
31157
32081
|
continue;
|
|
31158
32082
|
}
|
|
31159
32083
|
} else if (item.event_type === "task_update" && item.task_id) {
|
|
31160
32084
|
try {
|
|
31161
|
-
|
|
32085
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
|
|
32086
|
+
allowCreator: true,
|
|
32087
|
+
dispatchEventId
|
|
32088
|
+
}), ackItem);
|
|
31162
32089
|
} catch (err) {
|
|
31163
32090
|
this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
|
|
31164
32091
|
continue;
|
|
31165
32092
|
}
|
|
31166
32093
|
} else if (item.event_type === "task_comment" && item.source_id && item.task_id) {
|
|
31167
|
-
|
|
32094
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.handleTaskComment(item.source_id, item.task_id ?? "", item.actor_id, item.delivery_reason), ackItem);
|
|
31168
32095
|
} else if (item.event_type === "wiki_comment" && item.source_id) {
|
|
31169
|
-
|
|
32096
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.handleWikiComment(item.source_id, item.actor_id, item.delivery_reason), ackItem);
|
|
31170
32097
|
} else if (item.event_type === "schedule.fire" && item.source_id) {
|
|
31171
|
-
|
|
32098
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleScheduleFire(item.source_id, item.actor_id), ackItem);
|
|
31172
32099
|
} else if (item.event_type === "external_trigger" && item.source_id) {
|
|
31173
|
-
|
|
32100
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleExternalTriggerRun(item.source_id), ackItem);
|
|
32101
|
+
} else if (item.event_type === "channel_message" && item.source_id) {
|
|
32102
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleChannelMessage(item.source_id), ackItem);
|
|
31174
32103
|
} else if (item.event_type === "approval_decided" && item.source_id) {
|
|
31175
|
-
|
|
32104
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleApprovalDecided(item.source_id, item.actor_id, item.chat_id ?? null), ackItem);
|
|
31176
32105
|
} else if (item.event_type === "message" && item.source_id && item.chat_id) {
|
|
31177
|
-
|
|
31178
|
-
|
|
31179
|
-
|
|
31180
|
-
|
|
31181
|
-
try {
|
|
31182
|
-
msg = await this.opts.client.getMessage(item.source_id);
|
|
31183
|
-
} catch (err) {
|
|
31184
|
-
const status = err?.status;
|
|
31185
|
-
if (status === 404) {
|
|
31186
|
-
msg = null;
|
|
31187
|
-
} else {
|
|
31188
|
-
msgFetchFailed = true;
|
|
31189
|
-
this.opts.log?.warn(`catch-up message fetch failed for ${item.source_id}, leaving pending: ${String(err)}`);
|
|
31190
|
-
}
|
|
31191
|
-
}
|
|
31192
|
-
if (msgFetchFailed) {
|
|
31193
|
-
this.dispatchedMessages.delete(item.source_id);
|
|
31194
|
-
continue;
|
|
31195
|
-
}
|
|
31196
|
-
if (!msg || msg.sender_id === this.opts.agentUserId) {
|
|
31197
|
-
this.dispatchedMessages.delete(item.source_id);
|
|
31198
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => {
|
|
31199
|
-
});
|
|
31200
|
-
continue;
|
|
31201
|
-
}
|
|
31202
|
-
const decision = await this.buildMessageDispatchDecision(item.chat_id, msg);
|
|
31203
|
-
if (decision.action === "retry") {
|
|
31204
|
-
this.dispatchedMessages.delete(item.source_id);
|
|
31205
|
-
continue;
|
|
31206
|
-
}
|
|
31207
|
-
if (decision.action === "skip") {
|
|
31208
|
-
this.dispatchedMessages.delete(item.source_id);
|
|
31209
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => {
|
|
31210
|
-
});
|
|
31211
|
-
continue;
|
|
31212
|
-
}
|
|
31213
|
-
dispatched = await this.handleInboundEvent(decision.event);
|
|
31214
|
-
}
|
|
31215
|
-
if (dispatched) {
|
|
31216
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => {
|
|
32106
|
+
await this.consumeMessageWorkItem({
|
|
32107
|
+
id: item.id,
|
|
32108
|
+
source_id: item.source_id,
|
|
32109
|
+
chat_id: item.chat_id
|
|
31217
32110
|
});
|
|
31218
32111
|
}
|
|
31219
32112
|
} catch (err) {
|
|
@@ -31279,6 +32172,14 @@ ${fullSummary}` : fullSummary;
|
|
|
31279
32172
|
this.abortFork(targetId, "ws reconnect");
|
|
31280
32173
|
}
|
|
31281
32174
|
}
|
|
32175
|
+
if (this.laneLedger && this.inFlightDispatches === 0 && this.laneLedger.activeCount > 0) {
|
|
32176
|
+
log2?.info(`releasing ${this.laneLedger.activeCount} stale lane(s) on reconnect`);
|
|
32177
|
+
await this.laneLedger.releaseAll();
|
|
32178
|
+
}
|
|
32179
|
+
if (this.laneLedger && this.ledgerDisabled) {
|
|
32180
|
+
log2?.info("re-probing dispatch ledger after reconnect (was disabled)");
|
|
32181
|
+
this.ledgerDisabled = false;
|
|
32182
|
+
}
|
|
31282
32183
|
const intervalSec = data.heartbeat_interval > 0 ? data.heartbeat_interval : 30;
|
|
31283
32184
|
try {
|
|
31284
32185
|
const count = await fetchAllChats(client, config.org_id, this.chatInfoMap);
|
|
@@ -31349,6 +32250,10 @@ ${fullSummary}` : fullSummary;
|
|
|
31349
32250
|
}
|
|
31350
32251
|
if (this.heartbeatTimer)
|
|
31351
32252
|
clearInterval(this.heartbeatTimer);
|
|
32253
|
+
if (this.laneLedger && this.laneLedger.activeCount > 0) {
|
|
32254
|
+
this.opts.log?.info(`releasing ${this.laneLedger.activeCount} lane(s) on shutdown`);
|
|
32255
|
+
await this.laneLedger.releaseAll();
|
|
32256
|
+
}
|
|
31352
32257
|
await this.opts.onBeforeDisconnect?.();
|
|
31353
32258
|
this.opts.ws.disconnect();
|
|
31354
32259
|
this.opts.log?.info(`disconnected`);
|
|
@@ -31356,8 +32261,8 @@ ${fullSummary}` : fullSummary;
|
|
|
31356
32261
|
};
|
|
31357
32262
|
|
|
31358
32263
|
// ts/agent-core/dist/platform-config.js
|
|
31359
|
-
import * as
|
|
31360
|
-
import * as
|
|
32264
|
+
import * as fs3 from "node:fs";
|
|
32265
|
+
import * as path3 from "node:path";
|
|
31361
32266
|
function isPlatformManagedProfile(profile) {
|
|
31362
32267
|
return profile?.machine_id != null || profile?.model_management === "platform";
|
|
31363
32268
|
}
|
|
@@ -31368,14 +32273,17 @@ function resolveRuntimeModel(isOperatorOverride, platformModel, configModel) {
|
|
|
31368
32273
|
const operatorOverride = isOperatorOverride ? platformModel : null;
|
|
31369
32274
|
return operatorOverride ?? configModel ?? platformModel ?? void 0;
|
|
31370
32275
|
}
|
|
32276
|
+
function deriveModelIsPin(defaults, profile) {
|
|
32277
|
+
return defaults.modelIsPin !== void 0 ? defaults.modelIsPin : isPlatformModelOverride(profile);
|
|
32278
|
+
}
|
|
31371
32279
|
var CACHE_FILENAME = "parall-platform-config.json";
|
|
31372
32280
|
var SUPPORTED_SCHEMA_VERSION = 1;
|
|
31373
32281
|
function cachePath(stateDir) {
|
|
31374
|
-
return
|
|
32282
|
+
return path3.join(stateDir, CACHE_FILENAME);
|
|
31375
32283
|
}
|
|
31376
32284
|
function loadCache(stateDir) {
|
|
31377
32285
|
try {
|
|
31378
|
-
const raw =
|
|
32286
|
+
const raw = fs3.readFileSync(cachePath(stateDir), "utf-8");
|
|
31379
32287
|
return JSON.parse(raw);
|
|
31380
32288
|
} catch {
|
|
31381
32289
|
return null;
|
|
@@ -31385,33 +32293,38 @@ function saveCache(stateDir, response) {
|
|
|
31385
32293
|
const cached = {
|
|
31386
32294
|
version: response.version,
|
|
31387
32295
|
config: response.config,
|
|
32296
|
+
modelIsPin: response.model_is_pin,
|
|
31388
32297
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
31389
32298
|
};
|
|
31390
32299
|
const filePath = cachePath(stateDir);
|
|
31391
32300
|
const tmpPath = `${filePath}.tmp`;
|
|
31392
|
-
|
|
31393
|
-
|
|
31394
|
-
|
|
32301
|
+
fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
|
|
32302
|
+
fs3.writeFileSync(tmpPath, JSON.stringify(cached, null, 2), "utf-8");
|
|
32303
|
+
fs3.renameSync(tmpPath, filePath);
|
|
31395
32304
|
}
|
|
31396
32305
|
function runtimeModelName(canonicalModel, runtimeType, config) {
|
|
31397
32306
|
const runtime = runtimeType?.trim();
|
|
31398
|
-
if (!runtime || runtime === "openclaw")
|
|
32307
|
+
if (!runtime || runtime === "openclaw" || runtime === "hermes")
|
|
31399
32308
|
return canonicalModel;
|
|
31400
32309
|
const models = config.models ?? {};
|
|
31401
32310
|
const providers = models.providers ?? {};
|
|
31402
32311
|
const parall = providers.parall ?? {};
|
|
31403
32312
|
const catalog = Array.isArray(parall.models) ? parall.models : [];
|
|
31404
32313
|
const match = catalog.find((model) => model.id === canonicalModel);
|
|
31405
|
-
|
|
32314
|
+
if (!match)
|
|
32315
|
+
return null;
|
|
32316
|
+
const runtimeNames = match.runtime_names ?? {};
|
|
31406
32317
|
const runtimeName = runtimeNames[runtime];
|
|
31407
|
-
|
|
32318
|
+
if (typeof runtimeName === "string" && runtimeName)
|
|
32319
|
+
return runtimeName;
|
|
32320
|
+
return canonicalModel;
|
|
31408
32321
|
}
|
|
31409
32322
|
function extractDefaults(config, runtimeType) {
|
|
31410
32323
|
const agents = config.agents ?? {};
|
|
31411
32324
|
const defaults = agents.defaults ?? {};
|
|
31412
32325
|
let model = null;
|
|
31413
32326
|
if (typeof defaults.model === "string" && defaults.model) {
|
|
31414
|
-
const canonicalModel = defaults.model.replace(/^parall\//, "");
|
|
32327
|
+
const canonicalModel = defaults.model.replace(/^parall-anthropic\//, "").replace(/^parall\//, "");
|
|
31415
32328
|
model = runtimeModelName(canonicalModel, runtimeType, config);
|
|
31416
32329
|
}
|
|
31417
32330
|
let thinkingEffort = null;
|
|
@@ -31423,13 +32336,20 @@ function extractDefaults(config, runtimeType) {
|
|
|
31423
32336
|
function createPlatformConfigManager(opts) {
|
|
31424
32337
|
const { client, stateDir, runtimeType, log: log2 } = opts;
|
|
31425
32338
|
let cachedVersion;
|
|
31426
|
-
let currentDefaults = {
|
|
32339
|
+
let currentDefaults = {
|
|
32340
|
+
model: null,
|
|
32341
|
+
thinkingEffort: null,
|
|
32342
|
+
modelIsPin: void 0
|
|
32343
|
+
};
|
|
31427
32344
|
let currentRawConfig = null;
|
|
31428
32345
|
const cached = loadCache(stateDir);
|
|
31429
32346
|
if (cached) {
|
|
31430
|
-
cachedVersion = cached.version;
|
|
32347
|
+
cachedVersion = cached.modelIsPin === void 0 ? void 0 : cached.version;
|
|
31431
32348
|
currentRawConfig = cached.config;
|
|
31432
|
-
currentDefaults =
|
|
32349
|
+
currentDefaults = {
|
|
32350
|
+
...extractDefaults(cached.config, runtimeType),
|
|
32351
|
+
modelIsPin: cached.modelIsPin
|
|
32352
|
+
};
|
|
31433
32353
|
}
|
|
31434
32354
|
return {
|
|
31435
32355
|
async fetch() {
|
|
@@ -31455,7 +32375,10 @@ function createPlatformConfigManager(opts) {
|
|
|
31455
32375
|
saveCache(stateDir, fresh);
|
|
31456
32376
|
cachedVersion = fresh.version;
|
|
31457
32377
|
currentRawConfig = fresh.config;
|
|
31458
|
-
currentDefaults =
|
|
32378
|
+
currentDefaults = {
|
|
32379
|
+
...extractDefaults(fresh.config, runtimeType),
|
|
32380
|
+
modelIsPin: fresh.model_is_pin
|
|
32381
|
+
};
|
|
31459
32382
|
return currentDefaults;
|
|
31460
32383
|
},
|
|
31461
32384
|
current() {
|
|
@@ -31468,8 +32391,8 @@ function createPlatformConfigManager(opts) {
|
|
|
31468
32391
|
}
|
|
31469
32392
|
|
|
31470
32393
|
// ts/agent-core/dist/skills/index.js
|
|
31471
|
-
import * as
|
|
31472
|
-
import * as
|
|
32394
|
+
import * as fs4 from "node:fs";
|
|
32395
|
+
import * as path4 from "node:path";
|
|
31473
32396
|
|
|
31474
32397
|
// ts/agent-core/dist/skills/parall-platform.js
|
|
31475
32398
|
var PARALL_PLATFORM_SKILL = `# Parall Platform
|
|
@@ -31572,16 +32495,35 @@ original discussion and its approval/rejection IS the precedent.
|
|
|
31572
32495
|
|
|
31573
32496
|
Each \`[Event: message.new]\` includes \`[Chat: ... (prll://cht_xxx)]\` \u2014 use that chat URI to reply.
|
|
31574
32497
|
|
|
32498
|
+
> **How you pass the message body matters \u2014 your command runs through a shell.**
|
|
32499
|
+
> Inside double quotes the shell expands \`$\`, backticks, and \`$(...)\` *before*
|
|
32500
|
+
> the CLI sees them: \`--text "That costs $1,000"\` sends \`That costs ,000\`, and
|
|
32501
|
+
> \`--text "$(cmd)"\` runs \`cmd\`. Single quotes instead break on apostrophes
|
|
32502
|
+
> (\`I'm\`, \`don't\`). So do **not** wrap real message content in quotes \u2014 pass it
|
|
32503
|
+
> through \`--text-file\` (a written file, or a quoted heredoc \`<<'EOF'\` that
|
|
32504
|
+
> disables all expansion). Reserve \`--text "..."\` for short literals with no
|
|
32505
|
+
> \`$\`, backtick, or apostrophe.
|
|
32506
|
+
|
|
31575
32507
|
\`\`\`bash
|
|
31576
|
-
#
|
|
31577
|
-
|
|
32508
|
+
# One-off reply \u2192 quoted heredoc into stdin. The quoted delimiter <<'EOF'
|
|
32509
|
+
# disables ALL shell expansion, so $, backticks and apostrophes pass verbatim.
|
|
32510
|
+
parall messages send prll://cht_xxx --text-file - <<'PARALL_EOF'
|
|
32511
|
+
Sure \u2014 that's $1,000, and $(whoami) stays literal. I'm on it.
|
|
32512
|
+
PARALL_EOF
|
|
32513
|
+
|
|
32514
|
+
# Longer / multi-line reply \u2192 write it with your file tool (no shell touches
|
|
32515
|
+
# the body), then point --text-file at the file.
|
|
32516
|
+
parall messages send prll://cht_xxx --text-file /tmp/reply.md
|
|
31578
32517
|
|
|
31579
|
-
#
|
|
31580
|
-
parall
|
|
32518
|
+
# Short literal with no $, backtick, or apostrophe \u2192 --text is fine.
|
|
32519
|
+
parall messages send prll://cht_xxx --text "On it"
|
|
32520
|
+
|
|
32521
|
+
# Direct message by user URI or display name (same --text-file / heredoc rules)
|
|
32522
|
+
parall dm prll://usr_xxx --text-file /tmp/reply.md
|
|
31581
32523
|
parall dm "Alice" --text "Hello"
|
|
31582
32524
|
|
|
31583
32525
|
# Thread reply
|
|
31584
|
-
parall messages send prll://cht_xxx --text
|
|
32526
|
+
parall messages send prll://cht_xxx --text-file /tmp/reply.md --thread-root-id 01JWC...
|
|
31585
32527
|
|
|
31586
32528
|
# FYI message (no response expected \u2014 the recipient sees \`[Hint: no_reply]\`)
|
|
31587
32529
|
parall messages send prll://cht_xxx --text "FYI: done" --no-reply
|
|
@@ -31613,7 +32555,9 @@ parall messages send prll://cht_xxx --attachment att_xxx --text "See attached"
|
|
|
31613
32555
|
parall dm "Alice" --file /tmp/report.pdf --text "Report attached"
|
|
31614
32556
|
\`\`\`
|
|
31615
32557
|
|
|
31616
|
-
\`--file\` and \`--attachment\` are mutually exclusive. \`--text\`
|
|
32558
|
+
\`--file\` and \`--attachment\` are mutually exclusive. A caption (\`--text\` for
|
|
32559
|
+
short literals, or \`--text-file\` for anything with \`$\`, backticks, or quotes)
|
|
32560
|
+
can be combined with either.
|
|
31617
32561
|
|
|
31618
32562
|
## Approvals
|
|
31619
32563
|
|
|
@@ -32221,13 +33165,13 @@ var SKILLS = [
|
|
|
32221
33165
|
}
|
|
32222
33166
|
];
|
|
32223
33167
|
function writeSkillFiles(targetDir) {
|
|
32224
|
-
|
|
33168
|
+
fs4.mkdirSync(targetDir, { recursive: true });
|
|
32225
33169
|
for (const skill of SKILLS) {
|
|
32226
|
-
|
|
33170
|
+
fs4.writeFileSync(path4.join(targetDir, `${skill.name}.md`), skill.content, "utf8");
|
|
32227
33171
|
}
|
|
32228
33172
|
}
|
|
32229
33173
|
function buildSkillReferences(workspaceDir) {
|
|
32230
|
-
const dir =
|
|
33174
|
+
const dir = path4.join(workspaceDir, ".parall", "skills");
|
|
32231
33175
|
const lines = SKILLS.map((s) => `- ${s.description.split(":")[0]}: \`${dir}/${s.name}.md\``);
|
|
32232
33176
|
return `## Platform Skills (read on demand)
|
|
32233
33177
|
|
|
@@ -32237,7 +33181,7 @@ ${lines.join("\n")}
|
|
|
32237
33181
|
|
|
32238
33182
|
// ts/codex-agent/dist/config.js
|
|
32239
33183
|
import * as os2 from "node:os";
|
|
32240
|
-
import * as
|
|
33184
|
+
import * as path5 from "node:path";
|
|
32241
33185
|
function requireEnv(env, name) {
|
|
32242
33186
|
const value = env[name]?.trim();
|
|
32243
33187
|
if (!value) {
|
|
@@ -32246,15 +33190,15 @@ function requireEnv(env, name) {
|
|
|
32246
33190
|
return value;
|
|
32247
33191
|
}
|
|
32248
33192
|
function resolvePath(value) {
|
|
32249
|
-
return
|
|
33193
|
+
return path5.isAbsolute(value) ? value : path5.resolve(process.cwd(), value);
|
|
32250
33194
|
}
|
|
32251
33195
|
function resolveCodexAgentConfig(env = process.env) {
|
|
32252
33196
|
const apiUrl = requireEnv(env, "PRLL_API_URL");
|
|
32253
33197
|
const apiKey = requireEnv(env, "PRLL_API_KEY");
|
|
32254
33198
|
const orgId = requireEnv(env, "PRLL_ORG_ID");
|
|
32255
|
-
const codexHome = resolvePath(env.PRLL_CODEX_HOME?.trim() || env.CODEX_HOME?.trim() ||
|
|
32256
|
-
const stateDir = resolvePath(env.PRLL_STATE_DIR?.trim() ||
|
|
32257
|
-
const workspaceDir = resolvePath(env.PRLL_WORKSPACE_DIR?.trim() ||
|
|
33199
|
+
const codexHome = resolvePath(env.PRLL_CODEX_HOME?.trim() || env.CODEX_HOME?.trim() || path5.join(env.HOME || os2.homedir(), ".codex"));
|
|
33200
|
+
const stateDir = resolvePath(env.PRLL_STATE_DIR?.trim() || path5.join(env.HOME || os2.homedir(), ".parall-agent"));
|
|
33201
|
+
const workspaceDir = resolvePath(env.PRLL_WORKSPACE_DIR?.trim() || path5.join(stateDir, "workspace"));
|
|
32258
33202
|
return {
|
|
32259
33203
|
apiUrl,
|
|
32260
33204
|
apiKey,
|
|
@@ -32308,28 +33252,31 @@ function buildCodexRuntimeKey(agentUserId) {
|
|
|
32308
33252
|
}
|
|
32309
33253
|
function sessionStateFilePathForRuntime(stateDir, runtimeKey) {
|
|
32310
33254
|
const fileName = Buffer.from(runtimeKey).toString("base64url");
|
|
32311
|
-
return
|
|
33255
|
+
return path5.join(stateDir, "threads", `${fileName}.json`);
|
|
32312
33256
|
}
|
|
32313
33257
|
function contextFilePathForSession(stateDir, sessionKey) {
|
|
32314
33258
|
const fileName = Buffer.from(sessionKey).toString("base64url");
|
|
32315
|
-
return
|
|
33259
|
+
return path5.join(stateDir, "dispatch-context", `${fileName}.json`);
|
|
33260
|
+
}
|
|
33261
|
+
function dispatchContextDirPath(stateDir) {
|
|
33262
|
+
return dispatchLaneContextDir(stateDir);
|
|
32316
33263
|
}
|
|
32317
33264
|
function stepIdFilePathForSession(stateDir, sessionKey) {
|
|
32318
33265
|
const fileName = Buffer.from(sessionKey).toString("base64url");
|
|
32319
|
-
return
|
|
33266
|
+
return path5.join(stateDir, "step-ids", `${fileName}.txt`);
|
|
32320
33267
|
}
|
|
32321
33268
|
|
|
32322
33269
|
// ts/codex-agent/dist/dispatch.js
|
|
32323
33270
|
import { execSync as execSync2, spawn } from "node:child_process";
|
|
32324
33271
|
import { randomUUID } from "node:crypto";
|
|
32325
|
-
import * as
|
|
33272
|
+
import * as fs6 from "node:fs";
|
|
32326
33273
|
|
|
32327
33274
|
// ts/agent-core/dist/internal/attachment-input.js
|
|
32328
33275
|
import { execSync } from "node:child_process";
|
|
32329
33276
|
import { constants } from "node:fs";
|
|
32330
33277
|
import * as fsSync from "node:fs";
|
|
32331
|
-
import * as
|
|
32332
|
-
import * as
|
|
33278
|
+
import * as fs5 from "node:fs/promises";
|
|
33279
|
+
import * as path6 from "node:path";
|
|
32333
33280
|
var DEFAULT_MAX_TOTAL_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
32334
33281
|
var DEFAULT_ATTACHMENT_CACHE_MAX_BYTES = 512 * 1024 * 1024;
|
|
32335
33282
|
var DEFAULT_ATTACHMENT_DOWNLOAD_TIMEOUT_MS = 3e4;
|
|
@@ -32359,11 +33306,11 @@ async function prepareLocalImageAttachments(event, context2, opts) {
|
|
|
32359
33306
|
};
|
|
32360
33307
|
}
|
|
32361
33308
|
const rootDir = await ensureAttachmentRootDir(opts.workspaceDir);
|
|
32362
|
-
const messageDir =
|
|
33309
|
+
const messageDir = path6.join(rootDir, sanitizePathSegment(event.messageId));
|
|
32363
33310
|
await ensurePathIsNotSymlink(messageDir);
|
|
32364
|
-
await
|
|
33311
|
+
await fs5.mkdir(messageDir, { recursive: true });
|
|
32365
33312
|
await ensurePathIsNotSymlink(messageDir);
|
|
32366
|
-
const activeMessageDir =
|
|
33313
|
+
const activeMessageDir = path6.resolve(messageDir);
|
|
32367
33314
|
activeAttachmentDirs.add(activeMessageDir);
|
|
32368
33315
|
const maintenanceCooldownMs = opts.maintenanceCooldownMs ?? DEFAULT_MAINTENANCE_COOLDOWN_MS;
|
|
32369
33316
|
const maintenancePromise = scheduleAttachmentMaintenance(rootDir, {
|
|
@@ -32380,7 +33327,7 @@ async function prepareLocalImageAttachments(event, context2, opts) {
|
|
|
32380
33327
|
const notes = [];
|
|
32381
33328
|
let downloadedBytes = 0;
|
|
32382
33329
|
for (const att of imageAttachments) {
|
|
32383
|
-
const localPath =
|
|
33330
|
+
const localPath = path6.join(messageDir, localFileName(att.id, att.fileName, att.mimeType));
|
|
32384
33331
|
const downloadTimeoutMs = opts.downloadTimeoutMs ?? DEFAULT_ATTACHMENT_DOWNLOAD_TIMEOUT_MS;
|
|
32385
33332
|
const fetchFresh = async () => {
|
|
32386
33333
|
const fileInfo = await withTimeout(context2.client.getFileUrl(att.id), downloadTimeoutMs, `file URL lookup timed out after ${downloadTimeoutMs}ms`);
|
|
@@ -32437,7 +33384,7 @@ async function appendPreparedLocalAttachmentRefs(body, event, context2, opts) {
|
|
|
32437
33384
|
return { body: appendLocalAttachmentRefs(body, attachments), attachments };
|
|
32438
33385
|
}
|
|
32439
33386
|
function pinLocalAttachmentPaths(images) {
|
|
32440
|
-
const dirs = new Set(images.map((image) =>
|
|
33387
|
+
const dirs = new Set(images.map((image) => path6.resolve(path6.dirname(image.localPath))));
|
|
32441
33388
|
for (const dir of dirs) {
|
|
32442
33389
|
activeAttachmentDirs.add(dir);
|
|
32443
33390
|
}
|
|
@@ -32452,7 +33399,7 @@ function pinLocalAttachmentPaths(images) {
|
|
|
32452
33399
|
};
|
|
32453
33400
|
}
|
|
32454
33401
|
function attachmentRootDir(workspaceDir) {
|
|
32455
|
-
return
|
|
33402
|
+
return path6.join(path6.resolve(workspaceDir), ".parall", "attachments");
|
|
32456
33403
|
}
|
|
32457
33404
|
function ensureLocalAttachmentGitExclude(workingDirectory) {
|
|
32458
33405
|
try {
|
|
@@ -32461,8 +33408,8 @@ function ensureLocalAttachmentGitExclude(workingDirectory) {
|
|
|
32461
33408
|
encoding: "utf8",
|
|
32462
33409
|
stdio: ["ignore", "pipe", "ignore"]
|
|
32463
33410
|
}).trim();
|
|
32464
|
-
const excludePath =
|
|
32465
|
-
fsSync.mkdirSync(
|
|
33411
|
+
const excludePath = path6.isAbsolute(rel) ? rel : path6.join(workingDirectory, rel);
|
|
33412
|
+
fsSync.mkdirSync(path6.dirname(excludePath), { recursive: true });
|
|
32466
33413
|
const existing = fsSync.existsSync(excludePath) ? fsSync.readFileSync(excludePath, "utf8") : "";
|
|
32467
33414
|
if (existing.split(/\r?\n/).some((line) => line.trim() === ".parall/"))
|
|
32468
33415
|
return;
|
|
@@ -32498,18 +33445,18 @@ function scheduleAttachmentMaintenance(rootDir, opts) {
|
|
|
32498
33445
|
return run;
|
|
32499
33446
|
}
|
|
32500
33447
|
async function ensureAttachmentRootDir(workspaceDir) {
|
|
32501
|
-
const workspaceRoot =
|
|
32502
|
-
const parallDir =
|
|
33448
|
+
const workspaceRoot = path6.resolve(workspaceDir);
|
|
33449
|
+
const parallDir = path6.join(workspaceRoot, ".parall");
|
|
32503
33450
|
const rootDir = attachmentRootDir(workspaceRoot);
|
|
32504
|
-
await
|
|
33451
|
+
await fs5.mkdir(workspaceRoot, { recursive: true });
|
|
32505
33452
|
await ensurePathIsNotSymlink(parallDir);
|
|
32506
|
-
await
|
|
33453
|
+
await fs5.mkdir(parallDir, { recursive: true, mode: 448 });
|
|
32507
33454
|
await ensurePathIsNotSymlink(parallDir);
|
|
32508
33455
|
await ensurePathIsNotSymlink(rootDir);
|
|
32509
|
-
await
|
|
33456
|
+
await fs5.mkdir(rootDir, { recursive: true, mode: 448 });
|
|
32510
33457
|
await ensurePathIsNotSymlink(rootDir);
|
|
32511
|
-
const realWorkspace = await
|
|
32512
|
-
const realRoot = await
|
|
33458
|
+
const realWorkspace = await fs5.realpath(workspaceRoot);
|
|
33459
|
+
const realRoot = await fs5.realpath(rootDir);
|
|
32513
33460
|
if (!isPathInside(realRoot, realWorkspace)) {
|
|
32514
33461
|
throw new Error(`attachment root escapes workspace: ${rootDir}`);
|
|
32515
33462
|
}
|
|
@@ -32517,7 +33464,7 @@ async function ensureAttachmentRootDir(workspaceDir) {
|
|
|
32517
33464
|
}
|
|
32518
33465
|
async function ensurePathIsNotSymlink(filePath) {
|
|
32519
33466
|
try {
|
|
32520
|
-
const stat = await
|
|
33467
|
+
const stat = await fs5.lstat(filePath);
|
|
32521
33468
|
if (stat.isSymbolicLink()) {
|
|
32522
33469
|
throw new Error(`refusing to use symlinked attachment path ${filePath}`);
|
|
32523
33470
|
}
|
|
@@ -32528,8 +33475,8 @@ async function ensurePathIsNotSymlink(filePath) {
|
|
|
32528
33475
|
}
|
|
32529
33476
|
}
|
|
32530
33477
|
function isPathInside(childPath, parentPath) {
|
|
32531
|
-
const rel =
|
|
32532
|
-
return rel === "" || !!rel && !rel.startsWith("..") && !
|
|
33478
|
+
const rel = path6.relative(parentPath, childPath);
|
|
33479
|
+
return rel === "" || !!rel && !rel.startsWith("..") && !path6.isAbsolute(rel);
|
|
32533
33480
|
}
|
|
32534
33481
|
async function existingUsableFile(filePath, expectedSize, rootDir) {
|
|
32535
33482
|
try {
|
|
@@ -32540,30 +33487,30 @@ async function existingUsableFile(filePath, expectedSize, rootDir) {
|
|
|
32540
33487
|
}
|
|
32541
33488
|
}
|
|
32542
33489
|
async function localFileStatInsideRoot(filePath, rootDir) {
|
|
32543
|
-
const stat = await
|
|
33490
|
+
const stat = await fs5.lstat(filePath);
|
|
32544
33491
|
if (stat.isSymbolicLink()) {
|
|
32545
33492
|
throw new Error(`refusing to use symlinked attachment file ${filePath}`);
|
|
32546
33493
|
}
|
|
32547
33494
|
if (!stat.isFile()) {
|
|
32548
33495
|
throw new Error(`attachment path is not a file ${filePath}`);
|
|
32549
33496
|
}
|
|
32550
|
-
const realRoot = await
|
|
32551
|
-
const realFile = await
|
|
33497
|
+
const realRoot = await fs5.realpath(rootDir);
|
|
33498
|
+
const realFile = await fs5.realpath(filePath);
|
|
32552
33499
|
if (!isPathInside(realFile, realRoot)) {
|
|
32553
33500
|
throw new Error(`attachment file escapes workspace: ${filePath}`);
|
|
32554
33501
|
}
|
|
32555
33502
|
return stat;
|
|
32556
33503
|
}
|
|
32557
33504
|
async function localDirectoryStatInsideRoot(dirPath, rootDir) {
|
|
32558
|
-
const stat = await
|
|
33505
|
+
const stat = await fs5.lstat(dirPath);
|
|
32559
33506
|
if (stat.isSymbolicLink()) {
|
|
32560
33507
|
throw new Error(`refusing to use symlinked attachment directory ${dirPath}`);
|
|
32561
33508
|
}
|
|
32562
33509
|
if (!stat.isDirectory()) {
|
|
32563
33510
|
throw new Error(`attachment path is not a directory ${dirPath}`);
|
|
32564
33511
|
}
|
|
32565
|
-
const realRoot = await
|
|
32566
|
-
const realDir = await
|
|
33512
|
+
const realRoot = await fs5.realpath(rootDir);
|
|
33513
|
+
const realDir = await fs5.realpath(dirPath);
|
|
32567
33514
|
if (!isPathInside(realDir, realRoot)) {
|
|
32568
33515
|
throw new Error(`attachment directory escapes workspace: ${dirPath}`);
|
|
32569
33516
|
}
|
|
@@ -32571,7 +33518,7 @@ async function localDirectoryStatInsideRoot(dirPath, rootDir) {
|
|
|
32571
33518
|
}
|
|
32572
33519
|
async function openLocalFileInsideRoot(filePath, rootDir) {
|
|
32573
33520
|
const checkedStat = await localFileStatInsideRoot(filePath, rootDir);
|
|
32574
|
-
const file = await
|
|
33521
|
+
const file = await fs5.open(filePath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
32575
33522
|
let keepOpen = false;
|
|
32576
33523
|
try {
|
|
32577
33524
|
const openedStat = await file.stat();
|
|
@@ -32587,8 +33534,8 @@ async function openLocalFileInsideRoot(filePath, rootDir) {
|
|
|
32587
33534
|
}
|
|
32588
33535
|
}
|
|
32589
33536
|
async function openLocalTempFileInsideRoot(filePath, rootDir) {
|
|
32590
|
-
await localDirectoryStatInsideRoot(
|
|
32591
|
-
const file = await
|
|
33537
|
+
await localDirectoryStatInsideRoot(path6.dirname(filePath), rootDir);
|
|
33538
|
+
const file = await fs5.open(filePath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 384);
|
|
32592
33539
|
let keepOpen = false;
|
|
32593
33540
|
try {
|
|
32594
33541
|
const checkedStat = await localFileStatInsideRoot(filePath, rootDir);
|
|
@@ -32613,7 +33560,7 @@ async function assertLocalFileIdentity(filePath, rootDir, expected) {
|
|
|
32613
33560
|
async function removeLocalFileIfInside(filePath, rootDir) {
|
|
32614
33561
|
try {
|
|
32615
33562
|
await localFileStatInsideRoot(filePath, rootDir);
|
|
32616
|
-
await
|
|
33563
|
+
await fs5.rm(filePath, { force: true });
|
|
32617
33564
|
} catch {
|
|
32618
33565
|
}
|
|
32619
33566
|
}
|
|
@@ -32626,7 +33573,7 @@ function sameFile(a, b) {
|
|
|
32626
33573
|
async function cleanupOldAttachmentFiles(rootDir, ttlMs, log2, preserveDirs) {
|
|
32627
33574
|
let entries;
|
|
32628
33575
|
try {
|
|
32629
|
-
entries = await
|
|
33576
|
+
entries = await fs5.readdir(rootDir, { withFileTypes: true });
|
|
32630
33577
|
} catch {
|
|
32631
33578
|
return;
|
|
32632
33579
|
}
|
|
@@ -32634,15 +33581,15 @@ async function cleanupOldAttachmentFiles(rootDir, ttlMs, log2, preserveDirs) {
|
|
|
32634
33581
|
await Promise.all(entries.map(async (entry) => {
|
|
32635
33582
|
if (!entry.isDirectory())
|
|
32636
33583
|
return;
|
|
32637
|
-
const fullPath =
|
|
33584
|
+
const fullPath = path6.join(rootDir, entry.name);
|
|
32638
33585
|
try {
|
|
32639
|
-
if (preserveDirs?.has(
|
|
33586
|
+
if (preserveDirs?.has(path6.resolve(fullPath)))
|
|
32640
33587
|
return;
|
|
32641
|
-
const stat = await
|
|
33588
|
+
const stat = await fs5.lstat(fullPath);
|
|
32642
33589
|
if (!stat.isDirectory())
|
|
32643
33590
|
return;
|
|
32644
33591
|
if (stat.mtimeMs < cutoff) {
|
|
32645
|
-
await
|
|
33592
|
+
await fs5.rm(fullPath, { recursive: true, force: true });
|
|
32646
33593
|
}
|
|
32647
33594
|
} catch (err) {
|
|
32648
33595
|
log2?.warn?.(`agent-core: failed to clean attachment temp dir ${fullPath}: ${String(err)}`);
|
|
@@ -32654,7 +33601,7 @@ async function pruneAttachmentCache(rootDir, maxBytes, log2, preserveDirs) {
|
|
|
32654
33601
|
return;
|
|
32655
33602
|
let entries;
|
|
32656
33603
|
try {
|
|
32657
|
-
entries = await
|
|
33604
|
+
entries = await fs5.readdir(rootDir, { withFileTypes: true });
|
|
32658
33605
|
} catch {
|
|
32659
33606
|
return;
|
|
32660
33607
|
}
|
|
@@ -32663,9 +33610,9 @@ async function pruneAttachmentCache(rootDir, maxBytes, log2, preserveDirs) {
|
|
|
32663
33610
|
for (const entry of entries) {
|
|
32664
33611
|
if (!entry.isDirectory())
|
|
32665
33612
|
continue;
|
|
32666
|
-
const fullPath =
|
|
33613
|
+
const fullPath = path6.join(rootDir, entry.name);
|
|
32667
33614
|
try {
|
|
32668
|
-
const stat = await
|
|
33615
|
+
const stat = await fs5.lstat(fullPath);
|
|
32669
33616
|
if (!stat.isDirectory())
|
|
32670
33617
|
continue;
|
|
32671
33618
|
const size = await directorySize(fullPath);
|
|
@@ -32681,10 +33628,10 @@ async function pruneAttachmentCache(rootDir, maxBytes, log2, preserveDirs) {
|
|
|
32681
33628
|
for (const dir of dirs) {
|
|
32682
33629
|
if (total <= maxBytes)
|
|
32683
33630
|
break;
|
|
32684
|
-
if (preserveDirs?.has(
|
|
33631
|
+
if (preserveDirs?.has(path6.resolve(dir.path)))
|
|
32685
33632
|
continue;
|
|
32686
33633
|
try {
|
|
32687
|
-
await
|
|
33634
|
+
await fs5.rm(dir.path, { recursive: true, force: true });
|
|
32688
33635
|
total -= dir.size;
|
|
32689
33636
|
} catch (err) {
|
|
32690
33637
|
log2?.warn?.(`agent-core: failed to prune attachment cache dir ${dir.path}: ${String(err)}`);
|
|
@@ -32693,12 +33640,12 @@ async function pruneAttachmentCache(rootDir, maxBytes, log2, preserveDirs) {
|
|
|
32693
33640
|
}
|
|
32694
33641
|
async function directorySize(dirPath) {
|
|
32695
33642
|
let total = 0;
|
|
32696
|
-
const entries = await
|
|
33643
|
+
const entries = await fs5.readdir(dirPath, { withFileTypes: true });
|
|
32697
33644
|
for (const entry of entries) {
|
|
32698
|
-
const fullPath =
|
|
33645
|
+
const fullPath = path6.join(dirPath, entry.name);
|
|
32699
33646
|
let stat;
|
|
32700
33647
|
try {
|
|
32701
|
-
stat = await
|
|
33648
|
+
stat = await fs5.lstat(fullPath);
|
|
32702
33649
|
} catch {
|
|
32703
33650
|
continue;
|
|
32704
33651
|
}
|
|
@@ -32713,10 +33660,10 @@ async function directorySize(dirPath) {
|
|
|
32713
33660
|
return total;
|
|
32714
33661
|
}
|
|
32715
33662
|
function activeDirsForRoot(rootDir) {
|
|
32716
|
-
const root =
|
|
33663
|
+
const root = path6.resolve(rootDir);
|
|
32717
33664
|
const dirs = /* @__PURE__ */ new Set();
|
|
32718
33665
|
for (const dir of activeAttachmentDirs) {
|
|
32719
|
-
if (dir === root || dir.startsWith(`${root}${
|
|
33666
|
+
if (dir === root || dir.startsWith(`${root}${path6.sep}`)) {
|
|
32720
33667
|
dirs.add(dir);
|
|
32721
33668
|
}
|
|
32722
33669
|
}
|
|
@@ -32813,9 +33760,9 @@ async function writeResponseToFileWithLimit(res, filePath, maxBytes, rootDir) {
|
|
|
32813
33760
|
}
|
|
32814
33761
|
writtenStat = await file.stat();
|
|
32815
33762
|
await closeFile();
|
|
32816
|
-
await localDirectoryStatInsideRoot(
|
|
33763
|
+
await localDirectoryStatInsideRoot(path6.dirname(filePath), rootDir);
|
|
32817
33764
|
await assertLocalFileIdentity(tmpPath, rootDir, writtenStat);
|
|
32818
|
-
await
|
|
33765
|
+
await fs5.rename(tmpPath, filePath);
|
|
32819
33766
|
completed = true;
|
|
32820
33767
|
return written;
|
|
32821
33768
|
} finally {
|
|
@@ -32831,9 +33778,9 @@ async function writeResponseToFileWithLimit(res, filePath, maxBytes, rootDir) {
|
|
|
32831
33778
|
}
|
|
32832
33779
|
}
|
|
32833
33780
|
function localFileName(attachmentId, fileName, mimeType) {
|
|
32834
|
-
const safeName = sanitizePathSegment(
|
|
32835
|
-
const ext =
|
|
32836
|
-
const stem =
|
|
33781
|
+
const safeName = sanitizePathSegment(path6.basename(fileName || attachmentId));
|
|
33782
|
+
const ext = path6.extname(safeName) || extensionForMime(mimeType);
|
|
33783
|
+
const stem = path6.basename(safeName, path6.extname(safeName)) || attachmentId;
|
|
32837
33784
|
return `${sanitizePathSegment(attachmentId)}-${stem}${ext}`;
|
|
32838
33785
|
}
|
|
32839
33786
|
function extensionForMime(mimeType) {
|
|
@@ -33616,6 +34563,9 @@ var CodexAppServerAdapter = class {
|
|
|
33616
34563
|
if (this.opts.contextFilePath) {
|
|
33617
34564
|
env.PRLL_CONTEXT_FILE = this.opts.contextFilePath;
|
|
33618
34565
|
}
|
|
34566
|
+
if (this.opts.contextDirPath) {
|
|
34567
|
+
env.PRLL_CONTEXT_DIR = this.opts.contextDirPath;
|
|
34568
|
+
}
|
|
33619
34569
|
if (!env.PRLL_WIKI_MOUNT_ROOT?.trim()) {
|
|
33620
34570
|
env.PRLL_WIKI_MOUNT_ROOT = this.opts.workspaceDir;
|
|
33621
34571
|
}
|
|
@@ -33761,7 +34711,7 @@ var TurnSink = class {
|
|
|
33761
34711
|
}
|
|
33762
34712
|
};
|
|
33763
34713
|
function ensureGitRepo(workingDirectory) {
|
|
33764
|
-
|
|
34714
|
+
fs6.mkdirSync(workingDirectory, { recursive: true });
|
|
33765
34715
|
try {
|
|
33766
34716
|
execSync2("git rev-parse --is-inside-work-tree", { cwd: workingDirectory, stdio: "pipe" });
|
|
33767
34717
|
ensureLocalAttachmentGitExclude(workingDirectory);
|
|
@@ -33831,8 +34781,8 @@ function errToString(err) {
|
|
|
33831
34781
|
}
|
|
33832
34782
|
|
|
33833
34783
|
// ts/codex-agent/dist/session-manager.js
|
|
33834
|
-
import * as
|
|
33835
|
-
import * as
|
|
34784
|
+
import * as fs7 from "node:fs";
|
|
34785
|
+
import * as path7 from "node:path";
|
|
33836
34786
|
var CodexSessionManager = class {
|
|
33837
34787
|
mainSessionKey;
|
|
33838
34788
|
stateFilePath;
|
|
@@ -33869,14 +34819,14 @@ var CodexSessionManager = class {
|
|
|
33869
34819
|
clearMainThread() {
|
|
33870
34820
|
this.threadIds.delete(this.mainSessionKey);
|
|
33871
34821
|
try {
|
|
33872
|
-
|
|
34822
|
+
fs7.rmSync(this.stateFilePath, { force: true });
|
|
33873
34823
|
} catch (error) {
|
|
33874
34824
|
this.logger?.warn(`codex-agent: failed to clear main thread state at ${this.stateFilePath}: ${String(error)}`);
|
|
33875
34825
|
}
|
|
33876
34826
|
}
|
|
33877
34827
|
restore() {
|
|
33878
34828
|
try {
|
|
33879
|
-
const raw =
|
|
34829
|
+
const raw = fs7.readFileSync(this.stateFilePath, "utf8");
|
|
33880
34830
|
const parsed = JSON.parse(raw);
|
|
33881
34831
|
if (parsed.runtimeKey === this.mainSessionKey && typeof parsed.threadId === "string" && parsed.threadId.trim()) {
|
|
33882
34832
|
this.threadIds.set(this.mainSessionKey, parsed.threadId.trim());
|
|
@@ -33889,10 +34839,10 @@ var CodexSessionManager = class {
|
|
|
33889
34839
|
}
|
|
33890
34840
|
persist(threadId) {
|
|
33891
34841
|
try {
|
|
33892
|
-
|
|
34842
|
+
fs7.mkdirSync(path7.dirname(this.stateFilePath), { recursive: true });
|
|
33893
34843
|
const tmpPath = `${this.stateFilePath}.tmp`;
|
|
33894
|
-
|
|
33895
|
-
|
|
34844
|
+
fs7.writeFileSync(tmpPath, JSON.stringify({ runtimeKey: this.mainSessionKey, threadId }, null, 2));
|
|
34845
|
+
fs7.renameSync(tmpPath, this.stateFilePath);
|
|
33896
34846
|
} catch (error) {
|
|
33897
34847
|
this.logger?.warn(`codex-agent: failed to persist thread state at ${this.stateFilePath}: ${String(error)}`);
|
|
33898
34848
|
}
|
|
@@ -33900,8 +34850,8 @@ var CodexSessionManager = class {
|
|
|
33900
34850
|
};
|
|
33901
34851
|
|
|
33902
34852
|
// ts/codex-agent/dist/workspace.js
|
|
33903
|
-
import * as
|
|
33904
|
-
import * as
|
|
34853
|
+
import * as fs8 from "node:fs";
|
|
34854
|
+
import * as path8 from "node:path";
|
|
33905
34855
|
|
|
33906
34856
|
// ts/node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/error.js
|
|
33907
34857
|
function getLineColFromPtr(string, ptr) {
|
|
@@ -34595,12 +35545,12 @@ function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
|
|
|
34595
35545
|
|
|
34596
35546
|
// ts/codex-agent/dist/workspace.js
|
|
34597
35547
|
function ensureWorkspaceTrusted(codexHome, workspaceDir, log2) {
|
|
34598
|
-
const configPath =
|
|
34599
|
-
const normalizedPath =
|
|
35548
|
+
const configPath = path8.join(codexHome, "config.toml");
|
|
35549
|
+
const normalizedPath = path8.resolve(workspaceDir);
|
|
34600
35550
|
try {
|
|
34601
35551
|
let content = "";
|
|
34602
35552
|
try {
|
|
34603
|
-
content =
|
|
35553
|
+
content = fs8.readFileSync(configPath, "utf8");
|
|
34604
35554
|
} catch (err) {
|
|
34605
35555
|
if (err.code !== "ENOENT")
|
|
34606
35556
|
throw err;
|
|
@@ -34640,14 +35590,14 @@ ${trustLine}
|
|
|
34640
35590
|
content = content.substring(0, afterHeader) + trustLine + "\n" + content.substring(afterHeader);
|
|
34641
35591
|
}
|
|
34642
35592
|
}
|
|
34643
|
-
|
|
35593
|
+
fs8.writeFileSync(configPath, content, "utf8");
|
|
34644
35594
|
} else if (projects?.[normalizedPath] !== void 0) {
|
|
34645
35595
|
log2?.warn(`Codex config.toml has non-canonical header for ${normalizedPath}; skipping trust write`);
|
|
34646
35596
|
} else if (projects && !content.includes("[projects.")) {
|
|
34647
35597
|
log2?.warn(`Codex config.toml uses inline table for projects; skipping trust write for ${normalizedPath}`);
|
|
34648
35598
|
} else {
|
|
34649
|
-
|
|
34650
|
-
|
|
35599
|
+
fs8.mkdirSync(codexHome, { recursive: true });
|
|
35600
|
+
fs8.appendFileSync(configPath, `
|
|
34651
35601
|
${sectionHeader}
|
|
34652
35602
|
${trustLine}
|
|
34653
35603
|
`, "utf8");
|
|
@@ -34664,12 +35614,12 @@ function isParallProxyMode(env = process.env) {
|
|
|
34664
35614
|
return baseUrl === apiUrl || baseUrl.startsWith(`${apiUrl}/`);
|
|
34665
35615
|
}
|
|
34666
35616
|
function ensureParallProvider(codexHome, apiUrl, log2) {
|
|
34667
|
-
const configPath =
|
|
35617
|
+
const configPath = path8.join(codexHome, "config.toml");
|
|
34668
35618
|
const baseUrl = apiUrl.replace(/\/$/, "") + "/api/llm/v1";
|
|
34669
35619
|
try {
|
|
34670
35620
|
let content = "";
|
|
34671
35621
|
try {
|
|
34672
|
-
content =
|
|
35622
|
+
content = fs8.readFileSync(configPath, "utf8");
|
|
34673
35623
|
} catch (err) {
|
|
34674
35624
|
if (err.code !== "ENOENT")
|
|
34675
35625
|
throw err;
|
|
@@ -34700,8 +35650,8 @@ function ensureParallProvider(codexHome, apiUrl, log2) {
|
|
|
34700
35650
|
} else {
|
|
34701
35651
|
content = content.trimEnd() + "\n\n" + providerBlock + "\n";
|
|
34702
35652
|
}
|
|
34703
|
-
|
|
34704
|
-
|
|
35653
|
+
fs8.mkdirSync(codexHome, { recursive: true });
|
|
35654
|
+
fs8.writeFileSync(configPath, content, "utf8");
|
|
34705
35655
|
} catch (err) {
|
|
34706
35656
|
throw new Error(`failed to write Parall provider config: ${String(err)}`);
|
|
34707
35657
|
}
|
|
@@ -34718,18 +35668,18 @@ function ensureCodexWorkspace(workspaceDir, log2, agentIdentity) {
|
|
|
34718
35668
|
PRLL_REFERENCE_GUIDE,
|
|
34719
35669
|
buildSkillReferences(workspaceDir)
|
|
34720
35670
|
].join("\n\n");
|
|
34721
|
-
|
|
34722
|
-
const parallDir =
|
|
34723
|
-
|
|
34724
|
-
|
|
34725
|
-
writeSkillFiles(
|
|
34726
|
-
const codexConfigDir =
|
|
34727
|
-
|
|
35671
|
+
fs8.mkdirSync(workspaceDir, { recursive: true });
|
|
35672
|
+
const parallDir = path8.join(workspaceDir, ".parall");
|
|
35673
|
+
fs8.mkdirSync(parallDir, { recursive: true });
|
|
35674
|
+
fs8.writeFileSync(path8.join(parallDir, "system-prompt.md"), systemPrompt, "utf8");
|
|
35675
|
+
writeSkillFiles(path8.join(parallDir, "skills"));
|
|
35676
|
+
const codexConfigDir = path8.join(workspaceDir, ".codex");
|
|
35677
|
+
fs8.mkdirSync(codexConfigDir, { recursive: true });
|
|
34728
35678
|
const toml = `developer_instructions = """
|
|
34729
35679
|
${systemPrompt.replace(/\\/g, "\\\\").replace(/"""/g, '\\"""')}
|
|
34730
35680
|
"""
|
|
34731
35681
|
`;
|
|
34732
|
-
|
|
35682
|
+
fs8.writeFileSync(path8.join(codexConfigDir, "config.toml"), toml, "utf8");
|
|
34733
35683
|
}
|
|
34734
35684
|
|
|
34735
35685
|
// ts/codex-agent/dist/index.js
|
|
@@ -34804,14 +35754,13 @@ async function main() {
|
|
|
34804
35754
|
log: agentLog
|
|
34805
35755
|
});
|
|
34806
35756
|
const platformDefaults = await configMgr.fetch();
|
|
34807
|
-
|
|
34808
|
-
let platformModelOverride = isPlatformModelOverride(me.agent_profile);
|
|
35757
|
+
const platformModelOverride = deriveModelIsPin(platformDefaults, me.agent_profile);
|
|
34809
35758
|
const resolvedModel = resolveRuntimeModel(platformModelOverride, platformDefaults.model, config.model);
|
|
34810
|
-
const resolvedEffort =
|
|
35759
|
+
const resolvedEffort = platformDefaults.thinkingEffort ?? config.reasoningEffort;
|
|
34811
35760
|
if (platformDefaults.model)
|
|
34812
|
-
agentLog.info(`platform config: model=${platformDefaults.model} \u2192 resolved=${resolvedModel ?? "cli-default"} (${platformModelOverride ? "
|
|
35761
|
+
agentLog.info(`platform config: model=${platformDefaults.model} \u2192 resolved=${resolvedModel ?? "cli-default"} (${platformModelOverride ? "pin/override" : "floor"})`);
|
|
34813
35762
|
if (platformDefaults.thinkingEffort)
|
|
34814
|
-
agentLog.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort}
|
|
35763
|
+
agentLog.info(`platform config: reasoning_effort=${platformDefaults.thinkingEffort}`);
|
|
34815
35764
|
const adapter = new CodexAppServerAdapter({
|
|
34816
35765
|
codexBin: config.codexBin,
|
|
34817
35766
|
codexHome: config.codexHome,
|
|
@@ -34823,8 +35772,25 @@ async function main() {
|
|
|
34823
35772
|
sessionManager,
|
|
34824
35773
|
log: agentLog,
|
|
34825
35774
|
contextFilePath: mainContextFilePath,
|
|
35775
|
+
contextDirPath: dispatchContextDirPath(config.stateDir),
|
|
34826
35776
|
useParallProvider
|
|
34827
35777
|
});
|
|
35778
|
+
const refreshPlatformConfig = async () => {
|
|
35779
|
+
const updated = await configMgr.fetch();
|
|
35780
|
+
let refreshedProfile;
|
|
35781
|
+
if (updated.modelIsPin === void 0) {
|
|
35782
|
+
try {
|
|
35783
|
+
refreshedProfile = (await getAgentMeWithLegacyFallback(client, config.orgId)).agent_profile;
|
|
35784
|
+
} catch (err) {
|
|
35785
|
+
agentLog.warn(`platform config refresh: legacy /agents/me fallback failed: ${String(err)}`);
|
|
35786
|
+
}
|
|
35787
|
+
}
|
|
35788
|
+
const isPin = deriveModelIsPin(updated, refreshedProfile);
|
|
35789
|
+
adapter.updateConfig({
|
|
35790
|
+
model: resolveRuntimeModel(isPin, updated.model, config.model) ?? null,
|
|
35791
|
+
reasoningEffort: updated.thinkingEffort ?? config.reasoningEffort ?? null
|
|
35792
|
+
});
|
|
35793
|
+
};
|
|
34828
35794
|
const gateway = new ParallAgentGateway({
|
|
34829
35795
|
accountId: agentUserId,
|
|
34830
35796
|
client,
|
|
@@ -34852,26 +35818,9 @@ async function main() {
|
|
|
34852
35818
|
dispatchDeadlineMs: parseDispatchDeadlineMs(process.env.PRLL_DISPATCH_DEADLINE_MS),
|
|
34853
35819
|
contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
|
|
34854
35820
|
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
34855
|
-
|
|
34856
|
-
|
|
34857
|
-
|
|
34858
|
-
platformModelOverride = isPlatformModelOverride(refreshed.agent_profile);
|
|
34859
|
-
const updated = await configMgr.fetch();
|
|
34860
|
-
adapter.updateConfig({
|
|
34861
|
-
model: resolveRuntimeModel(platformModelOverride, updated.model, config.model) ?? null,
|
|
34862
|
-
reasoningEffort: platformManaged ? updated.thinkingEffort ?? config.reasoningEffort : config.reasoningEffort ?? null
|
|
34863
|
-
});
|
|
34864
|
-
},
|
|
34865
|
-
onSessionReady: async () => {
|
|
34866
|
-
const refreshed = await getAgentMeWithLegacyFallback(client, config.orgId);
|
|
34867
|
-
platformManaged = isPlatformManagedProfile(refreshed.agent_profile);
|
|
34868
|
-
platformModelOverride = isPlatformModelOverride(refreshed.agent_profile);
|
|
34869
|
-
const updated = await configMgr.fetch();
|
|
34870
|
-
adapter.updateConfig({
|
|
34871
|
-
model: resolveRuntimeModel(platformModelOverride, updated.model, config.model) ?? null,
|
|
34872
|
-
reasoningEffort: platformManaged ? updated.thinkingEffort ?? config.reasoningEffort : config.reasoningEffort ?? null
|
|
34873
|
-
});
|
|
34874
|
-
},
|
|
35821
|
+
dispatchContextDir: dispatchContextDirPath(config.stateDir),
|
|
35822
|
+
onConfigUpdate: refreshPlatformConfig,
|
|
35823
|
+
onSessionReady: refreshPlatformConfig,
|
|
34875
35824
|
onNewSession: () => {
|
|
34876
35825
|
sessionManager.clearMainThread();
|
|
34877
35826
|
},
|