ghostykit 0.6.2 → 0.6.4
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];
|
|
@@ -192,7 +193,7 @@ function toolLabel(t, v) {
|
|
|
192
193
|
if (str(x.title)) patch.label = str(x.title);
|
|
193
194
|
if (str(x.kind)) patch.kind = str(x.kind);
|
|
194
195
|
if (str(x.path)) patch.path = str(x.path);
|
|
195
|
-
if (str(x.detail)) patch
|
|
196
|
+
if (str(x.detail)) Object.assign(patch, { detail: str(x.detail), detailFromServer: true });
|
|
196
197
|
t.tools = (t.tools ?? []).map((y) => y.id === x.toolCallId ? { ...y, ...patch } : y);
|
|
197
198
|
}
|
|
198
199
|
function applyStoredCustom(t, m) {
|
|
@@ -258,7 +259,8 @@ function applySnapshot(s, messages) {
|
|
|
258
259
|
const tools = t.tools?.map((x) => {
|
|
259
260
|
const prev = o?.role === t.role ? o.tools?.find((y) => y.id === x.id) : void 0;
|
|
260
261
|
const status = x.status === "pending" && (prev?.status ?? (live ? "in_progress" : void 0));
|
|
261
|
-
|
|
262
|
+
const keepDetail = prev?.detailFromServer && !x.detailFromServer ? { detail: prev.detail, detailFromServer: true } : {};
|
|
263
|
+
return { ...prev, ...x, ...status ? { status } : {}, ...keepDetail };
|
|
262
264
|
});
|
|
263
265
|
const base = tools ? { ...t, tools } : t;
|
|
264
266
|
if (!o || o.role !== t.role) return base;
|
|
@@ -283,7 +285,8 @@ function applySnapshot(s, messages) {
|
|
|
283
285
|
};
|
|
284
286
|
}
|
|
285
287
|
function reduceAgUiEvent(prev, e, now = Date.now()) {
|
|
286
|
-
|
|
288
|
+
let s = prev.effects.length ? { ...prev, effects: [] } : prev;
|
|
289
|
+
if (s.stalled && PROGRESS.has(e.type)) s = { ...s, stalled: null };
|
|
287
290
|
switch (e.type) {
|
|
288
291
|
case "RUN_STARTED":
|
|
289
292
|
return { ...s, busy: true, error: null, step: null, runId: e.runId ?? s.runId };
|
|
@@ -323,6 +326,7 @@ function reduceAgUiEvent(prev, e, now = Date.now()) {
|
|
|
323
326
|
}
|
|
324
327
|
case "TOOL_CALL_END": {
|
|
325
328
|
const known = s.turns[s.turns.length - 1]?.tools?.find((x) => x.id === e.toolCallId);
|
|
329
|
+
if (known?.detailFromServer) return s;
|
|
326
330
|
return upsertTool(s, e.toolCallId, { detail: argsDetail(known?.args) });
|
|
327
331
|
}
|
|
328
332
|
case "TOOL_CALL_RESULT": {
|
|
@@ -355,6 +359,10 @@ function reduceAgUiEvent(prev, e, now = Date.now()) {
|
|
|
355
359
|
case "CUSTOM":
|
|
356
360
|
if (e.name === "ghosty.title") return { ...s, effects: [{ type: "title", title: e.value?.title }] };
|
|
357
361
|
if (e.name === "ghosty.history") return { ...s, historyNext: toHistory(e.value) };
|
|
362
|
+
if (e.name === "ghosty.stalled") {
|
|
363
|
+
const message = e.value?.message;
|
|
364
|
+
return { ...s, stalled: typeof message === "string" && message ? { message } : {} };
|
|
365
|
+
}
|
|
358
366
|
if (e.name === "ghosty.tool") {
|
|
359
367
|
const v = e.value ?? {};
|
|
360
368
|
if (typeof v.toolCallId !== "string") return s;
|
|
@@ -363,7 +371,7 @@ function reduceAgUiEvent(prev, e, now = Date.now()) {
|
|
|
363
371
|
if (str(v.title)) patch.label = str(v.title);
|
|
364
372
|
if (str(v.kind)) patch.kind = str(v.kind);
|
|
365
373
|
if (str(v.path)) patch.path = str(v.path);
|
|
366
|
-
if (str(v.detail)) patch
|
|
374
|
+
if (str(v.detail)) Object.assign(patch, { detail: str(v.detail), detailFromServer: true });
|
|
367
375
|
return upsertTool(s, v.toolCallId, patch);
|
|
368
376
|
}
|
|
369
377
|
if (e.name === "ghosty.replace") {
|
|
@@ -390,7 +398,7 @@ function toHistory(v) {
|
|
|
390
398
|
return { hasMoreBefore: h.hasMoreBefore === true, ...before !== void 0 ? { before } : {} };
|
|
391
399
|
}
|
|
392
400
|
function finish(s0, e, now, failed = false) {
|
|
393
|
-
let s = { ...s0, busy: false, step: null, openText: null };
|
|
401
|
+
let s = { ...s0, busy: false, step: null, openText: null, stalled: null };
|
|
394
402
|
const outcome = e.outcome;
|
|
395
403
|
const interrupted = outcome?.type === "interrupt" && Array.isArray(outcome.interrupts) && outcome.interrupts.length > 0;
|
|
396
404
|
const clockOffset = typeof e.timestamp === "number" ? now - e.timestamp : 0;
|
|
@@ -414,7 +422,9 @@ function finish(s0, e, now, failed = false) {
|
|
|
414
422
|
const seconds = t0 ? Math.max(1, Math.round((now - t0) / 1e3)) : void 0;
|
|
415
423
|
const turns = [...s.turns];
|
|
416
424
|
const last = turns[turns.length - 1];
|
|
417
|
-
|
|
425
|
+
const tr = e.metadata?.truncated;
|
|
426
|
+
const truncated = tr ? { ...typeof tr.reason === "string" ? { reason: tr.reason } : {} } : void 0;
|
|
427
|
+
if (last?.role === "assistant") turns[turns.length - 1] = { ...last, ...seconds !== void 0 ? { seconds } : {}, ...usage ? { usage } : {}, ...truncated ? { truncated } : {} };
|
|
418
428
|
s = { ...s, turns, turnStartedAt: 0, ...usage ? { usage } : {} };
|
|
419
429
|
if (!t0) return s;
|
|
420
430
|
const effect = {
|
package/dist/core/index.js
CHANGED
package/dist/core/types.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface ToolEntry {
|
|
|
23
23
|
path?: string;
|
|
24
24
|
/** Los argumentos, en corto («job: purge-notifications»). */
|
|
25
25
|
detail?: string;
|
|
26
|
+
/** `detail` lo puso el servidor (`ghosty.tool`): ya no se deriva de los argumentos. */
|
|
27
|
+
detailFromServer?: boolean;
|
|
26
28
|
/** Los argumentos en JSON tal como llegaron (TOOL_CALL_ARGS). */
|
|
27
29
|
args?: string;
|
|
28
30
|
}
|
|
@@ -95,6 +97,10 @@ export interface Turn {
|
|
|
95
97
|
usage?: Usage;
|
|
96
98
|
/** Lo que el usuario decidió en las tarjetas de este turno (sólo en vivo). */
|
|
97
99
|
decisions?: Decision[];
|
|
100
|
+
/** El servidor recortó la respuesta (`RUN_FINISHED.metadata.truncated`); `reason` si la dio. */
|
|
101
|
+
truncated?: {
|
|
102
|
+
reason?: string;
|
|
103
|
+
};
|
|
98
104
|
/** Los `CUSTOM` del turno (p. ej. `ghosty.delivery`), para `renderDeliveries`. Del snapshot salen
|
|
99
105
|
* del campo `custom` de cada mensaje del asistente. */
|
|
100
106
|
custom?: CustomEntry[];
|
|
@@ -171,6 +177,10 @@ export interface PilotState {
|
|
|
171
177
|
runId: string;
|
|
172
178
|
reason?: string;
|
|
173
179
|
} | null;
|
|
180
|
+
/** El agente se atoró y el servidor sigue intentando (`CUSTOM ghosty.stalled`); se quita con lo siguiente que llegue. */
|
|
181
|
+
stalled?: {
|
|
182
|
+
message?: string;
|
|
183
|
+
} | null;
|
|
174
184
|
/** Mensajes en espera de que termine el run vivo (steer rechazado). */
|
|
175
185
|
queued: QueuedMessage[];
|
|
176
186
|
/** 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-LPPND5ME.js";
|
|
35
35
|
import {
|
|
36
36
|
PilotHttpError,
|
|
37
37
|
createAgUiTransport,
|
|
@@ -896,6 +896,9 @@ var STRINGS_ES = {
|
|
|
896
896
|
loadingOlder: "Cargando\u2026",
|
|
897
897
|
continueRun: "Continuar",
|
|
898
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}`,
|
|
899
902
|
queued: "Se manda cuando termine",
|
|
900
903
|
subagentsWorking: (n) => n === 1 ? "1 agente trabajando" : `${n} agentes trabajando`,
|
|
901
904
|
subagentsDone: (n) => n === 1 ? "1 agente termin\xF3" : `${n} agentes terminaron`,
|
|
@@ -971,6 +974,9 @@ var STRINGS_EN = {
|
|
|
971
974
|
loadingOlder: "Loading\u2026",
|
|
972
975
|
continueRun: "Continue",
|
|
973
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}`,
|
|
974
980
|
queued: "Sends when this finishes",
|
|
975
981
|
subagentsWorking: (n) => n === 1 ? "1 agent working" : `${n} agents working`,
|
|
976
982
|
subagentsDone: (n) => n === 1 ? "1 agent finished" : `${n} agents finished`,
|
|
@@ -2388,7 +2394,7 @@ function usePilot(transport, opts = {}) {
|
|
|
2388
2394
|
ctrl.current?.abort();
|
|
2389
2395
|
const c = new AbortController();
|
|
2390
2396
|
ctrl.current = c;
|
|
2391
|
-
const steering =
|
|
2397
|
+
const steering = input.forwardedProps?.steer !== void 0 && input.forwardedProps?.steer !== false;
|
|
2392
2398
|
const userMsg = input.messages[0];
|
|
2393
2399
|
if (!steering && (input.messages.length || input.resume?.length || input.forwardedProps?.continueRun)) liveRun.current = input.runId;
|
|
2394
2400
|
optsRef.current.onSend?.(input);
|
|
@@ -2410,6 +2416,18 @@ function usePilot(transport, opts = {}) {
|
|
|
2410
2416
|
} catch (err) {
|
|
2411
2417
|
if (c.signal.aborted) return;
|
|
2412
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
|
+
}
|
|
2413
2431
|
if (code === "nothing_to_continue") {
|
|
2414
2432
|
liveRun.current = null;
|
|
2415
2433
|
commit({ ...ref.current, busy: false, turnStartedAt: 0, cutRun: null, effects: [] });
|
|
@@ -2461,8 +2479,8 @@ function usePilot(transport, opts = {}) {
|
|
|
2461
2479
|
async (text, attachments = [], _extra = {}) => {
|
|
2462
2480
|
if (!transportRef.current) return;
|
|
2463
2481
|
if (ref.current.busy) {
|
|
2464
|
-
const live = liveRun.current ?? ref.current.runId;
|
|
2465
|
-
if (!steerOn() || !
|
|
2482
|
+
const live = liveRun.current ?? ref.current.runId ?? true;
|
|
2483
|
+
if (!steerOn() || !text.trim() || attachments.length) return;
|
|
2466
2484
|
commit(beginSteer(ref.current, text));
|
|
2467
2485
|
await runNow({ messages: [userMessage(text, [])], forwardedProps: { steer: live } });
|
|
2468
2486
|
return;
|
|
@@ -2864,20 +2882,24 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2864
2882
|
turns.map((turn, i) => {
|
|
2865
2883
|
const isLast = i === turns.length - 1;
|
|
2866
2884
|
const live = busy && isLast;
|
|
2867
|
-
return /* @__PURE__ */
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
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);
|
|
2880
2901
|
}),
|
|
2902
|
+
busy && state.stalled && /* @__PURE__ */ jsx10("p", { className: "text-sm text-text-tertiary", children: state.stalled.message ?? t.stalled }),
|
|
2881
2903
|
state.queued.map((q, i) => (
|
|
2882
2904
|
// Esperan a que termine el run vivo (el servidor no los dejó entrar): salen solos.
|
|
2883
2905
|
/* @__PURE__ */ jsxs8("div", { "data-queued": true, className: "flex flex-col items-end gap-1 opacity-60", children: [
|
|
@@ -2926,6 +2948,15 @@ var Piloto = forwardRef3(function Piloto2(props, handle) {
|
|
|
2926
2948
|
] })
|
|
2927
2949
|
] })) }) });
|
|
2928
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
|
+
}
|
|
2929
2960
|
|
|
2930
2961
|
// src/react/Subagents.tsx
|
|
2931
2962
|
import { useState as useState10 } from "react";
|
package/dist/react/strings.d.ts
CHANGED
|
@@ -62,6 +62,12 @@ export interface PilotStrings {
|
|
|
62
62
|
continueRun: string;
|
|
63
63
|
/** La franja, si el servidor no dio razón. */
|
|
64
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;
|
|
65
71
|
/** Bajo un mensaje que espera a que termine el run vivo. */
|
|
66
72
|
queued: string;
|
|
67
73
|
/** «2 agentes trabajando». */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ghostykit",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
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",
|