fix: resolve 25 pre-publish blockers
- postinstall.mjs: fix alias package detection - migrate-legacy-plugin-entry: dedupe + regression tests - task_system: default consistency across runtime paths - task() contract: consistent tool behavior - runtime model selection, tool cap, stale-task cancellation - recovery sanitization, context-limit gating - Ralph semantic DONE hardening, Atlas fallback persistence - native-skill description/content, skill path traversal guard - publish workflow: platform awaited via reusable workflow job - release: version edits reapplied before commit/tag - JSONC plugin migration: top-level plugin key safety - cold-cache: user fallback models skip disconnected providers - docs/version/release framing updates Verified: bun test (4599 pass), tsc --noEmit clean, bun run build clean
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import type { BackgroundManager } from "../../features/background-agent"
|
||||
import { isAgentRegistered } from "../../features/claude-code-session-state"
|
||||
import { log } from "../../shared/logger"
|
||||
import { createInternalAgentTextPart, resolveInheritedPromptTools } from "../../shared"
|
||||
import { getAgentDisplayName } from "../../shared/agent-display-names"
|
||||
import { getAgentConfigKey } from "../../shared/agent-display-names"
|
||||
import { HOOK_NAME } from "./hook-name"
|
||||
import { BOULDER_CONTINUATION_PROMPT } from "./system-reminder-templates"
|
||||
import { resolveRecentPromptContextForSession } from "./recent-model-resolver"
|
||||
@@ -48,24 +49,33 @@ export async function injectBoulderContinuation(input: {
|
||||
const preferredSessionContext = preferredTaskSessionId
|
||||
? `\n\n[Preferred reuse session for current top-level plan task${preferredTaskTitle ? `: ${preferredTaskTitle}` : ""}: ${preferredTaskSessionId}]`
|
||||
: ""
|
||||
const prompt =
|
||||
BOULDER_CONTINUATION_PROMPT.replace(/{PLAN_NAME}/g, planName) +
|
||||
`\n\n[Status: ${total - remaining}/${total} completed, ${remaining} remaining]` +
|
||||
preferredSessionContext +
|
||||
worktreeContext
|
||||
const prompt =
|
||||
BOULDER_CONTINUATION_PROMPT.replace(/{PLAN_NAME}/g, planName) +
|
||||
`\n\n[Status: ${total - remaining}/${total} completed, ${remaining} remaining]` +
|
||||
preferredSessionContext +
|
||||
worktreeContext
|
||||
const continuationAgent = agent ?? (isAgentRegistered("atlas") ? "atlas" : undefined)
|
||||
|
||||
try {
|
||||
log(`[${HOOK_NAME}] Injecting boulder continuation`, { sessionID, planName, remaining })
|
||||
if (!continuationAgent || !isAgentRegistered(continuationAgent)) {
|
||||
log(`[${HOOK_NAME}] Skipped injection: continuation agent unavailable`, {
|
||||
sessionID,
|
||||
agent: continuationAgent ?? agent ?? "unknown",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
log(`[${HOOK_NAME}] Injecting boulder continuation`, { sessionID, planName, remaining })
|
||||
|
||||
const promptContext = await resolveRecentPromptContextForSession(ctx, sessionID)
|
||||
const inheritedTools = resolveInheritedPromptTools(sessionID, promptContext.tools)
|
||||
|
||||
await ctx.client.session.promptAsync({
|
||||
path: { id: sessionID },
|
||||
body: {
|
||||
agent: getAgentDisplayName(agent ?? "atlas"),
|
||||
...(promptContext.model !== undefined ? { model: promptContext.model } : {}),
|
||||
...(inheritedTools ? { tools: inheritedTools } : {}),
|
||||
await ctx.client.session.promptAsync({
|
||||
path: { id: sessionID },
|
||||
body: {
|
||||
agent: getAgentConfigKey(continuationAgent),
|
||||
...(promptContext.model !== undefined ? { model: promptContext.model } : {}),
|
||||
...(inheritedTools ? { tools: inheritedTools } : {}),
|
||||
parts: [createInternalAgentTextPart(prompt)],
|
||||
},
|
||||
query: { directory: ctx.directory },
|
||||
|
||||
@@ -6,7 +6,7 @@ import { join } from "node:path"
|
||||
import { randomUUID } from "node:crypto"
|
||||
|
||||
import { clearBoulderState, writeBoulderState } from "../../features/boulder-state"
|
||||
import { _resetForTesting } from "../../features/claude-code-session-state"
|
||||
import { _resetForTesting, registerAgentName } from "../../features/claude-code-session-state"
|
||||
import type { BoulderState } from "../../features/boulder-state"
|
||||
|
||||
const TEST_STORAGE_ROOT = join(tmpdir(), `atlas-compaction-storage-${randomUUID()}`)
|
||||
@@ -66,6 +66,8 @@ describe("atlas hook compaction agent filtering", () => {
|
||||
mkdirSync(testDirectory, { recursive: true })
|
||||
clearBoulderState(testDirectory)
|
||||
_resetForTesting()
|
||||
registerAgentName("atlas")
|
||||
registerAgentName("sisyphus")
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { tmpdir } from "node:os"
|
||||
import { join } from "node:path"
|
||||
import { clearBoulderState, readBoulderState, writeBoulderState } from "../../features/boulder-state"
|
||||
import type { BoulderState } from "../../features/boulder-state"
|
||||
import { _resetForTesting, setSessionAgent, subagentSessions } from "../../features/claude-code-session-state"
|
||||
import { _resetForTesting, registerAgentName, setSessionAgent, subagentSessions } from "../../features/claude-code-session-state"
|
||||
|
||||
const { createAtlasHook } = await import("./index")
|
||||
|
||||
@@ -64,6 +64,8 @@ describe("atlas hook idle-event session lineage", () => {
|
||||
promptCalls = []
|
||||
clearBoulderState(testDirectory)
|
||||
_resetForTesting()
|
||||
registerAgentName("atlas")
|
||||
registerAgentName("sisyphus")
|
||||
subagentSessions.clear()
|
||||
})
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
readBoulderState,
|
||||
readCurrentTopLevelTask,
|
||||
} from "../../features/boulder-state"
|
||||
import { getSessionAgent, subagentSessions } from "../../features/claude-code-session-state"
|
||||
import { getSessionAgent, isAgentRegistered, subagentSessions } from "../../features/claude-code-session-state"
|
||||
import { getAgentConfigKey } from "../../shared/agent-display-names"
|
||||
import { log } from "../../shared/logger"
|
||||
import { injectBoulderContinuation } from "./boulder-continuation-injector"
|
||||
@@ -141,7 +141,15 @@ export async function handleAtlasSessionIdle(input: {
|
||||
if (subagentSessions.has(sessionID)) {
|
||||
const sessionAgent = getSessionAgent(sessionID)
|
||||
const agentKey = getAgentConfigKey(sessionAgent ?? "")
|
||||
const requiredAgentKey = getAgentConfigKey(boulderState.agent ?? "atlas")
|
||||
const requiredAgentName = boulderState.agent ?? (isAgentRegistered("atlas") ? "atlas" : undefined)
|
||||
if (!requiredAgentName || !isAgentRegistered(requiredAgentName)) {
|
||||
log(`[${HOOK_NAME}] Skipped: boulder agent is unavailable for continuation`, {
|
||||
sessionID,
|
||||
requiredAgent: boulderState.agent ?? "unknown",
|
||||
})
|
||||
return
|
||||
}
|
||||
const requiredAgentKey = getAgentConfigKey(requiredAgentName)
|
||||
const agentMatches =
|
||||
agentKey === requiredAgentKey ||
|
||||
(requiredAgentKey === getAgentConfigKey("atlas") && agentKey === getAgentConfigKey("sisyphus"))
|
||||
@@ -149,10 +157,10 @@ export async function handleAtlasSessionIdle(input: {
|
||||
log(`[${HOOK_NAME}] Skipped: subagent agent does not match boulder agent`, {
|
||||
sessionID,
|
||||
agent: sessionAgent ?? "unknown",
|
||||
requiredAgent: boulderState.agent ?? "atlas",
|
||||
})
|
||||
return
|
||||
}
|
||||
requiredAgent: requiredAgentName,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const sessionState = getState(sessionID)
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
readBoulderState,
|
||||
} from "../../features/boulder-state"
|
||||
import type { BoulderState } from "../../features/boulder-state"
|
||||
import { _resetForTesting, subagentSessions, updateSessionAgent } from "../../features/claude-code-session-state"
|
||||
import { _resetForTesting, registerAgentName, subagentSessions, updateSessionAgent } from "../../features/claude-code-session-state"
|
||||
import type { PendingTaskRef } from "./types"
|
||||
|
||||
const TEST_STORAGE_ROOT = join(tmpdir(), `atlas-message-storage-${randomUUID()}`)
|
||||
@@ -90,6 +90,9 @@ describe("atlas hook", () => {
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
_resetForTesting()
|
||||
registerAgentName("atlas")
|
||||
registerAgentName("sisyphus")
|
||||
TEST_DIR = join(tmpdir(), `atlas-test-${randomUUID()}`)
|
||||
SISYPHUS_DIR = join(TEST_DIR, ".sisyphus")
|
||||
if (!existsSync(TEST_DIR)) {
|
||||
@@ -102,6 +105,7 @@ describe("atlas hook", () => {
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
_resetForTesting()
|
||||
clearBoulderState(TEST_DIR)
|
||||
if (existsSync(TEST_DIR)) {
|
||||
rmSync(TEST_DIR, { recursive: true, force: true })
|
||||
@@ -1182,9 +1186,11 @@ session_id: ses_untrusted_999
|
||||
|
||||
beforeEach(() => {
|
||||
_resetForTesting()
|
||||
subagentSessions.clear()
|
||||
setupMessageStorage(MAIN_SESSION_ID, "atlas")
|
||||
})
|
||||
registerAgentName("atlas")
|
||||
registerAgentName("sisyphus")
|
||||
subagentSessions.clear()
|
||||
setupMessageStorage(MAIN_SESSION_ID, "atlas")
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
cleanupMessageStorage(MAIN_SESSION_ID)
|
||||
|
||||
Reference in New Issue
Block a user