ghostykit 0.6.1 → 0.6.3
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.
|
@@ -116,7 +116,8 @@ function createPilotState(turns = []) {
|
|
|
116
116
|
effects: []
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
|
-
var NOT_IN_TURN = /* @__PURE__ */ new Set(["ghosty.title", "ghosty.injected", "ghosty.history", "ghosty.tool", "ghosty.replace", "ghosty.page"]);
|
|
119
|
+
var NOT_IN_TURN = /* @__PURE__ */ new Set(["ghosty.title", "ghosty.injected", "ghosty.history", "ghosty.tool", "ghosty.replace", "ghosty.page", "ghosty.stalled"]);
|
|
120
|
+
var PROGRESS = /* @__PURE__ */ new Set(["TEXT_MESSAGE_START", "TEXT_MESSAGE_CONTENT", "TEXT_MESSAGE_CHUNK", "TOOL_CALL_START", "TOOL_CALL_ARGS", "TOOL_CALL_CHUNK", "TOOL_CALL_RESULT", "REASONING_MESSAGE_CONTENT", "REASONING_MESSAGE_CHUNK", "RUN_STARTED"]);
|
|
120
121
|
function patchAssistant(s, patch) {
|
|
121
122
|
const turns = [...s.turns];
|
|
122
123
|
const last = turns[turns.length - 1];
|
|
@@ -283,7 +284,8 @@ function applySnapshot(s, messages) {
|
|
|
283
284
|
};
|
|
284
285
|
}
|
|
285
286
|
function reduceAgUiEvent(prev, e, now = Date.now()) {
|
|
286
|
-
|
|
287
|
+
let s = prev.effects.length ? { ...prev, effects: [] } : prev;
|
|
288
|
+
if (s.stalled && PROGRESS.has(e.type)) s = { ...s, stalled: null };
|
|
287
289
|
switch (e.type) {
|
|
288
290
|
case "RUN_STARTED":
|
|
289
291
|
return { ...s, busy: true, error: null, step: null, runId: e.runId ?? s.runId };
|
|
@@ -355,6 +357,10 @@ function reduceAgUiEvent(prev, e, now = Date.now()) {
|
|
|
355
357
|
case "CUSTOM":
|
|
356
358
|
if (e.name === "ghosty.title") return { ...s, effects: [{ type: "title", title: e.value?.title }] };
|
|
357
359
|
if (e.name === "ghosty.history") return { ...s, historyNext: toHistory(e.value) };
|
|
360
|
+
if (e.name === "ghosty.stalled") {
|
|
361
|
+
const message = e.value?.message;
|
|
362
|
+
return { ...s, stalled: typeof message === "string" && message ? { message } : {} };
|
|
363
|
+
}
|
|
358
364
|
if (e.name === "ghosty.tool") {
|
|
359
365
|
const v = e.value ?? {};
|
|
360
366
|
if (typeof v.toolCallId !== "string") return s;
|
|
@@ -390,7 +396,7 @@ function toHistory(v) {
|
|
|
390
396
|
return { hasMoreBefore: h.hasMoreBefore === true, ...before !== void 0 ? { before } : {} };
|
|
391
397
|
}
|
|
392
398
|
function finish(s0, e, now, failed = false) {
|
|
393
|
-
let s = { ...s0, busy: false, step: null, openText: null };
|
|
399
|
+
let s = { ...s0, busy: false, step: null, openText: null, stalled: null };
|
|
394
400
|
const outcome = e.outcome;
|
|
395
401
|
const interrupted = outcome?.type === "interrupt" && Array.isArray(outcome.interrupts) && outcome.interrupts.length > 0;
|
|
396
402
|
const clockOffset = typeof e.timestamp === "number" ? now - e.timestamp : 0;
|
|
@@ -408,11 +414,15 @@ function finish(s0, e, now, failed = false) {
|
|
|
408
414
|
{ used: 0, size: 0, cost: 0 }
|
|
409
415
|
) : null;
|
|
410
416
|
const usage = tokens || costUsd !== void 0 ? { ...tokens ?? { used: 0, size: 0, cost: 0 }, ...costUsd !== void 0 ? { cost: costUsd } : {} } : null;
|
|
417
|
+
const cut = e.metadata?.interrupted;
|
|
418
|
+
s = { ...s, cutRun: cut && typeof cut.runId === "string" ? { runId: cut.runId, ...typeof cut.reason === "string" && cut.reason ? { reason: cut.reason } : {} } : null };
|
|
411
419
|
const t0 = s.turnStartedAt;
|
|
412
420
|
const seconds = t0 ? Math.max(1, Math.round((now - t0) / 1e3)) : void 0;
|
|
413
421
|
const turns = [...s.turns];
|
|
414
422
|
const last = turns[turns.length - 1];
|
|
415
|
-
|
|
423
|
+
const tr = e.metadata?.truncated;
|
|
424
|
+
const truncated = tr ? { ...typeof tr.reason === "string" ? { reason: tr.reason } : {} } : void 0;
|
|
425
|
+
if (last?.role === "assistant") turns[turns.length - 1] = { ...last, ...seconds !== void 0 ? { seconds } : {}, ...usage ? { usage } : {}, ...truncated ? { truncated } : {} };
|
|
416
426
|
s = { ...s, turns, turnStartedAt: 0, ...usage ? { usage } : {} };
|
|
417
427
|
if (!t0) return s;
|
|
418
428
|
const effect = {
|
|
@@ -427,7 +437,7 @@ function finish(s0, e, now, failed = false) {
|
|
|
427
437
|
}
|
|
428
438
|
function beginSend(prev, text, attachments = [], now = Date.now()) {
|
|
429
439
|
const turns = text || attachments.length ? [...prev.turns, { role: "user", text, ...attachments.length ? { attachments } : {} }] : prev.turns;
|
|
430
|
-
return { ...prev, turns, busy: true, error: null, notice: null, turnStartedAt: now, effects: [{ type: "turn-start" }] };
|
|
440
|
+
return { ...prev, turns, busy: true, error: null, notice: null, cutRun: null, turnStartedAt: now, effects: [{ type: "turn-start" }] };
|
|
431
441
|
}
|
|
432
442
|
function beginSteer(prev, text) {
|
|
433
443
|
return { ...prev, turns: [...prev.turns, { role: "user", text }], notice: null, openText: null, effects: [] };
|
package/dist/core/index.js
CHANGED
package/dist/core/types.d.ts
CHANGED
|
@@ -95,6 +95,10 @@ export interface Turn {
|
|
|
95
95
|
usage?: Usage;
|
|
96
96
|
/** Lo que el usuario decidió en las tarjetas de este turno (sólo en vivo). */
|
|
97
97
|
decisions?: Decision[];
|
|
98
|
+
/** El servidor recortó la respuesta (`RUN_FINISHED.metadata.truncated`); `reason` si la dio. */
|
|
99
|
+
truncated?: {
|
|
100
|
+
reason?: string;
|
|
101
|
+
};
|
|
98
102
|
/** Los `CUSTOM` del turno (p. ej. `ghosty.delivery`), para `renderDeliveries`. Del snapshot salen
|
|
99
103
|
* del campo `custom` de cada mensaje del asistente. */
|
|
100
104
|
custom?: CustomEntry[];
|
|
@@ -163,6 +167,18 @@ export interface PilotState {
|
|
|
163
167
|
olderCount: number;
|
|
164
168
|
/** El `ghosty.history` que acompaña al snapshot que viene. */
|
|
165
169
|
historyNext?: HistoryPage | null;
|
|
170
|
+
/**
|
|
171
|
+
* El último run se cortó del lado del servidor (deploy, caja caída): `RUN_FINISHED.metadata.interrupted`.
|
|
172
|
+
* `reason` ya viene en palabras de persona. Se ofrece «Continuar».
|
|
173
|
+
*/
|
|
174
|
+
cutRun?: {
|
|
175
|
+
runId: string;
|
|
176
|
+
reason?: string;
|
|
177
|
+
} | null;
|
|
178
|
+
/** El agente se atoró y el servidor sigue intentando (`CUSTOM ghosty.stalled`); se quita con lo siguiente que llegue. */
|
|
179
|
+
stalled?: {
|
|
180
|
+
message?: string;
|
|
181
|
+
} | null;
|
|
166
182
|
/** Mensajes en espera de que termine el run vivo (steer rechazado). */
|
|
167
183
|
queued: QueuedMessage[];
|
|
168
184
|
/** 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-LFFJLMDO.js";
|
|
35
35
|
import {
|
|
36
36
|
PilotHttpError,
|
|
37
37
|
createAgUiTransport,
|
|
@@ -894,6 +894,11 @@ 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.",
|
|
899
|
+
stalled: "Se ator\xF3, sigo intentando\u2026",
|
|
900
|
+
truncated: "La respuesta qued\xF3 incompleta.",
|
|
901
|
+
attachmentFailed: (name, reason) => reason ? `No pude leer ${name}: ${reason}` : `No pude leer ${name}`,
|
|
897
902
|
queued: "Se manda cuando termine",
|
|
898
903
|
subagentsWorking: (n) => n === 1 ? "1 agente trabajando" : `${n} agentes trabajando`,
|
|
899
904
|
subagentsDone: (n) => n === 1 ? "1 agente termin\xF3" : `${n} agentes terminaron`,
|
|
@@ -967,6 +972,11 @@ var STRINGS_EN = {
|
|
|
967
972
|
steps: { waking: "Waking the agent\u2026", session: "Starting the session\u2026", compacting: "Compacting the conversation\u2026" },
|
|
968
973
|
loadOlder: "Load earlier messages",
|
|
969
974
|
loadingOlder: "Loading\u2026",
|
|
975
|
+
continueRun: "Continue",
|
|
976
|
+
runCut: "The answer was cut off before it finished.",
|
|
977
|
+
stalled: "Got stuck, still trying\u2026",
|
|
978
|
+
truncated: "The answer is incomplete.",
|
|
979
|
+
attachmentFailed: (name, reason) => reason ? `Couldn't read ${name}: ${reason}` : `Couldn't read ${name}`,
|
|
970
980
|
queued: "Sends when this finishes",
|
|
971
981
|
subagentsWorking: (n) => n === 1 ? "1 agent working" : `${n} agents working`,
|
|
972
982
|
subagentsDone: (n) => n === 1 ? "1 agent finished" : `${n} agents finished`,
|
|
@@ -2384,9 +2394,9 @@ function usePilot(transport, opts = {}) {
|
|
|
2384
2394
|
ctrl.current?.abort();
|
|
2385
2395
|
const c = new AbortController();
|
|
2386
2396
|
ctrl.current = c;
|
|
2387
|
-
const steering =
|
|
2397
|
+
const steering = input.forwardedProps?.steer !== void 0 && input.forwardedProps?.steer !== false;
|
|
2388
2398
|
const userMsg = input.messages[0];
|
|
2389
|
-
if (!steering && (input.messages.length || input.resume?.length)) liveRun.current = input.runId;
|
|
2399
|
+
if (!steering && (input.messages.length || input.resume?.length || input.forwardedProps?.continueRun)) liveRun.current = input.runId;
|
|
2390
2400
|
optsRef.current.onSend?.(input);
|
|
2391
2401
|
let ended = false;
|
|
2392
2402
|
try {
|
|
@@ -2406,6 +2416,23 @@ function usePilot(transport, opts = {}) {
|
|
|
2406
2416
|
} catch (err) {
|
|
2407
2417
|
if (c.signal.aborted) return;
|
|
2408
2418
|
const code = err instanceof PilotHttpError ? err.code : void 0;
|
|
2419
|
+
if (code === "run_not_live" && steering && userMsg) {
|
|
2420
|
+
const { text } = fromUserMessage(userMsg.content);
|
|
2421
|
+
let s = ref.current;
|
|
2422
|
+
for (let i = s.turns.length - 1; i >= 0; i--) {
|
|
2423
|
+
if (s.turns[i].role === "user" && s.turns[i].text === text) {
|
|
2424
|
+
s = { ...s, turns: [...s.turns.slice(0, i), ...s.turns.slice(i + 1)] };
|
|
2425
|
+
break;
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
commit({ ...s, busy: false, turnStartedAt: 0, effects: [] });
|
|
2429
|
+
return sendNew(text, []);
|
|
2430
|
+
}
|
|
2431
|
+
if (code === "nothing_to_continue") {
|
|
2432
|
+
liveRun.current = null;
|
|
2433
|
+
commit({ ...ref.current, busy: false, turnStartedAt: 0, cutRun: null, effects: [] });
|
|
2434
|
+
return;
|
|
2435
|
+
}
|
|
2409
2436
|
if ((code === "run_active" || code === "interrupt_pending") && !reopening) {
|
|
2410
2437
|
if (userMsg) {
|
|
2411
2438
|
const { text, attachments } = fromUserMessage(userMsg.content);
|
|
@@ -2452,8 +2479,8 @@ function usePilot(transport, opts = {}) {
|
|
|
2452
2479
|
async (text, attachments = [], _extra = {}) => {
|
|
2453
2480
|
if (!transportRef.current) return;
|
|
2454
2481
|
if (ref.current.busy) {
|
|
2455
|
-
const live = liveRun.current ?? ref.current.runId;
|
|
2456
|
-
if (!steerOn() || !
|
|
2482
|
+
const live = liveRun.current ?? ref.current.runId ?? true;
|
|
2483
|
+
if (!steerOn() || !text.trim() || attachments.length) return;
|
|
2457
2484
|
commit(beginSteer(ref.current, text));
|
|
2458
2485
|
await runNow({ messages: [userMessage(text, [])], forwardedProps: { steer: live } });
|
|
2459
2486
|
return;
|
|
@@ -2571,10 +2598,16 @@ function usePilot(transport, opts = {}) {
|
|
|
2571
2598
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2572
2599
|
[commit, runNow]
|
|
2573
2600
|
);
|
|
2601
|
+
const continueRun = useCallback2(async () => {
|
|
2602
|
+
const cut = ref.current.cutRun;
|
|
2603
|
+
if (!cut || ref.current.busy || !transportRef.current) return;
|
|
2604
|
+
commit(beginSend(ref.current, "", []));
|
|
2605
|
+
await runNow({ messages: [], forwardedProps: { continueRun: cut.runId } });
|
|
2606
|
+
}, [commit, runNow]);
|
|
2574
2607
|
const refresh = useCallback2(() => runNow({ messages: [] }, true), [runNow]);
|
|
2575
2608
|
const canSteer = Boolean(transport) && (opts.steer ?? state.agentState.caps?.steer === true);
|
|
2576
2609
|
const subagents = useMemo3(() => subagentsOf(state.agentState), [state.agentState]);
|
|
2577
|
-
return { state, threadId, send, stop, retry, respondPermission, recordDecision, expirePermission, canSteer, loadOlder, loadingOlder, subagents, stopSubagent, refresh };
|
|
2610
|
+
return { state, threadId, send, stop, retry, respondPermission, recordDecision, expirePermission, canSteer, loadOlder, loadingOlder, subagents, stopSubagent, refresh, continueRun };
|
|
2578
2611
|
}
|
|
2579
2612
|
|
|
2580
2613
|
// src/react/Piloto.tsx
|
|
@@ -2849,20 +2882,24 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2849
2882
|
turns.map((turn, i) => {
|
|
2850
2883
|
const isLast = i === turns.length - 1;
|
|
2851
2884
|
const live = busy && isLast;
|
|
2852
|
-
return /* @__PURE__ */
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2885
|
+
return /* @__PURE__ */ jsxs8("div", { "data-turn": i - olderCount, children: [
|
|
2886
|
+
turn.role === "user" ? /* @__PURE__ */ jsx10(UserBubble, { turn }) : /* @__PURE__ */ jsx10(
|
|
2887
|
+
AgentBubble,
|
|
2888
|
+
{
|
|
2889
|
+
turn,
|
|
2890
|
+
live,
|
|
2891
|
+
onRetry: isLast && features.retry !== false ? pilot.retry : void 0,
|
|
2892
|
+
onOption: isLast && !busy && features.replyOptions !== false ? (text) => send(text) : void 0,
|
|
2893
|
+
showUsage: features.usage !== false,
|
|
2894
|
+
showCopy: features.copy !== false,
|
|
2895
|
+
showDuration: features.duration !== false,
|
|
2896
|
+
renderDeliveries: slots.renderDeliveries
|
|
2897
|
+
}
|
|
2898
|
+
),
|
|
2899
|
+
turn.role === "assistant" && /* @__PURE__ */ jsx10(TurnNotes, { turn })
|
|
2900
|
+
] }, i - olderCount);
|
|
2865
2901
|
}),
|
|
2902
|
+
busy && state.stalled && /* @__PURE__ */ jsx10("p", { className: "text-sm text-text-tertiary", children: state.stalled.message ?? t.stalled }),
|
|
2866
2903
|
state.queued.map((q, i) => (
|
|
2867
2904
|
// Esperan a que termine el run vivo (el servidor no los dejó entrar): salen solos.
|
|
2868
2905
|
/* @__PURE__ */ jsxs8("div", { "data-queued": true, className: "flex flex-col items-end gap-1 opacity-60", children: [
|
|
@@ -2893,11 +2930,33 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2893
2930
|
children: /* @__PURE__ */ jsx10(ArrowDown, { className: "h-4 w-4" })
|
|
2894
2931
|
}
|
|
2895
2932
|
),
|
|
2933
|
+
state.cutRun && !busy && // El servidor cortó la respuesta (deploy, caja caída): la persona decide si sigue.
|
|
2934
|
+
/* @__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: [
|
|
2935
|
+
/* @__PURE__ */ jsx10("span", { className: "min-w-0 flex-1", children: state.cutRun.reason ?? t.runCut }),
|
|
2936
|
+
/* @__PURE__ */ jsx10(
|
|
2937
|
+
"button",
|
|
2938
|
+
{
|
|
2939
|
+
type: "button",
|
|
2940
|
+
onClick: () => void pilot.continueRun(),
|
|
2941
|
+
className: "shrink-0 rounded-full bg-brand-500 px-3 py-1 text-sm font-medium text-white transition hover:bg-brand-600",
|
|
2942
|
+
children: t.continueRun
|
|
2943
|
+
}
|
|
2944
|
+
)
|
|
2945
|
+
] }),
|
|
2896
2946
|
aboveComposer,
|
|
2897
2947
|
composer
|
|
2898
2948
|
] })
|
|
2899
2949
|
] })) }) });
|
|
2900
2950
|
});
|
|
2951
|
+
function TurnNotes({ turn }) {
|
|
2952
|
+
const { strings: t } = usePiloto();
|
|
2953
|
+
const failed = (turn.custom ?? []).filter((c) => c.name === "ghosty.attachments_failed").flatMap((c) => (c.value?.files ?? []).filter((f) => f?.name));
|
|
2954
|
+
if (!failed.length && !turn.truncated) return null;
|
|
2955
|
+
return /* @__PURE__ */ jsxs8("div", { "data-turn-notes": true, className: "mt-1 flex flex-col gap-0.5 text-xs text-text-tertiary", children: [
|
|
2956
|
+
failed.map((f, i) => /* @__PURE__ */ jsx10("p", { children: t.attachmentFailed(f.name, f.reason) }, i)),
|
|
2957
|
+
turn.truncated && /* @__PURE__ */ jsx10("p", { children: turn.truncated.reason ?? t.truncated })
|
|
2958
|
+
] });
|
|
2959
|
+
}
|
|
2901
2960
|
|
|
2902
2961
|
// src/react/Subagents.tsx
|
|
2903
2962
|
import { useState as useState10 } from "react";
|
package/dist/react/strings.d.ts
CHANGED
|
@@ -58,6 +58,16 @@ 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;
|
|
65
|
+
/** `ghosty.stalled` sin mensaje propio. */
|
|
66
|
+
stalled: string;
|
|
67
|
+
/** Bajo una respuesta recortada por el servidor. */
|
|
68
|
+
truncated: string;
|
|
69
|
+
/** Un adjunto que el agente no pudo leer: nombre y razón. */
|
|
70
|
+
attachmentFailed: (name: string, reason?: string) => string;
|
|
61
71
|
/** Bajo un mensaje que espera a que termine el run vivo. */
|
|
62
72
|
queued: string;
|
|
63
73
|
/** «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.3",
|
|
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",
|