feat(util-agent): extend AgentOptions with fork / cleanup (Phase 2a)
CI / check (pull_request) Successful in 3m20s
CI / check (pull_request) Successful in 3m20s
Add AgentForkFn and AgentCleanupFn type aliases. Extend AgentOptions with fork: AgentForkFn | null and cleanup: AgentCleanupFn | null fields. Add getAskSessionId / setAskSessionId session-cache helpers using <stepHash>:ask key shape (coexists with exec sessions in the same per-agent cache file). All four adapters pass fork: null, cleanup: null — real wiring lands in Phase 2b. Resolves #145. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,12 +14,20 @@ export type { FrontmatterFastPathResult } from "./frontmatter.js";
|
||||
export { tryFrontmatterFastPath } from "./frontmatter.js";
|
||||
export { buildFrontmatterRetryPrompt } from "./frontmatter-retry-prompt.js";
|
||||
export { createAgent, parseArgv } from "./run.js";
|
||||
export { getCachedSessionId, getCachePath, setCachedSessionId } from "./session-cache.js";
|
||||
export {
|
||||
getAskSessionId,
|
||||
getCachedSessionId,
|
||||
getCachePath,
|
||||
setAskSessionId,
|
||||
setCachedSessionId,
|
||||
} from "./session-cache.js";
|
||||
export { getConfigPath, getEnvPath, loadWorkflowConfig, resolveStorageRoot } from "./storage.js";
|
||||
export type {
|
||||
AdapterOutput,
|
||||
AgentCleanupFn,
|
||||
AgentContext,
|
||||
AgentContinueFn,
|
||||
AgentForkFn,
|
||||
AgentOptions,
|
||||
AgentRunFn,
|
||||
AgentRunResult,
|
||||
|
||||
@@ -14,6 +14,10 @@ function cacheKey(threadId: ThreadId, role: string): string {
|
||||
return `${threadId}:${role}`;
|
||||
}
|
||||
|
||||
function askCacheKey(stepHash: string): string {
|
||||
return `${stepHash}:ask`;
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
@@ -86,3 +90,33 @@ export async function setCachedSessionId(
|
||||
cache[cacheKey(threadId, role)] = sessionId;
|
||||
await writeCache(agentName, storageRoot, cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the cached ask-session ID for a stepHash.
|
||||
*
|
||||
* Ask sessions are forked side conversations spawned by `step ask` from a
|
||||
* specific completed step. They share the per-agent cache file with exec
|
||||
* sessions but use the `<stepHash>:ask` key shape so the two namespaces
|
||||
* never collide.
|
||||
*/
|
||||
export async function getAskSessionId(
|
||||
agentName: string,
|
||||
stepHash: string,
|
||||
storageRoot: string,
|
||||
): Promise<string | null> {
|
||||
const cache = await readCache(agentName, storageRoot);
|
||||
const sessionId = cache[askCacheKey(stepHash)];
|
||||
return sessionId ?? null;
|
||||
}
|
||||
|
||||
/** Write the ask-session ID for a stepHash into the cache. */
|
||||
export async function setAskSessionId(
|
||||
agentName: string,
|
||||
stepHash: string,
|
||||
sessionId: string,
|
||||
storageRoot: string,
|
||||
): Promise<void> {
|
||||
const cache = await readCache(agentName, storageRoot);
|
||||
cache[askCacheKey(stepHash)] = sessionId;
|
||||
await writeCache(agentName, storageRoot, cache);
|
||||
}
|
||||
|
||||
@@ -50,6 +50,21 @@ export type AgentContinueFn = (
|
||||
|
||||
export type AgentRunFn = (ctx: AgentContext) => Promise<AgentRunResult>;
|
||||
|
||||
/**
|
||||
* Fork an existing agent session, returning a new session ID that branches
|
||||
* from the source session's state. Used by `step ask` (Phase 2a infrastructure)
|
||||
* to spawn a side conversation from a completed step's session without
|
||||
* polluting the original session's history.
|
||||
*/
|
||||
export type AgentForkFn = (sessionId: string, store: AgentContext["store"]) => Promise<string>;
|
||||
|
||||
/**
|
||||
* Clean up adapter-level resources (e.g. close ACP client, kill subprocesses).
|
||||
* Invoked by the agent CLI factory after the run completes — regardless of
|
||||
* success or failure — so adapters can release I/O handles deterministically.
|
||||
*/
|
||||
export type AgentCleanupFn = () => Promise<void>;
|
||||
|
||||
export type AdapterOutput = {
|
||||
stepHash: string;
|
||||
detailHash: string;
|
||||
@@ -65,4 +80,14 @@ export type AgentOptions = {
|
||||
name: string;
|
||||
run: AgentRunFn;
|
||||
continue: AgentContinueFn;
|
||||
/**
|
||||
* Optional session-fork hook. null means the adapter does not yet support
|
||||
* `step ask` (Phase 2a placeholder — wired up in Phase 2b).
|
||||
*/
|
||||
fork: AgentForkFn | null;
|
||||
/**
|
||||
* Optional cleanup hook invoked after the agent CLI completes. null means
|
||||
* the adapter has no resources to release.
|
||||
*/
|
||||
cleanup: AgentCleanupFn | null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user