@amistio/cli 0.1.36 → 0.1.37

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.
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { createHash as createHash8, randomUUID } from "node:crypto";
5
- import { writeFile as writeFile10 } from "node:fs/promises";
4
+ import { createHash as createHash9, randomUUID as randomUUID2 } from "node:crypto";
5
+ import { writeFile as writeFile11 } from "node:fs/promises";
6
6
  import os8 from "node:os";
7
- import path16 from "node:path";
7
+ import path17 from "node:path";
8
8
  import { Command } from "commander";
9
9
 
10
10
  // ../shared/src/schemas.ts
@@ -484,6 +484,33 @@ var runnerProviderConfigSchema = z.object({
484
484
  models: z.record(runnerProviderModelIdSchema, runnerProviderModelSchema).default({})
485
485
  }).strict();
486
486
  var runnerProviderCatalogSchema = z.record(runnerProviderIdSchema, runnerProviderConfigSchema);
487
+ var runnerProviderClientIdSchema = z.string().trim().min(1).max(120);
488
+ var runnerProviderRouteTypeSchema = z.enum(["directProvider", "agentClient", "localRuntime"]);
489
+ var runnerProviderAuthStateSchema = z.enum(["notConfigured", "pendingUserAction", "authenticated", "expired", "revoked", "mismatch", "unavailable", "unsupported", "unknown"]);
490
+ var runnerProviderAuthStatusSchema = z.object({
491
+ providerId: runnerProviderIdSchema,
492
+ providerClientId: runnerProviderClientIdSchema,
493
+ routeType: runnerProviderRouteTypeSchema,
494
+ status: runnerProviderAuthStateSchema,
495
+ authMethodLabel: z.string().trim().min(1).max(120).optional(),
496
+ accountHost: z.string().trim().min(1).max(200).optional(),
497
+ accountLogin: z.string().trim().min(1).max(120).optional(),
498
+ accountFingerprint: z.string().trim().min(1).max(160).optional(),
499
+ modelCount: z.number().int().nonnegative().optional(),
500
+ checkedAt: isoDateTimeSchema,
501
+ expiresAt: isoDateTimeSchema.optional(),
502
+ message: z.string().max(400).optional(),
503
+ errorCode: z.string().trim().min(1).max(80).optional()
504
+ }).strict();
505
+ var runnerProviderAuthLinkRequestSchema = z.object({
506
+ providerId: runnerProviderIdSchema,
507
+ providerClientId: runnerProviderClientIdSchema,
508
+ routeType: runnerProviderRouteTypeSchema,
509
+ requestedAccountHost: z.string().trim().min(1).max(200).optional(),
510
+ requestedAccountLogin: z.string().trim().min(1).max(120).optional(),
511
+ requestedAccountFingerprint: z.string().trim().min(1).max(160).optional(),
512
+ intentExpiresAt: isoDateTimeSchema.optional()
513
+ }).strict();
487
514
  var runnerToolModelPreferenceSchema = z.object({
488
515
  tool: runnerToolSelectionSchema.optional(),
489
516
  invocationChannel: runnerInvocationChannelSchema.optional(),
@@ -855,6 +882,7 @@ var runnerHeartbeatItemSchema = baseItemSchema.extend({
855
882
  autoSyncPushedCount: z.number().int().nonnegative().optional(),
856
883
  autoSyncSkippedCount: z.number().int().nonnegative().optional(),
857
884
  autoSyncConflictCount: z.number().int().nonnegative().optional(),
885
+ providerAuthStatuses: z.array(runnerProviderAuthStatusSchema).max(20).optional(),
858
886
  lastSeenAt: isoDateTimeSchema
859
887
  });
860
888
  var runnerSettingsItemSchema = baseItemSchema.extend({
@@ -918,7 +946,7 @@ var runnerCredentialItemSchema = baseItemSchema.extend({
918
946
  lastUsedAt: isoDateTimeSchema.optional(),
919
947
  status: z.enum(["active", "revoked"]).default("active")
920
948
  });
921
- var runnerCommandKindSchema = z.enum(["update", "restart", "remove", "implementationHandoffRecovery"]);
949
+ var runnerCommandKindSchema = z.enum(["update", "restart", "remove", "implementationHandoffRecovery", "providerAuthLinkRequested"]);
922
950
  var runnerCommandStatusSchema = z.enum(["pending", "acknowledged", "running", "completed", "failed", "expired", "cancelled"]);
923
951
  var runnerCommandItemSchema = baseItemSchema.extend({
924
952
  type: z.literal("runnerCommand"),
@@ -930,6 +958,8 @@ var runnerCommandItemSchema = baseItemSchema.extend({
930
958
  repositoryLinkId: z.string().min(1),
931
959
  workItemId: z.string().min(1).optional(),
932
960
  handoffRecoveryAction: implementationHandoffRecoveryActionSchema.optional(),
961
+ providerAuthLinkRequest: runnerProviderAuthLinkRequestSchema.optional(),
962
+ providerAuthStatus: runnerProviderAuthStatusSchema.optional(),
933
963
  executionBranch: z.string().min(1).optional(),
934
964
  executionWorktreeKey: z.string().min(1).optional(),
935
965
  requestedByUserId: z.string().min(1),
@@ -3112,8 +3142,8 @@ var toolSessionMutationSchema = z3.object({
3112
3142
  });
3113
3143
  function resolveApiUrl(apiUrl, urlPath) {
3114
3144
  const base = apiUrl.endsWith("/") ? apiUrl.slice(0, -1) : apiUrl;
3115
- const path17 = urlPath.startsWith("/") ? urlPath : `/${urlPath}`;
3116
- return new URL(`${base}${path17}`);
3145
+ const path18 = urlPath.startsWith("/") ? urlPath : `/${urlPath}`;
3146
+ return new URL(`${base}${path18}`);
3117
3147
  }
3118
3148
 
3119
3149
  // src/orchestrator.ts
@@ -3522,13 +3552,887 @@ function truncateLocalError(error) {
3522
3552
  }
3523
3553
 
3524
3554
  // src/local-tool-runner.ts
3525
- import { spawn } from "node:child_process";
3526
- import { mkdtemp, readFile as readFile4, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
3555
+ import { mkdtemp, readFile as readFile5, rm as rm2, writeFile as writeFile6 } from "node:fs/promises";
3527
3556
  import os3 from "node:os";
3557
+ import path7 from "node:path";
3558
+
3559
+ // src/bounded-tool-adapter.ts
3560
+ import { constants } from "node:fs";
3561
+ import { access, mkdir as mkdir6, readdir as readdir3, readFile as readFile4, stat as stat3, writeFile as writeFile5 } from "node:fs/promises";
3528
3562
  import path6 from "node:path";
3563
+
3564
+ // src/host-execution.ts
3565
+ import { spawn } from "node:child_process";
3566
+ import { randomUUID } from "node:crypto";
3567
+ var hostExecutionProtocolVersion = "amistio.hostExecution.v1";
3568
+ var nativeHostHelperEnvironmentAllowlist = [
3569
+ "PATH",
3570
+ "Path",
3571
+ "HOME",
3572
+ "USERPROFILE",
3573
+ "TMPDIR",
3574
+ "TEMP",
3575
+ "TMP",
3576
+ "LANG",
3577
+ "LC_ALL",
3578
+ "CI",
3579
+ "NODE_ENV",
3580
+ "SystemRoot",
3581
+ "WINDIR",
3582
+ "ComSpec",
3583
+ "PATHEXT"
3584
+ ];
3585
+ var defaultNativeHostOutputBudgetBytes = 64 * 1024;
3586
+ var nodeHostExecutionCapabilities = {
3587
+ protocolVersion: hostExecutionProtocolVersion,
3588
+ implementation: "node",
3589
+ processGroups: process.platform === "win32" ? { supported: false, reason: "Windows process-group cleanup needs a native helper or platform-specific implementation." } : { supported: true },
3590
+ signalEscalation: { supported: true },
3591
+ streamCapture: { supported: true },
3592
+ pty: { supported: false, reason: "PTY bridging is reserved for a future native helper." },
3593
+ sandbox: { supported: false, reason: "Sandboxed command execution is reserved for a future native helper." },
3594
+ outputEncoding: "utf8"
3595
+ };
3596
+ var defaultHostExecution = {
3597
+ capabilities: nodeHostExecutionCapabilities,
3598
+ executeCommand: executeNodeCommand,
3599
+ commandExists
3600
+ };
3601
+ var runtimeHostExecutionCacheKey;
3602
+ var runtimeHostExecutionPortPromise;
3603
+ function getRuntimeHostExecutionPort(env = process.env) {
3604
+ const helperConfig = nativeHostHelperConfigFromEnvironment(env);
3605
+ const cacheKey = helperConfig?.command ?? "";
3606
+ if (!runtimeHostExecutionPortPromise || runtimeHostExecutionCacheKey !== cacheKey) {
3607
+ runtimeHostExecutionCacheKey = cacheKey;
3608
+ runtimeHostExecutionPortPromise = createHostExecutionPort(helperConfig ? { nativeHelper: helperConfig } : {});
3609
+ }
3610
+ return runtimeHostExecutionPortPromise;
3611
+ }
3612
+ async function createHostExecutionPort(options = {}) {
3613
+ const fallback = options.fallback ?? defaultHostExecution;
3614
+ const helperConfig = options.nativeHelper ?? nativeHostHelperConfigFromEnvironment();
3615
+ if (!helperConfig) {
3616
+ return fallback;
3617
+ }
3618
+ const discovery = await discoverNativeHostHelper(helperConfig);
3619
+ return createCompositeHostExecutionPort(fallback, discovery);
3620
+ }
3621
+ function nativeHostHelperConfigFromEnvironment(env = process.env) {
3622
+ const command = env.AMISTIO_HOST_HELPER_PATH?.trim();
3623
+ if (!command) {
3624
+ return void 0;
3625
+ }
3626
+ return { command };
3627
+ }
3628
+ async function discoverNativeHostHelper(config) {
3629
+ const normalizedConfig = normalizeNativeHostHelperConfig(config);
3630
+ const result = await executeHelperProcess(normalizedConfig, "--amistio-host-helper-handshake", void 0, normalizedConfig.handshakeTimeoutMs);
3631
+ if (result.status === "spawnFailed") {
3632
+ return { ok: false, failure: { code: "helper_unavailable", message: result.error?.message ?? "Native host helper is unavailable.", stdout: result.stdout, stderr: result.stderr } };
3633
+ }
3634
+ if (result.status === "timedOut") {
3635
+ return { ok: false, failure: { code: "helper_unavailable", message: result.error?.message ?? "Native host helper handshake timed out.", stdout: result.stdout, stderr: result.stderr } };
3636
+ }
3637
+ if (result.exitCode !== 0) {
3638
+ return { ok: false, failure: { code: "helper_failed", message: `Native host helper handshake exited with code ${result.exitCode}.`, stdout: result.stdout, stderr: result.stderr } };
3639
+ }
3640
+ const parsed = parseJson(result.stdout);
3641
+ if (!isNativeHostHelperHandshake(parsed)) {
3642
+ return { ok: false, failure: { code: "protocol_mismatch", message: "Native host helper handshake did not match the Amistio host execution protocol.", stdout: result.stdout, stderr: result.stderr } };
3643
+ }
3644
+ return { ok: true, helper: { config: normalizedConfig, handshake: parsed } };
3645
+ }
3646
+ function createNativeHostCommandRequestEnvelope(request, requestId = randomUUID()) {
3647
+ const env = filterNativeHostHelperEnvironment(request.env ?? process.env);
3648
+ const sandbox = request.sandbox ?? "none";
3649
+ return {
3650
+ protocolVersion: hostExecutionProtocolVersion,
3651
+ requestId,
3652
+ command: request.command,
3653
+ args: [...request.args],
3654
+ cwd: request.cwd,
3655
+ shell: request.shell ?? false,
3656
+ timeoutMs: request.timeoutMs ?? 0,
3657
+ stdin: request.stdin ? "provided" : "none",
3658
+ env,
3659
+ envAllowlist: Object.keys(env).sort(),
3660
+ streamOutput: request.streamOutput ?? false,
3661
+ requirePty: request.requirePty ?? false,
3662
+ sandbox,
3663
+ sandboxPolicy: nativeHostSandboxPolicy(request.cwd, sandbox),
3664
+ outputBudgetBytes: positiveInteger(request.outputBudgetBytes) ?? defaultNativeHostOutputBudgetBytes
3665
+ };
3666
+ }
3667
+ function createCompositeHostExecutionPort(fallback, discovery) {
3668
+ return {
3669
+ capabilities: discovery.ok ? discovery.helper.handshake.capabilities : fallback.capabilities,
3670
+ async executeCommand(request) {
3671
+ if (!discovery.ok) {
3672
+ if (request.requireNativeHelper) {
3673
+ return helperDiscoveryFailureResult(discovery.failure);
3674
+ }
3675
+ return fallback.executeCommand(request);
3676
+ }
3677
+ const helper = discovery.helper;
3678
+ const capabilityFailure = helperCapabilityFailure(request, helper.handshake.capabilities);
3679
+ if (capabilityFailure) {
3680
+ return capabilityFailure;
3681
+ }
3682
+ if (!canDelegateToNativeHelper(request)) {
3683
+ if (request.requireNativeHelper) {
3684
+ return unsupportedResult("helper_failed", "Native host helper delegation is unavailable for requests with stdin payloads.");
3685
+ }
3686
+ return fallback.executeCommand(request);
3687
+ }
3688
+ return executeNativeHelperCommand(helper, request);
3689
+ },
3690
+ commandExists: fallback.commandExists
3691
+ };
3692
+ }
3693
+ function helperCapabilityFailure(request, capabilities) {
3694
+ if (request.requirePty && !capabilities.pty.supported) {
3695
+ return unsupportedResult("pty_unsupported", capabilities.pty.reason ?? "PTY command execution is not supported by the native host helper.");
3696
+ }
3697
+ if (request.sandbox && request.sandbox !== "none" && !capabilities.sandbox.supported) {
3698
+ return unsupportedResult("sandbox_unsupported", capabilities.sandbox.reason ?? `${request.sandbox} sandbox execution is not supported by the native host helper.`);
3699
+ }
3700
+ return void 0;
3701
+ }
3702
+ function canDelegateToNativeHelper(request) {
3703
+ return !request.stdin && !request.shell;
3704
+ }
3705
+ async function executeNativeHelperCommand(helper, request) {
3706
+ const requestId = randomUUID();
3707
+ const envelope = createNativeHostCommandRequestEnvelope(request, requestId);
3708
+ const result = await executeHelperProcess(helper.config, "--amistio-host-helper-execute", JSON.stringify(envelope), request.timeoutMs ?? helper.config.executeTimeoutMs);
3709
+ if (result.status === "spawnFailed") {
3710
+ return { status: "helperUnavailable", exitCode: 1, stdout: result.stdout, stderr: result.stderr, error: { code: "helper_unavailable", message: result.error?.message ?? "Native host helper is unavailable." } };
3711
+ }
3712
+ if (result.status === "timedOut") {
3713
+ return result;
3714
+ }
3715
+ if (result.exitCode !== 0) {
3716
+ return { status: "failed", exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr, error: { code: "helper_failed", message: `Native host helper exited with code ${result.exitCode}.` } };
3717
+ }
3718
+ const parsed = parseJson(result.stdout);
3719
+ if (!isNativeHostCommandResultEnvelope(parsed) || parsed.requestId !== requestId) {
3720
+ return { status: "protocolMismatch", exitCode: 1, stdout: result.stdout, stderr: result.stderr, error: { code: "protocol_mismatch", message: "Native host helper command result did not match the Amistio host execution protocol." } };
3721
+ }
3722
+ return normalizeHostCommandResult(request, {
3723
+ status: parsed.status,
3724
+ exitCode: parsed.exitCode,
3725
+ stdout: parsed.stdout,
3726
+ stderr: parsed.stderr,
3727
+ ...parsed.error ? { error: parsed.error } : {}
3728
+ });
3729
+ }
3730
+ async function executeNodeCommand(request) {
3731
+ if (request.requirePty) {
3732
+ return unsupportedResult("pty_unsupported", "PTY command execution is not supported by the default Node host execution port.");
3733
+ }
3734
+ if (request.sandbox && request.sandbox !== "none") {
3735
+ return unsupportedResult("sandbox_unsupported", `${request.sandbox} sandbox execution is not supported by the default Node host execution port.`);
3736
+ }
3737
+ return new Promise((resolve) => {
3738
+ let child;
3739
+ try {
3740
+ child = spawn(request.command, [...request.args], {
3741
+ cwd: request.cwd,
3742
+ env: request.env ?? process.env,
3743
+ detached: process.platform !== "win32",
3744
+ shell: request.shell ?? false,
3745
+ stdio: ["pipe", "pipe", "pipe"]
3746
+ });
3747
+ } catch (error) {
3748
+ resolve(spawnFailedResult(error));
3749
+ return;
3750
+ }
3751
+ let stdout = "";
3752
+ let stderr = "";
3753
+ let settled = false;
3754
+ let forceKillTimer;
3755
+ let timedOutError;
3756
+ const timeout = request.timeoutMs && request.timeoutMs > 0 ? setTimeout(() => {
3757
+ if (settled) return;
3758
+ const message = request.timeoutMessage ?? `Command timed out after ${request.timeoutMs}ms: ${request.command}`;
3759
+ stderr += `${message}
3760
+ `;
3761
+ timedOutError = { code: "timeout", message };
3762
+ killProcessTree(child, "SIGTERM");
3763
+ forceKillTimer = setTimeout(() => killProcessTree(child, "SIGKILL"), request.killGraceMs ?? 5e3);
3764
+ forceKillTimer.unref?.();
3765
+ }, request.timeoutMs) : void 0;
3766
+ timeout?.unref?.();
3767
+ const resolveOnce = (result) => {
3768
+ if (settled) return;
3769
+ settled = true;
3770
+ if (timeout) clearTimeout(timeout);
3771
+ if (forceKillTimer) clearTimeout(forceKillTimer);
3772
+ resolve(result);
3773
+ };
3774
+ child.on("error", (error) => {
3775
+ if (timedOutError) return;
3776
+ resolveOnce(spawnFailedResult(error, stdout, stderr));
3777
+ });
3778
+ child.stdout.setEncoding("utf8");
3779
+ child.stderr.setEncoding("utf8");
3780
+ child.stdout.on("data", (chunk) => {
3781
+ stdout += chunk;
3782
+ if (request.streamOutput) process.stdout.write(chunk);
3783
+ });
3784
+ child.stderr.on("data", (chunk) => {
3785
+ stderr += chunk;
3786
+ if (request.streamOutput) process.stderr.write(chunk);
3787
+ });
3788
+ child.stdin.on("error", () => void 0);
3789
+ if (request.stdin) {
3790
+ child.stdin.write(request.stdin);
3791
+ }
3792
+ child.stdin.end();
3793
+ child.on("close", (exitCode) => {
3794
+ if (forceKillTimer) clearTimeout(forceKillTimer);
3795
+ if (timedOutError) {
3796
+ resolveOnce({ status: "timedOut", exitCode: 1, stdout, stderr, error: timedOutError });
3797
+ return;
3798
+ }
3799
+ resolveOnce({ status: "completed", exitCode: exitCode ?? 1, stdout, stderr });
3800
+ });
3801
+ });
3802
+ }
3803
+ function unsupportedResult(code, message) {
3804
+ return { status: "unsupported", exitCode: 1, stdout: "", stderr: "", error: { code, message } };
3805
+ }
3806
+ function helperDiscoveryFailureResult(failure) {
3807
+ const status = failure.code === "protocol_mismatch" ? "protocolMismatch" : failure.code === "helper_unavailable" ? "helperUnavailable" : "failed";
3808
+ return { status, exitCode: 1, stdout: failure.stdout ?? "", stderr: failure.stderr ?? "", error: { code: failure.code, message: failure.message } };
3809
+ }
3810
+ function spawnFailedResult(error, stdout = "", stderr = "") {
3811
+ return { status: "spawnFailed", exitCode: 1, stdout, stderr, error: { code: "spawn_failed", message: errorMessage(error) } };
3812
+ }
3813
+ function killProcessTree(child, signal) {
3814
+ if (child.pid && process.platform !== "win32") {
3815
+ try {
3816
+ process.kill(-child.pid, signal);
3817
+ return;
3818
+ } catch {
3819
+ }
3820
+ }
3821
+ child.kill(signal);
3822
+ }
3823
+ async function commandExists(command) {
3824
+ const lookupCommand = process.platform === "win32" ? "where" : "which";
3825
+ return new Promise((resolve) => {
3826
+ const lookup = spawn(lookupCommand, [command], { stdio: "ignore" });
3827
+ lookup.on("error", () => resolve(false));
3828
+ lookup.on("close", (exitCode) => resolve(exitCode === 0));
3829
+ });
3830
+ }
3831
+ function normalizeNativeHostHelperConfig(config) {
3832
+ return {
3833
+ command: config.command,
3834
+ args: config.args ? [...config.args] : [],
3835
+ cwd: config.cwd ?? process.cwd(),
3836
+ env: filterNativeHostHelperEnvironment(config.env ?? process.env),
3837
+ handshakeTimeoutMs: positiveInteger(config.handshakeTimeoutMs) ?? 2e3,
3838
+ executeTimeoutMs: positiveInteger(config.executeTimeoutMs) ?? 3e4
3839
+ };
3840
+ }
3841
+ function executeHelperProcess(config, mode, stdin, timeoutMs) {
3842
+ return new Promise((resolve) => {
3843
+ let child;
3844
+ try {
3845
+ child = spawn(config.command, [...config.args, mode], {
3846
+ cwd: config.cwd,
3847
+ env: config.env,
3848
+ shell: false,
3849
+ stdio: ["pipe", "pipe", "pipe"]
3850
+ });
3851
+ } catch (error) {
3852
+ resolve(spawnFailedResult(error));
3853
+ return;
3854
+ }
3855
+ let stdout = "";
3856
+ let stderr = "";
3857
+ let settled = false;
3858
+ const timeout = timeoutMs > 0 ? setTimeout(() => {
3859
+ if (settled) return;
3860
+ const message = `Native host helper timed out after ${timeoutMs}ms.`;
3861
+ stderr += `${message}
3862
+ `;
3863
+ child.kill("SIGKILL");
3864
+ settled = true;
3865
+ resolve({ status: "timedOut", exitCode: 1, stdout, stderr, error: { code: "timeout", message } });
3866
+ }, timeoutMs) : void 0;
3867
+ timeout?.unref?.();
3868
+ const resolveOnce = (result) => {
3869
+ if (settled) return;
3870
+ settled = true;
3871
+ if (timeout) clearTimeout(timeout);
3872
+ resolve(result);
3873
+ };
3874
+ child.on("error", (error) => resolveOnce(spawnFailedResult(error, stdout, stderr)));
3875
+ child.stdout.setEncoding("utf8");
3876
+ child.stderr.setEncoding("utf8");
3877
+ child.stdout.on("data", (chunk) => {
3878
+ stdout += chunk;
3879
+ });
3880
+ child.stderr.on("data", (chunk) => {
3881
+ stderr += chunk;
3882
+ });
3883
+ child.stdin.on("error", () => void 0);
3884
+ if (stdin) {
3885
+ child.stdin.write(stdin);
3886
+ }
3887
+ child.stdin.end();
3888
+ child.on("close", (exitCode) => {
3889
+ resolveOnce({ status: "completed", exitCode: exitCode ?? 1, stdout, stderr });
3890
+ });
3891
+ });
3892
+ }
3893
+ function filterNativeHostHelperEnvironment(env) {
3894
+ const allowedNames = new Set(nativeHostHelperEnvironmentAllowlist);
3895
+ const filtered = {};
3896
+ for (const [name, value] of Object.entries(env)) {
3897
+ if (!value || !allowedNames.has(name) || isSensitiveEnvironmentName(name)) {
3898
+ continue;
3899
+ }
3900
+ filtered[name] = value;
3901
+ }
3902
+ return filtered;
3903
+ }
3904
+ function nativeHostSandboxPolicy(cwd, mode) {
3905
+ return {
3906
+ mode,
3907
+ cwd,
3908
+ filesystem: mode === "filesystemReadOnly" ? "readOnly" : "default",
3909
+ network: mode === "networkDisabled" ? "disabled" : "default",
3910
+ envPolicy: "allowlist-only"
3911
+ };
3912
+ }
3913
+ function normalizeHostCommandResult(request, result) {
3914
+ const outputBudgetBytes = positiveInteger(request.outputBudgetBytes) ?? defaultNativeHostOutputBudgetBytes;
3915
+ const normalize = request.requirePty ? normalizePtyTranscript : (value) => value;
3916
+ return {
3917
+ ...result,
3918
+ stdout: capUtf8Text(normalize(result.stdout), outputBudgetBytes),
3919
+ stderr: capUtf8Text(normalize(result.stderr), outputBudgetBytes)
3920
+ };
3921
+ }
3922
+ function normalizePtyTranscript(value) {
3923
+ return value.replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, "").replace(/\u001b\][^\u0007]*(?:\u0007|\u001b\\)/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, "");
3924
+ }
3925
+ function capUtf8Text(value, budgetBytes) {
3926
+ if (Buffer.byteLength(value, "utf8") <= budgetBytes) {
3927
+ return value;
3928
+ }
3929
+ let capped = Buffer.from(value, "utf8").subarray(0, budgetBytes).toString("utf8");
3930
+ while (Buffer.byteLength(capped, "utf8") > budgetBytes && capped.length > 0) {
3931
+ capped = capped.slice(0, -1);
3932
+ }
3933
+ return capped;
3934
+ }
3935
+ function isSensitiveEnvironmentName(name) {
3936
+ return /(?:TOKEN|SECRET|PASSWORD|PASS|KEY|CREDENTIAL|AUTH|OAUTH|COOKIE|SESSION|PRIVATE|CERT|SSH)/i.test(name);
3937
+ }
3938
+ function isNativeHostHelperHandshake(value) {
3939
+ if (!isRecord(value) || value.protocolVersion !== hostExecutionProtocolVersion) return false;
3940
+ if (!isHostExecutionCapabilities(value.capabilities)) return false;
3941
+ if (!isRecord(value.secretBoundary)) return false;
3942
+ return value.secretBoundary.credentialPolicy === "never-send-credentials" && value.secretBoundary.environmentPolicy === "allowlist-only" && Array.isArray(value.secretBoundary.forbiddenInputs) && value.secretBoundary.forbiddenInputs.length > 0 && value.secretBoundary.forbiddenInputs.every((item) => typeof item === "string" && item.trim().length > 0);
3943
+ }
3944
+ function isHostExecutionCapabilities(value) {
3945
+ if (!isRecord(value)) return false;
3946
+ return value.protocolVersion === hostExecutionProtocolVersion && (value.implementation === "node" || value.implementation === "nativeHelper") && isCapabilitySupport(value.processGroups) && isCapabilitySupport(value.signalEscalation) && isCapabilitySupport(value.streamCapture) && isCapabilitySupport(value.pty) && isCapabilitySupport(value.sandbox) && value.outputEncoding === "utf8";
3947
+ }
3948
+ function isCapabilitySupport(value) {
3949
+ return isRecord(value) && typeof value.supported === "boolean" && (value.reason === void 0 || typeof value.reason === "string");
3950
+ }
3951
+ function isNativeHostCommandResultEnvelope(value) {
3952
+ if (!isRecord(value) || value.protocolVersion !== hostExecutionProtocolVersion) return false;
3953
+ return typeof value.requestId === "string" && isHostCommandStatus(value.status) && typeof value.exitCode === "number" && typeof value.stdout === "string" && typeof value.stderr === "string" && (value.error === void 0 || isHostCommandError(value.error));
3954
+ }
3955
+ function isHostCommandStatus(value) {
3956
+ return value === "completed" || value === "timedOut" || value === "spawnFailed" || value === "permissionDenied" || value === "helperUnavailable" || value === "protocolMismatch" || value === "killed" || value === "unsupported" || value === "failed";
3957
+ }
3958
+ function isHostCommandError(value) {
3959
+ return isRecord(value) && isHostCommandErrorCode(value.code) && typeof value.message === "string";
3960
+ }
3961
+ function isHostCommandErrorCode(value) {
3962
+ return value === "timeout" || value === "spawn_failed" || value === "permission_denied" || value === "helper_unavailable" || value === "protocol_mismatch" || value === "helper_failed" || value === "killed" || value === "pty_unsupported" || value === "sandbox_unsupported";
3963
+ }
3964
+ function isRecord(value) {
3965
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3966
+ }
3967
+ function parseJson(value) {
3968
+ try {
3969
+ return JSON.parse(value);
3970
+ } catch {
3971
+ return void 0;
3972
+ }
3973
+ }
3974
+ function positiveInteger(value) {
3975
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
3976
+ }
3977
+ function errorMessage(error) {
3978
+ return error instanceof Error ? error.message : String(error);
3979
+ }
3980
+
3981
+ // src/bounded-tool-adapter.ts
3982
+ var boundedToolAdapterCatalog = [
3983
+ { id: "filesystem.readFile", kind: "filesystem", displayName: "Read scoped file", implemented: true, mutating: false, concurrency: "parallelSafe", serializedResourceKinds: ["executionRoot"] },
3984
+ { id: "filesystem.listFiles", kind: "filesystem", displayName: "List scoped files", implemented: true, mutating: false, concurrency: "parallelSafe", serializedResourceKinds: ["executionRoot"] },
3985
+ { id: "filesystem.searchText", kind: "filesystem", displayName: "Search scoped text", implemented: true, mutating: false, concurrency: "parallelSafe", serializedResourceKinds: ["executionRoot"] },
3986
+ { id: "filesystem.writeFile", kind: "filesystem", displayName: "Write scoped file", implemented: true, mutating: true, concurrency: "serialized", serializedResourceKinds: ["executionRoot"] },
3987
+ { id: "shell.curatedCommand", kind: "shell", displayName: "Run runner-owned command preset", implemented: true, mutating: false, concurrency: "serialized", serializedResourceKinds: ["executionRoot", "process"] },
3988
+ { id: "git.status", kind: "git", displayName: "Read Git status", implemented: true, mutating: false, concurrency: "parallelSafe", serializedResourceKinds: ["gitIndex"] },
3989
+ { id: "git.diffNameOnly", kind: "git", displayName: "Read changed Git paths", implemented: true, mutating: false, concurrency: "parallelSafe", serializedResourceKinds: ["gitIndex"] },
3990
+ { id: "packageManager.detect", kind: "packageManager", displayName: "Detect package manager", implemented: true, mutating: false, concurrency: "parallelSafe", serializedResourceKinds: ["packageManagerCache"] },
3991
+ { id: "testRunner.profile", kind: "testRunner", displayName: "Run known verification profile", implemented: true, mutating: false, concurrency: "serialized", serializedResourceKinds: ["testRunner", "packageManagerCache"] },
3992
+ { id: "browser.check", kind: "browser", displayName: "Run local browser smoke check", implemented: true, mutating: false, concurrency: "serialized", serializedResourceKinds: ["browserProfile"] },
3993
+ { id: "modelClient.direct", kind: "modelClient", displayName: "Invoke direct model client", implemented: true, mutating: false, concurrency: "serialized", serializedResourceKinds: ["modelProvider"] },
3994
+ { id: "agentClient.compatibilityBridge", kind: "agentClient", displayName: "Invoke optional local AI client", implemented: true, mutating: true, concurrency: "serialized", serializedResourceKinds: ["agentSession", "providerAuth", "executionRoot"] }
3995
+ ];
3996
+ var testRunnerProfileIds = ["verify", "test", "typecheck", "lint", "build"];
3997
+ var packageLockfiles = {
3998
+ pnpm: ["pnpm-lock.yaml"],
3999
+ npm: ["package-lock.json"],
4000
+ yarn: ["yarn.lock"],
4001
+ bun: ["bun.lockb", "bun.lock"]
4002
+ };
4003
+ var ignoredTraversalDirectories = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", ".turbo", "coverage", ".cache"]);
4004
+ var defaultFilesystemListLimit = 200;
4005
+ var defaultFilesystemSearchLimit = 80;
4006
+ var filesystemSearchMaxFileBytes = 256e3;
4007
+ var defaultCuratedShellCommands = {
4008
+ "git.status.short": {
4009
+ command: "git",
4010
+ args: ["status", "--short"],
4011
+ displayName: "git status --short",
4012
+ mutationPolicy: "readOnly"
4013
+ },
4014
+ "git.diff.nameOnly": {
4015
+ command: "git",
4016
+ args: ["diff", "--name-only"],
4017
+ displayName: "git diff --name-only",
4018
+ mutationPolicy: "readOnly"
4019
+ },
4020
+ "package.detect": {
4021
+ command: process.execPath,
4022
+ args: ["-e", "const fs=require('fs');const files=['pnpm-lock.yaml','package-lock.json','yarn.lock','bun.lockb'];console.log(files.filter((file)=>fs.existsSync(file)).join('\\n'));"],
4023
+ displayName: "detect package manager lockfiles",
4024
+ mutationPolicy: "readOnly"
4025
+ }
4026
+ };
4027
+ function createBoundedToolAdapterPolicy(options) {
4028
+ const executionRoot = path6.resolve(options.executionRoot);
4029
+ return {
4030
+ executionRoot,
4031
+ mutationPolicy: options.mutationPolicy,
4032
+ timeoutMs: positiveInteger2(options.timeoutMs) ?? 3e4,
4033
+ outputBudgetBytes: positiveInteger2(options.outputBudgetBytes) ?? 64e3,
4034
+ allowedWorkKinds: options.allowedWorkKinds ?? ["assistantQuestion", "projectContextRefresh", "implementationVerification", "testQualityScan", "implementationTestGate", "implementation"],
4035
+ redaction: { localPaths: true, secretAssignments: true },
4036
+ concurrency: {
4037
+ mode: options.concurrencyMode ?? "serialized",
4038
+ resourceKey: options.resourceKey ?? executionRoot
4039
+ }
4040
+ };
4041
+ }
4042
+ async function runFilesystemReadTool(options) {
4043
+ const resolved = resolveRelativePath(options.policy, options.relativePath);
4044
+ if (!resolved.ok) {
4045
+ return failedResult("filesystem.readFile", resolved.error, options.policy);
4046
+ }
4047
+ try {
4048
+ const content = await readFile4(resolved.path, "utf8");
4049
+ return completedResult("filesystem.readFile", content, "", options.policy);
4050
+ } catch (error) {
4051
+ return failedResult("filesystem.readFile", { code: "execution_failed", message: `File read failed: ${errorMessage2(error)}` }, options.policy);
4052
+ }
4053
+ }
4054
+ async function runFilesystemWriteTool(options) {
4055
+ const mutation = ensureMutationAllowed(options.policy, "mutating");
4056
+ if (mutation) {
4057
+ return failedResult("filesystem.writeFile", mutation, options.policy);
4058
+ }
4059
+ const resolved = resolveRelativePath(options.policy, options.relativePath);
4060
+ if (!resolved.ok) {
4061
+ return failedResult("filesystem.writeFile", resolved.error, options.policy);
4062
+ }
4063
+ try {
4064
+ await mkdir6(path6.dirname(resolved.path), { recursive: true });
4065
+ await writeFile5(resolved.path, options.content, "utf8");
4066
+ const summary = { relativePath: normalizeRelativePath(options.relativePath), bytes: Buffer.byteLength(options.content, "utf8") };
4067
+ return completedResult("filesystem.writeFile", JSON.stringify(summary, null, 2), "", options.policy);
4068
+ } catch (error) {
4069
+ return failedResult("filesystem.writeFile", { code: "execution_failed", message: `File write failed: ${errorMessage2(error)}` }, options.policy);
4070
+ }
4071
+ }
4072
+ async function runFilesystemListTool(options) {
4073
+ const relativeDirectory = normalizeRelativePath(options.relativeDirectory ?? ".");
4074
+ const resolved = resolveRelativePath(options.policy, relativeDirectory);
4075
+ if (!resolved.ok) {
4076
+ return failedResult("filesystem.listFiles", resolved.error, options.policy);
4077
+ }
4078
+ try {
4079
+ const maxFiles = positiveInteger2(options.maxFiles) ?? defaultFilesystemListLimit;
4080
+ const files = await listFiles(options.policy.executionRoot, resolved.path, maxFiles);
4081
+ const summary = { relativeDirectory, files: files.items, truncated: files.truncated };
4082
+ return completedResult("filesystem.listFiles", JSON.stringify(summary, null, 2), "", options.policy);
4083
+ } catch (error) {
4084
+ return failedResult("filesystem.listFiles", { code: "execution_failed", message: `File list failed: ${errorMessage2(error)}` }, options.policy);
4085
+ }
4086
+ }
4087
+ async function runFilesystemSearchTool(options) {
4088
+ const query = options.query.trim();
4089
+ if (!query) {
4090
+ return failedResult("filesystem.searchText", { code: "command_not_allowed", message: "Search query must not be empty." }, options.policy);
4091
+ }
4092
+ const relativeDirectory = normalizeRelativePath(options.relativeDirectory ?? ".");
4093
+ const resolved = resolveRelativePath(options.policy, relativeDirectory);
4094
+ if (!resolved.ok) {
4095
+ return failedResult("filesystem.searchText", resolved.error, options.policy);
4096
+ }
4097
+ try {
4098
+ const maxMatches = positiveInteger2(options.maxMatches) ?? defaultFilesystemSearchLimit;
4099
+ const matches = await searchText(options.policy, resolved.path, query, maxMatches);
4100
+ const summary = { query, matches: matches.items, truncated: matches.truncated };
4101
+ return completedResult("filesystem.searchText", JSON.stringify(summary, null, 2), "", options.policy);
4102
+ } catch (error) {
4103
+ return failedResult("filesystem.searchText", { code: "execution_failed", message: `Text search failed: ${errorMessage2(error)}` }, options.policy);
4104
+ }
4105
+ }
4106
+ async function runGitStatusTool(options) {
4107
+ return executeCuratedCommand(defaultCuratedShellCommands["git.status.short"], options.policy, "git.status");
4108
+ }
4109
+ async function runGitDiffNameOnlyTool(options) {
4110
+ return executeCuratedCommand(defaultCuratedShellCommands["git.diff.nameOnly"], options.policy, "git.diffNameOnly");
4111
+ }
4112
+ async function runPackageManagerDetectTool(options) {
4113
+ try {
4114
+ const detection = await detectPackageManager(options.policy.executionRoot);
4115
+ return completedResult("packageManager.detect", JSON.stringify(detection, null, 2), "", options.policy);
4116
+ } catch (error) {
4117
+ return failedResult("packageManager.detect", { code: "execution_failed", message: `Package manager detection failed: ${errorMessage2(error)}` }, options.policy);
4118
+ }
4119
+ }
4120
+ async function runTestRunnerProfileTool(options) {
4121
+ const profileId = options.profileId ?? "test";
4122
+ if (!testRunnerProfileIds.includes(profileId)) {
4123
+ return failedResult("testRunner.profile", { code: "command_not_allowed", message: "Verification profile is not allowed by this runner policy." }, options.policy);
4124
+ }
4125
+ let detection;
4126
+ try {
4127
+ detection = await detectPackageManager(options.policy.executionRoot);
4128
+ } catch (error) {
4129
+ return failedResult("testRunner.profile", { code: "execution_failed", message: `Verification profile discovery failed: ${errorMessage2(error)}` }, options.policy);
4130
+ }
4131
+ if (!detection.profiles.includes(profileId)) {
4132
+ return failedResult("testRunner.profile", { code: "command_not_allowed", message: `Verification profile is not defined in local package scripts: ${profileId}.` }, options.policy);
4133
+ }
4134
+ const command = options.commandCatalog?.[profileId] ?? testRunnerProfileCommand(options.packageManager ?? detection.packageManager ?? "npm", profileId);
4135
+ return executeCuratedCommand(command, options.policy, "testRunner.profile");
4136
+ }
4137
+ async function runBrowserCheckTool(options) {
4138
+ const target = parseBrowserCheckTarget(options.url, options.allowedHosts);
4139
+ if (!target.ok) {
4140
+ return failedResult("browser.check", target.error, options.policy);
4141
+ }
4142
+ const abortController = new AbortController();
4143
+ const timeout = setTimeout(() => abortController.abort(), options.policy.timeoutMs);
4144
+ timeout.unref?.();
4145
+ try {
4146
+ const response = await (options.fetchImpl ?? fetch)(target.url, { method: "GET", redirect: "manual", signal: abortController.signal });
4147
+ const body = await response.text();
4148
+ const summary = {
4149
+ url: target.url.toString(),
4150
+ status: response.status,
4151
+ ok: response.ok,
4152
+ ...response.headers.get("content-type") ? { contentType: response.headers.get("content-type") } : {},
4153
+ ...extractHtmlTitle(body) ? { title: extractHtmlTitle(body) } : {},
4154
+ bytes: Buffer.byteLength(body, "utf8")
4155
+ };
4156
+ const stdout = JSON.stringify(summary, null, 2);
4157
+ if (response.ok) {
4158
+ return completedResult("browser.check", stdout, "", options.policy);
4159
+ }
4160
+ return failedResult("browser.check", { code: "execution_failed", message: `Browser check failed with HTTP ${response.status}.` }, options.policy, stdout);
4161
+ } catch (error) {
4162
+ if (abortController.signal.aborted) {
4163
+ return failedResult("browser.check", { code: "timeout", message: `Browser check timed out after ${formatTimeoutDuration(options.policy.timeoutMs)}.` }, options.policy);
4164
+ }
4165
+ return failedResult("browser.check", { code: "execution_failed", message: `Browser check failed: ${errorMessage2(error)}` }, options.policy);
4166
+ } finally {
4167
+ clearTimeout(timeout);
4168
+ }
4169
+ }
4170
+ async function executeCuratedCommand(command, policy, adapterId) {
4171
+ const hostExecution = await getRuntimeHostExecutionPort();
4172
+ const result = await hostExecution.executeCommand({
4173
+ command: command.command,
4174
+ args: command.args,
4175
+ cwd: policy.executionRoot,
4176
+ env: process.env,
4177
+ shell: false,
4178
+ timeoutMs: policy.timeoutMs,
4179
+ timeoutMessage: `Tool adapter timed out after ${formatTimeoutDuration(policy.timeoutMs)}: ${command.displayName}`
4180
+ });
4181
+ if (result.status === "timedOut") {
4182
+ return failedResult(adapterId, { code: "timeout", message: result.error?.message ?? `Tool adapter timed out after ${formatTimeoutDuration(policy.timeoutMs)}: ${command.displayName}` }, policy, result.stdout, result.stderr);
4183
+ }
4184
+ if (result.status === "spawnFailed" || result.status === "unsupported") {
4185
+ return failedResult(adapterId, { code: "execution_failed", message: result.error?.message ?? `Command execution failed: ${command.displayName}` }, policy, result.stdout, result.stderr);
4186
+ }
4187
+ if (result.exitCode === 0) {
4188
+ return completedResult(adapterId, result.stdout, result.stderr, policy);
4189
+ }
4190
+ return failedResult(adapterId, { code: "execution_failed", message: `${command.displayName} exited with code ${result.exitCode}.` }, policy, result.stdout, result.stderr);
4191
+ }
4192
+ async function detectPackageManager(executionRoot) {
4193
+ const packageJson = await readPackageJson(executionRoot);
4194
+ const lockfiles = await detectPackageLockfiles(executionRoot);
4195
+ const scripts = Object.keys(packageJson?.scripts ?? {}).sort();
4196
+ const packageManager = packageManagerFromPackageManagerField(packageJson?.packageManager) ?? packageManagerFromLockfiles(lockfiles);
4197
+ const profiles = testRunnerProfileIds.filter((profileId) => scripts.includes(profileId));
4198
+ return {
4199
+ ...packageManager ? { packageManager } : {},
4200
+ lockfiles,
4201
+ scripts,
4202
+ profiles
4203
+ };
4204
+ }
4205
+ async function listFiles(executionRoot, directoryPath, maxFiles) {
4206
+ const items = [];
4207
+ let truncated = false;
4208
+ const visit = async (currentDirectory) => {
4209
+ if (truncated) return;
4210
+ const entries = await readdir3(currentDirectory, { withFileTypes: true });
4211
+ for (const entry of entries.sort((first, second) => first.name.localeCompare(second.name))) {
4212
+ if (truncated) return;
4213
+ if (entry.name.startsWith(".") && ignoredTraversalDirectories.has(entry.name)) continue;
4214
+ if (ignoredTraversalDirectories.has(entry.name)) continue;
4215
+ const absolutePath = path6.join(currentDirectory, entry.name);
4216
+ if (entry.isDirectory()) {
4217
+ await visit(absolutePath);
4218
+ continue;
4219
+ }
4220
+ if (!entry.isFile()) continue;
4221
+ items.push(normalizeRelativePath(path6.relative(executionRoot, absolutePath)));
4222
+ if (items.length >= maxFiles) {
4223
+ truncated = true;
4224
+ return;
4225
+ }
4226
+ }
4227
+ };
4228
+ await visit(directoryPath);
4229
+ return { items, truncated };
4230
+ }
4231
+ async function searchText(policy, directoryPath, query, maxMatches) {
4232
+ const listed = await listFiles(policy.executionRoot, directoryPath, Math.max(maxMatches * 20, maxMatches));
4233
+ const lowerQuery = query.toLowerCase();
4234
+ const matches = [];
4235
+ let truncated = listed.truncated;
4236
+ for (const relativePath of listed.items) {
4237
+ if (matches.length >= maxMatches) {
4238
+ truncated = true;
4239
+ break;
4240
+ }
4241
+ const absolutePath = path6.join(policy.executionRoot, relativePath);
4242
+ const stats = await stat3(absolutePath);
4243
+ if (!stats.isFile() || stats.size > filesystemSearchMaxFileBytes) continue;
4244
+ const content = await readFile4(absolutePath, "utf8").catch(() => void 0);
4245
+ if (content === void 0 || content.includes("\0")) continue;
4246
+ const lines = content.split(/\r?\n/);
4247
+ for (const [lineIndex, line] of lines.entries()) {
4248
+ if (!line.toLowerCase().includes(lowerQuery)) continue;
4249
+ matches.push({ relativePath, line: lineIndex + 1, text: line.slice(0, 500) });
4250
+ if (matches.length >= maxMatches) {
4251
+ truncated = true;
4252
+ break;
4253
+ }
4254
+ }
4255
+ }
4256
+ return { items: matches, truncated };
4257
+ }
4258
+ async function readPackageJson(executionRoot) {
4259
+ const packageJsonPath = path6.join(executionRoot, "package.json");
4260
+ if (!await fileExists(packageJsonPath)) {
4261
+ return void 0;
4262
+ }
4263
+ const parsed = JSON.parse(await readFile4(packageJsonPath, "utf8"));
4264
+ if (!isRecord2(parsed)) {
4265
+ return void 0;
4266
+ }
4267
+ const scripts = isRecord2(parsed.scripts) ? Object.fromEntries(Object.entries(parsed.scripts).filter((entry) => typeof entry[1] === "string")) : void 0;
4268
+ return {
4269
+ ...typeof parsed.packageManager === "string" ? { packageManager: parsed.packageManager } : {},
4270
+ ...scripts ? { scripts } : {}
4271
+ };
4272
+ }
4273
+ async function detectPackageLockfiles(executionRoot) {
4274
+ const lockfileCandidates = Object.values(packageLockfiles).flat();
4275
+ const presentLockfiles = [];
4276
+ for (const lockfile of lockfileCandidates) {
4277
+ if (await fileExists(path6.join(executionRoot, lockfile))) {
4278
+ presentLockfiles.push(lockfile);
4279
+ }
4280
+ }
4281
+ return presentLockfiles;
4282
+ }
4283
+ function packageManagerFromPackageManagerField(value) {
4284
+ const packageManagerName = value?.split("@")[0]?.trim();
4285
+ return isPackageManagerName(packageManagerName) ? packageManagerName : void 0;
4286
+ }
4287
+ function packageManagerFromLockfiles(lockfiles) {
4288
+ for (const packageManager of ["pnpm", "npm", "yarn", "bun"]) {
4289
+ if (packageLockfiles[packageManager].some((lockfile) => lockfiles.includes(lockfile))) {
4290
+ return packageManager;
4291
+ }
4292
+ }
4293
+ return void 0;
4294
+ }
4295
+ function testRunnerProfileCommand(packageManager, profileId) {
4296
+ if (packageManager === "pnpm") {
4297
+ return { command: "corepack", args: ["pnpm", "--config.verify-deps-before-run=false", "run", profileId], displayName: `pnpm run ${profileId}`, mutationPolicy: "readOnly" };
4298
+ }
4299
+ if (packageManager === "yarn") {
4300
+ return { command: "corepack", args: ["yarn", "run", profileId], displayName: `yarn run ${profileId}`, mutationPolicy: "readOnly" };
4301
+ }
4302
+ if (packageManager === "bun") {
4303
+ return { command: "bun", args: ["run", profileId], displayName: `bun run ${profileId}`, mutationPolicy: "readOnly" };
4304
+ }
4305
+ return { command: "npm", args: ["run", profileId], displayName: `npm run ${profileId}`, mutationPolicy: "readOnly" };
4306
+ }
4307
+ function parseBrowserCheckTarget(value, allowedHosts) {
4308
+ let url;
4309
+ try {
4310
+ url = new URL(value);
4311
+ } catch {
4312
+ return { ok: false, error: { code: "command_not_allowed", message: "Browser check target URL is invalid." } };
4313
+ }
4314
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
4315
+ return { ok: false, error: { code: "command_not_allowed", message: "Browser check target must use http or https." } };
4316
+ }
4317
+ if (url.username || url.password) {
4318
+ return { ok: false, error: { code: "command_not_allowed", message: "Browser check target must not include credentials." } };
4319
+ }
4320
+ const allowedHostSet = new Set((allowedHosts ?? []).map((host2) => host2.toLowerCase()));
4321
+ const hostname = url.hostname.toLowerCase();
4322
+ const host = url.host.toLowerCase();
4323
+ if (!isLoopbackHost(hostname) && !allowedHostSet.has(hostname) && !allowedHostSet.has(host)) {
4324
+ return { ok: false, error: { code: "command_not_allowed", message: "Browser check target must be loopback or explicitly allowed by the runner policy." } };
4325
+ }
4326
+ return { ok: true, url };
4327
+ }
4328
+ function isLoopbackHost(hostname) {
4329
+ return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname.startsWith("127.") || hostname === "::1" || hostname === "[::1]" || hostname === "0.0.0.0";
4330
+ }
4331
+ function extractHtmlTitle(body) {
4332
+ const match = /<title[^>]*>([^<]*)<\/title>/i.exec(body);
4333
+ const title = match?.[1]?.replace(/\s+/g, " ").trim();
4334
+ return title || void 0;
4335
+ }
4336
+ async function fileExists(filePath) {
4337
+ try {
4338
+ await access(filePath, constants.F_OK);
4339
+ return true;
4340
+ } catch {
4341
+ return false;
4342
+ }
4343
+ }
4344
+ function isPackageManagerName(value) {
4345
+ return value === "pnpm" || value === "npm" || value === "yarn" || value === "bun";
4346
+ }
4347
+ function isRecord2(value) {
4348
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4349
+ }
4350
+ function resolveRelativePath(policy, relativePath) {
4351
+ if (path6.isAbsolute(relativePath)) {
4352
+ return { ok: false, error: { code: "out_of_root", message: "Tool adapter rejected absolute local path input." } };
4353
+ }
4354
+ const resolved = path6.resolve(policy.executionRoot, relativePath);
4355
+ if (!isInsideRoot(policy.executionRoot, resolved)) {
4356
+ return { ok: false, error: { code: "out_of_root", message: "Tool adapter path is outside the execution root." } };
4357
+ }
4358
+ return { ok: true, path: resolved };
4359
+ }
4360
+ function normalizeRelativePath(relativePath) {
4361
+ const normalized = relativePath.replaceAll("\\", "/").replace(/^\.\//, "");
4362
+ return normalized || ".";
4363
+ }
4364
+ function ensureMutationAllowed(policy, requiredPolicy) {
4365
+ if (requiredPolicy === "mutating" && policy.mutationPolicy !== "mutating") {
4366
+ return { code: "mutation_denied", message: "Tool adapter mutation is not allowed by the current harness policy." };
4367
+ }
4368
+ return void 0;
4369
+ }
4370
+ function completedResult(adapterId, stdout, stderr, policy) {
4371
+ const output = normalizeOutput(stdout, stderr, policy);
4372
+ return { adapterId, status: "completed", exitCode: 0, stdout: output.stdout, stderr: output.stderr, truncated: output.truncated };
4373
+ }
4374
+ function failedResult(adapterId, error, policy, stdout = "", stderr = "") {
4375
+ const output = normalizeOutput(stdout, stderr, policy);
4376
+ return { adapterId, status: "failed", exitCode: 1, stdout: output.stdout, stderr: output.stderr, truncated: output.truncated, error };
4377
+ }
4378
+ function normalizeOutput(stdout, stderr, policy) {
4379
+ const redactedStdout = redactToolAdapterText(stdout, policy);
4380
+ const redactedStderr = redactToolAdapterText(stderr, policy);
4381
+ const stdoutBudget = Math.floor(policy.outputBudgetBytes / 2);
4382
+ const stderrBudget = policy.outputBudgetBytes - stdoutBudget;
4383
+ const normalizedStdout = truncateToBudget(redactedStdout, stdoutBudget);
4384
+ const normalizedStderr = truncateToBudget(redactedStderr, stderrBudget);
4385
+ return {
4386
+ stdout: normalizedStdout.value,
4387
+ stderr: normalizedStderr.value,
4388
+ truncated: normalizedStdout.truncated || normalizedStderr.truncated
4389
+ };
4390
+ }
4391
+ function redactToolAdapterText(value, policy) {
4392
+ let result = value;
4393
+ if (policy.redaction.localPaths) {
4394
+ result = result.replaceAll(policy.executionRoot, "<execution-root>");
4395
+ }
4396
+ if (policy.redaction.secretAssignments) {
4397
+ result = result.replace(/\b([A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|API_KEY|ACCESS_KEY)[A-Z0-9_]*)=([^\s"'`]+)/gi, "$1=<redacted>");
4398
+ }
4399
+ return result;
4400
+ }
4401
+ function truncateToBudget(value, budgetBytes) {
4402
+ if (budgetBytes <= 0) {
4403
+ return { value: "", truncated: Boolean(value) };
4404
+ }
4405
+ const bytes = Buffer.byteLength(value, "utf8");
4406
+ if (bytes <= budgetBytes) {
4407
+ return { value, truncated: false };
4408
+ }
4409
+ const suffix = "\n[truncated by Amistio tool adapter output budget]";
4410
+ const contentBudget = Math.max(0, budgetBytes - Buffer.byteLength(suffix, "utf8"));
4411
+ return { value: `${Buffer.from(value, "utf8").subarray(0, contentBudget).toString("utf8")}${suffix}`, truncated: true };
4412
+ }
4413
+ function isInsideRoot(root, candidatePath) {
4414
+ const relative = path6.relative(root, candidatePath);
4415
+ return relative === "" || !relative.startsWith("..") && !path6.isAbsolute(relative);
4416
+ }
4417
+ function positiveInteger2(value) {
4418
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
4419
+ }
4420
+ function errorMessage2(error) {
4421
+ return error instanceof Error ? error.message : String(error);
4422
+ }
4423
+ function formatTimeoutDuration(timeoutMs) {
4424
+ if (timeoutMs < 1e3) {
4425
+ return `${timeoutMs}ms`;
4426
+ }
4427
+ const seconds = timeoutMs / 1e3;
4428
+ return Number.isInteger(seconds) ? `${seconds}s` : `${seconds.toFixed(1)}s`;
4429
+ }
4430
+
4431
+ // src/local-tool-runner.ts
3529
4432
  var localToolNames = runnerToolNames;
3530
4433
  var allReasoningEfforts = ["auto", "low", "medium", "high", "xhigh"];
3531
4434
  var highReasoningEfforts = ["auto", "high", "xhigh"];
4435
+ var localToolOutputBudgetBytes = 32 * 1024;
3532
4436
  var builtInProviderCatalogs = {
3533
4437
  opencode: {
3534
4438
  anthropic: {
@@ -3714,7 +4618,7 @@ async function detectLocalTools() {
3714
4618
  return Promise.all(
3715
4619
  localToolAdapters.map(async (adapter) => {
3716
4620
  const sdkAvailable = await isSdkAvailable(adapter);
3717
- const commandAvailable = adapter.executable ? await commandExists(adapter.executable) : false;
4621
+ const commandAvailable = adapter.executable ? await commandExists2(adapter.executable) : false;
3718
4622
  const providerCatalog = await detectRunnerProviderCatalog(adapter);
3719
4623
  return {
3720
4624
  name: adapter.name,
@@ -3732,9 +4636,9 @@ async function detectLocalTools() {
3732
4636
  );
3733
4637
  }
3734
4638
  async function runLocalTool(options) {
3735
- const promptTempDir = await mkdtemp(path6.join(os3.tmpdir(), "amistio-prompt-"));
3736
- const promptFilePath = path6.join(promptTempDir, "prompt.md");
3737
- await writeFile5(promptFilePath, options.prompt, "utf8");
4639
+ const promptTempDir = await mkdtemp(path7.join(os3.tmpdir(), "amistio-prompt-"));
4640
+ const promptFilePath = path7.join(promptTempDir, "prompt.md");
4641
+ await writeFile6(promptFilePath, options.prompt, "utf8");
3738
4642
  const modelConfig = normalizeModelOptions(options);
3739
4643
  try {
3740
4644
  const runnerOptions = {
@@ -3749,14 +4653,14 @@ async function runLocalTool(options) {
3749
4653
  runnerOptions.toolCommand = options.toolCommand;
3750
4654
  }
3751
4655
  const runner2 = await createToolRunner(runnerOptions);
3752
- const result = await executeToolRunner(runner2, {
4656
+ const result = normalizeToolExecutionResult(await executeToolRunner(runner2, {
3753
4657
  rootDir: options.rootDir,
3754
4658
  prompt: options.prompt,
3755
4659
  promptFilePath,
3756
4660
  streamOutput: Boolean(options.streamOutput),
3757
4661
  ...modelConfig,
3758
4662
  ...options.session ? { session: options.session } : {}
3759
- }, options.timeoutMs);
4663
+ }, options.timeoutMs), options.rootDir);
3760
4664
  return {
3761
4665
  toolName: runner2.toolName,
3762
4666
  displayCommand: runner2.kind === "sdk" ? runner2.displayCommand : runner2.invocation.displayCommand,
@@ -3770,7 +4674,7 @@ async function runLocalTool(options) {
3770
4674
  }
3771
4675
  }
3772
4676
  async function createToolRunPreview(options) {
3773
- const promptFilePath = path6.join(os3.tmpdir(), "amistio-generated-prompt.md");
4677
+ const promptFilePath = path7.join(os3.tmpdir(), "amistio-generated-prompt.md");
3774
4678
  const modelConfig = normalizeModelOptions(options);
3775
4679
  const runnerOptions = {
3776
4680
  rootDir: options.rootDir,
@@ -3831,7 +4735,7 @@ async function createToolRunner(options) {
3831
4735
  if (requiresModelSelection && !options.model) {
3832
4736
  throw new Error("Provider-backed model configuration requires --model or --provider with --model-id.");
3833
4737
  }
3834
- const adapter = tool === "auto" ? await selectFirstAvailableAdapter(requiresModelSelection, options.invocationChannel) : await selectRequestedAdapter(tool, options.invocationChannel);
4738
+ const adapter = tool === "auto" ? await selectFirstAvailableAdapter(options, options.invocationChannel) : await selectRequestedAdapter(tool, options.invocationChannel, options);
3835
4739
  if (requiresModelSelection && !adapter.supportsModelSelection) {
3836
4740
  throw new Error(`Model selection is not supported by ${adapter.name}. Remove model configuration or choose a model-aware adapter.`);
3837
4741
  }
@@ -3844,7 +4748,7 @@ async function createToolRunner(options) {
3844
4748
  allowCommandFallback: options.invocationChannel === "auto"
3845
4749
  };
3846
4750
  }
3847
- if (options.invocationChannel !== "sdk" && adapter.buildInvocation && adapter.executable && await commandExists(adapter.executable)) {
4751
+ if (options.invocationChannel !== "sdk" && adapter.buildInvocation && adapter.executable && await commandExists2(adapter.executable)) {
3848
4752
  return {
3849
4753
  toolName: adapter.name,
3850
4754
  kind: "command",
@@ -3866,10 +4770,10 @@ async function executeToolRunner(runner2, input, timeoutMs) {
3866
4770
  try {
3867
4771
  return await withTimeout(runner2.adapter.runWithSdk(input), timeoutMs, runner2.displayCommand);
3868
4772
  } catch (error) {
3869
- if (runner2.allowCommandFallback && runner2.adapter.buildInvocation && runner2.adapter.executable && await commandExists(runner2.adapter.executable)) {
4773
+ if (runner2.allowCommandFallback && runner2.adapter.buildInvocation && runner2.adapter.executable && await commandExists2(runner2.adapter.executable)) {
3870
4774
  const fallback = runner2.adapter.buildInvocation(input);
3871
4775
  const result = await executeToolInvocation(fallback, input.rootDir, input.streamOutput, timeoutMs);
3872
- const sdkFailure = `SDK execution for ${runner2.adapter.name} failed, fell back to ${fallback.displayCommand}: ${errorMessage(error)}`;
4776
+ const sdkFailure = `SDK execution for ${runner2.adapter.name} failed, fell back to ${fallback.displayCommand}: ${errorMessage3(error)}`;
3873
4777
  return {
3874
4778
  ...result,
3875
4779
  stderr: result.stderr ? `${sdkFailure}
@@ -3885,31 +4789,48 @@ function normalizeToolRequest(value) {
3885
4789
  }
3886
4790
  throw new Error(`Unsupported local tool: ${value}. Supported tools: auto, none, ${localToolNames.join(", ")}.`);
3887
4791
  }
3888
- async function selectFirstAvailableAdapter(requiresModelSelection = false, invocationChannel = "auto") {
4792
+ async function selectFirstAvailableAdapter(modelOptions, invocationChannel = "auto") {
4793
+ const requiresModelSelection = hasModelSelection(modelOptions);
4794
+ let modelSelectionFailure;
3889
4795
  for (const adapter of localToolAdapters) {
3890
4796
  if (requiresModelSelection && !adapter.supportsModelSelection) {
3891
4797
  continue;
3892
4798
  }
4799
+ const validationError = requiresModelSelection ? modelSelectionValidationError(adapter, modelOptions) : void 0;
4800
+ if (validationError) {
4801
+ modelSelectionFailure ??= validationError;
4802
+ continue;
4803
+ }
3893
4804
  const sdkAvailable = await isSdkAvailable(adapter);
3894
- const commandAvailable = adapter.executable ? await commandExists(adapter.executable) : false;
4805
+ const commandAvailable = adapter.executable ? await commandExists2(adapter.executable) : false;
3895
4806
  if (supportsRequestedInvocationChannel({ sdkAvailable, commandAvailable }, invocationChannel)) {
3896
4807
  return adapter;
3897
4808
  }
3898
4809
  }
3899
4810
  if (invocationChannel !== "auto") {
4811
+ if (modelSelectionFailure) {
4812
+ throw new Error(`No installed local AI tool supports the selected provider/model for ${invocationChannel} invocation. ${modelSelectionFailure}`);
4813
+ }
3900
4814
  throw new Error(`No installed local AI tool supports ${invocationChannel} invocation${requiresModelSelection ? " with model selection" : ""}. Select Auto or install a compatible tool.`);
3901
4815
  }
4816
+ if (modelSelectionFailure) {
4817
+ throw new Error(`No installed local AI tool supports the selected provider/model. ${modelSelectionFailure}`);
4818
+ }
3902
4819
  throw new Error(
3903
4820
  requiresModelSelection ? "No installed local AI tool supports model selection. Remove --model or choose a model-aware adapter." : `No supported local AI tool was found. Install one of ${localToolNames.join(", ")} or pass --tool-command "your-tool --prompt-file {promptFile}".`
3904
4821
  );
3905
4822
  }
3906
- async function selectRequestedAdapter(tool, invocationChannel = "auto") {
4823
+ async function selectRequestedAdapter(tool, invocationChannel = "auto", modelOptions) {
3907
4824
  const adapter = localToolAdapters.find((candidate) => candidate.name === tool);
3908
4825
  if (!adapter) {
3909
4826
  throw new Error(`Unsupported local tool: ${tool}.`);
3910
4827
  }
4828
+ if (modelOptions) {
4829
+ const validationError = modelSelectionValidationError(adapter, modelOptions);
4830
+ if (validationError) throw new Error(validationError);
4831
+ }
3911
4832
  const sdkAvailable = await isSdkAvailable(adapter);
3912
- const commandAvailable = adapter.executable ? await commandExists(adapter.executable) : false;
4833
+ const commandAvailable = adapter.executable ? await commandExists2(adapter.executable) : false;
3913
4834
  if (!supportsRequestedInvocationChannel({ sdkAvailable, commandAvailable }, invocationChannel)) {
3914
4835
  if (invocationChannel === "sdk") {
3915
4836
  throw new Error(`The ${tool} SDK was not found. Select Auto or Command invocation, install it, or pass --tool-command locally.`);
@@ -3925,18 +4846,78 @@ async function selectRequestedAdapter(tool, invocationChannel = "auto") {
3925
4846
  if (invocationChannel !== "sdk" && commandAvailable) {
3926
4847
  return adapter;
3927
4848
  }
3928
- throw new Error(`The ${tool} SDK or executable was not found. Install it or pass --tool-command.`);
4849
+ throw new Error(`The ${tool} SDK or executable was not found. Install it or pass --tool-command.`);
4850
+ }
4851
+ function supportsRequestedInvocationChannel(capability, invocationChannel) {
4852
+ if (invocationChannel === "auto") return capability.sdkAvailable || capability.commandAvailable;
4853
+ if (invocationChannel === "sdk") return capability.sdkAvailable;
4854
+ return capability.commandAvailable;
4855
+ }
4856
+ function hasModelSelection(options) {
4857
+ return Boolean(options.model || options.providerId || options.modelId || options.modelVariant || options.reasoningEffort && options.reasoningEffort !== "auto");
4858
+ }
4859
+ function modelSelectionValidationError(adapter, options) {
4860
+ if (!hasModelSelection(options)) return void 0;
4861
+ if (!adapter.supportsModelSelection) {
4862
+ return `Model selection is not supported by ${adapter.name}. Remove model configuration or choose a model-aware adapter.`;
4863
+ }
4864
+ if (!adapter.providerCatalog) return void 0;
4865
+ const selection = resolveModelSelectionParts(options);
4866
+ if (!selection.providerId && !selection.modelId) return void 0;
4867
+ if (selection.providerId) {
4868
+ const provider = adapter.providerCatalog[selection.providerId];
4869
+ if (!provider) return `Provider ${selection.providerId} is not available on ${adapter.name}.`;
4870
+ if (!selection.modelId) return void 0;
4871
+ const model = provider.models[selection.modelId];
4872
+ if (!model) return `Model ${selection.modelId} is not available for provider ${selection.providerId} on ${adapter.name}.`;
4873
+ return modelCapabilityValidationError(adapter, selection.providerId, model, options);
4874
+ }
4875
+ if (selection.modelId) {
4876
+ const match = findCatalogModel(adapter.providerCatalog, selection.modelId);
4877
+ if (!match) return `Model ${selection.modelId} is not available on ${adapter.name}.`;
4878
+ return modelCapabilityValidationError(adapter, match.providerId, match.model, options);
4879
+ }
4880
+ return void 0;
4881
+ }
4882
+ function resolveModelSelectionParts(options) {
4883
+ const providerId = options.providerId?.trim();
4884
+ const modelId = options.modelId?.trim();
4885
+ if (providerId || modelId) {
4886
+ return {
4887
+ ...providerId ? { providerId } : {},
4888
+ ...modelId ? { modelId } : {}
4889
+ };
4890
+ }
4891
+ const model = options.model?.trim();
4892
+ if (!model) return {};
4893
+ const separatorIndex = model.indexOf("/");
4894
+ if (separatorIndex > 0 && separatorIndex < model.length - 1) {
4895
+ return { providerId: model.slice(0, separatorIndex), modelId: model.slice(separatorIndex + 1) };
4896
+ }
4897
+ return { modelId: model };
3929
4898
  }
3930
- function supportsRequestedInvocationChannel(capability, invocationChannel) {
3931
- if (invocationChannel === "auto") return capability.sdkAvailable || capability.commandAvailable;
3932
- if (invocationChannel === "sdk") return capability.sdkAvailable;
3933
- return capability.commandAvailable;
4899
+ function findCatalogModel(catalog, modelId) {
4900
+ for (const [providerId, provider] of Object.entries(catalog)) {
4901
+ const model = provider.models[modelId];
4902
+ if (model) return { providerId, model };
4903
+ }
4904
+ return void 0;
4905
+ }
4906
+ function modelCapabilityValidationError(adapter, providerId, model, options) {
4907
+ const modelId = options.modelId ?? model.id;
4908
+ if (options.modelVariant && model.variants && !model.variants[options.modelVariant]) {
4909
+ return `Model variant ${options.modelVariant} is not available for ${providerId}/${modelId} on ${adapter.name}.`;
4910
+ }
4911
+ if (options.reasoningEffort && options.reasoningEffort !== "auto" && model.supportedReasoningEfforts?.length && !model.supportedReasoningEfforts.includes(options.reasoningEffort)) {
4912
+ return `${options.reasoningEffort} reasoning effort is not available for ${providerId}/${modelId} on ${adapter.name}.`;
4913
+ }
4914
+ return void 0;
3934
4915
  }
3935
4916
  async function isSdkAvailable(adapter) {
3936
4917
  if (!adapter.sdkPackageName || !adapter.runWithSdk) {
3937
4918
  return false;
3938
4919
  }
3939
- if (adapter.sdkRequiresExecutable && adapter.executable && !await commandExists(adapter.executable)) {
4920
+ if (adapter.sdkRequiresExecutable && adapter.executable && !await commandExists2(adapter.executable)) {
3940
4921
  return false;
3941
4922
  }
3942
4923
  return packageAvailable(adapter.sdkPackageName);
@@ -3949,13 +4930,9 @@ async function packageAvailable(packageName) {
3949
4930
  return false;
3950
4931
  }
3951
4932
  }
3952
- async function commandExists(command) {
3953
- const lookupCommand = process.platform === "win32" ? "where" : "which";
3954
- return new Promise((resolve) => {
3955
- const lookup = spawn(lookupCommand, [command], { stdio: "ignore" });
3956
- lookup.on("error", () => resolve(false));
3957
- lookup.on("close", (exitCode) => resolve(exitCode === 0));
3958
- });
4933
+ async function commandExists2(command) {
4934
+ const hostExecution = await getRuntimeHostExecutionPort();
4935
+ return hostExecution.commandExists(command);
3959
4936
  }
3960
4937
  async function detectRunnerProviderCatalog(adapter) {
3961
4938
  const localOpencodeConfigCatalog = adapter.name === "opencode" ? await loadLocalOpencodeProviderConfigCatalog() : void 0;
@@ -3963,16 +4940,16 @@ async function detectRunnerProviderCatalog(adapter) {
3963
4940
  }
3964
4941
  function localOpencodeProviderConfigPaths() {
3965
4942
  return [
3966
- path6.join(os3.homedir(), ".config", "opencode", "opencode.json"),
3967
- path6.join(os3.homedir(), ".config", "opencode", "config.json"),
3968
- path6.join(process.cwd(), "opencode.json")
4943
+ path7.join(os3.homedir(), ".config", "opencode", "opencode.json"),
4944
+ path7.join(os3.homedir(), ".config", "opencode", "config.json"),
4945
+ path7.join(process.cwd(), "opencode.json")
3969
4946
  ];
3970
4947
  }
3971
4948
  async function loadLocalOpencodeProviderConfigCatalog(configPaths = localOpencodeProviderConfigPaths()) {
3972
4949
  for (const configPath of configPaths) {
3973
4950
  try {
3974
- const parsed = JSON.parse(await readFile4(configPath, "utf8"));
3975
- const providerValue = isRecord(parsed) ? parsed.provider : void 0;
4951
+ const parsed = JSON.parse(await readFile5(configPath, "utf8"));
4952
+ const providerValue = isRecord3(parsed) ? parsed.provider : void 0;
3976
4953
  const catalog = sanitizeProviderCatalog(providerValue);
3977
4954
  if (catalog) return catalog;
3978
4955
  } catch {
@@ -3985,8 +4962,8 @@ function mergeProviderCatalogs(...catalogs) {
3985
4962
  for (const catalog of catalogs) {
3986
4963
  if (!catalog) continue;
3987
4964
  for (const [providerId, provider] of Object.entries(catalog)) {
3988
- const existing = isRecord(merged[providerId]) ? merged[providerId] : void 0;
3989
- const existingModels = existing && isRecord(existing.models) ? existing.models : {};
4965
+ const existing = isRecord3(merged[providerId]) ? merged[providerId] : void 0;
4966
+ const existingModels = existing && isRecord3(existing.models) ? existing.models : {};
3990
4967
  merged[providerId] = {
3991
4968
  ...existing,
3992
4969
  ...provider,
@@ -3999,7 +4976,7 @@ function mergeProviderCatalogs(...catalogs) {
3999
4976
  return parsed.success && Object.keys(parsed.data).length ? parsed.data : void 0;
4000
4977
  }
4001
4978
  function sanitizeProviderCatalog(value) {
4002
- if (!isRecord(value)) return void 0;
4979
+ if (!isRecord3(value)) return void 0;
4003
4980
  const catalog = {};
4004
4981
  for (const [providerId, providerValue] of Object.entries(value)) {
4005
4982
  const provider = sanitizeProviderConfig(providerId, providerValue);
@@ -4009,7 +4986,7 @@ function sanitizeProviderCatalog(value) {
4009
4986
  return parsed.success && Object.keys(parsed.data).length ? parsed.data : void 0;
4010
4987
  }
4011
4988
  function sanitizeProviderConfig(providerId, value) {
4012
- if (!isRecord(value)) return void 0;
4989
+ if (!isRecord3(value)) return void 0;
4013
4990
  const models = sanitizeProviderModels(value.models);
4014
4991
  if (!Object.keys(models).length) return void 0;
4015
4992
  return {
@@ -4020,7 +4997,7 @@ function sanitizeProviderConfig(providerId, value) {
4020
4997
  };
4021
4998
  }
4022
4999
  function sanitizeProviderModels(value) {
4023
- if (!isRecord(value)) return {};
5000
+ if (!isRecord3(value)) return {};
4024
5001
  const models = {};
4025
5002
  for (const [modelId, modelValue] of Object.entries(value)) {
4026
5003
  const model = sanitizeProviderModel(modelId, modelValue);
@@ -4029,7 +5006,7 @@ function sanitizeProviderModels(value) {
4029
5006
  return models;
4030
5007
  }
4031
5008
  function sanitizeProviderModel(modelId, value) {
4032
- if (!isRecord(value)) return void 0;
5009
+ if (!isRecord3(value)) return void 0;
4033
5010
  const reasoning = booleanValue(value.reasoning);
4034
5011
  const releaseDate = stringValue(value.release_date) ?? stringValue(value.releaseDate);
4035
5012
  const toolCall = booleanValue(value.tool_call) ?? booleanValue(value.toolCall);
@@ -4055,7 +5032,7 @@ function sanitizeProviderModel(modelId, value) {
4055
5032
  return model;
4056
5033
  }
4057
5034
  function sanitizeLimit(value) {
4058
- if (!isRecord(value)) return void 0;
5035
+ if (!isRecord3(value)) return void 0;
4059
5036
  const limit = {
4060
5037
  ...numberValue(value.context) !== void 0 ? { context: numberValue(value.context) } : {},
4061
5038
  ...numberValue(value.input) !== void 0 ? { input: numberValue(value.input) } : {},
@@ -4064,7 +5041,7 @@ function sanitizeLimit(value) {
4064
5041
  return Object.keys(limit).length ? limit : void 0;
4065
5042
  }
4066
5043
  function sanitizeModalities(value) {
4067
- if (!isRecord(value)) return void 0;
5044
+ if (!isRecord3(value)) return void 0;
4068
5045
  const modalities = {
4069
5046
  ...stringArrayValue(value.input) ? { input: stringArrayValue(value.input) } : {},
4070
5047
  ...stringArrayValue(value.output) ? { output: stringArrayValue(value.output) } : {}
@@ -4072,10 +5049,10 @@ function sanitizeModalities(value) {
4072
5049
  return Object.keys(modalities).length ? modalities : void 0;
4073
5050
  }
4074
5051
  function sanitizeVariants(value) {
4075
- if (!isRecord(value)) return void 0;
5052
+ if (!isRecord3(value)) return void 0;
4076
5053
  const variants = {};
4077
5054
  for (const [variantId, variantValue] of Object.entries(value)) {
4078
- variants[variantId] = isRecord(variantValue) && booleanValue(variantValue.disabled) !== void 0 ? { disabled: booleanValue(variantValue.disabled) } : {};
5055
+ variants[variantId] = isRecord3(variantValue) && booleanValue(variantValue.disabled) !== void 0 ? { disabled: booleanValue(variantValue.disabled) } : {};
4079
5056
  }
4080
5057
  return Object.keys(variants).length ? variants : void 0;
4081
5058
  }
@@ -4085,7 +5062,7 @@ function sanitizeReasoningEfforts(value) {
4085
5062
  const efforts = values.filter((candidate) => allReasoningEfforts.includes(candidate));
4086
5063
  return efforts.length ? efforts : void 0;
4087
5064
  }
4088
- function isRecord(value) {
5065
+ function isRecord3(value) {
4089
5066
  return typeof value === "object" && value !== null && !Array.isArray(value);
4090
5067
  }
4091
5068
  function stringValue(value) {
@@ -4101,66 +5078,38 @@ function stringArrayValue(value) {
4101
5078
  return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
4102
5079
  }
4103
5080
  async function executeToolInvocation(invocation, rootDir, streamOutput, timeoutMs) {
4104
- return new Promise((resolve, reject) => {
4105
- const child = spawn(invocation.command, invocation.args, {
4106
- cwd: rootDir,
4107
- env: process.env,
4108
- shell: invocation.shell ?? false,
4109
- stdio: ["pipe", "pipe", "pipe"]
4110
- });
4111
- let stdout = "";
4112
- let stderr = "";
4113
- let settled = false;
4114
- let forceKillTimer;
4115
- const timeout = timeoutMs && timeoutMs > 0 ? setTimeout(() => {
4116
- if (settled) return;
4117
- stderr += `${toolTimeoutMessage(invocation.displayCommand, timeoutMs)}
4118
- `;
4119
- child.kill("SIGTERM");
4120
- forceKillTimer = setTimeout(() => child.kill("SIGKILL"), 5e3);
4121
- forceKillTimer.unref?.();
4122
- rejectOnce(new Error(toolTimeoutMessage(invocation.displayCommand, timeoutMs)));
4123
- }, timeoutMs) : void 0;
4124
- timeout?.unref?.();
4125
- const resolveOnce = (value) => {
4126
- if (settled) return;
4127
- settled = true;
4128
- if (timeout) clearTimeout(timeout);
4129
- if (forceKillTimer) clearTimeout(forceKillTimer);
4130
- resolve(value);
4131
- };
4132
- const rejectOnce = (error) => {
4133
- if (settled) return;
4134
- settled = true;
4135
- if (timeout) clearTimeout(timeout);
4136
- if (forceKillTimer) clearTimeout(forceKillTimer);
4137
- reject(error);
4138
- };
4139
- child.on("error", rejectOnce);
4140
- child.stdout.setEncoding("utf8");
4141
- child.stderr.setEncoding("utf8");
4142
- child.stdout.on("data", (chunk) => {
4143
- stdout += chunk;
4144
- if (streamOutput) {
4145
- process.stdout.write(chunk);
4146
- }
4147
- });
4148
- child.stderr.on("data", (chunk) => {
4149
- stderr += chunk;
4150
- if (streamOutput) {
4151
- process.stderr.write(chunk);
4152
- }
4153
- });
4154
- child.stdin.on("error", () => void 0);
4155
- if (invocation.stdin) {
4156
- child.stdin.write(invocation.stdin);
4157
- }
4158
- child.stdin.end();
4159
- child.on("close", (exitCode) => {
4160
- if (forceKillTimer) clearTimeout(forceKillTimer);
4161
- resolveOnce({ exitCode: exitCode ?? 1, stdout, stderr });
4162
- });
5081
+ const hostExecution = await getRuntimeHostExecutionPort();
5082
+ const result = await hostExecution.executeCommand({
5083
+ command: invocation.command,
5084
+ args: invocation.args,
5085
+ cwd: rootDir,
5086
+ env: process.env,
5087
+ shell: invocation.shell ?? false,
5088
+ ...invocation.stdin ? { stdin: invocation.stdin } : {},
5089
+ ...timeoutMs ? { timeoutMs, timeoutMessage: toolTimeoutMessage(invocation.displayCommand, timeoutMs) } : {},
5090
+ streamOutput
4163
5091
  });
5092
+ if (result.status === "timedOut" || result.status === "spawnFailed" || result.status === "unsupported") {
5093
+ throw new Error(result.error?.message ?? `Command execution failed: ${invocation.displayCommand}`);
5094
+ }
5095
+ return { exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr };
5096
+ }
5097
+ function normalizeToolExecutionResult(result, rootDir) {
5098
+ return {
5099
+ ...result,
5100
+ stdout: normalizeLocalToolOutput(result.stdout, rootDir),
5101
+ stderr: normalizeLocalToolOutput(result.stderr, rootDir)
5102
+ };
5103
+ }
5104
+ function normalizeLocalToolOutput(value, rootDir) {
5105
+ const redacted = redactToolAdapterText(value, { executionRoot: rootDir, redaction: { localPaths: true, secretAssignments: true } });
5106
+ return truncateLocalToolOutput(redacted, localToolOutputBudgetBytes);
5107
+ }
5108
+ function truncateLocalToolOutput(value, budgetBytes) {
5109
+ if (Buffer.byteLength(value, "utf8") <= budgetBytes) return value;
5110
+ const suffix = "\n[truncated by Amistio local tool output budget]";
5111
+ const contentBudget = Math.max(0, budgetBytes - Buffer.byteLength(suffix, "utf8"));
5112
+ return `${Buffer.from(value, "utf8").subarray(0, contentBudget).toString("utf8")}${suffix}`;
4164
5113
  }
4165
5114
  async function withTimeout(promise, timeoutMs, displayCommand) {
4166
5115
  if (!timeoutMs || timeoutMs <= 0) {
@@ -4182,9 +5131,9 @@ async function withTimeout(promise, timeoutMs, displayCommand) {
4182
5131
  });
4183
5132
  }
4184
5133
  function toolTimeoutMessage(displayCommand, timeoutMs) {
4185
- return `Local tool timed out after ${formatTimeoutDuration(timeoutMs)}: ${displayCommand}`;
5134
+ return `Local tool timed out after ${formatTimeoutDuration2(timeoutMs)}: ${displayCommand}`;
4186
5135
  }
4187
- function formatTimeoutDuration(timeoutMs) {
5136
+ function formatTimeoutDuration2(timeoutMs) {
4188
5137
  if (timeoutMs < 1e3) {
4189
5138
  return `${timeoutMs}ms`;
4190
5139
  }
@@ -4246,7 +5195,7 @@ async function runClaudeSdk(input) {
4246
5195
  }
4247
5196
  }
4248
5197
  })) {
4249
- if (isRecord(message) && message.type === "result") {
5198
+ if (isRecord3(message) && message.type === "result") {
4250
5199
  if (message.subtype === "success" && typeof message.result === "string") {
4251
5200
  stdout += message.result;
4252
5201
  if (input.streamOutput) {
@@ -4332,9 +5281,9 @@ function extractTextParts(parts) {
4332
5281
  if (!Array.isArray(parts)) {
4333
5282
  return "";
4334
5283
  }
4335
- return parts.filter(isRecord).map((part) => part.type === "text" && typeof part.text === "string" ? part.text : "").filter(Boolean).join("\n");
5284
+ return parts.filter(isRecord3).map((part) => part.type === "text" && typeof part.text === "string" ? part.text : "").filter(Boolean).join("\n");
4336
5285
  }
4337
- function errorMessage(error) {
5286
+ function errorMessage3(error) {
4338
5287
  return error instanceof Error ? error.message : String(error);
4339
5288
  }
4340
5289
  function shellQuote(value) {
@@ -4345,14 +5294,14 @@ function shellQuote(value) {
4345
5294
  import { spawn as spawn2 } from "node:child_process";
4346
5295
  import { createHash as createHash3 } from "node:crypto";
4347
5296
  import { openSync } from "node:fs";
4348
- import { mkdir as mkdir6, readdir as readdir3, readFile as readFile5, writeFile as writeFile6 } from "node:fs/promises";
5297
+ import { mkdir as mkdir7, readdir as readdir4, readFile as readFile6, writeFile as writeFile7 } from "node:fs/promises";
4349
5298
  import os4 from "node:os";
4350
- import path7 from "node:path";
5299
+ import path8 from "node:path";
4351
5300
  function currentRunnerMode() {
4352
5301
  return process.env.AMISTIO_RUNNER_MODE === "background" ? "background" : "foreground";
4353
5302
  }
4354
5303
  function defaultRunnerMetadataDir() {
4355
- return path7.join(os4.homedir(), ".config", "amistio", "runners");
5304
+ return path8.join(os4.homedir(), ".config", "amistio", "runners");
4356
5305
  }
4357
5306
  function updatedCliRunnerLaunchOptions() {
4358
5307
  return { executablePath: "amistio", directExecutable: true };
@@ -4369,8 +5318,8 @@ async function startRunnerDaemon(input) {
4369
5318
  if (existing?.status === "running" && isProcessRunning(existing.pid)) {
4370
5319
  throw new Error(`Background runner ${existing.runnerId} is already running with PID ${existing.pid}.`);
4371
5320
  }
4372
- await mkdir6(metadataDir, { recursive: true });
4373
- const logPath = path7.join(metadataDir, `${runnerDaemonKey(input)}.log`);
5321
+ await mkdir7(metadataDir, { recursive: true });
5322
+ const logPath = path8.join(metadataDir, `${runnerDaemonKey(input)}.log`);
4374
5323
  const logFd = openSync(logPath, "a");
4375
5324
  const launch = resolveRunnerDaemonLaunch({
4376
5325
  args: input.args,
@@ -4397,7 +5346,7 @@ async function startRunnerDaemon(input) {
4397
5346
  projectId: input.projectId,
4398
5347
  repositoryLinkId: input.repositoryLinkId,
4399
5348
  runnerId: input.runnerId,
4400
- rootDir: path7.resolve(input.rootDir),
5349
+ rootDir: path8.resolve(input.rootDir),
4401
5350
  apiUrl: input.apiUrl,
4402
5351
  pid: child.pid,
4403
5352
  status: "running",
@@ -4411,8 +5360,8 @@ async function startRunnerDaemon(input) {
4411
5360
  }
4412
5361
  async function restartRunnerDaemonProcess(metadata, args, input = {}) {
4413
5362
  const metadataDir = input.metadataDir ?? defaultRunnerMetadataDir();
4414
- await mkdir6(metadataDir, { recursive: true });
4415
- const logPath = metadata.logPath ?? path7.join(metadataDir, `${runnerDaemonKey(metadata)}.log`);
5363
+ await mkdir7(metadataDir, { recursive: true });
5364
+ const logPath = metadata.logPath ?? path8.join(metadataDir, `${runnerDaemonKey(metadata)}.log`);
4416
5365
  const logFd = openSync(logPath, "a");
4417
5366
  const launch = resolveRunnerDaemonLaunch({
4418
5367
  args,
@@ -4449,12 +5398,12 @@ async function restartRunnerDaemonProcess(metadata, args, input = {}) {
4449
5398
  async function listRunnerDaemonMetadata(input, metadataDir = defaultRunnerMetadataDir()) {
4450
5399
  let entries;
4451
5400
  try {
4452
- entries = await readdir3(metadataDir);
5401
+ entries = await readdir4(metadataDir);
4453
5402
  } catch {
4454
5403
  return [];
4455
5404
  }
4456
5405
  const records = await Promise.all(
4457
- entries.filter((entry) => entry.endsWith(".json")).map(async (entry) => readRunnerDaemonMetadataFile(path7.join(metadataDir, entry)))
5406
+ entries.filter((entry) => entry.endsWith(".json")).map(async (entry) => readRunnerDaemonMetadataFile(path8.join(metadataDir, entry)))
4458
5407
  );
4459
5408
  return records.filter((record) => Boolean(record)).filter((record) => record.accountId === input.accountId && record.projectId === input.projectId && record.repositoryLinkId === input.repositoryLinkId).filter((record) => !input.runnerId || record.runnerId === input.runnerId).sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt));
4460
5409
  }
@@ -4462,8 +5411,8 @@ async function readRunnerDaemonMetadata(input, metadataDir = defaultRunnerMetada
4462
5411
  return readRunnerDaemonMetadataFile(runnerDaemonMetadataPath(input, metadataDir));
4463
5412
  }
4464
5413
  async function writeRunnerDaemonMetadata(metadata, metadataDir = defaultRunnerMetadataDir()) {
4465
- await mkdir6(metadataDir, { recursive: true });
4466
- await writeFile6(runnerDaemonMetadataPath(metadata, metadataDir), JSON.stringify(metadata, null, 2), { encoding: "utf8", mode: 384 });
5414
+ await mkdir7(metadataDir, { recursive: true });
5415
+ await writeFile7(runnerDaemonMetadataPath(metadata, metadataDir), JSON.stringify(metadata, null, 2), { encoding: "utf8", mode: 384 });
4467
5416
  }
4468
5417
  async function markRunnerDaemonStopped(metadata, metadataDir = defaultRunnerMetadataDir()) {
4469
5418
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -4525,14 +5474,14 @@ function runnerDaemonUptime(metadata, now = Date.now()) {
4525
5474
  return `${seconds}s`;
4526
5475
  }
4527
5476
  function runnerDaemonMetadataPath(input, metadataDir) {
4528
- return path7.join(metadataDir, `${runnerDaemonKey(input)}.json`);
5477
+ return path8.join(metadataDir, `${runnerDaemonKey(input)}.json`);
4529
5478
  }
4530
5479
  function runnerDaemonKey(input) {
4531
5480
  return createHash3("sha256").update(`${input.accountId}:${input.projectId}:${input.repositoryLinkId}:${input.runnerId}`).digest("hex");
4532
5481
  }
4533
5482
  async function readRunnerDaemonMetadataFile(filePath) {
4534
5483
  try {
4535
- const parsed = JSON.parse(await readFile5(filePath, "utf8"));
5484
+ const parsed = JSON.parse(await readFile6(filePath, "utf8"));
4536
5485
  if (parsed.schemaVersion !== 1 || !parsed.runnerId || !parsed.projectId || !parsed.repositoryLinkId) {
4537
5486
  return void 0;
4538
5487
  }
@@ -4545,9 +5494,9 @@ async function readRunnerDaemonMetadataFile(filePath) {
4545
5494
  // src/runner-service.ts
4546
5495
  import { spawn as spawn3 } from "node:child_process";
4547
5496
  import { createHash as createHash4 } from "node:crypto";
4548
- import { mkdir as mkdir7, readFile as readFile6, rm as rm3, writeFile as writeFile7 } from "node:fs/promises";
5497
+ import { mkdir as mkdir8, readFile as readFile7, rm as rm3, writeFile as writeFile8 } from "node:fs/promises";
4549
5498
  import os5 from "node:os";
4550
- import path8 from "node:path";
5499
+ import path9 from "node:path";
4551
5500
  function detectRunnerServicePlatform(platform = process.platform) {
4552
5501
  if (platform === "darwin") return "launchd";
4553
5502
  if (platform === "linux") return "systemd";
@@ -4563,14 +5512,14 @@ function createRunnerServiceDescriptor(input) {
4563
5512
  const serviceFilePath = runnerServiceFilePath(platform, serviceName, homeDir);
4564
5513
  const now = (/* @__PURE__ */ new Date()).toISOString();
4565
5514
  const command = [input.executablePath ?? process.execPath, input.scriptPath ?? process.argv[1], ...input.args];
4566
- const logPath = path8.join(input.metadataDir ?? defaultRunnerMetadataDir(), `${runnerServiceKey(input)}.service.log`);
5515
+ const logPath = path9.join(input.metadataDir ?? defaultRunnerMetadataDir(), `${runnerServiceKey(input)}.service.log`);
4567
5516
  const metadata = {
4568
5517
  schemaVersion: 1,
4569
5518
  accountId: input.accountId,
4570
5519
  projectId: input.projectId,
4571
5520
  repositoryLinkId: input.repositoryLinkId,
4572
5521
  runnerId: input.runnerId,
4573
- rootDir: path8.resolve(input.rootDir),
5522
+ rootDir: path9.resolve(input.rootDir),
4574
5523
  apiUrl: input.apiUrl,
4575
5524
  serviceName,
4576
5525
  serviceFilePath,
@@ -4587,9 +5536,9 @@ function createRunnerServiceDescriptor(input) {
4587
5536
  }
4588
5537
  async function installRunnerService(input, options = {}) {
4589
5538
  const descriptor = createRunnerServiceDescriptor(input);
4590
- await mkdir7(path8.dirname(descriptor.metadata.serviceFilePath), { recursive: true });
4591
- await mkdir7(input.metadataDir ?? defaultRunnerMetadataDir(), { recursive: true });
4592
- await writeFile7(descriptor.metadata.serviceFilePath, descriptor.content, { encoding: "utf8", mode: 384 });
5539
+ await mkdir8(path9.dirname(descriptor.metadata.serviceFilePath), { recursive: true });
5540
+ await mkdir8(input.metadataDir ?? defaultRunnerMetadataDir(), { recursive: true });
5541
+ await writeFile8(descriptor.metadata.serviceFilePath, descriptor.content, { encoding: "utf8", mode: 384 });
4593
5542
  await writeRunnerServiceMetadata(descriptor.metadata, input.metadataDir);
4594
5543
  if (options.activate !== false) {
4595
5544
  const activation = await activateRunnerService(descriptor.metadata);
@@ -4611,7 +5560,7 @@ async function removeRunnerService(input) {
4611
5560
  }
4612
5561
  async function readRunnerServiceMetadata(input, metadataDir = defaultRunnerMetadataDir()) {
4613
5562
  try {
4614
- const parsed = JSON.parse(await readFile6(runnerServiceMetadataPath(input, metadataDir), "utf8"));
5563
+ const parsed = JSON.parse(await readFile7(runnerServiceMetadataPath(input, metadataDir), "utf8"));
4615
5564
  if (parsed.schemaVersion !== 1 || !parsed.serviceName || !parsed.serviceFilePath) {
4616
5565
  return void 0;
4617
5566
  }
@@ -4621,8 +5570,8 @@ async function readRunnerServiceMetadata(input, metadataDir = defaultRunnerMetad
4621
5570
  }
4622
5571
  }
4623
5572
  async function writeRunnerServiceMetadata(metadata, metadataDir = defaultRunnerMetadataDir()) {
4624
- await mkdir7(metadataDir, { recursive: true });
4625
- await writeFile7(runnerServiceMetadataPath(metadata, metadataDir), JSON.stringify(metadata, null, 2), { encoding: "utf8", mode: 384 });
5573
+ await mkdir8(metadataDir, { recursive: true });
5574
+ await writeFile8(runnerServiceMetadataPath(metadata, metadataDir), JSON.stringify(metadata, null, 2), { encoding: "utf8", mode: 384 });
4626
5575
  }
4627
5576
  async function runnerServiceRuntimeStatus(metadata) {
4628
5577
  if (metadata.platform === "launchd") {
@@ -4705,12 +5654,12 @@ WantedBy=default.target
4705
5654
  }
4706
5655
  function runnerServiceFilePath(platform, serviceName, homeDir) {
4707
5656
  if (platform === "launchd") {
4708
- return path8.join(homeDir, "Library", "LaunchAgents", `${serviceName}.plist`);
5657
+ return path9.join(homeDir, "Library", "LaunchAgents", `${serviceName}.plist`);
4709
5658
  }
4710
- return path8.join(homeDir, ".config", "systemd", "user", `${serviceName}.service`);
5659
+ return path9.join(homeDir, ".config", "systemd", "user", `${serviceName}.service`);
4711
5660
  }
4712
5661
  function runnerServiceMetadataPath(input, metadataDir) {
4713
- return path8.join(metadataDir, `${runnerServiceKey(input)}.service.json`);
5662
+ return path9.join(metadataDir, `${runnerServiceKey(input)}.service.json`);
4714
5663
  }
4715
5664
  function runnerServiceName(input) {
4716
5665
  return `com.amistio.runner.${runnerServiceKey(input).slice(0, 20)}`;
@@ -4769,7 +5718,7 @@ function completedToolSessionClosedReason(session) {
4769
5718
  return "Completed one-shot tool run; this session is not reusable.";
4770
5719
  }
4771
5720
  function staleToolSessionClosedReason(session, now = /* @__PURE__ */ new Date()) {
4772
- if (session.status !== "open") {
5721
+ if (session.status !== "open" && session.status !== "active") {
4773
5722
  return void 0;
4774
5723
  }
4775
5724
  const lastActivityMs = Date.parse(session.lastActivityAt);
@@ -5045,8 +5994,8 @@ function createSmokeSession({ now, status, lastActivityAt }) {
5045
5994
  // src/sync.ts
5046
5995
  import { execFile as execFile3 } from "node:child_process";
5047
5996
  import { createHash as createHash5 } from "node:crypto";
5048
- import { mkdir as mkdir8, readdir as readdir4, readFile as readFile7, stat as stat3, writeFile as writeFile8 } from "node:fs/promises";
5049
- import path9 from "node:path";
5997
+ import { mkdir as mkdir9, readdir as readdir5, readFile as readFile8, stat as stat4, writeFile as writeFile9 } from "node:fs/promises";
5998
+ import path10 from "node:path";
5050
5999
  import { promisify as promisify3 } from "node:util";
5051
6000
  var execFileAsync3 = promisify3(execFile3);
5052
6001
  var legacySyncRoots = ["architecture", "context", "decisions", "features", "memory", "plans", "prompts", "workflows"];
@@ -5142,7 +6091,7 @@ async function readLocalSyncedDocuments(rootDir) {
5142
6091
  const documentFiles = await findBrainDocumentFiles(rootDir);
5143
6092
  const documents = [];
5144
6093
  for (const fullPath of documentFiles) {
5145
- const raw = await readFile7(fullPath, "utf8");
6094
+ const raw = await readFile8(fullPath, "utf8");
5146
6095
  const repoPath = toRepoPath(rootDir, fullPath);
5147
6096
  const parsed = parseSyncedDocument(raw, repoPath);
5148
6097
  if (!parsed) {
@@ -5192,8 +6141,8 @@ async function materializeBrainDocuments(rootDir, documents, options = {}) {
5192
6141
  result.skipped.push(document.repoPath);
5193
6142
  continue;
5194
6143
  }
5195
- await mkdir8(path9.dirname(fullPath), { recursive: true });
5196
- await writeFile8(fullPath, createSyncedDocumentContent(document), "utf8");
6144
+ await mkdir9(path10.dirname(fullPath), { recursive: true });
6145
+ await writeFile9(fullPath, createSyncedDocumentContent(document), "utf8");
5197
6146
  result.written.push(document.repoPath);
5198
6147
  }
5199
6148
  return result;
@@ -5322,7 +6271,7 @@ function parseSyncedHtml(content) {
5322
6271
  }
5323
6272
  async function readExistingSyncedDocument(fullPath) {
5324
6273
  try {
5325
- const raw = await readFile7(fullPath, "utf8");
6274
+ const raw = await readFile8(fullPath, "utf8");
5326
6275
  const parsed = parseSyncedDocument(raw, fullPath);
5327
6276
  if (!parsed) {
5328
6277
  return { exists: true };
@@ -5347,7 +6296,7 @@ async function readExistingSyncedDocument(fullPath) {
5347
6296
  async function findBrainDocumentFiles(rootDir) {
5348
6297
  const files = [];
5349
6298
  for (const syncRoot of [...syncRoots, htmlSyncRoot]) {
5350
- const fullRoot = path9.join(rootDir, syncRoot);
6299
+ const fullRoot = path10.join(rootDir, syncRoot);
5351
6300
  if (!await exists2(fullRoot)) {
5352
6301
  continue;
5353
6302
  }
@@ -5356,8 +6305,8 @@ async function findBrainDocumentFiles(rootDir) {
5356
6305
  return files;
5357
6306
  }
5358
6307
  async function walkBrainDocumentFiles(directory, files) {
5359
- for (const entry of await readdir4(directory, { withFileTypes: true })) {
5360
- const fullPath = path9.join(directory, entry.name);
6308
+ for (const entry of await readdir5(directory, { withFileTypes: true })) {
6309
+ const fullPath = path10.join(directory, entry.name);
5361
6310
  if (entry.isDirectory()) {
5362
6311
  await walkBrainDocumentFiles(fullPath, files);
5363
6312
  } else if (entry.isFile() && /\.(md|mdx|html?)$/i.test(entry.name)) {
@@ -5366,23 +6315,23 @@ async function walkBrainDocumentFiles(directory, files) {
5366
6315
  }
5367
6316
  }
5368
6317
  function safeRepoPath(rootDir, repoPath) {
5369
- if (path9.isAbsolute(repoPath)) {
6318
+ if (path10.isAbsolute(repoPath)) {
5370
6319
  throw new Error(`Refusing to use absolute repo path: ${repoPath}`);
5371
6320
  }
5372
- const normalized = path9.normalize(repoPath);
5373
- if (normalized === ".." || normalized.startsWith(`..${path9.sep}`)) {
6321
+ const normalized = path10.normalize(repoPath);
6322
+ if (normalized === ".." || normalized.startsWith(`..${path10.sep}`)) {
5374
6323
  throw new Error(`Refusing to use path outside the repository: ${repoPath}`);
5375
6324
  }
5376
- const root = path9.resolve(rootDir);
5377
- const fullPath = path9.resolve(root, normalized);
5378
- if (!fullPath.startsWith(`${root}${path9.sep}`)) {
6325
+ const root = path10.resolve(rootDir);
6326
+ const fullPath = path10.resolve(root, normalized);
6327
+ if (!fullPath.startsWith(`${root}${path10.sep}`)) {
5379
6328
  throw new Error(`Refusing to use path outside the repository: ${repoPath}`);
5380
6329
  }
5381
6330
  return fullPath;
5382
6331
  }
5383
6332
  function isControlPlanePath(repoPath) {
5384
- const normalized = path9.normalize(repoPath);
5385
- return syncRoots.some((syncRoot) => normalized === syncRoot || normalized.startsWith(`${syncRoot}${path9.sep}`)) || normalized === htmlSyncRoot || normalized.startsWith(`${htmlSyncRoot}${path9.sep}`);
6333
+ const normalized = path10.normalize(repoPath);
6334
+ return syncRoots.some((syncRoot) => normalized === syncRoot || normalized.startsWith(`${syncRoot}${path10.sep}`)) || normalized === htmlSyncRoot || normalized.startsWith(`${htmlSyncRoot}${path10.sep}`);
5386
6335
  }
5387
6336
  function canonicalControlPlaneRepoPath(repoPath) {
5388
6337
  const normalized = repoPath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\/+/, "");
@@ -5393,16 +6342,16 @@ function canonicalControlPlaneRepoPath(repoPath) {
5393
6342
  return normalized;
5394
6343
  }
5395
6344
  function toRepoPath(rootDir, fullPath) {
5396
- return path9.relative(rootDir, fullPath).split(path9.sep).join("/");
6345
+ return path10.relative(rootDir, fullPath).split(path10.sep).join("/");
5397
6346
  }
5398
6347
  function inferTitle(content, repoPath) {
5399
6348
  const heading = content.split("\n").find((line) => line.startsWith("# "))?.replace(/^#\s+/, "").trim();
5400
6349
  if (heading) return heading;
5401
6350
  const htmlHeading = content.match(/<h1\b[^>]*>([\s\S]*?)<\/h1>/i)?.[1]?.replace(/<[^>]+>/g, "").trim();
5402
- return htmlHeading || path9.basename(repoPath, path9.extname(repoPath));
6351
+ return htmlHeading || path10.basename(repoPath, path10.extname(repoPath));
5403
6352
  }
5404
6353
  async function collectExternalBrainDocumentsForPush(rootDir, metadata, existingDocuments, options) {
5405
- const root = path9.resolve(rootDir);
6354
+ const root = path10.resolve(rootDir);
5406
6355
  const maxBytes = (options.maxFileKb ?? defaultAutoSyncMaxFileKb) * 1024;
5407
6356
  const syncedAt = options.syncedAt ?? (/* @__PURE__ */ new Date()).toISOString();
5408
6357
  const existingById = new Map(existingDocuments.map((document) => [document.documentId, document]));
@@ -5418,7 +6367,7 @@ async function collectExternalBrainDocumentsForPush(rootDir, metadata, existingD
5418
6367
  continue;
5419
6368
  }
5420
6369
  const fullPath = safeRepoPath(root, normalizedRepoPath);
5421
- const fileStat = await stat3(fullPath).catch(() => void 0);
6370
+ const fileStat = await stat4(fullPath).catch(() => void 0);
5422
6371
  if (!fileStat?.isFile()) {
5423
6372
  skipped.push({ repoPath: normalizedRepoPath, reason: "unreadable" });
5424
6373
  continue;
@@ -5427,7 +6376,7 @@ async function collectExternalBrainDocumentsForPush(rootDir, metadata, existingD
5427
6376
  skipped.push({ repoPath: normalizedRepoPath, reason: "tooLarge" });
5428
6377
  continue;
5429
6378
  }
5430
- const content = await readFile7(fullPath, "utf8").catch(() => void 0);
6379
+ const content = await readFile8(fullPath, "utf8").catch(() => void 0);
5431
6380
  if (content === void 0) {
5432
6381
  skipped.push({ repoPath: normalizedRepoPath, reason: "unreadable" });
5433
6382
  continue;
@@ -5492,7 +6441,7 @@ async function listAutoSyncCandidatePaths(rootDir) {
5492
6441
  }
5493
6442
  const files = [];
5494
6443
  for (const syncRoot of [...syncRoots, htmlSyncRoot, ...legacySyncRoots]) {
5495
- const fullRoot = path9.join(rootDir, syncRoot);
6444
+ const fullRoot = path10.join(rootDir, syncRoot);
5496
6445
  if (await exists2(fullRoot)) {
5497
6446
  await walkAutoSyncFiles(rootDir, fullRoot, files);
5498
6447
  }
@@ -5500,9 +6449,9 @@ async function listAutoSyncCandidatePaths(rootDir) {
5500
6449
  return uniqueSortedRepoPaths(files);
5501
6450
  }
5502
6451
  async function walkAutoSyncFiles(rootDir, directory, files) {
5503
- for (const entry of await readdir4(directory, { withFileTypes: true }).catch(() => [])) {
5504
- const fullPath = path9.join(directory, entry.name);
5505
- const repoPath = normalizeRepoPath3(path9.relative(rootDir, fullPath));
6452
+ for (const entry of await readdir5(directory, { withFileTypes: true }).catch(() => [])) {
6453
+ const fullPath = path10.join(directory, entry.name);
6454
+ const repoPath = normalizeRepoPath3(path10.relative(rootDir, fullPath));
5506
6455
  if (entry.isDirectory()) {
5507
6456
  if (!autoSyncExcludedDirectoryNames.has(entry.name)) {
5508
6457
  await walkAutoSyncFiles(rootDir, fullPath, files);
@@ -5556,7 +6505,7 @@ function parseFrontmatterFromSyncedDocument(frontmatter) {
5556
6505
  }
5557
6506
  async function exists2(filePath) {
5558
6507
  try {
5559
- await stat3(filePath);
6508
+ await stat4(filePath);
5560
6509
  return true;
5561
6510
  } catch {
5562
6511
  return false;
@@ -5564,9 +6513,9 @@ async function exists2(filePath) {
5564
6513
  }
5565
6514
 
5566
6515
  // src/tool-session-store.ts
5567
- import { mkdir as mkdir9, readFile as readFile8, writeFile as writeFile9 } from "node:fs/promises";
6516
+ import { mkdir as mkdir10, readFile as readFile9, writeFile as writeFile10 } from "node:fs/promises";
5568
6517
  import os6 from "node:os";
5569
- import path10 from "node:path";
6518
+ import path11 from "node:path";
5570
6519
  var LocalToolSessionStore = class {
5571
6520
  constructor(filePath = defaultSessionStorePath()) {
5572
6521
  this.filePath = filePath;
@@ -5580,12 +6529,12 @@ var LocalToolSessionStore = class {
5580
6529
  async setProviderSessionId(toolSessionId, toolName, providerSessionId) {
5581
6530
  const data = await this.read();
5582
6531
  data[toolSessionId] = { toolName, providerSessionId, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
5583
- await mkdir9(path10.dirname(this.filePath), { recursive: true });
5584
- await writeFile9(this.filePath, JSON.stringify(data, null, 2), "utf8");
6532
+ await mkdir10(path11.dirname(this.filePath), { recursive: true });
6533
+ await writeFile10(this.filePath, JSON.stringify(data, null, 2), "utf8");
5585
6534
  }
5586
6535
  async read() {
5587
6536
  try {
5588
- return JSON.parse(await readFile8(this.filePath, "utf8"));
6537
+ return JSON.parse(await readFile9(this.filePath, "utf8"));
5589
6538
  } catch {
5590
6539
  return {};
5591
6540
  }
@@ -5593,16 +6542,16 @@ var LocalToolSessionStore = class {
5593
6542
  };
5594
6543
  function defaultSessionStorePath() {
5595
6544
  if (process.platform === "darwin") {
5596
- return path10.join(os6.homedir(), "Library", "Application Support", "Amistio", "tool-sessions.json");
6545
+ return path11.join(os6.homedir(), "Library", "Application Support", "Amistio", "tool-sessions.json");
5597
6546
  }
5598
6547
  if (process.platform === "win32") {
5599
- return path10.join(process.env.APPDATA ?? os6.homedir(), "Amistio", "tool-sessions.json");
6548
+ return path11.join(process.env.APPDATA ?? os6.homedir(), "Amistio", "tool-sessions.json");
5600
6549
  }
5601
- return path10.join(process.env.XDG_STATE_HOME ?? path10.join(os6.homedir(), ".local", "state"), "amistio", "tool-sessions.json");
6550
+ return path11.join(process.env.XDG_STATE_HOME ?? path11.join(os6.homedir(), ".local", "state"), "amistio", "tool-sessions.json");
5602
6551
  }
5603
6552
 
5604
6553
  // src/work-runner.ts
5605
- import path11 from "node:path";
6554
+ import path12 from "node:path";
5606
6555
  var generationResultStart = "AMISTIO_BRAIN_GENERATION_RESULT_START";
5607
6556
  var generationResultEnd = "AMISTIO_BRAIN_GENERATION_RESULT_END";
5608
6557
  var assistantAnswerStart = "AMISTIO_ASSISTANT_ANSWER_START";
@@ -7085,15 +8034,15 @@ function normalizeProjectContextRepoPath(value, options) {
7085
8034
  if (!trimmed || /^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(trimmed) || /^file:/i.test(trimmed) || /^[A-Za-z]:($|[^\\/])/.test(trimmed)) {
7086
8035
  throwUnsafeProjectContextPath();
7087
8036
  }
7088
- const absolute = trimmed.startsWith("/") || trimmed.startsWith("\\\\") || path11.isAbsolute(trimmed) || path11.win32.isAbsolute(trimmed);
8037
+ const absolute = trimmed.startsWith("/") || trimmed.startsWith("\\\\") || path12.isAbsolute(trimmed) || path12.win32.isAbsolute(trimmed);
7089
8038
  if (!absolute) {
7090
8039
  return normalizeRelativeProjectContextPath(trimmed);
7091
8040
  }
7092
8041
  if (!options.repositoryRoot) {
7093
8042
  throwUnsafeProjectContextPath();
7094
8043
  }
7095
- const useWindowsPathRules = path11.win32.isAbsolute(trimmed) && !trimmed.startsWith("/");
7096
- const relativePath = useWindowsPathRules ? path11.win32.relative(path11.win32.resolve(options.repositoryRoot), path11.win32.resolve(trimmed)) : path11.relative(path11.resolve(options.repositoryRoot), path11.resolve(trimmed));
8044
+ const useWindowsPathRules = path12.win32.isAbsolute(trimmed) && !trimmed.startsWith("/");
8045
+ const relativePath = useWindowsPathRules ? path12.win32.relative(path12.win32.resolve(options.repositoryRoot), path12.win32.resolve(trimmed)) : path12.relative(path12.resolve(options.repositoryRoot), path12.resolve(trimmed));
7097
8046
  return normalizeRelativeProjectContextPath(relativePath);
7098
8047
  }
7099
8048
  function normalizeRelativeProjectContextPath(value) {
@@ -7298,8 +8247,8 @@ function roundNumber(value, digits) {
7298
8247
  // src/importer.ts
7299
8248
  import { execFile as execFile4 } from "node:child_process";
7300
8249
  import { createHash as createHash7 } from "node:crypto";
7301
- import { readdir as readdir5, readFile as readFile9, stat as stat4 } from "node:fs/promises";
7302
- import path12 from "node:path";
8250
+ import { readdir as readdir6, readFile as readFile10, stat as stat5 } from "node:fs/promises";
8251
+ import path13 from "node:path";
7303
8252
  import { promisify as promisify4 } from "node:util";
7304
8253
  var execFileAsync4 = promisify4(execFile4);
7305
8254
  var defaultMaxFileKb = 256;
@@ -7320,12 +8269,12 @@ var documentFolderByType = {
7320
8269
  workflow: "docs/workflows"
7321
8270
  };
7322
8271
  async function inspectLocalRepository(rootDir, defaultBranch) {
7323
- const requestedRoot = path12.resolve(rootDir);
8272
+ const requestedRoot = path13.resolve(rootDir);
7324
8273
  const root = await runGit2(["-C", requestedRoot, "rev-parse", "--show-toplevel"]).catch(() => requestedRoot);
7325
8274
  const detectedBranch = await runGit2(["-C", root, "symbolic-ref", "--quiet", "--short", "HEAD"]).catch(() => defaultBranch);
7326
8275
  const originUrl = await runGit2(["-C", root, "remote", "get-url", "origin"]).catch(() => void 0);
7327
8276
  const parsedCloneUrl = originUrl ? parseOptionalOriginCloneUrl(originUrl) : void 0;
7328
- const repoName = (parsedCloneUrl?.repoName ?? path12.basename(root)) || "repository";
8277
+ const repoName = (parsedCloneUrl?.repoName ?? path13.basename(root)) || "repository";
7329
8278
  const fingerprintSeed = parsedCloneUrl ? `origin:${parsedCloneUrl.normalizedKey}` : `repo:${repoName}:${detectedBranch || defaultBranch}`;
7330
8279
  return {
7331
8280
  rootDir: root,
@@ -7337,7 +8286,7 @@ async function inspectLocalRepository(rootDir, defaultBranch) {
7337
8286
  };
7338
8287
  }
7339
8288
  async function scanLegacyDocuments(options) {
7340
- const rootDir = path12.resolve(options.rootDir);
8289
+ const rootDir = path13.resolve(options.rootDir);
7341
8290
  const maxBytes = (options.maxFileKb ?? defaultMaxFileKb) * 1024;
7342
8291
  const skipped = [];
7343
8292
  const candidates = [];
@@ -7357,8 +8306,8 @@ async function scanLegacyDocuments(options) {
7357
8306
  skipped.push({ repoPath, reason: "excluded" });
7358
8307
  continue;
7359
8308
  }
7360
- const fullPath = path12.join(rootDir, ...repoPath.split("/"));
7361
- const fileStat = await stat4(fullPath).catch(() => void 0);
8309
+ const fullPath = path13.join(rootDir, ...repoPath.split("/"));
8310
+ const fileStat = await stat5(fullPath).catch(() => void 0);
7362
8311
  if (!fileStat?.isFile()) {
7363
8312
  skipped.push({ repoPath, reason: "unreadable" });
7364
8313
  continue;
@@ -7367,7 +8316,7 @@ async function scanLegacyDocuments(options) {
7367
8316
  skipped.push({ repoPath, reason: "tooLarge" });
7368
8317
  continue;
7369
8318
  }
7370
- const content = await readFile9(fullPath, "utf8").catch(() => void 0);
8319
+ const content = await readFile10(fullPath, "utf8").catch(() => void 0);
7371
8320
  if (content === void 0) {
7372
8321
  skipped.push({ repoPath, reason: "unreadable" });
7373
8322
  continue;
@@ -7457,10 +8406,10 @@ async function listRepositoryPaths(rootDir) {
7457
8406
  return files;
7458
8407
  }
7459
8408
  async function walkRepository(rootDir, directory, files) {
7460
- const entries = await readdir5(directory, { withFileTypes: true }).catch(() => []);
8409
+ const entries = await readdir6(directory, { withFileTypes: true }).catch(() => []);
7461
8410
  for (const entry of entries) {
7462
- const fullPath = path12.join(directory, entry.name);
7463
- const repoPath = normalizeRepoPath4(path12.relative(rootDir, fullPath));
8411
+ const fullPath = path13.join(directory, entry.name);
8412
+ const repoPath = normalizeRepoPath4(path13.relative(rootDir, fullPath));
7464
8413
  if (entry.isDirectory()) {
7465
8414
  if (!excludedDirectoryNames.has(entry.name)) {
7466
8415
  await walkRepository(rootDir, fullPath, files);
@@ -7528,9 +8477,9 @@ function uniqueDestinationPath(basePath, sourcePath, usedPaths) {
7528
8477
  usedPaths.add(basePath);
7529
8478
  return basePath;
7530
8479
  }
7531
- const extension = path12.posix.extname(basePath) || ".md";
7532
- const directory = path12.posix.dirname(basePath);
7533
- const basename = path12.posix.basename(basePath, extension);
8480
+ const extension = path13.posix.extname(basePath) || ".md";
8481
+ const directory = path13.posix.dirname(basePath);
8482
+ const basename = path13.posix.basename(basePath, extension);
7534
8483
  const uniquePath = `${directory}/${basename}-${hashText(sourcePath, 8)}${extension}`;
7535
8484
  usedPaths.add(uniquePath);
7536
8485
  return uniquePath;
@@ -7603,7 +8552,7 @@ function inferTitle2(content, repoPath) {
7603
8552
  if (heading) return heading;
7604
8553
  const htmlHeading = body.match(/<h1\b[^>]*>([\s\S]*?)<\/h1>/i)?.[1]?.replace(/<[^>]+>/g, "").trim();
7605
8554
  if (htmlHeading) return htmlHeading;
7606
- const basename = path12.posix.basename(repoPath, path12.posix.extname(repoPath)).replace(/[-_]+/g, " ").trim();
8555
+ const basename = path13.posix.basename(repoPath, path13.posix.extname(repoPath)).replace(/[-_]+/g, " ").trim();
7607
8556
  return titleCase(basename || "Imported Document");
7608
8557
  }
7609
8558
  function stripFrontmatter(content) {
@@ -7637,7 +8586,7 @@ async function runGit2(args) {
7637
8586
 
7638
8587
  // src/runner-actions.ts
7639
8588
  import { spawn as spawn4 } from "node:child_process";
7640
- import path13 from "node:path";
8589
+ import path14 from "node:path";
7641
8590
  function buildBackgroundRunnerArgs(options) {
7642
8591
  const args = [
7643
8592
  "run",
@@ -7647,7 +8596,7 @@ function buildBackgroundRunnerArgs(options) {
7647
8596
  "--runner-id",
7648
8597
  options.runnerId,
7649
8598
  "--root",
7650
- path13.resolve(options.root),
8599
+ path14.resolve(options.root),
7651
8600
  "--session",
7652
8601
  options.session,
7653
8602
  "--interval-seconds",
@@ -7765,8 +8714,8 @@ function truncateProcessOutput(value) {
7765
8714
 
7766
8715
  // src/git-worktree.ts
7767
8716
  import { execFile as execFile5 } from "node:child_process";
7768
- import { copyFile, lstat, mkdir as mkdir10, readdir as readdir6, stat as stat5 } from "node:fs/promises";
7769
- import path14 from "node:path";
8717
+ import { copyFile, lstat, mkdir as mkdir11, readdir as readdir7, stat as stat6 } from "node:fs/promises";
8718
+ import path15 from "node:path";
7770
8719
  import { promisify as promisify5 } from "node:util";
7771
8720
  var execFileAsync5 = promisify5(execFile5);
7772
8721
  var exactLocalEnvironmentFiles = /* @__PURE__ */ new Set([".env", ".env.local", ".env.development", ".env.development.local", ".env.test", ".env.test.local", ".env.production", ".env.production.local"]);
@@ -7787,7 +8736,7 @@ function resolveWorktreeIdentity(workItem) {
7787
8736
  async function prepareGitWorktreeIsolation(rootDir, workItem) {
7788
8737
  const identity = resolveWorktreeIdentity(workItem);
7789
8738
  const repoRoot = await gitOutput(rootDir, ["rev-parse", "--show-toplevel"]).catch((error) => {
7790
- throw new Error(`Git worktree isolation requires a paired Git checkout: ${errorMessage2(error)}`);
8739
+ throw new Error(`Git worktree isolation requires a paired Git checkout: ${errorMessage4(error)}`);
7791
8740
  });
7792
8741
  const currentHead = await gitOutput(repoRoot, ["rev-parse", "HEAD"]);
7793
8742
  await assertBaseRevision(repoRoot, workItem.baseRevision, currentHead);
@@ -7798,11 +8747,11 @@ async function prepareGitWorktreeIsolation(rootDir, workItem) {
7798
8747
  const preparedLocalEnvironmentFileCount2 = await prepareLocalWorktreeEnvironment(repoRoot, worktreePath);
7799
8748
  return { ...identity, baseRevision, worktreePath, ...preparedLocalEnvironmentFileCount2 ? { preparedLocalEnvironmentFileCount: preparedLocalEnvironmentFileCount2 } : {} };
7800
8749
  }
7801
- await mkdir10(path14.dirname(worktreePath), { recursive: true });
8750
+ await mkdir11(path15.dirname(worktreePath), { recursive: true });
7802
8751
  const branchExists = await gitCommandSucceeds(repoRoot, ["show-ref", "--verify", "--quiet", `refs/heads/${identity.branch}`]);
7803
8752
  const worktreeArgs = branchExists ? ["worktree", "add", worktreePath, identity.branch] : ["worktree", "add", "-b", identity.branch, worktreePath, baseRevision];
7804
8753
  await gitOutput(repoRoot, worktreeArgs).catch((error) => {
7805
- throw new Error(`Could not create Git worktree ${identity.worktreeKey} on ${identity.branch}: ${errorMessage2(error)}`);
8754
+ throw new Error(`Could not create Git worktree ${identity.worktreeKey} on ${identity.branch}: ${errorMessage4(error)}`);
7806
8755
  });
7807
8756
  const preparedLocalEnvironmentFileCount = await prepareLocalWorktreeEnvironment(repoRoot, worktreePath);
7808
8757
  return { ...identity, baseRevision, worktreePath, ...preparedLocalEnvironmentFileCount ? { preparedLocalEnvironmentFileCount } : {} };
@@ -7816,9 +8765,9 @@ async function resolveExistingGitWorktreeIsolation(rootDir, workItem) {
7816
8765
  return { ...identity, baseRevision, worktreePath };
7817
8766
  }
7818
8767
  function localWorktreePath(repoRoot, worktreeKey) {
7819
- const repoName = path14.basename(repoRoot);
8768
+ const repoName = path15.basename(repoRoot);
7820
8769
  const worktreeSlug = worktreeKey.split("/").filter(Boolean).pop() ?? "work";
7821
- return path14.join(path14.dirname(repoRoot), `${repoName}.worktrees`, worktreeSlug);
8770
+ return path15.join(path15.dirname(repoRoot), `${repoName}.worktrees`, worktreeSlug);
7822
8771
  }
7823
8772
  async function assertExistingWorktree(worktreePath, branch) {
7824
8773
  await gitOutput(worktreePath, ["rev-parse", "--is-inside-work-tree"]);
@@ -7844,8 +8793,8 @@ async function prepareLocalWorktreeEnvironment(repoRoot, worktreePath) {
7844
8793
  const candidates = await localEnvironmentFileCandidates(repoRoot);
7845
8794
  let preparedCount = 0;
7846
8795
  for (const candidate of candidates) {
7847
- const sourcePath = path14.join(repoRoot, candidate);
7848
- const targetPath = path14.join(worktreePath, candidate);
8796
+ const sourcePath = path15.join(repoRoot, candidate);
8797
+ const targetPath = path15.join(worktreePath, candidate);
7849
8798
  if (await pathExists(targetPath)) {
7850
8799
  continue;
7851
8800
  }
@@ -7866,7 +8815,7 @@ async function prepareLocalWorktreeEnvironment(repoRoot, worktreePath) {
7866
8815
  }
7867
8816
  async function localEnvironmentFileCandidates(repoRoot) {
7868
8817
  const names = new Set(exactLocalEnvironmentFiles);
7869
- for (const entry of await readdir6(repoRoot)) {
8818
+ for (const entry of await readdir7(repoRoot)) {
7870
8819
  if (isAllowedLocalEnvironmentFile(entry)) {
7871
8820
  names.add(entry);
7872
8821
  }
@@ -7876,7 +8825,7 @@ async function localEnvironmentFileCandidates(repoRoot) {
7876
8825
  if (!isRootFileName(name)) {
7877
8826
  continue;
7878
8827
  }
7879
- const source = await lstat(path14.join(repoRoot, name)).catch(() => void 0);
8828
+ const source = await lstat(path15.join(repoRoot, name)).catch(() => void 0);
7880
8829
  if (source?.isFile()) {
7881
8830
  candidates.push(name);
7882
8831
  }
@@ -7887,7 +8836,7 @@ function isAllowedLocalEnvironmentFile(name) {
7887
8836
  return exactLocalEnvironmentFiles.has(name) || localEnvironmentFilePattern.test(name);
7888
8837
  }
7889
8838
  function isRootFileName(name) {
7890
- return name === path14.basename(name) && !name.includes("/") && !name.includes("\\");
8839
+ return name === path15.basename(name) && !name.includes("/") && !name.includes("\\");
7891
8840
  }
7892
8841
  async function gitOutput(cwd, args) {
7893
8842
  const { stdout } = await execFileAsync5("git", args, { cwd, maxBuffer: 1024 * 1024 });
@@ -7897,7 +8846,7 @@ async function gitCommandSucceeds(cwd, args) {
7897
8846
  return execFileAsync5("git", args, { cwd }).then(() => true, () => false);
7898
8847
  }
7899
8848
  async function pathExists(value) {
7900
- return stat5(value).then(() => true, () => false);
8849
+ return stat6(value).then(() => true, () => false);
7901
8850
  }
7902
8851
  function workIsolationSlug(scopeId, title) {
7903
8852
  const scopeSlug = slugify(scopeId);
@@ -7908,7 +8857,7 @@ function workIsolationSlug(scopeId, title) {
7908
8857
  function slugify(value) {
7909
8858
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "work";
7910
8859
  }
7911
- function errorMessage2(error) {
8860
+ function errorMessage4(error) {
7912
8861
  return error instanceof Error ? error.message : String(error);
7913
8862
  }
7914
8863
  function safeFileError(error) {
@@ -7920,7 +8869,7 @@ function safeFileError(error) {
7920
8869
 
7921
8870
  // src/implementation-handoff.ts
7922
8871
  import { execFile as execFile6 } from "node:child_process";
7923
- import path15 from "node:path";
8872
+ import path16 from "node:path";
7924
8873
  import { promisify as promisify6 } from "node:util";
7925
8874
  var execFileAsync6 = promisify6(execFile6);
7926
8875
  async function completeImplementationHandoff(input) {
@@ -8188,7 +9137,7 @@ async function cleanupWorktree(run, input) {
8188
9137
  return { status: "failed", message: "Cleanup skipped because the worktree is not clean after PR handoff." };
8189
9138
  }
8190
9139
  try {
8191
- await gitOutput2(run, input.primaryRepoRoot || path15.dirname(input.worktreePath), ["worktree", "remove", input.worktreePath]);
9140
+ await gitOutput2(run, input.primaryRepoRoot || path16.dirname(input.worktreePath), ["worktree", "remove", input.worktreePath]);
8192
9141
  return { status: "completed" };
8193
9142
  } catch (error) {
8194
9143
  return { status: "failed", message: `Cleanup failed: ${safeErrorMessage(error)}` };
@@ -8300,6 +9249,481 @@ function redactLocalPaths(value) {
8300
9249
  return value.replace(/(^|[\s'"`])(\/{1,2}(?:Users|home|private|tmp|var|Volumes)\/[^\s'"`]+)/g, "$1<local-path>").replace(/(^|[\s'"`])([A-Za-z]:\\[^\s'"`]+)/g, "$1<local-path>");
8301
9250
  }
8302
9251
 
9252
+ // src/direct-model-client.ts
9253
+ var githubModelsProviderId = "github-models";
9254
+ var githubModelsEndpoint = "https://models.github.ai/inference/chat/completions";
9255
+ var githubModelsSupportedModelIds = ["openai/gpt-4.1", "openai/gpt-4o", "openai/gpt-5"];
9256
+ function shouldUseDirectModelClient(options) {
9257
+ if (options.toolCommand) return false;
9258
+ if (options.providerId !== githubModelsProviderId) return false;
9259
+ return options.tool === void 0 || options.tool === "auto" || options.tool === "none";
9260
+ }
9261
+ function createDirectModelClientPreview(options) {
9262
+ const modelId = requireSupportedGithubModelsModelId(options.modelId);
9263
+ return {
9264
+ toolName: "amistio-direct",
9265
+ displayCommand: "GitHub Models Direct API <selected model>",
9266
+ supportsSessionReuse: false,
9267
+ resumabilityScope: "none",
9268
+ model: `${githubModelsProviderId}/${modelId}`,
9269
+ providerId: githubModelsProviderId,
9270
+ modelId,
9271
+ ...options.modelVariant ? { modelVariant: options.modelVariant } : {},
9272
+ ...options.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {}
9273
+ };
9274
+ }
9275
+ function resolveDirectModelClientPreference(options) {
9276
+ if (options.tool && options.tool !== "auto" && options.tool !== "none") {
9277
+ return void 0;
9278
+ }
9279
+ const modelFromCombinedPreference = options.model?.startsWith(`${githubModelsProviderId}/`) ? options.model.slice(`${githubModelsProviderId}/`.length) : void 0;
9280
+ const providerId = options.providerId ?? (modelFromCombinedPreference ? githubModelsProviderId : void 0);
9281
+ if (providerId !== githubModelsProviderId) {
9282
+ return void 0;
9283
+ }
9284
+ if (options.requestedInvocationChannel === "command") {
9285
+ return { ready: false, status: "channelUnsupported", message: "GitHub Models direct provider uses the Amistio direct client and does not support command invocation." };
9286
+ }
9287
+ let modelId;
9288
+ try {
9289
+ modelId = requireSupportedGithubModelsModelId(options.modelId ?? modelFromCombinedPreference);
9290
+ } catch (error) {
9291
+ return { ready: false, status: "modelUnsupported", message: errorMessage5(error) };
9292
+ }
9293
+ return {
9294
+ ready: true,
9295
+ config: {
9296
+ model: `${githubModelsProviderId}/${modelId}`,
9297
+ providerId: githubModelsProviderId,
9298
+ modelId,
9299
+ ...options.modelVariant ? { modelVariant: options.modelVariant } : {},
9300
+ ...options.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {}
9301
+ }
9302
+ };
9303
+ }
9304
+ async function createGithubModelsChatCompletion(options) {
9305
+ const modelId = requireSupportedGithubModelsModelId(options.modelId);
9306
+ const token = resolveGithubModelsToken(options.env ?? process.env);
9307
+ const requestInit = {
9308
+ method: "POST",
9309
+ headers: {
9310
+ authorization: `Bearer ${token}`,
9311
+ "content-type": "application/json"
9312
+ },
9313
+ body: JSON.stringify({
9314
+ model: modelId,
9315
+ messages: options.messages,
9316
+ ...options.tools?.length ? { tools: options.tools, tool_choice: "auto" } : {},
9317
+ ...options.reasoningEffort && options.reasoningEffort !== "auto" ? { reasoning_effort: options.reasoningEffort } : {}
9318
+ }),
9319
+ ...options.signal ? { signal: options.signal } : {}
9320
+ };
9321
+ const response = await (options.fetchImpl ?? fetch)(githubModelsEndpoint, requestInit);
9322
+ if (!response.ok) {
9323
+ throw new Error(`GitHub Models request failed with HTTP ${response.status}. Check local GitHub Models auth and selected model access.`);
9324
+ }
9325
+ return response.json();
9326
+ }
9327
+ function requireSupportedGithubModelsModelId(modelId) {
9328
+ const trimmed = modelId?.trim();
9329
+ if (!trimmed) {
9330
+ throw new Error("GitHub Models direct provider requires --provider github-models with --model-id.");
9331
+ }
9332
+ if (!githubModelsSupportedModelIds.includes(trimmed)) {
9333
+ throw new Error(`Unsupported GitHub Models direct model: ${trimmed}. Supported models: ${githubModelsSupportedModelIds.join(", ")}.`);
9334
+ }
9335
+ return trimmed;
9336
+ }
9337
+ function resolveGithubModelsToken(env) {
9338
+ const token = env.GITHUB_MODELS_TOKEN?.trim() || env.GITHUB_TOKEN?.trim();
9339
+ if (!token) {
9340
+ throw new Error("GitHub Models direct provider requires GITHUB_MODELS_TOKEN or GITHUB_TOKEN on this runner.");
9341
+ }
9342
+ return token;
9343
+ }
9344
+ function errorMessage5(error) {
9345
+ return error instanceof Error ? error.message : String(error);
9346
+ }
9347
+
9348
+ // src/amistio-direct-agent.ts
9349
+ var defaultMaxToolRounds = 8;
9350
+ async function runAmistioDirectAgent(options) {
9351
+ const preview = createDirectModelClientPreview(options);
9352
+ const messages = [
9353
+ { role: "system", content: directAgentSystemPrompt(options.toolPolicy) },
9354
+ { role: "user", content: options.prompt }
9355
+ ];
9356
+ let tokensIn = 0;
9357
+ let tokensOut = 0;
9358
+ let lastAssistantContent = "";
9359
+ const maxToolRounds = positiveInteger3(options.maxToolRounds) ?? defaultMaxToolRounds;
9360
+ for (let round = 0; round <= maxToolRounds; round += 1) {
9361
+ const response = await createGithubModelsChatCompletion({
9362
+ modelId: preview.modelId,
9363
+ messages,
9364
+ tools: directAgentToolDefinitions,
9365
+ ...options.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {},
9366
+ ...options.env ? { env: options.env } : {},
9367
+ ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {},
9368
+ ...options.signal ? { signal: options.signal } : {}
9369
+ });
9370
+ tokensIn += response.usage?.prompt_tokens ?? 0;
9371
+ tokensOut += response.usage?.completion_tokens ?? 0;
9372
+ const message = response.choices?.[0]?.message;
9373
+ const toolCalls = message?.tool_calls ?? [];
9374
+ lastAssistantContent = message?.content ?? lastAssistantContent;
9375
+ if (!toolCalls.length) {
9376
+ if (options.streamOutput && lastAssistantContent) {
9377
+ process.stdout.write(lastAssistantContent);
9378
+ }
9379
+ return {
9380
+ ...preview,
9381
+ exitCode: 0,
9382
+ stdout: lastAssistantContent,
9383
+ stderr: "",
9384
+ ...tokensIn ? { tokensIn } : {},
9385
+ ...tokensOut ? { tokensOut } : {}
9386
+ };
9387
+ }
9388
+ messages.push({ role: "assistant", content: message?.content ?? null, tool_calls: toolCalls });
9389
+ for (const toolCall of toolCalls) {
9390
+ const execution = await executeDirectAgentTool(toolCall, options.toolPolicy);
9391
+ messages.push({ role: "tool", tool_call_id: toolCall.id, content: JSON.stringify(execution) });
9392
+ }
9393
+ }
9394
+ return {
9395
+ ...preview,
9396
+ exitCode: 1,
9397
+ stdout: lastAssistantContent,
9398
+ stderr: `Amistio direct harness stopped after ${maxToolRounds} tool rounds without a final response.`,
9399
+ ...tokensIn ? { tokensIn } : {},
9400
+ ...tokensOut ? { tokensOut } : {}
9401
+ };
9402
+ }
9403
+ async function executeDirectAgentTool(toolCall, policy) {
9404
+ const args = parseToolArguments(toolCall.function.arguments);
9405
+ const toolName = toolCall.function.name;
9406
+ if (!args.ok) {
9407
+ return { toolName, result: toolFailure(toolName, "command_not_allowed", args.message) };
9408
+ }
9409
+ try {
9410
+ switch (toolName) {
9411
+ case "filesystem_read_file":
9412
+ return { toolName, result: await runFilesystemReadTool({ policy, relativePath: stringArg(args.value, "relativePath") }) };
9413
+ case "filesystem_list_files":
9414
+ return { toolName, result: await runFilesystemListTool({ policy, ...optionalStringField(args.value, "relativeDirectory"), ...optionalNumberField(args.value, "maxFiles") }) };
9415
+ case "filesystem_search_text":
9416
+ return { toolName, result: await runFilesystemSearchTool({ policy, query: stringArg(args.value, "query"), ...optionalStringField(args.value, "relativeDirectory"), ...optionalNumberField(args.value, "maxMatches") }) };
9417
+ case "filesystem_write_file":
9418
+ return { toolName, result: await runFilesystemWriteTool({ policy, relativePath: stringArg(args.value, "relativePath"), content: stringArg(args.value, "content") }) };
9419
+ case "git_status":
9420
+ return { toolName, result: await runGitStatusTool({ policy }) };
9421
+ case "git_diff_name_only":
9422
+ return { toolName, result: await runGitDiffNameOnlyTool({ policy }) };
9423
+ case "package_manager_detect":
9424
+ return { toolName, result: await runPackageManagerDetectTool({ policy }) };
9425
+ case "test_runner_profile":
9426
+ return { toolName, result: await runTestRunnerProfileTool({ policy, ...optionalTestProfileField(args.value, "profileId") }) };
9427
+ case "browser_check":
9428
+ return { toolName, result: await runBrowserCheckTool({ policy, url: stringArg(args.value, "url") }) };
9429
+ default:
9430
+ return { toolName, result: toolFailure(toolName, "command_not_allowed", `Unknown Amistio direct harness tool: ${toolName}.`) };
9431
+ }
9432
+ } catch (error) {
9433
+ return { toolName, result: toolFailure(toolName, "command_not_allowed", error instanceof Error ? error.message : String(error)) };
9434
+ }
9435
+ }
9436
+ var directAgentToolDefinitions = [
9437
+ toolDefinition("filesystem_read_file", "Read a UTF-8 file under the prepared execution root.", {
9438
+ type: "object",
9439
+ additionalProperties: false,
9440
+ required: ["relativePath"],
9441
+ properties: { relativePath: { type: "string" } }
9442
+ }),
9443
+ toolDefinition("filesystem_list_files", "List files under a directory within the prepared execution root.", {
9444
+ type: "object",
9445
+ additionalProperties: false,
9446
+ properties: { relativeDirectory: { type: "string" }, maxFiles: { type: "integer", minimum: 1, maximum: 500 } }
9447
+ }),
9448
+ toolDefinition("filesystem_search_text", "Search text under the prepared execution root with bounded output.", {
9449
+ type: "object",
9450
+ additionalProperties: false,
9451
+ required: ["query"],
9452
+ properties: { query: { type: "string" }, relativeDirectory: { type: "string" }, maxMatches: { type: "integer", minimum: 1, maximum: 200 } }
9453
+ }),
9454
+ toolDefinition("filesystem_write_file", "Write a UTF-8 file under the prepared execution root. Allowed only for mutating harness policy.", {
9455
+ type: "object",
9456
+ additionalProperties: false,
9457
+ required: ["relativePath", "content"],
9458
+ properties: { relativePath: { type: "string" }, content: { type: "string" } }
9459
+ }),
9460
+ toolDefinition("git_status", "Read git status --short for the execution root.", { type: "object", additionalProperties: false, properties: {} }),
9461
+ toolDefinition("git_diff_name_only", "Read changed Git paths for the execution root.", { type: "object", additionalProperties: false, properties: {} }),
9462
+ toolDefinition("package_manager_detect", "Detect local package manager lockfiles and declared verification scripts.", { type: "object", additionalProperties: false, properties: {} }),
9463
+ toolDefinition("test_runner_profile", "Run a declared local verification profile such as test, typecheck, lint, build, or verify.", {
9464
+ type: "object",
9465
+ additionalProperties: false,
9466
+ properties: { profileId: { type: "string", enum: ["verify", "test", "typecheck", "lint", "build"] } }
9467
+ }),
9468
+ toolDefinition("browser_check", "Run a loopback-only HTTP browser smoke check.", {
9469
+ type: "object",
9470
+ additionalProperties: false,
9471
+ required: ["url"],
9472
+ properties: { url: { type: "string" } }
9473
+ })
9474
+ ];
9475
+ function directAgentSystemPrompt(policy) {
9476
+ return [
9477
+ "You are the built-in Amistio direct harness running on the user's local runner.",
9478
+ "Use the provided tools to inspect files, make permitted edits, run bounded checks, and then return the final Amistio result requested by the user prompt.",
9479
+ `Mutation policy: ${policy.mutationPolicy}.`,
9480
+ policy.mutationPolicy === "mutating" ? "You may write files only through filesystem_write_file and only with repository-relative paths inside the prepared execution root." : "Do not call filesystem_write_file. This work is read-only.",
9481
+ "Prefer targeted list/search/read operations before edits. Prefer known verification profiles over broad commands. Never ask for arbitrary shell execution, absolute local paths, secrets, provider credentials, or environment dumps.",
9482
+ "Tool outputs are normalized and may be truncated; if more detail is needed, make a narrower tool request.",
9483
+ "When finished, answer with the final task result only. Preserve any structured result contract included in the user prompt."
9484
+ ].join("\n");
9485
+ }
9486
+ function toolDefinition(name, description, parameters) {
9487
+ return { type: "function", function: { name, description, parameters } };
9488
+ }
9489
+ function parseToolArguments(value) {
9490
+ try {
9491
+ const parsed = JSON.parse(value || "{}");
9492
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
9493
+ return { ok: true, value: parsed };
9494
+ }
9495
+ return { ok: false, message: "Tool arguments must be a JSON object." };
9496
+ } catch {
9497
+ return { ok: false, message: "Tool arguments must be valid JSON." };
9498
+ }
9499
+ }
9500
+ function stringArg(args, key) {
9501
+ const value = args[key];
9502
+ if (typeof value !== "string") {
9503
+ throw new Error(`Tool argument ${key} must be a string.`);
9504
+ }
9505
+ return value;
9506
+ }
9507
+ function optionalStringArg(args, key) {
9508
+ const value = args[key];
9509
+ return typeof value === "string" && value.trim() ? value : void 0;
9510
+ }
9511
+ function optionalStringField(args, key) {
9512
+ const value = optionalStringArg(args, key);
9513
+ return value ? { [key]: value } : {};
9514
+ }
9515
+ function optionalNumberArg(args, key) {
9516
+ const value = args[key];
9517
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
9518
+ }
9519
+ function optionalNumberField(args, key) {
9520
+ const value = optionalNumberArg(args, key);
9521
+ return value === void 0 ? {} : { [key]: value };
9522
+ }
9523
+ function optionalTestProfileArg(args, key) {
9524
+ const value = args[key];
9525
+ return value === "verify" || value === "test" || value === "typecheck" || value === "lint" || value === "build" ? value : void 0;
9526
+ }
9527
+ function optionalTestProfileField(args, key) {
9528
+ const value = optionalTestProfileArg(args, key);
9529
+ return value ? { profileId: value } : {};
9530
+ }
9531
+ function toolFailure(toolName, code, message) {
9532
+ return { adapterId: toolName, status: "failed", exitCode: 1, stdout: "", stderr: "", truncated: false, error: { code, message } };
9533
+ }
9534
+ function positiveInteger3(value) {
9535
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
9536
+ }
9537
+
9538
+ // src/harness-adapter.ts
9539
+ var AMISTIO_HARNESS_ID = "amistio";
9540
+ function harnessMutationPolicyForWorkKind(workKind) {
9541
+ return workKind === "implementation" ? "mutating" : "readOnly";
9542
+ }
9543
+ function createHarnessConcurrencyMetadata(executionPolicy, preview) {
9544
+ const serializedResourceKeys = /* @__PURE__ */ new Set([
9545
+ `claim-lane:${executionPolicy.claimLaneId}`,
9546
+ `execution-root:${executionPolicy.executionRoot}`
9547
+ ]);
9548
+ if (executionPolicy.mutationPolicy === "mutating") {
9549
+ serializedResourceKeys.add(`repository:${executionPolicy.executionRoot}`);
9550
+ }
9551
+ if (preview.providerId) {
9552
+ serializedResourceKeys.add(`provider:${preview.providerId}`);
9553
+ }
9554
+ if (preview.toolName && preview.toolName !== "custom") {
9555
+ serializedResourceKeys.add(`client:${preview.toolName}`);
9556
+ }
9557
+ return {
9558
+ claimLaneId: executionPolicy.claimLaneId,
9559
+ supportsConcurrentRuns: true,
9560
+ requiresExclusiveRepository: executionPolicy.mutationPolicy === "mutating",
9561
+ requiresExclusiveProviderAuth: Boolean(preview.providerId && preview.toolName !== "amistio-direct"),
9562
+ serializedResourceKeys: [...serializedResourceKeys]
9563
+ };
9564
+ }
9565
+ var builtinAmistioHarnessAdapter = {
9566
+ id: AMISTIO_HARNESS_ID,
9567
+ displayName: "Amistio",
9568
+ async createRunPreview({ executionPolicy, ...toolOptions }) {
9569
+ const preview = shouldUseDirectModelClient(toolOptions) ? createDirectModelClientPreview(toolOptions) : await createToolRunPreview(toolOptions);
9570
+ const concurrency = createHarnessConcurrencyMetadata(executionPolicy, preview);
9571
+ const toolAdapterPolicy = createBoundedToolAdapterPolicy({
9572
+ executionRoot: executionPolicy.executionRoot,
9573
+ mutationPolicy: executionPolicy.mutationPolicy,
9574
+ concurrencyMode: concurrency.requiresExclusiveRepository ? "serialized" : "parallelSafe",
9575
+ resourceKey: `claim-lane:${executionPolicy.claimLaneId}`
9576
+ });
9577
+ return { harnessId: AMISTIO_HARNESS_ID, displayName: "Amistio", executionPolicy, concurrency, toolAdapterPolicy, availableToolAdapters: boundedToolAdapterCatalog, preview };
9578
+ },
9579
+ async executeRun({ preparedRun, ...toolOptions }) {
9580
+ if (shouldUseDirectModelClient(toolOptions)) {
9581
+ return runAmistioDirectAgent({ ...toolOptions, toolPolicy: preparedRun.toolAdapterPolicy });
9582
+ }
9583
+ return runLocalTool(toolOptions);
9584
+ }
9585
+ };
9586
+
9587
+ // src/host-helper-conformance.ts
9588
+ async function runHostHelperConformance(config, cwd = process.cwd()) {
9589
+ const checks = [];
9590
+ const discovery = await discoverNativeHostHelper(config);
9591
+ checks.push({
9592
+ name: "handshake",
9593
+ passed: discovery.ok,
9594
+ detail: discovery.ok ? `protocol ${discovery.helper.handshake.protocolVersion}` : discovery.failure.message
9595
+ });
9596
+ if (!discovery.ok) {
9597
+ return { passed: false, checks };
9598
+ }
9599
+ const port = await createHostExecutionPort({ nativeHelper: config });
9600
+ const execution = await port.executeCommand({ command: process.execPath, args: ["-e", "console.log('amistio conformance')"], cwd, timeoutMs: 2e3, requireNativeHelper: true });
9601
+ checks.push(commandCheck("command execution", execution, (result) => result.status === "completed" && result.exitCode === 0 && result.stdout.includes("amistio conformance")));
9602
+ const boundedOutput = await port.executeCommand({ command: process.execPath, args: ["-e", "console.log('\u20AC\u20AC\u20AC\u20AC')"], cwd, timeoutMs: 2e3, requireNativeHelper: true, outputBudgetBytes: 7 });
9603
+ checks.push(commandCheck("bounded UTF-8 output", boundedOutput, (result) => result.status === "completed" && Buffer.byteLength(result.stdout, "utf8") <= 7 && !result.stdout.includes("\uFFFD")));
9604
+ if (!discovery.helper.handshake.capabilities.pty.supported) {
9605
+ const pty = await port.executeCommand({ command: process.execPath, args: ["-e", "console.log('pty')"], cwd, timeoutMs: 2e3, requireNativeHelper: true, requirePty: true });
9606
+ checks.push(commandCheck("PTY fail-closed", pty, (result) => result.status === "unsupported" && result.error?.code === "pty_unsupported"));
9607
+ } else {
9608
+ checks.push({ name: "PTY fail-closed", passed: true, detail: "Helper advertises PTY support; platform PTY enforcement must be verified by the helper's own tests." });
9609
+ }
9610
+ if (!discovery.helper.handshake.capabilities.sandbox.supported) {
9611
+ const sandbox = await port.executeCommand({ command: process.execPath, args: ["-e", "console.log('sandbox')"], cwd, timeoutMs: 2e3, requireNativeHelper: true, sandbox: "networkDisabled" });
9612
+ checks.push(commandCheck("sandbox fail-closed", sandbox, (result) => result.status === "unsupported" && result.error?.code === "sandbox_unsupported"));
9613
+ } else {
9614
+ checks.push({ name: "sandbox fail-closed", passed: true, detail: "Helper advertises sandbox support; OS enforcement must be verified by the helper's own platform tests." });
9615
+ }
9616
+ return { passed: checks.every((check) => check.passed), checks };
9617
+ }
9618
+ function commandCheck(name, result, predicate) {
9619
+ return {
9620
+ name,
9621
+ passed: predicate(result),
9622
+ detail: result.error?.message ?? `${result.status} exit ${result.exitCode}`
9623
+ };
9624
+ }
9625
+
9626
+ // src/provider-auth.ts
9627
+ import { createHash as createHash8 } from "node:crypto";
9628
+ var githubCopilotProviderId = "github-copilot";
9629
+ var githubCopilotProviderClientId = "github-copilot-sdk";
9630
+ var githubCopilotRouteType = "agentClient";
9631
+ var githubModelsProviderClientId = "github-models-api";
9632
+ var githubModelsRouteType = "directProvider";
9633
+ async function checkProviderAuthLink(input) {
9634
+ if (isGithubModelsDirectRequest(input.request)) {
9635
+ return checkGithubModelsDirectAuth(input);
9636
+ }
9637
+ if (!isGithubCopilotSdkRequest(input.request)) {
9638
+ return providerAuthResult(input.request, "unsupported", "Provider link target is not supported by this runner.", input.now);
9639
+ }
9640
+ const checkedAt = input.now?.() ?? (/* @__PURE__ */ new Date()).toISOString();
9641
+ let client;
9642
+ try {
9643
+ client = await (input.createCopilotClient ?? createDefaultCopilotClient)({ cwd: input.root, logLevel: "error", useLoggedInUser: true });
9644
+ await client.start();
9645
+ const authStatus = await client.getAuthStatus();
9646
+ const modelCount = authStatus.isAuthenticated ? await listCopilotModelCount(client) : void 0;
9647
+ const status = {
9648
+ providerId: githubCopilotProviderId,
9649
+ providerClientId: githubCopilotProviderClientId,
9650
+ routeType: githubCopilotRouteType,
9651
+ status: authStatus.isAuthenticated ? "authenticated" : "pendingUserAction",
9652
+ authMethodLabel: authStatus.authType ? `GitHub ${authStatus.authType}` : "GitHub Copilot SDK",
9653
+ ...authStatus.host ? { accountHost: authStatus.host } : {},
9654
+ ...authStatus.login ? { accountLogin: authStatus.login } : {},
9655
+ ...authStatus.host && authStatus.login ? { accountFingerprint: accountFingerprint(authStatus.host, authStatus.login) } : {},
9656
+ ...modelCount !== void 0 ? { modelCount } : {},
9657
+ checkedAt,
9658
+ message: authStatus.isAuthenticated ? "GitHub Copilot SDK is authenticated on this runner." : "Complete GitHub Copilot auth locally on the runner machine, then check again."
9659
+ };
9660
+ return { succeeded: true, message: status.message, providerAuthStatus: status };
9661
+ } catch {
9662
+ return providerAuthResult(input.request, "unavailable", "GitHub Copilot SDK auth status is unavailable on this runner.", input.now, "copilot_sdk_unavailable");
9663
+ } finally {
9664
+ await client?.stop().catch(() => void 0);
9665
+ }
9666
+ }
9667
+ function isGithubCopilotSdkRequest(request) {
9668
+ return request.providerId === githubCopilotProviderId && request.providerClientId === githubCopilotProviderClientId && request.routeType === githubCopilotRouteType;
9669
+ }
9670
+ function isGithubModelsDirectRequest(request) {
9671
+ return request.providerId === githubModelsProviderId && request.providerClientId === githubModelsProviderClientId && request.routeType === githubModelsRouteType;
9672
+ }
9673
+ function checkGithubModelsDirectAuth(input) {
9674
+ const checkedAt = input.now?.() ?? (/* @__PURE__ */ new Date()).toISOString();
9675
+ const tokenSource = githubModelsTokenSource(input.env ?? process.env);
9676
+ const message = tokenSource ? `${tokenSource} is configured for GitHub Models direct execution on this runner.` : "Set GITHUB_MODELS_TOKEN or GITHUB_TOKEN on this runner to enable GitHub Models direct execution.";
9677
+ const status = {
9678
+ providerId: githubModelsProviderId,
9679
+ providerClientId: githubModelsProviderClientId,
9680
+ routeType: githubModelsRouteType,
9681
+ status: tokenSource ? "authenticated" : "notConfigured",
9682
+ authMethodLabel: tokenSource ?? "GITHUB_MODELS_TOKEN or GITHUB_TOKEN",
9683
+ ...tokenSource ? { modelCount: githubModelsSupportedModelIds.length } : {},
9684
+ checkedAt,
9685
+ message
9686
+ };
9687
+ return { succeeded: true, message, providerAuthStatus: status };
9688
+ }
9689
+ function githubModelsTokenSource(env) {
9690
+ if (hasEnvValue(env.GITHUB_MODELS_TOKEN)) return "GITHUB_MODELS_TOKEN";
9691
+ if (hasEnvValue(env.GITHUB_TOKEN)) return "GITHUB_TOKEN";
9692
+ return void 0;
9693
+ }
9694
+ function hasEnvValue(value) {
9695
+ return typeof value === "string" && value.trim().length > 0;
9696
+ }
9697
+ async function createDefaultCopilotClient(options) {
9698
+ const { CopilotClient } = await import("@github/copilot-sdk");
9699
+ return new CopilotClient(options);
9700
+ }
9701
+ async function listCopilotModelCount(client) {
9702
+ try {
9703
+ return (await client.listModels()).length;
9704
+ } catch {
9705
+ return void 0;
9706
+ }
9707
+ }
9708
+ function providerAuthResult(request, status, message, now, errorCode) {
9709
+ return {
9710
+ succeeded: status !== "unsupported",
9711
+ message,
9712
+ providerAuthStatus: {
9713
+ providerId: request.providerId,
9714
+ providerClientId: request.providerClientId,
9715
+ routeType: request.routeType,
9716
+ status,
9717
+ checkedAt: now?.() ?? (/* @__PURE__ */ new Date()).toISOString(),
9718
+ message,
9719
+ ...errorCode ? { errorCode } : {}
9720
+ }
9721
+ };
9722
+ }
9723
+ function accountFingerprint(host, login) {
9724
+ return `sha256:${createHash8("sha256").update(`${host.toLowerCase()}\0${login.toLowerCase()}`).digest("hex")}`;
9725
+ }
9726
+
8303
9727
  // src/version.ts
8304
9728
  import { readFileSync } from "node:fs";
8305
9729
  function readCliPackageVersion() {
@@ -8434,7 +9858,7 @@ program.command("import").description("Pair an existing checkout and import lega
8434
9858
  });
8435
9859
  program.command("pair").description("Pair this repository with an Amistio web project").requiredOption("--account <accountId>", "Amistio account ID").requiredOption("--project <projectId>", "Amistio project ID").option("--repository-link <repositoryLinkId>", "Existing repository link ID").option("--default-branch <branch>", "Default branch", "main").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--pairing-code <code>", "Short-lived pairing code from the Amistio app").option("--token <token>", "Runner/device credential to store outside the repository").option("--root <path>", "Repository root", defaultRoot).action(async (options, command) => {
8436
9860
  const pairingRoot = await resolvePairingRoot(options.root, { explicitRoot: command.getOptionValueSource("root") === "cli" });
8437
- let repositoryLinkId = options.repositoryLink ?? `repo_${randomUUID()}`;
9861
+ let repositoryLinkId = options.repositoryLink ?? `repo_${randomUUID2()}`;
8438
9862
  let credential = options.token;
8439
9863
  if (options.pairingCode) {
8440
9864
  const pairing = await new ApiClient({
@@ -8620,7 +10044,7 @@ work.command("prompt").description("Print or write an approved work prompt witho
8620
10044
  }
8621
10045
  const prompt = await createRunnerWorkPrompt(context.client, context.metadata.amistioProjectId, workItem);
8622
10046
  if (options.out) {
8623
- await writeFile10(options.out, prompt, "utf8");
10047
+ await writeFile11(options.out, prompt, "utf8");
8624
10048
  console.log(`Wrote work prompt to ${options.out}.`);
8625
10049
  } else {
8626
10050
  console.log(prompt);
@@ -8634,6 +10058,51 @@ program.command("tools").description("List local AI coding tools that the Amisti
8634
10058
  }
8635
10059
  console.log("custom - pass --tool-command to use any other local runner command.");
8636
10060
  });
10061
+ var hostHelper = program.command("host-helper").description("Inspect the optional Amistio host helper used for stronger local execution primitives");
10062
+ hostHelper.command("status").description("Show configured host-helper protocol and capability status").option("--path <path>", "Helper executable path; defaults to AMISTIO_HOST_HELPER_PATH").action(async (options) => {
10063
+ const config = options.path?.trim() ? { command: options.path.trim() } : nativeHostHelperConfigFromEnvironment();
10064
+ console.log(`Protocol: ${hostExecutionProtocolVersion}`);
10065
+ if (!config) {
10066
+ console.log("Helper: not configured");
10067
+ console.log("Default execution: Node host port, no PTY, no sandbox.");
10068
+ console.log("Install @amistio/cli and set AMISTIO_HOST_HELPER_PATH to `amistio-host-helper` only on machines where the reviewed local helper is intended.");
10069
+ return;
10070
+ }
10071
+ console.log(`Helper: ${config.command}`);
10072
+ const discovery = await discoverNativeHostHelper(config);
10073
+ if (!discovery.ok) {
10074
+ console.log(`Status: ${discovery.failure.code}`);
10075
+ console.log(`Reason: ${discovery.failure.message}`);
10076
+ if (discovery.failure.stderr?.trim()) console.log(`stderr: ${truncateLogExcerpt(discovery.failure.stderr)}`);
10077
+ process.exitCode = 1;
10078
+ return;
10079
+ }
10080
+ const { capabilities, secretBoundary } = discovery.helper.handshake;
10081
+ console.log("Status: ready");
10082
+ console.log(`Implementation: ${capabilities.implementation}`);
10083
+ console.log(formatHostHelperCapability("Process groups", capabilities.processGroups));
10084
+ console.log(formatHostHelperCapability("Signal escalation", capabilities.signalEscalation));
10085
+ console.log(formatHostHelperCapability("Stream capture", capabilities.streamCapture));
10086
+ console.log(formatHostHelperCapability("PTY", capabilities.pty));
10087
+ console.log(formatHostHelperCapability("Sandbox", capabilities.sandbox));
10088
+ console.log(`Secret boundary: ${secretBoundary.credentialPolicy}, ${secretBoundary.environmentPolicy}`);
10089
+ });
10090
+ hostHelper.command("conformance").description("Run local compatibility checks against a host helper").option("--path <path>", "Helper executable path; defaults to AMISTIO_HOST_HELPER_PATH").action(async (options) => {
10091
+ const config = options.path?.trim() ? { command: options.path.trim() } : nativeHostHelperConfigFromEnvironment();
10092
+ if (!config) {
10093
+ console.log("No host helper configured. Set AMISTIO_HOST_HELPER_PATH or pass --path.");
10094
+ process.exitCode = 1;
10095
+ return;
10096
+ }
10097
+ const report = await runHostHelperConformance(config);
10098
+ console.log(`Host helper conformance: ${report.passed ? "passed" : "failed"}`);
10099
+ for (const check of report.checks) {
10100
+ console.log(` ${check.passed ? "ok" : "fail"} ${check.name}: ${check.detail}`);
10101
+ }
10102
+ if (!report.passed) {
10103
+ process.exitCode = 1;
10104
+ }
10105
+ });
8637
10106
  program.command("orchestrate").description("Update the Amistio control plane through a user-installed local AI tool").argument("[goal...]", "Goal or next-step instruction for the orchestration pass").option("--root <path>", "Repository root", defaultRoot).option("--tool <name>", "Local tool to use: auto, none, opencode, claude, codex, copilot, gemini, aider, cursor-agent", "auto").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel, "auto").option("--model <model>", "Model to request when the selected local tool supports model selection").option("--provider <providerId>", "Provider id for provider-backed model configuration").option("--model-id <modelId>", "Provider catalog model id to request").option("--model-variant <variant>", "Provider catalog model variant to request").option("--reasoning-effort <effort>", "Reasoning effort: auto, low, medium, high, or xhigh", parseReasoningEffort).option("--tool-command <command>", "Custom local command. Use {promptFile} and {root} placeholders when supported").option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--prompt-out <path>", "Write the generated orchestration prompt to a file before running").option("--dry-run", "Print the generated orchestration prompt without running a tool").option("--no-stream", "Capture local tool output instead of streaming it").action(async (goalParts, options) => {
8638
10107
  const goal = goalParts?.join(" ").trim() || "Review the current repository state and update the Amistio control plane with the next useful orchestration steps.";
8639
10108
  const prompt = await createOrchestrationPrompt({ rootDir: options.root, goal });
@@ -8657,7 +10126,7 @@ program.command("orchestrate").description("Update the Amistio control plane thr
8657
10126
  ...options.toolCommand ? { toolCommand: options.toolCommand } : {},
8658
10127
  ...localModelConfig,
8659
10128
  streamOutput: options.stream,
8660
- ...sessionPolicy === "none" ? {} : { session: { toolSessionId: `local_orchestration_${randomUUID()}`, policy: sessionPolicy, decision: localSessionDecision(sessionPolicy) } }
10129
+ ...sessionPolicy === "none" ? {} : { session: { toolSessionId: `local_orchestration_${randomUUID2()}`, policy: sessionPolicy, decision: localSessionDecision(sessionPolicy) } }
8661
10130
  });
8662
10131
  if (!options.stream && result.stdout.trim()) {
8663
10132
  console.log(result.stdout.trim());
@@ -8703,7 +10172,7 @@ program.command("run").description("Claim and run approved Amistio work locally"
8703
10172
  projectId: context.metadata.amistioProjectId,
8704
10173
  repositoryLinkId: context.metadata.repositoryLinkId,
8705
10174
  runnerId,
8706
- rootDir: path16.resolve(options.root),
10175
+ rootDir: path17.resolve(options.root),
8707
10176
  apiUrl: options.apiUrl,
8708
10177
  args: buildBackgroundRunnerArgs(resolvedOptions)
8709
10178
  });
@@ -8894,7 +10363,7 @@ runnerService.command("install").description("Install a user-level startup servi
8894
10363
  projectId: context.metadata.amistioProjectId,
8895
10364
  repositoryLinkId: context.metadata.repositoryLinkId,
8896
10365
  runnerId,
8897
- rootDir: path16.resolve(options.root),
10366
+ rootDir: path17.resolve(options.root),
8898
10367
  apiUrl: options.apiUrl,
8899
10368
  args,
8900
10369
  platform
@@ -8910,7 +10379,7 @@ runnerService.command("install").description("Install a user-level startup servi
8910
10379
  console.log(`Installed startup service ${metadata.serviceName}.`);
8911
10380
  console.log(`Service file: ${metadata.serviceFilePath}`);
8912
10381
  } catch (error) {
8913
- console.error(errorMessage3(error));
10382
+ console.error(errorMessage6(error));
8914
10383
  process.exitCode = 1;
8915
10384
  }
8916
10385
  });
@@ -9053,12 +10522,12 @@ function supportsConcurrentLocalToolExecution(toolConfig) {
9053
10522
  function aggregateRunnerLaneResults(results) {
9054
10523
  const stopResult = results.find((result) => result.stopRunner);
9055
10524
  if (stopResult) return stopResult;
9056
- const failedResult = results.find((result) => result.status === "failed");
9057
- if (failedResult) return failedResult;
10525
+ const failedResult2 = results.find((result) => result.status === "failed");
10526
+ if (failedResult2) return failedResult2;
9058
10527
  const blockedResult = results.find((result) => result.status === "blocked");
9059
10528
  if (blockedResult) return blockedResult;
9060
- const completedResult = results.find((result) => result.status === "completed" || result.status === "preview");
9061
- if (completedResult) return completedResult;
10529
+ const completedResult2 = results.find((result) => result.status === "completed" || result.status === "preview");
10530
+ if (completedResult2) return completedResult2;
9062
10531
  return results.find((result) => result.status === "idle") ?? { status: "idle", exitCode: 0 };
9063
10532
  }
9064
10533
  async function runAutoSyncCycle({ context, maxFileKb, quiet, quietDisabled, root, runnerId }) {
@@ -9110,7 +10579,7 @@ async function runAutoSyncCycle({ context, maxFileKb, quiet, quietDisabled, root
9110
10579
  }
9111
10580
  return { status, message, pushedCount, skippedCount, conflictCount, collection };
9112
10581
  } catch (error) {
9113
- const message = `Auto-sync failed: ${errorMessage3(error)}`;
10582
+ const message = `Auto-sync failed: ${errorMessage6(error)}`;
9114
10583
  await context.client.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "blocked", { ...heartbeatBase, autoSyncStatus: "failed", autoSyncMessage: message, autoSyncLastFailureAt: startedAt }).catch(() => void 0);
9115
10584
  return { status: "failed", message, pushedCount: 0, skippedCount: 0, conflictCount: 0 };
9116
10585
  }
@@ -9213,7 +10682,20 @@ async function runNextWorkItem({
9213
10682
  ...isolationTelemetry.executionBranch ? { currentBranch: isolationTelemetry.executionBranch } : {}
9214
10683
  });
9215
10684
  const resolvedModelConfig = toolConfigModelOptions(toolConfig);
9216
- const preview = await createToolRunPreview({ rootDir: executionRoot, prompt, tool: toolConfig.tool, invocationChannel: toolConfig.requestedInvocationChannel ?? "auto", ...toolCommand ? { toolCommand } : {}, ...resolvedModelConfig });
10685
+ const preparedHarnessRun = await builtinAmistioHarnessAdapter.createRunPreview({
10686
+ rootDir: executionRoot,
10687
+ prompt,
10688
+ tool: toolConfig.tool,
10689
+ invocationChannel: toolConfig.requestedInvocationChannel ?? "auto",
10690
+ ...toolCommand ? { toolCommand } : {},
10691
+ ...resolvedModelConfig,
10692
+ executionPolicy: {
10693
+ executionRoot,
10694
+ mutationPolicy: harnessMutationPolicyForWorkKind(result.workItem.workKind),
10695
+ claimLaneId
10696
+ }
10697
+ });
10698
+ const preview = preparedHarnessRun.preview;
9217
10699
  const sessionContext = await prepareToolSession({
9218
10700
  apiClient,
9219
10701
  projectId,
@@ -9237,7 +10719,15 @@ async function runNextWorkItem({
9237
10719
  status: "running",
9238
10720
  summary: `Local runner started ${preview.toolName} execution.`,
9239
10721
  idempotencyKey: `runner_milestone_started_${result.workItem.workItemId}_${result.workItem.attempt}`,
9240
- metadata: { tool: preview.toolName, invocationChannel: toolConfig.requestedInvocationChannel ?? "auto" }
10722
+ metadata: {
10723
+ tool: preview.toolName,
10724
+ invocationChannel: toolConfig.requestedInvocationChannel ?? "auto",
10725
+ claimLaneId: preparedHarnessRun.concurrency.claimLaneId,
10726
+ harnessSupportsConcurrentRuns: preparedHarnessRun.concurrency.supportsConcurrentRuns,
10727
+ harnessRequiresExclusiveRepository: preparedHarnessRun.concurrency.requiresExclusiveRepository,
10728
+ harnessRequiresExclusiveProviderAuth: preparedHarnessRun.concurrency.requiresExclusiveProviderAuth,
10729
+ harnessSerializedResourceKeys: preparedHarnessRun.concurrency.serializedResourceKeys
10730
+ }
9241
10731
  });
9242
10732
  const startedAt = Date.now();
9243
10733
  const providerSessionStore = new LocalToolSessionStore();
@@ -9245,7 +10735,8 @@ async function runNextWorkItem({
9245
10735
  let toolResult;
9246
10736
  const stopLeaseRenewal = startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem: result.workItem, telemetry: isolationTelemetry, heartbeatConcurrency });
9247
10737
  try {
9248
- toolResult = await runLocalTool({
10738
+ toolResult = await builtinAmistioHarnessAdapter.executeRun({
10739
+ preparedRun: preparedHarnessRun,
9249
10740
  rootDir: executionRoot,
9250
10741
  prompt,
9251
10742
  tool: toolConfig.tool,
@@ -9270,7 +10761,7 @@ async function runNextWorkItem({
9270
10761
  const message = `${preview.toolName} failed before returning a result.`;
9271
10762
  const settlements = await Promise.allSettled([
9272
10763
  apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency)),
9273
- markToolSessionBlocked(apiClient, projectId, sessionContext.toolSession, errorMessage3(error)),
10764
+ markToolSessionBlocked(apiClient, projectId, sessionContext.toolSession, errorMessage6(error)),
9274
10765
  apiClient.updateWorkStatus(projectId, result.workItem.workItemId, "failed", `run_failed_${result.workItem.workItemId}_${result.workItem.attempt}_${runnerId}`, runnerId, {
9275
10766
  ...isolationTelemetry,
9276
10767
  tool: preview.toolName,
@@ -9564,7 +11055,7 @@ async function runNextWorkItem({
9564
11055
  projectId,
9565
11056
  result.workItem.workItemId,
9566
11057
  finalStatus,
9567
- `run_${result.workItem.workItemId}_${randomUUID()}`,
11058
+ `run_${result.workItem.workItemId}_${randomUUID2()}`,
9568
11059
  runnerId,
9569
11060
  {
9570
11061
  tool: preview.toolName,
@@ -9654,11 +11145,11 @@ async function prepareWorktreeForClaimedItem({ apiClient, heartbeatConcurrency,
9654
11145
  });
9655
11146
  return { status: "ready", isolation };
9656
11147
  } catch (error) {
9657
- const message = errorMessage3(error);
11148
+ const message = errorMessage6(error);
9658
11149
  const telemetry = workItemIsolationTelemetry(workItem, { ...identity, baseRevision: workItem.baseRevision ?? "unknown", worktreePath: "" });
9659
11150
  const finalAttempt = workItem.attempt >= maxPreflightAttempts;
9660
11151
  const statusMessage = finalAttempt ? `Git worktree preflight failed after ${workItem.attempt}/${maxPreflightAttempts} attempts. ${message}` : `Git worktree preflight attempt ${workItem.attempt}/${maxPreflightAttempts} failed. Requeueing for retry. ${message}`;
9661
- const statusResult = await apiClient.updateWorkStatus(projectId, workItem.workItemId, finalAttempt ? "failed" : "approved", `worktree_${finalAttempt ? "failed" : "retry"}_${workItem.workItemId}_${workItem.attempt}_${randomUUID()}`, runnerId, {
11152
+ const statusResult = await apiClient.updateWorkStatus(projectId, workItem.workItemId, finalAttempt ? "failed" : "approved", `worktree_${finalAttempt ? "failed" : "retry"}_${workItem.workItemId}_${workItem.attempt}_${randomUUID2()}`, runnerId, {
9662
11153
  ...telemetry,
9663
11154
  message: statusMessage,
9664
11155
  ...finalAttempt ? { blockerReason: message } : { releaseClaim: true },
@@ -9726,8 +11217,8 @@ async function recordFinalizationFailure({ apiClient, durationMs, error, isolati
9726
11217
  const message = `${toolName} completed, but Amistio could not finalize the result. ${safeFinalizationFailureSummary(error)}`;
9727
11218
  const settlements = await Promise.allSettled([
9728
11219
  apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig)),
9729
- markToolSessionBlocked(apiClient, projectId, sessionContext.toolSession, errorMessage3(error)),
9730
- apiClient.updateWorkStatus(projectId, workItem.workItemId, "failed", `finalize_failed_${workItem.workItemId}_${workItem.attempt}_${randomUUID()}`, runnerId, {
11220
+ markToolSessionBlocked(apiClient, projectId, sessionContext.toolSession, errorMessage6(error)),
11221
+ apiClient.updateWorkStatus(projectId, workItem.workItemId, "failed", `finalize_failed_${workItem.workItemId}_${workItem.attempt}_${randomUUID2()}`, runnerId, {
9731
11222
  ...isolationTelemetry,
9732
11223
  tool: toolName,
9733
11224
  durationMs,
@@ -9845,7 +11336,7 @@ function autopilotWorkMetadata2(workItem) {
9845
11336
  function logRejectedSettlements(action, settlements) {
9846
11337
  for (const settlement of settlements) {
9847
11338
  if (settlement.status === "rejected") {
9848
- console.error(`${action} failed: ${errorMessage3(settlement.reason)}`);
11339
+ console.error(`${action} failed: ${errorMessage6(settlement.reason)}`);
9849
11340
  }
9850
11341
  }
9851
11342
  }
@@ -9858,7 +11349,13 @@ async function runPendingRunnerCommand(apiClient, context, heartbeatMetadata) {
9858
11349
  await updateRunnerCommandStatus(apiClient, context, command, "acknowledged", "Command acknowledged by local runner.");
9859
11350
  await updateRunnerCommandStatus(apiClient, context, command, "running", `Running ${runnerCommandLabel(command.commandKind)} command.`);
9860
11351
  const result = await executeRunnerCommand(apiClient, command, context);
9861
- await updateRunnerCommandStatus(apiClient, context, command, result.succeeded ? "completed" : "failed", result.message, result.error);
11352
+ await updateRunnerCommandStatus(apiClient, context, command, result.succeeded ? "completed" : "failed", result.message, result.error, result.providerAuthStatus);
11353
+ if (result.providerAuthStatus) {
11354
+ await apiClient.sendRunnerHeartbeat(context.projectId, context.runnerId, context.repositoryLinkId, "online", {
11355
+ ...heartbeatMetadata,
11356
+ providerAuthStatuses: mergeProviderAuthStatuses(heartbeatMetadata.providerAuthStatuses, result.providerAuthStatus)
11357
+ }).catch(() => void 0);
11358
+ }
9862
11359
  if (command.commandKind === "remove" && result.succeeded) {
9863
11360
  await new LocalCredentialStore().delete(credentialKey(context.accountId, context.projectId, context.repositoryLinkId));
9864
11361
  }
@@ -9867,14 +11364,15 @@ async function runPendingRunnerCommand(apiClient, context, heartbeatMetadata) {
9867
11364
  }
9868
11365
  return { handled: true, succeeded: result.succeeded, message: result.message, ...result.stopRunner ? { stopRunner: true } : {} };
9869
11366
  }
9870
- async function updateRunnerCommandStatus(apiClient, context, command, status, message, error) {
11367
+ async function updateRunnerCommandStatus(apiClient, context, command, status, message, error, providerAuthStatus) {
9871
11368
  const result = await apiClient.updateRunnerCommand(context.projectId, command.commandId, {
9872
11369
  runnerId: context.runnerId,
9873
11370
  repositoryLinkId: context.repositoryLinkId,
9874
11371
  status,
9875
- idempotencyKey: `runner_command_${command.commandId}_${status}_${randomUUID()}`,
11372
+ idempotencyKey: `runner_command_${command.commandId}_${status}_${randomUUID2()}`,
9876
11373
  message,
9877
- ...error ? { error } : {}
11374
+ ...error ? { error } : {},
11375
+ ...providerAuthStatus ? { providerAuthStatus } : {}
9878
11376
  });
9879
11377
  return result.command;
9880
11378
  }
@@ -9888,11 +11386,21 @@ async function executeRunnerCommand(apiClient, command, context) {
9888
11386
  if (command.commandKind === "implementationHandoffRecovery") {
9889
11387
  return executeImplementationHandoffRecoveryCommand(apiClient, command, context);
9890
11388
  }
11389
+ if (command.commandKind === "providerAuthLinkRequested") {
11390
+ return executeProviderAuthLinkCommand(command, context);
11391
+ }
9891
11392
  return runOfficialCliUpdateWithRuntimeRefresh({
9892
11393
  mode: currentRunnerMode(),
9893
11394
  restartBackgroundRunner: () => restartCurrentRunner(context, { useUpdatedCliExecutable: true })
9894
11395
  });
9895
11396
  }
11397
+ async function executeProviderAuthLinkCommand(command, context) {
11398
+ if (!command.providerAuthLinkRequest) {
11399
+ return { succeeded: false, message: "Provider link command is missing provider auth metadata." };
11400
+ }
11401
+ const result = await checkProviderAuthLink({ request: command.providerAuthLinkRequest, root: context.root });
11402
+ return { succeeded: result.succeeded, message: result.message, providerAuthStatus: result.providerAuthStatus };
11403
+ }
9896
11404
  async function executeImplementationHandoffRecoveryCommand(apiClient, command, context) {
9897
11405
  if (!command.workItemId || !command.handoffRecoveryAction) {
9898
11406
  return { succeeded: false, message: "Handoff recovery command is missing a scoped work item or action." };
@@ -9916,7 +11424,7 @@ async function executeImplementationHandoffRecoveryCommand(apiClient, command, c
9916
11424
  }
9917
11425
  return { succeeded: false, message: "Handoff recovery action is not a runner-local command." };
9918
11426
  } catch (error) {
9919
- return { succeeded: false, message: "Handoff recovery command failed locally.", error: errorMessage3(error) };
11427
+ return { succeeded: false, message: "Handoff recovery command failed locally.", error: errorMessage6(error) };
9920
11428
  }
9921
11429
  }
9922
11430
  async function retryImplementationHandoff(apiClient, context, workItem) {
@@ -9950,7 +11458,7 @@ async function findRecoveryWorkItem(apiClient, projectId, workItemId) {
9950
11458
  return workItems.find((item) => item.workItemId === workItemId);
9951
11459
  }
9952
11460
  async function submitRecoveredHandoff(apiClient, context, workItem, handoff, action) {
9953
- await apiClient.updateWorkStatus(context.projectId, workItem.workItemId, recoveredHandoffWorkStatus(handoff), `handoff_recovery_${workItem.workItemId}_${action}_${randomUUID()}`, context.runnerId, {
11461
+ await apiClient.updateWorkStatus(context.projectId, workItem.workItemId, recoveredHandoffWorkStatus(handoff), `handoff_recovery_${workItem.workItemId}_${action}_${randomUUID2()}`, context.runnerId, {
9954
11462
  implementationHandoff: handoff,
9955
11463
  ...handoff.message ? { message: handoff.message } : {},
9956
11464
  ...workItem.controllingAdrId ? { controllingAdrId: workItem.controllingAdrId } : {},
@@ -9984,15 +11492,20 @@ async function restartCurrentRunner(context, options = {}) {
9984
11492
  const replacement = await restartRunnerDaemonProcess(metadata, context.backgroundArgs, options.useUpdatedCliExecutable ? updatedCliRunnerLaunchOptions() : {});
9985
11493
  return { succeeded: true, stopRunner: true, message: `Replacement background runner started with PID ${replacement.pid}.` };
9986
11494
  } catch (error) {
9987
- return { succeeded: false, message: "Background restart failed.", error: errorMessage3(error) };
11495
+ return { succeeded: false, message: "Background restart failed.", error: errorMessage6(error) };
9988
11496
  }
9989
11497
  }
9990
11498
  function runnerCommandLabel(commandKind) {
9991
11499
  if (commandKind === "update") return "update";
9992
11500
  if (commandKind === "restart") return "restart";
9993
11501
  if (commandKind === "implementationHandoffRecovery") return "handoff recovery";
11502
+ if (commandKind === "providerAuthLinkRequested") return "provider auth link";
9994
11503
  return "remove";
9995
11504
  }
11505
+ function mergeProviderAuthStatuses(existingStatuses, nextStatus) {
11506
+ const statuses = existingStatuses?.filter((status) => status.providerId !== nextStatus.providerId || status.providerClientId !== nextStatus.providerClientId || status.routeType !== nextStatus.routeType) ?? [];
11507
+ return [...statuses, nextStatus].slice(-20);
11508
+ }
9996
11509
  async function replayPendingBrainGenerationFinalizations({ accountId, apiClient, projectId, repositoryLinkId, runnerId }) {
9997
11510
  const pendingEntries = await listPendingBrainGenerationFinalizations({ accountId, projectId, repositoryLinkId, runnerId });
9998
11511
  if (!pendingEntries.length) {
@@ -10038,7 +11551,7 @@ async function submitBrainGenerationFinalizationEntry(apiClient, entry, options
10038
11551
  try {
10039
11552
  result = await apiClient.submitBrainGenerationResult(entry.projectId, entry.workItemId, entry.result);
10040
11553
  } catch (error) {
10041
- const detail = truncateLogExcerpt(errorMessage3(error));
11554
+ const detail = truncateLogExcerpt(errorMessage6(error));
10042
11555
  if (isRetryableApiError(error)) {
10043
11556
  const updated = await markBrainGenerationFinalizationRetry(entry, detail);
10044
11557
  const message2 = `Pending brain generation finalization ${entry.workItemId} could not be replayed yet (${updated.retryCount} attempt${updated.retryCount === 1 ? "" : "s"}): ${detail}`;
@@ -10051,7 +11564,7 @@ async function submitBrainGenerationFinalizationEntry(apiClient, entry, options
10051
11564
  return { status: "failed", message, retryable: false };
10052
11565
  }
10053
11566
  await deleteBrainGenerationFinalizationEntry(entry).catch((error) => {
10054
- console.error(`delete pending brain generation finalization ${entry.workItemId} failed: ${errorMessage3(error)}`);
11567
+ console.error(`delete pending brain generation finalization ${entry.workItemId} failed: ${errorMessage6(error)}`);
10055
11568
  });
10056
11569
  if (options.recordReplayTelemetry) {
10057
11570
  await recordRunnerMilestone(apiClient, entry.projectId, result.workItem, entry.runnerId, entry.repositoryLinkId, {
@@ -10072,7 +11585,7 @@ async function submitDurableResultFinalizationEntry(apiClient, entry, options =
10072
11585
  try {
10073
11586
  response = await submitDurableResultMutation(apiClient, entry);
10074
11587
  } catch (error) {
10075
- const detail = truncateLogExcerpt(errorMessage3(error));
11588
+ const detail = truncateLogExcerpt(errorMessage6(error));
10076
11589
  if (isRetryableApiError(error)) {
10077
11590
  const updated = await markDurableResultFinalizationRetry(entry, detail);
10078
11591
  const message2 = `Pending ${runnerResultFinalizationLabel(entry)} finalization ${entry.workItemId} could not be replayed yet (${updated.retryCount} attempt${updated.retryCount === 1 ? "" : "s"}): ${detail}`;
@@ -10086,7 +11599,7 @@ async function submitDurableResultFinalizationEntry(apiClient, entry, options =
10086
11599
  }
10087
11600
  const workItem = durableResultResponseWorkItem(response);
10088
11601
  await deleteDurableResultFinalizationEntry(entry).catch((error) => {
10089
- console.error(`delete pending ${runnerResultFinalizationLabel(entry)} finalization ${entry.workItemId} failed: ${errorMessage3(error)}`);
11602
+ console.error(`delete pending ${runnerResultFinalizationLabel(entry)} finalization ${entry.workItemId} failed: ${errorMessage6(error)}`);
10090
11603
  });
10091
11604
  if (options.recordReplayTelemetry) {
10092
11605
  await recordRunnerMilestone(apiClient, entry.projectId, workItem, entry.runnerId, entry.repositoryLinkId, {
@@ -10211,7 +11724,7 @@ async function finalizeBrainGenerationWork({
10211
11724
  artifacts = parseBrainGenerationArtifacts(`${toolResult.stdout}
10212
11725
  ${toolResult.stderr}`);
10213
11726
  } catch (error) {
10214
- generationError = errorMessage3(error);
11727
+ generationError = errorMessage6(error);
10215
11728
  }
10216
11729
  } else {
10217
11730
  generationError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
@@ -10229,7 +11742,7 @@ ${toolResult.stderr}`);
10229
11742
  const resultMutation = {
10230
11743
  status: "completed",
10231
11744
  runnerId,
10232
- idempotencyKey: `generation_${workItem.workItemId}_${workItem.attempt}_${randomUUID()}`,
11745
+ idempotencyKey: `generation_${workItem.workItemId}_${workItem.attempt}_${randomUUID2()}`,
10233
11746
  artifacts,
10234
11747
  tool: toolName,
10235
11748
  durationMs,
@@ -10278,7 +11791,7 @@ ${toolResult.stderr}`);
10278
11791
  metadata: { tool: toolName, durationMs, artifactCount: replay.documentCount, verificationSummary: "Generated artifacts were accepted by the Amistio API for review." }
10279
11792
  });
10280
11793
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig)).catch((error) => {
10281
- console.error(`send generation completion heartbeat failed: ${errorMessage3(error)}`);
11794
+ console.error(`send generation completion heartbeat failed: ${errorMessage6(error)}`);
10282
11795
  });
10283
11796
  console.log(workItem.workKind === "planRevision" ? "Revised plan returned for review." : `Generated ${replay.documentCount} brain artifact${replay.documentCount === 1 ? "" : "s"} for review.`);
10284
11797
  return { status: "completed", exitCode: 0 };
@@ -10301,10 +11814,10 @@ ${toolResult.stderr}`);
10301
11814
  ...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
10302
11815
  ...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
10303
11816
  };
10304
- const failedResult = await apiClient.submitBrainGenerationResult(projectId, workItem.workItemId, {
11817
+ const failedResult2 = await apiClient.submitBrainGenerationResult(projectId, workItem.workItemId, {
10305
11818
  status: "failed",
10306
11819
  runnerId,
10307
- idempotencyKey: `generation_${workItem.workItemId}_${randomUUID()}`,
11820
+ idempotencyKey: `generation_${workItem.workItemId}_${randomUUID2()}`,
10308
11821
  tool: toolName,
10309
11822
  durationMs,
10310
11823
  ...failedSessionTelemetry,
@@ -10314,7 +11827,7 @@ ${toolResult.stderr}`);
10314
11827
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10315
11828
  status: "failed",
10316
11829
  summary: generationError ?? `${toolName} did not produce valid brain artifacts.`,
10317
- idempotencyKey: `runner_milestone_generation_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
11830
+ idempotencyKey: `runner_milestone_generation_failed_${workItem.workItemId}_${failedResult2.workItem.idempotencyKey}`,
10318
11831
  metadata: { tool: toolName, durationMs, verificationSummary: "Generation output could not be parsed into approved artifact JSON." }
10319
11832
  });
10320
11833
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
@@ -10343,7 +11856,7 @@ async function finalizeAssistantQuestionWork({
10343
11856
  answerResult = parseAssistantAnswerResult(`${toolResult.stdout}
10344
11857
  ${toolResult.stderr}`);
10345
11858
  } catch (error) {
10346
- answerError = errorMessage3(error);
11859
+ answerError = errorMessage6(error);
10347
11860
  }
10348
11861
  } else {
10349
11862
  answerError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
@@ -10354,7 +11867,7 @@ ${toolResult.stderr}`);
10354
11867
  const resultMutation = {
10355
11868
  status: "completed",
10356
11869
  runnerId,
10357
- idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID()}`,
11870
+ idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID2()}`,
10358
11871
  answer: answerResult.answer,
10359
11872
  sourceBoundary: answerResult.sourceBoundary,
10360
11873
  citations: answerResult.citations,
@@ -10390,14 +11903,14 @@ ${toolResult.stderr}`);
10390
11903
  const failedMutation = {
10391
11904
  status: "failed",
10392
11905
  runnerId,
10393
- idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID()}`,
11906
+ idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID2()}`,
10394
11907
  tool: toolName,
10395
11908
  durationMs,
10396
11909
  ...sessionTelemetry,
10397
11910
  message: `${toolName} did not produce a valid project knowledge answer.`,
10398
11911
  ...answerError ? { error: answerError } : {}
10399
11912
  };
10400
- const failedResult = await submitPrimaryRunnerResult(apiClient, {
11913
+ const failedResult2 = await submitPrimaryRunnerResult(apiClient, {
10401
11914
  accountId: workItem.accountId,
10402
11915
  projectId,
10403
11916
  repositoryLinkId,
@@ -10409,12 +11922,12 @@ ${toolResult.stderr}`);
10409
11922
  idempotencyKey: failedMutation.idempotencyKey,
10410
11923
  result: failedMutation
10411
11924
  });
10412
- if (!failedResult) return { status: "failed", exitCode: 1 };
11925
+ if (!failedResult2) return { status: "failed", exitCode: 1 };
10413
11926
  await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10414
11927
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10415
11928
  status: "failed",
10416
11929
  summary: answerError ?? `${toolName} did not produce a valid project knowledge answer.`,
10417
- idempotencyKey: `runner_milestone_assistant_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
11930
+ idempotencyKey: `runner_milestone_assistant_failed_${workItem.workItemId}_${failedResult2.workItem.idempotencyKey}`,
10418
11931
  metadata: { tool: toolName, durationMs, verificationSummary: "Assistant output did not include a valid structured answer." }
10419
11932
  });
10420
11933
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
@@ -10441,7 +11954,7 @@ async function finalizeImpactPreviewWork({
10441
11954
  report = parseImpactPreviewResult(`${toolResult.stdout}
10442
11955
  ${toolResult.stderr}`);
10443
11956
  } catch (error) {
10444
- previewError = errorMessage3(error);
11957
+ previewError = errorMessage6(error);
10445
11958
  }
10446
11959
  } else {
10447
11960
  previewError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
@@ -10453,7 +11966,7 @@ ${toolResult.stderr}`);
10453
11966
  const resultMutation = {
10454
11967
  status: "completed",
10455
11968
  runnerId,
10456
- idempotencyKey: `impact_${workItem.workItemId}_${randomUUID()}`,
11969
+ idempotencyKey: `impact_${workItem.workItemId}_${randomUUID2()}`,
10457
11970
  report: {
10458
11971
  ...report,
10459
11972
  analyzedRepoRevision: report.analyzedRepoRevision ?? metadata?.lastSyncedRevision
@@ -10490,14 +12003,14 @@ ${toolResult.stderr}`);
10490
12003
  const failedMutation = {
10491
12004
  status: "failed",
10492
12005
  runnerId,
10493
- idempotencyKey: `impact_${workItem.workItemId}_${randomUUID()}`,
12006
+ idempotencyKey: `impact_${workItem.workItemId}_${randomUUID2()}`,
10494
12007
  tool: toolName,
10495
12008
  durationMs,
10496
12009
  ...sessionTelemetry,
10497
12010
  message: `${toolName} did not produce a valid impact preview.`,
10498
12011
  ...previewError ? { error: previewError } : {}
10499
12012
  };
10500
- const failedResult = await submitPrimaryRunnerResult(apiClient, {
12013
+ const failedResult2 = await submitPrimaryRunnerResult(apiClient, {
10501
12014
  accountId: workItem.accountId,
10502
12015
  projectId,
10503
12016
  repositoryLinkId,
@@ -10509,12 +12022,12 @@ ${toolResult.stderr}`);
10509
12022
  idempotencyKey: failedMutation.idempotencyKey,
10510
12023
  result: failedMutation
10511
12024
  });
10512
- if (!failedResult) return { status: "failed", exitCode: 1 };
12025
+ if (!failedResult2) return { status: "failed", exitCode: 1 };
10513
12026
  await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10514
12027
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10515
12028
  status: "failed",
10516
12029
  summary: previewError ?? `${toolName} did not produce a valid impact preview.`,
10517
- idempotencyKey: `runner_milestone_impact_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
12030
+ idempotencyKey: `runner_milestone_impact_failed_${workItem.workItemId}_${failedResult2.workItem.idempotencyKey}`,
10518
12031
  metadata: { tool: toolName, durationMs, verificationSummary: "Impact preview output did not include valid structured JSON." }
10519
12032
  });
10520
12033
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
@@ -10540,7 +12053,7 @@ async function finalizeIssueDiagnosisWork({
10540
12053
  diagnosis = parseIssueDiagnosisResult(`${toolResult.stdout}
10541
12054
  ${toolResult.stderr}`);
10542
12055
  } catch (error) {
10543
- diagnosisError = errorMessage3(error);
12056
+ diagnosisError = errorMessage6(error);
10544
12057
  }
10545
12058
  } else {
10546
12059
  diagnosisError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
@@ -10551,7 +12064,7 @@ ${toolResult.stderr}`);
10551
12064
  const resultMutation = {
10552
12065
  status: "completed",
10553
12066
  runnerId,
10554
- idempotencyKey: `issue_${workItem.workItemId}_${randomUUID()}`,
12067
+ idempotencyKey: `issue_${workItem.workItemId}_${randomUUID2()}`,
10555
12068
  diagnosis,
10556
12069
  tool: toolName,
10557
12070
  durationMs,
@@ -10585,14 +12098,14 @@ ${toolResult.stderr}`);
10585
12098
  const failedMutation = {
10586
12099
  status: "failed",
10587
12100
  runnerId,
10588
- idempotencyKey: `issue_${workItem.workItemId}_${randomUUID()}`,
12101
+ idempotencyKey: `issue_${workItem.workItemId}_${randomUUID2()}`,
10589
12102
  tool: toolName,
10590
12103
  durationMs,
10591
12104
  ...sessionTelemetry,
10592
12105
  message: `${toolName} did not produce a valid issue diagnosis.`,
10593
12106
  ...diagnosisError ? { error: diagnosisError } : {}
10594
12107
  };
10595
- const failedResult = await submitPrimaryRunnerResult(apiClient, {
12108
+ const failedResult2 = await submitPrimaryRunnerResult(apiClient, {
10596
12109
  accountId: workItem.accountId,
10597
12110
  projectId,
10598
12111
  repositoryLinkId,
@@ -10604,12 +12117,12 @@ ${toolResult.stderr}`);
10604
12117
  idempotencyKey: failedMutation.idempotencyKey,
10605
12118
  result: failedMutation
10606
12119
  });
10607
- if (!failedResult) return { status: "failed", exitCode: 1 };
12120
+ if (!failedResult2) return { status: "failed", exitCode: 1 };
10608
12121
  await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10609
12122
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10610
12123
  status: "failed",
10611
12124
  summary: diagnosisError ?? `${toolName} did not produce a valid issue diagnosis.`,
10612
- idempotencyKey: `runner_milestone_issue_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
12125
+ idempotencyKey: `runner_milestone_issue_failed_${workItem.workItemId}_${failedResult2.workItem.idempotencyKey}`,
10613
12126
  metadata: { tool: toolName, durationMs, verificationSummary: "Issue diagnosis output did not include valid structured JSON." }
10614
12127
  });
10615
12128
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
@@ -10635,7 +12148,7 @@ async function finalizeSecurityPostureScanWork({
10635
12148
  scanResult = parseSecurityPostureScanResult(`${toolResult.stdout}
10636
12149
  ${toolResult.stderr}`);
10637
12150
  } catch (error) {
10638
- scanError = errorMessage3(error);
12151
+ scanError = errorMessage6(error);
10639
12152
  }
10640
12153
  } else {
10641
12154
  scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
@@ -10646,7 +12159,7 @@ ${toolResult.stderr}`);
10646
12159
  const resultMutation = {
10647
12160
  status: "completed",
10648
12161
  runnerId,
10649
- idempotencyKey: `security_${workItem.workItemId}_${randomUUID()}`,
12162
+ idempotencyKey: `security_${workItem.workItemId}_${randomUUID2()}`,
10650
12163
  result: scanResult,
10651
12164
  tool: toolName,
10652
12165
  durationMs,
@@ -10680,14 +12193,14 @@ ${toolResult.stderr}`);
10680
12193
  const failedMutation = {
10681
12194
  status: "failed",
10682
12195
  runnerId,
10683
- idempotencyKey: `security_${workItem.workItemId}_${randomUUID()}`,
12196
+ idempotencyKey: `security_${workItem.workItemId}_${randomUUID2()}`,
10684
12197
  tool: toolName,
10685
12198
  durationMs,
10686
12199
  ...sessionTelemetry,
10687
12200
  message: `${toolName} did not produce a valid security posture scan.`,
10688
12201
  ...scanError ? { error: scanError } : {}
10689
12202
  };
10690
- const failedResult = await submitPrimaryRunnerResult(apiClient, {
12203
+ const failedResult2 = await submitPrimaryRunnerResult(apiClient, {
10691
12204
  accountId: workItem.accountId,
10692
12205
  projectId,
10693
12206
  repositoryLinkId,
@@ -10699,12 +12212,12 @@ ${toolResult.stderr}`);
10699
12212
  idempotencyKey: failedMutation.idempotencyKey,
10700
12213
  result: failedMutation
10701
12214
  });
10702
- if (!failedResult) return { status: "failed", exitCode: 1 };
12215
+ if (!failedResult2) return { status: "failed", exitCode: 1 };
10703
12216
  await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10704
12217
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10705
12218
  status: "failed",
10706
12219
  summary: scanError ?? `${toolName} did not produce a valid security posture scan.`,
10707
- idempotencyKey: `runner_milestone_security_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
12220
+ idempotencyKey: `runner_milestone_security_failed_${workItem.workItemId}_${failedResult2.workItem.idempotencyKey}`,
10708
12221
  metadata: { tool: toolName, durationMs, verificationSummary: "Security posture output did not include valid structured JSON." }
10709
12222
  });
10710
12223
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
@@ -10730,7 +12243,7 @@ async function finalizeAppEvaluationScanWork({
10730
12243
  scanResult = parseAppEvaluationScanResult(`${toolResult.stdout}
10731
12244
  ${toolResult.stderr}`);
10732
12245
  } catch (error) {
10733
- scanError = errorMessage3(error);
12246
+ scanError = errorMessage6(error);
10734
12247
  }
10735
12248
  } else {
10736
12249
  scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
@@ -10741,7 +12254,7 @@ ${toolResult.stderr}`);
10741
12254
  const resultMutation = {
10742
12255
  status: "completed",
10743
12256
  runnerId,
10744
- idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID()}`,
12257
+ idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID2()}`,
10745
12258
  result: scanResult,
10746
12259
  tool: toolName,
10747
12260
  durationMs,
@@ -10775,14 +12288,14 @@ ${toolResult.stderr}`);
10775
12288
  const failedMutation = {
10776
12289
  status: "failed",
10777
12290
  runnerId,
10778
- idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID()}`,
12291
+ idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID2()}`,
10779
12292
  tool: toolName,
10780
12293
  durationMs,
10781
12294
  ...sessionTelemetry,
10782
12295
  message: `${toolName} did not produce a valid app evaluation scan.`,
10783
12296
  ...scanError ? { error: scanError } : {}
10784
12297
  };
10785
- const failedResult = await submitPrimaryRunnerResult(apiClient, {
12298
+ const failedResult2 = await submitPrimaryRunnerResult(apiClient, {
10786
12299
  accountId: workItem.accountId,
10787
12300
  projectId,
10788
12301
  repositoryLinkId,
@@ -10794,12 +12307,12 @@ ${toolResult.stderr}`);
10794
12307
  idempotencyKey: failedMutation.idempotencyKey,
10795
12308
  result: failedMutation
10796
12309
  });
10797
- if (!failedResult) return { status: "failed", exitCode: 1 };
12310
+ if (!failedResult2) return { status: "failed", exitCode: 1 };
10798
12311
  await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10799
12312
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10800
12313
  status: "failed",
10801
12314
  summary: scanError ?? `${toolName} did not produce a valid app evaluation scan.`,
10802
- idempotencyKey: `runner_milestone_app_evaluation_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
12315
+ idempotencyKey: `runner_milestone_app_evaluation_failed_${workItem.workItemId}_${failedResult2.workItem.idempotencyKey}`,
10803
12316
  metadata: { tool: toolName, durationMs, verificationSummary: "App evaluation output did not include valid structured JSON." }
10804
12317
  });
10805
12318
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
@@ -10825,7 +12338,7 @@ async function finalizeBrainConsolidationScanWork({
10825
12338
  scanResult = parseBrainConsolidationScanResult(`${toolResult.stdout}
10826
12339
  ${toolResult.stderr}`);
10827
12340
  } catch (error) {
10828
- scanError = errorMessage3(error);
12341
+ scanError = errorMessage6(error);
10829
12342
  }
10830
12343
  } else {
10831
12344
  scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
@@ -10836,7 +12349,7 @@ ${toolResult.stderr}`);
10836
12349
  const resultMutation = {
10837
12350
  status: "completed",
10838
12351
  runnerId,
10839
- idempotencyKey: `brain_consolidation_${workItem.workItemId}_${randomUUID()}`,
12352
+ idempotencyKey: `brain_consolidation_${workItem.workItemId}_${randomUUID2()}`,
10840
12353
  result: scanResult,
10841
12354
  tool: toolName,
10842
12355
  durationMs,
@@ -10870,14 +12383,14 @@ ${toolResult.stderr}`);
10870
12383
  const failedMutation = {
10871
12384
  status: "failed",
10872
12385
  runnerId,
10873
- idempotencyKey: `brain_consolidation_${workItem.workItemId}_${randomUUID()}`,
12386
+ idempotencyKey: `brain_consolidation_${workItem.workItemId}_${randomUUID2()}`,
10874
12387
  tool: toolName,
10875
12388
  durationMs,
10876
12389
  ...sessionTelemetry,
10877
12390
  message: `${toolName} did not produce a valid semantic brain consolidation scan.`,
10878
12391
  ...scanError ? { error: scanError } : {}
10879
12392
  };
10880
- const failedResult = await submitPrimaryRunnerResult(apiClient, {
12393
+ const failedResult2 = await submitPrimaryRunnerResult(apiClient, {
10881
12394
  accountId: workItem.accountId,
10882
12395
  projectId,
10883
12396
  repositoryLinkId,
@@ -10889,12 +12402,12 @@ ${toolResult.stderr}`);
10889
12402
  idempotencyKey: failedMutation.idempotencyKey,
10890
12403
  result: failedMutation
10891
12404
  });
10892
- if (!failedResult) return { status: "failed", exitCode: 1 };
12405
+ if (!failedResult2) return { status: "failed", exitCode: 1 };
10893
12406
  await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
10894
12407
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
10895
12408
  status: "failed",
10896
12409
  summary: scanError ?? `${toolName} did not produce a valid semantic brain consolidation scan.`,
10897
- idempotencyKey: `runner_milestone_brain_consolidation_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
12410
+ idempotencyKey: `runner_milestone_brain_consolidation_failed_${workItem.workItemId}_${failedResult2.workItem.idempotencyKey}`,
10898
12411
  metadata: { tool: toolName, durationMs, verificationSummary: "Brain consolidation output did not include valid structured JSON." }
10899
12412
  });
10900
12413
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
@@ -10921,7 +12434,7 @@ async function finalizeProjectContextRefreshWork({
10921
12434
  refreshResult = parseProjectContextRefreshResult(`${toolResult.stdout}
10922
12435
  ${toolResult.stderr}`, { repositoryRoot: executionRoot });
10923
12436
  } catch (error) {
10924
- refreshError = errorMessage3(error);
12437
+ refreshError = errorMessage6(error);
10925
12438
  }
10926
12439
  } else {
10927
12440
  refreshError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
@@ -10932,7 +12445,7 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
10932
12445
  const resultMutation = {
10933
12446
  status: "completed",
10934
12447
  runnerId,
10935
- idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID()}`,
12448
+ idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID2()}`,
10936
12449
  result: refreshResult,
10937
12450
  tool: toolName,
10938
12451
  durationMs,
@@ -10978,14 +12491,14 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
10978
12491
  const failedMutation = {
10979
12492
  status: "failed",
10980
12493
  runnerId,
10981
- idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID()}`,
12494
+ idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID2()}`,
10982
12495
  tool: toolName,
10983
12496
  durationMs,
10984
12497
  ...sessionTelemetry,
10985
12498
  message: `${toolName} did not produce a valid project context refresh.`,
10986
12499
  ...refreshError ? { error: refreshError } : {}
10987
12500
  };
10988
- const failedResult = await submitPrimaryRunnerResult(apiClient, {
12501
+ const failedResult2 = await submitPrimaryRunnerResult(apiClient, {
10989
12502
  accountId: workItem.accountId,
10990
12503
  projectId,
10991
12504
  repositoryLinkId,
@@ -10997,12 +12510,12 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
10997
12510
  idempotencyKey: failedMutation.idempotencyKey,
10998
12511
  result: failedMutation
10999
12512
  });
11000
- if (!failedResult) return { status: "failed", exitCode: 1 };
12513
+ if (!failedResult2) return { status: "failed", exitCode: 1 };
11001
12514
  await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
11002
12515
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
11003
12516
  status: "failed",
11004
12517
  summary: refreshError ?? `${toolName} did not produce a valid project context refresh.`,
11005
- idempotencyKey: `runner_milestone_project_context_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
12518
+ idempotencyKey: `runner_milestone_project_context_failed_${workItem.workItemId}_${failedResult2.workItem.idempotencyKey}`,
11006
12519
  metadata: { tool: toolName, durationMs, verificationSummary: "Project context output did not include valid structured JSON." }
11007
12520
  });
11008
12521
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
@@ -11028,7 +12541,7 @@ async function finalizeImplementationVerificationWork({
11028
12541
  verificationResult = parseImplementationVerificationResult(`${toolResult.stdout}
11029
12542
  ${toolResult.stderr}`);
11030
12543
  } catch (error) {
11031
- verificationError = errorMessage3(error);
12544
+ verificationError = errorMessage6(error);
11032
12545
  }
11033
12546
  } else {
11034
12547
  verificationError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
@@ -11039,7 +12552,7 @@ ${toolResult.stderr}`);
11039
12552
  const resultMutation = {
11040
12553
  status: "completed",
11041
12554
  runnerId,
11042
- idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID()}`,
12555
+ idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID2()}`,
11043
12556
  result: verificationResult,
11044
12557
  tool: toolName,
11045
12558
  durationMs,
@@ -11073,14 +12586,14 @@ ${toolResult.stderr}`);
11073
12586
  const failedMutation = {
11074
12587
  status: "failed",
11075
12588
  runnerId,
11076
- idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID()}`,
12589
+ idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID2()}`,
11077
12590
  tool: toolName,
11078
12591
  durationMs,
11079
12592
  ...sessionTelemetry,
11080
12593
  message: `${toolName} did not produce valid implementation verification proof.`,
11081
12594
  ...verificationError ? { error: verificationError } : {}
11082
12595
  };
11083
- const failedResult = await submitPrimaryRunnerResult(apiClient, {
12596
+ const failedResult2 = await submitPrimaryRunnerResult(apiClient, {
11084
12597
  accountId: workItem.accountId,
11085
12598
  projectId,
11086
12599
  repositoryLinkId,
@@ -11092,12 +12605,12 @@ ${toolResult.stderr}`);
11092
12605
  idempotencyKey: failedMutation.idempotencyKey,
11093
12606
  result: failedMutation
11094
12607
  });
11095
- if (!failedResult) return { status: "failed", exitCode: 1 };
12608
+ if (!failedResult2) return { status: "failed", exitCode: 1 };
11096
12609
  await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
11097
12610
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
11098
12611
  status: "failed",
11099
12612
  summary: verificationError ?? `${toolName} did not produce valid implementation verification proof.`,
11100
- idempotencyKey: `runner_milestone_implementation_verification_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
12613
+ idempotencyKey: `runner_milestone_implementation_verification_failed_${workItem.workItemId}_${failedResult2.workItem.idempotencyKey}`,
11101
12614
  metadata: { tool: toolName, durationMs, verificationSummary: "Implementation verification output did not include valid structured JSON." }
11102
12615
  });
11103
12616
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
@@ -11123,7 +12636,7 @@ async function finalizeTestQualityScanWork({
11123
12636
  scanResult = parseTestQualityScanResult(`${toolResult.stdout}
11124
12637
  ${toolResult.stderr}`);
11125
12638
  } catch (error) {
11126
- scanError = errorMessage3(error);
12639
+ scanError = errorMessage6(error);
11127
12640
  }
11128
12641
  } else {
11129
12642
  scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
@@ -11134,7 +12647,7 @@ ${toolResult.stderr}`);
11134
12647
  const resultMutation = {
11135
12648
  status: "completed",
11136
12649
  runnerId,
11137
- idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID()}`,
12650
+ idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID2()}`,
11138
12651
  result: scanResult,
11139
12652
  tool: toolName,
11140
12653
  durationMs,
@@ -11168,14 +12681,14 @@ ${toolResult.stderr}`);
11168
12681
  const failedMutation = {
11169
12682
  status: "failed",
11170
12683
  runnerId,
11171
- idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID()}`,
12684
+ idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID2()}`,
11172
12685
  tool: toolName,
11173
12686
  durationMs,
11174
12687
  ...sessionTelemetry,
11175
12688
  message: `${toolName} did not produce a valid test quality scan.`,
11176
12689
  ...scanError ? { error: scanError } : {}
11177
12690
  };
11178
- const failedResult = await submitPrimaryRunnerResult(apiClient, {
12691
+ const failedResult2 = await submitPrimaryRunnerResult(apiClient, {
11179
12692
  accountId: workItem.accountId,
11180
12693
  projectId,
11181
12694
  repositoryLinkId,
@@ -11187,12 +12700,12 @@ ${toolResult.stderr}`);
11187
12700
  idempotencyKey: failedMutation.idempotencyKey,
11188
12701
  result: failedMutation
11189
12702
  });
11190
- if (!failedResult) return { status: "failed", exitCode: 1 };
12703
+ if (!failedResult2) return { status: "failed", exitCode: 1 };
11191
12704
  await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
11192
12705
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
11193
12706
  status: "failed",
11194
12707
  summary: scanError ?? `${toolName} did not produce a valid test quality scan.`,
11195
- idempotencyKey: `runner_milestone_test_quality_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
12708
+ idempotencyKey: `runner_milestone_test_quality_failed_${workItem.workItemId}_${failedResult2.workItem.idempotencyKey}`,
11196
12709
  metadata: { tool: toolName, durationMs, verificationSummary: "Test quality output did not include valid structured JSON." }
11197
12710
  });
11198
12711
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
@@ -11218,7 +12731,7 @@ async function finalizeImplementationTestGateWork({
11218
12731
  gateResult = parseImplementationTestGateResult(`${toolResult.stdout}
11219
12732
  ${toolResult.stderr}`);
11220
12733
  } catch (error) {
11221
- gateError = errorMessage3(error);
12734
+ gateError = errorMessage6(error);
11222
12735
  }
11223
12736
  } else {
11224
12737
  gateError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
@@ -11229,7 +12742,7 @@ ${toolResult.stderr}`);
11229
12742
  const resultMutation = {
11230
12743
  status: "completed",
11231
12744
  runnerId,
11232
- idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID()}`,
12745
+ idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID2()}`,
11233
12746
  result: gateResult,
11234
12747
  tool: toolName,
11235
12748
  durationMs,
@@ -11263,14 +12776,14 @@ ${toolResult.stderr}`);
11263
12776
  const failedMutation = {
11264
12777
  status: "failed",
11265
12778
  runnerId,
11266
- idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID()}`,
12779
+ idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID2()}`,
11267
12780
  tool: toolName,
11268
12781
  durationMs,
11269
12782
  ...sessionTelemetry,
11270
12783
  message: `${toolName} did not produce a valid implementation test gate result.`,
11271
12784
  ...gateError ? { error: gateError } : {}
11272
12785
  };
11273
- const failedResult = await submitPrimaryRunnerResult(apiClient, {
12786
+ const failedResult2 = await submitPrimaryRunnerResult(apiClient, {
11274
12787
  accountId: workItem.accountId,
11275
12788
  projectId,
11276
12789
  repositoryLinkId,
@@ -11282,12 +12795,12 @@ ${toolResult.stderr}`);
11282
12795
  idempotencyKey: failedMutation.idempotencyKey,
11283
12796
  result: failedMutation
11284
12797
  });
11285
- if (!failedResult) return { status: "failed", exitCode: 1 };
12798
+ if (!failedResult2) return { status: "failed", exitCode: 1 };
11286
12799
  await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
11287
12800
  await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
11288
12801
  status: "failed",
11289
12802
  summary: gateError ?? `${toolName} did not produce a valid implementation test gate result.`,
11290
- idempotencyKey: `runner_milestone_implementation_test_gate_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
12803
+ idempotencyKey: `runner_milestone_implementation_test_gate_failed_${workItem.workItemId}_${failedResult2.workItem.idempotencyKey}`,
11291
12804
  metadata: { tool: toolName, durationMs, verificationSummary: "Implementation test gate output did not include valid structured JSON." }
11292
12805
  });
11293
12806
  await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
@@ -11492,7 +13005,7 @@ async function prepareToolSession({
11492
13005
  });
11493
13006
  return { ...selection, toolSession: toolSession2 };
11494
13007
  }
11495
- const toolSessionId = `tool_session_${randomUUID()}`;
13008
+ const toolSessionId = `tool_session_${randomUUID2()}`;
11496
13009
  const { toolSession } = await apiClient.createToolSession(projectId, {
11497
13010
  toolSessionId,
11498
13011
  repositoryLinkId,
@@ -11586,7 +13099,7 @@ function summarizeToolOutput(value) {
11586
13099
  }
11587
13100
  return trimmed.length > 300 ? `${trimmed.slice(0, 300)}...` : trimmed;
11588
13101
  }
11589
- function errorMessage3(error) {
13102
+ function errorMessage6(error) {
11590
13103
  return error instanceof Error ? error.message : String(error);
11591
13104
  }
11592
13105
  function errorDetail(error) {
@@ -11596,6 +13109,9 @@ function truncateLogExcerpt(value) {
11596
13109
  const trimmed = value.trim();
11597
13110
  return trimmed.length > 1200 ? `${trimmed.slice(0, 1200)}...` : trimmed;
11598
13111
  }
13112
+ function formatHostHelperCapability(label, support) {
13113
+ return `${label}: ${support.supported ? "supported" : "unsupported"}${support.reason ? ` (${support.reason})` : ""}`;
13114
+ }
11599
13115
  function collectRepeatedOption(value, previous) {
11600
13116
  return [...previous, value];
11601
13117
  }
@@ -11622,10 +13138,10 @@ function parseReasoningEffort(value) {
11622
13138
  throw new Error(`Expected reasoning effort auto, low, medium, high, or xhigh; received ${value}.`);
11623
13139
  }
11624
13140
  function inferRepoName(root) {
11625
- return path16.basename(path16.resolve(root)) || "repository";
13141
+ return path17.basename(path17.resolve(root)) || "repository";
11626
13142
  }
11627
13143
  function createRepoFingerprint(accountId, projectId, repositoryLinkId) {
11628
- return createHash8("sha256").update(`${accountId}:${projectId}:${repositoryLinkId}`).digest("hex");
13144
+ return createHash9("sha256").update(`${accountId}:${projectId}:${repositoryLinkId}`).digest("hex");
11629
13145
  }
11630
13146
  function defaultApiUrl() {
11631
13147
  const envApiUrl = process.env[AMISTIO_API_URL_ENV]?.trim();
@@ -11660,6 +13176,13 @@ async function resolveRunnerToolConfig({ apiClient, explicitInvocationChannel, e
11660
13176
  }
11661
13177
  if (explicitTool === "none") {
11662
13178
  const modelConfig2 = normalizeModelConfig({ model: explicitModel, providerId: explicitProviderId, modelId: explicitModelId, modelVariant: explicitModelVariant, reasoningEffort: explicitReasoningEffort });
13179
+ const directModelResolution = resolveDirectModelClientPreference({ ...modelConfig2, tool: "none", requestedInvocationChannel: explicitInvocationChannel ?? "auto" });
13180
+ if (directModelResolution) {
13181
+ if (!directModelResolution.ready) {
13182
+ return unavailableToolConfig({ capabilities, source: "cli", status: directModelResolution.status, message: directModelResolution.message, tool: "none", requestedInvocationChannel: explicitInvocationChannel ?? "auto", ...modelConfig2 });
13183
+ }
13184
+ return { ready: true, tool: "none", capabilities, source: "cli", status: "resolved", requestedInvocationChannel: explicitInvocationChannel ?? "auto", effectiveInvocationChannel: "sdk", ...directModelResolution.config, message: "Using Amistio direct model client." };
13185
+ }
11663
13186
  if (hasModelConfig(modelConfig2)) {
11664
13187
  return unavailableToolConfig({ capabilities, source: "cli", status: "modelUnsupported", message: "Model configuration cannot be used with --tool none.", tool: "none", requestedInvocationChannel: explicitInvocationChannel ?? "auto", ...modelConfig2 });
11665
13188
  }
@@ -11684,6 +13207,13 @@ async function resolveRunnerToolConfig({ apiClient, explicitInvocationChannel, e
11684
13207
  }
11685
13208
  function resolveRequestedTool({ capabilities, modelConfig, requestedInvocationChannel, requestedTool, source }) {
11686
13209
  const needsModelSelection = hasModelConfig(modelConfig);
13210
+ const directModelResolution = resolveDirectModelClientPreference({ ...modelConfig, tool: requestedTool, requestedInvocationChannel });
13211
+ if (directModelResolution) {
13212
+ if (!directModelResolution.ready) {
13213
+ return unavailableToolConfig({ capabilities, source, status: directModelResolution.status, requestedTool, requestedInvocationChannel, tool: requestedTool, ...modelConfig, message: directModelResolution.message });
13214
+ }
13215
+ return { ready: true, tool: requestedTool, capabilities, source, status: "resolved", requestedTool, requestedInvocationChannel, effectiveInvocationChannel: "sdk", ...directModelResolution.config, message: "Using Amistio direct model client." };
13216
+ }
11687
13217
  if (requestedTool === "auto") {
11688
13218
  const candidate = capabilities.find((capability2) => capability2.available && capabilitySupportsInvocationChannel(capability2, requestedInvocationChannel) && (!needsModelSelection || capability2.supportsModelSelection));
11689
13219
  if (!candidate) {
@@ -11910,7 +13440,7 @@ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode(), concurr
11910
13440
  };
11911
13441
  }
11912
13442
  function runnerMachineId() {
11913
- return createHash8("sha256").update(`${os8.hostname()}:${os8.platform()}:${os8.arch()}`).digest("hex").slice(0, 20);
13443
+ return createHash9("sha256").update(`${os8.hostname()}:${os8.platform()}:${os8.arch()}`).digest("hex").slice(0, 20);
11914
13444
  }
11915
13445
  async function delay(milliseconds) {
11916
13446
  await new Promise((resolve) => setTimeout(resolve, milliseconds));
@@ -11919,4 +13449,3 @@ program.parseAsync().catch((error) => {
11919
13449
  console.error(error instanceof Error ? error.message : String(error));
11920
13450
  process.exitCode = 1;
11921
13451
  });
11922
- //# sourceMappingURL=index.js.map