@jini-ai/chat 0.5.6 → 0.5.7
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/dist/core/durable-projection.d.ts +3 -1
- package/dist/core/durable-projection.js +40 -7
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/run-events/entry.d.ts +12 -1
- package/dist/core/run-events/entry.js +17 -6
- package/dist/core/tool-call-detail.d.ts +36 -0
- package/dist/core/tool-call-detail.js +49 -0
- package/dist/core/turn-usage.d.ts +11 -0
- package/dist/core/turn-usage.js +17 -0
- package/dist/react/chat-pane.d.ts +2 -2
- package/dist/react/chat-pane.js +1 -1
- package/dist/react/components/Composer.d.ts +2 -0
- package/dist/react/components/Composer.js +5 -2
- package/dist/react/components/Markdown.js +6 -6
- package/dist/react/components/MessageList.d.ts +7 -12
- package/dist/react/components/MessageList.js +20 -2
- package/dist/react/components/MessageRow.d.ts +6 -1
- package/dist/react/components/MessageRow.js +10 -4
- package/dist/react/components/MidRunUserMessage.d.ts +27 -0
- package/dist/react/components/MidRunUserMessage.js +39 -0
- package/dist/react/components/ToolCard.js +33 -22
- package/dist/react/features/chat-pane/components/ChatPane.d.ts +1 -1
- package/dist/react/features/chat-pane/components/ChatPane.js +11 -3
- package/dist/react/features/chat-pane/create-mid-run-message-poster.d.ts +44 -0
- package/dist/react/features/chat-pane/create-mid-run-message-poster.js +69 -0
- package/dist/react/features/chat-pane/hooks/useChatPane.hooks.d.ts +3 -1
- package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +55 -6
- package/dist/react/features/chat-pane/hooks/useChatPaneNewTabLinks.hooks.d.ts +25 -0
- package/dist/react/features/chat-pane/hooks/useChatPaneNewTabLinks.hooks.js +70 -0
- package/dist/react/features/chat-pane/index.d.ts +2 -1
- package/dist/react/features/chat-pane/index.js +1 -0
- package/dist/react/features/chat-pane/rules.d.ts +13 -0
- package/dist/react/features/chat-pane/rules.js +14 -0
- package/dist/react/features/chat-pane/styles.d.ts +1 -1
- package/dist/react/features/chat-pane/styles.js +7 -0
- package/dist/react/features/chat-pane/types.d.ts +23 -0
- package/dist/react/hooks/composer-draft-cache.d.ts +15 -0
- package/dist/react/hooks/composer-draft-cache.js +20 -0
- package/dist/react/hooks/useComposer.d.ts +14 -0
- package/dist/react/hooks/useComposer.js +78 -6
- package/dist/react/index.d.ts +4 -2
- package/dist/react/index.js +3 -1
- package/dist/server/durable-runs/recover.js +19 -11
- package/dist/server/run-finalizer/entry.js +2 -1
- package/dist/store/runtime/durable-run-store.js +5 -2
- package/dist/store/runtime/run-ledger.js +5 -2
- package/package.json +8 -8
|
@@ -6,7 +6,7 @@ export { RECOVERY_LIMIT, RECOVERY_WINDOW_MS, RUN_STALENESS_MS, SAVED_WORK_NOTICE
|
|
|
6
6
|
/** Retryability is independent of session resumability. Inspect executor diagnostics, never
|
|
7
7
|
* answer text or tool results: those can quote unrelated failures from completed work. Old
|
|
8
8
|
* attempt diagnostics remain visible but cannot decide the fate of their continuation. */
|
|
9
|
-
function
|
|
9
|
+
function attemptDiagnostics({ run }, _optional = {}) {
|
|
10
10
|
const events = run.message.events ?? [];
|
|
11
11
|
// Checkpoints merge adjacent text, so a base's event count is not a durable offset. The
|
|
12
12
|
// coordinator's recovery marker separates attempts and survives that compaction.
|
|
@@ -19,7 +19,10 @@ function attemptFailure(run) {
|
|
|
19
19
|
break;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
return events.slice(marker >= 0 ? marker + 1 : run.attemptBase.length).flatMap((event) => event.kind === "status" ? [event.label, event.detail ?? ""] : event.kind === "raw" ? [event.line] : []).join("\n");
|
|
23
|
+
}
|
|
24
|
+
function attemptFailure(run) {
|
|
25
|
+
const diagnostics = attemptDiagnostics({ run }, {});
|
|
23
26
|
if (/inactivity[_ -]?(timeout|watchdog)|watchdog.*(kill|stop)/i.test(diagnostics))
|
|
24
27
|
return "inactivity-watchdog";
|
|
25
28
|
const service = classifyAgentServiceFailure({ text: diagnostics });
|
|
@@ -34,13 +37,11 @@ function attemptFailure(run) {
|
|
|
34
37
|
return "permanent";
|
|
35
38
|
return "retryable";
|
|
36
39
|
}
|
|
37
|
-
function exclusion(run
|
|
40
|
+
function exclusion(run) {
|
|
38
41
|
if (run.cancelReason)
|
|
39
42
|
return run.cancelReason;
|
|
40
43
|
if (!run.principalId || run.engine !== "daemon")
|
|
41
44
|
return "excluded";
|
|
42
|
-
if (failure === "auth-failure" || failure === "inactivity-watchdog")
|
|
43
|
-
return failure;
|
|
44
45
|
return null;
|
|
45
46
|
}
|
|
46
47
|
function budgetNotice(run, now) {
|
|
@@ -71,12 +72,15 @@ export function createDurableRecovery(ports, _optional = {}) {
|
|
|
71
72
|
}
|
|
72
73
|
async function continueDead(run) {
|
|
73
74
|
await ports.cancelAttempt(run, {}).catch(() => undefined);
|
|
74
|
-
|
|
75
|
+
// A locator captured before a hard cancel can outlive its rollout. Reuse reconstruction,
|
|
76
|
+
// including prior turns/tool outcomes, instead of retrying a known missing native session.
|
|
77
|
+
const missingResume = run.request.agentId === 'codex' && /no rollout found for thread id/.test(attemptDiagnostics({ run }, {}));
|
|
78
|
+
const native = !missingResume && await nativeAllowed(run);
|
|
75
79
|
const nextRunId = ports.mintRunId();
|
|
76
80
|
const saved = [...(run.message.events ?? [])];
|
|
77
81
|
if (!saved.some((event) => event.kind === "text") && run.message.content)
|
|
78
82
|
saved.unshift({ kind: "text", text: run.message.content });
|
|
79
|
-
const events = recoveredRunEvents({ saved }, {});
|
|
83
|
+
const events = recoveredRunEvents({ saved }, { notice: missingResume ? 'silent' : 'visible' });
|
|
80
84
|
if (!await ports.store.advance({ run, nextRunId, now: ports.now(), events }, {}))
|
|
81
85
|
return "superseded";
|
|
82
86
|
const next = await ports.store.load({ messageId: run.messageId }, {});
|
|
@@ -98,10 +102,8 @@ export function createDurableRecovery(ports, _optional = {}) {
|
|
|
98
102
|
return "continued";
|
|
99
103
|
}
|
|
100
104
|
async function decide(run, trigger, liveRunId) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (reason)
|
|
104
|
-
return finalize(run, reason === "auth-failure" ? "Not logged in. Saved work is above." : "Stopped. Saved work is above.", true);
|
|
105
|
+
if (exclusion(run))
|
|
106
|
+
return finalize(run, "Stopped. Saved work is above.", true);
|
|
105
107
|
// A terminal executor end confirms this attempt is dead even when the daemon retains its
|
|
106
108
|
// replayable record (HTTP 200). Other triggers must obtain fresh liveness evidence.
|
|
107
109
|
const probe = trigger === "attempt-failed" ? "dead" : liveRunId === run.runId ? "live" : await ports.probe(run, {});
|
|
@@ -110,6 +112,12 @@ export function createDurableRecovery(ports, _optional = {}) {
|
|
|
110
112
|
ports.attach(run, {});
|
|
111
113
|
return "reattached";
|
|
112
114
|
}
|
|
115
|
+
// Diagnostics decide only the fate of an attempt that is no longer running. Read before the
|
|
116
|
+
// probe, raw stdout quoting a tool description ("OAuth-authenticated") canceled a live turn
|
|
117
|
+
// as "Not logged in" (luvira, 2026-10-08).
|
|
118
|
+
const failure = attemptFailure(run);
|
|
119
|
+
if (failure === "auth-failure" || failure === "inactivity-watchdog")
|
|
120
|
+
return finalize(run, failure === "auth-failure" ? "Not logged in. Saved work is above." : "Stopped. Saved work is above.", true);
|
|
113
121
|
if (failure === "permanent")
|
|
114
122
|
return finalize(run, EXHAUSTED_NOTICE);
|
|
115
123
|
return resolveMissing(run, probe, trigger);
|
|
@@ -86,8 +86,9 @@ export function createAssistantRunFinalizer(args, options = {}) {
|
|
|
86
86
|
// Each connection replays the complete history, rather than appending duplicate deltas.
|
|
87
87
|
watch.events = [];
|
|
88
88
|
watch.failed = false;
|
|
89
|
+
const carry = {};
|
|
89
90
|
for await (const frame of readSseFrames({ body: response.body })) {
|
|
90
|
-
const outcome = translateRunFrame({ kind: frame.event, raw: frame.data, notices: args.notices });
|
|
91
|
+
const outcome = translateRunFrame({ kind: frame.event, raw: frame.data, notices: args.notices }, { carry });
|
|
91
92
|
watch.events.push(...outcome.events);
|
|
92
93
|
if (outcome.error)
|
|
93
94
|
watch.failed = true;
|
|
@@ -88,8 +88,11 @@ function continuationEvents({ current, incoming }, _optional = {}) {
|
|
|
88
88
|
break;
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
if (marker >= 0)
|
|
92
|
-
|
|
91
|
+
if (marker >= 0) {
|
|
92
|
+
const event = incoming[marker];
|
|
93
|
+
const notice = event.kind === 'status' && event.label === '' ? 'silent' : 'visible';
|
|
94
|
+
return [...recoveredRunEvents({ saved }, { notice }), ...incoming.slice(marker + 1)];
|
|
95
|
+
}
|
|
93
96
|
return runContentFromEvents({ events: incoming }).length >= current.message.content.length ? incoming : saved;
|
|
94
97
|
}
|
|
95
98
|
/** Same kernel and message lock as checkpoint/settle. Claim changes run_id before any spawn;
|
|
@@ -126,15 +126,18 @@ export function createChatRunLedger({ kernel: hostKernel, isDaemonRunId }, _opti
|
|
|
126
126
|
async checkpoint(progress, _optional = {}) {
|
|
127
127
|
return kernel.transaction(async () => {
|
|
128
128
|
await kernel.lockKey(runLockKey(progress));
|
|
129
|
-
const current = await kernel.run((db) => db.selectFrom("ai_chat_messages").select(["content", "events_json"])
|
|
129
|
+
const current = await kernel.run((db) => db.selectFrom("ai_chat_messages").select(["content", "events_json", "run_status"])
|
|
130
130
|
.where((eb) => eb.and([isRunRow(eb, progress), notTerminal(eb)])).executeTakeFirst());
|
|
131
131
|
if (!current || progress.content.length < current.content.length)
|
|
132
132
|
return false;
|
|
133
133
|
const events = keepToolCheckpoints(readEvents(current.events_json), progress.events);
|
|
134
134
|
const grown = progress.content.length > current.content.length || hasNewToolProgress(readEvents(current.events_json), events);
|
|
135
|
+
// A checkpoint is the run producing output, so an accepted (`queued`) row is now `running`;
|
|
136
|
+
// nothing else moves it there, and a streaming answer must not read as still waiting.
|
|
137
|
+
const runStatus = current.run_status === "queued" ? "running" : current.run_status;
|
|
135
138
|
const result = await kernel.run((db) => db
|
|
136
139
|
.updateTable("ai_chat_messages")
|
|
137
|
-
.set({ content: progress.content, events_json: JSON.stringify(events) })
|
|
140
|
+
.set({ content: progress.content, events_json: JSON.stringify(events), run_status: runStatus })
|
|
138
141
|
.where((eb) => eb.and([isRunRow(eb, progress), notTerminal(eb)]))
|
|
139
142
|
.executeTakeFirst());
|
|
140
143
|
if (Number(result.numUpdatedRows) > 0 && grown) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/chat",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.7",
|
|
4
4
|
"description": "Jini's chat/artifact frontend, consolidated into one package with two subpaths: framework-free chat vocabulary and pure parsers in `/core`, headless hooks + presentational components + slots in `/react`. Import only the layer you need.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -194,10 +194,10 @@
|
|
|
194
194
|
"@radix-ui/react-select": "^2.3.7",
|
|
195
195
|
"@radix-ui/react-slot": "^1.3.3",
|
|
196
196
|
"recharts": "^3.10.1",
|
|
197
|
-
"@jini-ai/agent-runtime": "^0.4.
|
|
197
|
+
"@jini-ai/agent-runtime": "^0.4.4",
|
|
198
198
|
"@jini-ai/agentic": "^0.4.2",
|
|
199
|
-
"@jini-ai/protocol": "^0.4.
|
|
200
|
-
"@jini-ai/ui": "^0.4.
|
|
199
|
+
"@jini-ai/protocol": "^0.4.3",
|
|
200
|
+
"@jini-ai/ui": "^0.4.7",
|
|
201
201
|
"@jini-ai/diagnostics": "^0.5.0"
|
|
202
202
|
},
|
|
203
203
|
"peerDependenciesMeta": {
|
|
@@ -286,12 +286,12 @@
|
|
|
286
286
|
"@electric-sql/pglite": "0.5.8",
|
|
287
287
|
"pg": "8.23.0",
|
|
288
288
|
"@ag-ui/core": "0.0.58",
|
|
289
|
-
"@jini-ai/
|
|
290
|
-
"@jini-ai/agent-runtime": "0.4.3",
|
|
291
|
-
"@jini-ai/ui": "^0.4.6",
|
|
289
|
+
"@jini-ai/ui": "^0.4.7",
|
|
292
290
|
"@jini-ai/db": "0.3.1",
|
|
293
|
-
"@jini-ai/
|
|
291
|
+
"@jini-ai/protocol": "^0.4.3",
|
|
294
292
|
"@jini-ai/diagnostics": "^0.5.1",
|
|
293
|
+
"@jini-ai/agentic": "^0.4.3",
|
|
294
|
+
"@jini-ai/agent-runtime": "0.4.4",
|
|
295
295
|
"@jini-ai/core": "^0.4.2"
|
|
296
296
|
},
|
|
297
297
|
"typesVersions": {
|