@geohar/unbien-durable 0.0.0-stage → 0.1.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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -2
  3. package/dist/agent_config.d.ts +56 -0
  4. package/dist/agent_config.js +92 -0
  5. package/dist/agent_config.js.map +1 -0
  6. package/dist/bridge.d.ts +175 -0
  7. package/dist/bridge.js +921 -0
  8. package/dist/bridge.js.map +1 -0
  9. package/dist/event_source.d.ts +53 -0
  10. package/dist/event_source.js +124 -0
  11. package/dist/event_source.js.map +1 -0
  12. package/dist/extension.d.ts +34 -0
  13. package/dist/extension.js +46 -0
  14. package/dist/extension.js.map +1 -0
  15. package/dist/host.d.ts +2 -0
  16. package/dist/host.js +261 -0
  17. package/dist/host.js.map +1 -0
  18. package/dist/index.d.ts +17 -0
  19. package/dist/index.js +18 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/launch_wait_quit.d.ts +12 -0
  22. package/dist/launch_wait_quit.js +136 -0
  23. package/dist/launch_wait_quit.js.map +1 -0
  24. package/dist/mesh_delivery.d.ts +28 -0
  25. package/dist/mesh_delivery.js +31 -0
  26. package/dist/mesh_delivery.js.map +1 -0
  27. package/dist/mirror.d.ts +56 -0
  28. package/dist/mirror.js +136 -0
  29. package/dist/mirror.js.map +1 -0
  30. package/dist/ollama_cloud.d.ts +2 -0
  31. package/dist/ollama_cloud.js +98 -0
  32. package/dist/ollama_cloud.js.map +1 -0
  33. package/dist/pi_launch.d.ts +50 -0
  34. package/dist/pi_launch.js +84 -0
  35. package/dist/pi_launch.js.map +1 -0
  36. package/dist/port.d.ts +94 -0
  37. package/dist/port.js +121 -0
  38. package/dist/port.js.map +1 -0
  39. package/dist/registrar.d.ts +7 -0
  40. package/dist/registrar.js +31 -0
  41. package/dist/registrar.js.map +1 -0
  42. package/dist/thinking.d.ts +24 -0
  43. package/dist/thinking.js +36 -0
  44. package/dist/thinking.js.map +1 -0
  45. package/dist/view_translator.d.ts +83 -0
  46. package/dist/view_translator.js +317 -0
  47. package/dist/view_translator.js.map +1 -0
  48. package/package.json +63 -3
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @geohar/unbien-durable — public surface: the durable binding's ports,
3
+ * registrar, event source, extension bundle, launch port, and bridge.
4
+ * `host.ts` is the CLI entry (bin: unbien-durable) and stays out of the
5
+ * library surface.
6
+ */
7
+ export { DurableSessionControl, DurableSessionPort, DurableTranscriptMirror, } from "./port.js";
8
+ export { DurableToolRegistrar } from "./registrar.js";
9
+ export { ConversationViewSource, } from "./event_source.js";
10
+ export { ViewTranslator } from "./view_translator.js";
11
+ export { entryIdOf, toWire } from "./mirror.js";
12
+ export { createUnBienDurableExtension, } from "./extension.js";
13
+ export { HeadlessPiLaunch, HeadlessPiSession, } from "./pi_launch.js";
14
+ export { createLaunchWaitQuitTool, } from "./launch_wait_quit.js";
15
+ export { DurableBridge } from "./bridge.js";
16
+ export { ollamaCloudProvider } from "./ollama_cloud.js";
17
+ export { DEFAULT_THINKING_LEVEL, ensureThinkingLevel, parseThinkingLevel, } from "./thinking.js";
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,GAGxB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EACL,sBAAsB,GAEvB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,cAAc,EAA8B,MAAM,sBAAsB,CAAA;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EACL,4BAA4B,GAE7B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EACL,wBAAwB,GAEzB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,aAAa,EAA6B,MAAM,aAAa,CAAA;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,eAAe,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { type ToolRegistration } from "@earendil-works/pi-durable";
2
+ import type { HeadlessLaunchPort } from "@geohar/un-bien/core";
3
+ import type { MeshInbox, MeshNode } from "@geohar/un-bien/durable";
4
+ export interface LaunchWaitQuitDeps {
5
+ /** The headless launch port (HeadlessPiLaunch). */
6
+ launch: HeadlessLaunchPort;
7
+ /** The HOST's mesh node — the tool sends from the host's own peer. */
8
+ getMeshNode(): MeshNode | null;
9
+ /** The host's inbound mesh inbox (correlated reply waits). */
10
+ getInbox(): MeshInbox;
11
+ }
12
+ export declare function createLaunchWaitQuitTool(deps: LaunchWaitQuitDeps): ToolRegistration;
@@ -0,0 +1,136 @@
1
+ /**
2
+ * `launch_pi_task` — the durable binding's launch-wait-quit tool, and the
3
+ * ONE place the two bindings meet at runtime.
4
+ *
5
+ * Contract (documented for the model in the description):
6
+ * 1. fire up a headless pi-coding-agent session in the target cwd (a
7
+ * full interactive-equivalent pi: coding tools, its own model);
8
+ * 2. it joins the un-bien UDS mesh under a scoped name minted per call;
9
+ * 3. the task body is agent_sent to it as a MESH message — the launched
10
+ * session's own model handles it in its own turn and replies with
11
+ * `re=<send id>`;
12
+ * 4. the reply lands in the host's mesh inbox and is returned as the
13
+ * tool result;
14
+ * 5. the launched session is torn down in a finally — a failed task never
15
+ * leaks a live pi peer.
16
+ *
17
+ * Why mesh messages and not a direct prompt: the launched session is an
18
+ * independent agent (its own harness, its own model config); the mesh
19
+ * request/reply correlation gives both sides durable delivery semantics and
20
+ * the same observability every other peer sees.
21
+ */
22
+ import { Type } from "typebox";
23
+ import { defineTool } from "@earendil-works/pi-durable";
24
+ /** How long to wait for the peer's correlated reply (default 5 min — the
25
+ * launched agent runs a full multi-turn task with tools). */
26
+ const DEFAULT_REPLY_TIMEOUT_MS = 300_000;
27
+ /** Broker ACK deadline for the task delivery. */
28
+ const SEND_ACK_TIMEOUT_MS = 5_000;
29
+ export function createLaunchWaitQuitTool(deps) {
30
+ return defineTool({
31
+ name: "launch_pi_task",
32
+ description: "Launch a short-lived headless pi coding-agent session in a directory, " +
33
+ "send it a task over the agent mesh, wait for its reply, then shut it " +
34
+ "down. The launched session is a full pi agent (coding tools, its own " +
35
+ "model) joined to the local UDS mesh under a per-call scoped name — " +
36
+ "use it for ad-hoc coding firepower you don't want in your own " +
37
+ "conversation. The task must be self-contained: the peer has no " +
38
+ "access to this conversation's context. The target directory must " +
39
+ "have un-bien configured (auto-start) and the un-bien extension " +
40
+ "discoverable by pi there; a reply that never comes surfaces as a " +
41
+ "timeout, not a silent success.",
42
+ parameters: Type.Object({
43
+ task: Type.String({
44
+ description: "The task to send the launched agent, as a self-contained " +
45
+ "instruction (its reply comes back as this tool's result).",
46
+ }),
47
+ cwd: Type.Optional(Type.String({
48
+ description: "Directory to launch in. Default: this host's working directory.",
49
+ })),
50
+ timeout_ms: Type.Optional(Type.Number({
51
+ description: "Max ms to wait for the reply (default 300000 = 5 minutes).",
52
+ })),
53
+ }),
54
+ execute: async (args, _api, _context) => {
55
+ const meshNode = deps.getMeshNode();
56
+ if (!meshNode) {
57
+ return {
58
+ output: [
59
+ {
60
+ type: "text",
61
+ text: "Not on the agent mesh — the host must join the mesh before launching pi tasks.",
62
+ },
63
+ ],
64
+ isError: true,
65
+ };
66
+ }
67
+ const cwd = args.cwd ?? process.cwd();
68
+ const replyTimeout = typeof args.timeout_ms === "number" && args.timeout_ms > 0
69
+ ? args.timeout_ms
70
+ : DEFAULT_REPLY_TIMEOUT_MS;
71
+ // Scoped name: per-call unique, broker-safe (hyphens, no slashes —
72
+ // the broker's name grammar rejects path separators).
73
+ const name = `pi-task-${crypto.randomUUID().slice(0, 8)}`;
74
+ const peer = meshNode.peer();
75
+ const session = await deps.launch.launchHeadless({ cwd, name });
76
+ try {
77
+ await session.joinMeshUnderName();
78
+ const { address } = await session.awaitReady();
79
+ const ack = await peer.sendWithAck(address, { type: "pi_task", task: args.task }, null, SEND_ACK_TIMEOUT_MS);
80
+ if (ack.status !== "received") {
81
+ return {
82
+ output: [
83
+ {
84
+ type: "text",
85
+ text: `Task delivery to "${address}" failed (${ack.status}${ack.error ? `: ${ack.error}` : ""}). The session was shut down.`,
86
+ },
87
+ ],
88
+ isError: true,
89
+ };
90
+ }
91
+ const reply = await deps
92
+ .getInbox()
93
+ .wait({ re: ack.id }, replyTimeout, { interruptOnUnmatched: true });
94
+ if (!reply) {
95
+ return {
96
+ output: [
97
+ {
98
+ type: "text",
99
+ text: `No reply within ${replyTimeout}ms (send id ${ack.id}). ` +
100
+ `The session was shut down; if the reply arrives later it ` +
101
+ `lands in this host's mesh inbox.`,
102
+ },
103
+ ],
104
+ };
105
+ }
106
+ if (reply.re !== ack.id) {
107
+ // Early interrupt — an uncorrelated message arrived while waiting.
108
+ return {
109
+ output: [
110
+ {
111
+ type: "text",
112
+ text: `Early interrupt while awaiting the task reply: ${reply.from} ` +
113
+ `sent ${reply.id} (re=${reply.re}). Handle it, then re-issue ` +
114
+ `launch_pi_task. The launched session was shut down.`,
115
+ },
116
+ ],
117
+ };
118
+ }
119
+ const text = typeof reply.body === "string"
120
+ ? reply.body
121
+ : JSON.stringify(reply.body);
122
+ // SAFETY: a mesh reply body is a JSON tree by wire contract (the
123
+ // envelope codec only carries JSON); the inbox types it `unknown`
124
+ // only to stay harness-neutral.
125
+ return {
126
+ output: [{ type: "text", text }],
127
+ details: reply.body,
128
+ };
129
+ }
130
+ finally {
131
+ await session.tearDown();
132
+ }
133
+ },
134
+ });
135
+ }
136
+ //# sourceMappingURL=launch_wait_quit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch_wait_quit.js","sourceRoot":"","sources":["../src/launch_wait_quit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAE9B,OAAO,EAAE,UAAU,EAAyB,MAAM,4BAA4B,CAAA;AAM9E;8DAC8D;AAC9D,MAAM,wBAAwB,GAAG,OAAO,CAAA;AACxC,iDAAiD;AACjD,MAAM,mBAAmB,GAAG,KAAK,CAAA;AAWjC,MAAM,UAAU,wBAAwB,CACtC,IAAwB;IAExB,OAAO,UAAU,CAAC;QAChB,IAAI,EAAE,gBAAgB;QACtB,WAAW,EACT,wEAAwE;YACxE,uEAAuE;YACvE,uEAAuE;YACvE,qEAAqE;YACrE,gEAAgE;YAChE,iEAAiE;YACjE,mEAAmE;YACnE,iEAAiE;YACjE,mEAAmE;YACnE,gCAAgC;QAClC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;YACtB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;gBAChB,WAAW,EACT,2DAA2D;oBAC3D,2DAA2D;aAC9D,CAAC;YACF,GAAG,EAAE,IAAI,CAAC,QAAQ,CAChB,IAAI,CAAC,MAAM,CAAC;gBACV,WAAW,EACT,iEAAiE;aACpE,CAAC,CACH;YACD,UAAU,EAAE,IAAI,CAAC,QAAQ,CACvB,IAAI,CAAC,MAAM,CAAC;gBACV,WAAW,EACT,4DAA4D;aAC/D,CAAC,CACH;SACF,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;YACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;YACnC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,OAAO;oBACL,MAAM,EAAE;wBACN;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,gFAAgF;yBACvF;qBACF;oBACD,OAAO,EAAE,IAAI;iBACd,CAAA;YACH,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAA;YACrC,MAAM,YAAY,GAChB,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,GAAG,CAAC;gBACxD,CAAC,CAAC,IAAI,CAAC,UAAU;gBACjB,CAAC,CAAC,wBAAwB,CAAA;YAC9B,mEAAmE;YACnE,sDAAsD;YACtD,MAAM,IAAI,GAAG,WAAW,MAAM,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAA;YACzD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAA;YAC5B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAA;YAC/D,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,iBAAiB,EAAE,CAAA;gBACjC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAA;gBAC9C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,WAAW,CAChC,OAAO,EACP,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EACpC,IAAI,EACJ,mBAAmB,CACpB,CAAA;gBACD,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;oBAC9B,OAAO;wBACL,MAAM,EAAE;4BACN;gCACE,IAAI,EAAE,MAAM;gCACZ,IAAI,EAAE,qBAAqB,OAAO,aAAa,GAAG,CAAC,MAAM,GACvD,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EACjC,+BAA+B;6BAChC;yBACF;wBACD,OAAO,EAAE,IAAI;qBACd,CAAA;gBACH,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,IAAI;qBACrB,QAAQ,EAAE;qBACV,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC,CAAA;gBACrE,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,OAAO;wBACL,MAAM,EAAE;4BACN;gCACE,IAAI,EAAE,MAAM;gCACZ,IAAI,EACF,mBAAmB,YAAY,eAAe,GAAG,CAAC,EAAE,KAAK;oCACzD,2DAA2D;oCAC3D,kCAAkC;6BACrC;yBACF;qBACF,CAAA;gBACH,CAAC;gBACD,IAAI,KAAK,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,EAAE,CAAC;oBACxB,mEAAmE;oBACnE,OAAO;wBACL,MAAM,EAAE;4BACN;gCACE,IAAI,EAAE,MAAM;gCACZ,IAAI,EACF,kDAAkD,KAAK,CAAC,IAAI,GAAG;oCAC/D,QAAQ,KAAK,CAAC,EAAE,QAAQ,KAAK,CAAC,EAAE,8BAA8B;oCAC9D,qDAAqD;6BACxD;yBACF;qBACF,CAAA;gBACH,CAAC;gBACD,MAAM,IAAI,GACR,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;oBAC5B,CAAC,CAAC,KAAK,CAAC,IAAI;oBACZ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBAChC,iEAAiE;gBACjE,kEAAkE;gBAClE,gCAAgC;gBAChC,OAAO;oBACL,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;oBAChC,OAAO,EAAE,KAAK,CAAC,IAAiB;iBACjC,CAAA;YACH,CAAC;oBAAS,CAAC;gBACT,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAA;YAC1B,CAAC;QACH,CAAC;KACF,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The mesh → inbox feed: inbound mesh envelopes land in the MeshInbox (the
3
+ * model's pull/wait surface behind launch_pi_task / mesh_get_message /
4
+ * mesh_list_messages / mesh_wait_message).
5
+ *
6
+ * The durable analog of the pi extension's `peer.onMessage` →
7
+ * `_deliverMeshMessageToAgent` wiring (extension/src/commands/lifecycle.ts):
8
+ * broker system events (peer_joined / peer_left) and broker control frames
9
+ * are filtered, and every REAL agent-to-agent envelope is stored in the
10
+ * inbox. Replies arrive here already correlation-stamped by the sender
11
+ * (`env.re` = the outbound envelope id), so `launch_pi_task`'s
12
+ * `inbox.wait({ re: ack.id })` resolves the moment the launched peer's reply
13
+ * is delivered.
14
+ *
15
+ * Difference from the pi binding, by design: pi ALSO nudges the model (the
16
+ * drain pushes the message via pi.sendMessage as a custom message). The
17
+ * durable host has no push surface — the conversation is pull/wait only.
18
+ * A message that arrives while the model is between turns sits in the inbox
19
+ * until the model pulls it (mesh_list_messages) or a correlated wait
20
+ * (launch_pi_task, mesh_wait_message) returns it.
21
+ */
22
+ import type { MeshInbox, MeshNode } from "@geohar/un-bien/durable";
23
+ /**
24
+ * Subscribe `meshNode`'s message delivery into `inbox`. Idempotent per
25
+ * subscription (MeshNode.onMessage returns an unsubscribe the host never
26
+ * needs — the node's close() drops it).
27
+ */
28
+ export declare function wireMeshInbox(meshNode: MeshNode, inbox: MeshInbox): void;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Subscribe `meshNode`'s message delivery into `inbox`. Idempotent per
3
+ * subscription (MeshNode.onMessage returns an unsubscribe the host never
4
+ * needs — the node's close() drops it).
5
+ */
6
+ export function wireMeshInbox(meshNode, inbox) {
7
+ meshNode.onMessage((env) => {
8
+ const body = env.body;
9
+ // Broker system events: the roster self-heals — list_peers (the mesh
10
+ // tool + the host's peer inventory) queries the broker live, so there
11
+ // is no cached count to refresh on the durable host (the pi extension
12
+ // refreshes a session peer-count badge here; we have no badge).
13
+ if (body && (body.type === "peer_joined" || body.type === "peer_left"))
14
+ return;
15
+ if (env.from === "broker")
16
+ return; // other broker control messages — ignore
17
+ // Real agent-to-agent message: store for pull/wait. `deliveredAt`
18
+ // stays null — the durable binding has no pi.sendMessage push, so
19
+ // "delivered" means "in the inbox", which `sentAt` already records.
20
+ inbox.add({
21
+ id: env.id,
22
+ from: env.from,
23
+ to: meshNode.address(),
24
+ body: env.body,
25
+ re: env.re,
26
+ sentAt: Date.now(),
27
+ deliveredAt: null,
28
+ });
29
+ });
30
+ }
31
+ //# sourceMappingURL=mesh_delivery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mesh_delivery.js","sourceRoot":"","sources":["../src/mesh_delivery.ts"],"names":[],"mappings":"AAuBA;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,QAAkB,EAAE,KAAgB;IAChE,QAAQ,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE;QACzB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAgC,CAAA;QACjD,qEAAqE;QACrE,sEAAsE;QACtE,sEAAsE;QACtE,gEAAgE;QAChE,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,aAAa,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,CAAC;YACpE,OAAM;QACR,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAM,CAAC,yCAAyC;QAE3E,kEAAkE;QAClE,kEAAkE;QAClE,oEAAoE;QACpE,KAAK,CAAC,GAAG,CAAC;YACR,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,EAAE,EAAE,QAAQ,CAAC,OAAO,EAAE;YACtB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE;YAClB,WAAW,EAAE,IAAI;SAClB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Synchronous view of the durable transcript, in pi's `SessionEntry` wire
3
+ * shapes, so the app's entry replay (get_entries) renders a durable
4
+ * conversation exactly as it renders a pi session.
5
+ *
6
+ * Fed by the ConversationViewSource with every entry the conversation's
7
+ * ConversationTranscript holds (the watch window plus the pages loaded
8
+ * before it), in entry-id order; `ingest` ignores ids it already has, so
9
+ * the whole held transcript can be offered on every view value.
10
+ *
11
+ * Mapping (durable EntryRecord → pi entry):
12
+ * pi.user / pi.assistant / pi.tool-result (and any kind carrying model
13
+ * messages) → one `message` entry per model message, unchanged: pi-ai
14
+ * messages are pi's own, so thinking, toolCall, image, and toolResult
15
+ * blocks (with `details`, `isError`) keep their types. The first message
16
+ * keeps the entry id; later ones are `<id>.<n>`.
17
+ * pi.compaction → `compaction` {summary, firstKeptEntryId, tokensBefore}:
18
+ * the summary unwrapped from its `<summary>` user message.
19
+ * pi.reset → `custom_message` customType `un-bien:durable-reset`
20
+ * (display), carrying the handoff text when there is one.
21
+ * un-bien.app → `custom_message` with the app entry's customType,
22
+ * content, display, and details.
23
+ * anything else (pi.system, extension bookkeeping) → a typed stub
24
+ * `{type: kind}`, which the app skips like pi entry types it doesn't
25
+ * render.
26
+ *
27
+ * Wire ids are stringified durable ids (the app keys entries by string and
28
+ * pages with `since: <id>`); `entryIdOf` parses one back, suffix and all.
29
+ * Timestamps come from the entry's first model message; entries without one
30
+ * take the time this process first saw them.
31
+ */
32
+ import type { EntryId, EntryRecord } from "@earendil-works/pi-durable";
33
+ import type { EntriesPage, EntryRange } from "@geohar/un-bien/core";
34
+ /** The app-entry kind the bridge commits custom entries under. */
35
+ export declare const UN_BIEN_APP_KIND = "un-bien.app";
36
+ /** customType of a context reset in the app's transcript. */
37
+ export declare const RESET_CUSTOM_TYPE = "un-bien:durable-reset";
38
+ /** The durable entry id a wire id names (`"12"` and `"12.1"` → 12). */
39
+ export declare function entryIdOf(wireId: string): EntryId | undefined;
40
+ export declare class DurableTranscriptMirror {
41
+ private readonly wire;
42
+ private newest;
43
+ /** Add the records newer than every held one; returns those added. */
44
+ ingest(records: readonly EntryRecord[]): EntryRecord[];
45
+ /** The current leaf id (the newest wire entry). */
46
+ get leafId(): string | null;
47
+ page(range?: EntryRange): EntriesPage;
48
+ }
49
+ type WireEntry = {
50
+ id: string;
51
+ type: string;
52
+ timestamp: string;
53
+ } & Record<string, unknown>;
54
+ /** Map one durable entry to its pi wire entries (without parentId). */
55
+ export declare function toWire(record: EntryRecord): WireEntry[];
56
+ export {};
package/dist/mirror.js ADDED
@@ -0,0 +1,136 @@
1
+ /** The app-entry kind the bridge commits custom entries under. */
2
+ export const UN_BIEN_APP_KIND = "un-bien.app";
3
+ /** customType of a context reset in the app's transcript. */
4
+ export const RESET_CUSTOM_TYPE = "un-bien:durable-reset";
5
+ const SUMMARY_PREFIX = "The conversation history before this point was compacted into the following summary:\n\n<summary>\n";
6
+ const SUMMARY_SUFFIX = "\n</summary>";
7
+ /** The durable entry id a wire id names (`"12"` and `"12.1"` → 12). */
8
+ export function entryIdOf(wireId) {
9
+ const match = /^(\d+)(?:\.\d+)?$/.exec(wireId);
10
+ return match ? Number(match[1]) : undefined;
11
+ }
12
+ export class DurableTranscriptMirror {
13
+ wire = [];
14
+ newest;
15
+ /** Add the records newer than every held one; returns those added. */
16
+ ingest(records) {
17
+ const added = [];
18
+ for (const record of records) {
19
+ if (this.newest !== undefined && record.id <= this.newest)
20
+ continue;
21
+ this.newest = record.id;
22
+ added.push(record);
23
+ for (const entry of toWire(record)) {
24
+ const parentId = this.wire.at(-1)?.id ?? null;
25
+ this.wire.push({ ...entry, parentId });
26
+ }
27
+ }
28
+ return added;
29
+ }
30
+ /** The current leaf id (the newest wire entry). */
31
+ get leafId() {
32
+ return this.wire.at(-1)?.id ?? null;
33
+ }
34
+ page(range) {
35
+ if (range?.since !== undefined) {
36
+ const at = this.wire.findIndex((e) => e.id === range.since);
37
+ if (at === -1)
38
+ throw new Error(`Entry not found: ${range.since}`);
39
+ return { entries: this.wire.slice(at + 1), leafId: this.leafId };
40
+ }
41
+ return { entries: [...this.wire], leafId: this.leafId };
42
+ }
43
+ }
44
+ const firstSeen = new Map();
45
+ function timestampOf(record) {
46
+ const ms = record.model?.[0]
47
+ ?.timestamp;
48
+ if (typeof ms === "number" && Number.isFinite(ms)) {
49
+ return new Date(ms).toISOString();
50
+ }
51
+ let seen = firstSeen.get(record.id);
52
+ if (seen === undefined) {
53
+ seen = new Date().toISOString();
54
+ firstSeen.set(record.id, seen);
55
+ }
56
+ return seen;
57
+ }
58
+ /** The text of a message's content: a string, or its text blocks joined. */
59
+ function textOf(message) {
60
+ const content = message?.content;
61
+ if (typeof content === "string")
62
+ return content;
63
+ if (!Array.isArray(content))
64
+ return "";
65
+ return content
66
+ .filter((block) => block?.type === "text" &&
67
+ typeof block.text === "string")
68
+ .map((block) => block.text)
69
+ .join("\n");
70
+ }
71
+ /** Map one durable entry to its pi wire entries (without parentId). */
72
+ export function toWire(record) {
73
+ const id = String(record.id);
74
+ const timestamp = timestampOf(record);
75
+ switch (record.kind) {
76
+ case "pi.compaction": {
77
+ let summary = textOf(record.model?.[0]);
78
+ if (summary.startsWith(SUMMARY_PREFIX) && summary.endsWith(SUMMARY_SUFFIX)) {
79
+ summary = summary.slice(SUMMARY_PREFIX.length, summary.length - SUMMARY_SUFFIX.length);
80
+ }
81
+ return [
82
+ {
83
+ type: "compaction",
84
+ id,
85
+ timestamp,
86
+ summary,
87
+ firstKeptEntryId: record.head !== undefined ? String(record.head) : id,
88
+ tokensBefore: 0,
89
+ },
90
+ ];
91
+ }
92
+ case "pi.reset": {
93
+ const handoff = textOf(record.model?.[0]);
94
+ return [
95
+ {
96
+ type: "custom_message",
97
+ id,
98
+ timestamp,
99
+ customType: RESET_CUSTOM_TYPE,
100
+ content: handoff
101
+ ? `Context reset. Handoff:\n\n${handoff}`
102
+ : "Context reset",
103
+ display: true,
104
+ },
105
+ ];
106
+ }
107
+ case UN_BIEN_APP_KIND: {
108
+ const data = (record.data ?? {});
109
+ return [
110
+ {
111
+ type: "custom_message",
112
+ id,
113
+ timestamp,
114
+ customType: typeof data.customType === "string" ? data.customType : "",
115
+ content: typeof data.content === "string" ? data.content : "",
116
+ display: data.display !== false,
117
+ ...(data.details !== undefined ? { details: data.details } : {}),
118
+ },
119
+ ];
120
+ }
121
+ default:
122
+ break;
123
+ }
124
+ const messages = (record.model ?? []).filter((m) => typeof m.role === "string" &&
125
+ m.role !== "system");
126
+ if (messages.length === 0) {
127
+ return [{ type: record.kind, id, timestamp, kind: record.kind }];
128
+ }
129
+ return messages.map((message, n) => ({
130
+ type: "message",
131
+ id: n === 0 ? id : `${id}.${n}`,
132
+ timestamp,
133
+ message,
134
+ }));
135
+ }
136
+ //# sourceMappingURL=mirror.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mirror.js","sourceRoot":"","sources":["../src/mirror.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** The Ollama Cloud provider (register via models.setProvider in host.ts). */
2
+ export declare function ollamaCloudProvider(): import("@earendil-works/pi-ai").Provider<"openai-completions">;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Ollama Cloud provider for the durable host (skeleton).
3
+ *
4
+ * WHY THIS EXISTS: pi-durable has no pi-extension interface, so the
5
+ * `pi-ollama-cloud` extension (which registers the provider in pi sessions
6
+ * via pi.registerProvider) can't run here. pi-ai ships no ollama provider.
7
+ * The host owns its models registry (createModels + setProvider in host.ts),
8
+ * so the provider is composed host-side from the same data the extension
9
+ * uses: OpenAI-compatible completions transport against https://ollama.com/v1,
10
+ * OLLAMA_API_KEY auth, and the model catalog (compat flags copied from
11
+ * pi-ollama-cloud's models.generated.ts — they are pi-ai Model compat data,
12
+ * verified against the glm-5.3 entry).
13
+ *
14
+ * SKELETON SCOPE (TODOs):
15
+ * - Static catalog: glm-5.3 + glm-5.3-flash only. Sync more entries from
16
+ * pi-ollama-cloud's models.generated.ts as needed.
17
+ * - No live catalog refresh (fetchModels / refreshOllamaCatalog overlay):
18
+ * wire the GET https://ollama.com/v1/models + /api/show flow later if the
19
+ * static list goes stale.
20
+ */
21
+ import { createProvider, envApiKeyAuth } from "@earendil-works/pi-ai";
22
+ import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
23
+ /** Compat flags shared by the glm-5.x family (pi-ollama-cloud models.generated.ts). */
24
+ const GLM_COMPAT = {
25
+ maxTokensField: "max_tokens",
26
+ requiresAssistantAfterToolResult: false,
27
+ requiresReasoningContentOnAssistantMessages: false,
28
+ requiresThinkingAsText: false,
29
+ requiresToolResultName: false,
30
+ sendSessionAffinityHeaders: false,
31
+ supportsDeveloperRole: false,
32
+ supportsLongCacheRetention: false,
33
+ supportsReasoningEffort: true,
34
+ supportsStore: false,
35
+ supportsStrictMode: false,
36
+ supportsUsageInStreaming: true,
37
+ thinkingFormat: "openai",
38
+ zaiToolStream: false,
39
+ };
40
+ // TODO: sync further entries from pi-ollama-cloud models.generated.ts
41
+ const OLLAMA_CLOUD_MODELS = [
42
+ {
43
+ id: "glm-5.3",
44
+ name: "glm-5.3",
45
+ api: "openai-completions",
46
+ provider: "ollama-cloud",
47
+ baseUrl: "https://ollama.com/v1",
48
+ compat: { ...GLM_COMPAT },
49
+ contextWindow: 1048576,
50
+ cost: { cacheRead: 0.26, cacheWrite: 0, input: 1.4, output: 4.4 },
51
+ input: ["text"],
52
+ maxTokens: 524288,
53
+ reasoning: true,
54
+ thinkingLevelMap: {
55
+ high: "high",
56
+ low: "low",
57
+ medium: null,
58
+ minimal: null,
59
+ off: "none",
60
+ xhigh: "max",
61
+ },
62
+ },
63
+ {
64
+ id: "glm-5.3-flash",
65
+ name: "glm-5.3-flash",
66
+ api: "openai-completions",
67
+ provider: "ollama-cloud",
68
+ baseUrl: "https://ollama.com/v1",
69
+ compat: { ...GLM_COMPAT },
70
+ // TODO: verify flash's window/cost/thinking map against models.generated.ts
71
+ contextWindow: 1048576,
72
+ cost: { cacheRead: 0.03, cacheWrite: 0, input: 0.15, output: 0.5 },
73
+ input: ["text"],
74
+ maxTokens: 524288,
75
+ reasoning: true,
76
+ thinkingLevelMap: {
77
+ high: "high",
78
+ low: "low",
79
+ medium: null,
80
+ minimal: null,
81
+ off: "none",
82
+ xhigh: "max",
83
+ },
84
+ },
85
+ ];
86
+ /** The Ollama Cloud provider (register via models.setProvider in host.ts). */
87
+ export function ollamaCloudProvider() {
88
+ return createProvider({
89
+ id: "ollama-cloud",
90
+ name: "Ollama Cloud",
91
+ baseUrl: "https://ollama.com/v1",
92
+ auth: { apiKey: envApiKeyAuth("Ollama Cloud API key", ["OLLAMA_API_KEY"]) },
93
+ models: OLLAMA_CLOUD_MODELS,
94
+ // TODO: fetchModels overlay (refreshOllamaCatalog) for the live catalog.
95
+ api: openAICompletionsApi(),
96
+ });
97
+ }
98
+ //# sourceMappingURL=ollama_cloud.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama_cloud.js","sourceRoot":"","sources":["../src/ollama_cloud.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,cAAc,EAAE,aAAa,EAAc,MAAM,uBAAuB,CAAA;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,mDAAmD,CAAA;AAExF,uFAAuF;AACvF,MAAM,UAAU,GAAG;IACjB,cAAc,EAAE,YAAY;IAC5B,gCAAgC,EAAE,KAAK;IACvC,2CAA2C,EAAE,KAAK;IAClD,sBAAsB,EAAE,KAAK;IAC7B,sBAAsB,EAAE,KAAK;IAC7B,0BAA0B,EAAE,KAAK;IACjC,qBAAqB,EAAE,KAAK;IAC5B,0BAA0B,EAAE,KAAK;IACjC,uBAAuB,EAAE,IAAI;IAC7B,aAAa,EAAE,KAAK;IACpB,kBAAkB,EAAE,KAAK;IACzB,wBAAwB,EAAE,IAAI;IAC9B,cAAc,EAAE,QAAQ;IACxB,aAAa,EAAE,KAAK;CACZ,CAAA;AAEV,sEAAsE;AACtE,MAAM,mBAAmB,GAA2C;IAClE;QACE,EAAE,EAAE,SAAS;QACb,IAAI,EAAE,SAAS;QACf,GAAG,EAAE,oBAAoB;QACzB,QAAQ,EAAE,cAAc;QACxB,OAAO,EAAE,uBAAuB;QAChC,MAAM,EAAE,EAAE,GAAG,UAAU,EAAE;QACzB,aAAa,EAAE,OAAO;QACtB,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE;QACjE,KAAK,EAAE,CAAC,MAAM,CAAC;QACf,SAAS,EAAE,MAAM;QACjB,SAAS,EAAE,IAAI;QACf,gBAAgB,EAAE;YAChB,IAAI,EAAE,MAAM;YACZ,GAAG,EAAE,KAAK;YACV,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,IAAI;YACb,GAAG,EAAE,MAAM;YACX,KAAK,EAAE,KAAK;SACb;KACF;IACD;QACE,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,eAAe;QACrB,GAAG,EAAE,oBAAoB;QACzB,QAAQ,EAAE,cAAc;QACxB,OAAO,EAAE,uBAAuB;QAChC,MAAM,EAAE,EAAE,GAAG,UAAU,EAAE;QACzB,4EAA4E;QAC5E,aAAa,EAAE,OAAO;QACtB,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;QAClE,KAAK,EAAE,CAAC,MAAM,CAAC;QACf,SAAS,EAAE,MAAM;QACjB,SAAS,EAAE,IAAI;QACf,gBAAgB,EAAE;YAChB,IAAI,EAAE,MAAM;YACZ,GAAG,EAAE,KAAK;YACV,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,IAAI;YACb,GAAG,EAAE,MAAM;YACX,KAAK,EAAE,KAAK;SACb;KACF;CACF,CAAA;AAED,8EAA8E;AAC9E,MAAM,UAAU,mBAAmB;IACjC,OAAO,cAAc,CAAC;QACpB,EAAE,EAAE,cAAc;QAClB,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,uBAAuB;QAChC,IAAI,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,sBAAsB,EAAE,CAAC,gBAAgB,CAAC,CAAC,EAAE;QAC3E,MAAM,EAAE,mBAAmB;QAC3B,yEAAyE;QACzE,GAAG,EAAE,oBAAoB,EAAE;KAC5B,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Durable binding's HeadlessLaunchPort implementation: the launch-wait-quit
3
+ * primitive behind the `launch_pi_task` tool — the ONE place the two
4
+ * bindings meet at runtime.
5
+ *
6
+ * Launch path: the pi SDK's headless `createAgentSession` (the programmatic
7
+ * equivalent of `pi` without a TUI — NOT `pi -p`, whose print-mode exit the
8
+ * un-bien extension deliberately never auto-starts the relay in). The session
9
+ * loads its extensions through pi's own resource discovery from the target
10
+ * cwd (installed pi-packages, `<cwd>/.pi/extensions`, `<agentDir>`), so the
11
+ * un-bien pi extension must be discoverable there — when it is, its
12
+ * session_start auto-init joins the UDS mesh under the session's display
13
+ * name, which is the SCOPED name minted per invocation.
14
+ *
15
+ * Readiness: the local UDS broker is probed (observer probe — never
16
+ * registers, never perturbs the roster) until the scoped name appears as a
17
+ * peer address; that address is the agent_send routing key for the task.
18
+ *
19
+ * Teardown: `session.dispose()` — the un-bien extension's session_shutdown
20
+ * handler leaves the mesh and closes the relay, exactly as it does for a
21
+ * quitting interactive pi.
22
+ */
23
+ import { type AgentSession } from "@earendil-works/pi-coding-agent";
24
+ import type { HeadlessLaunchPort, HeadlessLaunchedSession } from "@geohar/un-bien/core";
25
+ export declare class HeadlessPiLaunch implements HeadlessLaunchPort {
26
+ launchHeadless(opts: {
27
+ cwd: string;
28
+ name: string;
29
+ }): Promise<HeadlessLaunchedSession>;
30
+ }
31
+ export declare class HeadlessPiSession implements HeadlessLaunchedSession {
32
+ private readonly session;
33
+ readonly name: string;
34
+ constructor(session: AgentSession, name: string);
35
+ /** join-mesh-under-name: bind the scoped name, then bring the un-bien
36
+ * extension up. `bindExtensions` fires session_start; the extension's
37
+ * auto-init resolves the mesh name from the session display name (set
38
+ * FIRST so the join sees it) and connects the broker. */
39
+ joinMeshUnderName(): Promise<void>;
40
+ /** await-ready: poll the broker's observer probe until the scoped name is
41
+ * a registered peer. Resolves with the broker-assigned address (the name
42
+ * may carry a `#N` collision suffix the broker added). */
43
+ awaitReady(opts?: {
44
+ timeoutMs?: number;
45
+ }): Promise<{
46
+ address: string;
47
+ }>;
48
+ /** tear-down: quit gracefully (extension disposal leaves the mesh). */
49
+ tearDown(): Promise<void>;
50
+ }