@gleapai/kai-bridge 0.2.2 → 0.2.3
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/package.json +1 -1
- package/runner/lib/acp/harnesses.mjs +16 -1
- package/runner/lib/contract.mjs +9 -12
- package/src/executor.mjs +14 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gleapai/kai-bridge",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Run Gleap Kai Code sessions on your own machine with your own Claude Code / Codex login — and preview your real dev servers from the dashboard or the phone.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,6 +56,11 @@ export function pickSessionMode(preferred, available) {
|
|
|
56
56
|
|
|
57
57
|
const OPENROUTER_BASE_URL = "https://openrouter.ai/api";
|
|
58
58
|
|
|
59
|
+
// Backstop turn cap for gateway (non-native-Anthropic) claude-harness
|
|
60
|
+
// runs when the host sends no explicit maxSteps — see the maxTurns spread
|
|
61
|
+
// in the claude harness options below.
|
|
62
|
+
const GATEWAY_FALLBACK_MAX_TURNS = 500;
|
|
63
|
+
|
|
59
64
|
/**
|
|
60
65
|
* Resolve the adapter binary: explicit override (`KAI_ACP_AGENT_CMD`,
|
|
61
66
|
* JSON `{cmd, args}` — tests and the local bridge's per-profile
|
|
@@ -208,7 +213,17 @@ export const HARNESSES = {
|
|
|
208
213
|
permissionMode: ctx.isPlanMode ? "plan" : ctx.isArtifactWriter ? "dontAsk" : "bypassPermissions",
|
|
209
214
|
...(ctx.allowedTools?.length ? { allowedTools: ctx.allowedTools } : {}),
|
|
210
215
|
...(ctx.disallowedTools?.length ? { disallowedTools: ctx.disallowedTools } : {}),
|
|
211
|
-
|
|
216
|
+
// Turn cap: explicit maxSteps wins; otherwise native Anthropic
|
|
217
|
+
// runs uncapped — maxBudgetUsd is the guard, and the CLI can
|
|
218
|
+
// price its own models. Gateway models get a generous backstop
|
|
219
|
+
// instead: the CLI prices unknown slugs at ~$0, so the budget
|
|
220
|
+
// never trips for them and turns are the only in-run guard
|
|
221
|
+
// that still bites.
|
|
222
|
+
...(ctx.maxSteps
|
|
223
|
+
? { maxTurns: ctx.maxSteps }
|
|
224
|
+
: isNativeAnthropic(ctx.model)
|
|
225
|
+
? {}
|
|
226
|
+
: { maxTurns: GATEWAY_FALLBACK_MAX_TURNS }),
|
|
212
227
|
...(ctx.maxBudgetUsd ? { maxBudgetUsd: ctx.maxBudgetUsd } : {}),
|
|
213
228
|
...(ctx.additionalDirectories?.length ? { additionalDirectories: ctx.additionalDirectories } : {}),
|
|
214
229
|
settings: {
|
package/runner/lib/contract.mjs
CHANGED
|
@@ -336,13 +336,6 @@ export function revertRepoMutations(workDir, baselines = null) {
|
|
|
336
336
|
const SUPPORTED_FILE_PART_MIME_PREFIXES = ["image/"];
|
|
337
337
|
const SUPPORTED_FILE_PART_MIMES = new Set(["application/pdf"]);
|
|
338
338
|
|
|
339
|
-
// LEGACY: steps are no longer enforced as a turn limit — budget
|
|
340
|
-
// (--max-budget-usd) is the sole cap (budget-only decision, 2026-06-18).
|
|
341
|
-
// Retained only so `--max-steps` parsing stays backward-compatible
|
|
342
|
-
// (Runner Contract v1); runners parse the value but ignore it for
|
|
343
|
-
// enforcement. Safe to remove once no caller passes --max-steps.
|
|
344
|
-
export const DEFAULT_STEP_CAP = 50;
|
|
345
|
-
|
|
346
339
|
/**
|
|
347
340
|
* Decode the full runner argv contract into one options object. All
|
|
348
341
|
* flags are optional except `--task-b64`; callers fail fast on an
|
|
@@ -355,9 +348,11 @@ export const DEFAULT_STEP_CAP = 50;
|
|
|
355
348
|
* --effort <tier> low|medium|high|extra_high|max
|
|
356
349
|
* --session-id <id> resume an existing engine session/thread
|
|
357
350
|
* --max-budget-usd <number> abort when accumulated cost exceeds
|
|
358
|
-
* --max-steps <number>
|
|
359
|
-
*
|
|
360
|
-
*
|
|
351
|
+
* --max-steps <number> explicit turn cap (SDK maxTurns) for
|
|
352
|
+
* agents that want one; OMITTED = no
|
|
353
|
+
* turn cap — budget (--max-budget-usd)
|
|
354
|
+
* is the primary limit (budget-only
|
|
355
|
+
* decision, 2026-06-18).
|
|
361
356
|
* --task-b64 <base64> REQUIRED — the user prompt
|
|
362
357
|
* --feedback-b64 <base64> wrapped follow-up prompt for resumes
|
|
363
358
|
* --answers-b64 <base64-json> string[][] question replies
|
|
@@ -378,11 +373,13 @@ export function parseRunnerArgs(rawArgs) {
|
|
|
378
373
|
|
|
379
374
|
const agent = normalizeAgentName(argv.agent);
|
|
380
375
|
|
|
376
|
+
// No default: an absent --max-steps means NO turn cap (the harness then
|
|
377
|
+
// omits the SDK's maxTurns entirely). Budget/deadline are the real guards.
|
|
381
378
|
const maxSteps = (() => {
|
|
382
379
|
const raw = argv["max-steps"];
|
|
383
|
-
if (raw == null || raw === true) return
|
|
380
|
+
if (raw == null || raw === true) return undefined;
|
|
384
381
|
const n = Number(raw);
|
|
385
|
-
return Number.isFinite(n) && n > 0 ? n :
|
|
382
|
+
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
386
383
|
})();
|
|
387
384
|
|
|
388
385
|
const maxBudgetUsd =
|
package/src/executor.mjs
CHANGED
|
@@ -20,7 +20,7 @@ const RUNNER = join(dirname(fileURLToPath(import.meta.url)), "..", "runner", "ac
|
|
|
20
20
|
const b64 = (v) => Buffer.from(typeof v === "string" ? v : JSON.stringify(v), "utf8").toString("base64");
|
|
21
21
|
|
|
22
22
|
/** turn.start payload → runner argv (no shell — we spawn node directly). */
|
|
23
|
-
export function buildRunnerArgs(turn, workDir) {
|
|
23
|
+
export function buildRunnerArgs(turn, workDir, profile) {
|
|
24
24
|
const args = ["--task-b64", b64(turn.task || ""), "--work-dir", workDir];
|
|
25
25
|
// The Server's coder payload signals plan mode as `planMode`, not as an
|
|
26
26
|
// agent name (the analyzer host does this same mapping before spawning
|
|
@@ -37,8 +37,18 @@ export function buildRunnerArgs(turn, workDir) {
|
|
|
37
37
|
// hint and mints its own ACP id).
|
|
38
38
|
if (turn.acpSessionId || turn.sessionId) args.push("--session-id", turn.acpSessionId || turn.sessionId);
|
|
39
39
|
if (turn.harness) args.push("--harness", turn.harness);
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
// BYO turns run uncapped: no --max-steps and no --max-budget-usd, even
|
|
41
|
+
// when the Server sends them — the work bills the operator's own harness
|
|
42
|
+
// subscription, so cost guards are the cloud's concern only. An absent
|
|
43
|
+
// --max-steps means no SDK maxTurns — a deep run must never die with
|
|
44
|
+
// "Reached maximum number of turns".
|
|
45
|
+
//
|
|
46
|
+
// The one exception is the `gleap-key` profile: there the Server hands
|
|
47
|
+
// the device GLEAP's API key (turn.credentials) and bills the org's AI
|
|
48
|
+
// credits, so the Server-computed wallet headroom must keep enforcing.
|
|
49
|
+
if (profile?.kind === "gleap-key" && turn.maxBudgetUsd > 0) {
|
|
50
|
+
args.push("--max-budget-usd", String(turn.maxBudgetUsd));
|
|
51
|
+
}
|
|
42
52
|
if (turn.feedback) args.push("--feedback-b64", b64(turn.feedback));
|
|
43
53
|
if (turn.questionAnswers?.length) args.push("--answers-b64", b64(turn.questionAnswers));
|
|
44
54
|
if (turn.attachments?.length) args.push("--attachments-b64", b64(turn.attachments));
|
|
@@ -114,7 +124,7 @@ export function buildRunnerEnv(turn, profile, kaiHome = KAI_HOME) {
|
|
|
114
124
|
*/
|
|
115
125
|
export function runTurn({ turn, profile, workDir, onEvent, onLog = () => {}, signal, kaiHome = KAI_HOME }) {
|
|
116
126
|
return new Promise((resolve) => {
|
|
117
|
-
const args = buildRunnerArgs(turn, workDir);
|
|
127
|
+
const args = buildRunnerArgs(turn, workDir, profile);
|
|
118
128
|
const env = buildRunnerEnv(turn, profile, kaiHome);
|
|
119
129
|
const child = spawn(process.execPath, [RUNNER, ...args], { cwd: workDir, env, stdio: ["ignore", "pipe", "pipe"] });
|
|
120
130
|
let result = null;
|