@goondocks/myco 0.15.1 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/{agent-run-T433ENJS.js → agent-run-LOZGH6P5.js} +8 -8
- package/dist/{agent-tasks-TAIU3V5I.js → agent-tasks-SNQ55FUN.js} +8 -8
- package/dist/{chunk-TRA3R4EC.js → chunk-34NHDRWI.js} +1 -3
- package/dist/chunk-34NHDRWI.js.map +1 -0
- package/dist/{chunk-6GG2IVNV.js → chunk-35FAQCKJ.js} +4 -4
- package/dist/{chunk-LF5Z62X6.js → chunk-4JVHWBZF.js} +2 -2
- package/dist/{chunk-GDCSPMH4.js → chunk-6JZEAOLG.js} +3 -3
- package/dist/{chunk-2QMDRZPJ.js → chunk-C3GNF7RJ.js} +5 -5
- package/dist/{chunk-DJQOYEK3.js → chunk-CJ2KTRWI.js} +52 -4
- package/dist/chunk-CJ2KTRWI.js.map +1 -0
- package/dist/{chunk-3MEOYXOW.js → chunk-D2NTFSVO.js} +3 -3
- package/dist/{chunk-75J2BR4P.js → chunk-D63XTGBV.js} +580 -415
- package/dist/chunk-D63XTGBV.js.map +1 -0
- package/dist/{chunk-5YQ6VOFZ.js → chunk-DZWSHCAC.js} +2 -2
- package/dist/{chunk-CUADDHHU.js → chunk-E7NUADTQ.js} +9 -1
- package/dist/chunk-E7NUADTQ.js.map +1 -0
- package/dist/{chunk-4O3QNM5I.js → chunk-G42PTGMC.js} +3 -3
- package/dist/{chunk-SV6UCB2Z.js → chunk-GSKXOCFG.js} +3 -1
- package/dist/chunk-GSKXOCFG.js.map +1 -0
- package/dist/{chunk-GCCBXCHF.js → chunk-HA7HO2X2.js} +3 -3
- package/dist/{chunk-RBFECYNA.js → chunk-I3S6L7QC.js} +2 -2
- package/dist/{chunk-SPJGJEFV.js → chunk-IRSNOBGD.js} +2 -2
- package/dist/{chunk-GYIA6XLB.js → chunk-MVBCON4D.js} +2 -2
- package/dist/chunk-MVBCON4D.js.map +1 -0
- package/dist/{chunk-Z7TZJ2SP.js → chunk-P6C6ADBU.js} +2 -2
- package/dist/{chunk-OKCSSDFC.js → chunk-RPILIIYT.js} +2 -2
- package/dist/{chunk-23FJUKCN.js → chunk-SGYYOTNM.js} +10 -4
- package/dist/{chunk-23FJUKCN.js.map → chunk-SGYYOTNM.js.map} +1 -1
- package/dist/{chunk-X5IXK5KO.js → chunk-TIAYBVSI.js} +153 -18
- package/dist/chunk-TIAYBVSI.js.map +1 -0
- package/dist/{chunk-TQO4PF5K.js → chunk-TPTF3R77.js} +4 -4
- package/dist/chunk-U5EW2VIQ.js +86 -0
- package/dist/chunk-U5EW2VIQ.js.map +1 -0
- package/dist/{chunk-DTE3SHYK.js → chunk-UILSK6DK.js} +2 -2
- package/dist/{chunk-EYMKBNRP.js → chunk-V2ZBYKDU.js} +3 -3
- package/dist/{chunk-BFM6AM6R.js → chunk-VTSNXAW7.js} +2 -2
- package/dist/{chunk-HHZ3RTEI.js → chunk-VWXDSDJU.js} +2 -2
- package/dist/{chunk-X4XFJG6I.js → chunk-W7ZOOZMK.js} +3 -3
- package/dist/{chunk-4BQ5QE76.js → chunk-XAXQ72L3.js} +9 -2
- package/dist/chunk-XAXQ72L3.js.map +1 -0
- package/dist/{cli-W37MRZHD.js → cli-W66XNY2H.js} +42 -42
- package/dist/{client-YNTTC75R.js → client-IZM3GJMT.js} +5 -5
- package/dist/{config-MOWCOJJ4.js → config-VHHCGE4F.js} +4 -4
- package/dist/{detect-GFYKKHLJ.js → detect-6FNYONJF.js} +2 -2
- package/dist/{detect-providers-EU35RUL3.js → detect-providers-R7QOB3H6.js} +5 -5
- package/dist/{doctor-PAAQU5AS.js → doctor-WLV7QNRP.js} +13 -13
- package/dist/{executor-4OXDK4ZA.js → executor-PUKL76TU.js} +495 -157
- package/dist/executor-PUKL76TU.js.map +1 -0
- package/dist/{init-PHQAQANR.js → init-5TB6A2RV.js} +18 -18
- package/dist/{init-wizard-RFD46XAJ.js → init-wizard-2Y52AX2A.js} +8 -8
- package/dist/{installer-BTUNKWOU.js → installer-AARSFXI6.js} +2 -2
- package/dist/llm-LS7U7BHC.js +17 -0
- package/dist/{loader-WGDVRGLM.js → loader-QDWQTBX4.js} +4 -4
- package/dist/{loader-WC4U5NM5.js → loader-YQDG5GI5.js} +4 -4
- package/dist/{logs-WFBX2I7C.js → logs-TMKNLSJY.js} +3 -3
- package/dist/{main-ADLCOYKM.js → main-3ZXFA56L.js} +608 -214
- package/dist/main-3ZXFA56L.js.map +1 -0
- package/dist/{open-3VPUP3HD.js → open-ZOZLFBEA.js} +8 -8
- package/dist/{openai-embeddings-SEIV7AM3.js → openai-embeddings-FUW6CSN2.js} +5 -5
- package/dist/{openrouter-ELODIZRP.js → openrouter-YSIUSUQL.js} +5 -5
- package/dist/{post-compact-5NYLOC46.js → post-compact-QSID23PX.js} +8 -8
- package/dist/{post-tool-use-SNNXSZ5Y.js → post-tool-use-4MTNNDTO.js} +7 -7
- package/dist/{post-tool-use-failure-POKVXQHY.js → post-tool-use-failure-WKSMUGJI.js} +8 -8
- package/dist/{pre-compact-ZUICBJEX.js → pre-compact-HFGKNBYV.js} +8 -8
- package/dist/{provider-check-B66E5PWS.js → provider-check-VEYONGNU.js} +5 -5
- package/dist/{registry-DHWVHXWY.js → registry-5R3DLJQH.js} +5 -5
- package/dist/{remove-SVU2V4Q7.js → remove-S5G6VGVT.js} +10 -10
- package/dist/{resolution-events-DBCRVZGU.js → resolution-events-CHOKR35X.js} +5 -5
- package/dist/{restart-NBB5CXJ4.js → restart-A7HJTFFM.js} +9 -9
- package/dist/{search-YUQZFRZX.js → search-HKBGO3QV.js} +9 -9
- package/dist/{server-NBRX56VL.js → server-UAATWKNX.js} +5 -5
- package/dist/{session-2QP4HMZ5.js → session-D2WPQTSR.js} +10 -10
- package/dist/{session-end-NNFBW7CQ.js → session-end-S4RYJG3Y.js} +7 -7
- package/dist/{session-start-NPNP4IXX.js → session-start-4RJ32KS5.js} +12 -12
- package/dist/{setup-llm-C3IGFLRN.js → setup-llm-EZRNEIW2.js} +9 -9
- package/dist/skill-staging-SWM7UC5D.js +25 -0
- package/dist/src/agent/definitions/tasks/full-intelligence.yaml +1 -1
- package/dist/src/agent/definitions/tasks/skill-generate.yaml +55 -21
- package/dist/src/cli.js +1 -1
- package/dist/src/daemon/main.js +1 -1
- package/dist/src/hooks/post-tool-use.js +1 -1
- package/dist/src/hooks/session-end.js +1 -1
- package/dist/src/hooks/session-start.js +1 -1
- package/dist/src/hooks/stop.js +1 -1
- package/dist/src/hooks/user-prompt-submit.js +1 -1
- package/dist/src/mcp/server.js +1 -1
- package/dist/src/symbionts/manifests/codex.yaml +1 -0
- package/dist/src/symbionts/templates/codex/settings.json +5 -0
- package/dist/src/worker/src/schema.ts +16 -0
- package/dist/{stats-FEEXIRMS.js → stats-PW7OGIBR.js} +10 -10
- package/dist/{stop-FGDGWXTK.js → stop-FVMLTWXU.js} +7 -7
- package/dist/{stop-failure-5YAGH2TQ.js → stop-failure-MD52CGFX.js} +8 -8
- package/dist/{subagent-start-UCKVJDR4.js → subagent-start-ZJZA7EAJ.js} +8 -8
- package/dist/{subagent-stop-H25B3QEC.js → subagent-stop-56H22JSE.js} +8 -8
- package/dist/{task-completed-2JGZN2CF.js → task-completed-TQ4WW2HW.js} +8 -8
- package/dist/{team-TG5WZXWO.js → team-RXYFXGUT.js} +6 -6
- package/dist/ui/assets/index-Bjv_ck3c.css +1 -0
- package/dist/ui/assets/index-RYHXSJv1.js +842 -0
- package/dist/ui/index.html +2 -2
- package/dist/{update-EG3N2EXI.js → update-HIJUK5ZK.js} +10 -10
- package/dist/{user-prompt-submit-7FFQ3ORA.js → user-prompt-submit-SLQNTQFL.js} +7 -7
- package/dist/{verify-2M3DYHEY.js → verify-ITBMLK67.js} +9 -9
- package/dist/{version-JUQU5W22.js → version-4YUDRPU4.js} +2 -2
- package/dist/version-4YUDRPU4.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-4BQ5QE76.js.map +0 -1
- package/dist/chunk-75J2BR4P.js.map +0 -1
- package/dist/chunk-CUADDHHU.js.map +0 -1
- package/dist/chunk-DJQOYEK3.js.map +0 -1
- package/dist/chunk-GYIA6XLB.js.map +0 -1
- package/dist/chunk-SV6UCB2Z.js.map +0 -1
- package/dist/chunk-TRA3R4EC.js.map +0 -1
- package/dist/chunk-X5IXK5KO.js.map +0 -1
- package/dist/executor-4OXDK4ZA.js.map +0 -1
- package/dist/llm-D4VWYUK7.js +0 -17
- package/dist/main-ADLCOYKM.js.map +0 -1
- package/dist/ui/assets/index-7Vimyg7g.js +0 -837
- package/dist/ui/assets/index-DlEQ8A8Y.css +0 -1
- /package/dist/{agent-run-T433ENJS.js.map → agent-run-LOZGH6P5.js.map} +0 -0
- /package/dist/{agent-tasks-TAIU3V5I.js.map → agent-tasks-SNQ55FUN.js.map} +0 -0
- /package/dist/{chunk-6GG2IVNV.js.map → chunk-35FAQCKJ.js.map} +0 -0
- /package/dist/{chunk-LF5Z62X6.js.map → chunk-4JVHWBZF.js.map} +0 -0
- /package/dist/{chunk-GDCSPMH4.js.map → chunk-6JZEAOLG.js.map} +0 -0
- /package/dist/{chunk-2QMDRZPJ.js.map → chunk-C3GNF7RJ.js.map} +0 -0
- /package/dist/{chunk-3MEOYXOW.js.map → chunk-D2NTFSVO.js.map} +0 -0
- /package/dist/{chunk-5YQ6VOFZ.js.map → chunk-DZWSHCAC.js.map} +0 -0
- /package/dist/{chunk-4O3QNM5I.js.map → chunk-G42PTGMC.js.map} +0 -0
- /package/dist/{chunk-GCCBXCHF.js.map → chunk-HA7HO2X2.js.map} +0 -0
- /package/dist/{chunk-RBFECYNA.js.map → chunk-I3S6L7QC.js.map} +0 -0
- /package/dist/{chunk-SPJGJEFV.js.map → chunk-IRSNOBGD.js.map} +0 -0
- /package/dist/{chunk-Z7TZJ2SP.js.map → chunk-P6C6ADBU.js.map} +0 -0
- /package/dist/{chunk-OKCSSDFC.js.map → chunk-RPILIIYT.js.map} +0 -0
- /package/dist/{chunk-TQO4PF5K.js.map → chunk-TPTF3R77.js.map} +0 -0
- /package/dist/{chunk-DTE3SHYK.js.map → chunk-UILSK6DK.js.map} +0 -0
- /package/dist/{chunk-EYMKBNRP.js.map → chunk-V2ZBYKDU.js.map} +0 -0
- /package/dist/{chunk-BFM6AM6R.js.map → chunk-VTSNXAW7.js.map} +0 -0
- /package/dist/{chunk-HHZ3RTEI.js.map → chunk-VWXDSDJU.js.map} +0 -0
- /package/dist/{chunk-X4XFJG6I.js.map → chunk-W7ZOOZMK.js.map} +0 -0
- /package/dist/{cli-W37MRZHD.js.map → cli-W66XNY2H.js.map} +0 -0
- /package/dist/{client-YNTTC75R.js.map → client-IZM3GJMT.js.map} +0 -0
- /package/dist/{config-MOWCOJJ4.js.map → config-VHHCGE4F.js.map} +0 -0
- /package/dist/{detect-GFYKKHLJ.js.map → detect-6FNYONJF.js.map} +0 -0
- /package/dist/{detect-providers-EU35RUL3.js.map → detect-providers-R7QOB3H6.js.map} +0 -0
- /package/dist/{doctor-PAAQU5AS.js.map → doctor-WLV7QNRP.js.map} +0 -0
- /package/dist/{init-PHQAQANR.js.map → init-5TB6A2RV.js.map} +0 -0
- /package/dist/{init-wizard-RFD46XAJ.js.map → init-wizard-2Y52AX2A.js.map} +0 -0
- /package/dist/{installer-BTUNKWOU.js.map → installer-AARSFXI6.js.map} +0 -0
- /package/dist/{llm-D4VWYUK7.js.map → llm-LS7U7BHC.js.map} +0 -0
- /package/dist/{loader-WC4U5NM5.js.map → loader-QDWQTBX4.js.map} +0 -0
- /package/dist/{loader-WGDVRGLM.js.map → loader-YQDG5GI5.js.map} +0 -0
- /package/dist/{logs-WFBX2I7C.js.map → logs-TMKNLSJY.js.map} +0 -0
- /package/dist/{open-3VPUP3HD.js.map → open-ZOZLFBEA.js.map} +0 -0
- /package/dist/{openai-embeddings-SEIV7AM3.js.map → openai-embeddings-FUW6CSN2.js.map} +0 -0
- /package/dist/{openrouter-ELODIZRP.js.map → openrouter-YSIUSUQL.js.map} +0 -0
- /package/dist/{post-compact-5NYLOC46.js.map → post-compact-QSID23PX.js.map} +0 -0
- /package/dist/{post-tool-use-SNNXSZ5Y.js.map → post-tool-use-4MTNNDTO.js.map} +0 -0
- /package/dist/{post-tool-use-failure-POKVXQHY.js.map → post-tool-use-failure-WKSMUGJI.js.map} +0 -0
- /package/dist/{pre-compact-ZUICBJEX.js.map → pre-compact-HFGKNBYV.js.map} +0 -0
- /package/dist/{provider-check-B66E5PWS.js.map → provider-check-VEYONGNU.js.map} +0 -0
- /package/dist/{registry-DHWVHXWY.js.map → registry-5R3DLJQH.js.map} +0 -0
- /package/dist/{remove-SVU2V4Q7.js.map → remove-S5G6VGVT.js.map} +0 -0
- /package/dist/{resolution-events-DBCRVZGU.js.map → resolution-events-CHOKR35X.js.map} +0 -0
- /package/dist/{restart-NBB5CXJ4.js.map → restart-A7HJTFFM.js.map} +0 -0
- /package/dist/{search-YUQZFRZX.js.map → search-HKBGO3QV.js.map} +0 -0
- /package/dist/{server-NBRX56VL.js.map → server-UAATWKNX.js.map} +0 -0
- /package/dist/{session-2QP4HMZ5.js.map → session-D2WPQTSR.js.map} +0 -0
- /package/dist/{session-end-NNFBW7CQ.js.map → session-end-S4RYJG3Y.js.map} +0 -0
- /package/dist/{session-start-NPNP4IXX.js.map → session-start-4RJ32KS5.js.map} +0 -0
- /package/dist/{setup-llm-C3IGFLRN.js.map → setup-llm-EZRNEIW2.js.map} +0 -0
- /package/dist/{version-JUQU5W22.js.map → skill-staging-SWM7UC5D.js.map} +0 -0
- /package/dist/{stats-FEEXIRMS.js.map → stats-PW7OGIBR.js.map} +0 -0
- /package/dist/{stop-FGDGWXTK.js.map → stop-FVMLTWXU.js.map} +0 -0
- /package/dist/{stop-failure-5YAGH2TQ.js.map → stop-failure-MD52CGFX.js.map} +0 -0
- /package/dist/{subagent-start-UCKVJDR4.js.map → subagent-start-ZJZA7EAJ.js.map} +0 -0
- /package/dist/{subagent-stop-H25B3QEC.js.map → subagent-stop-56H22JSE.js.map} +0 -0
- /package/dist/{task-completed-2JGZN2CF.js.map → task-completed-TQ4WW2HW.js.map} +0 -0
- /package/dist/{team-TG5WZXWO.js.map → team-RXYFXGUT.js.map} +0 -0
- /package/dist/{update-EG3N2EXI.js.map → update-HIJUK5ZK.js.map} +0 -0
- /package/dist/{user-prompt-submit-7FFQ3ORA.js.map → user-prompt-submit-SLQNTQFL.js.map} +0 -0
- /package/dist/{verify-2M3DYHEY.js.map → verify-ITBMLK67.js.map} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
+
SKILL_GENERATE_TASK,
|
|
3
4
|
STATUS_COMPLETED,
|
|
4
5
|
STATUS_FAILED,
|
|
5
6
|
STATUS_RUNNING,
|
|
@@ -30,13 +31,20 @@ import {
|
|
|
30
31
|
updateRunStatus,
|
|
31
32
|
updateSkillRecord,
|
|
32
33
|
upsertDigestExtract
|
|
33
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-D63XTGBV.js";
|
|
34
35
|
import {
|
|
35
36
|
fullTextSearch
|
|
36
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-UILSK6DK.js";
|
|
38
|
+
import {
|
|
39
|
+
cleanupStagedSkill,
|
|
40
|
+
readStagedManifest,
|
|
41
|
+
readStagedSkill,
|
|
42
|
+
writeStagedManifest,
|
|
43
|
+
writeStagedSkill
|
|
44
|
+
} from "./chunk-U5EW2VIQ.js";
|
|
37
45
|
import {
|
|
38
46
|
loadAllTasks
|
|
39
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-D2NTFSVO.js";
|
|
40
48
|
import {
|
|
41
49
|
getAgent,
|
|
42
50
|
getDefaultTask,
|
|
@@ -45,31 +53,33 @@ import {
|
|
|
45
53
|
loadSystemPrompt,
|
|
46
54
|
resolveDefinitionsDir,
|
|
47
55
|
resolveEffectiveConfig
|
|
48
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-W7ZOOZMK.js";
|
|
49
57
|
import {
|
|
50
58
|
insertTurn
|
|
51
59
|
} from "./chunk-QLCD77AN.js";
|
|
52
60
|
import {
|
|
53
61
|
insertResolutionEvent
|
|
54
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-P6C6ADBU.js";
|
|
55
63
|
import "./chunk-IB76KGBY.js";
|
|
56
64
|
import {
|
|
57
65
|
DEFAULT_IMPORTANCE,
|
|
58
66
|
insertSpore,
|
|
59
67
|
listSpores,
|
|
60
68
|
updateSporeStatus
|
|
61
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-IRSNOBGD.js";
|
|
62
70
|
import {
|
|
63
71
|
listSessions,
|
|
64
72
|
updateSession
|
|
65
|
-
} from "./chunk-
|
|
66
|
-
import "./chunk-
|
|
73
|
+
} from "./chunk-SGYYOTNM.js";
|
|
74
|
+
import "./chunk-4JVHWBZF.js";
|
|
67
75
|
import {
|
|
76
|
+
AGENT_SETTABLE_STATUSES,
|
|
77
|
+
CANDIDATE_STATUS,
|
|
68
78
|
createSchema
|
|
69
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-CJ2KTRWI.js";
|
|
70
80
|
import {
|
|
71
81
|
loadConfig
|
|
72
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-XAXQ72L3.js";
|
|
73
83
|
import {
|
|
74
84
|
getDatabase,
|
|
75
85
|
initDatabase,
|
|
@@ -77,7 +87,7 @@ import {
|
|
|
77
87
|
} from "./chunk-MYX5NCRH.js";
|
|
78
88
|
import {
|
|
79
89
|
getPluginVersion
|
|
80
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-VTSNXAW7.js";
|
|
81
91
|
import {
|
|
82
92
|
findPackageRoot
|
|
83
93
|
} from "./chunk-LPUQPDC2.js";
|
|
@@ -89,8 +99,8 @@ import {
|
|
|
89
99
|
SEARCH_SIMILARITY_THRESHOLD,
|
|
90
100
|
TEAM_SOURCE_PREFIX,
|
|
91
101
|
epochSeconds
|
|
92
|
-
} from "./chunk-
|
|
93
|
-
import "./chunk-
|
|
102
|
+
} from "./chunk-34NHDRWI.js";
|
|
103
|
+
import "./chunk-E7NUADTQ.js";
|
|
94
104
|
import "./chunk-D7TYRPRM.js";
|
|
95
105
|
import "./chunk-E4VLWIJC.js";
|
|
96
106
|
import {
|
|
@@ -836,6 +846,200 @@ function validateSkillContent(content, dirName) {
|
|
|
836
846
|
// src/agent/tools/skill-tools.ts
|
|
837
847
|
function createSkillTools(deps) {
|
|
838
848
|
const { agentId, machineId, projectRoot, vaultDir, recordTurn } = deps;
|
|
849
|
+
function findOverlappingCandidate(newTopic, existing) {
|
|
850
|
+
let best = null;
|
|
851
|
+
for (const candidate of existing) {
|
|
852
|
+
const score = descriptionSimilarity(newTopic, candidate.topic);
|
|
853
|
+
if (score >= DESCRIPTION_DUPLICATE_THRESHOLD && (!best || score > best.score)) {
|
|
854
|
+
best = { candidate, score };
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return best;
|
|
858
|
+
}
|
|
859
|
+
function candidateOverlapError(match) {
|
|
860
|
+
const common = `already has an existing candidate with a similar topic: "${match.topic}"`;
|
|
861
|
+
switch (match.status) {
|
|
862
|
+
case CANDIDATE_STATUS.DISMISSED:
|
|
863
|
+
return `Candidate rejected: the vault ${common} that was previously dismissed. Do not re-identify dismissed topics.`;
|
|
864
|
+
case CANDIDATE_STATUS.GENERATED:
|
|
865
|
+
return `Candidate rejected: the vault ${common} that was already fulfilled by a generated skill. Do not re-identify.`;
|
|
866
|
+
case CANDIDATE_STATUS.APPROVED:
|
|
867
|
+
return `Candidate rejected: the vault ${common} that is already queued in approved state. Wait for the generate task to process it.`;
|
|
868
|
+
case CANDIDATE_STATUS.IDENTIFIED:
|
|
869
|
+
return `Candidate rejected: the vault ${common} already in the review queue. Update the existing candidate with new evidence (action: update) instead of creating a duplicate.`;
|
|
870
|
+
default:
|
|
871
|
+
return `Candidate rejected: the vault ${common} in status '${match.status}'.`;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
function requireApprovedCandidate(candidateId) {
|
|
875
|
+
const candidate = getCandidate(candidateId);
|
|
876
|
+
if (!candidate) {
|
|
877
|
+
return {
|
|
878
|
+
error: `Candidate ${candidateId} not found. Skill writes require a candidate in the approved state.`
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
if (candidate.status !== CANDIDATE_STATUS.APPROVED) {
|
|
882
|
+
return {
|
|
883
|
+
error: `Candidate ${candidateId} is in '${candidate.status}' state. Skills can only be generated from candidates in 'approved' state \u2014 the human review step. If a candidate in an earlier state needs to become a skill, route it through the normal approval flow first.`,
|
|
884
|
+
candidate_status: candidate.status
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
return null;
|
|
888
|
+
}
|
|
889
|
+
function checkDedupGates(args) {
|
|
890
|
+
const existingSameName = getSkillRecordByName(args.name);
|
|
891
|
+
if (existingSameName) {
|
|
892
|
+
if (args.rejectSameName) {
|
|
893
|
+
return {
|
|
894
|
+
error: `Skill "${args.name}" already exists. This path is create-only. Use vault_write_skill to evolve the existing skill (it bumps the generation), or mark the current record stale via vault_skill_records first.`,
|
|
895
|
+
existing_skill: {
|
|
896
|
+
id: existingSameName.id,
|
|
897
|
+
name: existingSameName.name,
|
|
898
|
+
path: existingSameName.path
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
if (args.candidate_id) {
|
|
905
|
+
const candidate = getCandidate(args.candidate_id);
|
|
906
|
+
if (candidate?.skill_id) {
|
|
907
|
+
const linkedSkill = getSkillRecord(candidate.skill_id);
|
|
908
|
+
if (linkedSkill && linkedSkill.name !== args.name) {
|
|
909
|
+
return {
|
|
910
|
+
error: `Candidate ${args.candidate_id} is already fulfilled by skill "${linkedSkill.name}". Do not create a sibling skill. If the existing skill needs changes, write to the same name to evolve it (this bumps its generation), or mark it stale via vault_skill_records before replacing.`,
|
|
911
|
+
existing_skill: {
|
|
912
|
+
id: linkedSkill.id,
|
|
913
|
+
name: linkedSkill.name,
|
|
914
|
+
description: linkedSkill.description,
|
|
915
|
+
path: linkedSkill.path
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
const activeSkills = listSkillRecords({ agent_id: agentId, status: "active", limit: 200 });
|
|
922
|
+
let bestMatch = null;
|
|
923
|
+
for (const skill of activeSkills) {
|
|
924
|
+
const score = descriptionSimilarity(args.description, skill.description);
|
|
925
|
+
if (score >= DESCRIPTION_DUPLICATE_THRESHOLD && (!bestMatch || score > bestMatch.score)) {
|
|
926
|
+
bestMatch = { skill, score };
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
if (bestMatch) {
|
|
930
|
+
return {
|
|
931
|
+
error: `Description overlaps with existing active skill "${bestMatch.skill.name}" (Jaccard ${bestMatch.score.toFixed(2)}, threshold ${DESCRIPTION_DUPLICATE_THRESHOLD}). Do not create a duplicate. Either evolve the existing skill by writing to its name ("${bestMatch.skill.name}"), or reframe this skill so its description describes a distinct procedure.`,
|
|
932
|
+
overlapping_skill: {
|
|
933
|
+
id: bestMatch.skill.id,
|
|
934
|
+
name: bestMatch.skill.name,
|
|
935
|
+
description: bestMatch.skill.description,
|
|
936
|
+
path: bestMatch.skill.path
|
|
937
|
+
},
|
|
938
|
+
similarity: bestMatch.score
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
943
|
+
async function promoteNewSkill(params) {
|
|
944
|
+
const root = projectRoot ?? process.cwd();
|
|
945
|
+
const skillDir = resolve(root, ".agents", "skills", params.name);
|
|
946
|
+
const skillPath = resolve(skillDir, "SKILL.md");
|
|
947
|
+
const skillDirPreexisted = existsSync(skillDir);
|
|
948
|
+
async function cleanupCreatedSkillArtifactsOnRollback() {
|
|
949
|
+
try {
|
|
950
|
+
if (!skillDirPreexisted) {
|
|
951
|
+
rmSync(skillDir, { recursive: true, force: true });
|
|
952
|
+
} else {
|
|
953
|
+
rmSync(skillPath, { force: true });
|
|
954
|
+
}
|
|
955
|
+
} catch (rollbackErr) {
|
|
956
|
+
console.warn(
|
|
957
|
+
`[${params.label}] file rollback after DB failure also failed:`,
|
|
958
|
+
rollbackErr instanceof Error ? rollbackErr.message : rollbackErr
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
try {
|
|
962
|
+
const { syncSkillSymlinks } = await import("./installer-AARSFXI6.js");
|
|
963
|
+
syncSkillSymlinks(root, params.name, { remove: true });
|
|
964
|
+
} catch (rollbackErr) {
|
|
965
|
+
console.warn(
|
|
966
|
+
`[${params.label}] symlink rollback after DB failure also failed:`,
|
|
967
|
+
rollbackErr instanceof Error ? rollbackErr.message : rollbackErr
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
try {
|
|
972
|
+
mkdirSync(skillDir, { recursive: true });
|
|
973
|
+
writeFileSync(skillPath, params.content, "utf-8");
|
|
974
|
+
} catch (err) {
|
|
975
|
+
return {
|
|
976
|
+
error: `Failed to write skill file: ${err instanceof Error ? err.message : String(err)}`
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
try {
|
|
980
|
+
const { syncSkillSymlinks } = await import("./installer-AARSFXI6.js");
|
|
981
|
+
syncSkillSymlinks(root, params.name);
|
|
982
|
+
} catch (err) {
|
|
983
|
+
console.warn(
|
|
984
|
+
`[${params.label}] syncSkillSymlinks failed:`,
|
|
985
|
+
err instanceof Error ? err.message : err
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
const now = epochSeconds();
|
|
989
|
+
const relativePath = `.agents/skills/${params.name}/SKILL.md`;
|
|
990
|
+
const recordId = crypto2.randomUUID();
|
|
991
|
+
const generation = 1;
|
|
992
|
+
const txDb = getDatabase();
|
|
993
|
+
try {
|
|
994
|
+
txDb.transaction(() => {
|
|
995
|
+
insertSkillRecord({
|
|
996
|
+
id: recordId,
|
|
997
|
+
agent_id: agentId,
|
|
998
|
+
machine_id: machineId,
|
|
999
|
+
name: params.name,
|
|
1000
|
+
display_name: params.display_name,
|
|
1001
|
+
description: params.description,
|
|
1002
|
+
candidate_id: params.candidate_id ?? null,
|
|
1003
|
+
source_ids: params.source_ids,
|
|
1004
|
+
path: relativePath,
|
|
1005
|
+
created_at: now,
|
|
1006
|
+
updated_at: now
|
|
1007
|
+
});
|
|
1008
|
+
insertLineage({
|
|
1009
|
+
id: crypto2.randomUUID(),
|
|
1010
|
+
skill_id: recordId,
|
|
1011
|
+
generation,
|
|
1012
|
+
action: "created",
|
|
1013
|
+
rationale: params.rationale ?? "Initial skill creation",
|
|
1014
|
+
source_ids_added: params.source_ids,
|
|
1015
|
+
content_snapshot: params.content,
|
|
1016
|
+
created_at: now
|
|
1017
|
+
});
|
|
1018
|
+
params.linkCandidate?.(recordId, now);
|
|
1019
|
+
})();
|
|
1020
|
+
} catch (err) {
|
|
1021
|
+
await cleanupCreatedSkillArtifactsOnRollback();
|
|
1022
|
+
return {
|
|
1023
|
+
error: `Skill write aborted: database transaction failed and on-disk state was rolled back. ${err instanceof Error ? err.message : String(err)}`
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
return {
|
|
1027
|
+
id: recordId,
|
|
1028
|
+
name: params.name,
|
|
1029
|
+
path: relativePath,
|
|
1030
|
+
generation
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
function emitSkillNotification(kind, opts) {
|
|
1034
|
+
notify(vaultDir, {
|
|
1035
|
+
domain: "skills",
|
|
1036
|
+
type: kind === "created" ? "skill.created" : "skill.evolved",
|
|
1037
|
+
title: `Skill ${kind}: ${opts.display_name}`,
|
|
1038
|
+
message: opts.description.slice(0, 120),
|
|
1039
|
+
link: `/skills?skill=${encodeURIComponent(opts.name)}`,
|
|
1040
|
+
metadata: { skillId: opts.recordId, name: opts.name, generation: opts.generation }
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
839
1043
|
const vaultSkillCandidates = tool4(
|
|
840
1044
|
"vault_skill_candidates",
|
|
841
1045
|
"Manage skill candidates (identified topics that may become skills). Supports list, get, create, and update actions.",
|
|
@@ -845,7 +1049,9 @@ function createSkillTools(deps) {
|
|
|
845
1049
|
topic: external_exports.string().optional().describe("Skill topic (required for create)"),
|
|
846
1050
|
rationale: external_exports.string().optional().describe("Why this should be a skill (required for create)"),
|
|
847
1051
|
confidence: external_exports.number().optional().describe("Confidence score 0-1"),
|
|
848
|
-
status: external_exports.enum(
|
|
1052
|
+
status: external_exports.enum(AGENT_SETTABLE_STATUSES).optional().describe(
|
|
1053
|
+
"Candidate status \u2014 agent-settable values only. 'identified' is the initial state; 'dismissed' retires a candidate. 'approved' and 'generated' are lifecycle transitions owned by the human UI and vault_finalize_skill respectively."
|
|
1054
|
+
),
|
|
849
1055
|
source_ids: external_exports.string().optional().describe("JSON array of source spore/entity IDs"),
|
|
850
1056
|
skill_id: external_exports.string().optional().describe("Associated skill record ID (after materialization)"),
|
|
851
1057
|
limit: external_exports.number().optional().describe("Maximum candidates to return (for list)")
|
|
@@ -884,6 +1090,19 @@ function createSkillTools(deps) {
|
|
|
884
1090
|
overlapping_skills: overlapping.map((s) => ({ name: s.name, display_name: s.display_name, description: s.description }))
|
|
885
1091
|
});
|
|
886
1092
|
}
|
|
1093
|
+
const allExisting = listCandidates({ agent_id: agentId, limit: 500 });
|
|
1094
|
+
const match = findOverlappingCandidate(args.topic, allExisting);
|
|
1095
|
+
if (match) {
|
|
1096
|
+
return textResult({
|
|
1097
|
+
error: candidateOverlapError(match.candidate),
|
|
1098
|
+
existing_candidate: {
|
|
1099
|
+
id: match.candidate.id,
|
|
1100
|
+
status: match.candidate.status,
|
|
1101
|
+
topic: match.candidate.topic
|
|
1102
|
+
},
|
|
1103
|
+
similarity: match.score
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
887
1106
|
const now = epochSeconds();
|
|
888
1107
|
const candidate = insertCandidate({
|
|
889
1108
|
id: crypto2.randomUUID(),
|
|
@@ -991,7 +1210,7 @@ function createSkillTools(deps) {
|
|
|
991
1210
|
console.warn("[vault_skill_records] Failed to remove skill directory:", err instanceof Error ? err.message : err);
|
|
992
1211
|
}
|
|
993
1212
|
try {
|
|
994
|
-
const { syncSkillSymlinks } = await import("./installer-
|
|
1213
|
+
const { syncSkillSymlinks } = await import("./installer-AARSFXI6.js");
|
|
995
1214
|
syncSkillSymlinks(root, result.name, { remove: true });
|
|
996
1215
|
} catch (err) {
|
|
997
1216
|
console.warn("[vault_skill_records] Failed to remove symlinks:", err instanceof Error ? err.message : err);
|
|
@@ -1032,51 +1251,18 @@ function createSkillTools(deps) {
|
|
|
1032
1251
|
error: 'Invalid skill name: must be a simple directory name without path separators or ".."'
|
|
1033
1252
|
});
|
|
1034
1253
|
}
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
return textResult({
|
|
1044
|
-
error: `Candidate ${args.candidate_id} is already fulfilled by skill "${linkedSkill.name}". Do not create a sibling skill. If the existing skill needs changes, write to the same name to evolve it (this bumps its generation), or mark it stale via vault_skill_records before replacing.`,
|
|
1045
|
-
existing_skill: {
|
|
1046
|
-
id: linkedSkill.id,
|
|
1047
|
-
name: linkedSkill.name,
|
|
1048
|
-
description: linkedSkill.description,
|
|
1049
|
-
path: linkedSkill.path
|
|
1050
|
-
}
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1055
|
-
const activeSkills = listSkillRecords({ agent_id: agentId, status: "active", limit: 200 });
|
|
1056
|
-
let bestMatch = null;
|
|
1057
|
-
for (const skill of activeSkills) {
|
|
1058
|
-
const score = descriptionSimilarity(args.description, skill.description);
|
|
1059
|
-
if (score >= DESCRIPTION_DUPLICATE_THRESHOLD && (!bestMatch || score > bestMatch.score)) {
|
|
1060
|
-
bestMatch = { skill, score };
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
if (bestMatch) {
|
|
1064
|
-
recordTurn("vault_write_skill", args);
|
|
1065
|
-
return textResult({
|
|
1066
|
-
error: `Description overlaps with existing active skill "${bestMatch.skill.name}" (Jaccard ${bestMatch.score.toFixed(2)}, threshold ${DESCRIPTION_DUPLICATE_THRESHOLD}). Do not create a duplicate. Either evolve the existing skill by writing to its name ("${bestMatch.skill.name}"), or reframe this skill so its description describes a distinct procedure.`,
|
|
1067
|
-
overlapping_skill: {
|
|
1068
|
-
id: bestMatch.skill.id,
|
|
1069
|
-
name: bestMatch.skill.name,
|
|
1070
|
-
description: bestMatch.skill.description,
|
|
1071
|
-
path: bestMatch.skill.path
|
|
1072
|
-
},
|
|
1073
|
-
similarity: bestMatch.score
|
|
1074
|
-
});
|
|
1075
|
-
}
|
|
1254
|
+
const dedupError = checkDedupGates({
|
|
1255
|
+
candidate_id: args.candidate_id,
|
|
1256
|
+
name: args.name,
|
|
1257
|
+
description: args.description
|
|
1258
|
+
});
|
|
1259
|
+
if (dedupError) {
|
|
1260
|
+
recordTurn("vault_write_skill", args);
|
|
1261
|
+
return textResult(dedupError);
|
|
1076
1262
|
}
|
|
1263
|
+
const existing = getSkillRecordByName(args.name);
|
|
1077
1264
|
const root = projectRoot ?? process.cwd();
|
|
1078
|
-
const
|
|
1079
|
-
const skillPath = resolve(skillDir, "SKILL.md");
|
|
1265
|
+
const skillPath = resolve(root, ".agents", "skills", args.name, "SKILL.md");
|
|
1080
1266
|
if (existsSync(skillPath)) {
|
|
1081
1267
|
const existingContent = readFileSync(skillPath, "utf-8");
|
|
1082
1268
|
const violations = checkFrontmatterPreservation(existingContent, args.content);
|
|
@@ -1088,114 +1274,95 @@ function createSkillTools(deps) {
|
|
|
1088
1274
|
});
|
|
1089
1275
|
}
|
|
1090
1276
|
}
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1277
|
+
if (!existing) {
|
|
1278
|
+
if (args.candidate_id) {
|
|
1279
|
+
const candidateError = requireApprovedCandidate(args.candidate_id);
|
|
1280
|
+
if (candidateError) {
|
|
1281
|
+
recordTurn("vault_write_skill", args);
|
|
1282
|
+
return textResult(candidateError);
|
|
1283
|
+
}
|
|
1098
1284
|
}
|
|
1285
|
+
const linkCandidate = (recordId2, now2) => {
|
|
1286
|
+
if (!args.candidate_id) return;
|
|
1287
|
+
const exact = updateCandidate(args.candidate_id, {
|
|
1288
|
+
status: CANDIDATE_STATUS.GENERATED,
|
|
1289
|
+
skill_id: recordId2,
|
|
1290
|
+
updated_at: now2
|
|
1291
|
+
});
|
|
1292
|
+
if (exact) return;
|
|
1293
|
+
const approvedCandidates = listCandidates({ status: CANDIDATE_STATUS.APPROVED, limit: 10 });
|
|
1294
|
+
const prefixMatch = approvedCandidates.find((c) => c.id.startsWith(args.candidate_id));
|
|
1295
|
+
if (prefixMatch) {
|
|
1296
|
+
updateCandidate(prefixMatch.id, {
|
|
1297
|
+
status: CANDIDATE_STATUS.GENERATED,
|
|
1298
|
+
skill_id: recordId2,
|
|
1299
|
+
updated_at: now2
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
const result = await promoteNewSkill({
|
|
1304
|
+
name: args.name,
|
|
1305
|
+
display_name: args.display_name,
|
|
1306
|
+
description: args.description,
|
|
1307
|
+
content: args.content,
|
|
1308
|
+
source_ids: args.source_ids,
|
|
1309
|
+
candidate_id: args.candidate_id,
|
|
1310
|
+
rationale: args.rationale,
|
|
1311
|
+
linkCandidate,
|
|
1312
|
+
label: "vault_write_skill"
|
|
1313
|
+
});
|
|
1314
|
+
recordTurn("vault_write_skill", args);
|
|
1315
|
+
if ("error" in result) return textResult(result);
|
|
1316
|
+
emitSkillNotification("created", {
|
|
1317
|
+
name: result.name,
|
|
1318
|
+
display_name: args.display_name,
|
|
1319
|
+
description: args.description,
|
|
1320
|
+
recordId: result.id,
|
|
1321
|
+
generation: result.generation
|
|
1322
|
+
});
|
|
1323
|
+
return textResult(result);
|
|
1099
1324
|
}
|
|
1325
|
+
const priorSkillContent = readFileSync(skillPath, "utf-8");
|
|
1100
1326
|
try {
|
|
1101
|
-
mkdirSync(skillDir, { recursive: true });
|
|
1102
1327
|
writeFileSync(skillPath, args.content, "utf-8");
|
|
1103
1328
|
} catch (err) {
|
|
1104
1329
|
return textResult({ error: `Failed to write skill file: ${err instanceof Error ? err.message : String(err)}` });
|
|
1105
1330
|
}
|
|
1106
1331
|
try {
|
|
1107
|
-
const { syncSkillSymlinks } = await import("./installer-
|
|
1332
|
+
const { syncSkillSymlinks } = await import("./installer-AARSFXI6.js");
|
|
1108
1333
|
syncSkillSymlinks(root, args.name);
|
|
1109
1334
|
} catch (err) {
|
|
1110
1335
|
console.warn("[vault_write_skill] syncSkillSymlinks failed:", err instanceof Error ? err.message : err);
|
|
1111
1336
|
}
|
|
1112
1337
|
const now = epochSeconds();
|
|
1113
1338
|
const relativePath = `.agents/skills/${args.name}/SKILL.md`;
|
|
1114
|
-
const
|
|
1115
|
-
|
|
1116
|
-
let generation = 0;
|
|
1339
|
+
const generation = existing.generation + 1;
|
|
1340
|
+
const recordId = existing.id;
|
|
1117
1341
|
const txDb = getDatabase();
|
|
1118
1342
|
try {
|
|
1119
1343
|
txDb.transaction(() => {
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
content_snapshot: args.content,
|
|
1139
|
-
created_at: now
|
|
1140
|
-
});
|
|
1141
|
-
} else {
|
|
1142
|
-
recordId = crypto2.randomUUID();
|
|
1143
|
-
generation = 1;
|
|
1144
|
-
insertSkillRecord({
|
|
1145
|
-
id: recordId,
|
|
1146
|
-
agent_id: agentId,
|
|
1147
|
-
machine_id: machineId,
|
|
1148
|
-
name: args.name,
|
|
1149
|
-
display_name: args.display_name,
|
|
1150
|
-
description: args.description,
|
|
1151
|
-
candidate_id: args.candidate_id ?? null,
|
|
1152
|
-
source_ids: args.source_ids,
|
|
1153
|
-
path: relativePath,
|
|
1154
|
-
created_at: now,
|
|
1155
|
-
updated_at: now
|
|
1156
|
-
});
|
|
1157
|
-
insertLineage({
|
|
1158
|
-
id: crypto2.randomUUID(),
|
|
1159
|
-
skill_id: recordId,
|
|
1160
|
-
generation,
|
|
1161
|
-
action: "created",
|
|
1162
|
-
rationale: args.rationale ?? "Initial skill creation",
|
|
1163
|
-
source_ids_added: args.source_ids,
|
|
1164
|
-
content_snapshot: args.content,
|
|
1165
|
-
created_at: now
|
|
1166
|
-
});
|
|
1167
|
-
const approvedCandidates = listCandidates({ status: "approved", limit: 10 });
|
|
1168
|
-
let linkedCandidate = false;
|
|
1169
|
-
if (args.candidate_id && !linkedCandidate) {
|
|
1170
|
-
const exact = updateCandidate(args.candidate_id, {
|
|
1171
|
-
status: "generated",
|
|
1172
|
-
skill_id: recordId,
|
|
1173
|
-
updated_at: now
|
|
1174
|
-
});
|
|
1175
|
-
if (exact) linkedCandidate = true;
|
|
1176
|
-
}
|
|
1177
|
-
if (args.candidate_id && !linkedCandidate) {
|
|
1178
|
-
const prefixMatch = approvedCandidates.find((c) => c.id.startsWith(args.candidate_id));
|
|
1179
|
-
if (prefixMatch) {
|
|
1180
|
-
updateCandidate(prefixMatch.id, {
|
|
1181
|
-
status: "generated",
|
|
1182
|
-
skill_id: recordId,
|
|
1183
|
-
updated_at: now
|
|
1184
|
-
});
|
|
1185
|
-
linkedCandidate = true;
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1344
|
+
updateSkillRecord(existing.id, {
|
|
1345
|
+
display_name: args.display_name,
|
|
1346
|
+
description: args.description,
|
|
1347
|
+
generation,
|
|
1348
|
+
...args.source_ids !== void 0 ? { source_ids: args.source_ids } : {},
|
|
1349
|
+
path: relativePath,
|
|
1350
|
+
updated_at: now
|
|
1351
|
+
});
|
|
1352
|
+
insertLineage({
|
|
1353
|
+
id: crypto2.randomUUID(),
|
|
1354
|
+
skill_id: existing.id,
|
|
1355
|
+
generation,
|
|
1356
|
+
action: "updated",
|
|
1357
|
+
rationale: args.rationale ?? "Skill content updated",
|
|
1358
|
+
source_ids_added: args.source_ids,
|
|
1359
|
+
content_snapshot: args.content,
|
|
1360
|
+
created_at: now
|
|
1361
|
+
});
|
|
1189
1362
|
})();
|
|
1190
1363
|
} catch (err) {
|
|
1191
1364
|
try {
|
|
1192
|
-
|
|
1193
|
-
writeFileSync(skillPath, priorSkillContent, "utf-8");
|
|
1194
|
-
} else if (!skillDirPreexisted) {
|
|
1195
|
-
rmSync(skillDir, { recursive: true, force: true });
|
|
1196
|
-
} else {
|
|
1197
|
-
rmSync(skillPath, { force: true });
|
|
1198
|
-
}
|
|
1365
|
+
writeFileSync(skillPath, priorSkillContent, "utf-8");
|
|
1199
1366
|
} catch (rollbackErr) {
|
|
1200
1367
|
console.warn(
|
|
1201
1368
|
"[vault_write_skill] file rollback after DB failure also failed:",
|
|
@@ -1207,14 +1374,12 @@ function createSkillTools(deps) {
|
|
|
1207
1374
|
error: `Skill write aborted: database transaction failed and on-disk state was rolled back. ${err instanceof Error ? err.message : String(err)}`
|
|
1208
1375
|
});
|
|
1209
1376
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
link: `/skills?skill=${encodeURIComponent(args.name)}`,
|
|
1217
|
-
metadata: { skillId: recordId, name: args.name, generation }
|
|
1377
|
+
emitSkillNotification("evolved", {
|
|
1378
|
+
name: args.name,
|
|
1379
|
+
display_name: args.display_name,
|
|
1380
|
+
description: args.description,
|
|
1381
|
+
recordId,
|
|
1382
|
+
generation
|
|
1218
1383
|
});
|
|
1219
1384
|
recordTurn("vault_write_skill", args);
|
|
1220
1385
|
return textResult({
|
|
@@ -1226,10 +1391,145 @@ function createSkillTools(deps) {
|
|
|
1226
1391
|
},
|
|
1227
1392
|
{ annotations: { openWorldHint: true } }
|
|
1228
1393
|
);
|
|
1394
|
+
const vaultStageSkill = tool4(
|
|
1395
|
+
"vault_stage_skill",
|
|
1396
|
+
"Stage a provisional SKILL.md under .myco/staging/skills/<candidate_id>/ for later promotion by vault_finalize_skill. Use this from the skill-generate draft phase. The write is NOT live \u2014 the skill does not appear under .agents/skills/ and no DB rows are created until vault_finalize_skill is called with the same candidate_id.",
|
|
1397
|
+
{
|
|
1398
|
+
candidate_id: external_exports.string().describe(
|
|
1399
|
+
"Candidate ID from the instruction. Required \u2014 staging is keyed by candidate so the validate phase (and on-failure cleanup) can find the staged content."
|
|
1400
|
+
),
|
|
1401
|
+
name: external_exports.string().describe("Final skill directory name (kebab-case, no colon). Stored in the manifest for finalize."),
|
|
1402
|
+
display_name: external_exports.string().describe("Human-readable display name"),
|
|
1403
|
+
description: external_exports.string().describe("Short description \u2014 used for the dedup gate and the final skill record"),
|
|
1404
|
+
content: external_exports.string().describe("Full SKILL.md content in markdown including frontmatter"),
|
|
1405
|
+
source_ids: external_exports.string().optional().describe("JSON array of source spore/entity IDs"),
|
|
1406
|
+
rationale: external_exports.string().optional().describe("Why this skill is being created \u2014 stored in lineage after finalize")
|
|
1407
|
+
},
|
|
1408
|
+
async (args) => {
|
|
1409
|
+
recordTurn("vault_stage_skill", args);
|
|
1410
|
+
if (!vaultDir) {
|
|
1411
|
+
return textResult({
|
|
1412
|
+
error: "vault_stage_skill requires vaultDir on the tool deps \u2014 staging has no location otherwise"
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
const validationErrors = validateSkillContent(args.content, args.name);
|
|
1416
|
+
if (validationErrors.length > 0) {
|
|
1417
|
+
return textResult({
|
|
1418
|
+
error: "Skill validation failed. Fix these issues and re-stage.",
|
|
1419
|
+
issues: validationErrors
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
if (!args.name || /[/\\]|\.\./.test(args.name)) {
|
|
1423
|
+
return textResult({
|
|
1424
|
+
error: 'Invalid skill name: must be a simple directory name without path separators or ".."'
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
const candidateError = requireApprovedCandidate(args.candidate_id);
|
|
1428
|
+
if (candidateError) return textResult(candidateError);
|
|
1429
|
+
const dedupError = checkDedupGates({
|
|
1430
|
+
candidate_id: args.candidate_id,
|
|
1431
|
+
name: args.name,
|
|
1432
|
+
description: args.description,
|
|
1433
|
+
rejectSameName: true
|
|
1434
|
+
});
|
|
1435
|
+
if (dedupError) return textResult(dedupError);
|
|
1436
|
+
let stagingFilePath;
|
|
1437
|
+
try {
|
|
1438
|
+
stagingFilePath = writeStagedSkill(vaultDir, args.candidate_id, args.content);
|
|
1439
|
+
const manifest = {
|
|
1440
|
+
candidate_id: args.candidate_id,
|
|
1441
|
+
name: args.name,
|
|
1442
|
+
display_name: args.display_name,
|
|
1443
|
+
description: args.description,
|
|
1444
|
+
source_ids: args.source_ids ?? "[]",
|
|
1445
|
+
rationale: args.rationale ?? "Initial draft"
|
|
1446
|
+
};
|
|
1447
|
+
writeStagedManifest(vaultDir, args.candidate_id, manifest);
|
|
1448
|
+
} catch (err) {
|
|
1449
|
+
return textResult({
|
|
1450
|
+
error: `Failed to write staged skill: ${err instanceof Error ? err.message : String(err)}`
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
return textResult({
|
|
1454
|
+
candidate_id: args.candidate_id,
|
|
1455
|
+
staging_path: stagingFilePath,
|
|
1456
|
+
status: "staged"
|
|
1457
|
+
});
|
|
1458
|
+
},
|
|
1459
|
+
{ annotations: { openWorldHint: true } }
|
|
1460
|
+
);
|
|
1461
|
+
const vaultFinalizeSkill = tool4(
|
|
1462
|
+
"vault_finalize_skill",
|
|
1463
|
+
"Promote a staged skill to live at .agents/skills/<name>/ and insert the skill_records / lineage rows. Call this from skill-generate validate phase after your quality checks pass. Requires vault_stage_skill to have been called earlier with the same candidate_id; reads the staged SKILL.md + manifest rather than taking duplicate metadata.",
|
|
1464
|
+
{
|
|
1465
|
+
candidate_id: external_exports.string().describe("Candidate ID whose staged skill should be promoted. Must match a previous vault_stage_skill call.")
|
|
1466
|
+
},
|
|
1467
|
+
async (args) => {
|
|
1468
|
+
recordTurn("vault_finalize_skill", args);
|
|
1469
|
+
if (!vaultDir) {
|
|
1470
|
+
return textResult({
|
|
1471
|
+
error: "vault_finalize_skill requires vaultDir on the tool deps"
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
const stagedContent = readStagedSkill(vaultDir, args.candidate_id);
|
|
1475
|
+
const manifest = readStagedManifest(vaultDir, args.candidate_id);
|
|
1476
|
+
if (!stagedContent || !manifest) {
|
|
1477
|
+
return textResult({
|
|
1478
|
+
error: `No staged skill found for candidate ${args.candidate_id}. Call vault_stage_skill first.`
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
const candidateError = requireApprovedCandidate(args.candidate_id);
|
|
1482
|
+
if (candidateError) return textResult(candidateError);
|
|
1483
|
+
const validationErrors = validateSkillContent(stagedContent, manifest.name);
|
|
1484
|
+
if (validationErrors.length > 0) {
|
|
1485
|
+
return textResult({
|
|
1486
|
+
error: "Staged skill failed validation on finalize. Re-stage with valid content.",
|
|
1487
|
+
issues: validationErrors
|
|
1488
|
+
});
|
|
1489
|
+
}
|
|
1490
|
+
const dedupError = checkDedupGates({
|
|
1491
|
+
candidate_id: args.candidate_id,
|
|
1492
|
+
name: manifest.name,
|
|
1493
|
+
description: manifest.description,
|
|
1494
|
+
rejectSameName: true
|
|
1495
|
+
});
|
|
1496
|
+
if (dedupError) return textResult(dedupError);
|
|
1497
|
+
const result = await promoteNewSkill({
|
|
1498
|
+
name: manifest.name,
|
|
1499
|
+
display_name: manifest.display_name,
|
|
1500
|
+
description: manifest.description,
|
|
1501
|
+
content: stagedContent,
|
|
1502
|
+
source_ids: manifest.source_ids,
|
|
1503
|
+
candidate_id: manifest.candidate_id,
|
|
1504
|
+
rationale: manifest.rationale,
|
|
1505
|
+
linkCandidate: (recordId, now) => {
|
|
1506
|
+
updateCandidate(manifest.candidate_id, {
|
|
1507
|
+
status: CANDIDATE_STATUS.GENERATED,
|
|
1508
|
+
skill_id: recordId,
|
|
1509
|
+
updated_at: now
|
|
1510
|
+
});
|
|
1511
|
+
},
|
|
1512
|
+
label: "vault_finalize_skill"
|
|
1513
|
+
});
|
|
1514
|
+
if ("error" in result) return textResult(result);
|
|
1515
|
+
cleanupStagedSkill(vaultDir, args.candidate_id);
|
|
1516
|
+
emitSkillNotification("created", {
|
|
1517
|
+
name: manifest.name,
|
|
1518
|
+
display_name: manifest.display_name,
|
|
1519
|
+
description: manifest.description,
|
|
1520
|
+
recordId: result.id,
|
|
1521
|
+
generation: result.generation
|
|
1522
|
+
});
|
|
1523
|
+
return textResult(result);
|
|
1524
|
+
},
|
|
1525
|
+
{ annotations: { openWorldHint: true } }
|
|
1526
|
+
);
|
|
1229
1527
|
return [
|
|
1230
1528
|
vaultSkillCandidates,
|
|
1231
1529
|
vaultSkillRecords,
|
|
1232
|
-
vaultWriteSkill
|
|
1530
|
+
vaultWriteSkill,
|
|
1531
|
+
vaultStageSkill,
|
|
1532
|
+
vaultFinalizeSkill
|
|
1233
1533
|
];
|
|
1234
1534
|
}
|
|
1235
1535
|
|
|
@@ -1259,8 +1559,11 @@ var OBSERVABILITY_TOOL_NAMES = /* @__PURE__ */ new Set(["vault_report"]);
|
|
|
1259
1559
|
var SKILL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
1260
1560
|
"vault_skill_candidates",
|
|
1261
1561
|
"vault_skill_records",
|
|
1262
|
-
"vault_write_skill"
|
|
1562
|
+
"vault_write_skill",
|
|
1563
|
+
"vault_stage_skill",
|
|
1564
|
+
"vault_finalize_skill"
|
|
1263
1565
|
]);
|
|
1566
|
+
var VAULT_TOOL_COUNT = READ_TOOL_NAMES.size + WRITE_TOOL_NAMES.size + OBSERVABILITY_TOOL_NAMES.size + SKILL_TOOL_NAMES.size;
|
|
1264
1567
|
function setsOverlap(a, b) {
|
|
1265
1568
|
for (const item of a) {
|
|
1266
1569
|
if (b.has(item)) return true;
|
|
@@ -2233,6 +2536,17 @@ async function runAgent(vaultDir, options) {
|
|
|
2233
2536
|
tokensUsed = result.tokensUsed;
|
|
2234
2537
|
costUsd = result.costUsd;
|
|
2235
2538
|
phaseResults = result.phases;
|
|
2539
|
+
const requiredPhaseNames = new Set(
|
|
2540
|
+
config.phases.filter((p) => p.required).map((p) => p.name)
|
|
2541
|
+
);
|
|
2542
|
+
const failedRequired = phaseResults.find(
|
|
2543
|
+
(p) => p.status === "failed" && requiredPhaseNames.has(p.name)
|
|
2544
|
+
);
|
|
2545
|
+
if (failedRequired) {
|
|
2546
|
+
throw new Error(
|
|
2547
|
+
`Required phase "${failedRequired.name}" failed: ${failedRequired.summary}`
|
|
2548
|
+
);
|
|
2549
|
+
}
|
|
2236
2550
|
} else {
|
|
2237
2551
|
const taskPrompt = composeTaskPrompt(
|
|
2238
2552
|
vaultContext,
|
|
@@ -2298,6 +2612,11 @@ ${err.stack.split("\n").slice(0, 3).join("\n")}`;
|
|
|
2298
2612
|
} catch (dbErr) {
|
|
2299
2613
|
console.error(`[agent] Failed to save error to DB:`, dbErr);
|
|
2300
2614
|
}
|
|
2615
|
+
await cleanupOnTaskFailure({
|
|
2616
|
+
taskName: config.taskName,
|
|
2617
|
+
vaultDir,
|
|
2618
|
+
runContext: options?.runContext
|
|
2619
|
+
});
|
|
2301
2620
|
return {
|
|
2302
2621
|
runId,
|
|
2303
2622
|
status: STATUS_FAILED,
|
|
@@ -2306,10 +2625,29 @@ ${err.stack.split("\n").slice(0, 3).join("\n")}`;
|
|
|
2306
2625
|
};
|
|
2307
2626
|
}
|
|
2308
2627
|
}
|
|
2628
|
+
async function cleanupOnTaskFailure(args) {
|
|
2629
|
+
if (args.taskName !== SKILL_GENERATE_TASK) return;
|
|
2630
|
+
if (!args.vaultDir) return;
|
|
2631
|
+
const candidateId = args.runContext?.candidate_id;
|
|
2632
|
+
if (!candidateId) return;
|
|
2633
|
+
try {
|
|
2634
|
+
const { cleanupStagedSkill: cleanupStagedSkill2 } = await import("./skill-staging-SWM7UC5D.js");
|
|
2635
|
+
cleanupStagedSkill2(args.vaultDir, candidateId);
|
|
2636
|
+
console.warn(
|
|
2637
|
+
`[agent] skill-generate failed \u2014 cleaned up staging for candidate ${candidateId}`
|
|
2638
|
+
);
|
|
2639
|
+
} catch (cleanupErr) {
|
|
2640
|
+
console.warn(
|
|
2641
|
+
`[agent] Failed to clean staging for candidate ${candidateId}:`,
|
|
2642
|
+
cleanupErr instanceof Error ? cleanupErr.message : cleanupErr
|
|
2643
|
+
);
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2309
2646
|
export {
|
|
2647
|
+
cleanupOnTaskFailure,
|
|
2310
2648
|
composePhasePrompt,
|
|
2311
2649
|
composeTaskPrompt,
|
|
2312
2650
|
computeWaves,
|
|
2313
2651
|
runAgent
|
|
2314
2652
|
};
|
|
2315
|
-
//# sourceMappingURL=executor-
|
|
2653
|
+
//# sourceMappingURL=executor-PUKL76TU.js.map
|