bopodev-agent-sdk 0.1.19 → 0.1.22

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.
@@ -1,5 +1,5 @@
1
1
 
2
2
  
3
- > bopodev-agent-sdk@0.1.19 build /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopohq/packages/agent-sdk
3
+ > bopodev-agent-sdk@0.1.22 build /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopodev/packages/agent-sdk
4
4
  > tsc -p tsconfig.json --emitDeclarationOnly
5
5
 
@@ -1,4 +1,4 @@
1
1
 
2
- > bopodev-agent-sdk@0.1.15 typecheck /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopohq/packages/agent-sdk
2
+ > bopodev-agent-sdk@0.1.20 typecheck /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopodev/packages/agent-sdk
3
3
  > tsc -p tsconfig.json --noEmit
4
4
 
@@ -1701,6 +1701,26 @@ export declare const AgentCreateRequestSchema: z.ZodObject<{
1701
1701
  heartbeatCron: z.ZodString;
1702
1702
  monthlyBudgetUsd: z.ZodNumber;
1703
1703
  canHireAgents: z.ZodDefault<z.ZodBoolean>;
1704
+ sourceIssueId: z.ZodOptional<z.ZodString>;
1705
+ sourceIssueIds: z.ZodDefault<z.ZodArray<z.ZodString>>;
1706
+ delegationIntent: z.ZodOptional<z.ZodObject<{
1707
+ intentType: z.ZodLiteral<"agent_hiring_request">;
1708
+ requestedRole: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1709
+ requestedName: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1710
+ requestedManagerAgentId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1711
+ requestedProviderType: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
1712
+ claude_code: "claude_code";
1713
+ codex: "codex";
1714
+ cursor: "cursor";
1715
+ opencode: "opencode";
1716
+ gemini_cli: "gemini_cli";
1717
+ openai_api: "openai_api";
1718
+ anthropic_api: "anthropic_api";
1719
+ http: "http";
1720
+ shell: "shell";
1721
+ }>>>;
1722
+ requestedRuntimeModel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
1723
+ }, z.core.$strip>>;
1704
1724
  requestApproval: z.ZodDefault<z.ZodBoolean>;
1705
1725
  runtimeConfig: z.ZodDefault<z.ZodObject<{
1706
1726
  runtimeCommand: z.ZodOptional<z.ZodOptional<z.ZodString>>;
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "bopodev-agent-sdk",
3
- "version": "0.1.19",
3
+ "version": "0.1.22",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
7
  "types": "src/index.ts",
8
8
  "dependencies": {
9
- "bopodev-contracts": "0.1.19"
9
+ "bopodev-contracts": "0.1.22"
10
10
  },
11
11
  "scripts": {
12
12
  "build": "tsc -p tsconfig.json --emitDeclarationOnly",
package/src/adapters.ts CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  type DirectApiProvider
26
26
  } from "./runtime-http";
27
27
  import { homedir } from "node:os";
28
- import { join, resolve } from "node:path";
28
+ import { basename, join, resolve } from "node:path";
29
29
 
30
30
  function summarizeWork(context: HeartbeatContext) {
31
31
  if (context.workItems.length === 0) {
@@ -660,6 +660,21 @@ export async function testAdapterEnvironment(
660
660
  level: "info",
661
661
  message: `Command is executable: ${command}`
662
662
  });
663
+ const providerMismatch = detectProviderCommandMismatch(providerType, command);
664
+ if (providerMismatch) {
665
+ checks.push({
666
+ code: "command_provider_mismatch",
667
+ level: "error",
668
+ message: `Command '${command}' does not match selected provider '${providerType}'.`,
669
+ detail: `The command appears to be for provider '${providerMismatch}'. Select the matching provider or change the command.`
670
+ });
671
+ return {
672
+ providerType,
673
+ status: "fail",
674
+ testedAt: new Date().toISOString(),
675
+ checks
676
+ };
677
+ }
663
678
 
664
679
  if (providerType === "http") {
665
680
  return { providerType, status: "pass", testedAt: new Date().toISOString(), checks };
@@ -687,12 +702,12 @@ export async function testAdapterEnvironment(
687
702
  message: "Environment probe succeeded."
688
703
  });
689
704
  } else {
690
- const detail = `${probe.stderr}\n${probe.stdout}`.trim().slice(0, 500);
691
- const normalizedDetail = detail.toLowerCase();
705
+ const detail = summarizeProbeFailureDetail(probe.stdout, probe.stderr);
706
+ const rawEvidence = `${probe.stderr}\n${probe.stdout}`.toLowerCase();
692
707
  if (
693
708
  providerType === "codex" &&
694
- normalizedDetail.includes("401 unauthorized") &&
695
- (normalizedDetail.includes("missing bearer") || normalizedDetail.includes("authentication"))
709
+ rawEvidence.includes("401 unauthorized") &&
710
+ (rawEvidence.includes("missing bearer") || rawEvidence.includes("authentication"))
696
711
  ) {
697
712
  checks.push({
698
713
  code: "codex_auth_required",
@@ -724,6 +739,77 @@ export async function testAdapterEnvironment(
724
739
  };
725
740
  }
726
741
 
742
+ function detectProviderCommandMismatch(providerType: AgentProviderType, command: string) {
743
+ const normalized = basename(command).toLowerCase();
744
+ const known: Record<Exclude<AgentProviderType, "http" | "shell" | "openai_api" | "anthropic_api">, string[]> = {
745
+ claude_code: ["claude", "claude.exe", "claude.cmd"],
746
+ codex: ["codex", "codex.exe", "codex.cmd"],
747
+ cursor: ["cursor", "cursor.exe", "cursor.cmd"],
748
+ opencode: ["opencode", "opencode.exe", "opencode.cmd"],
749
+ gemini_cli: ["gemini", "gemini.exe", "gemini.cmd"]
750
+ };
751
+ const expected = known[providerType as keyof typeof known];
752
+ if (!expected) {
753
+ return null;
754
+ }
755
+ if (expected.includes(normalized)) {
756
+ return null;
757
+ }
758
+ for (const [candidateProvider, aliases] of Object.entries(known)) {
759
+ if (candidateProvider === providerType) {
760
+ continue;
761
+ }
762
+ if (aliases.includes(normalized)) {
763
+ return candidateProvider;
764
+ }
765
+ }
766
+ return null;
767
+ }
768
+
769
+ function summarizeProbeFailureDetail(stdout: string, stderr: string) {
770
+ const lines = [...stderr.split(/\r?\n/), ...stdout.split(/\r?\n/)].map((line) => line.trim()).filter(Boolean);
771
+ for (const line of lines) {
772
+ const parsed = parseJsonRecord(line);
773
+ if (!parsed) {
774
+ return line.replace(/\s+/g, " ").slice(0, 500);
775
+ }
776
+ const type = asString(parsed.type);
777
+ const subtype = asString(parsed.subtype);
778
+ if (
779
+ type === "thread.started" ||
780
+ type === "item.started" ||
781
+ type === "item.completed" ||
782
+ (type === "system" && subtype === "init")
783
+ ) {
784
+ continue;
785
+ }
786
+ if (type === "turn.failed") {
787
+ const failed = asString(parsed.error) || asString(parsed.message) || asString(parsed.result);
788
+ if (failed) {
789
+ return failed.replace(/\s+/g, " ").slice(0, 500);
790
+ }
791
+ }
792
+ const message = asString(parsed.message) || asString(parsed.result) || asString(parsed.error);
793
+ if (message) {
794
+ return message.replace(/\s+/g, " ").slice(0, 500);
795
+ }
796
+ }
797
+ return "";
798
+ }
799
+
800
+ function asString(value: unknown) {
801
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
802
+ }
803
+
804
+ function parseJsonRecord(line: string) {
805
+ try {
806
+ const parsed = JSON.parse(line) as unknown;
807
+ return typeof parsed === "object" && parsed !== null ? (parsed as Record<string, unknown>) : null;
808
+ } catch {
809
+ return null;
810
+ }
811
+ }
812
+
727
813
  export function createSkippedResult(providerLabel: string, providerKey: string, context: HeartbeatContext): AdapterExecutionResult {
728
814
  return {
729
815
  status: "skipped",
package/src/runtime.ts CHANGED
@@ -1403,6 +1403,7 @@ async function ensureCodexSkillsInjected(skillsSourceDir: string, env: NodeJS.Pr
1403
1403
  const codexHome = resolveCodexHome(env);
1404
1404
  const targetRoot = join(codexHome, SKILLS_DIR_NAME);
1405
1405
  await mkdir(targetRoot, { recursive: true });
1406
+ await pruneBrokenSkillSymlinks(targetRoot);
1406
1407
 
1407
1408
  const entries = await readdir(skillsSourceDir, { withFileTypes: true });
1408
1409
  for (const entry of entries) {
@@ -1505,6 +1506,7 @@ async function withProviderRuntimeIsolation(
1505
1506
 
1506
1507
  async function ensureSkillsInjectedAtHome(skillsSourceDir: string, targetRoot: string) {
1507
1508
  await mkdir(targetRoot, { recursive: true });
1509
+ await pruneBrokenSkillSymlinks(targetRoot);
1508
1510
  const entries = await readdir(skillsSourceDir, { withFileTypes: true });
1509
1511
  for (const entry of entries) {
1510
1512
  if (!entry.isDirectory()) continue;
@@ -1517,6 +1519,27 @@ async function ensureSkillsInjectedAtHome(skillsSourceDir: string, targetRoot: s
1517
1519
  }
1518
1520
  }
1519
1521
 
1522
+ async function pruneBrokenSkillSymlinks(targetRoot: string) {
1523
+ try {
1524
+ const entries = await readdir(targetRoot, { withFileTypes: true });
1525
+ for (const entry of entries) {
1526
+ const target = join(targetRoot, entry.name);
1527
+ const stats = await lstat(target).catch(() => null);
1528
+ if (!stats?.isSymbolicLink()) {
1529
+ continue;
1530
+ }
1531
+ const resolves = await access(target)
1532
+ .then(() => true)
1533
+ .catch(() => false);
1534
+ if (!resolves) {
1535
+ await rm(target, { recursive: true, force: true }).catch(() => undefined);
1536
+ }
1537
+ }
1538
+ } catch {
1539
+ return;
1540
+ }
1541
+ }
1542
+
1520
1543
  function resolveManagedCodexHome(env: NodeJS.ProcessEnv) {
1521
1544
  const managedRoot = resolveManagedCodexHomeRoot(env);
1522
1545
  const companyId = sanitizePathSegment(resolveControlPlaneEnvValue(env, "COMPANY_ID"));
@@ -1,4 +0,0 @@
1
-
2
- > bopodev-agent-sdk@0.1.15 lint /Users/danielkrusenstrahle/Documents/Projects/Monorepo/bopohq/packages/agent-sdk
3
- > tsc -p tsconfig.json --noEmit
4
-