@inflexa-ai/harness 0.48.2 → 0.48.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.
- package/dist/agents/sandbox/index.js +3 -0
- package/dist/agents/sandbox/mdv-preparation-agent.d.ts +9 -0
- package/dist/agents/sandbox/mdv-preparation-agent.js +37 -0
- package/dist/app/chat-turn-workflow.d.ts +3 -1
- package/dist/app/chat-turn-workflow.js +19 -3
- package/dist/app/chat-turn.d.ts +4 -2
- package/dist/app/chat-turn.js +70 -44
- package/dist/app/in-process-turn.js +3 -6
- package/dist/app/work.d.ts +7 -3
- package/dist/app/work.js +17 -16
- package/dist/contracts/chat-events.d.ts +5 -0
- package/dist/contracts/chat-parts.d.ts +64 -5
- package/dist/contracts/chat-parts.js +3 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/part-registry.js +2 -0
- package/dist/contracts/schemas/chat-events.d.ts +2 -0
- package/dist/contracts/schemas/chat-events.js +1 -0
- package/dist/contracts/schemas/chat-parts.d.ts +197 -5
- package/dist/contracts/schemas/chat-parts.js +32 -2
- package/dist/index.d.ts +1 -1
- package/dist/lib/regulatory-corpus/fetch-ich.js +1 -16
- package/dist/lib/slug.d.ts +18 -0
- package/dist/lib/slug.js +29 -0
- package/dist/lib/test/pdf-fixture.d.ts +5 -0
- package/dist/lib/test/pdf-fixture.js +14 -0
- package/dist/loop/chat-stream.js +1 -1
- package/dist/loop/run-loop.js +39 -35
- package/dist/loop/sub-agent.d.ts +2 -2
- package/dist/loop/sub-agent.js +1 -1
- package/dist/loop/test/metric-reader.d.ts +9 -0
- package/dist/loop/test/metric-reader.js +33 -0
- package/dist/loop/test/unnamed-model.d.ts +6 -0
- package/dist/loop/test/unnamed-model.js +24 -0
- package/dist/memory/conversation-display-recorder.d.ts +2 -0
- package/dist/memory/conversation-display-recorder.js +6 -2
- package/dist/memory/conversation-display-storage.d.ts +5 -2
- package/dist/memory/conversation-display-storage.js +28 -1
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +6 -4
- package/dist/prompts/sandbox/mdv-preparation-agent.d.ts +1 -0
- package/dist/prompts/sandbox/mdv-preparation-agent.js +56 -0
- package/dist/providers/ai-sdk.d.ts +17 -14
- package/dist/providers/ai-sdk.js +14 -16
- package/dist/report-model/fixture-resolver.js +9 -135
- package/dist/report-model/production-resolver.d.ts +3 -3
- package/dist/report-model/production-resolver.js +12 -127
- package/dist/report-model/reference-read.d.ts +38 -0
- package/dist/report-model/reference-read.js +149 -0
- package/dist/report-model/reference-resolver.d.ts +7 -0
- package/dist/report-model/reference-resolver.js +1 -1
- package/dist/report-render/chart.js +48 -46
- package/dist/report-render/figures/common.d.ts +34 -0
- package/dist/report-render/figures/common.js +4 -1
- package/dist/report-render/figures/equal-units.js +2 -4
- package/dist/report-render/figures/sankey.js +1 -3
- package/dist/report-render/figures/upset.js +2 -5
- package/dist/runtime/assemble.js +2 -1
- package/dist/sandbox/execution-id.js +2 -14
- package/dist/sandbox/leases.js +46 -9
- package/dist/schemas/validate-plan.js +2 -14
- package/dist/state/active-turns.d.ts +6 -2
- package/dist/state/active-turns.js +11 -7
- package/dist/state/data-profile.d.ts +8 -0
- package/dist/state/data-profile.js +18 -0
- package/dist/state/index.d.ts +1 -1
- package/dist/state/index.js +1 -1
- package/dist/state/migrations/20261102120000_record_the_waiter_of_a_pause.d.ts +6 -0
- package/dist/state/migrations/20261102120000_record_the_waiter_of_a_pause.js +10 -0
- package/dist/state/migrations/index.js +2 -0
- package/dist/state/paused-work.d.ts +5 -3
- package/dist/state/paused-work.js +11 -7
- package/dist/state/runs.d.ts +3 -2
- package/dist/state/runs.js +4 -3
- package/dist/tasks/data-profile-index.js +3 -17
- package/dist/tasks/data-profile-resolve.js +3 -17
- package/dist/tasks/data-profile.js +1 -0
- package/dist/tools/lib/opentargets-client.js +15 -25
- package/dist/tools/report-authoring/authoring-tools.d.ts +3 -3
- package/dist/tools/report-authoring/authoring-tools.js +7 -6
- package/dist/tools/research/generate-analogy-report.js +10 -2
- package/dist/workflows/execute-analysis.js +4 -3
- package/dist/workflows/pause.d.ts +3 -3
- package/dist/workflows/pause.js +15 -9
- package/dist/workflows/sandbox-step.js +1 -0
- package/package.json +7 -4
|
@@ -20,6 +20,7 @@ import { createDrugRepurposingAgent, meta as drugRepurposingMeta } from "./drug-
|
|
|
20
20
|
import { createEnrichmentAgent, meta as enrichmentMeta } from "./enrichment-agent.js";
|
|
21
21
|
import { createGenomicVariantAgent, meta as genomicVariantMeta } from "./genomic-variant-agent.js";
|
|
22
22
|
import { createImmuneProfilingAgent, meta as immuneProfilingMeta } from "./immune-profiling-agent.js";
|
|
23
|
+
import { createMdvPreparationAgent, meta as mdvPreparationMeta } from "./mdv-preparation-agent.js";
|
|
23
24
|
import { createMetabolomicsAgent, meta as metabolomicsMeta } from "./metabolomics-agent.js";
|
|
24
25
|
import { createMicrobiomeAgent, meta as microbiomeMeta } from "./microbiome-agent.js";
|
|
25
26
|
import { createMultiOmicsIntegrationAgent, meta as multiOmicsIntegrationMeta } from "./multi-omics-integration-agent.js";
|
|
@@ -60,6 +61,7 @@ export const SANDBOX_AGENT_META = {
|
|
|
60
61
|
[pkpdClinicalResponseMeta.id]: pkpdClinicalResponseMeta,
|
|
61
62
|
[immuneProfilingMeta.id]: immuneProfilingMeta,
|
|
62
63
|
[drugRepurposingMeta.id]: drugRepurposingMeta,
|
|
64
|
+
[mdvPreparationMeta.id]: mdvPreparationMeta,
|
|
63
65
|
[scientificExecutorMeta.id]: scientificExecutorMeta,
|
|
64
66
|
};
|
|
65
67
|
/**
|
|
@@ -88,6 +90,7 @@ export function createSandboxAgents(deps) {
|
|
|
88
90
|
[pkpdClinicalResponseMeta.id]: createPkpdClinicalResponseAgent(deps),
|
|
89
91
|
[immuneProfilingMeta.id]: createImmuneProfilingAgent(deps),
|
|
90
92
|
[drugRepurposingMeta.id]: createDrugRepurposingAgent(deps),
|
|
93
|
+
[mdvPreparationMeta.id]: createMdvPreparationAgent(deps),
|
|
91
94
|
[scientificExecutorMeta.id]: createScientificExecutorAgent(deps),
|
|
92
95
|
};
|
|
93
96
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MDV Preparation Agent — turns single-cell, scATAC-seq, spatial transcriptomics, and spatial
|
|
3
|
+
* proteomics data into curated, validated assets that MDV (Multi-Dimensional Viewer) loads.
|
|
4
|
+
*/
|
|
5
|
+
import type { AgentDefinition } from "../../loop/types.js";
|
|
6
|
+
import { type SandboxAgentDeps } from "./shared.js";
|
|
7
|
+
import type { AgentMeta } from "./types.js";
|
|
8
|
+
export declare const meta: AgentMeta;
|
|
9
|
+
export declare function createMdvPreparationAgent(deps: SandboxAgentDeps): AgentDefinition;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MDV Preparation Agent — turns single-cell, scATAC-seq, spatial transcriptomics, and spatial
|
|
3
|
+
* proteomics data into curated, validated assets that MDV (Multi-Dimensional Viewer) loads.
|
|
4
|
+
*/
|
|
5
|
+
import { mdvPreparationAgentPrompt } from "../../prompts/sandbox/mdv-preparation-agent.js";
|
|
6
|
+
import { BASE_SANDBOX_TOOLS, createSandboxAgent } from "./shared.js";
|
|
7
|
+
export const meta = {
|
|
8
|
+
id: "mdv-preparation-agent",
|
|
9
|
+
capabilities: [
|
|
10
|
+
"MDV asset preparation",
|
|
11
|
+
"single-cell data preparation for MDV",
|
|
12
|
+
"spatial data preparation for MDV",
|
|
13
|
+
"metadata curation for visualization",
|
|
14
|
+
"MDV asset validation",
|
|
15
|
+
],
|
|
16
|
+
suitableFor: [
|
|
17
|
+
"scrna-seq",
|
|
18
|
+
"snrna-seq",
|
|
19
|
+
"scatac-seq",
|
|
20
|
+
"multiome",
|
|
21
|
+
"cosmx",
|
|
22
|
+
"xenium",
|
|
23
|
+
"visium",
|
|
24
|
+
"merfish",
|
|
25
|
+
"codex",
|
|
26
|
+
"imc",
|
|
27
|
+
"mibi",
|
|
28
|
+
"anndata",
|
|
29
|
+
"mudata",
|
|
30
|
+
"spatialdata",
|
|
31
|
+
],
|
|
32
|
+
skills: ["mdv-preparation", "single-cell", "spatial-omics", "chromatin-regulation", "shared/omics-general"],
|
|
33
|
+
tools: [...BASE_SANDBOX_TOOLS],
|
|
34
|
+
};
|
|
35
|
+
export function createMdvPreparationAgent(deps) {
|
|
36
|
+
return createSandboxAgent(deps, meta, mdvPreparationAgentPrompt);
|
|
37
|
+
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { AuthContext } from "../auth/types.js";
|
|
17
17
|
import type { TokenUsageRollup } from "../contracts/usage.js";
|
|
18
|
-
import type { ChatFrame } from "../contracts/chat-events.js";
|
|
18
|
+
import type { ChatErrorEvent, ChatFrame, EventSource, FinishEvent } from "../contracts/chat-events.js";
|
|
19
19
|
import type { AgentDefinition } from "../loop/types.js";
|
|
20
20
|
import type { ThreadType } from "../memory/thread-store.js";
|
|
21
21
|
import type { PromptCachePolicy } from "../providers/types.js";
|
|
@@ -110,5 +110,7 @@ export declare const DEFAULT_ASK_WAIT_SECONDS: number;
|
|
|
110
110
|
* finds the workflow of a recovery by its registered name.
|
|
111
111
|
*/
|
|
112
112
|
export declare function registerChatTurns(deps: ChatTurnWorkflowDeps): ChatTurns;
|
|
113
|
+
/** The last frame of a turn that ran: `error` for a failed turn, else `finish` with the stop reason of the turn. */
|
|
114
|
+
export declare function endFrameOf(ran: ChatTurnRan, rootSource: EventSource): ChatErrorEvent | FinishEvent;
|
|
113
115
|
/** How a turn that ran ended, as a host reads it. */
|
|
114
116
|
export declare function chatTurnEndOf(ran: ChatTurnRan): ChatTurnEnd;
|
|
@@ -258,6 +258,7 @@ async function runTurnWorkflow(deps, input, signal) {
|
|
|
258
258
|
pool: deps.pool,
|
|
259
259
|
workId: input.turnId,
|
|
260
260
|
workKind: "turn",
|
|
261
|
+
waiter: { kind: "turn" },
|
|
261
262
|
analysisId: input.analysisId,
|
|
262
263
|
threadId: input.threadId,
|
|
263
264
|
emit: async (part) => {
|
|
@@ -277,9 +278,7 @@ async function runTurnWorkflow(deps, input, signal) {
|
|
|
277
278
|
};
|
|
278
279
|
const ran = await runOpenTurn(deps, { analysisId: input.analysisId, threadId: input.threadId, userInput: input.userText }, { kind: "ok", threadType: input.threadType, history: view, userMessage: opening.userMessage, contextRecords: opening.contextRecords }, agent.value, { session, author: input.author, startedAtMs: input.startedAtMs, promptCache: input.promptCache }, transport);
|
|
279
280
|
const { outcome } = ran;
|
|
280
|
-
await writer.write(
|
|
281
|
-
? { type: "error", message: outcome.reason, reason: outcome.code, source: rootSource }
|
|
282
|
-
: { type: "finish", source: rootSource, turnUsage: ran.turnUsage });
|
|
281
|
+
await writer.write(endFrameOf(ran, rootSource));
|
|
283
282
|
await writer.settle();
|
|
284
283
|
await closeDurableStream(CHAT_STREAM_KEY, outcome.status === "done" ? "stop" : outcome.status === "aborted" ? "aborted" : "error");
|
|
285
284
|
// A sub-agent that a stop canceled in its pause leaves its row, thus the end of the turn clears each row of the turn.
|
|
@@ -287,6 +286,23 @@ async function runTurnWorkflow(deps, input, signal) {
|
|
|
287
286
|
await DBOS.runStep(async () => unwrapOrThrow(await releaseThreadOfTurn(deps.pool, input.threadId, input.turnId)), { name: "chat-turn.release" });
|
|
288
287
|
return chatTurnEndOf(ran);
|
|
289
288
|
}
|
|
289
|
+
/** The last frame of a turn that ran: `error` for a failed turn, else `finish` with the stop reason of the turn. */
|
|
290
|
+
export function endFrameOf(ran, rootSource) {
|
|
291
|
+
const { outcome } = ran;
|
|
292
|
+
switch (outcome.status) {
|
|
293
|
+
case "failed":
|
|
294
|
+
return { type: "error", message: outcome.reason, reason: outcome.code, source: rootSource };
|
|
295
|
+
case "done":
|
|
296
|
+
return { type: "finish", source: rootSource, finishReason: outcome.finish.reason, turnUsage: ran.turnUsage };
|
|
297
|
+
// An abort that cut the run before its end leaves no finish of the loop.
|
|
298
|
+
case "aborted":
|
|
299
|
+
return { type: "finish", source: rootSource, finishReason: "aborted", turnUsage: ran.turnUsage };
|
|
300
|
+
default: {
|
|
301
|
+
const exhaustive = outcome;
|
|
302
|
+
throw new Error(`unhandled turn outcome: ${JSON.stringify(exhaustive)}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
290
306
|
/** How a turn that ran ended, as a host reads it. */
|
|
291
307
|
export function chatTurnEndOf(ran) {
|
|
292
308
|
const { outcome } = ran;
|
package/dist/app/chat-turn.d.ts
CHANGED
|
@@ -56,8 +56,10 @@ export type PreparedChatTurn = Extract<PrepareChatTurnResult, {
|
|
|
56
56
|
*/
|
|
57
57
|
export declare function prepareChatTurn(deps: PrepareChatTurnDeps, params: PrepareChatTurnParams): Promise<PrepareChatTurnResult>;
|
|
58
58
|
/**
|
|
59
|
-
* Open the thread of a turn:
|
|
60
|
-
* thread
|
|
59
|
+
* Open the thread of a turn: make it when it is absent, and seed its title. A thread that a different analysis
|
|
60
|
+
* owns is `not_found`, and so is an archived thread: a turn never writes into hidden state and never restores it.
|
|
61
|
+
* A failed make of the thread throws, because without its row the open cannot tell an absent thread from one that
|
|
62
|
+
* it must refuse.
|
|
61
63
|
*/
|
|
62
64
|
export declare function openThread(deps: PrepareChatTurnDeps, params: PrepareChatTurnParams): Promise<{
|
|
63
65
|
readonly kind: "ok";
|
package/dist/app/chat-turn.js
CHANGED
|
@@ -48,8 +48,10 @@ export async function prepareChatTurn(deps, params) {
|
|
|
48
48
|
return { kind: "ok", threadType: opened.threadType, ...assembled };
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
|
-
* Open the thread of a turn:
|
|
52
|
-
* thread
|
|
51
|
+
* Open the thread of a turn: make it when it is absent, and seed its title. A thread that a different analysis
|
|
52
|
+
* owns is `not_found`, and so is an archived thread: a turn never writes into hidden state and never restores it.
|
|
53
|
+
* A failed make of the thread throws, because without its row the open cannot tell an absent thread from one that
|
|
54
|
+
* it must refuse.
|
|
53
55
|
*/
|
|
54
56
|
export async function openThread(deps, params) {
|
|
55
57
|
const { pool } = deps;
|
|
@@ -58,52 +60,48 @@ export async function openThread(deps, params) {
|
|
|
58
60
|
// An unbound member gives a call that does nothing, thus the emit below needs
|
|
59
61
|
// no test of its own.
|
|
60
62
|
const observe = bindSessionEmit(deps.provenance, logger);
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
+
// `getThread` hides an archived row, thus a live row is the only one that this
|
|
64
|
+
// read can prove owned. An absent result covers a missing thread and an
|
|
65
|
+
// archived one, and the idempotent create below tells them apart.
|
|
63
66
|
const store = createThreadStore(pool);
|
|
64
67
|
const existing = unwrapOrThrow(await store.getThread(threadId));
|
|
65
68
|
if (existing && existing.analysisId !== analysisId) {
|
|
66
69
|
return { kind: "not_found" };
|
|
67
70
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
threadId,
|
|
82
|
-
analysisId,
|
|
83
|
-
title: deriveThreadTitle(userInput),
|
|
84
|
-
}));
|
|
85
|
-
threadType = created.threadType;
|
|
86
|
-
// The row that comes back is not proof that this call wrote it. The read
|
|
87
|
-
// above hides an archived thread, and the idempotent insert then reads
|
|
88
|
-
// that same row back. Thus a turn on an archived thread reaches here with
|
|
89
|
-
// a row that a different site created, and an emit would record a start
|
|
90
|
-
// that never happened.
|
|
91
|
-
//
|
|
92
|
-
// This site writes a live conversation thread and nothing else. An
|
|
93
|
-
// archived row, or a row of another kind, is therefore one that already
|
|
94
|
-
// existed. A root session has no parent, thus the event carries none.
|
|
95
|
-
if (created.deletedAt === null && created.threadType === "conversation") {
|
|
96
|
-
observe({ type: "create-session", analysisId, threadId, sessionKind: created.threadType });
|
|
97
|
-
}
|
|
71
|
+
if (!existing) {
|
|
72
|
+
// `createThread` is idempotent (ON CONFLICT reads the row back with no
|
|
73
|
+
// filter), so the returned row is authoritative: the inserted row, or the
|
|
74
|
+
// row that already existed, archived or owned by a different analysis. A
|
|
75
|
+
// conflict writes nothing, thus the refusal comes before each write of the
|
|
76
|
+
// turn.
|
|
77
|
+
const created = unwrapOrThrow(await store.createThread({
|
|
78
|
+
threadId,
|
|
79
|
+
analysisId,
|
|
80
|
+
title: deriveThreadTitle(userInput),
|
|
81
|
+
}));
|
|
82
|
+
if (created.analysisId !== analysisId || created.deletedAt !== null) {
|
|
83
|
+
return { kind: "not_found" };
|
|
98
84
|
}
|
|
99
|
-
|
|
100
|
-
|
|
85
|
+
// The row that comes back is not proof that this call wrote it: a create
|
|
86
|
+
// that races another writer reads that writer's row back. This site writes
|
|
87
|
+
// a live conversation thread and nothing else, thus a row of another kind
|
|
88
|
+
// is one that already existed. A root session has no parent, thus the event
|
|
89
|
+
// carries none.
|
|
90
|
+
if (created.threadType === "conversation") {
|
|
91
|
+
observe({ type: "create-session", analysisId, threadId, sessionKind: created.threadType });
|
|
101
92
|
}
|
|
93
|
+
return { kind: "ok", threadType: created.threadType };
|
|
102
94
|
}
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
// Seed the thread title from the first user message. Best-effort.
|
|
96
|
+
if (!existing.title || existing.title.length === 0) {
|
|
97
|
+
try {
|
|
98
|
+
unwrapOrThrow(await store.setAutoTitle(threadId, deriveThreadTitle(userInput)));
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
logger.warn("title-seed failed (non-fatal)", logger.errorFields(err));
|
|
102
|
+
}
|
|
105
103
|
}
|
|
106
|
-
return { kind: "ok", threadType };
|
|
104
|
+
return { kind: "ok", threadType: existing.threadType };
|
|
107
105
|
}
|
|
108
106
|
/** The analysis context and the run activity of a turn. A failed read gives the fallback of its kind. */
|
|
109
107
|
export async function readTurnContext(pool, analysisId) {
|
|
@@ -218,7 +216,7 @@ export async function runOpenTurn(deps, turn, prepared, agent, params, transport
|
|
|
218
216
|
await flush({
|
|
219
217
|
status: outcome.status,
|
|
220
218
|
reason: outcome.status === "failed" ? outcome.reason : undefined,
|
|
221
|
-
note:
|
|
219
|
+
note: closingNoteOf(outcome, recorder),
|
|
222
220
|
turnUsage,
|
|
223
221
|
turnDurationMs: durationMs,
|
|
224
222
|
});
|
|
@@ -292,15 +290,43 @@ function failureReasonOf(err) {
|
|
|
292
290
|
return findProviderError(err) === undefined ? "The turn stopped on an internal error." : "The model request failed.";
|
|
293
291
|
}
|
|
294
292
|
}
|
|
293
|
+
/** The note that closes a turn: a failed, an aborted, or a declined turn has one, and a turn that the model answered has none. */
|
|
294
|
+
function closingNoteOf(outcome, recorder) {
|
|
295
|
+
switch (outcome.status) {
|
|
296
|
+
case "failed": {
|
|
297
|
+
const note = conversationRecordTurn(failureNote(outcome.reason));
|
|
298
|
+
return { ...note, displayMessages: [...recorder.openRoundPauses(), ...note.displayMessages] };
|
|
299
|
+
}
|
|
300
|
+
case "aborted":
|
|
301
|
+
return interruptionNote(recorder);
|
|
302
|
+
case "done":
|
|
303
|
+
return outcome.finish.reason === "content-filter" ? declineNote(recorder) : undefined;
|
|
304
|
+
default: {
|
|
305
|
+
const exhaustive = outcome;
|
|
306
|
+
throw new Error(`unhandled turn outcome: ${JSON.stringify(exhaustive)}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* The note of a turn that the content filter stopped. A declined request often gives no message, and a round
|
|
312
|
+
* with no message gives no row. The note is the row that holds the display of such a round, thus a reload
|
|
313
|
+
* shows the refusal. It also tells the next turn that this request has no reply.
|
|
314
|
+
*/
|
|
315
|
+
function declineNote(recorder) {
|
|
316
|
+
return {
|
|
317
|
+
modelMessages: [syntheticUserMessage("[The model provider declined the request above, thus it has no reply.]")],
|
|
318
|
+
displayMessages: recorder.takeRound([]),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
295
321
|
/**
|
|
296
322
|
* The note of an aborted turn. Without it, the next turn reads the request of the aborted turn as open, and
|
|
297
|
-
* its reply also answers that request. The transcript shows the interruption from the turn record
|
|
298
|
-
* note
|
|
323
|
+
* its reply also answers that request. The transcript shows the interruption from the turn record. The display
|
|
324
|
+
* of the note holds only the pause parts of the open round, thus a reload shows how the wait ended.
|
|
299
325
|
*/
|
|
300
|
-
function interruptionNote() {
|
|
326
|
+
function interruptionNote(recorder) {
|
|
301
327
|
return {
|
|
302
328
|
modelMessages: [syntheticUserMessage("[The user interrupted the request above. Do not continue it unless the user asks you to.]")],
|
|
303
|
-
displayMessages:
|
|
329
|
+
displayMessages: recorder.openRoundPauses(),
|
|
304
330
|
};
|
|
305
331
|
}
|
|
306
332
|
function failureNote(reason) {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
8
8
|
import { toChatFrame } from "../contracts/chat-frame.js";
|
|
9
|
-
import { chatTurnEndOf } from "./chat-turn-workflow.js";
|
|
9
|
+
import { chatTurnEndOf, endFrameOf } from "./chat-turn-workflow.js";
|
|
10
10
|
import { createConversationDisplayRecorder } from "../memory/conversation-display-recorder.js";
|
|
11
11
|
import { prepareChatTurn, runOpenTurn, userTextOf, } from "./chat-turn.js";
|
|
12
12
|
/** Prepare a turn and resolve its agent. A refusal comes back before any row of the turn exists. */
|
|
@@ -112,11 +112,8 @@ export function inProcessChatTurns(deps) {
|
|
|
112
112
|
promptCache: request.promptCache,
|
|
113
113
|
})
|
|
114
114
|
.then((ran) => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
? { type: "error", message: turnEnd.reason ?? "The turn failed.", reason: turnEnd.code, source: rootSource }
|
|
118
|
-
: { type: "finish", source: rootSource, turnUsage: turnEnd.turnUsage });
|
|
119
|
-
return turnEnd;
|
|
115
|
+
log.push(endFrameOf(ran, rootSource));
|
|
116
|
+
return chatTurnEndOf(ran);
|
|
120
117
|
})
|
|
121
118
|
.catch(() => null)
|
|
122
119
|
.finally(() => log.close());
|
package/dist/app/work.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* of a turn and each step of a run wait in their own workflows. A stop ends a work in each state. A resume and a
|
|
7
7
|
* hold reach only a work that waits. Only a client resumes paused work.
|
|
8
8
|
*/
|
|
9
|
-
import type { PauseCause, WorkKind } from "../contracts/chat-parts.js";
|
|
9
|
+
import type { PauseCause, PauseWaiter, WorkKind } from "../contracts/chat-parts.js";
|
|
10
10
|
import type { RunAuthorizer } from "../execution/run-authorizer.js";
|
|
11
11
|
import type { RunCanceler } from "../execution/run-canceler.js";
|
|
12
12
|
import type { Logger } from "../lib/logger.js";
|
|
@@ -15,6 +15,8 @@ import type { ChatTurnSession } from "./chat-turn.js";
|
|
|
15
15
|
/** One waiting workflow of a paused work. */
|
|
16
16
|
export type PausedWorkflow = {
|
|
17
17
|
readonly workflowId: string;
|
|
18
|
+
/** What waits. Absent for a wait that started before the harness recorded the waiter. */
|
|
19
|
+
readonly waiter?: PauseWaiter;
|
|
18
20
|
readonly state: "retrying" | "paused";
|
|
19
21
|
readonly cause: PauseCause;
|
|
20
22
|
/** The count of timer retries before this wait. */
|
|
@@ -29,6 +31,7 @@ export type Work = {
|
|
|
29
31
|
/** The turn id, the run id, or the id of the profile workflow. */
|
|
30
32
|
readonly workId: string;
|
|
31
33
|
readonly kind: WorkKind;
|
|
34
|
+
/** The analysis of the work, as its own row names it. */
|
|
32
35
|
readonly analysisId: string;
|
|
33
36
|
readonly threadId?: string;
|
|
34
37
|
/** Each waiting workflow of the work. Empty for a work that runs. */
|
|
@@ -61,10 +64,11 @@ export type WorkHold = {
|
|
|
61
64
|
/** The durable work of a runtime, as a host uses it. */
|
|
62
65
|
export type WorkSurface = {
|
|
63
66
|
/**
|
|
64
|
-
* The active work of an analysis
|
|
67
|
+
* The active work of an analysis, or of each analysis when `analysisId` is absent: each turn that holds a thread,
|
|
68
|
+
* each `running` run, and each profile that runs. The count of reads does not depend on the count of analyses.
|
|
65
69
|
* A pause row whose workflow ended is not in it, and the list deletes that row.
|
|
66
70
|
*/
|
|
67
|
-
list(analysisId
|
|
71
|
+
list(analysisId?: string): Promise<Work[]>;
|
|
68
72
|
/**
|
|
69
73
|
* Stop the work, in each state: a turn stops, a run cancels with its children, and a profile fails. Work of a
|
|
70
74
|
* different analysis than the session gives `not_found`.
|
package/dist/app/work.js
CHANGED
|
@@ -11,9 +11,9 @@ import { createNoopLogger } from "../lib/console-logger.js";
|
|
|
11
11
|
import { passGate } from "../lib/hooks.js";
|
|
12
12
|
import { unwrapOrThrow } from "../lib/result.js";
|
|
13
13
|
import { turnCredentials } from "../loop/turn-credentials.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { clearPauses,
|
|
14
|
+
import { queryTurns } from "../state/active-turns.js";
|
|
15
|
+
import { queryRunningProfiles } from "../state/data-profile.js";
|
|
16
|
+
import { clearPauses, queryPauses, queryPausesOfWork } from "../state/paused-work.js";
|
|
17
17
|
import { queryActiveRunsByAnalysis } from "../state/runs.js";
|
|
18
18
|
import { HOLD_MESSAGE, RESUME_MESSAGE } from "../workflows/pause.js";
|
|
19
19
|
/** The workflow statuses that can still wait. */
|
|
@@ -23,6 +23,7 @@ const WORK_STOP_PROVENANCE = { agentId: "work-stop", callPath: ["work-stop"] };
|
|
|
23
23
|
function workflowOf(record) {
|
|
24
24
|
return {
|
|
25
25
|
workflowId: record.workflowId,
|
|
26
|
+
waiter: record.waiter,
|
|
26
27
|
state: record.state,
|
|
27
28
|
cause: record.cause,
|
|
28
29
|
attempt: record.attempt,
|
|
@@ -39,17 +40,16 @@ async function activeWorkflows(workflowIds) {
|
|
|
39
40
|
export function createWork(deps) {
|
|
40
41
|
const logger = (deps.logger ?? createNoopLogger()).named("harness.work");
|
|
41
42
|
async function list(analysisId) {
|
|
42
|
-
const [records, turns, runs,
|
|
43
|
-
(async () => unwrapOrThrow(await
|
|
44
|
-
(async () => unwrapOrThrow(await
|
|
43
|
+
const [records, turns, runs, profiles] = await Promise.all([
|
|
44
|
+
(async () => unwrapOrThrow(await queryPauses(deps.pool, analysisId)))(),
|
|
45
|
+
(async () => unwrapOrThrow(await queryTurns(deps.pool, analysisId)))(),
|
|
45
46
|
(async () => unwrapOrThrow(await queryActiveRunsByAnalysis(deps.pool, analysisId)))(),
|
|
46
|
-
(async () => unwrapOrThrow(await
|
|
47
|
+
(async () => unwrapOrThrow(await queryRunningProfiles(deps.pool, analysisId)))(),
|
|
47
48
|
]);
|
|
48
|
-
const profileWorkflowId = profile?.status === "running" ? (profile.workflowId ?? undefined) : undefined;
|
|
49
49
|
const active = await activeWorkflows([
|
|
50
50
|
...records.map((record) => record.workflowId),
|
|
51
51
|
...turns.map((turn) => turn.turnId),
|
|
52
|
-
...(
|
|
52
|
+
...profiles.map((profile) => profile.workflowId),
|
|
53
53
|
]);
|
|
54
54
|
const ended = records.filter((record) => !active.has(record.workflowId)).map((record) => record.workflowId);
|
|
55
55
|
if (ended.length > 0) {
|
|
@@ -64,23 +64,24 @@ export function createWork(deps) {
|
|
|
64
64
|
waitingOf.set(record.workId, [...(waitingOf.get(record.workId) ?? []), workflowOf(record)]);
|
|
65
65
|
}
|
|
66
66
|
const work = new Map();
|
|
67
|
-
const add = (workId, kind, threadId) => {
|
|
67
|
+
const add = (workId, kind, itemAnalysisId, threadId) => {
|
|
68
68
|
if (work.has(workId))
|
|
69
69
|
return;
|
|
70
|
-
work.set(workId, { workId, kind, analysisId, threadId, waiting: waitingOf.get(workId) ?? [] });
|
|
70
|
+
work.set(workId, { workId, kind, analysisId: itemAnalysisId, threadId, waiting: waitingOf.get(workId) ?? [] });
|
|
71
71
|
};
|
|
72
72
|
// A claim can outlive its turn, thus a turn is active while its workflow is. A `running` run is active while
|
|
73
73
|
// its row is, because the run canceler also converges a row whose workflow ended.
|
|
74
74
|
for (const turn of turns)
|
|
75
75
|
if (active.has(turn.turnId))
|
|
76
|
-
add(turn.turnId, "turn", turn.threadId);
|
|
76
|
+
add(turn.turnId, "turn", turn.analysisId, turn.threadId);
|
|
77
77
|
for (const run of runs)
|
|
78
|
-
add(run.runId, "run", run.threadId ?? undefined);
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
add(run.runId, "run", run.analysisId, run.threadId ?? undefined);
|
|
79
|
+
for (const profile of profiles)
|
|
80
|
+
if (active.has(profile.workflowId))
|
|
81
|
+
add(profile.workflowId, "profile", profile.analysisId, undefined);
|
|
81
82
|
for (const record of records)
|
|
82
83
|
if (active.has(record.workflowId))
|
|
83
|
-
add(record.workId, record.workKind, record.threadId);
|
|
84
|
+
add(record.workId, record.workKind, record.analysisId, record.threadId);
|
|
84
85
|
return [...work.values()];
|
|
85
86
|
}
|
|
86
87
|
return {
|
|
@@ -162,6 +162,11 @@ export type ToolFinishedEvent = {
|
|
|
162
162
|
export type FinishEvent = {
|
|
163
163
|
type: "finish";
|
|
164
164
|
source: EventSource;
|
|
165
|
+
/**
|
|
166
|
+
* Why the turn stopped: the finish reason of the last reply, for example `stop` or `content-filter`, or `aborted`,
|
|
167
|
+
* `max_iterations`, or `denied`. A word outside these stays as the provider sent it.
|
|
168
|
+
*/
|
|
169
|
+
finishReason: string;
|
|
165
170
|
/** What this loop's own LLM calls used. */
|
|
166
171
|
usage?: TokenUsageRollup;
|
|
167
172
|
/**
|
|
@@ -4,17 +4,20 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Categories:
|
|
6
6
|
* Presentation: data-presentation (agent-synthesized content),
|
|
7
|
-
* data-plan (plan by id), data-file-reference (files by path)
|
|
7
|
+
* data-plan (plan by id), data-file-reference (files by path),
|
|
8
|
+
* data-analogy-report (the result of generate_analogy_report)
|
|
8
9
|
* Approval: data-ask (a tool's pending user-approval prompt),
|
|
9
10
|
* data-plan-approval (the record of an approved plan)
|
|
10
11
|
* Compaction: data-compaction (a compaction of the conversation thread)
|
|
11
12
|
* Model retry: data-model-retry (a retry of a model request inside the retry envelope of the provider)
|
|
13
|
+
* Model refusal: data-model-refusal (a model request that the provider declined)
|
|
12
14
|
* Run lifecycle: run-started, dag-state, run-completed, run-failed, run-synthesis,
|
|
13
15
|
* synthesis-progress
|
|
14
16
|
* Per-step live: step-activity, step-file-tree
|
|
15
17
|
* Activity history: activity-entry (run stream), subagent-activity (conversation)
|
|
16
18
|
* Per-step final: step-output, step-summary, step-usage
|
|
17
19
|
*/
|
|
20
|
+
import type { AnalogicalReasonerOutput } from "./analogy-report.js";
|
|
18
21
|
import type { ToolCallOutcome } from "./chat-events.js";
|
|
19
22
|
import type { TokenUsageRollup } from "./usage.js";
|
|
20
23
|
export type PlanStep = {
|
|
@@ -71,6 +74,13 @@ export type PresentationPart = {
|
|
|
71
74
|
title?: string;
|
|
72
75
|
content: PresentationContent;
|
|
73
76
|
};
|
|
77
|
+
/** The result of one `generate_analogy_report` call, as the tool gave it to the model: a report or an `extraction-failed` envelope. */
|
|
78
|
+
export type AnalogyReportPart = {
|
|
79
|
+
type: "data-analogy-report";
|
|
80
|
+
/** The id of the tool call. */
|
|
81
|
+
id: string;
|
|
82
|
+
report: AnalogicalReasonerOutput;
|
|
83
|
+
};
|
|
74
84
|
export type PlanPart = {
|
|
75
85
|
type: "data-plan";
|
|
76
86
|
id: string;
|
|
@@ -498,8 +508,46 @@ export type ModelRetryPart = {
|
|
|
498
508
|
/** The HTTP status of the failed attempt. Absent for a failure with no status, for example a lost connection. */
|
|
499
509
|
status?: number;
|
|
500
510
|
};
|
|
511
|
+
/**
|
|
512
|
+
* A model request that the provider declined. `fallback`: another model answered the request, and `model` names it.
|
|
513
|
+
* `stopped`: no model answered, thus the reply of the request ended with the content filter.
|
|
514
|
+
*/
|
|
515
|
+
export type ModelRefusal = {
|
|
516
|
+
/** The id of the model response. */
|
|
517
|
+
id: string;
|
|
518
|
+
outcome: "fallback";
|
|
519
|
+
model: string;
|
|
520
|
+
} | {
|
|
521
|
+
/** The id of the model response. */
|
|
522
|
+
id: string;
|
|
523
|
+
outcome: "stopped";
|
|
524
|
+
};
|
|
525
|
+
export type ModelRefusalPart = {
|
|
526
|
+
type: "data-model-refusal";
|
|
527
|
+
} & ModelRefusal;
|
|
501
528
|
/** The kind of a durable work, and of the work that a pause belongs to. A sub-agent belongs to its turn, and a sandbox step to its run. */
|
|
502
529
|
export type WorkKind = "turn" | "run" | "profile";
|
|
530
|
+
/**
|
|
531
|
+
* What waits in a pause. A sub-agent names its agent id, and a sandbox step names its step id. `synthesis` and `run`
|
|
532
|
+
* are the two stages of a run workflow that wait: the synthesis, and the charge gate before any step starts.
|
|
533
|
+
*/
|
|
534
|
+
export type PauseWaiter = {
|
|
535
|
+
readonly kind: "turn";
|
|
536
|
+
} | {
|
|
537
|
+
readonly kind: "sub-agent";
|
|
538
|
+
readonly agentId: string;
|
|
539
|
+
} | {
|
|
540
|
+
readonly kind: "step";
|
|
541
|
+
readonly stepId: string;
|
|
542
|
+
} | {
|
|
543
|
+
readonly kind: "synthesis";
|
|
544
|
+
} | {
|
|
545
|
+
readonly kind: "run";
|
|
546
|
+
} | {
|
|
547
|
+
readonly kind: "profile";
|
|
548
|
+
};
|
|
549
|
+
/** How a wait ended: the deadline of `retrying`, the message of a resume, or a stop of the work. */
|
|
550
|
+
export type PauseEnd = "retry" | "resume" | "abort";
|
|
503
551
|
/**
|
|
504
552
|
* The state of one waiting workflow of paused work. Re-emitted under the same `id` when the wait ends, with the
|
|
505
553
|
* state `running`, so reconciling readers keep the latest state of each workflow.
|
|
@@ -511,17 +559,28 @@ export type PausePart = {
|
|
|
511
559
|
/** The id of the work: the turn, the run, or the profile workflow. A resume names it. */
|
|
512
560
|
workId: string;
|
|
513
561
|
workKind: WorkKind;
|
|
514
|
-
/**
|
|
562
|
+
/** What waits. Absent only for a wait that started before the harness recorded the waiter. */
|
|
563
|
+
waiter?: PauseWaiter;
|
|
564
|
+
/** `retrying`: the system sends the request again at `retryAt`. `paused`: only a resume ends the wait. `running`: the wait ended. */
|
|
515
565
|
state: "retrying" | "paused" | "running";
|
|
516
|
-
/**
|
|
517
|
-
cause
|
|
566
|
+
/** Why the work waited. The part that ends the wait keeps it. */
|
|
567
|
+
cause: PauseCause;
|
|
568
|
+
/** ISO 8601. The start of the wait. The part that ends the wait and a hold keep it. */
|
|
569
|
+
pausedAt: string;
|
|
518
570
|
/** The count of timer retries before this wait. Absent for the state `running`. */
|
|
519
571
|
attempt?: number;
|
|
520
572
|
/** ISO 8601. Present for the state `retrying` only. */
|
|
521
573
|
retryAt?: string;
|
|
574
|
+
/**
|
|
575
|
+
* ISO 8601. The time the wait ended. Present for the state `running` only. Absent when the replayed `pause.clear`
|
|
576
|
+
* step recorded no time.
|
|
577
|
+
*/
|
|
578
|
+
endedAt?: string;
|
|
579
|
+
/** How the wait ended. Present for the state `running` only. */
|
|
580
|
+
end?: PauseEnd;
|
|
522
581
|
};
|
|
523
582
|
/** The parts whose contracts this version of the package declares — the arms the part registry knows. */
|
|
524
|
-
export type KnownChatPart = PresentationPart | PlanPart | RunCardPart | PlanApprovalPart | FileReferencePart | AskPart | RunStartedPart | DagStatePart | StepActivityPart | ActivityEntryPart | SubAgentActivityPart | StepFileTreePart | StepOutputPart | StepSummaryPart | StepUsagePart | StepBlockedPart | RunSynthesisPart | SynthesisProgressPart | RunCompletedPart | RunFailedPart | ChildSessionStartedPart | ReportRenderedPart | CompactionPart | PausePart | ModelRetryPart;
|
|
583
|
+
export type KnownChatPart = PresentationPart | AnalogyReportPart | PlanPart | RunCardPart | PlanApprovalPart | FileReferencePart | AskPart | RunStartedPart | DagStatePart | StepActivityPart | ActivityEntryPart | SubAgentActivityPart | StepFileTreePart | StepOutputPart | StepSummaryPart | StepUsagePart | StepBlockedPart | RunSynthesisPart | SynthesisProgressPart | RunCompletedPart | RunFailedPart | ChildSessionStartedPart | ReportRenderedPart | CompactionPart | PausePart | ModelRetryPart | ModelRefusalPart;
|
|
525
584
|
/**
|
|
526
585
|
* A part of a known contract, or a part whose type this build does not know. An emitter newer than this
|
|
527
586
|
* build can lead the registry, and such a part already rides through the check boundary and the live view
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Categories:
|
|
6
6
|
* Presentation: data-presentation (agent-synthesized content),
|
|
7
|
-
* data-plan (plan by id), data-file-reference (files by path)
|
|
7
|
+
* data-plan (plan by id), data-file-reference (files by path),
|
|
8
|
+
* data-analogy-report (the result of generate_analogy_report)
|
|
8
9
|
* Approval: data-ask (a tool's pending user-approval prompt),
|
|
9
10
|
* data-plan-approval (the record of an approved plan)
|
|
10
11
|
* Compaction: data-compaction (a compaction of the conversation thread)
|
|
11
12
|
* Model retry: data-model-retry (a retry of a model request inside the retry envelope of the provider)
|
|
13
|
+
* Model refusal: data-model-refusal (a model request that the provider declined)
|
|
12
14
|
* Run lifecycle: run-started, dag-state, run-completed, run-failed, run-synthesis,
|
|
13
15
|
* synthesis-progress
|
|
14
16
|
* Per-step live: step-activity, step-file-tree
|
|
@@ -2,7 +2,7 @@ export { ORGAN_SYSTEMS, OrganSystemSchema, ORGAN_SYSTEM_LABELS, organSystemLabel
|
|
|
2
2
|
export type { OrganSystem } from "./organ-system.js";
|
|
3
3
|
export { SEVERITIES, SeveritySchema } from "./severity.js";
|
|
4
4
|
export type { Severity } from "./severity.js";
|
|
5
|
-
export type { PlanStep, PresentationContent, PresentationPart, PlanPart, RunCardPart, PlanApprovalPart, FileReferenceEntry, FileReferencePart, AskPart, RunStartedPart, StepStatus, DagStepState, PlanGraphStep, DagStatePart, StepPhase, StepActivityPart, ActivityEntryPart, SubAgentActivityEntry, SubAgentActivityPart, FileTreeEntry, StepFileTreePart, StepOutputFile, StepOutputPart, StepSummaryPart, StepUsagePart, StepBlockedPart, FindingReference, SynthesizedFinding, BiologicalTheme, RunSynthesisPart, SynthesisPhase, SynthesisProgressPart, RunCompletedFinding, RunCompletedPart, RunFailedPart, ChildSessionStartedPart, ReportRenderedPart, CompactionPart, PauseCause, WorkKind, PausePart, ModelRetryPart, ChatPart, KnownChatPart, } from "./chat-parts.js";
|
|
5
|
+
export type { PlanStep, PresentationContent, PresentationPart, AnalogyReportPart, PlanPart, RunCardPart, PlanApprovalPart, FileReferenceEntry, FileReferencePart, AskPart, RunStartedPart, StepStatus, DagStepState, PlanGraphStep, DagStatePart, StepPhase, StepActivityPart, ActivityEntryPart, SubAgentActivityEntry, SubAgentActivityPart, FileTreeEntry, StepFileTreePart, StepOutputFile, StepOutputPart, StepSummaryPart, StepUsagePart, StepBlockedPart, FindingReference, SynthesizedFinding, BiologicalTheme, RunSynthesisPart, SynthesisPhase, SynthesisProgressPart, RunCompletedFinding, RunCompletedPart, RunFailedPart, ChildSessionStartedPart, ReportRenderedPart, CompactionPart, PauseCause, PauseWaiter, PauseEnd, WorkKind, PausePart, ModelRetryPart, ModelRefusal, ModelRefusalPart, ChatPart, KnownChatPart, } from "./chat-parts.js";
|
|
6
6
|
export type { EventSource, TextDeltaEvent, IterationEvent, ToolStartedEvent, ToolFinishedEvent, ToolCallLeaf, ToolCallValue, FinishEvent, ChatErrorEvent, MessageStartEvent, ChatEvent, ChatFrame, ChatPartFrame, } from "./chat-events.js";
|
|
7
7
|
export type { TokenUsageRollup } from "./usage.js";
|
|
8
8
|
export { DATA_PROFILE_RUN_LITERAL } from "./data-profile.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const PART_REGISTRY = {
|
|
2
2
|
"data-presentation": { emitter: "conversation", consumer: "conversation", transient: false, reconciling: false },
|
|
3
|
+
"data-analogy-report": { emitter: "conversation", consumer: "conversation", transient: false, reconciling: false },
|
|
3
4
|
"data-plan": { emitter: "conversation", consumer: "conversation", transient: false, reconciling: false },
|
|
4
5
|
"data-run-card": { emitter: "conversation", consumer: "conversation", transient: false, reconciling: false },
|
|
5
6
|
"data-plan-approval": { emitter: "conversation", consumer: "conversation", transient: false, reconciling: false },
|
|
@@ -24,6 +25,7 @@ export const PART_REGISTRY = {
|
|
|
24
25
|
"data-compaction": { emitter: "conversation", consumer: "conversation", transient: true, reconciling: true },
|
|
25
26
|
"data-pause": { emitter: "workflow", consumer: "conversation", transient: false, reconciling: true },
|
|
26
27
|
"data-model-retry": { emitter: "conversation", consumer: "conversation", transient: true, reconciling: true },
|
|
28
|
+
"data-model-refusal": { emitter: "conversation", consumer: "conversation", transient: false, reconciling: false },
|
|
27
29
|
};
|
|
28
30
|
export function isTransient(type) {
|
|
29
31
|
return PART_REGISTRY[type].transient;
|
|
@@ -259,6 +259,7 @@ export declare const FinishEventSchema: z.ZodObject<{
|
|
|
259
259
|
callPath: z.ZodArray<z.ZodString>;
|
|
260
260
|
invocationId: z.ZodOptional<z.ZodString>;
|
|
261
261
|
}, z.core.$strip>;
|
|
262
|
+
finishReason: z.ZodString;
|
|
262
263
|
usage: z.ZodOptional<z.ZodObject<{
|
|
263
264
|
inputTokens: z.ZodOptional<z.ZodNumber>;
|
|
264
265
|
outputTokens: z.ZodOptional<z.ZodNumber>;
|
|
@@ -484,6 +485,7 @@ export declare const ChatEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
484
485
|
callPath: z.ZodArray<z.ZodString>;
|
|
485
486
|
invocationId: z.ZodOptional<z.ZodString>;
|
|
486
487
|
}, z.core.$strip>;
|
|
488
|
+
finishReason: z.ZodString;
|
|
487
489
|
usage: z.ZodOptional<z.ZodObject<{
|
|
488
490
|
inputTokens: z.ZodOptional<z.ZodNumber>;
|
|
489
491
|
outputTokens: z.ZodOptional<z.ZodNumber>;
|