@m13v/s4l 1.6.202 → 1.6.203-rc.2

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/mcp/dist/index.js CHANGED
@@ -1301,7 +1301,7 @@ tool("engagement_mode", {
1301
1301
  .optional()
1302
1302
  .describe("The RAW voice-memo transcript from the onboarding dictation interview, VERBATIM (do NOT " +
1303
1303
  "paraphrase or summarize). Persisted to the persona_corpus.txt sidecar (never config.json), " +
1304
- "capped ~8000 chars. This is the grounding pool the drafter quotes real specifics from " +
1304
+ "capped ~100000 chars. This is the grounding pool the drafter quotes real specifics from " +
1305
1305
  "(actual projects, numbers, opinions, phrasing), so keep it dense and first-hand."),
1306
1306
  voice: z
1307
1307
  .any()
@@ -1794,70 +1794,52 @@ tool("project_config", {
1794
1794
  }
1795
1795
  // Status / discovery mode: no project name supplied, or explicitly asked.
1796
1796
  if (args.status === true || !args.name) {
1797
- const projects = listManagedProjectStatus();
1798
- const rtReady = runtimeReady();
1799
- // On a bare .mcpb install the runtime step also materializes the pipeline
1800
- // source that xStatus shells into. Status must still work before that first
1801
- // install, otherwise the agent cannot discover that installation is the
1802
- // next milestone. Avoid probing Python until the owned runtime is ready.
1803
- const x = rtReady
1804
- ? await xStatus().catch(() => ({ connected: false, state: "status_unavailable" }))
1805
- : { connected: false, state: "runtime_not_ready" };
1806
- await ensureDoctorPhase(x.connected ? "full" : "pre_connect");
1807
- const ver = await versionStatus();
1808
- const configured = projects.some((p) => p.ready);
1809
- if (rtReady)
1810
- completeOnboardingMilestone("runtime_ready");
1811
- if (x.connected) {
1812
- completeOnboardingMilestone("x_connected", { state: x.state || "connected" });
1813
- }
1814
- if (configured) {
1815
- completeOnboardingMilestone("project_ready", {
1816
- missing_count: 0,
1817
- });
1818
- }
1819
- // mode_chosen completes when the user explicitly picked a mode (mode.json
1820
- // exists) OR this is a legacy install already past setup (a ready product),
1821
- // so adding this step never regresses an already-onboarded box.
1822
- if (modeChosen() || configured) {
1823
- completeOnboardingMilestone("mode_chosen", {
1824
- source: modeChosen() ? "chosen" : "backfilled_legacy",
1825
- });
1826
- }
1797
+ // ONE compute path: buildSnapshot -> scripts/snapshot.py, the same source
1798
+ // the menu bar and browser dashboard read. This branch used to recompute
1799
+ // projects/X/version inline (listManagedProjectStatus + xStatus), which
1800
+ // excluded the persona project and carried no setup_complete so the
1801
+ // panel's refresh() disagreed with the dashboard tool and the menu bar on
1802
+ // persona-only setups. Do not reintroduce a parallel compute here.
1803
+ const snap = await buildSnapshot();
1804
+ const projects = Array.isArray(snap.projects) ? snap.projects : [];
1805
+ const rtReady = !!snap.runtime_ready;
1806
+ const xConnected = !!snap.x_connected;
1807
+ const configured = (snap.projects_ready || 0) > 0;
1827
1808
  return jsonContent({
1828
1809
  configured,
1829
1810
  projects,
1830
1811
  runtime_ready: rtReady,
1831
- x_connected: x.connected,
1832
- x_state: x.state,
1833
- x_handle: x.handle ?? null,
1834
- mcp_version: ver.installed,
1835
- latest_version: ver.latest,
1836
- update_available: ver.update_available,
1837
- mode: currentMode(),
1838
- flags: currentFlags(),
1839
- update_hint: ver.update_available
1840
- ? `A newer version (${ver.latest}) is available — you're on ${ver.installed}. ` +
1812
+ x_connected: xConnected,
1813
+ x_state: snap.x_state,
1814
+ x_handle: snap.x_handle ?? null,
1815
+ setup_complete: !!snap.setup_complete,
1816
+ mcp_version: snap.version,
1817
+ latest_version: snap.latest_version,
1818
+ update_available: !!snap.update_available,
1819
+ mode: snap.mode,
1820
+ flags: snap.flags,
1821
+ update_hint: snap.update_available
1822
+ ? `A newer version (${snap.latest_version}) is available — you're on ${snap.version}. ` +
1841
1823
  `Tell the user and offer to run the \`runtime\` tool with action:'update' ` +
1842
1824
  `(or \`npx social-autoposter@latest update\`).`
1843
1825
  : undefined,
1844
1826
  required_fields: REQUIRED_FIELDS,
1845
1827
  recommended_fields: RECOMMENDED_FIELDS,
1846
1828
  config_path: configPath(),
1847
- ready_for_verification: rtReady && configured && x.connected,
1848
- onboarding: onboardingSnapshot(),
1829
+ ready_for_verification: !!snap.setup_complete,
1830
+ onboarding: snap.onboarding,
1849
1831
  next_step: !rtReady
1850
1832
  ? "Runtime is not ready yet. It provisions automatically on boot — poll runtime action:'status' until ready (only call runtime action:'install' to retry if status shows the boot provision failed or stalled), then continue setup automatically."
1851
1833
  : projects.length === 0
1852
1834
  ? "No projects yet. Discover the product from conversation context and the connected X profile; research its website, infer a conservative complete project, and call project_config. Ask only if no product can be identified." +
1853
- (x.connected ? "" : " X is not connected yet either — detect_x_sources, warn about keychain prompts, then run connect_x with confirm:true without a separate permission turn.")
1835
+ (xConnected ? "" : " X is not connected yet either — detect_x_sources, warn about keychain prompts, then run connect_x with confirm:true without a separate permission turn.")
1854
1836
  : projects.every((p) => p.ready)
1855
- ? (x.connected
1837
+ ? (xConnected
1856
1838
  ? "All configured projects are ready and X is connected. SCHEDULE THE AUTOPILOT: (1) call queue_setup and create each returned task with create_scheduled_task (prompt verbatim; 'already exists' is fine); (2) the autopilot then runs on its own (launchd kicker + queue worker). Call the `dashboard` tool to confirm the schedule is firing (schedule_state 'ok') — that is the terminal step; do NOT wait for or verify a draft card. Do NOT pause to ask the user to review drafts."
1857
1839
  : "All configured projects are ready, but X is NOT connected — posting needs a logged-in " +
1858
1840
  "x.com session. Detect sources and run project_config action:'connect_x', confirm:true; do not ask whether to proceed.")
1859
1841
  : "Some projects are missing required fields (see each project's missing_required). Derive them from config, context, profile_scan, and website research, then call project_config again. Ask only if a required field is genuinely unknowable." +
1860
- (x.connected ? "" : " X is also not connected yet; detect sources and run connect_x with confirm:true."),
1842
+ (xConnected ? "" : " X is also not connected yet; detect sources and run connect_x with confirm:true."),
1861
1843
  });
1862
1844
  }
1863
1845
  // Apply mode (incremental): merge whatever fields were supplied onto the
@@ -2980,6 +2962,12 @@ function kickerEnv() {
2980
2962
  S4L_STATE_DIR: sapsStateDir(),
2981
2963
  TWITTER_TAIL_LINK_RATE: "0",
2982
2964
  TWITTER_PAGE_GEN_RATE: "0",
2965
+ // Rolling virality bar percentile for the draft lane (2026-07-03): the
2966
+ // cycle script now applies the bar to DRAFT_ONLY too, so below-bar picks
2967
+ // never become review cards. p0.90 (not the script's 0.97 default) keeps
2968
+ // card volume useful while cutting bottom-of-pool drafts; cold start
2969
+ // (sample_count < 200) still shows brand-new installs every draft.
2970
+ S4L_TWITTER_VIRALITY_PCTILE: "0.90",
2983
2971
  };
2984
2972
  }
2985
2973
  async function ensureQueueKickerInstalled() {
@@ -3428,6 +3416,29 @@ async function buildSnapshot() {
3428
3416
  completeOnboardingMilestone("project_ready", { missing_count: 0 });
3429
3417
  if (snap.schedule_state === "ok")
3430
3418
  completeOnboardingMilestone("tasks_scheduled");
3419
+ // mode_chosen completes when the user explicitly picked a mode (mode.json
3420
+ // exists) OR this is a legacy install already past setup (a ready project),
3421
+ // so adding this step never regresses an already-onboarded box. (Moved here
3422
+ // from project_config's status branch so ALL milestone truth lives in one
3423
+ // place next to the snapshot.)
3424
+ if (modeChosen() || (snap.projects_ready || 0) > 0) {
3425
+ completeOnboardingMilestone("mode_chosen", {
3426
+ source: modeChosen() ? "chosen" : "backfilled_legacy",
3427
+ });
3428
+ }
3429
+ // profile_scanned has no file signal of its own, but a provisioned persona
3430
+ // project can only come from the scan+dictation flow — treat its presence as
3431
+ // the completed scan so old installs can't stick at "profile pending".
3432
+ if ((Array.isArray(snap.projects) ? snap.projects : []).some((p) => p?.persona)) {
3433
+ completeOnboardingMilestone("profile_scanned", { backfill: true });
3434
+ }
3435
+ // topics_seeded has no cheap live signal (it's a DB fact, not a file fact), so
3436
+ // installs that finished setup before the persona-path completion landed stay
3437
+ // stuck at 7/8 forever. Heal by re-running the idempotent seed for the ready
3438
+ // project(s) and completing the milestone on success. Fire-and-forget: the
3439
+ // ledger flip shows on the next snapshot.
3440
+ if ((snap.projects_ready || 0) > 0)
3441
+ void healTopicsSeeded(snap);
3431
3442
  // Persist this snapshot so the menu bar can answer "set up?" the SAME way when
3432
3443
  // the loopback server is unreachable (Claude Desktop closed or mid-restart)
3433
3444
  // instead of falling back to a divergent local rule. Refreshed on every
@@ -3436,6 +3447,37 @@ async function buildSnapshot() {
3436
3447
  persistStatusSummary(snap);
3437
3448
  return snap;
3438
3449
  }
3450
+ // One attempt per process: a failed seed (offline DB) retries on the next MCP
3451
+ // launch, not on every dashboard poll.
3452
+ let topicsSeedHealAttempted = false;
3453
+ async function healTopicsSeeded(snap) {
3454
+ if (topicsSeedHealAttempted)
3455
+ return;
3456
+ topicsSeedHealAttempted = true;
3457
+ try {
3458
+ const ledger = onboardingLedger();
3459
+ if (ledger?.milestones?.topics_seeded?.status === "complete")
3460
+ return;
3461
+ const ready = (Array.isArray(snap.projects) ? snap.projects : []).filter((p) => p && p.ready && typeof p.name === "string");
3462
+ for (const p of ready) {
3463
+ const seed = await runPython("scripts/seed_search_topics.py", ["--project", p.name], {
3464
+ timeoutMs: 60_000,
3465
+ });
3466
+ if (seed.code === 0) {
3467
+ const m = /planned=(\d+)\s+inserted=(\d+)\s+updated=(\d+)/.exec(seed.stdout);
3468
+ completeOnboardingMilestone("topics_seeded", {
3469
+ project: p.name,
3470
+ topic_count: m ? Number(m[1]) : 0,
3471
+ backfill: true,
3472
+ });
3473
+ return;
3474
+ }
3475
+ }
3476
+ }
3477
+ catch (e) {
3478
+ console.error("[snapshot] topics_seeded heal failed:", e?.message || e);
3479
+ }
3480
+ }
3439
3481
  // ---- dashboard localhost fallback -----------------------------------------
3440
3482
  // When the connected host doesn't support MCP Apps UI (Claude Code / Cowork
3441
3483
  // today), serve the SAME dist/panel.html from a loopback HTTP server. The page