@pi-harness/pi-harness 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 +161 -0
- package/apps/web/dist/assets/index-C4tLweAB.js +20 -0
- package/apps/web/dist/assets/index-DPgRbQ4_.css +1 -0
- package/apps/web/dist/index.html +14 -0
- package/apps/web/index.html +13 -0
- package/apps/web/package.json +24 -0
- package/apps/web/profile/cordis.yml +44 -0
- package/apps/web/server-dist/bin.d.ts +3 -0
- package/apps/web/server-dist/bin.d.ts.map +1 -0
- package/apps/web/server-dist/bin.js +67 -0
- package/apps/web/server-dist/bin.js.map +1 -0
- package/apps/web/src/command-palette.ts +84 -0
- package/apps/web/src/file-diff.ts +42 -0
- package/apps/web/src/main.ts +12 -0
- package/apps/web/src/runtime.css +4 -0
- package/apps/web/src/server/bin.ts +64 -0
- package/apps/web/src/style.css +7 -0
- package/apps/web/tsconfig.build.json +1 -0
- package/apps/web/tsconfig.json +1 -0
- package/package.json +48 -0
- package/packages/api-gateway/dist/index.d.ts +8 -0
- package/packages/api-gateway/dist/index.d.ts.map +1 -0
- package/packages/api-gateway/dist/index.js +582 -0
- package/packages/api-gateway/dist/index.js.map +1 -0
- package/packages/api-gateway/package.json +29 -0
- package/packages/api-gateway/src/index.ts +577 -0
- package/packages/api-gateway/test/index.test.ts +327 -0
- package/packages/api-gateway/tsconfig.build.json +1 -0
- package/packages/api-gateway/tsconfig.json +1 -0
- package/packages/bundle-web-app/cordis.patch.yml +12 -0
- package/packages/bundle-web-app/dist/index.d.ts +12 -0
- package/packages/bundle-web-app/dist/index.d.ts.map +1 -0
- package/packages/bundle-web-app/dist/index.js +63 -0
- package/packages/bundle-web-app/dist/index.js.map +1 -0
- package/packages/bundle-web-app/package.json +30 -0
- package/packages/bundle-web-app/src/index.ts +69 -0
- package/packages/bundle-web-app/test/index.test.ts +29 -0
- package/packages/bundle-web-app/tsconfig.build.json +1 -0
- package/packages/bundle-web-app/tsconfig.json +1 -0
- package/packages/cli/dist/args.d.ts +16 -0
- package/packages/cli/dist/args.d.ts.map +1 -0
- package/packages/cli/dist/args.js +53 -0
- package/packages/cli/dist/args.js.map +1 -0
- package/packages/cli/dist/bin.d.ts +3 -0
- package/packages/cli/dist/bin.d.ts.map +1 -0
- package/packages/cli/dist/bin.js +40 -0
- package/packages/cli/dist/bin.js.map +1 -0
- package/packages/cli/dist/main.d.ts +14 -0
- package/packages/cli/dist/main.d.ts.map +1 -0
- package/packages/cli/dist/main.js +148 -0
- package/packages/cli/dist/main.js.map +1 -0
- package/packages/cli/dist/node-stdio.d.ts +10 -0
- package/packages/cli/dist/node-stdio.d.ts.map +1 -0
- package/packages/cli/dist/node-stdio.js +34 -0
- package/packages/cli/dist/node-stdio.js.map +1 -0
- package/packages/cli/dist/relaunch.d.ts +5 -0
- package/packages/cli/dist/relaunch.d.ts.map +1 -0
- package/packages/cli/dist/relaunch.js +52 -0
- package/packages/cli/dist/relaunch.js.map +1 -0
- package/packages/cli/package.json +25 -0
- package/packages/cli/src/args.ts +50 -0
- package/packages/cli/src/bin.ts +41 -0
- package/packages/cli/src/main.ts +155 -0
- package/packages/cli/src/node-stdio.ts +40 -0
- package/packages/cli/src/relaunch.ts +48 -0
- package/packages/cli/test/args.test.ts +28 -0
- package/packages/cli/test/cli.test.ts +153 -0
- package/packages/cli/test/relaunch.test.ts +29 -0
- package/packages/cli/tsconfig.build.json +11 -0
- package/packages/cli/tsconfig.json +10 -0
- package/packages/client-web/dist/control-room.d.ts +102 -0
- package/packages/client-web/dist/control-room.d.ts.map +1 -0
- package/packages/client-web/dist/control-room.js +12 -0
- package/packages/client-web/dist/control-room.js.map +1 -0
- package/packages/client-web/dist/design-contract.d.ts +85 -0
- package/packages/client-web/dist/design-contract.d.ts.map +1 -0
- package/packages/client-web/dist/design-contract.js +99 -0
- package/packages/client-web/dist/design-contract.js.map +1 -0
- package/packages/client-web/dist/index.d.ts +17 -0
- package/packages/client-web/dist/index.d.ts.map +1 -0
- package/packages/client-web/dist/index.js +19 -0
- package/packages/client-web/dist/index.js.map +1 -0
- package/packages/client-web/dist/react-room.d.ts +6 -0
- package/packages/client-web/dist/react-room.d.ts.map +1 -0
- package/packages/client-web/dist/react-room.js +103 -0
- package/packages/client-web/dist/react-room.js.map +1 -0
- package/packages/client-web/package.json +29 -0
- package/packages/client-web/src/control-room.ts +10 -0
- package/packages/client-web/src/design-contract.ts +112 -0
- package/packages/client-web/src/index.ts +21 -0
- package/packages/client-web/src/react-room.tsx +78 -0
- package/packages/client-web/test/design-contract.test.ts +24 -0
- package/packages/client-web/tsconfig.build.json +1 -0
- package/packages/client-web/tsconfig.json +1 -0
- package/packages/core/dist/boot.d.ts +13 -0
- package/packages/core/dist/boot.d.ts.map +1 -0
- package/packages/core/dist/boot.js +96 -0
- package/packages/core/dist/boot.js.map +1 -0
- package/packages/core/dist/context.d.ts +3 -0
- package/packages/core/dist/context.d.ts.map +1 -0
- package/packages/core/dist/context.js +2 -0
- package/packages/core/dist/context.js.map +1 -0
- package/packages/core/dist/index.d.ts +7 -0
- package/packages/core/dist/index.d.ts.map +1 -0
- package/packages/core/dist/index.js +6 -0
- package/packages/core/dist/index.js.map +1 -0
- package/packages/core/dist/plugins/model.d.ts +8 -0
- package/packages/core/dist/plugins/model.d.ts.map +1 -0
- package/packages/core/dist/plugins/model.js +12 -0
- package/packages/core/dist/plugins/model.js.map +1 -0
- package/packages/core/dist/plugins/models.d.ts +16 -0
- package/packages/core/dist/plugins/models.d.ts.map +1 -0
- package/packages/core/dist/plugins/models.js +24 -0
- package/packages/core/dist/plugins/models.js.map +1 -0
- package/packages/core/dist/plugins/resources.d.ts +18 -0
- package/packages/core/dist/plugins/resources.d.ts.map +1 -0
- package/packages/core/dist/plugins/resources.js +48 -0
- package/packages/core/dist/plugins/resources.js.map +1 -0
- package/packages/core/dist/plugins/runtime.d.ts +14 -0
- package/packages/core/dist/plugins/runtime.d.ts.map +1 -0
- package/packages/core/dist/plugins/runtime.js +68 -0
- package/packages/core/dist/plugins/runtime.js.map +1 -0
- package/packages/core/dist/plugins/session.d.ts +15 -0
- package/packages/core/dist/plugins/session.d.ts.map +1 -0
- package/packages/core/dist/plugins/session.js +20 -0
- package/packages/core/dist/plugins/session.js.map +1 -0
- package/packages/core/dist/plugins/stdio.d.ts +8 -0
- package/packages/core/dist/plugins/stdio.d.ts.map +1 -0
- package/packages/core/dist/plugins/stdio.js +20 -0
- package/packages/core/dist/plugins/stdio.js.map +1 -0
- package/packages/core/dist/plugins/tools.d.ts +13 -0
- package/packages/core/dist/plugins/tools.d.ts.map +1 -0
- package/packages/core/dist/plugins/tools.js +17 -0
- package/packages/core/dist/plugins/tools.js.map +1 -0
- package/packages/core/dist/profile.d.ts +8 -0
- package/packages/core/dist/profile.d.ts.map +1 -0
- package/packages/core/dist/profile.js +28 -0
- package/packages/core/dist/profile.js.map +1 -0
- package/packages/core/dist/runtime.d.ts +16 -0
- package/packages/core/dist/runtime.d.ts.map +1 -0
- package/packages/core/dist/runtime.js +33 -0
- package/packages/core/dist/runtime.js.map +1 -0
- package/packages/core/dist/services.d.ts +63 -0
- package/packages/core/dist/services.d.ts.map +1 -0
- package/packages/core/dist/services.js +39 -0
- package/packages/core/dist/services.js.map +1 -0
- package/packages/core/dist/stdio.d.ts +25 -0
- package/packages/core/dist/stdio.d.ts.map +1 -0
- package/packages/core/dist/stdio.js +74 -0
- package/packages/core/dist/stdio.js.map +1 -0
- package/packages/core/package.json +44 -0
- package/packages/core/profiles/default/cordis.yml +35 -0
- package/packages/core/profiles/development/cordis.yml +47 -0
- package/packages/core/src/boot.ts +103 -0
- package/packages/core/src/context.ts +3 -0
- package/packages/core/src/index.ts +6 -0
- package/packages/core/src/plugins/model.ts +12 -0
- package/packages/core/src/plugins/models.ts +32 -0
- package/packages/core/src/plugins/resources.ts +57 -0
- package/packages/core/src/plugins/runtime.ts +73 -0
- package/packages/core/src/plugins/session.ts +27 -0
- package/packages/core/src/plugins/stdio.ts +20 -0
- package/packages/core/src/plugins/tools.ts +23 -0
- package/packages/core/src/profile.ts +31 -0
- package/packages/core/src/runtime.ts +39 -0
- package/packages/core/src/services.ts +105 -0
- package/packages/core/src/stdio.ts +88 -0
- package/packages/core/test/boot.test.ts +86 -0
- package/packages/core/test/profile.test.ts +39 -0
- package/packages/core/test/profiles.integration.test.ts +93 -0
- package/packages/core/test/runtime-fixture.ts +31 -0
- package/packages/core/test/runtime.test.ts +54 -0
- package/packages/core/test/services.test.ts +131 -0
- package/packages/core/test/stdio.test.ts +87 -0
- package/packages/core/tsconfig.build.json +10 -0
- package/packages/core/tsconfig.json +10 -0
- package/packages/host-webserver/dist/index.d.ts +29 -0
- package/packages/host-webserver/dist/index.d.ts.map +1 -0
- package/packages/host-webserver/dist/index.js +73 -0
- package/packages/host-webserver/dist/index.js.map +1 -0
- package/packages/host-webserver/package.json +27 -0
- package/packages/host-webserver/src/index.ts +97 -0
- package/packages/host-webserver/test/index.test.ts +43 -0
- package/packages/host-webserver/tsconfig.build.json +1 -0
- package/packages/host-webserver/tsconfig.json +1 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pi-harness/core",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Cordis boot and Pi runtime plugins for Pi Harness",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./plugins/*": {
|
|
14
|
+
"types": "./dist/plugins/*.d.ts",
|
|
15
|
+
"import": "./dist/plugins/*.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"profiles"
|
|
22
|
+
],
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=22.19.0"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc -p tsconfig.build.json",
|
|
28
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
29
|
+
"typecheck": "tsc -p tsconfig.json"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@deepseek-ai/cordis": "4.0.1",
|
|
33
|
+
"@deepseek-ai/cordis-plugin-group": "1.0.1",
|
|
34
|
+
"@deepseek-ai/cordis-plugin-hmr": "1.0.16",
|
|
35
|
+
"@deepseek-ai/cordis-plugin-include": "1.0.6",
|
|
36
|
+
"@deepseek-ai/cordis-plugin-loader": "1.0.2",
|
|
37
|
+
"@deepseek-ai/cordis-plugin-logger-console": "1.0.1",
|
|
38
|
+
"@deepseek-ai/cordis-plugin-timer": "1.1.3",
|
|
39
|
+
"@deepseek-ai/schemastery": "3.18.1",
|
|
40
|
+
"@earendil-works/pi-agent-core": "0.84.4",
|
|
41
|
+
"@earendil-works/pi-ai": "0.84.4",
|
|
42
|
+
"@earendil-works/pi-coding-agent": "0.84.4"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
- id: agent
|
|
2
|
+
name: cordis:group
|
|
3
|
+
group: true
|
|
4
|
+
config:
|
|
5
|
+
- id: models
|
|
6
|
+
name: "@pi-harness/core/plugins/models"
|
|
7
|
+
config:
|
|
8
|
+
provider: deepseek
|
|
9
|
+
model: deepseek-v4-flash
|
|
10
|
+
refreshOnCreate: false
|
|
11
|
+
- id: resources
|
|
12
|
+
name: "@pi-harness/core/plugins/resources"
|
|
13
|
+
config: {}
|
|
14
|
+
- id: model
|
|
15
|
+
name: "@pi-harness/core/plugins/model"
|
|
16
|
+
config: {}
|
|
17
|
+
- id: session
|
|
18
|
+
name: "@pi-harness/core/plugins/session"
|
|
19
|
+
config:
|
|
20
|
+
storage: jsonl
|
|
21
|
+
- id: tools
|
|
22
|
+
name: "@pi-harness/core/plugins/tools"
|
|
23
|
+
config:
|
|
24
|
+
names:
|
|
25
|
+
- read
|
|
26
|
+
- bash
|
|
27
|
+
- edit
|
|
28
|
+
- write
|
|
29
|
+
- id: runtime
|
|
30
|
+
name: "@pi-harness/core/plugins/runtime"
|
|
31
|
+
config:
|
|
32
|
+
thinkingLevel: medium
|
|
33
|
+
- id: stdio
|
|
34
|
+
name: "@pi-harness/core/plugins/stdio"
|
|
35
|
+
config: {}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
- id: logger
|
|
2
|
+
name: "@deepseek-ai/cordis-plugin-logger-console"
|
|
3
|
+
config: {}
|
|
4
|
+
- id: timer
|
|
5
|
+
name: "@deepseek-ai/cordis-plugin-timer"
|
|
6
|
+
config: {}
|
|
7
|
+
- id: hmr
|
|
8
|
+
name: "@deepseek-ai/cordis-plugin-hmr"
|
|
9
|
+
config:
|
|
10
|
+
base: !!js piHarnessLaunch.cwd
|
|
11
|
+
root:
|
|
12
|
+
- .
|
|
13
|
+
- id: agent
|
|
14
|
+
name: cordis:group
|
|
15
|
+
group: true
|
|
16
|
+
config:
|
|
17
|
+
- id: models
|
|
18
|
+
name: "@pi-harness/core/plugins/models"
|
|
19
|
+
config:
|
|
20
|
+
provider: deepseek
|
|
21
|
+
model: deepseek-v4-flash
|
|
22
|
+
refreshOnCreate: false
|
|
23
|
+
- id: resources
|
|
24
|
+
name: "@pi-harness/core/plugins/resources"
|
|
25
|
+
config: {}
|
|
26
|
+
- id: model
|
|
27
|
+
name: "@pi-harness/core/plugins/model"
|
|
28
|
+
config: {}
|
|
29
|
+
- id: session
|
|
30
|
+
name: "@pi-harness/core/plugins/session"
|
|
31
|
+
config:
|
|
32
|
+
storage: jsonl
|
|
33
|
+
- id: tools
|
|
34
|
+
name: "@pi-harness/core/plugins/tools"
|
|
35
|
+
config:
|
|
36
|
+
names:
|
|
37
|
+
- read
|
|
38
|
+
- bash
|
|
39
|
+
- edit
|
|
40
|
+
- write
|
|
41
|
+
- id: runtime
|
|
42
|
+
name: "@pi-harness/core/plugins/runtime"
|
|
43
|
+
config:
|
|
44
|
+
thinkingLevel: medium
|
|
45
|
+
- id: stdio
|
|
46
|
+
name: "@pi-harness/core/plugins/stdio"
|
|
47
|
+
config: {}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { dirname, resolve } from "node:path";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { Context, type FiberState } from "@deepseek-ai/cordis";
|
|
4
|
+
import Group from "@deepseek-ai/cordis-plugin-group";
|
|
5
|
+
import Include from "@deepseek-ai/cordis-plugin-include";
|
|
6
|
+
import Loader, { type EntryOptions } from "@deepseek-ai/cordis-plugin-loader";
|
|
7
|
+
|
|
8
|
+
class ReadonlyInclude extends Include {
|
|
9
|
+
override write(): void {}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface BootHarnessOptions {
|
|
13
|
+
configPath: string;
|
|
14
|
+
prepare?: (context: Context) => Promise<void> | void;
|
|
15
|
+
onFullReload?: () => void;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface BootedHarness {
|
|
20
|
+
readonly context: Context;
|
|
21
|
+
dispose(): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const FIBER_PENDING = 0 as FiberState.PENDING;
|
|
25
|
+
const FIBER_ACTIVE = 2 as FiberState.ACTIVE;
|
|
26
|
+
const FIBER_FAILED = 3 as FiberState.FAILED;
|
|
27
|
+
|
|
28
|
+
function formatError(error: unknown): string {
|
|
29
|
+
return error instanceof Error ? error.stack ?? error.message : String(error);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function assertEntriesActivated(context: Context): Promise<void> {
|
|
33
|
+
const loader = context.get("loader");
|
|
34
|
+
if (loader === undefined) throw new Error("Cordis Loader was disposed during startup");
|
|
35
|
+
const failures: string[] = [];
|
|
36
|
+
for (const entry of loader.entries()) {
|
|
37
|
+
if (entry.disabled) continue;
|
|
38
|
+
const fiber = entry.fiber;
|
|
39
|
+
if (fiber === undefined) {
|
|
40
|
+
failures.push(`${entry.options.name}: module did not load`);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (fiber.state === FIBER_ACTIVE) continue;
|
|
44
|
+
if (fiber.state === FIBER_FAILED) {
|
|
45
|
+
try {
|
|
46
|
+
await fiber.await();
|
|
47
|
+
} catch (error) {
|
|
48
|
+
failures.push(`${entry.options.name}: ${formatError(error)}`);
|
|
49
|
+
}
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (fiber.state === FIBER_PENDING) {
|
|
53
|
+
const missing = Object.keys(fiber.inject).filter((service) => fiber.ctx.get(service) === undefined);
|
|
54
|
+
failures.push(`${entry.options.name}: pending (waiting for services: ${missing.join(", ") || "unknown"})`);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
failures.push(`${entry.options.name}: fiber state ${String(fiber.state)}`);
|
|
58
|
+
}
|
|
59
|
+
if (failures.length > 0) throw new Error(`Cordis plugin tree did not activate:\n${failures.join("\n")}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function mountProfile(context: Context, configPath: string): Promise<void> {
|
|
63
|
+
context.loader.builtins.include = ReadonlyInclude;
|
|
64
|
+
context.loader.builtins.group = Group;
|
|
65
|
+
const root: EntryOptions = {
|
|
66
|
+
id: "profile",
|
|
67
|
+
name: "cordis:include",
|
|
68
|
+
config: { path: pathToFileURL(configPath).href },
|
|
69
|
+
};
|
|
70
|
+
await context.loader.create(root);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function bootHarness(options: BootHarnessOptions): Promise<BootedHarness> {
|
|
74
|
+
const configPath = resolve(options.configPath);
|
|
75
|
+
const context = new Context();
|
|
76
|
+
const abort = () => {
|
|
77
|
+
void context.fiber.dispose().catch(() => {});
|
|
78
|
+
};
|
|
79
|
+
if (options.signal?.aborted) throw new Error("Pi Harness startup was aborted", { cause: options.signal.reason });
|
|
80
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
81
|
+
let stage = "host preparation";
|
|
82
|
+
try {
|
|
83
|
+
context.baseUrl = `${pathToFileURL(dirname(configPath)).href}/`;
|
|
84
|
+
await context.plugin(Loader);
|
|
85
|
+
if (options.onFullReload !== undefined) context.loader.exit = options.onFullReload;
|
|
86
|
+
await options.prepare?.(context);
|
|
87
|
+
stage = "plugin tree activation";
|
|
88
|
+
await mountProfile(context, configPath);
|
|
89
|
+
await context.get("loader")?.await();
|
|
90
|
+
if (context.get("loader") !== undefined) await assertEntriesActivated(context);
|
|
91
|
+
} catch (cause) {
|
|
92
|
+
await context.fiber.dispose();
|
|
93
|
+
throw new Error(`Pi Harness ${stage} failed: ${formatError(cause)}`, { cause });
|
|
94
|
+
} finally {
|
|
95
|
+
options.signal?.removeEventListener("abort", abort);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
context,
|
|
99
|
+
async dispose() {
|
|
100
|
+
await context.fiber.dispose();
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
name: "pi-model",
|
|
5
|
+
inject: ["piModelRuntime", "piResources"],
|
|
6
|
+
apply(context: Context) {
|
|
7
|
+
const { runtime, provider, model: modelId } = context.piModelRuntime;
|
|
8
|
+
const model = runtime.getModel(provider, modelId);
|
|
9
|
+
if (model === undefined) throw new Error(`Pi model is not registered: ${provider}/${modelId}`);
|
|
10
|
+
context.provide("piModels", { runtime, model });
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
3
|
+
import z from "@deepseek-ai/schemastery";
|
|
4
|
+
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
|
|
6
|
+
export interface ModelsPluginConfig {
|
|
7
|
+
provider: string;
|
|
8
|
+
model: string;
|
|
9
|
+
refreshOnCreate?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Config: z<ModelsPluginConfig> = z.object({
|
|
13
|
+
provider: z.string().required(),
|
|
14
|
+
model: z.string().required(),
|
|
15
|
+
refreshOnCreate: z.boolean().default(false),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
name: "pi-models",
|
|
20
|
+
inject: ["piHarnessLaunch"],
|
|
21
|
+
Config,
|
|
22
|
+
async apply(context: Context, config: ModelsPluginConfig) {
|
|
23
|
+
const agentDir = context.piHarnessLaunch.agentDir;
|
|
24
|
+
const runtime = await ModelRuntime.create({
|
|
25
|
+
authPath: join(agentDir, "auth.json"),
|
|
26
|
+
modelsPath: join(agentDir, "models.json"),
|
|
27
|
+
modelsStorePath: join(agentDir, "models-store.json"),
|
|
28
|
+
refreshOnCreate: config.refreshOnCreate ?? false,
|
|
29
|
+
});
|
|
30
|
+
context.provide("piModelRuntime", { runtime, provider: config.provider, model: config.model });
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
import { createAgentSessionServices } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
|
|
5
|
+
export interface ResourcesPluginConfig {
|
|
6
|
+
noExtensions?: boolean;
|
|
7
|
+
noSkills?: boolean;
|
|
8
|
+
noPromptTemplates?: boolean;
|
|
9
|
+
noThemes?: boolean;
|
|
10
|
+
noContextFiles?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const Config: z<ResourcesPluginConfig> = z.object({
|
|
14
|
+
noExtensions: z.boolean().default(false),
|
|
15
|
+
noSkills: z.boolean().default(false),
|
|
16
|
+
noPromptTemplates: z.boolean().default(false),
|
|
17
|
+
noThemes: z.boolean().default(false),
|
|
18
|
+
noContextFiles: z.boolean().default(false),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
name: "pi-resources",
|
|
23
|
+
inject: ["piHarnessLaunch", "piModelRuntime"],
|
|
24
|
+
Config,
|
|
25
|
+
async apply(context: Context, config: ResourcesPluginConfig) {
|
|
26
|
+
const services = await createAgentSessionServices({
|
|
27
|
+
cwd: context.piHarnessLaunch.cwd,
|
|
28
|
+
agentDir: context.piHarnessLaunch.agentDir,
|
|
29
|
+
modelRuntime: context.piModelRuntime.runtime,
|
|
30
|
+
resourceLoaderOptions: {
|
|
31
|
+
noExtensions: config.noExtensions ?? false,
|
|
32
|
+
noSkills: config.noSkills ?? false,
|
|
33
|
+
noPromptTemplates: config.noPromptTemplates ?? false,
|
|
34
|
+
noThemes: config.noThemes ?? false,
|
|
35
|
+
noContextFiles: config.noContextFiles ?? false,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
const errors = services.diagnostics.filter((diagnostic) => diagnostic.type === "error");
|
|
39
|
+
if (errors.length > 0) throw new Error(`Pi resource loading failed:\n${errors.map((diagnostic) => diagnostic.message).join("\n")}`);
|
|
40
|
+
const resourceLoaderOptions = {
|
|
41
|
+
noExtensions: config.noExtensions ?? false,
|
|
42
|
+
noSkills: config.noSkills ?? false,
|
|
43
|
+
noPromptTemplates: config.noPromptTemplates ?? false,
|
|
44
|
+
noThemes: config.noThemes ?? false,
|
|
45
|
+
noContextFiles: config.noContextFiles ?? false,
|
|
46
|
+
};
|
|
47
|
+
context.provide("piResources", {
|
|
48
|
+
...services,
|
|
49
|
+
createForCwd: (cwd: string) => createAgentSessionServices({
|
|
50
|
+
cwd,
|
|
51
|
+
agentDir: context.piHarnessLaunch.agentDir,
|
|
52
|
+
modelRuntime: context.piModelRuntime.runtime,
|
|
53
|
+
resourceLoaderOptions,
|
|
54
|
+
}),
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
import { createAgentSessionFromServices, createAgentSessionRuntime, type AgentSession, type CreateAgentSessionRuntimeFactory } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { PiRuntime } from "../runtime.js";
|
|
5
|
+
|
|
6
|
+
export interface RuntimePluginConfig {
|
|
7
|
+
thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const Config: z<RuntimePluginConfig> = z.object({
|
|
11
|
+
thinkingLevel: z.union(["off", "minimal", "low", "medium", "high", "xhigh", "max"]).default("medium"),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
name: "pi-runtime",
|
|
16
|
+
inject: ["piModels", "piResources", "piSession", "piTools"],
|
|
17
|
+
Config,
|
|
18
|
+
async apply(context: Context, config: RuntimePluginConfig) {
|
|
19
|
+
const tools = context.piTools.acquire();
|
|
20
|
+
context.effect(() => () => tools.release());
|
|
21
|
+
const requestedTools = [...tools.names, ...tools.customTools.map((tool) => tool.name)];
|
|
22
|
+
const createRuntime: CreateAgentSessionRuntimeFactory = async ({ cwd, sessionManager, sessionStartEvent }) => {
|
|
23
|
+
const services = cwd === context.piResources.cwd ? context.piResources : await context.piResources.createForCwd(cwd);
|
|
24
|
+
const result = await createAgentSessionFromServices({
|
|
25
|
+
services,
|
|
26
|
+
sessionManager,
|
|
27
|
+
...(sessionStartEvent === undefined ? {} : { sessionStartEvent }),
|
|
28
|
+
model: context.piModels.model,
|
|
29
|
+
thinkingLevel: config.thinkingLevel ?? "medium",
|
|
30
|
+
tools: requestedTools,
|
|
31
|
+
customTools: tools.customTools,
|
|
32
|
+
});
|
|
33
|
+
return { ...result, services, diagnostics: services.diagnostics };
|
|
34
|
+
};
|
|
35
|
+
const sessionRuntime = await createAgentSessionRuntime(createRuntime, {
|
|
36
|
+
cwd: context.piResources.cwd,
|
|
37
|
+
agentDir: context.piResources.agentDir,
|
|
38
|
+
sessionManager: context.piSession.manager,
|
|
39
|
+
});
|
|
40
|
+
const runtime = new PiRuntime(sessionRuntime);
|
|
41
|
+
const bindSession = async (session: AgentSession): Promise<void> => {
|
|
42
|
+
const activeTools = new Set(session.getAllTools().map((tool) => tool.name));
|
|
43
|
+
const missingTools = requestedTools.filter((name) => !activeTools.has(name));
|
|
44
|
+
if (missingTools.length > 0) throw new Error(`Pi tools are not registered: ${missingTools.join(", ")}`);
|
|
45
|
+
await session.bindExtensions({
|
|
46
|
+
mode: "print",
|
|
47
|
+
abortHandler: () => {
|
|
48
|
+
void runtime.abort();
|
|
49
|
+
},
|
|
50
|
+
onError: (error) => {
|
|
51
|
+
context.emit("pi/extension-error", error);
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
context.provide("piRuntime", runtime);
|
|
56
|
+
let unsubscribe: (() => void) | undefined;
|
|
57
|
+
const rebindSession = async (session: AgentSession): Promise<void> => {
|
|
58
|
+
unsubscribe?.();
|
|
59
|
+
await bindSession(session);
|
|
60
|
+
unsubscribe = session.subscribe((event) => {
|
|
61
|
+
context.emit("pi/session-event", event);
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
sessionRuntime.setRebindSession(rebindSession);
|
|
65
|
+
await rebindSession(sessionRuntime.session);
|
|
66
|
+
context.effect(() => {
|
|
67
|
+
return async () => {
|
|
68
|
+
unsubscribe?.();
|
|
69
|
+
await runtime.dispose();
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { join, resolve } from "node:path";
|
|
2
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
3
|
+
import z from "@deepseek-ai/schemastery";
|
|
4
|
+
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
|
|
6
|
+
export interface SessionPluginConfig {
|
|
7
|
+
storage?: "memory" | "jsonl";
|
|
8
|
+
directory?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Config: z<SessionPluginConfig> = z.object({
|
|
12
|
+
storage: z.union(["memory", "jsonl"]).default("jsonl"),
|
|
13
|
+
directory: z.string(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
name: "pi-session",
|
|
18
|
+
inject: ["piHarnessLaunch"],
|
|
19
|
+
Config,
|
|
20
|
+
apply(context: Context, config: SessionPluginConfig) {
|
|
21
|
+
const storage = config.storage ?? "jsonl";
|
|
22
|
+
const manager = storage === "memory"
|
|
23
|
+
? SessionManager.inMemory(context.piHarnessLaunch.cwd)
|
|
24
|
+
: SessionManager.create(context.piHarnessLaunch.cwd, config.directory === undefined ? join(context.piHarnessLaunch.agentDir, "sessions") : resolve(context.piHarnessLaunch.cwd, config.directory));
|
|
25
|
+
context.provide("piSession", { manager });
|
|
26
|
+
},
|
|
27
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import { StdioApplication } from "../stdio.js";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
name: "pi-stdio",
|
|
6
|
+
inject: ["piRuntime", "piResources", "piHarnessStdio", "piHarnessLaunch"],
|
|
7
|
+
apply(context: Context) {
|
|
8
|
+
const application = new StdioApplication(context.piRuntime, context.piHarnessLaunch, context.piHarnessStdio);
|
|
9
|
+
for (const diagnostic of context.piResources.diagnostics) {
|
|
10
|
+
if (diagnostic.type !== "error") context.piHarnessStdio.writeError(`Resource ${diagnostic.type}: ${diagnostic.message}\n`);
|
|
11
|
+
}
|
|
12
|
+
context.on("pi/session-event", (event) => {
|
|
13
|
+
application.writeSessionEvent(event);
|
|
14
|
+
});
|
|
15
|
+
context.on("pi/extension-error", (error) => {
|
|
16
|
+
context.piHarnessStdio.writeError(`Extension error (${error.extensionPath}): ${error.error}\n`);
|
|
17
|
+
});
|
|
18
|
+
context.provide("piApplication", application);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
import { PiToolRegistry } from "../services.js";
|
|
4
|
+
|
|
5
|
+
export interface ToolsPluginConfig {
|
|
6
|
+
names?: string[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const DEFAULT_TOOLS = ["read", "bash", "edit", "write"];
|
|
10
|
+
|
|
11
|
+
export const Config: z<ToolsPluginConfig> = z.object({
|
|
12
|
+
names: z.array(z.string()).default(DEFAULT_TOOLS),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
name: "pi-tools",
|
|
17
|
+
Config,
|
|
18
|
+
apply(context: Context, config: ToolsPluginConfig) {
|
|
19
|
+
const names = config.names ?? DEFAULT_TOOLS;
|
|
20
|
+
if (new Set(names).size !== names.length) throw new Error("Pi core tool names must be unique");
|
|
21
|
+
context.provide("piTools", new PiToolRegistry(names));
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
export interface ResolveProfileConfigOptions {
|
|
6
|
+
profile?: string;
|
|
7
|
+
configPath?: string;
|
|
8
|
+
cwd?: string;
|
|
9
|
+
profilesDir?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function resolveProfileConfig(_options: ResolveProfileConfigOptions = {}): Promise<string> {
|
|
13
|
+
const options = _options;
|
|
14
|
+
if (options.profile !== undefined && options.configPath !== undefined) throw new Error("profile and configPath cannot be used together");
|
|
15
|
+
const cwd = options.cwd ?? process.cwd();
|
|
16
|
+
let configPath: string;
|
|
17
|
+
if (options.configPath !== undefined) {
|
|
18
|
+
configPath = resolve(cwd, options.configPath);
|
|
19
|
+
} else {
|
|
20
|
+
const profile = options.profile ?? "default";
|
|
21
|
+
if (!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(profile)) throw new Error(`Invalid profile name: ${profile}`);
|
|
22
|
+
const profilesDir = options.profilesDir ?? fileURLToPath(new URL("../profiles", import.meta.url));
|
|
23
|
+
configPath = resolve(profilesDir, profile, "cordis.yml");
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
await access(configPath);
|
|
27
|
+
} catch (cause) {
|
|
28
|
+
throw new Error(`Pi Harness profile config does not exist or is not readable: ${configPath}`, { cause });
|
|
29
|
+
}
|
|
30
|
+
return configPath;
|
|
31
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AgentSession, AgentSessionRuntime } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { PiRuntimeService } from "./services.js";
|
|
3
|
+
|
|
4
|
+
export class PiRuntimeDisposedError extends Error {
|
|
5
|
+
override readonly name = "PiRuntimeDisposedError";
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
super("Pi runtime is disposed");
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class PiRuntime implements PiRuntimeService {
|
|
13
|
+
readonly sessionRuntime: AgentSessionRuntime;
|
|
14
|
+
#disposed = false;
|
|
15
|
+
|
|
16
|
+
constructor(sessionRuntime: AgentSessionRuntime) {
|
|
17
|
+
this.sessionRuntime = sessionRuntime;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get session(): AgentSession {
|
|
21
|
+
return this.sessionRuntime.session;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async prompt(text: string): Promise<void> {
|
|
25
|
+
if (this.#disposed) throw new PiRuntimeDisposedError();
|
|
26
|
+
await this.sessionRuntime.session.prompt(text);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async abort(): Promise<void> {
|
|
30
|
+
if (this.#disposed) return;
|
|
31
|
+
await this.sessionRuntime.session.abort();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async dispose(): Promise<void> {
|
|
35
|
+
if (this.#disposed) return;
|
|
36
|
+
this.#disposed = true;
|
|
37
|
+
await this.sessionRuntime.dispose();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import type { AgentSession, AgentSessionEvent, AgentSessionRuntime, AgentSessionServices, ExtensionError, SessionManager, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
4
|
+
import type { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
|
|
6
|
+
export interface PiHarnessLaunch {
|
|
7
|
+
readonly cwd: string;
|
|
8
|
+
readonly agentDir: string;
|
|
9
|
+
readonly args: readonly string[];
|
|
10
|
+
requestExit(code: number): void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface PiModelsService {
|
|
14
|
+
readonly runtime: ModelRuntime;
|
|
15
|
+
readonly model: Model<Api>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PiModelRuntimeService {
|
|
19
|
+
readonly runtime: ModelRuntime;
|
|
20
|
+
readonly provider: string;
|
|
21
|
+
readonly model: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type PiResourcesService = AgentSessionServices & {
|
|
25
|
+
createForCwd(cwd: string): Promise<AgentSessionServices>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export interface PiSessionService {
|
|
29
|
+
readonly manager: SessionManager;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface PiRuntimeService {
|
|
33
|
+
readonly session: AgentSession;
|
|
34
|
+
readonly sessionRuntime: AgentSessionRuntime;
|
|
35
|
+
prompt(text: string): Promise<void>;
|
|
36
|
+
abort(): Promise<void>;
|
|
37
|
+
dispose(): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface PiToolsSnapshot {
|
|
41
|
+
readonly names: string[];
|
|
42
|
+
readonly customTools: ToolDefinition[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface PiToolsLease extends PiToolsSnapshot {
|
|
46
|
+
release(): void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class PiToolRegistry {
|
|
50
|
+
readonly #names: string[];
|
|
51
|
+
readonly #customTools = new Map<string, ToolDefinition>();
|
|
52
|
+
#leases = 0;
|
|
53
|
+
|
|
54
|
+
constructor(names: readonly string[] = []) {
|
|
55
|
+
this.#names = [...names];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
register(tool: ToolDefinition): () => void {
|
|
59
|
+
if (this.#leases > 0) throw new Error(`Pi tool registry is leased by pi-runtime; declare a Cordis injection that activates ${tool.name} before pi-runtime`);
|
|
60
|
+
if (this.#names.includes(tool.name) || this.#customTools.has(tool.name)) throw new Error(`Pi tool is already registered: ${tool.name}`);
|
|
61
|
+
this.#customTools.set(tool.name, tool);
|
|
62
|
+
return () => {
|
|
63
|
+
this.#customTools.delete(tool.name);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
snapshot(): PiToolsSnapshot {
|
|
68
|
+
return { names: [...this.#names], customTools: [...this.#customTools.values()] };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
acquire(): PiToolsLease {
|
|
72
|
+
this.#leases += 1;
|
|
73
|
+
let released = false;
|
|
74
|
+
return {
|
|
75
|
+
...this.snapshot(),
|
|
76
|
+
release: () => {
|
|
77
|
+
if (released) return;
|
|
78
|
+
released = true;
|
|
79
|
+
this.#leases -= 1;
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
declare module "@deepseek-ai/cordis" {
|
|
86
|
+
interface Context {
|
|
87
|
+
piHarnessLaunch: PiHarnessLaunch;
|
|
88
|
+
piModelRuntime: PiModelRuntimeService;
|
|
89
|
+
piModels: PiModelsService;
|
|
90
|
+
piResources: PiResourcesService;
|
|
91
|
+
piSession: PiSessionService;
|
|
92
|
+
piTools: PiToolRegistry;
|
|
93
|
+
piRuntime: PiRuntimeService;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface Events {
|
|
97
|
+
"pi/session-event"(event: AgentSessionEvent): void;
|
|
98
|
+
"pi/extension-error"(error: ExtensionError): void;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function provideLaunchContext(context: Context, launch: PiHarnessLaunch): () => void {
|
|
103
|
+
const value: PiHarnessLaunch = Object.freeze({ ...launch, args: Object.freeze([...launch.args]) });
|
|
104
|
+
return context.provide("piHarnessLaunch", value);
|
|
105
|
+
}
|