@kontextmind/kxm 0.7.91 → 0.7.93
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/.claude-plugin/marketplace.json +1 -1
- package/.kxm/workflows/default.yaml +1 -1
- package/CHANGELOG.md +212 -0
- package/README.md +3 -0
- package/docs/README.md +3 -0
- package/docs/agent-skills.md +123 -60
- package/docs/architecture.md +5 -2
- package/docs/cli-reference.md +3527 -0
- package/docs/config-reference.md +1943 -0
- package/docs/configuration.md +30 -4
- package/docs/continuous-improvement.md +122 -10
- package/docs/contracts/routing.md +95 -11
- package/docs/harness-routing.md +616 -0
- package/docs/kxm-handbook.md +106 -19
- package/docs/templates/README.md +1 -1
- package/docs/test-matrix.md +12 -6
- package/docs/troubleshooting.md +2 -2
- package/examples/project/.kxm/workflows/fix.yaml +1 -1
- package/examples/project/.kxm/workflows/improve.yaml +1 -1
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +9 -10
- package/plugins/kxm/README.md +238 -56
- package/plugins/kxm/dist/claude-hook.js +10083 -0
- package/plugins/kxm/dist/cli.js +2487 -1848
- package/plugins/kxm/dist/client.js +64 -0
- package/plugins/kxm/dist/core.js +102 -9
- package/plugins/kxm/dist/extension.js +210 -68
- package/plugins/kxm/dist/mcp-server.js +217 -40
- package/plugins/kxm/dist/runtime-supervisor.js +1628 -157
- package/plugins/kxm/dist/runtime.js +1874 -298
- package/plugins/kxm/dist/server.js +416 -82
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/hints.json +1 -1
- package/plugins/kxm/skills/kxm/SKILL.md +48 -24
- package/plugins/kxm/skills/kxm/references/protocol.md +3 -3
- package/plugins/kxm/skills/kxm-context-memory/SKILL.md +67 -21
- package/plugins/kxm/skills/kxm-definitions/SKILL.md +9 -0
- package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +82 -16
- package/plugins/kxm/skills/kxm-harvest/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +55 -27
- package/plugins/kxm/skills/kxm-insights/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-mind/SKILL.md +2 -2
- package/plugins/kxm/skills/{kxm-setup → kxm-mind-setup}/SKILL.md +4 -4
- package/plugins/kxm/skills/kxm-peer/SKILL.md +68 -93
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +156 -23
- package/plugins/kxm/skills/kxm-projects/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-protocol/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-query/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +74 -15
- package/plugins/kxm/skills/kxm-runs/SKILL.md +46 -17
- package/plugins/kxm/skills/kxm-session/SKILL.md +64 -36
- package/plugins/kxm/skills/kxm-skill-lifecycle/SKILL.md +44 -15
- package/plugins/kxm/skills/kxm-tasks/SKILL.md +16 -4
- package/plugins/kxm/skills/kxm-triage/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-work/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-workflow/SKILL.md +60 -19
- package/plugins/kxm/src/arbiter.ts +67 -22
- package/plugins/kxm/src/autocomplete.ts +1 -1
- package/plugins/kxm/src/claude-hook.ts +192 -0
- package/plugins/kxm/src/cli/project.ts +11 -5
- package/plugins/kxm/src/cli/system.ts +85 -13
- package/plugins/kxm/src/cli/types.ts +4 -1
- package/plugins/kxm/src/cli/workflows.ts +18 -16
- package/plugins/kxm/src/cli.ts +23 -13
- package/plugins/kxm/src/client.ts +15 -4
- package/plugins/kxm/src/commands.ts +19 -9
- package/plugins/kxm/src/config.ts +42 -7
- package/plugins/kxm/src/context-packet.ts +14 -2
- package/plugins/kxm/src/context.ts +16 -5
- package/plugins/kxm/src/dispatch-context.ts +286 -0
- package/plugins/kxm/src/engine-plan.ts +40 -0
- package/plugins/kxm/src/engine.ts +138 -6
- package/plugins/kxm/src/hub-env.ts +17 -1
- package/plugins/kxm/src/hub.ts +92 -29
- package/plugins/kxm/src/improve-sources.ts +228 -0
- package/plugins/kxm/src/improve.ts +325 -140
- package/plugins/kxm/src/local-snapshot.ts +101 -42
- package/plugins/kxm/src/mcp-server.ts +129 -30
- package/plugins/kxm/src/memory.ts +43 -20
- package/plugins/kxm/src/project-config.ts +25 -0
- package/plugins/kxm/src/protocol.ts +11 -0
- package/plugins/kxm/src/relevance.ts +138 -0
- package/plugins/kxm/src/retrospective.ts +16 -10
- package/plugins/kxm/src/runtime-service.ts +8 -1
- package/plugins/kxm/src/runtime-supervisor.ts +16 -2
- package/plugins/kxm/src/session-token-hint.ts +17 -0
- package/plugins/kxm/src/suggest.ts +7 -7
- package/plugins/kxm/src/workflow-manager.ts +80 -78
- package/plugins/kxm/src/workflow.ts +202 -12
- package/scripts/build-runtime.mjs +7 -1
- package/scripts/check-generated.mjs +1 -0
- package/scripts/emit-codex-artifacts.mjs +1 -1
|
@@ -14815,6 +14815,15 @@ function toAgentRecord(agent, staleAfterMs, now) {
|
|
|
14815
14815
|
return { ...agent, ...agentPresenceView(agent, staleAfterMs, now) };
|
|
14816
14816
|
}
|
|
14817
14817
|
var MAX_SYNC_BATCH_EVENTS = 100;
|
|
14818
|
+
var IMPROVEMENT_AREAS = [
|
|
14819
|
+
"harness",
|
|
14820
|
+
"gates",
|
|
14821
|
+
"implementation",
|
|
14822
|
+
"workflow",
|
|
14823
|
+
"documentation",
|
|
14824
|
+
"security",
|
|
14825
|
+
"other"
|
|
14826
|
+
];
|
|
14818
14827
|
var ProtocolError = class extends Error {
|
|
14819
14828
|
statusCode;
|
|
14820
14829
|
code;
|
|
@@ -15626,6 +15635,151 @@ import { createHash as createHash3, randomUUID as randomUUID2, timingSafeEqual }
|
|
|
15626
15635
|
|
|
15627
15636
|
// plugins/kxm/src/workflow.ts
|
|
15628
15637
|
import { createHash as createHash2 } from "node:crypto";
|
|
15638
|
+
|
|
15639
|
+
// plugins/kxm/src/relevance.ts
|
|
15640
|
+
var RELEVANCE_STOPWORDS = Object.freeze(/* @__PURE__ */ new Set([
|
|
15641
|
+
"a",
|
|
15642
|
+
"an",
|
|
15643
|
+
"and",
|
|
15644
|
+
"are",
|
|
15645
|
+
"as",
|
|
15646
|
+
"at",
|
|
15647
|
+
"be",
|
|
15648
|
+
"been",
|
|
15649
|
+
"but",
|
|
15650
|
+
"by",
|
|
15651
|
+
"can",
|
|
15652
|
+
"could",
|
|
15653
|
+
"did",
|
|
15654
|
+
"do",
|
|
15655
|
+
"does",
|
|
15656
|
+
"for",
|
|
15657
|
+
"from",
|
|
15658
|
+
"had",
|
|
15659
|
+
"has",
|
|
15660
|
+
"have",
|
|
15661
|
+
"how",
|
|
15662
|
+
"if",
|
|
15663
|
+
"in",
|
|
15664
|
+
"into",
|
|
15665
|
+
"is",
|
|
15666
|
+
"it",
|
|
15667
|
+
"its",
|
|
15668
|
+
"of",
|
|
15669
|
+
"on",
|
|
15670
|
+
"or",
|
|
15671
|
+
"our",
|
|
15672
|
+
"should",
|
|
15673
|
+
"so",
|
|
15674
|
+
"than",
|
|
15675
|
+
"that",
|
|
15676
|
+
"the",
|
|
15677
|
+
"their",
|
|
15678
|
+
"them",
|
|
15679
|
+
"then",
|
|
15680
|
+
"there",
|
|
15681
|
+
"these",
|
|
15682
|
+
"they",
|
|
15683
|
+
"this",
|
|
15684
|
+
"those",
|
|
15685
|
+
"to",
|
|
15686
|
+
"was",
|
|
15687
|
+
"we",
|
|
15688
|
+
"were",
|
|
15689
|
+
"what",
|
|
15690
|
+
"when",
|
|
15691
|
+
"where",
|
|
15692
|
+
"which",
|
|
15693
|
+
"while",
|
|
15694
|
+
"who",
|
|
15695
|
+
"why",
|
|
15696
|
+
"will",
|
|
15697
|
+
"with",
|
|
15698
|
+
"would",
|
|
15699
|
+
"you",
|
|
15700
|
+
"your"
|
|
15701
|
+
]));
|
|
15702
|
+
var RELEVANCE_K1 = 1.2;
|
|
15703
|
+
var RELEVANCE_B = 0.75;
|
|
15704
|
+
var MIN_TOKEN_CHARS = 2;
|
|
15705
|
+
var MAX_TOKEN_CHARS = 64;
|
|
15706
|
+
function foldPlural(token) {
|
|
15707
|
+
if (new RegExp("^\\p{N}+$", "u").test(token)) return token;
|
|
15708
|
+
if (token.length > 4 && token.endsWith("ies")) return `${token.slice(0, -3)}y`;
|
|
15709
|
+
if (token.length > 4 && token.endsWith("sses")) return token.slice(0, -2);
|
|
15710
|
+
if (token.length > 3 && token.endsWith("s") && !token.endsWith("ss") && !token.endsWith("us") && !token.endsWith("is")) {
|
|
15711
|
+
return token.slice(0, -1);
|
|
15712
|
+
}
|
|
15713
|
+
return token;
|
|
15714
|
+
}
|
|
15715
|
+
function relevanceTokens(text2) {
|
|
15716
|
+
const tokens = [];
|
|
15717
|
+
for (const raw of text2.normalize("NFKC").toLowerCase().split(/[^\p{L}\p{N}]+/u)) {
|
|
15718
|
+
if (raw.length < MIN_TOKEN_CHARS || raw.length > MAX_TOKEN_CHARS) continue;
|
|
15719
|
+
if (RELEVANCE_STOPWORDS.has(raw)) continue;
|
|
15720
|
+
tokens.push(foldPlural(raw));
|
|
15721
|
+
}
|
|
15722
|
+
return tokens;
|
|
15723
|
+
}
|
|
15724
|
+
function scoreRelevance(query, documents) {
|
|
15725
|
+
const scores = documents.map(() => 0);
|
|
15726
|
+
const terms = [...new Set(relevanceTokens(query))];
|
|
15727
|
+
if (terms.length === 0 || documents.length === 0) return scores;
|
|
15728
|
+
const indexed = documents.map((document) => {
|
|
15729
|
+
const tokens = relevanceTokens(document);
|
|
15730
|
+
const frequencies = /* @__PURE__ */ new Map();
|
|
15731
|
+
for (const token of tokens) frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
|
|
15732
|
+
return { length: tokens.length, frequencies };
|
|
15733
|
+
});
|
|
15734
|
+
const count = indexed.length;
|
|
15735
|
+
let totalLength = 0;
|
|
15736
|
+
for (const document of indexed) totalLength += document.length;
|
|
15737
|
+
const averageLength = totalLength > 0 ? totalLength / count : 1;
|
|
15738
|
+
const inverseFrequency = /* @__PURE__ */ new Map();
|
|
15739
|
+
for (const term of terms) {
|
|
15740
|
+
let documentFrequency = 0;
|
|
15741
|
+
for (const document of indexed) {
|
|
15742
|
+
if (document.frequencies.has(term)) documentFrequency += 1;
|
|
15743
|
+
}
|
|
15744
|
+
inverseFrequency.set(term, Math.log(1 + (count - documentFrequency + 0.5) / (documentFrequency + 0.5)));
|
|
15745
|
+
}
|
|
15746
|
+
indexed.forEach((document, index) => {
|
|
15747
|
+
let score = 0;
|
|
15748
|
+
for (const term of terms) {
|
|
15749
|
+
const frequency = document.frequencies.get(term) ?? 0;
|
|
15750
|
+
if (frequency === 0) continue;
|
|
15751
|
+
const lengthNorm = 1 - RELEVANCE_B + RELEVANCE_B * (document.length / averageLength);
|
|
15752
|
+
score += inverseFrequency.get(term) * (frequency * (RELEVANCE_K1 + 1) / (frequency + RELEVANCE_K1 * lengthNorm));
|
|
15753
|
+
}
|
|
15754
|
+
scores[index] = score;
|
|
15755
|
+
});
|
|
15756
|
+
return scores;
|
|
15757
|
+
}
|
|
15758
|
+
function roundRelevance(score) {
|
|
15759
|
+
return Math.round(score * 1e3) / 1e3;
|
|
15760
|
+
}
|
|
15761
|
+
function contextItemRelevanceText(item) {
|
|
15762
|
+
return item.stateKey !== void 0 ? `${item.summary} ${item.stateKey}` : item.summary;
|
|
15763
|
+
}
|
|
15764
|
+
function compareCodeUnitIds(left, right) {
|
|
15765
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
15766
|
+
}
|
|
15767
|
+
function rankRecall(query, items, limit) {
|
|
15768
|
+
const needle = query.toLowerCase();
|
|
15769
|
+
const scores = scoreRelevance(query, items.map(contextItemRelevanceText));
|
|
15770
|
+
const ranked = [];
|
|
15771
|
+
items.forEach((item, index) => {
|
|
15772
|
+
const score = scores[index] ?? 0;
|
|
15773
|
+
const phraseHit = needle === "" || item.summary.toLowerCase().includes(needle) || (item.stateKey ?? "").toLowerCase().includes(needle);
|
|
15774
|
+
if (phraseHit || score > 0) ranked.push({ item, score, phraseHit });
|
|
15775
|
+
});
|
|
15776
|
+
ranked.sort(
|
|
15777
|
+
(left, right) => (right.phraseHit ? 1 : 0) - (left.phraseHit ? 1 : 0) || right.score - left.score || compareCodeUnitIds(left.item.id, right.item.id)
|
|
15778
|
+
);
|
|
15779
|
+
return ranked.slice(0, limit).map(({ item, score }) => ({ item, relevance: roundRelevance(score) }));
|
|
15780
|
+
}
|
|
15781
|
+
|
|
15782
|
+
// plugins/kxm/src/workflow.ts
|
|
15629
15783
|
var JOURNAL_CATEGORIES = [
|
|
15630
15784
|
"plan",
|
|
15631
15785
|
"decision",
|
|
@@ -15715,17 +15869,8 @@ function applyJournalPromotion(entry, decision, decidedAt) {
|
|
|
15715
15869
|
return { ...entry, promotion: [...entry.promotion ?? [], record] };
|
|
15716
15870
|
}
|
|
15717
15871
|
function improvementReport(entries) {
|
|
15718
|
-
const areas = [
|
|
15719
|
-
"harness",
|
|
15720
|
-
"gates",
|
|
15721
|
-
"implementation",
|
|
15722
|
-
"workflow",
|
|
15723
|
-
"documentation",
|
|
15724
|
-
"security",
|
|
15725
|
-
"other"
|
|
15726
|
-
];
|
|
15727
15872
|
const severityWeight = { error: 3, warning: 2, info: 1 };
|
|
15728
|
-
return
|
|
15873
|
+
return IMPROVEMENT_AREAS.map((area) => {
|
|
15729
15874
|
const matching = entries.filter((entry) => entry.area === area);
|
|
15730
15875
|
const priorities = [...matching].filter((entry) => entry.category === "error" || entry.category === "contradiction" || entry.category === "lesson" || entry.category === "skill-candidate").sort((left, right) => severityWeight[right.severity] - severityWeight[left.severity]).slice(0, 10);
|
|
15731
15876
|
return {
|
|
@@ -15738,6 +15883,97 @@ function improvementReport(entries) {
|
|
|
15738
15883
|
};
|
|
15739
15884
|
}).filter((report) => report.total > 0);
|
|
15740
15885
|
}
|
|
15886
|
+
var SECURITY_SIGNAL_CLASSES = ["invalid_auth", "invalid_identity", "signal_mismatch"];
|
|
15887
|
+
var SIGNAL_CATEGORIES = ["error", "contradiction", "lesson", "skill-candidate"];
|
|
15888
|
+
var SIGNAL_SEVERITY_WEIGHT = { error: 3, warning: 2, info: 1 };
|
|
15889
|
+
var SIGNAL_CLASS = /^[a-z0-9_]{1,64}$/;
|
|
15890
|
+
var MAX_SIGNAL_IDS = 16;
|
|
15891
|
+
var MAX_SIGNAL_SUMMARY_KEY_CHARS = 160;
|
|
15892
|
+
function normalizeSignalSummary(summary) {
|
|
15893
|
+
return summary.toLowerCase().replace(/\b[a-z]+_[0-9a-f]{8,}\b/g, "<id>").replace(/\b\d{4}-\d{2}-\d{2}t\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:z|[+-]\d{2}:?\d{2})?/g, "<ts>").replace(/\b[0-9a-f]{7,}\b/g, "<hex>").replace(/\d+/g, "#").replace(/\s+/g, " ").trim().slice(0, MAX_SIGNAL_SUMMARY_KEY_CHARS);
|
|
15894
|
+
}
|
|
15895
|
+
function round3(value) {
|
|
15896
|
+
return Math.round(value * 1e3) / 1e3;
|
|
15897
|
+
}
|
|
15898
|
+
function signalKeyOf(entry, runs) {
|
|
15899
|
+
const classRef = entry.evidence.find((ref) => ref.startsWith("class:"));
|
|
15900
|
+
const signalClass = classRef?.slice("class:".length);
|
|
15901
|
+
if (signalClass !== void 0 && SIGNAL_CLASS.test(signalClass) && signalClass !== "unknown") {
|
|
15902
|
+
return { key: `${entry.category}|class:${signalClass}`, basis: "class", signalClass };
|
|
15903
|
+
}
|
|
15904
|
+
const run = runs.get(entry.runId);
|
|
15905
|
+
if (entry.category === "error" && entry.stageId !== void 0 && run) {
|
|
15906
|
+
return { key: `error|stage:${run.definitionId}/${entry.stageId}`, basis: "stage" };
|
|
15907
|
+
}
|
|
15908
|
+
return {
|
|
15909
|
+
key: `${entry.category}|summary:${normalizeSignalSummary(redactSecrets(entry.summary))}`,
|
|
15910
|
+
basis: "summary"
|
|
15911
|
+
};
|
|
15912
|
+
}
|
|
15913
|
+
function runAttemptCost(run) {
|
|
15914
|
+
let attempts = 0;
|
|
15915
|
+
for (const stage of run.stages) attempts += stage.attempts;
|
|
15916
|
+
return Math.max(1, attempts + (run.transitions?.length ?? 0));
|
|
15917
|
+
}
|
|
15918
|
+
function rankImprovementSignals(entries, runs, limit = 20) {
|
|
15919
|
+
const resolvedContradictions = /* @__PURE__ */ new Set();
|
|
15920
|
+
for (const entry of entries) {
|
|
15921
|
+
if (entry.category !== "decision" && entry.category !== "lesson") continue;
|
|
15922
|
+
for (const related of entry.relatedEntryIds) resolvedContradictions.add(`${entry.runId}\0${related}`);
|
|
15923
|
+
}
|
|
15924
|
+
const groups = /* @__PURE__ */ new Map();
|
|
15925
|
+
for (const entry of entries) {
|
|
15926
|
+
if (!SIGNAL_CATEGORIES.includes(entry.category)) continue;
|
|
15927
|
+
if (entry.category === "contradiction" && resolvedContradictions.has(`${entry.runId}\0${entry.id}`)) continue;
|
|
15928
|
+
const promotionState = journalPromotionState(entry);
|
|
15929
|
+
if (promotionState !== void 0 && promotionState !== "proposed") continue;
|
|
15930
|
+
const { key, basis, signalClass } = signalKeyOf(entry, runs);
|
|
15931
|
+
const group = groups.get(key);
|
|
15932
|
+
if (group) group.entries.push(entry);
|
|
15933
|
+
else groups.set(key, { basis, category: entry.category, ...signalClass !== void 0 ? { signalClass } : {}, entries: [entry] });
|
|
15934
|
+
}
|
|
15935
|
+
const areaRank = (area) => {
|
|
15936
|
+
const index = IMPROVEMENT_AREAS.indexOf(area);
|
|
15937
|
+
return index === -1 ? IMPROVEMENT_AREAS.length : index;
|
|
15938
|
+
};
|
|
15939
|
+
const signals = [];
|
|
15940
|
+
for (const [key, group] of groups) {
|
|
15941
|
+
const runIds = [...new Set(group.entries.map((entry) => entry.runId))].sort(compareCodeUnitIds);
|
|
15942
|
+
const entryIds = group.entries.map((entry) => entry.id).sort(compareCodeUnitIds);
|
|
15943
|
+
let severity = "info";
|
|
15944
|
+
for (const entry of group.entries) {
|
|
15945
|
+
if ((SIGNAL_SEVERITY_WEIGHT[entry.severity] ?? 0) > SIGNAL_SEVERITY_WEIGHT[severity]) severity = entry.severity;
|
|
15946
|
+
}
|
|
15947
|
+
const severityWeight = SIGNAL_SEVERITY_WEIGHT[severity];
|
|
15948
|
+
const knownRuns = runIds.map((runId) => runs.get(runId)).filter((run) => run !== void 0);
|
|
15949
|
+
const workflowCost = knownRuns.length > 0 ? knownRuns.reduce((total, run) => total + runAttemptCost(run), 0) / knownRuns.length : null;
|
|
15950
|
+
const withEvidence = group.entries.filter((entry) => entry.evidence.length > 0).length;
|
|
15951
|
+
const confidence = 0.5 + 0.5 * (withEvidence / group.entries.length);
|
|
15952
|
+
const areaCounts = /* @__PURE__ */ new Map();
|
|
15953
|
+
for (const entry of group.entries) areaCounts.set(entry.area, (areaCounts.get(entry.area) ?? 0) + 1);
|
|
15954
|
+
const area = [...areaCounts].sort((left, right) => right[1] - left[1] || areaRank(left[0]) - areaRank(right[0]))[0][0];
|
|
15955
|
+
const latest = [...group.entries].sort((left, right) => compareCodeUnitIds(left.createdAt, right.createdAt) || compareCodeUnitIds(left.id, right.id)).at(-1);
|
|
15956
|
+
const security = area === "security" || group.signalClass !== void 0 && SECURITY_SIGNAL_CLASSES.includes(group.signalClass);
|
|
15957
|
+
signals.push({
|
|
15958
|
+
key,
|
|
15959
|
+
basis: group.basis,
|
|
15960
|
+
category: group.category,
|
|
15961
|
+
area,
|
|
15962
|
+
...security ? { overrideTier: "security" } : {},
|
|
15963
|
+
frequency: runIds.length,
|
|
15964
|
+
runIds: runIds.slice(0, MAX_SIGNAL_IDS),
|
|
15965
|
+
entryIds: entryIds.slice(0, MAX_SIGNAL_IDS),
|
|
15966
|
+
severity,
|
|
15967
|
+
severityWeight,
|
|
15968
|
+
workflowCost,
|
|
15969
|
+
costBasis: workflowCost === null ? "unknown" : "run-attempts",
|
|
15970
|
+
confidence,
|
|
15971
|
+
priority: round3(runIds.length * severityWeight * (workflowCost ?? 1) * confidence),
|
|
15972
|
+
summary: redactSecrets(latest.summary)
|
|
15973
|
+
});
|
|
15974
|
+
}
|
|
15975
|
+
return signals.sort((left, right) => (right.overrideTier === "security" ? 1 : 0) - (left.overrideTier === "security" ? 1 : 0) || right.priority - left.priority || right.frequency - left.frequency || compareCodeUnitIds(left.key, right.key)).slice(0, limit);
|
|
15976
|
+
}
|
|
15741
15977
|
function object(value, name) {
|
|
15742
15978
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${name} must be an object`);
|
|
15743
15979
|
return value;
|
|
@@ -15825,6 +16061,19 @@ function workflowEvidenceStrings(evidence) {
|
|
|
15825
16061
|
function activeWorkflowAttempt(stage) {
|
|
15826
16062
|
return stage.attempts + 1;
|
|
15827
16063
|
}
|
|
16064
|
+
function journalAttemptFor(stage) {
|
|
16065
|
+
switch (stage.status) {
|
|
16066
|
+
case "in_progress":
|
|
16067
|
+
case "waiting":
|
|
16068
|
+
return stage.attempts + 1;
|
|
16069
|
+
case "pending":
|
|
16070
|
+
return stage.attempts > 0 ? stage.attempts : void 0;
|
|
16071
|
+
case "passed":
|
|
16072
|
+
case "warning":
|
|
16073
|
+
case "failed":
|
|
16074
|
+
return Math.max(1, stage.attempts);
|
|
16075
|
+
}
|
|
16076
|
+
}
|
|
15828
16077
|
function validIsoTimestamp(value) {
|
|
15829
16078
|
if (!value) return void 0;
|
|
15830
16079
|
const parsed = Date.parse(value);
|
|
@@ -16139,7 +16388,7 @@ function parseWorkflowDefinitions(raw, environment = process.env, onWarning) {
|
|
|
16139
16388
|
throw new Error(`stage ${stageId} maxAttempts must be an integer between 1 and 20`);
|
|
16140
16389
|
}
|
|
16141
16390
|
const area = stage.area ? requireString(stage.area, "stage.area", { max: 24 }) : void 0;
|
|
16142
|
-
if (area && !
|
|
16391
|
+
if (area && !IMPROVEMENT_AREAS.includes(area)) {
|
|
16143
16392
|
throw new Error(`stage ${stageId} area is invalid`);
|
|
16144
16393
|
}
|
|
16145
16394
|
const requiredEvidence = stringArray(stage.requiredEvidence ?? [], "stage.requiredEvidence").map((requirement, requirementIndex) => canonicalWorkflowEvidenceKey(
|
|
@@ -16977,7 +17226,7 @@ var AGENT_COMMANDS = [
|
|
|
16977
17226
|
group: "workflow",
|
|
16978
17227
|
verb: "run",
|
|
16979
17228
|
label: "Get workflow run",
|
|
16980
|
-
description: "Get a workflow's stages and journal
|
|
17229
|
+
description: "Get a workflow's stages and its learning journal (plans, decisions, contradictions, errors, lessons, and the other journal categories).",
|
|
16981
17230
|
parameters: {
|
|
16982
17231
|
type: "object",
|
|
16983
17232
|
properties: {
|
|
@@ -17051,20 +17300,24 @@ var AGENT_COMMANDS = [
|
|
|
17051
17300
|
group: "workflow",
|
|
17052
17301
|
verb: "record",
|
|
17053
17302
|
label: "Record workflow journal entry",
|
|
17054
|
-
description: "Record a plan, decision, contradiction, error, or
|
|
17303
|
+
description: "Record a plan, decision, contradiction, error, lesson, observation, hypothesis, experiment, state-change, or skill-candidate for continuous improvement. Pass stageId to bind the entry to that stage: the hub derives the attempt, and area defaults to the stage's declared area. Lessons and skill-candidates require evidence.",
|
|
17055
17304
|
parameters: {
|
|
17056
17305
|
type: "object",
|
|
17057
17306
|
properties: {
|
|
17058
17307
|
runId: { type: "string", description: "Active durable workflow run ID" },
|
|
17059
17308
|
category: {
|
|
17060
17309
|
type: "string",
|
|
17061
|
-
enum: [
|
|
17310
|
+
enum: [...JOURNAL_CATEGORIES],
|
|
17062
17311
|
description: "Category of journal entry"
|
|
17063
17312
|
},
|
|
17064
17313
|
area: {
|
|
17065
17314
|
type: "string",
|
|
17066
|
-
enum: [
|
|
17067
|
-
description: "System area"
|
|
17315
|
+
enum: [...IMPROVEMENT_AREAS],
|
|
17316
|
+
description: "System area; required unless stageId names a stage that declares an area"
|
|
17317
|
+
},
|
|
17318
|
+
stageId: {
|
|
17319
|
+
type: "string",
|
|
17320
|
+
description: "Stage the entry belongs to; the hub binds the attempt from the stage's state"
|
|
17068
17321
|
},
|
|
17069
17322
|
severity: {
|
|
17070
17323
|
type: "string",
|
|
@@ -17087,13 +17340,14 @@ var AGENT_COMMANDS = [
|
|
|
17087
17340
|
description: "Related previous journal entry IDs"
|
|
17088
17341
|
}
|
|
17089
17342
|
},
|
|
17090
|
-
required: ["runId", "category", "
|
|
17343
|
+
required: ["runId", "category", "summary"],
|
|
17091
17344
|
additionalProperties: false
|
|
17092
17345
|
},
|
|
17093
17346
|
async execute(client, args) {
|
|
17094
17347
|
return await client.recordWorkflowEntry(requiredString(args.runId, "runId"), {
|
|
17095
17348
|
category: requiredString(args.category, "category"),
|
|
17096
|
-
area:
|
|
17349
|
+
...optionalString2(args.area) ? { area: optionalString2(args.area) } : {},
|
|
17350
|
+
...optionalString2(args.stageId) ? { stageId: optionalString2(args.stageId) } : {},
|
|
17097
17351
|
...optionalString2(args.severity) ? { severity: optionalString2(args.severity) } : {},
|
|
17098
17352
|
summary: requiredString(args.summary, "summary"),
|
|
17099
17353
|
...optionalString2(args.details) ? { details: optionalString2(args.details) } : {},
|
|
@@ -17170,7 +17424,7 @@ var AGENT_COMMANDS = [
|
|
|
17170
17424
|
group: "workflow",
|
|
17171
17425
|
verb: "improve-report",
|
|
17172
17426
|
label: "Summarize improvement report",
|
|
17173
|
-
description: "Summarize workflow errors, contradictions, and
|
|
17427
|
+
description: "Summarize workflow errors, contradictions, lessons, and skill candidates by improvement area, plus ranked cross-run signals: duplicates merged across runs and scored by frequency x severity x run-attempt cost x evidence confidence, security first, with redacted text.",
|
|
17174
17428
|
parameters: {
|
|
17175
17429
|
type: "object",
|
|
17176
17430
|
properties: {},
|
|
@@ -17224,7 +17478,7 @@ var AGENT_COMMANDS = [
|
|
|
17224
17478
|
group: "context",
|
|
17225
17479
|
verb: "recall",
|
|
17226
17480
|
label: "Recall context metadata",
|
|
17227
|
-
description: "Search durable context records for a project by query; returns bounded metadata
|
|
17481
|
+
description: "Search durable context records for a project by query. Ranks exact-phrase matches first, then token relevance, then id; returns bounded metadata with a numeric relevance per item, never summaries.",
|
|
17228
17482
|
parameters: {
|
|
17229
17483
|
type: "object",
|
|
17230
17484
|
properties: {
|
|
@@ -17547,7 +17801,14 @@ function parseContextRequest(value) {
|
|
|
17547
17801
|
return request;
|
|
17548
17802
|
}
|
|
17549
17803
|
function validateContextPacketContents(request, packet) {
|
|
17550
|
-
const items = [
|
|
17804
|
+
const items = [
|
|
17805
|
+
...packet.currentState,
|
|
17806
|
+
...packet.knowledge,
|
|
17807
|
+
...packet.evidence,
|
|
17808
|
+
...packet.episodes,
|
|
17809
|
+
...packet.skills,
|
|
17810
|
+
...packet.contradictions
|
|
17811
|
+
];
|
|
17551
17812
|
if (items.length > MAX_CONTEXT_ITEMS) {
|
|
17552
17813
|
throw new ProtocolError(400, `context packet exceeds ${MAX_CONTEXT_ITEMS} items`, "context_limits_exceeded");
|
|
17553
17814
|
}
|
|
@@ -17569,12 +17830,12 @@ function validateContextPacketContents(request, packet) {
|
|
|
17569
17830
|
}
|
|
17570
17831
|
}
|
|
17571
17832
|
}
|
|
17833
|
+
function contextItemCharacters(item) {
|
|
17834
|
+
return item.summary.length + item.id.length + item.kind.length + (item.provenance.sourceRef?.length ?? 0);
|
|
17835
|
+
}
|
|
17572
17836
|
function estimateContextTokens(items) {
|
|
17573
17837
|
let characters = 0;
|
|
17574
|
-
for (const item of items)
|
|
17575
|
-
characters += item.summary.length + item.id.length + item.kind.length;
|
|
17576
|
-
if (item.provenance.sourceRef) characters += item.provenance.sourceRef.length;
|
|
17577
|
-
}
|
|
17838
|
+
for (const item of items) characters += contextItemCharacters(item);
|
|
17578
17839
|
return Math.ceil(characters / 4);
|
|
17579
17840
|
}
|
|
17580
17841
|
function authorityRank(authority) {
|
|
@@ -17619,7 +17880,7 @@ var ROLE_POLICIES = [
|
|
|
17619
17880
|
{
|
|
17620
17881
|
role: "repro",
|
|
17621
17882
|
label: "Reproduction specialist",
|
|
17622
|
-
kinds: ["episode", "knowledge"],
|
|
17883
|
+
kinds: ["episode", "knowledge", "evidence"],
|
|
17623
17884
|
journalCategories: ["error", "lesson", "observation", "contradiction"],
|
|
17624
17885
|
budgetTokens: 8e3
|
|
17625
17886
|
},
|
|
@@ -17640,7 +17901,7 @@ var ROLE_POLICIES = [
|
|
|
17640
17901
|
{
|
|
17641
17902
|
role: "implementer",
|
|
17642
17903
|
label: "Implementer",
|
|
17643
|
-
kinds: ["knowledge", "state", "skill", "episode"],
|
|
17904
|
+
kinds: ["knowledge", "state", "skill", "episode", "evidence"],
|
|
17644
17905
|
journalCategories: ["plan", "decision", "lesson", "state-change"],
|
|
17645
17906
|
budgetTokens: 16e3
|
|
17646
17907
|
},
|
|
@@ -17712,29 +17973,40 @@ function arbitrate(requestInput, pool, options = {}) {
|
|
|
17712
17973
|
const rank = kindRank.get(item.kind);
|
|
17713
17974
|
return rank === void 0 ? requestedKinds.length : rank;
|
|
17714
17975
|
};
|
|
17715
|
-
const
|
|
17716
|
-
|
|
17976
|
+
const allowedKinds = new Set(requestedKinds);
|
|
17977
|
+
const inertProposal = (item) => item.kind === "state" && item.status !== void 0 && item.status !== "current" || item.kind === "skill" && item.status === "proposed";
|
|
17978
|
+
const eligible = candidates.filter((item) => contradictions.has(item.id) || allowedKinds.has(item.kind) && !inertProposal(item));
|
|
17979
|
+
const scoreList = scoreRelevance(request.task, eligible.map(contextItemRelevanceText));
|
|
17980
|
+
const scores = /* @__PURE__ */ new Map();
|
|
17981
|
+
eligible.forEach((item, index) => scores.set(item, scoreList[index] ?? 0));
|
|
17982
|
+
const scoreOf = (item) => scores.get(item) ?? 0;
|
|
17983
|
+
const when = (item) => {
|
|
17984
|
+
const parsed = Date.parse(item.observedAt ?? item.validFrom ?? "");
|
|
17985
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
17986
|
+
};
|
|
17987
|
+
const ordered = [...eligible].sort(
|
|
17988
|
+
(left, right) => (contradictions.has(right.id) ? 1 : 0) - (contradictions.has(left.id) ? 1 : 0) || (left.project === request.project ? 0 : 1) - (right.project === request.project ? 0 : 1) || (scoreOf(right) > 0 ? 1 : 0) - (scoreOf(left) > 0 ? 1 : 0) || kindPreference(left) - kindPreference(right) || scoreOf(right) - scoreOf(left) || CONFIDENCE_RANK[right.confidence] - CONFIDENCE_RANK[left.confidence] || AUTHORITY_WEIGHT[right.authority] - AUTHORITY_WEIGHT[left.authority] || when(right) - when(left) || compareCodeUnitIds(left.id, right.id)
|
|
17717
17989
|
);
|
|
17718
|
-
const kindAllowed = (item) => (request.includeKinds ?? policy.kinds).includes(item.kind) || contradictions.has(item.id);
|
|
17719
17990
|
const selected = [];
|
|
17720
17991
|
const unresolvedGaps = [];
|
|
17992
|
+
let characters = 0;
|
|
17993
|
+
let deferredForBudget = 0;
|
|
17721
17994
|
for (const item of ordered) {
|
|
17722
17995
|
if (selected.length >= MAX_CONTEXT_ITEMS) {
|
|
17723
17996
|
unresolvedGaps.push("context item limit reached; refine the task or kinds");
|
|
17724
17997
|
break;
|
|
17725
17998
|
}
|
|
17726
|
-
|
|
17727
|
-
|
|
17728
|
-
|
|
17729
|
-
|
|
17730
|
-
unresolvedGaps.push(`budget of ${budget} tokens cannot fit any selected context`);
|
|
17731
|
-
break;
|
|
17732
|
-
}
|
|
17733
|
-
unresolvedGaps.push(`budget of ${budget} tokens reached; ${ordered.length - selected.length} candidates deferred`);
|
|
17734
|
-
break;
|
|
17999
|
+
const itemCharacters = contextItemCharacters(item);
|
|
18000
|
+
if (Math.ceil((characters + itemCharacters) / 4) > budget) {
|
|
18001
|
+
deferredForBudget += 1;
|
|
18002
|
+
continue;
|
|
17735
18003
|
}
|
|
18004
|
+
characters += itemCharacters;
|
|
17736
18005
|
selected.push(item);
|
|
17737
18006
|
}
|
|
18007
|
+
if (deferredForBudget > 0) {
|
|
18008
|
+
unresolvedGaps.push(selected.length === 0 ? `budget of ${budget} tokens cannot fit any selected context` : `budget of ${budget} tokens reached; ${deferredForBudget} candidates deferred`);
|
|
18009
|
+
}
|
|
17738
18010
|
if (candidates.length === 0) {
|
|
17739
18011
|
unresolvedGaps.push("no context records exist for this project yet");
|
|
17740
18012
|
}
|
|
@@ -17743,6 +18015,7 @@ function arbitrate(requestInput, pool, options = {}) {
|
|
|
17743
18015
|
workingState: options.workingState ?? {},
|
|
17744
18016
|
currentState: bySection("state").filter((item) => item.status === "current" || item.status === void 0),
|
|
17745
18017
|
knowledge: bySection("knowledge"),
|
|
18018
|
+
evidence: bySection("evidence"),
|
|
17746
18019
|
episodes: bySection("episode"),
|
|
17747
18020
|
skills: bySection("skill").filter((item) => item.status !== "proposed"),
|
|
17748
18021
|
contradictions: selected.filter((item) => contradictions.has(item.id)),
|
|
@@ -17767,7 +18040,12 @@ function arbitrate(requestInput, pool, options = {}) {
|
|
|
17767
18040
|
budgetTokens: budget,
|
|
17768
18041
|
candidateCount: candidates.length,
|
|
17769
18042
|
excludedSuperseded,
|
|
17770
|
-
unresolvedGaps
|
|
18043
|
+
unresolvedGaps,
|
|
18044
|
+
relevance: {
|
|
18045
|
+
taskTokens: new Set(relevanceTokens(request.task)).size,
|
|
18046
|
+
matchedCandidates: scoreList.filter((score) => score > 0).length,
|
|
18047
|
+
selected: selected.map((item) => roundRelevance(scoreOf(item)))
|
|
18048
|
+
}
|
|
17771
18049
|
}
|
|
17772
18050
|
};
|
|
17773
18051
|
}
|
|
@@ -17784,10 +18062,9 @@ function journalEntryToContextItem(entry, project) {
|
|
|
17784
18062
|
},
|
|
17785
18063
|
authority: entry.category === "decision" || entry.category === "plan" ? "evidence" : "evidence",
|
|
17786
18064
|
confidence: entry.severity === "error" ? "probable" : "probable",
|
|
17787
|
-
|
|
18065
|
+
observedAt: entry.createdAt,
|
|
17788
18066
|
evidenceRefs: entry.evidence.filter((ref) => ref.length > 0 && ref.length <= 200).slice(0, 16)
|
|
17789
18067
|
};
|
|
17790
|
-
if (entry.stageId !== void 0) item.observedAt = entry.createdAt;
|
|
17791
18068
|
if (kind === "skill") item.status = "proposed";
|
|
17792
18069
|
return parseContextItem(item);
|
|
17793
18070
|
}
|
|
@@ -18907,19 +19184,25 @@ function buildRetrospective(run, journal, exportedAt = (/* @__PURE__ */ new Date
|
|
|
18907
19184
|
const byCategory = {};
|
|
18908
19185
|
const byArea = {};
|
|
18909
19186
|
const byClass = {};
|
|
19187
|
+
const byErrorClass = {};
|
|
18910
19188
|
for (const entry of entries) {
|
|
18911
19189
|
increment(byCategory, entry.category);
|
|
18912
19190
|
increment(byArea, entry.area);
|
|
18913
19191
|
increment(byClass, classFromEvidence(entry.evidence));
|
|
19192
|
+
if (entry.category === "error") increment(byErrorClass, classFromEvidence(entry.evidence));
|
|
18914
19193
|
}
|
|
18915
|
-
const recurringErrorClasses = Object.entries(
|
|
19194
|
+
const recurringErrorClasses = Object.entries(byErrorClass).map(([errorClass, count]) => ({ class: errorClass, count })).sort((left, right) => right.count - left.count || left.class.localeCompare(right.class));
|
|
18916
19195
|
const resolvedContradictions = new Set(entries.filter((entry) => entry.category === "decision" || entry.category === "lesson").flatMap((entry) => entry.relatedEntryIds));
|
|
18917
19196
|
const openContradictions = entries.filter((entry) => entry.category === "contradiction" && !resolvedContradictions.has(entry.id)).map((entry) => ({ id: entry.id, summary: entry.summary, area: entry.area }));
|
|
18918
19197
|
const decisions = entries.filter((entry) => entry.category === "decision").map((entry) => ({ id: entry.id, summary: entry.summary, area: entry.area }));
|
|
18919
|
-
const proposedImprovements =
|
|
18920
|
-
|
|
18921
|
-
|
|
18922
|
-
|
|
19198
|
+
const proposedImprovements = rankImprovementSignals(
|
|
19199
|
+
journal.filter((entry) => entry.runId === run.id && (entry.category === "error" || entry.category === "lesson")),
|
|
19200
|
+
/* @__PURE__ */ new Map([[run.id, run]]),
|
|
19201
|
+
12
|
|
19202
|
+
).map((signal) => ({
|
|
19203
|
+
area: signal.area,
|
|
19204
|
+
summary: signal.summary,
|
|
19205
|
+
successMeasure: redactSecrets(`no recurrence of ${signal.key} in the next ${run.definitionId} run`),
|
|
18923
19206
|
status: "proposed"
|
|
18924
19207
|
}));
|
|
18925
19208
|
const evidenceAudit = buildEvidenceAudit(run);
|
|
@@ -20825,7 +21108,7 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
20825
21108
|
"Execute these stages in order:",
|
|
20826
21109
|
stageList,
|
|
20827
21110
|
"",
|
|
20828
|
-
|
|
21111
|
+
`At every stage, record material knowledge with kxm_workflow_record in one of these categories: ${JOURNAL_CATEGORIES.join(", ")}. Pass the stageId the entry belongs to; the hub binds the attempt and, when you omit area, uses the stage's declared area.`,
|
|
20829
21112
|
"Keep repository-local configuration in .kxm/config, logs in .kxm/logs, and durable workflow artifacts in .kxm/assets; never commit runtime logs, state, or secrets.",
|
|
20830
21113
|
"Complete each stage with kxm_workflow_checkpoint. Supply evidence as an object whose keys exactly match the stage's required evidence keys. Unrelated keys never satisfy a requirement. A warning or failure must be corrected and checkpointed again until it passes or the attempt limit is reached.",
|
|
20831
21114
|
"For a peer-evidence requirement, send or fan out with workflowContext containing this run ID, the exact stage ID, requirement key, and current 1-based attempt. At checkpoint, cite only the returned message IDs under evidenceRefs; the hub derives producer and reply provenance.",
|
|
@@ -20853,7 +21136,7 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
20853
21136
|
`Evidence: ${workflowEvidenceStrings(evidence).join(", ") || "none supplied"}`,
|
|
20854
21137
|
"",
|
|
20855
21138
|
nextInstruction,
|
|
20856
|
-
"Review the run with kxm_workflow_get and keep recording material
|
|
21139
|
+
"Review the run with kxm_workflow_get and keep recording material learning with kxm_workflow_record (any of its ten categories; pass stageId for stage-bound entries).",
|
|
20857
21140
|
"Do not claim the workflow is complete until the checkpoint response reports completed=true."
|
|
20858
21141
|
].join("\n"), "workflow resume prompt", { max: MAX_CONTENT_CHARS });
|
|
20859
21142
|
const seq = store.nextAgentSequence(run.targetAgentId);
|
|
@@ -20907,7 +21190,8 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
20907
21190
|
summary: `External workflow signal timed out: ${waiting.signalKey}`,
|
|
20908
21191
|
evidence: [`wait-created:${waiting.createdAt}`, `wait-expired:${waiting.expiresAt}`],
|
|
20909
21192
|
relatedEntryIds: [],
|
|
20910
|
-
createdAt: timestamp
|
|
21193
|
+
createdAt: timestamp,
|
|
21194
|
+
...stage ? { stageId: stage.id, attempt: stage.attempts + 1 } : {}
|
|
20911
21195
|
};
|
|
20912
21196
|
const definition = webhookWorkflows2.get(transition.definitionId);
|
|
20913
21197
|
const ttlMs = parseBoundedInteger(
|
|
@@ -20986,6 +21270,7 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
20986
21270
|
(candidate) => candidate.messageId === message.id && candidate.status === "running"
|
|
20987
21271
|
);
|
|
20988
21272
|
if (run) {
|
|
21273
|
+
const expiredStage = run.stages.find((candidate) => candidate.id === run.currentStage);
|
|
20989
21274
|
run.status = "failed";
|
|
20990
21275
|
delete run.currentStage;
|
|
20991
21276
|
run.updatedAt = nowIso();
|
|
@@ -21001,7 +21286,8 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
21001
21286
|
summary: "Workflow coordinator prompt expired before completion",
|
|
21002
21287
|
evidence: [`message:${message.id}`],
|
|
21003
21288
|
relatedEntryIds: [],
|
|
21004
|
-
createdAt: run.updatedAt
|
|
21289
|
+
createdAt: run.updatedAt,
|
|
21290
|
+
...expiredStage ? { stageId: expiredStage.id, attempt: expiredStage.attempts + 1 } : {}
|
|
21005
21291
|
};
|
|
21006
21292
|
store.saveJournalEntry(entry);
|
|
21007
21293
|
counters.journalEntries += 1;
|
|
@@ -21237,7 +21523,9 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
21237
21523
|
summary: `${stage.label}: ${summary}`,
|
|
21238
21524
|
evidence: workflowEvidenceStrings(evidence),
|
|
21239
21525
|
relatedEntryIds: [],
|
|
21240
|
-
createdAt: receivedAt
|
|
21526
|
+
createdAt: receivedAt,
|
|
21527
|
+
stageId: stage.id,
|
|
21528
|
+
attempt: stage.attempts
|
|
21241
21529
|
};
|
|
21242
21530
|
} else if (result.degraded) {
|
|
21243
21531
|
const stage = transition.stages.find((candidate) => candidate.id === result.stageId);
|
|
@@ -21254,7 +21542,9 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
21254
21542
|
...(stage.degradedRequirements ?? []).map((requirement) => `requirement:${requirement}`)
|
|
21255
21543
|
],
|
|
21256
21544
|
relatedEntryIds: [],
|
|
21257
|
-
createdAt: receivedAt
|
|
21545
|
+
createdAt: receivedAt,
|
|
21546
|
+
stageId: stage.id,
|
|
21547
|
+
attempt: stage.attempts
|
|
21258
21548
|
};
|
|
21259
21549
|
}
|
|
21260
21550
|
if (transition.status === "running") {
|
|
@@ -21522,7 +21812,9 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
21522
21812
|
`approved-min:${result.approval.approvedMinProducers}`
|
|
21523
21813
|
],
|
|
21524
21814
|
relatedEntryIds: [],
|
|
21525
|
-
createdAt: timestamp
|
|
21815
|
+
createdAt: timestamp,
|
|
21816
|
+
stageId,
|
|
21817
|
+
attempt: result.approval.attempt
|
|
21526
21818
|
};
|
|
21527
21819
|
store.saveWorkflowTransition(transition, void 0, entry);
|
|
21528
21820
|
publishOps(transition.project, "workflows");
|
|
@@ -21555,9 +21847,16 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
21555
21847
|
...skillLifecycle ? { skillLifecycle } : {}
|
|
21556
21848
|
});
|
|
21557
21849
|
counters.contextRequests += 1;
|
|
21850
|
+
const assembledRequest = outcome.audit.request;
|
|
21558
21851
|
logger({
|
|
21559
21852
|
event: "context_packet_assembled",
|
|
21560
|
-
|
|
21853
|
+
project: assembledRequest.project,
|
|
21854
|
+
role: assembledRequest.role,
|
|
21855
|
+
...assembledRequest.workflowRunId !== void 0 ? { workflowRunId: assembledRequest.workflowRunId } : {},
|
|
21856
|
+
...assembledRequest.stageId !== void 0 ? { stageId: assembledRequest.stageId } : {},
|
|
21857
|
+
taskChars: assembledRequest.task.length,
|
|
21858
|
+
taskTokens: outcome.audit.relevance.taskTokens,
|
|
21859
|
+
matchedCandidates: outcome.audit.relevance.matchedCandidates,
|
|
21561
21860
|
selectedIds: outcome.audit.selectedIds,
|
|
21562
21861
|
provenanceSummary: outcome.audit.provenanceSummary,
|
|
21563
21862
|
estimatedTokens: outcome.audit.estimatedTokens,
|
|
@@ -21572,14 +21871,25 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
21572
21871
|
if (method === "POST" && contextRecallMatch) {
|
|
21573
21872
|
const body = await readJson(request);
|
|
21574
21873
|
const { project: callerProject, caller: callerId } = contextCallerProject(request, body.project);
|
|
21575
|
-
const query = requireString(body.query ?? "", "query", { max: 500, allowEmpty: true })
|
|
21874
|
+
const query = requireString(body.query ?? "", "query", { max: 500, allowEmpty: true });
|
|
21576
21875
|
const kinds = Array.isArray(body.kinds) ? body.kinds.filter((kind) => typeof kind === "string") : void 0;
|
|
21577
21876
|
const limit = parseBoundedInteger(body.limit, "limit", 25, 1, 100);
|
|
21578
21877
|
const { pool } = projectContextPool2(callerProject);
|
|
21579
|
-
const
|
|
21878
|
+
const live = pool.filter((item) => item.status !== "superseded" && item.status !== "rejected").filter((item) => kinds === void 0 || kinds.includes(item.kind));
|
|
21879
|
+
const recalled = rankRecall(query, live, limit);
|
|
21580
21880
|
counters.contextRequests += 1;
|
|
21581
|
-
logger({
|
|
21582
|
-
|
|
21881
|
+
logger({
|
|
21882
|
+
event: "context_recall",
|
|
21883
|
+
project: callerProject,
|
|
21884
|
+
queryChars: query.length,
|
|
21885
|
+
queryTokens: new Set(relevanceTokens(query)).size,
|
|
21886
|
+
limit,
|
|
21887
|
+
results: recalled.length
|
|
21888
|
+
});
|
|
21889
|
+
json(response, 200, {
|
|
21890
|
+
items: recalled.map(({ item, relevance }) => ({ ...contextItemAuditMetadata(item), relevance })),
|
|
21891
|
+
unresolvedGaps: recalled.length === 0 ? ["no matching context records"] : []
|
|
21892
|
+
});
|
|
21583
21893
|
return;
|
|
21584
21894
|
}
|
|
21585
21895
|
const contextStateMatch = url.pathname.match(/^\/v1\/context\/state$/);
|
|
@@ -21715,7 +22025,11 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
21715
22025
|
nowIso()
|
|
21716
22026
|
);
|
|
21717
22027
|
store.saveJournalEntry(updated);
|
|
21718
|
-
|
|
22028
|
+
const promotedRun = workflowRuns.get(entry.runId);
|
|
22029
|
+
if (promotedRun) {
|
|
22030
|
+
publishOps(promotedRun.project, "workflows");
|
|
22031
|
+
exportTerminalRetrospective(promotedRun);
|
|
22032
|
+
}
|
|
21719
22033
|
logger({
|
|
21720
22034
|
event: "journal_promotion_recorded",
|
|
21721
22035
|
journalEntryId: entry.id,
|
|
@@ -21841,6 +22155,7 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
21841
22155
|
typeof body.outcome === "string" && body.outcome.trim() ? requireString(body.outcome, "outcome", { max: 64 }) : void 0
|
|
21842
22156
|
);
|
|
21843
22157
|
const checkpointStage = transition.stages.find((candidate) => candidate.id === stageId);
|
|
22158
|
+
const checkpointAttempt = result.transition?.attempt ?? checkpointStage.attempts;
|
|
21844
22159
|
if (result.transition) {
|
|
21845
22160
|
const transitionEntry = {
|
|
21846
22161
|
id: newId("journal"),
|
|
@@ -21852,7 +22167,9 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
21852
22167
|
summary: `typed transition ${result.transition.fromStage} -> ${result.transition.toStage} (${result.transition.outcome})`,
|
|
21853
22168
|
evidence: result.transition.evidenceKeys.map((key) => `requirement:${key}`),
|
|
21854
22169
|
relatedEntryIds: [],
|
|
21855
|
-
createdAt: timestamp
|
|
22170
|
+
createdAt: timestamp,
|
|
22171
|
+
stageId,
|
|
22172
|
+
attempt: checkpointAttempt
|
|
21856
22173
|
};
|
|
21857
22174
|
store.saveWorkflowTransition(transition, void 0, transitionEntry);
|
|
21858
22175
|
counters.journalEntries += 1;
|
|
@@ -21868,7 +22185,9 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
21868
22185
|
summary: `transition budget exhausted at ${stageId} (outcome ${body.outcome ?? status}); run failed safely`,
|
|
21869
22186
|
evidence: [`class:transition_budget_exhausted`, `stage:${stageId}`],
|
|
21870
22187
|
relatedEntryIds: [],
|
|
21871
|
-
createdAt: timestamp
|
|
22188
|
+
createdAt: timestamp,
|
|
22189
|
+
stageId,
|
|
22190
|
+
attempt: checkpointAttempt
|
|
21872
22191
|
};
|
|
21873
22192
|
store.saveWorkflowTransition(transition, void 0, exhaustEntry);
|
|
21874
22193
|
counters.journalEntries += 1;
|
|
@@ -21885,7 +22204,9 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
21885
22204
|
summary: `${checkpointStage.label}: ${summary}`,
|
|
21886
22205
|
evidence: workflowEvidenceStrings(evidence),
|
|
21887
22206
|
relatedEntryIds: [],
|
|
21888
|
-
createdAt: transition.updatedAt
|
|
22207
|
+
createdAt: transition.updatedAt,
|
|
22208
|
+
stageId,
|
|
22209
|
+
attempt: checkpointAttempt
|
|
21889
22210
|
};
|
|
21890
22211
|
} else if (result.degraded) {
|
|
21891
22212
|
entry = {
|
|
@@ -21901,7 +22222,9 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
21901
22222
|
...(checkpointStage.degradedRequirements ?? []).map((requirement) => `requirement:${requirement}`)
|
|
21902
22223
|
],
|
|
21903
22224
|
relatedEntryIds: [],
|
|
21904
|
-
createdAt: transition.updatedAt
|
|
22225
|
+
createdAt: transition.updatedAt,
|
|
22226
|
+
stageId,
|
|
22227
|
+
attempt: checkpointAttempt
|
|
21905
22228
|
};
|
|
21906
22229
|
}
|
|
21907
22230
|
store.saveWorkflowTransition(transition, void 0, entry);
|
|
@@ -21942,10 +22265,23 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
21942
22265
|
);
|
|
21943
22266
|
}
|
|
21944
22267
|
const category = parseJournalCategory(body.category);
|
|
21945
|
-
|
|
21946
|
-
if (
|
|
21947
|
-
|
|
22268
|
+
let stage;
|
|
22269
|
+
if (body.stageId !== void 0 && body.stageId !== null) {
|
|
22270
|
+
const requestedStageId = requireString(body.stageId, "stageId", { max: 128 });
|
|
22271
|
+
stage = run.stages.find((candidate) => candidate.id === requestedStageId);
|
|
22272
|
+
if (!stage) {
|
|
22273
|
+
throw new ProtocolError(400, `stageId ${requestedStageId} is not part of this workflow run`, "invalid_journal_relation");
|
|
22274
|
+
}
|
|
21948
22275
|
}
|
|
22276
|
+
const area = body.area == null ? stage?.area : requireString(body.area, "area", { max: 24 });
|
|
22277
|
+
if (area === void 0 || !IMPROVEMENT_AREAS.includes(area)) {
|
|
22278
|
+
throw new ProtocolError(
|
|
22279
|
+
400,
|
|
22280
|
+
"area is required unless stageId names a stage that declares an area",
|
|
22281
|
+
"invalid_improvement_area"
|
|
22282
|
+
);
|
|
22283
|
+
}
|
|
22284
|
+
const attempt = stage ? journalAttemptFor(stage) : void 0;
|
|
21949
22285
|
const severity = requireString(body.severity ?? "info", "severity", { max: 16 });
|
|
21950
22286
|
if (severity !== "info" && severity !== "warning" && severity !== "error") {
|
|
21951
22287
|
throw new ProtocolError(400, "severity must be info, warning, or error", "invalid_journal_severity");
|
|
@@ -21966,16 +22302,6 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
21966
22302
|
"journal_evidence_required"
|
|
21967
22303
|
);
|
|
21968
22304
|
}
|
|
21969
|
-
let stageId;
|
|
21970
|
-
let attempt;
|
|
21971
|
-
if (body.stageId !== void 0 && body.stageId !== null) {
|
|
21972
|
-
stageId = requireString(body.stageId, "stageId", { max: 128 });
|
|
21973
|
-
const stage = run.stages.find((candidate) => candidate.id === stageId);
|
|
21974
|
-
if (!stage) {
|
|
21975
|
-
throw new ProtocolError(400, `stageId ${stageId} is not part of this workflow run`, "invalid_journal_relation");
|
|
21976
|
-
}
|
|
21977
|
-
attempt = stage.attempts + 1;
|
|
21978
|
-
}
|
|
21979
22305
|
const entry = {
|
|
21980
22306
|
id: newId("journal"),
|
|
21981
22307
|
runId: run.id,
|
|
@@ -21988,11 +22314,12 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
21988
22314
|
evidence,
|
|
21989
22315
|
relatedEntryIds,
|
|
21990
22316
|
createdAt: nowIso(),
|
|
21991
|
-
...
|
|
22317
|
+
...stage !== void 0 ? { stageId: stage.id } : {},
|
|
21992
22318
|
...attempt !== void 0 ? { attempt } : {}
|
|
21993
22319
|
};
|
|
21994
22320
|
store.saveJournalEntry(entry);
|
|
21995
22321
|
counters.journalEntries += 1;
|
|
22322
|
+
exportTerminalRetrospective(run);
|
|
21996
22323
|
logger({
|
|
21997
22324
|
event: "workflow_journal_recorded",
|
|
21998
22325
|
workflowRunId: run.id,
|
|
@@ -22007,11 +22334,16 @@ data: ${JSON.stringify({ type: "ops", project, topic: "agents", at: nowIso() })}
|
|
|
22007
22334
|
if (method === "GET" && url.pathname === "/v1/improvements") {
|
|
22008
22335
|
const agent = requireAgent(request);
|
|
22009
22336
|
requireProjectAuth(request, agent.project);
|
|
22010
|
-
const
|
|
22011
|
-
[...workflowRuns.values()].filter((run) => run.project === agent.project).map((run) => run.id)
|
|
22337
|
+
const projectRuns = new Map(
|
|
22338
|
+
[...workflowRuns.values()].filter((run) => run.project === agent.project).map((run) => [run.id, run])
|
|
22012
22339
|
);
|
|
22340
|
+
const visibleRuns = new Set(projectRuns.keys());
|
|
22013
22341
|
const entries = [...journal.values()].filter((entry) => visibleRuns.has(entry.runId));
|
|
22014
|
-
json(response, 200, {
|
|
22342
|
+
json(response, 200, {
|
|
22343
|
+
reports: improvementReport(entries),
|
|
22344
|
+
signals: rankImprovementSignals(entries, projectRuns),
|
|
22345
|
+
entries: entries.length
|
|
22346
|
+
});
|
|
22015
22347
|
return;
|
|
22016
22348
|
}
|
|
22017
22349
|
if (method === "POST" && url.pathname === "/v1/agents/register") {
|
|
@@ -22429,6 +22761,7 @@ data: ${JSON.stringify({ agent: publicAgent(current, staleAfterMs) })}
|
|
|
22429
22761
|
publishOps(message.project, "messages");
|
|
22430
22762
|
const workflowRun = [...workflowRuns.values()].find((run) => run.messageId === message.id);
|
|
22431
22763
|
if (workflowRun?.status === "running") {
|
|
22764
|
+
const settledStage = workflowRun.stages.find((candidate) => candidate.id === workflowRun.currentStage);
|
|
22432
22765
|
workflowRun.status = "failed";
|
|
22433
22766
|
delete workflowRun.currentStage;
|
|
22434
22767
|
workflowRun.updatedAt = message.repliedAt;
|
|
@@ -22444,7 +22777,8 @@ data: ${JSON.stringify({ agent: publicAgent(current, staleAfterMs) })}
|
|
|
22444
22777
|
summary: "Coordinator settled before all required workflow checkpoints passed",
|
|
22445
22778
|
evidence: [`message:${message.id}`],
|
|
22446
22779
|
relatedEntryIds: [],
|
|
22447
|
-
createdAt: message.repliedAt
|
|
22780
|
+
createdAt: message.repliedAt,
|
|
22781
|
+
...settledStage ? { stageId: settledStage.id, attempt: settledStage.attempts + 1 } : {}
|
|
22448
22782
|
};
|
|
22449
22783
|
store.saveJournalEntry(entry);
|
|
22450
22784
|
counters.journalEntries += 1;
|