@whittlelabs/sifter 0.16.0 → 0.18.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/bin.js +325 -55
- package/bin.js.map +4 -4
- package/package.json +1 -1
package/bin.js
CHANGED
|
@@ -3180,6 +3180,14 @@ var require_device_code = __commonJS({
|
|
|
3180
3180
|
});
|
|
3181
3181
|
log(opts.brand.strings?.pairingDoneMessage ?? `Paired ${displayName}.`);
|
|
3182
3182
|
}
|
|
3183
|
+
const jobPools = await discoverMemberPools({
|
|
3184
|
+
keepApiUrl: reg.keepApiUrl,
|
|
3185
|
+
jobsApiUrl: reg.jobsApiUrl,
|
|
3186
|
+
clientId: token.clientId,
|
|
3187
|
+
clientSecret: token.clientSecret,
|
|
3188
|
+
fetcher,
|
|
3189
|
+
brand: opts.brand
|
|
3190
|
+
});
|
|
3183
3191
|
return {
|
|
3184
3192
|
schemaVersion: 1,
|
|
3185
3193
|
keepApiUrl: reg.keepApiUrl,
|
|
@@ -3195,9 +3203,38 @@ var require_device_code = __commonJS({
|
|
|
3195
3203
|
hostname: (0, os_1.hostname)(),
|
|
3196
3204
|
user: (0, os_1.userInfo)().username
|
|
3197
3205
|
},
|
|
3198
|
-
jobPools
|
|
3206
|
+
jobPools
|
|
3199
3207
|
};
|
|
3200
3208
|
}
|
|
3209
|
+
async function discoverMemberPools(opts) {
|
|
3210
|
+
const tokenUrl = `${opts.keepApiUrl.replace(/\/$/, "")}/api/service-tokens`;
|
|
3211
|
+
const tokenRes = await opts.fetcher(tokenUrl, {
|
|
3212
|
+
method: "POST",
|
|
3213
|
+
headers: { "Content-Type": "application/json" },
|
|
3214
|
+
body: JSON.stringify({
|
|
3215
|
+
clientId: opts.clientId,
|
|
3216
|
+
clientSecret: opts.clientSecret
|
|
3217
|
+
})
|
|
3218
|
+
});
|
|
3219
|
+
(0, version_check_1.checkResponseMinVersion)({
|
|
3220
|
+
response: tokenRes,
|
|
3221
|
+
currentVersion: opts.brand.product.version,
|
|
3222
|
+
packageName: opts.brand.product.packageName
|
|
3223
|
+
});
|
|
3224
|
+
const tokenJson = await readJson(tokenRes);
|
|
3225
|
+
const poolsUrl = `${opts.jobsApiUrl.replace(/\/$/, "")}/api/my/pools`;
|
|
3226
|
+
const poolsRes = await opts.fetcher(poolsUrl, {
|
|
3227
|
+
method: "GET",
|
|
3228
|
+
headers: { Authorization: `Bearer ${tokenJson.data.token}` }
|
|
3229
|
+
});
|
|
3230
|
+
(0, version_check_1.checkResponseMinVersion)({
|
|
3231
|
+
response: poolsRes,
|
|
3232
|
+
currentVersion: opts.brand.product.version,
|
|
3233
|
+
packageName: opts.brand.product.packageName
|
|
3234
|
+
});
|
|
3235
|
+
const poolsJson = await readJson(poolsRes);
|
|
3236
|
+
return poolsJson.data.pools;
|
|
3237
|
+
}
|
|
3201
3238
|
async function startDeviceCode(opts) {
|
|
3202
3239
|
const url = `${opts.keepApiUrl.replace(/\/$/, "")}/api/oauth/device`;
|
|
3203
3240
|
const response = await opts.fetcher(url, {
|
|
@@ -15803,6 +15840,17 @@ var require_prompt_execution = __commonJS({
|
|
|
15803
15840
|
}
|
|
15804
15841
|
}
|
|
15805
15842
|
}
|
|
15843
|
+
if (spec.gates !== void 0) {
|
|
15844
|
+
if (!Array.isArray(spec.gates)) {
|
|
15845
|
+
throw new Error("prompt-execution: spec.gates must be an array when present");
|
|
15846
|
+
}
|
|
15847
|
+
for (const gate of spec.gates) {
|
|
15848
|
+
const g = gate;
|
|
15849
|
+
if (typeof gate !== "object" || gate === null || typeof g.name !== "string" || typeof g.primitive !== "string" || typeof g.params !== "object" || g.params === null) {
|
|
15850
|
+
throw new Error("prompt-execution: each spec.gates entry must be { name, primitive, params }");
|
|
15851
|
+
}
|
|
15852
|
+
}
|
|
15853
|
+
}
|
|
15806
15854
|
return spec;
|
|
15807
15855
|
}
|
|
15808
15856
|
function renderPromptExecution(inputs) {
|
|
@@ -16022,6 +16070,8 @@ var require_prompt_execution2 = __commonJS({
|
|
|
16022
16070
|
spec.costCapHint = options.costCapHint;
|
|
16023
16071
|
if (options.progressChannel !== void 0)
|
|
16024
16072
|
spec.progressChannel = options.progressChannel;
|
|
16073
|
+
if (options.outputGates && options.outputGates.length > 0)
|
|
16074
|
+
spec.gates = options.outputGates;
|
|
16025
16075
|
if (options.rendering === "producer" && !options.prompt) {
|
|
16026
16076
|
throw new Error("rendering='producer' requires `prompt`");
|
|
16027
16077
|
}
|
|
@@ -18051,7 +18101,8 @@ var require_canonicalise = __commonJS({
|
|
|
18051
18101
|
return {
|
|
18052
18102
|
name: workflow.name,
|
|
18053
18103
|
description: workflow.description ?? null,
|
|
18054
|
-
|
|
18104
|
+
ownerClass: opts.ownerClass,
|
|
18105
|
+
ownerId: opts.ownerId,
|
|
18055
18106
|
retainPublishedVersions: workflow.retain?.publishedVersions ?? null,
|
|
18056
18107
|
...opts.publish !== void 0 ? { publish: opts.publish } : {},
|
|
18057
18108
|
nodes,
|
|
@@ -18081,6 +18132,7 @@ var require_canonicalise = __commonJS({
|
|
|
18081
18132
|
promptTemplate: templateText,
|
|
18082
18133
|
promptTemplatePath: promptOptions.promptTemplate,
|
|
18083
18134
|
outputSchema: toJsonSchema(promptOptions.outputSchema),
|
|
18135
|
+
...promptOptions.outputGates ? { outputGates: promptOptions.outputGates } : {},
|
|
18084
18136
|
outputLabel,
|
|
18085
18137
|
rendering: promptOptions.rendering,
|
|
18086
18138
|
...promptOptions.maxAttempts !== void 0 ? { maxAttempts: promptOptions.maxAttempts } : {},
|
|
@@ -18212,18 +18264,28 @@ var require_seed_workflows = __commonJS({
|
|
|
18212
18264
|
}
|
|
18213
18265
|
const results = [];
|
|
18214
18266
|
for (const workflow of opts.workflows) {
|
|
18215
|
-
|
|
18216
|
-
|
|
18217
|
-
|
|
18267
|
+
let ownerClass;
|
|
18268
|
+
let ownerId;
|
|
18269
|
+
if ("service" in workflow.owner) {
|
|
18270
|
+
ownerClass = "service";
|
|
18271
|
+
ownerId = workflow.owner.service;
|
|
18272
|
+
} else {
|
|
18273
|
+
const orgId = opts.ownerSlugToOrgId[workflow.owner.orgSlug];
|
|
18274
|
+
if (orgId === void 0) {
|
|
18275
|
+
throw new errors_1.SeedWorkflowsError(workflow.name, `seedWorkflows: owner.orgSlug "${workflow.owner.orgSlug}" not present in ownerSlugToOrgId map`);
|
|
18276
|
+
}
|
|
18277
|
+
ownerClass = "org";
|
|
18278
|
+
ownerId = orgId;
|
|
18218
18279
|
}
|
|
18219
|
-
const opts2 = {
|
|
18280
|
+
const opts2 = { ownerClass, ownerId };
|
|
18220
18281
|
if (opts.env !== void 0)
|
|
18221
18282
|
opts2.env = opts.env;
|
|
18222
18283
|
if (opts.publish !== void 0)
|
|
18223
18284
|
opts2.publish = opts.publish;
|
|
18224
18285
|
const body = await (0, canonicalise_1.canonicaliseWorkflow)(workflow, opts2);
|
|
18225
18286
|
logger.info(`seeding workflow ${workflow.name}`, {
|
|
18226
|
-
|
|
18287
|
+
ownerClass,
|
|
18288
|
+
ownerId,
|
|
18227
18289
|
nodes: body.nodes.length,
|
|
18228
18290
|
transitions: body.transitions.length
|
|
18229
18291
|
});
|
|
@@ -18363,6 +18425,128 @@ var require_chain = __commonJS({
|
|
|
18363
18425
|
}
|
|
18364
18426
|
});
|
|
18365
18427
|
|
|
18428
|
+
// ../../packages/shuttle/dist/executors/output-gates.js
|
|
18429
|
+
var require_output_gates = __commonJS({
|
|
18430
|
+
"../../packages/shuttle/dist/executors/output-gates.js"(exports2) {
|
|
18431
|
+
"use strict";
|
|
18432
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
18433
|
+
exports2.resolvePath = resolvePath;
|
|
18434
|
+
exports2.evaluateOutputGates = evaluateOutputGates;
|
|
18435
|
+
exports2.renderRepairPrompt = renderRepairPrompt;
|
|
18436
|
+
var INPUT_REF = "input:";
|
|
18437
|
+
function resolvePath(root, path) {
|
|
18438
|
+
let ctx = root;
|
|
18439
|
+
for (const seg of path.split(".")) {
|
|
18440
|
+
const project = seg.endsWith("[*]");
|
|
18441
|
+
const key = project ? seg.slice(0, -3) : seg;
|
|
18442
|
+
if (Array.isArray(ctx)) {
|
|
18443
|
+
ctx = ctx.map((el) => key ? el?.[key] : el);
|
|
18444
|
+
} else {
|
|
18445
|
+
ctx = key ? ctx?.[key] : ctx;
|
|
18446
|
+
}
|
|
18447
|
+
if (project && !Array.isArray(ctx)) {
|
|
18448
|
+
ctx = ctx == null ? [] : [ctx];
|
|
18449
|
+
}
|
|
18450
|
+
}
|
|
18451
|
+
return ctx;
|
|
18452
|
+
}
|
|
18453
|
+
function resolveRef(ref, output, inputs) {
|
|
18454
|
+
if (ref.startsWith(INPUT_REF)) {
|
|
18455
|
+
const label = ref.slice(INPUT_REF.length);
|
|
18456
|
+
const row = inputs.find((i) => i.label === label) ?? inputs.find((i) => i.label === "artifacts" && i.payload.label === label);
|
|
18457
|
+
if (!row)
|
|
18458
|
+
return void 0;
|
|
18459
|
+
return Object.prototype.hasOwnProperty.call(row.payload, "value") ? row.payload.value : row.payload;
|
|
18460
|
+
}
|
|
18461
|
+
return resolvePath(output, ref);
|
|
18462
|
+
}
|
|
18463
|
+
function asStrings(value) {
|
|
18464
|
+
if (Array.isArray(value))
|
|
18465
|
+
return value.map((v) => String(v));
|
|
18466
|
+
if (value == null)
|
|
18467
|
+
return [];
|
|
18468
|
+
return [String(value)];
|
|
18469
|
+
}
|
|
18470
|
+
var PRIMITIVES = {
|
|
18471
|
+
/**
|
|
18472
|
+
* `coverage` — the values at `got` must exactly cover the set at `want`:
|
|
18473
|
+
* every wanted value present, nothing invented, none repeated. Params:
|
|
18474
|
+
* `{ got: <output path>, want: <ref>, subject?: <noun> }`.
|
|
18475
|
+
*/
|
|
18476
|
+
coverage(params, output, inputs) {
|
|
18477
|
+
const subject = typeof params.subject === "string" ? params.subject : "item";
|
|
18478
|
+
const got = asStrings(resolveRef(String(params.got), output, inputs));
|
|
18479
|
+
const want = asStrings(resolveRef(String(params.want), output, inputs));
|
|
18480
|
+
const gotSet = new Set(got);
|
|
18481
|
+
const wantSet = new Set(want);
|
|
18482
|
+
const missing = want.filter((v) => !gotSet.has(v));
|
|
18483
|
+
const unknown = [...new Set(got.filter((v) => !wantSet.has(v)))];
|
|
18484
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18485
|
+
const duplicated = [];
|
|
18486
|
+
for (const v of got) {
|
|
18487
|
+
if (seen.has(v))
|
|
18488
|
+
duplicated.push(v);
|
|
18489
|
+
else
|
|
18490
|
+
seen.add(v);
|
|
18491
|
+
}
|
|
18492
|
+
if (missing.length === 0 && unknown.length === 0 && duplicated.length === 0)
|
|
18493
|
+
return [];
|
|
18494
|
+
const parts = [];
|
|
18495
|
+
if (missing.length)
|
|
18496
|
+
parts.push(`missing ${subject}(s): [${missing.join(", ")}]`);
|
|
18497
|
+
if (unknown.length)
|
|
18498
|
+
parts.push(`not a known ${subject}: [${unknown.join(", ")}]`);
|
|
18499
|
+
if (duplicated.length)
|
|
18500
|
+
parts.push(`assessed more than once: [${[...new Set(duplicated)].join(", ")}]`);
|
|
18501
|
+
return [
|
|
18502
|
+
`every ${subject} must be covered exactly once \u2014 ${parts.join("; ")}. Return one entry per ${subject}, using its exact id.`
|
|
18503
|
+
];
|
|
18504
|
+
},
|
|
18505
|
+
/**
|
|
18506
|
+
* `non_empty_when` — the array at `path` must be non-empty, optionally only
|
|
18507
|
+
* when `when.path` equals `when.equals`. Params:
|
|
18508
|
+
* `{ path: <output path>, when?: { path: <output path>, equals: <value> } }`.
|
|
18509
|
+
*/
|
|
18510
|
+
non_empty_when(params, output) {
|
|
18511
|
+
const when = params.when;
|
|
18512
|
+
if (when && typeof when.path === "string") {
|
|
18513
|
+
if (resolvePath(output, when.path) !== when.equals)
|
|
18514
|
+
return [];
|
|
18515
|
+
}
|
|
18516
|
+
const path = String(params.path);
|
|
18517
|
+
const value = resolvePath(output, path);
|
|
18518
|
+
if (Array.isArray(value) && value.length > 0)
|
|
18519
|
+
return [];
|
|
18520
|
+
const cond = when && typeof when.path === "string" ? ` when ${when.path} is ${JSON.stringify(when.equals)}` : "";
|
|
18521
|
+
return [
|
|
18522
|
+
`'${path}' must be a non-empty array${cond}; it was ${Array.isArray(value) ? "empty" : "absent"}.`
|
|
18523
|
+
];
|
|
18524
|
+
}
|
|
18525
|
+
};
|
|
18526
|
+
function evaluateOutputGates(gates, output, inputs) {
|
|
18527
|
+
const findings = [];
|
|
18528
|
+
for (const gate of gates) {
|
|
18529
|
+
const primitive = PRIMITIVES[gate.primitive];
|
|
18530
|
+
if (!primitive) {
|
|
18531
|
+
findings.push({ gate: gate.name, message: `unknown gate primitive '${gate.primitive}'` });
|
|
18532
|
+
continue;
|
|
18533
|
+
}
|
|
18534
|
+
for (const message of primitive(gate.params, output, inputs)) {
|
|
18535
|
+
findings.push({ gate: gate.name, message });
|
|
18536
|
+
}
|
|
18537
|
+
}
|
|
18538
|
+
return { ok: findings.length === 0, findings };
|
|
18539
|
+
}
|
|
18540
|
+
function renderRepairPrompt(findings) {
|
|
18541
|
+
return [
|
|
18542
|
+
"Your previous answer did not satisfy these output checks. Return the corrected, complete answer that resolves every item below. Produce the same output shape with the problems fixed, and nothing else \u2014 no commentary, no explanation of the changes.",
|
|
18543
|
+
"",
|
|
18544
|
+
...findings.map((f) => `- [${f.gate}] ${f.message}`)
|
|
18545
|
+
].join("\n");
|
|
18546
|
+
}
|
|
18547
|
+
}
|
|
18548
|
+
});
|
|
18549
|
+
|
|
18366
18550
|
// ../../packages/keep/dist/client.js
|
|
18367
18551
|
var require_client2 = __commonJS({
|
|
18368
18552
|
"../../packages/keep/dist/client.js"(exports2) {
|
|
@@ -18375,50 +18559,20 @@ var require_client2 = __commonJS({
|
|
|
18375
18559
|
constructor(config) {
|
|
18376
18560
|
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
18377
18561
|
this.headers = { "Content-Type": "application/json" };
|
|
18378
|
-
if (config.serviceSecret) {
|
|
18379
|
-
this.headers["X-Service-Secret"] = config.serviceSecret;
|
|
18380
|
-
}
|
|
18381
18562
|
if (config.serviceToken) {
|
|
18382
18563
|
this.headers["Authorization"] = `Bearer ${config.serviceToken}`;
|
|
18383
18564
|
} else if (config.accessToken) {
|
|
18384
18565
|
this.headers["Authorization"] = `Bearer ${config.accessToken}`;
|
|
18385
18566
|
}
|
|
18386
18567
|
}
|
|
18387
|
-
// ──
|
|
18388
|
-
|
|
18389
|
-
|
|
18390
|
-
|
|
18391
|
-
|
|
18392
|
-
|
|
18393
|
-
|
|
18394
|
-
|
|
18395
|
-
async checkClaimsBatch(subject, claims) {
|
|
18396
|
-
return this.request("POST", "/api/claims/batch", {
|
|
18397
|
-
subject,
|
|
18398
|
-
claims
|
|
18399
|
-
});
|
|
18400
|
-
}
|
|
18401
|
-
async getEffectivePermissions(subject, context) {
|
|
18402
|
-
return this.request("POST", "/api/claims/effective", {
|
|
18403
|
-
subject,
|
|
18404
|
-
context
|
|
18405
|
-
});
|
|
18406
|
-
}
|
|
18407
|
-
// ── API Key Verification ────────────────────────────────────────
|
|
18408
|
-
async verifyApiKey(key) {
|
|
18409
|
-
return this.request("POST", "/api/keys/claims", { key });
|
|
18410
|
-
}
|
|
18411
|
-
// ── Service Tokens ──────────────────────────────────────────────
|
|
18412
|
-
async exchangeServiceToken(clientId, clientSecret) {
|
|
18413
|
-
return this.request("POST", "/api/service-tokens", {
|
|
18414
|
-
clientId,
|
|
18415
|
-
clientSecret
|
|
18416
|
-
});
|
|
18417
|
-
}
|
|
18418
|
-
async verifyServiceToken(token) {
|
|
18419
|
-
return this.request("POST", "/api/service-tokens/claims", {
|
|
18420
|
-
token
|
|
18421
|
-
});
|
|
18568
|
+
// ── Actor Tokens ────────────────────────────────────────────────
|
|
18569
|
+
/**
|
|
18570
|
+
* Mint a short-lived `act_` token for an end user, with this client's
|
|
18571
|
+
* service identity recorded as the conduit. Requires this client to be
|
|
18572
|
+
* constructed with a service token holding `keep:actor-tokens:mint`.
|
|
18573
|
+
*/
|
|
18574
|
+
async mintActorToken(actor) {
|
|
18575
|
+
return this.request("POST", "/api/actor-tokens", { actor });
|
|
18422
18576
|
}
|
|
18423
18577
|
// ── Identity ────────────────────────────────────────────────────
|
|
18424
18578
|
async createAccount(email, password, name) {
|
|
@@ -18510,10 +18664,9 @@ var require_client2 = __commonJS({
|
|
|
18510
18664
|
return this.request("POST", `/api/users/${request.ownerPrincipalId}/service-identities`, request);
|
|
18511
18665
|
}
|
|
18512
18666
|
/**
|
|
18513
|
-
* Bind a role (by name) to a service identity
|
|
18514
|
-
*
|
|
18515
|
-
*
|
|
18516
|
-
* pools pass `teamId`/`orgId`.
|
|
18667
|
+
* Bind a role (by name) to a service identity, anchored to a
|
|
18668
|
+
* structural principal: user-owned Sifters pass `userId`;
|
|
18669
|
+
* team/org-anchored bindings pass `teamId`/`orgId`.
|
|
18517
18670
|
*
|
|
18518
18671
|
* Requires `keep:role-bindings:write`.
|
|
18519
18672
|
*/
|
|
@@ -19533,11 +19686,40 @@ var require_claude_code = __commonJS({
|
|
|
19533
19686
|
});
|
|
19534
19687
|
}
|
|
19535
19688
|
}
|
|
19536
|
-
|
|
19689
|
+
/**
|
|
19690
|
+
* In-session gate repair (ADR 0017): resume the session `execute()` returned a
|
|
19691
|
+
* handle for and correct its output. The repair turn works from the resumed
|
|
19692
|
+
* session's own context (no checkout needed), under the same `--json-schema`
|
|
19693
|
+
* constraint. A throwaway cwd hosts the run; the same handle rides back out so
|
|
19694
|
+
* a second repair round can resume again.
|
|
19695
|
+
*/
|
|
19696
|
+
async continue(continuation, followUp, signal, _ctx) {
|
|
19697
|
+
const handle = continuation;
|
|
19698
|
+
if (!handle || typeof handle.sessionId !== "string") {
|
|
19699
|
+
throw new Error("claude-code continue: missing session to resume");
|
|
19700
|
+
}
|
|
19701
|
+
const tmpBase = this.config.tmpDir ? (0, apply_1.expandHome)(this.config.tmpDir) : os_1.default.tmpdir();
|
|
19702
|
+
const scratch = await fs_1.promises.mkdtemp(path_1.default.join(tmpBase, "shuttle-cc-resume-"));
|
|
19703
|
+
try {
|
|
19704
|
+
const spec = {
|
|
19705
|
+
rendering: "producer",
|
|
19706
|
+
outputLabel: handle.outputLabel,
|
|
19707
|
+
outputSchema: handle.outputSchema
|
|
19708
|
+
};
|
|
19709
|
+
const { response } = await this.runClaude(followUp, spec, scratch, handle.childEnv, signal, handle.model, null, handle.sessionId);
|
|
19710
|
+
return response;
|
|
19711
|
+
} finally {
|
|
19712
|
+
await fs_1.promises.rm(scratch, { recursive: true, force: true }).catch(() => {
|
|
19713
|
+
});
|
|
19714
|
+
}
|
|
19715
|
+
}
|
|
19716
|
+
runClaude(prompt, spec, cwd, childEnv, signal, modelOverride, reporter, resumeSessionId) {
|
|
19537
19717
|
const outputLabel = spec.outputLabel;
|
|
19538
19718
|
const model = modelOverride ?? this.config.model;
|
|
19539
19719
|
return new Promise((resolve, reject) => {
|
|
19540
19720
|
const args = ["--print", "--output-format", "stream-json", "--verbose"];
|
|
19721
|
+
if (resumeSessionId)
|
|
19722
|
+
args.push("--resume", resumeSessionId);
|
|
19541
19723
|
if (spec.outputSchema && typeof spec.outputSchema === "object" && Object.keys(spec.outputSchema).length > 0) {
|
|
19542
19724
|
args.push("--json-schema", JSON.stringify(spec.outputSchema));
|
|
19543
19725
|
}
|
|
@@ -19632,7 +19814,18 @@ var require_claude_code = __commonJS({
|
|
|
19632
19814
|
aiProvider: "claude-code",
|
|
19633
19815
|
...model ? { aiModel: model } : {},
|
|
19634
19816
|
...stream.usage ?? {}
|
|
19635
|
-
}
|
|
19817
|
+
},
|
|
19818
|
+
// In-session repair handle (ADR 0017): present only when the run
|
|
19819
|
+
// reported a session id, so the runner can `--resume` it.
|
|
19820
|
+
...stream.sessionId ? {
|
|
19821
|
+
continuation: {
|
|
19822
|
+
sessionId: stream.sessionId,
|
|
19823
|
+
model,
|
|
19824
|
+
childEnv,
|
|
19825
|
+
outputLabel,
|
|
19826
|
+
outputSchema: spec.outputSchema
|
|
19827
|
+
}
|
|
19828
|
+
} : {}
|
|
19636
19829
|
},
|
|
19637
19830
|
readPaths: stream.readPaths,
|
|
19638
19831
|
toolText: stream.toolText
|
|
@@ -19732,6 +19925,7 @@ var require_claude_code = __commonJS({
|
|
|
19732
19925
|
let usage = null;
|
|
19733
19926
|
let resultMeta = null;
|
|
19734
19927
|
let structuredOutput = null;
|
|
19928
|
+
let sessionId = null;
|
|
19735
19929
|
let toolText = "";
|
|
19736
19930
|
for (const line of stdout.split("\n")) {
|
|
19737
19931
|
const trimmed = line.trim();
|
|
@@ -19743,6 +19937,8 @@ var require_claude_code = __commonJS({
|
|
|
19743
19937
|
} catch {
|
|
19744
19938
|
continue;
|
|
19745
19939
|
}
|
|
19940
|
+
if (typeof event.session_id === "string")
|
|
19941
|
+
sessionId = event.session_id;
|
|
19746
19942
|
if (event.type === "assistant") {
|
|
19747
19943
|
const content = event.message?.content;
|
|
19748
19944
|
if (!Array.isArray(content))
|
|
@@ -19779,7 +19975,7 @@ var require_claude_code = __commonJS({
|
|
|
19779
19975
|
};
|
|
19780
19976
|
}
|
|
19781
19977
|
}
|
|
19782
|
-
return { finalText, readPaths, usage, resultMeta, structuredOutput, toolText };
|
|
19978
|
+
return { finalText, readPaths, usage, resultMeta, structuredOutput, sessionId, toolText };
|
|
19783
19979
|
}
|
|
19784
19980
|
var MAX_REPORTED_READS = 2e3;
|
|
19785
19981
|
function relativeWorkspaceReads(readPaths, cwd) {
|
|
@@ -20043,7 +20239,6 @@ var require_anthropic_api = __commonJS({
|
|
|
20043
20239
|
}
|
|
20044
20240
|
async execute(dispatch, signal) {
|
|
20045
20241
|
const { prompt, spec } = (0, loom_1.renderPromptExecution)(dispatch.inputs);
|
|
20046
|
-
const outputLabel = spec.outputLabel;
|
|
20047
20242
|
const providerHints = (0, loom_1.pickProviderHints)(spec, this.instance.capabilityId);
|
|
20048
20243
|
const apiKey = await this.instance.resolveKey(providerHints);
|
|
20049
20244
|
const hintedModel = providerHints.model;
|
|
@@ -20052,6 +20247,33 @@ var require_anthropic_api = __commonJS({
|
|
|
20052
20247
|
max_tokens: this.instance.maxTokens,
|
|
20053
20248
|
messages: [{ role: "user", content: prompt }]
|
|
20054
20249
|
};
|
|
20250
|
+
return this.callApi(apiKey, body, spec, spec.outputLabel, signal);
|
|
20251
|
+
}
|
|
20252
|
+
/**
|
|
20253
|
+
* In-session gate repair (ADR 0017): re-call the Messages API with the prior
|
|
20254
|
+
* conversation plus the model's own last turn plus the repair turn, under the
|
|
20255
|
+
* same output contract. A fresh conversation is not started — the appended
|
|
20256
|
+
* assistant turn gives the model its previous answer to correct.
|
|
20257
|
+
*/
|
|
20258
|
+
async continue(continuation, followUp, signal, _ctx) {
|
|
20259
|
+
const handle = continuation;
|
|
20260
|
+
if (!handle || !Array.isArray(handle.messages)) {
|
|
20261
|
+
throw new Error("anthropic-api continue: missing conversation to resume");
|
|
20262
|
+
}
|
|
20263
|
+
const spec = {
|
|
20264
|
+
rendering: "producer",
|
|
20265
|
+
outputLabel: handle.outputLabel,
|
|
20266
|
+
outputSchema: handle.outputSchema
|
|
20267
|
+
};
|
|
20268
|
+
const body = {
|
|
20269
|
+
model: handle.model,
|
|
20270
|
+
max_tokens: handle.maxTokens,
|
|
20271
|
+
messages: [...handle.messages, { role: "user", content: followUp }]
|
|
20272
|
+
};
|
|
20273
|
+
return this.callApi(handle.apiKey, body, spec, handle.outputLabel, signal);
|
|
20274
|
+
}
|
|
20275
|
+
/** Shared Messages-API call + parse + validate, used by execute and continue. */
|
|
20276
|
+
async callApi(apiKey, body, spec, outputLabel, signal) {
|
|
20055
20277
|
const timeoutController = new AbortController();
|
|
20056
20278
|
const timer = setTimeout(() => timeoutController.abort(), this.instance.timeoutMs);
|
|
20057
20279
|
function onAbort() {
|
|
@@ -20099,7 +20321,17 @@ var require_anthropic_api = __commonJS({
|
|
|
20099
20321
|
outputs: [{ label: outputLabel, content: parsed ?? { raw: rawText } }],
|
|
20100
20322
|
rawText,
|
|
20101
20323
|
parsed,
|
|
20102
|
-
...usage ? { usage } : {}
|
|
20324
|
+
...usage ? { usage } : {},
|
|
20325
|
+
// In-session repair handle (ADR 0017): the conversation with the model's
|
|
20326
|
+
// answer appended, ready for `continue()` to add the repair turn.
|
|
20327
|
+
continuation: {
|
|
20328
|
+
apiKey,
|
|
20329
|
+
model: body.model,
|
|
20330
|
+
maxTokens: body.max_tokens,
|
|
20331
|
+
messages: [...body.messages, { role: "assistant", content: rawText }],
|
|
20332
|
+
outputLabel,
|
|
20333
|
+
outputSchema: spec.outputSchema
|
|
20334
|
+
}
|
|
20103
20335
|
};
|
|
20104
20336
|
} finally {
|
|
20105
20337
|
clearTimeout(timer);
|
|
@@ -20932,6 +21164,7 @@ var require_shuttle = __commonJS({
|
|
|
20932
21164
|
var decommission_1 = require_decommission();
|
|
20933
21165
|
var loom_1 = require_dist3();
|
|
20934
21166
|
var chain_1 = require_chain();
|
|
21167
|
+
var output_gates_1 = require_output_gates();
|
|
20935
21168
|
var keep_1 = require_dist4();
|
|
20936
21169
|
var apply_1 = require_apply();
|
|
20937
21170
|
var version_check_1 = require_version_check();
|
|
@@ -20946,6 +21179,37 @@ var require_shuttle = __commonJS({
|
|
|
20946
21179
|
var audit_log_1 = require_audit_log();
|
|
20947
21180
|
var spend_tracker_1 = require_spend_tracker();
|
|
20948
21181
|
var store_1 = require_store();
|
|
21182
|
+
var GATE_REPAIR_BUDGET = 2;
|
|
21183
|
+
async function runOutputGates(dispatch, response, executor, signal, ctx) {
|
|
21184
|
+
let spec;
|
|
21185
|
+
try {
|
|
21186
|
+
spec = (0, loom_1.readPromptExecutionSpec)(dispatch.inputs);
|
|
21187
|
+
} catch {
|
|
21188
|
+
return { ok: true, response };
|
|
21189
|
+
}
|
|
21190
|
+
const gates = spec.gates;
|
|
21191
|
+
if (!gates || gates.length === 0)
|
|
21192
|
+
return { ok: true, response };
|
|
21193
|
+
let current = response;
|
|
21194
|
+
let result = (0, output_gates_1.evaluateOutputGates)(gates, current.parsed ?? {}, dispatch.inputs);
|
|
21195
|
+
let attempts = 0;
|
|
21196
|
+
while (!result.ok && attempts < GATE_REPAIR_BUDGET && current.continuation !== void 0 && typeof executor.continue === "function") {
|
|
21197
|
+
try {
|
|
21198
|
+
current = await executor.continue(current.continuation, (0, output_gates_1.renderRepairPrompt)(result.findings), signal, ctx);
|
|
21199
|
+
} catch {
|
|
21200
|
+
break;
|
|
21201
|
+
}
|
|
21202
|
+
result = (0, output_gates_1.evaluateOutputGates)(gates, current.parsed ?? {}, dispatch.inputs);
|
|
21203
|
+
attempts += 1;
|
|
21204
|
+
}
|
|
21205
|
+
if (result.ok)
|
|
21206
|
+
return { ok: true, response: current };
|
|
21207
|
+
const detail = result.findings.map((f) => `[${f.gate}] ${f.message}`).join(" | ");
|
|
21208
|
+
return {
|
|
21209
|
+
ok: false,
|
|
21210
|
+
reason: `output gate(s) failed${attempts > 0 ? ` after ${attempts} repair attempt(s)` : ""}: ${detail}`
|
|
21211
|
+
};
|
|
21212
|
+
}
|
|
20949
21213
|
var Shuttle = class {
|
|
20950
21214
|
brand;
|
|
20951
21215
|
config;
|
|
@@ -21056,6 +21320,12 @@ var require_shuttle = __commonJS({
|
|
|
21056
21320
|
await observerChain.notify({ kind: "attendance.closed", jobId, attendanceId, status: "failed", reason, totalDurationMs: Date.now() - startedAt }, onObserverError);
|
|
21057
21321
|
return { status: "failed", reason };
|
|
21058
21322
|
}
|
|
21323
|
+
const gated = await runOutputGates(dispatch, response, executor, signal, executionCtx);
|
|
21324
|
+
if (!gated.ok) {
|
|
21325
|
+
await observerChain.notify({ kind: "attendance.closed", jobId, attendanceId, status: "failed", reason: gated.reason, totalDurationMs: Date.now() - startedAt }, onObserverError);
|
|
21326
|
+
return { status: "failed", reason: gated.reason };
|
|
21327
|
+
}
|
|
21328
|
+
response = gated.response;
|
|
21059
21329
|
await observerChain.notify({
|
|
21060
21330
|
kind: "executor.dispatch.completed",
|
|
21061
21331
|
jobId,
|
|
@@ -21728,7 +21998,7 @@ var import_path = require("path");
|
|
|
21728
21998
|
var import_promises = require("fs/promises");
|
|
21729
21999
|
var import_yaml = __toESM(require_dist());
|
|
21730
22000
|
var import_shuttle = __toESM(require_dist5());
|
|
21731
|
-
var buildVersion = true ? "0.
|
|
22001
|
+
var buildVersion = true ? "0.18.0" : pkg.version;
|
|
21732
22002
|
var sifterBrand = {
|
|
21733
22003
|
product: {
|
|
21734
22004
|
id: "sifter",
|