juno-code 1.0.51 → 1.0.53
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 +57 -0
- package/dist/bin/cli.js +678 -82
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/cli.mjs +675 -79
- package/dist/bin/cli.mjs.map +1 -1
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +369 -58
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +367 -56
- package/dist/index.mjs.map +1 -1
- package/dist/templates/scripts/__pycache__/parallel_runner.cpython-313.pyc +0 -0
- package/dist/templates/scripts/install_requirements.sh +21 -0
- package/dist/templates/scripts/kanban.sh +6 -2
- package/dist/templates/scripts/parallel_runner.sh +602 -131
- package/dist/templates/services/__pycache__/pi.cpython-313.pyc +0 -0
- package/dist/templates/services/__pycache__/pi.cpython-38.pyc +0 -0
- package/dist/templates/services/pi.py +418 -51
- package/dist/templates/skills/claude/ralph-loop/scripts/kanban.sh +6 -2
- package/dist/templates/skills/codex/ralph-loop/scripts/kanban.sh +6 -2
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -739,6 +739,8 @@ interface ExecutionRequest {
|
|
|
739
739
|
readonly thinking?: string;
|
|
740
740
|
/** Run Pi subagent in interactive live mode (forwarded to shell backend --live flag) */
|
|
741
741
|
readonly live?: boolean;
|
|
742
|
+
/** Start Pi live mode without an initial prompt (interactive continue flow) */
|
|
743
|
+
readonly liveInteractiveSession?: boolean;
|
|
742
744
|
}
|
|
743
745
|
/**
|
|
744
746
|
* Execution result interface for completed executions
|
|
@@ -1196,6 +1198,7 @@ declare function createExecutionRequest(options: {
|
|
|
1196
1198
|
continueConversation?: boolean;
|
|
1197
1199
|
thinking?: string;
|
|
1198
1200
|
live?: boolean;
|
|
1201
|
+
liveInteractiveSession?: boolean;
|
|
1199
1202
|
}): ExecutionRequest;
|
|
1200
1203
|
|
|
1201
1204
|
/** Session metadata */
|
package/dist/index.d.ts
CHANGED
|
@@ -739,6 +739,8 @@ interface ExecutionRequest {
|
|
|
739
739
|
readonly thinking?: string;
|
|
740
740
|
/** Run Pi subagent in interactive live mode (forwarded to shell backend --live flag) */
|
|
741
741
|
readonly live?: boolean;
|
|
742
|
+
/** Start Pi live mode without an initial prompt (interactive continue flow) */
|
|
743
|
+
readonly liveInteractiveSession?: boolean;
|
|
742
744
|
}
|
|
743
745
|
/**
|
|
744
746
|
* Execution result interface for completed executions
|
|
@@ -1196,6 +1198,7 @@ declare function createExecutionRequest(options: {
|
|
|
1196
1198
|
continueConversation?: boolean;
|
|
1197
1199
|
thinking?: string;
|
|
1198
1200
|
live?: boolean;
|
|
1201
|
+
liveInteractiveSession?: boolean;
|
|
1199
1202
|
}): ExecutionRequest;
|
|
1200
1203
|
|
|
1201
1204
|
/** Session metadata */
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var chalk = require('chalk');
|
|
5
|
-
var
|
|
5
|
+
var childProcess = require('child_process');
|
|
6
6
|
var fs3 = require('fs');
|
|
7
7
|
var path4 = require('path');
|
|
8
8
|
var os2 = require('os');
|
|
@@ -11,6 +11,7 @@ var zod = require('zod');
|
|
|
11
11
|
var yaml = require('js-yaml');
|
|
12
12
|
var events = require('events');
|
|
13
13
|
var execa = require('execa');
|
|
14
|
+
var util = require('util');
|
|
14
15
|
var uuid = require('uuid');
|
|
15
16
|
var process2 = require('process');
|
|
16
17
|
|
|
@@ -35,6 +36,7 @@ function _interopNamespace(e) {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
var chalk__default = /*#__PURE__*/_interopDefault(chalk);
|
|
39
|
+
var childProcess__namespace = /*#__PURE__*/_interopNamespace(childProcess);
|
|
38
40
|
var fs3__namespace = /*#__PURE__*/_interopNamespace(fs3);
|
|
39
41
|
var path4__namespace = /*#__PURE__*/_interopNamespace(path4);
|
|
40
42
|
var os2__namespace = /*#__PURE__*/_interopNamespace(os2);
|
|
@@ -62,7 +64,7 @@ var __export = (target, all) => {
|
|
|
62
64
|
exports.version = void 0;
|
|
63
65
|
var init_version = __esm({
|
|
64
66
|
"src/version.ts"() {
|
|
65
|
-
exports.version = "1.0.
|
|
67
|
+
exports.version = "1.0.53";
|
|
66
68
|
}
|
|
67
69
|
});
|
|
68
70
|
|
|
@@ -750,25 +752,28 @@ var init_shell_backend = __esm({
|
|
|
750
752
|
const scriptPath = await this.findScriptForSubagent(subagentType);
|
|
751
753
|
const result = await this.executeScript(scriptPath, request, toolId, subagentType);
|
|
752
754
|
const duration = Date.now() - startTime;
|
|
755
|
+
const structuredResult = this.buildStructuredOutput(subagentType, result);
|
|
756
|
+
const structuredPayload = this.parseStructuredResultPayload(structuredResult.content);
|
|
757
|
+
const structuredIndicatesError = structuredPayload?.is_error === true || structuredPayload?.subtype === "error";
|
|
758
|
+
const executionSucceeded = result.success && !structuredIndicatesError;
|
|
753
759
|
await this.emitProgressEvent({
|
|
754
760
|
sessionId: request.metadata?.sessionId || "unknown",
|
|
755
761
|
timestamp: /* @__PURE__ */ new Date(),
|
|
756
762
|
backend: "shell",
|
|
757
763
|
count: ++this.eventCounter,
|
|
758
764
|
type: "tool_result" /* TOOL_RESULT */,
|
|
759
|
-
content: `${request.toolName} completed successfully (${duration}ms)`,
|
|
765
|
+
content: executionSucceeded ? `${request.toolName} completed successfully (${duration}ms)` : `${request.toolName} completed with error (${duration}ms)`,
|
|
760
766
|
toolId,
|
|
761
767
|
metadata: {
|
|
762
768
|
toolName: request.toolName,
|
|
763
769
|
duration,
|
|
764
|
-
success:
|
|
770
|
+
success: executionSucceeded,
|
|
765
771
|
phase: "completion"
|
|
766
772
|
}
|
|
767
773
|
});
|
|
768
|
-
const structuredResult = this.buildStructuredOutput(subagentType, result);
|
|
769
774
|
const toolResult = {
|
|
770
775
|
content: structuredResult.content,
|
|
771
|
-
status:
|
|
776
|
+
status: executionSucceeded ? "completed" /* COMPLETED */ : "failed" /* FAILED */,
|
|
772
777
|
startTime: new Date(startTime),
|
|
773
778
|
endTime: /* @__PURE__ */ new Date(),
|
|
774
779
|
duration,
|
|
@@ -777,6 +782,9 @@ var init_shell_backend = __esm({
|
|
|
777
782
|
};
|
|
778
783
|
if (result.error) {
|
|
779
784
|
toolResult.error = new Error(result.error);
|
|
785
|
+
} else if (!executionSucceeded) {
|
|
786
|
+
const structuredErrorMessage = typeof structuredPayload?.error === "string" && structuredPayload.error || typeof structuredPayload?.result === "string" && structuredPayload.result || `${request.toolName} reported a structured error`;
|
|
787
|
+
toolResult.error = new Error(structuredErrorMessage);
|
|
780
788
|
}
|
|
781
789
|
if (structuredResult.metadata) {
|
|
782
790
|
toolResult.metadata = structuredResult.metadata;
|
|
@@ -1028,6 +1036,9 @@ var init_shell_backend = __esm({
|
|
|
1028
1036
|
if (isPython && subagentType === "pi" && request.arguments?.live === true) {
|
|
1029
1037
|
args.push("--live");
|
|
1030
1038
|
}
|
|
1039
|
+
if (isPython && subagentType === "pi" && request.arguments?.liveInteractiveSession === true) {
|
|
1040
|
+
args.push("--live-manual");
|
|
1041
|
+
}
|
|
1031
1042
|
if (isPython && this.config.debug) {
|
|
1032
1043
|
args.push("--verbose");
|
|
1033
1044
|
}
|
|
@@ -1052,7 +1063,7 @@ var init_shell_backend = __esm({
|
|
|
1052
1063
|
`Pi live mode stdio: ${shouldAttachLiveTerminal ? "inherit (interactive TTY or stdout-tty fallback)" : "pipe (headless/non-TTY)"}`
|
|
1053
1064
|
);
|
|
1054
1065
|
}
|
|
1055
|
-
const child =
|
|
1066
|
+
const child = childProcess.spawn(command, args, {
|
|
1056
1067
|
env: env2,
|
|
1057
1068
|
cwd: this.config.workingDirectory,
|
|
1058
1069
|
stdio: shouldAttachLiveTerminal ? "inherit" : ["pipe", "pipe", "pipe"]
|
|
@@ -1310,8 +1321,19 @@ var init_shell_backend = __esm({
|
|
|
1310
1321
|
if (subagentType === "pi") {
|
|
1311
1322
|
const piEvent = result.subAgentResponse ?? this.extractLastJsonEvent(result.output);
|
|
1312
1323
|
if (piEvent) {
|
|
1313
|
-
|
|
1314
|
-
|
|
1324
|
+
const piNestedEvent = typeof piEvent.sub_agent_response === "object" && piEvent.sub_agent_response ? piEvent.sub_agent_response : void 0;
|
|
1325
|
+
const piSessionId = typeof piEvent.session_id === "string" && piEvent.session_id ? piEvent.session_id : typeof piEvent.sessionId === "string" && piEvent.sessionId ? piEvent.sessionId : typeof piEvent.id === "string" && piEvent.type === "session" ? piEvent.id : typeof piNestedEvent?.session_id === "string" && piNestedEvent.session_id ? piNestedEvent.session_id : typeof piNestedEvent?.sessionId === "string" && piNestedEvent.sessionId ? piNestedEvent.sessionId : typeof piNestedEvent?.id === "string" && piNestedEvent.type === "session" ? piNestedEvent.id : typeof piEvent.sub_agent_response?.session_id === "string" && piEvent.sub_agent_response.session_id ? piEvent.sub_agent_response.session_id : void 0;
|
|
1326
|
+
const sanitizedPiEvent = { ...piEvent };
|
|
1327
|
+
delete sanitizedPiEvent.messages;
|
|
1328
|
+
if (sanitizedPiEvent.sub_agent_response && typeof sanitizedPiEvent.sub_agent_response === "object") {
|
|
1329
|
+
const inner = { ...sanitizedPiEvent.sub_agent_response };
|
|
1330
|
+
delete inner.messages;
|
|
1331
|
+
delete inner.type;
|
|
1332
|
+
sanitizedPiEvent.sub_agent_response = inner;
|
|
1333
|
+
}
|
|
1334
|
+
const hasDirectResultText = typeof piEvent.result === "string";
|
|
1335
|
+
let resultText = hasDirectResultText ? piEvent.result : void 0;
|
|
1336
|
+
if (resultText === void 0 && Array.isArray(piEvent.messages)) {
|
|
1315
1337
|
for (let i = piEvent.messages.length - 1; i >= 0; i--) {
|
|
1316
1338
|
const msg = piEvent.messages[i];
|
|
1317
1339
|
if (msg?.role === "assistant") {
|
|
@@ -1331,16 +1353,11 @@ var init_shell_backend = __esm({
|
|
|
1331
1353
|
}
|
|
1332
1354
|
}
|
|
1333
1355
|
}
|
|
1334
|
-
if (resultText) {
|
|
1356
|
+
if (resultText === void 0 && typeof piEvent.error === "string") {
|
|
1357
|
+
resultText = piEvent.error;
|
|
1358
|
+
}
|
|
1359
|
+
if (resultText !== void 0) {
|
|
1335
1360
|
const isError = piEvent.is_error ?? !result.success;
|
|
1336
|
-
const sanitizedPiEvent = { ...piEvent };
|
|
1337
|
-
delete sanitizedPiEvent.messages;
|
|
1338
|
-
if (sanitizedPiEvent.sub_agent_response && typeof sanitizedPiEvent.sub_agent_response === "object") {
|
|
1339
|
-
const inner = { ...sanitizedPiEvent.sub_agent_response };
|
|
1340
|
-
delete inner.messages;
|
|
1341
|
-
delete inner.type;
|
|
1342
|
-
sanitizedPiEvent.sub_agent_response = inner;
|
|
1343
|
-
}
|
|
1344
1361
|
const usage = piEvent.usage;
|
|
1345
1362
|
const totalCostUsd = typeof piEvent.total_cost_usd === "number" ? piEvent.total_cost_usd : typeof usage?.cost?.total === "number" ? usage.cost.total : void 0;
|
|
1346
1363
|
const structuredPayload = {
|
|
@@ -1348,9 +1365,9 @@ var init_shell_backend = __esm({
|
|
|
1348
1365
|
subtype: piEvent.subtype || (isError ? "error" : "success"),
|
|
1349
1366
|
is_error: isError,
|
|
1350
1367
|
result: resultText,
|
|
1351
|
-
error: piEvent.error,
|
|
1368
|
+
error: isError ? piEvent.error ?? result.error ?? resultText : piEvent.error,
|
|
1352
1369
|
stderr: result.error,
|
|
1353
|
-
session_id:
|
|
1370
|
+
session_id: piSessionId,
|
|
1354
1371
|
exit_code: result.exitCode,
|
|
1355
1372
|
duration_ms: piEvent.duration_ms ?? result.duration,
|
|
1356
1373
|
total_cost_usd: totalCostUsd,
|
|
@@ -1368,6 +1385,57 @@ var init_shell_backend = __esm({
|
|
|
1368
1385
|
metadata
|
|
1369
1386
|
};
|
|
1370
1387
|
}
|
|
1388
|
+
const isSessionSnapshotOnly = piEvent.type === "session" || piEvent.subtype === "session";
|
|
1389
|
+
if (isSessionSnapshotOnly) {
|
|
1390
|
+
const errorMessage = result.error?.trim() || "Pi exited before emitting a terminal result event (session snapshot only).";
|
|
1391
|
+
const structuredPayload = {
|
|
1392
|
+
type: "result",
|
|
1393
|
+
subtype: "error",
|
|
1394
|
+
is_error: true,
|
|
1395
|
+
result: errorMessage,
|
|
1396
|
+
error: errorMessage,
|
|
1397
|
+
stderr: result.error,
|
|
1398
|
+
session_id: piSessionId,
|
|
1399
|
+
exit_code: result.exitCode,
|
|
1400
|
+
duration_ms: result.duration,
|
|
1401
|
+
sub_agent_response: sanitizedPiEvent
|
|
1402
|
+
};
|
|
1403
|
+
const metadata = {
|
|
1404
|
+
...piEvent ? { subAgentResponse: piEvent } : void 0,
|
|
1405
|
+
structuredOutput: true,
|
|
1406
|
+
contentType: "application/json",
|
|
1407
|
+
rawOutput: result.output
|
|
1408
|
+
};
|
|
1409
|
+
return {
|
|
1410
|
+
content: JSON.stringify(structuredPayload),
|
|
1411
|
+
metadata
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
if (!result.success) {
|
|
1415
|
+
const errorMessage = result.error?.trim() || result.output?.trim() || "Unknown error";
|
|
1416
|
+
const structuredPayload = {
|
|
1417
|
+
type: "result",
|
|
1418
|
+
subtype: "error",
|
|
1419
|
+
is_error: true,
|
|
1420
|
+
result: errorMessage,
|
|
1421
|
+
error: errorMessage,
|
|
1422
|
+
stderr: result.error,
|
|
1423
|
+
session_id: piSessionId,
|
|
1424
|
+
exit_code: result.exitCode,
|
|
1425
|
+
duration_ms: result.duration,
|
|
1426
|
+
sub_agent_response: sanitizedPiEvent
|
|
1427
|
+
};
|
|
1428
|
+
const metadata = {
|
|
1429
|
+
...piEvent ? { subAgentResponse: piEvent } : void 0,
|
|
1430
|
+
structuredOutput: true,
|
|
1431
|
+
contentType: "application/json",
|
|
1432
|
+
rawOutput: result.output
|
|
1433
|
+
};
|
|
1434
|
+
return {
|
|
1435
|
+
content: JSON.stringify(structuredPayload),
|
|
1436
|
+
metadata
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1371
1439
|
}
|
|
1372
1440
|
}
|
|
1373
1441
|
if (!result.success) {
|
|
@@ -1432,6 +1500,20 @@ var init_shell_backend = __esm({
|
|
|
1432
1500
|
}
|
|
1433
1501
|
return null;
|
|
1434
1502
|
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Parse JSON structured output payload emitted by shell service wrappers.
|
|
1505
|
+
*/
|
|
1506
|
+
parseStructuredResultPayload(content) {
|
|
1507
|
+
try {
|
|
1508
|
+
const parsed = JSON.parse(content);
|
|
1509
|
+
if (!parsed || typeof parsed !== "object") {
|
|
1510
|
+
return null;
|
|
1511
|
+
}
|
|
1512
|
+
return parsed;
|
|
1513
|
+
} catch {
|
|
1514
|
+
return null;
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1435
1517
|
/**
|
|
1436
1518
|
* Extract the last valid JSON object from a script's stdout to use as a structured payload fallback.
|
|
1437
1519
|
*/
|
|
@@ -1754,6 +1836,31 @@ var SUBAGENT_DEFAULT_MODELS = {
|
|
|
1754
1836
|
cursor: "auto",
|
|
1755
1837
|
pi: ":pi"
|
|
1756
1838
|
};
|
|
1839
|
+
function isModelCompatibleWithSubagent(model, subagent) {
|
|
1840
|
+
if (!model.startsWith(":")) {
|
|
1841
|
+
return true;
|
|
1842
|
+
}
|
|
1843
|
+
const claudeShorthands = [":sonnet", ":haiku", ":opus"];
|
|
1844
|
+
const codexShorthands = [":codex", ":codex-mini", ":gpt-5", ":mini"];
|
|
1845
|
+
const geminiShorthands = [":pro", ":flash"];
|
|
1846
|
+
const isClaudeModel = claudeShorthands.includes(model) || model.startsWith(":claude");
|
|
1847
|
+
const isCodexModel = codexShorthands.includes(model) || model.startsWith(":gpt");
|
|
1848
|
+
const isGeminiModel = geminiShorthands.includes(model) || model.startsWith(":gemini");
|
|
1849
|
+
switch (subagent) {
|
|
1850
|
+
case "claude":
|
|
1851
|
+
return isClaudeModel || !isCodexModel && !isGeminiModel;
|
|
1852
|
+
case "codex":
|
|
1853
|
+
return isCodexModel || !isClaudeModel && !isGeminiModel;
|
|
1854
|
+
case "gemini":
|
|
1855
|
+
return isGeminiModel || !isClaudeModel && !isCodexModel;
|
|
1856
|
+
case "cursor":
|
|
1857
|
+
return true;
|
|
1858
|
+
case "pi":
|
|
1859
|
+
return true;
|
|
1860
|
+
default:
|
|
1861
|
+
return true;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1757
1864
|
|
|
1758
1865
|
// src/core/config.ts
|
|
1759
1866
|
var ENV_VAR_MAPPING = {
|
|
@@ -2277,6 +2384,13 @@ async function ensureHooksConfig(baseDir) {
|
|
|
2277
2384
|
existingConfig.defaultModels = baseDefaults;
|
|
2278
2385
|
needsUpdate = true;
|
|
2279
2386
|
}
|
|
2387
|
+
const selectedSubagentRaw = typeof existingConfig.defaultSubagent === "string" ? existingConfig.defaultSubagent : "claude";
|
|
2388
|
+
const selectedSubagent = selectedSubagentRaw in SUBAGENT_DEFAULT_MODELS ? selectedSubagentRaw : "claude";
|
|
2389
|
+
const selectedMapModel = existingConfig.defaultModels && typeof existingConfig.defaultModels === "object" ? existingConfig.defaultModels[selectedSubagent] : void 0;
|
|
2390
|
+
if (typeof selectedMapModel === "string" && isModelCompatibleWithSubagent(selectedMapModel, selectedSubagent) && existingConfig.defaultModel !== selectedMapModel) {
|
|
2391
|
+
existingConfig.defaultModel = selectedMapModel;
|
|
2392
|
+
needsUpdate = true;
|
|
2393
|
+
}
|
|
2280
2394
|
if (existingConfig.defaultMaxIterations === 50) {
|
|
2281
2395
|
existingConfig.defaultMaxIterations = DEFAULT_CONFIG.defaultMaxIterations;
|
|
2282
2396
|
needsUpdate = true;
|
|
@@ -2581,6 +2695,178 @@ function validateHooksConfig(hooks) {
|
|
|
2581
2695
|
// src/core/engine.ts
|
|
2582
2696
|
init_advanced_logger();
|
|
2583
2697
|
init_shell_backend();
|
|
2698
|
+
|
|
2699
|
+
// src/core/prompt-command-substitution.ts
|
|
2700
|
+
init_version();
|
|
2701
|
+
var SINGLE_QUOTED_MARKER = "!'";
|
|
2702
|
+
var TRIPLE_BACKTICK_MARKER = "!```";
|
|
2703
|
+
var TRIPLE_BACKTICK_CLOSER = "```";
|
|
2704
|
+
var DEFAULT_MAX_BUFFER_BYTES = 1024 * 1024;
|
|
2705
|
+
var DEFAULT_COMMAND_TIMEOUT_MS = 3e4;
|
|
2706
|
+
var COMMAND_TIMEOUT_ENV_KEY = "JUNO_CODE_PROMPT_SUBSTITUTION_TIMEOUT_MS";
|
|
2707
|
+
function findPromptCommandSubstitutions(prompt) {
|
|
2708
|
+
const matches = [];
|
|
2709
|
+
let cursor = 0;
|
|
2710
|
+
while (cursor < prompt.length) {
|
|
2711
|
+
const singleQuotedStart = prompt.indexOf(SINGLE_QUOTED_MARKER, cursor);
|
|
2712
|
+
const tripleBacktickStart = prompt.indexOf(TRIPLE_BACKTICK_MARKER, cursor);
|
|
2713
|
+
const markerStart = chooseNearestMarker(singleQuotedStart, tripleBacktickStart);
|
|
2714
|
+
if (markerStart === null) {
|
|
2715
|
+
break;
|
|
2716
|
+
}
|
|
2717
|
+
if (markerStart === singleQuotedStart) {
|
|
2718
|
+
const parsedSingleQuoted = parseSingleQuotedSubstitution(prompt, markerStart);
|
|
2719
|
+
if (!parsedSingleQuoted) {
|
|
2720
|
+
cursor = markerStart + SINGLE_QUOTED_MARKER.length;
|
|
2721
|
+
continue;
|
|
2722
|
+
}
|
|
2723
|
+
matches.push(parsedSingleQuoted);
|
|
2724
|
+
cursor = parsedSingleQuoted.endIndex;
|
|
2725
|
+
continue;
|
|
2726
|
+
}
|
|
2727
|
+
const parsedTripleBacktick = parseTripleBacktickSubstitution(prompt, markerStart);
|
|
2728
|
+
if (!parsedTripleBacktick) {
|
|
2729
|
+
cursor = markerStart + TRIPLE_BACKTICK_MARKER.length;
|
|
2730
|
+
continue;
|
|
2731
|
+
}
|
|
2732
|
+
matches.push(parsedTripleBacktick);
|
|
2733
|
+
cursor = parsedTripleBacktick.endIndex;
|
|
2734
|
+
}
|
|
2735
|
+
return matches;
|
|
2736
|
+
}
|
|
2737
|
+
async function resolvePromptCommandSubstitutions(prompt, options) {
|
|
2738
|
+
const matches = findPromptCommandSubstitutions(prompt);
|
|
2739
|
+
if (matches.length === 0) {
|
|
2740
|
+
return prompt;
|
|
2741
|
+
}
|
|
2742
|
+
const executor = options.executor ?? createDefaultPromptCommandExecutor(options);
|
|
2743
|
+
let result = "";
|
|
2744
|
+
let cursor = 0;
|
|
2745
|
+
for (const match of matches) {
|
|
2746
|
+
result += prompt.slice(cursor, match.startIndex);
|
|
2747
|
+
const commandOutput = await executor(match.command);
|
|
2748
|
+
result += normalizeCommandOutput(commandOutput);
|
|
2749
|
+
cursor = match.endIndex;
|
|
2750
|
+
}
|
|
2751
|
+
result += prompt.slice(cursor);
|
|
2752
|
+
return result;
|
|
2753
|
+
}
|
|
2754
|
+
function chooseNearestMarker(singleQuotedStart, tripleBacktickStart) {
|
|
2755
|
+
const singleExists = singleQuotedStart >= 0;
|
|
2756
|
+
const tripleExists = tripleBacktickStart >= 0;
|
|
2757
|
+
if (!singleExists && !tripleExists) {
|
|
2758
|
+
return null;
|
|
2759
|
+
}
|
|
2760
|
+
if (!singleExists) {
|
|
2761
|
+
return tripleBacktickStart;
|
|
2762
|
+
}
|
|
2763
|
+
if (!tripleExists) {
|
|
2764
|
+
return singleQuotedStart;
|
|
2765
|
+
}
|
|
2766
|
+
return Math.min(singleQuotedStart, tripleBacktickStart);
|
|
2767
|
+
}
|
|
2768
|
+
function parseSingleQuotedSubstitution(prompt, markerStart) {
|
|
2769
|
+
const contentStart = markerStart + SINGLE_QUOTED_MARKER.length;
|
|
2770
|
+
const closingQuote = findClosingSingleQuote(prompt, contentStart);
|
|
2771
|
+
if (closingQuote < 0) {
|
|
2772
|
+
return null;
|
|
2773
|
+
}
|
|
2774
|
+
const raw = prompt.slice(markerStart, closingQuote + 1);
|
|
2775
|
+
const command = prompt.slice(contentStart, closingQuote);
|
|
2776
|
+
return {
|
|
2777
|
+
syntax: "single-quoted",
|
|
2778
|
+
startIndex: markerStart,
|
|
2779
|
+
endIndex: closingQuote + 1,
|
|
2780
|
+
command,
|
|
2781
|
+
raw
|
|
2782
|
+
};
|
|
2783
|
+
}
|
|
2784
|
+
function findClosingSingleQuote(prompt, startIndex) {
|
|
2785
|
+
let escaped = false;
|
|
2786
|
+
for (let index = startIndex; index < prompt.length; index++) {
|
|
2787
|
+
const char = prompt[index];
|
|
2788
|
+
if (char === "'" && !escaped) {
|
|
2789
|
+
return index;
|
|
2790
|
+
}
|
|
2791
|
+
if (char === "\\" && !escaped) {
|
|
2792
|
+
escaped = true;
|
|
2793
|
+
continue;
|
|
2794
|
+
}
|
|
2795
|
+
escaped = false;
|
|
2796
|
+
}
|
|
2797
|
+
return -1;
|
|
2798
|
+
}
|
|
2799
|
+
function parseTripleBacktickSubstitution(prompt, markerStart) {
|
|
2800
|
+
const contentStart = markerStart + TRIPLE_BACKTICK_MARKER.length;
|
|
2801
|
+
const closingBackticks = prompt.indexOf(TRIPLE_BACKTICK_CLOSER, contentStart);
|
|
2802
|
+
if (closingBackticks < 0) {
|
|
2803
|
+
return null;
|
|
2804
|
+
}
|
|
2805
|
+
const raw = prompt.slice(markerStart, closingBackticks + TRIPLE_BACKTICK_CLOSER.length);
|
|
2806
|
+
const command = prompt.slice(contentStart, closingBackticks);
|
|
2807
|
+
return {
|
|
2808
|
+
syntax: "triple-backtick",
|
|
2809
|
+
startIndex: markerStart,
|
|
2810
|
+
endIndex: closingBackticks + TRIPLE_BACKTICK_CLOSER.length,
|
|
2811
|
+
command,
|
|
2812
|
+
raw
|
|
2813
|
+
};
|
|
2814
|
+
}
|
|
2815
|
+
function createDefaultPromptCommandExecutor(options) {
|
|
2816
|
+
const execFile2 = util.promisify(childProcess__namespace.execFile);
|
|
2817
|
+
const maxBufferBytes = options.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES;
|
|
2818
|
+
const commandTimeoutMs = resolvePromptCommandTimeoutMs(options.commandTimeoutMs);
|
|
2819
|
+
const shell = process.env.SHELL || "/bin/bash";
|
|
2820
|
+
return async (command) => {
|
|
2821
|
+
const normalizedCommand = command.trim();
|
|
2822
|
+
if (!normalizedCommand) {
|
|
2823
|
+
return "";
|
|
2824
|
+
}
|
|
2825
|
+
const commandForExecution = wrapCommandForNonInteractiveExecution(normalizedCommand);
|
|
2826
|
+
try {
|
|
2827
|
+
const result = await execFile2(shell, ["-lc", commandForExecution], {
|
|
2828
|
+
cwd: options.workingDirectory,
|
|
2829
|
+
env: options.environment ?? process.env,
|
|
2830
|
+
maxBuffer: maxBufferBytes,
|
|
2831
|
+
timeout: commandTimeoutMs
|
|
2832
|
+
});
|
|
2833
|
+
const stdout2 = typeof result === "string" || Buffer.isBuffer(result) ? String(result) : String(result.stdout ?? "");
|
|
2834
|
+
return stdout2;
|
|
2835
|
+
} catch (error) {
|
|
2836
|
+
const failedCommand = normalizedCommand.replace(/\s+/g, " ").trim();
|
|
2837
|
+
const details = error && typeof error === "object" && "stderr" in error ? String(error.stderr ?? "").trim() : "";
|
|
2838
|
+
const timeoutDetected = error && typeof error === "object" && (("code" in error ? String(error.code ?? "").toUpperCase() === "ETIMEDOUT" : false) || "killed" in error && Boolean(error.killed) && String(error.signal ?? "").toUpperCase() === "SIGTERM" || "message" in error && /timed?\s*out/i.test(String(error.message ?? "")));
|
|
2839
|
+
if (timeoutDetected) {
|
|
2840
|
+
throw new Error(
|
|
2841
|
+
`Prompt command substitution timed out after ${commandTimeoutMs}ms for \`${failedCommand}\``
|
|
2842
|
+
);
|
|
2843
|
+
}
|
|
2844
|
+
const suffix = details ? `: ${details}` : "";
|
|
2845
|
+
throw new Error(`Prompt command substitution failed for \`${failedCommand}\`${suffix}`);
|
|
2846
|
+
}
|
|
2847
|
+
};
|
|
2848
|
+
}
|
|
2849
|
+
function resolvePromptCommandTimeoutMs(explicitTimeoutMs) {
|
|
2850
|
+
if (typeof explicitTimeoutMs === "number" && Number.isFinite(explicitTimeoutMs) && explicitTimeoutMs > 0) {
|
|
2851
|
+
return explicitTimeoutMs;
|
|
2852
|
+
}
|
|
2853
|
+
const envValue = process.env[COMMAND_TIMEOUT_ENV_KEY];
|
|
2854
|
+
if (envValue !== void 0) {
|
|
2855
|
+
const parsed = Number(envValue);
|
|
2856
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
2857
|
+
return parsed;
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
return DEFAULT_COMMAND_TIMEOUT_MS;
|
|
2861
|
+
}
|
|
2862
|
+
function wrapCommandForNonInteractiveExecution(command) {
|
|
2863
|
+
return `(${command}) </dev/null`;
|
|
2864
|
+
}
|
|
2865
|
+
function normalizeCommandOutput(output) {
|
|
2866
|
+
return output.replace(/\r?\n$/, "");
|
|
2867
|
+
}
|
|
2868
|
+
|
|
2869
|
+
// src/core/engine.ts
|
|
2584
2870
|
var ExecutionStatus = /* @__PURE__ */ ((ExecutionStatus2) => {
|
|
2585
2871
|
ExecutionStatus2["PENDING"] = "pending";
|
|
2586
2872
|
ExecutionStatus2["RUNNING"] = "running";
|
|
@@ -2887,7 +3173,8 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
2887
3173
|
if (!request.requestId?.trim()) {
|
|
2888
3174
|
throw new Error("Request ID is required");
|
|
2889
3175
|
}
|
|
2890
|
-
|
|
3176
|
+
const allowEmptyInstructionForPiLiveInteractiveSession = request.subagent === "pi" && request.live === true && request.liveInteractiveSession === true && typeof request.resume === "string" && request.resume.trim().length > 0;
|
|
3177
|
+
if (!request.instruction?.trim() && !allowEmptyInstructionForPiLiveInteractiveSession) {
|
|
2891
3178
|
throw new Error("Instruction is required");
|
|
2892
3179
|
}
|
|
2893
3180
|
if (!request.subagent?.trim()) {
|
|
@@ -3125,44 +3412,62 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
3125
3412
|
engineLogger.warn("Hook START_ITERATION failed", { error, iterationNumber });
|
|
3126
3413
|
}
|
|
3127
3414
|
this.emit("iteration:start", { context, iterationNumber });
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3415
|
+
let toolRequest = null;
|
|
3416
|
+
try {
|
|
3417
|
+
const instructionTemplate = context.request.instruction;
|
|
3418
|
+
const resolvedInstruction = await resolvePromptCommandSubstitutions(instructionTemplate, {
|
|
3419
|
+
workingDirectory: context.request.workingDirectory,
|
|
3420
|
+
environment: {
|
|
3421
|
+
...process.env,
|
|
3422
|
+
JUNO_TASK_ROOT: process.env.JUNO_TASK_ROOT || context.request.workingDirectory
|
|
3423
|
+
}
|
|
3424
|
+
});
|
|
3425
|
+
this.emit("iteration:instruction-resolved", {
|
|
3426
|
+
context,
|
|
3427
|
+
iterationNumber,
|
|
3428
|
+
instruction: resolvedInstruction,
|
|
3429
|
+
templateInstruction: instructionTemplate
|
|
3430
|
+
});
|
|
3431
|
+
toolRequest = {
|
|
3432
|
+
toolName: this.getToolNameForSubagent(context.request.subagent),
|
|
3433
|
+
arguments: {
|
|
3434
|
+
instruction: resolvedInstruction,
|
|
3435
|
+
project_path: context.request.workingDirectory,
|
|
3436
|
+
...context.request.model !== void 0 && { model: context.request.model },
|
|
3437
|
+
...context.request.agents !== void 0 && { agents: context.request.agents },
|
|
3438
|
+
...context.request.tools !== void 0 && { tools: context.request.tools },
|
|
3439
|
+
...context.request.allowedTools !== void 0 && {
|
|
3440
|
+
allowedTools: context.request.allowedTools
|
|
3441
|
+
},
|
|
3442
|
+
...context.request.appendAllowedTools !== void 0 && {
|
|
3443
|
+
appendAllowedTools: context.request.appendAllowedTools
|
|
3444
|
+
},
|
|
3445
|
+
...context.request.disallowedTools !== void 0 && {
|
|
3446
|
+
disallowedTools: context.request.disallowedTools
|
|
3447
|
+
},
|
|
3448
|
+
...context.request.resume !== void 0 && { resume: context.request.resume },
|
|
3449
|
+
...context.request.continueConversation !== void 0 && {
|
|
3450
|
+
continueConversation: context.request.continueConversation
|
|
3451
|
+
},
|
|
3452
|
+
...context.request.thinking !== void 0 && { thinking: context.request.thinking },
|
|
3453
|
+
...context.request.live !== void 0 && { live: context.request.live },
|
|
3454
|
+
...context.request.liveInteractiveSession !== void 0 && {
|
|
3455
|
+
liveInteractiveSession: context.request.liveInteractiveSession
|
|
3456
|
+
},
|
|
3457
|
+
iteration: iterationNumber
|
|
3144
3458
|
},
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3459
|
+
timeout: context.request.timeoutMs || this.engineConfig.config.mcpTimeout,
|
|
3460
|
+
priority: context.request.priority || "normal",
|
|
3461
|
+
metadata: {
|
|
3462
|
+
sessionId: context.sessionContext.sessionId,
|
|
3463
|
+
iterationNumber
|
|
3148
3464
|
},
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
metadata: {
|
|
3156
|
-
sessionId: context.sessionContext.sessionId,
|
|
3157
|
-
iterationNumber
|
|
3158
|
-
},
|
|
3159
|
-
progressCallback: async (event) => {
|
|
3160
|
-
context.progressEvents.push(event);
|
|
3161
|
-
context.statistics.totalProgressEvents++;
|
|
3162
|
-
await this.processProgressEvent(context, event);
|
|
3163
|
-
}
|
|
3164
|
-
};
|
|
3165
|
-
try {
|
|
3465
|
+
progressCallback: async (event) => {
|
|
3466
|
+
context.progressEvents.push(event);
|
|
3467
|
+
context.statistics.totalProgressEvents++;
|
|
3468
|
+
await this.processProgressEvent(context, event);
|
|
3469
|
+
}
|
|
3470
|
+
};
|
|
3166
3471
|
if (!this.currentBackend) {
|
|
3167
3472
|
throw new Error("No backend initialized. Call initializeBackend() first.");
|
|
3168
3473
|
}
|
|
@@ -3231,7 +3536,10 @@ var ExecutionEngine = class extends events.EventEmitter {
|
|
|
3231
3536
|
duration,
|
|
3232
3537
|
error: mcpError,
|
|
3233
3538
|
progressEvents: [],
|
|
3234
|
-
request: toolRequest
|
|
3539
|
+
request: toolRequest ?? {
|
|
3540
|
+
toolName: this.getToolNameForSubagent(context.request.subagent),
|
|
3541
|
+
arguments: {}
|
|
3542
|
+
}
|
|
3235
3543
|
},
|
|
3236
3544
|
progressEvents: [],
|
|
3237
3545
|
error: mcpError
|
|
@@ -3746,6 +4054,9 @@ function createExecutionRequest(options) {
|
|
|
3746
4054
|
if (options.live !== void 0) {
|
|
3747
4055
|
result.live = options.live;
|
|
3748
4056
|
}
|
|
4057
|
+
if (options.liveInteractiveSession !== void 0) {
|
|
4058
|
+
result.liveInteractiveSession = options.liveInteractiveSession;
|
|
4059
|
+
}
|
|
3749
4060
|
return result;
|
|
3750
4061
|
}
|
|
3751
4062
|
|