@openscout/scout 0.2.20 → 0.2.21

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.
@@ -7,7 +7,7 @@
7
7
  <link rel="preconnect" href="https://fonts.googleapis.com" />
8
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
9
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&family=Spectral:wght@500;600&display=swap" rel="stylesheet" />
10
- <script type="module" crossorigin src="/assets/index-D2_peTVB.js"></script>
10
+ <script type="module" crossorigin src="/assets/index-D70CFXZt.js"></script>
11
11
  <link rel="stylesheet" crossorigin href="/assets/index-DAAaRFvV.css">
12
12
  </head>
13
13
  <body>
package/dist/main.mjs CHANGED
@@ -4412,6 +4412,9 @@ function resolveClaudeStreamJsonOutput(result, fallbackParts) {
4412
4412
  function sessionKey(options) {
4413
4413
  return `${options.agentName}:${options.sessionId}`;
4414
4414
  }
4415
+ function errorMessage(error) {
4416
+ return error instanceof Error ? error.message : String(error);
4417
+ }
4415
4418
  async function readOptionalFile(filePath) {
4416
4419
  try {
4417
4420
  const raw = await readFile4(filePath, "utf8");
@@ -4564,17 +4567,35 @@ class ClaudeStreamJsonSession {
4564
4567
  if (this.claudeSessionId) {
4565
4568
  args.push("--resume", this.claudeSessionId);
4566
4569
  }
4567
- this.process = spawn("claude", args, {
4568
- cwd: this.options.cwd,
4569
- env: buildManagedAgentEnvironment({
4570
- agentName: this.options.agentName,
4571
- currentDirectory: this.options.cwd,
4572
- baseEnv: process.env
4573
- })
4570
+ let child;
4571
+ try {
4572
+ child = spawn("claude", args, {
4573
+ cwd: this.options.cwd,
4574
+ env: buildManagedAgentEnvironment({
4575
+ agentName: this.options.agentName,
4576
+ currentDirectory: this.options.cwd,
4577
+ baseEnv: process.env
4578
+ })
4579
+ });
4580
+ } catch (error) {
4581
+ throw new Error(`Failed to spawn claude: ${errorMessage(error)}`);
4582
+ }
4583
+ this.process = child;
4584
+ child.on("error", (error) => {
4585
+ console.error(`[openscout-runtime] claude process error for ${this.options.agentName}: ${error.message}`);
4586
+ this.process = null;
4587
+ if (this.activeTurn) {
4588
+ const turn = this.activeTurn;
4589
+ this.activeTurn = null;
4590
+ if (turn.timer) {
4591
+ clearTimeout(turn.timer);
4592
+ }
4593
+ turn.reject(new Error(`Claude process error: ${error.message}`));
4594
+ }
4574
4595
  });
4575
- this.process.stdout.setEncoding("utf8");
4576
- this.process.stderr.setEncoding("utf8");
4577
- this.process.stdout.on("data", (chunk) => {
4596
+ child.stdout.setEncoding("utf8");
4597
+ child.stderr.setEncoding("utf8");
4598
+ child.stdout.on("data", (chunk) => {
4578
4599
  appendFile(this.stdoutLogPath, chunk).catch(() => {
4579
4600
  return;
4580
4601
  });
@@ -4590,12 +4611,12 @@ class ClaudeStreamJsonSession {
4590
4611
  this.handleEvent(JSON.parse(trimmed));
4591
4612
  }
4592
4613
  });
4593
- this.process.stderr.on("data", (chunk) => {
4614
+ child.stderr.on("data", (chunk) => {
4594
4615
  appendFile(this.stderrLogPath, chunk).catch(() => {
4595
4616
  return;
4596
4617
  });
4597
4618
  });
4598
- this.process.on("exit", (code) => {
4619
+ child.on("exit", (code) => {
4599
4620
  if (code !== 0 && this.activeTurn) {
4600
4621
  const turn = this.activeTurn;
4601
4622
  this.activeTurn = null;
@@ -4745,7 +4766,7 @@ function parseJsonLine(line) {
4745
4766
  return null;
4746
4767
  }
4747
4768
  }
4748
- function errorMessage(error) {
4769
+ function errorMessage2(error) {
4749
4770
  if (error instanceof Error) {
4750
4771
  return error.message;
4751
4772
  }
@@ -4770,7 +4791,7 @@ async function readOptionalFile2(filePath) {
4770
4791
  }
4771
4792
  }
4772
4793
  function isMissingCodexRolloutError(error) {
4773
- const message = errorMessage(error).toLowerCase();
4794
+ const message = errorMessage2(error).toLowerCase();
4774
4795
  return message.includes("no rollout found for thread id");
4775
4796
  }
4776
4797
 
@@ -4843,7 +4864,7 @@ class CodexAppServerSession {
4843
4864
  await this.persistState();
4844
4865
  } catch (error) {
4845
4866
  this.clearActiveTurn(turn);
4846
- reject(error instanceof Error ? error : new Error(errorMessage(error)));
4867
+ reject(error instanceof Error ? error : new Error(errorMessage2(error)));
4847
4868
  }
4848
4869
  });
4849
4870
  return {
@@ -4971,7 +4992,7 @@ class CodexAppServerSession {
4971
4992
  });
4972
4993
  });
4973
4994
  child.once("error", (error) => {
4974
- this.failSession(new Error(`Codex app-server failed for ${this.options.agentName}: ${errorMessage(error)}`));
4995
+ this.failSession(new Error(`Codex app-server failed for ${this.options.agentName}: ${errorMessage2(error)}`));
4975
4996
  });
4976
4997
  child.once("exit", (code, signal) => {
4977
4998
  this.failSession(new Error(`Codex app-server exited for ${this.options.agentName}` + (code !== null ? ` with code ${code}` : "") + (signal ? ` (${signal})` : "")));
@@ -5007,7 +5028,7 @@ class CodexAppServerSession {
5007
5028
  await this.persistThreadId();
5008
5029
  return;
5009
5030
  } catch (error) {
5010
- await appendFile2(this.stderrLogPath, `[openscout] failed to resume stored Codex thread ${storedThreadId}: ${errorMessage(error)}
5031
+ await appendFile2(this.stderrLogPath, `[openscout] failed to resume stored Codex thread ${storedThreadId}: ${errorMessage2(error)}
5011
5032
  `).catch(() => {
5012
5033
  return;
5013
5034
  });
@@ -5277,7 +5298,7 @@ class CodexAppServerSession {
5277
5298
  });
5278
5299
  } catch (error) {
5279
5300
  this.pendingRequests.delete(id);
5280
- reject(error instanceof Error ? error : new Error(errorMessage(error)));
5301
+ reject(error instanceof Error ? error : new Error(errorMessage2(error)));
5281
5302
  }
5282
5303
  });
5283
5304
  }
@@ -6759,11 +6780,33 @@ function killAgentSession(sessionName) {
6759
6780
  execSync(`tmux kill-session -t ${JSON.stringify(sessionName)}`, { stdio: "pipe" });
6760
6781
  } catch {}
6761
6782
  }
6783
+ function isHarnessBinaryAvailable(transport) {
6784
+ const binaryMap = {
6785
+ claude_stream_json: "claude",
6786
+ codex_app_server: "codex"
6787
+ };
6788
+ const binary = binaryMap[transport];
6789
+ if (!binary)
6790
+ return true;
6791
+ try {
6792
+ execFileSync3("sh", ["-lc", `command -v ${binary}`], {
6793
+ stdio: ["ignore", "pipe", "ignore"],
6794
+ encoding: "utf8"
6795
+ });
6796
+ return true;
6797
+ } catch {
6798
+ return false;
6799
+ }
6800
+ }
6762
6801
  async function ensureLocalAgentOnline(agentName, record) {
6763
6802
  const normalizedRecord = normalizeLocalAgentRecord(agentName, record);
6764
6803
  if (isLocalAgentRecordOnline(agentName, normalizedRecord)) {
6765
6804
  return normalizedRecord;
6766
6805
  }
6806
+ if (!isHarnessBinaryAvailable(normalizedRecord.transport)) {
6807
+ console.warn(`[openscout-runtime] skipping warmup for ${agentName}: harness binary for ${normalizedRecord.transport} not found in PATH`);
6808
+ return normalizedRecord;
6809
+ }
6767
6810
  const projectPath = normalizedRecord.cwd;
6768
6811
  const projectName = normalizedRecord.project || basename3(projectPath);
6769
6812
  const systemPromptTemplate = normalizedRecord.systemPrompt || buildLocalAgentSystemPromptTemplate();
@@ -7235,15 +7278,26 @@ async function loadRegisteredLocalAgentBindings(nodeId, options = {}) {
7235
7278
  ]);
7236
7279
  const requestedAgentIds = new Set((options.agentIds ?? []).filter(Boolean));
7237
7280
  const selectedEntries = Object.entries(registry).filter(([agentId]) => requestedAgentIds.size === 0 || requestedAgentIds.has(agentId));
7238
- return Promise.all(selectedEntries.map(async ([agentId, record]) => {
7281
+ const results = await Promise.all(selectedEntries.map(async ([agentId, record]) => {
7239
7282
  const baseRecord = overrides[agentId]?.projectRoot ? {
7240
7283
  ...record,
7241
7284
  projectRoot: overrides[agentId].projectRoot
7242
7285
  } : record;
7243
7286
  const harnessRecord = options.harness ? recordForHarness(baseRecord, options.harness) : baseRecord;
7244
- const effectiveRecord = options.ensureOnline ? await ensureLocalAgentOnline(agentId, harnessRecord) : normalizeLocalAgentRecord(agentId, harnessRecord);
7287
+ let effectiveRecord;
7288
+ if (options.ensureOnline) {
7289
+ try {
7290
+ effectiveRecord = await ensureLocalAgentOnline(agentId, harnessRecord);
7291
+ } catch (error) {
7292
+ console.error(`[openscout-runtime] failed to warm agent ${agentId}: ${error instanceof Error ? error.message : error}`);
7293
+ effectiveRecord = normalizeLocalAgentRecord(agentId, harnessRecord);
7294
+ }
7295
+ } else {
7296
+ effectiveRecord = normalizeLocalAgentRecord(agentId, harnessRecord);
7297
+ }
7245
7298
  return buildLocalAgentBinding(agentId, effectiveRecord, isLocalAgentRecordOnline(agentId, effectiveRecord), nodeId, "relay-agent-registry");
7246
7299
  }));
7300
+ return results;
7247
7301
  }
7248
7302
  async function inferLocalAgentBinding(agentId, nodeId) {
7249
7303
  if (!agentId || BUILT_IN_LOCAL_AGENT_IDS.has(agentId)) {
@@ -37869,7 +37923,7 @@ function renderScoutHelp(version2 = "0.2.18") {
37869
37923
  init_options();
37870
37924
 
37871
37925
  // ../../apps/desktop/src/shared/product.ts
37872
- var SCOUT_APP_VERSION = "0.2.20";
37926
+ var SCOUT_APP_VERSION = "0.2.21";
37873
37927
 
37874
37928
  // ../../apps/desktop/src/cli/main.ts
37875
37929
  async function main2() {
@@ -3852,6 +3852,9 @@ function resolveClaudeStreamJsonOutput(result, fallbackParts) {
3852
3852
  function sessionKey(options) {
3853
3853
  return `${options.agentName}:${options.sessionId}`;
3854
3854
  }
3855
+ function errorMessage(error) {
3856
+ return error instanceof Error ? error.message : String(error);
3857
+ }
3855
3858
  async function readOptionalFile(filePath) {
3856
3859
  try {
3857
3860
  const raw2 = await readFile4(filePath, "utf8");
@@ -4004,17 +4007,35 @@ class ClaudeStreamJsonSession {
4004
4007
  if (this.claudeSessionId) {
4005
4008
  args.push("--resume", this.claudeSessionId);
4006
4009
  }
4007
- this.process = spawn2("claude", args, {
4008
- cwd: this.options.cwd,
4009
- env: buildManagedAgentEnvironment({
4010
- agentName: this.options.agentName,
4011
- currentDirectory: this.options.cwd,
4012
- baseEnv: process.env
4013
- })
4010
+ let child;
4011
+ try {
4012
+ child = spawn2("claude", args, {
4013
+ cwd: this.options.cwd,
4014
+ env: buildManagedAgentEnvironment({
4015
+ agentName: this.options.agentName,
4016
+ currentDirectory: this.options.cwd,
4017
+ baseEnv: process.env
4018
+ })
4019
+ });
4020
+ } catch (error) {
4021
+ throw new Error(`Failed to spawn claude: ${errorMessage(error)}`);
4022
+ }
4023
+ this.process = child;
4024
+ child.on("error", (error) => {
4025
+ console.error(`[openscout-runtime] claude process error for ${this.options.agentName}: ${error.message}`);
4026
+ this.process = null;
4027
+ if (this.activeTurn) {
4028
+ const turn = this.activeTurn;
4029
+ this.activeTurn = null;
4030
+ if (turn.timer) {
4031
+ clearTimeout(turn.timer);
4032
+ }
4033
+ turn.reject(new Error(`Claude process error: ${error.message}`));
4034
+ }
4014
4035
  });
4015
- this.process.stdout.setEncoding("utf8");
4016
- this.process.stderr.setEncoding("utf8");
4017
- this.process.stdout.on("data", (chunk) => {
4036
+ child.stdout.setEncoding("utf8");
4037
+ child.stderr.setEncoding("utf8");
4038
+ child.stdout.on("data", (chunk) => {
4018
4039
  appendFile(this.stdoutLogPath, chunk).catch(() => {
4019
4040
  return;
4020
4041
  });
@@ -4030,12 +4051,12 @@ class ClaudeStreamJsonSession {
4030
4051
  this.handleEvent(JSON.parse(trimmed));
4031
4052
  }
4032
4053
  });
4033
- this.process.stderr.on("data", (chunk) => {
4054
+ child.stderr.on("data", (chunk) => {
4034
4055
  appendFile(this.stderrLogPath, chunk).catch(() => {
4035
4056
  return;
4036
4057
  });
4037
4058
  });
4038
- this.process.on("exit", (code) => {
4059
+ child.on("exit", (code) => {
4039
4060
  if (code !== 0 && this.activeTurn) {
4040
4061
  const turn = this.activeTurn;
4041
4062
  this.activeTurn = null;
@@ -4180,7 +4201,7 @@ function parseJsonLine(line) {
4180
4201
  return null;
4181
4202
  }
4182
4203
  }
4183
- function errorMessage(error) {
4204
+ function errorMessage2(error) {
4184
4205
  if (error instanceof Error) {
4185
4206
  return error.message;
4186
4207
  }
@@ -4205,7 +4226,7 @@ async function readOptionalFile2(filePath) {
4205
4226
  }
4206
4227
  }
4207
4228
  function isMissingCodexRolloutError(error) {
4208
- const message = errorMessage(error).toLowerCase();
4229
+ const message = errorMessage2(error).toLowerCase();
4209
4230
  return message.includes("no rollout found for thread id");
4210
4231
  }
4211
4232
 
@@ -4278,7 +4299,7 @@ class CodexAppServerSession {
4278
4299
  await this.persistState();
4279
4300
  } catch (error) {
4280
4301
  this.clearActiveTurn(turn);
4281
- reject(error instanceof Error ? error : new Error(errorMessage(error)));
4302
+ reject(error instanceof Error ? error : new Error(errorMessage2(error)));
4282
4303
  }
4283
4304
  });
4284
4305
  return {
@@ -4406,7 +4427,7 @@ class CodexAppServerSession {
4406
4427
  });
4407
4428
  });
4408
4429
  child.once("error", (error) => {
4409
- this.failSession(new Error(`Codex app-server failed for ${this.options.agentName}: ${errorMessage(error)}`));
4430
+ this.failSession(new Error(`Codex app-server failed for ${this.options.agentName}: ${errorMessage2(error)}`));
4410
4431
  });
4411
4432
  child.once("exit", (code, signal) => {
4412
4433
  this.failSession(new Error(`Codex app-server exited for ${this.options.agentName}` + (code !== null ? ` with code ${code}` : "") + (signal ? ` (${signal})` : "")));
@@ -4442,7 +4463,7 @@ class CodexAppServerSession {
4442
4463
  await this.persistThreadId();
4443
4464
  return;
4444
4465
  } catch (error) {
4445
- await appendFile2(this.stderrLogPath, `[openscout] failed to resume stored Codex thread ${storedThreadId}: ${errorMessage(error)}
4466
+ await appendFile2(this.stderrLogPath, `[openscout] failed to resume stored Codex thread ${storedThreadId}: ${errorMessage2(error)}
4446
4467
  `).catch(() => {
4447
4468
  return;
4448
4469
  });
@@ -4712,7 +4733,7 @@ class CodexAppServerSession {
4712
4733
  });
4713
4734
  } catch (error) {
4714
4735
  this.pendingRequests.delete(id);
4715
- reject(error instanceof Error ? error : new Error(errorMessage(error)));
4736
+ reject(error instanceof Error ? error : new Error(errorMessage2(error)));
4716
4737
  }
4717
4738
  });
4718
4739
  }
@@ -5955,11 +5976,33 @@ function killAgentSession(sessionName) {
5955
5976
  execSync(`tmux kill-session -t ${JSON.stringify(sessionName)}`, { stdio: "pipe" });
5956
5977
  } catch {}
5957
5978
  }
5979
+ function isHarnessBinaryAvailable(transport) {
5980
+ const binaryMap = {
5981
+ claude_stream_json: "claude",
5982
+ codex_app_server: "codex"
5983
+ };
5984
+ const binary = binaryMap[transport];
5985
+ if (!binary)
5986
+ return true;
5987
+ try {
5988
+ execFileSync3("sh", ["-lc", `command -v ${binary}`], {
5989
+ stdio: ["ignore", "pipe", "ignore"],
5990
+ encoding: "utf8"
5991
+ });
5992
+ return true;
5993
+ } catch {
5994
+ return false;
5995
+ }
5996
+ }
5958
5997
  async function ensureLocalAgentOnline(agentName, record) {
5959
5998
  const normalizedRecord = normalizeLocalAgentRecord(agentName, record);
5960
5999
  if (isLocalAgentRecordOnline(agentName, normalizedRecord)) {
5961
6000
  return normalizedRecord;
5962
6001
  }
6002
+ if (!isHarnessBinaryAvailable(normalizedRecord.transport)) {
6003
+ console.warn(`[openscout-runtime] skipping warmup for ${agentName}: harness binary for ${normalizedRecord.transport} not found in PATH`);
6004
+ return normalizedRecord;
6005
+ }
5963
6006
  const projectPath = normalizedRecord.cwd;
5964
6007
  const projectName = normalizedRecord.project || basename4(projectPath);
5965
6008
  const systemPromptTemplate = normalizedRecord.systemPrompt || buildLocalAgentSystemPromptTemplate();
@@ -6350,15 +6393,26 @@ async function loadRegisteredLocalAgentBindings(nodeId, options = {}) {
6350
6393
  ]);
6351
6394
  const requestedAgentIds = new Set((options.agentIds ?? []).filter(Boolean));
6352
6395
  const selectedEntries = Object.entries(registry).filter(([agentId]) => requestedAgentIds.size === 0 || requestedAgentIds.has(agentId));
6353
- return Promise.all(selectedEntries.map(async ([agentId, record]) => {
6396
+ const results = await Promise.all(selectedEntries.map(async ([agentId, record]) => {
6354
6397
  const baseRecord = overrides[agentId]?.projectRoot ? {
6355
6398
  ...record,
6356
6399
  projectRoot: overrides[agentId].projectRoot
6357
6400
  } : record;
6358
6401
  const harnessRecord = options.harness ? recordForHarness(baseRecord, options.harness) : baseRecord;
6359
- const effectiveRecord = options.ensureOnline ? await ensureLocalAgentOnline(agentId, harnessRecord) : normalizeLocalAgentRecord(agentId, harnessRecord);
6402
+ let effectiveRecord;
6403
+ if (options.ensureOnline) {
6404
+ try {
6405
+ effectiveRecord = await ensureLocalAgentOnline(agentId, harnessRecord);
6406
+ } catch (error) {
6407
+ console.error(`[openscout-runtime] failed to warm agent ${agentId}: ${error instanceof Error ? error.message : error}`);
6408
+ effectiveRecord = normalizeLocalAgentRecord(agentId, harnessRecord);
6409
+ }
6410
+ } else {
6411
+ effectiveRecord = normalizeLocalAgentRecord(agentId, harnessRecord);
6412
+ }
6360
6413
  return buildLocalAgentBinding(agentId, effectiveRecord, isLocalAgentRecordOnline(agentId, effectiveRecord), nodeId, "relay-agent-registry");
6361
6414
  }));
6415
+ return results;
6362
6416
  }
6363
6417
  async function inferLocalAgentBinding(agentId, nodeId) {
6364
6418
  if (!agentId || BUILT_IN_LOCAL_AGENT_IDS.has(agentId)) {
@@ -7970,7 +8024,7 @@ init_setup();
7970
8024
 
7971
8025
  // apps/desktop/src/shared/product.ts
7972
8026
  var SCOUT_PRODUCT_NAME = "Scout";
7973
- var SCOUT_APP_VERSION = "0.2.20";
8027
+ var SCOUT_APP_VERSION = "0.2.21";
7974
8028
 
7975
8029
  // apps/desktop/src/shared/surface-capabilities.ts
7976
8030
  function resolveScoutSurfaceCapabilities(surface) {
@@ -13846,9 +13900,9 @@ async function revealScoutElectronPath(filePath, host = {}) {
13846
13900
  if (!host.openPath) {
13847
13901
  throw new Error("Scout path opening is unavailable.");
13848
13902
  }
13849
- const errorMessage2 = await host.openPath(targetPath);
13850
- if (errorMessage2) {
13851
- throw new Error(errorMessage2);
13903
+ const errorMessage3 = await host.openPath(targetPath);
13904
+ if (errorMessage3) {
13905
+ throw new Error(errorMessage3);
13852
13906
  }
13853
13907
  return true;
13854
13908
  }
@@ -14414,9 +14468,9 @@ async function openScoutElectronAgentSession(agentId, host = {}) {
14414
14468
  if (!host.openPath) {
14415
14469
  throw new Error("Scout agent-session path opening is unavailable.");
14416
14470
  }
14417
- const errorMessage2 = await host.openPath(targetPath);
14418
- if (errorMessage2) {
14419
- throw new Error(errorMessage2);
14471
+ const errorMessage3 = await host.openPath(targetPath);
14472
+ if (errorMessage3) {
14473
+ throw new Error(errorMessage3);
14420
14474
  }
14421
14475
  return true;
14422
14476
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openscout/scout",
3
- "version": "0.2.20",
3
+ "version": "0.2.21",
4
4
  "description": "Published Scout package that installs the `scout` command",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -22,6 +22,6 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@openscout/runtime": "0.2.20"
25
+ "@openscout/runtime": "workspace:*"
26
26
  }
27
27
  }