ghostykit 0.4.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/dist/chunks/{chunk-JQEB37IY.js → chunk-ULNSZHZV.js} +194 -15
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +7 -1
- package/dist/core/jsonPatch.d.ts +11 -0
- package/dist/core/reducer.d.ts +11 -1
- package/dist/core/types.d.ts +32 -0
- package/dist/react/Piloto.d.ts +4 -0
- package/dist/react/Status.d.ts +3 -1
- package/dist/react/index.js +90 -16
- package/dist/react/strings.d.ts +4 -0
- package/dist/react/usePilot.d.ts +21 -2
- package/dist/styles.css +9 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -109,10 +109,13 @@ ref.current?.send("Why did this fail?"); // ask on the user'
|
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
Other options: `strings`, `toolIcons`, `formatUserText`, `markdown`, `features`
|
|
112
|
-
(`attachments`, `retry`, `copy`, `usage`, `replyOptions`, `duration`), `references` /
|
|
113
|
-
`onReferencesChange`, `voiceNoteText`, `placeholder`, `avatar`, `initialTurns`,
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
(`attachments`, `retry`, `copy`, `usage`, `replyOptions`, `duration`, `steer`), `references` /
|
|
113
|
+
`onReferencesChange`, `voiceNoteText`, `placeholder`, `avatar`, `initialTurns`, `loadOlder` (earlier
|
|
114
|
+
pages of a long thread), the slot `sidePanel`, and the callbacks `onTurnStart`, `onTitle`, `onState`,
|
|
115
|
+
`onEvent` (every raw AG-UI event), `onSend`. The agent's shared state (`STATE_SNAPSHOT` +
|
|
116
|
+
`STATE_DELTA`) is `pilot.state.agentState`, and each turn's `CUSTOM` events are `turn.custom` (what
|
|
117
|
+
`renderDeliveries` draws). To read the state outside the chat, call `usePilot(transport)` yourself
|
|
118
|
+
and pass the result as `pilot`. Every piece is
|
|
116
119
|
exported (`AgentBubble`, `AgentSteps`, `Composer`, `ConfirmCard`, `Markdown`, …) if you want to build
|
|
117
120
|
your own layout.
|
|
118
121
|
|
|
@@ -1,7 +1,120 @@
|
|
|
1
|
+
// src/core/jsonPatch.ts
|
|
2
|
+
function parsePath(path) {
|
|
3
|
+
if (path === "") return [];
|
|
4
|
+
if (!path.startsWith("/")) throw new Error(`bad path ${path}`);
|
|
5
|
+
return path.slice(1).split("/").map((k) => k.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
6
|
+
}
|
|
7
|
+
function get(doc, keys) {
|
|
8
|
+
let cur = doc;
|
|
9
|
+
for (const k of keys) {
|
|
10
|
+
if (Array.isArray(cur)) cur = cur[Number(k)];
|
|
11
|
+
else if (cur && typeof cur === "object") cur = cur[k];
|
|
12
|
+
else return void 0;
|
|
13
|
+
}
|
|
14
|
+
return cur;
|
|
15
|
+
}
|
|
16
|
+
function edit(doc, keys, fn) {
|
|
17
|
+
if (!keys.length) throw new Error("root");
|
|
18
|
+
const [k, ...rest] = keys;
|
|
19
|
+
if (!rest.length) return fn(doc, k);
|
|
20
|
+
if (Array.isArray(doc)) {
|
|
21
|
+
const i = Number(k);
|
|
22
|
+
if (!(i in doc)) throw new Error(`missing ${k}`);
|
|
23
|
+
const out = [...doc];
|
|
24
|
+
out[i] = edit(doc[i], rest, fn);
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
if (doc && typeof doc === "object" && k in doc) {
|
|
28
|
+
return { ...doc, [k]: edit(doc[k], rest, fn) };
|
|
29
|
+
}
|
|
30
|
+
throw new Error(`missing ${k}`);
|
|
31
|
+
}
|
|
32
|
+
function add(doc, keys, value) {
|
|
33
|
+
if (!keys.length) return value;
|
|
34
|
+
return edit(doc, keys, (parent, k) => {
|
|
35
|
+
if (Array.isArray(parent)) {
|
|
36
|
+
const i = k === "-" ? parent.length : Number(k);
|
|
37
|
+
if (!Number.isInteger(i) || i < 0 || i > parent.length) throw new Error(`index ${k}`);
|
|
38
|
+
const out = [...parent];
|
|
39
|
+
out.splice(i, 0, value);
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
if (parent && typeof parent === "object") return { ...parent, [k]: value };
|
|
43
|
+
throw new Error("not a container");
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function remove(doc, keys) {
|
|
47
|
+
return edit(doc, keys, (parent, k) => {
|
|
48
|
+
if (Array.isArray(parent)) {
|
|
49
|
+
const i = Number(k);
|
|
50
|
+
if (!(i in parent)) throw new Error(`index ${k}`);
|
|
51
|
+
return parent.filter((_, j) => j !== i);
|
|
52
|
+
}
|
|
53
|
+
if (parent && typeof parent === "object" && k in parent) {
|
|
54
|
+
const { [k]: _, ...rest } = parent;
|
|
55
|
+
return rest;
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`missing ${k}`);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function replace(doc, keys, value) {
|
|
61
|
+
if (!keys.length) return value;
|
|
62
|
+
return edit(doc, keys, (parent, k) => {
|
|
63
|
+
if (Array.isArray(parent)) {
|
|
64
|
+
const i = Number(k);
|
|
65
|
+
if (!(i in parent)) throw new Error(`index ${k}`);
|
|
66
|
+
const out = [...parent];
|
|
67
|
+
out[i] = value;
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
if (parent && typeof parent === "object" && k in parent) return { ...parent, [k]: value };
|
|
71
|
+
throw new Error(`missing ${k}`);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
var equal = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
75
|
+
function applyJsonPatch(doc, ops) {
|
|
76
|
+
let cur = doc;
|
|
77
|
+
try {
|
|
78
|
+
for (const o of ops) {
|
|
79
|
+
const keys = parsePath(o.path);
|
|
80
|
+
if (o.op === "add") cur = add(cur, keys, o.value);
|
|
81
|
+
else if (o.op === "remove") cur = remove(cur, keys);
|
|
82
|
+
else if (o.op === "replace") cur = replace(cur, keys, o.value);
|
|
83
|
+
else if (o.op === "move" || o.op === "copy") {
|
|
84
|
+
const from = parsePath(o.from ?? "");
|
|
85
|
+
const v = get(cur, from);
|
|
86
|
+
if (v === void 0) throw new Error(`missing ${o.from}`);
|
|
87
|
+
if (o.op === "move") cur = remove(cur, from);
|
|
88
|
+
cur = add(cur, keys, v);
|
|
89
|
+
} else if (o.op === "test") {
|
|
90
|
+
if (!equal(get(cur, keys), o.value)) throw new Error(`test ${o.path}`);
|
|
91
|
+
} else throw new Error(`op ${o.op}`);
|
|
92
|
+
}
|
|
93
|
+
return { doc: cur, ok: true };
|
|
94
|
+
} catch {
|
|
95
|
+
return { doc, ok: false };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
1
99
|
// src/core/reducer.ts
|
|
2
100
|
function createPilotState(turns = []) {
|
|
3
|
-
return {
|
|
101
|
+
return {
|
|
102
|
+
turns,
|
|
103
|
+
busy: false,
|
|
104
|
+
error: null,
|
|
105
|
+
notice: null,
|
|
106
|
+
usage: null,
|
|
107
|
+
permissions: [],
|
|
108
|
+
turnStartedAt: 0,
|
|
109
|
+
runId: null,
|
|
110
|
+
agentState: {},
|
|
111
|
+
step: null,
|
|
112
|
+
history: null,
|
|
113
|
+
olderCount: 0,
|
|
114
|
+
effects: []
|
|
115
|
+
};
|
|
4
116
|
}
|
|
117
|
+
var PROTOCOL_CUSTOM = /* @__PURE__ */ new Set(["ghosty.title", "ghosty.injected", "ghosty.history", "ghosty.tool"]);
|
|
5
118
|
function patchAssistant(s, patch) {
|
|
6
119
|
const turns = [...s.turns];
|
|
7
120
|
const last = turns[turns.length - 1];
|
|
@@ -72,9 +185,9 @@ function attachmentsOf(content) {
|
|
|
72
185
|
function messagesToTurns(messages) {
|
|
73
186
|
const turns = [];
|
|
74
187
|
let cur = null;
|
|
75
|
-
const assistant = () => {
|
|
188
|
+
const assistant = (id) => {
|
|
76
189
|
if (!cur) {
|
|
77
|
-
cur = { role: "assistant", text: "" };
|
|
190
|
+
cur = { ...id ? { id } : {}, role: "assistant", text: "" };
|
|
78
191
|
turns.push(cur);
|
|
79
192
|
}
|
|
80
193
|
return cur;
|
|
@@ -82,9 +195,12 @@ function messagesToTurns(messages) {
|
|
|
82
195
|
for (const m of messages) {
|
|
83
196
|
if (m.role === "user") {
|
|
84
197
|
cur = null;
|
|
85
|
-
turns.push({ role: "user", text: textOf(m.content), ...attachmentsOf(m.content) ? { attachments: attachmentsOf(m.content) } : {} });
|
|
198
|
+
turns.push({ ...m.id ? { id: m.id } : {}, role: "user", text: textOf(m.content), ...attachmentsOf(m.content) ? { attachments: attachmentsOf(m.content) } : {} });
|
|
86
199
|
} else if (m.role === "assistant") {
|
|
87
200
|
const t = assistant();
|
|
201
|
+
if (!t.id && m.id) t.id = m.id;
|
|
202
|
+
const custom = m.custom;
|
|
203
|
+
if (Array.isArray(custom) && custom.length) t.custom = [...t.custom ?? [], ...custom.filter((c) => c && typeof c.name === "string")];
|
|
88
204
|
const text = m.content ?? "";
|
|
89
205
|
if (text) t.text = t.text ? `${t.text}
|
|
90
206
|
|
|
@@ -94,11 +210,11 @@ ${text}` : text;
|
|
|
94
210
|
if (t.text.trim() && t.cuts?.[t.cuts.length - 1] !== t.text.length) t.cuts = [...t.cuts ?? [], t.text.length];
|
|
95
211
|
}
|
|
96
212
|
} else if (m.role === "tool") {
|
|
97
|
-
const t = assistant();
|
|
213
|
+
const t = assistant(m.id);
|
|
98
214
|
const failed = Boolean(m.error);
|
|
99
215
|
t.tools = (t.tools ?? []).map((x) => x.id === m.toolCallId ? { ...x, status: failed ? "failed" : "completed" } : x);
|
|
100
216
|
} else if (m.role === "reasoning") {
|
|
101
|
-
const t = assistant();
|
|
217
|
+
const t = assistant(m.id);
|
|
102
218
|
t.thought = (t.thought ?? "") + textOf(m.content);
|
|
103
219
|
}
|
|
104
220
|
}
|
|
@@ -106,27 +222,47 @@ ${text}` : text;
|
|
|
106
222
|
}
|
|
107
223
|
function applySnapshot(s, messages) {
|
|
108
224
|
const fresh = messagesToTurns(messages);
|
|
109
|
-
|
|
225
|
+
let prefix = [];
|
|
226
|
+
if (s.olderCount && fresh[0]?.id) {
|
|
227
|
+
const at = s.turns.findIndex((t) => t.id === fresh[0].id);
|
|
228
|
+
if (at >= 0) prefix = s.turns.slice(0, at);
|
|
229
|
+
}
|
|
230
|
+
const keepOlder = prefix.length > 0;
|
|
231
|
+
const old = keepOlder ? s.turns.slice(prefix.length) : s.turns.slice(s.olderCount);
|
|
232
|
+
const lastIndex = fresh.length - 1;
|
|
110
233
|
const turns = fresh.map((t, i) => {
|
|
111
234
|
const o = old[i];
|
|
112
|
-
|
|
235
|
+
const live = s.busy && i === lastIndex && t.role === "assistant";
|
|
236
|
+
const tools = t.tools?.map((x) => {
|
|
237
|
+
const prev = o?.role === t.role ? o.tools?.find((y) => y.id === x.id) : void 0;
|
|
238
|
+
const status = x.status === "pending" && (prev?.status ?? (live ? "in_progress" : void 0));
|
|
239
|
+
return { ...prev, ...x, ...status ? { status } : {} };
|
|
240
|
+
});
|
|
241
|
+
const base = tools ? { ...t, tools } : t;
|
|
242
|
+
if (!o || o.role !== t.role) return base;
|
|
113
243
|
return {
|
|
114
|
-
...
|
|
244
|
+
...base,
|
|
115
245
|
...o.seconds !== void 0 ? { seconds: o.seconds } : {},
|
|
116
246
|
...o.usage ? { usage: o.usage } : {},
|
|
117
247
|
...o.decisions ? { decisions: o.decisions } : {},
|
|
118
248
|
...o.thought && !t.thought ? { thought: o.thought } : {},
|
|
119
249
|
...o.attachments && !t.attachments ? { attachments: o.attachments } : {},
|
|
120
|
-
...o.
|
|
250
|
+
...o.custom && !t.custom ? { custom: o.custom } : {}
|
|
121
251
|
};
|
|
122
252
|
});
|
|
123
|
-
return {
|
|
253
|
+
return {
|
|
254
|
+
...s,
|
|
255
|
+
turns: [...prefix, ...turns],
|
|
256
|
+
olderCount: prefix.length,
|
|
257
|
+
history: keepOlder ? s.history : s.historyNext ?? null,
|
|
258
|
+
historyNext: void 0
|
|
259
|
+
};
|
|
124
260
|
}
|
|
125
261
|
function reduceAgUiEvent(prev, e, now = Date.now()) {
|
|
126
262
|
const s = prev.effects.length ? { ...prev, effects: [] } : prev;
|
|
127
263
|
switch (e.type) {
|
|
128
264
|
case "RUN_STARTED":
|
|
129
|
-
return { ...s, busy: true, error: null, runId: e.runId ?? s.runId };
|
|
265
|
+
return { ...s, busy: true, error: null, step: null, runId: e.runId ?? s.runId };
|
|
130
266
|
case "TEXT_MESSAGE_START":
|
|
131
267
|
if (e.role && e.role !== "assistant") return s;
|
|
132
268
|
return patchAssistant(s, (t) => ({ ...t, text: t.text && !t.text.endsWith("\n") ? `${t.text}
|
|
@@ -169,15 +305,46 @@ function reduceAgUiEvent(prev, e, now = Date.now()) {
|
|
|
169
305
|
return finish(s, e, now);
|
|
170
306
|
case "RUN_ERROR":
|
|
171
307
|
return finish({ ...s, error: typeof e.message === "string" ? e.message : "El agente fall\xF3." }, e, now, true);
|
|
308
|
+
case "STEP_STARTED":
|
|
309
|
+
return typeof e.stepName === "string" ? { ...s, step: e.stepName } : s;
|
|
310
|
+
case "STEP_FINISHED":
|
|
311
|
+
return s.step === e.stepName ? { ...s, step: null } : s;
|
|
312
|
+
case "STATE_SNAPSHOT": {
|
|
313
|
+
const agentState = e.snapshot && typeof e.snapshot === "object" ? e.snapshot : {};
|
|
314
|
+
return { ...s, agentState, effects: [{ type: "state", state: agentState }] };
|
|
315
|
+
}
|
|
316
|
+
case "STATE_DELTA": {
|
|
317
|
+
if (!Array.isArray(e.delta)) return s;
|
|
318
|
+
const { doc, ok } = applyJsonPatch(s.agentState, e.delta);
|
|
319
|
+
return ok ? { ...s, agentState: doc, effects: [{ type: "state", state: doc }] } : s;
|
|
320
|
+
}
|
|
172
321
|
case "CUSTOM":
|
|
173
322
|
if (e.name === "ghosty.title") return { ...s, effects: [{ type: "title", title: e.value?.title }] };
|
|
174
|
-
return s;
|
|
323
|
+
if (e.name === "ghosty.history") return { ...s, historyNext: toHistory(e.value) };
|
|
324
|
+
if (e.name === "ghosty.tool") {
|
|
325
|
+
const v = e.value ?? {};
|
|
326
|
+
if (typeof v.toolCallId !== "string") return s;
|
|
327
|
+
const str = (x) => typeof x === "string" && x ? x : void 0;
|
|
328
|
+
const patch = {};
|
|
329
|
+
if (str(v.title)) patch.label = str(v.title);
|
|
330
|
+
if (str(v.kind)) patch.kind = str(v.kind);
|
|
331
|
+
if (str(v.path)) patch.path = str(v.path);
|
|
332
|
+
if (str(v.detail)) patch.detail = str(v.detail);
|
|
333
|
+
return upsertTool(s, v.toolCallId, patch);
|
|
334
|
+
}
|
|
335
|
+
if (typeof e.name !== "string" || PROTOCOL_CUSTOM.has(e.name)) return s;
|
|
336
|
+
return patchAssistant(s, (t) => ({ ...t, custom: [...t.custom ?? [], { name: e.name, value: e.value, ...typeof e.timestamp === "number" ? { timestamp: e.timestamp } : {} }] }));
|
|
175
337
|
default:
|
|
176
338
|
return s;
|
|
177
339
|
}
|
|
178
340
|
}
|
|
341
|
+
function toHistory(v) {
|
|
342
|
+
if (!v || typeof v !== "object") return null;
|
|
343
|
+
const h = v;
|
|
344
|
+
return { hasMoreBefore: h.hasMoreBefore === true, ...typeof h.before === "string" ? { before: h.before } : {} };
|
|
345
|
+
}
|
|
179
346
|
function finish(s0, e, now, failed = false) {
|
|
180
|
-
let s = { ...s0, busy: false };
|
|
347
|
+
let s = { ...s0, busy: false, step: null };
|
|
181
348
|
const outcome = e.outcome;
|
|
182
349
|
const interrupted = outcome?.type === "interrupt" && Array.isArray(outcome.interrupts) && outcome.interrupts.length > 0;
|
|
183
350
|
const clockOffset = typeof e.timestamp === "number" ? now - e.timestamp : 0;
|
|
@@ -214,11 +381,19 @@ function beginSend(prev, text, attachments = [], now = Date.now()) {
|
|
|
214
381
|
const turns = text || attachments.length ? [...prev.turns, { role: "user", text, ...attachments.length ? { attachments } : {} }] : prev.turns;
|
|
215
382
|
return { ...prev, turns, busy: true, error: null, notice: null, turnStartedAt: now, effects: [{ type: "turn-start" }] };
|
|
216
383
|
}
|
|
384
|
+
function beginSteer(prev, text) {
|
|
385
|
+
return { ...prev, turns: [...prev.turns, { role: "user", text }], notice: null, effects: [] };
|
|
386
|
+
}
|
|
387
|
+
function prependHistory(prev, messages, page) {
|
|
388
|
+
const have = new Set(prev.turns.map((t) => t.id).filter(Boolean));
|
|
389
|
+
const older = messagesToTurns(messages).filter((t) => !t.id || !have.has(t.id));
|
|
390
|
+
return { ...prev, turns: [...older, ...prev.turns], olderCount: prev.olderCount + older.length, history: page, effects: [] };
|
|
391
|
+
}
|
|
217
392
|
function sendFailed(prev, message) {
|
|
218
393
|
return { ...prev, busy: false, turnStartedAt: 0, error: message, effects: [] };
|
|
219
394
|
}
|
|
220
395
|
function stopTurn(prev) {
|
|
221
|
-
return { ...prev, busy: false, turnStartedAt: 0, turns: closeOpenTools(prev.turns, "cancelled"), effects: [] };
|
|
396
|
+
return { ...prev, busy: false, step: null, turnStartedAt: 0, turns: closeOpenTools(prev.turns, "cancelled"), effects: [] };
|
|
222
397
|
}
|
|
223
398
|
function dropLastExchange(prev) {
|
|
224
399
|
const turns = [...prev.turns];
|
|
@@ -340,6 +515,7 @@ function namedVerb(t, verbs) {
|
|
|
340
515
|
return null;
|
|
341
516
|
}
|
|
342
517
|
function stepLabel(t, verbs, fallback = "Paso") {
|
|
518
|
+
if (t.label) return t.label;
|
|
343
519
|
const named = namedVerb(t, verbs);
|
|
344
520
|
if (named) return isRunning(t) && named.verb.running ? named.verb.running : named.verb.one;
|
|
345
521
|
return t.title ?? t.kind ?? fallback;
|
|
@@ -451,12 +627,15 @@ var TOOL_VERBS_EN = {
|
|
|
451
627
|
};
|
|
452
628
|
|
|
453
629
|
export {
|
|
630
|
+
applyJsonPatch,
|
|
454
631
|
createPilotState,
|
|
455
632
|
argsDetail,
|
|
456
633
|
toPermission,
|
|
457
634
|
messagesToTurns,
|
|
458
635
|
reduceAgUiEvent,
|
|
459
636
|
beginSend,
|
|
637
|
+
beginSteer,
|
|
638
|
+
prependHistory,
|
|
460
639
|
sendFailed,
|
|
461
640
|
stopTurn,
|
|
462
641
|
dropLastExchange,
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from "./types";
|
|
2
|
-
export { argsDetail, beginSend, createPilotState, dropLastExchange, expiryMs, lastUserTurn, messagesToTurns, reduceAgUiEvent, resolvePermission, sendFailed, stopTurn, toPermission, } from "./reducer";
|
|
2
|
+
export { argsDetail, beginSend, beginSteer, createPilotState, dropLastExchange, expiryMs, lastUserTurn, messagesToTurns, prependHistory, reduceAgUiEvent, resolvePermission, sendFailed, stopTurn, toPermission, } from "./reducer";
|
|
3
|
+
export { applyJsonPatch, type PatchOp } from "./jsonPatch";
|
|
3
4
|
export { isNarration, narrationToMarkdown, splitNarration } from "./narration";
|
|
4
5
|
export { cleanReplyOption, extractReplyOptions, type ReplyOptions } from "./options";
|
|
5
6
|
export { TOOL_VERBS_EN, TOOL_VERBS_ES, groupSteps, isRunning, stepLabel, summarizeTools, toolName, toolNames, type ToolVerb, type ToolVerbs, } from "./steps";
|
package/dist/core/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TOOL_VERBS_EN,
|
|
3
3
|
TOOL_VERBS_ES,
|
|
4
|
+
applyJsonPatch,
|
|
4
5
|
argsDetail,
|
|
5
6
|
beginSend,
|
|
7
|
+
beginSteer,
|
|
6
8
|
cleanReplyOption,
|
|
7
9
|
createPilotState,
|
|
8
10
|
dropLastExchange,
|
|
@@ -14,6 +16,7 @@ import {
|
|
|
14
16
|
lastUserTurn,
|
|
15
17
|
messagesToTurns,
|
|
16
18
|
narrationToMarkdown,
|
|
19
|
+
prependHistory,
|
|
17
20
|
reduceAgUiEvent,
|
|
18
21
|
resolvePermission,
|
|
19
22
|
sendFailed,
|
|
@@ -24,7 +27,7 @@ import {
|
|
|
24
27
|
toPermission,
|
|
25
28
|
toolName,
|
|
26
29
|
toolNames
|
|
27
|
-
} from "../chunks/chunk-
|
|
30
|
+
} from "../chunks/chunk-ULNSZHZV.js";
|
|
28
31
|
import {
|
|
29
32
|
applyReferences,
|
|
30
33
|
cleanUserText,
|
|
@@ -34,9 +37,11 @@ import {
|
|
|
34
37
|
export {
|
|
35
38
|
TOOL_VERBS_EN,
|
|
36
39
|
TOOL_VERBS_ES,
|
|
40
|
+
applyJsonPatch,
|
|
37
41
|
applyReferences,
|
|
38
42
|
argsDetail,
|
|
39
43
|
beginSend,
|
|
44
|
+
beginSteer,
|
|
40
45
|
cleanReplyOption,
|
|
41
46
|
cleanUserText,
|
|
42
47
|
createPilotState,
|
|
@@ -50,6 +55,7 @@ export {
|
|
|
50
55
|
lastUserTurn,
|
|
51
56
|
messagesToTurns,
|
|
52
57
|
narrationToMarkdown,
|
|
58
|
+
prependHistory,
|
|
53
59
|
reduceAgUiEvent,
|
|
54
60
|
resolvePermission,
|
|
55
61
|
sendFailed,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface PatchOp {
|
|
2
|
+
op: "add" | "remove" | "replace" | "move" | "copy" | "test";
|
|
3
|
+
path: string;
|
|
4
|
+
value?: unknown;
|
|
5
|
+
from?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Aplica `ops` en orden. Si una falla, regresa el documento original y `ok: false`. */
|
|
8
|
+
export declare function applyJsonPatch<T>(doc: T, ops: PatchOp[]): {
|
|
9
|
+
doc: T;
|
|
10
|
+
ok: boolean;
|
|
11
|
+
};
|
package/dist/core/reducer.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Interrupt, Message } from "@ag-ui/core";
|
|
2
|
-
import type { Attachment, Decision, PermissionRequest, PilotState, Turn } from "./types";
|
|
2
|
+
import type { Attachment, Decision, HistoryPage, PermissionRequest, PilotState, Turn } from "./types";
|
|
3
3
|
export declare function createPilotState(turns?: Turn[]): PilotState;
|
|
4
4
|
type Ev = {
|
|
5
5
|
type: string;
|
|
@@ -18,6 +18,16 @@ export declare function messagesToTurns(messages: Message[]): Turn[];
|
|
|
18
18
|
export declare function reduceAgUiEvent(prev: PilotState, e: Ev, now?: number): PilotState;
|
|
19
19
|
/** El usuario manda un mensaje (o contesta una tarjeta: `text` vacío, sin burbuja). */
|
|
20
20
|
export declare function beginSend(prev: PilotState, text: string, attachments?: Attachment[], now?: number): PilotState;
|
|
21
|
+
/**
|
|
22
|
+
* Steer: un mensaje del usuario mientras el run sigue. Su burbuja entra en el hilo y lo que el agente
|
|
23
|
+
* diga después va en una respuesta nueva debajo; el run (y su reloj) siguen siendo los mismos.
|
|
24
|
+
*/
|
|
25
|
+
export declare function beginSteer(prev: PilotState, text: string): PilotState;
|
|
26
|
+
/**
|
|
27
|
+
* Mensajes más viejos (`loadOlder`) delante del hilo. Las páginas empiezan en un mensaje del usuario;
|
|
28
|
+
* lo que ya se tenía (mismo id) no se repite.
|
|
29
|
+
*/
|
|
30
|
+
export declare function prependHistory(prev: PilotState, messages: Message[], page: HistoryPage | null): PilotState;
|
|
21
31
|
/** El servidor no aceptó el encargo (HTTP): se DICE, para que no se quede pensando. */
|
|
22
32
|
export declare function sendFailed(prev: PilotState, message: string): PilotState;
|
|
23
33
|
/** Detener: el cliente cortó la conexión (el servidor corta el run). */
|
package/dist/core/types.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface ToolEntry {
|
|
|
14
14
|
id: string;
|
|
15
15
|
/** El nombre de la herramienta (`toolCallName`): es la llave de los verbos de la app. */
|
|
16
16
|
title?: string;
|
|
17
|
+
/** El rótulo que puso el servidor (`CUSTOM ghosty.tool`): gana sobre los verbos de la app. */
|
|
18
|
+
label?: string;
|
|
17
19
|
kind?: string;
|
|
18
20
|
/** `in_progress` mientras corre; `completed` | `failed` con su resultado; `pending` = espera
|
|
19
21
|
* aprobación; `cancelled` = el run se detuvo antes de que terminara. */
|
|
@@ -38,7 +40,21 @@ export interface Decision {
|
|
|
38
40
|
status: "approved" | "rejected" | "expired" | "cancelled";
|
|
39
41
|
detail?: string;
|
|
40
42
|
}
|
|
43
|
+
/** Un evento `CUSTOM` del turno (entregas de archivos, avisos propios de la app). */
|
|
44
|
+
export interface CustomEntry {
|
|
45
|
+
name: string;
|
|
46
|
+
value: unknown;
|
|
47
|
+
timestamp?: number;
|
|
48
|
+
}
|
|
49
|
+
/** Hay mensajes más viejos que los del snapshot (`CUSTOM ghosty.history`). */
|
|
50
|
+
export interface HistoryPage {
|
|
51
|
+
hasMoreBefore: boolean;
|
|
52
|
+
/** Cursor opaco del servidor: se le regresa a `loadOlder`. */
|
|
53
|
+
before?: string;
|
|
54
|
+
}
|
|
41
55
|
export interface Turn {
|
|
56
|
+
/** El id del primer mensaje AG-UI del turno (sólo los que vienen de un snapshot). */
|
|
57
|
+
id?: string;
|
|
42
58
|
role: "user" | "assistant";
|
|
43
59
|
text: string;
|
|
44
60
|
/** Cuánto tardó, en segundos (sólo los turnos vistos en vivo). */
|
|
@@ -51,6 +67,9 @@ export interface Turn {
|
|
|
51
67
|
usage?: Usage;
|
|
52
68
|
/** Lo que el usuario decidió en las tarjetas de este turno (sólo en vivo). */
|
|
53
69
|
decisions?: Decision[];
|
|
70
|
+
/** Los `CUSTOM` del turno (p. ej. `ghosty.delivery`), para `renderDeliveries`. Del snapshot salen
|
|
71
|
+
* del campo `custom` de cada mensaje del asistente. */
|
|
72
|
+
custom?: CustomEntry[];
|
|
54
73
|
}
|
|
55
74
|
/** Una opción de la tarjeta de aprobación. */
|
|
56
75
|
export interface PermissionOption {
|
|
@@ -90,6 +109,9 @@ export type PilotEffect = {
|
|
|
90
109
|
} | {
|
|
91
110
|
type: "title";
|
|
92
111
|
title?: string;
|
|
112
|
+
} | {
|
|
113
|
+
type: "state";
|
|
114
|
+
state: Record<string, unknown>;
|
|
93
115
|
};
|
|
94
116
|
export interface PilotState {
|
|
95
117
|
turns: Turn[];
|
|
@@ -103,6 +125,16 @@ export interface PilotState {
|
|
|
103
125
|
/** Cuándo empezó el turno que ESTE cliente encargó (0 = ninguno). */
|
|
104
126
|
turnStartedAt: number;
|
|
105
127
|
runId: string | null;
|
|
128
|
+
/** El estado compartido del agente (`STATE_SNAPSHOT`, con `STATE_DELTA` aplicado encima). */
|
|
129
|
+
agentState: Record<string, unknown>;
|
|
130
|
+
/** El paso con nombre en curso (`STEP_STARTED`), p. ej. `waking`. */
|
|
131
|
+
step: string | null;
|
|
132
|
+
/** Si hay mensajes más viejos por cargar (`loadOlder`). */
|
|
133
|
+
history: HistoryPage | null;
|
|
134
|
+
/** Cuántos de `turns` llegaron con `loadOlder` (van antes del snapshot). */
|
|
135
|
+
olderCount: number;
|
|
136
|
+
/** El `ghosty.history` que acompaña al snapshot que viene. */
|
|
137
|
+
historyNext?: HistoryPage | null;
|
|
106
138
|
/** Efectos de la ÚLTIMA transición. El hook los ejecuta y los descarta. */
|
|
107
139
|
effects: PilotEffect[];
|
|
108
140
|
}
|
package/dist/react/Piloto.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface PilotoFeatures {
|
|
|
19
19
|
replyOptions?: boolean;
|
|
20
20
|
/** «· 12 s» junto a la frase de espera. Default: sí. */
|
|
21
21
|
duration?: boolean;
|
|
22
|
+
/** Mandar con el run vivo (steer). Default: lo que diga el agente en `state.caps.steer`. */
|
|
23
|
+
steer?: boolean;
|
|
22
24
|
}
|
|
23
25
|
export interface PilotoSlots {
|
|
24
26
|
/** Lo que la app pinte bajo cada respuesta (archivos, tarjetas propias). */
|
|
@@ -31,6 +33,8 @@ export interface PilotoSlots {
|
|
|
31
33
|
headerActions?: ReactNode;
|
|
32
34
|
/** Pegado arriba del composer (un aviso, un banner). */
|
|
33
35
|
aboveComposer?: ReactNode;
|
|
36
|
+
/** Un panel junto al chat (subagentes, vista previa), con el estado del agente. */
|
|
37
|
+
sidePanel?: (agentState: Record<string, unknown>) => ReactNode;
|
|
34
38
|
}
|
|
35
39
|
export interface PilotoProps extends UsePilotOptions {
|
|
36
40
|
transport: PilotTransport | null;
|
package/dist/react/Status.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** Los tres puntitos de «escribiendo» (color y animación en CSS: `gc-typing`). */
|
|
2
2
|
export declare function TypingDots(): import("react").JSX.Element;
|
|
3
|
-
export declare function WaitLabel({ since, showDuration, }: {
|
|
3
|
+
export declare function WaitLabel({ since, showDuration, step, }: {
|
|
4
|
+
/** El paso con nombre en curso (`STEP_STARTED`): si `strings.steps` lo nombra, esa es la frase. */
|
|
5
|
+
step?: string | null;
|
|
4
6
|
/** Desde que se MANDÓ el mensaje (no desde que se montó la etiqueta). */
|
|
5
7
|
since?: number | null;
|
|
6
8
|
showDuration?: boolean;
|
package/dist/react/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TOOL_VERBS_EN,
|
|
3
3
|
TOOL_VERBS_ES,
|
|
4
|
+
applyJsonPatch,
|
|
4
5
|
argsDetail,
|
|
5
6
|
beginSend,
|
|
7
|
+
beginSteer,
|
|
6
8
|
cleanReplyOption,
|
|
7
9
|
createPilotState,
|
|
8
10
|
dropLastExchange,
|
|
@@ -14,6 +16,7 @@ import {
|
|
|
14
16
|
lastUserTurn,
|
|
15
17
|
messagesToTurns,
|
|
16
18
|
narrationToMarkdown,
|
|
19
|
+
prependHistory,
|
|
17
20
|
reduceAgUiEvent,
|
|
18
21
|
resolvePermission,
|
|
19
22
|
sendFailed,
|
|
@@ -24,7 +27,7 @@ import {
|
|
|
24
27
|
toPermission,
|
|
25
28
|
toolName,
|
|
26
29
|
toolNames
|
|
27
|
-
} from "../chunks/chunk-
|
|
30
|
+
} from "../chunks/chunk-ULNSZHZV.js";
|
|
28
31
|
import {
|
|
29
32
|
PilotHttpError,
|
|
30
33
|
createAgUiTransport,
|
|
@@ -884,6 +887,9 @@ var STRINGS_ES = {
|
|
|
884
887
|
linkOpen: "Abrir",
|
|
885
888
|
close: "Cerrar",
|
|
886
889
|
goToLatest: "Ir al \xFAltimo mensaje",
|
|
890
|
+
steps: { waking: "Despertando al agente\u2026", session: "Preparando la sesi\xF3n\u2026", compacting: "Resumiendo la conversaci\xF3n\u2026" },
|
|
891
|
+
loadOlder: "Cargar mensajes anteriores",
|
|
892
|
+
loadingOlder: "Cargando\u2026",
|
|
887
893
|
confirmTitle: "Necesito tu permiso para",
|
|
888
894
|
approve: "Aprobar",
|
|
889
895
|
reject: "Rechazar",
|
|
@@ -949,6 +955,9 @@ var STRINGS_EN = {
|
|
|
949
955
|
linkOpen: "Open",
|
|
950
956
|
close: "Close",
|
|
951
957
|
goToLatest: "Go to latest message",
|
|
958
|
+
steps: { waking: "Waking the agent\u2026", session: "Starting the session\u2026", compacting: "Compacting the conversation\u2026" },
|
|
959
|
+
loadOlder: "Load earlier messages",
|
|
960
|
+
loadingOlder: "Loading\u2026",
|
|
952
961
|
confirmTitle: "I need your permission to",
|
|
953
962
|
approve: "Approve",
|
|
954
963
|
reject: "Reject",
|
|
@@ -2251,7 +2260,8 @@ function TypingDots() {
|
|
|
2251
2260
|
}
|
|
2252
2261
|
function WaitLabel({
|
|
2253
2262
|
since,
|
|
2254
|
-
showDuration = true
|
|
2263
|
+
showDuration = true,
|
|
2264
|
+
step
|
|
2255
2265
|
}) {
|
|
2256
2266
|
const { strings: t } = usePiloto();
|
|
2257
2267
|
const [mounted] = useState7(() => Date.now());
|
|
@@ -2262,7 +2272,8 @@ function WaitLabel({
|
|
|
2262
2272
|
}, []);
|
|
2263
2273
|
const seconds = Math.max(0, Math.floor((now - (since || mounted)) / 1e3));
|
|
2264
2274
|
const phrases = t.thinkingPhrases;
|
|
2265
|
-
const
|
|
2275
|
+
const stepText = step ? t.steps?.[step] : void 0;
|
|
2276
|
+
const what = stepText ? stepText : phrases?.length ? seconds >= STILL_WORKING_S ? t.stillWorking : phrases[Math.floor(seconds / 4) % phrases.length] : t.howItGoes(seconds);
|
|
2266
2277
|
return /* @__PURE__ */ jsx9("div", { className: "flex flex-col gap-0.5 text-sm text-text-secondary", children: /* @__PURE__ */ jsxs7("p", { className: "flex items-center", children: [
|
|
2267
2278
|
/* @__PURE__ */ jsx9(AnimatePresence2, { mode: "wait", initial: false, children: /* @__PURE__ */ jsx9(
|
|
2268
2279
|
motion2.span,
|
|
@@ -2329,6 +2340,7 @@ function usePilot(transport, opts = {}) {
|
|
|
2329
2340
|
if (e.type === "turn-start") o.onTurnStart?.();
|
|
2330
2341
|
else if (e.type === "turn-done") o.onTurnDone?.({ text: e.text, runId: e.runId, seconds: e.seconds, error: e.error, interrupted: e.interrupted });
|
|
2331
2342
|
else if (e.type === "title") o.onTitle?.(e.title);
|
|
2343
|
+
else if (e.type === "state") o.onState?.(e.state);
|
|
2332
2344
|
}
|
|
2333
2345
|
}, []);
|
|
2334
2346
|
const runNow = useCallback2(
|
|
@@ -2348,7 +2360,8 @@ function usePilot(transport, opts = {}) {
|
|
|
2348
2360
|
ctrl.current?.abort();
|
|
2349
2361
|
const c = new AbortController();
|
|
2350
2362
|
ctrl.current = c;
|
|
2351
|
-
|
|
2363
|
+
const steering = typeof input.forwardedProps?.steer === "string";
|
|
2364
|
+
if (!steering && (input.messages.length || input.resume?.length)) liveRun.current = input.runId;
|
|
2352
2365
|
optsRef.current.onSend?.(input);
|
|
2353
2366
|
let ended = false;
|
|
2354
2367
|
try {
|
|
@@ -2372,6 +2385,10 @@ function usePilot(transport, opts = {}) {
|
|
|
2372
2385
|
commit({ ...ref.current, notice: err instanceof Error ? err.message : null, effects: [] });
|
|
2373
2386
|
return runNow({ messages: [] }, true);
|
|
2374
2387
|
}
|
|
2388
|
+
if (steering && !reopening) {
|
|
2389
|
+
commit({ ...ref.current, notice: err instanceof Error && err.message ? err.message : null, effects: [] });
|
|
2390
|
+
return runNow({ messages: [] }, true);
|
|
2391
|
+
}
|
|
2375
2392
|
commit(sendFailed(ref.current, err instanceof Error && err.message ? err.message : "No se pudo hablar con el agente."));
|
|
2376
2393
|
return;
|
|
2377
2394
|
}
|
|
@@ -2390,9 +2407,17 @@ function usePilot(transport, opts = {}) {
|
|
|
2390
2407
|
...ref.current.permissions.filter((p) => !answers.current.has(p.id)).map((p) => ({ interruptId: p.id, status: "cancelled" })),
|
|
2391
2408
|
...[...answers.current.entries()].map(([interruptId, a]) => ({ interruptId, ...a }))
|
|
2392
2409
|
];
|
|
2410
|
+
const steerOn = () => optsRef.current.steer ?? ref.current.agentState.caps?.steer === true;
|
|
2393
2411
|
const send = useCallback2(
|
|
2394
2412
|
async (text, attachments = [], _extra = {}) => {
|
|
2395
|
-
if (!transportRef.current
|
|
2413
|
+
if (!transportRef.current) return;
|
|
2414
|
+
if (ref.current.busy) {
|
|
2415
|
+
const live = liveRun.current ?? ref.current.runId;
|
|
2416
|
+
if (!steerOn() || !live || !text.trim() || attachments.length) return;
|
|
2417
|
+
commit(beginSteer(ref.current, text));
|
|
2418
|
+
await runNow({ messages: [userMessage(text, [])], forwardedProps: { steer: live } });
|
|
2419
|
+
return;
|
|
2420
|
+
}
|
|
2396
2421
|
const resume = pendingResume();
|
|
2397
2422
|
answers.current.clear();
|
|
2398
2423
|
let s = ref.current;
|
|
@@ -2454,7 +2479,27 @@ function usePilot(transport, opts = {}) {
|
|
|
2454
2479
|
},
|
|
2455
2480
|
[commit, sendAnswersIfComplete]
|
|
2456
2481
|
);
|
|
2457
|
-
|
|
2482
|
+
const [loadingOlder, setLoadingOlder] = useState8(false);
|
|
2483
|
+
const loadingRef = useRef4(false);
|
|
2484
|
+
const loadOlder = useCallback2(async () => {
|
|
2485
|
+
const fn = optsRef.current.loadOlder;
|
|
2486
|
+
const h = ref.current.history;
|
|
2487
|
+
if (!fn || !h?.hasMoreBefore || loadingRef.current) return;
|
|
2488
|
+
loadingRef.current = true;
|
|
2489
|
+
setLoadingOlder(true);
|
|
2490
|
+
try {
|
|
2491
|
+
const page = await fn(h.before);
|
|
2492
|
+
if (ref.current.history !== h) return;
|
|
2493
|
+
commit(prependHistory(ref.current, page.messages ?? [], { hasMoreBefore: page.hasMoreBefore === true, ...page.before ? { before: page.before } : {} }));
|
|
2494
|
+
} catch (err) {
|
|
2495
|
+
commit({ ...ref.current, notice: err instanceof Error && err.message ? err.message : null, effects: [] });
|
|
2496
|
+
} finally {
|
|
2497
|
+
loadingRef.current = false;
|
|
2498
|
+
setLoadingOlder(false);
|
|
2499
|
+
}
|
|
2500
|
+
}, [commit]);
|
|
2501
|
+
const canSteer = Boolean(transport) && (opts.steer ?? state.agentState.caps?.steer === true);
|
|
2502
|
+
return { state, threadId, send, stop, retry, respondPermission, recordDecision, expirePermission, canSteer, loadOlder, loadingOlder };
|
|
2458
2503
|
}
|
|
2459
2504
|
|
|
2460
2505
|
// src/react/Piloto.tsx
|
|
@@ -2463,10 +2508,10 @@ var useIsoLayoutEffect = typeof window === "undefined" ? useEffect6 : useLayoutE
|
|
|
2463
2508
|
var ANCHOR_GAP = 24;
|
|
2464
2509
|
var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
2465
2510
|
const { transport, pilot: external, locale = "es", theme = "light", features = {}, slots = {} } = props;
|
|
2466
|
-
const own = usePilot(external ? null : transport, props);
|
|
2511
|
+
const own = usePilot(external ? null : transport, features.steer === void 0 ? props : { ...props, steer: features.steer });
|
|
2467
2512
|
const pilot = external ?? own;
|
|
2468
2513
|
const { state } = pilot;
|
|
2469
|
-
const { turns, busy, error, notice, permissions } = state;
|
|
2514
|
+
const { turns, busy, error, notice, permissions, olderCount } = state;
|
|
2470
2515
|
const strings = useMemo3(() => ({ ...locale === "en" ? STRINGS_EN : STRINGS_ES, ...props.strings }), [locale, props.strings]);
|
|
2471
2516
|
const config = useMemo3(
|
|
2472
2517
|
() => ({
|
|
@@ -2550,6 +2595,18 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2550
2595
|
const touchY = useRef5(null);
|
|
2551
2596
|
const [atBottom, setAtBottom] = useState9(true);
|
|
2552
2597
|
const message = (n) => scroller.current?.querySelector(`[data-turn="${n}"]`) ?? null;
|
|
2598
|
+
const olderSeen = useRef5(olderCount);
|
|
2599
|
+
const heightBefore = useRef5(0);
|
|
2600
|
+
useIsoLayoutEffect(() => {
|
|
2601
|
+
const el = scroller.current;
|
|
2602
|
+
if (el && olderCount > olderSeen.current && heightBefore.current) el.scrollTop += el.scrollHeight - heightBefore.current;
|
|
2603
|
+
olderSeen.current = olderCount;
|
|
2604
|
+
heightBefore.current = 0;
|
|
2605
|
+
}, [olderCount]);
|
|
2606
|
+
const loadOlder = () => {
|
|
2607
|
+
heightBefore.current = scroller.current?.scrollHeight ?? 0;
|
|
2608
|
+
void pilot.loadOlder();
|
|
2609
|
+
};
|
|
2553
2610
|
const placed = useRef5(false);
|
|
2554
2611
|
useEffect6(() => {
|
|
2555
2612
|
if (placed.current || turns.length === 0) return;
|
|
@@ -2571,10 +2628,10 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2571
2628
|
useEffect6(() => {
|
|
2572
2629
|
if (mine === mineSeen.current) return;
|
|
2573
2630
|
mineSeen.current = mine;
|
|
2574
|
-
if (mine) setAnchor(mineIndex);
|
|
2631
|
+
if (mine) setAnchor(mineIndex - olderCount);
|
|
2575
2632
|
following.current = true;
|
|
2576
2633
|
}, [mine, mineIndex]);
|
|
2577
|
-
const gapAbove = (a) => anchor ===
|
|
2634
|
+
const gapAbove = (a) => anchor === -olderCount ? a.offsetTop : ANCHOR_GAP;
|
|
2578
2635
|
const measureSpacer = useCallback3(() => {
|
|
2579
2636
|
const sc = scroller.current;
|
|
2580
2637
|
const sp = spacer.current;
|
|
@@ -2636,7 +2693,7 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2636
2693
|
onSubmit: send,
|
|
2637
2694
|
onStop: pilot.stop,
|
|
2638
2695
|
busy,
|
|
2639
|
-
canSteer:
|
|
2696
|
+
canSteer: pilot.canSteer,
|
|
2640
2697
|
placeholder,
|
|
2641
2698
|
attachments: showFiles,
|
|
2642
2699
|
upload: props.upload,
|
|
@@ -2650,8 +2707,12 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2650
2707
|
draft.n
|
|
2651
2708
|
)
|
|
2652
2709
|
] });
|
|
2710
|
+
const withPanel = (chat) => slots.sidePanel ? /* @__PURE__ */ jsxs8("div", { className: "flex min-h-0 flex-1 flex-row", children: [
|
|
2711
|
+
/* @__PURE__ */ jsx10("div", { className: "relative flex min-h-0 min-w-0 flex-1 flex-col", children: chat }),
|
|
2712
|
+
slots.sidePanel(state.agentState)
|
|
2713
|
+
] }) : chat;
|
|
2653
2714
|
const permissionCards = cards.map((p) => /* @__PURE__ */ jsx10(ConfirmCard, { request: p, onRespond: respond, onSettled: settle, onExpire: pilot.expirePermission }, p.id));
|
|
2654
|
-
return /* @__PURE__ */ jsx10(PilotoContext.Provider, { value: config, children: /* @__PURE__ */ jsx10("div", { ...rootProps(theme, props.skin), "data-busy": busy || void 0, className: `piloto ${props.className ?? ""}`, style: props.style, children: empty ? (
|
|
2715
|
+
return /* @__PURE__ */ jsx10(PilotoContext.Provider, { value: config, children: /* @__PURE__ */ jsx10("div", { ...rootProps(theme, props.skin), "data-busy": busy || void 0, className: `piloto ${props.className ?? ""}`, style: props.style, children: withPanel(empty ? (
|
|
2655
2716
|
// Un hilo vacío enseña lo mismo que la portada: la cara, el saludo, la caja y por dónde
|
|
2656
2717
|
// empezar. Si no cabe (panel bajo), se desplaza: `safe center` nunca empuja nada fuera.
|
|
2657
2718
|
/* @__PURE__ */ jsxs8("div", { className: "gs-scroll flex min-h-0 flex-1 flex-col items-center justify-center overflow-y-auto px-4 pb-[10vh] [justify-content:safe_center]", children: [
|
|
@@ -2692,10 +2753,20 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2692
2753
|
className: "gs-scroll relative min-h-0 flex-1 overflow-y-auto overflow-x-hidden",
|
|
2693
2754
|
children: [
|
|
2694
2755
|
/* @__PURE__ */ jsxs8("div", { ref: column, className: "mx-auto flex w-full max-w-[784px] flex-col gap-6 px-4 py-8 sm:px-6", children: [
|
|
2756
|
+
state.history?.hasMoreBefore && (external || props.loadOlder) && /* @__PURE__ */ jsx10(
|
|
2757
|
+
"button",
|
|
2758
|
+
{
|
|
2759
|
+
type: "button",
|
|
2760
|
+
onClick: loadOlder,
|
|
2761
|
+
disabled: pilot.loadingOlder,
|
|
2762
|
+
className: "self-center rounded-full border border-border-primary px-3 py-1 text-sm text-text-secondary transition hover:text-text-primary disabled:opacity-60",
|
|
2763
|
+
children: pilot.loadingOlder ? t.loadingOlder : t.loadOlder
|
|
2764
|
+
}
|
|
2765
|
+
),
|
|
2695
2766
|
turns.map((turn, i) => {
|
|
2696
2767
|
const isLast = i === turns.length - 1;
|
|
2697
2768
|
const live = busy && isLast;
|
|
2698
|
-
return /* @__PURE__ */ jsx10("div", { "data-turn": i, children: turn.role === "user" ? /* @__PURE__ */ jsx10(UserBubble, { turn }) : /* @__PURE__ */ jsx10(
|
|
2769
|
+
return /* @__PURE__ */ jsx10("div", { "data-turn": i - olderCount, children: turn.role === "user" ? /* @__PURE__ */ jsx10(UserBubble, { turn }) : /* @__PURE__ */ jsx10(
|
|
2699
2770
|
AgentBubble,
|
|
2700
2771
|
{
|
|
2701
2772
|
turn,
|
|
@@ -2707,12 +2778,12 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2707
2778
|
showDuration: features.duration !== false,
|
|
2708
2779
|
renderDeliveries: slots.renderDeliveries
|
|
2709
2780
|
}
|
|
2710
|
-
) }, i);
|
|
2781
|
+
) }, i - olderCount);
|
|
2711
2782
|
}),
|
|
2712
2783
|
permissionCards,
|
|
2713
2784
|
busy && !toolRunning && !cards.length && /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-1.5", children: [
|
|
2714
2785
|
/* @__PURE__ */ jsx10(TypingDots, {}),
|
|
2715
|
-
/* @__PURE__ */ jsx10(WaitLabel, { since: state.turnStartedAt, showDuration: features.duration !== false })
|
|
2786
|
+
/* @__PURE__ */ jsx10(WaitLabel, { since: state.turnStartedAt, step: state.step, showDuration: features.duration !== false })
|
|
2716
2787
|
] }),
|
|
2717
2788
|
notice && /* @__PURE__ */ jsx10("p", { className: "text-sm text-text-tertiary", children: notice }),
|
|
2718
2789
|
error && /* @__PURE__ */ jsx10("p", { className: "text-sm text-text-danger", children: error })
|
|
@@ -2735,7 +2806,7 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2735
2806
|
slots.aboveComposer,
|
|
2736
2807
|
composer
|
|
2737
2808
|
] })
|
|
2738
|
-
] }) }) });
|
|
2809
|
+
] })) }) });
|
|
2739
2810
|
});
|
|
2740
2811
|
export {
|
|
2741
2812
|
AgentBubble,
|
|
@@ -2769,9 +2840,11 @@ export {
|
|
|
2769
2840
|
UsageStats,
|
|
2770
2841
|
UserBubble,
|
|
2771
2842
|
WaitLabel,
|
|
2843
|
+
applyJsonPatch,
|
|
2772
2844
|
applyReferences,
|
|
2773
2845
|
argsDetail,
|
|
2774
2846
|
beginSend,
|
|
2847
|
+
beginSteer,
|
|
2775
2848
|
cleanReplyOption,
|
|
2776
2849
|
cleanUserText,
|
|
2777
2850
|
createAgUiTransport,
|
|
@@ -2788,6 +2861,7 @@ export {
|
|
|
2788
2861
|
messagesToTurns,
|
|
2789
2862
|
mimeOf,
|
|
2790
2863
|
narrationToMarkdown,
|
|
2864
|
+
prependHistory,
|
|
2791
2865
|
readSse,
|
|
2792
2866
|
reduceAgUiEvent,
|
|
2793
2867
|
renderInline,
|
package/dist/react/strings.d.ts
CHANGED
|
@@ -54,6 +54,10 @@ export interface PilotStrings {
|
|
|
54
54
|
linkOpen: string;
|
|
55
55
|
close: string;
|
|
56
56
|
goToLatest: string;
|
|
57
|
+
/** La frase de espera de cada paso con nombre (`STEP_STARTED.stepName`). Los que no estén aquí no se pintan. */
|
|
58
|
+
steps: Record<string, string>;
|
|
59
|
+
loadOlder: string;
|
|
60
|
+
loadingOlder: string;
|
|
57
61
|
confirmTitle: string;
|
|
58
62
|
approve: string;
|
|
59
63
|
reject: string;
|
package/dist/react/usePilot.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { BaseEvent, RunAgentInput } from "@ag-ui/core";
|
|
2
|
-
import type { Attachment, Decision, PermissionRequest, PilotState, Reference, Turn } from "../core/types";
|
|
1
|
+
import type { BaseEvent, Message, RunAgentInput } from "@ag-ui/core";
|
|
2
|
+
import type { Attachment, Decision, HistoryPage, PermissionRequest, PilotState, Reference, Turn } from "../core/types";
|
|
3
3
|
import { type PilotTransport } from "../transport";
|
|
4
4
|
export interface TurnDoneInfo {
|
|
5
5
|
/** El texto final de la respuesta. */
|
|
@@ -31,6 +31,20 @@ export interface UsePilotOptions {
|
|
|
31
31
|
onEvent?: (event: BaseEvent) => void;
|
|
32
32
|
/** Cada run que sale (analítica). */
|
|
33
33
|
onSend?: (input: RunAgentInput) => void;
|
|
34
|
+
/** El estado del agente cambió (`STATE_SNAPSHOT` / `STATE_DELTA` ya aplicado). */
|
|
35
|
+
onState?: (state: Record<string, unknown>) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Steer: mandar con el run vivo (docs/protocol.md §6). Default: lo que diga el agente en
|
|
38
|
+
* `state.caps.steer`. Sin steer, mandar mientras corre no hace nada.
|
|
39
|
+
*/
|
|
40
|
+
steer?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Trae los mensajes anteriores a `before` (el cursor de `CUSTOM ghosty.history`), p. ej. del REST de
|
|
43
|
+
* lectura de la app. Las páginas empiezan en un mensaje del usuario.
|
|
44
|
+
*/
|
|
45
|
+
loadOlder?: (before: string | undefined) => Promise<{
|
|
46
|
+
messages: Message[];
|
|
47
|
+
} & HistoryPage>;
|
|
34
48
|
}
|
|
35
49
|
export interface SendOptions {
|
|
36
50
|
references?: Reference[];
|
|
@@ -49,6 +63,11 @@ export interface Pilot {
|
|
|
49
63
|
recordDecision: (id: string, status: Decision["status"], asked?: PermissionRequest) => void;
|
|
50
64
|
/** Una tarjeta venció sin contestarse: se anota y su interrupt viaja como `cancelled` en el siguiente run. */
|
|
51
65
|
expirePermission: (id: string) => void;
|
|
66
|
+
/** Se puede mandar con el run vivo (steer). */
|
|
67
|
+
canSteer: boolean;
|
|
68
|
+
/** Antepone la página anterior del hilo (si hay `loadOlder` y `state.history.hasMoreBefore`). */
|
|
69
|
+
loadOlder: () => Promise<void>;
|
|
70
|
+
loadingOlder: boolean;
|
|
52
71
|
}
|
|
53
72
|
/** El contexto de la app como `context` de AG-UI: `[{description, value}]`, `value` siempre texto. */
|
|
54
73
|
export declare function toAgUiContext(ctx: unknown): RunAgentInput["context"];
|
package/dist/styles.css
CHANGED
|
@@ -511,6 +511,9 @@
|
|
|
511
511
|
.piloto .flex-col {
|
|
512
512
|
flex-direction: column;
|
|
513
513
|
}
|
|
514
|
+
.piloto .flex-row {
|
|
515
|
+
flex-direction: row;
|
|
516
|
+
}
|
|
514
517
|
.piloto .flex-wrap {
|
|
515
518
|
flex-wrap: wrap;
|
|
516
519
|
}
|
|
@@ -588,6 +591,9 @@
|
|
|
588
591
|
.piloto :where(.divide-border > :not(:last-child)) {
|
|
589
592
|
border-color: var(--color-border);
|
|
590
593
|
}
|
|
594
|
+
.piloto .self-center {
|
|
595
|
+
align-self: center;
|
|
596
|
+
}
|
|
591
597
|
.piloto .truncate {
|
|
592
598
|
overflow: hidden;
|
|
593
599
|
text-overflow: ellipsis;
|
|
@@ -1404,6 +1410,9 @@
|
|
|
1404
1410
|
.piloto .disabled\:opacity-50:disabled {
|
|
1405
1411
|
opacity: 50%;
|
|
1406
1412
|
}
|
|
1413
|
+
.piloto .disabled\:opacity-60:disabled {
|
|
1414
|
+
opacity: 60%;
|
|
1415
|
+
}
|
|
1407
1416
|
@supports (backdrop-filter: var(--tw)) {
|
|
1408
1417
|
.piloto .supports-\[backdrop-filter\]\:bg-background\/70 {
|
|
1409
1418
|
background-color: var(--color-background);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ghostykit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Ghosty Autopilot \u2014 the autopilot chat for your React app, on AG-UI: works with LangGraph, Mastra, CrewAI or any AG-UI agent. Streaming text, agent steps, human approval cards (interrupt/resume), attachments and voice.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/core/index.js",
|