@theokit/agents 13.0.0 → 13.2.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/CHANGELOG.md +213 -0
- package/dist/{agent-compiler-B0hb6HCo.d.ts → agent-compiler-BaOZzU7Q.d.ts} +26 -3
- package/dist/auth.d.ts +28 -1
- package/dist/auth.js +58 -0
- package/dist/auth.js.map +1 -1
- package/dist/{bridge-entry-DBkqwe6b.d.ts → bridge-entry-BgWauyLQ.d.ts} +4 -4
- package/dist/bridge.d.ts +5 -5
- package/dist/bridge.js +4 -4
- package/dist/{chunk-HGVT4VCE.js → chunk-2SP5BB6U.js} +39 -6
- package/dist/chunk-2SP5BB6U.js.map +1 -0
- package/dist/{chunk-KTQID5V3.js → chunk-CBP72LFM.js} +9 -3
- package/dist/chunk-CBP72LFM.js.map +1 -0
- package/dist/{chunk-PMMOOXR6.js → chunk-PYVLRSXZ.js} +22 -2
- package/dist/chunk-PYVLRSXZ.js.map +1 -0
- package/dist/{chunk-G7QBDGZ4.js → chunk-SL3QW5WZ.js} +32 -7
- package/dist/{chunk-G7QBDGZ4.js.map → chunk-SL3QW5WZ.js.map} +1 -1
- package/dist/config.d.ts +154 -4
- package/dist/config.js +312 -14
- package/dist/config.js.map +1 -1
- package/dist/{define-agent-DhwNmdej.d.ts → define-agent-DOZL_0uP.d.ts} +2 -2
- package/dist/{delegation-scoring-BzJEheml.d.ts → delegation-scoring-DLsMQ4BJ.d.ts} +14 -2
- package/dist/index.d.ts +9 -9
- package/dist/index.js +4 -4
- package/dist/{setting-sources-gate-DFu51i50.d.ts → setting-sources-gate-BXdZmY78.d.ts} +7 -1
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +2 -1
- package/dist/testing.js.map +1 -1
- package/dist/tools.d.ts +3 -3
- package/dist/tools.js +3 -2
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-HGVT4VCE.js.map +0 -1
- package/dist/chunk-KTQID5V3.js.map +0 -1
- package/dist/chunk-PMMOOXR6.js.map +0 -1
package/dist/config.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { TheokitAgentError } from '@theokit/sdk/errors';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { TrustPosture, LayerValues } from '@theokit/sdk';
|
|
4
|
-
export { DeclaredLayer, LayerValues } from '@theokit/sdk';
|
|
5
|
-
import { R as ResolvedCompatSource } from './setting-sources-gate-
|
|
3
|
+
import { TrustPosture, LayerValues, PermissionRule } from '@theokit/sdk';
|
|
4
|
+
export { DeclaredLayer, LayerValues, PermissionAction, PermissionRule } from '@theokit/sdk';
|
|
5
|
+
import { R as ResolvedCompatSource } from './setting-sources-gate-BXdZmY78.js';
|
|
6
6
|
export { resolveEffectiveContextWindow as effectiveContextWindow } from '@theokit/sdk/compaction';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -683,6 +683,156 @@ declare function layerPrecedence(layer: SettingsLayer): number;
|
|
|
683
683
|
*/
|
|
684
684
|
declare function settingsLayerChain(values: Partial<Record<SettingsLayer, Readonly<Record<string, unknown>>>>): LayerValues[];
|
|
685
685
|
|
|
686
|
+
/**
|
|
687
|
+
* A style was named and could not be read.
|
|
688
|
+
*
|
|
689
|
+
* Typed, and carrying the directories it searched, because the whole point of the refusal is that
|
|
690
|
+
* the author learns WHICH lookup failed. `error-handling.md` § 2: fail fast, fail clear.
|
|
691
|
+
*/
|
|
692
|
+
declare class OutputStyleError extends TheokitAgentError {
|
|
693
|
+
readonly name = "OutputStyleError";
|
|
694
|
+
constructor(message: string);
|
|
695
|
+
}
|
|
696
|
+
interface OutputStyle {
|
|
697
|
+
/** The filename without `.md` — what `outputStyle` in settings refers to. */
|
|
698
|
+
readonly name: string;
|
|
699
|
+
/** The body, which becomes a section of the system prompt. */
|
|
700
|
+
readonly content: string;
|
|
701
|
+
/** The `description` frontmatter key, when present. */
|
|
702
|
+
readonly description?: string;
|
|
703
|
+
/**
|
|
704
|
+
* `keep-coding-instructions: true` in frontmatter.
|
|
705
|
+
*
|
|
706
|
+
* The field with teeth. A style REPLACES the built-in software-engineering instructions by
|
|
707
|
+
* default, so a consumer who does not know that loses them without being told. Carried here so
|
|
708
|
+
* the decision belongs to the caller rather than to whoever wrote the style file.
|
|
709
|
+
*/
|
|
710
|
+
readonly keepCodingInstructions: boolean;
|
|
711
|
+
}
|
|
712
|
+
interface LoadOutputStyleInput {
|
|
713
|
+
/** The style to load, from `settings.json`'s `outputStyle`. `undefined` means none was asked for. */
|
|
714
|
+
readonly name: string | undefined;
|
|
715
|
+
/** Project root. Its `.claude/output-styles/` wins over the home directory's. */
|
|
716
|
+
readonly cwd: string;
|
|
717
|
+
/** Home directory, for styles that apply across every project. Omit to search the project only. */
|
|
718
|
+
readonly homeDir?: string;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* The named style, or `undefined` when none was requested.
|
|
722
|
+
*
|
|
723
|
+
* @throws OutputStyleError when a style IS named and no file backs it, listing the directories that
|
|
724
|
+
* were searched.
|
|
725
|
+
*/
|
|
726
|
+
declare function loadOutputStyle(input: LoadOutputStyleInput): OutputStyle | undefined;
|
|
727
|
+
/**
|
|
728
|
+
* The style the project's settings name, read from the project's style files.
|
|
729
|
+
*
|
|
730
|
+
* The two halves of the documented mechanism, joined. `loadSettings` reads the `outputStyle` key and
|
|
731
|
+
* {@link loadOutputStyle} reads the file; until this existed nothing connected them, so a consumer
|
|
732
|
+
* had to find both modules and know that one feeds the other. Two halves each individually correct
|
|
733
|
+
* and jointly unreachable is the same defect as a capability with no caller — it just takes two
|
|
734
|
+
* files to make.
|
|
735
|
+
*
|
|
736
|
+
* Deliberately does NOT soften the loader: a style named in settings with no file behind it still
|
|
737
|
+
* throws {@link OutputStyleError}. Returning `undefined` there would restore the exact silence this
|
|
738
|
+
* work removed — configured, not applied, and no way to tell which.
|
|
739
|
+
*
|
|
740
|
+
* @throws OutputStyleError when settings name a style and no file backs it.
|
|
741
|
+
*/
|
|
742
|
+
declare function resolveOutputStyle(input: {
|
|
743
|
+
readonly cwd: string;
|
|
744
|
+
readonly homeDir?: string;
|
|
745
|
+
readonly onWarn?: (message: string) => void;
|
|
746
|
+
}): OutputStyle | undefined;
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Translate a settings `permissions` block into rules the SDK's engine evaluates.
|
|
750
|
+
*
|
|
751
|
+
* The SDK ships `PermissionEngine(rules, { defaultAction })` and it is a CODE surface: you construct
|
|
752
|
+
* it with `PermissionRule[]`. What was missing was the path from the FILE to those rules. Measured
|
|
753
|
+
* 2026-09-11: `permissions` appears in 13 SDK files, and the SDK reads `settings.json` in three, of
|
|
754
|
+
* which two are sourcemaps and the third describes the hooks shape. An operator writing
|
|
755
|
+
* `{ "permissions": { "deny": ["Bash(curl:*)"] } }` got a file nothing translated.
|
|
756
|
+
*
|
|
757
|
+
* ## Partial fidelity would be worse than absence
|
|
758
|
+
*
|
|
759
|
+
* A `deny` that silently fails to match is a control the operator believes is in force — strictly
|
|
760
|
+
* more dangerous than no rule, because without one they would have written the guard themselves. So
|
|
761
|
+
* every entry this module cannot render faithfully is RETURNED as unsupported, with a reason, and
|
|
762
|
+
* excluded from the rules. Nothing is quietly turned into a matcher that does not bite.
|
|
763
|
+
*
|
|
764
|
+
* ## What it deliberately does not attempt
|
|
765
|
+
*
|
|
766
|
+
* The reference's specifier language is larger than this: path globs with `~` expansion, per-tool
|
|
767
|
+
* argument names, and matching semantics that differ by tool. Implementing a lookalike would produce
|
|
768
|
+
* rules that match *almost* the right calls, which is the failure above. Two forms are translated —
|
|
769
|
+
* a bare tool name, and `Tool(prefix:*)` / `Tool(exact)` — and everything else is reported.
|
|
770
|
+
*/
|
|
771
|
+
|
|
772
|
+
/** An entry that was understood well enough to refuse, but not well enough to translate. */
|
|
773
|
+
interface UnsupportedPermissionEntry {
|
|
774
|
+
/** The entry as written, so the operator can find it in their file. */
|
|
775
|
+
readonly entry: string;
|
|
776
|
+
/** Why it was not translated. */
|
|
777
|
+
readonly reason: string;
|
|
778
|
+
}
|
|
779
|
+
interface PermissionTranslation {
|
|
780
|
+
readonly rules: readonly PermissionRule[];
|
|
781
|
+
readonly unsupported: readonly UnsupportedPermissionEntry[];
|
|
782
|
+
}
|
|
783
|
+
/** The `permissions` block as a settings file may carry it. */
|
|
784
|
+
interface PermissionsBlock {
|
|
785
|
+
readonly allow?: readonly string[];
|
|
786
|
+
readonly deny?: readonly string[];
|
|
787
|
+
readonly ask?: readonly string[];
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* The rules a `permissions` block means, and the entries that could not be rendered.
|
|
791
|
+
*
|
|
792
|
+
* An absent or empty block yields nothing and reports nothing: most projects declare no permissions,
|
|
793
|
+
* and a translator that warned there would be noise in every one of them.
|
|
794
|
+
*/
|
|
795
|
+
declare function permissionRulesFromSettings(block: PermissionsBlock | undefined): PermissionTranslation;
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* The keys this layer can act on today, plus everything else untouched.
|
|
799
|
+
*
|
|
800
|
+
* Typed narrowly on purpose: a key is listed here when something in this package reads it, not
|
|
801
|
+
* because the reference documents it. Declaring a field the code ignores would publish a control
|
|
802
|
+
* that does nothing, which is the shape every item in this batch had.
|
|
803
|
+
*/
|
|
804
|
+
declare const SETTINGS_SCHEMA: z.ZodObject<{
|
|
805
|
+
outputStyle: z.ZodOptional<z.ZodString>;
|
|
806
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
807
|
+
permissions: z.ZodOptional<z.ZodObject<{
|
|
808
|
+
allow: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
809
|
+
deny: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
810
|
+
ask: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
811
|
+
}, z.core.$loose>>;
|
|
812
|
+
}, z.core.$loose>;
|
|
813
|
+
type Settings = z.infer<typeof SETTINGS_SCHEMA>;
|
|
814
|
+
interface LoadSettingsInput {
|
|
815
|
+
/** Project root. Its `.claude/settings.json` and `.claude/settings.local.json` are read. */
|
|
816
|
+
readonly cwd: string;
|
|
817
|
+
/** Home directory, for the user-level file. Omit to read the project only. */
|
|
818
|
+
readonly homeDir?: string;
|
|
819
|
+
/** Where a file that exists and cannot be parsed is reported. */
|
|
820
|
+
readonly onWarn?: (message: string) => void;
|
|
821
|
+
}
|
|
822
|
+
interface LoadSettingsResult {
|
|
823
|
+
readonly values: Settings;
|
|
824
|
+
/** Which layer each resolved key came from. */
|
|
825
|
+
readonly provenancePerKey: Readonly<Record<string, string>>;
|
|
826
|
+
/** Which declared layers contributed nothing — a file nobody is reading is named, not hidden. */
|
|
827
|
+
readonly precedenceReport: PrecedenceReport;
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* The settings as the declared layers resolve them.
|
|
831
|
+
*
|
|
832
|
+
* Never throws for a missing or malformed file; see {@link readLayer}.
|
|
833
|
+
*/
|
|
834
|
+
declare function loadSettings(input: LoadSettingsInput): LoadSettingsResult;
|
|
835
|
+
|
|
686
836
|
/**
|
|
687
837
|
* Split a markdown file into frontmatter lines and body.
|
|
688
838
|
*
|
|
@@ -772,4 +922,4 @@ declare class ContextPressureThresholdError extends TheokitAgentError {
|
|
|
772
922
|
*/
|
|
773
923
|
declare function contextPressure(usedTokens: number, effectiveWindow: number, thresholds?: ContextPressureThresholds): ContextPressure;
|
|
774
924
|
|
|
775
|
-
export { type ComposeInstructionsOptions, type ComposedInstructions, type ConfigLayer, type ContextPressure, ContextPressureThresholdError, type ContextPressureThresholds, type CustomCommand, type CustomCommandsResult, DEFAULT_CONTEXT_PRESSURE_THRESHOLDS, type ExpandImportsInput, FILE_INLINE_CAP, type InstructionBlock, type InstructionSource, type InstructionTree, type InstructionTreeBudget, LAYERS_ARE_POSITIONED, LayerOutOfOrderError, LayeredConfig, type LayeredConfigInput, type LayeredConfigResult, type LoadCustomCommandsInput, type LoadInstructionTreeInput, type ParsedFrontmatter, type PrecedenceReport, type ProvenancePerKey, SETTINGS_LAYERS, type SettingsLayer, type ShellResult, type TemplateDeps, type TrustRecord, TrustStore, TrustStorePermissionsError, blockAppliesTo, composeInstructions, contextPressure, expandCommandTemplate, expandInstructionImports, frontmatterValue, layerPrecedence, loadCustomCommands, loadInstructionTree, settingsLayerChain, splitFrontmatter, templateHints };
|
|
925
|
+
export { type ComposeInstructionsOptions, type ComposedInstructions, type ConfigLayer, type ContextPressure, ContextPressureThresholdError, type ContextPressureThresholds, type CustomCommand, type CustomCommandsResult, DEFAULT_CONTEXT_PRESSURE_THRESHOLDS, type ExpandImportsInput, FILE_INLINE_CAP, type InstructionBlock, type InstructionSource, type InstructionTree, type InstructionTreeBudget, LAYERS_ARE_POSITIONED, LayerOutOfOrderError, LayeredConfig, type LayeredConfigInput, type LayeredConfigResult, type LoadCustomCommandsInput, type LoadInstructionTreeInput, type LoadOutputStyleInput, type LoadSettingsInput, type LoadSettingsResult, type OutputStyle, OutputStyleError, type ParsedFrontmatter, type PermissionTranslation, type PermissionsBlock, type PrecedenceReport, type ProvenancePerKey, SETTINGS_LAYERS, type Settings, type SettingsLayer, type ShellResult, type TemplateDeps, type TrustRecord, TrustStore, TrustStorePermissionsError, type UnsupportedPermissionEntry, blockAppliesTo, composeInstructions, contextPressure, expandCommandTemplate, expandInstructionImports, frontmatterValue, layerPrecedence, loadCustomCommands, loadInstructionTree, loadOutputStyle, loadSettings, permissionRulesFromSettings, resolveOutputStyle, settingsLayerChain, splitFrontmatter, templateHints };
|
package/dist/config.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ensureSecureDir
|
|
3
3
|
} from "./chunk-D2EFYZBV.js";
|
|
4
|
-
import {
|
|
5
|
-
currentOperatorPolicy
|
|
6
|
-
} from "./chunk-HGVT4VCE.js";
|
|
7
4
|
import {
|
|
8
5
|
ConfigurationError
|
|
9
6
|
} from "./chunk-U72XTMYB.js";
|
|
7
|
+
import {
|
|
8
|
+
currentOperatorPolicy
|
|
9
|
+
} from "./chunk-2SP5BB6U.js";
|
|
10
10
|
import {
|
|
11
11
|
__name
|
|
12
12
|
} from "./chunk-Z4QWC7IK.js";
|
|
@@ -564,23 +564,61 @@ function composeInstructions(base, sources, options) {
|
|
|
564
564
|
__name(composeInstructions, "composeInstructions");
|
|
565
565
|
|
|
566
566
|
// src/config/custom-commands.ts
|
|
567
|
-
import { readFileSync as readFileSync4, readdirSync as
|
|
568
|
-
import { extname, join as
|
|
569
|
-
|
|
570
|
-
|
|
567
|
+
import { readFileSync as readFileSync4, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
568
|
+
import { extname, join as join3, relative as relative2 } from "path";
|
|
569
|
+
|
|
570
|
+
// src/bridge/unloaded-surfaces.ts
|
|
571
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
|
|
572
|
+
import { join as join2 } from "path";
|
|
573
|
+
var WORKFLOWS_DIR = join2(".claude", "workflows");
|
|
574
|
+
var SCRIPT_SUFFIXES = [
|
|
575
|
+
".js",
|
|
576
|
+
".mjs",
|
|
577
|
+
".cjs"
|
|
578
|
+
];
|
|
579
|
+
function reportUnloadedSurfaces(input) {
|
|
580
|
+
const dir = join2(input.cwd, WORKFLOWS_DIR);
|
|
581
|
+
if (!existsSync3(dir)) return;
|
|
582
|
+
let scripts;
|
|
583
|
+
try {
|
|
584
|
+
scripts = readdirSync2(dir).filter((f) => SCRIPT_SUFFIXES.some((s) => f.endsWith(s)));
|
|
585
|
+
} catch {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
if (scripts.length === 0) return;
|
|
589
|
+
input.onWarn(`${WORKFLOWS_DIR}: found ${String(scripts.length)} workflow script(s) \u2014 ${[
|
|
590
|
+
...scripts
|
|
591
|
+
].sort((a, b) => a.localeCompare(b)).join(", ")} \u2014 which this runtime does NOT execute. Every configuration surface it loads is data (commands, hooks, plugins, skills, subagents); a workflow file is code, and executing JavaScript found under a caller-supplied directory is a trust decision that belongs to you rather than to this library. The orchestration itself is supported: build the same pipeline with Workflow / agentStep / createSquad from @theokit/sdk, which you import explicitly.`);
|
|
592
|
+
}
|
|
593
|
+
__name(reportUnloadedSurfaces, "reportUnloadedSurfaces");
|
|
594
|
+
|
|
595
|
+
// src/config/custom-commands.ts
|
|
596
|
+
var COMMANDS_DIR = join3(".theokit", "commands");
|
|
597
|
+
var COMPAT_COMMANDS_DIR = join3(".claude", "commands");
|
|
571
598
|
var CLAUDE_CODE_SOURCE = "claude-code";
|
|
572
599
|
var IGNORE_WARNING3 = /* @__PURE__ */ __name(() => void 0, "IGNORE_WARNING");
|
|
573
600
|
function declaresCommands(sources) {
|
|
574
601
|
return sources?.some((source) => typeof source === "string" ? source === CLAUDE_CODE_SOURCE : source.import.includes("commands")) === true;
|
|
575
602
|
}
|
|
576
603
|
__name(declaresCommands, "declaresCommands");
|
|
604
|
+
function declaresClaudeCode(sources) {
|
|
605
|
+
return sources?.some((source) => (
|
|
606
|
+
// The narrowed form IS a declaration of this dialect by construction — its `kind` is the one
|
|
607
|
+
// literal, which is why `declaresCommands` does not re-check it either and why the
|
|
608
|
+
// `NarrowedCompatSource` docblock says a second dialect must revisit both. Comparing it here
|
|
609
|
+
// would be a runtime check the type already makes, and `no-unnecessary-condition` is right
|
|
610
|
+
// about that: the type is not lying, there is exactly one dialect.
|
|
611
|
+
typeof source !== "string" || source === CLAUDE_CODE_SOURCE
|
|
612
|
+
)) === true;
|
|
613
|
+
}
|
|
614
|
+
__name(declaresClaudeCode, "declaresClaudeCode");
|
|
577
615
|
function projectCommandDirs(input) {
|
|
578
616
|
if (input.projectDir === void 0) return [];
|
|
579
617
|
const dirs = [];
|
|
580
618
|
if (declaresCommands(input.compatSources)) {
|
|
581
|
-
dirs.push(
|
|
619
|
+
dirs.push(join3(input.projectDir, COMPAT_COMMANDS_DIR));
|
|
582
620
|
}
|
|
583
|
-
dirs.push(
|
|
621
|
+
dirs.push(join3(input.projectDir, COMMANDS_DIR));
|
|
584
622
|
return dirs;
|
|
585
623
|
}
|
|
586
624
|
__name(projectCommandDirs, "projectCommandDirs");
|
|
@@ -606,8 +644,14 @@ __name(mergeProjectCommands, "mergeProjectCommands");
|
|
|
606
644
|
function loadCustomCommands(input) {
|
|
607
645
|
const warn = input.onWarn ?? IGNORE_WARNING3;
|
|
608
646
|
const loaded = /* @__PURE__ */ new Map();
|
|
647
|
+
if (input.projectDir !== void 0 && declaresClaudeCode(input.compatSources)) {
|
|
648
|
+
reportUnloadedSurfaces({
|
|
649
|
+
cwd: input.projectDir,
|
|
650
|
+
onWarn: warn
|
|
651
|
+
});
|
|
652
|
+
}
|
|
609
653
|
if (input.homeDir !== void 0) {
|
|
610
|
-
for (const command of readCommandsDir(
|
|
654
|
+
for (const command of readCommandsDir(join3(input.homeDir, COMMANDS_DIR), "user", warn)) {
|
|
611
655
|
loaded.set(command.name, command);
|
|
612
656
|
}
|
|
613
657
|
}
|
|
@@ -636,14 +680,14 @@ __name(byProjectThenName, "byProjectThenName");
|
|
|
636
680
|
function readCommandsDir(dir, source, warn, root = dir) {
|
|
637
681
|
let entries;
|
|
638
682
|
try {
|
|
639
|
-
entries =
|
|
683
|
+
entries = readdirSync3(dir);
|
|
640
684
|
} catch {
|
|
641
685
|
return [];
|
|
642
686
|
}
|
|
643
687
|
entries.sort((a, b) => a.localeCompare(b));
|
|
644
688
|
const commands = [];
|
|
645
689
|
for (const entry of entries) {
|
|
646
|
-
const path =
|
|
690
|
+
const path = join3(dir, entry);
|
|
647
691
|
let stats;
|
|
648
692
|
try {
|
|
649
693
|
stats = statSync3(path);
|
|
@@ -824,14 +868,263 @@ function settingsLayerChain(values) {
|
|
|
824
868
|
}
|
|
825
869
|
__name(settingsLayerChain, "settingsLayerChain");
|
|
826
870
|
|
|
871
|
+
// src/config/output-styles.ts
|
|
872
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
|
|
873
|
+
import { join as join5 } from "path";
|
|
874
|
+
import { TheokitAgentError as TheokitAgentError3 } from "@theokit/sdk/errors";
|
|
875
|
+
|
|
876
|
+
// src/config/settings-file.ts
|
|
877
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
|
|
878
|
+
import { join as join4 } from "path";
|
|
879
|
+
import { z } from "zod";
|
|
880
|
+
var SETTINGS_SCHEMA = z.looseObject({
|
|
881
|
+
/** Selects a file under `.claude/output-styles/`. Consumed by `loadOutputStyle`. */
|
|
882
|
+
outputStyle: z.string().optional(),
|
|
883
|
+
/** Environment variables for the session. */
|
|
884
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
885
|
+
/**
|
|
886
|
+
* Tool permissions, as `settings-permissions.ts` translates them into `PermissionRule[]`.
|
|
887
|
+
*
|
|
888
|
+
* Typed here rather than left to the passthrough because the passthrough yields `unknown`, and a
|
|
889
|
+
* consumer would have to cast to reach it — a cast is where a wrong shape stops being checked. The
|
|
890
|
+
* SAFETY of this key depends on the translator seeing what the operator actually wrote.
|
|
891
|
+
*/
|
|
892
|
+
permissions: z.object({
|
|
893
|
+
allow: z.array(z.string()).optional(),
|
|
894
|
+
deny: z.array(z.string()).optional(),
|
|
895
|
+
ask: z.array(z.string()).optional()
|
|
896
|
+
}).loose().optional()
|
|
897
|
+
});
|
|
898
|
+
var IGNORE_WARNING4 = /* @__PURE__ */ __name(() => void 0, "IGNORE_WARNING");
|
|
899
|
+
var FILE_LAYERS = [
|
|
900
|
+
{
|
|
901
|
+
layer: "user",
|
|
902
|
+
dir: "home",
|
|
903
|
+
file: "settings.json"
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
layer: "project-shared",
|
|
907
|
+
dir: "cwd",
|
|
908
|
+
file: "settings.json"
|
|
909
|
+
},
|
|
910
|
+
{
|
|
911
|
+
layer: "project-local",
|
|
912
|
+
dir: "cwd",
|
|
913
|
+
file: "settings.local.json"
|
|
914
|
+
}
|
|
915
|
+
];
|
|
916
|
+
function readLayer(path, warn) {
|
|
917
|
+
if (!existsSync4(path)) return {};
|
|
918
|
+
try {
|
|
919
|
+
const parsed = JSON.parse(readFileSync5(path, "utf8"));
|
|
920
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
921
|
+
warn(`${path} is not a JSON object \u2014 it is NOT being applied.`);
|
|
922
|
+
return {};
|
|
923
|
+
}
|
|
924
|
+
return parsed;
|
|
925
|
+
} catch (error) {
|
|
926
|
+
warn(`${path} could not be parsed (${error.message}) \u2014 it is NOT being applied.`);
|
|
927
|
+
return {};
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
__name(readLayer, "readLayer");
|
|
931
|
+
function loadSettings(input) {
|
|
932
|
+
const warn = input.onWarn ?? IGNORE_WARNING4;
|
|
933
|
+
const layers = FILE_LAYERS.flatMap((entry) => {
|
|
934
|
+
const root = entry.dir === "home" ? input.homeDir : input.cwd;
|
|
935
|
+
if (root === void 0) return [];
|
|
936
|
+
const declared = SETTINGS_LAYERS.find((l) => l.layer === entry.layer);
|
|
937
|
+
if (declared === void 0) return [];
|
|
938
|
+
return [
|
|
939
|
+
{
|
|
940
|
+
layer: entry.layer,
|
|
941
|
+
precedence: declared.precedence,
|
|
942
|
+
values: readLayer(join4(root, ".claude", entry.file), warn)
|
|
943
|
+
}
|
|
944
|
+
];
|
|
945
|
+
});
|
|
946
|
+
const ordered = [
|
|
947
|
+
...layers
|
|
948
|
+
].sort((a, b) => (a.precedence ?? 0) - (b.precedence ?? 0));
|
|
949
|
+
const resolved = LayeredConfig.resolve({
|
|
950
|
+
layers: ordered,
|
|
951
|
+
schema: SETTINGS_SCHEMA
|
|
952
|
+
});
|
|
953
|
+
return {
|
|
954
|
+
values: resolved.value,
|
|
955
|
+
provenancePerKey: resolved.provenancePerKey,
|
|
956
|
+
precedenceReport: resolved.precedenceReport
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
__name(loadSettings, "loadSettings");
|
|
960
|
+
|
|
961
|
+
// src/config/output-styles.ts
|
|
962
|
+
var OUTPUT_STYLES_DIR = join5(".claude", "output-styles");
|
|
963
|
+
var OutputStyleError = class extends TheokitAgentError3 {
|
|
964
|
+
static {
|
|
965
|
+
__name(this, "OutputStyleError");
|
|
966
|
+
}
|
|
967
|
+
name = "OutputStyleError";
|
|
968
|
+
constructor(message) {
|
|
969
|
+
super(`[@theokit/agents] ${message}`, {
|
|
970
|
+
code: "output_style_not_found",
|
|
971
|
+
isRetryable: false
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
function loadOutputStyle(input) {
|
|
976
|
+
if (input.name === void 0) return void 0;
|
|
977
|
+
const searched = [
|
|
978
|
+
join5(input.cwd, OUTPUT_STYLES_DIR),
|
|
979
|
+
...input.homeDir === void 0 ? [] : [
|
|
980
|
+
join5(input.homeDir, OUTPUT_STYLES_DIR)
|
|
981
|
+
]
|
|
982
|
+
];
|
|
983
|
+
for (const dir of searched) {
|
|
984
|
+
const path = join5(dir, `${input.name}.md`);
|
|
985
|
+
if (!existsSync5(path)) continue;
|
|
986
|
+
const raw = readFileSync6(path, "utf8");
|
|
987
|
+
const parsed = splitFrontmatter(raw);
|
|
988
|
+
const body = parsed === void 0 ? raw : parsed.body;
|
|
989
|
+
const description = parsed === void 0 ? void 0 : frontmatterValue(parsed.frontmatter, "description");
|
|
990
|
+
return {
|
|
991
|
+
name: input.name,
|
|
992
|
+
content: body.trim(),
|
|
993
|
+
...description === void 0 ? {} : {
|
|
994
|
+
description
|
|
995
|
+
},
|
|
996
|
+
keepCodingInstructions: parsed !== void 0 && frontmatterValue(parsed.frontmatter, "keep-coding-instructions") === "true"
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
throw new OutputStyleError(`output style "${input.name}" is configured and no file backs it. Looked for ${input.name}.md in: ${searched.join(", ")}. A style that cannot be read is not applied, and a silent fallback would be indistinguishable from a style that had no effect.`);
|
|
1000
|
+
}
|
|
1001
|
+
__name(loadOutputStyle, "loadOutputStyle");
|
|
1002
|
+
function resolveOutputStyle(input) {
|
|
1003
|
+
const settings = loadSettings(input);
|
|
1004
|
+
return loadOutputStyle({
|
|
1005
|
+
...input,
|
|
1006
|
+
name: settings.values.outputStyle
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
__name(resolveOutputStyle, "resolveOutputStyle");
|
|
1010
|
+
|
|
1011
|
+
// src/config/settings-permissions.ts
|
|
1012
|
+
var ACTIONS = [
|
|
1013
|
+
"deny",
|
|
1014
|
+
"ask",
|
|
1015
|
+
"allow"
|
|
1016
|
+
];
|
|
1017
|
+
var MAX_TOOL = 64;
|
|
1018
|
+
var MAX_SPEC = 512;
|
|
1019
|
+
function splitEntry(entry) {
|
|
1020
|
+
const open = entry.indexOf("(");
|
|
1021
|
+
if (open === -1) return isToolName(entry) ? {
|
|
1022
|
+
tool: entry
|
|
1023
|
+
} : void 0;
|
|
1024
|
+
if (!entry.endsWith(")")) return void 0;
|
|
1025
|
+
const tool = entry.slice(0, open);
|
|
1026
|
+
const spec = entry.slice(open + 1, -1);
|
|
1027
|
+
if (!isToolName(tool) || spec.length > MAX_SPEC || spec.includes(")")) return void 0;
|
|
1028
|
+
return {
|
|
1029
|
+
tool,
|
|
1030
|
+
spec
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
__name(splitEntry, "splitEntry");
|
|
1034
|
+
function isToolName(value) {
|
|
1035
|
+
if (value.length === 0 || value.length > MAX_TOOL) return false;
|
|
1036
|
+
return /^[A-Za-z_][\w-]*$/.test(value);
|
|
1037
|
+
}
|
|
1038
|
+
__name(isToolName, "isToolName");
|
|
1039
|
+
var UNTRANSLATABLE = /[*?[\]{}~]/;
|
|
1040
|
+
function escapeRegExp(literal) {
|
|
1041
|
+
return literal.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
1042
|
+
}
|
|
1043
|
+
__name(escapeRegExp, "escapeRegExp");
|
|
1044
|
+
function translate(entry, action) {
|
|
1045
|
+
const parsed = splitEntry(entry.trim());
|
|
1046
|
+
if (parsed === void 0) {
|
|
1047
|
+
return {
|
|
1048
|
+
entry,
|
|
1049
|
+
reason: "not of the form `Tool` or `Tool(specifier)`."
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
const { tool, spec } = parsed;
|
|
1053
|
+
if (spec === void 0) return {
|
|
1054
|
+
tool,
|
|
1055
|
+
action
|
|
1056
|
+
};
|
|
1057
|
+
if (spec.length === 0) {
|
|
1058
|
+
return {
|
|
1059
|
+
entry,
|
|
1060
|
+
reason: "has an empty specifier \u2014 write `Tool` if you meant the whole tool."
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
const prefix = spec.endsWith(":*") ? spec.slice(0, -2) : void 0;
|
|
1064
|
+
const literal = prefix ?? spec;
|
|
1065
|
+
if (UNTRANSLATABLE.test(literal)) {
|
|
1066
|
+
return {
|
|
1067
|
+
entry,
|
|
1068
|
+
reason: "uses glob or path syntax this runtime does not translate. It is NOT being applied \u2014 a rule that matched almost the right calls would be worse than none, because you would believe a control is in force. Express it in code with `PermissionRule` instead."
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
return {
|
|
1072
|
+
tool,
|
|
1073
|
+
// Anchored at the start either way: unanchored, `Bash(ls)` would also deny `please ls`. The
|
|
1074
|
+
// prefix form stays open at the end, which is what `:*` means; the exact form closes with `$`
|
|
1075
|
+
// so `Bash(ls)` does not deny `ls-everything`.
|
|
1076
|
+
args: {
|
|
1077
|
+
// Built from the operator's own specifier, and escaped before it gets here so no metacharacter
|
|
1078
|
+
// of theirs survives into the pattern. `splitEntry` has already bounded its length, so the compiled
|
|
1079
|
+
// pattern is linear in a value this module controls the size of.
|
|
1080
|
+
// eslint-disable-next-line security/detect-non-literal-regexp -- the literal is escaped by `escapeRegExp` and length-bounded by `splitEntry`; the whole point of the module is to turn operator text into a matcher
|
|
1081
|
+
command: new RegExp(`^${escapeRegExp(literal)}${prefix === void 0 ? "$" : ""}`)
|
|
1082
|
+
},
|
|
1083
|
+
action
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
__name(translate, "translate");
|
|
1087
|
+
function isRule(value) {
|
|
1088
|
+
return "tool" in value;
|
|
1089
|
+
}
|
|
1090
|
+
__name(isRule, "isRule");
|
|
1091
|
+
function permissionRulesFromSettings(block) {
|
|
1092
|
+
if (block === void 0) return {
|
|
1093
|
+
rules: [],
|
|
1094
|
+
unsupported: []
|
|
1095
|
+
};
|
|
1096
|
+
const rules = [];
|
|
1097
|
+
const unsupported = [];
|
|
1098
|
+
for (const key of Object.keys(block)) {
|
|
1099
|
+
if (!ACTIONS.includes(key)) {
|
|
1100
|
+
unsupported.push({
|
|
1101
|
+
entry: key,
|
|
1102
|
+
reason: `is not a permission action. Expected one of: ${ACTIONS.join(", ")}.`
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
for (const action of ACTIONS) {
|
|
1107
|
+
for (const entry of block[action] ?? []) {
|
|
1108
|
+
const translated = translate(entry, action);
|
|
1109
|
+
if (isRule(translated)) rules.push(translated);
|
|
1110
|
+
else unsupported.push(translated);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
return {
|
|
1114
|
+
rules,
|
|
1115
|
+
unsupported
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
__name(permissionRulesFromSettings, "permissionRulesFromSettings");
|
|
1119
|
+
|
|
827
1120
|
// src/config/context-pressure.ts
|
|
828
1121
|
import { resolveEffectiveContextWindow } from "@theokit/sdk/compaction";
|
|
829
|
-
import { TheokitAgentError as
|
|
1122
|
+
import { TheokitAgentError as TheokitAgentError4 } from "@theokit/sdk/errors";
|
|
830
1123
|
var DEFAULT_CONTEXT_PRESSURE_THRESHOLDS = {
|
|
831
1124
|
warn: 0.75,
|
|
832
1125
|
critical: 0.9
|
|
833
1126
|
};
|
|
834
|
-
var ContextPressureThresholdError = class extends
|
|
1127
|
+
var ContextPressureThresholdError = class extends TheokitAgentError4 {
|
|
835
1128
|
static {
|
|
836
1129
|
__name(this, "ContextPressureThresholdError");
|
|
837
1130
|
}
|
|
@@ -862,6 +1155,7 @@ export {
|
|
|
862
1155
|
LAYERS_ARE_POSITIONED,
|
|
863
1156
|
LayerOutOfOrderError,
|
|
864
1157
|
LayeredConfig,
|
|
1158
|
+
OutputStyleError,
|
|
865
1159
|
SETTINGS_LAYERS,
|
|
866
1160
|
TrustStore,
|
|
867
1161
|
TrustStorePermissionsError,
|
|
@@ -875,6 +1169,10 @@ export {
|
|
|
875
1169
|
layerPrecedence,
|
|
876
1170
|
loadCustomCommands,
|
|
877
1171
|
loadInstructionTree,
|
|
1172
|
+
loadOutputStyle,
|
|
1173
|
+
loadSettings,
|
|
1174
|
+
permissionRulesFromSettings,
|
|
1175
|
+
resolveOutputStyle,
|
|
878
1176
|
settingsLayerChain,
|
|
879
1177
|
splitFrontmatter,
|
|
880
1178
|
templateHints
|