orchestrating 0.3.3 → 0.4.0

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.
Files changed (2) hide show
  1. package/bin/orch +42 -11
  2. package/package.json +1 -1
package/bin/orch CHANGED
@@ -1057,6 +1057,7 @@ if (adapter) {
1057
1057
  // Spawn claude with bidirectional stdin/stdout (stream-json mode)
1058
1058
  // Follow-ups are sent via stdin — no more kill/respawn.
1059
1059
  let claudeSessionId = null; // Claude's internal session ID (from init event)
1060
+ let pendingRespawn = null; // { prompt } — set when permission grant needs respawn
1060
1061
 
1061
1062
  function spawnClaude(claudeArgs) {
1062
1063
  const proc = spawn(command, claudeArgs, {
@@ -1104,8 +1105,8 @@ if (adapter) {
1104
1105
  if (yoloMode && event.kind === "permission_denied") {
1105
1106
  process.stderr.write(`${GREEN}[yolo] Auto-approving: ${event.toolName}${RESET}\n`);
1106
1107
  approvePermission(event.toolName, "session");
1107
- // Permissions are file-based need respawn for Claude to pick them up
1108
- respawnWithContinue(`Permission for ${event.toolName} was granted. Please retry your last action.`);
1108
+ // Queue respawnClaude will exit after this turn, then we respawn with -c
1109
+ pendingRespawn = { prompt: `Permission for ${event.toolName} was granted. Please retry your last action.` };
1109
1110
  }
1110
1111
  }
1111
1112
  });
@@ -1117,8 +1118,23 @@ if (adapter) {
1117
1118
  proc.on("exit", (code) => {
1118
1119
  childRunning = false;
1119
1120
  const exitCode = code ?? 0;
1120
- sendToServer({ type: "exit", sessionId, exitCode });
1121
- setTimeout(() => process.exit(exitCode), 200);
1121
+
1122
+ if (exitCode !== 0 || exitRequested) {
1123
+ // Non-zero exit or user requested stop — terminate
1124
+ sendToServer({ type: "exit", sessionId, exitCode });
1125
+ setTimeout(() => process.exit(exitCode), 200);
1126
+ } else if (pendingRespawn) {
1127
+ // Permission grant/deny triggered a respawn — do it now
1128
+ const { prompt: respawnPrompt } = pendingRespawn;
1129
+ pendingRespawn = null;
1130
+ respawnWithContinue(respawnPrompt);
1131
+ } else {
1132
+ // Normal exit (turn complete) — stay alive, emit idle, wait for follow-ups
1133
+ sendToServer({
1134
+ type: "agent_event", sessionId,
1135
+ event: { kind: "status", status: "idle" },
1136
+ });
1137
+ }
1122
1138
  });
1123
1139
 
1124
1140
  proc.on("error", (err) => {
@@ -1283,10 +1299,9 @@ if (adapter) {
1283
1299
 
1284
1300
  handleServerMessage = (msg) => {
1285
1301
  if (msg.type === "agent_input" && (msg.text || msg.images)) {
1286
- // Follow-up from dashboard — send via stdin (no respawn!)
1302
+ // Follow-up from dashboard
1287
1303
  if (childRunning) {
1288
1304
  process.stderr.write(`${DIM}[orch] Queuing input — claude is still processing${RESET}\n`);
1289
- // TODO: queue and send after current turn completes
1290
1305
  return;
1291
1306
  }
1292
1307
  let promptText = msg.text || "continue";
@@ -1303,17 +1318,33 @@ if (adapter) {
1303
1318
  const fileList = imagePaths.map((p) => `[Attached image: ${p} — use Read tool to view]`).join("\n");
1304
1319
  promptText = `${fileList}\n\n${promptText}`;
1305
1320
  }
1306
- sendFollowUp(promptText);
1321
+ // If process is still alive, send via stdin; otherwise respawn
1322
+ if (child && child.exitCode === null) {
1323
+ sendFollowUp(promptText);
1324
+ } else {
1325
+ respawnWithContinue(promptText);
1326
+ }
1307
1327
  } else if (msg.type === "agent_permission" && msg.tool && msg.action === "allow") {
1308
1328
  const scope = msg.scope || "session";
1309
1329
  approvePermission(msg.tool, scope);
1310
1330
  process.stderr.write(`${GREEN}Permission granted (${scope}): ${msg.tool}${RESET}\n`);
1311
- // Permissions are file-based in Claude Code need to restart the process
1312
- // so it picks up the updated settings.local.json
1313
- respawnWithContinue(`Permission for ${msg.tool} was granted. Please retry your last action.`);
1331
+ // Permissions are file-based Claude needs restart to pick up settings.local.json
1332
+ const respawnPrompt = `Permission for ${msg.tool} was granted. Please retry your last action.`;
1333
+ if (child && child.exitCode === null) {
1334
+ // Process still running — queue respawn for when it exits
1335
+ pendingRespawn = { prompt: respawnPrompt };
1336
+ } else {
1337
+ // Process already exited — respawn now
1338
+ respawnWithContinue(respawnPrompt);
1339
+ }
1314
1340
  } else if (msg.type === "agent_permission" && msg.tool && msg.action === "deny") {
1315
1341
  process.stderr.write(`${RED}Permission denied: ${msg.tool}${RESET}\n`);
1316
- respawnWithContinue(`Permission for ${msg.tool} was denied by the user. Do not retry this tool — find an alternative approach or skip this step.`);
1342
+ const denyPrompt = `Permission for ${msg.tool} was denied by the user. Do not retry this tool — find an alternative approach or skip this step.`;
1343
+ if (child && child.exitCode === null) {
1344
+ pendingRespawn = { prompt: denyPrompt };
1345
+ } else {
1346
+ respawnWithContinue(denyPrompt);
1347
+ }
1317
1348
  } else if (msg.type === "agent_permission" && msg.tool && msg.action === "revoke") {
1318
1349
  removePermission(msg.tool);
1319
1350
  broadcastPermissions();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orchestrating",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "description": "Stream terminal sessions to the orchestrat.ing dashboard",
5
5
  "type": "module",
6
6
  "bin": {