Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/release-loop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,20 @@ jobs:
npm install "$RUNNER_TEMP"/onkernel-loop-*.tgz
cat > smoke.mjs <<'NODE'
import { compileLoopToolCatalog, formatBrowserActResult, loop } from "@onkernel/loop";
import { attach, getLoopModel, NodeExecutionEnv } from "@onkernel/loop/pi";
import * as loopPi from "@onkernel/loop/pi";

for (const [name, value] of Object.entries({ compileLoopToolCatalog, formatBrowserActResult, attach, getLoopModel, NodeExecutionEnv })) {
const { attach, createLoopModels, getLoopModel } = loopPi;
for (const [name, value] of Object.entries({ compileLoopToolCatalog, formatBrowserActResult, attach, createLoopModels, getLoopModel })) {
if (typeof value !== "function") {
throw new Error(`expected ${name} to be a function, got ${typeof value}`);
}
}
if (typeof loop !== "object" || loop === null) {
throw new Error(`expected loop to be an object, got ${typeof loop}`);
}
if ("Agent" in loopPi || "NodeExecutionEnv" in loopPi) {
throw new Error("expected @onkernel/loop/pi not to re-export pi-agent-core");
}
console.log("ESM import smoke OK");
NODE
node smoke.mjs
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ plain pi objects. Nothing here is a Loop type you have to learn:

```ts
import Kernel from "@onkernel/sdk";
import { Agent } from "@earendil-works/pi-agent-core";
import { loop } from "@onkernel/loop";
import { Agent, attach } from "@onkernel/loop/pi";
import { attach } from "@onkernel/loop/pi";

const client = new Kernel({ apiKey: process.env.KERNEL_API_KEY! });
const browser = await client.browsers.create({ stealth: true });
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 11 additions & 13 deletions packages/loop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ extension described under [pi extension](#pi-extension).
## Install

```bash
npm install @onkernel/loop @onkernel/sdk
npm install @onkernel/loop @onkernel/sdk @earendil-works/pi-agent-core
```

Requires Node 22.19 or newer, `KERNEL_API_KEY` for browser execution, and the
Expand All @@ -31,8 +31,9 @@ you construct whatever pi agent you want with them. There is no agent class here

```ts
import Kernel from "@onkernel/sdk";
import { Agent } from "@earendil-works/pi-agent-core";
import { loop } from "@onkernel/loop";
import { Agent, attach } from "@onkernel/loop/pi";
import { attach } from "@onkernel/loop/pi";

const client = new Kernel({ apiKey: process.env.KERNEL_API_KEY! });
const browser = await client.browsers.create({ stealth: true });
Expand Down Expand Up @@ -72,8 +73,9 @@ package owns that are pi event handlers rather than constructor options, and
points the handle's `models` at this catalog:

```ts
import { AgentHarness, InMemorySessionRepo } from "@earendil-works/pi-agent-core";
import { loop } from "@onkernel/loop";
import { AgentHarness, attach, InMemorySessionRepo } from "@onkernel/loop/pi";
import { attach } from "@onkernel/loop/pi";

const session = await new InMemorySessionRepo().create();
const kb = attach({ client, browser });
Expand Down Expand Up @@ -105,8 +107,9 @@ the new one. Changing the model or the tool list compiles a new pair; nothing
mutates in place, and one shared execution-resource pool survives every change,
so browser refs, tabs, connections, and translator state are not reset.

`@onkernel/loop/pi` re-exports pi-agent-core's session, skill, prompt-template,
compaction, and execution-environment primitives used with the harness.
`@onkernel/loop/pi` does not re-export pi: install `@earendil-works/pi-agent-core`
and import its session, skill, prompt-template, compaction, and
execution-environment primitives directly, as these examples do.

### Tool context

Expand All @@ -116,15 +119,10 @@ and pi delivers the exact object (or the result of a zero-argument provider)
to every tool call:

```ts
import { AgentHarness, createBashTool, createReadTool, type ExecutionToolContext } from "@earendil-works/pi-agent-core";
import { NodeExecutionEnv } from "@earendil-works/pi-agent-core/node";
import { loop } from "@onkernel/loop";
import {
AgentHarness,
attach,
NodeExecutionEnv,
createBashTool,
createReadTool,
type ExecutionToolContext,
} from "@onkernel/loop/pi";
import { attach } from "@onkernel/loop/pi";

const compiled = kb.compile<ExecutionToolContext>({
model: "openai:gpt-5.6-sol",
Expand Down
3 changes: 2 additions & 1 deletion packages/loop/examples/agent-openai-smoke.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Kernel from "@onkernel/sdk";
import { loop } from "../src/index";
import { Agent, attach, type LoopModelRef, requireLoopEnvApiKeyForModel } from "../src/pi/index";
import { Agent } from "@earendil-works/pi-agent-core";
import { attach, type LoopModelRef, requireLoopEnvApiKeyForModel } from "../src/pi/index";
import { logAgentEvent, logAssistant } from "./shared/logging";
import { SCENARIOS } from "./shared/scenarios";

Expand Down
3 changes: 2 additions & 1 deletion packages/loop/examples/agent-provider-matrix.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Kernel from "@onkernel/sdk";
import { Agent, attach, type LoopModelRef, requireLoopEnvApiKeyForModel } from "../src/pi/index";
import { Agent } from "@earendil-works/pi-agent-core";
import { attach, type LoopModelRef, requireLoopEnvApiKeyForModel } from "../src/pi/index";
import { logAgentEvent, logAssistant } from "./shared/logging";
import { SCENARIOS } from "./shared/scenarios";
import { toolsForModel } from "./shared/tools";
Expand Down
3 changes: 2 additions & 1 deletion packages/loop/examples/anthropic-native-smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
// CONFIG selects the requested catalog; nothing is inferred or appended.
import Kernel from "@onkernel/sdk";
import { loop, type LoopAgentTool } from "../src/index";
import { Agent, attach, type LoopModelRef, requireLoopEnvApiKeyForModel } from "../src/pi/index";
import { Agent } from "@earendil-works/pi-agent-core";
import { attach, type LoopModelRef, requireLoopEnvApiKeyForModel } from "../src/pi/index";
import { logAgentEvent, logAssistant } from "./shared/logging";

const modelRef = (process.env.MODEL_REF as LoopModelRef | undefined) ?? "anthropic:claude-opus-5";
Expand Down
9 changes: 2 additions & 7 deletions packages/loop/examples/harness-openai-smoke.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import Kernel from "@onkernel/sdk";
import { loop } from "../src/index";
import {
AgentHarness,
attach,
InMemorySessionRepo,
type LoopModelRef,
requireLoopEnvApiKeyForModel,
} from "../src/pi/index";
import { AgentHarness, InMemorySessionRepo } from "@earendil-works/pi-agent-core";
import { attach, type LoopModelRef, requireLoopEnvApiKeyForModel } from "../src/pi/index";
import { logAgentEvent, logAssistant } from "./shared/logging";
import { SCENARIOS } from "./shared/scenarios";

Expand Down
9 changes: 2 additions & 7 deletions packages/loop/examples/harness-provider-matrix.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import Kernel from "@onkernel/sdk";
import {
AgentHarness,
attach,
InMemorySessionRepo,
type LoopModelRef,
requireLoopEnvApiKeyForModel,
} from "../src/pi/index";
import { AgentHarness, InMemorySessionRepo } from "@earendil-works/pi-agent-core";
import { attach, type LoopModelRef, requireLoopEnvApiKeyForModel } from "../src/pi/index";
import { logAgentEvent, logAssistant } from "./shared/logging";
import { SCENARIOS } from "./shared/scenarios";
import { toolsForModel } from "./shared/tools";
Expand Down
2 changes: 1 addition & 1 deletion packages/loop/examples/shared/logging.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AgentEvent, AgentHarnessEvent } from "../../src/pi/index";
import type { AgentEvent, AgentHarnessEvent } from "@earendil-works/pi-agent-core";

type AssistantLike = {
content: Array<{ type: string; text?: string }>;
Expand Down
1 change: 1 addition & 0 deletions packages/loop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"typebox": "1.3.7"
},
"peerDependencies": {
"@earendil-works/pi-agent-core": "0.83.0",
"@earendil-works/pi-coding-agent": "*"
},
"peerDependenciesMeta": {
Expand Down
3 changes: 0 additions & 3 deletions packages/loop/src/pi/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
export * from "@earendil-works/pi-agent-core";
Comment thread
cursor[bot] marked this conversation as resolved.
export { NodeExecutionEnv } from "@earendil-works/pi-agent-core/node";

export * from "./api-keys";
export * from "./models";
export {
Expand Down
8 changes: 5 additions & 3 deletions packages/loop/test/attach-session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import {
AgentHarness,
type AgentMessage,
type AgentTool,
InMemorySessionRepo,
type Session,
type StreamFn,
} from "@earendil-works/pi-agent-core";
import {
attach,
createLoopModels,
getLoopModel,
GOOGLE_INTERACTIONS_API,
InMemorySessionRepo,
type LoopModelInput,
type Session,
type StreamFn,
} from "../src/pi/index";
import { type KernelBrowser, loop, type LoopHarnessTool } from "../src/index";
import type Kernel from "@onkernel/sdk";
Expand Down
5 changes: 1 addition & 4 deletions packages/loop/test/attach.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,13 @@ import {
createAssistantMessageEventStream,
type Model,
} from "@earendil-works/pi-ai";
import { Agent, AgentHarness, InMemorySessionRepo, type StreamFn } from "@earendil-works/pi-agent-core";
import {
Agent,
AgentHarness,
attach,
createLoopModels,
GOOGLE_INTERACTIONS_API,
InMemorySessionRepo,
type LoopSimpleStreamOptions,
OPENAI_COMPUTER_USE_API,
type StreamFn,
} from "../src/pi/index";
import { type KernelBrowser, loop } from "../src/index";
import type Kernel from "@onkernel/sdk";
Expand Down
4 changes: 2 additions & 2 deletions packages/loop/test/e2e.live.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import {
AgentHarness,
type AgentHarnessEvent,
type AgentMessage,
attach,
InMemorySessionRepo,
} from "../src/pi/index";
} from "@earendil-works/pi-agent-core";
import { attach } from "../src/pi/index";
import { loop } from "../src/index";

const LIVE = process.env.LOOP_E2E_LIVE === "1";
Expand Down
7 changes: 3 additions & 4 deletions packages/loop/test/harness-context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@ import { type AssistantMessage, createAssistantMessageEventStream, type Model }
import {
AgentHarness,
type AgentHarnessTool,
attach,
createBashTool,
createEditTool,
createLoopModels,
createReadTool,
createWriteTool,
type ExecutionToolContext,
InMemorySessionRepo,
NodeExecutionEnv,
type StreamFn,
} from "../src/pi/index";
} from "@earendil-works/pi-agent-core";
import { NodeExecutionEnv } from "@earendil-works/pi-agent-core/node";
import { attach, createLoopModels } from "../src/pi/index";
import type Kernel from "@onkernel/sdk";
import type { KernelBrowser } from "../src/index";

Expand Down
27 changes: 27 additions & 0 deletions packages/loop/test/pi-export-surface.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { describe, expect, it } from "vitest";
import * as pi from "../src/pi/index";

// ./pi exports only loop-owned symbols. pi-agent-core is composed with, not
// re-exported: consumers construct Agent/AgentHarness from their own
// pi-agent-core install.
describe("./pi export surface", () => {
it("exports the loop pi binding", () => {
for (const name of ["attach", "createLoopModels", "loopModels", "getLoopModel", "listLoopModels", "loopApiKeyEnvVarsForProvider"]) {
expect(pi, name).toHaveProperty(name);
}
});

it("does not re-export pi-agent-core", () => {
const leaked = [
"Agent",
"AgentHarness",
"InMemorySessionRepo",
"NodeExecutionEnv",
"createBashTool",
"createEditTool",
"createReadTool",
"createWriteTool",
].filter((name) => name in pi);
expect(leaked).toEqual([]);
});
});
10 changes: 6 additions & 4 deletions packages/loop/test/published-declarations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,16 @@ import {
import {
Agent,
AgentHarness,
attach,
getLoopModel,
InMemorySessionRepo,
NodeExecutionEnv,
createBashTool,
createEditTool,
createReadTool,
createWriteTool,
type AgentHarnessTool,
type ExecutionToolContext,
} from "@onkernel/loop/pi";
} from "@earendil-works/pi-agent-core";
import { NodeExecutionEnv } from "@earendil-works/pi-agent-core/node";
import { attach, getLoopModel } from "@onkernel/loop/pi";
import type Kernel from "@onkernel/sdk";

declare const browser: KernelBrowser;
Expand Down Expand Up @@ -159,6 +158,9 @@ describe("published declarations", () => {
paths: {
"@onkernel/loop": [join(out, "index.d.ts")],
"@onkernel/loop/pi": [join(out, "pi", "index.d.ts")],
// The "*" fallback resolves a package root through its package.json,
// but not subpath exports; map this one explicitly.
"@earendil-works/pi-agent-core/node": [join(repoRoot, "node_modules", "@earendil-works", "pi-agent-core", "dist", "node.d.ts")],
"*": ["./node_modules/*"],
},
},
Expand Down
3 changes: 2 additions & 1 deletion packages/loop/test/tool-manager.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it, vi } from "vitest";
import { type AgentTool, GOOGLE_INTERACTIONS_API } from "../src/pi/index";
import type { AgentTool } from "@earendil-works/pi-agent-core";
import { GOOGLE_INTERACTIONS_API } from "../src/pi/index";
import {
callerToolIdentity,
type KernelBrowser,
Expand Down
Loading