@kushalkhemka/opensec-pi-subagents 0.20.1 → 0.20.2

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 CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.20.2] - 2026-10-16
11
+
12
+ ### Changed
13
+
14
+ - **Every project is trusted again by default**, as before 0.20.1: subagents load a project's `.pi` extensions and settings, and the extension uses its agents, skills, workflows and `subagents.json`, whatever Pi decided for the folder. To follow Pi's project trust decision instead (0.20.1's behaviour), set `"projectTrust": "pi"` in the global `~/.pi/agent/subagents.json`.
15
+
10
16
  ## [0.20.1] - 2026-10-16
11
17
 
12
18
  ### Fixed
package/README.md CHANGED
@@ -116,7 +116,12 @@ them to have more.
116
116
 
117
117
  ### Project trust
118
118
 
119
- Subagents follow Pi's project trust decision for the parent session.
119
+ By default (`projectTrust: "full"`) subagents and this extension trust every project: a child session loads the
120
+ project's `.pi/extensions` and `.pi/settings.json`, and the extension uses the project's agents, skills, workflows and
121
+ `subagents.json`, whatever Pi decided for the parent session.
122
+
123
+ To have them follow Pi's project trust decision instead, set `"projectTrust": "pi"` in the global
124
+ `~/.pi/agent/subagents.json` (a project's own file cannot change it). Then:
120
125
 
121
126
  - In a folder Pi does not trust, subagents load none of the project's `.pi/extensions`, `.pi/settings.json` (for
122
127
  example `shellCommandPrefix`), skills or prompts, exactly like the parent. This extension also ignores the project's
@@ -4,8 +4,9 @@
4
4
  * Pi 1.x does not load a folder's `.pi` settings, extensions, skills or prompts
5
5
  * until the user trusts the folder. Subagents run in sessions this extension
6
6
  * builds itself, and this extension reads project files of its own (agents,
7
- * workflows, skills, `subagents.json`), so both have to follow the decision the
8
- * parent session made rather than assume trust.
7
+ * workflows, skills, `subagents.json`). By default (`projectTrust: "full"`) both
8
+ * trust every project, as before 0.20.1; with `projectTrust: "pi"` in the global
9
+ * subagents.json they follow the decision the parent session made.
9
10
  */
10
11
  /**
11
12
  * Whether the session behind `ctx` trusts its project.
@@ -4,10 +4,21 @@
4
4
  * Pi 1.x does not load a folder's `.pi` settings, extensions, skills or prompts
5
5
  * until the user trusts the folder. Subagents run in sessions this extension
6
6
  * builds itself, and this extension reads project files of its own (agents,
7
- * workflows, skills, `subagents.json`), so both have to follow the decision the
8
- * parent session made rather than assume trust.
7
+ * workflows, skills, `subagents.json`). By default (`projectTrust: "full"`) both
8
+ * trust every project, as before 0.20.1; with `projectTrust: "pi"` in the global
9
+ * subagents.json they follow the decision the parent session made.
9
10
  */
10
11
  import { getAgentDir, hasTrustRequiringProjectResources, ProjectTrustStore, SettingsManager, } from "@earendil-works/pi-coding-agent";
12
+ import { globalProjectTrustMode } from "./settings.js";
13
+ /** Whether Pi's trust decision applies (`projectTrust: "pi"`); otherwise every project is trusted. */
14
+ function followsPiTrust() {
15
+ try {
16
+ return globalProjectTrustMode() === "pi";
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
11
22
  /**
12
23
  * Whether the session behind `ctx` trusts its project.
13
24
  *
@@ -16,6 +27,8 @@ import { getAgentDir, hasTrustRequiringProjectResources, ProjectTrustStore, Sett
16
27
  * context that throws (a session already torn down) is treated as untrusted.
17
28
  */
18
29
  export function isProjectTrustedBy(ctx) {
30
+ if (!followsPiTrust())
31
+ return true;
19
32
  if (typeof ctx?.isProjectTrusted !== "function")
20
33
  return true;
21
34
  try {
@@ -41,6 +54,8 @@ function defaultTrustIsAlways(cwd) {
41
54
  * reports otherwise.
42
55
  */
43
56
  export function predictProjectTrust(cwd) {
57
+ if (!followsPiTrust())
58
+ return true;
44
59
  try {
45
60
  if (!hasTrustRequiringProjectResources(cwd))
46
61
  return true;
@@ -65,6 +80,8 @@ export function predictProjectTrust(cwd) {
65
80
  * folder as trusted (`/trust`).
66
81
  */
67
82
  export function isProjectCodeTrusted(trusted, cwd) {
83
+ if (!followsPiTrust())
84
+ return true;
68
85
  if (!trusted)
69
86
  return false;
70
87
  try {
@@ -1,4 +1,5 @@
1
1
  import type { AgentMentionMode, JoinMode, ViewerMarkdownMode, WidgetMode } from "./types.js";
2
+ export type ProjectTrustMode = "full" | "pi";
2
3
  export interface SubagentsSettings {
3
4
  maxConcurrent?: number;
4
5
  /**
@@ -96,6 +97,17 @@ export interface SubagentsSettings {
96
97
  * Defaults to false.
97
98
  */
98
99
  strictAgentFiles?: boolean;
100
+ /**
101
+ * Which project files subagents and this extension may use.
102
+ * - "full" (default): every project's agents, skills, workflows, `subagents.json`,
103
+ * agent-file `extensions:` and, in child sessions, its `.pi` extensions and settings,
104
+ * as before 0.20.1.
105
+ * - "pi": follow Pi's trust decision for the folder: an untrusted project's files are
106
+ * not used, and project code needs a saved decision (`/trust`).
107
+ * Read from the global `~/.pi/agent/subagents.json` only: a project's own file cannot
108
+ * decide whether that project is trusted.
109
+ */
110
+ projectTrust?: ProjectTrustMode;
99
111
  /**
100
112
  * When true, the three built-in default agents (general-purpose, Explore, Plan)
101
113
  * are not registered at startup. User-defined agents from project/global custom
@@ -335,6 +347,8 @@ export type SettingsEmit = (event: string, payload: unknown) => void;
335
347
  * `project: false` reads the global file only — the project is not trusted,
336
348
  * and Pi ignores its `.pi/settings.json` too.
337
349
  */
350
+ /** `projectTrust` from the global settings file (see `SubagentsSettings.projectTrust`). */
351
+ export declare function globalProjectTrustMode(): ProjectTrustMode;
338
352
  export declare function loadSettings(cwd?: string, project?: boolean): SubagentsSettings;
339
353
  /**
340
354
  * Write project-local settings. Global is never touched from code.
package/dist/settings.js CHANGED
@@ -72,6 +72,9 @@ function sanitize(raw) {
72
72
  if (typeof r.strictAgentFiles === "boolean") {
73
73
  out.strictAgentFiles = r.strictAgentFiles;
74
74
  }
75
+ if (r.projectTrust === "full" || r.projectTrust === "pi") {
76
+ out.projectTrust = r.projectTrust;
77
+ }
75
78
  if (typeof r.disableDefaultAgents === "boolean") {
76
79
  out.disableDefaultAgents = r.disableDefaultAgents;
77
80
  }
@@ -158,6 +161,10 @@ function readSettingsFile(path) {
158
161
  * `project: false` reads the global file only — the project is not trusted,
159
162
  * and Pi ignores its `.pi/settings.json` too.
160
163
  */
164
+ /** `projectTrust` from the global settings file (see `SubagentsSettings.projectTrust`). */
165
+ export function globalProjectTrustMode() {
166
+ return readSettingsFile(globalPath()).projectTrust ?? "full";
167
+ }
161
168
  export function loadSettings(cwd = process.cwd(), project = true) {
162
169
  return { ...readSettingsFile(globalPath()), ...(project ? readSettingsFile(projectPath(cwd)) : {}) };
163
170
  }
package/docs/workflows.md CHANGED
@@ -124,7 +124,7 @@ A script you will run more than once belongs somewhere durable. Copy it out of t
124
124
 
125
125
  First hit wins, in that order, so a project file shadows a same-named global one.
126
126
 
127
- The two project locations are used only when Pi trusts the project to run its own code: the folder was trusted at Pi's prompt (or by `defaultProjectTrust`, or a CLI flag), or `/trust` recorded it. Pi trusts a folder without asking when it holds nothing Pi gates, and `.pi/workflows` is not gated, so a cloned repo that ships only workflows does not get them run by name until you run `/trust`. A refused name says so and runs nothing.
127
+ With `"projectTrust": "pi"` in the global subagents.json (the default, `"full"`, uses them in every project), the two project locations are used only when Pi trusts the project to run its own code: the folder was trusted at Pi's prompt (or by `defaultProjectTrust`, or a CLI flag), or `/trust` recorded it. Pi trusts a folder without asking when it holds nothing Pi gates, and `.pi/workflows` is not gated, so a cloned repo that ships only workflows does not get them run by name until you run `/trust`. A refused name says so and runs nothing.
128
128
 
129
129
  The file must carry an `export const meta = { name, description }` declaration. Those are ordinary directories that may hold anything, so that declaration is what marks a file as a workflow — name something else and you are told it is not a workflow rather than getting a parse error from halfway through it. Nothing in the file is executed to decide that.
130
130
 
@@ -288,7 +288,7 @@ The child runs in the *same* worker and vm context under its own globals, so it
288
288
  |---|---|
289
289
  | An inline script, as run | `<tmp>/pi-subagents-<uid>/<encoded-cwd>/<session>/tasks/<run id>.workflow.js` |
290
290
  | The resume journal | the same directory, `<run id>.workflow.jsonl` |
291
- | Saved workflows | `.pi/workflows/` → `.agents/workflows/` → `<agent dir>/workflows/`, first hit wins (project locations only in a project trusted to run its code) |
291
+ | Saved workflows | `.pi/workflows/` → `.agents/workflows/` → `<agent dir>/workflows/`, first hit wins (with `projectTrust: "pi"`, project locations only in a project trusted to run its code) |
292
292
 
293
293
  The first two are scratch: temp storage, wiped by a reboot or a temp sweep. Only the third is durable, and copying a script there is a manual step.
294
294
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kushalkhemka/opensec-pi-subagents",
3
- "version": "0.20.1",
3
+ "version": "0.20.2",
4
4
  "description": "OpenSec Subagents for Pi: a fork of @tintinweb/pi-subagents with parent model and thinking-level inheritance and cached agent discovery.",
5
5
  "author": "OpenSec",
6
6
  "contributors": [
package/src/index.ts CHANGED
@@ -3827,9 +3827,10 @@ Write the file using the write tool. Only write the file, nothing else.`;
3827
3827
  // Compile-time completeness guard for snapshotSettings(). If a field is added
3828
3828
  // to SubagentsSettings and not mirrored above, this Exclude is non-empty and
3829
3829
  // fails to satisfy `never` — turning a silent settings-erasure bug into a
3830
- // typecheck error. `npm run typecheck` runs in CI.
3830
+ // typecheck error. `npm run typecheck` runs in CI. (`projectTrust` is read from the
3831
+ // global file only, so it is never part of the project snapshot.)
3831
3832
  type _NoMissingSettingsKeys =
3832
- Exclude<keyof SubagentsSettings, keyof ReturnType<typeof snapshotSettings>> extends never
3833
+ Exclude<keyof SubagentsSettings, keyof ReturnType<typeof snapshotSettings> | "projectTrust"> extends never
3833
3834
  ? true
3834
3835
  : ["snapshotSettings() is missing a SubagentsSettings key"];
3835
3836
  const _settingsSnapshotIsComplete: _NoMissingSettingsKeys = true;
@@ -4,8 +4,9 @@
4
4
  * Pi 1.x does not load a folder's `.pi` settings, extensions, skills or prompts
5
5
  * until the user trusts the folder. Subagents run in sessions this extension
6
6
  * builds itself, and this extension reads project files of its own (agents,
7
- * workflows, skills, `subagents.json`), so both have to follow the decision the
8
- * parent session made rather than assume trust.
7
+ * workflows, skills, `subagents.json`). By default (`projectTrust: "full"`) both
8
+ * trust every project, as before 0.20.1; with `projectTrust: "pi"` in the global
9
+ * subagents.json they follow the decision the parent session made.
9
10
  */
10
11
 
11
12
  import {
@@ -14,6 +15,16 @@ import {
14
15
  ProjectTrustStore,
15
16
  SettingsManager,
16
17
  } from "@earendil-works/pi-coding-agent";
18
+ import { globalProjectTrustMode } from "./settings.js";
19
+
20
+ /** Whether Pi's trust decision applies (`projectTrust: "pi"`); otherwise every project is trusted. */
21
+ function followsPiTrust(): boolean {
22
+ try {
23
+ return globalProjectTrustMode() === "pi";
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
17
28
 
18
29
  /**
19
30
  * Whether the session behind `ctx` trusts its project.
@@ -23,6 +34,7 @@ import {
23
34
  * context that throws (a session already torn down) is treated as untrusted.
24
35
  */
25
36
  export function isProjectTrustedBy(ctx: { isProjectTrusted?: () => boolean } | undefined): boolean {
37
+ if (!followsPiTrust()) return true;
26
38
  if (typeof ctx?.isProjectTrusted !== "function") return true;
27
39
  try {
28
40
  return ctx.isProjectTrusted() === true;
@@ -49,6 +61,7 @@ function defaultTrustIsAlways(cwd: string): boolean {
49
61
  * reports otherwise.
50
62
  */
51
63
  export function predictProjectTrust(cwd: string): boolean {
64
+ if (!followsPiTrust()) return true;
52
65
  try {
53
66
  if (!hasTrustRequiringProjectResources(cwd)) return true;
54
67
  const saved = savedDecision(cwd);
@@ -71,6 +84,7 @@ export function predictProjectTrust(cwd: string): boolean {
71
84
  * folder as trusted (`/trust`).
72
85
  */
73
86
  export function isProjectCodeTrusted(trusted: boolean, cwd: string): boolean {
87
+ if (!followsPiTrust()) return true;
74
88
  if (!trusted) return false;
75
89
  try {
76
90
  return hasTrustRequiringProjectResources(cwd) || savedDecision(cwd) === true || defaultTrustIsAlways(cwd);
package/src/settings.ts CHANGED
@@ -9,6 +9,8 @@ import { NO_FALLBACK } from "./agent-types.js";
9
9
  import { createDiscoveryCache } from "./discovery-cache.js";
10
10
  import type { AgentMentionMode, JoinMode, ViewerMarkdownMode, WidgetMode } from "./types.js";
11
11
 
12
+ export type ProjectTrustMode = "full" | "pi";
13
+
12
14
  export interface SubagentsSettings {
13
15
  maxConcurrent?: number;
14
16
  /**
@@ -106,6 +108,17 @@ export interface SubagentsSettings {
106
108
  * Defaults to false.
107
109
  */
108
110
  strictAgentFiles?: boolean;
111
+ /**
112
+ * Which project files subagents and this extension may use.
113
+ * - "full" (default): every project's agents, skills, workflows, `subagents.json`,
114
+ * agent-file `extensions:` and, in child sessions, its `.pi` extensions and settings,
115
+ * as before 0.20.1.
116
+ * - "pi": follow Pi's trust decision for the folder: an untrusted project's files are
117
+ * not used, and project code needs a saved decision (`/trust`).
118
+ * Read from the global `~/.pi/agent/subagents.json` only: a project's own file cannot
119
+ * decide whether that project is trusted.
120
+ */
121
+ projectTrust?: ProjectTrustMode;
109
122
  /**
110
123
  * When true, the three built-in default agents (general-purpose, Explore, Plan)
111
124
  * are not registered at startup. User-defined agents from project/global custom
@@ -424,6 +437,9 @@ function sanitize(raw: unknown): SubagentsSettings {
424
437
  if (typeof r.strictAgentFiles === "boolean") {
425
438
  out.strictAgentFiles = r.strictAgentFiles;
426
439
  }
440
+ if (r.projectTrust === "full" || r.projectTrust === "pi") {
441
+ out.projectTrust = r.projectTrust;
442
+ }
427
443
  if (typeof r.disableDefaultAgents === "boolean") {
428
444
  out.disableDefaultAgents = r.disableDefaultAgents;
429
445
  }
@@ -514,6 +530,11 @@ function readSettingsFile(path: string): SubagentsSettings {
514
530
  * `project: false` reads the global file only — the project is not trusted,
515
531
  * and Pi ignores its `.pi/settings.json` too.
516
532
  */
533
+ /** `projectTrust` from the global settings file (see `SubagentsSettings.projectTrust`). */
534
+ export function globalProjectTrustMode(): ProjectTrustMode {
535
+ return readSettingsFile(globalPath()).projectTrust ?? "full";
536
+ }
537
+
517
538
  export function loadSettings(cwd: string = process.cwd(), project = true): SubagentsSettings {
518
539
  return { ...readSettingsFile(globalPath()), ...(project ? readSettingsFile(projectPath(cwd)) : {}) };
519
540
  }