@kody-ade/kody-engine 0.4.414 → 0.4.416
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 +322 -78
- package/package.json +25 -26
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.416",
|
|
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",
|
|
@@ -50,9 +50,9 @@ var init_package = __esm({
|
|
|
50
50
|
prepublishOnly: "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm build && pnpm verify:package"
|
|
51
51
|
},
|
|
52
52
|
dependencies: {
|
|
53
|
-
"@kody-ade/agency-domain": "0.1.1",
|
|
54
53
|
"@actions/cache": "^6.0.0",
|
|
55
54
|
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
55
|
+
"@kody-ade/agency-domain": "0.4.0",
|
|
56
56
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
57
57
|
convex: "^1.17.0",
|
|
58
58
|
zod: "^4.0.0"
|
|
@@ -1990,14 +1990,14 @@ function resolveImplementationCandidates(name, roots = getImplementationRoots())
|
|
|
1990
1990
|
function listCapabilityActions(projectCapabilitiesRoot = getProjectCapabilitiesRoot()) {
|
|
1991
1991
|
const seen = /* @__PURE__ */ new Set();
|
|
1992
1992
|
const out = [];
|
|
1993
|
-
const
|
|
1993
|
+
const add2 = (action) => {
|
|
1994
1994
|
if (!isSafeName(action.action) || !isSafeName(action.capability) || !isSafeName(action.implementation)) return;
|
|
1995
1995
|
if (seen.has(action.action)) return;
|
|
1996
1996
|
seen.add(action.action);
|
|
1997
1997
|
out.push(action);
|
|
1998
1998
|
};
|
|
1999
|
-
for (const action of listFolderCapabilityActions(projectCapabilitiesRoot, "project-folder"))
|
|
2000
|
-
for (const action of listBuiltinCapabilityActions(getBuiltinCapabilitiesRoot()))
|
|
1999
|
+
for (const action of listFolderCapabilityActions(projectCapabilitiesRoot, "project-folder")) add2(action);
|
|
2000
|
+
for (const action of listBuiltinCapabilityActions(getBuiltinCapabilitiesRoot())) add2(action);
|
|
2001
2001
|
return out.sort((a, b) => a.action.localeCompare(b.action));
|
|
2002
2002
|
}
|
|
2003
2003
|
function resolveCapabilityAction(action, projectCapabilitiesRoot = getProjectCapabilitiesRoot()) {
|
|
@@ -2476,14 +2476,15 @@ function createStateBackendFromEnv(env = process.env, client) {
|
|
|
2476
2476
|
});
|
|
2477
2477
|
return Array.isArray(result) ? result : [];
|
|
2478
2478
|
},
|
|
2479
|
-
async reserveAgencyDispatch(tenantId2,
|
|
2479
|
+
async reserveAgencyDispatch(tenantId2, reservation) {
|
|
2480
2480
|
const result = await transport.mutation(anyApi.agencyModel.reserveDispatch, {
|
|
2481
2481
|
tenantId: requireTenant(tenantId2),
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2482
|
+
...reservation,
|
|
2483
|
+
idempotencyKey: requireNonEmpty(reservation.idempotencyKey, "idempotencyKey"),
|
|
2484
|
+
loopId: requireNonEmpty(reservation.loopId, "loopId"),
|
|
2485
|
+
reservationId: requireNonEmpty(reservation.reservationId, "reservationId"),
|
|
2486
|
+
correlationId: requireNonEmpty(reservation.correlationId, "correlationId"),
|
|
2487
|
+
policyHash: requireNonEmpty(reservation.policyHash, "policyHash")
|
|
2487
2488
|
});
|
|
2488
2489
|
return result;
|
|
2489
2490
|
},
|
|
@@ -2496,15 +2497,32 @@ function createStateBackendFromEnv(env = process.env, client) {
|
|
|
2496
2497
|
now
|
|
2497
2498
|
});
|
|
2498
2499
|
},
|
|
2499
|
-
async finishAgencyDispatch(tenantId2, idempotencyKey, status, now, runId) {
|
|
2500
|
+
async finishAgencyDispatch(tenantId2, idempotencyKey, reservationId, status, now, runId) {
|
|
2500
2501
|
await transport.mutation(anyApi.agencyModel.finishDispatch, {
|
|
2501
2502
|
tenantId: requireTenant(tenantId2),
|
|
2502
2503
|
idempotencyKey: requireNonEmpty(idempotencyKey, "idempotencyKey"),
|
|
2504
|
+
reservationId: requireNonEmpty(reservationId, "reservationId"),
|
|
2503
2505
|
status,
|
|
2504
2506
|
now,
|
|
2505
2507
|
...runId ? { runId: requireNonEmpty(runId, "runId") } : {}
|
|
2506
2508
|
});
|
|
2507
2509
|
},
|
|
2510
|
+
async createAgencyModelRun(tenantId2, subjectType, subjectId, run, now) {
|
|
2511
|
+
await transport.mutation(anyApi.agencyModel.createRunRecord, {
|
|
2512
|
+
tenantId: requireTenant(tenantId2),
|
|
2513
|
+
subjectType,
|
|
2514
|
+
subjectId: requireNonEmpty(subjectId, "subjectId"),
|
|
2515
|
+
run,
|
|
2516
|
+
now
|
|
2517
|
+
});
|
|
2518
|
+
},
|
|
2519
|
+
async finishAgencyModelRun(tenantId2, run, now) {
|
|
2520
|
+
await transport.mutation(anyApi.agencyModel.finishRunRecord, {
|
|
2521
|
+
tenantId: requireTenant(tenantId2),
|
|
2522
|
+
run,
|
|
2523
|
+
now
|
|
2524
|
+
});
|
|
2525
|
+
},
|
|
2508
2526
|
async appendRunEvent(tenantId2, runId, goalId, event, time) {
|
|
2509
2527
|
await transport.mutation(anyApi.runEvents.append, {
|
|
2510
2528
|
tenantId: requireTenant(tenantId2),
|
|
@@ -12969,6 +12987,7 @@ var init_dispatchClassified = __esm({
|
|
|
12969
12987
|
|
|
12970
12988
|
// src/goal/agencyModelRepository.ts
|
|
12971
12989
|
import {
|
|
12990
|
+
createAgentDefinition,
|
|
12972
12991
|
createCapabilityDefinition,
|
|
12973
12992
|
createGoalDefinition,
|
|
12974
12993
|
createGoalState,
|
|
@@ -12977,7 +12996,8 @@ import {
|
|
|
12977
12996
|
createLoopState,
|
|
12978
12997
|
createOperationDefinition,
|
|
12979
12998
|
createRunOutput,
|
|
12980
|
-
createWorkflowDefinition
|
|
12999
|
+
createWorkflowDefinition,
|
|
13000
|
+
relationshipIssues
|
|
12981
13001
|
} from "@kody-ade/agency-domain";
|
|
12982
13002
|
function goalProgressFromOutputs(definition, outputs) {
|
|
12983
13003
|
const required2 = definition.objective.requiredEvidence;
|
|
@@ -12987,28 +13007,65 @@ function goalProgressFromOutputs(definition, outputs) {
|
|
|
12987
13007
|
);
|
|
12988
13008
|
return required2.filter((key) => satisfied.has(key)).length / required2.length;
|
|
12989
13009
|
}
|
|
12990
|
-
function
|
|
12991
|
-
for (const document of documents) {
|
|
12992
|
-
if (document.schemaVersion !== 1) throw new Error(`Unsupported Agency Definition schema: ${document.schemaVersion}`);
|
|
12993
|
-
if (document.kind === "intent") createIntentDefinition(document.data);
|
|
12994
|
-
else if (document.kind === "operation") createOperationDefinition(document.data);
|
|
12995
|
-
else if (document.kind === "goal") createGoalDefinition(document.data);
|
|
12996
|
-
else if (document.kind === "loop") createLoopDefinition(document.data);
|
|
12997
|
-
else if (document.kind === "workflow") createWorkflowDefinition(document.data);
|
|
12998
|
-
else createCapabilityDefinition(document.data);
|
|
12999
|
-
}
|
|
13000
|
-
}
|
|
13001
|
-
function parseManagedDefinition(document) {
|
|
13002
|
-
return document.kind === "goal" ? createGoalDefinition(document.data) : createLoopDefinition(document.data);
|
|
13003
|
-
}
|
|
13004
|
-
function parseState(document, definition) {
|
|
13010
|
+
function parseState(document, definition, kind) {
|
|
13005
13011
|
if (!document) return null;
|
|
13006
13012
|
if (document.schemaVersion !== 1) throw new Error(`Unsupported Agency State schema: ${document.schemaVersion}`);
|
|
13007
|
-
if (document.kind !==
|
|
13008
|
-
throw new Error(`Agency State does not match Definition: ${definition.
|
|
13013
|
+
if (document.kind !== kind || document.definitionId !== definition.id) {
|
|
13014
|
+
throw new Error(`Agency State does not match Definition: ${definition.id}`);
|
|
13009
13015
|
}
|
|
13010
13016
|
return document.kind === "goal" ? createGoalState(document.data) : createLoopState(document.data);
|
|
13011
13017
|
}
|
|
13018
|
+
function emptyCatalog() {
|
|
13019
|
+
return {
|
|
13020
|
+
intents: /* @__PURE__ */ new Map(),
|
|
13021
|
+
operations: /* @__PURE__ */ new Map(),
|
|
13022
|
+
goals: /* @__PURE__ */ new Map(),
|
|
13023
|
+
loops: /* @__PURE__ */ new Map(),
|
|
13024
|
+
workflows: /* @__PURE__ */ new Map(),
|
|
13025
|
+
capabilities: /* @__PURE__ */ new Map(),
|
|
13026
|
+
agents: /* @__PURE__ */ new Map()
|
|
13027
|
+
};
|
|
13028
|
+
}
|
|
13029
|
+
function addDefinition(catalog, document) {
|
|
13030
|
+
if (document.schemaVersion !== 1) throw new Error(`Unsupported Agency Definition schema: ${document.schemaVersion}`);
|
|
13031
|
+
if (document.kind === "intent") add(catalog.intents, createIntentDefinition(document.data), document.recordId);
|
|
13032
|
+
else if (document.kind === "operation") add(catalog.operations, createOperationDefinition(document.data), document.recordId);
|
|
13033
|
+
else if (document.kind === "goal") add(catalog.goals, createGoalDefinition(document.data), document.recordId);
|
|
13034
|
+
else if (document.kind === "loop") add(catalog.loops, createLoopDefinition(document.data), document.recordId);
|
|
13035
|
+
else if (document.kind === "workflow") add(catalog.workflows, createWorkflowDefinition(document.data), document.recordId);
|
|
13036
|
+
else if (document.kind === "capability") add(catalog.capabilities, createCapabilityDefinition(document.data), document.recordId);
|
|
13037
|
+
else add(catalog.agents, createAgentDefinition(document.data), document.recordId);
|
|
13038
|
+
}
|
|
13039
|
+
function add(collection, definition, revision) {
|
|
13040
|
+
const mutable = collection;
|
|
13041
|
+
if (mutable.has(definition.id)) throw new Error(`Duplicate Agency Definition: ${definition.id}`);
|
|
13042
|
+
mutable.set(definition.id, { definition, revision });
|
|
13043
|
+
}
|
|
13044
|
+
function validateRelationships(catalog) {
|
|
13045
|
+
const relationshipCatalog = {
|
|
13046
|
+
operations: [...catalog.operations.keys()],
|
|
13047
|
+
goals: [...catalog.goals.keys()],
|
|
13048
|
+
workflows: [...catalog.workflows.keys()],
|
|
13049
|
+
capabilities: [...catalog.capabilities.keys()]
|
|
13050
|
+
};
|
|
13051
|
+
const issues = [...catalog.goals.values(), ...catalog.loops.values()].flatMap(
|
|
13052
|
+
({ definition }) => relationshipIssues(definition, relationshipCatalog).map((issue2) => `${definition.id}: ${issue2}`)
|
|
13053
|
+
);
|
|
13054
|
+
for (const { definition } of catalog.operations.values()) {
|
|
13055
|
+
for (const intentId of definition.intentIds) {
|
|
13056
|
+
if (!catalog.intents.has(intentId)) issues.push(`${definition.id}: Missing Intent "${intentId}"`);
|
|
13057
|
+
}
|
|
13058
|
+
}
|
|
13059
|
+
for (const { definition } of catalog.workflows.values()) {
|
|
13060
|
+
for (const step of definition.steps) {
|
|
13061
|
+
if (!catalog.capabilities.has(step.capabilityRef.id)) {
|
|
13062
|
+
issues.push(`${definition.id}: Missing Capability "${step.capabilityRef.id}"`);
|
|
13063
|
+
}
|
|
13064
|
+
}
|
|
13065
|
+
}
|
|
13066
|
+
if (issues.length > 0) throw new Error(`Invalid Agency relationships:
|
|
13067
|
+
${issues.join("\n")}`);
|
|
13068
|
+
}
|
|
13012
13069
|
var AgencyModelRepository;
|
|
13013
13070
|
var init_agencyModelRepository = __esm({
|
|
13014
13071
|
"src/goal/agencyModelRepository.ts"() {
|
|
@@ -13020,19 +13077,31 @@ var init_agencyModelRepository = __esm({
|
|
|
13020
13077
|
}
|
|
13021
13078
|
backend;
|
|
13022
13079
|
tenantId;
|
|
13023
|
-
async listManagedWork() {
|
|
13024
|
-
const
|
|
13025
|
-
|
|
13026
|
-
|
|
13027
|
-
(
|
|
13028
|
-
|
|
13080
|
+
async listManagedWork(catalog) {
|
|
13081
|
+
const definitions = catalog ?? await this.loadCatalog();
|
|
13082
|
+
const managed = [
|
|
13083
|
+
...Array.from(definitions.goals.values(), ({ definition, revision }) => ({ definition, revision, kind: "goal" })),
|
|
13084
|
+
...Array.from(definitions.loops.values(), ({ definition, revision }) => ({ definition, revision, kind: "loop" }))
|
|
13085
|
+
];
|
|
13029
13086
|
return Promise.all(
|
|
13030
|
-
managed.map(async (
|
|
13031
|
-
definition:
|
|
13032
|
-
|
|
13087
|
+
managed.map(async (record2) => ({
|
|
13088
|
+
definition: record2.definition,
|
|
13089
|
+
revision: record2.revision,
|
|
13090
|
+
state: parseState(
|
|
13091
|
+
await this.backend.getAgencyState(this.tenantId, record2.definition.id),
|
|
13092
|
+
record2.definition,
|
|
13093
|
+
record2.kind
|
|
13094
|
+
)
|
|
13033
13095
|
}))
|
|
13034
13096
|
);
|
|
13035
13097
|
}
|
|
13098
|
+
async loadCatalog() {
|
|
13099
|
+
const documents = await this.backend.listAgencyDefinitions(this.tenantId);
|
|
13100
|
+
const catalog = emptyCatalog();
|
|
13101
|
+
for (const document of documents) addDefinition(catalog, document);
|
|
13102
|
+
validateRelationships(catalog);
|
|
13103
|
+
return catalog;
|
|
13104
|
+
}
|
|
13036
13105
|
async saveState(state, kind, updatedAt) {
|
|
13037
13106
|
const data = kind === "goal" ? createGoalState(state) : createLoopState(state);
|
|
13038
13107
|
await this.backend.putAgencyState(this.tenantId, state.definitionId, kind, 1, data, updatedAt);
|
|
@@ -13117,18 +13186,106 @@ var init_triggerDispatcher = __esm({
|
|
|
13117
13186
|
}
|
|
13118
13187
|
});
|
|
13119
13188
|
|
|
13189
|
+
// src/goal/policyResolver.ts
|
|
13190
|
+
import { createHash as createHash2 } from "crypto";
|
|
13191
|
+
function resolveDispatchPolicy(input) {
|
|
13192
|
+
const operation = input.catalog.operations.get(input.owner.definition.operationId);
|
|
13193
|
+
if (!operation) throw new Error(`Dispatch blocked: Operation "${input.owner.definition.operationId}" is unresolved`);
|
|
13194
|
+
if (operation.definition.intentIds.length === 0) {
|
|
13195
|
+
throw new Error(`Dispatch blocked: Operation "${operation.definition.id}" has no Intent`);
|
|
13196
|
+
}
|
|
13197
|
+
const intents = operation.definition.intentIds.map((intentId) => {
|
|
13198
|
+
const intent = input.catalog.intents.get(intentId);
|
|
13199
|
+
if (!intent) throw new Error(`Dispatch blocked: Intent "${intentId}" is unresolved`);
|
|
13200
|
+
return intent;
|
|
13201
|
+
});
|
|
13202
|
+
const policy = mergePolicies(intents.map(({ definition }) => definition.policy));
|
|
13203
|
+
const constraints = intents.flatMap(({ definition }) => definition.constraints);
|
|
13204
|
+
const requiresApproval = assertAuthorized(policy, constraints, input.target);
|
|
13205
|
+
const snapshotValue = { policy, constraints };
|
|
13206
|
+
return {
|
|
13207
|
+
snapshot: {
|
|
13208
|
+
hash: createHash2("sha256").update(stableJson(snapshotValue)).digest("hex"),
|
|
13209
|
+
...snapshotValue
|
|
13210
|
+
},
|
|
13211
|
+
operation,
|
|
13212
|
+
intents,
|
|
13213
|
+
trace: [
|
|
13214
|
+
pinned("trigger" in input.owner.definition ? "loop" : "goal", input.owner),
|
|
13215
|
+
input.target
|
|
13216
|
+
],
|
|
13217
|
+
requiresApproval
|
|
13218
|
+
};
|
|
13219
|
+
}
|
|
13220
|
+
function mergePolicies(policies) {
|
|
13221
|
+
const approvalOrder = ["none", "risky-actions", "all-actions"];
|
|
13222
|
+
return {
|
|
13223
|
+
approval: policies.reduce(
|
|
13224
|
+
(strictest, policy) => approvalOrder.indexOf(policy.approval) > approvalOrder.indexOf(strictest) ? policy.approval : strictest,
|
|
13225
|
+
"none"
|
|
13226
|
+
),
|
|
13227
|
+
authority: {
|
|
13228
|
+
allow: intersect(policies.map(({ authority }) => authority.allow)),
|
|
13229
|
+
deny: unique(policies.flatMap(({ authority }) => authority.deny))
|
|
13230
|
+
},
|
|
13231
|
+
budget: {
|
|
13232
|
+
maxRuns: Math.min(...policies.map(({ budget }) => budget.maxRuns)),
|
|
13233
|
+
maxTokens: Math.min(...policies.map(({ budget }) => budget.maxTokens)),
|
|
13234
|
+
maxCostUsd: Math.min(...policies.map(({ budget }) => budget.maxCostUsd)),
|
|
13235
|
+
maxDurationSeconds: Math.min(...policies.map(({ budget }) => budget.maxDurationSeconds))
|
|
13236
|
+
},
|
|
13237
|
+
maxConcurrentRuns: Math.min(...policies.map(({ maxConcurrentRuns }) => maxConcurrentRuns)),
|
|
13238
|
+
riskyActions: unique(policies.flatMap(({ riskyActions }) => riskyActions))
|
|
13239
|
+
};
|
|
13240
|
+
}
|
|
13241
|
+
function assertAuthorized(policy, constraints, target) {
|
|
13242
|
+
const action = `${target.kind}:${target.id}`;
|
|
13243
|
+
const matches = (patterns) => patterns.some((pattern) => pattern === "*" || pattern === target.id || pattern === action);
|
|
13244
|
+
if (matches(policy.authority.deny)) throw new Error(`Dispatch blocked: authority denies "${action}"`);
|
|
13245
|
+
if (!matches(policy.authority.allow)) throw new Error(`Dispatch blocked: authority does not allow "${action}"`);
|
|
13246
|
+
const matchingConstraints = constraints.filter(({ actions }) => matches(actions));
|
|
13247
|
+
const denied = matchingConstraints.find(({ effect }) => effect === "deny");
|
|
13248
|
+
if (denied) throw new Error(`Dispatch blocked by constraint "${denied.id}": ${denied.rule}`);
|
|
13249
|
+
const requiresApproval = policy.approval === "all-actions" || policy.approval === "risky-actions" && matches(policy.riskyActions) || matchingConstraints.some(({ effect }) => effect === "require-approval");
|
|
13250
|
+
return requiresApproval;
|
|
13251
|
+
}
|
|
13252
|
+
function pinned(kind, record2) {
|
|
13253
|
+
return { kind, id: record2.definition.id, revision: record2.revision };
|
|
13254
|
+
}
|
|
13255
|
+
function intersect(groups) {
|
|
13256
|
+
if (groups.length === 0) return [];
|
|
13257
|
+
if (groups.every((group) => group.includes("*"))) return ["*"];
|
|
13258
|
+
const candidates = unique(groups.flatMap((group) => group.filter((item) => item !== "*")));
|
|
13259
|
+
return candidates.filter((candidate) => groups.every((group) => group.includes("*") || group.includes(candidate)));
|
|
13260
|
+
}
|
|
13261
|
+
function unique(values) {
|
|
13262
|
+
return [...new Set(values)].sort();
|
|
13263
|
+
}
|
|
13264
|
+
function stableJson(value) {
|
|
13265
|
+
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
13266
|
+
if (value && typeof value === "object") {
|
|
13267
|
+
return `{${Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableJson(item)}`).join(",")}}`;
|
|
13268
|
+
}
|
|
13269
|
+
return JSON.stringify(value);
|
|
13270
|
+
}
|
|
13271
|
+
var init_policyResolver = __esm({
|
|
13272
|
+
"src/goal/policyResolver.ts"() {
|
|
13273
|
+
"use strict";
|
|
13274
|
+
}
|
|
13275
|
+
});
|
|
13276
|
+
|
|
13120
13277
|
// src/scripts/dispatchAgencyLoops.ts
|
|
13121
|
-
import {
|
|
13278
|
+
import { randomUUID } from "crypto";
|
|
13279
|
+
import {
|
|
13280
|
+
createLoopState as createLoopState2,
|
|
13281
|
+
createRun
|
|
13282
|
+
} from "@kody-ade/agency-domain";
|
|
13122
13283
|
async function dispatchAgencyLoopsWith(input) {
|
|
13123
13284
|
const repository = new AgencyModelRepository(input.backend, input.tenantId);
|
|
13124
|
-
const
|
|
13125
|
-
const
|
|
13126
|
-
|
|
13127
|
-
|
|
13128
|
-
).map((record2) => [record2.definition.id, record2.definition])
|
|
13129
|
-
);
|
|
13130
|
-
const loops = records.filter(
|
|
13131
|
-
(record2) => "trigger" in record2.definition
|
|
13285
|
+
const catalog = await repository.loadCatalog();
|
|
13286
|
+
const records = await repository.listManagedWork(catalog);
|
|
13287
|
+
const loops = records.flatMap(
|
|
13288
|
+
(record2) => "trigger" in record2.definition ? [{ ...record2, definition: record2.definition, state: record2.state }] : []
|
|
13132
13289
|
);
|
|
13133
13290
|
const results = [];
|
|
13134
13291
|
for (const record2 of loops) {
|
|
@@ -13147,17 +13304,56 @@ async function dispatchAgencyLoopsWith(input) {
|
|
|
13147
13304
|
results.push({ loopId: record2.definition.id, decision: "skipped", reason: decision.reason });
|
|
13148
13305
|
continue;
|
|
13149
13306
|
}
|
|
13307
|
+
let target;
|
|
13308
|
+
let policy;
|
|
13309
|
+
try {
|
|
13310
|
+
target = resolveTarget(record2.definition, catalog);
|
|
13311
|
+
policy = resolveDispatchPolicy({
|
|
13312
|
+
catalog,
|
|
13313
|
+
owner: { definition: record2.definition, revision: record2.revision },
|
|
13314
|
+
target: target.reference
|
|
13315
|
+
});
|
|
13316
|
+
} catch (error) {
|
|
13317
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
13318
|
+
await input.backend.recordSkippedAgencyDispatch(
|
|
13319
|
+
input.tenantId,
|
|
13320
|
+
decision.idempotencyKey,
|
|
13321
|
+
record2.definition.id,
|
|
13322
|
+
{ kind: "skip", reason, scheduledAt: decision.scheduledAt },
|
|
13323
|
+
now
|
|
13324
|
+
);
|
|
13325
|
+
results.push({ loopId: record2.definition.id, decision: "skipped", reason });
|
|
13326
|
+
continue;
|
|
13327
|
+
}
|
|
13150
13328
|
const leaseUntil = new Date(input.now.getTime() + 15 * 6e4).toISOString();
|
|
13151
|
-
const
|
|
13152
|
-
|
|
13153
|
-
|
|
13154
|
-
|
|
13329
|
+
const reservationId = `reservation-${randomUUID()}`;
|
|
13330
|
+
const correlationId = `corr-${randomUUID()}`;
|
|
13331
|
+
const trace = [policy.trace[0], ...target.intermediate, target.reference];
|
|
13332
|
+
const reservation = await input.backend.reserveAgencyDispatch(input.tenantId, {
|
|
13333
|
+
idempotencyKey: decision.idempotencyKey,
|
|
13334
|
+
loopId: record2.definition.id,
|
|
13155
13335
|
decision,
|
|
13156
13336
|
leaseUntil,
|
|
13337
|
+
reservationId,
|
|
13338
|
+
correlationId,
|
|
13339
|
+
policyHash: policy.snapshot.hash,
|
|
13340
|
+
effectivePolicy: policy.snapshot,
|
|
13341
|
+
definitionRefs: trace,
|
|
13342
|
+
maxConcurrentRuns: policy.snapshot.policy.maxConcurrentRuns,
|
|
13343
|
+
requiresApproval: policy.requiresApproval,
|
|
13344
|
+
approvalScopeKind: "loop",
|
|
13345
|
+
approvalScopeId: record2.definition.id,
|
|
13346
|
+
approvalAction: `${target.reference.kind}:${target.reference.id}`,
|
|
13157
13347
|
now
|
|
13158
|
-
);
|
|
13348
|
+
});
|
|
13159
13349
|
if (!reservation.acquired) {
|
|
13160
|
-
|
|
13350
|
+
const duplicate = reservation.reason === "duplicate";
|
|
13351
|
+
const reason = reservation.reason === "approval-required" ? "dispatch is waiting for approval" : reservation.reason === "concurrency-limit" ? "dispatch is waiting for policy capacity" : "trigger firing already reserved";
|
|
13352
|
+
results.push({
|
|
13353
|
+
loopId: record2.definition.id,
|
|
13354
|
+
decision: duplicate ? "duplicate" : "skipped",
|
|
13355
|
+
reason
|
|
13356
|
+
});
|
|
13161
13357
|
continue;
|
|
13162
13358
|
}
|
|
13163
13359
|
const runningState = createLoopState2({
|
|
@@ -13169,14 +13365,40 @@ async function dispatchAgencyLoopsWith(input) {
|
|
|
13169
13365
|
updatedAt: now
|
|
13170
13366
|
});
|
|
13171
13367
|
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
|
+
});
|
|
13172
13379
|
try {
|
|
13173
|
-
|
|
13380
|
+
await input.backend.createAgencyModelRun(
|
|
13381
|
+
input.tenantId,
|
|
13382
|
+
target.reference.kind,
|
|
13383
|
+
target.reference.id,
|
|
13384
|
+
activeRun,
|
|
13385
|
+
now
|
|
13386
|
+
);
|
|
13387
|
+
const output = await input.run(target.job);
|
|
13174
13388
|
const succeeded = output.exitCode === 0;
|
|
13389
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13390
|
+
await input.backend.finishAgencyModelRun(
|
|
13391
|
+
input.tenantId,
|
|
13392
|
+
terminalRun(activeRun, succeeded ? "succeeded" : "failed", finishedAt),
|
|
13393
|
+
finishedAt
|
|
13394
|
+
);
|
|
13175
13395
|
await input.backend.finishAgencyDispatch(
|
|
13176
13396
|
input.tenantId,
|
|
13177
13397
|
decision.idempotencyKey,
|
|
13398
|
+
reservationId,
|
|
13178
13399
|
succeeded ? "dispatched" : "failed",
|
|
13179
|
-
|
|
13400
|
+
finishedAt,
|
|
13401
|
+
runId
|
|
13180
13402
|
);
|
|
13181
13403
|
await repository.saveState(
|
|
13182
13404
|
createLoopState2({
|
|
@@ -13195,19 +13417,40 @@ async function dispatchAgencyLoopsWith(input) {
|
|
|
13195
13417
|
});
|
|
13196
13418
|
} catch (error) {
|
|
13197
13419
|
const reason = error instanceof Error ? error.message : String(error);
|
|
13198
|
-
|
|
13420
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13421
|
+
await input.backend.finishAgencyModelRun(input.tenantId, terminalRun(activeRun, "failed", finishedAt), finishedAt).catch(() => void 0);
|
|
13422
|
+
await input.backend.finishAgencyDispatch(
|
|
13423
|
+
input.tenantId,
|
|
13424
|
+
decision.idempotencyKey,
|
|
13425
|
+
reservationId,
|
|
13426
|
+
"failed",
|
|
13427
|
+
finishedAt,
|
|
13428
|
+
runId
|
|
13429
|
+
);
|
|
13199
13430
|
results.push({ loopId: record2.definition.id, decision: "failed", reason });
|
|
13200
13431
|
}
|
|
13201
13432
|
}
|
|
13202
13433
|
return results;
|
|
13203
13434
|
}
|
|
13204
|
-
function
|
|
13205
|
-
const
|
|
13206
|
-
if (!
|
|
13207
|
-
if (
|
|
13208
|
-
|
|
13209
|
-
}
|
|
13210
|
-
|
|
13435
|
+
function resolveTarget(loop, catalog) {
|
|
13436
|
+
const goal = loop.targetRef.kind === "goal" ? catalog.goals.get(loop.targetRef.id) : void 0;
|
|
13437
|
+
if (loop.targetRef.kind === "goal" && !goal) throw new Error(`Loop target Goal is missing: ${loop.targetRef.id}`);
|
|
13438
|
+
if (goal && goal.definition.operationId !== loop.operationId) {
|
|
13439
|
+
throw new Error(`Loop and target Goal must belong to the same Operation`);
|
|
13440
|
+
}
|
|
13441
|
+
const target = goal?.definition.executionRef ?? loop.targetRef;
|
|
13442
|
+
if (target.kind === "goal") throw new Error("Nested Goal target is invalid");
|
|
13443
|
+
const record2 = target.kind === "workflow" ? catalog.workflows.get(target.id) : catalog.capabilities.get(target.id);
|
|
13444
|
+
if (!record2) throw new Error(`Loop execution target is missing: ${target.kind}:${target.id}`);
|
|
13445
|
+
const reference = { kind: target.kind, id: target.id, revision: record2.revision };
|
|
13446
|
+
return {
|
|
13447
|
+
reference,
|
|
13448
|
+
intermediate: goal ? [{ kind: "goal", id: goal.definition.id, revision: goal.revision }] : [],
|
|
13449
|
+
job: target.kind === "workflow" ? { workflow: target.id, cliArgs: {}, flavor: "scheduled" } : { capability: target.id, cliArgs: {}, flavor: "scheduled" }
|
|
13450
|
+
};
|
|
13451
|
+
}
|
|
13452
|
+
function terminalRun(active, status, finishedAt) {
|
|
13453
|
+
return createRun({ ...active, status, finishedAt });
|
|
13211
13454
|
}
|
|
13212
13455
|
function repositoryTenant(config) {
|
|
13213
13456
|
const owner = config.github?.owner?.trim() || process.env.GITHUB_REPOSITORY?.split("/")[0]?.trim();
|
|
@@ -13220,6 +13463,7 @@ var init_dispatchAgencyLoops = __esm({
|
|
|
13220
13463
|
"use strict";
|
|
13221
13464
|
init_agencyModelRepository();
|
|
13222
13465
|
init_triggerDispatcher();
|
|
13466
|
+
init_policyResolver();
|
|
13223
13467
|
init_job();
|
|
13224
13468
|
init_state_backend();
|
|
13225
13469
|
dispatchAgencyLoops = async (ctx) => {
|
|
@@ -15078,9 +15322,9 @@ var init_kodyVariables = __esm({
|
|
|
15078
15322
|
});
|
|
15079
15323
|
|
|
15080
15324
|
// src/backendVault.ts
|
|
15081
|
-
import { createDecipheriv, createHash as
|
|
15325
|
+
import { createDecipheriv, createHash as createHash3 } from "crypto";
|
|
15082
15326
|
function cacheKey(owner, repo, masterKey) {
|
|
15083
|
-
const keyHash =
|
|
15327
|
+
const keyHash = createHash3("sha256").update(masterKey).digest("hex").slice(0, 16);
|
|
15084
15328
|
return `${owner}/${repo}:${keyHash}`.toLowerCase();
|
|
15085
15329
|
}
|
|
15086
15330
|
function decryptVault(payload, masterKey) {
|
|
@@ -15729,7 +15973,7 @@ var init_notifyTerminal = __esm({
|
|
|
15729
15973
|
});
|
|
15730
15974
|
|
|
15731
15975
|
// src/scripts/openAgencyModelReviewPr.ts
|
|
15732
|
-
import { createHash as
|
|
15976
|
+
import { createHash as createHash4 } from "crypto";
|
|
15733
15977
|
function parseAgencyModelProposal(raw) {
|
|
15734
15978
|
const text2 = raw.trim();
|
|
15735
15979
|
const jsonText = (text2.match(/^```(?:json)?\s*\n([\s\S]*?)\n```$/i)?.[1] ?? text2).trim();
|
|
@@ -15781,7 +16025,7 @@ function normalizeBundleFiles(bundle) {
|
|
|
15781
16025
|
});
|
|
15782
16026
|
}
|
|
15783
16027
|
function buildProposalId(issueNumber, bundle, sourceLabel) {
|
|
15784
|
-
const digest =
|
|
16028
|
+
const digest = createHash4("sha256").update(JSON.stringify({ issueNumber, sourceLabel, title: bundle.title, files: normalizeBundleFiles(bundle) })).digest("hex").slice(0, 16);
|
|
15785
16029
|
return `issue-${issueNumber}-${digest}`;
|
|
15786
16030
|
}
|
|
15787
16031
|
function isDryRun(ctx) {
|
|
@@ -17853,9 +18097,9 @@ var init_runFlow = __esm({
|
|
|
17853
18097
|
});
|
|
17854
18098
|
|
|
17855
18099
|
// src/scripts/previewBuildHelpers.ts
|
|
17856
|
-
import { createDecipheriv as createDecipheriv2, createHash as
|
|
18100
|
+
import { createDecipheriv as createDecipheriv2, createHash as createHash5, hkdfSync as hkdfSync2 } from "crypto";
|
|
17857
18101
|
function shortHash(s) {
|
|
17858
|
-
return
|
|
18102
|
+
return createHash5("sha256").update(s).digest("hex").slice(0, 6);
|
|
17859
18103
|
}
|
|
17860
18104
|
function previewAppName(repo, pr) {
|
|
17861
18105
|
const [owner, name] = repo.split("/");
|
|
@@ -17888,7 +18132,7 @@ function formatPreviewComment(args) {
|
|
|
17888
18132
|
].join("\n");
|
|
17889
18133
|
}
|
|
17890
18134
|
function defaultImageTag(repo, ref) {
|
|
17891
|
-
return
|
|
18135
|
+
return createHash5("sha256").update(`${repo}@${ref}`).digest("hex").slice(0, 12);
|
|
17892
18136
|
}
|
|
17893
18137
|
var init_previewBuildHelpers = __esm({
|
|
17894
18138
|
"src/scripts/previewBuildHelpers.ts"() {
|
|
@@ -25313,7 +25557,7 @@ init_config();
|
|
|
25313
25557
|
init_fetchRepoMcp();
|
|
25314
25558
|
|
|
25315
25559
|
// src/servers/mcpHttpServer.ts
|
|
25316
|
-
import { randomUUID } from "crypto";
|
|
25560
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
25317
25561
|
import { createServer as createServer4 } from "http";
|
|
25318
25562
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
25319
25563
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
@@ -25322,7 +25566,7 @@ function buildMcpHttpServer(opts) {
|
|
|
25322
25566
|
const transports = /* @__PURE__ */ new Map();
|
|
25323
25567
|
for (const route of opts.routes) {
|
|
25324
25568
|
const transport = new StreamableHTTPServerTransport({
|
|
25325
|
-
sessionIdGenerator: () =>
|
|
25569
|
+
sessionIdGenerator: () => randomUUID2()
|
|
25326
25570
|
});
|
|
25327
25571
|
transports.set(route.path, transport);
|
|
25328
25572
|
routes.set(route.path, route.name);
|
|
@@ -25894,7 +26138,7 @@ init_config();
|
|
|
25894
26138
|
|
|
25895
26139
|
// src/definition-hydration.ts
|
|
25896
26140
|
init_state_backend();
|
|
25897
|
-
import { createHash as
|
|
26141
|
+
import { createHash as createHash6 } from "crypto";
|
|
25898
26142
|
import * as fs50 from "fs";
|
|
25899
26143
|
import * as path51 from "path";
|
|
25900
26144
|
var SLUG_RE2 = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
@@ -25915,7 +26159,7 @@ function normalizeDefinitionBundle(bundle) {
|
|
|
25915
26159
|
return { schemaVersion: 1, files };
|
|
25916
26160
|
}
|
|
25917
26161
|
function definitionVersion(bundle) {
|
|
25918
|
-
return `sha256:${
|
|
26162
|
+
return `sha256:${createHash6("sha256").update(JSON.stringify(normalizeDefinitionBundle(bundle))).digest("hex")}`;
|
|
25919
26163
|
}
|
|
25920
26164
|
function verifyDefinition(definition) {
|
|
25921
26165
|
if (!SLUG_RE2.test(definition.slug)) throw new Error(`invalid definition slug: ${definition.slug}`);
|
|
@@ -26016,12 +26260,12 @@ import { createServer as createServer5 } from "http";
|
|
|
26016
26260
|
|
|
26017
26261
|
// src/pool/agency-loop-tick.ts
|
|
26018
26262
|
function normalizeRepositories(repositories) {
|
|
26019
|
-
const
|
|
26263
|
+
const unique2 = /* @__PURE__ */ new Set();
|
|
26020
26264
|
for (const raw of repositories) {
|
|
26021
26265
|
const repo = raw.trim().toLowerCase();
|
|
26022
|
-
if (/^[^/\s]+\/[^/\s]+$/.test(repo))
|
|
26266
|
+
if (/^[^/\s]+\/[^/\s]+$/.test(repo)) unique2.add(repo);
|
|
26023
26267
|
}
|
|
26024
|
-
return [...
|
|
26268
|
+
return [...unique2].sort();
|
|
26025
26269
|
}
|
|
26026
26270
|
async function runAgencyLoopTick(deps) {
|
|
26027
26271
|
const repositories = normalizeRepositories(await deps.discover());
|
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.416",
|
|
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",
|
|
@@ -12,32 +12,10 @@
|
|
|
12
12
|
"templates",
|
|
13
13
|
"kody.config.schema.json"
|
|
14
14
|
],
|
|
15
|
-
"scripts": {
|
|
16
|
-
"kody:run": "tsx bin/kody.ts",
|
|
17
|
-
"serve": "tsx bin/kody.ts serve",
|
|
18
|
-
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
19
|
-
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
20
|
-
"clean:dist": "node scripts/clean-dist.cjs",
|
|
21
|
-
"build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
|
|
22
|
-
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
23
|
-
"pretest": "pnpm check:modularity",
|
|
24
|
-
"test": "vitest run tests/unit tests/int --coverage",
|
|
25
|
-
"posttest": "tsx scripts/check-coverage-floor.ts",
|
|
26
|
-
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
27
|
-
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
28
|
-
"test:all": "vitest run tests --no-coverage",
|
|
29
|
-
"typecheck": "tsc --noEmit",
|
|
30
|
-
"lint": "biome check",
|
|
31
|
-
"lint:fix": "biome check --write",
|
|
32
|
-
"format": "biome format --write",
|
|
33
|
-
"verify:package": "node scripts/verify-package-tarball.cjs",
|
|
34
|
-
"brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
|
|
35
|
-
"prepublishOnly": "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm build && pnpm verify:package"
|
|
36
|
-
},
|
|
37
15
|
"dependencies": {
|
|
38
|
-
"@kody-ade/agency-domain": "0.1.1",
|
|
39
16
|
"@actions/cache": "^6.0.0",
|
|
40
17
|
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
18
|
+
"@kody-ade/agency-domain": "0.4.0",
|
|
41
19
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
42
20
|
"convex": "^1.17.0",
|
|
43
21
|
"zod": "^4.0.0"
|
|
@@ -59,5 +37,26 @@
|
|
|
59
37
|
"url": "git+https://github.com/aharonyaircohen/kody-engine.git"
|
|
60
38
|
},
|
|
61
39
|
"homepage": "https://github.com/aharonyaircohen/kody-engine",
|
|
62
|
-
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
|
|
63
|
-
|
|
40
|
+
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
|
|
41
|
+
"scripts": {
|
|
42
|
+
"kody:run": "tsx bin/kody.ts",
|
|
43
|
+
"serve": "tsx bin/kody.ts serve",
|
|
44
|
+
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
45
|
+
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
46
|
+
"clean:dist": "node scripts/clean-dist.cjs",
|
|
47
|
+
"build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
|
|
48
|
+
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
49
|
+
"pretest": "pnpm check:modularity",
|
|
50
|
+
"test": "vitest run tests/unit tests/int --coverage",
|
|
51
|
+
"posttest": "tsx scripts/check-coverage-floor.ts",
|
|
52
|
+
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
53
|
+
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
54
|
+
"test:all": "vitest run tests --no-coverage",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"lint": "biome check",
|
|
57
|
+
"lint:fix": "biome check --write",
|
|
58
|
+
"format": "biome format --write",
|
|
59
|
+
"verify:package": "node scripts/verify-package-tarball.cjs",
|
|
60
|
+
"brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
|
|
61
|
+
}
|
|
62
|
+
}
|