@euqns/nudge-mcp 1.36.0 → 1.38.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/README.md CHANGED
@@ -680,6 +680,56 @@ Overrides are read at each job startup; already-running turns retain their
680
680
  existing settings. Do not repeat generated top-level model/auth keys in the
681
681
  TOML overrides. Desktop MCP servers remain isolated unless explicitly enabled.
682
682
 
683
+ ### T3 Code desktop runner threads
684
+
685
+ When T3 Code is running on the runner's machine, new **Codex** jobs on macOS
686
+ and Linux execute through T3 itself. Look for **Nudge · running · Board · task …**
687
+ under the matching checkout's project. Open the thread to see the prompt,
688
+ streaming responses, and tool activity in the conversation pane. Nudge receives
689
+ the final answer and token usage, and updates the title with the job status.
690
+
691
+ Nudge resumes and steers these jobs in the same T3 conversation. Cancelling or
692
+ steering a job in Nudge interrupts its T3 turn. Keep using Nudge for job questions,
693
+ review, and scheduling; T3's composer also operates the underlying conversation,
694
+ but does not create or reopen a Nudge job. Completed conversations remain
695
+ available for manual follow-ups in T3.
696
+
697
+ The Codex bridge preserves the runner's Nudge MCP tools and sandbox: intake and
698
+ question turns stay read-only, ordinary tasks use workspace-write, and jobs that
699
+ explicitly need repository/network actions retain their existing access. T3 gets
700
+ a separate Codex provider instance per conversation using Nudge's isolated Codex
701
+ home. Job credentials live in an owner-only context under `~/.nudge/t3-native`,
702
+ are removed when execution ends, and are never put in T3 titles or prompts.
703
+
704
+ Claude, Cursor, Windows, and conversations originally started by an external CLI
705
+ continue using the terminal log view. Their previous conversation IDs are kept
706
+ with the original engine. Existing log-only threads and jobs already running
707
+ are not converted or backfilled.
708
+
709
+ Discovery reads `~/.t3/userdata/server-runtime.json` (or the `dev` variant) and
710
+ only connects over loopback. On macOS it finds T3 Code or T3 Code (Nightly) in
711
+ `/Applications` or `~/Applications`; other installs need a `t3` executable on
712
+ the runner's `PATH`. The installed CLI issues an expiring local session and
713
+ revokes it afterward (24 hours maximum for native execution, five minutes for
714
+ status updates). T3 API credentials are held in memory. Windows gets the fallback
715
+ thread and status updates, with its log saved locally.
716
+
717
+ | Runner environment variable | Purpose |
718
+ | --- | --- |
719
+ | `NUDGE_T3=0` | Disable automatic T3 threads. |
720
+ | `T3CODE_HOME=/path/to/.t3` | Find a nonstandard T3 data directory. |
721
+ | `NUDGE_T3_CLI=/path/to/t3` | Use a specific installed T3 CLI or executable wrapper. |
722
+
723
+ T3 must be open before the job starts. Missing T3 is silently skipped for new
724
+ jobs. A native conversation must resume in its original T3 environment; if that
725
+ environment is unavailable, the job reports an error. Once a native turn has
726
+ been submitted, the runner never falls back to a second CLI agent. Verified
727
+ against T3 Code Nightly `0.0.41-nightly.20260908.1400`; native execution requires
728
+ its HTTP orchestration and provider settings RPC interfaces. Fallback JSONL
729
+ logs are owner-only under `~/.nudge/t3-logs` and
730
+ retain up to 8 MiB of live output per run, followed by its final result.
731
+ They remain available after completion and can be removed when no longer needed.
732
+
683
733
  ### Runner output files
684
734
 
685
735
  A coding turn can optionally write the JSON manifest at the absolute path supplied
@@ -0,0 +1,101 @@
1
+ // T3 owns the app-server connection. This transparent stdio bridge applies the
2
+ // runner's MCP identity and sandbox to that connection, preserving native events.
3
+ import { spawn } from "node:child_process";
4
+ import { readFile, writeFile } from "node:fs/promises";
5
+ import { createInterface } from "node:readline";
6
+ import { fileURLToPath } from "node:url";
7
+ import { bundledCodexCommand, companionCodexConfig, subscriptionEnvironment } from "./codex-companion.js";
8
+ import { addUsage, emptyUsage } from "./engines/types.js";
9
+ export function applyRunnerPolicy(message, context) {
10
+ if (!["thread/start", "thread/resume", "thread/fork", "turn/start"].includes(message.method))
11
+ return message;
12
+ const params = { ...message.params, approvalPolicy: "never", approvalsReviewer: "user" };
13
+ if (message.method === "turn/start") {
14
+ params.sandboxPolicy = { type: context.sandbox === "read-only" ? "readOnly"
15
+ : context.sandbox === "workspace-write" ? "workspaceWrite" : "dangerFullAccess" };
16
+ }
17
+ else {
18
+ const config = companionCodexConfig(context.env);
19
+ params.sandbox = context.sandbox;
20
+ params.config = { ...params.config, model_provider: config.model_provider, "mcp_servers.nudge": config.mcp_servers.nudge };
21
+ }
22
+ return { ...message, params };
23
+ }
24
+ export function usageDelta(previous, current, last) {
25
+ const keys = ["inputTokens", "outputTokens", "cachedInputTokens", "reasoningTokens"];
26
+ if (!previous || keys.some((key) => (current[key] ?? 0) < (previous[key] ?? 0)))
27
+ return last;
28
+ return Object.fromEntries(keys.map((key) => [key, (current[key] ?? 0) - (previous[key] ?? 0)]));
29
+ }
30
+ export async function runBridge() {
31
+ let context;
32
+ if (process.env.NUDGE_T3_CONTEXT) {
33
+ try {
34
+ context = JSON.parse(await readFile(process.env.NUDGE_T3_CONTEXT, "utf8"));
35
+ }
36
+ catch (error) {
37
+ // A completed job removes its identity. Manual T3 follow-ups still work,
38
+ // using the normal T3 policy and no credentials from the finished job.
39
+ if (error.code !== "ENOENT")
40
+ throw new Error("Cannot read Nudge T3 job context");
41
+ }
42
+ }
43
+ const invocation = bundledCodexCommand(...process.argv.slice(2));
44
+ const child = spawn(invocation.command, invocation.args, {
45
+ env: subscriptionEnvironment(process.env), stdio: ["pipe", "pipe", "inherit"],
46
+ });
47
+ const input = createInterface({ input: process.stdin });
48
+ input.on("line", (line) => {
49
+ try {
50
+ child.stdin.write(context ? JSON.stringify(applyRunnerPolicy(JSON.parse(line), context)) + "\n" : line + "\n");
51
+ }
52
+ catch {
53
+ child.kill();
54
+ process.exitCode = 1;
55
+ }
56
+ });
57
+ input.on("close", () => child.stdin.end());
58
+ child.stdin.on("error", () => undefined);
59
+ // Keep complete native notifications, including tool calls and assistant deltas.
60
+ // Record usage separately because the public thread read model omits it.
61
+ const output = createInterface({ input: child.stdout });
62
+ let usageWrite = Promise.resolve();
63
+ const baselineByThread = new Map();
64
+ let runUsage = emptyUsage();
65
+ output.on("line", (line) => {
66
+ process.stdout.write(line + "\n");
67
+ if (!context)
68
+ return;
69
+ try {
70
+ const event = JSON.parse(line);
71
+ if (event.method !== "thread/tokenUsage/updated")
72
+ return;
73
+ const last = event.params?.tokenUsage?.last;
74
+ if (!last || typeof event.params.turnId !== "string")
75
+ return;
76
+ const total = event.params.tokenUsage.total;
77
+ const normalize = (value) => ({
78
+ inputTokens: value.inputTokens ?? 0, outputTokens: value.outputTokens ?? 0,
79
+ cachedInputTokens: value.cachedInputTokens ?? 0, reasoningTokens: value.reasoningOutputTokens ?? 0,
80
+ });
81
+ const current = normalize(total);
82
+ runUsage = addUsage(runUsage, usageDelta(baselineByThread.get(event.params.threadId), current, normalize(last)));
83
+ baselineByThread.set(event.params.threadId, current);
84
+ const target = context.usagePath;
85
+ const serialized = JSON.stringify(runUsage);
86
+ usageWrite = usageWrite.then(() => writeFile(target, serialized, { mode: 0o600 })).catch(() => undefined);
87
+ }
88
+ catch { /* The provider stream is forwarded unchanged. */ }
89
+ });
90
+ for (const signal of ["SIGINT", "SIGTERM"])
91
+ process.on(signal, () => child.kill(signal));
92
+ await new Promise((resolve, reject) => {
93
+ child.once("error", () => reject(new Error("Could not start Codex for T3")));
94
+ child.once("close", (code) => { process.exitCode = code ?? 1; input.close(); resolve(); });
95
+ });
96
+ await usageWrite;
97
+ }
98
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
99
+ runBridge().catch(() => { process.stderr.write("Nudge T3 Codex bridge failed\n"); process.exitCode = 1; });
100
+ }
101
+ //# sourceMappingURL=runner-t3-codex-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-t3-codex-bridge.js","sourceRoot":"","sources":["../src/runner-t3-codex-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,243 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
+ import { createRequire } from "node:module";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { setTimeout as delay } from "node:timers/promises";
7
+ import { fileURLToPath } from "node:url";
8
+ import { acquireInstallationLock } from "./runner-installation.js";
9
+ import { codexSandboxMode } from "./engines/codex.js";
10
+ import { ensureCodexShadowHome } from "./engines/isolation.js";
11
+ import { estimateCostUsd } from "./engines/pricing.js";
12
+ import { emptyUsage } from "./engines/types.js";
13
+ import { discoverT3, ensureT3JobThread, shellQuote, t3JobThreadId, t3JobTitle, withT3Client } from "./runner-t3.js";
14
+ const DIRECTORY = path.join(os.homedir(), ".nudge", "t3-native");
15
+ const PREFIX = "t3:";
16
+ export function t3ResumeId(environmentId, threadId) {
17
+ return `${PREFIX}${encodeURIComponent(environmentId)}:${encodeURIComponent(threadId)}`;
18
+ }
19
+ export function parseT3ResumeId(value) {
20
+ if (!value?.startsWith(PREFIX))
21
+ return null;
22
+ const parts = value.split(":");
23
+ if (parts.length !== 3 || !parts[1] || !parts[2])
24
+ throw new Error("Invalid T3 conversation reference");
25
+ const threadId = decodeURIComponent(parts[2]);
26
+ if (!/^nudge-[a-f0-9]{32}$/.test(threadId))
27
+ throw new Error("Invalid T3 runner thread ID");
28
+ return { environmentId: decodeURIComponent(parts[1]), threadId };
29
+ }
30
+ // server.updateSettings replaces the provider map. Serialize Nudge's read/merge/
31
+ // write across runner processes and preserve every unrelated (redacted) entry.
32
+ async function settingsLock(use) {
33
+ await mkdir(DIRECTORY, { recursive: true, mode: 0o700 });
34
+ const release = await acquireInstallationLock(path.join(DIRECTORY, "settings"), { timeoutMs: 15_000, retryMs: 100 });
35
+ try {
36
+ return await use();
37
+ }
38
+ finally {
39
+ await release();
40
+ }
41
+ }
42
+ async function configureProvider(client, threadId, contextPath) {
43
+ const home = await ensureCodexShadowHome();
44
+ let bridge = fileURLToPath(new URL("./runner-t3-codex-bridge.js", import.meta.url));
45
+ const args = [];
46
+ try {
47
+ await access(bridge);
48
+ }
49
+ catch {
50
+ bridge = fileURLToPath(new URL("./runner-t3-codex-bridge.ts", import.meta.url));
51
+ args.push("--import", createRequire(import.meta.url).resolve("tsx"));
52
+ }
53
+ const executable = path.join(DIRECTORY, "codex");
54
+ await writeFile(executable, `#!/bin/sh\nexec ${[process.execPath, ...args, bridge].map(shellQuote).join(" ")} "$@"\n`, { mode: 0o700 });
55
+ const instanceId = `nudge-${threadId}`;
56
+ await settingsLock(async () => {
57
+ const settings = await client.rpc("server.getSettings");
58
+ const instance = {
59
+ driver: "codex", displayName: "Nudge runner", enabled: true,
60
+ environment: [{ name: "NUDGE_T3_CONTEXT", value: contextPath, sensitive: false }],
61
+ config: { binaryPath: executable, homePath: home.path, shadowHomePath: "", launchArgs: "", customModels: [], enabled: true },
62
+ };
63
+ if (JSON.stringify(settings.providerInstances?.[instanceId]) !== JSON.stringify(instance)) {
64
+ await client.rpc("server.updateSettings", { patch: { providerInstances: { ...settings.providerInstances, [instanceId]: instance } } });
65
+ }
66
+ });
67
+ return instanceId;
68
+ }
69
+ export async function runT3Turn(client, threadId, modelSelection, options, pollMs = 1_000) {
70
+ const route = `/api/orchestration/threads/${encodeURIComponent(threadId)}`;
71
+ const detail = async () => (await client.request(route)).thread;
72
+ const before = await detail();
73
+ const previousTurnId = before.latestTurn?.turnId;
74
+ const commandId = randomUUID();
75
+ const messageId = randomUUID();
76
+ const createdAt = new Date().toISOString();
77
+ let dispatched = false;
78
+ let turnId;
79
+ const seenMessages = new Map();
80
+ const seenActivities = new Set((before.activities ?? []).map((a) => a.id));
81
+ let lastContact = Date.now();
82
+ let dispatchError;
83
+ try {
84
+ options.signal.throwIfAborted();
85
+ if (before.latestTurn?.state === "running") {
86
+ // Recover an in-flight turn after a runner restart. Never launch another
87
+ // agent alongside work already owned by this job's native thread.
88
+ turnId = previousTurnId;
89
+ dispatched = true;
90
+ options.log("event", "Reattached to the running T3 Code turn.");
91
+ }
92
+ else {
93
+ dispatched = true; // A timed-out POST may already have committed.
94
+ await client.dispatch({
95
+ type: "thread.turn.start", commandId, threadId,
96
+ message: { messageId, role: "user", text: options.prompt, attachments: [] },
97
+ modelSelection, runtimeMode: runtimeMode(options), interactionMode: "default", createdAt,
98
+ }).catch((error) => { dispatchError = error; });
99
+ }
100
+ for (;;) {
101
+ options.signal.throwIfAborted();
102
+ let thread;
103
+ try {
104
+ thread = await detail();
105
+ lastContact = Date.now();
106
+ }
107
+ catch {
108
+ if (Date.now() - lastContact > 30_000)
109
+ throw new Error("Lost connection to T3 Code. The conversation remains in T3; no CLI fallback was started.");
110
+ await delay(pollMs, undefined, { signal: options.signal });
111
+ continue;
112
+ }
113
+ const turn = thread.latestTurn;
114
+ if (!turnId && turn && turn.turnId !== previousTurnId)
115
+ turnId = turn.turnId;
116
+ // The public projection may take a moment to reflect accepted commands.
117
+ if (!turnId) {
118
+ if (thread.session?.status === "error" && thread.session.updatedAt >= createdAt)
119
+ throw new Error(thread.session.lastError || "T3 could not start the provider");
120
+ if (Date.now() - Date.parse(createdAt) > 30_000)
121
+ throw new Error(dispatchError
122
+ ? "T3 did not confirm the turn start. Inspect the thread before retrying."
123
+ : "T3 did not start the provider within 30 seconds");
124
+ }
125
+ else {
126
+ if (turn && turn.turnId !== turnId)
127
+ throw new Error("The T3 conversation advanced to another turn. Inspect its result before retrying the job.");
128
+ for (const activity of thread.activities ?? []) {
129
+ if (seenActivities.has(activity.id))
130
+ continue;
131
+ seenActivities.add(activity.id);
132
+ if (activity.summary)
133
+ options.log("event", activity.summary);
134
+ }
135
+ for (const message of thread.messages ?? []) {
136
+ if (message.role !== "assistant" || message.turnId !== turnId || !message.text)
137
+ continue;
138
+ const previous = seenMessages.get(message.id) ?? "";
139
+ if (message.text !== previous) {
140
+ options.log("stdout", message.text.startsWith(previous) ? message.text.slice(previous.length) : message.text);
141
+ seenMessages.set(message.id, message.text);
142
+ }
143
+ }
144
+ if (turn?.turnId === turnId && turn.state !== "running") {
145
+ const messages = (thread.messages ?? []).filter((m) => m.role === "assistant" && m.turnId === turnId);
146
+ const summary = messages.find((m) => m.id === turn.assistantMessageId)?.text ?? messages.at(-1)?.text ?? "";
147
+ return {
148
+ summary, usage: emptyUsage(), costBasis: "subscription", resolvedModel: options.model ?? undefined,
149
+ ...(turn.state !== "completed" ? { error: thread.session?.lastError || `T3 turn ${turn.state}` } : {}),
150
+ };
151
+ }
152
+ }
153
+ await delay(pollMs, undefined, { signal: options.signal });
154
+ }
155
+ }
156
+ finally {
157
+ if (dispatched) {
158
+ // Also stop a completed session: the next resume starts with fresh job
159
+ // credentials and cannot retain a stale runner token or sandbox policy.
160
+ if (options.signal.aborted || dispatchError) {
161
+ await client.dispatch({ type: "thread.turn.interrupt", threadId, ...(turnId ? { turnId } : {}), createdAt: new Date().toISOString() }).catch(() => undefined);
162
+ }
163
+ await client.dispatch({ type: "thread.session.stop", threadId, createdAt: new Date().toISOString() })
164
+ .catch(() => options.log("event", "Could not stop the T3 session. Check its thread before starting another run."));
165
+ }
166
+ }
167
+ }
168
+ function runtimeMode(options) {
169
+ return options.readOnly ? "approval-required" : codexSandboxMode(options) === "danger-full-access" ? "full-access" : "auto";
170
+ }
171
+ export async function createT3Engine(fallback, job, previousThreadId, warn) {
172
+ const resumed = parseT3ResumeId(previousThreadId);
173
+ if (resumed && (fallback.id !== "codex" || process.platform === "win32")) {
174
+ throw new Error("This T3 conversation requires the Codex runner on macOS or Linux");
175
+ }
176
+ // Raw CLI session IDs are not T3 IDs. Preserve their history via the original
177
+ // engine; newly started native conversations resume through T3 from then on.
178
+ if (!resumed && (fallback.id !== "codex" || previousThreadId || process.platform === "win32"))
179
+ return null;
180
+ let connection;
181
+ try {
182
+ connection = await discoverT3();
183
+ }
184
+ catch (error) {
185
+ if (resumed)
186
+ throw error;
187
+ warn("T3 Code is unavailable; using the local runner.");
188
+ return null;
189
+ }
190
+ if (!connection) {
191
+ if (resumed)
192
+ throw new Error("Open the local T3 Code app to resume this job's conversation");
193
+ return null;
194
+ }
195
+ if (resumed && resumed.environmentId !== connection.environmentId)
196
+ throw new Error("This job belongs to a different T3 Code environment");
197
+ const threadId = resumed?.threadId ?? t3JobThreadId(job.deployment, job.id);
198
+ const reference = t3ResumeId(connection.environmentId, threadId);
199
+ const view = {
200
+ threadId, logPath: "", log() { },
201
+ async finish(status) {
202
+ await withT3Client(connection, (client) => client.dispatch({ type: "thread.meta.update", threadId, title: t3JobTitle(job, status) }))
203
+ .catch(() => warn("T3 thread status could not be updated."));
204
+ },
205
+ };
206
+ const engine = {
207
+ id: fallback.id, displayName: `${fallback.displayName} in T3 Code`, detect: () => fallback.detect(),
208
+ execute: (options) => withT3Client(connection, async (client) => {
209
+ options.signal.throwIfAborted();
210
+ await mkdir(DIRECTORY, { recursive: true, mode: 0o700 });
211
+ // Only opaque paths go into T3 settings. Runner credentials remain in an
212
+ // owner-only temporary context and never appear in titles or prompts.
213
+ const contextPath = path.join(DIRECTORY, `${threadId}.json`);
214
+ const usagePath = path.join(DIRECTORY, `${threadId}.usage.json`);
215
+ if (resumed)
216
+ await client.request(`/api/orchestration/threads/${encodeURIComponent(threadId)}`);
217
+ const instanceId = await configureProvider(client, threadId, contextPath);
218
+ const modelSelection = { instanceId, model: options.model ?? job.model,
219
+ ...(options.effort ? { options: [{ id: "reasoningEffort", value: options.effort }] } : {}) };
220
+ await ensureT3JobThread(client, job, { threadId, modelSelection, runtimeMode: runtimeMode(options) });
221
+ options.onThreadId?.(reference);
222
+ options.log("event", "Running in T3 Code. Open the Nudge thread to see messages and tool activity.");
223
+ await writeFile(contextPath, JSON.stringify({ env: options.env, sandbox: codexSandboxMode(options), usagePath }), { mode: 0o600 });
224
+ await rm(usagePath, { force: true });
225
+ try {
226
+ const result = await runT3Turn(client, threadId, modelSelection, options);
227
+ let usage = emptyUsage();
228
+ try {
229
+ usage = JSON.parse(await readFile(usagePath, "utf8"));
230
+ }
231
+ catch { /* Provider may fail before reporting usage. */ }
232
+ const costUsd = estimateCostUsd(options.model ?? job.model, usage);
233
+ return { ...result, threadId: reference, usage, ...(costUsd != null ? { costUsd, costBasis: "estimated" } : {}) };
234
+ }
235
+ finally {
236
+ await rm(contextPath, { force: true });
237
+ await rm(usagePath, { force: true });
238
+ }
239
+ }, "24h"),
240
+ };
241
+ return { engine, view };
242
+ }
243
+ //# sourceMappingURL=runner-t3-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-t3-engine.js","sourceRoot":"","sources":["../src/runner-t3-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { applyRunnerPolicy, usageDelta } from "./runner-t3-codex-bridge.js";
4
+ import { createT3Engine, parseT3ResumeId, runT3Turn, t3ResumeId } from "./runner-t3-engine.js";
5
+ import { T3Client, t3JobThreadId } from "./runner-t3.js";
6
+ const options = () => ({ cwd: "/tmp", prompt: "New work", readOnly: true, signal: new AbortController().signal, log() { } });
7
+ const context = { env: { NUDGE_RUNNER_JOB: "job-a", NUDGE_RUNNER_TOKEN: "private-token" }, sandbox: "read-only", usagePath: "/tmp/unused" };
8
+ class FakeT3 extends T3Client {
9
+ commands = [];
10
+ snapshots;
11
+ commitThenTimeout = false;
12
+ afterRead;
13
+ constructor(snapshots) { super("http://127.0.0.1", "test"); this.snapshots = snapshots; }
14
+ async request() {
15
+ const thread = this.snapshots.length > 1 ? this.snapshots.shift() : this.snapshots[0];
16
+ this.afterRead?.();
17
+ return { thread };
18
+ }
19
+ async dispatch(command) {
20
+ this.commands.push(command);
21
+ if (this.commitThenTimeout && command.type === "thread.turn.start")
22
+ throw new Error("timeout after commit");
23
+ return {};
24
+ }
25
+ }
26
+ const previous = { latestTurn: { turnId: "old", state: "completed" }, messages: [{ id: "old-answer", role: "assistant", turnId: "old", text: "Old answer" }] };
27
+ const completed = { latestTurn: { turnId: "new", state: "completed", assistantMessageId: "final" }, messages: [
28
+ ...previous.messages,
29
+ { id: "progress", role: "assistant", turnId: "new", text: "Working" },
30
+ { id: "final", role: "assistant", turnId: "new", text: "Finished new work" },
31
+ ] };
32
+ test("native execution submits a real user message and returns only the new turn's final answer", async () => {
33
+ const client = new FakeT3([previous, completed]);
34
+ const result = await runT3Turn(client, "thread", { instanceId: "nudge", model: "model" }, options(), 1);
35
+ assert.equal(result.summary, "Finished new work");
36
+ assert.deepEqual(client.commands.map((c) => c.type), ["thread.turn.start", "thread.session.stop"]);
37
+ assert.deepEqual(client.commands[0].message, { messageId: client.commands[0].message.messageId, role: "user", text: "New work", attachments: [] });
38
+ assert.equal(client.commands[0].threadId, "thread");
39
+ });
40
+ test("an ambiguous start response never starts a duplicate turn", async () => {
41
+ const client = new FakeT3([previous, completed]);
42
+ client.commitThenTimeout = true;
43
+ assert.equal((await runT3Turn(client, "thread", {}, options(), 1)).summary, "Finished new work");
44
+ assert.equal(client.commands.filter((c) => c.type === "thread.turn.start").length, 1);
45
+ });
46
+ test("runner recovery reattaches to an existing running turn without dispatching another", async () => {
47
+ const client = new FakeT3([{ latestTurn: { turnId: "new", state: "running" } }, completed]);
48
+ assert.equal((await runT3Turn(client, "thread", {}, options(), 1)).summary, "Finished new work");
49
+ assert.deepEqual(client.commands.map((c) => c.type), ["thread.session.stop"]);
50
+ });
51
+ test("steering or cancellation interrupts and stops T3 before propagating the abort", async () => {
52
+ const abort = new AbortController();
53
+ const client = new FakeT3([previous, { latestTurn: { turnId: "new", state: "running" } }]);
54
+ let reads = 0;
55
+ client.afterRead = () => { if (++reads === 2)
56
+ abort.abort(); };
57
+ await assert.rejects(runT3Turn(client, "thread", {}, { ...options(), signal: abort.signal }, 1), { name: "AbortError" });
58
+ assert.deepEqual(client.commands.map((c) => c.type), ["thread.turn.start", "thread.turn.interrupt", "thread.session.stop"]);
59
+ });
60
+ test("a pre-aborted job starts no T3 turn", async () => {
61
+ const client = new FakeT3([previous]);
62
+ await assert.rejects(runT3Turn(client, "thread", {}, { ...options(), signal: AbortSignal.abort() }, 1));
63
+ assert.equal(client.commands.length, 0);
64
+ });
65
+ test("provider errors and interruption cannot be reported as success", async () => {
66
+ for (const state of ["error", "interrupted"]) {
67
+ const client = new FakeT3([previous, { ...completed, latestTurn: { ...completed.latestTurn, state }, session: { lastError: "Provider stopped" } }]);
68
+ assert.equal((await runT3Turn(client, "thread", {}, options(), 1)).error, "Provider stopped");
69
+ }
70
+ });
71
+ test("runner references preserve T3 environment and reject paths or raw CLI IDs", () => {
72
+ const threadId = t3JobThreadId("deployment", "job");
73
+ assert.deepEqual(parseT3ResumeId(t3ResumeId("environment", threadId)), { environmentId: "environment", threadId });
74
+ assert.equal(parseT3ResumeId("codex-session"), null);
75
+ assert.throws(() => parseT3ResumeId("t3:environment:..%2Fprivate"));
76
+ assert.throws(() => parseT3ResumeId("t3:broken"));
77
+ });
78
+ test("a native resume with T3 disabled fails instead of falling back to a new CLI session", async () => {
79
+ const prior = process.env.NUDGE_T3;
80
+ process.env.NUDGE_T3 = "0";
81
+ try {
82
+ const fallback = { id: "codex" };
83
+ assert.equal(await createT3Engine(fallback, {}, null, () => { }), null);
84
+ await assert.rejects(createT3Engine(fallback, {}, t3ResumeId("env", t3JobThreadId("d", "j")), () => { }), /Open the local T3/);
85
+ assert.equal(await createT3Engine(fallback, {}, "original-cli-session", () => { }), null);
86
+ }
87
+ finally {
88
+ if (prior === undefined)
89
+ delete process.env.NUDGE_T3;
90
+ else
91
+ process.env.NUDGE_T3 = prior;
92
+ }
93
+ });
94
+ test("the bridge enforces read-only and Nudge identity on both start and resume", () => {
95
+ for (const method of ["thread/start", "thread/resume", "thread/fork"]) {
96
+ const message = applyRunnerPolicy({ id: 7, method, params: { sandbox: "danger-full-access", config: { existing: true } } }, context);
97
+ assert.equal(message.params.sandbox, "read-only");
98
+ assert.equal(message.params.approvalPolicy, "never");
99
+ assert.equal(message.params.config.existing, true);
100
+ assert.deepEqual(message.params.config["mcp_servers.nudge"].env, context.env);
101
+ }
102
+ const turn = applyRunnerPolicy({ method: "turn/start", params: { sandboxPolicy: { type: "dangerFullAccess" } } }, context);
103
+ assert.deepEqual(turn.params.sandboxPolicy, { type: "readOnly" });
104
+ assert.equal(turn.params.approvalPolicy, "never");
105
+ const notification = { method: "initialize", params: { clientInfo: { name: "T3" } } };
106
+ assert.equal(applyRunnerPolicy(notification, context), notification);
107
+ });
108
+ test("task sandbox choices retain workspace-only and explicitly remote-capable execution", () => {
109
+ for (const [sandbox, type] of [["workspace-write", "workspaceWrite"], ["danger-full-access", "dangerFullAccess"]]) {
110
+ assert.deepEqual(applyRunnerPolicy({ method: "turn/start" }, { ...context, sandbox }).params.sandboxPolicy, { type });
111
+ }
112
+ });
113
+ test("usage counts only the new work on resume, deduplicates totals, and handles a reset", () => {
114
+ const total = { inputTokens: 1000, outputTokens: 100 };
115
+ const last = { inputTokens: 50, outputTokens: 5 };
116
+ assert.deepEqual(usageDelta(undefined, total, last), last);
117
+ assert.equal(usageDelta(total, total, last).inputTokens, 0);
118
+ assert.equal(usageDelta(total, { inputTokens: 1100, outputTokens: 110 }, last).inputTokens, 100);
119
+ assert.deepEqual(usageDelta(total, last, last), last);
120
+ });
121
+ //# sourceMappingURL=runner-t3-engine.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-t3-engine.test.js","sourceRoot":"","sources":["../src/runner-t3-engine.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ // Invoked in a T3 terminal. The JSONL file is data, never shell input.
2
+ import { open, stat } from "node:fs/promises";
3
+ import { fileURLToPath } from "node:url";
4
+ export function safeTerminalText(text) {
5
+ // Strip controls including ESC, OSC and C1 so agent output cannot issue
6
+ // terminal/clipboard commands. Preserve readable tabs and line breaks.
7
+ return text.replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, "");
8
+ }
9
+ export async function viewRunnerLog(file) {
10
+ let offset = 0;
11
+ let pending = "";
12
+ const decoder = new TextDecoder();
13
+ for (;;) {
14
+ const handle = await open(file, "r");
15
+ let bytesRead;
16
+ try {
17
+ const buffer = Buffer.alloc(64 * 1024);
18
+ ({ bytesRead } = await handle.read(buffer, 0, buffer.length, offset));
19
+ offset += bytesRead;
20
+ pending += decoder.decode(buffer.subarray(0, bytesRead), { stream: true });
21
+ }
22
+ finally {
23
+ await handle.close();
24
+ }
25
+ let end;
26
+ while ((end = pending.indexOf("\n")) >= 0) {
27
+ const entry = JSON.parse(pending.slice(0, end));
28
+ pending = pending.slice(end + 1);
29
+ process.stdout.write(safeTerminalText(String(entry.text ?? "")));
30
+ if (entry.done)
31
+ return;
32
+ }
33
+ if (!bytesRead) {
34
+ // A crashed runner must not leave a viewer alive indefinitely.
35
+ if (Date.now() - (await stat(file)).mtimeMs > 24 * 60 * 60 * 1000)
36
+ return;
37
+ await new Promise((resolve) => setTimeout(resolve, 500));
38
+ }
39
+ }
40
+ }
41
+ if (process.argv[1] === fileURLToPath(import.meta.url) && process.argv[2]) {
42
+ viewRunnerLog(process.argv[2]).catch(() => { console.error("Nudge runner log is unavailable."); process.exitCode = 1; });
43
+ }
44
+ //# sourceMappingURL=runner-t3-log-viewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-t3-log-viewer.js","sourceRoot":"","sources":["../src/runner-t3-log-viewer.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,wEAAwE;IACxE,uEAAuE;IACvE,OAAO,IAAI,CAAC,OAAO,CAAC,gCAAgC,EAAE,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY;IAC9C,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,SAAS,CAAC;QACR,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACrC,IAAI,SAAiB,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;YACvC,CAAC,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;YACtE,MAAM,IAAI,SAAS,CAAC;YACpB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7E,CAAC;gBAAS,CAAC;YAAC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QAAC,CAAC;QACnC,IAAI,GAAW,CAAC;QAChB,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YAChD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;YACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACjE,IAAI,KAAK,CAAC,IAAI;gBAAE,OAAO;QACzB,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,+DAA+D;YAC/D,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;gBAAE,OAAO;YAC1E,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;AACH,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1E,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3H,CAAC"}