@shivaedev/agent-pi 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/README.md +55 -1
- package/dist/PiOptions.d.ts +5 -0
- package/dist/PiOptions.d.ts.map +1 -0
- package/dist/PiOptions.js +2 -0
- package/dist/PiOptions.js.map +1 -0
- package/dist/adapters/deliver.d.ts +4 -0
- package/dist/adapters/deliver.d.ts.map +1 -0
- package/dist/adapters/deliver.js +19 -0
- package/dist/adapters/deliver.js.map +1 -0
- package/dist/adapters/effort.d.ts +5 -0
- package/dist/adapters/effort.d.ts.map +1 -0
- package/dist/adapters/effort.js +13 -0
- package/dist/adapters/effort.js.map +1 -0
- package/dist/adapters/failure.d.ts +6 -0
- package/dist/adapters/failure.d.ts.map +1 -0
- package/dist/adapters/failure.js +14 -0
- package/dist/adapters/failure.js.map +1 -0
- package/dist/adapters/listPiModels.d.ts +8 -0
- package/dist/adapters/listPiModels.d.ts.map +1 -0
- package/dist/adapters/listPiModels.js +11 -0
- package/dist/adapters/listPiModels.js.map +1 -0
- package/dist/adapters/mapping.d.ts +9 -0
- package/dist/adapters/mapping.d.ts.map +1 -0
- package/dist/adapters/mapping.js +44 -0
- package/dist/adapters/mapping.js.map +1 -0
- package/dist/adapters/messageEvents.d.ts +9 -0
- package/dist/adapters/messageEvents.d.ts.map +1 -0
- package/dist/adapters/messageEvents.js +41 -0
- package/dist/adapters/messageEvents.js.map +1 -0
- package/dist/adapters/openSession.d.ts +323 -0
- package/dist/adapters/openSession.d.ts.map +1 -0
- package/dist/adapters/openSession.js +41 -0
- package/dist/adapters/openSession.js.map +1 -0
- package/dist/adapters/runtime.d.ts +24 -0
- package/dist/adapters/runtime.d.ts.map +1 -0
- package/dist/adapters/runtime.js +60 -0
- package/dist/adapters/runtime.js.map +1 -0
- package/dist/adapters/textOf.d.ts +3 -0
- package/dist/adapters/textOf.d.ts.map +1 -0
- package/dist/adapters/textOf.js +4 -0
- package/dist/adapters/textOf.js.map +1 -0
- package/dist/makeOperations.d.ts +668 -0
- package/dist/makeOperations.d.ts.map +1 -0
- package/dist/makeOperations.js +55 -0
- package/dist/makeOperations.js.map +1 -0
- package/dist/pi.d.ts +4 -1
- package/dist/pi.d.ts.map +1 -1
- package/dist/pi.js +39 -1
- package/dist/pi.js.map +1 -1
- package/package.json +27 -4
- package/src/PiOptions.ts +4 -0
- package/src/adapters/deliver.ts +24 -0
- package/src/adapters/effort.ts +15 -0
- package/src/adapters/failure.ts +14 -0
- package/src/adapters/listPiModels.ts +16 -0
- package/src/adapters/mapping.ts +50 -0
- package/src/adapters/messageEvents.ts +50 -0
- package/src/adapters/openSession.ts +46 -0
- package/src/adapters/runtime.ts +70 -0
- package/src/adapters/textOf.ts +5 -0
- package/src/makeOperations.ts +75 -0
- package/src/pi.ts +44 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Effect, Exit, Scope, Stream } from "effect";
|
|
2
|
+
import { UnknownAcceptance } from "@shivaedev/agent/failures.ts";
|
|
3
|
+
import { protocolFailure } from "#adapters/failure.ts";
|
|
4
|
+
import { openSession } from "#adapters/openSession.ts";
|
|
5
|
+
import { textOf } from "#adapters/textOf.ts";
|
|
6
|
+
export const makeOperations = Effect.fnUntraced(function* (options) {
|
|
7
|
+
const runs = yield* Effect.sync(() => new Map());
|
|
8
|
+
const evidence = new Map();
|
|
9
|
+
function lookup(receipt) {
|
|
10
|
+
return Effect.suspend(() => {
|
|
11
|
+
const run = runs.get(receipt.operationId);
|
|
12
|
+
return receipt.backend === "pi" && receipt.reference.version === 1 && run?.receipt.reference.value === receipt.reference.value
|
|
13
|
+
? Effect.succeed(run)
|
|
14
|
+
: Effect.fail(protocolFailure("run is not open in this backend scope"));
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
const submit = Effect.fnUntraced(function* (request, resume) {
|
|
18
|
+
const run = yield* openSession(options, request, resume);
|
|
19
|
+
yield* run.prompt(textOf(request.input), "steer").pipe(Effect.mapError((error) => {
|
|
20
|
+
if (error._tag === "Rejected") {
|
|
21
|
+
evidence.set(request.operationId, { evidence: error.message, operationId: request.operationId, type: "not-accepted" });
|
|
22
|
+
return error;
|
|
23
|
+
}
|
|
24
|
+
return new UnknownAcceptance({ backend: "pi", message: error.message, operationId: request.operationId });
|
|
25
|
+
}));
|
|
26
|
+
runs.set(request.operationId, run);
|
|
27
|
+
evidence.set(request.operationId, { receipt: run.receipt, type: "accepted" });
|
|
28
|
+
yield* run.acknowledge;
|
|
29
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
30
|
+
runs.delete(request.operationId);
|
|
31
|
+
}));
|
|
32
|
+
return run.receipt;
|
|
33
|
+
});
|
|
34
|
+
const open = Effect.fnUntraced(function* (request, resume) {
|
|
35
|
+
if (evidence.has(request.operationId)) {
|
|
36
|
+
return yield* Effect.fail(protocolFailure("operation already attempted; reconcile instead"));
|
|
37
|
+
}
|
|
38
|
+
evidence.set(request.operationId, { operationId: request.operationId, reason: "submission has no acceptance evidence yet", type: "uncertain" });
|
|
39
|
+
const runScope = yield* Scope.fork(yield* Effect.scope);
|
|
40
|
+
return yield* submit(request, resume).pipe(Effect.provideService(Scope.Scope, runScope), Effect.onError((cause) => Scope.close(runScope, Exit.failCause(cause))));
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
events: (receipt) => Stream.unwrap(lookup(receipt).pipe(Effect.map((run) => run.events))),
|
|
44
|
+
inspect: (receipt) => lookup(receipt).pipe(Effect.flatMap((run) => run.inspect), Effect.catchTag("ProtocolFailure", () => Effect.succeed({ reason: "no live execution evidence in this backend scope", type: "missing" }))),
|
|
45
|
+
lookup,
|
|
46
|
+
open,
|
|
47
|
+
reconcile: (operationId) => Effect.sync(() => evidence.get(operationId) ?? {
|
|
48
|
+
operationId,
|
|
49
|
+
reason: "Pi has no operation-id lookup; missing local evidence does not prove non-acceptance",
|
|
50
|
+
type: "uncertain",
|
|
51
|
+
}),
|
|
52
|
+
resume: (reference, request) => reference.version === 1 ? open(request, reference.value) : Effect.fail(protocolFailure("unsupported session reference version")),
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=makeOperations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"makeOperations.js","sourceRoot":"","sources":["../src/makeOperations.ts"],"names":[],"mappings":"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"}
|
package/dist/pi.d.ts
CHANGED
package/dist/pi.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../src/pi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"pi.d.ts","sourceRoot":"","sources":["../src/pi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,OAAO,EAA0B,MAAM,6BAA6B,CAAC;AAI9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,wBAAgB,EAAE,CAAC,OAAO,GAAE,SAAc,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAoChE"}
|
package/dist/pi.js
CHANGED
|
@@ -1,2 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
|
+
import { Backend } from "@shivaedev/agent/backend.ts";
|
|
3
|
+
import { listPiModels } from "#adapters/listPiModels.ts";
|
|
4
|
+
import { textOf } from "#adapters/textOf.ts";
|
|
5
|
+
import { makeOperations } from "#makeOperations.ts";
|
|
6
|
+
export function pi(options = {}) {
|
|
7
|
+
return Layer.effect(Backend, Effect.gen(function* () {
|
|
8
|
+
const operations = yield* makeOperations(options);
|
|
9
|
+
return {
|
|
10
|
+
backend: "pi",
|
|
11
|
+
events: operations.events,
|
|
12
|
+
features: {
|
|
13
|
+
imageInput: { reason: "only text input is implemented", support: "unsupported" },
|
|
14
|
+
interruption: {
|
|
15
|
+
implementation: { interrupt: (receipt) => operations.lookup(receipt).pipe(Effect.flatMap((run) => run.interrupt)) },
|
|
16
|
+
support: "supported",
|
|
17
|
+
},
|
|
18
|
+
models: { implementation: { list: listPiModels }, support: "supported" },
|
|
19
|
+
queuedInput: {
|
|
20
|
+
implementation: {
|
|
21
|
+
queue: (receipt, input) => operations.lookup(receipt).pipe(Effect.flatMap((run) => run.prompt(textOf(input), "followUp"))),
|
|
22
|
+
},
|
|
23
|
+
support: "supported",
|
|
24
|
+
},
|
|
25
|
+
sessions: { implementation: { resume: operations.resume }, support: "supported" },
|
|
26
|
+
steering: {
|
|
27
|
+
implementation: {
|
|
28
|
+
steer: (receipt, input) => operations.lookup(receipt).pipe(Effect.flatMap((run) => run.prompt(textOf(input), "steer"))),
|
|
29
|
+
},
|
|
30
|
+
support: "supported",
|
|
31
|
+
},
|
|
32
|
+
transcript: { reason: "no provider history reader is implemented", support: "unsupported" },
|
|
33
|
+
},
|
|
34
|
+
inspect: operations.inspect,
|
|
35
|
+
reconcile: operations.reconcile,
|
|
36
|
+
start: (request) => operations.open(request, undefined),
|
|
37
|
+
};
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
2
40
|
//# sourceMappingURL=pi.js.map
|
package/dist/pi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pi.js","sourceRoot":"","sources":["../src/pi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"pi.js","sourceRoot":"","sources":["../src/pi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,OAAO,EAA0B,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,MAAM,UAAU,EAAE,CAAC,OAAO,GAAc,EAAE;IACzC,OAAO,KAAK,CAAC,MAAM,CAClB,OAAO,EACP,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAClD,OAAO;YACN,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,QAAQ,EAAE;gBACT,UAAU,EAAE,EAAE,MAAM,EAAE,gCAAgC,EAAE,OAAO,EAAE,aAAa,EAAE;gBAChF,YAAY,EAAE;oBACb,cAAc,EAAE,EAAE,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE;oBACnH,OAAO,EAAE,WAAW;iBACpB;gBACD,MAAM,EAAE,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE;gBACxE,WAAW,EAAE;oBACZ,cAAc,EAAE;wBACf,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;qBAC1H;oBACD,OAAO,EAAE,WAAW;iBACpB;gBACD,QAAQ,EAAE,EAAE,cAAc,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE;gBACjF,QAAQ,EAAE;oBACT,cAAc,EAAE;wBACf,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;qBACvH;oBACD,OAAO,EAAE,WAAW;iBACpB;gBACD,UAAU,EAAE,EAAE,MAAM,EAAE,2CAA2C,EAAE,OAAO,EAAE,aAAa,EAAE;aAC3F;YACD,OAAO,EAAE,UAAU,CAAC,OAAO;YAC3B,SAAS,EAAE,UAAU,CAAC,SAAS;YAC/B,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC;SACvD,CAAC;IACH,CAAC,CAAC,CACF,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/agent-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Pi integration for Effect applications",
|
|
5
5
|
"homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/agent-pi#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"@shivaedev/source": "./src/*.ts",
|
|
20
20
|
"types": "./dist/*.d.ts",
|
|
21
21
|
"default": "./dist/*.js"
|
|
22
|
-
}
|
|
22
|
+
},
|
|
23
|
+
"#test/*": "./src/test-support/*"
|
|
23
24
|
},
|
|
24
25
|
"exports": {
|
|
25
26
|
"./*.ts": {
|
|
@@ -32,12 +33,33 @@
|
|
|
32
33
|
"files": [
|
|
33
34
|
"dist",
|
|
34
35
|
"src",
|
|
36
|
+
"!src/**/*.test.ts",
|
|
37
|
+
"!src/**/*.spec.ts",
|
|
38
|
+
"!src/**/test-support",
|
|
35
39
|
"CHANGELOG.md",
|
|
36
40
|
"README.md"
|
|
37
41
|
],
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@shivaedev/agent": "0.3.0"
|
|
44
|
+
},
|
|
38
45
|
"devDependencies": {
|
|
46
|
+
"@earendil-works/pi-coding-agent": "0.84.4",
|
|
47
|
+
"@effect/vitest": "4.0.6",
|
|
48
|
+
"@shivaedev/quality": "0.61.0",
|
|
49
|
+
"@shivaedev/test-story": "0.39.0",
|
|
39
50
|
"@types/node": "24.19.2",
|
|
40
|
-
"
|
|
51
|
+
"effect": "4.0.6",
|
|
52
|
+
"typescript": "7.0.2",
|
|
53
|
+
"vitest": "5.0.5"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@earendil-works/pi-coding-agent": "0.84.4",
|
|
57
|
+
"effect": "4.0.6"
|
|
58
|
+
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"@earendil-works/pi-coding-agent": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
41
63
|
},
|
|
42
64
|
"engines": {
|
|
43
65
|
"node": ">=24"
|
|
@@ -48,7 +70,8 @@
|
|
|
48
70
|
},
|
|
49
71
|
"scripts": {
|
|
50
72
|
"build": "node --eval \"import('node:fs').then(({ rmSync }) => rmSync('dist', { force: true, recursive: true }))\" && tsc --project tsconfig.build.json",
|
|
51
|
-
"ready": "pnpm typecheck && pnpm build",
|
|
73
|
+
"ready": "pnpm typecheck && pnpm test && pnpm build",
|
|
74
|
+
"test": "vitest run --configLoader native",
|
|
52
75
|
"typecheck": "tsc --noEmit"
|
|
53
76
|
}
|
|
54
77
|
}
|
package/src/PiOptions.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import type { BackendFailure } from "@shivaedev/agent/failures.ts";
|
|
4
|
+
import { protocolFailure, rejected } from "#adapters/failure.ts";
|
|
5
|
+
|
|
6
|
+
export function deliver(session: AgentSession, text: string, delivery: "followUp" | "steer") {
|
|
7
|
+
return Effect.callback<void, BackendFailure>((resume) => {
|
|
8
|
+
let preflight: boolean | undefined;
|
|
9
|
+
session
|
|
10
|
+
.prompt(text, {
|
|
11
|
+
preflightResult: (ok) => {
|
|
12
|
+
preflight = ok;
|
|
13
|
+
if (ok) {
|
|
14
|
+
resume(Effect.void);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
streamingBehavior: delivery,
|
|
18
|
+
})
|
|
19
|
+
.then(
|
|
20
|
+
() => resume(Effect.void),
|
|
21
|
+
(error: unknown) => resume(Effect.fail(preflight === false ? rejected(error) : protocolFailure(error))),
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { rejected } from "#adapters/failure.ts";
|
|
3
|
+
|
|
4
|
+
export const piEfforts = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
5
|
+
export type PiThinkingLevel = (typeof piEfforts)[number];
|
|
6
|
+
|
|
7
|
+
export function thinkingLevel(effort: string | undefined) {
|
|
8
|
+
if (effort === undefined) {
|
|
9
|
+
return Effect.succeed(undefined);
|
|
10
|
+
}
|
|
11
|
+
const level = piEfforts.find((candidate) => candidate === effort);
|
|
12
|
+
return level === undefined
|
|
13
|
+
? Effect.fail(rejected(`effort ${effort} is not a thinking level Pi offers; choose one of ${piEfforts.join(", ")}`))
|
|
14
|
+
: Effect.succeed(level);
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BackendUnavailable, ProtocolFailure, Rejected } from "@shivaedev/agent/failures.ts";
|
|
2
|
+
|
|
3
|
+
export function describe(detail: unknown): string {
|
|
4
|
+
return detail instanceof Error ? detail.message : String(detail);
|
|
5
|
+
}
|
|
6
|
+
export function unavailable(detail: unknown) {
|
|
7
|
+
return new BackendUnavailable({ backend: "pi", message: describe(detail) });
|
|
8
|
+
}
|
|
9
|
+
export function protocolFailure(detail: unknown) {
|
|
10
|
+
return new ProtocolFailure({ backend: "pi", message: describe(detail) });
|
|
11
|
+
}
|
|
12
|
+
export function rejected(detail: unknown) {
|
|
13
|
+
return new Rejected({ backend: "pi", message: describe(detail) });
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { piEfforts } from "#adapters/effort.ts";
|
|
3
|
+
import { unavailable } from "#adapters/failure.ts";
|
|
4
|
+
import { loadSdk } from "#adapters/runtime.ts";
|
|
5
|
+
|
|
6
|
+
export const listPiModels = loadSdk.pipe(
|
|
7
|
+
Effect.flatMap((sdk) => Effect.tryPromise({ catch: unavailable, try: async () => (await sdk.ModelRuntime.create()).getAvailable() })),
|
|
8
|
+
Effect.map((models) =>
|
|
9
|
+
models.map((model, index) => ({
|
|
10
|
+
efforts: piEfforts,
|
|
11
|
+
id: `${model.provider}/${model.id}`,
|
|
12
|
+
isDefault: index === 0,
|
|
13
|
+
name: `${model.name} (${model.provider})`,
|
|
14
|
+
})),
|
|
15
|
+
),
|
|
16
|
+
);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { AgentSessionEvent } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Option, Schema } from "effect";
|
|
3
|
+
import type { RunEvent } from "@shivaedev/agent/events.ts";
|
|
4
|
+
import { messageEvents } from "./messageEvents.ts";
|
|
5
|
+
|
|
6
|
+
export function rawOf(kind: string, payload: unknown) {
|
|
7
|
+
return { kind, payload: JSON.stringify(payload), source: "pi" };
|
|
8
|
+
}
|
|
9
|
+
const decodeOutput = Schema.decodeUnknownOption(
|
|
10
|
+
Schema.Struct({ content: Schema.Array(Schema.Struct({ text: Schema.optional(Schema.String), type: Schema.String })) }),
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export function toAgentEvents(event: AgentSessionEvent): RunEvent[] {
|
|
14
|
+
const raw = rawOf(event.type, event);
|
|
15
|
+
switch (event.type) {
|
|
16
|
+
case "agent_start":
|
|
17
|
+
return [{ raw, state: "running", type: "execution" }];
|
|
18
|
+
case "agent_end": {
|
|
19
|
+
if (event.willRetry) {
|
|
20
|
+
return [{ raw, type: "raw" }];
|
|
21
|
+
}
|
|
22
|
+
const ending = event.messages.filter((message) => message.role === "assistant").at(-1);
|
|
23
|
+
const state = endState(ending?.stopReason);
|
|
24
|
+
return [{ raw, state, type: "execution" }];
|
|
25
|
+
}
|
|
26
|
+
case "message_end":
|
|
27
|
+
return messageEvents(event.message, raw);
|
|
28
|
+
case "tool_execution_start":
|
|
29
|
+
return [{ input: JSON.stringify(event.args), name: event.toolName, raw, toolId: event.toolCallId, type: "tool.started" }];
|
|
30
|
+
case "tool_execution_end": {
|
|
31
|
+
const output = Option.match(decodeOutput(event.result), {
|
|
32
|
+
onNone: () => "",
|
|
33
|
+
onSome: ({ content }) => content.flatMap((part) => (part.type === "text" && part.text !== undefined ? [part.text] : [])).join("\n"),
|
|
34
|
+
});
|
|
35
|
+
return [{ ok: !event.isError, output, raw, toolId: event.toolCallId, type: "tool.completed" }];
|
|
36
|
+
}
|
|
37
|
+
default:
|
|
38
|
+
return [{ raw, type: "raw" }];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function endState(reason: string | undefined) {
|
|
43
|
+
if (reason === "aborted") {
|
|
44
|
+
return "interrupted";
|
|
45
|
+
}
|
|
46
|
+
if (reason === "error") {
|
|
47
|
+
return "failed";
|
|
48
|
+
}
|
|
49
|
+
return "completed";
|
|
50
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { AgentSessionEvent } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { RunEvent } from "@shivaedev/agent/events.ts";
|
|
3
|
+
|
|
4
|
+
type PiMessage = Extract<AgentSessionEvent, { type: "message_end" }>["message"];
|
|
5
|
+
type Raw = NonNullable<RunEvent["raw"]>;
|
|
6
|
+
|
|
7
|
+
export function messageEvents(message: PiMessage, raw: Raw): RunEvent[] {
|
|
8
|
+
if (message.role === "user") {
|
|
9
|
+
const text =
|
|
10
|
+
typeof message.content === "string" ? message.content : message.content.flatMap((part) => (part.type === "text" ? [part.text] : [])).join("\n");
|
|
11
|
+
return [{ parts: [{ text, type: "text" }], raw, role: "user", type: "message" }];
|
|
12
|
+
}
|
|
13
|
+
if (message.role !== "assistant") {
|
|
14
|
+
return [{ raw, type: "raw" }];
|
|
15
|
+
}
|
|
16
|
+
const events: RunEvent[] = [];
|
|
17
|
+
const thinking = message.content.flatMap((part) => (part.type === "thinking" ? [part.thinking] : [])).join("\n");
|
|
18
|
+
const text = message.content.flatMap((part) => (part.type === "text" ? [part.text] : [])).join("\n");
|
|
19
|
+
if (thinking !== "") {
|
|
20
|
+
events.push({ raw, text: thinking, type: "thinking" });
|
|
21
|
+
}
|
|
22
|
+
if (text !== "") {
|
|
23
|
+
events.push({ parts: [{ text, type: "text" }], raw, role: "agent", type: "message" });
|
|
24
|
+
}
|
|
25
|
+
const model = `${message.provider}/${message.model}`;
|
|
26
|
+
events.push(
|
|
27
|
+
{
|
|
28
|
+
cacheReadTokens: message.usage.cacheRead,
|
|
29
|
+
cacheWriteTokens: message.usage.cacheWrite,
|
|
30
|
+
inputTokens: message.usage.input,
|
|
31
|
+
model,
|
|
32
|
+
outputTokens: message.usage.output,
|
|
33
|
+
raw,
|
|
34
|
+
scope: "delta",
|
|
35
|
+
type: "usage",
|
|
36
|
+
unit: "turn",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
amount: message.usage.cost.total,
|
|
40
|
+
currency: "USD",
|
|
41
|
+
model,
|
|
42
|
+
raw,
|
|
43
|
+
scope: "delta",
|
|
44
|
+
source: "provider",
|
|
45
|
+
type: "cost",
|
|
46
|
+
unit: "turn",
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
return events;
|
|
50
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type Cause, Effect, Queue, Stream } from "effect";
|
|
2
|
+
import type { RunEvent } from "@shivaedev/agent/events.ts";
|
|
3
|
+
import type { Observation, Receipt, StartRequest } from "@shivaedev/agent/run.ts";
|
|
4
|
+
import { deliver } from "#adapters/deliver.ts";
|
|
5
|
+
import { unavailable } from "#adapters/failure.ts";
|
|
6
|
+
import { rawOf, toAgentEvents } from "#adapters/mapping.ts";
|
|
7
|
+
import { openRuntime } from "#adapters/runtime.ts";
|
|
8
|
+
import type { PiOptions } from "#PiOptions.ts";
|
|
9
|
+
|
|
10
|
+
export const openSession = Effect.fnUntraced(function* (options: PiOptions, request: StartRequest, resume: string | undefined) {
|
|
11
|
+
const { session, sessionFile } = yield* openRuntime(options, request, resume);
|
|
12
|
+
const output = yield* Queue.unbounded<RunEvent, Cause.Done>();
|
|
13
|
+
let observation: Observation = { state: "accepted", type: "known" };
|
|
14
|
+
function emit(event: RunEvent) {
|
|
15
|
+
if (event.type === "execution") {
|
|
16
|
+
observation = { state: event.state, type: "known" };
|
|
17
|
+
}
|
|
18
|
+
Queue.offerUnsafe(output, event);
|
|
19
|
+
}
|
|
20
|
+
const unsubscribe = session.subscribe((event) => {
|
|
21
|
+
for (const mapped of toAgentEvents(event)) {
|
|
22
|
+
emit(mapped);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
yield* Effect.addFinalizer(() =>
|
|
26
|
+
Effect.sync(() => {
|
|
27
|
+
unsubscribe();
|
|
28
|
+
Queue.endUnsafe(output);
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
const receipt: Receipt = {
|
|
32
|
+
backend: "pi",
|
|
33
|
+
operationId: request.operationId,
|
|
34
|
+
reference: { value: sessionFile, version: 1 },
|
|
35
|
+
session: { value: sessionFile, version: 1 },
|
|
36
|
+
};
|
|
37
|
+
emit({ raw: rawOf("session/opened", { sessionFile, sessionId: session.sessionId }), reference: sessionFile, type: "session.opened" });
|
|
38
|
+
return {
|
|
39
|
+
acknowledge: Effect.sync(() => emit({ receipt, type: "acknowledged" })),
|
|
40
|
+
events: Stream.fromQueue(output),
|
|
41
|
+
inspect: Effect.sync(() => observation),
|
|
42
|
+
interrupt: Effect.tryPromise({ catch: unavailable, try: () => session.abort() }),
|
|
43
|
+
prompt: (text: string, delivery: "followUp" | "steer") => deliver(session, text, delivery),
|
|
44
|
+
receipt,
|
|
45
|
+
};
|
|
46
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { access } from "node:fs/promises";
|
|
2
|
+
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
import type { StartRequest } from "@shivaedev/agent/run.ts";
|
|
5
|
+
import { thinkingLevel } from "#adapters/effort.ts";
|
|
6
|
+
import { rejected, unavailable } from "#adapters/failure.ts";
|
|
7
|
+
import type { PiOptions } from "#PiOptions.ts";
|
|
8
|
+
|
|
9
|
+
export const loadSdk = Effect.tryPromise({ catch: unavailable, try: () => import("@earendil-works/pi-coding-agent") });
|
|
10
|
+
|
|
11
|
+
export const openRuntime = Effect.fnUntraced(function* (options: PiOptions, request: StartRequest, resume: string | undefined) {
|
|
12
|
+
const effort = yield* thinkingLevel(request.effort);
|
|
13
|
+
if (resume !== undefined) {
|
|
14
|
+
yield* Effect.tryPromise({ catch: () => unavailable("Pi session file is unavailable for resume"), try: () => access(resume) });
|
|
15
|
+
}
|
|
16
|
+
const sdk = yield* loadSdk;
|
|
17
|
+
const modelRuntime = yield* Effect.tryPromise({ catch: unavailable, try: () => sdk.ModelRuntime.create() });
|
|
18
|
+
const models = yield* Effect.tryPromise({ catch: unavailable, try: () => modelRuntime.getAvailable() });
|
|
19
|
+
const model = request.model === undefined ? models[0] : models.find((value) => `${value.provider}/${value.id}` === request.model);
|
|
20
|
+
if (model === undefined) {
|
|
21
|
+
return yield* Effect.fail(
|
|
22
|
+
rejected(`Pi offers no model ${request.model ?? "(default)"} with credentials; choose provider/model-id from its catalog`),
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
const session = yield* Effect.acquireRelease(
|
|
26
|
+
Effect.tryPromise({
|
|
27
|
+
catch: unavailable,
|
|
28
|
+
try: async () => {
|
|
29
|
+
const agentDir = sdk.getAgentDir();
|
|
30
|
+
const resourceLoader = new sdk.DefaultResourceLoader({
|
|
31
|
+
agentDir,
|
|
32
|
+
cwd: request.cwd,
|
|
33
|
+
...resourceOptions(options),
|
|
34
|
+
});
|
|
35
|
+
await resourceLoader.reload();
|
|
36
|
+
const { session: opened } = await sdk.createAgentSession({
|
|
37
|
+
agentDir,
|
|
38
|
+
customTools: [],
|
|
39
|
+
cwd: request.cwd,
|
|
40
|
+
model,
|
|
41
|
+
modelRuntime,
|
|
42
|
+
resourceLoader,
|
|
43
|
+
sessionManager: resume === undefined ? sdk.SessionManager.create(request.cwd) : sdk.SessionManager.open(resume, undefined, request.cwd),
|
|
44
|
+
...(options.constrainedPrompt === undefined ? {} : { tools: [] }),
|
|
45
|
+
...(effort === undefined ? {} : { thinkingLevel: effort }),
|
|
46
|
+
});
|
|
47
|
+
return opened;
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
(openedSession: AgentSession) => Effect.promise(() => openedSession.abort()).pipe(Effect.ensuring(Effect.sync(() => openedSession.dispose()))),
|
|
51
|
+
);
|
|
52
|
+
if (session.sessionFile === undefined) {
|
|
53
|
+
return yield* Effect.fail(unavailable("Pi opened a session without a persisted file reference"));
|
|
54
|
+
}
|
|
55
|
+
return { session, sessionFile: session.sessionFile };
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
function resourceOptions(options: PiOptions) {
|
|
59
|
+
if (options.constrainedPrompt === undefined) {
|
|
60
|
+
return { additionalSkillPaths: [...(options.skills ?? [])] };
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
noContextFiles: true,
|
|
64
|
+
noExtensions: true,
|
|
65
|
+
noPromptTemplates: true,
|
|
66
|
+
noSkills: true,
|
|
67
|
+
noThemes: true,
|
|
68
|
+
systemPrompt: options.constrainedPrompt,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Effect, Exit, Scope, Stream } from "effect";
|
|
2
|
+
import { UnknownAcceptance } from "@shivaedev/agent/failures.ts";
|
|
3
|
+
import type { Receipt, Reconciliation, StartRequest } from "@shivaedev/agent/run.ts";
|
|
4
|
+
import { protocolFailure } from "#adapters/failure.ts";
|
|
5
|
+
import { openSession } from "#adapters/openSession.ts";
|
|
6
|
+
import { textOf } from "#adapters/textOf.ts";
|
|
7
|
+
import type { PiOptions } from "#PiOptions.ts";
|
|
8
|
+
|
|
9
|
+
export const makeOperations = Effect.fnUntraced(function* (options: PiOptions) {
|
|
10
|
+
const runs = yield* Effect.sync(() => new Map<string, Effect.Success<ReturnType<typeof openSession>>>());
|
|
11
|
+
const evidence = new Map<string, Reconciliation>();
|
|
12
|
+
function lookup(receipt: Receipt) {
|
|
13
|
+
return Effect.suspend(() => {
|
|
14
|
+
const run = runs.get(receipt.operationId);
|
|
15
|
+
return receipt.backend === "pi" && receipt.reference.version === 1 && run?.receipt.reference.value === receipt.reference.value
|
|
16
|
+
? Effect.succeed(run)
|
|
17
|
+
: Effect.fail(protocolFailure("run is not open in this backend scope"));
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const submit = Effect.fnUntraced(function* (request: StartRequest, resume: string | undefined) {
|
|
21
|
+
const run = yield* openSession(options, request, resume);
|
|
22
|
+
yield* run.prompt(textOf(request.input), "steer").pipe(
|
|
23
|
+
Effect.mapError((error) => {
|
|
24
|
+
if (error._tag === "Rejected") {
|
|
25
|
+
evidence.set(request.operationId, { evidence: error.message, operationId: request.operationId, type: "not-accepted" });
|
|
26
|
+
return error;
|
|
27
|
+
}
|
|
28
|
+
return new UnknownAcceptance({ backend: "pi", message: error.message, operationId: request.operationId });
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
runs.set(request.operationId, run);
|
|
32
|
+
evidence.set(request.operationId, { receipt: run.receipt, type: "accepted" });
|
|
33
|
+
yield* run.acknowledge;
|
|
34
|
+
yield* Effect.addFinalizer(() =>
|
|
35
|
+
Effect.sync(() => {
|
|
36
|
+
runs.delete(request.operationId);
|
|
37
|
+
}),
|
|
38
|
+
);
|
|
39
|
+
return run.receipt;
|
|
40
|
+
});
|
|
41
|
+
const open = Effect.fnUntraced(function* (request: StartRequest, resume: string | undefined) {
|
|
42
|
+
if (evidence.has(request.operationId)) {
|
|
43
|
+
return yield* Effect.fail(protocolFailure("operation already attempted; reconcile instead"));
|
|
44
|
+
}
|
|
45
|
+
evidence.set(request.operationId, { operationId: request.operationId, reason: "submission has no acceptance evidence yet", type: "uncertain" });
|
|
46
|
+
const runScope = yield* Scope.fork(yield* Effect.scope);
|
|
47
|
+
return yield* submit(request, resume).pipe(
|
|
48
|
+
Effect.provideService(Scope.Scope, runScope),
|
|
49
|
+
Effect.onError((cause) => Scope.close(runScope, Exit.failCause(cause))),
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
events: (receipt: Receipt) => Stream.unwrap(lookup(receipt).pipe(Effect.map((run) => run.events))),
|
|
54
|
+
inspect: (receipt: Receipt) =>
|
|
55
|
+
lookup(receipt).pipe(
|
|
56
|
+
Effect.flatMap((run) => run.inspect),
|
|
57
|
+
Effect.catchTag("ProtocolFailure", () =>
|
|
58
|
+
Effect.succeed({ reason: "no live execution evidence in this backend scope", type: "missing" as const }),
|
|
59
|
+
),
|
|
60
|
+
),
|
|
61
|
+
lookup,
|
|
62
|
+
open,
|
|
63
|
+
reconcile: (operationId: string) =>
|
|
64
|
+
Effect.sync(
|
|
65
|
+
(): Reconciliation =>
|
|
66
|
+
evidence.get(operationId) ?? {
|
|
67
|
+
operationId,
|
|
68
|
+
reason: "Pi has no operation-id lookup; missing local evidence does not prove non-acceptance",
|
|
69
|
+
type: "uncertain",
|
|
70
|
+
},
|
|
71
|
+
),
|
|
72
|
+
resume: (reference: { readonly value: string; readonly version: number }, request: StartRequest) =>
|
|
73
|
+
reference.version === 1 ? open(request, reference.value) : Effect.fail(protocolFailure("unsupported session reference version")),
|
|
74
|
+
};
|
|
75
|
+
});
|
package/src/pi.ts
CHANGED
|
@@ -1 +1,44 @@
|
|
|
1
|
-
|
|
1
|
+
import { Effect, Layer } from "effect";
|
|
2
|
+
import { Backend, type ConfiguredBackend } from "@shivaedev/agent/backend.ts";
|
|
3
|
+
import { listPiModels } from "#adapters/listPiModels.ts";
|
|
4
|
+
import { textOf } from "#adapters/textOf.ts";
|
|
5
|
+
import { makeOperations } from "#makeOperations.ts";
|
|
6
|
+
import type { PiOptions } from "#PiOptions.ts";
|
|
7
|
+
|
|
8
|
+
export function pi(options: PiOptions = {}): Layer.Layer<Backend> {
|
|
9
|
+
return Layer.effect(
|
|
10
|
+
Backend,
|
|
11
|
+
Effect.gen(function* (): Effect.fn.Return<ConfiguredBackend> {
|
|
12
|
+
const operations = yield* makeOperations(options);
|
|
13
|
+
return {
|
|
14
|
+
backend: "pi",
|
|
15
|
+
events: operations.events,
|
|
16
|
+
features: {
|
|
17
|
+
imageInput: { reason: "only text input is implemented", support: "unsupported" },
|
|
18
|
+
interruption: {
|
|
19
|
+
implementation: { interrupt: (receipt) => operations.lookup(receipt).pipe(Effect.flatMap((run) => run.interrupt)) },
|
|
20
|
+
support: "supported",
|
|
21
|
+
},
|
|
22
|
+
models: { implementation: { list: listPiModels }, support: "supported" },
|
|
23
|
+
queuedInput: {
|
|
24
|
+
implementation: {
|
|
25
|
+
queue: (receipt, input) => operations.lookup(receipt).pipe(Effect.flatMap((run) => run.prompt(textOf(input), "followUp"))),
|
|
26
|
+
},
|
|
27
|
+
support: "supported",
|
|
28
|
+
},
|
|
29
|
+
sessions: { implementation: { resume: operations.resume }, support: "supported" },
|
|
30
|
+
steering: {
|
|
31
|
+
implementation: {
|
|
32
|
+
steer: (receipt, input) => operations.lookup(receipt).pipe(Effect.flatMap((run) => run.prompt(textOf(input), "steer"))),
|
|
33
|
+
},
|
|
34
|
+
support: "supported",
|
|
35
|
+
},
|
|
36
|
+
transcript: { reason: "no provider history reader is implemented", support: "unsupported" },
|
|
37
|
+
},
|
|
38
|
+
inspect: operations.inspect,
|
|
39
|
+
reconcile: operations.reconcile,
|
|
40
|
+
start: (request) => operations.open(request, undefined),
|
|
41
|
+
};
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
44
|
+
}
|