@kaddo/cli 3.74.1 → 3.76.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/dist/admin-dist/assets/index-BPQdYfAp.css +2 -0
- package/dist/admin-dist/assets/index-BuxKrty5.js +44 -0
- package/dist/admin-dist/index.html +2 -2
- package/dist/admin-server/index.js +63 -323
- package/dist/core.js +444 -122
- package/package.json +1 -1
- package/dist/admin-dist/assets/index-AAaxSvT4.js +0 -38
- package/dist/admin-dist/assets/index-BPt-9--k.css +0 -2
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>admin</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-BuxKrty5.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BPQdYfAp.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
|
@@ -67,6 +67,8 @@ import {
|
|
|
67
67
|
validateWorkItem as coreValidateWorkItem,
|
|
68
68
|
transitionWorkItem as coreTransitionWorkItem,
|
|
69
69
|
getWorkItemCaptureDefinition as coreGetCaptureDefinition,
|
|
70
|
+
buildRefinementHandoff as coreBuildRefinementHandoff,
|
|
71
|
+
getSystemMapProjection as coreGetSystemMapProjection,
|
|
70
72
|
WorkItemWriteError,
|
|
71
73
|
exists,
|
|
72
74
|
join,
|
|
@@ -128,11 +130,23 @@ function assertValidWorkItemId(workItemId) {
|
|
|
128
130
|
}
|
|
129
131
|
function mapWriteError(err) {
|
|
130
132
|
if (err instanceof WorkItemWriteError) throw new CoreError(err.code, err.message);
|
|
133
|
+
if (err instanceof WorkItemNotFoundError) throw new CoreError("WORK_ITEM_NOT_FOUND", "This Work Item does not exist in the current project.");
|
|
131
134
|
throw err;
|
|
132
135
|
}
|
|
133
136
|
function getCaptureDefinition() {
|
|
134
137
|
return coreGetCaptureDefinition();
|
|
135
138
|
}
|
|
139
|
+
function getSystemMap(dir) {
|
|
140
|
+
return coreGetSystemMapProjection(dir);
|
|
141
|
+
}
|
|
142
|
+
function getRefinementHandoff(dir, workItemId) {
|
|
143
|
+
assertValidWorkItemId(workItemId);
|
|
144
|
+
try {
|
|
145
|
+
return coreBuildRefinementHandoff(dir, workItemId);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
mapWriteError(err);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
136
150
|
function createWorkItemAdmin(dir, body) {
|
|
137
151
|
try {
|
|
138
152
|
const res = coreCreateWorkItem(dir, { intent: body.intent, type: body.type, answers: body.answers });
|
|
@@ -490,6 +504,7 @@ var LinkedDecisionSchema = z.object({
|
|
|
490
504
|
});
|
|
491
505
|
var LinkedKnowledgeSchema = z.object({ id: z.string(), title: z.string(), layer: z.string() });
|
|
492
506
|
var WorkItemDetailSchema = WorkItemListItemSchema.extend({
|
|
507
|
+
summary: z.string().nullable(),
|
|
493
508
|
actor: z.string().nullable(),
|
|
494
509
|
outcome: z.string().nullable(),
|
|
495
510
|
currentBehavior: z.string().nullable(),
|
|
@@ -507,7 +522,11 @@ var WorkItemDetailSchema = WorkItemListItemSchema.extend({
|
|
|
507
522
|
decisions: z.array(LinkedDecisionSchema),
|
|
508
523
|
relatedKnowledge: z.array(LinkedKnowledgeSchema),
|
|
509
524
|
source: z.object({ type: z.string(), id: z.string().optional(), inferred: z.boolean() }).passthrough(),
|
|
510
|
-
path: z.string()
|
|
525
|
+
path: z.string(),
|
|
526
|
+
refinement: z.object({
|
|
527
|
+
status: z.enum(["needs-refinement", "refined"]),
|
|
528
|
+
aspects: z.object({ outcome: z.boolean(), journey: z.boolean(), modules: z.boolean(), impact: z.boolean(), acceptance: z.boolean() })
|
|
529
|
+
})
|
|
511
530
|
});
|
|
512
531
|
var WorkItemInputSchema = z.object({
|
|
513
532
|
title: z.string(),
|
|
@@ -562,13 +581,42 @@ var WorkItemCreateWithAnswersSchema = z.object({
|
|
|
562
581
|
type: z.string().min(1),
|
|
563
582
|
answers: z.record(z.string(), z.string()).optional()
|
|
564
583
|
});
|
|
565
|
-
var
|
|
566
|
-
|
|
567
|
-
|
|
584
|
+
var SystemMapNodeSchema = z.object({
|
|
585
|
+
id: z.string(),
|
|
586
|
+
type: z.string(),
|
|
587
|
+
label: z.string(),
|
|
588
|
+
status: z.string().optional(),
|
|
589
|
+
path: z.string().optional(),
|
|
590
|
+
workItemRef: z.string().optional(),
|
|
591
|
+
knowledgeRef: z.object({ id: z.string(), layer: z.string() }).optional(),
|
|
592
|
+
moduleId: z.string().optional()
|
|
568
593
|
});
|
|
569
|
-
var
|
|
570
|
-
|
|
571
|
-
|
|
594
|
+
var SystemMapRelationshipSchema = z.object({
|
|
595
|
+
id: z.string(),
|
|
596
|
+
source: z.string(),
|
|
597
|
+
target: z.string(),
|
|
598
|
+
type: z.string(),
|
|
599
|
+
label: z.string()
|
|
600
|
+
});
|
|
601
|
+
var SystemMapGroupSchema = z.object({
|
|
602
|
+
id: z.string(),
|
|
603
|
+
label: z.string(),
|
|
604
|
+
repositoryId: z.string(),
|
|
605
|
+
available: z.boolean()
|
|
606
|
+
});
|
|
607
|
+
var SystemMapProjectionSchema = z.object({
|
|
608
|
+
system: z.object({ name: z.string() }),
|
|
609
|
+
nodes: z.array(SystemMapNodeSchema),
|
|
610
|
+
relationships: z.array(SystemMapRelationshipSchema),
|
|
611
|
+
groups: z.array(SystemMapGroupSchema),
|
|
612
|
+
metadata: z.object({
|
|
613
|
+
projectName: z.string(),
|
|
614
|
+
structure: z.string(),
|
|
615
|
+
nodeCount: z.number(),
|
|
616
|
+
relationshipCount: z.number(),
|
|
617
|
+
coverage: z.enum(["good", "partial", "sparse", "empty"]),
|
|
618
|
+
available: z.boolean()
|
|
619
|
+
})
|
|
572
620
|
});
|
|
573
621
|
var ErrorResponseSchema = z.object({
|
|
574
622
|
error: z.object({
|
|
@@ -577,303 +625,11 @@ var ErrorResponseSchema = z.object({
|
|
|
577
625
|
})
|
|
578
626
|
});
|
|
579
627
|
|
|
580
|
-
// src/refinement/service.ts
|
|
581
|
-
import crypto from "crypto";
|
|
582
|
-
import {
|
|
583
|
-
assembleRefinementContext,
|
|
584
|
-
getWorkItemAgentAssets,
|
|
585
|
-
normalizeAndValidateProposal,
|
|
586
|
-
applyRefinement,
|
|
587
|
-
WorkItemWriteError as WorkItemWriteError2
|
|
588
|
-
} from "@kaddo/cli/core";
|
|
589
|
-
|
|
590
|
-
// src/refinement/provider.ts
|
|
591
|
-
var RefinementProviderError = class extends Error {
|
|
592
|
-
code;
|
|
593
|
-
constructor(code, message) {
|
|
594
|
-
super(message);
|
|
595
|
-
this.name = "RefinementProviderError";
|
|
596
|
-
this.code = code;
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
|
-
|
|
600
|
-
// src/refinement/service.ts
|
|
601
|
-
var MAX_REPAIR_ATTEMPTS = 2;
|
|
602
|
-
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
603
|
-
var RefinementService = class {
|
|
604
|
-
constructor(provider, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
605
|
-
this.provider = provider;
|
|
606
|
-
this.timeoutMs = timeoutMs;
|
|
607
|
-
}
|
|
608
|
-
provider;
|
|
609
|
-
timeoutMs;
|
|
610
|
-
sessions = /* @__PURE__ */ new Map();
|
|
611
|
-
view(s) {
|
|
612
|
-
return { ...s };
|
|
613
|
-
}
|
|
614
|
-
async run(dir, workItemId, feedback, previous) {
|
|
615
|
-
const context = assembleRefinementContext(dir, workItemId);
|
|
616
|
-
if (context.workItem.status !== "draft") {
|
|
617
|
-
throw new RefinementProviderError("WORK_ITEM_NOT_EDITABLE", `A ${context.workItem.status} Work Item cannot be refined. Reopen it as Draft first.`);
|
|
618
|
-
}
|
|
619
|
-
const assets = getWorkItemAgentAssets();
|
|
620
|
-
const request = { context, assets, intent: context.workItem.intent, previousProposal: previous, feedback };
|
|
621
|
-
let lastErr;
|
|
622
|
-
for (let attempt = 0; attempt <= MAX_REPAIR_ATTEMPTS; attempt++) {
|
|
623
|
-
const ac = new AbortController();
|
|
624
|
-
const timer = setTimeout(() => ac.abort(), this.timeoutMs);
|
|
625
|
-
try {
|
|
626
|
-
const result = await this.provider.refine(request, ac.signal);
|
|
627
|
-
const { validation } = normalizeAndValidateProposal(dir, workItemId, result.proposal);
|
|
628
|
-
const contextUsed = context.knowledge.map((k) => ({ id: k.id, title: k.title, layer: k.layer }));
|
|
629
|
-
return { context, proposal: result.proposal, validation, contextUsed, meta: { ...result.meta, repairAttempts: attempt } };
|
|
630
|
-
} catch (err) {
|
|
631
|
-
lastErr = err;
|
|
632
|
-
if (!(err instanceof RefinementProviderError && err.code === "INVALID_RESPONSE")) break;
|
|
633
|
-
} finally {
|
|
634
|
-
clearTimeout(timer);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
throw lastErr instanceof Error ? lastErr : new RefinementProviderError("PROVIDER_ERROR", "Refinement failed.");
|
|
638
|
-
}
|
|
639
|
-
async start(dir, workItemId) {
|
|
640
|
-
const { context, proposal, validation, contextUsed, meta } = await this.run(dir, workItemId);
|
|
641
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
642
|
-
const session = {
|
|
643
|
-
refinementId: `ref_${crypto.randomBytes(8).toString("hex")}`,
|
|
644
|
-
workItemId,
|
|
645
|
-
sourceRevision: context.revision,
|
|
646
|
-
status: "ready-for-review",
|
|
647
|
-
intent: context.workItem.intent,
|
|
648
|
-
proposal,
|
|
649
|
-
validation,
|
|
650
|
-
contextUsed,
|
|
651
|
-
meta,
|
|
652
|
-
createdAt: now,
|
|
653
|
-
updatedAt: now
|
|
654
|
-
};
|
|
655
|
-
this.sessions.set(session.refinementId, session);
|
|
656
|
-
return this.view(session);
|
|
657
|
-
}
|
|
658
|
-
async feedback(dir, workItemId, refinementId, feedback) {
|
|
659
|
-
const session = this.get(refinementId, workItemId);
|
|
660
|
-
const { context, proposal, validation, contextUsed, meta } = await this.run(dir, workItemId, feedback, session.proposal);
|
|
661
|
-
session.sourceRevision = context.revision;
|
|
662
|
-
session.status = "ready-for-review";
|
|
663
|
-
session.proposal = proposal;
|
|
664
|
-
session.validation = validation;
|
|
665
|
-
session.contextUsed = contextUsed;
|
|
666
|
-
session.meta = meta;
|
|
667
|
-
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
668
|
-
return this.view(session);
|
|
669
|
-
}
|
|
670
|
-
apply(dir, workItemId, refinementId, expectedRevision) {
|
|
671
|
-
const session = this.get(refinementId, workItemId);
|
|
672
|
-
const current = assembleRefinementContext(dir, workItemId);
|
|
673
|
-
if (current.revision !== session.sourceRevision || expectedRevision !== session.sourceRevision) {
|
|
674
|
-
session.status = "stale";
|
|
675
|
-
throw new RefinementProviderError("WORK_ITEM_CONFLICT", "This Work Item changed while the refinement was running. The proposal has not been applied.");
|
|
676
|
-
}
|
|
677
|
-
let res;
|
|
678
|
-
try {
|
|
679
|
-
res = applyRefinement(dir, workItemId, session.proposal, session.sourceRevision);
|
|
680
|
-
} catch (err) {
|
|
681
|
-
if (err instanceof WorkItemWriteError2) throw new RefinementProviderError(err.code, err.message);
|
|
682
|
-
throw err;
|
|
683
|
-
}
|
|
684
|
-
session.status = "applied";
|
|
685
|
-
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
686
|
-
return { id: workItemId, path: res.path, revision: res.revision, status: "draft" };
|
|
687
|
-
}
|
|
688
|
-
get(refinementId, workItemId) {
|
|
689
|
-
const s = this.sessions.get(refinementId);
|
|
690
|
-
if (!s || s.workItemId !== workItemId) throw new RefinementProviderError("REFINEMENT_NOT_FOUND", "Refinement session not found.");
|
|
691
|
-
return s;
|
|
692
|
-
}
|
|
693
|
-
};
|
|
694
|
-
|
|
695
|
-
// src/refinement/heuristic-provider.ts
|
|
696
|
-
function firstSentence(s) {
|
|
697
|
-
const m = s.trim().match(/^(.*?[.!?])(\s|$)/);
|
|
698
|
-
return (m ? m[1] : s.trim()).trim();
|
|
699
|
-
}
|
|
700
|
-
function mentionsNegation(feedback, module) {
|
|
701
|
-
const re = new RegExp(`(not|no)\\b[^.]*\\b${module}\\b|\\b${module}\\b[^.]*(not affected|no afecta|reviewed)`, "i");
|
|
702
|
-
return re.test(feedback);
|
|
703
|
-
}
|
|
704
|
-
var HeuristicRefinementProvider = class {
|
|
705
|
-
name = "heuristic";
|
|
706
|
-
async refine(request) {
|
|
707
|
-
const start = Date.now();
|
|
708
|
-
const { context, intent, previousProposal, feedback } = request;
|
|
709
|
-
const modules = context.modules;
|
|
710
|
-
const affected = new Set(previousProposal?.affectedModules ?? ["core"]);
|
|
711
|
-
if (feedback) {
|
|
712
|
-
for (const m of modules) {
|
|
713
|
-
if (new RegExp(`\\b${m}\\b`, "i").test(feedback)) {
|
|
714
|
-
if (mentionsNegation(feedback, m)) affected.delete(m);
|
|
715
|
-
else affected.add(m);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
const affectedModules = modules.filter((m) => affected.has(m));
|
|
720
|
-
const moduleCoverage = modules.map(
|
|
721
|
-
(m) => affected.has(m) ? { id: m, status: "affected", reason: m === "core" ? "Backend behavior changes." : "User-facing change identified." } : { id: m, status: "unknown" }
|
|
722
|
-
);
|
|
723
|
-
const frontendAffected = affected.has("frontend");
|
|
724
|
-
const impactAnalysis = [
|
|
725
|
-
{ surface: "backend", status: "affected" },
|
|
726
|
-
{ surface: "frontend", status: frontendAffected ? "affected" : "unknown", ...frontendAffected ? {} : { question: "Is a user-facing surface involved?" } },
|
|
727
|
-
{ surface: "database", status: "reviewed-not-affected" },
|
|
728
|
-
{ surface: "feature-flags", status: "unknown", question: "Is this behavior controlled by a feature flag?" }
|
|
729
|
-
];
|
|
730
|
-
const summary = firstSentence(intent);
|
|
731
|
-
const proposal = {
|
|
732
|
-
outcome: {
|
|
733
|
-
actor: "User",
|
|
734
|
-
observableOutcome: summary,
|
|
735
|
-
currentBehavior: `Today: ${summary.toLowerCase()} is not yet supported as described.`,
|
|
736
|
-
targetBehavior: summary
|
|
737
|
-
},
|
|
738
|
-
journey: {
|
|
739
|
-
entryPoints: [frontendAffected ? "Public entry point" : "Application entry point"],
|
|
740
|
-
flow: ["Entry point", "Application logic", "Persistence", "Result"]
|
|
741
|
-
},
|
|
742
|
-
affectedModules,
|
|
743
|
-
moduleCoverage,
|
|
744
|
-
impactAnalysis,
|
|
745
|
-
scopeConfidence: {
|
|
746
|
-
level: "medium",
|
|
747
|
-
reasons: ["Primary behavior identified from the intent.", "Feature flag ownership not yet confirmed."]
|
|
748
|
-
},
|
|
749
|
-
scopeUnknowns: ["Is this behavior controlled by a feature flag?"],
|
|
750
|
-
acceptanceCriteria: [
|
|
751
|
-
`${summary}`,
|
|
752
|
-
"The change is covered by the affected modules above."
|
|
753
|
-
],
|
|
754
|
-
linkedDecisions: [],
|
|
755
|
-
relatedKnowledge: []
|
|
756
|
-
};
|
|
757
|
-
return { proposal, meta: { provider: this.name, durationMs: Date.now() - start, repairAttempts: 0 } };
|
|
758
|
-
}
|
|
759
|
-
};
|
|
760
|
-
|
|
761
|
-
// src/refinement/anthropic-provider.ts
|
|
762
|
-
var API_URL = "https://api.anthropic.com/v1/messages";
|
|
763
|
-
var SCHEMA_HINT = `Return ONLY a JSON object (no prose, no code fences) with this shape \u2014 omit fields you cannot determine:
|
|
764
|
-
{
|
|
765
|
-
"title"?: string,
|
|
766
|
-
"outcome"?: { "actor"?: string, "observableOutcome"?: string, "currentBehavior"?: string, "targetBehavior"?: string },
|
|
767
|
-
"journey"?: { "entryPoints"?: string[], "flow"?: string[] },
|
|
768
|
-
"affectedModules"?: string[],
|
|
769
|
-
"moduleCoverage"?: [{ "id": string, "status": "affected"|"reviewed-not-affected"|"unknown"|"not-applicable", "reason"?: string }],
|
|
770
|
-
"impactAnalysis"?: [{ "surface": string, "status": "affected"|"reviewed-not-affected"|"unknown"|"not-applicable", "reason"?: string, "question"?: string }],
|
|
771
|
-
"scopeConfidence"?: { "level": "high"|"medium"|"low", "reasons"?: string[] },
|
|
772
|
-
"scopeUnknowns"?: string[],
|
|
773
|
-
"acceptanceCriteria"?: string[],
|
|
774
|
-
"linkedDecisions"?: string[],
|
|
775
|
-
"relatedKnowledge"?: string[]
|
|
776
|
-
}
|
|
777
|
-
Only reference module ids, decision ids and knowledge ids that appear in the provided context. Prefer "unknown" over inventing facts.`;
|
|
778
|
-
function extractJson(text) {
|
|
779
|
-
const start = text.indexOf("{");
|
|
780
|
-
const end = text.lastIndexOf("}");
|
|
781
|
-
if (start < 0 || end <= start) throw new RefinementProviderError("INVALID_RESPONSE", "The model did not return a JSON proposal.");
|
|
782
|
-
try {
|
|
783
|
-
return JSON.parse(text.slice(start, end + 1));
|
|
784
|
-
} catch {
|
|
785
|
-
throw new RefinementProviderError("INVALID_RESPONSE", "The model returned a proposal that could not be parsed.");
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
var AnthropicRefinementProvider = class {
|
|
789
|
-
name = "anthropic";
|
|
790
|
-
apiKey;
|
|
791
|
-
model;
|
|
792
|
-
constructor(apiKey, model) {
|
|
793
|
-
this.apiKey = apiKey;
|
|
794
|
-
this.model = model;
|
|
795
|
-
}
|
|
796
|
-
async refine(request, signal) {
|
|
797
|
-
const start = Date.now();
|
|
798
|
-
const { context, assets, intent, previousProposal, feedback } = request;
|
|
799
|
-
const system = [
|
|
800
|
-
assets.agentPrompt,
|
|
801
|
-
assets.skill ?? "",
|
|
802
|
-
"# Output format",
|
|
803
|
-
SCHEMA_HINT
|
|
804
|
-
].filter(Boolean).join("\n\n");
|
|
805
|
-
const userParts = [
|
|
806
|
-
`# Work Item intent
|
|
807
|
-
${intent}`,
|
|
808
|
-
`# Project
|
|
809
|
-
${JSON.stringify(context.project)}`,
|
|
810
|
-
`# Registered modules
|
|
811
|
-
${context.modules.join(", ")}`,
|
|
812
|
-
`# Known decisions
|
|
813
|
-
${context.decisions.map((d) => `${d.id} \u2014 ${d.title}`).join("\n") || "(none)"}`,
|
|
814
|
-
`# Knowledge
|
|
815
|
-
${context.knowledge.map((k) => `${k.id} \u2014 ${k.title} (${k.layer})`).join("\n") || "(none)"}`,
|
|
816
|
-
`# Current Work Item model
|
|
817
|
-
${JSON.stringify(context.workItem.current)}`
|
|
818
|
-
];
|
|
819
|
-
if (previousProposal) userParts.push(`# Previous proposal
|
|
820
|
-
${JSON.stringify(previousProposal)}`);
|
|
821
|
-
if (feedback) userParts.push(`# Human feedback (augments the original intent, does not replace it)
|
|
822
|
-
${feedback}`);
|
|
823
|
-
let res;
|
|
824
|
-
try {
|
|
825
|
-
res = await fetch(API_URL, {
|
|
826
|
-
method: "POST",
|
|
827
|
-
signal,
|
|
828
|
-
headers: {
|
|
829
|
-
"content-type": "application/json",
|
|
830
|
-
"x-api-key": this.apiKey,
|
|
831
|
-
"anthropic-version": "2023-06-01"
|
|
832
|
-
},
|
|
833
|
-
body: JSON.stringify({
|
|
834
|
-
model: this.model,
|
|
835
|
-
max_tokens: 2048,
|
|
836
|
-
system,
|
|
837
|
-
messages: [{ role: "user", content: userParts.join("\n\n") }]
|
|
838
|
-
})
|
|
839
|
-
});
|
|
840
|
-
} catch (err) {
|
|
841
|
-
if (err.name === "AbortError") throw new RefinementProviderError("TIMEOUT", "The refinement timed out.");
|
|
842
|
-
throw new RefinementProviderError("PROVIDER_ERROR", "The refinement provider could not be reached.");
|
|
843
|
-
}
|
|
844
|
-
if (!res.ok) {
|
|
845
|
-
throw new RefinementProviderError("PROVIDER_ERROR", `The refinement provider returned an error (${res.status}).`);
|
|
846
|
-
}
|
|
847
|
-
const body = await res.json();
|
|
848
|
-
const text = (body.content ?? []).filter((c) => c.type === "text").map((c) => c.text ?? "").join("");
|
|
849
|
-
const proposal = extractJson(text);
|
|
850
|
-
return {
|
|
851
|
-
proposal,
|
|
852
|
-
meta: {
|
|
853
|
-
provider: this.name,
|
|
854
|
-
model: this.model,
|
|
855
|
-
durationMs: Date.now() - start,
|
|
856
|
-
inputTokens: body.usage?.input_tokens,
|
|
857
|
-
outputTokens: body.usage?.output_tokens
|
|
858
|
-
}
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
};
|
|
862
|
-
|
|
863
|
-
// src/refinement/index.ts
|
|
864
|
-
function createRefinementService() {
|
|
865
|
-
const key = process.env.ANTHROPIC_API_KEY;
|
|
866
|
-
const model = process.env.KADDO_REFINEMENT_MODEL || "claude-3-5-sonnet-latest";
|
|
867
|
-
const provider = key ? new AnthropicRefinementProvider(key, model) : new HeuristicRefinementProvider();
|
|
868
|
-
return new RefinementService(provider);
|
|
869
|
-
}
|
|
870
|
-
|
|
871
628
|
// src/server.ts
|
|
872
629
|
var WRITE_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
873
630
|
function statusForCode(code) {
|
|
874
631
|
switch (code) {
|
|
875
632
|
case "WORK_ITEM_NOT_FOUND":
|
|
876
|
-
case "REFINEMENT_NOT_FOUND":
|
|
877
633
|
return 404;
|
|
878
634
|
case "WORK_ITEM_CONFLICT":
|
|
879
635
|
case "WORK_ITEM_NOT_EDITABLE":
|
|
@@ -882,11 +638,6 @@ function statusForCode(code) {
|
|
|
882
638
|
case "INVALID_WORK_ITEM_ID":
|
|
883
639
|
case "INVALID_TRANSITION":
|
|
884
640
|
return 400;
|
|
885
|
-
case "TIMEOUT":
|
|
886
|
-
return 504;
|
|
887
|
-
case "PROVIDER_ERROR":
|
|
888
|
-
case "INVALID_RESPONSE":
|
|
889
|
-
return 502;
|
|
890
641
|
default:
|
|
891
642
|
return 500;
|
|
892
643
|
}
|
|
@@ -895,7 +646,6 @@ async function createAdminServer(opts) {
|
|
|
895
646
|
const { projectDir, storage, staticDir, host = "127.0.0.1", port = 4173 } = opts;
|
|
896
647
|
const app = Fastify({ logger: false });
|
|
897
648
|
const sessionManager = new SessionManager(storage);
|
|
898
|
-
const refinement = createRefinementService();
|
|
899
649
|
await app.register(fastifyCookie);
|
|
900
650
|
await app.register(fastifyCors, {
|
|
901
651
|
origin: `http://${host}:${port}`,
|
|
@@ -1003,28 +753,15 @@ async function createAdminServer(opts) {
|
|
|
1003
753
|
if (!parsed.success) return reply.code(400).send({ error: { code: "INVALID_INPUT", message: "expectedRevision is required." } });
|
|
1004
754
|
return writeHandler(reply, () => transitionWorkItemAdmin(projectDir, request.params.workItemId, "draft", parsed.data.expectedRevision));
|
|
1005
755
|
});
|
|
1006
|
-
|
|
756
|
+
app.get("/api/v1/admin/work-items/:workItemId/refinement-handoff", async (request, reply) => {
|
|
1007
757
|
try {
|
|
1008
|
-
return
|
|
758
|
+
return getRefinementHandoff(projectDir, request.params.workItemId);
|
|
1009
759
|
} catch (err) {
|
|
1010
|
-
if (err instanceof
|
|
760
|
+
if (err instanceof CoreError) {
|
|
1011
761
|
return reply.code(statusForCode(err.code)).send({ error: { code: err.code, message: err.message } });
|
|
1012
762
|
}
|
|
1013
763
|
throw err;
|
|
1014
764
|
}
|
|
1015
|
-
};
|
|
1016
|
-
app.post("/api/v1/admin/work-items/:workItemId/refinement", async (request, reply) => {
|
|
1017
|
-
return refineHandler(reply, () => refinement.start(projectDir, request.params.workItemId));
|
|
1018
|
-
});
|
|
1019
|
-
app.post("/api/v1/admin/work-items/:workItemId/refinement/feedback", async (request, reply) => {
|
|
1020
|
-
const parsed = RefinementFeedbackSchema.safeParse(request.body);
|
|
1021
|
-
if (!parsed.success) return reply.code(400).send({ error: { code: "INVALID_INPUT", message: "refinementId and feedback are required." } });
|
|
1022
|
-
return refineHandler(reply, () => refinement.feedback(projectDir, request.params.workItemId, parsed.data.refinementId, parsed.data.feedback));
|
|
1023
|
-
});
|
|
1024
|
-
app.post("/api/v1/admin/work-items/:workItemId/refinement/apply", async (request, reply) => {
|
|
1025
|
-
const parsed = RefinementApplySchema.safeParse(request.body);
|
|
1026
|
-
if (!parsed.success) return reply.code(400).send({ error: { code: "INVALID_INPUT", message: "refinementId and expectedRevision are required." } });
|
|
1027
|
-
return refineHandler(reply, () => refinement.apply(projectDir, request.params.workItemId, parsed.data.refinementId, parsed.data.expectedRevision));
|
|
1028
765
|
});
|
|
1029
766
|
app.get("/api/v1/admin/work-items/:workItemId", async (request, reply) => {
|
|
1030
767
|
try {
|
|
@@ -1041,6 +778,7 @@ async function createAdminServer(opts) {
|
|
|
1041
778
|
app.get("/api/v1/admin/readiness", coreRoute(getProjectReadiness));
|
|
1042
779
|
app.get("/api/v1/admin/route", coreRoute(getProjectRoute));
|
|
1043
780
|
app.get("/api/v1/admin/findings", coreRoute(getFindings));
|
|
781
|
+
app.get("/api/v1/admin/system", coreRoute(getSystemMap));
|
|
1044
782
|
app.get("/api/v1/admin/knowledge/inventory", coreRoute(getKnowledgeInventory));
|
|
1045
783
|
app.get("/api/v1/admin/knowledge/artifact/:artifactId", async (request) => {
|
|
1046
784
|
try {
|
|
@@ -1192,11 +930,13 @@ export {
|
|
|
1192
930
|
ProjectReadinessSchema,
|
|
1193
931
|
ProjectRouteSchema,
|
|
1194
932
|
ProjectSummarySchema,
|
|
1195
|
-
RefinementApplySchema,
|
|
1196
|
-
RefinementFeedbackSchema,
|
|
1197
933
|
RouteStepSchema,
|
|
1198
934
|
SQLiteAdminStorage,
|
|
1199
935
|
SessionManager,
|
|
936
|
+
SystemMapGroupSchema,
|
|
937
|
+
SystemMapNodeSchema,
|
|
938
|
+
SystemMapProjectionSchema,
|
|
939
|
+
SystemMapRelationshipSchema,
|
|
1200
940
|
ValidationResultSchema,
|
|
1201
941
|
WorkItemCreateSchema,
|
|
1202
942
|
WorkItemCreateWithAnswersSchema,
|