@openscout/scout 0.2.64 → 0.2.65
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 +24 -12
- package/bin/openscout-runtime.mjs +42 -0
- package/dist/client/assets/index-BOdG2WWL.css +1 -0
- package/dist/client/assets/index-mL1LA8IG.js +159 -0
- package/dist/client/index.html +2 -2
- package/dist/main.mjs +2404 -550
- package/dist/pair-supervisor.mjs +516 -147
- package/dist/runtime/base-daemon.mjs +1554 -0
- package/dist/runtime/broker-daemon.mjs +42956 -0
- package/dist/runtime/broker-process-manager.mjs +880 -0
- package/dist/runtime/mesh-discover.mjs +906 -0
- package/dist/scout-control-plane-web.mjs +3359 -1721
- package/dist/scout-web-server.mjs +3359 -1721
- package/package.json +4 -6
- package/dist/client/assets/arc.es-DglF81f4.js +0 -188
- package/dist/client/assets/index-BRvY3oC-.js +0 -159
- package/dist/client/assets/index-CUNnl56m.css +0 -1
package/dist/pair-supervisor.mjs
CHANGED
|
@@ -5930,8 +5930,8 @@ class CodexAdapter extends BaseAdapter {
|
|
|
5930
5930
|
const resumed = await this.request("thread/resume", {
|
|
5931
5931
|
threadId: storedThreadId,
|
|
5932
5932
|
cwd: options.cwd,
|
|
5933
|
-
approvalPolicy: "never",
|
|
5934
|
-
sandbox: "danger-full-access",
|
|
5933
|
+
approvalPolicy: options.approvalPolicy ?? "never",
|
|
5934
|
+
sandbox: options.sandbox ?? "danger-full-access",
|
|
5935
5935
|
baseInstructions: options.systemPrompt,
|
|
5936
5936
|
persistExtendedHistory: true
|
|
5937
5937
|
});
|
|
@@ -5961,8 +5961,8 @@ class CodexAdapter extends BaseAdapter {
|
|
|
5961
5961
|
}
|
|
5962
5962
|
const started = await this.request("thread/start", {
|
|
5963
5963
|
cwd: options.cwd,
|
|
5964
|
-
approvalPolicy: "never",
|
|
5965
|
-
sandbox: "danger-full-access",
|
|
5964
|
+
approvalPolicy: options.approvalPolicy ?? "never",
|
|
5965
|
+
sandbox: options.sandbox ?? "danger-full-access",
|
|
5966
5966
|
baseInstructions: options.systemPrompt,
|
|
5967
5967
|
ephemeral: false,
|
|
5968
5968
|
experimentalRawEvents: false,
|
|
@@ -9099,6 +9099,40 @@ var BUILT_IN_HARNESS_CATALOG = [
|
|
|
9099
9099
|
cwdFlag: "--cwd"
|
|
9100
9100
|
},
|
|
9101
9101
|
capabilities: ["chat", "invoke", "deliver", "review", "execute"]
|
|
9102
|
+
},
|
|
9103
|
+
{
|
|
9104
|
+
name: "cursor",
|
|
9105
|
+
harness: "cursor",
|
|
9106
|
+
label: "Cursor Agent SDK",
|
|
9107
|
+
description: "Programmatic agent execution via the Cursor Agent SDK",
|
|
9108
|
+
homepage: "https://cursor.com",
|
|
9109
|
+
tags: ["coding", "sdk", "cursor"],
|
|
9110
|
+
featured: true,
|
|
9111
|
+
order: 3,
|
|
9112
|
+
support: {
|
|
9113
|
+
...DEFAULT_SUPPORT,
|
|
9114
|
+
workspace: true,
|
|
9115
|
+
collaboration: true,
|
|
9116
|
+
files: true
|
|
9117
|
+
},
|
|
9118
|
+
install: {
|
|
9119
|
+
binary: "mw",
|
|
9120
|
+
requires: ["bun"],
|
|
9121
|
+
macos: "bun install -g missionwriter",
|
|
9122
|
+
linux: "bun install -g missionwriter",
|
|
9123
|
+
windows: "bun install -g missionwriter"
|
|
9124
|
+
},
|
|
9125
|
+
readiness: {
|
|
9126
|
+
anyOf: [
|
|
9127
|
+
{ kind: "env", key: "CURSOR_API_KEY" }
|
|
9128
|
+
],
|
|
9129
|
+
notReadyMessage: "Cursor Agent SDK is available but CURSOR_API_KEY is not set."
|
|
9130
|
+
},
|
|
9131
|
+
capabilities: ["invoke", "deliver", "execute"],
|
|
9132
|
+
metadata: {
|
|
9133
|
+
sdkType: "cursor",
|
|
9134
|
+
invocationModel: "one_shot"
|
|
9135
|
+
}
|
|
9102
9136
|
}
|
|
9103
9137
|
];
|
|
9104
9138
|
function expandHomePath(value) {
|
|
@@ -10673,6 +10707,22 @@ function diagnoseAgentIdentity(identity, candidates) {
|
|
|
10673
10707
|
var parseAgentSelector = parseAgentIdentity;
|
|
10674
10708
|
var formatAgentSelector = formatAgentIdentity;
|
|
10675
10709
|
var resolveAgentSelector = resolveAgentIdentity;
|
|
10710
|
+
// ../protocol/src/permission-policy.ts
|
|
10711
|
+
var SCOUT_PERMISSION_PROFILES = [
|
|
10712
|
+
"observe",
|
|
10713
|
+
"review",
|
|
10714
|
+
"workspace_write",
|
|
10715
|
+
"sandboxed_write",
|
|
10716
|
+
"trusted_local",
|
|
10717
|
+
"external_sandbox"
|
|
10718
|
+
];
|
|
10719
|
+
function normalizeScoutPermissionProfile(value) {
|
|
10720
|
+
const normalized = value?.trim().replaceAll("-", "_");
|
|
10721
|
+
return SCOUT_PERMISSION_PROFILES.find((profile) => profile === normalized);
|
|
10722
|
+
}
|
|
10723
|
+
function formatScoutPermissionProfiles() {
|
|
10724
|
+
return SCOUT_PERMISSION_PROFILES.join("|");
|
|
10725
|
+
}
|
|
10676
10726
|
// ../runtime/src/user-project-hints.ts
|
|
10677
10727
|
import { readdir, readFile as readFile3, stat } from "fs/promises";
|
|
10678
10728
|
import { homedir as homedir11 } from "os";
|
|
@@ -10998,7 +11048,7 @@ async function collectUserLevelProjectRootHints(options = {}) {
|
|
|
10998
11048
|
// ../runtime/src/setup.ts
|
|
10999
11049
|
var SCOUT_AGENT_ID = "scout";
|
|
11000
11050
|
var SCOUT_PRIMARY_CONVERSATION_ID = "dm.scout.primary";
|
|
11001
|
-
var MANAGED_AGENT_HARNESSES = ["claude", "codex"];
|
|
11051
|
+
var MANAGED_AGENT_HARNESSES = ["claude", "codex", "cursor"];
|
|
11002
11052
|
function titleCaseWords(value) {
|
|
11003
11053
|
return value.split(/[\s._-]+/).map((part) => part.trim()).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
11004
11054
|
}
|
|
@@ -11117,6 +11167,10 @@ var PROJECT_HARNESS_MARKERS = {
|
|
|
11117
11167
|
"AGENTS.md",
|
|
11118
11168
|
"CODEX.md",
|
|
11119
11169
|
".codex"
|
|
11170
|
+
],
|
|
11171
|
+
cursor: [
|
|
11172
|
+
".cursor",
|
|
11173
|
+
".cursorrules"
|
|
11120
11174
|
]
|
|
11121
11175
|
};
|
|
11122
11176
|
function partitionFlatAndNestedMarkers(markers) {
|
|
@@ -11181,6 +11235,9 @@ function normalizeManagedHarness(value, fallback) {
|
|
|
11181
11235
|
if (value === "claude") {
|
|
11182
11236
|
return "claude";
|
|
11183
11237
|
}
|
|
11238
|
+
if (value === "cursor") {
|
|
11239
|
+
return "cursor";
|
|
11240
|
+
}
|
|
11184
11241
|
return fallback;
|
|
11185
11242
|
}
|
|
11186
11243
|
function titleCase(value) {
|
|
@@ -11354,12 +11411,15 @@ function normalizeCapabilities(value) {
|
|
|
11354
11411
|
return normalized.length > 0 ? normalized : [...DEFAULT_CAPABILITIES];
|
|
11355
11412
|
}
|
|
11356
11413
|
function normalizeHarness(value, fallback) {
|
|
11357
|
-
return value === "codex" ? "codex" : value === "claude" ? "claude" : fallback;
|
|
11414
|
+
return value === "codex" ? "codex" : value === "claude" ? "claude" : value === "cursor" ? "cursor" : fallback;
|
|
11358
11415
|
}
|
|
11359
11416
|
function normalizeTransport(value, harness, fallback) {
|
|
11360
11417
|
if (harness === "codex") {
|
|
11361
11418
|
return "codex_app_server";
|
|
11362
11419
|
}
|
|
11420
|
+
if (harness === "cursor") {
|
|
11421
|
+
return "cursor_exec";
|
|
11422
|
+
}
|
|
11363
11423
|
if (value === "claude_stream_json") {
|
|
11364
11424
|
return "claude_stream_json";
|
|
11365
11425
|
}
|
|
@@ -11369,11 +11429,13 @@ function normalizeTransport(value, harness, fallback) {
|
|
|
11369
11429
|
return fallback === "codex_app_server" ? "claude_stream_json" : fallback;
|
|
11370
11430
|
}
|
|
11371
11431
|
function normalizeHarnessProfile(profile, harness, options) {
|
|
11432
|
+
const permissionProfile = normalizeScoutPermissionProfile(typeof profile?.permissionProfile === "string" ? profile.permissionProfile : options.fallbackPermissionProfile);
|
|
11372
11433
|
return {
|
|
11373
11434
|
cwd: profile?.cwd ? normalizeProjectRelativePath(options.projectRoot, profile.cwd) : options.projectRoot,
|
|
11374
11435
|
transport: normalizeTransport(profile?.transport, harness, harness === "codex" ? "codex_app_server" : DEFAULT_TRANSPORT),
|
|
11375
11436
|
sessionId: normalizeTmuxSessionName(profile?.sessionId, `${options.sessionKey}-${harness}`, options.sessionPrefix),
|
|
11376
|
-
launchArgs: normalizeLaunchArgs(profile?.launchArgs ?? options.fallbackLaunchArgs)
|
|
11437
|
+
launchArgs: normalizeLaunchArgs(profile?.launchArgs ?? options.fallbackLaunchArgs),
|
|
11438
|
+
...permissionProfile ? { permissionProfile } : {}
|
|
11377
11439
|
};
|
|
11378
11440
|
}
|
|
11379
11441
|
function buildHarnessProfiles(input) {
|
|
@@ -11389,7 +11451,8 @@ function buildHarnessProfiles(input) {
|
|
|
11389
11451
|
projectRoot: input.projectRoot,
|
|
11390
11452
|
sessionKey: input.sessionKey,
|
|
11391
11453
|
sessionPrefix: input.sessionPrefix,
|
|
11392
|
-
fallbackLaunchArgs: input.launchArgs
|
|
11454
|
+
fallbackLaunchArgs: input.launchArgs,
|
|
11455
|
+
fallbackPermissionProfile: input.permissionProfile
|
|
11393
11456
|
});
|
|
11394
11457
|
}
|
|
11395
11458
|
for (const harness of MANAGED_AGENT_HARNESSES) {
|
|
@@ -11402,7 +11465,8 @@ function buildHarnessProfiles(input) {
|
|
|
11402
11465
|
projectRoot: input.projectRoot,
|
|
11403
11466
|
sessionKey: input.sessionKey,
|
|
11404
11467
|
sessionPrefix: input.sessionPrefix,
|
|
11405
|
-
fallbackLaunchArgs: profiles[harness]?.launchArgs ?? input.launchArgs
|
|
11468
|
+
fallbackLaunchArgs: profiles[harness]?.launchArgs ?? input.launchArgs,
|
|
11469
|
+
fallbackPermissionProfile: profiles[harness]?.permissionProfile ?? input.permissionProfile
|
|
11406
11470
|
});
|
|
11407
11471
|
}
|
|
11408
11472
|
}
|
|
@@ -11410,7 +11474,8 @@ function buildHarnessProfiles(input) {
|
|
|
11410
11474
|
projectRoot: input.projectRoot,
|
|
11411
11475
|
sessionKey: input.sessionKey,
|
|
11412
11476
|
sessionPrefix: input.sessionPrefix,
|
|
11413
|
-
fallbackLaunchArgs: input.launchArgs
|
|
11477
|
+
fallbackLaunchArgs: input.launchArgs,
|
|
11478
|
+
fallbackPermissionProfile: input.permissionProfile
|
|
11414
11479
|
});
|
|
11415
11480
|
profiles[input.defaultHarness] = ensuredHarness;
|
|
11416
11481
|
return profiles;
|
|
@@ -11860,7 +11925,8 @@ async function readLegacyAgentRegistry() {
|
|
|
11860
11925
|
async function detectHarnessMarkers(projectRoot) {
|
|
11861
11926
|
const detected = {
|
|
11862
11927
|
claude: [],
|
|
11863
|
-
codex: []
|
|
11928
|
+
codex: [],
|
|
11929
|
+
cursor: []
|
|
11864
11930
|
};
|
|
11865
11931
|
for (const harness of MANAGED_AGENT_HARNESSES) {
|
|
11866
11932
|
for (const marker of PROJECT_HARNESS_MARKERS[harness]) {
|
|
@@ -12442,7 +12508,8 @@ async function resolveManifestBackedAgent(projectRoot, config, settings, overrid
|
|
|
12442
12508
|
defaultHarness,
|
|
12443
12509
|
profiles: config.agent?.runtime?.profiles,
|
|
12444
12510
|
runtime: runtimeDefaults,
|
|
12445
|
-
launchArgs: normalizeLaunchArgs(runtimeDefaults?.launchArgs)
|
|
12511
|
+
launchArgs: normalizeLaunchArgs(runtimeDefaults?.launchArgs),
|
|
12512
|
+
permissionProfile: normalizeScoutPermissionProfile(runtimeDefaults?.permissionProfile)
|
|
12446
12513
|
});
|
|
12447
12514
|
const view = resolvedRuntimeView(defaultHarness, harnessProfiles);
|
|
12448
12515
|
const base = {
|
|
@@ -12517,7 +12584,7 @@ async function buildProjectInventoryEntry(agent, sourceRoot) {
|
|
|
12517
12584
|
});
|
|
12518
12585
|
}
|
|
12519
12586
|
}
|
|
12520
|
-
if (manifestDefaultHarness === "claude" || manifestDefaultHarness === "codex") {
|
|
12587
|
+
if (manifestDefaultHarness === "claude" || manifestDefaultHarness === "codex" || manifestDefaultHarness === "cursor") {
|
|
12521
12588
|
harnesses.set(manifestDefaultHarness, {
|
|
12522
12589
|
harness: manifestDefaultHarness,
|
|
12523
12590
|
source: "manifest",
|
|
@@ -12802,6 +12869,29 @@ function buildManagedAgentShellExports(options) {
|
|
|
12802
12869
|
}
|
|
12803
12870
|
|
|
12804
12871
|
// ../runtime/src/claude-stream-json.ts
|
|
12872
|
+
function resolveRequesterTimeoutMs(timeoutMs) {
|
|
12873
|
+
if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0) {
|
|
12874
|
+
return timeoutMs;
|
|
12875
|
+
}
|
|
12876
|
+
return null;
|
|
12877
|
+
}
|
|
12878
|
+
function waitForRequesterResult(promise, timeoutMs, label) {
|
|
12879
|
+
const effectiveTimeoutMs = resolveRequesterTimeoutMs(timeoutMs);
|
|
12880
|
+
if (effectiveTimeoutMs === null) {
|
|
12881
|
+
return promise;
|
|
12882
|
+
}
|
|
12883
|
+
let timer = null;
|
|
12884
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
12885
|
+
timer = setTimeout(() => {
|
|
12886
|
+
reject(new Error(`Timed out after ${effectiveTimeoutMs}ms waiting for ${label}.`));
|
|
12887
|
+
}, effectiveTimeoutMs);
|
|
12888
|
+
});
|
|
12889
|
+
return Promise.race([promise, timeout]).finally(() => {
|
|
12890
|
+
if (timer) {
|
|
12891
|
+
clearTimeout(timer);
|
|
12892
|
+
}
|
|
12893
|
+
});
|
|
12894
|
+
}
|
|
12805
12895
|
function resolveClaudeStreamJsonOutput(result, fallbackParts) {
|
|
12806
12896
|
const trimmedResult = result?.trim();
|
|
12807
12897
|
if (trimmedResult) {
|
|
@@ -12898,7 +12988,7 @@ class ClaudeStreamJsonSession {
|
|
|
12898
12988
|
sessionId: this.claudeSessionId
|
|
12899
12989
|
};
|
|
12900
12990
|
}
|
|
12901
|
-
async invoke(prompt,
|
|
12991
|
+
async invoke(prompt, timeoutMs) {
|
|
12902
12992
|
await this.ensureStarted();
|
|
12903
12993
|
if (!this.process?.stdin) {
|
|
12904
12994
|
throw new Error(`Claude stream-json session for ${this.options.agentName} is not running.`);
|
|
@@ -12910,13 +13000,10 @@ class ClaudeStreamJsonSession {
|
|
|
12910
13000
|
const turn = {
|
|
12911
13001
|
id: randomUUID(),
|
|
12912
13002
|
output: [],
|
|
12913
|
-
timer: null,
|
|
12914
|
-
stallMs: stallTimeoutMs,
|
|
12915
13003
|
resolve: resolve4,
|
|
12916
13004
|
reject
|
|
12917
13005
|
};
|
|
12918
13006
|
this.activeTurn = turn;
|
|
12919
|
-
this.resetTurnWatchdog(turn);
|
|
12920
13007
|
});
|
|
12921
13008
|
const payload = JSON.stringify({
|
|
12922
13009
|
type: "user",
|
|
@@ -12929,7 +13016,7 @@ class ClaudeStreamJsonSession {
|
|
|
12929
13016
|
}) + `
|
|
12930
13017
|
`;
|
|
12931
13018
|
this.process.stdin.write(payload);
|
|
12932
|
-
const output = await outputPromise;
|
|
13019
|
+
const output = await waitForRequesterResult(outputPromise, timeoutMs, this.options.agentName);
|
|
12933
13020
|
return {
|
|
12934
13021
|
output,
|
|
12935
13022
|
sessionId: this.claudeSessionId
|
|
@@ -12951,27 +13038,10 @@ class ClaudeStreamJsonSession {
|
|
|
12951
13038
|
}) + `
|
|
12952
13039
|
`);
|
|
12953
13040
|
}
|
|
12954
|
-
resetTurnWatchdog(turn) {
|
|
12955
|
-
if (turn.timer) {
|
|
12956
|
-
clearTimeout(turn.timer);
|
|
12957
|
-
}
|
|
12958
|
-
turn.timer = setTimeout(() => {
|
|
12959
|
-
this.interrupt().catch(() => {
|
|
12960
|
-
return;
|
|
12961
|
-
});
|
|
12962
|
-
if (this.activeTurn?.id === turn.id) {
|
|
12963
|
-
this.activeTurn = null;
|
|
12964
|
-
}
|
|
12965
|
-
turn.reject(new Error(`${this.options.agentName} stalled \u2014 no stream event in ${turn.stallMs}ms`));
|
|
12966
|
-
}, turn.stallMs);
|
|
12967
|
-
}
|
|
12968
13041
|
async shutdown(options = {}) {
|
|
12969
13042
|
const turn = this.activeTurn;
|
|
12970
13043
|
this.activeTurn = null;
|
|
12971
13044
|
if (turn) {
|
|
12972
|
-
if (turn.timer) {
|
|
12973
|
-
clearTimeout(turn.timer);
|
|
12974
|
-
}
|
|
12975
13045
|
turn.reject(new Error(`Claude stream-json session for ${this.options.agentName} was shut down.`));
|
|
12976
13046
|
}
|
|
12977
13047
|
const child = this.process;
|
|
@@ -13049,9 +13119,6 @@ class ClaudeStreamJsonSession {
|
|
|
13049
13119
|
if (this.activeTurn) {
|
|
13050
13120
|
const turn = this.activeTurn;
|
|
13051
13121
|
this.activeTurn = null;
|
|
13052
|
-
if (turn.timer) {
|
|
13053
|
-
clearTimeout(turn.timer);
|
|
13054
|
-
}
|
|
13055
13122
|
turn.reject(new Error(`Claude process error: ${error.message}`));
|
|
13056
13123
|
}
|
|
13057
13124
|
});
|
|
@@ -13082,9 +13149,6 @@ class ClaudeStreamJsonSession {
|
|
|
13082
13149
|
if (code !== 0 && this.activeTurn) {
|
|
13083
13150
|
const turn = this.activeTurn;
|
|
13084
13151
|
this.activeTurn = null;
|
|
13085
|
-
if (turn.timer) {
|
|
13086
|
-
clearTimeout(turn.timer);
|
|
13087
|
-
}
|
|
13088
13152
|
turn.reject(new Error(`Claude exited with code ${code}`));
|
|
13089
13153
|
}
|
|
13090
13154
|
});
|
|
@@ -13106,7 +13170,6 @@ class ClaudeStreamJsonSession {
|
|
|
13106
13170
|
if (!turn) {
|
|
13107
13171
|
return;
|
|
13108
13172
|
}
|
|
13109
|
-
this.resetTurnWatchdog(turn);
|
|
13110
13173
|
if (event.type === "assistant") {
|
|
13111
13174
|
const content = event.message?.content ?? event.content ?? [];
|
|
13112
13175
|
for (const part of content) {
|
|
@@ -13118,17 +13181,11 @@ class ClaudeStreamJsonSession {
|
|
|
13118
13181
|
}
|
|
13119
13182
|
if (event.type === "result") {
|
|
13120
13183
|
this.activeTurn = null;
|
|
13121
|
-
if (turn.timer) {
|
|
13122
|
-
clearTimeout(turn.timer);
|
|
13123
|
-
}
|
|
13124
13184
|
turn.resolve(resolveClaudeStreamJsonOutput(event.result, turn.output));
|
|
13125
13185
|
return;
|
|
13126
13186
|
}
|
|
13127
13187
|
if (event.type === "error") {
|
|
13128
13188
|
this.activeTurn = null;
|
|
13129
|
-
if (turn.timer) {
|
|
13130
|
-
clearTimeout(turn.timer);
|
|
13131
|
-
}
|
|
13132
13189
|
turn.reject(new Error(event.error?.message ?? event.message ?? "Unknown Claude error"));
|
|
13133
13190
|
}
|
|
13134
13191
|
}
|
|
@@ -13181,10 +13238,17 @@ function normalizeCodexModelValue(value) {
|
|
|
13181
13238
|
const trimmed = value?.trim();
|
|
13182
13239
|
return trimmed ? trimmed : null;
|
|
13183
13240
|
}
|
|
13241
|
+
function normalizeCodexReasoningEffortValue(value) {
|
|
13242
|
+
const trimmed = value?.trim();
|
|
13243
|
+
return trimmed ? trimmed : null;
|
|
13244
|
+
}
|
|
13184
13245
|
function encodeCodexModelConfig(model) {
|
|
13185
13246
|
return `model=${JSON.stringify(model)}`;
|
|
13186
13247
|
}
|
|
13187
|
-
function
|
|
13248
|
+
function encodeCodexReasoningEffortConfig(reasoningEffort) {
|
|
13249
|
+
return `model_reasoning_effort=${JSON.stringify(reasoningEffort)}`;
|
|
13250
|
+
}
|
|
13251
|
+
function parseCodexConfigValue(value, expectedKey) {
|
|
13188
13252
|
const trimmed = value?.trim();
|
|
13189
13253
|
if (!trimmed) {
|
|
13190
13254
|
return null;
|
|
@@ -13194,7 +13258,7 @@ function parseCodexModelConfig(value) {
|
|
|
13194
13258
|
return null;
|
|
13195
13259
|
}
|
|
13196
13260
|
const key = trimmed.slice(0, separatorIndex).trim();
|
|
13197
|
-
if (key !==
|
|
13261
|
+
if (key !== expectedKey) {
|
|
13198
13262
|
return null;
|
|
13199
13263
|
}
|
|
13200
13264
|
const rawValue = trimmed.slice(separatorIndex + 1).trim();
|
|
@@ -13206,6 +13270,12 @@ function parseCodexModelConfig(value) {
|
|
|
13206
13270
|
}
|
|
13207
13271
|
return rawValue;
|
|
13208
13272
|
}
|
|
13273
|
+
function parseCodexModelConfig(value) {
|
|
13274
|
+
return parseCodexConfigValue(value, "model");
|
|
13275
|
+
}
|
|
13276
|
+
function parseCodexReasoningEffortConfig(value) {
|
|
13277
|
+
return parseCodexConfigValue(value, "model_reasoning_effort");
|
|
13278
|
+
}
|
|
13209
13279
|
function normalizeCodexAppServerLaunchArgs(launchArgs) {
|
|
13210
13280
|
const args = Array.isArray(launchArgs) ? launchArgs.map((entry) => String(entry).trim()).filter(Boolean) : [];
|
|
13211
13281
|
const normalized = [];
|
|
@@ -13235,11 +13305,36 @@ function normalizeCodexAppServerLaunchArgs(launchArgs) {
|
|
|
13235
13305
|
continue;
|
|
13236
13306
|
}
|
|
13237
13307
|
}
|
|
13308
|
+
if (current === "--reasoning-effort" || current === "--effort") {
|
|
13309
|
+
const reasoningEffort = normalizeCodexReasoningEffortValue(args[index + 1]);
|
|
13310
|
+
if (reasoningEffort) {
|
|
13311
|
+
normalized.push("-c", encodeCodexReasoningEffortConfig(reasoningEffort));
|
|
13312
|
+
index += 1;
|
|
13313
|
+
continue;
|
|
13314
|
+
}
|
|
13315
|
+
normalized.push(current);
|
|
13316
|
+
continue;
|
|
13317
|
+
}
|
|
13318
|
+
if (current.startsWith("--reasoning-effort=")) {
|
|
13319
|
+
const reasoningEffort = normalizeCodexReasoningEffortValue(current.slice("--reasoning-effort=".length));
|
|
13320
|
+
if (reasoningEffort) {
|
|
13321
|
+
normalized.push("-c", encodeCodexReasoningEffortConfig(reasoningEffort));
|
|
13322
|
+
continue;
|
|
13323
|
+
}
|
|
13324
|
+
}
|
|
13325
|
+
if (current.startsWith("--effort=")) {
|
|
13326
|
+
const reasoningEffort = normalizeCodexReasoningEffortValue(current.slice("--effort=".length));
|
|
13327
|
+
if (reasoningEffort) {
|
|
13328
|
+
normalized.push("-c", encodeCodexReasoningEffortConfig(reasoningEffort));
|
|
13329
|
+
continue;
|
|
13330
|
+
}
|
|
13331
|
+
}
|
|
13238
13332
|
if (current === "-c" || current === "--config") {
|
|
13239
13333
|
const next = args[index + 1];
|
|
13240
13334
|
if (typeof next === "string") {
|
|
13241
13335
|
const model = parseCodexModelConfig(next);
|
|
13242
|
-
|
|
13336
|
+
const reasoningEffort = parseCodexReasoningEffortConfig(next);
|
|
13337
|
+
normalized.push(current === "--config" ? "--config" : "-c", model ? encodeCodexModelConfig(model) : reasoningEffort ? encodeCodexReasoningEffortConfig(reasoningEffort) : next);
|
|
13243
13338
|
index += 1;
|
|
13244
13339
|
continue;
|
|
13245
13340
|
}
|
|
@@ -13247,7 +13342,8 @@ function normalizeCodexAppServerLaunchArgs(launchArgs) {
|
|
|
13247
13342
|
if (current.startsWith("--config=")) {
|
|
13248
13343
|
const value = current.slice("--config=".length);
|
|
13249
13344
|
const model = parseCodexModelConfig(value);
|
|
13250
|
-
|
|
13345
|
+
const reasoningEffort = parseCodexReasoningEffortConfig(value);
|
|
13346
|
+
normalized.push(model ? `--config=${encodeCodexModelConfig(model)}` : reasoningEffort ? `--config=${encodeCodexReasoningEffortConfig(reasoningEffort)}` : current);
|
|
13251
13347
|
continue;
|
|
13252
13348
|
}
|
|
13253
13349
|
normalized.push(current);
|
|
@@ -13275,6 +13371,50 @@ function readCodexAppServerModelFromLaunchArgs(launchArgs) {
|
|
|
13275
13371
|
}
|
|
13276
13372
|
return null;
|
|
13277
13373
|
}
|
|
13374
|
+
function readCodexAppServerReasoningEffortFromLaunchArgs(launchArgs) {
|
|
13375
|
+
const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
|
|
13376
|
+
for (let index = 0;index < normalized.length; index += 1) {
|
|
13377
|
+
const current = normalized[index] ?? "";
|
|
13378
|
+
if (current === "-c" || current === "--config") {
|
|
13379
|
+
const reasoningEffort = parseCodexReasoningEffortConfig(normalized[index + 1]);
|
|
13380
|
+
if (reasoningEffort) {
|
|
13381
|
+
return reasoningEffort;
|
|
13382
|
+
}
|
|
13383
|
+
index += 1;
|
|
13384
|
+
continue;
|
|
13385
|
+
}
|
|
13386
|
+
if (current.startsWith("--config=")) {
|
|
13387
|
+
const reasoningEffort = parseCodexReasoningEffortConfig(current.slice("--config=".length));
|
|
13388
|
+
if (reasoningEffort) {
|
|
13389
|
+
return reasoningEffort;
|
|
13390
|
+
}
|
|
13391
|
+
}
|
|
13392
|
+
}
|
|
13393
|
+
return null;
|
|
13394
|
+
}
|
|
13395
|
+
function resolveRequesterTimeoutMs2(timeoutMs) {
|
|
13396
|
+
if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0) {
|
|
13397
|
+
return timeoutMs;
|
|
13398
|
+
}
|
|
13399
|
+
return null;
|
|
13400
|
+
}
|
|
13401
|
+
function waitForRequesterResult2(promise, timeoutMs, label) {
|
|
13402
|
+
const effectiveTimeoutMs = resolveRequesterTimeoutMs2(timeoutMs);
|
|
13403
|
+
if (effectiveTimeoutMs === null) {
|
|
13404
|
+
return promise;
|
|
13405
|
+
}
|
|
13406
|
+
let timer = null;
|
|
13407
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
13408
|
+
timer = setTimeout(() => {
|
|
13409
|
+
reject(new Error(`Timed out after ${effectiveTimeoutMs}ms waiting for ${label}.`));
|
|
13410
|
+
}, effectiveTimeoutMs);
|
|
13411
|
+
});
|
|
13412
|
+
return Promise.race([promise, timeout]).finally(() => {
|
|
13413
|
+
if (timer) {
|
|
13414
|
+
clearTimeout(timer);
|
|
13415
|
+
}
|
|
13416
|
+
});
|
|
13417
|
+
}
|
|
13278
13418
|
function sessionKey2(options) {
|
|
13279
13419
|
return `${options.agentName}:${options.sessionId}`;
|
|
13280
13420
|
}
|
|
@@ -13377,17 +13517,79 @@ async function readOptionalFile2(filePath) {
|
|
|
13377
13517
|
return null;
|
|
13378
13518
|
}
|
|
13379
13519
|
}
|
|
13520
|
+
var SESSION_CATALOG_FILENAME2 = "session-catalog.json";
|
|
13521
|
+
var SESSION_CATALOG_MAX_ENTRIES2 = 64;
|
|
13522
|
+
async function readCodexSessionCatalog(runtimeDirectory) {
|
|
13523
|
+
const raw = await readOptionalFile2(join13(runtimeDirectory, SESSION_CATALOG_FILENAME2));
|
|
13524
|
+
if (!raw) {
|
|
13525
|
+
return { activeSessionId: null, sessions: [] };
|
|
13526
|
+
}
|
|
13527
|
+
const parsed = parseCodexMaybeJson(raw);
|
|
13528
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
13529
|
+
return { activeSessionId: null, sessions: [] };
|
|
13530
|
+
}
|
|
13531
|
+
return {
|
|
13532
|
+
activeSessionId: typeof parsed.activeSessionId === "string" ? parsed.activeSessionId : null,
|
|
13533
|
+
sessions: Array.isArray(parsed.sessions) ? parsed.sessions.filter((entry) => Boolean(entry) && typeof entry === "object" && typeof entry.id === "string" && typeof entry.startedAt === "number" && typeof entry.cwd === "string") : []
|
|
13534
|
+
};
|
|
13535
|
+
}
|
|
13536
|
+
async function writeCodexSessionCatalog(runtimeDirectory, catalog) {
|
|
13537
|
+
await mkdir5(runtimeDirectory, { recursive: true });
|
|
13538
|
+
await writeFile5(join13(runtimeDirectory, SESSION_CATALOG_FILENAME2), JSON.stringify(catalog, null, 2) + `
|
|
13539
|
+
`);
|
|
13540
|
+
}
|
|
13541
|
+
async function recordCodexSessionCatalog(runtimeDirectory, threadId, input) {
|
|
13542
|
+
const catalog = await readCodexSessionCatalog(runtimeDirectory);
|
|
13543
|
+
const now = Date.now();
|
|
13544
|
+
const sessions2 = catalog.sessions.map((session) => session.id === catalog.activeSessionId && session.id !== threadId && !session.endedAt ? { ...session, endedAt: now } : session);
|
|
13545
|
+
if (!sessions2.some((session) => session.id === threadId)) {
|
|
13546
|
+
sessions2.push({
|
|
13547
|
+
id: threadId,
|
|
13548
|
+
startedAt: now,
|
|
13549
|
+
cwd: input.cwd,
|
|
13550
|
+
harness: input.harness,
|
|
13551
|
+
transport: input.transport,
|
|
13552
|
+
model: input.model,
|
|
13553
|
+
approvalPolicy: input.approvalPolicy,
|
|
13554
|
+
sandbox: input.sandbox
|
|
13555
|
+
});
|
|
13556
|
+
}
|
|
13557
|
+
while (sessions2.length > SESSION_CATALOG_MAX_ENTRIES2) {
|
|
13558
|
+
sessions2.shift();
|
|
13559
|
+
}
|
|
13560
|
+
await writeCodexSessionCatalog(runtimeDirectory, {
|
|
13561
|
+
activeSessionId: threadId,
|
|
13562
|
+
sessions: sessions2
|
|
13563
|
+
});
|
|
13564
|
+
}
|
|
13565
|
+
async function closeCodexSessionCatalog(runtimeDirectory, threadId) {
|
|
13566
|
+
const catalog = await readCodexSessionCatalog(runtimeDirectory);
|
|
13567
|
+
const now = Date.now();
|
|
13568
|
+
const activeSessionId = threadId ?? catalog.activeSessionId;
|
|
13569
|
+
const sessions2 = catalog.sessions.map((session) => session.id === activeSessionId && !session.endedAt ? { ...session, endedAt: now } : session);
|
|
13570
|
+
await writeCodexSessionCatalog(runtimeDirectory, {
|
|
13571
|
+
activeSessionId: null,
|
|
13572
|
+
sessions: sessions2
|
|
13573
|
+
});
|
|
13574
|
+
}
|
|
13575
|
+
function parseCodexMaybeJson(value) {
|
|
13576
|
+
if (typeof value !== "string") {
|
|
13577
|
+
return value;
|
|
13578
|
+
}
|
|
13579
|
+
const trimmed = value.trim();
|
|
13580
|
+
if (!trimmed) {
|
|
13581
|
+
return value;
|
|
13582
|
+
}
|
|
13583
|
+
try {
|
|
13584
|
+
return JSON.parse(trimmed);
|
|
13585
|
+
} catch {
|
|
13586
|
+
return value;
|
|
13587
|
+
}
|
|
13588
|
+
}
|
|
13380
13589
|
function isMissingCodexRolloutError2(error) {
|
|
13381
13590
|
const message = errorMessage3(error).toLowerCase();
|
|
13382
13591
|
return message.includes("no rollout found for thread id");
|
|
13383
13592
|
}
|
|
13384
|
-
function resolveCodexCompletionGraceMs() {
|
|
13385
|
-
const parsed = Number.parseInt(process.env.OPENSCOUT_CODEX_COMPLETION_GRACE_MS ?? "", 10);
|
|
13386
|
-
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
13387
|
-
return parsed;
|
|
13388
|
-
}
|
|
13389
|
-
return 60000;
|
|
13390
|
-
}
|
|
13391
13593
|
|
|
13392
13594
|
class CodexAppServerSession {
|
|
13393
13595
|
options;
|
|
@@ -13436,14 +13638,14 @@ class CodexAppServerSession {
|
|
|
13436
13638
|
threadId: this.threadId
|
|
13437
13639
|
};
|
|
13438
13640
|
}
|
|
13439
|
-
async invoke(prompt, timeoutMs
|
|
13641
|
+
async invoke(prompt, timeoutMs, replyContext) {
|
|
13440
13642
|
return this.enqueue(async () => {
|
|
13441
13643
|
await this.ensureStarted();
|
|
13442
13644
|
if (!this.threadId) {
|
|
13443
13645
|
throw new Error(`Codex app-server session for ${this.options.agentName} has no active thread.`);
|
|
13444
13646
|
}
|
|
13445
|
-
const
|
|
13446
|
-
const turn = this.createActiveTurn(resolve4, reject
|
|
13647
|
+
const outputPromise = new Promise(async (resolve4, reject) => {
|
|
13648
|
+
const turn = this.createActiveTurn(resolve4, reject);
|
|
13447
13649
|
try {
|
|
13448
13650
|
await this.writeReplyContext(replyContext ?? null);
|
|
13449
13651
|
const response = await this.request("turn/start", {
|
|
@@ -13465,6 +13667,7 @@ class CodexAppServerSession {
|
|
|
13465
13667
|
reject(error instanceof Error ? error : new Error(errorMessage3(error)));
|
|
13466
13668
|
}
|
|
13467
13669
|
});
|
|
13670
|
+
const output = await waitForRequesterResult2(outputPromise, timeoutMs, this.options.agentName);
|
|
13468
13671
|
return {
|
|
13469
13672
|
output,
|
|
13470
13673
|
threadId: this.threadId
|
|
@@ -13474,7 +13677,7 @@ class CodexAppServerSession {
|
|
|
13474
13677
|
hasActiveTurn() {
|
|
13475
13678
|
return Boolean(this.activeTurn);
|
|
13476
13679
|
}
|
|
13477
|
-
async steerAndWait(prompt, timeoutMs
|
|
13680
|
+
async steerAndWait(prompt, timeoutMs) {
|
|
13478
13681
|
await this.ensureStarted();
|
|
13479
13682
|
if (!this.threadId) {
|
|
13480
13683
|
throw new Error(`Codex app-server session for ${this.options.agentName} has no active thread.`);
|
|
@@ -13538,9 +13741,6 @@ class CodexAppServerSession {
|
|
|
13538
13741
|
const activeTurn = this.activeTurn;
|
|
13539
13742
|
this.activeTurn = null;
|
|
13540
13743
|
if (activeTurn) {
|
|
13541
|
-
if (activeTurn.timer) {
|
|
13542
|
-
clearTimeout(activeTurn.timer);
|
|
13543
|
-
}
|
|
13544
13744
|
activeTurn.reject(new Error(`Codex app-server session for ${this.options.agentName} was shut down.`));
|
|
13545
13745
|
}
|
|
13546
13746
|
for (const pending of this.pendingRequests.values()) {
|
|
@@ -13559,6 +13759,7 @@ class CodexAppServerSession {
|
|
|
13559
13759
|
}
|
|
13560
13760
|
}
|
|
13561
13761
|
if (options.resetThread) {
|
|
13762
|
+
await closeCodexSessionCatalog(this.options.runtimeDirectory, this.threadId);
|
|
13562
13763
|
this.threadId = null;
|
|
13563
13764
|
this.threadPath = null;
|
|
13564
13765
|
await rm4(this.threadIdPath, { force: true });
|
|
@@ -13579,6 +13780,8 @@ class CodexAppServerSession {
|
|
|
13579
13780
|
cwd: options.cwd,
|
|
13580
13781
|
sessionId: options.sessionId,
|
|
13581
13782
|
systemPrompt: options.systemPrompt,
|
|
13783
|
+
approvalPolicy: options.approvalPolicy ?? "never",
|
|
13784
|
+
sandbox: options.sandbox ?? "danger-full-access",
|
|
13582
13785
|
threadId: options.threadId ?? null,
|
|
13583
13786
|
requireExistingThread: options.requireExistingThread === true,
|
|
13584
13787
|
launchArgs: normalizeCodexAppServerLaunchArgs(options.launchArgs)
|
|
@@ -13665,14 +13868,15 @@ class CodexAppServerSession {
|
|
|
13665
13868
|
const resumed = await this.request("thread/resume", {
|
|
13666
13869
|
threadId: storedThreadId,
|
|
13667
13870
|
cwd: this.options.cwd,
|
|
13668
|
-
approvalPolicy: "never",
|
|
13669
|
-
sandbox: "danger-full-access",
|
|
13871
|
+
approvalPolicy: this.options.approvalPolicy ?? "never",
|
|
13872
|
+
sandbox: this.options.sandbox ?? "danger-full-access",
|
|
13670
13873
|
baseInstructions: this.options.systemPrompt,
|
|
13671
13874
|
persistExtendedHistory: true
|
|
13672
13875
|
});
|
|
13673
13876
|
this.threadId = resumed.thread.id;
|
|
13674
13877
|
this.threadPath = resumed.thread.path ?? null;
|
|
13675
13878
|
await this.persistThreadId();
|
|
13879
|
+
await recordCodexSessionCatalog(this.options.runtimeDirectory, this.threadId, this.catalogRuntimeMeta());
|
|
13676
13880
|
return;
|
|
13677
13881
|
} catch (error) {
|
|
13678
13882
|
await appendFile3(this.stderrLogPath, `[openscout] failed to resume stored Codex thread ${storedThreadId}: ${errorMessage3(error)}
|
|
@@ -13695,8 +13899,8 @@ class CodexAppServerSession {
|
|
|
13695
13899
|
}
|
|
13696
13900
|
const started = await this.request("thread/start", {
|
|
13697
13901
|
cwd: this.options.cwd,
|
|
13698
|
-
approvalPolicy: "never",
|
|
13699
|
-
sandbox: "danger-full-access",
|
|
13902
|
+
approvalPolicy: this.options.approvalPolicy ?? "never",
|
|
13903
|
+
sandbox: this.options.sandbox ?? "danger-full-access",
|
|
13700
13904
|
baseInstructions: this.options.systemPrompt,
|
|
13701
13905
|
ephemeral: false,
|
|
13702
13906
|
experimentalRawEvents: false,
|
|
@@ -13705,71 +13909,47 @@ class CodexAppServerSession {
|
|
|
13705
13909
|
this.threadId = started.thread.id;
|
|
13706
13910
|
this.threadPath = started.thread.path ?? null;
|
|
13707
13911
|
await this.persistThreadId();
|
|
13912
|
+
await recordCodexSessionCatalog(this.options.runtimeDirectory, this.threadId, this.catalogRuntimeMeta());
|
|
13708
13913
|
}
|
|
13709
|
-
|
|
13914
|
+
catalogRuntimeMeta() {
|
|
13915
|
+
return {
|
|
13916
|
+
cwd: this.options.cwd,
|
|
13917
|
+
harness: "codex",
|
|
13918
|
+
transport: "codex_app_server",
|
|
13919
|
+
model: readCodexAppServerModelFromLaunchArgs(this.options.launchArgs),
|
|
13920
|
+
approvalPolicy: this.options.approvalPolicy ?? "never",
|
|
13921
|
+
sandbox: this.options.sandbox ?? "danger-full-access"
|
|
13922
|
+
};
|
|
13923
|
+
}
|
|
13924
|
+
createActiveTurn(resolve4, reject) {
|
|
13710
13925
|
if (this.activeTurn) {
|
|
13711
13926
|
throw new Error(`Codex app-server session for ${this.options.agentName} already has an active turn.`);
|
|
13712
13927
|
}
|
|
13713
13928
|
const turn = {
|
|
13714
13929
|
turnId: "",
|
|
13715
13930
|
startedAt: Date.now(),
|
|
13716
|
-
timer: null,
|
|
13717
|
-
graceTimer: null,
|
|
13718
|
-
timedOutAt: null,
|
|
13719
13931
|
messageOrder: [],
|
|
13720
13932
|
messageByItemId: new Map,
|
|
13721
13933
|
resolve: resolve4,
|
|
13722
13934
|
reject,
|
|
13723
13935
|
watchers: []
|
|
13724
13936
|
};
|
|
13725
|
-
turn.timer = setTimeout(() => {
|
|
13726
|
-
this.scheduleTurnTimeout(turn, timeoutMs);
|
|
13727
|
-
}, timeoutMs);
|
|
13728
13937
|
this.activeTurn = turn;
|
|
13729
13938
|
return turn;
|
|
13730
13939
|
}
|
|
13731
|
-
buildTurnTimeoutError(timeoutMs) {
|
|
13732
|
-
return new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`);
|
|
13733
|
-
}
|
|
13734
|
-
scheduleTurnTimeout(turn, timeoutMs) {
|
|
13735
|
-
if (turn.timedOutAt !== null) {
|
|
13736
|
-
return;
|
|
13737
|
-
}
|
|
13738
|
-
turn.timedOutAt = Date.now();
|
|
13739
|
-
this.interrupt().catch(() => {
|
|
13740
|
-
return;
|
|
13741
|
-
});
|
|
13742
|
-
const graceMs = resolveCodexCompletionGraceMs();
|
|
13743
|
-
if (graceMs <= 0) {
|
|
13744
|
-
this.rejectTimedOutTurn(turn, timeoutMs);
|
|
13745
|
-
return;
|
|
13746
|
-
}
|
|
13747
|
-
turn.graceTimer = setTimeout(() => {
|
|
13748
|
-
this.rejectTimedOutTurn(turn, timeoutMs);
|
|
13749
|
-
}, graceMs);
|
|
13750
|
-
}
|
|
13751
|
-
rejectTimedOutTurn(turn, timeoutMs) {
|
|
13752
|
-
if (this.activeTurn !== turn) {
|
|
13753
|
-
this.clearActiveTurn(turn);
|
|
13754
|
-
return;
|
|
13755
|
-
}
|
|
13756
|
-
this.clearActiveTurn(turn);
|
|
13757
|
-
const error = this.buildTurnTimeoutError(timeoutMs);
|
|
13758
|
-
for (const watcher of this.drainTurnWatchers(turn)) {
|
|
13759
|
-
watcher.reject(error);
|
|
13760
|
-
}
|
|
13761
|
-
turn.reject(error);
|
|
13762
|
-
}
|
|
13763
13940
|
addTurnWatcher(turn, resolve4, reject, timeoutMs) {
|
|
13764
13941
|
const watcher = {
|
|
13765
13942
|
resolve: resolve4,
|
|
13766
13943
|
reject,
|
|
13767
13944
|
timer: null
|
|
13768
13945
|
};
|
|
13769
|
-
|
|
13770
|
-
|
|
13771
|
-
|
|
13772
|
-
|
|
13946
|
+
const effectiveTimeoutMs = resolveRequesterTimeoutMs2(timeoutMs);
|
|
13947
|
+
if (effectiveTimeoutMs !== null) {
|
|
13948
|
+
watcher.timer = setTimeout(() => {
|
|
13949
|
+
this.removeTurnWatcher(turn, watcher);
|
|
13950
|
+
reject(new Error(`Timed out after ${effectiveTimeoutMs}ms waiting for ${this.options.agentName}.`));
|
|
13951
|
+
}, effectiveTimeoutMs);
|
|
13952
|
+
}
|
|
13773
13953
|
turn.watchers.push(watcher);
|
|
13774
13954
|
return watcher;
|
|
13775
13955
|
}
|
|
@@ -13790,12 +13970,6 @@ class CodexAppServerSession {
|
|
|
13790
13970
|
return watchers;
|
|
13791
13971
|
}
|
|
13792
13972
|
clearActiveTurn(turn) {
|
|
13793
|
-
if (turn.timer) {
|
|
13794
|
-
clearTimeout(turn.timer);
|
|
13795
|
-
}
|
|
13796
|
-
if (turn.graceTimer) {
|
|
13797
|
-
clearTimeout(turn.graceTimer);
|
|
13798
|
-
}
|
|
13799
13973
|
if (this.activeTurn === turn) {
|
|
13800
13974
|
this.activeTurn = null;
|
|
13801
13975
|
}
|
|
@@ -13992,9 +14166,6 @@ class CodexAppServerSession {
|
|
|
13992
14166
|
const activeTurn = this.activeTurn;
|
|
13993
14167
|
this.activeTurn = null;
|
|
13994
14168
|
if (activeTurn) {
|
|
13995
|
-
if (activeTurn.timer) {
|
|
13996
|
-
clearTimeout(activeTurn.timer);
|
|
13997
|
-
}
|
|
13998
14169
|
for (const watcher of this.drainTurnWatchers(activeTurn)) {
|
|
13999
14170
|
watcher.reject(error);
|
|
14000
14171
|
}
|
|
@@ -14098,6 +14269,8 @@ async function shutdownCodexAppServerAgent(options, shutdownOptions = {}) {
|
|
|
14098
14269
|
if (!session) {
|
|
14099
14270
|
if (shutdownOptions.resetThread) {
|
|
14100
14271
|
const runtimeDirectory = options.runtimeDirectory;
|
|
14272
|
+
const threadId = await readOptionalFile2(join13(runtimeDirectory, "codex-thread-id.txt"));
|
|
14273
|
+
await closeCodexSessionCatalog(runtimeDirectory, threadId);
|
|
14101
14274
|
await rm4(join13(runtimeDirectory, "codex-thread-id.txt"), { force: true });
|
|
14102
14275
|
}
|
|
14103
14276
|
return;
|
|
@@ -14649,12 +14822,15 @@ function runtimePackageDir() {
|
|
|
14649
14822
|
const explicit = process.env.OPENSCOUT_RUNTIME_PACKAGE_DIR?.trim();
|
|
14650
14823
|
if (explicit)
|
|
14651
14824
|
return explicit;
|
|
14652
|
-
const
|
|
14653
|
-
if (
|
|
14654
|
-
return
|
|
14825
|
+
const fromBundledPackage = findBundledRuntimeDir();
|
|
14826
|
+
if (fromBundledPackage)
|
|
14827
|
+
return fromBundledPackage;
|
|
14655
14828
|
const fromCwd = findWorkspaceRuntimeDir(process.cwd());
|
|
14656
14829
|
if (fromCwd)
|
|
14657
14830
|
return fromCwd;
|
|
14831
|
+
const fromGlobal = findGlobalRuntimeDir();
|
|
14832
|
+
if (fromGlobal)
|
|
14833
|
+
return fromGlobal;
|
|
14658
14834
|
const moduleDir = dirname6(fileURLToPath4(import.meta.url));
|
|
14659
14835
|
return resolve5(moduleDir, "..");
|
|
14660
14836
|
}
|
|
@@ -14676,6 +14852,8 @@ function findGlobalRuntimeDir() {
|
|
|
14676
14852
|
const scoutBin = result.stdout?.trim();
|
|
14677
14853
|
if (scoutBin) {
|
|
14678
14854
|
const scoutPkg = resolve5(scoutBin, "..", "..");
|
|
14855
|
+
if (isInstalledRuntimePackageDir(scoutPkg))
|
|
14856
|
+
return scoutPkg;
|
|
14679
14857
|
const nested = join15(scoutPkg, "node_modules", "@openscout", "runtime");
|
|
14680
14858
|
if (isInstalledRuntimePackageDir(nested))
|
|
14681
14859
|
return nested;
|
|
@@ -14686,6 +14864,11 @@ function findGlobalRuntimeDir() {
|
|
|
14686
14864
|
} catch {}
|
|
14687
14865
|
return null;
|
|
14688
14866
|
}
|
|
14867
|
+
function findBundledRuntimeDir() {
|
|
14868
|
+
const moduleDir = dirname6(fileURLToPath4(import.meta.url));
|
|
14869
|
+
const candidate = resolve5(moduleDir, "..");
|
|
14870
|
+
return isInstalledRuntimePackageDir(candidate) ? candidate : null;
|
|
14871
|
+
}
|
|
14689
14872
|
function findWorkspaceRuntimeDir(startDir) {
|
|
14690
14873
|
let current = resolve5(startDir);
|
|
14691
14874
|
while (true) {
|
|
@@ -14724,10 +14907,21 @@ function resolveBrokerStartTimeoutMs() {
|
|
|
14724
14907
|
return DEFAULT_BROKER_START_TIMEOUT_MS;
|
|
14725
14908
|
}
|
|
14726
14909
|
function resolveBrokerServiceLabel(mode) {
|
|
14727
|
-
const explicit = process.env.OPENSCOUT_BROKER_SERVICE_LABEL?.trim();
|
|
14910
|
+
const explicit = process.env.OPENSCOUT_SERVICE_LABEL?.trim() || process.env.OPENSCOUT_BROKER_SERVICE_LABEL?.trim();
|
|
14728
14911
|
if (explicit) {
|
|
14729
14912
|
return explicit;
|
|
14730
14913
|
}
|
|
14914
|
+
switch (mode) {
|
|
14915
|
+
case "prod":
|
|
14916
|
+
return "com.openscout";
|
|
14917
|
+
case "custom":
|
|
14918
|
+
return "com.openscout.custom";
|
|
14919
|
+
case "dev":
|
|
14920
|
+
default:
|
|
14921
|
+
return "dev.openscout";
|
|
14922
|
+
}
|
|
14923
|
+
}
|
|
14924
|
+
function legacyBrokerServiceLabel(mode) {
|
|
14731
14925
|
switch (mode) {
|
|
14732
14926
|
case "prod":
|
|
14733
14927
|
return "com.openscout.broker";
|
|
@@ -14786,6 +14980,7 @@ function renderLaunchAgentPlist(config) {
|
|
|
14786
14980
|
OPENSCOUT_CONTROL_HOME: config.controlHome,
|
|
14787
14981
|
OPENSCOUT_BROKER_SERVICE_MODE: config.mode,
|
|
14788
14982
|
OPENSCOUT_BROKER_SERVICE_LABEL: config.label,
|
|
14983
|
+
OPENSCOUT_SERVICE_LABEL: config.label,
|
|
14789
14984
|
OPENSCOUT_ADVERTISE_SCOPE: config.advertiseScope,
|
|
14790
14985
|
HOME: homedir14(),
|
|
14791
14986
|
PATH: launchPath,
|
|
@@ -14814,7 +15009,7 @@ function renderLaunchAgentPlist(config) {
|
|
|
14814
15009
|
<array>
|
|
14815
15010
|
<string>${xmlEscape(config.bunExecutable)}</string>
|
|
14816
15011
|
<string>${xmlEscape(join15(config.runtimePackageDir, "bin", "openscout-runtime.mjs"))}</string>
|
|
14817
|
-
<string>
|
|
15012
|
+
<string>base</string>
|
|
14818
15013
|
</array>
|
|
14819
15014
|
<key>WorkingDirectory</key>
|
|
14820
15015
|
<string>${xmlEscape(config.runtimePackageDir)}</string>
|
|
@@ -14878,6 +15073,22 @@ function writeLaunchAgentPlist(config) {
|
|
|
14878
15073
|
ensureServiceDirectories(config);
|
|
14879
15074
|
writeFileSync5(config.launchAgentPath, renderLaunchAgentPlist(config), "utf8");
|
|
14880
15075
|
}
|
|
15076
|
+
function bootoutCommand(config) {
|
|
15077
|
+
return `${launchctlPath()} bootout ${config.serviceTarget}`;
|
|
15078
|
+
}
|
|
15079
|
+
function legacyLaunchAgentPath(config) {
|
|
15080
|
+
return join15(homedir14(), "Library", "LaunchAgents", `${legacyBrokerServiceLabel(config.mode)}.plist`);
|
|
15081
|
+
}
|
|
15082
|
+
function legacyServiceTarget(config) {
|
|
15083
|
+
return `${config.domainTarget}/${legacyBrokerServiceLabel(config.mode)}`;
|
|
15084
|
+
}
|
|
15085
|
+
function bootoutLegacyBrokerService(config) {
|
|
15086
|
+
const legacyTarget = legacyServiceTarget(config);
|
|
15087
|
+
if (legacyTarget === config.serviceTarget) {
|
|
15088
|
+
return;
|
|
15089
|
+
}
|
|
15090
|
+
runCommand(launchctlPath(), ["bootout", legacyTarget], { allowFailure: true });
|
|
15091
|
+
}
|
|
14881
15092
|
function runCommand(command, args, options) {
|
|
14882
15093
|
const result = spawnSync(command, args, {
|
|
14883
15094
|
env: {
|
|
@@ -14996,6 +15207,7 @@ async function brokerServiceStatus(config = resolveBrokerServiceConfig()) {
|
|
|
14996
15207
|
label: config.label,
|
|
14997
15208
|
mode: config.mode,
|
|
14998
15209
|
launchAgentPath: config.launchAgentPath,
|
|
15210
|
+
bootoutCommand: bootoutCommand(config),
|
|
14999
15211
|
brokerUrl: config.brokerUrl,
|
|
15000
15212
|
brokerSocketPath: config.brokerSocketPath,
|
|
15001
15213
|
supportDirectory: config.supportDirectory,
|
|
@@ -15015,10 +15227,12 @@ async function brokerServiceStatus(config = resolveBrokerServiceConfig()) {
|
|
|
15015
15227
|
};
|
|
15016
15228
|
}
|
|
15017
15229
|
async function installBrokerService(config = resolveBrokerServiceConfig()) {
|
|
15230
|
+
bootoutLegacyBrokerService(config);
|
|
15018
15231
|
writeLaunchAgentPlist(config);
|
|
15019
15232
|
return brokerServiceStatus(config);
|
|
15020
15233
|
}
|
|
15021
15234
|
async function startBrokerService(config = resolveBrokerServiceConfig()) {
|
|
15235
|
+
bootoutLegacyBrokerService(config);
|
|
15022
15236
|
writeLaunchAgentPlist(config);
|
|
15023
15237
|
runCommand(launchctlPath(), ["bootout", config.serviceTarget], { allowFailure: true });
|
|
15024
15238
|
runCommand(launchctlPath(), ["bootstrap", config.domainTarget, config.launchAgentPath], { allowFailure: true });
|
|
@@ -15057,6 +15271,11 @@ async function uninstallBrokerService(config = resolveBrokerServiceConfig()) {
|
|
|
15057
15271
|
if (existsSync13(config.launchAgentPath)) {
|
|
15058
15272
|
rmSync2(config.launchAgentPath, { force: true });
|
|
15059
15273
|
}
|
|
15274
|
+
const legacyPath = legacyLaunchAgentPath(config);
|
|
15275
|
+
bootoutLegacyBrokerService(config);
|
|
15276
|
+
if (existsSync13(legacyPath)) {
|
|
15277
|
+
rmSync2(legacyPath, { force: true });
|
|
15278
|
+
}
|
|
15060
15279
|
return brokerServiceStatus(config);
|
|
15061
15280
|
}
|
|
15062
15281
|
async function main() {
|
|
@@ -15098,6 +15317,7 @@ function formatBrokerServiceStatus(status) {
|
|
|
15098
15317
|
`label: ${status.label}`,
|
|
15099
15318
|
`mode: ${status.mode}`,
|
|
15100
15319
|
`launch agent: ${status.installed ? status.launchAgentPath : "not installed"}`,
|
|
15320
|
+
`bootout: ${status.bootoutCommand}`,
|
|
15101
15321
|
`loaded: ${status.loaded ? "yes" : "no"}`,
|
|
15102
15322
|
`pid: ${status.pid ?? "\u2014"}`,
|
|
15103
15323
|
`launchd state: ${status.launchdState ?? "\u2014"}`,
|
|
@@ -15127,6 +15347,58 @@ var LOCAL_AGENT_SYSTEM_PROMPT_TEMPLATE_HINT = [
|
|
|
15127
15347
|
"{{relay_hub}}, {{broker_url}}, {{relay_command}}, {{openscout_root}}, {{relay_agent_comms_skill}}, and {{env.NAME}} variables."
|
|
15128
15348
|
].join("");
|
|
15129
15349
|
|
|
15350
|
+
// ../runtime/src/permission-policy.ts
|
|
15351
|
+
function parseScoutPermissionProfile(value) {
|
|
15352
|
+
const profile = normalizeScoutPermissionProfile(value);
|
|
15353
|
+
if (value && !profile) {
|
|
15354
|
+
throw new Error(`Unknown permission profile "${value}". Expected one of: ${formatScoutPermissionProfiles()}.`);
|
|
15355
|
+
}
|
|
15356
|
+
return profile;
|
|
15357
|
+
}
|
|
15358
|
+
function compileCodexPermissionProfile(profileInput) {
|
|
15359
|
+
const profile = profileInput ? parseScoutPermissionProfile(profileInput) ?? "trusted_local" : "trusted_local";
|
|
15360
|
+
switch (profile) {
|
|
15361
|
+
case "observe":
|
|
15362
|
+
case "review":
|
|
15363
|
+
return {
|
|
15364
|
+
profile,
|
|
15365
|
+
sandbox: "read-only",
|
|
15366
|
+
approvalPolicy: "on-request",
|
|
15367
|
+
enforcement: "native"
|
|
15368
|
+
};
|
|
15369
|
+
case "workspace_write":
|
|
15370
|
+
return {
|
|
15371
|
+
profile,
|
|
15372
|
+
sandbox: "workspace-write",
|
|
15373
|
+
approvalPolicy: "on-request",
|
|
15374
|
+
enforcement: "native"
|
|
15375
|
+
};
|
|
15376
|
+
case "sandboxed_write":
|
|
15377
|
+
return {
|
|
15378
|
+
profile,
|
|
15379
|
+
sandbox: "workspace-write",
|
|
15380
|
+
approvalPolicy: "on-request",
|
|
15381
|
+
enforcement: "advisory",
|
|
15382
|
+
note: "External sandbox placement is not wired yet; Codex receives its native workspace-write sandbox."
|
|
15383
|
+
};
|
|
15384
|
+
case "external_sandbox":
|
|
15385
|
+
return {
|
|
15386
|
+
profile,
|
|
15387
|
+
sandbox: "workspace-write",
|
|
15388
|
+
approvalPolicy: "on-request",
|
|
15389
|
+
enforcement: "advisory",
|
|
15390
|
+
note: "External sandbox placement is not wired yet; Codex receives its native workspace-write sandbox."
|
|
15391
|
+
};
|
|
15392
|
+
case "trusted_local":
|
|
15393
|
+
return {
|
|
15394
|
+
profile,
|
|
15395
|
+
sandbox: "danger-full-access",
|
|
15396
|
+
approvalPolicy: "never",
|
|
15397
|
+
enforcement: "native"
|
|
15398
|
+
};
|
|
15399
|
+
}
|
|
15400
|
+
}
|
|
15401
|
+
|
|
15130
15402
|
// ../runtime/src/local-agents.ts
|
|
15131
15403
|
var MODULE_DIRECTORY = dirname7(fileURLToPath5(import.meta.url));
|
|
15132
15404
|
var OPENSCOUT_REPO_ROOT = resolve6(MODULE_DIRECTORY, "..", "..", "..");
|
|
@@ -15576,6 +15848,46 @@ function buildLaunchArgsForRequestedModel(harness, model) {
|
|
|
15576
15848
|
}
|
|
15577
15849
|
return [];
|
|
15578
15850
|
}
|
|
15851
|
+
function normalizeRequestedReasoningEffort(reasoningEffort) {
|
|
15852
|
+
const trimmed = reasoningEffort?.trim();
|
|
15853
|
+
return trimmed ? trimmed : undefined;
|
|
15854
|
+
}
|
|
15855
|
+
function stripLaunchReasoningEffortForHarness(harness, launchArgs) {
|
|
15856
|
+
if (harness !== "codex") {
|
|
15857
|
+
return normalizeLaunchArgsForHarness(harness, launchArgs);
|
|
15858
|
+
}
|
|
15859
|
+
const next = [];
|
|
15860
|
+
const normalized = normalizeCodexAppServerLaunchArgs(launchArgs);
|
|
15861
|
+
for (let index = 0;index < normalized.length; index += 1) {
|
|
15862
|
+
const current = normalized[index] ?? "";
|
|
15863
|
+
if (current === "-c" || current === "--config") {
|
|
15864
|
+
const value = normalized[index + 1];
|
|
15865
|
+
if (readCodexAppServerReasoningEffortFromLaunchArgs(value ? [current, value] : [current])) {
|
|
15866
|
+
index += 1;
|
|
15867
|
+
continue;
|
|
15868
|
+
}
|
|
15869
|
+
next.push(current);
|
|
15870
|
+
if (value) {
|
|
15871
|
+
next.push(value);
|
|
15872
|
+
index += 1;
|
|
15873
|
+
}
|
|
15874
|
+
continue;
|
|
15875
|
+
}
|
|
15876
|
+
if (current.startsWith("--config=")) {
|
|
15877
|
+
if (readCodexAppServerReasoningEffortFromLaunchArgs([current])) {
|
|
15878
|
+
continue;
|
|
15879
|
+
}
|
|
15880
|
+
}
|
|
15881
|
+
next.push(current);
|
|
15882
|
+
}
|
|
15883
|
+
return next;
|
|
15884
|
+
}
|
|
15885
|
+
function buildLaunchArgsForRequestedReasoningEffort(harness, reasoningEffort) {
|
|
15886
|
+
if (harness === "codex") {
|
|
15887
|
+
return normalizeCodexAppServerLaunchArgs(["--reasoning-effort", reasoningEffort]);
|
|
15888
|
+
}
|
|
15889
|
+
return [];
|
|
15890
|
+
}
|
|
15579
15891
|
function applyRequestedModelToLaunchArgs(harness, launchArgs, model) {
|
|
15580
15892
|
const normalized = normalizeLaunchArgsForHarness(harness, launchArgs);
|
|
15581
15893
|
const requestedModel = normalizeRequestedModel(model);
|
|
@@ -15587,6 +15899,17 @@ function applyRequestedModelToLaunchArgs(harness, launchArgs, model) {
|
|
|
15587
15899
|
...buildLaunchArgsForRequestedModel(harness, requestedModel)
|
|
15588
15900
|
];
|
|
15589
15901
|
}
|
|
15902
|
+
function applyRequestedRuntimeOptionsToLaunchArgs(harness, launchArgs, options) {
|
|
15903
|
+
const withModel = applyRequestedModelToLaunchArgs(harness, launchArgs, options.model);
|
|
15904
|
+
const requestedReasoningEffort = normalizeRequestedReasoningEffort(options.reasoningEffort);
|
|
15905
|
+
if (!requestedReasoningEffort) {
|
|
15906
|
+
return withModel;
|
|
15907
|
+
}
|
|
15908
|
+
return [
|
|
15909
|
+
...stripLaunchReasoningEffortForHarness(harness, withModel),
|
|
15910
|
+
...buildLaunchArgsForRequestedReasoningEffort(harness, requestedReasoningEffort)
|
|
15911
|
+
];
|
|
15912
|
+
}
|
|
15590
15913
|
function defaultHarnessForOverride(override, fallback = "claude") {
|
|
15591
15914
|
return normalizeManagedHarness2(override.defaultHarness ?? override.runtime?.harness, fallback);
|
|
15592
15915
|
}
|
|
@@ -15606,16 +15929,17 @@ function overrideHarnessProfile(override, harness) {
|
|
|
15606
15929
|
cwd: normalizeProjectPath(profile?.cwd || override.runtime?.cwd || override.projectRoot || process.cwd()),
|
|
15607
15930
|
transport: normalizeLocalAgentTransport(profile?.transport ?? override.runtime?.transport, harness),
|
|
15608
15931
|
sessionId: normalizeTmuxSessionName2(profile?.sessionId, `${override.agentId}-${harness}`),
|
|
15609
|
-
launchArgs: launchArgsForOverrideHarness(override, harness)
|
|
15932
|
+
launchArgs: launchArgsForOverrideHarness(override, harness),
|
|
15933
|
+
...profile?.permissionProfile ? { permissionProfile: profile.permissionProfile } : {}
|
|
15610
15934
|
};
|
|
15611
15935
|
}
|
|
15612
15936
|
function normalizeManagedHarness2(value, fallback) {
|
|
15613
|
-
return value === "codex" ? "codex" : value === "claude" ? "claude" : fallback;
|
|
15937
|
+
return value === "codex" ? "codex" : value === "claude" ? "claude" : value === "cursor" ? "cursor" : fallback;
|
|
15614
15938
|
}
|
|
15615
15939
|
function normalizeLocalHarnessProfiles(agentId, record) {
|
|
15616
15940
|
const defaultHarness = normalizeManagedHarness2(typeof record.defaultHarness === "string" ? record.defaultHarness : record.harness, "claude");
|
|
15617
15941
|
const nextProfiles = {};
|
|
15618
|
-
for (const harness of ["claude", "codex"]) {
|
|
15942
|
+
for (const harness of ["claude", "codex", "cursor"]) {
|
|
15619
15943
|
const profile = record.harnessProfiles?.[harness];
|
|
15620
15944
|
if (!profile) {
|
|
15621
15945
|
continue;
|
|
@@ -15624,7 +15948,8 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
15624
15948
|
cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
|
|
15625
15949
|
transport: normalizeLocalAgentTransport(profile.transport, harness),
|
|
15626
15950
|
sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
|
|
15627
|
-
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
|
|
15951
|
+
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs),
|
|
15952
|
+
...profile.permissionProfile ? { permissionProfile: profile.permissionProfile } : {}
|
|
15628
15953
|
};
|
|
15629
15954
|
}
|
|
15630
15955
|
const runtimeHarness = normalizeManagedHarness2(record.harness, defaultHarness);
|
|
@@ -15633,7 +15958,8 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
15633
15958
|
cwd: normalizeProjectPath(record.cwd || process.cwd()),
|
|
15634
15959
|
transport: normalizeLocalAgentTransport(record.transport, runtimeHarness),
|
|
15635
15960
|
sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${runtimeHarness}`),
|
|
15636
|
-
launchArgs: normalizeLaunchArgsForHarness(runtimeHarness, record.launchArgs)
|
|
15961
|
+
launchArgs: normalizeLaunchArgsForHarness(runtimeHarness, record.launchArgs),
|
|
15962
|
+
...record.permissionProfile ? { permissionProfile: record.permissionProfile } : {}
|
|
15637
15963
|
};
|
|
15638
15964
|
}
|
|
15639
15965
|
if (!nextProfiles[defaultHarness]) {
|
|
@@ -15641,10 +15967,11 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
15641
15967
|
cwd: normalizeProjectPath(record.cwd || process.cwd()),
|
|
15642
15968
|
transport: normalizeLocalAgentTransport(record.transport, defaultHarness),
|
|
15643
15969
|
sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${defaultHarness}`),
|
|
15644
|
-
launchArgs: normalizeLaunchArgsForHarness(defaultHarness, record.launchArgs)
|
|
15970
|
+
launchArgs: normalizeLaunchArgsForHarness(defaultHarness, record.launchArgs),
|
|
15971
|
+
...record.permissionProfile ? { permissionProfile: record.permissionProfile } : {}
|
|
15645
15972
|
};
|
|
15646
15973
|
}
|
|
15647
|
-
for (const harness of ["claude", "codex"]) {
|
|
15974
|
+
for (const harness of ["claude", "codex", "cursor"]) {
|
|
15648
15975
|
const profile = nextProfiles[harness];
|
|
15649
15976
|
if (!profile) {
|
|
15650
15977
|
continue;
|
|
@@ -15653,7 +15980,8 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
15653
15980
|
cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
|
|
15654
15981
|
transport: normalizeLocalAgentTransport(profile.transport, harness),
|
|
15655
15982
|
sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
|
|
15656
|
-
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
|
|
15983
|
+
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs),
|
|
15984
|
+
...profile.permissionProfile ? { permissionProfile: profile.permissionProfile } : {}
|
|
15657
15985
|
};
|
|
15658
15986
|
}
|
|
15659
15987
|
return nextProfiles;
|
|
@@ -15678,13 +16006,15 @@ function recordForHarness(record, harnessOverride) {
|
|
|
15678
16006
|
cwd: fallbackCwd,
|
|
15679
16007
|
transport: fallbackTransport,
|
|
15680
16008
|
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs),
|
|
16009
|
+
...normalized.permissionProfile ? { permissionProfile: normalized.permissionProfile } : {},
|
|
15681
16010
|
harnessProfiles: {
|
|
15682
16011
|
...normalized.harnessProfiles,
|
|
15683
16012
|
[selectedHarness]: {
|
|
15684
16013
|
cwd: fallbackCwd,
|
|
15685
16014
|
transport: fallbackTransport,
|
|
15686
16015
|
sessionId: fallbackSessionId,
|
|
15687
|
-
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs)
|
|
16016
|
+
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs),
|
|
16017
|
+
...normalized.permissionProfile ? { permissionProfile: normalized.permissionProfile } : {}
|
|
15688
16018
|
}
|
|
15689
16019
|
}
|
|
15690
16020
|
};
|
|
@@ -15696,7 +16026,8 @@ function recordForHarness(record, harnessOverride) {
|
|
|
15696
16026
|
tmuxSession: profile.sessionId,
|
|
15697
16027
|
cwd: profile.cwd,
|
|
15698
16028
|
transport: profile.transport,
|
|
15699
|
-
launchArgs: profile.launchArgs
|
|
16029
|
+
launchArgs: profile.launchArgs,
|
|
16030
|
+
permissionProfile: profile.permissionProfile
|
|
15700
16031
|
};
|
|
15701
16032
|
}
|
|
15702
16033
|
function normalizeLocalAgentRecord(agentId, record) {
|
|
@@ -15727,6 +16058,7 @@ function normalizeLocalAgentRecord(agentId, record) {
|
|
|
15727
16058
|
transport: activeProfile?.transport ?? normalizeLocalAgentTransport(record.transport, harness),
|
|
15728
16059
|
capabilities: normalizeLocalAgentCapabilities(record.capabilities),
|
|
15729
16060
|
launchArgs: activeProfile?.launchArgs ?? normalizeLaunchArgsForHarness(harness, record.launchArgs),
|
|
16061
|
+
permissionProfile: activeProfile?.permissionProfile ?? record.permissionProfile,
|
|
15730
16062
|
registrationSource: record.registrationSource
|
|
15731
16063
|
};
|
|
15732
16064
|
}
|
|
@@ -15812,6 +16144,7 @@ function relayAgentOverrideFromLocalAgentRecord(agentId, record, existing) {
|
|
|
15812
16144
|
};
|
|
15813
16145
|
}
|
|
15814
16146
|
function buildCodexAgentSessionOptions(agentName, record, systemPrompt) {
|
|
16147
|
+
const permissionPosture = compileCodexPermissionProfile(record.permissionProfile);
|
|
15815
16148
|
return {
|
|
15816
16149
|
agentName,
|
|
15817
16150
|
sessionId: record.tmuxSession,
|
|
@@ -15819,7 +16152,10 @@ function buildCodexAgentSessionOptions(agentName, record, systemPrompt) {
|
|
|
15819
16152
|
systemPrompt: systemPrompt ?? buildLocalAgentSystemPrompt(agentName, record.project, record.cwd, { transport: "codex_app_server" }),
|
|
15820
16153
|
runtimeDirectory: relayAgentRuntimeDirectory(agentName),
|
|
15821
16154
|
logsDirectory: relayAgentLogsDirectory(agentName),
|
|
15822
|
-
launchArgs: normalizeLaunchArgsForHarness("codex", record.launchArgs)
|
|
16155
|
+
launchArgs: normalizeLaunchArgsForHarness("codex", record.launchArgs),
|
|
16156
|
+
permissionProfile: permissionPosture.profile,
|
|
16157
|
+
approvalPolicy: permissionPosture.approvalPolicy,
|
|
16158
|
+
sandbox: permissionPosture.sandbox
|
|
15823
16159
|
};
|
|
15824
16160
|
}
|
|
15825
16161
|
function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
|
|
@@ -16076,6 +16412,7 @@ async function startLocalAgent(input) {
|
|
|
16076
16412
|
const currentDirectory = input.currentDirectory ?? projectPath;
|
|
16077
16413
|
const effectiveCwd = input.cwdOverride ? normalizeProjectPath(input.cwdOverride) : undefined;
|
|
16078
16414
|
const shouldEnsureOnline = input.ensureOnline !== false;
|
|
16415
|
+
const requestedPermissionProfile = parseScoutPermissionProfile(input.permissionProfile);
|
|
16079
16416
|
const requestedDefinitionId = input.agentName?.trim() ? normalizeAgentSelectorSegment(input.agentName.trim()) : "";
|
|
16080
16417
|
if (input.agentName?.trim() && !requestedDefinitionId) {
|
|
16081
16418
|
throw new Error(`Invalid agent name "${input.agentName}".`);
|
|
@@ -16131,7 +16468,10 @@ async function startLocalAgent(input) {
|
|
|
16131
16468
|
const effectiveHarness = normalizeManagedHarness2(preferredHarness ?? configDefaultHarness, "claude");
|
|
16132
16469
|
const transport = normalizeLocalAgentTransport(undefined, effectiveHarness);
|
|
16133
16470
|
const sessionId = normalizeTmuxSessionName2(undefined, `${instance.id}-${effectiveHarness}`);
|
|
16134
|
-
const launchArgs =
|
|
16471
|
+
const launchArgs = applyRequestedRuntimeOptionsToLaunchArgs(effectiveHarness, [], {
|
|
16472
|
+
model: input.model,
|
|
16473
|
+
reasoningEffort: input.reasoningEffort
|
|
16474
|
+
});
|
|
16135
16475
|
const existingForInstance = overrides[instance.id];
|
|
16136
16476
|
if (existingForInstance && normalizeProjectPath(existingForInstance.projectRoot) !== projectRoot) {
|
|
16137
16477
|
throw new Error(`Another agent is already registered as ${instance.id} at ${existingForInstance.projectRoot}. ` + `Two clones of the same project on the same branch cannot both register here; ` + `rename one of the checkouts or switch its branch before running scout up.`);
|
|
@@ -16152,7 +16492,8 @@ async function startLocalAgent(input) {
|
|
|
16152
16492
|
cwd: effectiveCwd ?? projectRoot,
|
|
16153
16493
|
transport,
|
|
16154
16494
|
sessionId,
|
|
16155
|
-
launchArgs
|
|
16495
|
+
launchArgs,
|
|
16496
|
+
...requestedPermissionProfile ? { permissionProfile: requestedPermissionProfile } : {}
|
|
16156
16497
|
}
|
|
16157
16498
|
},
|
|
16158
16499
|
runtime: {
|
|
@@ -16176,7 +16517,10 @@ async function startLocalAgent(input) {
|
|
|
16176
16517
|
const nextHarness = normalizeManagedHarness2(resolvedHarness, "claude");
|
|
16177
16518
|
const nextDefaultHarness = preferredHarness ? normalizeManagedHarness2(preferredHarness, "claude") : defaultHarnessForOverride(matchingOverride, "claude");
|
|
16178
16519
|
const nextSessionId = normalizeTmuxSessionName2(undefined, `${instance.id}-${nextHarness}`);
|
|
16179
|
-
const nextLaunchArgs =
|
|
16520
|
+
const nextLaunchArgs = applyRequestedRuntimeOptionsToLaunchArgs(nextHarness, launchArgsForOverrideHarness(matchingOverride, nextHarness), {
|
|
16521
|
+
model: input.model,
|
|
16522
|
+
reasoningEffort: input.reasoningEffort
|
|
16523
|
+
});
|
|
16180
16524
|
overrides[instance.id] = {
|
|
16181
16525
|
agentId: instance.id,
|
|
16182
16526
|
definitionId: requestedDefinitionId,
|
|
@@ -16194,7 +16538,8 @@ async function startLocalAgent(input) {
|
|
|
16194
16538
|
[nextHarness]: {
|
|
16195
16539
|
...overrideHarnessProfile(matchingOverride, nextHarness),
|
|
16196
16540
|
sessionId: nextSessionId,
|
|
16197
|
-
launchArgs: nextLaunchArgs
|
|
16541
|
+
launchArgs: nextLaunchArgs,
|
|
16542
|
+
...requestedPermissionProfile ? { permissionProfile: requestedPermissionProfile } : {}
|
|
16198
16543
|
}
|
|
16199
16544
|
},
|
|
16200
16545
|
runtime: {
|
|
@@ -16208,9 +16553,12 @@ async function startLocalAgent(input) {
|
|
|
16208
16553
|
await writeRelayAgentOverrides(overrides);
|
|
16209
16554
|
targetAgentId = instance.id;
|
|
16210
16555
|
} else {
|
|
16211
|
-
if (input.model) {
|
|
16556
|
+
if (input.model || input.reasoningEffort || requestedPermissionProfile) {
|
|
16212
16557
|
const existingHarness = normalizeManagedHarness2(preferredHarness ?? matchingOverride.defaultHarness ?? matchingOverride.runtime?.harness, "claude");
|
|
16213
|
-
const nextLaunchArgs =
|
|
16558
|
+
const nextLaunchArgs = applyRequestedRuntimeOptionsToLaunchArgs(existingHarness, launchArgsForOverrideHarness(matchingOverride, existingHarness), {
|
|
16559
|
+
model: input.model,
|
|
16560
|
+
reasoningEffort: input.reasoningEffort
|
|
16561
|
+
});
|
|
16214
16562
|
overrides[matchingAgentId] = {
|
|
16215
16563
|
...matchingOverride,
|
|
16216
16564
|
launchArgs: existingHarness === defaultHarnessForOverride(matchingOverride, existingHarness) ? nextLaunchArgs : matchingOverride.launchArgs,
|
|
@@ -16218,7 +16566,8 @@ async function startLocalAgent(input) {
|
|
|
16218
16566
|
...matchingOverride.harnessProfiles ?? {},
|
|
16219
16567
|
[existingHarness]: {
|
|
16220
16568
|
...overrideHarnessProfile(matchingOverride, existingHarness),
|
|
16221
|
-
launchArgs: nextLaunchArgs
|
|
16569
|
+
launchArgs: nextLaunchArgs,
|
|
16570
|
+
...requestedPermissionProfile ? { permissionProfile: requestedPermissionProfile } : {}
|
|
16222
16571
|
}
|
|
16223
16572
|
}
|
|
16224
16573
|
};
|
|
@@ -16322,6 +16671,7 @@ function readPersistedClaudeSessionId(agentName) {
|
|
|
16322
16671
|
function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
16323
16672
|
const normalizedRecord = normalizeLocalAgentRecord(agentId, record);
|
|
16324
16673
|
const configuredModel = readLaunchModelForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs);
|
|
16674
|
+
const codexPermissionPosture = normalizeLocalAgentHarness(normalizedRecord.harness) === "codex" ? compileCodexPermissionProfile(normalizedRecord.permissionProfile) : null;
|
|
16325
16675
|
const definitionId = normalizedRecord.definitionId ?? agentId;
|
|
16326
16676
|
const displayName = titleCaseLocalAgentName(definitionId);
|
|
16327
16677
|
const projectRoot = normalizedRecord.projectRoot ?? normalizedRecord.cwd;
|
|
@@ -16348,7 +16698,13 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
16348
16698
|
nodeQualifier: instance.nodeQualifier,
|
|
16349
16699
|
workspaceQualifier: instance.workspaceQualifier,
|
|
16350
16700
|
branch: instance.branch,
|
|
16351
|
-
...configuredModel ? { model: configuredModel } : {}
|
|
16701
|
+
...configuredModel ? { model: configuredModel } : {},
|
|
16702
|
+
...codexPermissionPosture ? {
|
|
16703
|
+
permissionProfile: codexPermissionPosture.profile,
|
|
16704
|
+
approvalPolicy: codexPermissionPosture.approvalPolicy,
|
|
16705
|
+
sandbox: codexPermissionPosture.sandbox,
|
|
16706
|
+
permissionEnforcement: codexPermissionPosture.enforcement
|
|
16707
|
+
} : {}
|
|
16352
16708
|
}
|
|
16353
16709
|
},
|
|
16354
16710
|
agent: {
|
|
@@ -16377,7 +16733,13 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
16377
16733
|
nodeQualifier: instance.nodeQualifier,
|
|
16378
16734
|
workspaceQualifier: instance.workspaceQualifier,
|
|
16379
16735
|
branch: instance.branch,
|
|
16380
|
-
...configuredModel ? { model: configuredModel } : {}
|
|
16736
|
+
...configuredModel ? { model: configuredModel } : {},
|
|
16737
|
+
...codexPermissionPosture ? {
|
|
16738
|
+
permissionProfile: codexPermissionPosture.profile,
|
|
16739
|
+
approvalPolicy: codexPermissionPosture.approvalPolicy,
|
|
16740
|
+
sandbox: codexPermissionPosture.sandbox,
|
|
16741
|
+
permissionEnforcement: codexPermissionPosture.enforcement
|
|
16742
|
+
} : {}
|
|
16381
16743
|
},
|
|
16382
16744
|
agentClass: "general",
|
|
16383
16745
|
capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
|
|
@@ -16412,6 +16774,12 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
16412
16774
|
workspaceQualifier: instance.workspaceQualifier,
|
|
16413
16775
|
branch: instance.branch,
|
|
16414
16776
|
...configuredModel ? { model: configuredModel } : {},
|
|
16777
|
+
...codexPermissionPosture ? {
|
|
16778
|
+
permissionProfile: codexPermissionPosture.profile,
|
|
16779
|
+
approvalPolicy: codexPermissionPosture.approvalPolicy,
|
|
16780
|
+
sandbox: codexPermissionPosture.sandbox,
|
|
16781
|
+
permissionEnforcement: codexPermissionPosture.enforcement
|
|
16782
|
+
} : {},
|
|
16415
16783
|
...externalSessionId ? { externalSessionId } : {}
|
|
16416
16784
|
}
|
|
16417
16785
|
}
|
|
@@ -40091,6 +40459,7 @@ async function startSupervisorRuntime(state) {
|
|
|
40091
40459
|
relay: activeRelayUrl,
|
|
40092
40460
|
pairing: {
|
|
40093
40461
|
relay: payload.relay,
|
|
40462
|
+
...payload.fallbackRelays?.length ? { fallbackRelays: payload.fallbackRelays } : {},
|
|
40094
40463
|
room: payload.room,
|
|
40095
40464
|
publicKey: payload.publicKey,
|
|
40096
40465
|
expiresAt: payload.expiresAt,
|