@agentuity/coder-tui 2.0.9 → 2.0.11
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/commands.d.ts +1 -1
- package/dist/commands.js +3 -3
- package/dist/commands.js.map +1 -1
- package/dist/hub-overlay.js +2 -2
- package/dist/hub-overlay.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +2 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/remote-runtime.d.ts +16 -0
- package/dist/remote-runtime.d.ts.map +1 -0
- package/dist/remote-runtime.js +18 -0
- package/dist/remote-runtime.js.map +1 -0
- package/dist/remote-tui.d.ts +4 -4
- package/dist/remote-tui.d.ts.map +1 -1
- package/dist/remote-tui.js +72 -27
- package/dist/remote-tui.js.map +1 -1
- package/package.json +4 -4
- package/src/commands.ts +4 -4
- package/src/hub-overlay.ts +2 -2
- package/src/index.ts +2 -4
- package/src/protocol.ts +2 -0
- package/src/remote-runtime.ts +45 -0
- package/src/remote-tui.ts +92 -32
package/src/remote-tui.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Remote TUI — Native Pi Coding Agent Renderer for Remote Sessions
|
|
3
3
|
*
|
|
4
|
-
* Creates a real
|
|
4
|
+
* Creates a real AgentSessionRuntime + InteractiveMode backed by a remote sandbox
|
|
5
5
|
* via Hub WebSocket, with the coder extension loaded for Hub UI (footer,
|
|
6
6
|
* /hub overlay, commands, titlebar).
|
|
7
7
|
*
|
|
8
8
|
* Architecture:
|
|
9
9
|
* Remote TUI → Hub WebSocket (controller) → Sandbox (Pi RPC mode)
|
|
10
10
|
* - User input → agent.prompt() (monkey-patched) → RPC `prompt` → Hub → sandbox
|
|
11
|
-
* - Sandbox Pi → AgentEvent stream → Hub broadcast →
|
|
11
|
+
* - Sandbox Pi → AgentEvent stream → Hub broadcast → AgentSession event handling → InteractiveMode renders natively
|
|
12
12
|
* - Hub UI → coder extension (loaded via DefaultResourceLoader) provides footer, /hub, commands
|
|
13
13
|
*
|
|
14
14
|
* The local Agent never calls an LLM. Its prompt/steer/abort are monkey-patched
|
|
@@ -22,14 +22,16 @@
|
|
|
22
22
|
*
|
|
23
23
|
* IMPORTANT: Initialization order matters!
|
|
24
24
|
* 1. Create RemoteSession (no connection yet)
|
|
25
|
-
* 2. Create
|
|
25
|
+
* 2. Create AgentSessionRuntime, patch Agent/Session methods
|
|
26
26
|
* 3. Register ALL event handlers on RemoteSession
|
|
27
27
|
* 4. THEN connect — so hydration + replay events are captured
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import {
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
createAgentSessionFromServices,
|
|
32
|
+
createAgentSessionRuntime,
|
|
33
|
+
createAgentSessionServices,
|
|
34
|
+
getAgentDir,
|
|
33
35
|
InteractiveMode,
|
|
34
36
|
SessionManager,
|
|
35
37
|
} from '@mariozechner/pi-coding-agent';
|
|
@@ -49,6 +51,11 @@ import { RemoteSession } from './remote-session.ts';
|
|
|
49
51
|
import type { RpcEvent } from './remote-session.ts';
|
|
50
52
|
import { agentuityCoderHub } from './index.ts';
|
|
51
53
|
import { handleRemoteUiRequest, REMOTE_FIRE_AND_FORGET_UI_METHODS } from './remote-ui-handler.ts';
|
|
54
|
+
import {
|
|
55
|
+
dispatchRemoteSessionEvent,
|
|
56
|
+
installRemoteRuntimeOperationGuards,
|
|
57
|
+
REMOTE_RUNTIME_OPERATION_MESSAGE,
|
|
58
|
+
} from './remote-runtime.ts';
|
|
52
59
|
|
|
53
60
|
const DEBUG = !!process.env['AGENTUITY_DEBUG'];
|
|
54
61
|
|
|
@@ -60,7 +67,7 @@ function log(msg: string): void {
|
|
|
60
67
|
* Run the native Pi TUI connected to a remote sandbox session.
|
|
61
68
|
*
|
|
62
69
|
* This is the entry point for `agentuity coder start --remote <sessionId>`.
|
|
63
|
-
* Creates an
|
|
70
|
+
* Creates an AgentSessionRuntime with the coder extension loaded (Hub UI), then
|
|
64
71
|
* monkey-patches the Agent for remote-backed execution.
|
|
65
72
|
*/
|
|
66
73
|
export async function runRemoteTui(options: {
|
|
@@ -90,33 +97,74 @@ export async function runRemoteTui(options: {
|
|
|
90
97
|
let hydrationStreamingDetected = false;
|
|
91
98
|
let sessionResumeSeen = false;
|
|
92
99
|
|
|
93
|
-
// ── 2. Create
|
|
100
|
+
// ── 2. Create AgentSessionRuntime with coder extension loaded ──
|
|
94
101
|
// The extension provides Hub UI (footer, /hub overlay, commands, titlebar).
|
|
95
102
|
// AGENTUITY_CODER_NATIVE_REMOTE=1 tells it to skip legacy event rendering.
|
|
96
103
|
const cwd = process.cwd();
|
|
97
|
-
const
|
|
98
|
-
cwd,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
const runtime = await createAgentSessionRuntime(
|
|
105
|
+
async ({ cwd, agentDir, sessionManager, sessionStartEvent }) => {
|
|
106
|
+
const services = await createAgentSessionServices({
|
|
107
|
+
cwd,
|
|
108
|
+
agentDir,
|
|
109
|
+
resourceLoaderOptions: {
|
|
110
|
+
noExtensions: true, // Skip file-system extension discovery
|
|
111
|
+
extensionFactories: [agentuityCoderHub], // Load coder extension directly
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
...(await createAgentSessionFromServices({
|
|
117
|
+
services,
|
|
118
|
+
sessionManager,
|
|
119
|
+
sessionStartEvent,
|
|
120
|
+
tools: [], // No local tools — sandbox has all the tools
|
|
121
|
+
})),
|
|
122
|
+
services,
|
|
123
|
+
diagnostics: services.diagnostics,
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
cwd,
|
|
128
|
+
agentDir: getAgentDir(),
|
|
129
|
+
sessionManager: SessionManager.inMemory(),
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
const session = runtime.session;
|
|
133
|
+
const agent: any = session.agent;
|
|
134
|
+
let runtimeDisposed = false;
|
|
135
|
+
log('AgentSessionRuntime created');
|
|
103
136
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
137
|
+
function notifyBlockedRuntimeOperation(operation: string): void {
|
|
138
|
+
const message = `${REMOTE_RUNTIME_OPERATION_MESSAGE} (${operation})`;
|
|
139
|
+
const ctx = getNativeRemoteExtensionContext();
|
|
140
|
+
if (ctx?.hasUI) {
|
|
141
|
+
ctx.ui.notify(message, 'warning');
|
|
142
|
+
ctx.ui.setStatus('remote_runtime', operation);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
log(message);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
installRemoteRuntimeOperationGuards(runtime as any, (operation) =>
|
|
149
|
+
notifyBlockedRuntimeOperation(operation)
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
async function disposeRuntime(): Promise<void> {
|
|
153
|
+
if (runtimeDisposed) return;
|
|
154
|
+
runtimeDisposed = true;
|
|
155
|
+
await runtime.dispose();
|
|
156
|
+
}
|
|
110
157
|
|
|
111
158
|
// NOTE: Do NOT call session.bindExtensions() here.
|
|
112
159
|
// InteractiveMode.initExtensions() calls it with the proper uiContext.
|
|
113
160
|
// Calling it early fires session_start twice, duplicating extension init.
|
|
114
|
-
|
|
115
|
-
// Access the Agent instance (typed as `any` for monkey-patching)
|
|
116
|
-
const agent: any = session.agent;
|
|
117
161
|
let lifecycleState = remote.getLifecycleState();
|
|
118
162
|
let lifecycleOwnsWorkingMessage = false;
|
|
119
163
|
|
|
164
|
+
function emitSessionEvent(event: RpcEvent): void {
|
|
165
|
+
dispatchRemoteSessionEvent(session as any, event);
|
|
166
|
+
}
|
|
167
|
+
|
|
120
168
|
function applyLifecycleUi(state: RemoteLifecycleState): void {
|
|
121
169
|
const ctx = getNativeRemoteExtensionContext();
|
|
122
170
|
if (!ctx?.hasUI) return;
|
|
@@ -174,7 +222,7 @@ export async function runRemoteTui(options: {
|
|
|
174
222
|
|
|
175
223
|
// Emit synthetic agent_start so InteractiveMode shows "working" immediately
|
|
176
224
|
syntheticAgentStartEmitted = true;
|
|
177
|
-
|
|
225
|
+
emitSessionEvent({ type: 'agent_start', agentName: 'lead', timestamp: Date.now() } as any);
|
|
178
226
|
|
|
179
227
|
// Send RPC command to sandbox
|
|
180
228
|
remote.prompt(text);
|
|
@@ -268,7 +316,7 @@ export async function runRemoteTui(options: {
|
|
|
268
316
|
//
|
|
269
317
|
// Events that arrive before InteractiveMode is initialized are buffered
|
|
270
318
|
// and flushed after init (InteractiveMode registers listeners during init,
|
|
271
|
-
// so
|
|
319
|
+
// so session event dispatch before that fires into the void).
|
|
272
320
|
let interactiveModeReady = false;
|
|
273
321
|
let eventBuffer: RpcEvent[] = [];
|
|
274
322
|
let seenMessageStart = false;
|
|
@@ -315,7 +363,7 @@ export async function runRemoteTui(options: {
|
|
|
315
363
|
}
|
|
316
364
|
|
|
317
365
|
for (const event of syntheticEvents) {
|
|
318
|
-
|
|
366
|
+
emitSessionEvent(event);
|
|
319
367
|
}
|
|
320
368
|
}
|
|
321
369
|
|
|
@@ -507,10 +555,14 @@ export async function runRemoteTui(options: {
|
|
|
507
555
|
) {
|
|
508
556
|
log(`Live ${rpcEvent.type} without prior message_start — injecting synthetics`);
|
|
509
557
|
if (!hadSeenAgentStart) {
|
|
510
|
-
|
|
558
|
+
emitSessionEvent({
|
|
559
|
+
type: 'agent_start',
|
|
560
|
+
agentName: 'lead',
|
|
561
|
+
timestamp: Date.now(),
|
|
562
|
+
} as any);
|
|
511
563
|
seenAgentStart = true;
|
|
512
564
|
}
|
|
513
|
-
|
|
565
|
+
emitSessionEvent({
|
|
514
566
|
type: 'message_start',
|
|
515
567
|
message: {
|
|
516
568
|
role: 'assistant',
|
|
@@ -535,7 +587,7 @@ export async function runRemoteTui(options: {
|
|
|
535
587
|
}
|
|
536
588
|
|
|
537
589
|
// Emit to subscribers — InteractiveMode.handleEvent processes this
|
|
538
|
-
|
|
590
|
+
emitSessionEvent(rpcEvent);
|
|
539
591
|
if (injectedSyntheticMessageStart && rpcEvent.type === 'message_end') {
|
|
540
592
|
seenMessageStart = false;
|
|
541
593
|
seenAgentStart = hadSeenAgentStart;
|
|
@@ -787,7 +839,7 @@ export async function runRemoteTui(options: {
|
|
|
787
839
|
|
|
788
840
|
// ── 9. Start InteractiveMode — full native Pi TUI ──
|
|
789
841
|
log('Creating InteractiveMode');
|
|
790
|
-
const interactive = new InteractiveMode(
|
|
842
|
+
const interactive = new InteractiveMode(runtime);
|
|
791
843
|
log('InteractiveMode created, calling init...');
|
|
792
844
|
await interactive.init();
|
|
793
845
|
|
|
@@ -803,8 +855,8 @@ export async function runRemoteTui(options: {
|
|
|
803
855
|
// the streaming indicator right away, before any buffered events flush.
|
|
804
856
|
// This prevents the blank screen gap between connect and first event.
|
|
805
857
|
log('Hydration detected streaming — emitting immediate synthetics');
|
|
806
|
-
|
|
807
|
-
|
|
858
|
+
emitSessionEvent({ type: 'agent_start', agentName: 'lead', timestamp: Date.now() } as any);
|
|
859
|
+
emitSessionEvent({
|
|
808
860
|
type: 'message_start',
|
|
809
861
|
message: {
|
|
810
862
|
role: 'assistant',
|
|
@@ -836,7 +888,7 @@ export async function runRemoteTui(options: {
|
|
|
836
888
|
if (eventBuffer.length > 0) {
|
|
837
889
|
log(`Flushing ${eventBuffer.length} events: ${eventBuffer.map((e) => e.type).join(', ')}`);
|
|
838
890
|
for (const buffered of eventBuffer) {
|
|
839
|
-
|
|
891
|
+
emitSessionEvent(buffered);
|
|
840
892
|
if (buffered.type === 'agent_end') {
|
|
841
893
|
resolveRunningPrompt();
|
|
842
894
|
}
|
|
@@ -851,6 +903,11 @@ export async function runRemoteTui(options: {
|
|
|
851
903
|
remote.close();
|
|
852
904
|
setNativeRemoteExtensionContext(null);
|
|
853
905
|
interactive.stop();
|
|
906
|
+
void disposeRuntime().catch((err) => {
|
|
907
|
+
log(
|
|
908
|
+
`Runtime dispose error during cleanup: ${err instanceof Error ? err.message : String(err)}`
|
|
909
|
+
);
|
|
910
|
+
});
|
|
854
911
|
};
|
|
855
912
|
process.on('SIGINT', cleanup);
|
|
856
913
|
process.on('SIGTERM', cleanup);
|
|
@@ -863,6 +920,9 @@ export async function runRemoteTui(options: {
|
|
|
863
920
|
} finally {
|
|
864
921
|
remote.close();
|
|
865
922
|
setNativeRemoteExtensionContext(null);
|
|
923
|
+
await disposeRuntime().catch((err) => {
|
|
924
|
+
log(`Runtime dispose error: ${err instanceof Error ? err.message : String(err)}`);
|
|
925
|
+
});
|
|
866
926
|
log('Remote TUI exited');
|
|
867
927
|
}
|
|
868
928
|
|