@interf/compiler 0.5.0 → 0.6.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/README.md +126 -188
- package/builtin-workflows/interf/README.md +22 -10
- package/builtin-workflows/interf/compile/stages/shape/SKILL.md +6 -3
- package/builtin-workflows/interf/compile/stages/structure/SKILL.md +3 -0
- package/builtin-workflows/interf/compile/stages/summarize/SKILL.md +18 -2
- package/builtin-workflows/interf/improve/SKILL.md +2 -2
- package/builtin-workflows/interf/workflow.json +18 -4
- package/builtin-workflows/interf/{compiled.schema.json → workflow.schema.json} +9 -2
- package/dist/commands/check-draft.js +3 -3
- package/dist/commands/compile-controller.js +9 -16
- package/dist/commands/compile.d.ts +19 -1
- package/dist/commands/compile.js +98 -28
- package/dist/commands/create-workflow-wizard.d.ts +20 -2
- package/dist/commands/create-workflow-wizard.js +163 -27
- package/dist/commands/create.d.ts +1 -1
- package/dist/commands/create.js +67 -60
- package/dist/commands/dataset-selection.d.ts +6 -0
- package/dist/commands/dataset-selection.js +11 -0
- package/dist/commands/default.js +3 -3
- package/dist/commands/doctor.js +8 -8
- package/dist/commands/executor-flow.d.ts +1 -1
- package/dist/commands/executor-flow.js +5 -2
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +56 -48
- package/dist/commands/list.js +6 -3
- package/dist/commands/reset.js +1 -1
- package/dist/commands/source-config-wizard.d.ts +2 -2
- package/dist/commands/source-config-wizard.js +50 -17
- package/dist/commands/test-flow.js +5 -16
- package/dist/commands/test.d.ts +0 -6
- package/dist/commands/test.js +9 -17
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/agent-args.d.ts +1 -0
- package/dist/lib/agent-args.js +10 -0
- package/dist/lib/agent-execution.js +2 -1
- package/dist/lib/agent-preflight.js +2 -1
- package/dist/lib/agent-shells.d.ts +26 -1
- package/dist/lib/agent-shells.js +214 -40
- package/dist/lib/agents.d.ts +1 -1
- package/dist/lib/agents.js +1 -1
- package/dist/lib/builtin-compiled-workflow.d.ts +38 -0
- package/dist/lib/builtin-compiled-workflow.js +94 -0
- package/dist/lib/compiled-compile.d.ts +0 -4
- package/dist/lib/compiled-compile.js +11 -30
- package/dist/lib/compiled-paths.d.ts +1 -2
- package/dist/lib/compiled-paths.js +8 -13
- package/dist/lib/compiled-raw.d.ts +2 -2
- package/dist/lib/compiled-reset.d.ts +1 -0
- package/dist/lib/compiled-reset.js +42 -14
- package/dist/lib/compiled-schema.d.ts +11 -7
- package/dist/lib/compiled-schema.js +47 -16
- package/dist/lib/discovery.d.ts +1 -1
- package/dist/lib/discovery.js +2 -2
- package/dist/lib/executors.d.ts +1 -1
- package/dist/lib/executors.js +2 -2
- package/dist/lib/interf-detect.d.ts +0 -1
- package/dist/lib/interf-detect.js +7 -18
- package/dist/lib/interf-scaffold.js +4 -11
- package/dist/lib/interf-workflow-package.d.ts +8 -3
- package/dist/lib/interf-workflow-package.js +128 -62
- package/dist/lib/interf.d.ts +1 -1
- package/dist/lib/interf.js +1 -1
- package/dist/lib/local-workflows.d.ts +4 -3
- package/dist/lib/local-workflows.js +127 -104
- package/dist/lib/project-paths.d.ts +2 -4
- package/dist/lib/project-paths.js +13 -10
- package/dist/lib/runtime-acceptance.js +15 -3
- package/dist/lib/runtime-contracts.js +3 -2
- package/dist/lib/runtime-paths.d.ts +1 -0
- package/dist/lib/runtime-paths.js +4 -1
- package/dist/lib/runtime-prompt.js +4 -4
- package/dist/lib/runtime-reconcile.js +90 -64
- package/dist/lib/runtime-runs.js +29 -102
- package/dist/lib/runtime.d.ts +1 -1
- package/dist/lib/runtime.js +1 -1
- package/dist/lib/schema.d.ts +104 -54
- package/dist/lib/schema.js +32 -116
- package/dist/lib/source-config.js +21 -22
- package/dist/lib/state-health.js +4 -2
- package/dist/lib/state-io.js +2 -110
- package/dist/lib/state-view.js +8 -8
- package/dist/lib/state.d.ts +1 -0
- package/dist/lib/state.js +7 -0
- package/dist/lib/test-execution.js +2 -2
- package/dist/lib/test-paths.js +12 -3
- package/dist/lib/test-sandbox.js +4 -17
- package/dist/lib/test-specs.js +1 -1
- package/dist/lib/validate-compiled.js +13 -8
- package/dist/lib/validate.d.ts +5 -1
- package/dist/lib/validate.js +30 -22
- package/dist/lib/workflow-authoring.d.ts +26 -0
- package/dist/lib/workflow-authoring.js +119 -0
- package/dist/lib/workflow-definitions.d.ts +14 -3
- package/dist/lib/workflow-definitions.js +21 -17
- package/dist/lib/workflow-edit-session.d.ts +16 -0
- package/dist/lib/workflow-edit-session.js +57 -0
- package/dist/lib/workflow-edit-utils.d.ts +10 -0
- package/dist/lib/workflow-edit-utils.js +39 -0
- package/dist/lib/workflow-improvement.js +30 -217
- package/dist/lib/workflow-primitives.d.ts +2 -0
- package/dist/lib/workflow-primitives.js +5 -0
- package/dist/lib/workflow-stage-policy.d.ts +5 -0
- package/dist/lib/workflow-stage-policy.js +31 -0
- package/package.json +7 -8
- package/dist/lib/compiled-layout.d.ts +0 -2
- package/dist/lib/compiled-layout.js +0 -60
- package/dist/lib/obsidian.d.ts +0 -1
- package/dist/lib/obsidian.js +0 -15
- package/dist/lib/summarize-plan.d.ts +0 -17
- package/dist/lib/summarize-plan.js +0 -124
- package/dist/lib/workflow-abi.d.ts +0 -129
- package/dist/lib/workflow-abi.js +0 -156
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { createWorkflowAuthoringShell } from "./agent-shells.js";
|
|
5
|
+
import { WORKFLOW_SCHEMA_FILE } from "./compiled-schema.js";
|
|
6
|
+
import { projectRawSnapshot } from "./compiled-raw.js";
|
|
7
|
+
import { seedLocalWorkflowPackageFromBase } from "./interf-workflow-package.js";
|
|
8
|
+
import { validateWorkflowPackage, workflowDefinitionPath } from "./local-workflows.js";
|
|
9
|
+
import { runWorkflowEditSession } from "./workflow-edit-session.js";
|
|
10
|
+
import { copyDirectory } from "./workflow-edit-utils.js";
|
|
11
|
+
import { createCompiled, compileCompiled } from "./workflows.js";
|
|
12
|
+
function buildWorkflowAuthoringPrompt() {
|
|
13
|
+
return [
|
|
14
|
+
"This is an automated Interf workflow-authoring run, not an open-ended chat session.",
|
|
15
|
+
"Execute it now.",
|
|
16
|
+
"Read `runtime/authoring-context.json` first.",
|
|
17
|
+
`Then read \`workflow/README.md\`, \`workflow/workflow.json\`, and \`workflow/${WORKFLOW_SCHEMA_FILE}\`.`,
|
|
18
|
+
"Review `artifacts/source-dataset/raw/` and any `artifacts/preview-compiled/` notes before editing the package.",
|
|
19
|
+
"Prefer direct file-reading and search tools over shell commands for routine file inspection.",
|
|
20
|
+
"Do not use shell helpers like `cat`, `sed`, `ls`, or `find` when a native read/search tool can inspect the same files.",
|
|
21
|
+
"Edit only files under `workflow/`.",
|
|
22
|
+
"Keep the workflow valid for the current compiler API and workflow schema.",
|
|
23
|
+
"Make the package materially more specific to this dataset task than the seeded base workflow. A no-op copy is not acceptable.",
|
|
24
|
+
"Prefer explicit stage-doc, stage-policy, purpose, and zone-contract edits over vague rewrites.",
|
|
25
|
+
"Do not introduce wikilinks unless the workflow also creates the target note by exact basename or explicit relative path.",
|
|
26
|
+
"Respect stage boundaries: a stage may only introduce links that resolve within that stage's declared writes or already-existing read zones.",
|
|
27
|
+
"Do not make one stage point at files or notes that are only created later by another stage.",
|
|
28
|
+
"Prefer conservative retrieval routing over speculative note sprawl.",
|
|
29
|
+
"Do not narrate plans or ask follow-up questions.",
|
|
30
|
+
"Only emit user-visible updates that begin with STATUS:, DONE:, BLOCKED:, or ERROR:.",
|
|
31
|
+
"As soon as the workflow edits are complete, stop.",
|
|
32
|
+
].join("\n");
|
|
33
|
+
}
|
|
34
|
+
async function prepareWorkflowAuthoringPreview(options) {
|
|
35
|
+
const tempRoot = mkdtempSync(join(tmpdir(), "interf-workflow-preview-"));
|
|
36
|
+
projectRawSnapshot({
|
|
37
|
+
sourcePath: options.datasetPath,
|
|
38
|
+
destinationPath: tempRoot,
|
|
39
|
+
compiledPath: tempRoot,
|
|
40
|
+
mode: "copy",
|
|
41
|
+
prune: true,
|
|
42
|
+
preserveTimestamps: true,
|
|
43
|
+
});
|
|
44
|
+
copyDirectory(options.workflowPath, workflowDefinitionPath(tempRoot, options.workflowId));
|
|
45
|
+
const compiledPath = createCompiled("preview", tempRoot, options.workflowId, options.about, tempRoot);
|
|
46
|
+
const result = await compileCompiled({
|
|
47
|
+
executor: options.executor,
|
|
48
|
+
compiledPath,
|
|
49
|
+
...(options.reporter ? { reporter: options.reporter } : {}),
|
|
50
|
+
preserveStageShells: "on-failure",
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
tempRoot,
|
|
54
|
+
compiledPath,
|
|
55
|
+
compileResult: {
|
|
56
|
+
ok: result.ok,
|
|
57
|
+
failedStage: result.failedStage,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export async function runWorkflowAuthoringDraft(options) {
|
|
62
|
+
const workflowPath = seedLocalWorkflowPackageFromBase({
|
|
63
|
+
sourcePath: options.sourcePath,
|
|
64
|
+
baseWorkflowId: options.baseWorkflowId,
|
|
65
|
+
workflowId: options.workflowId,
|
|
66
|
+
label: options.label,
|
|
67
|
+
hint: options.hint,
|
|
68
|
+
});
|
|
69
|
+
let preview = null;
|
|
70
|
+
try {
|
|
71
|
+
if (options.preparePreview !== false) {
|
|
72
|
+
preview = await prepareWorkflowAuthoringPreview({
|
|
73
|
+
datasetPath: options.datasetPath,
|
|
74
|
+
about: options.taskPrompt,
|
|
75
|
+
workflowId: options.workflowId,
|
|
76
|
+
workflowPath,
|
|
77
|
+
executor: options.executor,
|
|
78
|
+
reporter: options.previewReporter ?? null,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const shell = createWorkflowAuthoringShell({
|
|
82
|
+
workflowPath,
|
|
83
|
+
workflowId: options.workflowId,
|
|
84
|
+
label: options.label,
|
|
85
|
+
baseWorkflowId: options.baseWorkflowId,
|
|
86
|
+
datasetPath: options.datasetPath,
|
|
87
|
+
taskPrompt: options.taskPrompt,
|
|
88
|
+
checks: options.checks ?? [],
|
|
89
|
+
preview,
|
|
90
|
+
});
|
|
91
|
+
if (preview) {
|
|
92
|
+
rmSync(preview.tempRoot, { recursive: true, force: true });
|
|
93
|
+
preview = null;
|
|
94
|
+
}
|
|
95
|
+
const session = await runWorkflowEditSession({
|
|
96
|
+
executor: options.executor,
|
|
97
|
+
workflowPath,
|
|
98
|
+
shell,
|
|
99
|
+
prompt: buildWorkflowAuthoringPrompt(),
|
|
100
|
+
validate: validateWorkflowPackage,
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
status: session.status,
|
|
104
|
+
changed: session.changed,
|
|
105
|
+
summary: session.summary,
|
|
106
|
+
validation: session.validation,
|
|
107
|
+
workflowPath,
|
|
108
|
+
shellPath: shell.rootPath,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
if (preview) {
|
|
113
|
+
rmSync(preview.tempRoot, { recursive: true, force: true });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export function resolveWorkflowDraftPath(sourcePath, workflowId) {
|
|
118
|
+
return workflowDefinitionPath(sourcePath, workflowId);
|
|
119
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RuntimeContractType, RuntimeStageAcceptance, WorkflowCompilerApi, WorkflowId, WorkflowZoneId } from "./schema.js";
|
|
1
|
+
import type { RuntimeContractType, RuntimeStageAcceptance, WorkflowCompilerApi, WorkflowCompiledSchema, WorkflowId, WorkflowZoneId } from "./schema.js";
|
|
2
2
|
export interface WorkflowStarterDoc {
|
|
3
3
|
relativePath: string;
|
|
4
4
|
content: string;
|
|
@@ -16,9 +16,14 @@ export interface WorkflowStageDefinition {
|
|
|
16
16
|
export interface WorkflowDefinition<TId extends string> {
|
|
17
17
|
id: TId;
|
|
18
18
|
compilerApi?: WorkflowCompilerApi;
|
|
19
|
+
purpose?: {
|
|
20
|
+
label: string;
|
|
21
|
+
taskHint: string;
|
|
22
|
+
};
|
|
19
23
|
label: string;
|
|
20
24
|
hint: string;
|
|
21
25
|
recommended?: boolean;
|
|
26
|
+
schema?: WorkflowCompiledSchema;
|
|
22
27
|
stages: WorkflowStageDefinition[];
|
|
23
28
|
stagePolicyNotes?: Record<string, string[]>;
|
|
24
29
|
starterDocs?: WorkflowStarterDoc[];
|
|
@@ -28,6 +33,10 @@ export type CompiledWorkflowId = string;
|
|
|
28
33
|
export declare function standaloneWorkflowDefinitionFromLocalPackage(local: {
|
|
29
34
|
id: string;
|
|
30
35
|
compiler_api?: WorkflowCompilerApi;
|
|
36
|
+
purpose?: {
|
|
37
|
+
label: string;
|
|
38
|
+
task_hint: string;
|
|
39
|
+
};
|
|
31
40
|
label: string;
|
|
32
41
|
hint: string;
|
|
33
42
|
stages?: Array<{
|
|
@@ -41,6 +50,7 @@ export declare function standaloneWorkflowDefinitionFromLocalPackage(local: {
|
|
|
41
50
|
acceptance?: RuntimeStageAcceptance;
|
|
42
51
|
}>;
|
|
43
52
|
stage_policy_notes?: Record<string, string[]>;
|
|
53
|
+
workflow_schema?: WorkflowCompiledSchema;
|
|
44
54
|
starter_docs: WorkflowStarterDoc[];
|
|
45
55
|
}): WorkflowDefinition<string>;
|
|
46
56
|
export declare function listCompiledWorkflowChoices(sourcePath?: string): WorkflowDefinition<string>[];
|
|
@@ -48,8 +58,9 @@ export declare function getCompiledWorkflow(workflowId: CompiledWorkflowId, opti
|
|
|
48
58
|
sourcePath?: string;
|
|
49
59
|
}): WorkflowDefinition<string>;
|
|
50
60
|
export declare function getActiveCompiledWorkflow(compiledPath: string, fallbackWorkflowId: CompiledWorkflowId): WorkflowDefinition<string>;
|
|
51
|
-
export declare function resolveCompiledWorkflowId(value: unknown): CompiledWorkflowId;
|
|
52
|
-
export declare function resolveCompiledWorkflowFromConfig(config: unknown): CompiledWorkflowId;
|
|
61
|
+
export declare function resolveCompiledWorkflowId(value: unknown): CompiledWorkflowId | null;
|
|
62
|
+
export declare function resolveCompiledWorkflowFromConfig(config: unknown): CompiledWorkflowId | null;
|
|
63
|
+
export declare function resolveRequiredCompiledWorkflowFromConfig(config: unknown, label?: string): CompiledWorkflowId;
|
|
53
64
|
export declare function formatCompiledWorkflowStageStep(workflowId: CompiledWorkflowId, stage: string, options?: {
|
|
54
65
|
sourcePath?: string;
|
|
55
66
|
}): string;
|
|
@@ -5,6 +5,7 @@ import { PACKAGE_ROOT } from "./config.js";
|
|
|
5
5
|
import { resolveSourceControlPath } from "./interf.js";
|
|
6
6
|
import { warnInterf } from "./logger.js";
|
|
7
7
|
import { workflowPackagePathForCompiled } from "./compiled-paths.js";
|
|
8
|
+
import { mergeStagePolicyNotesForStages } from "./workflow-stage-policy.js";
|
|
8
9
|
let builtinInterfWorkflowCache = null;
|
|
9
10
|
function toWorkflowStages(stages, fallbackStages) {
|
|
10
11
|
if (!stages || stages.length === 0)
|
|
@@ -20,19 +21,6 @@ function toWorkflowStages(stages, fallbackStages) {
|
|
|
20
21
|
...(stage.acceptance ? { acceptance: stage.acceptance } : {}),
|
|
21
22
|
}));
|
|
22
23
|
}
|
|
23
|
-
function mergeStagePolicyNotes(stages, baseNotes, overrideNotes) {
|
|
24
|
-
const merged = {};
|
|
25
|
-
for (const definition of stages) {
|
|
26
|
-
const combined = Array.from(new Set([
|
|
27
|
-
...(baseNotes?.[definition.id] ?? []),
|
|
28
|
-
...((overrideNotes?.[definition.id] ?? []).filter((note) => note.trim().length > 0)),
|
|
29
|
-
]));
|
|
30
|
-
if (combined.length > 0) {
|
|
31
|
-
merged[definition.id] = combined;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
35
|
-
}
|
|
36
24
|
export function standaloneWorkflowDefinitionFromLocalPackage(local) {
|
|
37
25
|
if (!local.stages || local.stages.length === 0) {
|
|
38
26
|
throw new Error(`Workflow package "${local.id}" is missing explicit stages. Compiled-local and portable workflow packages must be standalone.`);
|
|
@@ -44,11 +32,20 @@ export function standaloneWorkflowDefinitionFromLocalPackage(local) {
|
|
|
44
32
|
kind: "compiled",
|
|
45
33
|
version: 1,
|
|
46
34
|
},
|
|
35
|
+
...(local.purpose
|
|
36
|
+
? {
|
|
37
|
+
purpose: {
|
|
38
|
+
label: local.purpose.label,
|
|
39
|
+
taskHint: local.purpose.task_hint,
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
: {}),
|
|
47
43
|
label: local.label,
|
|
48
44
|
hint: local.hint,
|
|
49
45
|
recommended: false,
|
|
46
|
+
...(local.workflow_schema ? { schema: local.workflow_schema } : {}),
|
|
50
47
|
stages,
|
|
51
|
-
stagePolicyNotes:
|
|
48
|
+
stagePolicyNotes: mergeStagePolicyNotesForStages(stages, undefined, local.stage_policy_notes),
|
|
52
49
|
starterDocs: [...local.starter_docs],
|
|
53
50
|
scope: "local",
|
|
54
51
|
};
|
|
@@ -113,7 +110,7 @@ export function getCompiledWorkflow(workflowId, options = {}) {
|
|
|
113
110
|
return local;
|
|
114
111
|
throw new Error(`No local workflow package found for "${workflowId}" under ${join(options.sourcePath, "interf", "workflows", workflowId)}.`);
|
|
115
112
|
}
|
|
116
|
-
throw new Error(`Workflow "${workflowId}" is not built-in. Provide a source path so Interf
|
|
113
|
+
throw new Error(`Workflow "${workflowId}" is not built-in. Provide a source path so Interf can resolve a local workflow package.`);
|
|
117
114
|
}
|
|
118
115
|
export function getActiveCompiledWorkflow(compiledPath, fallbackWorkflowId) {
|
|
119
116
|
const workflowPath = workflowPackagePathForCompiled(compiledPath);
|
|
@@ -133,14 +130,21 @@ export function resolveCompiledWorkflowId(value) {
|
|
|
133
130
|
if (typeof value === "string" && isWorkflowId(value)) {
|
|
134
131
|
return value;
|
|
135
132
|
}
|
|
136
|
-
return
|
|
133
|
+
return null;
|
|
137
134
|
}
|
|
138
135
|
export function resolveCompiledWorkflowFromConfig(config) {
|
|
139
136
|
if (!config || typeof config !== "object")
|
|
140
|
-
return
|
|
137
|
+
return null;
|
|
141
138
|
const raw = config;
|
|
142
139
|
return resolveCompiledWorkflowId(raw.workflow);
|
|
143
140
|
}
|
|
141
|
+
export function resolveRequiredCompiledWorkflowFromConfig(config, label = "compiled config") {
|
|
142
|
+
const workflowId = resolveCompiledWorkflowFromConfig(config);
|
|
143
|
+
if (!workflowId) {
|
|
144
|
+
throw new Error(`Missing or invalid workflow in ${label}.`);
|
|
145
|
+
}
|
|
146
|
+
return workflowId;
|
|
147
|
+
}
|
|
144
148
|
export function formatCompiledWorkflowStageStep(workflowId, stage, options = {}) {
|
|
145
149
|
return formatWorkflowStageStep(getCompiledWorkflow(workflowId, options), stage);
|
|
146
150
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { WorkflowExecutor } from "./executors.js";
|
|
2
|
+
import type { WorkflowValidationResult } from "./local-workflows.js";
|
|
3
|
+
import { type WorkflowEditShellArtifacts } from "./workflow-edit-utils.js";
|
|
4
|
+
export interface WorkflowEditSessionResult {
|
|
5
|
+
status: "updated" | "no-change" | "invalid" | "executor-failed";
|
|
6
|
+
changed: boolean;
|
|
7
|
+
validation: WorkflowValidationResult | null;
|
|
8
|
+
summary: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function runWorkflowEditSession(options: {
|
|
11
|
+
executor: WorkflowExecutor;
|
|
12
|
+
workflowPath: string;
|
|
13
|
+
shell: WorkflowEditShellArtifacts;
|
|
14
|
+
prompt: string;
|
|
15
|
+
validate: (workflowPath: string) => WorkflowValidationResult;
|
|
16
|
+
}): Promise<WorkflowEditSessionResult>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { copyDirectory, directoriesMatch } from "./workflow-edit-utils.js";
|
|
3
|
+
export async function runWorkflowEditSession(options) {
|
|
4
|
+
copyDirectory(options.workflowPath, options.shell.workflowBeforePath);
|
|
5
|
+
writeFileSync(options.shell.promptLogPath, `${options.prompt}\n`);
|
|
6
|
+
let code = 1;
|
|
7
|
+
let executeError = null;
|
|
8
|
+
try {
|
|
9
|
+
code = await options.executor.execute(options.shell.rootPath, options.prompt, {
|
|
10
|
+
eventLogPath: options.shell.eventLogPath,
|
|
11
|
+
statusLogPath: options.shell.statusLogPath,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
executeError = error instanceof Error ? error.message : String(error);
|
|
16
|
+
}
|
|
17
|
+
copyDirectory(options.workflowPath, options.shell.workflowAfterPath);
|
|
18
|
+
const changed = !directoriesMatch(options.shell.workflowBeforePath, options.shell.workflowAfterPath);
|
|
19
|
+
if (executeError) {
|
|
20
|
+
copyDirectory(options.shell.workflowBeforePath, options.workflowPath);
|
|
21
|
+
return {
|
|
22
|
+
status: "executor-failed",
|
|
23
|
+
changed,
|
|
24
|
+
validation: null,
|
|
25
|
+
summary: `Workflow editor failed before completing: ${executeError}`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (code !== 0) {
|
|
29
|
+
copyDirectory(options.shell.workflowBeforePath, options.workflowPath);
|
|
30
|
+
return {
|
|
31
|
+
status: "executor-failed",
|
|
32
|
+
changed,
|
|
33
|
+
validation: null,
|
|
34
|
+
summary: `Workflow editor exited with code ${code}.`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const validation = options.validate(options.workflowPath);
|
|
38
|
+
if (!validation.ok) {
|
|
39
|
+
copyDirectory(options.shell.workflowBeforePath, options.workflowPath);
|
|
40
|
+
return {
|
|
41
|
+
status: "invalid",
|
|
42
|
+
changed,
|
|
43
|
+
validation,
|
|
44
|
+
summary: changed
|
|
45
|
+
? `Workflow package failed validation: ${validation.summary}`
|
|
46
|
+
: `Workflow package is invalid without any workflow edits: ${validation.summary}`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
status: changed ? "updated" : "no-change",
|
|
51
|
+
changed,
|
|
52
|
+
validation,
|
|
53
|
+
summary: changed
|
|
54
|
+
? "Workflow package updated and validated."
|
|
55
|
+
: "Workflow package is valid without additional edits.",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function copyDirectory(sourcePath: string, targetPath: string): void;
|
|
2
|
+
export declare function directoriesMatch(leftPath: string, rightPath: string): boolean;
|
|
3
|
+
export interface WorkflowEditShellArtifacts {
|
|
4
|
+
rootPath: string;
|
|
5
|
+
workflowBeforePath: string;
|
|
6
|
+
workflowAfterPath: string;
|
|
7
|
+
promptLogPath: string;
|
|
8
|
+
eventLogPath: string;
|
|
9
|
+
statusLogPath: string;
|
|
10
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, } from "node:fs";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
export function copyDirectory(sourcePath, targetPath) {
|
|
4
|
+
rmSync(targetPath, { recursive: true, force: true });
|
|
5
|
+
mkdirSync(targetPath, { recursive: true });
|
|
6
|
+
cpSync(sourcePath, targetPath, { recursive: true });
|
|
7
|
+
}
|
|
8
|
+
function listFileSnapshot(dirPath) {
|
|
9
|
+
const snapshot = new Map();
|
|
10
|
+
if (!existsSync(dirPath))
|
|
11
|
+
return snapshot;
|
|
12
|
+
const collect = (currentPath) => {
|
|
13
|
+
for (const entry of readdirSync(currentPath)) {
|
|
14
|
+
const fullPath = join(currentPath, entry);
|
|
15
|
+
const stat = statSync(fullPath);
|
|
16
|
+
if (stat.isDirectory()) {
|
|
17
|
+
collect(fullPath);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (!stat.isFile())
|
|
21
|
+
continue;
|
|
22
|
+
snapshot.set(relative(dirPath, fullPath), readFileSync(fullPath, "utf8"));
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
collect(dirPath);
|
|
26
|
+
return snapshot;
|
|
27
|
+
}
|
|
28
|
+
export function directoriesMatch(leftPath, rightPath) {
|
|
29
|
+
const left = listFileSnapshot(leftPath);
|
|
30
|
+
const right = listFileSnapshot(rightPath);
|
|
31
|
+
if (left.size !== right.size)
|
|
32
|
+
return false;
|
|
33
|
+
for (const [relativePath, content] of left.entries()) {
|
|
34
|
+
if (right.get(relativePath) !== content) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { appendFileSync,
|
|
1
|
+
import { appendFileSync, existsSync, mkdirSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { join, relative } from "node:path";
|
|
3
3
|
import { createWorkflowImprovementShell, freezeWorkflowImprovementShell, } from "./agent-shells.js";
|
|
4
4
|
import { readInterfConfig } from "./interf.js";
|
|
@@ -7,44 +7,9 @@ import { readJsonFileWithSchema } from "./parse.js";
|
|
|
7
7
|
import { saveCompiledInterfConfig } from "./source-config.js";
|
|
8
8
|
import { WorkflowImprovementRunLedgerSchema, } from "./schema.js";
|
|
9
9
|
import { resolveWorkflowImprovementReviewPaths } from "./workflow-review-paths.js";
|
|
10
|
+
import { runWorkflowEditSession } from "./workflow-edit-session.js";
|
|
10
11
|
import { targetTestRunsRootForCompiled, targetTestSandboxesRootForCompiled, workflowImprovementRunRoot, workflowPackagePathForCompiled, } from "./compiled-paths.js";
|
|
11
|
-
|
|
12
|
-
rmSync(targetPath, { recursive: true, force: true });
|
|
13
|
-
mkdirSync(targetPath, { recursive: true });
|
|
14
|
-
cpSync(sourcePath, targetPath, { recursive: true });
|
|
15
|
-
}
|
|
16
|
-
function listFileSnapshot(dirPath) {
|
|
17
|
-
const snapshot = new Map();
|
|
18
|
-
if (!existsSync(dirPath))
|
|
19
|
-
return snapshot;
|
|
20
|
-
const collect = (currentPath) => {
|
|
21
|
-
for (const entry of readdirSync(currentPath)) {
|
|
22
|
-
const fullPath = join(currentPath, entry);
|
|
23
|
-
const stat = statSync(fullPath);
|
|
24
|
-
if (stat.isDirectory()) {
|
|
25
|
-
collect(fullPath);
|
|
26
|
-
continue;
|
|
27
|
-
}
|
|
28
|
-
if (!stat.isFile())
|
|
29
|
-
continue;
|
|
30
|
-
snapshot.set(relative(dirPath, fullPath), readFileSync(fullPath, "utf8"));
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
collect(dirPath);
|
|
34
|
-
return snapshot;
|
|
35
|
-
}
|
|
36
|
-
function directoriesMatch(leftPath, rightPath) {
|
|
37
|
-
const left = listFileSnapshot(leftPath);
|
|
38
|
-
const right = listFileSnapshot(rightPath);
|
|
39
|
-
if (left.size !== right.size)
|
|
40
|
-
return false;
|
|
41
|
-
for (const [relativePath, content] of left.entries()) {
|
|
42
|
-
if (right.get(relativePath) !== content) {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
12
|
+
import { WORKFLOW_SCHEMA_FILE } from "./compiled-schema.js";
|
|
48
13
|
function toShellArtifactPath(absolutePath, sourceRoot, shellRoot) {
|
|
49
14
|
if (!absolutePath)
|
|
50
15
|
return null;
|
|
@@ -90,16 +55,18 @@ function buildLoopContext(options) {
|
|
|
90
55
|
}
|
|
91
56
|
function buildWorkflowImprovementPrompt() {
|
|
92
57
|
return [
|
|
93
|
-
"This is an automated Interf
|
|
58
|
+
"This is an automated Interf workflow-improvement run, not an open-ended chat session.",
|
|
94
59
|
"The user already invoked this through `interf compile` with self-improving loops enabled. Execute it now.",
|
|
95
60
|
"Read `runtime/loop-context.json` first.",
|
|
96
|
-
|
|
61
|
+
`Then read \`workflow/README.md\`, \`workflow/workflow.json\`, \`workflow/${WORKFLOW_SCHEMA_FILE}\`, and \`workflow/improve/SKILL.md\` if it exists.`,
|
|
97
62
|
"Review preserved evidence from earlier failures under `artifacts/` before you edit the workflow.",
|
|
98
63
|
"Treat `workflow/improve/SKILL.md` as guidance for how to improve the workflow, not as the default file to edit.",
|
|
99
64
|
"Prefer editing the stage docs, workflow contract, or schema ownership that actually change compiled outputs.",
|
|
100
65
|
"Edit only files under `workflow/`.",
|
|
101
66
|
"Do not edit truth checks, test specs, raw dataset files, or generated compiled outputs.",
|
|
102
|
-
"Keep the workflow valid for the current compiler API and
|
|
67
|
+
"Keep the workflow valid for the current compiler API and workflow schema.",
|
|
68
|
+
"Respect stage boundaries: a stage may only introduce links that resolve within that stage's declared writes or already-existing read zones.",
|
|
69
|
+
"Do not make one stage point at files or notes that are only created later by another stage.",
|
|
103
70
|
"Prefer small, defensible changes to workflow docs, stage docs, stage policies, or schema ownership over random churn.",
|
|
104
71
|
"Do not narrate plans or ask follow-up questions.",
|
|
105
72
|
"Only emit user-visible updates that begin with STATUS:, DONE:, BLOCKED:, or ERROR:.",
|
|
@@ -193,170 +160,14 @@ export async function runWorkflowImprovementLoop(options) {
|
|
|
193
160
|
context,
|
|
194
161
|
});
|
|
195
162
|
const workflowRoot = workflowPackagePathForCompiled(options.compiledPath);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
eventLogPath: shell.eventLogPath,
|
|
204
|
-
statusLogPath: shell.statusLogPath,
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
catch (error) {
|
|
208
|
-
executeError = error instanceof Error ? error.message : String(error);
|
|
209
|
-
}
|
|
210
|
-
copyDirectory(workflowRoot, shell.workflowAfterPath);
|
|
163
|
+
const session = await runWorkflowEditSession({
|
|
164
|
+
executor: options.executor,
|
|
165
|
+
workflowPath: workflowRoot,
|
|
166
|
+
shell,
|
|
167
|
+
prompt: buildWorkflowImprovementPrompt(),
|
|
168
|
+
validate: validateWorkflowPackage,
|
|
169
|
+
});
|
|
211
170
|
const preservedShellManifestPath = freezeWorkflowImprovementShell(shell.rootPath);
|
|
212
|
-
const changed = !directoriesMatch(shell.workflowBeforePath, shell.workflowAfterPath);
|
|
213
|
-
if (executeError) {
|
|
214
|
-
copyDirectory(shell.workflowBeforePath, workflowRoot);
|
|
215
|
-
const summary = `Workflow improver failed before completing: ${executeError}`;
|
|
216
|
-
writeWorkflowImprovementRunLedger(options.compiledPath, options.runId, buildWorkflowImprovementLoopRecord({
|
|
217
|
-
targetName: compiledName,
|
|
218
|
-
workflowId: options.workflowId,
|
|
219
|
-
runId: options.runId,
|
|
220
|
-
loopIndex: options.loopIndex,
|
|
221
|
-
shellPath: shell.rootPath,
|
|
222
|
-
loopRootPath: shell.loopRootPath,
|
|
223
|
-
workflowBeforePath: shell.workflowBeforePath,
|
|
224
|
-
workflowAfterPath: shell.workflowAfterPath,
|
|
225
|
-
promptLogPath: shell.promptLogPath,
|
|
226
|
-
eventLogPath: shell.eventLogPath,
|
|
227
|
-
statusLogPath: shell.statusLogPath,
|
|
228
|
-
preservedShellManifestPath,
|
|
229
|
-
changed,
|
|
230
|
-
validation: null,
|
|
231
|
-
result: "executor-failed",
|
|
232
|
-
summary,
|
|
233
|
-
}), {
|
|
234
|
-
targetName: compiledName,
|
|
235
|
-
workflowId: options.workflowId,
|
|
236
|
-
maxLoops: options.maxLoops,
|
|
237
|
-
maxAttempts: options.maxAttempts,
|
|
238
|
-
});
|
|
239
|
-
return {
|
|
240
|
-
status: "executor-failed",
|
|
241
|
-
changed,
|
|
242
|
-
validation: null,
|
|
243
|
-
summary,
|
|
244
|
-
shellPath: shell.rootPath,
|
|
245
|
-
loopRootPath: shell.loopRootPath,
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
if (code !== 0) {
|
|
249
|
-
copyDirectory(shell.workflowBeforePath, workflowRoot);
|
|
250
|
-
const summary = `Workflow improver exited with code ${code}.`;
|
|
251
|
-
writeWorkflowImprovementRunLedger(options.compiledPath, options.runId, buildWorkflowImprovementLoopRecord({
|
|
252
|
-
targetName: compiledName,
|
|
253
|
-
workflowId: options.workflowId,
|
|
254
|
-
runId: options.runId,
|
|
255
|
-
loopIndex: options.loopIndex,
|
|
256
|
-
shellPath: shell.rootPath,
|
|
257
|
-
loopRootPath: shell.loopRootPath,
|
|
258
|
-
workflowBeforePath: shell.workflowBeforePath,
|
|
259
|
-
workflowAfterPath: shell.workflowAfterPath,
|
|
260
|
-
promptLogPath: shell.promptLogPath,
|
|
261
|
-
eventLogPath: shell.eventLogPath,
|
|
262
|
-
statusLogPath: shell.statusLogPath,
|
|
263
|
-
preservedShellManifestPath,
|
|
264
|
-
changed,
|
|
265
|
-
validation: null,
|
|
266
|
-
result: "executor-failed",
|
|
267
|
-
summary,
|
|
268
|
-
}), {
|
|
269
|
-
targetName: compiledName,
|
|
270
|
-
workflowId: options.workflowId,
|
|
271
|
-
maxLoops: options.maxLoops,
|
|
272
|
-
maxAttempts: options.maxAttempts,
|
|
273
|
-
});
|
|
274
|
-
return {
|
|
275
|
-
status: "executor-failed",
|
|
276
|
-
changed,
|
|
277
|
-
validation: null,
|
|
278
|
-
summary,
|
|
279
|
-
shellPath: shell.rootPath,
|
|
280
|
-
loopRootPath: shell.loopRootPath,
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
if (!changed) {
|
|
284
|
-
const validation = validateWorkflowPackage(workflowRoot);
|
|
285
|
-
const result = validation.ok ? "no-change" : "invalid";
|
|
286
|
-
if (!validation.ok) {
|
|
287
|
-
copyDirectory(shell.workflowBeforePath, workflowRoot);
|
|
288
|
-
}
|
|
289
|
-
const summary = validation.ok
|
|
290
|
-
? "Workflow improver made no workflow edits."
|
|
291
|
-
: `Workflow variation is invalid without any workflow edits: ${validation.summary}`;
|
|
292
|
-
writeWorkflowImprovementRunLedger(options.compiledPath, options.runId, buildWorkflowImprovementLoopRecord({
|
|
293
|
-
targetName: compiledName,
|
|
294
|
-
workflowId: options.workflowId,
|
|
295
|
-
runId: options.runId,
|
|
296
|
-
loopIndex: options.loopIndex,
|
|
297
|
-
shellPath: shell.rootPath,
|
|
298
|
-
loopRootPath: shell.loopRootPath,
|
|
299
|
-
workflowBeforePath: shell.workflowBeforePath,
|
|
300
|
-
workflowAfterPath: shell.workflowAfterPath,
|
|
301
|
-
promptLogPath: shell.promptLogPath,
|
|
302
|
-
eventLogPath: shell.eventLogPath,
|
|
303
|
-
statusLogPath: shell.statusLogPath,
|
|
304
|
-
preservedShellManifestPath,
|
|
305
|
-
changed: false,
|
|
306
|
-
validation,
|
|
307
|
-
result,
|
|
308
|
-
summary,
|
|
309
|
-
}), {
|
|
310
|
-
targetName: compiledName,
|
|
311
|
-
workflowId: options.workflowId,
|
|
312
|
-
maxLoops: options.maxLoops,
|
|
313
|
-
maxAttempts: options.maxAttempts,
|
|
314
|
-
});
|
|
315
|
-
return {
|
|
316
|
-
status: result,
|
|
317
|
-
changed: false,
|
|
318
|
-
validation,
|
|
319
|
-
summary,
|
|
320
|
-
shellPath: shell.rootPath,
|
|
321
|
-
loopRootPath: shell.loopRootPath,
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
const validation = validateWorkflowPackage(workflowRoot);
|
|
325
|
-
if (!validation.ok) {
|
|
326
|
-
copyDirectory(shell.workflowBeforePath, workflowRoot);
|
|
327
|
-
const summary = `Workflow variation failed validation: ${validation.summary}`;
|
|
328
|
-
writeWorkflowImprovementRunLedger(options.compiledPath, options.runId, buildWorkflowImprovementLoopRecord({
|
|
329
|
-
targetName: compiledName,
|
|
330
|
-
workflowId: options.workflowId,
|
|
331
|
-
runId: options.runId,
|
|
332
|
-
loopIndex: options.loopIndex,
|
|
333
|
-
shellPath: shell.rootPath,
|
|
334
|
-
loopRootPath: shell.loopRootPath,
|
|
335
|
-
workflowBeforePath: shell.workflowBeforePath,
|
|
336
|
-
workflowAfterPath: shell.workflowAfterPath,
|
|
337
|
-
promptLogPath: shell.promptLogPath,
|
|
338
|
-
eventLogPath: shell.eventLogPath,
|
|
339
|
-
statusLogPath: shell.statusLogPath,
|
|
340
|
-
preservedShellManifestPath,
|
|
341
|
-
changed: true,
|
|
342
|
-
validation,
|
|
343
|
-
result: "invalid",
|
|
344
|
-
summary,
|
|
345
|
-
}), {
|
|
346
|
-
targetName: compiledName,
|
|
347
|
-
workflowId: options.workflowId,
|
|
348
|
-
maxLoops: options.maxLoops,
|
|
349
|
-
maxAttempts: options.maxAttempts,
|
|
350
|
-
});
|
|
351
|
-
return {
|
|
352
|
-
status: "invalid",
|
|
353
|
-
changed: true,
|
|
354
|
-
validation,
|
|
355
|
-
summary,
|
|
356
|
-
shellPath: shell.rootPath,
|
|
357
|
-
loopRootPath: shell.loopRootPath,
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
171
|
writeWorkflowImprovementRunLedger(options.compiledPath, options.runId, buildWorkflowImprovementLoopRecord({
|
|
361
172
|
targetName: compiledName,
|
|
362
173
|
workflowId: options.workflowId,
|
|
@@ -370,26 +181,28 @@ export async function runWorkflowImprovementLoop(options) {
|
|
|
370
181
|
eventLogPath: shell.eventLogPath,
|
|
371
182
|
statusLogPath: shell.statusLogPath,
|
|
372
183
|
preservedShellManifestPath,
|
|
373
|
-
changed:
|
|
374
|
-
validation,
|
|
375
|
-
result:
|
|
376
|
-
summary:
|
|
184
|
+
changed: session.changed,
|
|
185
|
+
validation: session.validation,
|
|
186
|
+
result: session.status,
|
|
187
|
+
summary: session.summary,
|
|
377
188
|
}), {
|
|
378
189
|
targetName: compiledName,
|
|
379
190
|
workflowId: options.workflowId,
|
|
380
191
|
maxLoops: options.maxLoops,
|
|
381
192
|
maxAttempts: options.maxAttempts,
|
|
382
193
|
});
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
194
|
+
if (session.status === "updated") {
|
|
195
|
+
updateCompiledWorkflowOrigin({
|
|
196
|
+
compiledPath: options.compiledPath,
|
|
197
|
+
selectedWorkflowId: options.workflowId,
|
|
198
|
+
localDraft: true,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
388
201
|
return {
|
|
389
|
-
status:
|
|
390
|
-
changed:
|
|
391
|
-
validation,
|
|
392
|
-
summary:
|
|
202
|
+
status: session.status,
|
|
203
|
+
changed: session.changed,
|
|
204
|
+
validation: session.validation,
|
|
205
|
+
summary: session.summary,
|
|
393
206
|
shellPath: shell.rootPath,
|
|
394
207
|
loopRootPath: shell.loopRootPath,
|
|
395
208
|
};
|