@kody-ade/kody-engine 0.4.416 → 0.4.418
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/kody.js +178 -40
- package/dist/implementations/types.ts +1 -0
- package/package.json +2 -2
package/dist/bin/kody.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "@kody-ade/kody-engine",
|
|
18
|
-
version: "0.4.
|
|
18
|
+
version: "0.4.418",
|
|
19
19
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
|
|
20
20
|
license: "MIT",
|
|
21
21
|
type: "module",
|
|
@@ -52,7 +52,7 @@ var init_package = __esm({
|
|
|
52
52
|
dependencies: {
|
|
53
53
|
"@actions/cache": "^6.0.0",
|
|
54
54
|
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
55
|
-
"@kody-ade/agency-domain": "0.
|
|
55
|
+
"@kody-ade/agency-domain": "0.5.0",
|
|
56
56
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
57
57
|
convex: "^1.17.0",
|
|
58
58
|
zod: "^4.0.0"
|
|
@@ -3415,6 +3415,7 @@ async function runAgent(opts) {
|
|
|
3415
3415
|
let outcomeKind = "generic_failed";
|
|
3416
3416
|
let errorMessage2;
|
|
3417
3417
|
let tokens = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };
|
|
3418
|
+
let costUsd = 0;
|
|
3418
3419
|
let messageCount = 0;
|
|
3419
3420
|
let finalText = "";
|
|
3420
3421
|
let getSubmitted;
|
|
@@ -3431,6 +3432,7 @@ async function runAgent(opts) {
|
|
|
3431
3432
|
outcomeKind = "generic_failed";
|
|
3432
3433
|
errorMessage2 = void 0;
|
|
3433
3434
|
tokens = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };
|
|
3435
|
+
costUsd = 0;
|
|
3434
3436
|
messageCount = 0;
|
|
3435
3437
|
let sawMutatingTool = false;
|
|
3436
3438
|
let sawTerminalSuccess = false;
|
|
@@ -3555,6 +3557,7 @@ async function runAgent(opts) {
|
|
|
3555
3557
|
};
|
|
3556
3558
|
}
|
|
3557
3559
|
queryOptions.settingSources = opts.settingSources ?? ["project", "local"];
|
|
3560
|
+
if (opts.abortController) queryOptions.abortController = opts.abortController;
|
|
3558
3561
|
const stableBinary = ensureStableClaudeBinary();
|
|
3559
3562
|
if (stableBinary) {
|
|
3560
3563
|
const sdkBinaryPathOption = ["pathToClaudeCode", "Exec", "utable"].join("");
|
|
@@ -3683,6 +3686,8 @@ async function runAgent(opts) {
|
|
|
3683
3686
|
}
|
|
3684
3687
|
}
|
|
3685
3688
|
if (m.type === "result") {
|
|
3689
|
+
const reportedCost = Number(m.total_cost_usd ?? 0);
|
|
3690
|
+
if (Number.isFinite(reportedCost) && reportedCost >= 0) costUsd = reportedCost;
|
|
3686
3691
|
if (m.subtype === "success") {
|
|
3687
3692
|
outcome = "completed";
|
|
3688
3693
|
outcomeKind = "ok";
|
|
@@ -3760,6 +3765,7 @@ async function runAgent(opts) {
|
|
|
3760
3765
|
ndjsonPath,
|
|
3761
3766
|
durationMs: Date.now() - startedAt,
|
|
3762
3767
|
tokens,
|
|
3768
|
+
costUsd,
|
|
3763
3769
|
messageCount
|
|
3764
3770
|
};
|
|
3765
3771
|
}
|
|
@@ -13038,7 +13044,6 @@ function addDefinition(catalog, document) {
|
|
|
13038
13044
|
}
|
|
13039
13045
|
function add(collection, definition, revision) {
|
|
13040
13046
|
const mutable = collection;
|
|
13041
|
-
if (mutable.has(definition.id)) throw new Error(`Duplicate Agency Definition: ${definition.id}`);
|
|
13042
13047
|
mutable.set(definition.id, { definition, revision });
|
|
13043
13048
|
}
|
|
13044
13049
|
function validateRelationships(catalog) {
|
|
@@ -13098,7 +13103,10 @@ var init_agencyModelRepository = __esm({
|
|
|
13098
13103
|
async loadCatalog() {
|
|
13099
13104
|
const documents = await this.backend.listAgencyDefinitions(this.tenantId);
|
|
13100
13105
|
const catalog = emptyCatalog();
|
|
13101
|
-
|
|
13106
|
+
const ordered = [...documents].sort(
|
|
13107
|
+
(left, right) => left.createdAt.localeCompare(right.createdAt) || left.recordId.localeCompare(right.recordId)
|
|
13108
|
+
);
|
|
13109
|
+
for (const document of ordered) addDefinition(catalog, document);
|
|
13102
13110
|
validateRelationships(catalog);
|
|
13103
13111
|
return catalog;
|
|
13104
13112
|
}
|
|
@@ -13325,7 +13333,19 @@ async function dispatchAgencyLoopsWith(input) {
|
|
|
13325
13333
|
results.push({ loopId: record2.definition.id, decision: "skipped", reason });
|
|
13326
13334
|
continue;
|
|
13327
13335
|
}
|
|
13328
|
-
const
|
|
13336
|
+
const failurePolicy = record2.definition.reconciliationPolicy.failure;
|
|
13337
|
+
const budget = policy.snapshot.policy.budget;
|
|
13338
|
+
const maxAttempts = Math.min(failurePolicy.maxAttempts, budget.maxRuns);
|
|
13339
|
+
const timeoutSeconds = Math.min(failurePolicy.timeoutSeconds, budget.maxDurationSeconds);
|
|
13340
|
+
const backoffBudgetSeconds = Array.from(
|
|
13341
|
+
{ length: Math.max(0, maxAttempts - 1) },
|
|
13342
|
+
(_, index) => failurePolicy.backoffSeconds * 2 ** index
|
|
13343
|
+
).reduce((sum, seconds) => sum + seconds, 0);
|
|
13344
|
+
const leaseSeconds = Math.min(
|
|
13345
|
+
budget.maxDurationSeconds,
|
|
13346
|
+
maxAttempts * timeoutSeconds + backoffBudgetSeconds
|
|
13347
|
+
);
|
|
13348
|
+
const leaseUntil = new Date(input.now.getTime() + leaseSeconds * 1e3).toISOString();
|
|
13329
13349
|
const reservationId = `reservation-${randomUUID()}`;
|
|
13330
13350
|
const correlationId = `corr-${randomUUID()}`;
|
|
13331
13351
|
const trace = [policy.trace[0], ...target.intermediate, target.reference];
|
|
@@ -13365,40 +13385,97 @@ async function dispatchAgencyLoopsWith(input) {
|
|
|
13365
13385
|
updatedAt: now
|
|
13366
13386
|
});
|
|
13367
13387
|
await repository.saveState(runningState, "loop", now);
|
|
13368
|
-
const runId = `run-${randomUUID()}`;
|
|
13369
|
-
const activeRun = createRun({
|
|
13370
|
-
id: runId,
|
|
13371
|
-
status: "running",
|
|
13372
|
-
origin: { kind: "loop", id: record2.definition.id, revision: record2.revision },
|
|
13373
|
-
target: target.reference,
|
|
13374
|
-
trace,
|
|
13375
|
-
effectivePolicy: policy.snapshot,
|
|
13376
|
-
correlationId,
|
|
13377
|
-
startedAt: now
|
|
13378
|
-
});
|
|
13379
13388
|
try {
|
|
13380
|
-
|
|
13381
|
-
|
|
13382
|
-
|
|
13383
|
-
|
|
13384
|
-
|
|
13385
|
-
|
|
13386
|
-
|
|
13387
|
-
const
|
|
13388
|
-
|
|
13389
|
+
let attempts = 0;
|
|
13390
|
+
let tokens = 0;
|
|
13391
|
+
let costUsd = 0;
|
|
13392
|
+
let succeeded = false;
|
|
13393
|
+
let reason = "target failed";
|
|
13394
|
+
let finalRunId;
|
|
13395
|
+
let finalCapabilityResults = [];
|
|
13396
|
+
const budgetDeadline = Date.now() + budget.maxDurationSeconds * 1e3;
|
|
13397
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
13398
|
+
const remainingMilliseconds = budgetDeadline - Date.now();
|
|
13399
|
+
if (remainingMilliseconds <= 0) {
|
|
13400
|
+
reason = "policy duration budget exhausted";
|
|
13401
|
+
break;
|
|
13402
|
+
}
|
|
13403
|
+
attempts = attempt;
|
|
13404
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13405
|
+
const runId = `run-${randomUUID()}`;
|
|
13406
|
+
finalRunId = runId;
|
|
13407
|
+
const activeRun = createRun({
|
|
13408
|
+
id: runId,
|
|
13409
|
+
status: "running",
|
|
13410
|
+
origin: { kind: "loop", id: record2.definition.id, revision: record2.revision },
|
|
13411
|
+
target: target.reference,
|
|
13412
|
+
trace,
|
|
13413
|
+
effectivePolicy: policy.snapshot,
|
|
13414
|
+
correlationId,
|
|
13415
|
+
startedAt
|
|
13416
|
+
});
|
|
13417
|
+
await input.backend.createAgencyModelRun(
|
|
13418
|
+
input.tenantId,
|
|
13419
|
+
target.reference.kind,
|
|
13420
|
+
target.reference.id,
|
|
13421
|
+
activeRun,
|
|
13422
|
+
startedAt
|
|
13423
|
+
);
|
|
13424
|
+
const attemptResult = await runAttempt(
|
|
13425
|
+
input.run,
|
|
13426
|
+
target.job,
|
|
13427
|
+
Math.min(timeoutSeconds, remainingMilliseconds / 1e3)
|
|
13428
|
+
);
|
|
13429
|
+
tokens += attemptResult.usage?.tokens ?? 0;
|
|
13430
|
+
costUsd += attemptResult.usage?.costUsd ?? 0;
|
|
13431
|
+
const finishedAt2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
13432
|
+
succeeded = attemptResult.exitCode === 0;
|
|
13433
|
+
finalCapabilityResults = attemptResult.capabilityResults ?? [];
|
|
13434
|
+
reason = attemptResult.reason ?? (succeeded ? "target dispatched" : "target failed");
|
|
13435
|
+
const usage = {
|
|
13436
|
+
tokens: attemptResult.usage?.tokens ?? 0,
|
|
13437
|
+
costUsd: attemptResult.usage?.costUsd ?? 0,
|
|
13438
|
+
durationSeconds: Math.max(0, (Date.parse(finishedAt2) - Date.parse(startedAt)) / 1e3)
|
|
13439
|
+
};
|
|
13440
|
+
if (tokens > budget.maxTokens || costUsd > budget.maxCostUsd) {
|
|
13441
|
+
succeeded = false;
|
|
13442
|
+
reason = tokens > budget.maxTokens ? "policy token budget exhausted" : "policy cost budget exhausted";
|
|
13443
|
+
}
|
|
13444
|
+
await input.backend.finishAgencyModelRun(
|
|
13445
|
+
input.tenantId,
|
|
13446
|
+
terminalRun(activeRun, succeeded ? "succeeded" : "failed", finishedAt2, usage),
|
|
13447
|
+
finishedAt2
|
|
13448
|
+
);
|
|
13449
|
+
if (tokens > budget.maxTokens || costUsd > budget.maxCostUsd) break;
|
|
13450
|
+
if (succeeded || attempt === maxAttempts) break;
|
|
13451
|
+
const backoffMilliseconds = failurePolicy.backoffSeconds * 2 ** (attempt - 1) * 1e3;
|
|
13452
|
+
if (Date.now() + backoffMilliseconds >= budgetDeadline) {
|
|
13453
|
+
reason = "policy duration budget exhausted during retry backoff";
|
|
13454
|
+
break;
|
|
13455
|
+
}
|
|
13456
|
+
await wait(backoffMilliseconds);
|
|
13457
|
+
}
|
|
13389
13458
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
|
|
13393
|
-
|
|
13394
|
-
|
|
13459
|
+
const goalRecord = record2.definition.targetRef.kind === "goal" ? records.find(
|
|
13460
|
+
(candidate) => "executionRef" in candidate.definition && candidate.definition.id === record2.definition.targetRef.id
|
|
13461
|
+
) : void 0;
|
|
13462
|
+
if (succeeded && goalRecord && finalRunId) {
|
|
13463
|
+
await appendCapabilityOutputs(
|
|
13464
|
+
repository,
|
|
13465
|
+
finalRunId,
|
|
13466
|
+
target.reference,
|
|
13467
|
+
finalCapabilityResults,
|
|
13468
|
+
finishedAt
|
|
13469
|
+
);
|
|
13470
|
+
await repository.refreshGoalProgress(goalRecord, finishedAt);
|
|
13471
|
+
}
|
|
13395
13472
|
await input.backend.finishAgencyDispatch(
|
|
13396
13473
|
input.tenantId,
|
|
13397
13474
|
decision.idempotencyKey,
|
|
13398
13475
|
reservationId,
|
|
13399
|
-
succeeded ? "dispatched" : "
|
|
13476
|
+
succeeded ? "dispatched" : "dead-letter",
|
|
13400
13477
|
finishedAt,
|
|
13401
|
-
|
|
13478
|
+
finalRunId
|
|
13402
13479
|
);
|
|
13403
13480
|
await repository.saveState(
|
|
13404
13481
|
createLoopState2({
|
|
@@ -13413,19 +13490,17 @@ async function dispatchAgencyLoopsWith(input) {
|
|
|
13413
13490
|
results.push({
|
|
13414
13491
|
loopId: record2.definition.id,
|
|
13415
13492
|
decision: succeeded ? "dispatched" : "failed",
|
|
13416
|
-
reason:
|
|
13493
|
+
reason: succeeded ? reason : `${reason}; dead-lettered after ${attempts} attempt${attempts === 1 ? "" : "s"}`
|
|
13417
13494
|
});
|
|
13418
13495
|
} catch (error) {
|
|
13419
13496
|
const reason = error instanceof Error ? error.message : String(error);
|
|
13420
13497
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13421
|
-
await input.backend.finishAgencyModelRun(input.tenantId, terminalRun(activeRun, "failed", finishedAt), finishedAt).catch(() => void 0);
|
|
13422
13498
|
await input.backend.finishAgencyDispatch(
|
|
13423
13499
|
input.tenantId,
|
|
13424
13500
|
decision.idempotencyKey,
|
|
13425
13501
|
reservationId,
|
|
13426
|
-
"
|
|
13427
|
-
finishedAt
|
|
13428
|
-
runId
|
|
13502
|
+
"dead-letter",
|
|
13503
|
+
finishedAt
|
|
13429
13504
|
);
|
|
13430
13505
|
results.push({ loopId: record2.definition.id, decision: "failed", reason });
|
|
13431
13506
|
}
|
|
@@ -13449,8 +13524,58 @@ function resolveTarget(loop, catalog) {
|
|
|
13449
13524
|
job: target.kind === "workflow" ? { workflow: target.id, cliArgs: {}, flavor: "scheduled" } : { capability: target.id, cliArgs: {}, flavor: "scheduled" }
|
|
13450
13525
|
};
|
|
13451
13526
|
}
|
|
13452
|
-
function terminalRun(active, status, finishedAt) {
|
|
13453
|
-
return createRun({ ...active, status, finishedAt });
|
|
13527
|
+
function terminalRun(active, status, finishedAt, usage) {
|
|
13528
|
+
return createRun({ ...active, status, finishedAt, usage });
|
|
13529
|
+
}
|
|
13530
|
+
async function runAttempt(run, job, timeoutSeconds) {
|
|
13531
|
+
const abortController = new AbortController();
|
|
13532
|
+
let timer;
|
|
13533
|
+
try {
|
|
13534
|
+
return await Promise.race([
|
|
13535
|
+
run(job, abortController),
|
|
13536
|
+
new Promise((resolve17) => {
|
|
13537
|
+
timer = setTimeout(() => {
|
|
13538
|
+
abortController.abort();
|
|
13539
|
+
resolve17({ exitCode: 124, reason: `target timed out after ${formatSeconds(timeoutSeconds)}s` });
|
|
13540
|
+
}, timeoutSeconds * 1e3);
|
|
13541
|
+
})
|
|
13542
|
+
]);
|
|
13543
|
+
} finally {
|
|
13544
|
+
if (timer) clearTimeout(timer);
|
|
13545
|
+
}
|
|
13546
|
+
}
|
|
13547
|
+
async function appendCapabilityOutputs(repository, runId, producer, results, createdAt) {
|
|
13548
|
+
for (const result of results) {
|
|
13549
|
+
const outputs = [
|
|
13550
|
+
...Object.entries(result.facts).map(([key, value]) => ({ kind: "fact", key, value })),
|
|
13551
|
+
...Object.entries(result.evidence ?? {}).map(([key, value]) => ({
|
|
13552
|
+
kind: "evidence",
|
|
13553
|
+
key,
|
|
13554
|
+
value
|
|
13555
|
+
})),
|
|
13556
|
+
...result.artifacts.map((artifact, index) => ({
|
|
13557
|
+
kind: "artifact",
|
|
13558
|
+
key: artifact.label || `artifact-${index + 1}`,
|
|
13559
|
+
value: artifact
|
|
13560
|
+
}))
|
|
13561
|
+
];
|
|
13562
|
+
for (const output of outputs) {
|
|
13563
|
+
await repository.appendOutput(`output-${randomUUID()}`, {
|
|
13564
|
+
...output,
|
|
13565
|
+
runId,
|
|
13566
|
+
producer: { kind: producer.kind, id: producer.id },
|
|
13567
|
+
contract: "capability-result/v1",
|
|
13568
|
+
createdAt
|
|
13569
|
+
});
|
|
13570
|
+
}
|
|
13571
|
+
}
|
|
13572
|
+
}
|
|
13573
|
+
function formatSeconds(seconds) {
|
|
13574
|
+
return Number.isInteger(seconds) ? String(seconds) : seconds.toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
|
|
13575
|
+
}
|
|
13576
|
+
async function wait(milliseconds) {
|
|
13577
|
+
if (milliseconds <= 0) return;
|
|
13578
|
+
await new Promise((resolve17) => setTimeout(resolve17, milliseconds));
|
|
13454
13579
|
}
|
|
13455
13580
|
function repositoryTenant(config) {
|
|
13456
13581
|
const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
|
|
@@ -13474,7 +13599,14 @@ var init_dispatchAgencyLoops = __esm({
|
|
|
13474
13599
|
tenantId: tenantId2,
|
|
13475
13600
|
backend,
|
|
13476
13601
|
now: /* @__PURE__ */ new Date(),
|
|
13477
|
-
run: (job) => runJob(job, {
|
|
13602
|
+
run: (job, abortController) => runJob(job, {
|
|
13603
|
+
cwd: ctx.cwd,
|
|
13604
|
+
config: ctx.config,
|
|
13605
|
+
verbose: ctx.verbose,
|
|
13606
|
+
quiet: ctx.quiet,
|
|
13607
|
+
chain: false,
|
|
13608
|
+
abortController
|
|
13609
|
+
})
|
|
13478
13610
|
});
|
|
13479
13611
|
ctx.data.agencyLoopDispatchResults = results;
|
|
13480
13612
|
};
|
|
@@ -20686,6 +20818,7 @@ async function runImplementation(profileName, input) {
|
|
|
20686
20818
|
isBackendHealthy: lm ? () => lm.isHealthy() : void 0,
|
|
20687
20819
|
verbose: input.verbose,
|
|
20688
20820
|
quiet: input.quiet,
|
|
20821
|
+
abortController: input.abortController,
|
|
20689
20822
|
ndjsonDir,
|
|
20690
20823
|
additionalDirectories: agentTaskArtifacts ? [agentTaskArtifacts.absDir] : void 0,
|
|
20691
20824
|
allowedToolsOverride: profile.claudeCode.tools,
|
|
@@ -20795,6 +20928,10 @@ async function runImplementation(profileName, input) {
|
|
|
20795
20928
|
reason: err instanceof Error ? err.message : String(err)
|
|
20796
20929
|
});
|
|
20797
20930
|
}
|
|
20931
|
+
ctx.output.usage = {
|
|
20932
|
+
tokens: agentResult.tokens ? agentResult.tokens.input + agentResult.tokens.output + agentResult.tokens.cacheRead + agentResult.tokens.cacheCreate : 0,
|
|
20933
|
+
costUsd: agentResult.costUsd ?? 0
|
|
20934
|
+
};
|
|
20798
20935
|
emitEvent(input.cwd, {
|
|
20799
20936
|
implementation: profileName,
|
|
20800
20937
|
kind: "agent_end",
|
|
@@ -21637,6 +21774,7 @@ async function runCapabilityImplementationStep(valid, profileName, capabilityIde
|
|
|
21637
21774
|
skipConfig: base.skipConfig,
|
|
21638
21775
|
verbose: base.verbose,
|
|
21639
21776
|
quiet: base.quiet,
|
|
21777
|
+
abortController: base.abortController,
|
|
21640
21778
|
preloadedData: Object.keys(preloadedData).length > 0 ? preloadedData : void 0
|
|
21641
21779
|
};
|
|
21642
21780
|
const shouldApplyResolvedCapabilityArgs = valid.implementation === void 0 && resolvedCapability && profileName === resolvedCapability.implementation;
|
|
@@ -453,6 +453,7 @@ export interface Context {
|
|
|
453
453
|
exitCode: number
|
|
454
454
|
prUrl?: string
|
|
455
455
|
reason?: string
|
|
456
|
+
usage?: { tokens: number; costUsd: number }
|
|
456
457
|
/**
|
|
457
458
|
* In-process hand-off to the next stage. A stage (e.g. `classify`) sets
|
|
458
459
|
* this so the orchestrator runs the chosen sub-orchestrator
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.418",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@actions/cache": "^6.0.0",
|
|
17
17
|
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
18
|
-
"@kody-ade/agency-domain": "0.
|
|
18
|
+
"@kody-ade/agency-domain": "0.5.0",
|
|
19
19
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
20
20
|
"convex": "^1.17.0",
|
|
21
21
|
"zod": "^4.0.0"
|