pi-adaptive-thinking 0.2.1 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-adaptive-thinking",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "private": false,
5
5
  "description": "Pi extension for adaptive reasoning-effort control",
6
6
  "keywords": [
@@ -31,12 +31,6 @@
31
31
  "access": "public",
32
32
  "provenance": true
33
33
  },
34
- "dependencies": {
35
- "proper-lockfile": "^4.1.2"
36
- },
37
- "devDependencies": {
38
- "@types/proper-lockfile": "4.1.4"
39
- },
40
34
  "peerDependencies": {
41
35
  "@earendil-works/pi-ai": "*",
42
36
  "@earendil-works/pi-coding-agent": "*",
@@ -1,7 +1,7 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { open, rm, stat } from "node:fs/promises";
2
3
  import { homedir } from "node:os";
3
4
  import { join } from "node:path";
4
- import lockfile from "proper-lockfile";
5
5
  import type {
6
6
  AgentEndEvent,
7
7
  AgentToolResult,
@@ -147,20 +147,53 @@ const agentDir = () => process.env.PI_CODING_AGENT_DIR ?? join(homedir(), ".pi",
147
147
 
148
148
  const globalSettingsPath = () => join(agentDir(), "settings.json");
149
149
 
150
- const withSettingsLock = async <T>(settingsPath: string, fn: () => Promise<T> | T): Promise<T> => {
151
- mkdirSync(join(settingsPath, ".."), { recursive: true });
152
- const lockPath = `${settingsPath}.adaptive-thinking`;
153
- if (!existsSync(lockPath)) writeFileSync(lockPath, "");
150
+ // ponytail: Node's wx exclusive-create plus an asynchronous fixed-delay retry loop replaces
151
+ // proper-lockfile; the bound matches its previous policy (99 retries at a fixed 20 ms delay).
152
+ // The .lock suffix distinguishes owned lock files from the legacy always-present marker the
153
+ // previous implementation pre-created next to the settings document.
154
+ // Stale recovery assumes the critical section stays synchronous; use a heartbeat lock if it gains
155
+ // asynchronous work.
156
+ const SETTINGS_LOCK_RETRY_DELAY_MS = 20;
157
+ const SETTINGS_LOCK_RETRIES = 99;
158
+ const SETTINGS_LOCK_STALE_MS = 10_000;
159
+
160
+ const sleep = (ms: number): Promise<void> =>
161
+ new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
162
+
163
+ const acquireSettingsLock = async (lockPath: string): Promise<void> => {
164
+ for (let attempt = 0; ; attempt += 1) {
165
+ try {
166
+ const handle = await open(lockPath, "wx");
167
+ await handle.close();
168
+ return;
169
+ } catch (cause) {
170
+ if (!(cause instanceof Error) || !("code" in cause) || cause.code !== "EEXIST") throw cause;
171
+ try {
172
+ const lock = await stat(lockPath);
173
+ if (lock.mtimeMs < Date.now() - SETTINGS_LOCK_STALE_MS) {
174
+ await rm(lockPath, { force: true });
175
+ continue;
176
+ }
177
+ } catch (statCause) {
178
+ if (!(statCause instanceof Error) || !("code" in statCause) || statCause.code !== "ENOENT")
179
+ throw statCause;
180
+ continue;
181
+ }
182
+ if (attempt >= SETTINGS_LOCK_RETRIES) throw cause;
183
+ }
184
+ await sleep(SETTINGS_LOCK_RETRY_DELAY_MS);
185
+ }
186
+ };
154
187
 
155
- const release = await lockfile.lock(lockPath, {
156
- realpath: false,
157
- retries: { retries: 99, factor: 1, minTimeout: 20, maxTimeout: 20 },
158
- });
188
+ const withSettingsLock = async <T>(settingsPath: string, fn: () => T): Promise<T> => {
189
+ mkdirSync(join(settingsPath, ".."), { recursive: true });
190
+ const lockPath = `${settingsPath}.adaptive-thinking.lock`;
191
+ await acquireSettingsLock(lockPath);
159
192
 
160
193
  try {
161
194
  return await fn();
162
195
  } finally {
163
- await release();
196
+ await rm(lockPath, { force: true });
164
197
  }
165
198
  };
166
199
 
@@ -385,6 +418,8 @@ export default function adaptiveThinkingExtension(pi: ExtensionAPI) {
385
418
  onSessionStart: (handler) => pi.on("session_start", handler),
386
419
  onToolCall: (handler) => pi.on("tool_call", handler),
387
420
  onAgentEnd: (handler) => pi.on("agent_end", handler),
421
+ // ponytail: the branches look identical but narrow the union so registerTool's
422
+ // generics infer per concrete ToolDefinition instead of falling back to defaults.
388
423
  registerTool: (tool) => {
389
424
  if (isAdaptiveThinkingSetThinkingLevelTool(tool)) pi.registerTool(tool);
390
425
  else pi.registerTool(tool);
package/src/config.ts CHANGED
@@ -77,7 +77,3 @@ export const parseAdaptiveThinkingConfig = (
77
77
 
78
78
  return { config, usedDeprecatedSystemPrompt: usesSystemPrompt };
79
79
  };
80
-
81
- /** Parses configuration at the public configuration seam. */
82
- export const parseConfig = (input: JsonValue | undefined): AdaptiveThinkingConfig =>
83
- parseAdaptiveThinkingConfig(input).config;