orquesta-agent 0.2.234 → 0.2.238
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 +45 -0
- package/dist/cli-auth-detect.d.ts +17 -0
- package/dist/cli-auth-detect.d.ts.map +1 -0
- package/dist/cli-auth-detect.js +31 -0
- package/dist/cli-auth-detect.js.map +1 -0
- package/dist/executor.d.ts +13 -4
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +257 -68
- package/dist/executor.js.map +1 -1
- package/dist/hook.d.ts +26 -1
- package/dist/hook.d.ts.map +1 -1
- package/dist/hook.js +170 -21
- package/dist/hook.js.map +1 -1
- package/dist/index.js +28 -14
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +42 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +100 -28
- package/dist/init.js.map +1 -1
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/sandbox.js +4 -0
- package/dist/sandbox.js.map +1 -1
- package/dist/supabase.d.ts +18 -8
- package/dist/supabase.d.ts.map +1 -1
- package/dist/supabase.js +36 -2
- package/dist/supabase.js.map +1 -1
- package/dist/types/hook-types.d.ts +49 -1
- package/dist/types/hook-types.d.ts.map +1 -1
- package/dist/ui/onboarding.js +1 -1
- package/dist/ui/onboarding.js.map +1 -1
- package/dist/ui/public/app.js +11 -1
- package/dist/ui/public/index.html +1 -0
- package/dist/ui/public/style.css +1 -0
- package/dist/ui/public/welcome.html +2 -0
- package/dist/ui/server.d.ts +1 -1
- package/dist/ui/server.d.ts.map +1 -1
- package/dist/ui/server.js +2 -2
- package/dist/ui/server.js.map +1 -1
- package/dist/ui/types.d.ts +1 -1
- package/dist/ui/types.d.ts.map +1 -1
- package/dist/ws-client.d.ts +1 -1
- package/dist/ws-client.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/executor.js
CHANGED
|
@@ -122,11 +122,12 @@ import * as logger from './logger.js';
|
|
|
122
122
|
// interactive-session spawn — node-pty can't launch the `.cmd` shim).
|
|
123
123
|
const nodeRequire = createRequire(import.meta.url);
|
|
124
124
|
import { isSandboxAvailable, buildBwrapArgs, shQuote, ensureStrictProjectDirs, encodeClaudeProjectDir } from './sandbox.js';
|
|
125
|
+
import { isLoggedOutMessage } from './cli-auth-detect.js';
|
|
125
126
|
import { parseCoordSpec, runCoordination } from './coordination.js';
|
|
126
127
|
import { parseSudosudoInstallSpec, runSudosudoInstall } from './sudosudo.js';
|
|
127
128
|
import { sendOutput, sendComplete, sendError, sendSupervisionRequest, sendExecutionResumed, updatePromptStatus, persistOutputLogs, clearOutputBuffer, sendRequirement, persistRequirement, sendQAInstructions, persistQAInstructions, sendPlanItemsGenerated, sendSessionOutput, sendSessionStarted, sendSessionEnded, sendSessionError, sendSessionLog, reportAgentError } from './supabase.js';
|
|
128
129
|
import { startTranscriptTail } from './claude-transcript.js';
|
|
129
|
-
import { createThinkingLog, createToolCallLog, createToolResultLog, createOutputLog, createErrorLog, createSystemLog, } from './types/agent-logs.js';
|
|
130
|
+
import { createThinkingLog, createToolCallLog, createToolResultLog, createOutputLog, createErrorLog, createSystemLog, createProgressLog, } from './types/agent-logs.js';
|
|
130
131
|
/**
|
|
131
132
|
* Build args for the `script` PTY wrapper, accounting for BSD vs util-linux differences.
|
|
132
133
|
* - util-linux (Linux): `script -q -c "<cmd>" /dev/null`
|
|
@@ -874,6 +875,101 @@ export function resolveKimiBinary() {
|
|
|
874
875
|
export function isKimiCliAvailable() {
|
|
875
876
|
return resolveKimiBinary() !== null;
|
|
876
877
|
}
|
|
878
|
+
// Resolve Cursor's headless CLI (`cursor-agent`). Its installer
|
|
879
|
+
// (`curl https://cursor.com/install -fsS | bash`) drops the binary in
|
|
880
|
+
// ~/.local/bin and only PRINTS a reminder to add that to PATH — so on a machine
|
|
881
|
+
// where the user never edited their shell profile, a bare `cursor-agent` lookup
|
|
882
|
+
// fails while the CLI is in fact installed. Same trap as kimi.
|
|
883
|
+
// Order: CURSOR_AGENT_PATH → on PATH → ~/.local/bin/cursor-agent.
|
|
884
|
+
// On Windows the PowerShell installer does put it on PATH, so the PATH probe is
|
|
885
|
+
// the working path there.
|
|
886
|
+
let cursorBinaryCache = null;
|
|
887
|
+
export function resolveCursorBinary() {
|
|
888
|
+
const now = Date.now();
|
|
889
|
+
if (cursorBinaryCache && now - cursorBinaryCache.at < CLI_DETECTION_TTL_MS)
|
|
890
|
+
return cursorBinaryCache.path;
|
|
891
|
+
let resolved = null;
|
|
892
|
+
const envPath = process.env.CURSOR_AGENT_PATH;
|
|
893
|
+
if (envPath) {
|
|
894
|
+
try {
|
|
895
|
+
fs.accessSync(envPath, fs.constants.X_OK);
|
|
896
|
+
resolved = envPath;
|
|
897
|
+
}
|
|
898
|
+
catch { /* bad override */ }
|
|
899
|
+
}
|
|
900
|
+
if (!resolved) {
|
|
901
|
+
try {
|
|
902
|
+
execSync('cursor-agent --version', { stdio: 'pipe', timeout: 15000 });
|
|
903
|
+
resolved = 'cursor-agent';
|
|
904
|
+
}
|
|
905
|
+
catch { /* not on PATH */ }
|
|
906
|
+
}
|
|
907
|
+
if (!resolved) {
|
|
908
|
+
const candidate = path.join(os.homedir(), '.local', 'bin', 'cursor-agent');
|
|
909
|
+
try {
|
|
910
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
911
|
+
resolved = candidate;
|
|
912
|
+
}
|
|
913
|
+
catch { /* not installed there */ }
|
|
914
|
+
}
|
|
915
|
+
cursorBinaryCache = { path: resolved, at: now };
|
|
916
|
+
return resolved;
|
|
917
|
+
}
|
|
918
|
+
// Cursor CLI (the `cursor-agent` binary). Authenticated separately with
|
|
919
|
+
// `cursor-agent login` against the user's own Cursor subscription — Orquesta
|
|
920
|
+
// never sees that credential.
|
|
921
|
+
export function isCursorCliAvailable() {
|
|
922
|
+
return resolveCursorBinary() !== null;
|
|
923
|
+
}
|
|
924
|
+
// Is cursor-agent actually LOGGED IN, not just installed?
|
|
925
|
+
//
|
|
926
|
+
// Installed-but-logged-out is a real state and used to be invisible: the agent
|
|
927
|
+
// reported `authenticated: available`, so the dashboard showed a green tick, the
|
|
928
|
+
// user pinned Cursor, and every prompt failed at execution with cursor's own
|
|
929
|
+
// auth error. `cursor-agent status` is the documented way to ask.
|
|
930
|
+
//
|
|
931
|
+
// Deliberately conservative: it reports NOT authenticated only when cursor says
|
|
932
|
+
// so in words. Any other outcome (an older build with no `status` subcommand, a
|
|
933
|
+
// timeout, an unparseable answer) reports authenticated with a null method —
|
|
934
|
+
// crying wolf on a working machine is worse than the tick we had before, and
|
|
935
|
+
// nothing about EXECUTION depends on this flag; selectCli() still keys purely on
|
|
936
|
+
// the binary existing.
|
|
937
|
+
let cursorAuthCache = null;
|
|
938
|
+
export function checkCursorAuth() {
|
|
939
|
+
const now = Date.now();
|
|
940
|
+
if (cursorAuthCache && now - cursorAuthCache.at < CLI_DETECTION_TTL_MS)
|
|
941
|
+
return cursorAuthCache.result;
|
|
942
|
+
const cache = (result) => {
|
|
943
|
+
cursorAuthCache = { result, at: now };
|
|
944
|
+
return result;
|
|
945
|
+
};
|
|
946
|
+
const binary = resolveCursorBinary();
|
|
947
|
+
if (!binary)
|
|
948
|
+
return cache({ available: false, authenticated: false, method: null });
|
|
949
|
+
// An API key in the environment authenticates cursor-agent on its own, and is
|
|
950
|
+
// what CI machines use — no point spawning a process to confirm it.
|
|
951
|
+
if (process.env.CURSOR_API_KEY)
|
|
952
|
+
return cache({ available: true, authenticated: true, method: 'api_key' });
|
|
953
|
+
try {
|
|
954
|
+
const out = execSync(`"${binary}" status`, {
|
|
955
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
956
|
+
timeout: 15000,
|
|
957
|
+
encoding: 'utf-8',
|
|
958
|
+
});
|
|
959
|
+
if (isLoggedOutMessage(out))
|
|
960
|
+
return cache({ available: true, authenticated: false, method: null });
|
|
961
|
+
return cache({ available: true, authenticated: true, method: 'subscription' });
|
|
962
|
+
}
|
|
963
|
+
catch (error) {
|
|
964
|
+
// Non-zero exit covers both "logged out" and "this build has no `status`",
|
|
965
|
+
// so the OUTPUT decides, not the exit code.
|
|
966
|
+
const err = error;
|
|
967
|
+
const combined = `${err.stdout ?? ''}${err.stderr ?? ''}`;
|
|
968
|
+
if (isLoggedOutMessage(combined))
|
|
969
|
+
return cache({ available: true, authenticated: false, method: null });
|
|
970
|
+
return cache({ available: true, authenticated: true, method: null });
|
|
971
|
+
}
|
|
972
|
+
}
|
|
877
973
|
// Dedupe the fallback warning: selectCli() runs on every heartbeat (the agent
|
|
878
974
|
// reports activeCli), so logging unconditionally spammed thousands of identical
|
|
879
975
|
// WARN lines. Only log when the message actually changes.
|
|
@@ -884,53 +980,48 @@ function warnCliOnce(msg) {
|
|
|
884
980
|
lastCliWarn = msg;
|
|
885
981
|
logger.warn(msg);
|
|
886
982
|
}
|
|
983
|
+
// Preference order when nothing is pinned, and the order a fallback walks when
|
|
984
|
+
// the pinned CLI turns out not to be installed. Table-driven rather than a nest
|
|
985
|
+
// of pairwise ifs: every CLI added here otherwise costs another N branches, and
|
|
986
|
+
// the pair that got missed is exactly how a preference silently no-ops.
|
|
987
|
+
const CLI_AUTO_ORDER = ['orquesta', 'claude', 'kimi', 'cursor'];
|
|
988
|
+
const CLI_AUTO_REASON = {
|
|
989
|
+
orquesta: 'Auto: orquesta available (local LLM)',
|
|
990
|
+
claude: 'Auto: claude available (Anthropic API)',
|
|
991
|
+
kimi: 'Auto: kimi available',
|
|
992
|
+
cursor: 'Auto: cursor-agent available',
|
|
993
|
+
};
|
|
887
994
|
// Select which CLI to use based on availability and preference
|
|
888
995
|
export function selectCli() {
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
996
|
+
const available = {
|
|
997
|
+
orquesta: isOrquestaCliAvailable(),
|
|
998
|
+
claude: isClaudeCliAvailable(),
|
|
999
|
+
kimi: isKimiCliAvailable(),
|
|
1000
|
+
cursor: isCursorCliAvailable(),
|
|
1001
|
+
};
|
|
1002
|
+
if (!CLI_AUTO_ORDER.some(cli => available[cli])) {
|
|
1003
|
+
return { cli: null, reason: `No CLI found (${CLI_AUTO_ORDER.join(', ')})` };
|
|
895
1004
|
}
|
|
1005
|
+
const preferred = globalCliPreference;
|
|
896
1006
|
// Explicit preference, available → use it.
|
|
897
|
-
if (
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
if (
|
|
907
|
-
return { cli:
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
if (
|
|
912
|
-
warnCliOnce('Orquesta CLI preferred but not available, falling back');
|
|
913
|
-
if (hasClaude)
|
|
914
|
-
return { cli: 'claude', reason: 'Fallback: orquesta not available' };
|
|
915
|
-
if (hasKimi)
|
|
916
|
-
return { cli: 'kimi', reason: 'Fallback: orquesta not available' };
|
|
917
|
-
}
|
|
918
|
-
if (globalCliPreference === 'claude' && !hasClaude) {
|
|
919
|
-
warnCliOnce('Claude CLI preferred but not available, falling back');
|
|
920
|
-
if (hasOrquesta)
|
|
921
|
-
return { cli: 'orquesta', reason: 'Fallback: claude not available' };
|
|
922
|
-
if (hasKimi)
|
|
923
|
-
return { cli: 'kimi', reason: 'Fallback: claude not available' };
|
|
924
|
-
}
|
|
925
|
-
// Auto mode: prefer orquesta → claude → kimi.
|
|
926
|
-
if (hasOrquesta) {
|
|
1007
|
+
if (preferred !== 'auto' && available[preferred]) {
|
|
1008
|
+
lastCliWarn = null;
|
|
1009
|
+
return { cli: preferred, reason: `User preference: ${preferred}` };
|
|
1010
|
+
}
|
|
1011
|
+
// Explicit preference, NOT available → warn once, then fall through to the
|
|
1012
|
+
// auto order skipping the one that isn't there.
|
|
1013
|
+
if (preferred !== 'auto') {
|
|
1014
|
+
warnCliOnce(`${preferred} CLI preferred but not available, falling back`);
|
|
1015
|
+
const fallback = CLI_AUTO_ORDER.find(cli => cli !== preferred && available[cli]);
|
|
1016
|
+
if (fallback)
|
|
1017
|
+
return { cli: fallback, reason: `Fallback: ${preferred} not available` };
|
|
1018
|
+
return { cli: null, reason: 'No CLI found' };
|
|
1019
|
+
}
|
|
1020
|
+
const auto = CLI_AUTO_ORDER.find(cli => available[cli]);
|
|
1021
|
+
if (auto) {
|
|
927
1022
|
lastCliWarn = null;
|
|
928
|
-
return { cli:
|
|
1023
|
+
return { cli: auto, reason: CLI_AUTO_REASON[auto] };
|
|
929
1024
|
}
|
|
930
|
-
if (hasClaude)
|
|
931
|
-
return { cli: 'claude', reason: 'Auto: claude available (Anthropic API)' };
|
|
932
|
-
if (hasKimi)
|
|
933
|
-
return { cli: 'kimi', reason: 'Auto: kimi available' };
|
|
934
1025
|
return { cli: null, reason: 'No CLI found' };
|
|
935
1026
|
}
|
|
936
1027
|
// Check if Claude CLI is authenticated
|
|
@@ -1762,6 +1853,36 @@ export function getCurrentGitBranch(cwd) {
|
|
|
1762
1853
|
return null;
|
|
1763
1854
|
}
|
|
1764
1855
|
}
|
|
1856
|
+
// Args for a file-editing tool, trimmed so the live stream can show WHAT
|
|
1857
|
+
// changed without carrying a whole file through the socket. Returns null for
|
|
1858
|
+
// anything that isn't a file edit — those args stay dropped when oversized.
|
|
1859
|
+
const EDIT_SIDE_CAP = 800;
|
|
1860
|
+
export function compactFileEditArgs(args) {
|
|
1861
|
+
if (!args || typeof args !== 'object')
|
|
1862
|
+
return null;
|
|
1863
|
+
const a = args;
|
|
1864
|
+
const path = a.file_path ?? a.filePath ?? a.path;
|
|
1865
|
+
if (typeof path !== 'string' || !path)
|
|
1866
|
+
return null;
|
|
1867
|
+
const trim = (v) => {
|
|
1868
|
+
if (typeof v !== 'string')
|
|
1869
|
+
return undefined;
|
|
1870
|
+
return v.length > EDIT_SIDE_CAP ? v.slice(0, EDIT_SIDE_CAP) + '\n…[truncated]' : v;
|
|
1871
|
+
};
|
|
1872
|
+
const out = { file_path: path };
|
|
1873
|
+
const oldStr = trim(a.old_string ?? a.oldString);
|
|
1874
|
+
const newStr = trim(a.new_string ?? a.newString ?? a.content);
|
|
1875
|
+
if (oldStr !== undefined)
|
|
1876
|
+
out.old_string = oldStr;
|
|
1877
|
+
if (newStr !== undefined)
|
|
1878
|
+
out.new_string = newStr;
|
|
1879
|
+
if (typeof a.reason === 'string')
|
|
1880
|
+
out.reason = a.reason.slice(0, 200);
|
|
1881
|
+
// A bare path (read/list) carries no diff — not worth a payload.
|
|
1882
|
+
if (oldStr === undefined && newStr === undefined)
|
|
1883
|
+
return null;
|
|
1884
|
+
return JSON.stringify(out, null, 2);
|
|
1885
|
+
}
|
|
1765
1886
|
export async function execute(options) {
|
|
1766
1887
|
const { commandId, promptId, content, workingDirectory, channel, permissionMode, attachments } = options;
|
|
1767
1888
|
const startTime = Date.now();
|
|
@@ -1959,13 +2080,21 @@ ${userRequestBody}`;
|
|
|
1959
2080
|
// (cliCommand stays the TYPE for flag decisions.)
|
|
1960
2081
|
const cliBinary = cliCommand === 'kimi'
|
|
1961
2082
|
? (resolveKimiBinary() || 'kimi')
|
|
1962
|
-
: cliCommand === '
|
|
1963
|
-
? (
|
|
1964
|
-
:
|
|
2083
|
+
: cliCommand === 'cursor'
|
|
2084
|
+
? (resolveCursorBinary() || 'cursor-agent')
|
|
2085
|
+
: cliCommand === 'claude'
|
|
2086
|
+
? (resolveClaudeBinary() || 'claude')
|
|
2087
|
+
: (cliCommand || 'claude');
|
|
1965
2088
|
const cwd = workingDirectory || process.cwd();
|
|
1966
2089
|
// Which branch this prompt runs on — captured up front so it's persisted even
|
|
1967
2090
|
// if the run fails, and broadcast live below once sendSpecialEvent exists.
|
|
1968
2091
|
const currentBranch = getCurrentGitBranch(cwd);
|
|
2092
|
+
// Last commit this run produced. Detected from the CLI's own output further
|
|
2093
|
+
// down; it used to be broadcast live and then dropped on the floor, so a run
|
|
2094
|
+
// that committed AND pushed still left prompts.git_commit_sha NULL and the UI
|
|
2095
|
+
// had no way to link the change it just shipped.
|
|
2096
|
+
let detectedCommitSha = null;
|
|
2097
|
+
let detectedCommitUrl = null;
|
|
1969
2098
|
logger.info(`CLI Selection: ${cliCommand} (${reason})`);
|
|
1970
2099
|
logger.info(`Spawning: ${cliBinary} -p "${fullContent.slice(0, 50)}..." in ${cwd}${currentBranch ? ` on branch ${currentBranch}` : ''}`);
|
|
1971
2100
|
// Hoist the static block into the CLI system prompt (claude/orquesta support
|
|
@@ -1974,7 +2103,9 @@ ${userRequestBody}`;
|
|
|
1974
2103
|
// can never regress to a worse state than before.
|
|
1975
2104
|
let promptContent = fullContent;
|
|
1976
2105
|
let sysPromptFilePath = null;
|
|
1977
|
-
|
|
2106
|
+
// kimi and cursor-agent have no --append-system-prompt-file, so their static
|
|
2107
|
+
// context stays inline in the prompt.
|
|
2108
|
+
if (cliCommand !== 'kimi' && cliCommand !== 'cursor') {
|
|
1978
2109
|
try {
|
|
1979
2110
|
const promptDir = process.platform === 'win32'
|
|
1980
2111
|
? os.tmpdir()
|
|
@@ -2001,15 +2132,28 @@ ${userRequestBody}`;
|
|
|
2001
2132
|
// Orquesta CLI doesn't need special format flags - it outputs directly
|
|
2002
2133
|
const baseFlags = cliCommand === 'orquesta'
|
|
2003
2134
|
? '--verbose' // Orquesta: simple verbose output
|
|
2004
|
-
: cliCommand === '
|
|
2005
|
-
? '--output-format text' //
|
|
2006
|
-
//
|
|
2007
|
-
//
|
|
2008
|
-
//
|
|
2009
|
-
//
|
|
2010
|
-
:
|
|
2011
|
-
|
|
2012
|
-
|
|
2135
|
+
: cliCommand === 'cursor'
|
|
2136
|
+
? '--output-format text' // Cursor: plain text. Its stream-json events are a
|
|
2137
|
+
// different shape from Claude's, and parseStreamJson
|
|
2138
|
+
// below only understands Claude's and orquesta-cli's,
|
|
2139
|
+
// so asking for it would give us an unparseable
|
|
2140
|
+
// stream instead of an answer.
|
|
2141
|
+
: cliCommand === 'kimi'
|
|
2142
|
+
? '--output-format text' // Kimi: plain text. NOTE: -p (one-shot) CANNOT be
|
|
2143
|
+
// combined with -y ("Cannot combine --prompt with
|
|
2144
|
+
// --yolo"), so dispatched kimi prompts respond as
|
|
2145
|
+
// chat (no autonomous tool use). Interactive kimi
|
|
2146
|
+
// sessions DO get -y (see ptyArgs below).
|
|
2147
|
+
: '--verbose --output-format stream-json'; // Claude: stream-json format
|
|
2148
|
+
// Kimi has no --dangerously-skip-permissions, and -p forbids -y. Cursor spells
|
|
2149
|
+
// the same thing --force (alias --yolo).
|
|
2150
|
+
const permFlags = cliCommand === 'kimi'
|
|
2151
|
+
? ''
|
|
2152
|
+
: mode !== 'auto'
|
|
2153
|
+
? ''
|
|
2154
|
+
: cliCommand === 'cursor'
|
|
2155
|
+
? '--force'
|
|
2156
|
+
: '--dangerously-skip-permissions';
|
|
2013
2157
|
// Per-project endpoint override (orquesta CLI only). Single-quoted for the
|
|
2014
2158
|
// shell-wrapped (Linux) path; pushed as a separate arg on Win/macOS below.
|
|
2015
2159
|
const useEndpoint = cliCommand === 'orquesta' && !!globalCliEndpoint;
|
|
@@ -2130,6 +2274,13 @@ ${userRequestBody}`;
|
|
|
2130
2274
|
else if (action === 'failed') {
|
|
2131
2275
|
output += `\n❌ Failed: ${title}\n`;
|
|
2132
2276
|
}
|
|
2277
|
+
// Persist the transition too. These used to be marker text ONLY, so a
|
|
2278
|
+
// replayed run had zero progress rows and rendered as a different run
|
|
2279
|
+
// than the one the user watched live (no plan, no checklist). `phase`
|
|
2280
|
+
// carries the action so a reader can rebuild the checklist state.
|
|
2281
|
+
if (action && title) {
|
|
2282
|
+
logs.push(createProgressLog(String(title), undefined, action === 'failed' ? 'error' : 'info', promptId, undefined, undefined, String(action)));
|
|
2283
|
+
}
|
|
2133
2284
|
}
|
|
2134
2285
|
else if (json.event === 'tool_call') {
|
|
2135
2286
|
const { tool, args } = json.data;
|
|
@@ -2138,6 +2289,15 @@ ${userRequestBody}`;
|
|
|
2138
2289
|
if (argsStr.length < 500) {
|
|
2139
2290
|
output += `${argsStr}\n`;
|
|
2140
2291
|
}
|
|
2292
|
+
else {
|
|
2293
|
+
// A file edit is the one thing the user actually wants to SEE, and
|
|
2294
|
+
// its args are almost always over the cap (old+new string), so the
|
|
2295
|
+
// live stream showed nothing while the DB row had the whole diff.
|
|
2296
|
+
// Emit a trimmed edit payload instead of dropping it entirely.
|
|
2297
|
+
const compact = compactFileEditArgs(args);
|
|
2298
|
+
if (compact)
|
|
2299
|
+
output += `${compact}\n`;
|
|
2300
|
+
}
|
|
2141
2301
|
logs.push(createToolCallLog(tool, args, 'info', promptId));
|
|
2142
2302
|
currentToolName = tool;
|
|
2143
2303
|
}
|
|
@@ -2448,6 +2608,8 @@ ${userRequestBody}`;
|
|
|
2448
2608
|
catch {
|
|
2449
2609
|
// Ignore errors getting git remote
|
|
2450
2610
|
}
|
|
2611
|
+
detectedCommitSha = commitMatch[1];
|
|
2612
|
+
detectedCommitUrl = commitUrl;
|
|
2451
2613
|
sendSpecialEvent('git_commit', {
|
|
2452
2614
|
commitSha: commitMatch[1],
|
|
2453
2615
|
message: commitMatch[2] || '',
|
|
@@ -2628,15 +2790,25 @@ ${userRequestBody}`;
|
|
|
2628
2790
|
// Record WHICH CLI + model executed it (timeline/history). Model is
|
|
2629
2791
|
// best-effort — the CLI/proxy pick the true upstream model internally
|
|
2630
2792
|
// (batuta-auto fans out per prompt), so we report the CLI's MODEL CONFIG:
|
|
2631
|
-
const cliTypeForDb = cliCommand === 'orquesta'
|
|
2793
|
+
const cliTypeForDb = cliCommand === 'orquesta'
|
|
2794
|
+
? 'orquesta-cli'
|
|
2795
|
+
: cliCommand === 'kimi'
|
|
2796
|
+
? 'kimi-cli'
|
|
2797
|
+
: cliCommand === 'cursor'
|
|
2798
|
+
? 'cursor-cli'
|
|
2799
|
+
: 'claude-cli';
|
|
2632
2800
|
let modelForDb;
|
|
2633
2801
|
if (cliCommand === 'kimi')
|
|
2634
2802
|
modelForDb = 'kimi-for-coding';
|
|
2803
|
+
// cursor-agent picks the model from the user's own Cursor settings and does
|
|
2804
|
+
// not report it in text mode, so leave it unset rather than guess.
|
|
2805
|
+
else if (cliCommand === 'cursor')
|
|
2806
|
+
modelForDb = undefined;
|
|
2635
2807
|
else if (cliCommand === 'orquesta')
|
|
2636
2808
|
modelForDb = (globalCliEndpoint === 'batuta' || !globalCliEndpoint) ? 'batuta-auto' : globalCliEndpoint;
|
|
2637
2809
|
else if (cliCommand === 'claude')
|
|
2638
2810
|
modelForDb = process.env.ANTHROPIC_MODEL || undefined;
|
|
2639
|
-
updatePromptStatus(promptId, status, summary || undefined, Object.keys(usage).length > 0 ? usage : undefined, finalResult, { cliType: cliTypeForDb, model: modelForDb, gitBranch: currentBranch || undefined }).catch(err => {
|
|
2811
|
+
updatePromptStatus(promptId, status, summary || undefined, Object.keys(usage).length > 0 ? usage : undefined, finalResult, { cliType: cliTypeForDb, model: modelForDb, gitBranch: currentBranch || undefined, gitCommitSha: detectedCommitSha || undefined, gitCommitUrl: detectedCommitUrl || undefined }).catch(err => {
|
|
2640
2812
|
logger.error(`Failed to update prompt status: ${err}`);
|
|
2641
2813
|
});
|
|
2642
2814
|
// Auto-publish any documents/assets the run generated so the operator
|
|
@@ -3490,20 +3662,30 @@ export async function startSession(options) {
|
|
|
3490
3662
|
// inside bwrap (/ is ro-bound). cliCommand stays the TYPE.
|
|
3491
3663
|
const cliBinary = cliCommand === 'kimi'
|
|
3492
3664
|
? (resolveKimiBinary() || 'kimi')
|
|
3493
|
-
: cliCommand === '
|
|
3494
|
-
? (
|
|
3495
|
-
: cliCommand
|
|
3496
|
-
|
|
3665
|
+
: cliCommand === 'cursor'
|
|
3666
|
+
? (resolveCursorBinary() || 'cursor-agent')
|
|
3667
|
+
: cliCommand === 'claude'
|
|
3668
|
+
? (resolveClaudeBinary() || 'claude')
|
|
3669
|
+
: cliCommand;
|
|
3670
|
+
const resolvedByPath = cliCommand === 'kimi' || cliCommand === 'cursor';
|
|
3671
|
+
logger.info(`Interactive session CLI: ${cliCommand}${resolvedByPath ? ` (${cliBinary})` : ''}`);
|
|
3497
3672
|
// Best-effort model + cli label reported in session:started so the dashboard
|
|
3498
3673
|
// can tag the interactive prompt (same mapping dispatched prompts use on
|
|
3499
3674
|
// completion). claude's exact model isn't knowable from a raw PTY, so it falls
|
|
3500
3675
|
// back to ANTHROPIC_MODEL when set, else undefined.
|
|
3501
3676
|
const sessionModel = cliCommand === 'kimi'
|
|
3502
3677
|
? 'kimi-for-coding'
|
|
3503
|
-
: cliCommand === '
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3678
|
+
: cliCommand === 'cursor'
|
|
3679
|
+
// cursor-agent uses whatever model the user selected in their own Cursor
|
|
3680
|
+
// account; it isn't knowable from the PTY, so report nothing rather than lie.
|
|
3681
|
+
? undefined
|
|
3682
|
+
: cliCommand === 'orquesta'
|
|
3683
|
+
? ((globalCliEndpoint === 'batuta' || !globalCliEndpoint) ? 'batuta-auto' : globalCliEndpoint)
|
|
3684
|
+
: (process.env.ANTHROPIC_MODEL || undefined);
|
|
3685
|
+
const sessionCliType = cliCommand === 'orquesta' ? 'orquesta'
|
|
3686
|
+
: cliCommand === 'kimi' ? 'kimi'
|
|
3687
|
+
: cliCommand === 'cursor' ? 'cursor'
|
|
3688
|
+
: 'claude';
|
|
3507
3689
|
// Filter undefined env values — node-pty requires Record<string, string>.
|
|
3508
3690
|
// In strict sandbox mode, sandboxEnv() first reduces to the allowlist so the
|
|
3509
3691
|
// interactive CLI can't read unrelated host secrets from its own env.
|
|
@@ -3583,7 +3765,7 @@ export async function startSession(options) {
|
|
|
3583
3765
|
// `ps aux`. Default-off because a file unreadable inside bwrap would silently
|
|
3584
3766
|
// strip the system prompt (degraded session, not a crash) — flip the default
|
|
3585
3767
|
// only after E2E confirms the sandbox reads it. Rollback = unset the var.
|
|
3586
|
-
const wantSysPromptFile = cliCommand !== 'kimi' && ((process.platform === 'win32' && cliCommand === 'orquesta') ||
|
|
3768
|
+
const wantSysPromptFile = cliCommand !== 'kimi' && cliCommand !== 'cursor' && ((process.platform === 'win32' && cliCommand === 'orquesta') ||
|
|
3587
3769
|
/^(1|true|yes|on)$/i.test(process.env.ORQUESTA_SYSPROMPT_FILE || ''));
|
|
3588
3770
|
if (wantSysPromptFile) {
|
|
3589
3771
|
try {
|
|
@@ -3602,7 +3784,13 @@ export async function startSession(options) {
|
|
|
3602
3784
|
}
|
|
3603
3785
|
}
|
|
3604
3786
|
let ptyArgs;
|
|
3605
|
-
if (cliCommand === '
|
|
3787
|
+
if (cliCommand === 'cursor') {
|
|
3788
|
+
// cursor-agent has no --append-system-prompt; it reads AGENTS.md / CLAUDE.md
|
|
3789
|
+
// from the working dir (which the agent already syncs), so the capabilities
|
|
3790
|
+
// block isn't injected. `--force` is its --dangerously-skip-permissions.
|
|
3791
|
+
ptyArgs = ['--force'];
|
|
3792
|
+
}
|
|
3793
|
+
else if (cliCommand === 'kimi') {
|
|
3606
3794
|
// Kimi has neither --dangerously-skip-permissions nor --append-system-prompt.
|
|
3607
3795
|
// `-y` auto-approves the interactive session; it reads project conventions from
|
|
3608
3796
|
// CLAUDE.md / AGENTS.md in the working dir (which the agent already syncs), so
|
|
@@ -3620,10 +3808,11 @@ export async function startSession(options) {
|
|
|
3620
3808
|
// resume flag, so the option is silently ignored there. Prepended so it's an
|
|
3621
3809
|
// unambiguous standalone flag, separate from --append-system-prompt's value.
|
|
3622
3810
|
if ((resume || resumeSessionId) && cliCommand !== 'kimi') {
|
|
3811
|
+
// cursor-agent spells these the same way claude does (--continue / --resume <id>).
|
|
3623
3812
|
// claude can target a SPECIFIC past conversation by id (--resume <id>);
|
|
3624
3813
|
// orquesta-cli only reattaches the latest (--continue). So when a specific
|
|
3625
3814
|
// id is requested and we're on claude, use --resume; otherwise --continue.
|
|
3626
|
-
if (resumeSessionId && cliCommand === 'claude') {
|
|
3815
|
+
if (resumeSessionId && (cliCommand === 'claude' || cliCommand === 'cursor')) {
|
|
3627
3816
|
ptyArgs.unshift('--resume', resumeSessionId);
|
|
3628
3817
|
logger.info(`[Session] Resuming conversation ${resumeSessionId} (--resume) for ${cliCommand}`);
|
|
3629
3818
|
}
|