@krapht/ork 1.0.0-alpha.13 → 1.0.0-alpha.15
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__-Bsb1kjgF.mjs +340 -0
- package/dist/{store-CM3cERGl.mjs → attach-BHO3X3cf.mjs} +36 -91
- package/dist/{attach-E6NNyA3T.mjs → attach-CPespxlQ.mjs} +6 -5
- package/dist/bin.mjs +148 -1444
- package/dist/brief-za0ozN_E.mjs +58 -0
- package/dist/{builtin-files-n6o6oWpB.mjs → builtin-files-2bHf76x-.mjs} +1 -1
- package/dist/{chips-CyeB_rhR.mjs → chips-BqcT58qr.mjs} +1 -1
- package/dist/{completion-SNNNxnv9.mjs → completion-CQ2UYKuJ.mjs} +52 -11
- package/dist/context-BNqDw9z1.mjs +106 -0
- package/dist/{find-CjsNYAAf.mjs → find-BYLlszqj.mjs} +1 -1
- package/dist/follow-ups-B4GJ3Vzw.mjs +461 -0
- package/dist/{format-C60TFpRe.mjs → format-C8MsFj8_.mjs} +30 -16
- package/dist/{help-groups-CyfPWlST.mjs → help-groups-CQ98mLjF.mjs} +20 -6
- package/dist/{in-a-run-BYjTXaST.mjs → in-a-run-Bp0k0J09.mjs} +1 -1
- package/dist/{__build_info__-Da73_nBs.mjs → install-DWtXTq4y.mjs} +3 -227
- package/dist/{limits-C5QKZNLT.mjs → limits-B1qzS4cX.mjs} +1 -1
- package/dist/{mcp-bcctR-wc.mjs → mcp-BOrVkyhN.mjs} +6 -6
- package/dist/{once-DPQu5Q3H.mjs → once-C1fL3GVo.mjs} +4 -4
- package/dist/orphan-guard-BFKg8VSd.mjs +58 -0
- package/dist/{output-CnRzrbUM.mjs → output-B1kwo0YU.mjs} +17 -1
- package/dist/{pause-BPvlDIvz.mjs → pause-DOMK-R8V.mjs} +4 -4
- package/dist/post-BzA6Qobq.mjs +44 -0
- package/dist/{relay-C8W0t_zc.mjs → relay-OWuu4fib.mjs} +2 -2
- package/dist/run--4-1xAF2.mjs +1451 -0
- package/dist/{run-CQIfq918.mjs → run-CiPsW9Kf.mjs} +1 -1
- package/dist/send-CnNNW-ot.mjs +105 -0
- package/dist/{src-CqRqsb2t.mjs → src-B9zWIVqo.mjs} +226 -44
- package/dist/{src-CGJGL1sZ.mjs → src-DQKz2eOH.mjs} +326 -81
- package/dist/{src-D93tHZ5B.mjs → src-DcrhgipG.mjs} +107 -47
- package/dist/{src-BeR6f70A.mjs → src-Dr_fTSu-.mjs} +5858 -3746
- package/dist/store-oKJ-cCf3.mjs +16 -0
- package/dist/ui/assets/app-Bdc6OBeZ.css +1 -0
- package/dist/ui/assets/app-Drp8Dm9y.js +111 -0
- package/dist/ui/assets/{index-CWwm8HTG.js → index-BtwkftKe.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/dist/{ui-D2Ydofq0.mjs → ui-CzKlsCnm.mjs} +4 -4
- package/dist/{usage-6WIowblz.mjs → usage-DHGNk2JL.mjs} +13 -141
- package/dist/{context-DHudLA5e.mjs → validate-Bs_gg22g.mjs} +17 -107
- package/dist/{verdict-CIy8tAyX.mjs → verdict-CMvPCpWC.mjs} +2 -2
- package/dist/{watch-uOU_WeBI.mjs → watch-CCCfw3n7.mjs} +24 -8
- package/dist/{workflows-7tQdMnrA.mjs → workflows-CW28ZU-z.mjs} +3 -3
- package/dist/{worktrees-ZCII298F.mjs → worktrees-CyE8aF6j.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-DYRC0V4p.js +0 -111
- package/dist/ui/assets/app-QoHVi-mb.css +0 -1
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { $t as
|
|
2
|
-
import { t as runOnce } from "./run-
|
|
1
|
+
import { $t as sessionDriver, Bn as outcomeEnd, Bt as stoppedEnd, Dt as reportSchemaFor, Ft as runScope, Gr as beforeOf, Ht as slotClock, It as calledWorkflows, Kr as programsRunBy, Kt as startAt, Nn as countedTokens, Pt as heldTimeout, Rt as endedWith, Tt as planNodeSession, Un as endFor, Vn as END_DETAIL_LIMIT, Vt as declaredProblem, Wt as lowerChild, Xr as markValue, Xt as envReadsOf, _i as AGENT_DRIVERS, ar as renderTemplate, ci as timeoutMs, di as integrationFor, ii as isToolNode, in as serversIn, ir as parseTemplate, li as INTEGRATIONS, nn as MCP_FILE, nt as sessionRefusals, ri as isClaudeNode, rn as mcpNotText, rr as evaluate, tn as declaredOutputOf, ui as TOOL_PREFIX, xt as planReason, zn as endOf, zt as failedWith } from "./src-Dr_fTSu-.mjs";
|
|
2
|
+
import { t as runOnce } from "./run-CiPsW9Kf.mjs";
|
|
3
|
+
import { n as holdGuard, r as orphanGuard, t as KILL_AFTER_MS } from "./orphan-guard-BFKg8VSd.mjs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { accessSync, constants, lstatSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
5
6
|
import { spawn } from "node:child_process";
|
|
7
|
+
import { Readable } from "node:stream";
|
|
6
8
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
7
9
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
8
10
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
9
|
-
import { Readable } from "node:stream";
|
|
10
11
|
// ../nodes/src/call.ts
|
|
11
12
|
/**
|
|
12
13
|
* @file The `workflow.call` node type (RFC-004): renders what the node asks for
|
|
@@ -538,43 +539,36 @@ const afterLateTurn = (first, later) => {
|
|
|
538
539
|
errors: first.errors
|
|
539
540
|
};
|
|
540
541
|
};
|
|
542
|
+
/** The least of the limits that are set; `undefined` with none. */
|
|
543
|
+
const least = (...limits) => {
|
|
544
|
+
const known = limits.filter((limit) => limit !== void 0);
|
|
545
|
+
return known.length === 0 ? void 0 : Math.min(...known);
|
|
546
|
+
};
|
|
547
|
+
/** A cap less what this activation used, beside what a quota says is left. */
|
|
548
|
+
const leftOf = (caps, used, quotaLeft) => least(...caps.map((cap) => cap === void 0 ? void 0 : cap - used), quotaLeft);
|
|
541
549
|
/**
|
|
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
|
-
*
|
|
550
|
+
* What another turn of a session may use. `spent`, `tokens` (counted, RFC-015)
|
|
551
|
+
* and `turns` are this activation's, which neither the node's cap nor the run's
|
|
552
|
+
* budget has counted. The quota has: the ledger took it as the session
|
|
553
|
+
* finished, so what it says is left is left. The run's tokens left have too:
|
|
554
|
+
* only the node's own `budgetTokens` takes `tokens` off. `null` when nothing
|
|
555
|
+
* is, and the turn does not start.
|
|
546
556
|
*/
|
|
547
|
-
const lateLimits = (spec,
|
|
548
|
-
const
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
557
|
+
const lateLimits = (spec, used, runLeft, quotaLeft, tokensLeft = {}) => {
|
|
558
|
+
const budgetUsd = leftOf([spec.budgetUsd, runLeft], used.spent, quotaLeft);
|
|
559
|
+
const budgetTokens = leftOf([spec.budgetTokens], used.tokens ?? 0, least(tokensLeft.run, tokensLeft.quota));
|
|
560
|
+
const maxTurns = spec.maxTurns === void 0 ? void 0 : spec.maxTurns - used.turns;
|
|
561
|
+
if ([
|
|
562
|
+
budgetUsd,
|
|
563
|
+
budgetTokens,
|
|
564
|
+
maxTurns
|
|
565
|
+
].some((left) => (left ?? 1) <= 0)) return null;
|
|
553
566
|
return {
|
|
554
567
|
...budgetUsd === void 0 ? {} : { budgetUsd },
|
|
568
|
+
...budgetTokens === void 0 ? {} : { budgetTokens },
|
|
555
569
|
...maxTurns === void 0 ? {} : { maxTurns }
|
|
556
570
|
};
|
|
557
571
|
};
|
|
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
572
|
/**
|
|
579
573
|
* Whether a limit across runs, rather than the node's work, ended a node: a
|
|
580
574
|
* session whose cap was a quota's and that ran into it, or one its plan refused
|
|
@@ -587,7 +581,7 @@ const limitWatch = (context) => {
|
|
|
587
581
|
why = reason;
|
|
588
582
|
},
|
|
589
583
|
session: (asked) => {
|
|
590
|
-
const left = context.quota?.()
|
|
584
|
+
const { leftUsd: left, leftTokens } = context.quota?.() ?? {};
|
|
591
585
|
let refused;
|
|
592
586
|
return {
|
|
593
587
|
saw: (event) => {
|
|
@@ -595,7 +589,7 @@ const limitWatch = (context) => {
|
|
|
595
589
|
},
|
|
596
590
|
ended: (outcome) => {
|
|
597
591
|
if (refused !== void 0 && (outcome?.report ?? null) === null) why = refused;
|
|
598
|
-
const capped = left !== void 0 && asked.budgetUsd !== void 0 && asked.budgetUsd >= left;
|
|
592
|
+
const capped = left !== void 0 && asked.budgetUsd !== void 0 && asked.budgetUsd >= left || leftTokens !== void 0 && asked.budgetTokens !== void 0 && asked.budgetTokens >= leftTokens;
|
|
599
593
|
const spent = outcome !== null && outcomeEnd(outcome).kind === "budget_limit";
|
|
600
594
|
if (capped && spent) why = context.quota?.().blocked ?? "a session reached what was left of a quota";
|
|
601
595
|
}
|
|
@@ -657,6 +651,61 @@ const sessionDriverOf = (context, plan, options) => {
|
|
|
657
651
|
const unfit = plan.pinned === void 0 ? void 0 : refusalOn(context, driver, options.drivers ?? AGENT_DRIVERS);
|
|
658
652
|
return unfit === void 0 ? driver : notStarted("invalid_node", unfit);
|
|
659
653
|
};
|
|
654
|
+
// ../nodes/src/start-budgets.ts
|
|
655
|
+
/** What a session may spend as it starts: the least of the budget and any quota. */
|
|
656
|
+
const budgetNow = (context) => context.budgetLeftNow === void 0 ? context.budgetLeftUsd : least(context.budgetLeftNow(), context.quota?.().leftUsd);
|
|
657
|
+
/** The counted tokens a session may use as it starts: the run's and a quota's. */
|
|
658
|
+
const tokensNow = (context) => least(context.tokensLeftNow?.(), context.quota?.().leftTokens);
|
|
659
|
+
/**
|
|
660
|
+
* What is `left` now, with `own` (the node's cap) set aside through `reserve`
|
|
661
|
+
* in the same step; with nothing left, it waits for a worker's reserve to
|
|
662
|
+
* free.
|
|
663
|
+
*/
|
|
664
|
+
const startWith = async (context, left, own, reserve) => {
|
|
665
|
+
const now = left();
|
|
666
|
+
if (now !== void 0 && now <= 0) {
|
|
667
|
+
context.reserveBudget?.(0);
|
|
668
|
+
context.reserveTokens?.(0);
|
|
669
|
+
return await context.budgetFreed?.(context.signal) === true ? startWith(context, left, own, reserve) : now;
|
|
670
|
+
}
|
|
671
|
+
reserve?.(own === void 0 || now === void 0 ? now : Math.min(own, now));
|
|
672
|
+
return now;
|
|
673
|
+
};
|
|
674
|
+
/**
|
|
675
|
+
* What a node's first session may spend, set aside from the run's budget in the
|
|
676
|
+
* same step that reads it (ADR-077), so a node starting beside it is never
|
|
677
|
+
* offered the same part: the least of what is left and the node's own
|
|
678
|
+
* `budgetUsd`. With nothing left, it waits for a worker running beside it to
|
|
679
|
+
* finish, as long as one may.
|
|
680
|
+
*/
|
|
681
|
+
const budgetToStart = (context) => startWith(context, () => budgetNow(context), isClaudeNode(context.node) ? context.node.budgetUsd ?? context.workflow.defaults?.budgetUsd : void 0, context.reserveBudget);
|
|
682
|
+
/** `budgetToStart` in counted tokens: the run's `budgetTokens` and a quota's. */
|
|
683
|
+
const tokensToStart = (context) => startWith(context, () => tokensNow(context), isClaudeNode(context.node) ? context.node.budgetTokens ?? context.workflow.defaults?.budgetTokens : void 0, context.reserveTokens);
|
|
684
|
+
/**
|
|
685
|
+
* What a node's first session may spend, in dollars and in counted tokens, set
|
|
686
|
+
* into `planOptions`; the node's result instead when nothing is left of
|
|
687
|
+
* either.
|
|
688
|
+
*/
|
|
689
|
+
const startBudgets = async (context, planOptions) => {
|
|
690
|
+
const usd = await budgetToStart(context);
|
|
691
|
+
if (usd !== void 0 && usd <= 0) return notStarted("budget_limit", "nothing is left of the run's budget");
|
|
692
|
+
if (usd !== void 0) planOptions.budgetLeftUsd = usd;
|
|
693
|
+
const waited = { done: false };
|
|
694
|
+
const tokens = await tokensToStart({
|
|
695
|
+
...context,
|
|
696
|
+
budgetFreed: async (signal) => {
|
|
697
|
+
waited.done = true;
|
|
698
|
+
return await context.budgetFreed?.(signal) ?? false;
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
if (tokens !== void 0 && tokens <= 0) return notStarted("budget_limit", "nothing is left of the run's tokens");
|
|
702
|
+
if (waited.done) {
|
|
703
|
+
context.reserveTokens?.(0);
|
|
704
|
+
return startBudgets(context, planOptions);
|
|
705
|
+
}
|
|
706
|
+
if (tokens !== void 0) planOptions.budgetLeftTokens = tokens;
|
|
707
|
+
return null;
|
|
708
|
+
};
|
|
660
709
|
// ../nodes/src/tool-output.ts
|
|
661
710
|
/**
|
|
662
711
|
* @file A tool node's structured result (`output: { format: json }`): the file
|
|
@@ -1742,9 +1791,8 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1742
1791
|
}
|
|
1743
1792
|
const blocked = context.quota?.().blocked;
|
|
1744
1793
|
if (blocked !== void 0) return notStarted("plan_limit", blocked, blocked);
|
|
1745
|
-
const
|
|
1746
|
-
if (
|
|
1747
|
-
if (budget !== void 0) planOptions.budgetLeftUsd = budget;
|
|
1794
|
+
const refused = await startBudgets(context, planOptions);
|
|
1795
|
+
if (refused !== null) return refused;
|
|
1748
1796
|
const plan = planNodeSession(context.workflow, context.id, planOptions);
|
|
1749
1797
|
if (!plan.ok) return notStarted("invalid_node", plan.error);
|
|
1750
1798
|
const servers = namedMcpEntries(context.projectRoot, plan.mcp);
|
|
@@ -1769,12 +1817,14 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1769
1817
|
if (context.mailbox !== void 0) ports.mailbox = context.mailbox;
|
|
1770
1818
|
if (context.claims !== void 0) ports.claims = context.claims;
|
|
1771
1819
|
if (context.feedback !== void 0) ports.feedback = context.feedback;
|
|
1820
|
+
if (context.followUps !== void 0) ports.followUps = context.followUps;
|
|
1772
1821
|
const whole = context.round > 1 ? wholeInput(context.input) : void 0;
|
|
1773
1822
|
if (whole !== void 0) ports.input = whole;
|
|
1774
1823
|
const watch = limitWatch(context);
|
|
1775
1824
|
const run = async (asked) => {
|
|
1776
1825
|
const watching = watch.session(asked);
|
|
1777
1826
|
context.reserveBudget?.(asked.budgetUsd);
|
|
1827
|
+
context.reserveTokens?.(asked.budgetTokens);
|
|
1778
1828
|
const session = runtime.start(asked, ports);
|
|
1779
1829
|
const stop = () => {
|
|
1780
1830
|
session.interrupt();
|
|
@@ -1793,6 +1843,7 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1793
1843
|
}
|
|
1794
1844
|
} finally {
|
|
1795
1845
|
context.reserveBudget?.(0);
|
|
1846
|
+
context.reserveTokens?.(0);
|
|
1796
1847
|
disconnect?.();
|
|
1797
1848
|
context.signal.removeEventListener("abort", stop);
|
|
1798
1849
|
}
|
|
@@ -1808,8 +1859,12 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1808
1859
|
}
|
|
1809
1860
|
return lateLimits(spec, {
|
|
1810
1861
|
spent: context.ownSpend?.(sessionId, usage.costUsd) ?? usage.costUsd,
|
|
1811
|
-
turns: usage.turns
|
|
1812
|
-
|
|
1862
|
+
turns: usage.turns,
|
|
1863
|
+
tokens: countedTokens(usage)
|
|
1864
|
+
}, context.budgetLeftNow?.(), quota?.leftUsd, {
|
|
1865
|
+
run: context.tokensLeftNow?.(),
|
|
1866
|
+
quota: quota?.leftTokens
|
|
1867
|
+
});
|
|
1813
1868
|
};
|
|
1814
1869
|
const resumeWith = (outcome, prompt, limits) => run({
|
|
1815
1870
|
...spec,
|
|
@@ -2090,20 +2145,23 @@ const mcpCallExecutor = (options = {}) => async (context) => {
|
|
|
2090
2145
|
* whatever it started: SIGTERM, then SIGKILL for what ignores that. A stop or
|
|
2091
2146
|
* a timeout therefore leaves nothing running, and an escaped process that
|
|
2092
2147
|
* keeps the output pipes open cannot hold the node. Off Windows it runs under
|
|
2093
|
-
* `sh`, whose `times` says the CPU the program and its children used,
|
|
2094
|
-
* the
|
|
2148
|
+
* `sh`, whose `times` says the CPU the program and its children used, beside
|
|
2149
|
+
* the orphan guard, which ends the group when `ork` dies, and at the
|
|
2150
|
+
* project's priority: `low` starts it below Ork's own (nice, and on macOS
|
|
2095
2151
|
* utility QoS).
|
|
2096
2152
|
*/
|
|
2097
|
-
/** How long a program may take to end after SIGTERM. */
|
|
2098
|
-
const KILL_AFTER_MS = 2e3;
|
|
2099
2153
|
/** How long to wait for the pipes to close after SIGKILL. */
|
|
2100
2154
|
const CLOSE_AFTER_MS = 500;
|
|
2155
|
+
/** The fd of the orphan guard's pipe from `ork`. */
|
|
2156
|
+
const WATCH_FD = 4;
|
|
2101
2157
|
/**
|
|
2102
|
-
* Runs `$0 "$@"
|
|
2103
|
-
* and
|
|
2158
|
+
* Runs `$0 "$@"` beside the orphan guard, which ends the group if `ork` dies,
|
|
2159
|
+
* and stops the guard once the program ends, so its leftovers live on as
|
|
2160
|
+
* before. Then writes the shell's `times` to fd 3: its children's user and
|
|
2161
|
+
* system time, the program's whole tree that it waited for. A program that
|
|
2104
2162
|
* isn't there says so on fd 3, as spawn's ENOENT would.
|
|
2105
2163
|
*/
|
|
2106
|
-
const TIMED = String.raw`command -v "$0" >/dev/null 2>&1 || { echo ork-missing >&3; exit 127; }; "$0" "$@" 3
|
|
2164
|
+
const TIMED = String.raw`command -v "$0" >/dev/null 2>&1 || { echo ork-missing >&3; exit 127; }; ${orphanGuard(WATCH_FD)}w=$!; "$0" "$@" 3>&- ${String(WATCH_FD)}<&-; s=$?; kill -s KILL $w 2>/dev/null; times >&3; exit $s`;
|
|
2107
2165
|
const MISSING = "ork-missing";
|
|
2108
2166
|
/** The children's line of `times` (`0m1.250s 0m0.300s`), in milliseconds. */
|
|
2109
2167
|
const cpuOfTimes = (text) => {
|
|
@@ -2144,10 +2202,12 @@ const spawnTool = (program, args, { cwd, signal, env, priority }) => {
|
|
|
2144
2202
|
"ignore",
|
|
2145
2203
|
"pipe",
|
|
2146
2204
|
"pipe",
|
|
2205
|
+
"pipe",
|
|
2147
2206
|
"pipe"
|
|
2148
2207
|
]
|
|
2149
2208
|
});
|
|
2150
2209
|
lowerChild(priority, child.pid);
|
|
2210
|
+
holdGuard(child.stdio[WATCH_FD]);
|
|
2151
2211
|
const fd3 = child.stdio[3];
|
|
2152
2212
|
if (fd3 instanceof Readable) fd3.setEncoding("utf8").on("data", (chunk) => {
|
|
2153
2213
|
times.push(chunk);
|