@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,13 +1,13 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import {
|
|
3
|
-
import { h as nodeSession, n as ORK_VERSION, v as findFollowUp } from "./__build_info__-
|
|
4
|
-
import { n as newRunId } from "./find-
|
|
1
|
+
import { $r as beforeOf, $t as git, An as liveStatus, Br as inside, Cn as endLinksOf, Ct as startRun, Ei as DRIVER_IDS, Fn as templateOf, G as parseInputPairs, Gn as createSignals, Hn as gitRulesOf, K as resolveInputs, Kn as endOf, L as openStore, Lr as runDirOf, Pr as placeFiles, Qt as inspectProject, R as storePath, Si as readyDriver, St as walkSummary, Ti as AGENT_DRIVERS, Tt as userMessageText, U as createReplay, Un as loadProject, W as replayLoopAnswers, Wn as machineRulesOf, _n as takeFollowUps, _t as planNodeSession, an as sessionDriver, at as adoptedCalls, ct as DETACHED_ENV, di as isClaudeNode, dr as parseTemplate, dt as NOT_STARTED, et as createRelay, fn as createFollowUps, fr as renderTemplate, hi as retiredDriverHint, hr as socketPath, it as createCalls, lt as detachRun, m as resolveBase, mr as serveRun, n as resumedOptions, nr as runGh, or as createFeedback, pn as checkFollowUpsDue, t as createWorkspace, vn as deliverFollowUp, vr as maskReplacer } from "./src-DLsFsmOD.mjs";
|
|
2
|
+
import { c as callsShown, d as createDecisionQueue, l as createTerminalHuman, o as formatEvent, s as formatRunEvent, t as attachRun } from "./attach-RQx__RzH.mjs";
|
|
3
|
+
import { h as nodeSession, n as ORK_VERSION, v as findFollowUp } from "./__build_info__-C1Nm0hlJ.mjs";
|
|
4
|
+
import { n as newRunId } from "./find-k489laBa.mjs";
|
|
5
5
|
import { c as write, i as detachHint, l as writeErr, s as style } from "./output-B1kwo0YU.mjs";
|
|
6
|
-
import { _ as
|
|
7
|
-
import { i as refusedBuiltin, o as workflowArg } from "./workflows-
|
|
8
|
-
import { a as formatReport, c as checksHere, d as unfitLines, i as walkLoaded, l as chooseReadyDriver, o as warningLines, s as budgetWarningLines, t as loadWorkflow, u as
|
|
6
|
+
import { _ as clearOutputFiles, a as builtinExecutors, d as maskedEnv, f as maskedNamesOfRun, i as onPath, l as clearStateFiles, m as sessionEnv, n as missingProgramMessage, p as runScrubber, r as missingPrograms, t as envProblems, v as namedMcpEntries, y as resultOf } from "./src-DmmrX2_C.mjs";
|
|
7
|
+
import { i as refusedBuiltin, o as workflowArg } from "./workflows-Djo1edlK.mjs";
|
|
8
|
+
import { a as formatReport, c as checksHere, d as runReadyDriver, f as unfitLines, i as walkLoaded, l as chooseReadyDriver, o as warningLines, s as budgetWarningLines, t as loadWorkflow, u as overrideRefusal } from "./validate-BS0nCccW.mjs";
|
|
9
9
|
import { a as createView, o as styler } from "./verdict-CMvPCpWC.mjs";
|
|
10
|
-
import { i as runLimits, r as limitsRefusal, t as chatNotice } from "./limits-
|
|
10
|
+
import { i as runLimits, r as limitsRefusal, t as chatNotice } from "./limits-CHqBO7wb.mjs";
|
|
11
11
|
import path from "node:path";
|
|
12
12
|
import { appendFileSync, existsSync, linkSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
13
13
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -478,9 +478,17 @@ const followUpSink = (options) => (input, at) => {
|
|
|
478
478
|
...options.gh === void 0 ? {} : { gh: options.gh }
|
|
479
479
|
});
|
|
480
480
|
};
|
|
481
|
+
/** Gh's own words when it has no login. */
|
|
482
|
+
const SIGNED_OUT = /not logged in|gh auth login/iu;
|
|
483
|
+
/**
|
|
484
|
+
* A gh error's first line, without a trailing period, so the notice is one
|
|
485
|
+
* line.
|
|
486
|
+
*/
|
|
487
|
+
const oneLine = (error) => (error.trim().split("\n")[0] ?? "").trim().replace(/\.$/u, "");
|
|
481
488
|
/**
|
|
482
489
|
* Moves the project's follow-ups whose time has come to due, as every `ork
|
|
483
|
-
* follow-ups` call does first. `gh` failing only warns, through `warn
|
|
490
|
+
* follow-ups` call does first. `gh` failing only warns, through `warn`: one
|
|
491
|
+
* line per call, however many refs failed.
|
|
484
492
|
*/
|
|
485
493
|
const refreshDue = async (store, project, warn, gh = runGh) => {
|
|
486
494
|
try {
|
|
@@ -489,7 +497,8 @@ const refreshDue = async (store, project, warn, gh = runGh) => {
|
|
|
489
497
|
project,
|
|
490
498
|
gh
|
|
491
499
|
});
|
|
492
|
-
|
|
500
|
+
const [first] = checked.errors;
|
|
501
|
+
if (first !== void 0) warn(checked.errors.some((error) => SIGNED_OUT.test(error)) ? "Can't check PRs: `gh` isn't signed in. After-merge follow-ups stay waiting; run `gh auth login` yourself." : `Can't check PRs: ${oneLine(first)}. After-merge follow-ups stay waiting.`);
|
|
493
502
|
return checked.due;
|
|
494
503
|
} catch (error) {
|
|
495
504
|
warn(`couldn't check what is due: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -610,6 +619,10 @@ const runNode = async (options) => {
|
|
|
610
619
|
named: plan.driver,
|
|
611
620
|
pinned: plan.pinned
|
|
612
621
|
});
|
|
622
|
+
if (options.driver !== void 0 && named === options.driver) {
|
|
623
|
+
const refusal = await overrideRefusal(options.driver, options.driverChecks);
|
|
624
|
+
if (refusal.length > 0) return failed(refusal);
|
|
625
|
+
}
|
|
613
626
|
const ready = named === void 0 ? chooseReadyDriver([options.nodeId], true, await (options.driverChecks ?? checksHere)()) : null;
|
|
614
627
|
if (ready?.ok === false) return failed(ready.lines);
|
|
615
628
|
if (ready?.line !== void 0) options.err(ready.line);
|
|
@@ -1149,7 +1162,8 @@ const runWorkflowFile = async (options) => {
|
|
|
1149
1162
|
const executorOptions = {
|
|
1150
1163
|
runtimeFor: (driver) => options.runtime(driver, placeFiles(place).runs),
|
|
1151
1164
|
inPlace: options.inPlace,
|
|
1152
|
-
maskedNames
|
|
1165
|
+
maskedNames,
|
|
1166
|
+
store: storePath(options.home)
|
|
1153
1167
|
};
|
|
1154
1168
|
if (options.driver !== void 0) executorOptions.driver = options.driver;
|
|
1155
1169
|
if (ready.readyDriver !== void 0) executorOptions.readyDriver = ready.readyDriver;
|
|
@@ -1238,7 +1252,7 @@ const isDriver = (value) => AGENT_DRIVERS.some((d) => d.id === value);
|
|
|
1238
1252
|
/** Why `--driver` refuses a value: a retired id names its replacement. */
|
|
1239
1253
|
const badDriver = (value) => `--${retiredDriverHint(value) ?? `driver must be one of ${AGENT_DRIVERS.map((d) => d.id).join(", ")}, got \`${value}\``}`;
|
|
1240
1254
|
/** Loads the drivers only when a run starts, so other commands stay fast. */
|
|
1241
|
-
const loadRuntime = async (driver, runs) => (await import("./src-
|
|
1255
|
+
const loadRuntime = async (driver, runs) => (await import("./src-BHod38UQ.mjs")).createRuntime(driver, sessionEnv(process.env), runs);
|
|
1242
1256
|
/**
|
|
1243
1257
|
* Ctrl-C asks the run to stop, and so does a terminal closing or a polite kill,
|
|
1244
1258
|
* so that the run ends tidily and leaves no result unscrubbed.
|
|
@@ -1289,11 +1303,11 @@ const run = defineCommand({
|
|
|
1289
1303
|
},
|
|
1290
1304
|
"follow-ups": {
|
|
1291
1305
|
type: "string",
|
|
1292
|
-
description: "Follow-up ids an agent does (repeatable or comma-separated): they become the run's brief, done when it publishes"
|
|
1306
|
+
description: "Follow-up ids an agent does, or `due` for every due one (repeatable or comma-separated): they become the run's brief, done when it publishes"
|
|
1293
1307
|
},
|
|
1294
1308
|
driver: {
|
|
1295
1309
|
type: "string",
|
|
1296
|
-
description: "
|
|
1310
|
+
description: `${DRIVER_IDS.join(", ")} (default: the driver ready here)`
|
|
1297
1311
|
},
|
|
1298
1312
|
"in-place": {
|
|
1299
1313
|
type: "boolean",
|
|
@@ -1360,8 +1374,9 @@ const run = defineCommand({
|
|
|
1360
1374
|
process.exitCode = 1;
|
|
1361
1375
|
return;
|
|
1362
1376
|
}
|
|
1377
|
+
const { prepareFollowUps: prepare, followUpsFlagFine: flagFine } = await import("./brief-Bs3cMQU0.mjs");
|
|
1363
1378
|
const cwd = process.cwd();
|
|
1364
|
-
if (refusedBuiltin([args.workflow], cwd)) return;
|
|
1379
|
+
if (refusedBuiltin([args.workflow], cwd) || !flagFine(rawArgs)) return;
|
|
1365
1380
|
const target = workflowArg(args.workflow, cwd);
|
|
1366
1381
|
const store = openStore();
|
|
1367
1382
|
if (args.detach) {
|
|
@@ -1393,9 +1408,8 @@ const run = defineCommand({
|
|
|
1393
1408
|
}
|
|
1394
1409
|
return;
|
|
1395
1410
|
}
|
|
1396
|
-
const
|
|
1397
|
-
|
|
1398
|
-
if (taking === void 0) {
|
|
1411
|
+
const taken = await prepare(store, rawArgs, args["run-id"], target);
|
|
1412
|
+
if (taken === void 0) {
|
|
1399
1413
|
store.close();
|
|
1400
1414
|
return;
|
|
1401
1415
|
}
|
|
@@ -1403,7 +1417,7 @@ const run = defineCommand({
|
|
|
1403
1417
|
const common = {
|
|
1404
1418
|
file: target.file,
|
|
1405
1419
|
cwd,
|
|
1406
|
-
|
|
1420
|
+
...taken,
|
|
1407
1421
|
inPlace: args["in-place"],
|
|
1408
1422
|
...args["machine-claude"] ? { machineClaude: true } : {},
|
|
1409
1423
|
json: args.json,
|
|
@@ -1411,7 +1425,6 @@ const run = defineCommand({
|
|
|
1411
1425
|
runtime: loadRuntime,
|
|
1412
1426
|
store,
|
|
1413
1427
|
...human === void 0 ? {} : { human },
|
|
1414
|
-
...taking.runId === void 0 ? {} : { runId: taking.runId },
|
|
1415
1428
|
out: write,
|
|
1416
1429
|
err: writeErr,
|
|
1417
1430
|
...args.driver === void 0 ? {} : { driver: args.driver }
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $ as sendFromScript, L as openStore, Q as scriptRefusal, Z as TOO_LONG } from "./src-DLsFsmOD.mjs";
|
|
2
2
|
import { l as writeErr } from "./output-B1kwo0YU.mjs";
|
|
3
|
-
import { u as inheritedScrubber } from "./src-
|
|
3
|
+
import { u as inheritedScrubber } from "./src-DmmrX2_C.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
// src/commands/send.ts
|
|
6
6
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $n as FEEDBACK_TARGETS, Bn as countedTokens, It as lowerChild, Lt as programFound, Qn as FEEDBACK_SEVERITIES, Rt as startAt, Ti as AGENT_DRIVERS, Xn as endFor, Yn as END_KINDS, Zn as FEEDBACK_KINDS, _r as MASK, dn as FOLLOW_UP_KINDS, qn as outcomeEnd, vt as parseReport, yr as maskSecrets } from "./src-DLsFsmOD.mjs";
|
|
2
2
|
import { n as holdGuard, r as orphanGuard } from "./orphan-guard-BFKg8VSd.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { z } from "zod";
|
|
@@ -9,13 +9,6 @@ import { createInterface } from "node:readline";
|
|
|
9
9
|
import { execFile, spawn } from "node:child_process";
|
|
10
10
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
11
11
|
import { createServer } from "node:http";
|
|
12
|
-
// ../core/src/follow-ups/types.ts
|
|
13
|
-
const FOLLOW_UP_KINDS = [
|
|
14
|
-
"change",
|
|
15
|
-
"message",
|
|
16
|
-
"task",
|
|
17
|
-
"check"
|
|
18
|
-
];
|
|
19
12
|
// ../runtime/src/hold.ts
|
|
20
13
|
/**
|
|
21
14
|
* Waits at a tool call while `hold` is held: "go" once it is released within
|
|
@@ -456,8 +449,10 @@ const classifyProviderError = (text, status) => {
|
|
|
456
449
|
* it can see (a turn cap, an exit code, a provider's error), and the report
|
|
457
450
|
* the session handed in decides the rest. The runner then writes the end in
|
|
458
451
|
* the node's numbers, its detail scrubbed with the feedback redactor. A
|
|
459
|
-
* session the driver could not run
|
|
460
|
-
*
|
|
452
|
+
* session the driver could not run (or that was stopped, with what it had
|
|
453
|
+
* spent) ends as `driverFailure` says, one whose process died as
|
|
454
|
+
* `failureReason` explains, and one that reached its token cap as
|
|
455
|
+
* `tokenCapOutcome` says.
|
|
461
456
|
*/
|
|
462
457
|
/** The end a report gives: done, a reported failure, or none handed in. */
|
|
463
458
|
const reportEnd = (report) => {
|
|
@@ -482,9 +477,15 @@ const errorEnd = (text, fallback = "unknown", status) => endFor(classifyProvider
|
|
|
482
477
|
*/
|
|
483
478
|
const settleEnd = (outcome, facts, scrub) => {
|
|
484
479
|
const end = outcome.report === null ? outcome.end ?? outcomeEnd(outcome) : reportEnd(outcome.report);
|
|
480
|
+
const { usage } = outcome;
|
|
481
|
+
const used = {
|
|
482
|
+
...facts,
|
|
483
|
+
usedTokens: countedTokens(usage),
|
|
484
|
+
...usage.costUsd > 0 ? { usedUsd: usage.costUsd } : {}
|
|
485
|
+
};
|
|
485
486
|
return {
|
|
486
487
|
...outcome,
|
|
487
|
-
end: endFor(end.kind,
|
|
488
|
+
end: endFor(end.kind, used, end.detail, scrub)
|
|
488
489
|
};
|
|
489
490
|
};
|
|
490
491
|
/**
|
|
@@ -517,6 +518,17 @@ const tokenCapOutcome = ({ budgetTokens }, usage, sessionId) => budgetTokens ===
|
|
|
517
518
|
usage,
|
|
518
519
|
errors: [`Reached the token budget (${String(budgetTokens)})`]
|
|
519
520
|
};
|
|
521
|
+
const REASON_LIMIT = 500;
|
|
522
|
+
/** Noise a Node crash ends stderr with, or a stack frame: never the reason. */
|
|
523
|
+
const isNoise = (line) => /^Node\.js v\d/u.test(line) || /^\s+at /u.test(line);
|
|
524
|
+
/**
|
|
525
|
+
* Why a process ended without a result: the last block of its stderr as one
|
|
526
|
+
* masked, capped line (the reason reaches the terminal, which is not masked).
|
|
527
|
+
*/
|
|
528
|
+
const failureReason = (name, stderr, code) => {
|
|
529
|
+
const block = stderr.split("\n").filter((line) => !isNoise(line)).join("\n").split(/\n\s*\n/u).map((text) => text.trim().replaceAll(/\s*\n\s*/gu, " ")).findLast((text) => text !== "");
|
|
530
|
+
return block === void 0 ? `${name} exited with code ${String(code)}` : maskSecrets(block).slice(0, REASON_LIMIT);
|
|
531
|
+
};
|
|
520
532
|
/**
|
|
521
533
|
* The kind of a failure to run the driver: its program missing, else what a
|
|
522
534
|
* provider's words in it say, else a crash.
|
|
@@ -527,27 +539,32 @@ const failureKind = (error) => {
|
|
|
527
539
|
if (/\bENOENT\b/u.test(text)) return "tool_missing";
|
|
528
540
|
return classifyProviderError(text)?.kind ?? "driver_crashed";
|
|
529
541
|
};
|
|
542
|
+
const NOTHING_SPENT = {
|
|
543
|
+
usage: {
|
|
544
|
+
costUsd: 0,
|
|
545
|
+
inputTokens: 0,
|
|
546
|
+
outputTokens: 0,
|
|
547
|
+
cacheReadTokens: 0,
|
|
548
|
+
cacheWriteTokens: 0,
|
|
549
|
+
turns: 0,
|
|
550
|
+
durationMs: 0
|
|
551
|
+
},
|
|
552
|
+
sessionId: ""
|
|
553
|
+
};
|
|
530
554
|
/**
|
|
531
555
|
* A finished event for a session the driver could not run, ended as `kind` with
|
|
532
|
-
* `message` (Ork's words or the tool's, masked) as its detail
|
|
556
|
+
* `message` (Ork's words or the tool's, masked) as its detail; a stopped one
|
|
557
|
+
* keeps what it had `spent` (its tokens decoded so far, dollars $0).
|
|
533
558
|
*/
|
|
534
|
-
const driverFailure = (message, kind, scope) => ({
|
|
559
|
+
const driverFailure = (message, kind, scope, spent = NOTHING_SPENT) => ({
|
|
535
560
|
type: "session.finished",
|
|
536
561
|
outcome: {
|
|
537
562
|
subtype: "error_driver",
|
|
538
563
|
end: endFor(kind, scope.facts, kind === "interrupted" ? void 0 : message, scope.scrub),
|
|
539
|
-
sessionId:
|
|
564
|
+
sessionId: spent.sessionId,
|
|
540
565
|
report: null,
|
|
541
566
|
text: "",
|
|
542
|
-
usage:
|
|
543
|
-
costUsd: 0,
|
|
544
|
-
inputTokens: 0,
|
|
545
|
-
outputTokens: 0,
|
|
546
|
-
cacheReadTokens: 0,
|
|
547
|
-
cacheWriteTokens: 0,
|
|
548
|
-
turns: 0,
|
|
549
|
-
durationMs: 0
|
|
550
|
-
},
|
|
567
|
+
usage: spent.usage,
|
|
551
568
|
errors: [message]
|
|
552
569
|
}
|
|
553
570
|
});
|
|
@@ -608,7 +625,6 @@ const spawnLeg = (start) => {
|
|
|
608
625
|
* Windows the process ends with `ork`, whatever ends `ork` (the orphan
|
|
609
626
|
* guard).
|
|
610
627
|
*/
|
|
611
|
-
const REASON_LIMIT = 500;
|
|
612
628
|
/** Marks a session's start with the driver that runs it. */
|
|
613
629
|
const withDriver = (event, driver) => event.type === "session.started" ? {
|
|
614
630
|
...event,
|
|
@@ -622,10 +638,11 @@ const settled = (outcome, input) => settleEnd(input.reported === void 0 ? outcom
|
|
|
622
638
|
...outcome,
|
|
623
639
|
report: input.reported()
|
|
624
640
|
}, input.scope.facts, input.scope.scrub);
|
|
641
|
+
/** What this leg `used`, with what the legs before it spent. */
|
|
642
|
+
const withCarried = (input, used) => used === void 0 || input.carried === void 0 ? used ?? input.carried : input.adapter.usage.combine(input.carried, used);
|
|
625
643
|
/** The session's end at its token cap, its earlier legs' usage included. */
|
|
626
644
|
const tokenCapEnd = (input, used, sessionId) => {
|
|
627
|
-
const
|
|
628
|
-
const capped = tokenCapOutcome(input.scope.facts, usage, sessionId);
|
|
645
|
+
const capped = tokenCapOutcome(input.scope.facts, used === void 0 ? void 0 : withCarried(input, used), sessionId);
|
|
629
646
|
return capped === void 0 ? void 0 : {
|
|
630
647
|
type: "session.finished",
|
|
631
648
|
outcome: settled(capped, input)
|
|
@@ -645,37 +662,36 @@ const legStep = (decoded, input) => {
|
|
|
645
662
|
const events = [];
|
|
646
663
|
let ended;
|
|
647
664
|
for (const said of decoded.events) {
|
|
648
|
-
|
|
665
|
+
if (said.type === "session.started" && carried !== void 0) continue;
|
|
666
|
+
if (said.type !== "session.finished") {
|
|
667
|
+
events.push(withDriver(said, adapter.driver));
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
const own = said.outcome.usage;
|
|
671
|
+
const usage = carried === void 0 ? own : adapter.usage.combine(carried, own);
|
|
672
|
+
const last = {
|
|
649
673
|
...said,
|
|
650
|
-
outcome: settled(
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
674
|
+
outcome: settled({
|
|
675
|
+
...said.outcome,
|
|
676
|
+
usage
|
|
677
|
+
}, input)
|
|
678
|
+
};
|
|
679
|
+
const { sessionId, end } = last.outcome;
|
|
680
|
+
if (deferred) ended = {
|
|
655
681
|
kind: "deferred",
|
|
656
|
-
sessionId
|
|
657
|
-
usage:
|
|
658
|
-
last
|
|
682
|
+
sessionId,
|
|
683
|
+
usage: own,
|
|
684
|
+
last
|
|
685
|
+
};
|
|
686
|
+
else if (input.withhold() && wentWell(end?.kind)) ended = {
|
|
687
|
+
kind: "continued",
|
|
688
|
+
sessionId,
|
|
689
|
+
usage: own,
|
|
690
|
+
last
|
|
659
691
|
};
|
|
660
692
|
else {
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
...event,
|
|
664
|
-
outcome: {
|
|
665
|
-
...event.outcome,
|
|
666
|
-
usage
|
|
667
|
-
}
|
|
668
|
-
};
|
|
669
|
-
if (input.withhold() && wentWell(event.outcome.end?.kind)) ended = {
|
|
670
|
-
kind: "continued",
|
|
671
|
-
sessionId: event.outcome.sessionId,
|
|
672
|
-
usage: event.outcome.usage,
|
|
673
|
-
last
|
|
674
|
-
};
|
|
675
|
-
else {
|
|
676
|
-
events.push(last);
|
|
677
|
-
ended = { kind: "finished" };
|
|
678
|
-
}
|
|
693
|
+
events.push(last);
|
|
694
|
+
ended = { kind: "finished" };
|
|
679
695
|
}
|
|
680
696
|
}
|
|
681
697
|
return ended === void 0 ? { events } : {
|
|
@@ -683,16 +699,6 @@ const legStep = (decoded, input) => {
|
|
|
683
699
|
ended
|
|
684
700
|
};
|
|
685
701
|
};
|
|
686
|
-
/** Noise a Node crash ends stderr with, or a stack frame: never the reason. */
|
|
687
|
-
const isNoise = (line) => /^Node\.js v\d/u.test(line) || /^\s+at /u.test(line);
|
|
688
|
-
/**
|
|
689
|
-
* Why a process ended without a result: the last block of its stderr as one
|
|
690
|
-
* masked, capped line (the reason reaches the terminal, which is not masked).
|
|
691
|
-
*/
|
|
692
|
-
const failureReason = (name, stderr, code) => {
|
|
693
|
-
const block = stderr.split("\n").filter((line) => !isNoise(line)).join("\n").split(/\n\s*\n/u).map((text) => text.trim().replaceAll(/\s*\n\s*/gu, " ")).findLast((text) => text !== "");
|
|
694
|
-
return block === void 0 ? `${name} exited with code ${String(code)}` : maskSecrets(block).slice(0, REASON_LIMIT);
|
|
695
|
-
};
|
|
696
702
|
/** Runs one process of a session, yielding its events; returns how it ended. */
|
|
697
703
|
const runLeg = async function* (input) {
|
|
698
704
|
const child = spawnLeg(input);
|
|
@@ -728,11 +734,14 @@ const runLeg = async function* (input) {
|
|
|
728
734
|
if (input.adapter.command.input === void 0) child.stdin.end();
|
|
729
735
|
let outcome;
|
|
730
736
|
let sessionId = "";
|
|
737
|
+
/** What this leg has used so far, as the last usage step said. */
|
|
738
|
+
let used;
|
|
731
739
|
const { decoder } = input;
|
|
732
740
|
const decode = decoder?.decode ?? input.adapter.decode;
|
|
733
741
|
try {
|
|
734
742
|
for await (const line of createInterface({ input: child.stdout })) {
|
|
735
743
|
const decoded = decode(line);
|
|
744
|
+
used = decoded.usage ?? used;
|
|
736
745
|
const { events, ended } = legStep(decoded, input);
|
|
737
746
|
for (const event of decoded.events) if (event.type === "session.started") sessionId = event.sessionId;
|
|
738
747
|
yield* events;
|
|
@@ -760,11 +769,18 @@ const runLeg = async function* (input) {
|
|
|
760
769
|
reason: `could not start ${input.bin}: ${end.message}`,
|
|
761
770
|
end: failureKind(end)
|
|
762
771
|
};
|
|
763
|
-
if (stopped || end.signal !== null)
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
772
|
+
if (stopped || end.signal !== null) {
|
|
773
|
+
const usage = withCarried(input, used);
|
|
774
|
+
return {
|
|
775
|
+
kind: "failed",
|
|
776
|
+
reason: "interrupted",
|
|
777
|
+
end: "interrupted",
|
|
778
|
+
...usage === void 0 ? {} : { spent: {
|
|
779
|
+
usage,
|
|
780
|
+
sessionId
|
|
781
|
+
} }
|
|
782
|
+
};
|
|
783
|
+
}
|
|
768
784
|
const reason = failureReason(input.adapter.command.bin, stderr, end.code);
|
|
769
785
|
return {
|
|
770
786
|
kind: "failed",
|
|
@@ -850,6 +866,7 @@ const releaseTool = defineTool({
|
|
|
850
866
|
});
|
|
851
867
|
// ../runtime/src/tools/delegate.ts
|
|
852
868
|
const BRIEF = z.string().min(1).describe("Self-contained: the goal, the files that matter, and what done looks like. The child does not see your conversation.");
|
|
869
|
+
const DESCRIPTION = z.string().exactOptional().describe("A few words naming this task; shown as the child's label.");
|
|
853
870
|
/** What the leader reads back: each report without the usage details. */
|
|
854
871
|
const reportLine = (report) => {
|
|
855
872
|
const { usage, ...output } = report.output;
|
|
@@ -875,7 +892,8 @@ const delegateTool = defineTool({
|
|
|
875
892
|
const ids = ports.ownership?.children.map((child) => child.id) ?? [];
|
|
876
893
|
return { tasks: z.array(z.strictObject({
|
|
877
894
|
child: z.enum(ids).describe(`The id of a node you own: ${ids.join(", ")}.`),
|
|
878
|
-
brief: BRIEF
|
|
895
|
+
brief: BRIEF,
|
|
896
|
+
description: DESCRIPTION
|
|
879
897
|
})).min(1) };
|
|
880
898
|
},
|
|
881
899
|
available: ({ ports }) => (ports.ownership?.children.length ?? 0) > 0,
|
|
@@ -891,7 +909,8 @@ const spawnWorkerTool = defineTool({
|
|
|
891
909
|
const ids = ports.ownership?.templates ?? [];
|
|
892
910
|
return { tasks: z.array(z.strictObject({
|
|
893
911
|
template: z.enum(ids).describe(`The id of a child worker you own: ${ids.join(", ")}.`),
|
|
894
|
-
brief: BRIEF
|
|
912
|
+
brief: BRIEF,
|
|
913
|
+
description: DESCRIPTION
|
|
895
914
|
})).min(1) };
|
|
896
915
|
},
|
|
897
916
|
available: ({ ports }) => ports.ownership !== void 0 && ports.ownership.templates.length > 0 && ports.ownership.workersLeft() > 0,
|
|
@@ -959,7 +978,7 @@ const followUpTool = defineTool({
|
|
|
959
978
|
text: z.string().min(1).max(4e3)
|
|
960
979
|
}).optional().describe("For a message: what the maintainer will send."),
|
|
961
980
|
steps: z.array(z.strictObject({
|
|
962
|
-
text: z.string().min(1),
|
|
981
|
+
text: z.string().trim().min(1),
|
|
963
982
|
command: z.string().optional().describe("Shown to the maintainer to copy; Ork never runs it.")
|
|
964
983
|
})).max(20).optional().describe("For a task or check: what to do, in order.")
|
|
965
984
|
},
|
|
@@ -1335,7 +1354,9 @@ const createSessionRuntime = (adapter, options = {}) => ({
|
|
|
1335
1354
|
}
|
|
1336
1355
|
const leg = yield* runLeg(legOf(legSpec, spent));
|
|
1337
1356
|
if (leg.kind === "failed") {
|
|
1338
|
-
|
|
1357
|
+
const { spent: stopped } = leg;
|
|
1358
|
+
if (stopped?.sessionId === "") stopped.sessionId = legSpec.resume ?? "";
|
|
1359
|
+
yield driverFailure(leg.reason, leg.end, scope, stopped);
|
|
1339
1360
|
return;
|
|
1340
1361
|
}
|
|
1341
1362
|
if (leg.kind === "finished") return;
|
|
@@ -1612,8 +1633,19 @@ const cacheEnv = (spec) => spec.promptCache === "1h" ? { [PROMPT_CACHE_1H]: "1"
|
|
|
1612
1633
|
const strictMcpOf = (spec) => spec.tools.strictMcp === true || spec.machineClaude !== true;
|
|
1613
1634
|
/** The variables that keep the machine's auto-memory out of a session. */
|
|
1614
1635
|
const memoryEnv = (spec) => spec.machineClaude === true ? {} : { [NO_AUTO_MEMORY]: "1" };
|
|
1636
|
+
/** Claude Code's default timeout for a Bash call, in milliseconds. */
|
|
1637
|
+
const BASH_TIMEOUT = "BASH_DEFAULT_TIMEOUT_MS";
|
|
1638
|
+
/**
|
|
1639
|
+
* Ten minutes, Claude Code's own most for a Bash call. At its default of two, a
|
|
1640
|
+
* longer command (a typecheck of the workspace) moves to the background, and
|
|
1641
|
+
* the session then polls it with `sleep`, `until` or `Monitor`, which ask.
|
|
1642
|
+
*/
|
|
1643
|
+
const BASH_TIMEOUT_MS = 6e5;
|
|
1644
|
+
/** The variables that let a session's Bash calls run to the end in front. */
|
|
1645
|
+
const bashEnv = () => ({ [BASH_TIMEOUT]: String(BASH_TIMEOUT_MS) });
|
|
1615
1646
|
/** Everything a session adds to its driver's environment. */
|
|
1616
1647
|
const driverEnv = (spec) => ({
|
|
1648
|
+
...bashEnv(),
|
|
1617
1649
|
...cacheEnv(spec),
|
|
1618
1650
|
...memoryEnv(spec)
|
|
1619
1651
|
});
|