@slock-ai/daemon 0.56.0 → 0.56.1-play.20260603140721

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.
@@ -1861,6 +1861,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
1861
1861
  return candidates.filter((candidate) => existsSync(candidate.path));
1862
1862
  }
1863
1863
 
1864
+ // src/authEnv.ts
1865
+ var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
1866
+ var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
1867
+ function scrubDaemonAuthEnv(env) {
1868
+ delete env[DAEMON_API_KEY_ENV];
1869
+ return env;
1870
+ }
1871
+ function scrubDaemonChildEnv(env) {
1872
+ delete env[DAEMON_API_KEY_ENV];
1873
+ delete env[SLOCK_AGENT_TOKEN_ENV];
1874
+ return env;
1875
+ }
1876
+
1864
1877
  // src/agentCredentialProxy.ts
1865
1878
  import { randomBytes } from "crypto";
1866
1879
  import http from "http";
@@ -3016,7 +3029,9 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
3016
3029
  var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
3017
3030
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
3018
3031
  var RAW_CREDENTIAL_ENV_DENYLIST = [
3019
- "SLOCK_AGENT_CREDENTIAL_KEY"
3032
+ "SLOCK_AGENT_TOKEN",
3033
+ "SLOCK_AGENT_CREDENTIAL_KEY",
3034
+ "SLOCK_AGENT_CREDENTIAL_KEY_FILE"
3020
3035
  ];
3021
3036
  var cachedOpencliBinPath;
3022
3037
  function resolveOpencliBinPath() {
@@ -3231,7 +3246,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
3231
3246
  ...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
3232
3247
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
3233
3248
  };
3234
- delete spawnEnv.SLOCK_AGENT_TOKEN;
3249
+ scrubDaemonChildEnv(spawnEnv);
3235
3250
  for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
3236
3251
  delete spawnEnv[key];
3237
3252
  }
@@ -3660,7 +3675,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
3660
3675
  }
3661
3676
  function resolveCommandOnPath(command, deps = {}) {
3662
3677
  const platform = deps.platform ?? process.platform;
3663
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
3678
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
3664
3679
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
3665
3680
  const existsSyncFn = deps.existsSyncFn ?? existsSync2;
3666
3681
  if (platform === "win32") {
@@ -3686,7 +3701,7 @@ function firstExistingPath(candidates, deps = {}) {
3686
3701
  return null;
3687
3702
  }
3688
3703
  function readCommandVersion(command, args = [], deps = {}) {
3689
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
3704
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
3690
3705
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
3691
3706
  try {
3692
3707
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -5149,11 +5164,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
5149
5164
  return parseCursorModelsOutput(String(result.stdout || ""));
5150
5165
  }
5151
5166
  function buildCursorModelProbeEnv(deps = {}) {
5152
- return withWindowsUserEnvironment({
5167
+ return scrubDaemonChildEnv(withWindowsUserEnvironment({
5153
5168
  ...deps.env ?? process.env,
5154
5169
  FORCE_COLOR: "0",
5155
5170
  NO_COLOR: "1"
5156
- }, deps);
5171
+ }, deps));
5157
5172
  }
5158
5173
  function runCursorModelsCommand() {
5159
5174
  return spawnSync("cursor-agent", ["models"], {
@@ -5209,7 +5224,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
5209
5224
  }
5210
5225
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
5211
5226
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
5212
- const env = deps.env ?? process.env;
5227
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
5213
5228
  const winPath = path8.win32;
5214
5229
  let geminiEntry = null;
5215
5230
  try {
@@ -5381,13 +5396,16 @@ var GeminiDriver = class {
5381
5396
  // src/drivers/kimi.ts
5382
5397
  import { randomUUID as randomUUID2 } from "crypto";
5383
5398
  import { spawn as spawn7 } from "child_process";
5384
- import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
5399
+ import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
5385
5400
  import os3 from "os";
5386
5401
  import path9 from "path";
5387
5402
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
5388
5403
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
5389
5404
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
5390
5405
  var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
5406
+ var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
5407
+ var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
5408
+ var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
5391
5409
  function parseToolArguments(raw) {
5392
5410
  if (typeof raw !== "string") return raw;
5393
5411
  try {
@@ -5396,6 +5414,73 @@ function parseToolArguments(raw) {
5396
5414
  return raw;
5397
5415
  }
5398
5416
  }
5417
+ function readKimiConfigSource(home = os3.homedir(), env = process.env) {
5418
+ const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
5419
+ if (inlineConfig && inlineConfig.trim()) {
5420
+ return {
5421
+ raw: inlineConfig,
5422
+ explicitPath: null,
5423
+ sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
5424
+ };
5425
+ }
5426
+ const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
5427
+ const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
5428
+ try {
5429
+ return {
5430
+ raw: readFileSync3(configPath, "utf8"),
5431
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
5432
+ sourcePath: configPath
5433
+ };
5434
+ } catch {
5435
+ return {
5436
+ raw: null,
5437
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
5438
+ sourcePath: configPath
5439
+ };
5440
+ }
5441
+ }
5442
+ function buildKimiSpawnEnv(env = process.env) {
5443
+ const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
5444
+ delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
5445
+ delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
5446
+ return scrubDaemonChildEnv(spawnEnv);
5447
+ }
5448
+ function buildKimiEffectiveEnv(ctx, overrideEnv) {
5449
+ return {
5450
+ ...process.env,
5451
+ ...ctx.config.envVars || {},
5452
+ ...overrideEnv || {}
5453
+ };
5454
+ }
5455
+ function buildKimiLaunchOptions(ctx, opts = {}) {
5456
+ const env = buildKimiEffectiveEnv(ctx, opts.env);
5457
+ const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
5458
+ const args = [];
5459
+ let configFilePath = null;
5460
+ let configContent = null;
5461
+ if (source.explicitPath) {
5462
+ configFilePath = source.explicitPath;
5463
+ } else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
5464
+ configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
5465
+ configContent = source.raw;
5466
+ if (opts.writeGeneratedConfig !== false) {
5467
+ writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
5468
+ chmodSync(configFilePath, 384);
5469
+ }
5470
+ }
5471
+ if (configFilePath) {
5472
+ args.push("--config-file", configFilePath);
5473
+ }
5474
+ if (ctx.config.model && ctx.config.model !== "default") {
5475
+ args.push("--model", ctx.config.model);
5476
+ }
5477
+ return {
5478
+ args,
5479
+ env: buildKimiSpawnEnv(env),
5480
+ configFilePath,
5481
+ configContent
5482
+ };
5483
+ }
5399
5484
  function resolveKimiSpawn(commandArgs, deps = {}) {
5400
5485
  return {
5401
5486
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -5419,7 +5504,25 @@ var KimiDriver = class {
5419
5504
  };
5420
5505
  model = {
5421
5506
  detectedModelsVerifiedAs: "launchable",
5422
- toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
5507
+ toLaunchSpec: (modelId, ctx, opts) => {
5508
+ if (!ctx) return { args: ["--model", modelId] };
5509
+ const launchCtx = {
5510
+ ...ctx,
5511
+ config: {
5512
+ ...ctx.config,
5513
+ model: modelId
5514
+ }
5515
+ };
5516
+ const launch = buildKimiLaunchOptions(launchCtx, {
5517
+ home: opts?.home,
5518
+ writeGeneratedConfig: false
5519
+ });
5520
+ return {
5521
+ args: launch.args,
5522
+ env: launch.env,
5523
+ configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
5524
+ };
5525
+ }
5423
5526
  };
5424
5527
  supportsStdinNotification = true;
5425
5528
  mcpToolPrefix = "";
@@ -5473,6 +5576,7 @@ var KimiDriver = class {
5473
5576
  }
5474
5577
  }
5475
5578
  }), "utf8");
5579
+ const launch = buildKimiLaunchOptions(ctx);
5476
5580
  const args = [
5477
5581
  "--wire",
5478
5582
  "--yolo",
@@ -5481,15 +5585,16 @@ var KimiDriver = class {
5481
5585
  "--mcp-config-file",
5482
5586
  mcpConfigPath,
5483
5587
  "--session",
5484
- this.sessionId
5588
+ this.sessionId,
5589
+ ...launch.args
5485
5590
  ];
5486
5591
  const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
5487
5592
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
5488
5593
  args.push("--model", launchRuntimeFields.model);
5489
5594
  }
5490
5595
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
5491
- const launch = resolveKimiSpawn(args);
5492
- const proc = spawn7(launch.command, launch.args, {
5596
+ const spawnTarget = resolveKimiSpawn(args);
5597
+ const proc = spawn7(spawnTarget.command, spawnTarget.args, {
5493
5598
  cwd: ctx.workingDirectory,
5494
5599
  stdio: ["pipe", "pipe", "pipe"],
5495
5600
  env: spawnEnv,
@@ -5497,7 +5602,7 @@ var KimiDriver = class {
5497
5602
  // and has an 8191-character command-line limit. Kimi's official
5498
5603
  // installer/uv entrypoint is an executable, so launch it directly and
5499
5604
  // keep prompts on stdin / files instead of routing through cmd.exe.
5500
- shell: launch.shell
5605
+ shell: spawnTarget.shell
5501
5606
  });
5502
5607
  proc.stdin?.write(JSON.stringify({
5503
5608
  jsonrpc: "2.0",
@@ -5611,14 +5716,9 @@ var KimiDriver = class {
5611
5716
  return detectKimiModels();
5612
5717
  }
5613
5718
  };
5614
- function detectKimiModels(home = os3.homedir()) {
5615
- const configPath = path9.join(home, ".kimi", "config.toml");
5616
- let raw;
5617
- try {
5618
- raw = readFileSync3(configPath, "utf8");
5619
- } catch {
5620
- return null;
5621
- }
5719
+ function detectKimiModels(home = os3.homedir(), opts = {}) {
5720
+ const raw = readKimiConfigSource(home, opts.env).raw;
5721
+ if (raw === null) return null;
5622
5722
  const models = [];
5623
5723
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
5624
5724
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -5882,7 +5982,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
5882
5982
  const platform = deps.platform ?? process.platform;
5883
5983
  const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
5884
5984
  const result = spawnSyncFn("opencode", ["models"], {
5885
- env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
5985
+ env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
5886
5986
  encoding: "utf8",
5887
5987
  timeout: 5e3,
5888
5988
  shell: platform === "win32"
@@ -6145,7 +6245,6 @@ var OpenCodeDriver = class {
6145
6245
  import { randomUUID as randomUUID3 } from "crypto";
6146
6246
  import { EventEmitter } from "events";
6147
6247
  import { mkdirSync as mkdirSync4, readdirSync } from "fs";
6148
- import { PassThrough, Writable } from "stream";
6149
6248
  import path11 from "path";
6150
6249
  import {
6151
6250
  AuthStorage,
@@ -6164,6 +6263,13 @@ var PI_PROVIDER_LABELS = {
6164
6263
  openai: "OpenAI",
6165
6264
  openrouter: "OpenRouter"
6166
6265
  };
6266
+ function createPiSdkEventMappingState(sessionId = null) {
6267
+ return {
6268
+ sessionId,
6269
+ sessionAnnounced: false,
6270
+ sawTextDelta: false
6271
+ };
6272
+ }
6167
6273
  function buildPiSessionDir(workingDirectory) {
6168
6274
  return path11.join(workingDirectory, PI_SESSION_DIR);
6169
6275
  }
@@ -6189,12 +6295,6 @@ function findPiSessionFile(sessionDir, sessionId) {
6189
6295
  const match = entries.find((entry) => entry.endsWith(suffix));
6190
6296
  return match ? path11.join(sessionDir, match) : null;
6191
6297
  }
6192
- function piSdkEventToJsonLine(event) {
6193
- if (event.type === "agent_end") {
6194
- return JSON.stringify({ type: "agent_end" });
6195
- }
6196
- return JSON.stringify(event);
6197
- }
6198
6298
  function detectPiModelsFromRegistry(modelRegistry) {
6199
6299
  const models = [];
6200
6300
  const seen = /* @__PURE__ */ new Set();
@@ -6244,114 +6344,309 @@ function piErrorMessage(error) {
6244
6344
  }
6245
6345
  return "Unknown Pi error";
6246
6346
  }
6247
- var PiSdkProcess = class extends EventEmitter {
6248
- constructor(session) {
6249
- super();
6250
- this.session = session;
6251
- this.stdin = new Writable({
6252
- write: (chunk, _encoding, callback) => {
6253
- this.handleInput(String(chunk)).then(
6254
- () => callback(),
6255
- (error) => {
6256
- this.writeError(error);
6257
- callback();
6258
- }
6259
- );
6347
+ function pushSessionInitIfNeeded(state, events) {
6348
+ if (!state.sessionAnnounced && state.sessionId) {
6349
+ events.push({ kind: "session_init", sessionId: state.sessionId });
6350
+ state.sessionAnnounced = true;
6351
+ }
6352
+ }
6353
+ function mapPiAssistantMessageEvent(assistantEvent, state) {
6354
+ switch (assistantEvent.type) {
6355
+ case "thinking_delta":
6356
+ return typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0 ? [{ kind: "thinking", text: assistantEvent.delta }] : [];
6357
+ case "text_delta":
6358
+ if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
6359
+ state.sawTextDelta = true;
6360
+ return [{ kind: "text", text: assistantEvent.delta }];
6260
6361
  }
6261
- });
6262
- this.session.subscribe((event) => {
6263
- this.writeStdout(piSdkEventToJsonLine(event));
6264
- });
6362
+ return [];
6363
+ case "text_end":
6364
+ return !state.sawTextDelta && typeof assistantEvent.content === "string" && assistantEvent.content.length > 0 ? [{ kind: "text", text: assistantEvent.content }] : [];
6365
+ case "error":
6366
+ return [{ kind: "error", message: piErrorMessage(assistantEvent.error.errorMessage || assistantEvent.error) }];
6367
+ case "thinking_start":
6368
+ case "thinking_end":
6369
+ case "text_start":
6370
+ case "toolcall_start":
6371
+ case "toolcall_delta":
6372
+ case "toolcall_end":
6373
+ case "start":
6374
+ case "done":
6375
+ return [];
6376
+ default: {
6377
+ const _exhaustive = assistantEvent;
6378
+ return _exhaustive;
6379
+ }
6380
+ }
6381
+ }
6382
+ function mapPiSdkEventToParsedEvents(event, state) {
6383
+ const events = [];
6384
+ pushSessionInitIfNeeded(state, events);
6385
+ switch (event.type) {
6386
+ case "agent_start":
6387
+ case "turn_start":
6388
+ case "turn_end":
6389
+ case "message_end":
6390
+ case "tool_execution_update":
6391
+ case "queue_update":
6392
+ case "session_info_changed":
6393
+ case "thinking_level_changed":
6394
+ case "auto_retry_start":
6395
+ case "auto_retry_end":
6396
+ return events;
6397
+ case "message_start":
6398
+ if (event.message.role === "assistant") {
6399
+ state.sawTextDelta = false;
6400
+ }
6401
+ return events;
6402
+ case "message_update":
6403
+ events.push(...mapPiAssistantMessageEvent(event.assistantMessageEvent, state));
6404
+ return events;
6405
+ case "tool_execution_start":
6406
+ events.push({
6407
+ kind: "tool_call",
6408
+ name: event.toolName || "unknown_tool",
6409
+ input: event.args ?? {}
6410
+ });
6411
+ return events;
6412
+ case "tool_execution_end":
6413
+ events.push({ kind: "tool_output", name: event.toolName || "unknown_tool" });
6414
+ return events;
6415
+ case "compaction_start":
6416
+ events.push({ kind: "compaction_started" });
6417
+ return events;
6418
+ case "compaction_end":
6419
+ events.push({ kind: "compaction_finished" });
6420
+ return events;
6421
+ case "agent_end":
6422
+ events.push({ kind: "turn_end", sessionId: state.sessionId || void 0 });
6423
+ return events;
6424
+ default: {
6425
+ const _exhaustive = event;
6426
+ return _exhaustive;
6427
+ }
6265
6428
  }
6266
- stdout = new PassThrough();
6267
- stderr = new PassThrough();
6268
- stdin;
6269
- pid = void 0;
6270
- exitCode = null;
6271
- signalCode = null;
6272
- killed = false;
6273
- buffer = "";
6274
- closed = false;
6275
- kill(signal = "SIGTERM") {
6276
- if (this.closed) return false;
6277
- this.killed = true;
6278
- this.signalCode = typeof signal === "string" ? signal : null;
6279
- void this.shutdown(null, this.signalCode);
6280
- return true;
6429
+ }
6430
+ var PI_RUNTIME_SESSION_DESCRIPTOR = {
6431
+ transport: "sdk",
6432
+ lifecycle: "sdk_session",
6433
+ input: {
6434
+ initial: "start",
6435
+ idle: "sdk_prompt",
6436
+ busy: "sdk_steer"
6437
+ },
6438
+ readiness: "sdk_ready",
6439
+ turnBoundary: "sdk_event",
6440
+ startPolicy: "immediate",
6441
+ inFlightWake: "steer",
6442
+ busyDelivery: "direct",
6443
+ postTurn: "keep_alive"
6444
+ };
6445
+ async function createPiAgentSessionForContext(ctx, sessionId) {
6446
+ const sessionDir = buildPiSessionDir(ctx.workingDirectory);
6447
+ mkdirSync4(sessionDir, { recursive: true });
6448
+ const spawnEnv = await buildPiSpawnEnv(ctx);
6449
+ const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
6450
+ const authStorage = AuthStorage.create(path11.join(agentDir, "auth.json"));
6451
+ const modelRegistry = ModelRegistry.create(authStorage, path11.join(agentDir, "models.json"));
6452
+ const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
6453
+ const model = resolvePiModelFromRegistry(launchRuntimeFields.model, modelRegistry);
6454
+ if (launchRuntimeFields.model && launchRuntimeFields.model !== "default" && !model) {
6455
+ throw new Error(`Pi model not found: ${launchRuntimeFields.model}`);
6456
+ }
6457
+ const settingsManager = SettingsManager.inMemory({ compaction: { enabled: false } });
6458
+ const resourceLoader = new DefaultResourceLoader({
6459
+ cwd: ctx.workingDirectory,
6460
+ agentDir,
6461
+ settingsManager,
6462
+ systemPromptOverride: () => ctx.standingPrompt
6463
+ });
6464
+ await resourceLoader.reload();
6465
+ const existingSessionFile = ctx.config.sessionId ? findPiSessionFile(sessionDir, ctx.config.sessionId) : null;
6466
+ const sessionManager = existingSessionFile ? SessionManager.open(existingSessionFile, sessionDir, ctx.workingDirectory) : SessionManager.create(ctx.workingDirectory, sessionDir, { id: sessionId });
6467
+ const { session } = await createAgentSession({
6468
+ cwd: ctx.workingDirectory,
6469
+ agentDir,
6470
+ model,
6471
+ thinkingLevel: launchRuntimeFields.reasoningEffort,
6472
+ authStorage,
6473
+ modelRegistry,
6474
+ resourceLoader,
6475
+ customTools: [
6476
+ createBashTool(ctx.workingDirectory, {
6477
+ spawnHook: (spawnContext) => ({
6478
+ ...spawnContext,
6479
+ env: {
6480
+ ...spawnContext.env,
6481
+ ...spawnEnv
6482
+ }
6483
+ })
6484
+ })
6485
+ ],
6486
+ sessionManager,
6487
+ settingsManager
6488
+ });
6489
+ return session;
6490
+ }
6491
+ var PiSdkRuntimeSession = class {
6492
+ constructor(ctx, setCurrentSessionId, sessionFactory = createPiAgentSessionForContext) {
6493
+ this.ctx = ctx;
6494
+ this.setCurrentSessionId = setCurrentSessionId;
6495
+ this.sessionFactory = sessionFactory;
6496
+ this.mappingState = createPiSdkEventMappingState(ctx.config.sessionId || null);
6497
+ }
6498
+ descriptor = PI_RUNTIME_SESSION_DESCRIPTOR;
6499
+ events = new EventEmitter();
6500
+ mappingState;
6501
+ session = null;
6502
+ unsubscribe = null;
6503
+ started = false;
6504
+ didClose = false;
6505
+ requestedStopReason;
6506
+ exitInfo = null;
6507
+ get pid() {
6508
+ return void 0;
6281
6509
  }
6282
- ref() {
6283
- return this;
6510
+ get currentSessionId() {
6511
+ return this.mappingState.sessionId;
6284
6512
  }
6285
- unref() {
6286
- return this;
6513
+ get exitCode() {
6514
+ return this.exitInfo?.code ?? null;
6287
6515
  }
6288
- async handleInput(chunk) {
6289
- this.buffer += chunk;
6290
- const lines = this.buffer.split("\n");
6291
- this.buffer = lines.pop() || "";
6292
- for (const line of lines) {
6293
- if (!line.trim() || this.closed) continue;
6294
- await this.handleCommand(line);
6295
- }
6516
+ get signalCode() {
6517
+ return this.exitInfo?.signal ?? null;
6296
6518
  }
6297
- async handleCommand(raw) {
6298
- let command;
6299
- try {
6300
- command = JSON.parse(raw);
6301
- } catch (error) {
6302
- this.writeError(error);
6303
- return;
6519
+ get closed() {
6520
+ return this.didClose;
6521
+ }
6522
+ on(event, cb) {
6523
+ this.events.on(event, cb);
6524
+ }
6525
+ async start(input) {
6526
+ if (this.started) {
6527
+ return { ok: false, reason: "runtime_error", error: "runtime session already started" };
6304
6528
  }
6305
- const id = typeof command.id === "string" ? command.id : void 0;
6306
- try {
6307
- if (command.type === "prompt") {
6308
- await this.session.prompt(String(command.message ?? ""));
6309
- this.writeStdout(JSON.stringify({ id, type: "response", command: "prompt", success: true }));
6310
- } else if (command.type === "steer") {
6311
- await this.session.steer(String(command.message ?? ""));
6312
- this.writeStdout(JSON.stringify({ id, type: "response", command: "steer", success: true }));
6313
- } else {
6314
- throw new Error(`Unsupported Pi SDK command: ${command.type || "unknown"}`);
6529
+ if (this.didClose) return { ok: false, reason: "closed" };
6530
+ this.started = true;
6531
+ const sessionId = input.sessionId || this.ctx.config.sessionId || randomUUID3();
6532
+ this.mappingState.sessionId = sessionId;
6533
+ this.setCurrentSessionId(sessionId);
6534
+ const session = await this.sessionFactory({
6535
+ ...this.ctx,
6536
+ config: {
6537
+ ...this.ctx.config,
6538
+ sessionId
6315
6539
  }
6316
- } catch (error) {
6317
- this.writeStdout(JSON.stringify({
6318
- id,
6319
- type: "response",
6320
- command: command.type || "unknown",
6321
- success: false,
6322
- error: piErrorMessage(error)
6323
- }));
6540
+ }, sessionId);
6541
+ this.session = session;
6542
+ this.mappingState.sessionId = session.sessionId;
6543
+ this.setCurrentSessionId(session.sessionId);
6544
+ this.unsubscribe = session.subscribe((event) => {
6545
+ for (const parsed of mapPiSdkEventToParsedEvents(event, this.mappingState)) {
6546
+ this.events.emit("runtime_event", parsed);
6547
+ }
6548
+ });
6549
+ this.emitSessionInit();
6550
+ this.launchPrompt(input.text);
6551
+ return { ok: true, acceptedAs: "prompt" };
6552
+ }
6553
+ send(input) {
6554
+ if (this.didClose) return { ok: false, reason: "closed" };
6555
+ const session = this.session;
6556
+ if (!session) return { ok: false, reason: "closed" };
6557
+ if (input.mode === "busy") {
6558
+ this.deferSdkCall(() => session.steer(input.text));
6559
+ return { ok: true, acceptedAs: "steer" };
6560
+ }
6561
+ if (session.isStreaming) {
6562
+ return { ok: false, reason: "busy_rejected", error: "Pi session is still streaming" };
6563
+ }
6564
+ this.launchPrompt(input.text);
6565
+ return { ok: true, acceptedAs: "prompt" };
6566
+ }
6567
+ async stop(opts) {
6568
+ if (this.didClose) return;
6569
+ this.requestedStopReason = opts?.reason;
6570
+ const signal = opts?.signal ?? "SIGTERM";
6571
+ const session = this.session;
6572
+ if (session?.isStreaming) {
6573
+ try {
6574
+ await session.abort();
6575
+ } catch (error) {
6576
+ this.events.emit("stderr", piErrorMessage(error));
6577
+ }
6578
+ }
6579
+ await this.disposeSession();
6580
+ this.emitExitAndClose(null, signal);
6581
+ }
6582
+ async dispose() {
6583
+ if (this.didClose) return;
6584
+ await this.disposeSession();
6585
+ this.emitExitAndClose(0, null);
6586
+ }
6587
+ emitSessionInit() {
6588
+ const sessionId = this.mappingState.sessionId;
6589
+ if (!sessionId || this.mappingState.sessionAnnounced) return;
6590
+ this.mappingState.sessionAnnounced = true;
6591
+ this.events.emit("runtime_event", { kind: "session_init", sessionId });
6592
+ }
6593
+ launchPrompt(text) {
6594
+ const session = this.session;
6595
+ if (!session) {
6596
+ this.events.emit("runtime_event", {
6597
+ kind: "error",
6598
+ message: "Pi SDK session is not started"
6599
+ });
6600
+ return;
6324
6601
  }
6602
+ this.deferSdkCall(() => session.prompt(text));
6325
6603
  }
6326
- writeStdout(line) {
6327
- if (this.closed) return;
6328
- this.stdout.write(line + "\n");
6329
- }
6330
- writeError(error) {
6331
- if (this.closed) return;
6332
- this.stderr.write(piErrorMessage(error) + "\n");
6604
+ deferSdkCall(invoke) {
6605
+ setImmediate(() => {
6606
+ if (this.didClose) return;
6607
+ try {
6608
+ void invoke().catch((error) => {
6609
+ if (this.didClose) return;
6610
+ this.events.emit("runtime_event", {
6611
+ kind: "error",
6612
+ message: piErrorMessage(error)
6613
+ });
6614
+ });
6615
+ } catch (error) {
6616
+ if (this.didClose) return;
6617
+ this.events.emit("runtime_event", {
6618
+ kind: "error",
6619
+ message: piErrorMessage(error)
6620
+ });
6621
+ }
6622
+ });
6333
6623
  }
6334
- async shutdown(code, signal) {
6335
- if (this.closed) return;
6336
- this.closed = true;
6337
- this.exitCode = code;
6338
- this.signalCode = signal;
6624
+ async disposeSession() {
6625
+ const unsubscribe = this.unsubscribe;
6626
+ this.unsubscribe = null;
6339
6627
  try {
6340
- if (this.session.isStreaming) {
6341
- await this.session.abort();
6342
- }
6343
- } catch (error) {
6344
- this.stderr.write(piErrorMessage(error) + "\n");
6628
+ unsubscribe?.();
6629
+ } catch {
6345
6630
  }
6631
+ const session = this.session;
6632
+ this.session = null;
6346
6633
  try {
6347
- this.session.dispose();
6348
- } catch {
6634
+ session?.dispose();
6635
+ } catch (error) {
6636
+ this.events.emit("stderr", piErrorMessage(error));
6349
6637
  }
6350
- this.stdin.destroy();
6351
- this.stdout.end();
6352
- this.stderr.end();
6353
- this.emit("exit", code, signal);
6354
- this.emit("close", code, signal);
6638
+ }
6639
+ emitExitAndClose(code, signal) {
6640
+ if (this.didClose) return;
6641
+ this.didClose = true;
6642
+ const info = {
6643
+ code,
6644
+ signal,
6645
+ reason: this.requestedStopReason ? "requested" : "runtime_exit"
6646
+ };
6647
+ this.exitInfo = info;
6648
+ this.events.emit("exit", info);
6649
+ this.events.emit("close", info);
6355
6650
  }
6356
6651
  };
6357
6652
  var PiDriver = class {
@@ -6378,10 +6673,9 @@ var PiDriver = class {
6378
6673
  busyDeliveryMode = "direct";
6379
6674
  usesSlockCliForCommunication = true;
6380
6675
  sessionId = null;
6381
- sessionAnnounced = false;
6382
- sawTextDelta = false;
6383
- requestId = 0;
6384
- process = null;
6676
+ get currentSessionId() {
6677
+ return this.sessionId;
6678
+ }
6385
6679
  probe() {
6386
6680
  return {
6387
6681
  available: true,
@@ -6391,144 +6685,20 @@ var PiDriver = class {
6391
6685
  async detectModels() {
6392
6686
  return detectPiModels();
6393
6687
  }
6394
- async spawn(ctx) {
6395
- this.sessionId = ctx.config.sessionId || randomUUID3();
6396
- this.sessionAnnounced = false;
6397
- this.sawTextDelta = false;
6398
- this.requestId = 0;
6399
- const sessionDir = buildPiSessionDir(ctx.workingDirectory);
6400
- mkdirSync4(sessionDir, { recursive: true });
6401
- const spawnEnv = await buildPiSpawnEnv(ctx);
6402
- const agentDir = spawnEnv.PI_CODING_AGENT_DIR || getAgentDir();
6403
- const authStorage = AuthStorage.create(path11.join(agentDir, "auth.json"));
6404
- const modelRegistry = ModelRegistry.create(authStorage, path11.join(agentDir, "models.json"));
6405
- const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
6406
- const model = resolvePiModelFromRegistry(launchRuntimeFields.model, modelRegistry);
6407
- if (launchRuntimeFields.model && launchRuntimeFields.model !== "default" && !model) {
6408
- throw new Error(`Pi model not found: ${launchRuntimeFields.model}`);
6409
- }
6410
- const settingsManager = SettingsManager.inMemory({ compaction: { enabled: false } });
6411
- const resourceLoader = new DefaultResourceLoader({
6412
- cwd: ctx.workingDirectory,
6413
- agentDir,
6414
- settingsManager,
6415
- systemPromptOverride: () => ctx.standingPrompt
6416
- });
6417
- await resourceLoader.reload();
6418
- const existingSessionFile = ctx.config.sessionId ? findPiSessionFile(sessionDir, ctx.config.sessionId) : null;
6419
- const sessionManager = existingSessionFile ? SessionManager.open(existingSessionFile, sessionDir, ctx.workingDirectory) : SessionManager.create(ctx.workingDirectory, sessionDir, { id: this.sessionId });
6420
- const { session } = await createAgentSession({
6421
- cwd: ctx.workingDirectory,
6422
- agentDir,
6423
- model,
6424
- thinkingLevel: launchRuntimeFields.reasoningEffort,
6425
- authStorage,
6426
- modelRegistry,
6427
- resourceLoader,
6428
- customTools: [
6429
- createBashTool(ctx.workingDirectory, {
6430
- spawnHook: (spawnContext) => ({
6431
- ...spawnContext,
6432
- env: {
6433
- ...spawnContext.env,
6434
- ...spawnEnv
6435
- }
6436
- })
6437
- })
6438
- ],
6439
- sessionManager,
6440
- settingsManager
6441
- });
6442
- this.sessionId = session.sessionId;
6443
- const proc = new PiSdkProcess(session);
6444
- this.process = proc;
6445
- setImmediate(() => {
6446
- if (this.process === proc && !proc.killed) {
6447
- this.sendRpcCommand("prompt", { message: ctx.prompt });
6448
- }
6688
+ createSession(ctx) {
6689
+ this.sessionId = ctx.config.sessionId || null;
6690
+ return new PiSdkRuntimeSession(ctx, (sessionId) => {
6691
+ this.sessionId = sessionId;
6449
6692
  });
6450
- return { process: proc };
6451
6693
  }
6452
- parseLine(line) {
6453
- let event;
6454
- try {
6455
- event = JSON.parse(line);
6456
- } catch {
6457
- return [];
6458
- }
6459
- const events = [];
6460
- if (event.type === "session" && event.id) {
6461
- this.sessionId = event.id;
6462
- if (!this.sessionAnnounced) {
6463
- this.sessionAnnounced = true;
6464
- events.push({ kind: "session_init", sessionId: event.id });
6465
- }
6466
- return events;
6467
- }
6468
- if (!this.sessionAnnounced && this.sessionId) {
6469
- events.push({ kind: "session_init", sessionId: this.sessionId });
6470
- this.sessionAnnounced = true;
6471
- }
6472
- if (event.type === "response") {
6473
- if (event.data?.sessionId && event.data.sessionId !== this.sessionId) {
6474
- this.sessionId = event.data.sessionId;
6475
- }
6476
- if (event.success === false) {
6477
- events.push({ kind: "error", message: piErrorMessage(event.error) });
6478
- }
6479
- return events;
6480
- }
6481
- if (event.type === "message_start" && event.message?.role === "assistant") {
6482
- this.sawTextDelta = false;
6483
- return events;
6484
- }
6485
- const assistantEvent = event.assistantMessageEvent;
6486
- if (event.type === "message_update" && assistantEvent) {
6487
- switch (assistantEvent.type) {
6488
- case "thinking_delta":
6489
- if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
6490
- events.push({ kind: "thinking", text: assistantEvent.delta });
6491
- }
6492
- break;
6493
- case "text_delta":
6494
- if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
6495
- this.sawTextDelta = true;
6496
- events.push({ kind: "text", text: assistantEvent.delta });
6497
- }
6498
- break;
6499
- case "thinking_start":
6500
- case "text_start":
6501
- break;
6502
- case "tool_use":
6503
- case "tool_call":
6504
- case "tool_start":
6505
- events.push({
6506
- kind: "tool_call",
6507
- name: assistantEvent.name || assistantEvent.toolName || "unknown_tool",
6508
- input: assistantEvent.input ?? assistantEvent.parameters ?? {}
6509
- });
6510
- break;
6511
- case "text_end":
6512
- if (!this.sawTextDelta && typeof assistantEvent.content === "string" && assistantEvent.content.length > 0) {
6513
- events.push({ kind: "text", text: assistantEvent.content });
6514
- }
6515
- break;
6516
- }
6517
- return events;
6518
- }
6519
- if (event.type === "agent_end") {
6520
- events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
6521
- } else if (event.type === "error") {
6522
- events.push({ kind: "error", message: piErrorMessage(event.error ?? event.message) });
6523
- }
6524
- return events;
6694
+ async spawn(_ctx) {
6695
+ throw new Error("PiDriver uses a native RuntimeSession; child-process spawn is unsupported");
6525
6696
  }
6526
- encodeStdinMessage(text, _sessionId, opts) {
6527
- return JSON.stringify({
6528
- id: this.nextRequestId(),
6529
- type: opts?.mode === "idle" ? "prompt" : "steer",
6530
- message: text
6531
- });
6697
+ parseLine(_line) {
6698
+ return [];
6699
+ }
6700
+ encodeStdinMessage(_text, _sessionId, _opts) {
6701
+ return null;
6532
6702
  }
6533
6703
  buildSystemPrompt(config, _agentId) {
6534
6704
  return buildCliTransportSystemPrompt(config, {
@@ -6541,18 +6711,141 @@ var PiDriver = class {
6541
6711
  messageNotificationStyle: "direct"
6542
6712
  });
6543
6713
  }
6544
- nextRequestId() {
6545
- this.requestId += 1;
6546
- return String(this.requestId);
6714
+ };
6715
+
6716
+ // src/drivers/runtimeSession.ts
6717
+ import { EventEmitter as EventEmitter2 } from "events";
6718
+ function descriptorFromDriver(driver) {
6719
+ const lifecycle = driver.lifecycle.kind === "per_turn" ? "turn_based" : "persistent_stream";
6720
+ const idle = driver.supportsStdinNotification ? "stdin" : "unsupported";
6721
+ const busy = driver.supportsStdinNotification ? "stdin_steer" : "unsupported";
6722
+ return {
6723
+ transport: "child_process",
6724
+ lifecycle,
6725
+ input: {
6726
+ initial: "start",
6727
+ idle,
6728
+ busy
6729
+ },
6730
+ readiness: "spawned",
6731
+ turnBoundary: driver.lifecycle.kind === "per_turn" ? "process_exit" : "parsed_event",
6732
+ startPolicy: driver.lifecycle.kind === "per_turn" ? driver.lifecycle.start : "immediate",
6733
+ inFlightWake: driver.lifecycle.inFlightWake,
6734
+ busyDelivery: driver.busyDeliveryMode,
6735
+ postTurn: driver.terminateProcessOnTurnEnd ? "terminate_process" : driver.endStdinOnTurnEnd ? "close_stdin" : "keep_alive"
6736
+ };
6737
+ }
6738
+ var ChildProcessRuntimeSession = class {
6739
+ constructor(driver, ctx) {
6740
+ this.driver = driver;
6741
+ this.ctx = ctx;
6742
+ this.descriptor = descriptorFromDriver(driver);
6547
6743
  }
6548
- sendRpcCommand(type, params) {
6549
- this.process?.stdin?.write(JSON.stringify({
6550
- id: this.nextRequestId(),
6551
- type,
6552
- ...params
6553
- }) + "\n");
6744
+ descriptor;
6745
+ events = new EventEmitter2();
6746
+ process = null;
6747
+ started = false;
6748
+ stdoutBuffer = "";
6749
+ requestedStopReason;
6750
+ get pid() {
6751
+ return this.process?.pid;
6752
+ }
6753
+ get currentSessionId() {
6754
+ return this.driver.currentSessionId;
6755
+ }
6756
+ get exitCode() {
6757
+ return this.process?.exitCode ?? null;
6758
+ }
6759
+ get signalCode() {
6760
+ return this.process?.signalCode ?? null;
6761
+ }
6762
+ get closed() {
6763
+ return this.process ? this.process.exitCode != null || this.process.signalCode != null : false;
6764
+ }
6765
+ on(event, cb) {
6766
+ this.events.on(event, cb);
6767
+ }
6768
+ async start(input) {
6769
+ if (this.started) {
6770
+ return { ok: false, reason: "runtime_error", error: "runtime session already started" };
6771
+ }
6772
+ this.started = true;
6773
+ const launchCtx = {
6774
+ ...this.ctx,
6775
+ prompt: input.text,
6776
+ config: {
6777
+ ...this.ctx.config,
6778
+ sessionId: input.sessionId ?? this.ctx.config.sessionId
6779
+ }
6780
+ };
6781
+ const { process: process2 } = await this.driver.spawn(launchCtx);
6782
+ this.process = process2;
6783
+ this.attachProcess(process2);
6784
+ return { ok: true, acceptedAs: "prompt" };
6785
+ }
6786
+ send(input) {
6787
+ const proc = this.process;
6788
+ if (!proc || this.closed) return { ok: false, reason: "closed" };
6789
+ const encoded = this.driver.encodeStdinMessage(input.text, input.sessionId ?? null, { mode: input.mode });
6790
+ if (!encoded) return { ok: false, reason: "unsupported" };
6791
+ proc.stdin?.write(encoded + "\n");
6792
+ return { ok: true, acceptedAs: input.mode === "busy" ? "steer" : "prompt" };
6793
+ }
6794
+ async stop(opts) {
6795
+ const proc = this.process;
6796
+ if (!proc || this.closed) return;
6797
+ this.requestedStopReason = opts?.reason;
6798
+ proc.kill(opts?.signal ?? "SIGTERM");
6799
+ if (!opts?.forceAfterMs) return;
6800
+ setTimeout(() => {
6801
+ if (!this.closed) {
6802
+ try {
6803
+ proc.kill("SIGKILL");
6804
+ } catch {
6805
+ }
6806
+ }
6807
+ }, opts.forceAfterMs).unref?.();
6808
+ }
6809
+ attachProcess(process2) {
6810
+ process2.stdout?.on("data", (chunk) => {
6811
+ const chunkText = chunk.toString();
6812
+ this.events.emit("stdout", chunkText);
6813
+ this.stdoutBuffer += chunkText;
6814
+ const lines = this.stdoutBuffer.split("\n");
6815
+ this.stdoutBuffer = lines.pop() || "";
6816
+ for (const line of lines) {
6817
+ if (!line.trim()) continue;
6818
+ for (const event of this.driver.parseLine(line)) {
6819
+ this.events.emit("runtime_event", event);
6820
+ }
6821
+ }
6822
+ });
6823
+ process2.stderr?.on("data", (chunk) => {
6824
+ const text = chunk.toString().trim();
6825
+ if (text) this.events.emit("stderr", text);
6826
+ });
6827
+ process2.on("error", (err) => {
6828
+ this.events.emit("error", err);
6829
+ });
6830
+ process2.on("exit", (code, signal) => {
6831
+ this.events.emit("exit", {
6832
+ code,
6833
+ signal,
6834
+ reason: this.requestedStopReason ? "requested" : "runtime_exit"
6835
+ });
6836
+ });
6837
+ process2.on("close", (code, signal) => {
6838
+ this.events.emit("close", {
6839
+ code,
6840
+ signal,
6841
+ reason: this.requestedStopReason ? "requested" : "runtime_exit"
6842
+ });
6843
+ });
6554
6844
  }
6555
6845
  };
6846
+ function createChildProcessRuntimeSession(driver, ctx) {
6847
+ return new ChildProcessRuntimeSession(driver, ctx);
6848
+ }
6556
6849
 
6557
6850
  // src/drivers/index.ts
6558
6851
  var driverFactories = {
@@ -6953,6 +7246,15 @@ var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS = 3;
6953
7246
  function assertNeverApmEffect(effect) {
6954
7247
  throw new Error(`Unhandled APM gated steering effect: ${String(effect)}`);
6955
7248
  }
7249
+ function requireImmediateRuntimeSendResult(result, context) {
7250
+ if (typeof result.then === "function") {
7251
+ throw new Error(`RuntimeSession.send returned async result in synchronous APM path: ${context}`);
7252
+ }
7253
+ return result;
7254
+ }
7255
+ function runtimeSendFailureOutcome(result) {
7256
+ return !result.ok && result.reason === "unsupported" ? "encode_failed" : "send_failed";
7257
+ }
6956
7258
  var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS = 250;
6957
7259
  var WORKSPACE_TEXT_FILE_MAX_BYTES = 1048576;
6958
7260
  var WORKSPACE_IMAGE_PREVIEW_MAX_BYTES = 5 * 1024 * 1024;
@@ -7900,7 +8202,7 @@ function buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes) {
7900
8202
  if (ap.lastActivityDetail) {
7901
8203
  context.push(`after ${ap.lastActivityDetail}`);
7902
8204
  }
7903
- if (ap.driver.busyDeliveryMode === "gated") {
8205
+ if (ap.runtime.descriptor.busyDelivery === "gated") {
7904
8206
  context.push(`phase=${ap.gatedSteering.phase}`);
7905
8207
  }
7906
8208
  if (ap.gatedSteering.outstandingToolUses > 0) {
@@ -7932,10 +8234,10 @@ function buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes) {
7932
8234
  sessionIdPresent: Boolean(ap.sessionId),
7933
8235
  inboxCount: ap.inbox.length,
7934
8236
  pendingNotificationCount: ap.notifications.pendingCount,
7935
- processPidPresent: typeof ap.process.pid === "number",
8237
+ processPidPresent: typeof ap.runtime.pid === "number",
7936
8238
  busyDeliveryMode: ap.driver.busyDeliveryMode,
7937
8239
  supportsStdinNotification: ap.driver.supportsStdinNotification,
7938
- gatedPhase: ap.driver.busyDeliveryMode === "gated" ? ap.gatedSteering.phase : void 0,
8240
+ gatedPhase: ap.runtime.descriptor.busyDelivery === "gated" ? ap.gatedSteering.phase : void 0,
7939
8241
  outstandingToolUses: ap.gatedSteering.outstandingToolUses,
7940
8242
  compacting: ap.gatedSteering.compacting,
7941
8243
  recentStderrCount: ap.recentStderr.length,
@@ -8078,8 +8380,10 @@ var RUNTIME_TELEMETRY_RESERVED_ATTR_KEYS = /* @__PURE__ */ new Set([
8078
8380
  "runtimeResultId",
8079
8381
  "daemonVersion",
8080
8382
  "daemon_version",
8383
+ "daemon_version_present",
8081
8384
  "computerVersion",
8082
- "computer_version"
8385
+ "computer_version",
8386
+ "computer_version_present"
8083
8387
  ]);
8084
8388
  function sanitizeRuntimeTelemetryPayloadAttrs(attrs) {
8085
8389
  const sanitized = {};
@@ -8136,9 +8440,11 @@ var AgentProcessManager = class _AgentProcessManager {
8136
8440
  stdinNotificationRetryMs;
8137
8441
  cliTransportTraceDir = null;
8138
8442
  deliveryTraceContexts = /* @__PURE__ */ new WeakMap();
8139
- processExitTraceAttrs = /* @__PURE__ */ new WeakMap();
8443
+ runtimeExitTraceAttrs = /* @__PURE__ */ new WeakMap();
8140
8444
  agentVisibleBoundaries = /* @__PURE__ */ new Map();
8141
8445
  agentVisibleMessageIds = /* @__PURE__ */ new Map();
8446
+ daemonVersion;
8447
+ computerVersion;
8142
8448
  constructor(chatBridgePath, sendToServer, daemonApiKey, opts) {
8143
8449
  this.chatBridgePath = chatBridgePath;
8144
8450
  this.slockCliPath = opts.slockCliPath ?? "";
@@ -8150,6 +8456,8 @@ var AgentProcessManager = class _AgentProcessManager {
8150
8456
  this.driverResolver = opts.driverResolver || getDriver;
8151
8457
  this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
8152
8458
  this.tracer = opts.tracer ?? noopTracer;
8459
+ this.daemonVersion = opts.daemonVersion?.trim() || null;
8460
+ this.computerVersion = opts.computerVersion?.trim() || null;
8153
8461
  this.stdinNotificationRetryMs = Math.max(
8154
8462
  0,
8155
8463
  Math.floor(opts.stdinNotificationRetryMs ?? STDIN_NOTIFICATION_RETRY_DELAY_MS)
@@ -8390,7 +8698,7 @@ var AgentProcessManager = class _AgentProcessManager {
8390
8698
  });
8391
8699
  span.end(status);
8392
8700
  }
8393
- startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId) {
8701
+ startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient = false) {
8394
8702
  return {
8395
8703
  agentId,
8396
8704
  launchId,
@@ -8399,6 +8707,7 @@ var AgentProcessManager = class _AgentProcessManager {
8399
8707
  session_id_present: Boolean(config.sessionId),
8400
8708
  launch_id_present: Boolean(launchId),
8401
8709
  wake_message_present: Boolean(wakeMessage),
8710
+ wake_message_transient: Boolean(wakeMessage && wakeMessageTransient),
8402
8711
  unread_channels_count: unreadSummary ? Object.keys(unreadSummary).length : 0,
8403
8712
  resume_prompt_present: Boolean(resumePrompt),
8404
8713
  queue_depth: this.agentStartQueue.length,
@@ -8410,6 +8719,9 @@ var AgentProcessManager = class _AgentProcessManager {
8410
8719
  getDeliveryTraceContext(message) {
8411
8720
  return this.deliveryTraceContexts.get(message) ?? {};
8412
8721
  }
8722
+ isTransientDelivery(message) {
8723
+ return this.getDeliveryTraceContext(message).transient === true;
8724
+ }
8413
8725
  deliveryTraceAttrs(agentId, message, attrs = {}) {
8414
8726
  const context = this.getDeliveryTraceContext(message);
8415
8727
  const deliveryCorrelationId = context.deliveryId ?? message.message_id;
@@ -8421,14 +8733,15 @@ var AgentProcessManager = class _AgentProcessManager {
8421
8733
  sender_type: message.sender_type,
8422
8734
  messageId: message.message_id,
8423
8735
  message_id_present: Boolean(message.message_id),
8736
+ transient_delivery: context.transient === true,
8424
8737
  ...attrs
8425
8738
  };
8426
8739
  }
8427
- async startAgent(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId) {
8428
- this.recordDaemonTrace("daemon.agent.start.requested", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
8740
+ async startAgent(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient = false) {
8741
+ this.recordDaemonTrace("daemon.agent.start.requested", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient));
8429
8742
  if (this.agents.has(agentId)) {
8430
8743
  this.recordDaemonTrace("daemon.agent.start.ignored", {
8431
- ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId),
8744
+ ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
8432
8745
  reason: "already_running"
8433
8746
  });
8434
8747
  logger.info(`[Agent ${agentId}] Start ignored (already running)`);
@@ -8436,7 +8749,7 @@ var AgentProcessManager = class _AgentProcessManager {
8436
8749
  }
8437
8750
  if (this.agentsStarting.has(agentId)) {
8438
8751
  this.recordDaemonTrace("daemon.agent.start.ignored", {
8439
- ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId),
8752
+ ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
8440
8753
  reason: "already_starting"
8441
8754
  });
8442
8755
  logger.info(`[Agent ${agentId}] Start ignored (startup in progress)`);
@@ -8444,7 +8757,7 @@ var AgentProcessManager = class _AgentProcessManager {
8444
8757
  }
8445
8758
  if (this.queuedAgentStarts.has(agentId)) {
8446
8759
  this.recordDaemonTrace("daemon.agent.start.ignored", {
8447
- ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId),
8760
+ ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
8448
8761
  reason: "already_queued"
8449
8762
  });
8450
8763
  logger.info(`[Agent ${agentId}] Start ignored (startup already queued)`);
@@ -8455,6 +8768,7 @@ var AgentProcessManager = class _AgentProcessManager {
8455
8768
  agentId,
8456
8769
  config,
8457
8770
  wakeMessage,
8771
+ wakeMessageTransient,
8458
8772
  unreadSummary,
8459
8773
  resumePrompt,
8460
8774
  launchId,
@@ -8463,7 +8777,7 @@ var AgentProcessManager = class _AgentProcessManager {
8463
8777
  };
8464
8778
  this.agentStartQueue.push(item);
8465
8779
  this.queuedAgentStarts.set(agentId, item);
8466
- this.recordDaemonTrace("daemon.agent.start.queued", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
8780
+ this.recordDaemonTrace("daemon.agent.start.queued", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient));
8467
8781
  logger.info(
8468
8782
  `[Agent ${agentId}] Start queued (queue=${this.agentStartQueue.length}, active=${this.activeAgentStartCount}, max=${this.maxConcurrentAgentStarts}, interval=${this.agentStartIntervalMs}ms)`
8469
8783
  );
@@ -8480,7 +8794,7 @@ var AgentProcessManager = class _AgentProcessManager {
8480
8794
  const waitMs = shouldRateLimit ? Math.max(0, this.agentStartIntervalMs - elapsed) : 0;
8481
8795
  if (waitMs > 0) {
8482
8796
  this.recordDaemonTrace("daemon.agent.start.rate_limited", {
8483
- ...this.startQueueTraceAttrs(next.agentId, next.config, next.wakeMessage, next.unreadSummary, next.resumePrompt, next.launchId),
8797
+ ...this.startQueueTraceAttrs(next.agentId, next.config, next.wakeMessage, next.unreadSummary, next.resumePrompt, next.launchId, next.wakeMessageTransient),
8484
8798
  wait_ms: waitMs
8485
8799
  });
8486
8800
  this.agentStartPumpTimer = setTimeout(() => {
@@ -8493,7 +8807,7 @@ var AgentProcessManager = class _AgentProcessManager {
8493
8807
  if (!item) return;
8494
8808
  if (this.queuedAgentStarts.get(item.agentId) !== item) {
8495
8809
  this.recordDaemonTrace("daemon.agent.start.skipped", {
8496
- ...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId),
8810
+ ...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId, item.wakeMessageTransient),
8497
8811
  reason: "stale_queue_item"
8498
8812
  });
8499
8813
  this.pumpAgentStartQueue();
@@ -8502,7 +8816,7 @@ var AgentProcessManager = class _AgentProcessManager {
8502
8816
  this.queuedAgentStarts.delete(item.agentId);
8503
8817
  if (this.agents.has(item.agentId) || this.agentsStarting.has(item.agentId)) {
8504
8818
  this.recordDaemonTrace("daemon.agent.start.skipped", {
8505
- ...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId),
8819
+ ...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId, item.wakeMessageTransient),
8506
8820
  reason: "already_running_or_starting"
8507
8821
  });
8508
8822
  logger.info(`[Agent ${item.agentId}] Queued start skipped (already running or starting)`);
@@ -8516,14 +8830,15 @@ var AgentProcessManager = class _AgentProcessManager {
8516
8830
  logger.info(
8517
8831
  `[Agent ${item.agentId}] Dequeued start (remaining=${this.agentStartQueue.length}, active=${this.activeAgentStartCount})`
8518
8832
  );
8519
- this.recordDaemonTrace("daemon.agent.start.dequeued", this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId));
8833
+ this.recordDaemonTrace("daemon.agent.start.dequeued", this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId, item.wakeMessageTransient));
8520
8834
  this.startAgentNow(
8521
8835
  item.agentId,
8522
8836
  item.config,
8523
8837
  item.wakeMessage,
8524
8838
  item.unreadSummary,
8525
8839
  item.resumePrompt,
8526
- item.launchId
8840
+ item.launchId,
8841
+ item.wakeMessageTransient ?? false
8527
8842
  ).then(() => {
8528
8843
  this.releaseAgentStartSlot(item.agentId, "spawn attempted");
8529
8844
  item.resolve();
@@ -8558,7 +8873,7 @@ var AgentProcessManager = class _AgentProcessManager {
8558
8873
  this.agentStartPumpTimer = null;
8559
8874
  }
8560
8875
  this.recordDaemonTrace("daemon.agent.start.cancelled", {
8561
- ...this.startQueueTraceAttrs(agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId),
8876
+ ...this.startQueueTraceAttrs(agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId, item.wakeMessageTransient),
8562
8877
  reason
8563
8878
  }, "cancelled");
8564
8879
  logger.info(`[Agent ${agentId}] Queued start cancelled (${reason})`);
@@ -8569,7 +8884,7 @@ var AgentProcessManager = class _AgentProcessManager {
8569
8884
  for (const item of this.agentStartQueue) {
8570
8885
  if (this.queuedAgentStarts.get(item.agentId) === item) {
8571
8886
  this.recordDaemonTrace("daemon.agent.start.cancelled", {
8572
- ...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId),
8887
+ ...this.startQueueTraceAttrs(item.agentId, item.config, item.wakeMessage, item.unreadSummary, item.resumePrompt, item.launchId, item.wakeMessageTransient),
8573
8888
  reason
8574
8889
  }, "cancelled");
8575
8890
  logger.info(`[Agent ${item.agentId}] Queued start cancelled (${reason})`);
@@ -8584,10 +8899,10 @@ var AgentProcessManager = class _AgentProcessManager {
8584
8899
  this.agentStartPumpTimer = null;
8585
8900
  }
8586
8901
  }
8587
- async startAgentNow(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId) {
8902
+ async startAgentNow(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient = false) {
8588
8903
  if (this.agents.has(agentId)) {
8589
8904
  this.recordDaemonTrace("daemon.agent.spawn.skipped", {
8590
- ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId),
8905
+ ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
8591
8906
  reason: "already_running"
8592
8907
  });
8593
8908
  logger.info(`[Agent ${agentId}] Start ignored (already running)`);
@@ -8595,14 +8910,15 @@ var AgentProcessManager = class _AgentProcessManager {
8595
8910
  }
8596
8911
  if (this.agentsStarting.has(agentId)) {
8597
8912
  this.recordDaemonTrace("daemon.agent.spawn.skipped", {
8598
- ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId),
8913
+ ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
8599
8914
  reason: "already_starting"
8600
8915
  });
8601
8916
  logger.info(`[Agent ${agentId}] Start ignored (startup in progress)`);
8602
8917
  return;
8603
8918
  }
8604
8919
  this.agentsStarting.add(agentId);
8605
- this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
8920
+ this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient));
8921
+ let agentProcess = null;
8606
8922
  try {
8607
8923
  const driver = this.driverResolver(config.runtime || "claude");
8608
8924
  const legacyWakeRuntimeProfile = wakeMessage ? runtimeProfileNotificationFromMessage(wakeMessage) : null;
@@ -8665,15 +8981,23 @@ var AgentProcessManager = class _AgentProcessManager {
8665
8981
  prompt += getBusyDeliveryNote(driver);
8666
8982
  promptSource = "resume_prompt";
8667
8983
  } else if (wakeMessage) {
8668
- const runtimeProfileControlPrompt = formatRuntimeProfileControlPrompt([wakeMessage]);
8669
- if (runtimeProfileControlPrompt) {
8984
+ const transientWakeMessage = wakeMessageTransient === true;
8985
+ const runtimeProfileControlPrompt = transientWakeMessage ? null : formatRuntimeProfileControlPrompt([wakeMessage]);
8986
+ if (transientWakeMessage) {
8987
+ prompt = `System notice received:
8988
+
8989
+ ${formatIncomingMessage(wakeMessage, driver)}
8990
+
8991
+ Respond as appropriate. Complete all your work before stopping.
8992
+ ${RESPONSE_TARGET_HINT}`;
8993
+ } else if (runtimeProfileControlPrompt) {
8670
8994
  prompt = runtimeProfileControlPrompt;
8671
8995
  } else {
8672
8996
  wakeMessageDeliveredAsInboxUpdate = true;
8673
8997
  prompt = this.formatInboxUpdateRuntimeInput([wakeMessage, ...startingInboxMessages], driver);
8674
8998
  }
8675
- promptSource = runtimeProfileControlPrompt ? "runtime_profile_control_message" : "wake_inbox_update";
8676
- if (!runtimeProfileControlPrompt && unreadSummary && Object.keys(unreadSummary).length > 0) {
8999
+ promptSource = transientWakeMessage ? "transient_wake_message" : runtimeProfileControlPrompt ? "runtime_profile_control_message" : "wake_inbox_update";
9000
+ if (!transientWakeMessage && !runtimeProfileControlPrompt && unreadSummary && Object.keys(unreadSummary).length > 0) {
8677
9001
  const otherUnread = Object.entries(unreadSummary);
8678
9002
  if (otherUnread.length > 0) {
8679
9003
  prompt += `
@@ -8730,7 +9054,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
8730
9054
  this.sendAgentStatus(agentId, "active", launchId || null);
8731
9055
  this.broadcastActivity(agentId, "online", "Process idle");
8732
9056
  this.recordDaemonTrace("daemon.agent.spawn.deferred", {
8733
- ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId),
9057
+ ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
8734
9058
  pending_messages_count: pendingMessages.length,
8735
9059
  reason: "defer_until_concrete_message"
8736
9060
  });
@@ -8740,7 +9064,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
8740
9064
  }
8741
9065
  return;
8742
9066
  }
8743
- const { process: proc } = await driver.spawn({
9067
+ const runtimeContext = {
8744
9068
  agentId,
8745
9069
  config: effectiveConfig,
8746
9070
  standingPrompt,
@@ -8752,15 +9076,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
8752
9076
  launchId: launchId || null,
8753
9077
  agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId),
8754
9078
  cliTransportTraceDir: this.cliTransportTraceDir
8755
- });
8756
- this.recordDaemonTrace("daemon.agent.spawn.created", {
8757
- ...this.startQueueTraceAttrs(agentId, effectiveConfig, wakeMessage, unreadSummary, resumePrompt, launchId),
8758
- detached: false,
8759
- new_session: false,
8760
- process_pid_present: typeof proc.pid === "number"
8761
- });
8762
- const agentProcess = {
8763
- process: proc,
9079
+ };
9080
+ const runtime = driver.createSession?.(runtimeContext) ?? createChildProcessRuntimeSession(driver, runtimeContext);
9081
+ agentProcess = {
9082
+ runtime,
8764
9083
  driver,
8765
9084
  inbox: wakeMessageDeliveredAsInboxUpdate && wakeMessage ? [wakeMessage, ...startingInboxMessages] : startingInboxMessages,
8766
9085
  config: runtimeConfig,
@@ -8807,30 +9126,20 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
8807
9126
  }
8808
9127
  if (wakeMessageDeliveredAsInboxUpdate) {
8809
9128
  this.recordInboxUpdateProjection(agentId, agentProcess, agentProcess.inbox, "spawn_wake_inbox_update", "wake", prompt);
8810
- } else if (wakeMessage) {
9129
+ } else if (wakeMessage && !wakeMessageTransient) {
8811
9130
  this.consumeVisibleMessages(agentId, { messages: [wakeMessage], source: "spawn_wake_message" });
8812
9131
  this.ackInjectedRuntimeProfileMessages(agentId, [wakeMessage], agentProcess.launchId);
8813
9132
  }
8814
- let buffer = "";
8815
- proc.stdout?.on("data", (chunk) => {
8816
- const chunkText = chunk.toString();
9133
+ runtime.on("stdout", (chunkText) => {
8817
9134
  const current = this.agents.get(agentId);
8818
9135
  if (current) {
8819
9136
  current.recentStdout = pushRecentStdout(current.recentStdout, chunkText);
8820
9137
  }
8821
- buffer += chunkText;
8822
- const lines = buffer.split("\n");
8823
- buffer = lines.pop() || "";
8824
- for (const line of lines) {
8825
- if (!line.trim()) continue;
8826
- const events = driver.parseLine(line);
8827
- for (const event of events) {
8828
- this.handleParsedEvent(agentId, event, driver);
8829
- }
8830
- }
8831
9138
  });
8832
- proc.stderr?.on("data", (chunk) => {
8833
- const text = chunk.toString().trim();
9139
+ runtime.on("runtime_event", (event) => {
9140
+ this.handleParsedEvent(agentId, event, driver);
9141
+ });
9142
+ runtime.on("stderr", (text) => {
8834
9143
  if (!text) return;
8835
9144
  const current = this.agents.get(agentId);
8836
9145
  if (driver.id === "codex" && isCodexProviderReconnectLog(text)) {
@@ -8855,7 +9164,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
8855
9164
  }
8856
9165
  logger.error(`[Agent ${agentId} stderr]: ${text}`);
8857
9166
  });
8858
- proc.on("error", (err) => {
9167
+ runtime.on("error", (err) => {
8859
9168
  const current = this.agents.get(agentId);
8860
9169
  if (current) {
8861
9170
  current.spawnError = err.message;
@@ -8870,9 +9179,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
8870
9179
  }, "error");
8871
9180
  logger.error(`[Agent ${agentId}] Process error: ${err.message}`);
8872
9181
  });
8873
- proc.on("exit", (code, signal) => {
9182
+ runtime.on("exit", ({ code, signal }) => {
8874
9183
  const current = this.agents.get(agentId);
8875
- if (current && current.process === proc) {
9184
+ if (current && current.runtime === runtime) {
8876
9185
  current.exitCode = code;
8877
9186
  current.exitSignal = signal;
8878
9187
  }
@@ -8887,14 +9196,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
8887
9196
  runtime_trace_active: Boolean(current?.runtimeTraceSpan),
8888
9197
  inbox_count: current?.inbox.length ?? 0,
8889
9198
  pending_notification_count: current?.notifications.pendingCount ?? 0,
8890
- ...this.processExitTraceAttrs.get(proc)
9199
+ ...this.runtimeExitTraceAttrs.get(runtime)
8891
9200
  });
8892
9201
  logger.info(`[Agent ${agentId}] Process exited with code ${code}${signal ? ` (signal ${signal})` : ""}`);
8893
9202
  });
8894
- proc.on("close", (code, signal) => {
9203
+ runtime.on("close", ({ code, signal }) => {
8895
9204
  if (this.agents.has(agentId)) {
8896
9205
  const ap = this.agents.get(agentId);
8897
- if (ap.process !== proc) return;
9206
+ if (ap.runtime !== runtime) return;
8898
9207
  ap.notifications.clearTimer();
8899
9208
  if (ap.pendingTrajectory?.timer) {
8900
9209
  clearTimeout(ap.pendingTrajectory.timer);
@@ -9035,14 +9344,54 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9035
9344
  }
9036
9345
  }
9037
9346
  });
9347
+ const startResult = await runtime.start({ text: prompt, sessionId: effectiveConfig.sessionId || null });
9348
+ if (!startResult.ok) {
9349
+ throw new Error(`Runtime session failed to start: ${startResult.reason}${startResult.error ? ` (${startResult.error})` : ""}`);
9350
+ }
9351
+ this.recordDaemonTrace("daemon.agent.spawn.created", {
9352
+ ...this.startQueueTraceAttrs(agentId, effectiveConfig, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient),
9353
+ detached: false,
9354
+ new_session: false,
9355
+ process_pid_present: typeof runtime.pid === "number"
9356
+ });
9038
9357
  this.sendAgentStatus(agentId, "active", launchId || null);
9039
9358
  this.broadcastActivity(agentId, "working", "Starting\u2026");
9040
9359
  this.startRuntimeStartupTimeout(agentId, agentProcess);
9041
9360
  } catch (err) {
9042
9361
  this.agentsStarting.delete(agentId);
9362
+ this.cleanupFailedRuntimeStart(agentId, agentProcess, err);
9043
9363
  throw err;
9044
9364
  }
9045
9365
  }
9366
+ cleanupFailedRuntimeStart(agentId, ap, err) {
9367
+ if (!ap) return;
9368
+ if (this.agents.get(agentId) !== ap) return;
9369
+ ap.notifications.clearTimer();
9370
+ if (ap.pendingTrajectory?.timer) {
9371
+ clearTimeout(ap.pendingTrajectory.timer);
9372
+ ap.pendingTrajectory.timer = null;
9373
+ }
9374
+ if (ap.activityHeartbeat) {
9375
+ clearInterval(ap.activityHeartbeat);
9376
+ ap.activityHeartbeat = null;
9377
+ }
9378
+ if (ap.compactionWatchdog) {
9379
+ clearTimeout(ap.compactionWatchdog);
9380
+ ap.compactionWatchdog = null;
9381
+ }
9382
+ this.clearRuntimeStartupTimeout(ap);
9383
+ this.clearStalledRecoverySigtermWatchdog(ap);
9384
+ this.endRuntimeTrace(ap, "error", {
9385
+ outcome: "runtime-start-failed",
9386
+ failure_detail: err instanceof Error ? err.message : String(err),
9387
+ ...runtimeTraceCounterAttrs(ap),
9388
+ ...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
9389
+ });
9390
+ cleanupAgentCredentialProxy(agentId, ap.launchId);
9391
+ this.revokeManagedRunnerCredential(agentId, ap.config, ap.launchId);
9392
+ this.agents.delete(agentId);
9393
+ this.idleAgentConfigs.delete(agentId);
9394
+ }
9046
9395
  async buildSpawnConfig(agentId, config) {
9047
9396
  const baseConfig = config.serverUrl === this.serverUrl ? config : { ...config, serverUrl: this.serverUrl };
9048
9397
  const runnerConfig = await this.ensureManagedRunnerCredential(agentId, baseConfig);
@@ -9211,7 +9560,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9211
9560
  ap.inbox.push(message);
9212
9561
  if (ap.driver.supportsStdinNotification && ap.sessionId) {
9213
9562
  ap.notifications.add();
9214
- if (ap.driver.busyDeliveryMode === "gated") {
9563
+ if (ap.runtime.descriptor.busyDelivery === "gated") {
9215
9564
  this.recordGatedSteeringEvent(agentId, ap, "buffer", {
9216
9565
  reason: "runtime_profile",
9217
9566
  kind,
@@ -9278,12 +9627,16 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9278
9627
  cleanupAgentCredentialProxy(agentId, ap.launchId);
9279
9628
  this.revokeManagedRunnerCredential(agentId, ap.config, ap.launchId);
9280
9629
  this.agents.delete(agentId);
9281
- this.processExitTraceAttrs.set(ap.process, {
9630
+ this.runtimeExitTraceAttrs.set(ap.runtime, {
9282
9631
  stop_source: silent ? "daemon_internal" : "explicit_request",
9283
9632
  stop_wait_requested: wait,
9284
9633
  stop_silent: silent
9285
9634
  });
9286
- ap.process.kill("SIGTERM");
9635
+ await ap.runtime.stop({
9636
+ signal: "SIGTERM",
9637
+ forceAfterMs: wait ? 5e3 : void 0,
9638
+ reason: silent ? "daemon_internal" : "explicit_request"
9639
+ });
9287
9640
  if (!silent) {
9288
9641
  this.sendRuntimeProfileReportFor(agentId, ap.config, ap.sessionId, ap.launchId);
9289
9642
  this.sendAgentStatus(agentId, "inactive", ap.launchId);
@@ -9292,22 +9645,18 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9292
9645
  }
9293
9646
  if (wait) {
9294
9647
  await new Promise((resolve) => {
9295
- const forceKillTimer = setTimeout(() => {
9648
+ const timeoutTimer = setTimeout(() => {
9296
9649
  if (!silent) {
9297
9650
  logger.warn(`[Agent ${agentId}] Stop timed out; force killing`);
9298
9651
  }
9299
- try {
9300
- ap.process.kill("SIGKILL");
9301
- } catch {
9302
- }
9303
9652
  resolve();
9304
9653
  }, 5e3);
9305
- ap.process.on("exit", () => {
9306
- clearTimeout(forceKillTimer);
9654
+ ap.runtime.on("exit", () => {
9655
+ clearTimeout(timeoutTimer);
9307
9656
  resolve();
9308
9657
  });
9309
- if (ap.process.exitCode !== null || ap.process.signalCode !== null) {
9310
- clearTimeout(forceKillTimer);
9658
+ if (ap.runtime.closed) {
9659
+ clearTimeout(timeoutTimer);
9311
9660
  resolve();
9312
9661
  }
9313
9662
  });
@@ -9317,9 +9666,24 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9317
9666
  if (traceContext.deliveryId) {
9318
9667
  this.deliveryTraceContexts.set(message, traceContext);
9319
9668
  }
9669
+ if (traceContext.transient) {
9670
+ this.deliveryTraceContexts.set(message, traceContext);
9671
+ }
9672
+ const transientDelivery = this.isTransientDelivery(message);
9320
9673
  const ap = this.agents.get(agentId);
9321
9674
  if (!ap) {
9322
9675
  if (this.agentsStarting.has(agentId) || this.queuedAgentStarts.has(agentId)) {
9676
+ if (transientDelivery) {
9677
+ const queuedStart2 = this.queuedAgentStarts.get(agentId);
9678
+ this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
9679
+ outcome: "transient_dropped_during_start",
9680
+ accepted: true,
9681
+ process_present: false,
9682
+ startup_pending: true,
9683
+ launchId: queuedStart2?.launchId
9684
+ }));
9685
+ return true;
9686
+ }
9323
9687
  const queuedStart = this.queuedAgentStarts.get(agentId);
9324
9688
  const pending = this.startingInboxes.get(agentId) || [];
9325
9689
  pending.push(message);
@@ -9337,7 +9701,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9337
9701
  const cached = this.idleAgentConfigs.get(agentId);
9338
9702
  if (cached) {
9339
9703
  const driver = this.driverResolver(cached.config.runtime || "claude");
9340
- if (this.shouldDeferWakeMessage(agentId, driver, message)) {
9704
+ if (!transientDelivery && this.shouldDeferWakeMessage(agentId, driver, message)) {
9341
9705
  this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
9342
9706
  outcome: "deferred_wake_message",
9343
9707
  accepted: true,
@@ -9360,7 +9724,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9360
9724
  session_id_present: Boolean(cached.sessionId),
9361
9725
  launchId: cached.launchId || void 0
9362
9726
  }));
9363
- return this.startAgent(agentId, cached.config, message, void 0, void 0, cached.launchId || void 0).then(() => true, (err) => {
9727
+ return this.startAgent(agentId, cached.config, message, void 0, void 0, cached.launchId || void 0, transientDelivery).then(() => true, (err) => {
9364
9728
  logger.error(`[Agent ${agentId}] Failed to auto-restart`, err);
9365
9729
  if (this.reportRunnerCredentialMintFailure(agentId, err, cached.launchId, "idle_auto_restart")) {
9366
9730
  return false;
@@ -9370,6 +9734,15 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9370
9734
  });
9371
9735
  }
9372
9736
  logger.warn(`[Agent ${agentId}] Delivery received but no running process or cached idle config exists`);
9737
+ if (transientDelivery) {
9738
+ this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
9739
+ outcome: "transient_dropped_no_process",
9740
+ accepted: true,
9741
+ process_present: false,
9742
+ cached_idle_config_present: false
9743
+ }));
9744
+ return true;
9745
+ }
9373
9746
  this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
9374
9747
  outcome: "rejected_no_process",
9375
9748
  accepted: false,
@@ -9380,7 +9753,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9380
9753
  this.broadcastActivity(agentId, "offline", "Process unavailable; restart required");
9381
9754
  return false;
9382
9755
  }
9383
- if (this.shouldDeferWakeMessage(agentId, ap.driver, message)) {
9756
+ if (!transientDelivery && this.shouldDeferWakeMessage(agentId, ap.driver, message)) {
9384
9757
  this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
9385
9758
  outcome: "deferred_wake_message",
9386
9759
  accepted: true,
@@ -9395,6 +9768,19 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9395
9768
  }
9396
9769
  const stickyTerminalFailure = classifyStickyTerminalFailure(ap);
9397
9770
  if (stickyTerminalFailure) {
9771
+ if (transientDelivery) {
9772
+ this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
9773
+ outcome: "transient_dropped_terminal_runtime_error",
9774
+ accepted: true,
9775
+ process_present: true,
9776
+ runtime: ap.config.runtime,
9777
+ session_id_present: Boolean(ap.sessionId),
9778
+ launchId: ap.launchId || void 0,
9779
+ is_idle: ap.isIdle,
9780
+ inbox_count: ap.inbox.length
9781
+ }));
9782
+ return true;
9783
+ }
9398
9784
  ap.inbox.push(message);
9399
9785
  this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
9400
9786
  outcome: "queued_terminal_runtime_error",
@@ -9411,6 +9797,30 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9411
9797
  return true;
9412
9798
  }
9413
9799
  if (ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
9800
+ if (transientDelivery) {
9801
+ this.commitApmIdleState(agentId, ap, false);
9802
+ this.startRuntimeTrace(agentId, ap, "stdin-idle-delivery", [message]);
9803
+ this.broadcastActivity(agentId, "working", "Message received");
9804
+ const stdinAccepted2 = this.deliverMessagesViaStdin(
9805
+ agentId,
9806
+ ap,
9807
+ [message],
9808
+ "idle",
9809
+ { transient: true }
9810
+ );
9811
+ this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
9812
+ outcome: "stdin_idle_transient_delivery",
9813
+ accepted: true,
9814
+ process_present: true,
9815
+ runtime: ap.config.runtime,
9816
+ session_id_present: true,
9817
+ launchId: ap.launchId || void 0,
9818
+ stdin_delivery_accepted: stdinAccepted2,
9819
+ delivered_messages_count: 1,
9820
+ inbox_count: ap.inbox.length
9821
+ }));
9822
+ return true;
9823
+ }
9414
9824
  ap.inbox.push(message);
9415
9825
  const nextMessages = [...ap.inbox];
9416
9826
  this.commitApmIdleState(agentId, ap, false);
@@ -9435,6 +9845,19 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9435
9845
  }));
9436
9846
  return true;
9437
9847
  }
9848
+ if (transientDelivery) {
9849
+ this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
9850
+ outcome: "transient_dropped_busy",
9851
+ accepted: true,
9852
+ process_present: true,
9853
+ runtime: ap.config.runtime,
9854
+ session_id_present: Boolean(ap.sessionId),
9855
+ launchId: ap.launchId || void 0,
9856
+ is_idle: ap.isIdle,
9857
+ inbox_count: ap.inbox.length
9858
+ }));
9859
+ return true;
9860
+ }
9438
9861
  ap.inbox.push(message);
9439
9862
  if (this.recoverStaleProcessForQueuedMessageIfNeeded(agentId, ap)) {
9440
9863
  this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
@@ -9475,7 +9898,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9475
9898
  if (ap.gatedSteering.compacting) {
9476
9899
  ap.notifications.add();
9477
9900
  ap.notifications.clearTimer();
9478
- if (ap.driver.busyDeliveryMode === "gated") {
9901
+ if (ap.runtime.descriptor.busyDelivery === "gated") {
9479
9902
  this.recordGatedSteeringEvent(agentId, ap, "buffer", {
9480
9903
  reason: "compaction_boundary",
9481
9904
  pendingMessages: ap.inbox.length
@@ -9500,7 +9923,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9500
9923
  }));
9501
9924
  return true;
9502
9925
  }
9503
- if (ap.driver.busyDeliveryMode === "gated") {
9926
+ if (ap.runtime.descriptor.busyDelivery === "gated") {
9504
9927
  ap.notifications.add();
9505
9928
  if (!ap.notifications.hasTimer) {
9506
9929
  this.scheduleStdinNotification(agentId, ap, STDIN_NOTIFICATION_INITIAL_DELAY_MS);
@@ -9683,7 +10106,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9683
10106
  traceparent: formatTraceparent(span.context)
9684
10107
  };
9685
10108
  const ap = this.agents.get(agentId);
9686
- if (ap && !(ap.sessionId && ap.driver.supportsStdinNotification && ap.isIdle) && !(ap.sessionId && ap.driver.busyDeliveryMode === "direct")) {
10109
+ if (ap && !(ap.sessionId && ap.driver.supportsStdinNotification && ap.isIdle) && !(ap.sessionId && ap.runtime.descriptor.busyDelivery === "direct")) {
9687
10110
  this.enqueueRuntimeProfileNotification(agentId, ap, message, kind, key);
9688
10111
  span.end("ok", {
9689
10112
  attrs: {
@@ -9713,7 +10136,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9713
10136
  });
9714
10137
  return written;
9715
10138
  }
9716
- if (ap?.sessionId && ap.driver.busyDeliveryMode === "direct") {
10139
+ if (ap?.sessionId && ap.runtime.descriptor.busyDelivery === "direct") {
9717
10140
  const written = this.deliverMessagesViaStdin(agentId, ap, [message], "busy");
9718
10141
  span.end(written ? "ok" : "error", {
9719
10142
  attrs: {
@@ -10136,23 +10559,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10136
10559
  clearTimeout(ap.stalledRecoverySigtermTimer);
10137
10560
  ap.stalledRecoverySigtermTimer = null;
10138
10561
  }
10139
- mergeProcessExitTraceAttrs(proc, attrs) {
10140
- this.processExitTraceAttrs.set(proc, {
10141
- ...this.processExitTraceAttrs.get(proc) ?? {},
10562
+ mergeRuntimeExitTraceAttrs(runtime, attrs) {
10563
+ this.runtimeExitTraceAttrs.set(runtime, {
10564
+ ...this.runtimeExitTraceAttrs.get(runtime) ?? {},
10142
10565
  ...attrs
10143
10566
  });
10144
10567
  }
10145
10568
  startStalledRecoverySigtermWatchdog(agentId, ap, runtimeLabel, queuedMessagesAtSignal, staleForMs) {
10146
10569
  this.clearStalledRecoverySigtermWatchdog(ap);
10147
10570
  const timeoutMs = stalledRecoverySigtermTimeoutMs();
10148
- const processAtSignal = ap.process;
10571
+ const runtimeAtSignal = ap.runtime;
10149
10572
  ap.stalledRecoverySigtermTimer = setTimeout(() => {
10150
10573
  ap.stalledRecoverySigtermTimer = null;
10151
10574
  const current = this.agents.get(agentId);
10152
- if (!current || current !== ap || current.process !== processAtSignal || current.expectedTerminationReason !== "stalled_recovery") {
10575
+ if (!current || current !== ap || current.runtime !== runtimeAtSignal || current.expectedTerminationReason !== "stalled_recovery") {
10153
10576
  return;
10154
10577
  }
10155
- this.mergeProcessExitTraceAttrs(processAtSignal, {
10578
+ this.mergeRuntimeExitTraceAttrs(runtimeAtSignal, {
10156
10579
  stalled_recovery_sigterm_timeout: true,
10157
10580
  stalled_recovery_sigterm_timeout_ms: timeoutMs
10158
10581
  });
@@ -10166,7 +10589,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10166
10589
  queued_messages_at_signal: queuedMessagesAtSignal,
10167
10590
  stale_age_ms_at_signal: staleForMs,
10168
10591
  timeout_ms: timeoutMs,
10169
- process_pid_present: typeof processAtSignal.pid === "number",
10592
+ process_pid_present: typeof runtimeAtSignal.pid === "number",
10170
10593
  session_id_present: Boolean(current.sessionId),
10171
10594
  supports_stdin_notification: current.driver.supportsStdinNotification,
10172
10595
  busy_delivery_mode: current.driver.busyDeliveryMode
@@ -10175,7 +10598,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10175
10598
  `[Agent ${agentId}] Stalled ${runtimeLabel} runtime did not exit after SIGTERM within ${timeoutMs}ms; force killing`
10176
10599
  );
10177
10600
  try {
10178
- processAtSignal.kill("SIGKILL");
10601
+ void runtimeAtSignal.stop({ signal: "SIGKILL", reason: "stalled_recovery_sigterm_timeout" });
10179
10602
  } catch (err) {
10180
10603
  const reason = err instanceof Error ? err.message : String(err);
10181
10604
  this.recordDaemonTrace("daemon.agent.stalled_recovery.sigkill_failed", {
@@ -10361,7 +10784,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10361
10784
  ap.runtimeProgress.noteRuntimeEvent(eventKind);
10362
10785
  }
10363
10786
  recordGatedSteeringEvent(agentId, ap, event, attrs = {}) {
10364
- if (ap.driver.busyDeliveryMode !== "gated") return;
10787
+ if (ap.runtime.descriptor.busyDelivery !== "gated") return;
10365
10788
  const reduction = reduceApmGatedRecentEvent(ap.gatedSteering, { event });
10366
10789
  this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState);
10367
10790
  this.recordRuntimeTraceEvent(agentId, ap, `runtime.gated_steering.${event}`, {
@@ -10396,7 +10819,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10396
10819
  }
10397
10820
  notifyGatedSteeringBoundary(agentId, ap, reason) {
10398
10821
  const readiness = reduceApmGatedFlushReadiness(ap.gatedSteering, {
10399
- isGated: ap.driver.busyDeliveryMode === "gated",
10822
+ isGated: ap.runtime.descriptor.busyDelivery === "gated",
10400
10823
  hasSession: Boolean(ap.sessionId),
10401
10824
  inboxLength: ap.inbox.length,
10402
10825
  reason
@@ -10446,7 +10869,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10446
10869
  });
10447
10870
  return true;
10448
10871
  }
10449
- if (ap.driver.busyDeliveryMode === "gated") {
10872
+ if (ap.runtime.descriptor.busyDelivery === "gated") {
10450
10873
  const flushReduction = reduceApmGatedFlush(ap.gatedSteering, { reason: effect.reason });
10451
10874
  this.commitGatedSteeringDecisionState(agentId, ap, flushReduction.nextState);
10452
10875
  this.recordGatedSteeringEvent(agentId, ap, "flush", {
@@ -10548,12 +10971,12 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10548
10971
  this.sendAgentStatus(agentId, "inactive", ap.launchId);
10549
10972
  this.idleAgentConfigs.delete(agentId);
10550
10973
  try {
10551
- this.processExitTraceAttrs.set(ap.process, {
10974
+ this.runtimeExitTraceAttrs.set(ap.runtime, {
10552
10975
  stop_source: "startup_timeout",
10553
10976
  expectedTerminationReason: "startup_timeout",
10554
10977
  timeout_ms: timeoutMs
10555
10978
  });
10556
- ap.process.kill("SIGTERM");
10979
+ void ap.runtime.stop({ signal: "SIGTERM", reason: "startup_timeout" });
10557
10980
  } catch (err) {
10558
10981
  const reason = err instanceof Error ? err.message : String(err);
10559
10982
  logger.warn(`[Agent ${agentId}] Failed to terminate startup-timed-out ${ap.driver.id} process: ${reason}`);
@@ -10639,13 +11062,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10639
11062
  );
10640
11063
  this.broadcastActivity(agentId, "working", `Restarting stalled ${runtimeLabel} runtime for queued message`);
10641
11064
  try {
10642
- this.processExitTraceAttrs.set(ap.process, {
11065
+ this.runtimeExitTraceAttrs.set(ap.runtime, {
10643
11066
  stop_source: "stalled_recovery",
10644
11067
  expectedTerminationReason: "stalled_recovery",
10645
11068
  queued_messages_count: ap.inbox.length
10646
11069
  });
10647
11070
  this.startStalledRecoverySigtermWatchdog(agentId, ap, runtimeLabel, ap.inbox.length, staleForMs);
10648
- ap.process.kill("SIGTERM");
11071
+ void ap.runtime.stop({ signal: "SIGTERM", reason: "stalled_recovery" });
10649
11072
  } catch (err) {
10650
11073
  this.clearStalledRecoverySigtermWatchdog(ap);
10651
11074
  const reason = err instanceof Error ? err.message : String(err);
@@ -10821,11 +11244,11 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10821
11244
  if (ap.driver.terminateProcessOnTurnEnd) {
10822
11245
  logger.info(`[Agent ${agentId}] Turn completed; terminating ${ap.driver.id} process`);
10823
11246
  try {
10824
- this.processExitTraceAttrs.set(ap.process, {
11247
+ this.runtimeExitTraceAttrs.set(ap.runtime, {
10825
11248
  stop_source: "turn_end",
10826
11249
  expectedTerminationReason: "turn_end"
10827
11250
  });
10828
- ap.process.kill("SIGTERM");
11251
+ void ap.runtime.stop({ signal: "SIGTERM", reason: "turn_end" });
10829
11252
  } catch (err) {
10830
11253
  const reason = err instanceof Error ? err.message : String(err);
10831
11254
  logger.warn(`[Agent ${agentId}] Failed to terminate ${ap.driver.id} after turn_end: ${reason}`);
@@ -10847,7 +11270,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10847
11270
  if (runtimeErrorDiagnostics.spanAttrs.runtime_error_action_required === true) {
10848
11271
  visibleErrorMessage = formatRuntimeLoginRequiredMessage(ap.driver.id);
10849
11272
  }
10850
- const shouldDisableToolBoundaryFlush = ap.driver.busyDeliveryMode === "gated" && this.isThinkingBlockMutationError(event.message);
11273
+ const shouldDisableToolBoundaryFlush = ap.runtime.descriptor.busyDelivery === "gated" && this.isThinkingBlockMutationError(event.message);
10851
11274
  const terminalFailure = classifyTerminalFailure(ap);
10852
11275
  const reduction = reduceApmGatedError(ap.gatedSteering, {
10853
11276
  disableToolBoundaryFlush: shouldDisableToolBoundaryFlush,
@@ -10878,11 +11301,11 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10878
11301
  if (terminalFailure.actionRequired) {
10879
11302
  logger.warn(`[Agent ${agentId}] ${ap.driver.id} auth requires user action; terminating runtime process`);
10880
11303
  try {
10881
- this.processExitTraceAttrs.set(ap.process, {
11304
+ this.runtimeExitTraceAttrs.set(ap.runtime, {
10882
11305
  stop_source: "runtime_auth_error",
10883
11306
  runtime_error_class: "AuthError"
10884
11307
  });
10885
- ap.process.kill("SIGTERM");
11308
+ void ap.runtime.stop({ signal: "SIGTERM", reason: "runtime_auth_error" });
10886
11309
  } catch (err) {
10887
11310
  const reason = err instanceof Error ? err.message : String(err);
10888
11311
  logger.warn(`[Agent ${agentId}] Failed to terminate ${ap.driver.id} after auth error: ${reason}`);
@@ -10907,8 +11330,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10907
11330
  recordRuntimeTelemetry(agentId, ap, event) {
10908
11331
  const sessionId = ap.driver.currentSessionId ?? event.sessionId;
10909
11332
  const payloadAttrs = sanitizeRuntimeTelemetryPayloadAttrs(event.attrs);
11333
+ const versionAttrs = this.runtimeTelemetryVersionAttrs();
10910
11334
  const telemetryAttrs = {
10911
11335
  ...payloadAttrs,
11336
+ ...versionAttrs,
10912
11337
  ...event.source ? { source: event.source } : {},
10913
11338
  ...event.usageKind ? { usageKind: event.usageKind } : {},
10914
11339
  ...sessionId ? { sessionId } : {},
@@ -10926,6 +11351,24 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10926
11351
  ap.runtimeTraceSpan?.addEvent(`runtime.telemetry.${event.name}`, telemetryAttrs);
10927
11352
  this.recordDaemonTrace(`daemon.runtime.telemetry.${event.name}`, attrs);
10928
11353
  }
11354
+ runtimeTelemetryVersionAttrs() {
11355
+ return {
11356
+ ...this.daemonVersion ? {
11357
+ daemonVersion: this.daemonVersion,
11358
+ daemon_version: this.daemonVersion,
11359
+ daemon_version_present: true
11360
+ } : {
11361
+ daemon_version_present: false
11362
+ },
11363
+ ...this.computerVersion ? {
11364
+ computerVersion: this.computerVersion,
11365
+ computer_version: this.computerVersion,
11366
+ computer_version_present: true
11367
+ } : {
11368
+ computer_version_present: false
11369
+ }
11370
+ };
11371
+ }
10929
11372
  sendAgentStatus(agentId, status, launchId) {
10930
11373
  this.sendToServer({ type: "agent:status", agentId, status, launchId: launchId || void 0 });
10931
11374
  }
@@ -11009,9 +11452,11 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
11009
11452
  ...projectionAttrs
11010
11453
  });
11011
11454
  logger.info(`[Agent ${agentId}] Sending stdin inbox update: ${inboxRows.length} changed target(s), ${inboxCount} pending message(s)`);
11012
- const encoded = ap.driver.encodeStdinMessage(notification, ap.sessionId, { mode: "busy" });
11013
- if (encoded) {
11014
- ap.process.stdin?.write(encoded + "\n");
11455
+ const sendResult = requireImmediateRuntimeSendResult(
11456
+ ap.runtime.send({ mode: "busy", text: notification, sessionId: ap.sessionId }),
11457
+ "busy inbox notification"
11458
+ );
11459
+ if (sendResult.ok) {
11015
11460
  this.recordDaemonTrace("daemon.agent.inbox_update.pushed", {
11016
11461
  agentId,
11017
11462
  runtime: ap.config.runtime,
@@ -11036,14 +11481,17 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
11036
11481
  return true;
11037
11482
  } else {
11038
11483
  ap.notifications.add(count);
11039
- const retryScheduled = ap.driver.busyDeliveryMode === "direct" ? this.scheduleStdinNotification(agentId, ap, this.stdinNotificationRetryMs) : false;
11484
+ const retryScheduled = ap.runtime.descriptor.busyDelivery === "direct" ? this.scheduleStdinNotification(agentId, ap, this.stdinNotificationRetryMs) : false;
11485
+ const outcome = runtimeSendFailureOutcome(sendResult);
11040
11486
  this.recordDaemonTrace("daemon.agent.stdin_notification", {
11041
11487
  agentId,
11042
11488
  runtime: ap.config.runtime,
11043
11489
  model: ap.config.model,
11044
11490
  launchId: ap.launchId || void 0,
11045
- outcome: "encode_failed",
11491
+ outcome,
11046
11492
  mode: "busy",
11493
+ failure_reason: sendResult.reason,
11494
+ failure_error: sendResult.error,
11047
11495
  pending_notification_count: count,
11048
11496
  retry_scheduled: retryScheduled,
11049
11497
  notification_timer_present: ap.notifications.hasTimer,
@@ -11094,8 +11542,11 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
11094
11542
  nativeStandingPrompt: Boolean(ap.driver.supportsNativeStandingPrompt)
11095
11543
  });
11096
11544
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.input.prepared", inputTraceAttrs);
11097
- const encoded = ap.driver.encodeStdinMessage(prompt, ap.sessionId, { mode });
11098
- if (!encoded) {
11545
+ const sendResult = requireImmediateRuntimeSendResult(
11546
+ ap.runtime.send({ mode, text: prompt, sessionId: ap.sessionId }),
11547
+ `${mode} inbox update`
11548
+ );
11549
+ if (!sendResult.ok) {
11099
11550
  if (mode === "idle") {
11100
11551
  this.commitApmIdleState(agentId, ap, true);
11101
11552
  }
@@ -11117,7 +11568,9 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
11117
11568
  ...this.messagesTraceAttrs(messages),
11118
11569
  ...inputTraceAttrs,
11119
11570
  ...projectionAttrs,
11120
- outcome: "encode_failed",
11571
+ outcome: runtimeSendFailureOutcome(sendResult),
11572
+ failure_reason: sendResult.reason,
11573
+ failure_error: sendResult.error,
11121
11574
  requeued_messages_count: 0,
11122
11575
  cursors_advanced: "none"
11123
11576
  }, "error");
@@ -11130,7 +11583,6 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
11130
11583
  if (this.containsOrdinaryInboxMessage(messages)) {
11131
11584
  ap.lastRuntimeError = null;
11132
11585
  }
11133
- ap.process.stdin?.write(encoded + "\n");
11134
11586
  this.recordDaemonTrace("daemon.agent.stdin_delivery", {
11135
11587
  agentId,
11136
11588
  launchId: ap.launchId || void 0,
@@ -11153,7 +11605,7 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
11153
11605
  return true;
11154
11606
  }
11155
11607
  /** Deliver a message to an agent via stdin, formatting it the same way as the MCP bridge */
11156
- deliverMessagesViaStdin(agentId, ap, messages, mode) {
11608
+ deliverMessagesViaStdin(agentId, ap, messages, mode, options = {}) {
11157
11609
  if (messages.length === 0) return true;
11158
11610
  const runtimeProfileMigrationMessages = messages.filter((message) => runtimeProfileNotificationFromMessage(message)?.kind === "migration");
11159
11611
  if (runtimeProfileMigrationMessages.length > 0) {
@@ -11191,8 +11643,10 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
11191
11643
  pending_notification_count: ap.notifications.pendingCount,
11192
11644
  busy_delivery_mode: ap.driver.busyDeliveryMode,
11193
11645
  supports_stdin_notification: ap.driver.supportsStdinNotification,
11646
+ transient_delivery: options.transient === true,
11194
11647
  ...this.messagesTraceAttrs(messages)
11195
11648
  };
11649
+ const traceSource = options.transient ? `stdin_${mode}_transient_delivery` : `stdin_${mode}_delivery`;
11196
11650
  const prompt = formatRuntimeProfileControlPrompt(messages) ?? (messages.length === 1 ? `New message received:
11197
11651
 
11198
11652
  ${formatIncomingMessage(messages[0], ap.driver)}
@@ -11205,16 +11659,21 @@ ${messages.map((message) => formatIncomingMessage(message, ap.driver)).join("\n"
11205
11659
  Respond as appropriate. Complete all your work before stopping.
11206
11660
  ${RESPONSE_TARGET_HINT}`);
11207
11661
  const inputTraceAttrs = buildRuntimeInputTraceAttrs({
11208
- source: `stdin_${mode}_delivery`,
11662
+ source: traceSource,
11209
11663
  prompt,
11210
11664
  messages,
11211
11665
  sessionIdPresent: Boolean(ap.sessionId),
11212
11666
  nativeStandingPrompt: Boolean(ap.driver.supportsNativeStandingPrompt)
11213
11667
  });
11214
11668
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.input.prepared", inputTraceAttrs);
11215
- const encoded = ap.driver.encodeStdinMessage(prompt, ap.sessionId, { mode });
11216
- if (!encoded) {
11217
- ap.inbox.unshift(...messages);
11669
+ const sendResult = requireImmediateRuntimeSendResult(
11670
+ ap.runtime.send({ mode, text: prompt, sessionId: ap.sessionId }),
11671
+ `${mode} message delivery`
11672
+ );
11673
+ if (!sendResult.ok) {
11674
+ if (!options.transient) {
11675
+ ap.inbox.unshift(...messages);
11676
+ }
11218
11677
  if (mode === "idle") {
11219
11678
  this.commitApmIdleState(agentId, ap, true);
11220
11679
  }
@@ -11224,12 +11683,16 @@ ${RESPONSE_TARGET_HINT}`);
11224
11683
  this.recordDaemonTrace("daemon.agent.stdin_delivery", {
11225
11684
  ...traceAttrs,
11226
11685
  ...inputTraceAttrs,
11227
- outcome: "encode_failed",
11228
- requeued_messages_count: messages.length
11686
+ outcome: runtimeSendFailureOutcome(sendResult),
11687
+ failure_reason: sendResult.reason,
11688
+ failure_error: sendResult.error,
11689
+ requeued_messages_count: options.transient ? 0 : messages.length
11229
11690
  }, "error");
11230
11691
  return false;
11231
11692
  }
11232
- this.consumeVisibleMessages(agentId, { messages, source: `stdin_${mode}_delivery` });
11693
+ if (!options.transient) {
11694
+ this.consumeVisibleMessages(agentId, { messages, source: traceSource });
11695
+ }
11233
11696
  const senders = [...new Set(messages.map((message) => `@${message.sender_name}`))].join(", ");
11234
11697
  logger.info(
11235
11698
  `[Agent ${agentId}] Delivering ${mode} ${messages.length === 1 ? "message" : `${messages.length} messages`} via stdin from ${senders}`
@@ -11237,7 +11700,6 @@ ${RESPONSE_TARGET_HINT}`);
11237
11700
  if (this.containsOrdinaryInboxMessage(messages)) {
11238
11701
  ap.lastRuntimeError = null;
11239
11702
  }
11240
- ap.process.stdin?.write(encoded + "\n");
11241
11703
  this.ackInjectedRuntimeProfileMessages(agentId, messages, ap.launchId);
11242
11704
  this.recordDaemonTrace("daemon.agent.stdin_delivery", {
11243
11705
  ...traceAttrs,
@@ -12205,7 +12667,7 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
12205
12667
  var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
12206
12668
  var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
12207
12669
  var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
12208
- var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
12670
+ var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
12209
12671
  var RunnerCredentialMintError2 = class extends Error {
12210
12672
  code;
12211
12673
  retryable;
@@ -12241,9 +12703,9 @@ function runnerCredentialErrorDetail2(error) {
12241
12703
  async function waitForRunnerCredentialRetry2() {
12242
12704
  await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
12243
12705
  }
12244
- function parseDaemonCliArgs(args) {
12706
+ function parseDaemonCliArgs(args, env = {}) {
12245
12707
  let serverUrl = "";
12246
- let apiKey = "";
12708
+ let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
12247
12709
  for (let i = 0; i < args.length; i++) {
12248
12710
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
12249
12711
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
@@ -12444,7 +12906,9 @@ var DaemonCore = class {
12444
12906
  serverUrl: options.serverUrl,
12445
12907
  defaultAgentEnvVarsProvider: options.defaultAgentEnvVarsProvider,
12446
12908
  slockCliPath: this.slockCliPath,
12447
- tracer: this.tracer
12909
+ tracer: this.tracer,
12910
+ daemonVersion: this.daemonVersion,
12911
+ computerVersion: this.computerVersion
12448
12912
  };
12449
12913
  this.agentManager = options.agentManagerFactory ? options.agentManagerFactory(this.chatBridgePath, (msg) => connection.send(msg), options.apiKey, agentManagerOptions) : new AgentProcessManager(this.chatBridgePath, (msg) => connection.send(msg), options.apiKey, agentManagerOptions);
12450
12914
  const connectionFactory = options.connectionFactory ?? ((connOptions) => new DaemonConnection(connOptions));
@@ -12695,7 +13159,8 @@ var DaemonCore = class {
12695
13159
  msg.wakeMessage,
12696
13160
  msg.unreadSummary,
12697
13161
  msg.resumePrompt,
12698
- msg.launchId
13162
+ msg.launchId,
13163
+ msg.wakeMessageTransient ?? false
12699
13164
  );
12700
13165
  }
12701
13166
  handleMessage(msg) {
@@ -12746,7 +13211,10 @@ var DaemonCore = class {
12746
13211
  logger.info(`[Agent ${msg.agentId}] Delivery received (seq=${msg.seq}, from=@${msg.message.sender_name}, target=${formatChannelTarget(msg)})`);
12747
13212
  try {
12748
13213
  span.addEvent("daemon.receive", { seq: msg.seq, deliveryId: msg.deliveryId });
12749
- const acceptedOrPromise = this.agentManager.deliverMessage(msg.agentId, msg.message, { deliveryId: msg.deliveryId });
13214
+ const acceptedOrPromise = this.agentManager.deliverMessage(msg.agentId, msg.message, {
13215
+ deliveryId: msg.deliveryId,
13216
+ transient: msg.transient ?? false
13217
+ });
12750
13218
  Promise.resolve(acceptedOrPromise).then((accepted) => {
12751
13219
  span.addEvent("daemon.deliver_to_agent_manager", { accepted });
12752
13220
  if (!accepted) {
@@ -13010,6 +13478,8 @@ var DaemonCore = class {
13010
13478
  };
13011
13479
 
13012
13480
  export {
13481
+ DAEMON_API_KEY_ENV,
13482
+ scrubDaemonAuthEnv,
13013
13483
  resolveWorkspaceDirectoryPath,
13014
13484
  scanWorkspaceDirectories,
13015
13485
  deleteWorkspaceDirectory,