Merge pull request #3467 from code-yeongyu/refactor/delegate-task-metadata-hardening
refactor: harden delegate-task metadata pipeline
This commit is contained in:
@@ -90,6 +90,17 @@ describe("extractSessionIdFromMetadata", () => {
|
||||
expect(result).toBe("ses_plugin_abc123")
|
||||
})
|
||||
|
||||
test("extracts legacy session aliases from tool metadata object", () => {
|
||||
// given
|
||||
const metadata = { sessionID: "ses_plugin_alias_123" }
|
||||
|
||||
// when
|
||||
const result = extractSessionIdFromMetadata(metadata)
|
||||
|
||||
// then
|
||||
expect(result).toBe("ses_plugin_alias_123")
|
||||
})
|
||||
|
||||
test("returns undefined for metadata without sessionId", () => {
|
||||
// given
|
||||
const metadata = { title: "some task" }
|
||||
|
||||
@@ -1,30 +1,20 @@
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { extractTaskLink } from "../../features/tool-metadata-store"
|
||||
import { log } from "../../shared/logger"
|
||||
import { isSessionInBoulderLineage } from "./boulder-session-lineage"
|
||||
import { HOOK_NAME } from "./hook-name"
|
||||
|
||||
export function extractSessionIdFromMetadata(metadata: unknown): string | undefined {
|
||||
if (metadata && typeof metadata === "object" && "sessionId" in metadata) {
|
||||
const value = (metadata as Record<string, unknown>).sessionId
|
||||
if (typeof value === "string" && value.startsWith("ses_")) {
|
||||
return value
|
||||
}
|
||||
const sessionId = extractTaskLink(metadata, "").sessionId
|
||||
if (typeof sessionId === "string" && sessionId.startsWith("ses_")) {
|
||||
return sessionId
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function extractSessionIdFromOutput(output: string): string | undefined {
|
||||
const taskMetadataBlocks = [...output.matchAll(/<task_metadata>([\s\S]*?)<\/task_metadata>/gi)]
|
||||
const lastTaskMetadataBlock = taskMetadataBlocks.at(-1)?.[1]
|
||||
if (lastTaskMetadataBlock) {
|
||||
const taskMetadataSessionMatch = lastTaskMetadataBlock.match(/session_id:\s*(ses_[a-zA-Z0-9_-]+)/i)
|
||||
if (taskMetadataSessionMatch) {
|
||||
return taskMetadataSessionMatch[1]
|
||||
}
|
||||
}
|
||||
|
||||
const explicitSessionMatches = [...output.matchAll(/Session ID:\s*(ses_[a-zA-Z0-9_-]+)/g)]
|
||||
return explicitSessionMatches.at(-1)?.[1]
|
||||
return extractTaskLink(undefined, output).sessionId
|
||||
}
|
||||
|
||||
export async function validateSubagentSessionId(input: {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { extractTaskLink } from "../../features/tool-metadata-store"
|
||||
import { stripInvisibleAgentCharacters } from "../../shared/agent-display-names"
|
||||
import { ULTRAWORK_VERIFICATION_PROMISE } from "./constants"
|
||||
|
||||
@@ -9,8 +10,6 @@ export interface OracleVerificationEvidence {
|
||||
|
||||
const AGENT_LINE_PATTERN = /^Agent:[ \t]*(\S+)$/im
|
||||
const PROMISE_TAG_PATTERN = /<promise>[ \t]*(\S+?)[ \t]*<\/promise>/is
|
||||
const TASK_METADATA_PATTERN = /<task_metadata>[ \t]*([\s\S]*?)[ \t]*<\/task_metadata>/is
|
||||
const SESSION_ID_LINE_PATTERN = /^session_id:[ \t]*(\S+)$/im
|
||||
|
||||
export function parseOracleVerificationEvidence(text: string): OracleVerificationEvidence | undefined {
|
||||
const trimmedText = text.trim()
|
||||
@@ -36,17 +35,9 @@ export function parseOracleVerificationEvidence(text: string): OracleVerificatio
|
||||
return undefined
|
||||
}
|
||||
|
||||
const metadataMatch = trimmedText.match(TASK_METADATA_PATTERN)
|
||||
let sessionID: string | undefined
|
||||
if (metadataMatch) {
|
||||
const metadataContent = metadataMatch[1]
|
||||
const sessionIDMatch = metadataContent.match(SESSION_ID_LINE_PATTERN)
|
||||
if (sessionIDMatch) {
|
||||
sessionID = sessionIDMatch[1]?.trim()
|
||||
}
|
||||
}
|
||||
const sessionID = extractTaskLink(undefined, trimmedText).sessionId
|
||||
|
||||
return { agent, promise, sessionID }
|
||||
return { agent, promise, sessionID }
|
||||
}
|
||||
|
||||
export function isOracleVerified(text: string): boolean {
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
import { extractTaskLink } from "../../features/tool-metadata-store"
|
||||
|
||||
const TARGET_TOOLS = ["task", "Task", "task_tool", "call_omo_agent"]
|
||||
|
||||
const SESSION_ID_PATTERNS = [
|
||||
/Session ID: (ses_[a-zA-Z0-9_-]+)/,
|
||||
/session_id: (ses_[a-zA-Z0-9_-]+)/,
|
||||
/<task_metadata>\s*session_id: (ses_[a-zA-Z0-9_-]+)/,
|
||||
/sessionId: (ses_[a-zA-Z0-9_-]+)/,
|
||||
]
|
||||
|
||||
function extractSessionId(output: string): string | null {
|
||||
for (const pattern of SESSION_ID_PATTERNS) {
|
||||
const match = output.match(pattern)
|
||||
if (match) return match[1] ?? null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function createTaskResumeInfoHook() {
|
||||
const toolExecuteAfter = async (
|
||||
input: { tool: string; sessionID: string; callID: string },
|
||||
@@ -25,7 +12,7 @@ export function createTaskResumeInfoHook() {
|
||||
if (outputText.startsWith("Error:") || outputText.startsWith("Failed")) return
|
||||
if (outputText.includes("\nto continue:")) return
|
||||
|
||||
const sessionId = extractSessionId(outputText)
|
||||
const sessionId = extractTaskLink(output.metadata, outputText).sessionId
|
||||
if (!sessionId) return
|
||||
|
||||
output.output =
|
||||
|
||||
@@ -78,6 +78,24 @@ describe("createTaskResumeInfoHook", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("#given target tool with session metadata object", () => {
|
||||
describe("#when output text omits session ID but metadata includes it", () => {
|
||||
it("#then should append resume info from metadata", async () => {
|
||||
const input = createInput("task")
|
||||
const output = {
|
||||
title: "task",
|
||||
output: "Task completed successfully",
|
||||
metadata: { sessionID: "ses_meta_123" },
|
||||
}
|
||||
|
||||
await afterHook(input, output)
|
||||
|
||||
expect(output.output).toContain("to continue:")
|
||||
expect(output.output).toContain("ses_meta_123")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("#given target tool with error output", () => {
|
||||
describe("#when output starts with Error:", () => {
|
||||
it("#then should not modify output", async () => {
|
||||
|
||||
Reference in New Issue
Block a user