@whittlelabs/sifter 0.35.0 → 0.37.0
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 +3 -1
- package/bin.js +352 -97
- package/bin.js.map +4 -4
- package/package.json +1 -1
package/bin.js
CHANGED
|
@@ -9961,6 +9961,8 @@ var require_progress = __commonJS({
|
|
|
9961
9961
|
seq = 0;
|
|
9962
9962
|
pending = [];
|
|
9963
9963
|
textBuf = "";
|
|
9964
|
+
/** The last two characters of reasoning emitted since the last tool; null when none. */
|
|
9965
|
+
reasoningTail = null;
|
|
9964
9966
|
timer = null;
|
|
9965
9967
|
closed = false;
|
|
9966
9968
|
flushMs;
|
|
@@ -9970,20 +9972,39 @@ var require_progress = __commonJS({
|
|
|
9970
9972
|
this.flushMs = options.flushMs ?? 400;
|
|
9971
9973
|
this.maxChars = options.maxChars ?? 1200;
|
|
9972
9974
|
}
|
|
9975
|
+
/** A fragment of reasoning, continuing whatever came before it. */
|
|
9973
9976
|
reasoning(text) {
|
|
9974
9977
|
if (this.closed || !text)
|
|
9975
9978
|
return;
|
|
9976
9979
|
this.textBuf += text;
|
|
9980
|
+
this.reasoningTail = ((this.reasoningTail ?? "") + text).slice(-2);
|
|
9977
9981
|
if (this.textBuf.length >= this.maxChars) {
|
|
9978
9982
|
this.flush();
|
|
9979
9983
|
} else {
|
|
9980
9984
|
this.arm();
|
|
9981
9985
|
}
|
|
9982
9986
|
}
|
|
9987
|
+
/**
|
|
9988
|
+
* A whole block of reasoning (a thinking summary, a message between tool
|
|
9989
|
+
* calls), trimmed and set off from the reasoning before it by one blank
|
|
9990
|
+
* line. A consumer flows consecutive reasoning frames into one passage, so
|
|
9991
|
+
* two blocks reported as fragments run together mid-sentence, and a
|
|
9992
|
+
* block's own trailing blank lines (thinking summaries end in them) would
|
|
9993
|
+
* show as empty lines before the next act.
|
|
9994
|
+
*/
|
|
9995
|
+
reasoningBlock(text) {
|
|
9996
|
+
const block = text.trim();
|
|
9997
|
+
if (this.closed || !block)
|
|
9998
|
+
return;
|
|
9999
|
+
const tail = this.reasoningTail;
|
|
10000
|
+
const gap = tail === null || tail.endsWith("\n\n") ? "" : tail.endsWith("\n") ? "\n" : "\n\n";
|
|
10001
|
+
this.reasoning(gap + block);
|
|
10002
|
+
}
|
|
9983
10003
|
tool(name, target) {
|
|
9984
10004
|
if (this.closed || !name)
|
|
9985
10005
|
return;
|
|
9986
10006
|
this.drainText();
|
|
10007
|
+
this.reasoningTail = null;
|
|
9987
10008
|
this.push({ kind: "tool", tool: target ? { name, target } : { name } });
|
|
9988
10009
|
this.flush();
|
|
9989
10010
|
}
|
|
@@ -10611,6 +10632,87 @@ var require_workspace_telemetry = __commonJS({
|
|
|
10611
10632
|
}
|
|
10612
10633
|
});
|
|
10613
10634
|
|
|
10635
|
+
// ../../packages/shuttle/dist/executors/cli-version.js
|
|
10636
|
+
var require_cli_version = __commonJS({
|
|
10637
|
+
"../../packages/shuttle/dist/executors/cli-version.js"(exports2) {
|
|
10638
|
+
"use strict";
|
|
10639
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
10640
|
+
exports2.cliVersion = cliVersion;
|
|
10641
|
+
var child_process_1 = require("child_process");
|
|
10642
|
+
var VERSION_TIMEOUT_MS = 5e3;
|
|
10643
|
+
function cliVersion(bin, env) {
|
|
10644
|
+
return new Promise((resolve) => {
|
|
10645
|
+
try {
|
|
10646
|
+
(0, child_process_1.execFile)(bin, ["--version"], { timeout: VERSION_TIMEOUT_MS, ...env ? { env } : {} }, (err, stdout) => {
|
|
10647
|
+
resolve(err ? null : String(stdout).match(/\d+\.\d+\.\d+/)?.[0] ?? null);
|
|
10648
|
+
});
|
|
10649
|
+
} catch {
|
|
10650
|
+
resolve(null);
|
|
10651
|
+
}
|
|
10652
|
+
});
|
|
10653
|
+
}
|
|
10654
|
+
}
|
|
10655
|
+
});
|
|
10656
|
+
|
|
10657
|
+
// ../../packages/shuttle/dist/version-check.js
|
|
10658
|
+
var require_version_check = __commonJS({
|
|
10659
|
+
"../../packages/shuttle/dist/version-check.js"(exports2) {
|
|
10660
|
+
"use strict";
|
|
10661
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
10662
|
+
exports2.UpgradeRequiredError = exports2.LATEST_VERSION_HEADER = exports2.MIN_VERSION_HEADER = void 0;
|
|
10663
|
+
exports2.compareSemver = compareSemver;
|
|
10664
|
+
exports2.checkResponseMinVersion = checkResponseMinVersion;
|
|
10665
|
+
exports2.readResponseLatestVersion = readResponseLatestVersion;
|
|
10666
|
+
exports2.MIN_VERSION_HEADER = "x-sifter-min-version";
|
|
10667
|
+
exports2.LATEST_VERSION_HEADER = "x-sifter-latest-version";
|
|
10668
|
+
var UpgradeRequiredError2 = class extends Error {
|
|
10669
|
+
currentVersion;
|
|
10670
|
+
minVersion;
|
|
10671
|
+
packageName;
|
|
10672
|
+
constructor(opts) {
|
|
10673
|
+
super(`The backend requires ${opts.packageName} ${opts.minVersion} or newer. You're running ${opts.currentVersion}. Upgrade: npm install -g ${opts.packageName}@latest`);
|
|
10674
|
+
this.name = "UpgradeRequiredError";
|
|
10675
|
+
this.currentVersion = opts.currentVersion;
|
|
10676
|
+
this.minVersion = opts.minVersion;
|
|
10677
|
+
this.packageName = opts.packageName;
|
|
10678
|
+
}
|
|
10679
|
+
};
|
|
10680
|
+
exports2.UpgradeRequiredError = UpgradeRequiredError2;
|
|
10681
|
+
function compareSemver(a, b) {
|
|
10682
|
+
const parse = (v) => {
|
|
10683
|
+
const core = v.replace(/^v/, "").split(/[-+]/, 1)[0];
|
|
10684
|
+
const parts = core.split(".").map((p) => Number.parseInt(p, 10));
|
|
10685
|
+
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
10686
|
+
};
|
|
10687
|
+
const [aMaj, aMin, aPat] = parse(a);
|
|
10688
|
+
const [bMaj, bMin, bPat] = parse(b);
|
|
10689
|
+
if (aMaj !== bMaj)
|
|
10690
|
+
return aMaj < bMaj ? -1 : 1;
|
|
10691
|
+
if (aMin !== bMin)
|
|
10692
|
+
return aMin < bMin ? -1 : 1;
|
|
10693
|
+
if (aPat !== bPat)
|
|
10694
|
+
return aPat < bPat ? -1 : 1;
|
|
10695
|
+
return 0;
|
|
10696
|
+
}
|
|
10697
|
+
function checkResponseMinVersion(opts) {
|
|
10698
|
+
const minVersion = opts.response.headers.get(exports2.MIN_VERSION_HEADER);
|
|
10699
|
+
if (!minVersion)
|
|
10700
|
+
return;
|
|
10701
|
+
if (compareSemver(opts.currentVersion, minVersion) >= 0)
|
|
10702
|
+
return;
|
|
10703
|
+
throw new UpgradeRequiredError2({
|
|
10704
|
+
currentVersion: opts.currentVersion,
|
|
10705
|
+
minVersion,
|
|
10706
|
+
packageName: opts.packageName
|
|
10707
|
+
});
|
|
10708
|
+
}
|
|
10709
|
+
function readResponseLatestVersion(response) {
|
|
10710
|
+
const latest = response.headers.get(exports2.LATEST_VERSION_HEADER);
|
|
10711
|
+
return latest && latest.trim().length > 0 ? latest.trim() : null;
|
|
10712
|
+
}
|
|
10713
|
+
}
|
|
10714
|
+
});
|
|
10715
|
+
|
|
10614
10716
|
// ../../packages/shuttle/dist/executors/claude-code.js
|
|
10615
10717
|
var require_claude_code = __commonJS({
|
|
10616
10718
|
"../../packages/shuttle/dist/executors/claude-code.js"(exports2) {
|
|
@@ -10634,9 +10736,12 @@ var require_claude_code = __commonJS({
|
|
|
10634
10736
|
var json_answer_1 = require_json_answer();
|
|
10635
10737
|
var models_1 = require_models();
|
|
10636
10738
|
var workspace_telemetry_1 = require_workspace_telemetry();
|
|
10739
|
+
var cli_version_1 = require_cli_version();
|
|
10637
10740
|
var apply_1 = require_apply();
|
|
10741
|
+
var version_check_1 = require_version_check();
|
|
10638
10742
|
var DEFAULT_TIMEOUT = 6e5;
|
|
10639
10743
|
var REVIEW_TOOLS = ["Read", "Grep", "Glob"];
|
|
10744
|
+
var THINKING_DISPLAY_FLOOR = "2.1.94";
|
|
10640
10745
|
function reviewRunArgs(settingSources) {
|
|
10641
10746
|
return [
|
|
10642
10747
|
"--setting-sources",
|
|
@@ -10891,13 +10996,17 @@ var require_claude_code = __commonJS({
|
|
|
10891
10996
|
args.push("--model", model);
|
|
10892
10997
|
if (effort)
|
|
10893
10998
|
args.push("--effort", effort);
|
|
10999
|
+
const version = await (0, cli_version_1.cliVersion)("claude", childEnv);
|
|
11000
|
+
if (version !== null && (0, version_check_1.compareSemver)(version, THINKING_DISPLAY_FLOOR) >= 0) {
|
|
11001
|
+
args.push("--thinking-display", "summarized");
|
|
11002
|
+
}
|
|
10894
11003
|
if (this.config.maxTurns !== void 0)
|
|
10895
11004
|
args.push("--max-turns", String(this.config.maxTurns));
|
|
10896
11005
|
if (allowedTools && allowedTools.length > 0) {
|
|
10897
11006
|
args.push("--allowedTools", allowedTools.join(","));
|
|
10898
11007
|
}
|
|
10899
11008
|
const tap = reporter ? new progress_1.StreamJsonTap({
|
|
10900
|
-
onReasoning: (t) => reporter.
|
|
11009
|
+
onReasoning: (t) => reporter.reasoningBlock(t),
|
|
10901
11010
|
onTool: (name, target) => reporter.tool(name, target),
|
|
10902
11011
|
onResult: () => reporter.flush()
|
|
10903
11012
|
}) : null;
|
|
@@ -11395,6 +11504,11 @@ var require_codex = __commonJS({
|
|
|
11395
11504
|
'approval_policy="never"',
|
|
11396
11505
|
"-c",
|
|
11397
11506
|
'web_search="disabled"',
|
|
11507
|
+
// Reasoning summaries are the live view's narration. With the user config
|
|
11508
|
+
// ignored, codex 0.162.1 emits no reasoning items at all; `detailed`
|
|
11509
|
+
// brings them back without touching the answer.
|
|
11510
|
+
"-c",
|
|
11511
|
+
'model_reasoning_summary="detailed"',
|
|
11398
11512
|
...DISABLED_FEATURES.flatMap((f) => ["--disable", f])
|
|
11399
11513
|
];
|
|
11400
11514
|
}
|
|
@@ -11649,12 +11763,17 @@ var require_codex = __commonJS({
|
|
|
11649
11763
|
if (event.type === "item.started" && kind === "command_execution" && typeof item?.command === "string") {
|
|
11650
11764
|
reporter.tool("shell", item.command);
|
|
11651
11765
|
} else if (event.type === "item.completed" && kind === "reasoning" && typeof item?.text === "string") {
|
|
11652
|
-
reporter.
|
|
11766
|
+
reporter.reasoningBlock(plainHeadings(item.text));
|
|
11767
|
+
} else if (event.type === "item.completed" && kind === "agent_message" && typeof item?.text === "string" && (0, json_answer_1.extractJsonObject)(item.text) === void 0) {
|
|
11768
|
+
reporter.reasoningBlock(item.text);
|
|
11653
11769
|
} else if (event.type === "turn.completed") {
|
|
11654
11770
|
reporter.flush();
|
|
11655
11771
|
}
|
|
11656
11772
|
});
|
|
11657
11773
|
}
|
|
11774
|
+
function plainHeadings(text) {
|
|
11775
|
+
return text.replace(/^\*\*([^*\n]+)\*\*[ \t]*$/gm, "$1");
|
|
11776
|
+
}
|
|
11658
11777
|
function itemKind(item) {
|
|
11659
11778
|
const kind = typeof item.type === "string" ? item.type : typeof item.item_type === "string" ? item.item_type : null;
|
|
11660
11779
|
return kind === "assistant_message" ? "agent_message" : kind;
|
|
@@ -12182,7 +12301,7 @@ ${JSON.stringify(spec.outputSchema)}
|
|
|
12182
12301
|
const target = [args.path, args.pattern].find((v) => typeof v === "string");
|
|
12183
12302
|
reporter.tool(data.toolName, target);
|
|
12184
12303
|
} else if (event.type === "assistant.reasoning" && typeof data.content === "string") {
|
|
12185
|
-
reporter.
|
|
12304
|
+
reporter.reasoningBlock(data.content);
|
|
12186
12305
|
} else if (event.type === "assistant.turn_end") {
|
|
12187
12306
|
reporter.flush();
|
|
12188
12307
|
}
|
|
@@ -13730,65 +13849,6 @@ var require_custom_script = __commonJS({
|
|
|
13730
13849
|
}
|
|
13731
13850
|
});
|
|
13732
13851
|
|
|
13733
|
-
// ../../packages/shuttle/dist/version-check.js
|
|
13734
|
-
var require_version_check = __commonJS({
|
|
13735
|
-
"../../packages/shuttle/dist/version-check.js"(exports2) {
|
|
13736
|
-
"use strict";
|
|
13737
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
13738
|
-
exports2.UpgradeRequiredError = exports2.LATEST_VERSION_HEADER = exports2.MIN_VERSION_HEADER = void 0;
|
|
13739
|
-
exports2.compareSemver = compareSemver;
|
|
13740
|
-
exports2.checkResponseMinVersion = checkResponseMinVersion;
|
|
13741
|
-
exports2.readResponseLatestVersion = readResponseLatestVersion;
|
|
13742
|
-
exports2.MIN_VERSION_HEADER = "x-sifter-min-version";
|
|
13743
|
-
exports2.LATEST_VERSION_HEADER = "x-sifter-latest-version";
|
|
13744
|
-
var UpgradeRequiredError2 = class extends Error {
|
|
13745
|
-
currentVersion;
|
|
13746
|
-
minVersion;
|
|
13747
|
-
packageName;
|
|
13748
|
-
constructor(opts) {
|
|
13749
|
-
super(`The backend requires ${opts.packageName} ${opts.minVersion} or newer. You're running ${opts.currentVersion}. Upgrade: npm install -g ${opts.packageName}@latest`);
|
|
13750
|
-
this.name = "UpgradeRequiredError";
|
|
13751
|
-
this.currentVersion = opts.currentVersion;
|
|
13752
|
-
this.minVersion = opts.minVersion;
|
|
13753
|
-
this.packageName = opts.packageName;
|
|
13754
|
-
}
|
|
13755
|
-
};
|
|
13756
|
-
exports2.UpgradeRequiredError = UpgradeRequiredError2;
|
|
13757
|
-
function compareSemver(a, b) {
|
|
13758
|
-
const parse = (v) => {
|
|
13759
|
-
const core = v.replace(/^v/, "").split(/[-+]/, 1)[0];
|
|
13760
|
-
const parts = core.split(".").map((p) => Number.parseInt(p, 10));
|
|
13761
|
-
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
13762
|
-
};
|
|
13763
|
-
const [aMaj, aMin, aPat] = parse(a);
|
|
13764
|
-
const [bMaj, bMin, bPat] = parse(b);
|
|
13765
|
-
if (aMaj !== bMaj)
|
|
13766
|
-
return aMaj < bMaj ? -1 : 1;
|
|
13767
|
-
if (aMin !== bMin)
|
|
13768
|
-
return aMin < bMin ? -1 : 1;
|
|
13769
|
-
if (aPat !== bPat)
|
|
13770
|
-
return aPat < bPat ? -1 : 1;
|
|
13771
|
-
return 0;
|
|
13772
|
-
}
|
|
13773
|
-
function checkResponseMinVersion(opts) {
|
|
13774
|
-
const minVersion = opts.response.headers.get(exports2.MIN_VERSION_HEADER);
|
|
13775
|
-
if (!minVersion)
|
|
13776
|
-
return;
|
|
13777
|
-
if (compareSemver(opts.currentVersion, minVersion) >= 0)
|
|
13778
|
-
return;
|
|
13779
|
-
throw new UpgradeRequiredError2({
|
|
13780
|
-
currentVersion: opts.currentVersion,
|
|
13781
|
-
minVersion,
|
|
13782
|
-
packageName: opts.packageName
|
|
13783
|
-
});
|
|
13784
|
-
}
|
|
13785
|
-
function readResponseLatestVersion(response) {
|
|
13786
|
-
const latest = response.headers.get(exports2.LATEST_VERSION_HEADER);
|
|
13787
|
-
return latest && latest.trim().length > 0 ? latest.trim() : null;
|
|
13788
|
-
}
|
|
13789
|
-
}
|
|
13790
|
-
});
|
|
13791
|
-
|
|
13792
13852
|
// ../../packages/shuttle/dist/branding/environment.js
|
|
13793
13853
|
var require_environment = __commonJS({
|
|
13794
13854
|
"../../packages/shuttle/dist/branding/environment.js"(exports2) {
|
|
@@ -22203,8 +22263,20 @@ var require_client2 = __commonJS({
|
|
|
22203
22263
|
* callers should be rare. Returns the dispatched-job context the
|
|
22204
22264
|
* subscriber needs to execute and submit.
|
|
22205
22265
|
*/
|
|
22206
|
-
async acceptDispatch(jobId) {
|
|
22207
|
-
return this.request("POST", `/api/jobs/${jobId}/attendance
|
|
22266
|
+
async acceptDispatch(jobId, options) {
|
|
22267
|
+
return this.request("POST", `/api/jobs/${jobId}/attendance`, options?.shiftId ? { shiftId: options.shiftId } : void 0);
|
|
22268
|
+
}
|
|
22269
|
+
// ── Shifts (whittlelabs ADR 0129) ────────────────────────────────
|
|
22270
|
+
/**
|
|
22271
|
+
* Start a shift for this process to claim on. A Jobs that predates shifts
|
|
22272
|
+
* answers 404; the subscribe loop then claims without one.
|
|
22273
|
+
*/
|
|
22274
|
+
async startShift() {
|
|
22275
|
+
return this.request("POST", "/api/my/shifts");
|
|
22276
|
+
}
|
|
22277
|
+
/** End a shift, releasing every claim still taken on it back to the queue. */
|
|
22278
|
+
async endShift(shiftId) {
|
|
22279
|
+
await this.request("DELETE", `/api/my/shifts/${shiftId}`);
|
|
22208
22280
|
}
|
|
22209
22281
|
async submit(jobId, options) {
|
|
22210
22282
|
return this.request("PUT", `/api/jobs/${jobId}/attendance`, options);
|
|
@@ -22226,12 +22298,15 @@ var require_client2 = __commonJS({
|
|
|
22226
22298
|
params.set("offset", String(options.offset));
|
|
22227
22299
|
if (options?.sort)
|
|
22228
22300
|
params.set("sort", options.sort);
|
|
22301
|
+
if (options?.shift)
|
|
22302
|
+
params.set("shift", options.shift);
|
|
22229
22303
|
const qs = params.toString();
|
|
22230
|
-
const { jobs, total, directives } = await this.request("GET", `/api/my/jobs${qs ? `?${qs}` : ""}`);
|
|
22304
|
+
const { jobs, total, directives, shift } = await this.request("GET", `/api/my/jobs${qs ? `?${qs}` : ""}`);
|
|
22231
22305
|
return {
|
|
22232
22306
|
data: jobs,
|
|
22233
22307
|
pagination: { total, limit: options?.limit ?? 25, offset: options?.offset ?? 0 },
|
|
22234
|
-
directives: directives ?? null
|
|
22308
|
+
directives: directives ?? null,
|
|
22309
|
+
shift: shift ?? null
|
|
22235
22310
|
};
|
|
22236
22311
|
}
|
|
22237
22312
|
// ── HTTP plumbing ────────────────────────────────────────────────
|
|
@@ -22322,6 +22397,17 @@ var require_subscribe = __commonJS({
|
|
|
22322
22397
|
const rest = seconds % 60;
|
|
22323
22398
|
return rest === 0 ? `${minutes}m` : `${minutes}m ${rest}s`;
|
|
22324
22399
|
}
|
|
22400
|
+
function statusOf(err) {
|
|
22401
|
+
const code = err?.statusCode;
|
|
22402
|
+
return typeof code === "number" ? code : void 0;
|
|
22403
|
+
}
|
|
22404
|
+
var HandBack = class {
|
|
22405
|
+
reason;
|
|
22406
|
+
constructor(reason) {
|
|
22407
|
+
this.reason = reason;
|
|
22408
|
+
}
|
|
22409
|
+
};
|
|
22410
|
+
var RELEASED = Symbol("released");
|
|
22325
22411
|
function newOutcome() {
|
|
22326
22412
|
let resolve;
|
|
22327
22413
|
let reject;
|
|
@@ -22362,7 +22448,10 @@ var require_subscribe = __commonJS({
|
|
|
22362
22448
|
let pollTimer = null;
|
|
22363
22449
|
let inflight = 0;
|
|
22364
22450
|
let resolvedPoolIds = [];
|
|
22365
|
-
const controllers = /* @__PURE__ */ new
|
|
22451
|
+
const controllers = /* @__PURE__ */ new Map();
|
|
22452
|
+
let shift = null;
|
|
22453
|
+
let shiftsSupported = true;
|
|
22454
|
+
let draining = null;
|
|
22366
22455
|
const holds = /* @__PURE__ */ new Set();
|
|
22367
22456
|
let fatalError = null;
|
|
22368
22457
|
let outcome = newOutcome();
|
|
@@ -22491,13 +22580,74 @@ var require_subscribe = __commonJS({
|
|
|
22491
22580
|
}
|
|
22492
22581
|
if (stopped || mySession !== session)
|
|
22493
22582
|
return;
|
|
22494
|
-
|
|
22583
|
+
try {
|
|
22584
|
+
const opened = await untilAvailable(() => startShift(), mySession);
|
|
22585
|
+
if (opened === STOPPED)
|
|
22586
|
+
return;
|
|
22587
|
+
} catch (err) {
|
|
22588
|
+
if (mySession === session) {
|
|
22589
|
+
stopped = true;
|
|
22590
|
+
outcome.reject(err);
|
|
22591
|
+
}
|
|
22592
|
+
throw err;
|
|
22593
|
+
}
|
|
22594
|
+
if (stopped || mySession !== session)
|
|
22595
|
+
return;
|
|
22596
|
+
logger.info("subscribe loop started", {
|
|
22597
|
+
pools: resolvedPoolIds,
|
|
22598
|
+
maxConcurrency,
|
|
22599
|
+
...shift ? { shiftId: shift.id } : {}
|
|
22600
|
+
});
|
|
22495
22601
|
schedulePoll(pollIntervalMs);
|
|
22496
22602
|
}
|
|
22603
|
+
async function startShift() {
|
|
22604
|
+
if (!shiftsSupported)
|
|
22605
|
+
return;
|
|
22606
|
+
try {
|
|
22607
|
+
shift = await opts.jobsClient.startShift();
|
|
22608
|
+
} catch (err) {
|
|
22609
|
+
if (isFatal(err) || isUnavailable(err) || isRateLimited(err))
|
|
22610
|
+
throw err;
|
|
22611
|
+
if (statusOf(err) === 404) {
|
|
22612
|
+
shiftsSupported = false;
|
|
22613
|
+
logger.debug?.("Jobs predates shifts; claims are held until each job's deadline");
|
|
22614
|
+
return;
|
|
22615
|
+
}
|
|
22616
|
+
logger.warn(`Could not start a shift (${describeOutage(err)}). Claims taken meanwhile are held until each job's deadline; the next poll tries again.`);
|
|
22617
|
+
}
|
|
22618
|
+
}
|
|
22619
|
+
async function endShift() {
|
|
22620
|
+
const ending = shift;
|
|
22621
|
+
shift = null;
|
|
22622
|
+
if (!ending)
|
|
22623
|
+
return;
|
|
22624
|
+
try {
|
|
22625
|
+
await opts.jobsClient.endShift(ending.id);
|
|
22626
|
+
} catch (err) {
|
|
22627
|
+
logger.debug?.("could not end the shift; it will lapse", {
|
|
22628
|
+
shiftId: ending.id,
|
|
22629
|
+
error: err instanceof Error ? err.message : String(err)
|
|
22630
|
+
});
|
|
22631
|
+
}
|
|
22632
|
+
}
|
|
22633
|
+
async function shiftReleased(ended) {
|
|
22634
|
+
const mine = [...controllers].filter(([, held]) => held.shiftId === ended.id);
|
|
22635
|
+
logger.warn(`Jobs ended this Sifter's shift (${ended.endReason ?? "ended"}) and released its claims` + (mine.length > 0 ? `; ${mine.length} job(s) running on it were handed back to the queue and stop here` : "") + ". Starting a new shift.", { shiftId: ended.id });
|
|
22636
|
+
for (const [controller] of mine)
|
|
22637
|
+
controller.abort(RELEASED);
|
|
22638
|
+
shift = null;
|
|
22639
|
+
try {
|
|
22640
|
+
await startShift();
|
|
22641
|
+
} catch (err) {
|
|
22642
|
+
if (isFatal(err))
|
|
22643
|
+
throw err;
|
|
22644
|
+
}
|
|
22645
|
+
}
|
|
22497
22646
|
async function finish() {
|
|
22498
22647
|
while (inflight > 0) {
|
|
22499
22648
|
await sleep(100);
|
|
22500
22649
|
}
|
|
22650
|
+
await endShift();
|
|
22501
22651
|
logger.info("subscribe loop stopped");
|
|
22502
22652
|
if (fatalError !== null)
|
|
22503
22653
|
outcome.reject(fatalError.error);
|
|
@@ -22512,45 +22662,61 @@ var require_subscribe = __commonJS({
|
|
|
22512
22662
|
}
|
|
22513
22663
|
wakeStartup?.();
|
|
22514
22664
|
}
|
|
22515
|
-
async function stop() {
|
|
22665
|
+
async function stop(options = {}) {
|
|
22666
|
+
if (options.handBack !== void 0) {
|
|
22667
|
+
for (const [controller] of controllers)
|
|
22668
|
+
controller.abort(new HandBack(options.handBack));
|
|
22669
|
+
}
|
|
22670
|
+
if (draining)
|
|
22671
|
+
return draining;
|
|
22516
22672
|
if (stopped)
|
|
22517
22673
|
return;
|
|
22518
22674
|
halt();
|
|
22519
|
-
|
|
22675
|
+
draining = finish().finally(() => {
|
|
22676
|
+
draining = null;
|
|
22677
|
+
});
|
|
22678
|
+
return draining;
|
|
22520
22679
|
}
|
|
22521
22680
|
async function fail(err) {
|
|
22522
22681
|
if (fatalError === null)
|
|
22523
22682
|
fatalError = { error: err };
|
|
22524
|
-
for (const controller of controllers)
|
|
22683
|
+
for (const [controller] of controllers)
|
|
22525
22684
|
controller.abort(err);
|
|
22685
|
+
if (draining)
|
|
22686
|
+
return draining;
|
|
22526
22687
|
if (stopped)
|
|
22527
22688
|
return;
|
|
22528
22689
|
halt();
|
|
22529
|
-
|
|
22690
|
+
draining = finish().finally(() => {
|
|
22691
|
+
draining = null;
|
|
22692
|
+
});
|
|
22693
|
+
return draining;
|
|
22530
22694
|
}
|
|
22531
22695
|
function schedulePoll(delayMs) {
|
|
22532
22696
|
const mySession = session;
|
|
22697
|
+
const checkInMs = shift ? shift.lapseAfterSeconds * 1e3 / 4 : Infinity;
|
|
22533
22698
|
pollTimer = setTimeout(() => {
|
|
22534
22699
|
void poll(mySession);
|
|
22535
|
-
}, delayMs);
|
|
22700
|
+
}, Math.min(delayMs, checkInMs));
|
|
22536
22701
|
}
|
|
22537
22702
|
async function poll(mySession) {
|
|
22538
22703
|
if (stopped || mySession !== session)
|
|
22539
22704
|
return;
|
|
22540
22705
|
let nextPollMs = pollIntervalMs;
|
|
22541
|
-
if (inflight >= maxConcurrency) {
|
|
22542
|
-
schedulePoll(pollIntervalMs);
|
|
22543
|
-
return;
|
|
22544
|
-
}
|
|
22545
22706
|
try {
|
|
22546
22707
|
const paused = holds.size > 0;
|
|
22547
22708
|
const free = maxConcurrency - inflight;
|
|
22548
|
-
|
|
22709
|
+
if (shift === null && shiftsSupported)
|
|
22710
|
+
await startShift();
|
|
22711
|
+
const { data, directives, shift: standing } = await opts.jobsClient.listDispatchableJobs({
|
|
22549
22712
|
pools: resolvedPoolIds,
|
|
22550
|
-
limit: paused ? 1 : accepts ? Math.max(free, DECLINE_WINDOW) : free,
|
|
22551
|
-
sort: "priority:desc,queued_at:asc"
|
|
22713
|
+
limit: paused || free <= 0 ? 1 : accepts ? Math.max(free, DECLINE_WINDOW) : free,
|
|
22714
|
+
sort: "priority:desc,queued_at:asc",
|
|
22715
|
+
...shift ? { shift: shift.id } : {}
|
|
22552
22716
|
});
|
|
22553
22717
|
noteReachable();
|
|
22718
|
+
if (shift && standing?.id === shift.id && standing.endedAt !== null)
|
|
22719
|
+
await shiftReleased(standing);
|
|
22554
22720
|
if (directives && Object.keys(directives).length > 0 && opts.onDirectives) {
|
|
22555
22721
|
try {
|
|
22556
22722
|
await opts.onDirectives(directives);
|
|
@@ -22598,8 +22764,9 @@ var require_subscribe = __commonJS({
|
|
|
22598
22764
|
}
|
|
22599
22765
|
async function runOnce(available) {
|
|
22600
22766
|
let dispatch;
|
|
22767
|
+
const shiftId = shift?.id ?? null;
|
|
22601
22768
|
try {
|
|
22602
|
-
dispatch = await opts.jobsClient.acceptDispatch(available.id);
|
|
22769
|
+
dispatch = await opts.jobsClient.acceptDispatch(available.id, shiftId ? { shiftId } : void 0);
|
|
22603
22770
|
} catch (err) {
|
|
22604
22771
|
if (isFatal(err)) {
|
|
22605
22772
|
void fail(err);
|
|
@@ -22616,8 +22783,13 @@ var require_subscribe = __commonJS({
|
|
|
22616
22783
|
});
|
|
22617
22784
|
return;
|
|
22618
22785
|
}
|
|
22786
|
+
logger.info(`Claimed job ${dispatch.job.id} (attempt ${dispatch.job.attempt})`, {
|
|
22787
|
+
jobId: dispatch.job.id,
|
|
22788
|
+
pool: dispatch.pool.name,
|
|
22789
|
+
...shiftId ? { shiftId } : {}
|
|
22790
|
+
});
|
|
22619
22791
|
const controller = new AbortController();
|
|
22620
|
-
controllers.
|
|
22792
|
+
controllers.set(controller, { shiftId });
|
|
22621
22793
|
if (fatalError !== null)
|
|
22622
22794
|
controller.abort(fatalError.error);
|
|
22623
22795
|
let quietTimer = null;
|
|
@@ -22657,9 +22829,20 @@ var require_subscribe = __commonJS({
|
|
|
22657
22829
|
controllers.delete(controller);
|
|
22658
22830
|
}
|
|
22659
22831
|
if (fatalError !== null) {
|
|
22660
|
-
logger.
|
|
22832
|
+
logger.error(shiftId ? `Dropping job ${dispatch.job.id}'s result after a fatal error. Its claim goes back to the queue when this Sifter's shift lapses.` : `Dropping job ${dispatch.job.id}'s result after a fatal error. Its claim is held until the job's deadline.`, { jobId: dispatch.job.id, status: result.status });
|
|
22833
|
+
return;
|
|
22834
|
+
}
|
|
22835
|
+
if (controller.signal.reason === RELEASED) {
|
|
22836
|
+
logger.warn(`Job ${dispatch.job.id} was handed back to the queue while it ran; its result is not submitted.`, {
|
|
22837
|
+
jobId: dispatch.job.id
|
|
22838
|
+
});
|
|
22661
22839
|
return;
|
|
22662
22840
|
}
|
|
22841
|
+
const handBack = controller.signal.reason instanceof HandBack ? controller.signal.reason.reason : null;
|
|
22842
|
+
if (handBack !== null && result.status !== "completed") {
|
|
22843
|
+
result = { status: "abandoned", reason: handBack };
|
|
22844
|
+
logger.warn(`Handing job ${dispatch.job.id} back to the queue: ${handBack}`, { jobId: dispatch.job.id });
|
|
22845
|
+
}
|
|
22663
22846
|
try {
|
|
22664
22847
|
for (const output of result.outputs ?? []) {
|
|
22665
22848
|
await opts.jobsClient.createOutput(dispatch.job.id, {
|
|
@@ -22681,7 +22864,12 @@ var require_subscribe = __commonJS({
|
|
|
22681
22864
|
jobId: dispatch.job.id,
|
|
22682
22865
|
error: err instanceof Error ? err.message : String(err)
|
|
22683
22866
|
});
|
|
22867
|
+
return;
|
|
22684
22868
|
}
|
|
22869
|
+
logger.info(`Submitted job ${dispatch.job.id} as ${result.status}`, {
|
|
22870
|
+
jobId: dispatch.job.id,
|
|
22871
|
+
...result.reason !== void 0 ? { reason: result.reason } : {}
|
|
22872
|
+
});
|
|
22685
22873
|
}
|
|
22686
22874
|
return {
|
|
22687
22875
|
start,
|
|
@@ -26322,9 +26510,12 @@ var require_logger = __commonJS({
|
|
|
26322
26510
|
"../../packages/shuttle/dist/logging/logger.js"(exports2) {
|
|
26323
26511
|
"use strict";
|
|
26324
26512
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
26513
|
+
exports2.LOG_FILE_MAX_BYTES = void 0;
|
|
26325
26514
|
exports2.createLogger = createLogger;
|
|
26326
26515
|
exports2.setGlobalLogger = setGlobalLogger;
|
|
26327
26516
|
exports2.getLogger = getLogger;
|
|
26517
|
+
var node_fs_1 = require("node:fs");
|
|
26518
|
+
var node_path_1 = require("node:path");
|
|
26328
26519
|
var LEVEL_ORDER = {
|
|
26329
26520
|
debug: 0,
|
|
26330
26521
|
info: 1,
|
|
@@ -26352,13 +26543,42 @@ var require_logger = __commonJS({
|
|
|
26352
26543
|
};
|
|
26353
26544
|
return JSON.stringify(entry);
|
|
26354
26545
|
}
|
|
26546
|
+
exports2.LOG_FILE_MAX_BYTES = 5 * 1024 * 1024;
|
|
26547
|
+
function fileSink(path) {
|
|
26548
|
+
let size = 0;
|
|
26549
|
+
let broken = false;
|
|
26550
|
+
try {
|
|
26551
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
|
|
26552
|
+
size = (0, node_fs_1.statSync)(path).size;
|
|
26553
|
+
} catch {
|
|
26554
|
+
size = 0;
|
|
26555
|
+
}
|
|
26556
|
+
return (line) => {
|
|
26557
|
+
if (broken)
|
|
26558
|
+
return;
|
|
26559
|
+
try {
|
|
26560
|
+
if (size > exports2.LOG_FILE_MAX_BYTES) {
|
|
26561
|
+
(0, node_fs_1.renameSync)(path, `${path}.1`);
|
|
26562
|
+
size = 0;
|
|
26563
|
+
}
|
|
26564
|
+
const entry = `${line}
|
|
26565
|
+
`;
|
|
26566
|
+
(0, node_fs_1.appendFileSync)(path, entry, { mode: 384 });
|
|
26567
|
+
size += Buffer.byteLength(entry);
|
|
26568
|
+
} catch {
|
|
26569
|
+
broken = true;
|
|
26570
|
+
}
|
|
26571
|
+
};
|
|
26572
|
+
}
|
|
26355
26573
|
function createLogger(options) {
|
|
26356
26574
|
const threshold = LEVEL_ORDER[options.level];
|
|
26357
26575
|
const format = options.format === "json" ? formatJson : formatPretty;
|
|
26576
|
+
const toFile = options.file ? fileSink(options.file) : null;
|
|
26358
26577
|
function emit(level, message, context) {
|
|
26359
26578
|
if (LEVEL_ORDER[level] < threshold)
|
|
26360
26579
|
return;
|
|
26361
26580
|
const line = format(level, message, context);
|
|
26581
|
+
toFile?.(line);
|
|
26362
26582
|
if (level === "error") {
|
|
26363
26583
|
console.error(line);
|
|
26364
26584
|
} else if (level === "warn") {
|
|
@@ -27654,6 +27874,7 @@ var require_shuttle = __commonJS({
|
|
|
27654
27874
|
exports2.mergeExecutorDefaults = mergeExecutorDefaults;
|
|
27655
27875
|
exports2.resolveAuth = resolveAuth;
|
|
27656
27876
|
var os_1 = require("os");
|
|
27877
|
+
var path_1 = require("path");
|
|
27657
27878
|
var jobs_1 = require_dist4();
|
|
27658
27879
|
var self_update_1 = require_self_update();
|
|
27659
27880
|
var decommission_1 = require_decommission();
|
|
@@ -27794,13 +28015,18 @@ var require_shuttle = __commonJS({
|
|
|
27794
28015
|
return this.spendTracker;
|
|
27795
28016
|
}
|
|
27796
28017
|
async start() {
|
|
28018
|
+
const paths = (0, apply_1.resolvePaths)(this.brand, this.profile);
|
|
28019
|
+
const logFile = this.config.credentials === void 0 ? (0, path_1.join)(paths.configDir, `${this.brand.product.id}.log`) : void 0;
|
|
27797
28020
|
(0, logger_1.setGlobalLogger)((0, logger_1.createLogger)({
|
|
27798
28021
|
level: this.config.logging.level,
|
|
27799
|
-
format: this.config.logging.format
|
|
28022
|
+
format: this.config.logging.format,
|
|
28023
|
+
...logFile ? { file: logFile } : {}
|
|
27800
28024
|
}));
|
|
27801
28025
|
const logger = (0, logger_1.getLogger)();
|
|
27802
|
-
logger.info(`${this.brand.product.title} starting
|
|
27803
|
-
|
|
28026
|
+
logger.info(`${this.brand.product.title} starting...`, {
|
|
28027
|
+
version: this.brand.product.version,
|
|
28028
|
+
...logFile ? { log: logFile } : {}
|
|
28029
|
+
});
|
|
27804
28030
|
this.broker = (0, credentials_1.createCredentialBroker)(this.config.credentials, this.credentialDeps);
|
|
27805
28031
|
const subscribeLogger = {
|
|
27806
28032
|
debug: (msg, meta) => logger.debug(msg, meta),
|
|
@@ -27938,7 +28164,7 @@ var require_shuttle = __commonJS({
|
|
|
27938
28164
|
const runOn = async (executor) => {
|
|
27939
28165
|
await observerChain.notify({ kind: "executor.dispatch.started", jobId, attendanceId, executor: executor.capability.id, strategy }, onObserverError);
|
|
27940
28166
|
const dispatchStartedAt = Date.now();
|
|
27941
|
-
const
|
|
28167
|
+
const ran = await (0, run_dispatch_1.runDispatch)(dispatch, executor, {
|
|
27942
28168
|
credentials: this.broker,
|
|
27943
28169
|
signal,
|
|
27944
28170
|
reportProgress: (channel, frames) => {
|
|
@@ -27946,6 +28172,8 @@ var require_shuttle = __commonJS({
|
|
|
27946
28172
|
});
|
|
27947
28173
|
}
|
|
27948
28174
|
});
|
|
28175
|
+
subscribeLogger.info(`Job ${jobId} ran on ${executor.capability.id} for ${formatDuration(Date.now() - dispatchStartedAt)}: ` + (ran.kind === "completed" ? "finished" : ran.kind === "gate-failed" ? "its output failed a gate" : "the executor failed"), { jobId, ...signal.aborted ? { aborted: true } : {} });
|
|
28176
|
+
const outcome = ran;
|
|
27949
28177
|
if (outcome.kind === "executor-failed") {
|
|
27950
28178
|
const err = outcome.error;
|
|
27951
28179
|
const reason = `executor failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
@@ -28105,14 +28333,21 @@ var require_shuttle = __commonJS({
|
|
|
28105
28333
|
logger.info(`${this.brand.product.title} stopping...`);
|
|
28106
28334
|
this.stopping.abort();
|
|
28107
28335
|
if (this.subscription) {
|
|
28108
|
-
await this.subscription.stop(
|
|
28336
|
+
await this.subscription.stop({
|
|
28337
|
+
handBack: `The ${this.brand.product.title} was stopped before it finished this job`
|
|
28338
|
+
});
|
|
28109
28339
|
}
|
|
28110
28340
|
logger.info(`${this.brand.product.title} stopped`);
|
|
28111
28341
|
this.lifetime.resolve();
|
|
28112
28342
|
}
|
|
28113
28343
|
/** Forward the current subscribe session's fatal, if one ends it, to the run's outcome. */
|
|
28114
28344
|
watchSession() {
|
|
28115
|
-
this.subscription?.done().catch((err) =>
|
|
28345
|
+
this.subscription?.done().catch((err) => {
|
|
28346
|
+
(0, logger_1.getLogger)().error(`${this.brand.product.title} stopped on a fatal error`, {
|
|
28347
|
+
error: err instanceof Error ? `${err.name}: ${err.message}` : String(err)
|
|
28348
|
+
});
|
|
28349
|
+
this.lifetime.reject(err);
|
|
28350
|
+
});
|
|
28116
28351
|
}
|
|
28117
28352
|
/**
|
|
28118
28353
|
* Note that an executor cannot run jobs on this machine, and keep checking
|
|
@@ -28276,29 +28511,49 @@ var require_shutdown = __commonJS({
|
|
|
28276
28511
|
"../../packages/shuttle/dist/lifecycle/shutdown.js"(exports2) {
|
|
28277
28512
|
"use strict";
|
|
28278
28513
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
28514
|
+
exports2.SHUTDOWN_DEADLINE_MS = void 0;
|
|
28279
28515
|
exports2.setupShutdownHandler = setupShutdownHandler;
|
|
28516
|
+
exports2.createShutdownHandler = createShutdownHandler;
|
|
28280
28517
|
var logger_1 = require_logger();
|
|
28518
|
+
exports2.SHUTDOWN_DEADLINE_MS = 2e4;
|
|
28281
28519
|
function setupShutdownHandler(onShutdown) {
|
|
28520
|
+
const handler = createShutdownHandler(onShutdown);
|
|
28521
|
+
process.on("SIGINT", () => handler("SIGINT"));
|
|
28522
|
+
process.on("SIGTERM", () => handler("SIGTERM"));
|
|
28523
|
+
process.on("SIGHUP", () => handler("SIGHUP"));
|
|
28524
|
+
}
|
|
28525
|
+
function createShutdownHandler(onShutdown, options = {}) {
|
|
28526
|
+
const deadlineMs = options.deadlineMs ?? exports2.SHUTDOWN_DEADLINE_MS;
|
|
28527
|
+
const exit = options.exit ?? ((code) => process.exit(code));
|
|
28282
28528
|
let shuttingDown = false;
|
|
28283
28529
|
const handler = (signal) => {
|
|
28284
28530
|
const logger = (0, logger_1.getLogger)();
|
|
28285
28531
|
if (shuttingDown) {
|
|
28286
|
-
logger.warn("Forced shutdown", {
|
|
28287
|
-
|
|
28532
|
+
logger.warn("Forced shutdown: exiting now. Jobs releases any claim still held once this shift lapses.", {
|
|
28533
|
+
signal
|
|
28534
|
+
});
|
|
28535
|
+
exit(1);
|
|
28536
|
+
return;
|
|
28288
28537
|
}
|
|
28289
28538
|
shuttingDown = true;
|
|
28290
|
-
logger.info("Shutting down...", { signal });
|
|
28539
|
+
logger.info("Shutting down: handing any job in progress back to the queue...", { signal });
|
|
28540
|
+
const deadline = setTimeout(() => {
|
|
28541
|
+
logger.warn(`Shutdown did not finish within ${Math.round(deadlineMs / 1e3)}s; exiting. Jobs releases any claim still held once this shift lapses.`);
|
|
28542
|
+
exit(1);
|
|
28543
|
+
}, deadlineMs);
|
|
28544
|
+
deadline.unref?.();
|
|
28291
28545
|
onShutdown().then(() => {
|
|
28292
|
-
|
|
28546
|
+
clearTimeout(deadline);
|
|
28547
|
+
exit(0);
|
|
28293
28548
|
}).catch((err) => {
|
|
28549
|
+
clearTimeout(deadline);
|
|
28294
28550
|
logger.error("Shutdown error", {
|
|
28295
28551
|
error: err instanceof Error ? err.message : String(err)
|
|
28296
28552
|
});
|
|
28297
|
-
|
|
28553
|
+
exit(1);
|
|
28298
28554
|
});
|
|
28299
28555
|
};
|
|
28300
|
-
|
|
28301
|
-
process.on("SIGTERM", () => handler("SIGTERM"));
|
|
28556
|
+
return handler;
|
|
28302
28557
|
}
|
|
28303
28558
|
}
|
|
28304
28559
|
});
|
|
@@ -29132,7 +29387,7 @@ var import_path = require("path");
|
|
|
29132
29387
|
var import_promises = require("fs/promises");
|
|
29133
29388
|
var import_yaml = __toESM(require_dist3());
|
|
29134
29389
|
var import_shuttle = __toESM(require_dist5());
|
|
29135
|
-
var buildVersion = true ? "0.
|
|
29390
|
+
var buildVersion = true ? "0.37.0" : pkg.version;
|
|
29136
29391
|
var AGENT_EXECUTORS = ["claude-code", "codex", "copilot"];
|
|
29137
29392
|
var sifterBrand = {
|
|
29138
29393
|
product: {
|