@jstn-sdk/ma 0.1.13 → 0.14.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/.agents/plugins/marketplace.json +0 -0
- package/.codex/agents/Architect.toml +1 -0
- package/.codex/agents/Auditor.toml +1 -0
- package/.codex/agents/Builder.toml +1 -0
- package/.codex/agents/Flow.toml +1 -0
- package/.codex/agents/Sage.toml +1 -0
- package/.codex/agents/Vibe.toml +1 -0
- package/.codex/hooks.json +12 -2
- package/.codex/prompts/enforcement.md +0 -0
- package/.codex/prompts/onboarding.md +6 -1
- package/.codex/prompts/release-rules.md +0 -0
- package/.codex/prompts/skill-contract.md +0 -0
- package/COVERAGE.md +7 -7
- package/DEMO.md +15 -6
- package/LICENSE +0 -0
- package/README.md +126 -793
- package/bin/ma.js +585 -97
- package/data/clone-data.ledger.json +0 -0
- package/data/clone-data.proof.json +0 -0
- package/data/clone-data.rvf +0 -0
- package/docs/README.md +8 -2
- package/docs/autonomous-tasks.md +46 -0
- package/docs/codex-integration.md +16 -0
- package/docs/getting-started.md +28 -4
- package/docs/installed-sdk.md +0 -0
- package/docs/mcp-setup.md +30 -1
- package/docs/qa/{release-issue-gates-0.1.13.json → release-issue-gates-0.14.0.json} +77 -77
- package/docs/qa/release-readiness-0.1.5.md +0 -0
- package/docs/qa/{release-readiness-0.1.13.md → release-readiness-0.14.0.md} +12 -12
- package/docs/quality.md +24 -0
- package/docs/reference/native-engineering-patterns.md +0 -0
- package/docs/reference/native-security-playbooks.md +0 -0
- package/docs/reference/native-source-selection.md +0 -0
- package/docs/reference/native-style-and-deslop.md +0 -0
- package/docs/release-spec.md +27 -14
- package/docs/skills.md +1 -1
- package/index.js +73 -0
- package/mcp/collections.json +0 -0
- package/mcp/fallback.json +0 -0
- package/mcp/local/code-intel.js +179 -41
- package/mcp/local/context.js +180 -0
- package/mcp/local/memory.js +17 -6
- package/mcp/local/playbooks.js +0 -0
- package/mcp/local/quality.js +39 -0
- package/mcp/local/state.js +7 -3
- package/mcp/local/team-run.js +8 -3
- package/mcp/local/trace.js +0 -0
- package/mcp/local-capabilities.json +9 -0
- package/mcp/native-playbooks.json +0 -0
- package/mcp/servers.json +0 -0
- package/package.json +26 -4
- package/plugins/meta-architect/.app.json +1 -1
- package/plugins/meta-architect/.codex-plugin/plugin.json +1 -1
- package/plugins/meta-architect/.mcp.json +1 -1
- package/plugins/meta-architect/README.md +2 -2
- package/plugins/meta-architect/obsidian/main.js +137 -4
- package/plugins/meta-architect/obsidian/manifest.json +1 -1
- package/plugins/meta-architect/obsidian/styles.css +0 -0
- package/plugins/meta-architect/skills/align/SKILL.md +0 -0
- package/plugins/meta-architect/skills/align/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/align/references/shared-language.md +0 -0
- package/plugins/meta-architect/skills/arch/SKILL.md +0 -0
- package/plugins/meta-architect/skills/arch/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/build/SKILL.md +0 -0
- package/plugins/meta-architect/skills/build/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/cleanup/SKILL.md +0 -0
- package/plugins/meta-architect/skills/cleanup/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/cleanup/references/style-and-deslop.md +0 -0
- package/plugins/meta-architect/skills/diagnose/SKILL.md +0 -0
- package/plugins/meta-architect/skills/diagnose/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/flow/SKILL.md +0 -0
- package/plugins/meta-architect/skills/flow/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/maestro/SKILL.md +0 -0
- package/plugins/meta-architect/skills/maestro/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/maestro/references/core-release-rules.md +0 -0
- package/plugins/meta-architect/skills/maestro/references/native-ingest-map.md +0 -0
- package/plugins/meta-architect/skills/sage/SKILL.md +0 -0
- package/plugins/meta-architect/skills/sage/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/sage/references/source-selection.md +0 -0
- package/plugins/meta-architect/skills/tdd/SKILL.md +0 -0
- package/plugins/meta-architect/skills/tdd/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/vet/SKILL.md +0 -0
- package/plugins/meta-architect/skills/vet/agents/openai.yaml +0 -0
- package/plugins/meta-architect/skills/vet/references/security-playbooks.md +0 -0
- package/plugins/meta-architect/skills/vibe/SKILL.md +0 -0
- package/plugins/meta-architect/skills/vibe/agents/openai.yaml +0 -0
- package/schemas/autonomous-task-queue.schema.json +56 -0
- package/schemas/handoff-packet.schema.json +36 -0
- package/schemas/setup-receipt.schema.json +28 -0
- package/schemas/skill-frontmatter.schema.json +20 -0
- package/schemas/support-bundle.schema.json +37 -0
- package/schemas/task-contract.schema.json +31 -0
- package/scripts/active-autonomy-hook.mjs +141 -11
- package/scripts/biome-staged.js +0 -0
- package/scripts/build-linux-packages.mjs +7 -3
- package/scripts/cleanup-test-fixtures.sh +55 -0
- package/scripts/context-hydration-hook.mjs +89 -0
- package/scripts/demo-smoke.js +285 -0
- package/scripts/doctor.js +10 -8
- package/scripts/install.sh +33 -4
- package/scripts/install.sh.sha256 +1 -0
- package/scripts/linux-package-lib.mjs +0 -0
- package/scripts/linux-package-smoke.mjs +2 -2
- package/scripts/package-size-check.mjs +26 -0
- package/scripts/plugin-sync.js +83 -17
- package/scripts/postinstall.js +17 -10
- package/scripts/prepack.js +9 -0
- package/scripts/ralph/prompt.md +0 -0
- package/scripts/release-metadata.js +0 -0
- package/scripts/release-sync.js +72 -7
- package/scripts/release-verify.js +100 -7
- package/scripts/setup-npmrc.js +48 -23
- package/scripts/skills-install.js +9 -2
- package/scripts/skills-manifest.js +2 -21
- package/scripts/skills-pack.js +2 -2
- package/scripts/skills-validate.js +5 -55
- package/skills/align/SKILL.md +0 -0
- package/skills/align/agents/openai.yaml +0 -0
- package/skills/align/references/shared-language.md +0 -0
- package/skills/arch/SKILL.md +0 -0
- package/skills/arch/agents/openai.yaml +0 -0
- package/skills/build/SKILL.md +0 -0
- package/skills/build/agents/openai.yaml +0 -0
- package/skills/cleanup/SKILL.md +0 -0
- package/skills/cleanup/agents/openai.yaml +0 -0
- package/skills/cleanup/references/style-and-deslop.md +0 -0
- package/skills/diagnose/SKILL.md +0 -0
- package/skills/diagnose/agents/openai.yaml +0 -0
- package/skills/flow/SKILL.md +0 -0
- package/skills/flow/agents/openai.yaml +0 -0
- package/skills/index.json +0 -0
- package/skills/maestro/SKILL.md +0 -0
- package/skills/maestro/agents/openai.yaml +0 -0
- package/skills/maestro/references/core-release-rules.md +0 -0
- package/skills/maestro/references/native-ingest-map.md +0 -0
- package/skills/sage/SKILL.md +0 -0
- package/skills/sage/agents/openai.yaml +0 -0
- package/skills/sage/references/source-selection.md +0 -0
- package/skills/tdd/SKILL.md +0 -0
- package/skills/tdd/agents/openai.yaml +0 -0
- package/skills/vet/SKILL.md +0 -0
- package/skills/vet/agents/openai.yaml +0 -0
- package/skills/vet/references/security-playbooks.md +0 -0
- package/skills/vibe/SKILL.md +0 -0
- package/skills/vibe/agents/openai.yaml +0 -0
- package/sprint/00-idea.md +0 -0
- package/sprint/01-architecture.md +0 -0
- package/sprint/02-oss-evidence.md +0 -0
- package/sprint/03-logic.md +0 -0
- package/sprint/04-security.md +0 -0
- package/sprint/05-dx-ux.md +0 -0
- package/sprint/06-build-plan.md +0 -0
- package/sprint/07-release.md +0 -0
- package/src/agents.js +279 -0
- package/src/bootstrap.js +252 -63
- package/src/build-gate.js +0 -0
- package/src/codex-app-server.js +291 -0
- package/src/decision-log.js +0 -0
- package/src/fs-utils.js +175 -4
- package/src/launcher.js +24 -21
- package/src/mcp-config.js +9 -1
- package/src/mcp-live-client.js +140 -7
- package/src/paths.js +6 -1
- package/src/policy.js +2 -2
- package/src/prelaunch.js +189 -0
- package/src/quality/ai-quality-orchestrator.js +328 -0
- package/src/release-issue-gates.js +62 -0
- package/src/release-operations.js +62 -0
- package/src/release-state.js +0 -0
- package/src/runtime/active-autonomy-core.js +0 -0
- package/src/runtime/agent-compat.js +31 -0
- package/src/runtime/alignment-sentinel.js +0 -0
- package/src/runtime/architect-review.js +248 -10
- package/src/runtime/autonomous-tasks.js +408 -0
- package/src/runtime/build-readiness.js +0 -0
- package/src/runtime/code-graph-rehearse.js +0 -0
- package/src/runtime/codeburn-core.js +112 -0
- package/src/runtime/context-authority.js +40 -0
- package/src/runtime/context-economy-core.js +40 -0
- package/src/runtime/continuity-graph.js +201 -0
- package/src/runtime/continuity-notes.js +71 -26
- package/src/runtime/core-source-ingest.js +0 -0
- package/src/runtime/detached-provider.js +82 -14
- package/src/runtime/doctor-report.js +18 -0
- package/src/runtime/environment-awareness-core.js +0 -0
- package/src/runtime/exposure-catalog.js +0 -0
- package/src/runtime/graphify-core.js +167 -0
- package/src/runtime/guidance-stack.js +9 -2
- package/src/runtime/handoff-packets.js +92 -0
- package/src/runtime/headroom-core.js +104 -0
- package/src/runtime/helper-orchestration-core.js +0 -0
- package/src/runtime/hook-profiles.js +20 -0
- package/src/runtime/learning-loop-core.js +110 -4
- package/src/runtime/live-agent-verification.js +122 -0
- package/src/runtime/maestro-events.js +0 -0
- package/src/runtime/maestro-manager.js +0 -0
- package/src/runtime/maestro-output.js +58 -0
- package/src/runtime/maestro-state.js +0 -0
- package/src/runtime/managed-markdown.js +29 -0
- package/src/runtime/mcp-authority.js +147 -0
- package/src/runtime/mcp-policy.js +0 -0
- package/src/runtime/obsidian-integration-core.js +255 -17
- package/src/runtime/obsidian-plugin-bridge.js +344 -38
- package/src/runtime/orchestrator.js +0 -0
- package/src/runtime/pi-maestro-core.js +89 -0
- package/src/runtime/preferences.js +79 -0
- package/src/runtime/project-context.js +636 -0
- package/src/runtime/prompt-strategy-core.js +0 -0
- package/src/runtime/quorum-review.js +0 -0
- package/src/runtime/ralph-execution-core.js +0 -0
- package/src/runtime/redaction-gateway.js +218 -19
- package/src/runtime/runtime-state.js +94 -2
- package/src/runtime/schema-migrations.js +162 -0
- package/src/runtime/semantic-recording-core.js +0 -0
- package/src/runtime/signal-hooks.js +13 -0
- package/src/runtime/skills-registry-export.js +92 -8
- package/src/runtime/startup-path.js +0 -0
- package/src/runtime/task-contracts.js +83 -0
- package/src/runtime/universal-plugin-broker-core.js +373 -50
- package/src/runtime/workspace-intelligence-runtime.js +0 -0
- package/src/runtime/workspace-virtualizer.js +0 -0
- package/src/runtime/workspaces.js +0 -0
- package/src/runtime-artifacts.js +0 -0
- package/src/setup-lifecycle.js +359 -0
- package/src/skill-frontmatter.js +78 -0
- package/src/skill-installer.js +226 -71
- package/src/skills.js +297 -19
- package/src/state-sync.js +0 -0
- package/src/test-fixtures.js +543 -0
- package/src/tui/grid.js +67 -0
- package/src/tui/status-grid.js +23 -0
- package/support-bundle.json +114 -0
- package/templates/AGENTS.md +6 -2
- package/templates/catalog-manifest.json +0 -0
- package/templates/model-instructions/core.md +0 -0
- package/templates/model-instructions/release.md +0 -0
- package/templates/model-instructions/security.md +0 -0
- package/templates/quality/ai-quality-rules.yml +37 -0
- package/docs/assets/image/Screenshot(1).png +0 -0
- package/docs/assets/image/Screenshot(2).png +0 -0
- package/docs/assets/image/Screenshot(3).png +0 -0
- package/docs/assets/image/Screenshot(4).png +0 -0
- package/docs/assets/image/Screenshot(5).png +0 -0
- package/docs/assets/image/Screenshot(6).png +0 -0
- package/docs/assets/image/Screenshot(7).png +0 -0
- package/docs/assets/image/Screenshot(8).png +0 -0
- package/docs/assets/image/Screenshot(9).png +0 -0
- package/docs/assets/meta-architect-logo.png +0 -0
- package/docs/assets/meta-architect-logo.svg +0 -8
- package/docs/onboarding.md +0 -81
- package/docs/prompt-guidance-contract.md +0 -17
- package/docs/prompt-guidance-fragments/active-autonomy-core.md +0 -7
- package/docs/skills-publishing.md +0 -255
|
@@ -10,8 +10,37 @@ export const obsidianPluginQueuePath = "Meta-Architect/Plugin Requests/request-q
|
|
|
10
10
|
export const obsidianPluginActiveContextPath =
|
|
11
11
|
"Meta-Architect/Plugin Context/Active Note Context.md";
|
|
12
12
|
export const obsidianPluginAttachmentDir = "Meta-Architect/Attachments";
|
|
13
|
+
const defaultAttachmentMaxBytes = 1_048_576;
|
|
14
|
+
const defaultAttachmentContentTypes = new Set([
|
|
15
|
+
"application/octet-stream",
|
|
16
|
+
"application/json",
|
|
17
|
+
"text/plain",
|
|
18
|
+
"text/markdown",
|
|
19
|
+
"image/png",
|
|
20
|
+
"image/jpeg",
|
|
21
|
+
"image/webp",
|
|
22
|
+
]);
|
|
13
23
|
|
|
14
24
|
const forbiddenMaTargets = [".ma/release.json", ".ma/decisions.json", ".ma/plans/", ".ma/specs/"];
|
|
25
|
+
const protocolMaxPayloadBytes = 64 * 1024;
|
|
26
|
+
const protocolActions = new Set(["queue", "capture", "open"]);
|
|
27
|
+
const protocolFields = new Set([
|
|
28
|
+
"action",
|
|
29
|
+
"open",
|
|
30
|
+
"path",
|
|
31
|
+
"note_path",
|
|
32
|
+
"from",
|
|
33
|
+
"to",
|
|
34
|
+
"content",
|
|
35
|
+
"data",
|
|
36
|
+
"content_type",
|
|
37
|
+
"overwrite",
|
|
38
|
+
"id",
|
|
39
|
+
"authority",
|
|
40
|
+
"records_as",
|
|
41
|
+
"protocol_token",
|
|
42
|
+
"protocol_token_hash",
|
|
43
|
+
]);
|
|
15
44
|
|
|
16
45
|
const pluginCapabilityNames = [
|
|
17
46
|
"in_app_plugin_surface",
|
|
@@ -59,6 +88,13 @@ export async function installObsidianPlugin({
|
|
|
59
88
|
if (!vaultPath || typeof vaultPath !== "string") {
|
|
60
89
|
throw new Error("Obsidian plugin install requires vaultPath");
|
|
61
90
|
}
|
|
91
|
+
if (
|
|
92
|
+
typeof pluginId !== "string" ||
|
|
93
|
+
!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(pluginId) ||
|
|
94
|
+
path.isAbsolute(pluginId)
|
|
95
|
+
) {
|
|
96
|
+
throw new Error(`Invalid Obsidian plugin id: ${pluginId}`);
|
|
97
|
+
}
|
|
62
98
|
const resolvedVaultPath = path.resolve(vaultPath);
|
|
63
99
|
const vaultStat = await fs.stat(resolvedVaultPath);
|
|
64
100
|
if (!vaultStat.isDirectory()) {
|
|
@@ -67,28 +103,73 @@ export async function installObsidianPlugin({
|
|
|
67
103
|
|
|
68
104
|
const sourceDir = path.join(packageRoot, "plugins", "meta-architect", "obsidian");
|
|
69
105
|
const targetDir = path.join(resolvedVaultPath, ".obsidian", "plugins", pluginId);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const manifest = JSON.parse(await fs.readFile(path.join(targetDir, "manifest.json"), "utf8"));
|
|
74
|
-
if (manifest.id !== pluginId) {
|
|
75
|
-
throw new Error(`Installed Obsidian plugin manifest id mismatch: ${manifest.id}`);
|
|
106
|
+
const relativeTarget = path.relative(resolvedVaultPath, path.resolve(targetDir));
|
|
107
|
+
if (relativeTarget.startsWith(`..${path.sep}`) || path.isAbsolute(relativeTarget)) {
|
|
108
|
+
throw new Error("Obsidian plugin target must remain inside the vault");
|
|
76
109
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
110
|
+
await assertNoSymlinkComponents(resolvedVaultPath, targetDir);
|
|
111
|
+
await assertNoSymlinkComponents(
|
|
112
|
+
resolvedVaultPath,
|
|
113
|
+
path.join(resolvedVaultPath, ".obsidian", "community-plugins.json"),
|
|
114
|
+
);
|
|
115
|
+
const sourceMainPath = path.join(sourceDir, "main.js");
|
|
116
|
+
const sourceManifestPath = path.join(sourceDir, "manifest.json");
|
|
117
|
+
const configPath = path.join(resolvedVaultPath, ".obsidian", "community-plugins.json");
|
|
118
|
+
const originalConfig = await readOptionalFile(configPath);
|
|
119
|
+
if (enable) parseCommunityPlugins(originalConfig);
|
|
120
|
+
const stagingDir = `${targetDir}.staging-${Date.now()}`;
|
|
121
|
+
const backupDir = `${targetDir}.backup-${Date.now()}`;
|
|
122
|
+
let hadTarget = false;
|
|
123
|
+
let installed = false;
|
|
124
|
+
try {
|
|
125
|
+
await fs.mkdir(path.dirname(targetDir), { recursive: true });
|
|
126
|
+
await fs.cp(sourceDir, stagingDir, { recursive: true, force: true });
|
|
80
127
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
128
|
+
const manifest = JSON.parse(await fs.readFile(path.join(stagingDir, "manifest.json"), "utf8"));
|
|
129
|
+
if (manifest.id !== pluginId) {
|
|
130
|
+
throw new Error(`Installed Obsidian plugin manifest id mismatch: ${manifest.id}`);
|
|
131
|
+
}
|
|
132
|
+
const sourceHash = await hashFile(sourceMainPath);
|
|
133
|
+
const installedHash = await hashFile(path.join(stagingDir, "main.js"));
|
|
134
|
+
const sourceManifestHash = await hashFile(sourceManifestPath);
|
|
135
|
+
const installedManifestHash = await hashFile(path.join(stagingDir, "manifest.json"));
|
|
136
|
+
if (sourceHash !== installedHash || sourceManifestHash !== installedManifestHash) {
|
|
137
|
+
throw new Error("Installed Obsidian plugin integrity mismatch");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
hadTarget = await pathExists(targetDir);
|
|
141
|
+
if (hadTarget) await fs.rename(targetDir, backupDir);
|
|
142
|
+
await fs.rename(stagingDir, targetDir);
|
|
143
|
+
installed = true;
|
|
144
|
+
const enabledPlugins = enable
|
|
145
|
+
? await enableObsidianCommunityPlugin({ vaultPath: resolvedVaultPath, pluginId })
|
|
146
|
+
: null;
|
|
147
|
+
|
|
148
|
+
const installedFiles = await listPluginFiles(targetDir);
|
|
149
|
+
await fs.rm(backupDir, { recursive: true, force: true });
|
|
150
|
+
return {
|
|
151
|
+
record_type: "obsidian_plugin_install",
|
|
152
|
+
records_as: "vault_context",
|
|
153
|
+
build_evidence: false,
|
|
154
|
+
enabled: enable,
|
|
155
|
+
community_plugins: enabledPlugins,
|
|
156
|
+
plugin_id: pluginId,
|
|
157
|
+
vault_path: resolvedVaultPath,
|
|
158
|
+
plugin_path: targetDir,
|
|
159
|
+
source_hash: sourceHash,
|
|
160
|
+
installed_hash: installedHash,
|
|
161
|
+
source_manifest_hash: sourceManifestHash,
|
|
162
|
+
installed_manifest_hash: installedManifestHash,
|
|
163
|
+
installed_files: installedFiles,
|
|
164
|
+
};
|
|
165
|
+
} catch (error) {
|
|
166
|
+
await fs.rm(stagingDir, { recursive: true, force: true });
|
|
167
|
+
if (installed) await fs.rm(targetDir, { recursive: true, force: true });
|
|
168
|
+
if (hadTarget) await fs.rename(backupDir, targetDir);
|
|
169
|
+
if (originalConfig === null) await fs.rm(configPath, { force: true });
|
|
170
|
+
else await fs.writeFile(configPath, originalConfig, "utf8");
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
92
173
|
}
|
|
93
174
|
|
|
94
175
|
export async function registerMetaArchitectObsidianPluginRuntime({
|
|
@@ -261,6 +342,7 @@ export async function applyObsidianFrontmatterAuthority({
|
|
|
261
342
|
}
|
|
262
343
|
|
|
263
344
|
export async function renameObsidianFileSafely({ app, file, newPath } = {}) {
|
|
345
|
+
assertSafeVaultPath(file?.path);
|
|
264
346
|
assertSafeVaultPath(newPath);
|
|
265
347
|
if (!app?.fileManager?.renameFile) {
|
|
266
348
|
throw new Error("Obsidian FileManager.renameFile is required for link-safe rename/move");
|
|
@@ -302,17 +384,28 @@ export function registerObsidianProtocolHandlers({
|
|
|
302
384
|
return { registered: false, handler: "ma", reason: "protocol_handler_unavailable" };
|
|
303
385
|
}
|
|
304
386
|
plugin.registerObsidianProtocolHandler("ma", async (params = {}) => {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
387
|
+
const request = validateProtocolRequest(params);
|
|
388
|
+
if (request.action === "queue") {
|
|
389
|
+
if (!hasProtocolAuthority(params, settings)) {
|
|
390
|
+
throw new Error("obsidian_protocol_queue_authority_required");
|
|
391
|
+
}
|
|
392
|
+
return writeQueueRequestToVault({ app, request, settings });
|
|
308
393
|
}
|
|
309
|
-
if (
|
|
394
|
+
if (request.action === "capture") {
|
|
395
|
+
if (!hasProtocolAuthority(request, settings)) {
|
|
396
|
+
throw new Error("obsidian_protocol_authority_required");
|
|
397
|
+
}
|
|
310
398
|
await captureActiveNoteToVault({ app, MarkdownView, settings });
|
|
311
|
-
return;
|
|
399
|
+
return protocolReceipt("capture", request);
|
|
400
|
+
}
|
|
401
|
+
if (!hasProtocolAuthority(request, settings)) {
|
|
402
|
+
throw new Error("obsidian_protocol_authority_required");
|
|
312
403
|
}
|
|
313
|
-
const target =
|
|
404
|
+
const target = request.path ?? "Meta-Architect/Map of Content.md";
|
|
405
|
+
assertSafeVaultPath(target);
|
|
314
406
|
const file = app?.vault?.getAbstractFileByPath?.(target);
|
|
315
407
|
if (file) await app?.workspace?.getLeaf?.(true)?.openFile?.(file);
|
|
408
|
+
return protocolReceipt("open", request);
|
|
316
409
|
});
|
|
317
410
|
return { registered: true, handler: "ma", supports: ["open", "queue", "capture"] };
|
|
318
411
|
}
|
|
@@ -398,18 +491,39 @@ export async function writeObsidianAttachment({
|
|
|
398
491
|
attachmentPath,
|
|
399
492
|
data,
|
|
400
493
|
contentType = "application/octet-stream",
|
|
494
|
+
overwrite = false,
|
|
495
|
+
maxBytes = defaultAttachmentMaxBytes,
|
|
496
|
+
allowedContentTypes = defaultAttachmentContentTypes,
|
|
497
|
+
requestId = null,
|
|
498
|
+
sourceAction = null,
|
|
401
499
|
} = {}) {
|
|
402
500
|
assertSafeVaultPath(attachmentPath);
|
|
501
|
+
assertMaOwnedPath(attachmentPath, "Meta-Architect/Attachments/");
|
|
403
502
|
if (!attachmentPath) {
|
|
404
503
|
throw new Error("Obsidian attachment write requires attachmentPath");
|
|
405
504
|
}
|
|
505
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
|
|
506
|
+
throw new Error("attachment_max_bytes_invalid");
|
|
507
|
+
}
|
|
406
508
|
const bytes = toUint8Array(data);
|
|
509
|
+
if (bytes.byteLength > maxBytes) {
|
|
510
|
+
throw new Error(`attachment_too_large:${attachmentPath}:${bytes.byteLength}`);
|
|
511
|
+
}
|
|
512
|
+
if (!isAllowedAttachmentContentType(contentType, allowedContentTypes)) {
|
|
513
|
+
throw new Error(`attachment_type_forbidden:${contentType}`);
|
|
514
|
+
}
|
|
407
515
|
const parent = attachmentPath.includes("/")
|
|
408
516
|
? attachmentPath.slice(0, attachmentPath.lastIndexOf("/"))
|
|
409
517
|
: "";
|
|
410
518
|
if (parent) await ensureVaultFolder(app, parent);
|
|
519
|
+
const existing = app?.vault?.getAbstractFileByPath?.(attachmentPath);
|
|
520
|
+
if (existing && !overwrite) {
|
|
521
|
+
throw new Error(`attachment_exists:${attachmentPath}`);
|
|
522
|
+
}
|
|
411
523
|
|
|
412
|
-
if (app?.vault?.
|
|
524
|
+
if (existing && app?.vault?.adapter?.writeBinary) {
|
|
525
|
+
await app.vault.adapter.writeBinary(attachmentPath, bytes);
|
|
526
|
+
} else if (app?.vault?.createBinary) {
|
|
413
527
|
await app.vault.createBinary(
|
|
414
528
|
attachmentPath,
|
|
415
529
|
bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength),
|
|
@@ -428,6 +542,8 @@ export async function writeObsidianAttachment({
|
|
|
428
542
|
content_type: contentType,
|
|
429
543
|
byte_length: bytes.byteLength,
|
|
430
544
|
sha256: createHash("sha256").update(bytes).digest("hex"),
|
|
545
|
+
request_id: requestId,
|
|
546
|
+
source_action: sourceAction,
|
|
431
547
|
};
|
|
432
548
|
}
|
|
433
549
|
|
|
@@ -442,12 +558,21 @@ export async function drainObsidianPluginRequestQueue({
|
|
|
442
558
|
}
|
|
443
559
|
const processed = [];
|
|
444
560
|
const refused = [];
|
|
561
|
+
const refusedQueueItems = [];
|
|
445
562
|
|
|
446
563
|
for (const request of queue) {
|
|
447
564
|
try {
|
|
448
565
|
if (!request || typeof request !== "object") {
|
|
449
566
|
throw new Error("invalid_request");
|
|
450
567
|
}
|
|
568
|
+
if (
|
|
569
|
+
["capture_active_note", "create_note", "rename_note", "write_attachment"].includes(
|
|
570
|
+
request.action,
|
|
571
|
+
) &&
|
|
572
|
+
!hasProtocolAuthority(request, settings)
|
|
573
|
+
) {
|
|
574
|
+
throw new Error("obsidian_queue_authority_required");
|
|
575
|
+
}
|
|
451
576
|
const target = request.note_path ?? request.path ?? "";
|
|
452
577
|
if (target) assertSafeVaultPath(target);
|
|
453
578
|
switch (request.action) {
|
|
@@ -496,6 +621,11 @@ export async function drainObsidianPluginRequestQueue({
|
|
|
496
621
|
attachmentPath: target,
|
|
497
622
|
data: request.data ?? "",
|
|
498
623
|
contentType: request.content_type,
|
|
624
|
+
overwrite: request.overwrite ?? false,
|
|
625
|
+
maxBytes: defaultAttachmentMaxBytes,
|
|
626
|
+
allowedContentTypes: defaultAttachmentContentTypes,
|
|
627
|
+
requestId: request.id ?? null,
|
|
628
|
+
sourceAction: request.action,
|
|
499
629
|
}),
|
|
500
630
|
});
|
|
501
631
|
break;
|
|
@@ -503,10 +633,19 @@ export async function drainObsidianPluginRequestQueue({
|
|
|
503
633
|
throw new Error(`unsupported_action:${request.action}`);
|
|
504
634
|
}
|
|
505
635
|
} catch (error) {
|
|
636
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
637
|
+
const previousAttempts = Number.isInteger(request?.attempts) ? request.attempts : 0;
|
|
638
|
+
refusedQueueItems.push({
|
|
639
|
+
...safeClone(request),
|
|
640
|
+
queue_status: "refused",
|
|
641
|
+
refusal_reason: reason,
|
|
642
|
+
attempts: previousAttempts + 1,
|
|
643
|
+
last_attempt_at: new Date().toISOString(),
|
|
644
|
+
});
|
|
506
645
|
refused.push({
|
|
507
646
|
request_id: request?.id ?? null,
|
|
508
647
|
action: request?.action ?? null,
|
|
509
|
-
reason
|
|
648
|
+
reason,
|
|
510
649
|
records_as: "vault_context",
|
|
511
650
|
build_evidence: false,
|
|
512
651
|
});
|
|
@@ -520,6 +659,7 @@ export async function drainObsidianPluginRequestQueue({
|
|
|
520
659
|
drained_at: new Date().toISOString(),
|
|
521
660
|
processed,
|
|
522
661
|
refused,
|
|
662
|
+
refusedQueueItems,
|
|
523
663
|
};
|
|
524
664
|
}
|
|
525
665
|
|
|
@@ -532,7 +672,7 @@ async function captureActiveNoteToVault({ app, Notice, MarkdownView, settings =
|
|
|
532
672
|
file: activeFile,
|
|
533
673
|
project: settings.projectName ?? "Meta-Architect",
|
|
534
674
|
sourceWorkspace: settings.sourceWorkspace ?? null,
|
|
535
|
-
})
|
|
675
|
+
});
|
|
536
676
|
}
|
|
537
677
|
const content = renderActiveContextNote(context);
|
|
538
678
|
const result = await createVaultContextNote({
|
|
@@ -547,7 +687,22 @@ async function captureActiveNoteToVault({ app, Notice, MarkdownView, settings =
|
|
|
547
687
|
return { ...result, active_context: context };
|
|
548
688
|
}
|
|
549
689
|
|
|
550
|
-
|
|
690
|
+
let obsidianQueueBusy = Promise.resolve();
|
|
691
|
+
|
|
692
|
+
function withObsidianQueueLock(task) {
|
|
693
|
+
const previous = obsidianQueueBusy;
|
|
694
|
+
let release;
|
|
695
|
+
obsidianQueueBusy = new Promise((resolve) => {
|
|
696
|
+
release = resolve;
|
|
697
|
+
});
|
|
698
|
+
return previous.then(task).finally(release);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
async function drainQueueFromVault(args) {
|
|
702
|
+
return withObsidianQueueLock(() => drainQueueFromVaultUnlocked(args));
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
async function drainQueueFromVaultUnlocked({ app, MarkdownView, settings = {} }) {
|
|
551
706
|
const queuePath = settings.queuePath ?? obsidianPluginQueuePath;
|
|
552
707
|
const file = app?.vault?.getAbstractFileByPath?.(queuePath);
|
|
553
708
|
if (!file) {
|
|
@@ -556,28 +711,42 @@ async function drainQueueFromVault({ app, MarkdownView, settings = {} }) {
|
|
|
556
711
|
const raw = await app.vault.read(file);
|
|
557
712
|
const queue = JSON.parse(raw);
|
|
558
713
|
const result = await drainObsidianPluginRequestQueue({ app, queue, MarkdownView, settings });
|
|
559
|
-
|
|
714
|
+
const nextQueue = `${JSON.stringify(result.refusedQueueItems, null, 2)}\n`;
|
|
715
|
+
if (raw !== nextQueue) await app.vault.modify(file, nextQueue);
|
|
560
716
|
return result;
|
|
561
717
|
}
|
|
562
718
|
|
|
563
|
-
async function writeQueueRequestToVault(
|
|
719
|
+
async function writeQueueRequestToVault(args) {
|
|
720
|
+
return withObsidianQueueLock(() => writeQueueRequestToVaultUnlocked(args));
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
async function writeQueueRequestToVaultUnlocked({ app, request, settings = {} }) {
|
|
724
|
+
if (!hasProtocolAuthority(request, settings)) {
|
|
725
|
+
throw new Error("obsidian_protocol_queue_authority_required");
|
|
726
|
+
}
|
|
564
727
|
const queuePath = obsidianPluginQueuePath;
|
|
565
728
|
await ensureVaultFolder(app, path.dirname(queuePath));
|
|
566
729
|
const file = app?.vault?.getAbstractFileByPath?.(queuePath);
|
|
567
730
|
const existing = file ? JSON.parse(await app.vault.read(file)) : [];
|
|
731
|
+
const queuedRequest = Object.fromEntries(
|
|
732
|
+
Object.entries(request).filter(([key]) => key !== "protocol_token"),
|
|
733
|
+
);
|
|
568
734
|
existing.push({
|
|
569
|
-
...
|
|
735
|
+
...queuedRequest,
|
|
570
736
|
id: request.id ?? `ma-${Date.now()}`,
|
|
571
737
|
queued_at: new Date().toISOString(),
|
|
572
738
|
records_as: "vault_context",
|
|
739
|
+
protocol_token_hash: request.protocol_token_hash ?? tokenHash(request.protocol_token),
|
|
573
740
|
});
|
|
574
741
|
const content = `${JSON.stringify(existing, null, 2)}\n`;
|
|
575
742
|
if (file) await app.vault.modify(file, content);
|
|
576
743
|
else await app.vault.create(queuePath, content);
|
|
744
|
+
return protocolReceipt("queue", request);
|
|
577
745
|
}
|
|
578
746
|
|
|
579
747
|
async function createVaultContextNote({ app, notePath, content, overwrite = true }) {
|
|
580
748
|
assertSafeVaultPath(notePath);
|
|
749
|
+
assertMaOwnedPath(notePath, "Meta-Architect/");
|
|
581
750
|
if (typeof content !== "string" || content.trim().length === 0) {
|
|
582
751
|
throw new Error("vault_context note content required");
|
|
583
752
|
}
|
|
@@ -657,6 +826,9 @@ function assertSafeVaultPath(targetPath) {
|
|
|
657
826
|
if (normalized.includes("../") || normalized === ".." || normalized.startsWith("..")) {
|
|
658
827
|
throw new Error("path_traversal_forbidden");
|
|
659
828
|
}
|
|
829
|
+
if ([".obsidian/", ".trash/", ".git/"].some((prefix) => normalized.startsWith(prefix))) {
|
|
830
|
+
throw new Error("vault_control_path_forbidden");
|
|
831
|
+
}
|
|
660
832
|
for (const forbidden of forbiddenMaTargets) {
|
|
661
833
|
if (normalized === forbidden || normalized.startsWith(forbidden)) {
|
|
662
834
|
throw new Error(`forbidden_authoritative_mutation:${forbidden}`);
|
|
@@ -664,6 +836,118 @@ function assertSafeVaultPath(targetPath) {
|
|
|
664
836
|
}
|
|
665
837
|
}
|
|
666
838
|
|
|
839
|
+
function assertMaOwnedPath(targetPath, prefix) {
|
|
840
|
+
const normalized = targetPath.replaceAll("\\", "/").replace(/^\/+/, "");
|
|
841
|
+
if (!normalized.startsWith(prefix)) throw new Error(`ma_owned_path_required:${prefix}`);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function hasVaultContextAuthority(params, settings) {
|
|
845
|
+
const requiredAuthority = settings.protocolAuthority ?? "vault_context";
|
|
846
|
+
return params.authority === requiredAuthority;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function hasProtocolAuthority(params, settings) {
|
|
850
|
+
return (
|
|
851
|
+
Boolean(settings.protocolToken) &&
|
|
852
|
+
(params?.protocol_token === settings.protocolToken ||
|
|
853
|
+
params?.protocol_token_hash === tokenHash(settings.protocolToken)) &&
|
|
854
|
+
hasVaultContextAuthority(params, settings)
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function validateProtocolRequest(params) {
|
|
859
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
860
|
+
throw new Error("obsidian_protocol_request_invalid");
|
|
861
|
+
}
|
|
862
|
+
const serialized = JSON.stringify(params);
|
|
863
|
+
if (Buffer.byteLength(serialized, "utf8") > protocolMaxPayloadBytes) {
|
|
864
|
+
throw new Error("obsidian_protocol_payload_too_large");
|
|
865
|
+
}
|
|
866
|
+
for (const key of Object.keys(params)) {
|
|
867
|
+
if (!protocolFields.has(key)) throw new Error(`obsidian_protocol_field_forbidden:${key}`);
|
|
868
|
+
}
|
|
869
|
+
const action = params.action ?? (params.open === "active" ? "capture" : null);
|
|
870
|
+
if (!protocolActions.has(action)) throw new Error("obsidian_protocol_action_forbidden");
|
|
871
|
+
if (action === "open" && params.open === "active") {
|
|
872
|
+
throw new Error("obsidian_protocol_action_invalid");
|
|
873
|
+
}
|
|
874
|
+
const request = { ...params, action };
|
|
875
|
+
for (const target of [request.path, request.note_path, request.from, request.to]) {
|
|
876
|
+
if (target !== undefined) assertSafeVaultPath(target);
|
|
877
|
+
}
|
|
878
|
+
if (request.content !== undefined && typeof request.content !== "string") {
|
|
879
|
+
throw new Error("obsidian_protocol_content_invalid");
|
|
880
|
+
}
|
|
881
|
+
if (request.data !== undefined && typeof request.data !== "string") {
|
|
882
|
+
throw new Error("obsidian_protocol_data_invalid");
|
|
883
|
+
}
|
|
884
|
+
return request;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
function protocolReceipt(action, request) {
|
|
888
|
+
return {
|
|
889
|
+
record_type: "obsidian_protocol_action",
|
|
890
|
+
records_as: "vault_context",
|
|
891
|
+
build_evidence: false,
|
|
892
|
+
action,
|
|
893
|
+
request_id: request.id ?? null,
|
|
894
|
+
status: "accepted",
|
|
895
|
+
authority: request.authority,
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
function tokenHash(token) {
|
|
900
|
+
return createHash("sha256")
|
|
901
|
+
.update(String(token ?? ""))
|
|
902
|
+
.digest("hex");
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
async function hashFile(filePath) {
|
|
906
|
+
return createHash("sha256")
|
|
907
|
+
.update(await fs.readFile(filePath))
|
|
908
|
+
.digest("hex");
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
async function assertNoSymlinkComponents(root, target) {
|
|
912
|
+
const rootPath = path.resolve(root);
|
|
913
|
+
const rootStat = await fs.lstat(rootPath);
|
|
914
|
+
if (rootStat.isSymbolicLink()) throw new Error("Obsidian vault root cannot be a symlink");
|
|
915
|
+
let current = rootPath;
|
|
916
|
+
for (const part of path
|
|
917
|
+
.relative(rootPath, path.resolve(target))
|
|
918
|
+
.split(path.sep)
|
|
919
|
+
.filter(Boolean)) {
|
|
920
|
+
current = path.join(current, part);
|
|
921
|
+
const stat = await fs.lstat(current).catch((error) => {
|
|
922
|
+
if (error?.code === "ENOENT") return null;
|
|
923
|
+
throw error;
|
|
924
|
+
});
|
|
925
|
+
if (stat?.isSymbolicLink())
|
|
926
|
+
throw new Error(`Obsidian plugin path cannot traverse a symlink: ${current}`);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function isAllowedAttachmentContentType(contentType, allowedContentTypes) {
|
|
931
|
+
if (!contentType || typeof contentType !== "string") {
|
|
932
|
+
return false;
|
|
933
|
+
}
|
|
934
|
+
if (allowedContentTypes instanceof Set) {
|
|
935
|
+
return allowedContentTypes.has(contentType);
|
|
936
|
+
}
|
|
937
|
+
if (Array.isArray(allowedContentTypes)) {
|
|
938
|
+
return allowedContentTypes.includes(contentType);
|
|
939
|
+
}
|
|
940
|
+
return defaultAttachmentContentTypes.has(contentType);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
function safeClone(value) {
|
|
944
|
+
try {
|
|
945
|
+
return structuredClone(value);
|
|
946
|
+
} catch {
|
|
947
|
+
return JSON.parse(JSON.stringify(value ?? null));
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
667
951
|
function toUint8Array(data) {
|
|
668
952
|
if (data instanceof Uint8Array) return data;
|
|
669
953
|
if (data instanceof ArrayBuffer) return new Uint8Array(data);
|
|
@@ -707,11 +991,8 @@ async function listPluginFiles(root) {
|
|
|
707
991
|
|
|
708
992
|
async function enableObsidianCommunityPlugin({ vaultPath, pluginId }) {
|
|
709
993
|
const configPath = path.join(vaultPath, ".obsidian", "community-plugins.json");
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
const parsed = JSON.parse(await fs.readFile(configPath, "utf8"));
|
|
713
|
-
if (Array.isArray(parsed)) enabled = parsed.filter((entry) => typeof entry === "string");
|
|
714
|
-
} catch {}
|
|
994
|
+
await assertNoSymlinkComponents(vaultPath, configPath);
|
|
995
|
+
const enabled = parseCommunityPlugins(await readOptionalFile(configPath));
|
|
715
996
|
if (!enabled.includes(pluginId)) {
|
|
716
997
|
enabled.push(pluginId);
|
|
717
998
|
}
|
|
@@ -719,6 +1000,31 @@ async function enableObsidianCommunityPlugin({ vaultPath, pluginId }) {
|
|
|
719
1000
|
return enabled;
|
|
720
1001
|
}
|
|
721
1002
|
|
|
1003
|
+
async function readOptionalFile(filePath) {
|
|
1004
|
+
try {
|
|
1005
|
+
return await fs.readFile(filePath, "utf8");
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
if (error?.code === "ENOENT") return null;
|
|
1008
|
+
throw error;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
async function pathExists(filePath) {
|
|
1013
|
+
try {
|
|
1014
|
+
await fs.lstat(filePath);
|
|
1015
|
+
return true;
|
|
1016
|
+
} catch (error) {
|
|
1017
|
+
if (error?.code === "ENOENT") return false;
|
|
1018
|
+
throw error;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
function parseCommunityPlugins(content) {
|
|
1023
|
+
if (content === null) return [];
|
|
1024
|
+
const parsed = JSON.parse(content);
|
|
1025
|
+
return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === "string") : [];
|
|
1026
|
+
}
|
|
1027
|
+
|
|
722
1028
|
function createMetaArchitectSettingTab(app, plugin, PluginSettingTab, Setting) {
|
|
723
1029
|
const instance = new PluginSettingTab(app, plugin);
|
|
724
1030
|
instance.display = () => {
|
|
File without changes
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export function isPiMaestroEnabled(env = process.env) {
|
|
2
|
+
return env.MA_MAESTRO_PI === "1";
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function assertPiMaestroToolAllowed(
|
|
6
|
+
name,
|
|
7
|
+
{ controlPlaneReady = true, buildReadiness } = {},
|
|
8
|
+
) {
|
|
9
|
+
if (!controlPlaneReady) throw new Error(`${name} blocked by control-plane readiness`);
|
|
10
|
+
if (name === "$build" && buildReadiness && buildReadiness.allowed !== true) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`$build blocked: ${(buildReadiness.blockers ?? []).join("; ") || "readiness failed"}`,
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createPiMaestroToolSurface({
|
|
18
|
+
runners = {},
|
|
19
|
+
controlPlaneReady = true,
|
|
20
|
+
buildReadiness,
|
|
21
|
+
beforeToolCall,
|
|
22
|
+
afterToolCall,
|
|
23
|
+
isWaitingReview = () => false,
|
|
24
|
+
} = {}) {
|
|
25
|
+
const surface = {
|
|
26
|
+
tools: {},
|
|
27
|
+
async dispatch(name, input = {}) {
|
|
28
|
+
if (!runners[name]) throw new Error(`Unknown pi-maestro tool: ${name}`);
|
|
29
|
+
if (isWaitingReview()) return { terminate: true, status: "WAITING_REVIEW" };
|
|
30
|
+
assertPiMaestroToolAllowed(name, { controlPlaneReady, buildReadiness });
|
|
31
|
+
await beforeToolCall?.(name, input);
|
|
32
|
+
const result = await runners[name](input);
|
|
33
|
+
await afterToolCall?.(name, result);
|
|
34
|
+
return { terminate: false, result };
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
surface.tools = Object.fromEntries(
|
|
38
|
+
Object.keys(runners).map((name) => [
|
|
39
|
+
name,
|
|
40
|
+
{
|
|
41
|
+
name,
|
|
42
|
+
description: `Run the bounded ${name} Meta-Architect lane`,
|
|
43
|
+
execute: (input) => surface.dispatch(name, input),
|
|
44
|
+
},
|
|
45
|
+
]),
|
|
46
|
+
);
|
|
47
|
+
return surface;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const maestroGateLanes = ["$arch", "$sage", "$flow", "$vet", "$vibe", "$build"];
|
|
51
|
+
|
|
52
|
+
export function createPiMaestroCapabilityMapping({
|
|
53
|
+
runners = {},
|
|
54
|
+
enabled = isPiMaestroEnabled(),
|
|
55
|
+
...options
|
|
56
|
+
} = {}) {
|
|
57
|
+
const laneRunners = Object.fromEntries(
|
|
58
|
+
maestroGateLanes
|
|
59
|
+
.filter((lane) => typeof runners[lane] === "function")
|
|
60
|
+
.map((lane) => [lane, runners[lane]]),
|
|
61
|
+
);
|
|
62
|
+
return {
|
|
63
|
+
enabled,
|
|
64
|
+
controlModel: "maestro-pi",
|
|
65
|
+
umbrella: "$maestro",
|
|
66
|
+
lanes: maestroGateLanes.map((id) => ({
|
|
67
|
+
id,
|
|
68
|
+
runner: laneRunners[id] ? "maestro" : "unavailable",
|
|
69
|
+
gated: id === "$build" || id === "$vet",
|
|
70
|
+
})),
|
|
71
|
+
surface: createPiMaestroToolSurface({ runners: laneRunners, ...options }),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function runPiMaestroExperimental({
|
|
76
|
+
agent,
|
|
77
|
+
tools,
|
|
78
|
+
beforeToolCall,
|
|
79
|
+
afterToolCall,
|
|
80
|
+
} = {}) {
|
|
81
|
+
if (!agent || typeof agent.run !== "function") {
|
|
82
|
+
return {
|
|
83
|
+
handled: false,
|
|
84
|
+
reason: "Maestro Pi control runtime is not installed or was not provided",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const result = await agent.run({ tools, beforeToolCall, afterToolCall });
|
|
88
|
+
return { handled: true, result };
|
|
89
|
+
}
|