@slock-ai/daemon 0.57.1-play.20260607140323 → 0.57.2

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.
@@ -351,40 +351,127 @@ var NoopActiveSpan = class {
351
351
  }
352
352
  };
353
353
  var noopTracer = new NoopTracer();
354
- function createScopedTracer(tracer, scopeAttrs) {
354
+ function projectTraceScopeAttrs(scope) {
355
+ const attrs = {};
356
+ addOptionalStringAttr(attrs, "daemon_version", scope.resource?.daemonVersion);
357
+ addPresenceAttr(attrs, "daemon_version_present", scope.resource?.daemonVersion);
358
+ addOptionalStringAttr(attrs, "computer_version", scope.resource?.computerVersion);
359
+ addPresenceAttr(attrs, "computer_version_present", scope.resource?.computerVersion);
360
+ addOptionalStringAttr(attrs, "deployment_environment", scope.resource?.deploymentEnvironment);
361
+ addOptionalStringAttr(attrs, "service_revision", scope.resource?.serviceRevision);
362
+ addOptionalStringAttr(attrs, "request_id", scope.request?.requestId);
363
+ addPresenceAttr(attrs, "request_id_present", scope.request?.requestId);
364
+ addOptionalStringAttr(attrs, "route_pattern", scope.request?.routePattern);
365
+ addOptionalStringAttr(attrs, "method", scope.request?.method);
366
+ addOptionalStringAttr(attrs, "caller_kind", scope.request?.callerKind);
367
+ if (typeof scope.request?.userIdPresent === "boolean") {
368
+ attrs.user_id_present = scope.request.userIdPresent;
369
+ }
370
+ addOptionalStringAttr(attrs, "server_id", scope.actor?.serverId);
371
+ addPresenceAttr(attrs, "server_id_present", scope.actor?.serverId, scope.actor?.serverIdPresent);
372
+ addOptionalStringAttr(attrs, "machine_id", scope.actor?.machineId);
373
+ addPresenceAttr(attrs, "machine_id_present", scope.actor?.machineId, scope.actor?.machineIdPresent);
374
+ addOptionalStringAttr(attrs, "agent_id", scope.actor?.agentId);
375
+ addPresenceAttr(attrs, "agent_id_present", scope.actor?.agentId, scope.actor?.agentIdPresent);
376
+ addOptionalStringAttr(attrs, "launch_id", scope.actor?.launchId);
377
+ addPresenceAttr(attrs, "launch_id_present", scope.actor?.launchId, scope.actor?.launchIdPresent);
378
+ addOptionalStringAttr(attrs, "session_id", scope.actor?.sessionId);
379
+ addPresenceAttr(attrs, "session_id_present", scope.actor?.sessionId, scope.actor?.sessionIdPresent);
380
+ return attrs;
381
+ }
382
+ function createTraceScopeTracer(tracer, scope, options = {}) {
383
+ const scopedTracer = createScopedTracer(tracer, projectTraceScopeAttrs(scope), {
384
+ attrPrecedence: options.scopeAttrPrecedence
385
+ });
386
+ return options.spanAttrContracts ? createSpanAttrContractTracer(scopedTracer, options.spanAttrContracts) : scopedTracer;
387
+ }
388
+ function createScopedTracer(tracer, scopeAttrs, options = {}) {
355
389
  if (!Object.keys(scopeAttrs).length) return tracer;
356
- return new ScopedTracer(tracer, scopeAttrs);
390
+ return new ScopedTracer(tracer, scopeAttrs, options.attrPrecedence ?? "scope");
357
391
  }
358
392
  var ScopedTracer = class {
359
- constructor(tracer, scopeAttrs) {
393
+ constructor(tracer, scopeAttrs, attrPrecedence) {
360
394
  this.tracer = tracer;
361
395
  this.scopeAttrs = scopeAttrs;
396
+ this.attrPrecedence = attrPrecedence;
362
397
  }
363
398
  startSpan(name, options) {
364
399
  const span = this.tracer.startSpan(name, {
365
400
  ...options,
366
- attrs: mergeAttrs(options.attrs, this.scopeAttrs)
401
+ attrs: this.mergeScopeAttrs(options.attrs)
367
402
  });
368
- return new ScopedActiveSpan(span, this.scopeAttrs);
403
+ return new ScopedActiveSpan(span, this.scopeAttrs, this.attrPrecedence);
404
+ }
405
+ mergeScopeAttrs(attrs) {
406
+ return this.attrPrecedence === "caller" ? mergeAttrs(this.scopeAttrs, attrs) : mergeAttrs(attrs, this.scopeAttrs);
369
407
  }
370
408
  };
371
409
  var ScopedActiveSpan = class {
372
- constructor(span, scopeAttrs) {
410
+ constructor(span, scopeAttrs, attrPrecedence) {
373
411
  this.span = span;
374
412
  this.scopeAttrs = scopeAttrs;
413
+ this.attrPrecedence = attrPrecedence;
375
414
  this.context = span.context;
376
415
  }
377
416
  context;
378
417
  addEvent(name, attrs) {
379
- this.span.addEvent(name, mergeAttrs(attrs, this.scopeAttrs));
418
+ this.span.addEvent(name, this.mergeScopeAttrs(attrs));
380
419
  }
381
420
  end(status, options) {
382
421
  this.span.end(status, options ? {
383
422
  ...options,
384
- attrs: mergeAttrs(options.attrs, this.scopeAttrs)
423
+ attrs: this.mergeScopeAttrs(options.attrs)
385
424
  } : void 0);
386
425
  }
426
+ mergeScopeAttrs(attrs) {
427
+ return this.attrPrecedence === "caller" ? mergeAttrs(this.scopeAttrs, attrs) : mergeAttrs(attrs, this.scopeAttrs);
428
+ }
429
+ };
430
+ function createSpanAttrContractTracer(tracer, contracts) {
431
+ if (!Object.keys(contracts).length) return tracer;
432
+ return new SpanAttrContractTracer(tracer, contracts);
433
+ }
434
+ var SpanAttrContractTracer = class {
435
+ constructor(tracer, contracts) {
436
+ this.tracer = tracer;
437
+ this.contracts = contracts;
438
+ }
439
+ startSpan(name, options) {
440
+ const contract = this.contracts[name];
441
+ if (!contract) return this.tracer.startSpan(name, options);
442
+ const span = this.tracer.startSpan(name, {
443
+ ...options,
444
+ attrs: filterTraceAttrs(options.attrs, contract.spanAttrs)
445
+ });
446
+ return new SpanAttrContractActiveSpan(span, contract);
447
+ }
387
448
  };
449
+ var SpanAttrContractActiveSpan = class {
450
+ constructor(span, contract) {
451
+ this.span = span;
452
+ this.contract = contract;
453
+ this.context = span.context;
454
+ }
455
+ context;
456
+ addEvent(name, attrs) {
457
+ this.span.addEvent(name, filterTraceAttrs(attrs, this.contract.eventAttrs?.[name]));
458
+ }
459
+ end(status, options) {
460
+ this.span.end(status, options ? {
461
+ ...options,
462
+ attrs: filterTraceAttrs(options.attrs, this.contract.endAttrs)
463
+ } : void 0);
464
+ }
465
+ };
466
+ function filterTraceAttrs(attrs, allowedKeys) {
467
+ if (!attrs || !allowedKeys) return attrs;
468
+ const allowed = new Set(allowedKeys);
469
+ const filtered = {};
470
+ for (const [key, value] of Object.entries(attrs)) {
471
+ if (allowed.has(key)) filtered[key] = value;
472
+ }
473
+ return Object.keys(filtered).length ? filtered : void 0;
474
+ }
388
475
  var BasicTracer = class {
389
476
  sink;
390
477
  clock;
@@ -482,6 +569,14 @@ function mergeAttrs(base, extra) {
482
569
  if (!extra) return base;
483
570
  return { ...base, ...extra };
484
571
  }
572
+ function addOptionalStringAttr(attrs, key, value) {
573
+ const normalized = value?.trim();
574
+ if (!normalized) return;
575
+ attrs[key] = normalized;
576
+ }
577
+ function addPresenceAttr(attrs, key, value, explicitPresent) {
578
+ attrs[key] = typeof explicitPresent === "boolean" ? explicitPresent : Boolean(value?.trim());
579
+ }
485
580
  function generateTraceId() {
486
581
  return randomNonZeroHex(TRACE_ID_HEX_LENGTH);
487
582
  }
@@ -1046,6 +1141,9 @@ var SERVER_CAPABILITY_MATRIX = {
1046
1141
 
1047
1142
  // ../shared/src/index.ts
1048
1143
  var RUNTIME_CONFIG_VERSION = 1;
1144
+ var EXTERNAL_AGENT_RUNTIME_ID = "external";
1145
+ var EXTERNAL_AGENT_RUNTIME_MODEL = "external";
1146
+ var EXTERNAL_AGENT_RUNTIME_DISPLAY_NAME = "External agent";
1049
1147
  var RUNTIMES = [
1050
1148
  { id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
1051
1149
  { id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
@@ -1058,6 +1156,9 @@ var RUNTIMES = [
1058
1156
  { id: "pi", displayName: "Pi", binary: "pi", supported: true }
1059
1157
  ];
1060
1158
  var RUNTIME_MODELS = {
1159
+ [EXTERNAL_AGENT_RUNTIME_ID]: [
1160
+ { id: EXTERNAL_AGENT_RUNTIME_MODEL, label: EXTERNAL_AGENT_RUNTIME_DISPLAY_NAME }
1161
+ ],
1061
1162
  claude: [
1062
1163
  { id: "opus", label: "Claude Opus" },
1063
1164
  { id: "claude-opus-4-8", label: "Claude Opus 4.8" },
@@ -1980,19 +2081,6 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
1980
2081
  return candidates.filter((candidate) => existsSync(candidate.path));
1981
2082
  }
1982
2083
 
1983
- // src/authEnv.ts
1984
- var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
1985
- var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
1986
- function scrubDaemonAuthEnv(env) {
1987
- delete env[DAEMON_API_KEY_ENV];
1988
- return env;
1989
- }
1990
- function scrubDaemonChildEnv(env) {
1991
- delete env[DAEMON_API_KEY_ENV];
1992
- delete env[SLOCK_AGENT_TOKEN_ENV];
1993
- return env;
1994
- }
1995
-
1996
2084
  // src/agentCredentialProxy.ts
1997
2085
  import { randomBytes } from "crypto";
1998
2086
  import http from "http";
@@ -3479,9 +3567,7 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
3479
3567
  var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
3480
3568
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
3481
3569
  var RAW_CREDENTIAL_ENV_DENYLIST = [
3482
- "SLOCK_AGENT_TOKEN",
3483
- "SLOCK_AGENT_CREDENTIAL_KEY",
3484
- "SLOCK_AGENT_CREDENTIAL_KEY_FILE"
3570
+ "SLOCK_AGENT_CREDENTIAL_KEY"
3485
3571
  ];
3486
3572
  var cachedOpencliBinPath;
3487
3573
  function resolveOpencliBinPath() {
@@ -3696,7 +3782,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
3696
3782
  ...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
3697
3783
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
3698
3784
  };
3699
- scrubDaemonChildEnv(spawnEnv);
3785
+ delete spawnEnv.SLOCK_AGENT_TOKEN;
3700
3786
  for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
3701
3787
  delete spawnEnv[key];
3702
3788
  }
@@ -4125,7 +4211,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
4125
4211
  }
4126
4212
  function resolveCommandOnPath(command, deps = {}) {
4127
4213
  const platform = deps.platform ?? process.platform;
4128
- const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
4214
+ const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
4129
4215
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
4130
4216
  const existsSyncFn = deps.existsSyncFn ?? existsSync2;
4131
4217
  if (platform === "win32") {
@@ -4151,7 +4237,7 @@ function firstExistingPath(candidates, deps = {}) {
4151
4237
  return null;
4152
4238
  }
4153
4239
  function readCommandVersion(command, args = [], deps = {}) {
4154
- const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
4240
+ const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
4155
4241
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
4156
4242
  try {
4157
4243
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -5505,11 +5591,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
5505
5591
  return parseCursorModelsOutput(String(result.stdout || ""));
5506
5592
  }
5507
5593
  function buildCursorModelProbeEnv(deps = {}) {
5508
- return scrubDaemonChildEnv(withWindowsUserEnvironment({
5594
+ return withWindowsUserEnvironment({
5509
5595
  ...deps.env ?? process.env,
5510
5596
  FORCE_COLOR: "0",
5511
5597
  NO_COLOR: "1"
5512
- }, deps));
5598
+ }, deps);
5513
5599
  }
5514
5600
  function runCursorModelsCommand() {
5515
5601
  return spawnSync("cursor-agent", ["models"], {
@@ -5565,7 +5651,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
5565
5651
  }
5566
5652
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
5567
5653
  const existsSyncFn = deps.existsSyncFn ?? existsSync4;
5568
- const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
5654
+ const env = deps.env ?? process.env;
5569
5655
  const winPath = path6.win32;
5570
5656
  let geminiEntry = null;
5571
5657
  try {
@@ -5705,15 +5791,12 @@ var GeminiDriver = class {
5705
5791
  // src/drivers/kimi.ts
5706
5792
  import { randomUUID as randomUUID2 } from "crypto";
5707
5793
  import { spawn as spawn7 } from "child_process";
5708
- import { chmodSync, existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
5794
+ import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
5709
5795
  import os3 from "os";
5710
5796
  import path7 from "path";
5711
5797
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
5712
5798
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
5713
5799
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
5714
- var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
5715
- var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
5716
- var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
5717
5800
  function parseToolArguments(raw) {
5718
5801
  if (typeof raw !== "string") return raw;
5719
5802
  try {
@@ -5722,73 +5805,6 @@ function parseToolArguments(raw) {
5722
5805
  return raw;
5723
5806
  }
5724
5807
  }
5725
- function readKimiConfigSource(home = os3.homedir(), env = process.env) {
5726
- const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
5727
- if (inlineConfig && inlineConfig.trim()) {
5728
- return {
5729
- raw: inlineConfig,
5730
- explicitPath: null,
5731
- sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
5732
- };
5733
- }
5734
- const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
5735
- const configPath = explicitPath && explicitPath.trim() ? explicitPath : path7.join(home, ".kimi", "config.toml");
5736
- try {
5737
- return {
5738
- raw: readFileSync3(configPath, "utf8"),
5739
- explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
5740
- sourcePath: configPath
5741
- };
5742
- } catch {
5743
- return {
5744
- raw: null,
5745
- explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
5746
- sourcePath: configPath
5747
- };
5748
- }
5749
- }
5750
- function buildKimiSpawnEnv(env = process.env) {
5751
- const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
5752
- delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
5753
- delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
5754
- return scrubDaemonChildEnv(spawnEnv);
5755
- }
5756
- function buildKimiEffectiveEnv(ctx, overrideEnv) {
5757
- return {
5758
- ...process.env,
5759
- ...ctx.config.envVars || {},
5760
- ...overrideEnv || {}
5761
- };
5762
- }
5763
- function buildKimiLaunchOptions(ctx, opts = {}) {
5764
- const env = buildKimiEffectiveEnv(ctx, opts.env);
5765
- const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
5766
- const args = [];
5767
- let configFilePath = null;
5768
- let configContent = null;
5769
- if (source.explicitPath) {
5770
- configFilePath = source.explicitPath;
5771
- } else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
5772
- configFilePath = path7.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
5773
- configContent = source.raw;
5774
- if (opts.writeGeneratedConfig !== false) {
5775
- writeFileSync3(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
5776
- chmodSync(configFilePath, 384);
5777
- }
5778
- }
5779
- if (configFilePath) {
5780
- args.push("--config-file", configFilePath);
5781
- }
5782
- if (ctx.config.model && ctx.config.model !== "default") {
5783
- args.push("--model", ctx.config.model);
5784
- }
5785
- return {
5786
- args,
5787
- env: buildKimiSpawnEnv(env),
5788
- configFilePath,
5789
- configContent
5790
- };
5791
- }
5792
5808
  function resolveKimiSpawn(commandArgs, deps = {}) {
5793
5809
  return {
5794
5810
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -5812,25 +5828,7 @@ var KimiDriver = class {
5812
5828
  };
5813
5829
  model = {
5814
5830
  detectedModelsVerifiedAs: "launchable",
5815
- toLaunchSpec: (modelId, ctx, opts) => {
5816
- if (!ctx) return { args: ["--model", modelId] };
5817
- const launchCtx = {
5818
- ...ctx,
5819
- config: {
5820
- ...ctx.config,
5821
- model: modelId
5822
- }
5823
- };
5824
- const launch = buildKimiLaunchOptions(launchCtx, {
5825
- home: opts?.home,
5826
- writeGeneratedConfig: false
5827
- });
5828
- return {
5829
- args: launch.args,
5830
- env: launch.env,
5831
- configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
5832
- };
5833
- }
5831
+ toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
5834
5832
  };
5835
5833
  supportsStdinNotification = true;
5836
5834
  mcpToolPrefix = "";
@@ -5856,23 +5854,21 @@ var KimiDriver = class {
5856
5854
  ` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
5857
5855
  ""
5858
5856
  ].join("\n"), "utf8");
5859
- const launch = buildKimiLaunchOptions(ctx);
5860
5857
  const args = [
5861
5858
  "--wire",
5862
5859
  "--yolo",
5863
5860
  "--agent-file",
5864
5861
  agentFilePath,
5865
5862
  "--session",
5866
- this.sessionId,
5867
- ...launch.args
5863
+ this.sessionId
5868
5864
  ];
5869
5865
  const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
5870
5866
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
5871
5867
  args.push("--model", launchRuntimeFields.model);
5872
5868
  }
5873
5869
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
5874
- const spawnTarget = resolveKimiSpawn(args);
5875
- const proc = spawn7(spawnTarget.command, spawnTarget.args, {
5870
+ const launch = resolveKimiSpawn(args);
5871
+ const proc = spawn7(launch.command, launch.args, {
5876
5872
  cwd: ctx.workingDirectory,
5877
5873
  stdio: ["pipe", "pipe", "pipe"],
5878
5874
  env: spawnEnv,
@@ -5880,7 +5876,7 @@ var KimiDriver = class {
5880
5876
  // and has an 8191-character command-line limit. Kimi's official
5881
5877
  // installer/uv entrypoint is an executable, so launch it directly and
5882
5878
  // keep prompts on stdin / files instead of routing through cmd.exe.
5883
- shell: spawnTarget.shell
5879
+ shell: launch.shell
5884
5880
  });
5885
5881
  proc.stdin?.write(JSON.stringify({
5886
5882
  jsonrpc: "2.0",
@@ -5994,9 +5990,14 @@ var KimiDriver = class {
5994
5990
  return detectKimiModels();
5995
5991
  }
5996
5992
  };
5997
- function detectKimiModels(home = os3.homedir(), opts = {}) {
5998
- const raw = readKimiConfigSource(home, opts.env).raw;
5999
- if (raw === null) return null;
5993
+ function detectKimiModels(home = os3.homedir()) {
5994
+ const configPath = path7.join(home, ".kimi", "config.toml");
5995
+ let raw;
5996
+ try {
5997
+ raw = readFileSync3(configPath, "utf8");
5998
+ } catch {
5999
+ return null;
6000
+ }
6000
6001
  const models = [];
6001
6002
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
6002
6003
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -6236,7 +6237,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
6236
6237
  const platform = deps.platform ?? process.platform;
6237
6238
  const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
6238
6239
  const result = spawnSyncFn("opencode", ["models"], {
6239
- env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
6240
+ env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
6240
6241
  encoding: "utf8",
6241
6242
  timeout: 5e3,
6242
6243
  shell: platform === "win32"
@@ -7481,16 +7482,19 @@ var RuntimeProgressState = class {
7481
7482
  };
7482
7483
 
7483
7484
  // src/runtimeNotificationState.ts
7485
+ function inboxNoticeMessageIdentity(message) {
7486
+ const seq = typeof message.seq === "number" && Number.isFinite(message.seq) && message.seq > 0 ? Math.floor(message.seq) : null;
7487
+ if (seq !== null) {
7488
+ return `s:${seq}`;
7489
+ }
7490
+ const id = typeof message.message_id === "string" && message.message_id.length > 0 ? message.message_id : typeof message.id === "string" && message.id.length > 0 ? message.id : "";
7491
+ return id.length > 0 ? `m:${id}` : "";
7492
+ }
7484
7493
  function computeInboxNoticeFingerprint(messages) {
7485
7494
  const keys = [];
7486
7495
  for (const m of messages) {
7487
- const seq = typeof m.seq === "number" && Number.isFinite(m.seq) && m.seq > 0 ? Math.floor(m.seq) : null;
7488
- if (seq !== null) {
7489
- keys.push(`s:${seq}`);
7490
- continue;
7491
- }
7492
- const id = typeof m.message_id === "string" && m.message_id.length > 0 ? m.message_id : typeof m.id === "string" && m.id.length > 0 ? m.id : "";
7493
- if (id.length > 0) keys.push(`m:${id}`);
7496
+ const key = inboxNoticeMessageIdentity(m);
7497
+ if (key.length > 0) keys.push(key);
7494
7498
  }
7495
7499
  if (keys.length === 0) return "";
7496
7500
  keys.sort();
@@ -7501,6 +7505,10 @@ var RuntimeNotificationState = class {
7501
7505
  pendingCountValue = 0;
7502
7506
  lastNoticeFingerprint = null;
7503
7507
  lastNoticeSessionId = null;
7508
+ lastEncodeFailedFingerprint = null;
7509
+ lastEncodeFailedSessionId = null;
7510
+ contributedSessionId = null;
7511
+ contributedIdentities = /* @__PURE__ */ new Set();
7504
7512
  get pendingCount() {
7505
7513
  return this.pendingCountValue;
7506
7514
  }
@@ -7543,13 +7551,63 @@ var RuntimeNotificationState = class {
7543
7551
  return this.lastNoticeFingerprint === fingerprint && this.lastNoticeSessionId === sessionId;
7544
7552
  }
7545
7553
  /** Register a fingerprint as written — call ONLY after a successful stdin write. */
7546
- recordNoticeWritten(fingerprint, sessionId) {
7554
+ recordNoticeWritten(fingerprint, sessionId, messages = []) {
7547
7555
  this.lastNoticeFingerprint = fingerprint;
7548
7556
  this.lastNoticeSessionId = sessionId;
7557
+ this.lastEncodeFailedFingerprint = null;
7558
+ this.lastEncodeFailedSessionId = null;
7559
+ this.ensureContributionSession(sessionId);
7560
+ for (const message of messages) {
7561
+ const identity = inboxNoticeMessageIdentity(message);
7562
+ if (identity.length > 0) {
7563
+ this.contributedIdentities.add(identity);
7564
+ }
7565
+ }
7566
+ }
7567
+ hasContributedMessage(message, sessionId) {
7568
+ if (this.contributedSessionId !== sessionId) return false;
7569
+ const identity = inboxNoticeMessageIdentity(message);
7570
+ return identity.length > 0 && this.contributedIdentities.has(identity);
7571
+ }
7572
+ filterUncontributedMessages(messages, sessionId) {
7573
+ if (this.contributedSessionId !== sessionId || this.contributedIdentities.size === 0) {
7574
+ return [...messages];
7575
+ }
7576
+ return messages.filter((message) => {
7577
+ const identity = inboxNoticeMessageIdentity(message);
7578
+ return identity.length === 0 || !this.contributedIdentities.has(identity);
7579
+ });
7580
+ }
7581
+ pruneContributedToPending(messages, sessionId) {
7582
+ this.ensureContributionSession(sessionId);
7583
+ if (this.contributedIdentities.size === 0) return;
7584
+ const pending = /* @__PURE__ */ new Set();
7585
+ for (const message of messages) {
7586
+ const identity = inboxNoticeMessageIdentity(message);
7587
+ if (identity.length > 0) pending.add(identity);
7588
+ }
7589
+ for (const identity of this.contributedIdentities) {
7590
+ if (!pending.has(identity)) {
7591
+ this.contributedIdentities.delete(identity);
7592
+ }
7593
+ }
7594
+ }
7595
+ isDuplicateEncodeFailedNotice(fingerprint, sessionId) {
7596
+ if (fingerprint.length === 0) return false;
7597
+ return this.lastEncodeFailedFingerprint === fingerprint && this.lastEncodeFailedSessionId === sessionId;
7598
+ }
7599
+ recordNoticeEncodeFailed(fingerprint, sessionId) {
7600
+ if (fingerprint.length === 0) return;
7601
+ this.lastEncodeFailedFingerprint = fingerprint;
7602
+ this.lastEncodeFailedSessionId = sessionId;
7549
7603
  }
7550
7604
  clearNoticeFingerprint() {
7551
7605
  this.lastNoticeFingerprint = null;
7552
7606
  this.lastNoticeSessionId = null;
7607
+ this.lastEncodeFailedFingerprint = null;
7608
+ this.lastEncodeFailedSessionId = null;
7609
+ this.contributedSessionId = null;
7610
+ this.contributedIdentities.clear();
7553
7611
  }
7554
7612
  schedule(callback, delayMs) {
7555
7613
  if (this.timerValue) return false;
@@ -7562,6 +7620,11 @@ var RuntimeNotificationState = class {
7562
7620
  this.clearTimer();
7563
7621
  return count;
7564
7622
  }
7623
+ ensureContributionSession(sessionId) {
7624
+ if (this.contributedSessionId === sessionId) return;
7625
+ this.contributedSessionId = sessionId;
7626
+ this.contributedIdentities.clear();
7627
+ }
7565
7628
  };
7566
7629
 
7567
7630
  // src/agentProcessManager.ts
@@ -8705,6 +8768,7 @@ var RUNTIME_TELEMETRY_RESERVED_ATTR_KEYS = /* @__PURE__ */ new Set([
8705
8768
  "sessionId",
8706
8769
  "turnId",
8707
8770
  "runtimeResultId",
8771
+ "runtimeResultIdSource",
8708
8772
  "daemonVersion",
8709
8773
  "daemon_version",
8710
8774
  "daemon_version_present",
@@ -8880,13 +8944,33 @@ var AgentProcessManager = class _AgentProcessManager {
8880
8944
  this.sendStdinNotification(agentId);
8881
8945
  }, delayMs);
8882
8946
  }
8947
+ flushPendingDirectStdinNotificationOnRuntimeProgress(agentId, ap, source) {
8948
+ if (ap.notifications.pendingCount === 0) return false;
8949
+ if (ap.isIdle) return false;
8950
+ if (!ap.sessionId) return false;
8951
+ if (!ap.driver.supportsStdinNotification) return false;
8952
+ if (ap.runtime.descriptor.busyDelivery !== "direct") return false;
8953
+ if (ap.gatedSteering.compacting) return false;
8954
+ this.recordDaemonTrace("daemon.agent.stdin_notification.retry_signal", {
8955
+ agentId,
8956
+ runtime: ap.config.runtime,
8957
+ model: ap.config.model,
8958
+ launchId: ap.launchId || void 0,
8959
+ source,
8960
+ mode: "busy",
8961
+ pending_notification_count: ap.notifications.pendingCount,
8962
+ inbox_count: ap.inbox.length,
8963
+ session_id_present: true
8964
+ });
8965
+ return this.sendStdinNotification(agentId, { forceUnsupportedRetry: true });
8966
+ }
8883
8967
  clearRuntimeErrorDeliveryBackoff(ap) {
8884
8968
  if (ap.runtimeErrorDeliveryBackoff.timer) {
8885
8969
  clearTimeout(ap.runtimeErrorDeliveryBackoff.timer);
8886
8970
  }
8887
8971
  ap.runtimeErrorDeliveryBackoff = createRuntimeErrorDeliveryBackoffState();
8888
8972
  }
8889
- clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, eventKind) {
8973
+ clearRuntimeErrorDeliveryBackoffWithTrace(agentId, ap, resetSource) {
8890
8974
  if (ap.runtimeErrorDeliveryBackoff.attempts === 0 && ap.runtimeErrorDeliveryBackoff.untilMs === 0) return;
8891
8975
  const attempts = ap.runtimeErrorDeliveryBackoff.attempts;
8892
8976
  const reason = ap.runtimeErrorDeliveryBackoff.reason;
@@ -8898,9 +8982,12 @@ var AgentProcessManager = class _AgentProcessManager {
8898
8982
  launchId: ap.launchId || void 0,
8899
8983
  reason: reason || void 0,
8900
8984
  attempts,
8901
- reset_source: eventKind
8985
+ reset_source: resetSource
8902
8986
  });
8903
8987
  }
8988
+ clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, eventKind) {
8989
+ this.clearRuntimeErrorDeliveryBackoffWithTrace(agentId, ap, eventKind);
8990
+ }
8904
8991
  runtimeErrorDeliveryBackoffRemainingMs(ap) {
8905
8992
  return Math.max(0, ap.runtimeErrorDeliveryBackoff.untilMs - Date.now());
8906
8993
  }
@@ -8928,8 +9015,10 @@ var AgentProcessManager = class _AgentProcessManager {
8928
9015
  return "provider_connection_error";
8929
9016
  case "ProviderStreamError":
8930
9017
  return "provider_stream_error";
8931
- default:
9018
+ case "TimeoutError":
8932
9019
  return null;
9020
+ default:
9021
+ return "runtime_error";
8933
9022
  }
8934
9023
  }
8935
9024
  noteRuntimeErrorDeliveryBackoff(agentId, ap, message, terminalFailure, stickyTerminalFailure, reasonOverride) {
@@ -9000,9 +9089,24 @@ var AgentProcessManager = class _AgentProcessManager {
9000
9089
  if (ap.inbox.length === 0) return false;
9001
9090
  const reason = ap.runtimeErrorDeliveryBackoff.reason || "runtime_error_backoff";
9002
9091
  if (ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
9003
- const messages = [...ap.inbox];
9092
+ ap.notifications.pruneContributedToPending(ap.inbox, ap.sessionId);
9093
+ const messages = ap.notifications.filterUncontributedMessages(ap.inbox, ap.sessionId);
9004
9094
  ap.notifications.clearPending();
9005
9095
  ap.notifications.clearTimer();
9096
+ if (messages.length === 0) {
9097
+ this.recordDaemonTrace("daemon.agent.runtime_error_delivery_backoff.flush", {
9098
+ agentId,
9099
+ runtime: ap.config.runtime,
9100
+ model: ap.config.model,
9101
+ launchId: ap.launchId || void 0,
9102
+ reason,
9103
+ mode: "idle",
9104
+ outcome: "suppressed_already_contributed",
9105
+ inbox_count: ap.inbox.length,
9106
+ messages_count: 0
9107
+ });
9108
+ return false;
9109
+ }
9006
9110
  this.commitApmIdleState(agentId, ap, false);
9007
9111
  this.startRuntimeTrace(agentId, ap, "runtime-error-backoff-idle-delivery", messages);
9008
9112
  this.broadcastActivity(agentId, "working", "Message received");
@@ -9683,6 +9787,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9683
9787
  runtimeProgress: new RuntimeProgressState(Date.now()),
9684
9788
  runtimeTraceSpan: null,
9685
9789
  runtimeTraceCounters: createRuntimeTraceCounters(),
9790
+ runtimeTelemetryResultSeq: 0,
9686
9791
  lastActivity: "",
9687
9792
  lastActivityDetail: "",
9688
9793
  recentStdout: [],
@@ -10235,6 +10340,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10235
10340
  this.agents.delete(agentId);
10236
10341
  if (!silent) {
10237
10342
  this.activityClientSeqByAgent.delete(agentId);
10343
+ this.agentVisibleBoundaries.delete(agentId);
10344
+ this.agentVisibleMessageIds.delete(agentId);
10238
10345
  }
10239
10346
  this.runtimeExitTraceAttrs.set(ap.runtime, {
10240
10347
  stop_source: silent ? "daemon_internal" : "explicit_request",
@@ -10458,6 +10565,25 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10458
10565
  }));
10459
10566
  return true;
10460
10567
  }
10568
+ ap.notifications.pruneContributedToPending(ap.inbox, ap.sessionId);
10569
+ const noticeFingerprint = computeInboxNoticeFingerprint([message]);
10570
+ const messageAlreadyPending = noticeFingerprint.length > 0 && ap.inbox.some((pending) => computeInboxNoticeFingerprint([pending]) === noticeFingerprint);
10571
+ const messageAlreadyContributed = ap.notifications.hasContributedMessage(message, ap.sessionId);
10572
+ if (messageAlreadyPending && (messageAlreadyContributed || ap.notifications.isDuplicateNotice(noticeFingerprint, ap.sessionId))) {
10573
+ this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
10574
+ outcome: "suppressed_duplicate_stdin_idle_delivery",
10575
+ accepted: true,
10576
+ process_present: true,
10577
+ runtime: ap.config.runtime,
10578
+ session_id_present: true,
10579
+ launchId: ap.launchId || void 0,
10580
+ is_idle: ap.isIdle,
10581
+ inbox_count: ap.inbox.length,
10582
+ pending_notification_count: ap.notifications.pendingCount
10583
+ }));
10584
+ logger.info(`[Agent ${agentId}] Suppressing duplicate idle stdin inbox update (unread-set unchanged since last write); pending=${ap.inbox.length}`);
10585
+ return true;
10586
+ }
10461
10587
  ap.inbox.push(message);
10462
10588
  const nextMessages = [...ap.inbox];
10463
10589
  this.commitApmIdleState(agentId, ap, false);
@@ -11326,6 +11452,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11326
11452
  if (row.flags.includes("task")) taskTargetCount += 1;
11327
11453
  }
11328
11454
  return {
11455
+ target_count: rows.length,
11456
+ changed_target_count: rows.length,
11329
11457
  inbox_target_count: rows.length,
11330
11458
  pending_message_count: pendingMessageCount,
11331
11459
  max_pending_per_target: maxPendingCount,
@@ -11499,7 +11627,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11499
11627
  return false;
11500
11628
  }
11501
11629
  const messages = [...ap.inbox];
11502
- ap.notifications.clear();
11630
+ ap.notifications.pruneContributedToPending(ap.inbox, ap.sessionId);
11631
+ ap.notifications.clearPending();
11632
+ ap.notifications.clearTimer();
11503
11633
  if (messages.length === 0) {
11504
11634
  this.recordApmGatedSteeringEffectTrace(agentId, ap, effect, {
11505
11635
  outcome: "empty",
@@ -11515,12 +11645,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11515
11645
  messageCount: messages.length
11516
11646
  });
11517
11647
  }
11518
- this.broadcastActivity(agentId, "working", "Message received");
11519
11648
  const runtimeProfileMessages = messages.filter((message) => runtimeProfileNotificationFromMessage(message));
11520
- const ordinaryMessages = messages.filter((message) => !runtimeProfileNotificationFromMessage(message));
11649
+ const ordinaryMessageCandidates = messages.filter((message) => !runtimeProfileNotificationFromMessage(message));
11650
+ const ordinaryMessages = ap.notifications.filterUncontributedMessages(
11651
+ ordinaryMessageCandidates,
11652
+ ap.sessionId
11653
+ );
11654
+ if (runtimeProfileMessages.length === 0 && ordinaryMessages.length === 0) {
11655
+ this.recordApmGatedSteeringEffectTrace(agentId, ap, effect, {
11656
+ outcome: "suppressed_already_contributed",
11657
+ delivered_messages_count: 0
11658
+ });
11659
+ return false;
11660
+ }
11661
+ this.broadcastActivity(agentId, "working", "Message received");
11521
11662
  let accepted = true;
11522
11663
  if (runtimeProfileMessages.length > 0) {
11523
- ap.inbox.splice(0, ap.inbox.length, ...ordinaryMessages);
11664
+ ap.inbox.splice(0, ap.inbox.length, ...ordinaryMessageCandidates);
11524
11665
  accepted = this.deliverMessagesViaStdin(agentId, ap, runtimeProfileMessages, effect.stdinMode);
11525
11666
  }
11526
11667
  if (ordinaryMessages.length > 0) {
@@ -11534,7 +11675,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11534
11675
  }
11535
11676
  this.recordApmGatedSteeringEffectTrace(agentId, ap, effect, {
11536
11677
  outcome: accepted ? "written" : "not_written",
11537
- delivered_messages_count: messages.length
11678
+ delivered_messages_count: runtimeProfileMessages.length + ordinaryMessages.length
11538
11679
  });
11539
11680
  return accepted;
11540
11681
  }
@@ -11766,6 +11907,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11766
11907
  this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
11767
11908
  const reduction = reduceApmGatedAssistantContinuation(ap.gatedSteering);
11768
11909
  this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "thinking" });
11910
+ this.flushPendingDirectStdinNotificationOnRuntimeProgress(agentId, ap, event.kind);
11769
11911
  }
11770
11912
  break;
11771
11913
  }
@@ -11776,6 +11918,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11776
11918
  this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
11777
11919
  const reduction = reduceApmGatedAssistantContinuation(ap.gatedSteering);
11778
11920
  this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "text" });
11921
+ this.flushPendingDirectStdinNotificationOnRuntimeProgress(agentId, ap, event.kind);
11779
11922
  }
11780
11923
  break;
11781
11924
  }
@@ -11848,6 +11991,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11848
11991
  if (ap) {
11849
11992
  if (event.sessionId) ap.sessionId = event.sessionId;
11850
11993
  const stickyTerminalFailure = classifyStickyTerminalFailure(ap);
11994
+ if (!stickyTerminalFailure && ap.runtimeErrorDeliveryBackoff.reason === "runtime_error") {
11995
+ this.clearRuntimeErrorDeliveryBackoffWithTrace(agentId, ap, "turn_end_unclassified_runtime_error");
11996
+ }
11851
11997
  const reduction = reduceApmGatedTurnEnd(ap.gatedSteering, {
11852
11998
  inboxLength: stickyTerminalFailure ? 0 : ap.inbox.length,
11853
11999
  supportsStdinNotification: ap.driver.supportsStdinNotification,
@@ -11979,7 +12125,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11979
12125
  }
11980
12126
  }
11981
12127
  recordRuntimeTelemetry(agentId, ap, event) {
11982
- const sessionId = ap.driver.currentSessionId ?? event.sessionId;
12128
+ const sessionId = ap.driver.currentSessionId ?? event.sessionId ?? ap.sessionId ?? ap.config.sessionId;
12129
+ const resultIdentity = this.runtimeTelemetryResultIdentity(agentId, ap, event);
11983
12130
  const payloadAttrs = sanitizeRuntimeTelemetryPayloadAttrs(event.attrs);
11984
12131
  const versionAttrs = this.runtimeTelemetryVersionAttrs();
11985
12132
  const telemetryAttrs = {
@@ -11989,7 +12136,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11989
12136
  ...event.usageKind ? { usageKind: event.usageKind } : {},
11990
12137
  ...sessionId ? { sessionId } : {},
11991
12138
  ...event.turnId ? { turnId: event.turnId } : {},
11992
- ...event.runtimeResultId ? { runtimeResultId: event.runtimeResultId } : {}
12139
+ ...resultIdentity
11993
12140
  };
11994
12141
  const attrs = {
11995
12142
  agentId,
@@ -12002,6 +12149,16 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
12002
12149
  ap.runtimeTraceSpan?.addEvent(`runtime.telemetry.${event.name}`, telemetryAttrs);
12003
12150
  this.recordDaemonTrace(`daemon.runtime.telemetry.${event.name}`, attrs);
12004
12151
  }
12152
+ runtimeTelemetryResultIdentity(agentId, ap, event) {
12153
+ if (event.runtimeResultId) return { runtimeResultId: event.runtimeResultId };
12154
+ if (event.name !== "token_usage" || event.source !== "claude_result_usage") return {};
12155
+ const sequence = ++ap.runtimeTelemetryResultSeq;
12156
+ const scope = ap.launchId || agentId;
12157
+ return {
12158
+ runtimeResultId: `${scope}:claude_result_usage:${sequence}`,
12159
+ runtimeResultIdSource: "daemon_sequence"
12160
+ };
12161
+ }
12005
12162
  runtimeTelemetryVersionAttrs() {
12006
12163
  return {
12007
12164
  ...this.daemonVersion ? {
@@ -12070,7 +12227,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
12070
12227
  }
12071
12228
  }
12072
12229
  /** Send a batched notification to the agent via stdin about pending messages */
12073
- sendStdinNotification(agentId) {
12230
+ sendStdinNotification(agentId, options = {}) {
12074
12231
  const ap = this.agents.get(agentId);
12075
12232
  if (!ap) return false;
12076
12233
  const count = ap.notifications.takePendingAndClearTimer();
@@ -12112,7 +12269,24 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
12112
12269
  }
12113
12270
  const inboxCount = ap.inbox.length;
12114
12271
  if (inboxCount === 0) return false;
12115
- const changedMessages = ap.inbox.slice(Math.max(0, ap.inbox.length - count));
12272
+ ap.notifications.pruneContributedToPending(ap.inbox, ap.sessionId);
12273
+ const changedMessageCandidates = ap.inbox.slice(Math.max(0, ap.inbox.length - count));
12274
+ const changedMessages = ap.notifications.filterUncontributedMessages(changedMessageCandidates, ap.sessionId);
12275
+ if (changedMessages.length === 0) {
12276
+ this.recordDaemonTrace("daemon.agent.stdin_notification", {
12277
+ agentId,
12278
+ runtime: ap.config.runtime,
12279
+ model: ap.config.model,
12280
+ launchId: ap.launchId || void 0,
12281
+ outcome: "suppressed_already_contributed",
12282
+ mode: "busy",
12283
+ pending_notification_count: count,
12284
+ inbox_count: ap.inbox.length,
12285
+ session_id_present: true
12286
+ });
12287
+ logger.info(`[Agent ${agentId}] Suppressing stdin inbox notice because all candidate messages already contributed; pending=${ap.inbox.length}`);
12288
+ return false;
12289
+ }
12116
12290
  const noticeFingerprint = computeInboxNoticeFingerprint(changedMessages);
12117
12291
  if (ap.notifications.isDuplicateNotice(noticeFingerprint, ap.sessionId)) {
12118
12292
  this.recordDaemonTrace("daemon.agent.stdin_notification", {
@@ -12129,6 +12303,22 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
12129
12303
  logger.info(`[Agent ${agentId}] Suppressing duplicate stdin inbox notice (unread-set unchanged since last write); pending=${ap.inbox.length}`);
12130
12304
  return false;
12131
12305
  }
12306
+ if (!options.forceUnsupportedRetry && ap.notifications.isDuplicateEncodeFailedNotice(noticeFingerprint, ap.sessionId)) {
12307
+ ap.notifications.add(count);
12308
+ this.recordDaemonTrace("daemon.agent.stdin_notification", {
12309
+ agentId,
12310
+ runtime: ap.config.runtime,
12311
+ model: ap.config.model,
12312
+ launchId: ap.launchId || void 0,
12313
+ outcome: "suppressed_duplicate_encode_failed",
12314
+ mode: "busy",
12315
+ pending_notification_count: count,
12316
+ inbox_count: ap.inbox.length,
12317
+ session_id_present: true
12318
+ });
12319
+ logger.info(`[Agent ${agentId}] Suppressing duplicate unsupported stdin inbox notice (unread-set unchanged since last encode failure); pending=${ap.inbox.length}`);
12320
+ return false;
12321
+ }
12132
12322
  const inboxRows = projectAgentInboxSnapshot(changedMessages);
12133
12323
  const notification = `[Slock inbox notice:
12134
12324
  ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
@@ -12151,6 +12341,7 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
12151
12341
  model: ap.config.model,
12152
12342
  launchId: ap.launchId || void 0,
12153
12343
  mode: "busy",
12344
+ source: "busy_stdin_notification",
12154
12345
  notification_byte_count: notificationByteCount,
12155
12346
  ...projectionAttrs
12156
12347
  });
@@ -12166,12 +12357,15 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
12166
12357
  inbox_target_count: inboxRows.length,
12167
12358
  session_id_present: true
12168
12359
  });
12169
- ap.notifications.recordNoticeWritten(noticeFingerprint, ap.sessionId);
12360
+ ap.notifications.recordNoticeWritten(noticeFingerprint, ap.sessionId, changedMessages);
12170
12361
  return true;
12171
12362
  } else {
12172
12363
  ap.notifications.add(count);
12173
- const retryScheduled = ap.runtime.descriptor.busyDelivery === "direct" ? this.scheduleStdinNotification(agentId, ap, this.stdinNotificationRetryMs) : false;
12364
+ const retryScheduled = ap.runtime.descriptor.busyDelivery === "direct" && sendResult.reason !== "unsupported" ? this.scheduleStdinNotification(agentId, ap, this.stdinNotificationRetryMs) : false;
12174
12365
  const outcome = runtimeSendFailureOutcome(sendResult);
12366
+ if (outcome === "encode_failed") {
12367
+ ap.notifications.recordNoticeEncodeFailed(noticeFingerprint, ap.sessionId);
12368
+ }
12175
12369
  this.recordDaemonTrace("daemon.agent.stdin_notification", {
12176
12370
  agentId,
12177
12371
  runtime: ap.config.runtime,
@@ -12213,6 +12407,7 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
12213
12407
  model: ap.config.model,
12214
12408
  launchId: ap.launchId || void 0,
12215
12409
  mode,
12410
+ source,
12216
12411
  notification_byte_count: Buffer.byteLength(renderedInput, "utf8"),
12217
12412
  cursors_advanced: "none",
12218
12413
  ...projectionAttrs
@@ -12291,6 +12486,7 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
12291
12486
  stdin_write_attempted: true,
12292
12487
  cursors_advanced: "none"
12293
12488
  });
12489
+ ap.notifications.recordNoticeWritten(computeInboxNoticeFingerprint(messages), ap.sessionId, messages);
12294
12490
  return true;
12295
12491
  }
12296
12492
  /** Deliver a message to an agent via stdin, formatting it the same way as the MCP bridge */
@@ -12507,7 +12703,7 @@ var DaemonConnection = class {
12507
12703
  logger.warn(`[Daemon] Dropping outbound message while disconnected: ${msg.type}`);
12508
12704
  }
12509
12705
  this.trace("daemon.connection.outbound_dropped", {
12510
- message_type: msg.type,
12706
+ outbound_message_kind: msg.type,
12511
12707
  ws_ready_state: this.ws?.readyState ?? null
12512
12708
  });
12513
12709
  }
@@ -12556,7 +12752,7 @@ var DaemonConnection = class {
12556
12752
  this.resetWatchdog();
12557
12753
  if (messageKind !== "ping") {
12558
12754
  this.trace("daemon.connection.inbound_received", {
12559
- message_type: messageKind,
12755
+ inbound_message_kind: messageKind,
12560
12756
  last_inbound_age_ms_bucket: "0"
12561
12757
  });
12562
12758
  }
@@ -12647,7 +12843,7 @@ var DaemonConnection = class {
12647
12843
  if (msg.launchId && latestLaunchId && msg.launchId !== latestLaunchId) {
12648
12844
  this.trace("daemon.connection.pending_activity_invalidated", {
12649
12845
  reason: "launch_changed",
12650
- message_type: msg.type,
12846
+ outbound_message_kind: msg.type,
12651
12847
  agentId: msg.agentId,
12652
12848
  stale_launch_id_present: true,
12653
12849
  next_launch_id_present: true
@@ -12667,7 +12863,7 @@ var DaemonConnection = class {
12667
12863
  this.pendingActivityByAgent.delete(identity.agentId);
12668
12864
  this.trace("daemon.connection.pending_activity_invalidated", {
12669
12865
  reason: "launch_changed",
12670
- message_type: msg.type,
12866
+ outbound_message_kind: msg.type,
12671
12867
  agentId: identity.agentId,
12672
12868
  stale_launch_id_present: Boolean(pending.launchId),
12673
12869
  next_launch_id_present: true
@@ -12696,7 +12892,7 @@ var DaemonConnection = class {
12696
12892
  ws.send(JSON.stringify(msg));
12697
12893
  }
12698
12894
  this.trace("daemon.connection.outbound_replayed", {
12699
- message_type: "agent:activity",
12895
+ outbound_message_kind: "agent:activity",
12700
12896
  message_count: pending.length
12701
12897
  });
12702
12898
  }
@@ -13522,7 +13718,52 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
13522
13718
  var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
13523
13719
  var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
13524
13720
  var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
13525
- var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
13721
+ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
13722
+ "daemon.lifecycle.start": {
13723
+ spanAttrs: ["machine_dir_present", "local_trace_enabled"],
13724
+ eventAttrs: {
13725
+ "daemon.machine_lock.acquired": ["machine_dir_present"]
13726
+ }
13727
+ },
13728
+ "daemon.lifecycle.stop": {
13729
+ spanAttrs: ["machine_lock_present"]
13730
+ },
13731
+ "daemon.runner_credential_mint.retry": {
13732
+ spanAttrs: ["agentId", "runtime", "attempt", "max_attempts", "status", "code", "reason", "retryable"]
13733
+ },
13734
+ "daemon.runner_credential_mint.failed": {
13735
+ spanAttrs: ["agentId", "runtime", "status", "code", "reason", "retryable", "max_attempts"]
13736
+ },
13737
+ "daemon.agent.spawn.failed": {
13738
+ spanAttrs: ["agentId", "launchId", "runtime", "model", "failure_reason", "failure_detail", "session_id_present"]
13739
+ },
13740
+ "daemon.agent.delivery": {
13741
+ spanAttrs: ["agentId", "deliveryId", "delivery_correlation_id", "messageId", "message_id_present", "seq"],
13742
+ eventAttrs: {
13743
+ "daemon.receive": ["seq", "deliveryId"],
13744
+ "daemon.deliver_to_agent_manager": ["accepted"],
13745
+ "daemon.ack.sent": ["seq"]
13746
+ },
13747
+ endAttrs: ["outcome", "ackSeq", "deliveryId", "error_class"]
13748
+ },
13749
+ "daemon.runtime_profile.control.received": {
13750
+ spanAttrs: ["agentId", "control_kind", "key_present", "launchId"],
13751
+ endAttrs: ["outcome", "error_class"]
13752
+ },
13753
+ "daemon.computer_control.received": {
13754
+ spanAttrs: ["action", "handled"]
13755
+ },
13756
+ "daemon.ready.sent": {
13757
+ spanAttrs: ["runtimes_count", "running_agents_count", "idle_agents_count", "runtime_profile_reports_count"]
13758
+ },
13759
+ "daemon.runtime_profile.report.sent": {
13760
+ spanAttrs: ["agentId", "launchId", "runtime", "report_source", "model_present", "session_ref_present", "workspace_ref_present"]
13761
+ },
13762
+ "daemon.connection.local_disconnect_observed": {
13763
+ spanAttrs: ["running_agents_count", "idle_agents_count"]
13764
+ }
13765
+ };
13766
+ var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
13526
13767
  var RunnerCredentialMintError2 = class extends Error {
13527
13768
  code;
13528
13769
  retryable;
@@ -13558,9 +13799,9 @@ function runnerCredentialErrorDetail2(error) {
13558
13799
  async function waitForRunnerCredentialRetry2() {
13559
13800
  await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
13560
13801
  }
13561
- function parseDaemonCliArgs(args, env = {}) {
13802
+ function parseDaemonCliArgs(args) {
13562
13803
  let serverUrl = "";
13563
- let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
13804
+ let apiKey = "";
13564
13805
  for (let i = 0; i < args.length; i++) {
13565
13806
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
13566
13807
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
@@ -13729,6 +13970,8 @@ var DaemonCore = class {
13729
13970
  tracer;
13730
13971
  injectedTracer;
13731
13972
  machineLock = null;
13973
+ observedServerId = null;
13974
+ observedMachineId = null;
13732
13975
  localTraceSink = null;
13733
13976
  traceBundleUploader = null;
13734
13977
  constructor(options) {
@@ -13779,6 +14022,7 @@ var DaemonCore = class {
13779
14022
  return process.env.SLOCK_DAEMON_LOCAL_TRACE !== "0";
13780
14023
  }
13781
14024
  resolveTraceJitter() {
14025
+ if (process.env.SLOCK_DAEMON_TRACE_JITTER_DISABLED === "1") return NO_JITTER;
13782
14026
  const lockId = this.machineLock?.lockId;
13783
14027
  return lockId ? computeTraceJitter(lockId) : NO_JITTER;
13784
14028
  }
@@ -13810,7 +14054,7 @@ var DaemonCore = class {
13810
14054
  workerUrl,
13811
14055
  tracer: this.tracer,
13812
14056
  currentFileProvider: () => this.localTraceSink?.getCurrentFile() ?? null,
13813
- lockId: this.machineLock?.lockId
14057
+ jitter: this.resolveTraceJitter()
13814
14058
  });
13815
14059
  this.traceBundleUploader.start();
13816
14060
  }
@@ -13889,22 +14133,29 @@ var DaemonCore = class {
13889
14133
  span.end(status);
13890
14134
  }
13891
14135
  withDaemonTraceScope(tracer) {
13892
- return createScopedTracer(tracer, this.daemonTraceScopeAttrs());
14136
+ return {
14137
+ startSpan: (name, options) => createTraceScopeTracer(tracer, this.daemonTraceScope(), {
14138
+ spanAttrContracts: DAEMON_CORE_TRACE_ATTR_CONTRACTS
14139
+ }).startSpan(name, options)
14140
+ };
13893
14141
  }
13894
- daemonTraceScopeAttrs() {
14142
+ daemonTraceScope() {
13895
14143
  return {
13896
- daemonVersion: this.daemonVersion,
13897
- daemon_version: this.daemonVersion,
13898
- daemon_version_present: Boolean(this.daemonVersion),
13899
- ...this.computerVersion ? {
13900
- computerVersion: this.computerVersion,
13901
- computer_version: this.computerVersion,
13902
- computer_version_present: true
13903
- } : {
13904
- computer_version_present: false
14144
+ resource: {
14145
+ daemonVersion: this.daemonVersion,
14146
+ computerVersion: this.computerVersion
14147
+ },
14148
+ actor: {
14149
+ serverId: this.observedServerId,
14150
+ machineId: this.observedMachineId
13905
14151
  }
13906
14152
  };
13907
14153
  }
14154
+ observeRuntimeContext(config) {
14155
+ const ctx = config.runtimeContext;
14156
+ if (ctx?.serverId) this.observedServerId = ctx.serverId;
14157
+ if (ctx?.machineId) this.observedMachineId = ctx.machineId;
14158
+ }
13908
14159
  async requestRunnerCredentialOnce(agentId, config) {
13909
14160
  const url = new URL(`/internal/computer/runners/${encodeURIComponent(agentId)}/credentials`, this.options.serverUrl);
13910
14161
  const res = await daemonFetch(url, {
@@ -13996,6 +14247,7 @@ var DaemonCore = class {
13996
14247
  );
13997
14248
  }
13998
14249
  async startAgentFromMessage(msg) {
14250
+ this.observeRuntimeContext(msg.config);
13999
14251
  const agentCredential = await this.mintRunnerCredential(msg.agentId, msg.config);
14000
14252
  const config = { ...msg.config, agentCredentialKey: agentCredential.apiKey, agentCredentialId: agentCredential.credentialId };
14001
14253
  await this.agentManager.startAgent(
@@ -14013,6 +14265,7 @@ var DaemonCore = class {
14013
14265
  logger.info(`[Daemon] Received ${msg.type}${summary ? ` ${summary}` : ""}`);
14014
14266
  switch (msg.type) {
14015
14267
  case "agent:start":
14268
+ this.observeRuntimeContext(msg.config);
14016
14269
  logger.info(`[Agent ${msg.agentId}] Start requested (runtime=${msg.config.runtime}, model=${msg.config.model}, session=${msg.config.sessionId || "new"}${msg.wakeMessage ? ", wake=true" : ""})`);
14017
14270
  this.startAgentFromMessage(msg).catch((err) => {
14018
14271
  const classification = classifySpawnFailure(err);
@@ -14329,8 +14582,6 @@ var DaemonCore = class {
14329
14582
  };
14330
14583
 
14331
14584
  export {
14332
- DAEMON_API_KEY_ENV,
14333
- scrubDaemonAuthEnv,
14334
14585
  subscribeDaemonLogs,
14335
14586
  resolveWorkspaceDirectoryPath,
14336
14587
  scanWorkspaceDirectories,
package/dist/cli/index.js CHANGED
@@ -860,11 +860,17 @@ async function runDeviceCodeLogin(options) {
860
860
  const pollIntervalMs = options.pollIntervalOverrideMs ?? serverIntervalMs;
861
861
  const deadlineMs = Date.now() + Math.max(1, authorizeBody.expiresIn ?? 600) * 1e3;
862
862
  while (Date.now() < deadlineMs) {
863
- const tokenRes = await httpFetch(`${base}/api/auth/device/token`, {
864
- method: "POST",
865
- headers: { "content-type": "application/json" },
866
- body: JSON.stringify({ deviceCode: authorizeBody.deviceCode })
867
- });
863
+ let tokenRes;
864
+ try {
865
+ tokenRes = await httpFetch(`${base}/api/auth/device/token`, {
866
+ method: "POST",
867
+ headers: { "content-type": "application/json" },
868
+ body: JSON.stringify({ deviceCode: authorizeBody.deviceCode })
869
+ });
870
+ } catch {
871
+ await delay(pollIntervalMs);
872
+ continue;
873
+ }
868
874
  if (tokenRes.ok) {
869
875
  const tokenBody = await tokenRes.json();
870
876
  if (!tokenBody.accessToken || !tokenBody.refreshToken || !tokenBody.userId) {
@@ -15249,6 +15255,12 @@ var SERVER_CAPABILITY_MATRIX = {
15249
15255
  };
15250
15256
 
15251
15257
  // ../shared/src/index.ts
15258
+ var EXTERNAL_AGENT_RUNTIME_ID = "external";
15259
+ var EXTERNAL_AGENT_RUNTIME_MODEL = "external";
15260
+ var EXTERNAL_AGENT_RUNTIME_DISPLAY_NAME = "External agent";
15261
+ function isExternalAgentRuntime(runtime) {
15262
+ return runtime === EXTERNAL_AGENT_RUNTIME_ID;
15263
+ }
15252
15264
  var RUNTIMES = [
15253
15265
  { id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
15254
15266
  { id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
@@ -15261,8 +15273,71 @@ var RUNTIMES = [
15261
15273
  { id: "pi", displayName: "Pi", binary: "pi", supported: true }
15262
15274
  ];
15263
15275
  function getRuntimeDisplayName(id) {
15276
+ if (isExternalAgentRuntime(id)) return EXTERNAL_AGENT_RUNTIME_DISPLAY_NAME;
15264
15277
  return RUNTIMES.find((r) => r.id === id)?.displayName ?? id;
15265
15278
  }
15279
+ var RUNTIME_MODELS = {
15280
+ [EXTERNAL_AGENT_RUNTIME_ID]: [
15281
+ { id: EXTERNAL_AGENT_RUNTIME_MODEL, label: EXTERNAL_AGENT_RUNTIME_DISPLAY_NAME }
15282
+ ],
15283
+ claude: [
15284
+ { id: "opus", label: "Claude Opus" },
15285
+ { id: "claude-opus-4-8", label: "Claude Opus 4.8" },
15286
+ { id: "claude-opus-4-7", label: "Claude Opus 4.7" },
15287
+ { id: "claude-opus-4-6", label: "Claude Opus 4.6" },
15288
+ { id: "sonnet", label: "Claude Sonnet" },
15289
+ { id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6" },
15290
+ { id: "haiku", label: "Claude Haiku" },
15291
+ { id: "claude-haiku-4-5", label: "Claude Haiku 4.5" }
15292
+ ],
15293
+ codex: [
15294
+ { id: "gpt-5.5", label: "GPT-5.5" },
15295
+ { id: "gpt-5.4", label: "GPT-5.4" },
15296
+ { id: "gpt-5.3-codex", label: "GPT-5.3 Codex" },
15297
+ { id: "gpt-5.3-codex-spark", label: "GPT-5.3 Codex Spark" },
15298
+ { id: "gpt-5.2-codex", label: "GPT-5.2 Codex" },
15299
+ { id: "gpt-5.2", label: "GPT-5.2" },
15300
+ { id: "gpt-5.1-codex-max", label: "GPT-5.1 Codex Max" },
15301
+ { id: "gpt-5.1-codex", label: "GPT-5.1 Codex" },
15302
+ { id: "gpt-5-codex", label: "GPT-5 Codex" },
15303
+ { id: "gpt-5", label: "GPT-5" }
15304
+ ],
15305
+ antigravity: [
15306
+ { id: "default", label: "AGY configured default", verified: "suggestion_only" }
15307
+ ],
15308
+ copilot: [
15309
+ { id: "gpt-5.4", label: "GPT-5.4" },
15310
+ { id: "gpt-5.2", label: "GPT-5.2" },
15311
+ { id: "claude-4-sonnet", label: "Claude 4 Sonnet" },
15312
+ { id: "claude-4.5-sonnet", label: "Claude 4.5 Sonnet" }
15313
+ ],
15314
+ cursor: [
15315
+ { id: "composer-2-fast", label: "Composer 2 Fast" },
15316
+ { id: "composer-2", label: "Composer 2" },
15317
+ { id: "auto", label: "Auto" }
15318
+ ],
15319
+ gemini: [
15320
+ { id: "default", label: "Configured Default / Auto", verified: "suggestion_only" },
15321
+ { id: "gemini-3.1-pro-preview", label: "Gemini 3.1 Pro (Preview)" },
15322
+ { id: "gemini-3-flash-preview", label: "Gemini 3 Flash (Preview)" },
15323
+ { id: "gemini-2.5-pro", label: "Gemini 2.5 Pro" },
15324
+ { id: "gemini-2.5-flash", label: "Gemini 2.5 Flash" }
15325
+ ],
15326
+ opencode: [
15327
+ { id: "default", label: "Configured Default / Auto", verified: "suggestion_only" },
15328
+ { id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro (OpenCode)", verified: "suggestion_only" },
15329
+ { id: "openrouter/anthropic/claude-opus-4.5", label: "Claude Opus 4.5 via OpenRouter", verified: "suggestion_only" },
15330
+ { id: "fusecode/opus[1m]", label: "Opus 1M via FuseCode", verified: "suggestion_only" }
15331
+ ],
15332
+ pi: [
15333
+ { id: "default", label: "Configured Default / Auto", verified: "suggestion_only" }
15334
+ ],
15335
+ // Kimi CLI resolves model keys from each user's local config, so the safest
15336
+ // built-in option is to defer to whatever default model the CLI already uses.
15337
+ kimi: [
15338
+ { id: "default", label: "Configured Default" }
15339
+ ]
15340
+ };
15266
15341
  var PLAN_CONFIG = {
15267
15342
  free: {
15268
15343
  displayName: "Hobby",
package/dist/core.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import {
2
- DAEMON_API_KEY_ENV,
3
2
  DAEMON_CLI_USAGE,
4
3
  DaemonCore,
5
4
  deleteWorkspaceDirectory,
@@ -9,11 +8,9 @@ import {
9
8
  resolveSlockCliPath,
10
9
  resolveWorkspaceDirectoryPath,
11
10
  scanWorkspaceDirectories,
12
- scrubDaemonAuthEnv,
13
11
  subscribeDaemonLogs
14
- } from "./chunk-4PZVJXRJ.js";
12
+ } from "./chunk-EKSATDT3.js";
15
13
  export {
16
- DAEMON_API_KEY_ENV,
17
14
  DAEMON_CLI_USAGE,
18
15
  DaemonCore,
19
16
  deleteWorkspaceDirectory,
@@ -23,6 +20,5 @@ export {
23
20
  resolveSlockCliPath,
24
21
  resolveWorkspaceDirectoryPath,
25
22
  scanWorkspaceDirectories,
26
- scrubDaemonAuthEnv,
27
23
  subscribeDaemonLogs
28
24
  };
package/dist/index.js CHANGED
@@ -2,13 +2,11 @@
2
2
  import {
3
3
  DAEMON_CLI_USAGE,
4
4
  DaemonCore,
5
- parseDaemonCliArgs,
6
- scrubDaemonAuthEnv
7
- } from "./chunk-4PZVJXRJ.js";
5
+ parseDaemonCliArgs
6
+ } from "./chunk-EKSATDT3.js";
8
7
 
9
8
  // src/index.ts
10
- var parsedArgs = parseDaemonCliArgs(process.argv.slice(2), process.env);
11
- scrubDaemonAuthEnv(process.env);
9
+ var parsedArgs = parseDaemonCliArgs(process.argv.slice(2));
12
10
  if (!parsedArgs) {
13
11
  console.error(DAEMON_CLI_USAGE);
14
12
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slock-ai/daemon",
3
- "version": "0.57.1-play.20260607140323",
3
+ "version": "0.57.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "slock-daemon": "dist/index.js"
@@ -1,96 +0,0 @@
1
- // src/drivers/piSdkRunner.ts
2
- import { readFile } from "fs/promises";
3
- import path from "path";
4
- import {
5
- AuthStorage,
6
- createAgentSessionFromServices,
7
- createAgentSessionServices,
8
- SessionManager
9
- } from "@earendil-works/pi-coding-agent";
10
- function writeJson(value) {
11
- process.stdout.write(`${JSON.stringify(value)}
12
- `);
13
- }
14
- function parseArgs(argv) {
15
- const index = argv.indexOf("--config");
16
- const configPath = index >= 0 ? argv[index + 1] : void 0;
17
- if (!configPath) throw new Error("Missing --config <path>");
18
- return { configPath };
19
- }
20
- function resolveConfiguredModel(modelId, modelRegistry) {
21
- if (!modelId) return void 0;
22
- const [provider, ...rest] = modelId.split("/");
23
- const providerScopedId = rest.join("/");
24
- if (provider && providerScopedId) {
25
- const exact = modelRegistry.find(provider, providerScopedId);
26
- if (exact) return exact;
27
- }
28
- return modelRegistry.getAll().find(
29
- (model) => model.id === modelId || `${model.provider}/${model.id}` === modelId || (providerScopedId ? model.id === providerScopedId : false)
30
- );
31
- }
32
- async function createSessionManager(config) {
33
- if (!config.sessionId) return SessionManager.create(config.cwd, config.sessionDir);
34
- const localSessions = await SessionManager.list(config.cwd, config.sessionDir);
35
- const match = localSessions.find((session) => session.id.startsWith(config.sessionId));
36
- if (match) return SessionManager.open(match.path, config.sessionDir);
37
- return SessionManager.create(config.cwd, config.sessionDir);
38
- }
39
- async function run() {
40
- const { configPath } = parseArgs(process.argv.slice(2));
41
- const config = JSON.parse(await readFile(configPath, "utf8"));
42
- const authStorage = AuthStorage.create(path.join(config.agentDir, "auth.json"));
43
- const services = await createAgentSessionServices({
44
- cwd: config.cwd,
45
- agentDir: config.agentDir,
46
- authStorage,
47
- resourceLoaderOptions: {
48
- appendSystemPrompt: [config.standingPrompt],
49
- noContextFiles: true,
50
- noExtensions: true,
51
- noPromptTemplates: true,
52
- noSkills: true,
53
- noThemes: true
54
- }
55
- });
56
- for (const diagnostic of services.diagnostics) {
57
- const line = `[Pi SDK] ${diagnostic.type}: ${diagnostic.message}`;
58
- if (diagnostic.type === "error") throw new Error(line);
59
- process.stderr.write(`${line}
60
- `);
61
- }
62
- const sessionManager = await createSessionManager(config);
63
- const model = resolveConfiguredModel(config.model, services.modelRegistry);
64
- if (config.model && !model) {
65
- throw new Error(`Configured Pi model '${config.model}' was not found in Pi model registry.`);
66
- }
67
- const { session } = await createAgentSessionFromServices({
68
- services,
69
- sessionManager,
70
- model
71
- });
72
- const header = session.sessionManager.getHeader();
73
- if (header) writeJson(header);
74
- const unsubscribe = session.subscribe((event) => writeJson(event));
75
- try {
76
- await session.prompt(config.prompt);
77
- } finally {
78
- unsubscribe();
79
- session.dispose();
80
- await services.settingsManager.flush();
81
- }
82
- }
83
- run().catch((error) => {
84
- const message = error instanceof Error ? error.message : String(error);
85
- writeJson({
86
- type: "message_end",
87
- message: {
88
- role: "assistant",
89
- content: [],
90
- stopReason: "error",
91
- errorMessage: message
92
- }
93
- });
94
- writeJson({ type: "turn_end" });
95
- process.exitCode = 1;
96
- });