indusagi-coding-agent 0.2.8 → 0.2.9
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 +2 -1
- package/src/_decls/entry.ts +18 -0
- package/src/_decls/guardrails.ts +35 -0
- package/src/_decls/index.ts +26 -0
- package/src/addons/contract.ts +236 -0
- package/src/addons/dispatch/event-dispatcher.ts +164 -0
- package/src/addons/dispatch/index.ts +25 -0
- package/src/addons/dispatch/tool-interceptor.ts +208 -0
- package/src/addons/host.ts +225 -0
- package/src/addons/index.ts +112 -0
- package/src/addons/manifest.ts +158 -0
- package/src/addons/sandbox.ts +170 -0
- package/src/addons/surface.ts +78 -0
- package/src/boot/auth-vault.ts +195 -0
- package/src/boot/boot.ts +138 -0
- package/src/boot/contract.ts +238 -0
- package/src/boot/heap.ts +59 -0
- package/src/boot/index.ts +28 -0
- package/src/boot/invocation.ts +93 -0
- package/src/boot/runners/addon-wiring.ts +153 -0
- package/src/boot/runners/checkpoint.ts +169 -0
- package/src/boot/runners/delegate-runner.ts +294 -0
- package/src/boot/runners/index.ts +13 -0
- package/src/boot/runners/link-runner.ts +45 -0
- package/src/boot/runners/memdir.ts +168 -0
- package/src/boot/runners/oneshot-runner.ts +58 -0
- package/src/boot/runners/read-state.ts +90 -0
- package/src/boot/runners/registry.ts +42 -0
- package/src/boot/runners/repl-runner.ts +143 -0
- package/src/boot/runners/server-mode.ts +121 -0
- package/src/boot/runners/session.ts +641 -0
- package/src/boot/server-token.ts +148 -0
- package/src/boot/stages.ts +167 -0
- package/src/boot/upgrade/apply.ts +94 -0
- package/src/boot/upgrade/index.ts +13 -0
- package/src/boot/upgrade/upgrades.ts +289 -0
- package/src/briefing/compose.ts +150 -0
- package/src/briefing/context-docs.ts +19 -0
- package/src/briefing/contract.ts +717 -0
- package/src/briefing/index.ts +31 -0
- package/src/briefing/macros.ts +97 -0
- package/src/briefing/skills.ts +47 -0
- package/src/capability-deck/bridge-ledger/index.ts +27 -0
- package/src/capability-deck/bridge-ledger/key.ts +67 -0
- package/src/capability-deck/bridge-ledger/ledger.ts +131 -0
- package/src/capability-deck/bridge-ledger/network.ts +117 -0
- package/src/capability-deck/builtin-bridge.ts +312 -0
- package/src/capability-deck/cards/bg-process-card.ts +335 -0
- package/src/capability-deck/cards/index.ts +115 -0
- package/src/capability-deck/cards/memory-card.ts +146 -0
- package/src/capability-deck/cards/plan-file.ts +97 -0
- package/src/capability-deck/cards/plan-tools.ts +185 -0
- package/src/capability-deck/cards/saas-card.ts +183 -0
- package/src/capability-deck/cards/task-card.ts +207 -0
- package/src/capability-deck/cards/todo-card.ts +168 -0
- package/src/capability-deck/cards/workflow-card.ts +247 -0
- package/src/capability-deck/contract.ts +388 -0
- package/src/capability-deck/index.ts +48 -0
- package/src/capability-deck/manifest.ts +109 -0
- package/src/capability-deck/provision.ts +169 -0
- package/src/channels/contract.ts +191 -0
- package/src/channels/framer.ts +50 -0
- package/src/channels/index.ts +101 -0
- package/src/channels/link/dialog.ts +129 -0
- package/src/channels/link/driver.ts +190 -0
- package/src/channels/link/index.ts +34 -0
- package/src/channels/link/server.ts +134 -0
- package/src/channels/oneshot.ts +90 -0
- package/src/channels/ops.ts +65 -0
- package/src/channels/session-ops.ts +81 -0
- package/src/conductor/bash-guard.ts +599 -0
- package/src/conductor/catalog/catalog.ts +116 -0
- package/src/conductor/catalog/index.ts +8 -0
- package/src/conductor/catalog/matcher.ts +134 -0
- package/src/conductor/conductor.ts +234 -0
- package/src/conductor/contract.ts +842 -0
- package/src/conductor/diagnostics.ts +227 -0
- package/src/conductor/index.ts +33 -0
- package/src/conductor/permissions.ts +588 -0
- package/src/conductor/quota-error.ts +49 -0
- package/src/conductor/signal-hub/hub.ts +46 -0
- package/src/conductor/signal-hub/index.ts +2 -0
- package/src/conductor/signal-hub/translate.test.ts +81 -0
- package/src/conductor/signal-hub/translate.ts +74 -0
- package/src/conductor/skill-parse/index.ts +2 -0
- package/src/conductor/skill-parse/parse.ts +108 -0
- package/src/conductor/transcript-store/index.ts +22 -0
- package/src/conductor/transcript-store/serialize.ts +116 -0
- package/src/conductor/transcript-store/store.ts +205 -0
- package/src/console/auth-status.ts +56 -0
- package/src/console/components/AgentsView.ts +165 -0
- package/src/console/components/BackgroundAgents.ts +155 -0
- package/src/console/components/Banner.ts +334 -0
- package/src/console/components/Composer.ts +94 -0
- package/src/console/components/StatusBar.ts +49 -0
- package/src/console/components/TerminalConsole.ts +1090 -0
- package/src/console/components/WorkingIndicator.ts +98 -0
- package/src/console/components/banner-sweep.ts +24 -0
- package/src/console/components/welcome.ts +74 -0
- package/src/console/contract.ts +630 -0
- package/src/console/index.ts +34 -0
- package/src/console/input/complete.ts +127 -0
- package/src/console/input/dir-reader.ts +34 -0
- package/src/console/input/index.ts +23 -0
- package/src/console/input/keymap.ts +159 -0
- package/src/console/input/paste.ts +104 -0
- package/src/console/mount.ts +56 -0
- package/src/console/overlays/approval-queue.ts +57 -0
- package/src/console/overlays/approval.ts +130 -0
- package/src/console/overlays/auth.ts +342 -0
- package/src/console/overlays/boards.ts +308 -0
- package/src/console/overlays/host.ts +36 -0
- package/src/console/overlays/index.ts +26 -0
- package/src/console/overlays/pickers.ts +258 -0
- package/src/console/overlays/sessions.ts +190 -0
- package/src/console/reducer.ts +182 -0
- package/src/console/slash/builtins.ts +81 -0
- package/src/console/slash/commands/dynamic.ts +83 -0
- package/src/console/slash/commands/integrations.ts +695 -0
- package/src/console/slash/commands/shared.ts +75 -0
- package/src/console/slash/commands/transcript.ts +263 -0
- package/src/console/slash/commands/workbench.ts +246 -0
- package/src/console/slash/index.ts +15 -0
- package/src/console/slash/registry.ts +70 -0
- package/src/console/slash/resolve.ts +63 -0
- package/src/console/startup.ts +209 -0
- package/src/console/theme/adapter.ts +45 -0
- package/src/console/theme/index.ts +7 -0
- package/src/console/theme/palette.ts +68 -0
- package/src/console/theme/resolve.ts +39 -0
- package/src/console/theme/tokens.ts +71 -0
- package/src/entry.ts +55 -0
- package/src/guardrails.ts +37 -0
- package/src/index.ts +18 -0
- package/src/insight/channel.ts +88 -0
- package/src/insight/contract.ts +185 -0
- package/src/insight/index.ts +110 -0
- package/src/insight/recorder.ts +213 -0
- package/src/insight/redaction.ts +157 -0
- package/src/insight/replay.ts +158 -0
- package/src/insight/sampling.ts +70 -0
- package/src/insight/serialize.ts +50 -0
- package/src/insight/sinks/console.ts +64 -0
- package/src/insight/sinks/file.ts +40 -0
- package/src/insight/sinks/index.ts +24 -0
- package/src/insight/sinks/stream.ts +54 -0
- package/src/integrations/sarvam/attach.ts +239 -0
- package/src/integrations/sarvam/config.ts +156 -0
- package/src/integrations/sarvam/index.ts +25 -0
- package/src/integrations/sarvam/sarvam.test.ts +60 -0
- package/src/integrations/sarvam/types.ts +27 -0
- package/src/integrations/zoho/attach.ts +342 -0
- package/src/integrations/zoho/config.ts +125 -0
- package/src/integrations/zoho/index.ts +27 -0
- package/src/integrations/zoho/types.ts +21 -0
- package/src/integrations/zoho/zoho.test.ts +50 -0
- package/src/kit/clipboard-image.ts +107 -0
- package/src/kit/external-editor.ts +48 -0
- package/src/kit/image.ts +59 -0
- package/src/kit/index.ts +51 -0
- package/src/kit/shell.ts +19 -0
- package/src/kit/tool-fetch.ts +85 -0
- package/src/launch/catalog.ts +148 -0
- package/src/launch/contract.ts +187 -0
- package/src/launch/credentials.ts +625 -0
- package/src/launch/index.ts +98 -0
- package/src/launch/invocation/attachments.ts +179 -0
- package/src/launch/invocation/flags.ts +196 -0
- package/src/launch/invocation/index.ts +25 -0
- package/src/launch/invocation/read.ts +260 -0
- package/src/launch/invocation/usage.ts +67 -0
- package/src/launch/login.ts +324 -0
- package/src/launch/oauth.test.ts +18 -0
- package/src/launch/oauth.ts +203 -0
- package/src/launch/packages.ts +194 -0
- package/src/launch/pickers.ts +189 -0
- package/src/runtime-bridge/bridges/_drive.ts +96 -0
- package/src/runtime-bridge/bridges/builtins.ts +68 -0
- package/src/runtime-bridge/bridges/claude-cli.ts +123 -0
- package/src/runtime-bridge/bridges/codex-cli.ts +142 -0
- package/src/runtime-bridge/bridges/index.ts +33 -0
- package/src/runtime-bridge/bridges/indusagi-cli.ts +155 -0
- package/src/runtime-bridge/broker.ts +227 -0
- package/src/runtime-bridge/contract.ts +122 -0
- package/src/runtime-bridge/index.ts +79 -0
- package/src/runtime-bridge/sink.ts +180 -0
- package/src/sessions/contract.ts +81 -0
- package/src/sessions/index.ts +13 -0
- package/src/sessions/library.ts +229 -0
- package/src/settings/contract.ts +114 -0
- package/src/settings/index.ts +32 -0
- package/src/settings/manager.ts +117 -0
- package/src/transcript-export/index.ts +45 -0
- package/src/transcript-export/publish.ts +260 -0
- package/src/transcript-export/sgr.ts +315 -0
- package/src/transcript-export/template.ts +272 -0
- package/src/transcript-export/theme-bridge.ts +150 -0
- package/src/window-budget/budget/estimate.ts +135 -0
- package/src/window-budget/budget/gate.ts +33 -0
- package/src/window-budget/budget/index.ts +16 -0
- package/src/window-budget/budget/slice.ts +56 -0
- package/src/window-budget/condenser.ts +58 -0
- package/src/window-budget/contract.ts +184 -0
- package/src/window-budget/index.ts +19 -0
- package/src/window-budget/microcompact.ts +95 -0
- package/src/window-budget/rehydrate.ts +136 -0
- package/src/window-budget/summarize/condense.ts +103 -0
- package/src/window-budget/summarize/index.ts +14 -0
- package/src/window-budget/summarize/prompt.ts +149 -0
- package/src/workflow-engine/agent-runner.ts +181 -0
- package/src/workflow-engine/display.ts +224 -0
- package/src/workflow-engine/engine.ts +294 -0
- package/src/workflow-engine/index.ts +23 -0
- package/src/workflow-engine/parse.ts +172 -0
- package/src/workflow-engine/structured-output.ts +35 -0
- package/src/workspace/brand.ts +29 -0
- package/src/workspace/index.ts +18 -0
- package/src/workspace/locator.ts +103 -0
- package/src/workspace/runtime-detect.ts +64 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Type declarations recovered from dist/types (no runtime body)
|
|
3
|
+
/**
|
|
4
|
+
* Briefing subsystem — public barrel.
|
|
5
|
+
*
|
|
6
|
+
* Re-exports the FROZEN prompt-and-presentation contract: the declarative
|
|
7
|
+
* system-prompt pipeline ({@link BriefingSection} over a {@link BriefingContext},
|
|
8
|
+
* composed by `composeBriefing`), the single-pass `$arg` macro model
|
|
9
|
+
* ({@link Macro}, {@link MacroScope}, {@link MacroToken}), the Agent-Skills
|
|
10
|
+
* capability cards ({@link SkillCard} parsed from a `SKILL.md`), the table-driven
|
|
11
|
+
* SGR machine ({@link SgrState}, {@link SgrToken}, {@link SgrMutation}), and the
|
|
12
|
+
* HTML-transcript color layer ({@link ExportTheme} behind a {@link ThemeBridge}
|
|
13
|
+
* with a {@link LuminanceLut}) plus the publish surface.
|
|
14
|
+
*
|
|
15
|
+
* Behavior modules land in this barrel as they are written. Currently exported:
|
|
16
|
+
* the declarative section composer (`composeBriefing` over `BRIEFING_SECTIONS`),
|
|
17
|
+
* the single-pass `$arg` macro scanner + loader (`loadMacros`, `applyMacros`,
|
|
18
|
+
* and friends), and the Agent-Skills capability-card loader (`loadSkillCards`,
|
|
19
|
+
* `gatherSkillCards`). The SGR painter, theme bridge, and transcript publisher
|
|
20
|
+
* are added here as they land. Consumers import the briefing surface from
|
|
21
|
+
* `src/briefing` rather than reaching into individual modules.
|
|
22
|
+
*/
|
|
23
|
+
export type { BriefingContext, BriefingSection, Briefing, BriefingInputs, SubagentBrief, ContextDoc, MacroOrigin, Macro, MacroScope, MacroTokenKind, MacroToken, SkillFrontmatter, SkillCard, SkillOutcomeKind, SkillDiagnostic, SkillLoad, SgrState, SgrToken, SgrMutation, ExportTheme, Rgb, LuminanceLut, ThemeMode, ThemeBridge, TranscriptPart, WidgetRender, PublishOptions, ShellSlot, BriefingFaultKind, BriefingFault, AgentState, AgentTool, TextContent, ImageContent, } from "./contract.js";
|
|
24
|
+
export { SKILL_NAME_LIMIT, SKILL_DESCRIPTION_LIMIT, SGR_INITIAL_STATE, FALLBACK_EXPORT_THEME, SHELL_SLOTS, briefingFault, } from "./contract.js";
|
|
25
|
+
export { BRIEFING_SECTIONS, composeBriefing, joinBlocks, bullets, escapeXml, TOOL_SUMMARIES } from "./compose.js";
|
|
26
|
+
export { scanMacroBody, resolveTokens, applyMacros, buildMacroScope, expandInvocation, loadMacros, readMacroFile, splitFrontmatter, } from "./macros.js";
|
|
27
|
+
export type { LoadMacrosOptions, FrontmatterSplit } from "./macros.js";
|
|
28
|
+
export { loadSkillCards, gatherSkillCards, modelInvocableCards, } from "./skills.js";
|
|
29
|
+
export type { SkillRoot } from "./skills.js";
|
|
30
|
+
export { gatherContextDocs } from "./context-docs.js";
|
|
31
|
+
export type { GatherContextDocsOptions } from "./context-docs.js";
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, join } from "node:path";
|
|
3
|
+
import { briefingFault } from "./contract.js";
|
|
4
|
+
import type { Macro, MacroOrigin, MacroScope, MacroToken } from "./contract.js";
|
|
5
|
+
|
|
6
|
+
export interface LoadMacrosOptions { readonly origin?: MacroOrigin; readonly label?: string; }
|
|
7
|
+
export interface FrontmatterSplit { readonly frontmatter: Record<string, unknown>; readonly body: string; }
|
|
8
|
+
|
|
9
|
+
const decimal = (ch: string): boolean => ch.charCodeAt(0) >= 48 && ch.charCodeAt(0) <= 57;
|
|
10
|
+
const bareword = (ch: string): boolean => { const c = ch.charCodeAt(0); return (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || c === 95; };
|
|
11
|
+
export const LEGACY_ALL_BAREWORD = ["A", "R", "G", "U", "M", "E", "N", "T", "S"].join("");
|
|
12
|
+
export const DERIVED_DESCRIPTION_BUDGET = 72;
|
|
13
|
+
let legacyReporter: ((kind: string, source: string) => void) | undefined;
|
|
14
|
+
|
|
15
|
+
export function setLegacyMacroReporter(fn: ((kind: string, source: string) => void) | undefined): void { legacyReporter = fn; }
|
|
16
|
+
export function reportLegacyUsage(kind: string, source: string): void { legacyReporter?.(kind, source); }
|
|
17
|
+
export function readNumber(text: string, i: number): { value: number; next: number } { let j = i; while (j < text.length && decimal(text[j])) j++; return { value: Number.parseInt(text.slice(i, j), 10), next: j }; }
|
|
18
|
+
export function readBareword(text: string, i: number): number { let j = i; while (j < text.length && bareword(text[j])) j++; return j; }
|
|
19
|
+
export function skipInlineSpace(text: string, i: number): number { let j = i; while (j < text.length && (text[j] === " " || text[j] === "\t")) j++; return j; }
|
|
20
|
+
|
|
21
|
+
type Scan = { token: MacroToken; next: number };
|
|
22
|
+
type LegacyScan = { token: MacroToken | null; literal?: string; next: number };
|
|
23
|
+
|
|
24
|
+
export function scanMacroBody(body: string): MacroToken[] {
|
|
25
|
+
const tokens: MacroToken[] = []; let literal = ""; let i = 0;
|
|
26
|
+
const flush = (): void => { if (literal) { tokens.push({ kind: "literal", text: literal }); literal = ""; } };
|
|
27
|
+
while (i < body.length) {
|
|
28
|
+
const ch = body[i];
|
|
29
|
+
if (ch === "{" && body[i + 1] === "{") {
|
|
30
|
+
if (body[i + 2] === "{" && body[i + 3] === "{") { literal += "{{"; i += 4; continue; }
|
|
31
|
+
const parsed = scanIndusForm(body, i);
|
|
32
|
+
if (parsed) { flush(); tokens.push(parsed.token); i = parsed.next; continue; }
|
|
33
|
+
} else if (ch === "$") {
|
|
34
|
+
const parsed = scanLegacyForm(body, i);
|
|
35
|
+
if (parsed) { flush(); if (parsed.token) tokens.push(parsed.token); else literal += parsed.literal ?? ""; i = parsed.next; continue; }
|
|
36
|
+
}
|
|
37
|
+
literal += ch; i++;
|
|
38
|
+
}
|
|
39
|
+
flush(); return tokens;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function scanIndusForm(text: string, start: number): Scan | null {
|
|
43
|
+
let i = skipInlineSpace(text, start + 2); if (text.slice(i, i + 4) !== "arg.") return null; i += 4;
|
|
44
|
+
const end = readBareword(text, i); const accessor = text.slice(i, end); i = end;
|
|
45
|
+
if (!accessor && decimal(text[i] ?? "")) { const num = readNumber(text, i); i = skipInlineSpace(text, num.next); return text[i] === "}" && text[i + 1] === "}" && num.value >= 1 ? { token: { kind: "positional", index: num.value }, next: i + 2 } : null; }
|
|
46
|
+
if (accessor === "all") { i = skipInlineSpace(text, i); return text[i] === "}" && text[i + 1] === "}" ? { token: { kind: "all" }, next: i + 2 } : null; }
|
|
47
|
+
if (accessor === "slice") {
|
|
48
|
+
i = skipInlineSpace(text, i); if (!decimal(text[i] ?? "")) return null; const startNum = readNumber(text, i); i = skipInlineSpace(text, startNum.next); let length: number | undefined;
|
|
49
|
+
if (decimal(text[i] ?? "")) { const len = readNumber(text, i); length = len.value; i = skipInlineSpace(text, len.next); }
|
|
50
|
+
if (text[i] !== "}" || text[i + 1] !== "}") return null;
|
|
51
|
+
return { token: length === undefined ? { kind: "slice", start: startNum.value } : { kind: "slice", start: startNum.value, length }, next: i + 2 };
|
|
52
|
+
}
|
|
53
|
+
if (accessor === "rest") { i = skipInlineSpace(text, i); let value = 2; if (decimal(text[i] ?? "")) { const num = readNumber(text, i); value = num.value; i = skipInlineSpace(text, num.next); } return text[i] === "}" && text[i + 1] === "}" ? { token: { kind: "slice", start: value }, next: i + 2 } : null; }
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function scanLegacyForm(text: string, start: number): LegacyScan | null {
|
|
58
|
+
const next = text[start + 1];
|
|
59
|
+
if (next === "$") return { token: null, literal: "$", next: start + 2 };
|
|
60
|
+
if (next && decimal(next)) { const num = readNumber(text, start + 1); if (num.value < 1) return null; reportLegacyUsage("positional", text.slice(start, num.next)); return { token: { kind: "positional", index: num.value }, next: num.next }; }
|
|
61
|
+
if (next === "@") { reportLegacyUsage("all", text.slice(start, start + 2)); return { token: { kind: "all" }, next: start + 2 }; }
|
|
62
|
+
if (next === "{") return scanLegacyBraceForm(text, start);
|
|
63
|
+
if (next && bareword(next)) { const end = readBareword(text, start + 1); if (text.slice(start + 1, end) === LEGACY_ALL_BAREWORD) { reportLegacyUsage("all", text.slice(start, end)); return { token: { kind: "all" }, next: end }; } }
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function scanLegacyBraceForm(text: string, start: number): LegacyScan | null {
|
|
68
|
+
let i = start + 2; if (text[i++] !== "@") return null;
|
|
69
|
+
if (text[i] === "}") { reportLegacyUsage("all", text.slice(start, i + 1)); return { token: { kind: "all" }, next: i + 1 }; }
|
|
70
|
+
if (text[i++] !== ":" || !decimal(text[i] ?? "")) return null; const startNum = readNumber(text, i); i = startNum.next; let length: number | undefined;
|
|
71
|
+
if (text[i] === ":") { i++; if (!decimal(text[i] ?? "")) return null; const len = readNumber(text, i); length = len.value; i = len.next; }
|
|
72
|
+
if (text[i] !== "}") return null; reportLegacyUsage("slice", text.slice(start, i + 1));
|
|
73
|
+
return { token: length === undefined ? { kind: "slice", start: startNum.value } : { kind: "slice", start: startNum.value, length }, next: i + 1 };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function splitArguments(raw: string): string[] {
|
|
77
|
+
const out: string[] = []; let current = ""; let inWord = false; let quote: string | null = null;
|
|
78
|
+
for (const ch of raw) { if (quote) { if (ch === quote) quote = null; else current += ch; inWord = true; } else if (ch === '"' || ch === "'") { quote = ch; inWord = true; } else if (/\s/.test(ch)) { if (inWord) { out.push(current); current = ""; inWord = false; } } else { current += ch; inWord = true; } }
|
|
79
|
+
if (inWord) out.push(current); return out;
|
|
80
|
+
}
|
|
81
|
+
export function buildMacroScope(raw: string): MacroScope { const args = splitArguments(raw); return { args, all: args.join(" "), raw }; }
|
|
82
|
+
export function resolveTokens(tokens: readonly MacroToken[], scope: MacroScope): string { return tokens.map((token) => { if (token.kind === "literal") return token.text; if (token.kind === "positional") return scope.args[token.index - 1] ?? ""; if (token.kind === "all") return scope.all; const from = Math.max(0, token.start - 1); return (token.length === undefined ? scope.args.slice(from) : scope.args.slice(from, from + Math.max(0, token.length))).join(" "); }).join(""); }
|
|
83
|
+
export function applyMacros(body: string, raw: string): string { return resolveTokens(scanMacroBody(body), buildMacroScope(raw)); }
|
|
84
|
+
export function firstSpaceIndex(value: string): number { for (let i = 0; i < value.length; i++) if (/\s/.test(value[i])) return i; return -1; }
|
|
85
|
+
export function expandInvocation(line: string, macros: readonly Macro[]): string { if (!line.startsWith("/")) return line; const sliced = line.slice(1); const space = firstSpaceIndex(sliced); const name = space < 0 ? sliced : sliced.slice(0, space); const macro = macros.find((item) => item.name === name); return macro ? applyMacros(macro.body, space < 0 ? "" : sliced.slice(space + 1)) : line; }
|
|
86
|
+
|
|
87
|
+
export function loadMacros(dir: string, opts: LoadMacrosOptions = {}): Macro[] {
|
|
88
|
+
const origin = opts.origin ?? "path"; const label = opts.label ?? origin; let entries: string[];
|
|
89
|
+
try { entries = readdirSync(dir); } catch { return []; }
|
|
90
|
+
const seen = new Set<string>(); const macros: Macro[] = [];
|
|
91
|
+
for (const entry of entries.sort()) { if (entry.startsWith(".") || !entry.toLowerCase().endsWith(".md")) continue; const path = join(dir, entry); try { if (!statSync(path).isFile()) continue; } catch { continue; } const macro = readMacroFile(path, origin, label); if (!seen.has(macro.name)) { seen.add(macro.name); macros.push(macro); } }
|
|
92
|
+
return macros;
|
|
93
|
+
}
|
|
94
|
+
export function readMacroFile(path: string, origin: MacroOrigin, label: string): Macro { let text: string; try { text = readFileSync(path, "utf8"); } catch (cause) { throw briefingFault("macro_invalid", `could not read macro file at ${path}`, cause); } const { frontmatter, body } = splitFrontmatter(text); const name = basename(path).replace(/\.md$/i, ""); const declared = typeof frontmatter.description === "string" ? frontmatter.description.trim() : ""; return { name, description: declared ? `${declared} (${label})` : deriveDescription(body, label), body, origin, source: path }; }
|
|
95
|
+
export function deriveDescription(body: string, label: string): string { const first = body.split("\n").find((line) => line.trim())?.trim() ?? ""; const trimmed = first.length > DERIVED_DESCRIPTION_BUDGET ? `${first.slice(0, DERIVED_DESCRIPTION_BUDGET).trimEnd()}…` : first; return `${trimmed || "custom macro"} (${label})`; }
|
|
96
|
+
export function splitFrontmatter(text: string): FrontmatterSplit { const lines = text.split("\n"); if (lines[0]?.trim() !== "---") return { frontmatter: {}, body: text }; const close = lines.findIndex((line, i) => i > 0 && line.trim() === "---"); if (close < 0) return { frontmatter: {}, body: text }; const frontmatter: Record<string, unknown> = {}; for (const line of lines.slice(1, close)) { if (!line.trim() || line.trimStart().startsWith("#")) continue; const colon = line.indexOf(":"); if (colon > 0) frontmatter[line.slice(0, colon).trim()] = unquoteScalar(line.slice(colon + 1).trim()); } return { frontmatter, body: lines.slice(close + 1).join("\n").replace(/^\n+/, "") }; }
|
|
97
|
+
export function unquoteScalar(value: string): string { if (value.length >= 2 && ((value[0] === '"' && value.at(-1) === '"') || (value[0] === "'" && value.at(-1) === "'"))) return value.slice(1, -1); return value; }
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
|
+
import { SKILL_DESCRIPTION_LIMIT, SKILL_NAME_LIMIT } from "./contract.js";
|
|
4
|
+
import type { MacroOrigin, SkillCard, SkillDiagnostic, SkillFrontmatter, SkillLoad } from "./contract.js";
|
|
5
|
+
import { splitFrontmatter } from "./macros.js";
|
|
6
|
+
|
|
7
|
+
export interface SkillRoot { readonly dir: string; readonly origin: MacroOrigin; }
|
|
8
|
+
interface Candidate { path: string; dirName: string; }
|
|
9
|
+
|
|
10
|
+
export const SKILL_MANIFEST = "SKILL.md";
|
|
11
|
+
export const PRUNED_DIRS = new Set(["node_modules", ".git"]);
|
|
12
|
+
export const KNOWN_FRONTMATTER_KEYS = new Set(["name", "description", "license", "compatibility", "metadata", "allowed-tools", "disable-model-invocation"]);
|
|
13
|
+
|
|
14
|
+
function* walkCandidates(root: string, seen: Set<string>): Generator<Candidate> { yield* walkLevel(root, true, seen); }
|
|
15
|
+
function* walkLevel(dir: string, atRoot: boolean, seen: Set<string>): Generator<Candidate> {
|
|
16
|
+
let entries: string[]; try { entries = readdirSync(dir).sort(); } catch { return; }
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
if (entry.startsWith(".")) continue; const full = join(dir, entry); let stats: import("node:fs").Stats;
|
|
19
|
+
try { stats = statSync(full); } catch { continue; }
|
|
20
|
+
if (stats.isDirectory()) { if (PRUNED_DIRS.has(entry)) continue; let real: string; try { real = realpathSync(full); } catch { continue; } if (!seen.has(real)) { seen.add(real); yield* walkLevel(full, false, seen); } }
|
|
21
|
+
else if (stats.isFile() && ((atRoot && entry.toLowerCase().endsWith(".md")) || (!atRoot && entry === SKILL_MANIFEST))) yield { path: full, dirName: basename(dirname(full)) };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function checkName(name: string, dirName: string, fromDir: boolean): string | null { if (!name) return "a skill must declare a non-empty name"; if (name.length > SKILL_NAME_LIMIT) return `the name exceeds the ${SKILL_NAME_LIMIT}-character limit`; if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) return "the name must be lowercase words joined by single hyphens"; if (!fromDir && name !== dirName) return `the declared name "${name}" does not match its directory "${dirName}"`; return null; }
|
|
26
|
+
export function checkDescription(description: string): string | null { if (!description) return "a skill must declare a description so the model can gate it"; return description.length > SKILL_DESCRIPTION_LIMIT ? `the description exceeds the ${SKILL_DESCRIPTION_LIMIT}-character limit` : null; }
|
|
27
|
+
export function checkFrontmatterKeys(frontmatter: Record<string, unknown>): string | null { for (const key of Object.keys(frontmatter)) if (!KNOWN_FRONTMATTER_KEYS.has(key)) return `unrecognised frontmatter key "${key}"`; return null; }
|
|
28
|
+
export function projectFrontmatter(raw: Record<string, unknown>): SkillFrontmatter {
|
|
29
|
+
const out: { name?: string; description?: string; license?: string; compatibility?: string; metadata?: Readonly<Record<string, unknown>>; allowedTools?: readonly string[]; disableModelInvocation?: boolean } = {};
|
|
30
|
+
for (const key of ["name", "description", "license", "compatibility"] as const) if (typeof raw[key] === "string") out[key] = raw[key].trim();
|
|
31
|
+
if (typeof raw.metadata === "object" && raw.metadata !== null) out.metadata = raw.metadata as Record<string, unknown>;
|
|
32
|
+
const tools = raw["allowed-tools"]; if (typeof tools === "string") { const list = tools.split(/[\s,]+/).filter(Boolean); if (list.length) out.allowedTools = list; } else if (Array.isArray(tools)) out.allowedTools = tools.map(String);
|
|
33
|
+
if (raw["disable-model-invocation"] === true || raw["disable-model-invocation"] === "true") out.disableModelInvocation = true;
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
export function diag(kind: SkillDiagnostic["kind"], location: string, detail: string): SkillDiagnostic { return { kind, location, detail }; }
|
|
37
|
+
export function describeCause(cause: unknown): string { return cause instanceof Error ? cause.message : String(cause); }
|
|
38
|
+
export function parseCandidate(candidate: Candidate, origin: MacroOrigin): { card: SkillCard | null; diagnostic: SkillDiagnostic } {
|
|
39
|
+
let text: string; try { text = readFileSync(candidate.path, "utf8"); } catch (cause) { return { card: null, diagnostic: diag("invalid", candidate.path, `could not read the skill file (${describeCause(cause)})`) }; }
|
|
40
|
+
const { frontmatter, body } = splitFrontmatter(text); const keyProblem = checkFrontmatterKeys(frontmatter); if (keyProblem) return { card: null, diagnostic: diag("invalid", candidate.path, keyProblem) };
|
|
41
|
+
const fm = projectFrontmatter(frontmatter); const fromDir = !(fm.name ?? ""); const name = fromDir ? candidate.dirName : fm.name!; const nameProblem = checkName(name, candidate.dirName, fromDir); if (nameProblem) return { card: null, diagnostic: diag("invalid", candidate.path, nameProblem) };
|
|
42
|
+
const description = fm.description ?? ""; const descriptionProblem = checkDescription(description); if (descriptionProblem) return { card: null, diagnostic: diag("invalid", candidate.path, descriptionProblem) };
|
|
43
|
+
return { card: { name, description, body, location: candidate.path, origin, frontmatter: fm }, diagnostic: diag("loaded", candidate.path, `loaded skill "${name}"`) };
|
|
44
|
+
}
|
|
45
|
+
export function loadSkillCards(dir: string, origin: MacroOrigin = "path"): SkillLoad { const cards: SkillCard[] = []; const diagnostics: SkillDiagnostic[] = []; for (const candidate of walkCandidates(dir, new Set())) { const parsed = parseCandidate(candidate, origin); diagnostics.push(parsed.diagnostic); if (parsed.card) cards.push(parsed.card); } return { cards, diagnostics }; }
|
|
46
|
+
export function gatherSkillCards(roots: readonly SkillRoot[]): SkillLoad { const cards: SkillCard[] = []; const diagnostics: SkillDiagnostic[] = []; const claimed = new Map<string, string>(); for (const root of roots) { const loaded = loadSkillCards(root.dir, root.origin); diagnostics.push(...loaded.diagnostics.filter((item: SkillDiagnostic) => item.kind !== "loaded")); for (const card of loaded.cards) { const winner = claimed.get(card.name); if (winner) diagnostics.push(diag("collision", card.location, `the name "${card.name}" was already claimed by ${winner}; this card is dropped`)); else { claimed.set(card.name, card.location); cards.push(card); diagnostics.push(diag("loaded", card.location, `loaded skill "${card.name}"`)); } } } return { cards, diagnostics }; }
|
|
47
|
+
export function modelInvocableCards(cards: readonly SkillCard[]): SkillCard[] { return cards.filter((card) => card.frontmatter.disableModelInvocation !== true); }
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Type declarations recovered from dist/types (no runtime body)
|
|
3
|
+
/**
|
|
4
|
+
* Bridge-ledger — public barrel for event-sourced MCP tool enrollment.
|
|
5
|
+
*
|
|
6
|
+
* Three concerns, one import site:
|
|
7
|
+
*
|
|
8
|
+
* - **Keys** (`./key`) — content-addressed and ULID {@link BridgeKey} minting,
|
|
9
|
+
* so re-enrolling the same external tool is an idempotent upsert.
|
|
10
|
+
* - **Ledger** (`./ledger`) — the immutable {@link BridgeLedger} value and its
|
|
11
|
+
* pure transitions: {@link enrollBridgeCard} (upsert), {@link retire} /
|
|
12
|
+
* {@link withdrawServer} (splice), each returning a new ledger folded by the
|
|
13
|
+
* contract's {@link reduceLedger}.
|
|
14
|
+
* - **Network** (`./network`) — {@link attachBridgeCapabilities}, which mounts
|
|
15
|
+
* external MCP servers through the framework's `mountProtocolBridge`, adapts
|
|
16
|
+
* the returned `ToolBox` into {@link Capability} objects + {@link
|
|
17
|
+
* CapabilityCard}s, and enrolls them into a ledger.
|
|
18
|
+
*
|
|
19
|
+
* The frozen shapes ({@link BridgeKey}, {@link BridgeEntry}, {@link
|
|
20
|
+
* LedgerSnapshot}, {@link reduceLedger}) continue to live in the deck contract;
|
|
21
|
+
* this module re-exports the behavior that operates on them.
|
|
22
|
+
*/
|
|
23
|
+
export { qualifyBridgeName, bridgeContentKey, bridgeUlidKey, } from "./key.js";
|
|
24
|
+
export type { BridgeLedger, EnrollRequest } from "./ledger.js";
|
|
25
|
+
export { emptyBridgeLedger, bridgeLedgerFromLog, enrollBridgeCard, retire, withdrawServer, liveCapabilities, liveCapabilitiesForServer, } from "./ledger.js";
|
|
26
|
+
export type { AttachResult } from "./network.js";
|
|
27
|
+
export { attachBridgeCapabilities, detachBridge, bridgeBoxToCapabilities, bridgeCapabilityCard, bridgeConfig, } from "./network.js";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Type declarations recovered from dist/types (no runtime body)
|
|
3
|
+
/**
|
|
4
|
+
* Bridge-key minting — stable identifiers for enrolled MCP capabilities.
|
|
5
|
+
*
|
|
6
|
+
* An enrolled bridge capability needs a key that is *the same* across sessions
|
|
7
|
+
* for the same external tool, so re-enrolling is an idempotent upsert rather
|
|
8
|
+
* than an accumulating duplicate. The catalog already settled this policy in the
|
|
9
|
+
* contract: a {@link BridgeKey} is "a content hash of the qualified
|
|
10
|
+
* `<server>__<tool>` name (and its schema) or a fresh ULID" — keyed by the
|
|
11
|
+
* capability's *identity*, never by a working-directory digest.
|
|
12
|
+
*
|
|
13
|
+
* This module implements both halves of that policy and nothing else:
|
|
14
|
+
*
|
|
15
|
+
* - {@link bridgeContentKey} — the default. A deterministic digest of the
|
|
16
|
+
* qualified name plus the canonicalized parameter schema. Two enrollments of
|
|
17
|
+
* the same remote tool (same name, same schema) collapse to one key, so the
|
|
18
|
+
* ledger upsert is naturally idempotent and the live set never duplicates a
|
|
19
|
+
* tool just because a server reconnected.
|
|
20
|
+
* - {@link bridgeUlidKey} — an opt-in, monotonic, time-sortable ULID for the
|
|
21
|
+
* rare case a caller wants every enrollment to be a distinct event (e.g. an
|
|
22
|
+
* ephemeral, per-invocation graft) rather than a deduplicated identity.
|
|
23
|
+
*
|
|
24
|
+
* Both return a branded {@link BridgeKey}; neither touches the filesystem, the
|
|
25
|
+
* clock-as-state, or any shared mutable value.
|
|
26
|
+
*/
|
|
27
|
+
import type { TSchema } from "@sinclair/typebox";
|
|
28
|
+
import { type BridgeKey } from "../contract.js";
|
|
29
|
+
/**
|
|
30
|
+
* The qualified, collision-free name the model sees for a remote tool —
|
|
31
|
+
* `"<server>__<tool>"`, using the framework's bridge {@link QUALIFIER}.
|
|
32
|
+
*
|
|
33
|
+
* The protocol bridge already stamps this exact form onto each grafted tool's
|
|
34
|
+
* descriptor; we recompute it here only so a key can be minted *before* a
|
|
35
|
+
* descriptor is in hand (e.g. from a {@link RemoteToolRef}-shaped pair).
|
|
36
|
+
*
|
|
37
|
+
* @param server the owning MCP server's id
|
|
38
|
+
* @param tool the remote tool's own (unqualified) name
|
|
39
|
+
*/
|
|
40
|
+
export declare function qualifyBridgeName(server: string, tool: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Mint a content-addressed {@link BridgeKey} from a remote tool's identity.
|
|
43
|
+
*
|
|
44
|
+
* The digest is taken over the qualified `<server>__<tool>` name and the
|
|
45
|
+
* canonicalized parameter schema, so the key is a pure function of *what the
|
|
46
|
+
* tool is*. Enrolling the same tool twice yields the same key — the ledger
|
|
47
|
+
* upsert deduplicates it — while a tool whose schema changed yields a new key,
|
|
48
|
+
* correctly surfacing it as a distinct capability.
|
|
49
|
+
*
|
|
50
|
+
* @param server the owning MCP server's id
|
|
51
|
+
* @param tool the remote tool's own (unqualified) name
|
|
52
|
+
* @param parameters the tool's parameter schema (TypeBox or raw JSON Schema)
|
|
53
|
+
*/
|
|
54
|
+
export declare function bridgeContentKey(server: string, tool: string, parameters?: TSchema | Record<string, unknown>): BridgeKey;
|
|
55
|
+
/**
|
|
56
|
+
* Mint a fresh, time-sortable {@link BridgeKey} as a ULID.
|
|
57
|
+
*
|
|
58
|
+
* Use when each enrollment must be a *distinct* event rather than a deduplicated
|
|
59
|
+
* identity — for instance an ephemeral, per-invocation graft, or when two
|
|
60
|
+
* differently-configured connections to the same server should each get their
|
|
61
|
+
* own live entry. The ULID is monotonic within a process so ordering is stable.
|
|
62
|
+
*
|
|
63
|
+
* @param monotonic when true (default) use the process-monotonic generator so
|
|
64
|
+
* keys minted in the same millisecond remain strictly increasing; pass false
|
|
65
|
+
* for a plain, independent ULID.
|
|
66
|
+
*/
|
|
67
|
+
export declare function bridgeUlidKey(monotonic?: boolean): BridgeKey;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Type declarations recovered from dist/types (no runtime body)
|
|
3
|
+
/**
|
|
4
|
+
* The immutable bridge ledger — event-sourced enrollment of MCP capabilities.
|
|
5
|
+
*
|
|
6
|
+
* MCP tools are grafted in and pulled out over a session's life: a server
|
|
7
|
+
* connects and contributes a handful of tools, another disconnects, the same
|
|
8
|
+
* server reconnects after a hot-reload. Rather than mutate a shared module-level
|
|
9
|
+
* array of live tools (which makes "who's enrolled right now" a function of call
|
|
10
|
+
* order and hides every past change), enrollment here is an **append-only event
|
|
11
|
+
* log** folded into a **derived snapshot**.
|
|
12
|
+
*
|
|
13
|
+
* - {@link BridgeLedger} is a plain immutable value: the ordered
|
|
14
|
+
* {@link BridgeEntry} log, its already-folded {@link LedgerSnapshot}, and the
|
|
15
|
+
* next sequence number to stamp.
|
|
16
|
+
* - Every mutation ({@link enrollBridgeCard}, {@link retire},
|
|
17
|
+
* {@link withdrawServer}) returns a *new* ledger; the input is never touched.
|
|
18
|
+
* `enroll` is an upsert (re-enrolling a key replaces its entry), `retire` is
|
|
19
|
+
* a splice (the keyed entry is removed from the live view).
|
|
20
|
+
* - The live view is produced solely by the contract's pure
|
|
21
|
+
* {@link reduceLedger} fold, so the snapshot can never drift from the log.
|
|
22
|
+
*
|
|
23
|
+
* The log is the source of truth; the snapshot is a cache of its fold. Because
|
|
24
|
+
* both live on the value, callers read `ledger.snapshot.live` directly and never
|
|
25
|
+
* re-reduce by hand.
|
|
26
|
+
*/
|
|
27
|
+
import { type AnyCapability, type BridgeEntry, type BridgeKey, type LedgerSnapshot } from "../contract.js";
|
|
28
|
+
/**
|
|
29
|
+
* An immutable, event-sourced view of MCP tool enrollment.
|
|
30
|
+
*
|
|
31
|
+
* Holds the append-only {@link BridgeEntry} log, the {@link LedgerSnapshot} that
|
|
32
|
+
* is its current fold, and the sequence number the next appended entry will
|
|
33
|
+
* carry. Treat every instance as frozen: the enroll/retire/withdraw helpers
|
|
34
|
+
* derive a new ledger rather than editing this one in place.
|
|
35
|
+
*/
|
|
36
|
+
export interface BridgeLedger {
|
|
37
|
+
/** The append-only enrollment event log, in append order. */
|
|
38
|
+
readonly log: readonly BridgeEntry[];
|
|
39
|
+
/** The current fold of {@link log} — the live capabilities and per-server counts. */
|
|
40
|
+
readonly snapshot: LedgerSnapshot;
|
|
41
|
+
/** The sequence number the next appended {@link BridgeEntry} will carry. */
|
|
42
|
+
readonly nextSeq: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The fields an enrollment supplies; the ledger stamps `op`, `seq`, and `at`.
|
|
46
|
+
*
|
|
47
|
+
* A caller names the capability, the owning server, and optionally a pre-minted
|
|
48
|
+
* {@link BridgeKey}; when the key is omitted it is content-addressed from the
|
|
49
|
+
* capability's identity so re-enrolling the same tool is idempotent.
|
|
50
|
+
*/
|
|
51
|
+
export interface EnrollRequest {
|
|
52
|
+
/** The grafted capability to enroll (its `name` is the qualified tool name). */
|
|
53
|
+
readonly capability: AnyCapability;
|
|
54
|
+
/** Id of the external MCP server that owns the capability. */
|
|
55
|
+
readonly server: string;
|
|
56
|
+
/**
|
|
57
|
+
* Optional explicit key. When omitted, a content key is derived from the
|
|
58
|
+
* capability's qualified name and parameter schema so the enrollment upserts
|
|
59
|
+
* any prior entry for the same tool instead of duplicating it.
|
|
60
|
+
*/
|
|
61
|
+
readonly key?: BridgeKey;
|
|
62
|
+
}
|
|
63
|
+
/** An empty ledger: no events, an empty live view, sequence numbering at 1. */
|
|
64
|
+
export declare function emptyBridgeLedger(): BridgeLedger;
|
|
65
|
+
/**
|
|
66
|
+
* Rebuild a ledger value from a persisted event log.
|
|
67
|
+
*
|
|
68
|
+
* The log is authoritative, so a ledger is fully reconstructable from it: fold
|
|
69
|
+
* it for the snapshot and resume sequence numbering one past its high-water
|
|
70
|
+
* mark. Use when rehydrating enrollment state across a restart.
|
|
71
|
+
*
|
|
72
|
+
* @param log the persisted append-only entries, in any order (sorted on fold)
|
|
73
|
+
*/
|
|
74
|
+
export declare function bridgeLedgerFromLog(log: readonly BridgeEntry[]): BridgeLedger;
|
|
75
|
+
/**
|
|
76
|
+
* Append an `enroll` event and return the resulting ledger.
|
|
77
|
+
*
|
|
78
|
+
* The graft is an **upsert**: the appended entry carries a {@link BridgeKey}, and
|
|
79
|
+
* because {@link reduceLedger} lets a later sequence win on a repeated key, a
|
|
80
|
+
* re-enrollment of the same tool transparently replaces its prior live entry
|
|
81
|
+
* rather than duplicating it. The input ledger is not mutated.
|
|
82
|
+
*
|
|
83
|
+
* @param ledger the current ledger (left untouched)
|
|
84
|
+
* @param req the capability + server to enroll, with an optional explicit key
|
|
85
|
+
* @param at enrollment timestamp; defaults to now (ISO-8601)
|
|
86
|
+
*/
|
|
87
|
+
export declare function enrollBridgeCard(ledger: BridgeLedger, req: EnrollRequest, at?: string): BridgeLedger;
|
|
88
|
+
/**
|
|
89
|
+
* Append a `retire` event for one capability and return the resulting ledger.
|
|
90
|
+
*
|
|
91
|
+
* The withdrawal is a **splice**: the appended `retire` entry names the key, and
|
|
92
|
+
* the fold drops that key from the live view. Retiring an unknown or
|
|
93
|
+
* already-retired key is a harmless no-op in the live set (the event is still
|
|
94
|
+
* recorded for the audit trail). The input ledger is not mutated.
|
|
95
|
+
*
|
|
96
|
+
* @param ledger the current ledger (left untouched)
|
|
97
|
+
* @param key the stable key of the capability to withdraw
|
|
98
|
+
* @param server the owning server id, recorded on the event
|
|
99
|
+
* @param at retirement timestamp; defaults to now (ISO-8601)
|
|
100
|
+
*/
|
|
101
|
+
export declare function retire(ledger: BridgeLedger, key: BridgeKey, server: string, at?: string): BridgeLedger;
|
|
102
|
+
/**
|
|
103
|
+
* Retire every capability a given server currently has live, in one batch.
|
|
104
|
+
*
|
|
105
|
+
* Appends one `retire` event per live key owned by the server — the disconnect
|
|
106
|
+
* counterpart to grafting a whole server's tool set. Servers with nothing live
|
|
107
|
+
* yield the ledger unchanged. The input ledger is not mutated.
|
|
108
|
+
*
|
|
109
|
+
* @param ledger the current ledger (left untouched)
|
|
110
|
+
* @param server the server whose live capabilities should all be withdrawn
|
|
111
|
+
* @param at retirement timestamp applied to every event; defaults to now
|
|
112
|
+
*/
|
|
113
|
+
export declare function withdrawServer(ledger: BridgeLedger, server: string, at?: string): BridgeLedger;
|
|
114
|
+
/**
|
|
115
|
+
* Read the live capabilities a single server currently contributes.
|
|
116
|
+
*
|
|
117
|
+
* Pure projection over the snapshot; the per-key→server association is recovered
|
|
118
|
+
* from the log (each live key's last-touching server). Handy for status panels
|
|
119
|
+
* and for {@link withdrawServer}'s batch retirement.
|
|
120
|
+
*
|
|
121
|
+
* @param ledger the ledger to read
|
|
122
|
+
* @param server the server id to filter by
|
|
123
|
+
*/
|
|
124
|
+
export declare function liveCapabilitiesForServer(ledger: BridgeLedger, server: string): AnyCapability[];
|
|
125
|
+
/**
|
|
126
|
+
* The flat list of every live capability, in stable iteration order.
|
|
127
|
+
*
|
|
128
|
+
* What a deck assembler grafts onto the static catalog before handing the
|
|
129
|
+
* conductor its `options.tools`.
|
|
130
|
+
*/
|
|
131
|
+
export declare function liveCapabilities(ledger: BridgeLedger): AnyCapability[];
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
// Type declarations recovered from dist/types (no runtime body)
|
|
3
|
+
/**
|
|
4
|
+
* The bridge network — mounting external MCP servers into the ledger.
|
|
5
|
+
*
|
|
6
|
+
* This is the side-effecting half of bridge enrollment. The framework's
|
|
7
|
+
* {@link mountProtocolBridge} does the protocol work: it connects every
|
|
8
|
+
* configured server, lists each ready endpoint's tools, and hands back a single
|
|
9
|
+
* {@link ToolBox} whose `descriptors()` advertise every grafted remote tool
|
|
10
|
+
* under its qualified `"<server>__<tool>"` name and whose `runner` routes a call
|
|
11
|
+
* back across the owning endpoint.
|
|
12
|
+
*
|
|
13
|
+
* What this module adds on top:
|
|
14
|
+
*
|
|
15
|
+
* - **Adaptation.** A {@link ToolBox} speaks the runtime's descriptor/runner
|
|
16
|
+
* contract; the conductor and catalog speak {@link Capability} (the
|
|
17
|
+
* framework `AgentTool`). {@link bridgeBoxToCapabilities} bridges the two,
|
|
18
|
+
* wrapping each descriptor + the shared runner into an `AgentTool` whose
|
|
19
|
+
* `execute` invokes the runner and projects its opaque outcome onto an
|
|
20
|
+
* {@link AgentToolResult}.
|
|
21
|
+
* - **Enrollment.** {@link attachBridgeCapabilities} folds those adapted
|
|
22
|
+
* capabilities into a {@link BridgeLedger} as `enroll` events, returning the
|
|
23
|
+
* new ledger, the live fleet (for teardown), and the typed status — never
|
|
24
|
+
* mutating a shared array.
|
|
25
|
+
* - **Cataloging.** {@link bridgeCapabilityCard} re-presents a grafted
|
|
26
|
+
* capability as a {@link CapabilityCard}, so dynamic MCP tools surface in
|
|
27
|
+
* help/introspection beside the static catalog rows.
|
|
28
|
+
*
|
|
29
|
+
* Failure isolation comes for free from the fleet: a server that faulted on
|
|
30
|
+
* connect contributes no descriptors and is simply absent from the enrollment.
|
|
31
|
+
*/
|
|
32
|
+
import { type AnyCapability, type CapabilityCard, type DeckFault } from "../contract.js";
|
|
33
|
+
import { type BridgeConfig, type ServerConfig, type ServerFleet, type FleetStatus } from "indusagi/interop";
|
|
34
|
+
import type { ToolBox } from "indusagi/runtime";
|
|
35
|
+
import { type BridgeLedger } from "./ledger.js";
|
|
36
|
+
/**
|
|
37
|
+
* Adapt every tool a mounted {@link ToolBox} advertises into a list of
|
|
38
|
+
* {@link Capability} objects the conductor and ledger consume.
|
|
39
|
+
*
|
|
40
|
+
* Each box descriptor becomes one `AgentTool`: its qualified name, description,
|
|
41
|
+
* and parameter schema are carried verbatim, and its `execute` defers to the
|
|
42
|
+
* box's shared `runner` — passing the abort signal through and projecting the
|
|
43
|
+
* outcome with {@link projectOutcome}. The runner already knows how to route a
|
|
44
|
+
* call by name back to the owning endpoint, so the wrapper stays a thin shim.
|
|
45
|
+
*
|
|
46
|
+
* @param box the tool box returned by {@link mountProtocolBridge}
|
|
47
|
+
*/
|
|
48
|
+
export declare function bridgeBoxToCapabilities(box: ToolBox): AnyCapability[];
|
|
49
|
+
/**
|
|
50
|
+
* Re-present a grafted bridge capability as a {@link CapabilityCard}.
|
|
51
|
+
*
|
|
52
|
+
* Lets dynamic MCP tools appear in the same catalog/help surface as the static
|
|
53
|
+
* cards: the card's `build` simply returns the already-live capability (the
|
|
54
|
+
* graft happened at mount time, so there is nothing further to construct). The
|
|
55
|
+
* owning server id is recovered from the qualified name for the title.
|
|
56
|
+
*
|
|
57
|
+
* @param capability a capability produced by {@link bridgeBoxToCapabilities}
|
|
58
|
+
*/
|
|
59
|
+
export declare function bridgeCapabilityCard(capability: AnyCapability): CapabilityCard;
|
|
60
|
+
/**
|
|
61
|
+
* The outcome of attaching one or more MCP servers to a ledger.
|
|
62
|
+
*
|
|
63
|
+
* Bundles the new {@link BridgeLedger} (with every reachable tool enrolled), the
|
|
64
|
+
* live {@link ServerFleet} the caller owns and must eventually tear down, the
|
|
65
|
+
* aggregate {@link FleetStatus} for rendering, the count of tools grafted, and a
|
|
66
|
+
* non-fatal {@link DeckFault} when the mount itself failed wholesale.
|
|
67
|
+
*/
|
|
68
|
+
export interface AttachResult {
|
|
69
|
+
/** The ledger after enrolling every grafted capability. */
|
|
70
|
+
readonly ledger: BridgeLedger;
|
|
71
|
+
/** The running fleet of endpoints; caller closes it via {@link detachBridge}. */
|
|
72
|
+
readonly fleet?: ServerFleet;
|
|
73
|
+
/** Aggregate per-server health, when a fleet came up. */
|
|
74
|
+
readonly status?: FleetStatus;
|
|
75
|
+
/** How many remote tools were enrolled. */
|
|
76
|
+
readonly enrolled: number;
|
|
77
|
+
/** A wholesale-mount fault, when connecting the bridge threw. */
|
|
78
|
+
readonly fault?: DeckFault;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Mount external MCP servers and enroll their tools into the bridge ledger.
|
|
82
|
+
*
|
|
83
|
+
* Connects every server in `config` through {@link mountProtocolBridge}, adapts
|
|
84
|
+
* the resulting box into capabilities, and folds each one into `ledger` as an
|
|
85
|
+
* `enroll` event — yielding a *new* ledger (the input is untouched). The live
|
|
86
|
+
* fleet is returned so the caller can read status and later tear it down; a
|
|
87
|
+
* faulted server simply contributes no tools and is reflected in the status.
|
|
88
|
+
*
|
|
89
|
+
* A wholesale failure (the mount call itself rejecting) is caught and reported
|
|
90
|
+
* as a `bridge` {@link DeckFault} on the result rather than thrown, so a bad MCP
|
|
91
|
+
* configuration degrades the deck instead of sinking session bootstrap.
|
|
92
|
+
*
|
|
93
|
+
* @param ledger the ledger to enroll into (left untouched)
|
|
94
|
+
* @param config the set of MCP servers to connect and graft
|
|
95
|
+
*/
|
|
96
|
+
export declare function attachBridgeCapabilities(ledger: BridgeLedger, config: BridgeConfig): Promise<AttachResult>;
|
|
97
|
+
/**
|
|
98
|
+
* Detach one or more servers: retire their ledger entries and close the fleet.
|
|
99
|
+
*
|
|
100
|
+
* Withdraws every named server's live capabilities from `ledger` (or, when no
|
|
101
|
+
* names are given, every server present in the fleet status) and gracefully
|
|
102
|
+
* tears the fleet down. Returns the new ledger; the fleet is no longer usable
|
|
103
|
+
* after this resolves. The teardown is best-effort — a close that throws is
|
|
104
|
+
* swallowed so ledger withdrawal still completes.
|
|
105
|
+
*
|
|
106
|
+
* @param ledger the current ledger (left untouched)
|
|
107
|
+
* @param fleet the live fleet to close
|
|
108
|
+
* @param servers optional subset of server ids to detach; defaults to all
|
|
109
|
+
*/
|
|
110
|
+
export declare function detachBridge(ledger: BridgeLedger, fleet: ServerFleet, servers?: readonly string[]): Promise<BridgeLedger>;
|
|
111
|
+
/**
|
|
112
|
+
* Convenience: turn a flat list of MCP server descriptions into the
|
|
113
|
+
* {@link BridgeConfig} {@link attachBridgeCapabilities} expects.
|
|
114
|
+
*
|
|
115
|
+
* @param servers the per-server transport configurations, in declaration order
|
|
116
|
+
*/
|
|
117
|
+
export declare function bridgeConfig(servers: readonly ServerConfig[]): BridgeConfig;
|