@inflexa-ai/harness 0.48.3 → 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.js +1 -0
- package/dist/app/chat-turn.d.ts +4 -2
- package/dist/app/chat-turn.js +46 -46
- package/dist/app/work.d.ts +7 -3
- package/dist/app/work.js +17 -16
- package/dist/contracts/chat-parts.d.ts +46 -5
- package/dist/contracts/chat-parts.js +2 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/part-registry.js +1 -0
- package/dist/contracts/schemas/chat-parts.d.ts +168 -4
- package/dist/contracts/schemas/chat-parts.js +21 -1
- 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/run-loop.js +23 -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 +4 -2
- package/dist/memory/conversation-display-storage.js +19 -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/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
|
+
}
|
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) {
|
|
@@ -295,10 +293,12 @@ function failureReasonOf(err) {
|
|
|
295
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. */
|
|
296
294
|
function closingNoteOf(outcome, recorder) {
|
|
297
295
|
switch (outcome.status) {
|
|
298
|
-
case "failed":
|
|
299
|
-
|
|
296
|
+
case "failed": {
|
|
297
|
+
const note = conversationRecordTurn(failureNote(outcome.reason));
|
|
298
|
+
return { ...note, displayMessages: [...recorder.openRoundPauses(), ...note.displayMessages] };
|
|
299
|
+
}
|
|
300
300
|
case "aborted":
|
|
301
|
-
return interruptionNote();
|
|
301
|
+
return interruptionNote(recorder);
|
|
302
302
|
case "done":
|
|
303
303
|
return outcome.finish.reason === "content-filter" ? declineNote(recorder) : undefined;
|
|
304
304
|
default: {
|
|
@@ -320,13 +320,13 @@ function declineNote(recorder) {
|
|
|
320
320
|
}
|
|
321
321
|
/**
|
|
322
322
|
* The note of an aborted turn. Without it, the next turn reads the request of the aborted turn as open, and
|
|
323
|
-
* its reply also answers that request. The transcript shows the interruption from the turn record
|
|
324
|
-
* 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.
|
|
325
325
|
*/
|
|
326
|
-
function interruptionNote() {
|
|
326
|
+
function interruptionNote(recorder) {
|
|
327
327
|
return {
|
|
328
328
|
modelMessages: [syntheticUserMessage("[The user interrupted the request above. Do not continue it unless the user asks you to.]")],
|
|
329
|
-
displayMessages:
|
|
329
|
+
displayMessages: recorder.openRoundPauses(),
|
|
330
330
|
};
|
|
331
331
|
}
|
|
332
332
|
function failureNote(reason) {
|
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 {
|
|
@@ -4,7 +4,8 @@
|
|
|
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)
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
* Activity history: activity-entry (run stream), subagent-activity (conversation)
|
|
17
18
|
* Per-step final: step-output, step-summary, step-usage
|
|
18
19
|
*/
|
|
20
|
+
import type { AnalogicalReasonerOutput } from "./analogy-report.js";
|
|
19
21
|
import type { ToolCallOutcome } from "./chat-events.js";
|
|
20
22
|
import type { TokenUsageRollup } from "./usage.js";
|
|
21
23
|
export type PlanStep = {
|
|
@@ -72,6 +74,13 @@ export type PresentationPart = {
|
|
|
72
74
|
title?: string;
|
|
73
75
|
content: PresentationContent;
|
|
74
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
|
+
};
|
|
75
84
|
export type PlanPart = {
|
|
76
85
|
type: "data-plan";
|
|
77
86
|
id: string;
|
|
@@ -518,6 +527,27 @@ export type ModelRefusalPart = {
|
|
|
518
527
|
} & ModelRefusal;
|
|
519
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. */
|
|
520
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";
|
|
521
551
|
/**
|
|
522
552
|
* The state of one waiting workflow of paused work. Re-emitted under the same `id` when the wait ends, with the
|
|
523
553
|
* state `running`, so reconciling readers keep the latest state of each workflow.
|
|
@@ -529,17 +559,28 @@ export type PausePart = {
|
|
|
529
559
|
/** The id of the work: the turn, the run, or the profile workflow. A resume names it. */
|
|
530
560
|
workId: string;
|
|
531
561
|
workKind: WorkKind;
|
|
532
|
-
/**
|
|
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. */
|
|
533
565
|
state: "retrying" | "paused" | "running";
|
|
534
|
-
/**
|
|
535
|
-
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;
|
|
536
570
|
/** The count of timer retries before this wait. Absent for the state `running`. */
|
|
537
571
|
attempt?: number;
|
|
538
572
|
/** ISO 8601. Present for the state `retrying` only. */
|
|
539
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;
|
|
540
581
|
};
|
|
541
582
|
/** The parts whose contracts this version of the package declares — the arms the part registry knows. */
|
|
542
|
-
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 | ModelRefusalPart;
|
|
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;
|
|
543
584
|
/**
|
|
544
585
|
* A part of a known contract, or a part whose type this build does not know. An emitter newer than this
|
|
545
586
|
* build can lead the registry, and such a part already rides through the check boundary and the live view
|
|
@@ -4,7 +4,8 @@
|
|
|
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)
|
|
@@ -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, ModelRefusal, ModelRefusalPart, 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 },
|