@nyte-ai/sdk 0.0.0-stage → 0.0.18

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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/THIRD-PARTY-NOTICES.md +800 -0
  3. package/dist/chunks/capability-2q3s96kg.js +57 -0
  4. package/dist/chunks/capability-2wk9ktc5.js +161 -0
  5. package/dist/chunks/capability-3qe1hz8t.js +78 -0
  6. package/dist/chunks/capability-591c8vr1.js +134 -0
  7. package/dist/chunks/capability-8hmez9nw.js +199 -0
  8. package/dist/chunks/capability-9747w1b1.js +55 -0
  9. package/dist/chunks/capability-artgq59v.js +232 -0
  10. package/dist/chunks/capability-b52hty54.js +17 -0
  11. package/dist/chunks/capability-cdz2rz6h.js +208 -0
  12. package/dist/chunks/capability-esdwn315.js +26 -0
  13. package/dist/chunks/capability-mr5m2wn2.js +6 -0
  14. package/dist/chunks/capability-nh422cp0.js +35 -0
  15. package/dist/chunks/capability-y1amted3.js +230 -0
  16. package/dist/src/capability.d.ts +2 -0
  17. package/dist/src/capability.js +6 -0
  18. package/dist/src/client.d.ts +14 -0
  19. package/dist/src/client.js +14 -0
  20. package/dist/src/connect.d.ts +2 -0
  21. package/dist/src/connect.js +15 -0
  22. package/dist/src/history.d.ts +14 -0
  23. package/dist/src/history.js +7 -0
  24. package/dist/src/index.client.d.ts +10 -0
  25. package/dist/src/index.client.js +57 -0
  26. package/dist/src/index.d.ts +9 -0
  27. package/dist/src/index.js +58 -0
  28. package/dist/src/index.web.d.ts +8 -0
  29. package/dist/src/index.web.js +63 -0
  30. package/dist/src/journal.d.ts +15 -0
  31. package/dist/src/journal.js +9 -0
  32. package/dist/src/login.d.ts +8 -0
  33. package/dist/src/login.js +6 -0
  34. package/dist/src/paint-spans.d.ts +11 -0
  35. package/dist/src/paint-spans.js +6 -0
  36. package/dist/src/part.d.ts +4 -0
  37. package/dist/src/part.js +6 -0
  38. package/dist/src/receipts.d.ts +9 -0
  39. package/dist/src/receipts.js +16 -0
  40. package/dist/src/rows.d.ts +37 -0
  41. package/dist/src/rows.js +12 -0
  42. package/dist/src/surface.d.ts +12 -0
  43. package/dist/src/surface.js +39 -0
  44. package/dist/src/watch.d.ts +30 -0
  45. package/dist/src/watch.js +14 -0
  46. package/dist/src/workspace.d.ts +4 -0
  47. package/dist/src/workspace.js +9 -0
  48. package/package.json +59 -3
  49. package/README.md +0 -3
@@ -0,0 +1,57 @@
1
+ // src/receipts.ts
2
+ import { BatchOutcomeSchema, MoveOutcomeSchema, RunEndSchema, SdkError } from "@nyte-ai/schema";
3
+ function receiptHandle2(rows, session, id, outcome) {
4
+ const input = { session, receipt: id };
5
+ return {
6
+ id,
7
+ async status() {
8
+ const status = await rows.call("receipt.status", input);
9
+ return status.kind === "settled" ? { kind: "settled", outcome: outcome(status.outcome) } : status;
10
+ },
11
+ async settled(options) {
12
+ return outcome(await rows.wait("receipt.settled", input, options));
13
+ },
14
+ async stop() {
15
+ const stopped = await rows.call("receipt.stop", input);
16
+ return stopped.kind === "settled" ? { kind: "settled", outcome: outcome(stopped.outcome) } : stopped;
17
+ }
18
+ };
19
+ }
20
+ function sendReceipt2(rows, session, id, message) {
21
+ return {
22
+ ...receiptHandle2(rows, session, id, sendOutcome),
23
+ message,
24
+ landed: () => rows.wait("receipt.landed", { session, receipt: id })
25
+ };
26
+ }
27
+ function runHandle2(rows, session, id) {
28
+ return {
29
+ id,
30
+ stop: () => rows.call("run.stop", { session, run: id }),
31
+ settled: (options) => rows.wait("run.settled", { session, run: id }, options)
32
+ };
33
+ }
34
+ function sendOutcome(value) {
35
+ if (typeof value === "object" && value !== null && "kind" in value && value.kind === "withdrawn")
36
+ return { kind: "withdrawn" };
37
+ const end = parsed2(RunEndSchema, value);
38
+ if (end !== null)
39
+ return end;
40
+ const moved = parsed2(MoveOutcomeSchema, value);
41
+ return moved !== null && moved.kind !== "moved" ? moved : unreadable("send");
42
+ }
43
+ function moveOutcome2(value) {
44
+ return parsed2(MoveOutcomeSchema, value) ?? unreadable("move");
45
+ }
46
+ function batchOutcome2(value) {
47
+ return parsed2(BatchOutcomeSchema, value) ?? unreadable("batch");
48
+ }
49
+ function parsed2(schema, value) {
50
+ const result = schema["~standard"].validate(value);
51
+ return result instanceof Promise || result.issues !== undefined ? null : result.value;
52
+ }
53
+ function unreadable(what) {
54
+ throw new SdkError("invalid", `The host answered a ${what} outcome the sdk cannot read.`);
55
+ }
56
+
57
+ export { receiptHandle2, sendReceipt2, runHandle2, moveOutcome2, batchOutcome2, parsed2 };
@@ -0,0 +1,161 @@
1
+ import {
2
+ retryDelay2,
3
+ backoff2
4
+ } from "./capability-artgq59v.js";
5
+
6
+ // src/journal.ts
7
+ import { asId, SdkError } from "@nyte-ai/schema";
8
+ import { dev } from "@nyte-ai/core";
9
+ function createSendJournal2() {
10
+ const entries = new Map;
11
+ return {
12
+ load: async () => [...entries.values()],
13
+ async put(entry) {
14
+ entries.set(entry.id, entry);
15
+ },
16
+ async remove(id) {
17
+ entries.delete(id);
18
+ }
19
+ };
20
+ }
21
+ function journalLoop2(rows, journal) {
22
+ const held = new Map;
23
+ const tails = new Map;
24
+ const shown = new Map;
25
+ const listeners = new Set;
26
+ const changed = () => {
27
+ for (const listener of listeners)
28
+ listener();
29
+ };
30
+ const post = async (current) => {
31
+ const { id, session, head, content, options } = current.entry;
32
+ for (;; ) {
33
+ try {
34
+ return await rows.call("head.send", {
35
+ session,
36
+ head,
37
+ content,
38
+ options: { ...options, id }
39
+ });
40
+ } catch (error) {
41
+ const closing = rows.link.signal.aborted;
42
+ if (closing || !(error instanceof SdkError && (error.code === "unavailable" || error.code === "stale"))) {
43
+ if (!closing)
44
+ await journal.remove(id);
45
+ held.delete(id);
46
+ changed();
47
+ throw error;
48
+ }
49
+ current.attempts += 1;
50
+ const delay = retryDelay2(current.attempts);
51
+ dev("sdk", "send retrying", { receipt: id, attempts: current.attempts, delay });
52
+ current.retrying = { at: Date.now() + delay, reason: error.message };
53
+ changed();
54
+ await backoff2(rows.link, delay, rows.link.signal);
55
+ }
56
+ }
57
+ };
58
+ const retarget = async (current, message) => {
59
+ const target = current.entry.options.replace ?? current.entry.id;
60
+ if (message === target)
61
+ return;
62
+ const all = [...held.values()];
63
+ const later = all.slice(all.findIndex((row) => row === current) + 1).filter(({ entry }) => entry.session === current.entry.session && entry.head === current.entry.head && entry.options.replace === target);
64
+ for (const row of later)
65
+ row.entry = { ...row.entry, options: { ...row.entry.options, replace: message } };
66
+ await Promise.all(later.map((row) => journal.put(row.entry)));
67
+ };
68
+ const hold = (entry) => {
69
+ const current = {
70
+ entry,
71
+ attempts: 0,
72
+ retrying: null,
73
+ answered: null,
74
+ answeredAfter: null
75
+ };
76
+ const key = JSON.stringify([entry.session, entry.head]);
77
+ const sent = (tails.get(key) ?? Promise.resolve()).then(async () => {
78
+ const answer = await post(current);
79
+ current.answered = answer.message;
80
+ current.answeredAfter = shown.get(key) ?? null;
81
+ await retarget(current, answer.message);
82
+ dev("sdk", "send answered", { receipt: entry.id, attempts: current.attempts + 1 });
83
+ changed();
84
+ return answer;
85
+ });
86
+ const tail = sent.then(() => {
87
+ return;
88
+ }, () => {
89
+ return;
90
+ });
91
+ tails.set(key, tail);
92
+ tail.then(() => {
93
+ if (tails.get(key) === tail)
94
+ tails.delete(key);
95
+ });
96
+ held.set(entry.id, current);
97
+ changed();
98
+ return sent;
99
+ };
100
+ return {
101
+ async send(entry) {
102
+ await journal.put(entry);
103
+ return hold(entry);
104
+ },
105
+ replay(entries) {
106
+ for (const entry of entries)
107
+ hold(entry).catch((error) => dev("sdk", "replay failed", {
108
+ receipt: entry.id,
109
+ code: error instanceof SdkError ? error.code : "internal"
110
+ }));
111
+ },
112
+ unsent(session, head) {
113
+ const shown = new Map;
114
+ const slots = new Map;
115
+ for (const current of held.values()) {
116
+ if (current.entry.session !== session || current.entry.head !== head)
117
+ continue;
118
+ const target = current.entry.options.replace ?? asId(current.entry.id);
119
+ const slot = slots.get(target) ?? target;
120
+ shown.set(slot, current);
121
+ if (current.answered !== null && current.answered !== target)
122
+ slots.set(current.answered, slot);
123
+ }
124
+ return [...shown].map(([id, current]) => unsentMessage(id, current));
125
+ },
126
+ landed(session, head, view) {
127
+ const key = JSON.stringify([session, head]);
128
+ const ids = new Set(view.messages.map((message) => message.id));
129
+ shown.set(key, Math.max(shown.get(key) ?? 0, view.seq));
130
+ for (const [receipt, current] of held) {
131
+ const { answered, answeredAfter, entry } = current;
132
+ if (answered === null || entry.session !== session || entry.head !== head || !ids.has(answered) && answeredAfter !== null && view.seq <= answeredAfter)
133
+ continue;
134
+ held.delete(receipt);
135
+ journal.remove(current.entry.id).catch(() => dev("sdk", "journal remove failed", { receipt }));
136
+ dev("sdk", "send landed", { receipt });
137
+ }
138
+ },
139
+ subscribe(listener) {
140
+ listeners.add(listener);
141
+ return () => {
142
+ listeners.delete(listener);
143
+ };
144
+ }
145
+ };
146
+ }
147
+ function unsentMessage(id, { entry, attempts, retrying }) {
148
+ return {
149
+ id,
150
+ turn: asId(`pending:${id}`),
151
+ at: entry.at,
152
+ kind: "user",
153
+ content: entry.content,
154
+ source: entry.options.source ?? { kind: "user" },
155
+ delivery: entry.options.delivery ?? "steer",
156
+ status: { kind: "unsent", attempts, retrying },
157
+ siblings: { index: 0, count: 1, ids: [] }
158
+ };
159
+ }
160
+
161
+ export { createSendJournal2, journalLoop2 };
@@ -0,0 +1,78 @@
1
+ import {
2
+ pushed2
3
+ } from "./capability-8hmez9nw.js";
4
+
5
+ // src/history.ts
6
+ function pagedViews2(head, options) {
7
+ let pages = [];
8
+ let turns = [];
9
+ let latest = null;
10
+ let shown = null;
11
+ let push = null;
12
+ let loading = null;
13
+ const show = (view) => {
14
+ const first = view.messages[0];
15
+ const before = shown?.messages ?? [];
16
+ const kept = first === undefined ? -1 : before.findIndex((message) => message.id === first.id);
17
+ pages = kept < 0 ? [] : before.slice(0, kept);
18
+ const paged = new Set(pages.map((message) => message.turn));
19
+ turns = (shown?.turns ?? []).filter((turn) => paged.has(turn.id));
20
+ latest = view;
21
+ shown = pages.length === 0 ? view : withPages(view, pages, turns);
22
+ push?.(shown);
23
+ };
24
+ const load = async (limit) => {
25
+ const oldest = shown?.messages[0];
26
+ if (oldest === undefined)
27
+ return 0;
28
+ const page = await head.history({ before: oldest.id, limit });
29
+ const current = latest;
30
+ if (page.messages.length === 0 || current === null || shown?.messages[0]?.id !== oldest.id)
31
+ return 0;
32
+ const known = new Set(turns.map((turn) => turn.id));
33
+ pages = [...page.messages, ...pages];
34
+ turns = [...page.turns.filter((turn) => !known.has(turn.id)), ...turns];
35
+ shown = withPages(current, pages, turns);
36
+ push?.(shown);
37
+ return page.messages.length;
38
+ };
39
+ return {
40
+ [Symbol.asyncIterator]: () => pushed2((sink) => {
41
+ const views = head.watch(options)[Symbol.asyncIterator]();
42
+ push = (view) => sink.next(view);
43
+ (async () => {
44
+ try {
45
+ for (;; ) {
46
+ const next = await views.next();
47
+ if (next.done === true)
48
+ break;
49
+ show(next.value);
50
+ }
51
+ sink.end();
52
+ } catch (error) {
53
+ sink.fail(error instanceof Error ? error : new Error(String(error)));
54
+ }
55
+ })();
56
+ return () => {
57
+ push = null;
58
+ views.return?.();
59
+ };
60
+ })[Symbol.asyncIterator](),
61
+ earlier(limit) {
62
+ loading ??= load(limit).finally(() => {
63
+ loading = null;
64
+ });
65
+ return loading;
66
+ }
67
+ };
68
+ }
69
+ function withPages(view, pages, turns) {
70
+ const own = new Set(view.turns.map((turn) => turn.id));
71
+ return {
72
+ ...view,
73
+ messages: [...pages, ...view.messages],
74
+ turns: [...turns.filter((turn) => !own.has(turn.id)), ...view.turns]
75
+ };
76
+ }
77
+
78
+ export { pagedViews2 };
@@ -0,0 +1,134 @@
1
+ import {
2
+ createSendJournal2
3
+ } from "./capability-2wk9ktc5.js";
4
+ import {
5
+ parsed2
6
+ } from "./capability-2q3s96kg.js";
7
+ import {
8
+ createClient2
9
+ } from "./capability-cdz2rz6h.js";
10
+
11
+ // src/connect.ts
12
+ import { IdentityChallengeSchema, isJson, SdkError } from "@nyte-ai/schema";
13
+ import { dev } from "@nyte-ai/core";
14
+ import { createTransport, decodeError, decodeOutput } from "@nyte-ai/protocol";
15
+ var paired = new Map;
16
+ var encoder = new TextEncoder;
17
+ async function connect2(options) {
18
+ const { credentials } = options;
19
+ const send = options.fetch ?? fetch;
20
+ const key = credentials.kind === "pairing" ? `${options.url}
21
+ ${credentials.code}` : null;
22
+ const kept = key === null ? undefined : paired.get(key);
23
+ const transport = createTransport({
24
+ url: options.url,
25
+ fetch: send,
26
+ headers: () => readCredentialHeaders(credentials, transport.credential() ?? kept ?? null)
27
+ });
28
+ if (options.expect !== undefined)
29
+ await verifyIdentity(options.url, send, options.expect.hostId);
30
+ const { view: first, frames } = await firstView(transport);
31
+ try {
32
+ return await openClient(options, transport, first, frames, key, kept);
33
+ } catch (error) {
34
+ await frames.return?.();
35
+ throw error;
36
+ }
37
+ }
38
+ async function openClient(options, transport, first, frames, key, kept) {
39
+ const { credentials } = options;
40
+ const answered = transport.host() ?? first.id;
41
+ if (options.expect !== undefined && (answered !== options.expect.hostId || first.id !== answered))
42
+ throw new SdkError("stale", `Host ${answered} answered instead of ${options.expect.hostId}.`, {
43
+ hostId: answered
44
+ });
45
+ const token = credentials.kind === "pairing" ? deviceToken(transport, kept) : null;
46
+ if (key !== null && token !== null)
47
+ paired.set(key, token);
48
+ const credential = token === null ? credentials : { kind: "token", token };
49
+ const journal = options.journal ?? createSendJournal2();
50
+ const journaled = await journal.load();
51
+ dev("sdk", "connected", {
52
+ hostId: first.id,
53
+ credential: credentials.kind,
54
+ journaled: journaled.length
55
+ });
56
+ return createClient2(transport, {
57
+ hostId: first.id,
58
+ principal: first.principal,
59
+ credential,
60
+ journal,
61
+ journaled,
62
+ hostFrames: frames
63
+ });
64
+ }
65
+ async function readCredentialHeaders(credentials, device) {
66
+ if (credentials.kind === "custom")
67
+ return credentials.headers();
68
+ if (credentials.kind === "token")
69
+ return { Authorization: `Bearer ${credentials.token}` };
70
+ return device === null ? { Authorization: `Pairing ${credentials.code}` } : { Authorization: `Bearer ${device}` };
71
+ }
72
+ function deviceToken(transport, kept) {
73
+ const token = transport.credential() ?? kept;
74
+ if (token === undefined)
75
+ throw new SdkError("unauthenticated", "The host returned no device credential for the pairing code.");
76
+ return token;
77
+ }
78
+ async function firstView(transport) {
79
+ const frames = transport.frames("connection.watch", {})[Symbol.asyncIterator]();
80
+ try {
81
+ const next = await frames.next();
82
+ if (next.done === true)
83
+ throw new SdkError("unavailable", "The host closed the connection before it answered.");
84
+ return {
85
+ view: decodeOutput("connection.watch", next.value),
86
+ frames: replayFirst(next.value, frames)
87
+ };
88
+ } catch (error) {
89
+ await frames.return?.();
90
+ throw error;
91
+ }
92
+ }
93
+ function replayFirst(first, rest) {
94
+ let replayed = false;
95
+ return {
96
+ async next() {
97
+ if (replayed)
98
+ return rest.next();
99
+ replayed = true;
100
+ return { done: false, value: first };
101
+ },
102
+ async return() {
103
+ await rest.return?.();
104
+ return { done: true, value: undefined };
105
+ }
106
+ };
107
+ }
108
+ async function verifyIdentity(url, send, expected) {
109
+ const nonce = crypto.randomUUID();
110
+ let response;
111
+ try {
112
+ response = await send(`${url.replace(/\/+$/, "")}/.nyte/identity?nonce=${encodeURIComponent(nonce)}`);
113
+ } catch (error) {
114
+ if (!(error instanceof TypeError))
115
+ throw error;
116
+ throw new SdkError("unavailable", error.message);
117
+ }
118
+ const body = await response.json().catch(() => null);
119
+ if (response.status === 404)
120
+ return;
121
+ if (!response.ok)
122
+ throw decodeError(isJson(body) ? body : null, response.status);
123
+ const challenge = parsed2(IdentityChallengeSchema, body);
124
+ if (challenge === null || challenge.nonce !== nonce)
125
+ throw new SdkError("invalid", "The host answered the identity challenge with something else.");
126
+ const key = await crypto.subtle.importKey("raw", Uint8Array.fromBase64(challenge.publicKey, { alphabet: "base64url" }), "Ed25519", false, ["verify"]);
127
+ const signed = await crypto.subtle.verify("Ed25519", key, Uint8Array.fromBase64(challenge.signature, { alphabet: "base64url" }), encoder.encode(`nyte/identity
128
+ ${challenge.hostId}
129
+ ${nonce}`));
130
+ if (!signed || challenge.hostId !== expected)
131
+ throw new SdkError("stale", signed ? `Host ${challenge.hostId} answered instead of ${expected}.` : `The identity signature of ${challenge.hostId} does not verify.`, { hostId: challenge.hostId });
132
+ }
133
+
134
+ export { connect2 };
@@ -0,0 +1,199 @@
1
+ // src/watch.ts
2
+ import { closable, coalesce } from "@nyte-ai/core";
3
+ import { applyViewFrame } from "@nyte-ai/protocol";
4
+ var DONE = { done: true, value: undefined };
5
+ function createHeadViews2(rows) {
6
+ const open = new Map;
7
+ const start = (key, session, head, options) => {
8
+ const sinks = new Set;
9
+ let view = null;
10
+ const frames = rows.watch("head.watch", { session, head, options })[Symbol.asyncIterator]();
11
+ const finish = (end) => {
12
+ if (open.get(key) === hold)
13
+ open.delete(key);
14
+ for (const sink of sinks)
15
+ end(sink);
16
+ };
17
+ (async () => {
18
+ try {
19
+ for (;; ) {
20
+ const next = await frames.next();
21
+ if (next.done === true)
22
+ break;
23
+ const current = applyViewFrame(view, next.value);
24
+ view = current;
25
+ for (const sink of sinks)
26
+ sink.next(current);
27
+ }
28
+ finish((sink) => sink.end());
29
+ } catch (error) {
30
+ finish((sink) => sink.fail(error instanceof Error ? error : new Error(String(error))));
31
+ }
32
+ })();
33
+ const hold = {
34
+ view: () => view,
35
+ subscribe(sink) {
36
+ sinks.add(sink);
37
+ return () => {
38
+ sinks.delete(sink);
39
+ if (sinks.size > 0)
40
+ return;
41
+ if (open.get(key) === hold)
42
+ open.delete(key);
43
+ frames.return?.();
44
+ };
45
+ }
46
+ };
47
+ return hold;
48
+ };
49
+ return {
50
+ acquire(session, head, options) {
51
+ const key = JSON.stringify([session, head, options?.tail ?? null]);
52
+ const known = open.get(key);
53
+ if (known !== undefined)
54
+ return known;
55
+ const created = start(key, session, head, options);
56
+ open.set(key, created);
57
+ return created;
58
+ }
59
+ };
60
+ }
61
+ function watchHead2(views, journal, input) {
62
+ return coalesce(pushed2((sink) => {
63
+ const { session, head, options } = input;
64
+ const hold = views.acquire(session, head, options);
65
+ let shown = null;
66
+ const emit = () => {
67
+ const view = hold.view();
68
+ if (view === null)
69
+ return;
70
+ journal.landed(session, head, view);
71
+ const ids = new Set(view.messages.map((message) => message.id));
72
+ const next = withUnsent2(view, journal.unsent(session, head), (id) => ids.has(id));
73
+ if (next === shown)
74
+ return;
75
+ shown = next;
76
+ sink.next(next);
77
+ };
78
+ const leave = hold.subscribe({
79
+ next: emit,
80
+ end: () => sink.end(),
81
+ fail: (error) => sink.fail(error)
82
+ });
83
+ const unsubscribe = journal.subscribe(emit);
84
+ emit();
85
+ return () => {
86
+ unsubscribe();
87
+ leave();
88
+ };
89
+ }));
90
+ }
91
+ function watchHost2(rows, opened) {
92
+ return coalesce(pushed2((sink) => {
93
+ let view = null;
94
+ const frames = rows.watch("connection.watch", {}, opened)[Symbol.asyncIterator]();
95
+ (async () => {
96
+ try {
97
+ for (;; ) {
98
+ const next = await frames.next();
99
+ if (next.done === true)
100
+ break;
101
+ view = hostView(next.value, rows.link.current(), rows.operations());
102
+ sink.next(view);
103
+ }
104
+ sink.end();
105
+ } catch (error) {
106
+ sink.fail(error instanceof Error ? error : new Error(String(error)));
107
+ }
108
+ })();
109
+ const unsubscribe = rows.link.subscribe((link) => {
110
+ if (view !== null) {
111
+ view = { ...view, link };
112
+ sink.next(view);
113
+ }
114
+ if (link.kind === "closed")
115
+ sink.end();
116
+ });
117
+ return () => {
118
+ unsubscribe();
119
+ frames.return?.();
120
+ };
121
+ }));
122
+ }
123
+ function withUnsent2(view, unsent, known) {
124
+ const over = new Map(unsent.map((message) => [message.id, message]));
125
+ const missing = unsent.filter((message) => !known(message.id));
126
+ if (missing.length === 0 && !view.messages.some((message) => over.has(message.id)))
127
+ return view;
128
+ const messages = view.messages.map((message) => over.get(message.id) ?? message);
129
+ const at = messages.findIndex((message) => message.kind === "user" && message.status.kind === "pending");
130
+ const index = at < 0 ? messages.length : at;
131
+ return {
132
+ ...view,
133
+ messages: [...messages.slice(0, index), ...missing, ...messages.slice(index)]
134
+ };
135
+ }
136
+ function hostView(wire, link, operations) {
137
+ return {
138
+ id: wire.id,
139
+ link,
140
+ operations,
141
+ name: wire.name,
142
+ platform: wire.platform,
143
+ version: wire.version,
144
+ catalog: wire.catalog,
145
+ plugins: wire.plugins,
146
+ settings: wire.settings,
147
+ defaultWorkspace: wire.defaultWorkspace,
148
+ authority: new Set(wire.authority)
149
+ };
150
+ }
151
+ function pushed2(start) {
152
+ return closable(() => {
153
+ let slot = null;
154
+ let ending = null;
155
+ let wake = null;
156
+ const signal = () => {
157
+ const resolve = wake;
158
+ wake = null;
159
+ resolve?.();
160
+ };
161
+ const stop = start({
162
+ next(value) {
163
+ if (ending !== null)
164
+ return;
165
+ slot = { value };
166
+ signal();
167
+ },
168
+ end() {
169
+ ending ??= "ended";
170
+ signal();
171
+ },
172
+ fail(error) {
173
+ ending ??= { error };
174
+ signal();
175
+ }
176
+ });
177
+ return {
178
+ async pull() {
179
+ for (;; ) {
180
+ const held = slot;
181
+ if (held !== null) {
182
+ slot = null;
183
+ return { done: false, value: held.value };
184
+ }
185
+ if (ending === "ended")
186
+ return DONE;
187
+ if (ending !== null)
188
+ throw ending.error;
189
+ await new Promise((resolve) => {
190
+ wake = resolve;
191
+ });
192
+ }
193
+ },
194
+ close: stop
195
+ };
196
+ });
197
+ }
198
+
199
+ export { createHeadViews2, watchHead2, watchHost2, withUnsent2, pushed2 };
@@ -0,0 +1,55 @@
1
+ // src/part.ts
2
+ import { asId, isJson, SdkError } from "@nyte-ai/schema";
3
+ import { callable, fitsActionOutput, fitsAnswer, fitsState } from "@nyte-ai/core";
4
+ function partHandle2(rows, session, id, kind) {
5
+ return {
6
+ id,
7
+ async update(next, options) {
8
+ const given = callable(next) ? await derived(rows, session, id, kind, next) : { value: next };
9
+ if (given === null)
10
+ return { kind: "missing" };
11
+ if (!isJson(given.value))
12
+ return { kind: "invalid", issues: [issue("The state is not JSON.")] };
13
+ return rows.call("part.update", { session, part: id, next: given.value, options });
14
+ },
15
+ async act(action, input) {
16
+ const named = String(action);
17
+ if (!isJson(input))
18
+ throw new SdkError("invalid", `The input of ${named} is not JSON.`);
19
+ const outcome = await rows.call("part.act", { session, part: id, action: named, input });
20
+ if (outcome.kind !== "ok")
21
+ return outcome;
22
+ const absent = outcome.output === null ? undefined : outcome.output;
23
+ if (fitsActionOutput(kind, action, absent))
24
+ return { kind: "ok", output: absent };
25
+ if (fitsActionOutput(kind, action, outcome.output))
26
+ return { kind: "ok", output: outcome.output };
27
+ return { kind: "failed", message: `This handle cannot carry ${named}'s output.` };
28
+ },
29
+ async answer(options) {
30
+ try {
31
+ const answer = await rows.wait("part.answer", { session, part: id }, options);
32
+ return answer !== null && fitsAnswer(kind, answer) ? answer : null;
33
+ } catch (error) {
34
+ if (options?.signal?.aborted === true)
35
+ return null;
36
+ throw error;
37
+ }
38
+ },
39
+ async close() {
40
+ await rows.call("part.close", { session, part: id });
41
+ }
42
+ };
43
+ }
44
+ async function derived(rows, session, id, kind, next) {
45
+ const view = await rows.call("head.view", { session, head: asId("main") });
46
+ const current = view.parts.find((part) => part.id === id) ?? view.messages.find((message) => message.kind === "part" && message.part === id);
47
+ if (current === undefined)
48
+ return null;
49
+ return fitsState(kind, current.state) ? { value: next(current.state) } : null;
50
+ }
51
+ function issue(message) {
52
+ return { severity: "error", path: [], message };
53
+ }
54
+
55
+ export { partHandle2 };