electric-ax 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.
@@ -0,0 +1,380 @@
1
+ "use strict";
2
+ const require_chunk = require('./chunk-BCwAaXi7.cjs');
3
+ const require_entity_stream_db = require('./entity-stream-db-Djo-7a11.cjs');
4
+ const __durable_streams_state = require_chunk.__toESM(require("@durable-streams/state"));
5
+ const __electric_ax_agents_runtime = require_chunk.__toESM(require("@electric-ax/agents-runtime"));
6
+ const react = require_chunk.__toESM(require("react"));
7
+ const ink = require_chunk.__toESM(require("ink"));
8
+ const __tanstack_react_db = require_chunk.__toESM(require("@tanstack/react-db"));
9
+ const react_jsx_runtime = require_chunk.__toESM(require("react/jsx-runtime"));
10
+
11
+ //#region src/observe-ui.tsx
12
+ function formatTime(iso) {
13
+ if (!iso) return ``;
14
+ try {
15
+ const d = new Date(iso);
16
+ return d.toLocaleTimeString([], {
17
+ hour: `2-digit`,
18
+ minute: `2-digit`,
19
+ second: `2-digit`
20
+ });
21
+ } catch {
22
+ return ``;
23
+ }
24
+ }
25
+ function truncate(s, max) {
26
+ return s.length > max ? s.slice(0, max - 3) + `...` : s;
27
+ }
28
+ function UserMessageView({ msg }) {
29
+ const time = formatTime(msg.timestamp);
30
+ const payload = msg.payload;
31
+ let text = ``;
32
+ if (typeof payload === `string`) text = payload;
33
+ else if (typeof payload === `object` && payload !== null) {
34
+ const p = payload;
35
+ text = typeof p.text === `string` ? p.text : JSON.stringify(payload);
36
+ }
37
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, {
38
+ flexDirection: "column",
39
+ marginTop: 1,
40
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Text, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
41
+ bold: true,
42
+ color: "cyan",
43
+ children: `┌ ${msg.from}`
44
+ }), time ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
45
+ dimColor: true,
46
+ children: ` ${time}`
47
+ }) : null] }), text.split(`\n`).map((line, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
48
+ color: "white",
49
+ children: `│ ${line}`
50
+ }, i))]
51
+ });
52
+ }
53
+ function AgentTextView({ text, accumulatedText, label }) {
54
+ const lines = accumulatedText.split(`\n`);
55
+ const cursor = text.status !== `completed` ? ` ▌` : ``;
56
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, {
57
+ flexDirection: "column",
58
+ marginTop: 1,
59
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
60
+ bold: true,
61
+ color: "green",
62
+ children: `┌ ${label ?? `assistant`}`
63
+ }) }), lines.map((line, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
64
+ color: "white",
65
+ children: `│ ${line}${i === lines.length - 1 ? cursor : ``}`
66
+ }, i))]
67
+ });
68
+ }
69
+ function ToolCallView({ tc }) {
70
+ let statusIcon;
71
+ let statusColor;
72
+ if (tc.status === `started`) {
73
+ statusIcon = `○`;
74
+ statusColor = `yellow`;
75
+ } else if (tc.isError) {
76
+ statusIcon = `✗`;
77
+ statusColor = `red`;
78
+ } else if (tc.result !== void 0) {
79
+ statusIcon = `✓`;
80
+ statusColor = `green`;
81
+ } else {
82
+ statusIcon = `⟳`;
83
+ statusColor = `yellow`;
84
+ }
85
+ const resultStr = tc.result;
86
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, {
87
+ flexDirection: "column",
88
+ children: [
89
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Text, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
90
+ color: statusColor,
91
+ children: ` ${statusIcon} `
92
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
93
+ bold: true,
94
+ dimColor: true,
95
+ children: tc.toolName
96
+ })] }),
97
+ resultStr !== void 0 && !tc.isError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolResultView, { result: resultStr }) : null,
98
+ resultStr !== void 0 && tc.isError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
99
+ color: "red",
100
+ children: ` ↳ ${truncate(resultStr, 120)}`
101
+ }) : null
102
+ ]
103
+ });
104
+ }
105
+ function ToolResultView({ result }) {
106
+ const lines = result.split(`\n`);
107
+ const maxLines = 5;
108
+ const shown = lines.slice(0, maxLines);
109
+ const remaining = lines.length - maxLines;
110
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, {
111
+ flexDirection: "column",
112
+ children: [shown.map((line, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
113
+ dimColor: true,
114
+ children: ` │ ${truncate(line, 100)}`
115
+ }, i)), remaining > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
116
+ dimColor: true,
117
+ children: ` │ ... ${remaining} more lines`
118
+ }) : null]
119
+ });
120
+ }
121
+ function MessageInput({ db, baseUrl, entityUrl, identity, disabled }) {
122
+ const [value, setValue] = (0, react.useState)(``);
123
+ const [error, setError] = (0, react.useState)(null);
124
+ const sendAction = (0, react.useMemo)(() => (0, __durable_streams_state.createOptimisticAction)({
125
+ onMutate: ({ text }) => {
126
+ db.collections.inbox.insert({
127
+ key: `optimistic-${Date.now()}`,
128
+ from: identity,
129
+ payload: { text },
130
+ timestamp: new Date().toISOString()
131
+ });
132
+ },
133
+ mutationFn: async ({ text }) => {
134
+ const res = await fetch(`${baseUrl}${entityUrl}/send`, {
135
+ method: `POST`,
136
+ headers: { "content-type": `application/json` },
137
+ body: JSON.stringify({
138
+ from: identity,
139
+ payload: { text }
140
+ })
141
+ });
142
+ if (!res.ok) {
143
+ const body = await res.text().catch(() => ``);
144
+ let message = `Send failed (${res.status})`;
145
+ if (body) try {
146
+ const data = JSON.parse(body);
147
+ if (data.message) message = String(data.message);
148
+ else message = body;
149
+ } catch (err) {
150
+ if (err instanceof SyntaxError) message = body;
151
+ else throw err;
152
+ }
153
+ throw new Error(message);
154
+ }
155
+ }
156
+ }), [
157
+ db,
158
+ baseUrl,
159
+ entityUrl,
160
+ identity
161
+ ]);
162
+ (0, ink.useInput)((input, key) => {
163
+ if (disabled) return;
164
+ if (key.return) {
165
+ if (value.trim()) {
166
+ setError(null);
167
+ const tx = sendAction({ text: value.trim() });
168
+ setValue(``);
169
+ tx.isPersisted.promise.catch((err) => {
170
+ setError(err.message);
171
+ });
172
+ }
173
+ return;
174
+ }
175
+ if (key.backspace || key.delete) {
176
+ setValue((prev) => prev.slice(0, -1));
177
+ return;
178
+ }
179
+ if (input && !key.ctrl && !key.meta) setValue((prev) => prev + input);
180
+ }, { isActive: !disabled });
181
+ if (disabled) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {});
182
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, {
183
+ flexDirection: "column",
184
+ marginTop: 1,
185
+ children: [error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
186
+ color: "red",
187
+ children: ` ${error}`
188
+ }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, { children: [
189
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
190
+ color: "cyan",
191
+ bold: true,
192
+ children: `> `
193
+ }),
194
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, { children: value }),
195
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
196
+ dimColor: true,
197
+ children: `▌`
198
+ })
199
+ ] })]
200
+ });
201
+ }
202
+ function AgentResponseView({ section, label, isStreaming }) {
203
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, {
204
+ flexDirection: "column",
205
+ children: [
206
+ section.items.map((item, i) => {
207
+ if (item.kind === `text`) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentTextView, {
208
+ text: {
209
+ key: `${label}-text-${i}`,
210
+ status: isStreaming ? `streaming` : `completed`
211
+ },
212
+ accumulatedText: item.text,
213
+ label
214
+ }, `${label}-text-${i}`);
215
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCallView, { tc: item }, item.toolCallId);
216
+ }),
217
+ section.done ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, {
218
+ marginTop: 1,
219
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
220
+ color: "green",
221
+ children: `✓ complete`
222
+ })
223
+ }) : null,
224
+ section.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, {
225
+ marginTop: 1,
226
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
227
+ color: "red",
228
+ children: `✗ ${section.error}`
229
+ })
230
+ }) : null
231
+ ]
232
+ });
233
+ }
234
+ function ObserveView({ db, entityUrl, baseUrl, identity }) {
235
+ const timelineQuery = (0, react.useMemo)(() => (0, __electric_ax_agents_runtime.createEntityIncludesQuery)(db), [db]);
236
+ const { data: timelineRows = [] } = (0, __tanstack_react_db.useLiveQuery)(timelineQuery, [timelineQuery]);
237
+ const timelineData = (0, __electric_ax_agents_runtime.normalizeEntityTimelineData)(timelineRows[0] ?? {
238
+ runs: [],
239
+ inbox: [],
240
+ wakes: [],
241
+ entities: []
242
+ });
243
+ const typedRuns = timelineData.runs;
244
+ const typedInbox = timelineData.inbox;
245
+ const timeline = (0, react.useMemo)(() => (0, __electric_ax_agents_runtime.buildSections)(typedRuns, typedInbox), [typedRuns, typedInbox]);
246
+ const { data: stopped = [] } = (0, __tanstack_react_db.useLiveQuery)((q) => q.from({ entityStopped: db.collections.entityStopped }), [db]);
247
+ const closed = stopped.length > 0;
248
+ const lastAgentIndex = (0, react.useMemo)(() => {
249
+ for (let i = timeline.length - 1; i >= 0; i--) if (timeline[i].kind === `agent_response`) return i;
250
+ return -1;
251
+ }, [timeline]);
252
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, {
253
+ flexDirection: "column",
254
+ children: [
255
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, {
256
+ marginBottom: 1,
257
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
258
+ dimColor: true,
259
+ children: `Observing ${entityUrl}${closed ? `` : ` (Ctrl+C to stop)`}`
260
+ })
261
+ }),
262
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, {
263
+ borderStyle: "single",
264
+ borderColor: "gray",
265
+ flexDirection: "column",
266
+ paddingX: 1,
267
+ children: [
268
+ timeline.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
269
+ dimColor: true,
270
+ children: "Waiting for events..."
271
+ }) : null,
272
+ timeline.map((section, i) => {
273
+ if (section.kind === `user_message`) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UserMessageView, { msg: {
274
+ key: `timeline-${i}`,
275
+ from: section.from ?? `user`,
276
+ payload: { text: section.text },
277
+ timestamp: new Date(section.timestamp).toISOString()
278
+ } }, `msg-${i}`);
279
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AgentResponseView, {
280
+ section,
281
+ label: entityUrl,
282
+ isStreaming: !closed && i === lastAgentIndex && !section.done
283
+ }, `agent-${i}`);
284
+ }),
285
+ closed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, {
286
+ marginTop: 1,
287
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
288
+ color: "yellow",
289
+ children: `⚠ Entity stopped`
290
+ })
291
+ }) : null
292
+ ]
293
+ }),
294
+ closed ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ink.Box, {
295
+ marginTop: 1,
296
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
297
+ color: "yellow",
298
+ children: `Stream closed`
299
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Newline, {})]
300
+ }) : null,
301
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageInput, {
302
+ db,
303
+ baseUrl,
304
+ entityUrl,
305
+ identity,
306
+ disabled: closed
307
+ })
308
+ ]
309
+ });
310
+ }
311
+ function ObserveApp({ entityUrl, baseUrl, identity, initialOffset }) {
312
+ const [db, setDb] = (0, react.useState)(null);
313
+ const [error, setError] = (0, react.useState)(null);
314
+ const closeRef = (0, react.useRef)(null);
315
+ (0, react.useEffect)(() => {
316
+ let cancelled = false;
317
+ require_entity_stream_db.createEntityStreamDB({
318
+ baseUrl,
319
+ entityUrl,
320
+ initialOffset
321
+ }).then((result) => {
322
+ if (cancelled) {
323
+ result.close();
324
+ return;
325
+ }
326
+ closeRef.current = result.close;
327
+ setDb(result.db);
328
+ }).catch((err) => {
329
+ if (!cancelled) setError(err instanceof Error ? err.message : String(err));
330
+ });
331
+ return () => {
332
+ cancelled = true;
333
+ closeRef.current?.();
334
+ };
335
+ }, [
336
+ baseUrl,
337
+ entityUrl,
338
+ initialOffset
339
+ ]);
340
+ if (error) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, {
341
+ flexDirection: "column",
342
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
343
+ color: "red",
344
+ children: `Error: ${error}`
345
+ })
346
+ });
347
+ if (!db) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Box, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ink.Text, {
348
+ dimColor: true,
349
+ children: `Connecting to ${entityUrl}...`
350
+ }) });
351
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ObserveView, {
352
+ db,
353
+ entityUrl,
354
+ baseUrl,
355
+ identity
356
+ });
357
+ }
358
+ function renderObserve(opts) {
359
+ const { entityUrl, baseUrl, identity, initialOffset } = opts;
360
+ const app = (0, ink.render)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ObserveApp, {
361
+ entityUrl,
362
+ baseUrl,
363
+ identity,
364
+ initialOffset
365
+ }));
366
+ process.on(`SIGINT`, () => {
367
+ app.unmount();
368
+ process.exit(0);
369
+ });
370
+ }
371
+
372
+ //#endregion
373
+ exports.AgentTextView = AgentTextView
374
+ exports.MessageInput = MessageInput
375
+ exports.ToolCallView = ToolCallView
376
+ exports.ToolResultView = ToolResultView
377
+ exports.UserMessageView = UserMessageView
378
+ exports.formatTime = formatTime
379
+ exports.renderObserve = renderObserve
380
+ exports.truncate = truncate
@@ -0,0 +1,59 @@
1
+ import { EntityStreamDB } from "./entity-stream-db-C2C3t_hD.cjs";
2
+ import { EntityTimelineContentItem, MessageReceived } from "@electric-ax/agents-runtime";
3
+ import React from "react";
4
+
5
+ //#region src/observe-ui.d.ts
6
+ interface StreamingText {
7
+ key: string;
8
+ status: `streaming` | `completed`;
9
+ }
10
+ declare function formatTime(iso: string | undefined): string;
11
+ declare function truncate(s: string, max: number): string;
12
+ declare function UserMessageView({
13
+ msg
14
+ }: {
15
+ msg: MessageReceived;
16
+ }): React.ReactElement;
17
+ declare function AgentTextView({
18
+ text,
19
+ accumulatedText,
20
+ label
21
+ }: {
22
+ text: StreamingText;
23
+ accumulatedText: string;
24
+ label?: string;
25
+ }): React.ReactElement;
26
+ declare function ToolCallView({
27
+ tc
28
+ }: {
29
+ tc: Extract<EntityTimelineContentItem, {
30
+ kind: `tool_call`;
31
+ }>;
32
+ }): React.ReactElement;
33
+ declare function ToolResultView({
34
+ result
35
+ }: {
36
+ result: string;
37
+ }): React.ReactElement;
38
+ declare function MessageInput({
39
+ db,
40
+ baseUrl,
41
+ entityUrl,
42
+ identity,
43
+ disabled
44
+ }: {
45
+ db: EntityStreamDB;
46
+ baseUrl: string;
47
+ entityUrl: string;
48
+ identity: string;
49
+ disabled: boolean;
50
+ }): React.ReactElement;
51
+ declare function renderObserve(opts: {
52
+ entityUrl: string;
53
+ baseUrl: string;
54
+ identity: string;
55
+ initialOffset?: string;
56
+ }): void;
57
+
58
+ //#endregion
59
+ export { AgentTextView, MessageInput, ToolCallView, ToolResultView, UserMessageView, formatTime, renderObserve, truncate };
@@ -0,0 +1,59 @@
1
+ import { EntityStreamDB } from "./entity-stream-db-BzuIvhSy.js";
2
+ import { EntityTimelineContentItem, MessageReceived } from "@electric-ax/agents-runtime";
3
+ import React from "react";
4
+
5
+ //#region src/observe-ui.d.ts
6
+ interface StreamingText {
7
+ key: string;
8
+ status: `streaming` | `completed`;
9
+ }
10
+ declare function formatTime(iso: string | undefined): string;
11
+ declare function truncate(s: string, max: number): string;
12
+ declare function UserMessageView({
13
+ msg
14
+ }: {
15
+ msg: MessageReceived;
16
+ }): React.ReactElement;
17
+ declare function AgentTextView({
18
+ text,
19
+ accumulatedText,
20
+ label
21
+ }: {
22
+ text: StreamingText;
23
+ accumulatedText: string;
24
+ label?: string;
25
+ }): React.ReactElement;
26
+ declare function ToolCallView({
27
+ tc
28
+ }: {
29
+ tc: Extract<EntityTimelineContentItem, {
30
+ kind: `tool_call`;
31
+ }>;
32
+ }): React.ReactElement;
33
+ declare function ToolResultView({
34
+ result
35
+ }: {
36
+ result: string;
37
+ }): React.ReactElement;
38
+ declare function MessageInput({
39
+ db,
40
+ baseUrl,
41
+ entityUrl,
42
+ identity,
43
+ disabled
44
+ }: {
45
+ db: EntityStreamDB;
46
+ baseUrl: string;
47
+ entityUrl: string;
48
+ identity: string;
49
+ disabled: boolean;
50
+ }): React.ReactElement;
51
+ declare function renderObserve(opts: {
52
+ entityUrl: string;
53
+ baseUrl: string;
54
+ identity: string;
55
+ initialOffset?: string;
56
+ }): void;
57
+
58
+ //#endregion
59
+ export { AgentTextView, MessageInput, ToolCallView, ToolResultView, UserMessageView, formatTime, renderObserve, truncate };