Files
stack/packages/mosaic/framework/runtime/pi/goal-extension.ts
T
2026-08-10 17:33:34 -05:00

1089 lines
37 KiB
TypeScript

import { createHash, randomUUID } from 'node:crypto';
import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';
const STATE_ENTRY_TYPE = 'mosaic-goal-state';
const CONTEXT_MESSAGE_TYPE = 'mosaic-goal-context';
const CONTINUATION_MESSAGE_TYPE = 'mosaic-goal-continuation';
const GOAL_REPORT_TOOL = 'mosaic_goal_report';
const STATUS_KEY = 'mosaic-goal';
const STATE_VERSION = 1 as const;
const MAX_STATEMENT_LENGTH = 8_000;
const MAX_SUMMARY_LENGTH = 2_000;
const MAX_EVIDENCE_ITEMS = 20;
const MAX_EVIDENCE_LENGTH = 1_000;
const MAX_NEXT_STEP_LENGTH = 2_000;
const DEFAULT_MAX_TURNS = 40;
const DEFAULT_MAX_NO_PROGRESS = 6;
const REQUIRED_VERIFICATION_PASSES = 2;
const DEFERRED_CONTINUATION_MS = 10;
const REDACTED_SECRET = '[REDACTED-SECRET]';
const PRIVATE_KEY_PATTERN =
/-----BEGIN(?: [A-Z0-9]+)* PRIVATE KEY(?: BLOCK)?-----[\s\S]*?(?:-----END(?: [A-Z0-9]+)* PRIVATE KEY(?: BLOCK)?-----|$)/g;
const CREDENTIAL_URL_PATTERN = /\b([A-Za-z][A-Za-z0-9+.-]*:\/\/)[^\s/:]+:[^\s/@]+@/g;
const AUTHORIZATION_PATTERN =
/(\b(?:authorization|proxy-authorization)\s*[:=]\s*)(bearer|basic)\s+[^\s,;]+/gi;
const STANDALONE_AUTH_PATTERN = /\b(bearer|basic)\s+[A-Za-z0-9._~+/=-]{16,}/gi;
const JWT_PATTERN = /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g;
const KNOWN_SECRET_PATTERN =
/\b(?:AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|glpat-[A-Za-z0-9_-]{20,}|npm_[A-Za-z0-9]{20,}|sk-(?:ant-(?:api\d{2}-)?|proj-)?[A-Za-z0-9_-]{20,}|(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}|xox[baprs]-[A-Za-z0-9-]{10,}|hf_[A-Za-z0-9]{20,})\b/g;
const SENSITIVE_ASSIGNMENT_PATTERN =
/((?:["']?(?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth(?:orization)?[_-]?token|client[_-]?secret|password|passwd|secret(?:[_-]?access[_-]?key)?|private[_-]?key|database[_-]?url|token|cookie|set[_-]?cookie)["']?)\s*)((?:=|:)\s*)("[^"\r\n]*"|'[^'\r\n]*'|[^\s,;]+)/gi;
const GOAL_PHASES = [
'active',
'verifying',
'paused',
'blocked',
'achieved',
'cancelled',
'exhausted',
] as const;
const REPORT_STATUSES = ['continue', 'achieved', 'blocked'] as const;
const CHECK_SOURCES = ['command', 'turn', 'compact', 'restore', 'report'] as const;
type GoalPhase = (typeof GOAL_PHASES)[number];
type GoalReportStatus = (typeof REPORT_STATUSES)[number];
type GoalCheckSource = (typeof CHECK_SOURCES)[number];
interface GoalReport {
status: GoalReportStatus;
summary: string;
evidence: string[];
nextStep?: string;
fingerprint: string;
reportedAt: string;
}
interface GoalState {
version: typeof STATE_VERSION;
goalId: string;
statement: string;
phase: GoalPhase;
startedAt: string;
updatedAt: string;
turnCount: number;
reportCount: number;
verificationPasses: number;
requiredVerificationPasses: number;
noProgressReports: number;
maxTurns: number;
maxNoProgressReports: number;
compactionCount: number;
lastCheckSource: GoalCheckSource;
lastCheckAt: string;
lastCheckOutcome: string;
lastProgressFingerprint?: string;
lastReport?: GoalReport;
stopReason?: string;
}
interface GoalReportInput {
status: GoalReportStatus;
summary: string;
evidence: string[];
nextStep?: string;
}
interface GoalLimits {
maxTurns: number;
maxNoProgressReports: number;
}
interface GoalCommand {
action: 'set' | 'status' | 'pause' | 'resume' | 'cancel' | 'help';
value: string;
}
const GoalReportParameters = {
type: 'object',
properties: {
status: {
type: 'string',
enum: REPORT_STATUSES,
description:
'continue while work remains, achieved only with completion evidence, or blocked',
},
summary: {
type: 'string',
minLength: 1,
maxLength: MAX_SUMMARY_LENGTH,
description: 'Concise progress or completion assessment',
},
evidence: {
type: 'array',
items: { type: 'string', minLength: 1, maxLength: MAX_EVIDENCE_LENGTH },
maxItems: MAX_EVIDENCE_ITEMS,
description: 'Concrete observations, commands, tests, or artifacts supporting the status',
},
nextStep: {
type: 'string',
minLength: 1,
maxLength: MAX_NEXT_STEP_LENGTH,
description: 'The next concrete action when work remains',
},
},
required: ['status', 'summary', 'evidence'],
additionalProperties: false,
} as const;
function nowIso(): string {
return new Date().toISOString();
}
function shouldRedactSensitiveAssignment(separator: string, rawValue: string): boolean {
if (separator.trim() === '=') return true;
const quoted =
(rawValue.startsWith('"') && rawValue.endsWith('"')) ||
(rawValue.startsWith("'") && rawValue.endsWith("'"));
if (quoted || rawValue.includes('://')) return true;
if (/^[a-f0-9]{20,}$/i.test(rawValue)) return true;
return (
rawValue.length >= 20 &&
/[A-Za-z]/.test(rawValue) &&
/\d/.test(rawValue) &&
/[-_./+=]/.test(rawValue)
);
}
function redactSensitiveText(value: string): string {
let redacted = value.replace(PRIVATE_KEY_PATTERN, REDACTED_SECRET);
redacted = redacted.replace(
CREDENTIAL_URL_PATTERN,
(_match: string, prefix: string): string => `${prefix}${REDACTED_SECRET}@`,
);
redacted = redacted.replace(
AUTHORIZATION_PATTERN,
(_match: string, prefix: string, scheme: string): string =>
`${prefix}${scheme} ${REDACTED_SECRET}`,
);
redacted = redacted.replace(
STANDALONE_AUTH_PATTERN,
(_match: string, scheme: string): string => `${scheme} ${REDACTED_SECRET}`,
);
redacted = redacted.replace(JWT_PATTERN, REDACTED_SECRET);
redacted = redacted.replace(KNOWN_SECRET_PATTERN, REDACTED_SECRET);
return redacted.replace(
SENSITIVE_ASSIGNMENT_PATTERN,
(_match: string, prefix: string, separator: string, rawValue: string): string =>
shouldRedactSensitiveAssignment(separator, rawValue)
? `${prefix}${separator}${REDACTED_SECRET}`
: _match,
);
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function isOneOf<const TValue extends string>(
value: unknown,
allowed: readonly TValue[],
): value is TValue {
return (
typeof value === 'string' && allowed.some((candidate: TValue): boolean => candidate === value)
);
}
function parseBoundedInteger(
value: string | undefined,
fallback: number,
minimum: number,
maximum: number,
): number {
if (value === undefined || !/^\d+$/.test(value.trim())) return fallback;
const parsed = Number.parseInt(value, 10);
if (!Number.isSafeInteger(parsed) || parsed < minimum || parsed > maximum) return fallback;
return parsed;
}
function readGoalLimits(env: NodeJS.ProcessEnv): GoalLimits {
return {
maxTurns: parseBoundedInteger(env['MOSAIC_GOAL_MAX_TURNS'], DEFAULT_MAX_TURNS, 1, 500),
maxNoProgressReports: parseBoundedInteger(
env['MOSAIC_GOAL_MAX_NO_PROGRESS'],
DEFAULT_MAX_NO_PROGRESS,
1,
100,
),
};
}
function parseStringArray(value: unknown): string[] | undefined {
if (!Array.isArray(value) || value.length > MAX_EVIDENCE_ITEMS) return undefined;
const result: string[] = [];
for (const item of value) {
if (typeof item !== 'string') return undefined;
const normalized = item.trim();
if (normalized.length === 0 || normalized.length > MAX_EVIDENCE_LENGTH) return undefined;
result.push(normalized);
}
return result;
}
function parseGoalReport(value: unknown): GoalReport | undefined {
if (!isRecord(value)) return undefined;
if (!isOneOf(value['status'], REPORT_STATUSES)) return undefined;
if (
typeof value['summary'] !== 'string' ||
value['summary'].length === 0 ||
value['summary'].length > MAX_SUMMARY_LENGTH
) {
return undefined;
}
const evidence = parseStringArray(value['evidence']);
if (evidence === undefined) return undefined;
if (typeof value['fingerprint'] !== 'string' || !/^[a-f0-9]{64}$/.test(value['fingerprint'])) {
return undefined;
}
if (
typeof value['reportedAt'] !== 'string' ||
value['reportedAt'].length === 0 ||
value['reportedAt'].length > 64
) {
return undefined;
}
const nextStepValue = value['nextStep'];
let nextStep: string | undefined;
if (nextStepValue !== undefined) {
if (
typeof nextStepValue !== 'string' ||
nextStepValue.length === 0 ||
nextStepValue.length > MAX_NEXT_STEP_LENGTH
) {
return undefined;
}
nextStep = nextStepValue;
}
return {
status: value['status'],
summary: value['summary'],
evidence,
...(nextStep === undefined ? {} : { nextStep }),
fingerprint: value['fingerprint'],
reportedAt: value['reportedAt'],
};
}
function readIntegerField(
value: Record<string, unknown>,
field: string,
minimum: number,
maximum: number = Number.MAX_SAFE_INTEGER,
): number | undefined {
const candidate = value[field];
if (
!Number.isSafeInteger(candidate) ||
typeof candidate !== 'number' ||
candidate < minimum ||
candidate > maximum
) {
return undefined;
}
return candidate;
}
function parseGoalState(value: unknown): GoalState | undefined {
if (!isRecord(value) || value['version'] !== STATE_VERSION) return undefined;
if (
typeof value['goalId'] !== 'string' ||
value['goalId'].length === 0 ||
value['goalId'].length > 128
) {
return undefined;
}
if (
typeof value['statement'] !== 'string' ||
value['statement'].length === 0 ||
value['statement'].length > MAX_STATEMENT_LENGTH
) {
return undefined;
}
if (!isOneOf(value['phase'], GOAL_PHASES)) return undefined;
if (!isOneOf(value['lastCheckSource'], CHECK_SOURCES)) return undefined;
if (
typeof value['startedAt'] !== 'string' ||
value['startedAt'].length > 64 ||
typeof value['updatedAt'] !== 'string' ||
value['updatedAt'].length > 64
) {
return undefined;
}
if (
typeof value['lastCheckAt'] !== 'string' ||
value['lastCheckAt'].length > 64 ||
typeof value['lastCheckOutcome'] !== 'string' ||
value['lastCheckOutcome'].length > 256
) {
return undefined;
}
const turnCount = readIntegerField(value, 'turnCount', 0, 500);
const reportCount = readIntegerField(value, 'reportCount', 0, 100_000);
const verificationPasses = readIntegerField(
value,
'verificationPasses',
0,
REQUIRED_VERIFICATION_PASSES,
);
const requiredVerificationPasses = readIntegerField(
value,
'requiredVerificationPasses',
REQUIRED_VERIFICATION_PASSES,
REQUIRED_VERIFICATION_PASSES,
);
const noProgressReports = readIntegerField(value, 'noProgressReports', 0, 100);
const maxTurns = readIntegerField(value, 'maxTurns', 1, 500);
const maxNoProgressReports = readIntegerField(value, 'maxNoProgressReports', 1, 100);
const compactionCount = readIntegerField(value, 'compactionCount', 0, 100_000);
if (
turnCount === undefined ||
reportCount === undefined ||
verificationPasses === undefined ||
requiredVerificationPasses === undefined ||
noProgressReports === undefined ||
maxTurns === undefined ||
maxNoProgressReports === undefined ||
compactionCount === undefined
) {
return undefined;
}
const lastReportValue = value['lastReport'];
const lastReport = lastReportValue === undefined ? undefined : parseGoalReport(lastReportValue);
if (lastReportValue !== undefined && lastReport === undefined) return undefined;
const lastProgressFingerprintValue = value['lastProgressFingerprint'];
let lastProgressFingerprint: string | undefined;
if (lastProgressFingerprintValue !== undefined) {
if (
typeof lastProgressFingerprintValue !== 'string' ||
!/^[a-f0-9]{64}$/.test(lastProgressFingerprintValue)
) {
return undefined;
}
lastProgressFingerprint = lastProgressFingerprintValue;
}
const stopReasonValue = value['stopReason'];
let stopReason: string | undefined;
if (stopReasonValue !== undefined) {
if (typeof stopReasonValue !== 'string' || stopReasonValue.length > MAX_SUMMARY_LENGTH) {
return undefined;
}
stopReason = stopReasonValue;
}
return {
version: STATE_VERSION,
goalId: value['goalId'],
statement: value['statement'],
phase: value['phase'],
startedAt: value['startedAt'],
updatedAt: value['updatedAt'],
turnCount,
reportCount,
verificationPasses,
requiredVerificationPasses,
noProgressReports,
maxTurns,
maxNoProgressReports,
compactionCount,
lastCheckSource: value['lastCheckSource'],
lastCheckAt: value['lastCheckAt'],
lastCheckOutcome: value['lastCheckOutcome'],
...(lastProgressFingerprint === undefined ? {} : { lastProgressFingerprint }),
...(lastReport === undefined ? {} : { lastReport }),
...(stopReason === undefined ? {} : { stopReason }),
};
}
function copyReport(report: GoalReport): GoalReport {
return {
...report,
evidence: [...report.evidence],
};
}
function copyState(state: GoalState): GoalState {
return {
...state,
...(state.lastReport === undefined ? {} : { lastReport: copyReport(state.lastReport) }),
};
}
function parseGoalReportInput(value: unknown): GoalReportInput {
if (!isRecord(value) || !isOneOf(value['status'], REPORT_STATUSES)) {
throw new Error('Goal report status must be continue, achieved, or blocked.');
}
if (typeof value['summary'] !== 'string') throw new Error('Goal report summary is required.');
const summary = value['summary'].trim();
if (summary.length === 0 || summary.length > MAX_SUMMARY_LENGTH) {
throw new Error(`Goal report summary must be 1-${MAX_SUMMARY_LENGTH} characters.`);
}
const evidence = parseStringArray(value['evidence']);
if (evidence === undefined) throw new Error('Goal report evidence is invalid.');
if (value['status'] === 'achieved' && evidence.length === 0) {
throw new Error('An achieved goal report requires concrete evidence.');
}
const rawNextStep = value['nextStep'];
if (rawNextStep !== undefined && typeof rawNextStep !== 'string') {
throw new Error('Goal report nextStep must be text.');
}
const nextStep = typeof rawNextStep === 'string' ? rawNextStep.trim() : undefined;
if (nextStep !== undefined && (nextStep.length === 0 || nextStep.length > MAX_NEXT_STEP_LENGTH)) {
throw new Error(`Goal report nextStep must be 1-${MAX_NEXT_STEP_LENGTH} characters.`);
}
return redactGoalReportInput({
status: value['status'],
summary,
evidence,
...(nextStep === undefined ? {} : { nextStep }),
});
}
function reportFingerprint(report: GoalReportInput): string {
const normalized = JSON.stringify({
summary: report.summary.trim().toLowerCase(),
evidence: report.evidence.map((item: string): string => item.trim().toLowerCase()),
nextStep: report.nextStep?.trim().toLowerCase() ?? '',
});
return createHash('sha256').update(normalized).digest('hex');
}
function redactGoalReportInput(report: GoalReportInput): GoalReportInput {
return {
status: report.status,
summary: redactSensitiveText(report.summary),
evidence: report.evidence.map(redactSensitiveText),
...(report.nextStep === undefined ? {} : { nextStep: redactSensitiveText(report.nextStep) }),
};
}
function redactGoalReport(report: GoalReport): GoalReport {
const redactedInput = redactGoalReportInput(report);
return {
...redactedInput,
fingerprint: reportFingerprint(redactedInput),
reportedAt: report.reportedAt,
};
}
function redactGoalState(state: GoalState): GoalState {
const lastReport =
state.lastReport === undefined ? undefined : redactGoalReport(state.lastReport);
const lastProgressFingerprint =
lastReport !== undefined && state.lastProgressFingerprint === state.lastReport?.fingerprint
? lastReport.fingerprint
: state.lastProgressFingerprint;
return {
...state,
statement: redactSensitiveText(state.statement),
...(lastReport === undefined ? {} : { lastReport }),
...(lastProgressFingerprint === undefined ? {} : { lastProgressFingerprint }),
...(state.stopReason === undefined
? {}
: { stopReason: redactSensitiveText(state.stopReason) }),
};
}
function goalStateContainsSensitiveText(state: GoalState): boolean {
const textValues = [state.statement];
if (state.stopReason !== undefined) textValues.push(state.stopReason);
if (state.lastReport !== undefined) {
textValues.push(state.lastReport.summary, ...state.lastReport.evidence);
if (state.lastReport.nextStep !== undefined) textValues.push(state.lastReport.nextStep);
}
return textValues.some((value: string): boolean => redactSensitiveText(value) !== value);
}
function isContinuingPhase(phase: GoalPhase): boolean {
return phase === 'active' || phase === 'verifying';
}
function canReplaceGoal(state: GoalState | undefined): boolean {
return state === undefined || state.phase === 'achieved' || state.phase === 'cancelled';
}
function parseGoalCommand(args: string): GoalCommand {
const trimmed = args.trim();
if (trimmed.length === 0) return { action: 'help', value: '' };
const separator = trimmed.indexOf(' ');
const first = (separator === -1 ? trimmed : trimmed.slice(0, separator)).toLowerCase();
const value = separator === -1 ? '' : trimmed.slice(separator + 1).trim();
if (first === 'set') return { action: 'set', value };
if (first === 'status') return { action: 'status', value };
if (first === 'pause') return { action: 'pause', value };
if (first === 'resume') return { action: 'resume', value };
if (first === 'cancel' || first === 'clear') return { action: 'cancel', value };
if (first === 'help') return { action: 'help', value };
return { action: 'set', value: trimmed };
}
function formatStatus(state: GoalState | undefined): string {
if (state === undefined) return 'No Mosaic goal is set. Use /goal set <statement>.';
const lines = [
`Goal ${state.goalId}`,
`Phase: ${state.phase}`,
`Turns: ${state.turnCount}/${state.maxTurns}`,
`Verification: ${state.verificationPasses}/${state.requiredVerificationPasses}`,
`No-progress reports: ${state.noProgressReports}/${state.maxNoProgressReports}`,
`Compactions checked: ${state.compactionCount}`,
`Goal: ${state.statement}`,
];
if (state.lastReport !== undefined) {
lines.push(`Latest report: ${state.lastReport.status}${state.lastReport.summary}`);
if (state.lastReport.evidence.length > 0) {
lines.push(
'Evidence:',
...state.lastReport.evidence.map((item: string): string => `- ${item}`),
);
}
if (state.lastReport.nextStep !== undefined) {
lines.push(`Next step: ${state.lastReport.nextStep}`);
}
}
if (state.stopReason !== undefined) lines.push(`Stopped: ${state.stopReason}`);
return lines.join('\n');
}
function buildGoalContract(state: GoalState): string {
const latest = state.lastReport;
const verificationInstruction =
state.phase === 'verifying'
? 'This is a verification pass. Re-inspect the actual result and rerun relevant checks; do not rely only on the prior claim.'
: 'Continue making concrete progress toward the goal.';
const lines = [
'[MOSAIC GOAL LOOP v1]',
`Goal ID: ${state.goalId}`,
`Goal: ${state.statement}`,
`Phase: ${state.phase}`,
`Budget: turn ${state.turnCount}/${state.maxTurns}; repeated no-progress reports ${state.noProgressReports}/${state.maxNoProgressReports}.`,
verificationInstruction,
'',
'Completion protocol:',
`- Before ending the work cycle, call ${GOAL_REPORT_TOOL} as the only tool call in the final assistant response.`,
'- Use status=continue whenever any requirement remains and provide the next concrete step.',
'- Use status=achieved only when concrete evidence covers the entire stated goal.',
'- Use status=blocked only for a genuine blocker that prevents meaningful progress.',
`- Achievement requires ${state.requiredVerificationPasses} consecutive evidence-bearing reports; the first claim starts a separate verification pass.`,
'- Never include secrets, tokens, credentials, private keys, or raw sensitive output in a report.',
'- Do not ask routine permission to continue. The operator can pause or cancel with /goal.',
];
if (latest !== undefined) {
lines.push('', `Previous report: ${latest.status}${latest.summary}`);
if (latest.nextStep !== undefined) lines.push(`Previous next step: ${latest.nextStep}`);
}
return lines.join('\n');
}
function continuationText(state: GoalState, reason: string): string {
if (state.phase === 'verifying') {
return `Goal ${state.goalId} requires a verification pass after ${reason}. Recheck the complete goal and report fresh evidence with ${GOAL_REPORT_TOOL}.`;
}
return `Goal remains active after ${reason}. Continue from the latest evidence and finish by calling ${GOAL_REPORT_TOOL}.`;
}
function toolResultIncludesGoalReport(toolResults: unknown): boolean {
if (!Array.isArray(toolResults)) return false;
return toolResults.some(
(result: unknown): boolean => isRecord(result) && result['toolName'] === GOAL_REPORT_TOOL,
);
}
function toolResultCount(toolResults: unknown): number {
return Array.isArray(toolResults) ? toolResults.length : 0;
}
export default function registerGoalExtension(pi: ExtensionAPI): void {
const limits = readGoalLimits(process.env);
let state: GoalState | undefined;
let continuationQueued = false;
let deferredTimer: ReturnType<typeof setTimeout> | undefined;
let lifecycleGeneration = 0;
let reportRollbackState: GoalState | undefined;
function updateStatus(ctx: ExtensionContext): void {
if (state === undefined || state.phase === 'cancelled') {
ctx.ui.setStatus(STATUS_KEY, undefined);
return;
}
ctx.ui.setStatus(STATUS_KEY, `🎯 ${state.phase} ${state.turnCount}/${state.maxTurns}`);
}
function persist(nextState: GoalState, ctx: ExtensionContext): void {
const redactedState = redactGoalState(nextState);
state = copyState(redactedState);
pi.appendEntry(STATE_ENTRY_TYPE, copyState(redactedState));
updateStatus(ctx);
}
function clearDeferredTimer(): void {
if (deferredTimer !== undefined) clearTimeout(deferredTimer);
deferredTimer = undefined;
}
function queueContinuation(
ctx: ExtensionContext,
reason: string,
trackDuplicate: boolean = true,
): void {
if (state === undefined || !isContinuingPhase(state.phase)) return;
if (ctx.hasPendingMessages()) return;
if (trackDuplicate && continuationQueued) return;
if (trackDuplicate) continuationQueued = true;
const options = ctx.isIdle()
? { triggerTurn: true as const }
: { triggerTurn: true as const, deliverAs: 'followUp' as const };
pi.sendMessage(
{
customType: CONTINUATION_MESSAGE_TYPE,
content: continuationText(state, reason),
display: true,
},
options,
);
}
function scheduleIdleContinuation(ctx: ExtensionContext, reason: string): void {
clearDeferredTimer();
const scheduledGeneration = lifecycleGeneration;
deferredTimer = setTimeout((): void => {
deferredTimer = undefined;
if (scheduledGeneration !== lifecycleGeneration || !ctx.isIdle()) return;
queueContinuation(ctx, reason);
}, DEFERRED_CONTINUATION_MS);
}
function restoreState(ctx: ExtensionContext): void {
state = undefined;
let rejectedSensitiveState = false;
for (const entry of ctx.sessionManager.getBranch()) {
if (entry.type !== 'custom' || entry.customType !== STATE_ENTRY_TYPE) continue;
const restoredState = parseGoalState(entry.data);
if (restoredState === undefined) {
state = undefined;
} else if (goalStateContainsSensitiveText(restoredState)) {
state = undefined;
rejectedSensitiveState = true;
} else if (rejectedSensitiveState) {
state = undefined;
} else {
state = redactGoalState(restoredState);
}
}
continuationQueued = false;
updateStatus(ctx);
if (rejectedSensitiveState) {
ctx.ui.notify(
'Mosaic goal state was not restored because persisted text matched a credential pattern. Remove the affected Pi session if it may contain a real secret, then set a new goal.',
'warning',
);
}
}
function createGoal(statement: string, ctx: ExtensionContext): void {
if (!canReplaceGoal(state)) {
ctx.ui.notify(
'A Mosaic goal already exists. Use /goal cancel before replacing it.',
'warning',
);
return;
}
if (statement.length === 0 || statement.length > MAX_STATEMENT_LENGTH) {
ctx.ui.notify(
`Usage: /goal set <statement> (${MAX_STATEMENT_LENGTH.toLocaleString()} characters maximum).`,
'warning',
);
return;
}
const timestamp = nowIso();
const redactedStatement = redactSensitiveText(statement);
persist(
{
version: STATE_VERSION,
goalId: randomUUID(),
statement: redactedStatement,
phase: 'active',
startedAt: timestamp,
updatedAt: timestamp,
turnCount: 0,
reportCount: 0,
verificationPasses: 0,
requiredVerificationPasses: REQUIRED_VERIFICATION_PASSES,
noProgressReports: 0,
maxTurns: limits.maxTurns,
maxNoProgressReports: limits.maxNoProgressReports,
compactionCount: 0,
lastCheckSource: 'command',
lastCheckAt: timestamp,
lastCheckOutcome: 'set',
},
ctx,
);
ctx.ui.notify(`Mosaic goal started: ${redactedStatement}`, 'info');
if (ctx.isIdle()) queueContinuation(ctx, 'goal start', false);
}
pi.registerCommand('goal', {
description: 'Set or control a persistent Mosaic goal loop',
handler: async (args, ctx): Promise<void> => {
const command = parseGoalCommand(args);
if (command.action === 'help') {
ctx.ui.notify(
[
'Mosaic goal commands:',
'/goal set <statement> (or /goal <statement>)',
'/goal status',
'/goal pause [reason]',
'/goal resume',
'/goal cancel',
].join('\n'),
'info',
);
return;
}
if (command.action === 'status') {
ctx.ui.notify(formatStatus(state), 'info');
return;
}
if (command.action === 'set') {
createGoal(command.value, ctx);
return;
}
if (state === undefined) {
ctx.ui.notify('No Mosaic goal is set.', 'warning');
return;
}
const timestamp = nowIso();
if (command.action === 'pause') {
if (!isContinuingPhase(state.phase)) {
ctx.ui.notify(`Goal cannot be paused from phase ${state.phase}.`, 'warning');
return;
}
persist(
{
...state,
phase: 'paused',
updatedAt: timestamp,
lastCheckSource: 'command',
lastCheckAt: timestamp,
lastCheckOutcome: 'paused',
stopReason: command.value || 'Paused by operator.',
},
ctx,
);
clearDeferredTimer();
continuationQueued = false;
if (!ctx.isIdle()) ctx.abort();
ctx.ui.notify('Mosaic goal paused.', 'info');
return;
}
if (command.action === 'cancel') {
persist(
{
...state,
phase: 'cancelled',
updatedAt: timestamp,
lastCheckSource: 'command',
lastCheckAt: timestamp,
lastCheckOutcome: 'cancelled',
stopReason: 'Cancelled by operator.',
},
ctx,
);
clearDeferredTimer();
continuationQueued = false;
if (!ctx.isIdle()) ctx.abort();
ctx.ui.notify('Mosaic goal cancelled.', 'info');
return;
}
if (command.action === 'resume') {
if (state.phase !== 'paused' && state.phase !== 'blocked' && state.phase !== 'exhausted') {
ctx.ui.notify(`Goal cannot be resumed from phase ${state.phase}.`, 'warning');
return;
}
persist(
{
...state,
phase: 'active',
updatedAt: timestamp,
turnCount: 0,
verificationPasses: 0,
noProgressReports: 0,
lastProgressFingerprint: undefined,
lastCheckSource: 'command',
lastCheckAt: timestamp,
lastCheckOutcome: 'resumed',
stopReason: undefined,
},
ctx,
);
continuationQueued = false;
ctx.ui.notify('Mosaic goal resumed with fresh bounded counters.', 'info');
if (ctx.isIdle()) queueContinuation(ctx, 'operator resume', false);
}
},
});
pi.registerTool({
name: GOAL_REPORT_TOOL,
label: 'Mosaic Goal Report',
description:
'Report structured progress for the active Mosaic /goal loop. Call it as the sole final tool when a work cycle is ready to stop, continue, verify, or block.',
promptSnippet: 'Report evidence-backed status for the active Mosaic goal loop',
promptGuidelines: [
'When a Mosaic goal is active, call mosaic_goal_report as the sole tool in the final assistant response for each work cycle.',
'Use mosaic_goal_report status=achieved only when concrete evidence covers the entire active goal.',
],
parameters: GoalReportParameters,
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
if (state === undefined || !isContinuingPhase(state.phase)) {
throw new Error(
'No active Mosaic goal can accept a report. Use /goal set or /goal resume.',
);
}
const input = parseGoalReportInput(params);
const fingerprint = reportFingerprint(input);
const timestamp = nowIso();
const report: GoalReport = {
...input,
fingerprint,
reportedAt: timestamp,
};
reportRollbackState = copyState(state);
let nextState: GoalState;
if (input.status === 'continue') {
const noProgressReports =
fingerprint === state.lastProgressFingerprint ? state.noProgressReports + 1 : 1;
const exhausted = noProgressReports >= state.maxNoProgressReports;
nextState = {
...state,
phase: exhausted ? 'exhausted' : 'active',
updatedAt: timestamp,
reportCount: state.reportCount + 1,
verificationPasses: 0,
noProgressReports,
lastProgressFingerprint: fingerprint,
lastReport: report,
lastCheckSource: 'report',
lastCheckAt: timestamp,
lastCheckOutcome: exhausted ? 'no-progress-limit' : 'continue',
...(exhausted
? {
stopReason: `Repeated no-progress report limit reached (${state.maxNoProgressReports}).`,
}
: { stopReason: undefined }),
};
} else if (input.status === 'achieved') {
const verificationPasses = state.phase === 'verifying' ? state.verificationPasses + 1 : 1;
const achieved = verificationPasses >= state.requiredVerificationPasses;
nextState = {
...state,
phase: achieved ? 'achieved' : 'verifying',
updatedAt: timestamp,
reportCount: state.reportCount + 1,
verificationPasses,
noProgressReports: 0,
lastProgressFingerprint: fingerprint,
lastReport: report,
lastCheckSource: 'report',
lastCheckAt: timestamp,
lastCheckOutcome: achieved ? 'verified-achieved' : 'provisional-achieved',
stopReason: undefined,
};
} else {
nextState = {
...state,
phase: 'blocked',
updatedAt: timestamp,
reportCount: state.reportCount + 1,
verificationPasses: 0,
noProgressReports: 0,
lastProgressFingerprint: fingerprint,
lastReport: report,
lastCheckSource: 'report',
lastCheckAt: timestamp,
lastCheckOutcome: 'blocked',
stopReason: input.summary,
};
}
persist(nextState, ctx);
if (nextState.phase === 'achieved') {
ctx.ui.notify(`Mosaic goal verified.\n${formatStatus(nextState)}`, 'info');
} else if (nextState.phase === 'verifying') {
ctx.ui.notify(
'Goal achievement is provisional; one verification pass is required.',
'info',
);
} else if (nextState.phase === 'blocked' || nextState.phase === 'exhausted') {
ctx.ui.notify(`Mosaic goal stopped in phase ${nextState.phase}.`, 'warning');
}
return {
content: [
{
type: 'text',
text:
nextState.phase === 'achieved'
? 'Goal verification complete.'
: `Goal report recorded; phase is ${nextState.phase}.`,
},
],
details: { state: copyState(nextState), report: copyReport(report) },
terminate: true,
};
},
});
pi.on('context', async (event) => {
const messages = event.messages.filter(
(message) =>
message.role !== 'custom' ||
(message.customType !== CONTEXT_MESSAGE_TYPE &&
message.customType !== CONTINUATION_MESSAGE_TYPE),
);
if (state === undefined || !isContinuingPhase(state.phase)) {
return messages.length === event.messages.length ? undefined : { messages };
}
messages.push({
role: 'custom',
customType: CONTEXT_MESSAGE_TYPE,
content: buildGoalContract(state),
display: false,
timestamp: Date.now(),
});
return { messages };
});
pi.on('turn_end', async (event, ctx) => {
if (state === undefined || state.phase === 'paused' || state.phase === 'cancelled') return;
const hasGoalReport = toolResultIncludesGoalReport(event.toolResults);
if (!isContinuingPhase(state.phase) && !hasGoalReport) return;
const timestamp = nowIso();
let nextState = {
...state,
updatedAt: timestamp,
turnCount: state.turnCount + 1,
lastCheckSource: 'turn' as const,
lastCheckAt: timestamp,
lastCheckOutcome: hasGoalReport ? 'reported' : 'checked-unreported',
};
if (
hasGoalReport &&
toolResultCount(event.toolResults) !== 1 &&
reportRollbackState !== undefined
) {
nextState = {
...reportRollbackState,
phase: 'active',
updatedAt: timestamp,
turnCount: reportRollbackState.turnCount + 1,
verificationPasses: 0,
lastCheckSource: 'turn',
lastCheckAt: timestamp,
lastCheckOutcome: 'mixed-goal-report-rejected',
stopReason: undefined,
};
ctx.ui.notify(
`${GOAL_REPORT_TOOL} must be the only tool call in its final response; the mixed report was ignored.`,
'warning',
);
}
reportRollbackState = undefined;
if (isContinuingPhase(nextState.phase) && nextState.turnCount >= nextState.maxTurns) {
nextState = {
...nextState,
phase: 'exhausted',
lastCheckOutcome: 'max-turn-limit',
stopReason: `Maximum autonomous turn limit reached (${nextState.maxTurns}).`,
};
persist(nextState, ctx);
ctx.ui.notify('Mosaic goal exhausted its autonomous turn limit.', 'warning');
ctx.abort();
return;
}
persist(nextState, ctx);
});
pi.on('agent_start', async () => {
continuationQueued = false;
clearDeferredTimer();
});
pi.on('agent_settled', async (_event, ctx) => {
if (state === undefined || !isContinuingPhase(state.phase)) return;
queueContinuation(ctx, state.phase === 'verifying' ? 'the provisional claim' : 'agent settle');
});
pi.on('session_compact', async (event, ctx) => {
if (state === undefined) return;
const timestamp = nowIso();
const wasContinuing = isContinuingPhase(state.phase);
persist(
{
...state,
phase: wasContinuing ? 'active' : state.phase,
updatedAt: timestamp,
verificationPasses: wasContinuing ? 0 : state.verificationPasses,
compactionCount: state.compactionCount + 1,
lastCheckSource: 'compact',
lastCheckAt: timestamp,
lastCheckOutcome: `checked-${event.reason}`,
...(wasContinuing ? { stopReason: undefined } : {}),
},
ctx,
);
if (wasContinuing) scheduleIdleContinuation(ctx, `${event.reason} compaction`);
});
pi.on('session_start', async (_event, ctx) => {
lifecycleGeneration += 1;
clearDeferredTimer();
restoreState(ctx);
if (state !== undefined && isContinuingPhase(state.phase)) {
const timestamp = nowIso();
persist(
{
...state,
updatedAt: timestamp,
lastCheckSource: 'restore',
lastCheckAt: timestamp,
lastCheckOutcome: 'session-start',
},
ctx,
);
scheduleIdleContinuation(ctx, 'session restore');
}
});
pi.on('session_tree', async (_event, ctx) => {
lifecycleGeneration += 1;
clearDeferredTimer();
restoreState(ctx);
if (state !== undefined && isContinuingPhase(state.phase)) {
const timestamp = nowIso();
persist(
{
...state,
updatedAt: timestamp,
lastCheckSource: 'restore',
lastCheckAt: timestamp,
lastCheckOutcome: 'tree-navigation',
},
ctx,
);
scheduleIdleContinuation(ctx, 'tree navigation');
}
});
pi.on('session_shutdown', async (_event, ctx) => {
lifecycleGeneration += 1;
clearDeferredTimer();
continuationQueued = false;
ctx.ui.setStatus(STATUS_KEY, undefined);
});
}