orkestrator 2.30.0-beta → 2.32.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.
- package/README.md +15 -3
- package/dist/client.js +1728 -158
- package/dist/main.js +227795 -217168
- package/package.json +3 -3
- package/resources/claude-bridge/dist/index.js +1029 -682
- package/resources/codex-bridge/dist/index.js +474 -150
|
@@ -1866,7 +1866,14 @@ var QUESTION_KEYS = new Set([
|
|
|
1866
1866
|
"allowFreeText",
|
|
1867
1867
|
"options"
|
|
1868
1868
|
]);
|
|
1869
|
-
var OPTION_KEYS = new Set([
|
|
1869
|
+
var OPTION_KEYS = new Set([
|
|
1870
|
+
"id",
|
|
1871
|
+
"label",
|
|
1872
|
+
"providerValue",
|
|
1873
|
+
"description",
|
|
1874
|
+
"preview",
|
|
1875
|
+
"amendment"
|
|
1876
|
+
]);
|
|
1870
1877
|
var AMENDMENT_KEYS = new Set(["kind", "detail"]);
|
|
1871
1878
|
var ANSWER_KEYS = new Set(["version", "interactionId", "sessionId", "answers"]);
|
|
1872
1879
|
var QUESTION_ANSWER_KEYS = new Set(["questionId", "optionIds", "freeText"]);
|
|
@@ -2522,6 +2529,10 @@ function claudeHome() {
|
|
|
2522
2529
|
function claudeJsonPath() {
|
|
2523
2530
|
return join(claudeHome(), ".claude.json");
|
|
2524
2531
|
}
|
|
2532
|
+
function claudeUserSettingsPath() {
|
|
2533
|
+
const configDir = overrideHome === null ? process.env.CLAUDE_CONFIG_DIR?.trim() : undefined;
|
|
2534
|
+
return join(configDir || join(claudeHome(), ".claude"), "settings.json");
|
|
2535
|
+
}
|
|
2525
2536
|
function claudePluginsDir() {
|
|
2526
2537
|
return join(claudeHome(), ".claude", "plugins");
|
|
2527
2538
|
}
|
|
@@ -2549,7 +2560,8 @@ function parseTurnInFlightMark(value) {
|
|
|
2549
2560
|
return {
|
|
2550
2561
|
bridgeInstanceId: record.bridgeInstanceId,
|
|
2551
2562
|
pid: record.pid,
|
|
2552
|
-
startedAt: record.startedAt
|
|
2563
|
+
startedAt: record.startedAt,
|
|
2564
|
+
...typeof record.requestId === "string" && record.requestId.length > 0 && record.requestId.length <= 200 ? { requestId: record.requestId } : {}
|
|
2553
2565
|
};
|
|
2554
2566
|
}
|
|
2555
2567
|
var MAX_SHELL_TASK_DETAILS = 256;
|
|
@@ -3764,7 +3776,106 @@ import { createHash as createHash5 } from "node:crypto";
|
|
|
3764
3776
|
|
|
3765
3777
|
// ../../bridges/claude-bridge/src/services/plugin-config.ts
|
|
3766
3778
|
import { readdir, access } from "node:fs/promises";
|
|
3767
|
-
import { join as join3
|
|
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);
|
|
3838
|
+
}
|
|
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);
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
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 } };
|
|
3870
|
+
}
|
|
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));
|
|
3878
|
+
}
|
|
3768
3879
|
|
|
3769
3880
|
// ../../bridges/claude-bridge/src/services/json-file-cache.ts
|
|
3770
3881
|
import { createHash as createHash4 } from "node:crypto";
|
|
@@ -3866,10 +3977,6 @@ async function pathExists(path) {
|
|
|
3866
3977
|
return false;
|
|
3867
3978
|
}
|
|
3868
3979
|
}
|
|
3869
|
-
function isPathWithin(parentDir, candidatePath) {
|
|
3870
|
-
const relativePath = relative(parentDir, candidatePath);
|
|
3871
|
-
return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
|
|
3872
|
-
}
|
|
3873
3980
|
async function scanCliPluginsDirectory(pluginsDir) {
|
|
3874
3981
|
if (!await pathExists(pluginsDir)) {
|
|
3875
3982
|
return [];
|
|
@@ -3896,11 +4003,11 @@ async function scanCliPluginsDirectory(pluginsDir) {
|
|
|
3896
4003
|
}
|
|
3897
4004
|
}
|
|
3898
4005
|
async function loadGlobalPlugins() {
|
|
3899
|
-
const plugins = await readJsonSliceCached(claudeJsonPath(), "plugins", (config) => config?.plugins);
|
|
4006
|
+
const plugins = await readJsonSliceCached(claudeJsonPath(), "plugins", (config) => claudePluginConfigList(config?.plugins));
|
|
3900
4007
|
return plugins ?? [];
|
|
3901
4008
|
}
|
|
3902
4009
|
async function loadProjectPlugins(cwd) {
|
|
3903
|
-
const plugins = await readJsonSliceCached(join3(cwd, ".claude", "plugins.json"), "plugins", (config) => config?.plugins);
|
|
4010
|
+
const plugins = await readJsonSliceCached(join3(cwd, ".claude", "plugins.json"), "plugins", (config) => claudePluginConfigList(config?.plugins));
|
|
3904
4011
|
return plugins ?? [];
|
|
3905
4012
|
}
|
|
3906
4013
|
async function loadCliInstalledPlugins() {
|
|
@@ -3910,37 +4017,30 @@ async function loadCliInstalledPlugins() {
|
|
|
3910
4017
|
if (!plugins) {
|
|
3911
4018
|
return scanCliPluginsDirectory(pluginsDir);
|
|
3912
4019
|
}
|
|
3913
|
-
return
|
|
4020
|
+
return remapInstalledPlugins2(pluginsDir, { plugins });
|
|
3914
4021
|
}
|
|
3915
|
-
function
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
if (markerIdx !== -1) {
|
|
3924
|
-
const relativePath = entry.installPath.substring(markerIdx + CLAUDE_PLUGINS_MARKER.length);
|
|
3925
|
-
const remappedPath = resolve(pluginsDir, relativePath);
|
|
3926
|
-
if (!isPathWithin(pluginsRoot, remappedPath)) {
|
|
3927
|
-
console.warn(`[plugin-config] Skipping plugin with unsafe install path: "${entry.installPath}"`);
|
|
3928
|
-
continue;
|
|
3929
|
-
}
|
|
3930
|
-
resolvedPath = remappedPath;
|
|
3931
|
-
} else {
|
|
3932
|
-
resolvedPath = entry.installPath;
|
|
3933
|
-
}
|
|
3934
|
-
plugins.push({
|
|
3935
|
-
type: "local",
|
|
3936
|
-
path: resolvedPath
|
|
3937
|
-
});
|
|
3938
|
-
}
|
|
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");
|
|
3939
4030
|
}
|
|
3940
|
-
|
|
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}"`));
|
|
3941
4041
|
}
|
|
3942
4042
|
async function loadProjectOverridesFromGlobal(cwd) {
|
|
3943
|
-
const plugins = await readJsonSliceCached(claudeJsonPath(), `projects:${cwd}:plugins`, (config) => config?.projects?.[cwd]?.plugins);
|
|
4043
|
+
const plugins = await readJsonSliceCached(claudeJsonPath(), `projects:${cwd}:plugins`, (config) => claudePluginConfigList(config?.projects?.[cwd]?.plugins));
|
|
3944
4044
|
return plugins ?? [];
|
|
3945
4045
|
}
|
|
3946
4046
|
async function readPluginManifest(pluginPath) {
|
|
@@ -3956,42 +4056,29 @@ async function readPluginManifest(pluginPath) {
|
|
|
3956
4056
|
};
|
|
3957
4057
|
}
|
|
3958
4058
|
function resolvePath(pluginPath, cwd) {
|
|
3959
|
-
|
|
3960
|
-
return join3(claudeHome(), pluginPath.slice(1));
|
|
3961
|
-
}
|
|
3962
|
-
if (pluginPath.startsWith("/")) {
|
|
3963
|
-
return pluginPath;
|
|
3964
|
-
}
|
|
3965
|
-
return join3(cwd, pluginPath);
|
|
4059
|
+
return resolveClaudePluginPath(pluginPath, cwd, claudeHome());
|
|
3966
4060
|
}
|
|
3967
|
-
|
|
3968
|
-
|
|
4061
|
+
function defaultSettingSources(includeProjectSources) {
|
|
4062
|
+
return includeProjectSources ? ["user", "project"] : ["user"];
|
|
4063
|
+
}
|
|
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([
|
|
3969
4067
|
loadGlobalPlugins(),
|
|
3970
4068
|
loadCliInstalledPlugins(),
|
|
3971
|
-
loadProjectOverridesFromGlobal(cwd),
|
|
3972
|
-
loadProjectPlugins(cwd)
|
|
4069
|
+
includeProjectSources ? loadProjectOverridesFromGlobal(cwd) : [],
|
|
4070
|
+
includeProjectSources ? loadProjectPlugins(cwd) : [],
|
|
4071
|
+
loadPluginSettings(cwd, sources)
|
|
3973
4072
|
]);
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
for (const plugin of projectGlobal) {
|
|
3984
|
-
const resolved = resolvePath(plugin.path, cwd);
|
|
3985
|
-
pluginMap.set(resolved, { ...plugin, path: resolved });
|
|
3986
|
-
}
|
|
3987
|
-
for (const plugin of projectLocal) {
|
|
3988
|
-
const resolved = resolvePath(plugin.path, cwd);
|
|
3989
|
-
pluginMap.set(resolved, { ...plugin, path: resolved });
|
|
3990
|
-
}
|
|
3991
|
-
return Array.from(pluginMap.values());
|
|
3992
|
-
}
|
|
3993
|
-
async function getPluginsForSdk(cwd, includeProjectSources = true) {
|
|
3994
|
-
const configs = includeProjectSources ? await getMergedPlugins(cwd) : [...await loadGlobalPlugins(), ...await loadCliInstalledPlugins()];
|
|
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 }));
|
|
4079
|
+
}
|
|
4080
|
+
async function getPluginsForSdk(cwd, includeProjectSources = true, settingSources = defaultSettingSources(includeProjectSources)) {
|
|
4081
|
+
const configs = await getMergedPlugins(cwd, includeProjectSources, settingSources);
|
|
3995
4082
|
const validPlugins = [];
|
|
3996
4083
|
for (const config of configs) {
|
|
3997
4084
|
if (await pathExists(config.path)) {
|
|
@@ -4117,90 +4204,18 @@ async function runtimeEnvironmentForAgentQuery(env = process.env, readTextFile =
|
|
|
4117
4204
|
return snapshot;
|
|
4118
4205
|
}
|
|
4119
4206
|
|
|
4120
|
-
// ../protocol/src/coordinator-web-research.ts
|
|
4121
|
-
var COORDINATOR_WEB_RESEARCH_MODES = ["off", "search", "search-and-mcp"];
|
|
4122
|
-
var DEFAULT_COORDINATOR_RESEARCH_MCP_SERVERS = Object.freeze([{ name: "context7", url: "https://mcp.context7.com/mcp" }]);
|
|
4123
|
-
var MAX_COORDINATOR_RESEARCH_MCP_SERVERS = 8;
|
|
4124
|
-
var COORDINATOR_WEB_RESEARCH_ENV = "ORKESTRATOR_COORDINATOR_WEB_RESEARCH";
|
|
4125
|
-
var COORDINATOR_RESEARCH_MCP_ENV = "ORKESTRATOR_COORDINATOR_RESEARCH_MCP";
|
|
4126
|
-
var RESERVED_SERVER_NAME = /^orkestrator/;
|
|
4127
|
-
var SERVER_NAME = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
4128
|
-
function isCoordinatorWebResearch(value) {
|
|
4129
|
-
return typeof value === "string" && COORDINATOR_WEB_RESEARCH_MODES.includes(value);
|
|
4130
|
-
}
|
|
4131
|
-
function coordinatorResearchMcpServerProblem(server) {
|
|
4132
|
-
if (!SERVER_NAME.test(server.name)) {
|
|
4133
|
-
return "Use 1 to 32 lowercase letters, digits or hyphens for the name.";
|
|
4134
|
-
}
|
|
4135
|
-
if (RESERVED_SERVER_NAME.test(server.name))
|
|
4136
|
-
return "Names starting with orkestrator are reserved.";
|
|
4137
|
-
let url;
|
|
4138
|
-
try {
|
|
4139
|
-
url = new URL(server.url);
|
|
4140
|
-
} catch {
|
|
4141
|
-
return "The URL is not valid.";
|
|
4142
|
-
}
|
|
4143
|
-
if (url.protocol !== "https:")
|
|
4144
|
-
return "Only https:// research servers are allowed.";
|
|
4145
|
-
if (url.username || url.password)
|
|
4146
|
-
return "Credentials in the URL are not allowed.";
|
|
4147
|
-
return;
|
|
4148
|
-
}
|
|
4149
|
-
function normalizeCoordinatorResearchMcpServers(value) {
|
|
4150
|
-
if (value === undefined)
|
|
4151
|
-
return DEFAULT_COORDINATOR_RESEARCH_MCP_SERVERS.map((s) => ({ ...s }));
|
|
4152
|
-
if (!Array.isArray(value))
|
|
4153
|
-
return [];
|
|
4154
|
-
const servers = [];
|
|
4155
|
-
const names = new Set;
|
|
4156
|
-
for (const entry of value) {
|
|
4157
|
-
if (servers.length >= MAX_COORDINATOR_RESEARCH_MCP_SERVERS)
|
|
4158
|
-
break;
|
|
4159
|
-
if (!entry || typeof entry !== "object")
|
|
4160
|
-
continue;
|
|
4161
|
-
const { name, url } = entry;
|
|
4162
|
-
if (typeof name !== "string" || typeof url !== "string")
|
|
4163
|
-
continue;
|
|
4164
|
-
const server = { name: name.trim(), url: url.trim() };
|
|
4165
|
-
if (coordinatorResearchMcpServerProblem(server) || names.has(server.name))
|
|
4166
|
-
continue;
|
|
4167
|
-
names.add(server.name);
|
|
4168
|
-
servers.push(server);
|
|
4169
|
-
}
|
|
4170
|
-
return servers;
|
|
4171
|
-
}
|
|
4172
|
-
function coordinatorWebResearchFromEnv(env) {
|
|
4173
|
-
const value = env[COORDINATOR_WEB_RESEARCH_ENV];
|
|
4174
|
-
return isCoordinatorWebResearch(value) ? value : "off";
|
|
4175
|
-
}
|
|
4176
|
-
function coordinatorResearchMcpServersFromEnv(env) {
|
|
4177
|
-
if (coordinatorWebResearchFromEnv(env) !== "search-and-mcp")
|
|
4178
|
-
return [];
|
|
4179
|
-
const raw = env[COORDINATOR_RESEARCH_MCP_ENV];
|
|
4180
|
-
if (!raw)
|
|
4181
|
-
return [];
|
|
4182
|
-
try {
|
|
4183
|
-
return normalizeCoordinatorResearchMcpServers(JSON.parse(raw));
|
|
4184
|
-
} catch {
|
|
4185
|
-
return [];
|
|
4186
|
-
}
|
|
4187
|
-
}
|
|
4188
|
-
var COORDINATOR_DENIED = Object.freeze([
|
|
4189
|
-
"file.write",
|
|
4190
|
-
"file.patch",
|
|
4191
|
-
"shell.mutate",
|
|
4192
|
-
"network"
|
|
4193
|
-
]);
|
|
4194
|
-
function coordinatorCapabilityPolicy(mode) {
|
|
4195
|
-
return {
|
|
4196
|
-
deny: [...COORDINATOR_DENIED],
|
|
4197
|
-
...mode === "off" ? {} : { allow: ["web.search"] }
|
|
4198
|
-
};
|
|
4199
|
-
}
|
|
4200
|
-
|
|
4201
4207
|
// ../protocol/src/native-agent-transcript-patch.ts
|
|
4202
4208
|
var FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
4203
4209
|
|
|
4210
|
+
// ../protocol/src/native-agent-policy-effects.ts
|
|
4211
|
+
function appendNativeAgentPolicyNote(policy, note) {
|
|
4212
|
+
return { ...policy, note: policy.note ? `${policy.note} ${note}` : note };
|
|
4213
|
+
}
|
|
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);
|
|
4217
|
+
}
|
|
4218
|
+
|
|
4204
4219
|
// ../protocol/src/native-agent-steer-rejection.ts
|
|
4205
4220
|
var NATIVE_AGENT_STEER_REJECTION_HTTP_STATUS = 429;
|
|
4206
4221
|
var MAX_STEER_JOURNAL_BYTES = 1024 * 1024 * 1024;
|
|
@@ -4263,6 +4278,15 @@ var NATIVE_AGENT_APPROVAL_MODES = Object.freeze({
|
|
|
4263
4278
|
}
|
|
4264
4279
|
]
|
|
4265
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"
|
|
4288
|
+
}
|
|
4289
|
+
});
|
|
4266
4290
|
var DISPATCH_OUTCOME_LOST_KIND = "dispatch-outcome-lost";
|
|
4267
4291
|
var NATIVE_AGENT_RETIRED_DISPATCH_TTL_MS = 24 * 60 * 60 * 1000;
|
|
4268
4292
|
var SUPPRESSED_COMPOSER_PARAMETERS = Object.freeze({
|
|
@@ -4414,6 +4438,87 @@ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
|
|
|
4414
4438
|
"slashCommandCatalogue"
|
|
4415
4439
|
]);
|
|
4416
4440
|
|
|
4441
|
+
// ../protocol/src/coordinator-web-research.ts
|
|
4442
|
+
var COORDINATOR_WEB_RESEARCH_MODES = ["off", "search", "search-and-mcp"];
|
|
4443
|
+
var DEFAULT_COORDINATOR_RESEARCH_MCP_SERVERS = Object.freeze([{ name: "context7", url: "https://mcp.context7.com/mcp" }]);
|
|
4444
|
+
var MAX_COORDINATOR_RESEARCH_MCP_SERVERS = 8;
|
|
4445
|
+
var COORDINATOR_WEB_RESEARCH_ENV = "ORKESTRATOR_COORDINATOR_WEB_RESEARCH";
|
|
4446
|
+
var COORDINATOR_RESEARCH_MCP_ENV = "ORKESTRATOR_COORDINATOR_RESEARCH_MCP";
|
|
4447
|
+
var RESERVED_SERVER_NAME = /^orkestrator/;
|
|
4448
|
+
var SERVER_NAME = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
4449
|
+
function isCoordinatorWebResearch(value) {
|
|
4450
|
+
return typeof value === "string" && COORDINATOR_WEB_RESEARCH_MODES.includes(value);
|
|
4451
|
+
}
|
|
4452
|
+
function coordinatorResearchMcpServerProblem(server) {
|
|
4453
|
+
if (!SERVER_NAME.test(server.name)) {
|
|
4454
|
+
return "Use 1 to 32 lowercase letters, digits or hyphens for the name.";
|
|
4455
|
+
}
|
|
4456
|
+
if (RESERVED_SERVER_NAME.test(server.name))
|
|
4457
|
+
return "Names starting with orkestrator are reserved.";
|
|
4458
|
+
let url;
|
|
4459
|
+
try {
|
|
4460
|
+
url = new URL(server.url);
|
|
4461
|
+
} catch {
|
|
4462
|
+
return "The URL is not valid.";
|
|
4463
|
+
}
|
|
4464
|
+
if (url.protocol !== "https:")
|
|
4465
|
+
return "Only https:// research servers are allowed.";
|
|
4466
|
+
if (url.username || url.password)
|
|
4467
|
+
return "Credentials in the URL are not allowed.";
|
|
4468
|
+
return;
|
|
4469
|
+
}
|
|
4470
|
+
function normalizeCoordinatorResearchMcpServers(value) {
|
|
4471
|
+
if (value === undefined)
|
|
4472
|
+
return DEFAULT_COORDINATOR_RESEARCH_MCP_SERVERS.map((s) => ({ ...s }));
|
|
4473
|
+
if (!Array.isArray(value))
|
|
4474
|
+
return [];
|
|
4475
|
+
const servers = [];
|
|
4476
|
+
const names = new Set;
|
|
4477
|
+
for (const entry of value) {
|
|
4478
|
+
if (servers.length >= MAX_COORDINATOR_RESEARCH_MCP_SERVERS)
|
|
4479
|
+
break;
|
|
4480
|
+
if (!entry || typeof entry !== "object")
|
|
4481
|
+
continue;
|
|
4482
|
+
const { name, url } = entry;
|
|
4483
|
+
if (typeof name !== "string" || typeof url !== "string")
|
|
4484
|
+
continue;
|
|
4485
|
+
const server = { name: name.trim(), url: url.trim() };
|
|
4486
|
+
if (coordinatorResearchMcpServerProblem(server) || names.has(server.name))
|
|
4487
|
+
continue;
|
|
4488
|
+
names.add(server.name);
|
|
4489
|
+
servers.push(server);
|
|
4490
|
+
}
|
|
4491
|
+
return servers;
|
|
4492
|
+
}
|
|
4493
|
+
function coordinatorWebResearchFromEnv(env) {
|
|
4494
|
+
const value = env[COORDINATOR_WEB_RESEARCH_ENV];
|
|
4495
|
+
return isCoordinatorWebResearch(value) ? value : "off";
|
|
4496
|
+
}
|
|
4497
|
+
function coordinatorResearchMcpServersFromEnv(env) {
|
|
4498
|
+
if (coordinatorWebResearchFromEnv(env) !== "search-and-mcp")
|
|
4499
|
+
return [];
|
|
4500
|
+
const raw = env[COORDINATOR_RESEARCH_MCP_ENV];
|
|
4501
|
+
if (!raw)
|
|
4502
|
+
return [];
|
|
4503
|
+
try {
|
|
4504
|
+
return normalizeCoordinatorResearchMcpServers(JSON.parse(raw));
|
|
4505
|
+
} catch {
|
|
4506
|
+
return [];
|
|
4507
|
+
}
|
|
4508
|
+
}
|
|
4509
|
+
var COORDINATOR_DENIED = Object.freeze([
|
|
4510
|
+
"file.write",
|
|
4511
|
+
"file.patch",
|
|
4512
|
+
"shell.mutate",
|
|
4513
|
+
"network"
|
|
4514
|
+
]);
|
|
4515
|
+
function coordinatorCapabilityPolicy(mode) {
|
|
4516
|
+
return {
|
|
4517
|
+
deny: [...COORDINATOR_DENIED],
|
|
4518
|
+
...mode === "off" ? {} : { allow: ["web.search"] }
|
|
4519
|
+
};
|
|
4520
|
+
}
|
|
4521
|
+
|
|
4417
4522
|
// ../../bridges/claude-bridge/src/services/mcp-tool-prefix.ts
|
|
4418
4523
|
function claudeMcpToolPrefix(name) {
|
|
4419
4524
|
let normalized = name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
@@ -4684,7 +4789,7 @@ function claudeReadOnlyAllowedTools(options) {
|
|
|
4684
4789
|
}
|
|
4685
4790
|
function claudeReadOnlySandbox(policy, options = {}) {
|
|
4686
4791
|
return {
|
|
4687
|
-
enabled:
|
|
4792
|
+
enabled: options.commandSandbox !== "unavailable",
|
|
4688
4793
|
failIfUnavailable: options.commandSandbox !== "unavailable",
|
|
4689
4794
|
autoAllowBashIfSandboxed: true,
|
|
4690
4795
|
allowUnsandboxedCommands: false,
|
|
@@ -4882,6 +4987,14 @@ function claudeWorkspaceCwd() {
|
|
|
4882
4987
|
function claudeTurnPolicy(sessionPolicy, readOnly) {
|
|
4883
4988
|
return effectiveExecutionPolicy(readOnly ? structuredClone(READ_ONLY_TURN_POLICY) : sessionPolicy);
|
|
4884
4989
|
}
|
|
4990
|
+
function claudeReportedPolicy(session) {
|
|
4991
|
+
if (session.status === "running" && session.commandDiscoveryInputs?.readOnly === true) {
|
|
4992
|
+
const turnPolicy = claudeTurnPolicy(session.executionPolicy, true);
|
|
4993
|
+
if (turnPolicy)
|
|
4994
|
+
return readOnlyTurnReportedPolicy(turnPolicy);
|
|
4995
|
+
}
|
|
4996
|
+
return session.executionPolicy;
|
|
4997
|
+
}
|
|
4885
4998
|
function claudePolicyPermissionMode(policy) {
|
|
4886
4999
|
return isRestrictivePolicy(policy) ? "default" : "bypassPermissions";
|
|
4887
5000
|
}
|
|
@@ -4924,7 +5037,7 @@ async function resolveClaudeDiscoveryConfig(sessionPolicy, inputs) {
|
|
|
4924
5037
|
const policy = claudeTurnPolicy(sessionPolicy, inputs.readOnly);
|
|
4925
5038
|
const cwd = claudeWorkspaceCwd();
|
|
4926
5039
|
const settingSources = claudeSettingSources(policy, inputs.includeLocalSettings);
|
|
4927
|
-
const plugins = await getPluginsForSdk(cwd, policy?.projectResources !== false);
|
|
5040
|
+
const plugins = await getPluginsForSdk(cwd, policy?.projectResources !== false, settingSources);
|
|
4928
5041
|
return {
|
|
4929
5042
|
cwd,
|
|
4930
5043
|
settingSources,
|
|
@@ -6813,21 +6926,31 @@ function turnInFlight(session) {
|
|
|
6813
6926
|
function syncTurnInFlightMark(session, options = {}) {
|
|
6814
6927
|
if (session.deleting)
|
|
6815
6928
|
return;
|
|
6929
|
+
if (options.started)
|
|
6930
|
+
session.turnInFlightRequestId = options.requestId;
|
|
6816
6931
|
writeTurnInFlightMark(session, options.stopped !== true && turnInFlight(session), options.started);
|
|
6817
6932
|
}
|
|
6818
6933
|
function clearTurnInFlightMarkForClose(session) {
|
|
6819
6934
|
writeTurnInFlightMark(session, false);
|
|
6820
6935
|
}
|
|
6821
6936
|
function writeTurnInFlightMark(session, wanted, claim = false) {
|
|
6822
|
-
|
|
6937
|
+
const renamed = claim && session.turnInFlightMarkOwned === true && session.turnInFlightMarkRequestId !== session.turnInFlightRequestId;
|
|
6938
|
+
if (wanted === (session.turnInFlightMarked === true) && !(wanted && claim && (!session.turnInFlightMarkOwned || renamed)))
|
|
6823
6939
|
return;
|
|
6824
6940
|
const sdkSessionId = session.sdkSessionId ?? sdkSessionIdFromBridgeId(session.id);
|
|
6825
6941
|
if (!sdkSessionId)
|
|
6826
6942
|
return;
|
|
6827
6943
|
session.turnInFlightMarked = wanted;
|
|
6828
6944
|
session.turnInFlightMarkOwned = wanted;
|
|
6945
|
+
const requestId = wanted ? session.turnInFlightRequestId : undefined;
|
|
6946
|
+
session.turnInFlightMarkRequestId = requestId;
|
|
6829
6947
|
const update = {
|
|
6830
|
-
turnInFlight: wanted ? {
|
|
6948
|
+
turnInFlight: wanted ? {
|
|
6949
|
+
bridgeInstanceId: BRIDGE_INSTANCE_ID,
|
|
6950
|
+
pid: process.pid,
|
|
6951
|
+
startedAt: Date.now(),
|
|
6952
|
+
...requestId ? { requestId } : {}
|
|
6953
|
+
} : undefined
|
|
6831
6954
|
};
|
|
6832
6955
|
updateSessionPreferences(sdkSessionId, update).catch((error) => {
|
|
6833
6956
|
debugLog("[session-manager] Failed to record the turn-in-flight mark:", error instanceof Error ? error.message : String(error));
|
|
@@ -6846,6 +6969,7 @@ function restoredTurnInterruption(preferences) {
|
|
|
6846
6969
|
status: "error",
|
|
6847
6970
|
error: CLAUDE_RESTARTED_MID_TURN_MESSAGE,
|
|
6848
6971
|
interruption: "restart",
|
|
6972
|
+
...mark.requestId ? { interruptedRequestId: mark.requestId } : {},
|
|
6849
6973
|
turnInFlightMarked: true
|
|
6850
6974
|
};
|
|
6851
6975
|
}
|
|
@@ -7385,7 +7509,6 @@ function claudeLaunchProfile(session, options) {
|
|
|
7385
7509
|
context1m: effectiveClaudeContext1m(options),
|
|
7386
7510
|
promptSuggestions: options?.promptSuggestions === true,
|
|
7387
7511
|
chrome: effectiveClaudeChrome(policy, options),
|
|
7388
|
-
maxBudgetUsd: typeof options?.maxBudgetUsd === "number" && options.maxBudgetUsd > 0 ? options.maxBudgetUsd : null,
|
|
7389
7512
|
thinking: typeof options?.parameterValues?.thinking === "string" ? options.parameterValues.thinking : "adaptive",
|
|
7390
7513
|
bypassCapable: claudePolicyPermissionMode(policy) === "bypassPermissions",
|
|
7391
7514
|
cwd: claudeWorkspaceCwd(),
|
|
@@ -7421,6 +7544,13 @@ function claudeLaunchedProfile(session, options) {
|
|
|
7421
7544
|
settingSources: JSON.stringify(claudeSettingSources(claudeTurnPolicy(session.executionPolicy, options?.readOnly), options?.includeLocalSettings))
|
|
7422
7545
|
};
|
|
7423
7546
|
}
|
|
7547
|
+
function claudeTurnSettingSources(session, options) {
|
|
7548
|
+
const policy = claudeTurnPolicy(session.executionPolicy, options?.readOnly);
|
|
7549
|
+
const running = openClaudeRuntime(session);
|
|
7550
|
+
if (options?.includeLocalSettings === undefined && running?.launch.settingSources !== undefined && !claudeLaunchConflict(running.launch, claudeLaunchProfile(session, options)))
|
|
7551
|
+
return JSON.parse(running.launch.settingSources);
|
|
7552
|
+
return claudeSettingSources(policy, options?.includeLocalSettings);
|
|
7553
|
+
}
|
|
7424
7554
|
function assertClaudeRuntimeStaticAccepts(session, options) {
|
|
7425
7555
|
const runtime = openClaudeRuntime(session);
|
|
7426
7556
|
if (!runtime || !claudeRuntimeOwesWork(runtime))
|
|
@@ -7442,7 +7572,7 @@ async function assertClaudeRuntimeAccepts(session, options, resources) {
|
|
|
7442
7572
|
if (!resources) {
|
|
7443
7573
|
const policy = claudeTurnPolicy(session.executionPolicy, options?.readOnly);
|
|
7444
7574
|
const [plugins, environment] = await Promise.all([
|
|
7445
|
-
getPluginsForSdk(request.cwd, policy?.projectResources !== false),
|
|
7575
|
+
getPluginsForSdk(request.cwd, policy?.projectResources !== false, claudeTurnSettingSources(session, options)),
|
|
7446
7576
|
runtimeEnvironmentForAgentQuery()
|
|
7447
7577
|
]);
|
|
7448
7578
|
resources = claudeLaunchResourceProfile(plugins, environment);
|
|
@@ -7487,8 +7617,6 @@ function claudeRuntimeOwesWork(runtime) {
|
|
|
7487
7617
|
function claudeLaunchConflict(runtime, request) {
|
|
7488
7618
|
if (runtime.chrome !== request.chrome)
|
|
7489
7619
|
return "Chrome setting";
|
|
7490
|
-
if (runtime.maxBudgetUsd !== request.maxBudgetUsd)
|
|
7491
|
-
return "cost budget";
|
|
7492
7620
|
if (runtime.context1m !== request.context1m)
|
|
7493
7621
|
return "1M context setting";
|
|
7494
7622
|
if (runtime.promptSuggestions !== request.promptSuggestions)
|
|
@@ -7645,7 +7773,7 @@ function noMessages() {
|
|
|
7645
7773
|
// ../../bridges/claude-bridge/src/services/session-manager-persistence.ts
|
|
7646
7774
|
import { constants } from "node:fs";
|
|
7647
7775
|
import { lstat as lstat2, open as open2, realpath } from "node:fs/promises";
|
|
7648
|
-
import { isAbsolute as
|
|
7776
|
+
import { isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve2, sep } from "node:path";
|
|
7649
7777
|
|
|
7650
7778
|
// ../protocol/src/node/command-change-journal.ts
|
|
7651
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";
|
|
@@ -7802,7 +7930,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
|
|
|
7802
7930
|
import { rmSync } from "node:fs";
|
|
7803
7931
|
import { copyFile, lstat, mkdtemp, rm as rm3 } from "node:fs/promises";
|
|
7804
7932
|
import { tmpdir } from "node:os";
|
|
7805
|
-
import { delimiter, isAbsolute
|
|
7933
|
+
import { delimiter, isAbsolute, join as join4, resolve } from "node:path";
|
|
7806
7934
|
var DEFAULT_MAX_UNTRACKED_FILE_BYTES = 2 * 1024 * 1024;
|
|
7807
7935
|
var DEFAULT_MAX_UNTRACKED_FILES = 5000;
|
|
7808
7936
|
var DEFAULT_SLOW_SNAPSHOT_MS = 500;
|
|
@@ -8084,8 +8212,8 @@ class WorkspaceChangeProbe {
|
|
|
8084
8212
|
return;
|
|
8085
8213
|
return {
|
|
8086
8214
|
root,
|
|
8087
|
-
indexPath:
|
|
8088
|
-
objectsPath:
|
|
8215
|
+
indexPath: isAbsolute(index) ? index : resolve(cwd, index),
|
|
8216
|
+
objectsPath: isAbsolute(objects) ? objects : resolve(cwd, objects)
|
|
8089
8217
|
};
|
|
8090
8218
|
}).catch(() => {
|
|
8091
8219
|
return;
|
|
@@ -9413,14 +9541,14 @@ function record(value) {
|
|
|
9413
9541
|
function text(value) {
|
|
9414
9542
|
return typeof value === "string" ? value : undefined;
|
|
9415
9543
|
}
|
|
9416
|
-
function
|
|
9544
|
+
function path2(value) {
|
|
9417
9545
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
9418
9546
|
}
|
|
9419
9547
|
function claudeProposalEvidence(toolName, input) {
|
|
9420
9548
|
const fields = record(input);
|
|
9421
9549
|
switch (toolName) {
|
|
9422
9550
|
case "Edit": {
|
|
9423
|
-
const filePath =
|
|
9551
|
+
const filePath = path2(fields.file_path);
|
|
9424
9552
|
if (!filePath)
|
|
9425
9553
|
return [];
|
|
9426
9554
|
return [
|
|
@@ -9438,7 +9566,7 @@ function claudeProposalEvidence(toolName, input) {
|
|
|
9438
9566
|
];
|
|
9439
9567
|
}
|
|
9440
9568
|
case "MultiEdit": {
|
|
9441
|
-
const filePath =
|
|
9569
|
+
const filePath = path2(fields.file_path);
|
|
9442
9570
|
if (!filePath || !Array.isArray(fields.edits))
|
|
9443
9571
|
return [];
|
|
9444
9572
|
return fields.edits.map((edit, index) => {
|
|
@@ -9457,7 +9585,7 @@ function claudeProposalEvidence(toolName, input) {
|
|
|
9457
9585
|
});
|
|
9458
9586
|
}
|
|
9459
9587
|
case "Write": {
|
|
9460
|
-
const filePath =
|
|
9588
|
+
const filePath = path2(fields.file_path);
|
|
9461
9589
|
if (!filePath)
|
|
9462
9590
|
return [];
|
|
9463
9591
|
return [
|
|
@@ -9475,7 +9603,7 @@ function claudeProposalEvidence(toolName, input) {
|
|
|
9475
9603
|
];
|
|
9476
9604
|
}
|
|
9477
9605
|
case "NotebookEdit": {
|
|
9478
|
-
const filePath =
|
|
9606
|
+
const filePath = path2(fields.notebook_path);
|
|
9479
9607
|
if (!filePath)
|
|
9480
9608
|
return [];
|
|
9481
9609
|
const deleting = fields.edit_mode === "delete";
|
|
@@ -10446,17 +10574,17 @@ function parseBase64ImageData(value) {
|
|
|
10446
10574
|
var BRIDGE_ATTACHMENT_ROOT_ENV = "ORKESTRATOR_BRIDGE_ATTACHMENT_ROOT";
|
|
10447
10575
|
function configuredAttachmentRoot(env = process.env) {
|
|
10448
10576
|
const value = env[BRIDGE_ATTACHMENT_ROOT_ENV]?.trim();
|
|
10449
|
-
return value &&
|
|
10577
|
+
return value && isAbsolute2(value) ? resolve2(value) : undefined;
|
|
10450
10578
|
}
|
|
10451
10579
|
function attachmentContainmentRoot(filePath, cwd, extraRoot = configuredAttachmentRoot()) {
|
|
10452
|
-
if (extraRoot &&
|
|
10580
|
+
if (extraRoot && isAbsolute2(filePath) && isPathWithin2(extraRoot, resolve2(filePath))) {
|
|
10453
10581
|
return extraRoot;
|
|
10454
10582
|
}
|
|
10455
|
-
return
|
|
10583
|
+
return resolve2(cwd);
|
|
10456
10584
|
}
|
|
10457
10585
|
function isPathWithin2(rootPath, targetPath) {
|
|
10458
|
-
const childPath =
|
|
10459
|
-
return childPath === "" || childPath !== ".." && !childPath.startsWith(`..${sep}`) && !
|
|
10586
|
+
const childPath = relative(rootPath, targetPath);
|
|
10587
|
+
return childPath === "" || childPath !== ".." && !childPath.startsWith(`..${sep}`) && !isAbsolute2(childPath);
|
|
10460
10588
|
}
|
|
10461
10589
|
function attachmentErrorForFsFailure(error) {
|
|
10462
10590
|
const code = error?.code;
|
|
@@ -10469,7 +10597,7 @@ function attachmentErrorForFsFailure(error) {
|
|
|
10469
10597
|
return new ClaudeAttachmentError("attachment_read_failed", "Image attachment could not be read safely from the workspace.");
|
|
10470
10598
|
}
|
|
10471
10599
|
async function assertNoSymlinkComponents(lexicalRoot, targetPath) {
|
|
10472
|
-
const childPath =
|
|
10600
|
+
const childPath = relative(lexicalRoot, targetPath);
|
|
10473
10601
|
let currentPath = lexicalRoot;
|
|
10474
10602
|
for (const segment of childPath.split(sep).filter(Boolean)) {
|
|
10475
10603
|
currentPath = join8(currentPath, segment);
|
|
@@ -10500,7 +10628,7 @@ async function assertOpenedWorkspaceFile(targetPath, canonicalRoot, openedStats)
|
|
|
10500
10628
|
}
|
|
10501
10629
|
async function readWorkspaceImageAttachment(filePath, cwd, afterSymlinkValidation, afterCanonicalValidation, afterInitialValidation) {
|
|
10502
10630
|
const lexicalRoot = attachmentContainmentRoot(filePath, cwd);
|
|
10503
|
-
const targetPath =
|
|
10631
|
+
const targetPath = isAbsolute2(filePath) ? resolve2(filePath) : resolve2(lexicalRoot, filePath);
|
|
10504
10632
|
if (!isPathWithin2(lexicalRoot, targetPath)) {
|
|
10505
10633
|
throw new ClaudeAttachmentError("attachment_outside_workspace", "Image attachment must be contained in the current workspace.");
|
|
10506
10634
|
}
|
|
@@ -10731,57 +10859,97 @@ var MAX_TRACKED_STREAM_ROWS = 256;
|
|
|
10731
10859
|
var MAX_SUBAGENT_PARTS_PER_CALL = 128;
|
|
10732
10860
|
var sessionStreamRows = new WeakMap;
|
|
10733
10861
|
function createPromptStreamState(session, sessionId, options) {
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
if (
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
}
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
|
|
10756
|
-
|
|
10757
|
-
|
|
10862
|
+
return new PromptStream(session, sessionId, options);
|
|
10863
|
+
}
|
|
10864
|
+
function explicitParentTaskUseId(partialMessage) {
|
|
10865
|
+
return typeof partialMessage.parent_tool_use_id === "string" && partialMessage.parent_tool_use_id.length > 0 ? partialMessage.parent_tool_use_id : undefined;
|
|
10866
|
+
}
|
|
10867
|
+
function materializeEntryValue(entry) {
|
|
10868
|
+
if (entry.pendingChunks) {
|
|
10869
|
+
entry.value += entry.pendingChunks.join("");
|
|
10870
|
+
entry.pendingChunks = undefined;
|
|
10871
|
+
}
|
|
10872
|
+
}
|
|
10873
|
+
|
|
10874
|
+
class PromptStream {
|
|
10875
|
+
session;
|
|
10876
|
+
sessionId;
|
|
10877
|
+
toolTracker;
|
|
10878
|
+
taskRegistry;
|
|
10879
|
+
activeTaskIds;
|
|
10880
|
+
pendingApiRetryMessageId;
|
|
10881
|
+
flushIntervalMs;
|
|
10882
|
+
maxBlockIndex;
|
|
10883
|
+
streamEventFlushTimer = null;
|
|
10884
|
+
rows = [];
|
|
10885
|
+
sharedRows;
|
|
10886
|
+
rowByApiMessage = new Map;
|
|
10887
|
+
rowByToolUseId = new Map;
|
|
10888
|
+
currentRow = null;
|
|
10889
|
+
rowSequence = 0;
|
|
10890
|
+
parentTaskByApiMessage = new Map;
|
|
10891
|
+
currentStreamApiMessageId = null;
|
|
10892
|
+
syntheticMessageKeyCounter = 0;
|
|
10893
|
+
dirtyRows = new Set;
|
|
10894
|
+
rowOfMessage = new Map;
|
|
10895
|
+
outputSeen = false;
|
|
10896
|
+
blockTarget = null;
|
|
10897
|
+
flushOnTimer = () => {
|
|
10898
|
+
this.streamEventFlushTimer = null;
|
|
10899
|
+
this.flushStreamedAssistantMessage();
|
|
10900
|
+
};
|
|
10901
|
+
constructor(session, sessionId, options) {
|
|
10902
|
+
this.session = session;
|
|
10903
|
+
this.sessionId = sessionId;
|
|
10904
|
+
this.flushIntervalMs = options.flushIntervalMs;
|
|
10905
|
+
this.maxBlockIndex = options.maxBlockIndex;
|
|
10906
|
+
this.toolTracker = options.toolTracker ?? new ToolTracker;
|
|
10907
|
+
this.taskRegistry = session.taskRegistry ??= new TaskRegistry;
|
|
10908
|
+
this.activeTaskIds = options.activeTaskIds ?? new Set;
|
|
10909
|
+
let sharedState = sessionStreamRows.get(session);
|
|
10910
|
+
if (!sharedState || sharedState.messages !== session.messages) {
|
|
10911
|
+
sharedState = { messages: session.messages, rows: new Map };
|
|
10912
|
+
sessionStreamRows.set(session, sharedState);
|
|
10913
|
+
}
|
|
10914
|
+
this.sharedRows = sharedState.rows;
|
|
10915
|
+
}
|
|
10916
|
+
get currentAssistantMessage() {
|
|
10917
|
+
return this.currentRow?.message ?? null;
|
|
10918
|
+
}
|
|
10919
|
+
get assistantOutputSeen() {
|
|
10920
|
+
return this.outputSeen;
|
|
10921
|
+
}
|
|
10922
|
+
forgetRow(row) {
|
|
10923
|
+
if (this.dirtyRows.delete(row))
|
|
10924
|
+
this.materializeRow(row, true);
|
|
10758
10925
|
for (const apiMessageId of row.blocksByApiMessage.keys()) {
|
|
10759
|
-
if (rowByApiMessage.get(apiMessageId) !== row)
|
|
10926
|
+
if (this.rowByApiMessage.get(apiMessageId) !== row)
|
|
10760
10927
|
continue;
|
|
10761
|
-
rowByApiMessage.delete(apiMessageId);
|
|
10762
|
-
parentTaskByApiMessage.delete(apiMessageId);
|
|
10928
|
+
this.rowByApiMessage.delete(apiMessageId);
|
|
10929
|
+
this.parentTaskByApiMessage.delete(apiMessageId);
|
|
10763
10930
|
}
|
|
10764
10931
|
for (const toolUseId of row.toolUseIds) {
|
|
10765
|
-
if (rowByToolUseId.get(toolUseId) === row)
|
|
10766
|
-
rowByToolUseId.delete(toolUseId);
|
|
10932
|
+
if (this.rowByToolUseId.get(toolUseId) === row)
|
|
10933
|
+
this.rowByToolUseId.delete(toolUseId);
|
|
10767
10934
|
}
|
|
10768
10935
|
if (row.message)
|
|
10769
|
-
rowOfMessage.delete(row.message);
|
|
10770
|
-
}
|
|
10771
|
-
|
|
10936
|
+
this.rowOfMessage.delete(row.message);
|
|
10937
|
+
}
|
|
10938
|
+
createRow(key, modelId) {
|
|
10939
|
+
const { rows, sharedRows, session } = this;
|
|
10772
10940
|
const shared = sharedRows.get(key);
|
|
10773
10941
|
if (shared && (!shared.message || session.messages.includes(shared.message))) {
|
|
10774
10942
|
rows.push(shared);
|
|
10775
10943
|
if (shared.message)
|
|
10776
|
-
rowOfMessage.set(shared.message, shared);
|
|
10944
|
+
this.rowOfMessage.set(shared.message, shared);
|
|
10777
10945
|
while (rows.length > MAX_TRACKED_STREAM_ROWS)
|
|
10778
|
-
forgetRow(rows.shift());
|
|
10946
|
+
this.forgetRow(rows.shift());
|
|
10779
10947
|
for (const id of shared.toolUseIds)
|
|
10780
|
-
rowByToolUseId.set(id, shared);
|
|
10948
|
+
this.rowByToolUseId.set(id, shared);
|
|
10781
10949
|
return shared;
|
|
10782
10950
|
}
|
|
10783
10951
|
const row = {
|
|
10784
|
-
sequence: rowSequence += 1,
|
|
10952
|
+
sequence: this.rowSequence += 1,
|
|
10785
10953
|
key,
|
|
10786
10954
|
message: null,
|
|
10787
10955
|
blocksByApiMessage: new Map,
|
|
@@ -10803,41 +10971,35 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
10803
10971
|
rows.push(row);
|
|
10804
10972
|
while (rows.length > MAX_TRACKED_STREAM_ROWS) {
|
|
10805
10973
|
const oldest = rows.shift();
|
|
10806
|
-
forgetRow(oldest);
|
|
10974
|
+
this.forgetRow(oldest);
|
|
10807
10975
|
}
|
|
10808
10976
|
return row;
|
|
10809
|
-
}
|
|
10810
|
-
|
|
10811
|
-
const known = rowByApiMessage.get(messageKey);
|
|
10977
|
+
}
|
|
10978
|
+
rowForApiMessage(messageKey, opensRow, parentToolUseId, modelId) {
|
|
10979
|
+
const known = this.rowByApiMessage.get(messageKey);
|
|
10812
10980
|
if (known)
|
|
10813
10981
|
return known;
|
|
10814
10982
|
let row;
|
|
10815
10983
|
if (!opensRow) {
|
|
10816
|
-
row = (parentToolUseId ? rowByToolUseId.get(parentToolUseId) : undefined) ?? currentRow ?? undefined;
|
|
10984
|
+
row = (parentToolUseId ? this.rowByToolUseId.get(parentToolUseId) : undefined) ?? this.currentRow ?? undefined;
|
|
10817
10985
|
}
|
|
10818
10986
|
if (!row) {
|
|
10819
|
-
flushStreamedAssistantMessage();
|
|
10820
|
-
row = createRow(messageKey, modelId);
|
|
10821
|
-
currentRow = row;
|
|
10987
|
+
this.flushStreamedAssistantMessage();
|
|
10988
|
+
row = this.createRow(messageKey, modelId);
|
|
10989
|
+
this.currentRow = row;
|
|
10822
10990
|
}
|
|
10823
|
-
rowByApiMessage.set(messageKey, row);
|
|
10991
|
+
this.rowByApiMessage.set(messageKey, row);
|
|
10824
10992
|
return row;
|
|
10825
|
-
}
|
|
10826
|
-
|
|
10993
|
+
}
|
|
10994
|
+
getBlocks(row, messageKey) {
|
|
10827
10995
|
let blocks = row.blocksByApiMessage.get(messageKey);
|
|
10828
10996
|
if (!blocks) {
|
|
10829
10997
|
blocks = new Map;
|
|
10830
10998
|
row.blocksByApiMessage.set(messageKey, blocks);
|
|
10831
10999
|
}
|
|
10832
11000
|
return blocks;
|
|
10833
|
-
}
|
|
10834
|
-
|
|
10835
|
-
if (entry.pendingChunks) {
|
|
10836
|
-
entry.value += entry.pendingChunks.join("");
|
|
10837
|
-
entry.pendingChunks = undefined;
|
|
10838
|
-
}
|
|
10839
|
-
};
|
|
10840
|
-
const rebuildOrderedParts = (row) => {
|
|
11001
|
+
}
|
|
11002
|
+
rebuildOrderedParts(row) {
|
|
10841
11003
|
const located = [];
|
|
10842
11004
|
for (const [messageKey, blocks] of row.blocksByApiMessage) {
|
|
10843
11005
|
const blockIndices = Array.from(blocks.keys()).sort((a, b) => a - b);
|
|
@@ -10864,17 +11026,14 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
10864
11026
|
blocks.delete(blockIndex);
|
|
10865
11027
|
if (entry.type === "tool-ref") {
|
|
10866
11028
|
row.toolUseIds.delete(entry.value);
|
|
10867
|
-
if (rowByToolUseId.get(entry.value) === row)
|
|
10868
|
-
rowByToolUseId.delete(entry.value);
|
|
11029
|
+
if (this.rowByToolUseId.get(entry.value) === row)
|
|
11030
|
+
this.rowByToolUseId.delete(entry.value);
|
|
10869
11031
|
}
|
|
10870
11032
|
}
|
|
10871
11033
|
row.orderedParts = shed.size === 0 ? located.map(({ entry }) => entry) : located.filter(({ entry }) => !shed.has(entry)).map(({ entry }) => entry);
|
|
10872
|
-
}
|
|
10873
|
-
|
|
10874
|
-
|
|
10875
|
-
let planApprovedThisTurn = false;
|
|
10876
|
-
let pendingPlanApprovalContinuation = null;
|
|
10877
|
-
const emitRow = (row) => {
|
|
11034
|
+
}
|
|
11035
|
+
emitRow(row) {
|
|
11036
|
+
const { session, sessionId } = this;
|
|
10878
11037
|
const message = row.message;
|
|
10879
11038
|
if (!message)
|
|
10880
11039
|
return;
|
|
@@ -10912,34 +11071,36 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
10912
11071
|
revision: message.revision
|
|
10913
11072
|
}
|
|
10914
11073
|
});
|
|
10915
|
-
}
|
|
10916
|
-
|
|
10917
|
-
for (let index = rows.length - 1;index >= 0; index -= 1) {
|
|
10918
|
-
const message = rows[index].message;
|
|
11074
|
+
}
|
|
11075
|
+
newestRowMessage() {
|
|
11076
|
+
for (let index = this.rows.length - 1;index >= 0; index -= 1) {
|
|
11077
|
+
const message = this.rows[index].message;
|
|
10919
11078
|
if (message)
|
|
10920
11079
|
return message;
|
|
10921
11080
|
}
|
|
10922
11081
|
return null;
|
|
10923
|
-
}
|
|
10924
|
-
|
|
10925
|
-
const
|
|
11082
|
+
}
|
|
11083
|
+
insertAfterAnchor(row, message) {
|
|
11084
|
+
const messages = this.session.messages;
|
|
11085
|
+
const anchorIndex = row.anchor ? messages.lastIndexOf(row.anchor) : -1;
|
|
10926
11086
|
if (anchorIndex < 0) {
|
|
10927
|
-
|
|
11087
|
+
messages.push(message);
|
|
10928
11088
|
return;
|
|
10929
11089
|
}
|
|
10930
11090
|
let index = anchorIndex + 1;
|
|
10931
|
-
while (index <
|
|
10932
|
-
const owner = rowOfMessage.get(
|
|
11091
|
+
while (index < messages.length) {
|
|
11092
|
+
const owner = this.rowOfMessage.get(messages[index]);
|
|
10933
11093
|
if (!owner || owner.sequence > row.sequence)
|
|
10934
11094
|
break;
|
|
10935
11095
|
index += 1;
|
|
10936
11096
|
}
|
|
10937
|
-
|
|
10938
|
-
}
|
|
10939
|
-
|
|
11097
|
+
messages.splice(index, 0, message);
|
|
11098
|
+
}
|
|
11099
|
+
materializeRow(row, rebuildBlocks) {
|
|
11100
|
+
const { session } = this;
|
|
10940
11101
|
if (rebuildBlocks)
|
|
10941
|
-
rebuildOrderedParts(row);
|
|
10942
|
-
const parts = buildMessageParts(row.orderedParts, toolTracker);
|
|
11102
|
+
this.rebuildOrderedParts(row);
|
|
11103
|
+
const parts = buildMessageParts(row.orderedParts, this.toolTracker);
|
|
10943
11104
|
const content = getMessageTextFromParts(parts);
|
|
10944
11105
|
if (!row.message) {
|
|
10945
11106
|
if (parts.length === 0 && !content)
|
|
@@ -10953,9 +11114,9 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
10953
11114
|
...row.modelId ? { modelId: row.modelId } : {},
|
|
10954
11115
|
...row.sdkUuid ? { sdkUuid: row.sdkUuid } : {}
|
|
10955
11116
|
};
|
|
10956
|
-
rowOfMessage.set(row.message, row);
|
|
10957
|
-
insertAfterAnchor(row, row.message);
|
|
10958
|
-
|
|
11117
|
+
this.rowOfMessage.set(row.message, row);
|
|
11118
|
+
this.insertAfterAnchor(row, row.message);
|
|
11119
|
+
this.outputSeen = true;
|
|
10959
11120
|
} else {
|
|
10960
11121
|
row.message.content = content;
|
|
10961
11122
|
row.message.parts = parts;
|
|
@@ -10968,32 +11129,26 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
10968
11129
|
(session.revisedRows ??= new RecentlyRevisedRows).note(row.message.id);
|
|
10969
11130
|
}
|
|
10970
11131
|
markTranscriptChanged(session);
|
|
10971
|
-
emitRow(row);
|
|
10972
|
-
}
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
streamEventFlushTimer = null;
|
|
10977
|
-
}
|
|
10978
|
-
if (dirtyRows.size === 0)
|
|
11132
|
+
this.emitRow(row);
|
|
11133
|
+
}
|
|
11134
|
+
flushStreamedAssistantMessage() {
|
|
11135
|
+
this.clearFlushTimer();
|
|
11136
|
+
if (this.dirtyRows.size === 0)
|
|
10979
11137
|
return;
|
|
10980
|
-
const pending = [...dirtyRows].sort((a, b) => a.sequence - b.sequence);
|
|
10981
|
-
dirtyRows.clear();
|
|
11138
|
+
const pending = [...this.dirtyRows].sort((a, b) => a.sequence - b.sequence);
|
|
11139
|
+
this.dirtyRows.clear();
|
|
10982
11140
|
for (const row of pending)
|
|
10983
|
-
materializeRow(row, true);
|
|
10984
|
-
}
|
|
10985
|
-
|
|
10986
|
-
dirtyRows.add(row);
|
|
10987
|
-
streamEventFlushTimer ??= setTimeout(
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
}
|
|
10991
|
-
};
|
|
10992
|
-
const applyAssistantRecord = (record) => {
|
|
10993
|
-
const messageKey = record.apiMessageId ?? record.uuid ?? `assistant-${syntheticMessageKeyCounter += 1}`;
|
|
11141
|
+
this.materializeRow(row, true);
|
|
11142
|
+
}
|
|
11143
|
+
scheduleStreamedAssistantMessageFlush(row) {
|
|
11144
|
+
this.dirtyRows.add(row);
|
|
11145
|
+
this.streamEventFlushTimer ??= setTimeout(this.flushOnTimer, this.flushIntervalMs);
|
|
11146
|
+
}
|
|
11147
|
+
applyAssistantRecord(record) {
|
|
11148
|
+
const messageKey = record.apiMessageId ?? record.uuid ?? `assistant-${this.syntheticMessageKeyCounter += 1}`;
|
|
10994
11149
|
const opensRow = record.isRoot && record.apiMessageId !== undefined;
|
|
10995
|
-
const row = rowForApiMessage(messageKey, opensRow, record.parentToolUseId, record.modelId);
|
|
10996
|
-
const blocks = getBlocks(row, messageKey);
|
|
11150
|
+
const row = this.rowForApiMessage(messageKey, opensRow, record.parentToolUseId, record.modelId);
|
|
11151
|
+
const blocks = this.getBlocks(row, messageKey);
|
|
10997
11152
|
const blockIndexBase = row.finalizedBlockCounts.get(messageKey) ?? 0;
|
|
10998
11153
|
for (const part of record.orderedParts) {
|
|
10999
11154
|
const blockIndex = blockIndexBase + (part.blockOffset ?? 0);
|
|
@@ -11005,7 +11160,7 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
11005
11160
|
});
|
|
11006
11161
|
if (part.type === "tool-ref") {
|
|
11007
11162
|
row.toolUseIds.add(part.value);
|
|
11008
|
-
rowByToolUseId.set(part.value, row);
|
|
11163
|
+
this.rowByToolUseId.set(part.value, row);
|
|
11009
11164
|
}
|
|
11010
11165
|
}
|
|
11011
11166
|
row.finalizedBlockCounts.set(messageKey, blockIndexBase + record.contentBlockCount);
|
|
@@ -11013,15 +11168,15 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
11013
11168
|
row.modelId = record.modelId;
|
|
11014
11169
|
if (record.isRoot && record.uuid)
|
|
11015
11170
|
row.sdkUuid = record.uuid;
|
|
11016
|
-
dirtyRows.delete(row);
|
|
11017
|
-
materializeRow(row, true);
|
|
11018
|
-
}
|
|
11019
|
-
|
|
11171
|
+
this.dirtyRows.delete(row);
|
|
11172
|
+
this.materializeRow(row, true);
|
|
11173
|
+
}
|
|
11174
|
+
refreshToolRows(toolUseIds) {
|
|
11020
11175
|
const affected = new Set;
|
|
11021
|
-
if (currentRow)
|
|
11022
|
-
affected.add(currentRow);
|
|
11176
|
+
if (this.currentRow)
|
|
11177
|
+
affected.add(this.currentRow);
|
|
11023
11178
|
for (const toolUseId of toolUseIds) {
|
|
11024
|
-
const row = rowByToolUseId.get(toolUseId);
|
|
11179
|
+
const row = this.rowByToolUseId.get(toolUseId);
|
|
11025
11180
|
if (row)
|
|
11026
11181
|
affected.add(row);
|
|
11027
11182
|
}
|
|
@@ -11029,150 +11184,145 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
11029
11184
|
for (const row of [...affected].sort((a, b) => a.sequence - b.sequence)) {
|
|
11030
11185
|
if (!row.message)
|
|
11031
11186
|
continue;
|
|
11032
|
-
const rebuildBlocks = dirtyRows.delete(row);
|
|
11033
|
-
materializeRow(row, rebuildBlocks);
|
|
11187
|
+
const rebuildBlocks = this.dirtyRows.delete(row);
|
|
11188
|
+
this.materializeRow(row, rebuildBlocks);
|
|
11034
11189
|
published.add(row.message);
|
|
11035
11190
|
}
|
|
11036
11191
|
return published;
|
|
11037
|
-
}
|
|
11038
|
-
|
|
11192
|
+
}
|
|
11193
|
+
rowHoldingTool(toolUseId) {
|
|
11194
|
+
return this.rowByToolUseId.get(toolUseId)?.message ?? this.currentRow?.message ?? this.newestRowMessage();
|
|
11195
|
+
}
|
|
11196
|
+
publishRow(message) {
|
|
11197
|
+
const row = this.rowOfMessage.get(message);
|
|
11198
|
+
if (row)
|
|
11199
|
+
this.emitRow(row);
|
|
11200
|
+
}
|
|
11201
|
+
applyPartialAssistantMessage(partialMessage) {
|
|
11039
11202
|
const streamEvent = partialMessage.event;
|
|
11040
|
-
|
|
11041
|
-
|
|
11042
|
-
|
|
11043
|
-
|
|
11044
|
-
|
|
11045
|
-
|
|
11046
|
-
|
|
11047
|
-
|
|
11048
|
-
if (explicitParentTaskUseId) {
|
|
11049
|
-
parentTaskByApiMessage.set(apiMessageId, explicitParentTaskUseId);
|
|
11050
|
-
}
|
|
11051
|
-
const row = rowForApiMessage(apiMessageId, isRootAssistant, explicitParentTaskUseId, modelId);
|
|
11052
|
-
if (modelId)
|
|
11053
|
-
row.modelId = modelId;
|
|
11054
|
-
getBlocks(row, apiMessageId);
|
|
11203
|
+
switch (streamEvent?.type) {
|
|
11204
|
+
case "message_start":
|
|
11205
|
+
return this.applyMessageStart(partialMessage, streamEvent);
|
|
11206
|
+
case "message_stop":
|
|
11207
|
+
return this.applyMessageStop();
|
|
11208
|
+
case "content_block_start": {
|
|
11209
|
+
const target = this.locateStreamBlock(partialMessage, streamEvent);
|
|
11210
|
+
return target ? this.applyContentBlockStart(target, streamEvent.content_block) : false;
|
|
11055
11211
|
}
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11212
|
+
case "content_block_delta": {
|
|
11213
|
+
const target = this.locateStreamBlock(partialMessage, streamEvent);
|
|
11214
|
+
return target ? this.applyContentBlockDelta(target, streamEvent.delta) : false;
|
|
11215
|
+
}
|
|
11216
|
+
default:
|
|
11217
|
+
this.locateStreamBlock(partialMessage, streamEvent);
|
|
11218
|
+
return false;
|
|
11061
11219
|
}
|
|
11062
|
-
|
|
11063
|
-
|
|
11064
|
-
|
|
11220
|
+
}
|
|
11221
|
+
applyMessageStart(partialMessage, streamEvent) {
|
|
11222
|
+
const apiMessageId = typeof streamEvent.message?.id === "string" ? streamEvent.message.id : undefined;
|
|
11223
|
+
this.currentStreamApiMessageId = apiMessageId ?? null;
|
|
11224
|
+
const isRootAssistant = isRootAssistantRecord(partialMessage.parent_tool_use_id, partialMessage.isSidechain);
|
|
11225
|
+
const modelId = isRootAssistant ? normalizeBackendModelId(streamEvent.message?.model) : undefined;
|
|
11226
|
+
if (apiMessageId) {
|
|
11227
|
+
const parentTaskUseId = explicitParentTaskUseId(partialMessage);
|
|
11228
|
+
if (parentTaskUseId)
|
|
11229
|
+
this.parentTaskByApiMessage.set(apiMessageId, parentTaskUseId);
|
|
11230
|
+
const row = this.rowForApiMessage(apiMessageId, isRootAssistant, parentTaskUseId, modelId);
|
|
11231
|
+
if (modelId)
|
|
11232
|
+
row.modelId = modelId;
|
|
11233
|
+
this.getBlocks(row, apiMessageId);
|
|
11065
11234
|
}
|
|
11066
|
-
|
|
11067
|
-
|
|
11235
|
+
return false;
|
|
11236
|
+
}
|
|
11237
|
+
applyMessageStop() {
|
|
11238
|
+
this.currentStreamApiMessageId = null;
|
|
11239
|
+
return false;
|
|
11240
|
+
}
|
|
11241
|
+
locateStreamBlock(partialMessage, streamEvent) {
|
|
11242
|
+
const blockIndex = Number.isInteger(streamEvent?.index) && streamEvent.index >= 0 && streamEvent.index <= this.maxBlockIndex ? streamEvent.index : undefined;
|
|
11243
|
+
if (blockIndex === undefined)
|
|
11244
|
+
return null;
|
|
11245
|
+
const messageKey = this.currentStreamApiMessageId ?? (typeof partialMessage.uuid === "string" ? partialMessage.uuid : undefined);
|
|
11246
|
+
if (!messageKey)
|
|
11247
|
+
return null;
|
|
11248
|
+
const explicitParent = explicitParentTaskUseId(partialMessage);
|
|
11249
|
+
const parentTaskUseId = explicitParent ?? this.parentTaskByApiMessage.get(messageKey);
|
|
11250
|
+
if (explicitParent)
|
|
11251
|
+
this.parentTaskByApiMessage.set(messageKey, explicitParent);
|
|
11252
|
+
const row = this.rowForApiMessage(messageKey, false, parentTaskUseId);
|
|
11253
|
+
const blocks = this.getBlocks(row, messageKey);
|
|
11254
|
+
const target = this.blockTarget ??= { row, blocks, blockIndex, messageKey, parentTaskUseId };
|
|
11255
|
+
target.row = row;
|
|
11256
|
+
target.blocks = blocks;
|
|
11257
|
+
target.blockIndex = blockIndex;
|
|
11258
|
+
target.messageKey = messageKey;
|
|
11259
|
+
target.parentTaskUseId = parentTaskUseId;
|
|
11260
|
+
return target;
|
|
11261
|
+
}
|
|
11262
|
+
applyContentBlockStart(target, contentBlock) {
|
|
11263
|
+
let type;
|
|
11264
|
+
let value;
|
|
11265
|
+
if (contentBlock?.type === "text") {
|
|
11266
|
+
type = "text";
|
|
11267
|
+
value = typeof contentBlock.text === "string" ? contentBlock.text : "";
|
|
11268
|
+
} else if (contentBlock?.type === "thinking") {
|
|
11269
|
+
type = "thinking";
|
|
11270
|
+
value = typeof contentBlock.thinking === "string" ? contentBlock.thinking : "";
|
|
11271
|
+
} else {
|
|
11068
11272
|
return false;
|
|
11069
11273
|
}
|
|
11070
|
-
const
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
value: `${entry?.value ?? ""}${chunk}`,
|
|
11088
|
-
timestamp: entry?.timestamp ?? new Date().toISOString(),
|
|
11089
|
-
messageUuid: messageKey,
|
|
11090
|
-
parentTaskUseId: entry?.parentTaskUseId ?? parentTaskUseId
|
|
11091
|
-
};
|
|
11092
|
-
};
|
|
11093
|
-
if (eventType === "content_block_start") {
|
|
11094
|
-
const contentBlock = streamEvent.content_block;
|
|
11095
|
-
if (contentBlock?.type === "text") {
|
|
11096
|
-
entry = {
|
|
11097
|
-
type: "text",
|
|
11098
|
-
value: typeof contentBlock.text === "string" ? contentBlock.text : "",
|
|
11099
|
-
timestamp: entry?.timestamp ?? new Date().toISOString(),
|
|
11100
|
-
messageUuid: messageKey,
|
|
11101
|
-
parentTaskUseId
|
|
11102
|
-
};
|
|
11103
|
-
} else if (contentBlock?.type === "thinking") {
|
|
11104
|
-
entry = {
|
|
11105
|
-
type: "thinking",
|
|
11106
|
-
value: typeof contentBlock.thinking === "string" ? contentBlock.thinking : "",
|
|
11107
|
-
timestamp: entry?.timestamp ?? new Date().toISOString(),
|
|
11108
|
-
messageUuid: messageKey,
|
|
11109
|
-
parentTaskUseId
|
|
11110
|
-
};
|
|
11111
|
-
} else {
|
|
11112
|
-
return false;
|
|
11113
|
-
}
|
|
11114
|
-
} else if (eventType === "content_block_delta") {
|
|
11115
|
-
const delta = streamEvent.delta;
|
|
11116
|
-
if (delta?.type === "text_delta") {
|
|
11117
|
-
entry = appendStreamedDelta("text", typeof delta.text === "string" ? delta.text : "");
|
|
11118
|
-
} else if (delta?.type === "thinking_delta") {
|
|
11119
|
-
entry = appendStreamedDelta("thinking", typeof delta.thinking === "string" ? delta.thinking : "");
|
|
11120
|
-
} else {
|
|
11121
|
-
return false;
|
|
11122
|
-
}
|
|
11274
|
+
const existing = target.blocks.get(target.blockIndex);
|
|
11275
|
+
this.storeStreamedEntry(target, {
|
|
11276
|
+
type,
|
|
11277
|
+
value,
|
|
11278
|
+
timestamp: existing?.timestamp ?? new Date().toISOString(),
|
|
11279
|
+
messageUuid: target.messageKey,
|
|
11280
|
+
parentTaskUseId: target.parentTaskUseId
|
|
11281
|
+
});
|
|
11282
|
+
return true;
|
|
11283
|
+
}
|
|
11284
|
+
applyContentBlockDelta(target, delta) {
|
|
11285
|
+
if (delta?.type === "text_delta") {
|
|
11286
|
+
const chunk = typeof delta.text === "string" ? delta.text : "";
|
|
11287
|
+
this.storeStreamedEntry(target, this.appendStreamedDelta(target, "text", chunk));
|
|
11288
|
+
} else if (delta?.type === "thinking_delta") {
|
|
11289
|
+
const chunk = typeof delta.thinking === "string" ? delta.thinking : "";
|
|
11290
|
+
this.storeStreamedEntry(target, this.appendStreamedDelta(target, "thinking", chunk));
|
|
11123
11291
|
} else {
|
|
11124
11292
|
return false;
|
|
11125
11293
|
}
|
|
11126
|
-
entriesForMessage.set(blockIndex, entry);
|
|
11127
|
-
scheduleStreamedAssistantMessageFlush(row);
|
|
11128
11294
|
return true;
|
|
11129
|
-
}
|
|
11130
|
-
|
|
11131
|
-
get
|
|
11132
|
-
|
|
11133
|
-
|
|
11134
|
-
|
|
11135
|
-
return
|
|
11136
|
-
},
|
|
11137
|
-
toolTracker,
|
|
11138
|
-
taskRegistry,
|
|
11139
|
-
activeTaskIds,
|
|
11140
|
-
get pendingPlanRejectionFeedback() {
|
|
11141
|
-
return pendingPlanRejectionFeedback;
|
|
11142
|
-
},
|
|
11143
|
-
set pendingPlanRejectionFeedback(value) {
|
|
11144
|
-
pendingPlanRejectionFeedback = value;
|
|
11145
|
-
},
|
|
11146
|
-
get planApprovedThisTurn() {
|
|
11147
|
-
return planApprovedThisTurn;
|
|
11148
|
-
},
|
|
11149
|
-
set planApprovedThisTurn(value) {
|
|
11150
|
-
planApprovedThisTurn = value;
|
|
11151
|
-
},
|
|
11152
|
-
get pendingPlanApprovalContinuation() {
|
|
11153
|
-
return pendingPlanApprovalContinuation;
|
|
11154
|
-
},
|
|
11155
|
-
set pendingPlanApprovalContinuation(value) {
|
|
11156
|
-
pendingPlanApprovalContinuation = value;
|
|
11157
|
-
},
|
|
11158
|
-
applyAssistantRecord,
|
|
11159
|
-
refreshToolRows,
|
|
11160
|
-
rowHoldingTool: (toolUseId) => rowByToolUseId.get(toolUseId)?.message ?? currentRow?.message ?? newestRowMessage(),
|
|
11161
|
-
publishRow: (message) => {
|
|
11162
|
-
const row = rowOfMessage.get(message);
|
|
11163
|
-
if (row)
|
|
11164
|
-
emitRow(row);
|
|
11165
|
-
},
|
|
11166
|
-
flushStreamedAssistantMessage,
|
|
11167
|
-
applyPartialAssistantMessage,
|
|
11168
|
-
beginPostSteerScope: flushStreamedAssistantMessage,
|
|
11169
|
-
clearFlushTimer: () => {
|
|
11170
|
-
if (streamEventFlushTimer) {
|
|
11171
|
-
clearTimeout(streamEventFlushTimer);
|
|
11172
|
-
streamEventFlushTimer = null;
|
|
11173
|
-
}
|
|
11295
|
+
}
|
|
11296
|
+
appendStreamedDelta(target, type, chunk) {
|
|
11297
|
+
const entry = target.blocks.get(target.blockIndex);
|
|
11298
|
+
if (entry?.type === type) {
|
|
11299
|
+
entry.parentTaskUseId ??= target.parentTaskUseId;
|
|
11300
|
+
(entry.pendingChunks ??= []).push(chunk);
|
|
11301
|
+
return entry;
|
|
11174
11302
|
}
|
|
11175
|
-
|
|
11303
|
+
if (entry)
|
|
11304
|
+
materializeEntryValue(entry);
|
|
11305
|
+
return {
|
|
11306
|
+
type,
|
|
11307
|
+
value: `${entry?.value ?? ""}${chunk}`,
|
|
11308
|
+
timestamp: entry?.timestamp ?? new Date().toISOString(),
|
|
11309
|
+
messageUuid: target.messageKey,
|
|
11310
|
+
parentTaskUseId: entry?.parentTaskUseId ?? target.parentTaskUseId
|
|
11311
|
+
};
|
|
11312
|
+
}
|
|
11313
|
+
storeStreamedEntry(target, entry) {
|
|
11314
|
+
target.blocks.set(target.blockIndex, entry);
|
|
11315
|
+
this.scheduleStreamedAssistantMessageFlush(target.row);
|
|
11316
|
+
}
|
|
11317
|
+
beginPostSteerScope() {
|
|
11318
|
+
this.flushStreamedAssistantMessage();
|
|
11319
|
+
}
|
|
11320
|
+
clearFlushTimer() {
|
|
11321
|
+
if (this.streamEventFlushTimer) {
|
|
11322
|
+
clearTimeout(this.streamEventFlushTimer);
|
|
11323
|
+
this.streamEventFlushTimer = null;
|
|
11324
|
+
}
|
|
11325
|
+
}
|
|
11176
11326
|
}
|
|
11177
11327
|
|
|
11178
11328
|
// ../protocol/src/structured-output-text.ts
|
|
@@ -11711,6 +11861,7 @@ function captureTurnStartup(session) {
|
|
|
11711
11861
|
abortController: session.abortController,
|
|
11712
11862
|
error: session.error,
|
|
11713
11863
|
interruption: session.interruption,
|
|
11864
|
+
interruptedRequestId: session.interruptedRequestId,
|
|
11714
11865
|
lastActivity: session.lastActivity,
|
|
11715
11866
|
persistedMessagesLoaded: session.persistedMessagesLoaded,
|
|
11716
11867
|
structuredOutput: session.structuredOutput,
|
|
@@ -11769,7 +11920,10 @@ function createTurnState(session, sessionId, init) {
|
|
|
11769
11920
|
settlingResult: false,
|
|
11770
11921
|
localCommandOutputSeen: false,
|
|
11771
11922
|
lastThinkingTokensEmitAt: 0,
|
|
11772
|
-
compactedTokensBefore: undefined
|
|
11923
|
+
compactedTokensBefore: undefined,
|
|
11924
|
+
pendingPlanRejectionFeedback: null,
|
|
11925
|
+
planApprovedThisTurn: false,
|
|
11926
|
+
pendingPlanApprovalContinuation: null
|
|
11773
11927
|
};
|
|
11774
11928
|
const diagnostics = createBridgeDiagnostics("claude", {
|
|
11775
11929
|
id: sessionId,
|
|
@@ -11880,6 +12034,7 @@ async function applyPromptPlanMode(admission, planMode) {
|
|
|
11880
12034
|
session.turnStartedAt = beforeStartup.turnStartedAt;
|
|
11881
12035
|
session.error = beforeStartup.error;
|
|
11882
12036
|
session.interruption = beforeStartup.interruption;
|
|
12037
|
+
session.interruptedRequestId = beforeStartup.interruptedRequestId;
|
|
11883
12038
|
session.lastActivity = beforeStartup.lastActivity;
|
|
11884
12039
|
session.persistedMessagesLoaded = beforeStartup.persistedMessagesLoaded;
|
|
11885
12040
|
if (needsTranscriptHydration)
|
|
@@ -11900,6 +12055,7 @@ function beginPromptActivity(admission) {
|
|
|
11900
12055
|
const { session, sessionId } = admission;
|
|
11901
12056
|
session.error = undefined;
|
|
11902
12057
|
session.interruption = undefined;
|
|
12058
|
+
session.interruptedRequestId = undefined;
|
|
11903
12059
|
session.lastActivity = new Date;
|
|
11904
12060
|
if (session.promptSuggestion !== undefined) {
|
|
11905
12061
|
session.promptSuggestion = undefined;
|
|
@@ -12332,7 +12488,7 @@ function commandBindingRevision(parts) {
|
|
|
12332
12488
|
}
|
|
12333
12489
|
return hash.toString(16).padStart(16, "0");
|
|
12334
12490
|
}
|
|
12335
|
-
function
|
|
12491
|
+
function isRecord2(value) {
|
|
12336
12492
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
12337
12493
|
}
|
|
12338
12494
|
function boundedIdentity(value, maxBytes) {
|
|
@@ -12352,7 +12508,7 @@ function displayCommandName(name) {
|
|
|
12352
12508
|
return name.startsWith("/") || name.startsWith("$") ? name : `/${name}`;
|
|
12353
12509
|
}
|
|
12354
12510
|
function normalizeAvailability(value) {
|
|
12355
|
-
if (!
|
|
12511
|
+
if (!isRecord2(value))
|
|
12356
12512
|
return;
|
|
12357
12513
|
if (typeof value.state !== "string")
|
|
12358
12514
|
return;
|
|
@@ -12367,7 +12523,7 @@ function normalizeAvailability(value) {
|
|
|
12367
12523
|
};
|
|
12368
12524
|
}
|
|
12369
12525
|
function normalizeInputPolicy(value) {
|
|
12370
|
-
if (!
|
|
12526
|
+
if (!isRecord2(value))
|
|
12371
12527
|
return;
|
|
12372
12528
|
const policy = {};
|
|
12373
12529
|
if (value.arguments === "none" || value.arguments === "optional" || value.arguments === "required") {
|
|
@@ -12382,7 +12538,7 @@ function normalizeInputPolicy(value) {
|
|
|
12382
12538
|
return Object.keys(policy).length > 0 ? policy : undefined;
|
|
12383
12539
|
}
|
|
12384
12540
|
function normalizeCommandCataloguePayload(payload, options = {}) {
|
|
12385
|
-
const record =
|
|
12541
|
+
const record = isRecord2(payload) ? payload : {};
|
|
12386
12542
|
const enhanced = record.catalogueVersion === NATIVE_AGENT_COMMAND_CATALOGUE_VERSION;
|
|
12387
12543
|
const rows = Array.isArray(record.commands) ? record.commands : [];
|
|
12388
12544
|
const byId = new Map;
|
|
@@ -12391,7 +12547,7 @@ function normalizeCommandCataloguePayload(payload, options = {}) {
|
|
|
12391
12547
|
let aliasBudget = COMMAND_CATALOGUE_LIMITS.maxAliasesTotal;
|
|
12392
12548
|
for (const candidate of rows.slice(0, COMMAND_CATALOGUE_LIMITS.maxCommands)) {
|
|
12393
12549
|
const row = typeof candidate === "string" ? { name: candidate } : candidate;
|
|
12394
|
-
if (!
|
|
12550
|
+
if (!isRecord2(row)) {
|
|
12395
12551
|
rejected += 1;
|
|
12396
12552
|
continue;
|
|
12397
12553
|
}
|
|
@@ -12471,7 +12627,8 @@ function normalizeCommandCataloguePayload(payload, options = {}) {
|
|
|
12471
12627
|
...status ? { status } : {},
|
|
12472
12628
|
...revision !== undefined ? { revision } : {},
|
|
12473
12629
|
...generation ? { generation } : {},
|
|
12474
|
-
...freshness ? { freshness } : {}
|
|
12630
|
+
...freshness ? { freshness } : {},
|
|
12631
|
+
...enhanced && status === "stale" && record.attachPending === true ? { attachPending: true } : {}
|
|
12475
12632
|
};
|
|
12476
12633
|
}
|
|
12477
12634
|
var LEGACY_ID_PREFIX = "legacy:";
|
|
@@ -12484,7 +12641,7 @@ function commandCatalogueBytes(commands) {
|
|
|
12484
12641
|
function parseBridgeCommandInvocation(value) {
|
|
12485
12642
|
if (value === undefined || value === null)
|
|
12486
12643
|
return { ok: true };
|
|
12487
|
-
if (!
|
|
12644
|
+
if (!isRecord2(value))
|
|
12488
12645
|
return { ok: false, error: "command must be an object" };
|
|
12489
12646
|
const id = boundedIdentity(value.id, COMMAND_CATALOGUE_LIMITS.maxIdBytes);
|
|
12490
12647
|
const name = boundedIdentity(value.name, COMMAND_CATALOGUE_LIMITS.maxNameBytes);
|
|
@@ -12687,6 +12844,40 @@ function redactSecrets(value) {
|
|
|
12687
12844
|
return result;
|
|
12688
12845
|
}
|
|
12689
12846
|
|
|
12847
|
+
// ../../bridges/claude-bridge/src/services/mcp-runtime-status.ts
|
|
12848
|
+
function mcpScope(scope, name, source) {
|
|
12849
|
+
if (name === "orkestrator")
|
|
12850
|
+
return "orkestrator";
|
|
12851
|
+
if (source?.includes("plugin") || scope?.includes("plugin"))
|
|
12852
|
+
return "plugin";
|
|
12853
|
+
if (scope === "project" || scope === "local")
|
|
12854
|
+
return scope;
|
|
12855
|
+
return "user";
|
|
12856
|
+
}
|
|
12857
|
+
function mcpRuntimeState(status) {
|
|
12858
|
+
switch (status) {
|
|
12859
|
+
case "connected":
|
|
12860
|
+
case "failed":
|
|
12861
|
+
case "needs-auth":
|
|
12862
|
+
case "disabled":
|
|
12863
|
+
return status;
|
|
12864
|
+
case "pending":
|
|
12865
|
+
return "connecting";
|
|
12866
|
+
default:
|
|
12867
|
+
return "unknown";
|
|
12868
|
+
}
|
|
12869
|
+
}
|
|
12870
|
+
function initMcpServerStatus(raw) {
|
|
12871
|
+
const source = typeof raw.source === "string" ? raw.source : undefined;
|
|
12872
|
+
return {
|
|
12873
|
+
name: raw.name,
|
|
12874
|
+
status: mcpRuntimeState(raw.status),
|
|
12875
|
+
...source ? { scope: mcpScope(source, raw.name) } : {},
|
|
12876
|
+
...typeof raw.error === "string" && raw.error ? { error: raw.error } : {},
|
|
12877
|
+
...Array.isArray(raw.tools) ? { tools: raw.tools.filter((tool) => typeof tool === "string") } : {}
|
|
12878
|
+
};
|
|
12879
|
+
}
|
|
12880
|
+
|
|
12690
12881
|
// ../../bridges/claude-bridge/src/services/claude-sign-in.ts
|
|
12691
12882
|
var AUTH_CACHE_TTL_MS = 30000;
|
|
12692
12883
|
var FAILED_AUTH_CACHE_TTL_MS = 5000;
|
|
@@ -12935,15 +13126,6 @@ function createProbe() {
|
|
|
12935
13126
|
...claudeExecutableOptions()
|
|
12936
13127
|
});
|
|
12937
13128
|
}
|
|
12938
|
-
function mcpScope(scope, name) {
|
|
12939
|
-
if (name === "orkestrator")
|
|
12940
|
-
return "orkestrator";
|
|
12941
|
-
if (scope?.includes("plugin"))
|
|
12942
|
-
return "plugin";
|
|
12943
|
-
if (scope === "project" || scope === "local")
|
|
12944
|
-
return "project";
|
|
12945
|
-
return "user";
|
|
12946
|
-
}
|
|
12947
13129
|
function mcpTransport(status) {
|
|
12948
13130
|
const type = status.config?.type;
|
|
12949
13131
|
if (type === "stdio" || type === "sse" || type === "http")
|
|
@@ -12966,7 +13148,7 @@ function normalizeMcp(status) {
|
|
|
12966
13148
|
id: status.name,
|
|
12967
13149
|
name: status.serverInfo?.name || status.name,
|
|
12968
13150
|
status: mappedStatus,
|
|
12969
|
-
scope: mcpScope(status.scope, status.name),
|
|
13151
|
+
scope: mcpScope(status.scope, status.name, status.source),
|
|
12970
13152
|
...transport ? { transport } : {},
|
|
12971
13153
|
toolCount: tools.length,
|
|
12972
13154
|
...tools.length ? { tools } : {},
|
|
@@ -13003,8 +13185,12 @@ async function performSessionMcpAction(sessionId, serverId, action) {
|
|
|
13003
13185
|
const session = sessions.get(sessionId);
|
|
13004
13186
|
if (!session)
|
|
13005
13187
|
throw new Error("Session not found");
|
|
13188
|
+
const live = readableControl(session);
|
|
13189
|
+
if (!live && action !== "reconnect" && action !== "enable" && action !== "disable") {
|
|
13190
|
+
throw new Error("Claude can sign in to an MCP server only while this session's Claude is running. Send a message, then sign in again; the sign-in link appears as a card in the conversation.");
|
|
13191
|
+
}
|
|
13006
13192
|
let probe;
|
|
13007
|
-
const control =
|
|
13193
|
+
const control = live ?? (probe = createProbe());
|
|
13008
13194
|
try {
|
|
13009
13195
|
if (action === "reconnect")
|
|
13010
13196
|
await control.reconnectMcpServer?.(serverId);
|
|
@@ -13248,7 +13434,7 @@ function boundedMessage(error, fallback) {
|
|
|
13248
13434
|
const message = error instanceof Error && error.message ? error.message : fallback;
|
|
13249
13435
|
return truncateUtf8(message, MAX_MESSAGE_BYTES);
|
|
13250
13436
|
}
|
|
13251
|
-
function
|
|
13437
|
+
function isRecord3(value) {
|
|
13252
13438
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
13253
13439
|
}
|
|
13254
13440
|
function sdkRows(value) {
|
|
@@ -13256,7 +13442,7 @@ function sdkRows(value) {
|
|
|
13256
13442
|
return [];
|
|
13257
13443
|
const rows = [];
|
|
13258
13444
|
for (const candidate of value.slice(0, COMMAND_CATALOGUE_LIMITS.maxCommands)) {
|
|
13259
|
-
if (!
|
|
13445
|
+
if (!isRecord3(candidate) || typeof candidate.name !== "string" || !candidate.name)
|
|
13260
13446
|
continue;
|
|
13261
13447
|
rows.push(candidate);
|
|
13262
13448
|
}
|
|
@@ -13438,7 +13624,7 @@ function recordInitCommandInventory(session) {
|
|
|
13438
13624
|
reannotate(session);
|
|
13439
13625
|
}
|
|
13440
13626
|
function recordCommandsChanged(session, message) {
|
|
13441
|
-
if (!
|
|
13627
|
+
if (!isRecord3(message) || !Array.isArray(message.commands))
|
|
13442
13628
|
return;
|
|
13443
13629
|
applyInventory(session, "replacement", sdkRows(message.commands));
|
|
13444
13630
|
}
|
|
@@ -13587,10 +13773,10 @@ async function refreshClaudeCommandCatalogue(sessionId) {
|
|
|
13587
13773
|
else
|
|
13588
13774
|
failures.push(`${label}: ${boundedMessage(result.reason, "reload failed")}`);
|
|
13589
13775
|
}
|
|
13590
|
-
if (skills.status === "fulfilled" &&
|
|
13776
|
+
if (skills.status === "fulfilled" && isRecord3(skills.value)) {
|
|
13591
13777
|
session.commandSkillNames = sdkRows(skills.value.skills).map((skill) => skill.name);
|
|
13592
13778
|
}
|
|
13593
|
-
const errorCount = plugins.status === "fulfilled" &&
|
|
13779
|
+
const errorCount = plugins.status === "fulfilled" && isRecord3(plugins.value) ? plugins.value.error_count : undefined;
|
|
13594
13780
|
if (typeof errorCount === "number" && errorCount > 0) {
|
|
13595
13781
|
failures.push(`plugins: ${errorCount} failed to load`);
|
|
13596
13782
|
}
|
|
@@ -13961,6 +14147,132 @@ function backgroundCommandInstruction(env) {
|
|
|
13961
14147
|
// ../../bridges/claude-bridge/src/services/claude-execution-sandbox.ts
|
|
13962
14148
|
import { realpathSync } from "node:fs";
|
|
13963
14149
|
|
|
14150
|
+
// ../protocol/src/node/claude-linux-sandbox.ts
|
|
14151
|
+
import { spawnSync } from "node:child_process";
|
|
14152
|
+
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";
|
|
14154
|
+
var CLAUDE_LINUX_SANDBOX_PROGRAMS = Object.freeze(["bwrap", "socat"]);
|
|
14155
|
+
var APPARMOR_BWRAP_PROFILE = "/etc/apparmor.d/bwrap-userns-restrict";
|
|
14156
|
+
var CLAUDE_LINUX_SANDBOX_PROBE_TIMEOUT_MS = 5000;
|
|
14157
|
+
var CLAUDE_LINUX_SANDBOX_PROBE_TTL_MS = 5 * 60000;
|
|
14158
|
+
var BWRAP_ARGS = Object.freeze([
|
|
14159
|
+
"--new-session",
|
|
14160
|
+
"--die-with-parent",
|
|
14161
|
+
"--unshare-user",
|
|
14162
|
+
"--unshare-pid",
|
|
14163
|
+
"--ro-bind",
|
|
14164
|
+
"/",
|
|
14165
|
+
"/",
|
|
14166
|
+
"--dev",
|
|
14167
|
+
"/dev",
|
|
14168
|
+
"--proc",
|
|
14169
|
+
"/proc",
|
|
14170
|
+
"--"
|
|
14171
|
+
]);
|
|
14172
|
+
var NESTED_ARGS = Object.freeze([
|
|
14173
|
+
"unshare",
|
|
14174
|
+
"--user",
|
|
14175
|
+
"--map-root-user",
|
|
14176
|
+
"--pid",
|
|
14177
|
+
"--mount",
|
|
14178
|
+
"--fork",
|
|
14179
|
+
"--mount-proc",
|
|
14180
|
+
"true"
|
|
14181
|
+
]);
|
|
14182
|
+
function executableOnPath(name, path = process.env.PATH) {
|
|
14183
|
+
for (const directory of (path ?? "").split(delimiter2)) {
|
|
14184
|
+
if (!directory)
|
|
14185
|
+
continue;
|
|
14186
|
+
try {
|
|
14187
|
+
accessSync(join11(directory, name), constants2.X_OK);
|
|
14188
|
+
return true;
|
|
14189
|
+
} catch {}
|
|
14190
|
+
}
|
|
14191
|
+
return false;
|
|
14192
|
+
}
|
|
14193
|
+
function runProbeCommand(command, args) {
|
|
14194
|
+
const result = spawnSync(command, [...args], {
|
|
14195
|
+
timeout: CLAUDE_LINUX_SANDBOX_PROBE_TIMEOUT_MS,
|
|
14196
|
+
killSignal: "SIGKILL",
|
|
14197
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
14198
|
+
encoding: "utf8",
|
|
14199
|
+
maxBuffer: 64 * 1024
|
|
14200
|
+
});
|
|
14201
|
+
if (result.error)
|
|
14202
|
+
return { ok: false, stderr: result.error.message };
|
|
14203
|
+
return { ok: result.status === 0, stderr: result.stderr ?? "" };
|
|
14204
|
+
}
|
|
14205
|
+
function appArmorBwrapProfileInstalled() {
|
|
14206
|
+
try {
|
|
14207
|
+
const enabled = readFileSync3("/sys/module/apparmor/parameters/enabled", "utf8");
|
|
14208
|
+
return enabled.trim().startsWith("Y") && existsSync2(APPARMOR_BWRAP_PROFILE);
|
|
14209
|
+
} catch {
|
|
14210
|
+
return false;
|
|
14211
|
+
}
|
|
14212
|
+
}
|
|
14213
|
+
function probeDetail(stderr) {
|
|
14214
|
+
const line = stderr.split(`
|
|
14215
|
+
`).map((entry) => entry.trim()).find(Boolean) ?? "no error output";
|
|
14216
|
+
return line.length > 200 ? `${line.slice(0, 199)}…` : line;
|
|
14217
|
+
}
|
|
14218
|
+
var UNAVAILABLE = "Claude's command sandbox cannot run commands on this host";
|
|
14219
|
+
function probeClaudeLinuxSandbox(dependencies = {}) {
|
|
14220
|
+
const onPath = dependencies.onPath ?? ((name) => executableOnPath(name));
|
|
14221
|
+
const run = dependencies.run ?? runProbeCommand;
|
|
14222
|
+
const appArmorBwrapProfile = dependencies.appArmorBwrapProfile ?? appArmorBwrapProfileInstalled;
|
|
14223
|
+
const missing = CLAUDE_LINUX_SANDBOX_PROGRAMS.filter((name) => !onPath(name));
|
|
14224
|
+
if (missing.length > 0) {
|
|
14225
|
+
return {
|
|
14226
|
+
available: false,
|
|
14227
|
+
failure: "missing-programs",
|
|
14228
|
+
reason: `${UNAVAILABLE}: the Claude CLI's Linux sandbox needs bubblewrap and socat on PATH, and ${missing.join(" and ")} ${missing.length === 1 ? "is" : "are"} missing. Install the bubblewrap and socat packages.`
|
|
14229
|
+
};
|
|
14230
|
+
}
|
|
14231
|
+
if (!onPath("unshare")) {
|
|
14232
|
+
return {
|
|
14233
|
+
available: false,
|
|
14234
|
+
failure: "probe-unavailable",
|
|
14235
|
+
reason: `${UNAVAILABLE}: whether a user namespace can be nested inside bubblewrap's, which the Claude CLI needs, could not be checked because unshare (util-linux) is not on PATH.`
|
|
14236
|
+
};
|
|
14237
|
+
}
|
|
14238
|
+
const nested = run("bwrap", [...BWRAP_ARGS, ...NESTED_ARGS]);
|
|
14239
|
+
if (nested.ok)
|
|
14240
|
+
return { available: true };
|
|
14241
|
+
const outer = run("bwrap", [...BWRAP_ARGS, "true"]);
|
|
14242
|
+
if (!outer.ok) {
|
|
14243
|
+
return {
|
|
14244
|
+
available: false,
|
|
14245
|
+
failure: "user-namespace",
|
|
14246
|
+
reason: `${UNAVAILABLE}: bubblewrap cannot create a user namespace (${probeDetail(outer.stderr)}). Unprivileged user namespaces are turned off or restricted here, for example by kernel.unprivileged_userns_clone, AppArmor's kernel.apparmor_restrict_unprivileged_userns, or a container's seccomp profile.`
|
|
14247
|
+
};
|
|
14248
|
+
}
|
|
14249
|
+
const detail = probeDetail(nested.stderr);
|
|
14250
|
+
if (appArmorBwrapProfile()) {
|
|
14251
|
+
return {
|
|
14252
|
+
available: false,
|
|
14253
|
+
failure: "nested-user-namespace",
|
|
14254
|
+
reason: `${UNAVAILABLE}: bubblewrap starts, but AppArmor's bwrap-userns-restrict profile strips capabilities from the user namespace the Claude CLI nests inside it, so every sandboxed command would fail (${detail}). Allow nested namespaces for bubblewrap with a local AppArmor profile, or remove bwrap-userns-restrict on a dedicated host; Orkestrator does not change AppArmor policy itself.`
|
|
14255
|
+
};
|
|
14256
|
+
}
|
|
14257
|
+
return {
|
|
14258
|
+
available: false,
|
|
14259
|
+
failure: "nested-user-namespace",
|
|
14260
|
+
reason: `${UNAVAILABLE}: bubblewrap starts, but a user namespace nested inside it is refused (${detail}), and the Claude CLI's command helper needs one, so every sandboxed command would fail. In a container this is usually the container's seccomp profile or missing capabilities.`
|
|
14261
|
+
};
|
|
14262
|
+
}
|
|
14263
|
+
var cached;
|
|
14264
|
+
var pinned;
|
|
14265
|
+
function claudeLinuxSandboxStatus(now = Date.now()) {
|
|
14266
|
+
if (pinned)
|
|
14267
|
+
return pinned;
|
|
14268
|
+
if (cached && now - cached.at >= 0 && now - cached.at < CLAUDE_LINUX_SANDBOX_PROBE_TTL_MS) {
|
|
14269
|
+
return cached.status;
|
|
14270
|
+
}
|
|
14271
|
+
const status = probeClaudeLinuxSandbox();
|
|
14272
|
+
cached = { status, at: now };
|
|
14273
|
+
return status;
|
|
14274
|
+
}
|
|
14275
|
+
|
|
13964
14276
|
// ../protocol/src/coordinator.ts
|
|
13965
14277
|
var COORDINATOR_COMMAND_SANDBOX_ENV = "ORKESTRATOR_COORDINATOR_COMMAND_SANDBOX";
|
|
13966
14278
|
function coordinatorCommandSandboxFromEnv(env) {
|
|
@@ -13973,7 +14285,9 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
|
|
|
13973
14285
|
"adopt_workflow",
|
|
13974
14286
|
"cancel_build_pipeline",
|
|
13975
14287
|
"cancel_multi_review",
|
|
14288
|
+
"cancel_schedule",
|
|
13976
14289
|
"compare_project_files",
|
|
14290
|
+
"create_schedule",
|
|
13977
14291
|
"create_ticket",
|
|
13978
14292
|
"get_build_pipeline",
|
|
13979
14293
|
"get_environment",
|
|
@@ -13995,6 +14309,7 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
|
|
|
13995
14309
|
"list_node_environments",
|
|
13996
14310
|
"list_nodes",
|
|
13997
14311
|
"list_projects",
|
|
14312
|
+
"list_schedules",
|
|
13998
14313
|
"list_tabs",
|
|
13999
14314
|
"list_tickets",
|
|
14000
14315
|
"list_workflows",
|
|
@@ -14052,6 +14367,16 @@ function claudeExecutionSandbox(policy, asksForApproval, readOnly = {}) {
|
|
|
14052
14367
|
}
|
|
14053
14368
|
};
|
|
14054
14369
|
}
|
|
14370
|
+
function claudeSandboxRefusal(policy, options = {}) {
|
|
14371
|
+
if ((options.platform ?? process.platform) !== "linux" || !policy)
|
|
14372
|
+
return;
|
|
14373
|
+
const launcher = "commandSandbox" in options ? options.commandSandbox : process.env[COORDINATOR_COMMAND_SANDBOX_ENV];
|
|
14374
|
+
const required = isCoordinatorReadOnlyPolicy(policy) ? launcher !== undefined && launcher !== "unavailable" : policy.sandbox === "provider";
|
|
14375
|
+
if (!required)
|
|
14376
|
+
return;
|
|
14377
|
+
const status = (options.linuxSandbox ?? claudeLinuxSandboxStatus)();
|
|
14378
|
+
return status.available ? undefined : status.reason;
|
|
14379
|
+
}
|
|
14055
14380
|
|
|
14056
14381
|
// ../protocol/src/agent-interaction-deadline.ts
|
|
14057
14382
|
var SYSTEM_CLOCK = {
|
|
@@ -14813,14 +15138,19 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
|
|
|
14813
15138
|
const effortLevel = options?.effort ?? "high";
|
|
14814
15139
|
const cwd = claudeWorkspaceCwd();
|
|
14815
15140
|
const policy = claudeTurnPolicy(session.executionPolicy, options?.readOnly);
|
|
15141
|
+
const sandboxRefusal = claudeSandboxRefusal(policy);
|
|
15142
|
+
if (sandboxRefusal)
|
|
15143
|
+
throw sessionOperationError("invalid", sandboxRefusal);
|
|
14816
15144
|
session.commandDiscoveryInputs = {
|
|
14817
15145
|
readOnly: options?.readOnly === true,
|
|
14818
15146
|
includeLocalSettings: options?.includeLocalSettings === true
|
|
14819
15147
|
};
|
|
14820
15148
|
const includeProjectResources = policy?.projectResources !== false;
|
|
15149
|
+
const settingSources = claudeTurnSettingSources(session, options);
|
|
15150
|
+
session.commandDiscoveryInputs.includeLocalSettings = settingSources.includes("local");
|
|
14821
15151
|
const [{ servers: mcpServers, names: mcpServerNames, revision: mcpConfigRevision }, plugins] = await Promise.all([
|
|
14822
15152
|
getMcpRuntimeConfig(cwd, process.env, options?.agentMcp, mcpSourceScopeForPolicy(policy)),
|
|
14823
|
-
getPluginsForSdk(cwd, includeProjectResources)
|
|
15153
|
+
getPluginsForSdk(cwd, includeProjectResources, settingSources)
|
|
14824
15154
|
]);
|
|
14825
15155
|
turn.mcpServerNames = mcpServerNames;
|
|
14826
15156
|
const mcpServerCount = Object.keys(mcpServers).length;
|
|
@@ -14885,6 +15215,7 @@ async function resolveQueryPlan(turn, sdkTextPrompt, testHooks) {
|
|
|
14885
15215
|
});
|
|
14886
15216
|
const mcpServersKey = createHash10("sha256").update(JSON.stringify(mcpServers)).digest("hex");
|
|
14887
15217
|
return {
|
|
15218
|
+
settingSources,
|
|
14888
15219
|
cwd,
|
|
14889
15220
|
policy,
|
|
14890
15221
|
effortLevel,
|
|
@@ -14968,7 +15299,6 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
|
|
|
14968
15299
|
} : { type: "adaptive", display: "summarized" },
|
|
14969
15300
|
...effectiveClaudeContext1m(options) ? { betas: ["context-1m-2025-08-07"] } : {},
|
|
14970
15301
|
extraArgs: effectiveClaudeChrome(policy, options) ? { chrome: null } : { "no-chrome": null },
|
|
14971
|
-
...typeof options?.maxBudgetUsd === "number" && options.maxBudgetUsd > 0 ? { maxBudgetUsd: options.maxBudgetUsd } : {},
|
|
14972
15302
|
includePartialMessages: true,
|
|
14973
15303
|
forwardSubagentText: true,
|
|
14974
15304
|
allowedTools,
|
|
@@ -14996,7 +15326,7 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
|
|
|
14996
15326
|
|
|
14997
15327
|
`)
|
|
14998
15328
|
},
|
|
14999
|
-
settingSources:
|
|
15329
|
+
settingSources: plan.settingSources,
|
|
15000
15330
|
...(fastMode || ultracode) && {
|
|
15001
15331
|
settings: {
|
|
15002
15332
|
...fastMode && { fastMode: true },
|
|
@@ -15022,6 +15352,7 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
|
|
|
15022
15352
|
PostCompact: [{ hooks: [latestTurnHook((target) => target.recordPostCompact)] }]
|
|
15023
15353
|
},
|
|
15024
15354
|
includeHookEvents: true,
|
|
15355
|
+
toolConfig: { askUserQuestion: { previewFormat: "markdown" } },
|
|
15025
15356
|
canUseTool: (toolName, input, permissionContext) => turnFor(permissionContext?.toolUseID).canUseTool(toolName, input, permissionContext),
|
|
15026
15357
|
onElicitation: (request, elicitationOptions) => answerClaudeElicitation(session, sessionId, request, elicitationOptions),
|
|
15027
15358
|
onUserDialog: async () => ({ behavior: "cancelled" })
|
|
@@ -15162,11 +15493,11 @@ function handleAssistantMessage(turn, message) {
|
|
|
15162
15493
|
state: "success"
|
|
15163
15494
|
});
|
|
15164
15495
|
reclaimReleasedTurnForAssistant(turn, message);
|
|
15165
|
-
if (
|
|
15496
|
+
if (turn.pendingPlanRejectionFeedback) {
|
|
15166
15497
|
debugLog("[session-manager] Claude responded after plan denial, clearing re-prompt feedback", {
|
|
15167
15498
|
sessionId
|
|
15168
15499
|
});
|
|
15169
|
-
|
|
15500
|
+
turn.pendingPlanRejectionFeedback = null;
|
|
15170
15501
|
}
|
|
15171
15502
|
const { orderedParts, newTaskIds, contentBlockCount } = parseMessageContent(message, toolTracker, turn.mcpServerNames, activeTaskIds, taskRegistry, receivedAt, recordUnknownContentBlock);
|
|
15172
15503
|
const evidenceParent = message.parent_tool_use_id;
|
|
@@ -15309,12 +15640,7 @@ async function handleInitMessage(turn, message) {
|
|
|
15309
15640
|
}
|
|
15310
15641
|
const allMcpServers = initMsg.mcp_servers || [];
|
|
15311
15642
|
const regularMcpServers = allMcpServers.filter((s) => !s.name?.startsWith("plugin:"));
|
|
15312
|
-
const mcpServerStatuses = regularMcpServers.map(
|
|
15313
|
-
name: s.name,
|
|
15314
|
-
status: s.status === "connected" ? "connected" : "failed",
|
|
15315
|
-
error: s.error,
|
|
15316
|
-
tools: s.tools
|
|
15317
|
-
}));
|
|
15643
|
+
const mcpServerStatuses = regularMcpServers.map(initMcpServerStatus);
|
|
15318
15644
|
const { statuses: pluginStatuses, errors: pluginErrors } = pluginStatusesFromInit(initMsg);
|
|
15319
15645
|
const previousPluginErrors = new Set(session.pluginErrorSignatures);
|
|
15320
15646
|
session.pluginErrorSignatures = pluginErrors.map(({ plugin, type, message, path, loaded }) => JSON.stringify([plugin, type, message, path, loaded]));
|
|
@@ -15686,14 +16012,6 @@ async function handleResultMessage(turn, message) {
|
|
|
15686
16012
|
createdAt: new Date().toISOString()
|
|
15687
16013
|
});
|
|
15688
16014
|
}
|
|
15689
|
-
if (resultMsg.subtype === "error_max_budget_usd") {
|
|
15690
|
-
appendTranscriptNotice(session, sessionId, (event) => eventEmitter.emit(event), {
|
|
15691
|
-
type: "status",
|
|
15692
|
-
content: "Claude stopped because this session reached its spending cap.",
|
|
15693
|
-
severity: "warning",
|
|
15694
|
-
createdAt: new Date().toISOString()
|
|
15695
|
-
});
|
|
15696
|
-
}
|
|
15697
16015
|
if (turn.options?.outputSchema) {
|
|
15698
16016
|
const failure = structuredOutputFailure("claude", resultMsg.subtype === "error_max_structured_output_retries" ? "schema_retry_exhausted" : "provider_error", resultError, {
|
|
15699
16017
|
requestId: turn.structuredRequestId,
|
|
@@ -16140,7 +16458,7 @@ function handleUserMessage(turn, message) {
|
|
|
16140
16458
|
for (const taskId of completedTaskIds) {
|
|
16141
16459
|
activeTaskIds.delete(taskId);
|
|
16142
16460
|
}
|
|
16143
|
-
if (
|
|
16461
|
+
if (turn.planApprovedThisTurn) {
|
|
16144
16462
|
for (const tool of toolTracker.getTools()) {
|
|
16145
16463
|
if (tool.toolName === "ExitPlanMode" && tool.toolState === "failure" && tool.toolUseId) {
|
|
16146
16464
|
console.warn("[session-manager] ExitPlanMode reported failure despite user approval — overriding to success and scheduling continuation re-prompt", { sessionId, toolUseId: tool.toolUseId, sdkError: tool.toolError });
|
|
@@ -16149,8 +16467,8 @@ function handleUserMessage(turn, message) {
|
|
|
16149
16467
|
output: "Plan approved by the user. Proceeding with implementation.",
|
|
16150
16468
|
error: undefined
|
|
16151
16469
|
});
|
|
16152
|
-
if (!
|
|
16153
|
-
|
|
16470
|
+
if (!turn.pendingPlanApprovalContinuation) {
|
|
16471
|
+
turn.pendingPlanApprovalContinuation = "The user has approved your plan. Please proceed with implementing it now. You are no longer in plan mode and may write, edit, and run commands as needed.";
|
|
16154
16472
|
}
|
|
16155
16473
|
}
|
|
16156
16474
|
}
|
|
@@ -16226,214 +16544,223 @@ function boundedPlan(content) {
|
|
|
16226
16544
|
planTruncated: true
|
|
16227
16545
|
};
|
|
16228
16546
|
}
|
|
16229
|
-
async function
|
|
16230
|
-
const {
|
|
16231
|
-
|
|
16232
|
-
|
|
16233
|
-
|
|
16234
|
-
|
|
16235
|
-
|
|
16236
|
-
|
|
16237
|
-
message: "AskUserQuestion contains duplicate question text. Ask the questions again with distinct wording."
|
|
16238
|
-
};
|
|
16239
|
-
}
|
|
16240
|
-
const questionId = permissionContext?.requestId || generateMessageId();
|
|
16241
|
-
const requestedAt = Date.now();
|
|
16242
|
-
const questionRequest = {
|
|
16243
|
-
id: questionId,
|
|
16244
|
-
sessionId,
|
|
16245
|
-
questions,
|
|
16246
|
-
toolUseId: permissionContext?.toolUseID ?? questionId,
|
|
16247
|
-
requestedAt,
|
|
16248
|
-
expiresAt: requestedAt + QUESTION_TIMEOUT_MS
|
|
16547
|
+
async function askUserQuestion(turn, input, permissionContext) {
|
|
16548
|
+
const { sessionId } = turn;
|
|
16549
|
+
const questions = Array.isArray(input.questions) ? input.questions : [];
|
|
16550
|
+
const questionTexts = questions.map((question) => question.question);
|
|
16551
|
+
if (new Set(questionTexts).size !== questionTexts.length) {
|
|
16552
|
+
return {
|
|
16553
|
+
behavior: "deny",
|
|
16554
|
+
message: "AskUserQuestion contains duplicate question text. Ask the questions again with distinct wording."
|
|
16249
16555
|
};
|
|
16250
|
-
|
|
16251
|
-
|
|
16252
|
-
|
|
16253
|
-
|
|
16254
|
-
|
|
16255
|
-
|
|
16256
|
-
|
|
16257
|
-
|
|
16258
|
-
|
|
16259
|
-
|
|
16260
|
-
|
|
16261
|
-
|
|
16262
|
-
|
|
16263
|
-
|
|
16264
|
-
|
|
16265
|
-
|
|
16266
|
-
|
|
16267
|
-
|
|
16268
|
-
|
|
16269
|
-
|
|
16270
|
-
|
|
16271
|
-
|
|
16272
|
-
|
|
16273
|
-
|
|
16556
|
+
}
|
|
16557
|
+
const questionId = permissionContext?.requestId || generateMessageId();
|
|
16558
|
+
const requestedAt = Date.now();
|
|
16559
|
+
const questionRequest = {
|
|
16560
|
+
id: questionId,
|
|
16561
|
+
sessionId,
|
|
16562
|
+
questions,
|
|
16563
|
+
toolUseId: permissionContext?.toolUseID ?? questionId,
|
|
16564
|
+
requestedAt,
|
|
16565
|
+
expiresAt: requestedAt + QUESTION_TIMEOUT_MS
|
|
16566
|
+
};
|
|
16567
|
+
if (!canAdmitInteraction(questionRequest, "questions")) {
|
|
16568
|
+
return {
|
|
16569
|
+
behavior: "deny",
|
|
16570
|
+
message: "Too many pending interactions. Answer an existing request first."
|
|
16571
|
+
};
|
|
16572
|
+
}
|
|
16573
|
+
let rejectOnExpiry;
|
|
16574
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
16575
|
+
rejectOnExpiry = reject;
|
|
16576
|
+
});
|
|
16577
|
+
const deadline = new InteractionDeadline({
|
|
16578
|
+
requestedAt,
|
|
16579
|
+
windowMs: QUESTION_TIMEOUT_MS,
|
|
16580
|
+
onExpire: () => rejectOnExpiry(new Error(interactionExpiryMessage("Question", deadline)))
|
|
16581
|
+
});
|
|
16582
|
+
const releaseDeadline = registerInteractionDeadline("questions", questionRequest, deadline);
|
|
16583
|
+
pendingQuestions.set(questionId, questionRequest);
|
|
16584
|
+
eventEmitter.emit({
|
|
16585
|
+
type: "question.asked",
|
|
16586
|
+
sessionId,
|
|
16587
|
+
data: questionRequest
|
|
16588
|
+
});
|
|
16589
|
+
const answerPromise = new Promise((resolve, reject) => {
|
|
16590
|
+
questionResolvers.set(questionId, { resolve, reject });
|
|
16591
|
+
});
|
|
16592
|
+
try {
|
|
16593
|
+
const answers = await Promise.race([answerPromise, timeoutPromise]);
|
|
16594
|
+
console.log("[session-manager] Received question answers", {
|
|
16595
|
+
questionId,
|
|
16596
|
+
answerCount: Object.keys(answers).length
|
|
16274
16597
|
});
|
|
16275
|
-
|
|
16276
|
-
|
|
16277
|
-
|
|
16278
|
-
|
|
16279
|
-
|
|
16280
|
-
});
|
|
16281
|
-
return {
|
|
16282
|
-
behavior: "allow",
|
|
16283
|
-
updatedInput: {
|
|
16284
|
-
questions: input.questions,
|
|
16285
|
-
answers
|
|
16286
|
-
}
|
|
16287
|
-
};
|
|
16288
|
-
} catch (error) {
|
|
16289
|
-
console.error("[session-manager] Error waiting for answer:", error);
|
|
16290
|
-
const message = error instanceof Error ? error.message : "Question was cancelled";
|
|
16291
|
-
if (pendingQuestions.has(questionId)) {
|
|
16292
|
-
eventEmitter.emit({
|
|
16293
|
-
type: "question.answered",
|
|
16294
|
-
sessionId,
|
|
16295
|
-
data: { requestId: questionId, cancelled: true }
|
|
16296
|
-
});
|
|
16598
|
+
return {
|
|
16599
|
+
behavior: "allow",
|
|
16600
|
+
updatedInput: {
|
|
16601
|
+
questions: input.questions,
|
|
16602
|
+
answers
|
|
16297
16603
|
}
|
|
16298
|
-
|
|
16299
|
-
|
|
16300
|
-
|
|
16301
|
-
|
|
16302
|
-
|
|
16604
|
+
};
|
|
16605
|
+
} catch (error) {
|
|
16606
|
+
console.error("[session-manager] Error waiting for answer:", error);
|
|
16607
|
+
const message = error instanceof Error ? error.message : "Question was cancelled";
|
|
16608
|
+
if (pendingQuestions.has(questionId)) {
|
|
16609
|
+
eventEmitter.emit({
|
|
16610
|
+
type: "question.answered",
|
|
16611
|
+
sessionId,
|
|
16612
|
+
data: { requestId: questionId, cancelled: true }
|
|
16613
|
+
});
|
|
16303
16614
|
}
|
|
16615
|
+
return { behavior: "deny", message };
|
|
16616
|
+
} finally {
|
|
16617
|
+
releaseDeadline();
|
|
16618
|
+
pendingQuestions.delete(questionId);
|
|
16619
|
+
questionResolvers.delete(questionId);
|
|
16304
16620
|
}
|
|
16305
|
-
|
|
16306
|
-
|
|
16307
|
-
|
|
16308
|
-
|
|
16309
|
-
|
|
16310
|
-
|
|
16311
|
-
|
|
16312
|
-
|
|
16313
|
-
message: `Plan mode could not be persisted safely: ${message}`
|
|
16314
|
-
};
|
|
16315
|
-
}
|
|
16316
|
-
eventEmitter.emit({
|
|
16317
|
-
type: "plan.enter-requested",
|
|
16318
|
-
sessionId,
|
|
16319
|
-
data: { sessionId }
|
|
16320
|
-
});
|
|
16621
|
+
}
|
|
16622
|
+
async function enterPlanMode(turn, input) {
|
|
16623
|
+
const { session, sessionId } = turn;
|
|
16624
|
+
debugLog("[session-manager] EnterPlanMode requested", { sessionId });
|
|
16625
|
+
try {
|
|
16626
|
+
await applySessionPlanMode(session, true);
|
|
16627
|
+
} catch (error) {
|
|
16628
|
+
const message = error instanceof Error ? error.message : "Failed to persist plan mode";
|
|
16321
16629
|
return {
|
|
16322
|
-
behavior: "
|
|
16323
|
-
|
|
16630
|
+
behavior: "deny",
|
|
16631
|
+
message: `Plan mode could not be persisted safely: ${message}`
|
|
16324
16632
|
};
|
|
16325
16633
|
}
|
|
16326
|
-
|
|
16327
|
-
|
|
16328
|
-
|
|
16329
|
-
}
|
|
16330
|
-
|
|
16331
|
-
|
|
16332
|
-
|
|
16333
|
-
|
|
16334
|
-
|
|
16335
|
-
|
|
16336
|
-
|
|
16337
|
-
|
|
16338
|
-
|
|
16339
|
-
|
|
16634
|
+
eventEmitter.emit({
|
|
16635
|
+
type: "plan.enter-requested",
|
|
16636
|
+
sessionId,
|
|
16637
|
+
data: { sessionId }
|
|
16638
|
+
});
|
|
16639
|
+
return {
|
|
16640
|
+
behavior: "allow",
|
|
16641
|
+
updatedInput: input
|
|
16642
|
+
};
|
|
16643
|
+
}
|
|
16644
|
+
async function requestPlanApproval(turn, input, permissionContext) {
|
|
16645
|
+
const { session, sessionId } = turn;
|
|
16646
|
+
debugLog("[session-manager] ExitPlanMode requested, waiting for user approval", {
|
|
16647
|
+
sessionId
|
|
16648
|
+
});
|
|
16649
|
+
const approvalId = permissionContext?.requestId || generateMessageId();
|
|
16650
|
+
const directPlan = boundedPlan(input?.plan);
|
|
16651
|
+
const requestedAt = Date.now();
|
|
16652
|
+
const approvalRequest = {
|
|
16653
|
+
id: approvalId,
|
|
16654
|
+
sessionId,
|
|
16655
|
+
toolUseId: permissionContext?.toolUseID ?? approvalId,
|
|
16656
|
+
...directPlan,
|
|
16657
|
+
requestedAt,
|
|
16658
|
+
expiresAt: requestedAt + PLAN_APPROVAL_TIMEOUT_MS
|
|
16659
|
+
};
|
|
16660
|
+
if (!canAdmitInteraction(approvalRequest, "approvals")) {
|
|
16661
|
+
return {
|
|
16662
|
+
behavior: "deny",
|
|
16663
|
+
message: "Too many pending interactions. Answer an existing request first."
|
|
16340
16664
|
};
|
|
16341
|
-
|
|
16342
|
-
|
|
16343
|
-
|
|
16344
|
-
|
|
16345
|
-
|
|
16346
|
-
|
|
16347
|
-
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
16357
|
-
|
|
16358
|
-
|
|
16359
|
-
|
|
16360
|
-
|
|
16361
|
-
|
|
16362
|
-
|
|
16363
|
-
|
|
16364
|
-
|
|
16665
|
+
}
|
|
16666
|
+
let rejectOnExpiry;
|
|
16667
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
16668
|
+
rejectOnExpiry = reject;
|
|
16669
|
+
});
|
|
16670
|
+
const deadline = new InteractionDeadline({
|
|
16671
|
+
requestedAt,
|
|
16672
|
+
windowMs: PLAN_APPROVAL_TIMEOUT_MS,
|
|
16673
|
+
onExpire: () => rejectOnExpiry(new Error(interactionExpiryMessage("Plan approval", deadline)))
|
|
16674
|
+
});
|
|
16675
|
+
const releaseDeadline = registerInteractionDeadline("plan-approvals", approvalRequest, deadline);
|
|
16676
|
+
pendingPlanApprovals.set(approvalId, approvalRequest);
|
|
16677
|
+
const approvalPromise = new Promise((resolve, reject) => {
|
|
16678
|
+
planApprovalResolvers.set(approvalId, { resolve, reject });
|
|
16679
|
+
});
|
|
16680
|
+
eventEmitter.emit({
|
|
16681
|
+
type: "plan.approval-requested",
|
|
16682
|
+
sessionId,
|
|
16683
|
+
data: approvalRequest
|
|
16684
|
+
});
|
|
16685
|
+
try {
|
|
16686
|
+
const response = await Promise.race([approvalPromise, timeoutPromise]);
|
|
16687
|
+
console.log("[session-manager] Plan approval result", {
|
|
16688
|
+
approvalId,
|
|
16689
|
+
approved: response.approved,
|
|
16690
|
+
hasFeedback: typeof response.feedback === "string" && response.feedback.length > 0
|
|
16365
16691
|
});
|
|
16366
|
-
|
|
16367
|
-
|
|
16368
|
-
|
|
16369
|
-
|
|
16370
|
-
|
|
16371
|
-
|
|
16372
|
-
|
|
16373
|
-
|
|
16692
|
+
if (response.approved) {
|
|
16693
|
+
try {
|
|
16694
|
+
const control = session.queryControl;
|
|
16695
|
+
if (!control?.setPermissionMode)
|
|
16696
|
+
throw new Error("Claude permission control is unavailable");
|
|
16697
|
+
await control.setPermissionMode(claudePermissionMode(claudeTurnPolicy(session.executionPolicy, turn.options?.readOnly), turn.options?.approvalMode));
|
|
16698
|
+
await applySessionPlanMode(session, false);
|
|
16699
|
+
} catch (error) {
|
|
16374
16700
|
try {
|
|
16375
16701
|
const control = session.queryControl;
|
|
16376
16702
|
if (!control?.setPermissionMode)
|
|
16377
16703
|
throw new Error("Claude permission control is unavailable");
|
|
16378
|
-
await control.setPermissionMode(
|
|
16379
|
-
|
|
16380
|
-
|
|
16381
|
-
try {
|
|
16382
|
-
const control = session.queryControl;
|
|
16383
|
-
if (!control?.setPermissionMode)
|
|
16384
|
-
throw new Error("Claude permission control is unavailable");
|
|
16385
|
-
await control.setPermissionMode("plan");
|
|
16386
|
-
} catch {
|
|
16387
|
-
abortSession(sessionId);
|
|
16388
|
-
}
|
|
16389
|
-
const message = error instanceof Error ? error.message : "Failed to persist plan mode";
|
|
16390
|
-
return {
|
|
16391
|
-
behavior: "deny",
|
|
16392
|
-
message: `Plan mode could not be exited safely: ${message}`
|
|
16393
|
-
};
|
|
16704
|
+
await control.setPermissionMode("plan");
|
|
16705
|
+
} catch {
|
|
16706
|
+
abortSession(sessionId);
|
|
16394
16707
|
}
|
|
16395
|
-
|
|
16396
|
-
eventEmitter.emit({
|
|
16397
|
-
type: "plan.exit-requested",
|
|
16398
|
-
sessionId,
|
|
16399
|
-
data: { sessionId }
|
|
16400
|
-
});
|
|
16401
|
-
return {
|
|
16402
|
-
behavior: "allow",
|
|
16403
|
-
updatedInput: input
|
|
16404
|
-
};
|
|
16405
|
-
} else {
|
|
16406
|
-
const feedbackMessage = response.feedback ? `User feedback: "${response.feedback}"` : "No specific feedback was provided.";
|
|
16407
|
-
const denyMessage = `User rejected the plan. ${feedbackMessage} Please revise your approach based on this feedback.`;
|
|
16408
|
-
stream.pendingPlanRejectionFeedback = response.feedback ? `I've reviewed the plan and I'd like changes: ${response.feedback}
|
|
16409
|
-
|
|
16410
|
-
Please revise the plan based on this feedback.` : `I've reviewed the plan and I don't approve it as-is. Please revise your approach.`;
|
|
16708
|
+
const message = error instanceof Error ? error.message : "Failed to persist plan mode";
|
|
16411
16709
|
return {
|
|
16412
16710
|
behavior: "deny",
|
|
16413
|
-
message:
|
|
16711
|
+
message: `Plan mode could not be exited safely: ${message}`
|
|
16414
16712
|
};
|
|
16415
16713
|
}
|
|
16416
|
-
|
|
16417
|
-
|
|
16418
|
-
|
|
16419
|
-
|
|
16420
|
-
|
|
16421
|
-
|
|
16422
|
-
|
|
16423
|
-
|
|
16424
|
-
|
|
16425
|
-
|
|
16426
|
-
|
|
16427
|
-
|
|
16428
|
-
|
|
16429
|
-
}
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
|
-
|
|
16433
|
-
|
|
16434
|
-
|
|
16714
|
+
turn.planApprovedThisTurn = true;
|
|
16715
|
+
eventEmitter.emit({
|
|
16716
|
+
type: "plan.exit-requested",
|
|
16717
|
+
sessionId,
|
|
16718
|
+
data: { sessionId }
|
|
16719
|
+
});
|
|
16720
|
+
return {
|
|
16721
|
+
behavior: "allow",
|
|
16722
|
+
updatedInput: input
|
|
16723
|
+
};
|
|
16724
|
+
} else {
|
|
16725
|
+
const feedbackMessage = response.feedback ? `User feedback: "${response.feedback}"` : "No specific feedback was provided.";
|
|
16726
|
+
const denyMessage = `User rejected the plan. ${feedbackMessage} Please revise your approach based on this feedback.`;
|
|
16727
|
+
turn.pendingPlanRejectionFeedback = response.feedback ? `I've reviewed the plan and I'd like changes: ${response.feedback}
|
|
16728
|
+
|
|
16729
|
+
Please revise the plan based on this feedback.` : `I've reviewed the plan and I don't approve it as-is. Please revise your approach.`;
|
|
16730
|
+
return {
|
|
16731
|
+
behavior: "deny",
|
|
16732
|
+
message: denyMessage
|
|
16733
|
+
};
|
|
16734
|
+
}
|
|
16735
|
+
} catch (error) {
|
|
16736
|
+
console.error("[session-manager] Error waiting for plan approval:", error);
|
|
16737
|
+
const errorMessage = error instanceof Error ? error.message : "Plan approval was cancelled";
|
|
16738
|
+
if (pendingPlanApprovals.has(approvalId)) {
|
|
16739
|
+
eventEmitter.emit({
|
|
16740
|
+
type: "plan.approval-responded",
|
|
16741
|
+
sessionId,
|
|
16742
|
+
data: {
|
|
16743
|
+
requestId: approvalId,
|
|
16744
|
+
approved: false,
|
|
16745
|
+
cancelled: true
|
|
16746
|
+
}
|
|
16747
|
+
});
|
|
16435
16748
|
}
|
|
16749
|
+
return { behavior: "deny", message: errorMessage };
|
|
16750
|
+
} finally {
|
|
16751
|
+
releaseDeadline();
|
|
16752
|
+
pendingPlanApprovals.delete(approvalId);
|
|
16753
|
+
planApprovalResolvers.delete(approvalId);
|
|
16436
16754
|
}
|
|
16755
|
+
}
|
|
16756
|
+
async function turnCanUseTool(turn, toolName, input, permissionContext) {
|
|
16757
|
+
const { session, sessionId } = turn;
|
|
16758
|
+
if (toolName === "AskUserQuestion")
|
|
16759
|
+
return askUserQuestion(turn, input, permissionContext);
|
|
16760
|
+
if (toolName === "EnterPlanMode")
|
|
16761
|
+
return enterPlanMode(turn, input);
|
|
16762
|
+
if (toolName === "ExitPlanMode")
|
|
16763
|
+
return requestPlanApproval(turn, input, permissionContext);
|
|
16437
16764
|
if ((claudeWorkflowResultTools(turn.mcpServerNames).includes(toolName) || claudeArtifactServerTools(turn.mcpServerNames).includes(toolName)) && !session.executionPolicy?.toolPolicy?.deny?.includes(toolName)) {
|
|
16438
16765
|
return { behavior: "allow", updatedInput: input };
|
|
16439
16766
|
}
|
|
@@ -16527,6 +16854,7 @@ function endClaudeRuntime(runtime, failure) {
|
|
|
16527
16854
|
session.status = "error";
|
|
16528
16855
|
session.error = claudeProcessKilledMessage(killedBy);
|
|
16529
16856
|
session.interruption = "restart";
|
|
16857
|
+
session.interruptedRequestId = session.turnInFlightRequestId;
|
|
16530
16858
|
session.turnStartedAt = undefined;
|
|
16531
16859
|
session.abortController = undefined;
|
|
16532
16860
|
session.completionBlockedByBackgroundTasks = false;
|
|
@@ -16585,7 +16913,10 @@ async function prepareJoin(turn, plan, running) {
|
|
|
16585
16913
|
const { session, options } = turn;
|
|
16586
16914
|
const { mcpServers, plugins, sdkPrompt, queryEnvironment, liveSettings, mcpServersKey } = plan;
|
|
16587
16915
|
const required = mcpServers[AGENT_MCP_SERVER_NAME] ? [AGENT_MCP_SERVER_NAME, ...options?.agentMcp?.design ? ["orkestrator-design"] : []] : [];
|
|
16588
|
-
await assertClaudeRuntimeAccepts(session, options,
|
|
16916
|
+
await assertClaudeRuntimeAccepts(session, options, {
|
|
16917
|
+
...claudeLaunchResourceProfile(plugins, queryEnvironment),
|
|
16918
|
+
settingSources: JSON.stringify(plan.settingSources)
|
|
16919
|
+
});
|
|
16589
16920
|
session.queryControl = running.control;
|
|
16590
16921
|
turn.queryIteratorControl = running.control;
|
|
16591
16922
|
try {
|
|
@@ -16643,6 +16974,7 @@ function launchClaudeRuntime(turn, plan) {
|
|
|
16643
16974
|
activeTaskIds: stream.activeTaskIds,
|
|
16644
16975
|
launch: {
|
|
16645
16976
|
...claudeLaunchedProfile(session, options),
|
|
16977
|
+
settingSources: JSON.stringify(plan.settingSources),
|
|
16646
16978
|
...claudeLaunchResourceProfile(plugins, queryEnvironment)
|
|
16647
16979
|
},
|
|
16648
16980
|
mcpServers: mcpServersKey,
|
|
@@ -16679,7 +17011,7 @@ function markQueryStarted(turn, testHooks) {
|
|
|
16679
17011
|
}, { once: true });
|
|
16680
17012
|
session.finishTurnInputIfSettled = turn.finishTurnInputForThisTurn;
|
|
16681
17013
|
turn.queryStarted = true;
|
|
16682
|
-
syncTurnInFlightMark(session, { started: true });
|
|
17014
|
+
syncTurnInFlightMark(session, { started: true, requestId: turn.dispatchRequestId });
|
|
16683
17015
|
testHooks?.onQueryStarted?.();
|
|
16684
17016
|
readLiveCommandInventory(session, control);
|
|
16685
17017
|
reportUltracodeRuntime(session, control);
|
|
@@ -16693,6 +17025,7 @@ function joinableRuntime(turn, plan) {
|
|
|
16693
17025
|
return running;
|
|
16694
17026
|
const requested = {
|
|
16695
17027
|
...claudeLaunchProfile(turn.session, turn.options),
|
|
17028
|
+
settingSources: JSON.stringify(plan.settingSources),
|
|
16696
17029
|
...claudeLaunchResourceProfile(plan.plugins, plan.queryEnvironment)
|
|
16697
17030
|
};
|
|
16698
17031
|
return claudeLaunchConflict(running.launch, requested) ? undefined : running;
|
|
@@ -16735,9 +17068,9 @@ function settleTurnEnd(turn) {
|
|
|
16735
17068
|
}
|
|
16736
17069
|
return;
|
|
16737
17070
|
}
|
|
16738
|
-
if (
|
|
16739
|
-
const feedbackPrompt =
|
|
16740
|
-
|
|
17071
|
+
if (turn.pendingPlanRejectionFeedback && !abortController.signal.aborted && !options?._isReprompt && !turn.superseded) {
|
|
17072
|
+
const feedbackPrompt = turn.pendingPlanRejectionFeedback;
|
|
17073
|
+
turn.pendingPlanRejectionFeedback = null;
|
|
16741
17074
|
debugLog("[session-manager] Re-prompting with plan rejection feedback", { sessionId });
|
|
16742
17075
|
session.status = "idle";
|
|
16743
17076
|
session.abortController = undefined;
|
|
@@ -16757,9 +17090,9 @@ function settleTurnEnd(turn) {
|
|
|
16757
17090
|
failureLog: "[session-manager] Failed to re-prompt with plan feedback:"
|
|
16758
17091
|
};
|
|
16759
17092
|
}
|
|
16760
|
-
if (
|
|
16761
|
-
const continuationPrompt =
|
|
16762
|
-
|
|
17093
|
+
if (turn.pendingPlanApprovalContinuation && !abortController.signal.aborted && !options?._isReprompt && !turn.superseded) {
|
|
17094
|
+
const continuationPrompt = turn.pendingPlanApprovalContinuation;
|
|
17095
|
+
turn.pendingPlanApprovalContinuation = null;
|
|
16763
17096
|
debugLog("[session-manager] Re-prompting after approved-plan ExitPlanMode failure", {
|
|
16764
17097
|
sessionId
|
|
16765
17098
|
});
|
|
@@ -16826,6 +17159,7 @@ function settleFailedTurn(turn, error) {
|
|
|
16826
17159
|
session.abortController = turn.beforeStartup.abortController;
|
|
16827
17160
|
session.error = turn.beforeStartup.error;
|
|
16828
17161
|
session.interruption = turn.beforeStartup.interruption;
|
|
17162
|
+
session.interruptedRequestId = turn.beforeStartup.interruptedRequestId;
|
|
16829
17163
|
session.inProgressUsage = turn.beforeStartup.inProgressUsage;
|
|
16830
17164
|
session.inProgressUsageGeneration = turn.beforeStartup.inProgressUsageGeneration;
|
|
16831
17165
|
const index = session.messages.indexOf(userMessage);
|
|
@@ -16854,6 +17188,7 @@ function settleFailedTurn(turn, error) {
|
|
|
16854
17188
|
session.turnStartedAt = undefined;
|
|
16855
17189
|
session.error = failure;
|
|
16856
17190
|
session.interruption = killedBy ? "restart" : undefined;
|
|
17191
|
+
session.interruptedRequestId = killedBy ? turn.dispatchRequestId : undefined;
|
|
16857
17192
|
session.abortController = undefined;
|
|
16858
17193
|
session.completionBlockedByBackgroundTasks = false;
|
|
16859
17194
|
cleanupPendingInteractions(sessionId);
|
|
@@ -17326,14 +17661,14 @@ var SESSION_ACTIVITY_BATCH_LIMITS = {
|
|
|
17326
17661
|
};
|
|
17327
17662
|
var SESSION_ACTIVITY_STATES = new Set(["idle", "working", "waiting", "missing"]);
|
|
17328
17663
|
var encoder3 = new TextEncoder;
|
|
17329
|
-
function
|
|
17664
|
+
function isRecord4(value) {
|
|
17330
17665
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17331
17666
|
}
|
|
17332
17667
|
function isBatchableSessionId(value) {
|
|
17333
17668
|
return typeof value === "string" && value.length > 0 && encoder3.encode(value).byteLength <= SESSION_ACTIVITY_BATCH_LIMITS.maxSessionIdBytes;
|
|
17334
17669
|
}
|
|
17335
17670
|
function parseSessionActivityObservation(value) {
|
|
17336
|
-
if (!
|
|
17671
|
+
if (!isRecord4(value))
|
|
17337
17672
|
return;
|
|
17338
17673
|
const { activity, readyForInput, completionPending, asyncQuestionItemIds, closing } = value;
|
|
17339
17674
|
const { contentRevision } = value;
|
|
@@ -17367,7 +17702,7 @@ function isContentRevision(value) {
|
|
|
17367
17702
|
return typeof value === "string" && value.length > 0 && value.length <= SESSION_ACTIVITY_BATCH_LIMITS.maxContentRevisionLength;
|
|
17368
17703
|
}
|
|
17369
17704
|
function parseSessionActivityBatchRequest(value) {
|
|
17370
|
-
if (!
|
|
17705
|
+
if (!isRecord4(value))
|
|
17371
17706
|
return { ok: false, error: "Expected a JSON object" };
|
|
17372
17707
|
if (value.version !== SESSION_ACTIVITY_BATCH_VERSION) {
|
|
17373
17708
|
return { ok: false, error: "Unsupported activity batch version" };
|
|
@@ -17610,7 +17945,7 @@ function decodeLocator(locator) {
|
|
|
17610
17945
|
}
|
|
17611
17946
|
try {
|
|
17612
17947
|
const body = JSON.parse(Buffer3.from(locator.slice(LOCATOR_PREFIX.length), "base64url").toString("utf8"));
|
|
17613
|
-
if (!
|
|
17948
|
+
if (!isRecord5(body))
|
|
17614
17949
|
return;
|
|
17615
17950
|
const { m, p, d } = body;
|
|
17616
17951
|
if (typeof m !== "string" || typeof d !== "string" || !Array.isArray(p))
|
|
@@ -17630,12 +17965,12 @@ var DETAIL_FIELDS = new Set([
|
|
|
17630
17965
|
"toolDiff",
|
|
17631
17966
|
"fileDataUrl"
|
|
17632
17967
|
]);
|
|
17633
|
-
function
|
|
17968
|
+
function isRecord5(value) {
|
|
17634
17969
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
17635
17970
|
}
|
|
17636
17971
|
function heavyDiff(part) {
|
|
17637
17972
|
const diff = part.toolDiff;
|
|
17638
|
-
if (!
|
|
17973
|
+
if (!isRecord5(diff))
|
|
17639
17974
|
return;
|
|
17640
17975
|
return typeof diff.diff === "string" || typeof diff.before === "string" || typeof diff.after === "string" ? diff : undefined;
|
|
17641
17976
|
}
|
|
@@ -17657,7 +17992,7 @@ function detailPayload(part) {
|
|
|
17657
17992
|
return Object.keys(payload).length > 0 ? payload : undefined;
|
|
17658
17993
|
}
|
|
17659
17994
|
function detailInputs(part) {
|
|
17660
|
-
const diff =
|
|
17995
|
+
const diff = isRecord5(part.toolDiff) ? part.toolDiff : undefined;
|
|
17661
17996
|
return [
|
|
17662
17997
|
part.type,
|
|
17663
17998
|
part.content,
|
|
@@ -17687,7 +18022,7 @@ function digestPayload(payload) {
|
|
|
17687
18022
|
};
|
|
17688
18023
|
}
|
|
17689
18024
|
function topLevelKey(part, index) {
|
|
17690
|
-
if (
|
|
18025
|
+
if (isRecord5(part)) {
|
|
17691
18026
|
if (typeof part.sourcePartId === "string" && part.sourcePartId.length <= 512) {
|
|
17692
18027
|
return `s:${part.sourcePartId}`;
|
|
17693
18028
|
}
|
|
@@ -17698,7 +18033,7 @@ function topLevelKey(part, index) {
|
|
|
17698
18033
|
return `i:${index}`;
|
|
17699
18034
|
}
|
|
17700
18035
|
function summarizePart(raw, messageId, path, depth) {
|
|
17701
|
-
if (!
|
|
18036
|
+
if (!isRecord5(raw) || depth > MAX_SUMMARY_DEPTH)
|
|
17702
18037
|
return raw;
|
|
17703
18038
|
const part = raw;
|
|
17704
18039
|
let summary;
|
|
@@ -17750,7 +18085,7 @@ function summarizePart(raw, messageId, path, depth) {
|
|
|
17750
18085
|
if (changed)
|
|
17751
18086
|
writable()[field] = next;
|
|
17752
18087
|
}
|
|
17753
|
-
if (
|
|
18088
|
+
if (isRecord5(part.task)) {
|
|
17754
18089
|
const task = summarizePart(part.task, messageId, [...path, "task"], depth + 1);
|
|
17755
18090
|
if (task !== part.task)
|
|
17756
18091
|
writable().task = task;
|
|
@@ -17758,7 +18093,7 @@ function summarizePart(raw, messageId, path, depth) {
|
|
|
17758
18093
|
return summary ?? part;
|
|
17759
18094
|
}
|
|
17760
18095
|
function isSummarizable(message) {
|
|
17761
|
-
return
|
|
18096
|
+
return isRecord5(message) && typeof message.id === "string" && Array.isArray(message.parts);
|
|
17762
18097
|
}
|
|
17763
18098
|
function summarizeBridgeMessage(message) {
|
|
17764
18099
|
if (!isSummarizable(message))
|
|
@@ -17784,15 +18119,15 @@ function resolvePath2(message, path) {
|
|
|
17784
18119
|
let current;
|
|
17785
18120
|
if (top.startsWith("s:")) {
|
|
17786
18121
|
const id = top.slice(2);
|
|
17787
|
-
current = message.parts.find((part) =>
|
|
18122
|
+
current = message.parts.find((part) => isRecord5(part) && part.sourcePartId === id);
|
|
17788
18123
|
} else if (top.startsWith("t:")) {
|
|
17789
18124
|
const id = top.slice(2);
|
|
17790
|
-
current = message.parts.find((part) =>
|
|
18125
|
+
current = message.parts.find((part) => isRecord5(part) && part.toolUseId === id && typeof part.sourcePartId !== "string");
|
|
17791
18126
|
} else if (top.startsWith("i:")) {
|
|
17792
18127
|
current = message.parts[Number(top.slice(2))];
|
|
17793
18128
|
}
|
|
17794
18129
|
for (let index = 0;index < rest.length; index += 1) {
|
|
17795
|
-
if (!
|
|
18130
|
+
if (!isRecord5(current))
|
|
17796
18131
|
return;
|
|
17797
18132
|
const field = rest[index];
|
|
17798
18133
|
if (field === "task") {
|
|
@@ -17825,7 +18160,7 @@ function readBridgeTranscriptDetail(messages, locator) {
|
|
|
17825
18160
|
if (!message)
|
|
17826
18161
|
return { version: 1, status: "missing" };
|
|
17827
18162
|
const part = resolvePath2(message, body.p);
|
|
17828
|
-
if (!
|
|
18163
|
+
if (!isRecord5(part))
|
|
17829
18164
|
return { version: 1, status: "missing" };
|
|
17830
18165
|
const payload = detailPayload(part);
|
|
17831
18166
|
if (!payload)
|
|
@@ -17928,7 +18263,7 @@ function decodePageCursor(cursor) {
|
|
|
17928
18263
|
}
|
|
17929
18264
|
try {
|
|
17930
18265
|
const body = JSON.parse(Buffer3.from(cursor.slice(PAGE_CURSOR_PREFIX.length), "base64url").toString("utf8"));
|
|
17931
|
-
if (!
|
|
18266
|
+
if (!isRecord5(body))
|
|
17932
18267
|
return;
|
|
17933
18268
|
const { g, e, b } = body;
|
|
17934
18269
|
if (typeof g !== "string" || typeof e !== "string")
|
|
@@ -18056,9 +18391,14 @@ function compressTranscriptReads(app) {
|
|
|
18056
18391
|
function query3(c) {
|
|
18057
18392
|
return (name) => c.req.query(name);
|
|
18058
18393
|
}
|
|
18059
|
-
function registerSessionTranscriptRoutes(app, generation) {
|
|
18060
|
-
app.get("/:id/transcript", (c) => {
|
|
18394
|
+
function registerSessionTranscriptRoutes(app, generation, materialize) {
|
|
18395
|
+
app.get("/:id/transcript", async (c) => {
|
|
18061
18396
|
const id = c.req.param("id");
|
|
18397
|
+
if (!peekSession(id)) {
|
|
18398
|
+
const resolved = await materialize(id);
|
|
18399
|
+
if (!resolved.ok)
|
|
18400
|
+
return c.json(resolved.body, resolved.status);
|
|
18401
|
+
}
|
|
18062
18402
|
const read = claudeTranscriptSource(id, generation);
|
|
18063
18403
|
if (!read)
|
|
18064
18404
|
return c.json({ error: "Session not found" }, 404);
|
|
@@ -18225,11 +18565,14 @@ session.get("/:id", async (c) => {
|
|
|
18225
18565
|
createdAt: sessionData.createdAt.toISOString(),
|
|
18226
18566
|
lastActivity: sessionData.lastActivity.toISOString(),
|
|
18227
18567
|
error: sessionData.error,
|
|
18228
|
-
...sessionData.status === "error" && sessionData.interruption ? {
|
|
18568
|
+
...sessionData.status === "error" && sessionData.interruption ? {
|
|
18569
|
+
interruption: sessionData.interruption,
|
|
18570
|
+
...sessionData.interruptedRequestId ? { interruptedRequestId: sessionData.interruptedRequestId } : {}
|
|
18571
|
+
} : {},
|
|
18229
18572
|
structuredOutputRequestId: sessionData.structuredOutputRequestId,
|
|
18230
18573
|
structuredOutput: sessionData.structuredOutput,
|
|
18231
18574
|
contextUsage: sessionData.inProgressUsage ?? sessionData.usage,
|
|
18232
|
-
policy: sessionData
|
|
18575
|
+
policy: claudeReportedPolicy(sessionData),
|
|
18233
18576
|
rateLimits: sessionData.rateLimits,
|
|
18234
18577
|
promptSuggestion: sessionData.promptSuggestion,
|
|
18235
18578
|
activity: sessionData.activity,
|
|
@@ -18329,7 +18672,7 @@ session.post("/:id/config", async (c) => {
|
|
|
18329
18672
|
} else if (permissionMode === "plan" || permissionMode === "bypassPermissions") {
|
|
18330
18673
|
await setSessionPreferences(sessionData.id, { planMode: permissionMode === "plan" });
|
|
18331
18674
|
}
|
|
18332
|
-
return c.json({ ok: true, policy: sessionData
|
|
18675
|
+
return c.json({ ok: true, policy: claudeReportedPolicy(sessionData) });
|
|
18333
18676
|
});
|
|
18334
18677
|
session.get("/:id/commands", async (c) => {
|
|
18335
18678
|
try {
|
|
@@ -18421,7 +18764,7 @@ session.get("/:id/messages", async (c) => {
|
|
|
18421
18764
|
const limit = parseTranscriptTailLimit(c.req.query("limit") ?? null);
|
|
18422
18765
|
return c.json(boundClaudeTranscriptResponse(messages, limit ?? undefined));
|
|
18423
18766
|
});
|
|
18424
|
-
registerSessionTranscriptRoutes(session, TRANSCRIPT_GENERATION);
|
|
18767
|
+
registerSessionTranscriptRoutes(session, TRANSCRIPT_GENERATION, (id) => resolveSession(id, "Failed to load session transcript"));
|
|
18425
18768
|
session.post("/:id/prompt", async (c) => {
|
|
18426
18769
|
const id = c.req.param("id");
|
|
18427
18770
|
const resolved = await resolveSession(id, "Failed to send prompt");
|
|
@@ -18527,6 +18870,10 @@ session.post("/:id/prompt", async (c) => {
|
|
|
18527
18870
|
await answerIdleSteerPrompt(sessionData, prompt, requestId);
|
|
18528
18871
|
return c.json({ status: "accepted", requestId, local: true });
|
|
18529
18872
|
}
|
|
18873
|
+
const sandboxRefusal = claudeSandboxRefusal(claudeTurnPolicy(sessionData.executionPolicy, typeof readOnly === "boolean" ? readOnly : undefined));
|
|
18874
|
+
if (sandboxRefusal) {
|
|
18875
|
+
return c.json({ error: sandboxRefusal, kind: "sandbox-unavailable" }, 422);
|
|
18876
|
+
}
|
|
18530
18877
|
await assertClaudeRuntimeAccepts(sessionData, {
|
|
18531
18878
|
...typeof readOnly === "boolean" ? { readOnly } : {},
|
|
18532
18879
|
agent,
|
|
@@ -18779,14 +19126,14 @@ session.get("/:id/runtime-health", (c) => {
|
|
|
18779
19126
|
return c.json(sessionRuntimeHealthBody(peekSession(c.req.param("id"))));
|
|
18780
19127
|
});
|
|
18781
19128
|
session.get("/:id/dispatch", async (c) => {
|
|
18782
|
-
const answer = (dispatch) => c.json({ dispatch });
|
|
19129
|
+
const answer = (dispatch, pending) => c.json({ dispatch, ...pending !== undefined ? { pending } : {} });
|
|
18783
19130
|
const requestId = c.req.query("requestId")?.trim();
|
|
18784
19131
|
if (!requestId)
|
|
18785
19132
|
return answer("unknown");
|
|
18786
19133
|
const id = c.req.param("id");
|
|
18787
19134
|
const state = getPromptDispatchState(id, requestId);
|
|
18788
19135
|
if (state === "processing" || state === "already-processed")
|
|
18789
|
-
return answer("dispatched");
|
|
19136
|
+
return answer("dispatched", state === "processing");
|
|
18790
19137
|
if (state === "new") {
|
|
18791
19138
|
return answer(restoredDispatchClaimLost(peekSession(id), requestId) ? "lost" : "unknown");
|
|
18792
19139
|
}
|
|
@@ -19629,7 +19976,7 @@ var X_ALREADY_SENT = "x-hono-already-sent";
|
|
|
19629
19976
|
|
|
19630
19977
|
// ../../node_modules/.bun/@hono+node-server@2.1.5+c539cad7f494f5c9/node_modules/@hono/node-server/dist/index.mjs
|
|
19631
19978
|
import { STATUS_CODES, ServerResponse, createServer } from "node:http";
|
|
19632
|
-
import { Http2ServerRequest, constants as
|
|
19979
|
+
import { Http2ServerRequest, constants as constants3 } from "node:http2";
|
|
19633
19980
|
import { Readable } from "node:stream";
|
|
19634
19981
|
|
|
19635
19982
|
// ../../node_modules/.bun/hono@4.13.13/node_modules/hono/dist/helper/websocket/index.js
|
|
@@ -20548,7 +20895,7 @@ var drainIncoming = (incoming) => {
|
|
|
20548
20895
|
incomingWithDrainState[incomingDraining] = true;
|
|
20549
20896
|
if (incoming instanceof Http2ServerRequest) {
|
|
20550
20897
|
try {
|
|
20551
|
-
incoming.stream?.close?.(
|
|
20898
|
+
incoming.stream?.close?.(constants3.NGHTTP2_NO_ERROR);
|
|
20552
20899
|
} catch {}
|
|
20553
20900
|
return;
|
|
20554
20901
|
}
|