@alphacoder-v0/pi-loops 0.17.5 → 0.17.6

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
@@ -3,6 +3,20 @@
3
3
  All notable changes to pi-loops are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow SemVer.
5
5
 
6
+ ## [0.17.6] - 2026-09-14
7
+
8
+ ### Added
9
+ - **A pi version floor, and a refusal that names it.** pi-loops needs pi ≥ 0.84.3 — found by
10
+ type-checking `src/` against older releases: 0.80.8 fails on `ctx.thinkingLevel` and
11
+ `session_compact_failed`, 0.84.3 passes clean — and nothing said so. `peerDependencies` is `*`
12
+ because pi's docs ask for that and pi installs with `--legacy-peer-deps`, which reads no range
13
+ anyway; so on an older pi the first sign was a link error, "does not provide an export named
14
+ ModelRuntime", from which nobody could read "your pi is too old". The manifest now points at
15
+ `src/extension-entry.ts`, which imports nothing pi has not exported for a long time, reads
16
+ pi's `VERSION` against `PI_MIN_VERSION` in `src/pi-floor.ts`, and only then imports the
17
+ extension; an old pi prints `pi-loops needs pi 0.84.3 or newer and this is pi 0.80.8: upgrade pi`.
18
+ Both READMEs state the floor and a test keeps them, the manifest and the code on one number.
19
+
6
20
  ## [0.17.5] - 2026-09-14
7
21
 
8
22
  ### Fixed
package/README.md CHANGED
@@ -29,15 +29,16 @@ reaches you is what changed rather than everything that is there.
29
29
 
30
30
  ## Install
31
31
 
32
- Node ≥ 22.6 (pi loads the TypeScript sources directly) and a pi to load it into — 0.85 is what this
33
- is tested against, and older ones are untested rather than refused. Nothing else: pi-loops has no
32
+ Node ≥ 22.6 (pi loads the TypeScript sources directly) and pi ≥ 0.84.3 to load it into — 0.85 is
33
+ what this is tested against, and an older pi is refused at load with a line that names the version
34
+ it needs, rather than a link error naming one missing export. Nothing else: pi-loops has no
34
35
  runtime dependencies. What you do want first is a provider you can actually talk to — run `pi`, send
35
36
  one message, make sure you get an answer. pi-loops runs sub-agents while you are not watching, and
36
37
  the first sign of credentials that do not work should not be an empty inbox tomorrow morning.
37
38
 
38
39
  ```bash
39
40
  pi install npm:@alphacoder-v0/pi-loops # from npm, following new releases
40
- pi install git:github.com/alphacoder-v0/pi-loops@v0.17.5 # or from GitHub, at a pinned tag
41
+ pi install git:github.com/alphacoder-v0/pi-loops@v0.17.6 # or from GitHub, at a pinned tag
41
42
  pi install /path/to/pi-loops # or a local checkout — `pi install .` in this repo
42
43
  pi -e /path/to/pi-loops # or none of them: try it for one run, installing nothing
43
44
  ```
package/README.zh-CN.md CHANGED
@@ -37,7 +37,7 @@ pi-loops host status # 看一眼没有 pi 开着时在跑的自
37
37
 
38
38
  ### 1. 先确认前提
39
39
 
40
- Node ≥ 22.6(pi 直接加载 TypeScript 源码),一个新到带得动扩展 API 的 pi(没有版本下限可查:`peerDependencies` 写的是 `*`,少了哪个 API 是加载时当场报错),以及**一个真的能说上话的 provider**——先跑一次 `pi`,发一句,确认有回答。pi-loops 会在你不看着的时候替你跑子代理;凭据没配好,别等到第二天早上看见空 inbox 才知道。
40
+ Node ≥ 22.6(pi 直接加载 TypeScript 源码),pi ≥ 0.84.3(实际在 0.85 上测;更老的 pi 会在加载时被明确拒绝,报一句写明所需版本的话,而不是一个缺导出的链接错误),以及**一个真的能说上话的 provider**——先跑一次 `pi`,发一句,确认有回答。pi-loops 会在你不看着的时候替你跑子代理;凭据没配好,别等到第二天早上看见空 inbox 才知道。
41
41
 
42
42
  pi-loops 自己没有任何运行时依赖。
43
43
 
@@ -45,7 +45,7 @@ pi-loops 自己没有任何运行时依赖。
45
45
 
46
46
  ```bash
47
47
  pi install npm:@alphacoder-v0/pi-loops # 从 npm 装,跟着新版本走
48
- pi install git:github.com/alphacoder-v0/pi-loops@v0.17.5 # 或者从 GitHub 装,钉住一个 tag
48
+ pi install git:github.com/alphacoder-v0/pi-loops@v0.17.6 # 或者从 GitHub 装,钉住一个 tag
49
49
  pi install /path/to/pi-loops # 或者本地检出;本仓库里就是 pi install .
50
50
  ```
51
51
 
@@ -515,7 +515,8 @@ Output protocol (mandatory):
515
515
  ## 代码结构
516
516
 
517
517
  ```
518
- src/pi-loops.ts 扩展入口:命令、工具、生命周期、状态栏角标、面板
518
+ src/extension-entry.ts pi 加载的入口:先查 pi 版本(src/pi-floor.ts),够了再导入 src/pi-loops.ts
519
+ src/pi-loops.ts 扩展本体:命令、工具、生命周期、状态栏角标、面板
519
520
  src/cli.ts `pi-loops`:会话入口(网页或终端)与 sessions / inspect / export / import / host
520
521
  src/cli-entry.mjs bin
521
522
  src/ts-entry.mjs .mjs 入口怎么加载本包的 .ts:Node 自己剥类型,装在 node_modules 下就借 pi 的 jiti
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alphacoder-v0/pi-loops",
3
- "version": "0.17.5",
3
+ "version": "0.17.6",
4
4
  "description": "An automation layer for pi: cron + stateful loops with a triage inbox, maker/checker verification, dynamic triggers, MCP notifications and tools, lifecycle hooks, session archives. A non-invasive pi extension.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -59,7 +59,7 @@
59
59
  "pi": {
60
60
  "image": "https://raw.githubusercontent.com/alphacoder-v0/pi-loops/main/docs/screenshot.png",
61
61
  "extensions": [
62
- "./src/pi-loops.ts"
62
+ "./src/extension-entry.ts"
63
63
  ],
64
64
  "skills": [
65
65
  "./skills"
@@ -0,0 +1,24 @@
1
+ /**
2
+ * What pi loads (`pi.extensions` in package.json): a version check, then the extension.
3
+ *
4
+ * The check cannot sit in `src/pi-loops.ts`. That file and the modules under it import names pi
5
+ * did not always export (`ModelRuntime`, `readStoredCredential`, `createSyntheticSourceInfo`) and
6
+ * subscribe to events it did not always emit. ESM links a whole import graph before running a line
7
+ * of it, so on an older pi the failure is a link error naming one export — "does not provide an
8
+ * export named ModelRuntime" — and nothing in it says "your pi is too old". This file imports only
9
+ * what every pi has exported for a long time, reads the version, and imports the rest afterwards,
10
+ * so what an old pi prints is the sentence that says which pi it needs.
11
+ *
12
+ * A namespace import on purpose: a name missing from an old pi is then `undefined` rather than a
13
+ * link error, which is exactly the case the check exists for.
14
+ */
15
+ import * as sdk from "@earendil-works/pi-coding-agent";
16
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
17
+ import { piTooOld } from "./pi-floor.ts";
18
+
19
+ export default async function (pi: ExtensionAPI) {
20
+ const refusal = piTooOld(sdk.VERSION);
21
+ if (refusal) throw new Error(refusal);
22
+ const { default: piLoops } = await import("./pi-loops.ts");
23
+ return piLoops(pi);
24
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The oldest pi this extension loads into. `src/extension-entry.ts` reads it before anything else
3
+ * is imported, both READMEs state it, and `test/pi-floor.test.ts` keeps the three the same.
4
+ *
5
+ * 0.84.3 is where the API this code is written against became complete: `session_compact_failed`
6
+ * arrived there, `ModelRuntime` and `readStoredCredential` in 0.80.8, `ctx.thinkingLevel` in
7
+ * between. It was found by type-checking `src/` against older releases rather than by reading
8
+ * changelogs — 0.80.8 fails on the two newest names, 0.84.3 passes clean — and it is the version
9
+ * to re-check the same way when a newer name is used. Nothing enforces it at install time: pi
10
+ * installs packages with `--legacy-peer-deps`, so `peerDependencies` stays `*` as pi's docs ask
11
+ * and the range there would not be read anyway.
12
+ */
13
+ export const PI_MIN_VERSION = "0.84.3";
14
+
15
+ /** `[major, minor, patch]`, or undefined for anything that does not start `N.N.N` (a `v` in front is fine). */
16
+ function parse(version: string): [number, number, number] | undefined {
17
+ const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(version.trim());
18
+ return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : undefined;
19
+ }
20
+
21
+ /**
22
+ * Why this pi cannot load pi-loops, as the one sentence pi prints after "Failed to load extension",
23
+ * or undefined when it can. A pi that reports no version is refused as well: every pi new enough
24
+ * to load this exports `VERSION`, so a missing one is an old one, not an unknown one.
25
+ */
26
+ export function piTooOld(version: string | undefined, min: string = PI_MIN_VERSION): string | undefined {
27
+ const have = version === undefined ? undefined : parse(version);
28
+ const need = parse(min);
29
+ if (!need) throw new Error(`PI_MIN_VERSION is not a version: ${JSON.stringify(min)}`);
30
+ const upgrade = "upgrade pi (`pi update`, or `npm i -g @earendil-works/pi-coding-agent`)";
31
+ if (!have) return `pi-loops needs pi ${min} or newer and this pi reports ${version === undefined ? "no version" : `version ${JSON.stringify(version)}`}: ${upgrade}`;
32
+ for (let i = 0; i < 3; i++) {
33
+ if (have[i] > need[i]) return undefined;
34
+ if (have[i] < need[i]) return `pi-loops needs pi ${min} or newer and this is pi ${version}: ${upgrade}`;
35
+ }
36
+ return undefined;
37
+ }