orkestrator 2.32.0-beta → 2.33.0-beta

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.
@@ -1669,7 +1669,7 @@ var shouldTransform = (res) => {
1669
1669
  };
1670
1670
 
1671
1671
  // ../../bridges/claude-bridge/src/index.ts
1672
- import { randomBytes, timingSafeEqual } from "node:crypto";
1672
+ import { randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
1673
1673
 
1674
1674
  // ../../bridges/claude-bridge/src/routes/health.ts
1675
1675
  var health = new Hono2;
@@ -2548,6 +2548,10 @@ function claudeCommandChangesDir() {
2548
2548
  function claudeChangeEvidenceDir() {
2549
2549
  return join(claudeHome(), ".claude", "orkestrator", "change-evidence");
2550
2550
  }
2551
+ function claudeCoordinatorSkillPluginsDir() {
2552
+ const configDir = overrideHome === null ? process.env.CLAUDE_CONFIG_DIR?.trim() : undefined;
2553
+ return join(configDir || join(claudeHome(), ".claude"), "orkestrator", "coordinator-skills");
2554
+ }
2551
2555
 
2552
2556
  // ../../bridges/claude-bridge/src/services/session-preferences.ts
2553
2557
  function parseTurnInFlightMark(value) {
@@ -3771,672 +3775,978 @@ function nextTurnGeneration() {
3771
3775
  turnGenerationCounter += 1;
3772
3776
  return turnGenerationCounter;
3773
3777
  }
3774
- // ../../bridges/claude-bridge/src/services/session-manager-runtime.ts
3775
- import { createHash as createHash5 } from "node:crypto";
3776
-
3777
- // ../../bridges/claude-bridge/src/services/plugin-config.ts
3778
- import { readdir, access } from "node:fs/promises";
3779
- import { join as join3 } from "node:path";
3780
-
3781
- // ../protocol/src/node/claude-plugin-set.ts
3782
- import path from "node:path";
3783
- var CLAUDE_PLUGINS_MARKER = "/.claude/plugins/";
3784
- function isRecord(value) {
3785
- return typeof value === "object" && value !== null && !Array.isArray(value);
3786
- }
3787
- function claudePluginConfigList(value) {
3788
- if (!Array.isArray(value))
3789
- return [];
3790
- const plugins = [];
3791
- for (const item of value) {
3792
- if (isRecord(item) && typeof item.path === "string" && item.path) {
3793
- plugins.push({ type: "local", path: item.path });
3794
- }
3795
- }
3796
- return plugins;
3797
- }
3798
- function isPathWithin(parentDir, candidatePath) {
3799
- const relativePath = path.relative(parentDir, candidatePath);
3800
- return relativePath === "" || !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
3801
- }
3802
- function remapInstalledPlugins(pluginsDir, installedPlugins, onUnsafe) {
3803
- const plugins = [];
3804
- const pluginsRoot = path.resolve(pluginsDir);
3805
- if (!isRecord(installedPlugins.plugins))
3806
- return plugins;
3807
- for (const [installedId, entries] of Object.entries(installedPlugins.plugins)) {
3808
- if (!Array.isArray(entries))
3809
- continue;
3810
- for (const entry of entries) {
3811
- if (!isRecord(entry) || typeof entry.installPath !== "string")
3812
- continue;
3813
- const installPath = entry.installPath;
3814
- const markerIdx = installPath.indexOf(CLAUDE_PLUGINS_MARKER);
3815
- let resolvedPath;
3816
- if (markerIdx !== -1) {
3817
- const relativePath = installPath.substring(markerIdx + CLAUDE_PLUGINS_MARKER.length);
3818
- const remappedPath = path.resolve(pluginsDir, relativePath);
3819
- if (!isPathWithin(pluginsRoot, remappedPath)) {
3820
- onUnsafe?.(installPath);
3821
- continue;
3822
- }
3823
- resolvedPath = remappedPath;
3824
- } else {
3825
- resolvedPath = installPath;
3826
- }
3827
- plugins.push({ type: "local", path: resolvedPath, installedId });
3828
- }
3829
- }
3830
- return plugins;
3831
- }
3832
- function resolveClaudePluginPath(pluginPath, cwd, home) {
3833
- if (pluginPath.startsWith("~"))
3834
- return path.join(home, pluginPath.slice(1));
3835
- if (pluginPath.startsWith("/"))
3836
- return pluginPath;
3837
- return path.join(cwd, pluginPath);
3778
+ // ../../bridges/claude-bridge/src/services/image-attachment-data.ts
3779
+ var SUPPORTED_IMAGE_MEDIA_TYPES = new Set([
3780
+ "image/jpeg",
3781
+ "image/png",
3782
+ "image/gif",
3783
+ "image/webp"
3784
+ ]);
3785
+ var MAX_IMAGE_ATTACHMENT_BYTES = 8 * 1024 * 1024;
3786
+ function decodedBase64ByteLength(value) {
3787
+ const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
3788
+ return value.length / 4 * 3 - padding;
3838
3789
  }
3839
- function mergeClaudePluginLayers(layers, options) {
3840
- const ordered = [
3841
- claudePluginConfigList(layers.global),
3842
- layers.cliInstalled,
3843
- ...options.includeProjectSources ? [claudePluginConfigList(layers.projectGlobal), claudePluginConfigList(layers.projectLocal)] : []
3844
- ];
3845
- const seen = new Set;
3846
- const paths = [];
3847
- for (const layer of ordered) {
3848
- for (const plugin of layer) {
3849
- const resolved = resolveClaudePluginPath(plugin.path, options.cwd, options.home);
3850
- if (seen.has(resolved))
3851
- continue;
3852
- seen.add(resolved);
3853
- paths.push(resolved);
3790
+ function parseBase64ImageData(value) {
3791
+ let data = value;
3792
+ let mediaType;
3793
+ if (value.startsWith("data:")) {
3794
+ const match = /^data:([^;,]+);base64,([\s\S]+)$/.exec(value);
3795
+ if (!match || !SUPPORTED_IMAGE_MEDIA_TYPES.has(match[1])) {
3796
+ return null;
3854
3797
  }
3798
+ mediaType = match[1];
3799
+ data = match[2];
3855
3800
  }
3856
- return paths;
3857
- }
3858
- var INVALID_PLUGIN_NAME = new RegExp("[@:\\s/\\\\\\u0000-\\u001f\\u007f\\u200b-\\u200f\\u2028-\\u202e\\u2060-\\u2064\\ufeff]");
3859
- var CLAUDE_PLUGIN_SETTING_SOURCES = ["local", "project", "user"];
3860
- function claudeInstalledPluginIdEnablement(id, settings, loadedSources) {
3861
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3862
- for (const source of ["local", "project", "user"]) {
3863
- if (!loadedSources.includes(source))
3864
- continue;
3865
- const document = settings[source];
3866
- const enabledPlugins = isRecord(document) ? document.enabledPlugins : undefined;
3867
- if (!isRecord(enabledPlugins) || !Object.hasOwn(enabledPlugins, id))
3868
- continue;
3869
- return { enabled: enabledPlugins[id] === true, decidedBy: { source, key: id } };
3801
+ const normalized = data.replace(/\s+/g, "");
3802
+ if (normalized.length === 0 || normalized.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(normalized) || decodedBase64ByteLength(normalized) > MAX_IMAGE_ATTACHMENT_BYTES) {
3803
+ return null;
3870
3804
  }
3871
- return { enabled: false };
3872
- }
3873
- function enabledInstalledPlugins(plugins, settings, loadedSources) {
3874
- return plugins.filter((plugin) => plugin.installedId === undefined || claudeInstalledPluginIdEnablement(plugin.installedId, settings, loadedSources).enabled);
3875
- }
3876
- function claudePluginSettingSources(settingSources) {
3877
- return CLAUDE_PLUGIN_SETTING_SOURCES.filter((source) => settingSources.includes(source));
3805
+ return { data: normalized, mediaType };
3878
3806
  }
3807
+ // ../../bridges/claude-bridge/src/services/session-manager-runtime.ts
3808
+ import { createHash as createHash6 } from "node:crypto";
3879
3809
 
3880
- // ../../bridges/claude-bridge/src/services/json-file-cache.ts
3881
- import { createHash as createHash4 } from "node:crypto";
3882
- import { readFile as readFile2, stat as stat2 } from "node:fs/promises";
3883
- var SEPARATOR = "\x00";
3884
- var slices = new Map;
3885
- var readCohorts = new Map;
3886
- var parseCounts = new Map;
3887
- var beforeStatForTesting = null;
3888
- function fingerprintOf(stats) {
3889
- return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}`;
3890
- }
3891
- function forgetFile(filePath) {
3892
- const prefix = `${filePath}${SEPARATOR}`;
3893
- for (const key of slices.keys()) {
3894
- if (key.startsWith(prefix))
3895
- slices.delete(key);
3810
+ // ../../bridges/claude-bridge/src/services/runtime-env.ts
3811
+ import { execFile as execFile2 } from "node:child_process";
3812
+ import { readFile as readFile2 } from "node:fs/promises";
3813
+ import { promisify } from "node:util";
3814
+ var GITHUB_CREDENTIAL_FILE_ENV = "ORKESTRATOR_GITHUB_CREDENTIAL_FILE";
3815
+ var GITHUB_MCP_TOKEN_ENV = "GITHUB_PERSONAL_ACCESS_TOKEN";
3816
+ var execFileAsync = promisify(execFile2);
3817
+ var GH_TOKEN_TIMEOUT_MS = 2000;
3818
+ var GH_TOKEN_CACHE_MS = 5 * 60000;
3819
+ var GH_TOKEN_FAILURE_CACHE_MS = 15000;
3820
+ var ghTokenCache = new WeakMap;
3821
+ async function readGitHubCliToken(env) {
3822
+ try {
3823
+ const { stdout } = await execFileAsync("gh", ["auth", "token", "--hostname", "github.com"], {
3824
+ env,
3825
+ timeout: GH_TOKEN_TIMEOUT_MS
3826
+ });
3827
+ return stdout.trim() || undefined;
3828
+ } catch {
3829
+ return;
3896
3830
  }
3897
3831
  }
3898
- function joinReadCohort(filePath) {
3899
- const existing = readCohorts.get(filePath);
3900
- if (existing) {
3901
- existing.readers += 1;
3902
- return existing;
3832
+ function cachedGitHubCliToken(env, reader) {
3833
+ const key = JSON.stringify([env.PATH, env.HOME, env.XDG_CONFIG_HOME, env.GH_CONFIG_DIR]);
3834
+ let entries = ghTokenCache.get(reader);
3835
+ if (!entries) {
3836
+ entries = new Map;
3837
+ ghTokenCache.set(reader, entries);
3903
3838
  }
3904
- const cohort = { readers: 1, parses: new Map };
3905
- readCohorts.set(filePath, cohort);
3906
- return cohort;
3839
+ const now = Date.now();
3840
+ const cached = entries.get(key);
3841
+ if (cached && cached.expiresAt > now)
3842
+ return cached.value;
3843
+ if (!cached && entries.size >= 8)
3844
+ entries.clear();
3845
+ const value = Promise.resolve().then(() => reader(env)).catch(() => {
3846
+ return;
3847
+ }).then((token) => {
3848
+ if (entries.get(key)?.value === value) {
3849
+ entries.set(key, {
3850
+ value: Promise.resolve(token),
3851
+ expiresAt: Date.now() + (token ? GH_TOKEN_CACHE_MS : GH_TOKEN_FAILURE_CACHE_MS)
3852
+ });
3853
+ }
3854
+ return token;
3855
+ });
3856
+ entries.set(key, { value, expiresAt: now + GH_TOKEN_TIMEOUT_MS });
3857
+ return value;
3907
3858
  }
3908
- function leaveReadCohort(filePath, cohort) {
3909
- cohort.readers -= 1;
3910
- if (cohort.readers === 0 && readCohorts.get(filePath) === cohort) {
3911
- readCohorts.delete(filePath);
3859
+ async function runtimeEnvironmentForAgentQuery(env = process.env, readTextFile = readFile2, readGhToken = readGitHubCliToken) {
3860
+ const snapshot = { ...env };
3861
+ const credentialFile = env[GITHUB_CREDENTIAL_FILE_ENV]?.trim();
3862
+ if (!credentialFile) {
3863
+ if (!snapshot[GITHUB_MCP_TOKEN_ENV]?.trim()) {
3864
+ const token = snapshot.GH_TOKEN?.trim() || snapshot.GITHUB_TOKEN?.trim() || await cachedGitHubCliToken(snapshot, readGhToken);
3865
+ if (token)
3866
+ snapshot[GITHUB_MCP_TOKEN_ENV] = token;
3867
+ }
3868
+ return snapshot;
3869
+ }
3870
+ let token = "";
3871
+ try {
3872
+ token = (await readTextFile(credentialFile, "utf8")).trim();
3873
+ } catch {}
3874
+ if (token) {
3875
+ snapshot.GITHUB_TOKEN = token;
3876
+ snapshot.GH_TOKEN = token;
3877
+ snapshot[GITHUB_MCP_TOKEN_ENV] = token;
3878
+ } else {
3879
+ delete snapshot.GITHUB_TOKEN;
3880
+ delete snapshot.GH_TOKEN;
3881
+ delete snapshot[GITHUB_MCP_TOKEN_ENV];
3912
3882
  }
3883
+ return snapshot;
3913
3884
  }
3914
- function parseOnce(filePath, fingerprint, cohort) {
3915
- const existing = cohort.parses.get(fingerprint);
3916
- if (existing)
3917
- return existing;
3918
- const parse = (async () => {
3919
- parseCounts.set(filePath, (parseCounts.get(filePath) ?? 0) + 1);
3920
- let bytes;
3921
- try {
3922
- bytes = await readFile2(filePath);
3923
- } catch {
3924
- return { parsed: null, digest: null };
3925
- }
3926
- const digest = createHash4("sha256").update(bytes).digest("base64url");
3927
- try {
3928
- return { parsed: JSON.parse(bytes.toString("utf-8")), digest };
3929
- } catch {
3930
- return { parsed: null, digest };
3931
- }
3932
- })();
3933
- cohort.parses.set(fingerprint, parse);
3934
- return parse;
3885
+
3886
+ // ../protocol/src/native-agent-transcript-patch.ts
3887
+ var FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
3888
+
3889
+ // ../protocol/src/native-agent-policy-effects.ts
3890
+ function appendNativeAgentPolicyNote(policy, note) {
3891
+ return { ...policy, note: policy.note ? `${policy.note} ${note}` : note };
3935
3892
  }
3936
- async function readJsonSliceCached(filePath, sliceKey, select) {
3937
- return (await readJsonSliceCachedWithDigest(filePath, sliceKey, select)).value;
3893
+ var READ_ONLY_TURN_POLICY_NOTE = "The current turn is read-only, so it runs under the read-only policy rather than this session's own.";
3894
+ function readOnlyTurnReportedPolicy(turnPolicy) {
3895
+ return appendNativeAgentPolicyNote(turnPolicy, READ_ONLY_TURN_POLICY_NOTE);
3938
3896
  }
3939
- async function readJsonSliceCachedWithDigest(filePath, sliceKey, select) {
3940
- const cohort = joinReadCohort(filePath);
3941
- try {
3942
- await beforeStatForTesting?.(filePath);
3943
- let fingerprint;
3944
- try {
3945
- fingerprint = fingerprintOf(await stat2(filePath));
3946
- } catch {
3947
- forgetFile(filePath);
3948
- return { value: null, digest: null };
3949
- }
3950
- const key = `${filePath}${SEPARATOR}${sliceKey}`;
3951
- const cached = slices.get(key);
3952
- if (cached && cached.fingerprint === fingerprint) {
3953
- return { value: cached.value, digest: cached.digest };
3897
+
3898
+ // ../protocol/src/native-agent-steer-rejection.ts
3899
+ var NATIVE_AGENT_STEER_REJECTION_HTTP_STATUS = 429;
3900
+ var MAX_STEER_JOURNAL_BYTES = 1024 * 1024 * 1024;
3901
+ // ../protocol/src/native-agent.ts
3902
+ var DEFAULT_OPENCODE_MODEL_PROVIDERS = Object.freeze([
3903
+ "opencode",
3904
+ "opencode-go"
3905
+ ]);
3906
+ var NATIVE_AGENT_APPROVAL_MODES = Object.freeze({
3907
+ claude: [
3908
+ {
3909
+ id: "default",
3910
+ label: "Ask",
3911
+ description: "Ask before editing files or running commands",
3912
+ kind: "ask"
3913
+ },
3914
+ {
3915
+ id: "acceptEdits",
3916
+ label: "Accept edits",
3917
+ description: "Apply file edits without asking; ask before running commands",
3918
+ kind: "auto-edits"
3919
+ },
3920
+ {
3921
+ id: "auto",
3922
+ label: "Auto",
3923
+ description: "A model reviews each action, approving safe ones and blocking risky ones",
3924
+ kind: "auto-review"
3925
+ },
3926
+ {
3927
+ id: "bypassPermissions",
3928
+ label: "Bypass",
3929
+ description: "Approve everything without asking",
3930
+ kind: "auto-approve"
3954
3931
  }
3955
- const { parsed, digest } = await parseOnce(filePath, fingerprint, cohort);
3956
- let value = null;
3957
- if (parsed !== null && parsed !== undefined) {
3958
- try {
3959
- value = select(parsed) ?? null;
3960
- } catch {
3961
- value = null;
3962
- }
3932
+ ],
3933
+ codex: [
3934
+ {
3935
+ id: "untrusted",
3936
+ label: "Untrusted",
3937
+ description: "Ask before running any command Codex does not trust",
3938
+ kind: "ask"
3939
+ },
3940
+ {
3941
+ id: "on-request",
3942
+ label: "On request",
3943
+ description: "Work in a sandbox; Codex asks before stepping outside it",
3944
+ kind: "ask"
3945
+ },
3946
+ {
3947
+ id: "auto-review",
3948
+ label: "Auto review",
3949
+ description: "Work in a sandbox; a reviewer agent decides when Codex may step outside it",
3950
+ kind: "auto-review"
3951
+ },
3952
+ {
3953
+ id: "never",
3954
+ label: "Full access",
3955
+ description: "Never ask; run everything",
3956
+ kind: "auto-approve"
3963
3957
  }
3964
- slices.set(key, { fingerprint, value, digest });
3965
- return { value, digest };
3966
- } finally {
3967
- leaveReadCohort(filePath, cohort);
3958
+ ]
3959
+ });
3960
+ var CODEX_CONTAINER_APPROVAL_DESCRIPTIONS = Object.freeze({
3961
+ ask: {
3962
+ "on-request": "Codex has no sandbox of its own in a container, so this asks before any command Codex does not trust, like Untrusted"
3963
+ },
3964
+ "auto-approve": {
3965
+ "on-request": "Codex has no sandbox of its own in a container, so this never asks; choose Untrusted to approve commands",
3966
+ "auto-review": "Codex has no sandbox of its own in a container, so the reviewer is never consulted and everything runs"
3968
3967
  }
3968
+ });
3969
+ var DISPATCH_OUTCOME_LOST_KIND = "dispatch-outcome-lost";
3970
+ var NATIVE_AGENT_RETIRED_DISPATCH_TTL_MS = 24 * 60 * 60 * 1000;
3971
+ var SUPPRESSED_COMPOSER_PARAMETERS = Object.freeze({
3972
+ claude: Object.freeze(["parameter:thinking", "parameter:context1m"]),
3973
+ codex: Object.freeze(["parameter:summary"]),
3974
+ cursor: Object.freeze(["parameter:variant"])
3975
+ });
3976
+ var NATIVE_AGENT_CAPABILITIES = [
3977
+ "file.write",
3978
+ "file.patch",
3979
+ "shell",
3980
+ "shell.mutate",
3981
+ "network",
3982
+ "web.search"
3983
+ ];
3984
+ function isNativeAgentCapability(value) {
3985
+ return typeof value === "string" && NATIVE_AGENT_CAPABILITIES.includes(value);
3969
3986
  }
3970
-
3971
- // ../../bridges/claude-bridge/src/services/plugin-config.ts
3972
- async function pathExists(path) {
3973
- try {
3974
- await access(path);
3975
- return true;
3976
- } catch {
3987
+ function isNativeAgentExecutionPolicy(value) {
3988
+ if (!value || typeof value !== "object" || Array.isArray(value))
3977
3989
  return false;
3978
- }
3979
- }
3980
- async function scanCliPluginsDirectory(pluginsDir) {
3981
- if (!await pathExists(pluginsDir)) {
3982
- return [];
3983
- }
3984
- try {
3985
- const entries = await readdir(pluginsDir, { withFileTypes: true });
3986
- const plugins = [];
3987
- for (const entry of entries) {
3988
- if (!entry.isDirectory()) {
3989
- continue;
3990
- }
3991
- const pluginPath = join3(pluginsDir, entry.name);
3992
- const manifestPath = join3(pluginPath, ".claude-plugin", "plugin.json");
3993
- if (await pathExists(manifestPath)) {
3994
- plugins.push({
3995
- type: "local",
3996
- path: pluginPath
3997
- });
3998
- }
3999
- }
4000
- return plugins;
4001
- } catch {
4002
- return [];
4003
- }
4004
- }
4005
- async function loadGlobalPlugins() {
4006
- const plugins = await readJsonSliceCached(claudeJsonPath(), "plugins", (config) => claudePluginConfigList(config?.plugins));
4007
- return plugins ?? [];
4008
- }
4009
- async function loadProjectPlugins(cwd) {
4010
- const plugins = await readJsonSliceCached(join3(cwd, ".claude", "plugins.json"), "plugins", (config) => claudePluginConfigList(config?.plugins));
4011
- return plugins ?? [];
4012
- }
4013
- async function loadCliInstalledPlugins() {
4014
- const pluginsDir = claudePluginsDir();
4015
- const installedPluginsPath = join3(pluginsDir, "installed_plugins.json");
4016
- const plugins = await readJsonSliceCached(installedPluginsPath, "plugins", (config) => config?.plugins);
4017
- if (!plugins) {
4018
- return scanCliPluginsDirectory(pluginsDir);
4019
- }
4020
- return remapInstalledPlugins2(pluginsDir, { plugins });
4021
- }
4022
- function pluginSettingsPath(source, cwd) {
4023
- switch (source) {
4024
- case "user":
4025
- return claudeUserSettingsPath();
4026
- case "project":
4027
- return join3(cwd, ".claude", "settings.json");
4028
- case "local":
4029
- return join3(cwd, ".claude", "settings.local.json");
4030
- }
4031
- }
4032
- async function loadPluginSettings(cwd, sources) {
4033
- const entries = await Promise.all(sources.map(async (source) => {
4034
- const enabledPlugins = await readJsonSliceCached(pluginSettingsPath(source, cwd), "enabledPlugins", (config) => config?.enabledPlugins ?? null);
4035
- return [source, enabledPlugins === null ? undefined : { enabledPlugins }];
4036
- }));
4037
- return Object.fromEntries(entries);
4038
- }
4039
- function remapInstalledPlugins2(pluginsDir, installedPlugins) {
4040
- return remapInstalledPlugins(pluginsDir, installedPlugins, (installPath) => console.warn(`[plugin-config] Skipping plugin with unsafe install path: "${installPath}"`));
4041
- }
4042
- async function loadProjectOverridesFromGlobal(cwd) {
4043
- const plugins = await readJsonSliceCached(claudeJsonPath(), `projects:${cwd}:plugins`, (config) => claudePluginConfigList(config?.projects?.[cwd]?.plugins));
4044
- return plugins ?? [];
4045
- }
4046
- async function readPluginManifest(pluginPath) {
4047
- const manifestPath = join3(pluginPath, ".claude-plugin", "plugin.json");
4048
- const manifest = await readJsonSliceCached(manifestPath, "manifest", (config) => config ? { name: config.name, description: config.description } : null);
4049
- if (!manifest) {
4050
- return null;
4051
- }
4052
- const name = manifest.name || pluginPath.split("/").pop() || "unknown";
4053
- return {
4054
- name,
4055
- description: manifest.description
4056
- };
3990
+ const policy = value;
3991
+ const isPlainObject = (candidate) => typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
3992
+ if (policy.toolPolicy !== undefined && !isPlainObject(policy.toolPolicy))
3993
+ return false;
3994
+ if (policy.capabilityPolicy !== undefined && !isPlainObject(policy.capabilityPolicy))
3995
+ return false;
3996
+ const toolPolicy = policy.toolPolicy;
3997
+ const capabilityPolicy = policy.capabilityPolicy;
3998
+ return (policy.id === "interactive-host" || policy.id === "interactive-container" || policy.id === "coordinator-read-only" || policy.id === "pipeline") && (policy.sandbox === "provider" || policy.sandbox === "container" || policy.sandbox === "none") && (policy.approvals === "ask" || policy.approvals === "auto-approve" || policy.approvals === "deny") && typeof policy.projectResources === "boolean" && (policy.networkAccess === "restricted" || policy.networkAccess === "full") && (policy.containerNetworkAccess === undefined || policy.containerNetworkAccess === "restricted" || policy.containerNetworkAccess === "full") && (policy.note === undefined || typeof policy.note === "string") && (toolPolicy === undefined || (toolPolicy.allow === undefined || Array.isArray(toolPolicy.allow) && toolPolicy.allow.every((item) => typeof item === "string")) && (toolPolicy.deny === undefined || Array.isArray(toolPolicy.deny) && toolPolicy.deny.every((item) => typeof item === "string"))) && (capabilityPolicy === undefined || Array.isArray(capabilityPolicy.deny) && capabilityPolicy.deny.every(isNativeAgentCapability) && (capabilityPolicy.allow === undefined || Array.isArray(capabilityPolicy.allow) && capabilityPolicy.allow.every(isNativeAgentCapability)));
4057
3999
  }
4058
- function resolvePath(pluginPath, cwd) {
4059
- return resolveClaudePluginPath(pluginPath, cwd, claudeHome());
4000
+ function nativeAgentPolicyDeniesWebSearch(policy) {
4001
+ const capabilities = policy?.capabilityPolicy;
4002
+ if (!capabilities)
4003
+ return false;
4004
+ if (capabilities.deny.includes("web.search"))
4005
+ return true;
4006
+ return capabilities.deny.includes("network") && !capabilities.allow?.includes("web.search");
4060
4007
  }
4061
- function defaultSettingSources(includeProjectSources) {
4062
- return includeProjectSources ? ["user", "project"] : ["user"];
4008
+ var MAX_NATIVE_AGENT_DRIFT_KINDS = 16;
4009
+ var MAX_NATIVE_AGENT_DRIFT_KIND_LENGTH = 128;
4010
+ var NATIVE_AGENT_PENDING_SIGN_IN_TTL_MS = 15 * 60000;
4011
+ var BACKGROUND_CAPABLE_SHELL_TOOL_NAMES = new Set([
4012
+ "bash",
4013
+ "shell",
4014
+ "terminal",
4015
+ "run_command",
4016
+ "runcommand",
4017
+ "run_terminal_cmd",
4018
+ "execute_command"
4019
+ ]);
4020
+ var NATIVE_AGENT_COMMAND_EXECUTION_KINDS = [
4021
+ "provider-prompt",
4022
+ "provider-command",
4023
+ "structured-skill",
4024
+ "bridge-template",
4025
+ "bridge-local",
4026
+ "session-action"
4027
+ ];
4028
+ var NATIVE_AGENT_COMMAND_ORIGINS = [
4029
+ "project",
4030
+ "user",
4031
+ "system",
4032
+ "admin",
4033
+ "plugin",
4034
+ "orkestrator",
4035
+ "unknown"
4036
+ ];
4037
+ var NATIVE_AGENT_COMMAND_UNAVAILABLE_REASONS = [
4038
+ "disabled",
4039
+ "shadowed",
4040
+ "reserved-name",
4041
+ "ambiguous",
4042
+ "requires-interactive-ui",
4043
+ "requires-shell-execution",
4044
+ "session-changing",
4045
+ "unqualified",
4046
+ "unsupported",
4047
+ "update-required"
4048
+ ];
4049
+ var NATIVE_AGENT_COMMAND_CATALOGUE_ERROR_CODES = [
4050
+ "timeout",
4051
+ "unreachable",
4052
+ "rejected",
4053
+ "malformed",
4054
+ "too-large",
4055
+ "provider-error",
4056
+ "outdated-bridge"
4057
+ ];
4058
+ var DEFAULT_NATIVE_AGENT_LIVE_WINDOW = {
4059
+ messages: 100,
4060
+ targetBytes: 512 * 1024
4061
+ };
4062
+ var NATIVE_AGENT_SYNC_MAX_SNAPSHOT_BYTES = 20 * 1024 * 1024;
4063
+ var NATIVE_AGENT_SYNC_MAX_PAGE_BYTES = 16 * 1024 * 1024 + 64 * 1024;
4064
+ var PROJECTION_FIELD_SET = new Set([
4065
+ "platform",
4066
+ "environmentId",
4067
+ "sessionId",
4068
+ "title",
4069
+ "shareUrl",
4070
+ "cursor",
4071
+ "connection",
4072
+ "turn",
4073
+ "messageWindow",
4074
+ "interactions",
4075
+ "composerControls",
4076
+ "composer",
4077
+ "readiness",
4078
+ "capabilities",
4079
+ "queue",
4080
+ "asyncQuestionResponses",
4081
+ "contextUsage",
4082
+ "rateLimits",
4083
+ "runtime",
4084
+ "runtimeHealthAuthoritative",
4085
+ "notices",
4086
+ "recoverableDispatch",
4087
+ "retiredDispatch",
4088
+ "backgroundTasks",
4089
+ "suggestedPrompt",
4090
+ "completionBlockedByBackgroundTasks",
4091
+ "turnBoundaries",
4092
+ "slashCommands",
4093
+ "slashCommandCatalogue"
4094
+ ]);
4095
+ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
4096
+ "sessionId",
4097
+ "title",
4098
+ "shareUrl",
4099
+ "cursor",
4100
+ "messageWindow",
4101
+ "composer",
4102
+ "readiness",
4103
+ "queue",
4104
+ "asyncQuestionResponses",
4105
+ "contextUsage",
4106
+ "rateLimits",
4107
+ "runtime",
4108
+ "runtimeHealthAuthoritative",
4109
+ "notices",
4110
+ "recoverableDispatch",
4111
+ "retiredDispatch",
4112
+ "backgroundTasks",
4113
+ "suggestedPrompt",
4114
+ "completionBlockedByBackgroundTasks",
4115
+ "turnBoundaries",
4116
+ "slashCommands",
4117
+ "slashCommandCatalogue"
4118
+ ]);
4119
+
4120
+ // ../../bridges/claude-bridge/src/services/coordinator-skills.ts
4121
+ import { createHash as createHash4, randomBytes } from "node:crypto";
4122
+ import {
4123
+ mkdir as mkdir2,
4124
+ readdir,
4125
+ readFile as readFile3,
4126
+ realpath,
4127
+ rename as rename2,
4128
+ rm as rm2,
4129
+ stat as stat2,
4130
+ symlink,
4131
+ writeFile as writeFile2
4132
+ } from "node:fs/promises";
4133
+ import { isAbsolute, join as join3 } from "node:path";
4134
+
4135
+ // ../protocol/src/coordinator.ts
4136
+ var COORDINATOR_COMMAND_SANDBOX_ENV = "ORKESTRATOR_COORDINATOR_COMMAND_SANDBOX";
4137
+ function coordinatorCommandSandboxFromEnv(env) {
4138
+ return env[COORDINATOR_COMMAND_SANDBOX_ENV] === "unavailable" ? "unavailable" : "required";
4063
4139
  }
4064
- async function getMergedPlugins(cwd, includeProjectSources = true, settingSources = defaultSettingSources(includeProjectSources)) {
4065
- const sources = claudePluginSettingSources(settingSources);
4066
- const [global2, cliInstalled, projectGlobal, projectLocal, settings] = await Promise.all([
4067
- loadGlobalPlugins(),
4068
- loadCliInstalledPlugins(),
4069
- includeProjectSources ? loadProjectOverridesFromGlobal(cwd) : [],
4070
- includeProjectSources ? loadProjectPlugins(cwd) : [],
4071
- loadPluginSettings(cwd, sources)
4072
- ]);
4073
- return mergeClaudePluginLayers({
4074
- global: global2,
4075
- cliInstalled: enabledInstalledPlugins(cliInstalled, settings, sources),
4076
- projectGlobal,
4077
- projectLocal
4078
- }, { cwd, home: claudeHome(), includeProjectSources }).map((path) => ({ type: "local", path }));
4140
+ var COORDINATOR_CLAUDE_USER_SKILLS_DIR_ENV = "ORKESTRATOR_COORDINATOR_CLAUDE_USER_SKILLS_DIR";
4141
+ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
4142
+ "ack_message",
4143
+ "add_ticket_comment",
4144
+ "address_multi_review",
4145
+ "adopt_workflow",
4146
+ "cancel_build_pipeline",
4147
+ "cancel_multi_review",
4148
+ "cancel_schedule",
4149
+ "compare_project_files",
4150
+ "create_schedule",
4151
+ "create_ticket",
4152
+ "get_build_pipeline",
4153
+ "get_environment",
4154
+ "get_launch_options",
4155
+ "get_message",
4156
+ "get_message_status",
4157
+ "get_multi_review",
4158
+ "get_repository_context",
4159
+ "get_tab_state",
4160
+ "get_tab_transcript",
4161
+ "get_ticket",
4162
+ "launch_environment",
4163
+ "launch_job",
4164
+ "launch_multi_review",
4165
+ "launch_node_environment",
4166
+ "list_agents",
4167
+ "list_environments",
4168
+ "list_mailboxes",
4169
+ "list_node_environments",
4170
+ "list_nodes",
4171
+ "list_projects",
4172
+ "list_schedules",
4173
+ "list_tabs",
4174
+ "list_tickets",
4175
+ "list_workflows",
4176
+ "open_multi_review",
4177
+ "open_multi_review_fix",
4178
+ "pause_build_pipeline",
4179
+ "read_messages",
4180
+ "read_node_environment",
4181
+ "request_env_value_reveal",
4182
+ "request_environment_deletion",
4183
+ "request_project_file_update",
4184
+ "request_project_on_node",
4185
+ "resume_build_pipeline",
4186
+ "send_message",
4187
+ "send_node_message",
4188
+ "send_prompt_to_node_tab",
4189
+ "send_prompt_to_tab",
4190
+ "start_build_pipeline",
4191
+ "start_environment",
4192
+ "start_multi_review",
4193
+ "start_node_environment",
4194
+ "stop_environment",
4195
+ "stop_node_environment",
4196
+ "transfer_files",
4197
+ "update_ticket"
4198
+ ]);
4199
+
4200
+ // ../../bridges/claude-bridge/src/services/coordinator-skills.ts
4201
+ var KEPT_FRONTMATTER_KEYS = Object.freeze([
4202
+ "name",
4203
+ "description",
4204
+ "when_to_use",
4205
+ "argument-hint",
4206
+ "arguments",
4207
+ "disable-model-invocation",
4208
+ "user-invocable",
4209
+ "version"
4210
+ ]);
4211
+ var MIRROR_FORMAT = 1;
4212
+ var MAX_SKILLS_PER_SOURCE = 256;
4213
+ var MAX_SKILL_FILE_BYTES = 1024 * 1024;
4214
+ var MAX_ENTRIES_PER_SKILL = 256;
4215
+ var KEPT_OLD_MIRRORS = 4;
4216
+ var SKILL_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
4217
+ var FRONTMATTER = /^?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/;
4218
+ function coordinatorSkillSources(cwd, env = process.env) {
4219
+ const userDir = env[COORDINATOR_CLAUDE_USER_SKILLS_DIR_ENV]?.trim();
4220
+ return [
4221
+ ...userDir && isAbsolute(userDir) ? [{ plugin: "user-skills", dir: userDir }] : [],
4222
+ { plugin: "project-skills", dir: join3(cwd, ".claude", "skills") }
4223
+ ];
4079
4224
  }
4080
- async function getPluginsForSdk(cwd, includeProjectSources = true, settingSources = defaultSettingSources(includeProjectSources)) {
4081
- const configs = await getMergedPlugins(cwd, includeProjectSources, settingSources);
4082
- const validPlugins = [];
4083
- for (const config of configs) {
4084
- if (await pathExists(config.path)) {
4085
- validPlugins.push({
4086
- type: "local",
4087
- path: config.path
4225
+ async function coordinatorSkillPlugins(sources, root = claudeCoordinatorSkillPluginsDir()) {
4226
+ const plugins = [];
4227
+ const skills = [];
4228
+ for (const source of sources) {
4229
+ try {
4230
+ const mirror = await mirrorSkillSource(source, root);
4231
+ if (!mirror)
4232
+ continue;
4233
+ plugins.push({ type: "local", path: mirror.path });
4234
+ skills.push(...mirror.names.map((name) => `${source.plugin}:${name}`));
4235
+ } catch (error) {
4236
+ debugLog("[coordinator-skills] Skipping skills source", {
4237
+ plugin: source.plugin,
4238
+ error: error instanceof Error ? error.message : String(error)
4088
4239
  });
4089
- } else {
4090
- const pluginName = config.path.split("/").pop() || "unknown";
4091
- console.warn(`[plugin-config] Skipping plugin "${pluginName}" - path does not exist: "${config.path}"`);
4092
4240
  }
4093
4241
  }
4094
- return validPlugins;
4242
+ return { plugins, skills };
4095
4243
  }
4096
- async function getPluginInfo(cwd) {
4097
- const [global2, cliInstalled, projectGlobal, projectLocal] = await Promise.all([
4098
- loadGlobalPlugins(),
4099
- loadCliInstalledPlugins(),
4100
- loadProjectOverridesFromGlobal(cwd),
4101
- loadProjectPlugins(cwd)
4102
- ]);
4103
- const globalPaths = new Set(global2.map((p) => resolvePath(p.path, cwd)));
4104
- const cliPaths = new Set(cliInstalled.map((p) => resolvePath(p.path, cwd)));
4105
- const projectGlobalPaths = new Set(projectGlobal.map((p) => resolvePath(p.path, cwd)));
4106
- const projectLocalPaths = new Set(projectLocal.map((p) => resolvePath(p.path, cwd)));
4107
- const merged = await getMergedPlugins(cwd);
4108
- const result = [];
4109
- for (const config of merged) {
4110
- const manifest = await readPluginManifest(config.path);
4111
- const exists = await pathExists(config.path);
4112
- let source = "global";
4113
- if (projectLocalPaths.has(config.path) || projectGlobalPaths.has(config.path)) {
4114
- source = "project";
4115
- } else if (cliPaths.has(config.path)) {
4116
- source = "cli";
4117
- } else if (globalPaths.has(config.path)) {
4118
- source = "global";
4119
- }
4120
- result.push({
4121
- name: manifest?.name || config.path.split("/").pop() || "unknown",
4122
- path: config.path,
4123
- description: manifest?.description,
4124
- source,
4125
- enabled: exists
4244
+ async function mirrorSkillSource(source, root) {
4245
+ const dir = await realpath(source.dir).catch(() => {
4246
+ return;
4247
+ });
4248
+ if (!dir)
4249
+ return;
4250
+ const skills = await readSkills(dir);
4251
+ if (skills.length === 0)
4252
+ return;
4253
+ const digest = createHash4("sha256").update(JSON.stringify([MIRROR_FORMAT, source.plugin, dir, skills])).digest("hex").slice(0, 24);
4254
+ const path = join3(root, `${source.plugin}-${digest}`);
4255
+ if (!await isMirror(path))
4256
+ await writeMirror(root, path, source.plugin, skills);
4257
+ await pruneOldMirrors(root, source.plugin, path);
4258
+ return { path, names: skills.map((skill) => skill.name) };
4259
+ }
4260
+ async function readSkills(dir) {
4261
+ const entries = (await readdir(dir)).filter((name) => !name.startsWith(".")).sort();
4262
+ const skills = [];
4263
+ const names = new Set;
4264
+ for (const entry of entries) {
4265
+ if (skills.length >= MAX_SKILLS_PER_SOURCE)
4266
+ break;
4267
+ const skillDir = await realpath(join3(dir, entry)).catch(() => {
4268
+ return;
4126
4269
  });
4270
+ if (!skillDir || !(await stat2(skillDir).catch(() => {
4271
+ return;
4272
+ }))?.isDirectory())
4273
+ continue;
4274
+ const skill = await readSkill(skillDir, entry);
4275
+ if (!skill || names.has(skill.name))
4276
+ continue;
4277
+ names.add(skill.name);
4278
+ skills.push(skill);
4127
4279
  }
4128
- return result;
4280
+ return skills;
4129
4281
  }
4130
-
4131
- // ../../bridges/claude-bridge/src/services/runtime-env.ts
4132
- import { execFile as execFile2 } from "node:child_process";
4133
- import { readFile as readFile3 } from "node:fs/promises";
4134
- import { promisify } from "node:util";
4135
- var GITHUB_CREDENTIAL_FILE_ENV = "ORKESTRATOR_GITHUB_CREDENTIAL_FILE";
4136
- var GITHUB_MCP_TOKEN_ENV = "GITHUB_PERSONAL_ACCESS_TOKEN";
4137
- var execFileAsync = promisify(execFile2);
4138
- var GH_TOKEN_TIMEOUT_MS = 2000;
4139
- var GH_TOKEN_CACHE_MS = 5 * 60000;
4140
- var GH_TOKEN_FAILURE_CACHE_MS = 15000;
4141
- var ghTokenCache = new WeakMap;
4142
- async function readGitHubCliToken(env) {
4143
- try {
4144
- const { stdout } = await execFileAsync("gh", ["auth", "token", "--hostname", "github.com"], {
4145
- env,
4146
- timeout: GH_TOKEN_TIMEOUT_MS
4147
- });
4148
- return stdout.trim() || undefined;
4149
- } catch {
4282
+ async function readSkill(skillDir, dirName) {
4283
+ const entries = await readdir(skillDir);
4284
+ const skillFile = entries.find((entry) => entry === "SKILL.md");
4285
+ if (!skillFile)
4286
+ return;
4287
+ const file = join3(skillDir, skillFile);
4288
+ const info = await stat2(file).catch(() => {
4289
+ return;
4290
+ });
4291
+ if (!info?.isFile() || info.size > MAX_SKILL_FILE_BYTES)
4292
+ return;
4293
+ const sanitized = sanitizeSkillFile(await readFile3(file, "utf8"), dirName);
4294
+ if (!sanitized) {
4295
+ debugLog("[coordinator-skills] Skipping skill with unreadable frontmatter", { dirName });
4296
+ return;
4297
+ }
4298
+ const links = entries.filter((entry) => entry.toLowerCase() !== "skill.md").sort().slice(0, MAX_ENTRIES_PER_SKILL).map((entry) => [entry, join3(skillDir, entry)]);
4299
+ return { name: sanitized.name, text: sanitized.text, links };
4300
+ }
4301
+ function yamlParser() {
4302
+ const parse = globalThis.Bun?.YAML?.parse;
4303
+ return typeof parse === "function" ? parse : null;
4304
+ }
4305
+ function sanitizeSkillFile(text, dirName, parse = yamlParser()) {
4306
+ const match = FRONTMATTER.exec(text);
4307
+ let fields = {};
4308
+ if (match) {
4309
+ if (!parse)
4310
+ return;
4311
+ let parsed;
4312
+ try {
4313
+ parsed = parse(match[1]);
4314
+ } catch {
4315
+ return;
4316
+ }
4317
+ if (parsed !== null && parsed !== undefined) {
4318
+ if (typeof parsed !== "object" || Array.isArray(parsed))
4319
+ return;
4320
+ fields = parsed;
4321
+ }
4322
+ }
4323
+ const declared = typeof fields.name === "string" ? fields.name.trim() : "";
4324
+ const name = SKILL_NAME.test(declared) ? declared : dirName;
4325
+ if (!SKILL_NAME.test(name))
4150
4326
  return;
4327
+ const lines = [`name: ${JSON.stringify(name)}`];
4328
+ for (const key of KEPT_FRONTMATTER_KEYS) {
4329
+ if (key === "name" || !Object.hasOwn(fields, key))
4330
+ continue;
4331
+ const value = frontmatterValue(fields[key]);
4332
+ if (value !== undefined)
4333
+ lines.push(`${key}: ${value}`);
4151
4334
  }
4335
+ const body = match ? text.slice(match[0].length) : text.replace(/^/, "");
4336
+ return { name, text: `---
4337
+ ${lines.join(`
4338
+ `)}
4339
+ ---
4340
+ ${body}` };
4152
4341
  }
4153
- function cachedGitHubCliToken(env, reader) {
4154
- const key = JSON.stringify([env.PATH, env.HOME, env.XDG_CONFIG_HOME, env.GH_CONFIG_DIR]);
4155
- let entries = ghTokenCache.get(reader);
4156
- if (!entries) {
4157
- entries = new Map;
4158
- ghTokenCache.set(reader, entries);
4159
- }
4160
- const now = Date.now();
4161
- const cached = entries.get(key);
4162
- if (cached && cached.expiresAt > now)
4163
- return cached.value;
4164
- if (!cached && entries.size >= 8)
4165
- entries.clear();
4166
- const value = Promise.resolve().then(() => reader(env)).catch(() => {
4342
+ function frontmatterValue(value) {
4343
+ if (typeof value === "string" || typeof value === "boolean")
4344
+ return JSON.stringify(value);
4345
+ if (typeof value === "number" && Number.isFinite(value))
4346
+ return JSON.stringify(String(value));
4347
+ if (Array.isArray(value) && value.every((item) => typeof item === "string"))
4348
+ return JSON.stringify(value);
4349
+ return;
4350
+ }
4351
+ async function isMirror(path) {
4352
+ return (await stat2(join3(path, ".claude-plugin", "plugin.json")).catch(() => {
4167
4353
  return;
4168
- }).then((token) => {
4169
- if (entries.get(key)?.value === value) {
4170
- entries.set(key, {
4171
- value: Promise.resolve(token),
4172
- expiresAt: Date.now() + (token ? GH_TOKEN_CACHE_MS : GH_TOKEN_FAILURE_CACHE_MS)
4173
- });
4174
- }
4175
- return token;
4176
- });
4177
- entries.set(key, { value, expiresAt: now + GH_TOKEN_TIMEOUT_MS });
4178
- return value;
4354
+ }))?.isFile() ? true : false;
4179
4355
  }
4180
- async function runtimeEnvironmentForAgentQuery(env = process.env, readTextFile = readFile3, readGhToken = readGitHubCliToken) {
4181
- const snapshot = { ...env };
4182
- const credentialFile = env[GITHUB_CREDENTIAL_FILE_ENV]?.trim();
4183
- if (!credentialFile) {
4184
- if (!snapshot[GITHUB_MCP_TOKEN_ENV]?.trim()) {
4185
- const token = snapshot.GH_TOKEN?.trim() || snapshot.GITHUB_TOKEN?.trim() || await cachedGitHubCliToken(snapshot, readGhToken);
4186
- if (token)
4187
- snapshot[GITHUB_MCP_TOKEN_ENV] = token;
4356
+ async function writeMirror(root, path, plugin, skills) {
4357
+ await mkdir2(root, { recursive: true, mode: 448 });
4358
+ const staging = join3(root, `.staging-${randomBytes(8).toString("hex")}`);
4359
+ try {
4360
+ await mkdir2(join3(staging, ".claude-plugin"), { recursive: true });
4361
+ await writeFile2(join3(staging, ".claude-plugin", "plugin.json"), JSON.stringify({
4362
+ name: plugin,
4363
+ description: plugin === "user-skills" ? "The user's own Claude skills, mirrored by Orkestrator without hooks or tool grants." : "This project's .claude/skills, mirrored by Orkestrator without hooks or tool grants."
4364
+ }));
4365
+ for (const skill of skills) {
4366
+ const skillDir = join3(staging, "skills", skill.name);
4367
+ await mkdir2(skillDir, { recursive: true });
4368
+ await writeFile2(join3(skillDir, "SKILL.md"), skill.text);
4369
+ for (const [name, target] of skill.links)
4370
+ await symlink(target, join3(skillDir, name));
4188
4371
  }
4189
- return snapshot;
4372
+ try {
4373
+ await rename2(staging, path);
4374
+ } catch (error) {
4375
+ if (!await isMirror(path))
4376
+ throw error;
4377
+ }
4378
+ } finally {
4379
+ await rm2(staging, { recursive: true, force: true });
4190
4380
  }
4191
- let token = "";
4381
+ }
4382
+ async function pruneOldMirrors(root, plugin, current) {
4192
4383
  try {
4193
- token = (await readTextFile(credentialFile, "utf8")).trim();
4384
+ const old = await Promise.all((await readdir(root)).filter((name) => name.startsWith(`${plugin}-`) && join3(root, name) !== current).map(async (name) => ({
4385
+ path: join3(root, name),
4386
+ mtimeMs: (await stat2(join3(root, name)).catch(() => {
4387
+ return;
4388
+ }))?.mtimeMs ?? 0
4389
+ })));
4390
+ old.sort((a, b) => b.mtimeMs - a.mtimeMs);
4391
+ for (const { path } of old.slice(KEPT_OLD_MIRRORS)) {
4392
+ await rm2(path, { recursive: true, force: true });
4393
+ }
4194
4394
  } catch {}
4195
- if (token) {
4196
- snapshot.GITHUB_TOKEN = token;
4197
- snapshot.GH_TOKEN = token;
4198
- snapshot[GITHUB_MCP_TOKEN_ENV] = token;
4199
- } else {
4200
- delete snapshot.GITHUB_TOKEN;
4201
- delete snapshot.GH_TOKEN;
4202
- delete snapshot[GITHUB_MCP_TOKEN_ENV];
4203
- }
4204
- return snapshot;
4205
4395
  }
4206
4396
 
4207
- // ../protocol/src/native-agent-transcript-patch.ts
4208
- var FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
4397
+ // ../../bridges/claude-bridge/src/services/plugin-config.ts
4398
+ import { readdir as readdir2, access } from "node:fs/promises";
4399
+ import { join as join4 } from "node:path";
4209
4400
 
4210
- // ../protocol/src/native-agent-policy-effects.ts
4211
- function appendNativeAgentPolicyNote(policy, note) {
4212
- return { ...policy, note: policy.note ? `${policy.note} ${note}` : note };
4401
+ // ../protocol/src/node/claude-plugin-set.ts
4402
+ import path from "node:path";
4403
+ var CLAUDE_PLUGINS_MARKER = "/.claude/plugins/";
4404
+ function isRecord(value) {
4405
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4213
4406
  }
4214
- var READ_ONLY_TURN_POLICY_NOTE = "The current turn is read-only, so it runs under the read-only policy rather than this session's own.";
4215
- function readOnlyTurnReportedPolicy(turnPolicy) {
4216
- return appendNativeAgentPolicyNote(turnPolicy, READ_ONLY_TURN_POLICY_NOTE);
4407
+ function claudePluginConfigList(value) {
4408
+ if (!Array.isArray(value))
4409
+ return [];
4410
+ const plugins = [];
4411
+ for (const item of value) {
4412
+ if (isRecord(item) && typeof item.path === "string" && item.path) {
4413
+ plugins.push({ type: "local", path: item.path });
4414
+ }
4415
+ }
4416
+ return plugins;
4417
+ }
4418
+ function isPathWithin(parentDir, candidatePath) {
4419
+ const relativePath = path.relative(parentDir, candidatePath);
4420
+ return relativePath === "" || !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
4421
+ }
4422
+ function remapInstalledPlugins(pluginsDir, installedPlugins, onUnsafe) {
4423
+ const plugins = [];
4424
+ const pluginsRoot = path.resolve(pluginsDir);
4425
+ if (!isRecord(installedPlugins.plugins))
4426
+ return plugins;
4427
+ for (const [installedId, entries] of Object.entries(installedPlugins.plugins)) {
4428
+ if (!Array.isArray(entries))
4429
+ continue;
4430
+ for (const entry of entries) {
4431
+ if (!isRecord(entry) || typeof entry.installPath !== "string")
4432
+ continue;
4433
+ const installPath = entry.installPath;
4434
+ const markerIdx = installPath.indexOf(CLAUDE_PLUGINS_MARKER);
4435
+ let resolvedPath;
4436
+ if (markerIdx !== -1) {
4437
+ const relativePath = installPath.substring(markerIdx + CLAUDE_PLUGINS_MARKER.length);
4438
+ const remappedPath = path.resolve(pluginsDir, relativePath);
4439
+ if (!isPathWithin(pluginsRoot, remappedPath)) {
4440
+ onUnsafe?.(installPath);
4441
+ continue;
4442
+ }
4443
+ resolvedPath = remappedPath;
4444
+ } else {
4445
+ resolvedPath = installPath;
4446
+ }
4447
+ plugins.push({ type: "local", path: resolvedPath, installedId });
4448
+ }
4449
+ }
4450
+ return plugins;
4451
+ }
4452
+ function resolveClaudePluginPath(pluginPath, cwd, home) {
4453
+ if (pluginPath.startsWith("~"))
4454
+ return path.join(home, pluginPath.slice(1));
4455
+ if (pluginPath.startsWith("/"))
4456
+ return pluginPath;
4457
+ return path.join(cwd, pluginPath);
4458
+ }
4459
+ function mergeClaudePluginLayers(layers, options) {
4460
+ const ordered = [
4461
+ claudePluginConfigList(layers.global),
4462
+ layers.cliInstalled,
4463
+ ...options.includeProjectSources ? [claudePluginConfigList(layers.projectGlobal), claudePluginConfigList(layers.projectLocal)] : []
4464
+ ];
4465
+ const seen = new Set;
4466
+ const paths = [];
4467
+ for (const layer of ordered) {
4468
+ for (const plugin of layer) {
4469
+ const resolved = resolveClaudePluginPath(plugin.path, options.cwd, options.home);
4470
+ if (seen.has(resolved))
4471
+ continue;
4472
+ seen.add(resolved);
4473
+ paths.push(resolved);
4474
+ }
4475
+ }
4476
+ return paths;
4477
+ }
4478
+ var INVALID_PLUGIN_NAME = new RegExp("[@:\\s/\\\\\\u0000-\\u001f\\u007f\\u200b-\\u200f\\u2028-\\u202e\\u2060-\\u2064\\ufeff]");
4479
+ var CLAUDE_PLUGIN_SETTING_SOURCES = ["local", "project", "user"];
4480
+ function claudeInstalledPluginIdEnablement(id, settings, loadedSources) {
4481
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4482
+ for (const source of ["local", "project", "user"]) {
4483
+ if (!loadedSources.includes(source))
4484
+ continue;
4485
+ const document = settings[source];
4486
+ const enabledPlugins = isRecord(document) ? document.enabledPlugins : undefined;
4487
+ if (!isRecord(enabledPlugins) || !Object.hasOwn(enabledPlugins, id))
4488
+ continue;
4489
+ return { enabled: enabledPlugins[id] === true, decidedBy: { source, key: id } };
4490
+ }
4491
+ return { enabled: false };
4492
+ }
4493
+ function enabledInstalledPlugins(plugins, settings, loadedSources) {
4494
+ return plugins.filter((plugin) => plugin.installedId === undefined || claudeInstalledPluginIdEnablement(plugin.installedId, settings, loadedSources).enabled);
4495
+ }
4496
+ function claudePluginSettingSources(settingSources) {
4497
+ return CLAUDE_PLUGIN_SETTING_SOURCES.filter((source) => settingSources.includes(source));
4217
4498
  }
4218
4499
 
4219
- // ../protocol/src/native-agent-steer-rejection.ts
4220
- var NATIVE_AGENT_STEER_REJECTION_HTTP_STATUS = 429;
4221
- var MAX_STEER_JOURNAL_BYTES = 1024 * 1024 * 1024;
4222
- // ../protocol/src/native-agent.ts
4223
- var DEFAULT_OPENCODE_MODEL_PROVIDERS = Object.freeze([
4224
- "opencode",
4225
- "opencode-go"
4226
- ]);
4227
- var NATIVE_AGENT_APPROVAL_MODES = Object.freeze({
4228
- claude: [
4229
- {
4230
- id: "default",
4231
- label: "Ask",
4232
- description: "Ask before editing files or running commands",
4233
- kind: "ask"
4234
- },
4235
- {
4236
- id: "acceptEdits",
4237
- label: "Accept edits",
4238
- description: "Apply file edits without asking; ask before running commands",
4239
- kind: "auto-edits"
4240
- },
4241
- {
4242
- id: "auto",
4243
- label: "Auto",
4244
- description: "A model reviews each action, approving safe ones and blocking risky ones",
4245
- kind: "auto-review"
4246
- },
4247
- {
4248
- id: "bypassPermissions",
4249
- label: "Bypass",
4250
- description: "Approve everything without asking",
4251
- kind: "auto-approve"
4500
+ // ../../bridges/claude-bridge/src/services/json-file-cache.ts
4501
+ import { createHash as createHash5 } from "node:crypto";
4502
+ import { readFile as readFile4, stat as stat3 } from "node:fs/promises";
4503
+ var SEPARATOR = "\x00";
4504
+ var slices = new Map;
4505
+ var readCohorts = new Map;
4506
+ var parseCounts = new Map;
4507
+ var beforeStatForTesting = null;
4508
+ function fingerprintOf(stats) {
4509
+ return `${stats.dev}:${stats.ino}:${stats.size}:${stats.mtimeMs}`;
4510
+ }
4511
+ function forgetFile(filePath) {
4512
+ const prefix = `${filePath}${SEPARATOR}`;
4513
+ for (const key of slices.keys()) {
4514
+ if (key.startsWith(prefix))
4515
+ slices.delete(key);
4516
+ }
4517
+ }
4518
+ function joinReadCohort(filePath) {
4519
+ const existing = readCohorts.get(filePath);
4520
+ if (existing) {
4521
+ existing.readers += 1;
4522
+ return existing;
4523
+ }
4524
+ const cohort = { readers: 1, parses: new Map };
4525
+ readCohorts.set(filePath, cohort);
4526
+ return cohort;
4527
+ }
4528
+ function leaveReadCohort(filePath, cohort) {
4529
+ cohort.readers -= 1;
4530
+ if (cohort.readers === 0 && readCohorts.get(filePath) === cohort) {
4531
+ readCohorts.delete(filePath);
4532
+ }
4533
+ }
4534
+ function parseOnce(filePath, fingerprint, cohort) {
4535
+ const existing = cohort.parses.get(fingerprint);
4536
+ if (existing)
4537
+ return existing;
4538
+ const parse = (async () => {
4539
+ parseCounts.set(filePath, (parseCounts.get(filePath) ?? 0) + 1);
4540
+ let bytes;
4541
+ try {
4542
+ bytes = await readFile4(filePath);
4543
+ } catch {
4544
+ return { parsed: null, digest: null };
4545
+ }
4546
+ const digest = createHash5("sha256").update(bytes).digest("base64url");
4547
+ try {
4548
+ return { parsed: JSON.parse(bytes.toString("utf-8")), digest };
4549
+ } catch {
4550
+ return { parsed: null, digest };
4252
4551
  }
4253
- ],
4254
- codex: [
4255
- {
4256
- id: "untrusted",
4257
- label: "Untrusted",
4258
- description: "Ask before running any command Codex does not trust",
4259
- kind: "ask"
4260
- },
4261
- {
4262
- id: "on-request",
4263
- label: "On request",
4264
- description: "Work in a sandbox; Codex asks before stepping outside it",
4265
- kind: "ask"
4266
- },
4267
- {
4268
- id: "auto-review",
4269
- label: "Auto review",
4270
- description: "Work in a sandbox; a reviewer agent decides when Codex may step outside it",
4271
- kind: "auto-review"
4272
- },
4273
- {
4274
- id: "never",
4275
- label: "Full access",
4276
- description: "Never ask; run everything",
4277
- kind: "auto-approve"
4552
+ })();
4553
+ cohort.parses.set(fingerprint, parse);
4554
+ return parse;
4555
+ }
4556
+ async function readJsonSliceCached(filePath, sliceKey, select) {
4557
+ return (await readJsonSliceCachedWithDigest(filePath, sliceKey, select)).value;
4558
+ }
4559
+ async function readJsonSliceCachedWithDigest(filePath, sliceKey, select) {
4560
+ const cohort = joinReadCohort(filePath);
4561
+ try {
4562
+ await beforeStatForTesting?.(filePath);
4563
+ let fingerprint;
4564
+ try {
4565
+ fingerprint = fingerprintOf(await stat3(filePath));
4566
+ } catch {
4567
+ forgetFile(filePath);
4568
+ return { value: null, digest: null };
4278
4569
  }
4279
- ]
4280
- });
4281
- var CODEX_CONTAINER_APPROVAL_DESCRIPTIONS = Object.freeze({
4282
- ask: {
4283
- "on-request": "Codex has no sandbox of its own in a container, so this asks before any command Codex does not trust, like Untrusted"
4284
- },
4285
- "auto-approve": {
4286
- "on-request": "Codex has no sandbox of its own in a container, so this never asks; choose Untrusted to approve commands",
4287
- "auto-review": "Codex has no sandbox of its own in a container, so the reviewer is never consulted and everything runs"
4570
+ const key = `${filePath}${SEPARATOR}${sliceKey}`;
4571
+ const cached = slices.get(key);
4572
+ if (cached && cached.fingerprint === fingerprint) {
4573
+ return { value: cached.value, digest: cached.digest };
4574
+ }
4575
+ const { parsed, digest } = await parseOnce(filePath, fingerprint, cohort);
4576
+ let value = null;
4577
+ if (parsed !== null && parsed !== undefined) {
4578
+ try {
4579
+ value = select(parsed) ?? null;
4580
+ } catch {
4581
+ value = null;
4582
+ }
4583
+ }
4584
+ slices.set(key, { fingerprint, value, digest });
4585
+ return { value, digest };
4586
+ } finally {
4587
+ leaveReadCohort(filePath, cohort);
4288
4588
  }
4289
- });
4290
- var DISPATCH_OUTCOME_LOST_KIND = "dispatch-outcome-lost";
4291
- var NATIVE_AGENT_RETIRED_DISPATCH_TTL_MS = 24 * 60 * 60 * 1000;
4292
- var SUPPRESSED_COMPOSER_PARAMETERS = Object.freeze({
4293
- claude: Object.freeze(["parameter:thinking", "parameter:context1m"]),
4294
- codex: Object.freeze(["parameter:summary"]),
4295
- cursor: Object.freeze(["parameter:variant"])
4296
- });
4297
- var NATIVE_AGENT_CAPABILITIES = [
4298
- "file.write",
4299
- "file.patch",
4300
- "shell",
4301
- "shell.mutate",
4302
- "network",
4303
- "web.search"
4304
- ];
4305
- function isNativeAgentCapability(value) {
4306
- return typeof value === "string" && NATIVE_AGENT_CAPABILITIES.includes(value);
4307
4589
  }
4308
- function isNativeAgentExecutionPolicy(value) {
4309
- if (!value || typeof value !== "object" || Array.isArray(value))
4310
- return false;
4311
- const policy = value;
4312
- const isPlainObject = (candidate) => typeof candidate === "object" && candidate !== null && !Array.isArray(candidate);
4313
- if (policy.toolPolicy !== undefined && !isPlainObject(policy.toolPolicy))
4314
- return false;
4315
- if (policy.capabilityPolicy !== undefined && !isPlainObject(policy.capabilityPolicy))
4590
+
4591
+ // ../../bridges/claude-bridge/src/services/plugin-config.ts
4592
+ async function pathExists(path) {
4593
+ try {
4594
+ await access(path);
4595
+ return true;
4596
+ } catch {
4316
4597
  return false;
4317
- const toolPolicy = policy.toolPolicy;
4318
- const capabilityPolicy = policy.capabilityPolicy;
4319
- return (policy.id === "interactive-host" || policy.id === "interactive-container" || policy.id === "coordinator-read-only" || policy.id === "pipeline") && (policy.sandbox === "provider" || policy.sandbox === "container" || policy.sandbox === "none") && (policy.approvals === "ask" || policy.approvals === "auto-approve" || policy.approvals === "deny") && typeof policy.projectResources === "boolean" && (policy.networkAccess === "restricted" || policy.networkAccess === "full") && (policy.containerNetworkAccess === undefined || policy.containerNetworkAccess === "restricted" || policy.containerNetworkAccess === "full") && (policy.note === undefined || typeof policy.note === "string") && (toolPolicy === undefined || (toolPolicy.allow === undefined || Array.isArray(toolPolicy.allow) && toolPolicy.allow.every((item) => typeof item === "string")) && (toolPolicy.deny === undefined || Array.isArray(toolPolicy.deny) && toolPolicy.deny.every((item) => typeof item === "string"))) && (capabilityPolicy === undefined || Array.isArray(capabilityPolicy.deny) && capabilityPolicy.deny.every(isNativeAgentCapability) && (capabilityPolicy.allow === undefined || Array.isArray(capabilityPolicy.allow) && capabilityPolicy.allow.every(isNativeAgentCapability)));
4598
+ }
4320
4599
  }
4321
- function nativeAgentPolicyDeniesWebSearch(policy) {
4322
- const capabilities = policy?.capabilityPolicy;
4323
- if (!capabilities)
4324
- return false;
4325
- if (capabilities.deny.includes("web.search"))
4326
- return true;
4327
- return capabilities.deny.includes("network") && !capabilities.allow?.includes("web.search");
4600
+ async function scanCliPluginsDirectory(pluginsDir) {
4601
+ if (!await pathExists(pluginsDir)) {
4602
+ return [];
4603
+ }
4604
+ try {
4605
+ const entries = await readdir2(pluginsDir, { withFileTypes: true });
4606
+ const plugins = [];
4607
+ for (const entry of entries) {
4608
+ if (!entry.isDirectory()) {
4609
+ continue;
4610
+ }
4611
+ const pluginPath = join4(pluginsDir, entry.name);
4612
+ const manifestPath = join4(pluginPath, ".claude-plugin", "plugin.json");
4613
+ if (await pathExists(manifestPath)) {
4614
+ plugins.push({
4615
+ type: "local",
4616
+ path: pluginPath
4617
+ });
4618
+ }
4619
+ }
4620
+ return plugins;
4621
+ } catch {
4622
+ return [];
4623
+ }
4624
+ }
4625
+ async function loadGlobalPlugins() {
4626
+ const plugins = await readJsonSliceCached(claudeJsonPath(), "plugins", (config) => claudePluginConfigList(config?.plugins));
4627
+ return plugins ?? [];
4628
+ }
4629
+ async function loadProjectPlugins(cwd) {
4630
+ const plugins = await readJsonSliceCached(join4(cwd, ".claude", "plugins.json"), "plugins", (config) => claudePluginConfigList(config?.plugins));
4631
+ return plugins ?? [];
4632
+ }
4633
+ async function loadCliInstalledPlugins() {
4634
+ const pluginsDir = claudePluginsDir();
4635
+ const installedPluginsPath = join4(pluginsDir, "installed_plugins.json");
4636
+ const plugins = await readJsonSliceCached(installedPluginsPath, "plugins", (config) => config?.plugins);
4637
+ if (!plugins) {
4638
+ return scanCliPluginsDirectory(pluginsDir);
4639
+ }
4640
+ return remapInstalledPlugins2(pluginsDir, { plugins });
4641
+ }
4642
+ function pluginSettingsPath(source, cwd) {
4643
+ switch (source) {
4644
+ case "user":
4645
+ return claudeUserSettingsPath();
4646
+ case "project":
4647
+ return join4(cwd, ".claude", "settings.json");
4648
+ case "local":
4649
+ return join4(cwd, ".claude", "settings.local.json");
4650
+ }
4651
+ }
4652
+ async function loadPluginSettings(cwd, sources) {
4653
+ const entries = await Promise.all(sources.map(async (source) => {
4654
+ const enabledPlugins = await readJsonSliceCached(pluginSettingsPath(source, cwd), "enabledPlugins", (config) => config?.enabledPlugins ?? null);
4655
+ return [source, enabledPlugins === null ? undefined : { enabledPlugins }];
4656
+ }));
4657
+ return Object.fromEntries(entries);
4658
+ }
4659
+ function remapInstalledPlugins2(pluginsDir, installedPlugins) {
4660
+ return remapInstalledPlugins(pluginsDir, installedPlugins, (installPath) => console.warn(`[plugin-config] Skipping plugin with unsafe install path: "${installPath}"`));
4661
+ }
4662
+ async function loadProjectOverridesFromGlobal(cwd) {
4663
+ const plugins = await readJsonSliceCached(claudeJsonPath(), `projects:${cwd}:plugins`, (config) => claudePluginConfigList(config?.projects?.[cwd]?.plugins));
4664
+ return plugins ?? [];
4665
+ }
4666
+ async function readPluginManifest(pluginPath) {
4667
+ const manifestPath = join4(pluginPath, ".claude-plugin", "plugin.json");
4668
+ const manifest = await readJsonSliceCached(manifestPath, "manifest", (config) => config ? { name: config.name, description: config.description } : null);
4669
+ if (!manifest) {
4670
+ return null;
4671
+ }
4672
+ const name = manifest.name || pluginPath.split("/").pop() || "unknown";
4673
+ return {
4674
+ name,
4675
+ description: manifest.description
4676
+ };
4677
+ }
4678
+ function resolvePath(pluginPath, cwd) {
4679
+ return resolveClaudePluginPath(pluginPath, cwd, claudeHome());
4680
+ }
4681
+ function defaultSettingSources(includeProjectSources) {
4682
+ return includeProjectSources ? ["user", "project"] : ["user"];
4683
+ }
4684
+ async function getMergedPlugins(cwd, includeProjectSources = true, settingSources = defaultSettingSources(includeProjectSources)) {
4685
+ const sources = claudePluginSettingSources(settingSources);
4686
+ const [global2, cliInstalled, projectGlobal, projectLocal, settings] = await Promise.all([
4687
+ loadGlobalPlugins(),
4688
+ loadCliInstalledPlugins(),
4689
+ includeProjectSources ? loadProjectOverridesFromGlobal(cwd) : [],
4690
+ includeProjectSources ? loadProjectPlugins(cwd) : [],
4691
+ loadPluginSettings(cwd, sources)
4692
+ ]);
4693
+ return mergeClaudePluginLayers({
4694
+ global: global2,
4695
+ cliInstalled: enabledInstalledPlugins(cliInstalled, settings, sources),
4696
+ projectGlobal,
4697
+ projectLocal
4698
+ }, { cwd, home: claudeHome(), includeProjectSources }).map((path) => ({ type: "local", path }));
4699
+ }
4700
+ async function getPluginsForSdk(cwd, includeProjectSources = true, settingSources = defaultSettingSources(includeProjectSources)) {
4701
+ const configs = await getMergedPlugins(cwd, includeProjectSources, settingSources);
4702
+ const validPlugins = [];
4703
+ for (const config of configs) {
4704
+ if (await pathExists(config.path)) {
4705
+ validPlugins.push({
4706
+ type: "local",
4707
+ path: config.path
4708
+ });
4709
+ } else {
4710
+ const pluginName = config.path.split("/").pop() || "unknown";
4711
+ console.warn(`[plugin-config] Skipping plugin "${pluginName}" - path does not exist: "${config.path}"`);
4712
+ }
4713
+ }
4714
+ return validPlugins;
4715
+ }
4716
+ async function getPluginInfo(cwd) {
4717
+ const [global2, cliInstalled, projectGlobal, projectLocal] = await Promise.all([
4718
+ loadGlobalPlugins(),
4719
+ loadCliInstalledPlugins(),
4720
+ loadProjectOverridesFromGlobal(cwd),
4721
+ loadProjectPlugins(cwd)
4722
+ ]);
4723
+ const globalPaths = new Set(global2.map((p) => resolvePath(p.path, cwd)));
4724
+ const cliPaths = new Set(cliInstalled.map((p) => resolvePath(p.path, cwd)));
4725
+ const projectGlobalPaths = new Set(projectGlobal.map((p) => resolvePath(p.path, cwd)));
4726
+ const projectLocalPaths = new Set(projectLocal.map((p) => resolvePath(p.path, cwd)));
4727
+ const merged = await getMergedPlugins(cwd);
4728
+ const result = [];
4729
+ for (const config of merged) {
4730
+ const manifest = await readPluginManifest(config.path);
4731
+ const exists = await pathExists(config.path);
4732
+ let source = "global";
4733
+ if (projectLocalPaths.has(config.path) || projectGlobalPaths.has(config.path)) {
4734
+ source = "project";
4735
+ } else if (cliPaths.has(config.path)) {
4736
+ source = "cli";
4737
+ } else if (globalPaths.has(config.path)) {
4738
+ source = "global";
4739
+ }
4740
+ result.push({
4741
+ name: manifest?.name || config.path.split("/").pop() || "unknown",
4742
+ path: config.path,
4743
+ description: manifest?.description,
4744
+ source,
4745
+ enabled: exists
4746
+ });
4747
+ }
4748
+ return result;
4328
4749
  }
4329
- var MAX_NATIVE_AGENT_DRIFT_KINDS = 16;
4330
- var MAX_NATIVE_AGENT_DRIFT_KIND_LENGTH = 128;
4331
- var NATIVE_AGENT_PENDING_SIGN_IN_TTL_MS = 15 * 60000;
4332
- var BACKGROUND_CAPABLE_SHELL_TOOL_NAMES = new Set([
4333
- "bash",
4334
- "shell",
4335
- "terminal",
4336
- "run_command",
4337
- "runcommand",
4338
- "run_terminal_cmd",
4339
- "execute_command"
4340
- ]);
4341
- var NATIVE_AGENT_COMMAND_EXECUTION_KINDS = [
4342
- "provider-prompt",
4343
- "provider-command",
4344
- "structured-skill",
4345
- "bridge-template",
4346
- "bridge-local",
4347
- "session-action"
4348
- ];
4349
- var NATIVE_AGENT_COMMAND_ORIGINS = [
4350
- "project",
4351
- "user",
4352
- "system",
4353
- "admin",
4354
- "plugin",
4355
- "orkestrator",
4356
- "unknown"
4357
- ];
4358
- var NATIVE_AGENT_COMMAND_UNAVAILABLE_REASONS = [
4359
- "disabled",
4360
- "shadowed",
4361
- "reserved-name",
4362
- "ambiguous",
4363
- "requires-interactive-ui",
4364
- "requires-shell-execution",
4365
- "session-changing",
4366
- "unqualified",
4367
- "unsupported",
4368
- "update-required"
4369
- ];
4370
- var NATIVE_AGENT_COMMAND_CATALOGUE_ERROR_CODES = [
4371
- "timeout",
4372
- "unreachable",
4373
- "rejected",
4374
- "malformed",
4375
- "too-large",
4376
- "provider-error",
4377
- "outdated-bridge"
4378
- ];
4379
- var DEFAULT_NATIVE_AGENT_LIVE_WINDOW = {
4380
- messages: 100,
4381
- targetBytes: 512 * 1024
4382
- };
4383
- var NATIVE_AGENT_SYNC_MAX_SNAPSHOT_BYTES = 20 * 1024 * 1024;
4384
- var NATIVE_AGENT_SYNC_MAX_PAGE_BYTES = 16 * 1024 * 1024 + 64 * 1024;
4385
- var PROJECTION_FIELD_SET = new Set([
4386
- "platform",
4387
- "environmentId",
4388
- "sessionId",
4389
- "title",
4390
- "shareUrl",
4391
- "cursor",
4392
- "connection",
4393
- "turn",
4394
- "messageWindow",
4395
- "interactions",
4396
- "composerControls",
4397
- "composer",
4398
- "readiness",
4399
- "capabilities",
4400
- "queue",
4401
- "asyncQuestionResponses",
4402
- "contextUsage",
4403
- "rateLimits",
4404
- "runtime",
4405
- "runtimeHealthAuthoritative",
4406
- "notices",
4407
- "recoverableDispatch",
4408
- "retiredDispatch",
4409
- "backgroundTasks",
4410
- "suggestedPrompt",
4411
- "completionBlockedByBackgroundTasks",
4412
- "turnBoundaries",
4413
- "slashCommands",
4414
- "slashCommandCatalogue"
4415
- ]);
4416
- var OPTIONAL_PROJECTION_FIELD_SET = new Set([
4417
- "sessionId",
4418
- "title",
4419
- "shareUrl",
4420
- "cursor",
4421
- "messageWindow",
4422
- "composer",
4423
- "readiness",
4424
- "queue",
4425
- "asyncQuestionResponses",
4426
- "contextUsage",
4427
- "rateLimits",
4428
- "runtime",
4429
- "runtimeHealthAuthoritative",
4430
- "notices",
4431
- "recoverableDispatch",
4432
- "retiredDispatch",
4433
- "backgroundTasks",
4434
- "suggestedPrompt",
4435
- "completionBlockedByBackgroundTasks",
4436
- "turnBoundaries",
4437
- "slashCommands",
4438
- "slashCommandCatalogue"
4439
- ]);
4440
4750
 
4441
4751
  // ../protocol/src/coordinator-web-research.ts
4442
4752
  var COORDINATOR_WEB_RESEARCH_MODES = ["off", "search", "search-and-mcp"];
@@ -4530,11 +4840,11 @@ function claudeMcpToolPrefix(name) {
4530
4840
 
4531
4841
  // ../../bridges/claude-bridge/src/services/read-only-policy.ts
4532
4842
  var CAPABILITY_TOOLS = new Map([
4533
- ["file.write", ["Write", "NotebookEdit"]],
4843
+ ["file.write", ["Write", "NotebookEdit", "EnterWorktree", "ExitWorktree"]],
4534
4844
  ["file.patch", ["Edit", "MultiEdit", "ApplyPatch"]],
4535
- ["shell", ["Bash", "BashOutput", "KillShell"]],
4536
- ["shell.mutate", []],
4537
- ["network", ["WebFetch", "WebSearch"]],
4845
+ ["shell", ["Bash", "BashOutput", "KillShell", "PowerShell"]],
4846
+ ["shell.mutate", ["PowerShell"]],
4847
+ ["network", ["WebFetch", "WebSearch", "RemoteTrigger"]],
4538
4848
  ["web.search", ["WebSearch"]]
4539
4849
  ]);
4540
4850
  var READ_ONLY_TOOLS = Object.freeze([
@@ -4782,6 +5092,7 @@ function claudeDisallowedTools(policy) {
4782
5092
  function claudeReadOnlyAllowedTools(options) {
4783
5093
  return [
4784
5094
  ...READ_ONLY_TOOLS,
5095
+ ...(options.skillNames ?? []).map((name) => `Skill(${name})`),
4785
5096
  ...options.policy && !nativeAgentPolicyDeniesWebSearch(options.policy) ? ["WebSearch"] : [],
4786
5097
  ...(options.agentMcpServerNames ?? []).map((name) => `${claudeMcpToolPrefix(name)}*`),
4787
5098
  ...(options.researchMcpServerNames ?? []).map((name) => `${claudeMcpToolPrefix(name)}*`)
@@ -5033,16 +5344,36 @@ function claudeSettingSources(policy, includeLocalSettings) {
5033
5344
  return ["user"];
5034
5345
  return includeLocalSettings ? ["user", "project", "local"] : ["user", "project"];
5035
5346
  }
5347
+ function claudeManagedSettings(policy) {
5348
+ return isCoordinatorReadOnlyPolicy(policy) ? { allowManagedHooksOnly: true, disableSkillShellExecution: true } : undefined;
5349
+ }
5350
+ function loadsCoordinatorSkills(policy, env = process.env) {
5351
+ return isCoordinatorReadOnlyPolicy(policy) && processExecutionPolicyIsCoordinator(env);
5352
+ }
5353
+ async function resolveClaudeTurnPlugins(cwd, policy, settingSources) {
5354
+ const [plugins, skills] = await Promise.all([
5355
+ getPluginsForSdk(cwd, policy?.projectResources !== false, settingSources),
5356
+ loadsCoordinatorSkills(policy) ? coordinatorSkillPlugins(coordinatorSkillSources(cwd)) : { plugins: [], skills: [] }
5357
+ ]);
5358
+ return { plugins: [...plugins, ...skills.plugins], coordinatorSkills: skills.skills };
5359
+ }
5036
5360
  async function resolveClaudeDiscoveryConfig(sessionPolicy, inputs) {
5037
5361
  const policy = claudeTurnPolicy(sessionPolicy, inputs.readOnly);
5038
5362
  const cwd = claudeWorkspaceCwd();
5039
5363
  const settingSources = claudeSettingSources(policy, inputs.includeLocalSettings);
5040
- const plugins = await getPluginsForSdk(cwd, policy?.projectResources !== false, settingSources);
5364
+ const { plugins } = await resolveClaudeTurnPlugins(cwd, policy, settingSources);
5365
+ const managedSettings = claudeManagedSettings(policy);
5041
5366
  return {
5042
5367
  cwd,
5043
5368
  settingSources,
5044
5369
  plugins,
5045
- fingerprint: JSON.stringify([cwd, settingSources, plugins.map((plugin) => plugin.path)])
5370
+ managedSettings,
5371
+ fingerprint: JSON.stringify([
5372
+ cwd,
5373
+ settingSources,
5374
+ plugins.map((plugin) => plugin.path),
5375
+ managedSettings ?? null
5376
+ ])
5046
5377
  };
5047
5378
  }
5048
5379
  function effectiveClaudeChrome(policy, options) {
@@ -5060,6 +5391,15 @@ var LIVE_BACKGROUND_TASK_STATUSES = new Set([
5060
5391
  "running",
5061
5392
  "paused"
5062
5393
  ]);
5394
+ var CLAUDE_RESUME_ORPHAN_SUMMARY = "Orphaned by a previous Claude Code process exit and reported in an aggregate summary.";
5395
+ function resumeOrphanScanOutcome(message, previous) {
5396
+ if (message.subtype !== "task_notification" || message.status !== "stopped" || message.summary !== CLAUDE_RESUME_ORPHAN_SUMMARY) {
5397
+ return;
5398
+ }
5399
+ if (!previous)
5400
+ return "cleanup";
5401
+ return LIVE_BACKGROUND_TASK_STATUSES.has(previous.status) ? undefined : "already-settled";
5402
+ }
5063
5403
  function registerBackgroundTaskCandidate(session, toolUseId, control) {
5064
5404
  const candidates = session.backgroundTaskCandidates ??= new Map;
5065
5405
  if (!candidates.has(toolUseId) && candidates.size >= MAX_BACKGROUND_TASK_CANDIDATES) {
@@ -6380,6 +6720,36 @@ var RECOGNIZED_UNRENDERED_BLOCK_TYPES = new Set([
6380
6720
  "document",
6381
6721
  "redacted_thinking"
6382
6722
  ]);
6723
+ function persistedUserImageParts(record) {
6724
+ if (record.type !== "user" || !isRootAssistantRecord(record.parent_tool_use_id, record.isSidechain))
6725
+ return [];
6726
+ const content = record.message?.content;
6727
+ if (!Array.isArray(content))
6728
+ return [];
6729
+ if (content.some((block) => block?.type === "tool_result"))
6730
+ return [];
6731
+ const parts = [];
6732
+ for (const block of content) {
6733
+ if (block?.type !== "image")
6734
+ continue;
6735
+ const source = block.source;
6736
+ if (source?.type !== "base64" || typeof source.media_type !== "string" || typeof source.data !== "string") {
6737
+ continue;
6738
+ }
6739
+ const parsed = parseBase64ImageData(`data:${source.media_type};base64,${source.data}`);
6740
+ if (!parsed?.mediaType)
6741
+ continue;
6742
+ const mediaType = parsed.mediaType;
6743
+ const extension = mediaType === "image/jpeg" ? "jpg" : mediaType.slice("image/".length);
6744
+ parts.push({
6745
+ type: "image",
6746
+ content: `image-${parts.length + 1}.${extension}`,
6747
+ fileUrl: `data:${mediaType};base64,${parsed.data}`,
6748
+ imageSource: "attachment"
6749
+ });
6750
+ }
6751
+ return parts;
6752
+ }
6383
6753
  function buildMessageParts(orderedParts, toolTracker) {
6384
6754
  const result = [];
6385
6755
  for (const entry of orderedParts) {
@@ -6723,6 +7093,9 @@ function reducePersistedBackgroundTaskMessage(tasks, message, timestamp) {
6723
7093
  if (message.subtype === "task_notification") {
6724
7094
  if (!message.status)
6725
7095
  return tasks ?? {};
7096
+ const orphanScan = resumeOrphanScanOutcome(message, previous);
7097
+ if (orphanScan === "already-settled")
7098
+ return tasks ?? {};
6726
7099
  const startedAt = previous?.startedAt ?? timestamp;
6727
7100
  const endedAt = timestamp >= startedAt ? timestamp : previous?.endedAt;
6728
7101
  const terminalStatus = message.status === "failed" ? "failed" : message.status === "stopped" ? "killed" : message.status === "completed" ? "completed" : "killed";
@@ -6742,7 +7115,8 @@ function reducePersistedBackgroundTaskMessage(tasks, message, timestamp) {
6742
7115
  usage: message.usage ?? previous?.usage,
6743
7116
  subagentType: previous?.subagentType,
6744
7117
  ...carriedTaskMetadata(previous),
6745
- ...taskMessageMetadata(message)
7118
+ ...taskMessageMetadata(message),
7119
+ ...orphanScan === "cleanup" ? { endReason: "resume_cleanup" } : {}
6746
7120
  }
6747
7121
  });
6748
7122
  }
@@ -6872,8 +7246,9 @@ function normalizePersistedSessionMessages(persisted) {
6872
7246
  });
6873
7247
  if (entry.raw.type === "system")
6874
7248
  continue;
6875
- const parts = buildMessageParts(entry.orderedParts, toolTracker);
6876
- if (entry.raw.type === "user" && !entry.content.trim())
7249
+ const images = persistedUserImageParts(entry.raw);
7250
+ const parts = [...buildMessageParts(entry.orderedParts, toolTracker), ...images];
7251
+ if (entry.raw.type === "user" && !entry.content.trim() && images.length === 0)
6877
7252
  continue;
6878
7253
  if (entry.raw.type === "assistant" && parts.length === 0 && !entry.content.trim()) {
6879
7254
  continue;
@@ -7527,7 +7902,7 @@ function effectiveClaudeContext1m(options) {
7527
7902
  return options?.parameterValues?.context1m === true && (!model || model === "default" || /opus|sonnet/i.test(model));
7528
7903
  }
7529
7904
  function claudeLaunchResourceProfile(plugins, environment) {
7530
- const digest = (value) => createHash5("sha256").update(JSON.stringify(value)).digest("hex");
7905
+ const digest = (value) => createHash6("sha256").update(JSON.stringify(value)).digest("hex");
7531
7906
  return {
7532
7907
  plugins: digest(plugins.map((plugin) => plugin.path)),
7533
7908
  credentials: digest([
@@ -7571,8 +7946,8 @@ async function assertClaudeRuntimeAccepts(session, options, resources) {
7571
7946
  refuse(claudeLaunchConflict(runtime.launch, request));
7572
7947
  if (!resources) {
7573
7948
  const policy = claudeTurnPolicy(session.executionPolicy, options?.readOnly);
7574
- const [plugins, environment] = await Promise.all([
7575
- getPluginsForSdk(request.cwd, policy?.projectResources !== false, claudeTurnSettingSources(session, options)),
7949
+ const [{ plugins }, environment] = await Promise.all([
7950
+ resolveClaudeTurnPlugins(request.cwd, policy, claudeTurnSettingSources(session, options)),
7576
7951
  runtimeEnvironmentForAgentQuery()
7577
7952
  ]);
7578
7953
  resources = claudeLaunchResourceProfile(plugins, environment);
@@ -7772,11 +8147,11 @@ function noMessages() {
7772
8147
 
7773
8148
  // ../../bridges/claude-bridge/src/services/session-manager-persistence.ts
7774
8149
  import { constants } from "node:fs";
7775
- import { lstat as lstat2, open as open2, realpath } from "node:fs/promises";
7776
- import { isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve2, sep } from "node:path";
8150
+ import { lstat as lstat2, open as open2, realpath as realpath2 } from "node:fs/promises";
8151
+ import { isAbsolute as isAbsolute3, join as join9, relative, resolve as resolve2, sep } from "node:path";
7777
8152
 
7778
8153
  // ../protocol/src/node/command-change-journal.ts
7779
- import { appendFile, mkdir as mkdir2, readFile as readFile4, rename as rename2, rm as rm2, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
8154
+ import { appendFile, mkdir as mkdir3, readFile as readFile5, rename as rename3, rm as rm3, stat as stat4, writeFile as writeFile3 } from "node:fs/promises";
7780
8155
  import { dirname } from "node:path";
7781
8156
  var MAX_JOURNAL_ENTRIES = 1000;
7782
8157
  var MAX_JOURNAL_FILES_PER_ENTRY = 20;
@@ -7850,10 +8225,10 @@ class CommandChangeJournal {
7850
8225
  const line = `${JSON.stringify({ id, change: bounded })}
7851
8226
  `;
7852
8227
  this.writes = this.writes.then(async () => {
7853
- await mkdir2(dirname(this.path), { recursive: true, mode: 448 });
8228
+ await mkdir3(dirname(this.path), { recursive: true, mode: 448 });
7854
8229
  if (!this.sizeChecked) {
7855
8230
  this.sizeChecked = true;
7856
- const existing = await stat3(this.path).catch(() => {
8231
+ const existing = await stat4(this.path).catch(() => {
7857
8232
  return;
7858
8233
  });
7859
8234
  if (existing && existing.size > MAX_JOURNAL_BYTES / 2)
@@ -7873,7 +8248,7 @@ class CommandChangeJournal {
7873
8248
  async remove() {
7874
8249
  this.removed = true;
7875
8250
  await this.writes;
7876
- await rm2(this.path, { force: true }).catch(() => {});
8251
+ await rm3(this.path, { force: true }).catch(() => {});
7877
8252
  }
7878
8253
  async compact() {
7879
8254
  this.appendsSinceCompaction = 0;
@@ -7881,16 +8256,16 @@ class CommandChangeJournal {
7881
8256
  const body = Array.from(records, ([id, change]) => JSON.stringify({ id, change })).join(`
7882
8257
  `);
7883
8258
  const temp = `${this.path}.${process.pid}.tmp`;
7884
- await writeFile2(temp, body ? `${body}
8259
+ await writeFile3(temp, body ? `${body}
7885
8260
  ` : "", { mode: 384 });
7886
- await rename2(temp, this.path);
8261
+ await rename3(temp, this.path);
7887
8262
  }
7888
8263
  }
7889
8264
  async function readJournal(path, maxEntries) {
7890
8265
  const records = new Map;
7891
8266
  let text;
7892
8267
  try {
7893
- const buffer = await readFile4(path);
8268
+ const buffer = await readFile5(path);
7894
8269
  text = buffer.subarray(Math.max(0, buffer.length - MAX_JOURNAL_BYTES)).toString("utf8");
7895
8270
  } catch {
7896
8271
  return records;
@@ -7928,9 +8303,9 @@ async function readJournal(path, maxEntries) {
7928
8303
  import { execFile as execFile3, spawn as spawn2 } from "node:child_process";
7929
8304
  import { randomUUID as randomUUID2 } from "node:crypto";
7930
8305
  import { rmSync } from "node:fs";
7931
- import { copyFile, lstat, mkdtemp, rm as rm3 } from "node:fs/promises";
8306
+ import { copyFile, lstat, mkdtemp, rm as rm4 } from "node:fs/promises";
7932
8307
  import { tmpdir } from "node:os";
7933
- import { delimiter, isAbsolute, join as join4, resolve } from "node:path";
8308
+ import { delimiter, isAbsolute as isAbsolute2, join as join5, resolve } from "node:path";
7934
8309
  var DEFAULT_MAX_UNTRACKED_FILE_BYTES = 2 * 1024 * 1024;
7935
8310
  var DEFAULT_MAX_UNTRACKED_FILES = 5000;
7936
8311
  var DEFAULT_SLOW_SNAPSHOT_MS = 500;
@@ -8019,7 +8394,7 @@ class WorkspaceChangeProbe {
8019
8394
  repo.latest = undefined;
8020
8395
  }
8021
8396
  async removeObjectDirectory(directory) {
8022
- await rm3(directory, { recursive: true, force: true }).catch(() => {});
8397
+ await rm4(directory, { recursive: true, force: true }).catch(() => {});
8023
8398
  temporaryObjectDirectories.delete(directory);
8024
8399
  }
8025
8400
  objectEnv(repo, directory, beforeDirectory) {
@@ -8212,8 +8587,8 @@ class WorkspaceChangeProbe {
8212
8587
  return;
8213
8588
  return {
8214
8589
  root,
8215
- indexPath: isAbsolute(index) ? index : resolve(cwd, index),
8216
- objectsPath: isAbsolute(objects) ? objects : resolve(cwd, objects)
8590
+ indexPath: isAbsolute2(index) ? index : resolve(cwd, index),
8591
+ objectsPath: isAbsolute2(objects) ? objects : resolve(cwd, objects)
8217
8592
  };
8218
8593
  }).catch(() => {
8219
8594
  return;
@@ -8256,8 +8631,8 @@ class WorkspaceChangeProbe {
8256
8631
  return;
8257
8632
  const { root, indexPath } = repo.location;
8258
8633
  const started = this.now();
8259
- const tempIndex = join4(this.tempDir, `orkestrator-probe-${randomUUID2()}.index`);
8260
- const objectDirectory = await mkdtemp(join4(this.tempDir, "orkestrator-probe-objects-"));
8634
+ const tempIndex = join5(this.tempDir, `orkestrator-probe-${randomUUID2()}.index`);
8635
+ const objectDirectory = await mkdtemp(join5(this.tempDir, "orkestrator-probe-objects-"));
8261
8636
  temporaryObjectDirectories.add(objectDirectory);
8262
8637
  let retained = false;
8263
8638
  try {
@@ -8274,7 +8649,7 @@ class WorkspaceChangeProbe {
8274
8649
  const included = [];
8275
8650
  const skipped = new Map;
8276
8651
  for (const path of untracked) {
8277
- const stats = await lstat(join4(root, path)).catch(() => {
8652
+ const stats = await lstat(join5(root, path)).catch(() => {
8278
8653
  return;
8279
8654
  });
8280
8655
  if (!stats)
@@ -8301,8 +8676,8 @@ class WorkspaceChangeProbe {
8301
8676
  retained = true;
8302
8677
  return snapshot;
8303
8678
  } finally {
8304
- await rm3(tempIndex, { force: true }).catch(() => {});
8305
- await rm3(`${tempIndex}.lock`, { force: true }).catch(() => {});
8679
+ await rm4(tempIndex, { force: true }).catch(() => {});
8680
+ await rm4(`${tempIndex}.lock`, { force: true }).catch(() => {});
8306
8681
  if (!retained)
8307
8682
  await this.removeObjectDirectory(objectDirectory);
8308
8683
  this.recordDuration(repo, this.now() - started);
@@ -8420,10 +8795,10 @@ function parseNumstatZ(output) {
8420
8795
  }
8421
8796
 
8422
8797
  // ../../bridges/claude-bridge/src/services/command-changes.ts
8423
- import { join as join7 } from "node:path";
8798
+ import { join as join8 } from "node:path";
8424
8799
 
8425
8800
  // ../../bridges/claude-bridge/src/services/change-evidence.ts
8426
- import { createHash as createHash7 } from "node:crypto";
8801
+ import { createHash as createHash8 } from "node:crypto";
8427
8802
  import {
8428
8803
  closeSync,
8429
8804
  existsSync,
@@ -8433,7 +8808,7 @@ import {
8433
8808
  rmSync as rmSync2,
8434
8809
  writeSync
8435
8810
  } from "node:fs";
8436
- import { join as join6 } from "node:path";
8811
+ import { join as join7 } from "node:path";
8437
8812
 
8438
8813
  // ../protocol/src/field-validation.ts
8439
8814
  function isPlainRecord(value) {
@@ -8836,10 +9211,10 @@ function workspaceObservationReportIssues(value) {
8836
9211
  }
8837
9212
 
8838
9213
  // ../protocol/src/node/change-tracking/evidence-spool.ts
8839
- import { createHash as createHash6, randomUUID as randomUUID3 } from "node:crypto";
9214
+ import { createHash as createHash7, randomUUID as randomUUID3 } from "node:crypto";
8840
9215
  import { readdirSync, readFileSync } from "node:fs";
8841
- import { appendFile as appendFile2, mkdir as mkdir3, rename as rename3, rm as rm4, writeFile as writeFile3 } from "node:fs/promises";
8842
- import { join as join5 } from "node:path";
9216
+ import { appendFile as appendFile2, mkdir as mkdir4, rename as rename4, rm as rm5, writeFile as writeFile4 } from "node:fs/promises";
9217
+ import { join as join6 } from "node:path";
8843
9218
  var DEFAULT_EVIDENCE_SPOOL_LIMITS = {
8844
9219
  maxEntries: 2048,
8845
9220
  maxEntryBytes: 256 * 1024,
@@ -8852,10 +9227,10 @@ var DEFAULT_EVIDENCE_SPOOL_LIMITS = {
8852
9227
  var ENTRIES_FILE = "entries.jsonl";
8853
9228
  var BODY_DIRECTORY = "bodies";
8854
9229
  function evidenceBodyId(bytes) {
8855
- return `sha256:${createHash6("sha256").update(bytes).digest("hex")}`;
9230
+ return `sha256:${createHash7("sha256").update(bytes).digest("hex")}`;
8856
9231
  }
8857
9232
  function stableEvidenceId(...parts) {
8858
- const hash = createHash6("sha256");
9233
+ const hash = createHash7("sha256");
8859
9234
  for (const part of parts)
8860
9235
  hash.update(`${part ?? ""}\x00`);
8861
9236
  return hash.digest("hex").slice(0, 40);
@@ -9101,8 +9476,8 @@ class ChangeEvidenceSpool {
9101
9476
  return;
9102
9477
  const lines = this.pendingLines.splice(0).join("");
9103
9478
  this.enqueueIo(async () => {
9104
- await mkdir3(directory, { recursive: true, mode: 448 });
9105
- await appendFile2(join5(directory, ENTRIES_FILE), lines, { mode: 384 });
9479
+ await mkdir4(directory, { recursive: true, mode: 448 });
9480
+ await appendFile2(join6(directory, ENTRIES_FILE), lines, { mode: 384 });
9106
9481
  });
9107
9482
  }
9108
9483
  persistBody(id, bytes) {
@@ -9110,9 +9485,9 @@ class ChangeEvidenceSpool {
9110
9485
  if (!directory || this.restoring)
9111
9486
  return;
9112
9487
  this.enqueueIo(async () => {
9113
- const bodies = join5(directory, BODY_DIRECTORY);
9114
- await mkdir3(bodies, { recursive: true, mode: 448 });
9115
- await writeFile3(join5(bodies, bodyFileName(id)), bytes, { mode: 384, flag: "wx" }).catch((error) => {
9488
+ const bodies = join6(directory, BODY_DIRECTORY);
9489
+ await mkdir4(bodies, { recursive: true, mode: 448 });
9490
+ await writeFile4(join6(bodies, bodyFileName(id)), bytes, { mode: 384, flag: "wx" }).catch((error) => {
9116
9491
  if (error.code !== "EEXIST")
9117
9492
  throw error;
9118
9493
  });
@@ -9122,7 +9497,7 @@ class ChangeEvidenceSpool {
9122
9497
  const directory = this.directory;
9123
9498
  if (!directory)
9124
9499
  return;
9125
- this.enqueueIo(() => rm4(join5(directory, BODY_DIRECTORY, bodyFileName(id)), { force: true }));
9500
+ this.enqueueIo(() => rm5(join6(directory, BODY_DIRECTORY, bodyFileName(id)), { force: true }));
9126
9501
  }
9127
9502
  compactPersisted() {
9128
9503
  const directory = this.directory;
@@ -9132,12 +9507,12 @@ class ChangeEvidenceSpool {
9132
9507
  const body = this.entries.map((stored) => JSON.stringify({ epoch: this.runtimeEpoch, entry: stored.entry })).join(`
9133
9508
  `);
9134
9509
  this.enqueueIo(async () => {
9135
- await mkdir3(directory, { recursive: true, mode: 448 });
9136
- const file = join5(directory, ENTRIES_FILE);
9510
+ await mkdir4(directory, { recursive: true, mode: 448 });
9511
+ const file = join6(directory, ENTRIES_FILE);
9137
9512
  const temp = `${file}.${process.pid}.tmp`;
9138
- await writeFile3(temp, body ? `${body}
9513
+ await writeFile4(temp, body ? `${body}
9139
9514
  ` : "", { mode: 384 });
9140
- await rename3(temp, file);
9515
+ await rename4(temp, file);
9141
9516
  });
9142
9517
  }
9143
9518
  async flush() {
@@ -9145,10 +9520,10 @@ class ChangeEvidenceSpool {
9145
9520
  await this.io;
9146
9521
  }
9147
9522
  restore(directory) {
9148
- const bodyDirectory = join5(directory, BODY_DIRECTORY);
9523
+ const bodyDirectory = join6(directory, BODY_DIRECTORY);
9149
9524
  try {
9150
9525
  for (const name of readdirSync(bodyDirectory)) {
9151
- const bytes = new Uint8Array(readFileSync(join5(bodyDirectory, name)));
9526
+ const bytes = new Uint8Array(readFileSync(join6(bodyDirectory, name)));
9152
9527
  const id = evidenceBodyId(bytes);
9153
9528
  if (bodyFileName(id) !== name)
9154
9529
  continue;
@@ -9160,7 +9535,7 @@ class ChangeEvidenceSpool {
9160
9535
  } catch {}
9161
9536
  let text = "";
9162
9537
  try {
9163
- text = readFileSync(join5(directory, ENTRIES_FILE), "utf8");
9538
+ text = readFileSync(join6(directory, ENTRIES_FILE), "utf8");
9164
9539
  } catch {
9165
9540
  return;
9166
9541
  }
@@ -9673,8 +10048,8 @@ function createChangeEvidence(options) {
9673
10048
  }
9674
10049
  function claimSpoolDirectory() {
9675
10050
  const scope = process.env.CWD || process.cwd();
9676
- const directory = join6(claudeChangeEvidenceDir(), createHash7("sha256").update(scope).digest("hex").slice(0, 32));
9677
- const owner = join6(directory, "owner.pid");
10051
+ const directory = join7(claudeChangeEvidenceDir(), createHash8("sha256").update(scope).digest("hex").slice(0, 32));
10052
+ const owner = join7(directory, "owner.pid");
9678
10053
  try {
9679
10054
  mkdirSync(directory, { recursive: true, mode: 448 });
9680
10055
  for (let attempt = 0;attempt < 2; attempt++) {
@@ -9850,7 +10225,7 @@ function commandChangeJournal(sdkSessionId) {
9850
10225
  if (journal) {
9851
10226
  journals.delete(sdkSessionId);
9852
10227
  } else {
9853
- journal = new CommandChangeJournal(join7(claudeCommandChangesDir(), `${sdkSessionId}.jsonl`));
10228
+ journal = new CommandChangeJournal(join8(claudeCommandChangesDir(), `${sdkSessionId}.jsonl`));
9854
10229
  }
9855
10230
  journals.set(sdkSessionId, journal);
9856
10231
  while (journals.size > MAX_CACHED_JOURNALS) {
@@ -10534,13 +10909,6 @@ function getImageMediaType(filePath) {
10534
10909
  return "image/png";
10535
10910
  }
10536
10911
  }
10537
- var SUPPORTED_IMAGE_MEDIA_TYPES = new Set([
10538
- "image/jpeg",
10539
- "image/png",
10540
- "image/gif",
10541
- "image/webp"
10542
- ]);
10543
- var MAX_IMAGE_ATTACHMENT_BYTES = 8 * 1024 * 1024;
10544
10912
 
10545
10913
  class ClaudeAttachmentError extends Error {
10546
10914
  code;
@@ -10550,41 +10918,20 @@ class ClaudeAttachmentError extends Error {
10550
10918
  this.code = code;
10551
10919
  }
10552
10920
  }
10553
- function decodedBase64ByteLength(value) {
10554
- const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
10555
- return value.length / 4 * 3 - padding;
10556
- }
10557
- function parseBase64ImageData(value) {
10558
- let data = value;
10559
- let mediaType;
10560
- if (value.startsWith("data:")) {
10561
- const match = /^data:([^;,]+);base64,([\s\S]+)$/.exec(value);
10562
- if (!match || !SUPPORTED_IMAGE_MEDIA_TYPES.has(match[1])) {
10563
- return null;
10564
- }
10565
- mediaType = match[1];
10566
- data = match[2];
10567
- }
10568
- const normalized = data.replace(/\s+/g, "");
10569
- if (normalized.length === 0 || normalized.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(normalized) || decodedBase64ByteLength(normalized) > MAX_IMAGE_ATTACHMENT_BYTES) {
10570
- return null;
10571
- }
10572
- return { data: normalized, mediaType };
10573
- }
10574
10921
  var BRIDGE_ATTACHMENT_ROOT_ENV = "ORKESTRATOR_BRIDGE_ATTACHMENT_ROOT";
10575
10922
  function configuredAttachmentRoot(env = process.env) {
10576
10923
  const value = env[BRIDGE_ATTACHMENT_ROOT_ENV]?.trim();
10577
- return value && isAbsolute2(value) ? resolve2(value) : undefined;
10924
+ return value && isAbsolute3(value) ? resolve2(value) : undefined;
10578
10925
  }
10579
10926
  function attachmentContainmentRoot(filePath, cwd, extraRoot = configuredAttachmentRoot()) {
10580
- if (extraRoot && isAbsolute2(filePath) && isPathWithin2(extraRoot, resolve2(filePath))) {
10927
+ if (extraRoot && isAbsolute3(filePath) && isPathWithin2(extraRoot, resolve2(filePath))) {
10581
10928
  return extraRoot;
10582
10929
  }
10583
10930
  return resolve2(cwd);
10584
10931
  }
10585
10932
  function isPathWithin2(rootPath, targetPath) {
10586
10933
  const childPath = relative(rootPath, targetPath);
10587
- return childPath === "" || childPath !== ".." && !childPath.startsWith(`..${sep}`) && !isAbsolute2(childPath);
10934
+ return childPath === "" || childPath !== ".." && !childPath.startsWith(`..${sep}`) && !isAbsolute3(childPath);
10588
10935
  }
10589
10936
  function attachmentErrorForFsFailure(error) {
10590
10937
  const code = error?.code;
@@ -10600,7 +10947,7 @@ async function assertNoSymlinkComponents(lexicalRoot, targetPath) {
10600
10947
  const childPath = relative(lexicalRoot, targetPath);
10601
10948
  let currentPath = lexicalRoot;
10602
10949
  for (const segment of childPath.split(sep).filter(Boolean)) {
10603
- currentPath = join8(currentPath, segment);
10950
+ currentPath = join9(currentPath, segment);
10604
10951
  const stats = await lstat2(currentPath).catch((error) => {
10605
10952
  throw attachmentErrorForFsFailure(error);
10606
10953
  });
@@ -10612,7 +10959,7 @@ async function assertNoSymlinkComponents(lexicalRoot, targetPath) {
10612
10959
  async function assertOpenedWorkspaceFile(targetPath, canonicalRoot, openedStats) {
10613
10960
  const [pathStats, canonicalTarget] = await Promise.all([
10614
10961
  lstat2(targetPath),
10615
- realpath(targetPath)
10962
+ realpath2(targetPath)
10616
10963
  ]).catch((error) => {
10617
10964
  throw attachmentErrorForFsFailure(error);
10618
10965
  });
@@ -10628,16 +10975,16 @@ async function assertOpenedWorkspaceFile(targetPath, canonicalRoot, openedStats)
10628
10975
  }
10629
10976
  async function readWorkspaceImageAttachment(filePath, cwd, afterSymlinkValidation, afterCanonicalValidation, afterInitialValidation) {
10630
10977
  const lexicalRoot = attachmentContainmentRoot(filePath, cwd);
10631
- const targetPath = isAbsolute2(filePath) ? resolve2(filePath) : resolve2(lexicalRoot, filePath);
10978
+ const targetPath = isAbsolute3(filePath) ? resolve2(filePath) : resolve2(lexicalRoot, filePath);
10632
10979
  if (!isPathWithin2(lexicalRoot, targetPath)) {
10633
10980
  throw new ClaudeAttachmentError("attachment_outside_workspace", "Image attachment must be contained in the current workspace.");
10634
10981
  }
10635
- const canonicalRoot = await realpath(lexicalRoot).catch((error) => {
10982
+ const canonicalRoot = await realpath2(lexicalRoot).catch((error) => {
10636
10983
  throw attachmentErrorForFsFailure(error);
10637
10984
  });
10638
10985
  await assertNoSymlinkComponents(lexicalRoot, targetPath);
10639
10986
  await afterSymlinkValidation?.(targetPath);
10640
- const canonicalTarget = await realpath(targetPath).catch((error) => {
10987
+ const canonicalTarget = await realpath2(targetPath).catch((error) => {
10641
10988
  throw attachmentErrorForFsFailure(error);
10642
10989
  });
10643
10990
  if (!isPathWithin2(canonicalRoot, canonicalTarget)) {
@@ -12157,8 +12504,8 @@ function createClaudeTurn(admission, input) {
12157
12504
  import { query as query2 } from "@anthropic-ai/claude-agent-sdk";
12158
12505
 
12159
12506
  // ../../bridges/claude-bridge/src/services/mcp-config.ts
12160
- import { createHash as createHash8 } from "node:crypto";
12161
- import { join as join9 } from "node:path";
12507
+ import { createHash as createHash9 } from "node:crypto";
12508
+ import { join as join10 } from "node:path";
12162
12509
 
12163
12510
  // ../protocol/src/agent-mail.ts
12164
12511
  var AGENT_MAIL_MAX_BODY_BYTES = 32 * 1024;
@@ -12267,7 +12614,7 @@ async function loadGlobalMcpServers() {
12267
12614
  return servers ?? {};
12268
12615
  }
12269
12616
  async function loadProjectMcpServers(cwd) {
12270
- const servers = await readJsonSliceCached(join9(cwd, ".mcp.json"), "mcpServers", (config) => config?.mcpServers);
12617
+ const servers = await readJsonSliceCached(join10(cwd, ".mcp.json"), "mcpServers", (config) => config?.mcpServers);
12271
12618
  return servers ?? {};
12272
12619
  }
12273
12620
  async function loadProjectOverridesFromGlobal2(cwd) {
@@ -12371,7 +12718,7 @@ async function loadScopedMcpServers(cwd, scope) {
12371
12718
  const [global2, projectGlobal, projectLocal] = await Promise.all([
12372
12719
  readJsonSliceCachedWithDigest(path, "mcpServers", (config) => config?.mcpServers),
12373
12720
  scope === "all" ? readJsonSliceCachedWithDigest(path, `projects:${cwd}:mcpServers`, (config) => config?.projects?.[cwd]?.mcpServers) : null,
12374
- scope === "all" ? readJsonSliceCachedWithDigest(join9(cwd, ".mcp.json"), "mcpServers", (config) => config?.mcpServers) : null
12721
+ scope === "all" ? readJsonSliceCachedWithDigest(join10(cwd, ".mcp.json"), "mcpServers", (config) => config?.mcpServers) : null
12375
12722
  ]);
12376
12723
  const configs = {
12377
12724
  ...global2.value,
@@ -12385,7 +12732,7 @@ async function loadScopedMcpServers(cwd, scope) {
12385
12732
  return { configs, revision: { fingerprint: revisionFingerprint(sources), sources, scope } };
12386
12733
  }
12387
12734
  function revisionFingerprint(sources) {
12388
- return createHash8("sha256").update(`user=${sources.user}\x00project=${sources.project}`).digest("base64url");
12735
+ return createHash9("sha256").update(`user=${sources.user}\x00project=${sources.project}`).digest("base64url");
12389
12736
  }
12390
12737
  async function getMcpRuntimeConfig(cwd, env = process.env, connection, sources = "all") {
12391
12738
  const { configs, revision } = await loadScopedMcpServers(cwd, normalizeScope(sources));
@@ -12725,8 +13072,8 @@ function idleSteerPromptReply(prompt, agentLabel) {
12725
13072
  }
12726
13073
 
12727
13074
  // ../../bridges/claude-bridge/src/services/claude-terminal-commands.ts
12728
- import { mkdir as mkdir4, readFile as readFile5, rename as rename4, writeFile as writeFile4 } from "node:fs/promises";
12729
- import { join as join10 } from "node:path";
13075
+ import { mkdir as mkdir5, readFile as readFile6, rename as rename5, writeFile as writeFile5 } from "node:fs/promises";
13076
+ import { join as join11 } from "node:path";
12730
13077
  var FILE_NAME = "terminal-slash-commands.json";
12731
13078
  var MAX_VERSIONS = 4;
12732
13079
  var MAX_NAMES = 512;
@@ -12737,7 +13084,7 @@ var dirty = false;
12737
13084
  var entries = [];
12738
13085
  var writes = Promise.resolve();
12739
13086
  function filePath() {
12740
- return join10(claudeOrkestratorDir(), FILE_NAME);
13087
+ return join11(claudeOrkestratorDir(), FILE_NAME);
12741
13088
  }
12742
13089
  function validVersion(value) {
12743
13090
  return typeof value === "string" && value.length > 0 && value.length <= MAX_VERSION_LENGTH;
@@ -12765,7 +13112,7 @@ function parseEntries(raw) {
12765
13112
  return result;
12766
13113
  }
12767
13114
  function load() {
12768
- loaded ??= readFile5(filePath(), "utf8").then(parseEntries, () => []).catch(() => []).then((stored) => {
13115
+ loaded ??= readFile6(filePath(), "utf8").then(parseEntries, () => []).catch(() => []).then((stored) => {
12769
13116
  const recorded = new Set(entries.map((entry) => entry.version));
12770
13117
  entries = [...entries, ...stored.filter((entry) => !recorded.has(entry.version))].slice(0, MAX_VERSIONS);
12771
13118
  });
@@ -12792,22 +13139,22 @@ function recordTerminalSlashCommands(version, commands) {
12792
13139
  writes = writes.then(async () => {
12793
13140
  await load();
12794
13141
  const directory = claudeOrkestratorDir();
12795
- await mkdir4(directory, { recursive: true });
13142
+ await mkdir5(directory, { recursive: true });
12796
13143
  const target = filePath();
12797
13144
  const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
12798
13145
  const snapshot = entries;
12799
- await writeFile4(temporary, `${JSON.stringify({ versions: snapshot })}
13146
+ await writeFile5(temporary, `${JSON.stringify({ versions: snapshot })}
12800
13147
  `, {
12801
13148
  mode: 384
12802
13149
  });
12803
- await rename4(temporary, target);
13150
+ await rename5(temporary, target);
12804
13151
  if (entries === snapshot)
12805
13152
  dirty = false;
12806
13153
  }).catch(() => {});
12807
13154
  }
12808
13155
 
12809
13156
  // ../../bridges/claude-bridge/src/services/session-manager-catalog.ts
12810
- import { createHash as createHash9 } from "node:crypto";
13157
+ import { createHash as createHash10 } from "node:crypto";
12811
13158
 
12812
13159
  // ../protocol/src/secret-redaction.ts
12813
13160
  var TEXT_RULES = [
@@ -13251,7 +13598,7 @@ async function steerClaudeSession(sessionId, text, requestId, expectedRunId) {
13251
13598
  requestId
13252
13599
  };
13253
13600
  const session = sessions.get(sessionId);
13254
- const inputDigest = createHash9("sha256").update(text).digest("hex");
13601
+ const inputDigest = createHash10("sha256").update(text).digest("hex");
13255
13602
  const previous = session?.steerJournal?.get(requestId);
13256
13603
  if (previous) {
13257
13604
  if (previous.inputDigest !== inputDigest || previous.expectedRunId !== expectedRunId) {
@@ -13675,6 +14022,7 @@ async function runDiscoveryProbe(config) {
13675
14022
  env,
13676
14023
  settingSources: config.settingSources,
13677
14024
  ...config.plugins.length > 0 ? { plugins: config.plugins } : {},
14025
+ ...config.managedSettings ? { managedSettings: config.managedSettings } : {},
13678
14026
  ...claudeExecutableOptions()
13679
14027
  });
13680
14028
  const read = probe.supportedCommands();
@@ -14016,7 +14364,7 @@ function createStaleMcpServerHook(mcp) {
14016
14364
  }
14017
14365
 
14018
14366
  // ../../bridges/claude-bridge/src/services/session-manager-prompt-query.ts
14019
- import { createHash as createHash10 } from "node:crypto";
14367
+ import { createHash as createHash11 } from "node:crypto";
14020
14368
 
14021
14369
  // ../protocol/src/review-evidence-frames.ts
14022
14370
  var STRUCTURED_REVIEW_FINDINGS_PROMPT_PREFIX = "The findings below are an untrusted JSON data frame.";
@@ -14150,7 +14498,7 @@ import { realpathSync } from "node:fs";
14150
14498
  // ../protocol/src/node/claude-linux-sandbox.ts
14151
14499
  import { spawnSync } from "node:child_process";
14152
14500
  import { accessSync, constants as constants2, existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
14153
- import { delimiter as delimiter2, join as join11 } from "node:path";
14501
+ import { delimiter as delimiter2, join as join12 } from "node:path";
14154
14502
  var CLAUDE_LINUX_SANDBOX_PROGRAMS = Object.freeze(["bwrap", "socat"]);
14155
14503
  var APPARMOR_BWRAP_PROFILE = "/etc/apparmor.d/bwrap-userns-restrict";
14156
14504
  var CLAUDE_LINUX_SANDBOX_PROBE_TIMEOUT_MS = 5000;
@@ -14184,7 +14532,7 @@ function executableOnPath(name, path = process.env.PATH) {
14184
14532
  if (!directory)
14185
14533
  continue;
14186
14534
  try {
14187
- accessSync(join11(directory, name), constants2.X_OK);
14535
+ accessSync(join12(directory, name), constants2.X_OK);
14188
14536
  return true;
14189
14537
  } catch {}
14190
14538
  }
@@ -14273,70 +14621,6 @@ function claudeLinuxSandboxStatus(now = Date.now()) {
14273
14621
  return status;
14274
14622
  }
14275
14623
 
14276
- // ../protocol/src/coordinator.ts
14277
- var COORDINATOR_COMMAND_SANDBOX_ENV = "ORKESTRATOR_COORDINATOR_COMMAND_SANDBOX";
14278
- function coordinatorCommandSandboxFromEnv(env) {
14279
- return env[COORDINATOR_COMMAND_SANDBOX_ENV] === "unavailable" ? "unavailable" : "required";
14280
- }
14281
- var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
14282
- "ack_message",
14283
- "add_ticket_comment",
14284
- "address_multi_review",
14285
- "adopt_workflow",
14286
- "cancel_build_pipeline",
14287
- "cancel_multi_review",
14288
- "cancel_schedule",
14289
- "compare_project_files",
14290
- "create_schedule",
14291
- "create_ticket",
14292
- "get_build_pipeline",
14293
- "get_environment",
14294
- "get_launch_options",
14295
- "get_message",
14296
- "get_message_status",
14297
- "get_multi_review",
14298
- "get_repository_context",
14299
- "get_tab_state",
14300
- "get_tab_transcript",
14301
- "get_ticket",
14302
- "launch_environment",
14303
- "launch_job",
14304
- "launch_multi_review",
14305
- "launch_node_environment",
14306
- "list_agents",
14307
- "list_environments",
14308
- "list_mailboxes",
14309
- "list_node_environments",
14310
- "list_nodes",
14311
- "list_projects",
14312
- "list_schedules",
14313
- "list_tabs",
14314
- "list_tickets",
14315
- "list_workflows",
14316
- "open_multi_review",
14317
- "open_multi_review_fix",
14318
- "pause_build_pipeline",
14319
- "read_messages",
14320
- "read_node_environment",
14321
- "request_env_value_reveal",
14322
- "request_environment_deletion",
14323
- "request_project_file_update",
14324
- "request_project_on_node",
14325
- "resume_build_pipeline",
14326
- "send_message",
14327
- "send_node_message",
14328
- "send_prompt_to_node_tab",
14329
- "send_prompt_to_tab",
14330
- "start_build_pipeline",
14331
- "start_environment",
14332
- "start_multi_review",
14333
- "start_node_environment",
14334
- "stop_environment",
14335
- "stop_node_environment",
14336
- "transfer_files",
14337
- "update_ticket"
14338
- ]);
14339
-
14340
14624
  // ../../bridges/claude-bridge/src/services/claude-execution-sandbox.ts
14341
14625
  function claudeReadOnlyWorkspaces(cwd) {
14342
14626
  let resolved;
@@ -14521,7 +14805,7 @@ function canAdmitInteraction(candidate, family) {
14521
14805
  }
14522
14806
 
14523
14807
  // ../../bridges/claude-bridge/src/services/session-manager-interactions.ts
14524
- import { readFile as readFile6 } from "node:fs/promises";
14808
+ import { readFile as readFile7 } from "node:fs/promises";
14525
14809
 
14526
14810
  // ../../bridges/claude-bridge/src/services/claude-elicitation-schema.ts
14527
14811
  function record2(value) {
@@ -14770,7 +15054,7 @@ async function getClaudeRuntimeVersions() {
14770
15054
  let bundledCliVersion;
14771
15055
  try {
14772
15056
  const sdkEntryUrl = import.meta.resolve("@anthropic-ai/claude-agent-sdk");
14773
- const manifest = JSON.parse(await readFile6(new URL("./package.json", sdkEntryUrl), "utf8"));
15057
+ const manifest = JSON.parse(await readFile7(new URL("./package.json", sdkEntryUrl), "utf8"));
14774
15058
  sdkVersion = manifest.version;
14775
15059
  bundledCliVersion = manifest.claudeCodeVersion;
14776
15060
  } catch (error) {
@@ -15145,12 +15429,14 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
15145
15429
  readOnly: options?.readOnly === true,
15146
15430
  includeLocalSettings: options?.includeLocalSettings === true
15147
15431
  };
15148
- const includeProjectResources = policy?.projectResources !== false;
15149
15432
  const settingSources = claudeTurnSettingSources(session, options);
15150
15433
  session.commandDiscoveryInputs.includeLocalSettings = settingSources.includes("local");
15151
- const [{ servers: mcpServers, names: mcpServerNames, revision: mcpConfigRevision }, plugins] = await Promise.all([
15434
+ const [
15435
+ { servers: mcpServers, names: mcpServerNames, revision: mcpConfigRevision },
15436
+ { plugins, coordinatorSkills }
15437
+ ] = await Promise.all([
15152
15438
  getMcpRuntimeConfig(cwd, process.env, options?.agentMcp, mcpSourceScopeForPolicy(policy)),
15153
- getPluginsForSdk(cwd, includeProjectResources, settingSources)
15439
+ resolveClaudeTurnPlugins(cwd, policy, settingSources)
15154
15440
  ]);
15155
15441
  turn.mcpServerNames = mcpServerNames;
15156
15442
  const mcpServerCount = Object.keys(mcpServers).length;
@@ -15176,7 +15462,8 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
15176
15462
  const allowedTools = coordinatorReadOnly ? claudeReadOnlyAllowedTools({
15177
15463
  agentMcpServerNames: mcpServerNames.has(AGENT_MCP_SERVER_NAME) ? [AGENT_MCP_SERVER_NAME] : [],
15178
15464
  policy,
15179
- researchMcpServerNames: coordinatorResearchMcpServersFromEnv(process.env).map((server) => server.name).filter((name) => mcpServerNames.has(name))
15465
+ researchMcpServerNames: coordinatorResearchMcpServersFromEnv(process.env).map((server) => server.name).filter((name) => mcpServerNames.has(name)),
15466
+ skillNames: coordinatorSkills
15180
15467
  }) : [
15181
15468
  ...policy?.toolPolicy?.allow ?? defaultAllowedTools,
15182
15469
  ...agentMailTools,
@@ -15213,7 +15500,7 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
15213
15500
  parameterValues: options?.parameterValues,
15214
15501
  fastMode
15215
15502
  });
15216
- const mcpServersKey = createHash10("sha256").update(JSON.stringify(mcpServers)).digest("hex");
15503
+ const mcpServersKey = createHash11("sha256").update(JSON.stringify(mcpServers)).digest("hex");
15217
15504
  return {
15218
15505
  settingSources,
15219
15506
  cwd,
@@ -15269,6 +15556,7 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
15269
15556
  ultracode,
15270
15557
  queryEnvironment
15271
15558
  } = plan;
15559
+ const managedSettings = claudeManagedSettings(policy);
15272
15560
  const latestTurnHook = (pick) => (...args) => pick(turnFor())(...args);
15273
15561
  const backgroundTaskHook = latestTurnHook((target) => target.recordBackgroundTaskHook);
15274
15562
  const commandChangeHook = commandChangeHookFor(turnFor, session.id);
@@ -15327,6 +15615,7 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
15327
15615
  `)
15328
15616
  },
15329
15617
  settingSources: plan.settingSources,
15618
+ ...managedSettings ? { managedSettings } : {},
15330
15619
  ...(fastMode || ultracode) && {
15331
15620
  settings: {
15332
15621
  ...fastMode && { fastMode: true },
@@ -16112,6 +16401,15 @@ function handleTaskNotification(turn, message) {
16112
16401
  const correlated = takeProvisionalBackgroundTask(session, notification.tool_use_id);
16113
16402
  const parked = takeSettlingBackgroundTask(session, notification.task_id);
16114
16403
  const previous = session.backgroundTasks?.[notification.task_id] ?? parked?.task ?? correlated.task;
16404
+ const orphanScan = resumeOrphanScanOutcome(notification, previous);
16405
+ if (orphanScan === "already-settled" && previous) {
16406
+ session.backgroundTasks = boundBackgroundTaskHistory({
16407
+ ...session.backgroundTasks,
16408
+ [previous.id]: previous
16409
+ });
16410
+ emitBackgroundTasks(turn);
16411
+ return;
16412
+ }
16115
16413
  const terminalStatus = notification.status === "failed" ? "failed" : notification.status === "stopped" ? "killed" : "completed";
16116
16414
  const task = {
16117
16415
  id: notification.task_id,
@@ -16127,7 +16425,8 @@ function handleTaskNotification(turn, message) {
16127
16425
  usage: notification.usage ?? previous?.usage,
16128
16426
  subagentType: previous?.subagentType,
16129
16427
  ...carriedTaskMetadata(previous),
16130
- ...taskMessageMetadata(notification)
16428
+ ...taskMessageMetadata(notification),
16429
+ ...orphanScan === "cleanup" ? { endReason: "resume_cleanup" } : {}
16131
16430
  };
16132
16431
  Object.assign(task, takeShellTaskFields(session, task.toolUseId), task.error ? { error: task.error } : {});
16133
16432
  session.backgroundTasks = boundBackgroundTaskHistory({
@@ -17440,6 +17739,58 @@ config.get("/models", async (c) => {
17440
17739
  });
17441
17740
  var config_default = config;
17442
17741
 
17742
+ // ../protocol/src/human-prompts.ts
17743
+ var HUMAN_PROMPT_SCAN_BYTES = 16 * 1024;
17744
+ function humanPromptObservation(messages, tasks) {
17745
+ const object = (value) => !!value && typeof value === "object" && !Array.isArray(value);
17746
+ const parts = [];
17747
+ let remaining = 64 * 1024;
17748
+ let visited = 0;
17749
+ const walk = (value, depth = 0) => {
17750
+ if (!Array.isArray(value) || depth > 8)
17751
+ return;
17752
+ for (const part of value) {
17753
+ if (++visited > 2048 || remaining <= 0 || parts.length >= 32)
17754
+ return;
17755
+ if (!object(part))
17756
+ continue;
17757
+ if (typeof part.toolOutput === "string" && (part.toolState === "pending" || part.toolOutput.includes("Output is being written to:"))) {
17758
+ const output = part.toolOutput.slice(-Math.min(HUMAN_PROMPT_SCAN_BYTES, remaining));
17759
+ remaining -= output.length;
17760
+ const args = object(part.toolArgs) ? part.toolArgs : {};
17761
+ const command = args.command ?? args.cmd;
17762
+ parts.push({
17763
+ toolUseId: String(part.toolUseId ?? part.id ?? "").slice(0, 200),
17764
+ toolName: String(part.toolName ?? "").slice(0, 100),
17765
+ toolState: part.toolState,
17766
+ toolOutput: output,
17767
+ ...typeof command === "string" ? { toolArgs: { command: command.slice(0, 500) } } : {}
17768
+ });
17769
+ }
17770
+ walk(part.parts, depth + 1);
17771
+ walk(part.childTools, depth + 1);
17772
+ if (object(part.task))
17773
+ walk([part.task], depth + 1);
17774
+ }
17775
+ };
17776
+ if (Array.isArray(messages)) {
17777
+ for (let i = messages.length - 1;i >= Math.max(0, messages.length - 64); i--) {
17778
+ const message = messages[i];
17779
+ if (object(message))
17780
+ walk(message.parts);
17781
+ }
17782
+ }
17783
+ const taskList = Array.isArray(tasks) ? tasks : object(tasks) ? Object.values(tasks) : [];
17784
+ const backgroundTasks = taskList.filter(object).filter((task) => task.status === "running" || task.status === "pending").slice(-64).map((task) => ({
17785
+ id: String(task.id ?? "").slice(0, 200),
17786
+ status: task.status,
17787
+ kind: task.kind,
17788
+ ...typeof task.outputFile === "string" ? { outputFile: task.outputFile.slice(0, 2048) } : {},
17789
+ ...typeof task.toolUseId === "string" ? { toolUseId: task.toolUseId.slice(0, 200) } : {}
17790
+ }));
17791
+ return { messages: [{ parts }], backgroundTasks };
17792
+ }
17793
+
17443
17794
  // ../../bridges/claude-bridge/src/routes/session.ts
17444
17795
  import { randomUUID as randomUUID5 } from "node:crypto";
17445
17796
 
@@ -17852,7 +18203,7 @@ sessionActivityBatch.post(SESSION_ACTIVITY_BATCH_PATH, async (c) => {
17852
18203
 
17853
18204
  // ../protocol/src/node/bridge-transcript-summary.ts
17854
18205
  import { Buffer as Buffer3 } from "node:buffer";
17855
- import { createHash as createHash11 } from "node:crypto";
18206
+ import { createHash as createHash12 } from "node:crypto";
17856
18207
 
17857
18208
  // ../protocol/src/node/progressive-transcript.ts
17858
18209
  var BRIDGE_TRANSCRIPT_MAX_MESSAGES = 100;
@@ -18018,7 +18369,7 @@ function digestPayload(payload) {
18018
18369
  const serialized = JSON.stringify(payload);
18019
18370
  return {
18020
18371
  bytes: Buffer3.byteLength(serialized),
18021
- digest: createHash11("sha256").update(serialized).digest("base64url").slice(0, 32)
18372
+ digest: createHash12("sha256").update(serialized).digest("base64url").slice(0, 32)
18022
18373
  };
18023
18374
  }
18024
18375
  function topLevelKey(part, index) {
@@ -18417,7 +18768,6 @@ function registerSessionTranscriptRoutes(app, generation, materialize) {
18417
18768
  // ../../bridges/claude-bridge/src/routes/session.ts
18418
18769
  var session = new Hono2;
18419
18770
  var TRANSCRIPT_GENERATION = randomUUID5();
18420
- var MAX_IMAGE_ATTACHMENT_BYTES2 = 8 * 1024 * 1024;
18421
18771
  var MAX_QUESTION_ANSWER_REQUEST_BYTES = AGENT_INTERACTION_LIMITS.maxSerializedPayloadBytes + 1024;
18422
18772
  var MAX_CLAUDE_TRANSCRIPT_RESPONSE_BYTES = 16 * 1024 * 1024;
18423
18773
  function boundClaudeTranscriptResponse(messages, tailMessages) {
@@ -18470,13 +18820,7 @@ function errorMessage(error, fallback) {
18470
18820
  return error instanceof Error && error.message ? error.message : fallback;
18471
18821
  }
18472
18822
  function isValidImageDataUrl(value) {
18473
- const match = /^data:image\/(?:jpeg|png|gif|webp);base64,([\s\S]+)$/.exec(value);
18474
- if (!match)
18475
- return false;
18476
- const data = match[1].replace(/\s+/g, "");
18477
- const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
18478
- const decodedBytes = data.length % 4 === 0 ? data.length / 4 * 3 - padding : Number.POSITIVE_INFINITY;
18479
- return data.length > 0 && data.length % 4 === 0 && /^[A-Za-z0-9+/]+={0,2}$/.test(data) && decodedBytes <= MAX_IMAGE_ATTACHMENT_BYTES2;
18823
+ return parseBase64ImageData(value)?.mediaType !== undefined;
18480
18824
  }
18481
18825
  function isBoundedClaudeQuestionAnswers(value, questionCount) {
18482
18826
  if (!Array.isArray(value) || value.length !== questionCount)
@@ -19119,6 +19463,10 @@ session.post("/:id/tasks/:taskId/stop", async (c) => {
19119
19463
  return c.json({ error: errorMessage(error, "Failed to stop background task") }, sessionErrorStatus(error));
19120
19464
  }
19121
19465
  });
19466
+ session.get("/:id/human-prompts", (c) => {
19467
+ const state = peekSession(c.req.param("id"));
19468
+ return c.json(humanPromptObservation(state?.messages, state?.backgroundTasks));
19469
+ });
19122
19470
  session.get("/:id/activity", async (c) => {
19123
19471
  return c.json(await readSessionActivityObservation(c.req.param("id")));
19124
19472
  });
@@ -21465,7 +21813,7 @@ app.onError((error, c) => {
21465
21813
  var BRIDGE_TOKEN_ENV = "CLAUDE_BRIDGE_TOKEN";
21466
21814
  var BRIDGE_ALLOWED_ORIGINS_ENV = "CLAUDE_BRIDGE_ALLOWED_ORIGINS";
21467
21815
  var configuredBridgeAuthToken = process.env[BRIDGE_TOKEN_ENV]?.trim();
21468
- var bridgeAuthToken = configuredBridgeAuthToken || randomBytes(32).toString("base64url");
21816
+ var bridgeAuthToken = configuredBridgeAuthToken || randomBytes2(32).toString("base64url");
21469
21817
  delete process.env[BRIDGE_TOKEN_ENV];
21470
21818
  process.env.ORKESTRATOR_NATIVE_AGENT_BRIDGE = "claude";
21471
21819
  var bridgeAuthEnabledOverrideForTesting = null;