@pi-harness/pi-harness 0.1.30 → 0.1.31

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.
Files changed (31) hide show
  1. package/README.md +1 -1
  2. package/apps/web/dist/assets/index-D9ZcvGXi.js +25 -0
  3. package/apps/web/dist/assets/{index-xF647pQJ.css → index-DJsNIiIo.css} +1 -1
  4. package/apps/web/dist/index.html +2 -2
  5. package/apps/web/package.json +1 -1
  6. package/apps/web/profile/cordis.yml +0 -221
  7. package/apps/web/src/style.css +3 -0
  8. package/node_modules/@pi-harness/api-gateway/dist/index.d.ts.map +1 -1
  9. package/node_modules/@pi-harness/api-gateway/dist/index.js +42 -11
  10. package/node_modules/@pi-harness/api-gateway/dist/index.js.map +1 -1
  11. package/node_modules/@pi-harness/api-gateway/package.json +1 -27
  12. package/node_modules/@pi-harness/cli/package.json +1 -1
  13. package/node_modules/@pi-harness/cli/profiles/default/cordis.yml +0 -87
  14. package/node_modules/@pi-harness/host-webserver/package.json +1 -1
  15. package/node_modules/@pi-harness/web-app/package.json +1 -1
  16. package/package.json +6 -79
  17. package/packages/api-gateway/dist/index.d.ts.map +1 -1
  18. package/packages/api-gateway/dist/index.js +42 -11
  19. package/packages/api-gateway/dist/index.js.map +1 -1
  20. package/packages/api-gateway/package.json +1 -27
  21. package/packages/api-gateway/src/index.ts +54 -16
  22. package/packages/api-gateway/test/index.test.ts +157 -2
  23. package/packages/api-gateway/test/packed-package.test.ts +18 -6
  24. package/packages/api-gateway/test/release-contract.test.ts +8 -4
  25. package/packages/bundle-web-app/package.json +1 -1
  26. package/packages/cli/package.json +1 -1
  27. package/packages/cli/profiles/default/cordis.yml +0 -87
  28. package/packages/cli/test/profiles/plugins/cordis.yml +122 -0
  29. package/packages/cli/test/profiles.integration.test.ts +40 -23
  30. package/packages/host-webserver/package.json +1 -1
  31. package/apps/web/dist/assets/index-B7pwZ76Z.js +0 -25
@@ -10,7 +10,7 @@ import timerPlugin from "@deepseek-ai/cordis-plugin-timer";
10
10
  import { SessionManager } from "@earendil-works/pi-coding-agent";
11
11
  import { afterEach, describe, expect, test, vi } from "vitest";
12
12
  import webServerPlugin from "@pi-harness/host-webserver";
13
- import { PiPluginUiRegistry, PiToolRegistry } from "@pi-harness/core";
13
+ import { PiPluginUiRegistry, PiToolRegistry, PiToolRegistryLeasedError } from "@pi-harness/core";
14
14
  import type { MarketplacePlugin } from "../src/marketplace.js";
15
15
  import apiPlugin from "../src/index.js";
16
16
  import type * as FsPromises from "node:fs/promises";
@@ -2190,12 +2190,167 @@ describe("API gateway plugin", () => {
2190
2190
  }
2191
2191
 
2192
2192
  expect(response.status).toBe(200);
2193
- await expect(response.json()).resolves.toMatchObject({ installed: true, plugin: { id: "skill-guard" } });
2193
+ await expect(response.json()).resolves.toMatchObject({ installed: true, restartRequired: false, plugin: { id: "skill-guard" } });
2194
2194
  expect((await readFile(npmLog, "utf8")).trim()).toMatch(/^install --save-exact --package-lock=false @pi-harness\/plugin-skill-guard@\d/u);
2195
2195
  expect(created).toEqual([expect.objectContaining({ name: "@pi-harness/plugin-skill-guard" })]);
2196
2196
  await expect(readFile(configPath, "utf8")).resolves.toContain('name: "@pi-harness/plugin-skill-guard"');
2197
2197
  });
2198
2198
 
2199
+ // A plugin registers its tools while it activates, and pi-runtime takes the tool registry when it activates, so an installed entry only works if it sits ahead of the runtime inside the runtime's own group.
2200
+ test("installs a marketplace plugin into the runtime's group ahead of the runtime", async () => {
2201
+ const directory = await mkdtemp(join(tmpdir(), "pi-harness-api-plugin-placement-"));
2202
+ temporaryDirectories.push(directory);
2203
+ const shimDirectory = join(directory, "bin");
2204
+ await mkdir(shimDirectory);
2205
+ await writeFile(join(directory, "package.json"), '{ "name": "harness" }\n', "utf8");
2206
+ const configPath = join(directory, "profile.yml");
2207
+ await writeFile(
2208
+ configPath,
2209
+ '- id: agent\n name: cordis:group\n group: true\n config:\n - id: tools\n name: "@pi-harness/core/plugins/tools"\n config: {}\n - id: runtime\n name: "@pi-harness/core/plugins/runtime"\n config: {}\n',
2210
+ "utf8",
2211
+ );
2212
+ await writeFile(join(shimDirectory, "npm"), "#!/bin/sh\nexit 0\n", { mode: 0o755 });
2213
+ const context = new Context();
2214
+ contexts.push(context);
2215
+ await context.plugin(webServerPlugin, { host: "127.0.0.1", port: 0 });
2216
+ const session = { sessionId: "marketplace-placement-session", sessionFile: undefined, messages: [], isStreaming: false, subscribe: () => () => {} };
2217
+ context.provide("piRuntime", { session, prompt: () => Promise.resolve(), abort: () => Promise.resolve(), dispose: () => Promise.resolve() } as never);
2218
+ context.provide("piModels", { model: { provider: "test", id: "model" } } as never);
2219
+ context.provide("piHarnessLaunch", { cwd: directory, agentDir: directory, configPath, args: [], requestExit() {} });
2220
+ const runtimeOptions = { id: "runtime", name: "@pi-harness/core/plugins/runtime", config: {} };
2221
+ const runtimeGroup = { data: [{ id: "tools", name: "@pi-harness/core/plugins/tools", config: {} }, runtimeOptions] };
2222
+ const loaderEntries = [
2223
+ // The loader addresses a nested group by its qualified id, while the profile file names the same entry by its own id.
2224
+ { id: "profile:agent", options: { id: "agent", name: "cordis:group" }, subgroup: runtimeGroup },
2225
+ { id: "profile:runtime", options: runtimeOptions, parent: runtimeGroup },
2226
+ ];
2227
+ const created: Array<[unknown, unknown, unknown]> = [];
2228
+ context.reflect.provide("loader", {
2229
+ entries: () => loaderEntries,
2230
+ create: (options: unknown, parent: unknown, position: unknown) => {
2231
+ created.push([options, parent, position]);
2232
+ return Promise.resolve("marketplace-entry");
2233
+ },
2234
+ resolve: () => ({ fiber: { await: () => Promise.resolve() } }),
2235
+ remove: () => Promise.resolve(),
2236
+ });
2237
+ await context.plugin(apiPlugin);
2238
+
2239
+ const originalPath = process.env.PATH ?? "";
2240
+ process.env.PATH = `${shimDirectory}:${originalPath}`;
2241
+ let response: Response;
2242
+ try {
2243
+ response = await fetch(context.webServer.url + "/api/marketplace/install", {
2244
+ method: "POST",
2245
+ headers: { "content-type": "application/json" },
2246
+ body: JSON.stringify({ id: "skill-guard" }),
2247
+ });
2248
+ } finally {
2249
+ process.env.PATH = originalPath;
2250
+ }
2251
+
2252
+ expect(response.status).toBe(200);
2253
+ await expect(response.json()).resolves.toMatchObject({ installed: true, restartRequired: false });
2254
+ expect(created).toEqual([[expect.objectContaining({ id: "marketplace-skill-guard", name: "@pi-harness/plugin-skill-guard" }), "profile:agent", 1]]);
2255
+ await expect(readFile(configPath, "utf8")).resolves.toBe(
2256
+ '- id: agent\n name: cordis:group\n group: true\n config:\n - id: tools\n name: "@pi-harness/core/plugins/tools"\n config: {}\n - id: marketplace-skill-guard\n name: "@pi-harness/plugin-skill-guard"\n config: {}\n - id: runtime\n name: "@pi-harness/core/plugins/runtime"\n config: {}\n',
2257
+ );
2258
+ });
2259
+
2260
+ // The runtime snapshots the tool set when it takes the registry, so a plugin that contributes tools cannot join a harness that is already running. Rolling the install back would leave the user unable to install it at all, so the package and the profile row stay and the console asks for a restart.
2261
+ test("keeps an installed plugin in place when the runtime already leased the tool registry", async () => {
2262
+ const directory = await mkdtemp(join(tmpdir(), "pi-harness-api-plugin-leased-"));
2263
+ temporaryDirectories.push(directory);
2264
+ const shimDirectory = join(directory, "bin");
2265
+ await mkdir(shimDirectory);
2266
+ await writeFile(join(directory, "package.json"), '{ "name": "harness", "dependencies": {} }\n', "utf8");
2267
+ const configPath = join(directory, "profile.yml");
2268
+ await writeFile(configPath, '- id: runtime\n name: "@pi-harness/core/plugins/runtime"\n config: {}\n', "utf8");
2269
+ await writeFile(join(shimDirectory, "npm"), "#!/bin/sh\nexit 0\n", { mode: 0o755 });
2270
+ const context = new Context();
2271
+ contexts.push(context);
2272
+ await context.plugin(webServerPlugin, { host: "127.0.0.1", port: 0 });
2273
+ const session = { sessionId: "marketplace-leased-session", sessionFile: undefined, messages: [], isStreaming: false, subscribe: () => () => {} };
2274
+ context.provide("piRuntime", { session, prompt: () => Promise.resolve(), abort: () => Promise.resolve(), dispose: () => Promise.resolve() } as never);
2275
+ context.provide("piModels", { model: { provider: "test", id: "model" } } as never);
2276
+ context.provide("piHarnessLaunch", { cwd: directory, agentDir: directory, configPath, args: [], requestExit() {} });
2277
+ const removed: string[] = [];
2278
+ context.reflect.provide("loader", {
2279
+ entries: () => [],
2280
+ create: () => Promise.resolve("marketplace-entry"),
2281
+ // The loader wraps an activation failure in its own error chain, which is what the gateway has to see through.
2282
+ resolve: () => ({
2283
+ fiber: {
2284
+ await: () => Promise.reject(new Error("failed to apply loader entry", { cause: new PiToolRegistryLeasedError("skill_scan") })),
2285
+ },
2286
+ }),
2287
+ remove: (id: string) => {
2288
+ removed.push(id);
2289
+ return Promise.resolve();
2290
+ },
2291
+ });
2292
+ await context.plugin(apiPlugin);
2293
+
2294
+ const originalPath = process.env.PATH ?? "";
2295
+ process.env.PATH = `${shimDirectory}:${originalPath}`;
2296
+ let response: Response;
2297
+ try {
2298
+ response = await fetch(context.webServer.url + "/api/marketplace/install", {
2299
+ method: "POST",
2300
+ headers: { "content-type": "application/json" },
2301
+ body: JSON.stringify({ id: "skill-guard" }),
2302
+ });
2303
+ } finally {
2304
+ process.env.PATH = originalPath;
2305
+ }
2306
+
2307
+ expect(response.status).toBe(200);
2308
+ await expect(response.json()).resolves.toMatchObject({ installed: true, restartRequired: true, plugin: { id: "skill-guard" } });
2309
+ expect(removed).toEqual(["marketplace-entry"]);
2310
+ await expect(readFile(configPath, "utf8")).resolves.toContain('name: "@pi-harness/plugin-skill-guard"');
2311
+ await expect(readFile(join(directory, "package.json"), "utf8")).resolves.toBe('{ "name": "harness", "dependencies": {} }\n');
2312
+ });
2313
+
2314
+ // Reverting the profile here would leave a plugin that can be switched off but never on again, so the change stays and the plugin comes back on the next start.
2315
+ test("keeps a re-enabled plugin enabled in the profile when the runtime already leased the tool registry", async () => {
2316
+ const context = new Context();
2317
+ contexts.push(context);
2318
+ const directory = await mkdtemp(join(tmpdir(), "pi-harness-api-plugin-toggle-leased-"));
2319
+ temporaryDirectories.push(directory);
2320
+ const configPath = join(directory, "profile.yml");
2321
+ const loaderEntry = {
2322
+ id: "profile:marketplace-skill-guard",
2323
+ options: { id: "marketplace-skill-guard", name: "@pi-harness/plugin-skill-guard", config: {} },
2324
+ update: () => Promise.reject(new Error("failed to apply loader entry", { cause: new PiToolRegistryLeasedError("skill_scan") })),
2325
+ };
2326
+ await writeFile(
2327
+ configPath,
2328
+ '- id: marketplace-skill-guard\n name: "@pi-harness/plugin-skill-guard"\n disabled: true\n config: {}\n- id: runtime\n name: "@pi-harness/core/plugins/runtime"\n config: {}\n',
2329
+ "utf8",
2330
+ );
2331
+ await context.plugin(webServerPlugin, { host: "127.0.0.1", port: 0 });
2332
+ const session = { sessionId: "plugin-toggle-leased-session", sessionFile: undefined, messages: [], isStreaming: false, subscribe: () => () => {} };
2333
+ context.provide("piRuntime", { session, prompt: () => Promise.resolve(), abort: () => Promise.resolve(), dispose: () => Promise.resolve() } as never);
2334
+ context.provide("piModels", { model: { provider: "test", id: "model" }, runtime: { getModels: () => [], getModel: () => undefined } } as never);
2335
+ context.provide("piHarnessLaunch", { cwd: directory, agentDir: directory, configPath, args: [], requestExit() {} });
2336
+ context.reflect.provide("loader", {
2337
+ *entries() {
2338
+ yield loaderEntry;
2339
+ },
2340
+ });
2341
+ await context.plugin(apiPlugin);
2342
+
2343
+ const response = await fetch(context.webServer.url + "/api/plugins/toggle", {
2344
+ method: "POST",
2345
+ headers: { "content-type": "application/json" },
2346
+ body: JSON.stringify({ id: "marketplace-skill-guard", enabled: true }),
2347
+ });
2348
+
2349
+ expect(response.status).toBe(200);
2350
+ await expect(response.json()).resolves.toMatchObject({ restartRequired: true, plugin: { id: "marketplace-skill-guard" } });
2351
+ await expect(readFile(configPath, "utf8")).resolves.not.toContain("disabled: true");
2352
+ });
2353
+
2199
2354
  test("locks marketplace installation before reading a request body", async () => {
2200
2355
  const cwd = await mkdtemp(join(tmpdir(), "pi-harness-api-plugin-install-lock-"));
2201
2356
  temporaryDirectories.push(cwd);
@@ -73,10 +73,13 @@ const bin = { "pi-harness": "./apps/web/server-dist/bin.js", pih: "./packages/cl
73
73
  write(join(harnessRoot, "package.json"), JSON.stringify({ name: "@pi-harness/pi-harness", version: "9.9.9", bin, dependencies: { "@earendil-works/pi-coding-agent": "0.84.4" } }));
74
74
  write(join(agentRoot, "package.json"), JSON.stringify({ name: "@earendil-works/pi-coding-agent", version: "0.84.4", dependencies: {} }));
75
75
  write(join(agentRoot, "dist", "cli", "args.js"), "");
76
- write(join(harnessRoot, "apps", "web", "profile", "cordis.yml"), "- id: modlens\\n name: \\"@pi-harness/plugin-modlens\\"\\n");
76
+ const profileEntries = ["- id: webserver\\n name: \\"@pi-harness/host-webserver\\"\\n"];
77
+ if (process.env.PACKED_PACKAGE_FIXTURE_BUNDLED_PLUGIN === "1") profileEntries.push("- id: modlens\\n name: \\"@pi-harness/plugin-modlens\\"\\n");
78
+ write(join(harnessRoot, "apps", "web", "profile", "cordis.yml"), profileEntries.join(""));
77
79
  const shippedDependencies = process.env.PACKED_PACKAGE_FIXTURE_SHIPPED_DEP === "1" ? { "@pi-harness/client-web": "9.9.9" } : undefined;
78
80
  write(join(harnessRoot, "apps", "web", "package.json"), JSON.stringify({ name: "@pi-harness/web", private: true, dependencies: shippedDependencies }));
79
- if (process.env.PACKED_PACKAGE_FIXTURE_PROFILE_PLUGIN !== "0") write(join(harnessRoot, "node_modules", "@pi-harness", "plugin-modlens", "dist", "index.js"), "export default {};\\n");
81
+ if (process.env.PACKED_PACKAGE_FIXTURE_PROFILE_ENTRY !== "0") write(join(harnessRoot, "node_modules", "@pi-harness", "host-webserver", "dist", "index.js"), "export default {};\\n");
82
+ if (process.env.PACKED_PACKAGE_FIXTURE_BUNDLED_PLUGIN === "1") write(join(harnessRoot, "node_modules", "@pi-harness", "plugin-modlens", "dist", "index.js"), "export default {};\\n");
80
83
  write(join(harnessRoot, "node_modules", "@pi-harness", "core", "dist", "index.js"), process.env.PACKED_PACKAGE_FIXTURE_CORE);
81
84
  write(join(harnessRoot, "node_modules", "@pi-harness", "core", "dist", "plugin-resolve.js"), process.env.PACKED_PACKAGE_FIXTURE_RESOLVE);
82
85
  if (process.env.PACKED_PACKAGE_FIXTURE_BINS === "1") for (const target of Object.values(bin)) write(join(harnessRoot, target), "");
@@ -94,8 +97,9 @@ const runSmokeTest = async (
94
97
  fakeNpm: string,
95
98
  bins: boolean,
96
99
  marketplace = true,
97
- profilePlugin = true,
100
+ profileEntry = true,
98
101
  shippedDependency = false,
102
+ bundledPlugin = false,
99
103
  ): ReturnType<typeof execFileAsync> =>
100
104
  execFileAsync(process.execPath, [resolve(repositoryRoot, "scripts/test-packed-package.mjs")], {
101
105
  env: {
@@ -105,8 +109,9 @@ const runSmokeTest = async (
105
109
  PACKED_PACKAGE_FIXTURE_CORE: fakeCoreSource,
106
110
  PACKED_PACKAGE_FIXTURE_RESOLVE: fakeResolveSource,
107
111
  PACKED_PACKAGE_FIXTURE_MARKETPLACE: marketplace ? "1" : "0",
108
- PACKED_PACKAGE_FIXTURE_PROFILE_PLUGIN: profilePlugin ? "1" : "0",
112
+ PACKED_PACKAGE_FIXTURE_PROFILE_ENTRY: profileEntry ? "1" : "0",
109
113
  PACKED_PACKAGE_FIXTURE_SHIPPED_DEP: shippedDependency ? "1" : "0",
114
+ PACKED_PACKAGE_FIXTURE_BUNDLED_PLUGIN: bundledPlugin ? "1" : "0",
110
115
  },
111
116
  });
112
117
 
@@ -130,11 +135,18 @@ describe("packed package smoke test", () => {
130
135
  await expect(runSmokeTest(fakeNpm, false)).rejects.toThrow(/missing the pi-harness entrypoint \.\/apps\/web\/server-dist\/bin\.js/u);
131
136
  });
132
137
 
133
- it("rejects an install whose shipped profile enables a plugin the tarball never brings in", async () => {
138
+ it("rejects an install whose shipped profile enables an entry the tarball never brings in", async () => {
134
139
  // The 0.1.29 launcher shipped a profile naming 45 packages it did not depend on, so a clean install failed to boot on the first entry the loader could not import.
135
140
  const fakeNpm = await createFakeNpm();
136
141
 
137
- await expect(runSmokeTest(fakeNpm, true, true, false)).rejects.toThrow(/enables 1 plugin\(s\) the install does not contain: @pi-harness\/plugin-modlens/u);
142
+ await expect(runSmokeTest(fakeNpm, true, true, false)).rejects.toThrow(/enables 1 entr\(ies\) the install does not contain: @pi-harness\/host-webserver/u);
143
+ });
144
+
145
+ it("rejects an install whose shipped profile bundles a pluggable plugin", async () => {
146
+ // Official plugins are installed from the plugin center like community ones, so a profile entry for one would hand every fresh install a plugin the user never asked for.
147
+ const fakeNpm = await createFakeNpm();
148
+
149
+ await expect(runSmokeTest(fakeNpm, true, true, true, false, true)).rejects.toThrow(/enables 1 pluggable plugin\(s\): @pi-harness\/plugin-modlens/u);
138
150
  });
139
151
 
140
152
  it("rejects an install whose shipped workspace depends on a package npm can never fetch", async () => {
@@ -98,8 +98,8 @@ describe("release contract", () => {
98
98
  expect(core.files).not.toContain("profiles");
99
99
  });
100
100
 
101
- test("the published launcher declares every plugin package its shipped profiles load", () => {
102
- // Every plugin in a shipped profile is now an ordinary npm package, so the package a user installs has to pull each one like any other dependency.
101
+ test("the published launcher declares every package its shipped profiles load, and no plugin", () => {
102
+ // A profile entry is imported by its bare specifier, so the package a user installs has to pull the infrastructure each entry names. Pluggable plugins are the exception by design: they are ordinary npm packages installed from the plugin center, so bundling one would put it in every install whether the user wanted it or not.
103
103
  const root = readJson("package.json") as { name: string; dependencies: Record<string, string> };
104
104
  const required = new Map<string, string>();
105
105
  for (const file of sourceFiles(resolve(repositoryRoot, "packages/cli/profiles"), ".yml")) {
@@ -107,8 +107,12 @@ describe("release contract", () => {
107
107
  required.set(packageNameOf(match[1] ?? ""), relative(repositoryRoot, file));
108
108
  }
109
109
  const external = [...required].filter(([name]) => !name.includes(":"));
110
- expect(external.filter(([name]) => name.startsWith("@pi-harness/plugin-")).length).toBeGreaterThan(0);
111
- for (const [name, file] of external) expect(root.dependencies[name], `${name} (loaded by ${file}) is not declared by ${root.name}`).toBeDefined();
110
+ expect(external.length).toBeGreaterThan(0);
111
+ for (const [name, file] of external) {
112
+ expect(name.startsWith("@pi-harness/plugin-"), `${name} (loaded by ${file}) is a pluggable plugin, which a shipped profile must not enable`).toBe(false);
113
+ expect(root.dependencies[name], `${name} (loaded by ${file}) is not declared by ${root.name}`).toBeDefined();
114
+ }
115
+ expect(Object.keys(root.dependencies).filter((name) => name.startsWith("@pi-harness/plugin-"))).toEqual([]);
112
116
  });
113
117
 
114
118
  test("the plugin API declares every package its sources import as a peer", () => {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/web-app",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "private": true,
5
5
  "description": "Patchable Web application bundle for Pi Harness",
6
6
  "homepage": "https://github.com/pi-harness/pi-harness#readme",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/cli",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "private": true,
5
5
  "description": "Plugin-first Pi Harness CLI",
6
6
  "homepage": "https://github.com/pi-harness/pi-harness#readme",
@@ -26,97 +26,10 @@
26
26
  - bash
27
27
  - edit
28
28
  - write
29
- - id: agent-teams
30
- name: "@pi-harness/plugin-agent-teams"
31
- config: {}
32
- - id: modlens
33
- name: "@pi-harness/plugin-modlens"
34
- config: {}
35
- - id: vision-toolkit
36
- name: "@pi-harness/plugin-vision-toolkit"
37
- config: {}
38
- - id: git-time-capsule
39
- name: "@pi-harness/plugin-git-time-capsule"
40
- config: {}
41
- - id: dependency-checker
42
- name: "@pi-harness/plugin-dependency-checker"
43
- config: {}
44
- - id: at-file
45
- name: "@pi-harness/plugin-at-file"
46
- config: {}
47
- - id: test-harness
48
- name: "@pi-harness/plugin-test-harness"
49
- config:
50
- timeoutMs: 120000
51
- - id: session-insights
52
- name: "@pi-harness/plugin-session-insights"
53
- config: {}
54
- - id: readme-gen
55
- name: "@pi-harness/plugin-readme-gen"
56
- config: {}
57
- - id: i18n-pair
58
- name: "@pi-harness/plugin-i18n-pair"
59
- config: {}
60
- - id: cleaner
61
- name: "@pi-harness/plugin-cleaner"
62
- config: {}
63
- - id: sql-lens
64
- name: "@pi-harness/plugin-sql-lens"
65
- config:
66
- timeoutMs: 5000
67
- - id: docker-sandbox
68
- name: "@pi-harness/plugin-docker-sandbox"
69
- config: {}
70
- - id: mcp-client
71
- name: "@pi-harness/plugin-mcp-client"
72
- config: {}
73
- - id: browser-fetch
74
- name: "@pi-harness/plugin-browser-fetch"
75
- config: {}
76
- - id: browser-session
77
- name: "@pi-harness/plugin-browser-session"
78
- config: {}
79
- - id: plugin-stars
80
- name: "@pi-harness/plugin-plugin-stars"
81
- config: {}
82
- - id: yaml-validator
83
- name: "@pi-harness/plugin-yaml-validator"
84
- config: {}
85
- - id: plugin-dev
86
- name: "@pi-harness/plugin-plugin-dev"
87
- config: {}
88
- - id: openpets
89
- name: "@pi-harness/plugin-openpets"
90
- config: {}
91
- - id: session-bridge
92
- name: "@pi-harness/plugin-session-bridge"
93
- config: {}
94
- - id: skill-guard
95
- name: "@pi-harness/plugin-skill-guard"
96
- config: {}
97
- - id: recall-unread
98
- name: "@pi-harness/plugin-recall-unread"
99
- config: {}
100
- - id: turn-rewind
101
- name: "@pi-harness/plugin-turn-rewind"
102
- config: {}
103
- - id: context-doctor
104
- name: "@pi-harness/plugin-context-doctor"
105
- config: {}
106
29
  - id: runtime
107
30
  name: "@pi-harness/core/plugins/runtime"
108
31
  config:
109
32
  thinkingLevel: medium
110
- - id: context
111
- name: "@pi-harness/plugin-context"
112
- config: {}
113
- - id: token-guard
114
- name: "@pi-harness/plugin-token-guard"
115
- config:
116
- maxPercent: 90
117
- - id: fail-logger
118
- name: "@pi-harness/plugin-fail-logger"
119
- config: {}
120
33
  - id: stdio
121
34
  name: "@pi-harness/core/plugins/stdio"
122
35
  config: {}
@@ -0,0 +1,122 @@
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: agent-teams
30
+ name: "@pi-harness/plugin-agent-teams"
31
+ config: {}
32
+ - id: modlens
33
+ name: "@pi-harness/plugin-modlens"
34
+ config: {}
35
+ - id: vision-toolkit
36
+ name: "@pi-harness/plugin-vision-toolkit"
37
+ config: {}
38
+ - id: git-time-capsule
39
+ name: "@pi-harness/plugin-git-time-capsule"
40
+ config: {}
41
+ - id: dependency-checker
42
+ name: "@pi-harness/plugin-dependency-checker"
43
+ config: {}
44
+ - id: at-file
45
+ name: "@pi-harness/plugin-at-file"
46
+ config: {}
47
+ - id: test-harness
48
+ name: "@pi-harness/plugin-test-harness"
49
+ config:
50
+ timeoutMs: 120000
51
+ - id: session-insights
52
+ name: "@pi-harness/plugin-session-insights"
53
+ config: {}
54
+ - id: readme-gen
55
+ name: "@pi-harness/plugin-readme-gen"
56
+ config: {}
57
+ - id: i18n-pair
58
+ name: "@pi-harness/plugin-i18n-pair"
59
+ config: {}
60
+ - id: cleaner
61
+ name: "@pi-harness/plugin-cleaner"
62
+ config: {}
63
+ - id: sql-lens
64
+ name: "@pi-harness/plugin-sql-lens"
65
+ config:
66
+ timeoutMs: 5000
67
+ - id: docker-sandbox
68
+ name: "@pi-harness/plugin-docker-sandbox"
69
+ config: {}
70
+ - id: mcp-client
71
+ name: "@pi-harness/plugin-mcp-client"
72
+ config: {}
73
+ - id: browser-fetch
74
+ name: "@pi-harness/plugin-browser-fetch"
75
+ config: {}
76
+ - id: browser-session
77
+ name: "@pi-harness/plugin-browser-session"
78
+ config: {}
79
+ - id: plugin-stars
80
+ name: "@pi-harness/plugin-plugin-stars"
81
+ config: {}
82
+ - id: yaml-validator
83
+ name: "@pi-harness/plugin-yaml-validator"
84
+ config: {}
85
+ - id: plugin-dev
86
+ name: "@pi-harness/plugin-plugin-dev"
87
+ config: {}
88
+ - id: openpets
89
+ name: "@pi-harness/plugin-openpets"
90
+ config: {}
91
+ - id: session-bridge
92
+ name: "@pi-harness/plugin-session-bridge"
93
+ config: {}
94
+ - id: skill-guard
95
+ name: "@pi-harness/plugin-skill-guard"
96
+ config: {}
97
+ - id: recall-unread
98
+ name: "@pi-harness/plugin-recall-unread"
99
+ config: {}
100
+ - id: turn-rewind
101
+ name: "@pi-harness/plugin-turn-rewind"
102
+ config: {}
103
+ - id: context-doctor
104
+ name: "@pi-harness/plugin-context-doctor"
105
+ config: {}
106
+ - id: runtime
107
+ name: "@pi-harness/core/plugins/runtime"
108
+ config:
109
+ thinkingLevel: medium
110
+ - id: context
111
+ name: "@pi-harness/plugin-context"
112
+ config: {}
113
+ - id: token-guard
114
+ name: "@pi-harness/plugin-token-guard"
115
+ config:
116
+ maxPercent: 90
117
+ - id: fail-logger
118
+ name: "@pi-harness/plugin-fail-logger"
119
+ config: {}
120
+ - id: stdio
121
+ name: "@pi-harness/core/plugins/stdio"
122
+ config: {}