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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pylon/upstream-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ after exact owned cleanup and supervisor exit. Pylon delivery tracked in [pylon#
| #2533, #2555, #2560 (`1e2cc2278`, `b11fd5e2d`, `346a463da`) | Claude Opus 5.5 adaptive thinking | **Adopt** | Configures always-on adaptive thinking and test fixture surfaces for Claude Opus 5.5. |
| #2645 (`703cc4547`) | Claude Code client version bump (2.1.281) & ban risk notice | **Adopt** | Claims client version 2.1.281 for Anthropic subscription OAuth requests to pass backend model gating, accompanied by user-facing ban-risk warning. |
| #2507 (`36b912fea`) | Remote catalog fetch from `prime-agent-catalog` | **Hold / Audit** | Introduces runtime network dependency for model metadata. Held pending evaluation of offline cache fallbacks and deterministic air-gapped guarantees. |
| #2475 (`561401b27`) | Truncate cell source to 2KB on `host_request` | **Hold / Audit** | May break downstream provenance reconstruction and context hash verification when cells exceed 2KB. Held for Pylon trace analysis. |
| #2475 (`561401b27`) | Truncate cell source to 2KB on `host_request` | **Adopt** | Caps spawning cell source attached to host requests at 2KB with truncation marker. Verified that Pylon does not consume `cellSourceCode` or depend on >2KB cell source for provenance. |
| #2382 (`d73349d50`) | Raw `child.kill("SIGKILL")` on worker bridge EPIPE | **Reject** | Violates Pylon syscall safety invariant; raw signals to unverified PIDs risk killing recycled processes during rapid worker crashes. Must rely on supervised process exit receipts. |
| #2471 (`8ee46be35`), #2478 (`02e5babb4`) | Live mutation of CPython `__closure__` and `__globals__` | **Reject** | Unsafe runtime bytecode/closure mutation in REPL state restore risks memory leaks and CPython interpreter `SIGSEGV` crashes. |
| #2388 (`1c1ad1e48`) | Synchronous session name reclamation on delete receipt | **Reject** | Deleting session name before child process unwinding completes creates race conditions with pending detached worker handles. |
Expand Down
1 change: 1 addition & 0 deletions packages/coding-agent/.changes/cap-spawn-cell-source.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Capped the spawning cell source attached to kernel host requests at 2KB with a truncation marker, so oversized cells stop re-shipping their full source on every spawn/progress-note/collect round trip and in each child's persisted spawnCode.
12 changes: 11 additions & 1 deletion packages/coding-agent/src/core/kernel/repl-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ const MAX_BACKGROUND_OUTPUT_CHARS = 64 * 1024;
// MAX_ATTACHMENT_DATA_CHARS; a line that cannot complete within this ceiling is
// corruption the protocol repair owns, not output worth buffering until OOM.
const MAX_PROTOCOL_LINE_CHARS = 32 * 1024 * 1024;
// The spawning cell's source rides every host-request round trip and persists per child
// as runtimeMetadata.spawnCode, so cap it once at this boundary.
// Keep below SPAWN_CODE_MAX_CHARS in daemon-session-list.ts so its 4000-char display slice stays a no-op.
const MAX_CELL_SOURCE_CHARS = 2 * 1024;
const CELL_SOURCE_TRUNCATION_MARKER = ` [... cell source truncated at ${MAX_CELL_SOURCE_CHARS} chars ...]`;

const MAX_KERNEL_STDERR_CHARS = 8 * 1024;
const MAX_KERNEL_STDERR_LOG_BYTES = 5 * 1024 * 1024;
Expand All @@ -91,6 +96,11 @@ function writeFullySync(fd: number, data: Buffer): void {
}
}

function capCellSourceCode(code: string | undefined): string | undefined {
if (code === undefined || code.length <= MAX_CELL_SOURCE_CHARS) return code;
return `${code.slice(0, MAX_CELL_SOURCE_CHARS)}${CELL_SOURCE_TRUNCATION_MARKER}`;
}

/** ExecuteResult plus the raw fields of the request's `done` event (state ops). */
interface InternalExecuteResult extends ExecuteResult {
doneFields?: Record<string, unknown>;
Expand Down Expand Up @@ -1331,7 +1341,7 @@ export class ReplKernelManager {
// Tag the request with the cell that triggered it. A blocking call is still
// the in-flight execution; detached spawns (asyncio.create_task) fire after
// the scheduling cell goes idle, so fall back to that last cell's source.
const cellSourceCode = this.activeExecution?.code ?? this.lastCellCode;
const cellSourceCode = capCellSourceCode(this.activeExecution?.code ?? this.lastCellCode);
return handler({ ...data, cellSourceCode });
}

Expand Down
5 changes: 5 additions & 0 deletions packages/coding-agent/test/repl-kernel-execute.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ describeIf("ReplKernelManager execute (real runtime)", () => {
const unknown = await manager.execute("import rlm\nawait rlm.host_request('test.unknown')");
expect(unknown.status).toBe("error");
expect(unknown.error?.evalue).toContain('host request type "test.unknown" is not available');

const padded = `${"# pad\n".repeat(500)}import rlm\nreply = await rlm.host_request('test.echo', {'value': 9, 'cellSourceCode': 'FAKE'})\n[len(reply['cell']), reply['cell'].endswith(' [... cell source truncated at 2048 chars ...]'), reply['cell'] == 'FAKE']`;
const capped = await manager.execute(padded);
expect(capped.status).toBe("ok");
expect(capped.result).toBe("[2094, True, False]");
}, 30_000);

it("spawns through rlm.spawn over the unchanged rlm.run wire type, requires a child name, and refuses a direct rlm call", async () => {
Expand Down
Loading