orchestrating 0.2.0 → 0.3.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.
- package/bin/orch +53 -55
- package/package.json +1 -1
package/bin/orch
CHANGED
|
@@ -750,14 +750,14 @@ const ADAPTERS = {
|
|
|
750
750
|
buildArgs(prompt, flags) {
|
|
751
751
|
const args = [
|
|
752
752
|
"--output-format", "stream-json",
|
|
753
|
+
"--input-format", "stream-json",
|
|
753
754
|
"--verbose",
|
|
754
755
|
];
|
|
755
756
|
if (flags.continue) {
|
|
756
757
|
args.push("-c");
|
|
757
|
-
if (prompt) args.push("-p", prompt);
|
|
758
|
-
} else {
|
|
759
|
-
args.push("-p", prompt);
|
|
760
758
|
}
|
|
759
|
+
// With --input-format stream-json, the initial prompt is sent via stdin
|
|
760
|
+
// so we don't use -p here. The prompt is sent after spawn.
|
|
761
761
|
return args;
|
|
762
762
|
},
|
|
763
763
|
mode: "structured",
|
|
@@ -1016,10 +1016,13 @@ if (adapter) {
|
|
|
1016
1016
|
return e;
|
|
1017
1017
|
})();
|
|
1018
1018
|
|
|
1019
|
-
// Spawn
|
|
1019
|
+
// Spawn claude with bidirectional stdin/stdout (stream-json mode)
|
|
1020
|
+
// Follow-ups are sent via stdin — no more kill/respawn.
|
|
1021
|
+
let claudeSessionId = null; // Claude's internal session ID (from init event)
|
|
1022
|
+
|
|
1020
1023
|
function spawnClaude(claudeArgs) {
|
|
1021
1024
|
const proc = spawn(command, claudeArgs, {
|
|
1022
|
-
stdio: ["
|
|
1025
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1023
1026
|
cwd: process.cwd(),
|
|
1024
1027
|
env: childEnv,
|
|
1025
1028
|
});
|
|
@@ -1043,18 +1046,28 @@ if (adapter) {
|
|
|
1043
1046
|
return;
|
|
1044
1047
|
}
|
|
1045
1048
|
|
|
1049
|
+
// Capture Claude's session ID for follow-ups
|
|
1050
|
+
if (raw.type === "system" && raw.subtype === "init" && raw.session_id) {
|
|
1051
|
+
claudeSessionId = raw.session_id;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// Detect turn completion — Claude finished responding, ready for next input
|
|
1055
|
+
if (raw.type === "result") {
|
|
1056
|
+
childRunning = false; // "idle" — accepting input
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1046
1059
|
// Normalize and relay each event
|
|
1047
1060
|
const events = normalizeClaudeEvent(raw);
|
|
1048
1061
|
for (const event of events) {
|
|
1049
1062
|
printLocalEvent(event);
|
|
1050
1063
|
sendToServer({ type: "agent_event", sessionId, event });
|
|
1051
1064
|
|
|
1052
|
-
// Yolo mode: auto-approve permission denials
|
|
1053
|
-
// claude will pick it up on respawn via pendingPermissionGrant
|
|
1065
|
+
// Yolo mode: auto-approve permission denials
|
|
1054
1066
|
if (yoloMode && event.kind === "permission_denied") {
|
|
1055
1067
|
process.stderr.write(`${GREEN}[yolo] Auto-approving: ${event.toolName}${RESET}\n`);
|
|
1056
1068
|
approvePermission(event.toolName, "session");
|
|
1057
|
-
|
|
1069
|
+
// Send follow-up via stdin to retry (no respawn needed)
|
|
1070
|
+
sendFollowUp(`Permission for ${event.toolName} was granted. Please retry your last action.`);
|
|
1058
1071
|
}
|
|
1059
1072
|
}
|
|
1060
1073
|
});
|
|
@@ -1066,21 +1079,8 @@ if (adapter) {
|
|
|
1066
1079
|
proc.on("exit", (code) => {
|
|
1067
1080
|
childRunning = false;
|
|
1068
1081
|
const exitCode = code ?? 0;
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
setTimeout(() => process.exit(exitCode), 200);
|
|
1072
|
-
} else if (pendingPermissionGrant) {
|
|
1073
|
-
// Permission was granted while claude was running — respawn to retry
|
|
1074
|
-
const tool = pendingPermissionGrant;
|
|
1075
|
-
pendingPermissionGrant = null;
|
|
1076
|
-
process.stderr.write(`${GREEN}Retrying with new permission: ${tool}${RESET}\n`);
|
|
1077
|
-
respawnWithContinue(`Permission for ${tool} was granted. Please retry your last action.`);
|
|
1078
|
-
} else {
|
|
1079
|
-
sendToServer({
|
|
1080
|
-
type: "agent_event", sessionId,
|
|
1081
|
-
event: { kind: "status", status: "idle" },
|
|
1082
|
-
});
|
|
1083
|
-
}
|
|
1082
|
+
sendToServer({ type: "exit", sessionId, exitCode });
|
|
1083
|
+
setTimeout(() => process.exit(exitCode), 200);
|
|
1084
1084
|
});
|
|
1085
1085
|
|
|
1086
1086
|
proc.on("error", (err) => {
|
|
@@ -1092,6 +1092,24 @@ if (adapter) {
|
|
|
1092
1092
|
return proc;
|
|
1093
1093
|
}
|
|
1094
1094
|
|
|
1095
|
+
// Send a follow-up message via stdin (no respawn!)
|
|
1096
|
+
function sendFollowUp(text) {
|
|
1097
|
+
if (!child || child.exitCode !== null) return;
|
|
1098
|
+
const msg = {
|
|
1099
|
+
type: "user",
|
|
1100
|
+
message: {
|
|
1101
|
+
role: "user",
|
|
1102
|
+
content: text,
|
|
1103
|
+
},
|
|
1104
|
+
};
|
|
1105
|
+
try {
|
|
1106
|
+
child.stdin.write(JSON.stringify(msg) + "\n");
|
|
1107
|
+
childRunning = true; // Claude is now processing
|
|
1108
|
+
} catch (err) {
|
|
1109
|
+
process.stderr.write(`${RED}[orch] Failed to send via stdin: ${err.message}${RESET}\n`);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1095
1113
|
// Auto-approve a tool permission (used by yolo mode and manual approval)
|
|
1096
1114
|
function approvePermission(toolName, scope) {
|
|
1097
1115
|
let permEntry = toolName;
|
|
@@ -1194,37 +1212,29 @@ if (adapter) {
|
|
|
1194
1212
|
}
|
|
1195
1213
|
}
|
|
1196
1214
|
|
|
1197
|
-
// Start the
|
|
1215
|
+
// Start the claude process (single process for entire session — no respawning)
|
|
1198
1216
|
const initialArgs = adapter.buildArgs(prompt, adapterFlags);
|
|
1199
1217
|
if (yoloMode) {
|
|
1200
1218
|
initialArgs.push("--dangerously-skip-permissions");
|
|
1201
1219
|
}
|
|
1202
1220
|
spawnClaude(initialArgs);
|
|
1203
1221
|
|
|
1204
|
-
//
|
|
1222
|
+
// Send initial prompt via stdin (not -p flag)
|
|
1205
1223
|
if (prompt) {
|
|
1224
|
+
sendFollowUp(prompt);
|
|
1225
|
+
// Emit the initial prompt as a user message so the dashboard shows it
|
|
1206
1226
|
sendToServer({
|
|
1207
1227
|
type: "agent_event", sessionId,
|
|
1208
1228
|
event: { kind: "user_message", text: prompt },
|
|
1209
1229
|
});
|
|
1210
1230
|
}
|
|
1211
1231
|
|
|
1212
|
-
// Respawn claude with -c to continue after permission grants or follow-up
|
|
1213
|
-
function respawnWithContinue(prompt) {
|
|
1214
|
-
const args = ["--output-format", "stream-json", "--verbose", "-c"];
|
|
1215
|
-
if (prompt) args.push("-p", prompt);
|
|
1216
|
-
if (yoloMode) args.push("--dangerously-skip-permissions");
|
|
1217
|
-
spawnClaude(args);
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
// Queue of permission grants received while claude is still running
|
|
1221
|
-
let pendingPermissionGrant = null;
|
|
1222
|
-
|
|
1223
1232
|
handleServerMessage = (msg) => {
|
|
1224
1233
|
if (msg.type === "agent_input" && (msg.text || msg.images)) {
|
|
1225
|
-
// Follow-up from dashboard —
|
|
1234
|
+
// Follow-up from dashboard — send via stdin (no respawn!)
|
|
1226
1235
|
if (childRunning) {
|
|
1227
|
-
process.stderr.write(`${DIM}[orch]
|
|
1236
|
+
process.stderr.write(`${DIM}[orch] Queuing input — claude is still processing${RESET}\n`);
|
|
1237
|
+
// TODO: queue and send after current turn completes
|
|
1228
1238
|
return;
|
|
1229
1239
|
}
|
|
1230
1240
|
let promptText = msg.text || "continue";
|
|
@@ -1241,28 +1251,16 @@ if (adapter) {
|
|
|
1241
1251
|
const fileList = imagePaths.map((p) => `[Attached image: ${p} — use Read tool to view]`).join("\n");
|
|
1242
1252
|
promptText = `${fileList}\n\n${promptText}`;
|
|
1243
1253
|
}
|
|
1244
|
-
|
|
1254
|
+
sendFollowUp(promptText);
|
|
1245
1255
|
} else if (msg.type === "agent_permission" && msg.tool && msg.action === "allow") {
|
|
1246
1256
|
const scope = msg.scope || "session";
|
|
1247
1257
|
approvePermission(msg.tool, scope);
|
|
1248
1258
|
process.stderr.write(`${GREEN}Permission granted (${scope}): ${msg.tool}${RESET}\n`);
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
// Claude is still running — remember the grant and respawn when it exits
|
|
1252
|
-
pendingPermissionGrant = msg.tool;
|
|
1253
|
-
} else {
|
|
1254
|
-
// Claude already finished — respawn with -c to retry with new permission
|
|
1255
|
-
respawnWithContinue(`Permission for ${msg.tool} was granted. Please retry your last action.`);
|
|
1256
|
-
}
|
|
1259
|
+
// Send permission grant as follow-up via stdin
|
|
1260
|
+
sendFollowUp(`Permission for ${msg.tool} was granted. Please retry your last action.`);
|
|
1257
1261
|
} else if (msg.type === "agent_permission" && msg.tool && msg.action === "deny") {
|
|
1258
1262
|
process.stderr.write(`${RED}Permission denied: ${msg.tool}${RESET}\n`);
|
|
1259
|
-
|
|
1260
|
-
if (childRunning) {
|
|
1261
|
-
// Claude is still running — remember and respawn when it exits
|
|
1262
|
-
pendingPermissionGrant = null;
|
|
1263
|
-
} else {
|
|
1264
|
-
respawnWithContinue(`Permission for ${msg.tool} was denied by the user. Do not retry this tool — find an alternative approach or skip this step.`);
|
|
1265
|
-
}
|
|
1263
|
+
sendFollowUp(`Permission for ${msg.tool} was denied by the user. Do not retry this tool — find an alternative approach or skip this step.`);
|
|
1266
1264
|
} else if (msg.type === "agent_permission" && msg.tool && msg.action === "revoke") {
|
|
1267
1265
|
removePermission(msg.tool);
|
|
1268
1266
|
broadcastPermissions();
|
|
@@ -1274,7 +1272,7 @@ if (adapter) {
|
|
|
1274
1272
|
} else if (msg.type === "stop_session") {
|
|
1275
1273
|
process.stderr.write(`${RED}[orch] Stopped from dashboard${RESET}\n`);
|
|
1276
1274
|
exitRequested = true;
|
|
1277
|
-
if (
|
|
1275
|
+
if (child && child.exitCode === null) {
|
|
1278
1276
|
child.kill("SIGINT");
|
|
1279
1277
|
} else {
|
|
1280
1278
|
cleanup();
|