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/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 child_process = require('child_process');
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.51";
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: result.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: result.success ? "completed" /* COMPLETED */ : "failed" /* FAILED */,
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 = child_process.spawn(command, args, {
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
- let resultText = piEvent.result;
1314
- if (!resultText && Array.isArray(piEvent.messages)) {
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: piEvent.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
- if (!request.instruction?.trim()) {
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
- const toolRequest = {
3129
- toolName: this.getToolNameForSubagent(context.request.subagent),
3130
- arguments: {
3131
- instruction: context.request.instruction,
3132
- project_path: context.request.workingDirectory,
3133
- ...context.request.model !== void 0 && { model: context.request.model },
3134
- ...context.request.agents !== void 0 && { agents: context.request.agents },
3135
- ...context.request.tools !== void 0 && { tools: context.request.tools },
3136
- ...context.request.allowedTools !== void 0 && {
3137
- allowedTools: context.request.allowedTools
3138
- },
3139
- ...context.request.appendAllowedTools !== void 0 && {
3140
- appendAllowedTools: context.request.appendAllowedTools
3141
- },
3142
- ...context.request.disallowedTools !== void 0 && {
3143
- disallowedTools: context.request.disallowedTools
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
- ...context.request.resume !== void 0 && { resume: context.request.resume },
3146
- ...context.request.continueConversation !== void 0 && {
3147
- continueConversation: context.request.continueConversation
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
- ...context.request.thinking !== void 0 && { thinking: context.request.thinking },
3150
- ...context.request.live !== void 0 && { live: context.request.live },
3151
- iteration: iterationNumber
3152
- },
3153
- timeout: context.request.timeoutMs || this.engineConfig.config.mcpTimeout,
3154
- priority: context.request.priority || "normal",
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