@parall/parall 1.64.0 → 1.65.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.
Files changed (2) hide show
  1. package/dist/index.bundle.mjs +320 -220
  2. package/package.json +3 -3
@@ -52248,6 +52248,67 @@ function describeRuntimeTurnTrigger(trigger) {
52248
52248
  }
52249
52249
  }
52250
52250
 
52251
+ // ../agent-core/dist/redact.js
52252
+ function redactSecrets(s, knownValues = []) {
52253
+ return maskKnownValues(s, knownValues).replace(/\b(agk|mck|cpk)_[A-Za-z0-9_-]+/g, "$1_***").replace(/\b(sk|pk|rk)-[A-Za-z0-9_-]{8,}/g, "$1-***").replace(/\bAKIA[0-9A-Z]{16}\b/g, "AKIA***").replace(/\b((?:bearer|basic)\s+)[A-Za-z0-9._~+/=-]+/gi, "$1***").replace(/(\b(?:[\w-]*[_-])?(?:api[_-]?key|key|token|secret|password|passwd|authorization)["']?\s*[=:]\s*)(?:"(?:\\.|[^"\\\r\n])*"|'(?:\\.|[^'\\\r\n])*'|[^\s&;,}\]"']+)/gi, "$1***").replace(/[A-Za-z0-9_-]{32,}/g, "***");
52254
+ }
52255
+ function maskKnownValues(s, knownValues) {
52256
+ let out = s;
52257
+ for (const v of knownValues) {
52258
+ if (typeof v === "string" && v.length >= 6)
52259
+ out = out.split(v).join("***");
52260
+ }
52261
+ return out;
52262
+ }
52263
+ function redactTurnOutcome(event, knownValues) {
52264
+ const redacted = { ...event };
52265
+ if (redacted.detail)
52266
+ redacted.detail = redactSecrets(redacted.detail, knownValues);
52267
+ if (redacted.raw) {
52268
+ redacted.raw = Object.fromEntries(Object.entries(redacted.raw).map(([k, v]) => [
52269
+ k,
52270
+ typeof v === "string" ? redactSecrets(v, knownValues) : v
52271
+ ]));
52272
+ }
52273
+ return redacted;
52274
+ }
52275
+ function describeTurnOutcomeFailure(outcome) {
52276
+ const retryNote = outcome.retryAt ? `, retry at ${outcome.retryAt}` : "";
52277
+ return {
52278
+ warn: `${outcome.outcome}${retryNote}${outcome.detail ? ` \u2014 ${outcome.detail}` : ""}`,
52279
+ stepMessage: `LLM turn ${outcome.outcome}${retryNote}${outcome.detail ? `: ${outcome.detail}` : ""}`
52280
+ };
52281
+ }
52282
+ function redactLogger(log, knownValues) {
52283
+ if (!log)
52284
+ return void 0;
52285
+ return {
52286
+ info: (message) => log.info(redactSecrets(message, knownValues)),
52287
+ warn: (message) => log.warn(redactSecrets(message, knownValues)),
52288
+ error: (message) => log.error(redactSecrets(message, knownValues)),
52289
+ ...log.child ? { child: (name) => redactLogger(log.child(name), knownValues) } : {}
52290
+ };
52291
+ }
52292
+
52293
+ // ../agent-core/dist/runtime-observation.js
52294
+ async function persistRuntimeObservation(persister, sessionId, target, event, log, knownSecrets = []) {
52295
+ const native = event.content.native && Object.fromEntries(Object.entries(event.content.native).map(([key, value]) => [
52296
+ key,
52297
+ typeof value !== "string" ? value : key.endsWith("_id") ? maskKnownValues(value, knownSecrets) : redactSecrets(value, knownSecrets)
52298
+ ]));
52299
+ try {
52300
+ await persister.persist(sessionId, "observation", {
52301
+ step_type: "observation",
52302
+ ...target,
52303
+ idempotency_key: `observation:${event.observationId}`,
52304
+ content: { ...event.content, ...native ? { native } : {} },
52305
+ projection: false
52306
+ });
52307
+ } catch (err) {
52308
+ log?.warn(`runtime observation could not be saved for ${sessionId}: ${String(err)}`);
52309
+ }
52310
+ }
52311
+
52251
52312
  // ../agent-core/dist/fork-prefix.js
52252
52313
  function sanitizeMeta(value) {
52253
52314
  return value.replace(/[\r\n]+/g, " ").replace(/[[\]|]/g, " ").trim();
@@ -52286,6 +52347,108 @@ function buildForkResultPrefix(results) {
52286
52347
  return blocks.join("\n\n") + "\n\n---\n\n";
52287
52348
  }
52288
52349
 
52350
+ // ../agent-core/dist/gateway-runtime-step.js
52351
+ import { randomUUID } from "node:crypto";
52352
+ async function createRuntimeStep(host, sessionId, target, runtimeEvent, stepIdFilePath, contextFilePath, laneContextFilePath2) {
52353
+ switch (runtimeEvent.type) {
52354
+ case "observation":
52355
+ await persistRuntimeObservation(host.stepPersister, sessionId, target, runtimeEvent, host.opts?.log, host.opts?.config ? [host.opts.config.api_key] : []);
52356
+ break;
52357
+ case "thinking":
52358
+ await host.stepPersister.persist(sessionId, "thinking", {
52359
+ step_type: "thinking",
52360
+ target_type: target.target_type,
52361
+ target_id: target.target_id,
52362
+ idempotency_key: randomUUID(),
52363
+ content: { text: runtimeEvent.text },
52364
+ group_key: runtimeEvent.groupKey
52365
+ });
52366
+ break;
52367
+ case "text":
52368
+ await host.stepPersister.persist(sessionId, "text", {
52369
+ step_type: "text",
52370
+ target_type: target.target_type,
52371
+ target_id: target.target_id,
52372
+ idempotency_key: randomUUID(),
52373
+ content: {
52374
+ text: runtimeEvent.text,
52375
+ suppressed: runtimeEvent.project !== true
52376
+ },
52377
+ projection: runtimeEvent.project === true,
52378
+ group_key: runtimeEvent.groupKey
52379
+ });
52380
+ break;
52381
+ case "tool_call": {
52382
+ const step = await host.stepPersister.persist(sessionId, "tool_call", {
52383
+ step_type: "tool_call",
52384
+ target_type: target.target_type,
52385
+ target_id: target.target_id,
52386
+ // call_id is session-unique for bridge runtimes (server-enforced),
52387
+ // so the bare form anchors the tool step pair across retries —
52388
+ // unlike parel's turn-scoped `tc:{turnId}:{callId}` (see
52389
+ // protocol-vectors/agent-steps.json).
52390
+ idempotency_key: `tc:${runtimeEvent.callId}`,
52391
+ content: {
52392
+ call_id: runtimeEvent.callId,
52393
+ tool_name: runtimeEvent.toolName,
52394
+ tool_input: runtimeEvent.input,
52395
+ status: "running",
52396
+ started_at: runtimeEvent.startedAt ?? (/* @__PURE__ */ new Date()).toISOString()
52397
+ },
52398
+ group_key: runtimeEvent.groupKey,
52399
+ runtime_key: runtimeEvent.callId
52400
+ });
52401
+ if (step) {
52402
+ if (contextFilePath) {
52403
+ host.updateContextFileStepId(contextFilePath, step.id);
52404
+ } else if (stepIdFilePath) {
52405
+ host.writeStepIdFile(stepIdFilePath, step.id);
52406
+ }
52407
+ if (laneContextFilePath2) {
52408
+ host.updateContextFileStepId(laneContextFilePath2, step.id);
52409
+ }
52410
+ }
52411
+ break;
52412
+ }
52413
+ case "tool_result":
52414
+ await host.stepPersister.persist(sessionId, "tool_result", {
52415
+ step_type: "tool_result",
52416
+ target_type: target.target_type,
52417
+ target_id: target.target_id,
52418
+ idempotency_key: `tr:${runtimeEvent.callId}`,
52419
+ content: {
52420
+ call_id: runtimeEvent.callId,
52421
+ tool_name: runtimeEvent.toolName,
52422
+ status: runtimeEvent.error ? "error" : "success",
52423
+ output: runtimeEvent.output,
52424
+ duration_ms: runtimeEvent.durationMs ?? 0,
52425
+ collapsible: true
52426
+ },
52427
+ group_key: runtimeEvent.groupKey
52428
+ });
52429
+ if (contextFilePath) {
52430
+ host.updateContextFileStepId(contextFilePath, null);
52431
+ } else if (stepIdFilePath) {
52432
+ host.clearStepIdFile(stepIdFilePath);
52433
+ }
52434
+ if (laneContextFilePath2) {
52435
+ host.updateContextFileStepId(laneContextFilePath2, null);
52436
+ }
52437
+ break;
52438
+ case "error":
52439
+ await host.stepPersister.persist(sessionId, "error", {
52440
+ step_type: "text",
52441
+ target_type: target.target_type,
52442
+ target_id: target.target_id,
52443
+ idempotency_key: randomUUID(),
52444
+ content: buildErrorStepContent(runtimeEvent.message),
52445
+ projection: false,
52446
+ group_key: runtimeEvent.groupKey
52447
+ });
52448
+ break;
52449
+ }
52450
+ }
52451
+
52289
52452
  // ../agent-core/dist/dispatch-recovery.js
52290
52453
  var DispatchRecovery = class {
52291
52454
  scan;
@@ -52345,7 +52508,7 @@ function splitChangeSource(sourceId) {
52345
52508
  // ../agent-core/dist/gateway-base.js
52346
52509
  import * as fs4 from "node:fs";
52347
52510
  import * as path5 from "node:path";
52348
- import { randomUUID } from "node:crypto";
52511
+ import { randomUUID as randomUUID2 } from "node:crypto";
52349
52512
 
52350
52513
  // ../sdk/dist/browser-viewer.js
52351
52514
  var BROWSER_VIEWER_READINESS_TIMEOUT_MS = 6e4;
@@ -52358,6 +52521,20 @@ function browserViewerRequestOptions(command, opts) {
52358
52521
  };
52359
52522
  }
52360
52523
 
52524
+ // ../sdk/dist/slack-endpoints.js
52525
+ function slackEndpoints(apiBase) {
52526
+ return {
52527
+ SLACK_CHANNELS: (orgId) => `${apiBase}/orgs/${orgId}/agents/me/slack/channels`,
52528
+ SLACK_USERS: (orgId) => `${apiBase}/orgs/${orgId}/agents/me/slack/users`,
52529
+ SLACK_REPLIES: (orgId) => `${apiBase}/orgs/${orgId}/agents/me/slack/replies`,
52530
+ SLACK_HISTORY: (orgId) => `${apiBase}/orgs/${orgId}/agents/me/slack/history`,
52531
+ SLACK_MEMBERS: (orgId) => `${apiBase}/orgs/${orgId}/agents/me/slack/members`,
52532
+ SLACK_STATUS: (orgId) => `${apiBase}/orgs/${orgId}/agents/me/slack/status`,
52533
+ SLACK_FILE: (orgId) => `${apiBase}/orgs/${orgId}/agents/me/slack/file`,
52534
+ SLACK_FILES: (orgId) => `${apiBase}/orgs/${orgId}/agents/me/slack/files`
52535
+ };
52536
+ }
52537
+
52361
52538
  // ../sdk/dist/clip-connection-endpoints.js
52362
52539
  var clipConnectionEndpoints = {
52363
52540
  ORG_CLIP_MCP_OAUTH_ATTEMPT: (orgId, clipId, attemptId) => `/api/v1/orgs/${orgId}/clip-registry/${clipId}/mcp-oauth-attempts/${attemptId}`,
@@ -52686,13 +52863,7 @@ var ENDPOINTS = {
52686
52863
  // Tier-B platform verb (agent-only): send one message as the bound bot.
52687
52864
  CHANNEL_SEND: (orgId) => `${API_BASE2}/orgs/${orgId}/agents/me/channel-send`,
52688
52865
  // Tier-B read verbs (agent-only): workspace visibility as the bot sees it.
52689
- SLACK_CHANNELS: (orgId) => `${API_BASE2}/orgs/${orgId}/agents/me/slack/channels`,
52690
- SLACK_USERS: (orgId) => `${API_BASE2}/orgs/${orgId}/agents/me/slack/users`,
52691
- SLACK_HISTORY: (orgId) => `${API_BASE2}/orgs/${orgId}/agents/me/slack/history`,
52692
- SLACK_MEMBERS: (orgId) => `${API_BASE2}/orgs/${orgId}/agents/me/slack/members`,
52693
- SLACK_STATUS: (orgId) => `${API_BASE2}/orgs/${orgId}/agents/me/slack/status`,
52694
- SLACK_FILE: (orgId) => `${API_BASE2}/orgs/${orgId}/agents/me/slack/file`,
52695
- SLACK_FILES: (orgId) => `${API_BASE2}/orgs/${orgId}/agents/me/slack/files`,
52866
+ ...slackEndpoints(API_BASE2),
52696
52867
  // WeChat tier-B read verbs (agent-only; internal research preview).
52697
52868
  ...wechatEndpoints(API_BASE2),
52698
52869
  // Invitations (org-scoped, admin)
@@ -53374,14 +53545,16 @@ var SlackFilesClient = class extends AttachmentClient {
53374
53545
  }
53375
53546
  /**
53376
53547
  * Tier-B file upload verb (agent-only): share a file into a Slack
53377
- * conversation this connection has seen inbound, with the same
53378
- * reply-anchor contract as the text send.
53548
+ * conversation or thread with the same addressing as text sends.
53549
+ * Native threadTs requires a server supporting Slack thread sends.
53379
53550
  */
53380
53551
  async sendSlackFile(orgId, input) {
53381
53552
  const fd = new FormData();
53382
53553
  fd.append("conversation_id", input.conversationId);
53383
53554
  if (input.replyTo)
53384
53555
  fd.append("reply_to", input.replyTo);
53556
+ if (input.threadTs)
53557
+ fd.append("thread_ts", input.threadTs);
53385
53558
  if (input.text)
53386
53559
  fd.append("text", input.text);
53387
53560
  fd.append("file", input.content, input.fileName);
@@ -53389,8 +53562,48 @@ var SlackFilesClient = class extends AttachmentClient {
53389
53562
  }
53390
53563
  };
53391
53564
 
53565
+ // ../sdk/dist/slack-read-client.js
53566
+ var SlackReadClient = class extends SlackFilesClient {
53567
+ slackReadQuery(base, query, extra) {
53568
+ const params = new URLSearchParams();
53569
+ if (query?.cursor)
53570
+ params.set("cursor", query.cursor);
53571
+ if (query?.limit)
53572
+ params.set("limit", String(query.limit));
53573
+ for (const [k, v] of Object.entries(extra ?? {}))
53574
+ params.set(k, v);
53575
+ const qs = params.toString();
53576
+ return qs ? `${base}?${qs}` : base;
53577
+ }
53578
+ /**
53579
+ * Tier-B read verbs (agent-only): workspace visibility as the bot sees
53580
+ * it. Same live gate as the send verb; authorization beyond it is the
53581
+ * bot's own Slack permissions.
53582
+ */
53583
+ async listSlackChannels(orgId, query) {
53584
+ return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_CHANNELS(orgId), query));
53585
+ }
53586
+ async listSlackUsers(orgId, query) {
53587
+ return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_USERS(orgId), query));
53588
+ }
53589
+ async slackHistory(orgId, conversationId, query) {
53590
+ return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_HISTORY(orgId), query, { conversation: conversationId }));
53591
+ }
53592
+ /** Read a Slack thread (parent and replies), one page in Slack order. */
53593
+ async slackReplies(orgId, channel, ts, query) {
53594
+ return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_REPLIES(orgId), query, { channel, ts }));
53595
+ }
53596
+ async slackMembers(orgId, conversationId, query) {
53597
+ return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_MEMBERS(orgId), query, { conversation: conversationId }));
53598
+ }
53599
+ /** Set/clear the Agents-pane "typing…" indicator (best-effort cosmetic). */
53600
+ async setSlackStatus(orgId, input) {
53601
+ await this.request("POST", ENDPOINTS.SLACK_STATUS(orgId), input);
53602
+ }
53603
+ };
53604
+
53392
53605
  // ../sdk/dist/channel-conversation-client.js
53393
- var ChannelConversationClient = class extends SlackFilesClient {
53606
+ var ChannelConversationClient = class extends SlackReadClient {
53394
53607
  /**
53395
53608
  * Org-admin write of what the agent receives from a group root
53396
53609
  * (`PATCH …/channel-conversations/{id}/attention`); the agent's own path
@@ -55064,38 +55277,6 @@ var ParallClient = class _ParallClient extends BrowserProfileClient {
55064
55277
  async sendChannelMessage(orgId, input) {
55065
55278
  return this.request("POST", ENDPOINTS.CHANNEL_SEND(orgId), input);
55066
55279
  }
55067
- slackReadQuery(base, query, extra) {
55068
- const params = new URLSearchParams();
55069
- if (query?.cursor)
55070
- params.set("cursor", query.cursor);
55071
- if (query?.limit)
55072
- params.set("limit", String(query.limit));
55073
- for (const [k, v] of Object.entries(extra ?? {}))
55074
- params.set(k, v);
55075
- const qs = params.toString();
55076
- return qs ? `${base}?${qs}` : base;
55077
- }
55078
- /**
55079
- * Tier-B read verbs (agent-only): workspace visibility as the bot sees
55080
- * it. Same live gate as the send verb; authorization beyond it is the
55081
- * bot's own Slack permissions.
55082
- */
55083
- async listSlackChannels(orgId, query) {
55084
- return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_CHANNELS(orgId), query));
55085
- }
55086
- async listSlackUsers(orgId, query) {
55087
- return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_USERS(orgId), query));
55088
- }
55089
- async slackHistory(orgId, conversationId, query) {
55090
- return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_HISTORY(orgId), query, { conversation: conversationId }));
55091
- }
55092
- async slackMembers(orgId, conversationId, query) {
55093
- return this.request("GET", this.slackReadQuery(ENDPOINTS.SLACK_MEMBERS(orgId), query, { conversation: conversationId }));
55094
- }
55095
- /** Set/clear the Agents-pane "typing…" indicator (best-effort cosmetic). */
55096
- async setSlackStatus(orgId, input) {
55097
- await this.request("POST", ENDPOINTS.SLACK_STATUS(orgId), input);
55098
- }
55099
55280
  /**
55100
55281
  * Authorized raw GET (binary responses) with the same auth, 401
55101
55282
  * refresh-and-retry-once, and error-envelope handling as `request` — the
@@ -56810,46 +56991,60 @@ function createActiveForkState(fork, targetId, continuationPrefix) {
56810
56991
  };
56811
56992
  }
56812
56993
 
56813
- // ../agent-core/dist/redact.js
56814
- function redactSecrets(s, knownValues = []) {
56815
- return maskKnownValues(s, knownValues).replace(/\b(agk|mck|cpk)_[A-Za-z0-9_-]+/g, "$1_***").replace(/\b(sk|pk|rk)-[A-Za-z0-9_-]{8,}/g, "$1-***").replace(/\bAKIA[0-9A-Z]{16}\b/g, "AKIA***").replace(/\b((?:bearer|basic)\s+)[A-Za-z0-9._~+/=-]+/gi, "$1***").replace(/(\b(?:[\w-]*[_-])?(?:api[_-]?key|key|token|secret|password|passwd|authorization)["']?\s*[=:]\s*)(?:"(?:\\.|[^"\\\r\n])*"|'(?:\\.|[^'\\\r\n])*'|[^\s&;,}\]"']+)/gi, "$1***").replace(/[A-Za-z0-9_-]{32,}/g, "***");
56994
+ // ../agent-core/dist/gateway-steer.js
56995
+ var pendingSteers = /* @__PURE__ */ new WeakMap();
56996
+ function hasMainSteers(state) {
56997
+ return (pendingSteers.get(state)?.size ?? 0) > 0;
56816
56998
  }
56817
- function maskKnownValues(s, knownValues) {
56818
- let out = s;
56819
- for (const v of knownValues) {
56820
- if (typeof v === "string" && v.length >= 6)
56821
- out = out.split(v).join("***");
56822
- }
56823
- return out;
56999
+ async function waitForMainSteers(state) {
57000
+ await Promise.allSettled(pendingSteers.get(state) ?? []);
56824
57001
  }
56825
- function redactTurnOutcome(event, knownValues) {
56826
- const redacted = { ...event };
56827
- if (redacted.detail)
56828
- redacted.detail = redactSecrets(redacted.detail, knownValues);
56829
- if (redacted.raw) {
56830
- redacted.raw = Object.fromEntries(Object.entries(redacted.raw).map(([k, v]) => [
56831
- k,
56832
- typeof v === "string" ? redactSecrets(v, knownValues) : v
56833
- ]));
57002
+ async function steerLaneMessage(host, event) {
57003
+ let pending = pendingSteers.get(host.dispatchState);
57004
+ if (!pending) {
57005
+ pending = /* @__PURE__ */ new Set();
57006
+ pendingSteers.set(host.dispatchState, pending);
57007
+ }
57008
+ const work = Promise.resolve().then(() => steerLaneMessageInner(host, event));
57009
+ pending.add(work);
57010
+ try {
57011
+ await work;
57012
+ } finally {
57013
+ pending.delete(work);
57014
+ if (pending.size === 0)
57015
+ pendingSteers.delete(host.dispatchState);
57016
+ host.notifyDrainWaiters?.();
56834
57017
  }
56835
- return redacted;
56836
57018
  }
56837
- function describeTurnOutcomeFailure(outcome) {
56838
- const retryNote = outcome.retryAt ? `, retry at ${outcome.retryAt}` : "";
56839
- return {
56840
- warn: `${outcome.outcome}${retryNote}${outcome.detail ? ` \u2014 ${outcome.detail}` : ""}`,
56841
- stepMessage: `LLM turn ${outcome.outcome}${retryNote}${outcome.detail ? `: ${outcome.detail}` : ""}`
56842
- };
57019
+ function hasUninjectedSameLaneEventAhead(host, event) {
57020
+ const eventIndex = host.dispatchState.mainBuffer.lastIndexOf(event);
57021
+ if (eventIndex <= 0)
57022
+ return false;
57023
+ const groupKey = host.dispatchGroupKey(event);
57024
+ return host.dispatchState.mainBuffer.slice(0, eventIndex).some((buffered) => host.dispatchGroupKey(buffered) === groupKey && !host.injectedMainBufferEvents.has(buffered));
56843
57025
  }
56844
- function redactLogger(log, knownValues) {
56845
- if (!log)
56846
- return void 0;
56847
- return {
56848
- info: (message) => log.info(redactSecrets(message, knownValues)),
56849
- warn: (message) => log.warn(redactSecrets(message, knownValues)),
56850
- error: (message) => log.error(redactSecrets(message, knownValues)),
56851
- ...log.child ? { child: (name) => redactLogger(log.child(name), knownValues) } : {}
56852
- };
57026
+ async function steerLaneMessageInner(host, event) {
57027
+ const { laneLedger: ledger, opts } = host;
57028
+ const adapter = opts.dispatchAdapter;
57029
+ if (!ledger || !adapter.enqueueDuringDispatch)
57030
+ return;
57031
+ if (hasUninjectedSameLaneEventAhead(host, event))
57032
+ return;
57033
+ const folded = await ledger.steerLive(event);
57034
+ if (!folded)
57035
+ return;
57036
+ if (!folded.frame)
57037
+ return;
57038
+ event.frame = folded.frame;
57039
+ const injected = await adapter.enqueueDuringDispatch(opts.runtimeKey, eventBody(event), folded.inputLifecycle);
57040
+ if (injected) {
57041
+ host.injectedMainBufferEvents.add(event);
57042
+ ledger.markSeen(event, folded.covered);
57043
+ opts.log?.info(`steer frame handed to adapter for ${event.messageId} (will drain for bookkeeping)`);
57044
+ } else if (folded.frame) {
57045
+ ledger.deferFrame(event, folded.frame, folded.covered);
57046
+ opts.log?.info(`steer folded but not injected for ${event.messageId} \u2014 frame deferred to the drain`);
57047
+ }
56853
57048
  }
56854
57049
 
56855
57050
  // ../agent-core/dist/lane-ledger.js
@@ -58595,36 +58790,6 @@ function applyWake(event, wake) {
58595
58790
  if (wake.attachments?.length)
58596
58791
  event.attachments = wake.attachments;
58597
58792
  }
58598
- function hasUninjectedSameLaneEventAhead(host, event) {
58599
- const eventIndex = host.dispatchState.mainBuffer.lastIndexOf(event);
58600
- if (eventIndex <= 0)
58601
- return false;
58602
- const groupKey = host.dispatchGroupKey(event);
58603
- return host.dispatchState.mainBuffer.slice(0, eventIndex).some((buffered) => host.dispatchGroupKey(buffered) === groupKey && !host.injectedMainBufferEvents.has(buffered));
58604
- }
58605
- async function steerLaneMessage(host, event) {
58606
- const { laneLedger: ledger, opts } = host;
58607
- const adapter = opts.dispatchAdapter;
58608
- if (!ledger || !adapter.enqueueDuringDispatch)
58609
- return;
58610
- if (hasUninjectedSameLaneEventAhead(host, event))
58611
- return;
58612
- const folded = await ledger.steerLive(event);
58613
- if (!folded)
58614
- return;
58615
- if (!folded.frame)
58616
- return;
58617
- event.frame = folded.frame;
58618
- const injected = await adapter.enqueueDuringDispatch(opts.runtimeKey, eventBody(event), folded.inputLifecycle);
58619
- if (injected) {
58620
- host.injectedMainBufferEvents.add(event);
58621
- ledger.markSeen(event, folded.covered);
58622
- opts.log?.info(`steer folded+injected for ${event.messageId} (will drain for bookkeeping)`);
58623
- } else if (folded.frame) {
58624
- ledger.deferFrame(event, folded.frame, folded.covered);
58625
- opts.log?.info(`steer folded but not injected for ${event.messageId} \u2014 frame deferred to the drain`);
58626
- }
58627
- }
58628
58793
  async function dispatchLaneGroup(host, opts) {
58629
58794
  const event = opts.events[opts.events.length - 1];
58630
58795
  return runInDispatchTrace({
@@ -58730,6 +58895,9 @@ async function dispatchLaneGroupInner(host, opts) {
58730
58895
  ]);
58731
58896
  dispatched = await host.runDispatch(event, opts.sessionKey, opts.bodyPrefix + eventBody(event), opts.earlier, opts.captureText, inputLifecycle);
58732
58897
  } catch (err) {
58898
+ while (opts.sessionKey === host.opts.runtimeKey && hasMainSteers(host.dispatchState)) {
58899
+ await waitForMainSteers(host.dispatchState);
58900
+ }
58733
58901
  host.noteSessionLane(opts.sessionKey, null);
58734
58902
  await runInPhaseSpan("settle", () => ledger.release(lane.laneKey, "runtime_error", releaseErrorInfo(err, [host.opts.config.api_key])).catch(() => {
58735
58903
  }));
@@ -58741,6 +58909,9 @@ async function dispatchLaneGroupInner(host, opts) {
58741
58909
  if (!dispatched) {
58742
58910
  return "shutdown";
58743
58911
  }
58912
+ while (opts.sessionKey === host.opts.runtimeKey && hasMainSteers(host.dispatchState)) {
58913
+ await waitForMainSteers(host.dispatchState);
58914
+ }
58744
58915
  const settled = host.consumeTurnOutcome(opts.sessionKey);
58745
58916
  if (settled) {
58746
58917
  if (settled.kind === "deferred") {
@@ -59084,6 +59255,19 @@ async function handleCompactSignal(host, data) {
59084
59255
  // ../agent-core/dist/gateway-runtime-turns.js
59085
59256
  var UNTARGETED_STEP = { target_type: "" };
59086
59257
  function handleRuntimeActivity(host, event) {
59258
+ if (event.kind === "observation") {
59259
+ const binding = host.sessionBindings.get(event.sessionKey);
59260
+ if (!binding || binding.runtimeSessionId !== event.event.runtimeSessionId) {
59261
+ host.opts.log?.warn(`runtime observation has no matching session binding on ${event.sessionKey}`);
59262
+ return;
59263
+ }
59264
+ host.inFlightRuntimeTurns += 1;
59265
+ void persistRuntimeObservation(host.stepPersister, binding.agentSessionId, UNTARGETED_STEP, event.event, host.opts.log, [host.opts.config.api_key]).finally(() => {
59266
+ host.inFlightRuntimeTurns -= 1;
59267
+ host.notifyDrainWaiters();
59268
+ });
59269
+ return;
59270
+ }
59087
59271
  const sessionKey = event.kind === "turn" ? event.turn.sessionKey : event.sessionKey;
59088
59272
  const label = event.kind === "turn" ? `runtime-initiated turn ${event.turn.groupKey} on ${sessionKey}` : `runtime child session close for ${sessionKey}`;
59089
59273
  const prior = host.runtimeActivityChains.get(sessionKey) ?? Promise.resolve();
@@ -59126,7 +59310,8 @@ async function runRuntimeTurn(host, turn) {
59126
59310
  };
59127
59311
  try {
59128
59312
  for await (const runtimeEvent of turn.events) {
59129
- deadline.touch();
59313
+ if (runtimeEvent.type !== "observation")
59314
+ deadline.touch();
59130
59315
  if (runtimeEvent.type === "runtime_session") {
59131
59316
  try {
59132
59317
  binding = await host.bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath);
@@ -59136,7 +59321,7 @@ async function runRuntimeTurn(host, turn) {
59136
59321
  }
59137
59322
  continue;
59138
59323
  }
59139
- if (!binding) {
59324
+ if (!binding || runtimeEvent.type === "observation" && binding.runtimeSessionId !== runtimeEvent.runtimeSessionId) {
59140
59325
  droppedWithoutBinding += 1;
59141
59326
  continue;
59142
59327
  }
@@ -59151,7 +59336,8 @@ async function runRuntimeTurn(host, turn) {
59151
59336
  await host.createRuntimeStep(binding.agentSessionId, UNTARGETED_STEP, { type: "error", message: failure.stepMessage, groupKey }, void 0, contextFilePath);
59152
59337
  continue;
59153
59338
  }
59154
- await ensureBegun();
59339
+ if (runtimeEvent.type !== "observation")
59340
+ await ensureBegun();
59155
59341
  stepCount += 1;
59156
59342
  await host.createRuntimeStep(binding.agentSessionId, UNTARGETED_STEP, runtimeEvent, void 0, contextFilePath);
59157
59343
  }
@@ -60319,11 +60505,11 @@ function parseDispatchDeadlineMs(raw) {
60319
60505
  }
60320
60506
  function inputStepIdempotencyKey(event) {
60321
60507
  if (event.type === "message" || event.type === "channel_message") {
60322
- return event.messageId ? `input:${event.messageId}` : randomUUID();
60508
+ return event.messageId ? `input:${event.messageId}` : randomUUID2();
60323
60509
  }
60324
60510
  if (event.dispatchEventId)
60325
60511
  return `input:${event.dispatchEventId}`;
60326
- return randomUUID();
60512
+ return randomUUID2();
60327
60513
  }
60328
60514
  function resolveStepTarget(event) {
60329
60515
  if (event.input?.step_target) {
@@ -60830,100 +61016,7 @@ var ParallAgentGateway = class {
60830
61016
  });
60831
61017
  }
60832
61018
  async createRuntimeStep(sessionId, target, runtimeEvent, stepIdFilePath, contextFilePath, laneContextFilePath2) {
60833
- switch (runtimeEvent.type) {
60834
- case "thinking":
60835
- await this.stepPersister.persist(sessionId, "thinking", {
60836
- step_type: "thinking",
60837
- target_type: target.target_type,
60838
- target_id: target.target_id,
60839
- idempotency_key: randomUUID(),
60840
- content: { text: runtimeEvent.text },
60841
- group_key: runtimeEvent.groupKey
60842
- });
60843
- break;
60844
- case "text":
60845
- await this.stepPersister.persist(sessionId, "text", {
60846
- step_type: "text",
60847
- target_type: target.target_type,
60848
- target_id: target.target_id,
60849
- idempotency_key: randomUUID(),
60850
- content: {
60851
- text: runtimeEvent.text,
60852
- suppressed: runtimeEvent.project !== true
60853
- },
60854
- projection: runtimeEvent.project === true,
60855
- group_key: runtimeEvent.groupKey
60856
- });
60857
- break;
60858
- case "tool_call": {
60859
- const step = await this.stepPersister.persist(sessionId, "tool_call", {
60860
- step_type: "tool_call",
60861
- target_type: target.target_type,
60862
- target_id: target.target_id,
60863
- // call_id is session-unique for bridge runtimes (server-enforced),
60864
- // so the bare form anchors the tool step pair across retries —
60865
- // unlike parel's turn-scoped `tc:{turnId}:{callId}` (see
60866
- // protocol-vectors/agent-steps.json).
60867
- idempotency_key: `tc:${runtimeEvent.callId}`,
60868
- content: {
60869
- call_id: runtimeEvent.callId,
60870
- tool_name: runtimeEvent.toolName,
60871
- tool_input: runtimeEvent.input,
60872
- status: "running",
60873
- started_at: runtimeEvent.startedAt ?? (/* @__PURE__ */ new Date()).toISOString()
60874
- },
60875
- group_key: runtimeEvent.groupKey,
60876
- runtime_key: runtimeEvent.callId
60877
- });
60878
- if (step) {
60879
- if (contextFilePath) {
60880
- this.updateContextFileStepId(contextFilePath, step.id);
60881
- } else if (stepIdFilePath) {
60882
- this.writeStepIdFile(stepIdFilePath, step.id);
60883
- }
60884
- if (laneContextFilePath2) {
60885
- this.updateContextFileStepId(laneContextFilePath2, step.id);
60886
- }
60887
- }
60888
- break;
60889
- }
60890
- case "tool_result":
60891
- await this.stepPersister.persist(sessionId, "tool_result", {
60892
- step_type: "tool_result",
60893
- target_type: target.target_type,
60894
- target_id: target.target_id,
60895
- idempotency_key: `tr:${runtimeEvent.callId}`,
60896
- content: {
60897
- call_id: runtimeEvent.callId,
60898
- tool_name: runtimeEvent.toolName,
60899
- status: runtimeEvent.error ? "error" : "success",
60900
- output: runtimeEvent.output,
60901
- duration_ms: runtimeEvent.durationMs ?? 0,
60902
- collapsible: true
60903
- },
60904
- group_key: runtimeEvent.groupKey
60905
- });
60906
- if (contextFilePath) {
60907
- this.updateContextFileStepId(contextFilePath, null);
60908
- } else if (stepIdFilePath) {
60909
- this.clearStepIdFile(stepIdFilePath);
60910
- }
60911
- if (laneContextFilePath2) {
60912
- this.updateContextFileStepId(laneContextFilePath2, null);
60913
- }
60914
- break;
60915
- case "error":
60916
- await this.stepPersister.persist(sessionId, "error", {
60917
- step_type: "text",
60918
- target_type: target.target_type,
60919
- target_id: target.target_id,
60920
- idempotency_key: randomUUID(),
60921
- content: buildErrorStepContent(runtimeEvent.message),
60922
- projection: false,
60923
- group_key: runtimeEvent.groupKey
60924
- });
60925
- break;
60926
- }
61019
+ return createRuntimeStep(this, sessionId, target, runtimeEvent, stepIdFilePath, contextFilePath, laneContextFilePath2);
60927
61020
  }
60928
61021
  writeContextFile(filePath, ctx) {
60929
61022
  try {
@@ -61073,7 +61166,8 @@ var ParallAgentGateway = class {
61073
61166
  inputLifecycle,
61074
61167
  noteActivity: dispatchDeadline.touch
61075
61168
  })) {
61076
- dispatchDeadline.touch();
61169
+ if (runtimeEvent.type !== "observation")
61170
+ dispatchDeadline.touch();
61077
61171
  if (runtimeEvent.type === "runtime_session") {
61078
61172
  const priorAgentSessionId = binding?.agentSessionId;
61079
61173
  binding = await runInPhaseSpan("session", () => this.bindRuntimeSession(sessionKey, runtimeEvent, contextFilePath, laneContextFilePath2));
@@ -61098,6 +61192,14 @@ var ParallAgentGateway = class {
61098
61192
  }
61099
61193
  continue;
61100
61194
  }
61195
+ if (runtimeEvent.type === "observation") {
61196
+ if (binding?.runtimeSessionId === runtimeEvent.runtimeSessionId) {
61197
+ await this.createRuntimeStep(binding.agentSessionId, stepTarget, runtimeEvent);
61198
+ } else {
61199
+ this.opts.log?.warn("runtime observation has no matching active session binding");
61200
+ }
61201
+ continue;
61202
+ }
61101
61203
  if (runtimeEvent.type === "turn_outcome") {
61102
61204
  const outcomeEvent = redactTurnOutcome(runtimeEvent, [this.opts.config.api_key]);
61103
61205
  turnOutcomeEvent = outcomeEvent;
@@ -61470,6 +61572,8 @@ var ParallAgentGateway = class {
61470
61572
  while (this.idleCompact.inFlight)
61471
61573
  await this.idleCompact.inFlight;
61472
61574
  while (this.dispatchState.mainBuffer.length > 0) {
61575
+ while (hasMainSteers(this.dispatchState))
61576
+ await waitForMainSteers(this.dispatchState);
61473
61577
  if (this.shuttingDown) {
61474
61578
  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`);
61475
61579
  break;
@@ -62045,13 +62149,9 @@ ${fullSummary}` : fullSummary;
62045
62149
  }
62046
62150
  }
62047
62151
  }
62048
- /**
62049
- * Nothing in flight: no dispatch, no runtime-initiated turn, and the
62050
- * runtime itself reports idle (isBusy — a turn it is executing that has
62051
- * not surfaced yet, or a follow-up hold after background work finished).
62052
- */
62152
+ /** Drain includes in-flight native work and unfinished steer registration. */
62053
62153
  isDrained() {
62054
- return this.inFlightDispatches === 0 && this.inFlightRuntimeTurns === 0 && !this.adapterBusy();
62154
+ return this.inFlightDispatches === 0 && this.inFlightRuntimeTurns === 0 && !hasMainSteers(this.dispatchState) && !this.adapterBusy();
62055
62155
  }
62056
62156
  adapterBusy() {
62057
62157
  try {
@@ -63094,17 +63194,17 @@ async function startWikiHelper(params) {
63094
63194
  }
63095
63195
 
63096
63196
  // dist/oc-session.js
63097
- import { randomUUID as randomUUID2 } from "node:crypto";
63197
+ import { randomUUID as randomUUID3 } from "node:crypto";
63098
63198
  import { existsSync as existsSync4, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
63099
63199
  import { join as join7, resolve as resolve2 } from "node:path";
63100
63200
  var CURRENT_SESSION_VERSION = 3;
63101
63201
  function generateId(existing) {
63102
63202
  for (let i = 0; i < 100; i++) {
63103
- const id = randomUUID2().slice(0, 8);
63203
+ const id = randomUUID3().slice(0, 8);
63104
63204
  if (!existing.has(id))
63105
63205
  return id;
63106
63206
  }
63107
- return randomUUID2();
63207
+ return randomUUID3();
63108
63208
  }
63109
63209
  function loadEntries(filePath) {
63110
63210
  if (!existsSync4(filePath))
@@ -63219,14 +63319,14 @@ var SessionManager = class _SessionManager {
63219
63319
  if (header && header.version > CURRENT_SESSION_VERSION) {
63220
63320
  throw new Error(`Session file ${this.sessionFile} uses version ${header.version}, but oc-session.ts only supports up to ${CURRENT_SESSION_VERSION}. Sync oc-session.ts with upstream pi-coding-agent (see AGENTS.md step 4).`);
63221
63321
  }
63222
- this.sessionId = header?.id ?? randomUUID2();
63322
+ this.sessionId = header?.id ?? randomUUID3();
63223
63323
  if (migrate(this.fileEntries))
63224
63324
  this.rewrite();
63225
63325
  this.buildIndex();
63226
63326
  this.flushed = true;
63227
63327
  }
63228
63328
  initEmpty() {
63229
- this.sessionId = randomUUID2();
63329
+ this.sessionId = randomUUID3();
63230
63330
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
63231
63331
  this.fileEntries = [
63232
63332
  {
@@ -63303,7 +63403,7 @@ var SessionManager = class _SessionManager {
63303
63403
  if (branch.length === 0)
63304
63404
  throw new Error(`Entry ${leafId} not found`);
63305
63405
  const pathWithoutLabels = branch.filter((e) => e.type !== "label");
63306
- const newId = randomUUID2();
63406
+ const newId = randomUUID3();
63307
63407
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
63308
63408
  const ts = timestamp.replace(/[:.]/g, "-");
63309
63409
  const newFile = join7(this.sessionDir, `${ts}_${newId}.jsonl`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/parall",
3
- "version": "1.64.0",
3
+ "version": "1.65.0",
4
4
  "description": "OpenClaw channel plugin for Parall IM",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -16,8 +16,8 @@
16
16
  "openclaw.plugin.json"
17
17
  ],
18
18
  "dependencies": {
19
- "@parall/agent-core": "1.64.0",
20
- "@parall/sdk": "1.64.0"
19
+ "@parall/agent-core": "1.65.0",
20
+ "@parall/sdk": "1.65.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "^22.0.0",