@xqli02/mneme 0.1.9 → 0.1.10

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xqli02/mneme",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Three-layer memory architecture for AI coding agents (Ledger + Beads + OpenCode)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,7 +2,7 @@
2
2
  * mneme auto — Dual-agent autonomous supervisor loop.
3
3
  *
4
4
  * Uses two agents in the same opencode session:
5
- * - Planner (default: gpt-5.2): analyzes goal, breaks down tasks, reviews results
5
+ * - Planner (default: gpt-4.1): analyzes goal, breaks down tasks, reviews results
6
6
  * - Executor (default: claude-opus-4.6): writes code, runs commands, implements changes
7
7
  *
8
8
  * The planner and executor alternate turns via per-message model switching.
@@ -35,7 +35,7 @@ import { color, log, run, has } from "../utils.mjs";
35
35
 
36
36
  // ── Default models ──────────────────────────────────────────────────────────
37
37
 
38
- const DEFAULT_PLANNER = "github-copilot/gpt-5.2";
38
+ const DEFAULT_PLANNER = "github-copilot/gpt-4.1";
39
39
  const DEFAULT_EXECUTOR = "github-copilot/claude-opus-4.6";
40
40
 
41
41
  // ── Argument parsing ────────────────────────────────────────────────────────
@@ -584,6 +584,28 @@ async function executeTurn(
584
584
  // Send async — returns immediately
585
585
  await client.session.promptAsync(sessionId, body);
586
586
 
587
+ // Quick check: if model is invalid, the session may error out almost
588
+ // instantly but promptAsync still returns 204. Poll once after a short
589
+ // delay to catch this before entering the long wait loop.
590
+ await sleep(2000);
591
+ try {
592
+ const sessions = await client.session.list();
593
+ const s = sessions?.find?.((ss) => ss.id === sessionId);
594
+ if (s && s.status && s.status !== "running" && s.status !== "pending") {
595
+ // Session already finished — likely an immediate error
596
+ const msgs = await client.session.messages(sessionId);
597
+ const lastMsg = msgs?.[msgs.length - 1];
598
+ const errInfo = lastMsg?.info?.error;
599
+ if (errInfo) {
600
+ const errMsg = errInfo.data?.message || errInfo.name || "unknown";
601
+ log.fail(`Model error: ${errMsg}`);
602
+ return { status: "error", aborted: true, quit: false };
603
+ }
604
+ }
605
+ } catch {
606
+ // Ignore probe failures — fall through to normal wait
607
+ }
608
+
587
609
  const HEARTBEAT_INTERVAL = 15_000; // print elapsed every 15s of silence
588
610
  const SILENCE_WARN = 30_000; // warn after 30s of no output
589
611
  const SILENCE_ABORT = 120_000; // auto-abort after 120s of no output
@@ -684,30 +706,49 @@ async function supervisorLoop(client, opts, inputQueue) {
684
706
  const plannerModel = parseModelSpec(opts.planner);
685
707
  const executorModel = parseModelSpec(opts.executor);
686
708
 
687
- // ── Validate models ──
688
- log.info("Validating models...");
689
- const modelsOutput = run("opencode models 2>/dev/null") || "";
690
- const validateModel = (label, spec) => {
691
- // Look for the model ID in the output; opencode models lists "provider/model"
692
- if (modelsOutput && !modelsOutput.includes(spec)) {
693
- log.fail(`${label} model "${spec}" not found in available models.`);
694
- console.log(color.dim(" Available models:"));
695
- for (const line of modelsOutput.split("\n").filter(l => l.trim())) {
696
- console.log(color.dim(` ${line.trim()}`));
697
- }
698
- throw new Error(`Invalid ${label.toLowerCase()} model: ${spec}`);
699
- }
700
- };
701
- validateModel("Planner", opts.planner);
702
- validateModel("Executor", opts.executor);
703
- log.ok("Models validated");
704
-
705
- // Create session
709
+ // Create session first (needed for model probing)
706
710
  log.info("Creating session...");
707
711
  const session = await client.session.create({ title: "mneme auto" });
708
712
  const sessionId = session.id;
709
713
  log.ok(`Session: ${sessionId}`);
710
714
 
715
+ // ── Validate models by sending a real test prompt ──
716
+ // opencode models lists theoretical models, but the provider may reject them
717
+ // at runtime (e.g. Copilot plan doesn't include gpt-5.x). Only a real API
718
+ // call reveals this — sync prompt returns the error, async silently fails.
719
+ log.info("Validating models (API probe)...");
720
+ const probeModels = [
721
+ { label: "Planner", spec: opts.planner, parsed: plannerModel },
722
+ { label: "Executor", spec: opts.executor, parsed: executorModel },
723
+ ];
724
+ // Deduplicate if both use the same model
725
+ const seen = new Set();
726
+ for (const m of probeModels) {
727
+ if (seen.has(m.spec)) continue;
728
+ seen.add(m.spec);
729
+ try {
730
+ const result = await client.session.prompt(sessionId, {
731
+ parts: [{ type: "text", text: "Say OK" }],
732
+ model: m.parsed,
733
+ });
734
+ // Check if the response contains an error
735
+ const err = result?.info?.error;
736
+ if (err) {
737
+ const msg = err.data?.message || err.name || "unknown error";
738
+ log.fail(`${m.label} model "${m.spec}" rejected by provider: ${msg}`);
739
+ console.log(color.dim(" Tip: run 'opencode models' to see listed models, but not all may be available on your plan."));
740
+ throw new Error(`${m.label} model unavailable: ${msg}`);
741
+ }
742
+ log.ok(`${m.label} model verified: ${m.spec}`);
743
+ } catch (probeErr) {
744
+ if (probeErr.message.includes("unavailable") || probeErr.message.includes("rejected")) {
745
+ throw probeErr; // re-throw our own errors
746
+ }
747
+ // API call itself failed — might be a transient issue
748
+ log.warn(`${m.label} model probe inconclusive: ${probeErr.message}`);
749
+ }
750
+ }
751
+
711
752
  // Start SSE event display
712
753
  const eventDisplay = createEventDisplay(client);
713
754
  eventDisplay.start().catch(() => {});