@ai-agentx/pi-men 0.84.4

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 ADDED
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ### Added
6
+
7
+ - Initial release of the `pi-men` CLI: the pi coding agent with the built-in four-layer memory engine enabled, using `~/.pi-men` as its independent data directory.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mario Zechner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # pi-men
2
+
3
+ A coding agent with built-in four-layer memory, built on the [pi](https://github.com/earendil-works/pi) base.
4
+
5
+ `pi-men` works exactly like `pi` — same interaction, same commands, same modes — plus a memory engine that captures your conversations and work, distills them into reusable memory, and re-injects the relevant parts into future sessions. The more you use it, the less you have to re-explain yourself.
6
+
7
+ ## Quick start
8
+
9
+ ```bash
10
+ npm install -g @ai-agentx/pi-men
11
+ pi-men # first run asks you to configure a model/API key, like pi
12
+ ```
13
+
14
+ All data lives in `~/.pi-men` (independent of any existing pi installation).
15
+
16
+ ## What you get
17
+
18
+ ### A full coding agent
19
+
20
+ pi-men inherits the complete pi base: multi-provider models (Anthropic, OpenAI, Google, Bedrock, and more, plus a built-in llama.cpp provider for local models), the standard tool set (read, bash, edit, write, grep, find, ls), interactive TUI with session tree and compaction, print/RPC/JSON modes for scripting, an extension API, skills, prompt templates, and themes.
21
+
22
+ ### Four-layer memory (the pi-men part)
23
+
24
+ | Layer | What it stores | Where |
25
+ |---|---|---|
26
+ | L0 Conversation | every turn, full fidelity | SQLite + daily JSONL |
27
+ | L1 Atom | structured memories: preferences, events, instructions, project facts, tasks, methods, artifacts | SQLite |
28
+ | L2 Scenario | narrative scene blocks that consolidate related memories | `scene_blocks/*.md` |
29
+ | L3 Core | a long-term persona / operating doctrine distilled from scenes | `persona.md` |
30
+
31
+ **Capture is automatic.** Finished turns are recorded, and every few turns (or after an idle timeout) a background pipeline distills them into L1 memories — with LLM-based deduplication that can store, update, merge, or skip against what it already knows. L2 and L3 consolidate periodically as new memories accumulate. The pipeline never blocks your turns.
32
+
33
+ **Recall is hybrid and cache-friendly.** Each request gets the relevant memories injected as a `<relevant-memories>` block on the user message, while a stable persona block is appended to the system prompt (so provider prompt caches stay valid). Search fuses SQLite FTS5 keyword scoring with vector similarity (when an embedding endpoint is configured) using reciprocal rank fusion; it works in one language-agnostic shot — Chinese and English both covered.
34
+
35
+ **You can always dig deeper** with two built-in tools the agent can call: `memory_search` (structured memories) and `conversation_search` (raw past conversations). Or save something explicitly with `/remember <text>`; `/memory` shows store status.
36
+
37
+ **It is optional.** Disable memory with one setting and pi-men behaves as a plain agent; the store stays on disk untouched.
38
+
39
+ ## Commands and tools
40
+
41
+ | Item | Type | Purpose |
42
+ |---|---|---|
43
+ | `/remember <text>` | command | save a long-term memory right now |
44
+ | `/memory` | command | show memory store status (counts, last pipeline runs) |
45
+ | `memory_search` | tool | agent searches structured memories (L1) |
46
+ | `conversation_search` | tool | agent searches raw past conversations (L0) |
47
+
48
+ ## Storage
49
+
50
+ ```
51
+ ~/.pi-men/
52
+ auth.json provider credentials
53
+ settings.json settings (pi format)
54
+ sessions/ session transcripts
55
+ memory/
56
+ memory.db SQLite: conversations, memories, FTS + vector indexes
57
+ conversations/ append-only daily JSONL (source of truth)
58
+ records/ append-only audit of every memory write/update/merge
59
+ scene_blocks/ L2 scene blocks + scene_index.json
60
+ persona.md L3 persona / operating doctrine
61
+ .backup/ persona backups
62
+ .metadata/ pipeline checkpoint (survives restarts)
63
+ ```
64
+
65
+ ## Configuration
66
+
67
+ Disable memory in `~/.pi-men/settings.json`:
68
+
69
+ ```json
70
+ { "memory": { "enabled": false } }
71
+ ```
72
+
73
+ Tune the engine in `~/.pi-men/memory.json` (all keys optional; defaults shown):
74
+
75
+ ```json
76
+ {
77
+ "promptMode": "code",
78
+ "capture": { "enabled": true },
79
+ "extraction": { "enabled": true, "enableDedup": true },
80
+ "pipeline": { "everyNConversations": 5, "l1IdleTimeoutSeconds": 600 },
81
+ "recall": { "enabled": true, "strategy": "hybrid", "maxResults": 5, "scoreThreshold": 0.3 },
82
+ "embedding": {
83
+ "provider": "none",
84
+ "baseUrl": "https://api.openai.com/v1",
85
+ "apiKey": "sk-...",
86
+ "model": "text-embedding-3-small",
87
+ "dimensions": 1536
88
+ }
89
+ }
90
+ ```
91
+
92
+ - `promptMode`: `"code"` extracts work memories (facts / tasks / methods / artifacts); `"chat"` extracts personal memories (persona / episodic / instructions).
93
+ - `embedding.provider`: `"none"` keeps recall keyword-only with zero network calls; `"openai"` enables semantic recall against any OpenAI-compatible `/embeddings` endpoint.
94
+
95
+ ## Notes
96
+
97
+ - Memory distillation uses your configured model and consumes tokens in the background. If you want the agent without the cost, set `memory.enabled: false` or `extraction.enabled: false`.
98
+ - Memory data never leaves your machine; the only network calls are to your model/embedding providers.
99
+
100
+ ## License
101
+
102
+ MIT — see [LICENSE](./LICENSE). Built on [pi](https://github.com/earendil-works/pi) by Earendil Works.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"","sourcesContent":["#!/usr/bin/env node\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { main } from \"@earendil-works/pi-coding-agent\";\nimport { configureHttpDispatcher } from \"./http-dispatcher.ts\";\n\n// Mirror the pi CLI startup so behavior is identical.\nprocess.title = \"pi-men\";\nprocess.env.PI_CODING_AGENT = \"true\";\nprocess.env.AI_AGENT = \"pi\";\nprocess.emitWarning = (() => {}) as typeof process.emitWarning;\n\n// Configure undici's global dispatcher before provider SDKs issue requests.\nconfigureHttpDispatcher();\n\n// pi-men is an independent product: config, auth, sessions, and memory live\n// under ~/.pi-men unless the user points PI_CODING_AGENT_DIR elsewhere.\nprocess.env.PI_CODING_AGENT_DIR ??= join(homedir(), \".pi-men\");\n\nawait main(process.argv.slice(2));\n"]}
package/dist/cli.js ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { main } from "@earendil-works/pi-coding-agent";
5
+ import { configureHttpDispatcher } from "./http-dispatcher.js";
6
+ // Mirror the pi CLI startup so behavior is identical.
7
+ process.title = "pi-men";
8
+ process.env.PI_CODING_AGENT = "true";
9
+ process.env.AI_AGENT = "pi";
10
+ process.emitWarning = (() => { });
11
+ // Configure undici's global dispatcher before provider SDKs issue requests.
12
+ configureHttpDispatcher();
13
+ // pi-men is an independent product: config, auth, sessions, and memory live
14
+ // under ~/.pi-men unless the user points PI_CODING_AGENT_DIR elsewhere.
15
+ process.env.PI_CODING_AGENT_DIR ??= join(homedir(), ".pi-men");
16
+ await main(process.argv.slice(2));
17
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,iCAAiC,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,sDAAsD;AACtD,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;AACzB,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,MAAM,CAAC;AACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC5B,OAAO,CAAC,WAAW,GAAG,CAAC,GAAG,EAAE,CAAC,EAAC,CAAC,CAA+B,CAAC;AAE/D,4EAA4E;AAC5E,uBAAuB,EAAE,CAAC;AAE1B,4EAA4E;AAC5E,wEAAwE;AACxE,OAAO,CAAC,GAAG,CAAC,mBAAmB,KAAK,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAE/D,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { main } from \"@earendil-works/pi-coding-agent\";\nimport { configureHttpDispatcher } from \"./http-dispatcher.ts\";\n\n// Mirror the pi CLI startup so behavior is identical.\nprocess.title = \"pi-men\";\nprocess.env.PI_CODING_AGENT = \"true\";\nprocess.env.AI_AGENT = \"pi\";\nprocess.emitWarning = (() => {}) as typeof process.emitWarning;\n\n// Configure undici's global dispatcher before provider SDKs issue requests.\nconfigureHttpDispatcher();\n\n// pi-men is an independent product: config, auth, sessions, and memory live\n// under ~/.pi-men unless the user points PI_CODING_AGENT_DIR elsewhere.\nprocess.env.PI_CODING_AGENT_DIR ??= join(homedir(), \".pi-men\");\n\nawait main(process.argv.slice(2));\n"]}
@@ -0,0 +1,2 @@
1
+ export declare function configureHttpDispatcher(timeoutMs?: number): void;
2
+ //# sourceMappingURL=http-dispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-dispatcher.d.ts","sourceRoot":"","sources":["../src/http-dispatcher.ts"],"names":[],"mappings":"AA2CA,wBAAgB,uBAAuB,CAAC,SAAS,GAAE,MAAqC,GAAG,IAAI,CA4B9F","sourcesContent":["import { EventEmitter } from \"node:events\";\nimport * as undici from \"undici\";\n\n// Ported from the pi CLI startup (coding-agent core/http-dispatcher.ts) so the\n// published pi-men package is self-contained: provider requests get long idle\n// timeouts, HTTP(S)_PROXY env support, and undici error handling identical to pi.\n\nconst DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;\n// Node's 250ms default can terminate valid connection attempts on high-latency routes.\nconst DEFAULT_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT_MS = 2_000;\n\nconst originalGlobalFetch = globalThis.fetch;\nlet installedGlobalFetch: typeof globalThis.fetch | undefined;\n\nconst ignoreUndiciDispatcherError = (_error: unknown): void => {};\n\n// Undici can emit an internal Client \"error\" while terminating a mid-stream\n// fetch body. The body stream still rejects through reader.read(); this listener\n// only prevents EventEmitter's unhandled \"error\" special case from crashing pi-men.\nfunction withUndiciErrorListener<T extends undici.Dispatcher>(dispatcher: T): T {\n\tif (dispatcher instanceof EventEmitter) {\n\t\tEventEmitter.prototype.on.call(dispatcher, \"error\", ignoreUndiciDispatcherError);\n\t}\n\treturn dispatcher;\n}\n\nfunction createUndiciClient(origin: string | URL, options: object): undici.Dispatcher {\n\treturn withUndiciErrorListener(new undici.Client(origin, options as undici.Client.Options));\n}\n\nfunction createUndiciOriginDispatcher(origin: string | URL, options: object): undici.Dispatcher {\n\tconst dispatcherOptions = options as undici.Pool.Options;\n\tif (dispatcherOptions.connections === 1) {\n\t\treturn createUndiciClient(origin, dispatcherOptions);\n\t}\n\treturn withUndiciErrorListener(\n\t\tnew undici.Pool(origin, {\n\t\t\t...dispatcherOptions,\n\t\t\tfactory: createUndiciClient,\n\t\t}),\n\t);\n}\n\nexport function configureHttpDispatcher(timeoutMs: number = DEFAULT_HTTP_IDLE_TIMEOUT_MS): void {\n\tconst dispatcher = withUndiciErrorListener(\n\t\tnew undici.EnvHttpProxyAgent({\n\t\t\tallowH2: false,\n\t\t\t// Keep HTTP origins on CONNECT tunnels as they were before Undici 8.7.\n\t\t\tproxyTunnel: true,\n\t\t\tbodyTimeout: timeoutMs,\n\t\t\tconnect: {\n\t\t\t\tautoSelectFamilyAttemptTimeout: DEFAULT_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT_MS,\n\t\t\t},\n\t\t\theadersTimeout: timeoutMs,\n\t\t\tclientFactory: createUndiciClient,\n\t\t\tfactory: createUndiciOriginDispatcher,\n\t\t}),\n\t);\n\tundici.setGlobalDispatcher(dispatcher);\n\t// Keep fetch and the dispatcher on the same undici implementation. Node 26.0's\n\t// bundled fetch can otherwise consume compressed responses through npm undici's\n\t// dispatcher without decompressing them, causing response.json() failures.\n\t// If a caller replaced fetch after module load, preserve that deliberate override.\n\tconst shouldInstallGlobals =\n\t\tinstalledGlobalFetch === undefined\n\t\t\t? globalThis.fetch === originalGlobalFetch\n\t\t\t: globalThis.fetch === installedGlobalFetch;\n\tif (shouldInstallGlobals) {\n\t\tundici.install?.();\n\t\tinstalledGlobalFetch = globalThis.fetch;\n\t}\n}\n"]}
@@ -0,0 +1,60 @@
1
+ import { EventEmitter } from "node:events";
2
+ import * as undici from "undici";
3
+ // Ported from the pi CLI startup (coding-agent core/http-dispatcher.ts) so the
4
+ // published pi-men package is self-contained: provider requests get long idle
5
+ // timeouts, HTTP(S)_PROXY env support, and undici error handling identical to pi.
6
+ const DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;
7
+ // Node's 250ms default can terminate valid connection attempts on high-latency routes.
8
+ const DEFAULT_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT_MS = 2_000;
9
+ const originalGlobalFetch = globalThis.fetch;
10
+ let installedGlobalFetch;
11
+ const ignoreUndiciDispatcherError = (_error) => { };
12
+ // Undici can emit an internal Client "error" while terminating a mid-stream
13
+ // fetch body. The body stream still rejects through reader.read(); this listener
14
+ // only prevents EventEmitter's unhandled "error" special case from crashing pi-men.
15
+ function withUndiciErrorListener(dispatcher) {
16
+ if (dispatcher instanceof EventEmitter) {
17
+ EventEmitter.prototype.on.call(dispatcher, "error", ignoreUndiciDispatcherError);
18
+ }
19
+ return dispatcher;
20
+ }
21
+ function createUndiciClient(origin, options) {
22
+ return withUndiciErrorListener(new undici.Client(origin, options));
23
+ }
24
+ function createUndiciOriginDispatcher(origin, options) {
25
+ const dispatcherOptions = options;
26
+ if (dispatcherOptions.connections === 1) {
27
+ return createUndiciClient(origin, dispatcherOptions);
28
+ }
29
+ return withUndiciErrorListener(new undici.Pool(origin, {
30
+ ...dispatcherOptions,
31
+ factory: createUndiciClient,
32
+ }));
33
+ }
34
+ export function configureHttpDispatcher(timeoutMs = DEFAULT_HTTP_IDLE_TIMEOUT_MS) {
35
+ const dispatcher = withUndiciErrorListener(new undici.EnvHttpProxyAgent({
36
+ allowH2: false,
37
+ // Keep HTTP origins on CONNECT tunnels as they were before Undici 8.7.
38
+ proxyTunnel: true,
39
+ bodyTimeout: timeoutMs,
40
+ connect: {
41
+ autoSelectFamilyAttemptTimeout: DEFAULT_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT_MS,
42
+ },
43
+ headersTimeout: timeoutMs,
44
+ clientFactory: createUndiciClient,
45
+ factory: createUndiciOriginDispatcher,
46
+ }));
47
+ undici.setGlobalDispatcher(dispatcher);
48
+ // Keep fetch and the dispatcher on the same undici implementation. Node 26.0's
49
+ // bundled fetch can otherwise consume compressed responses through npm undici's
50
+ // dispatcher without decompressing them, causing response.json() failures.
51
+ // If a caller replaced fetch after module load, preserve that deliberate override.
52
+ const shouldInstallGlobals = installedGlobalFetch === undefined
53
+ ? globalThis.fetch === originalGlobalFetch
54
+ : globalThis.fetch === installedGlobalFetch;
55
+ if (shouldInstallGlobals) {
56
+ undici.install?.();
57
+ installedGlobalFetch = globalThis.fetch;
58
+ }
59
+ }
60
+ //# sourceMappingURL=http-dispatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-dispatcher.js","sourceRoot":"","sources":["../src/http-dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,+EAA+E;AAC/E,8EAA8E;AAC9E,kFAAkF;AAElF,MAAM,4BAA4B,GAAG,OAAO,CAAC;AAC7C,uFAAuF;AACvF,MAAM,6CAA6C,GAAG,KAAK,CAAC;AAE5D,MAAM,mBAAmB,GAAG,UAAU,CAAC,KAAK,CAAC;AAC7C,IAAI,oBAAyD,CAAC;AAE9D,MAAM,2BAA2B,GAAG,CAAC,MAAe,EAAQ,EAAE,CAAC,EAAC,CAAC,CAAC;AAElE,4EAA4E;AAC5E,iFAAiF;AACjF,oFAAoF;AACpF,SAAS,uBAAuB,CAA8B,UAAa,EAAK;IAC/E,IAAI,UAAU,YAAY,YAAY,EAAE,CAAC;QACxC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,2BAA2B,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,UAAU,CAAC;AAAA,CAClB;AAED,SAAS,kBAAkB,CAAC,MAAoB,EAAE,OAAe,EAAqB;IACrF,OAAO,uBAAuB,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAgC,CAAC,CAAC,CAAC;AAAA,CAC5F;AAED,SAAS,4BAA4B,CAAC,MAAoB,EAAE,OAAe,EAAqB;IAC/F,MAAM,iBAAiB,GAAG,OAA8B,CAAC;IACzD,IAAI,iBAAiB,CAAC,WAAW,KAAK,CAAC,EAAE,CAAC;QACzC,OAAO,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,uBAAuB,CAC7B,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE;QACvB,GAAG,iBAAiB;QACpB,OAAO,EAAE,kBAAkB;KAC3B,CAAC,CACF,CAAC;AAAA,CACF;AAED,MAAM,UAAU,uBAAuB,CAAC,SAAS,GAAW,4BAA4B,EAAQ;IAC/F,MAAM,UAAU,GAAG,uBAAuB,CACzC,IAAI,MAAM,CAAC,iBAAiB,CAAC;QAC5B,OAAO,EAAE,KAAK;QACd,uEAAuE;QACvE,WAAW,EAAE,IAAI;QACjB,WAAW,EAAE,SAAS;QACtB,OAAO,EAAE;YACR,8BAA8B,EAAE,6CAA6C;SAC7E;QACD,cAAc,EAAE,SAAS;QACzB,aAAa,EAAE,kBAAkB;QACjC,OAAO,EAAE,4BAA4B;KACrC,CAAC,CACF,CAAC;IACF,MAAM,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC;IACvC,+EAA+E;IAC/E,gFAAgF;IAChF,2EAA2E;IAC3E,mFAAmF;IACnF,MAAM,oBAAoB,GACzB,oBAAoB,KAAK,SAAS;QACjC,CAAC,CAAC,UAAU,CAAC,KAAK,KAAK,mBAAmB;QAC1C,CAAC,CAAC,UAAU,CAAC,KAAK,KAAK,oBAAoB,CAAC;IAC9C,IAAI,oBAAoB,EAAE,CAAC;QAC1B,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QACnB,oBAAoB,GAAG,UAAU,CAAC,KAAK,CAAC;IACzC,CAAC;AAAA,CACD","sourcesContent":["import { EventEmitter } from \"node:events\";\nimport * as undici from \"undici\";\n\n// Ported from the pi CLI startup (coding-agent core/http-dispatcher.ts) so the\n// published pi-men package is self-contained: provider requests get long idle\n// timeouts, HTTP(S)_PROXY env support, and undici error handling identical to pi.\n\nconst DEFAULT_HTTP_IDLE_TIMEOUT_MS = 300_000;\n// Node's 250ms default can terminate valid connection attempts on high-latency routes.\nconst DEFAULT_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT_MS = 2_000;\n\nconst originalGlobalFetch = globalThis.fetch;\nlet installedGlobalFetch: typeof globalThis.fetch | undefined;\n\nconst ignoreUndiciDispatcherError = (_error: unknown): void => {};\n\n// Undici can emit an internal Client \"error\" while terminating a mid-stream\n// fetch body. The body stream still rejects through reader.read(); this listener\n// only prevents EventEmitter's unhandled \"error\" special case from crashing pi-men.\nfunction withUndiciErrorListener<T extends undici.Dispatcher>(dispatcher: T): T {\n\tif (dispatcher instanceof EventEmitter) {\n\t\tEventEmitter.prototype.on.call(dispatcher, \"error\", ignoreUndiciDispatcherError);\n\t}\n\treturn dispatcher;\n}\n\nfunction createUndiciClient(origin: string | URL, options: object): undici.Dispatcher {\n\treturn withUndiciErrorListener(new undici.Client(origin, options as undici.Client.Options));\n}\n\nfunction createUndiciOriginDispatcher(origin: string | URL, options: object): undici.Dispatcher {\n\tconst dispatcherOptions = options as undici.Pool.Options;\n\tif (dispatcherOptions.connections === 1) {\n\t\treturn createUndiciClient(origin, dispatcherOptions);\n\t}\n\treturn withUndiciErrorListener(\n\t\tnew undici.Pool(origin, {\n\t\t\t...dispatcherOptions,\n\t\t\tfactory: createUndiciClient,\n\t\t}),\n\t);\n}\n\nexport function configureHttpDispatcher(timeoutMs: number = DEFAULT_HTTP_IDLE_TIMEOUT_MS): void {\n\tconst dispatcher = withUndiciErrorListener(\n\t\tnew undici.EnvHttpProxyAgent({\n\t\t\tallowH2: false,\n\t\t\t// Keep HTTP origins on CONNECT tunnels as they were before Undici 8.7.\n\t\t\tproxyTunnel: true,\n\t\t\tbodyTimeout: timeoutMs,\n\t\t\tconnect: {\n\t\t\t\tautoSelectFamilyAttemptTimeout: DEFAULT_AUTO_SELECT_FAMILY_ATTEMPT_TIMEOUT_MS,\n\t\t\t},\n\t\t\theadersTimeout: timeoutMs,\n\t\t\tclientFactory: createUndiciClient,\n\t\t\tfactory: createUndiciOriginDispatcher,\n\t\t}),\n\t);\n\tundici.setGlobalDispatcher(dispatcher);\n\t// Keep fetch and the dispatcher on the same undici implementation. Node 26.0's\n\t// bundled fetch can otherwise consume compressed responses through npm undici's\n\t// dispatcher without decompressing them, causing response.json() failures.\n\t// If a caller replaced fetch after module load, preserve that deliberate override.\n\tconst shouldInstallGlobals =\n\t\tinstalledGlobalFetch === undefined\n\t\t\t? globalThis.fetch === originalGlobalFetch\n\t\t\t: globalThis.fetch === installedGlobalFetch;\n\tif (shouldInstallGlobals) {\n\t\tundici.install?.();\n\t\tinstalledGlobalFetch = globalThis.fetch;\n\t}\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@ai-agentx/pi-men",
3
+ "version": "0.84.4",
4
+ "description": "pi-men: a coding agent with built-in four-layer memory (conversations, atomic memories, scenes, persona), built on the pi base",
5
+ "type": "module",
6
+ "main": "./dist/cli.js",
7
+ "bin": {
8
+ "pi-men": "./dist/cli.js"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "README.md",
16
+ "CHANGELOG.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "clean": "node -e \"import('node:fs/promises').then(fs=>fs.rm('dist',{recursive:true,force:true}))\"",
21
+ "build": "tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js",
22
+ "prepublishOnly": "npm run clean && npm run build"
23
+ },
24
+ "keywords": [
25
+ "coding-agent",
26
+ "agent",
27
+ "memory",
28
+ "cli"
29
+ ],
30
+ "author": "Earendil Works",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/earendil-works/pi.git",
35
+ "directory": "packages/pi-men-cli"
36
+ },
37
+ "engines": {
38
+ "node": ">=22.19.0"
39
+ },
40
+ "dependencies": {
41
+ "@earendil-works/pi-coding-agent": "^0.84.4",
42
+ "undici": "8.9.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "22.19.19"
46
+ }
47
+ }