paqad-ai
Version:
Spec-driven development framework — AI agents that think before they type
373 lines (368 loc) • 12.6 kB
JavaScript
import { createRequire as __paqadCreateRequire } from 'module'; const require = __paqadCreateRequire(import.meta.url);
import {
currentStageOfAgent,
isChecksRed,
judgeStageReport,
resolveStageReport
} from "./chunk-WSTTWUET.js";
import {
estimateTokens
} from "./chunk-F657QMM6.js";
import {
readTranscriptUsage
} from "./chunk-YZTSRIE3.js";
import {
judgeMustNot,
transcriptToolUses
} from "./chunk-GCMANDHS.js";
import {
extractAssistantText,
extractMarkers
} from "./chunk-OAEANL22.js";
import {
appendFeatureStageRow,
currentFeature,
readFeatureStageUnit
} from "./chunk-5M55PDWL.js";
import {
STAGE_AGENT_KIND
} from "./chunk-7TBVBCDH.js";
import {
sha256Hex
} from "./chunk-RJMJYPHL.js";
import {
MANDATORY_STAGE_AGENTS,
STAGE_AGENT_STAGES,
stageForAgentName,
stripPluginAgentNamespace
} from "./chunk-NCYDV2N5.js";
import {
STAGE_EFFORT_LEVELS,
STAGE_MODELS,
resolveStageAgentSettings
} from "./chunk-ACJQW7SB.js";
import {
resolveHookSession
} from "./chunk-SG6LXLDH.js";
import {
PATHS
} from "./chunk-IHMBVS6H.js";
import {
init_esm_shims
} from "./chunk-IDFRWBMN.js";
// src/stage-isolation/subagent-completion.ts
init_esm_shims();
import { readFileSync as readFileSync2 } from "fs";
// src/stage-isolation/stage-settings.ts
init_esm_shims();
import { mkdirSync, readFileSync, writeFileSync } from "fs";
import { dirname, join } from "path";
var SAFE_SESSION_ID = /^[A-Za-z0-9._-]{1,128}$/;
function isSafeSessionId(sessionId) {
return SAFE_SESSION_ID.test(sessionId);
}
var STAGE_SETTINGS_DIR = "stage-settings.d";
function stageSettingsPath(projectRoot, sessionId) {
return join(projectRoot, PATHS.AGENCY_SESSION_DIR, STAGE_SETTINGS_DIR, `${sessionId}.json`);
}
function writeStageSettings(projectRoot, sessionId, env = process.env) {
if (!isSafeSessionId(sessionId)) {
throw new Error("unsafe session id");
}
const settings = resolveStageAgentSettings(projectRoot, env);
const path = stageSettingsPath(projectRoot, sessionId);
mkdirSync(dirname(path), { recursive: true });
writeFileSync(path, `${JSON.stringify(settings, null, 2)}
`, "utf8");
return settings;
}
function effortToPass(configured, fileDefault) {
if (configured !== "inherit") return `effort ${configured}`;
if (fileDefault !== "inherit") {
return `your own current effort level as effort, because its agent file defaults to ${fileDefault} effort`;
}
return null;
}
function modelToPass(configured, fileDefault) {
if (configured !== "inherit") return `model ${configured}`;
if (fileDefault !== "inherit") {
return `your own current model as model, because its agent file defaults to ${fileDefault}`;
}
return null;
}
function stagePassAdvice(setting, fileDefault) {
const parts = [
effortToPass(setting.effort, fileDefault.effort),
modelToPass(setting.model, fileDefault.model)
].filter((part) => part !== null);
if (parts.length === 0) {
return "pass nothing on its Agent call, so it runs at the session's own effort and model";
}
return `on its Agent call, pass ${parts.join(" and ")}`;
}
function stageSettingsReport(settings) {
const fileDefaults = resolveStageAgentSettings();
return Object.fromEntries(
Object.entries(settings).map(([stage, setting]) => [
stage,
{ ...setting, pass: stagePassAdvice(setting, fileDefaults[stage]) }
])
);
}
function isStageAgentSetting(value) {
if (!value || typeof value !== "object") return false;
const { effort, model } = value;
return STAGE_EFFORT_LEVELS.includes(effort) && STAGE_MODELS.includes(model);
}
function readStageSetting(projectRoot, sessionId, stage) {
if (!isSafeSessionId(sessionId)) return null;
try {
const parsed = JSON.parse(
readFileSync(stageSettingsPath(projectRoot, sessionId), "utf8")
);
const setting = parsed?.[stage];
return isStageAgentSetting(setting) ? { effort: setting.effort, model: setting.model } : null;
} catch {
return null;
}
}
// src/stage-isolation/subagent-completion.ts
function stageFromAgentType(agentType) {
if (typeof agentType !== "string") {
return null;
}
return stageForAgentName(stripPluginAgentNamespace(agentType));
}
function nonNegativeInt(value) {
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : null;
}
function resolveTokenCounts(payload, transcriptText) {
const usage = payload.usage ?? void 0;
const input = usage ? nonNegativeInt(usage.input_tokens) : null;
const output = usage ? nonNegativeInt(usage.output_tokens) : null;
if (input !== null && output !== null) {
const cached = nonNegativeInt(usage?.cache_read_input_tokens) ?? nonNegativeInt(usage?.cached_input_tokens) ?? 0;
return { tokens_input: input, tokens_cached: cached, tokens_output: output, exact: true };
}
const estimate = estimateTokens(transcriptText);
return { tokens_input: estimate, tokens_cached: 0, tokens_output: 0, exact: false };
}
function readAgentTranscriptText(path) {
try {
return readFileSync2(path, "utf8");
} catch {
return null;
}
}
function resolveStageAgentTokens(payload, transcriptText) {
const path = payload.agent_transcript_path;
if (typeof path === "string" && path.trim() !== "") {
const agentText = readAgentTranscriptText(path);
const usage = readTranscriptUsage(path);
if (agentText !== null && usage.messages.length > 0) {
return {
tokens_used: usage.totals.total,
tokens_not_recarried: estimateTokens(agentText),
exact: true
};
}
}
const counts = resolveTokenCounts(payload, transcriptText);
return {
tokens_used: counts.tokens_input + counts.tokens_output,
tokens_not_recarried: estimateTokens(transcriptText),
exact: false
};
}
function requestedSetting(projectRoot, sessionId, stage) {
const setting = readStageSetting(projectRoot, sessionId, stage);
return setting ? { requested_effort: setting.effort, requested_model: setting.model } : {};
}
function agentIdField(payload) {
return typeof payload.agent_id === "string" && payload.agent_id.trim() !== "" ? { agent_id: payload.agent_id.trim() } : {};
}
function transcriptStartedStages(payload) {
const path = payload.agent_transcript_path;
if (typeof path !== "string" || path.trim() === "") return [];
const text = readAgentTranscriptText(path);
if (text === null) return [];
return extractMarkers(extractAssistantText(text)).filter((marker) => marker.phase === "start").map((marker) => marker.stage);
}
function ledgerStartedStages(projectRoot, dirName, agentId) {
return readFeatureStageUnit(projectRoot, dirName).filter((row) => row.kind === "stage_start" && row.agent_id === agentId).map((row) => String(row.stage));
}
function stagesRunByAgent(projectRoot, dirName, stage, payload) {
const agentId = agentIdField(payload).agent_id;
const ran = /* @__PURE__ */ new Set([
stage,
...transcriptStartedStages(payload),
...agentId ? ledgerStartedStages(projectRoot, dirName, agentId) : []
]);
return STAGE_AGENT_STAGES.filter((candidate) => ran.has(candidate));
}
function agentTurns(payload) {
const path = payload.agent_transcript_path;
if (typeof path !== "string" || path.trim() === "") return null;
const usage = readTranscriptUsage(path);
return usage.messages.length > 0 ? usage.messages.length : null;
}
function turnLimitReason(stage, turns) {
const limit = MANDATORY_STAGE_AGENTS.find((def) => def.ledgerStage === stage)?.maxTurns;
return limit !== void 0 && turns !== null && turns >= limit ? `${stage} reached its turn limit of ${limit} and returned no report` : null;
}
function judgeStageAgentReturn(stage, payload, context) {
const rows = readFeatureStageUnit(context.projectRoot, context.dirName);
const agentId = agentIdField(payload).agent_id ?? null;
const report = resolveStageReport({
rows,
agentId,
payload,
adapter: context.adapter,
fallbackTranscript: context.fallbackTranscript
});
const path = payload.agent_transcript_path;
const text = typeof path === "string" && path.trim() !== "" ? readAgentTranscriptText(path) : null;
const violations = text === null ? [] : judgeMustNot(stage, transcriptToolUses(text));
const current = currentStageOfAgent(
rows,
agentId,
typeof payload.agent_type === "string" ? payload.agent_type : null
) ?? stage;
const turns = agentTurns(payload);
const row = report.handbackRow;
const shared = {
report_source: report.source,
gate_checked: report.gateChecked,
...turns === null ? {} : { api_turns: turns },
...violations.length > 0 ? { must_not: violations } : {}
};
if (row) {
return {
...shared,
report_status: String(row.report_status),
report_stated: row.report_stated === true,
advance: row.advance === true && violations.length === 0,
decision: typeof row.decision === "string" ? row.decision : null,
report_hash: String(row.report_hash),
...typeof row.stated_status === "string" ? { stated_status: row.stated_status } : {},
...typeof row.reason === "string" ? { reason: row.reason } : {}
};
}
const judged = judgeStageReport({
stage: current,
agentId,
reading: report.reading,
rows,
violations,
checksRed: isChecksRed(context.projectRoot, context.dirName, current)
});
const turnLimit = report.source === "none" ? turnLimitReason(current, turns) : null;
const reason = judged.reason ?? turnLimit;
return {
...shared,
report_status: judged.report_status,
report_stated: judged.report_stated,
advance: judged.advance,
decision: judged.decision,
...report.message === null ? {} : { report_hash: sha256Hex(report.message) },
...judged.stated_status ? { stated_status: judged.stated_status } : {},
...reason ? { reason } : {}
};
}
function recordStageAgentStart(input) {
try {
const agentType = typeof input.payload.agent_type === "string" ? input.payload.agent_type : null;
const stage = stageFromAgentType(agentType);
if (!stage || agentType === null) {
return false;
}
const sessionId = resolveHookSession(input.projectRoot, input.payload);
const dirName = currentFeature(input.projectRoot, sessionId);
if (!dirName) {
return false;
}
appendFeatureStageRow(
input.projectRoot,
sessionId,
dirName,
{
kind: STAGE_AGENT_KIND,
stage,
agent: agentType,
...agentIdField(input.payload),
event_status: "started",
host_pid: process.ppid,
tokens_used: 0,
tokens_not_recarried: 0,
estimate: true
},
input.now
);
return true;
} catch {
return false;
}
}
function recordStageAgentCompletion(input) {
try {
const stage = stageFromAgentType(
typeof input.payload.agent_type === "string" ? input.payload.agent_type : null
);
if (!stage) {
return false;
}
const sessionId = resolveHookSession(input.projectRoot, input.payload);
const dirName = currentFeature(input.projectRoot, sessionId);
if (!dirName) {
return false;
}
const tokens = resolveStageAgentTokens(input.payload, input.transcriptText);
const stages = stagesRunByAgent(input.projectRoot, dirName, stage, input.payload);
const judgement = judgeStageAgentReturn(stage, input.payload, {
projectRoot: input.projectRoot,
dirName,
adapter: input.adapter,
fallbackTranscript: input.transcriptText
});
if (judgement.report_source === "none") {
input.log?.(
`stage-agent-completion: no report found agent=${agentIdField(input.payload).agent_id ?? "none"} stage=${stage}`
);
}
appendFeatureStageRow(
input.projectRoot,
sessionId,
dirName,
{
kind: STAGE_AGENT_KIND,
stage,
...stages.length > 1 ? { stages } : {},
agent: input.payload.agent_type,
...agentIdField(input.payload),
event_status: "completed",
tokens_used: tokens.tokens_used,
tokens_not_recarried: tokens.tokens_not_recarried,
estimate: !tokens.exact,
...requestedSetting(input.projectRoot, sessionId, stage),
...judgement
},
input.now
);
return true;
} catch {
return false;
}
}
export {
SAFE_SESSION_ID,
isSafeSessionId,
writeStageSettings,
stageSettingsReport,
stageFromAgentType,
resolveTokenCounts,
resolveStageAgentTokens,
stagesRunByAgent,
judgeStageAgentReturn,
recordStageAgentStart,
recordStageAgentCompletion
};