orkestrator 2.30.0-beta → 2.31.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 +1725 -157
- package/dist/main.js +222674 -213019
- package/package.json +3 -3
- package/resources/claude-bridge/dist/index.js +774 -662
- package/resources/codex-bridge/dist/index.js +69 -21
|
@@ -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"]);
|
|
@@ -3764,7 +3771,86 @@ import { createHash as createHash5 } from "node:crypto";
|
|
|
3764
3771
|
|
|
3765
3772
|
// ../../bridges/claude-bridge/src/services/plugin-config.ts
|
|
3766
3773
|
import { readdir, access } from "node:fs/promises";
|
|
3767
|
-
import { join as join3
|
|
3774
|
+
import { join as join3 } from "node:path";
|
|
3775
|
+
|
|
3776
|
+
// ../protocol/src/node/claude-plugin-set.ts
|
|
3777
|
+
import path from "node:path";
|
|
3778
|
+
var CLAUDE_PLUGINS_MARKER = "/.claude/plugins/";
|
|
3779
|
+
function isRecord(value) {
|
|
3780
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3781
|
+
}
|
|
3782
|
+
function claudePluginConfigList(value) {
|
|
3783
|
+
if (!Array.isArray(value))
|
|
3784
|
+
return [];
|
|
3785
|
+
const plugins = [];
|
|
3786
|
+
for (const item of value) {
|
|
3787
|
+
if (isRecord(item) && typeof item.path === "string" && item.path) {
|
|
3788
|
+
plugins.push({ type: "local", path: item.path });
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
return plugins;
|
|
3792
|
+
}
|
|
3793
|
+
function isPathWithin(parentDir, candidatePath) {
|
|
3794
|
+
const relativePath = path.relative(parentDir, candidatePath);
|
|
3795
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
|
|
3796
|
+
}
|
|
3797
|
+
function remapInstalledPlugins(pluginsDir, installedPlugins, onUnsafe) {
|
|
3798
|
+
const plugins = [];
|
|
3799
|
+
const pluginsRoot = path.resolve(pluginsDir);
|
|
3800
|
+
if (!isRecord(installedPlugins.plugins))
|
|
3801
|
+
return plugins;
|
|
3802
|
+
for (const entries of Object.values(installedPlugins.plugins)) {
|
|
3803
|
+
if (!Array.isArray(entries))
|
|
3804
|
+
continue;
|
|
3805
|
+
for (const entry of entries) {
|
|
3806
|
+
if (!isRecord(entry) || typeof entry.installPath !== "string")
|
|
3807
|
+
continue;
|
|
3808
|
+
const installPath = entry.installPath;
|
|
3809
|
+
const markerIdx = installPath.indexOf(CLAUDE_PLUGINS_MARKER);
|
|
3810
|
+
let resolvedPath;
|
|
3811
|
+
if (markerIdx !== -1) {
|
|
3812
|
+
const relativePath = installPath.substring(markerIdx + CLAUDE_PLUGINS_MARKER.length);
|
|
3813
|
+
const remappedPath = path.resolve(pluginsDir, relativePath);
|
|
3814
|
+
if (!isPathWithin(pluginsRoot, remappedPath)) {
|
|
3815
|
+
onUnsafe?.(installPath);
|
|
3816
|
+
continue;
|
|
3817
|
+
}
|
|
3818
|
+
resolvedPath = remappedPath;
|
|
3819
|
+
} else {
|
|
3820
|
+
resolvedPath = installPath;
|
|
3821
|
+
}
|
|
3822
|
+
plugins.push({ type: "local", path: resolvedPath });
|
|
3823
|
+
}
|
|
3824
|
+
}
|
|
3825
|
+
return plugins;
|
|
3826
|
+
}
|
|
3827
|
+
function resolveClaudePluginPath(pluginPath, cwd, home) {
|
|
3828
|
+
if (pluginPath.startsWith("~"))
|
|
3829
|
+
return path.join(home, pluginPath.slice(1));
|
|
3830
|
+
if (pluginPath.startsWith("/"))
|
|
3831
|
+
return pluginPath;
|
|
3832
|
+
return path.join(cwd, pluginPath);
|
|
3833
|
+
}
|
|
3834
|
+
function mergeClaudePluginLayers(layers, options) {
|
|
3835
|
+
const ordered = [
|
|
3836
|
+
claudePluginConfigList(layers.global),
|
|
3837
|
+
layers.cliInstalled,
|
|
3838
|
+
...options.includeProjectSources ? [claudePluginConfigList(layers.projectGlobal), claudePluginConfigList(layers.projectLocal)] : []
|
|
3839
|
+
];
|
|
3840
|
+
const seen = new Set;
|
|
3841
|
+
const paths = [];
|
|
3842
|
+
for (const layer of ordered) {
|
|
3843
|
+
for (const plugin of layer) {
|
|
3844
|
+
const resolved = resolveClaudePluginPath(plugin.path, options.cwd, options.home);
|
|
3845
|
+
if (seen.has(resolved))
|
|
3846
|
+
continue;
|
|
3847
|
+
seen.add(resolved);
|
|
3848
|
+
paths.push(resolved);
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
return paths;
|
|
3852
|
+
}
|
|
3853
|
+
var INVALID_PLUGIN_NAME = new RegExp("[@:\\s/\\\\\\u0000-\\u001f\\u007f\\u200b-\\u200f\\u2028-\\u202e\\u2060-\\u2064\\ufeff]");
|
|
3768
3854
|
|
|
3769
3855
|
// ../../bridges/claude-bridge/src/services/json-file-cache.ts
|
|
3770
3856
|
import { createHash as createHash4 } from "node:crypto";
|
|
@@ -3866,10 +3952,6 @@ async function pathExists(path) {
|
|
|
3866
3952
|
return false;
|
|
3867
3953
|
}
|
|
3868
3954
|
}
|
|
3869
|
-
function isPathWithin(parentDir, candidatePath) {
|
|
3870
|
-
const relativePath = relative(parentDir, candidatePath);
|
|
3871
|
-
return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
|
|
3872
|
-
}
|
|
3873
3955
|
async function scanCliPluginsDirectory(pluginsDir) {
|
|
3874
3956
|
if (!await pathExists(pluginsDir)) {
|
|
3875
3957
|
return [];
|
|
@@ -3896,11 +3978,11 @@ async function scanCliPluginsDirectory(pluginsDir) {
|
|
|
3896
3978
|
}
|
|
3897
3979
|
}
|
|
3898
3980
|
async function loadGlobalPlugins() {
|
|
3899
|
-
const plugins = await readJsonSliceCached(claudeJsonPath(), "plugins", (config) => config?.plugins);
|
|
3981
|
+
const plugins = await readJsonSliceCached(claudeJsonPath(), "plugins", (config) => claudePluginConfigList(config?.plugins));
|
|
3900
3982
|
return plugins ?? [];
|
|
3901
3983
|
}
|
|
3902
3984
|
async function loadProjectPlugins(cwd) {
|
|
3903
|
-
const plugins = await readJsonSliceCached(join3(cwd, ".claude", "plugins.json"), "plugins", (config) => config?.plugins);
|
|
3985
|
+
const plugins = await readJsonSliceCached(join3(cwd, ".claude", "plugins.json"), "plugins", (config) => claudePluginConfigList(config?.plugins));
|
|
3904
3986
|
return plugins ?? [];
|
|
3905
3987
|
}
|
|
3906
3988
|
async function loadCliInstalledPlugins() {
|
|
@@ -3910,37 +3992,13 @@ async function loadCliInstalledPlugins() {
|
|
|
3910
3992
|
if (!plugins) {
|
|
3911
3993
|
return scanCliPluginsDirectory(pluginsDir);
|
|
3912
3994
|
}
|
|
3913
|
-
return
|
|
3995
|
+
return remapInstalledPlugins2(pluginsDir, { plugins });
|
|
3914
3996
|
}
|
|
3915
|
-
function
|
|
3916
|
-
|
|
3917
|
-
const CLAUDE_PLUGINS_MARKER = "/.claude/plugins/";
|
|
3918
|
-
const pluginsRoot = resolve(pluginsDir);
|
|
3919
|
-
for (const entries of Object.values(installedPlugins.plugins)) {
|
|
3920
|
-
for (const entry of entries) {
|
|
3921
|
-
const markerIdx = entry.installPath.indexOf(CLAUDE_PLUGINS_MARKER);
|
|
3922
|
-
let resolvedPath;
|
|
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
|
-
}
|
|
3939
|
-
}
|
|
3940
|
-
return plugins;
|
|
3997
|
+
function remapInstalledPlugins2(pluginsDir, installedPlugins) {
|
|
3998
|
+
return remapInstalledPlugins(pluginsDir, installedPlugins, (installPath) => console.warn(`[plugin-config] Skipping plugin with unsafe install path: "${installPath}"`));
|
|
3941
3999
|
}
|
|
3942
4000
|
async function loadProjectOverridesFromGlobal(cwd) {
|
|
3943
|
-
const plugins = await readJsonSliceCached(claudeJsonPath(), `projects:${cwd}:plugins`, (config) => config?.projects?.[cwd]?.plugins);
|
|
4001
|
+
const plugins = await readJsonSliceCached(claudeJsonPath(), `projects:${cwd}:plugins`, (config) => claudePluginConfigList(config?.projects?.[cwd]?.plugins));
|
|
3944
4002
|
return plugins ?? [];
|
|
3945
4003
|
}
|
|
3946
4004
|
async function readPluginManifest(pluginPath) {
|
|
@@ -3956,42 +4014,19 @@ async function readPluginManifest(pluginPath) {
|
|
|
3956
4014
|
};
|
|
3957
4015
|
}
|
|
3958
4016
|
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);
|
|
4017
|
+
return resolveClaudePluginPath(pluginPath, cwd, claudeHome());
|
|
3966
4018
|
}
|
|
3967
|
-
async function getMergedPlugins(cwd) {
|
|
4019
|
+
async function getMergedPlugins(cwd, includeProjectSources = true) {
|
|
3968
4020
|
const [global2, cliInstalled, projectGlobal, projectLocal] = await Promise.all([
|
|
3969
4021
|
loadGlobalPlugins(),
|
|
3970
4022
|
loadCliInstalledPlugins(),
|
|
3971
|
-
loadProjectOverridesFromGlobal(cwd),
|
|
3972
|
-
loadProjectPlugins(cwd)
|
|
4023
|
+
includeProjectSources ? loadProjectOverridesFromGlobal(cwd) : [],
|
|
4024
|
+
includeProjectSources ? loadProjectPlugins(cwd) : []
|
|
3973
4025
|
]);
|
|
3974
|
-
|
|
3975
|
-
for (const plugin of global2) {
|
|
3976
|
-
const resolved = resolvePath(plugin.path, cwd);
|
|
3977
|
-
pluginMap.set(resolved, { ...plugin, path: resolved });
|
|
3978
|
-
}
|
|
3979
|
-
for (const plugin of cliInstalled) {
|
|
3980
|
-
const resolved = resolvePath(plugin.path, cwd);
|
|
3981
|
-
pluginMap.set(resolved, { ...plugin, path: resolved });
|
|
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());
|
|
4026
|
+
return mergeClaudePluginLayers({ global: global2, cliInstalled, projectGlobal, projectLocal }, { cwd, home: claudeHome(), includeProjectSources }).map((path) => ({ type: "local", path }));
|
|
3992
4027
|
}
|
|
3993
4028
|
async function getPluginsForSdk(cwd, includeProjectSources = true) {
|
|
3994
|
-
const configs =
|
|
4029
|
+
const configs = await getMergedPlugins(cwd, includeProjectSources);
|
|
3995
4030
|
const validPlugins = [];
|
|
3996
4031
|
for (const config of configs) {
|
|
3997
4032
|
if (await pathExists(config.path)) {
|
|
@@ -4117,90 +4152,18 @@ async function runtimeEnvironmentForAgentQuery(env = process.env, readTextFile =
|
|
|
4117
4152
|
return snapshot;
|
|
4118
4153
|
}
|
|
4119
4154
|
|
|
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
4155
|
// ../protocol/src/native-agent-transcript-patch.ts
|
|
4202
4156
|
var FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
4203
4157
|
|
|
4158
|
+
// ../protocol/src/native-agent-policy-effects.ts
|
|
4159
|
+
function appendNativeAgentPolicyNote(policy, note) {
|
|
4160
|
+
return { ...policy, note: policy.note ? `${policy.note} ${note}` : note };
|
|
4161
|
+
}
|
|
4162
|
+
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.";
|
|
4163
|
+
function readOnlyTurnReportedPolicy(turnPolicy) {
|
|
4164
|
+
return appendNativeAgentPolicyNote(turnPolicy, READ_ONLY_TURN_POLICY_NOTE);
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4204
4167
|
// ../protocol/src/native-agent-steer-rejection.ts
|
|
4205
4168
|
var NATIVE_AGENT_STEER_REJECTION_HTTP_STATUS = 429;
|
|
4206
4169
|
var MAX_STEER_JOURNAL_BYTES = 1024 * 1024 * 1024;
|
|
@@ -4263,6 +4226,15 @@ var NATIVE_AGENT_APPROVAL_MODES = Object.freeze({
|
|
|
4263
4226
|
}
|
|
4264
4227
|
]
|
|
4265
4228
|
});
|
|
4229
|
+
var CODEX_CONTAINER_APPROVAL_DESCRIPTIONS = Object.freeze({
|
|
4230
|
+
ask: {
|
|
4231
|
+
"on-request": "Codex has no sandbox of its own in a container, so this asks before any command Codex does not trust, like Untrusted"
|
|
4232
|
+
},
|
|
4233
|
+
"auto-approve": {
|
|
4234
|
+
"on-request": "Codex has no sandbox of its own in a container, so this never asks; choose Untrusted to approve commands",
|
|
4235
|
+
"auto-review": "Codex has no sandbox of its own in a container, so the reviewer is never consulted and everything runs"
|
|
4236
|
+
}
|
|
4237
|
+
});
|
|
4266
4238
|
var DISPATCH_OUTCOME_LOST_KIND = "dispatch-outcome-lost";
|
|
4267
4239
|
var NATIVE_AGENT_RETIRED_DISPATCH_TTL_MS = 24 * 60 * 60 * 1000;
|
|
4268
4240
|
var SUPPRESSED_COMPOSER_PARAMETERS = Object.freeze({
|
|
@@ -4414,6 +4386,87 @@ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
|
|
|
4414
4386
|
"slashCommandCatalogue"
|
|
4415
4387
|
]);
|
|
4416
4388
|
|
|
4389
|
+
// ../protocol/src/coordinator-web-research.ts
|
|
4390
|
+
var COORDINATOR_WEB_RESEARCH_MODES = ["off", "search", "search-and-mcp"];
|
|
4391
|
+
var DEFAULT_COORDINATOR_RESEARCH_MCP_SERVERS = Object.freeze([{ name: "context7", url: "https://mcp.context7.com/mcp" }]);
|
|
4392
|
+
var MAX_COORDINATOR_RESEARCH_MCP_SERVERS = 8;
|
|
4393
|
+
var COORDINATOR_WEB_RESEARCH_ENV = "ORKESTRATOR_COORDINATOR_WEB_RESEARCH";
|
|
4394
|
+
var COORDINATOR_RESEARCH_MCP_ENV = "ORKESTRATOR_COORDINATOR_RESEARCH_MCP";
|
|
4395
|
+
var RESERVED_SERVER_NAME = /^orkestrator/;
|
|
4396
|
+
var SERVER_NAME = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
4397
|
+
function isCoordinatorWebResearch(value) {
|
|
4398
|
+
return typeof value === "string" && COORDINATOR_WEB_RESEARCH_MODES.includes(value);
|
|
4399
|
+
}
|
|
4400
|
+
function coordinatorResearchMcpServerProblem(server) {
|
|
4401
|
+
if (!SERVER_NAME.test(server.name)) {
|
|
4402
|
+
return "Use 1 to 32 lowercase letters, digits or hyphens for the name.";
|
|
4403
|
+
}
|
|
4404
|
+
if (RESERVED_SERVER_NAME.test(server.name))
|
|
4405
|
+
return "Names starting with orkestrator are reserved.";
|
|
4406
|
+
let url;
|
|
4407
|
+
try {
|
|
4408
|
+
url = new URL(server.url);
|
|
4409
|
+
} catch {
|
|
4410
|
+
return "The URL is not valid.";
|
|
4411
|
+
}
|
|
4412
|
+
if (url.protocol !== "https:")
|
|
4413
|
+
return "Only https:// research servers are allowed.";
|
|
4414
|
+
if (url.username || url.password)
|
|
4415
|
+
return "Credentials in the URL are not allowed.";
|
|
4416
|
+
return;
|
|
4417
|
+
}
|
|
4418
|
+
function normalizeCoordinatorResearchMcpServers(value) {
|
|
4419
|
+
if (value === undefined)
|
|
4420
|
+
return DEFAULT_COORDINATOR_RESEARCH_MCP_SERVERS.map((s) => ({ ...s }));
|
|
4421
|
+
if (!Array.isArray(value))
|
|
4422
|
+
return [];
|
|
4423
|
+
const servers = [];
|
|
4424
|
+
const names = new Set;
|
|
4425
|
+
for (const entry of value) {
|
|
4426
|
+
if (servers.length >= MAX_COORDINATOR_RESEARCH_MCP_SERVERS)
|
|
4427
|
+
break;
|
|
4428
|
+
if (!entry || typeof entry !== "object")
|
|
4429
|
+
continue;
|
|
4430
|
+
const { name, url } = entry;
|
|
4431
|
+
if (typeof name !== "string" || typeof url !== "string")
|
|
4432
|
+
continue;
|
|
4433
|
+
const server = { name: name.trim(), url: url.trim() };
|
|
4434
|
+
if (coordinatorResearchMcpServerProblem(server) || names.has(server.name))
|
|
4435
|
+
continue;
|
|
4436
|
+
names.add(server.name);
|
|
4437
|
+
servers.push(server);
|
|
4438
|
+
}
|
|
4439
|
+
return servers;
|
|
4440
|
+
}
|
|
4441
|
+
function coordinatorWebResearchFromEnv(env) {
|
|
4442
|
+
const value = env[COORDINATOR_WEB_RESEARCH_ENV];
|
|
4443
|
+
return isCoordinatorWebResearch(value) ? value : "off";
|
|
4444
|
+
}
|
|
4445
|
+
function coordinatorResearchMcpServersFromEnv(env) {
|
|
4446
|
+
if (coordinatorWebResearchFromEnv(env) !== "search-and-mcp")
|
|
4447
|
+
return [];
|
|
4448
|
+
const raw = env[COORDINATOR_RESEARCH_MCP_ENV];
|
|
4449
|
+
if (!raw)
|
|
4450
|
+
return [];
|
|
4451
|
+
try {
|
|
4452
|
+
return normalizeCoordinatorResearchMcpServers(JSON.parse(raw));
|
|
4453
|
+
} catch {
|
|
4454
|
+
return [];
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
var COORDINATOR_DENIED = Object.freeze([
|
|
4458
|
+
"file.write",
|
|
4459
|
+
"file.patch",
|
|
4460
|
+
"shell.mutate",
|
|
4461
|
+
"network"
|
|
4462
|
+
]);
|
|
4463
|
+
function coordinatorCapabilityPolicy(mode) {
|
|
4464
|
+
return {
|
|
4465
|
+
deny: [...COORDINATOR_DENIED],
|
|
4466
|
+
...mode === "off" ? {} : { allow: ["web.search"] }
|
|
4467
|
+
};
|
|
4468
|
+
}
|
|
4469
|
+
|
|
4417
4470
|
// ../../bridges/claude-bridge/src/services/mcp-tool-prefix.ts
|
|
4418
4471
|
function claudeMcpToolPrefix(name) {
|
|
4419
4472
|
let normalized = name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
@@ -4882,6 +4935,14 @@ function claudeWorkspaceCwd() {
|
|
|
4882
4935
|
function claudeTurnPolicy(sessionPolicy, readOnly) {
|
|
4883
4936
|
return effectiveExecutionPolicy(readOnly ? structuredClone(READ_ONLY_TURN_POLICY) : sessionPolicy);
|
|
4884
4937
|
}
|
|
4938
|
+
function claudeReportedPolicy(session) {
|
|
4939
|
+
if (session.status === "running" && session.commandDiscoveryInputs?.readOnly === true) {
|
|
4940
|
+
const turnPolicy = claudeTurnPolicy(session.executionPolicy, true);
|
|
4941
|
+
if (turnPolicy)
|
|
4942
|
+
return readOnlyTurnReportedPolicy(turnPolicy);
|
|
4943
|
+
}
|
|
4944
|
+
return session.executionPolicy;
|
|
4945
|
+
}
|
|
4885
4946
|
function claudePolicyPermissionMode(policy) {
|
|
4886
4947
|
return isRestrictivePolicy(policy) ? "default" : "bypassPermissions";
|
|
4887
4948
|
}
|
|
@@ -7385,7 +7446,6 @@ function claudeLaunchProfile(session, options) {
|
|
|
7385
7446
|
context1m: effectiveClaudeContext1m(options),
|
|
7386
7447
|
promptSuggestions: options?.promptSuggestions === true,
|
|
7387
7448
|
chrome: effectiveClaudeChrome(policy, options),
|
|
7388
|
-
maxBudgetUsd: typeof options?.maxBudgetUsd === "number" && options.maxBudgetUsd > 0 ? options.maxBudgetUsd : null,
|
|
7389
7449
|
thinking: typeof options?.parameterValues?.thinking === "string" ? options.parameterValues.thinking : "adaptive",
|
|
7390
7450
|
bypassCapable: claudePolicyPermissionMode(policy) === "bypassPermissions",
|
|
7391
7451
|
cwd: claudeWorkspaceCwd(),
|
|
@@ -7487,8 +7547,6 @@ function claudeRuntimeOwesWork(runtime) {
|
|
|
7487
7547
|
function claudeLaunchConflict(runtime, request) {
|
|
7488
7548
|
if (runtime.chrome !== request.chrome)
|
|
7489
7549
|
return "Chrome setting";
|
|
7490
|
-
if (runtime.maxBudgetUsd !== request.maxBudgetUsd)
|
|
7491
|
-
return "cost budget";
|
|
7492
7550
|
if (runtime.context1m !== request.context1m)
|
|
7493
7551
|
return "1M context setting";
|
|
7494
7552
|
if (runtime.promptSuggestions !== request.promptSuggestions)
|
|
@@ -7645,7 +7703,7 @@ function noMessages() {
|
|
|
7645
7703
|
// ../../bridges/claude-bridge/src/services/session-manager-persistence.ts
|
|
7646
7704
|
import { constants } from "node:fs";
|
|
7647
7705
|
import { lstat as lstat2, open as open2, realpath } from "node:fs/promises";
|
|
7648
|
-
import { isAbsolute as
|
|
7706
|
+
import { isAbsolute as isAbsolute2, join as join8, relative, resolve as resolve2, sep } from "node:path";
|
|
7649
7707
|
|
|
7650
7708
|
// ../protocol/src/node/command-change-journal.ts
|
|
7651
7709
|
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 +7860,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
|
|
|
7802
7860
|
import { rmSync } from "node:fs";
|
|
7803
7861
|
import { copyFile, lstat, mkdtemp, rm as rm3 } from "node:fs/promises";
|
|
7804
7862
|
import { tmpdir } from "node:os";
|
|
7805
|
-
import { delimiter, isAbsolute
|
|
7863
|
+
import { delimiter, isAbsolute, join as join4, resolve } from "node:path";
|
|
7806
7864
|
var DEFAULT_MAX_UNTRACKED_FILE_BYTES = 2 * 1024 * 1024;
|
|
7807
7865
|
var DEFAULT_MAX_UNTRACKED_FILES = 5000;
|
|
7808
7866
|
var DEFAULT_SLOW_SNAPSHOT_MS = 500;
|
|
@@ -8084,8 +8142,8 @@ class WorkspaceChangeProbe {
|
|
|
8084
8142
|
return;
|
|
8085
8143
|
return {
|
|
8086
8144
|
root,
|
|
8087
|
-
indexPath:
|
|
8088
|
-
objectsPath:
|
|
8145
|
+
indexPath: isAbsolute(index) ? index : resolve(cwd, index),
|
|
8146
|
+
objectsPath: isAbsolute(objects) ? objects : resolve(cwd, objects)
|
|
8089
8147
|
};
|
|
8090
8148
|
}).catch(() => {
|
|
8091
8149
|
return;
|
|
@@ -9413,14 +9471,14 @@ function record(value) {
|
|
|
9413
9471
|
function text(value) {
|
|
9414
9472
|
return typeof value === "string" ? value : undefined;
|
|
9415
9473
|
}
|
|
9416
|
-
function
|
|
9474
|
+
function path2(value) {
|
|
9417
9475
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
9418
9476
|
}
|
|
9419
9477
|
function claudeProposalEvidence(toolName, input) {
|
|
9420
9478
|
const fields = record(input);
|
|
9421
9479
|
switch (toolName) {
|
|
9422
9480
|
case "Edit": {
|
|
9423
|
-
const filePath =
|
|
9481
|
+
const filePath = path2(fields.file_path);
|
|
9424
9482
|
if (!filePath)
|
|
9425
9483
|
return [];
|
|
9426
9484
|
return [
|
|
@@ -9438,7 +9496,7 @@ function claudeProposalEvidence(toolName, input) {
|
|
|
9438
9496
|
];
|
|
9439
9497
|
}
|
|
9440
9498
|
case "MultiEdit": {
|
|
9441
|
-
const filePath =
|
|
9499
|
+
const filePath = path2(fields.file_path);
|
|
9442
9500
|
if (!filePath || !Array.isArray(fields.edits))
|
|
9443
9501
|
return [];
|
|
9444
9502
|
return fields.edits.map((edit, index) => {
|
|
@@ -9457,7 +9515,7 @@ function claudeProposalEvidence(toolName, input) {
|
|
|
9457
9515
|
});
|
|
9458
9516
|
}
|
|
9459
9517
|
case "Write": {
|
|
9460
|
-
const filePath =
|
|
9518
|
+
const filePath = path2(fields.file_path);
|
|
9461
9519
|
if (!filePath)
|
|
9462
9520
|
return [];
|
|
9463
9521
|
return [
|
|
@@ -9475,7 +9533,7 @@ function claudeProposalEvidence(toolName, input) {
|
|
|
9475
9533
|
];
|
|
9476
9534
|
}
|
|
9477
9535
|
case "NotebookEdit": {
|
|
9478
|
-
const filePath =
|
|
9536
|
+
const filePath = path2(fields.notebook_path);
|
|
9479
9537
|
if (!filePath)
|
|
9480
9538
|
return [];
|
|
9481
9539
|
const deleting = fields.edit_mode === "delete";
|
|
@@ -10446,17 +10504,17 @@ function parseBase64ImageData(value) {
|
|
|
10446
10504
|
var BRIDGE_ATTACHMENT_ROOT_ENV = "ORKESTRATOR_BRIDGE_ATTACHMENT_ROOT";
|
|
10447
10505
|
function configuredAttachmentRoot(env = process.env) {
|
|
10448
10506
|
const value = env[BRIDGE_ATTACHMENT_ROOT_ENV]?.trim();
|
|
10449
|
-
return value &&
|
|
10507
|
+
return value && isAbsolute2(value) ? resolve2(value) : undefined;
|
|
10450
10508
|
}
|
|
10451
10509
|
function attachmentContainmentRoot(filePath, cwd, extraRoot = configuredAttachmentRoot()) {
|
|
10452
|
-
if (extraRoot &&
|
|
10510
|
+
if (extraRoot && isAbsolute2(filePath) && isPathWithin2(extraRoot, resolve2(filePath))) {
|
|
10453
10511
|
return extraRoot;
|
|
10454
10512
|
}
|
|
10455
|
-
return
|
|
10513
|
+
return resolve2(cwd);
|
|
10456
10514
|
}
|
|
10457
10515
|
function isPathWithin2(rootPath, targetPath) {
|
|
10458
|
-
const childPath =
|
|
10459
|
-
return childPath === "" || childPath !== ".." && !childPath.startsWith(`..${sep}`) && !
|
|
10516
|
+
const childPath = relative(rootPath, targetPath);
|
|
10517
|
+
return childPath === "" || childPath !== ".." && !childPath.startsWith(`..${sep}`) && !isAbsolute2(childPath);
|
|
10460
10518
|
}
|
|
10461
10519
|
function attachmentErrorForFsFailure(error) {
|
|
10462
10520
|
const code = error?.code;
|
|
@@ -10469,7 +10527,7 @@ function attachmentErrorForFsFailure(error) {
|
|
|
10469
10527
|
return new ClaudeAttachmentError("attachment_read_failed", "Image attachment could not be read safely from the workspace.");
|
|
10470
10528
|
}
|
|
10471
10529
|
async function assertNoSymlinkComponents(lexicalRoot, targetPath) {
|
|
10472
|
-
const childPath =
|
|
10530
|
+
const childPath = relative(lexicalRoot, targetPath);
|
|
10473
10531
|
let currentPath = lexicalRoot;
|
|
10474
10532
|
for (const segment of childPath.split(sep).filter(Boolean)) {
|
|
10475
10533
|
currentPath = join8(currentPath, segment);
|
|
@@ -10500,7 +10558,7 @@ async function assertOpenedWorkspaceFile(targetPath, canonicalRoot, openedStats)
|
|
|
10500
10558
|
}
|
|
10501
10559
|
async function readWorkspaceImageAttachment(filePath, cwd, afterSymlinkValidation, afterCanonicalValidation, afterInitialValidation) {
|
|
10502
10560
|
const lexicalRoot = attachmentContainmentRoot(filePath, cwd);
|
|
10503
|
-
const targetPath =
|
|
10561
|
+
const targetPath = isAbsolute2(filePath) ? resolve2(filePath) : resolve2(lexicalRoot, filePath);
|
|
10504
10562
|
if (!isPathWithin2(lexicalRoot, targetPath)) {
|
|
10505
10563
|
throw new ClaudeAttachmentError("attachment_outside_workspace", "Image attachment must be contained in the current workspace.");
|
|
10506
10564
|
}
|
|
@@ -10731,57 +10789,97 @@ var MAX_TRACKED_STREAM_ROWS = 256;
|
|
|
10731
10789
|
var MAX_SUBAGENT_PARTS_PER_CALL = 128;
|
|
10732
10790
|
var sessionStreamRows = new WeakMap;
|
|
10733
10791
|
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
|
-
|
|
10792
|
+
return new PromptStream(session, sessionId, options);
|
|
10793
|
+
}
|
|
10794
|
+
function explicitParentTaskUseId(partialMessage) {
|
|
10795
|
+
return typeof partialMessage.parent_tool_use_id === "string" && partialMessage.parent_tool_use_id.length > 0 ? partialMessage.parent_tool_use_id : undefined;
|
|
10796
|
+
}
|
|
10797
|
+
function materializeEntryValue(entry) {
|
|
10798
|
+
if (entry.pendingChunks) {
|
|
10799
|
+
entry.value += entry.pendingChunks.join("");
|
|
10800
|
+
entry.pendingChunks = undefined;
|
|
10801
|
+
}
|
|
10802
|
+
}
|
|
10803
|
+
|
|
10804
|
+
class PromptStream {
|
|
10805
|
+
session;
|
|
10806
|
+
sessionId;
|
|
10807
|
+
toolTracker;
|
|
10808
|
+
taskRegistry;
|
|
10809
|
+
activeTaskIds;
|
|
10810
|
+
pendingApiRetryMessageId;
|
|
10811
|
+
flushIntervalMs;
|
|
10812
|
+
maxBlockIndex;
|
|
10813
|
+
streamEventFlushTimer = null;
|
|
10814
|
+
rows = [];
|
|
10815
|
+
sharedRows;
|
|
10816
|
+
rowByApiMessage = new Map;
|
|
10817
|
+
rowByToolUseId = new Map;
|
|
10818
|
+
currentRow = null;
|
|
10819
|
+
rowSequence = 0;
|
|
10820
|
+
parentTaskByApiMessage = new Map;
|
|
10821
|
+
currentStreamApiMessageId = null;
|
|
10822
|
+
syntheticMessageKeyCounter = 0;
|
|
10823
|
+
dirtyRows = new Set;
|
|
10824
|
+
rowOfMessage = new Map;
|
|
10825
|
+
outputSeen = false;
|
|
10826
|
+
blockTarget = null;
|
|
10827
|
+
flushOnTimer = () => {
|
|
10828
|
+
this.streamEventFlushTimer = null;
|
|
10829
|
+
this.flushStreamedAssistantMessage();
|
|
10830
|
+
};
|
|
10831
|
+
constructor(session, sessionId, options) {
|
|
10832
|
+
this.session = session;
|
|
10833
|
+
this.sessionId = sessionId;
|
|
10834
|
+
this.flushIntervalMs = options.flushIntervalMs;
|
|
10835
|
+
this.maxBlockIndex = options.maxBlockIndex;
|
|
10836
|
+
this.toolTracker = options.toolTracker ?? new ToolTracker;
|
|
10837
|
+
this.taskRegistry = session.taskRegistry ??= new TaskRegistry;
|
|
10838
|
+
this.activeTaskIds = options.activeTaskIds ?? new Set;
|
|
10839
|
+
let sharedState = sessionStreamRows.get(session);
|
|
10840
|
+
if (!sharedState || sharedState.messages !== session.messages) {
|
|
10841
|
+
sharedState = { messages: session.messages, rows: new Map };
|
|
10842
|
+
sessionStreamRows.set(session, sharedState);
|
|
10843
|
+
}
|
|
10844
|
+
this.sharedRows = sharedState.rows;
|
|
10845
|
+
}
|
|
10846
|
+
get currentAssistantMessage() {
|
|
10847
|
+
return this.currentRow?.message ?? null;
|
|
10848
|
+
}
|
|
10849
|
+
get assistantOutputSeen() {
|
|
10850
|
+
return this.outputSeen;
|
|
10851
|
+
}
|
|
10852
|
+
forgetRow(row) {
|
|
10853
|
+
if (this.dirtyRows.delete(row))
|
|
10854
|
+
this.materializeRow(row, true);
|
|
10758
10855
|
for (const apiMessageId of row.blocksByApiMessage.keys()) {
|
|
10759
|
-
if (rowByApiMessage.get(apiMessageId) !== row)
|
|
10856
|
+
if (this.rowByApiMessage.get(apiMessageId) !== row)
|
|
10760
10857
|
continue;
|
|
10761
|
-
rowByApiMessage.delete(apiMessageId);
|
|
10762
|
-
parentTaskByApiMessage.delete(apiMessageId);
|
|
10858
|
+
this.rowByApiMessage.delete(apiMessageId);
|
|
10859
|
+
this.parentTaskByApiMessage.delete(apiMessageId);
|
|
10763
10860
|
}
|
|
10764
10861
|
for (const toolUseId of row.toolUseIds) {
|
|
10765
|
-
if (rowByToolUseId.get(toolUseId) === row)
|
|
10766
|
-
rowByToolUseId.delete(toolUseId);
|
|
10862
|
+
if (this.rowByToolUseId.get(toolUseId) === row)
|
|
10863
|
+
this.rowByToolUseId.delete(toolUseId);
|
|
10767
10864
|
}
|
|
10768
10865
|
if (row.message)
|
|
10769
|
-
rowOfMessage.delete(row.message);
|
|
10770
|
-
}
|
|
10771
|
-
|
|
10866
|
+
this.rowOfMessage.delete(row.message);
|
|
10867
|
+
}
|
|
10868
|
+
createRow(key, modelId) {
|
|
10869
|
+
const { rows, sharedRows, session } = this;
|
|
10772
10870
|
const shared = sharedRows.get(key);
|
|
10773
10871
|
if (shared && (!shared.message || session.messages.includes(shared.message))) {
|
|
10774
10872
|
rows.push(shared);
|
|
10775
10873
|
if (shared.message)
|
|
10776
|
-
rowOfMessage.set(shared.message, shared);
|
|
10874
|
+
this.rowOfMessage.set(shared.message, shared);
|
|
10777
10875
|
while (rows.length > MAX_TRACKED_STREAM_ROWS)
|
|
10778
|
-
forgetRow(rows.shift());
|
|
10876
|
+
this.forgetRow(rows.shift());
|
|
10779
10877
|
for (const id of shared.toolUseIds)
|
|
10780
|
-
rowByToolUseId.set(id, shared);
|
|
10878
|
+
this.rowByToolUseId.set(id, shared);
|
|
10781
10879
|
return shared;
|
|
10782
10880
|
}
|
|
10783
10881
|
const row = {
|
|
10784
|
-
sequence: rowSequence += 1,
|
|
10882
|
+
sequence: this.rowSequence += 1,
|
|
10785
10883
|
key,
|
|
10786
10884
|
message: null,
|
|
10787
10885
|
blocksByApiMessage: new Map,
|
|
@@ -10803,41 +10901,35 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
10803
10901
|
rows.push(row);
|
|
10804
10902
|
while (rows.length > MAX_TRACKED_STREAM_ROWS) {
|
|
10805
10903
|
const oldest = rows.shift();
|
|
10806
|
-
forgetRow(oldest);
|
|
10904
|
+
this.forgetRow(oldest);
|
|
10807
10905
|
}
|
|
10808
10906
|
return row;
|
|
10809
|
-
}
|
|
10810
|
-
|
|
10811
|
-
const known = rowByApiMessage.get(messageKey);
|
|
10907
|
+
}
|
|
10908
|
+
rowForApiMessage(messageKey, opensRow, parentToolUseId, modelId) {
|
|
10909
|
+
const known = this.rowByApiMessage.get(messageKey);
|
|
10812
10910
|
if (known)
|
|
10813
10911
|
return known;
|
|
10814
10912
|
let row;
|
|
10815
10913
|
if (!opensRow) {
|
|
10816
|
-
row = (parentToolUseId ? rowByToolUseId.get(parentToolUseId) : undefined) ?? currentRow ?? undefined;
|
|
10914
|
+
row = (parentToolUseId ? this.rowByToolUseId.get(parentToolUseId) : undefined) ?? this.currentRow ?? undefined;
|
|
10817
10915
|
}
|
|
10818
10916
|
if (!row) {
|
|
10819
|
-
flushStreamedAssistantMessage();
|
|
10820
|
-
row = createRow(messageKey, modelId);
|
|
10821
|
-
currentRow = row;
|
|
10917
|
+
this.flushStreamedAssistantMessage();
|
|
10918
|
+
row = this.createRow(messageKey, modelId);
|
|
10919
|
+
this.currentRow = row;
|
|
10822
10920
|
}
|
|
10823
|
-
rowByApiMessage.set(messageKey, row);
|
|
10921
|
+
this.rowByApiMessage.set(messageKey, row);
|
|
10824
10922
|
return row;
|
|
10825
|
-
}
|
|
10826
|
-
|
|
10923
|
+
}
|
|
10924
|
+
getBlocks(row, messageKey) {
|
|
10827
10925
|
let blocks = row.blocksByApiMessage.get(messageKey);
|
|
10828
10926
|
if (!blocks) {
|
|
10829
10927
|
blocks = new Map;
|
|
10830
10928
|
row.blocksByApiMessage.set(messageKey, blocks);
|
|
10831
10929
|
}
|
|
10832
10930
|
return blocks;
|
|
10833
|
-
}
|
|
10834
|
-
|
|
10835
|
-
if (entry.pendingChunks) {
|
|
10836
|
-
entry.value += entry.pendingChunks.join("");
|
|
10837
|
-
entry.pendingChunks = undefined;
|
|
10838
|
-
}
|
|
10839
|
-
};
|
|
10840
|
-
const rebuildOrderedParts = (row) => {
|
|
10931
|
+
}
|
|
10932
|
+
rebuildOrderedParts(row) {
|
|
10841
10933
|
const located = [];
|
|
10842
10934
|
for (const [messageKey, blocks] of row.blocksByApiMessage) {
|
|
10843
10935
|
const blockIndices = Array.from(blocks.keys()).sort((a, b) => a - b);
|
|
@@ -10864,17 +10956,14 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
10864
10956
|
blocks.delete(blockIndex);
|
|
10865
10957
|
if (entry.type === "tool-ref") {
|
|
10866
10958
|
row.toolUseIds.delete(entry.value);
|
|
10867
|
-
if (rowByToolUseId.get(entry.value) === row)
|
|
10868
|
-
rowByToolUseId.delete(entry.value);
|
|
10959
|
+
if (this.rowByToolUseId.get(entry.value) === row)
|
|
10960
|
+
this.rowByToolUseId.delete(entry.value);
|
|
10869
10961
|
}
|
|
10870
10962
|
}
|
|
10871
10963
|
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) => {
|
|
10964
|
+
}
|
|
10965
|
+
emitRow(row) {
|
|
10966
|
+
const { session, sessionId } = this;
|
|
10878
10967
|
const message = row.message;
|
|
10879
10968
|
if (!message)
|
|
10880
10969
|
return;
|
|
@@ -10912,34 +11001,36 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
10912
11001
|
revision: message.revision
|
|
10913
11002
|
}
|
|
10914
11003
|
});
|
|
10915
|
-
}
|
|
10916
|
-
|
|
10917
|
-
for (let index = rows.length - 1;index >= 0; index -= 1) {
|
|
10918
|
-
const message = rows[index].message;
|
|
11004
|
+
}
|
|
11005
|
+
newestRowMessage() {
|
|
11006
|
+
for (let index = this.rows.length - 1;index >= 0; index -= 1) {
|
|
11007
|
+
const message = this.rows[index].message;
|
|
10919
11008
|
if (message)
|
|
10920
11009
|
return message;
|
|
10921
11010
|
}
|
|
10922
11011
|
return null;
|
|
10923
|
-
}
|
|
10924
|
-
|
|
10925
|
-
const
|
|
11012
|
+
}
|
|
11013
|
+
insertAfterAnchor(row, message) {
|
|
11014
|
+
const messages = this.session.messages;
|
|
11015
|
+
const anchorIndex = row.anchor ? messages.lastIndexOf(row.anchor) : -1;
|
|
10926
11016
|
if (anchorIndex < 0) {
|
|
10927
|
-
|
|
11017
|
+
messages.push(message);
|
|
10928
11018
|
return;
|
|
10929
11019
|
}
|
|
10930
11020
|
let index = anchorIndex + 1;
|
|
10931
|
-
while (index <
|
|
10932
|
-
const owner = rowOfMessage.get(
|
|
11021
|
+
while (index < messages.length) {
|
|
11022
|
+
const owner = this.rowOfMessage.get(messages[index]);
|
|
10933
11023
|
if (!owner || owner.sequence > row.sequence)
|
|
10934
11024
|
break;
|
|
10935
11025
|
index += 1;
|
|
10936
11026
|
}
|
|
10937
|
-
|
|
10938
|
-
}
|
|
10939
|
-
|
|
11027
|
+
messages.splice(index, 0, message);
|
|
11028
|
+
}
|
|
11029
|
+
materializeRow(row, rebuildBlocks) {
|
|
11030
|
+
const { session } = this;
|
|
10940
11031
|
if (rebuildBlocks)
|
|
10941
|
-
rebuildOrderedParts(row);
|
|
10942
|
-
const parts = buildMessageParts(row.orderedParts, toolTracker);
|
|
11032
|
+
this.rebuildOrderedParts(row);
|
|
11033
|
+
const parts = buildMessageParts(row.orderedParts, this.toolTracker);
|
|
10943
11034
|
const content = getMessageTextFromParts(parts);
|
|
10944
11035
|
if (!row.message) {
|
|
10945
11036
|
if (parts.length === 0 && !content)
|
|
@@ -10953,9 +11044,9 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
10953
11044
|
...row.modelId ? { modelId: row.modelId } : {},
|
|
10954
11045
|
...row.sdkUuid ? { sdkUuid: row.sdkUuid } : {}
|
|
10955
11046
|
};
|
|
10956
|
-
rowOfMessage.set(row.message, row);
|
|
10957
|
-
insertAfterAnchor(row, row.message);
|
|
10958
|
-
|
|
11047
|
+
this.rowOfMessage.set(row.message, row);
|
|
11048
|
+
this.insertAfterAnchor(row, row.message);
|
|
11049
|
+
this.outputSeen = true;
|
|
10959
11050
|
} else {
|
|
10960
11051
|
row.message.content = content;
|
|
10961
11052
|
row.message.parts = parts;
|
|
@@ -10968,32 +11059,26 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
10968
11059
|
(session.revisedRows ??= new RecentlyRevisedRows).note(row.message.id);
|
|
10969
11060
|
}
|
|
10970
11061
|
markTranscriptChanged(session);
|
|
10971
|
-
emitRow(row);
|
|
10972
|
-
}
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
streamEventFlushTimer = null;
|
|
10977
|
-
}
|
|
10978
|
-
if (dirtyRows.size === 0)
|
|
11062
|
+
this.emitRow(row);
|
|
11063
|
+
}
|
|
11064
|
+
flushStreamedAssistantMessage() {
|
|
11065
|
+
this.clearFlushTimer();
|
|
11066
|
+
if (this.dirtyRows.size === 0)
|
|
10979
11067
|
return;
|
|
10980
|
-
const pending = [...dirtyRows].sort((a, b) => a.sequence - b.sequence);
|
|
10981
|
-
dirtyRows.clear();
|
|
11068
|
+
const pending = [...this.dirtyRows].sort((a, b) => a.sequence - b.sequence);
|
|
11069
|
+
this.dirtyRows.clear();
|
|
10982
11070
|
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}`;
|
|
11071
|
+
this.materializeRow(row, true);
|
|
11072
|
+
}
|
|
11073
|
+
scheduleStreamedAssistantMessageFlush(row) {
|
|
11074
|
+
this.dirtyRows.add(row);
|
|
11075
|
+
this.streamEventFlushTimer ??= setTimeout(this.flushOnTimer, this.flushIntervalMs);
|
|
11076
|
+
}
|
|
11077
|
+
applyAssistantRecord(record) {
|
|
11078
|
+
const messageKey = record.apiMessageId ?? record.uuid ?? `assistant-${this.syntheticMessageKeyCounter += 1}`;
|
|
10994
11079
|
const opensRow = record.isRoot && record.apiMessageId !== undefined;
|
|
10995
|
-
const row = rowForApiMessage(messageKey, opensRow, record.parentToolUseId, record.modelId);
|
|
10996
|
-
const blocks = getBlocks(row, messageKey);
|
|
11080
|
+
const row = this.rowForApiMessage(messageKey, opensRow, record.parentToolUseId, record.modelId);
|
|
11081
|
+
const blocks = this.getBlocks(row, messageKey);
|
|
10997
11082
|
const blockIndexBase = row.finalizedBlockCounts.get(messageKey) ?? 0;
|
|
10998
11083
|
for (const part of record.orderedParts) {
|
|
10999
11084
|
const blockIndex = blockIndexBase + (part.blockOffset ?? 0);
|
|
@@ -11005,7 +11090,7 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
11005
11090
|
});
|
|
11006
11091
|
if (part.type === "tool-ref") {
|
|
11007
11092
|
row.toolUseIds.add(part.value);
|
|
11008
|
-
rowByToolUseId.set(part.value, row);
|
|
11093
|
+
this.rowByToolUseId.set(part.value, row);
|
|
11009
11094
|
}
|
|
11010
11095
|
}
|
|
11011
11096
|
row.finalizedBlockCounts.set(messageKey, blockIndexBase + record.contentBlockCount);
|
|
@@ -11013,15 +11098,15 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
11013
11098
|
row.modelId = record.modelId;
|
|
11014
11099
|
if (record.isRoot && record.uuid)
|
|
11015
11100
|
row.sdkUuid = record.uuid;
|
|
11016
|
-
dirtyRows.delete(row);
|
|
11017
|
-
materializeRow(row, true);
|
|
11018
|
-
}
|
|
11019
|
-
|
|
11101
|
+
this.dirtyRows.delete(row);
|
|
11102
|
+
this.materializeRow(row, true);
|
|
11103
|
+
}
|
|
11104
|
+
refreshToolRows(toolUseIds) {
|
|
11020
11105
|
const affected = new Set;
|
|
11021
|
-
if (currentRow)
|
|
11022
|
-
affected.add(currentRow);
|
|
11106
|
+
if (this.currentRow)
|
|
11107
|
+
affected.add(this.currentRow);
|
|
11023
11108
|
for (const toolUseId of toolUseIds) {
|
|
11024
|
-
const row = rowByToolUseId.get(toolUseId);
|
|
11109
|
+
const row = this.rowByToolUseId.get(toolUseId);
|
|
11025
11110
|
if (row)
|
|
11026
11111
|
affected.add(row);
|
|
11027
11112
|
}
|
|
@@ -11029,150 +11114,145 @@ function createPromptStreamState(session, sessionId, options) {
|
|
|
11029
11114
|
for (const row of [...affected].sort((a, b) => a.sequence - b.sequence)) {
|
|
11030
11115
|
if (!row.message)
|
|
11031
11116
|
continue;
|
|
11032
|
-
const rebuildBlocks = dirtyRows.delete(row);
|
|
11033
|
-
materializeRow(row, rebuildBlocks);
|
|
11117
|
+
const rebuildBlocks = this.dirtyRows.delete(row);
|
|
11118
|
+
this.materializeRow(row, rebuildBlocks);
|
|
11034
11119
|
published.add(row.message);
|
|
11035
11120
|
}
|
|
11036
11121
|
return published;
|
|
11037
|
-
}
|
|
11038
|
-
|
|
11122
|
+
}
|
|
11123
|
+
rowHoldingTool(toolUseId) {
|
|
11124
|
+
return this.rowByToolUseId.get(toolUseId)?.message ?? this.currentRow?.message ?? this.newestRowMessage();
|
|
11125
|
+
}
|
|
11126
|
+
publishRow(message) {
|
|
11127
|
+
const row = this.rowOfMessage.get(message);
|
|
11128
|
+
if (row)
|
|
11129
|
+
this.emitRow(row);
|
|
11130
|
+
}
|
|
11131
|
+
applyPartialAssistantMessage(partialMessage) {
|
|
11039
11132
|
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);
|
|
11133
|
+
switch (streamEvent?.type) {
|
|
11134
|
+
case "message_start":
|
|
11135
|
+
return this.applyMessageStart(partialMessage, streamEvent);
|
|
11136
|
+
case "message_stop":
|
|
11137
|
+
return this.applyMessageStop();
|
|
11138
|
+
case "content_block_start": {
|
|
11139
|
+
const target = this.locateStreamBlock(partialMessage, streamEvent);
|
|
11140
|
+
return target ? this.applyContentBlockStart(target, streamEvent.content_block) : false;
|
|
11055
11141
|
}
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11142
|
+
case "content_block_delta": {
|
|
11143
|
+
const target = this.locateStreamBlock(partialMessage, streamEvent);
|
|
11144
|
+
return target ? this.applyContentBlockDelta(target, streamEvent.delta) : false;
|
|
11145
|
+
}
|
|
11146
|
+
default:
|
|
11147
|
+
this.locateStreamBlock(partialMessage, streamEvent);
|
|
11148
|
+
return false;
|
|
11061
11149
|
}
|
|
11062
|
-
|
|
11063
|
-
|
|
11064
|
-
|
|
11150
|
+
}
|
|
11151
|
+
applyMessageStart(partialMessage, streamEvent) {
|
|
11152
|
+
const apiMessageId = typeof streamEvent.message?.id === "string" ? streamEvent.message.id : undefined;
|
|
11153
|
+
this.currentStreamApiMessageId = apiMessageId ?? null;
|
|
11154
|
+
const isRootAssistant = isRootAssistantRecord(partialMessage.parent_tool_use_id, partialMessage.isSidechain);
|
|
11155
|
+
const modelId = isRootAssistant ? normalizeBackendModelId(streamEvent.message?.model) : undefined;
|
|
11156
|
+
if (apiMessageId) {
|
|
11157
|
+
const parentTaskUseId = explicitParentTaskUseId(partialMessage);
|
|
11158
|
+
if (parentTaskUseId)
|
|
11159
|
+
this.parentTaskByApiMessage.set(apiMessageId, parentTaskUseId);
|
|
11160
|
+
const row = this.rowForApiMessage(apiMessageId, isRootAssistant, parentTaskUseId, modelId);
|
|
11161
|
+
if (modelId)
|
|
11162
|
+
row.modelId = modelId;
|
|
11163
|
+
this.getBlocks(row, apiMessageId);
|
|
11065
11164
|
}
|
|
11066
|
-
|
|
11067
|
-
|
|
11165
|
+
return false;
|
|
11166
|
+
}
|
|
11167
|
+
applyMessageStop() {
|
|
11168
|
+
this.currentStreamApiMessageId = null;
|
|
11169
|
+
return false;
|
|
11170
|
+
}
|
|
11171
|
+
locateStreamBlock(partialMessage, streamEvent) {
|
|
11172
|
+
const blockIndex = Number.isInteger(streamEvent?.index) && streamEvent.index >= 0 && streamEvent.index <= this.maxBlockIndex ? streamEvent.index : undefined;
|
|
11173
|
+
if (blockIndex === undefined)
|
|
11174
|
+
return null;
|
|
11175
|
+
const messageKey = this.currentStreamApiMessageId ?? (typeof partialMessage.uuid === "string" ? partialMessage.uuid : undefined);
|
|
11176
|
+
if (!messageKey)
|
|
11177
|
+
return null;
|
|
11178
|
+
const explicitParent = explicitParentTaskUseId(partialMessage);
|
|
11179
|
+
const parentTaskUseId = explicitParent ?? this.parentTaskByApiMessage.get(messageKey);
|
|
11180
|
+
if (explicitParent)
|
|
11181
|
+
this.parentTaskByApiMessage.set(messageKey, explicitParent);
|
|
11182
|
+
const row = this.rowForApiMessage(messageKey, false, parentTaskUseId);
|
|
11183
|
+
const blocks = this.getBlocks(row, messageKey);
|
|
11184
|
+
const target = this.blockTarget ??= { row, blocks, blockIndex, messageKey, parentTaskUseId };
|
|
11185
|
+
target.row = row;
|
|
11186
|
+
target.blocks = blocks;
|
|
11187
|
+
target.blockIndex = blockIndex;
|
|
11188
|
+
target.messageKey = messageKey;
|
|
11189
|
+
target.parentTaskUseId = parentTaskUseId;
|
|
11190
|
+
return target;
|
|
11191
|
+
}
|
|
11192
|
+
applyContentBlockStart(target, contentBlock) {
|
|
11193
|
+
let type;
|
|
11194
|
+
let value;
|
|
11195
|
+
if (contentBlock?.type === "text") {
|
|
11196
|
+
type = "text";
|
|
11197
|
+
value = typeof contentBlock.text === "string" ? contentBlock.text : "";
|
|
11198
|
+
} else if (contentBlock?.type === "thinking") {
|
|
11199
|
+
type = "thinking";
|
|
11200
|
+
value = typeof contentBlock.thinking === "string" ? contentBlock.thinking : "";
|
|
11201
|
+
} else {
|
|
11068
11202
|
return false;
|
|
11069
11203
|
}
|
|
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
|
-
}
|
|
11204
|
+
const existing = target.blocks.get(target.blockIndex);
|
|
11205
|
+
this.storeStreamedEntry(target, {
|
|
11206
|
+
type,
|
|
11207
|
+
value,
|
|
11208
|
+
timestamp: existing?.timestamp ?? new Date().toISOString(),
|
|
11209
|
+
messageUuid: target.messageKey,
|
|
11210
|
+
parentTaskUseId: target.parentTaskUseId
|
|
11211
|
+
});
|
|
11212
|
+
return true;
|
|
11213
|
+
}
|
|
11214
|
+
applyContentBlockDelta(target, delta) {
|
|
11215
|
+
if (delta?.type === "text_delta") {
|
|
11216
|
+
const chunk = typeof delta.text === "string" ? delta.text : "";
|
|
11217
|
+
this.storeStreamedEntry(target, this.appendStreamedDelta(target, "text", chunk));
|
|
11218
|
+
} else if (delta?.type === "thinking_delta") {
|
|
11219
|
+
const chunk = typeof delta.thinking === "string" ? delta.thinking : "";
|
|
11220
|
+
this.storeStreamedEntry(target, this.appendStreamedDelta(target, "thinking", chunk));
|
|
11123
11221
|
} else {
|
|
11124
11222
|
return false;
|
|
11125
11223
|
}
|
|
11126
|
-
entriesForMessage.set(blockIndex, entry);
|
|
11127
|
-
scheduleStreamedAssistantMessageFlush(row);
|
|
11128
11224
|
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
|
-
}
|
|
11225
|
+
}
|
|
11226
|
+
appendStreamedDelta(target, type, chunk) {
|
|
11227
|
+
const entry = target.blocks.get(target.blockIndex);
|
|
11228
|
+
if (entry?.type === type) {
|
|
11229
|
+
entry.parentTaskUseId ??= target.parentTaskUseId;
|
|
11230
|
+
(entry.pendingChunks ??= []).push(chunk);
|
|
11231
|
+
return entry;
|
|
11174
11232
|
}
|
|
11175
|
-
|
|
11233
|
+
if (entry)
|
|
11234
|
+
materializeEntryValue(entry);
|
|
11235
|
+
return {
|
|
11236
|
+
type,
|
|
11237
|
+
value: `${entry?.value ?? ""}${chunk}`,
|
|
11238
|
+
timestamp: entry?.timestamp ?? new Date().toISOString(),
|
|
11239
|
+
messageUuid: target.messageKey,
|
|
11240
|
+
parentTaskUseId: entry?.parentTaskUseId ?? target.parentTaskUseId
|
|
11241
|
+
};
|
|
11242
|
+
}
|
|
11243
|
+
storeStreamedEntry(target, entry) {
|
|
11244
|
+
target.blocks.set(target.blockIndex, entry);
|
|
11245
|
+
this.scheduleStreamedAssistantMessageFlush(target.row);
|
|
11246
|
+
}
|
|
11247
|
+
beginPostSteerScope() {
|
|
11248
|
+
this.flushStreamedAssistantMessage();
|
|
11249
|
+
}
|
|
11250
|
+
clearFlushTimer() {
|
|
11251
|
+
if (this.streamEventFlushTimer) {
|
|
11252
|
+
clearTimeout(this.streamEventFlushTimer);
|
|
11253
|
+
this.streamEventFlushTimer = null;
|
|
11254
|
+
}
|
|
11255
|
+
}
|
|
11176
11256
|
}
|
|
11177
11257
|
|
|
11178
11258
|
// ../protocol/src/structured-output-text.ts
|
|
@@ -11769,7 +11849,10 @@ function createTurnState(session, sessionId, init) {
|
|
|
11769
11849
|
settlingResult: false,
|
|
11770
11850
|
localCommandOutputSeen: false,
|
|
11771
11851
|
lastThinkingTokensEmitAt: 0,
|
|
11772
|
-
compactedTokensBefore: undefined
|
|
11852
|
+
compactedTokensBefore: undefined,
|
|
11853
|
+
pendingPlanRejectionFeedback: null,
|
|
11854
|
+
planApprovedThisTurn: false,
|
|
11855
|
+
pendingPlanApprovalContinuation: null
|
|
11773
11856
|
};
|
|
11774
11857
|
const diagnostics = createBridgeDiagnostics("claude", {
|
|
11775
11858
|
id: sessionId,
|
|
@@ -12332,7 +12415,7 @@ function commandBindingRevision(parts) {
|
|
|
12332
12415
|
}
|
|
12333
12416
|
return hash.toString(16).padStart(16, "0");
|
|
12334
12417
|
}
|
|
12335
|
-
function
|
|
12418
|
+
function isRecord2(value) {
|
|
12336
12419
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
12337
12420
|
}
|
|
12338
12421
|
function boundedIdentity(value, maxBytes) {
|
|
@@ -12352,7 +12435,7 @@ function displayCommandName(name) {
|
|
|
12352
12435
|
return name.startsWith("/") || name.startsWith("$") ? name : `/${name}`;
|
|
12353
12436
|
}
|
|
12354
12437
|
function normalizeAvailability(value) {
|
|
12355
|
-
if (!
|
|
12438
|
+
if (!isRecord2(value))
|
|
12356
12439
|
return;
|
|
12357
12440
|
if (typeof value.state !== "string")
|
|
12358
12441
|
return;
|
|
@@ -12367,7 +12450,7 @@ function normalizeAvailability(value) {
|
|
|
12367
12450
|
};
|
|
12368
12451
|
}
|
|
12369
12452
|
function normalizeInputPolicy(value) {
|
|
12370
|
-
if (!
|
|
12453
|
+
if (!isRecord2(value))
|
|
12371
12454
|
return;
|
|
12372
12455
|
const policy = {};
|
|
12373
12456
|
if (value.arguments === "none" || value.arguments === "optional" || value.arguments === "required") {
|
|
@@ -12382,7 +12465,7 @@ function normalizeInputPolicy(value) {
|
|
|
12382
12465
|
return Object.keys(policy).length > 0 ? policy : undefined;
|
|
12383
12466
|
}
|
|
12384
12467
|
function normalizeCommandCataloguePayload(payload, options = {}) {
|
|
12385
|
-
const record =
|
|
12468
|
+
const record = isRecord2(payload) ? payload : {};
|
|
12386
12469
|
const enhanced = record.catalogueVersion === NATIVE_AGENT_COMMAND_CATALOGUE_VERSION;
|
|
12387
12470
|
const rows = Array.isArray(record.commands) ? record.commands : [];
|
|
12388
12471
|
const byId = new Map;
|
|
@@ -12391,7 +12474,7 @@ function normalizeCommandCataloguePayload(payload, options = {}) {
|
|
|
12391
12474
|
let aliasBudget = COMMAND_CATALOGUE_LIMITS.maxAliasesTotal;
|
|
12392
12475
|
for (const candidate of rows.slice(0, COMMAND_CATALOGUE_LIMITS.maxCommands)) {
|
|
12393
12476
|
const row = typeof candidate === "string" ? { name: candidate } : candidate;
|
|
12394
|
-
if (!
|
|
12477
|
+
if (!isRecord2(row)) {
|
|
12395
12478
|
rejected += 1;
|
|
12396
12479
|
continue;
|
|
12397
12480
|
}
|
|
@@ -12471,7 +12554,8 @@ function normalizeCommandCataloguePayload(payload, options = {}) {
|
|
|
12471
12554
|
...status ? { status } : {},
|
|
12472
12555
|
...revision !== undefined ? { revision } : {},
|
|
12473
12556
|
...generation ? { generation } : {},
|
|
12474
|
-
...freshness ? { freshness } : {}
|
|
12557
|
+
...freshness ? { freshness } : {},
|
|
12558
|
+
...enhanced && status === "stale" && record.attachPending === true ? { attachPending: true } : {}
|
|
12475
12559
|
};
|
|
12476
12560
|
}
|
|
12477
12561
|
var LEGACY_ID_PREFIX = "legacy:";
|
|
@@ -12484,7 +12568,7 @@ function commandCatalogueBytes(commands) {
|
|
|
12484
12568
|
function parseBridgeCommandInvocation(value) {
|
|
12485
12569
|
if (value === undefined || value === null)
|
|
12486
12570
|
return { ok: true };
|
|
12487
|
-
if (!
|
|
12571
|
+
if (!isRecord2(value))
|
|
12488
12572
|
return { ok: false, error: "command must be an object" };
|
|
12489
12573
|
const id = boundedIdentity(value.id, COMMAND_CATALOGUE_LIMITS.maxIdBytes);
|
|
12490
12574
|
const name = boundedIdentity(value.name, COMMAND_CATALOGUE_LIMITS.maxNameBytes);
|
|
@@ -12687,6 +12771,40 @@ function redactSecrets(value) {
|
|
|
12687
12771
|
return result;
|
|
12688
12772
|
}
|
|
12689
12773
|
|
|
12774
|
+
// ../../bridges/claude-bridge/src/services/mcp-runtime-status.ts
|
|
12775
|
+
function mcpScope(scope, name, source) {
|
|
12776
|
+
if (name === "orkestrator")
|
|
12777
|
+
return "orkestrator";
|
|
12778
|
+
if (source?.includes("plugin") || scope?.includes("plugin"))
|
|
12779
|
+
return "plugin";
|
|
12780
|
+
if (scope === "project" || scope === "local")
|
|
12781
|
+
return scope;
|
|
12782
|
+
return "user";
|
|
12783
|
+
}
|
|
12784
|
+
function mcpRuntimeState(status) {
|
|
12785
|
+
switch (status) {
|
|
12786
|
+
case "connected":
|
|
12787
|
+
case "failed":
|
|
12788
|
+
case "needs-auth":
|
|
12789
|
+
case "disabled":
|
|
12790
|
+
return status;
|
|
12791
|
+
case "pending":
|
|
12792
|
+
return "connecting";
|
|
12793
|
+
default:
|
|
12794
|
+
return "unknown";
|
|
12795
|
+
}
|
|
12796
|
+
}
|
|
12797
|
+
function initMcpServerStatus(raw) {
|
|
12798
|
+
const source = typeof raw.source === "string" ? raw.source : undefined;
|
|
12799
|
+
return {
|
|
12800
|
+
name: raw.name,
|
|
12801
|
+
status: mcpRuntimeState(raw.status),
|
|
12802
|
+
...source ? { scope: mcpScope(source, raw.name) } : {},
|
|
12803
|
+
...typeof raw.error === "string" && raw.error ? { error: raw.error } : {},
|
|
12804
|
+
...Array.isArray(raw.tools) ? { tools: raw.tools.filter((tool) => typeof tool === "string") } : {}
|
|
12805
|
+
};
|
|
12806
|
+
}
|
|
12807
|
+
|
|
12690
12808
|
// ../../bridges/claude-bridge/src/services/claude-sign-in.ts
|
|
12691
12809
|
var AUTH_CACHE_TTL_MS = 30000;
|
|
12692
12810
|
var FAILED_AUTH_CACHE_TTL_MS = 5000;
|
|
@@ -12935,15 +13053,6 @@ function createProbe() {
|
|
|
12935
13053
|
...claudeExecutableOptions()
|
|
12936
13054
|
});
|
|
12937
13055
|
}
|
|
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
13056
|
function mcpTransport(status) {
|
|
12948
13057
|
const type = status.config?.type;
|
|
12949
13058
|
if (type === "stdio" || type === "sse" || type === "http")
|
|
@@ -12966,7 +13075,7 @@ function normalizeMcp(status) {
|
|
|
12966
13075
|
id: status.name,
|
|
12967
13076
|
name: status.serverInfo?.name || status.name,
|
|
12968
13077
|
status: mappedStatus,
|
|
12969
|
-
scope: mcpScope(status.scope, status.name),
|
|
13078
|
+
scope: mcpScope(status.scope, status.name, status.source),
|
|
12970
13079
|
...transport ? { transport } : {},
|
|
12971
13080
|
toolCount: tools.length,
|
|
12972
13081
|
...tools.length ? { tools } : {},
|
|
@@ -13003,8 +13112,12 @@ async function performSessionMcpAction(sessionId, serverId, action) {
|
|
|
13003
13112
|
const session = sessions.get(sessionId);
|
|
13004
13113
|
if (!session)
|
|
13005
13114
|
throw new Error("Session not found");
|
|
13115
|
+
const live = readableControl(session);
|
|
13116
|
+
if (!live && action !== "reconnect" && action !== "enable" && action !== "disable") {
|
|
13117
|
+
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.");
|
|
13118
|
+
}
|
|
13006
13119
|
let probe;
|
|
13007
|
-
const control =
|
|
13120
|
+
const control = live ?? (probe = createProbe());
|
|
13008
13121
|
try {
|
|
13009
13122
|
if (action === "reconnect")
|
|
13010
13123
|
await control.reconnectMcpServer?.(serverId);
|
|
@@ -13248,7 +13361,7 @@ function boundedMessage(error, fallback) {
|
|
|
13248
13361
|
const message = error instanceof Error && error.message ? error.message : fallback;
|
|
13249
13362
|
return truncateUtf8(message, MAX_MESSAGE_BYTES);
|
|
13250
13363
|
}
|
|
13251
|
-
function
|
|
13364
|
+
function isRecord3(value) {
|
|
13252
13365
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
13253
13366
|
}
|
|
13254
13367
|
function sdkRows(value) {
|
|
@@ -13256,7 +13369,7 @@ function sdkRows(value) {
|
|
|
13256
13369
|
return [];
|
|
13257
13370
|
const rows = [];
|
|
13258
13371
|
for (const candidate of value.slice(0, COMMAND_CATALOGUE_LIMITS.maxCommands)) {
|
|
13259
|
-
if (!
|
|
13372
|
+
if (!isRecord3(candidate) || typeof candidate.name !== "string" || !candidate.name)
|
|
13260
13373
|
continue;
|
|
13261
13374
|
rows.push(candidate);
|
|
13262
13375
|
}
|
|
@@ -13438,7 +13551,7 @@ function recordInitCommandInventory(session) {
|
|
|
13438
13551
|
reannotate(session);
|
|
13439
13552
|
}
|
|
13440
13553
|
function recordCommandsChanged(session, message) {
|
|
13441
|
-
if (!
|
|
13554
|
+
if (!isRecord3(message) || !Array.isArray(message.commands))
|
|
13442
13555
|
return;
|
|
13443
13556
|
applyInventory(session, "replacement", sdkRows(message.commands));
|
|
13444
13557
|
}
|
|
@@ -13587,10 +13700,10 @@ async function refreshClaudeCommandCatalogue(sessionId) {
|
|
|
13587
13700
|
else
|
|
13588
13701
|
failures.push(`${label}: ${boundedMessage(result.reason, "reload failed")}`);
|
|
13589
13702
|
}
|
|
13590
|
-
if (skills.status === "fulfilled" &&
|
|
13703
|
+
if (skills.status === "fulfilled" && isRecord3(skills.value)) {
|
|
13591
13704
|
session.commandSkillNames = sdkRows(skills.value.skills).map((skill) => skill.name);
|
|
13592
13705
|
}
|
|
13593
|
-
const errorCount = plugins.status === "fulfilled" &&
|
|
13706
|
+
const errorCount = plugins.status === "fulfilled" && isRecord3(plugins.value) ? plugins.value.error_count : undefined;
|
|
13594
13707
|
if (typeof errorCount === "number" && errorCount > 0) {
|
|
13595
13708
|
failures.push(`plugins: ${errorCount} failed to load`);
|
|
13596
13709
|
}
|
|
@@ -13973,7 +14086,9 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
|
|
|
13973
14086
|
"adopt_workflow",
|
|
13974
14087
|
"cancel_build_pipeline",
|
|
13975
14088
|
"cancel_multi_review",
|
|
14089
|
+
"cancel_schedule",
|
|
13976
14090
|
"compare_project_files",
|
|
14091
|
+
"create_schedule",
|
|
13977
14092
|
"create_ticket",
|
|
13978
14093
|
"get_build_pipeline",
|
|
13979
14094
|
"get_environment",
|
|
@@ -13995,6 +14110,7 @@ var COORDINATOR_CONTROL_TOOL_NAMES = Object.freeze([
|
|
|
13995
14110
|
"list_node_environments",
|
|
13996
14111
|
"list_nodes",
|
|
13997
14112
|
"list_projects",
|
|
14113
|
+
"list_schedules",
|
|
13998
14114
|
"list_tabs",
|
|
13999
14115
|
"list_tickets",
|
|
14000
14116
|
"list_workflows",
|
|
@@ -14968,7 +15084,6 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
|
|
|
14968
15084
|
} : { type: "adaptive", display: "summarized" },
|
|
14969
15085
|
...effectiveClaudeContext1m(options) ? { betas: ["context-1m-2025-08-07"] } : {},
|
|
14970
15086
|
extraArgs: effectiveClaudeChrome(policy, options) ? { chrome: null } : { "no-chrome": null },
|
|
14971
|
-
...typeof options?.maxBudgetUsd === "number" && options.maxBudgetUsd > 0 ? { maxBudgetUsd: options.maxBudgetUsd } : {},
|
|
14972
15087
|
includePartialMessages: true,
|
|
14973
15088
|
forwardSubagentText: true,
|
|
14974
15089
|
allowedTools,
|
|
@@ -15022,6 +15137,7 @@ function claudeQueryOptions(turn, plan, turnFor, mcp) {
|
|
|
15022
15137
|
PostCompact: [{ hooks: [latestTurnHook((target) => target.recordPostCompact)] }]
|
|
15023
15138
|
},
|
|
15024
15139
|
includeHookEvents: true,
|
|
15140
|
+
toolConfig: { askUserQuestion: { previewFormat: "markdown" } },
|
|
15025
15141
|
canUseTool: (toolName, input, permissionContext) => turnFor(permissionContext?.toolUseID).canUseTool(toolName, input, permissionContext),
|
|
15026
15142
|
onElicitation: (request, elicitationOptions) => answerClaudeElicitation(session, sessionId, request, elicitationOptions),
|
|
15027
15143
|
onUserDialog: async () => ({ behavior: "cancelled" })
|
|
@@ -15162,11 +15278,11 @@ function handleAssistantMessage(turn, message) {
|
|
|
15162
15278
|
state: "success"
|
|
15163
15279
|
});
|
|
15164
15280
|
reclaimReleasedTurnForAssistant(turn, message);
|
|
15165
|
-
if (
|
|
15281
|
+
if (turn.pendingPlanRejectionFeedback) {
|
|
15166
15282
|
debugLog("[session-manager] Claude responded after plan denial, clearing re-prompt feedback", {
|
|
15167
15283
|
sessionId
|
|
15168
15284
|
});
|
|
15169
|
-
|
|
15285
|
+
turn.pendingPlanRejectionFeedback = null;
|
|
15170
15286
|
}
|
|
15171
15287
|
const { orderedParts, newTaskIds, contentBlockCount } = parseMessageContent(message, toolTracker, turn.mcpServerNames, activeTaskIds, taskRegistry, receivedAt, recordUnknownContentBlock);
|
|
15172
15288
|
const evidenceParent = message.parent_tool_use_id;
|
|
@@ -15309,12 +15425,7 @@ async function handleInitMessage(turn, message) {
|
|
|
15309
15425
|
}
|
|
15310
15426
|
const allMcpServers = initMsg.mcp_servers || [];
|
|
15311
15427
|
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
|
-
}));
|
|
15428
|
+
const mcpServerStatuses = regularMcpServers.map(initMcpServerStatus);
|
|
15318
15429
|
const { statuses: pluginStatuses, errors: pluginErrors } = pluginStatusesFromInit(initMsg);
|
|
15319
15430
|
const previousPluginErrors = new Set(session.pluginErrorSignatures);
|
|
15320
15431
|
session.pluginErrorSignatures = pluginErrors.map(({ plugin, type, message, path, loaded }) => JSON.stringify([plugin, type, message, path, loaded]));
|
|
@@ -15686,14 +15797,6 @@ async function handleResultMessage(turn, message) {
|
|
|
15686
15797
|
createdAt: new Date().toISOString()
|
|
15687
15798
|
});
|
|
15688
15799
|
}
|
|
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
15800
|
if (turn.options?.outputSchema) {
|
|
15698
15801
|
const failure = structuredOutputFailure("claude", resultMsg.subtype === "error_max_structured_output_retries" ? "schema_retry_exhausted" : "provider_error", resultError, {
|
|
15699
15802
|
requestId: turn.structuredRequestId,
|
|
@@ -16140,7 +16243,7 @@ function handleUserMessage(turn, message) {
|
|
|
16140
16243
|
for (const taskId of completedTaskIds) {
|
|
16141
16244
|
activeTaskIds.delete(taskId);
|
|
16142
16245
|
}
|
|
16143
|
-
if (
|
|
16246
|
+
if (turn.planApprovedThisTurn) {
|
|
16144
16247
|
for (const tool of toolTracker.getTools()) {
|
|
16145
16248
|
if (tool.toolName === "ExitPlanMode" && tool.toolState === "failure" && tool.toolUseId) {
|
|
16146
16249
|
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 +16252,8 @@ function handleUserMessage(turn, message) {
|
|
|
16149
16252
|
output: "Plan approved by the user. Proceeding with implementation.",
|
|
16150
16253
|
error: undefined
|
|
16151
16254
|
});
|
|
16152
|
-
if (!
|
|
16153
|
-
|
|
16255
|
+
if (!turn.pendingPlanApprovalContinuation) {
|
|
16256
|
+
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
16257
|
}
|
|
16155
16258
|
}
|
|
16156
16259
|
}
|
|
@@ -16226,214 +16329,223 @@ function boundedPlan(content) {
|
|
|
16226
16329
|
planTruncated: true
|
|
16227
16330
|
};
|
|
16228
16331
|
}
|
|
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
|
|
16332
|
+
async function askUserQuestion(turn, input, permissionContext) {
|
|
16333
|
+
const { sessionId } = turn;
|
|
16334
|
+
const questions = Array.isArray(input.questions) ? input.questions : [];
|
|
16335
|
+
const questionTexts = questions.map((question) => question.question);
|
|
16336
|
+
if (new Set(questionTexts).size !== questionTexts.length) {
|
|
16337
|
+
return {
|
|
16338
|
+
behavior: "deny",
|
|
16339
|
+
message: "AskUserQuestion contains duplicate question text. Ask the questions again with distinct wording."
|
|
16249
16340
|
};
|
|
16250
|
-
|
|
16251
|
-
|
|
16252
|
-
|
|
16253
|
-
|
|
16254
|
-
|
|
16255
|
-
|
|
16256
|
-
|
|
16257
|
-
|
|
16258
|
-
|
|
16259
|
-
|
|
16260
|
-
|
|
16261
|
-
|
|
16262
|
-
|
|
16263
|
-
|
|
16264
|
-
|
|
16265
|
-
|
|
16266
|
-
|
|
16267
|
-
|
|
16268
|
-
|
|
16269
|
-
|
|
16270
|
-
|
|
16271
|
-
|
|
16272
|
-
|
|
16273
|
-
|
|
16341
|
+
}
|
|
16342
|
+
const questionId = permissionContext?.requestId || generateMessageId();
|
|
16343
|
+
const requestedAt = Date.now();
|
|
16344
|
+
const questionRequest = {
|
|
16345
|
+
id: questionId,
|
|
16346
|
+
sessionId,
|
|
16347
|
+
questions,
|
|
16348
|
+
toolUseId: permissionContext?.toolUseID ?? questionId,
|
|
16349
|
+
requestedAt,
|
|
16350
|
+
expiresAt: requestedAt + QUESTION_TIMEOUT_MS
|
|
16351
|
+
};
|
|
16352
|
+
if (!canAdmitInteraction(questionRequest, "questions")) {
|
|
16353
|
+
return {
|
|
16354
|
+
behavior: "deny",
|
|
16355
|
+
message: "Too many pending interactions. Answer an existing request first."
|
|
16356
|
+
};
|
|
16357
|
+
}
|
|
16358
|
+
let rejectOnExpiry;
|
|
16359
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
16360
|
+
rejectOnExpiry = reject;
|
|
16361
|
+
});
|
|
16362
|
+
const deadline = new InteractionDeadline({
|
|
16363
|
+
requestedAt,
|
|
16364
|
+
windowMs: QUESTION_TIMEOUT_MS,
|
|
16365
|
+
onExpire: () => rejectOnExpiry(new Error(interactionExpiryMessage("Question", deadline)))
|
|
16366
|
+
});
|
|
16367
|
+
const releaseDeadline = registerInteractionDeadline("questions", questionRequest, deadline);
|
|
16368
|
+
pendingQuestions.set(questionId, questionRequest);
|
|
16369
|
+
eventEmitter.emit({
|
|
16370
|
+
type: "question.asked",
|
|
16371
|
+
sessionId,
|
|
16372
|
+
data: questionRequest
|
|
16373
|
+
});
|
|
16374
|
+
const answerPromise = new Promise((resolve, reject) => {
|
|
16375
|
+
questionResolvers.set(questionId, { resolve, reject });
|
|
16376
|
+
});
|
|
16377
|
+
try {
|
|
16378
|
+
const answers = await Promise.race([answerPromise, timeoutPromise]);
|
|
16379
|
+
console.log("[session-manager] Received question answers", {
|
|
16380
|
+
questionId,
|
|
16381
|
+
answerCount: Object.keys(answers).length
|
|
16274
16382
|
});
|
|
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
|
-
});
|
|
16383
|
+
return {
|
|
16384
|
+
behavior: "allow",
|
|
16385
|
+
updatedInput: {
|
|
16386
|
+
questions: input.questions,
|
|
16387
|
+
answers
|
|
16297
16388
|
}
|
|
16298
|
-
|
|
16299
|
-
|
|
16300
|
-
|
|
16301
|
-
|
|
16302
|
-
|
|
16389
|
+
};
|
|
16390
|
+
} catch (error) {
|
|
16391
|
+
console.error("[session-manager] Error waiting for answer:", error);
|
|
16392
|
+
const message = error instanceof Error ? error.message : "Question was cancelled";
|
|
16393
|
+
if (pendingQuestions.has(questionId)) {
|
|
16394
|
+
eventEmitter.emit({
|
|
16395
|
+
type: "question.answered",
|
|
16396
|
+
sessionId,
|
|
16397
|
+
data: { requestId: questionId, cancelled: true }
|
|
16398
|
+
});
|
|
16303
16399
|
}
|
|
16400
|
+
return { behavior: "deny", message };
|
|
16401
|
+
} finally {
|
|
16402
|
+
releaseDeadline();
|
|
16403
|
+
pendingQuestions.delete(questionId);
|
|
16404
|
+
questionResolvers.delete(questionId);
|
|
16304
16405
|
}
|
|
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
|
-
});
|
|
16406
|
+
}
|
|
16407
|
+
async function enterPlanMode(turn, input) {
|
|
16408
|
+
const { session, sessionId } = turn;
|
|
16409
|
+
debugLog("[session-manager] EnterPlanMode requested", { sessionId });
|
|
16410
|
+
try {
|
|
16411
|
+
await applySessionPlanMode(session, true);
|
|
16412
|
+
} catch (error) {
|
|
16413
|
+
const message = error instanceof Error ? error.message : "Failed to persist plan mode";
|
|
16321
16414
|
return {
|
|
16322
|
-
behavior: "
|
|
16323
|
-
|
|
16415
|
+
behavior: "deny",
|
|
16416
|
+
message: `Plan mode could not be persisted safely: ${message}`
|
|
16324
16417
|
};
|
|
16325
16418
|
}
|
|
16326
|
-
|
|
16327
|
-
|
|
16328
|
-
|
|
16329
|
-
}
|
|
16330
|
-
|
|
16331
|
-
|
|
16332
|
-
|
|
16333
|
-
|
|
16334
|
-
|
|
16335
|
-
|
|
16336
|
-
|
|
16337
|
-
|
|
16338
|
-
|
|
16339
|
-
|
|
16419
|
+
eventEmitter.emit({
|
|
16420
|
+
type: "plan.enter-requested",
|
|
16421
|
+
sessionId,
|
|
16422
|
+
data: { sessionId }
|
|
16423
|
+
});
|
|
16424
|
+
return {
|
|
16425
|
+
behavior: "allow",
|
|
16426
|
+
updatedInput: input
|
|
16427
|
+
};
|
|
16428
|
+
}
|
|
16429
|
+
async function requestPlanApproval(turn, input, permissionContext) {
|
|
16430
|
+
const { session, sessionId } = turn;
|
|
16431
|
+
debugLog("[session-manager] ExitPlanMode requested, waiting for user approval", {
|
|
16432
|
+
sessionId
|
|
16433
|
+
});
|
|
16434
|
+
const approvalId = permissionContext?.requestId || generateMessageId();
|
|
16435
|
+
const directPlan = boundedPlan(input?.plan);
|
|
16436
|
+
const requestedAt = Date.now();
|
|
16437
|
+
const approvalRequest = {
|
|
16438
|
+
id: approvalId,
|
|
16439
|
+
sessionId,
|
|
16440
|
+
toolUseId: permissionContext?.toolUseID ?? approvalId,
|
|
16441
|
+
...directPlan,
|
|
16442
|
+
requestedAt,
|
|
16443
|
+
expiresAt: requestedAt + PLAN_APPROVAL_TIMEOUT_MS
|
|
16444
|
+
};
|
|
16445
|
+
if (!canAdmitInteraction(approvalRequest, "approvals")) {
|
|
16446
|
+
return {
|
|
16447
|
+
behavior: "deny",
|
|
16448
|
+
message: "Too many pending interactions. Answer an existing request first."
|
|
16340
16449
|
};
|
|
16341
|
-
|
|
16342
|
-
|
|
16343
|
-
|
|
16344
|
-
|
|
16345
|
-
|
|
16346
|
-
|
|
16347
|
-
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
16357
|
-
|
|
16358
|
-
|
|
16359
|
-
|
|
16360
|
-
|
|
16361
|
-
|
|
16362
|
-
|
|
16363
|
-
|
|
16364
|
-
|
|
16450
|
+
}
|
|
16451
|
+
let rejectOnExpiry;
|
|
16452
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
16453
|
+
rejectOnExpiry = reject;
|
|
16454
|
+
});
|
|
16455
|
+
const deadline = new InteractionDeadline({
|
|
16456
|
+
requestedAt,
|
|
16457
|
+
windowMs: PLAN_APPROVAL_TIMEOUT_MS,
|
|
16458
|
+
onExpire: () => rejectOnExpiry(new Error(interactionExpiryMessage("Plan approval", deadline)))
|
|
16459
|
+
});
|
|
16460
|
+
const releaseDeadline = registerInteractionDeadline("plan-approvals", approvalRequest, deadline);
|
|
16461
|
+
pendingPlanApprovals.set(approvalId, approvalRequest);
|
|
16462
|
+
const approvalPromise = new Promise((resolve, reject) => {
|
|
16463
|
+
planApprovalResolvers.set(approvalId, { resolve, reject });
|
|
16464
|
+
});
|
|
16465
|
+
eventEmitter.emit({
|
|
16466
|
+
type: "plan.approval-requested",
|
|
16467
|
+
sessionId,
|
|
16468
|
+
data: approvalRequest
|
|
16469
|
+
});
|
|
16470
|
+
try {
|
|
16471
|
+
const response = await Promise.race([approvalPromise, timeoutPromise]);
|
|
16472
|
+
console.log("[session-manager] Plan approval result", {
|
|
16473
|
+
approvalId,
|
|
16474
|
+
approved: response.approved,
|
|
16475
|
+
hasFeedback: typeof response.feedback === "string" && response.feedback.length > 0
|
|
16365
16476
|
});
|
|
16366
|
-
|
|
16367
|
-
|
|
16368
|
-
|
|
16369
|
-
|
|
16370
|
-
|
|
16371
|
-
|
|
16372
|
-
|
|
16373
|
-
|
|
16477
|
+
if (response.approved) {
|
|
16478
|
+
try {
|
|
16479
|
+
const control = session.queryControl;
|
|
16480
|
+
if (!control?.setPermissionMode)
|
|
16481
|
+
throw new Error("Claude permission control is unavailable");
|
|
16482
|
+
await control.setPermissionMode(claudePermissionMode(claudeTurnPolicy(session.executionPolicy, turn.options?.readOnly), turn.options?.approvalMode));
|
|
16483
|
+
await applySessionPlanMode(session, false);
|
|
16484
|
+
} catch (error) {
|
|
16374
16485
|
try {
|
|
16375
16486
|
const control = session.queryControl;
|
|
16376
16487
|
if (!control?.setPermissionMode)
|
|
16377
16488
|
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
|
-
};
|
|
16489
|
+
await control.setPermissionMode("plan");
|
|
16490
|
+
} catch {
|
|
16491
|
+
abortSession(sessionId);
|
|
16394
16492
|
}
|
|
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.`;
|
|
16493
|
+
const message = error instanceof Error ? error.message : "Failed to persist plan mode";
|
|
16411
16494
|
return {
|
|
16412
16495
|
behavior: "deny",
|
|
16413
|
-
message:
|
|
16496
|
+
message: `Plan mode could not be exited safely: ${message}`
|
|
16414
16497
|
};
|
|
16415
16498
|
}
|
|
16416
|
-
|
|
16417
|
-
|
|
16418
|
-
|
|
16419
|
-
|
|
16420
|
-
|
|
16421
|
-
|
|
16422
|
-
|
|
16423
|
-
|
|
16424
|
-
|
|
16425
|
-
|
|
16426
|
-
|
|
16427
|
-
|
|
16428
|
-
|
|
16429
|
-
}
|
|
16430
|
-
|
|
16431
|
-
|
|
16432
|
-
|
|
16433
|
-
|
|
16434
|
-
|
|
16499
|
+
turn.planApprovedThisTurn = true;
|
|
16500
|
+
eventEmitter.emit({
|
|
16501
|
+
type: "plan.exit-requested",
|
|
16502
|
+
sessionId,
|
|
16503
|
+
data: { sessionId }
|
|
16504
|
+
});
|
|
16505
|
+
return {
|
|
16506
|
+
behavior: "allow",
|
|
16507
|
+
updatedInput: input
|
|
16508
|
+
};
|
|
16509
|
+
} else {
|
|
16510
|
+
const feedbackMessage = response.feedback ? `User feedback: "${response.feedback}"` : "No specific feedback was provided.";
|
|
16511
|
+
const denyMessage = `User rejected the plan. ${feedbackMessage} Please revise your approach based on this feedback.`;
|
|
16512
|
+
turn.pendingPlanRejectionFeedback = response.feedback ? `I've reviewed the plan and I'd like changes: ${response.feedback}
|
|
16513
|
+
|
|
16514
|
+
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.`;
|
|
16515
|
+
return {
|
|
16516
|
+
behavior: "deny",
|
|
16517
|
+
message: denyMessage
|
|
16518
|
+
};
|
|
16519
|
+
}
|
|
16520
|
+
} catch (error) {
|
|
16521
|
+
console.error("[session-manager] Error waiting for plan approval:", error);
|
|
16522
|
+
const errorMessage = error instanceof Error ? error.message : "Plan approval was cancelled";
|
|
16523
|
+
if (pendingPlanApprovals.has(approvalId)) {
|
|
16524
|
+
eventEmitter.emit({
|
|
16525
|
+
type: "plan.approval-responded",
|
|
16526
|
+
sessionId,
|
|
16527
|
+
data: {
|
|
16528
|
+
requestId: approvalId,
|
|
16529
|
+
approved: false,
|
|
16530
|
+
cancelled: true
|
|
16531
|
+
}
|
|
16532
|
+
});
|
|
16435
16533
|
}
|
|
16534
|
+
return { behavior: "deny", message: errorMessage };
|
|
16535
|
+
} finally {
|
|
16536
|
+
releaseDeadline();
|
|
16537
|
+
pendingPlanApprovals.delete(approvalId);
|
|
16538
|
+
planApprovalResolvers.delete(approvalId);
|
|
16436
16539
|
}
|
|
16540
|
+
}
|
|
16541
|
+
async function turnCanUseTool(turn, toolName, input, permissionContext) {
|
|
16542
|
+
const { session, sessionId } = turn;
|
|
16543
|
+
if (toolName === "AskUserQuestion")
|
|
16544
|
+
return askUserQuestion(turn, input, permissionContext);
|
|
16545
|
+
if (toolName === "EnterPlanMode")
|
|
16546
|
+
return enterPlanMode(turn, input);
|
|
16547
|
+
if (toolName === "ExitPlanMode")
|
|
16548
|
+
return requestPlanApproval(turn, input, permissionContext);
|
|
16437
16549
|
if ((claudeWorkflowResultTools(turn.mcpServerNames).includes(toolName) || claudeArtifactServerTools(turn.mcpServerNames).includes(toolName)) && !session.executionPolicy?.toolPolicy?.deny?.includes(toolName)) {
|
|
16438
16550
|
return { behavior: "allow", updatedInput: input };
|
|
16439
16551
|
}
|
|
@@ -16735,9 +16847,9 @@ function settleTurnEnd(turn) {
|
|
|
16735
16847
|
}
|
|
16736
16848
|
return;
|
|
16737
16849
|
}
|
|
16738
|
-
if (
|
|
16739
|
-
const feedbackPrompt =
|
|
16740
|
-
|
|
16850
|
+
if (turn.pendingPlanRejectionFeedback && !abortController.signal.aborted && !options?._isReprompt && !turn.superseded) {
|
|
16851
|
+
const feedbackPrompt = turn.pendingPlanRejectionFeedback;
|
|
16852
|
+
turn.pendingPlanRejectionFeedback = null;
|
|
16741
16853
|
debugLog("[session-manager] Re-prompting with plan rejection feedback", { sessionId });
|
|
16742
16854
|
session.status = "idle";
|
|
16743
16855
|
session.abortController = undefined;
|
|
@@ -16757,9 +16869,9 @@ function settleTurnEnd(turn) {
|
|
|
16757
16869
|
failureLog: "[session-manager] Failed to re-prompt with plan feedback:"
|
|
16758
16870
|
};
|
|
16759
16871
|
}
|
|
16760
|
-
if (
|
|
16761
|
-
const continuationPrompt =
|
|
16762
|
-
|
|
16872
|
+
if (turn.pendingPlanApprovalContinuation && !abortController.signal.aborted && !options?._isReprompt && !turn.superseded) {
|
|
16873
|
+
const continuationPrompt = turn.pendingPlanApprovalContinuation;
|
|
16874
|
+
turn.pendingPlanApprovalContinuation = null;
|
|
16763
16875
|
debugLog("[session-manager] Re-prompting after approved-plan ExitPlanMode failure", {
|
|
16764
16876
|
sessionId
|
|
16765
16877
|
});
|
|
@@ -17326,14 +17438,14 @@ var SESSION_ACTIVITY_BATCH_LIMITS = {
|
|
|
17326
17438
|
};
|
|
17327
17439
|
var SESSION_ACTIVITY_STATES = new Set(["idle", "working", "waiting", "missing"]);
|
|
17328
17440
|
var encoder3 = new TextEncoder;
|
|
17329
|
-
function
|
|
17441
|
+
function isRecord4(value) {
|
|
17330
17442
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17331
17443
|
}
|
|
17332
17444
|
function isBatchableSessionId(value) {
|
|
17333
17445
|
return typeof value === "string" && value.length > 0 && encoder3.encode(value).byteLength <= SESSION_ACTIVITY_BATCH_LIMITS.maxSessionIdBytes;
|
|
17334
17446
|
}
|
|
17335
17447
|
function parseSessionActivityObservation(value) {
|
|
17336
|
-
if (!
|
|
17448
|
+
if (!isRecord4(value))
|
|
17337
17449
|
return;
|
|
17338
17450
|
const { activity, readyForInput, completionPending, asyncQuestionItemIds, closing } = value;
|
|
17339
17451
|
const { contentRevision } = value;
|
|
@@ -17367,7 +17479,7 @@ function isContentRevision(value) {
|
|
|
17367
17479
|
return typeof value === "string" && value.length > 0 && value.length <= SESSION_ACTIVITY_BATCH_LIMITS.maxContentRevisionLength;
|
|
17368
17480
|
}
|
|
17369
17481
|
function parseSessionActivityBatchRequest(value) {
|
|
17370
|
-
if (!
|
|
17482
|
+
if (!isRecord4(value))
|
|
17371
17483
|
return { ok: false, error: "Expected a JSON object" };
|
|
17372
17484
|
if (value.version !== SESSION_ACTIVITY_BATCH_VERSION) {
|
|
17373
17485
|
return { ok: false, error: "Unsupported activity batch version" };
|
|
@@ -17610,7 +17722,7 @@ function decodeLocator(locator) {
|
|
|
17610
17722
|
}
|
|
17611
17723
|
try {
|
|
17612
17724
|
const body = JSON.parse(Buffer3.from(locator.slice(LOCATOR_PREFIX.length), "base64url").toString("utf8"));
|
|
17613
|
-
if (!
|
|
17725
|
+
if (!isRecord5(body))
|
|
17614
17726
|
return;
|
|
17615
17727
|
const { m, p, d } = body;
|
|
17616
17728
|
if (typeof m !== "string" || typeof d !== "string" || !Array.isArray(p))
|
|
@@ -17630,12 +17742,12 @@ var DETAIL_FIELDS = new Set([
|
|
|
17630
17742
|
"toolDiff",
|
|
17631
17743
|
"fileDataUrl"
|
|
17632
17744
|
]);
|
|
17633
|
-
function
|
|
17745
|
+
function isRecord5(value) {
|
|
17634
17746
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
17635
17747
|
}
|
|
17636
17748
|
function heavyDiff(part) {
|
|
17637
17749
|
const diff = part.toolDiff;
|
|
17638
|
-
if (!
|
|
17750
|
+
if (!isRecord5(diff))
|
|
17639
17751
|
return;
|
|
17640
17752
|
return typeof diff.diff === "string" || typeof diff.before === "string" || typeof diff.after === "string" ? diff : undefined;
|
|
17641
17753
|
}
|
|
@@ -17657,7 +17769,7 @@ function detailPayload(part) {
|
|
|
17657
17769
|
return Object.keys(payload).length > 0 ? payload : undefined;
|
|
17658
17770
|
}
|
|
17659
17771
|
function detailInputs(part) {
|
|
17660
|
-
const diff =
|
|
17772
|
+
const diff = isRecord5(part.toolDiff) ? part.toolDiff : undefined;
|
|
17661
17773
|
return [
|
|
17662
17774
|
part.type,
|
|
17663
17775
|
part.content,
|
|
@@ -17687,7 +17799,7 @@ function digestPayload(payload) {
|
|
|
17687
17799
|
};
|
|
17688
17800
|
}
|
|
17689
17801
|
function topLevelKey(part, index) {
|
|
17690
|
-
if (
|
|
17802
|
+
if (isRecord5(part)) {
|
|
17691
17803
|
if (typeof part.sourcePartId === "string" && part.sourcePartId.length <= 512) {
|
|
17692
17804
|
return `s:${part.sourcePartId}`;
|
|
17693
17805
|
}
|
|
@@ -17698,7 +17810,7 @@ function topLevelKey(part, index) {
|
|
|
17698
17810
|
return `i:${index}`;
|
|
17699
17811
|
}
|
|
17700
17812
|
function summarizePart(raw, messageId, path, depth) {
|
|
17701
|
-
if (!
|
|
17813
|
+
if (!isRecord5(raw) || depth > MAX_SUMMARY_DEPTH)
|
|
17702
17814
|
return raw;
|
|
17703
17815
|
const part = raw;
|
|
17704
17816
|
let summary;
|
|
@@ -17750,7 +17862,7 @@ function summarizePart(raw, messageId, path, depth) {
|
|
|
17750
17862
|
if (changed)
|
|
17751
17863
|
writable()[field] = next;
|
|
17752
17864
|
}
|
|
17753
|
-
if (
|
|
17865
|
+
if (isRecord5(part.task)) {
|
|
17754
17866
|
const task = summarizePart(part.task, messageId, [...path, "task"], depth + 1);
|
|
17755
17867
|
if (task !== part.task)
|
|
17756
17868
|
writable().task = task;
|
|
@@ -17758,7 +17870,7 @@ function summarizePart(raw, messageId, path, depth) {
|
|
|
17758
17870
|
return summary ?? part;
|
|
17759
17871
|
}
|
|
17760
17872
|
function isSummarizable(message) {
|
|
17761
|
-
return
|
|
17873
|
+
return isRecord5(message) && typeof message.id === "string" && Array.isArray(message.parts);
|
|
17762
17874
|
}
|
|
17763
17875
|
function summarizeBridgeMessage(message) {
|
|
17764
17876
|
if (!isSummarizable(message))
|
|
@@ -17784,15 +17896,15 @@ function resolvePath2(message, path) {
|
|
|
17784
17896
|
let current;
|
|
17785
17897
|
if (top.startsWith("s:")) {
|
|
17786
17898
|
const id = top.slice(2);
|
|
17787
|
-
current = message.parts.find((part) =>
|
|
17899
|
+
current = message.parts.find((part) => isRecord5(part) && part.sourcePartId === id);
|
|
17788
17900
|
} else if (top.startsWith("t:")) {
|
|
17789
17901
|
const id = top.slice(2);
|
|
17790
|
-
current = message.parts.find((part) =>
|
|
17902
|
+
current = message.parts.find((part) => isRecord5(part) && part.toolUseId === id && typeof part.sourcePartId !== "string");
|
|
17791
17903
|
} else if (top.startsWith("i:")) {
|
|
17792
17904
|
current = message.parts[Number(top.slice(2))];
|
|
17793
17905
|
}
|
|
17794
17906
|
for (let index = 0;index < rest.length; index += 1) {
|
|
17795
|
-
if (!
|
|
17907
|
+
if (!isRecord5(current))
|
|
17796
17908
|
return;
|
|
17797
17909
|
const field = rest[index];
|
|
17798
17910
|
if (field === "task") {
|
|
@@ -17825,7 +17937,7 @@ function readBridgeTranscriptDetail(messages, locator) {
|
|
|
17825
17937
|
if (!message)
|
|
17826
17938
|
return { version: 1, status: "missing" };
|
|
17827
17939
|
const part = resolvePath2(message, body.p);
|
|
17828
|
-
if (!
|
|
17940
|
+
if (!isRecord5(part))
|
|
17829
17941
|
return { version: 1, status: "missing" };
|
|
17830
17942
|
const payload = detailPayload(part);
|
|
17831
17943
|
if (!payload)
|
|
@@ -17928,7 +18040,7 @@ function decodePageCursor(cursor) {
|
|
|
17928
18040
|
}
|
|
17929
18041
|
try {
|
|
17930
18042
|
const body = JSON.parse(Buffer3.from(cursor.slice(PAGE_CURSOR_PREFIX.length), "base64url").toString("utf8"));
|
|
17931
|
-
if (!
|
|
18043
|
+
if (!isRecord5(body))
|
|
17932
18044
|
return;
|
|
17933
18045
|
const { g, e, b } = body;
|
|
17934
18046
|
if (typeof g !== "string" || typeof e !== "string")
|
|
@@ -18229,7 +18341,7 @@ session.get("/:id", async (c) => {
|
|
|
18229
18341
|
structuredOutputRequestId: sessionData.structuredOutputRequestId,
|
|
18230
18342
|
structuredOutput: sessionData.structuredOutput,
|
|
18231
18343
|
contextUsage: sessionData.inProgressUsage ?? sessionData.usage,
|
|
18232
|
-
policy: sessionData
|
|
18344
|
+
policy: claudeReportedPolicy(sessionData),
|
|
18233
18345
|
rateLimits: sessionData.rateLimits,
|
|
18234
18346
|
promptSuggestion: sessionData.promptSuggestion,
|
|
18235
18347
|
activity: sessionData.activity,
|
|
@@ -18329,7 +18441,7 @@ session.post("/:id/config", async (c) => {
|
|
|
18329
18441
|
} else if (permissionMode === "plan" || permissionMode === "bypassPermissions") {
|
|
18330
18442
|
await setSessionPreferences(sessionData.id, { planMode: permissionMode === "plan" });
|
|
18331
18443
|
}
|
|
18332
|
-
return c.json({ ok: true, policy: sessionData
|
|
18444
|
+
return c.json({ ok: true, policy: claudeReportedPolicy(sessionData) });
|
|
18333
18445
|
});
|
|
18334
18446
|
session.get("/:id/commands", async (c) => {
|
|
18335
18447
|
try {
|