thoth-agents 0.2.11 → 0.3.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/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +21 -0
- package/README.md +301 -311
- package/dist/agents/configured-role-prompt.d.ts +9 -0
- package/dist/agents/prompt-sections.d.ts +8 -4
- package/dist/agents/prompt-utils.d.ts +0 -4
- package/dist/{chunk-ASKXWU6E.js → chunk-2MZ5C2RN.js} +1078 -1725
- package/dist/chunk-XO74DXBZ.js +1781 -0
- package/dist/{chunk-6BNAV534.js → chunk-YUCGIJPP.js} +12 -30
- package/dist/cli/claude-code-install.d.ts +24 -26
- package/dist/cli/claude-code-paths.d.ts +0 -28
- package/dist/cli/codex-install.d.ts +2 -2
- package/dist/cli/codex-paths.d.ts +0 -2
- package/dist/cli/index.js +279 -119
- package/dist/cli/install.d.ts +5 -1
- package/dist/cli/model-catalog/cache.d.ts +1 -1
- package/dist/cli/operations/claude-code.d.ts +6 -3
- package/dist/cli/operations/codex.d.ts +2 -1
- package/dist/cli/operations/opencode.d.ts +2 -1
- package/dist/cli/operations/types.d.ts +9 -1
- package/dist/cli/package-root.d.ts +1 -0
- package/dist/cli/providers.d.ts +6 -116
- package/dist/cli/skills.d.ts +19 -32
- package/dist/cli/thoth-mem-install.d.ts +45 -0
- package/dist/cli/tui/components/StatusView.d.ts +12 -1
- package/dist/cli/tui/index.js +256 -142
- package/dist/cli/tui/operations.d.ts +4 -3
- package/dist/cli/types.d.ts +0 -10
- package/dist/config/constants.d.ts +20 -16
- package/dist/config/index.d.ts +0 -1
- package/dist/config/loader.d.ts +1 -1
- package/dist/config/schema.d.ts +2 -38
- package/dist/config/utils.d.ts +1 -2
- package/dist/harness/adapters/codex-surfaces.d.ts +3 -3
- package/dist/harness/core/agent-pack.d.ts +37 -29
- package/dist/harness/core/memory-governance.d.ts +24 -48
- package/dist/harness/core/sdd.d.ts +364 -131
- package/dist/harness/generate-integration-packages.d.ts +20 -0
- package/dist/harness/opencode-init-command.d.ts +11 -0
- package/dist/harness/types.d.ts +14 -1
- package/dist/harness/writers/claude-code-subagent.d.ts +3 -1
- package/dist/hooks/index.d.ts +0 -5
- package/dist/index.js +562 -1314
- package/dist/mcp/index.d.ts +1 -2
- package/package.json +17 -12
- package/plugin/.claude-plugin/.thoth-agents-plugin-assets.json +50 -0
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/.codex-plugin/.thoth-agents-plugin-assets.json +16 -0
- package/plugin/.codex-plugin/plugin.json +7 -0
- package/plugin/.mcp.json +19 -0
- package/plugin/agents/deep.md +89 -0
- package/plugin/agents/designer.md +89 -0
- package/plugin/agents/explorer.md +90 -0
- package/plugin/agents/librarian.md +90 -0
- package/plugin/agents/oracle.md +92 -0
- package/plugin/agents/orchestrator.md +128 -0
- package/plugin/agents/quick.md +89 -0
- package/plugin/codex.mcp.json +17 -0
- package/plugin/settings.json +3 -0
- package/plugin/skills/thoth-archive/SKILL.md +40 -0
- package/plugin/skills/thoth-archive/scripts/archive.mjs +748 -0
- package/plugin/skills/thoth-constitution/SKILL.md +45 -0
- package/plugin/skills/thoth-constitution/scripts/validate.mjs +167 -0
- package/plugin/skills/thoth-constitution/templates/constitution.md +54 -0
- package/plugin/skills/thoth-init/SKILL.md +30 -0
- package/plugin/skills/thoth-init/scripts/init.mjs +141 -0
- package/plugin/skills/thoth-sdd/SKILL.md +83 -0
- package/plugin/skills/thoth-sdd/references/phases/analyze.md +21 -0
- package/plugin/skills/thoth-sdd/references/phases/checklist.md +20 -0
- package/plugin/skills/thoth-sdd/references/phases/clarify.md +15 -0
- package/plugin/skills/thoth-sdd/references/phases/converge.md +14 -0
- package/plugin/skills/thoth-sdd/references/phases/explore.md +10 -0
- package/plugin/skills/thoth-sdd/references/phases/implement.md +19 -0
- package/plugin/skills/thoth-sdd/references/phases/plan.md +15 -0
- package/plugin/skills/thoth-sdd/references/phases/specify.md +21 -0
- package/plugin/skills/thoth-sdd/references/phases/tasks.md +20 -0
- package/plugin/skills/thoth-sdd/references/phases/verify.md +22 -0
- package/plugin/skills/thoth-sdd/scripts/validate.mjs +1117 -0
- package/plugin/skills/thoth-sdd/templates/archive-report.md +25 -0
- package/plugin/skills/thoth-sdd/templates/checklist.md +27 -0
- package/plugin/skills/thoth-sdd/templates/plan.md +38 -0
- package/plugin/skills/thoth-sdd/templates/spec.md +54 -0
- package/plugin/skills/thoth-sdd/templates/tasks.md +29 -0
- package/plugin/skills/thoth-sdd/templates/verify-report.md +28 -0
- package/skills/README.md +12 -0
- package/skills/thoth-archive/SKILL.md +40 -0
- package/skills/thoth-archive/scripts/archive.mjs +748 -0
- package/skills/thoth-constitution/SKILL.md +45 -0
- package/skills/thoth-constitution/scripts/validate.mjs +167 -0
- package/skills/thoth-constitution/templates/constitution.md +54 -0
- package/skills/thoth-init/SKILL.md +30 -0
- package/skills/thoth-init/scripts/init.mjs +141 -0
- package/skills/thoth-sdd/SKILL.md +83 -0
- package/skills/thoth-sdd/references/phases/analyze.md +21 -0
- package/skills/thoth-sdd/references/phases/checklist.md +20 -0
- package/skills/thoth-sdd/references/phases/clarify.md +15 -0
- package/skills/thoth-sdd/references/phases/converge.md +14 -0
- package/skills/thoth-sdd/references/phases/explore.md +10 -0
- package/skills/thoth-sdd/references/phases/implement.md +19 -0
- package/skills/thoth-sdd/references/phases/plan.md +15 -0
- package/skills/thoth-sdd/references/phases/specify.md +21 -0
- package/skills/thoth-sdd/references/phases/tasks.md +20 -0
- package/skills/thoth-sdd/references/phases/verify.md +22 -0
- package/skills/thoth-sdd/scripts/validate.mjs +1117 -0
- package/skills/thoth-sdd/templates/archive-report.md +25 -0
- package/skills/thoth-sdd/templates/checklist.md +27 -0
- package/skills/thoth-sdd/templates/plan.md +38 -0
- package/skills/thoth-sdd/templates/spec.md +54 -0
- package/skills/thoth-sdd/templates/tasks.md +29 -0
- package/skills/thoth-sdd/templates/verify-report.md +28 -0
- package/thoth-agents.schema.json +0 -39
- package/dist/chunk-OZE7JVGS.js +0 -2344
- package/dist/cli/custom-skills.d.ts +0 -49
- package/dist/cli/skill-manifest.d.ts +0 -32
- package/dist/harness/core/skills.d.ts +0 -211
- package/dist/harness/generate-codex-plugin.d.ts +0 -9
- package/dist/harness/writers/claude-code-skill-layout.d.ts +0 -16
- package/dist/harness/writers/fs-skill-collect.d.ts +0 -7
- package/dist/harness/writers/skill-layout.d.ts +0 -16
- package/dist/hooks/chat-headers.d.ts +0 -16
- package/dist/hooks/phase-reminder/index.d.ts +0 -28
- package/dist/hooks/post-read-nudge/index.d.ts +0 -18
- package/dist/hooks/skill-sync.d.ts +0 -10
- package/dist/hooks/thoth-mem/index.d.ts +0 -46
- package/dist/hooks/thoth-mem/protocol.d.ts +0 -7
- package/dist/mcp/thoth.d.ts +0 -3
- package/dist/sdd/artifact-governance/artifact-loader.d.ts +0 -130
- package/dist/sdd/artifact-governance/index.d.ts +0 -6
- package/dist/sdd/artifact-governance/tasks-validator.d.ts +0 -17
- package/dist/sdd/artifact-governance/types.d.ts +0 -52
- package/dist/thoth/client.d.ts +0 -14
- package/dist/thoth/index.d.ts +0 -2
- package/src/skills/_shared/openspec-convention.md +0 -350
- package/src/skills/_shared/persistence-contract.md +0 -163
- package/src/skills/_shared/thoth-mem-convention.md +0 -123
- package/src/skills/executing-plans/SKILL.md +0 -288
- package/src/skills/plan-reviewer/SKILL.md +0 -219
- package/src/skills/requirements-interview/SKILL.md +0 -233
- package/src/skills/sdd-apply/SKILL.md +0 -89
- package/src/skills/sdd-archive/SKILL.md +0 -92
- package/src/skills/sdd-clarify/SKILL.md +0 -105
- package/src/skills/sdd-constitution/SKILL.md +0 -107
- package/src/skills/sdd-design/SKILL.md +0 -155
- package/src/skills/sdd-init/SKILL.md +0 -233
- package/src/skills/sdd-propose/SKILL.md +0 -101
- package/src/skills/sdd-spec/SKILL.md +0 -136
- package/src/skills/sdd-tasks/SKILL.md +0 -232
- package/src/skills/sdd-verify/SKILL.md +0 -131
- package/src/skills/thoth-mem-agents/SKILL.md +0 -210
|
@@ -0,0 +1,1781 @@
|
|
|
1
|
+
// src/config/loader.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
|
|
5
|
+
// src/cli/config-io.ts
|
|
6
|
+
import {
|
|
7
|
+
copyFileSync,
|
|
8
|
+
existsSync as existsSync2,
|
|
9
|
+
readFileSync,
|
|
10
|
+
renameSync,
|
|
11
|
+
statSync,
|
|
12
|
+
writeFileSync
|
|
13
|
+
} from "fs";
|
|
14
|
+
|
|
15
|
+
// src/cli/paths.ts
|
|
16
|
+
import { existsSync, mkdirSync } from "fs";
|
|
17
|
+
import { homedir } from "os";
|
|
18
|
+
import { dirname, join } from "path";
|
|
19
|
+
function getDefaultOpenCodeConfigDir() {
|
|
20
|
+
const userConfigDir = process.env.XDG_CONFIG_HOME ? process.env.XDG_CONFIG_HOME : join(homedir(), ".config");
|
|
21
|
+
return join(userConfigDir, "opencode");
|
|
22
|
+
}
|
|
23
|
+
function getCustomOpenCodeConfigDir() {
|
|
24
|
+
const configDir = process.env.OPENCODE_CONFIG_DIR?.trim();
|
|
25
|
+
return configDir || void 0;
|
|
26
|
+
}
|
|
27
|
+
function getConfigDir() {
|
|
28
|
+
const customConfigDir = getCustomOpenCodeConfigDir();
|
|
29
|
+
if (customConfigDir) {
|
|
30
|
+
return customConfigDir;
|
|
31
|
+
}
|
|
32
|
+
return getDefaultOpenCodeConfigDir();
|
|
33
|
+
}
|
|
34
|
+
function getOpenCodeConfigPaths() {
|
|
35
|
+
const configDir = getDefaultOpenCodeConfigDir();
|
|
36
|
+
return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
|
|
37
|
+
}
|
|
38
|
+
function getConfigJson() {
|
|
39
|
+
return getOpenCodeConfigPaths()[0];
|
|
40
|
+
}
|
|
41
|
+
function getConfigJsonc() {
|
|
42
|
+
return getOpenCodeConfigPaths()[1];
|
|
43
|
+
}
|
|
44
|
+
function getLiteConfig() {
|
|
45
|
+
return join(getConfigDir(), "thoth-agents.json");
|
|
46
|
+
}
|
|
47
|
+
function getLiteConfigJsonc() {
|
|
48
|
+
return join(getConfigDir(), "thoth-agents.jsonc");
|
|
49
|
+
}
|
|
50
|
+
function getOpenCodeManagedModelStatePath() {
|
|
51
|
+
return join(getConfigDir(), ".thoth-agents-managed-models.json");
|
|
52
|
+
}
|
|
53
|
+
function getExistingLiteConfigPath() {
|
|
54
|
+
const jsonPath = getLiteConfig();
|
|
55
|
+
if (existsSync(jsonPath)) return jsonPath;
|
|
56
|
+
const jsoncPath = getLiteConfigJsonc();
|
|
57
|
+
if (existsSync(jsoncPath)) return jsoncPath;
|
|
58
|
+
return jsonPath;
|
|
59
|
+
}
|
|
60
|
+
function getExistingConfigPath() {
|
|
61
|
+
const jsonPath = getConfigJson();
|
|
62
|
+
if (existsSync(jsonPath)) return jsonPath;
|
|
63
|
+
const jsoncPath = getConfigJsonc();
|
|
64
|
+
if (existsSync(jsoncPath)) return jsoncPath;
|
|
65
|
+
return jsonPath;
|
|
66
|
+
}
|
|
67
|
+
function ensureConfigDir() {
|
|
68
|
+
const configDir = getConfigDir();
|
|
69
|
+
if (!existsSync(configDir)) {
|
|
70
|
+
mkdirSync(configDir, { recursive: true });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function ensureOpenCodeConfigDir() {
|
|
74
|
+
const configDir = dirname(getConfigJson());
|
|
75
|
+
if (!existsSync(configDir)) {
|
|
76
|
+
mkdirSync(configDir, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/config/constants.ts
|
|
81
|
+
var SUBAGENT_NAMES = [
|
|
82
|
+
"explorer",
|
|
83
|
+
"librarian",
|
|
84
|
+
"oracle",
|
|
85
|
+
"designer",
|
|
86
|
+
"quick",
|
|
87
|
+
"deep"
|
|
88
|
+
];
|
|
89
|
+
var ORCHESTRATOR_NAME = "orchestrator";
|
|
90
|
+
var ALL_AGENT_NAMES = [
|
|
91
|
+
ORCHESTRATOR_NAME,
|
|
92
|
+
"explorer",
|
|
93
|
+
"librarian",
|
|
94
|
+
"oracle",
|
|
95
|
+
"designer",
|
|
96
|
+
"quick",
|
|
97
|
+
"deep"
|
|
98
|
+
];
|
|
99
|
+
var CONFIRMED_OPENAI_SUBAGENT_PRESET = {
|
|
100
|
+
explorer: { model: "gpt-5.6-luna", effort: "low" },
|
|
101
|
+
librarian: { model: "gpt-5.6-luna", effort: "xhigh" },
|
|
102
|
+
oracle: { model: "gpt-5.6-sol", effort: "xhigh" },
|
|
103
|
+
designer: { model: "gpt-5.6-sol", effort: "medium" },
|
|
104
|
+
quick: { model: "gpt-5.6-luna", effort: "xhigh" },
|
|
105
|
+
deep: { model: "gpt-5.6-sol", effort: "medium" }
|
|
106
|
+
};
|
|
107
|
+
var OPENCODE_OPENAI_ORCHESTRATOR_PRESET = {
|
|
108
|
+
model: "gpt-5.6-sol",
|
|
109
|
+
effort: "xhigh"
|
|
110
|
+
};
|
|
111
|
+
function buildDefaultModels() {
|
|
112
|
+
const defaults = {
|
|
113
|
+
orchestrator: void 0,
|
|
114
|
+
explorer: void 0,
|
|
115
|
+
librarian: void 0,
|
|
116
|
+
oracle: void 0,
|
|
117
|
+
designer: void 0,
|
|
118
|
+
quick: void 0,
|
|
119
|
+
deep: void 0
|
|
120
|
+
};
|
|
121
|
+
for (const [name, { model }] of Object.entries(
|
|
122
|
+
CONFIRMED_OPENAI_SUBAGENT_PRESET
|
|
123
|
+
)) {
|
|
124
|
+
defaults[name] = `openai/${model}`;
|
|
125
|
+
}
|
|
126
|
+
return defaults;
|
|
127
|
+
}
|
|
128
|
+
var DEFAULT_MODELS = buildDefaultModels();
|
|
129
|
+
function getDefaultOpenCodeModel(name) {
|
|
130
|
+
const subagentModel = DEFAULT_MODELS[name];
|
|
131
|
+
if (subagentModel !== void 0) {
|
|
132
|
+
return subagentModel;
|
|
133
|
+
}
|
|
134
|
+
return `openai/${OPENCODE_OPENAI_ORCHESTRATOR_PRESET.model}`;
|
|
135
|
+
}
|
|
136
|
+
function getDefaultOpenCodeVariant(name) {
|
|
137
|
+
if (name === ORCHESTRATOR_NAME) {
|
|
138
|
+
return OPENCODE_OPENAI_ORCHESTRATOR_PRESET.effort;
|
|
139
|
+
}
|
|
140
|
+
return CONFIRMED_OPENAI_SUBAGENT_PRESET[name].effort;
|
|
141
|
+
}
|
|
142
|
+
var POLL_INTERVAL_BACKGROUND_MS = 2e3;
|
|
143
|
+
var DEFAULT_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
144
|
+
var MAX_POLL_TIME_MS = 5 * 60 * 1e3;
|
|
145
|
+
|
|
146
|
+
// src/config/schema.ts
|
|
147
|
+
import { z } from "zod";
|
|
148
|
+
var AGENT_NAMES = [
|
|
149
|
+
"orchestrator",
|
|
150
|
+
"oracle",
|
|
151
|
+
"designer",
|
|
152
|
+
"explorer",
|
|
153
|
+
"librarian",
|
|
154
|
+
"quick",
|
|
155
|
+
"deep"
|
|
156
|
+
];
|
|
157
|
+
var FALLBACK_AGENT_NAMES = [...AGENT_NAMES];
|
|
158
|
+
var MANUAL_AGENT_NAMES = [...AGENT_NAMES];
|
|
159
|
+
var ProviderModelIdSchema = z.string().regex(
|
|
160
|
+
/^[^/\s]+\/[^\s]+$/,
|
|
161
|
+
"Expected provider/model format (provider/.../model)"
|
|
162
|
+
);
|
|
163
|
+
var ManualAgentPlanSchema = z.object({
|
|
164
|
+
primary: ProviderModelIdSchema,
|
|
165
|
+
fallback1: ProviderModelIdSchema,
|
|
166
|
+
fallback2: ProviderModelIdSchema,
|
|
167
|
+
fallback3: ProviderModelIdSchema
|
|
168
|
+
}).superRefine((value, ctx) => {
|
|
169
|
+
const unique = /* @__PURE__ */ new Set([
|
|
170
|
+
value.primary,
|
|
171
|
+
value.fallback1,
|
|
172
|
+
value.fallback2,
|
|
173
|
+
value.fallback3
|
|
174
|
+
]);
|
|
175
|
+
if (unique.size !== 4) {
|
|
176
|
+
ctx.addIssue({
|
|
177
|
+
code: z.ZodIssueCode.custom,
|
|
178
|
+
message: "primary and fallbacks must be unique per agent"
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
var ManualPlanSchema = z.object({
|
|
183
|
+
orchestrator: ManualAgentPlanSchema,
|
|
184
|
+
oracle: ManualAgentPlanSchema,
|
|
185
|
+
designer: ManualAgentPlanSchema,
|
|
186
|
+
explorer: ManualAgentPlanSchema,
|
|
187
|
+
librarian: ManualAgentPlanSchema,
|
|
188
|
+
quick: ManualAgentPlanSchema,
|
|
189
|
+
deep: ManualAgentPlanSchema
|
|
190
|
+
}).strict();
|
|
191
|
+
var AgentModelChainSchema = z.array(z.string()).min(1);
|
|
192
|
+
var FallbackChainsSchema = z.object({
|
|
193
|
+
orchestrator: AgentModelChainSchema.optional(),
|
|
194
|
+
oracle: AgentModelChainSchema.optional(),
|
|
195
|
+
designer: AgentModelChainSchema.optional(),
|
|
196
|
+
explorer: AgentModelChainSchema.optional(),
|
|
197
|
+
librarian: AgentModelChainSchema.optional(),
|
|
198
|
+
quick: AgentModelChainSchema.optional(),
|
|
199
|
+
deep: AgentModelChainSchema.optional()
|
|
200
|
+
}).catchall(AgentModelChainSchema);
|
|
201
|
+
var AgentOverrideConfigSchema = z.object({
|
|
202
|
+
model: z.union([
|
|
203
|
+
z.string(),
|
|
204
|
+
z.array(
|
|
205
|
+
z.union([
|
|
206
|
+
z.string(),
|
|
207
|
+
z.object({
|
|
208
|
+
id: z.string(),
|
|
209
|
+
variant: z.string().optional()
|
|
210
|
+
})
|
|
211
|
+
])
|
|
212
|
+
)
|
|
213
|
+
]).optional(),
|
|
214
|
+
temperature: z.number().min(0).max(2).optional(),
|
|
215
|
+
steps: z.number().int().min(1).optional(),
|
|
216
|
+
variant: z.string().optional().catch(void 0)
|
|
217
|
+
});
|
|
218
|
+
var TmuxLayoutSchema = z.enum([
|
|
219
|
+
"main-horizontal",
|
|
220
|
+
// Main pane on top, agents stacked below
|
|
221
|
+
"main-vertical",
|
|
222
|
+
// Main pane on left, agents stacked on right
|
|
223
|
+
"tiled",
|
|
224
|
+
// All panes equal size grid
|
|
225
|
+
"even-horizontal",
|
|
226
|
+
// All panes side by side
|
|
227
|
+
"even-vertical"
|
|
228
|
+
// All panes stacked vertically
|
|
229
|
+
]);
|
|
230
|
+
var TmuxConfigSchema = z.object({
|
|
231
|
+
enabled: z.boolean().default(false),
|
|
232
|
+
layout: TmuxLayoutSchema.default("main-vertical"),
|
|
233
|
+
main_pane_size: z.number().min(20).max(80).default(60)
|
|
234
|
+
// percentage for main pane
|
|
235
|
+
});
|
|
236
|
+
var PresetSchema = z.record(z.string(), AgentOverrideConfigSchema);
|
|
237
|
+
var AgentNameSchema = z.enum(AGENT_NAMES);
|
|
238
|
+
var McpNameSchema = z.enum(["exa", "context7", "grep_app"]);
|
|
239
|
+
var CodexGenerationConfigSchema = z.object({
|
|
240
|
+
enabled: z.boolean().default(false),
|
|
241
|
+
outputRoot: z.string().optional(),
|
|
242
|
+
dryRun: z.boolean().default(true)
|
|
243
|
+
});
|
|
244
|
+
var ClaudeCodeGenerationConfigSchema = z.object({
|
|
245
|
+
enabled: z.boolean().default(false),
|
|
246
|
+
outputRoot: z.string().optional(),
|
|
247
|
+
dryRun: z.boolean().default(true)
|
|
248
|
+
});
|
|
249
|
+
var FailoverConfigSchema = z.object({
|
|
250
|
+
enabled: z.boolean().default(true),
|
|
251
|
+
timeoutMs: z.number().min(0).default(15e3),
|
|
252
|
+
retryDelayMs: z.number().min(0).default(500),
|
|
253
|
+
chains: FallbackChainsSchema.default({})
|
|
254
|
+
});
|
|
255
|
+
var PluginConfigSchema = z.object({
|
|
256
|
+
$schema: z.string().optional(),
|
|
257
|
+
preset: z.string().optional(),
|
|
258
|
+
setDefaultAgent: z.boolean().optional(),
|
|
259
|
+
scoringEngineVersion: z.enum(["v1", "v2-shadow", "v2"]).optional(),
|
|
260
|
+
balanceProviderUsage: z.boolean().optional(),
|
|
261
|
+
manualPlan: ManualPlanSchema.optional(),
|
|
262
|
+
presets: z.record(z.string(), PresetSchema).optional(),
|
|
263
|
+
agents: z.record(z.string(), AgentOverrideConfigSchema).optional(),
|
|
264
|
+
disabled_mcps: z.array(z.string()).optional(),
|
|
265
|
+
tmux: TmuxConfigSchema.optional(),
|
|
266
|
+
fallback: FailoverConfigSchema.optional(),
|
|
267
|
+
codex: CodexGenerationConfigSchema.optional(),
|
|
268
|
+
claudeCode: ClaudeCodeGenerationConfigSchema.optional()
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// src/config/utils.ts
|
|
272
|
+
function getAgentOverride(config, name) {
|
|
273
|
+
return config?.agents?.[name];
|
|
274
|
+
}
|
|
275
|
+
function getPrimaryModelId(model) {
|
|
276
|
+
if (Array.isArray(model)) {
|
|
277
|
+
const first = model[0];
|
|
278
|
+
return typeof first === "string" ? first : first?.id;
|
|
279
|
+
}
|
|
280
|
+
return model;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/cli/providers.ts
|
|
284
|
+
var THOTH_AGENTS_CONFIG_SCHEMA_URL = "https://unpkg.com/thoth-agents@latest/thoth-agents.schema.json";
|
|
285
|
+
function buildOpenAIModelMappings() {
|
|
286
|
+
const mappings = {
|
|
287
|
+
orchestrator: {
|
|
288
|
+
model: getDefaultOpenCodeModel("orchestrator"),
|
|
289
|
+
variant: getDefaultOpenCodeVariant("orchestrator")
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
for (const [name, preset] of Object.entries(
|
|
293
|
+
CONFIRMED_OPENAI_SUBAGENT_PRESET
|
|
294
|
+
)) {
|
|
295
|
+
mappings[name] = {
|
|
296
|
+
model: `openai/${preset.model}`,
|
|
297
|
+
variant: preset.effort
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return mappings;
|
|
301
|
+
}
|
|
302
|
+
var MODEL_MAPPINGS = {
|
|
303
|
+
openai: buildOpenAIModelMappings()
|
|
304
|
+
};
|
|
305
|
+
function generateLiteConfig(installConfig) {
|
|
306
|
+
const config = {
|
|
307
|
+
$schema: THOTH_AGENTS_CONFIG_SCHEMA_URL,
|
|
308
|
+
preset: "openai",
|
|
309
|
+
presets: {}
|
|
310
|
+
};
|
|
311
|
+
const createAgentConfig = (modelInfo) => {
|
|
312
|
+
return {
|
|
313
|
+
model: modelInfo.model,
|
|
314
|
+
variant: modelInfo.variant
|
|
315
|
+
};
|
|
316
|
+
};
|
|
317
|
+
const buildPreset = (mappingName) => {
|
|
318
|
+
const mapping = MODEL_MAPPINGS[mappingName];
|
|
319
|
+
return Object.fromEntries(
|
|
320
|
+
Object.entries(mapping).map(([agentName, modelInfo]) => [
|
|
321
|
+
agentName,
|
|
322
|
+
createAgentConfig(modelInfo)
|
|
323
|
+
])
|
|
324
|
+
);
|
|
325
|
+
};
|
|
326
|
+
config.presets.openai = buildPreset("openai");
|
|
327
|
+
if (installConfig.hasTmux) {
|
|
328
|
+
config.tmux = {
|
|
329
|
+
enabled: true,
|
|
330
|
+
layout: "main-vertical",
|
|
331
|
+
main_pane_size: 60
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return config;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// src/cli/config-io.ts
|
|
338
|
+
var PACKAGE_NAME = "thoth-agents";
|
|
339
|
+
function stripJsonComments(json) {
|
|
340
|
+
const commentPattern = /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g;
|
|
341
|
+
const trailingCommaPattern = /\\"|"(?:\\"|[^"])*"|(,)(\s*[}\]])/g;
|
|
342
|
+
return json.replace(
|
|
343
|
+
commentPattern,
|
|
344
|
+
(match, commentGroup) => commentGroup ? "" : match
|
|
345
|
+
).replace(
|
|
346
|
+
trailingCommaPattern,
|
|
347
|
+
(match, comma, closing) => comma ? closing : match
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
function parseConfigFile(path2) {
|
|
351
|
+
try {
|
|
352
|
+
if (!existsSync2(path2)) return { config: null };
|
|
353
|
+
const stat = statSync(path2);
|
|
354
|
+
if (stat.size === 0) return { config: null };
|
|
355
|
+
const content = readFileSync(path2, "utf-8");
|
|
356
|
+
if (content.trim().length === 0) return { config: null };
|
|
357
|
+
return { config: JSON.parse(stripJsonComments(content)) };
|
|
358
|
+
} catch (err) {
|
|
359
|
+
return { config: null, error: String(err) };
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function parseConfig(path2) {
|
|
363
|
+
const result = parseConfigFile(path2);
|
|
364
|
+
if (result.config || result.error) return result;
|
|
365
|
+
if (path2.endsWith(".json")) {
|
|
366
|
+
const jsoncPath = path2.replace(/\.json$/, ".jsonc");
|
|
367
|
+
return parseConfigFile(jsoncPath);
|
|
368
|
+
}
|
|
369
|
+
return { config: null };
|
|
370
|
+
}
|
|
371
|
+
function writeConfig(configPath, config) {
|
|
372
|
+
if (configPath.endsWith(".jsonc")) {
|
|
373
|
+
console.warn(
|
|
374
|
+
"[config-manager] Writing to .jsonc file - comments will not be preserved"
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
const tmpPath = `${configPath}.tmp`;
|
|
378
|
+
const bakPath = `${configPath}.bak`;
|
|
379
|
+
const content = `${JSON.stringify(config, null, 2)}
|
|
380
|
+
`;
|
|
381
|
+
if (existsSync2(configPath)) {
|
|
382
|
+
copyFileSync(configPath, bakPath);
|
|
383
|
+
}
|
|
384
|
+
writeFileSync(tmpPath, content);
|
|
385
|
+
renameSync(tmpPath, configPath);
|
|
386
|
+
}
|
|
387
|
+
function mergeOpenCodeMainConfig(config, update) {
|
|
388
|
+
if (update.ensurePlugin) {
|
|
389
|
+
const plugins = Array.isArray(config.plugin) ? config.plugin : [];
|
|
390
|
+
config.plugin = [
|
|
391
|
+
...plugins.filter(
|
|
392
|
+
(plugin) => plugin !== PACKAGE_NAME && !plugin.startsWith(`${PACKAGE_NAME}@`)
|
|
393
|
+
),
|
|
394
|
+
`${PACKAGE_NAME}@latest`
|
|
395
|
+
];
|
|
396
|
+
}
|
|
397
|
+
if (update.disableDefaults) {
|
|
398
|
+
const agent = config.agent && typeof config.agent === "object" ? { ...config.agent } : {};
|
|
399
|
+
const explore = agent.explore && typeof agent.explore === "object" ? { ...agent.explore } : {};
|
|
400
|
+
const general = agent.general && typeof agent.general === "object" ? { ...agent.general } : {};
|
|
401
|
+
agent.explore = { ...explore, disable: true };
|
|
402
|
+
agent.general = { ...general, disable: true };
|
|
403
|
+
config.agent = agent;
|
|
404
|
+
}
|
|
405
|
+
return config;
|
|
406
|
+
}
|
|
407
|
+
function updateOpenCodeMainConfig(update) {
|
|
408
|
+
const configPath = getExistingConfigPath();
|
|
409
|
+
try {
|
|
410
|
+
ensureOpenCodeConfigDir();
|
|
411
|
+
} catch (err) {
|
|
412
|
+
return {
|
|
413
|
+
success: false,
|
|
414
|
+
configPath,
|
|
415
|
+
error: `Failed to create config directory: ${err}`
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
try {
|
|
419
|
+
const { config: parsedConfig, error } = parseConfig(configPath);
|
|
420
|
+
if (error) {
|
|
421
|
+
return {
|
|
422
|
+
success: false,
|
|
423
|
+
configPath,
|
|
424
|
+
error: `Failed to parse config: ${error}`
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
writeConfig(
|
|
428
|
+
configPath,
|
|
429
|
+
mergeOpenCodeMainConfig(parsedConfig ?? {}, update)
|
|
430
|
+
);
|
|
431
|
+
return { success: true, configPath };
|
|
432
|
+
} catch (err) {
|
|
433
|
+
return {
|
|
434
|
+
success: false,
|
|
435
|
+
configPath,
|
|
436
|
+
error: `Failed to update opencode config: ${err}`
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
function writeLiteConfig(installConfig, targetPath) {
|
|
441
|
+
const configPath = targetPath ?? getLiteConfig();
|
|
442
|
+
try {
|
|
443
|
+
ensureConfigDir();
|
|
444
|
+
const config = generateLiteConfig(installConfig);
|
|
445
|
+
const tmpPath = `${configPath}.tmp`;
|
|
446
|
+
const bakPath = `${configPath}.bak`;
|
|
447
|
+
const content = `${JSON.stringify(config, null, 2)}
|
|
448
|
+
`;
|
|
449
|
+
if (existsSync2(configPath)) {
|
|
450
|
+
copyFileSync(configPath, bakPath);
|
|
451
|
+
}
|
|
452
|
+
writeFileSync(tmpPath, content);
|
|
453
|
+
renameSync(tmpPath, configPath);
|
|
454
|
+
return { success: true, configPath };
|
|
455
|
+
} catch (err) {
|
|
456
|
+
return {
|
|
457
|
+
success: false,
|
|
458
|
+
configPath,
|
|
459
|
+
error: `Failed to write lite config: ${err}`
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
function detectCurrentConfig() {
|
|
464
|
+
const result = {
|
|
465
|
+
isInstalled: false,
|
|
466
|
+
hasOpenAI: false,
|
|
467
|
+
hasTmux: false
|
|
468
|
+
};
|
|
469
|
+
const { config } = parseConfig(getExistingConfigPath());
|
|
470
|
+
if (!config) return result;
|
|
471
|
+
const plugins = config.plugin ?? [];
|
|
472
|
+
result.isInstalled = plugins.some((p) => p.startsWith(PACKAGE_NAME));
|
|
473
|
+
const { config: liteConfig } = parseConfig(getLiteConfig());
|
|
474
|
+
if (liteConfig && typeof liteConfig === "object") {
|
|
475
|
+
const configObj = liteConfig;
|
|
476
|
+
const presetName = configObj.preset;
|
|
477
|
+
const presets = configObj.presets;
|
|
478
|
+
const agents = presets?.[presetName];
|
|
479
|
+
if (agents) {
|
|
480
|
+
const models = Object.values(agents).map((a) => a?.model).filter(Boolean);
|
|
481
|
+
result.hasOpenAI = models.some((m) => m?.startsWith("openai/"));
|
|
482
|
+
}
|
|
483
|
+
if (configObj.tmux && typeof configObj.tmux === "object") {
|
|
484
|
+
const tmuxConfig = configObj.tmux;
|
|
485
|
+
result.hasTmux = tmuxConfig.enabled === true;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return result;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/config/loader.ts
|
|
492
|
+
var PROMPTS_DIR_NAME = "thoth-agents";
|
|
493
|
+
function loadConfigFromPath(configPath) {
|
|
494
|
+
try {
|
|
495
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
496
|
+
const rawConfig = JSON.parse(stripJsonComments(content));
|
|
497
|
+
const result = PluginConfigSchema.safeParse(rawConfig);
|
|
498
|
+
if (!result.success) {
|
|
499
|
+
console.warn(`[thoth-agents] Invalid config at ${configPath}:`);
|
|
500
|
+
console.warn(result.error.format());
|
|
501
|
+
return null;
|
|
502
|
+
}
|
|
503
|
+
return result.data;
|
|
504
|
+
} catch (error) {
|
|
505
|
+
if (error instanceof Error && "code" in error && error.code !== "ENOENT") {
|
|
506
|
+
console.warn(
|
|
507
|
+
`[thoth-agents] Error reading config from ${configPath}:`,
|
|
508
|
+
error.message
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
return null;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
function findConfigPath(basePath) {
|
|
515
|
+
const jsoncPath = `${basePath}.jsonc`;
|
|
516
|
+
const jsonPath = `${basePath}.json`;
|
|
517
|
+
if (fs.existsSync(jsoncPath)) {
|
|
518
|
+
return jsoncPath;
|
|
519
|
+
}
|
|
520
|
+
if (fs.existsSync(jsonPath)) {
|
|
521
|
+
return jsonPath;
|
|
522
|
+
}
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
function deepMerge(base, override) {
|
|
526
|
+
if (!base) return override;
|
|
527
|
+
if (!override) return base;
|
|
528
|
+
const result = { ...base };
|
|
529
|
+
for (const key of Object.keys(override)) {
|
|
530
|
+
const baseVal = base[key];
|
|
531
|
+
const overrideVal = override[key];
|
|
532
|
+
if (typeof baseVal === "object" && baseVal !== null && typeof overrideVal === "object" && overrideVal !== null && !Array.isArray(baseVal) && !Array.isArray(overrideVal)) {
|
|
533
|
+
result[key] = deepMerge(
|
|
534
|
+
baseVal,
|
|
535
|
+
overrideVal
|
|
536
|
+
);
|
|
537
|
+
} else {
|
|
538
|
+
result[key] = overrideVal;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return result;
|
|
542
|
+
}
|
|
543
|
+
function loadPluginConfig(directory) {
|
|
544
|
+
const userConfigBasePath = path.join(getConfigDir(), "thoth-agents");
|
|
545
|
+
const projectConfigBasePath = path.join(
|
|
546
|
+
directory,
|
|
547
|
+
".opencode",
|
|
548
|
+
"thoth-agents"
|
|
549
|
+
);
|
|
550
|
+
const userConfigPath = findConfigPath(userConfigBasePath);
|
|
551
|
+
const projectConfigPath = findConfigPath(projectConfigBasePath);
|
|
552
|
+
let config = userConfigPath ? loadConfigFromPath(userConfigPath) ?? {} : {};
|
|
553
|
+
const projectConfig = projectConfigPath ? loadConfigFromPath(projectConfigPath) : null;
|
|
554
|
+
if (projectConfig) {
|
|
555
|
+
config = {
|
|
556
|
+
...config,
|
|
557
|
+
...projectConfig,
|
|
558
|
+
agents: deepMerge(config.agents, projectConfig.agents),
|
|
559
|
+
tmux: deepMerge(config.tmux, projectConfig.tmux),
|
|
560
|
+
fallback: deepMerge(config.fallback, projectConfig.fallback),
|
|
561
|
+
codex: deepMerge(config.codex, projectConfig.codex),
|
|
562
|
+
claudeCode: deepMerge(config.claudeCode, projectConfig.claudeCode)
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
const envPreset = process.env.THOTH_AGENTS_PRESET;
|
|
566
|
+
if (envPreset) {
|
|
567
|
+
config.preset = envPreset;
|
|
568
|
+
}
|
|
569
|
+
if (config.preset) {
|
|
570
|
+
const preset = config.presets?.[config.preset];
|
|
571
|
+
if (preset) {
|
|
572
|
+
config.agents = deepMerge(preset, config.agents);
|
|
573
|
+
} else {
|
|
574
|
+
const presetSource = envPreset === config.preset ? "environment variable" : "config file";
|
|
575
|
+
const availablePresets = config.presets ? Object.keys(config.presets).join(", ") : "none";
|
|
576
|
+
console.warn(
|
|
577
|
+
`[thoth-agents] Preset "${config.preset}" not found (from ${presetSource}). Available presets: ${availablePresets}`
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return config;
|
|
582
|
+
}
|
|
583
|
+
function loadAgentPrompt(agentName, preset) {
|
|
584
|
+
const presetDirName = preset && /^[a-zA-Z0-9_-]+$/.test(preset) ? preset : void 0;
|
|
585
|
+
const promptsDir = path.join(getConfigDir(), PROMPTS_DIR_NAME);
|
|
586
|
+
const promptSearchDirs = presetDirName ? [path.join(promptsDir, presetDirName), promptsDir] : [promptsDir];
|
|
587
|
+
const result = {};
|
|
588
|
+
const readFirstPrompt = (fileName, errorPrefix) => {
|
|
589
|
+
for (const dir of promptSearchDirs) {
|
|
590
|
+
const promptPath = path.join(dir, fileName);
|
|
591
|
+
if (!fs.existsSync(promptPath)) {
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
try {
|
|
595
|
+
return fs.readFileSync(promptPath, "utf-8");
|
|
596
|
+
} catch (error) {
|
|
597
|
+
console.warn(
|
|
598
|
+
`[thoth-agents] ${errorPrefix} ${promptPath}:`,
|
|
599
|
+
error instanceof Error ? error.message : String(error)
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
return void 0;
|
|
604
|
+
};
|
|
605
|
+
result.prompt = readFirstPrompt(
|
|
606
|
+
`${agentName}.md`,
|
|
607
|
+
"Error reading prompt file"
|
|
608
|
+
);
|
|
609
|
+
result.appendPrompt = readFirstPrompt(
|
|
610
|
+
`${agentName}_append.md`,
|
|
611
|
+
"Error reading append prompt file"
|
|
612
|
+
);
|
|
613
|
+
return result;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// src/agents/prompt-dialects.ts
|
|
617
|
+
var OPENCODE_CAPABILITIES = {
|
|
618
|
+
agentDefinitions: "supported",
|
|
619
|
+
delegatedExecution: "supported",
|
|
620
|
+
parallelDelegation: "supported",
|
|
621
|
+
runtimeHooks: "supported",
|
|
622
|
+
mcpConfiguration: "supported",
|
|
623
|
+
skillPackaging: "supported",
|
|
624
|
+
rolePermissions: "supported",
|
|
625
|
+
parentContextInjection: "supported",
|
|
626
|
+
memoryGovernanceEnforcement: "supported"
|
|
627
|
+
};
|
|
628
|
+
var CODEX_PROMPT_CAPABILITIES = {
|
|
629
|
+
agentDefinitions: "supported",
|
|
630
|
+
delegatedExecution: "supported",
|
|
631
|
+
parallelDelegation: "supported",
|
|
632
|
+
runtimeHooks: "unknown",
|
|
633
|
+
mcpConfiguration: "supported",
|
|
634
|
+
skillPackaging: "supported",
|
|
635
|
+
rolePermissions: "instruction-only",
|
|
636
|
+
parentContextInjection: "instruction-only",
|
|
637
|
+
memoryGovernanceEnforcement: "instruction-only"
|
|
638
|
+
};
|
|
639
|
+
var CLAUDE_CODE_PROMPT_CAPABILITIES = {
|
|
640
|
+
agentDefinitions: "supported",
|
|
641
|
+
delegatedExecution: "supported",
|
|
642
|
+
parallelDelegation: "supported",
|
|
643
|
+
runtimeHooks: "supported",
|
|
644
|
+
mcpConfiguration: "supported",
|
|
645
|
+
skillPackaging: "supported",
|
|
646
|
+
rolePermissions: "supported",
|
|
647
|
+
parentContextInjection: "supported",
|
|
648
|
+
memoryGovernanceEnforcement: "instruction-only"
|
|
649
|
+
};
|
|
650
|
+
function supportedCapabilityProfile(capabilities) {
|
|
651
|
+
return {
|
|
652
|
+
capabilities,
|
|
653
|
+
renderCapabilityDisclosure: () => void 0
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
function codexCapabilityDisclosure(capability) {
|
|
657
|
+
const status = CODEX_PROMPT_CAPABILITIES[capability];
|
|
658
|
+
if (status === "supported") {
|
|
659
|
+
return void 0;
|
|
660
|
+
}
|
|
661
|
+
if (status === "unknown") {
|
|
662
|
+
return `${capability}: unknown in Codex; treat related behavior as diagnostic-only unless the active Codex host documents support.`;
|
|
663
|
+
}
|
|
664
|
+
return `${capability}: ${status} in Codex; preserve the role responsibility as prompt guidance because equivalent runtime enforcement is not guaranteed.`;
|
|
665
|
+
}
|
|
666
|
+
function claudeCodeCapabilityDisclosure(capability) {
|
|
667
|
+
const status = CLAUDE_CODE_PROMPT_CAPABILITIES[capability];
|
|
668
|
+
if (status === "supported") {
|
|
669
|
+
return void 0;
|
|
670
|
+
}
|
|
671
|
+
return `${capability}: ${status} in Claude Code; installed provider guidance owns provider-dependent enforcement and mechanics.`;
|
|
672
|
+
}
|
|
673
|
+
var OPENCODE_PROMPT_DIALECT = {
|
|
674
|
+
harness: "opencode",
|
|
675
|
+
tools: {
|
|
676
|
+
delegationTool: "task",
|
|
677
|
+
backgroundDelegationTool: "task(background=true)",
|
|
678
|
+
backgroundStatusTool: "task_status",
|
|
679
|
+
userQuestionTool: "question",
|
|
680
|
+
progressTool: "todowrite",
|
|
681
|
+
hostStatusSurface: "task_status",
|
|
682
|
+
lifecycle: {
|
|
683
|
+
statusAction: "wait, poll, and collect",
|
|
684
|
+
terminalState: "terminal task_status result",
|
|
685
|
+
nonterminalState: "nonterminal task_status result",
|
|
686
|
+
sameSessionProbe: "task_status on the same task session",
|
|
687
|
+
enforcement: "runtime-supported"
|
|
688
|
+
},
|
|
689
|
+
roleReference: (role) => `@${role}`
|
|
690
|
+
},
|
|
691
|
+
capabilities: supportedCapabilityProfile(OPENCODE_CAPABILITIES),
|
|
692
|
+
dispatchLabel(method) {
|
|
693
|
+
switch (method) {
|
|
694
|
+
case "root-coordinator":
|
|
695
|
+
return "root coordinator";
|
|
696
|
+
case "task":
|
|
697
|
+
return "task";
|
|
698
|
+
case "synchronous-task-only":
|
|
699
|
+
return "synchronous task only";
|
|
700
|
+
}
|
|
701
|
+
},
|
|
702
|
+
renderRoleInvocation(role) {
|
|
703
|
+
return `@${role}`;
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
var CODEX_PROMPT_DIALECT = {
|
|
707
|
+
harness: "codex",
|
|
708
|
+
tools: {
|
|
709
|
+
delegationTool: "collaboration.spawn_agent",
|
|
710
|
+
backgroundDelegationTool: "collaboration.spawn_agent",
|
|
711
|
+
backgroundStatusTool: "collaboration.wait_agent",
|
|
712
|
+
userQuestionTool: "request_user_input",
|
|
713
|
+
progressTool: "functions.update_plan",
|
|
714
|
+
hostStatusSurface: "collaboration.list_agents",
|
|
715
|
+
lifecycle: {
|
|
716
|
+
statusAction: "wait and inspect status",
|
|
717
|
+
terminalState: "terminal mailbox completion or failure update",
|
|
718
|
+
nonterminalState: "collaboration.wait_agent timeout or silence",
|
|
719
|
+
sameSessionProbe: "collaboration.list_agents on the same task path",
|
|
720
|
+
enforcement: "runtime-supported"
|
|
721
|
+
},
|
|
722
|
+
roleReference: (role) => `${role} role agent`
|
|
723
|
+
},
|
|
724
|
+
capabilities: {
|
|
725
|
+
capabilities: CODEX_PROMPT_CAPABILITIES,
|
|
726
|
+
renderCapabilityDisclosure: codexCapabilityDisclosure
|
|
727
|
+
},
|
|
728
|
+
dispatchLabel(method) {
|
|
729
|
+
switch (method) {
|
|
730
|
+
case "root-coordinator":
|
|
731
|
+
return "ambient Codex root session coordinator";
|
|
732
|
+
case "task":
|
|
733
|
+
return "collaboration.spawn_agent";
|
|
734
|
+
case "synchronous-task-only":
|
|
735
|
+
return "synchronous collaboration.spawn_agent only";
|
|
736
|
+
}
|
|
737
|
+
},
|
|
738
|
+
renderRoleInvocation(role) {
|
|
739
|
+
return role === "orchestrator" ? "orchestrator role agent" : `${role} subagent`;
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
var CLAUDE_CODE_SUBAGENT_NAMESPACE = "thoth-agents";
|
|
743
|
+
function claudeCodeSubagentType(role) {
|
|
744
|
+
return `${CLAUDE_CODE_SUBAGENT_NAMESPACE}:${role}`;
|
|
745
|
+
}
|
|
746
|
+
var CLAUDE_CODE_PROMPT_DIALECT = {
|
|
747
|
+
harness: "claude",
|
|
748
|
+
tools: {
|
|
749
|
+
delegationTool: "Agent",
|
|
750
|
+
backgroundDelegationTool: "Agent(run_in_background=true)",
|
|
751
|
+
backgroundStatusTool: "TaskOutput",
|
|
752
|
+
userQuestionTool: "AskUserQuestion",
|
|
753
|
+
progressTool: "TodoWrite",
|
|
754
|
+
hostStatusSurface: "TodoWrite",
|
|
755
|
+
lifecycle: {
|
|
756
|
+
statusAction: "wait, inspect, and collect",
|
|
757
|
+
terminalState: "terminal TaskOutput result",
|
|
758
|
+
nonterminalState: "nonterminal TaskOutput result",
|
|
759
|
+
sameSessionProbe: "TaskOutput on the same task session",
|
|
760
|
+
enforcement: "runtime-supported"
|
|
761
|
+
},
|
|
762
|
+
roleReference: (role) => role === "orchestrator" ? "the main-thread orchestrator" : `Agent(subagent_type: ${claudeCodeSubagentType(role)})`
|
|
763
|
+
},
|
|
764
|
+
capabilities: {
|
|
765
|
+
capabilities: CLAUDE_CODE_PROMPT_CAPABILITIES,
|
|
766
|
+
renderCapabilityDisclosure: claudeCodeCapabilityDisclosure
|
|
767
|
+
},
|
|
768
|
+
dispatchLabel(method) {
|
|
769
|
+
switch (method) {
|
|
770
|
+
case "root-coordinator":
|
|
771
|
+
return "main-session coordinator";
|
|
772
|
+
case "task":
|
|
773
|
+
return "Agent tool";
|
|
774
|
+
case "synchronous-task-only":
|
|
775
|
+
return "synchronous Agent only";
|
|
776
|
+
}
|
|
777
|
+
},
|
|
778
|
+
renderRoleInvocation(role) {
|
|
779
|
+
return role === "orchestrator" ? "main-thread orchestrator" : claudeCodeSubagentType(role);
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
// src/harness/core/agent-pack.ts
|
|
784
|
+
var AGENT_ROLES = [
|
|
785
|
+
{
|
|
786
|
+
name: "orchestrator",
|
|
787
|
+
mode: "adaptive-root",
|
|
788
|
+
dispatch: "root-coordinator",
|
|
789
|
+
canMutateWorkspace: true,
|
|
790
|
+
scope: "requirements, SDD coordination, routing, bounded direct work, decisions, and synthesis",
|
|
791
|
+
responsibility: "Keep the task coherent, own sequential specification, planning, task, convergence, and archive coordination, act directly when work is clear and bounded, and delegate only for net gain.",
|
|
792
|
+
toolGovernance: [
|
|
793
|
+
"may inspect and edit bounded direct work, but every verification is delegated to oracle",
|
|
794
|
+
"loads the matching thoth-sdd phase contract on demand instead of carrying every phase protocol in its prompt",
|
|
795
|
+
"owns governed coordination writes under openspec/ and uses append-only tasks.md updates during convergence",
|
|
796
|
+
"delegates independent or specialist work only when it produces a net gain",
|
|
797
|
+
"keeps requirements, decisions, and final synthesis in the root thread"
|
|
798
|
+
],
|
|
799
|
+
verification: [
|
|
800
|
+
"delegates analyze and every verify phase to oracle, including Direct and Accelerated routes",
|
|
801
|
+
"consolidates summarized evidence returned by child agents"
|
|
802
|
+
]
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
name: "explorer",
|
|
806
|
+
mode: "read-only",
|
|
807
|
+
dispatch: "task",
|
|
808
|
+
canMutateWorkspace: false,
|
|
809
|
+
scope: "local repository discovery",
|
|
810
|
+
responsibility: "Resolve broad or uncertain repository questions and return distilled evidence.",
|
|
811
|
+
toolGovernance: [
|
|
812
|
+
"uses read, search, and code-navigation tools only",
|
|
813
|
+
"does not mutate files or delegate further"
|
|
814
|
+
],
|
|
815
|
+
verification: ["reports inspected paths, confidence, and remaining gaps"]
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
name: "librarian",
|
|
819
|
+
mode: "read-only",
|
|
820
|
+
dispatch: "task",
|
|
821
|
+
canMutateWorkspace: false,
|
|
822
|
+
scope: "authoritative external research with local confirmation when needed",
|
|
823
|
+
responsibility: "Gather current authoritative evidence and separate documented facts from inference.",
|
|
824
|
+
toolGovernance: [
|
|
825
|
+
"uses research and read-only local tools",
|
|
826
|
+
"does not mutate files or delegate further"
|
|
827
|
+
],
|
|
828
|
+
verification: ["provides direct sources for substantive external claims"]
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
name: "oracle",
|
|
832
|
+
mode: "read-only",
|
|
833
|
+
dispatch: "synchronous-task-only",
|
|
834
|
+
canMutateWorkspace: false,
|
|
835
|
+
scope: "diagnosis, architecture, analysis, and independent verification",
|
|
836
|
+
responsibility: "Independently analyze plans and perform every implementation verification, exposing correctness risks and judging whether results satisfy their contracts.",
|
|
837
|
+
toolGovernance: [
|
|
838
|
+
"performs read-only analysis and independent review and is never the implementer of the work it judges",
|
|
839
|
+
"does not implement, persist artifacts, or delegate further"
|
|
840
|
+
],
|
|
841
|
+
verification: ["separates observations, risks, and recommendations"]
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
name: "designer",
|
|
845
|
+
mode: "write-capable",
|
|
846
|
+
dispatch: "synchronous-task-only",
|
|
847
|
+
canMutateWorkspace: true,
|
|
848
|
+
scope: "UI/UX decisions, implementation, and visual verification",
|
|
849
|
+
responsibility: "Own user-facing implementation choices and visual quality for UI work.",
|
|
850
|
+
toolGovernance: [
|
|
851
|
+
"may edit focused UI/UX files",
|
|
852
|
+
"owns screenshots and visual QA",
|
|
853
|
+
"does not delegate further"
|
|
854
|
+
],
|
|
855
|
+
verification: ["includes visual verification when applicable"]
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
name: "quick",
|
|
859
|
+
mode: "write-capable",
|
|
860
|
+
dispatch: "synchronous-task-only",
|
|
861
|
+
canMutateWorkspace: true,
|
|
862
|
+
scope: "fast bounded implementation",
|
|
863
|
+
responsibility: "Implement narrow, clear, low-risk changes within an explicitly bounded surface.",
|
|
864
|
+
toolGovernance: [
|
|
865
|
+
"edits only bounded targets",
|
|
866
|
+
"escalates when discovery or correctness risk exceeds the assignment",
|
|
867
|
+
"does not delegate further"
|
|
868
|
+
],
|
|
869
|
+
verification: ["runs the smallest sufficient focused check"]
|
|
870
|
+
},
|
|
871
|
+
{
|
|
872
|
+
name: "deep",
|
|
873
|
+
mode: "write-capable",
|
|
874
|
+
dispatch: "synchronous-task-only",
|
|
875
|
+
canMutateWorkspace: true,
|
|
876
|
+
scope: "correctness-critical implementation and verification",
|
|
877
|
+
responsibility: "Handle multi-file, edge-case-heavy, or high-risk implementation with full local context.",
|
|
878
|
+
toolGovernance: [
|
|
879
|
+
"may edit implementation and tests within the assigned surface",
|
|
880
|
+
"validates shared behavior against related code and call sites",
|
|
881
|
+
"does not delegate further"
|
|
882
|
+
],
|
|
883
|
+
verification: ["reports focused checks and relevant edge-case evidence"]
|
|
884
|
+
}
|
|
885
|
+
];
|
|
886
|
+
var ORCHESTRATION_POLICY = {
|
|
887
|
+
maxDelegationDepth: 1,
|
|
888
|
+
singleWriter: true,
|
|
889
|
+
rules: [
|
|
890
|
+
"The root performs bounded direct work when scope and intent are clear.",
|
|
891
|
+
"Delegate only when specialization, context isolation, or independent parallel work creates a net gain.",
|
|
892
|
+
"Prefer child agents for read-heavy exploration, research, analysis, and verification.",
|
|
893
|
+
"Use one writer for each mutable surface and never parallelize overlapping writes.",
|
|
894
|
+
"Child agents return distilled evidence instead of raw logs or file dumps."
|
|
895
|
+
]
|
|
896
|
+
};
|
|
897
|
+
var AGENT_RETURN_CONTRACT = [
|
|
898
|
+
"conclusion",
|
|
899
|
+
"evidence",
|
|
900
|
+
"verification",
|
|
901
|
+
"risks",
|
|
902
|
+
"openQuestions",
|
|
903
|
+
"nextAction"
|
|
904
|
+
];
|
|
905
|
+
var VERIFICATION_PROTOCOL = [
|
|
906
|
+
"Completion reports identify changed files and verification evidence.",
|
|
907
|
+
"Behavior changes receive the smallest sufficient automated check or a declared manual check.",
|
|
908
|
+
"Visual changes receive designer-owned visual QA when applicable."
|
|
909
|
+
];
|
|
910
|
+
var AGENT_PACK_CONTRACT = {
|
|
911
|
+
roles: [...AGENT_ROLES],
|
|
912
|
+
orchestrationPolicy: {
|
|
913
|
+
...ORCHESTRATION_POLICY,
|
|
914
|
+
rules: [...ORCHESTRATION_POLICY.rules]
|
|
915
|
+
},
|
|
916
|
+
returnContract: [...AGENT_RETURN_CONTRACT],
|
|
917
|
+
verificationProtocol: [...VERIFICATION_PROTOCOL]
|
|
918
|
+
};
|
|
919
|
+
function getAgentRole(name) {
|
|
920
|
+
const role = AGENT_ROLES.find((candidate) => candidate.name === name);
|
|
921
|
+
if (!role) {
|
|
922
|
+
throw new Error(`Unknown agent role: ${name}`);
|
|
923
|
+
}
|
|
924
|
+
return role;
|
|
925
|
+
}
|
|
926
|
+
function getAgentPackContract() {
|
|
927
|
+
return {
|
|
928
|
+
roles: AGENT_PACK_CONTRACT.roles.map((role) => ({
|
|
929
|
+
...role,
|
|
930
|
+
writeScope: role.writeScope ? [...role.writeScope] : void 0,
|
|
931
|
+
toolGovernance: [...role.toolGovernance],
|
|
932
|
+
verification: [...role.verification]
|
|
933
|
+
})),
|
|
934
|
+
orchestrationPolicy: {
|
|
935
|
+
...AGENT_PACK_CONTRACT.orchestrationPolicy,
|
|
936
|
+
rules: [...AGENT_PACK_CONTRACT.orchestrationPolicy.rules]
|
|
937
|
+
},
|
|
938
|
+
returnContract: [...AGENT_PACK_CONTRACT.returnContract],
|
|
939
|
+
verificationProtocol: [...AGENT_PACK_CONTRACT.verificationProtocol]
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// src/harness/core/sdd.ts
|
|
944
|
+
var SDD_PHASES = [
|
|
945
|
+
{
|
|
946
|
+
id: "explore",
|
|
947
|
+
order: 0,
|
|
948
|
+
availableFor: ["full"],
|
|
949
|
+
requiredFor: ["full"],
|
|
950
|
+
activation: "required",
|
|
951
|
+
prerequisites: [],
|
|
952
|
+
producesArtifact: false,
|
|
953
|
+
defaultAgentRole: "explorer",
|
|
954
|
+
eligibleAgentRoles: ["explorer"],
|
|
955
|
+
reason: "Resolve broad repository uncertainty before requirements are fixed."
|
|
956
|
+
},
|
|
957
|
+
{
|
|
958
|
+
id: "specify",
|
|
959
|
+
order: 1,
|
|
960
|
+
availableFor: ["accelerated", "full"],
|
|
961
|
+
requiredFor: ["accelerated", "full"],
|
|
962
|
+
activation: "required",
|
|
963
|
+
prerequisites: ["explore"],
|
|
964
|
+
producesArtifact: true,
|
|
965
|
+
defaultAgentRole: "orchestrator",
|
|
966
|
+
eligibleAgentRoles: ["orchestrator"],
|
|
967
|
+
reason: "Define the user-visible requirements and acceptance contract."
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
id: "clarify",
|
|
971
|
+
order: 2,
|
|
972
|
+
availableFor: ["accelerated", "full"],
|
|
973
|
+
requiredFor: [],
|
|
974
|
+
activation: "conditional",
|
|
975
|
+
prerequisites: ["specify"],
|
|
976
|
+
producesArtifact: false,
|
|
977
|
+
defaultAgentRole: "orchestrator",
|
|
978
|
+
eligibleAgentRoles: ["orchestrator"],
|
|
979
|
+
reason: "Resolve only material ambiguity that would change the solution.",
|
|
980
|
+
condition: "Activate when unresolved decisions cannot be handled by a safe local assumption."
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
id: "plan",
|
|
984
|
+
order: 3,
|
|
985
|
+
availableFor: ["accelerated", "full"],
|
|
986
|
+
requiredFor: ["accelerated", "full"],
|
|
987
|
+
activation: "required",
|
|
988
|
+
prerequisites: ["specify"],
|
|
989
|
+
producesArtifact: true,
|
|
990
|
+
defaultAgentRole: "orchestrator",
|
|
991
|
+
eligibleAgentRoles: ["orchestrator"],
|
|
992
|
+
reason: "Translate requirements into an executable technical approach."
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
id: "checklist",
|
|
996
|
+
order: 4,
|
|
997
|
+
availableFor: ["accelerated", "full"],
|
|
998
|
+
requiredFor: [],
|
|
999
|
+
activation: "conditional",
|
|
1000
|
+
prerequisites: ["specify", "plan"],
|
|
1001
|
+
producesArtifact: true,
|
|
1002
|
+
defaultAgentRole: "orchestrator",
|
|
1003
|
+
eligibleAgentRoles: ["orchestrator"],
|
|
1004
|
+
reason: "Audit requirement quality when risk justifies an explicit checklist.",
|
|
1005
|
+
condition: "Activate for high-risk, compliance-sensitive, or ambiguity-prone requirements."
|
|
1006
|
+
},
|
|
1007
|
+
{
|
|
1008
|
+
id: "tasks",
|
|
1009
|
+
order: 5,
|
|
1010
|
+
availableFor: ["accelerated", "full"],
|
|
1011
|
+
requiredFor: ["accelerated", "full"],
|
|
1012
|
+
activation: "required",
|
|
1013
|
+
prerequisites: ["specify", "plan"],
|
|
1014
|
+
producesArtifact: true,
|
|
1015
|
+
defaultAgentRole: "orchestrator",
|
|
1016
|
+
eligibleAgentRoles: ["orchestrator"],
|
|
1017
|
+
reason: "Produce dependency-ordered implementation slices with verification."
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
id: "analyze",
|
|
1021
|
+
order: 6,
|
|
1022
|
+
availableFor: ["full"],
|
|
1023
|
+
requiredFor: ["full"],
|
|
1024
|
+
activation: "required",
|
|
1025
|
+
prerequisites: ["specify", "plan", "tasks"],
|
|
1026
|
+
producesArtifact: false,
|
|
1027
|
+
defaultAgentRole: "oracle",
|
|
1028
|
+
eligibleAgentRoles: ["oracle"],
|
|
1029
|
+
reason: "Independently check cross-artifact consistency before high-risk implementation."
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
id: "implement",
|
|
1033
|
+
order: 7,
|
|
1034
|
+
availableFor: ["direct", "accelerated", "full"],
|
|
1035
|
+
requiredFor: ["direct", "accelerated", "full"],
|
|
1036
|
+
activation: "required",
|
|
1037
|
+
prerequisites: ["tasks", "analyze"],
|
|
1038
|
+
producesArtifact: false,
|
|
1039
|
+
defaultAgentRole: "orchestrator",
|
|
1040
|
+
eligibleAgentRoles: ["orchestrator", "designer", "quick", "deep"],
|
|
1041
|
+
reason: "Let the adaptive root act directly or route the settled work to one writer."
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
id: "verify",
|
|
1045
|
+
order: 8,
|
|
1046
|
+
availableFor: ["direct", "accelerated", "full"],
|
|
1047
|
+
requiredFor: ["direct", "accelerated", "full"],
|
|
1048
|
+
activation: "required",
|
|
1049
|
+
prerequisites: ["implement"],
|
|
1050
|
+
producesArtifact: true,
|
|
1051
|
+
defaultAgentRole: "oracle",
|
|
1052
|
+
eligibleAgentRoles: ["oracle"],
|
|
1053
|
+
reason: "Independently judge the result against requirements, contracts, and focused checks."
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
id: "converge",
|
|
1057
|
+
order: 9,
|
|
1058
|
+
availableFor: ["accelerated", "full"],
|
|
1059
|
+
requiredFor: [],
|
|
1060
|
+
activation: "conditional",
|
|
1061
|
+
prerequisites: ["verify"],
|
|
1062
|
+
producesArtifact: true,
|
|
1063
|
+
defaultAgentRole: "orchestrator",
|
|
1064
|
+
eligibleAgentRoles: ["orchestrator"],
|
|
1065
|
+
reason: "Append traceable remaining work to tasks.md before another implementation loop.",
|
|
1066
|
+
condition: "Activate only when verification finds actionable defects."
|
|
1067
|
+
},
|
|
1068
|
+
{
|
|
1069
|
+
id: "archive",
|
|
1070
|
+
order: 10,
|
|
1071
|
+
availableFor: ["accelerated", "full"],
|
|
1072
|
+
requiredFor: ["accelerated", "full"],
|
|
1073
|
+
activation: "required",
|
|
1074
|
+
prerequisites: ["verify"],
|
|
1075
|
+
producesArtifact: true,
|
|
1076
|
+
defaultAgentRole: "orchestrator",
|
|
1077
|
+
eligibleAgentRoles: ["orchestrator"],
|
|
1078
|
+
reason: "Transactionally sync declared durable deltas and close verified work with a dated audit trail."
|
|
1079
|
+
}
|
|
1080
|
+
];
|
|
1081
|
+
var SDD_ARTIFACT_GRAPH = [
|
|
1082
|
+
{
|
|
1083
|
+
id: "spec",
|
|
1084
|
+
path: "spec.md",
|
|
1085
|
+
producedBy: "specify",
|
|
1086
|
+
consumes: [],
|
|
1087
|
+
requiredFor: ["accelerated", "full"]
|
|
1088
|
+
},
|
|
1089
|
+
{
|
|
1090
|
+
id: "plan",
|
|
1091
|
+
path: "plan.md",
|
|
1092
|
+
producedBy: "plan",
|
|
1093
|
+
consumes: ["spec"],
|
|
1094
|
+
requiredFor: ["accelerated", "full"]
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
id: "tasks",
|
|
1098
|
+
path: "tasks.md",
|
|
1099
|
+
producedBy: "tasks",
|
|
1100
|
+
consumes: ["spec", "plan"],
|
|
1101
|
+
requiredFor: ["accelerated", "full"]
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
id: "requirements-checklist",
|
|
1105
|
+
path: "checklists/requirements.md",
|
|
1106
|
+
producedBy: "checklist",
|
|
1107
|
+
consumes: ["spec"],
|
|
1108
|
+
requiredFor: []
|
|
1109
|
+
},
|
|
1110
|
+
{
|
|
1111
|
+
id: "research",
|
|
1112
|
+
path: "research.md",
|
|
1113
|
+
producedBy: "plan",
|
|
1114
|
+
consumes: ["spec"],
|
|
1115
|
+
requiredFor: []
|
|
1116
|
+
},
|
|
1117
|
+
{
|
|
1118
|
+
id: "data-model",
|
|
1119
|
+
path: "data-model.md",
|
|
1120
|
+
producedBy: "plan",
|
|
1121
|
+
consumes: ["spec"],
|
|
1122
|
+
requiredFor: []
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
id: "contracts",
|
|
1126
|
+
path: "contracts/",
|
|
1127
|
+
producedBy: "plan",
|
|
1128
|
+
consumes: ["spec"],
|
|
1129
|
+
requiredFor: []
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
id: "quickstart",
|
|
1133
|
+
path: "quickstart.md",
|
|
1134
|
+
producedBy: "plan",
|
|
1135
|
+
consumes: ["spec", "plan"],
|
|
1136
|
+
requiredFor: []
|
|
1137
|
+
},
|
|
1138
|
+
{
|
|
1139
|
+
id: "verify-report",
|
|
1140
|
+
path: "verify-report.md",
|
|
1141
|
+
producedBy: "verify",
|
|
1142
|
+
consumes: ["spec", "plan", "tasks"],
|
|
1143
|
+
requiredFor: ["accelerated", "full"]
|
|
1144
|
+
},
|
|
1145
|
+
{
|
|
1146
|
+
id: "archive-report",
|
|
1147
|
+
path: "archive-report.md",
|
|
1148
|
+
producedBy: "archive",
|
|
1149
|
+
consumes: ["spec", "plan", "tasks", "verify-report"],
|
|
1150
|
+
requiredFor: ["accelerated", "full"]
|
|
1151
|
+
}
|
|
1152
|
+
];
|
|
1153
|
+
var SDD_ROUTE_EXECUTION_POLICIES = [
|
|
1154
|
+
{
|
|
1155
|
+
route: "direct",
|
|
1156
|
+
planningMode: "none",
|
|
1157
|
+
validationGates: [],
|
|
1158
|
+
optionalArtifactsByDefault: false,
|
|
1159
|
+
routineUserPauses: false,
|
|
1160
|
+
artifactRevisionPolicy: "none"
|
|
1161
|
+
},
|
|
1162
|
+
{
|
|
1163
|
+
route: "accelerated",
|
|
1164
|
+
planningMode: "fast-forward",
|
|
1165
|
+
validationGates: ["specify", "ready", "closeout"],
|
|
1166
|
+
optionalArtifactsByDefault: false,
|
|
1167
|
+
routineUserPauses: false,
|
|
1168
|
+
artifactRevisionPolicy: "revalidate-affected-downstream"
|
|
1169
|
+
},
|
|
1170
|
+
{
|
|
1171
|
+
route: "full",
|
|
1172
|
+
planningMode: "gated",
|
|
1173
|
+
validationGates: ["specify", "plan", "tasks", "ready", "closeout"],
|
|
1174
|
+
optionalArtifactsByDefault: false,
|
|
1175
|
+
routineUserPauses: false,
|
|
1176
|
+
artifactRevisionPolicy: "revalidate-affected-downstream"
|
|
1177
|
+
}
|
|
1178
|
+
];
|
|
1179
|
+
var SDD_WORKFLOW_CONTRACT = {
|
|
1180
|
+
artifactRoot: "openspec/changes/<feature>/",
|
|
1181
|
+
phases: [...SDD_PHASES],
|
|
1182
|
+
routePolicies: SDD_ROUTE_EXECUTION_POLICIES.map((policy) => ({
|
|
1183
|
+
...policy,
|
|
1184
|
+
validationGates: [...policy.validationGates]
|
|
1185
|
+
})),
|
|
1186
|
+
routingRules: [
|
|
1187
|
+
"An explicitly requested direct, accelerated, or full route wins; a generic SDD request sets accelerated as the minimum route.",
|
|
1188
|
+
"Direct work is the default for clear, bounded, low-risk changes; documentation and mechanical work may remain direct across multiple files.",
|
|
1189
|
+
"Accelerated SDD is used for multi-surface behavior, architecture, partial clarity, moderate risk, or broad non-behavioral coordination.",
|
|
1190
|
+
"Accelerated planning fast-forwards specify, plan, and tasks in one uninterrupted root pass without routine user pauses.",
|
|
1191
|
+
"Full SDD is used for unresolved scope, cross-cutting behavior or architecture, high contract risk, or high failure cost.",
|
|
1192
|
+
"Use architectural-grilling before specification only when the user explicitly requests it or material product or architecture decisions remain human-owned and unresolved; never require it merely because the route is Full.",
|
|
1193
|
+
"User input is requested only when a material unresolved decision would change the result."
|
|
1194
|
+
],
|
|
1195
|
+
artifactRules: [
|
|
1196
|
+
"Spec Kit artifact semantics are preserved inside the governed openspec store.",
|
|
1197
|
+
"Accelerated and full routes require spec.md, plan.md, tasks.md, verify-report.md, and archive-report.md.",
|
|
1198
|
+
"Research, data model, contracts, quickstart, and requirements checklist are created only when useful.",
|
|
1199
|
+
"The adaptive root writes coordination artifacts after loading the matching bundled phase contract; implementation ownership stays with the root or one writer role.",
|
|
1200
|
+
"After oracle PASS, archive transactionally synchronizes only explicitly declared durable ADDED, MODIFIED, REMOVED, and RENAMED requirement deltas into openspec/specs; INTERNAL requirements and undeclared prose never update permanent specifications.",
|
|
1201
|
+
"Archive stages and backs up writes so handled failures roll back within the active process; forced process or operating-system termination is not crash-atomic."
|
|
1202
|
+
],
|
|
1203
|
+
verificationRules: [
|
|
1204
|
+
"Every route delegates focused verification to read-only oracle; the implementation writer never verifies its own work.",
|
|
1205
|
+
"Full SDD delegates independent cross-artifact analysis to oracle before implementation.",
|
|
1206
|
+
"Accelerated and full verification persists verify-report.md with a pass or fail verdict and requirement compliance matrix.",
|
|
1207
|
+
"An artifact-backed fail verdict routes through append-only convergence, implementation, and verification again; direct work returns straight to implementation.",
|
|
1208
|
+
"A pass verdict is required before accelerated or full work can archive."
|
|
1209
|
+
]
|
|
1210
|
+
};
|
|
1211
|
+
function clonePhase(phase) {
|
|
1212
|
+
return {
|
|
1213
|
+
...phase,
|
|
1214
|
+
availableFor: [...phase.availableFor],
|
|
1215
|
+
requiredFor: [...phase.requiredFor],
|
|
1216
|
+
prerequisites: [...phase.prerequisites],
|
|
1217
|
+
eligibleAgentRoles: [...phase.eligibleAgentRoles]
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
function getSddWorkflowContract() {
|
|
1221
|
+
return {
|
|
1222
|
+
artifactRoot: SDD_WORKFLOW_CONTRACT.artifactRoot,
|
|
1223
|
+
phases: SDD_WORKFLOW_CONTRACT.phases.map(clonePhase),
|
|
1224
|
+
routePolicies: SDD_WORKFLOW_CONTRACT.routePolicies.map((policy) => ({
|
|
1225
|
+
...policy,
|
|
1226
|
+
validationGates: [...policy.validationGates]
|
|
1227
|
+
})),
|
|
1228
|
+
routingRules: [...SDD_WORKFLOW_CONTRACT.routingRules],
|
|
1229
|
+
artifactRules: [...SDD_WORKFLOW_CONTRACT.artifactRules],
|
|
1230
|
+
verificationRules: [...SDD_WORKFLOW_CONTRACT.verificationRules]
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
function getSddRouteExecutionPolicy(route) {
|
|
1234
|
+
const policy = SDD_ROUTE_EXECUTION_POLICIES.find(
|
|
1235
|
+
(candidate) => candidate.route === route
|
|
1236
|
+
);
|
|
1237
|
+
if (!policy) {
|
|
1238
|
+
throw new Error(`Unknown SDD route policy: ${route}`);
|
|
1239
|
+
}
|
|
1240
|
+
return {
|
|
1241
|
+
...policy,
|
|
1242
|
+
validationGates: [...policy.validationGates]
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
function getSddArtifactGraph() {
|
|
1246
|
+
return SDD_ARTIFACT_GRAPH.map((artifact) => ({
|
|
1247
|
+
...artifact,
|
|
1248
|
+
consumes: [...artifact.consumes],
|
|
1249
|
+
requiredFor: [...artifact.requiredFor]
|
|
1250
|
+
}));
|
|
1251
|
+
}
|
|
1252
|
+
function getSddPhase(id) {
|
|
1253
|
+
const phase = SDD_PHASES.find((candidate) => candidate.id === id);
|
|
1254
|
+
if (!phase) {
|
|
1255
|
+
throw new Error(`Unknown SDD phase: ${id}`);
|
|
1256
|
+
}
|
|
1257
|
+
return clonePhase(phase);
|
|
1258
|
+
}
|
|
1259
|
+
function renderSddPhaseDispatchTemplate() {
|
|
1260
|
+
return `## PHASE
|
|
1261
|
+
phase=<phase-id>
|
|
1262
|
+
|
|
1263
|
+
## ROUTE / CHANGE
|
|
1264
|
+
<direct|accelerated|full> / <feature-or-direct-task>
|
|
1265
|
+
|
|
1266
|
+
## OBJECTIVE
|
|
1267
|
+
<phase objective>
|
|
1268
|
+
|
|
1269
|
+
## INPUT ARTIFACTS
|
|
1270
|
+
<required files, evidence, and prior handoff>
|
|
1271
|
+
|
|
1272
|
+
## REQUIREMENTS
|
|
1273
|
+
<concrete outcomes and phase instructions>
|
|
1274
|
+
|
|
1275
|
+
## BOUNDARIES
|
|
1276
|
+
<allowed writes, assigned surface, and non-goals>
|
|
1277
|
+
|
|
1278
|
+
## VERIFICATION
|
|
1279
|
+
<done criteria, blockers, and checks>
|
|
1280
|
+
|
|
1281
|
+
## EXPECTED OUTPUT
|
|
1282
|
+
<phase result fields>
|
|
1283
|
+
|
|
1284
|
+
## HANDOFF
|
|
1285
|
+
<what the next phase must preserve>
|
|
1286
|
+
|
|
1287
|
+
## MEMORY
|
|
1288
|
+
provider=thoth-mem
|
|
1289
|
+
project=<project-name>
|
|
1290
|
+
root_session_id=<stable-root-session-id|unavailable>
|
|
1291
|
+
authorization=<none|recall|observe>
|
|
1292
|
+
context:
|
|
1293
|
+
<bounded recalled context or - none>`;
|
|
1294
|
+
}
|
|
1295
|
+
function getSddPhaseOwner(_route, phaseId) {
|
|
1296
|
+
return getSddPhase(phaseId).defaultAgentRole;
|
|
1297
|
+
}
|
|
1298
|
+
function getRequiredSddPhaseOrder(route) {
|
|
1299
|
+
return SDD_PHASES.filter(
|
|
1300
|
+
(phase) => phase.requiredFor.includes(route)
|
|
1301
|
+
).map((phase) => phase.id);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
// src/agents/prompt-sections.ts
|
|
1305
|
+
function createQuestionProtocolSection() {
|
|
1306
|
+
return { kind: "question-protocol", toolConcept: "userQuestion" };
|
|
1307
|
+
}
|
|
1308
|
+
function createSubagentRulesSection(memoryAccess = "dispatch-scoped") {
|
|
1309
|
+
return {
|
|
1310
|
+
kind: "subagent-rules",
|
|
1311
|
+
memoryAccess,
|
|
1312
|
+
progressConcept: "progress",
|
|
1313
|
+
userQuestionConcept: "userQuestion"
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
function createReasoningDisciplineSection() {
|
|
1317
|
+
return { kind: "reasoning-discipline" };
|
|
1318
|
+
}
|
|
1319
|
+
function createResponseBudgetSection() {
|
|
1320
|
+
return { kind: "response-budget" };
|
|
1321
|
+
}
|
|
1322
|
+
function createStepBudgetSection(steps) {
|
|
1323
|
+
if (steps === void 0 || !Number.isInteger(steps) || steps <= 0) {
|
|
1324
|
+
return void 0;
|
|
1325
|
+
}
|
|
1326
|
+
return { kind: "step-budget", steps };
|
|
1327
|
+
}
|
|
1328
|
+
function getPrimaryModelId2(model) {
|
|
1329
|
+
if (Array.isArray(model)) {
|
|
1330
|
+
const first = model[0];
|
|
1331
|
+
return typeof first === "string" ? first : first?.id;
|
|
1332
|
+
}
|
|
1333
|
+
return model;
|
|
1334
|
+
}
|
|
1335
|
+
function detectModelFamilyFromModel(model) {
|
|
1336
|
+
const id = getPrimaryModelId2(model)?.toLowerCase();
|
|
1337
|
+
if (!id) return void 0;
|
|
1338
|
+
if (id.includes("gpt") || id.startsWith("openai/")) return "openai";
|
|
1339
|
+
return void 0;
|
|
1340
|
+
}
|
|
1341
|
+
function createModelFamilySection(role, model) {
|
|
1342
|
+
const family = detectModelFamilyFromModel(model);
|
|
1343
|
+
return family ? { kind: "model-family", role, family } : void 0;
|
|
1344
|
+
}
|
|
1345
|
+
function roleText(template) {
|
|
1346
|
+
return { kind: "role-text", template };
|
|
1347
|
+
}
|
|
1348
|
+
function roleTemplate(role) {
|
|
1349
|
+
return `{{role.${role}}}`;
|
|
1350
|
+
}
|
|
1351
|
+
function renderSddRoute(route) {
|
|
1352
|
+
return getRequiredSddPhaseOrder(route).map(
|
|
1353
|
+
(phase) => `${phase} (${roleTemplate(getSddPhaseOwner(route, phase))})`
|
|
1354
|
+
).join(" -> ");
|
|
1355
|
+
}
|
|
1356
|
+
function renderRoleDirectory() {
|
|
1357
|
+
return getAgentPackContract().roles.filter((role) => role.name !== "orchestrator").map((role) => `- ${roleTemplate(role.name)}: ${role.responsibility}`).join("\n");
|
|
1358
|
+
}
|
|
1359
|
+
function renderArtifactSummary() {
|
|
1360
|
+
return getSddArtifactGraph().filter((artifact) => artifact.requiredFor.length === 0).map((artifact) => artifact.path).join(", ");
|
|
1361
|
+
}
|
|
1362
|
+
function createOrchestratorPromptSections() {
|
|
1363
|
+
const workflow = getSddWorkflowContract();
|
|
1364
|
+
const policy = getAgentPackContract().orchestrationPolicy;
|
|
1365
|
+
const accelerated = getSddRouteExecutionPolicy("accelerated");
|
|
1366
|
+
const full = getSddRouteExecutionPolicy("full");
|
|
1367
|
+
return [
|
|
1368
|
+
roleText(`<role>
|
|
1369
|
+
You are the adaptive root for thoth-agents. Keep requirements, decisions, execution ownership, and final synthesis in this thread.
|
|
1370
|
+
</role>
|
|
1371
|
+
|
|
1372
|
+
<operating-model>
|
|
1373
|
+
- Handle bounded direct work when intent and risk are clear; never verify your own implementation.
|
|
1374
|
+
- Delegate only for net gain from specialization, context isolation, review, or safe parallelism. The maximum delegation depth is ${policy.maxDelegationDepth}; children never delegate.
|
|
1375
|
+
- Maintain one writer for each mutable surface. Parallelize only independent work with no overlapping writes.
|
|
1376
|
+
- Keep prompts bounded; request distilled evidence, not raw logs or full files.
|
|
1377
|
+
- Use \`{{userQuestionTool}}\` only when a material unresolved choice changes the result. Continue all safe non-blocked work first.
|
|
1378
|
+
- Use \`{{progressTool}}\` only when the work genuinely has multiple dependent steps.
|
|
1379
|
+
</operating-model>
|
|
1380
|
+
|
|
1381
|
+
<routing>
|
|
1382
|
+
${renderRoleDirectory()}
|
|
1383
|
+
</routing>
|
|
1384
|
+
|
|
1385
|
+
<sdd-routing>
|
|
1386
|
+
- An explicitly requested route wins; a generic SDD request sets Accelerated as the minimum unless Full risk applies.
|
|
1387
|
+
- Direct: clear, bounded, low-risk work. ${renderSddRoute("direct")}.
|
|
1388
|
+
- Documentation or mechanical work may remain Direct across multiple files when it is clear and low risk.
|
|
1389
|
+
- Accelerated SDD: multi-surface behavior, architecture, partial clarity, or moderate risk. ${renderSddRoute("accelerated")}.
|
|
1390
|
+
- For Accelerated, run specify -> plan -> tasks in one uninterrupted root pass. Do not pause between those planning artifacts; ask only for a material unresolved decision.
|
|
1391
|
+
- Its thoth-sdd validator gates are ${accelerated.validationGates.join(" -> ")}; optional artifacts are off by default.
|
|
1392
|
+
- Full SDD: uncertain scope, cross-cutting behavior or architecture, high contract risk, or high failure cost. ${renderSddRoute("full")}.
|
|
1393
|
+
- Full gates are ${full.validationGates.join(" -> ")}; checklist remains conditional.
|
|
1394
|
+
- Happy path: verify -> archive. Artifact-backed failure loop: verify fail -> converge -> implement -> verify. Direct failure loop: verify fail -> implement -> verify.
|
|
1395
|
+
- Conditional phases: clarify only for material ambiguity; checklist only when requirement risk justifies it; converge only when verification finds actionable defects.
|
|
1396
|
+
- When implementation discoveries refine the same intent, update the canonical artifact and revalidate only affected downstream artifacts. Split a new change when the intent changes.
|
|
1397
|
+
- Load the bundled \`thoth-sdd\` skill only after selecting Accelerated or Full, then read only the reference for the current phase.
|
|
1398
|
+
- Root owns specify, clarify, plan, checklist, tasks, converge, and archive coordination; these phases are not delegated merely to change prompts.
|
|
1399
|
+
- Delegate analyze and every verify phase to ${roleTemplate("oracle")}, including Direct and Accelerated work. The implementation writer must never review itself.
|
|
1400
|
+
</sdd-routing>
|
|
1401
|
+
|
|
1402
|
+
<external-skills>
|
|
1403
|
+
- Use bundled \`thoth-constitution\` for constitution lifecycle and \`thoth-archive\` for verified artifact-backed closeout.
|
|
1404
|
+
- Use the installed mandatory \`tdd\` skill for behavior changes and \`simplify\` after implementation without changing behavior.
|
|
1405
|
+
- During an SDD, never invoke the thoth-agents CLI, \`npx skills add\`, or a network fetch. A missing local contract means incomplete installation.
|
|
1406
|
+
- Use progressive-context-router only for repository instruction or context-router work.
|
|
1407
|
+
- Use architectural-grilling before specification only when the user explicitly asks to be grilled or material human-owned product or architecture decisions remain unresolved.
|
|
1408
|
+
- Do not invoke it merely because the route is Full. While grilling, ask one material question per turn and await explicit closure.
|
|
1409
|
+
- Feed accepted decisions forward; spec.md and plan.md remain canonical instead of creating a duplicate blueprint artifact by default.
|
|
1410
|
+
</external-skills>
|
|
1411
|
+
|
|
1412
|
+
<memory>
|
|
1413
|
+
- Load the installed \`thoth-mem\` skill for resume or prior work and provider-backed memory; never invent its protocol.
|
|
1414
|
+
- Preserve a durable decision, root cause, convention, or discovery only when reusable. Root owns the stable root session ID, project, lifecycle, real-user intent, and authorization; never invent identity or confirmed effects.
|
|
1415
|
+
- Follow the skill at verified compaction and a meaningful semantic boundary. Children receive only bounded MEMORY dispatch and never own root lifecycle.
|
|
1416
|
+
- \`openspec/\` remains canonical; do not mirror SDD phase artifacts into provider memory. A memory failure degrades memory only and does not block unrelated implementation or verification.
|
|
1417
|
+
</memory>
|
|
1418
|
+
|
|
1419
|
+
<artifacts>
|
|
1420
|
+
- Preserve Spec Kit semantics inside ${workflow.artifactRoot}.
|
|
1421
|
+
- Accelerated and Full require spec.md, plan.md, tasks.md, verify-report.md, and archive-report.md; optional when useful: ${renderArtifactSummary()}.
|
|
1422
|
+
- Root owns openspec/ coordination and thoth-sdd gates; product work has one writer, and root alone moves [~] -> [x] after evidence.
|
|
1423
|
+
- ${roleTemplate("oracle")} returns read-only findings. Root persists verification; after PASS, archive syncs declared durable deltas and moves the change to openspec/changes/archive/YYYY-MM-DD-<feature>/.
|
|
1424
|
+
</artifacts>
|
|
1425
|
+
|
|
1426
|
+
<execution>
|
|
1427
|
+
- Validate contracts and tests first; use test-first work for behavior. Root or one writer implements; do not delegate merely because an agent exists.
|
|
1428
|
+
- ${roleTemplate("oracle")} always provides independent verification and also owns Full SDD analysis. Root and implementation writers never self-approve.
|
|
1429
|
+
- Preserve unrelated working-tree changes. Never instruct an agent to discard them.
|
|
1430
|
+
- Report changed files, evidence, risks, and capability gaps truthfully.
|
|
1431
|
+
</execution>
|
|
1432
|
+
|
|
1433
|
+
<delegation>
|
|
1434
|
+
- Dispatch through \`{{delegationTool}}\` with this envelope; use the same boundaries for non-SDD work.
|
|
1435
|
+
- Parallelize only independent work and await requested results before synthesis.
|
|
1436
|
+
- Child return fields: conclusion, evidence, verification, risks, openQuestions, nextAction.
|
|
1437
|
+
|
|
1438
|
+
${renderSddPhaseDispatchTemplate()}
|
|
1439
|
+
</delegation>`),
|
|
1440
|
+
createQuestionProtocolSection()
|
|
1441
|
+
];
|
|
1442
|
+
}
|
|
1443
|
+
var ROLE_SPECIFIC_RULES = {
|
|
1444
|
+
explorer: [
|
|
1445
|
+
"Resolve the assigned repository question with paths, symbols, and concise anchors.",
|
|
1446
|
+
"Search broadly only when the target is genuinely unknown; stop once the evidence is decision-ready."
|
|
1447
|
+
],
|
|
1448
|
+
librarian: [
|
|
1449
|
+
"Prefer current official documentation and primary sources.",
|
|
1450
|
+
"Cite every substantive external claim and label inference explicitly."
|
|
1451
|
+
],
|
|
1452
|
+
oracle: [
|
|
1453
|
+
"Separate observations, risks, and recommendations.",
|
|
1454
|
+
"Review against stated requirements and contracts; do not invent implementation scope.",
|
|
1455
|
+
"For analyze or verify, load the matching bundled thoth-sdd reference and remain read-only.",
|
|
1456
|
+
"Reject self-review: the implementing root or writer cannot substitute for independent oracle judgment."
|
|
1457
|
+
],
|
|
1458
|
+
designer: [
|
|
1459
|
+
"Own user-facing choices, implementation, and visual verification.",
|
|
1460
|
+
"Check relevant responsive and interaction states when feasible."
|
|
1461
|
+
],
|
|
1462
|
+
quick: [
|
|
1463
|
+
"Make the smallest complete edit and stop after focused verification.",
|
|
1464
|
+
"Escalate instead of expanding a bounded assignment into broad discovery."
|
|
1465
|
+
],
|
|
1466
|
+
deep: [
|
|
1467
|
+
"Build the necessary local mental model and use tests first for behavior changes.",
|
|
1468
|
+
"Verify related call sites, edge cases, and shared contracts before completion."
|
|
1469
|
+
]
|
|
1470
|
+
};
|
|
1471
|
+
function childSections(roleName) {
|
|
1472
|
+
const role = getAgentRole(roleName);
|
|
1473
|
+
const dispatch = role.dispatch === "synchronous-task-only" ? "{{dispatch.synchronous-task-only}}" : "{{dispatch.task}}";
|
|
1474
|
+
const writeScope = role.writeScope?.length ? `
|
|
1475
|
+
- Write scope: ${role.writeScope.join(", ")}` : "";
|
|
1476
|
+
const modeRules = role.mode === "read-only" ? [
|
|
1477
|
+
"Do not mutate the workspace.",
|
|
1478
|
+
"Do not create coordination artifacts."
|
|
1479
|
+
] : [
|
|
1480
|
+
"Edit only the assigned phase surface.",
|
|
1481
|
+
"Preserve unrelated working-tree changes and never use destructive Git cleanup."
|
|
1482
|
+
];
|
|
1483
|
+
const sections = [
|
|
1484
|
+
roleText(`<role>
|
|
1485
|
+
You are ${roleName}.
|
|
1486
|
+
</role>
|
|
1487
|
+
|
|
1488
|
+
<mode>
|
|
1489
|
+
- Mode: ${role.mode}
|
|
1490
|
+
- Dispatch: ${dispatch}
|
|
1491
|
+
- Scope: ${role.scope}${writeScope}
|
|
1492
|
+
</mode>
|
|
1493
|
+
|
|
1494
|
+
<responsibility>
|
|
1495
|
+
${role.responsibility}
|
|
1496
|
+
</responsibility>`),
|
|
1497
|
+
createReasoningDisciplineSection(),
|
|
1498
|
+
roleText(`<rules>
|
|
1499
|
+
- Do not delegate further or manage root progress.
|
|
1500
|
+
- ${modeRules.join("\n- ")}
|
|
1501
|
+
- ${ROLE_SPECIFIC_RULES[roleName].join("\n- ")}
|
|
1502
|
+
- Ask only when a local blocking decision cannot be resolved from the assignment and evidence.
|
|
1503
|
+
</rules>`),
|
|
1504
|
+
createSubagentRulesSection(),
|
|
1505
|
+
createQuestionProtocolSection(),
|
|
1506
|
+
roleText(`<return-contract>
|
|
1507
|
+
Return a compact result with these fields:
|
|
1508
|
+
- conclusion
|
|
1509
|
+
- evidence
|
|
1510
|
+
- verification
|
|
1511
|
+
- risks
|
|
1512
|
+
- openQuestions
|
|
1513
|
+
- nextAction
|
|
1514
|
+
</return-contract>`),
|
|
1515
|
+
createResponseBudgetSection()
|
|
1516
|
+
];
|
|
1517
|
+
return sections;
|
|
1518
|
+
}
|
|
1519
|
+
function createReadOnlySpecialistPromptSections(role) {
|
|
1520
|
+
return childSections(role);
|
|
1521
|
+
}
|
|
1522
|
+
function createWriteCapableSpecialistPromptSections(role) {
|
|
1523
|
+
return childSections(role);
|
|
1524
|
+
}
|
|
1525
|
+
function createRolePromptSections(role) {
|
|
1526
|
+
const mode = getAgentRole(role).mode;
|
|
1527
|
+
switch (mode) {
|
|
1528
|
+
case "adaptive-root":
|
|
1529
|
+
return createOrchestratorPromptSections();
|
|
1530
|
+
case "read-only":
|
|
1531
|
+
return createReadOnlySpecialistPromptSections(role);
|
|
1532
|
+
case "write-capable":
|
|
1533
|
+
return createWriteCapableSpecialistPromptSections(
|
|
1534
|
+
role
|
|
1535
|
+
);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
function renderQuestionProtocol(_section, dialect) {
|
|
1539
|
+
return `<questions>
|
|
1540
|
+
Use \`${dialect.tools.userQuestionTool}\` only for a blocking material choice, destructive or security-sensitive action, or missing secret. Do safe non-blocked work first and ask one targeted question with a recommended default.
|
|
1541
|
+
</questions>`;
|
|
1542
|
+
}
|
|
1543
|
+
function renderSubagentRules(section, dialect) {
|
|
1544
|
+
const rules = [
|
|
1545
|
+
`- Do not delegate further or call \`${dialect.tools.progressTool}\`.`,
|
|
1546
|
+
`- Use \`${dialect.tools.userQuestionTool}\` only for a local blocking choice.`,
|
|
1547
|
+
"- Use terminating checks; avoid watch processes and indefinite waits.",
|
|
1548
|
+
"- Never discard or overwrite unrelated working-tree changes."
|
|
1549
|
+
];
|
|
1550
|
+
if (section.memoryAccess === "dispatch-scoped") {
|
|
1551
|
+
rules.push(
|
|
1552
|
+
"- Read the dispatch MEMORY block: `none` forbids provider work, `recall` permits bounded reads, and `observe` additionally permits a bounded durable observation under the delegated scope.",
|
|
1553
|
+
"- For `recall` or `observe`, load and follow the installed `thoth-mem` skill; do not invent provider mechanics or claim unconfirmed effects.",
|
|
1554
|
+
"- MEMORY authorization does not authorize workspace mutation. It never transfers root lifecycle or real-user-intent ownership to a child.",
|
|
1555
|
+
"- `openspec/` remains canonical; do not mirror SDD phase artifacts into provider memory.",
|
|
1556
|
+
"- Report unavailable, degraded, stale, contradictory, or insufficient memory evidence and continue unrelated assigned work when safe."
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
return rules.join("\n");
|
|
1560
|
+
}
|
|
1561
|
+
function renderReasoningDiscipline() {
|
|
1562
|
+
return `<reasoning-discipline>
|
|
1563
|
+
- Check the most likely failure mode and one meaningful alternative before acting.
|
|
1564
|
+
- Ground conclusions in current evidence and verify the assigned outcome before returning.
|
|
1565
|
+
</reasoning-discipline>`;
|
|
1566
|
+
}
|
|
1567
|
+
function renderResponseBudget() {
|
|
1568
|
+
return "Be concise. Return distilled evidence and outcomes, not raw logs or full-file dumps.";
|
|
1569
|
+
}
|
|
1570
|
+
function renderStepBudget(section) {
|
|
1571
|
+
return `<step-budget>
|
|
1572
|
+
- Execution budget: ${section.steps} steps.
|
|
1573
|
+
- Prioritize high-signal checks and return partial evidence with the next target instead of looping.
|
|
1574
|
+
</step-budget>`;
|
|
1575
|
+
}
|
|
1576
|
+
function getRoleModelProfile(role) {
|
|
1577
|
+
switch (role) {
|
|
1578
|
+
case "orchestrator":
|
|
1579
|
+
return "Act directly on bounded work; delegate only for net gain and synthesize all results.";
|
|
1580
|
+
case "explorer":
|
|
1581
|
+
return "Navigate from broad uncertainty to exact repository anchors.";
|
|
1582
|
+
case "librarian":
|
|
1583
|
+
return "Prioritize current primary sources, versions, and explicit citations.";
|
|
1584
|
+
case "oracle":
|
|
1585
|
+
return "Challenge assumptions and return evidence-backed judgment.";
|
|
1586
|
+
case "designer":
|
|
1587
|
+
return "Make concrete UX choices and verify the visible result.";
|
|
1588
|
+
case "quick":
|
|
1589
|
+
return "Favor the smallest complete edit and focused verification.";
|
|
1590
|
+
case "deep":
|
|
1591
|
+
return "Trace shared behavior, test assumptions, and verify edge cases.";
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
function renderModelFamily(section) {
|
|
1595
|
+
const familyGuidance = {
|
|
1596
|
+
openai: "Plan briefly, then act with explicit tool targets and return shapes."
|
|
1597
|
+
};
|
|
1598
|
+
return `<model-profile family="${section.family}">
|
|
1599
|
+
- ${familyGuidance[section.family]}
|
|
1600
|
+
- ${getRoleModelProfile(section.role)}
|
|
1601
|
+
</model-profile>`;
|
|
1602
|
+
}
|
|
1603
|
+
function renderRoleText(section, dialect) {
|
|
1604
|
+
return section.template.replaceAll("{{delegationTool}}", dialect.tools.delegationTool).replaceAll(
|
|
1605
|
+
"{{backgroundDelegationTool}}",
|
|
1606
|
+
dialect.tools.backgroundDelegationTool ?? dialect.tools.delegationTool
|
|
1607
|
+
).replaceAll(
|
|
1608
|
+
"{{backgroundStatusTool}}",
|
|
1609
|
+
dialect.tools.backgroundStatusTool ?? dialect.tools.hostStatusSurface ?? ""
|
|
1610
|
+
).replaceAll("{{userQuestionTool}}", dialect.tools.userQuestionTool).replaceAll("{{progressTool}}", dialect.tools.progressTool).replaceAll(
|
|
1611
|
+
"{{lifecycleStatusAction}}",
|
|
1612
|
+
dialect.tools.lifecycle.statusAction
|
|
1613
|
+
).replaceAll(
|
|
1614
|
+
"{{lifecycleTerminalState}}",
|
|
1615
|
+
dialect.tools.lifecycle.terminalState
|
|
1616
|
+
).replaceAll(
|
|
1617
|
+
"{{lifecycleNonterminalState}}",
|
|
1618
|
+
dialect.tools.lifecycle.nonterminalState
|
|
1619
|
+
).replaceAll(
|
|
1620
|
+
"{{lifecycleSameSessionProbe}}",
|
|
1621
|
+
dialect.tools.lifecycle.sameSessionProbe
|
|
1622
|
+
).replaceAll("{{dispatch.task}}", dialect.dispatchLabel("task")).replaceAll(
|
|
1623
|
+
"{{dispatch.synchronous-task-only}}",
|
|
1624
|
+
dialect.dispatchLabel("synchronous-task-only")
|
|
1625
|
+
).replace(
|
|
1626
|
+
/{{role\.([\w-]+)}}/g,
|
|
1627
|
+
(_match, role) => dialect.renderRoleInvocation(role)
|
|
1628
|
+
);
|
|
1629
|
+
}
|
|
1630
|
+
function renderPromptSection(section, dialect) {
|
|
1631
|
+
switch (section.kind) {
|
|
1632
|
+
case "question-protocol":
|
|
1633
|
+
return renderQuestionProtocol(section, dialect);
|
|
1634
|
+
case "subagent-rules":
|
|
1635
|
+
return renderSubagentRules(section, dialect);
|
|
1636
|
+
case "reasoning-discipline":
|
|
1637
|
+
return renderReasoningDiscipline();
|
|
1638
|
+
case "response-budget":
|
|
1639
|
+
return renderResponseBudget();
|
|
1640
|
+
case "step-budget":
|
|
1641
|
+
return renderStepBudget(section);
|
|
1642
|
+
case "model-family":
|
|
1643
|
+
return renderModelFamily(section);
|
|
1644
|
+
case "role-text":
|
|
1645
|
+
return renderRoleText(section, dialect);
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
function renderRolePrompt(sections, dialect) {
|
|
1649
|
+
return sections.map((section) => renderPromptSection(section, dialect).trim()).filter(Boolean).join("\n\n");
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
// src/agents/prompt-utils.ts
|
|
1653
|
+
var QUESTION_PROTOCOL = renderPromptSection(
|
|
1654
|
+
createQuestionProtocolSection(),
|
|
1655
|
+
OPENCODE_PROMPT_DIALECT
|
|
1656
|
+
);
|
|
1657
|
+
var SUBAGENT_RULES = renderPromptSection(
|
|
1658
|
+
createSubagentRulesSection(),
|
|
1659
|
+
OPENCODE_PROMPT_DIALECT
|
|
1660
|
+
);
|
|
1661
|
+
var RESPONSE_BUDGET = renderPromptSection(
|
|
1662
|
+
createResponseBudgetSection(),
|
|
1663
|
+
OPENCODE_PROMPT_DIALECT
|
|
1664
|
+
);
|
|
1665
|
+
function trimPromptSection(section) {
|
|
1666
|
+
const trimmed = section?.trim();
|
|
1667
|
+
return trimmed ? trimmed : void 0;
|
|
1668
|
+
}
|
|
1669
|
+
function appendPromptSections(...sections) {
|
|
1670
|
+
return sections.map(trimPromptSection).filter(Boolean).join("\n\n");
|
|
1671
|
+
}
|
|
1672
|
+
function getStepBudgetPromptSection(steps) {
|
|
1673
|
+
const section = createStepBudgetSection(steps);
|
|
1674
|
+
if (!section) {
|
|
1675
|
+
return void 0;
|
|
1676
|
+
}
|
|
1677
|
+
return renderPromptSection(section, OPENCODE_PROMPT_DIALECT);
|
|
1678
|
+
}
|
|
1679
|
+
function getModelFamilyPromptSection(role, model) {
|
|
1680
|
+
const section = createModelFamilySection(role, model);
|
|
1681
|
+
if (!section) {
|
|
1682
|
+
return void 0;
|
|
1683
|
+
}
|
|
1684
|
+
return renderPromptSection(section, OPENCODE_PROMPT_DIALECT);
|
|
1685
|
+
}
|
|
1686
|
+
function replacePromptPlaceholders(template, placeholders = {}) {
|
|
1687
|
+
return Object.entries(placeholders).reduce((prompt, [key, value]) => {
|
|
1688
|
+
if (value === void 0) {
|
|
1689
|
+
return prompt;
|
|
1690
|
+
}
|
|
1691
|
+
return prompt.replaceAll(`{{${key}}}`, String(value));
|
|
1692
|
+
}, template);
|
|
1693
|
+
}
|
|
1694
|
+
function composeAgentPrompt({
|
|
1695
|
+
basePrompt,
|
|
1696
|
+
customPrompt,
|
|
1697
|
+
customAppendPrompt,
|
|
1698
|
+
placeholders
|
|
1699
|
+
}) {
|
|
1700
|
+
const resolvedBase = replacePromptPlaceholders(basePrompt, placeholders);
|
|
1701
|
+
if (customPrompt) {
|
|
1702
|
+
return replacePromptPlaceholders(customPrompt, placeholders);
|
|
1703
|
+
}
|
|
1704
|
+
return appendPromptSections(
|
|
1705
|
+
resolvedBase,
|
|
1706
|
+
replacePromptPlaceholders(customAppendPrompt ?? "", placeholders)
|
|
1707
|
+
);
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
// src/mcp/context7.ts
|
|
1711
|
+
var CONTEXT7_MCP_URL = "https://mcp.context7.com/mcp";
|
|
1712
|
+
var context7 = {
|
|
1713
|
+
type: "remote",
|
|
1714
|
+
url: CONTEXT7_MCP_URL,
|
|
1715
|
+
headers: process.env.CONTEXT7_API_KEY ? { CONTEXT7_API_KEY: process.env.CONTEXT7_API_KEY } : void 0,
|
|
1716
|
+
oauth: false
|
|
1717
|
+
};
|
|
1718
|
+
|
|
1719
|
+
// src/mcp/exa.ts
|
|
1720
|
+
var exa = {
|
|
1721
|
+
type: "local",
|
|
1722
|
+
command: ["npx", "-y", "exa-mcp-server"]
|
|
1723
|
+
};
|
|
1724
|
+
|
|
1725
|
+
// src/mcp/grep-app.ts
|
|
1726
|
+
var GREP_APP_MCP_URL = "https://mcp.grep.app";
|
|
1727
|
+
var grep_app = {
|
|
1728
|
+
type: "remote",
|
|
1729
|
+
url: GREP_APP_MCP_URL,
|
|
1730
|
+
oauth: false
|
|
1731
|
+
};
|
|
1732
|
+
|
|
1733
|
+
export {
|
|
1734
|
+
SUBAGENT_NAMES,
|
|
1735
|
+
ALL_AGENT_NAMES,
|
|
1736
|
+
CONFIRMED_OPENAI_SUBAGENT_PRESET,
|
|
1737
|
+
OPENCODE_OPENAI_ORCHESTRATOR_PRESET,
|
|
1738
|
+
DEFAULT_MODELS,
|
|
1739
|
+
getDefaultOpenCodeModel,
|
|
1740
|
+
getDefaultOpenCodeVariant,
|
|
1741
|
+
POLL_INTERVAL_BACKGROUND_MS,
|
|
1742
|
+
getOpenCodeConfigPaths,
|
|
1743
|
+
getOpenCodeManagedModelStatePath,
|
|
1744
|
+
getExistingLiteConfigPath,
|
|
1745
|
+
getExistingConfigPath,
|
|
1746
|
+
ensureConfigDir,
|
|
1747
|
+
generateLiteConfig,
|
|
1748
|
+
stripJsonComments,
|
|
1749
|
+
parseConfig,
|
|
1750
|
+
writeConfig,
|
|
1751
|
+
updateOpenCodeMainConfig,
|
|
1752
|
+
writeLiteConfig,
|
|
1753
|
+
detectCurrentConfig,
|
|
1754
|
+
loadPluginConfig,
|
|
1755
|
+
loadAgentPrompt,
|
|
1756
|
+
getAgentOverride,
|
|
1757
|
+
getPrimaryModelId,
|
|
1758
|
+
OPENCODE_PROMPT_DIALECT,
|
|
1759
|
+
CODEX_PROMPT_DIALECT,
|
|
1760
|
+
CLAUDE_CODE_SUBAGENT_NAMESPACE,
|
|
1761
|
+
claudeCodeSubagentType,
|
|
1762
|
+
CLAUDE_CODE_PROMPT_DIALECT,
|
|
1763
|
+
getAgentPackContract,
|
|
1764
|
+
createStepBudgetSection,
|
|
1765
|
+
createModelFamilySection,
|
|
1766
|
+
createOrchestratorPromptSections,
|
|
1767
|
+
createReadOnlySpecialistPromptSections,
|
|
1768
|
+
createWriteCapableSpecialistPromptSections,
|
|
1769
|
+
createRolePromptSections,
|
|
1770
|
+
renderPromptSection,
|
|
1771
|
+
renderRolePrompt,
|
|
1772
|
+
appendPromptSections,
|
|
1773
|
+
getStepBudgetPromptSection,
|
|
1774
|
+
getModelFamilyPromptSection,
|
|
1775
|
+
composeAgentPrompt,
|
|
1776
|
+
CONTEXT7_MCP_URL,
|
|
1777
|
+
context7,
|
|
1778
|
+
exa,
|
|
1779
|
+
GREP_APP_MCP_URL,
|
|
1780
|
+
grep_app
|
|
1781
|
+
};
|