@tanstack/ai-acp 0.2.5 → 0.2.6

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.
@@ -1,4 +1,4 @@
1
- import { isSecretRef, resolveGitSkillDir } from "@tanstack/ai-sandbox";
1
+ import { discoverSkillDirs, isSecretRef, resolveGitSkillDir, resolveHarnessCwd } from "@tanstack/ai-sandbox";
2
2
  //#region src/adapters/projection.ts
3
3
  /**
4
4
  * Generic workspace projector for ACP harnesses.
@@ -22,10 +22,6 @@ import { isSecretRef, resolveGitSkillDir } from "@tanstack/ai-sandbox";
22
22
  function shellQuote(value) {
23
23
  return `'${value.replace(/'/g, `'\\''`)}'`;
24
24
  }
25
- function basenameOf(path) {
26
- const segments = path.split("/").filter((segment) => segment !== "");
27
- return segments[segments.length - 1] ?? path;
28
- }
29
25
  /**
30
26
  * Make a sandbox path relative to the workspace root, so shell commands work on
31
27
  * every provider. Only `fs.*` remaps the virtual `/workspace`; a raw `/workspace`
@@ -84,11 +80,13 @@ async function projectAcpWorkspace(handle, projection, options) {
84
80
  else {
85
81
  await handle.fs.mkdir(`${projection.root}/${skillsDir}`);
86
82
  for (const skill of gitSkills) {
87
- const source = skill.into ?? resolveGitSkillDir(projection.root, skill);
88
- const relSource = relativeToRoot(projection.root, source);
89
- const relTarget = `${skillsDir}/${basenameOf(source)}`;
90
- const cp = await handle.process.exec(`cp -r ${shellQuote(relSource)} ${shellQuote(relTarget)}`, { cwd: projection.root });
91
- if (cp.exitCode !== 0) console.warn(`[${harnessName}] failed to copy gitSkill "${skill.repo}" into ${relTarget}: ${cp.stderr.trim()}`);
83
+ const discovered = await discoverSkillDirs(handle, skill.into ?? resolveGitSkillDir(projection.root, skill));
84
+ for (const { name, dir } of discovered) {
85
+ const relSource = relativeToRoot(resolveHarnessCwd(handle, projection.root), resolveHarnessCwd(handle, dir));
86
+ const relTarget = `${skillsDir}/${name}`;
87
+ const cp = await handle.process.exec(`cp -r ${shellQuote(relSource)} ${shellQuote(relTarget)}`, { cwd: projection.root });
88
+ if (cp.exitCode !== 0) console.warn(`[${harnessName}] failed to copy gitSkill "${skill.repo}" into ${relTarget}: ${cp.stderr.trim()}`);
89
+ }
92
90
  }
93
91
  }
94
92
  for (const skill of projection.skills) if (skill.kind === "agent-skill") console.warn(`[${harnessName}] agentSkill "${skill.name}" cannot be projected: there is no generic ACP primitive to install a skill by bare name. Provide it as a gitSkill instead. Skipping.`);
@@ -1 +1 @@
1
- {"version":3,"file":"projection.js","names":[],"sources":["../../../src/adapters/projection.ts"],"sourcesContent":["/**\n * Generic workspace projector for ACP harnesses.\n *\n * `withSandbox` surfaces a portable {@link WorkspaceProjection} (skills, plugins,\n * a secret resolver, a one-time marker path) via a capability. Most of it maps\n * onto ACP natively:\n *\n * - **MCP skills** → passed straight through ACP's `newSession` `mcpServers`\n * (see {@link workspaceMcpServers}); no config file is written, because an\n * ACP agent receives MCP servers over the protocol. This is the key\n * difference from file-based harnesses (Claude Code, Codex) that read MCP\n * from disk.\n * - **gitSkill repos** → linked into the harness's skills directory (when the\n * harness declares one via `skillsDir`, e.g. `.pi/skills`).\n * - **agentSkill / plugins** → no generic ACP primitive, so we warn-and-skip.\n *\n * `fileSkill` and `instructions` are already written by the provider-agnostic\n * bootstrap (into the workspace root + `AGENTS.md`), so they need no projection.\n */\nimport { isSecretRef, resolveGitSkillDir } from '@tanstack/ai-sandbox'\nimport type {\n BearerRef,\n SandboxHandle,\n SecretRef,\n WorkspaceProjection,\n} from '@tanstack/ai-sandbox'\n\n/** ACP `newSession` MCP server descriptor (HTTP transport). */\nexport interface AcpMcpServer {\n name: string\n url: string\n headers: Array<{ name: string; value: string }>\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`\n}\n\nfunction basenameOf(path: string): string {\n const segments = path.split('/').filter((segment) => segment !== '')\n return segments[segments.length - 1] ?? path\n}\n\n/**\n * Make a sandbox path relative to the workspace root, so shell commands work on\n * every provider. Only `fs.*` remaps the virtual `/workspace`; a raw `/workspace`\n * in a shell command is the real path in a container but a non-existent absolute\n * path on local-process. Running relative to the root (the exec cwd) is correct\n * everywhere.\n */\nfunction relativeToRoot(root: string, p: string): string {\n return p.startsWith(`${root}/`) ? p.slice(root.length + 1) : p\n}\n\nfunction isBearerMarker(value: unknown): value is BearerRef {\n return (\n typeof value === 'object' &&\n value !== null &&\n isSecretRef((value as { __bearerRef?: unknown }).__bearerRef)\n )\n}\n\nfunction resolveHeaderValue(\n value: string | SecretRef | BearerRef,\n resolveSecret: (ref: SecretRef) => string,\n): string {\n if (isSecretRef(value)) return resolveSecret(value)\n if (isBearerMarker(value)) return `Bearer ${resolveSecret(value.__bearerRef)}`\n return value\n}\n\n/**\n * Build ACP `mcpServers` entries from the workspace's `mcp` skills, resolving\n * every header value (SecretRef / bearer / plain string). Returns `[]` when\n * there are no MCP skills. Pass the result to `startAcpSession({ mcpServers })`.\n */\nexport function workspaceMcpServers(\n projection: WorkspaceProjection,\n): Array<AcpMcpServer> {\n const servers: Array<AcpMcpServer> = []\n for (const skill of projection.skills) {\n if (skill.kind !== 'mcp') continue\n const rawUrl = skill.config['url']\n const url = typeof rawUrl === 'string' ? rawUrl : ''\n const headers = Object.entries(skill.config.headers ?? {}).map(\n ([name, value]) => ({\n name,\n value: resolveHeaderValue(value, projection.resolveSecret),\n }),\n )\n servers.push({ name: skill.name, url, headers })\n }\n return servers\n}\n\n/**\n * Project the non-MCP parts of a workspace into an ACP harness. Links each\n * cloned `gitSkill` repo into the harness's skills directory when one is\n * declared; warns and skips `agentSkill`s, `plugin`s, and (when no `skillsDir`\n * is configured) `gitSkill`s. Idempotent — gated by the projection marker.\n *\n * MCP skills are NOT handled here; pass {@link workspaceMcpServers} to the ACP\n * session instead.\n */\nexport async function projectAcpWorkspace(\n handle: SandboxHandle,\n projection: WorkspaceProjection,\n options: { skillsDir?: string; harnessName: string },\n): Promise<void> {\n // Idempotent, non-secret operations only — gate on the one-time marker.\n if (await handle.fs.exists(projection.markerPath)) return\n\n const { skillsDir, harnessName } = options\n const gitSkills = projection.skills.filter((skill) => skill.kind === 'git')\n\n if (gitSkills.length > 0) {\n if (skillsDir === undefined) {\n for (const skill of gitSkills) {\n console.warn(\n `[${harnessName}] gitSkill \"${skill.repo}\" cannot be projected: this ` +\n 'harness declares no `skillsDir`. The clone is still available under ' +\n 'the workspace, but the harness will not auto-discover it. Skipping link.',\n )\n }\n } else {\n // Create the skills dir via fs (which remaps the virtual root), then copy\n // each clone in with paths relative to the exec cwd (the workspace root)\n // so the shell command resolves on every provider.\n await handle.fs.mkdir(`${projection.root}/${skillsDir}`)\n for (const skill of gitSkills) {\n const source = skill.into ?? resolveGitSkillDir(projection.root, skill)\n const relSource = relativeToRoot(projection.root, source)\n const relTarget = `${skillsDir}/${basenameOf(source)}`\n const cp = await handle.process.exec(\n `cp -r ${shellQuote(relSource)} ${shellQuote(relTarget)}`,\n { cwd: projection.root },\n )\n if (cp.exitCode !== 0) {\n console.warn(\n `[${harnessName}] failed to copy gitSkill \"${skill.repo}\" into ${relTarget}: ${cp.stderr.trim()}`,\n )\n }\n }\n }\n }\n\n for (const skill of projection.skills) {\n if (skill.kind === 'agent-skill') {\n console.warn(\n `[${harnessName}] agentSkill \"${skill.name}\" cannot be projected: there is ` +\n 'no generic ACP primitive to install a skill by bare name. Provide it as ' +\n 'a gitSkill instead. Skipping.',\n )\n }\n }\n for (const name of projection.plugins) {\n console.warn(\n `[${harnessName}] plugin \"${name}\" cannot be projected: ACP has no generic ` +\n 'plugin concept. Provide its functionality as a gitSkill or MCP server. Skipping.',\n )\n }\n\n await handle.fs.write(projection.markerPath, '')\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAkCA,SAAS,WAAW,OAAuB;CACzC,OAAO,IAAI,MAAM,QAAQ,MAAM,OAAO,EAAE;AAC1C;AAEA,SAAS,WAAW,MAAsB;CACxC,MAAM,WAAW,KAAK,MAAM,GAAG,CAAC,CAAC,QAAQ,YAAY,YAAY,EAAE;CACnE,OAAO,SAAS,SAAS,SAAS,MAAM;AAC1C;;;;;;;;AASA,SAAS,eAAe,MAAc,GAAmB;CACvD,OAAO,EAAE,WAAW,GAAG,KAAK,EAAE,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC,IAAI;AAC/D;AAEA,SAAS,eAAe,OAAoC;CAC1D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,YAAa,MAAoC,WAAW;AAEhE;AAEA,SAAS,mBACP,OACA,eACQ;CACR,IAAI,YAAY,KAAK,GAAG,OAAO,cAAc,KAAK;CAClD,IAAI,eAAe,KAAK,GAAG,OAAO,UAAU,cAAc,MAAM,WAAW;CAC3E,OAAO;AACT;;;;;;AAOA,SAAgB,oBACd,YACqB;CACrB,MAAM,UAA+B,CAAC;CACtC,KAAK,MAAM,SAAS,WAAW,QAAQ;EACrC,IAAI,MAAM,SAAS,OAAO;EAC1B,MAAM,SAAS,MAAM,OAAO;EAC5B,MAAM,MAAM,OAAO,WAAW,WAAW,SAAS;EAClD,MAAM,UAAU,OAAO,QAAQ,MAAM,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,KACxD,CAAC,MAAM,YAAY;GAClB;GACA,OAAO,mBAAmB,OAAO,WAAW,aAAa;EAC3D,EACF;EACA,QAAQ,KAAK;GAAE,MAAM,MAAM;GAAM;GAAK;EAAQ,CAAC;CACjD;CACA,OAAO;AACT;;;;;;;;;;AAWA,eAAsB,oBACpB,QACA,YACA,SACe;CAEf,IAAI,MAAM,OAAO,GAAG,OAAO,WAAW,UAAU,GAAG;CAEnD,MAAM,EAAE,WAAW,gBAAgB;CACnC,MAAM,YAAY,WAAW,OAAO,QAAQ,UAAU,MAAM,SAAS,KAAK;CAE1E,IAAI,UAAU,SAAS,GACrB,IAAI,cAAc,KAAA,GAChB,KAAK,MAAM,SAAS,WAClB,QAAQ,KACN,IAAI,YAAY,cAAc,MAAM,KAAK,2KAG3C;MAEG;EAIL,MAAM,OAAO,GAAG,MAAM,GAAG,WAAW,KAAK,GAAG,WAAW;EACvD,KAAK,MAAM,SAAS,WAAW;GAC7B,MAAM,SAAS,MAAM,QAAQ,mBAAmB,WAAW,MAAM,KAAK;GACtE,MAAM,YAAY,eAAe,WAAW,MAAM,MAAM;GACxD,MAAM,YAAY,GAAG,UAAU,GAAG,WAAW,MAAM;GACnD,MAAM,KAAK,MAAM,OAAO,QAAQ,KAC9B,SAAS,WAAW,SAAS,EAAE,GAAG,WAAW,SAAS,KACtD,EAAE,KAAK,WAAW,KAAK,CACzB;GACA,IAAI,GAAG,aAAa,GAClB,QAAQ,KACN,IAAI,YAAY,6BAA6B,MAAM,KAAK,SAAS,UAAU,IAAI,GAAG,OAAO,KAAK,GAChG;EAEJ;CACF;CAGF,KAAK,MAAM,SAAS,WAAW,QAC7B,IAAI,MAAM,SAAS,eACjB,QAAQ,KACN,IAAI,YAAY,gBAAgB,MAAM,KAAK,sIAG7C;CAGJ,KAAK,MAAM,QAAQ,WAAW,SAC5B,QAAQ,KACN,IAAI,YAAY,YAAY,KAAK,2HAEnC;CAGF,MAAM,OAAO,GAAG,MAAM,WAAW,YAAY,EAAE;AACjD"}
1
+ {"version":3,"file":"projection.js","names":[],"sources":["../../../src/adapters/projection.ts"],"sourcesContent":["/**\n * Generic workspace projector for ACP harnesses.\n *\n * `withSandbox` surfaces a portable {@link WorkspaceProjection} (skills, plugins,\n * a secret resolver, a one-time marker path) via a capability. Most of it maps\n * onto ACP natively:\n *\n * - **MCP skills** → passed straight through ACP's `newSession` `mcpServers`\n * (see {@link workspaceMcpServers}); no config file is written, because an\n * ACP agent receives MCP servers over the protocol. This is the key\n * difference from file-based harnesses (Claude Code, Codex) that read MCP\n * from disk.\n * - **gitSkill repos** → linked into the harness's skills directory (when the\n * harness declares one via `skillsDir`, e.g. `.pi/skills`).\n * - **agentSkill / plugins** → no generic ACP primitive, so we warn-and-skip.\n *\n * `fileSkill` and `instructions` are already written by the provider-agnostic\n * bootstrap (into the workspace root + `AGENTS.md`), so they need no projection.\n */\nimport {\n discoverSkillDirs,\n isSecretRef,\n resolveGitSkillDir,\n resolveHarnessCwd,\n} from '@tanstack/ai-sandbox'\nimport type {\n BearerRef,\n SandboxHandle,\n SecretRef,\n WorkspaceProjection,\n} from '@tanstack/ai-sandbox'\n\n/** ACP `newSession` MCP server descriptor (HTTP transport). */\nexport interface AcpMcpServer {\n name: string\n url: string\n headers: Array<{ name: string; value: string }>\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replace(/'/g, `'\\\\''`)}'`\n}\n\n/**\n * Make a sandbox path relative to the workspace root, so shell commands work on\n * every provider. Only `fs.*` remaps the virtual `/workspace`; a raw `/workspace`\n * in a shell command is the real path in a container but a non-existent absolute\n * path on local-process. Running relative to the root (the exec cwd) is correct\n * everywhere.\n */\nfunction relativeToRoot(root: string, p: string): string {\n return p.startsWith(`${root}/`) ? p.slice(root.length + 1) : p\n}\n\nfunction isBearerMarker(value: unknown): value is BearerRef {\n return (\n typeof value === 'object' &&\n value !== null &&\n isSecretRef((value as { __bearerRef?: unknown }).__bearerRef)\n )\n}\n\nfunction resolveHeaderValue(\n value: string | SecretRef | BearerRef,\n resolveSecret: (ref: SecretRef) => string,\n): string {\n if (isSecretRef(value)) return resolveSecret(value)\n if (isBearerMarker(value)) return `Bearer ${resolveSecret(value.__bearerRef)}`\n return value\n}\n\n/**\n * Build ACP `mcpServers` entries from the workspace's `mcp` skills, resolving\n * every header value (SecretRef / bearer / plain string). Returns `[]` when\n * there are no MCP skills. Pass the result to `startAcpSession({ mcpServers })`.\n */\nexport function workspaceMcpServers(\n projection: WorkspaceProjection,\n): Array<AcpMcpServer> {\n const servers: Array<AcpMcpServer> = []\n for (const skill of projection.skills) {\n if (skill.kind !== 'mcp') continue\n const rawUrl = skill.config['url']\n const url = typeof rawUrl === 'string' ? rawUrl : ''\n const headers = Object.entries(skill.config.headers ?? {}).map(\n ([name, value]) => ({\n name,\n value: resolveHeaderValue(value, projection.resolveSecret),\n }),\n )\n servers.push({ name: skill.name, url, headers })\n }\n return servers\n}\n\n/**\n * Project the non-MCP parts of a workspace into an ACP harness. Links each\n * cloned `gitSkill` repo into the harness's skills directory when one is\n * declared; warns and skips `agentSkill`s, `plugin`s, and (when no `skillsDir`\n * is configured) `gitSkill`s. Idempotent — gated by the projection marker.\n *\n * MCP skills are NOT handled here; pass {@link workspaceMcpServers} to the ACP\n * session instead.\n */\nexport async function projectAcpWorkspace(\n handle: SandboxHandle,\n projection: WorkspaceProjection,\n options: { skillsDir?: string; harnessName: string },\n): Promise<void> {\n // Idempotent, non-secret operations only — gate on the one-time marker.\n if (await handle.fs.exists(projection.markerPath)) return\n\n const { skillsDir, harnessName } = options\n const gitSkills = projection.skills.filter((skill) => skill.kind === 'git')\n\n if (gitSkills.length > 0) {\n if (skillsDir === undefined) {\n for (const skill of gitSkills) {\n console.warn(\n `[${harnessName}] gitSkill \"${skill.repo}\" cannot be projected: this ` +\n 'harness declares no `skillsDir`. The clone is still available under ' +\n 'the workspace, but the harness will not auto-discover it. Skipping link.',\n )\n }\n } else {\n // Create the skills dir via fs (which remaps the virtual root), then copy\n // each clone in with paths relative to the exec cwd (the workspace root)\n // so the shell command resolves on every provider.\n await handle.fs.mkdir(`${projection.root}/${skillsDir}`)\n for (const skill of gitSkills) {\n // Keep virtual `/workspace` paths for fs discovery. handle.fs remaps\n // them. Remap only when building shell-relative paths so Daytona and\n // local-process both resolve the same clone.\n const source = skill.into ?? resolveGitSkillDir(projection.root, skill)\n const discovered = await discoverSkillDirs(handle, source)\n for (const { name, dir } of discovered) {\n const realRoot = resolveHarnessCwd(handle, projection.root)\n const realDir = resolveHarnessCwd(handle, dir)\n const relSource = relativeToRoot(realRoot, realDir)\n const relTarget = `${skillsDir}/${name}`\n const cp = await handle.process.exec(\n `cp -r ${shellQuote(relSource)} ${shellQuote(relTarget)}`,\n { cwd: projection.root },\n )\n if (cp.exitCode !== 0) {\n console.warn(\n `[${harnessName}] failed to copy gitSkill \"${skill.repo}\" into ${relTarget}: ${cp.stderr.trim()}`,\n )\n }\n }\n }\n }\n }\n\n for (const skill of projection.skills) {\n if (skill.kind === 'agent-skill') {\n console.warn(\n `[${harnessName}] agentSkill \"${skill.name}\" cannot be projected: there is ` +\n 'no generic ACP primitive to install a skill by bare name. Provide it as ' +\n 'a gitSkill instead. Skipping.',\n )\n }\n }\n for (const name of projection.plugins) {\n console.warn(\n `[${harnessName}] plugin \"${name}\" cannot be projected: ACP has no generic ` +\n 'plugin concept. Provide its functionality as a gitSkill or MCP server. Skipping.',\n )\n }\n\n await handle.fs.write(projection.markerPath, '')\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAuCA,SAAS,WAAW,OAAuB;CACzC,OAAO,IAAI,MAAM,QAAQ,MAAM,OAAO,EAAE;AAC1C;;;;;;;;AASA,SAAS,eAAe,MAAc,GAAmB;CACvD,OAAO,EAAE,WAAW,GAAG,KAAK,EAAE,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC,IAAI;AAC/D;AAEA,SAAS,eAAe,OAAoC;CAC1D,OACE,OAAO,UAAU,YACjB,UAAU,QACV,YAAa,MAAoC,WAAW;AAEhE;AAEA,SAAS,mBACP,OACA,eACQ;CACR,IAAI,YAAY,KAAK,GAAG,OAAO,cAAc,KAAK;CAClD,IAAI,eAAe,KAAK,GAAG,OAAO,UAAU,cAAc,MAAM,WAAW;CAC3E,OAAO;AACT;;;;;;AAOA,SAAgB,oBACd,YACqB;CACrB,MAAM,UAA+B,CAAC;CACtC,KAAK,MAAM,SAAS,WAAW,QAAQ;EACrC,IAAI,MAAM,SAAS,OAAO;EAC1B,MAAM,SAAS,MAAM,OAAO;EAC5B,MAAM,MAAM,OAAO,WAAW,WAAW,SAAS;EAClD,MAAM,UAAU,OAAO,QAAQ,MAAM,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,KACxD,CAAC,MAAM,YAAY;GAClB;GACA,OAAO,mBAAmB,OAAO,WAAW,aAAa;EAC3D,EACF;EACA,QAAQ,KAAK;GAAE,MAAM,MAAM;GAAM;GAAK;EAAQ,CAAC;CACjD;CACA,OAAO;AACT;;;;;;;;;;AAWA,eAAsB,oBACpB,QACA,YACA,SACe;CAEf,IAAI,MAAM,OAAO,GAAG,OAAO,WAAW,UAAU,GAAG;CAEnD,MAAM,EAAE,WAAW,gBAAgB;CACnC,MAAM,YAAY,WAAW,OAAO,QAAQ,UAAU,MAAM,SAAS,KAAK;CAE1E,IAAI,UAAU,SAAS,GACrB,IAAI,cAAc,KAAA,GAChB,KAAK,MAAM,SAAS,WAClB,QAAQ,KACN,IAAI,YAAY,cAAc,MAAM,KAAK,2KAG3C;MAEG;EAIL,MAAM,OAAO,GAAG,MAAM,GAAG,WAAW,KAAK,GAAG,WAAW;EACvD,KAAK,MAAM,SAAS,WAAW;GAK7B,MAAM,aAAa,MAAM,kBAAkB,QAD5B,MAAM,QAAQ,mBAAmB,WAAW,MAAM,KAAK,CACb;GACzD,KAAK,MAAM,EAAE,MAAM,SAAS,YAAY;IAGtC,MAAM,YAAY,eAFD,kBAAkB,QAAQ,WAAW,IAErB,GADjB,kBAAkB,QAAQ,GACC,CAAO;IAClD,MAAM,YAAY,GAAG,UAAU,GAAG;IAClC,MAAM,KAAK,MAAM,OAAO,QAAQ,KAC9B,SAAS,WAAW,SAAS,EAAE,GAAG,WAAW,SAAS,KACtD,EAAE,KAAK,WAAW,KAAK,CACzB;IACA,IAAI,GAAG,aAAa,GAClB,QAAQ,KACN,IAAI,YAAY,6BAA6B,MAAM,KAAK,SAAS,UAAU,IAAI,GAAG,OAAO,KAAK,GAChG;GAEJ;EACF;CACF;CAGF,KAAK,MAAM,SAAS,WAAW,QAC7B,IAAI,MAAM,SAAS,eACjB,QAAQ,KACN,IAAI,YAAY,gBAAgB,MAAM,KAAK,sIAG7C;CAGJ,KAAK,MAAM,QAAQ,WAAW,SAC5B,QAAQ,KACN,IAAI,YAAY,YAAY,KAAK,2HAEnC;CAGF,MAAM,OAAO,GAAG,MAAM,WAAW,YAAY,EAAE;AACjD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-acp",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Shared Agent Client Protocol (ACP) transport, session, and AG-UI translation for TanStack AI harness adapters.",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -37,12 +37,12 @@
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@tanstack/ai": "^0.44.0",
40
- "@tanstack/ai-sandbox": "^0.3.1"
40
+ "@tanstack/ai-sandbox": "^0.3.2"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@vitest/coverage-v8": "4.0.14",
44
44
  "@tanstack/ai": "0.44.0",
45
- "@tanstack/ai-sandbox": "0.3.1",
45
+ "@tanstack/ai-sandbox": "0.3.2",
46
46
  "@tanstack/ai-sandbox-local-process": "0.2.1"
47
47
  },
48
48
  "scripts": {
@@ -51,8 +51,8 @@
51
51
  "lint:fix": "oxlint src --type-aware --fix",
52
52
  "test:build": "publint --strict",
53
53
  "test:oxlint": "oxlint src --type-aware",
54
- "test:lib": "vitest",
55
- "test:lib:dev": "pnpm test:lib --watch",
54
+ "test:lib": "vitest run --testTimeout=30000",
55
+ "test:lib:dev": "vitest --watch",
56
56
  "test:types": "tsc"
57
57
  }
58
58
  }
@@ -17,7 +17,12 @@
17
17
  * `fileSkill` and `instructions` are already written by the provider-agnostic
18
18
  * bootstrap (into the workspace root + `AGENTS.md`), so they need no projection.
19
19
  */
20
- import { isSecretRef, resolveGitSkillDir } from '@tanstack/ai-sandbox'
20
+ import {
21
+ discoverSkillDirs,
22
+ isSecretRef,
23
+ resolveGitSkillDir,
24
+ resolveHarnessCwd,
25
+ } from '@tanstack/ai-sandbox'
21
26
  import type {
22
27
  BearerRef,
23
28
  SandboxHandle,
@@ -36,11 +41,6 @@ function shellQuote(value: string): string {
36
41
  return `'${value.replace(/'/g, `'\\''`)}'`
37
42
  }
38
43
 
39
- function basenameOf(path: string): string {
40
- const segments = path.split('/').filter((segment) => segment !== '')
41
- return segments[segments.length - 1] ?? path
42
- }
43
-
44
44
  /**
45
45
  * Make a sandbox path relative to the workspace root, so shell commands work on
46
46
  * every provider. Only `fs.*` remaps the virtual `/workspace`; a raw `/workspace`
@@ -128,17 +128,25 @@ export async function projectAcpWorkspace(
128
128
  // so the shell command resolves on every provider.
129
129
  await handle.fs.mkdir(`${projection.root}/${skillsDir}`)
130
130
  for (const skill of gitSkills) {
131
+ // Keep virtual `/workspace` paths for fs discovery. handle.fs remaps
132
+ // them. Remap only when building shell-relative paths so Daytona and
133
+ // local-process both resolve the same clone.
131
134
  const source = skill.into ?? resolveGitSkillDir(projection.root, skill)
132
- const relSource = relativeToRoot(projection.root, source)
133
- const relTarget = `${skillsDir}/${basenameOf(source)}`
134
- const cp = await handle.process.exec(
135
- `cp -r ${shellQuote(relSource)} ${shellQuote(relTarget)}`,
136
- { cwd: projection.root },
137
- )
138
- if (cp.exitCode !== 0) {
139
- console.warn(
140
- `[${harnessName}] failed to copy gitSkill "${skill.repo}" into ${relTarget}: ${cp.stderr.trim()}`,
135
+ const discovered = await discoverSkillDirs(handle, source)
136
+ for (const { name, dir } of discovered) {
137
+ const realRoot = resolveHarnessCwd(handle, projection.root)
138
+ const realDir = resolveHarnessCwd(handle, dir)
139
+ const relSource = relativeToRoot(realRoot, realDir)
140
+ const relTarget = `${skillsDir}/${name}`
141
+ const cp = await handle.process.exec(
142
+ `cp -r ${shellQuote(relSource)} ${shellQuote(relTarget)}`,
143
+ { cwd: projection.root },
141
144
  )
145
+ if (cp.exitCode !== 0) {
146
+ console.warn(
147
+ `[${harnessName}] failed to copy gitSkill "${skill.repo}" into ${relTarget}: ${cp.stderr.trim()}`,
148
+ )
149
+ }
142
150
  }
143
151
  }
144
152
  }