@plurnk/plurnk-agui 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.
Files changed (52) hide show
  1. package/.env.example +37 -0
  2. package/README.md +65 -0
  3. package/SPEC.md +115 -0
  4. package/dist/AguiPlus.d.ts +58 -0
  5. package/dist/AguiPlus.d.ts.map +1 -0
  6. package/dist/AguiPlus.js +67 -0
  7. package/dist/AguiPlus.js.map +1 -0
  8. package/dist/DaemonClient.d.ts +9 -0
  9. package/dist/DaemonClient.d.ts.map +1 -0
  10. package/dist/DaemonClient.js +55 -0
  11. package/dist/DaemonClient.js.map +1 -0
  12. package/dist/DaemonSeam.d.ts +103 -0
  13. package/dist/DaemonSeam.d.ts.map +1 -0
  14. package/dist/DaemonSeam.js +7 -0
  15. package/dist/DaemonSeam.js.map +1 -0
  16. package/dist/EventRouter.d.ts +14 -0
  17. package/dist/EventRouter.d.ts.map +1 -0
  18. package/dist/EventRouter.js +27 -0
  19. package/dist/EventRouter.js.map +1 -0
  20. package/dist/Module.d.ts +23 -0
  21. package/dist/Module.d.ts.map +1 -0
  22. package/dist/Module.js +200 -0
  23. package/dist/Module.js.map +1 -0
  24. package/dist/Portal.d.ts +33 -0
  25. package/dist/Portal.d.ts.map +1 -0
  26. package/dist/Portal.js +62 -0
  27. package/dist/Portal.js.map +1 -0
  28. package/dist/ProposalHitl.d.ts +14 -0
  29. package/dist/ProposalHitl.d.ts.map +1 -0
  30. package/dist/ProposalHitl.js +64 -0
  31. package/dist/ProposalHitl.js.map +1 -0
  32. package/dist/Server.d.ts +10 -0
  33. package/dist/Server.d.ts.map +1 -0
  34. package/dist/Server.js +232 -0
  35. package/dist/Server.js.map +1 -0
  36. package/dist/Translator.d.ts +17 -0
  37. package/dist/Translator.d.ts.map +1 -0
  38. package/dist/Translator.js +200 -0
  39. package/dist/Translator.js.map +1 -0
  40. package/dist/bin/serve.d.ts +3 -0
  41. package/dist/bin/serve.d.ts.map +1 -0
  42. package/dist/bin/serve.js +9 -0
  43. package/dist/bin/serve.js.map +1 -0
  44. package/dist/index.d.ts +12 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +15 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/types.d.ts +134 -0
  49. package/dist/types.d.ts.map +1 -0
  50. package/dist/types.js +10 -0
  51. package/dist/types.js.map +1 -0
  52. package/package.json +65 -0
package/.env.example ADDED
@@ -0,0 +1,37 @@
1
+ # ============================================================================
2
+ # @plurnk/plurnk-agui — every knob lives here; no code fallback hides one.
3
+ # ============================================================================
4
+
5
+ # The plurnk daemon this bridge fronts (the bridge is an ordinary client of the
6
+ # JSON-RPC-over-WebSocket wire — it holds no state the daemon doesn't).
7
+ PLURNK_AGUI_DAEMON_URL=ws://127.0.0.1:3044
8
+
9
+ # The AG-UI listener. POST / with Accept: text/event-stream is the run endpoint
10
+ # (the AG-UI HttpAgent default shape); POST /resolve answers proposals/questions.
11
+ PLURNK_AGUI_HOST=127.0.0.1
12
+ PLURNK_AGUI_PORT=3080
13
+
14
+ # Session naming: an AG-UI threadId maps to the daemon session `<prefix>-<threadId>`
15
+ # (created on the thread's first run, reattached on every run after — AG-UI threads
16
+ # ARE plurnk sessions, so the extended context persists across runs).
17
+ PLURNK_AGUI_SESSION_PREFIX=agui
18
+
19
+ # Operator questions ([300]): 1 = sessions created by this bridge request
20
+ # settings.questions, so the model's asks surface as plurnk.proposal events for
21
+ # the frontend to answer via POST /resolve (the daemon's PLURNK_QUESTIONS=0
22
+ # ceiling still wins). 0 = headless semantics — asks are refused with the
23
+ # self-decide steer, exactly like bench.
24
+ PLURNK_AGUI_QUESTIONS=1
25
+
26
+ # Loop flags forwarded on every run this bridge starts. yolo=1 auto-accepts
27
+ # file-edit proposals daemon-side (questions still stop the world — the daemon
28
+ # never yolo-answers a human question).
29
+ PLURNK_AGUI_YOLO=0
30
+
31
+ # Per-run turn ceiling forwarded to loop.run.
32
+ PLURNK_AGUI_MAX_TURNS=24
33
+
34
+ # Bridge auth — EMPTY (shipped) = local trust (bind 127.0.0.1 and the port IS the boundary,
35
+ # the same posture as the daemon's own WS). Set a token and every POST must carry
36
+ # `Authorization: Bearer <token>` — the knob for any non-loopback deployment.
37
+ PLURNK_AGUI_TOKEN=
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # @plurnk/plurnk-agui
2
+
3
+ The plurnk daemon's **transport daughter**: owns the client interface, serving
4
+ **AG-UI+** — the [Agent-User Interaction Protocol](https://docs.ag-ui.com) plus plurnk
5
+ metadata extensions — as the **sole** client interaction surface. Any AG-UI frontend
6
+ (CopilotKit, community UIs, the plurnk CLI/TUI/nvim clients) is a plurnk client.
7
+ Vendor-agnostic, MIT.
8
+
9
+ ## Topology
10
+
11
+ An **in-process module** of the daemon, not an external service: the daemon activates it
12
+ at boot (`daemon.registerModule(Module.init(opts))`), handing it the curated core seam
13
+ (event source, loop-control, op dispatch, journal/metadata reads, proposal HITL,
14
+ session lifecycle — plurnk-service#355). The module opens its own HTTP/SSE listener and
15
+ owns the transport+security line at that edge: bearer auth, per-session authorization,
16
+ input validation. Below the seam: plurnk terms (loop/turn/proposal). Above: AG-UI terms
17
+ (run/message/tool-call/state). This module is the translation.
18
+
19
+ A legacy standalone bridge (`plurnk-agui` bin: WS client → SSE server) ships during the
20
+ migration and retires at cutover.
21
+
22
+ ## The interface (AG-UI+)
23
+
24
+ One endpoint: `POST /` with `RunAgentInput` (`threadId`, `runId`, `messages`,
25
+ `forwardedProps`) → `text/event-stream` of AG-UI events.
26
+
27
+ - **Runs**: turns are `STEP_*`; PLAN is `THINKING_TEXT_MESSAGE_*`; SEND bodies are
28
+ `TEXT_MESSAGE_*`; every other op row is a `TOOL_CALL_*` triple + `TOOL_CALL_RESULT`.
29
+ Numbers pass through verbatim (`contextSize` = the daemon's effective prompt budget).
30
+ - **HITL, terminate-resume**: a stopped-world proposal (file edit, exec, MCP auth) emits
31
+ a `request_approval` tool-call — a `SEND[300]` operator question emits
32
+ `request_user_input` — and the run **finishes** while the loop stays paused in-engine.
33
+ The next run's tool-result message (`toolCallId: "prop:<id>"`, content
34
+ `{decision: accept|reject|cancel, body?}`) resolves it; the continued loop streams there.
35
+ Pending proposals re-surface on (re)connect — a days-old question is discoverable.
36
+ - **Reads ride STATE**: providers/aliases/budget/session arrive as `STATE_SNAPSHOT` on
37
+ `RUN_STARTED` and `STATE_DELTA` on change — observed, not polled.
38
+ - **Session options** ride the thread's first run: `forwardedProps.plurnk`
39
+ (`projectRoot`, `constraints`, `settings`); per-run knobs (`maxTurns`, `flags`,
40
+ `alias`/`model`) every run.
41
+ - **Cancel**: dropping the SSE aborts a live loop (hangup is the abort); a
42
+ proposal-terminated run leaves the paused loop for the resume.
43
+ - An AG-UI `threadId` **is** a plurnk session (`<prefix>-<threadId>`) — extended context
44
+ persists across runs; history replays as `MESSAGES_SNAPSHOT` on reattach.
45
+ - **Tier 2 metadata** (`CUSTOM plurnk.*`: row, stream, telemetry, terminated) carries
46
+ what AG-UI has no term for — fold state, coordinates, tags, token truth. Generic
47
+ frontends skip it; family clients render it richly. The full contract: `SPEC.md`
48
+ (every `{§}` anchor cited by a test).
49
+
50
+ ## Consume
51
+
52
+ ```ts
53
+ import { Module } from "@plurnk/plurnk-agui";
54
+ daemon.registerModule(Module.init({ host: "127.0.0.1", port: 3080, sessionPrefix: "agui" }));
55
+ ```
56
+
57
+ Legacy bridge: `npx plurnk-agui` fronts `ws://127.0.0.1:3044`, listens on `:3080`;
58
+ config is `.env` over the shipped `.env.example` (every knob documented there).
59
+
60
+ ## Dependencies
61
+
62
+ **Zero at runtime — no Zod, no SDK.** The AG-UI event shapes are hand-defined plain TS
63
+ (the official `@ag-ui/core` is 0.0.x and would drag Zod in, so it is deliberately NOT a
64
+ dependency). If the official SDK ever stabilizes dependency-free, adopting it is a
65
+ types-only swap.
package/SPEC.md ADDED
@@ -0,0 +1,115 @@
1
+ # @plurnk/plurnk-agui — the projection contract
2
+
3
+ The bridge is **a client of the plurnk daemon and a server of AG-UI**: it consumes the same
4
+ JSON-RPC-over-WebSocket wire every plurnk client speaks, and emits the Agent-User Interaction
5
+ Protocol's SSE event stream. Zero daemon changes; the daemon's numbers and semantics pass
6
+ through, never recomputed. Every `{§}` anchor below is cited by a `[§]` test.
7
+
8
+ ## Architecture
9
+
10
+ - **The bridge is an ordinary daemon client** {§agui-daemon-client} — it connects to
11
+ `PLURNK_AGUI_DAEMON_URL`, calls the public methods (`session.create`/`session.attach`,
12
+ `loop.run`, `loop.resolve`), and subscribes to the public notifications (`log/entry`,
13
+ `loop/proposal`, `loop/terminated`). It holds no state the daemon doesn't: a bridge restart
14
+ loses nothing (threads reattach by session name).
15
+ - **An AG-UI thread IS a plurnk session** {§agui-thread-is-session} — `threadId` maps to the
16
+ session `<PLURNK_AGUI_SESSION_PREFIX>-<threadId>`, created on the thread's first run with
17
+ `settings.questions` per `PLURNK_AGUI_QUESTIONS`, reattached on every run after. Plurnk's
18
+ extended context persists across AG-UI runs because the session does.
19
+ - **Zero runtime dependencies** {§agui-zero-dep} — the AG-UI event shapes are hand-defined
20
+ plain JSON (`src/types.ts`); the SSE encoding is `data: <json>\n\n`. The protocol is young;
21
+ a zero-dep daughter beats tracking SDK churn, and adopting the official SDK later is a
22
+ types-only swap.
23
+
24
+ ## The projection {§agui-projection}
25
+
26
+ One daemon notification in, zero-or-more AG-UI events out (`Translator`, pure):
27
+
28
+ | plurnk wire | AG-UI events |
29
+ |---|---|
30
+ | `loop.run` accepted | `RUN_STARTED` |
31
+ | `log/entry` turn boundary | `STEP_FINISHED` + `STEP_STARTED` (`turn-<id>`) |
32
+ | `log/entry` op=PLAN (model) | `THINKING_TEXT_MESSAGE_START/CONTENT/END` |
33
+ | `log/entry` op=SEND (model) | `TEXT_MESSAGE_START/CONTENT/END` + `CUSTOM plurnk.send` (signal/status) |
34
+ | `log/entry` other op (model) | `TOOL_CALL_START/ARGS/END` (+ `TOOL_CALL_RESULT` when rx exists) |
35
+ | `log/entry` op=model (mirror) | nothing — forensic, not speech |
36
+ | `log/entry` origin≠model | `CUSTOM plurnk.ambient` (foists, deltas, narrations) |
37
+ | `loop/proposal` | `CUSTOM plurnk.proposal` |
38
+ | `loop/terminated` | `STATE_DELTA` (budget) + `CUSTOM plurnk.terminated` (sessionId, loopId, turnIds, costPico, meta) + `RUN_FINISHED` (200) or `RUN_ERROR` (else) |
39
+ | `telemetry/event` | `CUSTOM plurnk.telemetry` |
40
+ | `stream/event` + `stream/concluded` | `CUSTOM plurnk.stream` (payload self-discriminates: event carries `state`, concluded `closeStatus`) |
41
+ | `loop/quiesced` | `CUSTOM plurnk.quiesced` |
42
+
43
+ - **An op row IS a tool call** — its `coordinate` is the `toolCallId`, its tx the args (one
44
+ delta: a dispatched plurnk op is atomic), its rx the result. The log-shaped richness the
45
+ core vocabulary can't hold (fold state, tags, tokens) stays on the row inside
46
+ `plurnk.ambient`/`TOOL_CALL_RESULT` payloads.
47
+ - **The custom namespace** {§agui-custom-namespace} — everything plurnk-specific rides
48
+ `CUSTOM` events named `plurnk.*` (`plurnk.send`, `plurnk.ambient`, `plurnk.proposal`,
49
+ `plurnk.telemetry`, `plurnk.stream`, `plurnk.quiesced`, `plurnk.terminated` — the full loop
50
+ outcome the budget `STATE_DELTA` can't hold). Generic frontends skip unknown customs; plurnk-aware frontends render
51
+ them richly. Nothing plurnk-specific ever masquerades as a core event.
52
+ - **Numbers pass through verbatim** {§agui-numbers-passthrough} — the budget `STATE_DELTA`
53
+ carries the daemon's own usage figures (`contextTokens`, `contextSize` = the effective
54
+ prompt budget, service#345). The bridge never recomputes a number; the daemon's gauge is
55
+ the gauge.
56
+
57
+ - **The row channel** {§agui-row-channel} — every log row ALSO rides `CUSTOM plurnk.row`
58
+ carrying the full wire entry (fold state, tags-in-signal, tokens, coordinate) alongside its
59
+ core projection. Rich clients (TUI/nvim) render plurnk-native fidelity from `plurnk.row`;
60
+ generic clients never see the difference. This is the metadata channel the exclusive-portal
61
+ migration stands on.
62
+ - **The gauge starts true** — `RUN_STARTED` is followed by a `STATE_SNAPSHOT` carrying the
63
+ daemon's `providers.list` truth (the effective prompt budget, the active model), then
64
+ `STATE_DELTA`s. A dropped SSE stream cancels the loop (`loop.cancel`) — the frontend hanging
65
+ up IS the abort signal; no run is orphaned unwatched.
66
+
67
+ - **Reattach replays** {§agui-replay} — a rediscovered thread (the bridge restarted, a second
68
+ frontend arrived) attaches to its existing session by name→id and opens ORIENTED: the model
69
+ run's SENDs replay as `MESSAGES_SNAPSHOT` (the conversation spine; everything else stays
70
+ reachable via live `plurnk.row`), and every pending stop-the-world proposal re-surfaces
71
+ immediately via the daemon's `proposal.list` — the indefinite-wait ruling's client half: a
72
+ days-old question is discoverable, never a mystery hang.
73
+
74
+ ## Stop-the-world {§agui-proposal-resolve}
75
+
76
+ Every daemon proposal — file edits, MCP auths, `[300]` operator questions (service#346) —
77
+ surfaces as `CUSTOM plurnk.proposal` carrying `{logEntryId, op, target, body, attrs, flags}`
78
+ (`attrs.question`/`attrs.choices` for questions). The frontend answers via
79
+ `POST /resolve {threadId, logEntryId, decision, body?}` — a passthrough to the daemon's
80
+ `loop.resolve`, where an accept `body` is the answer. The SSE stream stays open while the
81
+ world is stopped — **indefinitely by default** (service ruling: a stopped world waits for its
82
+ human; the timeout is operator opt-in) — and the run resumes on resolution.
83
+
84
+ ## The management plane {§agui-management-plane}
85
+
86
+ AG-UI models the RUN plane; the workspace plane rides the charter's ONE escape hatch:
87
+ `POST /plurnk/rpc {threadId, method, params}` — a verbatim JSON-RPC passthrough on the
88
+ thread's own daemon connection (session scoping is exactly the thread's). The daemon's
89
+ method registry is the contract; discover it with `{method: "discover"}`. Session options
90
+ for a thread's FIRST run ride `RunAgentInput.forwardedProps.plurnk` {§agui-forwarded-props}
91
+ (`projectRoot`, `constraints`, `settings` — composing over the bridge's questions default).
92
+
93
+ - **Topology scoping** {§agui-topology-scope} — the session broadcast carries every run's rows
94
+ (workers, the plurnk run, siblings); only the THREAD's model run projects onto the core
95
+ vocabulary. Foreign rows ride `plurnk.row`/`plurnk.ambient` — rich clients render the
96
+ topology; a worker's SEND never masquerades as the assistant speaking.
97
+ - **Concurrent runs: the daemon decides** — a `POST /` while a run is live passes through to
98
+ `loop.run`, whose OWN semantics apply (inject-into-the-active-loop or enqueue — the ack's
99
+ `action` says which). Mechanism, not policy: the bridge adds no queueing of its own; both
100
+ open streams observe the shared session events and end at `loop/terminated`.
101
+
102
+ - **Auth** {§agui-auth} — `PLURNK_AGUI_TOKEN` empty (shipped) = local trust: the loopback bind
103
+ is the boundary, the daemon's own posture. Set, every POST must carry
104
+ `Authorization: Bearer <token>`, checked before any body is read.
105
+ - **Daemon loss fails hard — by design, not omission**: the bridge holds no state the daemon
106
+ doesn't, so a dropped daemon connection kills the affected request loudly and the operator's
107
+ supervisor restarts the bridge (reattach reconstructs every thread by name). Backoff loops
108
+ hide outages; a dead daemon should look dead.
109
+
110
+ ## The run endpoint {§agui-run-endpoint}
111
+
112
+ `POST /` (or `/agui`) with an AG-UI `RunAgentInput` body: the last `user` message becomes the
113
+ `loop.run` prompt (`maxTurns`/`flags.yolo` from env); the response is `text/event-stream`,
114
+ one `data:` line per event, ending after `RUN_FINISHED`/`RUN_ERROR`. Yolo never auto-answers
115
+ a question — that's the daemon's own rule; the bridge inherits it.
@@ -0,0 +1,58 @@
1
+ import type { AguiEvent, ProposalNotification } from "./types.ts";
2
+ export declare const proposalToolCallId: (logEntryId: number) => string;
3
+ export declare const logEntryIdFromToolCallId: (toolCallId: string) => number | null;
4
+ export declare const proposalToolName: (op: string) => string;
5
+ export declare const proposalToolCall: (p: ProposalNotification) => AguiEvent[];
6
+ export interface ToolResultMessage {
7
+ toolCallId?: string;
8
+ content?: string;
9
+ role?: string;
10
+ }
11
+ export interface Resolution {
12
+ logEntryId: number;
13
+ decision: "accept" | "reject" | "cancel";
14
+ body?: string;
15
+ }
16
+ export declare const resolutionFromToolResult: (m: ToolResultMessage) => Resolution | null;
17
+ export interface AguiPlusState {
18
+ providers?: Array<{
19
+ alias: string;
20
+ model: string;
21
+ active: boolean;
22
+ contextSize: number | null;
23
+ }>;
24
+ session?: {
25
+ id: number;
26
+ name: string;
27
+ projectRoot?: string | null;
28
+ budget?: number | null;
29
+ };
30
+ sessions?: Array<{
31
+ id: number;
32
+ name: string;
33
+ }>;
34
+ constraints?: Array<{
35
+ effect: string;
36
+ glob: string;
37
+ }>;
38
+ }
39
+ export declare const stateSnapshot: (s: AguiPlusState) => AguiEvent;
40
+ export declare const stateDelta: (patches: Array<{
41
+ op: string;
42
+ path: string;
43
+ value?: unknown;
44
+ }>) => AguiEvent;
45
+ export interface ActionRequest {
46
+ kind: string;
47
+ params: Record<string, unknown>;
48
+ }
49
+ export declare const parseAction: (forwardedProps: unknown) => ActionRequest | null;
50
+ export type ActionOutcome = {
51
+ ok: true;
52
+ result?: unknown;
53
+ } | {
54
+ ok: false;
55
+ error: string;
56
+ };
57
+ export declare const actionResult: (kind: string, outcome: ActionOutcome) => AguiEvent;
58
+ //# sourceMappingURL=AguiPlus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AguiPlus.d.ts","sourceRoot":"","sources":["../src/AguiPlus.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAKlE,eAAO,MAAM,kBAAkB,eAAgB,MAAM,KAAG,MAA8B,CAAC;AACvF,eAAO,MAAM,wBAAwB,eAAgB,MAAM,KAAG,MAAM,GAAG,IAGtE,CAAC;AAOF,eAAO,MAAM,gBAAgB,OAAQ,MAAM,KAAG,MAAqE,CAAC;AAIpH,eAAO,MAAM,gBAAgB,MAAO,oBAAoB,KAAG,SAAS,EAOnE,CAAC;AAKF,MAAM,WAAW,iBAAiB;IAAG,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AAC3F,MAAM,WAAW,UAAU;IAAG,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AAC3G,eAAO,MAAM,wBAAwB,MAAO,iBAAiB,KAAG,UAAU,GAAG,IAiB5E,CAAC;AAIF,MAAM,WAAW,aAAa;IAC1B,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IACjG,OAAO,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC5F,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzD;AACD,eAAO,MAAM,aAAa,MAAO,aAAa,KAAG,SAAkE,CAAC;AACpH,eAAO,MAAM,UAAU,YAAa,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,KAAG,SAAsD,CAAC;AASlJ,MAAM,WAAW,aAAa;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE;AAChF,eAAO,MAAM,WAAW,mBAAoB,OAAO,KAAG,aAAa,GAAG,IAOrE,CAAC;AACF,MAAM,MAAM,aAAa,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAC1F,eAAO,MAAM,YAAY,SAAU,MAAM,WAAW,aAAa,KAAG,SACe,CAAC"}
@@ -0,0 +1,67 @@
1
+ // The AG-UI+ projection (single-interface rendering) — prototyped + unit-tested
2
+ // standalone ahead of the in-process module (plurnk-agui#2, WS-1). Pure functions:
3
+ // engine state → AG-UI events, and the inverse resume-mapping. No transport, no I/O
4
+ // — so it ports into the in-process module unchanged when the seam lands.
5
+ //
6
+ // §1 proposals/[300] questions → tool-calls (terminate-resume HITL); §2 reads →
7
+ // shared STATE. This is the flagship choreography de-risked as logic before code.
8
+ // ── §1 — stop-the-world → tool-call ──────────────────────────────────
9
+ // toolCallId correlates the TERMINATING run's TOOL_CALL with the RESUME run's
10
+ // tool-result → the exact pending proposal. Encodes the logEntryId: `prop:<id>`.
11
+ export const proposalToolCallId = (logEntryId) => `prop:${logEntryId}`;
12
+ export const logEntryIdFromToolCallId = (toolCallId) => {
13
+ const m = /^prop:(\d+)$/.exec(toolCallId);
14
+ return m === null ? null : Number(m[1]);
15
+ };
16
+ // Tool-call NAME — AG-UI terminology all the way up to the seam (operator ruling,
17
+ // 2026-07-09): the client-facing name is AG-UI-conventional, NOT plurnk-namespaced.
18
+ // A side-effecting proposal is an approval request; a [300] question elicits input.
19
+ // The plurnk correlation rides the opaque toolCallId (`prop:<logEntryId>`), so the
20
+ // generic names carry no plurnk vocabulary upward.
21
+ export const proposalToolName = (op) => (op === "SEND" ? "request_user_input" : "request_approval");
22
+ // The run's tail when it hits a pause: the tool-call, then the CALLER emits
23
+ // RUN_FINISHED to terminate. The loop stays paused in-engine — untouched.
24
+ export const proposalToolCall = (p) => {
25
+ const toolCallId = proposalToolCallId(p.logEntryId);
26
+ return [
27
+ { type: "TOOL_CALL_START", toolCallId, toolCallName: proposalToolName(p.op) },
28
+ { type: "TOOL_CALL_ARGS", toolCallId, delta: JSON.stringify({ op: p.op, target: p.target, body: p.body, attrs: p.attrs, staleClobberRisk: p.staleClobberRisk ?? false }) },
29
+ { type: "TOOL_CALL_END", toolCallId },
30
+ ];
31
+ };
32
+ export const resolutionFromToolResult = (m) => {
33
+ if (typeof m.toolCallId !== "string")
34
+ return null;
35
+ const logEntryId = logEntryIdFromToolCallId(m.toolCallId);
36
+ if (logEntryId === null)
37
+ return null;
38
+ let decision;
39
+ let body;
40
+ if (typeof m.content === "string" && m.content.length > 0) {
41
+ try {
42
+ const parsed = JSON.parse(m.content);
43
+ decision = parsed.decision;
44
+ body = parsed.body;
45
+ }
46
+ catch {
47
+ decision = m.content.trim(); // tolerate a bare decision string
48
+ }
49
+ }
50
+ if (decision !== "accept" && decision !== "reject" && decision !== "cancel")
51
+ return null;
52
+ return { logEntryId, decision, ...(body !== undefined ? { body } : {}) };
53
+ };
54
+ export const stateSnapshot = (s) => ({ type: "STATE_SNAPSHOT", snapshot: { plurnk: s } });
55
+ export const stateDelta = (patches) => ({ type: "STATE_DELTA", delta: patches });
56
+ export const parseAction = (forwardedProps) => {
57
+ const action = forwardedProps?.plurnk?.action;
58
+ if (action === null || typeof action !== "object")
59
+ return null;
60
+ const kind = action.kind;
61
+ if (typeof kind !== "string" || kind.length === 0)
62
+ return null;
63
+ const { kind: _kind, ...params } = action;
64
+ return { kind, params };
65
+ };
66
+ export const actionResult = (kind, outcome) => ({ type: "CUSTOM", name: "plurnk.action.result", value: { kind, ...outcome } });
67
+ //# sourceMappingURL=AguiPlus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AguiPlus.js","sourceRoot":"","sources":["../src/AguiPlus.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export default class DaemonClient {
2
+ #private;
3
+ private constructor();
4
+ static connect(url: string): Promise<DaemonClient>;
5
+ call<T = unknown>(method: string, params?: object): Promise<T>;
6
+ on(method: string, fn: (params: unknown) => void): () => void;
7
+ close(): void;
8
+ }
9
+ //# sourceMappingURL=DaemonClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DaemonClient.d.ts","sourceRoot":"","sources":["../src/DaemonClient.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,OAAO,OAAO,YAAY;;IAM7B,OAAO,eAeN;IAED,OAAa,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAOvD;IAED,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAMjE;IAED,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAK5D;IAED,KAAK,IAAI,IAAI,CAEZ;CACJ"}
@@ -0,0 +1,55 @@
1
+ // An ordinary client of the plurnk daemon's JSON-RPC-over-WebSocket wire — the same protocol
2
+ // the CLI/TUI/nvim clients speak. The bridge holds no state the daemon doesn't (§agui-daemon-client).
3
+ export default class DaemonClient {
4
+ #ws;
5
+ #nextId = 1;
6
+ #pending = new Map();
7
+ #listeners = new Map();
8
+ constructor(ws) {
9
+ this.#ws = ws;
10
+ ws.addEventListener("message", (ev) => {
11
+ const msg = JSON.parse(String(ev.data));
12
+ if (typeof msg.id === "number" && this.#pending.has(msg.id)) {
13
+ const waiter = this.#pending.get(msg.id);
14
+ this.#pending.delete(msg.id);
15
+ if (msg.error !== undefined)
16
+ waiter.reject(new Error(msg.error.message));
17
+ else
18
+ waiter.resolve(msg.result);
19
+ return;
20
+ }
21
+ if (typeof msg.method === "string") {
22
+ for (const fn of this.#listeners.get(msg.method) ?? [])
23
+ fn(msg.params);
24
+ }
25
+ });
26
+ }
27
+ static async connect(url) {
28
+ const ws = new WebSocket(url);
29
+ await new Promise((resolve, reject) => {
30
+ ws.addEventListener("open", () => resolve(), { once: true });
31
+ ws.addEventListener("error", () => reject(new Error(`plurnk-agui: cannot reach the daemon at ${url} — is plurnk-service running?`)), { once: true });
32
+ });
33
+ return new DaemonClient(ws);
34
+ }
35
+ call(method, params = {}) {
36
+ const id = this.#nextId++;
37
+ return new Promise((resolve, reject) => {
38
+ this.#pending.set(id, { resolve: resolve, reject });
39
+ this.#ws.send(JSON.stringify({ jsonrpc: "2.0", id, method, params }));
40
+ });
41
+ }
42
+ on(method, fn) {
43
+ let set = this.#listeners.get(method);
44
+ if (set === undefined) {
45
+ set = new Set();
46
+ this.#listeners.set(method, set);
47
+ }
48
+ set.add(fn);
49
+ return () => { set.delete(fn); };
50
+ }
51
+ close() {
52
+ this.#ws.close();
53
+ }
54
+ }
55
+ //# sourceMappingURL=DaemonClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DaemonClient.js","sourceRoot":"","sources":["../src/DaemonClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ export type ProposalDecision = "accept" | "reject" | "cancel";
2
+ export interface ProposalResolution {
3
+ decision: ProposalDecision;
4
+ body?: string;
5
+ outcome?: string;
6
+ }
7
+ export interface PendingProposal {
8
+ logEntryId: number;
9
+ runId: number;
10
+ loopId: number;
11
+ turnId: number;
12
+ op: string;
13
+ suffix: string;
14
+ scheme: string | null;
15
+ pathname: string | null;
16
+ tx: string | null;
17
+ attrs: string | null;
18
+ }
19
+ export interface PlurnkStatement {
20
+ op: string;
21
+ [key: string]: unknown;
22
+ }
23
+ export type LogEntryWire = Record<string, unknown>;
24
+ export interface DaemonSeam {
25
+ subscribeToEvents(handler: (sessionId: number | null, method: string, params: unknown) => void): () => void;
26
+ pendingProposals(sessionId: number): Promise<PendingProposal[]>;
27
+ resolveProposal(logEntryId: number, resolution: ProposalResolution): void;
28
+ runLoop(args: {
29
+ sessionId: number;
30
+ runId: number;
31
+ prompt: string;
32
+ maxTurns?: number;
33
+ flags?: {
34
+ yolo?: boolean;
35
+ };
36
+ openPaths?: string[];
37
+ }): Promise<{
38
+ action: "injected_next_turn" | "enqueued_new_loop";
39
+ loopId: number;
40
+ turnSeq?: number;
41
+ }>;
42
+ cancelDrain(runId: number, reason?: string): boolean;
43
+ dispatchAsClient(args: {
44
+ sessionId: number;
45
+ runId: number;
46
+ statement: PlurnkStatement;
47
+ }): Promise<{
48
+ status: number;
49
+ [key: string]: unknown;
50
+ }>;
51
+ readLog(args: {
52
+ sessionId: number;
53
+ runId: number;
54
+ loopId?: number;
55
+ turnId?: number;
56
+ sinceId?: number;
57
+ limit?: number;
58
+ loopSeq?: number;
59
+ turnSeq?: number;
60
+ sequence?: number;
61
+ }): Promise<LogEntryWire[]>;
62
+ listProviders(): {
63
+ aliases: Array<{
64
+ alias: string;
65
+ provider: string;
66
+ model: string;
67
+ active: boolean;
68
+ contextSize: number | null;
69
+ }>;
70
+ };
71
+ createSession(args: {
72
+ name?: string;
73
+ projectRoot?: string | null;
74
+ settings?: string;
75
+ constraints?: Array<{
76
+ effect: string;
77
+ glob: string;
78
+ }>;
79
+ }): Promise<ClientEnvelope>;
80
+ attachSession(args: {
81
+ sessionId: number;
82
+ runId?: number;
83
+ runName?: string;
84
+ }): Promise<ClientEnvelope>;
85
+ listSessions(): Promise<Array<{
86
+ id: number;
87
+ name: string;
88
+ }>>;
89
+ listRuns(sessionId: number): Promise<Array<{
90
+ id: number;
91
+ name: string;
92
+ }>>;
93
+ }
94
+ export interface ClientEnvelope {
95
+ sessionId: number;
96
+ sessionName: string;
97
+ projectRoot: string | null;
98
+ runId: number;
99
+ runName: string;
100
+ modelRunId: number | null;
101
+ clientLoopId: number | null;
102
+ }
103
+ //# sourceMappingURL=DaemonSeam.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DaemonSeam.d.ts","sourceRoot":"","sources":["../src/DaemonSeam.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ // My coupling to the daemon's in-process client-interface seam (service#355). The
2
+ // module depends on THIS interface — the committed, tested contract — never the
3
+ // Daemon class's guts. It grows as the service lands hooks (loop-control,
4
+ // session/envelope, reads, fork, execs/auth, the boot plug-point); today it carries
5
+ // what's published: the event source (B) and proposal read + resolve (C + A-resolve).
6
+ export {};
7
+ //# sourceMappingURL=DaemonSeam.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DaemonSeam.js","sourceRoot":"","sources":["../src/DaemonSeam.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,gFAAgF;AAChF,0EAA0E;AAC1E,oFAAoF;AACpF,sFAAsF"}
@@ -0,0 +1,14 @@
1
+ import type { AguiEvent } from "./types.ts";
2
+ export default class EventRouter {
3
+ #private;
4
+ constructor(args: {
5
+ threadId: string;
6
+ runId: string;
7
+ modelRunId?: number | null;
8
+ sessionId?: number | null;
9
+ });
10
+ runStarted(snapshot?: unknown): AguiEvent[];
11
+ replay(entries: Array<Record<string, unknown>>): AguiEvent[];
12
+ route(method: string, params: unknown): AguiEvent[];
13
+ }
14
+ //# sourceMappingURL=EventRouter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventRouter.d.ts","sourceRoot":"","sources":["../src/EventRouter.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAgD,MAAM,YAAY,CAAC;AAE1F,MAAM,CAAC,OAAO,OAAO,WAAW;;IAG5B,YAAY,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAE3G;IAED,UAAU,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,CAAyC;IACpF,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,EAAE,CAAoC;IAEhG,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,CAUlD;CACJ"}
@@ -0,0 +1,27 @@
1
+ // The module's per-run render core (plurnk-agui#2). Routes a daemon event
2
+ // (method, params) from the seam's event source → AG-UI events, composing the proven
3
+ // projections: log/entry → core vocab, loop/terminated → RUN_FINISHED + budget STATE,
4
+ // telemetry/stream → CUSTOM. Per-run state (turn tracking, model-run binding) lives in
5
+ // the Translator. Proposals are ProposalHitl's domain (the terminate-resume tool-call),
6
+ // so this router deliberately leaves loop/proposal to it — one owner per concern.
7
+ import Translator from "./Translator.js";
8
+ export default class EventRouter {
9
+ #t;
10
+ constructor(args) {
11
+ this.#t = new Translator(args);
12
+ }
13
+ runStarted(snapshot) { return this.#t.runStarted(snapshot); }
14
+ replay(entries) { return this.#t.replay(entries); }
15
+ route(method, params) {
16
+ switch (method) {
17
+ case "log/entry": return this.#t.logEntry(params);
18
+ case "loop/terminated": return this.#t.terminated(params);
19
+ case "telemetry/event": return this.#t.telemetry(params.event ?? params);
20
+ case "stream/event":
21
+ case "stream/concluded": return [{ type: "CUSTOM", name: "plurnk.stream", value: params }];
22
+ case "loop/proposal": return []; // ProposalHitl owns HITL (terminate-resume tool-call)
23
+ default: return []; // session/created + anything unmodeled: the module handles out-of-band
24
+ }
25
+ }
26
+ }
27
+ //# sourceMappingURL=EventRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventRouter.js","sourceRoot":"","sources":["../src/EventRouter.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,qFAAqF;AACrF,sFAAsF;AACtF,uFAAuF;AACvF,wFAAwF;AACxF,kFAAkF;AAElF,OAAO,UAAU,MAAM,iBAAiB,CAAC;AAGzC,MAAM,CAAC,OAAO,OAAO,WAAW;IAC5B,EAAE,CAAa;IAEf,YAAY,IAAgG;QACxG,IAAI,CAAC,EAAE,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,UAAU,CAAC,QAAkB,IAAiB,OAAO,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACpF,MAAM,CAAC,OAAuC,IAAiB,OAAO,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAEhG,KAAK,CAAC,MAAc,EAAE,MAAe;QACjC,QAAQ,MAAM,EAAE,CAAC;YACb,KAAK,WAAW,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,MAA8B,CAAC,CAAC;YAC1E,KAAK,iBAAiB,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAgC,CAAC,CAAC;YACpF,KAAK,iBAAiB,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC,SAAS,CAAE,MAA8B,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC;YAClG,KAAK,cAAc,CAAC;YACpB,KAAK,kBAAkB,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YAC3F,KAAK,eAAe,EAAE,OAAO,EAAE,CAAC,CAAC,sDAAsD;YACvF,SAAS,OAAO,EAAE,CAAC,CAAC,uEAAuE;QAC/F,CAAC;IACL,CAAC;CACJ"}
@@ -0,0 +1,23 @@
1
+ import type { DaemonSeam } from "./DaemonSeam.ts";
2
+ export interface ModuleOptions {
3
+ host: string;
4
+ port: number;
5
+ sessionPrefix: string;
6
+ token?: string;
7
+ maxTurns?: number;
8
+ }
9
+ export default class Module {
10
+ #private;
11
+ constructor(seam: DaemonSeam, opts: ModuleOptions);
12
+ static init(opts: ModuleOptions): (seam: DaemonSeam) => Promise<Module>;
13
+ listen(): Promise<{
14
+ host: string;
15
+ port: number;
16
+ }>;
17
+ address(): {
18
+ host: string;
19
+ port: number;
20
+ };
21
+ close(): Promise<void>;
22
+ }
23
+ //# sourceMappingURL=Module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Module.d.ts","sourceRoot":"","sources":["../src/Module.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAkB,MAAM,iBAAiB,CAAC;AAGlE,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,CAAC,OAAO,OAAO,MAAM;;IAOvB,YAAY,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,EAKhD;IAGD,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAMtE;IAEK,MAAM,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAMtD;IAED,OAAO,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAIxC;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAG3B;CAwIJ"}