ai-runtime-engine 1.3.0 → 2.8.0
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/CHANGELOG.md +638 -0
- package/dist/agents/admit.d.ts +69 -0
- package/dist/agents/admit.js +129 -0
- package/dist/agents/definition.d.ts +36 -0
- package/dist/agents/definition.js +9 -0
- package/dist/agents/envelope.d.ts +53 -0
- package/dist/agents/envelope.js +68 -0
- package/dist/agents/finding.d.ts +79 -0
- package/dist/agents/finding.js +80 -0
- package/dist/agents/roles.d.ts +36 -0
- package/dist/agents/roles.js +44 -0
- package/dist/agents/synthesize.d.ts +44 -0
- package/dist/agents/synthesize.js +60 -0
- package/dist/agents/task.d.ts +112 -0
- package/dist/agents/task.js +48 -0
- package/dist/agents/worker.d.ts +91 -0
- package/dist/agents/worker.js +377 -0
- package/dist/capabilities/capability.d.ts +117 -0
- package/dist/capabilities/capability.js +66 -0
- package/dist/capabilities/registry.d.ts +139 -0
- package/dist/capabilities/registry.js +413 -0
- package/dist/capabilities/vocabulary.d.ts +32 -0
- package/dist/capabilities/vocabulary.js +34 -0
- package/dist/cli/cli.js +55 -4
- package/dist/cli/commands/cleanup.js +29 -27
- package/dist/cli/commands/doctor.d.ts +14 -0
- package/dist/cli/commands/doctor.js +38 -8
- package/dist/cli/commands/executions.js +34 -25
- package/dist/cli/commands/info.d.ts +1 -0
- package/dist/cli/commands/info.js +11 -9
- package/dist/cli/commands/init.js +19 -0
- package/dist/cli/commands/inspect.d.ts +40 -1
- package/dist/cli/commands/inspect.js +157 -2
- package/dist/cli/commands/mcp.d.ts +45 -0
- package/dist/cli/commands/mcp.js +148 -0
- package/dist/cli/commands/route.js +21 -0
- package/dist/cli/commands/run.d.ts +1 -0
- package/dist/cli/commands/run.js +21 -2
- package/dist/cli/commands/skills.d.ts +2 -0
- package/dist/cli/commands/skills.js +29 -7
- package/dist/cli/interactive/ansi.d.ts +41 -0
- package/dist/cli/interactive/ansi.js +43 -0
- package/dist/cli/interactive/complete.d.ts +10 -0
- package/dist/cli/interactive/complete.js +19 -0
- package/dist/cli/interactive/lanes.d.ts +69 -0
- package/dist/cli/interactive/lanes.js +181 -0
- package/dist/cli/interactive/repl.d.ts +3 -0
- package/dist/cli/interactive/repl.js +91 -13
- package/dist/cli/interactive/session.d.ts +8 -0
- package/dist/cli/interactive/session.js +73 -2
- package/dist/cli/render.d.ts +7 -0
- package/dist/cli/render.js +10 -0
- package/dist/cli/runtimeSession.d.ts +11 -0
- package/dist/cli/runtimeSession.js +17 -0
- package/dist/config/defaults.d.ts +3 -1
- package/dist/config/defaults.js +2 -0
- package/dist/config/schema.d.ts +1 -0
- package/dist/config/schema.js +2 -2
- package/dist/context/lossVerifier.d.ts +24 -0
- package/dist/context/lossVerifier.js +45 -0
- package/dist/context/summarize.d.ts +19 -0
- package/dist/context/summarize.js +53 -0
- package/dist/core/fallback/fallback.d.ts +5 -0
- package/dist/core/fallback/fallback.js +3 -1
- package/dist/core/router/router.d.ts +3 -0
- package/dist/core/router/router.js +1 -0
- package/dist/executions/agentTasks.d.ts +627 -0
- package/dist/executions/agentTasks.js +149 -0
- package/dist/executions/checkpoint.d.ts +5 -1
- package/dist/executions/checkpoint.js +13 -1
- package/dist/executions/execution.d.ts +36 -2
- package/dist/executions/store.d.ts +37 -0
- package/dist/executions/store.js +33 -0
- package/dist/generation/generateAdapter.d.ts +14 -0
- package/dist/generation/generateAdapter.js +38 -0
- package/dist/generation/generateSkill.d.ts +26 -0
- package/dist/generation/generateSkill.js +51 -0
- package/dist/index.d.ts +47 -4
- package/dist/index.js +33 -2
- package/dist/mcp/client.d.ts +70 -0
- package/dist/mcp/client.js +221 -0
- package/dist/mcp/manager.d.ts +151 -0
- package/dist/mcp/manager.js +493 -0
- package/dist/mcp/protocol.d.ts +216 -0
- package/dist/mcp/protocol.js +149 -0
- package/dist/mcp/toolAdapter.d.ts +44 -0
- package/dist/mcp/toolAdapter.js +94 -0
- package/dist/mcp/transport.d.ts +109 -0
- package/dist/mcp/transport.js +383 -0
- package/dist/memory/embedders/hash.d.ts +12 -0
- package/dist/memory/embedders/hash.js +31 -0
- package/dist/memory/embedders/http.d.ts +25 -0
- package/dist/memory/embedders/http.js +48 -0
- package/dist/memory/memory.d.ts +19 -2
- package/dist/memory/memory.js +75 -11
- package/dist/memory/semantic.d.ts +17 -0
- package/dist/memory/semantic.js +29 -0
- package/dist/orchestration/budget.d.ts +30 -0
- package/dist/orchestration/budget.js +40 -0
- package/dist/orchestration/executor.d.ts +64 -1
- package/dist/orchestration/executor.js +104 -7
- package/dist/orchestration/orchestrator.d.ts +35 -1
- package/dist/orchestration/orchestrator.js +106 -8
- package/dist/orchestration/plan.d.ts +15 -1
- package/dist/orchestration/plan.js +23 -4
- package/dist/orchestration/planner.d.ts +19 -1
- package/dist/orchestration/planner.js +25 -5
- package/dist/plugin/ai.d.ts +4 -0
- package/dist/plugin/ai.js +9 -0
- package/dist/runtime/config.js +50 -6
- package/dist/runtime/intent/aiClassifier.d.ts +19 -0
- package/dist/runtime/intent/aiClassifier.js +74 -0
- package/dist/runtime/models/modelProfile.d.ts +61 -0
- package/dist/runtime/models/modelProfile.js +139 -0
- package/dist/runtime/planning/deriveCapabilities.d.ts +95 -0
- package/dist/runtime/planning/deriveCapabilities.js +146 -0
- package/dist/runtime/policy.d.ts +10 -0
- package/dist/runtime/policy.js +9 -2
- package/dist/runtime/runtime.d.ts +233 -0
- package/dist/runtime/runtime.js +1042 -60
- package/dist/runtime/types.d.ts +88 -2
- package/dist/security/redact.js +22 -10
- package/dist/skills/manifest.d.ts +3 -0
- package/dist/skills/manifest.js +24 -0
- package/dist/skills/registry.d.ts +16 -1
- package/dist/skills/registry.js +21 -1
- package/dist/skills/skill.d.ts +6 -1
- package/dist/store/area.d.ts +15 -1
- package/dist/store/area.js +19 -8
- package/dist/store/crypto.d.ts +21 -0
- package/dist/store/crypto.js +49 -0
- package/dist/store/paths.d.ts +5 -1
- package/dist/store/paths.js +6 -0
- package/dist/store/store.d.ts +15 -3
- package/dist/store/store.js +28 -7
- package/dist/telemetry/sinks/otlp.d.ts +31 -0
- package/dist/telemetry/sinks/otlp.js +76 -0
- package/dist/tools/builtins/filesystem.js +1 -0
- package/dist/tools/builtins/git.js +1 -0
- package/dist/tools/builtins/shell.js +1 -0
- package/dist/tools/permissions.d.ts +28 -0
- package/dist/tools/permissions.js +72 -0
- package/dist/tools/registry.d.ts +18 -2
- package/dist/tools/registry.js +22 -2
- package/dist/tools/tool.d.ts +4 -0
- package/dist/types.d.ts +5 -1
- package/dist/util/flatten.d.ts +11 -0
- package/dist/util/flatten.js +18 -0
- package/dist/util/hash.d.ts +19 -0
- package/dist/util/hash.js +39 -0
- package/dist/util/semaphore.d.ts +19 -0
- package/dist/util/semaphore.js +60 -0
- package/package.json +24 -9
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synthesizing agents from the registry (Phase 3.7) — auto-decomposition.
|
|
3
|
+
*
|
|
4
|
+
* THE TOOL SET IS THE WHOLE PROBLEM. A capability's providers are NAMESPACED ids (`tool:filesystem`)
|
|
5
|
+
* while the tool registry holds bare ones (`filesystem`), and `narrowEnvelope` intersects against the
|
|
6
|
+
* bare set. Emitting provider ids directly yields an empty catalog for every derived agent — a feature
|
|
7
|
+
* that is inert while looking correct, and whose obvious tests ("a derived agent cannot name a tool
|
|
8
|
+
* outside the registry") pass vacuously on the empty array. So the prefix is stripped here, membership
|
|
9
|
+
* in the parent catalog is asserted here, and the tests assert a NON-EMPTY result.
|
|
10
|
+
*
|
|
11
|
+
* WHAT IS NOT USED: BM25 relevance. It was measured to abstain on exactly the goals these roles exist
|
|
12
|
+
* for — "review the changed files", "find the bug", "audit the auth module" all derive nothing, because
|
|
13
|
+
* its thresholds were tuned for a different job (advisory capability hints, where a false positive
|
|
14
|
+
* merely over-advises). Gating whether an agent EXISTS on that calibration would make decomposition a
|
|
15
|
+
* silent no-op for the goals it is for. Roles are offered whenever they have tools; which one fits the
|
|
16
|
+
* goal is the planner's judgment, made from the role descriptions, which is exactly its job.
|
|
17
|
+
*/
|
|
18
|
+
import { AGENT_ROLES, roleAgentId } from './roles.js';
|
|
19
|
+
/** Strip the `tool:` / `skill:` source prefix from a provider id. */
|
|
20
|
+
function bareProviderId(providerId) {
|
|
21
|
+
const at = providerId.indexOf(':');
|
|
22
|
+
return at < 0 ? { kind: '', id: providerId } : { kind: providerId.slice(0, at), id: providerId.slice(at + 1) };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Offer one bounded agent per role that has at least one usable tool.
|
|
26
|
+
*
|
|
27
|
+
* A role draws the parent's tools that provide at least one READ-effect capability. Effects are
|
|
28
|
+
* advisory metadata and never authorization (invariant 25) — the permission clamp in `narrowEnvelope`
|
|
29
|
+
* is what actually denies writing. Using them here only NARROWS what a machine-generated agent is
|
|
30
|
+
* offered, which fails safe: a tool with no declared effects is inferred write-shaped and excluded.
|
|
31
|
+
*/
|
|
32
|
+
export function synthesizeAgents(input) {
|
|
33
|
+
const parent = new Set(input.parentTools);
|
|
34
|
+
const readable = new Set();
|
|
35
|
+
for (const cap of input.capabilities) {
|
|
36
|
+
if (!cap.effects.includes('read') || cap.effects.includes('write'))
|
|
37
|
+
continue;
|
|
38
|
+
for (const providerId of cap.providers) {
|
|
39
|
+
const { kind, id } = bareProviderId(providerId);
|
|
40
|
+
// Tools only: a skill brings its own tool requirements, and a derived agent gets no skills.
|
|
41
|
+
if (kind === 'tool' && parent.has(id))
|
|
42
|
+
readable.add(id);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const tools = [...readable].sort();
|
|
46
|
+
if (tools.length === 0)
|
|
47
|
+
return []; // nothing to read with ⇒ nothing worth offering
|
|
48
|
+
return (input.roles ?? AGENT_ROLES).map((role) => ({
|
|
49
|
+
id: roleAgentId(role.id),
|
|
50
|
+
roleId: role.id,
|
|
51
|
+
definition: {
|
|
52
|
+
objective: role.objective,
|
|
53
|
+
tools,
|
|
54
|
+
// No skills: a skill's declared tools would have to be re-checked against the envelope, and a
|
|
55
|
+
// derived agent has no operator to have vetted that.
|
|
56
|
+
skills: [],
|
|
57
|
+
outputContract: role.outputContract,
|
|
58
|
+
},
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent task state (Phase 3.4) - the record, and THE PROJECTION TABLE.
|
|
3
|
+
*
|
|
4
|
+
* The state set is the PRD's; no state is invented here. Every one of them projects onto the EXISTING
|
|
5
|
+
* PlanStepStatus / ExecutionStatus vocabularies, and that is deliberate: 3.4 adds no new status
|
|
6
|
+
* anywhere, which is what stops a later phase from needing one.
|
|
7
|
+
*
|
|
8
|
+
* An INTERRUPTION IS NOT A STATE. A crash, a parent pause, or a parent cancel leaves the task in a
|
|
9
|
+
* normal state and records WHY in `interruption`, so the reason is auditable without growing the
|
|
10
|
+
* lifecycle.
|
|
11
|
+
*/
|
|
12
|
+
import type { ExecutionPlan, PlanStepStatus } from '../orchestration/plan.js';
|
|
13
|
+
import type { StepObservation, StepObservationCode } from '../orchestration/executor.js';
|
|
14
|
+
import type { Checkpoint, ExecutionStatus } from '../executions/execution.js';
|
|
15
|
+
import type { AdmissionRejection } from './admit.js';
|
|
16
|
+
import type { Finding } from './finding.js';
|
|
17
|
+
export type AgentTaskState = 'created' | 'queued' | 'running' | 'completed' | 'failed' | 'cancelled' | 'waiting_for_input' | 'waiting_for_clarification' | 'paused';
|
|
18
|
+
export interface AgentTaskRecord {
|
|
19
|
+
/** Phase 3.5: record schema version. A record whose `v` is not 1 is DROPPED on read, never coerced —
|
|
20
|
+
* a future shape must not be silently reinterpreted as this one. */
|
|
21
|
+
v: 1;
|
|
22
|
+
/** `ag_<base36 now>_<pid>_<counter>`. `agentTaskId`, NEVER `taskId` - that name belongs to the router. */
|
|
23
|
+
agentTaskId: string;
|
|
24
|
+
agentId: string;
|
|
25
|
+
stepId: string;
|
|
26
|
+
state: AgentTaskState;
|
|
27
|
+
createdAt: number;
|
|
28
|
+
/** Phase 3.5: last write. Distinct from `endedAt` — a task is written many times before it ends. */
|
|
29
|
+
updatedAt: number;
|
|
30
|
+
startedAt?: number;
|
|
31
|
+
endedAt?: number;
|
|
32
|
+
/** Additive, and NOT a state. Carries why a non-terminal task stopped. Cleared when a resumed task is
|
|
33
|
+
* promoted back to `running`, so the stamp always describes the LATEST interruption (Phase 3.5). */
|
|
34
|
+
interruption?: {
|
|
35
|
+
kind: 'crash' | 'pause' | 'parent-cancel';
|
|
36
|
+
at: number;
|
|
37
|
+
detail?: string;
|
|
38
|
+
};
|
|
39
|
+
/** Which run/plan minted this record. */
|
|
40
|
+
provenance: {
|
|
41
|
+
executionId?: string;
|
|
42
|
+
planVersion: number;
|
|
43
|
+
};
|
|
44
|
+
/** Hash of the AgentDefinition. An operator who edits the definition invalidates the inner plan. */
|
|
45
|
+
agentDefHash: string;
|
|
46
|
+
/** Hash of the NARROWED envelope — the tools/permissions/caps the inner plan was built against. */
|
|
47
|
+
envelopeHash: string;
|
|
48
|
+
/** Hash of the STEP, not its id. Plan step ids (`s1`, `auto1`) recur across replans, so id-matching
|
|
49
|
+
* would bind a record to a different step's work; the input is what makes the step this step. */
|
|
50
|
+
stepInputHash: string;
|
|
51
|
+
/** How many times this task has been started. Findings minted on attempt 2 are namespaced by it, so a
|
|
52
|
+
* resumed attempt can never mint an id that collides with a finding already on the record. */
|
|
53
|
+
attempt: number;
|
|
54
|
+
innerPlanVersion?: number;
|
|
55
|
+
/** The inner plan with per-step statuses. Dropped (never truncated) when oversized — a half-written
|
|
56
|
+
* plan is worse than none, because it would be executed. */
|
|
57
|
+
innerPlan?: ExecutionPlan;
|
|
58
|
+
/** THE resume primitive: inner steps already done, fed straight to `executePlan`'s `skip`. Survives
|
|
59
|
+
* `innerPlan` being dropped, so a too-large plan still costs re-planning, never re-execution. */
|
|
60
|
+
innerCompletedSteps: string[];
|
|
61
|
+
/** Bounded slice of inner observations, in executor order. */
|
|
62
|
+
innerObservations: StepObservation[];
|
|
63
|
+
innerObservationsOmitted: number;
|
|
64
|
+
/** Workspace fingerprint of the inner run — the outer checkpoint cannot see files only an agent touched. */
|
|
65
|
+
innerCheckpoint?: Checkpoint;
|
|
66
|
+
/** The one inner wait that is reachable: the inner planner asked a question. */
|
|
67
|
+
pendingInner?: {
|
|
68
|
+
kind: 'clarification';
|
|
69
|
+
question: string;
|
|
70
|
+
at: number;
|
|
71
|
+
};
|
|
72
|
+
innerSteps: {
|
|
73
|
+
total: number;
|
|
74
|
+
succeeded: number;
|
|
75
|
+
};
|
|
76
|
+
/** What the step RESERVED from the outer pool (`envelope.reservation`). */
|
|
77
|
+
callsReserved: number;
|
|
78
|
+
/** Cumulative across attempts. Charged, never refunded downward. */
|
|
79
|
+
callsUsed: number;
|
|
80
|
+
/** DERIVED for reporting only — `max(0, reserved - used)`. Nothing computes a budget from this. */
|
|
81
|
+
callsRefunded: number;
|
|
82
|
+
toolCallsUsed: number;
|
|
83
|
+
findings: Finding[];
|
|
84
|
+
/** Candidates the admission pipeline REFUSED. Auditable, and never threaded anywhere. */
|
|
85
|
+
diagnostics: AdmissionRejection[];
|
|
86
|
+
failure?: {
|
|
87
|
+
code: StepObservationCode;
|
|
88
|
+
message: string;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/** Terminal agent-task states: reached once, never left. A commit may not move a task out of one. */
|
|
92
|
+
export declare const AGENT_TERMINAL: ReadonlySet<AgentTaskState>;
|
|
93
|
+
/** States a persisted task may be resumed from. Exactly the complement of AGENT_TERMINAL. */
|
|
94
|
+
export declare const AGENT_RESUMABLE: ReadonlySet<AgentTaskState>;
|
|
95
|
+
/** One row per agent-task state. Read this table; never re-derive a projection at a call site. */
|
|
96
|
+
export interface ProjectionRow {
|
|
97
|
+
step: PlanStepStatus;
|
|
98
|
+
exec: ExecutionStatus;
|
|
99
|
+
code?: StepObservationCode;
|
|
100
|
+
/** False for the states only a persisted, resumable task can reach (Phase 3.5). */
|
|
101
|
+
reachableIn34: boolean;
|
|
102
|
+
}
|
|
103
|
+
export declare const AGENT_TASK_PROJECTION: Readonly<Record<AgentTaskState, ProjectionRow>>;
|
|
104
|
+
/** The states an agent task can actually reach in 3.4. */
|
|
105
|
+
export declare const REACHABLE_IN_34: AgentTaskState[];
|
|
106
|
+
/**
|
|
107
|
+
* Mint an agent task id. Base36 clock + PID + a counter, so ids are stable under a fake clock and still
|
|
108
|
+
* unique across processes. The pid is not decoration: tests freeze the clock, and two Runtimes in one
|
|
109
|
+
* process would otherwise mint the same id — a collision that propagates straight into `Finding.id`
|
|
110
|
+
* (`${agentTaskId}_f<n>`) and would silently merge two agents' findings.
|
|
111
|
+
*/
|
|
112
|
+
export declare function nextAgentTaskId(now: number): string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent task state (Phase 3.4) - the record, and THE PROJECTION TABLE.
|
|
3
|
+
*
|
|
4
|
+
* The state set is the PRD's; no state is invented here. Every one of them projects onto the EXISTING
|
|
5
|
+
* PlanStepStatus / ExecutionStatus vocabularies, and that is deliberate: 3.4 adds no new status
|
|
6
|
+
* anywhere, which is what stops a later phase from needing one.
|
|
7
|
+
*
|
|
8
|
+
* An INTERRUPTION IS NOT A STATE. A crash, a parent pause, or a parent cancel leaves the task in a
|
|
9
|
+
* normal state and records WHY in `interruption`, so the reason is auditable without growing the
|
|
10
|
+
* lifecycle.
|
|
11
|
+
*/
|
|
12
|
+
/** Terminal agent-task states: reached once, never left. A commit may not move a task out of one. */
|
|
13
|
+
export const AGENT_TERMINAL = new Set(['completed', 'failed', 'cancelled']);
|
|
14
|
+
/** States a persisted task may be resumed from. Exactly the complement of AGENT_TERMINAL. */
|
|
15
|
+
export const AGENT_RESUMABLE = new Set([
|
|
16
|
+
'created',
|
|
17
|
+
'queued',
|
|
18
|
+
'running',
|
|
19
|
+
'waiting_for_input',
|
|
20
|
+
'waiting_for_clarification',
|
|
21
|
+
'paused',
|
|
22
|
+
]);
|
|
23
|
+
export const AGENT_TASK_PROJECTION = {
|
|
24
|
+
created: { step: 'pending', exec: 'running', reachableIn34: true },
|
|
25
|
+
queued: { step: 'pending', exec: 'running', reachableIn34: true },
|
|
26
|
+
running: { step: 'running', exec: 'running', reachableIn34: true },
|
|
27
|
+
completed: { step: 'succeeded', exec: 'completed', reachableIn34: true },
|
|
28
|
+
failed: { step: 'failed', exec: 'failed', code: 'agent-failed', reachableIn34: true },
|
|
29
|
+
cancelled: { step: 'skipped', exec: 'cancelled', code: 'cancelled', reachableIn34: true },
|
|
30
|
+
// Declared, not reachable in 3.4: nothing persists an AgentTaskRecord yet, so nothing can be resumed
|
|
31
|
+
// into a waiting state. The rows exist so 3.5 inherits a mapping instead of inventing one.
|
|
32
|
+
waiting_for_input: { step: 'pending', exec: 'waiting_for_input', reachableIn34: false },
|
|
33
|
+
waiting_for_clarification: { step: 'pending', exec: 'waiting_for_clarification', reachableIn34: false },
|
|
34
|
+
paused: { step: 'pending', exec: 'paused', reachableIn34: false },
|
|
35
|
+
};
|
|
36
|
+
/** The states an agent task can actually reach in 3.4. */
|
|
37
|
+
export const REACHABLE_IN_34 = Object.keys(AGENT_TASK_PROJECTION).filter((s) => AGENT_TASK_PROJECTION[s].reachableIn34);
|
|
38
|
+
let counter = 0;
|
|
39
|
+
/**
|
|
40
|
+
* Mint an agent task id. Base36 clock + PID + a counter, so ids are stable under a fake clock and still
|
|
41
|
+
* unique across processes. The pid is not decoration: tests freeze the clock, and two Runtimes in one
|
|
42
|
+
* process would otherwise mint the same id — a collision that propagates straight into `Finding.id`
|
|
43
|
+
* (`${agentTaskId}_f<n>`) and would silently merge two agents' findings.
|
|
44
|
+
*/
|
|
45
|
+
export function nextAgentTaskId(now) {
|
|
46
|
+
counter += 1;
|
|
47
|
+
return `ag_${now.toString(36)}_${process.pid.toString(36)}_${counter.toString(36)}`;
|
|
48
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent worker (Phase 3.4) - runs ONE agent step.
|
|
3
|
+
*
|
|
4
|
+
* IT NEVER CALLS `orchestrate()`. It composes `generatePlan` + `executePlan` directly, because
|
|
5
|
+
* re-entering the orchestrator inside a step would re-run the parent's gates per step: re-asking for
|
|
6
|
+
* approval, re-applying the parent call budget, and re-checking plan/dry-run mode. A useful consequence
|
|
7
|
+
* of that same structure is that a dry run can never reach a worker at all - `orchestrate` returns
|
|
8
|
+
* before `executePlan` is ever called.
|
|
9
|
+
*
|
|
10
|
+
* IT NEVER CONSTRUCTS AN `AI`. One router: the inner planning call goes through a METERED FACADE over
|
|
11
|
+
* the parent's AI, so every inner model call is counted against the envelope's reservation - including
|
|
12
|
+
* the ones a skill makes through `SkillContext.ai`, which the parent budget cannot see today.
|
|
13
|
+
*
|
|
14
|
+
* DEPTH 1 IS STRUCTURAL: the inner plan is generated with `agents: []`, so `validatePlan` rejects a
|
|
15
|
+
* nested agent step as `unknown agent`. There is no runtime depth counter to get wrong.
|
|
16
|
+
*/
|
|
17
|
+
import { AI } from '../plugin/ai.js';
|
|
18
|
+
import type { AgentTaskRecord } from './task.js';
|
|
19
|
+
import type { AgentEnvelope } from './envelope.js';
|
|
20
|
+
import type { AgentDefinition } from './definition.js';
|
|
21
|
+
import type { PlanStep } from '../orchestration/plan.js';
|
|
22
|
+
import type { StepObservation } from '../orchestration/executor.js';
|
|
23
|
+
import type { Skill, SkillResult, SkillValidation } from '../skills/skill.js';
|
|
24
|
+
import type { ToolResult } from '../tools/tool.js';
|
|
25
|
+
import type { ArtifactRef } from '../runtime/types.js';
|
|
26
|
+
import type { PermissionPolicy } from '../runtime/policy.js';
|
|
27
|
+
import type { Clock } from '../util/clock.js';
|
|
28
|
+
/** How much of a step input may reach the inner prompt. It is model-authored, and it is fenced. */
|
|
29
|
+
export declare const AGENT_INPUT_MAX = 1000;
|
|
30
|
+
/** Inner observations persisted per task. The record lives in a JSON file that is rewritten every
|
|
31
|
+
* commit, so this is a durability bound, not a display one. Beyond it, the count is kept and the
|
|
32
|
+
* content dropped — an honest "there was more" rather than a silently short list. */
|
|
33
|
+
export declare const INNER_OBS_MAX = 20;
|
|
34
|
+
/** Admission rejections kept per task. They accumulate across attempts and are rewritten every commit. */
|
|
35
|
+
export declare const DIAGNOSTICS_KEPT = 50;
|
|
36
|
+
export interface AgentWorkerDeps {
|
|
37
|
+
ai: AI;
|
|
38
|
+
clock: Clock;
|
|
39
|
+
/** Narrowed to the envelope by the caller; the worker does not re-derive a catalog. */
|
|
40
|
+
skills: Skill[];
|
|
41
|
+
runSkill: (id: string, input: unknown, overrides: {
|
|
42
|
+
permissions: PermissionPolicy;
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
ai?: AI;
|
|
45
|
+
}) => Promise<{
|
|
46
|
+
result: SkillResult;
|
|
47
|
+
validation: SkillValidation;
|
|
48
|
+
}>;
|
|
49
|
+
runTool: (id: string, input: unknown, overrides: {
|
|
50
|
+
permissions: PermissionPolicy;
|
|
51
|
+
signal?: AbortSignal;
|
|
52
|
+
}) => Promise<ToolResult>;
|
|
53
|
+
/** Store evidence content. Returns `unavailable` rather than a dangling ref when the store is off. */
|
|
54
|
+
putArtifact: (content: string, source: string) => {
|
|
55
|
+
ref?: ArtifactRef;
|
|
56
|
+
unavailable: boolean;
|
|
57
|
+
};
|
|
58
|
+
emit?: (event: {
|
|
59
|
+
type: 'agent.task.started' | 'agent.task.completed';
|
|
60
|
+
record: AgentTaskRecord;
|
|
61
|
+
}) => void;
|
|
62
|
+
parentSignal?: AbortSignal;
|
|
63
|
+
/** Why the parent aborted, when it knows. Distinguishes a cancel from a pause on the record. */
|
|
64
|
+
abortReason?: () => 'pause' | 'parent-cancel' | undefined;
|
|
65
|
+
provenance: {
|
|
66
|
+
executionId?: string;
|
|
67
|
+
planVersion: number;
|
|
68
|
+
};
|
|
69
|
+
/** Phase 3.5: called whenever the record MATERIALLY changes, so inner progress reaches disk while the
|
|
70
|
+
* agent is still running. Without a seam inside the inner run, everything between `running` and
|
|
71
|
+
* `finish()` — the inner plan, every completed inner step, every inner model call — is lost to a
|
|
72
|
+
* crash, and the resume has nothing to skip. Synchronous; must not throw. */
|
|
73
|
+
onRecord?: (record: AgentTaskRecord) => void;
|
|
74
|
+
/** Phase 3.5: a persisted record to CONTINUE instead of minting a fresh one. The caller proves it
|
|
75
|
+
* belongs to THIS step by step-input hash before passing it. */
|
|
76
|
+
resume?: AgentTaskRecord;
|
|
77
|
+
/** Phase 3.5: the human's answer to this task's inner clarification. Model-authored text from the
|
|
78
|
+
* user, so it is fenced into the inner goal like any other untrusted input. */
|
|
79
|
+
resumeAnswer?: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* What makes a step THIS step. Plan step ids (`s1`, `auto1`) are model-authored and recur across
|
|
83
|
+
* replans, so binding a persisted record by id alone would hand one step's completed inner work to a
|
|
84
|
+
* different step that happens to share its id — same agent, different input, silently wrong findings.
|
|
85
|
+
*/
|
|
86
|
+
export declare function stepIdentity(step: PlanStep): string;
|
|
87
|
+
export interface RunAgentTaskResult {
|
|
88
|
+
observation: StepObservation;
|
|
89
|
+
record: AgentTaskRecord;
|
|
90
|
+
}
|
|
91
|
+
export declare function runAgentTask(step: PlanStep, envelope: AgentEnvelope, definition: AgentDefinition, deps: AgentWorkerDeps): Promise<RunAgentTaskResult>;
|