@parall/daemon 1.29.3 → 1.31.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 +12 -11
- package/bundle/parall-claude-agent.js +252 -85
- package/bundle/parall-codex-agent.js +248 -94
- package/bundle/parall-daemon.js +1076 -299
- package/bundle/parall-openclaw-agent.js +2 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +52 -2
- package/dist/config.d.ts +13 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +17 -0
- package/dist/filesystem.d.ts +7 -0
- package/dist/filesystem.d.ts.map +1 -0
- package/dist/filesystem.js +118 -0
- package/dist/index.js +30 -3
- package/dist/runtimes.d.ts +9 -8
- package/dist/runtimes.d.ts.map +1 -1
- package/dist/runtimes.js +47 -83
- package/dist/supervisor.d.ts +5 -0
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +63 -0
- package/dist/updater-manifest.d.ts +39 -0
- package/dist/updater-manifest.d.ts.map +1 -0
- package/dist/updater-manifest.js +94 -0
- package/dist/updater.d.ts +60 -0
- package/dist/updater.d.ts.map +1 -0
- package/dist/updater.js +409 -0
- package/package.json +6 -6
|
@@ -3,6 +3,34 @@
|
|
|
3
3
|
// ts/claude-agent/dist/index.js
|
|
4
4
|
import * as os3 from "node:os";
|
|
5
5
|
|
|
6
|
+
// ts/agent-core/dist/provider-config.js
|
|
7
|
+
function llmSource(pc) {
|
|
8
|
+
if (pc?.llm_source)
|
|
9
|
+
return pc.llm_source;
|
|
10
|
+
if (pc?.openai_api_key || pc?.openai_base_url || pc?.anthropic_auth_token || pc?.anthropic_base_url) {
|
|
11
|
+
return "custom";
|
|
12
|
+
}
|
|
13
|
+
return "parall";
|
|
14
|
+
}
|
|
15
|
+
function clearAllProviderCreds(env) {
|
|
16
|
+
delete env.ANTHROPIC_AUTH_TOKEN;
|
|
17
|
+
delete env.ANTHROPIC_BASE_URL;
|
|
18
|
+
delete env.ANTHROPIC_API_KEY;
|
|
19
|
+
delete env.OPENAI_API_KEY;
|
|
20
|
+
delete env.OPENAI_BASE_URL;
|
|
21
|
+
delete env.PRLL_CLAUDE_ALLOW_API_KEY;
|
|
22
|
+
}
|
|
23
|
+
function parseProviderConfig(env) {
|
|
24
|
+
const raw = env.PRLL_PROVIDER_CONFIG?.trim();
|
|
25
|
+
if (!raw)
|
|
26
|
+
return void 0;
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(raw);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
throw new Error(`Invalid PRLL_PROVIDER_CONFIG JSON: ${String(err)}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
6
34
|
// ts/agent-core/dist/session-state.js
|
|
7
35
|
function normalizeSessionKey(sessionKey) {
|
|
8
36
|
return sessionKey.toLowerCase();
|
|
@@ -428,9 +456,13 @@ function createLogger(prefix) {
|
|
|
428
456
|
return {
|
|
429
457
|
info: (msg) => console.log(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`),
|
|
430
458
|
warn: (msg) => console.warn(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`),
|
|
431
|
-
error: (msg) => console.error(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`)
|
|
459
|
+
error: (msg) => console.error(`${(/* @__PURE__ */ new Date()).toISOString()} [${prefix}] ${msg}`),
|
|
460
|
+
child: (sub) => createLogger(`${prefix}:${sub}`)
|
|
432
461
|
};
|
|
433
462
|
}
|
|
463
|
+
function childLogger(logger, sub) {
|
|
464
|
+
return logger.child ? logger.child(sub) : logger;
|
|
465
|
+
}
|
|
434
466
|
|
|
435
467
|
// ts/agent-core/dist/gateway-base.js
|
|
436
468
|
import * as os from "node:os";
|
|
@@ -470,6 +502,7 @@ var ENDPOINTS = {
|
|
|
470
502
|
ORG_MEMBER: (orgId, userId) => `${API_BASE}/orgs/${orgId}/members/${userId}`,
|
|
471
503
|
ORG_MEMBER_CHATS: (orgId, memberId) => `${API_BASE}/orgs/${orgId}/members/${memberId}/chats`,
|
|
472
504
|
ORG_MEMBER_TASKS: (orgId, memberId) => `${API_BASE}/orgs/${orgId}/members/${memberId}/tasks`,
|
|
505
|
+
REF_SEARCH: (orgId) => `${API_BASE}/orgs/${orgId}/refs/search`,
|
|
473
506
|
// Direct messages (org-scoped, atomic find-or-create + send)
|
|
474
507
|
DM: (orgId) => `${API_BASE}/orgs/${orgId}/dm`,
|
|
475
508
|
// Onboarding
|
|
@@ -513,6 +546,7 @@ var ENDPOINTS = {
|
|
|
513
546
|
AGENT_ACTIVITY: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/activity`,
|
|
514
547
|
AGENT_MONITOR: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/monitor`,
|
|
515
548
|
AGENT_ME: (orgId) => `${API_BASE}/orgs/${orgId}/agents/me`,
|
|
549
|
+
AGENT_NEW_SESSION: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/new-session`,
|
|
516
550
|
AGENT_SESSIONS: (orgId, agentId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/sessions`,
|
|
517
551
|
AGENT_SESSION: (orgId, agentId, sessionId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/sessions/${sessionId}`,
|
|
518
552
|
AGENT_SESSION_STEPS: (orgId, agentId, sessionId) => `${API_BASE}/orgs/${orgId}/agents/${agentId}/sessions/${sessionId}/steps`,
|
|
@@ -551,6 +585,8 @@ var ENDPOINTS = {
|
|
|
551
585
|
MACHINE_KEY: (orgId, machineId, keyId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/keys/${keyId}`,
|
|
552
586
|
MACHINE_RUNTIME_AUTH_SESSIONS: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/runtime-auth-sessions`,
|
|
553
587
|
MACHINE_RUNTIME_AUTH_SESSION_COMPLETE: (orgId, machineId, sessionId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/runtime-auth-sessions/${sessionId}/complete`,
|
|
588
|
+
MACHINE_REQUEST_UPDATE: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/request-update`,
|
|
589
|
+
MACHINE_BROWSE: (orgId, machineId) => `${API_BASE}/orgs/${orgId}/machines/${machineId}/browse`,
|
|
554
590
|
// Machine self-control-plane (mck_-scoped). The bearer token implicitly
|
|
555
591
|
// identifies the Machine, so there is no `:mid` URL parameter — these are
|
|
556
592
|
// "self" routes called by the daemon for its own host.
|
|
@@ -560,6 +596,7 @@ var ENDPOINTS = {
|
|
|
560
596
|
MACHINES_ME_AGENT_LAUNCH_CREDENTIAL: (agentId) => `${API_BASE}/machines/me/agents/${agentId}/launch-credential`,
|
|
561
597
|
MACHINES_ME_AGENT_WORKSPACE_STATE: (agentId) => `${API_BASE}/machines/me/agents/${agentId}/workspace-state`,
|
|
562
598
|
MACHINES_ME_WS_TICKET: `${API_BASE}/machines/me/ws/ticket`,
|
|
599
|
+
MACHINES_ME_BROWSE_RESPONSE: (requestId) => `${API_BASE}/machines/me/browse-response/${requestId}`,
|
|
563
600
|
// Tasks (org-scoped)
|
|
564
601
|
TASKS: (orgId) => `${API_BASE}/orgs/${orgId}/tasks`,
|
|
565
602
|
TASK: (orgId, taskId) => `${API_BASE}/orgs/${orgId}/tasks/${taskId}`,
|
|
@@ -594,6 +631,12 @@ var ENDPOINTS = {
|
|
|
594
631
|
INVITATION_ACCEPT: (id) => `${API_BASE}/invitations/${id}/accept`,
|
|
595
632
|
INVITATION_DECLINE: (id) => `${API_BASE}/invitations/${id}/decline`,
|
|
596
633
|
INVITATION_BY_TOKEN: (token) => `${API_BASE}/invitations/by-token/${token}`,
|
|
634
|
+
// Org-level shareable invite link
|
|
635
|
+
ORG_INVITE_LINK: (orgId) => `${API_BASE}/orgs/${orgId}/invite-link`,
|
|
636
|
+
ORG_INVITE_LINK_REGENERATE: (orgId) => `${API_BASE}/orgs/${orgId}/invite-link/regenerate`,
|
|
637
|
+
ORG_INVITE_LINK_JOIN_REQUESTS: (orgId) => `${API_BASE}/orgs/${orgId}/invite-link/join-requests`,
|
|
638
|
+
ORG_INVITE_LINK_JOIN_REQUEST_DECIDE: (orgId, jrId) => `${API_BASE}/orgs/${orgId}/invite-link/join-requests/${jrId}/decide`,
|
|
639
|
+
INVITE_LINK_JOIN: `${API_BASE}/invite-link/join`,
|
|
597
640
|
// Wikis (org-scoped, served by wiki-service)
|
|
598
641
|
WIKIS: (orgId) => `${WIKI_BASE}/orgs/${orgId}/wikis`,
|
|
599
642
|
WIKI: (orgId, wikiId) => `${WIKI_BASE}/orgs/${orgId}/wikis/${wikiId}`,
|
|
@@ -713,6 +756,8 @@ var WS_EVENTS = {
|
|
|
713
756
|
INVITATION_ACCEPTED: "invitation.accepted",
|
|
714
757
|
INVITATION_DECLINED: "invitation.declined",
|
|
715
758
|
INVITATION_REVOKED: "invitation.revoked",
|
|
759
|
+
ORG_JOIN_REQUEST_NEW: "org.join_request.new",
|
|
760
|
+
ORG_INVITE_LINK_JOINED: "org.invite_link.joined",
|
|
716
761
|
AGENT_CONFIG_UPDATE: "agent_config.update",
|
|
717
762
|
PRESENCE_UPDATE: "presence.update",
|
|
718
763
|
WIKI_CHANGESET_CREATED: "wiki.changeset.created",
|
|
@@ -739,7 +784,11 @@ var WS_EVENTS = {
|
|
|
739
784
|
MACHINE_AGENT_ATTACHED: "machine.agent.attached",
|
|
740
785
|
MACHINE_AGENT_DETACHED: "machine.agent.detached",
|
|
741
786
|
MACHINE_STOP: "machine.stop",
|
|
742
|
-
MACHINE_WORKSPACE_SETUP_REQUESTED: "machine.workspace.setup.requested"
|
|
787
|
+
MACHINE_WORKSPACE_SETUP_REQUESTED: "machine.workspace.setup.requested",
|
|
788
|
+
MACHINE_FILESYSTEM_BROWSE: "machine.filesystem.browse",
|
|
789
|
+
MACHINE_UPDATE: "machine.update",
|
|
790
|
+
MACHINE_CONFIG_UPDATED: "machine.config.updated",
|
|
791
|
+
AGENT_NEW_SESSION: "agent.new_session"
|
|
743
792
|
};
|
|
744
793
|
|
|
745
794
|
// ts/sdk/dist/client.js
|
|
@@ -1068,6 +1117,9 @@ var ParallClient = class _ParallClient {
|
|
|
1068
1117
|
const res = await this.request("GET", ENDPOINTS.ORG_MEMBERS_ONLINE(orgId));
|
|
1069
1118
|
return res.user_ids ?? [];
|
|
1070
1119
|
}
|
|
1120
|
+
async searchRefs(orgId, params) {
|
|
1121
|
+
return this.request("GET", ENDPOINTS.REF_SEARCH(orgId), void 0, params);
|
|
1122
|
+
}
|
|
1071
1123
|
async removeOrgMember(orgId, userId) {
|
|
1072
1124
|
return this.request("DELETE", ENDPOINTS.ORG_MEMBER(orgId, userId));
|
|
1073
1125
|
}
|
|
@@ -1121,6 +1173,34 @@ var ParallClient = class _ParallClient {
|
|
|
1121
1173
|
async getInvitationByToken(token) {
|
|
1122
1174
|
return this.request("GET", ENDPOINTS.INVITATION_BY_TOKEN(token));
|
|
1123
1175
|
}
|
|
1176
|
+
// ---- Org-level shareable invite link ----
|
|
1177
|
+
/** Fetch the org's current invite link. Auto-creates on first call
|
|
1178
|
+
* so the settings UI never sees an empty state. */
|
|
1179
|
+
async getOrgInviteLink(orgId) {
|
|
1180
|
+
return this.request("GET", ENDPOINTS.ORG_INVITE_LINK(orgId));
|
|
1181
|
+
}
|
|
1182
|
+
/** Rotate the token in place. The old token stops resolving immediately. */
|
|
1183
|
+
async regenerateOrgInviteLink(orgId) {
|
|
1184
|
+
return this.request("POST", ENDPOINTS.ORG_INVITE_LINK_REGENERATE(orgId));
|
|
1185
|
+
}
|
|
1186
|
+
/** Toggle the require-approval flag on the org's invite link. */
|
|
1187
|
+
async updateOrgInviteLink(orgId, data) {
|
|
1188
|
+
return this.request("PATCH", ENDPOINTS.ORG_INVITE_LINK(orgId), data);
|
|
1189
|
+
}
|
|
1190
|
+
/** Redeem an invite-link token. Caller must be authenticated; the
|
|
1191
|
+
* token is the lookup key so this endpoint is NOT org-scoped. */
|
|
1192
|
+
async joinByInviteLink(token) {
|
|
1193
|
+
return this.request("POST", ENDPOINTS.INVITE_LINK_JOIN, { token });
|
|
1194
|
+
}
|
|
1195
|
+
/** Admin: list pending invite-link join requests for an org. */
|
|
1196
|
+
async listOrgJoinRequests(orgId) {
|
|
1197
|
+
const res = await this.request("GET", ENDPOINTS.ORG_INVITE_LINK_JOIN_REQUESTS(orgId));
|
|
1198
|
+
return res.data;
|
|
1199
|
+
}
|
|
1200
|
+
/** Admin: approve or reject a pending invite-link join request. */
|
|
1201
|
+
async decideOrgJoinRequest(orgId, jrId, decision) {
|
|
1202
|
+
return this.request("POST", ENDPOINTS.ORG_INVITE_LINK_JOIN_REQUEST_DECIDE(orgId, jrId), { decision });
|
|
1203
|
+
}
|
|
1124
1204
|
// ---- Direct Messages (org-scoped) ----
|
|
1125
1205
|
async sendDirectMessage(orgId, req) {
|
|
1126
1206
|
return this.request("POST", ENDPOINTS.DM(orgId), req);
|
|
@@ -1285,6 +1365,9 @@ var ParallClient = class _ParallClient {
|
|
|
1285
1365
|
return this.request("GET", ENDPOINTS.AGENT_ME(orgId));
|
|
1286
1366
|
}
|
|
1287
1367
|
// ---- Agent Sessions (org-scoped) ----
|
|
1368
|
+
async requestNewAgentSession(orgId, agentId) {
|
|
1369
|
+
return this.request("POST", ENDPOINTS.AGENT_NEW_SESSION(orgId, agentId));
|
|
1370
|
+
}
|
|
1288
1371
|
async createAgentSession(orgId, agentId, req) {
|
|
1289
1372
|
return this.request("POST", ENDPOINTS.AGENT_SESSIONS(orgId, agentId), req);
|
|
1290
1373
|
}
|
|
@@ -1387,19 +1470,20 @@ var ParallClient = class _ParallClient {
|
|
|
1387
1470
|
}
|
|
1388
1471
|
// ---- Daemon-mode Machine management (org-scoped, user auth) ----
|
|
1389
1472
|
/**
|
|
1390
|
-
* Create a new daemon-mode Machine. Returns the Machine row +
|
|
1391
|
-
* mck_ token.
|
|
1473
|
+
* Create a new self-hosted daemon-mode Machine. Returns the Machine row +
|
|
1474
|
+
* one-shot mck_ token.
|
|
1392
1475
|
*/
|
|
1393
1476
|
async createMachine(orgId, opts) {
|
|
1394
1477
|
return this.request("POST", ENDPOINTS.MACHINES(orgId), opts);
|
|
1395
1478
|
}
|
|
1396
1479
|
/**
|
|
1397
|
-
* Returns
|
|
1398
|
-
* "Workspaces" UI list. Legacy 1:1 machines are
|
|
1480
|
+
* Returns local daemon_mode=true machines (non-terminated). Backs the
|
|
1481
|
+
* "Workspaces" UI list. Legacy 1:1 and retired hosted daemon machines are
|
|
1482
|
+
* excluded.
|
|
1399
1483
|
*/
|
|
1400
1484
|
async getDaemonMachines(orgId) {
|
|
1401
1485
|
const all = await this.getMachines(orgId);
|
|
1402
|
-
return all.filter((m) => m.daemon_mode && m.status !== "terminated");
|
|
1486
|
+
return all.filter((m) => m.daemon_mode && m.compute_mode === "local" && m.status !== "terminated");
|
|
1403
1487
|
}
|
|
1404
1488
|
/** Attach an agent to a daemon-mode Machine. */
|
|
1405
1489
|
async attachAgent(orgId, machineId, agentId, opts) {
|
|
@@ -1419,8 +1503,8 @@ var ParallClient = class _ParallClient {
|
|
|
1419
1503
|
async retryAgentWorkspaceSetup(orgId, machineId, agentId) {
|
|
1420
1504
|
return this.request("POST", ENDPOINTS.MACHINE_AGENT_WORKSPACE_SETUP(orgId, machineId, agentId));
|
|
1421
1505
|
}
|
|
1422
|
-
async patchMachineLLMSource(orgId, machineId,
|
|
1423
|
-
return this.request("PATCH", ENDPOINTS.MACHINE_LLM_SOURCE(orgId, machineId), { llm_source:
|
|
1506
|
+
async patchMachineLLMSource(orgId, machineId, llmSource2) {
|
|
1507
|
+
return this.request("PATCH", ENDPOINTS.MACHINE_LLM_SOURCE(orgId, machineId), { llm_source: llmSource2 });
|
|
1424
1508
|
}
|
|
1425
1509
|
/** Get machine-level runtime auth state. */
|
|
1426
1510
|
async getMachineRuntimeAuth(orgId, machineId) {
|
|
@@ -1461,8 +1545,9 @@ var ParallClient = class _ParallClient {
|
|
|
1461
1545
|
* daemon should call this on a fixed cadence (e.g. every 30s) so an
|
|
1462
1546
|
* external observer can detect a wedged supervisor.
|
|
1463
1547
|
*/
|
|
1464
|
-
async postMachineHeartbeat() {
|
|
1465
|
-
|
|
1548
|
+
async postMachineHeartbeat(daemonVersion) {
|
|
1549
|
+
const body = daemonVersion ? { daemon_version: daemonVersion } : void 0;
|
|
1550
|
+
return this.request("POST", ENDPOINTS.MACHINES_ME_HEALTH, body);
|
|
1466
1551
|
}
|
|
1467
1552
|
async reportAgentWorkspaceState(agentId, state) {
|
|
1468
1553
|
const res = await this.request("PUT", ENDPOINTS.MACHINES_ME_AGENT_WORKSPACE_STATE(agentId), state);
|
|
@@ -1484,9 +1569,19 @@ var ParallClient = class _ParallClient {
|
|
|
1484
1569
|
async getMachineWsTicket() {
|
|
1485
1570
|
return this.request("POST", ENDPOINTS.MACHINES_ME_WS_TICKET);
|
|
1486
1571
|
}
|
|
1572
|
+
async postBrowseResponse(requestId, response) {
|
|
1573
|
+
return this.request("POST", ENDPOINTS.MACHINES_ME_BROWSE_RESPONSE(requestId), response);
|
|
1574
|
+
}
|
|
1487
1575
|
async resizeMachine(orgId, machineId, spec) {
|
|
1488
1576
|
return this.request("PATCH", ENDPOINTS.MACHINE_SPEC(orgId, machineId), spec);
|
|
1489
1577
|
}
|
|
1578
|
+
/** Signal a local daemon-mode Machine to check for and apply an update. */
|
|
1579
|
+
async requestMachineUpdate(orgId, machineId, mandatory = false) {
|
|
1580
|
+
await this.request("POST", ENDPOINTS.MACHINE_REQUEST_UPDATE(orgId, machineId), { mandatory });
|
|
1581
|
+
}
|
|
1582
|
+
async browseMachineFilesystem(orgId, machineId, path9) {
|
|
1583
|
+
return this.request("POST", ENDPOINTS.MACHINE_BROWSE(orgId, machineId), { path: path9 }, void 0, false, { timeoutMs: 15e3 });
|
|
1584
|
+
}
|
|
1490
1585
|
/** Create a new machine key. Returns the raw key string (shown once) + metadata. */
|
|
1491
1586
|
async createMachineKey(orgId, machineId, name) {
|
|
1492
1587
|
return this.request("POST", ENDPOINTS.MACHINE_KEYS(orgId, machineId), name ? { name } : void 0);
|
|
@@ -2334,6 +2429,7 @@ var ParallWs = class {
|
|
|
2334
2429
|
};
|
|
2335
2430
|
|
|
2336
2431
|
// ts/agent-core/dist/gateway-base.js
|
|
2432
|
+
var LIVE_SESSION_STATUSES = /* @__PURE__ */ new Set(["open", "active", "idle"]);
|
|
2337
2433
|
function parseShutdownDeadlineMs(raw) {
|
|
2338
2434
|
if (!raw)
|
|
2339
2435
|
return void 0;
|
|
@@ -2399,6 +2495,7 @@ var ParallAgentGateway = class {
|
|
|
2399
2495
|
shuttingDown = false;
|
|
2400
2496
|
inFlightDispatches = 0;
|
|
2401
2497
|
drainResolvers = [];
|
|
2498
|
+
pendingRestartNotification = null;
|
|
2402
2499
|
DISPATCHED_MESSAGES_CAP = 5e3;
|
|
2403
2500
|
COLD_START_WINDOW_MS;
|
|
2404
2501
|
// SHUTDOWN_DEADLINE_MS is read by waitForDrain via the configured value
|
|
@@ -2413,7 +2510,7 @@ var ParallAgentGateway = class {
|
|
|
2413
2510
|
async run(abortSignal) {
|
|
2414
2511
|
const { ws, log: log2 } = this.opts;
|
|
2415
2512
|
ws.onStateChange((state) => {
|
|
2416
|
-
log2?.info(`
|
|
2513
|
+
log2?.info(`connection state \u2192 ${state}`);
|
|
2417
2514
|
});
|
|
2418
2515
|
ws.on("hello", async (data) => {
|
|
2419
2516
|
await this.handleHello(data);
|
|
@@ -2442,16 +2539,29 @@ var ParallAgentGateway = class {
|
|
|
2442
2539
|
await this.handleMessage(data);
|
|
2443
2540
|
});
|
|
2444
2541
|
ws.on("agent_config.update", async (data) => {
|
|
2445
|
-
this.opts.log?.info(`
|
|
2542
|
+
this.opts.log?.info(`config update notification (version=${data.version})`);
|
|
2446
2543
|
try {
|
|
2447
2544
|
await this.opts.onConfigUpdate?.(data);
|
|
2448
2545
|
} catch (err) {
|
|
2449
|
-
this.opts.log?.warn(`
|
|
2546
|
+
this.opts.log?.warn(`config update failed: ${String(err)}`);
|
|
2547
|
+
}
|
|
2548
|
+
});
|
|
2549
|
+
ws.on("agent.new_session", async (data) => {
|
|
2550
|
+
const prevId = data.previous_session_id ?? "";
|
|
2551
|
+
this.opts.log?.info(`new session signal received (previous=${prevId})`);
|
|
2552
|
+
this.sessionBindings.clear();
|
|
2553
|
+
if (prevId) {
|
|
2554
|
+
this.pendingRestartNotification = `[Harness Notification] This is a fresh session. Your previous session (${prevId}) was ended by the user and you have been restarted.`;
|
|
2555
|
+
}
|
|
2556
|
+
try {
|
|
2557
|
+
await this.opts.onNewSession?.(prevId);
|
|
2558
|
+
} catch (err) {
|
|
2559
|
+
this.opts.log?.warn(`onNewSession callback failed: ${String(err)}`);
|
|
2450
2560
|
}
|
|
2451
2561
|
});
|
|
2452
2562
|
ws.on("recovery.overflow", () => {
|
|
2453
|
-
this.opts.log?.warn(`
|
|
2454
|
-
this.catchUpFromDispatch().catch((err) => this.opts.log?.warn(`
|
|
2563
|
+
this.opts.log?.warn(`recovery.overflow \u2014 triggering full catch-up`);
|
|
2564
|
+
this.catchUpFromDispatch().catch((err) => this.opts.log?.warn(`overflow catch-up failed: ${String(err)}`));
|
|
2455
2565
|
});
|
|
2456
2566
|
ws.on("task.assigned", async (data) => {
|
|
2457
2567
|
if (data.assignee_id !== this.opts.agentUserId)
|
|
@@ -2465,7 +2575,7 @@ var ParallAgentGateway = class {
|
|
|
2465
2575
|
});
|
|
2466
2576
|
}
|
|
2467
2577
|
} catch (err) {
|
|
2468
|
-
this.opts.log?.error(`
|
|
2578
|
+
this.opts.log?.error(`task dispatch failed for ${data.id}: ${String(err)}`);
|
|
2469
2579
|
}
|
|
2470
2580
|
});
|
|
2471
2581
|
ws.on("dispatch.new", async (data) => {
|
|
@@ -2479,7 +2589,7 @@ var ParallAgentGateway = class {
|
|
|
2479
2589
|
});
|
|
2480
2590
|
}
|
|
2481
2591
|
} catch (err) {
|
|
2482
|
-
this.opts.log?.error(`
|
|
2592
|
+
this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
2483
2593
|
}
|
|
2484
2594
|
} else if (data.event_type === "task_update") {
|
|
2485
2595
|
if (!data.task_id)
|
|
@@ -2491,7 +2601,7 @@ var ParallAgentGateway = class {
|
|
|
2491
2601
|
});
|
|
2492
2602
|
}
|
|
2493
2603
|
} catch (err) {
|
|
2494
|
-
this.opts.log?.error(`
|
|
2604
|
+
this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
|
|
2495
2605
|
}
|
|
2496
2606
|
} else if (data.event_type === "schedule.fire") {
|
|
2497
2607
|
if (!data.source_id)
|
|
@@ -2503,7 +2613,7 @@ var ParallAgentGateway = class {
|
|
|
2503
2613
|
});
|
|
2504
2614
|
}
|
|
2505
2615
|
} catch (err) {
|
|
2506
|
-
this.opts.log?.error(`
|
|
2616
|
+
this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
2507
2617
|
}
|
|
2508
2618
|
} else if (data.event_type === "approval_decided") {
|
|
2509
2619
|
if (!data.source_id)
|
|
@@ -2515,13 +2625,13 @@ var ParallAgentGateway = class {
|
|
|
2515
2625
|
});
|
|
2516
2626
|
}
|
|
2517
2627
|
} catch (err) {
|
|
2518
|
-
this.opts.log?.error(`
|
|
2628
|
+
this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
2519
2629
|
}
|
|
2520
2630
|
} else if (data.event_type !== "message" && data.event_type !== "task_assign") {
|
|
2521
|
-
this.opts.log?.info(`
|
|
2631
|
+
this.opts.log?.info(`dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) \u2014 no-op`);
|
|
2522
2632
|
}
|
|
2523
2633
|
});
|
|
2524
|
-
this.opts.log?.info(`
|
|
2634
|
+
this.opts.log?.info(`connecting to ${this.opts.connectionLabel ?? "Parall WS"}...`);
|
|
2525
2635
|
await ws.connect();
|
|
2526
2636
|
return new Promise((resolve3) => {
|
|
2527
2637
|
abortSignal.addEventListener("abort", async () => {
|
|
@@ -2635,7 +2745,7 @@ var ParallAgentGateway = class {
|
|
|
2635
2745
|
}
|
|
2636
2746
|
});
|
|
2637
2747
|
} catch (err) {
|
|
2638
|
-
this.opts.log?.warn(`
|
|
2748
|
+
this.opts.log?.warn(`failed to create input step: ${String(err)}`);
|
|
2639
2749
|
}
|
|
2640
2750
|
}
|
|
2641
2751
|
async createRuntimeStep(sessionId, event, runtimeEvent, stepIdFilePath, contextFilePath) {
|
|
@@ -2713,12 +2823,12 @@ var ParallAgentGateway = class {
|
|
|
2713
2823
|
target_type: target.target_type,
|
|
2714
2824
|
target_id: target.target_id,
|
|
2715
2825
|
content: { text: runtimeEvent.message, suppressed: false },
|
|
2716
|
-
projection:
|
|
2826
|
+
projection: false
|
|
2717
2827
|
});
|
|
2718
2828
|
break;
|
|
2719
2829
|
}
|
|
2720
2830
|
} catch (err) {
|
|
2721
|
-
this.opts.log?.warn(`
|
|
2831
|
+
this.opts.log?.warn(`failed to create ${runtimeEvent.type} step: ${String(err)}`);
|
|
2722
2832
|
}
|
|
2723
2833
|
}
|
|
2724
2834
|
writeContextFile(filePath, ctx) {
|
|
@@ -2726,7 +2836,7 @@ var ParallAgentGateway = class {
|
|
|
2726
2836
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
2727
2837
|
fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
2728
2838
|
} catch (err) {
|
|
2729
|
-
this.opts.log?.warn(`
|
|
2839
|
+
this.opts.log?.warn(`failed to write context file ${filePath}: ${String(err)}`);
|
|
2730
2840
|
}
|
|
2731
2841
|
}
|
|
2732
2842
|
updateContextFileStepId(filePath, stepId) {
|
|
@@ -2736,7 +2846,7 @@ var ParallAgentGateway = class {
|
|
|
2736
2846
|
ctx.step_id = stepId;
|
|
2737
2847
|
fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
2738
2848
|
} catch (err) {
|
|
2739
|
-
this.opts.log?.warn(`
|
|
2849
|
+
this.opts.log?.warn(`failed to update context file step_id ${filePath}: ${String(err)}`);
|
|
2740
2850
|
}
|
|
2741
2851
|
}
|
|
2742
2852
|
updateContextFileSessionId(filePath, sessionId) {
|
|
@@ -2746,7 +2856,7 @@ var ParallAgentGateway = class {
|
|
|
2746
2856
|
ctx.session_id = sessionId;
|
|
2747
2857
|
fs.writeFileSync(filePath, JSON.stringify(ctx), "utf8");
|
|
2748
2858
|
} catch (err) {
|
|
2749
|
-
this.opts.log?.warn(`
|
|
2859
|
+
this.opts.log?.warn(`failed to update context file session_id ${filePath}: ${String(err)}`);
|
|
2750
2860
|
}
|
|
2751
2861
|
}
|
|
2752
2862
|
/** @deprecated Use writeContextFile / updateContextFileStepId. */
|
|
@@ -2755,7 +2865,7 @@ var ParallAgentGateway = class {
|
|
|
2755
2865
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
2756
2866
|
fs.writeFileSync(filePath, stepId, "utf8");
|
|
2757
2867
|
} catch (err) {
|
|
2758
|
-
this.opts.log?.warn(`
|
|
2868
|
+
this.opts.log?.warn(`failed to write step id file ${filePath}: ${String(err)}`);
|
|
2759
2869
|
}
|
|
2760
2870
|
}
|
|
2761
2871
|
/** @deprecated Use writeContextFile / updateContextFileStepId. */
|
|
@@ -2789,6 +2899,17 @@ var ParallAgentGateway = class {
|
|
|
2789
2899
|
parent_session_id: parentSessionId,
|
|
2790
2900
|
runtime_ref: Object.keys(runtimeRef).length > 0 ? runtimeRef : void 0
|
|
2791
2901
|
});
|
|
2902
|
+
if (!LIVE_SESSION_STATUSES.has(session.status)) {
|
|
2903
|
+
this.opts.log?.warn?.(`createAgentSession returned terminal session ${session.id} (${session.status}), resetting runtime for ${sessionKey}`);
|
|
2904
|
+
this.sessionBindings.delete(sessionKey);
|
|
2905
|
+
try {
|
|
2906
|
+
await this.opts.onSessionStale?.(sessionKey);
|
|
2907
|
+
} catch (e) {
|
|
2908
|
+
this.opts.log?.warn?.(`onSessionStale failed: ${e}`);
|
|
2909
|
+
}
|
|
2910
|
+
this.opts.log?.info?.(`stale session self-heal complete for ${sessionKey} \u2014 next dispatch will create a fresh session`);
|
|
2911
|
+
throw new Error(`Agent session ${session.id} is terminal (${session.status})`);
|
|
2912
|
+
}
|
|
2792
2913
|
const binding = {
|
|
2793
2914
|
sessionKey,
|
|
2794
2915
|
agentSessionId: session.id,
|
|
@@ -2812,9 +2933,13 @@ var ParallAgentGateway = class {
|
|
|
2812
2933
|
// catch-up on the replacement pod — otherwise we silently drop work.
|
|
2813
2934
|
async runDispatch(event, sessionKey, bodyForAgent, earlierEvents = [], captureText) {
|
|
2814
2935
|
if (this.shuttingDown) {
|
|
2815
|
-
this.opts.log?.info(`
|
|
2936
|
+
this.opts.log?.info(`skipping dispatch for ${event.messageId} (shutting down) \u2014 leaving unacked for catch-up on replacement pod`);
|
|
2816
2937
|
return false;
|
|
2817
2938
|
}
|
|
2939
|
+
if (this.pendingRestartNotification) {
|
|
2940
|
+
bodyForAgent = this.pendingRestartNotification + "\n\n---\n\n" + bodyForAgent;
|
|
2941
|
+
this.pendingRestartNotification = null;
|
|
2942
|
+
}
|
|
2818
2943
|
setSessionChatId(sessionKey, event.targetId);
|
|
2819
2944
|
setSessionMessageId(sessionKey, event.messageId);
|
|
2820
2945
|
setDispatchMessageId(sessionKey, event.messageId);
|
|
@@ -2944,7 +3069,7 @@ var ParallAgentGateway = class {
|
|
|
2944
3069
|
item.resolve(true);
|
|
2945
3070
|
}
|
|
2946
3071
|
} catch (err) {
|
|
2947
|
-
this.opts.log?.error(`
|
|
3072
|
+
this.opts.log?.error(`fork dispatch failed for ${last.messageId}: ${String(err)}`);
|
|
2948
3073
|
for (const item of items) {
|
|
2949
3074
|
item.resolve(false);
|
|
2950
3075
|
}
|
|
@@ -2962,7 +3087,7 @@ var ParallAgentGateway = class {
|
|
|
2962
3087
|
try {
|
|
2963
3088
|
historyPath = this.opts.dispatchAdapter.getSessionHistoryPath?.(fork.fork.sessionKey);
|
|
2964
3089
|
} catch (err) {
|
|
2965
|
-
this.opts.log?.warn?.(`
|
|
3090
|
+
this.opts.log?.warn?.(`failed to resolve fork history path: ${String(err)}`);
|
|
2966
3091
|
}
|
|
2967
3092
|
this.dispatchState.pendingForkResults.push({
|
|
2968
3093
|
forkSessionKey: fork.fork.sessionKey,
|
|
@@ -3013,7 +3138,7 @@ var ParallAgentGateway = class {
|
|
|
3013
3138
|
try {
|
|
3014
3139
|
while (this.dispatchState.mainBuffer.length > 0) {
|
|
3015
3140
|
if (this.shuttingDown) {
|
|
3016
|
-
this.opts.log?.info(`
|
|
3141
|
+
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`);
|
|
3017
3142
|
break;
|
|
3018
3143
|
}
|
|
3019
3144
|
const targetId = this.dispatchState.mainBuffer[0].targetId;
|
|
@@ -3079,7 +3204,7 @@ var ParallAgentGateway = class {
|
|
|
3079
3204
|
this.dispatchState.mainBuffer.push(event);
|
|
3080
3205
|
if (this.dispatchState.mainCurrentTargetId === event.targetId && await this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))) {
|
|
3081
3206
|
this.startInjectedTyping(event);
|
|
3082
|
-
this.opts.log?.info(`
|
|
3207
|
+
this.opts.log?.info(`steer injected for ${event.messageId} (will drain for bookkeeping)`);
|
|
3083
3208
|
}
|
|
3084
3209
|
if (!this.dispatchState.mainDispatching && !this.draining && this.dispatchState.mainBuffer.length > 0) {
|
|
3085
3210
|
this.dispatchState.mainDispatching = true;
|
|
@@ -3108,7 +3233,7 @@ var ParallAgentGateway = class {
|
|
|
3108
3233
|
preDispatchBranchPoint: this.dispatchState.mainPreDispatchBranchPoint
|
|
3109
3234
|
});
|
|
3110
3235
|
if (!fork) {
|
|
3111
|
-
this.opts.log?.warn(`
|
|
3236
|
+
this.opts.log?.warn(`fork failed, buffering event for main session`);
|
|
3112
3237
|
this.dispatchState.mainBuffer.push(event);
|
|
3113
3238
|
return false;
|
|
3114
3239
|
}
|
|
@@ -3124,7 +3249,7 @@ var ParallAgentGateway = class {
|
|
|
3124
3249
|
activeFork.queue.push({ event, resolve: resolve3 });
|
|
3125
3250
|
});
|
|
3126
3251
|
this.runForkDrainLoop(activeFork).catch((err) => {
|
|
3127
|
-
this.opts.log?.error(`
|
|
3252
|
+
this.opts.log?.error(`fork drain loop error: ${String(err)}`);
|
|
3128
3253
|
});
|
|
3129
3254
|
return firstEventPromise;
|
|
3130
3255
|
}
|
|
@@ -3144,7 +3269,7 @@ var ParallAgentGateway = class {
|
|
|
3144
3269
|
this.chatInfoMap.set(chatId, chatInfo);
|
|
3145
3270
|
return chatInfo;
|
|
3146
3271
|
} catch (err) {
|
|
3147
|
-
this.opts.log?.warn(`
|
|
3272
|
+
this.opts.log?.warn(`failed to resolve chat ${chatId}: ${String(err)}`);
|
|
3148
3273
|
return null;
|
|
3149
3274
|
}
|
|
3150
3275
|
}
|
|
@@ -3217,7 +3342,7 @@ var ParallAgentGateway = class {
|
|
|
3217
3342
|
this.dispatchedMessages.delete(data.id);
|
|
3218
3343
|
}
|
|
3219
3344
|
} catch (err) {
|
|
3220
|
-
this.opts.log?.error(`
|
|
3345
|
+
this.opts.log?.error(`event dispatch failed for ${data.id}: ${String(err)}`);
|
|
3221
3346
|
this.dispatchedMessages.delete(data.id);
|
|
3222
3347
|
}
|
|
3223
3348
|
}
|
|
@@ -3226,11 +3351,11 @@ var ParallAgentGateway = class {
|
|
|
3226
3351
|
return false;
|
|
3227
3352
|
const dedupeKey = `${task.id}:${task.updated_at}`;
|
|
3228
3353
|
if (this.dispatchedTasks.has(dedupeKey)) {
|
|
3229
|
-
this.opts.log?.info(`
|
|
3354
|
+
this.opts.log?.info(`skipping already-dispatched task ${task.identifier ?? task.id}`);
|
|
3230
3355
|
return false;
|
|
3231
3356
|
}
|
|
3232
3357
|
this.dispatchedTasks.add(dedupeKey);
|
|
3233
|
-
this.opts.log?.info(`
|
|
3358
|
+
this.opts.log?.info(`task assigned: ${task.identifier ?? task.id} "${task.title}"`);
|
|
3234
3359
|
const parts = [`Title: ${task.title}`];
|
|
3235
3360
|
parts.push(`Status: ${task.status}`, `Priority: ${task.priority}`);
|
|
3236
3361
|
if (task.project_id)
|
|
@@ -3271,7 +3396,7 @@ var ParallAgentGateway = class {
|
|
|
3271
3396
|
const isAssignee = task.assignee_id === this.opts.agentUserId;
|
|
3272
3397
|
const isCreatorUpdate = opts.allowCreator === true && task.creator_id === this.opts.agentUserId;
|
|
3273
3398
|
if (!isAssignee && !isCreatorUpdate) {
|
|
3274
|
-
this.opts.log?.info(`
|
|
3399
|
+
this.opts.log?.info(`skipping stale task dispatch ${ackSourceId ?? taskId} \u2014 assigned to ${task.assignee_id}, creator ${task.creator_id}`);
|
|
3275
3400
|
return true;
|
|
3276
3401
|
}
|
|
3277
3402
|
return this.handleTaskAssignment(task, ackSourceId);
|
|
@@ -3289,7 +3414,7 @@ var ParallAgentGateway = class {
|
|
|
3289
3414
|
} catch (err) {
|
|
3290
3415
|
const status = err?.status;
|
|
3291
3416
|
if (status === 404) {
|
|
3292
|
-
this.opts.log?.info(`
|
|
3417
|
+
this.opts.log?.info(`skipping deleted comment ${commentId}, acking stale dispatch`);
|
|
3293
3418
|
this.dispatchedTasks.delete(dedupeKey);
|
|
3294
3419
|
return true;
|
|
3295
3420
|
}
|
|
@@ -3301,7 +3426,7 @@ var ParallAgentGateway = class {
|
|
|
3301
3426
|
return true;
|
|
3302
3427
|
}
|
|
3303
3428
|
if (comment.hints?.no_reply) {
|
|
3304
|
-
this.opts.log?.info(`
|
|
3429
|
+
this.opts.log?.info(`skipping no_reply task comment ${commentId}, acking stale dispatch`);
|
|
3305
3430
|
this.dispatchedTasks.delete(dedupeKey);
|
|
3306
3431
|
return true;
|
|
3307
3432
|
}
|
|
@@ -3311,7 +3436,7 @@ var ParallAgentGateway = class {
|
|
|
3311
3436
|
} catch {
|
|
3312
3437
|
}
|
|
3313
3438
|
const taskLabel = task ? `${task.identifier ?? task.id} "${task.title}"` : taskId;
|
|
3314
|
-
this.opts.log?.info(`
|
|
3439
|
+
this.opts.log?.info(`task comment on ${taskLabel} by ${actorId ?? "unknown"}`);
|
|
3315
3440
|
const parts = [];
|
|
3316
3441
|
if (task) {
|
|
3317
3442
|
parts.push(`Task: ${task.title} (prll://${task.id})`);
|
|
@@ -3361,10 +3486,10 @@ var ParallAgentGateway = class {
|
|
|
3361
3486
|
} catch (err) {
|
|
3362
3487
|
const status = err?.status;
|
|
3363
3488
|
if (status === 404) {
|
|
3364
|
-
this.opts.log?.warn(`
|
|
3489
|
+
this.opts.log?.warn(`schedule run ${runId} not accessible (404), acking stale dispatch`);
|
|
3365
3490
|
return true;
|
|
3366
3491
|
}
|
|
3367
|
-
this.opts.log?.warn(`
|
|
3492
|
+
this.opts.log?.warn(`schedule run fetch failed for ${runId}, leaving pending: ${String(err)}`);
|
|
3368
3493
|
return false;
|
|
3369
3494
|
}
|
|
3370
3495
|
if (!run)
|
|
@@ -3378,7 +3503,7 @@ var ParallAgentGateway = class {
|
|
|
3378
3503
|
if (this.dispatchedTasks.has(dedupeKey))
|
|
3379
3504
|
return false;
|
|
3380
3505
|
this.dispatchedTasks.add(dedupeKey);
|
|
3381
|
-
this.opts.log?.info(`
|
|
3506
|
+
this.opts.log?.info(`schedule fired: ${run.id} (schedule ${run.schedule_id})`);
|
|
3382
3507
|
const event = {
|
|
3383
3508
|
type: "schedule",
|
|
3384
3509
|
// Route by schedule_id (not attached chat_id) so concurrent fires of
|
|
@@ -3415,10 +3540,10 @@ var ParallAgentGateway = class {
|
|
|
3415
3540
|
} catch (err) {
|
|
3416
3541
|
const status = err?.status;
|
|
3417
3542
|
if (status === 404 || status === 403) {
|
|
3418
|
-
this.opts.log?.warn(`
|
|
3543
|
+
this.opts.log?.warn(`approval ${approvalId} not accessible (${status}), acking stale dispatch`);
|
|
3419
3544
|
return true;
|
|
3420
3545
|
}
|
|
3421
|
-
this.opts.log?.warn(`
|
|
3546
|
+
this.opts.log?.warn(`approval fetch failed for ${approvalId}, leaving pending: ${String(err)}`);
|
|
3422
3547
|
return false;
|
|
3423
3548
|
}
|
|
3424
3549
|
if (!approval)
|
|
@@ -3429,7 +3554,7 @@ var ParallAgentGateway = class {
|
|
|
3429
3554
|
if (this.dispatchedTasks.has(dedupeKey))
|
|
3430
3555
|
return false;
|
|
3431
3556
|
this.dispatchedTasks.add(dedupeKey);
|
|
3432
|
-
this.opts.log?.info(`
|
|
3557
|
+
this.opts.log?.info(`approval decided: ${approval.id} (${approval.status})`);
|
|
3433
3558
|
const statusLabel = approval.status === "approved" ? "Approved" : "Rejected";
|
|
3434
3559
|
const execInfo = approval.execution_status ? ` | execution: ${approval.execution_status}` : "";
|
|
3435
3560
|
const body = `${statusLabel}: ${approval.title}${execInfo}`;
|
|
@@ -3479,14 +3604,14 @@ var ParallAgentGateway = class {
|
|
|
3479
3604
|
try {
|
|
3480
3605
|
dispatched = await this.handleTaskDispatch(item.task_id, item.source_id ?? item.task_id);
|
|
3481
3606
|
} catch (err) {
|
|
3482
|
-
this.opts.log?.warn(`
|
|
3607
|
+
this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
|
|
3483
3608
|
continue;
|
|
3484
3609
|
}
|
|
3485
3610
|
} else if (item.event_type === "task_update" && item.task_id) {
|
|
3486
3611
|
try {
|
|
3487
3612
|
dispatched = await this.handleTaskDispatch(item.task_id, item.source_id ?? item.task_id, { allowCreator: true });
|
|
3488
3613
|
} catch (err) {
|
|
3489
|
-
this.opts.log?.warn(`
|
|
3614
|
+
this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
|
|
3490
3615
|
continue;
|
|
3491
3616
|
}
|
|
3492
3617
|
} else if (item.event_type === "task_comment" && item.source_id && item.task_id) {
|
|
@@ -3508,7 +3633,7 @@ var ParallAgentGateway = class {
|
|
|
3508
3633
|
msg = null;
|
|
3509
3634
|
} else {
|
|
3510
3635
|
msgFetchFailed = true;
|
|
3511
|
-
this.opts.log?.warn(`
|
|
3636
|
+
this.opts.log?.warn(`catch-up message fetch failed for ${item.source_id}, leaving pending: ${String(err)}`);
|
|
3512
3637
|
}
|
|
3513
3638
|
}
|
|
3514
3639
|
if (msgFetchFailed) {
|
|
@@ -3539,13 +3664,13 @@ var ParallAgentGateway = class {
|
|
|
3539
3664
|
});
|
|
3540
3665
|
}
|
|
3541
3666
|
} catch (err) {
|
|
3542
|
-
this.opts.log?.warn(`
|
|
3667
|
+
this.opts.log?.warn(`catch-up dispatch ${item.id} (${item.event_type}) failed: ${String(err)}`);
|
|
3543
3668
|
}
|
|
3544
3669
|
}
|
|
3545
3670
|
cursor = !this.shuttingDown && page.has_more ? page.next_cursor : void 0;
|
|
3546
3671
|
} while (cursor);
|
|
3547
3672
|
if (processed > 0 || skippedOld > 0) {
|
|
3548
|
-
this.opts.log?.info(`
|
|
3673
|
+
this.opts.log?.info(`dispatch catch-up: processed ${processed}, skipped ${skippedOld} old item(s)`);
|
|
3549
3674
|
}
|
|
3550
3675
|
}
|
|
3551
3676
|
async handleHello(data) {
|
|
@@ -3554,7 +3679,7 @@ var ParallAgentGateway = class {
|
|
|
3554
3679
|
const intervalSec = data.heartbeat_interval > 0 ? data.heartbeat_interval : 30;
|
|
3555
3680
|
try {
|
|
3556
3681
|
const count = await fetchAllChats(client, config.org_id, this.chatInfoMap);
|
|
3557
|
-
log2?.info(`
|
|
3682
|
+
log2?.info(`WebSocket connected, ${count} chats cached`);
|
|
3558
3683
|
await this.opts.onSessionReady?.({
|
|
3559
3684
|
activeSessionId: this.activeSessionId,
|
|
3560
3685
|
ws: this.opts.ws,
|
|
@@ -3568,7 +3693,7 @@ var ParallAgentGateway = class {
|
|
|
3568
3693
|
const expectedMs = intervalSec * 1e3;
|
|
3569
3694
|
const drift = now - this.lastHeartbeatAt - expectedMs;
|
|
3570
3695
|
if (drift > 15e3) {
|
|
3571
|
-
log2?.warn(`
|
|
3696
|
+
log2?.warn(`heartbeat drift ${drift}ms \u2014 event loop may be blocked`);
|
|
3572
3697
|
}
|
|
3573
3698
|
this.lastHeartbeatAt = now;
|
|
3574
3699
|
if (this.opts.ws.state !== "connected")
|
|
@@ -3584,10 +3709,10 @@ var ParallAgentGateway = class {
|
|
|
3584
3709
|
const isFirstHello = !this.hadSuccessfulHello;
|
|
3585
3710
|
this.hadSuccessfulHello = true;
|
|
3586
3711
|
this.catchUpFromDispatch(isFirstHello).catch((err) => {
|
|
3587
|
-
log2?.warn(`
|
|
3712
|
+
log2?.warn(`dispatch catch-up failed: ${String(err)}`);
|
|
3588
3713
|
});
|
|
3589
3714
|
} catch (err) {
|
|
3590
|
-
log2?.error(`
|
|
3715
|
+
log2?.error(`failed to fetch chats: ${String(err)}`);
|
|
3591
3716
|
}
|
|
3592
3717
|
}
|
|
3593
3718
|
// Resolves when in-flight dispatches hit 0 or the deadline elapses.
|
|
@@ -3613,12 +3738,12 @@ var ParallAgentGateway = class {
|
|
|
3613
3738
|
async shutdown() {
|
|
3614
3739
|
this.shuttingDown = true;
|
|
3615
3740
|
if (this.inFlightDispatches > 0) {
|
|
3616
|
-
this.opts.log?.info(`
|
|
3741
|
+
this.opts.log?.info(`draining ${this.inFlightDispatches} in-flight dispatch(es), deadline ${this.SHUTDOWN_DEADLINE_MS}ms`);
|
|
3617
3742
|
await this.waitForDrain(this.SHUTDOWN_DEADLINE_MS);
|
|
3618
3743
|
if (this.inFlightDispatches > 0) {
|
|
3619
|
-
this.opts.log?.warn(`
|
|
3744
|
+
this.opts.log?.warn(`drain deadline hit; ${this.inFlightDispatches} dispatch(es) still running \u2014 they will be killed by process exit`);
|
|
3620
3745
|
} else {
|
|
3621
|
-
this.opts.log?.info(`
|
|
3746
|
+
this.opts.log?.info(`drain complete`);
|
|
3622
3747
|
}
|
|
3623
3748
|
}
|
|
3624
3749
|
if (this.heartbeatTimer)
|
|
@@ -3629,7 +3754,7 @@ var ParallAgentGateway = class {
|
|
|
3629
3754
|
this.activeDispatches.clear();
|
|
3630
3755
|
await this.opts.onBeforeDisconnect?.();
|
|
3631
3756
|
this.opts.ws.disconnect();
|
|
3632
|
-
this.opts.log?.info(`
|
|
3757
|
+
this.opts.log?.info(`disconnected`);
|
|
3633
3758
|
}
|
|
3634
3759
|
};
|
|
3635
3760
|
|
|
@@ -4248,8 +4373,8 @@ function resolveClaudeAgentConfig(env = process.env) {
|
|
|
4248
4373
|
const apiKey = requireEnv(env, "PRLL_API_KEY");
|
|
4249
4374
|
const orgId = requireEnv(env, "PRLL_ORG_ID");
|
|
4250
4375
|
const claudeHome = resolvePath(env.PRLL_CLAUDE_HOME?.trim() || env.HOME || os2.homedir());
|
|
4251
|
-
const stateDir = resolvePath(env.
|
|
4252
|
-
const workspaceDir = resolvePath(env.
|
|
4376
|
+
const stateDir = resolvePath(env.PRLL_STATE_DIR?.trim() || path4.join(claudeHome, ".parall-agent"));
|
|
4377
|
+
const workspaceDir = resolvePath(env.PRLL_WORKSPACE_DIR?.trim() || path4.join(stateDir, "workspace"));
|
|
4253
4378
|
const additionalDirs = parseList(env.PRLL_CLAUDE_ADD_DIRS).map(resolvePath);
|
|
4254
4379
|
return {
|
|
4255
4380
|
apiUrl,
|
|
@@ -5149,7 +5274,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
|
|
|
5149
5274
|
const pending = this.pendingInjections.get(sessionKey) ?? 0;
|
|
5150
5275
|
if (pending > 0) {
|
|
5151
5276
|
this.pendingInjections.delete(sessionKey);
|
|
5152
|
-
context.log?.info?.(`
|
|
5277
|
+
context.log?.info?.(`consuming ${pending} steer turn(s)`);
|
|
5153
5278
|
for (let i = 0; i < pending; i++) {
|
|
5154
5279
|
yield* this.consumeSteerTurn(sessionKey, context.log);
|
|
5155
5280
|
}
|
|
@@ -5166,7 +5291,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
|
|
|
5166
5291
|
promptBody = prepared.body;
|
|
5167
5292
|
releasePreparedAttachments = pinLocalAttachmentPaths(prepared.attachments.images);
|
|
5168
5293
|
} catch (err) {
|
|
5169
|
-
context.log?.warn?.(`
|
|
5294
|
+
context.log?.warn?.(`failed to prepare local attachments: ${String(err)}`);
|
|
5170
5295
|
}
|
|
5171
5296
|
try {
|
|
5172
5297
|
yield* this.runTurn(sessionKey, promptBody, context.log);
|
|
@@ -5195,12 +5320,16 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
|
|
|
5195
5320
|
}
|
|
5196
5321
|
this.opts.sessionManager.cleanupFork(fork.sessionKey);
|
|
5197
5322
|
}
|
|
5198
|
-
|
|
5199
|
-
this.shuttingDown = true;
|
|
5323
|
+
resetProcesses() {
|
|
5200
5324
|
for (const [sessionKey, state] of this.processes) {
|
|
5201
5325
|
this.killProcess(sessionKey, state);
|
|
5202
5326
|
}
|
|
5203
5327
|
this.processes.clear();
|
|
5328
|
+
this.pendingInjections.clear();
|
|
5329
|
+
}
|
|
5330
|
+
async shutdown() {
|
|
5331
|
+
this.shuttingDown = true;
|
|
5332
|
+
this.resetProcesses();
|
|
5204
5333
|
await this.opts.sessionManager.shutdownAll();
|
|
5205
5334
|
}
|
|
5206
5335
|
static STEER_TURN_TIMEOUT_MS = 1e4;
|
|
@@ -5217,7 +5346,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
|
|
|
5217
5346
|
async *consumeSteerTurn(sessionKey, log2) {
|
|
5218
5347
|
const state = this.processes.get(sessionKey);
|
|
5219
5348
|
if (!state || state.done) {
|
|
5220
|
-
throw new Error("
|
|
5349
|
+
throw new Error("process dead during steer consumption");
|
|
5221
5350
|
}
|
|
5222
5351
|
const groupKey = randomUUID();
|
|
5223
5352
|
const parserNext = state.parser.next();
|
|
@@ -5227,7 +5356,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
|
|
|
5227
5356
|
]);
|
|
5228
5357
|
if (firstRead.kind === "timeout") {
|
|
5229
5358
|
state.steerReadPending = parserNext;
|
|
5230
|
-
log2?.info?.(`
|
|
5359
|
+
log2?.info?.(`steer turn timeout \u2014 steer was incorporated into previous turn`);
|
|
5231
5360
|
return;
|
|
5232
5361
|
}
|
|
5233
5362
|
let next = firstRead.result;
|
|
@@ -5235,7 +5364,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
|
|
|
5235
5364
|
if (next.done) {
|
|
5236
5365
|
state.done = true;
|
|
5237
5366
|
this.processes.delete(sessionKey);
|
|
5238
|
-
throw new Error("
|
|
5367
|
+
throw new Error("process exited while consuming steer turn");
|
|
5239
5368
|
}
|
|
5240
5369
|
const parsed = next.value;
|
|
5241
5370
|
if (parsed.type === "session_id") {
|
|
@@ -5306,7 +5435,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
|
|
|
5306
5435
|
const detail = state.handle.stderrChunks.join("").trim();
|
|
5307
5436
|
const exit = await state.handle.exitPromise.catch(() => ({ code: null, signal: null }));
|
|
5308
5437
|
if (detail) {
|
|
5309
|
-
log2?.warn?.(`
|
|
5438
|
+
log2?.warn?.(`subprocess stderr: ${detail}`);
|
|
5310
5439
|
}
|
|
5311
5440
|
if (!sawError) {
|
|
5312
5441
|
yield {
|
|
@@ -5350,7 +5479,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
|
|
|
5350
5479
|
}
|
|
5351
5480
|
ensureProcess(sessionKey, log2) {
|
|
5352
5481
|
if (this.shuttingDown) {
|
|
5353
|
-
throw new Error("
|
|
5482
|
+
throw new Error("adapter shutting down, refusing new process");
|
|
5354
5483
|
}
|
|
5355
5484
|
const existing = this.processes.get(sessionKey);
|
|
5356
5485
|
if (existing && !existing.done) {
|
|
@@ -5373,7 +5502,7 @@ var ClaudeCodeAdapter = class _ClaudeCodeAdapter {
|
|
|
5373
5502
|
spawnProcess(sessionKey, log2) {
|
|
5374
5503
|
const args = this.buildArgs(sessionKey);
|
|
5375
5504
|
const env = buildSpawnEnv(process.env, this.opts.claudeHome, this.buildPlaceholderContext(sessionKey), { allowApiKey: this.opts.allowApiKey, effortLevel: this._effortLevel });
|
|
5376
|
-
log2?.info(`
|
|
5505
|
+
log2?.info(`spawn long-lived ${this.opts.claudeBin} (session ${sessionKey}, model=${this._model || "default"}, effort=${this._effortLevel || "default"}, mode=${this.opts.permissionMode})`);
|
|
5377
5506
|
const proc = spawn(IS_WIN32 ? quoteWin32Arg(this.opts.claudeBin) : this.opts.claudeBin, IS_WIN32 ? args.map(quoteWin32Arg) : args, {
|
|
5378
5507
|
cwd: this.opts.workspaceDir,
|
|
5379
5508
|
env,
|
|
@@ -5628,6 +5757,13 @@ var ClaudeSessionManager = class _ClaudeSessionManager {
|
|
|
5628
5757
|
}
|
|
5629
5758
|
}
|
|
5630
5759
|
}
|
|
5760
|
+
clearMainSession() {
|
|
5761
|
+
this.sessionIds.delete(this.mainSessionKey);
|
|
5762
|
+
try {
|
|
5763
|
+
fs6.unlinkSync(this.stateFilePath);
|
|
5764
|
+
} catch {
|
|
5765
|
+
}
|
|
5766
|
+
}
|
|
5631
5767
|
restore() {
|
|
5632
5768
|
try {
|
|
5633
5769
|
const raw = fs6.readFileSync(this.stateFilePath, "utf8");
|
|
@@ -5673,6 +5809,7 @@ function ensureClaudeWorkspace(workspaceDir, log2, agentIdentity) {
|
|
|
5673
5809
|
|
|
5674
5810
|
// ts/claude-agent/dist/index.js
|
|
5675
5811
|
var log = createLogger("claude-agent");
|
|
5812
|
+
var activeLog = log;
|
|
5676
5813
|
async function getAgentMeWithLegacyFallback(client, orgId) {
|
|
5677
5814
|
try {
|
|
5678
5815
|
return await client.getAgentMe(orgId);
|
|
@@ -5684,7 +5821,27 @@ async function getAgentMeWithLegacyFallback(client, orgId) {
|
|
|
5684
5821
|
return { ...user, agent_profile: null };
|
|
5685
5822
|
}
|
|
5686
5823
|
}
|
|
5824
|
+
function resolveProviderEnv() {
|
|
5825
|
+
const pc = parseProviderConfig(process.env);
|
|
5826
|
+
if (!pc)
|
|
5827
|
+
return;
|
|
5828
|
+
clearAllProviderCreds(process.env);
|
|
5829
|
+
const source = llmSource(pc);
|
|
5830
|
+
if (source === "parall") {
|
|
5831
|
+
process.env.ANTHROPIC_AUTH_TOKEN = process.env.PRLL_API_KEY;
|
|
5832
|
+
process.env.ANTHROPIC_BASE_URL = `${process.env.PRLL_API_URL}/api/llm`;
|
|
5833
|
+
process.env.PRLL_CLAUDE_ALLOW_API_KEY = "1";
|
|
5834
|
+
} else if (source === "custom") {
|
|
5835
|
+
if (pc.anthropic_auth_token) {
|
|
5836
|
+
process.env.ANTHROPIC_AUTH_TOKEN = pc.anthropic_auth_token;
|
|
5837
|
+
process.env.PRLL_CLAUDE_ALLOW_API_KEY = "1";
|
|
5838
|
+
}
|
|
5839
|
+
if (pc.anthropic_base_url)
|
|
5840
|
+
process.env.ANTHROPIC_BASE_URL = pc.anthropic_base_url;
|
|
5841
|
+
}
|
|
5842
|
+
}
|
|
5687
5843
|
async function main() {
|
|
5844
|
+
resolveProviderEnv();
|
|
5688
5845
|
const config = resolveClaudeAgentConfig(process.env);
|
|
5689
5846
|
if (config.allowApiKey && (process.env.ANTHROPIC_API_KEY || process.env.ANTHROPIC_AUTH_TOKEN)) {
|
|
5690
5847
|
log.warn("PRLL_CLAUDE_ALLOW_API_KEY=1 \u2014 ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN will reach the Claude CLI; billing routes through Anthropic API pay-per-use instead of Claude.ai OAuth.");
|
|
@@ -5696,28 +5853,30 @@ async function main() {
|
|
|
5696
5853
|
});
|
|
5697
5854
|
const me = await getAgentMeWithLegacyFallback(client, config.orgId);
|
|
5698
5855
|
const agentUserId = me.id;
|
|
5699
|
-
|
|
5856
|
+
const agentLog = childLogger(log, agentUserId);
|
|
5857
|
+
activeLog = agentLog;
|
|
5858
|
+
ensureClaudeWorkspace(config.workspaceDir, agentLog, {
|
|
5700
5859
|
userId: agentUserId,
|
|
5701
5860
|
displayName: me.display_name,
|
|
5702
5861
|
description: me.agent_profile?.description ?? void 0
|
|
5703
5862
|
});
|
|
5704
5863
|
const runtimeKey = config.runtimeKey || buildClaudeRuntimeKey(agentUserId);
|
|
5705
5864
|
const sessionStateFilePath = sessionStateFilePathForRuntime(config.stateDir, runtimeKey);
|
|
5706
|
-
const sessionManager = new ClaudeSessionManager(runtimeKey, sessionStateFilePath,
|
|
5865
|
+
const sessionManager = new ClaudeSessionManager(runtimeKey, sessionStateFilePath, agentLog);
|
|
5707
5866
|
const resolvedWsUrl = resolveWsUrl(config.apiUrl, config.wsUrl, config.swimlaneName);
|
|
5708
5867
|
const ws = new ParallWs({
|
|
5709
5868
|
getTicket: () => client.getWsTicket(),
|
|
5710
5869
|
wsUrl: resolvedWsUrl
|
|
5711
5870
|
});
|
|
5712
|
-
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "claude-code", log });
|
|
5871
|
+
const configMgr = createPlatformConfigManager({ client, stateDir: config.stateDir, runtimeType: "claude-code", log: agentLog });
|
|
5713
5872
|
const platformDefaults = await configMgr.fetch();
|
|
5714
5873
|
let platformManaged = isPlatformManagedProfile(me.agent_profile);
|
|
5715
5874
|
const resolvedModel = platformManaged ? platformDefaults.model ?? config.model : config.model;
|
|
5716
5875
|
const resolvedEffort = platformManaged ? platformDefaults.thinkingEffort ?? (process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || void 0) : process.env.CLAUDE_CODE_EFFORT_LEVEL?.trim() || void 0;
|
|
5717
5876
|
if (platformDefaults.model)
|
|
5718
|
-
|
|
5877
|
+
agentLog.info(`platform config: model=${platformDefaults.model} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
5719
5878
|
if (platformDefaults.thinkingEffort)
|
|
5720
|
-
|
|
5879
|
+
agentLog.info(`platform config: thinking_effort=${platformDefaults.thinkingEffort} (${platformManaged ? "applied" : "ignored, model_management=self"})`);
|
|
5721
5880
|
const adapter = new ClaudeCodeAdapter({
|
|
5722
5881
|
claudeBin: config.claudeBin,
|
|
5723
5882
|
claudeHome: config.claudeHome,
|
|
@@ -5762,7 +5921,7 @@ async function main() {
|
|
|
5762
5921
|
claude_home: config.claudeHome
|
|
5763
5922
|
},
|
|
5764
5923
|
dispatchAdapter: adapter,
|
|
5765
|
-
log,
|
|
5924
|
+
log: agentLog,
|
|
5766
5925
|
shutdownDeadlineMs: parseShutdownDeadlineMs(process.env.PRLL_SHUTDOWN_DEADLINE_MS),
|
|
5767
5926
|
contextFilePathForSession: (sessionKey) => contextFilePathForSession(config.stateDir, sessionKey),
|
|
5768
5927
|
stepIdFilePathForSession: (sessionKey) => stepIdFilePathForSession(config.stateDir, sessionKey),
|
|
@@ -5790,14 +5949,22 @@ async function main() {
|
|
|
5790
5949
|
// gateway's shuttingDown flag alone does not tear them down. Piggyback on
|
|
5791
5950
|
// onBeforeDisconnect to close stdin and SIGTERM any survivors after
|
|
5792
5951
|
// in-flight drains finish.
|
|
5793
|
-
onBeforeDisconnect: () => adapter.shutdown()
|
|
5952
|
+
onBeforeDisconnect: () => adapter.shutdown(),
|
|
5953
|
+
onNewSession: async () => {
|
|
5954
|
+
sessionManager.clearMainSession();
|
|
5955
|
+
adapter.resetProcesses();
|
|
5956
|
+
},
|
|
5957
|
+
onSessionStale: () => {
|
|
5958
|
+
sessionManager.clearMainSession();
|
|
5959
|
+
adapter.resetProcesses();
|
|
5960
|
+
}
|
|
5794
5961
|
});
|
|
5795
5962
|
const abortController = new AbortController();
|
|
5796
5963
|
const abort = () => abortController.abort();
|
|
5797
5964
|
process.on("SIGINT", abort);
|
|
5798
5965
|
process.on("SIGTERM", abort);
|
|
5799
5966
|
try {
|
|
5800
|
-
|
|
5967
|
+
agentLog.info(`starting self-hosted Claude runtime for ${me.display_name} (${agentUserId})`);
|
|
5801
5968
|
await gateway.run(abortController.signal);
|
|
5802
5969
|
} finally {
|
|
5803
5970
|
process.off("SIGINT", abort);
|
|
@@ -5805,6 +5972,6 @@ async function main() {
|
|
|
5805
5972
|
}
|
|
5806
5973
|
}
|
|
5807
5974
|
main().catch((err) => {
|
|
5808
|
-
|
|
5975
|
+
activeLog.error(`fatal: ${String(err)}`);
|
|
5809
5976
|
process.exitCode = 1;
|
|
5810
5977
|
});
|