diff --git a/README.md b/README.md index 730033651..04fc9f476 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ To learn more about Pi: | **[@earendil-works/chord](packages/chord)** | Standalone application-composition runtime for services, replicated state, RPC, and plugins | | **[@earendil-works/pi-telemetry](packages/telemetry)** | Vendor-neutral telemetry contracts, reference adapter, conformance tests, and typed schemas | | **[@earendil-works/pi-ai](packages/ai)** | Unified multi-provider LLM API (OpenAI, Anthropic, Google, etc.) | +| **[@earendil-works/pi-durable](packages/durable)** | Durable conversation, task, and document runtime | | **[@earendil-works/pi-agent-core](packages/agent)** | Agent runtime with tool calling and state management | | **[@earendil-works/pi-coding-agent](packages/coding-agent)** | Interactive coding agent CLI | | **[@earendil-works/pi-tui](packages/tui)** | Terminal UI library with differential rendering | diff --git a/package-lock.json b/package-lock.json index 44ead3b0b..fec9dbd0c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -737,6 +737,10 @@ "resolved": "packages/coding-agent", "link": true }, + "node_modules/@earendil-works/pi-durable": { + "resolved": "packages/durable", + "link": true + }, "node_modules/@earendil-works/pi-evals": { "resolved": "packages/evals", "link": true @@ -5511,6 +5515,22 @@ "extraneous": true, "license": "MIT" }, + "packages/durable": { + "name": "@earendil-works/pi-durable", + "version": "0.85.1", + "license": "MIT", + "dependencies": { + "@earendil-works/chord": "^0.85.1", + "@earendil-works/pi-ai": "^0.85.1" + }, + "devDependencies": { + "shx": "0.4.0", + "vitest": "4.1.9" + }, + "engines": { + "node": ">=22.19.0" + } + }, "packages/evals": { "name": "@earendil-works/pi-evals", "version": "0.85.1", diff --git a/package.json b/package.json index 3e736d361..4b311456e 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ ], "scripts": { "clean": "npm run clean --workspaces", - "build": "cd packages/chord && npm run build && cd ../tui && npm run build && cd ../telemetry && npm run build && cd ../ai && npm run build && cd ../agent && npm run build && cd ../session-backends/sqlite-node && npm run build && cd ../../protocol && npm run build && cd ../client && npm run build && cd ../server && npm run build && cd ../coding-agent && npm run build", - "build:offline": "cd packages/chord && npm run build && cd ../tui && npm run build && cd ../telemetry && npm run build && cd ../ai && npm run build:offline && cd ../agent && npm run build && cd ../session-backends/sqlite-node && npm run build && cd ../../protocol && npm run build && cd ../client && npm run build && cd ../server && npm run build && cd ../coding-agent && npm run build", + "build": "cd packages/chord && npm run build && cd ../tui && npm run build && cd ../telemetry && npm run build && cd ../ai && npm run build && cd ../durable && npm run build && cd ../agent && npm run build && cd ../session-backends/sqlite-node && npm run build && cd ../../protocol && npm run build && cd ../client && npm run build && cd ../server && npm run build && cd ../coding-agent && npm run build", + "build:offline": "cd packages/chord && npm run build && cd ../tui && npm run build && cd ../telemetry && npm run build && cd ../ai && npm run build:offline && cd ../durable && npm run build && cd ../agent && npm run build && cd ../session-backends/sqlite-node && npm run build && cd ../../protocol && npm run build && cd ../client && npm run build && cd ../server && npm run build && cd ../coding-agent && npm run build", "build:native:darwin": "npm --prefix packages/tui run build:native:darwin", "build:native:linux": "npm --prefix packages/tui run build:native:linux", "build:native:win32": "npm --prefix packages/tui run build:native:win32", diff --git a/packages/coding-agent/src/experimental/services/README.md b/packages/coding-agent/src/experimental/services/README.md index 1f41cd501..2660359f0 100644 --- a/packages/coding-agent/src/experimental/services/README.md +++ b/packages/coding-agent/src/experimental/services/README.md @@ -22,4 +22,4 @@ A foreground server uses repeatable `-e` options to establish its default Sessio A host may reload selected facets when their declared service shape is unchanged; retained consumers keep the same service facades while replacement implementations and singleton snapshots are installed behind them. Synchronous setup-time `env.provide()`, `env.provideMany()`, `env.use()`, and `env.observe()` calls produce the internal dependency graph; setup does not repeat a declarative dependency list, and service handles remain disconnected until the complete graph validates. Providers activate before consumers, observations connect with their consuming facet, and facet replacement or host shutdown disposes affected lifecycles in reverse dependency order. Server and Session service tokens are non-local and automatically published by their providing host. Presentation-only hookpoints such as `SlashCommands` are explicitly local and never enter an RPC catalogue. -A facet always calls unqualified `env.use()` or `env.observe()`. The host resolves each token across facet-provided and connected services. The example plugin package contributes `/hello` through its TUI facet. `examples/plugins/pi-example-plugin/` is an actual `@earendil-works/pi-example-plugin` package: repeatable `-e` options select plugin packages as server defaults or for one Session branch, Chord discovers and builds their conventional facets, and the attached presentation receives matching TUI artifacts through `PresentationPlugins`. Other host facets from the same plugin remain separate bundle entries rather than one aggregate plugin object. Transport bindings remain internal machinery rather than part of the facet environment. `ExperimentalClientTui` currently owns terminal rendering, state subscriptions, navigation, and action dispatch directly. The server provider remains directly assembled until its complete host environment exists. The canvas, diff-review, and task-observation examples in `packages/agent/docs/pico-v5-chord-usage.md` are extension patterns, not built-in coding-agent services. Private references, trace carriers, and flow control remain protocol/host infrastructure slices rather than presentation service tokens. +A facet always calls unqualified `env.use()` or `env.observe()`. The host resolves each token across facet-provided and connected services. The example plugin package contributes `/hello` through its TUI facet. `examples/plugins/pi-example-plugin/` is an actual `@earendil-works/pi-example-plugin` package: repeatable `-e` options select plugin packages as server defaults or for one Session branch, Chord discovers and builds their conventional facets, and the attached presentation receives matching TUI artifacts through `PresentationPlugins`. Other host facets from the same plugin remain separate bundle entries rather than one aggregate plugin object. Transport bindings remain internal machinery rather than part of the facet environment. `ExperimentalClientTui` currently owns terminal rendering, state subscriptions, navigation, and action dispatch directly. The server provider remains directly assembled until its complete host environment exists. The canvas, diff-review, and task-observation examples in `packages/durable/docs/pico-v5-chord-usage.md` are extension patterns, not built-in coding-agent services. Private references, trace carriers, and flow control remain protocol/host infrastructure slices rather than presentation service tokens. diff --git a/packages/durable/CHANGELOG.md b/packages/durable/CHANGELOG.md new file mode 100644 index 000000000..524d0da20 --- /dev/null +++ b/packages/durable/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog + +## [Unreleased] + +### Added + +- Added the initial Pico durable record contracts and detached in-memory storage implementation. diff --git a/packages/durable/README.md b/packages/durable/README.md new file mode 100644 index 000000000..a4788bc93 --- /dev/null +++ b/packages/durable/README.md @@ -0,0 +1,15 @@ +# @earendil-works/pi-durable + +Durable conversation, task, and document runtime for Pi. + +This package contains the Pico runtime. Its current public API provides the durable record contracts and detached in-memory storage implementation: + +```ts +import { MemoryStorage, ROOT_CONVERSATION_ID } from "@earendil-works/pi-durable"; +``` + +The normative design and implementation sequence are in: + +- [`docs/pico-v5.md`](docs/pico-v5.md) +- [`docs/pico-v5-handoff.md`](docs/pico-v5-handoff.md) +- [`docs/pico-v5-chord-usage.md`](docs/pico-v5-chord-usage.md) diff --git a/packages/agent/docs/pico-v5-chord-usage.md b/packages/durable/docs/pico-v5-chord-usage.md similarity index 100% rename from packages/agent/docs/pico-v5-chord-usage.md rename to packages/durable/docs/pico-v5-chord-usage.md diff --git a/packages/agent/docs/pico-v5-handoff.md b/packages/durable/docs/pico-v5-handoff.md similarity index 99% rename from packages/agent/docs/pico-v5-handoff.md rename to packages/durable/docs/pico-v5-handoff.md index a537e664a..9aa2e3f22 100644 --- a/packages/agent/docs/pico-v5-handoff.md +++ b/packages/durable/docs/pico-v5-handoff.md @@ -1,6 +1,6 @@ # Pico5 implementation handoff -`packages/agent/docs/pico-v5.md` is normative. Implement this list in order. +`packages/durable/docs/pico-v5.md` is normative. Implement this list in order. After every package: run its tests, run `npm run check`, and stop for user review. Do not redesign later packages while implementing the current one. diff --git a/packages/agent/docs/pico-v5.md b/packages/durable/docs/pico-v5.md similarity index 100% rename from packages/agent/docs/pico-v5.md rename to packages/durable/docs/pico-v5.md diff --git a/packages/durable/package.json b/packages/durable/package.json new file mode 100644 index 000000000..6fcfd5818 --- /dev/null +++ b/packages/durable/package.json @@ -0,0 +1,52 @@ +{ + "name": "@earendil-works/pi-durable", + "version": "0.85.1", + "description": "Durable conversation, task, and document runtime for Pi", + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "source": "./src/index.ts", + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./package.json": "./package.json" + }, + "sideEffects": false, + "files": [ + "dist", + "docs", + "README.md" + ], + "scripts": { + "clean": "shx rm -rf dist", + "build": "tsgo -p tsconfig.build.json", + "test": "vitest --run", + "prepublishOnly": "npm run clean && npm run build" + }, + "keywords": [ + "durable-execution", + "agents", + "conversations", + "tasks" + ], + "author": "Earendil Works", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/earendil-works/pi.git", + "directory": "packages/durable" + }, + "engines": { + "node": ">=22.19.0" + }, + "dependencies": { + "@earendil-works/chord": "^0.85.1", + "@earendil-works/pi-ai": "^0.85.1" + }, + "devDependencies": { + "shx": "0.4.0", + "vitest": "4.1.9" + } +} diff --git a/packages/agent/src/pico/index.ts b/packages/durable/src/index.ts similarity index 100% rename from packages/agent/src/pico/index.ts rename to packages/durable/src/index.ts diff --git a/packages/agent/src/pico/memory-storage.ts b/packages/durable/src/memory-storage.ts similarity index 100% rename from packages/agent/src/pico/memory-storage.ts rename to packages/durable/src/memory-storage.ts diff --git a/packages/agent/src/pico/types.ts b/packages/durable/src/types.ts similarity index 100% rename from packages/agent/src/pico/types.ts rename to packages/durable/src/types.ts diff --git a/packages/agent/test/pico/memory-storage.test.ts b/packages/durable/test/memory-storage.test.ts similarity index 99% rename from packages/agent/test/pico/memory-storage.test.ts rename to packages/durable/test/memory-storage.test.ts index 62be33a8e..b5f069e16 100644 --- a/packages/agent/test/pico/memory-storage.test.ts +++ b/packages/durable/test/memory-storage.test.ts @@ -1,7 +1,7 @@ import type { JsonValue } from "@earendil-works/chord"; import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context"; import { describe, expect, it } from "vitest"; -import { MemoryStorage } from "../../src/pico/memory-storage.ts"; +import { MemoryStorage } from "../src/memory-storage.ts"; import { type EntryRecord, type Id, @@ -9,7 +9,7 @@ import { ROOT_CONVERSATION_ID, type StorageWrite, type TaskRecord, -} from "../../src/pico/types.ts"; +} from "../src/types.ts"; const context = BACKGROUND_CONTEXT; type StoredTask = TaskRecord; diff --git a/packages/durable/tsconfig.build.json b/packages/durable/tsconfig.build.json new file mode 100644 index 000000000..27422c97b --- /dev/null +++ b/packages/durable/tsconfig.build.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist", + "paths": { + "@earendil-works/chord": ["../chord/dist/index.d.ts"], + "@earendil-works/pi-ai": ["../ai/dist/index.d.ts"] + }, + "rootDir": "./src" + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist", "**/*.d.ts", "src/**/*.d.ts"] +} diff --git a/scripts/check-entry-graphs.mjs b/scripts/check-entry-graphs.mjs index fdbf5d330..5ca3ef7cd 100644 --- a/scripts/check-entry-graphs.mjs +++ b/scripts/check-entry-graphs.mjs @@ -21,6 +21,7 @@ const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const WORKSPACE = { "@earendil-works/chord": "packages/chord/src", "@earendil-works/pi-ai": "packages/ai/src", + "@earendil-works/pi-durable": "packages/durable/src", "@earendil-works/pi-agent-core": "packages/agent/src", "@earendil-works/pi-telemetry": "packages/telemetry/src", "@earendil-works/pi-tui": "packages/tui/src", diff --git a/scripts/local-release.mjs b/scripts/local-release.mjs index e8549bacd..0a37c9a08 100644 --- a/scripts/local-release.mjs +++ b/scripts/local-release.mjs @@ -10,6 +10,7 @@ const packages = [ { directory: "packages/chord", name: "@earendil-works/chord" }, { directory: "packages/telemetry", name: "@earendil-works/pi-telemetry" }, { directory: "packages/ai", name: "@earendil-works/pi-ai" }, + { directory: "packages/durable", name: "@earendil-works/pi-durable" }, { directory: "packages/tui", name: "@earendil-works/pi-tui" }, { directory: "packages/agent", name: "@earendil-works/pi-agent-core" }, { directory: "packages/protocol", name: "@earendil-works/pi-protocol" }, diff --git a/tsconfig.json b/tsconfig.json index 6fb5c81de..d86e580ce 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,8 @@ "@earendil-works/pi-ai/oauth": ["./packages/ai/src/oauth.ts"], "@earendil-works/pi-ai/*": ["./packages/ai/src/*.ts", "./packages/ai/src/providers/*.ts"], "@earendil-works/pi-ai/dist/*": ["./packages/ai/src/*"], + "@earendil-works/pi-durable": ["./packages/durable/src/index.ts"], + "@earendil-works/pi-durable/*": ["./packages/durable/src/*"], "@earendil-works/pi-agent-core": ["./packages/agent/src/index.ts"], "@earendil-works/pi-agent-core/harness/session/testing": [ "./packages/agent/src/harness/session/testing/index.ts"