pi.lock 0.0.0-stage → 0.1.1
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/README.md +91 -2
- package/dist/bridge.d.ts +7 -0
- package/dist/bridge.js +18 -0
- package/dist/bridge.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +82 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +1 -0
- package/dist/commands.js +114 -0
- package/dist/commands.js.map +1 -0
- package/dist/common.d.ts +17 -0
- package/dist/common.js +87 -0
- package/dist/common.js.map +1 -0
- package/dist/definition.d.ts +32 -0
- package/dist/definition.js +384 -0
- package/dist/definition.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +6 -0
- package/dist/init.js +39 -0
- package/dist/init.js.map +1 -0
- package/dist/loader.d.ts +48 -0
- package/dist/loader.js +141 -0
- package/dist/loader.js.map +1 -0
- package/dist/lock.d.ts +17 -0
- package/dist/lock.js +93 -0
- package/dist/lock.js.map +1 -0
- package/dist/mcp.d.ts +8 -0
- package/dist/mcp.js +113 -0
- package/dist/mcp.js.map +1 -0
- package/dist/model.d.ts +14 -0
- package/dist/model.js +78 -0
- package/dist/model.js.map +1 -0
- package/dist/runtime.d.ts +37 -0
- package/dist/runtime.js +266 -0
- package/dist/runtime.js.map +1 -0
- package/docs/acceptance.md +54 -0
- package/docs/acceptance.tap +178 -0
- package/docs/architecture.md +47 -0
- package/examples/demo/.pi/SYSTEM.md +1 -0
- package/examples/demo/.pi/extensions/demo.ts +13 -0
- package/examples/demo/.pi/models.json +26 -0
- package/examples/demo/.pi/prompts/hello.md +4 -0
- package/examples/demo/.pi/settings.json +15 -0
- package/examples/demo/.pi/skills/explain/SKILL.md +5 -0
- package/examples/demo/pi.lock +473 -0
- package/npm-shrinkwrap.json +1880 -0
- package/package.json +46 -4
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { createAgentSession as createPiSession, createAgentSessionRuntime, SettingsManager, SessionManager, } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { lazyStream } from "@earendil-works/pi-ai";
|
|
5
|
+
import { assertEngine } from "./bridge.js";
|
|
6
|
+
import { digest, fail, findProject, replaceRoots } from "./common.js";
|
|
7
|
+
import { readLock, verifyDefinition, verifySnapshot, materialize, draftLock, saveLock } from "./lock.js";
|
|
8
|
+
import { LockedResourceLoader } from "./loader.js";
|
|
9
|
+
import { assertModel, keyBinding, modelIdentity, modelRuntime, selectedModel } from "./model.js";
|
|
10
|
+
const UI_SETTERS = new Set([
|
|
11
|
+
"setTheme", "setHideThinkingBlock", "setShowCacheMissNotices", "setQuietStartup", "setCollapseChangelog", "setLastChangelogVersion",
|
|
12
|
+
"setShowImages", "setClearOnShrink", "setShowTerminalProgress", "setTuiMode", "setFullscreenExitOutput", "setFullscreenScrollbar",
|
|
13
|
+
"setFullscreenCopyOnSelect", "setFullscreenWheelScrollLines", "setDoubleEscapeAction", "setTreeFilterMode", "setShowHardwareCursor",
|
|
14
|
+
"setEditorPaddingX", "setOutputPad", "setAutocompleteMaxVisible", "setMermaidRenderingMode",
|
|
15
|
+
]);
|
|
16
|
+
function lockedSettings(settings, frozen) {
|
|
17
|
+
const manager = SettingsManager.inMemory({ ...settings, enableAnalytics: false, enableInstallTelemetry: false, cacheWarming: "off" }, { projectTrusted: true });
|
|
18
|
+
if (frozen)
|
|
19
|
+
for (const name of Object.getOwnPropertyNames(SettingsManager.prototype)) {
|
|
20
|
+
if ((name.startsWith("set") && !UI_SETTERS.has(name)) || name === "applyOverrides") {
|
|
21
|
+
manager[name] = () => fail("FROZEN_SETTING", `${name} changes locked behavior. Edit .pi/settings.json and run pi-lock lock, or start --dev.`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return manager;
|
|
25
|
+
}
|
|
26
|
+
function developmentSettings(projectRoot) {
|
|
27
|
+
const path = resolve(projectRoot, ".pi/settings.json");
|
|
28
|
+
return SettingsManager.fromStorage({ withLock(scope, fn) {
|
|
29
|
+
if (scope === "project") {
|
|
30
|
+
fn(undefined);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const changed = fn(readFileSync(path, "utf8"));
|
|
34
|
+
if (changed !== undefined)
|
|
35
|
+
writeFileSync(path, changed);
|
|
36
|
+
} }, { projectTrusted: true });
|
|
37
|
+
}
|
|
38
|
+
export function headlessUI(overrides = {}) {
|
|
39
|
+
const required = async () => fail("INTERACTION_REQUIRED", "INTERACTION_REQUIRED: this operation needs an interaction handler. Supply uiContext in the SDK or respond through TUI/RPC.");
|
|
40
|
+
const noop = () => { };
|
|
41
|
+
return {
|
|
42
|
+
select: required, confirm: required, input: required, editor: required, custom: required,
|
|
43
|
+
notify: noop, onTerminalInput: () => noop, setStatus: noop, setWorkingMessage: noop,
|
|
44
|
+
setWorkingVisible: noop, setWorkingIndicator: noop, setHiddenThinkingLabel: noop, setWidget: noop,
|
|
45
|
+
setFooter: noop, setHeader: noop, setTitle: noop, pasteToEditor: noop, setEditorText: noop,
|
|
46
|
+
getEditorText: () => "", addAutocompleteProvider: noop, setEditorComponent: noop,
|
|
47
|
+
get theme() { return undefined; },
|
|
48
|
+
...overrides,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function pinSession(manager, lock, dev) {
|
|
52
|
+
const pins = manager.getEntries().filter((e) => e.type === "custom" && e.customType === "pi-lock");
|
|
53
|
+
if (pins.some((e) => e.data?.agentDigest !== lock.agentDigest) && !dev)
|
|
54
|
+
fail("SESSION_VERSION_MISMATCH", "This session belongs to a different Agent version. Use its matching release or start a new session.");
|
|
55
|
+
if (!pins.length) {
|
|
56
|
+
if (manager.buildSessionContext().messages.length && !dev)
|
|
57
|
+
fail("SESSION_UNPINNED", "This transcript has no Agent version. Start a new locked session; importing history requires an explicit migration.");
|
|
58
|
+
manager.appendCustomEntry("pi-lock", { agentDigest: lock.agentDigest, lockfileVersion: 1 });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function effectiveConfiguration(session, releaseRoot, cwd, lock) {
|
|
62
|
+
const loader = session.resourceLoader;
|
|
63
|
+
const tools = session.getAllTools().map(info => {
|
|
64
|
+
const tool = session.getToolDefinition(info.name);
|
|
65
|
+
return { name: info.name, description: info.description, parameters: tool?.parameters ?? info.parameters,
|
|
66
|
+
promptSnippet: tool?.promptSnippet, promptGuidelines: tool?.promptGuidelines, exposure: info.exposure,
|
|
67
|
+
constrainedSampling: tool?.constrainedSampling };
|
|
68
|
+
});
|
|
69
|
+
return replaceRoots({
|
|
70
|
+
model: modelIdentity(session.model), thinkingLevel: session.thinkingLevel,
|
|
71
|
+
settings: lock.definition.settings,
|
|
72
|
+
extensions: loader.getExtensions().extensions.map(e => ({ path: e.path, commands: [...e.commands.keys()], hooks: [...e.handlers.keys()] })),
|
|
73
|
+
skills: loader.getSkills().skills, prompts: loader.getPrompts().prompts,
|
|
74
|
+
context: loader.getAgentsFiles().agentsFiles, systemPrompt: session.systemPrompt,
|
|
75
|
+
tools, activeTools: session.getActiveToolNames(), mcp: loader.mcp?.catalog,
|
|
76
|
+
}, [[releaseRoot, "<AGENT>"], [cwd, "<WORKSPACE>"]]);
|
|
77
|
+
}
|
|
78
|
+
export async function createLockedRuntime(options = {}, prepared) {
|
|
79
|
+
const allowed = new Set(["projectRoot", "cwd", "sessionManager", "sessionDir", "sessionFile", "uiContext", "dev", "apiKey", "credentials", "env"]);
|
|
80
|
+
for (const key of Object.keys(options))
|
|
81
|
+
if (!allowed.has(key))
|
|
82
|
+
fail("UNLOCKED_OVERRIDE", `SDK option ${key} is not a runtime binding. Put Agent behavior in .pi/settings.json or an extension, then lock it.`);
|
|
83
|
+
assertEngine();
|
|
84
|
+
const cwd = realpathSync(resolve(options.cwd ?? process.cwd()));
|
|
85
|
+
const projectRoot = findProject(cwd, options.projectRoot);
|
|
86
|
+
let lock = prepared ?? (options.dev ? await draftLock(projectRoot, true) : readLock(projectRoot));
|
|
87
|
+
if (!options.dev)
|
|
88
|
+
verifyDefinition(projectRoot, lock);
|
|
89
|
+
let releaseRoot = materialize(projectRoot, lock);
|
|
90
|
+
const stateDir = resolve(options.sessionDir ?? resolve(projectRoot, ".pi-lock/state/sessions", lock.agentDigest, digest(cwd).slice(0, 12)));
|
|
91
|
+
const agentDir = resolve(projectRoot, ".pi-lock/state/ui");
|
|
92
|
+
mkdirSync(agentDir, { recursive: true });
|
|
93
|
+
const manager = options.sessionManager ?? (options.sessionFile ? SessionManager.open(resolve(options.sessionFile), stateDir, cwd) : SessionManager.create(cwd, stateDir));
|
|
94
|
+
let host;
|
|
95
|
+
let lastInspection;
|
|
96
|
+
const create = async (input) => {
|
|
97
|
+
verifySnapshot(releaseRoot, lock);
|
|
98
|
+
pinSession(input.sessionManager, lock, Boolean(options.dev));
|
|
99
|
+
const settings = options.dev ? developmentSettings(projectRoot) : lockedSettings(lock.definition.settings, true);
|
|
100
|
+
const models = await modelRuntime(lock.definition, options, Boolean(options.dev));
|
|
101
|
+
const model = selectedModel(models, lock.definition, Boolean(options.dev));
|
|
102
|
+
if (!options.dev)
|
|
103
|
+
assertModel(model, lock.model);
|
|
104
|
+
const loader = new LockedResourceLoader(releaseRoot, input.cwd, lock.definition, options.env, lock.effective?.mcp);
|
|
105
|
+
await loader.reload();
|
|
106
|
+
let result;
|
|
107
|
+
try {
|
|
108
|
+
result = await createPiSession({ cwd: input.cwd, agentDir,
|
|
109
|
+
modelRuntime: models, model, thinkingLevel: lock.definition.settings.defaultThinkingLevel,
|
|
110
|
+
scopedModels: options.dev ? undefined : [{ model: model, thinkingLevel: lock.definition.settings.defaultThinkingLevel ?? "medium" }],
|
|
111
|
+
resourceLoader: loader, settingsManager: settings, sessionManager: input.sessionManager,
|
|
112
|
+
sessionStartEvent: input.sessionStartEvent, });
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
await loader.mcp?.close();
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
const session = result.session;
|
|
119
|
+
if (options.dev && (!settings.getDefaultProvider() || !settings.getDefaultModel())) {
|
|
120
|
+
const setModel = session.setModel.bind(session);
|
|
121
|
+
session.setModel = (async (next, opts = {}) => setModel(next, { ...opts, persist: true }));
|
|
122
|
+
}
|
|
123
|
+
if (options.dev)
|
|
124
|
+
await models.refresh({ allowNetwork: false });
|
|
125
|
+
const errors = [];
|
|
126
|
+
const originalBind = session.bindExtensions.bind(session);
|
|
127
|
+
let boundRunner;
|
|
128
|
+
let boundMode;
|
|
129
|
+
const validate = (initial = true) => {
|
|
130
|
+
if (errors.length)
|
|
131
|
+
fail("EXTENSION_RUNTIME_FAILED", errors.join("\n"));
|
|
132
|
+
if (!options.dev)
|
|
133
|
+
assertModel(session.model, lock.model);
|
|
134
|
+
const effective = effectiveConfiguration(session, releaseRoot, input.cwd, lock);
|
|
135
|
+
if (initial && lock.effective && !options.dev && digest(effective) !== digest(lock.effective))
|
|
136
|
+
fail("EFFECTIVE_CONFIG_DRIFT", "Actual model, prompt, tools, or extensions differ from pi.lock. Mode-dependent extension initialization is not portable.");
|
|
137
|
+
lastInspection = { agentDigest: lock.agentDigest, effectiveDigest: digest(effective), frozen: !options.dev, pi: lock.runtime.pi,
|
|
138
|
+
credentialEnvironment: lock.definition.settings.defaultProvider ? keyBinding(lock.definition) : undefined, configuration: effective };
|
|
139
|
+
};
|
|
140
|
+
session.bindExtensions = async (bindings) => {
|
|
141
|
+
// Pi RPC may rebind twice after new_session. Run startup hooks once per extension runtime.
|
|
142
|
+
if (boundRunner === session.extensionRunner) {
|
|
143
|
+
if (bindings.mode && bindings.mode !== boundMode)
|
|
144
|
+
fail("MODE_ALREADY_BOUND", "Create a fresh runtime when changing its interface mode.");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
await originalBind({ ...bindings, uiContext: bindings.uiContext ?? headlessUI(options.uiContext), onError: error => { errors.push(`${error.event}: ${error.error}`); bindings.onError?.(error); } });
|
|
148
|
+
validate();
|
|
149
|
+
await loader.mcp?.check();
|
|
150
|
+
boundRunner = session.extensionRunner;
|
|
151
|
+
boundMode = bindings.mode ?? "print";
|
|
152
|
+
};
|
|
153
|
+
if (!options.dev) {
|
|
154
|
+
const setModel = session.setModel.bind(session);
|
|
155
|
+
session.setModel = (async (next, opts) => { assertModel(next, lock.model); return setModel(next, opts); });
|
|
156
|
+
const setThinking = session.setThinkingLevel.bind(session);
|
|
157
|
+
session.setThinkingLevel = (level, opts) => {
|
|
158
|
+
if (level !== session.thinkingLevel)
|
|
159
|
+
fail("FROZEN_SETTING", "Thinking level is locked. Edit .pi/settings.json and re-lock.");
|
|
160
|
+
return setThinking(level, opts);
|
|
161
|
+
};
|
|
162
|
+
for (const name of ["setSteeringMode", "setFollowUpMode", "setAutoCompactionEnabled", "setAutoRetryEnabled"]) {
|
|
163
|
+
if (typeof session[name] === "function")
|
|
164
|
+
session[name] = () => fail("FROZEN_SETTING", `${name} changes locked behavior. Use --dev, then re-lock.`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const originalPrompt = session.prompt.bind(session);
|
|
168
|
+
session.prompt = async (text, opts) => {
|
|
169
|
+
verifySnapshot(releaseRoot, lock);
|
|
170
|
+
validate(false);
|
|
171
|
+
await loader.mcp?.check();
|
|
172
|
+
const result = await originalPrompt(text, opts);
|
|
173
|
+
if (errors.length)
|
|
174
|
+
fail("EXTENSION_RUNTIME_FAILED", errors.join("\n"));
|
|
175
|
+
const last = session.messages.at(-1);
|
|
176
|
+
if (last?.role === "assistant" && last.stopReason === "error")
|
|
177
|
+
fail("MODEL_REQUEST_FAILED", last.errorMessage ?? "Model request failed.");
|
|
178
|
+
return result;
|
|
179
|
+
};
|
|
180
|
+
const stream = models.streamSimple.bind(models);
|
|
181
|
+
models.streamSimple = (selected, context, streamOptions) => {
|
|
182
|
+
verifySnapshot(releaseRoot, lock);
|
|
183
|
+
if (!options.dev)
|
|
184
|
+
assertModel(selected, lock.model);
|
|
185
|
+
return lazyStream(selected, async () => { await loader.mcp?.check(); return stream(selected, context, streamOptions); });
|
|
186
|
+
};
|
|
187
|
+
const reload = session.reload.bind(session);
|
|
188
|
+
session.reload = async (opts) => {
|
|
189
|
+
if (!options.dev)
|
|
190
|
+
verifyDefinition(projectRoot, lock);
|
|
191
|
+
else {
|
|
192
|
+
const next = await draftLock(projectRoot, true);
|
|
193
|
+
if (next.model) {
|
|
194
|
+
const configured = models.getModel(next.definition.settings.defaultProvider, next.definition.settings.defaultModel);
|
|
195
|
+
if (!configured || digest(modelIdentity(configured)) !== digest(next.model))
|
|
196
|
+
fail("RESTART_REQUIRED", "Model configuration changed. Restart pi-lock --dev to rebuild its provider runtime.");
|
|
197
|
+
}
|
|
198
|
+
lock = next;
|
|
199
|
+
releaseRoot = materialize(projectRoot, next);
|
|
200
|
+
loader.root = releaseRoot;
|
|
201
|
+
loader.definition = next.definition;
|
|
202
|
+
}
|
|
203
|
+
await reload(opts);
|
|
204
|
+
validate();
|
|
205
|
+
boundRunner = session.extensionRunner;
|
|
206
|
+
};
|
|
207
|
+
return { ...result, services: { cwd: input.cwd, agentDir, modelRuntime: models, settingsManager: settings, resourceLoader: loader, diagnostics: [] }, diagnostics: [] };
|
|
208
|
+
};
|
|
209
|
+
host = await createAgentSessionRuntime(create, { cwd, agentDir, sessionManager: manager });
|
|
210
|
+
const originalDispose = host.dispose.bind(host);
|
|
211
|
+
let disposing;
|
|
212
|
+
host.dispose = () => disposing ??= (async () => {
|
|
213
|
+
await host.session.abort();
|
|
214
|
+
await host.services.settingsManager.flush();
|
|
215
|
+
await originalDispose();
|
|
216
|
+
})();
|
|
217
|
+
const bind = async (mode = "print", ui) => {
|
|
218
|
+
await host.session.bindExtensions({ mode, uiContext: headlessUI(ui ?? options.uiContext),
|
|
219
|
+
commandContextActions: {
|
|
220
|
+
waitForIdle: () => host.session.agent.waitForIdle(),
|
|
221
|
+
newSession: opts => host.newSession(opts), fork: (id, opts) => host.fork(id, opts),
|
|
222
|
+
navigateTree: (id, opts) => host.session.navigateTree(id, opts),
|
|
223
|
+
switchSession: (path, opts) => host.switchSession(path, opts),
|
|
224
|
+
reload: () => host.session.reload(),
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
host.setRebindSession(() => bind());
|
|
229
|
+
return { runtime: host, get lock() { return lock; }, projectRoot, get releaseRoot() { return releaseRoot; }, inspect: () => lastInspection, bind, dispose: () => host.dispose() };
|
|
230
|
+
}
|
|
231
|
+
/** Drop-in-shaped SDK entry: same Pi session, with project-only loading and frozen checks. */
|
|
232
|
+
export async function createAgentSession(options = {}) {
|
|
233
|
+
const locked = await createLockedRuntime(options);
|
|
234
|
+
try {
|
|
235
|
+
await locked.bind("print", options.uiContext);
|
|
236
|
+
}
|
|
237
|
+
catch (e) {
|
|
238
|
+
await locked.dispose();
|
|
239
|
+
throw e;
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
get session() { return locked.runtime.session; },
|
|
243
|
+
runtime: locked.runtime,
|
|
244
|
+
inspect: locked.inspect,
|
|
245
|
+
get agentDigest() { return locked.lock.agentDigest; },
|
|
246
|
+
dispose: locked.dispose,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
export async function lockProject(projectRoot) {
|
|
250
|
+
const root = findProject(projectRoot, projectRoot);
|
|
251
|
+
const lock = await draftLock(root);
|
|
252
|
+
const runtime = await createLockedRuntime({ projectRoot: root, cwd: root, sessionManager: SessionManager.inMemory(root) }, lock);
|
|
253
|
+
try {
|
|
254
|
+
await runtime.bind();
|
|
255
|
+
lock.effective = runtime.inspect().configuration;
|
|
256
|
+
// A source edit during extension initialization must never produce a mixed release.
|
|
257
|
+
verifyDefinition(root, lock);
|
|
258
|
+
saveLock(root, lock);
|
|
259
|
+
materialize(root, lock);
|
|
260
|
+
return lock;
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
await runtime.dispose();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# pi.lock Acceptance Report
|
|
2
|
+
|
|
3
|
+
Acceptance date: 2026-10-18 (Asia/Shanghai). Project and npm package: `pi.lock@0.1.0`. Supported Pi version: **1.3.0**.
|
|
4
|
+
|
|
5
|
+
The package identifier and SDK self-reference were updated for publication as `pi.lock`; the CLI command remains `pi-lock`.
|
|
6
|
+
|
|
7
|
+
## Results
|
|
8
|
+
|
|
9
|
+
`npm test`: **26 passed, 0 failed, 0 skipped**, about 47.2 seconds. TypeScript build passed. The full output is available in [acceptance.tap](acceptance.tap).
|
|
10
|
+
|
|
11
|
+
Both the source checkout and packed npm package were run. After installation in a clean application directory, the package verified and ran an unchanged agent definition and lockfile. The acceptance test checks that the npm package includes `npm-shrinkwrap` and excludes `.pi-lock` state and `auth.json`.
|
|
12
|
+
|
|
13
|
+
## Acceptance scope
|
|
14
|
+
|
|
15
|
+
| Capability | Evidence collected |
|
|
16
|
+
|---|---|
|
|
17
|
+
| Pi version | Official npm registry reported `latest` as 1.3.0; dependency versions are exact-pinned |
|
|
18
|
+
| Global configuration isolation | Polluted HOME, global extensions and skills, parent AGENTS, and task-directory `.pi`; marker extension did not execute and ambient instructions were not loaded |
|
|
19
|
+
| Portable resources | Copied an agent to another directory; digest, effective configuration fingerprint, and content matched |
|
|
20
|
+
| SDK / TUI / RPC / print | Real Pi `AgentSession`; native TUI ran in a PTY; compared provider transcripts/options, including hooks, tool schemas, and the follow-up request after tool execution |
|
|
21
|
+
| HTTP model path | Connected Pi's native OpenAI-compatible HTTP streaming implementation to a local service; compared SDK and CLI request bodies and checked explicit credential injection |
|
|
22
|
+
| Frozen execution | Additions, modifications, and deletions of resources; model/thinking/settings changes; and lockfile or snapshot tampering were rejected |
|
|
23
|
+
| Lifecycle | New, reloaded, forked, and resumed sessions used the locked factory; RPC new-session initialization ran once |
|
|
24
|
+
| Multiple agents | Ran two agents concurrently in one SDK process; reloading one left the other working; process cwd/env stayed unchanged |
|
|
25
|
+
| Git rollback | Committed V1 and V2 in a temporary Git repository, checked out V1, and ran it; an already-started V1 continued reading its snapshot after the source directory changed to V2 |
|
|
26
|
+
| Session version | Resuming an old session with a new agent version failed explicitly |
|
|
27
|
+
| npm package and dependencies | Ran real npm pack/install; package resources, helper imports, and dependency files were locked; tampering was rejected; missing dependencies after relocation prevented execution and `sync --frozen` restored them |
|
|
28
|
+
| MCP | A local stdio server tool call succeeded; schema changes before startup and during a session were detected; codemode/deferred exposure initialized through native tool support |
|
|
29
|
+
| Interaction | SDK without a callback did not auto-approve; explicit native `uiContext.confirm` callback worked |
|
|
30
|
+
| Model fallback | A missing locked model failed instead of selecting another model |
|
|
31
|
+
| Native resource conventions | Project AGENTS, project `.agents/skills`, loose skills, globs, and resource exclusions resolved correctly |
|
|
32
|
+
| Tune, then release | Native model selection API wrote to `.pi/settings.json` in development mode; the next lock used the saved model |
|
|
33
|
+
| RPC output | Extension startup `console.log` did not corrupt JSONL stdout |
|
|
34
|
+
| Package reproducibility | Installed the tarball in a new application without source-project development dependencies; the unchanged lock verified and ran |
|
|
35
|
+
|
|
36
|
+
Request comparisons normalize deployment paths, session/response IDs, timestamps, durations, and usage statistics from the local mock model. Instructions, content, tool schemas, call arguments, and hook results are compared.
|
|
37
|
+
|
|
38
|
+
## Environment
|
|
39
|
+
|
|
40
|
+
- macOS 27.0.1 / arm64
|
|
41
|
+
- Node.js v22.23.2
|
|
42
|
+
- npm 10.9.8
|
|
43
|
+
- Pi `@earendil-works/pi-coding-agent` 1.3.0
|
|
44
|
+
- The PTY driver uses system Python 3; it is not a runtime requirement for pi-lock
|
|
45
|
+
- Demo agent digest: `bf54f0845e5553fc6948add9a1711455feb5bc98d39eb40a9ade8cca00436827`
|
|
46
|
+
- Full test-log SHA-256: `4e41da880942cf2bffa3d2c53cb23c77fdba932a90693c488891a6765265de1d`
|
|
47
|
+
|
|
48
|
+
## Tested boundaries
|
|
49
|
+
|
|
50
|
+
Model responses came from a deterministic local provider and local HTTP fixture; no real provider account, paid request, or live model response was used as a substitute for consistency checks. MCP tool calls and drift detection used a local stdio fixture. External MCP HTTP services, OAuth provider login, and the full third-party extension ecosystem were not tested against live services.
|
|
51
|
+
|
|
52
|
+
Relocation and clean-install tests ran in separate directories on the same macOS machine. The Docker command was available, but its daemon was not, so Linux, Windows, and another physical machine were not tested. Same-machine directory relocation is not represented as cross-platform acceptance.
|
|
53
|
+
|
|
54
|
+
See the [README](../README.md) and [architecture notes](architecture.md) for extension compatibility, host-command dependencies, session migration, and external side-effect boundaries.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
|
|
2
|
+
> pi.lock@0.1.0 test
|
|
3
|
+
> npm run build && node --test --test-concurrency=1 test/*.test.mjs
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
> pi.lock@0.1.0 build
|
|
7
|
+
> tsc -p tsconfig.json
|
|
8
|
+
|
|
9
|
+
TAP version 13
|
|
10
|
+
# Subtest: locks latest Pi 1.3.0 and preserves the ordinary .pi layout
|
|
11
|
+
ok 1 - locks latest Pi 1.3.0 and preserves the ordinary .pi layout
|
|
12
|
+
---
|
|
13
|
+
duration_ms: 3073.224209
|
|
14
|
+
type: 'test'
|
|
15
|
+
...
|
|
16
|
+
# Subtest: ignores polluted HOME, global extensions, global skills, parent AGENTS and workspace .pi
|
|
17
|
+
ok 2 - ignores polluted HOME, global extensions, global skills, parent AGENTS and workspace .pi
|
|
18
|
+
---
|
|
19
|
+
duration_ms: 1860.822375
|
|
20
|
+
type: 'test'
|
|
21
|
+
...
|
|
22
|
+
# Subtest: relocation preserves Agent digest and actual effective configuration
|
|
23
|
+
ok 3 - relocation preserves Agent digest and actual effective configuration
|
|
24
|
+
---
|
|
25
|
+
duration_ms: 65.6075
|
|
26
|
+
type: 'test'
|
|
27
|
+
...
|
|
28
|
+
# Subtest: frozen execution rejects changed, removed and added resources before extension execution
|
|
29
|
+
ok 4 - frozen execution rejects changed, removed and added resources before extension execution
|
|
30
|
+
---
|
|
31
|
+
duration_ms: 81.851125
|
|
32
|
+
type: 'test'
|
|
33
|
+
...
|
|
34
|
+
# Subtest: frozen SDK and CLI reject behavioral overrides and thinking changes
|
|
35
|
+
ok 5 - frozen SDK and CLI reject behavioral overrides and thinking changes
|
|
36
|
+
---
|
|
37
|
+
duration_ms: 500.402667
|
|
38
|
+
type: 'test'
|
|
39
|
+
...
|
|
40
|
+
# Subtest: SDK, native print and native RPC produce the same real model requests including hooks and tool rounds
|
|
41
|
+
ok 6 - SDK, native print and native RPC produce the same real model requests including hooks and tool rounds
|
|
42
|
+
---
|
|
43
|
+
duration_ms: 3589.101084
|
|
44
|
+
type: 'test'
|
|
45
|
+
...
|
|
46
|
+
# Subtest: the real native TUI in a PTY uses the same model requests as the SDK
|
|
47
|
+
ok 7 - the real native TUI in a PTY uses the same model requests as the SDK
|
|
48
|
+
---
|
|
49
|
+
duration_ms: 2101.829708
|
|
50
|
+
type: 'test'
|
|
51
|
+
...
|
|
52
|
+
# Subtest: new, reload, fork and resume retain locked resources and session version
|
|
53
|
+
ok 8 - new, reload, fork and resume retain locked resources and session version
|
|
54
|
+
---
|
|
55
|
+
duration_ms: 110.145875
|
|
56
|
+
type: 'test'
|
|
57
|
+
...
|
|
58
|
+
# Subtest: two Agents coexist in one SDK process and reload without changing cwd or environment
|
|
59
|
+
ok 9 - two Agents coexist in one SDK process and reload without changing cwd or environment
|
|
60
|
+
---
|
|
61
|
+
duration_ms: 93.562625
|
|
62
|
+
type: 'test'
|
|
63
|
+
...
|
|
64
|
+
# Subtest: Git rollback restores an earlier Agent; running snapshots survive source changes
|
|
65
|
+
ok 10 - Git rollback restores an earlier Agent; running snapshots survive source changes
|
|
66
|
+
---
|
|
67
|
+
duration_ms: 175.602584
|
|
68
|
+
type: 'test'
|
|
69
|
+
...
|
|
70
|
+
# Subtest: resuming a session with a different Agent version fails explicitly
|
|
71
|
+
ok 11 - resuming a session with a different Agent version fails explicitly
|
|
72
|
+
---
|
|
73
|
+
duration_ms: 64.530166
|
|
74
|
+
type: 'test'
|
|
75
|
+
...
|
|
76
|
+
# Subtest: relative helper imports are captured; absolute paths and escaping symlinks are rejected
|
|
77
|
+
ok 12 - relative helper imports are captured; absolute paths and escaping symlinks are rejected
|
|
78
|
+
---
|
|
79
|
+
duration_ms: 30.073458
|
|
80
|
+
type: 'test'
|
|
81
|
+
...
|
|
82
|
+
# Subtest: unsupported mode-dependent extension initialization fails rather than pretending parity
|
|
83
|
+
ok 13 - unsupported mode-dependent extension initialization fails rather than pretending parity
|
|
84
|
+
---
|
|
85
|
+
duration_ms: 1760.34975
|
|
86
|
+
type: 'test'
|
|
87
|
+
...
|
|
88
|
+
# Subtest: --dev reload reads local edits; frozen running sessions remain on their immutable release
|
|
89
|
+
ok 14 - --dev reload reads local edits; frozen running sessions remain on their immutable release
|
|
90
|
+
---
|
|
91
|
+
duration_ms: 72.374875
|
|
92
|
+
type: 'test'
|
|
93
|
+
...
|
|
94
|
+
# Subtest: lock content and materialized-release corruption fail explicitly
|
|
95
|
+
ok 15 - lock content and materialized-release corruption fail explicitly
|
|
96
|
+
---
|
|
97
|
+
duration_ms: 27.148125
|
|
98
|
+
type: 'test'
|
|
99
|
+
...
|
|
100
|
+
# Subtest: installed npm resource packages and dependency files are pinned, portable, restorable and checked before use
|
|
101
|
+
ok 16 - installed npm resource packages and dependency files are pinned, portable, restorable and checked before use
|
|
102
|
+
---
|
|
103
|
+
duration_ms: 4159.828458
|
|
104
|
+
type: 'test'
|
|
105
|
+
...
|
|
106
|
+
# Subtest: MCP tool schemas are locked, tool calls work, and startup/mid-session drift is blocked
|
|
107
|
+
ok 17 - MCP tool schemas are locked, tool calls work, and startup/mid-session drift is blocked
|
|
108
|
+
---
|
|
109
|
+
duration_ms: 249.616667
|
|
110
|
+
type: 'test'
|
|
111
|
+
...
|
|
112
|
+
# Subtest: MCP codemode and deferred exposure use Pi's native discovery tools in every runtime
|
|
113
|
+
ok 18 - MCP codemode and deferred exposure use Pi's native discovery tools in every runtime
|
|
114
|
+
---
|
|
115
|
+
duration_ms: 335.502875
|
|
116
|
+
type: 'test'
|
|
117
|
+
...
|
|
118
|
+
# Subtest: headless UI does not silently approve and explicit SDK UI handlers work
|
|
119
|
+
ok 19 - headless UI does not silently approve and explicit SDK UI handlers work
|
|
120
|
+
---
|
|
121
|
+
duration_ms: 53.980375
|
|
122
|
+
type: 'test'
|
|
123
|
+
...
|
|
124
|
+
# Subtest: an unavailable locked model never falls back to another model
|
|
125
|
+
ok 20 - an unavailable locked model never falls back to another model
|
|
126
|
+
---
|
|
127
|
+
duration_ms: 4.249167
|
|
128
|
+
type: 'test'
|
|
129
|
+
...
|
|
130
|
+
# PLUGIN_STARTUP_LOG
|
|
131
|
+
# Subtest: native OpenAI-compatible HTTP requests use explicit credentials and match across SDK and CLI
|
|
132
|
+
ok 21 - native OpenAI-compatible HTTP requests use explicit credentials and match across SDK and CLI
|
|
133
|
+
---
|
|
134
|
+
duration_ms: 2070.440458
|
|
135
|
+
type: 'test'
|
|
136
|
+
...
|
|
137
|
+
# Subtest: extension startup logging cannot corrupt native RPC stdout
|
|
138
|
+
ok 22 - extension startup logging cannot corrupt native RPC stdout
|
|
139
|
+
---
|
|
140
|
+
duration_ms: 1933.456042
|
|
141
|
+
type: 'test'
|
|
142
|
+
...
|
|
143
|
+
# > pi.lock@0.1.0 prepack
|
|
144
|
+
# > node scripts/prepack.mjs
|
|
145
|
+
# npm warn deprecated node-domexception@1.0.0: Use your platform's native DOMException instead
|
|
146
|
+
# Subtest: native RPC new_session initializes extensions exactly once, matching SDK lifecycle
|
|
147
|
+
ok 23 - native RPC new_session initializes extensions exactly once, matching SDK lifecycle
|
|
148
|
+
---
|
|
149
|
+
duration_ms: 3999.409083
|
|
150
|
+
type: 'test'
|
|
151
|
+
...
|
|
152
|
+
# Subtest: native project context, loose skills, glob selection and resource exclusions are resolved locally
|
|
153
|
+
ok 24 - native project context, loose skills, glob selection and resource exclusions are resolved locally
|
|
154
|
+
---
|
|
155
|
+
duration_ms: 53.543625
|
|
156
|
+
type: 'test'
|
|
157
|
+
...
|
|
158
|
+
# Subtest: development model selection uses native Pi APIs and persists settings for the subsequent lock
|
|
159
|
+
ok 25 - development model selection uses native Pi APIs and persists settings for the subsequent lock
|
|
160
|
+
---
|
|
161
|
+
duration_ms: 63.647584
|
|
162
|
+
type: 'test'
|
|
163
|
+
...
|
|
164
|
+
# Subtest: packed npm release installs into a clean app and runs an unchanged Agent lock
|
|
165
|
+
ok 26 - packed npm release installs into a clean app and runs an unchanged Agent lock
|
|
166
|
+
---
|
|
167
|
+
duration_ms: 16188.030791
|
|
168
|
+
type: 'test'
|
|
169
|
+
...
|
|
170
|
+
1..26
|
|
171
|
+
# tests 26
|
|
172
|
+
# suites 0
|
|
173
|
+
# pass 26
|
|
174
|
+
# fail 0
|
|
175
|
+
# cancelled 0
|
|
176
|
+
# skipped 0
|
|
177
|
+
# todo 0
|
|
178
|
+
# duration_ms 44981.566833
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# pi.lock architecture
|
|
2
|
+
|
|
3
|
+
The project and npm package are named **pi.lock**. The CLI command remains `pi-lock`; the SDK imports from `pi.lock`; the Agent lockfile is also `pi.lock`; local runtime state lives in `.pi-lock/`.
|
|
4
|
+
|
|
5
|
+
## One construction path
|
|
6
|
+
|
|
7
|
+
`src/runtime.ts:createLockedRuntime` is shared by the SDK, Pi InteractiveMode, Pi runRpcMode, and Pi runPrintMode. It constructs Pi's AgentSessionRuntime with an explicit runtime factory. Every new/resumed/forked session goes through that factory. No adapter invokes Pi's default service/resource discovery.
|
|
8
|
+
|
|
9
|
+
`src/bridge.ts` contains the imports that use the pinned Pi 1.3.0 internal layout. Pi exports the ResourceLoader contract, but not its explicit extension-factory initializer or every MCP helper. Those version-specific integration points are isolated instead of modifying the installed Pi package.
|
|
10
|
+
|
|
11
|
+
## Definition and locking
|
|
12
|
+
|
|
13
|
+
The human-facing source remains native `.pi` configuration. `readDefinition` computes a project-scoped inventory, follows declared resources/packages, captures relative extension helpers, and records integrity-pinned local npm dependencies. It rejects escaping paths and symlinks. Package hashes include supporting files, not only extension entry points and SKILL.md.
|
|
14
|
+
|
|
15
|
+
The runtime identity hashes compiled pi-lock JavaScript and the installed production dependency closure, excluding packages containing only TypeScript declarations. npm can hoist a developer's @types/node into a dependency's type-only edge; such non-executable metadata must not change Agent identity. The distributed npm package carries a shrinkwrap generated from the source package-lock so fresh installations restore the runtime dependency graph. The definition and effective startup configuration are included in the lock digest. This prevents a nominally identical Pi version string from silently masking a different installed implementation.
|
|
16
|
+
|
|
17
|
+
`lockProject` builds a candidate snapshot, initializes extensions, captures the effective model/prompt/resource/tool configuration, rechecks that source did not change during initialization, and only then atomically saves pi.lock. Credentials, wall-clock timestamps and absolute deployment paths are not lock identifiers. Absolute resource/workspace paths in diagnostics are normalized to `<AGENT>` and `<WORKSPACE>`.
|
|
18
|
+
|
|
19
|
+
## Execution and immutability
|
|
20
|
+
|
|
21
|
+
Frozen startup checks both the installed implementation and current project inventory against pi.lock. It materializes ordinary files in `.pi-lock/releases/<agentDigest>` and verifies them before executing extensions. That snapshot, not the source tree, supplies lazy Skill/template reads and relative imports. Changing or checking out source files therefore does not switch an existing session's instruction files.
|
|
22
|
+
|
|
23
|
+
Frozen reload verifies source before reinitializing the locked snapshot. Developer reload intentionally creates a new snapshot from current project resources. Provider/model definition changes during development require a restart; ordinary local prompt/Skill/extension reloads preserve Pi's normal `/reload` workflow.
|
|
24
|
+
|
|
25
|
+
SessionManager remains Pi's native JSONL/in-memory tree manager. An initial custom entry binds each transcript to agentDigest. An imported transcript without a pin or a transcript from another version is rejected. No automatic history migration is attempted.
|
|
26
|
+
|
|
27
|
+
## Isolation scope
|
|
28
|
+
|
|
29
|
+
SettingsManager is in memory in frozen mode. Developer writes use a custom storage backend bound to exactly `.pi/settings.json`. ModelRuntime has an explicit credential store and in-memory model store, no models.json path discovery and no remote catalogue refresh. A missing model never falls back to the first available provider.
|
|
30
|
+
|
|
31
|
+
The custom ResourceLoader never walks parent context directories or user-global resource locations. Extension compilation disables filesystem cache and tsconfig path discovery; Pi and TypeBox imports are mapped to the pinned host modules. Additional resources returned by `resources_discover` are rejected unless first declared as project resources and locked.
|
|
32
|
+
|
|
33
|
+
No process-global environment or cwd changes are made by the SDK. The standalone CLI sets Pi's process-level paths before importing Pi so the native TUI's remaining getAgentDir calls target `.pi-lock/state/ui`. This is deliberately confined to the CLI process.
|
|
34
|
+
|
|
35
|
+
This is a reproducibility contract for cooperating host code and compatible extensions. Unrestricted JavaScript, filesystem tools, shell commands and external services retain their normal effects. It is not a sandbox or a proof that arbitrary dynamically generated code has no ambient dependencies.
|
|
36
|
+
|
|
37
|
+
## MCP
|
|
38
|
+
|
|
39
|
+
MCP connections are established before startup validation. The same connected clients supply the recorded catalogue and execute calls. Server instructions, capabilities, and all paginated tool schemas are compared against the lock; catalogues are checked again before requests/tool calls. A server-side change fails explicitly instead of replacing a same-named tool silently.
|
|
40
|
+
|
|
41
|
+
Pi's native MCP tool-definition adapter and its codemode/tool-search extensions provide tool exposure and execution. The project-specific transport/bootstrap layer supplies only explicit configuration, secrets, roots and process environment. Resource-list/read adapters call the same MCP clients. OAuth providers or non-project command runners require explicit integration.
|
|
42
|
+
|
|
43
|
+
## Evidence
|
|
44
|
+
|
|
45
|
+
Acceptance tests compare the actual provider transcript/options across SDK, print, RPC and a PTY-hosted native TUI, including an extension prompt hook and the second tool-result round. Only path relocation, transcript timestamps, durations, response/session identifiers and fake-provider usage metadata are normalized. Another test uses Pi's native OpenAI HTTP implementation and compares actual request bodies and explicit Authorization headers against a local streaming server.
|
|
46
|
+
|
|
47
|
+
Other tests cover polluted HOME/workspace/ancestor settings, source/dependency/snapshot tampering, npm restoration after relocation, Git rollback with live old snapshots, session version mismatches, lifecycle changes, multiple SDK agents, missing interaction callbacks and MCP drift. The final execution record is in acceptance.md.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You are a helpful agent. Use the supplied tools and skills when needed.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Type, fauxProvider, fauxAssistantMessage, fauxToolCall } from "@earendil-works/pi-ai";
|
|
2
|
+
|
|
3
|
+
// Ordinary Pi extension. The provider is offline and deterministic; no API key or network.
|
|
4
|
+
export default function (pi) {
|
|
5
|
+
const fake = fauxProvider({ provider: "pi-lock-demo", api: "openai-completions", models: [{ id: "demo", name: "Offline demo", reasoning: false, contextWindow: 32768, maxTokens: 1024 }] });
|
|
6
|
+
fake.setResponses(Array.from({ length: 100 }, () => (context) => {
|
|
7
|
+
const last = context.messages.at(-1);
|
|
8
|
+
if (last?.role === "toolResult") return fauxAssistantMessage("Agent versioned by pi.lock. " + last.content.filter(c => c.type === "text").map(c => c.text).join(""));
|
|
9
|
+
return fauxAssistantMessage(fauxToolCall("echo", { text: "Hello from the same Pi agent." }, { id: "echo-demo" }), { stopReason: "toolUse" });
|
|
10
|
+
}));
|
|
11
|
+
pi.registerProvider("pi-lock-demo", { api: "openai-completions", baseUrl: "http://localhost:0", apiKey: "offline-demo", streamSimple: fake.provider.streamSimple, models: [{ id: "demo", name: "Offline demo", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 32768, maxTokens: 1024 }] });
|
|
12
|
+
pi.registerTool({ name: "echo", label: "Echo", description: "Echo a text value.", parameters: Type.Object({ text: Type.String() }), execute: async (_id, args) => ({ content: [{ type: "text", text: args.text }], details: {} }) });
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"providers": {
|
|
3
|
+
"pi-lock-demo": {
|
|
4
|
+
"api": "openai-completions",
|
|
5
|
+
"baseUrl": "http://localhost:0",
|
|
6
|
+
"models": [
|
|
7
|
+
{
|
|
8
|
+
"id": "demo",
|
|
9
|
+
"name": "Offline demo",
|
|
10
|
+
"reasoning": false,
|
|
11
|
+
"input": [
|
|
12
|
+
"text"
|
|
13
|
+
],
|
|
14
|
+
"cost": {
|
|
15
|
+
"input": 0,
|
|
16
|
+
"output": 0,
|
|
17
|
+
"cacheRead": 0,
|
|
18
|
+
"cacheWrite": 0
|
|
19
|
+
},
|
|
20
|
+
"contextWindow": 32768,
|
|
21
|
+
"maxTokens": 1024
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|