fix(metis): switch primary model to claude-sonnet-4-6 + correct AGENTS.md inaccuracies
Source code change: - src/shared/model-requirements.ts: prepend claude-sonnet-4-6 to metis fallback chain so Sonnet becomes the default. Opus 4.7 max remains as the immediate fallback for callers who want extra reasoning. - src/shared/model-requirements.test.ts: update assertion to expect Sonnet primary + Opus secondary. AGENTS.md accuracy fixes (verified against source): - Agent modes: Sisyphus/Hephaestus are 'primary' (not 'all'); Sisyphus-Junior is 'subagent' (not 'all'). Confirmed via 'const MODE: AgentMode = ...' in each agent file. Also clarified Prometheus has no agentSources factory and is built via buildPrometheusAgentConfig. - Sisyphus fallback chain: corrected order to kimi-k2.6 → k2p5 → kimi-k2.5 → gpt-5.5 medium → glm-5 → big-pickle (was missing kimi-k2.5). - Librarian/Explore: added missing minimax-m2.7 step between -highspeed and claude-haiku-4-5. - Metis chain: removed fictitious gemini-3.1-pro entry. - Sisyphus-Junior chain: spelled out the actual fallback (was 'user-configurable'). - Temperatures: Sisyphus/Hephaestus do not set explicit temperature (model default); Sisyphus-Junior is 0.1 via SISYPHUS_JUNIOR_DEFAULTS. - Quick category default: gpt-5.4-mini (not gpt-5.4-mini-fast). Team-mode corrections: - Eligibility registry has 3 verdicts: eligible (sisyphus, atlas, sisyphus-junior), conditional (hephaestus — needs D-36 teammate permission), hard-reject (oracle, librarian, explore, multimodal-looker, metis, momus, prometheus). - Schema has 11 fields, not 4: added max_messages_per_run, max_wall_clock_minutes, max_member_turns, base_dir, message_payload_max_bytes, recipient_unread_max_bytes, mailbox_poll_interval_ms. - Hooks: 'team-session-events' is 4 sub-handlers in src/plugin/event.ts (team-idle-wake-hint, team-lead-orphan-handler, team-member-error-handler, team-member-status-handler), not a single Continuation-tier hook. - Tier counts now show base + team-mode: ToolGuard 14/15, Transform 5/7. - Total: 52 base hooks, 59 with team-mode. Doc cascade for the Metis change: - docs/guide/orchestration.md, agent-model-matching.md, installation.md - docs/reference/configuration.md, features.md
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
import { describe, expect, spyOn, test } from "bun:test"
|
||||
|
||||
import { disposeCreatedHooks } from "./create-hooks"
|
||||
import { createPluginDispose } from "./plugin-dispose"
|
||||
|
||||
describe("createPluginDispose", () => {
|
||||
test("#given plugin with active managers and hooks #when dispose() is called #then backgroundManager.shutdown() is called", async () => {
|
||||
// given
|
||||
const backgroundManager = {
|
||||
shutdown: async (): Promise<void> => {},
|
||||
}
|
||||
const skillMcpManager = {
|
||||
disconnectAll: async (): Promise<void> => {},
|
||||
}
|
||||
const lspManager = {
|
||||
stopAll: async (): Promise<void> => {},
|
||||
}
|
||||
const shutdownSpy = spyOn(backgroundManager, "shutdown")
|
||||
const dispose = createPluginDispose({
|
||||
backgroundManager,
|
||||
skillMcpManager,
|
||||
lspManager,
|
||||
disposeHooks: (): void => {},
|
||||
})
|
||||
|
||||
// when
|
||||
await dispose()
|
||||
|
||||
// then
|
||||
expect(shutdownSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
test("#given plugin with active MCP connections #when dispose() is called #then skillMcpManager.disconnectAll() is called", async () => {
|
||||
// given
|
||||
const backgroundManager = {
|
||||
shutdown: async (): Promise<void> => {},
|
||||
}
|
||||
const skillMcpManager = {
|
||||
disconnectAll: async (): Promise<void> => {},
|
||||
}
|
||||
const lspManager = {
|
||||
stopAll: async (): Promise<void> => {},
|
||||
}
|
||||
const disconnectAllSpy = spyOn(skillMcpManager, "disconnectAll")
|
||||
const dispose = createPluginDispose({
|
||||
backgroundManager,
|
||||
skillMcpManager,
|
||||
lspManager,
|
||||
disposeHooks: (): void => {},
|
||||
})
|
||||
|
||||
// when
|
||||
await dispose()
|
||||
|
||||
// then
|
||||
expect(disconnectAllSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
test("#given plugin with hooks that have dispose #when dispose() is called #then each hook's dispose is called", async () => {
|
||||
// given
|
||||
const claudeCodeHooks = {
|
||||
dispose: (): void => {},
|
||||
}
|
||||
const commentChecker = {
|
||||
dispose: (): void => {},
|
||||
}
|
||||
const runtimeFallback = {
|
||||
dispose: (): void => {},
|
||||
}
|
||||
const todoContinuationEnforcer = {
|
||||
dispose: (): void => {},
|
||||
}
|
||||
const autoSlashCommand = {
|
||||
dispose: (): void => {},
|
||||
}
|
||||
const lspManager = {
|
||||
stopAll: async (): Promise<void> => {},
|
||||
}
|
||||
const claudeCodeHooksDisposeSpy = spyOn(claudeCodeHooks, "dispose")
|
||||
const commentCheckerDisposeSpy = spyOn(commentChecker, "dispose")
|
||||
const runtimeFallbackDisposeSpy = spyOn(runtimeFallback, "dispose")
|
||||
const todoContinuationEnforcerDisposeSpy = spyOn(todoContinuationEnforcer, "dispose")
|
||||
const autoSlashCommandDisposeSpy = spyOn(autoSlashCommand, "dispose")
|
||||
const dispose = createPluginDispose({
|
||||
backgroundManager: {
|
||||
shutdown: async (): Promise<void> => {},
|
||||
},
|
||||
skillMcpManager: {
|
||||
disconnectAll: async (): Promise<void> => {},
|
||||
},
|
||||
lspManager,
|
||||
disposeHooks: (): void => {
|
||||
disposeCreatedHooks({
|
||||
claudeCodeHooks,
|
||||
commentChecker,
|
||||
runtimeFallback,
|
||||
todoContinuationEnforcer,
|
||||
autoSlashCommand,
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
// when
|
||||
await dispose()
|
||||
|
||||
// then
|
||||
expect(claudeCodeHooksDisposeSpy).toHaveBeenCalledTimes(1)
|
||||
expect(commentCheckerDisposeSpy).toHaveBeenCalledTimes(1)
|
||||
expect(runtimeFallbackDisposeSpy).toHaveBeenCalledTimes(1)
|
||||
expect(todoContinuationEnforcerDisposeSpy).toHaveBeenCalledTimes(1)
|
||||
expect(autoSlashCommandDisposeSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
test("#given dispose already called #when dispose() called again #then no errors", async () => {
|
||||
// given
|
||||
const backgroundManager = {
|
||||
shutdown: async (): Promise<void> => {},
|
||||
}
|
||||
const skillMcpManager = {
|
||||
disconnectAll: async (): Promise<void> => {},
|
||||
}
|
||||
const lspManager = {
|
||||
stopAll: async (): Promise<void> => {},
|
||||
}
|
||||
const disposeHooks = {
|
||||
run: (): void => {},
|
||||
}
|
||||
const shutdownSpy = spyOn(backgroundManager, "shutdown")
|
||||
const disconnectAllSpy = spyOn(skillMcpManager, "disconnectAll")
|
||||
const stopAllSpy = spyOn(lspManager, "stopAll")
|
||||
const disposeHooksSpy = spyOn(disposeHooks, "run")
|
||||
const dispose = createPluginDispose({
|
||||
backgroundManager,
|
||||
skillMcpManager,
|
||||
lspManager,
|
||||
disposeHooks: disposeHooks.run,
|
||||
})
|
||||
|
||||
// when
|
||||
await dispose()
|
||||
await dispose()
|
||||
|
||||
// then
|
||||
expect(shutdownSpy).toHaveBeenCalledTimes(1)
|
||||
expect(disconnectAllSpy).toHaveBeenCalledTimes(1)
|
||||
expect(stopAllSpy).toHaveBeenCalledTimes(1)
|
||||
expect(disposeHooksSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
test("#given backgroundManager.shutdown() throws #when dispose() is called #then skillMcpManager.disconnectAll() and disposeHooks() are still called", async () => {
|
||||
// given
|
||||
const backgroundManager = {
|
||||
shutdown: async (): Promise<void> => {
|
||||
throw new Error("shutdown failed")
|
||||
},
|
||||
}
|
||||
const skillMcpManager = {
|
||||
disconnectAll: async (): Promise<void> => {},
|
||||
}
|
||||
const lspManager = {
|
||||
stopAll: async (): Promise<void> => {},
|
||||
}
|
||||
const disposeHooksCalls: number[] = []
|
||||
const disconnectAllSpy = spyOn(skillMcpManager, "disconnectAll")
|
||||
const dispose = createPluginDispose({
|
||||
backgroundManager,
|
||||
skillMcpManager,
|
||||
lspManager,
|
||||
disposeHooks: (): void => {
|
||||
disposeHooksCalls.push(1)
|
||||
},
|
||||
})
|
||||
|
||||
// when
|
||||
await dispose()
|
||||
|
||||
// then
|
||||
expect(disconnectAllSpy).toHaveBeenCalledTimes(1)
|
||||
expect(disposeHooksCalls).toHaveLength(1)
|
||||
})
|
||||
|
||||
test("#given skillMcpManager.disconnectAll() throws #when dispose() is called #then disposeHooks() is still called", async () => {
|
||||
// given
|
||||
const backgroundManager = {
|
||||
shutdown: async (): Promise<void> => {},
|
||||
}
|
||||
const skillMcpManager = {
|
||||
disconnectAll: async (): Promise<void> => {
|
||||
throw new Error("disconnectAll failed")
|
||||
},
|
||||
}
|
||||
const lspManager = {
|
||||
stopAll: async (): Promise<void> => {},
|
||||
}
|
||||
const disposeHooksCalls: number[] = []
|
||||
const shutdownSpy = spyOn(backgroundManager, "shutdown")
|
||||
const dispose = createPluginDispose({
|
||||
backgroundManager,
|
||||
skillMcpManager,
|
||||
lspManager,
|
||||
disposeHooks: (): void => {
|
||||
disposeHooksCalls.push(1)
|
||||
},
|
||||
})
|
||||
|
||||
// when
|
||||
await dispose()
|
||||
|
||||
// then
|
||||
expect(shutdownSpy).toHaveBeenCalledTimes(1)
|
||||
expect(disposeHooksCalls).toHaveLength(1)
|
||||
})
|
||||
|
||||
test("#given active LSP clients #when dispose runs #then lsp manager is stopped", async () => {
|
||||
// given
|
||||
const lspManager = {
|
||||
stopAll: async (): Promise<void> => {},
|
||||
}
|
||||
const stopAllSpy = spyOn(lspManager, "stopAll")
|
||||
const dispose = createPluginDispose({
|
||||
backgroundManager: {
|
||||
shutdown: async (): Promise<void> => {},
|
||||
},
|
||||
skillMcpManager: {
|
||||
disconnectAll: async (): Promise<void> => {},
|
||||
},
|
||||
lspManager,
|
||||
disposeHooks: (): void => {},
|
||||
})
|
||||
|
||||
// when
|
||||
await dispose()
|
||||
|
||||
// then
|
||||
expect(stopAllSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user