@inflexa-ai/harness 0.44.0 → 0.45.1
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/conversation-agent.d.ts +10 -0
- package/dist/agents/conversation-agent.js +19 -1
- package/dist/agents/sandbox/shared.d.ts +23 -2
- package/dist/agents/sandbox/shared.js +51 -18
- package/dist/agents/sandbox/translational-safety-agent.js +26 -0
- package/dist/agents/sandbox/types.d.ts +24 -1
- package/dist/agents/sandbox-catalog.d.ts +14 -0
- package/dist/agents/sandbox-catalog.js +19 -0
- package/dist/app/approve-plan.d.ts +51 -0
- package/dist/app/approve-plan.js +84 -0
- package/dist/app/chat-turn.js +6 -3
- package/dist/app/data-profile-view.d.ts +1 -1
- package/dist/app/run-activity.js +4 -6
- package/dist/config/environment-stores.d.ts +4 -2
- package/dist/contracts/chat-events.d.ts +14 -2
- package/dist/contracts/chat-events.js +1 -1
- package/dist/contracts/chat-frame.d.ts +7 -0
- package/dist/contracts/chat-frame.js +24 -6
- package/dist/contracts/chat-parts.d.ts +73 -2
- package/dist/contracts/chat-parts.js +3 -1
- package/dist/contracts/index.d.ts +4 -2
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/part-registry.js +3 -0
- package/dist/contracts/schemas/chat-events.d.ts +38 -0
- package/dist/contracts/schemas/chat-events.js +8 -0
- package/dist/contracts/schemas/chat-parts.d.ts +239 -15
- package/dist/contracts/schemas/chat-parts.js +80 -12
- package/dist/contracts/schemas/index.d.ts +2 -2
- package/dist/contracts/schemas/index.js +2 -2
- package/dist/contracts/subagent-activity.d.ts +12 -0
- package/dist/contracts/subagent-activity.js +76 -0
- package/dist/execution/artifact-metadata.js +1 -3
- package/dist/execution/dbos-workflow-purger.d.ts +2 -0
- package/dist/execution/dbos-workflow-purger.js +21 -14
- package/dist/execution/run-canceler.d.ts +33 -11
- package/dist/execution/run-canceler.js +130 -28
- package/dist/execution/run-end-record.d.ts +30 -0
- package/dist/execution/run-end-record.js +27 -0
- package/dist/execution/run-event-parts.d.ts +6 -5
- package/dist/execution/run-event-parts.js +6 -5
- package/dist/execution/run-event-stream.d.ts +18 -6
- package/dist/execution/run-event-stream.js +67 -17
- package/dist/execution/run-synthesis.d.ts +21 -0
- package/dist/execution/run-synthesis.js +77 -3
- package/dist/index.d.ts +28 -10
- package/dist/index.js +26 -2
- package/dist/lib/hooks.d.ts +1 -1
- package/dist/lib/metrics.d.ts +2 -2
- package/dist/loop/call-guard.d.ts +50 -0
- package/dist/loop/call-guard.js +90 -0
- package/dist/loop/compaction.d.ts +5 -0
- package/dist/loop/compaction.js +17 -0
- package/dist/loop/run-agent.d.ts +9 -0
- package/dist/loop/run-agent.js +16 -1
- package/dist/loop/run-to-terminal.js +4 -1
- package/dist/loop/types.d.ts +8 -0
- package/dist/memory/card-builders.d.ts +11 -0
- package/dist/memory/card-builders.js +35 -21
- package/dist/memory/conversation-display-recorder.d.ts +2 -2
- package/dist/memory/conversation-display-recorder.js +38 -4
- package/dist/memory/conversation-display-storage.d.ts +9 -2
- package/dist/memory/conversation-display-storage.js +18 -3
- package/dist/memory/thread-history.d.ts +42 -8
- package/dist/memory/thread-history.js +164 -45
- package/dist/memory/thread-store.d.ts +13 -0
- package/dist/memory/thread-store.js +15 -1
- package/dist/prompts/briefing.d.ts +57 -3
- package/dist/prompts/briefing.js +129 -2
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +3 -1
- package/dist/prompts/planner.js +13 -2
- package/dist/provenance/seam.d.ts +39 -20
- package/dist/provenance/seam.js +7 -5
- package/dist/providers/ai-sdk.js +93 -11
- package/dist/providers/types.d.ts +6 -0
- package/dist/reference-data/catalog.js +141 -2
- package/dist/report-model/authoring-grammar.d.ts +26 -26
- package/dist/report-model/pin-snapshot.d.ts +1 -1
- package/dist/report-model/pin-snapshot.js +2 -50
- package/dist/runtime/assemble.d.ts +8 -1
- package/dist/runtime/assemble.js +11 -0
- package/dist/runtime/boot.d.ts +3 -0
- package/dist/runtime/boot.js +10 -1
- package/dist/sandbox/image-packages.d.ts +26 -4
- package/dist/sandbox/image-packages.js +18 -4
- package/dist/sandbox/sandbox-step-translate.d.ts +23 -0
- package/dist/sandbox/sandbox-step-translate.js +24 -0
- package/dist/schemas/artifact-manifest.d.ts +1 -1
- package/dist/schemas/artifact-manifest.js +11 -9
- package/dist/schemas/data-profile-schemas.d.ts +12 -12
- package/dist/schemas/plan-schemas.d.ts +75 -21
- package/dist/schemas/plan-schemas.js +1 -1
- package/dist/schemas/run-synthesis.d.ts +2 -2
- package/dist/schemas/workflow-state.d.ts +113 -0
- package/dist/schemas/workflow-state.js +54 -0
- package/dist/state/analyses-metrics.d.ts +79 -7
- package/dist/state/analyses-metrics.js +141 -6
- package/dist/state/artifacts.d.ts +66 -3
- package/dist/state/artifacts.js +85 -7
- package/dist/state/data-profile.d.ts +4 -2
- package/dist/state/data-profile.js +7 -5
- package/dist/state/index.d.ts +9 -8
- package/dist/state/index.js +5 -5
- package/dist/state/migrations/20261012120000_index_the_runs_by_start.d.ts +8 -0
- package/dist/state/migrations/20261012120000_index_the_runs_by_start.js +12 -0
- package/dist/state/migrations/20261013120000_index_the_ended_runs_and_the_recent_threads.d.ts +8 -0
- package/dist/state/migrations/20261013120000_index_the_ended_runs_and_the_recent_threads.js +13 -0
- package/dist/state/migrations/20261013180000_narrow_the_active_run_index.d.ts +8 -0
- package/dist/state/migrations/20261013180000_narrow_the_active_run_index.js +13 -0
- package/dist/state/migrations/20261014120000_index_the_plans_by_parent_and_the_runs_by_plan.d.ts +8 -0
- package/dist/state/migrations/20261014120000_index_the_plans_by_parent_and_the_runs_by_plan.js +13 -0
- package/dist/state/migrations/index.js +8 -0
- package/dist/state/plans.d.ts +21 -0
- package/dist/state/plans.js +25 -0
- package/dist/state/runs.d.ts +79 -14
- package/dist/state/runs.js +150 -33
- package/dist/state/schema.d.ts +2 -0
- package/dist/state/step-executions.d.ts +9 -0
- package/dist/state/step-executions.js +22 -0
- package/dist/tasks/data-profile-activity.d.ts +7 -1
- package/dist/tasks/data-profile-activity.js +18 -9
- package/dist/tasks/data-profile.d.ts +15 -1
- package/dist/tasks/data-profile.js +16 -11
- package/dist/tools/ad-hoc-router.d.ts +2 -0
- package/dist/tools/ad-hoc-router.js +18 -2
- package/dist/tools/approval/gateway.js +28 -5
- package/dist/tools/capability-data.d.ts +36 -0
- package/dist/tools/capability-data.js +73 -0
- package/dist/tools/display/show-user.d.ts +1 -1
- package/dist/tools/execute-analysis.d.ts +80 -1
- package/dist/tools/execute-analysis.js +352 -110
- package/dist/tools/knowledge/check.d.ts +60 -0
- package/dist/tools/knowledge/check.js +92 -0
- package/dist/tools/knowledge/client.d.ts +382 -0
- package/dist/tools/knowledge/client.js +302 -0
- package/dist/tools/knowledge/environment.d.ts +78 -0
- package/dist/tools/knowledge/environment.js +105 -0
- package/dist/tools/knowledge/index.d.ts +49 -0
- package/dist/tools/knowledge/index.js +70 -0
- package/dist/tools/knowledge/local-slots.d.ts +62 -0
- package/dist/tools/knowledge/local-slots.js +177 -0
- package/dist/tools/knowledge/pin.d.ts +14 -0
- package/dist/tools/knowledge/pin.js +17 -0
- package/dist/tools/knowledge/recommend.d.ts +92 -0
- package/dist/tools/knowledge/recommend.js +98 -0
- package/dist/tools/knowledge/situation.d.ts +143 -0
- package/dist/tools/knowledge/situation.js +164 -0
- package/dist/tools/knowledge/skeleton.d.ts +69 -0
- package/dist/tools/knowledge/skeleton.js +202 -0
- package/dist/tools/knowledge/template.d.ts +137 -0
- package/dist/tools/knowledge/template.js +267 -0
- package/dist/tools/research/generate-plan.d.ts +23 -0
- package/dist/tools/research/generate-plan.js +203 -12
- package/dist/tools/research/guarded-address.d.ts +20 -0
- package/dist/tools/research/guarded-address.js +148 -0
- package/dist/tools/research/inspect-data-profile.d.ts +1 -1
- package/dist/tools/research/inspect-run.d.ts +1 -1
- package/dist/tools/research/inspect-run.js +7 -7
- package/dist/tools/research/searxng-engine.d.ts +40 -0
- package/dist/tools/research/searxng-engine.js +120 -0
- package/dist/tools/research/web-fetch.d.ts +124 -0
- package/dist/tools/research/web-fetch.js +627 -0
- package/dist/tools/research/web-search.d.ts +75 -0
- package/dist/tools/research/web-search.js +112 -0
- package/dist/tools/sandbox/list-available-packages.d.ts +7 -4
- package/dist/tools/sandbox/list-available-packages.js +12 -4
- package/dist/tools/sandbox/list-available-refs.d.ts +21 -0
- package/dist/tools/sandbox/list-available-refs.js +60 -3
- package/dist/tools/sandbox/report-blocker.js +5 -1
- package/dist/tools/terminal-text.d.ts +11 -0
- package/dist/tools/terminal-text.js +19 -0
- package/dist/tools/workspace/decision-record.d.ts +81 -0
- package/dist/tools/workspace/decision-record.js +117 -0
- package/dist/tools/workspace/edit-file.d.ts +3 -1
- package/dist/tools/workspace/edit-file.js +20 -2
- package/dist/tools/workspace/execute-command.d.ts +17 -0
- package/dist/tools/workspace/execute-command.js +28 -1
- package/dist/tools/workspace/index.d.ts +1 -0
- package/dist/tools/workspace/index.js +1 -0
- package/dist/tools/workspace/mutator.d.ts +1 -1
- package/dist/workflows/execute-analysis.d.ts +62 -21
- package/dist/workflows/execute-analysis.js +307 -79
- package/dist/workflows/plan/graph.d.ts +16 -0
- package/dist/workflows/plan/graph.js +36 -0
- package/dist/workflows/sandbox-step.d.ts +12 -0
- package/dist/workflows/sandbox-step.js +14 -12
- package/package.json +5 -1
|
@@ -37,6 +37,28 @@ import { describeDbError } from "../lib/db-result.js";
|
|
|
37
37
|
import { queryRun } from "../state/runs.js";
|
|
38
38
|
import { queryStepsByRun } from "../state/step-executions.js";
|
|
39
39
|
import { foldRunEventParts, parseRunEventPart } from "./run-event-parts.js";
|
|
40
|
+
const canceledPart = (runId) => ({ type: "data-run-failed", runId, error: "Run canceled", reason: "canceled" });
|
|
41
|
+
/** The terminal part that a settled run row gives, or `null` when the body of the run writes the part. */
|
|
42
|
+
function partOfRow(runId, row) {
|
|
43
|
+
switch (row.status) {
|
|
44
|
+
case "canceled":
|
|
45
|
+
return canceledPart(runId);
|
|
46
|
+
case "suspended_insufficient_funds":
|
|
47
|
+
return row.error === null
|
|
48
|
+
? { type: "data-run-failed", runId, error: "Run suspended" }
|
|
49
|
+
: { type: "data-run-failed", runId, error: row.error, reason: row.error };
|
|
50
|
+
case "failed":
|
|
51
|
+
return { type: "data-run-failed", runId, error: row.error ?? "Run failed" };
|
|
52
|
+
case "completed":
|
|
53
|
+
case "partial":
|
|
54
|
+
case "running":
|
|
55
|
+
return null;
|
|
56
|
+
default: {
|
|
57
|
+
const _exhaustive = row.status;
|
|
58
|
+
throw new Error(`unhandled run status: ${String(_exhaustive)}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
40
62
|
/** Durable stream key every run producer writes its parts under. */
|
|
41
63
|
const RUN_EVENT_STREAM_KEY = "events";
|
|
42
64
|
/**
|
|
@@ -58,8 +80,8 @@ const CHILD_DISCOVERY_INTERVAL_MS = 1_000;
|
|
|
58
80
|
* the stream can end before the row changes, and the later reads wait out that
|
|
59
81
|
* window.
|
|
60
82
|
*/
|
|
61
|
-
const
|
|
62
|
-
const
|
|
83
|
+
const TERMINAL_ROW_READ_ATTEMPTS = 4;
|
|
84
|
+
const TERMINAL_ROW_READ_INTERVAL_MS = 500;
|
|
63
85
|
/**
|
|
64
86
|
* Build the DBOS-backed realization of {@link RunEventStream}.
|
|
65
87
|
*
|
|
@@ -72,7 +94,8 @@ const CANCELED_ROW_READ_INTERVAL_MS = 500;
|
|
|
72
94
|
*/
|
|
73
95
|
export function createRunEventStream(deps) {
|
|
74
96
|
const baseLogger = (deps.logger ?? createNoopLogger()).named("run-event-stream");
|
|
75
|
-
const
|
|
97
|
+
const readRun = deps.readRun ?? ((runId) => queryRun(deps.pool, runId).map((row) => (row === null ? null : { status: row.status, error: row.error })));
|
|
98
|
+
const readWorkflowStatus = deps.readWorkflowStatus ?? (async (runId) => (await DBOS.getWorkflowStatus(runId))?.status ?? null);
|
|
76
99
|
const waitForRowRead = deps.sleep ?? sleep;
|
|
77
100
|
return {
|
|
78
101
|
async subscribe({ runId, onPart, signal }) {
|
|
@@ -210,37 +233,64 @@ export function createRunEventStream(deps) {
|
|
|
210
233
|
await Promise.race([sleep(CHILD_DISCOVERY_INTERVAL_MS), aborted, parentRead]);
|
|
211
234
|
}
|
|
212
235
|
})();
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
236
|
+
const workflowTerminalPart = async () => {
|
|
237
|
+
let workflowStatus;
|
|
238
|
+
try {
|
|
239
|
+
workflowStatus = await readWorkflowStatus(runId);
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
logger.warn("workflow status read failed", logger.errorFields(err));
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
switch (workflowStatus) {
|
|
246
|
+
case "CANCELLED":
|
|
247
|
+
return canceledPart(runId);
|
|
248
|
+
case "ERROR":
|
|
249
|
+
case "MAX_RECOVERY_ATTEMPTS_EXCEEDED":
|
|
250
|
+
case "SUCCESS":
|
|
251
|
+
return { type: "data-run-failed", runId, error: "Run ended without a terminal status" };
|
|
252
|
+
case "PENDING":
|
|
253
|
+
case "ENQUEUED":
|
|
254
|
+
case "DELAYED":
|
|
255
|
+
logger.warn("run streams ended while the workflow is active", { workflowStatus });
|
|
256
|
+
return null;
|
|
257
|
+
default:
|
|
258
|
+
// No workflow: the launch did not start it, or it never started. An unknown status gives no part too.
|
|
259
|
+
if (workflowStatus !== null)
|
|
260
|
+
logger.warn("unknown workflow status", { workflowStatus });
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
const missingTerminalPart = async () => {
|
|
265
|
+
let rowReadsRunning = false;
|
|
266
|
+
for (let attempt = 1; attempt <= TERMINAL_ROW_READ_ATTEMPTS; attempt++) {
|
|
267
|
+
const read = await readRun(runId);
|
|
268
|
+
rowReadsRunning = false;
|
|
216
269
|
if (read.isErr()) {
|
|
217
270
|
logger.warn("run row read failed", { err: describeDbError(read.error) });
|
|
218
271
|
}
|
|
219
272
|
else {
|
|
220
|
-
const status = read.value;
|
|
221
|
-
if (status === "canceled")
|
|
222
|
-
return { type: "data-run-failed", runId, error: "Run canceled", reason: "canceled" };
|
|
223
273
|
// The launch writes the row before the workflow starts, thus
|
|
224
274
|
// no row means that the id is not an analysis run.
|
|
225
|
-
if (
|
|
226
|
-
return null;
|
|
227
|
-
// Only an active row can still become `canceled`.
|
|
228
|
-
if (status !== "running" && status !== "suspended_insufficient_funds")
|
|
275
|
+
if (read.value === null)
|
|
229
276
|
return null;
|
|
277
|
+
if (read.value.status !== "running")
|
|
278
|
+
return partOfRow(runId, read.value);
|
|
279
|
+
rowReadsRunning = true;
|
|
230
280
|
}
|
|
231
|
-
if (attempt <
|
|
232
|
-
await Promise.race([waitForRowRead(
|
|
281
|
+
if (attempt < TERMINAL_ROW_READ_ATTEMPTS)
|
|
282
|
+
await Promise.race([waitForRowRead(TERMINAL_ROW_READ_INTERVAL_MS), aborted]);
|
|
233
283
|
if (signal.aborted)
|
|
234
284
|
return null;
|
|
235
285
|
}
|
|
236
|
-
return null;
|
|
286
|
+
return rowReadsRunning ? workflowTerminalPart() : null;
|
|
237
287
|
};
|
|
238
288
|
const settled = (async () => {
|
|
239
289
|
await discovery;
|
|
240
290
|
await parentRead;
|
|
241
291
|
await Promise.all(childReads);
|
|
242
292
|
if (!sawTerminal && !signal.aborted) {
|
|
243
|
-
const terminal = await
|
|
293
|
+
const terminal = await missingTerminalPart();
|
|
244
294
|
if (terminal !== null)
|
|
245
295
|
pending.push(terminal);
|
|
246
296
|
}
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* `failed` part. We do not throw here for routine submitter rejections.
|
|
25
25
|
*/
|
|
26
26
|
import { type RunSynthesis } from "../schemas/run-synthesis.js";
|
|
27
|
+
import type { RunSynthesisPart } from "../contracts/chat-parts.js";
|
|
27
28
|
import type { StepSummary } from "../schemas/step-summary.js";
|
|
28
29
|
import type { ToolOutputStore } from "../loop/tool-output.js";
|
|
29
30
|
import type { ChatDataPart, EmitFn } from "../loop/types.js";
|
|
@@ -119,6 +120,26 @@ export interface PersistSynthesisInput {
|
|
|
119
120
|
readonly synthesis: RunSynthesis;
|
|
120
121
|
}
|
|
121
122
|
export declare function persistSynthesis(args: PersistSynthesisInput): Promise<string>;
|
|
123
|
+
export interface ReadSynthesisInput {
|
|
124
|
+
/** Absolute host root of the analysis's workspace tree. */
|
|
125
|
+
readonly workspaceRoot: string;
|
|
126
|
+
readonly runId: string;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Read the text of a synthesis record under the cap, or give `undefined` when the bytes do not come back.
|
|
130
|
+
*
|
|
131
|
+
* The read takes a bounded byte window, thus a file of any size costs the cap. A run id that can escape
|
|
132
|
+
* the run tree, an absent file, a genuine read fault, and a file over the cap each give `undefined`,
|
|
133
|
+
* because a collection reports no error for one record.
|
|
134
|
+
*/
|
|
135
|
+
export declare function readSynthesisText(args: ReadSynthesisInput): Promise<string | undefined>;
|
|
136
|
+
/**
|
|
137
|
+
* The validated synthesis record of a run as the flat `data-run-synthesis` part, the shape that the run
|
|
138
|
+
* stream carries for the same record. It gives `null` for each fault of {@link readSynthesisText}, for text
|
|
139
|
+
* that is not JSON, and for JSON that fails {@link RunSynthesisSchema}. The run row stays the authority on
|
|
140
|
+
* whether a synthesis exists, thus the read does not consult it.
|
|
141
|
+
*/
|
|
142
|
+
export declare function readRunSynthesis(args: ReadSynthesisInput): Promise<RunSynthesisPart | null>;
|
|
122
143
|
export declare function formatSynthesisEmbeddingText(synthesis: RunSynthesis): string;
|
|
123
144
|
/**
|
|
124
145
|
* Build the `data-run-synthesis` chat part the parent stream emits when the
|
|
@@ -23,16 +23,18 @@
|
|
|
23
23
|
* resolves with `{ kind: "failed" }`, the wrapper emits a synthesis-progress
|
|
24
24
|
* `failed` part. We do not throw here for routine submitter rejections.
|
|
25
25
|
*/
|
|
26
|
-
import { readFile } from "node:fs/promises";
|
|
26
|
+
import { open, readFile } from "node:fs/promises";
|
|
27
27
|
import { join } from "node:path";
|
|
28
28
|
import { ok } from "neverthrow";
|
|
29
29
|
import { z } from "zod";
|
|
30
30
|
import { writeFileWithinRoot } from "../lib/fs-helpers.js";
|
|
31
|
+
import { tryFs } from "../lib/fs-result.js";
|
|
31
32
|
import { hintForZodIssue } from "../lib/zod-issues.js";
|
|
32
33
|
import { BiologicalThemeSchema, KeyReferenceSchema, RunSynthesisSchema, SynthesizedFindingSchema, } from "../schemas/run-synthesis.js";
|
|
33
34
|
import { synthesisAgentPrompt } from "../prompts/synthesis-agent.js";
|
|
34
35
|
import { composeSystemPrompt } from "../agents/system-prompt.js";
|
|
35
|
-
import {
|
|
36
|
+
import { flattenDataPart } from "../contracts/chat-frame.js";
|
|
37
|
+
import { isSafeId, runDir } from "../workspace/paths.js";
|
|
36
38
|
import { decodeObjectString } from "../lib/zod-issues.js";
|
|
37
39
|
import { runToTerminal } from "../loop/run-to-terminal.js";
|
|
38
40
|
import { passthroughStep } from "../loop/run-step.js";
|
|
@@ -435,15 +437,87 @@ export async function loadStepSummariesFromDisk(args) {
|
|
|
435
437
|
}
|
|
436
438
|
return out;
|
|
437
439
|
}
|
|
440
|
+
/** The file name of the synthesis record inside the run directory. */
|
|
441
|
+
const SYNTHESIS_FILE = "synthesis.json";
|
|
442
|
+
/**
|
|
443
|
+
* The cap of one synthesis read. A synthesis record is small by construction, and the cap bounds the
|
|
444
|
+
* cost of a file that is not. A file over the cap gives no text, because a cut JSON text cannot parse.
|
|
445
|
+
*/
|
|
446
|
+
const SYNTHESIS_CAP_BYTES = 1024 * 1024;
|
|
438
447
|
export async function persistSynthesis(args) {
|
|
439
448
|
// Confine to the run directory, not the whole workspace: synthesis.json sits
|
|
440
449
|
// at runs/{runId}/, above any step's RW mount and never in the hard-linked
|
|
441
450
|
// `data/` inputs.
|
|
442
451
|
const runRoot = join(args.workspaceRoot, runDir(args.runId));
|
|
443
|
-
const path = join(runRoot,
|
|
452
|
+
const path = join(runRoot, SYNTHESIS_FILE);
|
|
444
453
|
await writeFileWithinRoot(runRoot, path, JSON.stringify(args.synthesis, null, 2));
|
|
445
454
|
return path;
|
|
446
455
|
}
|
|
456
|
+
/**
|
|
457
|
+
* Read the text of a synthesis record under the cap, or give `undefined` when the bytes do not come back.
|
|
458
|
+
*
|
|
459
|
+
* The read takes a bounded byte window, thus a file of any size costs the cap. A run id that can escape
|
|
460
|
+
* the run tree, an absent file, a genuine read fault, and a file over the cap each give `undefined`,
|
|
461
|
+
* because a collection reports no error for one record.
|
|
462
|
+
*/
|
|
463
|
+
export async function readSynthesisText(args) {
|
|
464
|
+
// The path builder refuses an id that can escape the run tree with a throw. The guard keeps the read on
|
|
465
|
+
// the value channel.
|
|
466
|
+
if (!isSafeId(args.runId)) {
|
|
467
|
+
return undefined;
|
|
468
|
+
}
|
|
469
|
+
const absolute = join(args.workspaceRoot, runDir(args.runId), SYNTHESIS_FILE);
|
|
470
|
+
return tryFs("runSynthesis.readText", async () => {
|
|
471
|
+
const handle = await open(absolute, "r");
|
|
472
|
+
try {
|
|
473
|
+
// The window takes one byte more than the cap. A read that fills it states that the file
|
|
474
|
+
// holds more bytes than the cap admits, thus the two conditions stay apart.
|
|
475
|
+
const buffer = Buffer.alloc(SYNTHESIS_CAP_BYTES + 1);
|
|
476
|
+
// One read of a network-backed file can give fewer bytes than the window holds. Such a
|
|
477
|
+
// short read cuts the text in the middle of the JSON, and the record then parses to no
|
|
478
|
+
// key at all. Thus the loop reads again from the offset that the last read reached, and
|
|
479
|
+
// it stops when the window fills or when a read gives no byte.
|
|
480
|
+
let filled = 0;
|
|
481
|
+
while (filled < buffer.length) {
|
|
482
|
+
const { bytesRead } = await handle.read(buffer, filled, buffer.length - filled, filled);
|
|
483
|
+
if (bytesRead === 0) {
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
filled += bytesRead;
|
|
487
|
+
}
|
|
488
|
+
return filled > SYNTHESIS_CAP_BYTES ? undefined : buffer.subarray(0, filled).toString("utf8");
|
|
489
|
+
}
|
|
490
|
+
finally {
|
|
491
|
+
await handle.close();
|
|
492
|
+
}
|
|
493
|
+
}, { path: absolute, onAbsent: () => undefined }).unwrapOr(undefined);
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* The validated synthesis record of a run as the flat `data-run-synthesis` part, the shape that the run
|
|
497
|
+
* stream carries for the same record. It gives `null` for each fault of {@link readSynthesisText}, for text
|
|
498
|
+
* that is not JSON, and for JSON that fails {@link RunSynthesisSchema}. The run row stays the authority on
|
|
499
|
+
* whether a synthesis exists, thus the read does not consult it.
|
|
500
|
+
*/
|
|
501
|
+
export async function readRunSynthesis(args) {
|
|
502
|
+
const text = await readSynthesisText(args);
|
|
503
|
+
if (text === undefined) {
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
let json;
|
|
507
|
+
try {
|
|
508
|
+
json = JSON.parse(text);
|
|
509
|
+
}
|
|
510
|
+
catch {
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
const parsed = RunSynthesisSchema.safeParse(json);
|
|
514
|
+
if (!parsed.success) {
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
// The payload comes from a validated `RunSynthesis`, and the builder sets each field of the part contract.
|
|
518
|
+
// The tests parse the result with `RunSynthesisPartSchema`.
|
|
519
|
+
return flattenDataPart(buildRunSynthesisPart(args.runId, parsed.data));
|
|
520
|
+
}
|
|
447
521
|
// ── Embedding text ──────────────────────────────────────────────────
|
|
448
522
|
export function formatSynthesisEmbeddingText(synthesis) {
|
|
449
523
|
return [
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ export { resolveCompositionEyes } from "./runtime/assemble.js";
|
|
|
16
16
|
export type { ThreadAgentResolver, UnregisteredThreadType } from "./runtime/assemble.js";
|
|
17
17
|
export { createReportSessionSpawn } from "./app/spawn-report-session.js";
|
|
18
18
|
export type { ReportSessionSpawn, ReportSessionSpawnDeps, ReportSessionPaging, SpawnRefusal, ReportBrief, ReportSessionDelta, NewestReportChild, } from "./app/spawn-report-session.js";
|
|
19
|
+
export { launchPlanRun } from "./tools/execute-analysis.js";
|
|
20
|
+
export type { LaunchPlanRunDeps, LaunchPlanRunInput, LaunchPlanRunRefusal, PlanLaunch } from "./tools/execute-analysis.js";
|
|
21
|
+
export { approvePlan } from "./app/approve-plan.js";
|
|
22
|
+
export type { ApprovePlanDeps, ApprovePlanInput, ApprovePlanRefusal, PlanApproval } from "./app/approve-plan.js";
|
|
19
23
|
export type { EnsureSessionStateResult } from "./app/report-session-runtime.js";
|
|
20
24
|
export { bootHarness } from "./runtime/boot.js";
|
|
21
25
|
export type { BootHarnessDeps, BootedHarness } from "./runtime/boot.js";
|
|
@@ -48,10 +52,14 @@ export type { WorkflowPurger } from "./execution/workflow-purger.js";
|
|
|
48
52
|
export { createRunCanceler, UnknownRunError } from "./execution/run-canceler.js";
|
|
49
53
|
export type { RunCanceler, RunCancelerDeps, CancelRunResult } from "./execution/run-canceler.js";
|
|
50
54
|
export { defineTool, isToolError } from "./tools/define-tool.js";
|
|
55
|
+
export { createHttpKnowledgeClient, createKnowledgeRecommendTool, createKnowledgeCheckTool, createKnowledgeTools, knowledgeToolDefinitionHash, } from "./tools/knowledge/index.js";
|
|
56
|
+
export type { KnowledgeClient, KnowledgeSituation, HttpKnowledgeClientConfig } from "./tools/knowledge/index.js";
|
|
51
57
|
export type { Tool, ToolDefinition, ToolContext, ToolError } from "./tools/define-tool.js";
|
|
52
58
|
export { normalizeDetail, DETAIL_MAX_LENGTH } from "./loop/tool-detail.js";
|
|
53
59
|
export { createCitationResolver } from "./citations/resolve.js";
|
|
54
60
|
export type { CitationResolverConfig, CitationResolverDependencies, CitationResolverSourceConfig } from "./citations/resolve.js";
|
|
61
|
+
export { createSearxngEngine } from "./tools/research/searxng-engine.js";
|
|
62
|
+
export type { WebSearchEngine } from "./tools/research/web-search.js";
|
|
55
63
|
export { CitationKindSchema, CitationInputSchema, CitationSourceSchema, CitationSourceStatusSchema, CitationResolutionResultSchema, CitationVerdictSchema, CitationCoverageSchema, ComparisonStatusSchema, CitationFieldSchema, UnsupportedWorkKindSchema, CitationIdentifiersSchema, NormalizedCitationMetadataSchema, NormalizedCitationSchema, CitationRecordSchema, IdentifierEvidenceSchema, CitationSourceOutcomeSchema, CitationFieldComparisonSchema, CitationConflictSchema, CitationCandidateClusterSchema, CitationClusterRelationSchema, CitationSourceOperationSchema, CitationSourcePlanItemSchema, } from "./citations/types.js";
|
|
56
64
|
export type { CitationKind, CitationInput, CitationSource, CitationSourceStatus, CitationResolver, CitationResolveOptions, CitationResolutionResult, CitationVerdict, CitationCoverage, ComparisonStatus, CitationField, UnsupportedWorkKind, CitationIdentifiers, NormalizedCitationMetadata, NormalizedCitation, CitationSourceOutcome, CitationRecord, IdentifierEvidence, CitationFieldComparison, CitationConflict, CitationCandidateCluster, CitationSourceClient, CitationSourceRequest, CitationSourceOperation, CitationSourcePlanItem, RegistrationAgencyEvidence, } from "./citations/types.js";
|
|
57
65
|
export { createAskGateway } from "./tools/approval/gateway.js";
|
|
@@ -85,21 +93,23 @@ export { createStreamingChat } from "./providers/streaming-chat.js";
|
|
|
85
93
|
export { openChatTurn, prepareChatTurn, runChatTurn } from "./app/chat-turn.js";
|
|
86
94
|
export type { ChatTurnOutcome, ChatTurnRan, ChatTurnRefusal, ChatTurnResult, ChatTurnSession, OpenChatTurn, OpenChatTurnParams, PrepareChatTurnDeps, PrepareChatTurnParams, PrepareChatTurnResult, RunChatTurnDeps, RunChatTurnParams, RunOpenChatTurnParams, } from "./app/chat-turn.js";
|
|
87
95
|
export { createThreadStore } from "./memory/thread-store.js";
|
|
88
|
-
export type { ThreadStore, Thread, ThreadType, ThreadInputError, CreateThreadInput, ListThreadsInput, ThreadPage } from "./memory/thread-store.js";
|
|
96
|
+
export type { ThreadStore, Thread, ThreadType, ThreadInputError, CreateThreadInput, ListThreadsInput, ListRecentThreadsInput, ThreadPage, } from "./memory/thread-store.js";
|
|
89
97
|
export { createThreadHistory, conversationRecordTurn } from "./memory/thread-history.js";
|
|
90
|
-
export type { ThreadHistory, StoredMessage, RetractOutcome, ConversationTurn, TurnWrite, TurnWriteResult, TurnClose, TurnStatus, StoredTurnRecord, } from "./memory/thread-history.js";
|
|
98
|
+
export type { ThreadHistory, StoredMessage, LoadPageOptions, TurnPage, RetractOutcome, ConversationTurn, TurnWrite, TurnWriteResult, TurnClose, TurnStatus, StoredTurnRecord, } from "./memory/thread-history.js";
|
|
91
99
|
export { syntheticUserMessage, syntheticRecordMessage, isSyntheticUserMessage, isSyntheticRecordMessage } from "./memory/ai-sdk-message-storage.js";
|
|
92
100
|
export { createConversationDisplayRecorder } from "./memory/conversation-display-recorder.js";
|
|
93
101
|
export type { ConversationDisplayRecorder, ConversationDisplayRecorderOptions } from "./memory/conversation-display-recorder.js";
|
|
94
102
|
export { envelopeDisplayMessages, parseStoredDisplayEnvelope } from "./memory/conversation-display-storage.js";
|
|
95
103
|
export type { ConversationDisplayMetadata, ConversationUIData, ConversationUIMessage, StoredDisplayEnvelope } from "./memory/conversation-display-storage.js";
|
|
96
104
|
export { storedMessagesToChat } from "./memory/conversation-display-replay.js";
|
|
97
|
-
export type { ChatEvent, ChatFrame, ChatPartFrame, TextDeltaEvent, IterationEvent, ToolStartedEvent, ToolFinishedEvent, ToolOutcome, ToolCallOutcome, ToolCallDetail, FinishEvent, ChatErrorEvent, } from "./contracts/chat-events.js";
|
|
105
|
+
export type { ChatEvent, ChatFrame, ChatPartFrame, TextDeltaEvent, IterationEvent, ToolStartedEvent, ToolFinishedEvent, ToolOutcome, ToolCallOutcome, ToolCallDetail, FinishEvent, ChatErrorEvent, MessageStartEvent, } from "./contracts/chat-events.js";
|
|
98
106
|
export { toChatFrame, applyChatFrame, checkChatPart } from "./contracts/chat-frame.js";
|
|
107
|
+
export { createSubAgentActivityTracker, SUBAGENT_ACTIVITY_MAX_ENTRIES } from "./contracts/subagent-activity.js";
|
|
108
|
+
export type { SubAgentActivityTracker } from "./contracts/subagent-activity.js";
|
|
99
109
|
export type { TextPart, ToolCallPart, MessagePart, ChatMessage } from "./contracts/message.js";
|
|
100
110
|
export type { TurnTerminal, ApplyFrameResult, ChatPartCheck } from "./contracts/chat-frame.js";
|
|
101
111
|
export type { TokenUsageRollup } from "./contracts/usage.js";
|
|
102
|
-
export type { ChatPart, PresentationPart, PresentationContent, PlanPart, RunCardPart, FileReferencePart, FileReferenceEntry, RunStartedPart, DagStatePart, DagStepState, StepStatus, StepActivityPart, StepPhase, StepFileTreePart, FileTreeEntry, StepOutputPart, StepOutputFile, StepSummaryPart, StepUsagePart, StepBlockedPart, RunSynthesisPart, SynthesizedFinding, BiologicalTheme, SynthesisProgressPart, SynthesisPhase, RunCompletedPart, RunCompletedFinding, RunFailedPart, CompactionPart, AskPart, ChildSessionStartedPart, ReportRenderedPart, } from "./contracts/chat-parts.js";
|
|
112
|
+
export type { ChatPart, PresentationPart, PresentationContent, PlanPart, RunCardPart, PlanApprovalPart, FileReferencePart, FileReferenceEntry, RunStartedPart, DagStatePart, DagStepState, PlanGraphStep, StepStatus, StepActivityPart, ActivityEntryPart, SubAgentActivityEntry, SubAgentActivityPart, StepPhase, StepFileTreePart, FileTreeEntry, StepOutputPart, StepOutputFile, StepSummaryPart, StepUsagePart, StepBlockedPart, RunSynthesisPart, FindingReference, SynthesizedFinding, BiologicalTheme, SynthesisProgressPart, SynthesisPhase, RunCompletedPart, RunCompletedFinding, RunFailedPart, CompactionPart, AskPart, ChildSessionStartedPart, ReportRenderedPart, } from "./contracts/chat-parts.js";
|
|
103
113
|
export { PART_REGISTRY, isTransient, isReconciling, isSidebarPart } from "./contracts/part-registry.js";
|
|
104
114
|
export type { ChatPartType, PartDescriptor, PartEmitter, PartConsumer } from "./contracts/part-registry.js";
|
|
105
115
|
export { parseStructureUrl, alphafoldEntryUrl, alphafoldPredictionUrl } from "./contracts/structure-source.js";
|
|
@@ -115,7 +125,7 @@ export { createPool } from "./lib/storage.js";
|
|
|
115
125
|
export type { PoolConfig } from "./lib/storage.js";
|
|
116
126
|
export type { Pool } from "pg";
|
|
117
127
|
export { registerDataProfileWorkflow, triggerDataProfile, runDataProfile, describeDataProfileStartError } from "./tasks/data-profile.js";
|
|
118
|
-
export type { DataProfileDeps, DataProfileWorkflowInput, DataProfileTriggerDeps, DataProfileTriggerParams, DataProfileTriggerResult, DataProfileStartError, } from "./tasks/data-profile.js";
|
|
128
|
+
export type { DataProfileDeps, DataProfileWorkflowInput, DataProfileTriggerDeps, DataProfileTriggerParams, DataProfileTriggerResult, DataProfileStartError, DataProfileSettlement, NotifyDataProfileSettled, } from "./tasks/data-profile.js";
|
|
119
129
|
/**
|
|
120
130
|
* The synthetic run id the data-profile workflow stamps onto its own frame — the profile has no run
|
|
121
131
|
* row, so this literal is the only thing that identifies its work as the profile's.
|
|
@@ -132,6 +142,12 @@ export { upsertAnalysis } from "./state/analyses.js";
|
|
|
132
142
|
export { clearDataProfile, loadDataProfileStatus, tryRetryDataProfile, reconcileOrphanedDataProfile } from "./state/data-profile.js";
|
|
133
143
|
export type { DataProfileAxis, DataProfileChecked, DataProfileCompanionCompleteness, DataProfileCoverage, DataProfileDimension, DataProfileFile, DataProfileGroup, DataProfileGroupSlot, DataProfileInputSignature, DataProfileKind, DataProfileLifecycleStatus, DataProfileMemberAnnotation, DataProfileObservation, DataProfileOrganism, DataProfilePartition, DataProfileProbeReport, DataProfileQualityAssessment, DataProfileQuarantine, DataProfileRecipeStep, DataProfileResult, DataProfileSubjectSource, } from "./contracts/data-profile.js";
|
|
134
144
|
export type { DataProfileStatus } from "./state/data-profile.js";
|
|
145
|
+
export { queryLatestRunsByAnalyses, queryDataProfileStatusByAnalyses, queryRunProgressByRuns, queryRunsEndedSince } from "./state/analyses-metrics.js";
|
|
146
|
+
export type { DataProfileSummary, EndedRunSummary, LatestRunSummary, RunProgressSummary } from "./state/analyses-metrics.js";
|
|
147
|
+
export { queryArtifactPage, countArtifactsByRuns } from "./state/artifacts.js";
|
|
148
|
+
export type { ArtifactExtensionCount, ArtifactPage, ArtifactPageEntry, ArtifactPageQuery, ArtifactPageSort, RunArtifactCount } from "./state/artifacts.js";
|
|
149
|
+
export { readRunSynthesis } from "./execution/run-synthesis.js";
|
|
150
|
+
export type { ReadSynthesisInput } from "./execution/run-synthesis.js";
|
|
135
151
|
export { DIMENSION_CATEGORIES, DIMENSION_CATEGORY_IDS, DIMENSION_PROBES, DIMENSION_PROBE_IDS, GROUP_CATEGORIES, GROUP_CATEGORY_IDS, GROUP_ROLES, GROUP_ROLE_IDS, PROBE_OUTCOME_IDS, dimensionCategoryEntry, dimensionScope, groupCategoryEntry, } from "./contracts/profile-vocabulary.js";
|
|
136
152
|
export type { DimensionCategory, DimensionCategoryEntry, DimensionProbe, DimensionProbeEntry, DimensionScope, DimensionTreatment, GroupCategory, GroupCategoryEntry, GroupRole, GroupRoleEntry, ProbeOutcome, } from "./contracts/profile-vocabulary.js";
|
|
137
153
|
export { dimensionCardinalities, profileCaveats, profileDatasetFileCount, profileDimensions, profileFileRecords, profileGroups, } from "./app/data-profile-view.js";
|
|
@@ -165,22 +181,24 @@ export type { ExecuteAnalysisDeps, ExecuteAnalysisInput, ExecuteAnalysisResult,
|
|
|
165
181
|
export { registerSandboxStep } from "./workflows/sandbox-step.js";
|
|
166
182
|
export type { SandboxStepDeps, SandboxStepInput, SandboxStepResult, SandboxAgentBuildContext } from "./workflows/sandbox-step.js";
|
|
167
183
|
export { createRunEventStream } from "./execution/run-event-stream.js";
|
|
168
|
-
export type { RunEventStream, RunEventStreamDeps, RunEventSubscribeOptions, RunEventPartHandler } from "./execution/run-event-stream.js";
|
|
184
|
+
export type { RunEventStream, RunEventStreamDeps, RunEventRunRead, RunEventSubscribeOptions, RunEventPartHandler } from "./execution/run-event-stream.js";
|
|
169
185
|
export { pipeFoldedRunEvents } from "./execution/run-event-parts.js";
|
|
170
186
|
export { createSandboxAgents, SANDBOX_AGENT_META } from "./agents/sandbox/index.js";
|
|
171
187
|
export type { SandboxAgentDeps, SandboxStepCoords, AgentMeta } from "./agents/sandbox/index.js";
|
|
172
188
|
export { AnalysisPlanSchema } from "./schemas/workflow-state.js";
|
|
173
189
|
export type { AnalysisPlan, AnalysisStep } from "./schemas/workflow-state.js";
|
|
174
190
|
export type { PlanStep } from "./workflows/execute-analysis-scheduler.js";
|
|
191
|
+
export { planGraphOf } from "./workflows/plan/graph.js";
|
|
192
|
+
export { planPartOf } from "./memory/card-builders.js";
|
|
175
193
|
export { validatePlan } from "./schemas/validate-plan.js";
|
|
176
194
|
export type { ValidatePlanOptions } from "./schemas/validate-plan.js";
|
|
177
195
|
export type { ValidationResult } from "./schemas/validate-plan.js";
|
|
178
|
-
export { upsertPlan, loadPlan } from "./state/plans.js";
|
|
179
|
-
export type { UpsertPlanInput } from "./state/plans.js";
|
|
180
|
-
export { insertRun, reserveRunById, queryActiveRun, queryActiveRunsByAnalysis, updateRunStatus, markRunCanceledIfActive, queryRun, queryRunsByAnalysis, RunDedupCollisionError, RunIdentityCollisionError, } from "./state/runs.js";
|
|
196
|
+
export { upsertPlan, loadPlan, loadPlanRecord } from "./state/plans.js";
|
|
197
|
+
export type { UpsertPlanInput, PlanRecord } from "./state/plans.js";
|
|
198
|
+
export { insertRun, reserveRunById, queryActiveRun, queryActiveRunsByAnalysis, updateRunStatus, markRunCanceledIfActive, queryRun, queryRunsByAnalysis, countRunsByAnalysis, queryRunIdsByPlan, RunDedupCollisionError, RunIdentityCollisionError, } from "./state/runs.js";
|
|
181
199
|
export type { InsertRunInput, RunReservation } from "./state/runs.js";
|
|
182
200
|
export { queryStepsByRun } from "./state/step-executions.js";
|
|
183
|
-
export type { CortexRunRow, StepExecutionRow, RunStatus } from "./state/schema.js";
|
|
201
|
+
export type { CortexRunRow, StepExecutionRow, RunStatus, TerminalRunStatus } from "./state/schema.js";
|
|
184
202
|
export type { DbError } from "./lib/db-result.js";
|
|
185
203
|
export { REFERENCE_DATA_CATALOG, REFERENCE_DATA_CATALOG_VERSION, ReferenceArtifactPathSchema, ReferenceArtifactSchema, ReferenceDataCatalogSchema, ReferenceDatasetSchema, ReferenceSha256Schema, UnknownReferenceDatasetError, referenceArtifactKey, resolveReferenceInstallPlan, } from "./reference-data/catalog.js";
|
|
186
204
|
export type { ReferenceArtifact, ReferenceDataCatalog, ReferenceDataset, ReferenceInstallPlan, ReferenceInstallPlanDataset } from "./reference-data/catalog.js";
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,11 @@ export { resolveCompositionEyes } from "./runtime/assemble.js";
|
|
|
22
22
|
// the report sessions of one analysis. An embedder reaches the listing for its
|
|
23
23
|
// navigation surface, thus these names belong on the front door.
|
|
24
24
|
export { createReportSessionSpawn } from "./app/spawn-report-session.js";
|
|
25
|
+
// Start the run of a stored plan, and approve a plan by its id. Each gives a `Result` and never throws, thus
|
|
26
|
+
// a host route calls them with no `try`. `CoreRuntime.approvePlan` binds the approval over the deps of the
|
|
27
|
+
// conversation agent.
|
|
28
|
+
export { launchPlanRun } from "./tools/execute-analysis.js";
|
|
29
|
+
export { approvePlan } from "./app/approve-plan.js";
|
|
25
30
|
// The harness-owned boot sequence: ordered boot steps around `assembleCoreRuntime`
|
|
26
31
|
// (skill validation → state init → connection budget → assemble → launch) + a
|
|
27
32
|
// `shutdown` handle the embedder wires to its process signals.
|
|
@@ -65,6 +70,10 @@ export { createDbosWorkflowPurger } from "./execution/dbos-workflow-purger.js";
|
|
|
65
70
|
export { createRunCanceler, UnknownRunError } from "./execution/run-canceler.js";
|
|
66
71
|
// Tool primitive.
|
|
67
72
|
export { defineTool, isToolError } from "./tools/define-tool.js";
|
|
73
|
+
// The knowledge plane: an embedder binds a client at its composition root
|
|
74
|
+
// (or none), and it can hand `knowledge_recommend` to the conversation agent
|
|
75
|
+
// through `hostTools`.
|
|
76
|
+
export { createHttpKnowledgeClient, createKnowledgeRecommendTool, createKnowledgeCheckTool, createKnowledgeTools, knowledgeToolDefinitionHash, } from "./tools/knowledge/index.js";
|
|
68
77
|
// Tool-call-detail authoring aids for an embedder that contributes a host tool.
|
|
69
78
|
// A `describeCall` hook that pre-empts truncation — a count in place of a
|
|
70
79
|
// mid-value cut that would name a file that does not exist — must gate on the
|
|
@@ -76,6 +85,12 @@ export { defineTool, isToolError } from "./tools/define-tool.js";
|
|
|
76
85
|
export { normalizeDetail, DETAIL_MAX_LENGTH } from "./loop/tool-detail.js";
|
|
77
86
|
// Host-agnostic citation verification service and its public contracts.
|
|
78
87
|
export { createCitationResolver } from "./citations/resolve.js";
|
|
88
|
+
// The web-search engine type and the SearXNG realization. An embedder cannot
|
|
89
|
+
// build its composition root without these two names: it constructs the engine
|
|
90
|
+
// and types the `webSearch` field of `ConversationAgentDeps` (the front-door
|
|
91
|
+
// rule of the harness-embedder-exports spec). The tools stay off the barrel —
|
|
92
|
+
// the conversation agent constructs them itself.
|
|
93
|
+
export { createSearxngEngine } from "./tools/research/searxng-engine.js";
|
|
79
94
|
export { CitationKindSchema, CitationInputSchema, CitationSourceSchema, CitationSourceStatusSchema, CitationResolutionResultSchema, CitationVerdictSchema, CitationCoverageSchema, ComparisonStatusSchema, CitationFieldSchema, UnsupportedWorkKindSchema, CitationIdentifiersSchema, NormalizedCitationMetadataSchema, NormalizedCitationSchema, CitationRecordSchema, IdentifierEvidenceSchema, CitationSourceOutcomeSchema, CitationFieldComparisonSchema, CitationConflictSchema, CitationCandidateClusterSchema, CitationClusterRelationSchema, CitationSourceOperationSchema, CitationSourcePlanItemSchema, } from "./citations/types.js";
|
|
80
95
|
// Seam: tool approval (`ctx.ask`). `createAskGateway` realizes the `Ask` seam
|
|
81
96
|
// over the injected `Pool` and carries the outward `answer`/`pending` API plus
|
|
@@ -156,6 +171,7 @@ export { envelopeDisplayMessages, parseStoredDisplayEnvelope } from "./memory/co
|
|
|
156
171
|
// by dropping that part, so one stale row cannot fail a whole thread.
|
|
157
172
|
export { storedMessagesToChat } from "./memory/conversation-display-replay.js";
|
|
158
173
|
export { toChatFrame, applyChatFrame, checkChatPart } from "./contracts/chat-frame.js";
|
|
174
|
+
export { createSubAgentActivityTracker, SUBAGENT_ACTIVITY_MAX_ENTRIES } from "./contracts/subagent-activity.js";
|
|
159
175
|
export { PART_REGISTRY, isTransient, isReconciling, isSidebarPart } from "./contracts/part-registry.js";
|
|
160
176
|
// The source grammar of a `structure` presentation card — a host re-runs the same parse over a
|
|
161
177
|
// persisted `url` before it fetches, so the rule lives in one place for both sides.
|
|
@@ -215,6 +231,12 @@ export { upsertAnalysis } from "./state/analyses.js";
|
|
|
215
231
|
// analysis's input set empties (deferring on a running workflow), so the UI
|
|
216
232
|
// stops advertising a profile that describes files the analysis no longer has.
|
|
217
233
|
export { clearDataProfile, loadDataProfileStatus, tryRetryDataProfile, reconcileOrphanedDataProfile } from "./state/data-profile.js";
|
|
234
|
+
// The grouped reads that summarize a page of analyses: one statement for the whole list.
|
|
235
|
+
export { queryLatestRunsByAnalyses, queryDataProfileStatusByAnalyses, queryRunProgressByRuns, queryRunsEndedSince } from "./state/analyses-metrics.js";
|
|
236
|
+
// The artifact ledger reads of one analysis: one page with an exact total, and the step-output count of each run of a list in one statement.
|
|
237
|
+
export { queryArtifactPage, countArtifactsByRuns } from "./state/artifacts.js";
|
|
238
|
+
// The validated synthesis record of a run as the flat `data-run-synthesis` part of the run stream, or null.
|
|
239
|
+
export { readRunSynthesis } from "./execution/run-synthesis.js";
|
|
218
240
|
// The controlled vocabulary the profiler draws from, as data. A consumer rendering a
|
|
219
241
|
// stored group needs the category's meaning, and the package owns the catalogue — a host
|
|
220
242
|
// does not extend it.
|
|
@@ -277,12 +299,14 @@ export { createSandboxAgents, SANDBOX_AGENT_META } from "./agents/sandbox/index.
|
|
|
277
299
|
// an embedder supplies the plan's step DATA and the harness composes each step's
|
|
278
300
|
// seed at dispatch, when the step's dependencies have actually produced something.
|
|
279
301
|
export { AnalysisPlanSchema } from "./schemas/workflow-state.js";
|
|
302
|
+
export { planGraphOf } from "./workflows/plan/graph.js";
|
|
303
|
+
export { planPartOf } from "./memory/card-builders.js";
|
|
280
304
|
export { validatePlan } from "./schemas/validate-plan.js";
|
|
281
305
|
// Plan + run + step ledgers around a trigger. `upsertPlan` takes a
|
|
282
306
|
// caller-derived id and inserts-if-absent (the deterministic-id intake path);
|
|
283
307
|
// the run-state calls mirror the chat trigger's reserve → dedup → status flow.
|
|
284
|
-
export { upsertPlan, loadPlan } from "./state/plans.js";
|
|
285
|
-
export { insertRun, reserveRunById, queryActiveRun, queryActiveRunsByAnalysis, updateRunStatus, markRunCanceledIfActive, queryRun, queryRunsByAnalysis, RunDedupCollisionError, RunIdentityCollisionError, } from "./state/runs.js";
|
|
308
|
+
export { upsertPlan, loadPlan, loadPlanRecord } from "./state/plans.js";
|
|
309
|
+
export { insertRun, reserveRunById, queryActiveRun, queryActiveRunsByAnalysis, updateRunStatus, markRunCanceledIfActive, queryRun, queryRunsByAnalysis, countRunsByAnalysis, queryRunIdsByPlan, RunDedupCollisionError, RunIdentityCollisionError, } from "./state/runs.js";
|
|
286
310
|
export { queryStepsByRun } from "./state/step-executions.js";
|
|
287
311
|
// Host-neutral reference-data catalog and shared installation receipts. Each
|
|
288
312
|
// artifact names the third-party upstream it is fetched from; embedders own all
|
package/dist/lib/hooks.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface NoticeFailure {
|
|
|
29
29
|
readonly reason: string;
|
|
30
30
|
}
|
|
31
31
|
export type GateName = "RunAuthorizer.authorize" | "RunCharge.open" | "ArtifactRegistry.register" | "resolveRequestHeaders" | "resolveSandboxLabels";
|
|
32
|
-
export type NoticeName = "RunAuthorizer.revoke" | "RunAuthorizer.revokeByJti" | "RunCharge.close" | "ArtifactRegistry.sync" | "UsageRecorder.record";
|
|
32
|
+
export type NoticeName = "RunAuthorizer.revoke" | "RunAuthorizer.revokeByJti" | "RunCharge.close" | "ArtifactRegistry.sync" | "UsageRecorder.record" | "notifyDataProfileSettled";
|
|
33
33
|
export type GateRefusal = {
|
|
34
34
|
readonly kind: "failed";
|
|
35
35
|
readonly gate: GateName;
|
package/dist/lib/metrics.d.ts
CHANGED
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
* on the no-op provider for the life of the process.
|
|
33
33
|
*/
|
|
34
34
|
import type { StepExecutionStatus } from "../state/schema.js";
|
|
35
|
-
/** Terminal status of a run
|
|
36
|
-
export type RunOutcome = "completed" | "partial" | "failed" | "canceled";
|
|
35
|
+
/** Terminal status of a run, the same as the run ledger. */
|
|
36
|
+
export type RunOutcome = "completed" | "partial" | "failed" | "canceled" | "suspended_insufficient_funds";
|
|
37
37
|
/** The workflow family a run belongs to. */
|
|
38
38
|
export type RunWorkflow = "analysis" | "data_profile";
|
|
39
39
|
/** Terminal status of an executed step. A blocker is a failure to deliver, so `blocked` maps here to `failed`. */
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The call guard: a bound on repeated tool calls inside one agent run.
|
|
3
|
+
*
|
|
4
|
+
* A model that treats a tool answer as a state to poll calls the same tool
|
|
5
|
+
* with the same input again and again, or drifts through one query after
|
|
6
|
+
* another, until the iteration cap or the wall clock ends the run. A frontier
|
|
7
|
+
* model reads an "unavailable" answer once and continues; a smaller model
|
|
8
|
+
* does not. The guard makes that difference a host decision: the third call
|
|
9
|
+
* with an input the run already sent, the call past the budget of one tool,
|
|
10
|
+
* or the call past the budget of the whole run, answers a tool error that
|
|
11
|
+
* tells the model to continue with what it has. The service behind the tool never sees the refused call.
|
|
12
|
+
*
|
|
13
|
+
* The guard wraps a list of tools for ONE run. Build it where the tools of
|
|
14
|
+
* the run are built, never at construction of the host, thus the counters
|
|
15
|
+
* start at zero for every run. A terminal tool (one that ends the run) must
|
|
16
|
+
* not be wrapped: a refusal there would strand the run.
|
|
17
|
+
*/
|
|
18
|
+
import type { Tool } from "../tools/define-tool.js";
|
|
19
|
+
export interface CallGuardPolicy {
|
|
20
|
+
/** The calls with one identical input that a run may make to one tool. The next one is refused. */
|
|
21
|
+
readonly identicalLimit: number;
|
|
22
|
+
/** The calls, with any input, that a run may make to one tool. The next one is refused. */
|
|
23
|
+
readonly perToolLimit: number;
|
|
24
|
+
/** The calls, with any input, that a run may make across every guarded tool. The next one is refused by every tool. */
|
|
25
|
+
readonly totalLimit: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Two identical calls let a model confirm an answer once. Twelve calls of one
|
|
29
|
+
* tool is above the largest count a frontier planner made in the Phase 0
|
|
30
|
+
* campaign (six and a half calls per plan across ALL tools) and far below the
|
|
31
|
+
* two hundred that a looping planner reached. Forty calls across every tool
|
|
32
|
+
* bounds the round over the tools that stays under each per-tool budget: in
|
|
33
|
+
* the 32-task campaign every submitted Sonnet plan but two needed fewer, and
|
|
34
|
+
* the one plan that ran to the wall clock made 62.
|
|
35
|
+
*/
|
|
36
|
+
export declare const DEFAULT_CALL_GUARD: CallGuardPolicy;
|
|
37
|
+
/** A stable key for one input: the JSON text with the object keys sorted at every depth. */
|
|
38
|
+
export declare function canonicalInputKey(input: unknown): string;
|
|
39
|
+
export interface GuardedCallRefusal {
|
|
40
|
+
readonly tool: string;
|
|
41
|
+
readonly kind: "identical" | "budget" | "total";
|
|
42
|
+
readonly calls: number;
|
|
43
|
+
}
|
|
44
|
+
export interface CallGuardOptions {
|
|
45
|
+
readonly policy?: CallGuardPolicy;
|
|
46
|
+
/** Called on each refusal, for the log of the host. */
|
|
47
|
+
readonly onRefusal?: (refusal: GuardedCallRefusal) => void;
|
|
48
|
+
}
|
|
49
|
+
/** Wrap each tool so its `execute` obeys the policy for the life of the returned list. */
|
|
50
|
+
export declare function guardRepeatedCalls(tools: readonly Tool[], options?: CallGuardOptions): Tool[];
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The call guard: a bound on repeated tool calls inside one agent run.
|
|
3
|
+
*
|
|
4
|
+
* A model that treats a tool answer as a state to poll calls the same tool
|
|
5
|
+
* with the same input again and again, or drifts through one query after
|
|
6
|
+
* another, until the iteration cap or the wall clock ends the run. A frontier
|
|
7
|
+
* model reads an "unavailable" answer once and continues; a smaller model
|
|
8
|
+
* does not. The guard makes that difference a host decision: the third call
|
|
9
|
+
* with an input the run already sent, the call past the budget of one tool,
|
|
10
|
+
* or the call past the budget of the whole run, answers a tool error that
|
|
11
|
+
* tells the model to continue with what it has. The service behind the tool never sees the refused call.
|
|
12
|
+
*
|
|
13
|
+
* The guard wraps a list of tools for ONE run. Build it where the tools of
|
|
14
|
+
* the run are built, never at construction of the host, thus the counters
|
|
15
|
+
* start at zero for every run. A terminal tool (one that ends the run) must
|
|
16
|
+
* not be wrapped: a refusal there would strand the run.
|
|
17
|
+
*/
|
|
18
|
+
import { err } from "neverthrow";
|
|
19
|
+
/**
|
|
20
|
+
* Two identical calls let a model confirm an answer once. Twelve calls of one
|
|
21
|
+
* tool is above the largest count a frontier planner made in the Phase 0
|
|
22
|
+
* campaign (six and a half calls per plan across ALL tools) and far below the
|
|
23
|
+
* two hundred that a looping planner reached. Forty calls across every tool
|
|
24
|
+
* bounds the round over the tools that stays under each per-tool budget: in
|
|
25
|
+
* the 32-task campaign every submitted Sonnet plan but two needed fewer, and
|
|
26
|
+
* the one plan that ran to the wall clock made 62.
|
|
27
|
+
*/
|
|
28
|
+
export const DEFAULT_CALL_GUARD = { identicalLimit: 2, perToolLimit: 12, totalLimit: 40 };
|
|
29
|
+
/** A stable key for one input: the JSON text with the object keys sorted at every depth. */
|
|
30
|
+
export function canonicalInputKey(input) {
|
|
31
|
+
return JSON.stringify(sortKeys(input));
|
|
32
|
+
}
|
|
33
|
+
function sortKeys(value) {
|
|
34
|
+
if (Array.isArray(value))
|
|
35
|
+
return value.map(sortKeys);
|
|
36
|
+
if (value !== null && typeof value === "object") {
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const key of Object.keys(value).sort())
|
|
39
|
+
out[key] = sortKeys(value[key]);
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
/** Wrap each tool so its `execute` obeys the policy for the life of the returned list. */
|
|
45
|
+
export function guardRepeatedCalls(tools, options = {}) {
|
|
46
|
+
const policy = options.policy ?? DEFAULT_CALL_GUARD;
|
|
47
|
+
let total = 0;
|
|
48
|
+
return tools.map((tool) => {
|
|
49
|
+
let calls = 0;
|
|
50
|
+
const byInput = new Map();
|
|
51
|
+
const guarded = {
|
|
52
|
+
...tool,
|
|
53
|
+
...(tool.describeCall ? { describeCall: tool.describeCall.bind(tool) } : {}),
|
|
54
|
+
...(tool.describeResult ? { describeResult: tool.describeResult.bind(tool) } : {}),
|
|
55
|
+
async execute(input, ctx) {
|
|
56
|
+
calls += 1;
|
|
57
|
+
const key = canonicalInputKey(input);
|
|
58
|
+
const identical = (byInput.get(key) ?? 0) + 1;
|
|
59
|
+
byInput.set(key, identical);
|
|
60
|
+
if (identical > policy.identicalLimit) {
|
|
61
|
+
options.onRefusal?.({ tool: tool.id, kind: "identical", calls: identical });
|
|
62
|
+
return err({
|
|
63
|
+
error: `This run already called ${tool.id} with this exact input ${identical - 1} times, and the answer does not change within a run. ` +
|
|
64
|
+
"Use the answer you have and continue. If the answer was that a resource is absent, plan without it or ask for it; do not search again.",
|
|
65
|
+
retryable: false,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
total += 1;
|
|
69
|
+
if (total > policy.totalLimit) {
|
|
70
|
+
options.onRefusal?.({ tool: tool.id, kind: "total", calls: total });
|
|
71
|
+
return err({
|
|
72
|
+
error: `This run made ${policy.totalLimit} calls across its search tools, which is the search budget of one run. ` +
|
|
73
|
+
"Submit the plan you have now. If a required resource is absent, ask for it; do not search again.",
|
|
74
|
+
retryable: false,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (calls > policy.perToolLimit) {
|
|
78
|
+
options.onRefusal?.({ tool: tool.id, kind: "budget", calls });
|
|
79
|
+
return err({
|
|
80
|
+
error: `This run made ${policy.perToolLimit} calls of ${tool.id}, which is the budget of one run for one tool. ` +
|
|
81
|
+
"Continue with what you have. If a required resource is absent, ask for it; do not search again.",
|
|
82
|
+
retryable: false,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return tool.execute(input, ctx);
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
return guarded;
|
|
89
|
+
});
|
|
90
|
+
}
|