fix(lint): restore 0 Biome diagnostics and fix web-mode-onboarding test timeout
- Remove/prefix unused imports and variables across 11 src/ files to clear 74 diagnostics introduced by 37 subsequent commits since run #3 - Fix pre-existing timeout in web-mode-onboarding integration test: - Add timeoutMs: 120_000 to launchPackagedWebHost call (was unbounded) - Raise AbortSignal.timeout on simple fetches 10s → 30s (under parallel load) - Raise overall test timeout 180s → 420s (budget: 120+60+30+30+120+30=390s) - Log autoresearch run #4 and update lessons in autoresearch.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
b2bcb922de
commit
05953e9599
14 changed files with 127 additions and 80 deletions
|
|
@ -2,3 +2,4 @@
|
|||
{"run": 1, "commit": "15269f4", "metric": 40.0, "metrics": {}, "status": "keep", "description": "baseline measurement", "timestamp": 1778242955776, "segment": 0, "confidence": null, "asi": {"hypothesis": "baseline measurement", "breakdown": "26 errors, 13 warnings, 1 info"}}
|
||||
{"run": 2, "commit": "72e27f9", "metric": 11.0, "metrics": {}, "status": "keep", "description": "auto-fix format + organizeImports: biome check --write src/", "timestamp": 1778243276590, "segment": 0, "confidence": null, "asi": {"hypothesis": "All 26 errors are auto-fixable format/organizeImports; fixing them drops total from 40 to 11", "breakdown": "0 errors, 11 warnings"}}
|
||||
{"run": 3, "commit": "c6ee770", "metric": 0.0, "metrics": {}, "status": "keep", "description": "fix 11 unused imports/variables by removing or prefixing with underscore", "timestamp": 1778243617559, "segment": 0, "confidence": 3.64, "asi": {"hypothesis": "All 11 remaining warnings are unused imports/variables \u2014 removing unused imports and prefixing intentionally kept but unused variables with underscore eliminates all diagnostics", "breakdown": "Removed: injectReasoningGuidance, withQueryTimeout (unused import), getAutoSession, logWarning (2x), debugLog, readFileSync/unlinkSync/writeFileSync. Prefixed: MAX_HISTOGRAM_BUCKETS, REASONING_ASSIST_MAX_CHARS, basePath param."}}
|
||||
{"run": 4, "commit": "b2bcb922d", "metric": 0.0, "metrics": {}, "status": "keep", "description": "re-fix 74 new diagnostics from 37 subsequent commits: biome --write dropped to 16, manual unused-import/var/param cleanup to 0; fixed web-mode-onboarding test timeout (timeoutMs 120s, AbortSignal 30s, test budget 420s)", "timestamp": 1778403638931, "segment": 0, "confidence": null, "asi": {"hypothesis": "37 new commits introduced 74 diagnostics (57 errors, 17 warnings); auto-fix handles format/import errors, manual prefix/removal handles unsafe unused-import warnings", "breakdown": "0 errors, 0 warnings after fix; all 409 test files pass"}}
|
||||
|
|
|
|||
|
|
@ -44,4 +44,10 @@ Run until interrupted by the user.
|
|||
## What's Been Tried
|
||||
|
||||
- **#2 (auto-fix)**: `biome check --write` — fixed 26 auto-fixable errors (format/organizeImports), dropped diagnostics from 40 to 11. Status: keep.
|
||||
- **#3 (manual fixes)**: Removed 7 unused imports (`injectReasoningGuidance`, `withQueryTimeout`, `getAutoSession`, `logWarning` x3, `debugLog`, `readFileSync/unlinkSync/writeFileSync`) and prefixed 4 intentionally-unused items with underscore (`_MAX_HISTOGRAM_BUCKETS`, `_REASONING_ASSIST_MAX_CHARS`, `_basePath`, `_withQueryTimeout`). Dropped from 11 to 0. Status: keep.
|
||||
- **#3 (manual fixes)**: Removed 7 unused imports and prefixed 4 intentionally-unused items with underscore. Dropped from 11 to 0. Status: keep.
|
||||
- **#4 (regression re-fix)**: 37 new commits introduced 74 diagnostics. `biome check --write` fixed 58 (auto-safe), manual prefix/removal fixed the remaining 16 unsafe warnings across 11 files. Also fixed pre-existing web-mode-onboarding test timeout: added `timeoutMs: 120_000` to `launchPackagedWebHost`, raised `AbortSignal.timeout` on simple fetches 10s→30s, raised test budget 180s→420s. All 409 test files pass. Diagnostics: 0. Status: keep.
|
||||
|
||||
## Lessons
|
||||
- New development (37 commits) is enough to re-introduce 74 diagnostics. Re-run autoresearch periodically (monthly or after large feature branches land).
|
||||
- Pattern of new violations: unused imports from refactors, unused function params from stubs, duplicate imports. Auto-fix handles errors; unsafe-fix (unused-import/var) requires manual triage.
|
||||
- Integration test timeout under parallel load: cold-start Next.js can consume most of a 180s test timeout leaving insufficient budget for multi-step API calls. Fix: bound launch phase separately, raise individual fetch timeouts, increase overall budget to match worst-case sum.
|
||||
|
|
|
|||
|
|
@ -11,19 +11,21 @@ import {
|
|||
sanitizeCommand,
|
||||
} from "@singularity-forge/pi-coding-agent";
|
||||
import { rewriteCommandWithRtk } from "../shared/rtk.js";
|
||||
import {
|
||||
addEvent,
|
||||
pendingAlerts,
|
||||
pushAlert,
|
||||
setPendingAlerts,
|
||||
} from "./bg-events.js";
|
||||
import { addEvent, pushAlert } from "./bg-events.js";
|
||||
import { analyzeLine } from "./output-formatter.js";
|
||||
import { startPortProbing, transitionToReady } from "./readiness-detector.js";
|
||||
import { DEAD_PROCESS_TTL, MAX_BUFFER_LINES } from "./types.js";
|
||||
import { formatUptime, restoreWindowsVTInput } from "./utilities.js";
|
||||
|
||||
// Re-export event/alert helpers so existing consumers (bg-shell-lifecycle.js)
|
||||
// continue to work without changing their import paths.
|
||||
export { addEvent, MAX_PENDING_ALERTS, pendingAlerts, pushAlert, setPendingAlerts } from "./bg-events.js";
|
||||
export {
|
||||
addEvent,
|
||||
MAX_PENDING_ALERTS,
|
||||
pendingAlerts,
|
||||
pushAlert,
|
||||
setPendingAlerts,
|
||||
} from "./bg-events.js";
|
||||
// ── Process Registry ───────────────────────────────────────────────────────
|
||||
export const processes = new Map();
|
||||
export function addOutputLine(bg, stream, line) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { existsSync, readdirSync } from "node:fs";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { existsSync, readdirSync } from "node:fs";
|
||||
import { join, relative, resolve } from "node:path";
|
||||
import { isToolCallEventType } from "@singularity-forge/pi-coding-agent";
|
||||
import { resetAskUserQuestionsCache } from "../../ask-user-questions.js";
|
||||
|
|
@ -69,6 +69,7 @@ import {
|
|||
import { initSessionRecorder } from "../session-recorder.js";
|
||||
import { deriveState } from "../state.js";
|
||||
import { countGoogleGeminiCliTokens } from "../token-counter.js";
|
||||
import { getSessionTodoCompactionBlock } from "../tools/session-todo-tool.js";
|
||||
import { parseUnitId } from "../unit-id.js";
|
||||
import { logWarning as safetyLogWarning } from "../workflow-logger.js";
|
||||
import {
|
||||
|
|
@ -79,7 +80,6 @@ import {
|
|||
import { handleAgentEnd } from "./agent-end-recovery.js";
|
||||
import { installNotifyInterceptor } from "./notify-interceptor.js";
|
||||
import { buildBeforeAgentStartResult } from "./system-context.js";
|
||||
import { getSessionTodoCompactionBlock } from "../tools/session-todo-tool.js";
|
||||
import {
|
||||
checkToolCallLoop,
|
||||
resetToolCallLoopGuard,
|
||||
|
|
@ -212,7 +212,7 @@ export function registerHooks(pi, ecosystemHandlers = []) {
|
|||
resetMemorySleeper();
|
||||
try {
|
||||
const sid = ctx.sessionManager?.getSessionId?.() ?? "";
|
||||
const sfile = ctx.sessionManager?.getSessionFile?.() ?? "";
|
||||
const _sfile = ctx.sessionManager?.getSessionFile?.() ?? "";
|
||||
if (sid) {
|
||||
process.stderr.write(`[forge] session ${sid.slice(0, 8)}\n`);
|
||||
}
|
||||
|
|
@ -676,9 +676,7 @@ export function registerHooks(pi, ecosystemHandlers = []) {
|
|||
workState.length > 0
|
||||
? `Work in progress: ${workState.join(". ")}.`
|
||||
: "Session compacted. No active work state.";
|
||||
const summary = todoBlock
|
||||
? `${baseSummary}\n\n${todoBlock}`
|
||||
: baseSummary;
|
||||
const summary = todoBlock ? `${baseSummary}\n\n${todoBlock}` : baseSummary;
|
||||
const result = {
|
||||
compaction: {
|
||||
summary,
|
||||
|
|
|
|||
|
|
@ -23,15 +23,14 @@ import {
|
|||
loadProjectSFPreferences,
|
||||
resolveAllSkillReferences,
|
||||
} from "./preferences.js";
|
||||
|
||||
// Re-export from thin shared module so external importers keep working.
|
||||
export {
|
||||
serializePreferencesToFrontmatter,
|
||||
yamlSafeString,
|
||||
} from "./preferences-serializer.js";
|
||||
import {
|
||||
serializePreferencesToFrontmatter,
|
||||
yamlSafeString,
|
||||
} from "./preferences-serializer.js";
|
||||
|
||||
import { serializePreferencesToFrontmatter } from "./preferences-serializer.js";
|
||||
|
||||
/** Extract body content after frontmatter closing delimiter, or null if none. */
|
||||
function extractBodyAfterFrontmatter(content) {
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ export function renderTriageMarkdown(result, sourcePath) {
|
|||
section("Unclear Notes", result.unclear_notes),
|
||||
].join("\n");
|
||||
}
|
||||
function renderEvalJsonl(result) {
|
||||
function _renderEvalJsonl(result) {
|
||||
return (
|
||||
result.eval_candidates
|
||||
.map((item) =>
|
||||
|
|
@ -270,7 +270,7 @@ function detectRecurringPatterns(result) {
|
|||
}
|
||||
return proposals;
|
||||
}
|
||||
function renderSkillProposals(result) {
|
||||
function _renderSkillProposals(result) {
|
||||
const proposals = detectRecurringPatterns(result);
|
||||
if (proposals.length === 0) return "\n";
|
||||
return (
|
||||
|
|
@ -367,7 +367,7 @@ function normalizedItems(result, createdAt) {
|
|||
for (const item of result.unclear_notes) push("unclear_note", item);
|
||||
return items;
|
||||
}
|
||||
function renderNormalizedJsonl(result, createdAt) {
|
||||
function _renderNormalizedJsonl(result, createdAt) {
|
||||
const items = normalizedItems(result, createdAt);
|
||||
return (
|
||||
items
|
||||
|
|
@ -565,7 +565,14 @@ export async function triageTodoDump(basePath, llmCall, options = {}) {
|
|||
insertTriageEval(crypto.randomUUID(), id, item, createdAt);
|
||||
}
|
||||
for (const item of normalizedItems(result, createdAt)) {
|
||||
insertTriageItem(crypto.randomUUID(), id, item.kind, item.content, item.evidence, createdAt);
|
||||
insertTriageItem(
|
||||
crypto.randomUUID(),
|
||||
id,
|
||||
item.kind,
|
||||
item.content,
|
||||
item.evidence,
|
||||
createdAt,
|
||||
);
|
||||
}
|
||||
const skillProposals = detectRecurringPatterns(result);
|
||||
for (const skill of skillProposals) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
setAllExperimentalFlags,
|
||||
setExperimentalFlag,
|
||||
} from "../../experimental.js";
|
||||
import { inferPresetName, resolvePreset } from "../../operating-model.js";
|
||||
import {
|
||||
getGlobalSFPreferencesPath,
|
||||
getProjectSFPreferencesPath,
|
||||
|
|
@ -28,11 +29,6 @@ import { formattedShortcutPair } from "../../shortcut-defs.js";
|
|||
import { deriveState } from "../../state.js";
|
||||
import { writeUokDiagnostics } from "../../uok/diagnostic-synthesis.js";
|
||||
import { projectRoot } from "../context.js";
|
||||
import {
|
||||
SF_MODE_PRESET_NAMES,
|
||||
inferPresetName,
|
||||
resolvePreset,
|
||||
} from "../../operating-model.js";
|
||||
export function showHelp(ctx, args = "") {
|
||||
const summaryLines = [
|
||||
"SF — Singularity Forge\n",
|
||||
|
|
|
|||
|
|
@ -432,8 +432,12 @@ function openMetricsDb(basePath) {
|
|||
)
|
||||
`);
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_metrics_name ON metrics(name)`);
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_metrics_session ON metrics(session_id)`);
|
||||
db.exec(`CREATE INDEX IF NOT EXISTS idx_metrics_name_ts ON metrics(name, timestamp DESC)`);
|
||||
db.exec(
|
||||
`CREATE INDEX IF NOT EXISTS idx_metrics_session ON metrics(session_id)`,
|
||||
);
|
||||
db.exec(
|
||||
`CREATE INDEX IF NOT EXISTS idx_metrics_name_ts ON metrics(name, timestamp DESC)`,
|
||||
);
|
||||
_metricsDb = db;
|
||||
} catch (err) {
|
||||
logWarning("metrics-central", `Failed to open metrics.db: ${err.message}`);
|
||||
|
|
@ -450,7 +454,7 @@ function closeMetricsDb() {
|
|||
_metricsDb = null;
|
||||
}
|
||||
|
||||
function ensureMetricsTable(db) {
|
||||
function _ensureMetricsTable(db) {
|
||||
// no-op — metrics.db is set up by openMetricsDb
|
||||
void db;
|
||||
}
|
||||
|
|
@ -513,11 +517,13 @@ function persistMetricsToDb(registry, sessionId, _ignored) {
|
|||
try {
|
||||
const row = _metricsDb?.prepare("SELECT count(*) as n FROM metrics").get();
|
||||
if (row && row.n > METRICS_DB_ROW_CAP) {
|
||||
_metricsDb.prepare(
|
||||
_metricsDb
|
||||
.prepare(
|
||||
`DELETE FROM metrics WHERE rowid NOT IN (
|
||||
SELECT rowid FROM metrics ORDER BY timestamp DESC LIMIT ${METRICS_DB_ROW_CAP}
|
||||
)`,
|
||||
).run();
|
||||
)
|
||||
.run();
|
||||
}
|
||||
} catch (_) {
|
||||
// swallow — prune failure must never surface to the user
|
||||
|
|
|
|||
|
|
@ -9,21 +9,22 @@
|
|||
* Consumer: index.js → steerableAutonomousExtension(pi) on every startup.
|
||||
*/
|
||||
|
||||
import {
|
||||
handleSteerableModeKey,
|
||||
SteerableAutonomousPanel,
|
||||
} from "./steerable-autonomous-panel.js";
|
||||
import { SF_MODE_PRESET_NAMES, inferPresetName, resolvePreset } from "./operating-model.js";
|
||||
import { getAutoSession } from "./auto/session.js";
|
||||
import { isAutoActive, startAutoDetached } from "./auto.js";
|
||||
import { projectRoot } from "./commands/context.js";
|
||||
import {
|
||||
inferPresetName,
|
||||
resolvePreset,
|
||||
SF_MODE_PRESET_NAMES,
|
||||
} from "./operating-model.js";
|
||||
import { SteerableAutonomousPanel } from "./steerable-autonomous-panel.js";
|
||||
|
||||
export default function steerableAutonomousExtension(api) {
|
||||
let panel = null;
|
||||
let isAutonomousActive = false;
|
||||
|
||||
// Track autonomous mode state
|
||||
api.on("session_start", async (_, ctx) => {
|
||||
api.on("session_start", async (_, _ctx) => {
|
||||
isAutonomousActive = false;
|
||||
if (panel) {
|
||||
panel.hide();
|
||||
|
|
@ -60,7 +61,8 @@ export default function steerableAutonomousExtension(api) {
|
|||
}
|
||||
const current = inferPresetName(s.getMode()) ?? SF_MODE_PRESET_NAMES[0];
|
||||
const idx = SF_MODE_PRESET_NAMES.indexOf(current);
|
||||
const nextName = SF_MODE_PRESET_NAMES[(idx + 1) % SF_MODE_PRESET_NAMES.length];
|
||||
const nextName =
|
||||
SF_MODE_PRESET_NAMES[(idx + 1) % SF_MODE_PRESET_NAMES.length];
|
||||
const preset = resolvePreset(nextName);
|
||||
s.setMode({
|
||||
workMode: preset.workMode,
|
||||
|
|
@ -70,16 +72,14 @@ export default function steerableAutonomousExtension(api) {
|
|||
});
|
||||
ctx.ui.notify(`Mode → ${nextName} (${preset.description})`, "info");
|
||||
} catch {
|
||||
ctx.ui.notify(
|
||||
"Mode: use /mode ask|plan|build",
|
||||
"info",
|
||||
);
|
||||
ctx.ui.notify("Mode: use /mode ask|plan|build", "info");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
api.registerShortcut("ctrl+y", {
|
||||
description: "Toggle YOLO mode (build + autonomous + deep + unrestricted; bypass git prompts). If not running, starts the autonomous loop immediately.",
|
||||
description:
|
||||
"Toggle YOLO mode (build + autonomous + deep + unrestricted; bypass git prompts). If not running, starts the autonomous loop immediately.",
|
||||
handler: async (ctx) => {
|
||||
const session = getAutoSession();
|
||||
// Toggle full-autonomy preset in AutoSession (handles mode slam + restore)
|
||||
|
|
@ -109,7 +109,8 @@ export default function steerableAutonomousExtension(api) {
|
|||
|
||||
// Handle slash command for panel
|
||||
api.registerCommand("steer", {
|
||||
description: "Open steerable autonomous panel (Shift+Tab during autonomous)",
|
||||
description:
|
||||
"Open steerable autonomous panel (Shift+Tab during autonomous)",
|
||||
handler: async (_, ctx) => {
|
||||
if (!isAutonomousActive) {
|
||||
ctx.ui.notify(
|
||||
|
|
@ -132,10 +133,7 @@ export default function steerableAutonomousExtension(api) {
|
|||
// Listen for autonomous mode changes
|
||||
api.on("autonomous_start", async (_, ctx) => {
|
||||
isAutonomousActive = true;
|
||||
ctx.ui.notify(
|
||||
"🤖 Autonomous mode active — Shift+Tab to steer",
|
||||
"info",
|
||||
);
|
||||
ctx.ui.notify("🤖 Autonomous mode active — Shift+Tab to steer", "info");
|
||||
});
|
||||
|
||||
api.on("autonomous_stop", async (_, ctx) => {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
*/
|
||||
|
||||
import { createInterface } from "node:readline";
|
||||
import { getEditorKeybindings } from "@singularity-forge/pi-tui";
|
||||
|
||||
// ─── Constants ──────────────────────────────────────────────────────────────
|
||||
const PANEL_WIDTH = 60;
|
||||
|
|
@ -216,7 +215,7 @@ const ACTION_HANDLERS = {
|
|||
// Would trigger immediate reassessment
|
||||
},
|
||||
|
||||
close: async (ctx) => {
|
||||
close: async (_ctx) => {
|
||||
// Just hide the panel
|
||||
},
|
||||
};
|
||||
|
|
@ -247,7 +246,7 @@ export class SteerableAutonomousPanel {
|
|||
this.render();
|
||||
|
||||
// Set up key listener
|
||||
this.rl.input.on("keypress", (str, key) => {
|
||||
this.rl.input.on("keypress", (_str, key) => {
|
||||
this.handleKeyPress(key);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
import assert from "node:assert/strict";
|
||||
import { test } from "vitest";
|
||||
import {
|
||||
clearRunawayGuardState,
|
||||
evaluateRunawayGuard,
|
||||
resetRunawayGuardState,
|
||||
} from "../auto-runaway-guard.js";
|
||||
|
|
@ -61,7 +60,15 @@ test("progress check returns none regardless of hard-pause conditions", () => {
|
|||
evaluateRunawayGuard(
|
||||
"discuss-milestone",
|
||||
"M001",
|
||||
{ toolCalls: 67, sessionTokens: 1_500_000, elapsedMs: 22 * 60 * 1000, changedFiles: 0, worktreeFingerprint: null, worktreeChangedSinceStart: false, topTools: {} },
|
||||
{
|
||||
toolCalls: 67,
|
||||
sessionTokens: 1_500_000,
|
||||
elapsedMs: 22 * 60 * 1000,
|
||||
changedFiles: 0,
|
||||
worktreeFingerprint: null,
|
||||
worktreeChangedSinceStart: false,
|
||||
topTools: {},
|
||||
},
|
||||
config,
|
||||
now,
|
||||
);
|
||||
|
|
@ -71,12 +78,24 @@ test("progress check returns none regardless of hard-pause conditions", () => {
|
|||
const r = evaluateRunawayGuard(
|
||||
"discuss-milestone",
|
||||
"M001",
|
||||
{ toolCalls: 67, sessionTokens: 2_000_000, elapsedMs: 25 * 60 * 1000, changedFiles: 1, worktreeFingerprint: null, worktreeChangedSinceStart: false, topTools: {} },
|
||||
{
|
||||
toolCalls: 67,
|
||||
sessionTokens: 2_000_000,
|
||||
elapsedMs: 25 * 60 * 1000,
|
||||
changedFiles: 1,
|
||||
worktreeFingerprint: null,
|
||||
worktreeChangedSinceStart: false,
|
||||
topTools: {},
|
||||
},
|
||||
config,
|
||||
now + 180_000,
|
||||
);
|
||||
// The progress check fires BEFORE the hard-pause block, returning 'none'
|
||||
assert.equal(r.action, "none", "progress check should return none even when hardPause conditions are met");
|
||||
assert.equal(
|
||||
r.action,
|
||||
"none",
|
||||
"progress check should return none even when hardPause conditions are met",
|
||||
);
|
||||
});
|
||||
|
||||
test("returns none when changedFiles > 0 despite token growth and 2 warnings", () => {
|
||||
|
|
@ -133,7 +152,11 @@ test("returns none when worktreeChangedSinceStart === true despite token growth"
|
|||
config,
|
||||
now + 180_000,
|
||||
);
|
||||
assert.equal(r.action, "none", "should not pause when worktreeChangedSinceStart === true");
|
||||
assert.equal(
|
||||
r.action,
|
||||
"none",
|
||||
"should not pause when worktreeChangedSinceStart === true",
|
||||
);
|
||||
});
|
||||
|
||||
test("returns none when changedFiles is explicitly 0 but worktreeChangedSinceStart is false", () => {
|
||||
|
|
@ -154,7 +177,11 @@ test("returns none when changedFiles is explicitly 0 but worktreeChangedSinceSta
|
|||
const r = evaluateRunawayGuard(
|
||||
"discuss-milestone",
|
||||
"M001",
|
||||
makeMetrics({ sessionTokens: 2_940_000, changedFiles: 0, worktreeChangedSinceStart: false }),
|
||||
makeMetrics({
|
||||
sessionTokens: 2_940_000,
|
||||
changedFiles: 0,
|
||||
worktreeChangedSinceStart: false,
|
||||
}),
|
||||
config,
|
||||
now + 180_000,
|
||||
);
|
||||
|
|
@ -199,5 +226,9 @@ test("discuss-milestone with file changes does not get hard-paused", () => {
|
|||
config,
|
||||
now + 180_000,
|
||||
);
|
||||
assert.equal(r.action, "none", "discuss-milestone with file changes should not be paused");
|
||||
assert.equal(
|
||||
r.action,
|
||||
"none",
|
||||
"discuss-milestone with file changes should not be paused",
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,18 +12,12 @@ import { mkdtempSync, rmSync } from "node:fs";
|
|||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, describe, expect, test } from "vitest";
|
||||
import { closeDatabase } from "../sf-db.js";
|
||||
import { _clearSfRootCache } from "../paths.js";
|
||||
import { PersistentAgent } from "../uok/persistent-agent.js";
|
||||
import { closeDatabase } from "../sf-db.js";
|
||||
import { AgentSwarm } from "../uok/agent-swarm.js";
|
||||
import {
|
||||
CoordinatorAgent,
|
||||
WorkerAgent,
|
||||
ScoutAgent,
|
||||
ReviewerAgent,
|
||||
createDefaultSwarm,
|
||||
} from "../uok/swarm-roles.js";
|
||||
import { PersistentAgent } from "../uok/persistent-agent.js";
|
||||
import { SwarmDispatchLayer } from "../uok/swarm-dispatch.js";
|
||||
import { createDefaultSwarm } from "../uok/swarm-roles.js";
|
||||
|
||||
// ─── Shared Setup ─────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -86,7 +80,10 @@ describe("PersistentAgent — identity", () => {
|
|||
describe("PersistentAgent — messaging", () => {
|
||||
test("send_receive_delivers_message", () => {
|
||||
const root = makeProject();
|
||||
const agentA = new PersistentAgent(root, { name: "sender", role: "worker" });
|
||||
const agentA = new PersistentAgent(root, {
|
||||
name: "sender",
|
||||
role: "worker",
|
||||
});
|
||||
const agentB = new PersistentAgent(root, {
|
||||
name: "receiver",
|
||||
role: "worker",
|
||||
|
|
@ -132,8 +129,14 @@ describe("PersistentAgent — messaging", () => {
|
|||
name: "broadcaster",
|
||||
role: "coordinator",
|
||||
});
|
||||
const agentB = new PersistentAgent(root, { name: "recv-b", role: "worker" });
|
||||
const agentC = new PersistentAgent(root, { name: "recv-c", role: "worker" });
|
||||
const agentB = new PersistentAgent(root, {
|
||||
name: "recv-b",
|
||||
role: "worker",
|
||||
});
|
||||
const agentC = new PersistentAgent(root, {
|
||||
name: "recv-c",
|
||||
role: "worker",
|
||||
});
|
||||
|
||||
agentA.broadcast("announcement", {}, ["recv-b", "recv-c"]);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ function saveTodos(baseDir, todos) {
|
|||
writeFileSync(todoPath(baseDir), JSON.stringify(todos, null, 2), "utf-8");
|
||||
}
|
||||
|
||||
function nextId(todos) {
|
||||
function nextId(_todos) {
|
||||
// Short base-36 timestamp suffix for readable IDs.
|
||||
return `t${Date.now().toString(36)}`;
|
||||
}
|
||||
|
|
@ -103,9 +103,7 @@ export function executeSessionTodoList(baseDir) {
|
|||
details: { operation: "list", todos: [] },
|
||||
};
|
||||
}
|
||||
const lines = todos.map(
|
||||
(t) => `[${t.done ? "x" : " "}] ${t.id}: ${t.text}`,
|
||||
);
|
||||
const lines = todos.map((t) => `[${t.done ? "x" : " "}] ${t.id}: ${t.text}`);
|
||||
return {
|
||||
content: [{ type: "text", text: lines.join("\n") }],
|
||||
details: { operation: "list", todos },
|
||||
|
|
|
|||
|
|
@ -518,6 +518,9 @@ test("fresh sf --web browser onboarding stays locked on failed validation and un
|
|||
launchCwd: tempProject,
|
||||
tempHome,
|
||||
browserLogPath,
|
||||
// Cap launch at 120s so the remaining budget (~180s+) covers API calls
|
||||
// (valid-key validation triggers a bridge restart = up to 120s itself).
|
||||
timeoutMs: 120_000,
|
||||
env: {
|
||||
SF_WEB_TEST_FAKE_API_KEY_VALIDATION: "1",
|
||||
ANTHROPIC_API_KEY: "",
|
||||
|
|
@ -574,7 +577,7 @@ test("fresh sf --web browser onboarding stays locked on failed validation and un
|
|||
const bootBefore = await fetch(`${launch.url}/api/boot`, {
|
||||
method: "GET",
|
||||
headers: { Accept: "application/json", ...auth },
|
||||
signal: AbortSignal.timeout(10_000),
|
||||
signal: AbortSignal.timeout(30_000),
|
||||
});
|
||||
assert.equal(
|
||||
bootBefore.ok,
|
||||
|
|
@ -598,7 +601,7 @@ test("fresh sf --web browser onboarding stays locked on failed validation and un
|
|||
providerId: "openai",
|
||||
apiKey: "invalid-demo-key",
|
||||
}),
|
||||
signal: AbortSignal.timeout(10_000),
|
||||
signal: AbortSignal.timeout(30_000),
|
||||
});
|
||||
assert.equal(invalidValidation.status, 422);
|
||||
const invalidPayload = (await invalidValidation.json()) as any;
|
||||
|
|
@ -637,7 +640,7 @@ test("fresh sf --web browser onboarding stays locked on failed validation and un
|
|||
const bootAfter = await fetch(`${launch.url}/api/boot`, {
|
||||
method: "GET",
|
||||
headers: { Accept: "application/json", ...auth },
|
||||
signal: AbortSignal.timeout(10_000),
|
||||
signal: AbortSignal.timeout(30_000),
|
||||
});
|
||||
const bootAfterText = await bootAfter.clone().text();
|
||||
assert.equal(
|
||||
|
|
@ -648,4 +651,4 @@ test("fresh sf --web browser onboarding stays locked on failed validation and un
|
|||
const bootAfterPayload = (await bootAfter.json()) as any;
|
||||
assert.equal(bootAfterPayload.onboarding.locked, false);
|
||||
assert.equal(bootAfterPayload.onboarding.lockReason, null);
|
||||
}, 180_000);
|
||||
}, 420_000);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue