@rallycry/conveyor-agent 8.2.0 → 8.3.1
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/{chunk-W4NCD23G.js → chunk-H3UHIWQF.js} +303 -67
- package/dist/chunk-H3UHIWQF.js.map +1 -0
- package/dist/{chunk-KTNGU2WU.js → chunk-Y7ZVMFJN.js} +108 -3
- package/dist/chunk-Y7ZVMFJN.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -2
- package/dist/{tag-audit-handler-E6BZGVIG.js → tag-audit-handler-FVGEBGSB.js} +2 -2
- package/dist/{task-audit-handler-MXSNTY22.js → task-audit-handler-JNS4NH5O.js} +2 -2
- package/package.json +2 -2
- package/dist/chunk-KTNGU2WU.js.map +0 -1
- package/dist/chunk-W4NCD23G.js.map +0 -1
- /package/dist/{tag-audit-handler-E6BZGVIG.js.map → tag-audit-handler-FVGEBGSB.js.map} +0 -0
- /package/dist/{task-audit-handler-MXSNTY22.js.map → task-audit-handler-JNS4NH5O.js.map} +0 -0
|
@@ -656,6 +656,10 @@ function inheritedEnv(socketPath) {
|
|
|
656
656
|
env.CONVEYOR_HOOK_SOCKET = socketPath;
|
|
657
657
|
return env;
|
|
658
658
|
}
|
|
659
|
+
function buildPromptBytes(text, delivery) {
|
|
660
|
+
const paste = `\x1B[200~${text}\x1B[201~`;
|
|
661
|
+
return delivery === "prefill" ? paste : `${paste}\r`;
|
|
662
|
+
}
|
|
659
663
|
async function transcriptSize(path) {
|
|
660
664
|
try {
|
|
661
665
|
return (await stat(path)).size;
|
|
@@ -846,15 +850,18 @@ var PtySession = class {
|
|
|
846
850
|
});
|
|
847
851
|
this.pty = pty;
|
|
848
852
|
}
|
|
853
|
+
deliverPrompt(text) {
|
|
854
|
+
this.writeStdin(buildPromptBytes(text, this.options.promptDelivery));
|
|
855
|
+
}
|
|
849
856
|
async feedPrompt() {
|
|
850
857
|
if (typeof this.prompt === "string") {
|
|
851
|
-
this.
|
|
858
|
+
this.deliverPrompt(this.prompt);
|
|
852
859
|
return;
|
|
853
860
|
}
|
|
854
861
|
for await (const message of this.prompt) {
|
|
855
862
|
const content = message.message.content;
|
|
856
863
|
const text = typeof content === "string" ? content : JSON.stringify(content);
|
|
857
|
-
this.
|
|
864
|
+
this.deliverPrompt(text);
|
|
858
865
|
}
|
|
859
866
|
}
|
|
860
867
|
handleProgress(progress) {
|
|
@@ -890,6 +897,100 @@ var PtySession = class {
|
|
|
890
897
|
}
|
|
891
898
|
};
|
|
892
899
|
|
|
900
|
+
// src/harness/pty/credentials.ts
|
|
901
|
+
import { chmod as chmod2, mkdir as mkdir3, readFile, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
902
|
+
import { join as join4 } from "path";
|
|
903
|
+
var SYNTH_TOKEN_TTL_MS = 365 * 24 * 60 * 60 * 1e3;
|
|
904
|
+
var REFRESH_SKEW_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
905
|
+
function claudeCredentialsPath() {
|
|
906
|
+
return join4(claudeConfigHome(), ".credentials.json");
|
|
907
|
+
}
|
|
908
|
+
function isConveyorCloudEnv(env = process.env) {
|
|
909
|
+
return Boolean(env.CLAUDESPACE_NAME || env.CODESPACE_NAME || env.CODESPACES);
|
|
910
|
+
}
|
|
911
|
+
function parseClaudeAiOauth(raw) {
|
|
912
|
+
if (!raw || raw.trim() === "") return null;
|
|
913
|
+
try {
|
|
914
|
+
const parsed = JSON.parse(raw);
|
|
915
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
916
|
+
const oauth = parsed.claudeAiOauth;
|
|
917
|
+
if (typeof oauth !== "object" || oauth === null) return null;
|
|
918
|
+
const record = oauth;
|
|
919
|
+
return {
|
|
920
|
+
accessToken: record.accessToken,
|
|
921
|
+
refreshToken: record.refreshToken,
|
|
922
|
+
expiresAt: record.expiresAt
|
|
923
|
+
};
|
|
924
|
+
} catch {
|
|
925
|
+
return null;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
function buildSynthesizedCredentials(token, now) {
|
|
929
|
+
return JSON.stringify({
|
|
930
|
+
claudeAiOauth: {
|
|
931
|
+
accessToken: token,
|
|
932
|
+
expiresAt: now + SYNTH_TOKEN_TTL_MS,
|
|
933
|
+
scopes: ["user:inference", "user:profile"],
|
|
934
|
+
subscriptionType: "max"
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
function planCredentialsWrite(input) {
|
|
939
|
+
if (!input.isCloud) return { action: "skip", reason: "not-cloud" };
|
|
940
|
+
if (!input.token) return { action: "skip", reason: "no-token" };
|
|
941
|
+
const contents = buildSynthesizedCredentials(input.token, input.now);
|
|
942
|
+
const existing = parseClaudeAiOauth(input.existingRaw);
|
|
943
|
+
if (!existing) return { action: "write", contents };
|
|
944
|
+
if (typeof existing.refreshToken === "string" && existing.refreshToken.length > 0) {
|
|
945
|
+
return { action: "skip", reason: "foreign-credentials" };
|
|
946
|
+
}
|
|
947
|
+
const fresh = existing.accessToken === input.token && typeof existing.expiresAt === "number" && existing.expiresAt > input.now + REFRESH_SKEW_MS;
|
|
948
|
+
if (fresh) return { action: "skip", reason: "current" };
|
|
949
|
+
return { action: "write", contents };
|
|
950
|
+
}
|
|
951
|
+
async function readRaw(path) {
|
|
952
|
+
try {
|
|
953
|
+
return await readFile(path, "utf8");
|
|
954
|
+
} catch {
|
|
955
|
+
return null;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
async function ensureClaudeCredentials(env = process.env) {
|
|
959
|
+
try {
|
|
960
|
+
const path = claudeCredentialsPath();
|
|
961
|
+
const plan = planCredentialsWrite({
|
|
962
|
+
isCloud: isConveyorCloudEnv(env),
|
|
963
|
+
token: env.CLAUDE_CODE_OAUTH_TOKEN,
|
|
964
|
+
existingRaw: await readRaw(path),
|
|
965
|
+
now: Date.now()
|
|
966
|
+
});
|
|
967
|
+
if (plan.action === "skip") return;
|
|
968
|
+
await mkdir3(claudeConfigHome(), { recursive: true });
|
|
969
|
+
await writeFile3(path, plan.contents, { encoding: "utf8", mode: 384 });
|
|
970
|
+
await chmod2(path, 384).catch(() => {
|
|
971
|
+
});
|
|
972
|
+
} catch (err) {
|
|
973
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
974
|
+
process.stderr.write(`[conveyor-agent] claude credentials sync failed: ${message}
|
|
975
|
+
`);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
async function removeConveyorCredentials(env = process.env) {
|
|
979
|
+
try {
|
|
980
|
+
if (!isConveyorCloudEnv(env)) return;
|
|
981
|
+
const path = claudeCredentialsPath();
|
|
982
|
+
const existing = parseClaudeAiOauth(await readRaw(path));
|
|
983
|
+
if (existing && typeof existing.refreshToken === "string" && existing.refreshToken.length > 0) {
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
await rm2(path, { force: true });
|
|
987
|
+
} catch (err) {
|
|
988
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
989
|
+
process.stderr.write(`[conveyor-agent] claude credentials removal failed: ${message}
|
|
990
|
+
`);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
893
994
|
// src/harness/pty/index.ts
|
|
894
995
|
var PtyHarness = class {
|
|
895
996
|
/**
|
|
@@ -908,6 +1009,7 @@ var PtyHarness = class {
|
|
|
908
1009
|
opts.resume ?? opts.options.resume,
|
|
909
1010
|
this.bridge
|
|
910
1011
|
);
|
|
1012
|
+
await ensureClaudeCredentials();
|
|
911
1013
|
await session.start();
|
|
912
1014
|
try {
|
|
913
1015
|
for await (const event of session.events()) {
|
|
@@ -951,7 +1053,10 @@ function createServiceLogger(service) {
|
|
|
951
1053
|
|
|
952
1054
|
export {
|
|
953
1055
|
defineTool,
|
|
1056
|
+
sessionTranscriptPath,
|
|
1057
|
+
ensureClaudeCredentials,
|
|
1058
|
+
removeConveyorCredentials,
|
|
954
1059
|
createHarness,
|
|
955
1060
|
createServiceLogger
|
|
956
1061
|
};
|
|
957
|
-
//# sourceMappingURL=chunk-
|
|
1062
|
+
//# sourceMappingURL=chunk-Y7ZVMFJN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/harness/types.ts","../src/harness/claude-code/index.ts","../src/harness/pty/session.ts","../src/harness/pty/event-queue.ts","../src/harness/pty/hook-socket.ts","../src/harness/pty/jsonl-tailer.ts","../src/harness/pty/record-mapper.ts","../src/harness/pty/spawn-args.ts","../src/harness/pty/settings.ts","../src/harness/pty/tool-server.ts","../src/harness/pty/mcp-server.ts","../src/harness/pty/credentials.ts","../src/harness/pty/index.ts","../src/harness/index.ts","../src/utils/logger.ts"],"sourcesContent":["/**\n * Harness-neutral types for agent query execution.\n *\n * These types abstract the underlying agent SDK so that the runner,\n * execution, and tool layers never reference SDK-specific imports\n * directly. The only place that should import from\n * `@anthropic-ai/claude-agent-sdk` is `harness/claude-code/`.\n */\n\nimport type { z } from \"zod\";\n\n// ── Events emitted by the harness during a query ────────────────────────\n\nexport interface HarnessSystemInitEvent {\n type: \"system\";\n subtype: \"init\";\n session_id?: string;\n model: string;\n}\n\nexport interface HarnessCompactBoundaryEvent {\n type: \"system\";\n subtype: \"compact_boundary\";\n compact_metadata: { trigger: \"manual\" | \"auto\"; pre_tokens: number };\n}\n\nexport interface HarnessTaskStartedEvent {\n type: \"system\";\n subtype: \"task_started\";\n task_id: string;\n description: string;\n}\n\nexport interface HarnessTaskProgressEvent {\n type: \"system\";\n subtype: \"task_progress\";\n task_id: string;\n description: string;\n usage?: { tool_uses: number; duration_ms: number };\n}\n\nexport type HarnessSystemEvent =\n | HarnessSystemInitEvent\n | HarnessCompactBoundaryEvent\n | HarnessTaskStartedEvent\n | HarnessTaskProgressEvent;\n\nexport interface HarnessContentBlock {\n type: string;\n text?: string;\n name?: string;\n input?: unknown;\n id?: string;\n}\n\nexport interface HarnessAssistantEvent {\n type: \"assistant\";\n message: {\n role: \"assistant\";\n content: HarnessContentBlock[];\n usage?: {\n input_tokens?: number;\n cache_read_input_tokens?: number;\n cache_creation_input_tokens?: number;\n };\n };\n}\n\nexport interface HarnessResultSuccessEvent {\n type: \"result\";\n subtype: \"success\";\n result: string;\n total_cost_usd: number;\n modelUsage?: Record<string, unknown>;\n sessionId?: string;\n}\n\nexport interface HarnessResultErrorEvent {\n type: \"result\";\n subtype: \"error\";\n errors: string[];\n sessionId?: string;\n}\n\nexport type HarnessResultEvent = HarnessResultSuccessEvent | HarnessResultErrorEvent;\n\nexport interface HarnessRateLimitEvent {\n type: \"rate_limit_event\";\n rate_limit_info: {\n status: string;\n rateLimitType?: string;\n utilization?: number;\n resetsAt?: unknown;\n };\n}\n\nexport interface HarnessToolProgressEvent {\n type: \"tool_progress\";\n tool_name?: string;\n elapsed_time_seconds?: number;\n}\n\nexport type HarnessEvent =\n | HarnessSystemEvent\n | HarnessAssistantEvent\n | HarnessResultEvent\n | HarnessRateLimitEvent\n | HarnessToolProgressEvent;\n\n// ── User message fed into a query ───────────────────────────────────────\n\nexport interface HarnessUserMessage {\n type: \"user\";\n session_id: string;\n message: { role: \"user\"; content: string | unknown[] };\n parent_tool_use_id: null;\n}\n\n// ── Tool definition (harness-neutral) ───────────────────────────────────\n\nexport interface HarnessToolAnnotations {\n readOnlyHint?: boolean;\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- generic tool handler */\nexport interface HarnessToolDefinition {\n name: string;\n description: string;\n schema: z.ZodRawShape;\n handler: (\n input: any,\n ) => Promise<{ content: { type: string; text?: string; data?: string; mimeType?: string }[] }>;\n annotations?: HarnessToolAnnotations;\n}\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n// ── Hook types ──────────────────────────────────────────────────────────\n\nexport interface HarnessHookInput {\n hook_event_name: string;\n tool_name: string;\n tool_response: unknown;\n}\n\nexport interface HarnessHookOutput {\n continue: boolean;\n}\n\nexport type HarnessPostToolUseHook = (input: HarnessHookInput) => Promise<HarnessHookOutput>;\n\n// ── MCP server handle (opaque to the runner) ────────────────────────────\n\n/** Opaque handle returned by `AgentHarness.createMcpServer()`. */\nexport type HarnessMcpServer = unknown;\n\n// ── PTY relay bridge ────────────────────────────────────────────────────\n\n/**\n * Bidirectional bridge between a PTY harness session and the S2 server relay.\n *\n * Kept harness-neutral (no AgentConnection import) so this module never depends\n * on the connection layer. Only the PTY harness consumes it; the SDK harness\n * ignores it. The runner adapts an `AgentConnection` into this shape.\n */\nexport interface PtyBridge {\n /** Forward a raw chunk of terminal output to the relay (fire-and-forget). */\n sendOutput(data: string, dims?: { cols: number; rows: number }): void;\n /** Subscribe to user keystrokes forwarded from the relay. Returns unsubscribe. */\n onInput(handler: (data: string) => void): () => void;\n /** Subscribe to reconciled terminal-resize events from the relay. Returns unsubscribe. */\n onResize(handler: (cols: number, rows: number) => void): () => void;\n}\n\n// ── Query options ───────────────────────────────────────────────────────\n\nexport interface HarnessQueryOptions {\n model: string;\n systemPrompt: unknown;\n cwd: string;\n permissionMode: \"plan\" | \"bypassPermissions\";\n allowDangerouslySkipPermissions: boolean;\n tools: { type: \"preset\"; preset: \"claude_code\" };\n mcpServers: Record<string, HarnessMcpServer>;\n settingSources?: (\"user\" | \"project\" | \"local\")[];\n sandbox?: { enabled: boolean };\n maxTurns?: number;\n maxBudgetUsd?: number;\n effort?: string;\n thinking?: unknown;\n betas?: unknown;\n disallowedTools?: string[];\n abortController?: AbortController;\n enableFileCheckpointing?: boolean;\n canUseTool?: (\n toolName: string,\n input: Record<string, unknown>,\n ) => Promise<\n | { behavior: \"allow\"; updatedInput?: Record<string, unknown> }\n | { behavior: \"deny\"; message: string }\n >;\n hooks?: Record<string, { hooks: HarnessPostToolUseHook[]; timeout: number }[]>;\n resume?: string;\n sessionId?: string;\n stderr?: (data: string) => void;\n /**\n * How the PTY harness delivers the query prompt to the spawned CLI:\n * - \"submit\" (default): paste the prompt and press Enter — the turn runs\n * immediately (auto-mode tasks, follow-up chat messages, resumes).\n * - \"prefill\": paste the prompt into the TUI input WITHOUT submitting, so\n * a human at the Connected-TUI terminal can review/edit/Enter it\n * (manual-mode initial instructions).\n * The SDK harness has no interactive input box and ignores this option.\n */\n promptDelivery?: \"submit\" | \"prefill\";\n}\n\n// ── Tool definition helper ──────────────────────────────────────────────\n\n/**\n * Construct a `HarnessToolDefinition` with the same signature as the SDK's\n * `tool()` helper, but without importing the SDK. The harness implementation\n * converts these into SDK-native tools when `createMcpServer()` is called.\n */\nexport function defineTool<T extends z.ZodRawShape>(\n name: string,\n description: string,\n schema: T,\n handler: (\n input: z.infer<z.ZodObject<T>>,\n ) => Promise<{ content: { type: string; text?: string; data?: string; mimeType?: string }[] }>,\n options?: { annotations?: HarnessToolAnnotations },\n): HarnessToolDefinition {\n return {\n name,\n description,\n schema,\n handler: handler as HarnessToolDefinition[\"handler\"],\n annotations: options?.annotations,\n };\n}\n\n// ── AgentHarness interface ──────────────────────────────────────────────\n\nexport interface AgentHarness {\n /** Start a streaming query and return an async generator of events. */\n executeQuery(options: {\n prompt: string | AsyncGenerator<HarnessUserMessage, void, unknown>;\n options: HarnessQueryOptions;\n resume?: string;\n }): AsyncGenerator<HarnessEvent, void>;\n\n /** Wrap an array of harness-neutral tool definitions into an MCP server. */\n createMcpServer(config: { name: string; tools: HarnessToolDefinition[] }): HarnessMcpServer;\n}\n","/**\n * ClaudeCodeHarness — wraps `@anthropic-ai/claude-agent-sdk` behind the\n * generic `AgentHarness` interface.\n *\n * This is the ONLY module that should import from the SDK.\n */\n\nimport { query, tool, createSdkMcpServer } from \"@anthropic-ai/claude-agent-sdk\";\nimport type {\n AgentHarness,\n HarnessEvent,\n HarnessQueryOptions,\n HarnessToolDefinition,\n HarnessMcpServer,\n HarnessUserMessage,\n} from \"../types.js\";\n\nexport class ClaudeCodeHarness implements AgentHarness {\n async *executeQuery(opts: {\n prompt: string | AsyncGenerator<HarnessUserMessage, void, unknown>;\n options: HarnessQueryOptions;\n resume?: string;\n }): AsyncGenerator<HarnessEvent, void> {\n const sdkEvents = query({\n prompt: opts.prompt as Parameters<typeof query>[0][\"prompt\"],\n options: {\n ...(opts.options as Parameters<typeof query>[0][\"options\"]),\n ...(opts.resume ? { resume: opts.resume } : {}),\n ...(opts.options.sessionId ? { sessionId: opts.options.sessionId } : {}),\n ...(opts.options.abortController ? { abortController: opts.options.abortController } : {}),\n },\n });\n\n for await (const event of sdkEvents) {\n yield event as unknown as HarnessEvent;\n }\n }\n\n createMcpServer(config: { name: string; tools: HarnessToolDefinition[] }): HarnessMcpServer {\n const sdkTools = config.tools.map((t) =>\n tool(\n t.name,\n t.description,\n t.schema,\n t.handler as Parameters<typeof tool>[3],\n t.annotations ? { annotations: t.annotations } : undefined,\n ),\n );\n return createSdkMcpServer({ name: config.name, tools: sdkTools });\n }\n}\n","/**\n * PtySession — orchestrates one `claude` CLI run under a pseudo-terminal.\n *\n * It merges two event sources into a single stream:\n * 1. the on-disk transcript JSONL (tailed via JsonlTailer), and\n * 2. PostToolUse progress envelopes relayed over a Unix socket.\n *\n * Raw PTY stdout is never turned into HarnessEvents — only the trusted\n * transcript file and the validated socket envelopes become events. When a\n * PtyBridge is supplied, that raw stdout is instead mirrored verbatim to the\n * S2 relay so the S5 \"Connected TUI\" terminal can render it, and relayed\n * keystrokes / resizes are fed back into the pty.\n *\n * `node-pty` is loaded lazily so that SDK-only consumers never pull in the\n * native module.\n */\n\nimport { mkdtemp, mkdir, rm, stat } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, dirname } from \"node:path\";\nimport type { HarnessEvent, HarnessQueryOptions, HarnessUserMessage, PtyBridge } from \"../types.js\";\nimport { AsyncEventQueue } from \"./event-queue.js\";\nimport { HookSocketServer, type ToolProgress } from \"./hook-socket.js\";\nimport { JsonlTailer } from \"./jsonl-tailer.js\";\nimport { buildSpawnArgs, resolveClaudeBinary, buildExitErrors } from \"./spawn-args.js\";\nimport { writeHookSettings, sessionTranscriptPath } from \"./settings.js\";\nimport { PtyToolServer, startToolServers } from \"./tool-server.js\";\n\n// Cap on the rolling tail of raw terminal output retained for diagnostics when\n// `claude` exits without a result. Bounded so a long-running session can't grow\n// this unboundedly; only the most recent bytes (where a failure surfaces) matter.\nconst MAX_DIAGNOSTIC_OUTPUT = 4000;\n\ninterface PtyProcess {\n onData(listener: (data: string) => void): void;\n onExit(listener: (event: { exitCode: number }) => void): void;\n write(data: string): void;\n resize(cols: number, rows: number): void;\n kill(signal?: string): void;\n}\n\ninterface PtySpawnOptions {\n name: string;\n cols: number;\n rows: number;\n cwd: string;\n env: Record<string, string>;\n}\n\ntype PtySpawn = (file: string, args: string[], options: PtySpawnOptions) => PtyProcess;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction extractSpawn(mod: unknown): PtySpawn | null {\n if (!isRecord(mod)) return null;\n if (typeof mod.spawn === \"function\") return mod.spawn as PtySpawn;\n const def = mod.default;\n if (isRecord(def) && typeof def.spawn === \"function\") return def.spawn as PtySpawn;\n return null;\n}\n\nasync function loadPtySpawn(): Promise<PtySpawn> {\n const mod: unknown = await import(\"node-pty\");\n const spawn = extractSpawn(mod);\n if (!spawn) throw new Error(\"node-pty: spawn export not found\");\n return spawn;\n}\n\nexport function inheritedEnv(socketPath: string): Record<string, string> {\n const env: Record<string, string> = {};\n for (const [key, value] of Object.entries(process.env)) {\n if (typeof value === \"string\") env[key] = value;\n }\n // Do NOT force CLAUDE_CONFIG_DIR. Forcing it to ~/.claude makes the CLI read\n // <dir>/.claude.json — a fresh, un-onboarded config — instead of the user's\n // real ~/.claude.json, which re-triggers the first-run theme picker + login\n // even though valid credentials already exist in ~/.claude/. The loop above\n // already propagates an explicitly-set CLAUDE_CONFIG_DIR (pods/codespaces),\n // and the config dir is unchanged either way, so transcript tailing (which\n // uses claudeConfigHome()) still resolves to the same projects/ directory.\n env.CONVEYOR_HOOK_SOCKET = socketPath;\n return env;\n}\n\n/**\n * The exact bytes written to the CLI's stdin to deliver a prompt. The text is\n * wrapped in bracketed-paste markers so embedded newlines land as literal\n * lines in the input box instead of submitting it early. With \"submit\"\n * delivery (the default) a trailing Enter follows the paste; with \"prefill\"\n * the text is left in the input box unsubmitted for the human at the\n * Connected-TUI terminal.\n */\nexport function buildPromptBytes(text: string, delivery?: \"submit\" | \"prefill\"): string {\n const paste = `\\x1b[200~${text}\\x1b[201~`;\n return delivery === \"prefill\" ? paste : `${paste}\\r`;\n}\n\n/** Current size of the transcript, or 0 if it does not exist yet. */\nasync function transcriptSize(path: string): Promise<number> {\n try {\n return (await stat(path)).size;\n } catch {\n return 0;\n }\n}\n\nexport class PtySession {\n private readonly queue = new AsyncEventQueue<HarnessEvent>();\n private socket: HookSocketServer | null = null;\n private tailer: JsonlTailer | null = null;\n private pty: PtyProcess | null = null;\n private tempDir = \"\";\n private sawResult = false;\n // Rolling tail of raw PTY output, retained only to enrich the error when the\n // CLI exits before emitting a result (the bytes are otherwise relayed to S5\n // and never become events). Trimmed to MAX_DIAGNOSTIC_OUTPUT on every write.\n private recentOutput = \"\";\n private _toreDown = false;\n private readonly exitListeners: ((code: number) => void)[] = [];\n private readonly idleListeners: (() => void)[] = [];\n private abortHandler: (() => void) | null = null;\n private cols = 120;\n private rows = 40;\n private unsubInput: (() => void) | null = null;\n private unsubResize: (() => void) | null = null;\n // In-process HTTP MCP servers exposing the harness tools to the spawned CLI,\n // plus the path to the `--mcp-config` that points at them.\n private toolServers: PtyToolServer[] = [];\n private mcpConfigPath: string | null = null;\n\n constructor(\n private readonly prompt: string | AsyncGenerator<HarnessUserMessage, void, unknown>,\n private readonly options: HarnessQueryOptions,\n private readonly resume?: string,\n private readonly bridge?: PtyBridge,\n ) {}\n\n onIdle(listener: () => void): void {\n this.idleListeners.push(listener);\n }\n\n onExit(listener: (code: number) => void): void {\n this.exitListeners.push(listener);\n }\n\n get isToreDown(): boolean {\n return this._toreDown;\n }\n\n get hookSocketPath(): string | null {\n return this.socket ? this.socket.socketPath : null;\n }\n\n events(): AsyncGenerator<HarnessEvent, void> {\n return this.queue.drain();\n }\n\n async start(): Promise<void> {\n // Fail fast before allocating resources: the tailer can only locate the\n // transcript if we know which session id the CLI will write to.\n const sessionId = this.resume ?? this.options.sessionId;\n if (!sessionId) {\n throw new Error(\"PtySession requires options.sessionId or a resume target\");\n }\n // stop()/softStop() abort the query's shared AbortController. The SDK\n // harness consumes it natively; under the PTY harness we must translate an\n // abort into teardown() — otherwise a mid-turn `claude` (which may never\n // emit a transcript `result` and does not exit) is left running while the\n // event consumer stays parked on the queue.\n const signal = this.options.abortController?.signal;\n if (signal?.aborted) {\n // Already aborted before we allocated anything — tear down (which closes\n // the empty queue so a parked consumer completes) and bail.\n await this.teardown();\n return;\n }\n try {\n this.tempDir = await mkdtemp(join(tmpdir(), \"conveyor-pty-\"));\n const socketPath = join(this.tempDir, \"hook.sock\");\n this.socket = new HookSocketServer(socketPath, (progress) => this.handleProgress(progress));\n await this.socket.listen();\n const { settingsPath } = await writeHookSettings(this.tempDir);\n // Expose the harness's in-process tools to the spawned CLI over a loopback\n // HTTP MCP server, written into a `--mcp-config` consumed by spawn().\n await this.setupToolServers();\n const transcriptPath = sessionTranscriptPath(this.options.cwd, sessionId);\n await mkdir(dirname(transcriptPath), { recursive: true });\n // On resume the transcript already holds the prior session's records; start\n // tailing at EOF so historical records aren't replayed as fresh events.\n const startOffset = this.resume ? await transcriptSize(transcriptPath) : 0;\n this.tailer = new JsonlTailer(transcriptPath, (event) => this.handleTranscriptEvent(event));\n this.tailer.start(startOffset);\n await this.spawn(settingsPath, socketPath);\n // Register after spawn so the handler has a live pty to kill, then\n // re-check: an abort racing with allocation (fired before the listener\n // attached) would otherwise be missed.\n if (signal) {\n this.abortHandler = () => {\n void this.teardown();\n };\n signal.addEventListener(\"abort\", this.abortHandler, { once: true });\n if (signal.aborted) {\n await this.teardown();\n return;\n }\n }\n // Wire relayed keystrokes / resizes into the live pty. Registered only\n // after a successful spawn so writeStdin/resizePty have a process to\n // target; the unsubscribers are cleared in teardown().\n if (this.bridge) {\n this.unsubInput = this.bridge.onInput((data) => this.writeStdin(data));\n this.unsubResize = this.bridge.onResize((cols, rows) => this.resizePty(cols, rows));\n }\n await this.feedPrompt();\n } catch (err) {\n // A failure after we begin allocating (socket listen, tailer interval,\n // temp dir, spawn) would otherwise leak those resources: the PtyHarness\n // consumer only calls teardown() once start() has resolved.\n await this.teardown();\n throw err;\n }\n }\n\n writeStdin(text: string): void {\n this.pty?.write(text);\n }\n\n /** Apply a relayed resize to the live pty (reconciled dims from the server). */\n private resizePty(cols: number, rows: number): void {\n if (cols <= 0 || rows <= 0) return;\n this.cols = cols;\n this.rows = rows;\n try {\n this.pty?.resize(cols, rows);\n } catch {\n /* pty already exited */\n }\n }\n\n async teardown(): Promise<void> {\n if (this._toreDown) return;\n this._toreDown = true;\n this.unsubInput?.();\n this.unsubInput = null;\n this.unsubResize?.();\n this.unsubResize = null;\n if (this.abortHandler) {\n this.options.abortController?.signal.removeEventListener(\"abort\", this.abortHandler);\n this.abortHandler = null;\n }\n try {\n this.pty?.kill();\n } catch {\n /* pty already exited */\n }\n this.pty = null;\n this.tailer?.close();\n this.tailer = null;\n if (this.socket) {\n await this.socket.close();\n this.socket = null;\n }\n for (const toolServer of this.toolServers) {\n try {\n await toolServer.close();\n } catch {\n /* already closed */\n }\n }\n this.toolServers = [];\n this.mcpConfigPath = null;\n this.queue.close();\n if (this.tempDir) {\n await rm(this.tempDir, { recursive: true, force: true });\n this.tempDir = \"\";\n }\n }\n\n /**\n * Serve the harness's in-process tools to the spawned CLI over loopback HTTP\n * (handlers run in THIS process against the live task-token connection) and\n * record the `--mcp-config` path for spawn(). No-op when the harness was\n * constructed without tools (e.g. SDK-only callers).\n */\n private async setupToolServers(): Promise<void> {\n const { servers, mcpConfigPath } = await startToolServers(\n this.options.mcpServers ?? {},\n this.tempDir,\n );\n this.toolServers = servers;\n this.mcpConfigPath = mcpConfigPath;\n }\n\n private async spawn(settingsPath: string, socketPath: string): Promise<void> {\n const args = buildSpawnArgs({\n resume: this.resume,\n sessionId: this.options.sessionId,\n model: this.options.model,\n permissionMode: this.options.permissionMode,\n settingsPath,\n // Only this config: ignore the user's ~/.claude.json / project .mcp.json\n // so the agent's tool set is deterministic (and a stale personal conveyor\n // server doesn't load).\n ...(this.mcpConfigPath ? { mcpConfigPath: this.mcpConfigPath, strictMcpConfig: true } : {}),\n });\n const spawn = await loadPtySpawn();\n const pty = spawn(resolveClaudeBinary(), args, {\n name: \"xterm-color\",\n cols: this.cols,\n rows: this.rows,\n cwd: this.options.cwd,\n env: inheritedEnv(socketPath),\n });\n // Mirror raw terminal output to the relay (and on to the S5 terminal). The\n // first chunk creates the server-side scrollback ring — which is what makes\n // the Connected-TUI terminal appear, i.e. this doubles as the attach\n // handshake. Stdout is intentionally NOT turned into HarnessEvents.\n pty.onData((data) => {\n this.bridge?.sendOutput(data, { cols: this.cols, rows: this.rows });\n // Retain a bounded tail so finalizeOnExit can explain an early exit.\n this.recentOutput = (this.recentOutput + data).slice(-MAX_DIAGNOSTIC_OUTPUT);\n });\n pty.onExit((event) => {\n void this.finalizeOnExit(event.exitCode);\n });\n this.pty = pty;\n }\n\n private deliverPrompt(text: string): void {\n this.writeStdin(buildPromptBytes(text, this.options.promptDelivery));\n }\n\n private async feedPrompt(): Promise<void> {\n if (typeof this.prompt === \"string\") {\n this.deliverPrompt(this.prompt);\n return;\n }\n for await (const message of this.prompt) {\n const content = message.message.content;\n const text = typeof content === \"string\" ? content : JSON.stringify(content);\n this.deliverPrompt(text);\n }\n }\n\n private handleProgress(progress: ToolProgress): void {\n this.queue.push({\n type: \"tool_progress\",\n ...(progress.tool_name === undefined ? {} : { tool_name: progress.tool_name }),\n ...(progress.elapsed_time_seconds === undefined\n ? {}\n : { elapsed_time_seconds: progress.elapsed_time_seconds }),\n });\n }\n\n private handleTranscriptEvent(event: HarnessEvent): void {\n this.queue.push(event);\n if (event.type === \"result\") {\n this.sawResult = true;\n for (const listener of this.idleListeners) listener();\n // A transcript `result` marks end-of-query. Close the stream so the\n // generator completes — mirroring the SDK harness's per-query model.\n // Interactive `claude` does not exit after a result, so we must not wait\n // for process exit to terminate the event stream.\n this.queue.close();\n }\n }\n\n private async finalizeOnExit(exitCode: number): Promise<void> {\n // A teardown-driven kill (stop/softStop/abort) fires this exit handler, but\n // the turn is already discarded — pushing a synthetic error result here\n // would be spurious and would race teardown()'s queue.close().\n if (this._toreDown) return;\n if (this.tailer) {\n this.tailer.close();\n await this.tailer.flush();\n }\n if (!this.sawResult) {\n // Fold the swallowed terminal tail into the error so a bare exit (missing\n // `claude` binary, auth stop, unknown flag) is diagnosable from the logs\n // rather than only from the live S5 terminal.\n this.queue.push({\n type: \"result\",\n subtype: \"error\",\n errors: buildExitErrors(exitCode, this.recentOutput, resolveClaudeBinary()),\n });\n }\n for (const listener of this.exitListeners) listener(exitCode);\n this.queue.close();\n }\n}\n","/**\n * AsyncEventQueue — bridges callback-style producers (transcript tailer, hook\n * socket) into a single async-generator consumer. Producers call `push`;\n * the consumer drains via the async generator returned by `drain()`.\n */\nexport class AsyncEventQueue<T> {\n private readonly items: T[] = [];\n private closed = false;\n private wake: (() => void) | null = null;\n\n push(item: T): void {\n if (this.closed) return;\n this.items.push(item);\n this.signal();\n }\n\n close(): void {\n this.closed = true;\n this.signal();\n }\n\n get isClosed(): boolean {\n return this.closed;\n }\n\n private signal(): void {\n const wake = this.wake;\n if (wake) {\n this.wake = null;\n wake();\n }\n }\n\n async *drain(): AsyncGenerator<T, void> {\n while (!this.closed || this.items.length > 0) {\n if (this.items.length > 0) {\n const item = this.items.shift();\n if (item !== undefined) yield item;\n continue;\n }\n await new Promise<void>((resolve) => {\n this.wake = resolve;\n });\n }\n }\n}\n","/**\n * Unix-domain socket server that receives PostToolUse progress envelopes from\n * the hook helper and surfaces them as `tool_progress` events.\n *\n * Only the newline-delimited JSON envelope is parsed (in `parseEnvelope`),\n * and malformed lines are dropped — raw PTY stdout is never parsed here.\n */\n\nimport { createServer, type Server, type Socket } from \"node:net\";\nimport { unlink } from \"node:fs/promises\";\n\nexport interface ToolProgress {\n tool_name?: string;\n elapsed_time_seconds?: number;\n}\n\nexport function parseEnvelope(line: string): ToolProgress | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n return null;\n }\n if (typeof parsed !== \"object\" || parsed === null) return null;\n const record = parsed as Record<string, unknown>;\n const result: ToolProgress = {};\n if (typeof record.tool_name === \"string\") result.tool_name = record.tool_name;\n if (typeof record.elapsed_time_seconds === \"number\") {\n result.elapsed_time_seconds = record.elapsed_time_seconds;\n }\n return result;\n}\n\nexport class HookSocketServer {\n private server: Server | null = null;\n private buffer = \"\";\n private _closed = false;\n\n constructor(\n public readonly socketPath: string,\n private readonly onProgress: (progress: ToolProgress) => void,\n ) {}\n\n async listen(): Promise<void> {\n await unlink(this.socketPath).catch(() => undefined);\n await new Promise<void>((resolve, reject) => {\n const server = createServer((socket) => this.handleConnection(socket));\n server.on(\"error\", reject);\n server.listen(this.socketPath, () => {\n resolve();\n });\n this.server = server;\n });\n }\n\n get isClosed(): boolean {\n return this._closed;\n }\n\n async close(): Promise<void> {\n if (this._closed) return;\n this._closed = true;\n const server = this.server;\n this.server = null;\n if (server) {\n await new Promise<void>((resolve) => {\n server.close(() => {\n resolve();\n });\n });\n }\n await unlink(this.socketPath).catch(() => undefined);\n }\n\n private handleConnection(socket: Socket): void {\n // Swallow connection-level errors (e.g. ECONNRESET when the hook helper is\n // killed mid-write during teardown). Without an \"error\" listener Node\n // treats it as unhandled and crashes the process.\n socket.on(\"error\", () => undefined);\n socket.on(\"data\", (chunk: Buffer) => {\n this.buffer += chunk.toString(\"utf8\");\n this.ingest();\n });\n }\n\n private ingest(): void {\n let index = this.buffer.indexOf(\"\\n\");\n while (index >= 0) {\n const line = this.buffer.slice(0, index);\n this.buffer = this.buffer.slice(index + 1);\n const progress = parseEnvelope(line);\n if (progress) this.onProgress(progress);\n index = this.buffer.indexOf(\"\\n\");\n }\n }\n}\n","/**\n * Tails a Claude Code transcript JSONL file, emitting a `HarnessEvent` for\n * each newly-appended record. Polls on a short interval because the file is\n * written by a separate process (the `claude` CLI under the PTY).\n *\n * All reads are serialized through a promise chain so the polling interval\n * and the final `flush()` on exit can never read overlapping byte ranges.\n */\n\nimport { open } from \"node:fs/promises\";\nimport type { HarnessEvent } from \"../types.js\";\nimport { mapTranscriptRecord } from \"./record-mapper.js\";\n\nconst POLL_INTERVAL_MS = 25;\n\nexport class JsonlTailer {\n private offset = 0;\n private buffer = \"\";\n private timer: ReturnType<typeof setInterval> | null = null;\n private chain: Promise<void> = Promise.resolve();\n private _closed = false;\n\n constructor(\n private readonly path: string,\n private readonly onEvent: (event: HarnessEvent) => void,\n ) {}\n\n /**\n * Begin tailing. Pass `fromOffset` to skip bytes already present when the\n * tail starts (used on resume so the prior session's records aren't replayed).\n */\n start(fromOffset = 0): void {\n if (this.timer) return;\n this.offset = fromOffset;\n this.timer = setInterval(() => {\n if (this._closed) return;\n void this.enqueueRead();\n }, POLL_INTERVAL_MS);\n }\n\n close(): void {\n this._closed = true;\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n /** Read any remaining bytes and flush a trailing line without a newline. */\n async flush(): Promise<void> {\n await this.enqueueRead();\n if (this.buffer.length > 0) {\n this.emitLine(this.buffer);\n this.buffer = \"\";\n }\n }\n\n private enqueueRead(): Promise<void> {\n this.chain = this.chain.then(() => this.readOnce());\n return this.chain;\n }\n\n private async readOnce(): Promise<void> {\n let handle: Awaited<ReturnType<typeof open>> | null = null;\n try {\n handle = await open(this.path, \"r\");\n const stats = await handle.stat();\n if (stats.size <= this.offset) return;\n const length = stats.size - this.offset;\n const buf = Buffer.alloc(length);\n await handle.read(buf, 0, length, this.offset);\n this.offset = stats.size;\n this.consume(buf.toString(\"utf8\"));\n } catch {\n /* transient read error (e.g. file not yet created); retry on next poll */\n } finally {\n if (handle) await handle.close();\n }\n }\n\n private consume(chunk: string): void {\n this.buffer += chunk;\n let index = this.buffer.indexOf(\"\\n\");\n while (index >= 0) {\n const line = this.buffer.slice(0, index);\n this.buffer = this.buffer.slice(index + 1);\n this.emitLine(line);\n index = this.buffer.indexOf(\"\\n\");\n }\n }\n\n private emitLine(line: string): void {\n const trimmed = line.trim();\n if (trimmed.length === 0) return;\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch {\n return;\n }\n const event = mapTranscriptRecord(parsed);\n if (event) this.onEvent(event);\n }\n}\n","/**\n * Pure transforms from Claude Code transcript JSONL records into\n * harness-neutral `HarnessEvent`s. This is the parity core: the events it\n * produces must match what `ClaudeCodeHarness` yields for the same logical\n * messages (see `helpers/mock-sdk.ts`).\n *\n * Transcript files are a trusted on-disk artifact written by the `claude`\n * CLI, so parsing their lines is allowed — unlike raw PTY stdout, which is\n * never parsed.\n */\n\nimport type {\n HarnessEvent,\n HarnessAssistantEvent,\n HarnessContentBlock,\n HarnessResultEvent,\n HarnessSystemInitEvent,\n} from \"../types.js\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/** Custom predicate so narrowing yields `unknown[]`, not `any[]`. */\nfunction isUnknownArray(value: unknown): value is unknown[] {\n return Array.isArray(value);\n}\n\nfunction stringField(record: Record<string, unknown>, ...keys: string[]): string | undefined {\n for (const key of keys) {\n const value = record[key];\n if (typeof value === \"string\") return value;\n }\n return undefined;\n}\n\nfunction numberField(record: Record<string, unknown>, ...keys: string[]): number | undefined {\n for (const key of keys) {\n const value = record[key];\n if (typeof value === \"number\") return value;\n }\n return undefined;\n}\n\nfunction mapSystem(record: Record<string, unknown>): HarnessSystemInitEvent | null {\n if (record.subtype !== \"init\") return null;\n const event: HarnessSystemInitEvent = {\n type: \"system\",\n subtype: \"init\",\n model: stringField(record, \"model\") ?? \"\",\n };\n const sessionId = stringField(record, \"session_id\", \"sessionId\");\n if (sessionId !== undefined) event.session_id = sessionId;\n return event;\n}\n\ntype AssistantUsage = NonNullable<HarnessAssistantEvent[\"message\"][\"usage\"]>;\n\nfunction mapUsage(message: Record<string, unknown>): AssistantUsage | undefined {\n const usage = message.usage;\n if (!isRecord(usage)) return undefined;\n const result: AssistantUsage = {};\n const input = numberField(usage, \"input_tokens\");\n const cacheRead = numberField(usage, \"cache_read_input_tokens\");\n const cacheCreation = numberField(usage, \"cache_creation_input_tokens\");\n if (input !== undefined) result.input_tokens = input;\n if (cacheRead !== undefined) result.cache_read_input_tokens = cacheRead;\n if (cacheCreation !== undefined) result.cache_creation_input_tokens = cacheCreation;\n return result;\n}\n\nfunction mapContentBlock(raw: unknown): HarnessContentBlock | null {\n if (!isRecord(raw)) return null;\n const type = stringField(raw, \"type\");\n if (type === undefined) return null;\n const block: HarnessContentBlock = { type };\n const text = stringField(raw, \"text\");\n if (text !== undefined) block.text = text;\n const name = stringField(raw, \"name\");\n if (name !== undefined) block.name = name;\n const id = stringField(raw, \"id\");\n if (id !== undefined) block.id = id;\n if (\"input\" in raw) block.input = raw.input;\n return block;\n}\n\nfunction mapAssistant(record: Record<string, unknown>): HarnessAssistantEvent | null {\n const message = record.message;\n if (!isRecord(message)) return null;\n const rawContent: unknown[] = isUnknownArray(message.content) ? message.content : [];\n const content: HarnessContentBlock[] = [];\n for (const item of rawContent) {\n const block = mapContentBlock(item);\n if (block) content.push(block);\n }\n const result: HarnessAssistantEvent = {\n type: \"assistant\",\n message: { role: \"assistant\", content },\n };\n const usage = mapUsage(message);\n if (usage !== undefined) result.message.usage = usage;\n return result;\n}\n\nfunction mapResultSuccess(record: Record<string, unknown>): HarnessResultEvent {\n const event: HarnessResultEvent = {\n type: \"result\",\n subtype: \"success\",\n result: stringField(record, \"result\") ?? \"\",\n total_cost_usd: numberField(record, \"total_cost_usd\", \"totalCostUsd\") ?? 0,\n };\n const modelUsage = record.modelUsage;\n if (isRecord(modelUsage)) event.modelUsage = modelUsage;\n const sessionId = stringField(record, \"session_id\", \"sessionId\");\n if (sessionId !== undefined) event.sessionId = sessionId;\n return event;\n}\n\nfunction mapResultError(record: Record<string, unknown>): HarnessResultEvent {\n const rawErrors = isUnknownArray(record.errors) ? record.errors : [];\n const errors = rawErrors.filter((e): e is string => typeof e === \"string\");\n const event: HarnessResultEvent = { type: \"result\", subtype: \"error\", errors };\n const sessionId = stringField(record, \"session_id\", \"sessionId\");\n if (sessionId !== undefined) event.sessionId = sessionId;\n return event;\n}\n\nfunction mapResult(record: Record<string, unknown>): HarnessResultEvent | null {\n if (record.subtype === \"success\") return mapResultSuccess(record);\n if (record.subtype === \"error\") return mapResultError(record);\n return null;\n}\n\nexport function mapTranscriptRecord(raw: unknown): HarnessEvent | null {\n if (!isRecord(raw)) return null;\n switch (raw.type) {\n case \"system\":\n return mapSystem(raw);\n case \"assistant\":\n return mapAssistant(raw);\n case \"result\":\n return mapResult(raw);\n default:\n return null;\n }\n}\n","/**\n * The `claude` CLI process boundary: resolve the binary, build its argv, and\n * interpret its exit when it dies before producing a result. Kept pure so it\n * can be unit-tested without a real spawn.\n */\n\nexport interface SpawnArgsInput {\n resume?: string;\n sessionId?: string;\n model: string;\n permissionMode: \"plan\" | \"bypassPermissions\";\n settingsPath: string;\n mcpConfigPath?: string;\n /** When set with mcpConfigPath, the CLI uses ONLY that config and ignores the\n * user's `~/.claude.json` / project `.mcp.json` servers. */\n strictMcpConfig?: boolean;\n}\n\nexport function resolveClaudeBinary(): string {\n return process.env.CONVEYOR_CLAUDE_BIN ?? \"claude\";\n}\n\nexport function buildSpawnArgs(input: SpawnArgsInput): string[] {\n const args: string[] = [];\n if (input.resume) {\n args.push(\"--resume\", input.resume);\n } else if (input.sessionId) {\n args.push(\"--session-id\", input.sessionId);\n }\n args.push(\"--model\", input.model);\n if (input.permissionMode === \"bypassPermissions\") {\n args.push(\"--dangerously-skip-permissions\");\n } else {\n args.push(\"--permission-mode\", \"plan\");\n }\n args.push(\"--settings\", input.settingsPath);\n if (input.mcpConfigPath) {\n args.push(\"--mcp-config\", input.mcpConfigPath);\n if (input.strictMcpConfig) {\n args.push(\"--strict-mcp-config\");\n }\n }\n return args;\n}\n\n// ─── Exit diagnostics ──────────────────────────────────────────────────────\n// The PTY harness never turns raw `claude` stdout/stderr into HarnessEvents —\n// it relays them to the S5 terminal instead. So when `claude` dies before\n// emitting a transcript result, the captured agent logs show only the generic\n// \"claude exited (code N) without a result\"; the real reason (a missing binary,\n// an auth/onboarding stop, an unknown CLI flag) scrolled past in the live\n// terminal only. These helpers fold a bounded, ANSI-stripped tail of that\n// scrollback back into the error so the failure is self-describing in the logs.\n\n// ESC (0x1B) built via fromCharCode so no control char appears as a source\n// literal (keeps the no-control-regex lint rule happy). Matches CSI sequences\n// (ESC [ ... final-byte) — the bulk of TUI escape noise; any stray ESC bytes\n// from other sequences are removed by the control-char pass below.\nconst ANSI_CSI = new RegExp(`${String.fromCharCode(27)}\\\\[[0-9;?]*[ -/]*[@-~]`, \"g\");\n\n/**\n * Strip ANSI escapes + terminal control noise and collapse to the last few\n * readable lines, capped at `maxChars`. Returns \"\" when nothing printable\n * remains (e.g. the process produced no output at all).\n */\nexport function cleanTerminalOutput(raw: string, maxChars = 1200): string {\n const noAnsi = raw.replace(ANSI_CSI, \"\");\n // Drop C0 control chars (and DEL) except tab; fold CR into newline.\n let out = \"\";\n for (const ch of noAnsi) {\n const code = ch.charCodeAt(0);\n if (ch === \"\\r\" || ch === \"\\n\") out += \"\\n\";\n else if (ch === \"\\t\") out += ch;\n else if (code < 0x20 || code === 0x7f) continue;\n else out += ch;\n }\n const lines = out\n .split(\"\\n\")\n .map((line) => line.trimEnd())\n .filter((line) => line.trim().length > 0);\n const text = lines.join(\"\\n\").trim();\n return text.length > maxChars ? `…${text.slice(-maxChars)}` : text;\n}\n\n/**\n * True when the terminal tail shows node-pty failed to exec the target binary\n * (missing `claude` CLI on PATH). node-pty prints `execvp(3) failed.: No such\n * file or directory` to the pty and the child exits with code 1.\n */\nexport function isMissingBinaryFailure(tail: string): boolean {\n return /execvp\\(\\d+\\) failed|no such file or directory|command not found/i.test(tail);\n}\n\n/**\n * Build the `errors[]` for a `claude` process that exited before emitting a\n * transcript result. The first entry is kept byte-for-byte stable so existing\n * consumers/log scrapers that key off it keep working; richer context is\n * appended after it.\n */\nexport function buildExitErrors(exitCode: number, rawOutput: string, binary: string): string[] {\n const errors = [`claude exited (code ${exitCode}) without a result`];\n const tail = cleanTerminalOutput(rawOutput);\n if (isMissingBinaryFailure(tail)) {\n errors.push(\n `The \\`${binary}\\` CLI could not be started — it is not installed or not on PATH. ` +\n `Install the Claude Code CLI in this environment (npm i -g @anthropic-ai/claude-code) ` +\n `or set CONVEYOR_CLAUDE_BIN to its absolute path.`,\n );\n }\n if (tail) {\n errors.push(`Last terminal output before exit:\\n${tail}`);\n }\n return errors;\n}\n","/**\n * Claude Code config-dir paths and per-run settings/hook materialization.\n *\n * The PTY harness writes a throwaway `settings.json` (passed via\n * `claude --settings`) wiring a PostToolUse hook that relays tool-progress\n * envelopes to a Unix socket. Writing a per-run file avoids clobbering the\n * user's real `~/.claude/settings.json`.\n */\n\nimport { mkdir, writeFile, chmod } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport function claudeConfigHome(): string {\n return process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), \".claude\");\n}\n\nexport function projectSlug(cwd: string): string {\n return cwd.replace(/\\//g, \"-\");\n}\n\nexport function sessionTranscriptPath(cwd: string, sessionId: string): string {\n return join(claudeConfigHome(), \"projects\", projectSlug(cwd), `${sessionId}.jsonl`);\n}\n\n/**\n * CJS PostToolUse hook helper. Reads the hook payload from stdin, relays a\n * tool-progress envelope to the harness socket, and prints `{continue:true}`\n * so the CLI proceeds. A short fallback timer guarantees the process exits\n * even if the socket is unavailable.\n */\nconst HOOK_HELPER_SOURCE = `\"use strict\";\nconst net = require(\"node:net\");\n\nlet raw = \"\";\nprocess.stdin.setEncoding(\"utf8\");\nprocess.stdin.on(\"data\", (chunk) => {\n raw += chunk;\n});\nprocess.stdin.on(\"end\", () => {\n let toolName = \"\";\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed.tool_name === \"string\") toolName = parsed.tool_name;\n } catch {\n toolName = \"\";\n }\n relay(toolName);\n});\n\nfunction relay(toolName) {\n const socketPath = process.env.CONVEYOR_HOOK_SOCKET;\n let done = false;\n const finish = () => {\n if (done) return;\n done = true;\n process.stdout.write(JSON.stringify({ continue: true }));\n process.exit(0);\n };\n const fallback = setTimeout(finish, 250);\n if (!socketPath) {\n clearTimeout(fallback);\n finish();\n return;\n }\n const client = net.connect(socketPath, () => {\n // PostToolUse does not supply tool duration, so elapsed_time_seconds is\n // omitted rather than reported as a misleading 0 (the field is optional).\n const line = JSON.stringify({ tool_name: toolName }) + \"\\\\n\";\n client.write(line, () => {\n client.end();\n });\n });\n client.on(\"close\", () => {\n clearTimeout(fallback);\n finish();\n });\n client.on(\"error\", () => {\n clearTimeout(fallback);\n finish();\n });\n}\n`;\n\nexport interface HookSettingsResult {\n settingsPath: string;\n helperPath: string;\n}\n\nexport async function writeHookSettings(dir: string): Promise<HookSettingsResult> {\n const helperPath = join(dir, \"hook-helper.cjs\");\n const settingsPath = join(dir, \"settings.json\");\n await mkdir(dir, { recursive: true });\n await writeFile(helperPath, HOOK_HELPER_SOURCE, \"utf8\");\n await chmod(helperPath, 0o755);\n const settings = {\n // Auto-approve every tool call so the interactive (PTY) agent never stops to\n // prompt the viewer. This only suppresses the per-tool approval prompt — it\n // does NOT relax the planning gate: in discovery/plan mode the spawn passes\n // `--permission-mode plan`, which keeps the agent read-only (no edits/commits\n // until it exits plan mode) regardless of this allow-list. In build mode the\n // spawn already bypasses prompts via `--dangerously-skip-permissions`.\n // `mcp__conveyor` is listed explicitly so the agent's Conveyor tools are\n // covered even if the `*` wildcard doesn't match MCP tool names.\n permissions: {\n allow: [\"*\", \"mcp__conveyor\"],\n },\n hooks: {\n PostToolUse: [\n {\n matcher: \"*\",\n hooks: [{ type: \"command\", command: `node ${JSON.stringify(helperPath)}` }],\n },\n ],\n },\n };\n await writeFile(settingsPath, JSON.stringify(settings, null, 2), \"utf8\");\n return { settingsPath, helperPath };\n}\n","/**\n * In-process MCP server that exposes the agent's Conveyor tools to the spawned\n * `claude` CLI over Streamable HTTP on loopback.\n *\n * The tool handlers must run IN the agent process — they call\n * `connection.call(...)` on the live task-token `AgentConnection`. So instead of\n * spawning a separate stdio MCP child and bridging calls back over a socket, we\n * serve the same in-process handlers over an HTTP MCP transport bound to\n * `127.0.0.1`, and point `claude` at it via `--mcp-config` (type: \"http\"). This\n * is the wiring the PTY harness deferred (\"S4\"); the SDK harness already gets\n * these tools in-process.\n *\n * Security: bound to loopback only (never 0.0.0.0) and gated by a random\n * per-run bearer token that `claude` echoes from the generated mcp-config.\n */\n\nimport { createServer, type Server as HttpServer } from \"node:http\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { randomBytes } from \"node:crypto\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport type { HarnessMcpServer, HarnessToolDefinition } from \"../types.js\";\nimport { PtyMcpServer } from \"./mcp-server.js\";\n\nconst LOOPBACK = \"127.0.0.1\";\n\nexport interface PtyToolServerHandle {\n /** `http://127.0.0.1:<port>/mcp` — the URL written into the mcp-config. */\n url: string;\n /** Bearer token required on every request (defense-in-depth over loopback). */\n token: string;\n}\n\nexport class PtyToolServer {\n private http: HttpServer | null = null;\n private transport: StreamableHTTPServerTransport | null = null;\n private mcp: McpServer | null = null;\n private readonly token = randomBytes(24).toString(\"base64url\");\n\n constructor(\n private readonly name: string,\n private readonly tools: HarnessToolDefinition[],\n ) {}\n\n async start(): Promise<PtyToolServerHandle> {\n const mcp = new McpServer({ name: this.name, version: \"1.0.0\" });\n // The SDK's `tool()` is heavily generic over the zod shape; our tool defs are\n // intentionally loose-typed (`schema: z.ZodRawShape`, handler returns MCP\n // content). Cast the registrar to a simpler shape to bridge the boundary\n // without widening to `any` — the SDK still validates input against `schema`.\n const register = mcp.tool.bind(mcp) as unknown as (\n name: string,\n description: string,\n schema: unknown,\n cb: (args: unknown) => unknown,\n ) => void;\n for (const tool of this.tools) {\n register(tool.name, tool.description, tool.schema, (args) => tool.handler(args));\n }\n\n const transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => randomBytes(16).toString(\"hex\"),\n enableJsonResponse: true,\n });\n await mcp.connect(transport);\n\n const server = createServer((req, res) => {\n void this.handle(req, res, transport);\n });\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(0, LOOPBACK, () => resolve());\n });\n\n const address = server.address();\n const port = address && typeof address === \"object\" ? address.port : 0;\n\n this.http = server;\n this.transport = transport;\n this.mcp = mcp;\n return { url: `http://${LOOPBACK}:${port}/mcp`, token: this.token };\n }\n\n private async handle(\n req: IncomingMessage,\n res: ServerResponse,\n transport: StreamableHTTPServerTransport,\n ): Promise<void> {\n if (req.headers.authorization !== `Bearer ${this.token}`) {\n res.writeHead(401).end();\n return;\n }\n try {\n await transport.handleRequest(req, res);\n } catch {\n if (res.headersSent) res.end();\n else res.writeHead(500).end();\n }\n }\n\n async close(): Promise<void> {\n try {\n await this.transport?.close();\n } catch {\n /* already closed */\n }\n this.transport = null;\n try {\n await this.mcp?.close();\n } catch {\n /* already closed */\n }\n this.mcp = null;\n const http = this.http;\n this.http = null;\n if (http) {\n await new Promise<void>((resolve) => {\n http.close(() => resolve());\n });\n }\n }\n}\n\nexport interface ToolServersResult {\n servers: PtyToolServer[];\n /** Path to the written `--mcp-config`, or null when there were no tools. */\n mcpConfigPath: string | null;\n}\n\n/**\n * Start one loopback HTTP MCP server per harness tool group (in practice just\n * \"conveyor\") and write the `--mcp-config` the spawned `claude` consumes. No-op\n * (null config) when the harness was constructed without tools.\n */\nexport async function startToolServers(\n mcpServers: Record<string, HarnessMcpServer>,\n tempDir: string,\n): Promise<ToolServersResult> {\n const servers: PtyToolServer[] = [];\n const config: Record<string, { type: \"http\"; url: string; headers: Record<string, string> }> = {};\n for (const [name, handle] of Object.entries(mcpServers)) {\n const tools = handle instanceof PtyMcpServer ? handle.tools : [];\n if (tools.length === 0) continue;\n const server = new PtyToolServer(name, tools);\n const { url, token } = await server.start();\n servers.push(server);\n config[name] = { type: \"http\", url, headers: { Authorization: `Bearer ${token}` } };\n }\n if (Object.keys(config).length === 0) return { servers, mcpConfigPath: null };\n const mcpConfigPath = join(tempDir, \"mcp-config.json\");\n await writeFile(mcpConfigPath, JSON.stringify({ mcpServers: config }, null, 2), \"utf8\");\n return { servers, mcpConfigPath };\n}\n","import type { HarnessToolDefinition } from \"../types.js\";\n\ntype ToolResult = Awaited<ReturnType<HarnessToolDefinition[\"handler\"]>>;\n\n/**\n * In-process MCP server handle for the PTY harness.\n *\n * S1 materializes tools as a directly-invokable in-process handle: callers\n * resolve a tool by name and invoke its handler. Wiring these tools to the\n * `claude` CLI over a live stdio MCP transport (via `--mcp-config`) is\n * deferred to S4 — the spawn layer already threads `mcpConfigPath` through\n * for that purpose.\n */\nexport class PtyMcpServer {\n constructor(\n public readonly name: string,\n public readonly tools: HarnessToolDefinition[],\n ) {}\n\n getTool(name: string): HarnessToolDefinition | undefined {\n return this.tools.find((tool) => tool.name === name);\n }\n\n invokeTool(name: string, input: unknown): Promise<ToolResult> {\n const tool = this.getTool(name);\n if (!tool) return Promise.reject(new Error(`Unknown tool: ${name}`));\n return tool.handler(input);\n }\n}\n","/**\n * Synthesizes `<configHome>/.credentials.json` from CLAUDE_CODE_OAUTH_TOKEN so\n * the interactive `claude` TUI starts authenticated in Conveyor-managed cloud\n * environments. The interactive TUI does NOT read CLAUDE_CODE_OAUTH_TOKEN —\n * that env var is honored only in headless/CI runs — so without a credentials\n * file a fresh pod's Connected TUI lands on the login-method picker.\n *\n * Safety policy: a credentials file whose `claudeAiOauth` carries a\n * refreshToken was written by a real interactive `/login` (the OAuth code flow\n * always issues one) and is never touched. Our synthesized file never has one —\n * that absence IS the \"written by Conveyor\" marker. If live verification ever\n * disproves that assumption, the fallback hardening is a sidecar marker file\n * (e.g. `<configHome>/.conveyor-credentials.json` holding a hash of the token\n * we wrote) — noted here so the next person doesn't reinvent the policy.\n *\n * Decision core is pure (planCredentialsWrite) so the overwrite policy can be\n * unit-tested without fs; the effectful wrappers never throw — a GCS-FUSE\n * hiccup must not kill PtySession.start().\n */\n\nimport { chmod, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { claudeConfigHome } from \"./settings.js\";\n\n// `claude setup-token` tokens are valid for ~1 year. The exact expiry is\n// unknowable from the token itself; claim a far-future expiresAt so the CLI\n// never decides the credential is expired and attempts a refresh it cannot\n// perform (no refreshToken). The file is re-synthesized on every spawn, so the\n// window slides.\nconst SYNTH_TOKEN_TTL_MS = 365 * 24 * 60 * 60 * 1000;\n// Rewrite our own file when it has less than this much runway left — a cheap\n// staleness guard for a long-lived file on the persistent user-home mount.\nconst REFRESH_SKEW_MS = 30 * 24 * 60 * 60 * 1000;\n\nexport function claudeCredentialsPath(): string {\n return join(claudeConfigHome(), \".credentials.json\");\n}\n\n/**\n * Only act inside Conveyor-managed cloud environments (GKE claudespace pods\n * export CLAUDESPACE_NAME in entrypoint.sh; GitHub Codespaces set\n * CODESPACE_NAME/CODESPACES) — never touch a developer's real ~/.claude.\n */\nexport function isConveyorCloudEnv(env: NodeJS.ProcessEnv = process.env): boolean {\n return Boolean(env.CLAUDESPACE_NAME || env.CODESPACE_NAME || env.CODESPACES);\n}\n\nexport interface CredentialsWriteInput {\n isCloud: boolean;\n /** CLAUDE_CODE_OAUTH_TOKEN from the environment. */\n token: string | undefined;\n /** Current file contents; null when the file is missing. */\n existingRaw: string | null;\n now: number;\n}\n\nexport type CredentialsPlan =\n | { action: \"skip\"; reason: \"not-cloud\" | \"no-token\" | \"foreign-credentials\" | \"current\" }\n | { action: \"write\"; contents: string };\n\ninterface ParsedOauth {\n accessToken: unknown;\n refreshToken: unknown;\n expiresAt: unknown;\n}\n\nfunction parseClaudeAiOauth(raw: string | null): ParsedOauth | null {\n if (!raw || raw.trim() === \"\") return null;\n try {\n const parsed: unknown = JSON.parse(raw);\n if (typeof parsed !== \"object\" || parsed === null) return null;\n const oauth = (parsed as Record<string, unknown>).claudeAiOauth;\n if (typeof oauth !== \"object\" || oauth === null) return null;\n const record = oauth as Record<string, unknown>;\n return {\n accessToken: record.accessToken,\n refreshToken: record.refreshToken,\n expiresAt: record.expiresAt,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * The synthesized shape is the empirical-iteration point: if a live pod's TUI\n * still shows the login picker, adjust scopes/subscriptionType HERE and\n * re-verify on a Claudespace diagnostic card.\n */\nfunction buildSynthesizedCredentials(token: string, now: number): string {\n return JSON.stringify({\n claudeAiOauth: {\n accessToken: token,\n expiresAt: now + SYNTH_TOKEN_TTL_MS,\n scopes: [\"user:inference\", \"user:profile\"],\n subscriptionType: \"max\",\n },\n });\n}\n\n/** Pure overwrite-policy core — see the decision table in the tests. */\nexport function planCredentialsWrite(input: CredentialsWriteInput): CredentialsPlan {\n if (!input.isCloud) return { action: \"skip\", reason: \"not-cloud\" };\n if (!input.token) return { action: \"skip\", reason: \"no-token\" };\n\n const contents = buildSynthesizedCredentials(input.token, input.now);\n const existing = parseClaudeAiOauth(input.existingRaw);\n // Missing, empty, unparseable, or shapeless file — ours to (re)write.\n if (!existing) return { action: \"write\", contents };\n // A refreshToken means a genuine interactive /login — never clobber.\n if (typeof existing.refreshToken === \"string\" && existing.refreshToken.length > 0) {\n return { action: \"skip\", reason: \"foreign-credentials\" };\n }\n const fresh =\n existing.accessToken === input.token &&\n typeof existing.expiresAt === \"number\" &&\n existing.expiresAt > input.now + REFRESH_SKEW_MS;\n // Skip when current to avoid write churn on the GCS-FUSE mount.\n if (fresh) return { action: \"skip\", reason: \"current\" };\n return { action: \"write\", contents };\n}\n\nasync function readRaw(path: string): Promise<string | null> {\n try {\n return await readFile(path, \"utf8\");\n } catch {\n return null;\n }\n}\n\n/**\n * Materialize CLAUDE_CODE_OAUTH_TOKEN as a credentials file for the\n * interactive TUI. Best-effort: logs and returns on any failure.\n */\nexport async function ensureClaudeCredentials(env: NodeJS.ProcessEnv = process.env): Promise<void> {\n try {\n const path = claudeCredentialsPath();\n const plan = planCredentialsWrite({\n isCloud: isConveyorCloudEnv(env),\n token: env.CLAUDE_CODE_OAUTH_TOKEN,\n existingRaw: await readRaw(path),\n now: Date.now(),\n });\n if (plan.action === \"skip\") return;\n await mkdir(claudeConfigHome(), { recursive: true });\n await writeFile(path, plan.contents, { encoding: \"utf8\", mode: 0o600 });\n // The GCS-FUSE CSI mount (file-mode=700) may reject chmod; it already\n // enforces owner-only access, so this is belt-and-braces for local disks.\n await chmod(path, 0o600).catch(() => {});\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[conveyor-agent] claude credentials sync failed: ${message}\\n`);\n }\n}\n\n/**\n * Delete the credentials file only when it is ours (no refreshToken). Used\n * when a runtime key update switches the agent to ANTHROPIC_API_KEY — stale\n * subscription credentials must not keep authenticating the next TUI spawn.\n */\nexport async function removeConveyorCredentials(\n env: NodeJS.ProcessEnv = process.env,\n): Promise<void> {\n try {\n if (!isConveyorCloudEnv(env)) return;\n const path = claudeCredentialsPath();\n const existing = parseClaudeAiOauth(await readRaw(path));\n if (existing && typeof existing.refreshToken === \"string\" && existing.refreshToken.length > 0) {\n return;\n }\n await rm(path, { force: true });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[conveyor-agent] claude credentials removal failed: ${message}\\n`);\n }\n}\n","/**\n * PtyHarness — drives the `claude` CLI under a pseudo-terminal behind the\n * generic `AgentHarness` interface. Selected at runtime via\n * `createHarness(\"pty\")`. As of conveyor-agent v8 this is the default for the\n * task chat path; set `CONVEYOR_HARNESS=sdk` to roll back to the SDK harness.\n */\n\nimport type {\n AgentHarness,\n HarnessEvent,\n HarnessQueryOptions,\n HarnessToolDefinition,\n HarnessMcpServer,\n HarnessUserMessage,\n PtyBridge,\n} from \"../types.js\";\nimport { PtySession } from \"./session.js\";\nimport { PtyMcpServer } from \"./mcp-server.js\";\nimport { ensureClaudeCredentials } from \"./credentials.js\";\n\nexport { PtySession, PtyMcpServer };\n\nexport class PtyHarness implements AgentHarness {\n /**\n * `bridge` relays raw terminal I/O to/from the S2 server (and on to the S5\n * terminal). It is undefined for SDK-only callers and PTY runs that never\n * attach a relay; the session simply discards stdout in that case.\n */\n constructor(private readonly bridge?: PtyBridge) {}\n\n async *executeQuery(opts: {\n prompt: string | AsyncGenerator<HarnessUserMessage, void, unknown>;\n options: HarnessQueryOptions;\n resume?: string;\n }): AsyncGenerator<HarnessEvent, void> {\n const session = new PtySession(\n opts.prompt,\n opts.options,\n opts.resume ?? opts.options.resume,\n this.bridge,\n );\n // The interactive TUI ignores CLAUDE_CODE_OAUTH_TOKEN (headless-only); in\n // Conveyor cloud envs, materialize it as `<configHome>/.credentials.json`\n // so the CLI starts authenticated instead of at the login-method picker.\n // Every PTY spawn (fresh, follow-up, resume, retry, mode restart) flows\n // through here — this is the single PtySession construction site.\n await ensureClaudeCredentials();\n await session.start();\n try {\n for await (const event of session.events()) {\n yield event;\n }\n } finally {\n await session.teardown();\n }\n }\n\n createMcpServer(config: { name: string; tools: HarnessToolDefinition[] }): HarnessMcpServer {\n return new PtyMcpServer(config.name, config.tools);\n }\n}\n","export { defineTool } from \"./types.js\";\n\nexport type {\n AgentHarness,\n HarnessEvent,\n HarnessSystemEvent,\n HarnessSystemInitEvent,\n HarnessCompactBoundaryEvent,\n HarnessTaskStartedEvent,\n HarnessTaskProgressEvent,\n HarnessAssistantEvent,\n HarnessContentBlock,\n HarnessResultEvent,\n HarnessResultSuccessEvent,\n HarnessResultErrorEvent,\n HarnessRateLimitEvent,\n HarnessToolProgressEvent,\n HarnessUserMessage,\n HarnessToolDefinition,\n HarnessToolAnnotations,\n HarnessMcpServer,\n HarnessQueryOptions,\n HarnessHookInput,\n HarnessHookOutput,\n HarnessPostToolUseHook,\n PtyBridge,\n} from \"./types.js\";\n\nexport { ClaudeCodeHarness } from \"./claude-code/index.js\";\nexport { PtyHarness } from \"./pty/index.js\";\n\nimport { ClaudeCodeHarness } from \"./claude-code/index.js\";\nimport { PtyHarness } from \"./pty/index.js\";\nimport type { AgentHarness, PtyBridge } from \"./types.js\";\n\nexport type HarnessKind = \"sdk\" | \"pty\";\n\n/**\n * Pick the harness implementation. `ptyBridge` is only consumed by the PTY\n * harness (to stream stdout to the S2 relay and receive keystrokes/resize);\n * the SDK harness ignores it.\n */\nexport function createHarness(kind: HarnessKind = \"sdk\", ptyBridge?: PtyBridge): AgentHarness {\n return kind === \"pty\" ? new PtyHarness(ptyBridge) : new ClaudeCodeHarness();\n}\n","/** Minimal structured logger for conveyor-agent (writes to stderr). */\nexport function createServiceLogger(service: string) {\n const prefix = `[conveyor-agent:${service}]`;\n return {\n info(message: string, data?: Record<string, unknown>): void {\n const extra = data ? ` ${JSON.stringify(data)}` : \"\";\n process.stderr.write(`${prefix} ${message}${extra}\\n`);\n },\n warn(message: string, data?: Record<string, unknown>): void {\n const extra = data ? ` ${JSON.stringify(data)}` : \"\";\n process.stderr.write(`${prefix} WARN ${message}${extra}\\n`);\n },\n error(message: string, data?: Record<string, unknown>): void {\n const extra = data ? ` ${JSON.stringify(data)}` : \"\";\n process.stderr.write(`${prefix} ERROR ${message}${extra}\\n`);\n },\n };\n}\n"],"mappings":";AA+NO,SAAS,WACd,MACA,aACA,QACA,SAGA,SACuB;AACvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,SAAS;AAAA,EACxB;AACF;;;ACxOA,SAAS,OAAO,MAAM,0BAA0B;AAUzC,IAAM,oBAAN,MAAgD;AAAA,EACrD,OAAO,aAAa,MAImB;AACrC,UAAM,YAAY,MAAM;AAAA,MACtB,QAAQ,KAAK;AAAA,MACb,SAAS;AAAA,QACP,GAAI,KAAK;AAAA,QACT,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,QAC7C,GAAI,KAAK,QAAQ,YAAY,EAAE,WAAW,KAAK,QAAQ,UAAU,IAAI,CAAC;AAAA,QACtE,GAAI,KAAK,QAAQ,kBAAkB,EAAE,iBAAiB,KAAK,QAAQ,gBAAgB,IAAI,CAAC;AAAA,MAC1F;AAAA,IACF,CAAC;AAED,qBAAiB,SAAS,WAAW;AACnC,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,gBAAgB,QAA4E;AAC1F,UAAM,WAAW,OAAO,MAAM;AAAA,MAAI,CAAC,MACjC;AAAA,QACE,EAAE;AAAA,QACF,EAAE;AAAA,QACF,EAAE;AAAA,QACF,EAAE;AAAA,QACF,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,IAAI;AAAA,MACnD;AAAA,IACF;AACA,WAAO,mBAAmB,EAAE,MAAM,OAAO,MAAM,OAAO,SAAS,CAAC;AAAA,EAClE;AACF;;;ACjCA,SAAS,SAAS,SAAAA,QAAO,IAAI,YAAY;AACzC,SAAS,cAAc;AACvB,SAAS,QAAAC,OAAM,eAAe;;;ACdvB,IAAM,kBAAN,MAAyB;AAAA,EACb,QAAa,CAAC;AAAA,EACvB,SAAS;AAAA,EACT,OAA4B;AAAA,EAEpC,KAAK,MAAe;AAClB,QAAI,KAAK,OAAQ;AACjB,SAAK,MAAM,KAAK,IAAI;AACpB,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,IAAI,WAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,SAAe;AACrB,UAAM,OAAO,KAAK;AAClB,QAAI,MAAM;AACR,WAAK,OAAO;AACZ,WAAK;AAAA,IACP;AAAA,EACF;AAAA,EAEA,OAAO,QAAiC;AACtC,WAAO,CAAC,KAAK,UAAU,KAAK,MAAM,SAAS,GAAG;AAC5C,UAAI,KAAK,MAAM,SAAS,GAAG;AACzB,cAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,YAAI,SAAS,OAAW,OAAM;AAC9B;AAAA,MACF;AACA,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,aAAK,OAAO;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;ACrCA,SAAS,oBAA8C;AACvD,SAAS,cAAc;AAOhB,SAAS,cAAc,MAAmC;AAC/D,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAC1D,QAAM,SAAS;AACf,QAAM,SAAuB,CAAC;AAC9B,MAAI,OAAO,OAAO,cAAc,SAAU,QAAO,YAAY,OAAO;AACpE,MAAI,OAAO,OAAO,yBAAyB,UAAU;AACnD,WAAO,uBAAuB,OAAO;AAAA,EACvC;AACA,SAAO;AACT;AAEO,IAAM,mBAAN,MAAuB;AAAA,EAK5B,YACkB,YACC,YACjB;AAFgB;AACC;AAAA,EAChB;AAAA,EAFe;AAAA,EACC;AAAA,EANX,SAAwB;AAAA,EACxB,SAAS;AAAA,EACT,UAAU;AAAA,EAOlB,MAAM,SAAwB;AAC5B,UAAM,OAAO,KAAK,UAAU,EAAE,MAAM,MAAM,MAAS;AACnD,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,SAAS,aAAa,CAAC,WAAW,KAAK,iBAAiB,MAAM,CAAC;AACrE,aAAO,GAAG,SAAS,MAAM;AACzB,aAAO,OAAO,KAAK,YAAY,MAAM;AACnC,gBAAQ;AAAA,MACV,CAAC;AACD,WAAK,SAAS;AAAA,IAChB,CAAC;AAAA,EACH;AAAA,EAEA,IAAI,WAAoB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,QAAS;AAClB,SAAK,UAAU;AACf,UAAM,SAAS,KAAK;AACpB,SAAK,SAAS;AACd,QAAI,QAAQ;AACV,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,eAAO,MAAM,MAAM;AACjB,kBAAQ;AAAA,QACV,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AACA,UAAM,OAAO,KAAK,UAAU,EAAE,MAAM,MAAM,MAAS;AAAA,EACrD;AAAA,EAEQ,iBAAiB,QAAsB;AAI7C,WAAO,GAAG,SAAS,MAAM,MAAS;AAClC,WAAO,GAAG,QAAQ,CAAC,UAAkB;AACnC,WAAK,UAAU,MAAM,SAAS,MAAM;AACpC,WAAK,OAAO;AAAA,IACd,CAAC;AAAA,EACH;AAAA,EAEQ,SAAe;AACrB,QAAI,QAAQ,KAAK,OAAO,QAAQ,IAAI;AACpC,WAAO,SAAS,GAAG;AACjB,YAAM,OAAO,KAAK,OAAO,MAAM,GAAG,KAAK;AACvC,WAAK,SAAS,KAAK,OAAO,MAAM,QAAQ,CAAC;AACzC,YAAM,WAAW,cAAc,IAAI;AACnC,UAAI,SAAU,MAAK,WAAW,QAAQ;AACtC,cAAQ,KAAK,OAAO,QAAQ,IAAI;AAAA,IAClC;AAAA,EACF;AACF;;;ACtFA,SAAS,YAAY;;;ACUrB,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAGA,SAAS,eAAe,OAAoC;AAC1D,SAAO,MAAM,QAAQ,KAAK;AAC5B;AAEA,SAAS,YAAY,WAAoC,MAAoC;AAC3F,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,OAAO,UAAU,SAAU,QAAO;AAAA,EACxC;AACA,SAAO;AACT;AAEA,SAAS,YAAY,WAAoC,MAAoC;AAC3F,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,OAAO,GAAG;AACxB,QAAI,OAAO,UAAU,SAAU,QAAO;AAAA,EACxC;AACA,SAAO;AACT;AAEA,SAAS,UAAU,QAAgE;AACjF,MAAI,OAAO,YAAY,OAAQ,QAAO;AACtC,QAAM,QAAgC;AAAA,IACpC,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO,YAAY,QAAQ,OAAO,KAAK;AAAA,EACzC;AACA,QAAM,YAAY,YAAY,QAAQ,cAAc,WAAW;AAC/D,MAAI,cAAc,OAAW,OAAM,aAAa;AAChD,SAAO;AACT;AAIA,SAAS,SAAS,SAA8D;AAC9E,QAAM,QAAQ,QAAQ;AACtB,MAAI,CAAC,SAAS,KAAK,EAAG,QAAO;AAC7B,QAAM,SAAyB,CAAC;AAChC,QAAM,QAAQ,YAAY,OAAO,cAAc;AAC/C,QAAM,YAAY,YAAY,OAAO,yBAAyB;AAC9D,QAAM,gBAAgB,YAAY,OAAO,6BAA6B;AACtE,MAAI,UAAU,OAAW,QAAO,eAAe;AAC/C,MAAI,cAAc,OAAW,QAAO,0BAA0B;AAC9D,MAAI,kBAAkB,OAAW,QAAO,8BAA8B;AACtE,SAAO;AACT;AAEA,SAAS,gBAAgB,KAA0C;AACjE,MAAI,CAAC,SAAS,GAAG,EAAG,QAAO;AAC3B,QAAM,OAAO,YAAY,KAAK,MAAM;AACpC,MAAI,SAAS,OAAW,QAAO;AAC/B,QAAM,QAA6B,EAAE,KAAK;AAC1C,QAAM,OAAO,YAAY,KAAK,MAAM;AACpC,MAAI,SAAS,OAAW,OAAM,OAAO;AACrC,QAAM,OAAO,YAAY,KAAK,MAAM;AACpC,MAAI,SAAS,OAAW,OAAM,OAAO;AACrC,QAAM,KAAK,YAAY,KAAK,IAAI;AAChC,MAAI,OAAO,OAAW,OAAM,KAAK;AACjC,MAAI,WAAW,IAAK,OAAM,QAAQ,IAAI;AACtC,SAAO;AACT;AAEA,SAAS,aAAa,QAA+D;AACnF,QAAM,UAAU,OAAO;AACvB,MAAI,CAAC,SAAS,OAAO,EAAG,QAAO;AAC/B,QAAM,aAAwB,eAAe,QAAQ,OAAO,IAAI,QAAQ,UAAU,CAAC;AACnF,QAAM,UAAiC,CAAC;AACxC,aAAW,QAAQ,YAAY;AAC7B,UAAM,QAAQ,gBAAgB,IAAI;AAClC,QAAI,MAAO,SAAQ,KAAK,KAAK;AAAA,EAC/B;AACA,QAAM,SAAgC;AAAA,IACpC,MAAM;AAAA,IACN,SAAS,EAAE,MAAM,aAAa,QAAQ;AAAA,EACxC;AACA,QAAM,QAAQ,SAAS,OAAO;AAC9B,MAAI,UAAU,OAAW,QAAO,QAAQ,QAAQ;AAChD,SAAO;AACT;AAEA,SAAS,iBAAiB,QAAqD;AAC7E,QAAM,QAA4B;AAAA,IAChC,MAAM;AAAA,IACN,SAAS;AAAA,IACT,QAAQ,YAAY,QAAQ,QAAQ,KAAK;AAAA,IACzC,gBAAgB,YAAY,QAAQ,kBAAkB,cAAc,KAAK;AAAA,EAC3E;AACA,QAAM,aAAa,OAAO;AAC1B,MAAI,SAAS,UAAU,EAAG,OAAM,aAAa;AAC7C,QAAM,YAAY,YAAY,QAAQ,cAAc,WAAW;AAC/D,MAAI,cAAc,OAAW,OAAM,YAAY;AAC/C,SAAO;AACT;AAEA,SAAS,eAAe,QAAqD;AAC3E,QAAM,YAAY,eAAe,OAAO,MAAM,IAAI,OAAO,SAAS,CAAC;AACnE,QAAM,SAAS,UAAU,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AACzE,QAAM,QAA4B,EAAE,MAAM,UAAU,SAAS,SAAS,OAAO;AAC7E,QAAM,YAAY,YAAY,QAAQ,cAAc,WAAW;AAC/D,MAAI,cAAc,OAAW,OAAM,YAAY;AAC/C,SAAO;AACT;AAEA,SAAS,UAAU,QAA4D;AAC7E,MAAI,OAAO,YAAY,UAAW,QAAO,iBAAiB,MAAM;AAChE,MAAI,OAAO,YAAY,QAAS,QAAO,eAAe,MAAM;AAC5D,SAAO;AACT;AAEO,SAAS,oBAAoB,KAAmC;AACrE,MAAI,CAAC,SAAS,GAAG,EAAG,QAAO;AAC3B,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AACH,aAAO,UAAU,GAAG;AAAA,IACtB,KAAK;AACH,aAAO,aAAa,GAAG;AAAA,IACzB,KAAK;AACH,aAAO,UAAU,GAAG;AAAA,IACtB;AACE,aAAO;AAAA,EACX;AACF;;;ADpIA,IAAM,mBAAmB;AAElB,IAAM,cAAN,MAAkB;AAAA,EAOvB,YACmB,MACA,SACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EARX,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAA+C;AAAA,EAC/C,QAAuB,QAAQ,QAAQ;AAAA,EACvC,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,EAWlB,MAAM,aAAa,GAAS;AAC1B,QAAI,KAAK,MAAO;AAChB,SAAK,SAAS;AACd,SAAK,QAAQ,YAAY,MAAM;AAC7B,UAAI,KAAK,QAAS;AAClB,WAAK,KAAK,YAAY;AAAA,IACxB,GAAG,gBAAgB;AAAA,EACrB;AAAA,EAEA,QAAc;AACZ,SAAK,UAAU;AACf,QAAI,KAAK,OAAO;AACd,oBAAc,KAAK,KAAK;AACxB,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,QAAuB;AAC3B,UAAM,KAAK,YAAY;AACvB,QAAI,KAAK,OAAO,SAAS,GAAG;AAC1B,WAAK,SAAS,KAAK,MAAM;AACzB,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AAAA,EAEQ,cAA6B;AACnC,SAAK,QAAQ,KAAK,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AAClD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,WAA0B;AACtC,QAAI,SAAkD;AACtD,QAAI;AACF,eAAS,MAAM,KAAK,KAAK,MAAM,GAAG;AAClC,YAAM,QAAQ,MAAM,OAAO,KAAK;AAChC,UAAI,MAAM,QAAQ,KAAK,OAAQ;AAC/B,YAAM,SAAS,MAAM,OAAO,KAAK;AACjC,YAAM,MAAM,OAAO,MAAM,MAAM;AAC/B,YAAM,OAAO,KAAK,KAAK,GAAG,QAAQ,KAAK,MAAM;AAC7C,WAAK,SAAS,MAAM;AACpB,WAAK,QAAQ,IAAI,SAAS,MAAM,CAAC;AAAA,IACnC,QAAQ;AAAA,IAER,UAAE;AACA,UAAI,OAAQ,OAAM,OAAO,MAAM;AAAA,IACjC;AAAA,EACF;AAAA,EAEQ,QAAQ,OAAqB;AACnC,SAAK,UAAU;AACf,QAAI,QAAQ,KAAK,OAAO,QAAQ,IAAI;AACpC,WAAO,SAAS,GAAG;AACjB,YAAM,OAAO,KAAK,OAAO,MAAM,GAAG,KAAK;AACvC,WAAK,SAAS,KAAK,OAAO,MAAM,QAAQ,CAAC;AACzC,WAAK,SAAS,IAAI;AAClB,cAAQ,KAAK,OAAO,QAAQ,IAAI;AAAA,IAClC;AAAA,EACF;AAAA,EAEQ,SAAS,MAAoB;AACnC,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,QAAQ,WAAW,EAAG;AAC1B,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,OAAO;AAAA,IAC7B,QAAQ;AACN;AAAA,IACF;AACA,UAAM,QAAQ,oBAAoB,MAAM;AACxC,QAAI,MAAO,MAAK,QAAQ,KAAK;AAAA,EAC/B;AACF;;;AErFO,SAAS,sBAA8B;AAC5C,SAAO,QAAQ,IAAI,uBAAuB;AAC5C;AAEO,SAAS,eAAe,OAAiC;AAC9D,QAAM,OAAiB,CAAC;AACxB,MAAI,MAAM,QAAQ;AAChB,SAAK,KAAK,YAAY,MAAM,MAAM;AAAA,EACpC,WAAW,MAAM,WAAW;AAC1B,SAAK,KAAK,gBAAgB,MAAM,SAAS;AAAA,EAC3C;AACA,OAAK,KAAK,WAAW,MAAM,KAAK;AAChC,MAAI,MAAM,mBAAmB,qBAAqB;AAChD,SAAK,KAAK,gCAAgC;AAAA,EAC5C,OAAO;AACL,SAAK,KAAK,qBAAqB,MAAM;AAAA,EACvC;AACA,OAAK,KAAK,cAAc,MAAM,YAAY;AAC1C,MAAI,MAAM,eAAe;AACvB,SAAK,KAAK,gBAAgB,MAAM,aAAa;AAC7C,QAAI,MAAM,iBAAiB;AACzB,WAAK,KAAK,qBAAqB;AAAA,IACjC;AAAA,EACF;AACA,SAAO;AACT;AAeA,IAAM,WAAW,IAAI,OAAO,GAAG,OAAO,aAAa,EAAE,CAAC,0BAA0B,GAAG;AAO5E,SAAS,oBAAoB,KAAa,WAAW,MAAc;AACxE,QAAM,SAAS,IAAI,QAAQ,UAAU,EAAE;AAEvC,MAAI,MAAM;AACV,aAAW,MAAM,QAAQ;AACvB,UAAM,OAAO,GAAG,WAAW,CAAC;AAC5B,QAAI,OAAO,QAAQ,OAAO,KAAM,QAAO;AAAA,aAC9B,OAAO,IAAM,QAAO;AAAA,aACpB,OAAO,MAAQ,SAAS,IAAM;AAAA,QAClC,QAAO;AAAA,EACd;AACA,QAAM,QAAQ,IACX,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,EAC5B,OAAO,CAAC,SAAS,KAAK,KAAK,EAAE,SAAS,CAAC;AAC1C,QAAM,OAAO,MAAM,KAAK,IAAI,EAAE,KAAK;AACnC,SAAO,KAAK,SAAS,WAAW,SAAI,KAAK,MAAM,CAAC,QAAQ,CAAC,KAAK;AAChE;AAOO,SAAS,uBAAuB,MAAuB;AAC5D,SAAO,oEAAoE,KAAK,IAAI;AACtF;AAQO,SAAS,gBAAgB,UAAkB,WAAmB,QAA0B;AAC7F,QAAM,SAAS,CAAC,uBAAuB,QAAQ,oBAAoB;AACnE,QAAM,OAAO,oBAAoB,SAAS;AAC1C,MAAI,uBAAuB,IAAI,GAAG;AAChC,WAAO;AAAA,MACL,SAAS,MAAM;AAAA,IAGjB;AAAA,EACF;AACA,MAAI,MAAM;AACR,WAAO,KAAK;AAAA,EAAsC,IAAI,EAAE;AAAA,EAC1D;AACA,SAAO;AACT;;;ACxGA,SAAS,OAAO,WAAW,aAAa;AACxC,SAAS,eAAe;AACxB,SAAS,YAAY;AAEd,SAAS,mBAA2B;AACzC,SAAO,QAAQ,IAAI,qBAAqB,KAAK,QAAQ,GAAG,SAAS;AACnE;AAEO,SAAS,YAAY,KAAqB;AAC/C,SAAO,IAAI,QAAQ,OAAO,GAAG;AAC/B;AAEO,SAAS,sBAAsB,KAAa,WAA2B;AAC5E,SAAO,KAAK,iBAAiB,GAAG,YAAY,YAAY,GAAG,GAAG,GAAG,SAAS,QAAQ;AACpF;AAQA,IAAM,qBAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0D3B,eAAsB,kBAAkB,KAA0C;AAChF,QAAM,aAAa,KAAK,KAAK,iBAAiB;AAC9C,QAAM,eAAe,KAAK,KAAK,eAAe;AAC9C,QAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,QAAM,UAAU,YAAY,oBAAoB,MAAM;AACtD,QAAM,MAAM,YAAY,GAAK;AAC7B,QAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASf,aAAa;AAAA,MACX,OAAO,CAAC,KAAK,eAAe;AAAA,IAC9B;AAAA,IACA,OAAO;AAAA,MACL,aAAa;AAAA,QACX;AAAA,UACE,SAAS;AAAA,UACT,OAAO,CAAC,EAAE,MAAM,WAAW,SAAS,QAAQ,KAAK,UAAU,UAAU,CAAC,GAAG,CAAC;AAAA,QAC5E;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,UAAU,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,MAAM;AACvE,SAAO,EAAE,cAAc,WAAW;AACpC;;;ACtGA,SAAS,gBAAAC,qBAA+C;AAExD,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,QAAAC,aAAY;AACrB,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB;AAC1B,SAAS,qCAAqC;;;ACTvC,IAAM,eAAN,MAAmB;AAAA,EACxB,YACkB,MACA,OAChB;AAFgB;AACA;AAAA,EACf;AAAA,EAFe;AAAA,EACA;AAAA,EAGlB,QAAQ,MAAiD;AACvD,WAAO,KAAK,MAAM,KAAK,CAACC,UAASA,MAAK,SAAS,IAAI;AAAA,EACrD;AAAA,EAEA,WAAW,MAAc,OAAqC;AAC5D,UAAMA,QAAO,KAAK,QAAQ,IAAI;AAC9B,QAAI,CAACA,MAAM,QAAO,QAAQ,OAAO,IAAI,MAAM,iBAAiB,IAAI,EAAE,CAAC;AACnE,WAAOA,MAAK,QAAQ,KAAK;AAAA,EAC3B;AACF;;;ADFA,IAAM,WAAW;AASV,IAAM,gBAAN,MAAoB;AAAA,EAMzB,YACmB,MACA,OACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAPX,OAA0B;AAAA,EAC1B,YAAkD;AAAA,EAClD,MAAwB;AAAA,EACf,QAAQ,YAAY,EAAE,EAAE,SAAS,WAAW;AAAA,EAO7D,MAAM,QAAsC;AAC1C,UAAM,MAAM,IAAI,UAAU,EAAE,MAAM,KAAK,MAAM,SAAS,QAAQ,CAAC;AAK/D,UAAM,WAAW,IAAI,KAAK,KAAK,GAAG;AAMlC,eAAWC,SAAQ,KAAK,OAAO;AAC7B,eAASA,MAAK,MAAMA,MAAK,aAAaA,MAAK,QAAQ,CAAC,SAASA,MAAK,QAAQ,IAAI,CAAC;AAAA,IACjF;AAEA,UAAM,YAAY,IAAI,8BAA8B;AAAA,MAClD,oBAAoB,MAAM,YAAY,EAAE,EAAE,SAAS,KAAK;AAAA,MACxD,oBAAoB;AAAA,IACtB,CAAC;AACD,UAAM,IAAI,QAAQ,SAAS;AAE3B,UAAM,SAASC,cAAa,CAAC,KAAK,QAAQ;AACxC,WAAK,KAAK,OAAO,KAAK,KAAK,SAAS;AAAA,IACtC,CAAC;AACD,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,aAAO,KAAK,SAAS,MAAM;AAC3B,aAAO,OAAO,GAAG,UAAU,MAAM,QAAQ,CAAC;AAAA,IAC5C,CAAC;AAED,UAAM,UAAU,OAAO,QAAQ;AAC/B,UAAM,OAAO,WAAW,OAAO,YAAY,WAAW,QAAQ,OAAO;AAErE,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,SAAK,MAAM;AACX,WAAO,EAAE,KAAK,UAAU,QAAQ,IAAI,IAAI,QAAQ,OAAO,KAAK,MAAM;AAAA,EACpE;AAAA,EAEA,MAAc,OACZ,KACA,KACA,WACe;AACf,QAAI,IAAI,QAAQ,kBAAkB,UAAU,KAAK,KAAK,IAAI;AACxD,UAAI,UAAU,GAAG,EAAE,IAAI;AACvB;AAAA,IACF;AACA,QAAI;AACF,YAAM,UAAU,cAAc,KAAK,GAAG;AAAA,IACxC,QAAQ;AACN,UAAI,IAAI,YAAa,KAAI,IAAI;AAAA,UACxB,KAAI,UAAU,GAAG,EAAE,IAAI;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI;AACF,YAAM,KAAK,WAAW,MAAM;AAAA,IAC9B,QAAQ;AAAA,IAER;AACA,SAAK,YAAY;AACjB,QAAI;AACF,YAAM,KAAK,KAAK,MAAM;AAAA,IACxB,QAAQ;AAAA,IAER;AACA,SAAK,MAAM;AACX,UAAM,OAAO,KAAK;AAClB,SAAK,OAAO;AACZ,QAAI,MAAM;AACR,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,aAAK,MAAM,MAAM,QAAQ,CAAC;AAAA,MAC5B,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAaA,eAAsB,iBACpB,YACA,SAC4B;AAC5B,QAAM,UAA2B,CAAC;AAClC,QAAM,SAAyF,CAAC;AAChG,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,UAAU,GAAG;AACvD,UAAM,QAAQ,kBAAkB,eAAe,OAAO,QAAQ,CAAC;AAC/D,QAAI,MAAM,WAAW,EAAG;AACxB,UAAM,SAAS,IAAI,cAAc,MAAM,KAAK;AAC5C,UAAM,EAAE,KAAK,MAAM,IAAI,MAAM,OAAO,MAAM;AAC1C,YAAQ,KAAK,MAAM;AACnB,WAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,KAAK,SAAS,EAAE,eAAe,UAAU,KAAK,GAAG,EAAE;AAAA,EACpF;AACA,MAAI,OAAO,KAAK,MAAM,EAAE,WAAW,EAAG,QAAO,EAAE,SAAS,eAAe,KAAK;AAC5E,QAAM,gBAAgBC,MAAK,SAAS,iBAAiB;AACrD,QAAMC,WAAU,eAAe,KAAK,UAAU,EAAE,YAAY,OAAO,GAAG,MAAM,CAAC,GAAG,MAAM;AACtF,SAAO,EAAE,SAAS,cAAc;AAClC;;;AP3HA,IAAM,wBAAwB;AAoB9B,SAASC,UAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,aAAa,KAA+B;AACnD,MAAI,CAACA,UAAS,GAAG,EAAG,QAAO;AAC3B,MAAI,OAAO,IAAI,UAAU,WAAY,QAAO,IAAI;AAChD,QAAM,MAAM,IAAI;AAChB,MAAIA,UAAS,GAAG,KAAK,OAAO,IAAI,UAAU,WAAY,QAAO,IAAI;AACjE,SAAO;AACT;AAEA,eAAe,eAAkC;AAC/C,QAAM,MAAe,MAAM,OAAO,UAAU;AAC5C,QAAM,QAAQ,aAAa,GAAG;AAC9B,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,kCAAkC;AAC9D,SAAO;AACT;AAEO,SAAS,aAAa,YAA4C;AACvE,QAAM,MAA8B,CAAC;AACrC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG,GAAG;AACtD,QAAI,OAAO,UAAU,SAAU,KAAI,GAAG,IAAI;AAAA,EAC5C;AAQA,MAAI,uBAAuB;AAC3B,SAAO;AACT;AAUO,SAAS,iBAAiB,MAAc,UAAyC;AACtF,QAAM,QAAQ,YAAY,IAAI;AAC9B,SAAO,aAAa,YAAY,QAAQ,GAAG,KAAK;AAClD;AAGA,eAAe,eAAe,MAA+B;AAC3D,MAAI;AACF,YAAQ,MAAM,KAAK,IAAI,GAAG;AAAA,EAC5B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,aAAN,MAAiB;AAAA,EAwBtB,YACmB,QACA,SACA,QACA,QACjB;AAJiB;AACA;AACA;AACA;AAAA,EAChB;AAAA,EAJgB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EA3BF,QAAQ,IAAI,gBAA8B;AAAA,EACnD,SAAkC;AAAA,EAClC,SAA6B;AAAA,EAC7B,MAAyB;AAAA,EACzB,UAAU;AAAA,EACV,YAAY;AAAA;AAAA;AAAA;AAAA,EAIZ,eAAe;AAAA,EACf,YAAY;AAAA,EACH,gBAA4C,CAAC;AAAA,EAC7C,gBAAgC,CAAC;AAAA,EAC1C,eAAoC;AAAA,EACpC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,aAAkC;AAAA,EAClC,cAAmC;AAAA;AAAA;AAAA,EAGnC,cAA+B,CAAC;AAAA,EAChC,gBAA+B;AAAA,EASvC,OAAO,UAA4B;AACjC,SAAK,cAAc,KAAK,QAAQ;AAAA,EAClC;AAAA,EAEA,OAAO,UAAwC;AAC7C,SAAK,cAAc,KAAK,QAAQ;AAAA,EAClC;AAAA,EAEA,IAAI,aAAsB;AACxB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,iBAAgC;AAClC,WAAO,KAAK,SAAS,KAAK,OAAO,aAAa;AAAA,EAChD;AAAA,EAEA,SAA6C;AAC3C,WAAO,KAAK,MAAM,MAAM;AAAA,EAC1B;AAAA,EAEA,MAAM,QAAuB;AAG3B,UAAM,YAAY,KAAK,UAAU,KAAK,QAAQ;AAC9C,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,0DAA0D;AAAA,IAC5E;AAMA,UAAM,SAAS,KAAK,QAAQ,iBAAiB;AAC7C,QAAI,QAAQ,SAAS;AAGnB,YAAM,KAAK,SAAS;AACpB;AAAA,IACF;AACA,QAAI;AACF,WAAK,UAAU,MAAM,QAAQC,MAAK,OAAO,GAAG,eAAe,CAAC;AAC5D,YAAM,aAAaA,MAAK,KAAK,SAAS,WAAW;AACjD,WAAK,SAAS,IAAI,iBAAiB,YAAY,CAAC,aAAa,KAAK,eAAe,QAAQ,CAAC;AAC1F,YAAM,KAAK,OAAO,OAAO;AACzB,YAAM,EAAE,aAAa,IAAI,MAAM,kBAAkB,KAAK,OAAO;AAG7D,YAAM,KAAK,iBAAiB;AAC5B,YAAM,iBAAiB,sBAAsB,KAAK,QAAQ,KAAK,SAAS;AACxE,YAAMC,OAAM,QAAQ,cAAc,GAAG,EAAE,WAAW,KAAK,CAAC;AAGxD,YAAM,cAAc,KAAK,SAAS,MAAM,eAAe,cAAc,IAAI;AACzE,WAAK,SAAS,IAAI,YAAY,gBAAgB,CAAC,UAAU,KAAK,sBAAsB,KAAK,CAAC;AAC1F,WAAK,OAAO,MAAM,WAAW;AAC7B,YAAM,KAAK,MAAM,cAAc,UAAU;AAIzC,UAAI,QAAQ;AACV,aAAK,eAAe,MAAM;AACxB,eAAK,KAAK,SAAS;AAAA,QACrB;AACA,eAAO,iBAAiB,SAAS,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAClE,YAAI,OAAO,SAAS;AAClB,gBAAM,KAAK,SAAS;AACpB;AAAA,QACF;AAAA,MACF;AAIA,UAAI,KAAK,QAAQ;AACf,aAAK,aAAa,KAAK,OAAO,QAAQ,CAAC,SAAS,KAAK,WAAW,IAAI,CAAC;AACrE,aAAK,cAAc,KAAK,OAAO,SAAS,CAAC,MAAM,SAAS,KAAK,UAAU,MAAM,IAAI,CAAC;AAAA,MACpF;AACA,YAAM,KAAK,WAAW;AAAA,IACxB,SAAS,KAAK;AAIZ,YAAM,KAAK,SAAS;AACpB,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,WAAW,MAAoB;AAC7B,SAAK,KAAK,MAAM,IAAI;AAAA,EACtB;AAAA;AAAA,EAGQ,UAAU,MAAc,MAAoB;AAClD,QAAI,QAAQ,KAAK,QAAQ,EAAG;AAC5B,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,QAAI;AACF,WAAK,KAAK,OAAO,MAAM,IAAI;AAAA,IAC7B,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,MAAM,WAA0B;AAC9B,QAAI,KAAK,UAAW;AACpB,SAAK,YAAY;AACjB,SAAK,aAAa;AAClB,SAAK,aAAa;AAClB,SAAK,cAAc;AACnB,SAAK,cAAc;AACnB,QAAI,KAAK,cAAc;AACrB,WAAK,QAAQ,iBAAiB,OAAO,oBAAoB,SAAS,KAAK,YAAY;AACnF,WAAK,eAAe;AAAA,IACtB;AACA,QAAI;AACF,WAAK,KAAK,KAAK;AAAA,IACjB,QAAQ;AAAA,IAER;AACA,SAAK,MAAM;AACX,SAAK,QAAQ,MAAM;AACnB,SAAK,SAAS;AACd,QAAI,KAAK,QAAQ;AACf,YAAM,KAAK,OAAO,MAAM;AACxB,WAAK,SAAS;AAAA,IAChB;AACA,eAAW,cAAc,KAAK,aAAa;AACzC,UAAI;AACF,cAAM,WAAW,MAAM;AAAA,MACzB,QAAQ;AAAA,MAER;AAAA,IACF;AACA,SAAK,cAAc,CAAC;AACpB,SAAK,gBAAgB;AACrB,SAAK,MAAM,MAAM;AACjB,QAAI,KAAK,SAAS;AAChB,YAAM,GAAG,KAAK,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACvD,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,mBAAkC;AAC9C,UAAM,EAAE,SAAS,cAAc,IAAI,MAAM;AAAA,MACvC,KAAK,QAAQ,cAAc,CAAC;AAAA,MAC5B,KAAK;AAAA,IACP;AACA,SAAK,cAAc;AACnB,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,MAAc,MAAM,cAAsB,YAAmC;AAC3E,UAAM,OAAO,eAAe;AAAA,MAC1B,QAAQ,KAAK;AAAA,MACb,WAAW,KAAK,QAAQ;AAAA,MACxB,OAAO,KAAK,QAAQ;AAAA,MACpB,gBAAgB,KAAK,QAAQ;AAAA,MAC7B;AAAA;AAAA;AAAA;AAAA,MAIA,GAAI,KAAK,gBAAgB,EAAE,eAAe,KAAK,eAAe,iBAAiB,KAAK,IAAI,CAAC;AAAA,IAC3F,CAAC;AACD,UAAM,QAAQ,MAAM,aAAa;AACjC,UAAM,MAAM,MAAM,oBAAoB,GAAG,MAAM;AAAA,MAC7C,MAAM;AAAA,MACN,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,KAAK,KAAK,QAAQ;AAAA,MAClB,KAAK,aAAa,UAAU;AAAA,IAC9B,CAAC;AAKD,QAAI,OAAO,CAAC,SAAS;AACnB,WAAK,QAAQ,WAAW,MAAM,EAAE,MAAM,KAAK,MAAM,MAAM,KAAK,KAAK,CAAC;AAElE,WAAK,gBAAgB,KAAK,eAAe,MAAM,MAAM,CAAC,qBAAqB;AAAA,IAC7E,CAAC;AACD,QAAI,OAAO,CAAC,UAAU;AACpB,WAAK,KAAK,eAAe,MAAM,QAAQ;AAAA,IACzC,CAAC;AACD,SAAK,MAAM;AAAA,EACb;AAAA,EAEQ,cAAc,MAAoB;AACxC,SAAK,WAAW,iBAAiB,MAAM,KAAK,QAAQ,cAAc,CAAC;AAAA,EACrE;AAAA,EAEA,MAAc,aAA4B;AACxC,QAAI,OAAO,KAAK,WAAW,UAAU;AACnC,WAAK,cAAc,KAAK,MAAM;AAC9B;AAAA,IACF;AACA,qBAAiB,WAAW,KAAK,QAAQ;AACvC,YAAM,UAAU,QAAQ,QAAQ;AAChC,YAAM,OAAO,OAAO,YAAY,WAAW,UAAU,KAAK,UAAU,OAAO;AAC3E,WAAK,cAAc,IAAI;AAAA,IACzB;AAAA,EACF;AAAA,EAEQ,eAAe,UAA8B;AACnD,SAAK,MAAM,KAAK;AAAA,MACd,MAAM;AAAA,MACN,GAAI,SAAS,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,SAAS,UAAU;AAAA,MAC5E,GAAI,SAAS,yBAAyB,SAClC,CAAC,IACD,EAAE,sBAAsB,SAAS,qBAAqB;AAAA,IAC5D,CAAC;AAAA,EACH;AAAA,EAEQ,sBAAsB,OAA2B;AACvD,SAAK,MAAM,KAAK,KAAK;AACrB,QAAI,MAAM,SAAS,UAAU;AAC3B,WAAK,YAAY;AACjB,iBAAW,YAAY,KAAK,cAAe,UAAS;AAKpD,WAAK,MAAM,MAAM;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,MAAc,eAAe,UAAiC;AAI5D,QAAI,KAAK,UAAW;AACpB,QAAI,KAAK,QAAQ;AACf,WAAK,OAAO,MAAM;AAClB,YAAM,KAAK,OAAO,MAAM;AAAA,IAC1B;AACA,QAAI,CAAC,KAAK,WAAW;AAInB,WAAK,MAAM,KAAK;AAAA,QACd,MAAM;AAAA,QACN,SAAS;AAAA,QACT,QAAQ,gBAAgB,UAAU,KAAK,cAAc,oBAAoB,CAAC;AAAA,MAC5E,CAAC;AAAA,IACH;AACA,eAAW,YAAY,KAAK,cAAe,UAAS,QAAQ;AAC5D,SAAK,MAAM,MAAM;AAAA,EACnB;AACF;;;ASnXA,SAAS,SAAAC,QAAO,SAAAC,QAAO,UAAU,MAAAC,KAAI,aAAAC,kBAAiB;AACtD,SAAS,QAAAC,aAAY;AAQrB,IAAM,qBAAqB,MAAM,KAAK,KAAK,KAAK;AAGhD,IAAM,kBAAkB,KAAK,KAAK,KAAK,KAAK;AAErC,SAAS,wBAAgC;AAC9C,SAAOC,MAAK,iBAAiB,GAAG,mBAAmB;AACrD;AAOO,SAAS,mBAAmB,MAAyB,QAAQ,KAAc;AAChF,SAAO,QAAQ,IAAI,oBAAoB,IAAI,kBAAkB,IAAI,UAAU;AAC7E;AAqBA,SAAS,mBAAmB,KAAwC;AAClE,MAAI,CAAC,OAAO,IAAI,KAAK,MAAM,GAAI,QAAO;AACtC,MAAI;AACF,UAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,QAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAC1D,UAAM,QAAS,OAAmC;AAClD,QAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,UAAM,SAAS;AACf,WAAO;AAAA,MACL,aAAa,OAAO;AAAA,MACpB,cAAc,OAAO;AAAA,MACrB,WAAW,OAAO;AAAA,IACpB;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,SAAS,4BAA4B,OAAe,KAAqB;AACvE,SAAO,KAAK,UAAU;AAAA,IACpB,eAAe;AAAA,MACb,aAAa;AAAA,MACb,WAAW,MAAM;AAAA,MACjB,QAAQ,CAAC,kBAAkB,cAAc;AAAA,MACzC,kBAAkB;AAAA,IACpB;AAAA,EACF,CAAC;AACH;AAGO,SAAS,qBAAqB,OAA+C;AAClF,MAAI,CAAC,MAAM,QAAS,QAAO,EAAE,QAAQ,QAAQ,QAAQ,YAAY;AACjE,MAAI,CAAC,MAAM,MAAO,QAAO,EAAE,QAAQ,QAAQ,QAAQ,WAAW;AAE9D,QAAM,WAAW,4BAA4B,MAAM,OAAO,MAAM,GAAG;AACnE,QAAM,WAAW,mBAAmB,MAAM,WAAW;AAErD,MAAI,CAAC,SAAU,QAAO,EAAE,QAAQ,SAAS,SAAS;AAElD,MAAI,OAAO,SAAS,iBAAiB,YAAY,SAAS,aAAa,SAAS,GAAG;AACjF,WAAO,EAAE,QAAQ,QAAQ,QAAQ,sBAAsB;AAAA,EACzD;AACA,QAAM,QACJ,SAAS,gBAAgB,MAAM,SAC/B,OAAO,SAAS,cAAc,YAC9B,SAAS,YAAY,MAAM,MAAM;AAEnC,MAAI,MAAO,QAAO,EAAE,QAAQ,QAAQ,QAAQ,UAAU;AACtD,SAAO,EAAE,QAAQ,SAAS,SAAS;AACrC;AAEA,eAAe,QAAQ,MAAsC;AAC3D,MAAI;AACF,WAAO,MAAM,SAAS,MAAM,MAAM;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAsB,wBAAwB,MAAyB,QAAQ,KAAoB;AACjG,MAAI;AACF,UAAM,OAAO,sBAAsB;AACnC,UAAM,OAAO,qBAAqB;AAAA,MAChC,SAAS,mBAAmB,GAAG;AAAA,MAC/B,OAAO,IAAI;AAAA,MACX,aAAa,MAAM,QAAQ,IAAI;AAAA,MAC/B,KAAK,KAAK,IAAI;AAAA,IAChB,CAAC;AACD,QAAI,KAAK,WAAW,OAAQ;AAC5B,UAAMC,OAAM,iBAAiB,GAAG,EAAE,WAAW,KAAK,CAAC;AACnD,UAAMC,WAAU,MAAM,KAAK,UAAU,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AAGtE,UAAMC,OAAM,MAAM,GAAK,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACzC,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,OAAO,MAAM,oDAAoD,OAAO;AAAA,CAAI;AAAA,EACtF;AACF;AAOA,eAAsB,0BACpB,MAAyB,QAAQ,KAClB;AACf,MAAI;AACF,QAAI,CAAC,mBAAmB,GAAG,EAAG;AAC9B,UAAM,OAAO,sBAAsB;AACnC,UAAM,WAAW,mBAAmB,MAAM,QAAQ,IAAI,CAAC;AACvD,QAAI,YAAY,OAAO,SAAS,iBAAiB,YAAY,SAAS,aAAa,SAAS,GAAG;AAC7F;AAAA,IACF;AACA,UAAMC,IAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,EAChC,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,YAAQ,OAAO,MAAM,uDAAuD,OAAO;AAAA,CAAI;AAAA,EACzF;AACF;;;ACzJO,IAAM,aAAN,MAAyC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM9C,YAA6B,QAAoB;AAApB;AAAA,EAAqB;AAAA,EAArB;AAAA,EAE7B,OAAO,aAAa,MAImB;AACrC,UAAM,UAAU,IAAI;AAAA,MAClB,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,UAAU,KAAK,QAAQ;AAAA,MAC5B,KAAK;AAAA,IACP;AAMA,UAAM,wBAAwB;AAC9B,UAAM,QAAQ,MAAM;AACpB,QAAI;AACF,uBAAiB,SAAS,QAAQ,OAAO,GAAG;AAC1C,cAAM;AAAA,MACR;AAAA,IACF,UAAE;AACA,YAAM,QAAQ,SAAS;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,gBAAgB,QAA4E;AAC1F,WAAO,IAAI,aAAa,OAAO,MAAM,OAAO,KAAK;AAAA,EACnD;AACF;;;AClBO,SAAS,cAAc,OAAoB,OAAO,WAAqC;AAC5F,SAAO,SAAS,QAAQ,IAAI,WAAW,SAAS,IAAI,IAAI,kBAAkB;AAC5E;;;AC3CO,SAAS,oBAAoB,SAAiB;AACnD,QAAM,SAAS,mBAAmB,OAAO;AACzC,SAAO;AAAA,IACL,KAAK,SAAiB,MAAsC;AAC1D,YAAM,QAAQ,OAAO,IAAI,KAAK,UAAU,IAAI,CAAC,KAAK;AAClD,cAAQ,OAAO,MAAM,GAAG,MAAM,IAAI,OAAO,GAAG,KAAK;AAAA,CAAI;AAAA,IACvD;AAAA,IACA,KAAK,SAAiB,MAAsC;AAC1D,YAAM,QAAQ,OAAO,IAAI,KAAK,UAAU,IAAI,CAAC,KAAK;AAClD,cAAQ,OAAO,MAAM,GAAG,MAAM,SAAS,OAAO,GAAG,KAAK;AAAA,CAAI;AAAA,IAC5D;AAAA,IACA,MAAM,SAAiB,MAAsC;AAC3D,YAAM,QAAQ,OAAO,IAAI,KAAK,UAAU,IAAI,CAAC,KAAK;AAClD,cAAQ,OAAO,MAAM,GAAG,MAAM,UAAU,OAAO,GAAG,KAAK;AAAA,CAAI;AAAA,IAC7D;AAAA,EACF;AACF;","names":["mkdir","join","createServer","writeFile","join","tool","tool","createServer","join","writeFile","isRecord","join","mkdir","chmod","mkdir","rm","writeFile","join","join","mkdir","writeFile","chmod","rm"]}
|
package/dist/cli.js
CHANGED
|
@@ -9,11 +9,11 @@ import {
|
|
|
9
9
|
loadForwardPorts,
|
|
10
10
|
runSetupCommand,
|
|
11
11
|
runStartCommand
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-H3UHIWQF.js";
|
|
13
13
|
import "./chunk-FDWECEDJ.js";
|
|
14
14
|
import {
|
|
15
15
|
createServiceLogger
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-Y7ZVMFJN.js";
|
|
17
17
|
|
|
18
18
|
// src/cli.ts
|
|
19
19
|
import { readFileSync } from "fs";
|
package/dist/index.d.ts
CHANGED
|
@@ -603,9 +603,10 @@ declare function hasUnpushedCommits(cwd: string): boolean;
|
|
|
603
603
|
declare function stageAndCommit(cwd: string, message: string): string | null;
|
|
604
604
|
/** Update the git remote URL with a fresh token. */
|
|
605
605
|
declare function updateRemoteToken(cwd: string, token: string): void;
|
|
606
|
-
/** Best-effort flush of pending work
|
|
607
|
-
*
|
|
608
|
-
*
|
|
606
|
+
/** Best-effort flush of pending work: pushes any real (intentional) commits to
|
|
607
|
+
* the task branch, and captures uncommitted changes as a WIP snapshot on the
|
|
608
|
+
* `conveyor-wip/<branch>` ref — never as commits on the branch itself. When
|
|
609
|
+
* the tree is clean, a previously pushed WIP ref is deleted. Never throws. */
|
|
609
610
|
declare function flushPendingChanges(cwd: string, opts?: {
|
|
610
611
|
wipMessage?: string;
|
|
611
612
|
refreshToken?: () => Promise<string | undefined>;
|
package/dist/index.js
CHANGED
|
@@ -25,9 +25,9 @@ import {
|
|
|
25
25
|
stageAndCommit,
|
|
26
26
|
unshallowRepo,
|
|
27
27
|
updateRemoteToken
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-H3UHIWQF.js";
|
|
29
29
|
import "./chunk-FDWECEDJ.js";
|
|
30
|
-
import "./chunk-
|
|
30
|
+
import "./chunk-Y7ZVMFJN.js";
|
|
31
31
|
|
|
32
32
|
// src/runner/file-cache.ts
|
|
33
33
|
var DEFAULT_MAX_SIZE_BYTES = 50 * 1024 * 1024;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createHarness,
|
|
3
3
|
createServiceLogger
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-Y7ZVMFJN.js";
|
|
5
5
|
|
|
6
6
|
// src/runner/tag-audit-handler.ts
|
|
7
7
|
var logger = createServiceLogger("TagAudit");
|
|
@@ -219,4 +219,4 @@ export {
|
|
|
219
219
|
buildTagAuditPrompt,
|
|
220
220
|
handleTagAudit
|
|
221
221
|
};
|
|
222
|
-
//# sourceMappingURL=tag-audit-handler-
|
|
222
|
+
//# sourceMappingURL=tag-audit-handler-FVGEBGSB.js.map
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
createHarness,
|
|
6
6
|
createServiceLogger,
|
|
7
7
|
defineTool
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-Y7ZVMFJN.js";
|
|
9
9
|
|
|
10
10
|
// src/runner/task-audit-handler.ts
|
|
11
11
|
import { z } from "zod";
|
|
@@ -798,4 +798,4 @@ async function handleTaskAudit(request, connection, projectDir) {
|
|
|
798
798
|
export {
|
|
799
799
|
handleTaskAudit
|
|
800
800
|
};
|
|
801
|
-
//# sourceMappingURL=task-audit-handler-
|
|
801
|
+
//# sourceMappingURL=task-audit-handler-JNS4NH5O.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rallycry/conveyor-agent",
|
|
3
|
-
"version": "8.
|
|
4
|
-
"description": "Conveyor Agent Runner v8
|
|
3
|
+
"version": "8.3.1",
|
|
4
|
+
"description": "Conveyor Agent Runner v8 - PTY harness for the task chat (SDK harness for audit/project-chat). Agent-as-User architecture with BaseService patterns. Works locally too.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
7
7
|
"claude",
|