pi-ptc-subagents 1.5.1 → 2.0.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/index.d.ts CHANGED
@@ -662,14 +662,16 @@ interface DefaultModeConfig {
662
662
  export declare function readDefaultModeConfig(agentDir: string): DefaultModeConfig;
663
663
  /**
664
664
  * Which model-facing tools this package registers, independent of PTC mode (which decides
665
- * which registered tools are *active*). Read from the same agent-dir file as
666
- * {@link readDefaultModeConfig}, beside the `defaultMode` key. ADR-0025.
665
+ * which registered tools are *active*).
667
666
  *
668
- * The order below is the order of increasing responsibility: `off` hands the whole
669
- * orchestration question back to pi, `subagents` keeps only the subagent face and lets pi's
670
- * `codemode` orchestrate, `full` keeps today's set.
667
+ * These are DETECTED values, not settings. The list is two long because detection is the only
668
+ * thing that produces them: `subagents` keeps the subagent face and lets pi's `codemode`
669
+ * orchestrate, `full` keeps the whole set. There was a third, `off`, and a `surfaceMode` key
670
+ * that could pin any of the three (ADR-0025); both are gone, and
671
+ * {@link detectSurfaceMode} is now a pure function of what pi is — see its doc for why the
672
+ * escape hatch the setting provided is not one this package can replace.
671
673
  */
672
- export declare const SURFACE_MODES: readonly ["off", "subagents", "full"];
674
+ declare const SURFACE_MODES: readonly ["subagents", "full"];
673
675
  type SurfaceMode = (typeof SURFACE_MODES)[number];
674
676
  /**
675
677
  * Whether the pi that launched us will actually LOAD its `codemode` extension.
@@ -719,43 +721,98 @@ export declare function readCodemodeSwitch(agentDir: string, cwd: string, argv?:
719
721
  * Whether `codemode` will be CALLABLE, as a third question distinct from whether pi ships the
720
722
  * directory (ADR-0026) and whether it will load the extension (ADR-0027).
721
723
  *
722
- * - `"active"` — a loadout names it, so the model can call it.
723
- * - `"inactive"` — nothing names it, which on a real install is the DEFAULT: pi registers
724
- * `codemode` with `defaultActive: false` and activates it only when a loadout says so.
724
+ * - `"active"` — a loadout names it OR pi's MCP extension will auto-enable it from `mcp.json`
725
+ * (ADR-0033), so the model can call it.
726
+ * - `"inactive"` — nothing names it and nothing auto-enables it, which on a real install is the
727
+ * DEFAULT: pi registers `codemode` with `defaultActive: false`
728
+ * (`dist/extensions/codemode/index.js:26`).
725
729
  *
726
730
  * `"inactive"` is what every failure of this probe on the positive side resolves to, and that is
727
731
  * the design rather than a fallback: `subagents` is chosen only on positive evidence that the tool
728
732
  * is callable. See ADR-0029, "The bound that makes the weaker guarantee sufficient".
729
733
  */
730
734
  type CodemodeActivation = "active" | "inactive";
731
- /** How the answer was decided, so a test can tell a configured answer from pi's default. */
732
- type CodemodeActivationSource = "cli" | "project" | "user" | "default" | "invalid";
735
+ /**
736
+ * How the answer was decided, so a test can tell a configured answer from pi's default.
737
+ *
738
+ * `"loadout"` is pi's own live tool set — the answer is read, not reconstructed, so there is no
739
+ * provenance left to name: whatever put `codemode` there is pi's business and not something this
740
+ * package can see. `"mcp"` is ADR-0033: the live loadout said `inactive` and the MCP auto-enable
741
+ * evidence said pi will activate `codemode` anyway, because pi's MCP extension does that from its
742
+ * own `session_start` handler and a read from ours can be too early to see it.
743
+ */
744
+ type CodemodeActivationSource = "loadout" | "invalid" | "mcp";
733
745
  /** The answer plus enough provenance to explain it in a notice. */
734
746
  interface CodemodeActivationResolution {
735
747
  activation: CodemodeActivation;
736
748
  source: CodemodeActivationSource;
737
749
  /** Set when a settings file could not be read as a JSON object; the answer still resolves. */
738
750
  error?: string;
751
+ /**
752
+ * Set when an `mcp.json` could not be read as a JSON object (ADR-0033); that file contributed
753
+ * nothing to the answer, and the answer still resolves.
754
+ */
755
+ mcpError?: string;
739
756
  }
740
757
  /**
741
- * Resolve whether `codemode` will be in the model's tool list, in the order pi resolves the
742
- * loadout: the command-line allowlist, then the merged `defaultTools`, then pi's own default.
758
+ * Everything the loadout side of this probe used to need, deleted with the probe.
759
+ *
760
+ * `PI_DEFAULT_TOOL_NAMES`, `isToolModifier`, `stringEntries`, `mergeDefaultTools` and
761
+ * `resolveDefaultTools` were a second implementation of pi's `defaultTools` resolution — the merge
762
+ * direction, the modifier grammar, the "a list of only modifiers starts from pi's four defaults"
763
+ * rule. It existed because the factory had to know the answer before pi would let this package ask,
764
+ * and reimplementing the rule was the only way to know it in advance.
765
+ *
766
+ * The answer is now read from pi instead, so there is nothing left for these to be right about. A
767
+ * second implementation of a rule the host owns is a defect waiting for the host to change the rule,
768
+ * which is how #131 happened: a project's `defaultTools` was honoured here whether or not pi would
769
+ * ever read it.
770
+ */
771
+ /**
772
+ * The command-line reader is gone with the reconstruction it existed for.
773
+ *
774
+ * `splitCliToolList`, `PI_VALUE_CONSUMING_FLAGS`, `PI_CONDITIONALLY_CONSUMING_FLAGS`,
775
+ * `piPrintConsumesNext`, `CliToolFlags` and `cliToolFlags` parsed `process.argv` to work out
776
+ * which tools pi would activate: `--tools` as an allowlist, `--exclude-tools` as a denylist,
777
+ * `--no-tools` and `--no-builtin-tools` as the two ways of emptying the list, plus the value-
778
+ * consuming grammar those flags live in, because a value that looks like a flag must not be
779
+ * re-read as one.
743
780
  *
744
- * The last of those is the load-bearing one and is what makes absence of evidence mean
745
- * `inactive`: pi registers `codemode` inactive, so a session that configured nothing does not get
746
- * it, and delegating orchestration to a tool the model cannot call is the failure this exists to
747
- * prevent.
781
+ * It was careful — a 294-case differential against pi's own parser found no disagreement once it
782
+ * was made to agree, and every divergence before that had been argued away as pointing in a safe
783
+ * direction. That argument is what this deletion is about rather than the code: it was wrong.
784
+ * Reading the command line correctly is not the same as knowing what pi will do with it, and the
785
+ * settings half had already shown how that goes wrong — #131 is this reader's sibling, with the
786
+ * same shape of mistake on the file side.
787
+ *
788
+ * The reader's replacement is one call, and it is the host's own answer rather than a second
789
+ * opinion on it. See {@link readCodemodeActivation}.
748
790
  */
749
- export declare function resolveCodemodeActivation(argv: readonly string[], projectSettings: unknown, userSettings: unknown): CodemodeActivationResolution;
750
791
  /**
751
- * Whether an explicit `surfaceMode` disagrees with what the table above decided.
792
+ * Whether `codemode` is in pi's active tool set, as pi reports it.
793
+ *
794
+ * This is the ADR-0029 loadout mirror with the mirror taken out. It used to resolve the same
795
+ * question by parsing the command line and merging the two `defaultTools` files, replaying pi's
796
+ * precedence over them:
797
+ *
798
+ * 1. `--tools` decides the list, and it beats `--no-tools`.
799
+ * 2. `--exclude-tools` vetoes whichever list won, because the denylist is a `.filter` over that
800
+ * list rather than another source.
801
+ * 3. `--no-tools` and `--no-builtin-tools` with no allowlist empty the list, so `codemode` is not
802
+ * active whatever `defaultTools` says.
803
+ * 4. Otherwise `defaultTools` decides, vetoed by the denylist exactly as in (2).
752
804
  *
753
- * The explicit key always WINS — that is what an override is for — so this exists only to be
754
- * reported. `off` is exempt: it means "I do not want this package's surface at all", which is a
755
- * statement about the package rather than a claim about who orchestrates, and warning about it on
756
- * every session would be crying wolf.
805
+ * All four are still true, and none of them is restated here. pi has already applied them, along
806
+ * with the project-trust decision, by the time an extension can read its loadout at
807
+ * `session_start`; asking it is both shorter and correct in the case that a second implementation
808
+ * cannot be, which is when the two disagree about something pi changed.
809
+ *
810
+ * Absence still means `inactive`, and that is unchanged rather than defaulted: pi registers
811
+ * `codemode` with `defaultActive: false`, so a session that configured nothing does not get it, and
812
+ * delegating orchestration to a tool the model cannot call is the failure this whole probe exists
813
+ * to prevent.
757
814
  */
758
- export declare function surfaceModeConflict(explicit: SurfaceMode | undefined, detected: SurfaceMode): boolean;
815
+ export declare function resolveCodemodeActivation(activeToolNames: readonly string[]): CodemodeActivationResolution;
759
816
  /**
760
817
  * Whether the pi that launched us ships its own `codemode` orchestration tool.
761
818
  *
@@ -785,80 +842,75 @@ interface CodemodePresence {
785
842
  /** How the answer was reached, so a test can tell a measured yes from a failed probe. */
786
843
  how: "found" | "not-found" | "no-entry" | "unresolvable-entry";
787
844
  }
788
- /** Result of reading the surface mode, with enough detail to warn about a broken file. */
789
- interface SurfaceModeConfig {
845
+ /** The detected surface, plus every probe result `session_start` reports on. */
846
+ interface DetectedSurface {
847
+ /** What this package registers. Decided by {@link detectedSurfaceMode}; never configured. */
790
848
  surfaceMode: SurfaceMode;
791
- source: "file" | "default" | "invalid";
792
- error?: string;
793
- /**
794
- * What the probe found, on the paths where the surface was NOT decided by the file. Absent
795
- * when the user set the key: that call never consults the probe (see
796
- * {@link readSurfaceModeConfig}), so there is nothing to report.
797
- */
798
- codemode?: CodemodePresence;
799
- /**
800
- * Whether pi will actually load its own codemode (ADR-0027). Absent for the same reason as
801
- * {@link codemode}: an explicit key short-circuits the probe, so there is nothing to report.
802
- */
803
- codemodeSwitch?: CodemodeSwitchResolution;
804
- /**
805
- * Whether `codemode` will be in the model's tool list (ADR-0029), the third axis of the
806
- * detected table. Absent for the same reason as {@link codemodeSwitch}.
807
- */
808
- codemodeActivation?: CodemodeActivationResolution;
809
- /**
810
- * What the four-case table decided, carried even when an explicit key overrode it — that
811
- * difference is exactly what {@link surfaceModeConflict} reports on.
812
- */
813
- detected?: SurfaceMode;
849
+ /** Whether this pi ships a `codemode` at all. */
850
+ codemode: CodemodePresence;
851
+ /** Whether pi will load it (ADR-0027). */
852
+ codemodeSwitch: CodemodeSwitchResolution;
853
+ /** Whether the model will be able to call it (ADR-0029), the third axis of the table. */
854
+ codemodeActivation: CodemodeActivationResolution;
814
855
  }
815
856
  /**
816
- * Read `surfaceMode` from the agent-dir config file.
857
+ * Decide the surface by asking the pi. A pure function of the three probes and nothing else.
858
+ *
859
+ * This used to read a `surfaceMode` key first and only fall back to the probes, and the argument
860
+ * for that was an escape hatch: pi has `registerTool` but no `unregisterTool`, so a surface chosen
861
+ * wrongly cannot be corrected once the factory has returned — a session that got the wrong answer
862
+ * keeps it until it restarts. That argument is real, and it is also why the hatch is gone rather
863
+ * than merely narrowed:
817
864
  *
818
- * A pure function over the filesystem, shaped like {@link readDefaultModeConfig} on purpose:
819
- * an absent file, an absent key, unparseable JSON, a non-object, a wrong-typed value and an
820
- * out-of-set value all resolve to the detected default rather than a hardcoded one, and every
821
- * malformed shape additionally reports `invalid` with a reason. A malformed setting must never
822
- * half-apply - which tools exist is not something to change on a guess.
865
+ * - **The wrong answer is now reported rather than prevented.** `session_start` measures the live
866
+ * registry against all three probes and says so when they disagree (ADR-0029), which a pinned
867
+ * value used to suppress.
868
+ * - **The direction that actually hurts is not reachable by a setting anyway.** A pin can only
869
+ * make the surface *more* capable than detection, never less: every way the probes come back
870
+ * wrong (`subagents` chosen for a `codemode` that cannot run) resolves to `full`, and no value
871
+ * the file could hold would have produced a better outcome than the one already chosen.
872
+ * - **Disabling is pi's job now.** The `off` value existed because a package that cannot be
873
+ * switched off is a package that stays loaded forever. `pi config` turns a package's extensions
874
+ * off without loading them — measured, not assumed: on pi 1.1.0 a `packages` entry whose
875
+ * `extensions` is `[]` or `["!dist/index.js"]` does not load the extension at all, while
876
+ * `["+dist/index.js"]` and an absent key both do. That is a channel this package cannot offer
877
+ * for itself, because the switch that uses it is pi's.
823
878
  *
824
- * The `presence` and `codemodeSwitch` arguments are parameters rather than hidden calls, so a test
825
- * can state the pi it is reasoning about instead of depending on the machine it runs on. Omit
826
- * them and the real probes answer, but only on a path that actually needs the answer: they are
827
- * resolved inside the fallback branches rather than in a default parameter, because a default
828
- * parameter is evaluated on EVERY call -- including the ones an explicit `surfaceMode` key
829
- * short-circuits, where the user paid a `realpathSync` plus up to three `statSync` and two
830
- * settings reads to set one line of JSON and get a constant.
879
+ * The `presence` and `codemodeSwitch` arguments stay parameters rather than hidden calls, so a
880
+ * test can state the pi it is reasoning about instead of depending on the machine it runs on.
881
+ *
882
+ * The probes are resolved here rather than in default parameters, so every call pays for all
883
+ * three — the shortcut that used to exist (`a default parameter is evaluated on EVERY call`)
884
+ * saved real work only when a key short-circuited the probes entirely, and there is no key left.
885
+ * The cost, named rather than rounded: each of the two settings-reading probes reads the SAME two
886
+ * files, so one detection is four settings reads over two files plus the two `mcp.json` reads
887
+ * ADR-0033 added. The duplication is pre-existing and deliberate — each probe is copied from pi
888
+ * whole and kept self-contained — so the number is recorded, not optimised.
831
889
  */
832
- export declare function readSurfaceModeConfig(agentDir: string, presence?: CodemodePresence, codemodeSwitch?: CodemodeSwitchResolution, codemodeActivation?: CodemodeActivationResolution, cwd?: string): SurfaceModeConfig;
833
- /** Outcome of writing `surfaceMode` for `/ptc surface`. */
834
- type SurfaceModeWrite = {
835
- ok: true;
836
- path: string;
837
- /** The value already in the file, or `undefined` when there was none. */
838
- previous: SurfaceMode | undefined;
839
- /** False when the file already said this, in which case NOTHING was written. */
840
- changed: boolean;
841
- } | {
842
- ok: false;
890
+ export declare function detectSurfaceMode(agentDir: string, activeToolNames: readonly string[], presence?: CodemodePresence, codemodeSwitch?: CodemodeSwitchResolution, codemodeActivation?: CodemodeActivationResolution, cwd?: string): DetectedSurface;
891
+ /** A `surfaceMode` left in `ptc.json` by a release that still had the key. */
892
+ interface LegacySurfaceKey {
893
+ /** The path it was found in, or `undefined` when there was no file to look in. */
843
894
  path: string;
844
- error: string;
845
- };
895
+ /** The value that was there, or the JSON rendering when it was not a string. */
896
+ value: unknown;
897
+ }
846
898
  /**
847
- * Write one `surfaceMode` key into the agent-dir config, preserving every other key.
899
+ * Read a `surfaceMode` key that this package no longer acts on, so `session_start` can say so.
848
900
  *
849
- * Three rules, each of which is a way a naive rewrite goes wrong:
901
+ * The key shipped in v1.6.0 and the three values it took are gone, which means a user who set
902
+ * `"off"` to keep this package out of their sessions would find it back on the next upgrade with
903
+ * nothing to explain why. Nothing here restores the behaviour: the returned value is only ever
904
+ * turned into a notice that names the replacement. The alternative — honouring `off` forever as a
905
+ * compatibility shim — is the switch this change exists to delete, and it would leave the package
906
+ * carrying two ways to be disabled, one of which silently does nothing.
850
907
  *
851
- * - **A malformed file is never overwritten.** An unparseable `ptc.json` is a file the user may
852
- * be mid-edit on, and this command is not a licence to replace it with something valid that
853
- * drops whatever was in it. The read side already reports that shape rather than acting on it
854
- * ({@link readSurfaceModeConfig}), and the write side has to agree.
855
- * - **Other keys survive.** `defaultMode` lives in the same file (ADR-0010), and a command that
856
- * wrote `{"surfaceMode": …}` wholesale would silently reset the user's mode preference.
857
- * - **An unchanged value writes nothing.** `/ptc surface full` on a session already at `full`
858
- * should not touch the file's mtime, and — more to the point — should not trigger the reload
859
- * that would follow, since a reload replaces every extension instance for no reason.
908
+ * Returns `undefined` for an absent file, an absent key, and for a file this cannot parse or that
909
+ * is not an object. Those last two are NOT reported here: a `ptc.json` too broken to read has no
910
+ * `surfaceMode` to be stale about, and the parse failure belongs to whoever owns that file
911
+ * (`defaultMode` still lives there, and {@link readDefaultModeConfig} is what reports it).
860
912
  */
861
- export declare function setSurfaceMode(agentDir: string, value: unknown): SurfaceModeWrite;
913
+ export declare function readLegacySurfaceKey(agentDir: string): LegacySurfaceKey | undefined;
862
914
  /** Why the mode declined to turn on. Surfaced in the entry notification / debug logs. */
863
915
  type ModeBlockReason = "not-tui" | "config-off" | "tools-unavailable" | "restricted-session";
864
916
  /** Either the loadout to apply, or the reason the mode stayed off. */
@@ -1520,16 +1572,9 @@ export declare class TurnPools {
1520
1572
  export interface PtcSubagentsOptions {
1521
1573
  /** Use this session-scoped background runtime instead of constructing one. */
1522
1574
  backgroundRuntime?: BackgroundTaskRuntime;
1523
- /**
1524
- * Test seam for ADR-0025's surface mode. When set it wins over the agent-dir `ptc.json`,
1525
- * so a test never reads the developer's real settings -- and the four test files that all
1526
- * build this factory through one stub would otherwise inherit whatever the machine happens
1527
- * to have. Undefined in production, where the file is the only source.
1528
- */
1529
- surfaceMode?: SurfaceMode;
1530
1575
  /**
1531
1576
  * ADR-0026 test seam: what the codemode probe found. Undefined in production, where the
1532
- * probe really runs. A test that exercises the DETECTED default states the pi it
1577
+ * probe really runs. A test that exercises the DETECTED surface states the pi it
1533
1578
  * assumes rather than inheriting whatever process.argv the test runner happens to have,
1534
1579
  * which is how the previous version of that test passed for the wrong reason.
1535
1580
  */
@@ -1541,8 +1586,24 @@ export interface PtcSubagentsOptions {
1541
1586
  * and that is the case this seam exists to state.
1542
1587
  */
1543
1588
  codemodeSwitch?: CodemodeSwitchResolution;
1589
+ /**
1590
+ * ADR-0029 test seam: whether `codemode` will be in the model's tool list. Undefined in
1591
+ * production, where `readCodemodeActivation` reads pi's own active tool set and the MCP
1592
+ * evidence — no settings file and no `argv` on this axis any more (ADR-0035).
1593
+ *
1594
+ * This seam exists because the `surfaceMode` pin it replaced did. That pin was there to keep a
1595
+ * test off the developer's machine: the probe it fed read a settings file out of the developer's
1596
+ * own agent dir, so a test file that pinned nothing decided its own surface by however the
1597
+ * machine running it happened to be configured. Removing the pin without adding this would have
1598
+ * moved that failure back in, silently, to every stub-built factory.
1599
+ *
1600
+ * The machine-dependence is gone — the loadout is handed in rather than read off disk — but the
1601
+ * seam stays, because a test whose subject IS the axis needs to state an answer pi would not
1602
+ * produce on its own (`null` runs the real probe; see `tests/helpers/ptc.ts`).
1603
+ */
1604
+ codemodeActivation?: CodemodeActivationResolution;
1544
1605
  }
1545
1606
  export default function ptcSubagents(pi: ExtensionAPI, options?: PtcSubagentsOptions): void;
1546
1607
  //#endregion
1547
- export type { Binding, BindingContext, BindingTable, CodemodePresence, CodemodeSwitch, CodemodeSwitchResolution, CodemodeSwitchSource, CreateBuiltinBindingsOptions, DefaultModeConfig, ModeBlockReason, ModeEntryDecision, ModeEntryInput, ModeHideStrategy, PersistedModeState, PtcConfig, PtcErrorKind, PtcErrorShape, PtcImage, PtcJsonValue, PtcModeState, PtcRunOutcome, PtcSurface, RunPtcProgramOptions, SurfaceModeConfig, TurnPoolsOptions, WorkerPoolOptions, WorkerPoolStats, WorkerPoolWorkerOptions };
1608
+ export type { Binding, BindingContext, BindingTable, CodemodePresence, CodemodeSwitch, CodemodeSwitchResolution, CodemodeSwitchSource, CreateBuiltinBindingsOptions, DefaultModeConfig, DetectedSurface, LegacySurfaceKey, ModeBlockReason, ModeEntryDecision, ModeEntryInput, ModeHideStrategy, PersistedModeState, PtcConfig, PtcErrorKind, PtcErrorShape, PtcImage, PtcJsonValue, PtcModeState, PtcRunOutcome, PtcSurface, RunPtcProgramOptions, TurnPoolsOptions, WorkerPoolOptions, WorkerPoolStats, WorkerPoolWorkerOptions };
1548
1609
  //# sourceMappingURL=index.d.ts.map