supipowers 1.5.3 → 2.0.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 +14 -8
- package/bin/install.mjs +20 -5
- package/bin/install.ts +95 -0
- package/package.json +8 -4
- package/skills/context-mode/SKILL.md +17 -10
- package/skills/harness/SKILL.md +94 -0
- package/skills/ui-design/SKILL.md +63 -0
- package/skills/ui-design/sub-agent-templates/component-builder.md +29 -0
- package/skills/ui-design/sub-agent-templates/design-critic.md +46 -0
- package/skills/ui-design/sub-agent-templates/pencil/component-builder.md +29 -0
- package/skills/ui-design/sub-agent-templates/pencil/design-critic.md +42 -0
- package/skills/ui-design/sub-agent-templates/pencil/section-assembler.md +27 -0
- package/skills/ui-design/sub-agent-templates/section-assembler.md +27 -0
- package/skills/ultraplan-discover/SKILL.md +96 -0
- package/skills/ultraplan-intake/SKILL.md +89 -0
- package/skills/ultraplan-research/SKILL.md +129 -0
- package/skills/ultraplan-review/SKILL.md +86 -0
- package/skills/ultraplan-review-scope/SKILL.md +111 -0
- package/skills/ultraplan-review-structure/SKILL.md +120 -0
- package/skills/ultraplan-review-tdd/SKILL.md +142 -0
- package/skills/ultraplan-scout/SKILL.md +110 -0
- package/skills/ultraplan-synthesize/SKILL.md +124 -0
- package/src/{quality/ai-session.ts → ai/final-message.ts} +27 -0
- package/src/ai/schema-text.ts +129 -0
- package/src/ai/structured-output.ts +274 -0
- package/src/ai/template.ts +27 -0
- package/src/bootstrap.ts +63 -28
- package/src/commands/agents.ts +131 -42
- package/src/commands/ai-review.ts +251 -30
- package/src/commands/clear.ts +434 -0
- package/src/commands/commit.ts +1 -0
- package/src/commands/config.ts +242 -44
- package/src/commands/context.ts +55 -28
- package/src/commands/doctor.ts +234 -6
- package/src/commands/fix-pr.ts +306 -131
- package/src/commands/generate.ts +111 -21
- package/src/commands/memory.ts +192 -0
- package/src/commands/model-picker.ts +28 -21
- package/src/commands/model.ts +18 -8
- package/src/commands/optimize-context.ts +408 -29
- package/src/commands/plan.ts +2 -0
- package/src/commands/qa.ts +312 -137
- package/src/commands/release.ts +259 -76
- package/src/commands/review.ts +293 -59
- package/src/commands/status.ts +200 -13
- package/src/commands/supi.ts +3 -35
- package/src/commands/ui-design.ts +394 -0
- package/src/commands/ultraplan.ts +1518 -0
- package/src/commands/update.ts +86 -0
- package/src/config/defaults.ts +62 -0
- package/src/config/loader.ts +448 -60
- package/src/config/schema.ts +108 -2
- package/src/context/optimizer.ts +25 -33
- package/src/context/rule-renderer.ts +223 -0
- package/src/context/savings.ts +258 -0
- package/src/context/startup-check.ts +380 -0
- package/src/context/startup-optimizer.ts +355 -0
- package/src/context/tokenignore.ts +146 -0
- package/src/context-mode/cache-handle.ts +49 -0
- package/src/context-mode/cache-preview.ts +71 -0
- package/src/context-mode/cache-store.ts +738 -0
- package/src/context-mode/compressor.ts +131 -26
- package/src/context-mode/dedup.ts +108 -0
- package/src/context-mode/detector.ts +35 -4
- package/src/context-mode/event-extractor.ts +14 -12
- package/src/context-mode/event-store.ts +91 -36
- package/src/context-mode/hooks.ts +798 -56
- package/src/context-mode/knowledge/store.ts +255 -11
- package/src/context-mode/memory-store.ts +325 -0
- package/src/context-mode/metrics-recorder.ts +158 -0
- package/src/context-mode/metrics-store.ts +765 -0
- package/src/context-mode/model.ts +24 -0
- package/src/context-mode/processor-keys.ts +29 -0
- package/src/context-mode/processors/build.ts +66 -0
- package/src/context-mode/processors/docker.ts +57 -0
- package/src/context-mode/processors/git.ts +111 -0
- package/src/context-mode/processors/json.ts +112 -0
- package/src/context-mode/processors/k8s.ts +67 -0
- package/src/context-mode/processors/lint.ts +67 -0
- package/src/context-mode/processors/log.ts +86 -0
- package/src/context-mode/processors/registry.ts +116 -0
- package/src/context-mode/processors/test-runner.ts +102 -0
- package/src/context-mode/processors/types.ts +20 -0
- package/src/context-mode/repomap.ts +400 -0
- package/src/context-mode/routing.ts +97 -24
- package/src/context-mode/sandbox/runners.ts +5 -1
- package/src/context-mode/snapshot-builder.ts +106 -11
- package/src/context-mode/source-hash.ts +173 -0
- package/src/context-mode/tool-name.ts +11 -0
- package/src/context-mode/tools.ts +654 -22
- package/src/context-mode/web/fetcher.ts +31 -12
- package/src/debug/logger.ts +2 -1
- package/src/deps/registry.ts +1 -1
- package/src/discipline/failure-summarizer.ts +170 -0
- package/src/discipline/failure-taxonomy.ts +131 -0
- package/src/discipline/workflow-invariants.ts +125 -0
- package/src/discovery/index.ts +31 -0
- package/src/discovery/lsp.ts +87 -0
- package/src/discovery/rank.ts +144 -0
- package/src/discovery/sources.ts +89 -0
- package/src/discovery/workflow.ts +87 -0
- package/src/docs/contracts.ts +39 -0
- package/src/docs/drift.ts +117 -87
- package/src/fix-pr/assessment.ts +200 -0
- package/src/fix-pr/contracts.ts +47 -0
- package/src/fix-pr/fetch-comments.ts +80 -0
- package/src/fix-pr/prompt-builder.ts +58 -40
- package/src/fix-pr/scripts/exec.ts +34 -0
- package/src/fix-pr/scripts/trigger-review.ts +106 -0
- package/src/fix-pr/scripts/wait-and-check.ts +108 -0
- package/src/fix-pr/types.ts +4 -0
- package/src/git/branch-finish.ts +5 -0
- package/src/git/commit-contract.ts +83 -0
- package/src/git/commit.ts +121 -184
- package/src/git/status.ts +62 -8
- package/src/harness/anti_slop/architecture-parser.ts +210 -0
- package/src/harness/anti_slop/backend-factory.ts +30 -0
- package/src/harness/anti_slop/backend.ts +140 -0
- package/src/harness/anti_slop/desloppify-adapter.ts +319 -0
- package/src/harness/anti_slop/fallow-adapter.ts +305 -0
- package/src/harness/anti_slop/installer.ts +227 -0
- package/src/harness/anti_slop/queue.ts +216 -0
- package/src/harness/anti_slop/recommend.ts +84 -0
- package/src/harness/anti_slop/score.ts +180 -0
- package/src/harness/anti_slop/synthetic-edit-test.ts +128 -0
- package/src/harness/artifacts/agents-md.ts +88 -0
- package/src/harness/artifacts/checks-wiring.ts +57 -0
- package/src/harness/artifacts/docs-tree.ts +79 -0
- package/src/harness/artifacts/lint-configs.ts +136 -0
- package/src/harness/artifacts/review-agents.ts +67 -0
- package/src/harness/bare-entry.ts +108 -0
- package/src/harness/command.ts +1010 -0
- package/src/harness/default-agents/design.md +23 -0
- package/src/harness/default-agents/discover.md +18 -0
- package/src/harness/default-agents/implement.md +24 -0
- package/src/harness/default-agents/plan.md +19 -0
- package/src/harness/default-agents/research.md +21 -0
- package/src/harness/default-agents/validate.md +22 -0
- package/src/harness/gc/reporter.ts +28 -0
- package/src/harness/gc/runner.ts +136 -0
- package/src/harness/hooks/layer-context-inject.ts +155 -0
- package/src/harness/hooks/post-session-sweep.ts +130 -0
- package/src/harness/hooks/pre-edit-dupe-probe.ts +224 -0
- package/src/harness/hooks/register.ts +118 -0
- package/src/harness/model.ts +117 -0
- package/src/harness/pipeline.ts +348 -0
- package/src/harness/project-paths.ts +235 -0
- package/src/harness/stage-runner.ts +107 -0
- package/src/harness/stages/design.ts +386 -0
- package/src/harness/stages/discover.ts +454 -0
- package/src/harness/stages/implement.ts +162 -0
- package/src/harness/stages/plan.ts +335 -0
- package/src/harness/stages/research.ts +263 -0
- package/src/harness/stages/validate.ts +684 -0
- package/src/harness/storage.ts +467 -0
- package/src/harness/tools.ts +426 -0
- package/src/lsp/bridge.ts +56 -95
- package/src/lsp/capabilities.ts +108 -0
- package/src/lsp/contracts.ts +35 -0
- package/src/lsp/detector.ts +8 -12
- package/src/markdown-frontmatter.ts +68 -0
- package/src/mempalace/bridge.ts +129 -0
- package/src/mempalace/config.ts +75 -0
- package/src/mempalace/format.ts +163 -0
- package/src/mempalace/hooks.ts +370 -0
- package/src/mempalace/installer-helper.ts +194 -0
- package/src/mempalace/python/mempalace_bridge.py +440 -0
- package/src/mempalace/runtime.ts +565 -0
- package/src/mempalace/schema.ts +264 -0
- package/src/mempalace/session-summary.ts +198 -0
- package/src/mempalace/tool.ts +186 -0
- package/src/mempalace/uv.ts +256 -0
- package/src/migrate/runner.ts +354 -0
- package/src/planning/approval-flow.ts +206 -9
- package/src/planning/plan-writer-prompt.ts +4 -3
- package/src/planning/planning-ask-tool.ts +39 -0
- package/src/planning/render-markdown.ts +74 -0
- package/src/planning/spec.ts +42 -0
- package/src/planning/system-prompt.ts +11 -8
- package/src/planning/validate.ts +84 -0
- package/src/platform/omp.ts +15 -2
- package/src/platform/system-prompt.ts +37 -0
- package/src/platform/test-utils.ts +3 -0
- package/src/platform/types.ts +6 -1
- package/src/qa/config.ts +12 -6
- package/src/qa/detect-app-type.ts +13 -6
- package/src/qa/matrix.ts +12 -6
- package/src/qa/prompt-builder.ts +28 -30
- package/src/qa/scripts/dev-server-utils.ts +72 -0
- package/src/qa/scripts/run-e2e-tests.ts +226 -0
- package/src/qa/scripts/start-dev-server.ts +138 -0
- package/src/qa/scripts/stop-dev-server.ts +77 -0
- package/src/qa/session.ts +13 -7
- package/src/quality/ai-setup.ts +27 -25
- package/src/quality/contracts.ts +34 -0
- package/src/quality/gates/ai-review.ts +20 -58
- package/src/quality/gates/command.ts +249 -46
- package/src/quality/review-gates.ts +18 -2
- package/src/quality/runner.ts +63 -22
- package/src/quality/schemas.ts +37 -2
- package/src/quality/setup.ts +96 -16
- package/src/release/changelog.ts +1 -1
- package/src/release/channels/custom.ts +13 -3
- package/src/release/channels/types.ts +5 -0
- package/src/release/contracts.ts +90 -0
- package/src/release/executor.ts +122 -45
- package/src/release/prompt.ts +18 -2
- package/src/release/targets.ts +86 -0
- package/src/release/version.ts +96 -71
- package/src/review/agent-loader.ts +221 -109
- package/src/review/fixer.ts +10 -6
- package/src/review/multi-agent-runner.ts +114 -13
- package/src/review/output.ts +12 -139
- package/src/review/runner.ts +12 -6
- package/src/review/scope.ts +144 -24
- package/src/review/types.ts +1 -20
- package/src/review/validator.ts +12 -6
- package/src/storage/fix-pr-sessions.ts +21 -14
- package/src/storage/plans.ts +14 -5
- package/src/storage/qa-sessions.ts +25 -19
- package/src/storage/reliability-metrics.ts +180 -0
- package/src/storage/reports.ts +8 -7
- package/src/storage/review-sessions.ts +55 -20
- package/src/tool-catalog/active-tool-controller.ts +164 -0
- package/src/tool-catalog/active-tool-planner.ts +212 -0
- package/src/tool-catalog/tool-groups.ts +102 -0
- package/src/types.ts +1399 -5
- package/src/ui-design/backend-adapter.ts +78 -0
- package/src/ui-design/backends/local-html.ts +82 -0
- package/src/ui-design/backends/pencil-mcp.ts +111 -0
- package/src/ui-design/components-scanner.ts +124 -0
- package/src/ui-design/config.ts +55 -0
- package/src/ui-design/pen-scanner.ts +95 -0
- package/src/ui-design/pen-selector.ts +72 -0
- package/src/ui-design/prompt-builder.ts +73 -0
- package/src/ui-design/scanner.ts +136 -0
- package/src/ui-design/session.ts +974 -0
- package/src/ui-design/system-prompt.ts +312 -0
- package/src/ui-design/tokens-scanner.ts +181 -0
- package/src/ui-design/types.ts +96 -0
- package/src/ultraplan/agent-catalog.ts +522 -0
- package/src/ultraplan/authoring/agent-catalog.ts +310 -0
- package/src/ultraplan/authoring/authoring-tools.ts +552 -0
- package/src/ultraplan/authoring/command-handlers.ts +339 -0
- package/src/ultraplan/authoring/markdown.ts +510 -0
- package/src/ultraplan/authoring/model.ts +162 -0
- package/src/ultraplan/authoring/pipeline.ts +319 -0
- package/src/ultraplan/authoring/stage-runner.ts +141 -0
- package/src/ultraplan/authoring/stages/approve.ts +249 -0
- package/src/ultraplan/authoring/stages/discover.ts +289 -0
- package/src/ultraplan/authoring/stages/intake.ts +203 -0
- package/src/ultraplan/authoring/stages/research.ts +399 -0
- package/src/ultraplan/authoring/stages/review.ts +333 -0
- package/src/ultraplan/authoring/stages/scout.ts +188 -0
- package/src/ultraplan/authoring/stages/synthesize.ts +348 -0
- package/src/ultraplan/authoring/storage.ts +594 -0
- package/src/ultraplan/authoring/synth-gate.ts +165 -0
- package/src/ultraplan/authoring-draft.ts +653 -0
- package/src/ultraplan/authoring-persist.ts +180 -0
- package/src/ultraplan/authoring-tool.ts +608 -0
- package/src/ultraplan/authoring-wizard.ts +587 -0
- package/src/ultraplan/batch/merge.ts +98 -0
- package/src/ultraplan/batch/planner.ts +150 -0
- package/src/ultraplan/batch/presenter.ts +97 -0
- package/src/ultraplan/batch/storage.ts +420 -0
- package/src/ultraplan/batch/supervisor.ts +317 -0
- package/src/ultraplan/batch/worker.ts +26 -0
- package/src/ultraplan/batch/worktree.ts +110 -0
- package/src/ultraplan/contracts.ts +1593 -0
- package/src/ultraplan/default-agents/authoring/discoverer.md +12 -0
- package/src/ultraplan/default-agents/authoring/intake.md +12 -0
- package/src/ultraplan/default-agents/authoring/planner.md +12 -0
- package/src/ultraplan/default-agents/authoring/researcher.md +12 -0
- package/src/ultraplan/default-agents/authoring/scope-checker.md +12 -0
- package/src/ultraplan/default-agents/authoring/scout.md +12 -0
- package/src/ultraplan/default-agents/authoring/structure-checker.md +12 -0
- package/src/ultraplan/default-agents/authoring/tdd-checker.md +12 -0
- package/src/ultraplan/default-agents/backend-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/backend-executor.md +10 -0
- package/src/ultraplan/default-agents/backend-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/backend-tester.md +10 -0
- package/src/ultraplan/default-agents/frontend-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/frontend-executor.md +10 -0
- package/src/ultraplan/default-agents/frontend-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/frontend-tester.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-domain-reviewer.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-executor.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-stack-reviewer.md +10 -0
- package/src/ultraplan/default-agents/infrastructure-tester.md +10 -0
- package/src/ultraplan/execution/contract.ts +71 -0
- package/src/ultraplan/execution/policy.ts +217 -0
- package/src/ultraplan/execution/runtime-tools.ts +107 -0
- package/src/ultraplan/execution/session-runner.ts +281 -0
- package/src/ultraplan/next-router.ts +85 -0
- package/src/ultraplan/presenter.ts +359 -0
- package/src/ultraplan/project-paths.ts +342 -0
- package/src/ultraplan/runtime/active-execution.ts +72 -0
- package/src/ultraplan/runtime/apply-mutation.ts +416 -0
- package/src/ultraplan/runtime/blockers.ts +243 -0
- package/src/ultraplan/runtime/hook-bridge.ts +486 -0
- package/src/ultraplan/runtime/launch-context.ts +207 -0
- package/src/ultraplan/runtime/migration.ts +524 -0
- package/src/ultraplan/runtime/normalize.ts +281 -0
- package/src/ultraplan/runtime/proof.ts +260 -0
- package/src/ultraplan/runtime/reducer.ts +416 -0
- package/src/ultraplan/runtime/repair.ts +251 -0
- package/src/ultraplan/runtime/tracker-storage.ts +368 -0
- package/src/ultraplan/session-selection.ts +291 -0
- package/src/ultraplan/storage.ts +374 -0
- package/src/utils/editor.ts +38 -0
- package/src/utils/executable.ts +80 -0
- package/src/utils/paths.ts +1 -20
- package/src/utils/shell.ts +31 -0
- package/src/visual/companion.ts +2 -1
- package/src/visual/scripts/frame-template.html +60 -0
- package/src/visual/scripts/index.js +59 -13
- package/src/visual/scripts/package.json +3 -0
- package/src/visual/start-server.ts +2 -1
- package/src/workspace/git-scope.ts +64 -0
- package/src/workspace/locks.ts +23 -0
- package/src/workspace/package-manager.ts +117 -0
- package/src/workspace/path-mapping.ts +75 -0
- package/src/workspace/project-slug.ts +92 -0
- package/src/workspace/repo-root.ts +137 -0
- package/src/workspace/selector.ts +115 -0
- package/src/workspace/state-paths.ts +118 -0
- package/src/workspace/targets.ts +313 -0
- package/src/fix-pr/scripts/diff-comments.sh +0 -33
- package/src/fix-pr/scripts/fetch-pr-comments.sh +0 -25
- package/src/fix-pr/scripts/trigger-review.sh +0 -36
- package/src/fix-pr/scripts/wait-and-check.sh +0 -37
- package/src/qa/scripts/detect-app-type.sh +0 -68
- package/src/qa/scripts/discover-routes.sh +0 -143
- package/src/qa/scripts/run-e2e-tests.sh +0 -131
- package/src/qa/scripts/start-dev-server.sh +0 -46
- package/src/qa/scripts/stop-dev-server.sh +0 -36
- package/src/review/prompts/fix-output-schema.md +0 -18
- package/src/review/prompts/review-output-schema.md +0 -38
- package/src/review/template.ts +0 -15
- /package/src/{review → ai}/prompts/invalid-output-retry.md +0 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Round-trip-stable markdown <-> authored.json serializer for the synth-stage `$EDITOR`
|
|
3
|
+
* gate.
|
|
4
|
+
*
|
|
5
|
+
* Design: the markdown surfaces only the **editable** fields (frontmatter + scenario titles,
|
|
6
|
+
* steps, dependencies, level, status). Non-editable fields (agent slot bindings, proofs,
|
|
7
|
+
* progress summaries, review gates) are preserved from the original draft JSON via the
|
|
8
|
+
* `applyAuthoredPatch` helper. This lets the user re-shape the plan without having to
|
|
9
|
+
* understand the full schema, and keeps the planner's slot/proof choices intact unless the
|
|
10
|
+
* user explicitly re-runs the planner.
|
|
11
|
+
*
|
|
12
|
+
* Format:
|
|
13
|
+
*
|
|
14
|
+
* ---
|
|
15
|
+
* sessionId: <id>
|
|
16
|
+
* title: <title>
|
|
17
|
+
* goal: <goal>
|
|
18
|
+
* createdAt: <iso>
|
|
19
|
+
* updatedAt: <iso>
|
|
20
|
+
* ---
|
|
21
|
+
* ## Stack: <stack>
|
|
22
|
+
*
|
|
23
|
+
* - applicability: applicable
|
|
24
|
+
*
|
|
25
|
+
* ### Domain: <name> (id=<domain-id>)
|
|
26
|
+
*
|
|
27
|
+
* #### Scenario: <title> (id=<id>) [level=<unit|integration|e2e>]
|
|
28
|
+
*
|
|
29
|
+
* - status: planned
|
|
30
|
+
* - steps:
|
|
31
|
+
* - step 1
|
|
32
|
+
* - step 2
|
|
33
|
+
* - dependencies:
|
|
34
|
+
* - other-id
|
|
35
|
+
*
|
|
36
|
+
* Comments (`<!-- ... -->`) are tolerated and ignored on parse so we can prepend error
|
|
37
|
+
* annotations to a malformed file without breaking the round-trip.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
41
|
+
|
|
42
|
+
import type {
|
|
43
|
+
UltraPlanApplicability,
|
|
44
|
+
UltraPlanAuthoredArtifact,
|
|
45
|
+
UltraPlanScenarioLevel,
|
|
46
|
+
UltraPlanScenarioStatus,
|
|
47
|
+
UltraPlanStackId,
|
|
48
|
+
} from "../../types.js";
|
|
49
|
+
import {
|
|
50
|
+
validateUltraPlanAuthoredArtifact,
|
|
51
|
+
ULTRAPLAN_LEVELS,
|
|
52
|
+
ULTRAPLAN_STACKS,
|
|
53
|
+
} from "../contracts.js";
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Public types
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
export interface AuthoredMarkdownParseError {
|
|
60
|
+
line: number | null;
|
|
61
|
+
message: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Editable slice of an authored artifact \u2014 the fields the user can change via `$EDITOR`.
|
|
66
|
+
* Everything not in this struct (agent slots, proofs, progress, review gates, status of
|
|
67
|
+
* stacks/domains) is preserved verbatim from the original draft.
|
|
68
|
+
*/
|
|
69
|
+
export interface AuthoredEditablePatch {
|
|
70
|
+
sessionId: string;
|
|
71
|
+
title: string;
|
|
72
|
+
goal: string;
|
|
73
|
+
createdAt: string;
|
|
74
|
+
updatedAt: string;
|
|
75
|
+
stacks: AuthoredEditableStack[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface AuthoredEditableStack {
|
|
79
|
+
stack: UltraPlanStackId;
|
|
80
|
+
applicability: UltraPlanApplicability;
|
|
81
|
+
domains: AuthoredEditableDomain[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface AuthoredEditableDomain {
|
|
85
|
+
id: string;
|
|
86
|
+
name: string;
|
|
87
|
+
scenarios: AuthoredEditableScenario[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface AuthoredEditableScenario {
|
|
91
|
+
id: string;
|
|
92
|
+
title: string;
|
|
93
|
+
level: UltraPlanScenarioLevel;
|
|
94
|
+
status: UltraPlanScenarioStatus;
|
|
95
|
+
steps: string[];
|
|
96
|
+
dependencies: string[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type AuthoredMarkdownParseResult =
|
|
100
|
+
| { ok: true; patch: AuthoredEditablePatch }
|
|
101
|
+
| { ok: false; errors: AuthoredMarkdownParseError[] };
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Serialization
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
export function serializeAuthoredToMarkdown(authored: UltraPlanAuthoredArtifact): string {
|
|
108
|
+
const lines: string[] = [];
|
|
109
|
+
lines.push("---");
|
|
110
|
+
lines.push(stringifyYaml({
|
|
111
|
+
sessionId: authored.sessionId,
|
|
112
|
+
title: authored.title,
|
|
113
|
+
goal: authored.goal,
|
|
114
|
+
createdAt: authored.createdAt,
|
|
115
|
+
updatedAt: authored.updatedAt,
|
|
116
|
+
}).trimEnd());
|
|
117
|
+
lines.push("---");
|
|
118
|
+
lines.push("");
|
|
119
|
+
|
|
120
|
+
for (const stack of authored.stacks) {
|
|
121
|
+
lines.push(`## Stack: ${stack.stack}`);
|
|
122
|
+
lines.push("");
|
|
123
|
+
lines.push(`- applicability: ${stack.applicability}`);
|
|
124
|
+
lines.push("");
|
|
125
|
+
for (const domain of stack.domains) {
|
|
126
|
+
lines.push(`### Domain: ${domain.name} (id=${domain.id})`);
|
|
127
|
+
lines.push("");
|
|
128
|
+
const allScenarios = [...domain.unit, ...domain.integration, ...domain.e2e];
|
|
129
|
+
for (const scenario of allScenarios) {
|
|
130
|
+
lines.push(`#### Scenario: ${scenario.title} (id=${scenario.id}) [level=${scenario.level}]`);
|
|
131
|
+
lines.push("");
|
|
132
|
+
lines.push(`- status: ${scenario.status}`);
|
|
133
|
+
lines.push(`- steps:${scenario.steps.length === 0 ? " []" : ""}`);
|
|
134
|
+
for (const step of scenario.steps) lines.push(` - ${step}`);
|
|
135
|
+
const deps = scenario.dependencies ?? [];
|
|
136
|
+
lines.push(`- dependencies:${deps.length === 0 ? " []" : ""}`);
|
|
137
|
+
for (const dep of deps) lines.push(` - ${dep}`);
|
|
138
|
+
lines.push("");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return lines.join("\n").trimEnd() + "\n";
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
// Parsing
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
|
|
150
|
+
const STACK_HEADING = /^##\s+Stack:\s+(.+)$/;
|
|
151
|
+
const DOMAIN_HEADING = /^###\s+Domain:\s+(.+?)\s*\(id=([^)]+)\)\s*$/;
|
|
152
|
+
const SCENARIO_HEADING = /^####\s+Scenario:\s+(.+?)\s*\(id=([^)]+)\)\s*\[level=(unit|integration|e2e)\]\s*$/;
|
|
153
|
+
const FRONTMATTER_DELIM = /^---\s*$/;
|
|
154
|
+
|
|
155
|
+
interface ListContext {
|
|
156
|
+
key: "steps" | "dependencies";
|
|
157
|
+
items: string[];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface ParserState {
|
|
161
|
+
errors: AuthoredMarkdownParseError[];
|
|
162
|
+
fmRaw: string[];
|
|
163
|
+
stacks: AuthoredEditableStack[];
|
|
164
|
+
currentStack: AuthoredEditableStack | null;
|
|
165
|
+
currentDomain: AuthoredEditableDomain | null;
|
|
166
|
+
currentScenario: AuthoredEditableScenario | null;
|
|
167
|
+
list: ListContext | null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function pushError(state: ParserState, line: number | null, message: string) {
|
|
171
|
+
state.errors.push({ line, message });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function flushList(state: ParserState) {
|
|
175
|
+
if (!state.list || !state.currentScenario) return;
|
|
176
|
+
if (state.list.key === "steps") state.currentScenario.steps = state.list.items;
|
|
177
|
+
else state.currentScenario.dependencies = state.list.items;
|
|
178
|
+
state.list = null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function applyScenarioField(state: ParserState, key: string, value: string, line: number) {
|
|
182
|
+
const s = state.currentScenario!;
|
|
183
|
+
switch (key) {
|
|
184
|
+
case "status":
|
|
185
|
+
s.status = value.trim() as UltraPlanScenarioStatus;
|
|
186
|
+
return;
|
|
187
|
+
case "steps":
|
|
188
|
+
case "dependencies": {
|
|
189
|
+
flushList(state);
|
|
190
|
+
if (value.trim() === "[]") {
|
|
191
|
+
if (key === "steps") s.steps = [];
|
|
192
|
+
else s.dependencies = [];
|
|
193
|
+
state.list = null;
|
|
194
|
+
} else {
|
|
195
|
+
state.list = { key, items: [] };
|
|
196
|
+
}
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
default:
|
|
200
|
+
pushError(state, line, `Unknown scenario field: ${key}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function applyStackField(state: ParserState, key: string, value: string, line: number) {
|
|
205
|
+
const s = state.currentStack!;
|
|
206
|
+
if (key === "applicability") {
|
|
207
|
+
s.applicability = value.trim() as UltraPlanApplicability;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
// Tolerate unknown stack-level fields (status, etc.) — they are non-editable and round-trip
|
|
211
|
+
// through the original draft via applyAuthoredPatch.
|
|
212
|
+
void line;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function parseAuthoredFromMarkdown(markdown: string): AuthoredMarkdownParseResult {
|
|
216
|
+
const state: ParserState = {
|
|
217
|
+
errors: [],
|
|
218
|
+
fmRaw: [],
|
|
219
|
+
stacks: [],
|
|
220
|
+
currentStack: null,
|
|
221
|
+
currentDomain: null,
|
|
222
|
+
currentScenario: null,
|
|
223
|
+
list: null,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const rawLines = markdown.split(/\r?\n/);
|
|
227
|
+
let i = 0;
|
|
228
|
+
|
|
229
|
+
// Skip leading comment annotations and blank lines.
|
|
230
|
+
while (i < rawLines.length) {
|
|
231
|
+
const t = rawLines[i]!.trim();
|
|
232
|
+
if (t === "" || /^<!--[\s\S]*-->$/.test(t)) {
|
|
233
|
+
i += 1;
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (i >= rawLines.length || !FRONTMATTER_DELIM.test(rawLines[i]!)) {
|
|
240
|
+
pushError(state, i + 1, "Missing YAML frontmatter; expected --- on the first non-blank, non-comment line.");
|
|
241
|
+
return { ok: false, errors: state.errors };
|
|
242
|
+
}
|
|
243
|
+
i += 1;
|
|
244
|
+
const fmStartIndex = i;
|
|
245
|
+
while (i < rawLines.length && !FRONTMATTER_DELIM.test(rawLines[i]!)) {
|
|
246
|
+
state.fmRaw.push(rawLines[i]!);
|
|
247
|
+
i += 1;
|
|
248
|
+
}
|
|
249
|
+
if (i >= rawLines.length) {
|
|
250
|
+
pushError(state, fmStartIndex, "Frontmatter not terminated by closing ---.");
|
|
251
|
+
return { ok: false, errors: state.errors };
|
|
252
|
+
}
|
|
253
|
+
i += 1;
|
|
254
|
+
|
|
255
|
+
let frontmatter: Record<string, unknown> = {};
|
|
256
|
+
try {
|
|
257
|
+
const parsed = parseYaml(state.fmRaw.join("\n"));
|
|
258
|
+
frontmatter = (parsed && typeof parsed === "object") ? (parsed as Record<string, unknown>) : {};
|
|
259
|
+
} catch (error) {
|
|
260
|
+
pushError(state, 1, `Frontmatter YAML parse error: ${error instanceof Error ? error.message : String(error)}`);
|
|
261
|
+
return { ok: false, errors: state.errors };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
for (; i < rawLines.length; i += 1) {
|
|
265
|
+
const raw = rawLines[i]!;
|
|
266
|
+
const lineNo = i + 1;
|
|
267
|
+
const trimmed = raw.trim();
|
|
268
|
+
|
|
269
|
+
if (trimmed === "" || /^<!--[\s\S]*-->$/.test(trimmed)) continue;
|
|
270
|
+
|
|
271
|
+
const stackMatch = STACK_HEADING.exec(raw);
|
|
272
|
+
if (stackMatch) {
|
|
273
|
+
flushList(state);
|
|
274
|
+
const stackId = stackMatch[1]!.trim() as UltraPlanStackId;
|
|
275
|
+
if (!ULTRAPLAN_STACKS.includes(stackId)) {
|
|
276
|
+
pushError(state, lineNo, `Unknown stack: ${stackId}`);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const next: AuthoredEditableStack = { stack: stackId, applicability: "applicable", domains: [] };
|
|
280
|
+
state.stacks.push(next);
|
|
281
|
+
state.currentStack = next;
|
|
282
|
+
state.currentDomain = null;
|
|
283
|
+
state.currentScenario = null;
|
|
284
|
+
state.list = null;
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const domainMatch = DOMAIN_HEADING.exec(raw);
|
|
289
|
+
if (domainMatch) {
|
|
290
|
+
flushList(state);
|
|
291
|
+
if (!state.currentStack) {
|
|
292
|
+
pushError(state, lineNo, "Domain heading without a parent ## Stack heading.");
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const domain: AuthoredEditableDomain = {
|
|
296
|
+
id: domainMatch[2]!.trim(),
|
|
297
|
+
name: domainMatch[1]!.trim(),
|
|
298
|
+
scenarios: [],
|
|
299
|
+
};
|
|
300
|
+
state.currentStack.domains.push(domain);
|
|
301
|
+
state.currentDomain = domain;
|
|
302
|
+
state.currentScenario = null;
|
|
303
|
+
state.list = null;
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const scenarioMatch = SCENARIO_HEADING.exec(raw);
|
|
308
|
+
if (scenarioMatch) {
|
|
309
|
+
flushList(state);
|
|
310
|
+
if (!state.currentStack || !state.currentDomain) {
|
|
311
|
+
pushError(state, lineNo, "Scenario heading without parent stack and domain.");
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
const level = scenarioMatch[3]! as UltraPlanScenarioLevel;
|
|
315
|
+
if (!ULTRAPLAN_LEVELS.includes(level)) {
|
|
316
|
+
pushError(state, lineNo, `Unknown scenario level: ${level}`);
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const scenario: AuthoredEditableScenario = {
|
|
320
|
+
id: scenarioMatch[2]!.trim(),
|
|
321
|
+
title: scenarioMatch[1]!.trim(),
|
|
322
|
+
level,
|
|
323
|
+
status: "planned",
|
|
324
|
+
steps: [],
|
|
325
|
+
dependencies: [],
|
|
326
|
+
};
|
|
327
|
+
state.currentDomain.scenarios.push(scenario);
|
|
328
|
+
state.currentScenario = scenario;
|
|
329
|
+
state.list = null;
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (/^\s+-\s+/.test(raw) && state.list && state.currentScenario) {
|
|
334
|
+
const item = raw.replace(/^\s+-\s+/, "").trim();
|
|
335
|
+
state.list.items.push(item);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const fieldMatch = /^-\s+([A-Za-z][\w-]*):\s*(.*)$/.exec(raw);
|
|
340
|
+
if (fieldMatch) {
|
|
341
|
+
const key = fieldMatch[1]!;
|
|
342
|
+
const value = fieldMatch[2]!;
|
|
343
|
+
flushList(state);
|
|
344
|
+
if (state.currentScenario) {
|
|
345
|
+
applyScenarioField(state, key, value, lineNo);
|
|
346
|
+
} else if (state.currentStack && !state.currentDomain) {
|
|
347
|
+
applyStackField(state, key, value, lineNo);
|
|
348
|
+
}
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
flushList(state);
|
|
353
|
+
|
|
354
|
+
if (state.errors.length > 0) {
|
|
355
|
+
return { ok: false, errors: state.errors };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const sessionId = String(frontmatter.sessionId ?? "");
|
|
359
|
+
if (!sessionId) {
|
|
360
|
+
pushError(state, null, "Frontmatter is missing sessionId.");
|
|
361
|
+
return { ok: false, errors: state.errors };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return {
|
|
365
|
+
ok: true,
|
|
366
|
+
patch: {
|
|
367
|
+
sessionId,
|
|
368
|
+
title: String(frontmatter.title ?? ""),
|
|
369
|
+
goal: String(frontmatter.goal ?? ""),
|
|
370
|
+
createdAt: String(frontmatter.createdAt ?? ""),
|
|
371
|
+
updatedAt: String(frontmatter.updatedAt ?? ""),
|
|
372
|
+
stacks: state.stacks,
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ---------------------------------------------------------------------------
|
|
378
|
+
// Patch application
|
|
379
|
+
// ---------------------------------------------------------------------------
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Apply an editable patch onto an existing draft authored artifact, then validate the
|
|
383
|
+
* result. Non-editable fields (agentSlots, proofs, progress, review gates, etc.) are
|
|
384
|
+
* preserved from `original`; the patch only touches the visible-in-markdown subset.
|
|
385
|
+
*
|
|
386
|
+
* Scenarios in the patch are matched to scenarios in the original by id; any scenario whose
|
|
387
|
+
* id is not in the original is skipped (its agent-slot bindings would be undefined). Any
|
|
388
|
+
* original scenario whose id is missing from the patch is dropped (the user removed it).
|
|
389
|
+
*
|
|
390
|
+
* Stacks and domains follow the same id-matching rule; new stacks/domains in the patch with
|
|
391
|
+
* no original counterpart cannot be applied because they lack agent-slot bindings, and we
|
|
392
|
+
* surface that as a validation error rather than silently inserting empty data.
|
|
393
|
+
*/
|
|
394
|
+
export function applyAuthoredPatch(
|
|
395
|
+
original: UltraPlanAuthoredArtifact,
|
|
396
|
+
patch: AuthoredEditablePatch,
|
|
397
|
+
): { ok: true; value: UltraPlanAuthoredArtifact } | { ok: false; errors: string[] } {
|
|
398
|
+
if (patch.sessionId !== original.sessionId) {
|
|
399
|
+
return { ok: false, errors: [`Patch sessionId ${patch.sessionId} does not match original ${original.sessionId}`] };
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const originalStacksById = new Map(original.stacks.map((s) => [s.stack, s]));
|
|
403
|
+
const newStacks = [];
|
|
404
|
+
const errors: string[] = [];
|
|
405
|
+
|
|
406
|
+
for (const stackPatch of patch.stacks) {
|
|
407
|
+
const originalStack = originalStacksById.get(stackPatch.stack);
|
|
408
|
+
if (!originalStack) {
|
|
409
|
+
errors.push(
|
|
410
|
+
`Stack ${stackPatch.stack} appears in the edited markdown but not in the original draft; new stacks cannot be added through the editor.`,
|
|
411
|
+
);
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const originalDomainsById = new Map(originalStack.domains.map((d) => [d.id, d]));
|
|
416
|
+
const newDomains = [];
|
|
417
|
+
for (const domainPatch of stackPatch.domains) {
|
|
418
|
+
const originalDomain = originalDomainsById.get(domainPatch.id);
|
|
419
|
+
if (!originalDomain) {
|
|
420
|
+
errors.push(
|
|
421
|
+
`Domain ${domainPatch.id} (stack ${stackPatch.stack}) appears in the edited markdown but not in the original draft.`,
|
|
422
|
+
);
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
const originalScenariosById = new Map<string, typeof originalDomain.unit[number]>();
|
|
426
|
+
for (const s of [...originalDomain.unit, ...originalDomain.integration, ...originalDomain.e2e]) {
|
|
427
|
+
originalScenariosById.set(s.id, s);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const unit = [];
|
|
431
|
+
const integration = [];
|
|
432
|
+
const e2e = [];
|
|
433
|
+
for (const scenarioPatch of domainPatch.scenarios) {
|
|
434
|
+
const originalScenario = originalScenariosById.get(scenarioPatch.id);
|
|
435
|
+
if (!originalScenario) {
|
|
436
|
+
errors.push(
|
|
437
|
+
`Scenario ${scenarioPatch.id} (domain ${domainPatch.id}) appears in the edited markdown but not in the original draft.`,
|
|
438
|
+
);
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
const merged = {
|
|
442
|
+
...originalScenario,
|
|
443
|
+
title: scenarioPatch.title,
|
|
444
|
+
level: scenarioPatch.level,
|
|
445
|
+
status: scenarioPatch.status,
|
|
446
|
+
steps: scenarioPatch.steps,
|
|
447
|
+
dependencies: scenarioPatch.dependencies,
|
|
448
|
+
};
|
|
449
|
+
if (merged.level === "unit") unit.push(merged);
|
|
450
|
+
else if (merged.level === "integration") integration.push(merged);
|
|
451
|
+
else e2e.push(merged);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
newDomains.push({
|
|
455
|
+
...originalDomain,
|
|
456
|
+
name: domainPatch.name,
|
|
457
|
+
unit,
|
|
458
|
+
integration,
|
|
459
|
+
e2e,
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
newStacks.push({
|
|
464
|
+
...originalStack,
|
|
465
|
+
applicability: stackPatch.applicability,
|
|
466
|
+
domains: newDomains,
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (errors.length > 0) {
|
|
471
|
+
return { ok: false, errors };
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const next: UltraPlanAuthoredArtifact = {
|
|
475
|
+
...original,
|
|
476
|
+
title: patch.title,
|
|
477
|
+
goal: patch.goal,
|
|
478
|
+
updatedAt: patch.updatedAt || original.updatedAt,
|
|
479
|
+
stacks: newStacks,
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const validation = validateUltraPlanAuthoredArtifact(next);
|
|
483
|
+
if (!validation.ok) {
|
|
484
|
+
return { ok: false, errors: validation.errors };
|
|
485
|
+
}
|
|
486
|
+
return { ok: true, value: validation.value };
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// ---------------------------------------------------------------------------
|
|
490
|
+
// Error annotation
|
|
491
|
+
// ---------------------------------------------------------------------------
|
|
492
|
+
|
|
493
|
+
export function annotateParseErrors(markdown: string, errors: AuthoredMarkdownParseError[]): string {
|
|
494
|
+
if (errors.length === 0) return markdown;
|
|
495
|
+
const header: string[] = [];
|
|
496
|
+
header.push("<!-- AUTHORED EDIT ERRORS \u2014 fix the issues below, then save and close to re-validate. -->");
|
|
497
|
+
for (const err of errors) {
|
|
498
|
+
const prefix = err.line !== null ? `[line ${err.line}] ` : "";
|
|
499
|
+
header.push(`<!-- ${prefix}${err.message} -->`);
|
|
500
|
+
}
|
|
501
|
+
header.push("");
|
|
502
|
+
return `${header.join("\n")}\n${markdown}`;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
export function stripParseErrorAnnotations(markdown: string): string {
|
|
506
|
+
return markdown.replace(
|
|
507
|
+
/^(<!--\s*AUTHORED EDIT ERRORS[\s\S]*?-->\n)((<!--[\s\S]*?-->\n)*)\n?/,
|
|
508
|
+
"",
|
|
509
|
+
);
|
|
510
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-action-id model resolution for the multi-stage authoring pipeline.
|
|
3
|
+
*
|
|
4
|
+
* The pipeline runs every stage in a fresh `platform.createAgentSession`. Each spawned session
|
|
5
|
+
* needs a model identifier and (optionally) a thinking level. Selection follows the standard
|
|
6
|
+
* supipowers four-tier resolution implemented by `resolveModelForAction`:
|
|
7
|
+
* 1. `model.json` per-action override (`actions["ultraplan.authoring.<slot>"]`)
|
|
8
|
+
* 2. `model.json#default` (the workspace-wide supipowers default)
|
|
9
|
+
* 3. The platform's harness role hint (architect / research / review / default)
|
|
10
|
+
* 4. The main session model
|
|
11
|
+
*
|
|
12
|
+
* Stack-parameterized researchers each get their own action id (`...researcher.frontend`,
|
|
13
|
+
* `...researcher.backend`, `...researcher.infrastructure`), so per-stack overrides are a flat
|
|
14
|
+
* lookup in `model.json`. We register every action id at module load so the resolver and any
|
|
15
|
+
* tooling that introspects the registry sees them on first import.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type {
|
|
19
|
+
ModelConfig,
|
|
20
|
+
ResolvedModel,
|
|
21
|
+
UltraPlanAuthoringSlotName,
|
|
22
|
+
UltraPlanStackId,
|
|
23
|
+
} from "../../types.js";
|
|
24
|
+
import {
|
|
25
|
+
resolveModelForAction,
|
|
26
|
+
type ModelPlatformBridge,
|
|
27
|
+
} from "../../config/model-resolver.js";
|
|
28
|
+
import type { ModelActionRegistry } from "../../config/model-registry.js";
|
|
29
|
+
import { modelRegistry } from "../../config/model-registry-instance.js";
|
|
30
|
+
import { ULTRAPLAN_STACKS } from "../contracts.js";
|
|
31
|
+
|
|
32
|
+
/** Action-id namespace for the authoring pipeline. Centralized so callers do not inline. */
|
|
33
|
+
export const ULTRAPLAN_AUTHORING_ACTION_NAMESPACE = "ultraplan.authoring";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns the action id for an authoring slot, optionally parameterised by stack for the
|
|
37
|
+
* researcher slot.
|
|
38
|
+
*
|
|
39
|
+
* - For non-researcher slots, the stack hint is ignored.
|
|
40
|
+
* - For the researcher slot with no stack hint, the unparameterised id is returned (used by
|
|
41
|
+
* tooling that needs to address "the family of researcher actions" in the abstract). At
|
|
42
|
+
* resolution time, callers should always pass a stack hint.
|
|
43
|
+
*/
|
|
44
|
+
export function getAuthoringActionId(
|
|
45
|
+
slot: UltraPlanAuthoringSlotName,
|
|
46
|
+
stackHint: UltraPlanStackId | null = null,
|
|
47
|
+
): string {
|
|
48
|
+
if (slot === "researcher" && stackHint !== null) {
|
|
49
|
+
return `${ULTRAPLAN_AUTHORING_ACTION_NAMESPACE}.researcher.${stackHint}`;
|
|
50
|
+
}
|
|
51
|
+
return `${ULTRAPLAN_AUTHORING_ACTION_NAMESPACE}.${slot}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the model and thinking level for an authoring slot. Mirrors `resolveModelForAction`
|
|
56
|
+
* exactly — this is a thin wrapper that builds the correct action id and delegates so the
|
|
57
|
+
* four-tier fallback semantics stay identical to every other resolver call site.
|
|
58
|
+
*
|
|
59
|
+
* Use this in stage runners just before `createAgentSession`:
|
|
60
|
+
*
|
|
61
|
+
* const { model, thinkingLevel } = resolveAuthoringSlotModel(
|
|
62
|
+
* "researcher", "backend", config, modelRegistry, platformBridge,
|
|
63
|
+
* );
|
|
64
|
+
* const session = platform.createAgentSession({ ..., model, thinkingLevel });
|
|
65
|
+
*/
|
|
66
|
+
export function resolveAuthoringSlotModel(
|
|
67
|
+
slot: UltraPlanAuthoringSlotName,
|
|
68
|
+
stackHint: UltraPlanStackId | null,
|
|
69
|
+
config: ModelConfig,
|
|
70
|
+
registry: ModelActionRegistry,
|
|
71
|
+
platform: ModelPlatformBridge,
|
|
72
|
+
): ResolvedModel {
|
|
73
|
+
const actionId = getAuthoringActionId(slot, stackHint);
|
|
74
|
+
return resolveModelForAction(actionId, registry, config, platform);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Action registration. Runs at module load — every `import "..."` of this file ensures the
|
|
79
|
+
// authoring action ids are visible to the registry. Cheap to do up-front because each slot
|
|
80
|
+
// is registered exactly once (the registry is idempotent for repeated `register` calls with
|
|
81
|
+
// the same id; see `model-registry.ts`).
|
|
82
|
+
//
|
|
83
|
+
// Harness role hints map authoring slots to the four canonical harness roles (architect,
|
|
84
|
+
// research, review, default) so users on a stock harness get sensible models without ever
|
|
85
|
+
// editing model.json. Per-stack researchers all share the `research` role hint.
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
interface AuthoringActionRegistration {
|
|
89
|
+
id: string;
|
|
90
|
+
label: string;
|
|
91
|
+
harnessRoleHint: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function buildAuthoringActionRegistrations(): AuthoringActionRegistration[] {
|
|
95
|
+
const fixed: AuthoringActionRegistration[] = [
|
|
96
|
+
{
|
|
97
|
+
id: getAuthoringActionId("intake"),
|
|
98
|
+
label: "UltraPlan authoring · intake",
|
|
99
|
+
harnessRoleHint: "architect",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: getAuthoringActionId("scout"),
|
|
103
|
+
label: "UltraPlan authoring · scout",
|
|
104
|
+
harnessRoleHint: "research",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: getAuthoringActionId("discoverer"),
|
|
108
|
+
label: "UltraPlan authoring · discover",
|
|
109
|
+
harnessRoleHint: "architect",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: getAuthoringActionId("planner"),
|
|
113
|
+
label: "UltraPlan authoring · planner",
|
|
114
|
+
harnessRoleHint: "architect",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: getAuthoringActionId("structure-checker"),
|
|
118
|
+
label: "UltraPlan authoring · structure checker",
|
|
119
|
+
harnessRoleHint: "review",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: getAuthoringActionId("scope-checker"),
|
|
123
|
+
label: "UltraPlan authoring · scope checker",
|
|
124
|
+
harnessRoleHint: "review",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: getAuthoringActionId("tdd-checker"),
|
|
128
|
+
label: "UltraPlan authoring · TDD checker",
|
|
129
|
+
harnessRoleHint: "review",
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
const researchers: AuthoringActionRegistration[] = ULTRAPLAN_STACKS.map((stack) => ({
|
|
134
|
+
id: getAuthoringActionId("researcher", stack),
|
|
135
|
+
label: `UltraPlan authoring · researcher (${stack})`,
|
|
136
|
+
harnessRoleHint: "research",
|
|
137
|
+
}));
|
|
138
|
+
|
|
139
|
+
return [...fixed, ...researchers];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Registry of every authoring action id, exposed for tooling and tests. The order is stable
|
|
144
|
+
* (fixed slots first, then researchers in `ULTRAPLAN_STACKS` order) so snapshot tests can
|
|
145
|
+
* match without sorting.
|
|
146
|
+
*/
|
|
147
|
+
export const AUTHORING_ACTION_REGISTRATIONS: readonly AuthoringActionRegistration[] = Object.freeze(
|
|
148
|
+
buildAuthoringActionRegistrations(),
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// Register them once at module load. Node's module cache guarantees this file's body runs at
|
|
152
|
+
// most once per process, so the registry never sees duplicate calls (the registry rejects
|
|
153
|
+
// duplicate ids by design).
|
|
154
|
+
for (const action of AUTHORING_ACTION_REGISTRATIONS) {
|
|
155
|
+
modelRegistry.register({
|
|
156
|
+
id: action.id,
|
|
157
|
+
category: "sub-agent",
|
|
158
|
+
parent: "ultraplan",
|
|
159
|
+
label: action.label,
|
|
160
|
+
harnessRoleHint: action.harnessRoleHint,
|
|
161
|
+
});
|
|
162
|
+
}
|