@cruxy/cli 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/loop.d.ts +15 -0
- package/dist/agent/loop.js +13 -2
- package/dist/agent/prompts.d.ts +7 -0
- package/dist/agent/prompts.js +6 -0
- package/dist/agent/session.d.ts +14 -0
- package/dist/agent/session.js +10 -1
- package/dist/brand/index.d.ts +1 -1
- package/dist/brand/index.js +1 -1
- package/dist/brand/voice.d.ts +20 -0
- package/dist/brand/voice.js +54 -0
- package/dist/cli/commands/memory.d.ts +8 -0
- package/dist/cli/commands/memory.js +98 -0
- package/dist/cli/commands/pr.js +9 -1
- package/dist/cli/program.js +2 -0
- package/dist/cli/session-factory.js +31 -1
- package/dist/config/schema.d.ts +114 -28
- package/dist/config/schema.js +38 -0
- package/dist/constants.d.ts +11 -0
- package/dist/constants.js +11 -0
- package/dist/errors/constructors.d.ts +23 -0
- package/dist/errors/constructors.js +86 -6
- package/dist/errors/types.d.ts +12 -0
- package/dist/errors/types.js +20 -0
- package/dist/hooks/types.d.ts +1 -1
- package/dist/memory/index.d.ts +7 -0
- package/dist/memory/index.js +7 -0
- package/dist/memory/recall.d.ts +32 -0
- package/dist/memory/recall.js +73 -0
- package/dist/memory/remember-tool.d.ts +25 -0
- package/dist/memory/remember-tool.js +56 -0
- package/dist/memory/secrets.d.ts +29 -0
- package/dist/memory/secrets.js +61 -0
- package/dist/memory/service.d.ts +92 -0
- package/dist/memory/service.js +164 -0
- package/dist/memory/store.d.ts +32 -0
- package/dist/memory/store.js +100 -0
- package/dist/memory/trust.d.ts +52 -0
- package/dist/memory/trust.js +106 -0
- package/dist/memory/types.d.ts +101 -0
- package/dist/memory/types.js +58 -0
- package/dist/plan/service.d.ts +9 -0
- package/dist/plan/service.js +6 -0
- package/dist/render/state.js +4 -1
- package/dist/render/types.d.ts +7 -1
- package/dist/routing/index.d.ts +2 -0
- package/dist/routing/index.js +5 -0
- package/dist/routing/resolve.d.ts +17 -0
- package/dist/routing/resolve.js +18 -0
- package/dist/routing/router.d.ts +47 -0
- package/dist/routing/router.js +84 -0
- package/dist/routing/types.d.ts +42 -0
- package/dist/routing/types.js +27 -0
- package/dist/subagent/orchestrator.d.ts +6 -0
- package/dist/subagent/orchestrator.js +2 -0
- package/dist/subagent/types.d.ts +6 -0
- package/dist/vcs/generate.d.ts +3 -1
- package/dist/vcs/generate.js +4 -1
- package/package.json +2 -2
package/dist/config/schema.d.ts
CHANGED
|
@@ -182,7 +182,7 @@ export declare const IndexConfigSchema: z.ZodObject<{
|
|
|
182
182
|
};
|
|
183
183
|
enabled: boolean;
|
|
184
184
|
embedder: "fastembed";
|
|
185
|
-
store: "
|
|
185
|
+
store: "memory" | "auto" | "sqlite";
|
|
186
186
|
maxFileBytes: number;
|
|
187
187
|
chunk: {
|
|
188
188
|
windowLines: number;
|
|
@@ -196,7 +196,7 @@ export declare const IndexConfigSchema: z.ZodObject<{
|
|
|
196
196
|
} | undefined;
|
|
197
197
|
enabled?: boolean | undefined;
|
|
198
198
|
embedder?: "fastembed" | undefined;
|
|
199
|
-
store?: "
|
|
199
|
+
store?: "memory" | "auto" | "sqlite" | undefined;
|
|
200
200
|
maxFileBytes?: number | undefined;
|
|
201
201
|
chunk?: {
|
|
202
202
|
windowLines?: number | undefined;
|
|
@@ -320,18 +320,18 @@ export declare const SandboxConfigSchema: z.ZodObject<{
|
|
|
320
320
|
*/
|
|
321
321
|
mounts: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
322
322
|
}, "strict", z.ZodTypeAny, {
|
|
323
|
+
memory: string;
|
|
323
324
|
image: string;
|
|
324
325
|
enabled: boolean;
|
|
325
|
-
memory: string;
|
|
326
326
|
network: "none" | "host-loopback" | "full";
|
|
327
327
|
pids: number;
|
|
328
328
|
cpus: number;
|
|
329
329
|
mounts: string[];
|
|
330
330
|
timeout?: number | undefined;
|
|
331
331
|
}, {
|
|
332
|
+
memory?: string | undefined;
|
|
332
333
|
image?: string | undefined;
|
|
333
334
|
enabled?: boolean | undefined;
|
|
334
|
-
memory?: string | undefined;
|
|
335
335
|
network?: "none" | "host-loopback" | "full" | undefined;
|
|
336
336
|
pids?: number | undefined;
|
|
337
337
|
cpus?: number | undefined;
|
|
@@ -365,6 +365,49 @@ export declare const HooksConfigSchema: z.ZodObject<{
|
|
|
365
365
|
trustPrompt?: boolean | undefined;
|
|
366
366
|
}>;
|
|
367
367
|
export type HooksConfig = z.infer<typeof HooksConfigSchema>;
|
|
368
|
+
/**
|
|
369
|
+
* Multi-model routing (C.30): map declared task classes to tiers so mechanical
|
|
370
|
+
* work runs on a cheap tier and hard reasoning on a strong one. Fully opt-in —
|
|
371
|
+
* with an empty `map` and no `default`, every task class routes to the session's
|
|
372
|
+
* single tier and behavior is unchanged (routing stays inert until configured).
|
|
373
|
+
* Only tier names appear here; upstream model names never do (U.8). Keys are the
|
|
374
|
+
* fixed {@link TASK_CLASSES}, so a mistyped class is rejected at config load.
|
|
375
|
+
*/
|
|
376
|
+
export declare const RoutingConfigSchema: z.ZodObject<{
|
|
377
|
+
/** Tier for any task class not in `map`. Unset → the tier implied by
|
|
378
|
+
* `model.model` (a real tier, or the auto-fallback tier). */
|
|
379
|
+
default: z.ZodOptional<z.ZodEnum<["kavi", "vaani", "mira"]>>;
|
|
380
|
+
/** Per-task-class tier overrides; anything omitted takes `default`. */
|
|
381
|
+
map: z.ZodDefault<z.ZodRecord<z.ZodEnum<["main-turn", "subagent", "plan", "commit-msg", "classify", "summarize"]>, z.ZodEnum<["kavi", "vaani", "mira"]>>>;
|
|
382
|
+
}, "strict", z.ZodTypeAny, {
|
|
383
|
+
map: Partial<Record<"main-turn" | "subagent" | "plan" | "commit-msg" | "classify" | "summarize", "kavi" | "vaani" | "mira">>;
|
|
384
|
+
default?: "kavi" | "vaani" | "mira" | undefined;
|
|
385
|
+
}, {
|
|
386
|
+
map?: Partial<Record<"main-turn" | "subagent" | "plan" | "commit-msg" | "classify" | "summarize", "kavi" | "vaani" | "mira">> | undefined;
|
|
387
|
+
default?: "kavi" | "vaani" | "mira" | undefined;
|
|
388
|
+
}>;
|
|
389
|
+
/**
|
|
390
|
+
* Persistent memory (C.29): structured notes recalled across sessions. ON by
|
|
391
|
+
* default and safe to be so — user memory is self-authored (nothing you didn't
|
|
392
|
+
* write is injected) and project memory still requires an explicit
|
|
393
|
+
* `cruxy memory trust`, so a cloned repo never auto-injects. Recall is bounded
|
|
394
|
+
* by a token budget; the `remember` tool refuses to persist secrets.
|
|
395
|
+
*/
|
|
396
|
+
export declare const MemoryConfigSchema: z.ZodObject<{
|
|
397
|
+
/** Master switch. When false, nothing is recalled and the `remember` tool
|
|
398
|
+
* is not registered (memory stays fully inert). */
|
|
399
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
400
|
+
/** Token budget for the recalled block injected at session start; entries
|
|
401
|
+
* beyond it are pruned oldest-first (the omission is stated, never silent). */
|
|
402
|
+
maxRecallTokens: z.ZodDefault<z.ZodNumber>;
|
|
403
|
+
}, "strict", z.ZodTypeAny, {
|
|
404
|
+
enabled: boolean;
|
|
405
|
+
maxRecallTokens: number;
|
|
406
|
+
}, {
|
|
407
|
+
enabled?: boolean | undefined;
|
|
408
|
+
maxRecallTokens?: number | undefined;
|
|
409
|
+
}>;
|
|
410
|
+
export type MemoryConfig = z.infer<typeof MemoryConfigSchema>;
|
|
368
411
|
/** MCP server entry — stdio or URL transport (wired up in a later phase). */
|
|
369
412
|
export declare const McpServerSchema: z.ZodObject<{
|
|
370
413
|
command: z.ZodOptional<z.ZodString>;
|
|
@@ -546,7 +589,7 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
546
589
|
};
|
|
547
590
|
enabled: boolean;
|
|
548
591
|
embedder: "fastembed";
|
|
549
|
-
store: "
|
|
592
|
+
store: "memory" | "auto" | "sqlite";
|
|
550
593
|
maxFileBytes: number;
|
|
551
594
|
chunk: {
|
|
552
595
|
windowLines: number;
|
|
@@ -560,7 +603,7 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
560
603
|
} | undefined;
|
|
561
604
|
enabled?: boolean | undefined;
|
|
562
605
|
embedder?: "fastembed" | undefined;
|
|
563
|
-
store?: "
|
|
606
|
+
store?: "memory" | "auto" | "sqlite" | undefined;
|
|
564
607
|
maxFileBytes?: number | undefined;
|
|
565
608
|
chunk?: {
|
|
566
609
|
windowLines?: number | undefined;
|
|
@@ -662,18 +705,18 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
662
705
|
*/
|
|
663
706
|
mounts: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
664
707
|
}, "strict", z.ZodTypeAny, {
|
|
708
|
+
memory: string;
|
|
665
709
|
image: string;
|
|
666
710
|
enabled: boolean;
|
|
667
|
-
memory: string;
|
|
668
711
|
network: "none" | "host-loopback" | "full";
|
|
669
712
|
pids: number;
|
|
670
713
|
cpus: number;
|
|
671
714
|
mounts: string[];
|
|
672
715
|
timeout?: number | undefined;
|
|
673
716
|
}, {
|
|
717
|
+
memory?: string | undefined;
|
|
674
718
|
image?: string | undefined;
|
|
675
719
|
enabled?: boolean | undefined;
|
|
676
|
-
memory?: string | undefined;
|
|
677
720
|
network?: "none" | "host-loopback" | "full" | undefined;
|
|
678
721
|
pids?: number | undefined;
|
|
679
722
|
cpus?: number | undefined;
|
|
@@ -697,6 +740,33 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
697
740
|
enabled?: boolean | undefined;
|
|
698
741
|
trustPrompt?: boolean | undefined;
|
|
699
742
|
}>>;
|
|
743
|
+
routing: z.ZodDefault<z.ZodObject<{
|
|
744
|
+
/** Tier for any task class not in `map`. Unset → the tier implied by
|
|
745
|
+
* `model.model` (a real tier, or the auto-fallback tier). */
|
|
746
|
+
default: z.ZodOptional<z.ZodEnum<["kavi", "vaani", "mira"]>>;
|
|
747
|
+
/** Per-task-class tier overrides; anything omitted takes `default`. */
|
|
748
|
+
map: z.ZodDefault<z.ZodRecord<z.ZodEnum<["main-turn", "subagent", "plan", "commit-msg", "classify", "summarize"]>, z.ZodEnum<["kavi", "vaani", "mira"]>>>;
|
|
749
|
+
}, "strict", z.ZodTypeAny, {
|
|
750
|
+
map: Partial<Record<"main-turn" | "subagent" | "plan" | "commit-msg" | "classify" | "summarize", "kavi" | "vaani" | "mira">>;
|
|
751
|
+
default?: "kavi" | "vaani" | "mira" | undefined;
|
|
752
|
+
}, {
|
|
753
|
+
map?: Partial<Record<"main-turn" | "subagent" | "plan" | "commit-msg" | "classify" | "summarize", "kavi" | "vaani" | "mira">> | undefined;
|
|
754
|
+
default?: "kavi" | "vaani" | "mira" | undefined;
|
|
755
|
+
}>>;
|
|
756
|
+
memory: z.ZodDefault<z.ZodObject<{
|
|
757
|
+
/** Master switch. When false, nothing is recalled and the `remember` tool
|
|
758
|
+
* is not registered (memory stays fully inert). */
|
|
759
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
760
|
+
/** Token budget for the recalled block injected at session start; entries
|
|
761
|
+
* beyond it are pruned oldest-first (the omission is stated, never silent). */
|
|
762
|
+
maxRecallTokens: z.ZodDefault<z.ZodNumber>;
|
|
763
|
+
}, "strict", z.ZodTypeAny, {
|
|
764
|
+
enabled: boolean;
|
|
765
|
+
maxRecallTokens: number;
|
|
766
|
+
}, {
|
|
767
|
+
enabled?: boolean | undefined;
|
|
768
|
+
maxRecallTokens?: number | undefined;
|
|
769
|
+
}>>;
|
|
700
770
|
mcpServers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
701
771
|
command: z.ZodOptional<z.ZodString>;
|
|
702
772
|
args: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
@@ -715,14 +785,18 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
715
785
|
cruxy: {
|
|
716
786
|
gatewayUrl: string;
|
|
717
787
|
};
|
|
788
|
+
memory: {
|
|
789
|
+
enabled: boolean;
|
|
790
|
+
maxRecallTokens: number;
|
|
791
|
+
};
|
|
718
792
|
checkpoint: {
|
|
719
793
|
enabled: boolean;
|
|
720
794
|
retention: number;
|
|
721
795
|
};
|
|
722
796
|
sandbox: {
|
|
797
|
+
memory: string;
|
|
723
798
|
image: string;
|
|
724
799
|
enabled: boolean;
|
|
725
|
-
memory: string;
|
|
726
800
|
network: "none" | "host-loopback" | "full";
|
|
727
801
|
pids: number;
|
|
728
802
|
cpus: number;
|
|
@@ -732,6 +806,14 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
732
806
|
approval: {
|
|
733
807
|
mode: "prompt";
|
|
734
808
|
};
|
|
809
|
+
subagent: {
|
|
810
|
+
maxDepth: number;
|
|
811
|
+
defaultBudget: {
|
|
812
|
+
maxTokens: number;
|
|
813
|
+
maxIterations: number;
|
|
814
|
+
timeoutMs?: number | undefined;
|
|
815
|
+
};
|
|
816
|
+
};
|
|
735
817
|
model: {
|
|
736
818
|
provider: "cruxy" | "anthropic" | "openai" | "custom";
|
|
737
819
|
model: string;
|
|
@@ -770,21 +852,13 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
770
852
|
};
|
|
771
853
|
enabled: boolean;
|
|
772
854
|
embedder: "fastembed";
|
|
773
|
-
store: "
|
|
855
|
+
store: "memory" | "auto" | "sqlite";
|
|
774
856
|
maxFileBytes: number;
|
|
775
857
|
chunk: {
|
|
776
858
|
windowLines: number;
|
|
777
859
|
overlapLines: number;
|
|
778
860
|
};
|
|
779
861
|
};
|
|
780
|
-
subagent: {
|
|
781
|
-
maxDepth: number;
|
|
782
|
-
defaultBudget: {
|
|
783
|
-
maxTokens: number;
|
|
784
|
-
maxIterations: number;
|
|
785
|
-
timeoutMs?: number | undefined;
|
|
786
|
-
};
|
|
787
|
-
};
|
|
788
862
|
test: {
|
|
789
863
|
maxIterations: number;
|
|
790
864
|
captureBytes: number;
|
|
@@ -794,6 +868,10 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
794
868
|
enabled: boolean;
|
|
795
869
|
trustPrompt: boolean;
|
|
796
870
|
};
|
|
871
|
+
routing: {
|
|
872
|
+
map: Partial<Record<"main-turn" | "subagent" | "plan" | "commit-msg" | "classify" | "summarize", "kavi" | "vaani" | "mira">>;
|
|
873
|
+
default?: "kavi" | "vaani" | "mira" | undefined;
|
|
874
|
+
};
|
|
797
875
|
mcpServers: Record<string, {
|
|
798
876
|
command?: string | undefined;
|
|
799
877
|
args?: string[] | undefined;
|
|
@@ -804,14 +882,18 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
804
882
|
cruxy?: {
|
|
805
883
|
gatewayUrl?: string | undefined;
|
|
806
884
|
} | undefined;
|
|
885
|
+
memory?: {
|
|
886
|
+
enabled?: boolean | undefined;
|
|
887
|
+
maxRecallTokens?: number | undefined;
|
|
888
|
+
} | undefined;
|
|
807
889
|
checkpoint?: {
|
|
808
890
|
enabled?: boolean | undefined;
|
|
809
891
|
retention?: number | undefined;
|
|
810
892
|
} | undefined;
|
|
811
893
|
sandbox?: {
|
|
894
|
+
memory?: string | undefined;
|
|
812
895
|
image?: string | undefined;
|
|
813
896
|
enabled?: boolean | undefined;
|
|
814
|
-
memory?: string | undefined;
|
|
815
897
|
network?: "none" | "host-loopback" | "full" | undefined;
|
|
816
898
|
pids?: number | undefined;
|
|
817
899
|
cpus?: number | undefined;
|
|
@@ -821,6 +903,14 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
821
903
|
approval?: {
|
|
822
904
|
mode?: "prompt" | undefined;
|
|
823
905
|
} | undefined;
|
|
906
|
+
subagent?: {
|
|
907
|
+
maxDepth?: number | undefined;
|
|
908
|
+
defaultBudget?: {
|
|
909
|
+
maxTokens?: number | undefined;
|
|
910
|
+
maxIterations?: number | undefined;
|
|
911
|
+
timeoutMs?: number | undefined;
|
|
912
|
+
} | undefined;
|
|
913
|
+
} | undefined;
|
|
824
914
|
model?: {
|
|
825
915
|
provider?: "cruxy" | "anthropic" | "openai" | "custom" | undefined;
|
|
826
916
|
model?: string | undefined;
|
|
@@ -859,21 +949,13 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
859
949
|
} | undefined;
|
|
860
950
|
enabled?: boolean | undefined;
|
|
861
951
|
embedder?: "fastembed" | undefined;
|
|
862
|
-
store?: "
|
|
952
|
+
store?: "memory" | "auto" | "sqlite" | undefined;
|
|
863
953
|
maxFileBytes?: number | undefined;
|
|
864
954
|
chunk?: {
|
|
865
955
|
windowLines?: number | undefined;
|
|
866
956
|
overlapLines?: number | undefined;
|
|
867
957
|
} | undefined;
|
|
868
958
|
} | undefined;
|
|
869
|
-
subagent?: {
|
|
870
|
-
maxDepth?: number | undefined;
|
|
871
|
-
defaultBudget?: {
|
|
872
|
-
maxTokens?: number | undefined;
|
|
873
|
-
maxIterations?: number | undefined;
|
|
874
|
-
timeoutMs?: number | undefined;
|
|
875
|
-
} | undefined;
|
|
876
|
-
} | undefined;
|
|
877
959
|
test?: {
|
|
878
960
|
maxIterations?: number | undefined;
|
|
879
961
|
command?: string | undefined;
|
|
@@ -883,6 +965,10 @@ export declare const CruxyConfigSchema: z.ZodObject<{
|
|
|
883
965
|
enabled?: boolean | undefined;
|
|
884
966
|
trustPrompt?: boolean | undefined;
|
|
885
967
|
} | undefined;
|
|
968
|
+
routing?: {
|
|
969
|
+
map?: Partial<Record<"main-turn" | "subagent" | "plan" | "commit-msg" | "classify" | "summarize", "kavi" | "vaani" | "mira">> | undefined;
|
|
970
|
+
default?: "kavi" | "vaani" | "mira" | undefined;
|
|
971
|
+
} | undefined;
|
|
886
972
|
mcpServers?: Record<string, {
|
|
887
973
|
command?: string | undefined;
|
|
888
974
|
args?: string[] | undefined;
|
package/dist/config/schema.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { LOG_LEVELS } from "../utils/logger.js";
|
|
3
|
+
import { MODEL_TIERS } from "../brand/voice.js";
|
|
4
|
+
import { TASK_CLASSES } from "../routing/types.js";
|
|
3
5
|
export const ProviderSchema = z.enum([
|
|
4
6
|
"cruxy",
|
|
5
7
|
"anthropic",
|
|
@@ -254,6 +256,40 @@ export const HooksConfigSchema = z
|
|
|
254
256
|
trustPrompt: z.boolean().default(true),
|
|
255
257
|
})
|
|
256
258
|
.strict();
|
|
259
|
+
/**
|
|
260
|
+
* Multi-model routing (C.30): map declared task classes to tiers so mechanical
|
|
261
|
+
* work runs on a cheap tier and hard reasoning on a strong one. Fully opt-in —
|
|
262
|
+
* with an empty `map` and no `default`, every task class routes to the session's
|
|
263
|
+
* single tier and behavior is unchanged (routing stays inert until configured).
|
|
264
|
+
* Only tier names appear here; upstream model names never do (U.8). Keys are the
|
|
265
|
+
* fixed {@link TASK_CLASSES}, so a mistyped class is rejected at config load.
|
|
266
|
+
*/
|
|
267
|
+
export const RoutingConfigSchema = z
|
|
268
|
+
.object({
|
|
269
|
+
/** Tier for any task class not in `map`. Unset → the tier implied by
|
|
270
|
+
* `model.model` (a real tier, or the auto-fallback tier). */
|
|
271
|
+
default: z.enum(MODEL_TIERS).optional(),
|
|
272
|
+
/** Per-task-class tier overrides; anything omitted takes `default`. */
|
|
273
|
+
map: z.record(z.enum(TASK_CLASSES), z.enum(MODEL_TIERS)).default({}),
|
|
274
|
+
})
|
|
275
|
+
.strict();
|
|
276
|
+
/**
|
|
277
|
+
* Persistent memory (C.29): structured notes recalled across sessions. ON by
|
|
278
|
+
* default and safe to be so — user memory is self-authored (nothing you didn't
|
|
279
|
+
* write is injected) and project memory still requires an explicit
|
|
280
|
+
* `cruxy memory trust`, so a cloned repo never auto-injects. Recall is bounded
|
|
281
|
+
* by a token budget; the `remember` tool refuses to persist secrets.
|
|
282
|
+
*/
|
|
283
|
+
export const MemoryConfigSchema = z
|
|
284
|
+
.object({
|
|
285
|
+
/** Master switch. When false, nothing is recalled and the `remember` tool
|
|
286
|
+
* is not registered (memory stays fully inert). */
|
|
287
|
+
enabled: z.boolean().default(true),
|
|
288
|
+
/** Token budget for the recalled block injected at session start; entries
|
|
289
|
+
* beyond it are pruned oldest-first (the omission is stated, never silent). */
|
|
290
|
+
maxRecallTokens: z.number().int().positive().default(1000),
|
|
291
|
+
})
|
|
292
|
+
.strict();
|
|
257
293
|
/** MCP server entry — stdio or URL transport (wired up in a later phase). */
|
|
258
294
|
export const McpServerSchema = z
|
|
259
295
|
.object({
|
|
@@ -278,6 +314,8 @@ export const CruxyConfigSchema = z
|
|
|
278
314
|
test: TestConfigSchema.default({}),
|
|
279
315
|
sandbox: SandboxConfigSchema.default({}),
|
|
280
316
|
hooks: HooksConfigSchema.default({}),
|
|
317
|
+
routing: RoutingConfigSchema.default({}),
|
|
318
|
+
memory: MemoryConfigSchema.default({}),
|
|
281
319
|
mcpServers: z.record(z.string(), McpServerSchema).default({}),
|
|
282
320
|
logLevel: z.enum(LOG_LEVELS).default("info"),
|
|
283
321
|
})
|
package/dist/constants.d.ts
CHANGED
|
@@ -30,6 +30,17 @@ export declare const COMMANDS_DIR_NAME = "commands";
|
|
|
30
30
|
/** Per-repo hook-trust record, in the GLOBAL dir only (`~/.cruxy/trust.json`) —
|
|
31
31
|
* never in a repo, so cloning carries no trust (C.19 supply-chain safety). */
|
|
32
32
|
export declare const TRUST_FILE_NAME = "trust.json";
|
|
33
|
+
/**
|
|
34
|
+
* Persistent memory (C.29). Structured entries live under the memory subdir of
|
|
35
|
+
* the project dir (`<cwd>/.cruxy/memory`) and the global dir (`~/.cruxy/memory`),
|
|
36
|
+
* one JSON file per scope — pure data, validated, never eval'd.
|
|
37
|
+
*/
|
|
38
|
+
export declare const MEMORY_DIR_NAME = "memory";
|
|
39
|
+
export declare const MEMORY_FILE_NAME = "entries.json";
|
|
40
|
+
/** Per-repo PROJECT-memory trust record, in the GLOBAL dir only
|
|
41
|
+
* (`~/.cruxy/memory-trust.json`) — its own file, independent of hook trust, so
|
|
42
|
+
* cloning a repo carries zero memory trust (C.29 supply-chain safety). */
|
|
43
|
+
export declare const MEMORY_TRUST_FILE_NAME = "memory-trust.json";
|
|
33
44
|
/**
|
|
34
45
|
* Absolute path of the shipped builtin skills directory (`<pkg>/skills`).
|
|
35
46
|
* Anchored the same way as the package.json lookup above: both `dist/` and
|
package/dist/constants.js
CHANGED
|
@@ -50,6 +50,17 @@ export const COMMANDS_DIR_NAME = "commands";
|
|
|
50
50
|
/** Per-repo hook-trust record, in the GLOBAL dir only (`~/.cruxy/trust.json`) —
|
|
51
51
|
* never in a repo, so cloning carries no trust (C.19 supply-chain safety). */
|
|
52
52
|
export const TRUST_FILE_NAME = "trust.json";
|
|
53
|
+
/**
|
|
54
|
+
* Persistent memory (C.29). Structured entries live under the memory subdir of
|
|
55
|
+
* the project dir (`<cwd>/.cruxy/memory`) and the global dir (`~/.cruxy/memory`),
|
|
56
|
+
* one JSON file per scope — pure data, validated, never eval'd.
|
|
57
|
+
*/
|
|
58
|
+
export const MEMORY_DIR_NAME = "memory";
|
|
59
|
+
export const MEMORY_FILE_NAME = "entries.json";
|
|
60
|
+
/** Per-repo PROJECT-memory trust record, in the GLOBAL dir only
|
|
61
|
+
* (`~/.cruxy/memory-trust.json`) — its own file, independent of hook trust, so
|
|
62
|
+
* cloning a repo carries zero memory trust (C.29 supply-chain safety). */
|
|
63
|
+
export const MEMORY_TRUST_FILE_NAME = "memory-trust.json";
|
|
53
64
|
/**
|
|
54
65
|
* Absolute path of the shipped builtin skills directory (`<pkg>/skills`).
|
|
55
66
|
* Anchored the same way as the package.json lookup above: both `dist/` and
|
|
@@ -11,6 +11,15 @@ export declare function usageError(title: string, nextSteps?: string[]): CruxyEr
|
|
|
11
11
|
export declare function interactiveRequired(what: string, alternatives?: string[]): CruxyError;
|
|
12
12
|
export declare function configKeyUnknown(key: string): CruxyError;
|
|
13
13
|
export declare function providerUnsupported(provider: string): CruxyError;
|
|
14
|
+
/**
|
|
15
|
+
* A task class is routed (C.30) to a tier the gateway does not offer. A usage
|
|
16
|
+
* error the user fixes in config — cruxy fails loud here rather than silently
|
|
17
|
+
* substituting a different tier (which would hand a user a model they never
|
|
18
|
+
* asked for). Distinct from runtime unavailability (overload/budget), which
|
|
19
|
+
* stays on the U.5 api codes. Params are tier/class NAMES only — never an
|
|
20
|
+
* upstream model id — so the message is gag-safe by construction (U.8).
|
|
21
|
+
*/
|
|
22
|
+
export declare function routingTierUnavailable(tier: string, taskClass: string, offered: string[]): CruxyError;
|
|
14
23
|
export declare function configParse(path: string, underlying?: unknown): CruxyError;
|
|
15
24
|
export declare function configInvalid(issues: string, path?: string): CruxyError;
|
|
16
25
|
export declare function authMissingKey(provider: string, envVar: string): CruxyError;
|
|
@@ -127,6 +136,20 @@ export declare function subagentFailed(underlying?: unknown): CruxyError;
|
|
|
127
136
|
* invents a test command — the fix is always to declare one.
|
|
128
137
|
*/
|
|
129
138
|
export declare function testCommandNotFound(): CruxyError;
|
|
139
|
+
/**
|
|
140
|
+
* A memory write was refused because the content matched a known secret shape
|
|
141
|
+
* (C.29). Secrets are NEVER persisted to memory — the fix is to remember a
|
|
142
|
+
* non-secret description, not the secret itself.
|
|
143
|
+
*/
|
|
144
|
+
export declare function memorySecretRefused(kind: string): CruxyError;
|
|
145
|
+
/**
|
|
146
|
+
* A repo's project memory is present but has not been trusted on this machine
|
|
147
|
+
* (C.29). It is never recalled into the model's context silently — a cloned repo
|
|
148
|
+
* cannot inject notes until you review and trust them.
|
|
149
|
+
*/
|
|
150
|
+
export declare function memoryUntrusted(root: string): CruxyError;
|
|
151
|
+
/** A malformed memory entry was rejected (C.29) — excluded, never eval'd. */
|
|
152
|
+
export declare function memoryInvalid(detail: string): CruxyError;
|
|
130
153
|
export declare function internal(underlying?: unknown): CruxyError;
|
|
131
154
|
/**
|
|
132
155
|
* Map a known provider/transport error (from `@cruxy/sdk`) to a typed
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApiError, AuthError, NetworkError, OverloadedError, RateLimitError, } from "@cruxy/sdk";
|
|
2
|
+
import { scrubModelNames } from "../brand/index.js";
|
|
2
3
|
import { CruxyError, ErrorCode } from "./types.js";
|
|
3
4
|
/**
|
|
4
5
|
* Helper constructors for {@link CruxyError}. Each encodes the title, the human
|
|
@@ -15,6 +16,18 @@ export function messageOf(underlying) {
|
|
|
15
16
|
return undefined;
|
|
16
17
|
return String(underlying);
|
|
17
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* {@link messageOf}, gag-scrubbed (U.8) for a gateway/provider-originated error.
|
|
21
|
+
* A gateway error body is an arbitrary EXTERNAL string, so it bypasses the
|
|
22
|
+
* structural tier gag — this is the boundary that scrubs any upstream model name
|
|
23
|
+
* out of it before it can reach a user-facing `cause`. Used by the provider-error
|
|
24
|
+
* constructors below (everything `classifyProviderError` routes through). The raw
|
|
25
|
+
* message stays on `underlying`, shown verbatim only under `--verbose`.
|
|
26
|
+
*/
|
|
27
|
+
function scrubbedMessageOf(underlying) {
|
|
28
|
+
const msg = messageOf(underlying);
|
|
29
|
+
return msg === undefined ? undefined : scrubModelNames(msg);
|
|
30
|
+
}
|
|
18
31
|
// ── usage (exit 2) ────────────────────────────────────────────────────────────
|
|
19
32
|
export function usageError(title, nextSteps) {
|
|
20
33
|
return new CruxyError({
|
|
@@ -57,6 +70,28 @@ export function providerUnsupported(provider) {
|
|
|
57
70
|
meta: { provider },
|
|
58
71
|
});
|
|
59
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* A task class is routed (C.30) to a tier the gateway does not offer. A usage
|
|
75
|
+
* error the user fixes in config — cruxy fails loud here rather than silently
|
|
76
|
+
* substituting a different tier (which would hand a user a model they never
|
|
77
|
+
* asked for). Distinct from runtime unavailability (overload/budget), which
|
|
78
|
+
* stays on the U.5 api codes. Params are tier/class NAMES only — never an
|
|
79
|
+
* upstream model id — so the message is gag-safe by construction (U.8).
|
|
80
|
+
*/
|
|
81
|
+
export function routingTierUnavailable(tier, taskClass, offered) {
|
|
82
|
+
return new CruxyError({
|
|
83
|
+
code: ErrorCode.RoutingTierUnavailable,
|
|
84
|
+
title: `the "${tier}" tier is not available for the "${taskClass}" task`,
|
|
85
|
+
cause: `routing maps "${taskClass}" to the "${tier}" tier, which this gateway does not currently offer`,
|
|
86
|
+
nextSteps: [
|
|
87
|
+
`route it to an available tier, e.g. \`cruxy config set routing.map.${taskClass} ${offered[0] ?? "vaani"}\``,
|
|
88
|
+
offered.length
|
|
89
|
+
? `available tiers: ${offered.join(", ")}`
|
|
90
|
+
: "no tiers are currently available",
|
|
91
|
+
],
|
|
92
|
+
meta: { tier, taskClass, offered },
|
|
93
|
+
});
|
|
94
|
+
}
|
|
60
95
|
// ── config (exit 3) ───────────────────────────────────────────────────────────
|
|
61
96
|
export function configParse(path, underlying) {
|
|
62
97
|
return new CruxyError({
|
|
@@ -99,7 +134,7 @@ export function authInvalid(underlying) {
|
|
|
99
134
|
return new CruxyError({
|
|
100
135
|
code: ErrorCode.AuthInvalid,
|
|
101
136
|
title: "the provider rejected your credentials",
|
|
102
|
-
cause:
|
|
137
|
+
cause: scrubbedMessageOf(underlying),
|
|
103
138
|
nextSteps: [
|
|
104
139
|
"verify your API key is correct and active",
|
|
105
140
|
"re-export the key and try again",
|
|
@@ -112,7 +147,7 @@ export function gatewayUnreachable(underlying) {
|
|
|
112
147
|
return new CruxyError({
|
|
113
148
|
code: ErrorCode.GatewayUnreachable,
|
|
114
149
|
title: "could not reach the model gateway",
|
|
115
|
-
cause:
|
|
150
|
+
cause: scrubbedMessageOf(underlying),
|
|
116
151
|
nextSteps: [
|
|
117
152
|
"check your internet connection",
|
|
118
153
|
"verify the gateway URL with `cruxy config get cruxy.gatewayUrl`",
|
|
@@ -129,7 +164,7 @@ export function apiError(underlying) {
|
|
|
129
164
|
title: status
|
|
130
165
|
? `the model provider returned an error (HTTP ${status})`
|
|
131
166
|
: "the model provider returned an error",
|
|
132
|
-
cause:
|
|
167
|
+
cause: scrubbedMessageOf(underlying),
|
|
133
168
|
nextSteps: [
|
|
134
169
|
"retry in a moment; if it persists, check the provider's status",
|
|
135
170
|
],
|
|
@@ -142,7 +177,7 @@ export function apiRateLimit(underlying) {
|
|
|
142
177
|
return new CruxyError({
|
|
143
178
|
code: ErrorCode.ApiRateLimit,
|
|
144
179
|
title: "rate limited by the model provider",
|
|
145
|
-
cause:
|
|
180
|
+
cause: scrubbedMessageOf(underlying),
|
|
146
181
|
nextSteps: [
|
|
147
182
|
retryAfterMs
|
|
148
183
|
? `wait ~${Math.ceil(retryAfterMs / 1000)}s and retry`
|
|
@@ -156,7 +191,7 @@ export function apiOverloaded(underlying) {
|
|
|
156
191
|
return new CruxyError({
|
|
157
192
|
code: ErrorCode.ApiOverloaded,
|
|
158
193
|
title: "the model provider is overloaded",
|
|
159
|
-
cause:
|
|
194
|
+
cause: scrubbedMessageOf(underlying),
|
|
160
195
|
nextSteps: ["retry in a few moments"],
|
|
161
196
|
underlying,
|
|
162
197
|
});
|
|
@@ -165,7 +200,7 @@ export function budgetExhausted(underlying) {
|
|
|
165
200
|
return new CruxyError({
|
|
166
201
|
code: ErrorCode.BudgetExhausted,
|
|
167
202
|
title: "your Cruxy budget is exhausted",
|
|
168
|
-
cause:
|
|
203
|
+
cause: scrubbedMessageOf(underlying),
|
|
169
204
|
nextSteps: ["top up or raise your budget, then retry"],
|
|
170
205
|
underlying,
|
|
171
206
|
});
|
|
@@ -567,6 +602,51 @@ export function testCommandNotFound() {
|
|
|
567
602
|
],
|
|
568
603
|
});
|
|
569
604
|
}
|
|
605
|
+
// ── persistent memory (exit 14) — C.29 ────────────────────────────────────────
|
|
606
|
+
/**
|
|
607
|
+
* A memory write was refused because the content matched a known secret shape
|
|
608
|
+
* (C.29). Secrets are NEVER persisted to memory — the fix is to remember a
|
|
609
|
+
* non-secret description, not the secret itself.
|
|
610
|
+
*/
|
|
611
|
+
export function memorySecretRefused(kind) {
|
|
612
|
+
return new CruxyError({
|
|
613
|
+
code: ErrorCode.MemorySecret,
|
|
614
|
+
title: "refused to save that note — it looks like it contains a secret",
|
|
615
|
+
cause: `the content matched a ${kind}; secrets are never written to memory`,
|
|
616
|
+
nextSteps: [
|
|
617
|
+
"remember a non-secret description instead (e.g. “the deploy key lives in 1Password”, not the key)",
|
|
618
|
+
],
|
|
619
|
+
meta: { kind },
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* A repo's project memory is present but has not been trusted on this machine
|
|
624
|
+
* (C.29). It is never recalled into the model's context silently — a cloned repo
|
|
625
|
+
* cannot inject notes until you review and trust them.
|
|
626
|
+
*/
|
|
627
|
+
export function memoryUntrusted(root) {
|
|
628
|
+
return new CruxyError({
|
|
629
|
+
code: ErrorCode.MemoryUntrusted,
|
|
630
|
+
title: "this project's memory has not been trusted",
|
|
631
|
+
cause: "project memory can arrive with a cloned repo (another author), so it is not recalled until you trust it",
|
|
632
|
+
nextSteps: [
|
|
633
|
+
"review the entries with `cruxy memory list`",
|
|
634
|
+
"then trust them with `cruxy memory trust .` (re-trust is required if they change)",
|
|
635
|
+
],
|
|
636
|
+
meta: { root },
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
/** A malformed memory entry was rejected (C.29) — excluded, never eval'd. */
|
|
640
|
+
export function memoryInvalid(detail) {
|
|
641
|
+
return new CruxyError({
|
|
642
|
+
code: ErrorCode.MemoryInvalid,
|
|
643
|
+
title: "a memory entry is malformed and was excluded",
|
|
644
|
+
cause: detail,
|
|
645
|
+
nextSteps: [
|
|
646
|
+
"inspect the file with `cruxy memory list`, or clear it with `cruxy memory clear`",
|
|
647
|
+
],
|
|
648
|
+
});
|
|
649
|
+
}
|
|
570
650
|
// ── internal (exit 1) ─────────────────────────────────────────────────────────
|
|
571
651
|
export function internal(underlying) {
|
|
572
652
|
return new CruxyError({
|
package/dist/errors/types.d.ts
CHANGED
|
@@ -21,6 +21,9 @@ export declare const ErrorCode: {
|
|
|
21
21
|
readonly PlanInvalid: "CRUXY_E_PLAN_INVALID";
|
|
22
22
|
readonly PlanRevisionLimit: "CRUXY_E_PLAN_REVISION_LIMIT";
|
|
23
23
|
readonly CheckpointNotFound: "CRUXY_E_CHECKPOINT_NOT_FOUND";
|
|
24
|
+
/** Routing (C.30): a task class is mapped to a tier the gateway does not
|
|
25
|
+
* offer — fix the config, NEVER a silent substitution to another tier. */
|
|
26
|
+
readonly RoutingTierUnavailable: "CRUXY_E_ROUTING_TIER_UNAVAILABLE";
|
|
24
27
|
readonly ConfigParse: "CRUXY_E_CONFIG_PARSE";
|
|
25
28
|
readonly ConfigInvalid: "CRUXY_E_CONFIG_INVALID";
|
|
26
29
|
readonly AuthMissingKey: "CRUXY_E_AUTH_MISSING_KEY";
|
|
@@ -68,6 +71,15 @@ export declare const ErrorCode: {
|
|
|
68
71
|
readonly HookInvalid: "CRUXY_E_HOOK_INVALID";
|
|
69
72
|
/** A malformed custom slash-command definition — excluded and surfaced. */
|
|
70
73
|
readonly SlashInvalid: "CRUXY_E_SLASH_INVALID";
|
|
74
|
+
/** A malformed memory entry — excluded from recall/writes and surfaced,
|
|
75
|
+
* never eval'd. */
|
|
76
|
+
readonly MemoryInvalid: "CRUXY_E_MEMORY_INVALID";
|
|
77
|
+
/** A repo's project memory has not been trusted — it is never recalled into
|
|
78
|
+
* context silently (supply-chain safety). */
|
|
79
|
+
readonly MemoryUntrusted: "CRUXY_E_MEMORY_UNTRUSTED";
|
|
80
|
+
/** A memory write was refused because the content matched a secret shape —
|
|
81
|
+
* secrets are never persisted (defense in depth over C.17). */
|
|
82
|
+
readonly MemorySecret: "CRUXY_E_MEMORY_SECRET";
|
|
71
83
|
};
|
|
72
84
|
export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
|
|
73
85
|
/** The process exit code for an error code (defaults to 1 for safety). */
|
package/dist/errors/types.js
CHANGED
|
@@ -23,6 +23,9 @@ export const ErrorCode = {
|
|
|
23
23
|
PlanInvalid: "CRUXY_E_PLAN_INVALID",
|
|
24
24
|
PlanRevisionLimit: "CRUXY_E_PLAN_REVISION_LIMIT",
|
|
25
25
|
CheckpointNotFound: "CRUXY_E_CHECKPOINT_NOT_FOUND",
|
|
26
|
+
/** Routing (C.30): a task class is mapped to a tier the gateway does not
|
|
27
|
+
* offer — fix the config, NEVER a silent substitution to another tier. */
|
|
28
|
+
RoutingTierUnavailable: "CRUXY_E_ROUTING_TIER_UNAVAILABLE",
|
|
26
29
|
// config (exit 3)
|
|
27
30
|
ConfigParse: "CRUXY_E_CONFIG_PARSE",
|
|
28
31
|
ConfigInvalid: "CRUXY_E_CONFIG_INVALID",
|
|
@@ -82,6 +85,16 @@ export const ErrorCode = {
|
|
|
82
85
|
HookInvalid: "CRUXY_E_HOOK_INVALID",
|
|
83
86
|
/** A malformed custom slash-command definition — excluded and surfaced. */
|
|
84
87
|
SlashInvalid: "CRUXY_E_SLASH_INVALID",
|
|
88
|
+
// persistent memory (exit 14) — C.29
|
|
89
|
+
/** A malformed memory entry — excluded from recall/writes and surfaced,
|
|
90
|
+
* never eval'd. */
|
|
91
|
+
MemoryInvalid: "CRUXY_E_MEMORY_INVALID",
|
|
92
|
+
/** A repo's project memory has not been trusted — it is never recalled into
|
|
93
|
+
* context silently (supply-chain safety). */
|
|
94
|
+
MemoryUntrusted: "CRUXY_E_MEMORY_UNTRUSTED",
|
|
95
|
+
/** A memory write was refused because the content matched a secret shape —
|
|
96
|
+
* secrets are never persisted (defense in depth over C.17). */
|
|
97
|
+
MemorySecret: "CRUXY_E_MEMORY_SECRET",
|
|
85
98
|
};
|
|
86
99
|
/**
|
|
87
100
|
* Category exit codes. Distinct per category so a caller (CI, a script) can
|
|
@@ -97,6 +110,7 @@ const EXIT_CODES = {
|
|
|
97
110
|
[ErrorCode.PlanInvalid]: 2,
|
|
98
111
|
[ErrorCode.PlanRevisionLimit]: 2,
|
|
99
112
|
[ErrorCode.CheckpointNotFound]: 2,
|
|
113
|
+
[ErrorCode.RoutingTierUnavailable]: 2,
|
|
100
114
|
[ErrorCode.ConfigParse]: 3,
|
|
101
115
|
[ErrorCode.ConfigInvalid]: 3,
|
|
102
116
|
[ErrorCode.AuthMissingKey]: 4,
|
|
@@ -142,6 +156,12 @@ const EXIT_CODES = {
|
|
|
142
156
|
[ErrorCode.HookUntrusted]: 13,
|
|
143
157
|
[ErrorCode.HookInvalid]: 13,
|
|
144
158
|
[ErrorCode.SlashInvalid]: 13,
|
|
159
|
+
// Persistent memory (C.29). A malformed entry and an untrusted project are
|
|
160
|
+
// data-safety problems; a refused secret write is a security stop — grouped
|
|
161
|
+
// for a greppable exit code.
|
|
162
|
+
[ErrorCode.MemoryInvalid]: 14,
|
|
163
|
+
[ErrorCode.MemoryUntrusted]: 14,
|
|
164
|
+
[ErrorCode.MemorySecret]: 14,
|
|
145
165
|
};
|
|
146
166
|
/** The process exit code for an error code (defaults to 1 for safety). */
|
|
147
167
|
export function exitCodeFor(code) {
|
package/dist/hooks/types.d.ts
CHANGED
|
@@ -103,8 +103,8 @@ export declare const SlashFrontmatterSchema: z.ZodObject<{
|
|
|
103
103
|
command?: string | undefined;
|
|
104
104
|
}, {
|
|
105
105
|
description: string;
|
|
106
|
-
command?: string | undefined;
|
|
107
106
|
kind?: "shell" | "prompt" | undefined;
|
|
107
|
+
command?: string | undefined;
|
|
108
108
|
}>;
|
|
109
109
|
export type SlashFrontmatter = z.infer<typeof SlashFrontmatterSchema>;
|
|
110
110
|
/** A validated custom slash command, tagged with source. */
|