@vimhead.dev/norn-cli 0.1.0-tip.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/README.md +13 -0
- package/assets/README.md +157 -0
- package/assets/docs/README.md +23 -0
- package/assets/docs/agents.md +64 -0
- package/assets/docs/cli.md +183 -0
- package/assets/docs/composition.md +76 -0
- package/assets/docs/persistence.md +56 -0
- package/assets/docs/projects.md +75 -0
- package/assets/docs/recovery.md +56 -0
- package/assets/docs/resources.md +61 -0
- package/assets/docs/workflows.md +57 -0
- package/assets/examples/agent-then-analysis/README.md +103 -0
- package/assets/examples/agent-then-analysis/input.json +5 -0
- package/assets/examples/agent-then-analysis/norn.project.json +4 -0
- package/assets/examples/agent-then-analysis/plugin.ts +89 -0
- package/assets/examples/coordinating-multiple-agents/README.md +56 -0
- package/assets/examples/coordinating-multiple-agents/input.json +10 -0
- package/assets/examples/coordinating-multiple-agents/norn.project.json +4 -0
- package/assets/examples/coordinating-multiple-agents/plugin.ts +102 -0
- package/assets/examples/coordinating-multiple-agents/queue-adapter.ts +52 -0
- package/assets/examples/coordinating-multiple-agents/work-queue.ts +153 -0
- package/assets/examples/minimal-workflow/README.md +71 -0
- package/assets/examples/minimal-workflow/norn.project.json +4 -0
- package/assets/examples/minimal-workflow/plugin.ts +29 -0
- package/assets/examples/shared-state/README.md +19 -0
- package/assets/examples/shared-state/input.json +1 -0
- package/assets/examples/shared-state/norn.project.json +4 -0
- package/assets/examples/shared-state/plugin.ts +47 -0
- package/assets/examples/worktree-development-loop/README.md +66 -0
- package/assets/examples/worktree-development-loop/index.ts +1 -0
- package/assets/examples/worktree-development-loop/manifest.ts +26 -0
- package/assets/examples/worktree-development-loop/norn.project.json +9 -0
- package/assets/examples/worktree-development-loop/plugin.ts +27 -0
- package/assets/examples/worktree-development-loop/shared/commands.ts +6 -0
- package/assets/examples/worktree-development-loop/state.ts +23 -0
- package/assets/examples/worktree-development-loop/workflows/development-loop/declaration.ts +8 -0
- package/assets/examples/worktree-development-loop/workflows/development-loop/execute.ts +18 -0
- package/assets/examples/worktree-development-loop/workflows/development-loop/index.ts +4 -0
- package/assets/examples/worktree-development-loop/workflows/development-loop/repository.ts +22 -0
- package/assets/examples/worktree-development-loop/workflows/development-loop/schema.ts +14 -0
- package/assets/examples/worktree-development-loop/workflows/implementation/declaration.ts +8 -0
- package/assets/examples/worktree-development-loop/workflows/implementation/execute.ts +54 -0
- package/assets/examples/worktree-development-loop/workflows/implementation/index.ts +3 -0
- package/assets/examples/worktree-development-loop/workflows/implementation/schema.ts +12 -0
- package/assets/examples/worktree-development-loop/workflows/planning/declaration.ts +8 -0
- package/assets/examples/worktree-development-loop/workflows/planning/execute.ts +28 -0
- package/assets/examples/worktree-development-loop/workflows/planning/index.ts +3 -0
- package/assets/examples/worktree-development-loop/workflows/planning/schema.ts +12 -0
- package/assets/examples/worktree-development-loop/workflows/review/declaration.ts +8 -0
- package/assets/examples/worktree-development-loop/workflows/review/execute.ts +53 -0
- package/assets/examples/worktree-development-loop/workflows/review/index.ts +10 -0
- package/assets/examples/worktree-development-loop/workflows/review/schema.ts +23 -0
- package/assets/examples/worktree-development-loop/workflows/review-router/declaration.ts +12 -0
- package/assets/examples/worktree-development-loop/workflows/review-router/execute.ts +51 -0
- package/assets/examples/worktree-development-loop/workflows/review-router/index.ts +3 -0
- package/assets/examples/worktree-development-loop/workflows/review-router/schema.ts +12 -0
- package/assets/package.json +1 -0
- package/assets/packages/cli/src/build-info.ts +36 -0
- package/assets/packages/cli/src/bun/cli.ts +16 -0
- package/assets/packages/cli/src/cli.ts +1135 -0
- package/assets/packages/cli/src/client.ts +167 -0
- package/assets/packages/cli/src/documentation-intro.ts +30 -0
- package/assets/packages/cli/src/documentation.ts +149 -0
- package/assets/packages/cli/src/generated-build-info.ts +12 -0
- package/assets/packages/cli/src/internal/agent-directory.ts +5 -0
- package/assets/packages/cli/src/internal/agent-response-tool.ts +96 -0
- package/assets/packages/cli/src/internal/agents.ts +365 -0
- package/assets/packages/cli/src/internal/artifacts.ts +26 -0
- package/assets/packages/cli/src/internal/commands.ts +180 -0
- package/assets/packages/cli/src/internal/documentation-bundle.ts +49 -0
- package/assets/packages/cli/src/internal/engine.ts +501 -0
- package/assets/packages/cli/src/internal/errors.ts +39 -0
- package/assets/packages/cli/src/internal/file-names.ts +3 -0
- package/assets/packages/cli/src/internal/launch-request.ts +94 -0
- package/assets/packages/cli/src/internal/logs.ts +41 -0
- package/assets/packages/cli/src/internal/metrics.ts +356 -0
- package/assets/packages/cli/src/internal/pi-assets.ts +95 -0
- package/assets/packages/cli/src/internal/resource-bindings.ts +35 -0
- package/assets/packages/cli/src/internal/run-lease.ts +158 -0
- package/assets/packages/cli/src/internal/run-log.ts +59 -0
- package/assets/packages/cli/src/internal/run-names.ts +36 -0
- package/assets/packages/cli/src/internal/run-resources.ts +23 -0
- package/assets/packages/cli/src/internal/run-state.ts +380 -0
- package/assets/packages/cli/src/internal/run-store.ts +323 -0
- package/assets/packages/cli/src/internal/run.ts +133 -0
- package/assets/packages/cli/src/internal/state-store.ts +75 -0
- package/assets/packages/cli/src/internal/usage.ts +70 -0
- package/assets/packages/cli/src/internal/workflow-registry.ts +176 -0
- package/assets/packages/cli/src/plugin-loader.ts +412 -0
- package/assets/packages/cli/src/resources.ts +67 -0
- package/assets/packages/core/src/agent-protocol.ts +1 -0
- package/assets/packages/core/src/atomic-files.ts +24 -0
- package/assets/packages/core/src/errors.ts +3 -0
- package/assets/packages/sdk/src/agent-resource-adapter.ts +11 -0
- package/assets/packages/sdk/src/api.ts +821 -0
- package/assets/packages/sdk/src/files.ts +136 -0
- package/assets/packages/sdk/src/index.ts +6 -0
- package/assets/packages/sdk/src/resources.ts +20 -0
- package/assets/packages/sdk/src/schema.ts +48 -0
- package/assets/packages/sdk/src/seer/config.ts +62 -0
- package/assets/packages/sdk/src/seer/index.ts +7 -0
- package/assets/packages/sdk/src/state-adapter.ts +75 -0
- package/assets/setup/providers.md +128 -0
- package/assets/setup/releases.md +76 -0
- package/assets/tests/workflow-ref.test.ts +113 -0
- package/bin/norn.mjs +10 -0
- package/dist/build-info.d.ts +30 -0
- package/dist/build-info.js +6 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1032 -0
- package/dist/client.d.ts +48 -0
- package/dist/client.js +118 -0
- package/dist/documentation-intro.d.ts +5 -0
- package/dist/documentation-intro.js +29 -0
- package/dist/documentation.d.ts +33 -0
- package/dist/documentation.js +132 -0
- package/dist/generated-build-info.d.ts +10 -0
- package/dist/generated-build-info.js +14 -0
- package/dist/internal/agent-directory.d.ts +4 -0
- package/dist/internal/agent-directory.js +8 -0
- package/dist/internal/agent-response-tool.d.ts +21 -0
- package/dist/internal/agent-response-tool.js +79 -0
- package/dist/internal/agents.d.ts +29 -0
- package/dist/internal/agents.js +336 -0
- package/dist/internal/artifacts.d.ts +10 -0
- package/dist/internal/artifacts.js +29 -0
- package/dist/internal/commands.d.ts +18 -0
- package/dist/internal/commands.js +147 -0
- package/dist/internal/documentation-bundle.d.ts +16 -0
- package/dist/internal/documentation-bundle.js +42 -0
- package/dist/internal/engine.d.ts +44 -0
- package/dist/internal/engine.js +399 -0
- package/dist/internal/errors.d.ts +14 -0
- package/dist/internal/errors.js +38 -0
- package/dist/internal/file-names.d.ts +1 -0
- package/dist/internal/file-names.js +7 -0
- package/dist/internal/launch-request.d.ts +33 -0
- package/dist/internal/launch-request.js +110 -0
- package/dist/internal/logs.d.ts +16 -0
- package/dist/internal/logs.js +38 -0
- package/dist/internal/metrics.d.ts +19 -0
- package/dist/internal/metrics.js +282 -0
- package/dist/internal/pi-assets.d.ts +13 -0
- package/dist/internal/pi-assets.js +94 -0
- package/dist/internal/resource-bindings.d.ts +13 -0
- package/dist/internal/resource-bindings.js +34 -0
- package/dist/internal/run-lease.d.ts +32 -0
- package/dist/internal/run-lease.js +166 -0
- package/dist/internal/run-log.d.ts +30 -0
- package/dist/internal/run-log.js +71 -0
- package/dist/internal/run-names.d.ts +1 -0
- package/dist/internal/run-names.js +144 -0
- package/dist/internal/run-resources.d.ts +6 -0
- package/dist/internal/run-resources.js +26 -0
- package/dist/internal/run-state.d.ts +95 -0
- package/dist/internal/run-state.js +323 -0
- package/dist/internal/run-store.d.ts +35 -0
- package/dist/internal/run-store.js +314 -0
- package/dist/internal/run.d.ts +51 -0
- package/dist/internal/run.js +101 -0
- package/dist/internal/state-store.d.ts +22 -0
- package/dist/internal/state-store.js +97 -0
- package/dist/internal/usage.d.ts +5 -0
- package/dist/internal/usage.js +70 -0
- package/dist/internal/workflow-registry.d.ts +35 -0
- package/dist/internal/workflow-registry.js +129 -0
- package/dist/plugin-loader.d.ts +55 -0
- package/dist/plugin-loader.js +353 -0
- package/dist/resources.d.ts +11 -0
- package/dist/resources.js +98 -0
- package/package.json +52 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// src/internal/agents.ts
|
|
2
|
+
import {
|
|
3
|
+
SessionManager,
|
|
4
|
+
createAgentSessionServices,
|
|
5
|
+
createAgentSessionFromServices,
|
|
6
|
+
createEventBus
|
|
7
|
+
} from "@earendil-works/pi-coding-agent";
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
import { mkdir } from "node:fs/promises";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
|
|
14
|
+
// ../core/src/agent-protocol.ts
|
|
15
|
+
var AGENT_RESPONSE_TOOL_NAME = "pi_workflows_agent_response";
|
|
16
|
+
|
|
17
|
+
// src/internal/agents.ts
|
|
18
|
+
import {
|
|
19
|
+
NornAgentResponseToolFactory
|
|
20
|
+
} from "./agent-response-tool.js";
|
|
21
|
+
import { resolveNornAgentDirectory } from "./agent-directory.js";
|
|
22
|
+
import { errorMessage } from "./errors.js";
|
|
23
|
+
import { safeFileName } from "./file-names.js";
|
|
24
|
+
import { NornSessionResourceBindings } from "./resource-bindings.js";
|
|
25
|
+
import { agentUsageFromValue, emptyAgentUsage, totalAgentUsage } from "./usage.js";
|
|
26
|
+
var DEFAULT_AGENT_ATTEMPTS = 3;
|
|
27
|
+
var DEFAULT_AGENT_TOOL_ALLOWLIST = ["read", "bash", "edit", "write", AGENT_RESPONSE_TOOL_NAME];
|
|
28
|
+
var NornAgentRunner = class {
|
|
29
|
+
constructor(input) {
|
|
30
|
+
this.input = input;
|
|
31
|
+
this.responseToolFactory = new NornAgentResponseToolFactory(input.responseCollector);
|
|
32
|
+
}
|
|
33
|
+
input;
|
|
34
|
+
responseToolFactory;
|
|
35
|
+
async createSession(agentInput) {
|
|
36
|
+
const cwd = agentInput.cwd ? this.resolveFromCwd(agentInput.cwd) : this.input.cwd;
|
|
37
|
+
const sessionDir = resolve(this.input.runRoot, "sessions");
|
|
38
|
+
await mkdir(sessionDir, { recursive: true });
|
|
39
|
+
const eventBus = createEventBus();
|
|
40
|
+
const agentDir = this.input.agentDir ?? resolveNornAgentDirectory({ home: homedir(), environment: process.env });
|
|
41
|
+
const services = await createAgentSessionServices({
|
|
42
|
+
cwd,
|
|
43
|
+
agentDir,
|
|
44
|
+
resourceLoaderOptions: {
|
|
45
|
+
eventBus,
|
|
46
|
+
systemPromptOverride: systemPromptOverride(agentInput),
|
|
47
|
+
appendSystemPromptOverride: appendSystemPromptOverride(agentInput)
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const loader = services.resourceLoader;
|
|
51
|
+
const errors = [
|
|
52
|
+
...services.diagnostics.filter((diagnostic) => diagnostic.type === "error").map((diagnostic) => diagnostic.message),
|
|
53
|
+
...loader.getExtensions().errors.map((error) => `Failed to load extension ${error.path}: ${error.error}`)
|
|
54
|
+
];
|
|
55
|
+
if (errors.length > 0) throw new Error(errors.join("\n"));
|
|
56
|
+
const resourceBindings = new NornSessionResourceBindings();
|
|
57
|
+
let session;
|
|
58
|
+
try {
|
|
59
|
+
await resourceBindings.bind({
|
|
60
|
+
adapters: agentInput.resourceAdapters ?? [],
|
|
61
|
+
runId: this.input.id,
|
|
62
|
+
label: agentInput.label,
|
|
63
|
+
reservedTools: [
|
|
64
|
+
"read",
|
|
65
|
+
"bash",
|
|
66
|
+
"edit",
|
|
67
|
+
"write",
|
|
68
|
+
"grep",
|
|
69
|
+
"find",
|
|
70
|
+
"ls",
|
|
71
|
+
"powershell",
|
|
72
|
+
AGENT_RESPONSE_TOOL_NAME,
|
|
73
|
+
...loader.getExtensions().extensions.flatMap((extension) => [...extension.tools.keys()])
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
({ session } = await createAgentSessionFromServices({
|
|
77
|
+
services,
|
|
78
|
+
sessionManager: SessionManager.create(cwd, sessionDir),
|
|
79
|
+
tools: [...withAgentResponseTool(agentInput.tools), ...resourceBindings.tools.map((tool) => tool.name)],
|
|
80
|
+
customTools: [this.responseToolFactory.create(), ...resourceBindings.tools],
|
|
81
|
+
model: agentInput.model ?? this.input.model,
|
|
82
|
+
thinkingLevel: agentInput.thinkingLevel ?? this.input.thinkingLevel
|
|
83
|
+
}));
|
|
84
|
+
await agentInput.beforeSessionStart?.({ events: eventBus });
|
|
85
|
+
await session.bindExtensions({});
|
|
86
|
+
await this.input.logger.record({ type: "agent.spawned", label: agentInput.label, cwd });
|
|
87
|
+
return new CreatedNornAgentSession({
|
|
88
|
+
...this.input,
|
|
89
|
+
label: agentInput.label,
|
|
90
|
+
cwd,
|
|
91
|
+
session,
|
|
92
|
+
resourceBindings,
|
|
93
|
+
events: eventBus
|
|
94
|
+
});
|
|
95
|
+
} catch (error) {
|
|
96
|
+
const errors2 = [error];
|
|
97
|
+
try {
|
|
98
|
+
session?.dispose();
|
|
99
|
+
} catch (cleanupError) {
|
|
100
|
+
errors2.push(cleanupError);
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
await resourceBindings.dispose();
|
|
104
|
+
} catch (cleanupError) {
|
|
105
|
+
errors2.push(cleanupError);
|
|
106
|
+
}
|
|
107
|
+
if (errors2.length > 1) throw new AggregateError(errors2, "Agent creation and resource cleanup failed");
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async prompt(agentInput) {
|
|
112
|
+
const agent = await this.createSession(agentInput);
|
|
113
|
+
try {
|
|
114
|
+
return await agent.prompt(agentInput);
|
|
115
|
+
} finally {
|
|
116
|
+
await agent.dispose();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
resolveFromCwd(path) {
|
|
120
|
+
const resolvedPath = isAbsolute(path) ? path : resolve(this.input.cwd, path);
|
|
121
|
+
const pathFromBoundary = relative(this.input.boundaryRoot, resolvedPath);
|
|
122
|
+
if (pathFromBoundary === ".." || pathFromBoundary.startsWith(`..${sep}`) || isAbsolute(pathFromBoundary)) {
|
|
123
|
+
throw new Error(`Agent cwd escapes ${this.input.boundaryName} isolation: ${path}`);
|
|
124
|
+
}
|
|
125
|
+
return resolvedPath;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
function systemPromptOverride(agentInput) {
|
|
129
|
+
const systemPrompt = agentInput.systemPrompt;
|
|
130
|
+
return systemPrompt === void 0 ? void 0 : () => systemPrompt;
|
|
131
|
+
}
|
|
132
|
+
function appendSystemPromptOverride(agentInput) {
|
|
133
|
+
const appendSystemPrompt = agentInput.appendSystemPrompt;
|
|
134
|
+
return appendSystemPrompt === void 0 ? void 0 : (base) => [...base, ...appendSystemPrompt];
|
|
135
|
+
}
|
|
136
|
+
var CreatedNornAgentSession = class {
|
|
137
|
+
constructor(input) {
|
|
138
|
+
this.input = input;
|
|
139
|
+
this.label = input.label;
|
|
140
|
+
this.cwd = input.cwd;
|
|
141
|
+
this.events = input.events;
|
|
142
|
+
}
|
|
143
|
+
input;
|
|
144
|
+
label;
|
|
145
|
+
cwd;
|
|
146
|
+
events;
|
|
147
|
+
isDisposed = false;
|
|
148
|
+
async prompt(agentInput) {
|
|
149
|
+
return (await this.promptWithResult(agentInput)).response;
|
|
150
|
+
}
|
|
151
|
+
async promptWithResult(agentInput) {
|
|
152
|
+
if (this.isDisposed) throw new Error(`Workflow agent session is disposed: ${this.label}`);
|
|
153
|
+
const maxAttempts = Math.max(1, Math.floor(agentInput.maxAttempts ?? DEFAULT_AGENT_ATTEMPTS));
|
|
154
|
+
const attempts = [];
|
|
155
|
+
const startedAtMs = Date.now();
|
|
156
|
+
let isTerminalEventRecorded = false;
|
|
157
|
+
await this.input.logger.record({ type: "agent.started", label: this.label, cwd: this.cwd, maxAttempts });
|
|
158
|
+
try {
|
|
159
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
160
|
+
await this.input.logger.record({ type: "agent.attempt.started", label: this.label, attempt });
|
|
161
|
+
const raw = await this.runAttempt(agentInput, attempt, attempts.at(-1)?.error);
|
|
162
|
+
const rawAttempt = { attempt, ...raw };
|
|
163
|
+
attempts.push(rawAttempt);
|
|
164
|
+
try {
|
|
165
|
+
if (!raw.responseToolCalled) throw new Error(`Agent did not call ${AGENT_RESPONSE_TOOL_NAME}`);
|
|
166
|
+
const response = agentInput.response.parse(raw.toolResponse);
|
|
167
|
+
const usage = totalAgentUsage(attempts.map((candidate) => candidate.usage));
|
|
168
|
+
const result = {
|
|
169
|
+
label: this.label,
|
|
170
|
+
cwd: this.cwd,
|
|
171
|
+
response,
|
|
172
|
+
usage,
|
|
173
|
+
raw: { ...raw, usage, attempts }
|
|
174
|
+
};
|
|
175
|
+
await this.input.logs.write(`agents/${safeFileName(this.label)}.json`, JSON.stringify(result, null, 2));
|
|
176
|
+
isTerminalEventRecorded = true;
|
|
177
|
+
await this.input.logger.record({ type: "agent.completed", label: this.label, attempts: attempt, durationMs: Date.now() - startedAtMs, usage });
|
|
178
|
+
return result;
|
|
179
|
+
} catch (error) {
|
|
180
|
+
const message = errorMessage(error);
|
|
181
|
+
attempts[attempts.length - 1] = { ...rawAttempt, error: message };
|
|
182
|
+
await this.input.logs.write(
|
|
183
|
+
`agents/${safeFileName(this.label)}.attempt-${attempt}.raw.json`,
|
|
184
|
+
JSON.stringify({ label: this.label, cwd: this.cwd, raw: attempts.at(-1) }, null, 2)
|
|
185
|
+
);
|
|
186
|
+
await this.input.logger.record({ type: "agent.attempt.failed", label: this.label, attempt, error: message });
|
|
187
|
+
if (attempt === maxAttempts) {
|
|
188
|
+
const usage = totalAgentUsage(attempts.map((candidate) => candidate.usage));
|
|
189
|
+
isTerminalEventRecorded = true;
|
|
190
|
+
await this.input.logger.record({ type: "agent.failed", label: this.label, attempts: attempt, durationMs: Date.now() - startedAtMs, usage, error: message });
|
|
191
|
+
throw new Error(
|
|
192
|
+
`Agent ${this.label} did not return a valid structured response after ${attempt} attempt(s). Raw output saved to logs/agents/${safeFileName(this.label)}.attempt-${attempt}.raw.json: ${message}`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
throw new Error(`Agent ${this.label} did not run`);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (!isTerminalEventRecorded) {
|
|
200
|
+
await this.input.logger.record({
|
|
201
|
+
type: "agent.failed",
|
|
202
|
+
label: this.label,
|
|
203
|
+
attempts: attempts.length,
|
|
204
|
+
durationMs: Date.now() - startedAtMs,
|
|
205
|
+
usage: totalAgentUsage(attempts.map((candidate) => candidate.usage)),
|
|
206
|
+
error: errorMessage(error)
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async dispose() {
|
|
213
|
+
if (this.isDisposed) return;
|
|
214
|
+
this.isDisposed = true;
|
|
215
|
+
const errors = [];
|
|
216
|
+
try {
|
|
217
|
+
await this.input.session.abort();
|
|
218
|
+
} catch (error) {
|
|
219
|
+
errors.push(error);
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
this.input.session.dispose();
|
|
223
|
+
} catch (error) {
|
|
224
|
+
errors.push(error);
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
await this.input.resourceBindings.dispose();
|
|
228
|
+
} catch (error) {
|
|
229
|
+
errors.push(error);
|
|
230
|
+
}
|
|
231
|
+
if (errors.length) throw new AggregateError(errors, "Agent disposal failed");
|
|
232
|
+
await this.input.logger.record({ type: "agent.disposed", label: this.label });
|
|
233
|
+
}
|
|
234
|
+
async runAttempt(agentInput, attempt, previousError) {
|
|
235
|
+
const responseRunId = `${this.input.id}:${attempt}:${randomUUID()}`;
|
|
236
|
+
const messages = [];
|
|
237
|
+
let text = "";
|
|
238
|
+
const releaseResponseSlot = this.input.responseCollector.begin(responseRunId, this.label, agentInput.response);
|
|
239
|
+
const unsubscribe = this.input.session.subscribe((event) => {
|
|
240
|
+
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
241
|
+
text += event.assistantMessageEvent.delta;
|
|
242
|
+
}
|
|
243
|
+
if (event.type === "message_end" && event.message) messages.push(event.message);
|
|
244
|
+
});
|
|
245
|
+
const abortNestedSession = () => {
|
|
246
|
+
void this.input.session.abort();
|
|
247
|
+
};
|
|
248
|
+
this.input.signal?.addEventListener("abort", abortNestedSession, { once: true });
|
|
249
|
+
let capturedResponse = { called: false };
|
|
250
|
+
try {
|
|
251
|
+
this.input.signal?.throwIfAborted();
|
|
252
|
+
await this.input.session.prompt(withResponseToolInstruction(agentInput.prompt, agentInput.response, this.label, responseRunId, attempt, previousError), {
|
|
253
|
+
...agentInput.options,
|
|
254
|
+
source: agentInput.options?.source ?? "extension"
|
|
255
|
+
});
|
|
256
|
+
this.input.signal?.throwIfAborted();
|
|
257
|
+
capturedResponse = this.input.responseCollector.get(responseRunId);
|
|
258
|
+
if (!capturedResponse.called) {
|
|
259
|
+
await this.promptForStructuredResponse(agentInput, responseRunId, attempt, previousError);
|
|
260
|
+
this.input.signal?.throwIfAborted();
|
|
261
|
+
capturedResponse = this.input.responseCollector.get(responseRunId);
|
|
262
|
+
}
|
|
263
|
+
} finally {
|
|
264
|
+
this.input.signal?.removeEventListener("abort", abortNestedSession);
|
|
265
|
+
releaseResponseSlot();
|
|
266
|
+
unsubscribe();
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
text: text.trim(),
|
|
270
|
+
messages,
|
|
271
|
+
responseToolCalled: capturedResponse.called,
|
|
272
|
+
usage: usageFromMessages(messages),
|
|
273
|
+
toolResponse: capturedResponse.response,
|
|
274
|
+
sessionFile: this.input.session.sessionFile
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
async promptForStructuredResponse(agentInput, responseRunId, attempt, previousError) {
|
|
278
|
+
const activeToolNames = this.input.session.getActiveToolNames();
|
|
279
|
+
await this.input.logger.record({ type: "agent.response-finalization.started", label: this.label, attempt });
|
|
280
|
+
this.input.session.setActiveToolsByName([AGENT_RESPONSE_TOOL_NAME]);
|
|
281
|
+
try {
|
|
282
|
+
await this.input.session.prompt(withResponseToolFinalizationInstruction(agentInput.response, this.label, responseRunId, attempt, previousError), {
|
|
283
|
+
...agentInput.options,
|
|
284
|
+
source: agentInput.options?.source ?? "extension"
|
|
285
|
+
});
|
|
286
|
+
await this.input.logger.record({ type: "agent.response-finalization.completed", label: this.label, attempt });
|
|
287
|
+
} catch (error) {
|
|
288
|
+
await this.input.logger.record({ type: "agent.response-finalization.failed", label: this.label, attempt, error: errorMessage(error) });
|
|
289
|
+
throw error;
|
|
290
|
+
} finally {
|
|
291
|
+
this.input.session.setActiveToolsByName(activeToolNames);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
function usageFromMessages(messages) {
|
|
296
|
+
return totalAgentUsage(messages.map(usageFromMessage).filter((usage) => usage !== void 0));
|
|
297
|
+
}
|
|
298
|
+
function usageFromMessage(message) {
|
|
299
|
+
if (!message || typeof message !== "object" || !("usage" in message)) return void 0;
|
|
300
|
+
return agentUsageFromValue(message.usage) ?? emptyAgentUsage();
|
|
301
|
+
}
|
|
302
|
+
function withResponseToolInstruction(prompt, responseSchema, label, responseRunId, attempt, previousError) {
|
|
303
|
+
return [
|
|
304
|
+
prompt,
|
|
305
|
+
"",
|
|
306
|
+
"Structured workflow response:",
|
|
307
|
+
`Use the ${AGENT_RESPONSE_TOOL_NAME} tool as your final action to record the workflow response.`,
|
|
308
|
+
"Do not emit the workflow response as assistant text or Markdown.",
|
|
309
|
+
`Pass runId exactly as: ${responseRunId}`,
|
|
310
|
+
`Pass label exactly as: ${label}`,
|
|
311
|
+
"Pass the structured workflow response in the tool's response argument.",
|
|
312
|
+
"The response argument must match this JSON Schema:",
|
|
313
|
+
JSON.stringify(z.toJSONSchema(responseSchema), null, 2),
|
|
314
|
+
...attempt > 1 && previousError ? ["", `Previous structured response attempt failed: ${previousError}`] : []
|
|
315
|
+
].join("\n");
|
|
316
|
+
}
|
|
317
|
+
function withResponseToolFinalizationInstruction(responseSchema, label, responseRunId, attempt, previousError) {
|
|
318
|
+
return [
|
|
319
|
+
"Your previous workflow response turn ended without recording the structured response.",
|
|
320
|
+
`Use the ${AGENT_RESPONSE_TOOL_NAME} tool now. It is the only active tool for this turn.`,
|
|
321
|
+
"Do not answer in assistant text or Markdown.",
|
|
322
|
+
`Pass runId exactly as: ${responseRunId}`,
|
|
323
|
+
`Pass label exactly as: ${label}`,
|
|
324
|
+
"Pass the structured workflow response in the tool's response argument, based on the work you already completed in this session.",
|
|
325
|
+
"The response argument must match this JSON Schema:",
|
|
326
|
+
JSON.stringify(z.toJSONSchema(responseSchema), null, 2),
|
|
327
|
+
...attempt > 1 && previousError ? ["", `Previous structured response attempt failed: ${previousError}`] : []
|
|
328
|
+
].join("\n");
|
|
329
|
+
}
|
|
330
|
+
function withAgentResponseTool(tools) {
|
|
331
|
+
if (!tools) return [...DEFAULT_AGENT_TOOL_ALLOWLIST];
|
|
332
|
+
return Array.from(/* @__PURE__ */ new Set([...tools, AGENT_RESPONSE_TOOL_NAME]));
|
|
333
|
+
}
|
|
334
|
+
export {
|
|
335
|
+
NornAgentRunner
|
|
336
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { NornFileCoordinator } from "@vimhead.dev/norn/files";
|
|
2
|
+
import type { NornArtifactRef } from "@vimhead.dev/norn";
|
|
3
|
+
export declare class NornArtifacts {
|
|
4
|
+
private readonly artifactsRoot;
|
|
5
|
+
private readonly files;
|
|
6
|
+
constructor(artifactsRoot: string, files: NornFileCoordinator);
|
|
7
|
+
write(path: string, content: string): Promise<NornArtifactRef>;
|
|
8
|
+
read(ref: NornArtifactRef): Promise<string>;
|
|
9
|
+
private resolveArtifactPath;
|
|
10
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/internal/artifacts.ts
|
|
2
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
+
var NornArtifacts = class {
|
|
4
|
+
constructor(artifactsRoot, files) {
|
|
5
|
+
this.artifactsRoot = artifactsRoot;
|
|
6
|
+
this.files = files;
|
|
7
|
+
}
|
|
8
|
+
artifactsRoot;
|
|
9
|
+
files;
|
|
10
|
+
async write(path, content) {
|
|
11
|
+
await this.files.writeText(this.resolveArtifactPath(path), content);
|
|
12
|
+
return { path };
|
|
13
|
+
}
|
|
14
|
+
async read(ref) {
|
|
15
|
+
return this.files.readText(this.resolveArtifactPath(ref.path));
|
|
16
|
+
}
|
|
17
|
+
resolveArtifactPath(path) {
|
|
18
|
+
if (isAbsolute(path)) throw new Error(`Artifact path must be relative: ${path}`);
|
|
19
|
+
const resolvedPath = resolve(this.artifactsRoot, path);
|
|
20
|
+
const relativePath = relative(this.artifactsRoot, resolvedPath);
|
|
21
|
+
if (relativePath === "" || relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {
|
|
22
|
+
throw new Error(`Artifact path escapes artifacts directory: ${path}`);
|
|
23
|
+
}
|
|
24
|
+
return resolvedPath;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
export {
|
|
28
|
+
NornArtifacts
|
|
29
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { NornCommandRunInput, NornCommandRunResult } from "@vimhead.dev/norn";
|
|
2
|
+
import type { NornRunLogs } from "./logs.ts";
|
|
3
|
+
import type { NornRunLogger } from "./run-log.ts";
|
|
4
|
+
type NornCommandRunnerInput = {
|
|
5
|
+
readonly boundaryRoot: string;
|
|
6
|
+
readonly boundaryName: string;
|
|
7
|
+
readonly cwd: string;
|
|
8
|
+
readonly signal?: AbortSignal;
|
|
9
|
+
readonly logs: NornRunLogs;
|
|
10
|
+
readonly logger: NornRunLogger;
|
|
11
|
+
};
|
|
12
|
+
export declare class NornCommandRunner {
|
|
13
|
+
private readonly input;
|
|
14
|
+
constructor(input: NornCommandRunnerInput);
|
|
15
|
+
run(commandInput: NornCommandRunInput): Promise<NornCommandRunResult>;
|
|
16
|
+
private resolveFromCwd;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// src/internal/commands.ts
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
5
|
+
import { errorMessage } from "./errors.js";
|
|
6
|
+
import { safeFileName } from "./file-names.js";
|
|
7
|
+
var NornCommandRunner = class {
|
|
8
|
+
constructor(input) {
|
|
9
|
+
this.input = input;
|
|
10
|
+
}
|
|
11
|
+
input;
|
|
12
|
+
async run(commandInput) {
|
|
13
|
+
const cwd = this.resolveFromCwd(commandInput.cwd ?? this.input.cwd);
|
|
14
|
+
const startedAtMs = Date.now();
|
|
15
|
+
const invocationId = randomUUID();
|
|
16
|
+
const stdoutRef = commandLog({ label: commandInput.label, invocationId, stream: "stdout" });
|
|
17
|
+
const stderrRef = commandLog({ label: commandInput.label, invocationId, stream: "stderr" });
|
|
18
|
+
await this.input.logger.record({ type: "command.started", invocationId, label: commandInput.label, command: commandInput.command, cwd, stdoutLogId: stdoutRef.id, stderrLogId: stderrRef.id });
|
|
19
|
+
let result;
|
|
20
|
+
const stdoutLog = await this.input.logs.createWriteStream(stdoutRef);
|
|
21
|
+
const stderrLog = await this.input.logs.createWriteStream(stderrRef);
|
|
22
|
+
try {
|
|
23
|
+
result = await spawnCommand({
|
|
24
|
+
command: commandInput.command,
|
|
25
|
+
cwd,
|
|
26
|
+
env: { ...process.env, ...commandInput.env ?? {} },
|
|
27
|
+
timeoutMs: commandInput.timeoutMs,
|
|
28
|
+
signal: this.input.signal,
|
|
29
|
+
stdoutStream: stdoutLog.stream,
|
|
30
|
+
stderrStream: stderrLog.stream
|
|
31
|
+
});
|
|
32
|
+
} catch (error) {
|
|
33
|
+
await this.input.logger.record({ type: "command.failed", invocationId, label: commandInput.label, durationMs: Date.now() - startedAtMs, error: errorMessage(error) });
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
const commandResult = {
|
|
37
|
+
label: commandInput.label,
|
|
38
|
+
command: commandInput.command,
|
|
39
|
+
cwd,
|
|
40
|
+
exitCode: result.exitCode,
|
|
41
|
+
stdoutTail: result.stdoutTail,
|
|
42
|
+
stderrTail: result.stderrTail,
|
|
43
|
+
killed: result.killed,
|
|
44
|
+
stdoutLog: stdoutLog.log,
|
|
45
|
+
stderrLog: stderrLog.log
|
|
46
|
+
};
|
|
47
|
+
await this.input.logger.record({
|
|
48
|
+
type: "command.completed",
|
|
49
|
+
invocationId,
|
|
50
|
+
label: commandInput.label,
|
|
51
|
+
durationMs: Date.now() - startedAtMs,
|
|
52
|
+
exitCode: result.exitCode,
|
|
53
|
+
killed: result.killed,
|
|
54
|
+
stdoutLogId: commandResult.stdoutLog.id,
|
|
55
|
+
stderrLogId: commandResult.stderrLog.id
|
|
56
|
+
});
|
|
57
|
+
return commandResult;
|
|
58
|
+
}
|
|
59
|
+
resolveFromCwd(path) {
|
|
60
|
+
const resolvedPath = isAbsolute(path) ? path : resolve(this.input.cwd, path);
|
|
61
|
+
const pathFromBoundary = relative(this.input.boundaryRoot, resolvedPath);
|
|
62
|
+
if (pathFromBoundary === ".." || pathFromBoundary.startsWith(`..${sep}`) || isAbsolute(pathFromBoundary)) {
|
|
63
|
+
throw new Error(`Command cwd escapes ${this.input.boundaryName} isolation: ${path}`);
|
|
64
|
+
}
|
|
65
|
+
return resolvedPath;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
async function spawnCommand(input) {
|
|
69
|
+
const command = typeof input.command === "string" ? "bash" : input.command[0];
|
|
70
|
+
const args = typeof input.command === "string" ? ["-c", input.command] : input.command.slice(1);
|
|
71
|
+
let killed = false;
|
|
72
|
+
const stdout = new BoundedTextBuffer();
|
|
73
|
+
const stderr = new BoundedTextBuffer();
|
|
74
|
+
return new Promise((resolvePromise, reject) => {
|
|
75
|
+
const child = spawn(command, args, { cwd: input.cwd, env: input.env, shell: false });
|
|
76
|
+
let timeout;
|
|
77
|
+
let escalation;
|
|
78
|
+
let isSettled = false;
|
|
79
|
+
const cleanup = () => {
|
|
80
|
+
if (timeout) clearTimeout(timeout);
|
|
81
|
+
if (escalation) clearTimeout(escalation);
|
|
82
|
+
input.signal?.removeEventListener("abort", killChild);
|
|
83
|
+
};
|
|
84
|
+
const finish = (result) => {
|
|
85
|
+
if (isSettled) return;
|
|
86
|
+
isSettled = true;
|
|
87
|
+
cleanup();
|
|
88
|
+
void closeStreams(input.stdoutStream, input.stderrStream).then(() => resolvePromise(result), reject);
|
|
89
|
+
};
|
|
90
|
+
const fail = (error) => {
|
|
91
|
+
if (isSettled) return;
|
|
92
|
+
isSettled = true;
|
|
93
|
+
cleanup();
|
|
94
|
+
void closeStreams(input.stdoutStream, input.stderrStream).then(() => reject(error), reject);
|
|
95
|
+
};
|
|
96
|
+
const killChild = () => {
|
|
97
|
+
if (isSettled || killed) return;
|
|
98
|
+
killed = true;
|
|
99
|
+
child.kill("SIGTERM");
|
|
100
|
+
escalation = setTimeout(() => {
|
|
101
|
+
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
102
|
+
}, 5e3);
|
|
103
|
+
escalation.unref?.();
|
|
104
|
+
};
|
|
105
|
+
if (input.timeoutMs !== void 0) timeout = setTimeout(killChild, input.timeoutMs);
|
|
106
|
+
if (input.signal?.aborted) killChild();
|
|
107
|
+
input.signal?.addEventListener("abort", killChild, { once: true });
|
|
108
|
+
child.stdout?.on("data", (chunk) => {
|
|
109
|
+
const text = chunk.toString();
|
|
110
|
+
stdout.append(text);
|
|
111
|
+
input.stdoutStream.write(text);
|
|
112
|
+
});
|
|
113
|
+
child.stderr?.on("data", (chunk) => {
|
|
114
|
+
const text = chunk.toString();
|
|
115
|
+
stderr.append(text);
|
|
116
|
+
input.stderrStream.write(text);
|
|
117
|
+
});
|
|
118
|
+
child.on("error", fail);
|
|
119
|
+
child.on("close", (code) => finish({ exitCode: code, stdoutTail: stdout.value(), stderrTail: stderr.value(), killed }));
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function commandLog(input) {
|
|
123
|
+
return { id: `commands/${safeFileName(input.label)}-${input.invocationId}.${input.stream}` };
|
|
124
|
+
}
|
|
125
|
+
var BoundedTextBuffer = class {
|
|
126
|
+
constructor(maxChars = 128e3) {
|
|
127
|
+
this.maxChars = maxChars;
|
|
128
|
+
}
|
|
129
|
+
maxChars;
|
|
130
|
+
text = "";
|
|
131
|
+
append(value) {
|
|
132
|
+
this.text += value;
|
|
133
|
+
if (this.text.length > this.maxChars) this.text = this.text.slice(this.text.length - this.maxChars);
|
|
134
|
+
}
|
|
135
|
+
value() {
|
|
136
|
+
return this.text;
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
async function closeStreams(...streams) {
|
|
140
|
+
await Promise.all(streams.map((stream) => new Promise((resolvePromise, reject) => {
|
|
141
|
+
stream.on("error", reject);
|
|
142
|
+
stream.end(resolvePromise);
|
|
143
|
+
})));
|
|
144
|
+
}
|
|
145
|
+
export {
|
|
146
|
+
NornCommandRunner
|
|
147
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type NornDocumentationFile = {
|
|
2
|
+
readonly path: string;
|
|
3
|
+
readonly content: string;
|
|
4
|
+
};
|
|
5
|
+
export type NornDocumentationBundle = {
|
|
6
|
+
readonly version: string;
|
|
7
|
+
readonly files: readonly NornDocumentationFile[];
|
|
8
|
+
};
|
|
9
|
+
export declare const DOCUMENTATION_PATHS: {
|
|
10
|
+
readonly readme: "README.md";
|
|
11
|
+
readonly index: "docs/README.md";
|
|
12
|
+
readonly docs: "docs";
|
|
13
|
+
readonly examples: "examples";
|
|
14
|
+
};
|
|
15
|
+
export declare function validateDocumentationBundle(bundle: NornDocumentationBundle): void;
|
|
16
|
+
export declare function hashDocumentationBundle(bundle: NornDocumentationBundle): string;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/internal/documentation-bundle.ts
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
var DOCUMENTATION_PATHS = {
|
|
4
|
+
readme: "README.md",
|
|
5
|
+
index: "docs/README.md",
|
|
6
|
+
docs: "docs",
|
|
7
|
+
examples: "examples"
|
|
8
|
+
};
|
|
9
|
+
function validateDocumentationBundle(bundle) {
|
|
10
|
+
if (typeof bundle.version !== "string" || bundle.version.length === 0) throw new Error("Documentation bundle version must not be empty");
|
|
11
|
+
const paths = /* @__PURE__ */ new Set();
|
|
12
|
+
for (const file of bundle.files) {
|
|
13
|
+
if (typeof file.content !== "string") throw new Error(`Documentation asset must contain text: ${file.path}`);
|
|
14
|
+
if (typeof file.path !== "string" || !/^[a-zA-Z0-9._/-]+$/.test(file.path) || file.path.split("/").some((part) => part === "" || part === "." || part === ".." || part.endsWith(".") || /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(part))) {
|
|
15
|
+
throw new Error(`Invalid documentation asset path: ${file.path}`);
|
|
16
|
+
}
|
|
17
|
+
const portablePath = file.path.toLowerCase();
|
|
18
|
+
if (paths.has(portablePath)) throw new Error(`Duplicate documentation asset path: ${file.path}`);
|
|
19
|
+
paths.add(portablePath);
|
|
20
|
+
}
|
|
21
|
+
for (const path of paths) {
|
|
22
|
+
const parts = path.split("/");
|
|
23
|
+
while (parts.pop() && parts.length > 0) {
|
|
24
|
+
if (paths.has(parts.join("/"))) throw new Error(`Documentation file/directory collision: ${path}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
for (const path of [DOCUMENTATION_PATHS.readme, DOCUMENTATION_PATHS.index]) {
|
|
28
|
+
if (!bundle.files.some((file) => file.path === path)) throw new Error(`Missing documentation asset: ${path}`);
|
|
29
|
+
}
|
|
30
|
+
if (!bundle.files.some((file) => file.path.startsWith("examples/"))) throw new Error("Missing documentation examples");
|
|
31
|
+
}
|
|
32
|
+
function hashDocumentationBundle(bundle) {
|
|
33
|
+
return createHash("sha256").update(JSON.stringify({
|
|
34
|
+
version: bundle.version,
|
|
35
|
+
files: bundle.files.map((file) => ({ path: file.path, content: file.content })).sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0)
|
|
36
|
+
})).digest("hex");
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
DOCUMENTATION_PATHS,
|
|
40
|
+
hashDocumentationBundle,
|
|
41
|
+
validateDocumentationBundle
|
|
42
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type NornAnyWorkflowDeclaration, type NornAnyWorkflowPluginManifest, type NornDispose, type NornRunStartOptions, type NornRunResult, type NornRunCheckpoint, type NornRunInfo, type NornWorkflowPlugin } from "@vimhead.dev/norn";
|
|
2
|
+
import { NornAgentResponseCollector } from "./agent-response-tool.ts";
|
|
3
|
+
import { type NornRunResumeRequest } from "./launch-request.ts";
|
|
4
|
+
import { type NornRegisteredWorkflow } from "./workflow-registry.ts";
|
|
5
|
+
export type NornEngineInput = {
|
|
6
|
+
readonly cwd: string;
|
|
7
|
+
readonly agentDir?: string;
|
|
8
|
+
readonly signal?: AbortSignal;
|
|
9
|
+
readonly responseCollector?: NornAgentResponseCollector;
|
|
10
|
+
readonly gateMode?: "auto" | "pause";
|
|
11
|
+
readonly config?: Record<string, unknown>;
|
|
12
|
+
};
|
|
13
|
+
export declare class NornEngine {
|
|
14
|
+
private readonly input;
|
|
15
|
+
private readonly registry;
|
|
16
|
+
private readonly registrarState;
|
|
17
|
+
private readonly disposersByPlugin;
|
|
18
|
+
private readonly responseCollector;
|
|
19
|
+
private readonly activeRuns;
|
|
20
|
+
constructor(input: NornEngineInput);
|
|
21
|
+
registerPlugin<TManifest extends NornAnyWorkflowPluginManifest>(plugin: NornWorkflowPlugin<TManifest>): NornDispose;
|
|
22
|
+
listWorkflows(): import("@vimhead.dev/norn").NornRegisteredWorkflowInfo[];
|
|
23
|
+
visibleWorkflowEntries(): NornRegisteredWorkflow[];
|
|
24
|
+
private resolvePluginImplementation;
|
|
25
|
+
runWorkflow<TWorkflow extends NornAnyWorkflowDeclaration>(workflow: TWorkflow, params: unknown, options: NornRunStartOptions | undefined): Promise<NornRunResult>;
|
|
26
|
+
listRunCheckpoints(path: string): Promise<NornRunCheckpoint[]>;
|
|
27
|
+
rollbackRun(path: string, checkpointId: string): Promise<NornRunInfo>;
|
|
28
|
+
resumeWorkflow(path: string, params?: unknown): Promise<NornRunResult>;
|
|
29
|
+
resumeRequestedWorkflow(input: {
|
|
30
|
+
readonly runRoot: string;
|
|
31
|
+
readonly request: NornRunResumeRequest;
|
|
32
|
+
}): Promise<NornRunResult>;
|
|
33
|
+
private resumeWorkflowExecution;
|
|
34
|
+
private runScheduler;
|
|
35
|
+
private executeWorkflowStep;
|
|
36
|
+
private nextWorkflowStep;
|
|
37
|
+
private createRunSession;
|
|
38
|
+
private openRunSession;
|
|
39
|
+
private buildRun;
|
|
40
|
+
private startActiveRun;
|
|
41
|
+
private finishActiveRun;
|
|
42
|
+
private failActiveRun;
|
|
43
|
+
private disposePlugin;
|
|
44
|
+
}
|