@krapht/ork 1.0.0-alpha.13 → 1.0.0-alpha.14
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/{__build_info__-Da73_nBs.mjs → __build_info__-B82-dGJY.mjs} +2 -2
- package/dist/{attach-E6NNyA3T.mjs → attach-CdtGX9se.mjs} +5 -5
- package/dist/bin.mjs +194 -215
- package/dist/{builtin-files-n6o6oWpB.mjs → builtin-files-Bdq23IIi.mjs} +1 -1
- package/dist/{chips-CyeB_rhR.mjs → chips-BqcT58qr.mjs} +1 -1
- package/dist/{completion-SNNNxnv9.mjs → completion--ywde-56.mjs} +52 -11
- package/dist/{context-DHudLA5e.mjs → context-Y_I4Mofq.mjs} +17 -7
- package/dist/{find-CjsNYAAf.mjs → find-BVX-bL4u.mjs} +1 -1
- package/dist/{format-C60TFpRe.mjs → format-CvTE5CZQ.mjs} +12 -12
- package/dist/{help-groups-CyfPWlST.mjs → help-groups-CpR2UzSn.mjs} +16 -6
- package/dist/{in-a-run-BYjTXaST.mjs → in-a-run-cL5N9gpH.mjs} +1 -1
- package/dist/{limits-C5QKZNLT.mjs → limits-Dhe--wmX.mjs} +1 -1
- package/dist/{mcp-bcctR-wc.mjs → mcp-CsmboyXv.mjs} +6 -6
- package/dist/{once-DPQu5Q3H.mjs → once-CrwI-ZdN.mjs} +4 -4
- package/dist/{output-CnRzrbUM.mjs → output-B1kwo0YU.mjs} +17 -1
- package/dist/{pause-BPvlDIvz.mjs → pause-DssrYcda.mjs} +4 -4
- package/dist/{relay-C8W0t_zc.mjs → relay-DASRgsTQ.mjs} +2 -2
- package/dist/{run-CQIfq918.mjs → run-D8divGCs.mjs} +1 -1
- package/dist/send-CXv8bx0i.mjs +105 -0
- package/dist/{src-CGJGL1sZ.mjs → src-8Mul0695.mjs} +8 -10
- package/dist/{src-D93tHZ5B.mjs → src-C7z_6fhn.mjs} +92 -39
- package/dist/{src-CqRqsb2t.mjs → src-njdtejgZ.mjs} +98 -33
- package/dist/{src-BeR6f70A.mjs → src-tsuZRu0H.mjs} +2353 -918
- package/dist/{store-CM3cERGl.mjs → store-CleF7KeS.mjs} +21 -73
- package/dist/ui/assets/app-D_nAPnHM.css +1 -0
- package/dist/ui/assets/{app-DYRC0V4p.js → app-DqBAtXSZ.js} +19 -19
- package/dist/ui/assets/{index-CWwm8HTG.js → index-D-_uPI2v.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/dist/{ui-D2Ydofq0.mjs → ui-CNO1M0qT.mjs} +4 -4
- package/dist/{usage-6WIowblz.mjs → usage-BVXi0iFY.mjs} +13 -141
- package/dist/{verdict-CIy8tAyX.mjs → verdict-CMvPCpWC.mjs} +2 -2
- package/dist/{watch-uOU_WeBI.mjs → watch-DVNIciHp.mjs} +10 -8
- package/dist/{workflows-7tQdMnrA.mjs → workflows-B30QkwTk.mjs} +3 -3
- package/dist/{worktrees-ZCII298F.mjs → worktrees-DD4_TpkH.mjs} +3 -5
- package/package.json +2 -2
- package/dist/clean-Bh4KZgzc.mjs +0 -91
- package/dist/decide-DuIHACvH.mjs +0 -50
- package/dist/list-BUb5Uc3H.mjs +0 -117
- package/dist/remove-BkFWGVYo.mjs +0 -201
- package/dist/report-CEN8VoUb.mjs +0 -140
- package/dist/send-CaZlGG5a.mjs +0 -163
- package/dist/ui/assets/app-QoHVi-mb.css +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $t as
|
|
2
|
-
import { t as runOnce } from "./run-
|
|
1
|
+
import { $t as countedTokens, Cn as MCP_FILE, Dn as endOf, Ft as reportSchemaFor, Gn as parseTemplate, Gr as isToolNode, Ht as heldTimeout, Kn as renderTemplate, Lr as markValue, Mr as beforeOf, Nr as programsRunBy, Nt as planNodeSession, On as outcomeEnd, Qr as integrationFor, Tn as serversIn, Wn as evaluate, Wr as isClaudeNode, Xr as INTEGRATIONS, Yr as timeoutMs, Zr as TOOL_PREFIX, an as stoppedEnd, en as runScope, gn as sessionDriver, ii as AGENT_DRIVERS, in as failedWith, jn as endFor, kn as END_DETAIL_LIMIT, kt as planReason, ln as lowerChild, mn as envReadsOf, nt as sessionRefusals, on as declaredProblem, rn as endedWith, sn as slotClock, tn as calledWorkflows, un as startAt, vn as declaredOutputOf, wn as mcpNotText } from "./src-tsuZRu0H.mjs";
|
|
2
|
+
import { t as runOnce } from "./run-D8divGCs.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { accessSync, constants, lstatSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
@@ -538,43 +538,36 @@ const afterLateTurn = (first, later) => {
|
|
|
538
538
|
errors: first.errors
|
|
539
539
|
};
|
|
540
540
|
};
|
|
541
|
+
/** The least of the limits that are set; `undefined` with none. */
|
|
542
|
+
const least = (...limits) => {
|
|
543
|
+
const known = limits.filter((limit) => limit !== void 0);
|
|
544
|
+
return known.length === 0 ? void 0 : Math.min(...known);
|
|
545
|
+
};
|
|
546
|
+
/** A cap less what this activation used, beside what a quota says is left. */
|
|
547
|
+
const leftOf = (caps, used, quotaLeft) => least(...caps.map((cap) => cap === void 0 ? void 0 : cap - used), quotaLeft);
|
|
541
548
|
/**
|
|
542
|
-
* What another turn of a session may use. `spent
|
|
543
|
-
* activation's, which neither the node's cap nor the run's
|
|
544
|
-
* The quota has: the ledger took it as the session
|
|
545
|
-
*
|
|
549
|
+
* What another turn of a session may use. `spent`, `tokens` (counted, RFC-015)
|
|
550
|
+
* and `turns` are this activation's, which neither the node's cap nor the run's
|
|
551
|
+
* budget has counted. The quota has: the ledger took it as the session
|
|
552
|
+
* finished, so what it says is left is left. The run's tokens left have too:
|
|
553
|
+
* only the node's own `budgetTokens` takes `tokens` off. `null` when nothing
|
|
554
|
+
* is, and the turn does not start.
|
|
546
555
|
*/
|
|
547
|
-
const lateLimits = (spec,
|
|
548
|
-
const
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
556
|
+
const lateLimits = (spec, used, runLeft, quotaLeft, tokensLeft = {}) => {
|
|
557
|
+
const budgetUsd = leftOf([spec.budgetUsd, runLeft], used.spent, quotaLeft);
|
|
558
|
+
const budgetTokens = leftOf([spec.budgetTokens], used.tokens ?? 0, least(tokensLeft.run, tokensLeft.quota));
|
|
559
|
+
const maxTurns = spec.maxTurns === void 0 ? void 0 : spec.maxTurns - used.turns;
|
|
560
|
+
if ([
|
|
561
|
+
budgetUsd,
|
|
562
|
+
budgetTokens,
|
|
563
|
+
maxTurns
|
|
564
|
+
].some((left) => (left ?? 1) <= 0)) return null;
|
|
553
565
|
return {
|
|
554
566
|
...budgetUsd === void 0 ? {} : { budgetUsd },
|
|
567
|
+
...budgetTokens === void 0 ? {} : { budgetTokens },
|
|
555
568
|
...maxTurns === void 0 ? {} : { maxTurns }
|
|
556
569
|
};
|
|
557
570
|
};
|
|
558
|
-
/** What a session may spend as it starts: the least of the budget and any quota. */
|
|
559
|
-
const budgetNow = (context) => {
|
|
560
|
-
if (context.budgetLeftNow === void 0) return context.budgetLeftUsd;
|
|
561
|
-
const known = [context.budgetLeftNow(), context.quota?.().leftUsd].flatMap((left) => left === void 0 ? [] : [left]);
|
|
562
|
-
return known.length === 0 ? void 0 : Math.min(...known);
|
|
563
|
-
};
|
|
564
|
-
/**
|
|
565
|
-
* What a node's first session may spend, set aside from the run's budget in the
|
|
566
|
-
* same step that reads it (ADR-077), so a node starting beside it is never
|
|
567
|
-
* offered the same part: the least of what is left and the node's own
|
|
568
|
-
* `budgetUsd`. With nothing left, it waits for a worker running beside it to
|
|
569
|
-
* finish, as long as one may.
|
|
570
|
-
*/
|
|
571
|
-
const budgetToStart = async (context) => {
|
|
572
|
-
const left = budgetNow(context);
|
|
573
|
-
if (left !== void 0 && left <= 0) return await context.budgetFreed?.(context.signal) === true ? budgetToStart(context) : left;
|
|
574
|
-
const own = isClaudeNode(context.node) ? context.node.budgetUsd ?? context.workflow.defaults?.budgetUsd : void 0;
|
|
575
|
-
context.reserveBudget?.(own === void 0 || left === void 0 ? left : Math.min(own, left));
|
|
576
|
-
return left;
|
|
577
|
-
};
|
|
578
571
|
/**
|
|
579
572
|
* Whether a limit across runs, rather than the node's work, ended a node: a
|
|
580
573
|
* session whose cap was a quota's and that ran into it, or one its plan refused
|
|
@@ -587,7 +580,7 @@ const limitWatch = (context) => {
|
|
|
587
580
|
why = reason;
|
|
588
581
|
},
|
|
589
582
|
session: (asked) => {
|
|
590
|
-
const left = context.quota?.()
|
|
583
|
+
const { leftUsd: left, leftTokens } = context.quota?.() ?? {};
|
|
591
584
|
let refused;
|
|
592
585
|
return {
|
|
593
586
|
saw: (event) => {
|
|
@@ -595,7 +588,7 @@ const limitWatch = (context) => {
|
|
|
595
588
|
},
|
|
596
589
|
ended: (outcome) => {
|
|
597
590
|
if (refused !== void 0 && (outcome?.report ?? null) === null) why = refused;
|
|
598
|
-
const capped = left !== void 0 && asked.budgetUsd !== void 0 && asked.budgetUsd >= left;
|
|
591
|
+
const capped = left !== void 0 && asked.budgetUsd !== void 0 && asked.budgetUsd >= left || leftTokens !== void 0 && asked.budgetTokens !== void 0 && asked.budgetTokens >= leftTokens;
|
|
599
592
|
const spent = outcome !== null && outcomeEnd(outcome).kind === "budget_limit";
|
|
600
593
|
if (capped && spent) why = context.quota?.().blocked ?? "a session reached what was left of a quota";
|
|
601
594
|
}
|
|
@@ -657,6 +650,61 @@ const sessionDriverOf = (context, plan, options) => {
|
|
|
657
650
|
const unfit = plan.pinned === void 0 ? void 0 : refusalOn(context, driver, options.drivers ?? AGENT_DRIVERS);
|
|
658
651
|
return unfit === void 0 ? driver : notStarted("invalid_node", unfit);
|
|
659
652
|
};
|
|
653
|
+
// ../nodes/src/start-budgets.ts
|
|
654
|
+
/** What a session may spend as it starts: the least of the budget and any quota. */
|
|
655
|
+
const budgetNow = (context) => context.budgetLeftNow === void 0 ? context.budgetLeftUsd : least(context.budgetLeftNow(), context.quota?.().leftUsd);
|
|
656
|
+
/** The counted tokens a session may use as it starts: the run's and a quota's. */
|
|
657
|
+
const tokensNow = (context) => least(context.tokensLeftNow?.(), context.quota?.().leftTokens);
|
|
658
|
+
/**
|
|
659
|
+
* What is `left` now, with `own` (the node's cap) set aside through `reserve`
|
|
660
|
+
* in the same step; with nothing left, it waits for a worker's reserve to
|
|
661
|
+
* free.
|
|
662
|
+
*/
|
|
663
|
+
const startWith = async (context, left, own, reserve) => {
|
|
664
|
+
const now = left();
|
|
665
|
+
if (now !== void 0 && now <= 0) {
|
|
666
|
+
context.reserveBudget?.(0);
|
|
667
|
+
context.reserveTokens?.(0);
|
|
668
|
+
return await context.budgetFreed?.(context.signal) === true ? startWith(context, left, own, reserve) : now;
|
|
669
|
+
}
|
|
670
|
+
reserve?.(own === void 0 || now === void 0 ? now : Math.min(own, now));
|
|
671
|
+
return now;
|
|
672
|
+
};
|
|
673
|
+
/**
|
|
674
|
+
* What a node's first session may spend, set aside from the run's budget in the
|
|
675
|
+
* same step that reads it (ADR-077), so a node starting beside it is never
|
|
676
|
+
* offered the same part: the least of what is left and the node's own
|
|
677
|
+
* `budgetUsd`. With nothing left, it waits for a worker running beside it to
|
|
678
|
+
* finish, as long as one may.
|
|
679
|
+
*/
|
|
680
|
+
const budgetToStart = (context) => startWith(context, () => budgetNow(context), isClaudeNode(context.node) ? context.node.budgetUsd ?? context.workflow.defaults?.budgetUsd : void 0, context.reserveBudget);
|
|
681
|
+
/** `budgetToStart` in counted tokens: the run's `budgetTokens` and a quota's. */
|
|
682
|
+
const tokensToStart = (context) => startWith(context, () => tokensNow(context), isClaudeNode(context.node) ? context.node.budgetTokens ?? context.workflow.defaults?.budgetTokens : void 0, context.reserveTokens);
|
|
683
|
+
/**
|
|
684
|
+
* What a node's first session may spend, in dollars and in counted tokens, set
|
|
685
|
+
* into `planOptions`; the node's result instead when nothing is left of
|
|
686
|
+
* either.
|
|
687
|
+
*/
|
|
688
|
+
const startBudgets = async (context, planOptions) => {
|
|
689
|
+
const usd = await budgetToStart(context);
|
|
690
|
+
if (usd !== void 0 && usd <= 0) return notStarted("budget_limit", "nothing is left of the run's budget");
|
|
691
|
+
if (usd !== void 0) planOptions.budgetLeftUsd = usd;
|
|
692
|
+
const waited = { done: false };
|
|
693
|
+
const tokens = await tokensToStart({
|
|
694
|
+
...context,
|
|
695
|
+
budgetFreed: async (signal) => {
|
|
696
|
+
waited.done = true;
|
|
697
|
+
return await context.budgetFreed?.(signal) ?? false;
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
if (tokens !== void 0 && tokens <= 0) return notStarted("budget_limit", "nothing is left of the run's tokens");
|
|
701
|
+
if (waited.done) {
|
|
702
|
+
context.reserveTokens?.(0);
|
|
703
|
+
return startBudgets(context, planOptions);
|
|
704
|
+
}
|
|
705
|
+
if (tokens !== void 0) planOptions.budgetLeftTokens = tokens;
|
|
706
|
+
return null;
|
|
707
|
+
};
|
|
660
708
|
// ../nodes/src/tool-output.ts
|
|
661
709
|
/**
|
|
662
710
|
* @file A tool node's structured result (`output: { format: json }`): the file
|
|
@@ -1742,9 +1790,8 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1742
1790
|
}
|
|
1743
1791
|
const blocked = context.quota?.().blocked;
|
|
1744
1792
|
if (blocked !== void 0) return notStarted("plan_limit", blocked, blocked);
|
|
1745
|
-
const
|
|
1746
|
-
if (
|
|
1747
|
-
if (budget !== void 0) planOptions.budgetLeftUsd = budget;
|
|
1793
|
+
const refused = await startBudgets(context, planOptions);
|
|
1794
|
+
if (refused !== null) return refused;
|
|
1748
1795
|
const plan = planNodeSession(context.workflow, context.id, planOptions);
|
|
1749
1796
|
if (!plan.ok) return notStarted("invalid_node", plan.error);
|
|
1750
1797
|
const servers = namedMcpEntries(context.projectRoot, plan.mcp);
|
|
@@ -1775,6 +1822,7 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1775
1822
|
const run = async (asked) => {
|
|
1776
1823
|
const watching = watch.session(asked);
|
|
1777
1824
|
context.reserveBudget?.(asked.budgetUsd);
|
|
1825
|
+
context.reserveTokens?.(asked.budgetTokens);
|
|
1778
1826
|
const session = runtime.start(asked, ports);
|
|
1779
1827
|
const stop = () => {
|
|
1780
1828
|
session.interrupt();
|
|
@@ -1793,6 +1841,7 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1793
1841
|
}
|
|
1794
1842
|
} finally {
|
|
1795
1843
|
context.reserveBudget?.(0);
|
|
1844
|
+
context.reserveTokens?.(0);
|
|
1796
1845
|
disconnect?.();
|
|
1797
1846
|
context.signal.removeEventListener("abort", stop);
|
|
1798
1847
|
}
|
|
@@ -1808,8 +1857,12 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1808
1857
|
}
|
|
1809
1858
|
return lateLimits(spec, {
|
|
1810
1859
|
spent: context.ownSpend?.(sessionId, usage.costUsd) ?? usage.costUsd,
|
|
1811
|
-
turns: usage.turns
|
|
1812
|
-
|
|
1860
|
+
turns: usage.turns,
|
|
1861
|
+
tokens: countedTokens(usage)
|
|
1862
|
+
}, context.budgetLeftNow?.(), quota?.leftUsd, {
|
|
1863
|
+
run: context.tokensLeftNow?.(),
|
|
1864
|
+
quota: quota?.leftTokens
|
|
1865
|
+
});
|
|
1813
1866
|
};
|
|
1814
1867
|
const resumeWith = (outcome, prompt, limits) => run({
|
|
1815
1868
|
...spec,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $n as maskSecrets, $t as countedTokens, An as END_KINDS, Mn as FEEDBACK_KINDS, Nn as FEEDBACK_SEVERITIES, On as outcomeEnd, Pn as FEEDBACK_TARGETS, Pt as parseReport, Zn as MASK, ii as AGENT_DRIVERS, jn as endFor, ln as lowerChild, un as startAt } from "./src-tsuZRu0H.mjs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { existsSync, linkSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
@@ -448,7 +448,8 @@ const classifyProviderError = (text, status) => {
|
|
|
448
448
|
* it can see (a turn cap, an exit code, a provider's error), and the report
|
|
449
449
|
* the session handed in decides the rest. The runner then writes the end in
|
|
450
450
|
* the node's numbers, its detail scrubbed with the feedback redactor. A
|
|
451
|
-
* session the driver could not run ends as `driverFailure` says
|
|
451
|
+
* session the driver could not run ends as `driverFailure` says, and one that
|
|
452
|
+
* reached its token cap as `tokenCapOutcome` says.
|
|
452
453
|
*/
|
|
453
454
|
/** The end a report gives: done, a reported failure, or none handed in. */
|
|
454
455
|
const reportEnd = (report) => {
|
|
@@ -479,6 +480,36 @@ const settleEnd = (outcome, facts, scrub) => {
|
|
|
479
480
|
};
|
|
480
481
|
};
|
|
481
482
|
/**
|
|
483
|
+
* What a session's end is written with: the node's limits and program, and its
|
|
484
|
+
* folder and the home folder taken out of the detail.
|
|
485
|
+
*/
|
|
486
|
+
const scopeOf = (spec, bin, env) => ({
|
|
487
|
+
facts: {
|
|
488
|
+
...spec.maxTurns === void 0 ? {} : { maxTurns: spec.maxTurns },
|
|
489
|
+
...spec.budgetUsd === void 0 ? {} : { budgetUsd: spec.budgetUsd },
|
|
490
|
+
...spec.budgetTokens === void 0 ? {} : { budgetTokens: spec.budgetTokens },
|
|
491
|
+
program: path.basename(bin)
|
|
492
|
+
},
|
|
493
|
+
scrub: {
|
|
494
|
+
roots: [spec.cwd],
|
|
495
|
+
home: env["HOME"] ?? homedir()
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
/**
|
|
499
|
+
* The outcome of a session stopped once its counted tokens (RFC-015) reach
|
|
500
|
+
* `budgetTokens`: `budget_limit`, with what it used so far; `undefined` under
|
|
501
|
+
* the cap, or with none.
|
|
502
|
+
*/
|
|
503
|
+
const tokenCapOutcome = ({ budgetTokens }, usage, sessionId) => budgetTokens === void 0 || usage === void 0 || countedTokens(usage) < budgetTokens ? void 0 : {
|
|
504
|
+
subtype: "error_max_budget_tokens",
|
|
505
|
+
end: endFor("budget_limit"),
|
|
506
|
+
sessionId,
|
|
507
|
+
report: null,
|
|
508
|
+
text: "",
|
|
509
|
+
usage,
|
|
510
|
+
errors: [`Reached the token budget (${String(budgetTokens)})`]
|
|
511
|
+
};
|
|
512
|
+
/**
|
|
482
513
|
* The kind of a failure to run the driver: its program missing, else what a
|
|
483
514
|
* provider's words in it say, else a crash.
|
|
484
515
|
*/
|
|
@@ -534,6 +565,15 @@ const settled = (outcome, input) => settleEnd(input.reported === void 0 ? outcom
|
|
|
534
565
|
...outcome,
|
|
535
566
|
report: input.reported()
|
|
536
567
|
}, input.scope.facts, input.scope.scrub);
|
|
568
|
+
/** The session's end at its token cap, its earlier legs' usage included. */
|
|
569
|
+
const tokenCapEnd = (input, used, sessionId) => {
|
|
570
|
+
const usage = used === void 0 || input.carried === void 0 ? used : input.adapter.usage.combine(input.carried, used);
|
|
571
|
+
const capped = tokenCapOutcome(input.scope.facts, usage, sessionId);
|
|
572
|
+
return capped === void 0 ? void 0 : {
|
|
573
|
+
type: "session.finished",
|
|
574
|
+
outcome: settled(capped, input)
|
|
575
|
+
};
|
|
576
|
+
};
|
|
537
577
|
/** The kinds of a session that ended well, so a queued message continues it. */
|
|
538
578
|
const wentWell = (kind) => kind === "done" || kind === "reported_failure" || kind === "no_report";
|
|
539
579
|
/**
|
|
@@ -639,16 +679,26 @@ const runLeg = async function* (input) {
|
|
|
639
679
|
for (const line of input.pending.splice(0)) write(line);
|
|
640
680
|
if (input.adapter.command.input === void 0) child.stdin.end();
|
|
641
681
|
let outcome;
|
|
682
|
+
let sessionId = "";
|
|
642
683
|
const { decoder } = input;
|
|
643
684
|
const decode = decoder?.decode ?? input.adapter.decode;
|
|
644
685
|
try {
|
|
645
686
|
for await (const line of createInterface({ input: child.stdout })) {
|
|
646
|
-
const
|
|
687
|
+
const decoded = decode(line);
|
|
688
|
+
const { events, ended } = legStep(decoded, input);
|
|
689
|
+
for (const event of decoded.events) if (event.type === "session.started") sessionId = event.sessionId;
|
|
647
690
|
yield* events;
|
|
648
691
|
if (ended !== void 0) {
|
|
649
692
|
child.stdin.end();
|
|
650
693
|
outcome = ended;
|
|
651
694
|
}
|
|
695
|
+
const capped = outcome === void 0 ? tokenCapEnd(input, decoded.usage, sessionId) : void 0;
|
|
696
|
+
if (capped !== void 0) {
|
|
697
|
+
stopped = true;
|
|
698
|
+
child.kill("SIGINT");
|
|
699
|
+
yield capped;
|
|
700
|
+
return { kind: "finished" };
|
|
701
|
+
}
|
|
652
702
|
}
|
|
653
703
|
const end = await Promise.race([exited, spawnError.then((error) => error)]);
|
|
654
704
|
if (outcome !== void 0) return outcome;
|
|
@@ -1034,33 +1084,6 @@ const orkTools = (context) => ORK_TOOLS.flatMap((tool) => tool.bind(context) ??
|
|
|
1034
1084
|
/** Their Claude Code names, for the session's allowed tools. */
|
|
1035
1085
|
const preapprovedTools = (tools) => tools.filter((t) => t.preapproved).map((t) => toolId(t.name));
|
|
1036
1086
|
// ../runtime/src/session-runner.ts
|
|
1037
|
-
/**
|
|
1038
|
-
* @file The session runner: one agent CLI session, driven through an adapter's
|
|
1039
|
-
* strategies (`adapter.ts`), written once for every driver. It serves Ork's
|
|
1040
|
-
* tools on a local MCP endpoint, wires the session to it, and runs the tool
|
|
1041
|
-
* as one or more processes (legs). A paused node's tool call is held, and
|
|
1042
|
-
* past the hold window deferred; the next leg resumes the session once the
|
|
1043
|
-
* node continues (ADR-041). A tool that takes no message mid-session gets one
|
|
1044
|
-
* at the next leg, which resumes the session with it. A driver that cannot
|
|
1045
|
-
* hold the final answer to the report schema gets Ork's `report` tool, and
|
|
1046
|
-
* the session ends with the report it accepted. Interrupting stops the
|
|
1047
|
-
* running process.
|
|
1048
|
-
*/
|
|
1049
|
-
/**
|
|
1050
|
-
* What a session's end is written with: the node's limits and program, and its
|
|
1051
|
-
* folder and the home folder taken out of the detail.
|
|
1052
|
-
*/
|
|
1053
|
-
const scopeOf = (spec, bin, env) => ({
|
|
1054
|
-
facts: {
|
|
1055
|
-
...spec.maxTurns === void 0 ? {} : { maxTurns: spec.maxTurns },
|
|
1056
|
-
...spec.budgetUsd === void 0 ? {} : { budgetUsd: spec.budgetUsd },
|
|
1057
|
-
program: path.basename(bin)
|
|
1058
|
-
},
|
|
1059
|
-
scrub: {
|
|
1060
|
-
roots: [spec.cwd],
|
|
1061
|
-
home: env["HOME"] ?? homedir()
|
|
1062
|
-
}
|
|
1063
|
-
});
|
|
1064
1087
|
/** A spec that resumes `sessionId`; a fork's later legs continue the fork. */
|
|
1065
1088
|
const resumed = (spec, sessionId) => ({
|
|
1066
1089
|
...spec,
|
|
@@ -1391,9 +1414,23 @@ const REPORT_TOOL = "StructuredOutput";
|
|
|
1391
1414
|
const claudeDecoder = () => {
|
|
1392
1415
|
const calls = /* @__PURE__ */ new Map();
|
|
1393
1416
|
let accepted;
|
|
1417
|
+
/** Each model response's usage, by message id: its blocks repeat it. */
|
|
1418
|
+
const responses = /* @__PURE__ */ new Map();
|
|
1419
|
+
/** The leg's usage so far, when `message` is a model response. */
|
|
1420
|
+
const usageSoFar = (message) => {
|
|
1421
|
+
const inner = get$1(message, "message");
|
|
1422
|
+
const id = get$1(inner, "id");
|
|
1423
|
+
if (get$1(message, "type") !== "assistant" || typeof id !== "string") return;
|
|
1424
|
+
responses.set(id, {
|
|
1425
|
+
...usageOf({ usage: get$1(inner, "usage") }),
|
|
1426
|
+
turns: 1
|
|
1427
|
+
});
|
|
1428
|
+
return [...responses.values()].reduce((a, b) => addUsage(a, b));
|
|
1429
|
+
};
|
|
1394
1430
|
return {
|
|
1395
1431
|
decode: (line) => {
|
|
1396
1432
|
const message = parseLine$2(line);
|
|
1433
|
+
const usage = usageSoFar(message);
|
|
1397
1434
|
for (const block of blocks(message)) {
|
|
1398
1435
|
if (block["type"] === "tool_use" && block["name"] === REPORT_TOOL) calls.set(str$2(block["id"]), block["input"]);
|
|
1399
1436
|
const id = str$2(block["tool_use_id"]);
|
|
@@ -1408,7 +1445,8 @@ const claudeDecoder = () => {
|
|
|
1408
1445
|
} : message;
|
|
1409
1446
|
return {
|
|
1410
1447
|
events: normalize(kept),
|
|
1411
|
-
deferred: isDeferredResult(message)
|
|
1448
|
+
deferred: isDeferredResult(message),
|
|
1449
|
+
...usage === void 0 ? {} : { usage }
|
|
1412
1450
|
};
|
|
1413
1451
|
},
|
|
1414
1452
|
exit: () => []
|
|
@@ -1941,6 +1979,26 @@ const commandCodeDecoder = () => {
|
|
|
1941
1979
|
let sessionId = "";
|
|
1942
1980
|
let turns = 0;
|
|
1943
1981
|
let held;
|
|
1982
|
+
/** The leg's tokens so far, from each `model_request_end` frame. */
|
|
1983
|
+
let used;
|
|
1984
|
+
/** A model request's usage, added to the leg's so far. */
|
|
1985
|
+
const requestEnded = (usage) => {
|
|
1986
|
+
const said = isObject$1(usage) ? usage : {};
|
|
1987
|
+
const own = {
|
|
1988
|
+
costUsd: 0,
|
|
1989
|
+
inputTokens: num$1(said["inputTokens"]),
|
|
1990
|
+
outputTokens: num$1(said["outputTokens"]),
|
|
1991
|
+
cacheReadTokens: num$1(said["cacheReadTokens"]),
|
|
1992
|
+
cacheWriteTokens: num$1(said["cacheWriteTokens"]),
|
|
1993
|
+
turns,
|
|
1994
|
+
durationMs: 0
|
|
1995
|
+
};
|
|
1996
|
+
used = used === void 0 ? own : {
|
|
1997
|
+
...addUsage(used, own),
|
|
1998
|
+
turns
|
|
1999
|
+
};
|
|
2000
|
+
return used;
|
|
2001
|
+
};
|
|
1944
2002
|
const frame = (event) => {
|
|
1945
2003
|
const type = event["type"];
|
|
1946
2004
|
const id = str$1(event["toolCallId"]);
|
|
@@ -2017,7 +2075,11 @@ const commandCodeDecoder = () => {
|
|
|
2017
2075
|
return { events: [] };
|
|
2018
2076
|
}
|
|
2019
2077
|
const event = message["event"];
|
|
2020
|
-
|
|
2078
|
+
if (message["type"] !== "event" || !isObject$1(event)) return { events: [] };
|
|
2079
|
+
return {
|
|
2080
|
+
events: frame(event),
|
|
2081
|
+
...event["type"] === "model_request_end" ? { usage: requestEnded(event["usage"]) } : {}
|
|
2082
|
+
};
|
|
2021
2083
|
},
|
|
2022
2084
|
exit: (code) => {
|
|
2023
2085
|
if (held !== void 0) return [result(held, code)];
|
|
@@ -2584,7 +2646,10 @@ const openCodeDecoder = (spec) => {
|
|
|
2584
2646
|
switch (message["type"]) {
|
|
2585
2647
|
case "step_finish":
|
|
2586
2648
|
usage = sumUsage(usage, stepUsage(message["part"]));
|
|
2587
|
-
|
|
2649
|
+
return {
|
|
2650
|
+
events,
|
|
2651
|
+
usage
|
|
2652
|
+
};
|
|
2588
2653
|
case "error":
|
|
2589
2654
|
errors.push(errorText(message["error"]));
|
|
2590
2655
|
failed ??= errorLineEnd(message["error"]);
|