mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-08 01:57:31 +01:00
feat(runner): forward ACP _meta from ACPX as a bounded copy (#19259)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The native runner runs Claude through ACPX and maps ACP updates into canonical run events. > - Claude sends useful data in the ACP `_meta` field: the real tool name, the parent tool of a subagent call, the terminal exit code, and the rate-limit state. > - ACPX 0.13.1 drops all `_meta` before the runner sees it. > - Three planned features need this data: richer tool rows, a rate-limit gauge, and a subagent tree. > - This pull request forwards `_meta` from ACPX and bounds it once in the runner. > - The benefit is that each feature can read the data from one safe, shared place. ## Linked Issues or Issue Description **Problem or motivation** ACPX keeps only the title, status, kind, locations, raw input, raw output and content of an ACP tool update. It drops `_meta`. Claude's tool name, `parentToolUseId`, `terminal_exit` and `_claude/rateLimit` never reach the runner. **Proposed solution** Forward `_meta` from ACPX on tool and status events. Copy it into a bounded, JSON-safe object in the runner before any mapper reads it. **Alternatives considered** Read the full ACP notification through `onSessionNotification` and join it to runtime events by tool call ID. That needs a side table and depends on callback order. Forwarding on the event is simpler and keeps the data with the event. ## What Changed - `patches/acpx@0.13.1.patch`: ACPX now sets `_meta` on `tool_call` events and on status events (`usage_update`, `current_mode_update`, `config_option_update`, `session_info_update`). The type declarations document the field as unvalidated producer data. - `packages/paperclip-runner/src/provider-events.ts`: new `boundedAcpMeta()`. `createAcpxToolEventNormalizer` applies it to every runtime event. Limits: depth 6, 64 entries per object or array, 4 KB per string, about 16 KB in total. It drops `__proto__`, `constructor` and `prototype` keys, functions and non-finite numbers. - The hardened ACPX sidecar path keeps its explicit field allowlist. It does not forward `_meta` yet. A feature that needs a field there must add it to that allowlist. - Tests for the passthrough and for each bound. ## Verification - `cd packages/paperclip-runner && npx vitest run src/provider-events.test.ts` - The canonical mappers do not read `_meta` yet, so run events do not change. Follow-up pull requests use it. ## Risks - Low risk. The new field is additive and the mappers ignore it today. - The lockfile patch hash for `acpx@0.13.1` changes. A fresh `pnpm install` applies the new patch. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use and code execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [ ] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d8c47159d4
commit
29afc633f6
3 files changed
+302
-72
No files matched your search
@@ -6,6 +6,7 @@ import {
|
||||
canonicalProviderEventsFromAcpxRuntimeEvent,
|
||||
canonicalProviderEventsFromCodex,
|
||||
canonicalProviderEventsFromOpenCodePart,
|
||||
boundedAcpMeta,
|
||||
createAcpxToolEventNormalizer,
|
||||
createGrokMessageNormalizer,
|
||||
providerFamilyCapabilities,
|
||||
@@ -555,6 +556,84 @@ describe("provider-neutral events", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("forwards ACP _meta as a bounded JSON-safe copy on every runtime event", () => {
|
||||
const normalize = createAcpxToolEventNormalizer();
|
||||
const tool = normalize({
|
||||
type: "tool_call",
|
||||
tag: "tool_call",
|
||||
toolCallId: "tool-meta",
|
||||
title: "Terminal",
|
||||
status: "pending",
|
||||
_meta: {
|
||||
claudeCode: { toolName: "Bash", parentToolUseId: "toolu_parent" },
|
||||
terminal_exit: { exit_code: 2, signal: null },
|
||||
["__proto__"]: { polluted: true },
|
||||
},
|
||||
} as never);
|
||||
expect(tool._meta).toEqual({
|
||||
claudeCode: { toolName: "Bash", parentToolUseId: "toolu_parent" },
|
||||
terminal_exit: { exit_code: 2, signal: null },
|
||||
});
|
||||
const status = normalize({
|
||||
type: "status",
|
||||
tag: "usage_update",
|
||||
text: "usage updated",
|
||||
_meta: { "_claude/rateLimit": { status: "allowed", utilization: 0.78 } },
|
||||
} as never);
|
||||
expect(status._meta).toEqual({
|
||||
"_claude/rateLimit": { status: "allowed", utilization: 0.78 },
|
||||
});
|
||||
expect(
|
||||
normalize({ type: "status", text: "x", _meta: "not an object" } as never),
|
||||
).not.toHaveProperty("_meta");
|
||||
});
|
||||
|
||||
it("bounds ACP _meta depth, breadth, string size and total size", () => {
|
||||
let deep: Record<string, unknown> = { leaf: true };
|
||||
for (let index = 0; index < 10; index += 1) deep = { next: deep };
|
||||
const wide = Object.fromEntries(
|
||||
Array.from({ length: 100 }, (_, index) => [`k${index}`, index]),
|
||||
);
|
||||
const bounded = boundedAcpMeta({
|
||||
deep,
|
||||
wide,
|
||||
long: "x".repeat(10_000),
|
||||
big: Array.from({ length: 64 }, () => "y".repeat(3_000)),
|
||||
notFinite: Number.POSITIVE_INFINITY,
|
||||
fn: () => undefined,
|
||||
})!;
|
||||
expect(Buffer.byteLength(JSON.stringify(bounded))).toBeLessThanOrEqual(16_000);
|
||||
expect(Object.keys(bounded.wide as object)).toHaveLength(64);
|
||||
expect((bounded.long as string).length).toBe(4_000);
|
||||
expect(bounded).not.toHaveProperty("notFinite");
|
||||
expect(bounded).not.toHaveProperty("fn");
|
||||
expect(JSON.stringify(bounded.deep)).not.toContain("leaf");
|
||||
expect(boundedAcpMeta([1, 2])).toBeUndefined();
|
||||
expect(boundedAcpMeta({})).toBeUndefined();
|
||||
});
|
||||
|
||||
it("charges every retained container, separator and escaped byte against the ACP _meta budget", () => {
|
||||
let tree: unknown = [];
|
||||
for (let level = 0; level < 3; level += 1) tree = Array.from({ length: 64 }, () => tree);
|
||||
const nested = boundedAcpMeta({ a: tree })!;
|
||||
expect(Buffer.byteLength(JSON.stringify(nested))).toBeLessThanOrEqual(16_000);
|
||||
const escaped = boundedAcpMeta({ nulls: "\u0000".repeat(4_000), ok: "kept?" })!;
|
||||
expect(Buffer.byteLength(JSON.stringify(escaped))).toBeLessThanOrEqual(16_000);
|
||||
expect(escaped).not.toHaveProperty("nulls");
|
||||
expect(escaped.ok).toBe("kept?");
|
||||
});
|
||||
|
||||
it("inspects a bounded number of keys on a very wide ACP _meta object", () => {
|
||||
let reads = 0;
|
||||
const wide = new Proxy(
|
||||
Object.fromEntries(Array.from({ length: 10_000 }, (_, index) => [`__proto__x${index}`.replace("__proto__x", "k"), index])),
|
||||
{ get(target, key, receiver) { reads += 1; return Reflect.get(target, key, receiver); } },
|
||||
);
|
||||
const bounded = boundedAcpMeta({ wide })!;
|
||||
expect(Object.keys(bounded.wide as object)).toHaveLength(64);
|
||||
expect(reads).toBeLessThanOrEqual(64);
|
||||
});
|
||||
|
||||
it("preserves ACPX identity across title-less progress and completion updates", () => {
|
||||
const normalize = createAcpxToolEventNormalizer();
|
||||
const started = normalize({
|
||||
|
||||
@@ -264,6 +264,95 @@ function boundedAcpxLifecycleLocations(value: unknown): unknown[] | undefined {
|
||||
});
|
||||
}
|
||||
|
||||
const MAX_ACP_META_DEPTH = 6;
|
||||
const MAX_ACP_META_ENTRIES = 64;
|
||||
const MAX_ACP_META_STRING_BYTES = 4_000;
|
||||
const MAX_ACP_META_TOTAL_BYTES = 16_000;
|
||||
const UNSAFE_ACP_META_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
||||
|
||||
/**
|
||||
* Bounded, JSON-safe copy of an ACP update `_meta` object. The patched ACPX
|
||||
* runtime forwards `_meta` unvalidated on tool and status events (for example
|
||||
* Claude's `_meta.claudeCode.toolName`, `parentToolUseId`, `terminal_exit`
|
||||
* and `_claude/rateLimit`). The values are producer-controlled hints: they
|
||||
* can name a tool or carry a provider counter, but they never grant authority.
|
||||
*
|
||||
* Limits: depth 6, 64 keys or items per container, 4 KB per string and about
|
||||
* 16 KB in total. Entries beyond a limit are dropped, never partially parsed.
|
||||
* Returns undefined when nothing usable remains.
|
||||
*/
|
||||
export function boundedAcpMeta(
|
||||
value: unknown,
|
||||
): Record<string, unknown> | undefined {
|
||||
// The budget is the serialized JSON size of the retained copy: every
|
||||
// scalar at its escaped length, every bracket, separator and key. A value
|
||||
// that does not fit is dropped whole, so the copy never exceeds the bound.
|
||||
let budget = MAX_ACP_META_TOTAL_BYTES;
|
||||
const charge = (bytes: number): boolean => {
|
||||
if (bytes > budget) return false;
|
||||
budget -= bytes;
|
||||
return true;
|
||||
};
|
||||
const visit = (input: unknown, depth: number): unknown => {
|
||||
if (input === null || typeof input === "boolean") {
|
||||
return charge(String(input).length) ? input : undefined;
|
||||
}
|
||||
if (typeof input === "number") {
|
||||
if (!Number.isFinite(input)) return undefined;
|
||||
return charge(JSON.stringify(input).length) ? input : undefined;
|
||||
}
|
||||
if (typeof input === "string") {
|
||||
const bounded = boundedAcpxLifecycleText(input, MAX_ACP_META_STRING_BYTES) ?? "";
|
||||
return charge(Buffer.byteLength(JSON.stringify(bounded))) ? bounded : undefined;
|
||||
}
|
||||
if (depth >= MAX_ACP_META_DEPTH || typeof input !== "object") return undefined;
|
||||
if (!charge(2)) return undefined;
|
||||
if (Array.isArray(input)) {
|
||||
const items: unknown[] = [];
|
||||
const limit = Math.min(input.length, MAX_ACP_META_ENTRIES);
|
||||
for (let index = 0; index < limit && budget > 0; index += 1) {
|
||||
if (items.length > 0 && !charge(1)) break;
|
||||
const next = visit(input[index], depth + 1);
|
||||
if (next !== undefined) items.push(next);
|
||||
else if (items.length > 0) budget += 1;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
const output: Record<string, unknown> = {};
|
||||
let retained = 0;
|
||||
let inspected = 0;
|
||||
// for...in stops early; Object.entries would materialize every key of a
|
||||
// wide producer object before any limit applied.
|
||||
for (const key in input as Record<string, unknown>) {
|
||||
if (!Object.prototype.hasOwnProperty.call(input, key)) continue;
|
||||
if (retained >= MAX_ACP_META_ENTRIES || inspected >= MAX_ACP_META_ENTRIES * 4 || budget <= 0) break;
|
||||
inspected += 1;
|
||||
if (UNSAFE_ACP_META_KEYS.has(key) || Buffer.byteLength(key) > 160) continue;
|
||||
const keyBytes = Buffer.byteLength(JSON.stringify(key)) + 1 + (retained > 0 ? 1 : 0);
|
||||
if (!charge(keyBytes)) break;
|
||||
const next = visit((input as Record<string, unknown>)[key], depth + 1);
|
||||
if (next === undefined) {
|
||||
budget += keyBytes;
|
||||
continue;
|
||||
}
|
||||
output[key] = next;
|
||||
retained += 1;
|
||||
}
|
||||
return output;
|
||||
};
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
||||
const bounded = visit(value, 0) as Record<string, unknown> | undefined;
|
||||
return bounded && Object.keys(bounded).length > 0 ? bounded : undefined;
|
||||
}
|
||||
|
||||
/** Bound any forwarded `_meta` on a runtime event, whatever its type. */
|
||||
function withBoundedAcpMeta<T extends AcpRuntimeEventShape>(event: T): T {
|
||||
if (!Object.prototype.hasOwnProperty.call(event, "_meta")) return event;
|
||||
const { _meta: raw, ...rest } = event as T & { _meta?: unknown };
|
||||
const meta = boundedAcpMeta(raw);
|
||||
return (meta ? { ...rest, _meta: meta } : rest) as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* ACP tool updates are deltas: a later update may omit the title, kind, and
|
||||
* locations that were present on the opening event. ACPX represents a missing
|
||||
@@ -291,7 +380,8 @@ export function createAcpxToolEventNormalizer<
|
||||
T extends AcpRuntimeEventShape,
|
||||
>(): (event: T) => T {
|
||||
const lifecycle = new Map<string, AcpxToolLifecycleIdentity>();
|
||||
return (event) => {
|
||||
return (input) => {
|
||||
const event = withBoundedAcpMeta(input);
|
||||
if (event.type !== "tool_call" || !event.toolCallId) return event;
|
||||
const toolCallId = boundedAcpxLifecycleId(event.toolCallId);
|
||||
const boundedTitle = boundedAcpxLifecycleText(
|
||||
|
||||
+132
-71
@@ -1,5 +1,5 @@
|
||||
diff --git a/dist/client-CxNllqui.d.ts b/dist/client-CxNllqui.d.ts
|
||||
index 5e2113ac0..45aff03c3 100644
|
||||
index 5e2113ac0a1a92c99322cf01e5c106760a0b0dbf..45aff03c37427071de49b45eb0361a9a808786e0 100644
|
||||
--- a/dist/client-CxNllqui.d.ts
|
||||
+++ b/dist/client-CxNllqui.d.ts
|
||||
@@ -120,6 +120,8 @@ declare class AcpClient {
|
||||
@@ -20,9 +20,10 @@ index 5e2113ac0..45aff03c3 100644
|
||||
private setSessionModelThroughConfig;
|
||||
private setSessionModelThroughLegacyMethod;
|
||||
diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
index d454fd7c5bf742b469be75eb8c3988b694a0ffaa..b29bee391efcdc1ca9c37e9608cc34f527fe428e 100644
|
||||
--- a/dist/live-checkpoint-BSIrfgVo.js
|
||||
+++ b/dist/live-checkpoint-BSIrfgVo.js
|
||||
@@ -6,7 +6,7 @@
|
||||
@@ -6,7 +6,7 @@ import os from "node:os";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { execFile, spawn } from "node:child_process";
|
||||
import { Readable, Writable } from "node:stream";
|
||||
@@ -31,7 +32,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
import readline from "node:readline/promises";
|
||||
//#region src/errors.ts
|
||||
var AcpxOperationalError = class extends Error {
|
||||
@@ -1068,12 +1068,14 @@
|
||||
@@ -1068,12 +1068,14 @@ function serializeSessionRecordForDisk(record) {
|
||||
last_agent_disconnect_reason: canonical.lastAgentDisconnectReason,
|
||||
protocol_version: canonical.protocolVersion,
|
||||
agent_capabilities: canonical.agentCapabilities,
|
||||
@@ -46,7 +47,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
acpx: canonical.acpx,
|
||||
imported_from: canonical.importedFrom ? {
|
||||
record_id: canonical.importedFrom.recordId,
|
||||
@@ -1239,7 +1241,8 @@
|
||||
@@ -1239,7 +1241,8 @@ function parseRequestTokenUsage(raw) {
|
||||
for (const [key, value] of Object.entries(record)) {
|
||||
const parsed = parseTokenUsage(value);
|
||||
if (parsed == null) return null;
|
||||
@@ -56,7 +57,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
@@ -1325,6 +1328,93 @@
|
||||
@@ -1325,6 +1328,93 @@ function isAgentMessage$1(raw) {
|
||||
function isConversationMessage(raw) {
|
||||
return raw === "Resume" || isUserMessage$1(raw) || isAgentMessage$1(raw);
|
||||
}
|
||||
@@ -150,7 +151,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
function parseConversationRecord(record) {
|
||||
if (!hasValidConversationCore(record)) return;
|
||||
const title = parseConversationTitle(record.title);
|
||||
@@ -1339,7 +1429,8 @@
|
||||
@@ -1339,7 +1429,8 @@ function parseConversationRecord(record) {
|
||||
updated_at: record.updated_at,
|
||||
cumulative_token_usage: cumulativeTokenUsage ?? {},
|
||||
cumulative_cost: cumulativeCost,
|
||||
@@ -160,7 +161,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
};
|
||||
}
|
||||
const INVALID_VALUE = Symbol("invalid");
|
||||
@@ -1541,12 +1632,14 @@
|
||||
@@ -1541,12 +1632,14 @@ function parseSessionRecord(raw) {
|
||||
lastAgentDisconnectReason: optionals.lastAgentDisconnectReason,
|
||||
protocolVersion: typeof record.protocol_version === "number" ? record.protocol_version : void 0,
|
||||
agentCapabilities: asRecord$4(record.agent_capabilities),
|
||||
@@ -175,7 +176,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
acpx: parseAcpxState(record.acpx),
|
||||
importedFrom: metadata.importedFrom
|
||||
};
|
||||
@@ -1661,8 +1754,9 @@
|
||||
@@ -1661,8 +1754,9 @@ const ZED_TAG_KEYS = /* @__PURE__ */ new Set([
|
||||
"RedactedThinking",
|
||||
"ToolUse"
|
||||
]);
|
||||
@@ -186,7 +187,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
"agent_capabilities",
|
||||
"messages.Agent.content.ToolUse.input",
|
||||
"acpx.desired_config_options",
|
||||
@@ -3104,10 +3198,10 @@
|
||||
@@ -3104,10 +3198,10 @@ function authEnvKey(methodId) {
|
||||
authEnvKeyCache.set(methodId, key);
|
||||
return key;
|
||||
}
|
||||
@@ -199,7 +200,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
if (typeof value === "string" && value.trim().length > 0) return value;
|
||||
}
|
||||
function protectedEnvKey(key) {
|
||||
@@ -3135,8 +3229,21 @@
|
||||
@@ -3135,8 +3229,21 @@ function promotePrefixedAuthEnvironment(env) {
|
||||
}
|
||||
return protectedKeys;
|
||||
}
|
||||
@@ -223,7 +224,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
const protectedAuthEnvKeys = promotePrefixedAuthEnvironment(env);
|
||||
if (authCredentials) for (const [methodId, credential] of Object.entries(authCredentials)) {
|
||||
addAuthCredentialEnvKeys(protectedAuthEnvKeys, methodId, credential);
|
||||
@@ -3178,10 +3285,10 @@
|
||||
@@ -3178,10 +3285,10 @@ function resolveConfiguredAuthCredential(methodId, authCredentials) {
|
||||
const configCredentials = authCredentials ?? {};
|
||||
return configCredentials[methodId] ?? configCredentials[toEnvToken(methodId)];
|
||||
}
|
||||
@@ -236,7 +237,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
stdio: [
|
||||
"pipe",
|
||||
"pipe",
|
||||
@@ -3286,28 +3393,12 @@
|
||||
@@ -3286,28 +3393,12 @@ function formatAvailableModelIds(models) {
|
||||
const ids = models?.availableModels.map((model) => model.modelId.trim()).filter((modelId) => modelId.length > 0) ?? [];
|
||||
return ids.length > 0 ? ids.join(", ") : "none advertised";
|
||||
}
|
||||
@@ -266,7 +267,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
}
|
||||
//#endregion
|
||||
//#region src/acp/session-control-errors.ts
|
||||
@@ -3961,10 +4052,13 @@
|
||||
@@ -3961,10 +4052,13 @@ function resolveClientCapabilities(params) {
|
||||
...params.elicitationModes.includes("url") ? { url: {} } : {}
|
||||
} } : {}
|
||||
};
|
||||
@@ -282,7 +283,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
};
|
||||
}
|
||||
function hasResponseField(response, field) {
|
||||
@@ -4067,6 +4161,27 @@
|
||||
@@ -4067,6 +4161,27 @@ function createNdJsonMessageStream(agentCommand, output, input) {
|
||||
} })
|
||||
};
|
||||
}
|
||||
@@ -310,7 +311,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
var AcpClient = class {
|
||||
options;
|
||||
connection;
|
||||
@@ -4105,7 +4220,9 @@
|
||||
@@ -4105,7 +4220,9 @@ var AcpClient = class {
|
||||
cwd: asAbsoluteCwd(options.cwd),
|
||||
authPolicy: options.authPolicy ?? "skip",
|
||||
permissionPolicy: snapshotPermissionPolicy(options.permissionPolicy),
|
||||
@@ -321,7 +322,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
};
|
||||
this.eventHandlers = {
|
||||
onAcpMessage: this.options.onAcpMessage,
|
||||
@@ -4216,9 +4333,21 @@
|
||||
@@ -4216,9 +4333,21 @@ var AcpClient = class {
|
||||
this.lastAgentExit = void 0;
|
||||
this.lastKnownPid = child.pid ?? void 0;
|
||||
this.attachAgentLifecycleObservers(child);
|
||||
@@ -343,7 +344,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
if (!this.options.verbose) return;
|
||||
process.stderr.write(chunk);
|
||||
});
|
||||
@@ -4226,6 +4355,7 @@
|
||||
@@ -4226,6 +4355,7 @@ var AcpClient = class {
|
||||
const output = Readable.toWeb(child.stdout);
|
||||
const stream = this.createTappedStream(createNdJsonMessageStream(this.options.agentCommand, input, output));
|
||||
const connection = this.createConnection(stream, launch);
|
||||
@@ -351,7 +352,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
connection.signal.addEventListener("abort", () => {
|
||||
this.recordAgentExit("connection_close", child.exitCode ?? null, child.signalCode ?? null);
|
||||
}, { once: true });
|
||||
@@ -4253,7 +4383,12 @@
|
||||
@@ -4253,7 +4383,12 @@ var AcpClient = class {
|
||||
geminiAcp: isGeminiAcpCommand(spawnCommand, args),
|
||||
copilotAcp: isCopilotAcpCommand(spawnCommand, args),
|
||||
claudeAcp: isClaudeAcpCommand(spawnCommand, args),
|
||||
@@ -365,7 +366,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
};
|
||||
}
|
||||
logAgentLaunch(plan) {
|
||||
@@ -4279,10 +4414,17 @@
|
||||
@@ -4279,10 +4414,17 @@ var AcpClient = class {
|
||||
}
|
||||
async spawnAgentProcess(plan) {
|
||||
const spawnCommand = buildAgentSpawnCommand(plan.spawnCommand, plan.args, process.platform, plan.spawnOptions.env);
|
||||
@@ -385,7 +386,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
try {
|
||||
await waitForSpawn$1(spawnedChild);
|
||||
} catch (error) {
|
||||
@@ -4293,10 +4435,10 @@
|
||||
@@ -4293,10 +4435,10 @@ var AcpClient = class {
|
||||
createConnection(stream, launch) {
|
||||
const app = client({ name: "acpx" }).onNotification(methods.client.session.update, async ({ params }) => {
|
||||
await this.handleSessionUpdate(params);
|
||||
@@ -399,7 +400,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
}).onRequest(methods.client.fs.readTextFile, async ({ params }) => {
|
||||
return await this.handleReadTextFile(params);
|
||||
}).onRequest(methods.client.fs.writeTextFile, async ({ params }) => {
|
||||
@@ -4312,6 +4454,11 @@
|
||||
@@ -4312,6 +4454,11 @@ var AcpClient = class {
|
||||
}).onRequest(methods.client.terminal.release, async ({ params }) => {
|
||||
return await this.handleReleaseTerminal(params);
|
||||
});
|
||||
@@ -411,7 +412,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
if (launch.devinAcp) app.onRequest("_cognition.ai/request_diagnostics", (params) => {
|
||||
return params && typeof params === "object" && !Array.isArray(params) ? params : {};
|
||||
}, async () => ({}));
|
||||
@@ -4333,12 +4480,12 @@
|
||||
@@ -4333,12 +4480,12 @@ var AcpClient = class {
|
||||
async initializeProtocolConnection(connection, launch) {
|
||||
const initializePromise = connection.initialize({
|
||||
protocolVersion: PROTOCOL_VERSION,
|
||||
@@ -426,7 +427,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
clientInfo: resolveClientInfo(launch.devinAcp)
|
||||
});
|
||||
const initialized = launch.geminiAcp ? await withTimeout(initializePromise, resolveGeminiAcpStartupTimeoutMs()) : await initializePromise;
|
||||
@@ -4358,6 +4505,49 @@
|
||||
@@ -4358,6 +4505,49 @@ var AcpClient = class {
|
||||
throw normalizedError;
|
||||
}
|
||||
createTappedStream(base) {
|
||||
@@ -476,7 +477,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
const onAcpMessage = () => this.eventHandlers.onAcpMessage;
|
||||
const onAcpOutputMessage = () => this.eventHandlers.onAcpOutputMessage;
|
||||
const elicitationRequestIds = /* @__PURE__ */ new Set();
|
||||
@@ -4367,8 +4557,12 @@
|
||||
@@ -4367,8 +4557,12 @@ var AcpClient = class {
|
||||
const shouldSuppressInboundReplaySessionUpdate = (message) => {
|
||||
return this.suppressReplaySessionUpdateMessages && isSessionUpdateNotification(message);
|
||||
};
|
||||
@@ -490,7 +491,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
if (requestId !== void 0) {
|
||||
elicitationRequestIds.add(requestId);
|
||||
return;
|
||||
@@ -4379,6 +4573,7 @@
|
||||
@@ -4379,6 +4573,7 @@ var AcpClient = class {
|
||||
}
|
||||
};
|
||||
return {
|
||||
@@ -498,7 +499,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
readable: new ReadableStream({ async start(controller) {
|
||||
const reader = base.readable.getReader();
|
||||
try {
|
||||
@@ -4386,16 +4581,27 @@
|
||||
@@ -4386,16 +4581,27 @@ var AcpClient = class {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
if (!value) continue;
|
||||
@@ -528,7 +529,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
const promptOwner = promptRequestOwner(message);
|
||||
if (promptOwner) bindPromptOwner(promptOwner);
|
||||
const id = responseId(message);
|
||||
@@ -4403,10 +4609,20 @@
|
||||
@@ -4403,10 +4609,20 @@ var AcpClient = class {
|
||||
onAcpOutputMessage()?.("outbound", message);
|
||||
onAcpMessage()?.("outbound", message);
|
||||
}
|
||||
@@ -549,7 +550,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
writer.releaseLock();
|
||||
}
|
||||
} })
|
||||
@@ -4422,7 +4638,10 @@
|
||||
@@ -4422,7 +4638,10 @@ var AcpClient = class {
|
||||
const createPromise = this.runConnectionRequest(() => connection.newSession({
|
||||
cwd: sessionCwd,
|
||||
mcpServers: this.options.mcpServers ?? [],
|
||||
@@ -561,7 +562,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
}));
|
||||
result = claudeAcp ? await withTimeout(createPromise, resolveClaudeAcpSessionCreateTimeoutMs()) : await createPromise;
|
||||
} catch (error) {
|
||||
@@ -4454,6 +4673,7 @@
|
||||
@@ -4454,6 +4673,7 @@ var AcpClient = class {
|
||||
}
|
||||
async loadSessionWithOptions(sessionId, cwd = this.options.cwd, options = {}) {
|
||||
const connection = this.getConnection();
|
||||
@@ -569,7 +570,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
const sessionCwd = await resolveAgentSessionCwd(cwd, this.options.agentCommand);
|
||||
const previousSuppression = this.applySessionUpdateSuppression(Boolean(options.suppressReplayUpdates));
|
||||
let response;
|
||||
@@ -4461,7 +4681,9 @@
|
||||
@@ -4461,7 +4681,9 @@ var AcpClient = class {
|
||||
response = await this.runConnectionRequest(() => connection.loadSession({
|
||||
sessionId,
|
||||
cwd: sessionCwd,
|
||||
@@ -580,7 +581,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
}));
|
||||
await this.waitForSessionUpdateDrain(options.replayIdleMs ?? REPLAY_IDLE_MS, options.replayDrainTimeoutMs ?? REPLAY_DRAIN_TIMEOUT_MS);
|
||||
} finally {
|
||||
@@ -4474,11 +4696,14 @@
|
||||
@@ -4474,11 +4696,14 @@ var AcpClient = class {
|
||||
}
|
||||
async resumeSession(sessionId, cwd = this.options.cwd) {
|
||||
const connection = this.getConnection();
|
||||
@@ -596,7 +597,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
}));
|
||||
this.loadedSessionId = sessionId;
|
||||
const result = toReconnectedSessionResult(response);
|
||||
@@ -4590,15 +4815,17 @@
|
||||
@@ -4590,15 +4815,17 @@ var AcpClient = class {
|
||||
throw maybeWrapSessionControlError("session/set_config_option", error, `for "${configId}"="${value}"`);
|
||||
}
|
||||
}
|
||||
@@ -620,7 +621,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
}
|
||||
async setSessionModelThroughConfig(sessionId, modelId, configId) {
|
||||
const connection = this.getConnection();
|
||||
@@ -4608,7 +4835,9 @@
|
||||
@@ -4608,7 +4835,9 @@ var AcpClient = class {
|
||||
configId,
|
||||
value: modelId
|
||||
}));
|
||||
@@ -631,7 +632,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
return response;
|
||||
} catch (error) {
|
||||
return this.throwSessionModelError("session/set_config_option", modelId, error);
|
||||
@@ -4864,7 +5093,7 @@
|
||||
@@ -4864,7 +5093,7 @@ var AcpClient = class {
|
||||
}
|
||||
selectAuthMethod(methods) {
|
||||
for (const method of methods) {
|
||||
@@ -640,7 +641,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
if (envCredential) return {
|
||||
methodId: method.id,
|
||||
credential: envCredential,
|
||||
@@ -4890,7 +5119,7 @@
|
||||
@@ -4890,7 +5119,7 @@ var AcpClient = class {
|
||||
}
|
||||
readAgentSpecificEnvCredential(methodId) {
|
||||
if (!this.isGrokBuildAcpCommand() || methodId !== "xai.api_key") return;
|
||||
@@ -649,7 +650,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
||||
}
|
||||
selectAgentManagedAuthMethod(methodId) {
|
||||
@@ -4915,9 +5144,9 @@
|
||||
@@ -4915,9 +5144,9 @@ var AcpClient = class {
|
||||
await connection.authenticate({ methodId: selected.methodId });
|
||||
this.log(`authenticated with method ${selected.methodId} (${selected.source})`);
|
||||
}
|
||||
@@ -661,7 +662,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
if (hostResponse) return hostResponse;
|
||||
const { response, recorded } = await this.resolvePermissionRequestFromMode(params);
|
||||
if (!recorded) {
|
||||
@@ -4926,14 +5155,81 @@
|
||||
@@ -4926,14 +5155,81 @@ var AcpClient = class {
|
||||
}
|
||||
return response;
|
||||
}
|
||||
@@ -745,7 +746,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
})).then((response) => ({
|
||||
kind: "response",
|
||||
response
|
||||
@@ -4970,7 +5266,7 @@
|
||||
@@ -4970,7 +5266,7 @@ var AcpClient = class {
|
||||
isElicitationSessionCancelling(sessionId) {
|
||||
return this.closing || this.cancellingSessionIds.has(sessionId);
|
||||
}
|
||||
@@ -754,7 +755,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
if (!this.options.onPermissionRequest) return;
|
||||
const signal = this.cancellationSignalForSession(params.sessionId);
|
||||
try {
|
||||
@@ -4978,7 +5274,7 @@
|
||||
@@ -4978,7 +5274,7 @@ var AcpClient = class {
|
||||
sessionId: params.sessionId,
|
||||
raw: params,
|
||||
inferredKind: inferToolKind(params)
|
||||
@@ -763,7 +764,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
return this.hostPermissionDecisionResponse(params, signal, decision);
|
||||
} catch (error) {
|
||||
return this.hostPermissionErrorResponse(params, signal, error);
|
||||
@@ -5028,6 +5324,12 @@
|
||||
@@ -5028,6 +5324,12 @@ var AcpClient = class {
|
||||
attachAgentLifecycleObservers(child) {
|
||||
child.once("exit", (exitCode, signal) => {
|
||||
this.recordAgentExit("process_exit", exitCode, signal);
|
||||
@@ -776,7 +777,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
});
|
||||
child.once("close", (exitCode, signal) => {
|
||||
this.recordAgentExit("process_close", exitCode, signal);
|
||||
@@ -5100,6 +5402,9 @@
|
||||
@@ -5100,6 +5402,9 @@ var AcpClient = class {
|
||||
return await this.filesystem.readTextFile(params);
|
||||
} catch (error) {
|
||||
this.recordPermissionError(params.sessionId, error);
|
||||
@@ -786,7 +787,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -5239,6 +5544,7 @@
|
||||
@@ -5239,6 +5544,7 @@ function applyConversation(record, conversation) {
|
||||
record.cumulative_token_usage = conversation.cumulative_token_usage;
|
||||
record.cumulative_cost = conversation.cumulative_cost;
|
||||
record.request_token_usage = conversation.request_token_usage;
|
||||
@@ -794,7 +795,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
}
|
||||
//#endregion
|
||||
//#region src/runtime/engine/session-options.ts
|
||||
@@ -5701,7 +6007,8 @@
|
||||
@@ -5701,7 +6007,8 @@ function cloneSessionConversation(conversation) {
|
||||
updated_at: conversation.updated_at,
|
||||
cumulative_token_usage: deepClone(conversation.cumulative_token_usage ?? {}),
|
||||
cumulative_cost: cloneUsageCost(conversation.cumulative_cost),
|
||||
@@ -804,7 +805,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
};
|
||||
}
|
||||
function cloneUsageCost(cost) {
|
||||
@@ -5771,10 +6078,20 @@
|
||||
@@ -5771,10 +6078,20 @@ function recordSessionUpdate(conversation, state, notification, timestamp = isoN
|
||||
trimConversationForRuntime(conversation);
|
||||
return acpx;
|
||||
}
|
||||
@@ -827,7 +828,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
updateConversationTimestamp(conversation, timestamp);
|
||||
trimConversationForRuntime(conversation);
|
||||
return true;
|
||||
@@ -6136,6 +6453,7 @@
|
||||
@@ -6136,6 +6453,7 @@ async function connectAndLoadSession(options) {
|
||||
pendingAgentSessionId = loadState.pendingAgentSessionId;
|
||||
sessionModels = loadState.sessionModels;
|
||||
const preferenceReplay = await replayFreshSessionPreferences({
|
||||
@@ -835,7 +836,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
client,
|
||||
record,
|
||||
createdFreshSession,
|
||||
@@ -6193,14 +6511,16 @@
|
||||
@@ -6193,14 +6511,16 @@ function logReconnectAttempt(record, storedProcessAlive, shouldReconnect, verbos
|
||||
if (shouldReconnect) process.stderr.write(`[acpx] saved session pid ${record.pid} is dead; respawning agent and attempting session reconnect\n`);
|
||||
}
|
||||
async function replayFreshSessionPreferences(params) {
|
||||
@@ -854,7 +855,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
client: params.client,
|
||||
sessionId: params.sessionId,
|
||||
desiredModeId: params.desiredModeId,
|
||||
@@ -6219,7 +6539,7 @@
|
||||
@@ -6219,7 +6539,7 @@ async function replayFreshSessionPreferences(params) {
|
||||
verbose: params.verbose,
|
||||
suppressWarnings: params.suppressWarnings
|
||||
});
|
||||
@@ -863,7 +864,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
client: params.client,
|
||||
record: params.record,
|
||||
sessionId: params.sessionId,
|
||||
@@ -6435,6 +6755,27 @@
|
||||
@@ -6435,6 +6755,27 @@ async function withConnectedSession(options) {
|
||||
//#region src/runtime/engine/prompt-turn.ts
|
||||
const SESSION_REPLY_IDLE_MS = 1e3;
|
||||
const SESSION_REPLY_DRAIN_TIMEOUT_MS = 5e3;
|
||||
@@ -891,7 +892,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
async function runPromptTurn(params) {
|
||||
try {
|
||||
const promptPromise = params.client.prompt(params.sessionId, params.prompt, params.onPromptRequestStarted, params.onElicitation);
|
||||
@@ -6444,7 +6785,23 @@
|
||||
@@ -6444,7 +6785,23 @@ async function runPromptTurn(params) {
|
||||
idleMs: SESSION_REPLY_IDLE_MS,
|
||||
timeoutMs: SESSION_REPLY_DRAIN_TIMEOUT_MS
|
||||
}).catch(() => {});
|
||||
@@ -916,7 +917,7 @@ diff --git a/dist/live-checkpoint-BSIrfgVo.js b/dist/live-checkpoint-BSIrfgVo.js
|
||||
stopReason: response.stopReason,
|
||||
source: "rpc"
|
||||
diff --git a/dist/runtime.d.ts b/dist/runtime.d.ts
|
||||
index e8102acb0..377c5d838 100644
|
||||
index e8102acb03c4c38830ad5ec22f356125eb0423b7..04c7e3ae0453b352447efb53af17f6e25ee98307 100644
|
||||
--- a/dist/runtime.d.ts
|
||||
+++ b/dist/runtime.d.ts
|
||||
@@ -1,7 +1,8 @@
|
||||
@@ -949,10 +950,15 @@ index e8102acb0..377c5d838 100644
|
||||
type AcpRuntimeEvent = {
|
||||
type: "text_delta";
|
||||
text: string;
|
||||
@@ -186,6 +193,10 @@ type AcpRuntimeEvent = {
|
||||
@@ -186,6 +193,15 @@ type AcpRuntimeEvent = {
|
||||
* non-null `input` schema.
|
||||
*/
|
||||
availableCommands?: AcpRuntimeAvailableCommand[];
|
||||
+ /**
|
||||
+ * Paperclip patch: the ACP update's `_meta`, forwarded by reference and
|
||||
+ * unvalidated. Producer-controlled; consumers must bound and allowlist it.
|
||||
+ */
|
||||
+ _meta?: Record<string, unknown>;
|
||||
+} | {
|
||||
+ type: "plan";
|
||||
+ tag: "plan";
|
||||
@@ -960,7 +966,19 @@ index e8102acb0..377c5d838 100644
|
||||
} | {
|
||||
type: "tool_call";
|
||||
text: string;
|
||||
@@ -312,7 +323,51 @@ type AcpRuntimeOptions = {
|
||||
@@ -198,6 +214,11 @@ type AcpRuntimeEvent = {
|
||||
rawInput?: unknown;
|
||||
rawOutput?: unknown;
|
||||
content?: ToolCallContent[];
|
||||
+ /**
|
||||
+ * Paperclip patch: the ACP update's `_meta`, forwarded by reference and
|
||||
+ * unvalidated. Producer-controlled; consumers must bound and allowlist it.
|
||||
+ */
|
||||
+ _meta?: Record<string, unknown>;
|
||||
} |
|
||||
/**
|
||||
* Compatibility terminal event emitted by runTurn(...). startTurn(...).events
|
||||
@@ -312,7 +333,51 @@ type AcpRuntimeOptions = {
|
||||
elicitationModes?: readonly AcpElicitationMode[];
|
||||
onPermissionRequest?: (req: AcpPermissionRequest, ctx: {
|
||||
signal: AbortSignal;
|
||||
@@ -1012,7 +1030,7 @@ index e8102acb0..377c5d838 100644
|
||||
};
|
||||
type AcpFileSessionStoreOptions = {
|
||||
stateDir: string;
|
||||
@@ -369,6 +424,7 @@ declare class AcpRuntimeManager {
|
||||
@@ -369,6 +434,7 @@ declare class AcpRuntimeManager {
|
||||
private createAndSaveRuntimeRecord;
|
||||
private retainInitializedSessionOwner;
|
||||
startTurn(input: {
|
||||
@@ -1020,7 +1038,7 @@ index e8102acb0..377c5d838 100644
|
||||
handle: AcpRuntimeHandle;
|
||||
text: string;
|
||||
attachments?: AcpRuntimeTurnAttachment[];
|
||||
@@ -414,6 +470,7 @@ declare class AcpRuntimeManager {
|
||||
@@ -414,6 +480,7 @@ declare class AcpRuntimeManager {
|
||||
private cleanupRuntimeTurn;
|
||||
private finalizeRuntimeTurnRecord;
|
||||
runTurn(input: {
|
||||
@@ -1029,7 +1047,7 @@ index e8102acb0..377c5d838 100644
|
||||
text: string;
|
||||
attachments?: AcpRuntimeTurnAttachment[];
|
||||
diff --git a/dist/runtime.js b/dist/runtime.js
|
||||
index a1f4a70a0..a17fce3b3 100644
|
||||
index a1f4a70a003792c6eacf68b6b038f37bfec1db53..f8603a26e626253b454812865b99b0d7aa4495f6 100644
|
||||
--- a/dist/runtime.js
|
||||
+++ b/dist/runtime.js
|
||||
@@ -153,7 +153,7 @@ function resolveTextChunk(params) {
|
||||
@@ -1050,7 +1068,15 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
return {
|
||||
type: "text_delta",
|
||||
text,
|
||||
@@ -371,7 +371,7 @@ const PROMPT_EVENT_PARSERS = {
|
||||
@@ -321,6 +321,7 @@ function assignForwardedToolPayload(event, payload) {
|
||||
if (Object.prototype.hasOwnProperty.call(payload, "rawInput")) event.rawInput = payload.rawInput;
|
||||
if (Object.prototype.hasOwnProperty.call(payload, "rawOutput")) event.rawOutput = payload.rawOutput;
|
||||
if (shouldForwardArray(payload.content)) event.content = payload.content;
|
||||
+ if (isRecord(payload._meta)) event._meta = payload._meta;
|
||||
}
|
||||
function parsePromptEventLine(line) {
|
||||
const trimmed = line.trim();
|
||||
@@ -371,7 +372,7 @@ const PROMPT_EVENT_PARSERS = {
|
||||
current_mode_update: (payload) => statusUpdateEvent("current_mode_update", payload),
|
||||
config_option_update: (payload) => statusUpdateEvent("config_option_update", payload),
|
||||
session_info_update: (payload) => statusUpdateEvent("session_info_update", payload),
|
||||
@@ -1059,7 +1085,32 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
client_operation: clientOperationEvent,
|
||||
update: updateStatusEvent,
|
||||
done: () => null,
|
||||
@@ -424,6 +424,34 @@ function availableCommandsUpdateEvent(payload) {
|
||||
@@ -388,11 +389,12 @@ function usageUpdateEvent(payload) {
|
||||
used,
|
||||
size,
|
||||
cost: normalizeUsageCost(payload.cost),
|
||||
- breakdown: normalizeUsageBreakdown(meta?.usage)
|
||||
+ breakdown: normalizeUsageBreakdown(meta?.usage),
|
||||
+ meta
|
||||
});
|
||||
}
|
||||
function buildUsageUpdateEvent(parts) {
|
||||
- const { used, size, cost, breakdown } = parts;
|
||||
+ const { used, size, cost, breakdown, meta } = parts;
|
||||
return {
|
||||
type: "status",
|
||||
text: used != null && size != null ? `usage updated: ${used}/${size}` : "usage updated",
|
||||
@@ -400,7 +402,8 @@ function buildUsageUpdateEvent(parts) {
|
||||
...used != null ? { used } : {},
|
||||
...size != null ? { size } : {},
|
||||
...cost ? { cost } : {},
|
||||
- ...breakdown ? { breakdown } : {}
|
||||
+ ...breakdown ? { breakdown } : {},
|
||||
+ ...meta ? { _meta: meta } : {}
|
||||
};
|
||||
}
|
||||
function availableCommandsUpdateEvent(payload) {
|
||||
@@ -424,6 +427,34 @@ function availableCommandsUpdateEvent(payload) {
|
||||
availableCommands
|
||||
};
|
||||
}
|
||||
@@ -1094,7 +1145,17 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
function normalizeUsageCost(value) {
|
||||
if (!isRecord(value)) return;
|
||||
const amount = asOptionalFiniteNumber(value.amount);
|
||||
@@ -812,7 +840,22 @@ var AcpRuntimeManager = class {
|
||||
@@ -474,7 +505,8 @@ function statusUpdateEvent(tag, payload) {
|
||||
return {
|
||||
type: "status",
|
||||
text,
|
||||
- tag
|
||||
+ tag,
|
||||
+ ...isRecord(payload._meta) ? { _meta: payload._meta } : {}
|
||||
};
|
||||
}
|
||||
function clientOperationEvent(payload, tag) {
|
||||
@@ -812,7 +844,22 @@ var AcpRuntimeManager = class {
|
||||
this.deps = deps;
|
||||
}
|
||||
createClient(options) {
|
||||
@@ -1118,7 +1179,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
}
|
||||
createSessionOwner(input) {
|
||||
const owner = {
|
||||
@@ -821,12 +864,16 @@ var AcpRuntimeManager = class {
|
||||
@@ -821,12 +868,16 @@ var AcpRuntimeManager = class {
|
||||
pendingSessionUpdates: []
|
||||
};
|
||||
input.client.setEventHandlers({
|
||||
@@ -1135,7 +1196,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
const active = owner.activeTurn;
|
||||
if (active) {
|
||||
const { task, turn } = active;
|
||||
@@ -863,6 +910,9 @@ var AcpRuntimeManager = class {
|
||||
@@ -863,6 +914,9 @@ var AcpRuntimeManager = class {
|
||||
projection.checkpoint.request();
|
||||
}
|
||||
routeOwnedClientOperation(owner, operation) {
|
||||
@@ -1145,7 +1206,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
const active = owner.activeTurn;
|
||||
if (!active) return;
|
||||
const { task, turn } = active;
|
||||
@@ -1008,6 +1058,8 @@ var AcpRuntimeManager = class {
|
||||
@@ -1008,6 +1062,8 @@ var AcpRuntimeManager = class {
|
||||
record.closedAt = void 0;
|
||||
record.protocolVersion = owner.client.initializeResult?.protocolVersion;
|
||||
record.agentCapabilities = owner.client.initializeResult?.agentCapabilities;
|
||||
@@ -1154,7 +1215,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
applyLifecycleSnapshotToRecord(record, owner.client.getAgentLifecycleSnapshot());
|
||||
}
|
||||
async finishBufferedOwnerControl(owner, record) {
|
||||
@@ -1086,6 +1138,11 @@ var AcpRuntimeManager = class {
|
||||
@@ -1086,6 +1142,11 @@ var AcpRuntimeManager = class {
|
||||
record.closed = false;
|
||||
record.closedAt = void 0;
|
||||
this.closingActiveRecords.delete(record.acpxRecordId);
|
||||
@@ -1166,7 +1227,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
await this.options.sessionStore.save(record);
|
||||
return record;
|
||||
}
|
||||
@@ -1149,6 +1206,8 @@ var AcpRuntimeManager = class {
|
||||
@@ -1149,6 +1210,8 @@ var AcpRuntimeManager = class {
|
||||
this.closingActiveRecords.delete(record.acpxRecordId);
|
||||
record.protocolVersion = client.initializeResult?.protocolVersion;
|
||||
record.agentCapabilities = client.initializeResult?.agentCapabilities;
|
||||
@@ -1175,7 +1236,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
applyConfigOptionsToRecord(record, session.sessionResult);
|
||||
const modelApplication = await applyRequestedModelIfAdvertised({
|
||||
client,
|
||||
@@ -1301,9 +1360,11 @@ var AcpRuntimeManager = class {
|
||||
@@ -1301,9 +1364,11 @@ var AcpRuntimeManager = class {
|
||||
client: turn.client,
|
||||
sessionId,
|
||||
prompt: task.promptInput,
|
||||
@@ -1187,7 +1248,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
onPromptRequestStarted: () => task.promptStarted.resolve(),
|
||||
onElicitation: task.input.onElicitation
|
||||
});
|
||||
@@ -1469,6 +1530,10 @@ var AcpRuntimeManager = class {
|
||||
@@ -1469,6 +1534,10 @@ var AcpRuntimeManager = class {
|
||||
setSessionConfigOption: async (configId, value) => {
|
||||
return (await task.state.activeController.setResolvedSessionConfigOption(configId, value)).response;
|
||||
},
|
||||
@@ -1198,7 +1259,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
setResolvedSessionConfigOption: async (configId, value) => await this.setRuntimeResolvedSessionConfigOption(task, turn, configId, value)
|
||||
};
|
||||
}
|
||||
@@ -1575,6 +1640,8 @@ var AcpRuntimeManager = class {
|
||||
@@ -1575,6 +1644,8 @@ var AcpRuntimeManager = class {
|
||||
reconcileAgentSessionId(turn.record, turn.record.agentSessionId);
|
||||
turn.record.protocolVersion = turn.client.initializeResult?.protocolVersion;
|
||||
turn.record.agentCapabilities = turn.client.initializeResult?.agentCapabilities;
|
||||
@@ -1207,7 +1268,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
turn.record.acpx = turn.acpxState;
|
||||
applyConversation(turn.record, turn.conversation);
|
||||
applyLifecycleSnapshotToRecord(turn.record, turn.client.getAgentLifecycleSnapshot());
|
||||
@@ -1707,6 +1774,17 @@ var AcpRuntimeManager = class {
|
||||
@@ -1707,6 +1778,17 @@ var AcpRuntimeManager = class {
|
||||
});
|
||||
await this.options.sessionStore.save(result.record);
|
||||
}
|
||||
@@ -1225,7 +1286,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
async cancel(handle) {
|
||||
await this.activeControllers.get(handle.acpxRecordId ?? handle.sessionKey)?.requestCancelActivePrompt();
|
||||
}
|
||||
@@ -2055,6 +2133,7 @@ var AcpxRuntime = class {
|
||||
@@ -2055,6 +2137,7 @@ var AcpxRuntime = class {
|
||||
const turnPromise = this.getManager().then((manager) => manager.startTurn({
|
||||
handle,
|
||||
text: input.text,
|
||||
@@ -1233,7 +1294,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
attachments: input.attachments,
|
||||
mode: input.mode,
|
||||
sessionMode: state.mode,
|
||||
@@ -2087,6 +2166,7 @@ var AcpxRuntime = class {
|
||||
@@ -2087,6 +2170,7 @@ var AcpxRuntime = class {
|
||||
yield* (await this.getManager()).runTurn({
|
||||
handle,
|
||||
text: input.text,
|
||||
@@ -1241,7 +1302,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
attachments: input.attachments,
|
||||
mode: input.mode,
|
||||
sessionMode: state.mode,
|
||||
@@ -2119,6 +2199,14 @@ var AcpxRuntime = class {
|
||||
@@ -2119,6 +2203,14 @@ var AcpxRuntime = class {
|
||||
const { handle, state } = this.resolveManagerHandle(input.handle);
|
||||
await (await this.getManager()).setConfigOption(handle, input.key, input.value, state.mode);
|
||||
}
|
||||
@@ -1257,7 +1318,7 @@ index a1f4a70a0..a17fce3b3 100644
|
||||
const { handle } = this.resolveManagerHandle(input.handle);
|
||||
await (await this.getManager()).cancel(handle);
|
||||
diff --git a/dist/session-options-DwRDODlr.d.ts b/dist/session-options-DwRDODlr.d.ts
|
||||
index c3da16452..40ae05653 100644
|
||||
index c3da1645235bbea22de3f8484149051cd7dca56b..40ae0565394aaa96e0315a2cf3c943c500c77560 100644
|
||||
--- a/dist/session-options-DwRDODlr.d.ts
|
||||
+++ b/dist/session-options-DwRDODlr.d.ts
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
Reference in new issue
Block a user