@h-rig/cli 0.0.6-alpha.23 → 0.0.6-alpha.24
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/bin/rig.js +144 -28
- package/dist/src/commands/_operator-view.js +145 -29
- package/dist/src/commands/_pi-frontend.js +145 -29
- package/dist/src/commands/_pi-worker-bridge-extension.js +145 -29
- package/dist/src/commands/run.js +145 -29
- package/dist/src/commands/task.js +145 -29
- package/dist/src/commands.js +144 -28
- package/dist/src/index.js +144 -28
- package/package.json +6 -6
package/dist/bin/rig.js
CHANGED
|
@@ -6703,6 +6703,15 @@ function appendTranscript(state, label, text2) {
|
|
|
6703
6703
|
state.transcript.splice(0, state.transcript.length - MAX_TRANSCRIPT_LINES);
|
|
6704
6704
|
}
|
|
6705
6705
|
}
|
|
6706
|
+
function nativePiUi(ctx) {
|
|
6707
|
+
const ui = ctx.ui;
|
|
6708
|
+
return typeof ui.emitSessionEvent === "function" && typeof ui.appendSessionMessages === "function" ? ui : null;
|
|
6709
|
+
}
|
|
6710
|
+
function syncNativeDisplayCwd(ctx, state) {
|
|
6711
|
+
const ui = nativePiUi(ctx);
|
|
6712
|
+
if (ui?.setDisplayCwd && state.cwd)
|
|
6713
|
+
ui.setDisplayCwd(state.cwd);
|
|
6714
|
+
}
|
|
6706
6715
|
function parseExtensionUiRequest(value) {
|
|
6707
6716
|
const request = recordOf(value) ?? {};
|
|
6708
6717
|
const requestId = String(request.requestId ?? request.id ?? `ui-${Date.now()}`);
|
|
@@ -6746,8 +6755,13 @@ function renderBridgeWidget(state) {
|
|
|
6746
6755
|
function updatePiUi(ctx, state) {
|
|
6747
6756
|
ctx.ui.setTitle("Pi \xB7 Rig worker daemon");
|
|
6748
6757
|
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker Pi WS live" : state.status);
|
|
6758
|
+
syncNativeDisplayCwd(ctx, state);
|
|
6759
|
+
if (state.nativeStream && nativePiUi(ctx)) {
|
|
6760
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
|
|
6761
|
+
return;
|
|
6762
|
+
}
|
|
6749
6763
|
ctx.ui.setWorkingVisible(false);
|
|
6750
|
-
ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
|
|
6764
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", [`Worker Pi daemon bridge \xB7 degraded widget transcript`, ...renderBridgeWidget(state)], { placement: "aboveEditor" });
|
|
6751
6765
|
}
|
|
6752
6766
|
function applyStatus(state, payload) {
|
|
6753
6767
|
const status = recordOf(payload.status) ?? payload;
|
|
@@ -6765,7 +6779,7 @@ function applyMessage(state, message2) {
|
|
|
6765
6779
|
const label = role === "assistant" ? "Pi" : role === "user" ? "You" : role === "tool" || role === "toolResult" ? "Tool" : "System";
|
|
6766
6780
|
appendTranscript(state, label, textFromContent(record.content ?? record.message ?? record.text ?? ""));
|
|
6767
6781
|
}
|
|
6768
|
-
function applyPiEvent(state, eventValue) {
|
|
6782
|
+
function applyPiEvent(ctx, state, eventValue) {
|
|
6769
6783
|
const event = recordOf(eventValue);
|
|
6770
6784
|
if (!event)
|
|
6771
6785
|
return;
|
|
@@ -6773,11 +6787,20 @@ function applyPiEvent(state, eventValue) {
|
|
|
6773
6787
|
if (type === "agent_start") {
|
|
6774
6788
|
state.streaming = true;
|
|
6775
6789
|
state.status = "streaming";
|
|
6790
|
+
} else if (type === "agent_end") {
|
|
6791
|
+
state.streaming = false;
|
|
6792
|
+
state.status = "idle";
|
|
6793
|
+
} else if (type === "queue_update") {
|
|
6794
|
+
const steering = Array.isArray(event.steering) ? event.steering.length : 0;
|
|
6795
|
+
const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
|
|
6796
|
+
state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
|
|
6797
|
+
}
|
|
6798
|
+
const native = nativePiUi(ctx);
|
|
6799
|
+
if (state.nativeStream && native?.emitSessionEvent) {
|
|
6800
|
+
native.emitSessionEvent(eventValue);
|
|
6776
6801
|
return;
|
|
6777
6802
|
}
|
|
6778
6803
|
if (type === "agent_end") {
|
|
6779
|
-
state.streaming = false;
|
|
6780
|
-
state.status = "idle";
|
|
6781
6804
|
appendTranscript(state, "System", "Agent turn complete.");
|
|
6782
6805
|
return;
|
|
6783
6806
|
}
|
|
@@ -6802,29 +6825,60 @@ function applyPiEvent(state, eventValue) {
|
|
|
6802
6825
|
}
|
|
6803
6826
|
if (type === "tool_execution_end") {
|
|
6804
6827
|
appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.result ?? `${String(event.toolName ?? "tool")} complete`));
|
|
6805
|
-
return;
|
|
6806
|
-
}
|
|
6807
|
-
if (type === "queue_update") {
|
|
6808
|
-
const steering = Array.isArray(event.steering) ? event.steering.length : 0;
|
|
6809
|
-
const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
|
|
6810
|
-
state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
|
|
6811
6828
|
}
|
|
6812
6829
|
}
|
|
6830
|
+
function firstPendingShell(state) {
|
|
6831
|
+
return state.pendingShells[0];
|
|
6832
|
+
}
|
|
6833
|
+
function finishPendingShell(state, shell, result) {
|
|
6834
|
+
const index = state.pendingShells.indexOf(shell);
|
|
6835
|
+
if (index !== -1)
|
|
6836
|
+
state.pendingShells.splice(index, 1);
|
|
6837
|
+
shell.resolve(result);
|
|
6838
|
+
}
|
|
6839
|
+
function failPendingShell(state, shell, error) {
|
|
6840
|
+
const index = state.pendingShells.indexOf(shell);
|
|
6841
|
+
if (index !== -1)
|
|
6842
|
+
state.pendingShells.splice(index, 1);
|
|
6843
|
+
shell.reject(error);
|
|
6844
|
+
}
|
|
6813
6845
|
function applyUiEvent(state, value) {
|
|
6814
6846
|
const event = recordOf(value);
|
|
6815
6847
|
if (!event)
|
|
6816
6848
|
return;
|
|
6817
6849
|
const type = String(event.type ?? "ui");
|
|
6818
|
-
if (type === "shell.
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6850
|
+
if (type === "shell.chunk") {
|
|
6851
|
+
const pending = firstPendingShell(state);
|
|
6852
|
+
const chunk = asText(event.chunk);
|
|
6853
|
+
if (pending) {
|
|
6854
|
+
pending.sawChunk = true;
|
|
6855
|
+
pending.onData(Buffer.from(chunk));
|
|
6856
|
+
} else {
|
|
6857
|
+
appendTranscript(state, "Tool", chunk);
|
|
6858
|
+
}
|
|
6859
|
+
return;
|
|
6860
|
+
}
|
|
6861
|
+
if (type === "shell.end") {
|
|
6862
|
+
const pending = firstPendingShell(state);
|
|
6863
|
+
const output = asText(event.output ?? "");
|
|
6864
|
+
const exitCode = typeof event.exitCode === "number" ? event.exitCode : event.isError === true ? 1 : 0;
|
|
6865
|
+
if (pending) {
|
|
6866
|
+
if (output && !pending.sawChunk)
|
|
6867
|
+
pending.onData(Buffer.from(output));
|
|
6868
|
+
finishPendingShell(state, pending, { exitCode });
|
|
6869
|
+
} else {
|
|
6870
|
+
appendTranscript(state, event.isError === true ? "Error" : "Tool", output || `exit ${String(exitCode)}`);
|
|
6871
|
+
}
|
|
6872
|
+
return;
|
|
6873
|
+
}
|
|
6874
|
+
if (type === "shell.start") {
|
|
6875
|
+
if (!firstPendingShell(state))
|
|
6876
|
+
appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
|
|
6877
|
+
return;
|
|
6878
|
+
}
|
|
6879
|
+
appendTranscript(state, "System", `${type}: ${asText(event)}`);
|
|
6826
6880
|
}
|
|
6827
|
-
function applyEnvelope(state, envelopeValue) {
|
|
6881
|
+
function applyEnvelope(ctx, state, envelopeValue) {
|
|
6828
6882
|
const envelope = recordOf(envelopeValue);
|
|
6829
6883
|
if (!envelope)
|
|
6830
6884
|
return;
|
|
@@ -6833,7 +6887,8 @@ function applyEnvelope(state, envelopeValue) {
|
|
|
6833
6887
|
const metadata = recordOf(envelope.metadata);
|
|
6834
6888
|
state.cwd = typeof metadata?.cwd === "string" ? metadata.cwd : state.cwd;
|
|
6835
6889
|
state.status = "worker Pi daemon ready";
|
|
6836
|
-
|
|
6890
|
+
if (!state.nativeStream)
|
|
6891
|
+
appendTranscript(state, "System", "Connected to worker Pi daemon.");
|
|
6837
6892
|
} else if (type === "status.update") {
|
|
6838
6893
|
applyStatus(state, envelope);
|
|
6839
6894
|
} else if (type === "activity.update") {
|
|
@@ -6845,10 +6900,11 @@ function applyEnvelope(state, envelopeValue) {
|
|
|
6845
6900
|
} else if (type === "pi.ui_event") {
|
|
6846
6901
|
applyUiEvent(state, envelope.event);
|
|
6847
6902
|
} else if (type === "pi.event") {
|
|
6848
|
-
applyPiEvent(state, envelope.event);
|
|
6903
|
+
applyPiEvent(ctx, state, envelope.event);
|
|
6849
6904
|
} else if (type === "error") {
|
|
6850
6905
|
appendTranscript(state, "Error", asText(envelope.message ?? envelope.detail ?? "unknown error"));
|
|
6851
6906
|
}
|
|
6907
|
+
syncNativeDisplayCwd(ctx, state);
|
|
6852
6908
|
}
|
|
6853
6909
|
async function waitForWorkerReady(options, ctx, state) {
|
|
6854
6910
|
while (true) {
|
|
@@ -6869,7 +6925,7 @@ async function waitForWorkerReady(options, ctx, state) {
|
|
|
6869
6925
|
continue;
|
|
6870
6926
|
}
|
|
6871
6927
|
const sessionRecord = recordOf(session) ?? {};
|
|
6872
|
-
applyEnvelope(state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
|
|
6928
|
+
applyEnvelope(ctx, state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
|
|
6873
6929
|
updatePiUi(ctx, state);
|
|
6874
6930
|
return true;
|
|
6875
6931
|
}
|
|
@@ -6899,7 +6955,7 @@ async function connectWorkerStream(options, ctx, state) {
|
|
|
6899
6955
|
if (!catchupDone)
|
|
6900
6956
|
buffered.push(payload);
|
|
6901
6957
|
else {
|
|
6902
|
-
applyEnvelope(state, payload);
|
|
6958
|
+
applyEnvelope(ctx, state, payload);
|
|
6903
6959
|
updatePiUi(ctx, state);
|
|
6904
6960
|
}
|
|
6905
6961
|
} catch (error) {
|
|
@@ -6922,8 +6978,12 @@ async function connectWorkerStream(options, ctx, state) {
|
|
|
6922
6978
|
getRunPiCommandsViaServer(options.context, options.runId)
|
|
6923
6979
|
]);
|
|
6924
6980
|
const messages = Array.isArray(messagesPayload.messages) ? messagesPayload.messages : [];
|
|
6925
|
-
|
|
6926
|
-
|
|
6981
|
+
const native = nativePiUi(ctx);
|
|
6982
|
+
if (state.nativeStream && native?.appendSessionMessages)
|
|
6983
|
+
native.appendSessionMessages(messages);
|
|
6984
|
+
else
|
|
6985
|
+
for (const message2 of messages)
|
|
6986
|
+
applyMessage(state, message2);
|
|
6927
6987
|
applyStatus(state, statusPayload);
|
|
6928
6988
|
const commands = Array.isArray(commandsPayload.commands) ? commandsPayload.commands : [];
|
|
6929
6989
|
state.commands = commands.flatMap((command) => {
|
|
@@ -6932,7 +6992,7 @@ async function connectWorkerStream(options, ctx, state) {
|
|
|
6932
6992
|
});
|
|
6933
6993
|
catchupDone = true;
|
|
6934
6994
|
for (const payload of buffered.splice(0))
|
|
6935
|
-
applyEnvelope(state, payload);
|
|
6995
|
+
applyEnvelope(ctx, state, payload);
|
|
6936
6996
|
updatePiUi(ctx, state);
|
|
6937
6997
|
} catch (error) {
|
|
6938
6998
|
appendTranscript(state, "Error", `Worker Pi catch-up failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -6941,6 +7001,51 @@ async function connectWorkerStream(options, ctx, state) {
|
|
|
6941
7001
|
}
|
|
6942
7002
|
await closePromise;
|
|
6943
7003
|
}
|
|
7004
|
+
function createRemoteBashOperations(options, state, excludeFromContext) {
|
|
7005
|
+
return {
|
|
7006
|
+
exec(command, _cwd, execOptions) {
|
|
7007
|
+
return new Promise((resolve20, reject) => {
|
|
7008
|
+
const pending = {
|
|
7009
|
+
command,
|
|
7010
|
+
onData: execOptions.onData,
|
|
7011
|
+
resolve: resolve20,
|
|
7012
|
+
reject,
|
|
7013
|
+
sawChunk: false
|
|
7014
|
+
};
|
|
7015
|
+
const cleanup = () => {
|
|
7016
|
+
execOptions.signal?.removeEventListener("abort", onAbort);
|
|
7017
|
+
if (timer)
|
|
7018
|
+
clearTimeout(timer);
|
|
7019
|
+
};
|
|
7020
|
+
const onAbort = () => {
|
|
7021
|
+
cleanup();
|
|
7022
|
+
failPendingShell(state, pending, new Error("Remote worker shell command aborted locally."));
|
|
7023
|
+
};
|
|
7024
|
+
const timeoutMs = typeof execOptions.timeout === "number" && execOptions.timeout > 0 ? execOptions.timeout : 0;
|
|
7025
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
7026
|
+
cleanup();
|
|
7027
|
+
failPendingShell(state, pending, new Error(`Remote worker shell command timed out after ${timeoutMs}ms.`));
|
|
7028
|
+
}, timeoutMs) : null;
|
|
7029
|
+
const wrappedResolve = pending.resolve;
|
|
7030
|
+
const wrappedReject = pending.reject;
|
|
7031
|
+
pending.resolve = (result) => {
|
|
7032
|
+
cleanup();
|
|
7033
|
+
wrappedResolve(result);
|
|
7034
|
+
};
|
|
7035
|
+
pending.reject = (error) => {
|
|
7036
|
+
cleanup();
|
|
7037
|
+
wrappedReject(error);
|
|
7038
|
+
};
|
|
7039
|
+
execOptions.signal?.addEventListener("abort", onAbort, { once: true });
|
|
7040
|
+
state.pendingShells.push(pending);
|
|
7041
|
+
sendRunPiShellViaServer(options.context, options.runId, `${excludeFromContext ? "!!" : "!"}${command}`).catch((error) => {
|
|
7042
|
+
cleanup();
|
|
7043
|
+
failPendingShell(state, pending, error instanceof Error ? error : new Error(String(error)));
|
|
7044
|
+
});
|
|
7045
|
+
});
|
|
7046
|
+
}
|
|
7047
|
+
};
|
|
7048
|
+
}
|
|
6944
7049
|
async function answerPendingUi(options, state, line) {
|
|
6945
7050
|
const pending = state.pendingUi;
|
|
6946
7051
|
if (!pending)
|
|
@@ -7006,13 +7111,22 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
7006
7111
|
commands: [],
|
|
7007
7112
|
streaming: false,
|
|
7008
7113
|
pendingUi: null,
|
|
7009
|
-
|
|
7114
|
+
pendingShells: [],
|
|
7115
|
+
wsConnected: false,
|
|
7116
|
+
nativeStream: false
|
|
7010
7117
|
};
|
|
7011
7118
|
if (options.initialMessageSent)
|
|
7012
7119
|
appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
|
|
7120
|
+
let nativePiUiContextAvailable = false;
|
|
7121
|
+
pi.on("user_bash", (event) => {
|
|
7122
|
+
state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
|
|
7123
|
+
return { operations: createRemoteBashOperations(options, state, event.excludeFromContext === true) };
|
|
7124
|
+
});
|
|
7013
7125
|
pi.on("session_start", async (_event, ctx) => {
|
|
7126
|
+
nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
|
|
7127
|
+
state.nativeStream = nativePiUiContextAvailable;
|
|
7014
7128
|
updatePiUi(ctx, state);
|
|
7015
|
-
ctx.ui.notify("Rig worker Pi bridge extension loaded", "info");
|
|
7129
|
+
ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker Pi native stream bridge loaded" : "Rig worker Pi bridge extension loaded (degraded widget fallback)", "info");
|
|
7016
7130
|
ctx.ui.onTerminalInput((data) => {
|
|
7017
7131
|
if (data.includes("\x04")) {
|
|
7018
7132
|
ctx.shutdown();
|
|
@@ -7026,6 +7140,8 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
7026
7140
|
const text2 = [editorText, inlineText].filter(Boolean).join(" ").trim();
|
|
7027
7141
|
if (!text2)
|
|
7028
7142
|
return;
|
|
7143
|
+
if (text2.startsWith("!"))
|
|
7144
|
+
return;
|
|
7029
7145
|
ctx.ui.setEditorText("");
|
|
7030
7146
|
routeInput(options, ctx, state, text2).catch((error) => {
|
|
7031
7147
|
appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
|
|
@@ -522,6 +522,15 @@ function appendTranscript(state, label, text) {
|
|
|
522
522
|
state.transcript.splice(0, state.transcript.length - MAX_TRANSCRIPT_LINES);
|
|
523
523
|
}
|
|
524
524
|
}
|
|
525
|
+
function nativePiUi(ctx) {
|
|
526
|
+
const ui = ctx.ui;
|
|
527
|
+
return typeof ui.emitSessionEvent === "function" && typeof ui.appendSessionMessages === "function" ? ui : null;
|
|
528
|
+
}
|
|
529
|
+
function syncNativeDisplayCwd(ctx, state) {
|
|
530
|
+
const ui = nativePiUi(ctx);
|
|
531
|
+
if (ui?.setDisplayCwd && state.cwd)
|
|
532
|
+
ui.setDisplayCwd(state.cwd);
|
|
533
|
+
}
|
|
525
534
|
function parseExtensionUiRequest(value) {
|
|
526
535
|
const request = recordOf(value) ?? {};
|
|
527
536
|
const requestId = String(request.requestId ?? request.id ?? `ui-${Date.now()}`);
|
|
@@ -565,8 +574,13 @@ function renderBridgeWidget(state) {
|
|
|
565
574
|
function updatePiUi(ctx, state) {
|
|
566
575
|
ctx.ui.setTitle("Pi \xB7 Rig worker daemon");
|
|
567
576
|
ctx.ui.setStatus("rig-worker-pi", state.wsConnected ? "worker Pi WS live" : state.status);
|
|
577
|
+
syncNativeDisplayCwd(ctx, state);
|
|
578
|
+
if (state.nativeStream && nativePiUi(ctx)) {
|
|
579
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", undefined);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
568
582
|
ctx.ui.setWorkingVisible(false);
|
|
569
|
-
ctx.ui.setWidget("rig-worker-pi-transcript", renderBridgeWidget(state), { placement: "aboveEditor" });
|
|
583
|
+
ctx.ui.setWidget("rig-worker-pi-transcript", [`Worker Pi daemon bridge \xB7 degraded widget transcript`, ...renderBridgeWidget(state)], { placement: "aboveEditor" });
|
|
570
584
|
}
|
|
571
585
|
function applyStatus(state, payload) {
|
|
572
586
|
const status = recordOf(payload.status) ?? payload;
|
|
@@ -584,7 +598,7 @@ function applyMessage(state, message) {
|
|
|
584
598
|
const label = role === "assistant" ? "Pi" : role === "user" ? "You" : role === "tool" || role === "toolResult" ? "Tool" : "System";
|
|
585
599
|
appendTranscript(state, label, textFromContent(record.content ?? record.message ?? record.text ?? ""));
|
|
586
600
|
}
|
|
587
|
-
function applyPiEvent(state, eventValue) {
|
|
601
|
+
function applyPiEvent(ctx, state, eventValue) {
|
|
588
602
|
const event = recordOf(eventValue);
|
|
589
603
|
if (!event)
|
|
590
604
|
return;
|
|
@@ -592,11 +606,20 @@ function applyPiEvent(state, eventValue) {
|
|
|
592
606
|
if (type === "agent_start") {
|
|
593
607
|
state.streaming = true;
|
|
594
608
|
state.status = "streaming";
|
|
609
|
+
} else if (type === "agent_end") {
|
|
610
|
+
state.streaming = false;
|
|
611
|
+
state.status = "idle";
|
|
612
|
+
} else if (type === "queue_update") {
|
|
613
|
+
const steering = Array.isArray(event.steering) ? event.steering.length : 0;
|
|
614
|
+
const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
|
|
615
|
+
state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
|
|
616
|
+
}
|
|
617
|
+
const native = nativePiUi(ctx);
|
|
618
|
+
if (state.nativeStream && native?.emitSessionEvent) {
|
|
619
|
+
native.emitSessionEvent(eventValue);
|
|
595
620
|
return;
|
|
596
621
|
}
|
|
597
622
|
if (type === "agent_end") {
|
|
598
|
-
state.streaming = false;
|
|
599
|
-
state.status = "idle";
|
|
600
623
|
appendTranscript(state, "System", "Agent turn complete.");
|
|
601
624
|
return;
|
|
602
625
|
}
|
|
@@ -621,29 +644,60 @@ function applyPiEvent(state, eventValue) {
|
|
|
621
644
|
}
|
|
622
645
|
if (type === "tool_execution_end") {
|
|
623
646
|
appendTranscript(state, event.isError === true ? "Error" : "Tool", asText(event.result ?? `${String(event.toolName ?? "tool")} complete`));
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
if (type === "queue_update") {
|
|
627
|
-
const steering = Array.isArray(event.steering) ? event.steering.length : 0;
|
|
628
|
-
const followUp = Array.isArray(event.followUp) ? event.followUp.length : 0;
|
|
629
|
-
state.status = `queued \xB7 steer ${steering} \xB7 follow-up ${followUp}`;
|
|
630
647
|
}
|
|
631
648
|
}
|
|
649
|
+
function firstPendingShell(state) {
|
|
650
|
+
return state.pendingShells[0];
|
|
651
|
+
}
|
|
652
|
+
function finishPendingShell(state, shell, result) {
|
|
653
|
+
const index = state.pendingShells.indexOf(shell);
|
|
654
|
+
if (index !== -1)
|
|
655
|
+
state.pendingShells.splice(index, 1);
|
|
656
|
+
shell.resolve(result);
|
|
657
|
+
}
|
|
658
|
+
function failPendingShell(state, shell, error) {
|
|
659
|
+
const index = state.pendingShells.indexOf(shell);
|
|
660
|
+
if (index !== -1)
|
|
661
|
+
state.pendingShells.splice(index, 1);
|
|
662
|
+
shell.reject(error);
|
|
663
|
+
}
|
|
632
664
|
function applyUiEvent(state, value) {
|
|
633
665
|
const event = recordOf(value);
|
|
634
666
|
if (!event)
|
|
635
667
|
return;
|
|
636
668
|
const type = String(event.type ?? "ui");
|
|
637
|
-
if (type === "shell.
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
}
|
|
646
|
-
|
|
669
|
+
if (type === "shell.chunk") {
|
|
670
|
+
const pending = firstPendingShell(state);
|
|
671
|
+
const chunk = asText(event.chunk);
|
|
672
|
+
if (pending) {
|
|
673
|
+
pending.sawChunk = true;
|
|
674
|
+
pending.onData(Buffer.from(chunk));
|
|
675
|
+
} else {
|
|
676
|
+
appendTranscript(state, "Tool", chunk);
|
|
677
|
+
}
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
if (type === "shell.end") {
|
|
681
|
+
const pending = firstPendingShell(state);
|
|
682
|
+
const output = asText(event.output ?? "");
|
|
683
|
+
const exitCode = typeof event.exitCode === "number" ? event.exitCode : event.isError === true ? 1 : 0;
|
|
684
|
+
if (pending) {
|
|
685
|
+
if (output && !pending.sawChunk)
|
|
686
|
+
pending.onData(Buffer.from(output));
|
|
687
|
+
finishPendingShell(state, pending, { exitCode });
|
|
688
|
+
} else {
|
|
689
|
+
appendTranscript(state, event.isError === true ? "Error" : "Tool", output || `exit ${String(exitCode)}`);
|
|
690
|
+
}
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
if (type === "shell.start") {
|
|
694
|
+
if (!firstPendingShell(state))
|
|
695
|
+
appendTranscript(state, "Tool", `$ ${asText(event.command)}`);
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
appendTranscript(state, "System", `${type}: ${asText(event)}`);
|
|
699
|
+
}
|
|
700
|
+
function applyEnvelope(ctx, state, envelopeValue) {
|
|
647
701
|
const envelope = recordOf(envelopeValue);
|
|
648
702
|
if (!envelope)
|
|
649
703
|
return;
|
|
@@ -652,7 +706,8 @@ function applyEnvelope(state, envelopeValue) {
|
|
|
652
706
|
const metadata = recordOf(envelope.metadata);
|
|
653
707
|
state.cwd = typeof metadata?.cwd === "string" ? metadata.cwd : state.cwd;
|
|
654
708
|
state.status = "worker Pi daemon ready";
|
|
655
|
-
|
|
709
|
+
if (!state.nativeStream)
|
|
710
|
+
appendTranscript(state, "System", "Connected to worker Pi daemon.");
|
|
656
711
|
} else if (type === "status.update") {
|
|
657
712
|
applyStatus(state, envelope);
|
|
658
713
|
} else if (type === "activity.update") {
|
|
@@ -664,10 +719,11 @@ function applyEnvelope(state, envelopeValue) {
|
|
|
664
719
|
} else if (type === "pi.ui_event") {
|
|
665
720
|
applyUiEvent(state, envelope.event);
|
|
666
721
|
} else if (type === "pi.event") {
|
|
667
|
-
applyPiEvent(state, envelope.event);
|
|
722
|
+
applyPiEvent(ctx, state, envelope.event);
|
|
668
723
|
} else if (type === "error") {
|
|
669
724
|
appendTranscript(state, "Error", asText(envelope.message ?? envelope.detail ?? "unknown error"));
|
|
670
725
|
}
|
|
726
|
+
syncNativeDisplayCwd(ctx, state);
|
|
671
727
|
}
|
|
672
728
|
async function waitForWorkerReady(options, ctx, state) {
|
|
673
729
|
while (true) {
|
|
@@ -688,7 +744,7 @@ async function waitForWorkerReady(options, ctx, state) {
|
|
|
688
744
|
continue;
|
|
689
745
|
}
|
|
690
746
|
const sessionRecord = recordOf(session) ?? {};
|
|
691
|
-
applyEnvelope(state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
|
|
747
|
+
applyEnvelope(ctx, state, { type: "ready", metadata: sessionRecord.metadata ?? sessionRecord });
|
|
692
748
|
updatePiUi(ctx, state);
|
|
693
749
|
return true;
|
|
694
750
|
}
|
|
@@ -718,7 +774,7 @@ async function connectWorkerStream(options, ctx, state) {
|
|
|
718
774
|
if (!catchupDone)
|
|
719
775
|
buffered.push(payload);
|
|
720
776
|
else {
|
|
721
|
-
applyEnvelope(state, payload);
|
|
777
|
+
applyEnvelope(ctx, state, payload);
|
|
722
778
|
updatePiUi(ctx, state);
|
|
723
779
|
}
|
|
724
780
|
} catch (error) {
|
|
@@ -741,8 +797,12 @@ async function connectWorkerStream(options, ctx, state) {
|
|
|
741
797
|
getRunPiCommandsViaServer(options.context, options.runId)
|
|
742
798
|
]);
|
|
743
799
|
const messages = Array.isArray(messagesPayload.messages) ? messagesPayload.messages : [];
|
|
744
|
-
|
|
745
|
-
|
|
800
|
+
const native = nativePiUi(ctx);
|
|
801
|
+
if (state.nativeStream && native?.appendSessionMessages)
|
|
802
|
+
native.appendSessionMessages(messages);
|
|
803
|
+
else
|
|
804
|
+
for (const message of messages)
|
|
805
|
+
applyMessage(state, message);
|
|
746
806
|
applyStatus(state, statusPayload);
|
|
747
807
|
const commands = Array.isArray(commandsPayload.commands) ? commandsPayload.commands : [];
|
|
748
808
|
state.commands = commands.flatMap((command) => {
|
|
@@ -751,7 +811,7 @@ async function connectWorkerStream(options, ctx, state) {
|
|
|
751
811
|
});
|
|
752
812
|
catchupDone = true;
|
|
753
813
|
for (const payload of buffered.splice(0))
|
|
754
|
-
applyEnvelope(state, payload);
|
|
814
|
+
applyEnvelope(ctx, state, payload);
|
|
755
815
|
updatePiUi(ctx, state);
|
|
756
816
|
} catch (error) {
|
|
757
817
|
appendTranscript(state, "Error", `Worker Pi catch-up failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -760,6 +820,51 @@ async function connectWorkerStream(options, ctx, state) {
|
|
|
760
820
|
}
|
|
761
821
|
await closePromise;
|
|
762
822
|
}
|
|
823
|
+
function createRemoteBashOperations(options, state, excludeFromContext) {
|
|
824
|
+
return {
|
|
825
|
+
exec(command, _cwd, execOptions) {
|
|
826
|
+
return new Promise((resolve3, reject) => {
|
|
827
|
+
const pending = {
|
|
828
|
+
command,
|
|
829
|
+
onData: execOptions.onData,
|
|
830
|
+
resolve: resolve3,
|
|
831
|
+
reject,
|
|
832
|
+
sawChunk: false
|
|
833
|
+
};
|
|
834
|
+
const cleanup = () => {
|
|
835
|
+
execOptions.signal?.removeEventListener("abort", onAbort);
|
|
836
|
+
if (timer)
|
|
837
|
+
clearTimeout(timer);
|
|
838
|
+
};
|
|
839
|
+
const onAbort = () => {
|
|
840
|
+
cleanup();
|
|
841
|
+
failPendingShell(state, pending, new Error("Remote worker shell command aborted locally."));
|
|
842
|
+
};
|
|
843
|
+
const timeoutMs = typeof execOptions.timeout === "number" && execOptions.timeout > 0 ? execOptions.timeout : 0;
|
|
844
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
845
|
+
cleanup();
|
|
846
|
+
failPendingShell(state, pending, new Error(`Remote worker shell command timed out after ${timeoutMs}ms.`));
|
|
847
|
+
}, timeoutMs) : null;
|
|
848
|
+
const wrappedResolve = pending.resolve;
|
|
849
|
+
const wrappedReject = pending.reject;
|
|
850
|
+
pending.resolve = (result) => {
|
|
851
|
+
cleanup();
|
|
852
|
+
wrappedResolve(result);
|
|
853
|
+
};
|
|
854
|
+
pending.reject = (error) => {
|
|
855
|
+
cleanup();
|
|
856
|
+
wrappedReject(error);
|
|
857
|
+
};
|
|
858
|
+
execOptions.signal?.addEventListener("abort", onAbort, { once: true });
|
|
859
|
+
state.pendingShells.push(pending);
|
|
860
|
+
sendRunPiShellViaServer(options.context, options.runId, `${excludeFromContext ? "!!" : "!"}${command}`).catch((error) => {
|
|
861
|
+
cleanup();
|
|
862
|
+
failPendingShell(state, pending, error instanceof Error ? error : new Error(String(error)));
|
|
863
|
+
});
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
}
|
|
763
868
|
async function answerPendingUi(options, state, line) {
|
|
764
869
|
const pending = state.pendingUi;
|
|
765
870
|
if (!pending)
|
|
@@ -825,13 +930,22 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
825
930
|
commands: [],
|
|
826
931
|
streaming: false,
|
|
827
932
|
pendingUi: null,
|
|
828
|
-
|
|
933
|
+
pendingShells: [],
|
|
934
|
+
wsConnected: false,
|
|
935
|
+
nativeStream: false
|
|
829
936
|
};
|
|
830
937
|
if (options.initialMessageSent)
|
|
831
938
|
appendTranscript(state, "System", "Initial message sent to worker Pi daemon.");
|
|
939
|
+
let nativePiUiContextAvailable = false;
|
|
940
|
+
pi.on("user_bash", (event) => {
|
|
941
|
+
state.nativeStream = Boolean(state.nativeStream || nativePiUiContextAvailable);
|
|
942
|
+
return { operations: createRemoteBashOperations(options, state, event.excludeFromContext === true) };
|
|
943
|
+
});
|
|
832
944
|
pi.on("session_start", async (_event, ctx) => {
|
|
945
|
+
nativePiUiContextAvailable = Boolean(nativePiUi(ctx));
|
|
946
|
+
state.nativeStream = nativePiUiContextAvailable;
|
|
833
947
|
updatePiUi(ctx, state);
|
|
834
|
-
ctx.ui.notify("Rig worker Pi bridge extension loaded", "info");
|
|
948
|
+
ctx.ui.notify(nativePiUiContextAvailable ? "Rig worker Pi native stream bridge loaded" : "Rig worker Pi bridge extension loaded (degraded widget fallback)", "info");
|
|
835
949
|
ctx.ui.onTerminalInput((data) => {
|
|
836
950
|
if (data.includes("\x04")) {
|
|
837
951
|
ctx.shutdown();
|
|
@@ -845,6 +959,8 @@ function createRigWorkerPiBridgeExtension(options) {
|
|
|
845
959
|
const text = [editorText, inlineText].filter(Boolean).join(" ").trim();
|
|
846
960
|
if (!text)
|
|
847
961
|
return;
|
|
962
|
+
if (text.startsWith("!"))
|
|
963
|
+
return;
|
|
848
964
|
ctx.ui.setEditorText("");
|
|
849
965
|
routeInput(options, ctx, state, text).catch((error) => {
|
|
850
966
|
appendTranscript(state, "Error", error instanceof Error ? error.message : String(error));
|