@schilderlabs/pitown 0.2.1 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-CocC9qO1.mjs","names":["writeJson"],"sources":["../../core/src/lease.ts","../../core/src/metrics.ts","../../core/src/controller.ts","../../core/src/loop.ts"],"sourcesContent":["import { mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\"\nimport { homedir, hostname } from \"node:os\"\nimport { join } from \"node:path\"\n\ninterface LeaseData {\n\trunId: string\n\trepoId: string\n\tbranch: string\n\tpid: number\n\thostname: string\n\tstartedAt: string\n}\n\nfunction sanitize(value: string): string {\n\treturn value.replace(/[^a-zA-Z0-9._-]+/g, \"_\")\n}\n\nfunction processAlive(pid: number): boolean {\n\tif (!Number.isFinite(pid) || pid <= 0) return false\n\ttry {\n\t\tprocess.kill(pid, 0)\n\t\treturn true\n\t} catch {\n\t\treturn false\n\t}\n}\n\nexport function acquireRepoLease(runId: string, repoId: string, branch: string): { path: string; release: () => void } {\n\tconst locksDir = join(homedir(), \".pi-town\", \"locks\")\n\tmkdirSync(locksDir, { recursive: true })\n\n\tconst leasePath = join(locksDir, `pi-town-${sanitize(repoId)}-${sanitize(branch)}.json`)\n\tconst nextData: LeaseData = {\n\t\trunId,\n\t\trepoId,\n\t\tbranch,\n\t\tpid: process.pid,\n\t\thostname: hostname(),\n\t\tstartedAt: new Date().toISOString(),\n\t}\n\n\ttry {\n\t\tconst current = JSON.parse(readFileSync(leasePath, \"utf-8\")) as LeaseData\n\t\tif (processAlive(current.pid)) {\n\t\t\tthrow new Error(`Pi Town lease already held by pid ${current.pid} on ${current.hostname} for run ${current.runId}.`)\n\t\t}\n\t\trmSync(leasePath, { force: true })\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") {\n\t\t\tif (error instanceof Error && error.message.startsWith(\"Pi Town lease already held\")) throw error\n\t\t}\n\t}\n\n\twriteFileSync(leasePath, `${JSON.stringify(nextData, null, 2)}\\n`, \"utf-8\")\n\n\treturn {\n\t\tpath: leasePath,\n\t\trelease: () => {\n\t\t\ttry {\n\t\t\t\tconst current = JSON.parse(readFileSync(leasePath, \"utf-8\")) as LeaseData\n\t\t\t\tif (current.runId === runId) rmSync(leasePath, { force: true })\n\t\t\t} catch {\n\t\t\t\t// ignore cleanup failures\n\t\t\t}\n\t\t},\n\t}\n}\n","import type { FeedbackCycle, InterruptRecord, MetricsSnapshot, TaskAttempt } from \"./types.js\"\n\nfunction round(value: number): number {\n\treturn Math.round(value * 1000) / 1000\n}\n\nfunction diffHours(start: string, end: string): number {\n\treturn (Date.parse(end) - Date.parse(start)) / 3_600_000\n}\n\nfunction average(values: number[]): number | null {\n\tif (values.length === 0) return null\n\treturn values.reduce((sum, value) => sum + value, 0) / values.length\n}\n\nexport function computeInterruptRate(interrupts: InterruptRecord[], taskAttempts: TaskAttempt[]): number {\n\tif (taskAttempts.length === 0) return 0\n\treturn round(interrupts.length / taskAttempts.length)\n}\n\nexport function computeAutonomousCompletionRate(taskAttempts: TaskAttempt[]): number {\n\tconst completed = taskAttempts.filter((task) => task.status === \"completed\")\n\tif (completed.length === 0) return 0\n\tconst autonomous = completed.filter((task) => !task.interrupted)\n\treturn round(autonomous.length / completed.length)\n}\n\nexport function computeContextCoverageScore(interrupts: InterruptRecord[]): number {\n\tconst observed = new Set(interrupts.map((interrupt) => interrupt.category))\n\tif (observed.size === 0) return 0\n\n\tconst covered = new Set(\n\t\tinterrupts.filter((interrupt) => interrupt.fixType).map((interrupt) => interrupt.category),\n\t)\n\n\treturn round(covered.size / observed.size)\n}\n\nexport function computeMeanTimeToCorrect(interrupts: InterruptRecord[]): number | null {\n\tconst resolved = interrupts.filter((interrupt) => interrupt.resolvedAt)\n\tconst hours = resolved.map((interrupt) => diffHours(interrupt.createdAt, interrupt.resolvedAt!))\n\tconst value = average(hours)\n\treturn value === null ? null : round(value)\n}\n\nexport function computeFeedbackToDemoCycleTime(feedbackCycles: FeedbackCycle[]): number | null {\n\tconst hours = feedbackCycles.map((cycle) => diffHours(cycle.feedbackAt, cycle.demoReadyAt))\n\tconst value = average(hours)\n\treturn value === null ? null : round(value)\n}\n\nexport function computeMetrics(input: {\n\ttaskAttempts: TaskAttempt[]\n\tinterrupts: InterruptRecord[]\n\tfeedbackCycles?: FeedbackCycle[]\n}): MetricsSnapshot {\n\tconst observedCategories = new Set(input.interrupts.map((interrupt) => interrupt.category))\n\tconst coveredCategories = new Set(\n\t\tinput.interrupts.filter((interrupt) => interrupt.fixType).map((interrupt) => interrupt.category),\n\t)\n\tconst completedTasks = input.taskAttempts.filter((task) => task.status === \"completed\").length\n\n\treturn {\n\t\tinterruptRate: computeInterruptRate(input.interrupts, input.taskAttempts),\n\t\tautonomousCompletionRate: computeAutonomousCompletionRate(input.taskAttempts),\n\t\tcontextCoverageScore: computeContextCoverageScore(input.interrupts),\n\t\tmeanTimeToCorrectHours: computeMeanTimeToCorrect(input.interrupts),\n\t\tfeedbackToDemoCycleTimeHours: computeFeedbackToDemoCycleTime(input.feedbackCycles ?? []),\n\t\ttotals: {\n\t\t\ttaskAttempts: input.taskAttempts.length,\n\t\t\tcompletedTasks,\n\t\t\tinterrupts: input.interrupts.length,\n\t\t\tobservedInterruptCategories: observedCategories.size,\n\t\t\tcoveredInterruptCategories: coveredCategories.size,\n\t\t},\n\t}\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport {\n\tcreateAgentSessionRecord,\n\tcreateAgentState,\n\tgetAgentSessionsDir,\n\tgetLatestAgentSession,\n\treadAgentState,\n\twriteAgentState,\n} from \"./agents.js\"\nimport { appendJsonl } from \"./events.js\"\nimport { acquireRepoLease } from \"./lease.js\"\nimport { computeMetrics } from \"./metrics.js\"\nimport { createPiAuthHelpMessage, detectPiAuthFailure } from \"./pi.js\"\nimport { createRepoSlug, getCurrentBranch, getRepoIdentity, getRepoRoot } from \"./repo.js\"\nimport { assertCommandAvailable, runCommandSync } from \"./shell.js\"\nimport type { ControllerRunResult, PiInvocationRecord, RunManifest, RunOptions, RunSummary } from \"./types.js\"\n\nfunction createRunId(): string {\n\treturn `run-${new Date().toISOString().replace(/[:.]/g, \"-\")}`\n}\n\nfunction createPiInvocationArgs(input: {\n\tsessionDir?: string | null\n\tsessionPath?: string | null\n\tprompt: string\n\tappendedSystemPrompt?: string | null | undefined\n\textensionPath?: string | null | undefined\n}) {\n\tconst args: string[] = []\n\n\tif (input.extensionPath) args.push(\"--extension\", input.extensionPath)\n\tif (input.appendedSystemPrompt) args.push(\"--append-system-prompt\", input.appendedSystemPrompt)\n\tif (input.sessionPath) args.push(\"--session\", input.sessionPath)\n\telse if (input.sessionDir) args.push(\"--session-dir\", input.sessionDir)\n\telse throw new Error(\"Pi invocation requires a session path or session directory\")\n\targs.push(\"-p\", input.prompt)\n\n\treturn args\n}\n\nfunction writeJson(path: string, value: unknown) {\n\twriteFileSync(path, `${JSON.stringify(value, null, 2)}\\n`, \"utf-8\")\n}\n\nfunction writeText(path: string, value: string) {\n\twriteFileSync(path, value, \"utf-8\")\n}\n\nfunction createPiPrompt(input: {\n\trepoRoot: string\n\tplanPath: string | null\n\tgoal: string | null\n\trecommendedPlanDir: string | null\n}): string {\n\tconst goal = input.goal ?? \"continue from current scaffold state\"\n\n\tif (input.planPath) {\n\t\treturn [\n\t\t\t\"You are the Pi Town mayor agent for this repository.\",\n\t\t\t\"Coordinate the next bounded unit of work, keep updates concise, and leave a durable artifact trail.\",\n\t\t\t\"\",\n\t\t\t\"Read the private plans in:\",\n\t\t\t`- ${input.planPath}`,\n\t\t\t\"\",\n\t\t\t\"and the current code in:\",\n\t\t\t`- ${input.repoRoot}`,\n\t\t\t\"\",\n\t\t\t`Goal: ${goal}`,\n\t\t\t\"Continue from the current scaffold state.\",\n\t\t\t\"Keep any persisted run artifacts high-signal and avoid copying private plan contents into them.\",\n\t\t].join(\"\\n\")\n\t}\n\n\treturn [\n\t\t\"You are the Pi Town mayor agent for this repository.\",\n\t\t\"Coordinate the next bounded unit of work, keep updates concise, and leave a durable artifact trail.\",\n\t\t\"\",\n\t\t`Work in the repository at: ${input.repoRoot}`,\n\t\t`Goal: ${goal}`,\n\t\t\"No private plan path is configured for this run.\",\n\t\tinput.recommendedPlanDir\n\t\t\t? `If you need private plans, use a user-owned location such as: ${input.recommendedPlanDir}`\n\t\t\t: \"If you need private plans, keep them in a user-owned location outside the repo.\",\n\t\t\"Continue from the current scaffold state.\",\n\t].join(\"\\n\")\n}\n\nfunction assertPiRuntimeAvailable(piCommand: string) {\n\ttry {\n\t\tassertCommandAvailable(piCommand)\n\t} catch (error) {\n\t\tif (piCommand === \"pi\") {\n\t\t\tthrow new Error(\n\t\t\t\t[\n\t\t\t\t\t\"Pi Town requires the `pi` CLI to run `pitown run`.\",\n\t\t\t\t\t\"Install Pi: npm install -g @mariozechner/pi-coding-agent\",\n\t\t\t\t\t\"Then authenticate Pi and verify it works: pi -p \\\"hello\\\"\",\n\t\t\t\t\t`Details: ${(error as Error).message}`,\n\t\t\t\t].join(\"\\n\"),\n\t\t\t)\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t[\n\t\t\t\t`Pi Town could not execute the configured Pi command: ${piCommand}`,\n\t\t\t\t\"Make sure the command exists on PATH or points to an executable file.\",\n\t\t\t\t`Details: ${(error as Error).message}`,\n\t\t\t].join(\"\\n\"),\n\t\t)\n\t}\n}\n\nfunction createManifest(input: {\n\trunId: string\n\trepoId: string\n\trepoSlug: string\n\trepoRoot: string\n\tbranch: string\n\tgoal: string | null\n\tplanPath: string | null\n\trecommendedPlanDir: string | null\n\tmode: \"single-pi\"\n\tleasePath: string\n}): RunManifest {\n\treturn {\n\t\trunId: input.runId,\n\t\trepoId: input.repoId,\n\t\trepoSlug: input.repoSlug,\n\t\trepoRoot: input.repoRoot,\n\t\tbranch: input.branch,\n\t\tgoal: input.goal,\n\t\tplanPath: input.planPath,\n\t\trecommendedPlanDir: input.recommendedPlanDir,\n\t\tmode: input.mode,\n\t\tstartedAt: new Date().toISOString(),\n\t\tendedAt: null,\n\t\tstopReason: null,\n\t\tleasePath: input.leasePath,\n\t\tpiExitCode: null,\n\t\tcompletedTaskCount: 0,\n\t\tblockedTaskCount: 0,\n\t\tskippedTaskCount: 0,\n\t\ttotalCostUsd: 0,\n\t}\n}\n\nfunction createSummary(input: {\n\trunId: string\n\tmode: \"single-pi\"\n\texitCode: number\n\tstdout: string\n\tstderr: string\n\trecommendedPlanDir: string | null\n}): RunSummary {\n\tconst success = input.exitCode === 0\n\tconst recommendation =\n\t\tinput.recommendedPlanDir === null\n\t\t\t? \"\"\n\t\t\t: ` No plan path was configured. Recommended private plans location: ${input.recommendedPlanDir}.`\n\tconst authHelp =\n\t\tsuccess || !detectPiAuthFailure(input.stderr, input.stdout) ? \"\" : ` ${createPiAuthHelpMessage()}`\n\n\treturn {\n\t\trunId: input.runId,\n\t\tmode: input.mode,\n\t\tcreatedAt: new Date().toISOString(),\n\t\tsuccess,\n\t\tmessage: success\n\t\t\t? `Pi invocation completed.${recommendation}`\n\t\t\t: `Pi invocation failed.${authHelp}${recommendation}`,\n\t\tpiExitCode: input.exitCode,\n\t\trecommendedPlanDir: input.recommendedPlanDir,\n\t}\n}\n\nexport function runController(options: RunOptions): ControllerRunResult {\n\tconst cwd = options.cwd ?? process.cwd()\n\tconst artifactsDir = options.artifactsDir\n\tconst repoRoot = getRepoRoot(cwd)\n\tconst repoId = getRepoIdentity(repoRoot)\n\tconst repoSlug = createRepoSlug(repoId, repoRoot)\n\tconst branch = options.branch ?? getCurrentBranch(repoRoot) ?? \"workspace\"\n\tconst goal = options.goal ?? null\n\tconst planPath = options.planPath ?? null\n\tconst recommendedPlanDir = planPath ? null : (options.recommendedPlanDir ?? null)\n\tconst mode = options.mode ?? \"single-pi\"\n\tconst piCommand = options.piCommand ?? \"pi\"\n\tconst runId = createRunId()\n\tconst runDir = join(artifactsDir, \"runs\", runId)\n\tconst latestDir = join(artifactsDir, \"latest\")\n\tconst stdoutPath = join(runDir, \"stdout.txt\")\n\tconst stderrPath = join(runDir, \"stderr.txt\")\n\tconst prompt = createPiPrompt({ repoRoot, planPath, goal, recommendedPlanDir })\n\tconst existingMayorState = readAgentState(artifactsDir, \"mayor\")\n\tconst existingMayorSession =\n\t\texistingMayorState?.session.sessionPath || existingMayorState?.session.sessionDir\n\t\t\t? existingMayorState.session\n\t\t\t: getLatestAgentSession(artifactsDir, \"mayor\")\n\tconst mayorSessionDir = existingMayorSession.sessionDir ?? getAgentSessionsDir(artifactsDir, \"mayor\")\n\tconst mayorState = createAgentState({\n\t\tagentId: \"mayor\",\n\t\trole: \"mayor\",\n\t\tstatus: \"starting\",\n\t\ttask: goal,\n\t\tbranch,\n\t\tlastMessage: goal ? `Starting mayor run for goal: ${goal}` : \"Starting mayor run\",\n\t\trunId,\n\t\tsession: createAgentSessionRecord({\n\t\t\tsessionDir: mayorSessionDir,\n\t\t\tsessionId: existingMayorSession.sessionId,\n\t\t\tsessionPath: existingMayorSession.sessionPath,\n\t\t}),\n\t})\n\n\tassertPiRuntimeAvailable(piCommand)\n\n\tmkdirSync(runDir, { recursive: true })\n\tmkdirSync(latestDir, { recursive: true })\n\n\twriteText(join(runDir, \"questions.jsonl\"), \"\")\n\twriteText(join(runDir, \"interventions.jsonl\"), \"\")\n\twriteJson(join(runDir, \"agent-state.json\"), {\n\t\tstatus: \"starting\",\n\t\tupdatedAt: new Date().toISOString(),\n\t})\n\twriteAgentState(artifactsDir, mayorState)\n\n\tconst lease = acquireRepoLease(runId, repoId, branch)\n\n\ttry {\n\t\tconst manifest = createManifest({\n\t\t\trunId,\n\t\t\trepoId,\n\t\t\trepoSlug,\n\t\t\trepoRoot,\n\t\t\tbranch,\n\t\t\tgoal,\n\t\t\tplanPath,\n\t\t\trecommendedPlanDir,\n\t\t\tmode,\n\t\t\tleasePath: lease.path,\n\t\t})\n\n\t\tappendJsonl(join(runDir, \"events.jsonl\"), {\n\t\t\ttype: \"run_started\",\n\t\t\trunId,\n\t\t\trepoId,\n\t\t\trepoSlug,\n\t\t\tbranch,\n\t\t\tcreatedAt: manifest.startedAt,\n\t\t})\n\n\t\tconst piStartedAt = new Date().toISOString()\n\t\tappendJsonl(join(runDir, \"events.jsonl\"), {\n\t\t\ttype: \"pi_invocation_started\",\n\t\t\trunId,\n\t\t\tcommand: piCommand,\n\t\t\tcreatedAt: piStartedAt,\n\t\t})\n\n\t\twriteAgentState(\n\t\t\tartifactsDir,\n\t\t\tcreateAgentState({\n\t\t\t\t...mayorState,\n\t\t\t\tstatus: \"running\",\n\t\t\t\tlastMessage: goal ? `Mayor working on: ${goal}` : \"Mayor working\",\n\t\t\t}),\n\t\t)\n\n\t\tconst piArgs = createPiInvocationArgs({\n\t\t\tsessionDir: mayorState.session.sessionPath === null ? mayorSessionDir : null,\n\t\t\tsessionPath: mayorState.session.sessionPath,\n\t\t\tprompt,\n\t\t\tappendedSystemPrompt: options.appendedSystemPrompt,\n\t\t\textensionPath: options.extensionPath,\n\t\t})\n\t\tconst piResult = runCommandSync(piCommand, piArgs, {\n\t\t\tcwd: repoRoot,\n\t\t\tenv: process.env,\n\t\t})\n\t\tconst piEndedAt = new Date().toISOString()\n\t\tconst latestMayorSession = getLatestAgentSession(artifactsDir, \"mayor\")\n\n\t\twriteText(stdoutPath, piResult.stdout)\n\t\twriteText(stderrPath, piResult.stderr)\n\n\t\tconst piInvocation: PiInvocationRecord = {\n\t\t\tcommand: piCommand,\n\t\t\tcwd: repoRoot,\n\t\t\trepoRoot,\n\t\t\tplanPath,\n\t\t\tgoal,\n\t\t\tsessionDir: latestMayorSession.sessionDir,\n\t\t\tsessionId: latestMayorSession.sessionId,\n\t\t\tsessionPath: latestMayorSession.sessionPath,\n\t\t\tstartedAt: piStartedAt,\n\t\t\tendedAt: piEndedAt,\n\t\t\texitCode: piResult.exitCode,\n\t\t\tstdoutPath,\n\t\t\tstderrPath,\n\t\t\tpromptSummary: planPath\n\t\t\t\t? \"Read private plan path and continue from current scaffold state.\"\n\t\t\t\t: \"Continue from current scaffold state without a configured private plan path.\",\n\t\t}\n\t\twriteJson(join(runDir, \"pi-invocation.json\"), piInvocation)\n\n\t\tappendJsonl(join(runDir, \"events.jsonl\"), {\n\t\t\ttype: \"pi_invocation_finished\",\n\t\t\trunId,\n\t\t\tcommand: piCommand,\n\t\t\texitCode: piInvocation.exitCode,\n\t\t\tcreatedAt: piEndedAt,\n\t\t})\n\n\t\tconst metrics = computeMetrics({\n\t\t\ttaskAttempts: [],\n\t\t\tinterrupts: [],\n\t\t})\n\t\tconst summary = createSummary({\n\t\t\trunId,\n\t\t\tmode,\n\t\t\texitCode: piInvocation.exitCode,\n\t\t\tstdout: piResult.stdout,\n\t\t\tstderr: piResult.stderr,\n\t\t\trecommendedPlanDir,\n\t\t})\n\t\tconst finalManifest: RunManifest = {\n\t\t\t...manifest,\n\t\t\tendedAt: piEndedAt,\n\t\t\tstopReason:\n\t\t\t\tpiInvocation.exitCode === 0\n\t\t\t\t\t? \"pi invocation completed\"\n\t\t\t\t\t: `pi invocation exited with code ${piInvocation.exitCode}`,\n\t\t\tpiExitCode: piInvocation.exitCode,\n\t\t}\n\n\t\twriteJson(join(runDir, \"manifest.json\"), finalManifest)\n\t\twriteJson(join(runDir, \"metrics.json\"), metrics)\n\t\twriteJson(join(runDir, \"run-summary.json\"), summary)\n\t\twriteJson(join(runDir, \"agent-state.json\"), {\n\t\t\tstatus: summary.success ? \"completed\" : \"failed\",\n\t\t\tupdatedAt: piEndedAt,\n\t\t\texitCode: piInvocation.exitCode,\n\t\t})\n\t\twriteJson(join(latestDir, \"manifest.json\"), finalManifest)\n\t\twriteJson(join(latestDir, \"metrics.json\"), metrics)\n\t\twriteJson(join(latestDir, \"run-summary.json\"), summary)\n\t\twriteAgentState(\n\t\t\tartifactsDir,\n\t\t\tcreateAgentState({\n\t\t\t\t...mayorState,\n\t\t\t\t\tstatus: piInvocation.exitCode === 0 ? \"idle\" : \"blocked\",\n\t\t\t\t\tlastMessage:\n\t\t\t\t\t\tpiInvocation.exitCode === 0\n\t\t\t\t\t\t\t? \"Mayor run completed and is ready for the next instruction\"\n\t\t\t\t\t\t\t: `Mayor run stopped with exit code ${piInvocation.exitCode}`,\n\t\t\t\t\tblocked: piInvocation.exitCode !== 0,\n\t\t\t\t\twaitingOn: piInvocation.exitCode === 0 ? null : \"human-or-follow-up-run\",\n\t\t\t\t\tsession: createAgentSessionRecord({\n\t\t\t\t\t\tsessionDir: latestMayorSession.sessionDir,\n\t\t\t\t\t\tsessionId: latestMayorSession.sessionId,\n\t\t\t\t\t\tsessionPath: latestMayorSession.sessionPath,\n\t\t\t\t\t}),\n\t\t\t\t}),\n\t\t\t)\n\n\t\tappendJsonl(join(runDir, \"events.jsonl\"), {\n\t\t\ttype: \"run_finished\",\n\t\t\trunId,\n\t\t\tcreatedAt: finalManifest.endedAt,\n\t\t\tstopReason: finalManifest.stopReason,\n\t\t\tmetrics,\n\t\t})\n\n\t\treturn {\n\t\t\trunId,\n\t\t\trunDir,\n\t\t\tlatestDir,\n\t\t\tmanifest: finalManifest,\n\t\t\tmetrics,\n\t\t\tsummary,\n\t\t\tpiInvocation,\n\t\t}\n\t} finally {\n\t\tlease.release()\n\t}\n}\n","import { mkdirSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { listAgentStates } from \"./agents.js\"\nimport { runController } from \"./controller.js\"\nimport { appendJsonl } from \"./events.js\"\nimport { computeMetrics } from \"./metrics.js\"\nimport { listTaskRecords } from \"./tasks.js\"\nimport type {\n\tBoardSnapshot,\n\tControllerRunResult,\n\tLoopIterationResult,\n\tLoopOptions,\n\tLoopRunResult,\n\tLoopStopReason,\n\tMetricsSnapshot,\n} from \"./types.js\"\n\nconst DEFAULT_MAX_ITERATIONS = 10\nconst DEFAULT_MAX_WALL_TIME_MS = 3_600_000\nconst DEFAULT_BACKGROUND_POLL_MS = 250\n\nfunction createLoopId(): string {\n\treturn `loop-${new Date().toISOString().replace(/[:.]/g, \"-\")}`\n}\n\nfunction writeJson(path: string, value: unknown) {\n\twriteFileSync(path, `${JSON.stringify(value, null, 2)}\\n`, \"utf-8\")\n}\n\nfunction sleepMs(ms: number) {\n\tAtomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms)\n}\n\nfunction hasBackgroundWork(board: BoardSnapshot): boolean {\n\treturn (\n\t\tboard.agents.some(\n\t\t\t(agent) =>\n\t\t\t\tagent.agentId !== \"mayor\" &&\n\t\t\t\t(agent.status === \"queued\" || agent.status === \"running\" || agent.status === \"starting\"),\n\t\t) || board.tasks.some((task) => task.status === \"queued\" || task.status === \"running\")\n\t)\n}\n\nfunction waitForBackgroundWorkToSettle(input: {\n\tartifactsDir: string\n\tmaxWallTimeMs: number\n\tloopStartedAt: number\n\tpollIntervalMs?: number\n}): { timedOut: boolean; board: BoardSnapshot } {\n\tconst pollIntervalMs = input.pollIntervalMs ?? DEFAULT_BACKGROUND_POLL_MS\n\tlet board = snapshotBoard(input.artifactsDir)\n\n\twhile (hasBackgroundWork(board)) {\n\t\tif (Date.now() - input.loopStartedAt >= input.maxWallTimeMs) {\n\t\t\treturn { timedOut: true, board }\n\t\t}\n\n\t\tsleepMs(pollIntervalMs)\n\t\tboard = snapshotBoard(input.artifactsDir)\n\t}\n\n\treturn { timedOut: false, board }\n}\n\nexport function snapshotBoard(artifactsDir: string): BoardSnapshot {\n\tconst tasks = listTaskRecords(artifactsDir)\n\tconst agents = listAgentStates(artifactsDir)\n\n\tconst taskEntries = tasks.map((task) => ({ taskId: task.taskId, status: task.status }))\n\tconst agentEntries = agents.map((agent) => ({\n\t\tagentId: agent.agentId,\n\t\tstatus: agent.status,\n\t\tblocked: agent.blocked,\n\t}))\n\n\tconst allTasksCompleted = tasks.length > 0 && tasks.every((task) => task.status === \"completed\")\n\tconst allRemainingTasksBlocked =\n\t\ttasks.length > 0 && tasks.every((task) => task.status === \"completed\" || task.status === \"blocked\" || task.status === \"aborted\")\n\n\tconst mayor = agents.find((agent) => agent.agentId === \"mayor\")\n\tconst mayorBlocked = mayor?.blocked === true\n\n\tconst hasQueuedOrRunningWork =\n\t\tagents.some((agent) => agent.status === \"queued\" || agent.status === \"running\" || agent.status === \"starting\") ||\n\t\ttasks.some((task) => task.status === \"queued\" || task.status === \"running\")\n\n\treturn {\n\t\ttasks: taskEntries,\n\t\tagents: agentEntries,\n\t\tallTasksCompleted,\n\t\tallRemainingTasksBlocked,\n\t\tmayorBlocked,\n\t\thasQueuedOrRunningWork,\n\t}\n}\n\nexport function evaluateStopCondition(input: {\n\titeration: number\n\tmaxIterations: number\n\telapsedMs: number\n\tmaxWallTimeMs: number\n\tpiExitCode: number\n\tstopOnPiFailure: boolean\n\tstopOnMayorIdleNoWork: boolean\n\tboard: BoardSnapshot\n\tmetrics: MetricsSnapshot\n\tinterruptRateThreshold: number | null\n}): { stopReason: LoopStopReason | null; continueReason: string | null } {\n\tif (input.iteration >= input.maxIterations) {\n\t\treturn { stopReason: \"max-iterations-reached\", continueReason: null }\n\t}\n\n\tif (input.elapsedMs >= input.maxWallTimeMs) {\n\t\treturn { stopReason: \"max-wall-time-reached\", continueReason: null }\n\t}\n\n\tif (input.stopOnPiFailure && input.piExitCode !== 0) {\n\t\treturn { stopReason: \"pi-exit-nonzero\", continueReason: null }\n\t}\n\n\tif (input.board.allTasksCompleted) {\n\t\treturn { stopReason: \"all-tasks-completed\", continueReason: null }\n\t}\n\n\tif (input.board.mayorBlocked) {\n\t\treturn { stopReason: \"mayor-blocked\", continueReason: null }\n\t}\n\n\tconst mayor = input.board.agents.find((agent) => agent.agentId === \"mayor\")\n\tif (input.stopOnMayorIdleNoWork && mayor && !input.board.hasQueuedOrRunningWork && input.board.tasks.length === 0 && mayor.status === \"idle\") {\n\t\treturn { stopReason: \"mayor-idle-no-work\", continueReason: null }\n\t}\n\n\tif (input.board.allRemainingTasksBlocked) {\n\t\treturn { stopReason: \"all-remaining-tasks-blocked\", continueReason: null }\n\t}\n\n\tif (\n\t\tinput.interruptRateThreshold !== null &&\n\t\tinput.metrics.interruptRate > input.interruptRateThreshold\n\t) {\n\t\treturn { stopReason: \"high-interrupt-rate\", continueReason: null }\n\t}\n\n\tconst reasons: string[] = []\n\tif (input.board.hasQueuedOrRunningWork) reasons.push(\"queued or running work remains\")\n\tif (input.board.tasks.length === 0) reasons.push(\"no tasks tracked yet\")\n\tif (reasons.length === 0) reasons.push(\"mayor idle, no stop condition met\")\n\n\treturn { stopReason: null, continueReason: reasons.join(\"; \") }\n}\n\nfunction aggregateMetrics(iterations: LoopIterationResult[]): MetricsSnapshot {\n\tif (iterations.length === 0) {\n\t\treturn computeMetrics({ taskAttempts: [], interrupts: [] })\n\t}\n\n\tlet totalTaskAttempts = 0\n\tlet totalCompletedTasks = 0\n\tlet totalInterrupts = 0\n\tlet totalObservedInterruptCategories = 0\n\tlet totalCoveredInterruptCategories = 0\n\tlet interruptRateSum = 0\n\tlet autonomousCompletionRateSum = 0\n\tlet contextCoverageScoreSum = 0\n\tlet mttcValues: number[] = []\n\tlet ftdValues: number[] = []\n\n\tfor (const iter of iterations) {\n\t\tconst m = iter.metrics\n\t\ttotalTaskAttempts += m.totals.taskAttempts\n\t\ttotalCompletedTasks += m.totals.completedTasks\n\t\ttotalInterrupts += m.totals.interrupts\n\t\ttotalObservedInterruptCategories += m.totals.observedInterruptCategories\n\t\ttotalCoveredInterruptCategories += m.totals.coveredInterruptCategories\n\t\tinterruptRateSum += m.interruptRate\n\t\tautonomousCompletionRateSum += m.autonomousCompletionRate\n\t\tcontextCoverageScoreSum += m.contextCoverageScore\n\t\tif (m.meanTimeToCorrectHours !== null) mttcValues.push(m.meanTimeToCorrectHours)\n\t\tif (m.feedbackToDemoCycleTimeHours !== null) ftdValues.push(m.feedbackToDemoCycleTimeHours)\n\t}\n\n\tconst count = iterations.length\n\tconst round = (v: number) => Math.round(v * 1000) / 1000\n\tconst avg = (values: number[]) => (values.length === 0 ? null : round(values.reduce((s, v) => s + v, 0) / values.length))\n\n\treturn {\n\t\tinterruptRate: round(interruptRateSum / count),\n\t\tautonomousCompletionRate: round(autonomousCompletionRateSum / count),\n\t\tcontextCoverageScore: round(contextCoverageScoreSum / count),\n\t\tmeanTimeToCorrectHours: avg(mttcValues),\n\t\tfeedbackToDemoCycleTimeHours: avg(ftdValues),\n\t\ttotals: {\n\t\t\ttaskAttempts: totalTaskAttempts,\n\t\t\tcompletedTasks: totalCompletedTasks,\n\t\t\tinterrupts: totalInterrupts,\n\t\t\tobservedInterruptCategories: totalObservedInterruptCategories,\n\t\t\tcoveredInterruptCategories: totalCoveredInterruptCategories,\n\t\t},\n\t}\n}\n\nexport function runLoop(options: LoopOptions): LoopRunResult {\n\tconst maxIterations = options.maxIterations ?? DEFAULT_MAX_ITERATIONS\n\tconst maxWallTimeMs = options.maxWallTimeMs ?? DEFAULT_MAX_WALL_TIME_MS\n\tconst stopOnPiFailure = options.stopOnPiFailure ?? true\n\tconst stopOnMayorIdleNoWork = options.stopOnMayorIdleNoWork ?? false\n\tconst interruptRateThreshold = options.interruptRateThreshold ?? null\n\tconst loopId = createLoopId()\n\tconst artifactsDir = options.runOptions.artifactsDir\n\tconst loopDir = join(artifactsDir, \"loops\", loopId)\n\n\tmkdirSync(loopDir, { recursive: true })\n\n\tconst loopStartedAt = Date.now()\n\tconst iterations: LoopIterationResult[] = []\n\tlet finalStopReason: LoopStopReason = \"max-iterations-reached\"\n\tlet needsMayorFollowUp = false\n\n\tappendJsonl(join(loopDir, \"events.jsonl\"), {\n\t\ttype: \"loop_started\",\n\t\tloopId,\n\t\tmaxIterations,\n\t\tmaxWallTimeMs,\n\t\tstopOnPiFailure,\n\t\tcreatedAt: new Date().toISOString(),\n\t})\n\n\tfor (let iteration = 1; iteration <= maxIterations; iteration++) {\n\t\tif (needsMayorFollowUp) {\n\t\t\tconst settled = waitForBackgroundWorkToSettle({\n\t\t\t\tartifactsDir,\n\t\t\t\tmaxWallTimeMs,\n\t\t\t\tloopStartedAt,\n\t\t\t})\n\n\t\t\tappendJsonl(join(loopDir, \"events.jsonl\"), {\n\t\t\t\ttype: \"loop_background_work_settled\",\n\t\t\t\tloopId,\n\t\t\t\titeration,\n\t\t\t\ttimedOut: settled.timedOut,\n\t\t\t\tboardSnapshot: settled.board,\n\t\t\t\tcreatedAt: new Date().toISOString(),\n\t\t\t})\n\n\t\t\tif (settled.timedOut) {\n\t\t\t\tfinalStopReason = \"max-wall-time-reached\"\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tneedsMayorFollowUp = false\n\t\t}\n\n\t\tconst iterationStart = Date.now()\n\n\t\tlet controllerResult: ControllerRunResult\n\t\ttry {\n\t\t\tcontrollerResult = runController(options.runOptions)\n\t\t} catch (error) {\n\t\t\t// If controller fails to run (e.g. lease issue), record and stop\n\t\t\tappendJsonl(join(loopDir, \"events.jsonl\"), {\n\t\t\t\ttype: \"loop_iteration_error\",\n\t\t\t\tloopId,\n\t\t\t\titeration,\n\t\t\t\terror: (error as Error).message,\n\t\t\t\tcreatedAt: new Date().toISOString(),\n\t\t\t})\n\t\t\tfinalStopReason = \"pi-exit-nonzero\"\n\t\t\tbreak\n\t\t}\n\n\t\tconst iterationElapsedMs = Date.now() - iterationStart\n\t\tconst totalElapsedMs = Date.now() - loopStartedAt\n\t\tconst board = snapshotBoard(artifactsDir)\n\t\tconst metrics = controllerResult.metrics\n\n\t\tconst { stopReason, continueReason } = evaluateStopCondition({\n\t\t\titeration,\n\t\t\tmaxIterations,\n\t\t\telapsedMs: totalElapsedMs,\n\t\t\tmaxWallTimeMs,\n\t\t\tpiExitCode: controllerResult.piInvocation.exitCode,\n\t\t\tstopOnPiFailure,\n\t\t\tstopOnMayorIdleNoWork,\n\t\t\tboard,\n\t\t\tmetrics,\n\t\t\tinterruptRateThreshold,\n\t\t})\n\n\t\tconst iterationResult: LoopIterationResult = {\n\t\t\titeration,\n\t\t\tcontrollerResult,\n\t\t\tboardSnapshot: board,\n\t\t\tmetrics,\n\t\t\telapsedMs: iterationElapsedMs,\n\t\t\tcontinueReason,\n\t\t\tstopReason,\n\t\t}\n\n\t\titerations.push(iterationResult)\n\n\t\twriteJson(join(loopDir, `iteration-${iteration}.json`), {\n\t\t\titeration,\n\t\t\trunId: controllerResult.runId,\n\t\t\tboardSnapshot: board,\n\t\t\tmetrics,\n\t\t\telapsedMs: iterationElapsedMs,\n\t\t\tcontinueReason,\n\t\t\tstopReason,\n\t\t})\n\n\t\tappendJsonl(join(loopDir, \"events.jsonl\"), {\n\t\t\ttype: \"loop_iteration_completed\",\n\t\t\tloopId,\n\t\t\titeration,\n\t\t\trunId: controllerResult.runId,\n\t\t\tpiExitCode: controllerResult.piInvocation.exitCode,\n\t\t\tstopReason,\n\t\t\tcontinueReason,\n\t\t\tcreatedAt: new Date().toISOString(),\n\t\t})\n\n\t\tif (options.onIterationComplete) {\n\t\t\toptions.onIterationComplete(iterationResult)\n\t\t}\n\n\t\tif (stopReason !== null) {\n\t\t\tfinalStopReason = stopReason\n\t\t\tbreak\n\t\t}\n\n\t\tneedsMayorFollowUp = hasBackgroundWork(board)\n\t}\n\n\tconst totalElapsedMs = Date.now() - loopStartedAt\n\tconst lastIteration = iterations.at(-1)\n\tconst finalBoard = lastIteration ? lastIteration.boardSnapshot : snapshotBoard(artifactsDir)\n\tconst aggregate = aggregateMetrics(iterations)\n\n\tconst loopResult: LoopRunResult = {\n\t\tloopId,\n\t\titerations,\n\t\tstopReason: finalStopReason,\n\t\ttotalIterations: iterations.length,\n\t\ttotalElapsedMs,\n\t\tfinalBoardSnapshot: finalBoard,\n\t\taggregateMetrics: aggregate,\n\t}\n\n\twriteJson(join(loopDir, \"loop-summary.json\"), {\n\t\tloopId,\n\t\tstopReason: finalStopReason,\n\t\ttotalIterations: iterations.length,\n\t\ttotalElapsedMs,\n\t\tfinalBoardSnapshot: finalBoard,\n\t\taggregateMetrics: aggregate,\n\t})\n\n\tappendJsonl(join(loopDir, \"events.jsonl\"), {\n\t\ttype: \"loop_finished\",\n\t\tloopId,\n\t\tstopReason: finalStopReason,\n\t\ttotalIterations: iterations.length,\n\t\ttotalElapsedMs,\n\t\tcreatedAt: new Date().toISOString(),\n\t})\n\n\treturn loopResult\n}\n"],"mappings":"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"}
@@ -9,4 +9,4 @@ function createPiAuthHelpMessage() {
9
9
 
10
10
  //#endregion
11
11
  export { detectPiAuthFailure as n, createPiAuthHelpMessage as t };
12
- //# sourceMappingURL=pi-C0fURZj7.mjs.map
12
+ //# sourceMappingURL=pi-C7HRNjBG.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pi-C0fURZj7.mjs","names":[],"sources":["../../core/src/pi.ts"],"sourcesContent":["export function detectPiAuthFailure(stderr: string, stdout: string): boolean {\n\tconst output = `${stdout}\\n${stderr}`.toLowerCase()\n\treturn (\n\t\toutput.includes(\"no models available\") ||\n\t\toutput.includes(\"not authenticated\") ||\n\t\toutput.includes(\"authentication\") ||\n\t\toutput.includes(\"/login\") ||\n\t\toutput.includes(\"api key\")\n\t)\n}\n\nexport function createPiAuthHelpMessage(): string {\n\treturn 'Pi appears to be installed but not authenticated or configured. Verify Pi works first: pi -p \"hello\". Either set an API key or run `pi` and use `/login`.'\n}\n"],"mappings":";AAAA,SAAgB,oBAAoB,QAAgB,QAAyB;CAC5E,MAAM,SAAS,GAAG,OAAO,IAAI,SAAS,aAAa;AACnD,QACC,OAAO,SAAS,sBAAsB,IACtC,OAAO,SAAS,oBAAoB,IACpC,OAAO,SAAS,iBAAiB,IACjC,OAAO,SAAS,SAAS,IACzB,OAAO,SAAS,UAAU;;AAI5B,SAAgB,0BAAkC;AACjD,QAAO"}
1
+ {"version":3,"file":"pi-C7HRNjBG.mjs","names":[],"sources":["../../core/src/pi.ts"],"sourcesContent":["export function detectPiAuthFailure(stderr: string, stdout: string): boolean {\n\tconst output = `${stdout}\\n${stderr}`.toLowerCase()\n\treturn (\n\t\toutput.includes(\"no models available\") ||\n\t\toutput.includes(\"not authenticated\") ||\n\t\toutput.includes(\"authentication\") ||\n\t\toutput.includes(\"/login\") ||\n\t\toutput.includes(\"api key\")\n\t)\n}\n\nexport function createPiAuthHelpMessage(): string {\n\treturn 'Pi appears to be installed but not authenticated or configured. Verify Pi works first: pi -p \"hello\". Either set an API key or run `pi` and use `/login`.'\n}\n"],"mappings":";AAAA,SAAgB,oBAAoB,QAAgB,QAAyB;CAC5E,MAAM,SAAS,GAAG,OAAO,IAAI,SAAS,aAAa;AACnD,QACC,OAAO,SAAS,sBAAsB,IACtC,OAAO,SAAS,oBAAoB,IACpC,OAAO,SAAS,iBAAiB,IACjC,OAAO,SAAS,SAAS,IACzB,OAAO,SAAS,UAAU;;AAI5B,SAAgB,0BAAkC;AACjD,QAAO"}
@@ -0,0 +1,45 @@
1
+ import { d as createRepoSlug, h as isGitRepo, i as getLatestRunPointerPath, m as getRepoRoot, n as parseOptionalRepoFlag, o as getRepoArtifactsDir, p as getRepoIdentity } from "./config-BG1v4iIi.mjs";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+
4
+ //#region src/repo-context.ts
5
+ function resolveRepoContext(argv) {
6
+ const { repo, rest } = parseOptionalRepoFlag(argv);
7
+ if (repo) {
8
+ const repoRoot = getRepoRoot(repo);
9
+ const repoSlug = createRepoSlug(getRepoIdentity(repoRoot), repoRoot);
10
+ return {
11
+ repoRoot,
12
+ repoSlug,
13
+ artifactsDir: getRepoArtifactsDir(repoSlug),
14
+ rest
15
+ };
16
+ }
17
+ const cwd = process.cwd();
18
+ const repoRoot = getRepoRoot(cwd);
19
+ const repoSlug = createRepoSlug(getRepoIdentity(repoRoot), repoRoot);
20
+ const artifactsDir = getRepoArtifactsDir(repoSlug);
21
+ if (isGitRepo(cwd) || existsSync(artifactsDir)) return {
22
+ repoRoot,
23
+ repoSlug,
24
+ artifactsDir,
25
+ rest
26
+ };
27
+ const latestPointerPath = getLatestRunPointerPath();
28
+ if (!existsSync(latestPointerPath)) return {
29
+ repoRoot,
30
+ repoSlug,
31
+ artifactsDir,
32
+ rest
33
+ };
34
+ const latest = JSON.parse(readFileSync(latestPointerPath, "utf-8"));
35
+ return {
36
+ repoRoot: latest.repoRoot,
37
+ repoSlug: latest.repoSlug,
38
+ artifactsDir: getRepoArtifactsDir(latest.repoSlug),
39
+ rest
40
+ };
41
+ }
42
+
43
+ //#endregion
44
+ export { resolveRepoContext as t };
45
+ //# sourceMappingURL=repo-context-BuA2JqPm.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-context-BuA2JqPm.mjs","names":[],"sources":["../src/repo-context.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\"\nimport { createRepoSlug, getRepoIdentity, getRepoRoot, isGitRepo } from \"../../core/src/index.js\"\nimport { parseOptionalRepoFlag } from \"./config.js\"\nimport { getLatestRunPointerPath, getRepoArtifactsDir } from \"./paths.js\"\n\ninterface LatestRunPointer {\n\trepoSlug: string\n\trepoRoot: string\n}\n\nexport interface ResolvedRepoContext {\n\trepoRoot: string\n\trepoSlug: string\n\tartifactsDir: string\n\trest: string[]\n}\n\nexport function resolveRepoContext(argv: string[]): ResolvedRepoContext {\n\tconst { repo, rest } = parseOptionalRepoFlag(argv)\n\n\tif (repo) {\n\t\tconst repoRoot = getRepoRoot(repo)\n\t\tconst repoSlug = createRepoSlug(getRepoIdentity(repoRoot), repoRoot)\n\t\treturn {\n\t\t\trepoRoot,\n\t\t\trepoSlug,\n\t\t\tartifactsDir: getRepoArtifactsDir(repoSlug),\n\t\t\trest,\n\t\t}\n\t}\n\n\tconst cwd = process.cwd()\n\tconst repoRoot = getRepoRoot(cwd)\n\tconst repoSlug = createRepoSlug(getRepoIdentity(repoRoot), repoRoot)\n\tconst artifactsDir = getRepoArtifactsDir(repoSlug)\n\tif (isGitRepo(cwd) || existsSync(artifactsDir)) {\n\t\treturn {\n\t\t\trepoRoot,\n\t\t\trepoSlug,\n\t\t\tartifactsDir,\n\t\t\trest,\n\t\t}\n\t}\n\n\tconst latestPointerPath = getLatestRunPointerPath()\n\tif (!existsSync(latestPointerPath)) {\n\t\treturn {\n\t\t\trepoRoot,\n\t\t\trepoSlug,\n\t\t\tartifactsDir,\n\t\t\trest,\n\t\t}\n\t}\n\n\tconst latest = JSON.parse(readFileSync(latestPointerPath, \"utf-8\")) as LatestRunPointer\n\treturn {\n\t\trepoRoot: latest.repoRoot,\n\t\trepoSlug: latest.repoSlug,\n\t\tartifactsDir: getRepoArtifactsDir(latest.repoSlug),\n\t\trest,\n\t}\n}\n"],"mappings":";;;;AAiBA,SAAgB,mBAAmB,MAAqC;CACvE,MAAM,EAAE,MAAM,SAAS,sBAAsB,KAAK;AAElD,KAAI,MAAM;EACT,MAAM,WAAW,YAAY,KAAK;EAClC,MAAM,WAAW,eAAe,gBAAgB,SAAS,EAAE,SAAS;AACpE,SAAO;GACN;GACA;GACA,cAAc,oBAAoB,SAAS;GAC3C;GACA;;CAGF,MAAM,MAAM,QAAQ,KAAK;CACzB,MAAM,WAAW,YAAY,IAAI;CACjC,MAAM,WAAW,eAAe,gBAAgB,SAAS,EAAE,SAAS;CACpE,MAAM,eAAe,oBAAoB,SAAS;AAClD,KAAI,UAAU,IAAI,IAAI,WAAW,aAAa,CAC7C,QAAO;EACN;EACA;EACA;EACA;EACA;CAGF,MAAM,oBAAoB,yBAAyB;AACnD,KAAI,CAAC,WAAW,kBAAkB,CACjC,QAAO;EACN;EACA;EACA;EACA;EACA;CAGF,MAAM,SAAS,KAAK,MAAM,aAAa,mBAAmB,QAAQ,CAAC;AACnE,QAAO;EACN,UAAU,OAAO;EACjB,UAAU,OAAO;EACjB,cAAc,oBAAoB,OAAO,SAAS;EAClD;EACA"}
package/dist/run.d.mts CHANGED
@@ -1,76 +1,7 @@
1
- //#region ../core/src/types.d.ts
2
- type RunMode = "single-pi";
3
- interface MetricsSnapshot {
4
- interruptRate: number;
5
- autonomousCompletionRate: number;
6
- contextCoverageScore: number;
7
- meanTimeToCorrectHours: number | null;
8
- feedbackToDemoCycleTimeHours: number | null;
9
- totals: {
10
- taskAttempts: number;
11
- completedTasks: number;
12
- interrupts: number;
13
- observedInterruptCategories: number;
14
- coveredInterruptCategories: number;
15
- };
16
- }
17
- interface RunManifest {
18
- runId: string;
19
- repoId: string;
20
- repoSlug: string;
21
- repoRoot: string;
22
- branch: string;
23
- goal: string | null;
24
- planPath: string | null;
25
- recommendedPlanDir: string | null;
26
- mode: RunMode;
27
- startedAt: string;
28
- endedAt: string | null;
29
- stopReason: string | null;
30
- leasePath: string;
31
- piExitCode: number | null;
32
- completedTaskCount: number;
33
- blockedTaskCount: number;
34
- skippedTaskCount: number;
35
- totalCostUsd: number;
36
- }
37
- interface PiInvocationRecord {
38
- command: string;
39
- cwd: string;
40
- repoRoot: string;
41
- planPath: string | null;
42
- goal: string | null;
43
- sessionDir: string | null;
44
- sessionId: string | null;
45
- sessionPath: string | null;
46
- startedAt: string;
47
- endedAt: string;
48
- exitCode: number;
49
- stdoutPath: string;
50
- stderrPath: string;
51
- promptSummary: string;
52
- }
53
- interface RunSummary {
54
- runId: string;
55
- mode: RunMode;
56
- createdAt: string;
57
- success: boolean;
58
- message: string;
59
- piExitCode: number;
60
- recommendedPlanDir: string | null;
61
- }
62
- interface ControllerRunResult {
63
- runId: string;
64
- runDir: string;
65
- latestDir: string;
66
- manifest: RunManifest;
67
- metrics: MetricsSnapshot;
68
- summary: RunSummary;
69
- piInvocation: PiInvocationRecord;
70
- }
71
- //#endregion
1
+ import { n as LoopRunResult } from "./types-COGNGvsY.mjs";
2
+
72
3
  //#region src/run.d.ts
73
- declare function runTown(argv?: string[]): ControllerRunResult;
4
+ declare function runTown(argv?: string[]): LoopRunResult;
74
5
  //#endregion
75
6
  export { runTown };
76
7
  //# sourceMappingURL=run.d.mts.map
package/dist/run.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { t as runController } from "./controller-9ihAZj3V.mjs";
2
- import { t as isDirectExecution } from "./entrypoint-CyJDLudQ.mjs";
3
- import { a as getRecommendedPlanDir, c as getRepoLatestRunPointerPath, d as createRepoSlug, i as getLatestRunPointerPath, l as getTownHomeDir, m as getRepoRoot, p as getRepoIdentity, r as resolveRunConfig, s as getRepoArtifactsDir } from "./config-CUpe9o0x.mjs";
1
+ import { t as runLoop } from "./loop-CocC9qO1.mjs";
2
+ import { t as isDirectExecution } from "./entrypoint-WBAQmFbT.mjs";
3
+ import { a as getRecommendedPlanDir, d as createRepoSlug, i as getLatestRunPointerPath, l as getTownHomeDir, m as getRepoRoot, o as getRepoArtifactsDir, p as getRepoIdentity, r as resolveRunConfig, s as getRepoLatestRunPointerPath } from "./config-BG1v4iIi.mjs";
4
4
  import { existsSync, mkdirSync, statSync, writeFileSync } from "node:fs";
5
5
  import { dirname, join } from "node:path";
6
6
  import { readPiTownMayorPrompt, resolvePiTownExtensionPath } from "@schilderlabs/pitown-package";
@@ -27,6 +27,11 @@ function createLatestRunPointer(result, repoSlug, repoRoot) {
27
27
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
28
28
  };
29
29
  }
30
+ function getLatestControllerResult(result) {
31
+ const latestIteration = result.iterations[result.iterations.length - 1];
32
+ if (!latestIteration) throw new Error("Autonomous run did not produce any mayor iterations.");
33
+ return latestIteration.controllerResult;
34
+ }
30
35
  function runTown(argv = process.argv.slice(2)) {
31
36
  const config = resolveRunConfig(argv);
32
37
  assertDirectory(config.repo, "Target repo");
@@ -35,34 +40,44 @@ function runTown(argv = process.argv.slice(2)) {
35
40
  const repoRoot = getRepoRoot(config.repo);
36
41
  const repoSlug = createRepoSlug(getRepoIdentity(repoRoot), repoRoot);
37
42
  const recommendedPlanDir = config.plan ? null : getRecommendedPlanDir(repoSlug);
38
- const result = runController({
39
- artifactsDir: getRepoArtifactsDir(repoSlug),
40
- cwd: repoRoot,
41
- goal: config.goal,
42
- mode: "single-pi",
43
- planPath: config.plan,
44
- recommendedPlanDir,
45
- appendedSystemPrompt: readPiTownMayorPrompt(),
46
- extensionPath: resolvePiTownExtensionPath()
43
+ const result = runLoop({
44
+ runOptions: {
45
+ artifactsDir: getRepoArtifactsDir(repoSlug),
46
+ cwd: repoRoot,
47
+ goal: config.goal,
48
+ mode: "single-pi",
49
+ planPath: config.plan,
50
+ recommendedPlanDir,
51
+ appendedSystemPrompt: readPiTownMayorPrompt(),
52
+ extensionPath: resolvePiTownExtensionPath()
53
+ },
54
+ stopOnMayorIdleNoWork: true
47
55
  });
48
- const latestPointer = createLatestRunPointer(result, repoSlug, repoRoot);
56
+ const latestControllerResult = getLatestControllerResult(result);
57
+ const latestPointer = createLatestRunPointer(latestControllerResult, repoSlug, repoRoot);
49
58
  writeJson(getLatestRunPointerPath(), latestPointer);
50
59
  writeJson(getRepoLatestRunPointerPath(repoSlug), latestPointer);
51
- console.log("[pitown] run written");
52
- console.log(`- run id: ${result.runId}`);
53
- console.log(`- repo root: ${result.manifest.repoRoot}`);
54
- console.log(`- branch: ${result.manifest.branch}`);
55
- console.log(`- artifacts: ${result.runDir}`);
60
+ console.log("[pitown] autonomous run written");
61
+ console.log(`- loop id: ${result.loopId}`);
62
+ console.log(`- iterations: ${result.totalIterations}`);
63
+ console.log(`- stop reason: ${result.stopReason}`);
64
+ console.log(`- latest run id: ${latestControllerResult.runId}`);
65
+ console.log(`- repo root: ${latestControllerResult.manifest.repoRoot}`);
66
+ console.log(`- branch: ${latestControllerResult.manifest.branch}`);
67
+ console.log(`- artifacts: ${latestControllerResult.runDir}`);
56
68
  console.log(`- latest metrics: ${latestPointer.metricsPath}`);
57
- console.log(`- pi exit code: ${result.piInvocation.exitCode}`);
58
- if (!result.summary.success) console.log(`- note: ${result.summary.message}`);
59
- if (result.manifest.planPath) console.log(`- plan path: ${result.manifest.planPath}`);
60
- if (result.summary.recommendedPlanDir) console.log(`- recommended plans: ${result.summary.recommendedPlanDir}`);
69
+ console.log(`- aggregate interrupt rate: ${result.aggregateMetrics.interruptRate}`);
70
+ console.log(`- aggregate autonomous completion: ${result.aggregateMetrics.autonomousCompletionRate}`);
71
+ console.log(`- latest pi exit code: ${latestControllerResult.piInvocation.exitCode}`);
72
+ if (!latestControllerResult.summary.success) console.log(`- note: ${latestControllerResult.summary.message}`);
73
+ if (latestControllerResult.manifest.planPath) console.log(`- plan path: ${latestControllerResult.manifest.planPath}`);
74
+ if (latestControllerResult.summary.recommendedPlanDir) console.log(`- recommended plans: ${latestControllerResult.summary.recommendedPlanDir}`);
61
75
  return result;
62
76
  }
63
77
  if (isDirectExecution(import.meta.url)) {
64
78
  const result = runTown();
65
- if (result.piInvocation.exitCode !== 0) process.exitCode = result.piInvocation.exitCode;
79
+ const latestIteration = result.iterations[result.iterations.length - 1];
80
+ if (latestIteration && latestIteration.controllerResult.piInvocation.exitCode !== 0) process.exitCode = latestIteration.controllerResult.piInvocation.exitCode;
66
81
  }
67
82
 
68
83
  //#endregion
package/dist/run.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"run.mjs","names":[],"sources":["../src/run.ts"],"sourcesContent":["import { existsSync, mkdirSync, statSync, writeFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport { readPiTownMayorPrompt, resolvePiTownExtensionPath } from \"@schilderlabs/pitown-package\"\nimport {\n\tcreateRepoSlug,\n\tgetRepoIdentity,\n\tgetRepoRoot,\n\trunController,\n\ttype ControllerRunResult,\n} from \"../../core/src/index.js\"\nimport { isDirectExecution } from \"./entrypoint.js\"\nimport { resolveRunConfig } from \"./config.js\"\nimport {\n\tgetLatestRunPointerPath,\n\tgetRecommendedPlanDir,\n\tgetRepoArtifactsDir,\n\tgetRepoLatestRunPointerPath,\n\tgetTownHomeDir,\n} from \"./paths.js\"\n\ninterface LatestRunPointer {\n\trepoSlug: string\n\trepoRoot: string\n\trunId: string\n\trunDir: string\n\tlatestDir: string\n\tmanifestPath: string\n\tmetricsPath: string\n\tsummaryPath: string\n\tupdatedAt: string\n}\n\nfunction writeJson(path: string, value: unknown) {\n\tmkdirSync(dirname(path), { recursive: true })\n\twriteFileSync(path, `${JSON.stringify(value, null, 2)}\\n`, \"utf-8\")\n}\n\nfunction assertDirectory(path: string, label: string) {\n\tif (!existsSync(path)) throw new Error(`${label} does not exist: ${path}`)\n\tif (!statSync(path).isDirectory()) throw new Error(`${label} is not a directory: ${path}`)\n}\n\nfunction createLatestRunPointer(result: ControllerRunResult, repoSlug: string, repoRoot: string): LatestRunPointer {\n\treturn {\n\t\trepoSlug,\n\t\trepoRoot,\n\t\trunId: result.runId,\n\t\trunDir: result.runDir,\n\t\tlatestDir: result.latestDir,\n\t\tmanifestPath: join(result.latestDir, \"manifest.json\"),\n\t\tmetricsPath: join(result.latestDir, \"metrics.json\"),\n\t\tsummaryPath: join(result.latestDir, \"run-summary.json\"),\n\t\tupdatedAt: new Date().toISOString(),\n\t}\n}\n\nexport function runTown(argv = process.argv.slice(2)): ControllerRunResult {\n\tconst config = resolveRunConfig(argv)\n\tassertDirectory(config.repo, \"Target repo\")\n\tif (config.plan) assertDirectory(config.plan, \"Plan path\")\n\n\tconst townHome = getTownHomeDir()\n\tmkdirSync(townHome, { recursive: true })\n\n\tconst repoRoot = getRepoRoot(config.repo)\n\tconst repoId = getRepoIdentity(repoRoot)\n\tconst repoSlug = createRepoSlug(repoId, repoRoot)\n\tconst recommendedPlanDir = config.plan ? null : getRecommendedPlanDir(repoSlug)\n\tconst artifactsDir = getRepoArtifactsDir(repoSlug)\n\n\tconst result = runController({\n\t\tartifactsDir,\n\t\tcwd: repoRoot,\n\t\tgoal: config.goal,\n\t\tmode: \"single-pi\",\n\t\tplanPath: config.plan,\n\t\trecommendedPlanDir,\n\t\tappendedSystemPrompt: readPiTownMayorPrompt(),\n\t\textensionPath: resolvePiTownExtensionPath(),\n\t})\n\n\tconst latestPointer = createLatestRunPointer(result, repoSlug, repoRoot)\n\twriteJson(getLatestRunPointerPath(), latestPointer)\n\twriteJson(getRepoLatestRunPointerPath(repoSlug), latestPointer)\n\n\tconsole.log(\"[pitown] run written\")\n\tconsole.log(`- run id: ${result.runId}`)\n\tconsole.log(`- repo root: ${result.manifest.repoRoot}`)\n\tconsole.log(`- branch: ${result.manifest.branch}`)\n\tconsole.log(`- artifacts: ${result.runDir}`)\n\tconsole.log(`- latest metrics: ${latestPointer.metricsPath}`)\n\tconsole.log(`- pi exit code: ${result.piInvocation.exitCode}`)\n\tif (!result.summary.success) console.log(`- note: ${result.summary.message}`)\n\tif (result.manifest.planPath) console.log(`- plan path: ${result.manifest.planPath}`)\n\tif (result.summary.recommendedPlanDir) console.log(`- recommended plans: ${result.summary.recommendedPlanDir}`)\n\n\treturn result\n}\n\nif (isDirectExecution(import.meta.url)) {\n\tconst result = runTown()\n\tif (result.piInvocation.exitCode !== 0) process.exitCode = result.piInvocation.exitCode\n}\n"],"mappings":";;;;;;;;AAgCA,SAAS,UAAU,MAAc,OAAgB;AAChD,WAAU,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC7C,eAAc,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,EAAE,CAAC,KAAK,QAAQ;;AAGpE,SAAS,gBAAgB,MAAc,OAAe;AACrD,KAAI,CAAC,WAAW,KAAK,CAAE,OAAM,IAAI,MAAM,GAAG,MAAM,mBAAmB,OAAO;AAC1E,KAAI,CAAC,SAAS,KAAK,CAAC,aAAa,CAAE,OAAM,IAAI,MAAM,GAAG,MAAM,uBAAuB,OAAO;;AAG3F,SAAS,uBAAuB,QAA6B,UAAkB,UAAoC;AAClH,QAAO;EACN;EACA;EACA,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,WAAW,OAAO;EAClB,cAAc,KAAK,OAAO,WAAW,gBAAgB;EACrD,aAAa,KAAK,OAAO,WAAW,eAAe;EACnD,aAAa,KAAK,OAAO,WAAW,mBAAmB;EACvD,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC;;AAGF,SAAgB,QAAQ,OAAO,QAAQ,KAAK,MAAM,EAAE,EAAuB;CAC1E,MAAM,SAAS,iBAAiB,KAAK;AACrC,iBAAgB,OAAO,MAAM,cAAc;AAC3C,KAAI,OAAO,KAAM,iBAAgB,OAAO,MAAM,YAAY;AAG1D,WADiB,gBAAgB,EACb,EAAE,WAAW,MAAM,CAAC;CAExC,MAAM,WAAW,YAAY,OAAO,KAAK;CAEzC,MAAM,WAAW,eADF,gBAAgB,SAAS,EACA,SAAS;CACjD,MAAM,qBAAqB,OAAO,OAAO,OAAO,sBAAsB,SAAS;CAG/E,MAAM,SAAS,cAAc;EAC5B,cAHoB,oBAAoB,SAAS;EAIjD,KAAK;EACL,MAAM,OAAO;EACb,MAAM;EACN,UAAU,OAAO;EACjB;EACA,sBAAsB,uBAAuB;EAC7C,eAAe,4BAA4B;EAC3C,CAAC;CAEF,MAAM,gBAAgB,uBAAuB,QAAQ,UAAU,SAAS;AACxE,WAAU,yBAAyB,EAAE,cAAc;AACnD,WAAU,4BAA4B,SAAS,EAAE,cAAc;AAE/D,SAAQ,IAAI,uBAAuB;AACnC,SAAQ,IAAI,aAAa,OAAO,QAAQ;AACxC,SAAQ,IAAI,gBAAgB,OAAO,SAAS,WAAW;AACvD,SAAQ,IAAI,aAAa,OAAO,SAAS,SAAS;AAClD,SAAQ,IAAI,gBAAgB,OAAO,SAAS;AAC5C,SAAQ,IAAI,qBAAqB,cAAc,cAAc;AAC7D,SAAQ,IAAI,mBAAmB,OAAO,aAAa,WAAW;AAC9D,KAAI,CAAC,OAAO,QAAQ,QAAS,SAAQ,IAAI,WAAW,OAAO,QAAQ,UAAU;AAC7E,KAAI,OAAO,SAAS,SAAU,SAAQ,IAAI,gBAAgB,OAAO,SAAS,WAAW;AACrF,KAAI,OAAO,QAAQ,mBAAoB,SAAQ,IAAI,wBAAwB,OAAO,QAAQ,qBAAqB;AAE/G,QAAO;;AAGR,IAAI,kBAAkB,OAAO,KAAK,IAAI,EAAE;CACvC,MAAM,SAAS,SAAS;AACxB,KAAI,OAAO,aAAa,aAAa,EAAG,SAAQ,WAAW,OAAO,aAAa"}
1
+ {"version":3,"file":"run.mjs","names":[],"sources":["../src/run.ts"],"sourcesContent":["import { existsSync, mkdirSync, statSync, writeFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport { readPiTownMayorPrompt, resolvePiTownExtensionPath } from \"@schilderlabs/pitown-package\"\nimport {\n\tcreateRepoSlug,\n\tgetRepoIdentity,\n\tgetRepoRoot,\n\trunLoop,\n\ttype ControllerRunResult,\n\ttype LoopRunResult,\n} from \"../../core/src/index.js\"\nimport { isDirectExecution } from \"./entrypoint.js\"\nimport { resolveRunConfig } from \"./config.js\"\nimport {\n\tgetLatestRunPointerPath,\n\tgetRecommendedPlanDir,\n\tgetRepoArtifactsDir,\n\tgetRepoLatestRunPointerPath,\n\tgetTownHomeDir,\n} from \"./paths.js\"\n\ninterface LatestRunPointer {\n\trepoSlug: string\n\trepoRoot: string\n\trunId: string\n\trunDir: string\n\tlatestDir: string\n\tmanifestPath: string\n\tmetricsPath: string\n\tsummaryPath: string\n\tupdatedAt: string\n}\n\nfunction writeJson(path: string, value: unknown) {\n\tmkdirSync(dirname(path), { recursive: true })\n\twriteFileSync(path, `${JSON.stringify(value, null, 2)}\\n`, \"utf-8\")\n}\n\nfunction assertDirectory(path: string, label: string) {\n\tif (!existsSync(path)) throw new Error(`${label} does not exist: ${path}`)\n\tif (!statSync(path).isDirectory()) throw new Error(`${label} is not a directory: ${path}`)\n}\n\nfunction createLatestRunPointer(result: ControllerRunResult, repoSlug: string, repoRoot: string): LatestRunPointer {\n\treturn {\n\t\trepoSlug,\n\t\trepoRoot,\n\t\trunId: result.runId,\n\t\trunDir: result.runDir,\n\t\tlatestDir: result.latestDir,\n\t\tmanifestPath: join(result.latestDir, \"manifest.json\"),\n\t\tmetricsPath: join(result.latestDir, \"metrics.json\"),\n\t\tsummaryPath: join(result.latestDir, \"run-summary.json\"),\n\t\tupdatedAt: new Date().toISOString(),\n\t}\n}\n\nfunction getLatestControllerResult(result: LoopRunResult): ControllerRunResult {\n\tconst latestIteration = result.iterations[result.iterations.length - 1]\n\tif (!latestIteration) {\n\t\tthrow new Error(\"Autonomous run did not produce any mayor iterations.\")\n\t}\n\n\treturn latestIteration.controllerResult\n}\n\nexport function runTown(argv = process.argv.slice(2)): LoopRunResult {\n\tconst config = resolveRunConfig(argv)\n\tassertDirectory(config.repo, \"Target repo\")\n\tif (config.plan) assertDirectory(config.plan, \"Plan path\")\n\n\tconst townHome = getTownHomeDir()\n\tmkdirSync(townHome, { recursive: true })\n\n\tconst repoRoot = getRepoRoot(config.repo)\n\tconst repoId = getRepoIdentity(repoRoot)\n\tconst repoSlug = createRepoSlug(repoId, repoRoot)\n\tconst recommendedPlanDir = config.plan ? null : getRecommendedPlanDir(repoSlug)\n\tconst artifactsDir = getRepoArtifactsDir(repoSlug)\n\n\tconst result = runLoop({\n\t\trunOptions: {\n\t\t\tartifactsDir,\n\t\t\tcwd: repoRoot,\n\t\t\tgoal: config.goal,\n\t\t\tmode: \"single-pi\",\n\t\t\tplanPath: config.plan,\n\t\t\trecommendedPlanDir,\n\t\t\tappendedSystemPrompt: readPiTownMayorPrompt(),\n\t\t\textensionPath: resolvePiTownExtensionPath(),\n\t\t},\n\t\tstopOnMayorIdleNoWork: true,\n\t})\n\tconst latestControllerResult = getLatestControllerResult(result)\n\n\tconst latestPointer = createLatestRunPointer(latestControllerResult, repoSlug, repoRoot)\n\twriteJson(getLatestRunPointerPath(), latestPointer)\n\twriteJson(getRepoLatestRunPointerPath(repoSlug), latestPointer)\n\n\tconsole.log(\"[pitown] autonomous run written\")\n\tconsole.log(`- loop id: ${result.loopId}`)\n\tconsole.log(`- iterations: ${result.totalIterations}`)\n\tconsole.log(`- stop reason: ${result.stopReason}`)\n\tconsole.log(`- latest run id: ${latestControllerResult.runId}`)\n\tconsole.log(`- repo root: ${latestControllerResult.manifest.repoRoot}`)\n\tconsole.log(`- branch: ${latestControllerResult.manifest.branch}`)\n\tconsole.log(`- artifacts: ${latestControllerResult.runDir}`)\n\tconsole.log(`- latest metrics: ${latestPointer.metricsPath}`)\n\tconsole.log(`- aggregate interrupt rate: ${result.aggregateMetrics.interruptRate}`)\n\tconsole.log(`- aggregate autonomous completion: ${result.aggregateMetrics.autonomousCompletionRate}`)\n\tconsole.log(`- latest pi exit code: ${latestControllerResult.piInvocation.exitCode}`)\n\tif (!latestControllerResult.summary.success) console.log(`- note: ${latestControllerResult.summary.message}`)\n\tif (latestControllerResult.manifest.planPath) console.log(`- plan path: ${latestControllerResult.manifest.planPath}`)\n\tif (latestControllerResult.summary.recommendedPlanDir) {\n\t\tconsole.log(`- recommended plans: ${latestControllerResult.summary.recommendedPlanDir}`)\n\t}\n\n\treturn result\n}\n\nif (isDirectExecution(import.meta.url)) {\n\tconst result = runTown()\n\tconst latestIteration = result.iterations[result.iterations.length - 1]\n\tif (latestIteration && latestIteration.controllerResult.piInvocation.exitCode !== 0) {\n\t\tprocess.exitCode = latestIteration.controllerResult.piInvocation.exitCode\n\t}\n}\n"],"mappings":";;;;;;;;AAiCA,SAAS,UAAU,MAAc,OAAgB;AAChD,WAAU,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAC7C,eAAc,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,EAAE,CAAC,KAAK,QAAQ;;AAGpE,SAAS,gBAAgB,MAAc,OAAe;AACrD,KAAI,CAAC,WAAW,KAAK,CAAE,OAAM,IAAI,MAAM,GAAG,MAAM,mBAAmB,OAAO;AAC1E,KAAI,CAAC,SAAS,KAAK,CAAC,aAAa,CAAE,OAAM,IAAI,MAAM,GAAG,MAAM,uBAAuB,OAAO;;AAG3F,SAAS,uBAAuB,QAA6B,UAAkB,UAAoC;AAClH,QAAO;EACN;EACA;EACA,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,WAAW,OAAO;EAClB,cAAc,KAAK,OAAO,WAAW,gBAAgB;EACrD,aAAa,KAAK,OAAO,WAAW,eAAe;EACnD,aAAa,KAAK,OAAO,WAAW,mBAAmB;EACvD,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC;;AAGF,SAAS,0BAA0B,QAA4C;CAC9E,MAAM,kBAAkB,OAAO,WAAW,OAAO,WAAW,SAAS;AACrE,KAAI,CAAC,gBACJ,OAAM,IAAI,MAAM,uDAAuD;AAGxE,QAAO,gBAAgB;;AAGxB,SAAgB,QAAQ,OAAO,QAAQ,KAAK,MAAM,EAAE,EAAiB;CACpE,MAAM,SAAS,iBAAiB,KAAK;AACrC,iBAAgB,OAAO,MAAM,cAAc;AAC3C,KAAI,OAAO,KAAM,iBAAgB,OAAO,MAAM,YAAY;AAG1D,WADiB,gBAAgB,EACb,EAAE,WAAW,MAAM,CAAC;CAExC,MAAM,WAAW,YAAY,OAAO,KAAK;CAEzC,MAAM,WAAW,eADF,gBAAgB,SAAS,EACA,SAAS;CACjD,MAAM,qBAAqB,OAAO,OAAO,OAAO,sBAAsB,SAAS;CAG/E,MAAM,SAAS,QAAQ;EACtB,YAAY;GACX,cAJmB,oBAAoB,SAAS;GAKhD,KAAK;GACL,MAAM,OAAO;GACb,MAAM;GACN,UAAU,OAAO;GACjB;GACA,sBAAsB,uBAAuB;GAC7C,eAAe,4BAA4B;GAC3C;EACD,uBAAuB;EACvB,CAAC;CACF,MAAM,yBAAyB,0BAA0B,OAAO;CAEhE,MAAM,gBAAgB,uBAAuB,wBAAwB,UAAU,SAAS;AACxF,WAAU,yBAAyB,EAAE,cAAc;AACnD,WAAU,4BAA4B,SAAS,EAAE,cAAc;AAE/D,SAAQ,IAAI,kCAAkC;AAC9C,SAAQ,IAAI,cAAc,OAAO,SAAS;AAC1C,SAAQ,IAAI,iBAAiB,OAAO,kBAAkB;AACtD,SAAQ,IAAI,kBAAkB,OAAO,aAAa;AAClD,SAAQ,IAAI,oBAAoB,uBAAuB,QAAQ;AAC/D,SAAQ,IAAI,gBAAgB,uBAAuB,SAAS,WAAW;AACvE,SAAQ,IAAI,aAAa,uBAAuB,SAAS,SAAS;AAClE,SAAQ,IAAI,gBAAgB,uBAAuB,SAAS;AAC5D,SAAQ,IAAI,qBAAqB,cAAc,cAAc;AAC7D,SAAQ,IAAI,+BAA+B,OAAO,iBAAiB,gBAAgB;AACnF,SAAQ,IAAI,sCAAsC,OAAO,iBAAiB,2BAA2B;AACrG,SAAQ,IAAI,0BAA0B,uBAAuB,aAAa,WAAW;AACrF,KAAI,CAAC,uBAAuB,QAAQ,QAAS,SAAQ,IAAI,WAAW,uBAAuB,QAAQ,UAAU;AAC7G,KAAI,uBAAuB,SAAS,SAAU,SAAQ,IAAI,gBAAgB,uBAAuB,SAAS,WAAW;AACrH,KAAI,uBAAuB,QAAQ,mBAClC,SAAQ,IAAI,wBAAwB,uBAAuB,QAAQ,qBAAqB;AAGzF,QAAO;;AAGR,IAAI,kBAAkB,OAAO,KAAK,IAAI,EAAE;CACvC,MAAM,SAAS,SAAS;CACxB,MAAM,kBAAkB,OAAO,WAAW,OAAO,WAAW,SAAS;AACrE,KAAI,mBAAmB,gBAAgB,iBAAiB,aAAa,aAAa,EACjF,SAAQ,WAAW,gBAAgB,iBAAiB,aAAa"}
package/dist/status.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { t as isDirectExecution } from "./entrypoint-CyJDLudQ.mjs";
2
- import { c as getRepoLatestRunPointerPath, d as createRepoSlug, i as getLatestRunPointerPath, l as getTownHomeDir, m as getRepoRoot, p as getRepoIdentity, s as getRepoArtifactsDir, t as parseCliFlags } from "./config-CUpe9o0x.mjs";
1
+ import { t as isDirectExecution } from "./entrypoint-WBAQmFbT.mjs";
2
+ import { d as createRepoSlug, i as getLatestRunPointerPath, l as getTownHomeDir, m as getRepoRoot, o as getRepoArtifactsDir, p as getRepoIdentity, s as getRepoLatestRunPointerPath, t as parseCliFlags } from "./config-BG1v4iIi.mjs";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
 
@@ -0,0 +1,195 @@
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+
4
+ //#region ../core/src/events.ts
5
+ function appendJsonl(filePath, value) {
6
+ mkdirSync(dirname(filePath), { recursive: true });
7
+ writeFileSync(filePath, `${JSON.stringify(value)}\n`, {
8
+ encoding: "utf-8",
9
+ flag: "a"
10
+ });
11
+ }
12
+ function readJsonl(filePath) {
13
+ try {
14
+ return readFileSync(filePath, "utf-8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line) => JSON.parse(line));
15
+ } catch {
16
+ return [];
17
+ }
18
+ }
19
+
20
+ //#endregion
21
+ //#region ../core/src/agents.ts
22
+ function writeJson$1(path, value) {
23
+ mkdirSync(dirname(path), { recursive: true });
24
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
25
+ }
26
+ function ensureMailbox(path) {
27
+ mkdirSync(dirname(path), { recursive: true });
28
+ if (!existsSync(path)) writeFileSync(path, "", "utf-8");
29
+ }
30
+ function getAgentsDir(artifactsDir) {
31
+ return join(artifactsDir, "agents");
32
+ }
33
+ function getAgentDir(artifactsDir, agentId) {
34
+ return join(getAgentsDir(artifactsDir), agentId);
35
+ }
36
+ function getAgentStatePath(artifactsDir, agentId) {
37
+ return join(getAgentDir(artifactsDir, agentId), "state.json");
38
+ }
39
+ function getAgentSessionPath(artifactsDir, agentId) {
40
+ return join(getAgentDir(artifactsDir, agentId), "session.json");
41
+ }
42
+ function getAgentMailboxPath(artifactsDir, agentId, box) {
43
+ return join(getAgentDir(artifactsDir, agentId), `${box}.jsonl`);
44
+ }
45
+ function getAgentSessionsDir(artifactsDir, agentId) {
46
+ return join(getAgentDir(artifactsDir, agentId), "sessions");
47
+ }
48
+ function getSessionIdFromPath(sessionPath) {
49
+ if (!sessionPath) return null;
50
+ return /_([0-9a-f-]+)\.jsonl$/i.exec(sessionPath)?.[1] ?? null;
51
+ }
52
+ function createAgentSessionRecord(input) {
53
+ return {
54
+ runtime: "pi",
55
+ persisted: true,
56
+ sessionDir: input?.sessionDir ?? null,
57
+ sessionId: input?.sessionId ?? null,
58
+ sessionPath: input?.sessionPath ?? null,
59
+ processId: input?.processId ?? null,
60
+ lastAttachedAt: input?.lastAttachedAt ?? null
61
+ };
62
+ }
63
+ function createAgentState(input) {
64
+ return {
65
+ agentId: input.agentId,
66
+ role: input.role,
67
+ status: input.status,
68
+ taskId: input.taskId ?? null,
69
+ task: input.task ?? null,
70
+ branch: input.branch ?? null,
71
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
72
+ lastMessage: input.lastMessage ?? null,
73
+ waitingOn: input.waitingOn ?? null,
74
+ blocked: input.blocked ?? false,
75
+ runId: input.runId ?? null,
76
+ session: input.session ?? createAgentSessionRecord()
77
+ };
78
+ }
79
+ function writeAgentState(artifactsDir, state) {
80
+ mkdirSync(getAgentDir(artifactsDir, state.agentId), { recursive: true });
81
+ ensureMailbox(getAgentMailboxPath(artifactsDir, state.agentId, "inbox"));
82
+ ensureMailbox(getAgentMailboxPath(artifactsDir, state.agentId, "outbox"));
83
+ writeJson$1(getAgentStatePath(artifactsDir, state.agentId), state);
84
+ writeJson$1(getAgentSessionPath(artifactsDir, state.agentId), state.session);
85
+ }
86
+ function readAgentState(artifactsDir, agentId) {
87
+ const statePath = getAgentStatePath(artifactsDir, agentId);
88
+ try {
89
+ return JSON.parse(readFileSync(statePath, "utf-8"));
90
+ } catch {
91
+ return null;
92
+ }
93
+ }
94
+ function listAgentStates(artifactsDir) {
95
+ const agentsDir = getAgentsDir(artifactsDir);
96
+ let entries;
97
+ try {
98
+ entries = readdirSync(agentsDir);
99
+ } catch {
100
+ return [];
101
+ }
102
+ return entries.map((entry) => readAgentState(artifactsDir, entry)).filter((state) => state !== null).sort((left, right) => left.agentId.localeCompare(right.agentId));
103
+ }
104
+ function appendAgentMessage(input) {
105
+ const record = {
106
+ box: input.box,
107
+ from: input.from,
108
+ body: input.body,
109
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
110
+ };
111
+ appendJsonl(getAgentMailboxPath(input.artifactsDir, input.agentId, input.box), record);
112
+ return record;
113
+ }
114
+ function readAgentMessages(artifactsDir, agentId, box) {
115
+ return readJsonl(getAgentMailboxPath(artifactsDir, agentId, box));
116
+ }
117
+ function getLatestAgentSession(artifactsDir, agentId) {
118
+ const sessionDir = getAgentSessionsDir(artifactsDir, agentId);
119
+ let entries;
120
+ try {
121
+ entries = readdirSync(sessionDir);
122
+ } catch {
123
+ return createAgentSessionRecord({ sessionDir });
124
+ }
125
+ const latestSessionPath = entries.filter((entry) => entry.endsWith(".jsonl")).sort().at(-1) ?? null;
126
+ if (latestSessionPath === null) return createAgentSessionRecord({ sessionDir });
127
+ const sessionPath = join(sessionDir, latestSessionPath);
128
+ return createAgentSessionRecord({
129
+ sessionDir,
130
+ sessionPath,
131
+ sessionId: getSessionIdFromPath(sessionPath)
132
+ });
133
+ }
134
+
135
+ //#endregion
136
+ //#region ../core/src/tasks.ts
137
+ function writeJson(path, value) {
138
+ mkdirSync(dirname(path), { recursive: true });
139
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
140
+ }
141
+ function getTasksDir(artifactsDir) {
142
+ return join(artifactsDir, "tasks");
143
+ }
144
+ function getTaskPath(artifactsDir, taskId) {
145
+ return join(getTasksDir(artifactsDir), `${taskId}.json`);
146
+ }
147
+ function createTaskRecord(input) {
148
+ const now = (/* @__PURE__ */ new Date()).toISOString();
149
+ return {
150
+ taskId: input.taskId,
151
+ title: input.title,
152
+ status: input.status,
153
+ role: input.role,
154
+ assignedAgentId: input.assignedAgentId,
155
+ createdBy: input.createdBy,
156
+ createdAt: now,
157
+ updatedAt: now
158
+ };
159
+ }
160
+ function writeTaskRecord(artifactsDir, task) {
161
+ writeJson(getTaskPath(artifactsDir, task.taskId), task);
162
+ }
163
+ function updateTaskRecordStatus(artifactsDir, taskId, status) {
164
+ const task = readTaskRecord(artifactsDir, taskId);
165
+ if (task === null) return null;
166
+ const updatedTask = {
167
+ ...task,
168
+ status,
169
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
170
+ };
171
+ writeTaskRecord(artifactsDir, updatedTask);
172
+ return updatedTask;
173
+ }
174
+ function readTaskRecord(artifactsDir, taskId) {
175
+ const path = getTaskPath(artifactsDir, taskId);
176
+ try {
177
+ return JSON.parse(readFileSync(path, "utf-8"));
178
+ } catch {
179
+ return null;
180
+ }
181
+ }
182
+ function listTaskRecords(artifactsDir) {
183
+ const tasksDir = getTasksDir(artifactsDir);
184
+ let entries;
185
+ try {
186
+ entries = readdirSync(tasksDir);
187
+ } catch {
188
+ return [];
189
+ }
190
+ return entries.filter((entry) => entry.endsWith(".json")).map((entry) => readTaskRecord(artifactsDir, entry.replace(/\.json$/, ""))).filter((task) => task !== null).sort((left, right) => left.taskId.localeCompare(right.taskId));
191
+ }
192
+
193
+ //#endregion
194
+ export { appendAgentMessage as a, getAgentDir as c, listAgentStates as d, readAgentMessages as f, appendJsonl as h, writeTaskRecord as i, getAgentSessionsDir as l, writeAgentState as m, listTaskRecords as n, createAgentSessionRecord as o, readAgentState as p, updateTaskRecordStatus as r, createAgentState as s, createTaskRecord as t, getLatestAgentSession as u };
195
+ //# sourceMappingURL=tasks-De4IAy3x.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tasks-De4IAy3x.mjs","names":["writeJson"],"sources":["../../core/src/events.ts","../../core/src/agents.ts","../../core/src/tasks.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { dirname } from \"node:path\"\n\nexport function appendJsonl(filePath: string, value: unknown) {\n\tmkdirSync(dirname(filePath), { recursive: true })\n\twriteFileSync(filePath, `${JSON.stringify(value)}\\n`, { encoding: \"utf-8\", flag: \"a\" })\n}\n\nexport function readJsonl<T>(filePath: string): T[] {\n\ttry {\n\t\tconst raw = readFileSync(filePath, \"utf-8\")\n\t\treturn raw\n\t\t\t.split(/\\r?\\n/)\n\t\t\t.map((line) => line.trim())\n\t\t\t.filter(Boolean)\n\t\t\t.map((line) => JSON.parse(line) as T)\n\t} catch {\n\t\treturn []\n\t}\n}\n","import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport { appendJsonl, readJsonl } from \"./events.js\"\nimport type {\n\tAgentMailbox,\n\tAgentMessageRecord,\n\tAgentSessionRecord,\n\tAgentStateSnapshot,\n\tAgentStatus,\n} from \"./types.js\"\n\nfunction writeJson(path: string, value: unknown) {\n\tmkdirSync(dirname(path), { recursive: true })\n\twriteFileSync(path, `${JSON.stringify(value, null, 2)}\\n`, \"utf-8\")\n}\n\nfunction ensureMailbox(path: string) {\n\tmkdirSync(dirname(path), { recursive: true })\n\tif (!existsSync(path)) writeFileSync(path, \"\", \"utf-8\")\n}\n\nexport function getAgentsDir(artifactsDir: string): string {\n\treturn join(artifactsDir, \"agents\")\n}\n\nexport function getAgentDir(artifactsDir: string, agentId: string): string {\n\treturn join(getAgentsDir(artifactsDir), agentId)\n}\n\nexport function getAgentStatePath(artifactsDir: string, agentId: string): string {\n\treturn join(getAgentDir(artifactsDir, agentId), \"state.json\")\n}\n\nexport function getAgentSessionPath(artifactsDir: string, agentId: string): string {\n\treturn join(getAgentDir(artifactsDir, agentId), \"session.json\")\n}\n\nexport function getAgentMailboxPath(artifactsDir: string, agentId: string, box: AgentMailbox): string {\n\treturn join(getAgentDir(artifactsDir, agentId), `${box}.jsonl`)\n}\n\nexport function getAgentSessionsDir(artifactsDir: string, agentId: string): string {\n\treturn join(getAgentDir(artifactsDir, agentId), \"sessions\")\n}\n\nexport function getSessionIdFromPath(sessionPath: string | null | undefined): string | null {\n\tif (!sessionPath) return null\n\tconst match = /_([0-9a-f-]+)\\.jsonl$/i.exec(sessionPath)\n\treturn match?.[1] ?? null\n}\n\nexport function createAgentSessionRecord(\n\tinput?: Partial<Pick<AgentSessionRecord, \"sessionDir\" | \"sessionId\" | \"sessionPath\" | \"processId\" | \"lastAttachedAt\">>,\n): AgentSessionRecord {\n\treturn {\n\t\truntime: \"pi\",\n\t\tpersisted: true,\n\t\tsessionDir: input?.sessionDir ?? null,\n\t\tsessionId: input?.sessionId ?? null,\n\t\tsessionPath: input?.sessionPath ?? null,\n\t\tprocessId: input?.processId ?? null,\n\t\tlastAttachedAt: input?.lastAttachedAt ?? null,\n\t}\n}\n\nexport function createAgentState(input: {\n\tagentId: string\n\trole: string\n\tstatus: AgentStatus\n\ttaskId?: string | null\n\ttask?: string | null\n\tbranch?: string | null\n\tlastMessage?: string | null\n\twaitingOn?: string | null\n\tblocked?: boolean\n\trunId?: string | null\n\tsession?: AgentSessionRecord\n}): AgentStateSnapshot {\n\treturn {\n\t\tagentId: input.agentId,\n\t\trole: input.role,\n\t\tstatus: input.status,\n\t\ttaskId: input.taskId ?? null,\n\t\ttask: input.task ?? null,\n\t\tbranch: input.branch ?? null,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tlastMessage: input.lastMessage ?? null,\n\t\twaitingOn: input.waitingOn ?? null,\n\t\tblocked: input.blocked ?? false,\n\t\trunId: input.runId ?? null,\n\t\tsession: input.session ?? createAgentSessionRecord(),\n\t}\n}\n\nexport function writeAgentState(artifactsDir: string, state: AgentStateSnapshot) {\n\tmkdirSync(getAgentDir(artifactsDir, state.agentId), { recursive: true })\n\tensureMailbox(getAgentMailboxPath(artifactsDir, state.agentId, \"inbox\"))\n\tensureMailbox(getAgentMailboxPath(artifactsDir, state.agentId, \"outbox\"))\n\twriteJson(getAgentStatePath(artifactsDir, state.agentId), state)\n\twriteJson(getAgentSessionPath(artifactsDir, state.agentId), state.session)\n}\n\nexport function readAgentState(artifactsDir: string, agentId: string): AgentStateSnapshot | null {\n\tconst statePath = getAgentStatePath(artifactsDir, agentId)\n\ttry {\n\t\treturn JSON.parse(readFileSync(statePath, \"utf-8\")) as AgentStateSnapshot\n\t} catch {\n\t\treturn null\n\t}\n}\n\nexport function listAgentStates(artifactsDir: string): AgentStateSnapshot[] {\n\tconst agentsDir = getAgentsDir(artifactsDir)\n\tlet entries: string[]\n\ttry {\n\t\tentries = readdirSync(agentsDir)\n\t} catch {\n\t\treturn []\n\t}\n\n\treturn entries\n\t\t.map((entry) => readAgentState(artifactsDir, entry))\n\t\t.filter((state): state is AgentStateSnapshot => state !== null)\n\t\t.sort((left, right) => left.agentId.localeCompare(right.agentId))\n}\n\nexport function appendAgentMessage(input: {\n\tartifactsDir: string\n\tagentId: string\n\tbox: AgentMailbox\n\tfrom: string\n\tbody: string\n}): AgentMessageRecord {\n\tconst record: AgentMessageRecord = {\n\t\tbox: input.box,\n\t\tfrom: input.from,\n\t\tbody: input.body,\n\t\tcreatedAt: new Date().toISOString(),\n\t}\n\tappendJsonl(getAgentMailboxPath(input.artifactsDir, input.agentId, input.box), record)\n\treturn record\n}\n\nexport function readAgentMessages(artifactsDir: string, agentId: string, box: AgentMailbox): AgentMessageRecord[] {\n\treturn readJsonl<AgentMessageRecord>(getAgentMailboxPath(artifactsDir, agentId, box))\n}\n\nexport function getLatestAgentSession(artifactsDir: string, agentId: string): AgentSessionRecord {\n\tconst sessionDir = getAgentSessionsDir(artifactsDir, agentId)\n\tlet entries: string[]\n\ttry {\n\t\tentries = readdirSync(sessionDir)\n\t} catch {\n\t\treturn createAgentSessionRecord({ sessionDir })\n\t}\n\n\tconst latestSessionPath =\n\t\tentries\n\t\t\t.filter((entry) => entry.endsWith(\".jsonl\"))\n\t\t\t.sort()\n\t\t\t.at(-1) ?? null\n\n\tif (latestSessionPath === null) return createAgentSessionRecord({ sessionDir })\n\n\tconst sessionPath = join(sessionDir, latestSessionPath)\n\treturn createAgentSessionRecord({\n\t\tsessionDir,\n\t\tsessionPath,\n\t\tsessionId: getSessionIdFromPath(sessionPath),\n\t})\n}\n","import { mkdirSync, readFileSync, readdirSync, writeFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\nimport type { TaskRecord, TaskStatus } from \"./types.js\"\n\nfunction writeJson(path: string, value: unknown) {\n\tmkdirSync(dirname(path), { recursive: true })\n\twriteFileSync(path, `${JSON.stringify(value, null, 2)}\\n`, \"utf-8\")\n}\n\nexport function getTasksDir(artifactsDir: string): string {\n\treturn join(artifactsDir, \"tasks\")\n}\n\nexport function getTaskPath(artifactsDir: string, taskId: string): string {\n\treturn join(getTasksDir(artifactsDir), `${taskId}.json`)\n}\n\nexport function createTaskRecord(input: {\n\ttaskId: string\n\ttitle: string\n\tstatus: TaskStatus\n\trole: string\n\tassignedAgentId: string\n\tcreatedBy: string\n}): TaskRecord {\n\tconst now = new Date().toISOString()\n\treturn {\n\t\ttaskId: input.taskId,\n\t\ttitle: input.title,\n\t\tstatus: input.status,\n\t\trole: input.role,\n\t\tassignedAgentId: input.assignedAgentId,\n\t\tcreatedBy: input.createdBy,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t}\n}\n\nexport function writeTaskRecord(artifactsDir: string, task: TaskRecord) {\n\twriteJson(getTaskPath(artifactsDir, task.taskId), task)\n}\n\nexport function updateTaskRecordStatus(artifactsDir: string, taskId: string, status: TaskStatus): TaskRecord | null {\n\tconst task = readTaskRecord(artifactsDir, taskId)\n\tif (task === null) return null\n\n\tconst updatedTask: TaskRecord = {\n\t\t...task,\n\t\tstatus,\n\t\tupdatedAt: new Date().toISOString(),\n\t}\n\twriteTaskRecord(artifactsDir, updatedTask)\n\treturn updatedTask\n}\n\nexport function readTaskRecord(artifactsDir: string, taskId: string): TaskRecord | null {\n\tconst path = getTaskPath(artifactsDir, taskId)\n\ttry {\n\t\treturn JSON.parse(readFileSync(path, \"utf-8\")) as TaskRecord\n\t} catch {\n\t\treturn null\n\t}\n}\n\nexport function listTaskRecords(artifactsDir: string): TaskRecord[] {\n\tconst tasksDir = getTasksDir(artifactsDir)\n\tlet entries: string[]\n\ttry {\n\t\tentries = readdirSync(tasksDir)\n\t} catch {\n\t\treturn []\n\t}\n\n\treturn entries\n\t\t.filter((entry) => entry.endsWith(\".json\"))\n\t\t.map((entry) => readTaskRecord(artifactsDir, entry.replace(/\\.json$/, \"\")))\n\t\t.filter((task): task is TaskRecord => task !== null)\n\t\t.sort((left, right) => left.taskId.localeCompare(right.taskId))\n}\n"],"mappings":"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"}