@kody-ade/kody-engine 0.4.353 → 0.4.355

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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +137 -35
  2. package/package.json +1 -1
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.353",
18
+ version: "0.4.355",
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",
@@ -2436,6 +2436,53 @@ var init_registry = __esm({
2436
2436
  }
2437
2437
  });
2438
2438
 
2439
+ // src/trustPolicy.ts
2440
+ function trustSubjectKey(subject) {
2441
+ return `${subject.kind}:${subject.id}`;
2442
+ }
2443
+ function parseTrustMode(rawJson, subject) {
2444
+ return parseTrustModeOverride(rawJson, subject) ?? "ask";
2445
+ }
2446
+ function parseTrustModeOverride(rawJson, subject) {
2447
+ const key = trustSubjectKey(subject);
2448
+ try {
2449
+ const parsed = JSON.parse(rawJson);
2450
+ const rawMode = subject.kind === "capability" ? parsed?.capabilities?.[subject.id]?.mode : parsed?.subjects?.[key]?.mode;
2451
+ if (rawMode === "auto" || rawMode === "ask") return rawMode;
2452
+ return null;
2453
+ } catch {
2454
+ return null;
2455
+ }
2456
+ }
2457
+ function readTrustMode(stateOrRepoSlug, repoSlugOrSubject, maybeSubject) {
2458
+ const mode = typeof stateOrRepoSlug === "string" ? readTrustModeOverride(stateOrRepoSlug, repoSlugOrSubject) : readTrustModeOverride(stateOrRepoSlug, repoSlugOrSubject, maybeSubject);
2459
+ return mode ?? "ask";
2460
+ }
2461
+ function readTrustModeOverride(stateOrRepoSlug, repoSlugOrSubject, maybeSubject) {
2462
+ const state = typeof stateOrRepoSlug === "string" ? void 0 : stateOrRepoSlug;
2463
+ const repoSlug = typeof stateOrRepoSlug === "string" ? stateOrRepoSlug : typeof repoSlugOrSubject === "string" ? repoSlugOrSubject : "";
2464
+ const subject = typeof stateOrRepoSlug === "string" ? repoSlugOrSubject : maybeSubject;
2465
+ if (!subject?.id) return null;
2466
+ try {
2467
+ const loaded = readStateText({ state: state ?? defaultStateForRepoSlug(repoSlug) }, void 0, TRUST_FILE_PATH);
2468
+ return loaded ? parseTrustModeOverride(loaded.content, subject) : null;
2469
+ } catch {
2470
+ return null;
2471
+ }
2472
+ }
2473
+ function defaultStateForRepoSlug(repoSlug) {
2474
+ const [owner, repo] = repoSlug.split("/");
2475
+ return { repo: `${owner}/kody-state`, path: repo ?? repoSlug };
2476
+ }
2477
+ var TRUST_FILE_PATH;
2478
+ var init_trustPolicy = __esm({
2479
+ "src/trustPolicy.ts"() {
2480
+ "use strict";
2481
+ init_stateRepo();
2482
+ TRUST_FILE_PATH = "state/trust.json";
2483
+ }
2484
+ });
2485
+
2439
2486
  // src/capabilityMcp.ts
2440
2487
  var capabilityMcp_exports = {};
2441
2488
  __export(capabilityMcp_exports, {
@@ -2581,28 +2628,14 @@ function readLedger(label) {
2581
2628
  }
2582
2629
  }
2583
2630
  function parseCapabilityTrustMode(rawJson, capabilitySlug) {
2584
- try {
2585
- const parsed = JSON.parse(rawJson);
2586
- return parsed?.capabilities?.[capabilitySlug]?.mode === "auto" ? "auto" : "ask";
2587
- } catch {
2588
- return "ask";
2589
- }
2590
- }
2591
- function defaultStateForRepoSlug(repoSlug) {
2592
- const [owner, repo] = repoSlug.split("/");
2593
- return { repo: `${owner}/kody-state`, path: repo ?? repoSlug };
2631
+ return parseTrustMode(rawJson, { kind: "capability", id: capabilitySlug });
2594
2632
  }
2595
2633
  function readCapabilityTrustMode(stateOrRepoSlug, repoSlugOrCapabilitySlug, maybeCapabilitySlug) {
2596
2634
  const state = typeof stateOrRepoSlug === "string" ? void 0 : stateOrRepoSlug;
2597
2635
  const repoSlug = typeof stateOrRepoSlug === "string" ? stateOrRepoSlug : repoSlugOrCapabilitySlug ?? "";
2598
2636
  const capabilitySlug = typeof stateOrRepoSlug === "string" ? repoSlugOrCapabilitySlug : maybeCapabilitySlug;
2599
2637
  if (!capabilitySlug) return "ask";
2600
- try {
2601
- const loaded = readStateText({ state: state ?? defaultStateForRepoSlug(repoSlug) }, void 0, TRUST_FILE_PATH);
2602
- return loaded ? parseCapabilityTrustMode(loaded.content, capabilitySlug) : "ask";
2603
- } catch {
2604
- return "ask";
2605
- }
2638
+ return readTrustMode(state, repoSlug, { kind: "capability", id: capabilitySlug });
2606
2639
  }
2607
2640
  function readThread(repoSlug, number, limit = 10) {
2608
2641
  const meta = JSON.parse(gh(["api", `repos/${repoSlug}/issues/${number}`]));
@@ -2932,17 +2965,16 @@ function buildCapabilityMcpServer(opts) {
2932
2965
  });
2933
2966
  return { server };
2934
2967
  }
2935
- var FAIL_CONCLUSIONS, RUNNING_STATUSES, TRUST_FILE_PATH, THREAD_BODY_MAX, CHECK_FAIL_CONCLUSIONS, DEFAULT_IGNORE_CHECKS, trackMarker, commentMarker, GATE_EXEMPT_CAPABILITIES, CAPABILITY_MCP_TOOL_NAMES;
2968
+ var FAIL_CONCLUSIONS, RUNNING_STATUSES, THREAD_BODY_MAX, CHECK_FAIL_CONCLUSIONS, DEFAULT_IGNORE_CHECKS, trackMarker, commentMarker, GATE_EXEMPT_CAPABILITIES, CAPABILITY_MCP_TOOL_NAMES;
2936
2969
  var init_capabilityMcp = __esm({
2937
2970
  "src/capabilityMcp.ts"() {
2938
2971
  "use strict";
2939
2972
  init_dashboardCmsMcp();
2940
2973
  init_issue();
2941
2974
  init_registry();
2942
- init_stateRepo();
2975
+ init_trustPolicy();
2943
2976
  FAIL_CONCLUSIONS = /* @__PURE__ */ new Set(["FAILURE", "TIMED_OUT", "ACTION_REQUIRED", "STARTUP_FAILURE", "CANCELLED"]);
2944
2977
  RUNNING_STATUSES = /* @__PURE__ */ new Set(["IN_PROGRESS", "QUEUED", "PENDING", "WAITING", "REQUESTED"]);
2945
- TRUST_FILE_PATH = "state/trust.json";
2946
2978
  THREAD_BODY_MAX = 4e3;
2947
2979
  CHECK_FAIL_CONCLUSIONS = /* @__PURE__ */ new Set(["FAILURE", "TIMED_OUT", "STARTUP_FAILURE", "ACTION_REQUIRED"]);
2948
2980
  DEFAULT_IGNORE_CHECKS = ["run", "kody", "capability-tick", "agent-ask", "chat"];
@@ -9335,14 +9367,16 @@ function scalarFacts(facts) {
9335
9367
  )
9336
9368
  );
9337
9369
  }
9338
- function autonomyBlockReason(ctx, goalId, goal, dispatch2) {
9370
+ function autonomyBlockReason(ctx, goalId, goal, goalState, dispatch2) {
9339
9371
  if (ctx.data.jobForce === true) return null;
9340
- if (goal.runWithoutApproval !== true) {
9372
+ const selfMode = firstTrustOverride(ctx, subjectCandidates(managedModelSubjectKind(goal), goalId, goalState));
9373
+ if (selfMode === "ask" || selfMode !== "auto" && goal.runWithoutApproval !== true) {
9341
9374
  return `Run without approval is off for ${managedModelKind(goal)} ${goalId}`;
9342
9375
  }
9343
9376
  if (dispatch2.workflow) {
9344
- const workflow = readWorkflowDefinition(ctx.config, ctx.cwd, dispatch2.workflow);
9345
- if (workflow && workflow.runWithoutApproval !== true) {
9377
+ const workflowMode = firstTrustOverride(ctx, [{ kind: "workflow", id: dispatch2.workflow }]);
9378
+ const workflow = workflowMode === "auto" ? null : readWorkflowDefinition(ctx.config, ctx.cwd, dispatch2.workflow);
9379
+ if (workflowMode === "ask" || workflowMode !== "auto" && workflow && workflow.runWithoutApproval !== true) {
9346
9380
  return `Run without approval is off for workflow ${dispatch2.workflow}`;
9347
9381
  }
9348
9382
  }
@@ -9350,16 +9384,65 @@ function autonomyBlockReason(ctx, goalId, goal, dispatch2) {
9350
9384
  if (targetGoal && targetGoal !== goalId) {
9351
9385
  const target = fetchGoalState(ctx.config, targetGoal, ctx.cwd);
9352
9386
  const targetManaged = target ? managedGoalFromState(expandManagedGoalState(target)) : null;
9353
- if (targetManaged && targetManaged.runWithoutApproval !== true) {
9387
+ const targetMode = targetManaged ? firstTrustOverride(ctx, subjectCandidates(managedModelSubjectKind(targetManaged), targetGoal, target)) : null;
9388
+ if (targetMode === "ask" || targetMode !== "auto" && targetManaged && targetManaged.runWithoutApproval !== true) {
9354
9389
  return `Run without approval is off for goal ${targetGoal}`;
9355
9390
  }
9356
9391
  }
9357
9392
  return null;
9358
9393
  }
9394
+ function scheduleWaitDecision(previousScheduleState, plannedDecision, reason) {
9395
+ const at = plannedDecision.scheduleState.lastGoalTickAt;
9396
+ return {
9397
+ kind: "wait",
9398
+ reason,
9399
+ scheduleState: {
9400
+ mode: "agentLoop",
9401
+ lastGoalTickAt: previousScheduleState?.lastGoalTickAt ?? plannedDecision.scheduleState.lastGoalTickAt,
9402
+ lastDecision: { kind: "wait", reason, at },
9403
+ capabilities: previousScheduleState?.capabilities ?? unmarkPlannedCapabilityDispatch(plannedDecision.scheduleState, at)
9404
+ }
9405
+ };
9406
+ }
9407
+ function unmarkPlannedCapabilityDispatch(scheduleState, at) {
9408
+ return Object.fromEntries(
9409
+ Object.entries(scheduleState.capabilities).map(([slug2, status]) => {
9410
+ if (status.lastFiredAt !== at) return [slug2, status];
9411
+ const rest = { ...status };
9412
+ delete rest.lastFiredAt;
9413
+ return [slug2, rest];
9414
+ })
9415
+ );
9416
+ }
9359
9417
  function managedModelKind(goal) {
9360
9418
  return goal.loopTarget || goal.schedule ? "Loop" : "Goal";
9361
9419
  }
9362
- function stageAutonomyBlocked(data, goalId, goal, goalState, reason) {
9420
+ function managedModelSubjectKind(goal) {
9421
+ return managedModelKind(goal) === "Loop" ? "loop" : "goal";
9422
+ }
9423
+ function subjectCandidates(kind, id, state) {
9424
+ const ids = /* @__PURE__ */ new Set();
9425
+ ids.add(id);
9426
+ if (state?.extra) {
9427
+ for (const key of ["sourceTemplate", "templateId", "template"]) {
9428
+ const value = state.extra[key];
9429
+ if (typeof value === "string" && value.trim()) ids.add(value.trim());
9430
+ }
9431
+ }
9432
+ return [...ids].map((candidate) => ({ kind, id: candidate }));
9433
+ }
9434
+ function firstTrustOverride(ctx, subjects) {
9435
+ if (!ctx.config.state) return null;
9436
+ const repoSlug = ctx.config.github?.owner && ctx.config.github?.repo ? `${ctx.config.github.owner}/${ctx.config.github.repo}` : "";
9437
+ for (const subject of subjects) {
9438
+ const mode = readTrustModeOverride(ctx.config.state, repoSlug, subject);
9439
+ if (mode) return mode;
9440
+ }
9441
+ return null;
9442
+ }
9443
+ function stageAutonomyBlocked(data, goalId, goal, goalState, waitDecision, previousScheduleState) {
9444
+ const reason = typeof waitDecision === "string" ? waitDecision : waitDecision.reason;
9445
+ const scheduleState = typeof waitDecision === "string" ? void 0 : waitDecision.scheduleState;
9363
9446
  stageGoalRunLogEvent(data, goalId, {
9364
9447
  source: "goal-manager",
9365
9448
  event: "goal.tick.wait",
@@ -9369,6 +9452,18 @@ function stageAutonomyBlocked(data, goalId, goal, goalState, reason) {
9369
9452
  status: "wait",
9370
9453
  reason,
9371
9454
  goal: goalRunLogSnapshot(goalId, goalState, goal),
9455
+ ...scheduleState ? {
9456
+ inspection: {
9457
+ previousScheduleState,
9458
+ scheduleState
9459
+ },
9460
+ change: {
9461
+ scheduleState: {
9462
+ previousDecision: previousScheduleState?.lastDecision,
9463
+ nextDecision: scheduleState.lastDecision
9464
+ }
9465
+ }
9466
+ } : {},
9372
9467
  decision: { kind: "wait", reason }
9373
9468
  });
9374
9469
  }
@@ -9550,6 +9645,7 @@ var init_advanceManagedGoal = __esm({
9550
9645
  init_targetLoopResolution();
9551
9646
  init_typeDefinitions();
9552
9647
  init_issue();
9648
+ init_trustPolicy();
9553
9649
  init_workflowDefinitions();
9554
9650
  init_goalCapabilityScheduling();
9555
9651
  advanceManagedGoal = async (ctx) => {
@@ -9643,14 +9739,17 @@ var init_advanceManagedGoal = __esm({
9643
9739
  }
9644
9740
  restoreGoalIdFact();
9645
9741
  goal.raw = writeManagedGoalToState({ ...goal.raw, state: goal.state }, managed);
9646
- goal.raw.extra.scheduleState = decision2.scheduleState;
9647
- ctx.data.managedGoalDecision = decision2;
9648
- const autonomyBlock = decision2.kind === "dispatch" && decision2.dispatch ? autonomyBlockReason(ctx, goal.id, managed, decision2.dispatch) : null;
9742
+ const autonomyBlock = decision2.kind === "dispatch" && decision2.dispatch ? autonomyBlockReason(ctx, goal.id, managed, goal.raw, decision2.dispatch) : null;
9649
9743
  if (autonomyBlock) {
9650
- stageAutonomyBlocked(ctx.data, goal.id, managed, goal.state, autonomyBlock);
9744
+ const waitDecision = scheduleWaitDecision(previousScheduleState, decision2, autonomyBlock);
9745
+ goal.raw.extra.scheduleState = waitDecision.scheduleState;
9746
+ ctx.data.managedGoalDecision = waitDecision;
9747
+ stageAutonomyBlocked(ctx.data, goal.id, managed, goal.state, waitDecision, previousScheduleState);
9651
9748
  ctx.output.reason = autonomyBlock;
9652
9749
  return;
9653
9750
  }
9751
+ goal.raw.extra.scheduleState = decision2.scheduleState;
9752
+ ctx.data.managedGoalDecision = decision2;
9654
9753
  if (decision2.kind === "dispatch" && decision2.dispatch) {
9655
9754
  ctx.output.nextDispatch = {
9656
9755
  ...decision2.dispatch.action ? { action: decision2.dispatch.action } : {},
@@ -9705,14 +9804,17 @@ var init_advanceManagedGoal = __esm({
9705
9804
  });
9706
9805
  restoreGoalIdFact();
9707
9806
  goal.raw = writeManagedGoalToState({ ...goal.raw, state: goal.state }, managed);
9708
- goal.raw.extra.scheduleState = decision2.scheduleState;
9709
- ctx.data.managedGoalDecision = decision2;
9710
- const autonomyBlock = decision2.kind === "dispatch" && decision2.dispatch ? autonomyBlockReason(ctx, goal.id, managed, decision2.dispatch) : null;
9807
+ const autonomyBlock = decision2.kind === "dispatch" && decision2.dispatch ? autonomyBlockReason(ctx, goal.id, managed, goal.raw, decision2.dispatch) : null;
9711
9808
  if (autonomyBlock) {
9712
- stageAutonomyBlocked(ctx.data, goal.id, managed, goal.state, autonomyBlock);
9809
+ const waitDecision = scheduleWaitDecision(previousScheduleState, decision2, autonomyBlock);
9810
+ goal.raw.extra.scheduleState = waitDecision.scheduleState;
9811
+ ctx.data.managedGoalDecision = waitDecision;
9812
+ stageAutonomyBlocked(ctx.data, goal.id, managed, goal.state, waitDecision, previousScheduleState);
9713
9813
  ctx.output.reason = autonomyBlock;
9714
9814
  return;
9715
9815
  }
9816
+ goal.raw.extra.scheduleState = decision2.scheduleState;
9817
+ ctx.data.managedGoalDecision = decision2;
9716
9818
  if (decision2.kind === "dispatch" && decision2.dispatch) {
9717
9819
  ctx.output.nextDispatch = {
9718
9820
  capability: decision2.dispatch.capability,
@@ -9772,7 +9874,7 @@ var init_advanceManagedGoal = __esm({
9772
9874
  ...decision.implementation ? { implementation: decision.implementation } : {},
9773
9875
  cliArgs: decision.cliArgs
9774
9876
  };
9775
- const autonomyBlock = autonomyBlockReason(ctx, goal.id, managed, planned);
9877
+ const autonomyBlock = autonomyBlockReason(ctx, goal.id, managed, goal.raw, planned);
9776
9878
  if (autonomyBlock) {
9777
9879
  stageAutonomyBlocked(ctx.data, goal.id, managed, goal.state, autonomyBlock);
9778
9880
  ctx.output.reason = autonomyBlock;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.353",
3
+ "version": "0.4.355",
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",