@botiverse/raft-daemon 0.69.0 → 0.70.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.
@@ -2225,7 +2225,12 @@ var agentApiIntegrationLoginResponseSchema = passthroughObject({
2225
2225
  status: z2.enum(["logged_in", "already_logged_in", "approval_required"]),
2226
2226
  service: agentApiIntegrationServiceSchema,
2227
2227
  scopes: z2.array(z2.string()),
2228
- requestId: z2.string(),
2228
+ requestId: z2.string().optional(),
2229
+ session: passthroughObject({
2230
+ status: z2.literal("stored"),
2231
+ source: z2.enum(["cache", "fresh"]),
2232
+ path: nullableStringSchema
2233
+ }).optional(),
2229
2234
  approval: passthroughObject({
2230
2235
  requestId: z2.string(),
2231
2236
  target: nullableStringSchema,
@@ -3739,6 +3744,8 @@ var DISPLAY_PLAN_CONFIG = {
3739
3744
  }
3740
3745
  };
3741
3746
  var FREE_MONTHLY_FILE_UPLOAD_LIMIT_BYTES = 100 * 1024 * 1024;
3747
+ var FREE_SINGLE_FILE_UPLOAD_LIMIT_BYTES = 50 * 1024 * 1024;
3748
+ var PRO_SINGLE_FILE_UPLOAD_LIMIT_BYTES = 200 * 1024 * 1024;
3742
3749
  var TRIAL_START_DATE = /* @__PURE__ */ new Date("2026-04-18T00:00:00Z");
3743
3750
  var TRIAL_END_DATE = /* @__PURE__ */ new Date("2026-06-23T12:00:00Z");
3744
3751
  var TRIAL_DURATION_DAYS = (TRIAL_END_DATE.getTime() - TRIAL_START_DATE.getTime()) / (24 * 60 * 60 * 1e3);
@@ -4237,7 +4244,7 @@ Threads are sub-conversations attached to a specific message. They let you discu
4237
4244
  - When you receive a message from a thread (the target has a \`:shortid\` suffix), **always reply using that same target** to keep the conversation in the thread.
4238
4245
  - **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=00000000 ...]\`, reply with \`raft message send --target "#general:00000000" <<'${D}'\` followed by the message body and \`${D}\`. The thread will be auto-created if it doesn't exist yet. Example IDs like \`00000000\` are placeholders; real message IDs come from received messages.
4239
4246
  - When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
4240
- - You can read thread history: \`raft message read --channel "#general:00000000"\`
4247
+ - You can read thread history: \`raft message read --target "#general:00000000"\`
4241
4248
  - You can stop receiving ordinary delivery for a thread with \`raft thread unfollow --target "#general:00000000"\`. Only do this when your work in that thread is clearly complete or no longer relevant.
4242
4249
  - Threads cannot be nested \u2014 you cannot start a thread inside a thread.`;
4243
4250
  }
@@ -4245,7 +4252,7 @@ function buildDiscoverySection() {
4245
4252
  return `### Discovering people and channels
4246
4253
 
4247
4254
  Call \`raft server info\` to see all channels in this server, which ones you have joined, other agents, and humans.
4248
- Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with \`raft message read\` and \`raft channel members\`, but you cannot send messages there or receive ordinary channel delivery until you join with \`raft channel join --target "#channel-name"\`. Private channels require a human with access to add you. To leave a regular channel you have joined, use \`raft channel leave --target "#channel-name"\`. To mute ordinary Activity delivery without leaving a regular channel, use \`raft channel mute "#channel-name"\`; personal @mentions and DMs still pierce (a task pierces only when it personally @mentions you), and thread following is separate. To reverse that setting, use \`raft channel unmute "#channel-name"\`. To stop following a thread without leaving its parent channel, use \`raft thread unfollow --target "#channel-name:shortid"\`.
4255
+ Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with \`raft message read\` and \`raft channel members\`, but you cannot send messages there or receive ordinary channel delivery until you join with \`raft channel join --target "#channel-name"\`. Private channels require a human with access to add you. To leave a regular channel you have joined, use \`raft channel leave --target "#channel-name"\`. To mute ordinary Activity delivery without leaving a regular channel, use \`raft channel mute --target "#channel-name"\`; personal @mentions and DMs still pierce (a task pierces only when it personally @mentions you), and thread following is separate. To reverse that setting, use \`raft channel unmute --target "#channel-name"\`. To stop following a thread without leaving its parent channel, use \`raft thread unfollow --target "#channel-name:shortid"\`.
4249
4256
  Private channels are membership-gated. If \`raft server info\` shows a channel as private, treat its name, members, and content as private to that channel; do not disclose that information in other channels, DMs, summaries, or task reports unless a human explicitly asks within an authorized context. In \`raft channel members\`, human role labels such as owner/admin show server-level authority; no role label means ordinary member.`;
4250
4257
  }
4251
4258
  function buildChannelAwarenessSection() {
@@ -4259,9 +4266,9 @@ Each channel has a **name** and optionally a **description** that define its pur
4259
4266
  function buildReadingHistorySection() {
4260
4267
  return `### Reading history
4261
4268
 
4262
- \`raft message read --channel "#channel-name"\` or \`raft message read --channel dm:@peer-name\` or \`raft message read --channel "#channel:shortid"\`
4269
+ \`raft message read --target "#channel-name"\` or \`raft message read --target dm:@peer-name\` or \`raft message read --target "#channel:shortid"\`
4263
4270
 
4264
- To jump directly to a specific hit with nearby context, use \`raft message read --channel "..." --around "messageId"\` or \`raft message read --channel "..." --around 12345\`.`;
4271
+ To jump directly to a specific hit with nearby context, use \`raft message read --target "..." --around "messageId"\` or \`raft message read --target "..." --around 12345\`.`;
4265
4272
  }
4266
4273
  function buildHistoricalReferencesSection() {
4267
4274
  return `### Historical references
@@ -4290,7 +4297,7 @@ Only top-level channel / DM messages can become tasks. Messages inside threads a
4290
4297
  **Assignee** is independent from status \u2014 a task can be claimed or unclaimed at any status except \`done\`.
4291
4298
 
4292
4299
  **Workflow:**
4293
- 1. Receive a message that requires action \u2192 claim it first (by task number if already a task, or by message ID if it's a regular message). Use repeat flags: \`raft task claim --channel "#channel" --number 1 --number 2\` or \`raft task claim --channel "#channel" --message-id abc12345\`.
4300
+ 1. Receive a message that requires action \u2192 claim it first (by task number if already a task, or by message ID if it's a regular message). Use repeat flags: \`raft task claim --target "#channel" --number 1 --number 2\` or \`raft task claim --target "#channel" --message-id abc12345\`.
4294
4301
  2. If the claim fails, someone else is working on it \u2014 do not work on that task unless an owner/admin explicitly redirects it to you
4295
4302
  3. Post updates in the task's thread: \`raft message send --target "#channel:msgShortId" <<'${D}'\` followed by the message body and \`${D}\`
4296
4303
  4. When done, set status to \`in_review\` so a human can validate via \`raft task update\`
@@ -4302,7 +4309,7 @@ Only top-level channel / DM messages can become tasks. Messages inside threads a
4302
4309
  - \`raft task create\` only creates the task \u2014 to own it, call \`raft task claim\` afterward.
4303
4310
  - Typical uses for \`raft task create\` are breaking down a larger task into parallel subtasks, or batch-creating genuinely new work for others to claim.
4304
4311
  - If someone already sent the work item as a message, just claim that existing message/task instead of creating a new one.
4305
- - If the work already exists as a message, reuse it via \`raft task claim --channel "#channel" --message-id abc12345\`.
4312
+ - If the work already exists as a message, reuse it via \`raft task claim --target "#channel" --message-id abc12345\`.
4306
4313
 
4307
4314
  **Creating new tasks:**
4308
4315
  - The task system exists to prevent duplicate work. If you see an existing task for the work, either claim that task or leave it alone.
@@ -4521,7 +4528,7 @@ function buildPrompt(config, opts) {
4521
4528
  const channelAwarenessSection = cliGuideSections.channelAwareness;
4522
4529
  const thirdPartyIntegrationsSection = `### Third-party integrations
4523
4530
 
4524
- If a built-in Slock app or registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app or built-in Slock app, first run \`raft integration list\` and match the app to a listed service before browsing the app. Use \`raft integration login --service <service>\` to provision or reuse your agent login for that service. If the service exposes manifest-backed HTTP API actions, prefer \`raft integration invoke --service <service> --list-actions\` and then \`raft integration invoke --service <service> --action <name>\`; the CLI performs the stateless Agent Login callback handoff and service-session setup for you. If the service exposes an agent behavior manifest and you need to run its local CLI, run \`raft integration env --service <service>\` before invoking that CLI; if it prints exports, apply them first so service credentials stay under a per-agent profile HOME/XDG tree instead of the host user's global HOME. If it reports that no local env is required, do not invent HOME/XDG overrides; for HTTP API action services, use \`raft integration invoke\` instead. If it fails, do not run that local CLI with the host user's HOME; report that the service manifest is unsupported. Slock does not execute local commands from remote manifests automatically. If the CLI reports that the \`integration\` command is unknown, the local daemon/CLI is too old for Slock Agent Login; report that the machine must be upgraded/restarted instead of calling internal HTTP endpoints yourself. When \`raft integration login\` returns \`Agent login ready\` or \`Already logged in\`, the agent-side login is ready. If the output includes a service callback handoff URL, treat it as a stateless Login with Raft API-action session handoff URL; prefer \`raft integration invoke\` over manually opening or curling it. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow, use internal request IDs as OAuth callback codes outside the documented CLI flow, call internal Slock integration endpoints directly, or call third-party exchange endpoints unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use \`raft profile show\`. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the listed service / Slock Agent Login path.`;
4531
+ If a built-in Slock app or registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app or built-in Slock app, first run \`raft integration list\` and match the app to a listed service before browsing the app. Use \`raft integration login --service <service>\` to provision or reuse your agent login for that service; the CLI consumes the one-time Agent Login handoff internally and stores the service-owned session for this agent, so the successful output should be \`Agent login ready\` / \`Already logged in\`, not a raw request code you need to keep. If the service exposes manifest-backed HTTP API actions, prefer \`raft integration invoke --service <service> --list-actions\` and then \`raft integration invoke --service <service> --action <name>\`; the CLI uses the stored service session or refreshes it internally. If the service exposes an agent behavior manifest and you need to run its local CLI, run \`raft integration env --service <service>\` before invoking that CLI; if it prints exports, apply them first so service credentials stay under a per-agent profile HOME/XDG tree instead of the host user's global HOME. If it reports that no local env is required, do not invent HOME/XDG overrides; for HTTP API action services, use \`raft integration invoke\` instead. If it fails, do not run that local CLI with the host user's HOME; report that the service manifest is unsupported. Slock does not execute local commands from remote manifests automatically. If the CLI reports that the \`integration\` command is unknown, the local daemon/CLI is too old for Slock Agent Login; report that the machine must be upgraded/restarted instead of calling internal HTTP endpoints yourself. Do not store or reuse raw \`oauth_access_request\` request codes; those are one-time compatibility handoffs for services to exchange once for their own session/token. Prefer \`raft integration invoke\` over manually opening or curling callback URLs. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow, use internal request IDs as OAuth callback codes outside the documented CLI flow, call internal Slock integration endpoints directly, or call third-party exchange endpoints unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use \`raft profile show\`. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the listed service / Slock Agent Login path.`;
4525
4532
  const readingHistorySection = cliGuideSections.readingHistory;
4526
4533
  const historicalReferenceSection = cliGuideSections.historicalReferences;
4527
4534
  const tasksSection = cliGuideSections.tasks;
@@ -13371,24 +13378,38 @@ function safeSessionFilename(value) {
13371
13378
  const normalized = value.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
13372
13379
  return normalized || "unknown-session";
13373
13380
  }
13374
- function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
13381
+ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir, join, resolve) {
13375
13382
  try {
13376
13383
  const dir = path14.join(fallbackDir, ".slock", "runtime-sessions");
13377
13384
  mkdirSync4(dir, { recursive: true });
13378
13385
  const filePath = path14.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
13386
+ const searchedPaths = resolve?.searchedPaths ?? [];
13379
13387
  writeFileSync4(filePath, JSON.stringify({
13380
13388
  type: "runtime_session_handoff",
13381
13389
  runtime,
13382
13390
  sessionId,
13391
+ // Persisted L1<->L2 join keys (#460 V3): without these the
13392
+ // session->launch mapping lives only in daemon memory and old L1
13393
+ // transcripts can never re-join their launch after restart/adopt.
13394
+ // ids-only, closed fields (no content payload).
13395
+ launchId: join?.launchId ?? null,
13396
+ processInstanceId: join?.processInstanceId ?? null,
13397
+ // Negative-space (fruit#0 pt2): a resolve-miss is explicit evidence, not a
13398
+ // silent "not found". Recording the lookup method + the directories checked
13399
+ // makes "looked in the wrong place" distinguishable from "looked in the right
13400
+ // place and it is genuinely absent". ids/paths only — no transcript content.
13401
+ resolveStatus: "transcript_resolve_missing",
13402
+ lookupMethod: resolve?.lookupMethod ?? "none",
13403
+ searchedPaths,
13383
13404
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
13384
- note: "The native runtime transcript file was not found on this machine; this daemon-created handoff records the runtime session identity for diagnostics."
13405
+ note: "The native runtime transcript file was not found on this machine; this daemon-created handoff records the runtime session identity + the directories checked for diagnostics."
13385
13406
  }) + "\n", { mode: 384 });
13386
13407
  return {
13387
13408
  label: sessionId,
13388
13409
  path: filePath,
13389
13410
  runtime,
13390
13411
  reachable: true,
13391
- reason: "native session file path not found; using daemon handoff file"
13412
+ reason: `native session file path not found; using daemon handoff file; searched=[${searchedPaths.join(", ")}]`
13392
13413
  };
13393
13414
  } catch {
13394
13415
  return null;
@@ -13413,12 +13434,16 @@ function ensureRuntimeHomeDir(config, defaultHomeDir, workspacePath, opts = {})
13413
13434
  function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir, opts) {
13414
13435
  let resolvedPath = null;
13415
13436
  let lookupMethod = "none";
13437
+ const searchedPaths = [];
13416
13438
  if (runtime === "claude") {
13417
13439
  lookupMethod = "claude_jsonl";
13418
- resolvedPath = findSessionJsonl(path14.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`);
13440
+ const claudeRoot = path14.join(homeDir, ".claude", "projects");
13441
+ searchedPaths.push(claudeRoot);
13442
+ resolvedPath = findSessionJsonl(claudeRoot, (filename) => filename === `${sessionId}.jsonl`);
13419
13443
  } else if (runtime === "codex") {
13420
13444
  lookupMethod = "codex_jsonl";
13421
13445
  for (const root of codexSessionRootCandidates(homeDir)) {
13446
+ searchedPaths.push(root);
13422
13447
  resolvedPath = findSessionJsonl(root, (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId));
13423
13448
  if (resolvedPath) break;
13424
13449
  }
@@ -13430,7 +13455,13 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), f
13430
13455
  resolvedPath = findPiSessionFile2(sessionId, opts?.workingDirectory, homeDir);
13431
13456
  }
13432
13457
  if (!resolvedPath && fallbackDir) {
13433
- const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
13458
+ const fallback = writeRuntimeSessionHandoff(
13459
+ runtime,
13460
+ sessionId,
13461
+ fallbackDir,
13462
+ { launchId: opts?.launchId, processInstanceId: opts?.processInstanceId },
13463
+ { lookupMethod, searchedPaths }
13464
+ );
13434
13465
  if (fallback) {
13435
13466
  return {
13436
13467
  ...fallback,
@@ -13445,7 +13476,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), f
13445
13476
  reachable: Boolean(resolvedPath)
13446
13477
  };
13447
13478
  if (!resolvedPath) {
13448
- ref.reason = `session file path not found; attempted_lookup=${lookupMethod}`;
13479
+ ref.reason = `session file path not found; attempted_lookup=${lookupMethod}; searched=[${searchedPaths.join(", ")}]`;
13449
13480
  }
13450
13481
  return ref;
13451
13482
  }
@@ -13499,7 +13530,7 @@ function formatIncomingMessage(message, options = {}) {
13499
13530
  `[Slock thread context: you were added to a new thread via @mention.]`,
13500
13531
  `parent: ${message.thread_join_context.parent_target}`,
13501
13532
  `thread: ${message.thread_join_context.thread_target}`,
13502
- `suggested next step: raft message read --channel "${message.thread_join_context.suggested_read_history_target}"`,
13533
+ `suggested next step: raft message read --target "${message.thread_join_context.suggested_read_history_target}"`,
13503
13534
  "",
13504
13535
  "Parent message:",
13505
13536
  formatThreadContextMessage(message.thread_join_context.parent_message),
@@ -15329,7 +15360,8 @@ var AgentProcessManager = class _AgentProcessManager {
15329
15360
  detailKind: activity.statusEntry.detailKind ?? "daemon_activity",
15330
15361
  entries: activity.entries,
15331
15362
  launchId: ap?.launchId || void 0,
15332
- clientSeq: ap ? this.nextActivityClientSeq(agentId) : void 0
15363
+ clientSeq: ap ? this.nextActivityClientSeq(agentId) : void 0,
15364
+ isHeartbeat: false
15333
15365
  });
15334
15366
  }
15335
15367
  recordRuntimeDiagnosticActivity(agentId, ap, event) {
@@ -15342,7 +15374,8 @@ var AgentProcessManager = class _AgentProcessManager {
15342
15374
  detailKind: ap.lastActivityDetailKind || "none",
15343
15375
  entries: [runtimeDiagnosticTrajectoryEntry(event)],
15344
15376
  launchId: ap.launchId || void 0,
15345
- clientSeq: this.nextActivityClientSeq(agentId)
15377
+ clientSeq: this.nextActivityClientSeq(agentId),
15378
+ isHeartbeat: false
15346
15379
  });
15347
15380
  this.recordDaemonTrace("daemon.runtime.diagnostic", {
15348
15381
  agentId,
@@ -15393,7 +15426,8 @@ var AgentProcessManager = class _AgentProcessManager {
15393
15426
  detailKind: ap.lastActivityDetailKind || "none",
15394
15427
  entries: [runtimeRecoveryTrajectoryEntry(event)],
15395
15428
  launchId: ap.launchId || void 0,
15396
- clientSeq: this.nextActivityClientSeq(agentId)
15429
+ clientSeq: this.nextActivityClientSeq(agentId),
15430
+ isHeartbeat: false
15397
15431
  });
15398
15432
  this.recordDaemonTrace("daemon.runtime.recovery.visible", {
15399
15433
  agentId,
@@ -15641,7 +15675,8 @@ var AgentProcessManager = class _AgentProcessManager {
15641
15675
  this.lifecycleRecords.setRestartSnapshot(agentId, {
15642
15676
  config: this.buildRestartSafeConfig(ap.config, nextConfigSessionId),
15643
15677
  sessionId: nextConfigSessionId,
15644
- launchId: nextLaunchId
15678
+ launchId: nextLaunchId,
15679
+ processInstanceId: ap.processInstanceId
15645
15680
  });
15646
15681
  this.recordStartRebind(agentId, start, reason, previousLaunchId, nextLaunchId, nextSessionId);
15647
15682
  this.sendAgentStatus(agentId, "active", nextLaunchId);
@@ -16072,6 +16107,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16072
16107
  config: this.buildRestartSafeConfig(runtimeConfig, runtimeConfig.sessionId || null),
16073
16108
  sessionId: runtimeConfig.sessionId || null,
16074
16109
  launchId: effectiveLaunchId
16110
+ // No live AgentProcess in this adoption branch — omit rather than
16111
+ // fabricate; the marker's processInstanceId is nullable by contract.
16075
16112
  });
16076
16113
  this.sendAgentStatus(agentId, "active", effectiveLaunchId);
16077
16114
  this.broadcastActivity(agentId, "online", "Process idle", [], void 0, "idle");
@@ -16161,7 +16198,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16161
16198
  this.lifecycleRecords.setRestartSnapshot(agentId, {
16162
16199
  config: this.buildRestartSafeConfig(runtimeConfig, restartSessionId),
16163
16200
  sessionId: restartSessionId,
16164
- launchId: effectiveLaunchId
16201
+ launchId: effectiveLaunchId,
16202
+ processInstanceId: agentProcess.processInstanceId
16165
16203
  });
16166
16204
  if (pendingStartRebind) {
16167
16205
  this.recordStartRebind(agentId, pendingStartRebind, "startup_registered", originalLaunchId, effectiveLaunchId, agentProcess.sessionId);
@@ -16363,7 +16401,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16363
16401
  this.lifecycleRecords.setRestartSnapshot(agentId, {
16364
16402
  config: nextConfig,
16365
16403
  sessionId: ap.sessionId,
16366
- launchId: ap.launchId
16404
+ launchId: ap.launchId,
16405
+ processInstanceId: ap.processInstanceId
16367
16406
  });
16368
16407
  this.broadcastMessageReceivedActivity(agentId);
16369
16408
  this.lifecycleRecords.deleteRestartSnapshot(agentId);
@@ -16381,7 +16420,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16381
16420
  this.lifecycleRecords.setRestartSnapshot(agentId, {
16382
16421
  config: nextConfig,
16383
16422
  sessionId: ap.sessionId,
16384
- launchId: ap.launchId
16423
+ launchId: ap.launchId,
16424
+ processInstanceId: ap.processInstanceId
16385
16425
  });
16386
16426
  const report = this.recordSpawnFailure(agentId, "runner_credential_mint");
16387
16427
  this.assertStartPendingDeliveryInvariants("queued-continuation-runner-credential-mint-failure");
@@ -16401,7 +16441,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16401
16441
  this.lifecycleRecords.setRestartSnapshot(agentId, {
16402
16442
  config: nextConfig,
16403
16443
  sessionId: ap.sessionId,
16404
- launchId: ap.launchId
16444
+ launchId: ap.launchId,
16445
+ processInstanceId: ap.processInstanceId
16405
16446
  });
16406
16447
  this.broadcastActivity(agentId, "online", "Process idle", [], void 0, "idle");
16407
16448
  });
@@ -16410,7 +16451,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16410
16451
  this.lifecycleRecords.setRestartSnapshot(agentId, {
16411
16452
  config: this.buildRestartSafeConfig(ap.config, ap.sessionId),
16412
16453
  sessionId: ap.sessionId,
16413
- launchId: ap.launchId
16454
+ launchId: ap.launchId,
16455
+ processInstanceId: ap.processInstanceId
16414
16456
  });
16415
16457
  if (!ap.driver.supportsStdinNotification) {
16416
16458
  logger.info(`[Agent ${agentId}] Turn completed; cached idle state for future restart`);
@@ -16422,7 +16464,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16422
16464
  this.lifecycleRecords.setRestartSnapshot(agentId, {
16423
16465
  config: this.buildRestartSafeConfig(ap.config, ap.sessionId),
16424
16466
  sessionId: ap.sessionId,
16425
- launchId: ap.launchId
16467
+ launchId: ap.launchId,
16468
+ processInstanceId: ap.processInstanceId
16426
16469
  });
16427
16470
  logger.warn(`[Agent ${agentId}] Recoverable provider stream failure (${reason}) \u2014 keeping agent wakeable`);
16428
16471
  this.sendAgentStatus(agentId, "active", ap.launchId);
@@ -16638,7 +16681,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16638
16681
  this.lifecycleRecords.setRestartSnapshot(agentId, {
16639
16682
  config: retryConfig,
16640
16683
  sessionId: retrySessionId,
16641
- launchId: ap.launchId
16684
+ launchId: ap.launchId,
16685
+ processInstanceId: ap.processInstanceId
16642
16686
  });
16643
16687
  this.recordDaemonTrace("daemon.agent.startup_timeout.retry_config_cached", {
16644
16688
  agentId,
@@ -17552,7 +17596,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17552
17596
  }
17553
17597
  return result;
17554
17598
  }
17555
- buildRuntimeProfileReport(agentId, config, sessionId, launchId, observedRuntimeHomeDir) {
17599
+ buildRuntimeProfileReport(agentId, config, sessionId, launchId, observedRuntimeHomeDir, processInstanceId) {
17556
17600
  const workspacePath = path14.join(this.dataDir, agentId);
17557
17601
  const runtimeHomeDir = observedRuntimeHomeDir || resolveRuntimeHomeDir(config, this.runtimeSessionHomeDir, workspacePath, { agentId, slockHome: this.slockHome });
17558
17602
  return {
@@ -17568,7 +17612,16 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17568
17612
  path: workspacePath,
17569
17613
  reachable: true
17570
17614
  },
17571
- sessionRef: sessionId ? resolveRuntimeSessionRef(config.runtime, sessionId, runtimeHomeDir, workspacePath, { agentId, workingDirectory: workspacePath }) : null
17615
+ sessionRef: sessionId ? resolveRuntimeSessionRef(config.runtime, sessionId, runtimeHomeDir, workspacePath, {
17616
+ agentId,
17617
+ workingDirectory: workspacePath,
17618
+ // Join keys come from the CALLER's context (running agent OR idle
17619
+ // restart snapshot) — reading only live state here left the
17620
+ // restart/adopt path writing launchId:null markers, exactly the
17621
+ // V3 class this exists to close (Leiysky review on #3863).
17622
+ launchId: launchId || void 0,
17623
+ processInstanceId: processInstanceId ?? this.agents.get(agentId)?.processInstanceId
17624
+ }) : null
17572
17625
  }
17573
17626
  };
17574
17627
  }
@@ -17580,12 +17633,13 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17580
17633
  running.config,
17581
17634
  running.sessionId,
17582
17635
  running.launchId,
17583
- running.runtime.currentRuntimeHomeDir
17636
+ running.runtime.currentRuntimeHomeDir,
17637
+ running.processInstanceId
17584
17638
  );
17585
17639
  }
17586
17640
  const idle = this.lifecycleRecords.getRestartSnapshot(agentId);
17587
17641
  if (idle) {
17588
- return this.buildRuntimeProfileReport(agentId, idle.config, idle.sessionId, idle.launchId);
17642
+ return this.buildRuntimeProfileReport(agentId, idle.config, idle.sessionId, idle.launchId, null, idle.processInstanceId);
17589
17643
  }
17590
17644
  return null;
17591
17645
  }
@@ -17983,7 +18037,9 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17983
18037
  }
17984
18038
  const ref = resolveRuntimeSessionRef(runtime, actualSessionId, homeDir, workspaceDir, {
17985
18039
  agentId,
17986
- workingDirectory: workspaceDir
18040
+ workingDirectory: workspaceDir,
18041
+ launchId: this.agents.get(agentId)?.launchId || void 0,
18042
+ processInstanceId: this.agents.get(agentId)?.processInstanceId
17987
18043
  });
17988
18044
  const tier = runtimeTier(runtime);
17989
18045
  const span = this.tracer.startSpan("daemon.session_transcript.read", {
@@ -18251,9 +18307,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18251
18307
  entries,
18252
18308
  launchId,
18253
18309
  clientSeq,
18254
- producerFactId
18310
+ producerFactId,
18311
+ isHeartbeat: false
18255
18312
  });
18256
- this.recordActivityProducedTrace(agentId, activityKind, detail, detailKind, entries, ap, launchId, clientSeq, producerFactId);
18313
+ this.recordActivityProducedTrace(agentId, activityKind, detail, detailKind, entries, ap, launchId, clientSeq, producerFactId, false);
18257
18314
  if (ap) {
18258
18315
  ap.lastActivityKind = activityKind;
18259
18316
  ap.lastActivity = activityDisplay;
@@ -18279,9 +18336,12 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18279
18336
  detailKind: ap.lastActivityDetailKind,
18280
18337
  launchId: heartbeatLaunchId,
18281
18338
  clientSeq: heartbeatClientSeq,
18282
- producerFactId: heartbeatProducerFactId
18339
+ producerFactId: heartbeatProducerFactId,
18340
+ // The one knowing site: this timer re-broadcasts stale
18341
+ // lastActivity, so it declares its replay provenance.
18342
+ isHeartbeat: true
18283
18343
  });
18284
- this.recordActivityProducedTrace(agentId, ap.lastActivityKind, ap.lastActivityDetail, ap.lastActivityDetailKind, [], ap, heartbeatLaunchId, heartbeatClientSeq, heartbeatProducerFactId);
18344
+ this.recordActivityProducedTrace(agentId, ap.lastActivityKind, ap.lastActivityDetail, ap.lastActivityDetailKind, [], ap, heartbeatLaunchId, heartbeatClientSeq, heartbeatProducerFactId, true);
18285
18345
  }, ACTIVITY_HEARTBEAT_MS);
18286
18346
  ap.activityHeartbeat = { kind: "active", timer };
18287
18347
  }
@@ -18290,7 +18350,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18290
18350
  }
18291
18351
  }
18292
18352
  }
18293
- recordActivityProducedTrace(agentId, activityKind, detail, detailKind, entries, ap, launchId, clientSeq, producerFactId) {
18353
+ recordActivityProducedTrace(agentId, activityKind, detail, detailKind, entries, ap, launchId, clientSeq, producerFactId, isHeartbeat) {
18294
18354
  const runtimeContext = ap?.config.runtimeContext;
18295
18355
  this.recordDaemonTrace("daemon.agent.activity.produced", {
18296
18356
  agentId,
@@ -18312,6 +18372,11 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18312
18372
  client_seq_present: typeof clientSeq === "number",
18313
18373
  producerFactId,
18314
18374
  producer_fact_id: producerFactId,
18375
+ // #460 V1: the wire's producer-declared replay-provenance bit must be
18376
+ // independently verifiable from daemon-side evidence (L2<->L3 seam);
18377
+ // closed boolean, mirrors the agent:activity isHeartbeat field exactly.
18378
+ isHeartbeat,
18379
+ is_heartbeat: isHeartbeat,
18315
18380
  correlation_id: `agent:${agentId}:daemonActivity:${launchId ?? "legacy"}:${clientSeq ?? "unsequenced"}`,
18316
18381
  session_id_present: Boolean(ap?.sessionId),
18317
18382
  runtime: ap?.config.runtime
@@ -18360,9 +18425,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18360
18425
  launchId,
18361
18426
  probeId,
18362
18427
  clientSeq,
18363
- producerFactId
18428
+ producerFactId,
18429
+ isHeartbeat: false
18364
18430
  });
18365
- this.recordActivityProducedTrace(agentId, activityKind, detail, detailKind, [], ap, launchId, clientSeq, producerFactId);
18431
+ this.recordActivityProducedTrace(agentId, activityKind, detail, detailKind, [], ap, launchId, clientSeq, producerFactId, false);
18366
18432
  }
18367
18433
  flushPendingTrajectory(agentId) {
18368
18434
  const ap = this.agents.get(agentId);
@@ -21409,7 +21475,17 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
21409
21475
  spanAttrs: ["running_agents_count", "idle_agents_count"]
21410
21476
  },
21411
21477
  "daemon.agent.activity.produced": {
21412
- spanAttrs: ["agentId", "agent_id", "server_id", "machine_id", "activity", "detail_present", "entry_kinds", "ap_present", "launchId", "launch_id", "launch_id_present", "clientSeq", "client_seq", "client_seq_present", "correlation_id", "session_id_present", "runtime"]
21478
+ // isHeartbeat/is_heartbeat (#460 V1) and process_instance_id (#460 V3)
21479
+ // were emitted by agentProcessManager but scrubbed here (pilot violation
21480
+ // V4): this list is a RUNTIME allowlist (SpanAttrContractTracer), so an
21481
+ // emission-site key that is not added here silently dies before disk.
21482
+ // Witness for the pair lives in agentProcessManager.builtin.e2e.test.ts
21483
+ // behind a contract-wrapped tracer (production-isomorphic oracle).
21484
+ // producerFactId/producer_fact_id and activity_kind/detail_kind are ALSO
21485
+ // emitted-and-scrubbed today; deliberately NOT added here — banned-join-
21486
+ // key discipline for the fact id (#460 classification ruling) means
21487
+ // widening needs its own ruling, not a drive-by.
21488
+ spanAttrs: ["agentId", "agent_id", "server_id", "machine_id", "activity", "detail_present", "entry_kinds", "ap_present", "launchId", "launch_id", "launch_id_present", "clientSeq", "client_seq", "client_seq_present", "correlation_id", "session_id_present", "runtime", "isHeartbeat", "is_heartbeat", "process_instance_id"]
21413
21489
  },
21414
21490
  "daemon.agent.activity.skipped": {
21415
21491
  spanAttrs: ["agentId", "event_kind", "reason", "text_length"]
@@ -21493,7 +21569,7 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
21493
21569
  }
21494
21570
  async function runBundledSlockCli(argv) {
21495
21571
  process.argv = [process.execPath, "slock", ...argv];
21496
- await import("./dist-P3SAWND7.js");
21572
+ await import("./dist-I6ATP6I5.js");
21497
21573
  }
21498
21574
  function detectRuntimes(tracer = noopTracer) {
21499
21575
  const ids = [];