merge: integrate origin/dev into modular-enforcement branch

Resolves all merge conflicts, preserving our split module structure
while integrating all dev changes:
- Custom agent summaries support (parseRegisteredAgentSummaries)
- Background notification queue (enqueueNotificationForParent)
- Atlas shared git-worktree module (collectGitDiffStats, formatFileChanges)
- Ralph-loop withTimeout + DEFAULT_API_TIMEOUT=5000
- Session recovery assistant_prefill_unsupported error type
- Atlas agentOverrides forwarding
- Config handler plan model demotion (buildPlanDemoteConfig)
- Delegate-task agentOverrides, promptSyncWithModelSuggestionRetry, variant
- LSP init timeout + stale init detection
- isPlanFamily function + task-continuation-enforcer hook
- Handoff command
This commit is contained in:
YeonGyu-Kim
2026-02-08 17:34:47 +09:00
74 changed files with 4016 additions and 654 deletions
@@ -2,6 +2,7 @@ export type RecoveryErrorType =
| "tool_result_missing"
| "thinking_block_order"
| "thinking_disabled_violation"
| "assistant_prefill_unsupported"
| null
function getErrorMessage(error: unknown): string {
@@ -41,6 +42,13 @@ export function extractMessageIndex(error: unknown): number | null {
export function detectErrorType(error: unknown): RecoveryErrorType {
const message = getErrorMessage(error)
if (
message.includes("assistant message prefill") ||
message.includes("conversation must end with a user message")
) {
return "assistant_prefill_unsupported"
}
if (
message.includes("thinking") &&
(message.includes("first block") ||
+4
View File
@@ -81,11 +81,13 @@ export function createSessionRecoveryHook(ctx: PluginInput, options?: SessionRec
tool_result_missing: "Tool Crash Recovery",
thinking_block_order: "Thinking Block Recovery",
thinking_disabled_violation: "Thinking Strip Recovery",
"assistant_prefill_unsupported": "Prefill Unsupported",
}
const toastMessages: Record<RecoveryErrorType & string, string> = {
tool_result_missing: "Injecting cancelled tool results...",
thinking_block_order: "Fixing message structure...",
thinking_disabled_violation: "Stripping thinking blocks...",
"assistant_prefill_unsupported": "Prefill not supported; continuing without recovery.",
}
await ctx.client.tui
@@ -117,6 +119,8 @@ export function createSessionRecoveryHook(ctx: PluginInput, options?: SessionRec
const resumeConfig = extractResumeConfig(lastUser, sessionID)
await resumeSession(ctx.client, resumeConfig)
}
} else if (errorType === "assistant_prefill_unsupported") {
success = true
}
return success
+57
View File
@@ -129,6 +129,63 @@ describe("detectErrorType", () => {
})
})
describe("assistant_prefill_unsupported errors", () => {
it("should detect assistant message prefill error from direct message", () => {
//#given an error about assistant message prefill not being supported
const error = {
message: "This model does not support assistant message prefill. The conversation must end with a user message.",
}
//#when detectErrorType is called
const result = detectErrorType(error)
//#then should return assistant_prefill_unsupported
expect(result).toBe("assistant_prefill_unsupported")
})
it("should detect assistant message prefill error from nested error object", () => {
//#given an Anthropic API error with nested structure matching the real error format
const error = {
error: {
type: "invalid_request_error",
message: "This model does not support assistant message prefill. The conversation must end with a user message.",
},
}
//#when detectErrorType is called
const result = detectErrorType(error)
//#then should return assistant_prefill_unsupported
expect(result).toBe("assistant_prefill_unsupported")
})
it("should detect error with only 'conversation must end with a user message' fragment", () => {
//#given an error containing only the user message requirement
const error = {
message: "The conversation must end with a user message.",
}
//#when detectErrorType is called
const result = detectErrorType(error)
//#then should return assistant_prefill_unsupported
expect(result).toBe("assistant_prefill_unsupported")
})
it("should detect error with only 'assistant message prefill' fragment", () => {
//#given an error containing only the prefill mention
const error = {
message: "This model does not support assistant message prefill.",
}
//#when detectErrorType is called
const result = detectErrorType(error)
//#then should return assistant_prefill_unsupported
expect(result).toBe("assistant_prefill_unsupported")
})
})
describe("unrecognized errors", () => {
it("should return null for unrecognized error patterns", () => {
// given an unrelated error