Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"access": "public"
},
"scripts": {
"build": "pnpm audit:domains && node scripts/clean-dist.mjs && tsc && cp src/setup/lark-scopes.json dist/setup/ && pnpm dashboard:bundle && chmod +x dist/cli.js && node scripts/audit-dist.mjs",
"build": "pnpm audit:domains && node scripts/clean-dist.mjs && tsc && node scripts/generate-runtime-build-id.mjs && cp src/setup/lark-scopes.json dist/setup/ && pnpm dashboard:bundle && chmod +x dist/cli.js && node scripts/audit-dist.mjs",
"audit:domains": "node scripts/audit-public-domains.mjs",
"dashboard:bundle": "node scripts/build-dashboard.mjs",
"dashboard:watch": "node scripts/build-dashboard.mjs --watch",
Expand Down
15 changes: 15 additions & 0 deletions scripts/generate-runtime-build-id.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env node
import { writeFileSync } from 'node:fs';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import {
collectCompiledRuntimeEntries,
computeRuntimeBuildId,
} from '../dist/utils/runtime-build-id.js';

const projectRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const sourceRoot = join(projectRoot, 'src');
const distRoot = join(projectRoot, 'dist');
const buildId = computeRuntimeBuildId(collectCompiledRuntimeEntries(sourceRoot, distRoot));
writeFileSync(join(distRoot, '.runtime-build-id'), `${buildId}\n`, 'utf8');
process.stdout.write(`runtime build id: ${buildId.slice(0, 12)}\n`);
15 changes: 11 additions & 4 deletions src/adapters/cli/codex-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,27 @@ export function createCodexAppAdapter(pathOverride?: string): CliAdapter {
return null;
},

async writeInput(pty: PtyHandle, content: string) {
async writeInput(pty: PtyHandle, content: string, context) {
// Chunked + throttled stdin injection — a single send-keys of the whole
// (potentially ~20KB) control line overruns the pane pty input buffer and
// gets dropped. See runner-input.ts.
return writeRunnerInput(pty, '::botmux-codex-app:', content);
return writeRunnerInput(pty, '::botmux-codex-app:', content, undefined, context?.turnId);
},

async writeStructuredInput(pty, content, codexAppInput) {
async writeStructuredInput(pty, content, codexAppInput, context) {
// The legacy prompt remains in the control payload as a compatibility
// fallback. The runner uses the sidecar only on supported app-server
// versions and never reverse-parses the XML-ish legacy envelope.
return writeRunnerInput(pty, '::botmux-codex-app:', content, codexAppInput);
return writeRunnerInput(
pty,
'::botmux-codex-app:',
content,
codexAppInput,
context?.turnId,
);
},

supportsTypeAhead: true,
completionPattern: undefined,
readyPattern: /›/,
systemHints: [],
Expand Down
18 changes: 13 additions & 5 deletions src/adapters/cli/runner-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,17 @@ export const RUNNER_INPUT_CHUNK_BYTES = 1024;
* pane pty between writes. */
export const RUNNER_INPUT_THROTTLE_MS = 20;

export function encodeRunnerInput(content: string, codexAppInput?: CodexAppTurnInput): string {
const payload = codexAppInput
? { type: 'message', content, codexAppInput }
: { type: 'message', content };
export function encodeRunnerInput(
content: string,
codexAppInput?: CodexAppTurnInput,
replyTurnId?: string,
): string {
const payload = {
type: 'message' as const,
content,
...(codexAppInput ? { codexAppInput } : {}),
...(replyTurnId ? { replyTurnId } : {}),
};
return Buffer.from(JSON.stringify(payload), 'utf8').toString('base64');
}

Expand Down Expand Up @@ -84,8 +91,9 @@ export async function writeRunnerInput(
markerPrefix: string,
content: string,
codexAppInput?: CodexAppTurnInput,
replyTurnId?: string,
): Promise<{ submitted: boolean }> {
const line = `${markerPrefix}${encodeRunnerInput(content, codexAppInput)}`;
const line = `${markerPrefix}${encodeRunnerInput(content, codexAppInput, replyTurnId)}`;

// Non-tmux fallback (raw PTY): a single write is fine — there's no send-keys
// process to time out, and the PTY write isn't bounded the same way.
Expand Down
9 changes: 9 additions & 0 deletions src/adapters/cli/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ export type SubmitRecheckResult = boolean | {
cliSessionId?: string;
};

/** Optional per-input correlation metadata. Adapters that do not need it may
* ignore it; runner-based adapters use the immutable botmux/Lark turn id to
* keep protocol ids separate from reply-routing ids. */
export interface WriteInputContext {
turnId?: string;
}

/** A session discovered on disk that botmux can resume (import) into a topic —
* surfaced by `/adopt`'s second filter. Unlike an AdoptableSession (a live
* tmux/zellij pane botmux *observes*), this is a stored transcript botmux
Expand Down Expand Up @@ -190,6 +197,7 @@ export interface CliAdapter {
writeInput(
pty: PtyHandle,
content: string,
context?: WriteInputContext,
): Promise<void | {
submitted: boolean;
cliSessionId?: string;
Expand All @@ -208,6 +216,7 @@ export interface CliAdapter {
pty: PtyHandle,
content: string,
codexAppInput: CodexAppTurnInput,
context?: WriteInputContext,
): Promise<void | {
submitted: boolean;
cliSessionId?: string;
Expand Down
Loading