@openscout/scout 0.2.63 → 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 +40 -3
- 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 +3263 -1416
- package/dist/pair-supervisor.mjs +554 -156
- 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 +3507 -1717
- package/dist/scout-web-server.mjs +3507 -1717
- package/package.json +4 -6
- package/dist/client/assets/arc.es-B2XMIUBi.js +0 -188
- package/dist/client/assets/index-DB4dwQr2.css +0 -1
- package/dist/client/assets/index-DiSMCren.js +0 -158
- package/dist/control-plane-client/assets/addon-fit-DX4qG4td.js +0 -1
- package/dist/control-plane-client/assets/addon-webgl-DCtw1yLn.js +0 -64
- package/dist/control-plane-client/assets/arc.es-CcKhIPRw.js +0 -188
- package/dist/control-plane-client/assets/index-BGcMYoYJ.css +0 -1
- package/dist/control-plane-client/assets/index-BkEXBsPk.js +0 -140
- package/dist/control-plane-client/assets/xterm-B-qIQCd3.js +0 -16
- package/dist/control-plane-client/index.html +0 -16
- package/dist/control-plane-client/openscout-icon.png +0 -0
- /package/dist/{control-plane-client → client}/assets/index-TVkH_WDG.js +0 -0
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,
|
|
@@ -8321,10 +8321,12 @@ function scopesMatch(left, right) {
|
|
|
8321
8321
|
if (left.sessionId !== right.sessionId) {
|
|
8322
8322
|
return false;
|
|
8323
8323
|
}
|
|
8324
|
-
|
|
8325
|
-
|
|
8324
|
+
switch (left.kind) {
|
|
8325
|
+
case "session":
|
|
8326
|
+
return true;
|
|
8327
|
+
case "file_change":
|
|
8328
|
+
return right.kind === "file_change" && left.turnId === right.turnId && left.blockId === right.blockId;
|
|
8326
8329
|
}
|
|
8327
|
-
return left.turnId === right.turnId && left.blockId === right.blockId;
|
|
8328
8330
|
}
|
|
8329
8331
|
function pathForWebHandoffScope(scope) {
|
|
8330
8332
|
switch (scope.kind) {
|
|
@@ -9097,6 +9099,40 @@ var BUILT_IN_HARNESS_CATALOG = [
|
|
|
9097
9099
|
cwdFlag: "--cwd"
|
|
9098
9100
|
},
|
|
9099
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
|
+
}
|
|
9100
9136
|
}
|
|
9101
9137
|
];
|
|
9102
9138
|
function expandHomePath(value) {
|
|
@@ -10671,6 +10707,22 @@ function diagnoseAgentIdentity(identity, candidates) {
|
|
|
10671
10707
|
var parseAgentSelector = parseAgentIdentity;
|
|
10672
10708
|
var formatAgentSelector = formatAgentIdentity;
|
|
10673
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
|
+
}
|
|
10674
10726
|
// ../runtime/src/user-project-hints.ts
|
|
10675
10727
|
import { readdir, readFile as readFile3, stat } from "fs/promises";
|
|
10676
10728
|
import { homedir as homedir11 } from "os";
|
|
@@ -10996,7 +11048,7 @@ async function collectUserLevelProjectRootHints(options = {}) {
|
|
|
10996
11048
|
// ../runtime/src/setup.ts
|
|
10997
11049
|
var SCOUT_AGENT_ID = "scout";
|
|
10998
11050
|
var SCOUT_PRIMARY_CONVERSATION_ID = "dm.scout.primary";
|
|
10999
|
-
var MANAGED_AGENT_HARNESSES = ["claude", "codex"];
|
|
11051
|
+
var MANAGED_AGENT_HARNESSES = ["claude", "codex", "cursor"];
|
|
11000
11052
|
function titleCaseWords(value) {
|
|
11001
11053
|
return value.split(/[\s._-]+/).map((part) => part.trim()).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
11002
11054
|
}
|
|
@@ -11115,6 +11167,10 @@ var PROJECT_HARNESS_MARKERS = {
|
|
|
11115
11167
|
"AGENTS.md",
|
|
11116
11168
|
"CODEX.md",
|
|
11117
11169
|
".codex"
|
|
11170
|
+
],
|
|
11171
|
+
cursor: [
|
|
11172
|
+
".cursor",
|
|
11173
|
+
".cursorrules"
|
|
11118
11174
|
]
|
|
11119
11175
|
};
|
|
11120
11176
|
function partitionFlatAndNestedMarkers(markers) {
|
|
@@ -11179,6 +11235,9 @@ function normalizeManagedHarness(value, fallback) {
|
|
|
11179
11235
|
if (value === "claude") {
|
|
11180
11236
|
return "claude";
|
|
11181
11237
|
}
|
|
11238
|
+
if (value === "cursor") {
|
|
11239
|
+
return "cursor";
|
|
11240
|
+
}
|
|
11182
11241
|
return fallback;
|
|
11183
11242
|
}
|
|
11184
11243
|
function titleCase(value) {
|
|
@@ -11352,12 +11411,15 @@ function normalizeCapabilities(value) {
|
|
|
11352
11411
|
return normalized.length > 0 ? normalized : [...DEFAULT_CAPABILITIES];
|
|
11353
11412
|
}
|
|
11354
11413
|
function normalizeHarness(value, fallback) {
|
|
11355
|
-
return value === "codex" ? "codex" : value === "claude" ? "claude" : fallback;
|
|
11414
|
+
return value === "codex" ? "codex" : value === "claude" ? "claude" : value === "cursor" ? "cursor" : fallback;
|
|
11356
11415
|
}
|
|
11357
11416
|
function normalizeTransport(value, harness, fallback) {
|
|
11358
11417
|
if (harness === "codex") {
|
|
11359
11418
|
return "codex_app_server";
|
|
11360
11419
|
}
|
|
11420
|
+
if (harness === "cursor") {
|
|
11421
|
+
return "cursor_exec";
|
|
11422
|
+
}
|
|
11361
11423
|
if (value === "claude_stream_json") {
|
|
11362
11424
|
return "claude_stream_json";
|
|
11363
11425
|
}
|
|
@@ -11367,11 +11429,13 @@ function normalizeTransport(value, harness, fallback) {
|
|
|
11367
11429
|
return fallback === "codex_app_server" ? "claude_stream_json" : fallback;
|
|
11368
11430
|
}
|
|
11369
11431
|
function normalizeHarnessProfile(profile, harness, options) {
|
|
11432
|
+
const permissionProfile = normalizeScoutPermissionProfile(typeof profile?.permissionProfile === "string" ? profile.permissionProfile : options.fallbackPermissionProfile);
|
|
11370
11433
|
return {
|
|
11371
11434
|
cwd: profile?.cwd ? normalizeProjectRelativePath(options.projectRoot, profile.cwd) : options.projectRoot,
|
|
11372
11435
|
transport: normalizeTransport(profile?.transport, harness, harness === "codex" ? "codex_app_server" : DEFAULT_TRANSPORT),
|
|
11373
11436
|
sessionId: normalizeTmuxSessionName(profile?.sessionId, `${options.sessionKey}-${harness}`, options.sessionPrefix),
|
|
11374
|
-
launchArgs: normalizeLaunchArgs(profile?.launchArgs ?? options.fallbackLaunchArgs)
|
|
11437
|
+
launchArgs: normalizeLaunchArgs(profile?.launchArgs ?? options.fallbackLaunchArgs),
|
|
11438
|
+
...permissionProfile ? { permissionProfile } : {}
|
|
11375
11439
|
};
|
|
11376
11440
|
}
|
|
11377
11441
|
function buildHarnessProfiles(input) {
|
|
@@ -11387,7 +11451,8 @@ function buildHarnessProfiles(input) {
|
|
|
11387
11451
|
projectRoot: input.projectRoot,
|
|
11388
11452
|
sessionKey: input.sessionKey,
|
|
11389
11453
|
sessionPrefix: input.sessionPrefix,
|
|
11390
|
-
fallbackLaunchArgs: input.launchArgs
|
|
11454
|
+
fallbackLaunchArgs: input.launchArgs,
|
|
11455
|
+
fallbackPermissionProfile: input.permissionProfile
|
|
11391
11456
|
});
|
|
11392
11457
|
}
|
|
11393
11458
|
for (const harness of MANAGED_AGENT_HARNESSES) {
|
|
@@ -11400,7 +11465,8 @@ function buildHarnessProfiles(input) {
|
|
|
11400
11465
|
projectRoot: input.projectRoot,
|
|
11401
11466
|
sessionKey: input.sessionKey,
|
|
11402
11467
|
sessionPrefix: input.sessionPrefix,
|
|
11403
|
-
fallbackLaunchArgs: profiles[harness]?.launchArgs ?? input.launchArgs
|
|
11468
|
+
fallbackLaunchArgs: profiles[harness]?.launchArgs ?? input.launchArgs,
|
|
11469
|
+
fallbackPermissionProfile: profiles[harness]?.permissionProfile ?? input.permissionProfile
|
|
11404
11470
|
});
|
|
11405
11471
|
}
|
|
11406
11472
|
}
|
|
@@ -11408,7 +11474,8 @@ function buildHarnessProfiles(input) {
|
|
|
11408
11474
|
projectRoot: input.projectRoot,
|
|
11409
11475
|
sessionKey: input.sessionKey,
|
|
11410
11476
|
sessionPrefix: input.sessionPrefix,
|
|
11411
|
-
fallbackLaunchArgs: input.launchArgs
|
|
11477
|
+
fallbackLaunchArgs: input.launchArgs,
|
|
11478
|
+
fallbackPermissionProfile: input.permissionProfile
|
|
11412
11479
|
});
|
|
11413
11480
|
profiles[input.defaultHarness] = ensuredHarness;
|
|
11414
11481
|
return profiles;
|
|
@@ -11858,7 +11925,8 @@ async function readLegacyAgentRegistry() {
|
|
|
11858
11925
|
async function detectHarnessMarkers(projectRoot) {
|
|
11859
11926
|
const detected = {
|
|
11860
11927
|
claude: [],
|
|
11861
|
-
codex: []
|
|
11928
|
+
codex: [],
|
|
11929
|
+
cursor: []
|
|
11862
11930
|
};
|
|
11863
11931
|
for (const harness of MANAGED_AGENT_HARNESSES) {
|
|
11864
11932
|
for (const marker of PROJECT_HARNESS_MARKERS[harness]) {
|
|
@@ -12440,7 +12508,8 @@ async function resolveManifestBackedAgent(projectRoot, config, settings, overrid
|
|
|
12440
12508
|
defaultHarness,
|
|
12441
12509
|
profiles: config.agent?.runtime?.profiles,
|
|
12442
12510
|
runtime: runtimeDefaults,
|
|
12443
|
-
launchArgs: normalizeLaunchArgs(runtimeDefaults?.launchArgs)
|
|
12511
|
+
launchArgs: normalizeLaunchArgs(runtimeDefaults?.launchArgs),
|
|
12512
|
+
permissionProfile: normalizeScoutPermissionProfile(runtimeDefaults?.permissionProfile)
|
|
12444
12513
|
});
|
|
12445
12514
|
const view = resolvedRuntimeView(defaultHarness, harnessProfiles);
|
|
12446
12515
|
const base = {
|
|
@@ -12515,7 +12584,7 @@ async function buildProjectInventoryEntry(agent, sourceRoot) {
|
|
|
12515
12584
|
});
|
|
12516
12585
|
}
|
|
12517
12586
|
}
|
|
12518
|
-
if (manifestDefaultHarness === "claude" || manifestDefaultHarness === "codex") {
|
|
12587
|
+
if (manifestDefaultHarness === "claude" || manifestDefaultHarness === "codex" || manifestDefaultHarness === "cursor") {
|
|
12519
12588
|
harnesses.set(manifestDefaultHarness, {
|
|
12520
12589
|
harness: manifestDefaultHarness,
|
|
12521
12590
|
source: "manifest",
|
|
@@ -12800,6 +12869,29 @@ function buildManagedAgentShellExports(options) {
|
|
|
12800
12869
|
}
|
|
12801
12870
|
|
|
12802
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
|
+
}
|
|
12803
12895
|
function resolveClaudeStreamJsonOutput(result, fallbackParts) {
|
|
12804
12896
|
const trimmedResult = result?.trim();
|
|
12805
12897
|
if (trimmedResult) {
|
|
@@ -12896,7 +12988,7 @@ class ClaudeStreamJsonSession {
|
|
|
12896
12988
|
sessionId: this.claudeSessionId
|
|
12897
12989
|
};
|
|
12898
12990
|
}
|
|
12899
|
-
async invoke(prompt,
|
|
12991
|
+
async invoke(prompt, timeoutMs) {
|
|
12900
12992
|
await this.ensureStarted();
|
|
12901
12993
|
if (!this.process?.stdin) {
|
|
12902
12994
|
throw new Error(`Claude stream-json session for ${this.options.agentName} is not running.`);
|
|
@@ -12908,13 +13000,10 @@ class ClaudeStreamJsonSession {
|
|
|
12908
13000
|
const turn = {
|
|
12909
13001
|
id: randomUUID(),
|
|
12910
13002
|
output: [],
|
|
12911
|
-
timer: null,
|
|
12912
|
-
stallMs: stallTimeoutMs,
|
|
12913
13003
|
resolve: resolve4,
|
|
12914
13004
|
reject
|
|
12915
13005
|
};
|
|
12916
13006
|
this.activeTurn = turn;
|
|
12917
|
-
this.resetTurnWatchdog(turn);
|
|
12918
13007
|
});
|
|
12919
13008
|
const payload = JSON.stringify({
|
|
12920
13009
|
type: "user",
|
|
@@ -12927,7 +13016,7 @@ class ClaudeStreamJsonSession {
|
|
|
12927
13016
|
}) + `
|
|
12928
13017
|
`;
|
|
12929
13018
|
this.process.stdin.write(payload);
|
|
12930
|
-
const output = await outputPromise;
|
|
13019
|
+
const output = await waitForRequesterResult(outputPromise, timeoutMs, this.options.agentName);
|
|
12931
13020
|
return {
|
|
12932
13021
|
output,
|
|
12933
13022
|
sessionId: this.claudeSessionId
|
|
@@ -12949,27 +13038,10 @@ class ClaudeStreamJsonSession {
|
|
|
12949
13038
|
}) + `
|
|
12950
13039
|
`);
|
|
12951
13040
|
}
|
|
12952
|
-
resetTurnWatchdog(turn) {
|
|
12953
|
-
if (turn.timer) {
|
|
12954
|
-
clearTimeout(turn.timer);
|
|
12955
|
-
}
|
|
12956
|
-
turn.timer = setTimeout(() => {
|
|
12957
|
-
this.interrupt().catch(() => {
|
|
12958
|
-
return;
|
|
12959
|
-
});
|
|
12960
|
-
if (this.activeTurn?.id === turn.id) {
|
|
12961
|
-
this.activeTurn = null;
|
|
12962
|
-
}
|
|
12963
|
-
turn.reject(new Error(`${this.options.agentName} stalled \u2014 no stream event in ${turn.stallMs}ms`));
|
|
12964
|
-
}, turn.stallMs);
|
|
12965
|
-
}
|
|
12966
13041
|
async shutdown(options = {}) {
|
|
12967
13042
|
const turn = this.activeTurn;
|
|
12968
13043
|
this.activeTurn = null;
|
|
12969
13044
|
if (turn) {
|
|
12970
|
-
if (turn.timer) {
|
|
12971
|
-
clearTimeout(turn.timer);
|
|
12972
|
-
}
|
|
12973
13045
|
turn.reject(new Error(`Claude stream-json session for ${this.options.agentName} was shut down.`));
|
|
12974
13046
|
}
|
|
12975
13047
|
const child = this.process;
|
|
@@ -13047,9 +13119,6 @@ class ClaudeStreamJsonSession {
|
|
|
13047
13119
|
if (this.activeTurn) {
|
|
13048
13120
|
const turn = this.activeTurn;
|
|
13049
13121
|
this.activeTurn = null;
|
|
13050
|
-
if (turn.timer) {
|
|
13051
|
-
clearTimeout(turn.timer);
|
|
13052
|
-
}
|
|
13053
13122
|
turn.reject(new Error(`Claude process error: ${error.message}`));
|
|
13054
13123
|
}
|
|
13055
13124
|
});
|
|
@@ -13080,9 +13149,6 @@ class ClaudeStreamJsonSession {
|
|
|
13080
13149
|
if (code !== 0 && this.activeTurn) {
|
|
13081
13150
|
const turn = this.activeTurn;
|
|
13082
13151
|
this.activeTurn = null;
|
|
13083
|
-
if (turn.timer) {
|
|
13084
|
-
clearTimeout(turn.timer);
|
|
13085
|
-
}
|
|
13086
13152
|
turn.reject(new Error(`Claude exited with code ${code}`));
|
|
13087
13153
|
}
|
|
13088
13154
|
});
|
|
@@ -13104,7 +13170,6 @@ class ClaudeStreamJsonSession {
|
|
|
13104
13170
|
if (!turn) {
|
|
13105
13171
|
return;
|
|
13106
13172
|
}
|
|
13107
|
-
this.resetTurnWatchdog(turn);
|
|
13108
13173
|
if (event.type === "assistant") {
|
|
13109
13174
|
const content = event.message?.content ?? event.content ?? [];
|
|
13110
13175
|
for (const part of content) {
|
|
@@ -13116,17 +13181,11 @@ class ClaudeStreamJsonSession {
|
|
|
13116
13181
|
}
|
|
13117
13182
|
if (event.type === "result") {
|
|
13118
13183
|
this.activeTurn = null;
|
|
13119
|
-
if (turn.timer) {
|
|
13120
|
-
clearTimeout(turn.timer);
|
|
13121
|
-
}
|
|
13122
13184
|
turn.resolve(resolveClaudeStreamJsonOutput(event.result, turn.output));
|
|
13123
13185
|
return;
|
|
13124
13186
|
}
|
|
13125
13187
|
if (event.type === "error") {
|
|
13126
13188
|
this.activeTurn = null;
|
|
13127
|
-
if (turn.timer) {
|
|
13128
|
-
clearTimeout(turn.timer);
|
|
13129
|
-
}
|
|
13130
13189
|
turn.reject(new Error(event.error?.message ?? event.message ?? "Unknown Claude error"));
|
|
13131
13190
|
}
|
|
13132
13191
|
}
|
|
@@ -13179,10 +13238,17 @@ function normalizeCodexModelValue(value) {
|
|
|
13179
13238
|
const trimmed = value?.trim();
|
|
13180
13239
|
return trimmed ? trimmed : null;
|
|
13181
13240
|
}
|
|
13241
|
+
function normalizeCodexReasoningEffortValue(value) {
|
|
13242
|
+
const trimmed = value?.trim();
|
|
13243
|
+
return trimmed ? trimmed : null;
|
|
13244
|
+
}
|
|
13182
13245
|
function encodeCodexModelConfig(model) {
|
|
13183
13246
|
return `model=${JSON.stringify(model)}`;
|
|
13184
13247
|
}
|
|
13185
|
-
function
|
|
13248
|
+
function encodeCodexReasoningEffortConfig(reasoningEffort) {
|
|
13249
|
+
return `model_reasoning_effort=${JSON.stringify(reasoningEffort)}`;
|
|
13250
|
+
}
|
|
13251
|
+
function parseCodexConfigValue(value, expectedKey) {
|
|
13186
13252
|
const trimmed = value?.trim();
|
|
13187
13253
|
if (!trimmed) {
|
|
13188
13254
|
return null;
|
|
@@ -13192,7 +13258,7 @@ function parseCodexModelConfig(value) {
|
|
|
13192
13258
|
return null;
|
|
13193
13259
|
}
|
|
13194
13260
|
const key = trimmed.slice(0, separatorIndex).trim();
|
|
13195
|
-
if (key !==
|
|
13261
|
+
if (key !== expectedKey) {
|
|
13196
13262
|
return null;
|
|
13197
13263
|
}
|
|
13198
13264
|
const rawValue = trimmed.slice(separatorIndex + 1).trim();
|
|
@@ -13204,6 +13270,12 @@ function parseCodexModelConfig(value) {
|
|
|
13204
13270
|
}
|
|
13205
13271
|
return rawValue;
|
|
13206
13272
|
}
|
|
13273
|
+
function parseCodexModelConfig(value) {
|
|
13274
|
+
return parseCodexConfigValue(value, "model");
|
|
13275
|
+
}
|
|
13276
|
+
function parseCodexReasoningEffortConfig(value) {
|
|
13277
|
+
return parseCodexConfigValue(value, "model_reasoning_effort");
|
|
13278
|
+
}
|
|
13207
13279
|
function normalizeCodexAppServerLaunchArgs(launchArgs) {
|
|
13208
13280
|
const args = Array.isArray(launchArgs) ? launchArgs.map((entry) => String(entry).trim()).filter(Boolean) : [];
|
|
13209
13281
|
const normalized = [];
|
|
@@ -13233,11 +13305,36 @@ function normalizeCodexAppServerLaunchArgs(launchArgs) {
|
|
|
13233
13305
|
continue;
|
|
13234
13306
|
}
|
|
13235
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
|
+
}
|
|
13236
13332
|
if (current === "-c" || current === "--config") {
|
|
13237
13333
|
const next = args[index + 1];
|
|
13238
13334
|
if (typeof next === "string") {
|
|
13239
13335
|
const model = parseCodexModelConfig(next);
|
|
13240
|
-
|
|
13336
|
+
const reasoningEffort = parseCodexReasoningEffortConfig(next);
|
|
13337
|
+
normalized.push(current === "--config" ? "--config" : "-c", model ? encodeCodexModelConfig(model) : reasoningEffort ? encodeCodexReasoningEffortConfig(reasoningEffort) : next);
|
|
13241
13338
|
index += 1;
|
|
13242
13339
|
continue;
|
|
13243
13340
|
}
|
|
@@ -13245,7 +13342,8 @@ function normalizeCodexAppServerLaunchArgs(launchArgs) {
|
|
|
13245
13342
|
if (current.startsWith("--config=")) {
|
|
13246
13343
|
const value = current.slice("--config=".length);
|
|
13247
13344
|
const model = parseCodexModelConfig(value);
|
|
13248
|
-
|
|
13345
|
+
const reasoningEffort = parseCodexReasoningEffortConfig(value);
|
|
13346
|
+
normalized.push(model ? `--config=${encodeCodexModelConfig(model)}` : reasoningEffort ? `--config=${encodeCodexReasoningEffortConfig(reasoningEffort)}` : current);
|
|
13249
13347
|
continue;
|
|
13250
13348
|
}
|
|
13251
13349
|
normalized.push(current);
|
|
@@ -13273,6 +13371,50 @@ function readCodexAppServerModelFromLaunchArgs(launchArgs) {
|
|
|
13273
13371
|
}
|
|
13274
13372
|
return null;
|
|
13275
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
|
+
}
|
|
13276
13418
|
function sessionKey2(options) {
|
|
13277
13419
|
return `${options.agentName}:${options.sessionId}`;
|
|
13278
13420
|
}
|
|
@@ -13375,23 +13517,86 @@ async function readOptionalFile2(filePath) {
|
|
|
13375
13517
|
return null;
|
|
13376
13518
|
}
|
|
13377
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
|
+
}
|
|
13378
13589
|
function isMissingCodexRolloutError2(error) {
|
|
13379
13590
|
const message = errorMessage3(error).toLowerCase();
|
|
13380
13591
|
return message.includes("no rollout found for thread id");
|
|
13381
13592
|
}
|
|
13382
|
-
function resolveCodexCompletionGraceMs() {
|
|
13383
|
-
const parsed = Number.parseInt(process.env.OPENSCOUT_CODEX_COMPLETION_GRACE_MS ?? "", 10);
|
|
13384
|
-
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
13385
|
-
return parsed;
|
|
13386
|
-
}
|
|
13387
|
-
return 60000;
|
|
13388
|
-
}
|
|
13389
13593
|
|
|
13390
13594
|
class CodexAppServerSession {
|
|
13391
13595
|
options;
|
|
13392
13596
|
key;
|
|
13393
13597
|
threadIdPath;
|
|
13394
13598
|
statePath;
|
|
13599
|
+
replyContextPath;
|
|
13395
13600
|
stdoutLogPath;
|
|
13396
13601
|
stderrLogPath;
|
|
13397
13602
|
process = null;
|
|
@@ -13409,6 +13614,7 @@ class CodexAppServerSession {
|
|
|
13409
13614
|
this.key = sessionKey2(options);
|
|
13410
13615
|
this.threadIdPath = join13(options.runtimeDirectory, "codex-thread-id.txt");
|
|
13411
13616
|
this.statePath = join13(options.runtimeDirectory, "state.json");
|
|
13617
|
+
this.replyContextPath = join13(options.runtimeDirectory, "scout-reply-context.json");
|
|
13412
13618
|
this.stdoutLogPath = join13(options.logsDirectory, "stdout.log");
|
|
13413
13619
|
this.stderrLogPath = join13(options.logsDirectory, "stderr.log");
|
|
13414
13620
|
this.lastConfigSignature = this.configSignature(options);
|
|
@@ -13432,15 +13638,16 @@ class CodexAppServerSession {
|
|
|
13432
13638
|
threadId: this.threadId
|
|
13433
13639
|
};
|
|
13434
13640
|
}
|
|
13435
|
-
async invoke(prompt, timeoutMs
|
|
13641
|
+
async invoke(prompt, timeoutMs, replyContext) {
|
|
13436
13642
|
return this.enqueue(async () => {
|
|
13437
13643
|
await this.ensureStarted();
|
|
13438
13644
|
if (!this.threadId) {
|
|
13439
13645
|
throw new Error(`Codex app-server session for ${this.options.agentName} has no active thread.`);
|
|
13440
13646
|
}
|
|
13441
|
-
const
|
|
13442
|
-
const turn = this.createActiveTurn(resolve4, reject
|
|
13647
|
+
const outputPromise = new Promise(async (resolve4, reject) => {
|
|
13648
|
+
const turn = this.createActiveTurn(resolve4, reject);
|
|
13443
13649
|
try {
|
|
13650
|
+
await this.writeReplyContext(replyContext ?? null);
|
|
13444
13651
|
const response = await this.request("turn/start", {
|
|
13445
13652
|
threadId: this.threadId,
|
|
13446
13653
|
cwd: this.options.cwd,
|
|
@@ -13455,10 +13662,12 @@ class CodexAppServerSession {
|
|
|
13455
13662
|
turn.turnId = response.turn.id;
|
|
13456
13663
|
await this.persistState();
|
|
13457
13664
|
} catch (error) {
|
|
13665
|
+
await this.clearReplyContext();
|
|
13458
13666
|
this.clearActiveTurn(turn);
|
|
13459
13667
|
reject(error instanceof Error ? error : new Error(errorMessage3(error)));
|
|
13460
13668
|
}
|
|
13461
13669
|
});
|
|
13670
|
+
const output = await waitForRequesterResult2(outputPromise, timeoutMs, this.options.agentName);
|
|
13462
13671
|
return {
|
|
13463
13672
|
output,
|
|
13464
13673
|
threadId: this.threadId
|
|
@@ -13468,7 +13677,7 @@ class CodexAppServerSession {
|
|
|
13468
13677
|
hasActiveTurn() {
|
|
13469
13678
|
return Boolean(this.activeTurn);
|
|
13470
13679
|
}
|
|
13471
|
-
async steerAndWait(prompt, timeoutMs
|
|
13680
|
+
async steerAndWait(prompt, timeoutMs) {
|
|
13472
13681
|
await this.ensureStarted();
|
|
13473
13682
|
if (!this.threadId) {
|
|
13474
13683
|
throw new Error(`Codex app-server session for ${this.options.agentName} has no active thread.`);
|
|
@@ -13532,9 +13741,6 @@ class CodexAppServerSession {
|
|
|
13532
13741
|
const activeTurn = this.activeTurn;
|
|
13533
13742
|
this.activeTurn = null;
|
|
13534
13743
|
if (activeTurn) {
|
|
13535
|
-
if (activeTurn.timer) {
|
|
13536
|
-
clearTimeout(activeTurn.timer);
|
|
13537
|
-
}
|
|
13538
13744
|
activeTurn.reject(new Error(`Codex app-server session for ${this.options.agentName} was shut down.`));
|
|
13539
13745
|
}
|
|
13540
13746
|
for (const pending of this.pendingRequests.values()) {
|
|
@@ -13553,6 +13759,7 @@ class CodexAppServerSession {
|
|
|
13553
13759
|
}
|
|
13554
13760
|
}
|
|
13555
13761
|
if (options.resetThread) {
|
|
13762
|
+
await closeCodexSessionCatalog(this.options.runtimeDirectory, this.threadId);
|
|
13556
13763
|
this.threadId = null;
|
|
13557
13764
|
this.threadPath = null;
|
|
13558
13765
|
await rm4(this.threadIdPath, { force: true });
|
|
@@ -13573,6 +13780,8 @@ class CodexAppServerSession {
|
|
|
13573
13780
|
cwd: options.cwd,
|
|
13574
13781
|
sessionId: options.sessionId,
|
|
13575
13782
|
systemPrompt: options.systemPrompt,
|
|
13783
|
+
approvalPolicy: options.approvalPolicy ?? "never",
|
|
13784
|
+
sandbox: options.sandbox ?? "danger-full-access",
|
|
13576
13785
|
threadId: options.threadId ?? null,
|
|
13577
13786
|
requireExistingThread: options.requireExistingThread === true,
|
|
13578
13787
|
launchArgs: normalizeCodexAppServerLaunchArgs(options.launchArgs)
|
|
@@ -13603,6 +13812,7 @@ class CodexAppServerSession {
|
|
|
13603
13812
|
currentDirectory: this.options.cwd,
|
|
13604
13813
|
baseEnv: process.env
|
|
13605
13814
|
});
|
|
13815
|
+
env.OPENSCOUT_REPLY_CONTEXT_FILE = this.replyContextPath;
|
|
13606
13816
|
const child = spawn3(codexExecutable, [
|
|
13607
13817
|
"app-server",
|
|
13608
13818
|
...buildScoutMcpCodexLaunchArgs({
|
|
@@ -13658,14 +13868,15 @@ class CodexAppServerSession {
|
|
|
13658
13868
|
const resumed = await this.request("thread/resume", {
|
|
13659
13869
|
threadId: storedThreadId,
|
|
13660
13870
|
cwd: this.options.cwd,
|
|
13661
|
-
approvalPolicy: "never",
|
|
13662
|
-
sandbox: "danger-full-access",
|
|
13871
|
+
approvalPolicy: this.options.approvalPolicy ?? "never",
|
|
13872
|
+
sandbox: this.options.sandbox ?? "danger-full-access",
|
|
13663
13873
|
baseInstructions: this.options.systemPrompt,
|
|
13664
13874
|
persistExtendedHistory: true
|
|
13665
13875
|
});
|
|
13666
13876
|
this.threadId = resumed.thread.id;
|
|
13667
13877
|
this.threadPath = resumed.thread.path ?? null;
|
|
13668
13878
|
await this.persistThreadId();
|
|
13879
|
+
await recordCodexSessionCatalog(this.options.runtimeDirectory, this.threadId, this.catalogRuntimeMeta());
|
|
13669
13880
|
return;
|
|
13670
13881
|
} catch (error) {
|
|
13671
13882
|
await appendFile3(this.stderrLogPath, `[openscout] failed to resume stored Codex thread ${storedThreadId}: ${errorMessage3(error)}
|
|
@@ -13688,8 +13899,8 @@ class CodexAppServerSession {
|
|
|
13688
13899
|
}
|
|
13689
13900
|
const started = await this.request("thread/start", {
|
|
13690
13901
|
cwd: this.options.cwd,
|
|
13691
|
-
approvalPolicy: "never",
|
|
13692
|
-
sandbox: "danger-full-access",
|
|
13902
|
+
approvalPolicy: this.options.approvalPolicy ?? "never",
|
|
13903
|
+
sandbox: this.options.sandbox ?? "danger-full-access",
|
|
13693
13904
|
baseInstructions: this.options.systemPrompt,
|
|
13694
13905
|
ephemeral: false,
|
|
13695
13906
|
experimentalRawEvents: false,
|
|
@@ -13698,71 +13909,47 @@ class CodexAppServerSession {
|
|
|
13698
13909
|
this.threadId = started.thread.id;
|
|
13699
13910
|
this.threadPath = started.thread.path ?? null;
|
|
13700
13911
|
await this.persistThreadId();
|
|
13912
|
+
await recordCodexSessionCatalog(this.options.runtimeDirectory, this.threadId, this.catalogRuntimeMeta());
|
|
13701
13913
|
}
|
|
13702
|
-
|
|
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) {
|
|
13703
13925
|
if (this.activeTurn) {
|
|
13704
13926
|
throw new Error(`Codex app-server session for ${this.options.agentName} already has an active turn.`);
|
|
13705
13927
|
}
|
|
13706
13928
|
const turn = {
|
|
13707
13929
|
turnId: "",
|
|
13708
13930
|
startedAt: Date.now(),
|
|
13709
|
-
timer: null,
|
|
13710
|
-
graceTimer: null,
|
|
13711
|
-
timedOutAt: null,
|
|
13712
13931
|
messageOrder: [],
|
|
13713
13932
|
messageByItemId: new Map,
|
|
13714
13933
|
resolve: resolve4,
|
|
13715
13934
|
reject,
|
|
13716
13935
|
watchers: []
|
|
13717
13936
|
};
|
|
13718
|
-
turn.timer = setTimeout(() => {
|
|
13719
|
-
this.scheduleTurnTimeout(turn, timeoutMs);
|
|
13720
|
-
}, timeoutMs);
|
|
13721
13937
|
this.activeTurn = turn;
|
|
13722
13938
|
return turn;
|
|
13723
13939
|
}
|
|
13724
|
-
buildTurnTimeoutError(timeoutMs) {
|
|
13725
|
-
return new Error(`Timed out after ${timeoutMs}ms waiting for ${this.options.agentName}.`);
|
|
13726
|
-
}
|
|
13727
|
-
scheduleTurnTimeout(turn, timeoutMs) {
|
|
13728
|
-
if (turn.timedOutAt !== null) {
|
|
13729
|
-
return;
|
|
13730
|
-
}
|
|
13731
|
-
turn.timedOutAt = Date.now();
|
|
13732
|
-
this.interrupt().catch(() => {
|
|
13733
|
-
return;
|
|
13734
|
-
});
|
|
13735
|
-
const graceMs = resolveCodexCompletionGraceMs();
|
|
13736
|
-
if (graceMs <= 0) {
|
|
13737
|
-
this.rejectTimedOutTurn(turn, timeoutMs);
|
|
13738
|
-
return;
|
|
13739
|
-
}
|
|
13740
|
-
turn.graceTimer = setTimeout(() => {
|
|
13741
|
-
this.rejectTimedOutTurn(turn, timeoutMs);
|
|
13742
|
-
}, graceMs);
|
|
13743
|
-
}
|
|
13744
|
-
rejectTimedOutTurn(turn, timeoutMs) {
|
|
13745
|
-
if (this.activeTurn !== turn) {
|
|
13746
|
-
this.clearActiveTurn(turn);
|
|
13747
|
-
return;
|
|
13748
|
-
}
|
|
13749
|
-
this.clearActiveTurn(turn);
|
|
13750
|
-
const error = this.buildTurnTimeoutError(timeoutMs);
|
|
13751
|
-
for (const watcher of this.drainTurnWatchers(turn)) {
|
|
13752
|
-
watcher.reject(error);
|
|
13753
|
-
}
|
|
13754
|
-
turn.reject(error);
|
|
13755
|
-
}
|
|
13756
13940
|
addTurnWatcher(turn, resolve4, reject, timeoutMs) {
|
|
13757
13941
|
const watcher = {
|
|
13758
13942
|
resolve: resolve4,
|
|
13759
13943
|
reject,
|
|
13760
13944
|
timer: null
|
|
13761
13945
|
};
|
|
13762
|
-
|
|
13763
|
-
|
|
13764
|
-
|
|
13765
|
-
|
|
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
|
+
}
|
|
13766
13953
|
turn.watchers.push(watcher);
|
|
13767
13954
|
return watcher;
|
|
13768
13955
|
}
|
|
@@ -13783,12 +13970,6 @@ class CodexAppServerSession {
|
|
|
13783
13970
|
return watchers;
|
|
13784
13971
|
}
|
|
13785
13972
|
clearActiveTurn(turn) {
|
|
13786
|
-
if (turn.timer) {
|
|
13787
|
-
clearTimeout(turn.timer);
|
|
13788
|
-
}
|
|
13789
|
-
if (turn.graceTimer) {
|
|
13790
|
-
clearTimeout(turn.graceTimer);
|
|
13791
|
-
}
|
|
13792
13973
|
if (this.activeTurn === turn) {
|
|
13793
13974
|
this.activeTurn = null;
|
|
13794
13975
|
}
|
|
@@ -13845,6 +14026,20 @@ class CodexAppServerSession {
|
|
|
13845
14026
|
error: buildUnsupportedServerRequestError2(message)
|
|
13846
14027
|
});
|
|
13847
14028
|
}
|
|
14029
|
+
async writeReplyContext(context) {
|
|
14030
|
+
if (!context) {
|
|
14031
|
+
await this.clearReplyContext();
|
|
14032
|
+
return;
|
|
14033
|
+
}
|
|
14034
|
+
await mkdir5(this.options.runtimeDirectory, { recursive: true });
|
|
14035
|
+
await writeFile5(this.replyContextPath, `${JSON.stringify(context, null, 2)}
|
|
14036
|
+
`, "utf8");
|
|
14037
|
+
}
|
|
14038
|
+
async clearReplyContext() {
|
|
14039
|
+
await rm4(this.replyContextPath, { force: true }).catch(() => {
|
|
14040
|
+
return;
|
|
14041
|
+
});
|
|
14042
|
+
}
|
|
13848
14043
|
handleNotification(message) {
|
|
13849
14044
|
const method = message.method;
|
|
13850
14045
|
const params = message.params ?? {};
|
|
@@ -13899,6 +14094,7 @@ class CodexAppServerSession {
|
|
|
13899
14094
|
return;
|
|
13900
14095
|
}
|
|
13901
14096
|
if (method === "turn/completed") {
|
|
14097
|
+
this.clearReplyContext();
|
|
13902
14098
|
this.completeTurn(params);
|
|
13903
14099
|
return;
|
|
13904
14100
|
}
|
|
@@ -13970,9 +14166,6 @@ class CodexAppServerSession {
|
|
|
13970
14166
|
const activeTurn = this.activeTurn;
|
|
13971
14167
|
this.activeTurn = null;
|
|
13972
14168
|
if (activeTurn) {
|
|
13973
|
-
if (activeTurn.timer) {
|
|
13974
|
-
clearTimeout(activeTurn.timer);
|
|
13975
|
-
}
|
|
13976
14169
|
for (const watcher of this.drainTurnWatchers(activeTurn)) {
|
|
13977
14170
|
watcher.reject(error);
|
|
13978
14171
|
}
|
|
@@ -14076,6 +14269,8 @@ async function shutdownCodexAppServerAgent(options, shutdownOptions = {}) {
|
|
|
14076
14269
|
if (!session) {
|
|
14077
14270
|
if (shutdownOptions.resetThread) {
|
|
14078
14271
|
const runtimeDirectory = options.runtimeDirectory;
|
|
14272
|
+
const threadId = await readOptionalFile2(join13(runtimeDirectory, "codex-thread-id.txt"));
|
|
14273
|
+
await closeCodexSessionCatalog(runtimeDirectory, threadId);
|
|
14079
14274
|
await rm4(join13(runtimeDirectory, "codex-thread-id.txt"), { force: true });
|
|
14080
14275
|
}
|
|
14081
14276
|
return;
|
|
@@ -14627,12 +14822,15 @@ function runtimePackageDir() {
|
|
|
14627
14822
|
const explicit = process.env.OPENSCOUT_RUNTIME_PACKAGE_DIR?.trim();
|
|
14628
14823
|
if (explicit)
|
|
14629
14824
|
return explicit;
|
|
14630
|
-
const
|
|
14631
|
-
if (
|
|
14632
|
-
return
|
|
14825
|
+
const fromBundledPackage = findBundledRuntimeDir();
|
|
14826
|
+
if (fromBundledPackage)
|
|
14827
|
+
return fromBundledPackage;
|
|
14633
14828
|
const fromCwd = findWorkspaceRuntimeDir(process.cwd());
|
|
14634
14829
|
if (fromCwd)
|
|
14635
14830
|
return fromCwd;
|
|
14831
|
+
const fromGlobal = findGlobalRuntimeDir();
|
|
14832
|
+
if (fromGlobal)
|
|
14833
|
+
return fromGlobal;
|
|
14636
14834
|
const moduleDir = dirname6(fileURLToPath4(import.meta.url));
|
|
14637
14835
|
return resolve5(moduleDir, "..");
|
|
14638
14836
|
}
|
|
@@ -14654,6 +14852,8 @@ function findGlobalRuntimeDir() {
|
|
|
14654
14852
|
const scoutBin = result.stdout?.trim();
|
|
14655
14853
|
if (scoutBin) {
|
|
14656
14854
|
const scoutPkg = resolve5(scoutBin, "..", "..");
|
|
14855
|
+
if (isInstalledRuntimePackageDir(scoutPkg))
|
|
14856
|
+
return scoutPkg;
|
|
14657
14857
|
const nested = join15(scoutPkg, "node_modules", "@openscout", "runtime");
|
|
14658
14858
|
if (isInstalledRuntimePackageDir(nested))
|
|
14659
14859
|
return nested;
|
|
@@ -14664,6 +14864,11 @@ function findGlobalRuntimeDir() {
|
|
|
14664
14864
|
} catch {}
|
|
14665
14865
|
return null;
|
|
14666
14866
|
}
|
|
14867
|
+
function findBundledRuntimeDir() {
|
|
14868
|
+
const moduleDir = dirname6(fileURLToPath4(import.meta.url));
|
|
14869
|
+
const candidate = resolve5(moduleDir, "..");
|
|
14870
|
+
return isInstalledRuntimePackageDir(candidate) ? candidate : null;
|
|
14871
|
+
}
|
|
14667
14872
|
function findWorkspaceRuntimeDir(startDir) {
|
|
14668
14873
|
let current = resolve5(startDir);
|
|
14669
14874
|
while (true) {
|
|
@@ -14702,10 +14907,21 @@ function resolveBrokerStartTimeoutMs() {
|
|
|
14702
14907
|
return DEFAULT_BROKER_START_TIMEOUT_MS;
|
|
14703
14908
|
}
|
|
14704
14909
|
function resolveBrokerServiceLabel(mode) {
|
|
14705
|
-
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();
|
|
14706
14911
|
if (explicit) {
|
|
14707
14912
|
return explicit;
|
|
14708
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) {
|
|
14709
14925
|
switch (mode) {
|
|
14710
14926
|
case "prod":
|
|
14711
14927
|
return "com.openscout.broker";
|
|
@@ -14764,6 +14980,7 @@ function renderLaunchAgentPlist(config) {
|
|
|
14764
14980
|
OPENSCOUT_CONTROL_HOME: config.controlHome,
|
|
14765
14981
|
OPENSCOUT_BROKER_SERVICE_MODE: config.mode,
|
|
14766
14982
|
OPENSCOUT_BROKER_SERVICE_LABEL: config.label,
|
|
14983
|
+
OPENSCOUT_SERVICE_LABEL: config.label,
|
|
14767
14984
|
OPENSCOUT_ADVERTISE_SCOPE: config.advertiseScope,
|
|
14768
14985
|
HOME: homedir14(),
|
|
14769
14986
|
PATH: launchPath,
|
|
@@ -14792,7 +15009,7 @@ function renderLaunchAgentPlist(config) {
|
|
|
14792
15009
|
<array>
|
|
14793
15010
|
<string>${xmlEscape(config.bunExecutable)}</string>
|
|
14794
15011
|
<string>${xmlEscape(join15(config.runtimePackageDir, "bin", "openscout-runtime.mjs"))}</string>
|
|
14795
|
-
<string>
|
|
15012
|
+
<string>base</string>
|
|
14796
15013
|
</array>
|
|
14797
15014
|
<key>WorkingDirectory</key>
|
|
14798
15015
|
<string>${xmlEscape(config.runtimePackageDir)}</string>
|
|
@@ -14856,6 +15073,22 @@ function writeLaunchAgentPlist(config) {
|
|
|
14856
15073
|
ensureServiceDirectories(config);
|
|
14857
15074
|
writeFileSync5(config.launchAgentPath, renderLaunchAgentPlist(config), "utf8");
|
|
14858
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
|
+
}
|
|
14859
15092
|
function runCommand(command, args, options) {
|
|
14860
15093
|
const result = spawnSync(command, args, {
|
|
14861
15094
|
env: {
|
|
@@ -14974,6 +15207,7 @@ async function brokerServiceStatus(config = resolveBrokerServiceConfig()) {
|
|
|
14974
15207
|
label: config.label,
|
|
14975
15208
|
mode: config.mode,
|
|
14976
15209
|
launchAgentPath: config.launchAgentPath,
|
|
15210
|
+
bootoutCommand: bootoutCommand(config),
|
|
14977
15211
|
brokerUrl: config.brokerUrl,
|
|
14978
15212
|
brokerSocketPath: config.brokerSocketPath,
|
|
14979
15213
|
supportDirectory: config.supportDirectory,
|
|
@@ -14993,10 +15227,12 @@ async function brokerServiceStatus(config = resolveBrokerServiceConfig()) {
|
|
|
14993
15227
|
};
|
|
14994
15228
|
}
|
|
14995
15229
|
async function installBrokerService(config = resolveBrokerServiceConfig()) {
|
|
15230
|
+
bootoutLegacyBrokerService(config);
|
|
14996
15231
|
writeLaunchAgentPlist(config);
|
|
14997
15232
|
return brokerServiceStatus(config);
|
|
14998
15233
|
}
|
|
14999
15234
|
async function startBrokerService(config = resolveBrokerServiceConfig()) {
|
|
15235
|
+
bootoutLegacyBrokerService(config);
|
|
15000
15236
|
writeLaunchAgentPlist(config);
|
|
15001
15237
|
runCommand(launchctlPath(), ["bootout", config.serviceTarget], { allowFailure: true });
|
|
15002
15238
|
runCommand(launchctlPath(), ["bootstrap", config.domainTarget, config.launchAgentPath], { allowFailure: true });
|
|
@@ -15035,6 +15271,11 @@ async function uninstallBrokerService(config = resolveBrokerServiceConfig()) {
|
|
|
15035
15271
|
if (existsSync13(config.launchAgentPath)) {
|
|
15036
15272
|
rmSync2(config.launchAgentPath, { force: true });
|
|
15037
15273
|
}
|
|
15274
|
+
const legacyPath = legacyLaunchAgentPath(config);
|
|
15275
|
+
bootoutLegacyBrokerService(config);
|
|
15276
|
+
if (existsSync13(legacyPath)) {
|
|
15277
|
+
rmSync2(legacyPath, { force: true });
|
|
15278
|
+
}
|
|
15038
15279
|
return brokerServiceStatus(config);
|
|
15039
15280
|
}
|
|
15040
15281
|
async function main() {
|
|
@@ -15076,6 +15317,7 @@ function formatBrokerServiceStatus(status) {
|
|
|
15076
15317
|
`label: ${status.label}`,
|
|
15077
15318
|
`mode: ${status.mode}`,
|
|
15078
15319
|
`launch agent: ${status.installed ? status.launchAgentPath : "not installed"}`,
|
|
15320
|
+
`bootout: ${status.bootoutCommand}`,
|
|
15079
15321
|
`loaded: ${status.loaded ? "yes" : "no"}`,
|
|
15080
15322
|
`pid: ${status.pid ?? "\u2014"}`,
|
|
15081
15323
|
`launchd state: ${status.launchdState ?? "\u2014"}`,
|
|
@@ -15105,6 +15347,58 @@ var LOCAL_AGENT_SYSTEM_PROMPT_TEMPLATE_HINT = [
|
|
|
15105
15347
|
"{{relay_hub}}, {{broker_url}}, {{relay_command}}, {{openscout_root}}, {{relay_agent_comms_skill}}, and {{env.NAME}} variables."
|
|
15106
15348
|
].join("");
|
|
15107
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
|
+
|
|
15108
15402
|
// ../runtime/src/local-agents.ts
|
|
15109
15403
|
var MODULE_DIRECTORY = dirname7(fileURLToPath5(import.meta.url));
|
|
15110
15404
|
var OPENSCOUT_REPO_ROOT = resolve6(MODULE_DIRECTORY, "..", "..", "..");
|
|
@@ -15554,6 +15848,46 @@ function buildLaunchArgsForRequestedModel(harness, model) {
|
|
|
15554
15848
|
}
|
|
15555
15849
|
return [];
|
|
15556
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
|
+
}
|
|
15557
15891
|
function applyRequestedModelToLaunchArgs(harness, launchArgs, model) {
|
|
15558
15892
|
const normalized = normalizeLaunchArgsForHarness(harness, launchArgs);
|
|
15559
15893
|
const requestedModel = normalizeRequestedModel(model);
|
|
@@ -15565,6 +15899,17 @@ function applyRequestedModelToLaunchArgs(harness, launchArgs, model) {
|
|
|
15565
15899
|
...buildLaunchArgsForRequestedModel(harness, requestedModel)
|
|
15566
15900
|
];
|
|
15567
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
|
+
}
|
|
15568
15913
|
function defaultHarnessForOverride(override, fallback = "claude") {
|
|
15569
15914
|
return normalizeManagedHarness2(override.defaultHarness ?? override.runtime?.harness, fallback);
|
|
15570
15915
|
}
|
|
@@ -15584,16 +15929,17 @@ function overrideHarnessProfile(override, harness) {
|
|
|
15584
15929
|
cwd: normalizeProjectPath(profile?.cwd || override.runtime?.cwd || override.projectRoot || process.cwd()),
|
|
15585
15930
|
transport: normalizeLocalAgentTransport(profile?.transport ?? override.runtime?.transport, harness),
|
|
15586
15931
|
sessionId: normalizeTmuxSessionName2(profile?.sessionId, `${override.agentId}-${harness}`),
|
|
15587
|
-
launchArgs: launchArgsForOverrideHarness(override, harness)
|
|
15932
|
+
launchArgs: launchArgsForOverrideHarness(override, harness),
|
|
15933
|
+
...profile?.permissionProfile ? { permissionProfile: profile.permissionProfile } : {}
|
|
15588
15934
|
};
|
|
15589
15935
|
}
|
|
15590
15936
|
function normalizeManagedHarness2(value, fallback) {
|
|
15591
|
-
return value === "codex" ? "codex" : value === "claude" ? "claude" : fallback;
|
|
15937
|
+
return value === "codex" ? "codex" : value === "claude" ? "claude" : value === "cursor" ? "cursor" : fallback;
|
|
15592
15938
|
}
|
|
15593
15939
|
function normalizeLocalHarnessProfiles(agentId, record) {
|
|
15594
15940
|
const defaultHarness = normalizeManagedHarness2(typeof record.defaultHarness === "string" ? record.defaultHarness : record.harness, "claude");
|
|
15595
15941
|
const nextProfiles = {};
|
|
15596
|
-
for (const harness of ["claude", "codex"]) {
|
|
15942
|
+
for (const harness of ["claude", "codex", "cursor"]) {
|
|
15597
15943
|
const profile = record.harnessProfiles?.[harness];
|
|
15598
15944
|
if (!profile) {
|
|
15599
15945
|
continue;
|
|
@@ -15602,7 +15948,8 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
15602
15948
|
cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
|
|
15603
15949
|
transport: normalizeLocalAgentTransport(profile.transport, harness),
|
|
15604
15950
|
sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
|
|
15605
|
-
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
|
|
15951
|
+
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs),
|
|
15952
|
+
...profile.permissionProfile ? { permissionProfile: profile.permissionProfile } : {}
|
|
15606
15953
|
};
|
|
15607
15954
|
}
|
|
15608
15955
|
const runtimeHarness = normalizeManagedHarness2(record.harness, defaultHarness);
|
|
@@ -15611,7 +15958,8 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
15611
15958
|
cwd: normalizeProjectPath(record.cwd || process.cwd()),
|
|
15612
15959
|
transport: normalizeLocalAgentTransport(record.transport, runtimeHarness),
|
|
15613
15960
|
sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${runtimeHarness}`),
|
|
15614
|
-
launchArgs: normalizeLaunchArgsForHarness(runtimeHarness, record.launchArgs)
|
|
15961
|
+
launchArgs: normalizeLaunchArgsForHarness(runtimeHarness, record.launchArgs),
|
|
15962
|
+
...record.permissionProfile ? { permissionProfile: record.permissionProfile } : {}
|
|
15615
15963
|
};
|
|
15616
15964
|
}
|
|
15617
15965
|
if (!nextProfiles[defaultHarness]) {
|
|
@@ -15619,10 +15967,11 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
15619
15967
|
cwd: normalizeProjectPath(record.cwd || process.cwd()),
|
|
15620
15968
|
transport: normalizeLocalAgentTransport(record.transport, defaultHarness),
|
|
15621
15969
|
sessionId: normalizeTmuxSessionName2(record.tmuxSession, `${agentId}-${defaultHarness}`),
|
|
15622
|
-
launchArgs: normalizeLaunchArgsForHarness(defaultHarness, record.launchArgs)
|
|
15970
|
+
launchArgs: normalizeLaunchArgsForHarness(defaultHarness, record.launchArgs),
|
|
15971
|
+
...record.permissionProfile ? { permissionProfile: record.permissionProfile } : {}
|
|
15623
15972
|
};
|
|
15624
15973
|
}
|
|
15625
|
-
for (const harness of ["claude", "codex"]) {
|
|
15974
|
+
for (const harness of ["claude", "codex", "cursor"]) {
|
|
15626
15975
|
const profile = nextProfiles[harness];
|
|
15627
15976
|
if (!profile) {
|
|
15628
15977
|
continue;
|
|
@@ -15631,7 +15980,8 @@ function normalizeLocalHarnessProfiles(agentId, record) {
|
|
|
15631
15980
|
cwd: normalizeProjectPath(profile.cwd || record.cwd || process.cwd()),
|
|
15632
15981
|
transport: normalizeLocalAgentTransport(profile.transport, harness),
|
|
15633
15982
|
sessionId: normalizeTmuxSessionName2(profile.sessionId, `${agentId}-${harness}`),
|
|
15634
|
-
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs)
|
|
15983
|
+
launchArgs: normalizeLaunchArgsForHarness(harness, profile.launchArgs),
|
|
15984
|
+
...profile.permissionProfile ? { permissionProfile: profile.permissionProfile } : {}
|
|
15635
15985
|
};
|
|
15636
15986
|
}
|
|
15637
15987
|
return nextProfiles;
|
|
@@ -15656,13 +16006,15 @@ function recordForHarness(record, harnessOverride) {
|
|
|
15656
16006
|
cwd: fallbackCwd,
|
|
15657
16007
|
transport: fallbackTransport,
|
|
15658
16008
|
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs),
|
|
16009
|
+
...normalized.permissionProfile ? { permissionProfile: normalized.permissionProfile } : {},
|
|
15659
16010
|
harnessProfiles: {
|
|
15660
16011
|
...normalized.harnessProfiles,
|
|
15661
16012
|
[selectedHarness]: {
|
|
15662
16013
|
cwd: fallbackCwd,
|
|
15663
16014
|
transport: fallbackTransport,
|
|
15664
16015
|
sessionId: fallbackSessionId,
|
|
15665
|
-
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs)
|
|
16016
|
+
launchArgs: normalizeLaunchArgsForHarness(selectedHarness, normalized.launchArgs),
|
|
16017
|
+
...normalized.permissionProfile ? { permissionProfile: normalized.permissionProfile } : {}
|
|
15666
16018
|
}
|
|
15667
16019
|
}
|
|
15668
16020
|
};
|
|
@@ -15674,7 +16026,8 @@ function recordForHarness(record, harnessOverride) {
|
|
|
15674
16026
|
tmuxSession: profile.sessionId,
|
|
15675
16027
|
cwd: profile.cwd,
|
|
15676
16028
|
transport: profile.transport,
|
|
15677
|
-
launchArgs: profile.launchArgs
|
|
16029
|
+
launchArgs: profile.launchArgs,
|
|
16030
|
+
permissionProfile: profile.permissionProfile
|
|
15678
16031
|
};
|
|
15679
16032
|
}
|
|
15680
16033
|
function normalizeLocalAgentRecord(agentId, record) {
|
|
@@ -15705,6 +16058,7 @@ function normalizeLocalAgentRecord(agentId, record) {
|
|
|
15705
16058
|
transport: activeProfile?.transport ?? normalizeLocalAgentTransport(record.transport, harness),
|
|
15706
16059
|
capabilities: normalizeLocalAgentCapabilities(record.capabilities),
|
|
15707
16060
|
launchArgs: activeProfile?.launchArgs ?? normalizeLaunchArgsForHarness(harness, record.launchArgs),
|
|
16061
|
+
permissionProfile: activeProfile?.permissionProfile ?? record.permissionProfile,
|
|
15708
16062
|
registrationSource: record.registrationSource
|
|
15709
16063
|
};
|
|
15710
16064
|
}
|
|
@@ -15790,6 +16144,7 @@ function relayAgentOverrideFromLocalAgentRecord(agentId, record, existing) {
|
|
|
15790
16144
|
};
|
|
15791
16145
|
}
|
|
15792
16146
|
function buildCodexAgentSessionOptions(agentName, record, systemPrompt) {
|
|
16147
|
+
const permissionPosture = compileCodexPermissionProfile(record.permissionProfile);
|
|
15793
16148
|
return {
|
|
15794
16149
|
agentName,
|
|
15795
16150
|
sessionId: record.tmuxSession,
|
|
@@ -15797,7 +16152,10 @@ function buildCodexAgentSessionOptions(agentName, record, systemPrompt) {
|
|
|
15797
16152
|
systemPrompt: systemPrompt ?? buildLocalAgentSystemPrompt(agentName, record.project, record.cwd, { transport: "codex_app_server" }),
|
|
15798
16153
|
runtimeDirectory: relayAgentRuntimeDirectory(agentName),
|
|
15799
16154
|
logsDirectory: relayAgentLogsDirectory(agentName),
|
|
15800
|
-
launchArgs: normalizeLaunchArgsForHarness("codex", record.launchArgs)
|
|
16155
|
+
launchArgs: normalizeLaunchArgsForHarness("codex", record.launchArgs),
|
|
16156
|
+
permissionProfile: permissionPosture.profile,
|
|
16157
|
+
approvalPolicy: permissionPosture.approvalPolicy,
|
|
16158
|
+
sandbox: permissionPosture.sandbox
|
|
15801
16159
|
};
|
|
15802
16160
|
}
|
|
15803
16161
|
function buildClaudeAgentSessionOptions(agentName, record, systemPrompt) {
|
|
@@ -16054,6 +16412,7 @@ async function startLocalAgent(input) {
|
|
|
16054
16412
|
const currentDirectory = input.currentDirectory ?? projectPath;
|
|
16055
16413
|
const effectiveCwd = input.cwdOverride ? normalizeProjectPath(input.cwdOverride) : undefined;
|
|
16056
16414
|
const shouldEnsureOnline = input.ensureOnline !== false;
|
|
16415
|
+
const requestedPermissionProfile = parseScoutPermissionProfile(input.permissionProfile);
|
|
16057
16416
|
const requestedDefinitionId = input.agentName?.trim() ? normalizeAgentSelectorSegment(input.agentName.trim()) : "";
|
|
16058
16417
|
if (input.agentName?.trim() && !requestedDefinitionId) {
|
|
16059
16418
|
throw new Error(`Invalid agent name "${input.agentName}".`);
|
|
@@ -16109,7 +16468,10 @@ async function startLocalAgent(input) {
|
|
|
16109
16468
|
const effectiveHarness = normalizeManagedHarness2(preferredHarness ?? configDefaultHarness, "claude");
|
|
16110
16469
|
const transport = normalizeLocalAgentTransport(undefined, effectiveHarness);
|
|
16111
16470
|
const sessionId = normalizeTmuxSessionName2(undefined, `${instance.id}-${effectiveHarness}`);
|
|
16112
|
-
const launchArgs =
|
|
16471
|
+
const launchArgs = applyRequestedRuntimeOptionsToLaunchArgs(effectiveHarness, [], {
|
|
16472
|
+
model: input.model,
|
|
16473
|
+
reasoningEffort: input.reasoningEffort
|
|
16474
|
+
});
|
|
16113
16475
|
const existingForInstance = overrides[instance.id];
|
|
16114
16476
|
if (existingForInstance && normalizeProjectPath(existingForInstance.projectRoot) !== projectRoot) {
|
|
16115
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.`);
|
|
@@ -16130,7 +16492,8 @@ async function startLocalAgent(input) {
|
|
|
16130
16492
|
cwd: effectiveCwd ?? projectRoot,
|
|
16131
16493
|
transport,
|
|
16132
16494
|
sessionId,
|
|
16133
|
-
launchArgs
|
|
16495
|
+
launchArgs,
|
|
16496
|
+
...requestedPermissionProfile ? { permissionProfile: requestedPermissionProfile } : {}
|
|
16134
16497
|
}
|
|
16135
16498
|
},
|
|
16136
16499
|
runtime: {
|
|
@@ -16154,7 +16517,10 @@ async function startLocalAgent(input) {
|
|
|
16154
16517
|
const nextHarness = normalizeManagedHarness2(resolvedHarness, "claude");
|
|
16155
16518
|
const nextDefaultHarness = preferredHarness ? normalizeManagedHarness2(preferredHarness, "claude") : defaultHarnessForOverride(matchingOverride, "claude");
|
|
16156
16519
|
const nextSessionId = normalizeTmuxSessionName2(undefined, `${instance.id}-${nextHarness}`);
|
|
16157
|
-
const nextLaunchArgs =
|
|
16520
|
+
const nextLaunchArgs = applyRequestedRuntimeOptionsToLaunchArgs(nextHarness, launchArgsForOverrideHarness(matchingOverride, nextHarness), {
|
|
16521
|
+
model: input.model,
|
|
16522
|
+
reasoningEffort: input.reasoningEffort
|
|
16523
|
+
});
|
|
16158
16524
|
overrides[instance.id] = {
|
|
16159
16525
|
agentId: instance.id,
|
|
16160
16526
|
definitionId: requestedDefinitionId,
|
|
@@ -16172,7 +16538,8 @@ async function startLocalAgent(input) {
|
|
|
16172
16538
|
[nextHarness]: {
|
|
16173
16539
|
...overrideHarnessProfile(matchingOverride, nextHarness),
|
|
16174
16540
|
sessionId: nextSessionId,
|
|
16175
|
-
launchArgs: nextLaunchArgs
|
|
16541
|
+
launchArgs: nextLaunchArgs,
|
|
16542
|
+
...requestedPermissionProfile ? { permissionProfile: requestedPermissionProfile } : {}
|
|
16176
16543
|
}
|
|
16177
16544
|
},
|
|
16178
16545
|
runtime: {
|
|
@@ -16186,9 +16553,12 @@ async function startLocalAgent(input) {
|
|
|
16186
16553
|
await writeRelayAgentOverrides(overrides);
|
|
16187
16554
|
targetAgentId = instance.id;
|
|
16188
16555
|
} else {
|
|
16189
|
-
if (input.model) {
|
|
16556
|
+
if (input.model || input.reasoningEffort || requestedPermissionProfile) {
|
|
16190
16557
|
const existingHarness = normalizeManagedHarness2(preferredHarness ?? matchingOverride.defaultHarness ?? matchingOverride.runtime?.harness, "claude");
|
|
16191
|
-
const nextLaunchArgs =
|
|
16558
|
+
const nextLaunchArgs = applyRequestedRuntimeOptionsToLaunchArgs(existingHarness, launchArgsForOverrideHarness(matchingOverride, existingHarness), {
|
|
16559
|
+
model: input.model,
|
|
16560
|
+
reasoningEffort: input.reasoningEffort
|
|
16561
|
+
});
|
|
16192
16562
|
overrides[matchingAgentId] = {
|
|
16193
16563
|
...matchingOverride,
|
|
16194
16564
|
launchArgs: existingHarness === defaultHarnessForOverride(matchingOverride, existingHarness) ? nextLaunchArgs : matchingOverride.launchArgs,
|
|
@@ -16196,7 +16566,8 @@ async function startLocalAgent(input) {
|
|
|
16196
16566
|
...matchingOverride.harnessProfiles ?? {},
|
|
16197
16567
|
[existingHarness]: {
|
|
16198
16568
|
...overrideHarnessProfile(matchingOverride, existingHarness),
|
|
16199
|
-
launchArgs: nextLaunchArgs
|
|
16569
|
+
launchArgs: nextLaunchArgs,
|
|
16570
|
+
...requestedPermissionProfile ? { permissionProfile: requestedPermissionProfile } : {}
|
|
16200
16571
|
}
|
|
16201
16572
|
}
|
|
16202
16573
|
};
|
|
@@ -16300,6 +16671,7 @@ function readPersistedClaudeSessionId(agentName) {
|
|
|
16300
16671
|
function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
16301
16672
|
const normalizedRecord = normalizeLocalAgentRecord(agentId, record);
|
|
16302
16673
|
const configuredModel = readLaunchModelForHarness(normalizeLocalAgentHarness(normalizedRecord.harness), normalizedRecord.launchArgs);
|
|
16674
|
+
const codexPermissionPosture = normalizeLocalAgentHarness(normalizedRecord.harness) === "codex" ? compileCodexPermissionProfile(normalizedRecord.permissionProfile) : null;
|
|
16303
16675
|
const definitionId = normalizedRecord.definitionId ?? agentId;
|
|
16304
16676
|
const displayName = titleCaseLocalAgentName(definitionId);
|
|
16305
16677
|
const projectRoot = normalizedRecord.projectRoot ?? normalizedRecord.cwd;
|
|
@@ -16326,7 +16698,13 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
16326
16698
|
nodeQualifier: instance.nodeQualifier,
|
|
16327
16699
|
workspaceQualifier: instance.workspaceQualifier,
|
|
16328
16700
|
branch: instance.branch,
|
|
16329
|
-
...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
|
+
} : {}
|
|
16330
16708
|
}
|
|
16331
16709
|
},
|
|
16332
16710
|
agent: {
|
|
@@ -16355,7 +16733,13 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
16355
16733
|
nodeQualifier: instance.nodeQualifier,
|
|
16356
16734
|
workspaceQualifier: instance.workspaceQualifier,
|
|
16357
16735
|
branch: instance.branch,
|
|
16358
|
-
...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
|
+
} : {}
|
|
16359
16743
|
},
|
|
16360
16744
|
agentClass: "general",
|
|
16361
16745
|
capabilities: normalizeLocalAgentCapabilities(normalizedRecord.capabilities),
|
|
@@ -16390,6 +16774,12 @@ function buildLocalAgentBinding(agentId, record, alive, nodeId, source) {
|
|
|
16390
16774
|
workspaceQualifier: instance.workspaceQualifier,
|
|
16391
16775
|
branch: instance.branch,
|
|
16392
16776
|
...configuredModel ? { model: configuredModel } : {},
|
|
16777
|
+
...codexPermissionPosture ? {
|
|
16778
|
+
permissionProfile: codexPermissionPosture.profile,
|
|
16779
|
+
approvalPolicy: codexPermissionPosture.approvalPolicy,
|
|
16780
|
+
sandbox: codexPermissionPosture.sandbox,
|
|
16781
|
+
permissionEnforcement: codexPermissionPosture.enforcement
|
|
16782
|
+
} : {},
|
|
16393
16783
|
...externalSessionId ? { externalSessionId } : {}
|
|
16394
16784
|
}
|
|
16395
16785
|
}
|
|
@@ -16488,7 +16878,11 @@ var scoutBrokerPaths = {
|
|
|
16488
16878
|
deliver: "/v1/deliver",
|
|
16489
16879
|
activity: "/v1/activity",
|
|
16490
16880
|
collaborationRecords: "/v1/collaboration/records",
|
|
16491
|
-
collaborationEvents: "/v1/collaboration/events"
|
|
16881
|
+
collaborationEvents: "/v1/collaboration/events",
|
|
16882
|
+
pairingAttach: "/v1/pairing/attach",
|
|
16883
|
+
pairingDetach: "/v1/pairing/detach",
|
|
16884
|
+
localSessionsAttach: "/v1/local-sessions/attach",
|
|
16885
|
+
localSessionsDetach: "/v1/local-sessions/detach"
|
|
16492
16886
|
}
|
|
16493
16887
|
};
|
|
16494
16888
|
|
|
@@ -16845,7 +17239,10 @@ async function sendScoutDirectMessage(input) {
|
|
|
16845
17239
|
body: input.body.trim(),
|
|
16846
17240
|
intent: "consult",
|
|
16847
17241
|
replyToMessageId: input.replyToMessageId ?? undefined,
|
|
16848
|
-
execution:
|
|
17242
|
+
execution: {
|
|
17243
|
+
...input.executionHarness ? { harness: input.executionHarness } : {},
|
|
17244
|
+
session: "new"
|
|
17245
|
+
},
|
|
16849
17246
|
ensureAwake: true,
|
|
16850
17247
|
messageMetadata: {
|
|
16851
17248
|
source,
|
|
@@ -36238,7 +36635,7 @@ function deleteMobilePushRegistrationByToken(pushToken) {
|
|
|
36238
36635
|
writeDb().query(`DELETE FROM mobile_push_registrations WHERE push_token = ?1`).run(token);
|
|
36239
36636
|
}
|
|
36240
36637
|
function base64UrlEncode(input) {
|
|
36241
|
-
const buffer = typeof input === "string" ? Buffer.from(input, "utf8") : input;
|
|
36638
|
+
const buffer = typeof input === "string" ? Buffer.from(input, "utf8") : Buffer.from(Array.from(input));
|
|
36242
36639
|
return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
36243
36640
|
}
|
|
36244
36641
|
function loadApnsCredentials() {
|
|
@@ -36283,7 +36680,7 @@ function apnsJwt(credentials) {
|
|
|
36283
36680
|
}));
|
|
36284
36681
|
const unsignedToken = `${header}.${claims}`;
|
|
36285
36682
|
const privateKey = createPrivateKey(credentials.privateKeyPem);
|
|
36286
|
-
const signature = signWithKey("sha256",
|
|
36683
|
+
const signature = signWithKey("sha256", new TextEncoder().encode(unsignedToken), privateKey);
|
|
36287
36684
|
const token = `${unsignedToken}.${base64UrlEncode(signature)}`;
|
|
36288
36685
|
cachedApnsJwt = {
|
|
36289
36686
|
cacheKey,
|
|
@@ -36339,11 +36736,11 @@ async function sendApnsAlertToRegistration(registration, alert) {
|
|
|
36339
36736
|
apnsId = typeof headers["apns-id"] === "string" ? headers["apns-id"] : null;
|
|
36340
36737
|
});
|
|
36341
36738
|
request.on("data", (chunk) => {
|
|
36342
|
-
chunks.push(
|
|
36739
|
+
chunks.push(chunk);
|
|
36343
36740
|
});
|
|
36344
36741
|
request.on("end", () => {
|
|
36345
36742
|
client.close();
|
|
36346
|
-
const rawBody = chunks.
|
|
36743
|
+
const rawBody = chunks.join("");
|
|
36347
36744
|
let reason = null;
|
|
36348
36745
|
if (rawBody.trim().length > 0) {
|
|
36349
36746
|
try {
|
|
@@ -40062,6 +40459,7 @@ async function startSupervisorRuntime(state) {
|
|
|
40062
40459
|
relay: activeRelayUrl,
|
|
40063
40460
|
pairing: {
|
|
40064
40461
|
relay: payload.relay,
|
|
40462
|
+
...payload.fallbackRelays?.length ? { fallbackRelays: payload.fallbackRelays } : {},
|
|
40065
40463
|
room: payload.room,
|
|
40066
40464
|
publicKey: payload.publicKey,
|
|
40067
40465
|
expiresAt: payload.expiresAt,
|