@krapht/ork 1.0.0-alpha.15 → 1.0.0-alpha.17
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/README.md +10 -4
- package/dist/{__build_info__-Bsb1kjgF.mjs → __build_info__-C1Nm0hlJ.mjs} +25 -6
- package/dist/apply-Mb0fNpCX.mjs +17 -0
- package/dist/{attach-CPespxlQ.mjs → attach-EP374oMP.mjs} +5 -5
- package/dist/{attach-BHO3X3cf.mjs → attach-RQx__RzH.mjs} +46 -8
- package/dist/bin.mjs +238 -369
- package/dist/brief-Bs3cMQU0.mjs +100 -0
- package/dist/{builtin-files-2bHf76x-.mjs → builtin-files-yYaNTQRU.mjs} +1 -1
- package/dist/command-tree-BkSYWmYJ.mjs +63 -0
- package/dist/{completion-CQ2UYKuJ.mjs → completion-DIXWlnAl.mjs} +87 -6
- package/dist/{context-BNqDw9z1.mjs → context-Dq-VM_sq.mjs} +3 -3
- package/dist/{find-BYLlszqj.mjs → find-k489laBa.mjs} +2 -2
- package/dist/flags-B4ZZWspN.mjs +125 -0
- package/dist/{follow-ups-B4GJ3Vzw.mjs → follow-ups-DD8NTPe6.mjs} +59 -24
- package/dist/{format-C8MsFj8_.mjs → format-DQc9A_ei.mjs} +17 -4
- package/dist/{help-groups-CQ98mLjF.mjs → help-groups-hB6kY34d.mjs} +1 -63
- package/dist/in-a-run-D_9GlAlS.mjs +27 -0
- package/dist/{install-DWtXTq4y.mjs → install-PVlV2QPq.mjs} +1 -1
- package/dist/{limits-B1qzS4cX.mjs → limits-CHqBO7wb.mjs} +1 -1
- package/dist/{mcp-BOrVkyhN.mjs → mcp-D38Ajb0b.mjs} +6 -6
- package/dist/{once-C1fL3GVo.mjs → once-DuUzpzL2.mjs} +3 -3
- package/dist/parse-BzWpMN-a.mjs +107 -0
- package/dist/{pause-DOMK-R8V.mjs → pause-Bx3ePGrn.mjs} +3 -3
- package/dist/{post-BzA6Qobq.mjs → post-2CaJz_mV.mjs} +5 -3
- package/dist/print-n2qetUHc.mjs +19 -0
- package/dist/project-records-Cune1l6I.mjs +230 -0
- package/dist/{relay-OWuu4fib.mjs → relay-D7RN4m68.mjs} +1 -1
- package/dist/{run-CiPsW9Kf.mjs → run-BfxdsQJk.mjs} +1 -1
- package/dist/{run--4-1xAF2.mjs → run-OOWQIKMJ.mjs} +33 -20
- package/dist/{send-CnNNW-ot.mjs → send-CKth_vgn.mjs} +2 -2
- package/dist/{src-B9zWIVqo.mjs → src-BHod38UQ.mjs} +104 -72
- package/dist/{src-Dr_fTSu-.mjs → src-DLsFsmOD.mjs} +790 -502
- package/dist/{src-DQKz2eOH.mjs → src-DOjTY2ET.mjs} +238 -79
- package/dist/{src-DcrhgipG.mjs → src-DmmrX2_C.mjs} +162 -58
- package/dist/{store-oKJ-cCf3.mjs → store-DACH768X.mjs} +1 -1
- package/dist/ui/assets/app-9Q-62Ywz.css +1 -0
- package/dist/ui/assets/app-CN3acQ-1.js +111 -0
- package/dist/ui/assets/{index-BtwkftKe.js → index-B2uGxA4W.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/dist/{ui-CzKlsCnm.mjs → ui-CJ-BX12n.mjs} +3 -3
- package/dist/{usage-DHGNk2JL.mjs → usage-DxZ6JGd4.mjs} +62 -107
- package/dist/{validate-Bs_gg22g.mjs → validate-BS0nCccW.mjs} +35 -8
- package/dist/{watch-CCCfw3n7.mjs → watch-Dp7IB4FR.mjs} +5 -5
- package/dist/{workflows-CW28ZU-z.mjs → workflows-Djo1edlK.mjs} +2 -2
- package/dist/{worktrees-CyE8aF6j.mjs → worktrees-Drof_Nyx.mjs} +49 -20
- package/package.json +8 -4
- package/dist/brief-za0ozN_E.mjs +0 -58
- package/dist/in-a-run-Bp0k0J09.mjs +0 -24
- package/dist/ui/assets/app-Bdc6OBeZ.css +0 -1
- package/dist/ui/assets/app-Drp8Dm9y.js +0 -111
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { $
|
|
2
|
-
import { t as runOnce } from "./run-
|
|
1
|
+
import { $r as beforeOf, At as endedWith, Bn as countedTokens, Dt as runScope, Et as heldTimeout, It as lowerChild, Jn as END_DETAIL_LIMIT, Kn as endOf, Mt as stoppedEnd, Nt as declaredProblem, Ot as calledWorkflows, Pt as slotClock, R as storePath, Rt as startAt, Ti as AGENT_DRIVERS, Xn as endFor, Y as sessionRefusals, _i as INTEGRATIONS, _t as planNodeSession, an as sessionDriver, cn as MCP_FILE, di as isClaudeNode, dr as parseTemplate, ei as programsRunBy, fi as isToolNode, fr as renderTemplate, gi as timeoutMs, ii as markValue, jt as failedWith, ln as mcpNotText, mt as planReason, nn as envReadsOf, qn as outcomeEnd, sn as declaredOutputOf, un as serversIn, ur as evaluate, vi as TOOL_PREFIX, yi as integrationFor, yt as reportSchemaFor } from "./src-DLsFsmOD.mjs";
|
|
2
|
+
import { t as runOnce } from "./run-BfxdsQJk.mjs";
|
|
3
3
|
import { n as holdGuard, r as orphanGuard, t as KILL_AFTER_MS } from "./orphan-guard-BFKg8VSd.mjs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { accessSync, constants, lstatSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { accessSync, constants, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
7
7
|
import { Readable } from "node:stream";
|
|
8
8
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
@@ -60,7 +60,9 @@ const callExecutor = (context) => {
|
|
|
60
60
|
// ../nodes/src/claude-plan.ts
|
|
61
61
|
/**
|
|
62
62
|
* @file What a Claude node's session is planned with, read from the node's
|
|
63
|
-
* context
|
|
63
|
+
* context, and what it is told beyond its plan: the note of a sent-back input
|
|
64
|
+
* on re-entry, a resumed leader's message, and the turn for messages that
|
|
65
|
+
* came in as it finished.
|
|
64
66
|
*/
|
|
65
67
|
/** The plan options every session of a node starts from. */
|
|
66
68
|
const planOptionsOf = (context, inPlace) => {
|
|
@@ -83,6 +85,30 @@ const planOptionsOf = (context, inPlace) => {
|
|
|
83
85
|
if (context.claims !== void 0) planOptions.claims = context.claims;
|
|
84
86
|
return planOptions;
|
|
85
87
|
};
|
|
88
|
+
const MAX_INPUT = 8192;
|
|
89
|
+
const inputJson = (input) => JSON.stringify(input ?? {}, void 0, 2);
|
|
90
|
+
/** What a sent-back input is whole, for `read_input`, when its note cuts it. */
|
|
91
|
+
const wholeInput = (input) => {
|
|
92
|
+
const json = inputJson(input);
|
|
93
|
+
return json.length > MAX_INPUT ? json : void 0;
|
|
94
|
+
};
|
|
95
|
+
/** A re-entry's note of what was sent back; `read_input` has a cut rest. */
|
|
96
|
+
const reentryNote = (round, input) => {
|
|
97
|
+
const json = inputJson(input);
|
|
98
|
+
const cut = json.length > MAX_INPUT;
|
|
99
|
+
const body = cut ? `${json.slice(0, MAX_INPUT)}\n…` : json;
|
|
100
|
+
const rest = cut ? `\n\nThat is the first ${String(MAX_INPUT)} of its ${String(json.length)} characters. Call read_input for all of it before you act on it.` : "";
|
|
101
|
+
return `\n\n## Round ${String(round)}\n\nThe workflow sent this task back to you with:\n\n\`\`\`json\n${body}\n\`\`\`${rest}`;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* What a resumed leader is told: it already has its brief and its earlier work,
|
|
105
|
+
* so only the new round, and the new task when its owner sent one.
|
|
106
|
+
*/
|
|
107
|
+
const resumeMessage = (round, input, brief) => [brief === void 0 ? sentBack(round, input) : `## New task from \`${brief.from}\`\n\n${brief.text}`, "End with a new structured report."].join("\n\n");
|
|
108
|
+
/** A first-round resume is a session the run was cut off in (`ork resume`). */
|
|
109
|
+
const sentBack = (round, input) => round === 1 ? "Ork was stopped while you worked on this task, and the run is resumed. Your earlier turns are above: check what you finished, and carry on from there." : `The workflow sent your task back to you.${reentryNote(round, input)}`;
|
|
110
|
+
/** What a session is told when messages came in as it finished. */
|
|
111
|
+
const lateMessage = (count) => `[ork] ${String(count)} message${count === 1 ? "" : "s"} came in while you finished. Call read_inbox, act on ${count === 1 ? "it" : "them"} if needed, and end with a new structured report.`;
|
|
86
112
|
// ../nodes/src/claude-result.ts
|
|
87
113
|
/**
|
|
88
114
|
* @file A finished session as a node's result: its report is the output, held
|
|
@@ -552,12 +578,13 @@ const leftOf = (caps, used, quotaLeft) => least(...caps.map((cap) => cap === voi
|
|
|
552
578
|
* budget has counted. The quota has: the ledger took it as the session
|
|
553
579
|
* finished, so what it says is left is left. The run's tokens left have too:
|
|
554
580
|
* only the node's own `budgetTokens` takes `tokens` off. `null` when nothing
|
|
555
|
-
* is, and the turn does not start.
|
|
581
|
+
* is, and the turn does not start. `turnsAtLeast` lifts the turns left to that
|
|
582
|
+
* many (a failed check's resume: `CHECK_RETRY_TURNS`).
|
|
556
583
|
*/
|
|
557
|
-
const lateLimits = (spec, used, runLeft, quotaLeft, tokensLeft = {}) => {
|
|
584
|
+
const lateLimits = (spec, used, runLeft, quotaLeft, tokensLeft = {}, turnsAtLeast = 0) => {
|
|
558
585
|
const budgetUsd = leftOf([spec.budgetUsd, runLeft], used.spent, quotaLeft);
|
|
559
586
|
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;
|
|
587
|
+
const maxTurns = spec.maxTurns === void 0 ? void 0 : Math.max(spec.maxTurns - used.turns, turnsAtLeast);
|
|
561
588
|
if ([
|
|
562
589
|
budgetUsd,
|
|
563
590
|
budgetTokens,
|
|
@@ -569,18 +596,36 @@ const lateLimits = (spec, used, runLeft, quotaLeft, tokensLeft = {}) => {
|
|
|
569
596
|
...maxTurns === void 0 ? {} : { maxTurns }
|
|
570
597
|
};
|
|
571
598
|
};
|
|
599
|
+
const reached = (cap, used) => cap !== void 0 && used >= cap;
|
|
600
|
+
/**
|
|
601
|
+
* Why the run's budget stopped a session that reached its cap, if it did. A
|
|
602
|
+
* unit blames the run when the session reached it, or when it is the only cap
|
|
603
|
+
* the session had; otherwise the node's own cap in the other unit may have
|
|
604
|
+
* stopped it. A resumed turn's dollars are its own: its cost is the session's
|
|
605
|
+
* total (ADR-046) while its cap is what was left, so `priorCostUsd` comes off,
|
|
606
|
+
* read as `ownSpend` reads it (a lower total counted again from zero).
|
|
607
|
+
*/
|
|
608
|
+
const runSpent = (run, asked, outcome, priorCostUsd) => {
|
|
609
|
+
const total = outcome.usage.costUsd;
|
|
610
|
+
const turnCostUsd = total < priorCostUsd ? total : total - priorCostUsd;
|
|
611
|
+
if (run.tokens !== void 0 && (reached(asked.budgetTokens, countedTokens(outcome.usage)) || asked.budgetUsd === void 0)) return `the run's budget of ${String(run.tokens)} tokens is spent`;
|
|
612
|
+
return run.usd !== void 0 && (reached(asked.budgetUsd, turnCostUsd) || asked.budgetTokens === void 0) ? `the run's budget of $${String(run.usd)} is spent` : void 0;
|
|
613
|
+
};
|
|
572
614
|
/**
|
|
573
615
|
* Whether a limit across runs, rather than the node's work, ended a node: a
|
|
574
616
|
* session whose cap was a quota's and that ran into it, or one its plan refused
|
|
575
|
-
* and that gave no report. A
|
|
617
|
+
* and that gave no report. A session whose cap was the run's budget and that
|
|
618
|
+
* ran into it stops the run as `budget_exhausted` (`run`, from `runCapsOf`). A
|
|
619
|
+
* node that still succeeded is left alone.
|
|
576
620
|
*/
|
|
577
|
-
const limitWatch = (context) => {
|
|
621
|
+
const limitWatch = (context, run = {}) => {
|
|
578
622
|
let why;
|
|
623
|
+
let runWhy;
|
|
579
624
|
return {
|
|
580
625
|
stoppedBy: (reason) => {
|
|
581
626
|
why = reason;
|
|
582
627
|
},
|
|
583
|
-
session: (asked) => {
|
|
628
|
+
session: (asked, priorCostUsd = 0) => {
|
|
584
629
|
const { leftUsd: left, leftTokens } = context.quota?.() ?? {};
|
|
585
630
|
let refused;
|
|
586
631
|
return {
|
|
@@ -592,6 +637,7 @@ const limitWatch = (context) => {
|
|
|
592
637
|
const capped = left !== void 0 && asked.budgetUsd !== void 0 && asked.budgetUsd >= left || leftTokens !== void 0 && asked.budgetTokens !== void 0 && asked.budgetTokens >= leftTokens;
|
|
593
638
|
const spent = outcome !== null && outcomeEnd(outcome).kind === "budget_limit";
|
|
594
639
|
if (capped && spent) why = context.quota?.().blocked ?? "a session reached what was left of a quota";
|
|
640
|
+
else if (spent) runWhy = runSpent(run, asked, outcome, priorCostUsd);
|
|
595
641
|
}
|
|
596
642
|
};
|
|
597
643
|
},
|
|
@@ -601,6 +647,11 @@ const limitWatch = (context) => {
|
|
|
601
647
|
...endedWith(result, endFor("plan_limit", {}, why)),
|
|
602
648
|
limited: why
|
|
603
649
|
};
|
|
650
|
+
if (runWhy !== void 0) return {
|
|
651
|
+
...result,
|
|
652
|
+
limited: runWhy,
|
|
653
|
+
stopsAs: "budget_exhausted"
|
|
654
|
+
};
|
|
604
655
|
const { end } = result;
|
|
605
656
|
return end?.kind === "plan_limit" ? {
|
|
606
657
|
...result,
|
|
@@ -678,9 +729,31 @@ const startWith = async (context, left, own, reserve) => {
|
|
|
678
729
|
* `budgetUsd`. With nothing left, it waits for a worker running beside it to
|
|
679
730
|
* finish, as long as one may.
|
|
680
731
|
*/
|
|
681
|
-
const budgetToStart = (context) => startWith(context, () => budgetNow(context),
|
|
732
|
+
const budgetToStart = (context) => startWith(context, () => budgetNow(context), ownCaps(context).usd, context.reserveBudget);
|
|
733
|
+
/** The node's own caps: its `budgetUsd` and `budgetTokens`, else the defaults'. */
|
|
734
|
+
const ownCaps = (context) => isClaudeNode(context.node) ? {
|
|
735
|
+
usd: context.node.budgetUsd ?? context.workflow.defaults?.budgetUsd,
|
|
736
|
+
tokens: context.node.budgetTokens ?? context.workflow.defaults?.budgetTokens
|
|
737
|
+
} : {};
|
|
738
|
+
/**
|
|
739
|
+
* Where the run's budget, not the node's own cap, caps the node's first
|
|
740
|
+
* session: the run has a budget in that unit, and what `startBudgets` left the
|
|
741
|
+
* session is below the node's own cap, or it has none. A later turn of the
|
|
742
|
+
* session keeps them: both its caps go down by what it used, and the run's by
|
|
743
|
+
* what others used too.
|
|
744
|
+
*/
|
|
745
|
+
const runCapsOf = (context, planOptions) => {
|
|
746
|
+
const own = ownCaps(context);
|
|
747
|
+
const limits = context.workflow.limits;
|
|
748
|
+
const caps = {};
|
|
749
|
+
const usd = planOptions.budgetLeftUsd;
|
|
750
|
+
if (context.budgetLeftNow?.() !== void 0 && limits?.budgetUsd !== void 0 && usd !== void 0 && usd < (own.usd ?? Infinity)) caps.usd = limits.budgetUsd;
|
|
751
|
+
const tokens = planOptions.budgetLeftTokens;
|
|
752
|
+
if (context.tokensLeftNow?.() !== void 0 && limits?.budgetTokens !== void 0 && tokens !== void 0 && tokens < (own.tokens ?? Infinity)) caps.tokens = limits.budgetTokens;
|
|
753
|
+
return caps;
|
|
754
|
+
};
|
|
682
755
|
/** `budgetToStart` in counted tokens: the run's `budgetTokens` and a quota's. */
|
|
683
|
-
const tokensToStart = (context) => startWith(context, () => tokensNow(context),
|
|
756
|
+
const tokensToStart = (context) => startWith(context, () => tokensNow(context), ownCaps(context).tokens, context.reserveTokens);
|
|
684
757
|
/**
|
|
685
758
|
* What a node's first session may spend, in dollars and in counted tokens, set
|
|
686
759
|
* into `planOptions`; the node's result instead when nothing is left of
|
|
@@ -1027,6 +1100,62 @@ const skippedCheck = (outputFile) => {
|
|
|
1027
1100
|
}
|
|
1028
1101
|
}, endFor("done"));
|
|
1029
1102
|
};
|
|
1103
|
+
// ../nodes/src/run-marker.ts
|
|
1104
|
+
/**
|
|
1105
|
+
* @file The marker a run leaves on its sessions and programs (ADR-047), and the
|
|
1106
|
+
* stores it guards: the commands that act as the user refuse in the stores of
|
|
1107
|
+
* the runs that started a process, and everywhere under a marker that lists
|
|
1108
|
+
* none, which an older `ork` sets.
|
|
1109
|
+
*/
|
|
1110
|
+
/** Set for a program in a run. */
|
|
1111
|
+
const RUN_ENV = "ORK_RUN";
|
|
1112
|
+
/** Set for a Claude session. It says no more than that: no run, no node. */
|
|
1113
|
+
const SESSION_ENV = "ORK_SESSION";
|
|
1114
|
+
/**
|
|
1115
|
+
* The stores whose runs started a session or program, as a JSON array of real
|
|
1116
|
+
* paths: the commands that act as the user refuse only in these.
|
|
1117
|
+
*/
|
|
1118
|
+
const GUARDED_STORES_ENV = "ORK_GUARDED_STORES";
|
|
1119
|
+
/**
|
|
1120
|
+
* The variable that says a program in a run or a session started this process,
|
|
1121
|
+
* or `null` when none does.
|
|
1122
|
+
*/
|
|
1123
|
+
const runMarkerIn = (env) => ["ORK_RUN", "ORK_SESSION"].find((name) => env[name] !== void 0) ?? null;
|
|
1124
|
+
/** The stores `env` lists, or `undefined` when it lists none or garbles it. */
|
|
1125
|
+
const listIn = (env) => {
|
|
1126
|
+
const text = env[GUARDED_STORES_ENV];
|
|
1127
|
+
if (text === void 0) return void 0;
|
|
1128
|
+
try {
|
|
1129
|
+
const value = JSON.parse(text);
|
|
1130
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
|
|
1131
|
+
} catch {
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
/** A store file's real path, so a symlinked `~/.ork` is the same store. */
|
|
1136
|
+
const realStore = (store) => {
|
|
1137
|
+
try {
|
|
1138
|
+
return realpathSync(store);
|
|
1139
|
+
} catch {
|
|
1140
|
+
return path.resolve(store);
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
/**
|
|
1144
|
+
* The stores a run that started this process is in: `null` when no run did, and
|
|
1145
|
+
* `"all"` when one did but listed none (an older `ork`), which fails closed.
|
|
1146
|
+
*/
|
|
1147
|
+
const guardedStoresIn = (env) => runMarkerIn(env) === null ? null : listIn(env) ?? "all";
|
|
1148
|
+
/** Whether a run in `store` (a store file) started this process. */
|
|
1149
|
+
const storeGuarded = (env, store) => {
|
|
1150
|
+
const guarded = guardedStoresIn(env);
|
|
1151
|
+
return guarded === "all" || guarded !== null && guarded.includes(realStore(store));
|
|
1152
|
+
};
|
|
1153
|
+
/**
|
|
1154
|
+
* What a run in `store` (default: the user's) tells its sessions and programs:
|
|
1155
|
+
* the stores it inherited, and its own. Nothing when it inherited "all", so
|
|
1156
|
+
* that holds.
|
|
1157
|
+
*/
|
|
1158
|
+
const guardOf = (env, store = storePath()) => guardedStoresIn(env) === "all" ? {} : { [GUARDED_STORES_ENV]: JSON.stringify([.../* @__PURE__ */ new Set([...listIn(env) ?? [], realStore(store)])]) };
|
|
1030
1159
|
// ../nodes/src/tool-scrub.ts
|
|
1031
1160
|
/**
|
|
1032
1161
|
* @file Masking a secret in what a program prints. A plain search, so a value
|
|
@@ -1190,16 +1319,13 @@ const scrubberOf = (values) => {
|
|
|
1190
1319
|
const OUTPUT_ENV = "ORK_OUTPUT";
|
|
1191
1320
|
/** A file a `watch:` node's program keeps between looks (RFC-011). */
|
|
1192
1321
|
const STATE_ENV = "ORK_STATE";
|
|
1193
|
-
/** Set for a program in a run. */
|
|
1194
|
-
const RUN_ENV = "ORK_RUN";
|
|
1195
|
-
/** Set for a Claude session. It says no more than that: no run, no node. */
|
|
1196
|
-
const SESSION_ENV = "ORK_SESSION";
|
|
1197
1322
|
/** The variables a caller's run reads, for a run it calls to mask too. */
|
|
1198
1323
|
const MASKED_ENV = "ORK_MASKED";
|
|
1199
1324
|
/** Every variable Ork sets itself; a workflow's `env:` cannot set them. */
|
|
1200
1325
|
const IDENTITY_KEYS = /* @__PURE__ */ new Set([
|
|
1201
1326
|
RUN_ENV,
|
|
1202
1327
|
SESSION_ENV,
|
|
1328
|
+
GUARDED_STORES_ENV,
|
|
1203
1329
|
MASKED_ENV,
|
|
1204
1330
|
"ORK_NODE",
|
|
1205
1331
|
"ORK_ROUND",
|
|
@@ -1218,20 +1344,21 @@ const IDENTITY_KEYS = /* @__PURE__ */ new Set([
|
|
|
1218
1344
|
*/
|
|
1219
1345
|
const withoutIdentity = (env) => Object.fromEntries(Object.entries(env).flatMap(([key, value]) => value === void 0 || IDENTITY_KEYS.has(key) ? [] : [[key, value]]));
|
|
1220
1346
|
/**
|
|
1221
|
-
* What a Claude session
|
|
1222
|
-
* Bash from speaking as the user
|
|
1347
|
+
* What a Claude session of a run in `store` (default: the user's) runs with: no
|
|
1348
|
+
* identity, only the marker that stops its Bash from speaking as the user
|
|
1349
|
+
* through `ork chat`, and the stores it must not do that in.
|
|
1223
1350
|
*/
|
|
1224
|
-
const sessionEnv = (env) => ({
|
|
1351
|
+
const sessionEnv = (env, store) => ({
|
|
1225
1352
|
...withoutIdentity(env),
|
|
1226
|
-
[SESSION_ENV]: "1"
|
|
1353
|
+
[SESSION_ENV]: "1",
|
|
1354
|
+
...guardOf(env, store)
|
|
1227
1355
|
});
|
|
1228
1356
|
/**
|
|
1229
|
-
*
|
|
1230
|
-
*
|
|
1357
|
+
* Who a node's program is, and the stores its run guards (none listed without
|
|
1358
|
+
* `store`, which guards every one). It is attribution, not a boundary.
|
|
1231
1359
|
*/
|
|
1232
|
-
const
|
|
1233
|
-
|
|
1234
|
-
const identityOf = (context, orkBin) => ({
|
|
1360
|
+
const identityOf = (context, orkBin, environ = {}, store) => ({
|
|
1361
|
+
...store === void 0 ? {} : guardOf(environ, store),
|
|
1235
1362
|
[RUN_ENV]: context.runId,
|
|
1236
1363
|
ORK_NODE: context.id,
|
|
1237
1364
|
ORK_ROUND: String(context.round),
|
|
@@ -1453,7 +1580,7 @@ const toolExecutor = (given, options = {}) => async (context) => {
|
|
|
1453
1580
|
const outputFile = node.output?.format === "json" ? prepareOutputFile(context.runDir, context.id, context.round) : void 0;
|
|
1454
1581
|
const env = {
|
|
1455
1582
|
...nodeEnv.values,
|
|
1456
|
-
...identityOf(context, options.orkBin),
|
|
1583
|
+
...identityOf(context, options.orkBin, environ, options.store),
|
|
1457
1584
|
...maskedEnv(namesToMask(context.workflow, environ, options.maskedNames)),
|
|
1458
1585
|
...outputFile === void 0 ? {} : { [OUTPUT_ENV]: outputFile },
|
|
1459
1586
|
...node.watch === void 0 ? {} : { [STATE_ENV]: prepareStateFile(context.runDir, context.id) }
|
|
@@ -1730,30 +1857,6 @@ const checkedResult = (result, { check, resumes }) => {
|
|
|
1730
1857
|
* through the session's ports, and resumes its session on re-entry
|
|
1731
1858
|
* (ADR-019).
|
|
1732
1859
|
*/
|
|
1733
|
-
const MAX_INPUT = 8192;
|
|
1734
|
-
const inputJson = (input) => JSON.stringify(input ?? {}, void 0, 2);
|
|
1735
|
-
/** What a sent-back input is whole, for `read_input`, when its note cuts it. */
|
|
1736
|
-
const wholeInput = (input) => {
|
|
1737
|
-
const json = inputJson(input);
|
|
1738
|
-
return json.length > MAX_INPUT ? json : void 0;
|
|
1739
|
-
};
|
|
1740
|
-
/** A re-entry's note of what was sent back; `read_input` has a cut rest. */
|
|
1741
|
-
const reentryNote = (round, input) => {
|
|
1742
|
-
const json = inputJson(input);
|
|
1743
|
-
const cut = json.length > MAX_INPUT;
|
|
1744
|
-
const body = cut ? `${json.slice(0, MAX_INPUT)}\n…` : json;
|
|
1745
|
-
const rest = cut ? `\n\nThat is the first ${String(MAX_INPUT)} of its ${String(json.length)} characters. Call read_input for all of it before you act on it.` : "";
|
|
1746
|
-
return `\n\n## Round ${String(round)}\n\nThe workflow sent this task back to you with:\n\n\`\`\`json\n${body}\n\`\`\`${rest}`;
|
|
1747
|
-
};
|
|
1748
|
-
/**
|
|
1749
|
-
* What a resumed leader is told: it already has its brief and its earlier work,
|
|
1750
|
-
* so only the new round, and the new task when its owner sent one.
|
|
1751
|
-
*/
|
|
1752
|
-
const resumeMessage = (round, input, brief) => [brief === void 0 ? sentBack(round, input) : `## New task from \`${brief.from}\`\n\n${brief.text}`, "End with a new structured report."].join("\n\n");
|
|
1753
|
-
/** A first-round resume is a session the run was cut off in (`ork resume`). */
|
|
1754
|
-
const sentBack = (round, input) => round === 1 ? "Ork was stopped while you worked on this task, and the run is resumed. Your earlier turns are above: check what you finished, and carry on from there." : `The workflow sent your task back to you.${reentryNote(round, input)}`;
|
|
1755
|
-
/** What a session is told when messages came in as it finished. */
|
|
1756
|
-
const lateMessage = (count) => `[ork] ${String(count)} message${count === 1 ? "" : "s"} came in while you finished. Call read_inbox, act on ${count === 1 ? "it" : "them"} if needed, and end with a new structured report.`;
|
|
1757
1860
|
/** A re-entered leader resumes; anyone else starts with the full brief. */
|
|
1758
1861
|
const specFor = (spec, context) => {
|
|
1759
1862
|
if (context.resume !== void 0) return {
|
|
@@ -1820,9 +1923,9 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1820
1923
|
if (context.followUps !== void 0) ports.followUps = context.followUps;
|
|
1821
1924
|
const whole = context.round > 1 ? wholeInput(context.input) : void 0;
|
|
1822
1925
|
if (whole !== void 0) ports.input = whole;
|
|
1823
|
-
const watch = limitWatch(context);
|
|
1824
|
-
const run = async (asked) => {
|
|
1825
|
-
const watching = watch.session(asked);
|
|
1926
|
+
const watch = limitWatch(context, runCapsOf(context, planOptions));
|
|
1927
|
+
const run = async (asked, prior = 0) => {
|
|
1928
|
+
const watching = watch.session(asked, prior);
|
|
1826
1929
|
context.reserveBudget?.(asked.budgetUsd);
|
|
1827
1930
|
context.reserveTokens?.(asked.budgetTokens);
|
|
1828
1931
|
const session = runtime.start(asked, ports);
|
|
@@ -1851,7 +1954,7 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1851
1954
|
return outcome;
|
|
1852
1955
|
};
|
|
1853
1956
|
const first = await run(spec);
|
|
1854
|
-
const limitsAfter = ({ sessionId, usage }) => {
|
|
1957
|
+
const limitsAfter = ({ sessionId, usage }, turnsAtLeast = 0) => {
|
|
1855
1958
|
const quota = context.quota?.();
|
|
1856
1959
|
if (quota?.blocked !== void 0) {
|
|
1857
1960
|
watch.stoppedBy(quota.blocked);
|
|
@@ -1864,14 +1967,14 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1864
1967
|
}, context.budgetLeftNow?.(), quota?.leftUsd, {
|
|
1865
1968
|
run: context.tokensLeftNow?.(),
|
|
1866
1969
|
quota: quota?.leftTokens
|
|
1867
|
-
});
|
|
1970
|
+
}, turnsAtLeast);
|
|
1868
1971
|
};
|
|
1869
1972
|
const resumeWith = (outcome, prompt, limits) => run({
|
|
1870
1973
|
...spec,
|
|
1871
1974
|
...limits,
|
|
1872
1975
|
resume: outcome.sessionId,
|
|
1873
1976
|
prompt
|
|
1874
|
-
});
|
|
1977
|
+
}, outcome.usage.costUsd);
|
|
1875
1978
|
const lateTurn = (current) => {
|
|
1876
1979
|
const late = context.mailbox?.pending() ?? 0;
|
|
1877
1980
|
const limits = limitsAfter(current);
|
|
@@ -1897,7 +2000,7 @@ const claudeExecutor = (options) => async (context) => {
|
|
|
1897
2000
|
},
|
|
1898
2001
|
late: lateTurn,
|
|
1899
2002
|
resume: async (current, check) => {
|
|
1900
|
-
const limits = limitsAfter(current);
|
|
2003
|
+
const limits = limitsAfter(current, 20);
|
|
1901
2004
|
if (limits === null) return null;
|
|
1902
2005
|
await context.hold?.wait("start", context.signal);
|
|
1903
2006
|
return context.signal.aborted ? null : resumeWith(current, checkFailedMessage(check, {
|
|
@@ -2271,6 +2374,7 @@ const spawnTool = (program, args, { cwd, signal, env, priority }) => {
|
|
|
2271
2374
|
const builtinExecutors = (options) => {
|
|
2272
2375
|
const tool = toolExecutor(options.tools ?? spawnTool, {
|
|
2273
2376
|
...options.orkBin === void 0 ? {} : { orkBin: options.orkBin },
|
|
2377
|
+
...options.store === void 0 ? {} : { store: options.store },
|
|
2274
2378
|
...options.maskedNames === void 0 ? {} : { maskedNames: options.maskedNames }
|
|
2275
2379
|
});
|
|
2276
2380
|
const claude = claudeExecutor({
|
|
@@ -2411,4 +2515,4 @@ const missingPrograms = (workflow, installed = (program) => onPath(program)) =>
|
|
|
2411
2515
|
}
|
|
2412
2516
|
return [...missing.values()];
|
|
2413
2517
|
};
|
|
2414
|
-
export {
|
|
2518
|
+
export { clearOutputFiles as _, builtinExecutors as a, toolExecutor as c, maskedEnv as d, maskedNamesOfRun as f, storeGuarded as g, runMarkerIn as h, onPath as i, clearStateFiles as l, sessionEnv as m, missingProgramMessage as n, spawnTool as o, runScrubber as p, missingPrograms as r, mcpCallExecutor as s, envProblems as t, inheritedScrubber as u, namedMcpEntries as v, resultOf as y };
|