@shivaedev/agent-pi 0.3.1 → 0.5.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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0 - 2026-11-05
4
+
5
+ ### Breaking
6
+
7
+ - Require Effect 4.1.0 and its matching companion packages. Move `effect`, `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/sql-pg`, `@effect/sql-sqlite-node`, `@effect/atom-react` and `@effect/vitest` to exactly 4.1.0 together.
8
+
9
+ ## 0.4.0 - 2026-11-04
10
+
11
+ ### Fixed
12
+
13
+ - Acknowledge Pi's accepted prompt dispositions, complete handled initial prompts without waiting for a run, and preserve active runs when input is handled or queued.
14
+ - Treat prompt rejection before a disposition as refusal and resolution without a disposition as uncertain acceptance.
15
+ - Report aborted settlements as interrupted while preserving final assistant failures.
16
+
17
+ ### Breaking
18
+
19
+ - Require Pi's coding-agent SDK 1.4.0. Consumers must upgrade their `@earendil-works/pi-coding-agent` peer dependency before using this adapter release. Terminal execution events now arrive at `agent_settled`; consumers reading raw `agent_end` events must wait for settlement to determine the run outcome.
20
+
3
21
  ## 0.3.1 - 2026-11-04
4
22
 
5
23
  ### Changed
package/README.md CHANGED
@@ -30,11 +30,11 @@ const work = Effect.gen(function* () {
30
30
 
31
31
  ### Acceptance, events and recovery
32
32
 
33
- A receipt acknowledges Pi's successful preflight, independently of its still-running prompt promise. A preflight refusal becomes `Rejected`. A prompt failure without preflight evidence becomes `UnknownAcceptance` with the submitted operation ID. Reconciliation returns remembered acceptance or refusal evidence within this backend instance; unknown IDs remain uncertain because Pi has no operation-ID lookup. An operation ID cannot be submitted twice to the same backend instance.
33
+ A receipt acknowledges Pi's `started`, `queued` or `handled` disposition, independently of its prompt promise. A handled initial prompt completes without a run; handled or queued input leaves an active run open. A prompt promise rejected before any disposition becomes `Rejected`, matching Pi's own RPC refusal signal. Resolution without a disposition becomes `UnknownAcceptance` with the submitted operation ID. Reconciliation returns remembered acceptance or refusal evidence within this backend instance; unknown IDs remain uncertain because Pi has no operation-ID lookup. An operation ID cannot be submitted twice to the same backend instance.
34
34
 
35
35
  The version-one session reference contains the native session-file path. Save `receipt.session`, close its prior run scope, then pass it with a new start request to `agent.sessions.resume`; the adapter opens that file through `SessionManager.open`, preserving its native history. An unavailable file fails with `BackendUnavailable` before submission rather than silently creating replacement history. An unsupported reference version fails with `ProtocolFailure`. References do not contain application agent identities.
36
36
 
37
- `events(receipt)` is a live, single-consumer stream buffered from subscription until the run scope closes. It includes the session-file reference, acknowledgement, execution state, settled user and assistant messages, thinking, tool calls and results, turn token deltas and separate provider USD cost deltas. Raw payloads preserve the complete provider event. Unmapped events, including retry and compaction notifications, remain `raw` events. An `agent_end` with `willRetry` does not report completion. Final assistant stop reasons distinguish completed, failed and interrupted runs; `agent_settled` stays raw and does not overwrite that outcome.
37
+ `events(receipt)` is a live, single-consumer stream buffered from subscription until the run scope closes. It includes the session-file reference, acknowledgement, execution state, settled user and assistant messages, thinking, tool calls and results, turn token deltas and separate provider USD cost deltas. Raw payloads preserve the complete provider event. Unmapped events, including retry and compaction notifications, remain `raw` events. An `agent_end` stays raw while the run remains open. At `agent_settled`, final assistant stop reasons distinguish completed, failed and interrupted runs, and `aborted` always reports interruption.
38
38
 
39
39
  `inspect` reads execution evidence from the live session; after its scope closes it returns missing evidence. This is not a persisted run monitor or a transcript reader. Interruption calls Pi's abort; the scope finalizer unsubscribes, aborts and disposes even if abort fails.
40
40
 
@@ -1,4 +1,7 @@
1
- import type { AgentSession } from "@earendil-works/pi-coding-agent";
1
+ import type { AgentSession, PromptOptions } from "@earendil-works/pi-coding-agent";
2
2
  import { Effect } from "effect";
3
- export declare function deliver(session: AgentSession, text: string, delivery: "followUp" | "steer"): Effect.Effect<void, import("@shivaedev/agent/failures.ts").BackendUnavailable | import("@shivaedev/agent/failures.ts").ProtocolFailure | import("@shivaedev/agent/failures.ts").Rejected | import("@shivaedev/agent/failures.ts").UnknownAcceptance, never>;
3
+ import type { BackendFailure } from "@shivaedev/agent/failures.ts";
4
+ type PromptDisposition = Parameters<NonNullable<PromptOptions["preflightResult"]>>[0];
5
+ export declare function deliver(session: AgentSession, text: string, delivery: "followUp" | "steer"): Effect.Effect<PromptDisposition, BackendFailure>;
6
+ export {};
4
7
  //# sourceMappingURL=deliver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../../src/adapters/deliver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,wBAAgB,OAAO,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO,+PAkB1F"}
1
+ {"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../../src/adapters/deliver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAGnE,KAAK,iBAAiB,GAAG,UAAU,CAAC,WAAW,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEtF,wBAAgB,OAAO,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAwB7I"}
@@ -2,18 +2,24 @@ import { Effect } from "effect";
2
2
  import { protocolFailure, rejected } from "#adapters/failure.ts";
3
3
  export function deliver(session, text, delivery) {
4
4
  return Effect.callback((resume) => {
5
- let preflight;
5
+ let accepted = false;
6
6
  session
7
7
  .prompt(text, {
8
- preflightResult: (ok) => {
9
- preflight = ok;
10
- if (ok) {
11
- resume(Effect.void);
12
- }
8
+ preflightResult: (disposition) => {
9
+ accepted = true;
10
+ resume(Effect.succeed(disposition));
13
11
  },
14
12
  streamingBehavior: delivery,
15
13
  })
16
- .then(() => resume(Effect.void), (error) => resume(Effect.fail(preflight === false ? rejected(error) : protocolFailure(error))));
14
+ .then(() => {
15
+ if (!accepted) {
16
+ resume(Effect.fail(protocolFailure("Pi prompt resolved without acceptance evidence")));
17
+ }
18
+ }, (error) => {
19
+ if (!accepted) {
20
+ resume(Effect.fail(rejected(error)));
21
+ }
22
+ });
17
23
  });
18
24
  }
19
25
  //# sourceMappingURL=deliver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"deliver.js","sourceRoot":"","sources":["../../src/adapters/deliver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEjE,MAAM,UAAU,OAAO,CAAC,OAAqB,EAAE,IAAY,EAAE,QAA8B;IAC1F,OAAO,MAAM,CAAC,QAAQ,CAAuB,CAAC,MAAM,EAAE,EAAE;QACvD,IAAI,SAA8B,CAAC;QACnC,OAAO;aACL,MAAM,CAAC,IAAI,EAAE;YACb,eAAe,EAAE,CAAC,EAAE,EAAE,EAAE;gBACvB,SAAS,GAAG,EAAE,CAAC;gBACf,IAAI,EAAE,EAAE,CAAC;oBACR,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACrB,CAAC;YACF,CAAC;YACD,iBAAiB,EAAE,QAAQ;SAC3B,CAAC;aACD,IAAI,CACJ,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EACzB,CAAC,KAAc,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CACvG,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"deliver.js","sourceRoot":"","sources":["../../src/adapters/deliver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAIjE,MAAM,UAAU,OAAO,CAAC,OAAqB,EAAE,IAAY,EAAE,QAA8B;IAC1F,OAAO,MAAM,CAAC,QAAQ,CAAoC,CAAC,MAAM,EAAE,EAAE;QACpE,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,OAAO;aACL,MAAM,CAAC,IAAI,EAAE;YACb,eAAe,EAAE,CAAC,WAAW,EAAE,EAAE;gBAChC,QAAQ,GAAG,IAAI,CAAC;gBAChB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;YACrC,CAAC;YACD,iBAAiB,EAAE,QAAQ;SAC3B,CAAC;aACD,IAAI,CACJ,GAAG,EAAE;YACJ,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACf,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,gDAAgD,CAAC,CAAC,CAAC,CAAC;YACxF,CAAC;QACF,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;YAClB,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACf,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACtC,CAAC;QACF,CAAC,CACD,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC"}
@@ -5,5 +5,5 @@ export declare function rawOf(kind: string, payload: unknown): {
5
5
  payload: string;
6
6
  source: string;
7
7
  };
8
- export declare function toAgentEvents(event: AgentSessionEvent): RunEvent[];
8
+ export declare function makeEventMapper(): (event: AgentSessionEvent) => RunEvent[];
9
9
  //# sourceMappingURL=mapping.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../../src/adapters/mapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAG3D,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;;;;EAEnD;AAKD,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,QAAQ,EAAE,CA2BlE"}
1
+ {"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../../src/adapters/mapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAG3D,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;;;;EAEnD;AAKD,wBAAgB,eAAe,YAEf,iBAAiB,KAAG,QAAQ,EAAE,CA+B7C"}
@@ -4,33 +4,39 @@ export function rawOf(kind, payload) {
4
4
  return { kind, payload: JSON.stringify(payload), source: "pi" };
5
5
  }
6
6
  const decodeOutput = Schema.decodeUnknownOption(Schema.Struct({ content: Schema.Array(Schema.Struct({ text: Schema.optional(Schema.String), type: Schema.String })) }));
7
- export function toAgentEvents(event) {
8
- const raw = rawOf(event.type, event);
9
- switch (event.type) {
10
- case "agent_start":
11
- return [{ raw, state: "running", type: "execution" }];
12
- case "agent_end": {
13
- if (event.willRetry) {
7
+ export function makeEventMapper() {
8
+ let ending = "completed";
9
+ return (event) => {
10
+ const raw = rawOf(event.type, event);
11
+ switch (event.type) {
12
+ case "agent_start":
13
+ ending = "completed";
14
+ return [{ raw, state: "running", type: "execution" }];
15
+ case "agent_end": {
16
+ if (event.willRetry) {
17
+ return [{ raw, type: "raw" }];
18
+ }
19
+ const assistant = event.messages.filter((message) => message.role === "assistant").at(-1);
20
+ ending = endState(assistant?.stopReason);
14
21
  return [{ raw, type: "raw" }];
15
22
  }
16
- const ending = event.messages.filter((message) => message.role === "assistant").at(-1);
17
- const state = endState(ending?.stopReason);
18
- return [{ raw, state, type: "execution" }];
19
- }
20
- case "message_end":
21
- return messageEvents(event.message, raw);
22
- case "tool_execution_start":
23
- return [{ input: JSON.stringify(event.args), name: event.toolName, raw, toolId: event.toolCallId, type: "tool.started" }];
24
- case "tool_execution_end": {
25
- const output = Option.match(decodeOutput(event.result), {
26
- onNone: () => "",
27
- onSome: ({ content }) => content.flatMap((part) => (part.type === "text" && part.text !== undefined ? [part.text] : [])).join("\n"),
28
- });
29
- return [{ ok: !event.isError, output, raw, toolId: event.toolCallId, type: "tool.completed" }];
23
+ case "agent_settled":
24
+ return [{ raw, state: event.aborted ? "interrupted" : ending, type: "execution" }];
25
+ case "message_end":
26
+ return messageEvents(event.message, raw);
27
+ case "tool_execution_start":
28
+ return [{ input: JSON.stringify(event.args), name: event.toolName, raw, toolId: event.toolCallId, type: "tool.started" }];
29
+ case "tool_execution_end": {
30
+ const output = Option.match(decodeOutput(event.result), {
31
+ onNone: () => "",
32
+ onSome: ({ content }) => content.flatMap((part) => (part.type === "text" && part.text !== undefined ? [part.text] : [])).join("\n"),
33
+ });
34
+ return [{ ok: !event.isError, output, raw, toolId: event.toolCallId, type: "tool.completed" }];
35
+ }
36
+ default:
37
+ return [{ raw, type: "raw" }];
30
38
  }
31
- default:
32
- return [{ raw, type: "raw" }];
33
- }
39
+ };
34
40
  }
35
41
  function endState(reason) {
36
42
  if (reason === "aborted") {
@@ -1 +1 @@
1
- {"version":3,"file":"mapping.js","sourceRoot":"","sources":["../../src/adapters/mapping.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mapping.js","sourceRoot":"","sources":["../../src/adapters/mapping.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"openSession.d.ts","sourceRoot":"","sources":["../../src/adapters/openSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AAO3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAiCP,MAAM,YAAY,UAAU,GAAG,OAAO;;;;;;;;;;;;;6IAGrD,CAAC"}
1
+ {"version":3,"file":"openSession.d.ts","sourceRoot":"","sources":["../../src/adapters/openSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AAO3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAkCP,MAAM,YAAY,UAAU,GAAG,OAAO;;;;;;;;;;;;;6IAarD,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { Effect, Queue, Stream } from "effect";
2
2
  import { deliver } from "#adapters/deliver.ts";
3
3
  import { unavailable } from "#adapters/failure.ts";
4
- import { rawOf, toAgentEvents } from "#adapters/mapping.ts";
4
+ import { makeEventMapper, rawOf } from "#adapters/mapping.ts";
5
5
  import { openRuntime } from "#adapters/runtime.ts";
6
6
  export const openSession = Effect.fnUntraced(function* (options, request, resume) {
7
7
  const { session, sessionFile } = yield* openRuntime(options, request, resume);
@@ -13,6 +13,7 @@ export const openSession = Effect.fnUntraced(function* (options, request, resume
13
13
  }
14
14
  Queue.offerUnsafe(output, event);
15
15
  }
16
+ const toAgentEvents = makeEventMapper();
16
17
  const unsubscribe = session.subscribe((event) => {
17
18
  for (const mapped of toAgentEvents(event)) {
18
19
  emit(mapped);
@@ -34,7 +35,11 @@ export const openSession = Effect.fnUntraced(function* (options, request, resume
34
35
  events: Stream.fromQueue(output),
35
36
  inspect: Effect.sync(() => observation),
36
37
  interrupt: Effect.tryPromise({ catch: unavailable, try: () => session.abort() }),
37
- prompt: (text, delivery) => deliver(session, text, delivery),
38
+ prompt: (text, delivery) => deliver(session, text, delivery).pipe(Effect.tap((disposition) => Effect.sync(() => {
39
+ if (disposition === "handled" && observation.type === "known" && observation.state === "accepted") {
40
+ emit({ state: "completed", type: "execution" });
41
+ }
42
+ })), Effect.asVoid),
38
43
  receipt,
39
44
  };
40
45
  });
@@ -1 +1 @@
1
- {"version":3,"file":"openSession.js","sourceRoot":"","sources":["../../src/adapters/openSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAG3D,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnD,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,OAAkB,EAAE,OAAqB,EAAE,MAA0B;IAC5H,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC9E,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,SAAS,EAAwB,CAAC;IAC9D,IAAI,WAAW,GAAgB,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACpE,SAAS,IAAI,CAAC,KAAe;QAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAChC,WAAW,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QACrD,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IACD,MAAM,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;QAC/C,KAAK,MAAM,MAAM,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,CAAC;QACd,CAAC;IACF,CAAC,CAAC,CAAC;IACH,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAC/B,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QAChB,WAAW,EAAE,CAAC;QACd,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC,CAAC,CACF,CAAC;IACF,MAAM,OAAO,GAAY;QACxB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE;QAC7C,OAAO,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE;KAC3C,CAAC;IACF,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,gBAAgB,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;IACtI,OAAO;QACN,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QACvE,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;QAChC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC;QACvC,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QAChF,MAAM,EAAE,CAAC,IAAY,EAAE,QAA8B,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC;QAC1F,OAAO;KACP,CAAC;AACH,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"openSession.js","sourceRoot":"","sources":["../../src/adapters/openSession.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/agent-pi",
3
- "version": "0.3.1",
3
+ "version": "0.5.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": {
@@ -40,21 +40,21 @@
40
40
  "README.md"
41
41
  ],
42
42
  "dependencies": {
43
- "@shivaedev/agent": "0.3.1"
43
+ "@shivaedev/agent": "0.4.0"
44
44
  },
45
45
  "devDependencies": {
46
- "@earendil-works/pi-coding-agent": "0.84.4",
47
- "@effect/vitest": "4.0.7",
48
- "@shivaedev/quality": "0.61.1",
49
- "@shivaedev/test-story": "0.39.1",
46
+ "@earendil-works/pi-coding-agent": "1.4.0",
47
+ "@effect/vitest": "4.1.0",
48
+ "@shivaedev/quality": "0.65.0",
49
+ "@shivaedev/test-story": "0.42.0",
50
50
  "@types/node": "24.19.2",
51
- "effect": "4.0.7",
51
+ "effect": "4.1.0",
52
52
  "typescript": "7.0.2",
53
53
  "vitest": "5.0.5"
54
54
  },
55
55
  "peerDependencies": {
56
- "@earendil-works/pi-coding-agent": "0.84.4",
57
- "effect": "4.0.7"
56
+ "@earendil-works/pi-coding-agent": "1.4.0",
57
+ "effect": "4.1.0"
58
58
  },
59
59
  "peerDependenciesMeta": {
60
60
  "@earendil-works/pi-coding-agent": {
@@ -1,24 +1,32 @@
1
- import type { AgentSession } from "@earendil-works/pi-coding-agent";
1
+ import type { AgentSession, PromptOptions } from "@earendil-works/pi-coding-agent";
2
2
  import { Effect } from "effect";
3
3
  import type { BackendFailure } from "@shivaedev/agent/failures.ts";
4
4
  import { protocolFailure, rejected } from "#adapters/failure.ts";
5
5
 
6
- export function deliver(session: AgentSession, text: string, delivery: "followUp" | "steer") {
7
- return Effect.callback<void, BackendFailure>((resume) => {
8
- let preflight: boolean | undefined;
6
+ type PromptDisposition = Parameters<NonNullable<PromptOptions["preflightResult"]>>[0];
7
+
8
+ export function deliver(session: AgentSession, text: string, delivery: "followUp" | "steer"): Effect.Effect<PromptDisposition, BackendFailure> {
9
+ return Effect.callback<PromptDisposition, BackendFailure>((resume) => {
10
+ let accepted = false;
9
11
  session
10
12
  .prompt(text, {
11
- preflightResult: (ok) => {
12
- preflight = ok;
13
- if (ok) {
14
- resume(Effect.void);
15
- }
13
+ preflightResult: (disposition) => {
14
+ accepted = true;
15
+ resume(Effect.succeed(disposition));
16
16
  },
17
17
  streamingBehavior: delivery,
18
18
  })
19
19
  .then(
20
- () => resume(Effect.void),
21
- (error: unknown) => resume(Effect.fail(preflight === false ? rejected(error) : protocolFailure(error))),
20
+ () => {
21
+ if (!accepted) {
22
+ resume(Effect.fail(protocolFailure("Pi prompt resolved without acceptance evidence")));
23
+ }
24
+ },
25
+ (error: unknown) => {
26
+ if (!accepted) {
27
+ resume(Effect.fail(rejected(error)));
28
+ }
29
+ },
22
30
  );
23
31
  });
24
32
  }
@@ -10,33 +10,39 @@ const decodeOutput = Schema.decodeUnknownOption(
10
10
  Schema.Struct({ content: Schema.Array(Schema.Struct({ text: Schema.optional(Schema.String), type: Schema.String })) }),
11
11
  );
12
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) {
13
+ export function makeEventMapper() {
14
+ let ending: "completed" | "failed" | "interrupted" = "completed";
15
+ return (event: AgentSessionEvent): RunEvent[] => {
16
+ const raw = rawOf(event.type, event);
17
+ switch (event.type) {
18
+ case "agent_start":
19
+ ending = "completed";
20
+ return [{ raw, state: "running", type: "execution" }];
21
+ case "agent_end": {
22
+ if (event.willRetry) {
23
+ return [{ raw, type: "raw" }];
24
+ }
25
+ const assistant = event.messages.filter((message) => message.role === "assistant").at(-1);
26
+ ending = endState(assistant?.stopReason);
20
27
  return [{ raw, type: "raw" }];
21
28
  }
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" }];
29
+ case "agent_settled":
30
+ return [{ raw, state: event.aborted ? "interrupted" : ending, type: "execution" }];
31
+ case "message_end":
32
+ return messageEvents(event.message, raw);
33
+ case "tool_execution_start":
34
+ return [{ input: JSON.stringify(event.args), name: event.toolName, raw, toolId: event.toolCallId, type: "tool.started" }];
35
+ case "tool_execution_end": {
36
+ const output = Option.match(decodeOutput(event.result), {
37
+ onNone: () => "",
38
+ onSome: ({ content }) => content.flatMap((part) => (part.type === "text" && part.text !== undefined ? [part.text] : [])).join("\n"),
39
+ });
40
+ return [{ ok: !event.isError, output, raw, toolId: event.toolCallId, type: "tool.completed" }];
41
+ }
42
+ default:
43
+ return [{ raw, type: "raw" }];
36
44
  }
37
- default:
38
- return [{ raw, type: "raw" }];
39
- }
45
+ };
40
46
  }
41
47
 
42
48
  function endState(reason: string | undefined) {
@@ -3,7 +3,7 @@ import type { RunEvent } from "@shivaedev/agent/events.ts";
3
3
  import type { Observation, Receipt, StartRequest } from "@shivaedev/agent/run.ts";
4
4
  import { deliver } from "#adapters/deliver.ts";
5
5
  import { unavailable } from "#adapters/failure.ts";
6
- import { rawOf, toAgentEvents } from "#adapters/mapping.ts";
6
+ import { makeEventMapper, rawOf } from "#adapters/mapping.ts";
7
7
  import { openRuntime } from "#adapters/runtime.ts";
8
8
  import type { PiOptions } from "#PiOptions.ts";
9
9
 
@@ -17,6 +17,7 @@ export const openSession = Effect.fnUntraced(function* (options: PiOptions, requ
17
17
  }
18
18
  Queue.offerUnsafe(output, event);
19
19
  }
20
+ const toAgentEvents = makeEventMapper();
20
21
  const unsubscribe = session.subscribe((event) => {
21
22
  for (const mapped of toAgentEvents(event)) {
22
23
  emit(mapped);
@@ -40,7 +41,17 @@ export const openSession = Effect.fnUntraced(function* (options: PiOptions, requ
40
41
  events: Stream.fromQueue(output),
41
42
  inspect: Effect.sync(() => observation),
42
43
  interrupt: Effect.tryPromise({ catch: unavailable, try: () => session.abort() }),
43
- prompt: (text: string, delivery: "followUp" | "steer") => deliver(session, text, delivery),
44
+ prompt: (text: string, delivery: "followUp" | "steer") =>
45
+ deliver(session, text, delivery).pipe(
46
+ Effect.tap((disposition) =>
47
+ Effect.sync(() => {
48
+ if (disposition === "handled" && observation.type === "known" && observation.state === "accepted") {
49
+ emit({ state: "completed", type: "execution" });
50
+ }
51
+ }),
52
+ ),
53
+ Effect.asVoid,
54
+ ),
44
55
  receipt,
45
56
  };
46
57
  });