agent-trellis 0.7.0 → 0.8.0
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 +97 -38
- package/dist/adapters/claude-code.js +3 -2
- package/dist/adapters/codex.js +3 -2
- package/dist/adapters/jsonMcp.d.ts +3 -1
- package/dist/adapters/jsonMcp.js +18 -4
- package/dist/adapters/kimi-code.d.ts +21 -0
- package/dist/adapters/kimi-code.js +107 -0
- package/dist/adapters/kiro.js +3 -2
- package/dist/adapters/mcpPlan.d.ts +6 -1
- package/dist/adapters/mcpPlan.js +106 -8
- package/dist/adapters/pi.js +22 -3
- package/dist/adapters/symlinkPlan.d.ts +7 -0
- package/dist/adapters/symlinkPlan.js +16 -7
- package/dist/cli.js +121 -21
- package/dist/commands/doctor.d.ts +7 -2
- package/dist/commands/doctor.js +38 -2
- package/dist/commands/init.d.ts +4 -2
- package/dist/commands/init.js +26 -13
- package/dist/commands/kimi.d.ts +16 -0
- package/dist/commands/kimi.js +64 -0
- package/dist/commands/manage.d.ts +39 -0
- package/dist/commands/manage.js +126 -0
- package/dist/commands/mcp.d.ts +22 -1
- package/dist/commands/mcp.js +65 -2
- package/dist/commands/mcpGateway.d.ts +6 -0
- package/dist/commands/mcpGateway.js +34 -14
- package/dist/commands/mcpImport.d.ts +27 -0
- package/dist/commands/mcpImport.js +336 -0
- package/dist/commands/memory.d.ts +5 -3
- package/dist/commands/memory.js +12 -8
- package/dist/commands/migrate.d.ts +16 -5
- package/dist/commands/migrate.js +90 -17
- package/dist/commands/onboard.d.ts +74 -2
- package/dist/commands/onboard.js +458 -71
- package/dist/commands/rollback.js +4 -1
- package/dist/commands/skill.d.ts +17 -0
- package/dist/commands/skill.js +47 -2
- package/dist/commands/sync.js +2 -0
- package/dist/core/canonical.d.ts +15 -7
- package/dist/core/canonical.js +101 -13
- package/dist/core/capabilitySelection.d.ts +45 -0
- package/dist/core/capabilitySelection.js +124 -0
- package/dist/core/types.d.ts +25 -2
- package/dist/core/types.js +7 -0
- package/dist/lib/backup.d.ts +2 -0
- package/dist/lib/backup.js +4 -0
- package/dist/lib/builtinSkills.d.ts +2 -0
- package/dist/lib/builtinSkills.js +4 -0
- package/dist/lib/cliMetadata.d.ts +1 -0
- package/dist/lib/cliMetadata.js +14 -0
- package/dist/lib/gatewayBackend.d.ts +28 -0
- package/dist/lib/gatewayBackend.js +72 -1
- package/dist/lib/installAgent.d.ts +7 -5
- package/dist/lib/installAgent.js +4 -3
- package/dist/lib/mcpConnect.js +5 -1
- package/dist/lib/mcpMigrateRead.d.ts +1 -0
- package/dist/lib/mcpMigrateRead.js +3 -0
- package/dist/lib/mcpRuntime.d.ts +46 -0
- package/dist/lib/mcpRuntime.js +185 -0
- package/dist/lib/mcpStatusProvider.d.ts +10 -0
- package/dist/lib/mcpStatusProvider.js +25 -0
- package/dist/lib/mcpToolRegistry.d.ts +18 -0
- package/dist/lib/mcpToolRegistry.js +107 -14
- package/dist/lib/memoryGraph.d.ts +1 -1
- package/dist/lib/memoryGraph.js +9 -3
- package/dist/lib/memoryProvider.d.ts +58 -0
- package/dist/lib/memoryProvider.js +175 -0
- package/dist/lib/memoryStatus.d.ts +17 -0
- package/dist/lib/memoryStatus.js +88 -0
- package/dist/lib/nativeMemory.d.ts +19 -0
- package/dist/lib/nativeMemory.js +129 -0
- package/dist/lib/runtimeControlProvider.d.ts +16 -0
- package/dist/lib/runtimeControlProvider.js +168 -0
- package/dist/lib/secretEnv.d.ts +2 -1
- package/dist/lib/secretEnv.js +6 -2
- package/dist/lib/skillProvider.d.ts +13 -0
- package/dist/lib/skillProvider.js +210 -0
- package/dist/lib/taskProvider.d.ts +7 -0
- package/dist/lib/taskProvider.js +43 -0
- package/dist/lib/taskStore.d.ts +46 -0
- package/dist/lib/taskStore.js +156 -0
- package/dist/lib/terminalPicker.d.ts +23 -49
- package/dist/lib/terminalPicker.js +53 -218
- package/dist/pi-bridge/bundle.js +475 -37
- package/dist/pi-bridge/index.js +42 -21
- package/dist/pi-bridge/schemaTranslate.d.ts +4 -8
- package/dist/pi-bridge/schemaTranslate.js +4 -8
- package/dist/probes/kimi-code.d.ts +12 -0
- package/dist/probes/kimi-code.js +55 -0
- package/dist/sdk.d.ts +1 -1
- package/docs/architecture.md +224 -26
- package/docs/assets/agent-trellis-architecture-horizontal.svg +80 -0
- package/docs/assets/agent-trellis-architecture-vertical.svg +82 -0
- package/docs/assets/trellis-mark.svg +12 -0
- package/docs/getting-started.md +328 -35
- package/docs/overview.md +131 -0
- package/docs/overview.zh-CN.md +100 -0
- package/docs/research.md +59 -0
- package/docs/roadmap.md +9 -5
- package/package.json +3 -2
- package/schema/builtin-skills/trellis-runtime/SKILL.md +117 -0
- package/schema/capability-selection.example.yaml +23 -0
- package/schema/scope.example.yaml +1 -1
- package/schema/servers.example.yaml +15 -1
|
@@ -134,7 +134,10 @@ async function restoreOperation(homeDir, runId, op) {
|
|
|
134
134
|
}
|
|
135
135
|
export async function applyRollbackPlan(homeDir, runId, manifest, items) {
|
|
136
136
|
const restorePaths = new Set(items.filter((i) => i.action === "restore").map((i) => i.path));
|
|
137
|
-
|
|
137
|
+
// Restore in reverse write order. A single onboarding transaction may
|
|
138
|
+
// update servers.yaml several times (mode, memory, routes); replaying
|
|
139
|
+
// restores forward would leave the file at an intermediate snapshot.
|
|
140
|
+
for (const op of [...manifest.operations].reverse()) {
|
|
138
141
|
if (!restorePaths.has(op.path))
|
|
139
142
|
continue;
|
|
140
143
|
await restoreOperation(homeDir, runId, op);
|
package/dist/commands/skill.d.ts
CHANGED
|
@@ -8,6 +8,23 @@
|
|
|
8
8
|
* symlinkPlan.ts`'s pre-existing stale-symlink removal.
|
|
9
9
|
*/
|
|
10
10
|
import type { AgentId } from "../core/types.js";
|
|
11
|
+
import { type BackupSession } from "../lib/backup.js";
|
|
12
|
+
export type BuiltinSkillUpdateAction = "create" | "update" | "already-current";
|
|
13
|
+
export interface BuiltinSkillUpdatePlan {
|
|
14
|
+
name: "trellis-runtime";
|
|
15
|
+
action: BuiltinSkillUpdateAction;
|
|
16
|
+
detail: string;
|
|
17
|
+
path: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function collectBuiltinSkillUpdatePlan(homeDir?: string): BuiltinSkillUpdatePlan;
|
|
20
|
+
export declare function applyBuiltinSkillUpdatePlan(plan: BuiltinSkillUpdatePlan, backup?: BackupSession): void;
|
|
21
|
+
export declare function runSkillUpdateBuiltin(opts?: {
|
|
22
|
+
homeDir?: string;
|
|
23
|
+
json?: boolean;
|
|
24
|
+
dryRun?: boolean;
|
|
25
|
+
}): {
|
|
26
|
+
exitCode: number;
|
|
27
|
+
};
|
|
11
28
|
export interface SkillListEntry {
|
|
12
29
|
name: string;
|
|
13
30
|
scope: readonly AgentId[];
|
package/dist/commands/skill.js
CHANGED
|
@@ -7,16 +7,56 @@
|
|
|
7
7
|
* `decideDirImport` (src/lib/dirEquals.ts) and `src/adapters/
|
|
8
8
|
* symlinkPlan.ts`'s pre-existing stale-symlink removal.
|
|
9
9
|
*/
|
|
10
|
-
import { cpSync, existsSync, mkdirSync, rmSync } from "node:fs";
|
|
10
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { join } from "node:path";
|
|
13
|
+
import { dirname } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
13
15
|
import { decideDirImport } from "../lib/dirEquals.js";
|
|
14
16
|
import { findSkillFile } from "../lib/skillFile.js";
|
|
15
17
|
import { loadCanonicalSource } from "../core/canonical.js";
|
|
16
18
|
import { resolveScope } from "../core/types.js";
|
|
19
|
+
import { isBuiltinSkillName } from "../lib/builtinSkills.js";
|
|
20
|
+
import { openBackupSession } from "../lib/backup.js";
|
|
21
|
+
function builtinSkillTemplate() {
|
|
22
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
return readFileSync(join(here, "..", "..", "schema", "builtin-skills", "trellis-runtime", "SKILL.md"), "utf8");
|
|
24
|
+
}
|
|
25
|
+
export function collectBuiltinSkillUpdatePlan(homeDir = homedir()) {
|
|
26
|
+
const path = join(homeDir, ".trellis", "skills", "trellis-runtime", "SKILL.md");
|
|
27
|
+
if (!existsSync(path))
|
|
28
|
+
return { name: "trellis-runtime", action: "create", detail: "will install the package-owned Runtime Skill", path };
|
|
29
|
+
if (readFileSync(path, "utf8") === builtinSkillTemplate())
|
|
30
|
+
return { name: "trellis-runtime", action: "already-current", detail: "package-owned Runtime Skill is already current", path };
|
|
31
|
+
return { name: "trellis-runtime", action: "update", detail: "will refresh the package-owned Runtime Skill; existing Agent symlinks will see the update", path };
|
|
32
|
+
}
|
|
33
|
+
export function applyBuiltinSkillUpdatePlan(plan, backup) {
|
|
34
|
+
if (plan.action === "already-current")
|
|
35
|
+
return;
|
|
36
|
+
const content = builtinSkillTemplate();
|
|
37
|
+
mkdirSync(dirname(plan.path), { recursive: true });
|
|
38
|
+
if (backup)
|
|
39
|
+
backup.writeFile(plan.path, content);
|
|
40
|
+
else
|
|
41
|
+
writeFileSync(plan.path, content);
|
|
42
|
+
}
|
|
43
|
+
export function runSkillUpdateBuiltin(opts = {}) {
|
|
44
|
+
const homeDir = opts.homeDir ?? homedir();
|
|
45
|
+
const plan = collectBuiltinSkillUpdatePlan(homeDir);
|
|
46
|
+
if (!opts.dryRun && plan.action !== "already-current") {
|
|
47
|
+
const backup = openBackupSession(homeDir, "skill-update-builtin");
|
|
48
|
+
applyBuiltinSkillUpdatePlan(plan, backup);
|
|
49
|
+
backup.finalize();
|
|
50
|
+
}
|
|
51
|
+
if (opts.json)
|
|
52
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
53
|
+
else
|
|
54
|
+
console.log(`${opts.dryRun ? "[dry run] " : ""}skill update-builtin\n [${plan.action}] ${plan.detail}`);
|
|
55
|
+
return { exitCode: 0 };
|
|
56
|
+
}
|
|
17
57
|
export function collectSkillList(homeDir = homedir()) {
|
|
18
58
|
const canonical = loadCanonicalSource(homeDir);
|
|
19
|
-
return canonical.skills.map((skill) => ({
|
|
59
|
+
return canonical.skills.filter((skill) => !isBuiltinSkillName(skill.name)).map((skill) => ({
|
|
20
60
|
name: skill.name,
|
|
21
61
|
scope: resolveScope(skill.scope, canonical.managedAgents),
|
|
22
62
|
}));
|
|
@@ -37,6 +77,9 @@ export function runSkillList(opts = {}) {
|
|
|
37
77
|
return { exitCode: 0 };
|
|
38
78
|
}
|
|
39
79
|
export function collectSkillAddPlan(name, fromPath, homeDir = homedir()) {
|
|
80
|
+
if (isBuiltinSkillName(name)) {
|
|
81
|
+
return { name, action: "conflict", detail: `"${name}" is a Trellis package-owned Skill and cannot be replaced with skill add` };
|
|
82
|
+
}
|
|
40
83
|
const skillFile = findSkillFile(fromPath);
|
|
41
84
|
if (!skillFile) {
|
|
42
85
|
return { name, action: "invalid-source", detail: `${fromPath} has no SKILL.md` };
|
|
@@ -77,6 +120,8 @@ export function runSkillAdd(name, fromPath, opts = {}) {
|
|
|
77
120
|
return { exitCode: plan.action === "conflict" || plan.action === "invalid-source" ? 1 : 0 };
|
|
78
121
|
}
|
|
79
122
|
export function collectSkillRemovePlan(name, homeDir = homedir()) {
|
|
123
|
+
if (isBuiltinSkillName(name))
|
|
124
|
+
return { name, action: "not-found" };
|
|
80
125
|
const dir = join(homeDir, ".trellis", "skills", name);
|
|
81
126
|
return { name, action: existsSync(dir) ? "removed" : "not-found" };
|
|
82
127
|
}
|
package/dist/commands/sync.js
CHANGED
|
@@ -11,12 +11,14 @@ import { ClaudeCodeAdapter } from "../adapters/claude-code.js";
|
|
|
11
11
|
import { CodexAdapter } from "../adapters/codex.js";
|
|
12
12
|
import { KiroAdapter } from "../adapters/kiro.js";
|
|
13
13
|
import { PiAdapter } from "../adapters/pi.js";
|
|
14
|
+
import { KimiCodeAdapter } from "../adapters/kimi-code.js";
|
|
14
15
|
import { openBackupSession } from "../lib/backup.js";
|
|
15
16
|
const ADAPTER_FACTORY = {
|
|
16
17
|
"claude-code": (homeDir) => new ClaudeCodeAdapter(homeDir),
|
|
17
18
|
codex: (homeDir) => new CodexAdapter(homeDir),
|
|
18
19
|
kiro: (homeDir) => new KiroAdapter(homeDir),
|
|
19
20
|
pi: (homeDir) => new PiAdapter(homeDir),
|
|
21
|
+
"kimi-code": (homeDir) => new KimiCodeAdapter(homeDir),
|
|
20
22
|
};
|
|
21
23
|
/** Only agents in `canonical.managedAgents` — an agent present on this
|
|
22
24
|
* machine but not managed gets no adapter at all, not a zero-item plan
|
package/dist/core/canonical.d.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* workspace scope"). See openspec/changes/trellis-sync-p1/specs/
|
|
5
5
|
* canonical-source-loading/spec.md for the exact contract this implements.
|
|
6
6
|
*/
|
|
7
|
-
import type { AgentId, CanonicalSource, McpServerDef } from "./types.js";
|
|
7
|
+
import type { AgentId, CapabilityDelivery, CanonicalSource, McpRoute, McpServerDef } from "./types.js";
|
|
8
|
+
import type { BackupSession } from "../lib/backup.js";
|
|
8
9
|
/**
|
|
9
10
|
* The on-disk shape for one server entry — `static_env`/`env_aliases`
|
|
10
11
|
* (snake_case, like every other multi-word key across `.trellis/*.yaml`)
|
|
@@ -27,10 +28,10 @@ export type ServersYamlWriteResult = {
|
|
|
27
28
|
ok: false;
|
|
28
29
|
error: string;
|
|
29
30
|
};
|
|
30
|
-
export declare function upsertServerYaml(path: string, name: string, def: McpServerDef): ServersYamlWriteResult;
|
|
31
|
+
export declare function upsertServerYaml(path: string, name: string, def: McpServerDef, backup?: BackupSession): ServersYamlWriteResult;
|
|
31
32
|
/** Inverse of `upsertServerYaml` — same preservation guarantee, same
|
|
32
33
|
* refusal posture on a missing/unparseable file. */
|
|
33
|
-
export declare function removeServerYaml(path: string, name: string): ServersYamlWriteResult;
|
|
34
|
+
export declare function removeServerYaml(path: string, name: string, backup?: BackupSession): ServersYamlWriteResult;
|
|
34
35
|
/**
|
|
35
36
|
* `trellis onboard --mcp-mode <value>` (trellis-onboard-mcp-mode) —
|
|
36
37
|
* everything needed to select or clear the top-level `hub`/`gateway`
|
|
@@ -56,7 +57,14 @@ export type McpMode = {
|
|
|
56
57
|
* starter file). Selecting one mode always clears the other two
|
|
57
58
|
* (design.md D3) — never left for the caller to remember.
|
|
58
59
|
*/
|
|
59
|
-
export declare function writeMcpModeYaml(path: string, mode: McpMode): ServersYamlWriteResult;
|
|
60
|
+
export declare function writeMcpModeYaml(path: string, mode: McpMode, backup?: BackupSession): ServersYamlWriteResult;
|
|
61
|
+
export type McpRoutesWriteResult = ServersYamlWriteResult;
|
|
62
|
+
/** Writes explicit per-agent routes while preserving legacy hub/gateway
|
|
63
|
+
* shorthand. An empty route map removes the optional key. */
|
|
64
|
+
export declare function writeMcpRoutesYaml(path: string, routes: Partial<Record<AgentId, McpRoute>>, backup?: BackupSession): McpRoutesWriteResult;
|
|
65
|
+
/** Writes per-agent capability delivery preferences into servers.yaml.
|
|
66
|
+
* Omitted entries continue to use the native default. */
|
|
67
|
+
export declare function writeMcpRuntimeDeliveryYaml(path: string, delivery: Partial<Record<AgentId, CapabilityDelivery>>, backup?: BackupSession): ServersYamlWriteResult;
|
|
60
68
|
export type SecretsPolicyWriteResult = {
|
|
61
69
|
ok: true;
|
|
62
70
|
} | {
|
|
@@ -78,7 +86,7 @@ export type SecretsPolicyWriteResult = {
|
|
|
78
86
|
export declare function writeSecretsPolicyExtraction(path: string, extraction: {
|
|
79
87
|
varName: string;
|
|
80
88
|
envFilePath: string;
|
|
81
|
-
}): SecretsPolicyWriteResult;
|
|
89
|
+
}, backup?: BackupSession): SecretsPolicyWriteResult;
|
|
82
90
|
/**
|
|
83
91
|
* Ensures one exact line is present in a `.gitignore` file, creating the
|
|
84
92
|
* file if it doesn't exist yet — idempotent (a no-op if the line is
|
|
@@ -89,7 +97,7 @@ export declare function writeSecretsPolicyExtraction(path: string, extraction: {
|
|
|
89
97
|
* init`'s own bootstrap, so a machine that never extracts a secret never
|
|
90
98
|
* gains this file at all.
|
|
91
99
|
*/
|
|
92
|
-
export declare function ensureGitignoreEntry(gitignorePath: string, line: string): void;
|
|
100
|
+
export declare function ensureGitignoreEntry(gitignorePath: string, line: string, backup?: BackupSession): void;
|
|
93
101
|
/**
|
|
94
102
|
* Ensures a shell rc file sources the local secrets env file on every new
|
|
95
103
|
* shell — one generic pointer block, appended once, idempotent (detected
|
|
@@ -102,7 +110,7 @@ export declare function ensureGitignoreEntry(gitignorePath: string, line: string
|
|
|
102
110
|
* lines `parseDotenv` already expects, so that format never needs an
|
|
103
111
|
* `export` prefix of its own.
|
|
104
112
|
*/
|
|
105
|
-
export declare function ensureShellEnvSource(rcPath: string, envFilePath: string): void;
|
|
113
|
+
export declare function ensureShellEnvSource(rcPath: string, envFilePath: string, backup?: BackupSession): void;
|
|
106
114
|
/**
|
|
107
115
|
* `homeDir` defaults to the real `~` and is only ever overridden for tests
|
|
108
116
|
* and `scripts/sandbox.sh` — the same seam P0's probes use
|
package/dist/core/canonical.js
CHANGED
|
@@ -89,8 +89,39 @@ function loadServersYaml(path) {
|
|
|
89
89
|
knownHostInjected: parsed.known_host_injected ?? [],
|
|
90
90
|
hub: parsed.hub,
|
|
91
91
|
gateway: fromGatewayYaml(parsed.gateway),
|
|
92
|
+
routes: fromRoutesYaml(parsed.routes),
|
|
93
|
+
runtime: fromRuntimeYaml(parsed.runtime),
|
|
92
94
|
};
|
|
93
95
|
}
|
|
96
|
+
function fromRuntimeYaml(runtime) {
|
|
97
|
+
if (!runtime?.delivery)
|
|
98
|
+
return undefined;
|
|
99
|
+
const delivery = {};
|
|
100
|
+
for (const [rawAgent, rawDelivery] of Object.entries(runtime.delivery)) {
|
|
101
|
+
if (!ALL_AGENTS.includes(rawAgent))
|
|
102
|
+
continue;
|
|
103
|
+
if (rawDelivery !== "native" && rawDelivery !== "mcp" && rawDelivery !== "both")
|
|
104
|
+
continue;
|
|
105
|
+
delivery[rawAgent] = rawDelivery;
|
|
106
|
+
}
|
|
107
|
+
return Object.keys(delivery).length > 0 ? { delivery } : undefined;
|
|
108
|
+
}
|
|
109
|
+
function fromRoutesYaml(routes) {
|
|
110
|
+
if (!routes)
|
|
111
|
+
return undefined;
|
|
112
|
+
const out = {};
|
|
113
|
+
for (const [rawAgent, rawRoute] of Object.entries(routes)) {
|
|
114
|
+
if (!ALL_AGENTS.includes(rawAgent))
|
|
115
|
+
continue;
|
|
116
|
+
if (!rawRoute?.mode || !["direct", "gateway", "hub"].includes(rawRoute.mode))
|
|
117
|
+
continue;
|
|
118
|
+
out[rawAgent] = {
|
|
119
|
+
mode: rawRoute.mode,
|
|
120
|
+
...(rawRoute.servers ? { servers: rawRoute.servers } : {}),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
124
|
+
}
|
|
94
125
|
/**
|
|
95
126
|
* A `gateway:` block with no `enabled` key reads as off, not as on — the
|
|
96
127
|
* only safe default for a field whose whole effect is to stop writing the
|
|
@@ -137,7 +168,15 @@ function forceBlockStyle(node) {
|
|
|
137
168
|
node.flow = false;
|
|
138
169
|
}
|
|
139
170
|
}
|
|
140
|
-
|
|
171
|
+
function persistText(path, content, backup) {
|
|
172
|
+
if (existsSync(path) && readFileSync(path, "utf-8") === content)
|
|
173
|
+
return;
|
|
174
|
+
if (backup)
|
|
175
|
+
backup.writeFile(path, content);
|
|
176
|
+
else
|
|
177
|
+
writeFileSync(path, content);
|
|
178
|
+
}
|
|
179
|
+
export function upsertServerYaml(path, name, def, backup) {
|
|
141
180
|
if (!existsSync(path)) {
|
|
142
181
|
return { ok: false, error: `${path} does not exist — run \`trellis init\` first` };
|
|
143
182
|
}
|
|
@@ -151,12 +190,12 @@ export function upsertServerYaml(path, name, def) {
|
|
|
151
190
|
doc.setIn(["servers", name], toServerDefYaml(def));
|
|
152
191
|
forceBlockStyle(doc.get("servers", true));
|
|
153
192
|
forceBlockStyle(doc.getIn(["servers", name], true));
|
|
154
|
-
|
|
193
|
+
persistText(path, doc.toString(), backup);
|
|
155
194
|
return { ok: true };
|
|
156
195
|
}
|
|
157
196
|
/** Inverse of `upsertServerYaml` — same preservation guarantee, same
|
|
158
197
|
* refusal posture on a missing/unparseable file. */
|
|
159
|
-
export function removeServerYaml(path, name) {
|
|
198
|
+
export function removeServerYaml(path, name, backup) {
|
|
160
199
|
if (!existsSync(path)) {
|
|
161
200
|
return { ok: false, error: `${path} does not exist — run \`trellis init\` first` };
|
|
162
201
|
}
|
|
@@ -168,7 +207,7 @@ export function removeServerYaml(path, name) {
|
|
|
168
207
|
return { ok: false, error: `could not parse ${path}: ${err instanceof Error ? err.message : String(err)}` };
|
|
169
208
|
}
|
|
170
209
|
doc.deleteIn(["servers", name]);
|
|
171
|
-
|
|
210
|
+
persistText(path, doc.toString(), backup);
|
|
172
211
|
return { ok: true };
|
|
173
212
|
}
|
|
174
213
|
/**
|
|
@@ -181,7 +220,7 @@ export function removeServerYaml(path, name) {
|
|
|
181
220
|
* starter file). Selecting one mode always clears the other two
|
|
182
221
|
* (design.md D3) — never left for the caller to remember.
|
|
183
222
|
*/
|
|
184
|
-
export function writeMcpModeYaml(path, mode) {
|
|
223
|
+
export function writeMcpModeYaml(path, mode, backup) {
|
|
185
224
|
if (!existsSync(path)) {
|
|
186
225
|
return { ok: false, error: `${path} does not exist — run \`trellis init\` first` };
|
|
187
226
|
}
|
|
@@ -214,7 +253,56 @@ export function writeMcpModeYaml(path, mode) {
|
|
|
214
253
|
forceBlockStyle(doc.get("gateway", true));
|
|
215
254
|
break;
|
|
216
255
|
}
|
|
217
|
-
|
|
256
|
+
persistText(path, doc.toString(), backup);
|
|
257
|
+
return { ok: true };
|
|
258
|
+
}
|
|
259
|
+
/** Writes explicit per-agent routes while preserving legacy hub/gateway
|
|
260
|
+
* shorthand. An empty route map removes the optional key. */
|
|
261
|
+
export function writeMcpRoutesYaml(path, routes, backup) {
|
|
262
|
+
if (!existsSync(path)) {
|
|
263
|
+
return { ok: false, error: `${path} does not exist — run \`trellis init\` first` };
|
|
264
|
+
}
|
|
265
|
+
let doc;
|
|
266
|
+
try {
|
|
267
|
+
doc = parseDocument(readFileSync(path, "utf-8"));
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
return { ok: false, error: `could not parse ${path}: ${err instanceof Error ? err.message : String(err)}` };
|
|
271
|
+
}
|
|
272
|
+
const serialized = Object.fromEntries(Object.entries(routes).map(([agent, route]) => [agent, { mode: route.mode, ...(route.servers ? { servers: route.servers } : {}) }]));
|
|
273
|
+
if (Object.keys(serialized).length === 0) {
|
|
274
|
+
doc.delete("routes");
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
doc.set("routes", serialized);
|
|
278
|
+
forceBlockStyle(doc.get("routes", true));
|
|
279
|
+
}
|
|
280
|
+
persistText(path, doc.toString(), backup);
|
|
281
|
+
return { ok: true };
|
|
282
|
+
}
|
|
283
|
+
/** Writes per-agent capability delivery preferences into servers.yaml.
|
|
284
|
+
* Omitted entries continue to use the native default. */
|
|
285
|
+
export function writeMcpRuntimeDeliveryYaml(path, delivery, backup) {
|
|
286
|
+
if (!existsSync(path)) {
|
|
287
|
+
return { ok: false, error: path + " does not exist — run trellis init first" };
|
|
288
|
+
}
|
|
289
|
+
let doc;
|
|
290
|
+
try {
|
|
291
|
+
doc = parseDocument(readFileSync(path, "utf-8"));
|
|
292
|
+
}
|
|
293
|
+
catch (err) {
|
|
294
|
+
return { ok: false, error: `could not parse ${path}: ${err instanceof Error ? err.message : String(err)}` };
|
|
295
|
+
}
|
|
296
|
+
const serialized = Object.fromEntries(Object.entries(delivery).map(([agent, mode]) => [agent, mode]));
|
|
297
|
+
if (Object.keys(serialized).length === 0) {
|
|
298
|
+
doc.delete("runtime");
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
doc.setIn(["runtime", "delivery"], serialized);
|
|
302
|
+
forceBlockStyle(doc.get("runtime", true));
|
|
303
|
+
forceBlockStyle(doc.getIn(["runtime", "delivery"], true));
|
|
304
|
+
}
|
|
305
|
+
persistText(path, doc.toString(), backup);
|
|
218
306
|
return { ok: true };
|
|
219
307
|
}
|
|
220
308
|
function loadSecretsPolicyYaml(path, homeDir) {
|
|
@@ -240,7 +328,7 @@ function loadSecretsPolicyYaml(path, homeDir) {
|
|
|
240
328
|
* `varName` to `allowed_vars` only if not already present (dedup, same
|
|
241
329
|
* "already there is a no-op" rule every other Trellis writer follows).
|
|
242
330
|
*/
|
|
243
|
-
export function writeSecretsPolicyExtraction(path, extraction) {
|
|
331
|
+
export function writeSecretsPolicyExtraction(path, extraction, backup) {
|
|
244
332
|
if (!existsSync(path)) {
|
|
245
333
|
return { ok: false, error: `${path} does not exist — run \`trellis init\` first` };
|
|
246
334
|
}
|
|
@@ -262,7 +350,7 @@ export function writeSecretsPolicyExtraction(path, extraction) {
|
|
|
262
350
|
doc.set("allowed_vars", [...currentAllowedVars, extraction.varName]);
|
|
263
351
|
forceBlockStyle(doc.get("allowed_vars", true));
|
|
264
352
|
}
|
|
265
|
-
|
|
353
|
+
persistText(path, doc.toString(), backup);
|
|
266
354
|
return { ok: true };
|
|
267
355
|
}
|
|
268
356
|
/**
|
|
@@ -275,13 +363,13 @@ export function writeSecretsPolicyExtraction(path, extraction) {
|
|
|
275
363
|
* init`'s own bootstrap, so a machine that never extracts a secret never
|
|
276
364
|
* gains this file at all.
|
|
277
365
|
*/
|
|
278
|
-
export function ensureGitignoreEntry(gitignorePath, line) {
|
|
366
|
+
export function ensureGitignoreEntry(gitignorePath, line, backup) {
|
|
279
367
|
const existing = existsSync(gitignorePath) ? readFileSync(gitignorePath, "utf-8") : "";
|
|
280
368
|
const lines = existing.split("\n").map((l) => l.trim());
|
|
281
369
|
if (lines.includes(line))
|
|
282
370
|
return;
|
|
283
371
|
const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
284
|
-
|
|
372
|
+
persistText(gitignorePath, `${existing}${separator}${line}\n`, backup);
|
|
285
373
|
}
|
|
286
374
|
const SHELL_ENV_SOURCE_MARKER = "# >>> trellis mcp secrets >>>";
|
|
287
375
|
const SHELL_ENV_SOURCE_END_MARKER = "# <<< trellis mcp secrets <<<";
|
|
@@ -297,17 +385,17 @@ const SHELL_ENV_SOURCE_END_MARKER = "# <<< trellis mcp secrets <<<";
|
|
|
297
385
|
* lines `parseDotenv` already expects, so that format never needs an
|
|
298
386
|
* `export` prefix of its own.
|
|
299
387
|
*/
|
|
300
|
-
export function ensureShellEnvSource(rcPath, envFilePath) {
|
|
388
|
+
export function ensureShellEnvSource(rcPath, envFilePath, backup) {
|
|
301
389
|
const existing = existsSync(rcPath) ? readFileSync(rcPath, "utf-8") : "";
|
|
302
390
|
if (existing.includes(SHELL_ENV_SOURCE_MARKER))
|
|
303
391
|
return;
|
|
304
392
|
const separator = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
305
393
|
const block = `${SHELL_ENV_SOURCE_MARKER}\nif [ -f "${envFilePath}" ]; then\n set -a\n source "${envFilePath}"\n set +a\nfi\n${SHELL_ENV_SOURCE_END_MARKER}\n`;
|
|
306
394
|
mkdirSync(dirname(rcPath), { recursive: true });
|
|
307
|
-
|
|
395
|
+
persistText(rcPath, `${existing}${separator}${block}`, backup);
|
|
308
396
|
}
|
|
309
397
|
/** `undefined` if the name has no entry in scope.yaml's map — "shared with
|
|
310
|
-
* all
|
|
398
|
+
* all managed agents by default. A recognized but empty list is left as
|
|
311
399
|
* authored (an explicitly agent-less scope), not coerced to "all". */
|
|
312
400
|
/** Missing file and `agents: []` both resolve to `[]` — zero managed
|
|
313
401
|
* agents (D1), never "everyone." An unrecognized id is dropped with a
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AgentId, CapabilityDelivery, CanonicalSource, McpRouteMode } from "./types.js";
|
|
2
|
+
export type NameSelection = "all" | "none" | string[];
|
|
3
|
+
export interface CapabilitySelection {
|
|
4
|
+
skills: NameSelection;
|
|
5
|
+
mcpServers: NameSelection;
|
|
6
|
+
memories: NameSelection;
|
|
7
|
+
memoryMigration: "all" | "none";
|
|
8
|
+
mcpRoutes: Partial<Record<AgentId, {
|
|
9
|
+
mode: McpRouteMode;
|
|
10
|
+
servers?: string[];
|
|
11
|
+
}>>;
|
|
12
|
+
runtimeDelivery: Partial<Record<AgentId, CapabilityDelivery>>;
|
|
13
|
+
}
|
|
14
|
+
export interface CapabilityItem {
|
|
15
|
+
name: string;
|
|
16
|
+
displayName: string;
|
|
17
|
+
fingerprint?: string;
|
|
18
|
+
status: "new" | "unchanged" | "changed" | "unsupported";
|
|
19
|
+
detail?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface UnsupportedCapabilitySource {
|
|
22
|
+
name: string;
|
|
23
|
+
detail: string;
|
|
24
|
+
}
|
|
25
|
+
export interface CapabilityInventory {
|
|
26
|
+
skills: CapabilityItem[];
|
|
27
|
+
mcpServers: CapabilityItem[];
|
|
28
|
+
canonicalMemories: CapabilityItem[];
|
|
29
|
+
nativeMemory: UnsupportedCapabilitySource[];
|
|
30
|
+
}
|
|
31
|
+
export type SelectionFileResult = {
|
|
32
|
+
ok: true;
|
|
33
|
+
selection: CapabilitySelection;
|
|
34
|
+
} | {
|
|
35
|
+
ok: false;
|
|
36
|
+
error: string;
|
|
37
|
+
};
|
|
38
|
+
export declare function parseCapabilitySelectionFile(path: string): SelectionFileResult;
|
|
39
|
+
export declare function defaultCapabilitySelection(): CapabilitySelection;
|
|
40
|
+
export declare function resolveSelectedNames(selection: NameSelection, available: readonly string[]): string[];
|
|
41
|
+
export declare function selectionContains(selection: NameSelection, name: string): boolean;
|
|
42
|
+
export declare function buildCapabilityInventory(source: {
|
|
43
|
+
skillNames: readonly string[];
|
|
44
|
+
mcpServerNames: readonly string[];
|
|
45
|
+
}, canonical: Pick<CanonicalSource, "skills" | "mcp" | "memories">, nativeMemory?: UnsupportedCapabilitySource[]): CapabilityInventory;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { parse as parseYaml } from "yaml";
|
|
3
|
+
import { ALL_AGENTS } from "./types.js";
|
|
4
|
+
const ALL_SELECTION = {
|
|
5
|
+
skills: "all",
|
|
6
|
+
mcpServers: "all",
|
|
7
|
+
memories: "all",
|
|
8
|
+
memoryMigration: "all",
|
|
9
|
+
mcpRoutes: {},
|
|
10
|
+
runtimeDelivery: {},
|
|
11
|
+
};
|
|
12
|
+
function parseNameSelection(value, field) {
|
|
13
|
+
if (value === undefined)
|
|
14
|
+
return "all";
|
|
15
|
+
if (value === "all" || value === "none")
|
|
16
|
+
return value;
|
|
17
|
+
if (Array.isArray(value) && value.every((item) => typeof item === "string" && item.length > 0)) {
|
|
18
|
+
return [...new Set(value)];
|
|
19
|
+
}
|
|
20
|
+
throw new Error(`${field} must be "all", "none", or a list of names`);
|
|
21
|
+
}
|
|
22
|
+
function parseRoutes(value) {
|
|
23
|
+
if (value === undefined)
|
|
24
|
+
return {};
|
|
25
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
26
|
+
throw new Error("mcp_routes must be a map of agent ids to route definitions");
|
|
27
|
+
}
|
|
28
|
+
const routes = {};
|
|
29
|
+
for (const [rawAgent, rawRoute] of Object.entries(value)) {
|
|
30
|
+
if (!ALL_AGENTS.includes(rawAgent)) {
|
|
31
|
+
throw new Error(`mcp_routes contains unknown agent "${rawAgent}"`);
|
|
32
|
+
}
|
|
33
|
+
if (typeof rawRoute !== "object" || rawRoute === null || Array.isArray(rawRoute)) {
|
|
34
|
+
throw new Error(`mcp_routes.${rawAgent} must be a route object`);
|
|
35
|
+
}
|
|
36
|
+
const route = rawRoute;
|
|
37
|
+
if (route.mode !== "direct" && route.mode !== "gateway" && route.mode !== "hub") {
|
|
38
|
+
throw new Error(`mcp_routes.${rawAgent}.mode must be direct, gateway, or hub`);
|
|
39
|
+
}
|
|
40
|
+
if (route.servers !== undefined && (!Array.isArray(route.servers) || !route.servers.every((item) => typeof item === "string" && item.length > 0))) {
|
|
41
|
+
throw new Error(`mcp_routes.${rawAgent}.servers must be a list of names`);
|
|
42
|
+
}
|
|
43
|
+
routes[rawAgent] = {
|
|
44
|
+
mode: route.mode,
|
|
45
|
+
...(route.servers ? { servers: [...new Set(route.servers)] } : {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return routes;
|
|
49
|
+
}
|
|
50
|
+
function parseRuntimeDelivery(value) {
|
|
51
|
+
if (value === undefined)
|
|
52
|
+
return {};
|
|
53
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
54
|
+
throw new Error("runtime_delivery must be a map of agent ids to native, mcp, or both");
|
|
55
|
+
}
|
|
56
|
+
const delivery = {};
|
|
57
|
+
for (const [rawAgent, rawMode] of Object.entries(value)) {
|
|
58
|
+
if (!ALL_AGENTS.includes(rawAgent)) {
|
|
59
|
+
throw new Error(`runtime_delivery contains unknown agent "${rawAgent}"`);
|
|
60
|
+
}
|
|
61
|
+
if (rawMode !== "native" && rawMode !== "mcp" && rawMode !== "both") {
|
|
62
|
+
throw new Error(`runtime_delivery.${rawAgent} must be native, mcp, or both`);
|
|
63
|
+
}
|
|
64
|
+
delivery[rawAgent] = rawMode;
|
|
65
|
+
}
|
|
66
|
+
return delivery;
|
|
67
|
+
}
|
|
68
|
+
export function parseCapabilitySelectionFile(path) {
|
|
69
|
+
if (!existsSync(path))
|
|
70
|
+
return { ok: false, error: `selection file does not exist: ${path}` };
|
|
71
|
+
try {
|
|
72
|
+
const parsed = (parseYaml(readFileSync(path, "utf8")) ?? {});
|
|
73
|
+
return {
|
|
74
|
+
ok: true,
|
|
75
|
+
selection: {
|
|
76
|
+
skills: parseNameSelection(parsed.skills, "skills"),
|
|
77
|
+
mcpServers: parseNameSelection(parsed.mcp_servers, "mcp_servers"),
|
|
78
|
+
memories: parseNameSelection(parsed.memories, "memories"),
|
|
79
|
+
memoryMigration: parsed.memory_migration === undefined || parsed.memory_migration === "all" ? "all" : parsed.memory_migration === "none" ? "none" : (() => { throw new Error('memory_migration must be "all" or "none"'); })(),
|
|
80
|
+
mcpRoutes: parseRoutes(parsed.mcp_routes),
|
|
81
|
+
runtimeDelivery: parseRuntimeDelivery(parsed.runtime_delivery),
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
return { ok: false, error: `could not parse selection file ${path}: ${err instanceof Error ? err.message : String(err)}` };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function defaultCapabilitySelection() {
|
|
90
|
+
return { ...ALL_SELECTION, mcpRoutes: {}, runtimeDelivery: {} };
|
|
91
|
+
}
|
|
92
|
+
export function resolveSelectedNames(selection, available) {
|
|
93
|
+
if (selection === "all")
|
|
94
|
+
return [...available];
|
|
95
|
+
if (selection === "none")
|
|
96
|
+
return [];
|
|
97
|
+
const availableSet = new Set(available);
|
|
98
|
+
return [...new Set(selection.filter((name) => availableSet.has(name)))];
|
|
99
|
+
}
|
|
100
|
+
export function selectionContains(selection, name) {
|
|
101
|
+
return selection === "all" || (selection !== "none" && selection.includes(name));
|
|
102
|
+
}
|
|
103
|
+
export function buildCapabilityInventory(source, canonical, nativeMemory = []) {
|
|
104
|
+
const canonicalSkills = new Set(canonical.skills.map((skill) => skill.name));
|
|
105
|
+
const canonicalMcp = new Set(Object.keys(canonical.mcp.servers));
|
|
106
|
+
return {
|
|
107
|
+
skills: source.skillNames.map((name) => ({
|
|
108
|
+
name,
|
|
109
|
+
displayName: name,
|
|
110
|
+
status: canonicalSkills.has(name) ? "unchanged" : "new",
|
|
111
|
+
})),
|
|
112
|
+
mcpServers: source.mcpServerNames.map((name) => ({
|
|
113
|
+
name,
|
|
114
|
+
displayName: name,
|
|
115
|
+
status: canonicalMcp.has(name) ? "unchanged" : "new",
|
|
116
|
+
})),
|
|
117
|
+
canonicalMemories: canonical.memories.map((memory) => ({
|
|
118
|
+
name: memory.name,
|
|
119
|
+
displayName: memory.name,
|
|
120
|
+
status: "unchanged",
|
|
121
|
+
})),
|
|
122
|
+
nativeMemory,
|
|
123
|
+
};
|
|
124
|
+
}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
* files on disk are its serialization, not the other way around.
|
|
6
6
|
*/
|
|
7
7
|
export type Transport = "stdio" | "http" | "sse";
|
|
8
|
-
export type
|
|
8
|
+
export type McpAuthMode = "oauth";
|
|
9
|
+
export type AgentId = "claude-code" | "codex" | "kiro" | "pi" | "kimi-code";
|
|
9
10
|
export declare const ALL_AGENTS: readonly AgentId[];
|
|
10
11
|
/**
|
|
11
12
|
* Every scopable item (skill, subagent, memory entry, MCP server) defaults
|
|
12
|
-
* to
|
|
13
|
+
* to all managed agents when `scope` is omitted — sharing everywhere is the
|
|
13
14
|
* common case Trellis exists for; restricting to specific agents is the
|
|
14
15
|
* exception and must be declared explicitly. See docs/architecture.md
|
|
15
16
|
* "Private / agent-specific capabilities".
|
|
@@ -26,6 +27,9 @@ export type Scope = AgentId[] | undefined;
|
|
|
26
27
|
export declare function resolveScope(scope: Scope, managedAgents: readonly AgentId[]): readonly AgentId[];
|
|
27
28
|
export interface McpServerDef {
|
|
28
29
|
transport: Transport;
|
|
30
|
+
/** Explicit authorization classification. Absence means ordinary MCP;
|
|
31
|
+
* Trellis never infers OAuth from a URL or a transient 401. */
|
|
32
|
+
auth?: McpAuthMode;
|
|
29
33
|
/** stdio only */
|
|
30
34
|
command?: string;
|
|
31
35
|
args?: string[];
|
|
@@ -109,6 +113,18 @@ export interface GatewayConfig {
|
|
|
109
113
|
*/
|
|
110
114
|
agents?: AgentId[];
|
|
111
115
|
}
|
|
116
|
+
export type McpRouteMode = "direct" | "gateway" | "hub";
|
|
117
|
+
export interface McpRoute {
|
|
118
|
+
mode: McpRouteMode;
|
|
119
|
+
/** Omit to expose every eligible canonical server for this agent. */
|
|
120
|
+
servers?: string[];
|
|
121
|
+
}
|
|
122
|
+
export type CapabilityDelivery = "native" | "mcp" | "both";
|
|
123
|
+
export interface McpRuntimeConfig {
|
|
124
|
+
/** Per-agent delivery for Trellis-owned capabilities and upstream MCP.
|
|
125
|
+
* Omitted means native, preserving the existing adapter behavior. */
|
|
126
|
+
delivery?: Partial<Record<AgentId, CapabilityDelivery>>;
|
|
127
|
+
}
|
|
112
128
|
export interface McpConfig {
|
|
113
129
|
servers: Record<string, McpServerDef>;
|
|
114
130
|
/**
|
|
@@ -127,7 +143,14 @@ export interface McpConfig {
|
|
|
127
143
|
* two mean different things and may both be set, in which case gateway
|
|
128
144
|
* wins per agent (trellis-mcp-gateway-hosting design.md D5/D6). */
|
|
129
145
|
gateway?: GatewayConfig;
|
|
146
|
+
/** Optional per-agent route overrides. Explicit routes take precedence
|
|
147
|
+
* over the legacy hub/gateway shorthand for the named agent. */
|
|
148
|
+
routes?: Partial<Record<AgentId, McpRoute>>;
|
|
149
|
+
/** Optional runtime delivery preferences. */
|
|
150
|
+
runtime?: McpRuntimeConfig;
|
|
130
151
|
}
|
|
152
|
+
export declare function capabilityDeliveryForAgent(agentId: AgentId, mcp: McpConfig): CapabilityDelivery;
|
|
153
|
+
export declare function usesNativeCapabilityDelivery(agentId: AgentId, mcp: McpConfig): boolean;
|
|
131
154
|
export interface SkillRef {
|
|
132
155
|
name: string;
|
|
133
156
|
/** Absolute path to the skill's directory. Must contain `SKILL.md` — the
|
package/dist/core/types.js
CHANGED
|
@@ -9,6 +9,7 @@ export const ALL_AGENTS = [
|
|
|
9
9
|
"codex",
|
|
10
10
|
"kiro",
|
|
11
11
|
"pi",
|
|
12
|
+
"kimi-code",
|
|
12
13
|
];
|
|
13
14
|
/**
|
|
14
15
|
* `managedAgents` is the hard outer boundary (trellis-managed-agents
|
|
@@ -22,3 +23,9 @@ export function resolveScope(scope, managedAgents) {
|
|
|
22
23
|
const candidates = scope ?? managedAgents;
|
|
23
24
|
return candidates.filter((id) => managedAgents.includes(id));
|
|
24
25
|
}
|
|
26
|
+
export function capabilityDeliveryForAgent(agentId, mcp) {
|
|
27
|
+
return mcp.runtime?.delivery?.[agentId] ?? "native";
|
|
28
|
+
}
|
|
29
|
+
export function usesNativeCapabilityDelivery(agentId, mcp) {
|
|
30
|
+
return capabilityDeliveryForAgent(agentId, mcp) !== "mcp";
|
|
31
|
+
}
|
package/dist/lib/backup.d.ts
CHANGED
|
@@ -39,10 +39,12 @@ export interface BackupManifest {
|
|
|
39
39
|
operations: BackupOperation[];
|
|
40
40
|
}
|
|
41
41
|
export interface BackupSession {
|
|
42
|
+
readonly runId: string;
|
|
42
43
|
writeFile(path: string, content: string): void;
|
|
43
44
|
createSymlink(path: string, linkTarget: string): Promise<void>;
|
|
44
45
|
repairSymlink(path: string, oldLinkTarget: string, newLinkTarget: string): Promise<void>;
|
|
45
46
|
removeSymlink(path: string, oldLinkTarget: string): Promise<void>;
|
|
47
|
+
hasOperations(): boolean;
|
|
46
48
|
/** Writes manifest.json. No-op (creates nothing) if zero operations
|
|
47
49
|
* were ever recorded. */
|
|
48
50
|
finalize(): void;
|