@gamaze/hicortex 0.22.3 → 0.23.1
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/README.md +4 -2
- package/assets/dashboard.html +430 -140
- package/dist/calibration.d.ts +26 -11
- package/dist/calibration.js +33 -13
- package/dist/capture.js +4 -1
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +26 -0
- package/dist/cluster.d.ts +8 -5
- package/dist/cluster.js +3 -7
- package/dist/consolidate.d.ts +4 -64
- package/dist/consolidate.js +15 -285
- package/dist/db.js +21 -0
- package/dist/dedup.d.ts +14 -9
- package/dist/dedup.js +33 -25
- package/dist/distiller.d.ts +18 -0
- package/dist/distiller.js +94 -9
- package/dist/eval/planted-harness.js +1 -1
- package/dist/eval/run-eval.js +2 -4
- package/dist/llm.d.ts +12 -1
- package/dist/llm.js +14 -3
- package/dist/mcp-stdio.d.ts +61 -3
- package/dist/mcp-stdio.js +272 -51
- package/dist/nightly.js +2 -4
- package/dist/recall-hook-cli.js +10 -0
- package/dist/recall-index.js +12 -0
- package/dist/reconsolidation.d.ts +17 -11
- package/dist/reconsolidation.js +38 -28
- package/dist/relink.d.ts +2 -2
- package/dist/relink.js +2 -2
- package/dist/retrieval.d.ts +5 -4
- package/dist/retrieval.js +5 -4
- package/dist/state.d.ts +8 -8
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +2 -2
- package/dist/sweep-volatile.d.ts +55 -0
- package/dist/sweep-volatile.js +184 -0
- package/dist/types.d.ts +26 -28
- package/dist/wrapper-prompt.d.ts +43 -0
- package/dist/wrapper-prompt.js +122 -0
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/distiller.js
CHANGED
|
@@ -10,10 +10,12 @@ exports.extractConversationText = extractConversationText;
|
|
|
10
10
|
exports.distillSession = distillSession;
|
|
11
11
|
exports.isNoExtractResponse = isNoExtractResponse;
|
|
12
12
|
exports.hasMinimalSubstance = hasMinimalSubstance;
|
|
13
|
+
exports.isVolatileStatusEntry = isVolatileStatusEntry;
|
|
13
14
|
exports.typeFromTag = typeFromTag;
|
|
14
15
|
exports.parseDistilledEntries = parseDistilledEntries;
|
|
15
16
|
const prompts_js_1 = require("./prompts.js");
|
|
16
17
|
const redact_js_1 = require("./redact.js");
|
|
18
|
+
const calibration_js_1 = require("./calibration.js");
|
|
17
19
|
const MAX_TRANSCRIPT_CHARS = 80_000;
|
|
18
20
|
const MIN_CONVERSATION_CHARS = 200;
|
|
19
21
|
// #339 (2026-08-24 postmortem): NO_EXTRACT over-firing visibility threshold.
|
|
@@ -403,21 +405,36 @@ async function distillChunk(llm, transcript, projectName, date, onUsage) {
|
|
|
403
405
|
console.log(`[hicortex] topic-first check: ${offTopic}/${parsed.length} entries look actor/bracket-led (prompt may be ignored)`);
|
|
404
406
|
}
|
|
405
407
|
const entries = [];
|
|
406
|
-
const
|
|
408
|
+
const substanceDropped = [];
|
|
409
|
+
const volatileDropped = [];
|
|
407
410
|
for (const entry of parsed) {
|
|
408
|
-
if (hasMinimalSubstance(entry.content)) {
|
|
409
|
-
|
|
411
|
+
if (!hasMinimalSubstance(entry.content)) {
|
|
412
|
+
substanceDropped.push(entry.content);
|
|
413
|
+
continue;
|
|
410
414
|
}
|
|
411
|
-
|
|
412
|
-
|
|
415
|
+
// #489 volatility gate (owner decision 2: entry-level, deterministic,
|
|
416
|
+
// beside the substance gate — cleanMessageContent untouched): GH-ticket /
|
|
417
|
+
// version-bump / commit-state status shapes drop into the SAME #156
|
|
418
|
+
// trail (owner decision 1: mark-and-skip, never silent). Kill-switch =
|
|
419
|
+
// the release-managed calibration constant (owner decision 5).
|
|
420
|
+
if (calibration_js_1.VOLATILE_STATUS_FILTER && isVolatileStatusEntry(entry.content)) {
|
|
421
|
+
volatileDropped.push(entry.content);
|
|
422
|
+
continue;
|
|
413
423
|
}
|
|
424
|
+
entries.push(entry);
|
|
414
425
|
}
|
|
415
|
-
|
|
416
|
-
|
|
426
|
+
const dropped = [...substanceDropped, ...volatileDropped];
|
|
427
|
+
for (const [label, list] of [
|
|
428
|
+
["Substance gate", substanceDropped],
|
|
429
|
+
["Volatility gate", volatileDropped],
|
|
430
|
+
]) {
|
|
431
|
+
if (list.length === 0)
|
|
432
|
+
continue;
|
|
433
|
+
for (const d of list) {
|
|
417
434
|
const preview = d.length > 120 ? `${d.slice(0, 120)}…` : d;
|
|
418
|
-
console.log(`[hicortex]
|
|
435
|
+
console.log(`[hicortex] ${label}: dropped "${preview}"`);
|
|
419
436
|
}
|
|
420
|
-
console.log(`[hicortex]
|
|
437
|
+
console.log(`[hicortex] ${label}: dropped ${list.length}/${parsed.length} entr(ies)`);
|
|
421
438
|
}
|
|
422
439
|
// Parsed-zero bypass (#339 CR finding 2): a non-empty response with no
|
|
423
440
|
// NO_EXTRACT token that still parses to zero bullets is the silent twin of
|
|
@@ -508,6 +525,74 @@ function hasMinimalSubstance(entry) {
|
|
|
508
525
|
return false;
|
|
509
526
|
return true;
|
|
510
527
|
}
|
|
528
|
+
// ---------------------------------------------------------------------------
|
|
529
|
+
// Volatility gate (#489) — deterministic, entry-level, beside the substance
|
|
530
|
+
// gate. Owner directive (gold-set A adjudication): GitHub ticket/workflow
|
|
531
|
+
// states and version numbers churn faster than nightly consolidation retires
|
|
532
|
+
// them — they arrive as supersession noise and inflate the store with
|
|
533
|
+
// same-subject rows. The ephemera PROMPT already names these categories and
|
|
534
|
+
// demonstrably under-fires (a month of it running, gold A still full of
|
|
535
|
+
// GH-status/version rows) — so the filter is deterministic, catching entries
|
|
536
|
+
// synthesized from ANY source prose.
|
|
537
|
+
// ---------------------------------------------------------------------------
|
|
538
|
+
/**
|
|
539
|
+
* Durability escapes — decision/policy predicates that OVERRIDE every
|
|
540
|
+
* volatility trigger (#489 spec): "switched from X to Y" is a durable model
|
|
541
|
+
* choice even though it names two versions; a version boundary is a policy
|
|
542
|
+
* even though it cites one. Whole-word, case-insensitive. Version numbers
|
|
543
|
+
* never trigger alone — version + STATUS-verb triggers; version +
|
|
544
|
+
* DECISION-verb escapes.
|
|
545
|
+
*/
|
|
546
|
+
const VOLATILE_ESCAPES = /\b(?:switch(?:ed)?\s+from|adopt(?:ed)?|standardi[sz]ed|deprecated|polic(?:y|ies)|rule|boundary|convention|must|never|always|only\s+when)\b/i;
|
|
547
|
+
/** T1 — GH ticket/workflow status: a #number ticket + a status predicate, or
|
|
548
|
+
* a workflow-run shape (checks/CI outcome, test-count status). */
|
|
549
|
+
const TICKET_REF = /(?:^|[\s(#])(?:pr|issue|epic)?\s*#\d+/i;
|
|
550
|
+
const TICKET_STATUS = /\b(?:merged?|closed?|reopened?|opened?|approved?|blocked?|failing|pass(?:ed|ing)?|green|red|ready|draft)\b/i;
|
|
551
|
+
const WORKFLOW_STATUS = /\b(?:checks?\s+(?:passed|failed|green|red)|ci\s+(?:green|red)|\d+\s+tests?\s+(?:pass(?:ed|ing)?|fail(?:ed|ing)?))\b/i;
|
|
552
|
+
/** T2 — version-bump status: a semver-ish number + a release verb, or a
|
|
553
|
+
* version→version transition. The `\b` boundary keeps "Qwen3.6" (no
|
|
554
|
+
* boundary between n and 3) from matching — model names are not versions. */
|
|
555
|
+
const SEMVERISH = /\bv?\d+\.\d+(?:\.\d+)?\b/;
|
|
556
|
+
const RELEASE_VERB = /\b(?:released?|deployed?|promoted?|published?|tagged?|bumped?|cut|shipped?|rolled?\s+out|dist-tags?)\b/i;
|
|
557
|
+
const VERSION_TRANSITION = /\bv?\d+\.\d+(?:\.\d+)?\b[^.\d]{0,20}(?:→|->|to)\s*v?\d+\.\d+(?:\.\d+)?\b/i;
|
|
558
|
+
/** T3 — branch/commit state: a short sha (7-40 hex, word-bounded) + a
|
|
559
|
+
* vcs-motion verb. The SHA is the discriminator, so the verb list is bare
|
|
560
|
+
* ("rebased feature branch onto 4f9c1ab" must fire); a durable narrative
|
|
561
|
+
* carrying a sha + verb rides the escape list or the length cap. */
|
|
562
|
+
const SHORT_SHA = /\b[0-9a-f]{7,40}\b/;
|
|
563
|
+
const VCS_STATE = /\b(?:pushed?|merged?|rebased?|force-?pushed?|updated?)\b/i;
|
|
564
|
+
/**
|
|
565
|
+
* True when the entry is a VOLATILE STATUS SHAPE — GH-ticket/workflow status,
|
|
566
|
+
* version-bump status, or branch/commit state — and carries no durability
|
|
567
|
+
* escape (#489, owner decisions 1+2). Runs in distillChunk's gate zone beside
|
|
568
|
+
* hasMinimalSubstance; drops ride the SAME #156 dropped[] trail. Also reused
|
|
569
|
+
* verbatim by `hicortex sweep-volatile` over the stored corpus — ONE gate,
|
|
570
|
+
* one meaning.
|
|
571
|
+
*
|
|
572
|
+
* PRECISION OVER RECALL (the substance gate's law, inherited): escapes are
|
|
573
|
+
* checked FIRST and override every trigger; entries longer than
|
|
574
|
+
* VOLATILE_GATE_MAX_CHARS are treated as mixed prose whose status clause is
|
|
575
|
+
* not the DOMINANT content (kept). Accepted false-positive mode, stated
|
|
576
|
+
* plainly: an entry pairing a status clause with a distinct durable clause
|
|
577
|
+
* and no escape word drops, losing the durable half — unless the distiller
|
|
578
|
+
* emitted that half as its own entry, which is exactly what the ephemera
|
|
579
|
+
* prompt tells it to do. Every drop is auditable via the trail.
|
|
580
|
+
*/
|
|
581
|
+
function isVolatileStatusEntry(entry) {
|
|
582
|
+
const raw = entry.trim();
|
|
583
|
+
if (!raw || raw.length > calibration_js_1.VOLATILE_GATE_MAX_CHARS)
|
|
584
|
+
return false;
|
|
585
|
+
if (VOLATILE_ESCAPES.test(raw))
|
|
586
|
+
return false;
|
|
587
|
+
const ticketStatus = TICKET_REF.test(raw) && TICKET_STATUS.test(raw);
|
|
588
|
+
const workflowStatus = WORKFLOW_STATUS.test(raw);
|
|
589
|
+
// A version number + release verb, OR a bare version→version transition
|
|
590
|
+
// (the transition fires without a verb — "0.22.2 → 0.22.3 on rc" is a
|
|
591
|
+
// version-bump status; the escape list carries the durable flips).
|
|
592
|
+
const versionBump = (SEMVERISH.test(raw) && RELEASE_VERB.test(raw)) || VERSION_TRANSITION.test(raw);
|
|
593
|
+
const commitState = SHORT_SHA.test(raw) && VCS_STATE.test(raw);
|
|
594
|
+
return ticketStatus || workflowStatus || versionBump || commitState;
|
|
595
|
+
}
|
|
511
596
|
/**
|
|
512
597
|
* Map a single-letter type tag to the stored memory_type. Unknown/absent →
|
|
513
598
|
* experience (the pre-#216 default). `[L]` is explicitly rejected →
|
|
@@ -644,7 +644,7 @@ function renderPlantedReport(args) {
|
|
|
644
644
|
`- conflicts (guard-C): flagged ${s.conflict_flagged}, skipped ${s.conflict_skipped} ` +
|
|
645
645
|
`(zone clusters + judged merges refused on a conflicts link)\n` +
|
|
646
646
|
`- zone: clusters_found ${s.merges.clusters_found}, merged ${s.merges.merged_clusters}, ` +
|
|
647
|
-
`losers_merged ${s.merges.losers_merged},
|
|
647
|
+
`losers_merged ${s.merges.losers_merged}, project_skipped ${s.merges.skipped_project_mismatch}, ` +
|
|
648
648
|
`conflict_skipped ${s.merges.skipped_conflict}\n` +
|
|
649
649
|
`- skipped: infra ${s.skipped_infra}, idempotent ${s.skipped_idempotent}, above_ceiling ${s.skipped_above_ceiling}\n`);
|
|
650
650
|
return L.join("\n");
|
package/dist/eval/run-eval.js
CHANGED
|
@@ -66,11 +66,9 @@ function renderDups(d) {
|
|
|
66
66
|
`${d.pairAttribution.recoveryReingest} recovery/re-ingest-suspect (${pct(d.pairAttribution.totalPairs > 0 ? d.pairAttribution.recoveryReingest / d.pairAttribution.totalPairs : 0)}), ${d.pairAttribution.organic} organic (${pct(d.pairAttribution.totalPairs > 0 ? d.pairAttribution.organic / d.pairAttribution.totalPairs : 0)}).\n`);
|
|
67
67
|
lines.push(`### Top ${d.topClusters.length} clusters (0.90 threshold, largest first)\n`);
|
|
68
68
|
d.topClusters.forEach((cluster, i) => {
|
|
69
|
+
// #206 decision 2: project is the only merge-safety rail (agent rail removed).
|
|
69
70
|
const mismatch = cluster.metadataMismatch;
|
|
70
|
-
const mismatchFlags = [
|
|
71
|
-
mismatch.projectMismatch ? "project" : null,
|
|
72
|
-
mismatch.sourceAgentMismatch ? "source_agent" : null,
|
|
73
|
-
].filter(Boolean);
|
|
71
|
+
const mismatchFlags = [mismatch.projectMismatch ? "project" : null].filter(Boolean);
|
|
74
72
|
lines.push(`**Cluster ${i + 1}** — size ${cluster.size}, attribution: ${cluster.attribution.recoveryReingest} recovery-pair(s) / ${cluster.attribution.organic} organic-pair(s)` +
|
|
75
73
|
(mismatchFlags.length > 0 ? `, metadata mismatch: ${mismatchFlags.join(", ")}` : ", metadata consistent"));
|
|
76
74
|
for (const m of cluster.members) {
|
package/dist/llm.d.ts
CHANGED
|
@@ -237,9 +237,20 @@ export declare class LlmClient {
|
|
|
237
237
|
private acquireFlightGuard;
|
|
238
238
|
private dispatchOnce;
|
|
239
239
|
/**
|
|
240
|
-
* Claude CLI:
|
|
240
|
+
* Claude CLI: invoke `claude -p` for subscription users.
|
|
241
241
|
* No API key needed — uses CC's authenticated session.
|
|
242
242
|
*
|
|
243
|
+
* Invocation contract (#512): the binary is called with a plain argument
|
|
244
|
+
* array and NO shell; the prompt travels on the child's stdin (execFileSync
|
|
245
|
+
* pipes stdin when `input` is set — the `< /dev/null` of the old shell
|
|
246
|
+
* command line is gone with the shell). The prompt is transcript-derived
|
|
247
|
+
* data, so it must reach the binary as bytes, never as command-line text
|
|
248
|
+
* an intermediary could interpret; argv stays exactly the fixed flag set.
|
|
249
|
+
* Side effects: stdin delivery lifts the per-argument exec limit on long
|
|
250
|
+
* transcripts, and a claudePath containing spaces works (one argv element,
|
|
251
|
+
* never re-parsed). cwd is left unset on purpose — the child inherits
|
|
252
|
+
* process.cwd() like every other phase of the daemon.
|
|
253
|
+
*
|
|
243
254
|
* Token usage (#246): the claude CLI JSON output does not carry a token
|
|
244
255
|
* usage field, so this path returns `usage: undefined`. The CLI is billed
|
|
245
256
|
* by Claude subscription, not per-token — there is nothing to meter. The
|
package/dist/llm.js
CHANGED
|
@@ -570,9 +570,20 @@ class LlmClient {
|
|
|
570
570
|
return this.completeOpenAiCompat(model, prompt, maxTokens, timeoutMs);
|
|
571
571
|
}
|
|
572
572
|
/**
|
|
573
|
-
* Claude CLI:
|
|
573
|
+
* Claude CLI: invoke `claude -p` for subscription users.
|
|
574
574
|
* No API key needed — uses CC's authenticated session.
|
|
575
575
|
*
|
|
576
|
+
* Invocation contract (#512): the binary is called with a plain argument
|
|
577
|
+
* array and NO shell; the prompt travels on the child's stdin (execFileSync
|
|
578
|
+
* pipes stdin when `input` is set — the `< /dev/null` of the old shell
|
|
579
|
+
* command line is gone with the shell). The prompt is transcript-derived
|
|
580
|
+
* data, so it must reach the binary as bytes, never as command-line text
|
|
581
|
+
* an intermediary could interpret; argv stays exactly the fixed flag set.
|
|
582
|
+
* Side effects: stdin delivery lifts the per-argument exec limit on long
|
|
583
|
+
* transcripts, and a claudePath containing spaces works (one argv element,
|
|
584
|
+
* never re-parsed). cwd is left unset on purpose — the child inherits
|
|
585
|
+
* process.cwd() like every other phase of the daemon.
|
|
586
|
+
*
|
|
576
587
|
* Token usage (#246): the claude CLI JSON output does not carry a token
|
|
577
588
|
* usage field, so this path returns `usage: undefined`. The CLI is billed
|
|
578
589
|
* by Claude subscription, not per-token — there is nothing to meter. The
|
|
@@ -580,10 +591,10 @@ class LlmClient {
|
|
|
580
591
|
* correct outcome (no meterable cost to defend against).
|
|
581
592
|
*/
|
|
582
593
|
async completeClaude(model, prompt, timeoutMs) {
|
|
583
|
-
const {
|
|
594
|
+
const { execFileSync } = require("node:child_process");
|
|
584
595
|
const claudePath = this.config.baseUrl; // baseUrl stores the claude binary path
|
|
585
596
|
try {
|
|
586
|
-
const raw =
|
|
597
|
+
const raw = execFileSync(claudePath, ["-p", "--model", model, "--max-turns", "1", "--output-format", "json", "--no-session-persistence"], { encoding: "utf-8", timeout: timeoutMs, maxBuffer: 10 * 1024 * 1024, input: prompt });
|
|
587
598
|
const data = JSON.parse(raw);
|
|
588
599
|
if (data.is_error) {
|
|
589
600
|
throw new Error(`Claude CLI error: ${data.result}`);
|
package/dist/mcp-stdio.d.ts
CHANGED
|
@@ -42,6 +42,27 @@
|
|
|
42
42
|
* port: explicit error, never a spawn. A remote target that is down is
|
|
43
43
|
* likewise an explicit error — we never spawn for remote URLs.
|
|
44
44
|
*
|
|
45
|
+
* Startup retry (#501): a REMOTE target that is unreachable at launch is
|
|
46
|
+
* TRANSIENT, not fatal — the product case is a client (e.g. Claude Desktop
|
|
47
|
+
* auto-launched at login) starting before the VPN/DNS that carries the
|
|
48
|
+
* server URL is up (ENOTFOUND/EAI_AGAIN/ECONNREFUSED/timeouts). The bridge
|
|
49
|
+
* then keeps the stdio side ALIVE and answers `initialize` IMMEDIATELY
|
|
50
|
+
* (design B), retrying the upstream connect with backoff (1s→2s→4s… capped
|
|
51
|
+
* 10s) for a 60s window. Why answer immediately: MCP clients cancel a
|
|
52
|
+
* pending `initialize` at ~60s (TS SDK DEFAULT_REQUEST_TIMEOUT_MSEC;
|
|
53
|
+
* Claude Desktop observed cancelling at ~60s in the wild) — a delayed
|
|
54
|
+
* initialize would lose the session the retry window is meant to save, and
|
|
55
|
+
* the first tools/list request carries the same ~60s client budget, so the
|
|
56
|
+
* window deliberately stays at the BOTTOM of the 60–90s range the issue
|
|
57
|
+
* proposed (evidence + decision: issue #501 design-note comment). The
|
|
58
|
+
* daemon's initialize-result `instructions` are unknowable while it is down
|
|
59
|
+
* and are therefore omitted on this path (the pre-#383 shape); tools
|
|
60
|
+
* handlers await upstream readiness. NEVER retried: 401/403 (auth is not
|
|
61
|
+
* transient — existing HICORTEX_AUTH_TOKEN hint) and a reachable-but-not-
|
|
62
|
+
* healthy endpoint (foreign service). Local targets keep the autostart poll
|
|
63
|
+
* (which already waits 30s). Mid-session SSE reconnect after an established
|
|
64
|
+
* connection drops is OUT OF SCOPE (#501 follow-up).
|
|
65
|
+
*
|
|
45
66
|
* STDIO DISCIPLINE: stdout carries ONLY the MCP protocol. Every diagnostic
|
|
46
67
|
* goes to stderr; fatal errors are a one-liner on stderr + non-zero exit
|
|
47
68
|
* (thrown to cli.ts's catch). Cancellation downstream→upstream rides the
|
|
@@ -51,6 +72,7 @@
|
|
|
51
72
|
* upstream request id (a verbatim forward would carry the downstream id,
|
|
52
73
|
* which means nothing to the daemon) — and reject the in-flight bridge call.
|
|
53
74
|
*/
|
|
75
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
54
76
|
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
55
77
|
/** Where the bridge target came from — surfaces in the startup diagnostic. */
|
|
56
78
|
export type BridgeTargetSource = "option" | "env" | "config" | "default";
|
|
@@ -122,6 +144,16 @@ export interface EnsureDaemonOptions {
|
|
|
122
144
|
* every fail-path (explicit error, never silent degradation).
|
|
123
145
|
*/
|
|
124
146
|
export declare function ensureDaemonReady(target: BridgeTarget, options?: EnsureDaemonOptions): Promise<void>;
|
|
147
|
+
export type StartupFailureKind = "auth" | "foreign" | "transient" | "fatal";
|
|
148
|
+
/**
|
|
149
|
+
* Classify a startup failure of the upstream connect sequence. Only a REMOTE
|
|
150
|
+
* target's network-level failure is transient (#501); auth rejections and a
|
|
151
|
+
* reachable-but-unhealthy endpoint are fatal immediately, and local targets
|
|
152
|
+
* keep their own autostart-poll semantics.
|
|
153
|
+
*/
|
|
154
|
+
export declare function classifyStartupFailure(err: unknown, target: BridgeTarget): StartupFailureKind;
|
|
155
|
+
/** Backoff step N (0-based): base·2^N, capped — 1s, 2s, 4s, 8s, then the cap. */
|
|
156
|
+
export declare function nextRetryDelayMs(attempt: number, baseMs: number, maxMs: number): number;
|
|
125
157
|
export interface McpStdioOptions extends EnsureDaemonOptions {
|
|
126
158
|
/** Explicit target URL (test seam; normally resolved from env/config). */
|
|
127
159
|
serverUrl?: string;
|
|
@@ -129,10 +161,36 @@ export interface McpStdioOptions extends EnsureDaemonOptions {
|
|
|
129
161
|
authToken?: string;
|
|
130
162
|
/** Injectable downstream transport (test seam; default: real stdio). */
|
|
131
163
|
downstream?: Transport;
|
|
164
|
+
/** Injectable upstream connect (test seam; default: real SSE transport). */
|
|
165
|
+
connectUpstream?: (target: BridgeTarget, token: string | undefined) => Promise<Client>;
|
|
166
|
+
/** Retry pacing for the #501 transient-unreachable window (test seams). */
|
|
167
|
+
retryWindowMs?: number;
|
|
168
|
+
retryBaseDelayMs?: number;
|
|
169
|
+
retryMaxDelayMs?: number;
|
|
132
170
|
}
|
|
133
171
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
172
|
+
* Connect the upstream Client to the daemon's SSE MCP endpoint. requestInit
|
|
173
|
+
* headers ride BOTH the GET /sse and the POST /messages (SDK 1.28
|
|
174
|
+
* _commonHeaders/send). Raw errors propagate — classification happens at the
|
|
175
|
+
* call site. Exported for the ghost-reconnect unit test.
|
|
176
|
+
*
|
|
177
|
+
* On failure the transport is closed EXPLICITLY: the SDK's Client.connect
|
|
178
|
+
* closes only when the initialize REQUEST fails after a successful start —
|
|
179
|
+
* a failed transport.start() (refused/DNS) propagates out of Protocol.connect
|
|
180
|
+
* with no cleanup, and the still-open EventSource keeps eventsource's ~3s
|
|
181
|
+
* reconnect loop alive. Every retry attempt would leak one ghost that, once
|
|
182
|
+
* the server appears, opens a REAL authed SSE session on the daemon and is
|
|
183
|
+
* never closed (proven empirically on SDK 1.28.0 / eventsource 3.0.7, PR
|
|
184
|
+
* review round 1 — pinned by the ghost-reconnect unit test).
|
|
185
|
+
*/
|
|
186
|
+
export declare function defaultConnectUpstream(target: BridgeTarget, token: string | undefined): Promise<Client>;
|
|
187
|
+
/**
|
|
188
|
+
* Run the stdio MCP bridge. Fast path (daemon reachable now): connect
|
|
189
|
+
* upstream first, then serve stdio with the daemon's forwarded instructions
|
|
190
|
+
* — exactly the pre-#501 sequence. Slow path (REMOTE target, transient
|
|
191
|
+
* network failure — the boot race): serve stdio IMMEDIATELY (design B,
|
|
192
|
+
* initialize answered at once) and retry the upstream connect with backoff
|
|
193
|
+
* for the retry window. Resolves once bridging is established; every setup
|
|
194
|
+
* failure throws for cli.ts to report on stderr and exit 1.
|
|
137
195
|
*/
|
|
138
196
|
export declare function runMcpStdio(options?: McpStdioOptions): Promise<void>;
|