@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
package/dist/state/schema.d.ts
CHANGED
|
@@ -54,6 +54,8 @@ export declare const RunStatus: z.ZodEnum<{
|
|
|
54
54
|
suspended_insufficient_funds: "suspended_insufficient_funds";
|
|
55
55
|
}>;
|
|
56
56
|
export type RunStatus = z.infer<typeof RunStatus>;
|
|
57
|
+
/** A status that a run keeps once it ended: every status but `running`. */
|
|
58
|
+
export type TerminalRunStatus = Exclude<RunStatus, "running">;
|
|
57
59
|
export declare const StepExecutionStatus: z.ZodEnum<{
|
|
58
60
|
failed: "failed";
|
|
59
61
|
pending: "pending";
|
|
@@ -74,4 +74,13 @@ export declare function updateStepExecution(pool: Querier, runId: string, stepId
|
|
|
74
74
|
* these steps never started.
|
|
75
75
|
*/
|
|
76
76
|
export declare function sweepPendingStepExecutions(pool: Querier, runId: string): ResultAsync<void, DbError>;
|
|
77
|
+
/**
|
|
78
|
+
* Settle a run's still-`running` rows to `canceled` after an engine cancel.
|
|
79
|
+
* A canceled child re-raises before it writes a terminal row, so this is the
|
|
80
|
+
* only writer for a step that the cancel cut. `canceled`, not `skipped`: these
|
|
81
|
+
* steps started. The `status = 'running'` guard keeps a terminal row that a
|
|
82
|
+
* child wrote first. `started_at` and `completed_at` are ISO 8601 `TEXT`, so
|
|
83
|
+
* the duration casts both; it is NULL when `started_at` is NULL.
|
|
84
|
+
*/
|
|
85
|
+
export declare function cancelRunningStepExecutions(pool: Querier, runId: string, reason: string): ResultAsync<void, DbError>;
|
|
77
86
|
export declare function queryStepsByRun(pool: Querier, runId: string): ResultAsync<StepExecutionRow[], DbError>;
|
|
@@ -136,6 +136,28 @@ export function sweepPendingStepExecutions(pool, runId) {
|
|
|
136
136
|
});
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Settle a run's still-`running` rows to `canceled` after an engine cancel.
|
|
141
|
+
* A canceled child re-raises before it writes a terminal row, so this is the
|
|
142
|
+
* only writer for a step that the cancel cut. `canceled`, not `skipped`: these
|
|
143
|
+
* steps started. The `status = 'running'` guard keeps a terminal row that a
|
|
144
|
+
* child wrote first. `started_at` and `completed_at` are ISO 8601 `TEXT`, so
|
|
145
|
+
* the duration casts both; it is NULL when `started_at` is NULL.
|
|
146
|
+
*/
|
|
147
|
+
export function cancelRunningStepExecutions(pool, runId, reason) {
|
|
148
|
+
const now = new Date().toISOString();
|
|
149
|
+
return tryMutation("stepExecutions.cancelRunningStepExecutions", async () => {
|
|
150
|
+
await pool.query({
|
|
151
|
+
text: `UPDATE cortex_step_executions
|
|
152
|
+
SET status = 'canceled',
|
|
153
|
+
completed_at = $2::text,
|
|
154
|
+
duration_ms = (EXTRACT(EPOCH FROM ($2::text::timestamptz - started_at::timestamptz)) * 1000)::bigint,
|
|
155
|
+
error = $3
|
|
156
|
+
WHERE run_id = $1 AND status = 'running'`,
|
|
157
|
+
values: [runId, now, reason],
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
}
|
|
139
161
|
export function queryStepsByRun(pool, runId) {
|
|
140
162
|
return tryQuery("stepExecutions.queryStepsByRun", async () => {
|
|
141
163
|
const result = await pool.query({
|
|
@@ -103,5 +103,11 @@ export interface ProfileActivityFrame {
|
|
|
103
103
|
* A failing write is swallowed and logged, never propagated: observation is a
|
|
104
104
|
* diagnostic channel, and a dropped frame must not fail a profile that is otherwise
|
|
105
105
|
* succeeding. This mirrors the sandbox-step producer's stance on the same write.
|
|
106
|
+
*
|
|
107
|
+
* Each emission writes the reconciling activity part, so a late subscriber folds to the
|
|
108
|
+
* current activity, and then one `data-activity-entry`, so it also gets each earlier line.
|
|
109
|
+
* The two parts carry one time from `now`, which the body binds to the checkpointed clock of
|
|
110
|
+
* its workflow. The two writes fail alone. The sequence number of the entry advances before
|
|
111
|
+
* the clock read and the writes, thus a failure cannot shift a later id.
|
|
106
112
|
*/
|
|
107
|
-
export declare function createProfileActivityEmitter(write: ActivityWrite, frame: ProfileActivityFrame, logger: Logger): ProfileActivityEmitter;
|
|
113
|
+
export declare function createProfileActivityEmitter(write: ActivityWrite, frame: ProfileActivityFrame, logger: Logger, now: () => Promise<number>): ProfileActivityEmitter;
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* anything that throws on the way there reaches the failure path having emitted
|
|
20
20
|
* no terminal at all.
|
|
21
21
|
*/
|
|
22
|
-
import {
|
|
22
|
+
import { activityEmission, activityForTool } from "../sandbox/sandbox-step-translate.js";
|
|
23
23
|
/**
|
|
24
24
|
* Build the profile's activity channel over `write`.
|
|
25
25
|
*
|
|
@@ -30,21 +30,30 @@ import { activityForTool, stepPartId } from "../sandbox/sandbox-step-translate.j
|
|
|
30
30
|
* A failing write is swallowed and logged, never propagated: observation is a
|
|
31
31
|
* diagnostic channel, and a dropped frame must not fail a profile that is otherwise
|
|
32
32
|
* succeeding. This mirrors the sandbox-step producer's stance on the same write.
|
|
33
|
+
*
|
|
34
|
+
* Each emission writes the reconciling activity part, so a late subscriber folds to the
|
|
35
|
+
* current activity, and then one `data-activity-entry`, so it also gets each earlier line.
|
|
36
|
+
* The two parts carry one time from `now`, which the body binds to the checkpointed clock of
|
|
37
|
+
* its workflow. The two writes fail alone. The sequence number of the entry advances before
|
|
38
|
+
* the clock read and the writes, thus a failure cannot shift a later id.
|
|
33
39
|
*/
|
|
34
|
-
export function createProfileActivityEmitter(write, frame, logger) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// accumulating them. Minted through the shared construction because that
|
|
38
|
-
// sameness across producers is what makes the fold correct.
|
|
39
|
-
const id = stepPartId("step-activity", frame.runId, frame.stepId);
|
|
40
|
-
const emit = async (phase, activity) => {
|
|
40
|
+
export function createProfileActivityEmitter(write, frame, logger, now) {
|
|
41
|
+
let seq = 0;
|
|
42
|
+
const writeAlone = async (part, phase) => {
|
|
41
43
|
try {
|
|
42
|
-
await write(
|
|
44
|
+
await write(part);
|
|
43
45
|
}
|
|
44
46
|
catch (err) {
|
|
45
47
|
logger.warn("run-event write failed (non-fatal)", { phase, ...logger.errorFields(err) });
|
|
46
48
|
}
|
|
47
49
|
};
|
|
50
|
+
const emit = async (phase, activity) => {
|
|
51
|
+
const position = seq++;
|
|
52
|
+
const at = new Date(await now()).toISOString();
|
|
53
|
+
const parts = activityEmission(frame, position, phase, activity, at);
|
|
54
|
+
await writeAlone(parts.activity, phase);
|
|
55
|
+
await writeAlone(parts.entry, phase);
|
|
56
|
+
};
|
|
48
57
|
return {
|
|
49
58
|
sandboxInit: () => emit("sandbox-init", "Starting sandbox"),
|
|
50
59
|
scanning: () => emit("executing", "Scanning input files"),
|
|
@@ -28,7 +28,7 @@ import type { SandboxAgentDeps } from "../agents/sandbox/shared.js";
|
|
|
28
28
|
import type { BioToolKeys } from "../tools/bio/keys.js";
|
|
29
29
|
import type { ToolOutputStore } from "../loop/tool-output.js";
|
|
30
30
|
import type { DbError } from "../lib/db-result.js";
|
|
31
|
-
import { type GateRefusal } from "../lib/hooks.js";
|
|
31
|
+
import { type GateRefusal, type NoticeFailure } from "../lib/hooks.js";
|
|
32
32
|
import type { Logger } from "../lib/logger.js";
|
|
33
33
|
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
34
34
|
import type { ChatProvider, EmbeddingProvider } from "../providers/types.js";
|
|
@@ -38,6 +38,15 @@ import { type ResolveWorkspaceRoot } from "../workspace/paths.js";
|
|
|
38
38
|
import { type ProfileSubmission } from "../schemas/data-profile-schemas.js";
|
|
39
39
|
import { type ProfileResolution } from "./data-profile-resolve.js";
|
|
40
40
|
import { type DataProfileResult } from "../state/index.js";
|
|
41
|
+
/** The settle of one profile claim: the ledger row left `running`, and the terminal write stamped it. */
|
|
42
|
+
export interface DataProfileSettlement {
|
|
43
|
+
readonly analysisId: string;
|
|
44
|
+
readonly status: "completed" | "failed";
|
|
45
|
+
/** The `data_profile_completed_at` that the terminal write stamped, as an ISO 8601 string. */
|
|
46
|
+
readonly completedAt: string;
|
|
47
|
+
}
|
|
48
|
+
/** The notice of a profile settle. The workflow body calls it one time for each settled claim. */
|
|
49
|
+
export type NotifyDataProfileSettled = (settlement: DataProfileSettlement) => ResultAsync<void, NoticeFailure>;
|
|
41
50
|
/** The body's construction-time deps — closed over at registration. */
|
|
42
51
|
export interface DataProfileDeps extends EnvironmentStorePaths {
|
|
43
52
|
/** Operational logging seam; omitted falls back to no-op. */
|
|
@@ -73,6 +82,11 @@ export interface DataProfileDeps extends EnvironmentStorePaths {
|
|
|
73
82
|
* Unbound keeps the current shape: no link tool, and no error.
|
|
74
83
|
*/
|
|
75
84
|
readonly extendAnalysisFarm?: ExtendAnalysisFarm;
|
|
85
|
+
/**
|
|
86
|
+
* Notice of each profile settle, for the records of the embedder. Unbound, the
|
|
87
|
+
* body calls nothing. It is not a provenance hook.
|
|
88
|
+
*/
|
|
89
|
+
readonly notifyDataProfileSettled?: NotifyDataProfileSettled;
|
|
76
90
|
}
|
|
77
91
|
/**
|
|
78
92
|
* Workflow input. JSON-serialisable — DBOS persists it as the workflow's
|
|
@@ -296,7 +296,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
296
296
|
// ledger has no suspended state and the retry path takes a `failed` row again.
|
|
297
297
|
const suspendProfile = async (suspension) => {
|
|
298
298
|
logger.warn("profile suspended", { reason: suspension.reason });
|
|
299
|
-
settleTerminalWrite(logger, analysisId, unwrapOrThrow(await failDataProfile(deps.pool, analysisId, suspension.reason)), "failed");
|
|
299
|
+
await settleTerminalWrite(logger, analysisId, unwrapOrThrow(await failDataProfile(deps.pool, analysisId, suspension.reason)), "failed", deps.notifyDataProfileSettled);
|
|
300
300
|
await revoke("data-profile-suspended");
|
|
301
301
|
unwrapOrThrow(await suspendAnalysis(deps.pool, analysisId));
|
|
302
302
|
await activity.failed(suspension.reason);
|
|
@@ -310,7 +310,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
310
310
|
// this capability, and a body that composed its own strings is a body they can drift from.
|
|
311
311
|
// The frame is the workflow's synthetic one: both values are constants shared by every analysis,
|
|
312
312
|
// so they identify nothing and consumers are required not to key on them.
|
|
313
|
-
const activity = createProfileActivityEmitter((part) => DBOS.writeStream("events", part), { runId: DATA_PROFILE_RUN_LITERAL, stepId: DATA_PROFILE_STEP_LITERAL }, logger);
|
|
313
|
+
const activity = createProfileActivityEmitter((part) => DBOS.writeStream("events", part), { runId: DATA_PROFILE_RUN_LITERAL, stepId: DATA_PROFILE_STEP_LITERAL }, logger, () => DBOS.now());
|
|
314
314
|
try {
|
|
315
315
|
// Record which workflow owns this profile before anything else, so a consumer that resolves
|
|
316
316
|
// the id from the ledger can subscribe from the earliest possible moment. Guarded on a real
|
|
@@ -337,7 +337,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
337
337
|
// catalogue's fit is measured, and a completion that emitted none would make the
|
|
338
338
|
// empty-manifest path invisible to the same monitoring that watches every other.
|
|
339
339
|
logProfileMonitoring(logger, { dimensions: [], probes: [], repairRounds: 0, absorb: "none" });
|
|
340
|
-
settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId)), "completed");
|
|
340
|
+
await settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId)), "completed", deps.notifyDataProfileSettled);
|
|
341
341
|
await revoke("data-profile-completed");
|
|
342
342
|
// This is a terminal completion like any other, so it reports one — a consumer watching
|
|
343
343
|
// an empty-manifest profile sees it settle rather than seeing the stream simply stop.
|
|
@@ -390,7 +390,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
390
390
|
await activity.indexing();
|
|
391
391
|
await indexProfile({ deps, analysisId, session: runSession, record, scan, logger });
|
|
392
392
|
logProfileMonitoring(logger, { resolution, dimensions: [], probes: [], repairRounds: 0, absorb: "none" });
|
|
393
|
-
settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId, record)), "completed");
|
|
393
|
+
await settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId, record)), "completed", deps.notifyDataProfileSettled);
|
|
394
394
|
await revoke("data-profile-completed");
|
|
395
395
|
await activity.complete();
|
|
396
396
|
return;
|
|
@@ -416,7 +416,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
416
416
|
repairRounds: 0,
|
|
417
417
|
absorb: absorb.kind,
|
|
418
418
|
});
|
|
419
|
-
settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId, absorbed)), "completed");
|
|
419
|
+
await settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId, absorbed)), "completed", deps.notifyDataProfileSettled);
|
|
420
420
|
await revoke("data-profile-completed");
|
|
421
421
|
await activity.complete();
|
|
422
422
|
return;
|
|
@@ -571,7 +571,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
571
571
|
});
|
|
572
572
|
// 6. Complete — store the FULL profiler finding plus the input signature for
|
|
573
573
|
// staleness detection. The scratch tree is gone; this row is all that survives.
|
|
574
|
-
settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId, profileRecord)), "completed");
|
|
574
|
+
await settleTerminalWrite(logger, analysisId, unwrapOrThrow(await completeDataProfile(deps.pool, analysisId, profileRecord)), "completed", deps.notifyDataProfileSettled);
|
|
575
575
|
await revoke("data-profile-completed");
|
|
576
576
|
// LAST statement of the success path, and that placement is the guarantee that exactly
|
|
577
577
|
// one terminal activity is ever emitted. Everything that could still throw — the ledger
|
|
@@ -598,7 +598,7 @@ export async function runDataProfileBody(input, deps) {
|
|
|
598
598
|
return await suspendProfile(suspension);
|
|
599
599
|
logger.error("profile failed", logger.errorFields(err));
|
|
600
600
|
const reason = profileFailureReason(err);
|
|
601
|
-
settleTerminalWrite(logger, analysisId, unwrapOrThrow(await failDataProfile(deps.pool, analysisId, reason)), "failed");
|
|
601
|
+
await settleTerminalWrite(logger, analysisId, unwrapOrThrow(await failDataProfile(deps.pool, analysisId, reason)), "failed", deps.notifyDataProfileSettled);
|
|
602
602
|
await revoke("data-profile-failed");
|
|
603
603
|
// The same bounded, user-safe line the ledger receives — never the raw error, whose paths and
|
|
604
604
|
// stack frames stay in the log record above. This is the only other terminal emission, so
|
|
@@ -617,18 +617,23 @@ function logTerminalNoop(logger, analysisId, write) {
|
|
|
617
617
|
}
|
|
618
618
|
/**
|
|
619
619
|
* Settle one terminal write of the profile ledger. A refused stamp is logged
|
|
620
|
-
* (see `logTerminalNoop`). An accepted stamp records the
|
|
621
|
-
*
|
|
620
|
+
* (see `logTerminalNoop`) and sends no notice. An accepted stamp records the
|
|
621
|
+
* profile's run outcome and then delivers the settle notice when the deps bind
|
|
622
|
+
* one: the CAS admits one write per `running` claim, so this is the exactly-once
|
|
622
623
|
* point of the transition — a recovery replay of the body finds the row no
|
|
623
|
-
* longer `running`, and
|
|
624
|
+
* longer `running`, and does neither again. The duration runs from the
|
|
624
625
|
* `data_profile_started_at` the claim stamped, never from a body-local clock.
|
|
626
|
+
* A crash between the stamp and the notice loses that one notice.
|
|
625
627
|
*/
|
|
626
|
-
function settleTerminalWrite(logger, analysisId, write, status) {
|
|
628
|
+
async function settleTerminalWrite(logger, analysisId, write, status, notify) {
|
|
627
629
|
if (!write.stamped) {
|
|
628
630
|
logTerminalNoop(logger, analysisId, status === "completed" ? "completion" : "failure");
|
|
629
631
|
return;
|
|
630
632
|
}
|
|
631
633
|
recordRunCompleted({ workflow: "data_profile", status, durationMs: elapsedSinceIso(write.startedAt) });
|
|
634
|
+
if (notify === undefined)
|
|
635
|
+
return;
|
|
636
|
+
await deliverNotice(logger, "notifyDataProfileSettled", notify({ analysisId, status, completedAt: write.completedAt }));
|
|
632
637
|
}
|
|
633
638
|
/** A one-line, user-facing description of a `DataProfileStartError`. */
|
|
634
639
|
export function describeDataProfileStartError(e) {
|
|
@@ -24,6 +24,8 @@ export interface AdHocRoute {
|
|
|
24
24
|
readonly agentId: string;
|
|
25
25
|
readonly resources: ResourceSpec;
|
|
26
26
|
readonly rationale: string;
|
|
27
|
+
/** The capability phrase of the chosen agent, when the router copied one that the agent advertises. */
|
|
28
|
+
readonly capability?: string;
|
|
27
29
|
/** The validated package entries of the step. Empty is a normal answer. */
|
|
28
30
|
readonly packages: readonly string[];
|
|
29
31
|
/** Every entry that the validation removed, with the reason for the caveat. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsonSchema, tool as aiTool } from "ai";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { PLANNABLE_AGENT_CATALOG } from "../agents/sandbox-catalog.js";
|
|
4
|
+
import { SANDBOX_AGENT_META } from "../agents/sandbox/index.js";
|
|
4
5
|
import { forSubAgent } from "../auth/types.js";
|
|
5
6
|
import { DATA_PROFILE_ORIENTATION_MAX_CHARS, buildDataProfileOrientation } from "../app/data-profile-orientation.js";
|
|
6
7
|
import { createNoopUsageRecorder } from "../billing/noop-usage-recorder.js";
|
|
@@ -24,6 +25,7 @@ const resourcesSchema = z.object({
|
|
|
24
25
|
});
|
|
25
26
|
const routeSchema = z.object({
|
|
26
27
|
agentId: z.string().optional().describe("The id of one specialist from the eligible catalog, copied exactly."),
|
|
28
|
+
capability: z.string().optional().describe("The one capability phrase of that specialist which this request uses, copied exactly from its capabilities."),
|
|
27
29
|
resources: resourcesSchema
|
|
28
30
|
.optional()
|
|
29
31
|
.describe("The sandbox resources of the step, inside the stated ranges. Omit to take the defaults; a value outside the ranges is replaced by the defaults."),
|
|
@@ -241,6 +243,20 @@ export async function routeAdHocRequest(deps, input) {
|
|
|
241
243
|
// never becomes a `fallbackClass`, because that class describes the route
|
|
242
244
|
// as a whole and a step agent reads it as "the routing itself fell back".
|
|
243
245
|
const { packages, droppedPackages } = await routePackages(candidate.packages, deps.resolvePackages, logger);
|
|
244
|
-
|
|
245
|
-
|
|
246
|
+
// Kept only when the chosen agent really advertises it. A phrase that no agent advertises
|
|
247
|
+
// resolves no data requirement anyway, and carrying it would put a fabricated capability into
|
|
248
|
+
// the step and into the run record. An absent phrase is a normal answer, not a fallback class:
|
|
249
|
+
// most requests use a capability that declares nothing.
|
|
250
|
+
const requestedCapability = typeof candidate.capability === "string" ? candidate.capability.trim() : "";
|
|
251
|
+
const capability = (SANDBOX_AGENT_META[agentId]?.capabilities ?? []).includes(requestedCapability) ? requestedCapability : undefined;
|
|
252
|
+
logger.info("ad hoc route selected", { agentId, resources, rationale, failure, packages, droppedPackages, ...(capability ? { capability } : {}) });
|
|
253
|
+
return {
|
|
254
|
+
agentId,
|
|
255
|
+
resources,
|
|
256
|
+
rationale,
|
|
257
|
+
packages,
|
|
258
|
+
droppedPackages,
|
|
259
|
+
...(capability ? { capability } : {}),
|
|
260
|
+
...(failure ? { fallbackClass: failure } : {}),
|
|
261
|
+
};
|
|
246
262
|
}
|
|
@@ -40,8 +40,11 @@ export function createAskGateway(deps) {
|
|
|
40
40
|
// A standing grant this user holds for this ask's grant key — the command
|
|
41
41
|
// unless the tool supplied a broader key — short-circuits the prompt: record
|
|
42
42
|
// the invocation as `resolved` for the audit trail and return without pausing.
|
|
43
|
+
// The one part it emits is already settled, so no surface prompts, and the
|
|
44
|
+
// transcript still shows that a standing grant approved the call.
|
|
43
45
|
if (unwrapOrThrow(await selectGrant(pool, ctx.analysisId, ctx.userId, request.grantKey ?? request.command))) {
|
|
44
46
|
unwrapOrThrow(await insertGrantedAsk(pool, row, now));
|
|
47
|
+
await emitAskPart(ctx.emit, id, request, "resolved", "always");
|
|
45
48
|
return { kind: "always" };
|
|
46
49
|
}
|
|
47
50
|
unwrapOrThrow(await insertPendingAsk(pool, row));
|
|
@@ -69,7 +72,7 @@ export function createAskGateway(deps) {
|
|
|
69
72
|
throw new AskRejectedError("approval request is no longer available");
|
|
70
73
|
}
|
|
71
74
|
if (resolution.status !== "pending") {
|
|
72
|
-
await emitAskPart(ctx.emit, id, request, resolution.status);
|
|
75
|
+
await emitAskPart(ctx.emit, id, request, resolution.status, replyKindOf(resolution.status, resolution.reply));
|
|
73
76
|
return settle(resolution.status, resolution.reply, ctx.signal);
|
|
74
77
|
}
|
|
75
78
|
await sleepUntil(POLL_INTERVAL_MS, ctx.signal);
|
|
@@ -88,7 +91,7 @@ export function createAskGateway(deps) {
|
|
|
88
91
|
return { ask, answer, pending, sweepExpired };
|
|
89
92
|
}
|
|
90
93
|
/** Emit (or reconcile, under the same id) the ask's `data-ask` chat part. */
|
|
91
|
-
function emitAskPart(emit, id, request, status) {
|
|
94
|
+
function emitAskPart(emit, id, request, status, reply) {
|
|
92
95
|
return emit({
|
|
93
96
|
type: "data-ask",
|
|
94
97
|
data: {
|
|
@@ -97,16 +100,36 @@ function emitAskPart(emit, id, request, status) {
|
|
|
97
100
|
command: request.command,
|
|
98
101
|
...(request.detail !== undefined ? { detail: request.detail } : {}),
|
|
99
102
|
status,
|
|
103
|
+
...(reply !== undefined ? { reply } : {}),
|
|
100
104
|
},
|
|
101
105
|
});
|
|
102
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* The kind of reply the terminal part shows: the kind that `settle` acts on for a `resolved` or `rejected` row,
|
|
109
|
+
* and none for any other status. It never reads the feedback of a rejection, which is for the model.
|
|
110
|
+
*/
|
|
111
|
+
function replyKindOf(status, reply) {
|
|
112
|
+
switch (status) {
|
|
113
|
+
case "resolved":
|
|
114
|
+
return approvalOf(reply).kind;
|
|
115
|
+
case "rejected":
|
|
116
|
+
return "reject";
|
|
117
|
+
case "expired":
|
|
118
|
+
case "aborted":
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** The approval that a `resolved` row grants. */
|
|
123
|
+
function approvalOf(reply) {
|
|
124
|
+
// Only `once` / `always` reach `resolved`; guard the JSONB round-trip and
|
|
125
|
+
// default to the narrowest approval if a malformed reply ever slipped in.
|
|
126
|
+
return reply && (reply.kind === "once" || reply.kind === "always") ? reply : { kind: "once" };
|
|
127
|
+
}
|
|
103
128
|
/** Resolve an approval or throw the terminal denial the ledger recorded. */
|
|
104
129
|
function settle(status, reply, signal) {
|
|
105
130
|
switch (status) {
|
|
106
131
|
case "resolved":
|
|
107
|
-
|
|
108
|
-
// default to the narrowest approval if a malformed reply ever slipped in.
|
|
109
|
-
return reply && (reply.kind === "once" || reply.kind === "always") ? reply : { kind: "once" };
|
|
132
|
+
return approvalOf(reply);
|
|
110
133
|
case "rejected":
|
|
111
134
|
throw new AskRejectedError(reply && reply.kind === "reject" ? reply.feedback : undefined);
|
|
112
135
|
case "expired":
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* inspectCapabilityData — the reference data a specialist capability cannot run without, and
|
|
3
|
+
* whether this store holds it.
|
|
4
|
+
*
|
|
5
|
+
* The method of a capability lives in a skill pack, and only a sandbox step agent reads a skill.
|
|
6
|
+
* Thus a conversation or planning surface knows a capability by its phrase alone, and it cannot
|
|
7
|
+
* name the datasets the method needs. It then does one of two wrong things: it calls the work
|
|
8
|
+
* impossible, or it designs a method around what it can see and substitutes for what it cannot.
|
|
9
|
+
*
|
|
10
|
+
* The declaration on the agent metadata is the one place a surface above the step can read, and
|
|
11
|
+
* this tool is how it reads it. The answer names an absent dataset by its CATALOG ID, because the
|
|
12
|
+
* id is what an install takes — a surface that can provision needs the identifier, not a
|
|
13
|
+
* description. Where the ids reach an agent matters: a prompt names no dataset, so the knowledge
|
|
14
|
+
* arrives through a tool, which carries its own description into the context at attach time.
|
|
15
|
+
*/
|
|
16
|
+
import type { EnvironmentStorePaths } from "../config/environment-stores.js";
|
|
17
|
+
export type InspectCapabilityDataDeps = Pick<EnvironmentStorePaths, "refStorePath">;
|
|
18
|
+
/** One capability, its declared datasets, and what the store holds of them. */
|
|
19
|
+
export interface CapabilityDataRow {
|
|
20
|
+
readonly agentId: string;
|
|
21
|
+
readonly capability: string;
|
|
22
|
+
readonly installed: readonly string[];
|
|
23
|
+
readonly absent: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
export interface CapabilityDataResult {
|
|
26
|
+
/** False when the store could not be answered for: every `absent` list is then unknown, not empty. */
|
|
27
|
+
readonly storeReadable: boolean;
|
|
28
|
+
readonly rows: readonly CapabilityDataRow[];
|
|
29
|
+
readonly content: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build the tool. The store read happens per call, not at construction: a dataset installed during
|
|
33
|
+
* the conversation must show as installed on the next call, and a cached answer would have the
|
|
34
|
+
* agent offer a download the user already approved.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createInspectCapabilityDataTool(deps?: InspectCapabilityDataDeps): import("./define-tool.js").Tool<Record<string, never>, CapabilityDataResult>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* inspectCapabilityData — the reference data a specialist capability cannot run without, and
|
|
3
|
+
* whether this store holds it.
|
|
4
|
+
*
|
|
5
|
+
* The method of a capability lives in a skill pack, and only a sandbox step agent reads a skill.
|
|
6
|
+
* Thus a conversation or planning surface knows a capability by its phrase alone, and it cannot
|
|
7
|
+
* name the datasets the method needs. It then does one of two wrong things: it calls the work
|
|
8
|
+
* impossible, or it designs a method around what it can see and substitutes for what it cannot.
|
|
9
|
+
*
|
|
10
|
+
* The declaration on the agent metadata is the one place a surface above the step can read, and
|
|
11
|
+
* this tool is how it reads it. The answer names an absent dataset by its CATALOG ID, because the
|
|
12
|
+
* id is what an install takes — a surface that can provision needs the identifier, not a
|
|
13
|
+
* description. Where the ids reach an agent matters: a prompt names no dataset, so the knowledge
|
|
14
|
+
* arrives through a tool, which carries its own description into the context at attach time.
|
|
15
|
+
*/
|
|
16
|
+
import { ok } from "neverthrow";
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
import { declaredDataRequirements } from "../agents/sandbox-catalog.js";
|
|
19
|
+
import { defineTool } from "./define-tool.js";
|
|
20
|
+
import { readInstalledDatasetIds } from "./sandbox/list-available-refs.js";
|
|
21
|
+
const NO_DECLARATIONS = "No capability of this install declares reference data that it cannot run without.";
|
|
22
|
+
const STORE_UNREADABLE = "The reference store could not be read, so whether these datasets are installed is UNKNOWN — not absent. " +
|
|
23
|
+
"Say that, and do not offer to install a dataset on this answer alone.";
|
|
24
|
+
function renderRows(rows, storeReadable) {
|
|
25
|
+
if (rows.length === 0)
|
|
26
|
+
return NO_DECLARATIONS;
|
|
27
|
+
const lines = rows.map((row) => {
|
|
28
|
+
const state = storeReadable
|
|
29
|
+
? [row.absent.length > 0 ? `absent: ${row.absent.join(", ")}` : null, row.installed.length > 0 ? `installed: ${row.installed.join(", ")}` : null]
|
|
30
|
+
.filter((part) => part !== null)
|
|
31
|
+
.join(" | ")
|
|
32
|
+
: `declared: ${[...row.installed, ...row.absent].sort().join(", ")}`;
|
|
33
|
+
return `- ${row.agentId} — "${row.capability}"\n ${state}`;
|
|
34
|
+
});
|
|
35
|
+
const header = storeReadable
|
|
36
|
+
? "Reference data a capability cannot run without, and what this store holds. An absent id is what an install takes."
|
|
37
|
+
: `Reference data a capability cannot run without. ${STORE_UNREADABLE}`;
|
|
38
|
+
return [header, "", ...lines].join("\n");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build the tool. The store read happens per call, not at construction: a dataset installed during
|
|
42
|
+
* the conversation must show as installed on the next call, and a cached answer would have the
|
|
43
|
+
* agent offer a download the user already approved.
|
|
44
|
+
*/
|
|
45
|
+
export function createInspectCapabilityDataTool(deps = {}) {
|
|
46
|
+
return defineTool({
|
|
47
|
+
id: "inspect_capability_data",
|
|
48
|
+
description: "The reference data a specialist capability cannot run without, and whether this install holds it. " +
|
|
49
|
+
"Call it BEFORE you design a method yourself, and before you tell the user that something cannot be done — a capability whose data is absent is one install away, not impossible, and this is the only place that names WHICH datasets. " +
|
|
50
|
+
"Each entry names a catalog dataset id, which is exactly what an install takes; a surface with a way to provision can act on it directly. " +
|
|
51
|
+
"It takes no arguments and the whole set is small, so one call is the complete answer. " +
|
|
52
|
+
"An unreadable store answers UNKNOWN rather than absent, so a store that cannot be read never reads as a store that holds nothing.",
|
|
53
|
+
inputSchema: z.object({}),
|
|
54
|
+
describeCall: () => "declared capability data",
|
|
55
|
+
execute: async () => {
|
|
56
|
+
const declared = declaredDataRequirements();
|
|
57
|
+
if (declared.length === 0)
|
|
58
|
+
return ok({ storeReadable: true, rows: [], content: NO_DECLARATIONS });
|
|
59
|
+
const installedIds = await readInstalledDatasetIds(deps);
|
|
60
|
+
const storeReadable = installedIds !== undefined;
|
|
61
|
+
const rows = declared.map((requirement) => {
|
|
62
|
+
const datasets = [...requirement.datasets].sort();
|
|
63
|
+
return {
|
|
64
|
+
agentId: requirement.agentId,
|
|
65
|
+
capability: requirement.capability,
|
|
66
|
+
installed: installedIds === undefined ? [] : datasets.filter((dataset) => installedIds.has(dataset)),
|
|
67
|
+
absent: installedIds === undefined ? datasets : datasets.filter((dataset) => !installedIds.has(dataset)),
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
return ok({ storeReadable, rows, content: renderRows(rows, storeReadable) });
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
@@ -26,7 +26,7 @@ type ShowUserOutput = {
|
|
|
26
26
|
id: string;
|
|
27
27
|
};
|
|
28
28
|
export declare const showUserTool: import("../define-tool.js").Tool<{
|
|
29
|
-
kind: "code" | "
|
|
29
|
+
kind: "code" | "echart" | "markdown" | "svg" | "table" | "structure";
|
|
30
30
|
title?: string | undefined;
|
|
31
31
|
spec?: Record<string, unknown> | undefined;
|
|
32
32
|
dataPath?: string | undefined;
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* Unified async edge for approved plans and explicitly requested one-step ad
|
|
3
3
|
* hoc analyses. Both modes launch the ordinary executeAnalysis workflow.
|
|
4
4
|
*/
|
|
5
|
+
import { ResultAsync } from "neverthrow";
|
|
5
6
|
import type { Pool } from "pg";
|
|
7
|
+
import type { AgentSession } from "../auth/types.js";
|
|
6
8
|
import type { UsageRecorder } from "../billing/usage-recorder.js";
|
|
7
9
|
import type { EnvironmentStorePaths } from "../config/environment-stores.js";
|
|
8
10
|
import type { ResourcePolicy } from "../config/resource-limits.js";
|
|
@@ -15,7 +17,7 @@ import { type AnalysisPlan } from "../schemas/workflow-state.js";
|
|
|
15
17
|
import type { ExecuteAnalysisInput, ExecuteAnalysisResult } from "../workflows/execute-analysis.js";
|
|
16
18
|
import { type AdHocRoute } from "./ad-hoc-router.js";
|
|
17
19
|
type ExecuteAnalysisWorkflow = (input: ExecuteAnalysisInput) => Promise<ExecuteAnalysisResult>;
|
|
18
|
-
export interface ExecuteAnalysisToolDeps extends Pick<EnvironmentStorePaths, "farmLockFile" | "imagePackagesFile" | "readPoolInventory"> {
|
|
20
|
+
export interface ExecuteAnalysisToolDeps extends Pick<EnvironmentStorePaths, "farmLockFile" | "imagePackagesFile" | "readPoolInventory" | "refStorePath"> {
|
|
19
21
|
readonly pool: Pool;
|
|
20
22
|
readonly executeAnalysisWorkflow: ExecuteAnalysisWorkflow;
|
|
21
23
|
readonly runAuthorizer: RunAuthorizer;
|
|
@@ -54,7 +56,84 @@ export declare class PlanPackagesMissingError extends Error {
|
|
|
54
56
|
readonly unavailable?: string | undefined;
|
|
55
57
|
constructor(missing: string[], collisions: string[], unavailable?: string | undefined);
|
|
56
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* A declared dataset that the reference store does not hold, found at the pre-launch pass. The
|
|
61
|
+
* message names the datasets and no remedy command — the remedy belongs to the embedder, and its
|
|
62
|
+
* wrapper appends it.
|
|
63
|
+
*/
|
|
64
|
+
export declare class PlanReferencesMissingError extends Error {
|
|
65
|
+
readonly missing: string[];
|
|
66
|
+
readonly unavailable?: string | undefined;
|
|
67
|
+
constructor(missing: string[], unavailable?: string | undefined);
|
|
68
|
+
}
|
|
69
|
+
/** The deps that start the run of a stored plan. */
|
|
70
|
+
export type LaunchPlanRunDeps = Pick<ExecuteAnalysisToolDeps, "pool" | "executeAnalysisWorkflow" | "runAuthorizer" | "runLauncher" | "resourcePolicy" | "extendAnalysisFarm" | "refStorePath" | "logger">;
|
|
71
|
+
/**
|
|
72
|
+
* The run of a stored plan. `started` is false when this call started nothing: `runId` then names the
|
|
73
|
+
* active run of the plan, or the newest run of the plan when the view of the caller was stale.
|
|
74
|
+
*/
|
|
75
|
+
export type PlanLaunch = {
|
|
76
|
+
runId: string;
|
|
77
|
+
started: boolean;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Why a stored plan did not start a run. `store_failed.cause` and `launch_failed.cause` are the
|
|
81
|
+
* values as the failing call gave them, thus a caller that must throw them keeps their class.
|
|
82
|
+
*/
|
|
83
|
+
export type LaunchPlanRunRefusal = {
|
|
84
|
+
type: "plan_not_found";
|
|
85
|
+
planId: string;
|
|
86
|
+
} | {
|
|
87
|
+
type: "plan_invalid";
|
|
88
|
+
planId: string;
|
|
89
|
+
malformed: boolean;
|
|
90
|
+
errors: string[];
|
|
91
|
+
} | {
|
|
92
|
+
type: "packages_missing";
|
|
93
|
+
missing: string[];
|
|
94
|
+
collisions: string[];
|
|
95
|
+
unavailable?: string;
|
|
96
|
+
} | {
|
|
97
|
+
type: "references_missing";
|
|
98
|
+
missing: string[];
|
|
99
|
+
unavailable?: string;
|
|
100
|
+
} | {
|
|
101
|
+
type: "run_not_found";
|
|
102
|
+
runId: string;
|
|
103
|
+
} | {
|
|
104
|
+
type: "not_authorized";
|
|
105
|
+
reason: string;
|
|
106
|
+
} | {
|
|
107
|
+
type: "suspended";
|
|
108
|
+
reason: string;
|
|
109
|
+
} | {
|
|
110
|
+
type: "store_failed";
|
|
111
|
+
cause: unknown;
|
|
112
|
+
} | {
|
|
113
|
+
type: "launch_failed";
|
|
114
|
+
cause: unknown;
|
|
115
|
+
};
|
|
57
116
|
export declare function buildAdHocPlan(request: string, route: AdHocRoute, createdAt?: string): AnalysisPlan;
|
|
117
|
+
/**
|
|
118
|
+
* `newestRunId` is the newest run of the plan that the caller saw, or `null` when the caller saw no run.
|
|
119
|
+
* Without it, the launch compares no run.
|
|
120
|
+
*/
|
|
121
|
+
export type LaunchPlanRunInput = {
|
|
122
|
+
session: AgentSession;
|
|
123
|
+
planId: string;
|
|
124
|
+
newestRunId?: string | null;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Start the run of a stored plan. It reads the plan under the analysis of the session, validates it,
|
|
128
|
+
* compares the newest run of the plan with `newestRunId`, links its packages, reserves a run,
|
|
129
|
+
* authorizes it, and starts the workflow. It gives the run id, or the refusal, and it emits no chat
|
|
130
|
+
* part. `started` is false when this call started nothing: a run of the plan was already active, or the
|
|
131
|
+
* newest run of the plan is not the run that the caller saw. `runId` then names that run.
|
|
132
|
+
*
|
|
133
|
+
* This is an API boundary: a host calls it outside a tool body, thus no outcome above rejects the
|
|
134
|
+
* promise.
|
|
135
|
+
*/
|
|
136
|
+
export declare function launchPlanRun(deps: LaunchPlanRunDeps, input: LaunchPlanRunInput): ResultAsync<PlanLaunch, LaunchPlanRunRefusal>;
|
|
58
137
|
export declare function createExecuteAnalysisTool(deps: ExecuteAnalysisToolDeps): import("./define-tool.js").Tool<{
|
|
59
138
|
mode: "plan" | "adhoc";
|
|
60
139
|
planId?: string | undefined;
|