@kolisachint/hoocode-agent 0.5.24 → 0.5.26

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.
Files changed (85) hide show
  1. package/CHANGELOG.md +149 -0
  2. package/dist/core/brand.d.ts +1 -0
  3. package/dist/core/brand.d.ts.map +1 -1
  4. package/dist/core/brand.js +1 -0
  5. package/dist/core/brand.js.map +1 -1
  6. package/dist/core/canvas/discovery.d.ts +11 -3
  7. package/dist/core/canvas/discovery.d.ts.map +1 -1
  8. package/dist/core/canvas/discovery.js +18 -3
  9. package/dist/core/canvas/discovery.js.map +1 -1
  10. package/dist/core/canvas/lifecycle.d.ts +93 -0
  11. package/dist/core/canvas/lifecycle.d.ts.map +1 -0
  12. package/dist/core/canvas/lifecycle.js +165 -0
  13. package/dist/core/canvas/lifecycle.js.map +1 -0
  14. package/dist/core/canvas/plugin-canvases.d.ts +30 -0
  15. package/dist/core/canvas/plugin-canvases.d.ts.map +1 -0
  16. package/dist/core/canvas/plugin-canvases.js +51 -0
  17. package/dist/core/canvas/plugin-canvases.js.map +1 -0
  18. package/dist/core/canvas/registry.d.ts +89 -0
  19. package/dist/core/canvas/registry.d.ts.map +1 -1
  20. package/dist/core/canvas/registry.js +205 -10
  21. package/dist/core/canvas/registry.js.map +1 -1
  22. package/dist/core/canvas/scaffold.d.ts +123 -0
  23. package/dist/core/canvas/scaffold.d.ts.map +1 -0
  24. package/dist/core/canvas/scaffold.js +376 -0
  25. package/dist/core/canvas/scaffold.js.map +1 -0
  26. package/dist/core/canvas/session.d.ts +46 -1
  27. package/dist/core/canvas/session.d.ts.map +1 -1
  28. package/dist/core/canvas/session.js +91 -2
  29. package/dist/core/canvas/session.js.map +1 -1
  30. package/dist/core/canvas/trust.d.ts +4 -5
  31. package/dist/core/canvas/trust.d.ts.map +1 -1
  32. package/dist/core/canvas/trust.js +23 -8
  33. package/dist/core/canvas/trust.js.map +1 -1
  34. package/dist/core/extensions/plugins/formats/copilot.d.ts +10 -0
  35. package/dist/core/extensions/plugins/formats/copilot.d.ts.map +1 -1
  36. package/dist/core/extensions/plugins/formats/copilot.js +36 -4
  37. package/dist/core/extensions/plugins/formats/copilot.js.map +1 -1
  38. package/dist/core/extensions/plugins/formats/jsonManifest.d.ts.map +1 -1
  39. package/dist/core/extensions/plugins/formats/jsonManifest.js +15 -2
  40. package/dist/core/extensions/plugins/formats/jsonManifest.js.map +1 -1
  41. package/dist/core/extensions/plugins/formats/shared.d.ts +30 -2
  42. package/dist/core/extensions/plugins/formats/shared.d.ts.map +1 -1
  43. package/dist/core/extensions/plugins/formats/shared.js +72 -0
  44. package/dist/core/extensions/plugins/formats/shared.js.map +1 -1
  45. package/dist/core/extensions/plugins/install.d.ts +31 -0
  46. package/dist/core/extensions/plugins/install.d.ts.map +1 -1
  47. package/dist/core/extensions/plugins/install.js +102 -12
  48. package/dist/core/extensions/plugins/install.js.map +1 -1
  49. package/dist/core/extensions/plugins/listing.d.ts.map +1 -1
  50. package/dist/core/extensions/plugins/listing.js +1 -0
  51. package/dist/core/extensions/plugins/listing.js.map +1 -1
  52. package/dist/core/extensions/plugins/manifest.d.ts +18 -0
  53. package/dist/core/extensions/plugins/manifest.d.ts.map +1 -1
  54. package/dist/core/extensions/plugins/manifest.js.map +1 -1
  55. package/dist/core/extensions/plugins/packaging.d.ts.map +1 -1
  56. package/dist/core/extensions/plugins/packaging.js +1 -0
  57. package/dist/core/extensions/plugins/packaging.js.map +1 -1
  58. package/dist/core/tools/canvas.d.ts +23 -3
  59. package/dist/core/tools/canvas.d.ts.map +1 -1
  60. package/dist/core/tools/canvas.js +99 -4
  61. package/dist/core/tools/canvas.js.map +1 -1
  62. package/dist/extensions/core/canvas.d.ts +20 -2
  63. package/dist/extensions/core/canvas.d.ts.map +1 -1
  64. package/dist/extensions/core/canvas.js +279 -36
  65. package/dist/extensions/core/canvas.js.map +1 -1
  66. package/dist/extensions/core/marketplace.d.ts.map +1 -1
  67. package/dist/extensions/core/marketplace.js +28 -1
  68. package/dist/extensions/core/marketplace.js.map +1 -1
  69. package/dist/extensions/core/scaffold.d.ts +6 -0
  70. package/dist/extensions/core/scaffold.d.ts.map +1 -1
  71. package/dist/extensions/core/scaffold.js +6 -0
  72. package/dist/extensions/core/scaffold.js.map +1 -1
  73. package/dist/modes/interactive/interactive-mode.d.ts +11 -0
  74. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  75. package/dist/modes/interactive/interactive-mode.js +40 -0
  76. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  77. package/dist/modes/interactive/resource-display.d.ts +11 -0
  78. package/dist/modes/interactive/resource-display.d.ts.map +1 -1
  79. package/dist/modes/interactive/resource-display.js +20 -1
  80. package/dist/modes/interactive/resource-display.js.map +1 -1
  81. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  82. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  83. package/examples/extensions/sandbox/package.json +1 -1
  84. package/examples/extensions/with-deps/package.json +1 -1
  85. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"packaging.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/packaging.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Packaging — everything that has to happen before a plugin can be published,\n * and nothing that publishes it.\n *\n * The split is the point. Preparing a release is mechanical: run the gates at\n * their strictest, write a README, produce the marketplace index entry. All of\n * that is automated. *Publishing* — pushing an artifact into a marketplace that\n * other agents install from autonomously — stays a human act, because an agent\n * that can do it unattended is a supply-chain compromise primitive. See\n * docs/plugin-system-architecture.md §3.2–§3.3.\n *\n * Packaging works **in place**, in the plugin's production home. There is no\n * separate output location: by the time a plugin reaches here it is already in\n * its target platform's layout, and copying it somewhere else would just create\n * a fourth directory role to confuse with the other three (§8.3).\n *\n * The two platforms are not symmetric here, and the lane is built for the harder\n * one. A `claude` plugin is already live locally from `~/.claude/skills/<id>/`,\n * so publishing is optional and additive. For `github`, local installs are\n * deprecated — publishing is the *only* route into the ecosystem — so the\n * GitHub flow (a PR against a marketplace repo's index) is the primary case and\n * the Claude submission is the variant.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { CapabilityDoc } from \"../../capabilities/registry.js\";\nimport { isPluginPlatform, type PluginPlatform, resolvePluginPlatforms } from \"./formats/platform-targets.js\";\nimport { formatGateFindings, type GateFinding, type GateResult, runStaticGates, withFindings } from \"./gates.js\";\nimport { productionRoot } from \"./locations.js\";\nimport type { NormalizedPlugin } from \"./manifest.js\";\nimport { runSmokeGate } from \"./smoke.js\";\nimport {\n\tdistractorCandidates,\n\tloadTriggerCases,\n\townCandidates,\n\trunTriggerEval,\n\ttype TriggerJudge,\n\ttriggerFindings,\n} from \"./trigger-eval.js\";\n\n/**\n * Where packaging records what it produced, at the plugin root.\n *\n * A dotfile beside the existing `.authored.json` provenance marker rather than a\n * visible `marketplace-entry.json`: the plugin directory is the thing that gets\n * copied into a marketplace repo, and a build record is not part of the artifact.\n */\nexport const PUBLISH_RECORD_FILE = \".hoocode-publish.json\";\n\n/** Placeholder `source` for a plugin that is not in a git checkout — deliberately unusable as-is. */\nconst SOURCE_PLACEHOLDER = \"<REPLACE: git URL, or path within the marketplace repo>\";\n\n/** A `marketplace.json` `plugins[]` entry, ready to paste into an index. */\nexport interface MarketplaceEntry {\n\tname: string;\n\tdescription?: string;\n\tversion?: string;\n\tsource: string | { source: \"git\"; url: string } | { source: \"github\"; repo: string };\n}\n\nexport interface PublishRecord {\n\tpackagedAt: string;\n\tplatform: PluginPlatform;\n\tentry: MarketplaceEntry;\n\t/** Whether the gates were green when this record was written. */\n\tok: boolean;\n}\n\nexport interface PackageResult {\n\tok: boolean;\n\tdir: string;\n\tplatform: PluginPlatform;\n\t/** Files packaging created or refreshed, relative to the plugin root. */\n\twritten: string[];\n\tentry: MarketplaceEntry;\n\tevaluation: GateResult;\n\t/** Human-facing next steps; deliberately instructions, not actions. */\n\tinstructions: string;\n}\n\n/**\n * Which ecosystem this plugin is being packaged for.\n *\n * Where it lives is the strongest evidence — the production homes are per\n * platform, so a plugin under `~/.agents/publish/github/` is a github artifact\n * whatever its manifests say. A plugin outside a production home (installed, or\n * authored by an older version) falls back to what it declares, then to the\n * session default.\n */\nexport function resolvePackagePlatform(plugin: NormalizedPlugin): PluginPlatform {\n\tconst root = path.resolve(plugin.root);\n\tfor (const platform of [\"claude\", \"github\"] as const) {\n\t\tconst home = path.resolve(productionRoot(platform));\n\t\tif (root === home || root.startsWith(`${home}${path.sep}`)) return platform;\n\t}\n\tconst declared = plugin.supportPlatform.filter(isPluginPlatform);\n\treturn declared[0] ?? resolvePluginPlatforms()[0];\n}\n\n/** Best-effort origin URL of the repo containing `dir`, if it is in one. */\nfunction gitOriginUrl(dir: string): string | undefined {\n\ttry {\n\t\tconst url = execFileSync(\"git\", [\"-C\", dir, \"remote\", \"get-url\", \"origin\"], {\n\t\t\tencoding: \"utf8\",\n\t\t\ttimeout: 5_000,\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t}).trim();\n\t\treturn url || undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction capabilityLines(plugin: NormalizedPlugin): string[] {\n\tconst lines: string[] = [];\n\tconst add = (label: string, present: unknown) => {\n\t\tif (present) lines.push(`- ${label}`);\n\t};\n\tadd(\"Skills\", plugin.skillsDir);\n\tadd(\"Slash commands\", plugin.commandsDir);\n\tadd(\"Subagents\", plugin.agentsDir);\n\tadd(\"Hooks\", plugin.hooks);\n\tadd(\"MCP servers\", plugin.mcpServers);\n\treturn lines;\n}\n\nfunction installSection(plugin: NormalizedPlugin, platform: PluginPlatform): string[] {\n\tif (platform === \"claude\") {\n\t\treturn [\n\t\t\t\"## Install\",\n\t\t\t\"\",\n\t\t\t\"```bash\",\n\t\t\t\"claude plugin marketplace add <marketplace-repo>\",\n\t\t\t`claude plugin install ${plugin.id}@<marketplace>`,\n\t\t\t\"```\",\n\t\t\t\"\",\n\t\t\t`Or copy this directory to \\`~/.claude/skills/${plugin.id}/\\` — a folder there`,\n\t\t\t\"carrying a `.claude-plugin/plugin.json` loads with no install step.\",\n\t\t];\n\t}\n\treturn [\n\t\t\"## Install\",\n\t\t\"\",\n\t\t\"```bash\",\n\t\t\"copilot plugin marketplace add <marketplace-repo>\",\n\t\t`copilot plugin install ${plugin.id}@<marketplace>`,\n\t\t\"```\",\n\t];\n}\n\n/**\n * Generate a README from what the plugin actually contains.\n *\n * Never overwrites: a hand-written README is the author's description of their\n * own plugin, and a generated one derived from the directory listing is strictly\n * worse. This only fills the gap where there is nothing at all.\n */\nfunction writeReadme(plugin: NormalizedPlugin, platform: PluginPlatform): string | undefined {\n\tconst file = path.join(plugin.root, \"README.md\");\n\tif (existsSync(file)) return undefined;\n\n\tconst capabilities = capabilityLines(plugin);\n\tconst body = [\n\t\t`# ${plugin.id}`,\n\t\t\"\",\n\t\tplugin.description ?? \"_No description provided._\",\n\t\t\"\",\n\t\t...(capabilities.length > 0 ? [\"## Provides\", \"\", ...capabilities, \"\"] : []),\n\t\t...installSection(plugin, platform),\n\t\t\"\",\n\t\t...(plugin.unsupportedSurfaces?.length\n\t\t\t? [\n\t\t\t\t\t\"## Notes\",\n\t\t\t\t\t\"\",\n\t\t\t\t\t`This plugin ships surfaces hoocode does not load: ${plugin.unsupportedSurfaces.join(\", \")}.`,\n\t\t\t\t\t\"They are preserved as-is and are the target platform's concern.\",\n\t\t\t\t\t\"\",\n\t\t\t\t]\n\t\t\t: []),\n\t].join(\"\\n\");\n\n\twriteFileSync(file, `${body.trimEnd()}\\n`, \"utf8\");\n\treturn \"README.md\";\n}\n\n/**\n * Build the marketplace index entry.\n *\n * `source` is where the *published* plugin will live, which packaging cannot\n * know: the artifact is on this machine and has not been pushed anywhere. When\n * the plugin sits in a git checkout its origin is a plausible starting point;\n * otherwise the field is a clearly-marked placeholder for the human to fill.\n * Guessing here would produce an index entry that resolves to nothing, which is\n * worse than an obvious blank.\n */\nfunction buildEntry(plugin: NormalizedPlugin): MarketplaceEntry {\n\tconst origin = gitOriginUrl(plugin.root);\n\treturn {\n\t\tname: plugin.id,\n\t\t...(plugin.description ? { description: plugin.description } : {}),\n\t\t...(plugin.version ? { version: plugin.version } : {}),\n\t\tsource: origin ? { source: \"git\", url: origin } : SOURCE_PLACEHOLDER,\n\t};\n}\n\n/** True when the entry still needs a human to say where the plugin will be hosted. */\nexport function entryNeedsSource(entry: MarketplaceEntry): boolean {\n\treturn entry.source === SOURCE_PLACEHOLDER;\n}\n\nfunction publishSteps(plugin: NormalizedPlugin, platform: PluginPlatform): string[] {\n\tif (platform === \"github\") {\n\t\treturn [\n\t\t\t\"Publish (GitHub Copilot) — this is the only route into the ecosystem,\",\n\t\t\t\"local installs being deprecated:\",\n\t\t\t\"\",\n\t\t\t\" 1. Copy this directory into your marketplace repo.\",\n\t\t\t\" 2. Add the entry above to its `.github/marketplace.json` `plugins` array.\",\n\t\t\t\" 3. Open a pull request; the plugin is installable once it merges.\",\n\t\t];\n\t}\n\treturn [\n\t\t`Publish (Claude Code) — optional: the plugin is already live locally from`,\n\t\t`\\`~/.claude/skills/${plugin.id}/\\`. To share it:`,\n\t\t\"\",\n\t\t\" 1. Copy this directory into your marketplace repo.\",\n\t\t\" 2. Add the entry above to its `.claude-plugin/marketplace.json` `plugins` array.\",\n\t\t\" 3. Open a pull request, or submit to the community directory for review\",\n\t\t\" (approved plugins are pinned to a commit SHA).\",\n\t];\n}\n\n/**\n * Package a plugin for publication: gate it at full strictness, write a README,\n * produce its marketplace entry, and record what it produced. Touches no remote.\n */\nexport async function packagePlugin(\n\tplugin: NormalizedPlugin,\n\tplatformOverride?: PluginPlatform,\n\toptions: { judge?: TriggerJudge; distractors?: readonly CapabilityDoc[] } = {},\n): Promise<PackageResult> {\n\tconst platform = platformOverride ?? resolvePackagePlatform(plugin);\n\n\t// A plugin can only be published into an ecosystem whose manifest it carries.\n\t// `claude plugin validate` says so too, but as a wall of validator output about\n\t// a missing file; a plugin in the native layout has simply been authored for\n\t// the wrong target, and that is worth saying in one sentence. Nothing is\n\t// written — there is no artifact to package.\n\tif (!plugin.supportPlatform.includes(platform)) {\n\t\tconst finding: GateFinding = {\n\t\t\tgate: \"G1\",\n\t\t\tseverity: \"error\",\n\t\t\tmessage:\n\t\t\t\t`Plugin \"${plugin.id}\" carries no ${platform} manifest (it offers: ${plugin.supportPlatform.join(\", \")}), ` +\n\t\t\t\t`so it cannot be published to that ecosystem. Re-author it with --platform ${platform}.`,\n\t\t};\n\t\tconst evaluation: GateResult = { ok: false, findings: [finding], conformance: \"round-trip\", plugin };\n\t\treturn {\n\t\t\tok: false,\n\t\t\tdir: plugin.root,\n\t\t\tplatform,\n\t\t\twritten: [],\n\t\t\tentry: buildEntry(plugin),\n\t\t\tevaluation,\n\t\t\tinstructions: `Plugin \"${plugin.id}\" cannot be packaged for ${platform}.\\n\\n${formatGateFindings(evaluation)}`,\n\t\t};\n\t}\n\n\t// Strict: warnings that are tolerable in a local plugin — an unresolvable\n\t// binary, a vendor-validator nit — are not tolerable in something other people\n\t// will install on the strength of the marketplace vouching for it.\n\tlet evaluation = runStaticGates(plugin.root, { platform, strict: true });\n\tif (evaluation.plugin) {\n\t\tevaluation = withFindings(evaluation, await runSmokeGate(evaluation.plugin), true);\n\t}\n\n\t// G4 is the publish path's gate (§4.3) and nothing else's: it costs a model\n\t// call, and the question it asks — is this description actually worth the\n\t// context it occupies — only becomes other people's problem at publication.\n\t// With no judge or no gold set it reports `not-run`, which is `info`, so a\n\t// machine without a model is never blocked from publishing by a check it\n\t// could not perform.\n\tconst own = ownCandidates(plugin);\n\tif (own.length > 0) {\n\t\tconst outcome = await runTriggerEval(\n\t\t\tplugin.id,\n\t\t\t[...own, ...distractorCandidates(plugin.id, options.distractors ?? [])],\n\t\t\tloadTriggerCases(plugin.root),\n\t\t\toptions.judge,\n\t\t);\n\t\tevaluation = withFindings(evaluation, triggerFindings(outcome), true);\n\t}\n\n\tconst written: string[] = [];\n\tconst readme = writeReadme(plugin, platform);\n\tif (readme) written.push(readme);\n\n\tconst entry = buildEntry(plugin);\n\tconst record: PublishRecord = {\n\t\tpackagedAt: new Date().toISOString(),\n\t\tplatform,\n\t\tentry,\n\t\tok: evaluation.ok,\n\t};\n\twriteFileSync(path.join(plugin.root, PUBLISH_RECORD_FILE), `${JSON.stringify(record, null, 2)}\\n`, \"utf8\");\n\twritten.push(PUBLISH_RECORD_FILE);\n\n\tconst instructions = [\n\t\tevaluation.ok\n\t\t\t? `Plugin \"${plugin.id}\" is ready to publish (${platform}). The remaining step is yours.`\n\t\t\t: `Plugin \"${plugin.id}\" is NOT ready to publish — these must pass first:`,\n\t\t\"\",\n\t\tformatGateFindings(evaluation),\n\t\t\"\",\n\t\t\"Marketplace entry:\",\n\t\t\"```json\",\n\t\tJSON.stringify(entry, null, 2),\n\t\t\"```\",\n\t\t...(entryNeedsSource(entry) ? [\"\", \"Replace `source` with where the plugin will be hosted.\"] : []),\n\t\t\"\",\n\t\t...publishSteps(plugin, platform),\n\t\t\"\",\n\t\t\"Publishing is deliberately not automated: an agent that can push executable\",\n\t\t\"code into a marketplace other agents install from unattended is a supply-chain\",\n\t\t\"risk, so the last step stays with you.\",\n\t].join(\"\\n\");\n\n\treturn { ok: evaluation.ok, dir: plugin.root, platform, written, entry, evaluation, instructions };\n}\n\n// ── Staging into a local marketplace checkout ────────────────────────────────\n\n/**\n * Files that exist for hoocode's benefit and have no business in a published\n * artifact: the provenance marker and packaging's own build record.\n */\nconst INTERNAL_FILES = new Set([PUBLISH_RECORD_FILE, \".authored.json\"]);\n\n/** Where each platform's marketplace index lives, and the subdirectory plugins are vendored into. */\nconst INDEX_FILE: Record<PluginPlatform, string> = {\n\tclaude: path.join(\".claude-plugin\", \"marketplace.json\"),\n\tgithub: path.join(\".github\", \"marketplace.json\"),\n};\nconst VENDOR_DIR = \"plugins\";\n\nexport interface StageResult {\n\tok: boolean;\n\tmessage: string;\n\t/** Path the plugin was copied to, relative to the marketplace root. */\n\tpluginPath?: string;\n\t/** The index that was written, relative to the marketplace root. */\n\tindexPath?: string;\n\t/** True when the marketplace had no index for this platform and one was created. */\n\tcreatedIndex?: boolean;\n}\n\ninterface RawIndex {\n\tname?: string;\n\tplugins?: Array<Record<string, unknown>>;\n\t[key: string]: unknown;\n}\n\n/**\n * Copy a packaged plugin into a **local** marketplace checkout and add its index\n * entry, leaving the result uncommitted.\n *\n * This is the far side of the automation line drawn in §3.3, and it is where the\n * line actually falls. Everything here is local, reversible, and shows up in\n * `git status` — the human reviews a diff and decides. What stays manual is the\n * step that leaves the machine: no commit, no push, no PR. Those are what make a\n * plugin installable by other people's agents, and that is the act install is\n * trusting a human to have performed.\n *\n * Only reachable from `/plugin publish`, never from a tool: a human typed the\n * command, which is the whole point.\n */\nexport function stageIntoMarketplace(\n\tplugin: NormalizedPlugin,\n\tplatform: PluginPlatform,\n\tmarketplaceDir: string,\n): StageResult {\n\tconst root = path.resolve(marketplaceDir);\n\tif (!existsSync(root)) {\n\t\treturn { ok: false, message: `Marketplace directory not found: ${root}` };\n\t}\n\tconst pluginRoot = path.resolve(plugin.root);\n\tif (pluginRoot === root || pluginRoot.startsWith(`${root}${path.sep}`)) {\n\t\treturn { ok: false, message: `Plugin \"${plugin.id}\" already lives inside ${root}; nothing to stage.` };\n\t}\n\n\tconst indexRel = INDEX_FILE[platform];\n\tconst indexAbs = path.join(root, indexRel);\n\tlet index: RawIndex;\n\tlet createdIndex = false;\n\tif (existsSync(indexAbs)) {\n\t\ttry {\n\t\t\tindex = JSON.parse(readFileSync(indexAbs, \"utf8\")) as RawIndex;\n\t\t} catch (error) {\n\t\t\treturn { ok: false, message: `${indexRel} is not valid JSON (${(error as Error).message}); fix it first.` };\n\t\t}\n\t} else {\n\t\t// A marketplace repo that does not have an index yet is a normal starting\n\t\t// point, and refusing here would mean the first plugin can never be staged.\n\t\tindex = { name: path.basename(root), plugins: [] };\n\t\tcreatedIndex = true;\n\t}\n\tif (!Array.isArray(index.plugins)) index.plugins = [];\n\n\tconst dest = path.join(root, VENDOR_DIR, plugin.id);\n\trmSync(dest, { recursive: true, force: true });\n\tmkdirSync(path.dirname(dest), { recursive: true });\n\tcpSync(pluginRoot, dest, {\n\t\trecursive: true,\n\t\tfilter: (src) => !INTERNAL_FILES.has(path.basename(src)),\n\t});\n\n\t// Vendored in the repo, so the source is a path relative to it — not the\n\t// git origin `packagePlugin` guessed, which points at wherever the plugin was\n\t// authored rather than at the marketplace.\n\tconst entry: MarketplaceEntry = { ...buildEntry(plugin), source: `./${VENDOR_DIR}/${plugin.id}` };\n\tconst existingAt = index.plugins.findIndex((p) => p?.name === plugin.id);\n\tif (existingAt >= 0) index.plugins[existingAt] = { ...index.plugins[existingAt], ...entry };\n\telse index.plugins.push(entry as unknown as Record<string, unknown>);\n\n\tmkdirSync(path.dirname(indexAbs), { recursive: true });\n\twriteFileSync(indexAbs, `${JSON.stringify(index, null, 2)}\\n`, \"utf8\");\n\n\tconst verb = existingAt >= 0 ? \"Updated\" : \"Added\";\n\treturn {\n\t\tok: true,\n\t\tmessage:\n\t\t\t`${verb} \"${plugin.id}\" in ${path.basename(root)}: copied to ${VENDOR_DIR}/${plugin.id}/ and ` +\n\t\t\t`${createdIndex ? \"created\" : \"updated\"} ${indexRel}. Nothing was committed or pushed.`,\n\t\tpluginPath: `${VENDOR_DIR}/${plugin.id}`,\n\t\tindexPath: indexRel,\n\t\tcreatedIndex,\n\t};\n}\n\n/** Read back the record packaging left, for callers that want to show it. */\nexport function readPublishRecord(dir: string): PublishRecord | undefined {\n\ttry {\n\t\tconst file = path.join(dir, PUBLISH_RECORD_FILE);\n\t\treturn existsSync(file) ? (JSON.parse(readFileSync(file, \"utf8\")) as PublishRecord) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
1
+ {"version":3,"file":"packaging.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/packaging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7F,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,gBAAgB,EAAuB,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC9G,OAAO,EAAE,kBAAkB,EAAqC,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACjH,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EACN,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,cAAc,EAEd,eAAe,GACf,MAAM,mBAAmB,CAAC;AAE3B;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,uBAAuB,CAAC;AAE3D,uGAAqG;AACrG,MAAM,kBAAkB,GAAG,yDAAyD,CAAC;AA8BrF;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAwB,EAAkB;IAChF,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvC,KAAK,MAAM,QAAQ,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAU,EAAE,CAAC;QACtD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;QACpD,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAAE,OAAO,QAAQ,CAAC;IAC7E,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACjE,OAAO,QAAQ,CAAC,CAAC,CAAC,IAAI,sBAAsB,EAAE,CAAC,CAAC,CAAC,CAAC;AAAA,CAClD;AAED,4EAA4E;AAC5E,SAAS,YAAY,CAAC,GAAW,EAAsB;IACtD,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE;YAC3E,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACnC,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,GAAG,IAAI,SAAS,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AAAA,CACD;AAED,SAAS,eAAe,CAAC,MAAwB,EAAY;IAC5D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,OAAgB,EAAE,EAAE,CAAC;QAChD,IAAI,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC;IAAA,CACtC,CAAC;IACF,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAChC,GAAG,CAAC,gBAAgB,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;IAC1C,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IACnC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IACtC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IACjD,OAAO,KAAK,CAAC;AAAA,CACb;AAED,SAAS,cAAc,CAAC,MAAwB,EAAE,QAAwB,EAAY;IACrF,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO;YACN,YAAY;YACZ,EAAE;YACF,SAAS;YACT,kDAAkD;YAClD,yBAAyB,MAAM,CAAC,EAAE,gBAAgB;YAClD,KAAK;YACL,EAAE;YACF,gDAAgD,MAAM,CAAC,EAAE,wBAAsB;YAC/E,qEAAqE;SACrE,CAAC;IACH,CAAC;IACD,OAAO;QACN,YAAY;QACZ,EAAE;QACF,SAAS;QACT,mDAAmD;QACnD,0BAA0B,MAAM,CAAC,EAAE,gBAAgB;QACnD,KAAK;KACL,CAAC;AAAA,CACF;AAED;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,MAAwB,EAAE,QAAwB,EAAsB;IAC5F,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACjD,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAEvC,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG;QACZ,KAAK,MAAM,CAAC,EAAE,EAAE;QAChB,EAAE;QACF,MAAM,CAAC,WAAW,IAAI,4BAA4B;QAClD,EAAE;QACF,GAAG,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,EAAE,EAAE,EAAE,GAAG,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5E,GAAG,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC;QACnC,EAAE;QACF,GAAG,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM;YACrC,CAAC,CAAC;gBACA,UAAU;gBACV,EAAE;gBACF,qDAAqD,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;gBAC7F,iEAAiE;gBACjE,EAAE;aACF;YACF,CAAC,CAAC,EAAE,CAAC;KACN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACnD,OAAO,WAAW,CAAC;AAAA,CACnB;AAED;;;;;;;;;GASG;AACH,SAAS,UAAU,CAAC,MAAwB,EAAoB;IAC/D,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACzC,OAAO;QACN,IAAI,EAAE,MAAM,CAAC,EAAE;QACf,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,kBAAkB;KACpE,CAAC;AAAA,CACF;AAED,sFAAsF;AACtF,MAAM,UAAU,gBAAgB,CAAC,KAAuB,EAAW;IAClE,OAAO,KAAK,CAAC,MAAM,KAAK,kBAAkB,CAAC;AAAA,CAC3C;AAED,SAAS,YAAY,CAAC,MAAwB,EAAE,QAAwB,EAAY;IACnF,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO;YACN,yEAAuE;YACvE,kCAAkC;YAClC,EAAE;YACF,sDAAsD;YACtD,6EAA6E;YAC7E,qEAAqE;SACrE,CAAC;IACH,CAAC;IACD,OAAO;QACN,6EAA2E;QAC3E,sBAAsB,MAAM,CAAC,EAAE,mBAAmB;QAClD,EAAE;QACF,sDAAsD;QACtD,oFAAoF;QACpF,2EAA2E;QAC3E,qDAAqD;KACrD,CAAC;AAAA,CACF;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAClC,MAAwB,EACxB,gBAAiC,EACjC,OAAO,GAAqE,EAAE,EACrD;IACzB,MAAM,QAAQ,GAAG,gBAAgB,IAAI,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAEpE,8EAA8E;IAC9E,gFAAgF;IAChF,6EAA6E;IAC7E,yEAAyE;IACzE,+CAA6C;IAC7C,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChD,MAAM,OAAO,GAAgB;YAC5B,IAAI,EAAE,IAAI;YACV,QAAQ,EAAE,OAAO;YACjB,OAAO,EACN,WAAW,MAAM,CAAC,EAAE,gBAAgB,QAAQ,yBAAyB,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK;gBAC3G,6EAA6E,QAAQ,GAAG;SACzF,CAAC;QACF,MAAM,UAAU,GAAe,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;QACrG,OAAO;YACN,EAAE,EAAE,KAAK;YACT,GAAG,EAAE,MAAM,CAAC,IAAI;YAChB,QAAQ;YACR,OAAO,EAAE,EAAE;YACX,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,UAAU;YACV,YAAY,EAAE,WAAW,MAAM,CAAC,EAAE,4BAA4B,QAAQ,QAAQ,kBAAkB,CAAC,UAAU,CAAC,EAAE;SAC9G,CAAC;IACH,CAAC;IAED,4EAA0E;IAC1E,iFAA+E;IAC/E,mEAAmE;IACnE,IAAI,UAAU,GAAG,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IACzE,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;QACvB,UAAU,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IACpF,CAAC;IAED,6EAA4E;IAC5E,4EAA0E;IAC1E,8EAA4E;IAC5E,2EAA2E;IAC3E,yEAAyE;IACzE,qBAAqB;IACrB,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,OAAO,GAAG,MAAM,cAAc,CACnC,MAAM,CAAC,EAAE,EACT,CAAC,GAAG,GAAG,EAAE,GAAG,oBAAoB,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,EACvE,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,EAC7B,OAAO,CAAC,KAAK,CACb,CAAC;QACF,UAAU,GAAG,YAAY,CAAC,UAAU,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC7C,IAAI,MAAM;QAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAEjC,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAkB;QAC7B,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,QAAQ;QACR,KAAK;QACL,EAAE,EAAE,UAAU,CAAC,EAAE;KACjB,CAAC;IACF,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,mBAAmB,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3G,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAElC,MAAM,YAAY,GAAG;QACpB,UAAU,CAAC,EAAE;YACZ,CAAC,CAAC,WAAW,MAAM,CAAC,EAAE,0BAA0B,QAAQ,iCAAiC;YACzF,CAAC,CAAC,WAAW,MAAM,CAAC,EAAE,sDAAoD;QAC3E,EAAE;QACF,kBAAkB,CAAC,UAAU,CAAC;QAC9B,EAAE;QACF,oBAAoB;QACpB,SAAS;QACT,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9B,KAAK;QACL,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,wDAAwD,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAClG,EAAE;QACF,GAAG,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC;QACjC,EAAE;QACF,6EAA6E;QAC7E,gFAAgF;QAChF,wCAAwC;KACxC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,OAAO,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;AAAA,CACnG;AAED,oJAAgF;AAEhF;;;GAGG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAExE,qGAAqG;AACrG,MAAM,UAAU,GAAmC;IAClD,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,kBAAkB,CAAC;IACvD,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC;CAChD,CAAC;AACF,MAAM,UAAU,GAAG,SAAS,CAAC;AAmB7B;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,oBAAoB,CACnC,MAAwB,EACxB,QAAwB,EACxB,cAAsB,EACR;IACd,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAC1C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,oCAAoC,IAAI,EAAE,EAAE,CAAC;IAC3E,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;QACxE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,MAAM,CAAC,EAAE,0BAA0B,IAAI,qBAAqB,EAAE,CAAC;IACxG,CAAC;IAED,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC3C,IAAI,KAAe,CAAC;IACpB,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,IAAI,CAAC;YACJ,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAa,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,QAAQ,uBAAwB,KAAe,CAAC,OAAO,kBAAkB,EAAE,CAAC;QAC7G,CAAC;IACF,CAAC;SAAM,CAAC;QACP,0EAA0E;QAC1E,4EAA4E;QAC5E,KAAK,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QACnD,YAAY,GAAG,IAAI,CAAC;IACrB,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;QAAE,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC;IAEtD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACpD,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE;QACxB,SAAS,EAAE,IAAI;QACf,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;KACxD,CAAC,CAAC;IAEH,2EAAyE;IACzE,8EAA8E;IAC9E,2CAA2C;IAC3C,MAAM,KAAK,GAAqB,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,UAAU,IAAI,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC;IAClG,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC;IACzE,IAAI,UAAU,IAAI,CAAC;QAAE,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC;;QACvF,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAA2C,CAAC,CAAC;IAErE,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,aAAa,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAEvE,MAAM,IAAI,GAAG,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;IACnD,OAAO;QACN,EAAE,EAAE,IAAI;QACR,OAAO,EACN,GAAG,IAAI,KAAK,MAAM,CAAC,EAAE,QAAQ,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,UAAU,IAAI,MAAM,CAAC,EAAE,QAAQ;YAC9F,GAAG,YAAY,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,IAAI,QAAQ,oCAAoC;QACxF,UAAU,EAAE,GAAG,UAAU,IAAI,MAAM,CAAC,EAAE,EAAE;QACxC,SAAS,EAAE,QAAQ;QACnB,YAAY;KACZ,CAAC;AAAA,CACF;AAED,6EAA6E;AAC7E,MAAM,UAAU,iBAAiB,CAAC,GAAW,EAA6B;IACzE,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;QACjD,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAmB,CAAC,CAAC,CAAC,SAAS,CAAC;IACjG,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AAAA,CACD","sourcesContent":["/**\n * Packaging — everything that has to happen before a plugin can be published,\n * and nothing that publishes it.\n *\n * The split is the point. Preparing a release is mechanical: run the gates at\n * their strictest, write a README, produce the marketplace index entry. All of\n * that is automated. *Publishing* — pushing an artifact into a marketplace that\n * other agents install from autonomously — stays a human act, because an agent\n * that can do it unattended is a supply-chain compromise primitive. See\n * docs/plugin-system-architecture.md §3.2–§3.3.\n *\n * Packaging works **in place**, in the plugin's production home. There is no\n * separate output location: by the time a plugin reaches here it is already in\n * its target platform's layout, and copying it somewhere else would just create\n * a fourth directory role to confuse with the other three (§8.3).\n *\n * The two platforms are not symmetric here, and the lane is built for the harder\n * one. A `claude` plugin is already live locally from `~/.claude/skills/<id>/`,\n * so publishing is optional and additive. For `github`, local installs are\n * deprecated — publishing is the *only* route into the ecosystem — so the\n * GitHub flow (a PR against a marketplace repo's index) is the primary case and\n * the Claude submission is the variant.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { CapabilityDoc } from \"../../capabilities/registry.js\";\nimport { isPluginPlatform, type PluginPlatform, resolvePluginPlatforms } from \"./formats/platform-targets.js\";\nimport { formatGateFindings, type GateFinding, type GateResult, runStaticGates, withFindings } from \"./gates.js\";\nimport { productionRoot } from \"./locations.js\";\nimport type { NormalizedPlugin } from \"./manifest.js\";\nimport { runSmokeGate } from \"./smoke.js\";\nimport {\n\tdistractorCandidates,\n\tloadTriggerCases,\n\townCandidates,\n\trunTriggerEval,\n\ttype TriggerJudge,\n\ttriggerFindings,\n} from \"./trigger-eval.js\";\n\n/**\n * Where packaging records what it produced, at the plugin root.\n *\n * A dotfile beside the existing `.authored.json` provenance marker rather than a\n * visible `marketplace-entry.json`: the plugin directory is the thing that gets\n * copied into a marketplace repo, and a build record is not part of the artifact.\n */\nexport const PUBLISH_RECORD_FILE = \".hoocode-publish.json\";\n\n/** Placeholder `source` for a plugin that is not in a git checkout — deliberately unusable as-is. */\nconst SOURCE_PLACEHOLDER = \"<REPLACE: git URL, or path within the marketplace repo>\";\n\n/** A `marketplace.json` `plugins[]` entry, ready to paste into an index. */\nexport interface MarketplaceEntry {\n\tname: string;\n\tdescription?: string;\n\tversion?: string;\n\tsource: string | { source: \"git\"; url: string } | { source: \"github\"; repo: string };\n}\n\nexport interface PublishRecord {\n\tpackagedAt: string;\n\tplatform: PluginPlatform;\n\tentry: MarketplaceEntry;\n\t/** Whether the gates were green when this record was written. */\n\tok: boolean;\n}\n\nexport interface PackageResult {\n\tok: boolean;\n\tdir: string;\n\tplatform: PluginPlatform;\n\t/** Files packaging created or refreshed, relative to the plugin root. */\n\twritten: string[];\n\tentry: MarketplaceEntry;\n\tevaluation: GateResult;\n\t/** Human-facing next steps; deliberately instructions, not actions. */\n\tinstructions: string;\n}\n\n/**\n * Which ecosystem this plugin is being packaged for.\n *\n * Where it lives is the strongest evidence — the production homes are per\n * platform, so a plugin under `~/.agents/publish/github/` is a github artifact\n * whatever its manifests say. A plugin outside a production home (installed, or\n * authored by an older version) falls back to what it declares, then to the\n * session default.\n */\nexport function resolvePackagePlatform(plugin: NormalizedPlugin): PluginPlatform {\n\tconst root = path.resolve(plugin.root);\n\tfor (const platform of [\"claude\", \"github\"] as const) {\n\t\tconst home = path.resolve(productionRoot(platform));\n\t\tif (root === home || root.startsWith(`${home}${path.sep}`)) return platform;\n\t}\n\tconst declared = plugin.supportPlatform.filter(isPluginPlatform);\n\treturn declared[0] ?? resolvePluginPlatforms()[0];\n}\n\n/** Best-effort origin URL of the repo containing `dir`, if it is in one. */\nfunction gitOriginUrl(dir: string): string | undefined {\n\ttry {\n\t\tconst url = execFileSync(\"git\", [\"-C\", dir, \"remote\", \"get-url\", \"origin\"], {\n\t\t\tencoding: \"utf8\",\n\t\t\ttimeout: 5_000,\n\t\t\tstdio: [\"ignore\", \"pipe\", \"ignore\"],\n\t\t}).trim();\n\t\treturn url || undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction capabilityLines(plugin: NormalizedPlugin): string[] {\n\tconst lines: string[] = [];\n\tconst add = (label: string, present: unknown) => {\n\t\tif (present) lines.push(`- ${label}`);\n\t};\n\tadd(\"Skills\", plugin.skillsDir);\n\tadd(\"Slash commands\", plugin.commandsDir);\n\tadd(\"Subagents\", plugin.agentsDir);\n\tadd(\"Hooks\", plugin.hooks);\n\tadd(\"MCP servers\", plugin.mcpServers);\n\tadd(\"Canvases\", plugin.canvasExtensions?.length);\n\treturn lines;\n}\n\nfunction installSection(plugin: NormalizedPlugin, platform: PluginPlatform): string[] {\n\tif (platform === \"claude\") {\n\t\treturn [\n\t\t\t\"## Install\",\n\t\t\t\"\",\n\t\t\t\"```bash\",\n\t\t\t\"claude plugin marketplace add <marketplace-repo>\",\n\t\t\t`claude plugin install ${plugin.id}@<marketplace>`,\n\t\t\t\"```\",\n\t\t\t\"\",\n\t\t\t`Or copy this directory to \\`~/.claude/skills/${plugin.id}/\\` — a folder there`,\n\t\t\t\"carrying a `.claude-plugin/plugin.json` loads with no install step.\",\n\t\t];\n\t}\n\treturn [\n\t\t\"## Install\",\n\t\t\"\",\n\t\t\"```bash\",\n\t\t\"copilot plugin marketplace add <marketplace-repo>\",\n\t\t`copilot plugin install ${plugin.id}@<marketplace>`,\n\t\t\"```\",\n\t];\n}\n\n/**\n * Generate a README from what the plugin actually contains.\n *\n * Never overwrites: a hand-written README is the author's description of their\n * own plugin, and a generated one derived from the directory listing is strictly\n * worse. This only fills the gap where there is nothing at all.\n */\nfunction writeReadme(plugin: NormalizedPlugin, platform: PluginPlatform): string | undefined {\n\tconst file = path.join(plugin.root, \"README.md\");\n\tif (existsSync(file)) return undefined;\n\n\tconst capabilities = capabilityLines(plugin);\n\tconst body = [\n\t\t`# ${plugin.id}`,\n\t\t\"\",\n\t\tplugin.description ?? \"_No description provided._\",\n\t\t\"\",\n\t\t...(capabilities.length > 0 ? [\"## Provides\", \"\", ...capabilities, \"\"] : []),\n\t\t...installSection(plugin, platform),\n\t\t\"\",\n\t\t...(plugin.unsupportedSurfaces?.length\n\t\t\t? [\n\t\t\t\t\t\"## Notes\",\n\t\t\t\t\t\"\",\n\t\t\t\t\t`This plugin ships surfaces hoocode does not load: ${plugin.unsupportedSurfaces.join(\", \")}.`,\n\t\t\t\t\t\"They are preserved as-is and are the target platform's concern.\",\n\t\t\t\t\t\"\",\n\t\t\t\t]\n\t\t\t: []),\n\t].join(\"\\n\");\n\n\twriteFileSync(file, `${body.trimEnd()}\\n`, \"utf8\");\n\treturn \"README.md\";\n}\n\n/**\n * Build the marketplace index entry.\n *\n * `source` is where the *published* plugin will live, which packaging cannot\n * know: the artifact is on this machine and has not been pushed anywhere. When\n * the plugin sits in a git checkout its origin is a plausible starting point;\n * otherwise the field is a clearly-marked placeholder for the human to fill.\n * Guessing here would produce an index entry that resolves to nothing, which is\n * worse than an obvious blank.\n */\nfunction buildEntry(plugin: NormalizedPlugin): MarketplaceEntry {\n\tconst origin = gitOriginUrl(plugin.root);\n\treturn {\n\t\tname: plugin.id,\n\t\t...(plugin.description ? { description: plugin.description } : {}),\n\t\t...(plugin.version ? { version: plugin.version } : {}),\n\t\tsource: origin ? { source: \"git\", url: origin } : SOURCE_PLACEHOLDER,\n\t};\n}\n\n/** True when the entry still needs a human to say where the plugin will be hosted. */\nexport function entryNeedsSource(entry: MarketplaceEntry): boolean {\n\treturn entry.source === SOURCE_PLACEHOLDER;\n}\n\nfunction publishSteps(plugin: NormalizedPlugin, platform: PluginPlatform): string[] {\n\tif (platform === \"github\") {\n\t\treturn [\n\t\t\t\"Publish (GitHub Copilot) — this is the only route into the ecosystem,\",\n\t\t\t\"local installs being deprecated:\",\n\t\t\t\"\",\n\t\t\t\" 1. Copy this directory into your marketplace repo.\",\n\t\t\t\" 2. Add the entry above to its `.github/marketplace.json` `plugins` array.\",\n\t\t\t\" 3. Open a pull request; the plugin is installable once it merges.\",\n\t\t];\n\t}\n\treturn [\n\t\t`Publish (Claude Code) — optional: the plugin is already live locally from`,\n\t\t`\\`~/.claude/skills/${plugin.id}/\\`. To share it:`,\n\t\t\"\",\n\t\t\" 1. Copy this directory into your marketplace repo.\",\n\t\t\" 2. Add the entry above to its `.claude-plugin/marketplace.json` `plugins` array.\",\n\t\t\" 3. Open a pull request, or submit to the community directory for review\",\n\t\t\" (approved plugins are pinned to a commit SHA).\",\n\t];\n}\n\n/**\n * Package a plugin for publication: gate it at full strictness, write a README,\n * produce its marketplace entry, and record what it produced. Touches no remote.\n */\nexport async function packagePlugin(\n\tplugin: NormalizedPlugin,\n\tplatformOverride?: PluginPlatform,\n\toptions: { judge?: TriggerJudge; distractors?: readonly CapabilityDoc[] } = {},\n): Promise<PackageResult> {\n\tconst platform = platformOverride ?? resolvePackagePlatform(plugin);\n\n\t// A plugin can only be published into an ecosystem whose manifest it carries.\n\t// `claude plugin validate` says so too, but as a wall of validator output about\n\t// a missing file; a plugin in the native layout has simply been authored for\n\t// the wrong target, and that is worth saying in one sentence. Nothing is\n\t// written — there is no artifact to package.\n\tif (!plugin.supportPlatform.includes(platform)) {\n\t\tconst finding: GateFinding = {\n\t\t\tgate: \"G1\",\n\t\t\tseverity: \"error\",\n\t\t\tmessage:\n\t\t\t\t`Plugin \"${plugin.id}\" carries no ${platform} manifest (it offers: ${plugin.supportPlatform.join(\", \")}), ` +\n\t\t\t\t`so it cannot be published to that ecosystem. Re-author it with --platform ${platform}.`,\n\t\t};\n\t\tconst evaluation: GateResult = { ok: false, findings: [finding], conformance: \"round-trip\", plugin };\n\t\treturn {\n\t\t\tok: false,\n\t\t\tdir: plugin.root,\n\t\t\tplatform,\n\t\t\twritten: [],\n\t\t\tentry: buildEntry(plugin),\n\t\t\tevaluation,\n\t\t\tinstructions: `Plugin \"${plugin.id}\" cannot be packaged for ${platform}.\\n\\n${formatGateFindings(evaluation)}`,\n\t\t};\n\t}\n\n\t// Strict: warnings that are tolerable in a local plugin — an unresolvable\n\t// binary, a vendor-validator nit — are not tolerable in something other people\n\t// will install on the strength of the marketplace vouching for it.\n\tlet evaluation = runStaticGates(plugin.root, { platform, strict: true });\n\tif (evaluation.plugin) {\n\t\tevaluation = withFindings(evaluation, await runSmokeGate(evaluation.plugin), true);\n\t}\n\n\t// G4 is the publish path's gate (§4.3) and nothing else's: it costs a model\n\t// call, and the question it asks — is this description actually worth the\n\t// context it occupies — only becomes other people's problem at publication.\n\t// With no judge or no gold set it reports `not-run`, which is `info`, so a\n\t// machine without a model is never blocked from publishing by a check it\n\t// could not perform.\n\tconst own = ownCandidates(plugin);\n\tif (own.length > 0) {\n\t\tconst outcome = await runTriggerEval(\n\t\t\tplugin.id,\n\t\t\t[...own, ...distractorCandidates(plugin.id, options.distractors ?? [])],\n\t\t\tloadTriggerCases(plugin.root),\n\t\t\toptions.judge,\n\t\t);\n\t\tevaluation = withFindings(evaluation, triggerFindings(outcome), true);\n\t}\n\n\tconst written: string[] = [];\n\tconst readme = writeReadme(plugin, platform);\n\tif (readme) written.push(readme);\n\n\tconst entry = buildEntry(plugin);\n\tconst record: PublishRecord = {\n\t\tpackagedAt: new Date().toISOString(),\n\t\tplatform,\n\t\tentry,\n\t\tok: evaluation.ok,\n\t};\n\twriteFileSync(path.join(plugin.root, PUBLISH_RECORD_FILE), `${JSON.stringify(record, null, 2)}\\n`, \"utf8\");\n\twritten.push(PUBLISH_RECORD_FILE);\n\n\tconst instructions = [\n\t\tevaluation.ok\n\t\t\t? `Plugin \"${plugin.id}\" is ready to publish (${platform}). The remaining step is yours.`\n\t\t\t: `Plugin \"${plugin.id}\" is NOT ready to publish — these must pass first:`,\n\t\t\"\",\n\t\tformatGateFindings(evaluation),\n\t\t\"\",\n\t\t\"Marketplace entry:\",\n\t\t\"```json\",\n\t\tJSON.stringify(entry, null, 2),\n\t\t\"```\",\n\t\t...(entryNeedsSource(entry) ? [\"\", \"Replace `source` with where the plugin will be hosted.\"] : []),\n\t\t\"\",\n\t\t...publishSteps(plugin, platform),\n\t\t\"\",\n\t\t\"Publishing is deliberately not automated: an agent that can push executable\",\n\t\t\"code into a marketplace other agents install from unattended is a supply-chain\",\n\t\t\"risk, so the last step stays with you.\",\n\t].join(\"\\n\");\n\n\treturn { ok: evaluation.ok, dir: plugin.root, platform, written, entry, evaluation, instructions };\n}\n\n// ── Staging into a local marketplace checkout ────────────────────────────────\n\n/**\n * Files that exist for hoocode's benefit and have no business in a published\n * artifact: the provenance marker and packaging's own build record.\n */\nconst INTERNAL_FILES = new Set([PUBLISH_RECORD_FILE, \".authored.json\"]);\n\n/** Where each platform's marketplace index lives, and the subdirectory plugins are vendored into. */\nconst INDEX_FILE: Record<PluginPlatform, string> = {\n\tclaude: path.join(\".claude-plugin\", \"marketplace.json\"),\n\tgithub: path.join(\".github\", \"marketplace.json\"),\n};\nconst VENDOR_DIR = \"plugins\";\n\nexport interface StageResult {\n\tok: boolean;\n\tmessage: string;\n\t/** Path the plugin was copied to, relative to the marketplace root. */\n\tpluginPath?: string;\n\t/** The index that was written, relative to the marketplace root. */\n\tindexPath?: string;\n\t/** True when the marketplace had no index for this platform and one was created. */\n\tcreatedIndex?: boolean;\n}\n\ninterface RawIndex {\n\tname?: string;\n\tplugins?: Array<Record<string, unknown>>;\n\t[key: string]: unknown;\n}\n\n/**\n * Copy a packaged plugin into a **local** marketplace checkout and add its index\n * entry, leaving the result uncommitted.\n *\n * This is the far side of the automation line drawn in §3.3, and it is where the\n * line actually falls. Everything here is local, reversible, and shows up in\n * `git status` — the human reviews a diff and decides. What stays manual is the\n * step that leaves the machine: no commit, no push, no PR. Those are what make a\n * plugin installable by other people's agents, and that is the act install is\n * trusting a human to have performed.\n *\n * Only reachable from `/plugin publish`, never from a tool: a human typed the\n * command, which is the whole point.\n */\nexport function stageIntoMarketplace(\n\tplugin: NormalizedPlugin,\n\tplatform: PluginPlatform,\n\tmarketplaceDir: string,\n): StageResult {\n\tconst root = path.resolve(marketplaceDir);\n\tif (!existsSync(root)) {\n\t\treturn { ok: false, message: `Marketplace directory not found: ${root}` };\n\t}\n\tconst pluginRoot = path.resolve(plugin.root);\n\tif (pluginRoot === root || pluginRoot.startsWith(`${root}${path.sep}`)) {\n\t\treturn { ok: false, message: `Plugin \"${plugin.id}\" already lives inside ${root}; nothing to stage.` };\n\t}\n\n\tconst indexRel = INDEX_FILE[platform];\n\tconst indexAbs = path.join(root, indexRel);\n\tlet index: RawIndex;\n\tlet createdIndex = false;\n\tif (existsSync(indexAbs)) {\n\t\ttry {\n\t\t\tindex = JSON.parse(readFileSync(indexAbs, \"utf8\")) as RawIndex;\n\t\t} catch (error) {\n\t\t\treturn { ok: false, message: `${indexRel} is not valid JSON (${(error as Error).message}); fix it first.` };\n\t\t}\n\t} else {\n\t\t// A marketplace repo that does not have an index yet is a normal starting\n\t\t// point, and refusing here would mean the first plugin can never be staged.\n\t\tindex = { name: path.basename(root), plugins: [] };\n\t\tcreatedIndex = true;\n\t}\n\tif (!Array.isArray(index.plugins)) index.plugins = [];\n\n\tconst dest = path.join(root, VENDOR_DIR, plugin.id);\n\trmSync(dest, { recursive: true, force: true });\n\tmkdirSync(path.dirname(dest), { recursive: true });\n\tcpSync(pluginRoot, dest, {\n\t\trecursive: true,\n\t\tfilter: (src) => !INTERNAL_FILES.has(path.basename(src)),\n\t});\n\n\t// Vendored in the repo, so the source is a path relative to it — not the\n\t// git origin `packagePlugin` guessed, which points at wherever the plugin was\n\t// authored rather than at the marketplace.\n\tconst entry: MarketplaceEntry = { ...buildEntry(plugin), source: `./${VENDOR_DIR}/${plugin.id}` };\n\tconst existingAt = index.plugins.findIndex((p) => p?.name === plugin.id);\n\tif (existingAt >= 0) index.plugins[existingAt] = { ...index.plugins[existingAt], ...entry };\n\telse index.plugins.push(entry as unknown as Record<string, unknown>);\n\n\tmkdirSync(path.dirname(indexAbs), { recursive: true });\n\twriteFileSync(indexAbs, `${JSON.stringify(index, null, 2)}\\n`, \"utf8\");\n\n\tconst verb = existingAt >= 0 ? \"Updated\" : \"Added\";\n\treturn {\n\t\tok: true,\n\t\tmessage:\n\t\t\t`${verb} \"${plugin.id}\" in ${path.basename(root)}: copied to ${VENDOR_DIR}/${plugin.id}/ and ` +\n\t\t\t`${createdIndex ? \"created\" : \"updated\"} ${indexRel}. Nothing was committed or pushed.`,\n\t\tpluginPath: `${VENDOR_DIR}/${plugin.id}`,\n\t\tindexPath: indexRel,\n\t\tcreatedIndex,\n\t};\n}\n\n/** Read back the record packaging left, for callers that want to show it. */\nexport function readPublishRecord(dir: string): PublishRecord | undefined {\n\ttry {\n\t\tconst file = path.join(dir, PUBLISH_RECORD_FILE);\n\t\treturn existsSync(file) ? (JSON.parse(readFileSync(file, \"utf8\")) as PublishRecord) : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
@@ -1,13 +1,19 @@
1
1
  /**
2
- * The two agent-facing canvas tools.
2
+ * The three agent-facing canvas tools.
3
3
  *
4
4
  * Design: `docs/canvas-extensions-design.md` §11.5. Copilot names its own shape in
5
5
  * the SDK types — `list_canvas_capabilities` to discover, `invoke_canvas_action` to
6
6
  * invoke — and hoocode mirrors it, for its own reason as well as fidelity:
7
7
  * `AGENTS.md` budgets the prompt at ~4,140 tokens with ~2,710 of it tool schemas,
8
8
  * and every active tool's schema is re-sent on every request. One tool per open
9
- * action would make that surface grow with how many canvases are open. Two fixed
10
- * tools keep it flat.
9
+ * action would make that surface grow with how many canvases are open. Fixed tools
10
+ * keep it flat.
11
+ *
12
+ * `reload_canvas` is hoocode's, not Copilot's, and it is what makes a canvas
13
+ * something the agent can *iterate on* rather than only drive: an edit to
14
+ * `extension.mjs` is invisible until the child forked from the old bytes is
15
+ * replaced. See {@link createReloadTool} for why reloading is the agent's to do
16
+ * while opening is not.
11
17
  *
12
18
  * **There is deliberately no "open a canvas" tool.** Opening forks a process and
13
19
  * binds a listening socket; that is a person's decision, gated by workspace trust
@@ -24,6 +30,11 @@ import { type ToolDefinition } from "../extensions/types.js";
24
30
  export declare const LIST_CANVAS_CAPABILITIES_TOOL_NAME = "list_canvas_capabilities";
25
31
  /** Tool name for action invocation, matching Copilot's. */
26
32
  export declare const INVOKE_CANVAS_ACTION_TOOL_NAME = "invoke_canvas_action";
33
+ /**
34
+ * Tool name for re-forking an edited extension. hoocode's own — Copilot has no
35
+ * equivalent because its `/create-canvas` flow reloads the panel itself.
36
+ */
37
+ export declare const RELOAD_CANVAS_TOOL_NAME = "reload_canvas";
27
38
  /**
28
39
  * Ceiling on a serialized action result, in characters.
29
40
  *
@@ -37,6 +48,15 @@ export interface CanvasCapabilitiesDetails {
37
48
  instances: number;
38
49
  actions: number;
39
50
  }
51
+ /** What `reload_canvas` reports. */
52
+ export interface CanvasReloadDetails {
53
+ extensionId: string;
54
+ reopened: number;
55
+ dropped: number;
56
+ actionsAdded: number;
57
+ actionsRemoved: number;
58
+ actionsChanged: number;
59
+ }
40
60
  /** What `invoke_canvas_action` reports. */
41
61
  export interface CanvasInvokeDetails {
42
62
  instanceId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../../src/core/tools/canvas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAEzE,8DAA8D;AAC9D,eAAO,MAAM,kCAAkC,6BAA6B,CAAC;AAC7E,2DAA2D;AAC3D,eAAO,MAAM,8BAA8B,yBAAyB,CAAC;AAErE;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AAe7C,+CAA+C;AAC/C,MAAM,WAAW,yBAAyB;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;CACnB;AAkHD;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,cAAc,GAAG,cAAc,EAAE,CAGtF","sourcesContent":["/**\n * The two agent-facing canvas tools.\n *\n * Design: `docs/canvas-extensions-design.md` §11.5. Copilot names its own shape in\n * the SDK types — `list_canvas_capabilities` to discover, `invoke_canvas_action` to\n * invoke — and hoocode mirrors it, for its own reason as well as fidelity:\n * `AGENTS.md` budgets the prompt at ~4,140 tokens with ~2,710 of it tool schemas,\n * and every active tool's schema is re-sent on every request. One tool per open\n * action would make that surface grow with how many canvases are open. Two fixed\n * tools keep it flat.\n *\n * **There is deliberately no \"open a canvas\" tool.** Opening forks a process and\n * binds a listening socket; that is a person's decision, gated by workspace trust\n * (§5). The agent drives a surface a human has already opened. This also keeps the\n * injection surface flat: a poisoned issue title rendered into a canvas can at most\n * cause an action on an instance the person chose to open.\n *\n * These are optional tools, created only when canvas support is available and at\n * least one canvas is open — so a repository without canvases pays nothing.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport type { CanvasRegistry } from \"../canvas/registry.js\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.js\";\n\n/** Tool name for capability discovery, matching Copilot's. */\nexport const LIST_CANVAS_CAPABILITIES_TOOL_NAME = \"list_canvas_capabilities\";\n/** Tool name for action invocation, matching Copilot's. */\nexport const INVOKE_CANVAS_ACTION_TOOL_NAME = \"invoke_canvas_action\";\n\n/**\n * Ceiling on a serialized action result, in characters.\n *\n * Whatever an action returns lands in the model's context window.\n * `pr-artifact-explorer` truncates its own payloads (`entries.slice(0, 200)`), but\n * nothing in the contract obliges a canvas to, so the host caps it too.\n */\nexport const CANVAS_RESULT_MAX_CHARS = 8_000;\n\nconst listParams = Type.Object({}, { additionalProperties: false });\n\nconst invokeParams = Type.Object(\n\t{\n\t\tinstanceId: Type.String({ description: \"From list_canvas_capabilities.\" }),\n\t\taction: Type.String({ description: \"Action name declared by that instance's canvas.\" }),\n\t\tinput: Type.Optional(Type.Unknown({ description: \"Action input, matching the action's declared schema.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\ntype InvokeParams = Static<typeof invokeParams>;\n\n/** What `list_canvas_capabilities` reports. */\nexport interface CanvasCapabilitiesDetails {\n\tinstances: number;\n\tactions: number;\n}\n\n/** What `invoke_canvas_action` reports. */\nexport interface CanvasInvokeDetails {\n\tinstanceId: string;\n\taction: string;\n\ttruncated: boolean;\n}\n\nfunction textResult(text: string) {\n\treturn { content: [{ type: \"text\" as const, text }] };\n}\n\n/** Serialize an action result, capped so a chatty canvas cannot flood the context. */\nfunction renderResult(value: unknown): { text: string; truncated: boolean } {\n\tconst serialized = value === undefined ? \"null\" : JSON.stringify(value, null, 1);\n\tif (serialized.length <= CANVAS_RESULT_MAX_CHARS) return { text: serialized, truncated: false };\n\treturn {\n\t\ttext: `${serialized.slice(0, CANVAS_RESULT_MAX_CHARS)}\\n… truncated at ${CANVAS_RESULT_MAX_CHARS} characters.`,\n\t\ttruncated: true,\n\t};\n}\n\n/**\n * Discovery: every open instance, its canvas, and the actions it declares with\n * their input schemas.\n *\n * Takes no parameters. A filter would add schema bytes on every request to save\n * bytes in a response the model reads once.\n */\nfunction createListCapabilitiesTool(registry: CanvasRegistry): ToolDefinition {\n\treturn defineTool<typeof listParams, CanvasCapabilitiesDetails>({\n\t\tname: LIST_CANVAS_CAPABILITIES_TOOL_NAME,\n\t\tlabel: LIST_CANVAS_CAPABILITIES_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"List the open canvases and the actions each one accepts, with their input schemas. Call this before invoke_canvas_action to learn the instanceId and the action's schema.\",\n\t\tpromptSnippet: \"Discover open canvases and the actions they accept\",\n\t\tparameters: listParams,\n\t\tasync execute() {\n\t\t\tconst instances = registry.listInstances();\n\t\t\tconst bindings = registry.activeActions();\n\t\t\tif (instances.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\t...textResult(\"No canvas is open. A person opens a canvas; you can then drive it.\"),\n\t\t\t\t\tdetails: { instances: 0, actions: 0 },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst report = instances.map((instance) => ({\n\t\t\t\tinstanceId: instance.instanceId,\n\t\t\t\tcanvas: instance.canvasId,\n\t\t\t\textension: instance.extensionId,\n\t\t\t\ttitle: instance.title,\n\t\t\t\tstatus: instance.status,\n\t\t\t\tactions: bindings\n\t\t\t\t\t.filter((binding) => binding.instanceId === instance.instanceId)\n\t\t\t\t\t.map((binding) => binding.action),\n\t\t\t}));\n\t\t\treturn {\n\t\t\t\t...textResult(JSON.stringify(report, null, 1)),\n\t\t\t\tdetails: { instances: instances.length, actions: bindings.length },\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** Invocation: run one declared action against one open instance. */\nfunction createInvokeActionTool(registry: CanvasRegistry): ToolDefinition {\n\treturn defineTool<typeof invokeParams, CanvasInvokeDetails>({\n\t\tname: INVOKE_CANVAS_ACTION_TOOL_NAME,\n\t\tlabel: INVOKE_CANVAS_ACTION_TOOL_NAME,\n\t\t// The description deliberately makes no safety claim. An earlier version said\n\t\t// actions \"cannot edit files or run commands\", which is false: a canvas\n\t\t// extension is arbitrary Node code running with the user's privileges, and\n\t\t// `pr-artifact-explorer` really does download artifacts to disk and call the\n\t\t// GitHub API. Those side effects never pass hoocode's permission gate, because\n\t\t// the gate sits in front of hoocode's own tools, not inside a forked\n\t\t// extension — the workspace-trust gate (canvas/trust.ts) is the control here,\n\t\t// not a sentence in a tool schema. Never tell the model a safety property the\n\t\t// runtime does not enforce.\n\t\tdescription:\n\t\t\t\"Invoke an action on an open canvas. Actions are implemented by the canvas extension itself: an action may change what the person is looking at and can have side effects of its own, so read the action's description before calling it.\",\n\t\tpromptSnippet: \"Act on an open canvas the user is looking at\",\n\t\tparameters: invokeParams,\n\t\tasync execute(_toolCallId, params: InvokeParams, signal) {\n\t\t\t// instanceId is a UUID and unique across every canvas, so the model does not\n\t\t\t// have to carry the extension and canvas ids too — the registry already knows\n\t\t\t// which instance a given id belongs to.\n\t\t\tconst instance = registry.listInstances().find((open) => open.instanceId === params.instanceId);\n\t\t\tif (!instance) {\n\t\t\t\t// Tools report failure by throwing here, as the built-ins do; the loop turns\n\t\t\t\t// a rejection into the model's tool result.\n\t\t\t\tconst open = registry.listInstances().map((other) => other.instanceId);\n\t\t\t\tthrow new Error(\n\t\t\t\t\topen.length === 0\n\t\t\t\t\t\t? \"No canvas is open, so there is nothing to act on.\"\n\t\t\t\t\t\t: `No open canvas instance \"${params.instanceId}\". Open instances: ${open.join(\", \")}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Honour the turn's abort signal: without this, aborting a turn leaves the\n\t\t\t\t// request running and its answer arriving for a turn nobody awaits.\n\t\t\t\tconst result = await registry.invokeAction(instance, params.action, params.input as never, { signal });\n\t\t\t\tconst { text, truncated } = renderResult(result);\n\t\t\t\treturn {\n\t\t\t\t\t...textResult(text),\n\t\t\t\t\tdetails: { instanceId: params.instanceId, action: params.action, truncated },\n\t\t\t\t};\n\t\t\t} catch (cause) {\n\t\t\t\t// Canvas handlers throw CanvasError with a machine-readable code, which the\n\t\t\t\t// runner preserves across the process boundary as CanvasCallError.code. Only\n\t\t\t\t// the message is rendered to the model, so fold the code into it rather than\n\t\t\t\t// letting the typed-error intent (§8) stop at the tool boundary.\n\t\t\t\tconst code = cause instanceof Error && \"code\" in cause ? String(cause.code) : undefined;\n\t\t\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\t\t\tthrow new Error(code ? `${code}: ${message}` : message);\n\t\t\t}\n\t\t},\n\t});\n}\n\n/**\n * The canvas tools, or none.\n *\n * Returns an empty array while nothing is open, so the two schemas are absent from\n * the prompt in the overwhelmingly common case of a repository with no canvases —\n * the same reason `registry.activeActions()` is empty until an instance exists\n * (§7). Callers re-derive this when the open set changes.\n */\nexport function createCanvasToolDefinitions(registry: CanvasRegistry): ToolDefinition[] {\n\tif (registry.listInstances().length === 0) return [];\n\treturn [createListCapabilitiesTool(registry), createInvokeActionTool(registry)];\n}\n"]}
1
+ {"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../../src/core/tools/canvas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAEzE,8DAA8D;AAC9D,eAAO,MAAM,kCAAkC,6BAA6B,CAAC;AAC7E,2DAA2D;AAC3D,eAAO,MAAM,8BAA8B,yBAAyB,CAAC;AACrE;;;GAGG;AACH,eAAO,MAAM,uBAAuB,kBAAkB,CAAC;AAEvD;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AA0B7C,+CAA+C;AAC/C,MAAM,WAAW,yBAAyB;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,oCAAoC;AACpC,MAAM,WAAW,mBAAmB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACvB;AAED,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;CACnB;AAgND;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,cAAc,GAAG,cAAc,EAAE,CAGtF","sourcesContent":["/**\n * The three agent-facing canvas tools.\n *\n * Design: `docs/canvas-extensions-design.md` §11.5. Copilot names its own shape in\n * the SDK types — `list_canvas_capabilities` to discover, `invoke_canvas_action` to\n * invoke — and hoocode mirrors it, for its own reason as well as fidelity:\n * `AGENTS.md` budgets the prompt at ~4,140 tokens with ~2,710 of it tool schemas,\n * and every active tool's schema is re-sent on every request. One tool per open\n * action would make that surface grow with how many canvases are open. Fixed tools\n * keep it flat.\n *\n * `reload_canvas` is hoocode's, not Copilot's, and it is what makes a canvas\n * something the agent can *iterate on* rather than only drive: an edit to\n * `extension.mjs` is invisible until the child forked from the old bytes is\n * replaced. See {@link createReloadTool} for why reloading is the agent's to do\n * while opening is not.\n *\n * **There is deliberately no \"open a canvas\" tool.** Opening forks a process and\n * binds a listening socket; that is a person's decision, gated by workspace trust\n * (§5). The agent drives a surface a human has already opened. This also keeps the\n * injection surface flat: a poisoned issue title rendered into a canvas can at most\n * cause an action on an instance the person chose to open.\n *\n * These are optional tools, created only when canvas support is available and at\n * least one canvas is open — so a repository without canvases pays nothing.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport type { CanvasRegistry } from \"../canvas/registry.js\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.js\";\n\n/** Tool name for capability discovery, matching Copilot's. */\nexport const LIST_CANVAS_CAPABILITIES_TOOL_NAME = \"list_canvas_capabilities\";\n/** Tool name for action invocation, matching Copilot's. */\nexport const INVOKE_CANVAS_ACTION_TOOL_NAME = \"invoke_canvas_action\";\n/**\n * Tool name for re-forking an edited extension. hoocode's own — Copilot has no\n * equivalent because its `/create-canvas` flow reloads the panel itself.\n */\nexport const RELOAD_CANVAS_TOOL_NAME = \"reload_canvas\";\n\n/**\n * Ceiling on a serialized action result, in characters.\n *\n * Whatever an action returns lands in the model's context window.\n * `pr-artifact-explorer` truncates its own payloads (`entries.slice(0, 200)`), but\n * nothing in the contract obliges a canvas to, so the host caps it too.\n */\nexport const CANVAS_RESULT_MAX_CHARS = 8_000;\n\nconst listParams = Type.Object({}, { additionalProperties: false });\n\nconst reloadParams = Type.Object(\n\t{\n\t\textensionId: Type.String({\n\t\t\tdescription: \"The extension whose code changed. From list_canvas_capabilities (the `extension` field).\",\n\t\t}),\n\t},\n\t{ additionalProperties: false },\n);\n\ntype ReloadParams = Static<typeof reloadParams>;\n\nconst invokeParams = Type.Object(\n\t{\n\t\tinstanceId: Type.String({ description: \"From list_canvas_capabilities.\" }),\n\t\taction: Type.String({ description: \"Action name declared by that instance's canvas.\" }),\n\t\tinput: Type.Optional(Type.Unknown({ description: \"Action input, matching the action's declared schema.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\ntype InvokeParams = Static<typeof invokeParams>;\n\n/** What `list_canvas_capabilities` reports. */\nexport interface CanvasCapabilitiesDetails {\n\tinstances: number;\n\tactions: number;\n}\n\n/** What `reload_canvas` reports. */\nexport interface CanvasReloadDetails {\n\textensionId: string;\n\treopened: number;\n\tdropped: number;\n\tactionsAdded: number;\n\tactionsRemoved: number;\n\tactionsChanged: number;\n}\n\n/** What `invoke_canvas_action` reports. */\nexport interface CanvasInvokeDetails {\n\tinstanceId: string;\n\taction: string;\n\ttruncated: boolean;\n}\n\nfunction textResult(text: string) {\n\treturn { content: [{ type: \"text\" as const, text }] };\n}\n\n/** Serialize an action result, capped so a chatty canvas cannot flood the context. */\nfunction renderResult(value: unknown): { text: string; truncated: boolean } {\n\tconst serialized = value === undefined ? \"null\" : JSON.stringify(value, null, 1);\n\tif (serialized.length <= CANVAS_RESULT_MAX_CHARS) return { text: serialized, truncated: false };\n\treturn {\n\t\ttext: `${serialized.slice(0, CANVAS_RESULT_MAX_CHARS)}\\n… truncated at ${CANVAS_RESULT_MAX_CHARS} characters.`,\n\t\ttruncated: true,\n\t};\n}\n\n/**\n * Discovery: every open instance, its canvas, and the actions it declares with\n * their input schemas.\n *\n * Takes no parameters. A filter would add schema bytes on every request to save\n * bytes in a response the model reads once.\n */\nfunction createListCapabilitiesTool(registry: CanvasRegistry): ToolDefinition {\n\treturn defineTool<typeof listParams, CanvasCapabilitiesDetails>({\n\t\tname: LIST_CANVAS_CAPABILITIES_TOOL_NAME,\n\t\tlabel: LIST_CANVAS_CAPABILITIES_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"List the open canvases and the actions each one accepts, with their input schemas. Call this before invoke_canvas_action to learn the instanceId and the action's schema.\",\n\t\tpromptSnippet: \"Discover open canvases and the actions they accept\",\n\t\tparameters: listParams,\n\t\tasync execute() {\n\t\t\tconst instances = registry.listInstances();\n\t\t\tconst bindings = registry.activeActions();\n\t\t\tif (instances.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\t...textResult(\"No canvas is open. A person opens a canvas; you can then drive it.\"),\n\t\t\t\t\tdetails: { instances: 0, actions: 0 },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst report = instances.map((instance) => ({\n\t\t\t\tinstanceId: instance.instanceId,\n\t\t\t\tcanvas: instance.canvasId,\n\t\t\t\textension: instance.extensionId,\n\t\t\t\ttitle: instance.title,\n\t\t\t\tstatus: instance.status,\n\t\t\t\tactions: bindings\n\t\t\t\t\t.filter((binding) => binding.instanceId === instance.instanceId)\n\t\t\t\t\t.map((binding) => binding.action),\n\t\t\t}));\n\t\t\treturn {\n\t\t\t\t...textResult(JSON.stringify(report, null, 1)),\n\t\t\t\tdetails: { instances: instances.length, actions: bindings.length },\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** Invocation: run one declared action against one open instance. */\nfunction createInvokeActionTool(registry: CanvasRegistry): ToolDefinition {\n\treturn defineTool<typeof invokeParams, CanvasInvokeDetails>({\n\t\tname: INVOKE_CANVAS_ACTION_TOOL_NAME,\n\t\tlabel: INVOKE_CANVAS_ACTION_TOOL_NAME,\n\t\t// The description deliberately makes no safety claim. An earlier version said\n\t\t// actions \"cannot edit files or run commands\", which is false: a canvas\n\t\t// extension is arbitrary Node code running with the user's privileges, and\n\t\t// `pr-artifact-explorer` really does download artifacts to disk and call the\n\t\t// GitHub API. Those side effects never pass hoocode's permission gate, because\n\t\t// the gate sits in front of hoocode's own tools, not inside a forked\n\t\t// extension — the workspace-trust gate (canvas/trust.ts) is the control here,\n\t\t// not a sentence in a tool schema. Never tell the model a safety property the\n\t\t// runtime does not enforce.\n\t\tdescription:\n\t\t\t\"Invoke an action on an open canvas. Actions are implemented by the canvas extension itself: an action may change what the person is looking at and can have side effects of its own, so read the action's description before calling it.\",\n\t\tpromptSnippet: \"Act on an open canvas the user is looking at\",\n\t\tparameters: invokeParams,\n\t\tasync execute(_toolCallId, params: InvokeParams, signal) {\n\t\t\t// instanceId is a UUID and unique across every canvas, so the model does not\n\t\t\t// have to carry the extension and canvas ids too — the registry already knows\n\t\t\t// which instance a given id belongs to.\n\t\t\tconst instance = registry.listInstances().find((open) => open.instanceId === params.instanceId);\n\t\t\tif (!instance) {\n\t\t\t\t// Tools report failure by throwing here, as the built-ins do; the loop turns\n\t\t\t\t// a rejection into the model's tool result.\n\t\t\t\tconst open = registry.listInstances().map((other) => other.instanceId);\n\t\t\t\tthrow new Error(\n\t\t\t\t\topen.length === 0\n\t\t\t\t\t\t? \"No canvas is open, so there is nothing to act on.\"\n\t\t\t\t\t\t: `No open canvas instance \"${params.instanceId}\". Open instances: ${open.join(\", \")}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Honour the turn's abort signal: without this, aborting a turn leaves the\n\t\t\t\t// request running and its answer arriving for a turn nobody awaits.\n\t\t\t\tconst result = await registry.invokeAction(instance, params.action, params.input as never, { signal });\n\t\t\t\tconst { text, truncated } = renderResult(result);\n\t\t\t\treturn {\n\t\t\t\t\t...textResult(text),\n\t\t\t\t\tdetails: { instanceId: params.instanceId, action: params.action, truncated },\n\t\t\t\t};\n\t\t\t} catch (cause) {\n\t\t\t\t// Canvas handlers throw CanvasError with a machine-readable code, which the\n\t\t\t\t// runner preserves across the process boundary as CanvasCallError.code. Only\n\t\t\t\t// the message is rendered to the model, so fold the code into it rather than\n\t\t\t\t// letting the typed-error intent (§8) stop at the tool boundary.\n\t\t\t\tconst code = cause instanceof Error && \"code\" in cause ? String(cause.code) : undefined;\n\t\t\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\t\t\tthrow new Error(code ? `${code}: ${message}` : message);\n\t\t\t}\n\t\t},\n\t});\n}\n\n/**\n * Reload: re-fork an extension whose source changed, carrying its open instances.\n *\n * This is the tool that makes a canvas *iterable* by the agent — \"add a column\",\n * \"make the header sticky\" — which is the whole point of authoring one in a\n * session. Editing `extension.mjs` alone changes nothing: the child forked from\n * the old bytes keeps serving until it is replaced.\n *\n * It reloads; it does not open. That distinction is what keeps §11.5's reasoning\n * intact. Opening is a person's decision because it starts a process from a\n * directory nobody has vouched for; reloading only restarts an extension the\n * person already opened, in a workspace they already trusted, from a path the\n * host already resolved. The model cannot reach a new extension through it, and\n * a poisoned string in some canvas's data still cannot cause one to start.\n *\n * It is not a safety boundary on the *contents* of the file, and must not be\n * described as one: whatever wrote `extension.mjs` — the model's own edit,\n * through the permission gate — is what runs.\n */\nfunction createReloadTool(registry: CanvasRegistry): ToolDefinition {\n\treturn defineTool<typeof reloadParams, CanvasReloadDetails>({\n\t\tname: RELOAD_CANVAS_TOOL_NAME,\n\t\tlabel: RELOAD_CANVAS_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Restart an open canvas extension so your edits to its source take effect. Editing the extension's file does nothing on its own — the running process was forked from the old code. Call this after every edit. It reports which actions you added, removed or changed, so use it to confirm an action you just wrote is really callable. Open instances are carried across and keep their instanceId, but each gets a NEW url: tell the person the new url, because the tab they have open is now dead.\",\n\t\tpromptSnippet: \"Restart an edited canvas so the change is live\",\n\t\tparameters: reloadParams,\n\t\tasync execute(_toolCallId, params: ReloadParams, signal) {\n\t\t\tconst running = [...new Set(registry.listInstances().map((instance) => instance.extensionId))];\n\t\t\tif (!running.includes(params.extensionId)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\trunning.length === 0\n\t\t\t\t\t\t? \"No canvas is open, so there is nothing to reload.\"\n\t\t\t\t\t\t: `Canvas extension \"${params.extensionId}\" has nothing open. Running: ${running.join(\", \")}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// A failed reload is the common case while iterating — the edit did not\n\t\t\t// parse, or threw at module scope. The registry leaves the old child\n\t\t\t// serving in that case, so this reads as \"your edit is broken and the\n\t\t\t// canvas is untouched\", which is what the model needs to hear to fix it.\n\t\t\tconst result = await registry.reload(params.extensionId, { signal });\n\n\t\t\tconst lines = [`Reloaded ${params.extensionId}. It declares: ${result.canvases.join(\", \") || \"no canvases\"}.`];\n\n\t\t\t// The capability delta is the answer to the question an author actually has\n\t\t\t// after an edit — did the host see the action I just wrote? Silence would read\n\t\t\t// as success, so \"nothing changed\" is said out loud too.\n\t\t\tconst { added, removed, changed, current } = result.actions;\n\t\t\tif (added.length + removed.length + changed.length === 0) {\n\t\t\t\tlines.push(`Actions unchanged: ${current.join(\", \") || \"none\"}.`);\n\t\t\t} else {\n\t\t\t\tif (added.length > 0) lines.push(`Actions added: ${added.join(\", \")}.`);\n\t\t\t\tif (removed.length > 0) lines.push(`Actions removed: ${removed.join(\", \")}.`);\n\t\t\t\tif (changed.length > 0) {\n\t\t\t\t\tlines.push(\n\t\t\t\t\t\t`Actions changed (description or inputSchema): ${changed.join(\", \")}. Any schema you were holding for these is stale.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlines.push(`Now callable: ${current.join(\", \") || \"none\"}.`);\n\t\t\t}\n\t\t\tif (result.reopened.length > 0) {\n\t\t\t\tlines.push(\n\t\t\t\t\t\"Re-opened (give the person the new url — their old tab points at a closed port):\",\n\t\t\t\t\t...result.reopened.map(\n\t\t\t\t\t\t(instance) =>\n\t\t\t\t\t\t\t` ${instance.canvasId} (${instance.instanceId})${instance.url ? ` — ${instance.url}` : \"\"}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (result.dropped.length > 0) {\n\t\t\t\tlines.push(\n\t\t\t\t\t\"Did not come back:\",\n\t\t\t\t\t...result.dropped.map((drop) => ` ${drop.canvasId} (${drop.instanceId}): ${drop.reason}`),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (result.reopened.length === 0 && result.dropped.length === 0) {\n\t\t\t\tlines.push(\"Nothing was open, so nothing was re-opened.\");\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...textResult(lines.join(\"\\n\")),\n\t\t\t\tdetails: {\n\t\t\t\t\textensionId: params.extensionId,\n\t\t\t\t\treopened: result.reopened.length,\n\t\t\t\t\tdropped: result.dropped.length,\n\t\t\t\t\tactionsAdded: added.length,\n\t\t\t\t\tactionsRemoved: removed.length,\n\t\t\t\t\tactionsChanged: changed.length,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t});\n}\n\n/**\n * The canvas tools, or none.\n *\n * Returns an empty array while nothing is open, so the two schemas are absent from\n * the prompt in the overwhelmingly common case of a repository with no canvases —\n * the same reason `registry.activeActions()` is empty until an instance exists\n * (§7). Callers re-derive this when the open set changes.\n */\nexport function createCanvasToolDefinitions(registry: CanvasRegistry): ToolDefinition[] {\n\tif (registry.listInstances().length === 0) return [];\n\treturn [createListCapabilitiesTool(registry), createInvokeActionTool(registry), createReloadTool(registry)];\n}\n"]}
@@ -1,13 +1,19 @@
1
1
  /**
2
- * The two agent-facing canvas tools.
2
+ * The three agent-facing canvas tools.
3
3
  *
4
4
  * Design: `docs/canvas-extensions-design.md` §11.5. Copilot names its own shape in
5
5
  * the SDK types — `list_canvas_capabilities` to discover, `invoke_canvas_action` to
6
6
  * invoke — and hoocode mirrors it, for its own reason as well as fidelity:
7
7
  * `AGENTS.md` budgets the prompt at ~4,140 tokens with ~2,710 of it tool schemas,
8
8
  * and every active tool's schema is re-sent on every request. One tool per open
9
- * action would make that surface grow with how many canvases are open. Two fixed
10
- * tools keep it flat.
9
+ * action would make that surface grow with how many canvases are open. Fixed tools
10
+ * keep it flat.
11
+ *
12
+ * `reload_canvas` is hoocode's, not Copilot's, and it is what makes a canvas
13
+ * something the agent can *iterate on* rather than only drive: an edit to
14
+ * `extension.mjs` is invisible until the child forked from the old bytes is
15
+ * replaced. See {@link createReloadTool} for why reloading is the agent's to do
16
+ * while opening is not.
11
17
  *
12
18
  * **There is deliberately no "open a canvas" tool.** Opening forks a process and
13
19
  * binds a listening socket; that is a person's decision, gated by workspace trust
@@ -24,6 +30,11 @@ import { defineTool } from "../extensions/types.js";
24
30
  export const LIST_CANVAS_CAPABILITIES_TOOL_NAME = "list_canvas_capabilities";
25
31
  /** Tool name for action invocation, matching Copilot's. */
26
32
  export const INVOKE_CANVAS_ACTION_TOOL_NAME = "invoke_canvas_action";
33
+ /**
34
+ * Tool name for re-forking an edited extension. hoocode's own — Copilot has no
35
+ * equivalent because its `/create-canvas` flow reloads the panel itself.
36
+ */
37
+ export const RELOAD_CANVAS_TOOL_NAME = "reload_canvas";
27
38
  /**
28
39
  * Ceiling on a serialized action result, in characters.
29
40
  *
@@ -33,6 +44,11 @@ export const INVOKE_CANVAS_ACTION_TOOL_NAME = "invoke_canvas_action";
33
44
  */
34
45
  export const CANVAS_RESULT_MAX_CHARS = 8_000;
35
46
  const listParams = Type.Object({}, { additionalProperties: false });
47
+ const reloadParams = Type.Object({
48
+ extensionId: Type.String({
49
+ description: "The extension whose code changed. From list_canvas_capabilities (the `extension` field).",
50
+ }),
51
+ }, { additionalProperties: false });
36
52
  const invokeParams = Type.Object({
37
53
  instanceId: Type.String({ description: "From list_canvas_capabilities." }),
38
54
  action: Type.String({ description: "Action name declared by that instance's canvas." }),
@@ -143,6 +159,85 @@ function createInvokeActionTool(registry) {
143
159
  },
144
160
  });
145
161
  }
162
+ /**
163
+ * Reload: re-fork an extension whose source changed, carrying its open instances.
164
+ *
165
+ * This is the tool that makes a canvas *iterable* by the agent — "add a column",
166
+ * "make the header sticky" — which is the whole point of authoring one in a
167
+ * session. Editing `extension.mjs` alone changes nothing: the child forked from
168
+ * the old bytes keeps serving until it is replaced.
169
+ *
170
+ * It reloads; it does not open. That distinction is what keeps §11.5's reasoning
171
+ * intact. Opening is a person's decision because it starts a process from a
172
+ * directory nobody has vouched for; reloading only restarts an extension the
173
+ * person already opened, in a workspace they already trusted, from a path the
174
+ * host already resolved. The model cannot reach a new extension through it, and
175
+ * a poisoned string in some canvas's data still cannot cause one to start.
176
+ *
177
+ * It is not a safety boundary on the *contents* of the file, and must not be
178
+ * described as one: whatever wrote `extension.mjs` — the model's own edit,
179
+ * through the permission gate — is what runs.
180
+ */
181
+ function createReloadTool(registry) {
182
+ return defineTool({
183
+ name: RELOAD_CANVAS_TOOL_NAME,
184
+ label: RELOAD_CANVAS_TOOL_NAME,
185
+ description: "Restart an open canvas extension so your edits to its source take effect. Editing the extension's file does nothing on its own — the running process was forked from the old code. Call this after every edit. It reports which actions you added, removed or changed, so use it to confirm an action you just wrote is really callable. Open instances are carried across and keep their instanceId, but each gets a NEW url: tell the person the new url, because the tab they have open is now dead.",
186
+ promptSnippet: "Restart an edited canvas so the change is live",
187
+ parameters: reloadParams,
188
+ async execute(_toolCallId, params, signal) {
189
+ const running = [...new Set(registry.listInstances().map((instance) => instance.extensionId))];
190
+ if (!running.includes(params.extensionId)) {
191
+ throw new Error(running.length === 0
192
+ ? "No canvas is open, so there is nothing to reload."
193
+ : `Canvas extension "${params.extensionId}" has nothing open. Running: ${running.join(", ")}.`);
194
+ }
195
+ // A failed reload is the common case while iterating — the edit did not
196
+ // parse, or threw at module scope. The registry leaves the old child
197
+ // serving in that case, so this reads as "your edit is broken and the
198
+ // canvas is untouched", which is what the model needs to hear to fix it.
199
+ const result = await registry.reload(params.extensionId, { signal });
200
+ const lines = [`Reloaded ${params.extensionId}. It declares: ${result.canvases.join(", ") || "no canvases"}.`];
201
+ // The capability delta is the answer to the question an author actually has
202
+ // after an edit — did the host see the action I just wrote? Silence would read
203
+ // as success, so "nothing changed" is said out loud too.
204
+ const { added, removed, changed, current } = result.actions;
205
+ if (added.length + removed.length + changed.length === 0) {
206
+ lines.push(`Actions unchanged: ${current.join(", ") || "none"}.`);
207
+ }
208
+ else {
209
+ if (added.length > 0)
210
+ lines.push(`Actions added: ${added.join(", ")}.`);
211
+ if (removed.length > 0)
212
+ lines.push(`Actions removed: ${removed.join(", ")}.`);
213
+ if (changed.length > 0) {
214
+ lines.push(`Actions changed (description or inputSchema): ${changed.join(", ")}. Any schema you were holding for these is stale.`);
215
+ }
216
+ lines.push(`Now callable: ${current.join(", ") || "none"}.`);
217
+ }
218
+ if (result.reopened.length > 0) {
219
+ lines.push("Re-opened (give the person the new url — their old tab points at a closed port):", ...result.reopened.map((instance) => ` ${instance.canvasId} (${instance.instanceId})${instance.url ? ` — ${instance.url}` : ""}`));
220
+ }
221
+ if (result.dropped.length > 0) {
222
+ lines.push("Did not come back:", ...result.dropped.map((drop) => ` ${drop.canvasId} (${drop.instanceId}): ${drop.reason}`));
223
+ }
224
+ if (result.reopened.length === 0 && result.dropped.length === 0) {
225
+ lines.push("Nothing was open, so nothing was re-opened.");
226
+ }
227
+ return {
228
+ ...textResult(lines.join("\n")),
229
+ details: {
230
+ extensionId: params.extensionId,
231
+ reopened: result.reopened.length,
232
+ dropped: result.dropped.length,
233
+ actionsAdded: added.length,
234
+ actionsRemoved: removed.length,
235
+ actionsChanged: changed.length,
236
+ },
237
+ };
238
+ },
239
+ });
240
+ }
146
241
  /**
147
242
  * The canvas tools, or none.
148
243
  *
@@ -154,6 +249,6 @@ function createInvokeActionTool(registry) {
154
249
  export function createCanvasToolDefinitions(registry) {
155
250
  if (registry.listInstances().length === 0)
156
251
  return [];
157
- return [createListCapabilitiesTool(registry), createInvokeActionTool(registry)];
252
+ return [createListCapabilitiesTool(registry), createInvokeActionTool(registry), createReloadTool(registry)];
158
253
  }
159
254
  //# sourceMappingURL=canvas.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../../src/core/tools/canvas.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The two agent-facing canvas tools.\n *\n * Design: `docs/canvas-extensions-design.md` §11.5. Copilot names its own shape in\n * the SDK types — `list_canvas_capabilities` to discover, `invoke_canvas_action` to\n * invoke — and hoocode mirrors it, for its own reason as well as fidelity:\n * `AGENTS.md` budgets the prompt at ~4,140 tokens with ~2,710 of it tool schemas,\n * and every active tool's schema is re-sent on every request. One tool per open\n * action would make that surface grow with how many canvases are open. Two fixed\n * tools keep it flat.\n *\n * **There is deliberately no \"open a canvas\" tool.** Opening forks a process and\n * binds a listening socket; that is a person's decision, gated by workspace trust\n * (§5). The agent drives a surface a human has already opened. This also keeps the\n * injection surface flat: a poisoned issue title rendered into a canvas can at most\n * cause an action on an instance the person chose to open.\n *\n * These are optional tools, created only when canvas support is available and at\n * least one canvas is open — so a repository without canvases pays nothing.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport type { CanvasRegistry } from \"../canvas/registry.js\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.js\";\n\n/** Tool name for capability discovery, matching Copilot's. */\nexport const LIST_CANVAS_CAPABILITIES_TOOL_NAME = \"list_canvas_capabilities\";\n/** Tool name for action invocation, matching Copilot's. */\nexport const INVOKE_CANVAS_ACTION_TOOL_NAME = \"invoke_canvas_action\";\n\n/**\n * Ceiling on a serialized action result, in characters.\n *\n * Whatever an action returns lands in the model's context window.\n * `pr-artifact-explorer` truncates its own payloads (`entries.slice(0, 200)`), but\n * nothing in the contract obliges a canvas to, so the host caps it too.\n */\nexport const CANVAS_RESULT_MAX_CHARS = 8_000;\n\nconst listParams = Type.Object({}, { additionalProperties: false });\n\nconst invokeParams = Type.Object(\n\t{\n\t\tinstanceId: Type.String({ description: \"From list_canvas_capabilities.\" }),\n\t\taction: Type.String({ description: \"Action name declared by that instance's canvas.\" }),\n\t\tinput: Type.Optional(Type.Unknown({ description: \"Action input, matching the action's declared schema.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\ntype InvokeParams = Static<typeof invokeParams>;\n\n/** What `list_canvas_capabilities` reports. */\nexport interface CanvasCapabilitiesDetails {\n\tinstances: number;\n\tactions: number;\n}\n\n/** What `invoke_canvas_action` reports. */\nexport interface CanvasInvokeDetails {\n\tinstanceId: string;\n\taction: string;\n\ttruncated: boolean;\n}\n\nfunction textResult(text: string) {\n\treturn { content: [{ type: \"text\" as const, text }] };\n}\n\n/** Serialize an action result, capped so a chatty canvas cannot flood the context. */\nfunction renderResult(value: unknown): { text: string; truncated: boolean } {\n\tconst serialized = value === undefined ? \"null\" : JSON.stringify(value, null, 1);\n\tif (serialized.length <= CANVAS_RESULT_MAX_CHARS) return { text: serialized, truncated: false };\n\treturn {\n\t\ttext: `${serialized.slice(0, CANVAS_RESULT_MAX_CHARS)}\\n… truncated at ${CANVAS_RESULT_MAX_CHARS} characters.`,\n\t\ttruncated: true,\n\t};\n}\n\n/**\n * Discovery: every open instance, its canvas, and the actions it declares with\n * their input schemas.\n *\n * Takes no parameters. A filter would add schema bytes on every request to save\n * bytes in a response the model reads once.\n */\nfunction createListCapabilitiesTool(registry: CanvasRegistry): ToolDefinition {\n\treturn defineTool<typeof listParams, CanvasCapabilitiesDetails>({\n\t\tname: LIST_CANVAS_CAPABILITIES_TOOL_NAME,\n\t\tlabel: LIST_CANVAS_CAPABILITIES_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"List the open canvases and the actions each one accepts, with their input schemas. Call this before invoke_canvas_action to learn the instanceId and the action's schema.\",\n\t\tpromptSnippet: \"Discover open canvases and the actions they accept\",\n\t\tparameters: listParams,\n\t\tasync execute() {\n\t\t\tconst instances = registry.listInstances();\n\t\t\tconst bindings = registry.activeActions();\n\t\t\tif (instances.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\t...textResult(\"No canvas is open. A person opens a canvas; you can then drive it.\"),\n\t\t\t\t\tdetails: { instances: 0, actions: 0 },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst report = instances.map((instance) => ({\n\t\t\t\tinstanceId: instance.instanceId,\n\t\t\t\tcanvas: instance.canvasId,\n\t\t\t\textension: instance.extensionId,\n\t\t\t\ttitle: instance.title,\n\t\t\t\tstatus: instance.status,\n\t\t\t\tactions: bindings\n\t\t\t\t\t.filter((binding) => binding.instanceId === instance.instanceId)\n\t\t\t\t\t.map((binding) => binding.action),\n\t\t\t}));\n\t\t\treturn {\n\t\t\t\t...textResult(JSON.stringify(report, null, 1)),\n\t\t\t\tdetails: { instances: instances.length, actions: bindings.length },\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** Invocation: run one declared action against one open instance. */\nfunction createInvokeActionTool(registry: CanvasRegistry): ToolDefinition {\n\treturn defineTool<typeof invokeParams, CanvasInvokeDetails>({\n\t\tname: INVOKE_CANVAS_ACTION_TOOL_NAME,\n\t\tlabel: INVOKE_CANVAS_ACTION_TOOL_NAME,\n\t\t// The description deliberately makes no safety claim. An earlier version said\n\t\t// actions \"cannot edit files or run commands\", which is false: a canvas\n\t\t// extension is arbitrary Node code running with the user's privileges, and\n\t\t// `pr-artifact-explorer` really does download artifacts to disk and call the\n\t\t// GitHub API. Those side effects never pass hoocode's permission gate, because\n\t\t// the gate sits in front of hoocode's own tools, not inside a forked\n\t\t// extension — the workspace-trust gate (canvas/trust.ts) is the control here,\n\t\t// not a sentence in a tool schema. Never tell the model a safety property the\n\t\t// runtime does not enforce.\n\t\tdescription:\n\t\t\t\"Invoke an action on an open canvas. Actions are implemented by the canvas extension itself: an action may change what the person is looking at and can have side effects of its own, so read the action's description before calling it.\",\n\t\tpromptSnippet: \"Act on an open canvas the user is looking at\",\n\t\tparameters: invokeParams,\n\t\tasync execute(_toolCallId, params: InvokeParams, signal) {\n\t\t\t// instanceId is a UUID and unique across every canvas, so the model does not\n\t\t\t// have to carry the extension and canvas ids too — the registry already knows\n\t\t\t// which instance a given id belongs to.\n\t\t\tconst instance = registry.listInstances().find((open) => open.instanceId === params.instanceId);\n\t\t\tif (!instance) {\n\t\t\t\t// Tools report failure by throwing here, as the built-ins do; the loop turns\n\t\t\t\t// a rejection into the model's tool result.\n\t\t\t\tconst open = registry.listInstances().map((other) => other.instanceId);\n\t\t\t\tthrow new Error(\n\t\t\t\t\topen.length === 0\n\t\t\t\t\t\t? \"No canvas is open, so there is nothing to act on.\"\n\t\t\t\t\t\t: `No open canvas instance \"${params.instanceId}\". Open instances: ${open.join(\", \")}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Honour the turn's abort signal: without this, aborting a turn leaves the\n\t\t\t\t// request running and its answer arriving for a turn nobody awaits.\n\t\t\t\tconst result = await registry.invokeAction(instance, params.action, params.input as never, { signal });\n\t\t\t\tconst { text, truncated } = renderResult(result);\n\t\t\t\treturn {\n\t\t\t\t\t...textResult(text),\n\t\t\t\t\tdetails: { instanceId: params.instanceId, action: params.action, truncated },\n\t\t\t\t};\n\t\t\t} catch (cause) {\n\t\t\t\t// Canvas handlers throw CanvasError with a machine-readable code, which the\n\t\t\t\t// runner preserves across the process boundary as CanvasCallError.code. Only\n\t\t\t\t// the message is rendered to the model, so fold the code into it rather than\n\t\t\t\t// letting the typed-error intent (§8) stop at the tool boundary.\n\t\t\t\tconst code = cause instanceof Error && \"code\" in cause ? String(cause.code) : undefined;\n\t\t\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\t\t\tthrow new Error(code ? `${code}: ${message}` : message);\n\t\t\t}\n\t\t},\n\t});\n}\n\n/**\n * The canvas tools, or none.\n *\n * Returns an empty array while nothing is open, so the two schemas are absent from\n * the prompt in the overwhelmingly common case of a repository with no canvases —\n * the same reason `registry.activeActions()` is empty until an instance exists\n * (§7). Callers re-derive this when the open set changes.\n */\nexport function createCanvasToolDefinitions(registry: CanvasRegistry): ToolDefinition[] {\n\tif (registry.listInstances().length === 0) return [];\n\treturn [createListCapabilitiesTool(registry), createInvokeActionTool(registry)];\n}\n"]}
1
+ {"version":3,"file":"canvas.js","sourceRoot":"","sources":["../../../src/core/tools/canvas.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The three agent-facing canvas tools.\n *\n * Design: `docs/canvas-extensions-design.md` §11.5. Copilot names its own shape in\n * the SDK types — `list_canvas_capabilities` to discover, `invoke_canvas_action` to\n * invoke — and hoocode mirrors it, for its own reason as well as fidelity:\n * `AGENTS.md` budgets the prompt at ~4,140 tokens with ~2,710 of it tool schemas,\n * and every active tool's schema is re-sent on every request. One tool per open\n * action would make that surface grow with how many canvases are open. Fixed tools\n * keep it flat.\n *\n * `reload_canvas` is hoocode's, not Copilot's, and it is what makes a canvas\n * something the agent can *iterate on* rather than only drive: an edit to\n * `extension.mjs` is invisible until the child forked from the old bytes is\n * replaced. See {@link createReloadTool} for why reloading is the agent's to do\n * while opening is not.\n *\n * **There is deliberately no \"open a canvas\" tool.** Opening forks a process and\n * binds a listening socket; that is a person's decision, gated by workspace trust\n * (§5). The agent drives a surface a human has already opened. This also keeps the\n * injection surface flat: a poisoned issue title rendered into a canvas can at most\n * cause an action on an instance the person chose to open.\n *\n * These are optional tools, created only when canvas support is available and at\n * least one canvas is open — so a repository without canvases pays nothing.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport type { CanvasRegistry } from \"../canvas/registry.js\";\nimport { defineTool, type ToolDefinition } from \"../extensions/types.js\";\n\n/** Tool name for capability discovery, matching Copilot's. */\nexport const LIST_CANVAS_CAPABILITIES_TOOL_NAME = \"list_canvas_capabilities\";\n/** Tool name for action invocation, matching Copilot's. */\nexport const INVOKE_CANVAS_ACTION_TOOL_NAME = \"invoke_canvas_action\";\n/**\n * Tool name for re-forking an edited extension. hoocode's own — Copilot has no\n * equivalent because its `/create-canvas` flow reloads the panel itself.\n */\nexport const RELOAD_CANVAS_TOOL_NAME = \"reload_canvas\";\n\n/**\n * Ceiling on a serialized action result, in characters.\n *\n * Whatever an action returns lands in the model's context window.\n * `pr-artifact-explorer` truncates its own payloads (`entries.slice(0, 200)`), but\n * nothing in the contract obliges a canvas to, so the host caps it too.\n */\nexport const CANVAS_RESULT_MAX_CHARS = 8_000;\n\nconst listParams = Type.Object({}, { additionalProperties: false });\n\nconst reloadParams = Type.Object(\n\t{\n\t\textensionId: Type.String({\n\t\t\tdescription: \"The extension whose code changed. From list_canvas_capabilities (the `extension` field).\",\n\t\t}),\n\t},\n\t{ additionalProperties: false },\n);\n\ntype ReloadParams = Static<typeof reloadParams>;\n\nconst invokeParams = Type.Object(\n\t{\n\t\tinstanceId: Type.String({ description: \"From list_canvas_capabilities.\" }),\n\t\taction: Type.String({ description: \"Action name declared by that instance's canvas.\" }),\n\t\tinput: Type.Optional(Type.Unknown({ description: \"Action input, matching the action's declared schema.\" })),\n\t},\n\t{ additionalProperties: false },\n);\n\ntype InvokeParams = Static<typeof invokeParams>;\n\n/** What `list_canvas_capabilities` reports. */\nexport interface CanvasCapabilitiesDetails {\n\tinstances: number;\n\tactions: number;\n}\n\n/** What `reload_canvas` reports. */\nexport interface CanvasReloadDetails {\n\textensionId: string;\n\treopened: number;\n\tdropped: number;\n\tactionsAdded: number;\n\tactionsRemoved: number;\n\tactionsChanged: number;\n}\n\n/** What `invoke_canvas_action` reports. */\nexport interface CanvasInvokeDetails {\n\tinstanceId: string;\n\taction: string;\n\ttruncated: boolean;\n}\n\nfunction textResult(text: string) {\n\treturn { content: [{ type: \"text\" as const, text }] };\n}\n\n/** Serialize an action result, capped so a chatty canvas cannot flood the context. */\nfunction renderResult(value: unknown): { text: string; truncated: boolean } {\n\tconst serialized = value === undefined ? \"null\" : JSON.stringify(value, null, 1);\n\tif (serialized.length <= CANVAS_RESULT_MAX_CHARS) return { text: serialized, truncated: false };\n\treturn {\n\t\ttext: `${serialized.slice(0, CANVAS_RESULT_MAX_CHARS)}\\n… truncated at ${CANVAS_RESULT_MAX_CHARS} characters.`,\n\t\ttruncated: true,\n\t};\n}\n\n/**\n * Discovery: every open instance, its canvas, and the actions it declares with\n * their input schemas.\n *\n * Takes no parameters. A filter would add schema bytes on every request to save\n * bytes in a response the model reads once.\n */\nfunction createListCapabilitiesTool(registry: CanvasRegistry): ToolDefinition {\n\treturn defineTool<typeof listParams, CanvasCapabilitiesDetails>({\n\t\tname: LIST_CANVAS_CAPABILITIES_TOOL_NAME,\n\t\tlabel: LIST_CANVAS_CAPABILITIES_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"List the open canvases and the actions each one accepts, with their input schemas. Call this before invoke_canvas_action to learn the instanceId and the action's schema.\",\n\t\tpromptSnippet: \"Discover open canvases and the actions they accept\",\n\t\tparameters: listParams,\n\t\tasync execute() {\n\t\t\tconst instances = registry.listInstances();\n\t\t\tconst bindings = registry.activeActions();\n\t\t\tif (instances.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\t...textResult(\"No canvas is open. A person opens a canvas; you can then drive it.\"),\n\t\t\t\t\tdetails: { instances: 0, actions: 0 },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst report = instances.map((instance) => ({\n\t\t\t\tinstanceId: instance.instanceId,\n\t\t\t\tcanvas: instance.canvasId,\n\t\t\t\textension: instance.extensionId,\n\t\t\t\ttitle: instance.title,\n\t\t\t\tstatus: instance.status,\n\t\t\t\tactions: bindings\n\t\t\t\t\t.filter((binding) => binding.instanceId === instance.instanceId)\n\t\t\t\t\t.map((binding) => binding.action),\n\t\t\t}));\n\t\t\treturn {\n\t\t\t\t...textResult(JSON.stringify(report, null, 1)),\n\t\t\t\tdetails: { instances: instances.length, actions: bindings.length },\n\t\t\t};\n\t\t},\n\t});\n}\n\n/** Invocation: run one declared action against one open instance. */\nfunction createInvokeActionTool(registry: CanvasRegistry): ToolDefinition {\n\treturn defineTool<typeof invokeParams, CanvasInvokeDetails>({\n\t\tname: INVOKE_CANVAS_ACTION_TOOL_NAME,\n\t\tlabel: INVOKE_CANVAS_ACTION_TOOL_NAME,\n\t\t// The description deliberately makes no safety claim. An earlier version said\n\t\t// actions \"cannot edit files or run commands\", which is false: a canvas\n\t\t// extension is arbitrary Node code running with the user's privileges, and\n\t\t// `pr-artifact-explorer` really does download artifacts to disk and call the\n\t\t// GitHub API. Those side effects never pass hoocode's permission gate, because\n\t\t// the gate sits in front of hoocode's own tools, not inside a forked\n\t\t// extension — the workspace-trust gate (canvas/trust.ts) is the control here,\n\t\t// not a sentence in a tool schema. Never tell the model a safety property the\n\t\t// runtime does not enforce.\n\t\tdescription:\n\t\t\t\"Invoke an action on an open canvas. Actions are implemented by the canvas extension itself: an action may change what the person is looking at and can have side effects of its own, so read the action's description before calling it.\",\n\t\tpromptSnippet: \"Act on an open canvas the user is looking at\",\n\t\tparameters: invokeParams,\n\t\tasync execute(_toolCallId, params: InvokeParams, signal) {\n\t\t\t// instanceId is a UUID and unique across every canvas, so the model does not\n\t\t\t// have to carry the extension and canvas ids too — the registry already knows\n\t\t\t// which instance a given id belongs to.\n\t\t\tconst instance = registry.listInstances().find((open) => open.instanceId === params.instanceId);\n\t\t\tif (!instance) {\n\t\t\t\t// Tools report failure by throwing here, as the built-ins do; the loop turns\n\t\t\t\t// a rejection into the model's tool result.\n\t\t\t\tconst open = registry.listInstances().map((other) => other.instanceId);\n\t\t\t\tthrow new Error(\n\t\t\t\t\topen.length === 0\n\t\t\t\t\t\t? \"No canvas is open, so there is nothing to act on.\"\n\t\t\t\t\t\t: `No open canvas instance \"${params.instanceId}\". Open instances: ${open.join(\", \")}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Honour the turn's abort signal: without this, aborting a turn leaves the\n\t\t\t\t// request running and its answer arriving for a turn nobody awaits.\n\t\t\t\tconst result = await registry.invokeAction(instance, params.action, params.input as never, { signal });\n\t\t\t\tconst { text, truncated } = renderResult(result);\n\t\t\t\treturn {\n\t\t\t\t\t...textResult(text),\n\t\t\t\t\tdetails: { instanceId: params.instanceId, action: params.action, truncated },\n\t\t\t\t};\n\t\t\t} catch (cause) {\n\t\t\t\t// Canvas handlers throw CanvasError with a machine-readable code, which the\n\t\t\t\t// runner preserves across the process boundary as CanvasCallError.code. Only\n\t\t\t\t// the message is rendered to the model, so fold the code into it rather than\n\t\t\t\t// letting the typed-error intent (§8) stop at the tool boundary.\n\t\t\t\tconst code = cause instanceof Error && \"code\" in cause ? String(cause.code) : undefined;\n\t\t\t\tconst message = cause instanceof Error ? cause.message : String(cause);\n\t\t\t\tthrow new Error(code ? `${code}: ${message}` : message);\n\t\t\t}\n\t\t},\n\t});\n}\n\n/**\n * Reload: re-fork an extension whose source changed, carrying its open instances.\n *\n * This is the tool that makes a canvas *iterable* by the agent — \"add a column\",\n * \"make the header sticky\" — which is the whole point of authoring one in a\n * session. Editing `extension.mjs` alone changes nothing: the child forked from\n * the old bytes keeps serving until it is replaced.\n *\n * It reloads; it does not open. That distinction is what keeps §11.5's reasoning\n * intact. Opening is a person's decision because it starts a process from a\n * directory nobody has vouched for; reloading only restarts an extension the\n * person already opened, in a workspace they already trusted, from a path the\n * host already resolved. The model cannot reach a new extension through it, and\n * a poisoned string in some canvas's data still cannot cause one to start.\n *\n * It is not a safety boundary on the *contents* of the file, and must not be\n * described as one: whatever wrote `extension.mjs` — the model's own edit,\n * through the permission gate — is what runs.\n */\nfunction createReloadTool(registry: CanvasRegistry): ToolDefinition {\n\treturn defineTool<typeof reloadParams, CanvasReloadDetails>({\n\t\tname: RELOAD_CANVAS_TOOL_NAME,\n\t\tlabel: RELOAD_CANVAS_TOOL_NAME,\n\t\tdescription:\n\t\t\t\"Restart an open canvas extension so your edits to its source take effect. Editing the extension's file does nothing on its own — the running process was forked from the old code. Call this after every edit. It reports which actions you added, removed or changed, so use it to confirm an action you just wrote is really callable. Open instances are carried across and keep their instanceId, but each gets a NEW url: tell the person the new url, because the tab they have open is now dead.\",\n\t\tpromptSnippet: \"Restart an edited canvas so the change is live\",\n\t\tparameters: reloadParams,\n\t\tasync execute(_toolCallId, params: ReloadParams, signal) {\n\t\t\tconst running = [...new Set(registry.listInstances().map((instance) => instance.extensionId))];\n\t\t\tif (!running.includes(params.extensionId)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\trunning.length === 0\n\t\t\t\t\t\t? \"No canvas is open, so there is nothing to reload.\"\n\t\t\t\t\t\t: `Canvas extension \"${params.extensionId}\" has nothing open. Running: ${running.join(\", \")}.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// A failed reload is the common case while iterating — the edit did not\n\t\t\t// parse, or threw at module scope. The registry leaves the old child\n\t\t\t// serving in that case, so this reads as \"your edit is broken and the\n\t\t\t// canvas is untouched\", which is what the model needs to hear to fix it.\n\t\t\tconst result = await registry.reload(params.extensionId, { signal });\n\n\t\t\tconst lines = [`Reloaded ${params.extensionId}. It declares: ${result.canvases.join(\", \") || \"no canvases\"}.`];\n\n\t\t\t// The capability delta is the answer to the question an author actually has\n\t\t\t// after an edit — did the host see the action I just wrote? Silence would read\n\t\t\t// as success, so \"nothing changed\" is said out loud too.\n\t\t\tconst { added, removed, changed, current } = result.actions;\n\t\t\tif (added.length + removed.length + changed.length === 0) {\n\t\t\t\tlines.push(`Actions unchanged: ${current.join(\", \") || \"none\"}.`);\n\t\t\t} else {\n\t\t\t\tif (added.length > 0) lines.push(`Actions added: ${added.join(\", \")}.`);\n\t\t\t\tif (removed.length > 0) lines.push(`Actions removed: ${removed.join(\", \")}.`);\n\t\t\t\tif (changed.length > 0) {\n\t\t\t\t\tlines.push(\n\t\t\t\t\t\t`Actions changed (description or inputSchema): ${changed.join(\", \")}. Any schema you were holding for these is stale.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlines.push(`Now callable: ${current.join(\", \") || \"none\"}.`);\n\t\t\t}\n\t\t\tif (result.reopened.length > 0) {\n\t\t\t\tlines.push(\n\t\t\t\t\t\"Re-opened (give the person the new url — their old tab points at a closed port):\",\n\t\t\t\t\t...result.reopened.map(\n\t\t\t\t\t\t(instance) =>\n\t\t\t\t\t\t\t` ${instance.canvasId} (${instance.instanceId})${instance.url ? ` — ${instance.url}` : \"\"}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (result.dropped.length > 0) {\n\t\t\t\tlines.push(\n\t\t\t\t\t\"Did not come back:\",\n\t\t\t\t\t...result.dropped.map((drop) => ` ${drop.canvasId} (${drop.instanceId}): ${drop.reason}`),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (result.reopened.length === 0 && result.dropped.length === 0) {\n\t\t\t\tlines.push(\"Nothing was open, so nothing was re-opened.\");\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...textResult(lines.join(\"\\n\")),\n\t\t\t\tdetails: {\n\t\t\t\t\textensionId: params.extensionId,\n\t\t\t\t\treopened: result.reopened.length,\n\t\t\t\t\tdropped: result.dropped.length,\n\t\t\t\t\tactionsAdded: added.length,\n\t\t\t\t\tactionsRemoved: removed.length,\n\t\t\t\t\tactionsChanged: changed.length,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t});\n}\n\n/**\n * The canvas tools, or none.\n *\n * Returns an empty array while nothing is open, so the two schemas are absent from\n * the prompt in the overwhelmingly common case of a repository with no canvases —\n * the same reason `registry.activeActions()` is empty until an instance exists\n * (§7). Callers re-derive this when the open set changes.\n */\nexport function createCanvasToolDefinitions(registry: CanvasRegistry): ToolDefinition[] {\n\tif (registry.listInstances().length === 0) return [];\n\treturn [createListCapabilitiesTool(registry), createInvokeActionTool(registry), createReloadTool(registry)];\n}\n"]}
@@ -10,11 +10,29 @@
10
10
  * reaches the abandon path (§11.6) and the extension is told to release the port it
11
11
  * may already have bound, rather than the spinner merely disappearing.
12
12
  *
13
- * The two agent tools register on the first successful open and stay for the session:
13
+ * `/new-canvas` is registered here rather than beside `/new-skill` and friends
14
+ * because it is not a file-writing command any more: it opens what it scaffolds
15
+ * and hands the agent a brief to build it, which needs this file's session and
16
+ * `pi.sendUserMessage`. Its decisions live in `core/canvas/scaffold.ts`.
17
+ *
18
+ * The agent tools register on the first successful open and stay for the session:
14
19
  * `registerTool` has no counterpart to remove a tool. So a session that never opens a
15
20
  * canvas pays nothing for them, which is the case that matters (§11.5); after the
16
21
  * first open they cost ~235 tokens and answer honestly when nothing is open.
17
22
  */
23
+ import { type CanvasSessionOptions } from "../../core/canvas/session.js";
18
24
  import type { ExtensionAPI } from "../../core/extensions/types.js";
19
- export declare function setupCanvas(pi: ExtensionAPI): void;
25
+ /**
26
+ * Test seams, and only that.
27
+ *
28
+ * `/new-canvas` opens what it writes, so driving it without a terminal needs a
29
+ * runtime that does not depend on hoocode having been built, and a home
30
+ * directory that is not the developer's. Everything else this file does is
31
+ * decided in `core/canvas/`, where it is testable without any of this.
32
+ */
33
+ export interface CanvasSetupOverrides {
34
+ homeDir?: string;
35
+ resolveRuntime?: CanvasSessionOptions["resolveRuntime"];
36
+ }
37
+ export declare function setupCanvas(pi: ExtensionAPI, overrides?: CanvasSetupOverrides): void;
20
38
  //# sourceMappingURL=canvas.d.ts.map