ghostykit 0.6.0 → 0.6.2
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.
|
@@ -184,6 +184,26 @@ function attachmentsOf(content) {
|
|
|
184
184
|
}
|
|
185
185
|
return out.length ? out : void 0;
|
|
186
186
|
}
|
|
187
|
+
function toolLabel(t, v) {
|
|
188
|
+
const x = v ?? {};
|
|
189
|
+
if (typeof x.toolCallId !== "string") return;
|
|
190
|
+
const str = (y) => typeof y === "string" && y ? y : void 0;
|
|
191
|
+
const patch = {};
|
|
192
|
+
if (str(x.title)) patch.label = str(x.title);
|
|
193
|
+
if (str(x.kind)) patch.kind = str(x.kind);
|
|
194
|
+
if (str(x.path)) patch.path = str(x.path);
|
|
195
|
+
if (str(x.detail)) patch.detail = str(x.detail);
|
|
196
|
+
t.tools = (t.tools ?? []).map((y) => y.id === x.toolCallId ? { ...y, ...patch } : y);
|
|
197
|
+
}
|
|
198
|
+
function applyStoredCustom(t, m) {
|
|
199
|
+
const custom = m.metadata?.custom;
|
|
200
|
+
if (!Array.isArray(custom)) return;
|
|
201
|
+
for (const c of custom) {
|
|
202
|
+
if (!c || typeof c.name !== "string") continue;
|
|
203
|
+
if (c.name === "ghosty.tool") toolLabel(t, c.value);
|
|
204
|
+
else if (!NOT_IN_TURN.has(c.name)) t.custom = [...t.custom ?? [], c];
|
|
205
|
+
}
|
|
206
|
+
}
|
|
187
207
|
function messagesToTurns(messages) {
|
|
188
208
|
const turns = [];
|
|
189
209
|
let cur = null;
|
|
@@ -201,8 +221,6 @@ function messagesToTurns(messages) {
|
|
|
201
221
|
} else if (m.role === "assistant") {
|
|
202
222
|
const t = assistant();
|
|
203
223
|
if (!t.id && m.id) t.id = m.id;
|
|
204
|
-
const custom = m.metadata?.custom;
|
|
205
|
-
if (Array.isArray(custom) && custom.length) t.custom = [...t.custom ?? [], ...custom.filter((c) => c && typeof c.name === "string")];
|
|
206
224
|
const text = m.content ?? "";
|
|
207
225
|
if (text) t.text = t.text ? `${t.text}
|
|
208
226
|
|
|
@@ -211,10 +229,12 @@ ${text}` : text;
|
|
|
211
229
|
t.tools = [...t.tools ?? [], { id: c.id, title: c.function.name, status: "pending", args: c.function.arguments, detail: argsDetail(c.function.arguments) }];
|
|
212
230
|
if (t.text.trim() && t.cuts?.[t.cuts.length - 1] !== t.text.length) t.cuts = [...t.cuts ?? [], t.text.length];
|
|
213
231
|
}
|
|
232
|
+
applyStoredCustom(t, m);
|
|
214
233
|
} else if (m.role === "tool") {
|
|
215
234
|
const t = assistant(m.id);
|
|
216
235
|
const failed = Boolean(m.error);
|
|
217
236
|
t.tools = (t.tools ?? []).map((x) => x.id === m.toolCallId ? { ...x, status: failed ? "failed" : "completed" } : x);
|
|
237
|
+
applyStoredCustom(t, m);
|
|
218
238
|
} else if (m.role === "reasoning") {
|
|
219
239
|
const t = assistant(m.id);
|
|
220
240
|
t.thought = (t.thought ?? "") + textOf(m.content);
|
|
@@ -366,7 +386,8 @@ ${content}` : content };
|
|
|
366
386
|
function toHistory(v) {
|
|
367
387
|
if (!v || typeof v !== "object") return null;
|
|
368
388
|
const h = v;
|
|
369
|
-
|
|
389
|
+
const before = typeof h.before === "string" || typeof h.before === "number" ? String(h.before) : void 0;
|
|
390
|
+
return { hasMoreBefore: h.hasMoreBefore === true, ...before !== void 0 ? { before } : {} };
|
|
370
391
|
}
|
|
371
392
|
function finish(s0, e, now, failed = false) {
|
|
372
393
|
let s = { ...s0, busy: false, step: null, openText: null };
|
|
@@ -387,6 +408,8 @@ function finish(s0, e, now, failed = false) {
|
|
|
387
408
|
{ used: 0, size: 0, cost: 0 }
|
|
388
409
|
) : null;
|
|
389
410
|
const usage = tokens || costUsd !== void 0 ? { ...tokens ?? { used: 0, size: 0, cost: 0 }, ...costUsd !== void 0 ? { cost: costUsd } : {} } : null;
|
|
411
|
+
const cut = e.metadata?.interrupted;
|
|
412
|
+
s = { ...s, cutRun: cut && typeof cut.runId === "string" ? { runId: cut.runId, ...typeof cut.reason === "string" && cut.reason ? { reason: cut.reason } : {} } : null };
|
|
390
413
|
const t0 = s.turnStartedAt;
|
|
391
414
|
const seconds = t0 ? Math.max(1, Math.round((now - t0) / 1e3)) : void 0;
|
|
392
415
|
const turns = [...s.turns];
|
|
@@ -406,7 +429,7 @@ function finish(s0, e, now, failed = false) {
|
|
|
406
429
|
}
|
|
407
430
|
function beginSend(prev, text, attachments = [], now = Date.now()) {
|
|
408
431
|
const turns = text || attachments.length ? [...prev.turns, { role: "user", text, ...attachments.length ? { attachments } : {} }] : prev.turns;
|
|
409
|
-
return { ...prev, turns, busy: true, error: null, notice: null, turnStartedAt: now, effects: [{ type: "turn-start" }] };
|
|
432
|
+
return { ...prev, turns, busy: true, error: null, notice: null, cutRun: null, turnStartedAt: now, effects: [{ type: "turn-start" }] };
|
|
410
433
|
}
|
|
411
434
|
function beginSteer(prev, text) {
|
|
412
435
|
return { ...prev, turns: [...prev.turns, { role: "user", text }], notice: null, openText: null, effects: [] };
|
|
@@ -439,7 +462,33 @@ function markSubagent(prev, taskId, status) {
|
|
|
439
462
|
function prependHistory(prev, messages, page) {
|
|
440
463
|
const have = new Set(prev.turns.map((t) => t.id).filter(Boolean));
|
|
441
464
|
const older = messagesToTurns(messages).filter((t) => !t.id || !have.has(t.id));
|
|
442
|
-
|
|
465
|
+
let turns = prev.turns;
|
|
466
|
+
const tail = older[older.length - 1];
|
|
467
|
+
const head = turns[0];
|
|
468
|
+
if (tail?.role === "assistant" && head?.role === "assistant") {
|
|
469
|
+
older[older.length - 1] = joinTurns(tail, head);
|
|
470
|
+
turns = turns.slice(1);
|
|
471
|
+
}
|
|
472
|
+
const olderCount = Math.max(0, prev.olderCount + older.length - (turns === prev.turns ? 0 : 1));
|
|
473
|
+
return { ...prev, turns: [...older, ...turns], olderCount, history: page ? { ...page, ...page.before !== void 0 ? { before: String(page.before) } : {} } : page, effects: [] };
|
|
474
|
+
}
|
|
475
|
+
function cutsOf(a, b, shift) {
|
|
476
|
+
const bridge = a.text.trim() && b.tools?.length && !b.cuts?.length ? [a.text.length] : [];
|
|
477
|
+
const cuts = [...a.cuts ?? [], ...bridge, ...(b.cuts ?? []).map((c) => c + shift)];
|
|
478
|
+
return cuts.length ? { cuts } : {};
|
|
479
|
+
}
|
|
480
|
+
function joinTurns(a, b) {
|
|
481
|
+
const sep = a.text && b.text ? "\n\n" : "";
|
|
482
|
+
const shift = a.text.length + sep.length;
|
|
483
|
+
return {
|
|
484
|
+
...b,
|
|
485
|
+
...a.id ? { id: a.id } : {},
|
|
486
|
+
text: a.text + sep + b.text,
|
|
487
|
+
...a.tools || b.tools ? { tools: [...a.tools ?? [], ...b.tools ?? []] } : {},
|
|
488
|
+
...cutsOf(a, b, shift),
|
|
489
|
+
...a.custom || b.custom ? { custom: [...a.custom ?? [], ...b.custom ?? []] } : {},
|
|
490
|
+
...a.thought || b.thought ? { thought: (a.thought ?? "") + (b.thought ?? "") } : {}
|
|
491
|
+
};
|
|
443
492
|
}
|
|
444
493
|
function sendFailed(prev, message) {
|
|
445
494
|
return { ...prev, busy: false, turnStartedAt: 0, error: message, effects: [] };
|
package/dist/core/index.js
CHANGED
package/dist/core/types.d.ts
CHANGED
|
@@ -163,6 +163,14 @@ export interface PilotState {
|
|
|
163
163
|
olderCount: number;
|
|
164
164
|
/** El `ghosty.history` que acompaña al snapshot que viene. */
|
|
165
165
|
historyNext?: HistoryPage | null;
|
|
166
|
+
/**
|
|
167
|
+
* El último run se cortó del lado del servidor (deploy, caja caída): `RUN_FINISHED.metadata.interrupted`.
|
|
168
|
+
* `reason` ya viene en palabras de persona. Se ofrece «Continuar».
|
|
169
|
+
*/
|
|
170
|
+
cutRun?: {
|
|
171
|
+
runId: string;
|
|
172
|
+
reason?: string;
|
|
173
|
+
} | null;
|
|
166
174
|
/** Mensajes en espera de que termine el run vivo (steer rechazado). */
|
|
167
175
|
queued: QueuedMessage[];
|
|
168
176
|
/** Lo que estaba en espera cuando la persona detuvo: regresa a la caja (`n` cambia en cada vez). */
|
package/dist/react/index.js
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
toPermission,
|
|
32
32
|
toolName,
|
|
33
33
|
toolNames
|
|
34
|
-
} from "../chunks/chunk-
|
|
34
|
+
} from "../chunks/chunk-ZJ34IR2Z.js";
|
|
35
35
|
import {
|
|
36
36
|
PilotHttpError,
|
|
37
37
|
createAgUiTransport,
|
|
@@ -894,6 +894,8 @@ var STRINGS_ES = {
|
|
|
894
894
|
steps: { waking: "Despertando al agente\u2026", session: "Preparando la sesi\xF3n\u2026", compacting: "Resumiendo la conversaci\xF3n\u2026" },
|
|
895
895
|
loadOlder: "Cargar mensajes anteriores",
|
|
896
896
|
loadingOlder: "Cargando\u2026",
|
|
897
|
+
continueRun: "Continuar",
|
|
898
|
+
runCut: "La respuesta se cort\xF3 antes de terminar.",
|
|
897
899
|
queued: "Se manda cuando termine",
|
|
898
900
|
subagentsWorking: (n) => n === 1 ? "1 agente trabajando" : `${n} agentes trabajando`,
|
|
899
901
|
subagentsDone: (n) => n === 1 ? "1 agente termin\xF3" : `${n} agentes terminaron`,
|
|
@@ -967,6 +969,8 @@ var STRINGS_EN = {
|
|
|
967
969
|
steps: { waking: "Waking the agent\u2026", session: "Starting the session\u2026", compacting: "Compacting the conversation\u2026" },
|
|
968
970
|
loadOlder: "Load earlier messages",
|
|
969
971
|
loadingOlder: "Loading\u2026",
|
|
972
|
+
continueRun: "Continue",
|
|
973
|
+
runCut: "The answer was cut off before it finished.",
|
|
970
974
|
queued: "Sends when this finishes",
|
|
971
975
|
subagentsWorking: (n) => n === 1 ? "1 agent working" : `${n} agents working`,
|
|
972
976
|
subagentsDone: (n) => n === 1 ? "1 agent finished" : `${n} agents finished`,
|
|
@@ -2386,7 +2390,7 @@ function usePilot(transport, opts = {}) {
|
|
|
2386
2390
|
ctrl.current = c;
|
|
2387
2391
|
const steering = typeof input.forwardedProps?.steer === "string";
|
|
2388
2392
|
const userMsg = input.messages[0];
|
|
2389
|
-
if (!steering && (input.messages.length || input.resume?.length)) liveRun.current = input.runId;
|
|
2393
|
+
if (!steering && (input.messages.length || input.resume?.length || input.forwardedProps?.continueRun)) liveRun.current = input.runId;
|
|
2390
2394
|
optsRef.current.onSend?.(input);
|
|
2391
2395
|
let ended = false;
|
|
2392
2396
|
try {
|
|
@@ -2406,6 +2410,11 @@ function usePilot(transport, opts = {}) {
|
|
|
2406
2410
|
} catch (err) {
|
|
2407
2411
|
if (c.signal.aborted) return;
|
|
2408
2412
|
const code = err instanceof PilotHttpError ? err.code : void 0;
|
|
2413
|
+
if (code === "nothing_to_continue") {
|
|
2414
|
+
liveRun.current = null;
|
|
2415
|
+
commit({ ...ref.current, busy: false, turnStartedAt: 0, cutRun: null, effects: [] });
|
|
2416
|
+
return;
|
|
2417
|
+
}
|
|
2409
2418
|
if ((code === "run_active" || code === "interrupt_pending") && !reopening) {
|
|
2410
2419
|
if (userMsg) {
|
|
2411
2420
|
const { text, attachments } = fromUserMessage(userMsg.content);
|
|
@@ -2571,10 +2580,16 @@ function usePilot(transport, opts = {}) {
|
|
|
2571
2580
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2572
2581
|
[commit, runNow]
|
|
2573
2582
|
);
|
|
2583
|
+
const continueRun = useCallback2(async () => {
|
|
2584
|
+
const cut = ref.current.cutRun;
|
|
2585
|
+
if (!cut || ref.current.busy || !transportRef.current) return;
|
|
2586
|
+
commit(beginSend(ref.current, "", []));
|
|
2587
|
+
await runNow({ messages: [], forwardedProps: { continueRun: cut.runId } });
|
|
2588
|
+
}, [commit, runNow]);
|
|
2574
2589
|
const refresh = useCallback2(() => runNow({ messages: [] }, true), [runNow]);
|
|
2575
2590
|
const canSteer = Boolean(transport) && (opts.steer ?? state.agentState.caps?.steer === true);
|
|
2576
2591
|
const subagents = useMemo3(() => subagentsOf(state.agentState), [state.agentState]);
|
|
2577
|
-
return { state, threadId, send, stop, retry, respondPermission, recordDecision, expirePermission, canSteer, loadOlder, loadingOlder, subagents, stopSubagent, refresh };
|
|
2592
|
+
return { state, threadId, send, stop, retry, respondPermission, recordDecision, expirePermission, canSteer, loadOlder, loadingOlder, subagents, stopSubagent, refresh, continueRun };
|
|
2578
2593
|
}
|
|
2579
2594
|
|
|
2580
2595
|
// src/react/Piloto.tsx
|
|
@@ -2893,6 +2908,19 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2893
2908
|
children: /* @__PURE__ */ jsx10(ArrowDown, { className: "h-4 w-4" })
|
|
2894
2909
|
}
|
|
2895
2910
|
),
|
|
2911
|
+
state.cutRun && !busy && // El servidor cortó la respuesta (deploy, caja caída): la persona decide si sigue.
|
|
2912
|
+
/* @__PURE__ */ jsxs8("div", { "data-cut-run": true, role: "status", className: "mb-2 flex items-center gap-3 rounded-2xl border border-border-primary bg-background-secondary px-3 py-2 text-sm text-text-secondary", children: [
|
|
2913
|
+
/* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1", children: state.cutRun.reason ?? t.runCut }),
|
|
2914
|
+
/* @__PURE__ */ jsx10(
|
|
2915
|
+
"button",
|
|
2916
|
+
{
|
|
2917
|
+
type: "button",
|
|
2918
|
+
onClick: () => void pilot.continueRun(),
|
|
2919
|
+
className: "shrink-0 rounded-full bg-brand-500 px-3 py-1 text-sm font-medium text-white transition hover:bg-brand-600",
|
|
2920
|
+
children: t.continueRun
|
|
2921
|
+
}
|
|
2922
|
+
)
|
|
2923
|
+
] }),
|
|
2896
2924
|
aboveComposer,
|
|
2897
2925
|
composer
|
|
2898
2926
|
] })
|
package/dist/react/strings.d.ts
CHANGED
|
@@ -58,6 +58,10 @@ export interface PilotStrings {
|
|
|
58
58
|
steps: Record<string, string>;
|
|
59
59
|
loadOlder: string;
|
|
60
60
|
loadingOlder: string;
|
|
61
|
+
/** El botón de la franja de un run cortado por el servidor (mismo rótulo en iOS y Android). */
|
|
62
|
+
continueRun: string;
|
|
63
|
+
/** La franja, si el servidor no dio razón. */
|
|
64
|
+
runCut: string;
|
|
61
65
|
/** Bajo un mensaje que espera a que termine el run vivo. */
|
|
62
66
|
queued: string;
|
|
63
67
|
/** «2 agentes trabajando». */
|
package/dist/react/usePilot.d.ts
CHANGED
|
@@ -82,6 +82,8 @@ export interface Pilot {
|
|
|
82
82
|
* por fuera que algo cambió (p. ej. el bus `/me/events` avisa que un subagente terminó).
|
|
83
83
|
*/
|
|
84
84
|
refresh: () => Promise<void>;
|
|
85
|
+
/** Continúa el run que el servidor cortó (`state.cutRun`): `forwardedProps.continueRun`. */
|
|
86
|
+
continueRun: () => Promise<void>;
|
|
85
87
|
}
|
|
86
88
|
/** El contexto de la app como `context` de AG-UI: `[{description, value}]`, `value` siempre texto. */
|
|
87
89
|
export declare function toAgUiContext(ctx: unknown): RunAgentInput["context"];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ghostykit",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
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",
|