Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/guardian-secret-grant-tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ian-pascoe/pi-guardian": minor
---

Allow Pi Secret's `secret_ask_grant` and `secret_save` by default, like its other `secret_*` tools: neither reveals a value, and both ask the user in Pi Secret's own dialog.
5 changes: 5 additions & 0 deletions .changeset/secret-providers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ian-pascoe/pi-secret": minor
---

Add Secret Providers, starting with 1Password through the `op` CLI. `secret_list` now searches Secret Providers for items and fields (`query`, `provider`, `vault`, `item`, `status`, `limit`), never values. `secret_ask_grant` asks the user to approve Secret Grants, each a Secret Name and the 1Password Reference (`op://vault/item/field`) its value is read from again at each use, so one-time passwords stay current. `secret_save` saves a typed Secret into a new 1Password item after the user approves. The session stores only a grant's locator, never its value. Optional `secret.providers.1password.account` and `vaults` settings narrow what the agent can see. A backslash right before a Secret Reference now makes it literal text, so documentation and tests can contain the syntax. Provider-request scrubbing now leaves a message already sent unchanged when a value held later appears in it, so holding a Secret no longer discards the provider's prompt cache.
5 changes: 5 additions & 0 deletions .changeset/secret-registry-refresh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ian-pascoe/pi-utils": minor
---

Let the secret registry hold Secrets whose values live elsewhere: `holdRefreshableSecret(name, owner, { refresh, value?, expiresAt? })` registers a refresh callback, `refreshSecret(name)` reads the current value through it and keeps every value read redacted, and `isSecretHeld(name)` reports a held Secret before its first read. `redactionGeneration()` counts the values held so far, and `redactSecrets(value, generation)` redacts only the values held by that generation, so text redacted once can be redacted the same way later.
2 changes: 1 addition & 1 deletion .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
unset $(git rev-parse --local-env-vars)
pnpm verify
pnpm verify:push
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
- **Coexistence** — When changing tool activation, context delivery, or a package's tool-result text, trace other extensions using the same Pi APIs. Test affected standalone and combined modes: combined-mode tests live in the root `test/` and run with `pnpm test:root`, which `pnpm --filter` skips.
- **Tool registration** — Register every extension tool through `acceptNullForOptionalArguments` from `@ian-pascoe/pi-utils/null-optional-arguments` so `null` for an optional parameter behaves like omitting it, and cover new tools with `expectNullOptionalArgumentsOmitted` from `@ian-pascoe/pi-utils/tool-testing`.
- **Cache proofs** — Prove prefix stability in offline SDK integration tests by comparing the affected ordered tool definitions, system prompt, or message history. Equal name sets or token estimates alone are insufficient.
- **Verification** — While iterating, run focused checks such as `pnpm --filter <package> test`. `pnpm verify` and the pre-push hook share a Turborepo cache keyed on file contents, so rerunning either on unchanged files replays instantly; never bypass the hook with `--no-verify`.
- **Verification** — While iterating, run focused checks such as `pnpm --filter <package> test`. The pre-push hook runs `pnpm verify:push` (format, lint, and typechecks); CI runs the full `pnpm verify`, including every test suite, so leave full test runs to CI. Both share a Turborepo cache keyed on file contents, so reruns on unchanged files replay instantly; never bypass the hook with `--no-verify`.
- **PR completion** — Include a Changeset covering every package with releasable changes before opening a PR; test-only package edits need no bump. Follow [docs/releases.md](docs/releases.md) for versioning and release gates.

## Context pointers
Expand Down
38 changes: 19 additions & 19 deletions GLOSSARY-MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@
Read the repository ADRs before a package glossary, then read only the package
glossary relevant to the work:

| Package | Glossary | Domain focus |
| ----------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------- |
| `@ian-pascoe/pi-advisor` | [`packages/pi-advisor/GLOSSARY.md`](packages/pi-advisor/GLOSSARY.md) | Session review and tool grants |
| `@ian-pascoe/pi-minimal-subagents` | [`packages/pi-minimal-subagents/GLOSSARY.md`](packages/pi-minimal-subagents/GLOSSARY.md) | Persistent nested agents |
| `@ian-pascoe/pi-bible-verses` | [`packages/pi-bible-verses/GLOSSARY.md`](packages/pi-bible-verses/GLOSSARY.md) | Offline verse rotation and provenance |
| `@ian-pascoe/pi-tps-tracker` | [`packages/pi-tps-tracker/GLOSSARY.md`](packages/pi-tps-tracker/GLOSSARY.md) | Output-token throughput measurement |
| `@ian-pascoe/pi-command-deck` | [`packages/pi-command-deck/GLOSSARY.md`](packages/pi-command-deck/GLOSSARY.md) | Vim prompt editor and session chrome |
| `@ian-pascoe/pi-git-checkpoints` | [`packages/pi-git-checkpoints/GLOSSARY.md`](packages/pi-git-checkpoints/GLOSSARY.md) | Session-linked worktree restoration |
| `@ian-pascoe/pi-formatter` | [`packages/pi-formatter/GLOSSARY.md`](packages/pi-formatter/GLOSSARY.md) | Automatic post-edit formatting |
| `@ian-pascoe/pi-lsp` | [`packages/pi-lsp/GLOSSARY.md`](packages/pi-lsp/GLOSSARY.md) | Language-server tools and edit feedback |
| `@ian-pascoe/pi-dap` | [`packages/pi-dap/GLOSSARY.md`](packages/pi-dap/GLOSSARY.md) | Interactive debug sessions |
| `@ian-pascoe/pi-termctrl` | [`packages/pi-termctrl/GLOSSARY.md`](packages/pi-termctrl/GLOSSARY.md) | Terminals and Background jobs |
| `@ian-pascoe/pi-web-tools` | [`packages/pi-web-tools/GLOSSARY.md`](packages/pi-web-tools/GLOSSARY.md) | Public web search and retrieval |
| `@ian-pascoe/pi-todo` | [`packages/pi-todo/GLOSSARY.md`](packages/pi-todo/GLOSSARY.md) | Minimal session work tracking |
| `@ian-pascoe/pi-context-management` | [`packages/pi-context-management/GLOSSARY.md`](packages/pi-context-management/GLOSSARY.md) | Session Notes, History, and Rollover |
| `@ian-pascoe/pi-skills-selector` | [`packages/pi-skills-selector/GLOSSARY.md`](packages/pi-skills-selector/GLOSSARY.md) | Explicit Skill References in user input |
| `@ian-pascoe/pi-guardian` | [`packages/pi-guardian/GLOSSARY.md`](packages/pi-guardian/GLOSSARY.md) | Model-reviewed tool-call gating |
| `@ian-pascoe/pi-secret` | [`packages/pi-secret/GLOSSARY.md`](packages/pi-secret/GLOSSARY.md) | User-entered secrets kept from the model |
| `@ian-pascoe/pi-utils` | [`packages/pi-utils/GLOSSARY.md`](packages/pi-utils/GLOSSARY.md) | Shared UI conventions and Steer Yield |
| Package | Glossary | Domain focus |
| ----------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------- |
| `@ian-pascoe/pi-advisor` | [`packages/pi-advisor/GLOSSARY.md`](packages/pi-advisor/GLOSSARY.md) | Session review and tool grants |
| `@ian-pascoe/pi-minimal-subagents` | [`packages/pi-minimal-subagents/GLOSSARY.md`](packages/pi-minimal-subagents/GLOSSARY.md) | Persistent nested agents |
| `@ian-pascoe/pi-bible-verses` | [`packages/pi-bible-verses/GLOSSARY.md`](packages/pi-bible-verses/GLOSSARY.md) | Offline verse rotation and provenance |
| `@ian-pascoe/pi-tps-tracker` | [`packages/pi-tps-tracker/GLOSSARY.md`](packages/pi-tps-tracker/GLOSSARY.md) | Output-token throughput measurement |
| `@ian-pascoe/pi-command-deck` | [`packages/pi-command-deck/GLOSSARY.md`](packages/pi-command-deck/GLOSSARY.md) | Vim prompt editor and session chrome |
| `@ian-pascoe/pi-git-checkpoints` | [`packages/pi-git-checkpoints/GLOSSARY.md`](packages/pi-git-checkpoints/GLOSSARY.md) | Session-linked worktree restoration |
| `@ian-pascoe/pi-formatter` | [`packages/pi-formatter/GLOSSARY.md`](packages/pi-formatter/GLOSSARY.md) | Automatic post-edit formatting |
| `@ian-pascoe/pi-lsp` | [`packages/pi-lsp/GLOSSARY.md`](packages/pi-lsp/GLOSSARY.md) | Language-server tools and edit feedback |
| `@ian-pascoe/pi-dap` | [`packages/pi-dap/GLOSSARY.md`](packages/pi-dap/GLOSSARY.md) | Interactive debug sessions |
| `@ian-pascoe/pi-termctrl` | [`packages/pi-termctrl/GLOSSARY.md`](packages/pi-termctrl/GLOSSARY.md) | Terminals and Background jobs |
| `@ian-pascoe/pi-web-tools` | [`packages/pi-web-tools/GLOSSARY.md`](packages/pi-web-tools/GLOSSARY.md) | Public web search and retrieval |
| `@ian-pascoe/pi-todo` | [`packages/pi-todo/GLOSSARY.md`](packages/pi-todo/GLOSSARY.md) | Minimal session work tracking |
| `@ian-pascoe/pi-context-management` | [`packages/pi-context-management/GLOSSARY.md`](packages/pi-context-management/GLOSSARY.md) | Session Notes, History, and Rollover |
| `@ian-pascoe/pi-skills-selector` | [`packages/pi-skills-selector/GLOSSARY.md`](packages/pi-skills-selector/GLOSSARY.md) | Explicit Skill References in user input |
| `@ian-pascoe/pi-guardian` | [`packages/pi-guardian/GLOSSARY.md`](packages/pi-guardian/GLOSSARY.md) | Model-reviewed tool-call gating |
| `@ian-pascoe/pi-secret` | [`packages/pi-secret/GLOSSARY.md`](packages/pi-secret/GLOSSARY.md) | User-entered and provider-granted secrets kept from the model |
| `@ian-pascoe/pi-utils` | [`packages/pi-utils/GLOSSARY.md`](packages/pi-utils/GLOSSARY.md) | Shared UI conventions and Steer Yield |

`pi-context-management` implements session-owned Notes, selected-branch History retrieval with
a read-only Lineage scope over related agents' sessions, and native Context Checkpoints for
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ pnpm install
pnpm verify
```

`pnpm verify` runs through Turborepo, which caches each check by its inputs. A rerun on unchanged files, including the pre-push hook after a commit, replays from `.turbo/cache`.
`pnpm verify` runs through Turborepo, which caches each check by its inputs. A rerun on unchanged files replays from `.turbo/cache`. The pre-push hook runs `pnpm verify:push`, the same checks without the test suites; CI runs the full `pnpm verify`.
A package task's inputs cover only its own files and its declared workspace dependencies. When a test imports another package by relative path or reads a repository file, add that path to the task's `inputs` in `turbo.json`; otherwise a change there replays a stale pass.

Focused package checks:
Expand Down
7 changes: 7 additions & 0 deletions docs/adr/0010-secret-registry-refresh-callbacks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Let secret registry entries carry a refresh callback

[ADR-0008](0008-secret-redaction-registry-in-pi-utils.md) made `@ian-pascoe/pi-utils` own a process-wide registry of held Secret values, through which Child Agents resolve their root session's Secrets. Pi Secret now reads a Secret Grant's value from its Secret Provider at each use ([pi-secret ADR-0001](../../packages/pi-secret/docs/adr/0001-read-secret-grants-at-each-use.md)). A registry that only caches values would give Child Agents a stale value, such as an expired one-time password. So the owner of an entry can register an asynchronous refresh callback with it. A Child Agent's `tool_call` awaits the callback before substituting the value, and every value it returns stays registered for Redaction, including values that have since been replaced. The synchronous lookup and redaction APIs stay as they are, so Pi Guardian, which only redacts, is unaffected.

## Considered Options

- **Children use the root's most recently read value**: rejected because a Child Agent would then behave differently from its root for one-time passwords and revoked credentials.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"typecheck": "turbo run typecheck:root typecheck",
"typecheck:root": "tsc --noEmit",
"verify": "turbo run format:check lint typecheck:root typecheck test test:root --output-logs=errors-only",
"verify:push": "turbo run format:check lint typecheck:root typecheck --output-logs=errors-only",
"version-packages": "changeset version && pnpm install --lockfile-only"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion packages/pi-guardian/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Every `tool_call`, including calls a tool issues itself (such as a `codemode` sc
1. for `bash`, `terminal_start`, and `powershell`, `deny` when a segment of the command matches a `deny` **Command Rule** (see below), whatever the tool's Tool Policy;
2. the configured `tools.<name>` setting, if any;
3. else the built-in default:
- `allow`: `read`, `grep`, `find`, `ls`, `codemode`, `tool_search`, `todo`, `web_search`, and Context Management's `context_notes`, `context_history`, and `context_rollover`, which only touch the session's own notes, journal, and handoff, pi-advisor's `advisor_report`, which only hands an Advisor Review's findings to pi-advisor, and Pi Secret's `secret_ask`, `secret_list`, and `secret_forget`, which never reveal a Secret's value;
- `allow`: `read`, `grep`, `find`, `ls`, `codemode`, `tool_search`, `todo`, `web_search`, and Context Management's `context_notes`, `context_history`, and `context_rollover`, which only touch the session's own notes, journal, and handoff, pi-advisor's `advisor_report`, which only hands an Advisor Review's findings to pi-advisor, and Pi Secret's `secret_ask`, `secret_ask_grant`, `secret_list`, `secret_save`, and `secret_forget`, which never reveal a Secret's value and ask the user in Pi Secret's own dialog before granting or saving one;
- `edit` and `write`: `allow`, unless the target is a **Sensitive Path**, which is reviewed;
- `bash`: `allow` only for a **Safe Command**, otherwise reviewed;
- `terminal_start`, `terminal_send`, and `powershell`: reviewed, with the user's `deny` and `review` Command Rules named to the Guardian; a `terminal_start` or `powershell` command matching a `deny` rule is denied like `bash`;
Expand Down
7 changes: 5 additions & 2 deletions packages/pi-guardian/src/tool-policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ export const allowedByDefault: readonly string[] = [
"context_rollover",
// pi-advisor: finishes an Advisor Review; no side effects outside Pi.
"advisor_report",
// Pi Secret: asks the user, lists, or forgets Secrets; none reveals a value. Calls that use a
// Secret Reference are judged by their own tool's policy.
// Pi Secret: asks the user for, lists, saves, or forgets Secrets; none reveals a value, and
// grants and saves need the user's approval in Pi Secret's own dialog. Calls that use a Secret
// Reference are judged by their own tool's policy.
"secret_ask",
"secret_ask_grant",
"secret_list",
"secret_save",
"secret_forget",
];
/** Tools reviewed by default regardless of their annotations. */
Expand Down
2 changes: 2 additions & 0 deletions packages/pi-guardian/test/tool-policy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,9 @@ describe("Tool Policy resolution", () => {
"context_rollover",
"advisor_report",
"secret_ask",
"secret_ask_grant",
"secret_list",
"secret_save",
"secret_forget",
])("allows %s by default", (toolName) => {
expect(resolveToolPolicy(call({ toolName }))).toEqual({ policy: "allow", source: "default" });
Expand Down
30 changes: 27 additions & 3 deletions packages/pi-secret/GLOSSARY.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Secret context

The Secret context lets an agent use credentials the user enters without the values reaching the model provider, the session file, or the screen.
The Secret context lets an agent use credentials the user enters, or grants from a Secret Provider, without the values reaching the model provider, the session file, or the screen.

## Language

**Secret**:
A credential value the user entered in answer to a Secret Request, usable by the agent only through its Secret References until it expires or is forgotten.
A credential value, entered by the user or read through a Secret Grant, usable by the agent only through its Secret References until it expires or is forgotten.
_Avoid_: Credential, password, env var

**Secret Name**:
Expand All @@ -16,14 +16,38 @@ _Avoid_: Key, secret key, label
A string standing in for a Secret's value wherever the agent writes or reads it: `{{secret:NAME}}` in any tool argument, or the `PI_SECRET_NAME` environment variable in a spawned process. The agent and the transcript only ever see references.
_Avoid_: Placeholder, token, handle

**Secret Provider**:
An external store of credentials, such as 1Password, that Secrets can come from instead of being typed by the user, or that the user's typed Secrets can be saved into.
_Avoid_: Backend, vault, secret manager, source

**Secret Grant**:
The user's approval, given in answer to a Grant Request or when saving a typed Secret, for one Secret Name to be read from one location in a Secret Provider until the Secret Lifetime ends. The value is read again from the Secret Provider each time it is used.
_Avoid_: Permission, authorization, access

**Secret Locator**:
The location of one value in a Secret Provider, written as a URI whose scheme names the Secret Provider. A Secret Grant points at one Secret Locator.
_Avoid_: Path, address, provider reference

**1Password Reference**:
The Secret Locator form for 1Password: `op://vault/item/field`.
_Avoid_: Secret Reference (that term means the agent's stand-in for a Secret), op URI

**Secret Request**:
An agent's request for one or more Secret Names, optionally with a reason shown to the user, which the user answers by entering, skipping, or cancelling each value. Every request prompts again and replaces any Secret already held under the same name.
_Avoid_: Prompt, secret ask

**Grant Request**:
An agent's request for one or more Secret Grants, each naming a Secret Name and a Secret Locator, optionally with a reason shown to the user, which the user answers by approving or denying each grant or cancelling them all. Like a Secret Request, it replaces any Secret already held under the same name.
_Avoid_: Grant prompt, access request

**Secret Lifetime**:
The fixed span the user configures, counted from entry, after which a Secret expires and can no longer be read anywhere, including from the sessions that recorded it. The agent can neither choose nor extend it.
The fixed span the user configures, counted from entry or approval, after which a Secret or Secret Grant expires and can no longer be read anywhere, including from the sessions that recorded it. The agent can neither choose nor extend it.
_Avoid_: TTL, session lifetime, timeout

**Redaction**:
Replacing every occurrence of a held Secret's value, as is or in a common encoding such as base64, with its Secret Reference in what a tool returns or the provider receives.
_Avoid_: Masking, scrubbing

**Redaction Generation**:
How many values have been held in the process so far. A message is redacted at the generation it was first sent at, so values held later never change what the provider already cached.
_Avoid_: Redaction version, epoch
Loading
Loading