Repository navigation
feat: pass each project's trust to pi and name what trusting it loads - #690
Conversation
aaa9701 to
f187c9d
Compare
f187c9d to
e80df86
Compare
Additional observations (non-blocking)Beyond the existing review threads, here are a few more considerations: 1. New sessions during
|
Olga Lavrichenko (OLavrik)
left a comment
There was a problem hiding this comment.
All blocking issues resolved. Non-blocking observations acknowledged — they don't block the merge.
Fixed:
- Child preparation race (revalidation after assemble)
- Trust store read failure (undefined vs null distinction)
- Reload failure handling (error notify to user)
Non-blocking (noted for future):
- Abort reason semantics
- Sessions created during await window
- Partial failure reporting
- No caching in trustSummary
LGTM ✅
92ee01b to
f43e715
Compare
- Project records carry pi-level trust (piResourceTrust), applied at every
SettingsManager site including subagent children; existing projects are
grandfathered unless pi's trust.json denies them.
- project.setTrust({ resources: true }) is the only pi-level grant, sent by
surfaces that name what it loads; trusted: false revokes both grants.
- Grants and revokes reach live chats through the reload gate.
- project.trustSummary (protocol v78) backs ProjectTrustNotice, which
replaces ProjectSkillsNotice and gains Stop trusting.
…is revoked Children capture the project's pi-level trust at creation and pi-subagents retains them across a parent reload, so a revoke now aborts every live child of a session whose trust went stale, before that session's reload is requested. The Stop-trusting confirmation says so.
…t store piProjectTrustDecision distinguishes no saved decision (null) from a store it could not read (undefined); the migration leaves such a record without the field, which reads as untrusted, and seeds it on a later load.
…t change reports stopSessionChildren now exposes the settlement of every abort (a failed abort disposes the child); the trust revoke awaits it before requesting the parent reload, while Stop all keeps returning its count at once.
… trust change could not reload pi-delegation revalidates the projectTrusted binding after assembling a child and disposes one whose settings captured a trust that has since changed; applyPiResourceTrust reports a failed reload instead of swallowing it, and project.setTrust fails naming the chats still on the previous trust.
…r a settings change A trust change on a busy chat defers its reload; if that reload later fails, the chat now receives an error notify asking to close or reload it instead of silently keeping the previous resources.
… from pi's own decision
…keep a revoke path
f43e715 to
7dbc52a
Compare
Review round: rebase + fixesRebased onto
Body, screenshots and Testing are updated. Heads-up for the stack: #691 needs to adapt to |
Problem
Every session passes
projectTrusted: trueto pi, so pi loads every repository's own project resourcesthe moment a chat opens, trusted or not:
.pi/settings.json;SYSTEM.md;Only ThinkRail's committed skill aliases wait for the per-project trust grant.
The trust notice only counts those aliases. A repository whose risky content is a
.pi/extension or asubagent definition therefore shows no notice at all.
Approach
piResourceTrust(granted/untrusted).SettingsManagerwherever the host builds one fora project: sessions, pre-session loaders and skill listings, and subagent children.
ParentContext.isProjectTrustedinpi-delegation). ThinkRail answers the parent's loaded trust AND the current record, so a childnever holds more than its parent or the record.
piResourceTrustreads as untrusted and asks once through thetrust notice, instead of keeping code-running resources nobody consented to.
trust.json(nearest ancestor), else its globaldefaultProjectTrust: "always"; otherwise, and when the store is unreadable,untrusted.project.setTrust({ trusted: true, resources: true })is the only way to grant pi-level trust.Only screens that name what it loads send it: the Welcome notice, New Workspace and the Skills dialog
(the in-chat place to grant it), all built on one shared derivation.
acknowledged set, so aliases that arrived since still wait for review.
trusted: falserevokes both; the notice offers Stop trusting whenever either grant is held.reload only the chats whose trust is stale: an idle chat now; a busy one finishes its turn and reloads
at settlement. A revoke stops live subagents first. A chat still opening re-checks its trust when it
registers. Every trust change publishes the project record, so clients stay in sync even when a reload
fails and
project.setTrustreports it.project.trustSummary(PROJECT_TRUST_PROTOCOL_VERSION = 81)reports the alias skills and the kinds of pi resources present (
piResources: ProjectResourceKind[]:extensions, settings, skills, prompts, themes, system prompt, subagents).
ProjectTrustNoticereplacesProjectSkillsNoticeon Welcome and in NewWorkspace. For example: "This project ships 1 skill and its own Pi resources — off until you trust
it", then exactly what trust loads ("Trust loads its prompts."), saying it runs code only when
extensions or settings are present.
project.aliasSkills, which meansaliases only.
Changes
Project.piResourceTrustandProjectTrustSummary;project.trustSummarymethod and theproject.setTrustresourcesparameter;ProjectResourceKind;main's subagent concurrency and turn changes), and a "Project trust" section in the contracts SPEC.packages/server/src/projects:setPiTrustSeed, the seeding inopenProject, and the two grantsin
setProjectTrust, which publishes the record.packages/server/src/agent:projectTrust.ts:piTrustsProjectPathandprojectTrustSummary;skillAdmission.ts:piResourceTrustedandadmissionContextFor;extensions.ts: trust applied before every load, and listing trust;agentSessionManager.ts:buildSessionSettings(cwd, projectTrusted),applyPiResourceTrust, theregistration re-check, and
liveParentContext.isProjectTrusted;delegation.ts:stopSessionChildren.packages/pi-delegation: optionalisProjectTrustedonParentContextandResourceContextInput;buildChildSettingsreceives that trust (building on fix(delegation): deliver the image autoResize override to subagent children #657's hook), and a child prepared across a trustchange is refused. The pure-pi fallback in
pi-subagentspasses itsctx, so vanilla-pi children nowinherit the session's trust too.
project.setTrustwithresourcesplus the gated reload;project.trustSummary; the trustseed;
admissionContextForin place of four hand-built contexts.ProjectTrustNotice;chat/projectTrust.tsandchat/useProjectTrustSummary.ts, shared by Welcome,New Workspace and the Skills dialog (in
chat/becausechatmay not depend onpanels);selectSupportsProjectTrust..pi/extensionscode runs only under pi-level trust (both loader branches);e2e for subagent-definition-only projects (
new-workspace), the Welcome grant/Stop-trusting flow(
welcome) and the Skills dialog grant (skills-switches).goal-and-requirements.md("Trust is explicit").Notes for reviewers:
projectTrusted: false(alias roots are explicit paths), and pre-session skill listingsnow hide a repository's
.pi/skillsuntil pi-level trust is granted — hence the changed expectation inextensions.test.ts..pi/promptsregardless of trust: it reads text the userchooses to send and runs nothing. pi's own prompt-template loading follows pi-level trust.
trust was granted on a host that supports it; otherwise only new chats lose the project's skills.
.pi/entries (minusmcp.json, which no ThinkRailsession loads before feat(server): run the MCP servers from mcp.json in chats on pi's built-in engine #691); a test asserts each detected kind is also gated by pi's
hasTrustRequiringProjectResources.piResources(it adds an MCP server count to the summary) and to the removedprojectTrusteddelegation binding.Screenshots
Captured with the e2e harness's hermetic fixture project (one committed skill alias plus a
.pi/agentssubagent definition), 1440×900 at 2×, dark theme; the same throwaway scenario ran onmain(before) and on this PR's tree (after).Welcome trust notice — untrusted project
Welcome trust notice — after trusting
Welcome — pi already trusts the folder, alias skills still pending (Stop trusting stays reachable)
Chat Skills dialog trust banner
Related issues
No tracked issue. This is part of the MCP support stack (milestone 1); review and merge in order:
#689 has merged; this PR is rebased onto
main. #691–#695 still sit on the previous head of this branch and need a restack.Testing
On this PR's tree (after the rebase onto
mainand the review fixes):bunx turbo run typecheck --filter='!@thinkrail/desktop'→ 19/19; each review-fix commit also typechecks on its ownbun run test→ 20/20 tasksbun run lint→ clean;check:deps,check:boundaries,check:seams,check:spec-surface,check:compiler→ OKbun run e2e→ run twice; each run had one unrelated chat-rendering failure (chat-scroll.spec.ts:616, then the knownactivity-breadcrumbs.spec.ts:240flake) that passes on focused reruns (--repeat-each=3: 6/6 and 12/12)bun run e2e:full e2e/subagents.live.spec.ts e2e/skills-reload.live.spec.ts e2e/new-workspace.live.spec.ts) → 6 passed. The completebun run e2e:fullsuite was not rerun.Desktop:
@thinkrail/desktop's typecheck still cannot run here: itselectrobun preparestep waits on a cache lock held by a runningelectrobun dev. The same lock blocks the pre-commit hook, so the review-fix commits were made with--no-verifyafter running the hook's other checks by hand. This change touches no desktop file.Checklist
bun run lint,bun run typecheck,bun run testbun run e2e, orbun run e2e:fullwhen touching agent behavior) — see Testing: one unrelated flake per full runSPEC.md/ top-level specs updated to reflect any boundary, contract, or behavior change