@parall/parall 1.43.0 → 1.45.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.
@@ -1 +1 @@
1
- {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,uGAAuG;AACvG,KAAK,qBAAqB,CAAC,CAAC,GAAG,OAAO,IAAI,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;AACnF,OAAO,EAQL,KAAK,eAAe,EAGrB,MAAM,oBAAoB,CAAC;AAqB5B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAiKxD,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,IAAI,EAAE,aAAa,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,eAAe,CA0KlB;AAED,eAAO,MAAM,aAAa,EAAE,qBAAqB,CAAC,qBAAqB,CAiKtE,CAAC"}
1
+ {"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,uGAAuG;AACvG,KAAK,qBAAqB,CAAC,CAAC,GAAG,OAAO,IAAI,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;AACnF,OAAO,EASL,KAAK,eAAe,EAGrB,MAAM,oBAAoB,CAAC;AAqB5B,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AA2KxD,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,IAAI,EAAE,aAAa,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,eAAe,CA0KlB;AAED,eAAO,MAAM,aAAa,EAAE,qBAAqB,CAAC,qBAAqB,CAqLtE,CAAC"}
package/dist/gateway.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ParallAgentGateway, capabilityBinDir, parseShutdownDeadlineMs, parseForkDeadlineMs, parseDispatchDeadlineMs, initAgentTelemetry, createOtelLogger, } from '@parall/agent-core';
1
+ import { ParallAgentGateway, capabilityBinDir, dispatchLaneContextDir, parseShutdownDeadlineMs, parseForkDeadlineMs, parseDispatchDeadlineMs, initAgentTelemetry, createOtelLogger, } from '@parall/agent-core';
2
2
  import { appendPreparedLocalAttachmentRefs, ensureLocalAttachmentGitExclude, pinLocalAttachmentPaths, } from '@parall/agent-core/internal/attachment-input';
3
3
  import { ApiError, ParallClient, ParallWs } from '@parall/sdk';
4
4
  import * as crypto from 'node:crypto';
@@ -11,6 +11,15 @@ import { fetchAndApplyPlatformConfig, getChannelCapabilityKeys } from './config-
11
11
  import { startWikiHelper } from './wiki-helper.js';
12
12
  import { SessionManager } from './oc-session.js';
13
13
  import { cleanupForkSession, clearSessionState, forkOrchestratorSession, resolveSessionId, resolveTranscriptFile, } from './fork.js';
14
+ /**
15
+ * Per-session dispatch context file (PRLL_CONTEXT_FILE contract) — same
16
+ * encoding as the claude/codex bridges: base64url(sessionKey) under the
17
+ * state dir's dispatch-context/.
18
+ */
19
+ function sessionContextFilePath(stateDir, sessionKey) {
20
+ const fileName = Buffer.from(sessionKey).toString('base64url');
21
+ return path.join(stateDir, 'dispatch-context', `${fileName}.json`);
22
+ }
14
23
  function resolveWsUrl(account) {
15
24
  if (account.config.ws_url)
16
25
  return account.config.ws_url;
@@ -404,6 +413,18 @@ export const parallGateway = {
404
413
  runtimeKey: orchestratorKey,
405
414
  runtimeRef: { hostname: os.hostname(), pid: process.pid },
406
415
  dispatchAdapter,
416
+ // Opts openclaw into the dispatch ledger (claim/fold/complete +
417
+ // idempotent reply effects) — the same shared-gateway machinery
418
+ // claude/codex ride; openclaw stays buffer-only (no mid-turn steer),
419
+ // which the ledger does not require. Replies already flow through
420
+ // @parall/cli, which reads PRLL_CONTEXT_DIR (injected in hooks.ts)
421
+ // to bind dispatch_lane + reply effect keys.
422
+ // Design: docs/engineering-design/dispatch-convergence-design.md §7 (S2).
423
+ dispatchContextDir: dispatchLaneContextDir(stateDir),
424
+ // Per-session context file (PRLL_CONTEXT_FILE contract) — the CLI's
425
+ // TYPED dispatch binding (parall tasks update → task_update effect)
426
+ // reads lane/event/task fields from this file, not the lane dir.
427
+ contextFilePathForSession: (sessionKey) => sessionContextFilePath(stateDir, sessionKey),
407
428
  log: otelLog,
408
429
  // Live capability view for hint routing: the channel reply hint
409
430
  // points at the vendor CLI only while the grant is active.
@@ -428,6 +449,12 @@ export const parallGateway = {
428
449
  wikiMountRoot,
429
450
  ws,
430
451
  orchestratorSessionKey: orchestratorKey,
452
+ dispatchContextDir: dispatchLaneContextDir(stateDir),
453
+ contextFilePathForSession: (sessionKey) => sessionContextFilePath(stateDir, sessionKey),
454
+ // Lease-renewal bridge: openclaw tool calls flow through hooks,
455
+ // not the RuntimeEvent stream, so hook activity must renew the
456
+ // ledger lanes or a long exec outlives the lease (STALE_LANE).
457
+ touchRuntimeActivity: (sessionKey) => gateway.touchRuntimeActivity(sessionKey),
431
458
  });
432
459
  },
433
460
  onSessionBinding: async ({ sessionKey, agentSessionId }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAgH7D,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,iBAAiB,QAmLzD"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAgH7D,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,iBAAiB,QA+OzD"}
package/dist/hooks.js CHANGED
@@ -118,6 +118,45 @@ export function registerParallHooks(api) {
118
118
  };
119
119
  });
120
120
  const pendingSendCalls = new Map();
121
+ // Lane lease keepalive across the tool-call lifecycle. OpenClaw tool calls
122
+ // bypass the RuntimeEvent stream, so lane renewal must ride these hooks —
123
+ // and a ONE-SHOT touch at tool start is not enough: a single exec running
124
+ // past the lane TTL (10min server-side) would still be dethroned mid-call
125
+ // and the eventual CLI reply would die with STALE_LANE. While any tool call
126
+ // is in flight for a session, an unref'd interval keeps touching; the
127
+ // half-TTL throttle in the ledger decides when a real heartbeat is sent.
128
+ const laneRenewals = new Map();
129
+ const LANE_RENEW_INTERVAL_MS = 180_000;
130
+ const beginToolRenewal = (sessionKey) => {
131
+ const accountId = extractAccountIdFromSessionKey(sessionKey);
132
+ const state = accountId ? getParallAccountState(accountId) : undefined;
133
+ if (!state?.touchRuntimeActivity)
134
+ return;
135
+ state.touchRuntimeActivity(sessionKey);
136
+ const entry = laneRenewals.get(sessionKey) ?? { count: 0 };
137
+ entry.count++;
138
+ if (!entry.timer) {
139
+ entry.timer = setInterval(() => state.touchRuntimeActivity?.(sessionKey), LANE_RENEW_INTERVAL_MS);
140
+ entry.timer.unref?.();
141
+ }
142
+ laneRenewals.set(sessionKey, entry);
143
+ };
144
+ const endToolRenewal = (sessionKey) => {
145
+ const entry = laneRenewals.get(sessionKey);
146
+ if (entry) {
147
+ entry.count--;
148
+ if (entry.count <= 0) {
149
+ if (entry.timer)
150
+ clearInterval(entry.timer);
151
+ laneRenewals.delete(sessionKey);
152
+ }
153
+ }
154
+ const accountId = extractAccountIdFromSessionKey(sessionKey);
155
+ const state = accountId ? getParallAccountState(accountId) : undefined;
156
+ // Final touch on completion — covers the reply window right after a
157
+ // long tool call ends.
158
+ state?.touchRuntimeActivity?.(sessionKey);
159
+ };
121
160
  // before_tool_call -> (1) update dispatch metrics, (2) await step creation, (3) ENV injection
122
161
  // OpenClaw tool calls bypass the RuntimeEvent stream (they go through hooks, not the
123
162
  // gateway-base for-await loop), so dispatch metrics must be updated here as well.
@@ -125,6 +164,9 @@ export function registerParallHooks(api) {
125
164
  const sessionKey = ctx.sessionKey;
126
165
  if (sessionKey) {
127
166
  recordToolCall(sessionKey);
167
+ // Every tool (not just exec) keeps the ledger lane alive for its
168
+ // whole lifetime — see laneRenewals above.
169
+ beginToolRenewal(sessionKey);
128
170
  if (event.toolName === 'exec' && event.toolCallId) {
129
171
  const command = event.params?.command;
130
172
  if (isParallSendCommand(command)) {
@@ -209,6 +251,20 @@ export function registerParallHooks(api) {
209
251
  injectedEnv.PRLL_TRIGGER_MESSAGE_ID = triggerMsgId;
210
252
  if (state.wikiMountRoot)
211
253
  injectedEnv.PRLL_WIKI_MOUNT_ROOT = state.wikiMountRoot;
254
+ // Dispatch-ledger contract: the CLI keys into this directory by send
255
+ // target to bind dispatch_lane + the reply:<dsp> effect key (idempotent
256
+ // replies). Set only when the gateway runs the ledger.
257
+ if (state.dispatchContextDir)
258
+ injectedEnv.PRLL_CONTEXT_DIR = state.dispatchContextDir;
259
+ // Per-session context file — the CLI's TYPED binding (parall tasks
260
+ // update → task_update:<dsp> effect) reads lane/event/task from here.
261
+ if (state.contextFilePathForSession) {
262
+ injectedEnv.PRLL_CONTEXT_FILE = state.contextFilePathForSession(sessionKey);
263
+ }
264
+ // Long tool calls bypass the RuntimeEvent stream — forward activity so
265
+ // the ledger lane lease renews (a >TTL exec would otherwise be dethroned
266
+ // mid-turn and the eventual CLI reply would die with STALE_LANE).
267
+ state.touchRuntimeActivity?.(sessionKey);
212
268
  // OpenClaw context (upstream doesn't inject these into exec env yet)
213
269
  injectedEnv.OPENCLAW_SESSION_KEY = sessionKey;
214
270
  if (event.toolCallId)
@@ -223,6 +279,8 @@ export function registerParallHooks(api) {
223
279
  });
224
280
  // after_tool_call -> track CLI outcomes + send tool_result step to AgentSession
225
281
  api.on('after_tool_call', async (event, ctx) => {
282
+ if (ctx.sessionKey)
283
+ endToolRenewal(ctx.sessionKey);
226
284
  if (ctx.sessionKey && event.toolCallId && pendingSendCalls.delete(event.toolCallId)) {
227
285
  recordMessageSend(ctx.sessionKey, !event.error);
228
286
  }