@kody-ade/kody-engine 0.4.395 → 0.4.397

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 +244 -217
  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.395",
18
+ version: "0.4.397",
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",
@@ -2549,6 +2549,222 @@ var init_registry = __esm({
2549
2549
  }
2550
2550
  });
2551
2551
 
2552
+ // src/chat/convex-client.ts
2553
+ import { ConvexHttpClient } from "convex/browser";
2554
+ function isPlainObject2(value) {
2555
+ if (value === null || typeof value !== "object") return false;
2556
+ const proto = Object.getPrototypeOf(value);
2557
+ return proto === Object.prototype || proto === null;
2558
+ }
2559
+ function deepMapKeys(value, mapKey) {
2560
+ if (Array.isArray(value)) return value.map((item) => deepMapKeys(item, mapKey));
2561
+ if (isPlainObject2(value)) {
2562
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [mapKey(key), deepMapKeys(item, mapKey)]));
2563
+ }
2564
+ return value;
2565
+ }
2566
+ function deepEscapeKeys(value) {
2567
+ return deepMapKeys(value, (k) => NEEDS_ESCAPE.test(k) ? `${ESCAPE_CHAR}${k}` : k);
2568
+ }
2569
+ function deepUnescapeKeys(value) {
2570
+ return deepMapKeys(value, (k) => k.startsWith(ESCAPE_CHAR) ? k.slice(1) : k);
2571
+ }
2572
+ function injectServiceKey(args, serviceKey = process.env.KODY_SERVICE_KEY) {
2573
+ if (!serviceKey) return args;
2574
+ if (args === void 0) return { serviceKey };
2575
+ if (typeof args !== "object" || args === null || Array.isArray(args)) return args;
2576
+ return { ...args, serviceKey };
2577
+ }
2578
+ function withEscapedKeys(client, serviceKey = process.env.KODY_SERVICE_KEY) {
2579
+ return new Proxy(client, {
2580
+ get(target, prop, receiver) {
2581
+ if (CALL_METHODS.includes(prop)) {
2582
+ const method = Reflect.get(target, prop, target);
2583
+ return async (fn, args) => {
2584
+ const authed = injectServiceKey(args, serviceKey);
2585
+ const result = await method.call(target, fn, authed === void 0 ? void 0 : deepEscapeKeys(authed));
2586
+ return deepUnescapeKeys(result);
2587
+ };
2588
+ }
2589
+ const value = Reflect.get(target, prop, receiver);
2590
+ return typeof value === "function" ? value.bind(target) : value;
2591
+ }
2592
+ });
2593
+ }
2594
+ function createConvexClientFromEnv(env = process.env) {
2595
+ const url = env.CONVEX_URL?.trim();
2596
+ if (!url) return null;
2597
+ return withEscapedKeys(new ConvexHttpClient(url), env.KODY_SERVICE_KEY);
2598
+ }
2599
+ var ESCAPE_CHAR, NEEDS_ESCAPE, CALL_METHODS;
2600
+ var init_convex_client = __esm({
2601
+ "src/chat/convex-client.ts"() {
2602
+ "use strict";
2603
+ ESCAPE_CHAR = "~";
2604
+ NEEDS_ESCAPE = /^[$_~]/;
2605
+ CALL_METHODS = ["query", "mutation", "action"];
2606
+ }
2607
+ });
2608
+
2609
+ // src/state-backend.ts
2610
+ import { anyApi } from "convex/server";
2611
+ function requireTenant(tenantId) {
2612
+ const value = tenantId.trim();
2613
+ if (!/^[^/\s]+\/[^/\s]+$/.test(value)) throw new Error("tenantId must be an owner/repository pair");
2614
+ return value;
2615
+ }
2616
+ function requireNonEmpty(value, name) {
2617
+ const normalized = value.trim();
2618
+ if (!normalized) throw new Error(`${name} must not be empty`);
2619
+ return normalized;
2620
+ }
2621
+ function createStateBackendFromEnv(env = process.env, client) {
2622
+ const url = env.CONVEX_URL?.trim();
2623
+ const serviceKey = env.KODY_SERVICE_KEY?.trim();
2624
+ if (!url || !serviceKey) throw new Error("CONVEX_URL and KODY_SERVICE_KEY are required");
2625
+ const transport = client ?? createConvexClientFromEnv(env);
2626
+ return {
2627
+ async get(tenantId, taskKey, kind) {
2628
+ const result = await transport.query(anyApi.taskState.get, {
2629
+ tenantId: requireTenant(tenantId),
2630
+ taskKey: requireNonEmpty(taskKey, "taskKey"),
2631
+ kind: requireNonEmpty(kind, "kind")
2632
+ });
2633
+ return result ?? null;
2634
+ },
2635
+ async save(tenantId, taskKey, kind, doc, expectedUpdatedAt) {
2636
+ await transport.mutation(anyApi.taskState.save, {
2637
+ tenantId: requireTenant(tenantId),
2638
+ taskKey: requireNonEmpty(taskKey, "taskKey"),
2639
+ kind: requireNonEmpty(kind, "kind"),
2640
+ doc,
2641
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2642
+ ...expectedUpdatedAt ? { expectedUpdatedAt } : {}
2643
+ });
2644
+ },
2645
+ async getRepoDoc(tenantId, kind) {
2646
+ const result = await transport.query(anyApi.repoDocs.get, {
2647
+ tenantId: requireTenant(tenantId),
2648
+ kind: requireNonEmpty(kind, "kind")
2649
+ });
2650
+ return result ?? null;
2651
+ },
2652
+ async listRepoDocs(tenantId, prefix) {
2653
+ const result = await transport.query(anyApi.repoDocs.listByPrefix, {
2654
+ tenantId: requireTenant(tenantId),
2655
+ prefix: requireNonEmpty(prefix, "prefix")
2656
+ });
2657
+ return Array.isArray(result) ? result : [];
2658
+ },
2659
+ async saveRepoDoc(tenantId, kind, doc, expectedUpdatedAt) {
2660
+ await transport.mutation(anyApi.repoDocs.save, {
2661
+ tenantId: requireTenant(tenantId),
2662
+ kind: requireNonEmpty(kind, "kind"),
2663
+ doc,
2664
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2665
+ ...expectedUpdatedAt ? { expectedUpdatedAt } : {}
2666
+ });
2667
+ },
2668
+ async getGoal(tenantId, goalId) {
2669
+ const result = await transport.query(anyApi.goals.get, {
2670
+ tenantId: requireTenant(tenantId),
2671
+ goalId: requireNonEmpty(goalId, "goalId")
2672
+ });
2673
+ return result ?? null;
2674
+ },
2675
+ async listGoals(tenantId) {
2676
+ const result = await transport.query(anyApi.goals.list, { tenantId: requireTenant(tenantId) });
2677
+ return Array.isArray(result) ? result : [];
2678
+ },
2679
+ async saveGoal(tenantId, goalId, state, updatedAt, expectedUpdatedAt) {
2680
+ await transport.mutation(anyApi.goals.save, {
2681
+ tenantId: requireTenant(tenantId),
2682
+ goalId: requireNonEmpty(goalId, "goalId"),
2683
+ state,
2684
+ updatedAt,
2685
+ ...expectedUpdatedAt ? { expectedUpdatedAt } : {}
2686
+ });
2687
+ },
2688
+ async appendDailyLog(tenantId, stream, date, entry) {
2689
+ await transport.mutation(anyApi.dailyLogs.append, {
2690
+ tenantId: requireTenant(tenantId),
2691
+ stream,
2692
+ date: requireNonEmpty(date, "date"),
2693
+ entry
2694
+ });
2695
+ },
2696
+ async saveAgencyRun(tenantId, runId, subjectType, subjectId, run, updatedAt) {
2697
+ await transport.mutation(anyApi.agencyRuns.save, {
2698
+ tenantId: requireTenant(tenantId),
2699
+ runId: requireNonEmpty(runId, "runId"),
2700
+ subjectType,
2701
+ subjectId: requireNonEmpty(subjectId, "subjectId"),
2702
+ run,
2703
+ updatedAt
2704
+ });
2705
+ },
2706
+ async appendRunEvent(tenantId, runId, goalId, event, time) {
2707
+ await transport.mutation(anyApi.runEvents.append, {
2708
+ tenantId: requireTenant(tenantId),
2709
+ runId: requireNonEmpty(runId, "runId"),
2710
+ ...goalId ? { goalId: requireNonEmpty(goalId, "goalId") } : {},
2711
+ event,
2712
+ time: requireNonEmpty(time, "time")
2713
+ });
2714
+ },
2715
+ async getManifest(tenantId, kind) {
2716
+ const result = await transport.query(anyApi.manifests.get, {
2717
+ tenantId: requireTenant(tenantId),
2718
+ kind: requireNonEmpty(kind, "kind")
2719
+ });
2720
+ return result ?? null;
2721
+ },
2722
+ async saveReport(tenantId, slug, runId, title, body, meta, updatedAt) {
2723
+ await transport.mutation(anyApi.reports.save, {
2724
+ tenantId: requireTenant(tenantId),
2725
+ slug: requireNonEmpty(slug, "slug"),
2726
+ runId: requireNonEmpty(runId, "runId"),
2727
+ title,
2728
+ body,
2729
+ meta,
2730
+ updatedAt
2731
+ });
2732
+ },
2733
+ async listIntents(tenantId) {
2734
+ const result = await transport.query(anyApi.intents.list, { tenantId: requireTenant(tenantId) });
2735
+ return Array.isArray(result) ? result : [];
2736
+ },
2737
+ async getIntent(tenantId, intentId) {
2738
+ const result = await transport.query(anyApi.intents.get, {
2739
+ tenantId: requireTenant(tenantId),
2740
+ intentId: requireNonEmpty(intentId, "intentId")
2741
+ });
2742
+ return result ?? null;
2743
+ },
2744
+ async saveIntent(tenantId, intentId, intent, updatedAt) {
2745
+ await transport.mutation(anyApi.intents.save, {
2746
+ tenantId: requireTenant(tenantId),
2747
+ intentId: requireNonEmpty(intentId, "intentId"),
2748
+ intent,
2749
+ updatedAt
2750
+ });
2751
+ },
2752
+ async appendIntentDecision(tenantId, intentId, decision) {
2753
+ await transport.mutation(anyApi.intents.appendDecision, {
2754
+ tenantId: requireTenant(tenantId),
2755
+ intentId: requireNonEmpty(intentId, "intentId"),
2756
+ decision
2757
+ });
2758
+ }
2759
+ };
2760
+ }
2761
+ var init_state_backend = __esm({
2762
+ "src/state-backend.ts"() {
2763
+ "use strict";
2764
+ init_convex_client();
2765
+ }
2766
+ });
2767
+
2552
2768
  // src/trustPolicy.ts
2553
2769
  function trustSubjectKey(subject) {
2554
2770
  return `${subject.kind}:${subject.id}`;
@@ -2583,6 +2799,20 @@ function readTrustModeOverride(stateOrRepoSlug, repoSlugOrSubject, maybeSubject)
2583
2799
  return null;
2584
2800
  }
2585
2801
  }
2802
+ async function readTrustModeOverrideAsync(repoSlug, subject, state) {
2803
+ if (!subject.id) return null;
2804
+ const backendConfigured = Boolean(
2805
+ process.env.CONVEX_URL?.trim() && process.env.KODY_SERVICE_KEY?.trim() && /^[^/\s]+\/[^/\s]+$/.test(repoSlug)
2806
+ );
2807
+ if (backendConfigured) {
2808
+ const stored = await createStateBackendFromEnv().getManifest(repoSlug, "capability-trust");
2809
+ return stored ? parseTrustModeOverride(JSON.stringify(stored.doc), subject) : null;
2810
+ }
2811
+ if (process.env.GITHUB_ACTIONS === "true") {
2812
+ throw new Error("Convex backend is required for trust policy in GitHub Actions");
2813
+ }
2814
+ return readTrustModeOverride(state, repoSlug, subject);
2815
+ }
2586
2816
  function defaultStateForRepoSlug(repoSlug) {
2587
2817
  const [owner, repo] = repoSlug.split("/");
2588
2818
  return { repo: `${owner}/kody-state`, path: repo ?? repoSlug };
@@ -2592,6 +2822,7 @@ var init_trustPolicy = __esm({
2592
2822
  "src/trustPolicy.ts"() {
2593
2823
  "use strict";
2594
2824
  init_stateRepo();
2825
+ init_state_backend();
2595
2826
  TRUST_FILE_PATH = "state/trust.json";
2596
2827
  }
2597
2828
  });
@@ -3777,215 +4008,6 @@ var init_agents = __esm({
3777
4008
  }
3778
4009
  });
3779
4010
 
3780
- // src/chat/convex-client.ts
3781
- import { ConvexHttpClient } from "convex/browser";
3782
- function isPlainObject2(value) {
3783
- if (value === null || typeof value !== "object") return false;
3784
- const proto = Object.getPrototypeOf(value);
3785
- return proto === Object.prototype || proto === null;
3786
- }
3787
- function deepMapKeys(value, mapKey) {
3788
- if (Array.isArray(value)) return value.map((item) => deepMapKeys(item, mapKey));
3789
- if (isPlainObject2(value)) {
3790
- return Object.fromEntries(Object.entries(value).map(([key, item]) => [mapKey(key), deepMapKeys(item, mapKey)]));
3791
- }
3792
- return value;
3793
- }
3794
- function deepEscapeKeys(value) {
3795
- return deepMapKeys(value, (k) => NEEDS_ESCAPE.test(k) ? `${ESCAPE_CHAR}${k}` : k);
3796
- }
3797
- function deepUnescapeKeys(value) {
3798
- return deepMapKeys(value, (k) => k.startsWith(ESCAPE_CHAR) ? k.slice(1) : k);
3799
- }
3800
- function injectServiceKey(args, serviceKey = process.env.KODY_SERVICE_KEY) {
3801
- if (!serviceKey) return args;
3802
- if (args === void 0) return { serviceKey };
3803
- if (typeof args !== "object" || args === null || Array.isArray(args)) return args;
3804
- return { ...args, serviceKey };
3805
- }
3806
- function withEscapedKeys(client, serviceKey = process.env.KODY_SERVICE_KEY) {
3807
- return new Proxy(client, {
3808
- get(target, prop, receiver) {
3809
- if (CALL_METHODS.includes(prop)) {
3810
- const method = Reflect.get(target, prop, target);
3811
- return async (fn, args) => {
3812
- const authed = injectServiceKey(args, serviceKey);
3813
- const result = await method.call(target, fn, authed === void 0 ? void 0 : deepEscapeKeys(authed));
3814
- return deepUnescapeKeys(result);
3815
- };
3816
- }
3817
- const value = Reflect.get(target, prop, receiver);
3818
- return typeof value === "function" ? value.bind(target) : value;
3819
- }
3820
- });
3821
- }
3822
- function createConvexClientFromEnv(env = process.env) {
3823
- const url = env.CONVEX_URL?.trim();
3824
- if (!url) return null;
3825
- return withEscapedKeys(new ConvexHttpClient(url), env.KODY_SERVICE_KEY);
3826
- }
3827
- var ESCAPE_CHAR, NEEDS_ESCAPE, CALL_METHODS;
3828
- var init_convex_client = __esm({
3829
- "src/chat/convex-client.ts"() {
3830
- "use strict";
3831
- ESCAPE_CHAR = "~";
3832
- NEEDS_ESCAPE = /^[$_~]/;
3833
- CALL_METHODS = ["query", "mutation", "action"];
3834
- }
3835
- });
3836
-
3837
- // src/state-backend.ts
3838
- import { anyApi } from "convex/server";
3839
- function requireTenant(tenantId) {
3840
- const value = tenantId.trim();
3841
- if (!/^[^/\s]+\/[^/\s]+$/.test(value)) throw new Error("tenantId must be an owner/repository pair");
3842
- return value;
3843
- }
3844
- function requireNonEmpty(value, name) {
3845
- const normalized = value.trim();
3846
- if (!normalized) throw new Error(`${name} must not be empty`);
3847
- return normalized;
3848
- }
3849
- function createStateBackendFromEnv(env = process.env, client) {
3850
- const url = env.CONVEX_URL?.trim();
3851
- const serviceKey = env.KODY_SERVICE_KEY?.trim();
3852
- if (!url || !serviceKey) throw new Error("CONVEX_URL and KODY_SERVICE_KEY are required");
3853
- const transport = client ?? createConvexClientFromEnv(env);
3854
- return {
3855
- async get(tenantId, taskKey, kind) {
3856
- const result = await transport.query(anyApi.taskState.get, {
3857
- tenantId: requireTenant(tenantId),
3858
- taskKey: requireNonEmpty(taskKey, "taskKey"),
3859
- kind: requireNonEmpty(kind, "kind")
3860
- });
3861
- return result ?? null;
3862
- },
3863
- async save(tenantId, taskKey, kind, doc, expectedUpdatedAt) {
3864
- await transport.mutation(anyApi.taskState.save, {
3865
- tenantId: requireTenant(tenantId),
3866
- taskKey: requireNonEmpty(taskKey, "taskKey"),
3867
- kind: requireNonEmpty(kind, "kind"),
3868
- doc,
3869
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3870
- ...expectedUpdatedAt ? { expectedUpdatedAt } : {}
3871
- });
3872
- },
3873
- async getRepoDoc(tenantId, kind) {
3874
- const result = await transport.query(anyApi.repoDocs.get, {
3875
- tenantId: requireTenant(tenantId),
3876
- kind: requireNonEmpty(kind, "kind")
3877
- });
3878
- return result ?? null;
3879
- },
3880
- async listRepoDocs(tenantId, prefix) {
3881
- const result = await transport.query(anyApi.repoDocs.listByPrefix, {
3882
- tenantId: requireTenant(tenantId),
3883
- prefix: requireNonEmpty(prefix, "prefix")
3884
- });
3885
- return Array.isArray(result) ? result : [];
3886
- },
3887
- async saveRepoDoc(tenantId, kind, doc, expectedUpdatedAt) {
3888
- await transport.mutation(anyApi.repoDocs.save, {
3889
- tenantId: requireTenant(tenantId),
3890
- kind: requireNonEmpty(kind, "kind"),
3891
- doc,
3892
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3893
- ...expectedUpdatedAt ? { expectedUpdatedAt } : {}
3894
- });
3895
- },
3896
- async getGoal(tenantId, goalId) {
3897
- const result = await transport.query(anyApi.goals.get, {
3898
- tenantId: requireTenant(tenantId),
3899
- goalId: requireNonEmpty(goalId, "goalId")
3900
- });
3901
- return result ?? null;
3902
- },
3903
- async listGoals(tenantId) {
3904
- const result = await transport.query(anyApi.goals.list, { tenantId: requireTenant(tenantId) });
3905
- return Array.isArray(result) ? result : [];
3906
- },
3907
- async saveGoal(tenantId, goalId, state, updatedAt, expectedUpdatedAt) {
3908
- await transport.mutation(anyApi.goals.save, {
3909
- tenantId: requireTenant(tenantId),
3910
- goalId: requireNonEmpty(goalId, "goalId"),
3911
- state,
3912
- updatedAt,
3913
- ...expectedUpdatedAt ? { expectedUpdatedAt } : {}
3914
- });
3915
- },
3916
- async appendDailyLog(tenantId, stream, date, entry) {
3917
- await transport.mutation(anyApi.dailyLogs.append, {
3918
- tenantId: requireTenant(tenantId),
3919
- stream,
3920
- date: requireNonEmpty(date, "date"),
3921
- entry
3922
- });
3923
- },
3924
- async saveAgencyRun(tenantId, runId, subjectType, subjectId, run, updatedAt) {
3925
- await transport.mutation(anyApi.agencyRuns.save, {
3926
- tenantId: requireTenant(tenantId),
3927
- runId: requireNonEmpty(runId, "runId"),
3928
- subjectType,
3929
- subjectId: requireNonEmpty(subjectId, "subjectId"),
3930
- run,
3931
- updatedAt
3932
- });
3933
- },
3934
- async appendRunEvent(tenantId, runId, goalId, event, time) {
3935
- await transport.mutation(anyApi.runEvents.append, {
3936
- tenantId: requireTenant(tenantId),
3937
- runId: requireNonEmpty(runId, "runId"),
3938
- ...goalId ? { goalId: requireNonEmpty(goalId, "goalId") } : {},
3939
- event,
3940
- time: requireNonEmpty(time, "time")
3941
- });
3942
- },
3943
- async saveReport(tenantId, slug, runId, title, body, meta, updatedAt) {
3944
- await transport.mutation(anyApi.reports.save, {
3945
- tenantId: requireTenant(tenantId),
3946
- slug: requireNonEmpty(slug, "slug"),
3947
- runId: requireNonEmpty(runId, "runId"),
3948
- title,
3949
- body,
3950
- meta,
3951
- updatedAt
3952
- });
3953
- },
3954
- async listIntents(tenantId) {
3955
- const result = await transport.query(anyApi.intents.list, { tenantId: requireTenant(tenantId) });
3956
- return Array.isArray(result) ? result : [];
3957
- },
3958
- async getIntent(tenantId, intentId) {
3959
- const result = await transport.query(anyApi.intents.get, {
3960
- tenantId: requireTenant(tenantId),
3961
- intentId: requireNonEmpty(intentId, "intentId")
3962
- });
3963
- return result ?? null;
3964
- },
3965
- async saveIntent(tenantId, intentId, intent, updatedAt) {
3966
- await transport.mutation(anyApi.intents.save, {
3967
- tenantId: requireTenant(tenantId),
3968
- intentId: requireNonEmpty(intentId, "intentId"),
3969
- intent,
3970
- updatedAt
3971
- });
3972
- },
3973
- async appendIntentDecision(tenantId, intentId, decision) {
3974
- await transport.mutation(anyApi.intents.appendDecision, {
3975
- tenantId: requireTenant(tenantId),
3976
- intentId: requireNonEmpty(intentId, "intentId"),
3977
- decision
3978
- });
3979
- }
3980
- };
3981
- }
3982
- var init_state_backend = __esm({
3983
- "src/state-backend.ts"() {
3984
- "use strict";
3985
- init_convex_client();
3986
- }
3987
- });
3988
-
3989
4011
  // src/task-artifacts.ts
3990
4012
  import fs10 from "fs";
3991
4013
  import path12 from "path";
@@ -8971,7 +8993,9 @@ function decodeGoal(doc) {
8971
8993
  async function fetchGoalStateAsync(config, goalId, cwd) {
8972
8994
  const tenantId = backendTenant(config);
8973
8995
  if (backendEnabled(config) && tenantId) {
8974
- return decodeGoal(await createStateBackendFromEnv().getGoal(tenantId, goalId));
8996
+ const fromBackend = decodeGoal(await createStateBackendFromEnv().getGoal(tenantId, goalId));
8997
+ if (fromBackend) return fromBackend;
8998
+ return fetchGoalStateLegacy(config, goalId, cwd);
8975
8999
  }
8976
9000
  if (backendRequired()) throw new Error("Convex backend is required for goal state in GitHub Actions");
8977
9001
  return fetchGoalStateLegacy(config, goalId, cwd);
@@ -10356,12 +10380,12 @@ function scalarFacts(facts) {
10356
10380
  async function autonomyBlockReason(ctx, goalId, goal, goalState, dispatch2, options = {}) {
10357
10381
  if (ctx.data.jobForce === true) return null;
10358
10382
  const selfKind = managedModelKind(goal);
10359
- const selfMode = selfKind === "Goal" ? firstTrustOverride(ctx, subjectCandidates("goal", goalId, goalState)) : null;
10383
+ const selfMode = selfKind === "Goal" ? await firstTrustOverride(ctx, subjectCandidates("goal", goalId, goalState)) : null;
10360
10384
  if (selfKind === "Goal" && (selfMode === "ask" || selfMode !== "auto" && goal.runWithoutApproval !== true)) {
10361
10385
  return `Run without approval is off for ${managedModelKind(goal)} ${goalId}`;
10362
10386
  }
10363
10387
  if (dispatch2.workflow && selfKind === "Goal") {
10364
- const workflowMode = firstTrustOverride(ctx, [{ kind: "workflow", id: dispatch2.workflow }]);
10388
+ const workflowMode = await firstTrustOverride(ctx, [{ kind: "workflow", id: dispatch2.workflow }]);
10365
10389
  const workflow = workflowMode === "auto" ? null : readWorkflowDefinition(ctx.config, ctx.cwd, dispatch2.workflow);
10366
10390
  if (workflowMode === "ask" || workflowMode !== "auto" && workflow && workflow.runWithoutApproval !== true) {
10367
10391
  return `Run without approval is off for workflow ${dispatch2.workflow}`;
@@ -10372,7 +10396,7 @@ async function autonomyBlockReason(ctx, goalId, goal, goalState, dispatch2, opti
10372
10396
  const target = await fetchGoalStateAsync(ctx.config, targetGoal, ctx.cwd);
10373
10397
  const targetManaged = target ? managedGoalFromState(expandManagedGoalState(target)) : null;
10374
10398
  const targetIsGoal = targetManaged ? managedModelKind(targetManaged) === "Goal" : false;
10375
- const targetMode = targetManaged && targetIsGoal ? firstTrustOverride(ctx, subjectCandidates("goal", targetGoal, target)) : null;
10399
+ const targetMode = targetManaged && targetIsGoal ? await firstTrustOverride(ctx, subjectCandidates("goal", targetGoal, target)) : null;
10376
10400
  if (targetIsGoal && (targetMode === "ask" || targetMode !== "auto" && targetManaged && targetManaged.runWithoutApproval !== true)) {
10377
10401
  return `Run without approval is off for goal ${targetGoal}`;
10378
10402
  }
@@ -10416,11 +10440,14 @@ function subjectCandidates(kind, id, state) {
10416
10440
  }
10417
10441
  return [...ids].map((candidate) => ({ kind, id: candidate }));
10418
10442
  }
10419
- function firstTrustOverride(ctx, subjects) {
10420
- if (!ctx.config.state) return null;
10443
+ async function firstTrustOverride(ctx, subjects) {
10444
+ const backendConfigured = Boolean(
10445
+ process.env.CONVEX_URL?.trim() && process.env.KODY_SERVICE_KEY?.trim()
10446
+ );
10447
+ if (!ctx.config.state && !backendConfigured) return null;
10421
10448
  const repoSlug = ctx.config.github?.owner && ctx.config.github?.repo ? `${ctx.config.github.owner}/${ctx.config.github.repo}` : "";
10422
10449
  for (const subject of subjects) {
10423
- const mode = readTrustModeOverride(ctx.config.state, repoSlug, subject);
10450
+ const mode = await readTrustModeOverrideAsync(repoSlug, subject, ctx.config.state);
10424
10451
  if (mode) return mode;
10425
10452
  }
10426
10453
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.395",
3
+ "version": "0.4.397",
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",