@ai-sdk/harness-pi 1.0.103 → 1.0.105

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @ai-sdk/harness-pi
2
2
 
3
+ ## 1.0.105
4
+
5
+ ### Patch Changes
6
+
7
+ - a514695: fix(harness-pi): let extensions read host-backed session workspaces
8
+ - c359fc0: fix(harness-pi): expose tools registered by inline extensions to models
9
+ - Updated dependencies [9e1d1b2]
10
+ - Updated dependencies [a495511]
11
+ - @ai-sdk/provider-utils@5.0.37
12
+ - @ai-sdk/harness@1.0.103
13
+
14
+ ## 1.0.104
15
+
16
+ ### Patch Changes
17
+
18
+ - eeed977: feat(harness): allow passing arbitrary headers with inference requests via `headers` property in `HarnessAgentSettings`
19
+ - a39c8bf: chore(harness): clarify `writeSkills` helper intent to require materializing skills in HOME directory
20
+ - Updated dependencies [fe86f8f]
21
+ - Updated dependencies [255cccf]
22
+ - Updated dependencies [eeed977]
23
+ - Updated dependencies [a39c8bf]
24
+ - @ai-sdk/harness@1.0.102
25
+
3
26
  ## 1.0.103
4
27
 
5
28
  ### Patch Changes
package/dist/index.js CHANGED
@@ -111,16 +111,18 @@ import {
111
111
  SessionManager,
112
112
  SettingsManager
113
113
  } from "@earendil-works/pi-coding-agent";
114
- import { mkdir as mkdir3, rm as rm2 } from "fs/promises";
114
+ import { randomUUID } from "crypto";
115
+ import { mkdir as mkdir3, rm as rm2, writeFile as writeFile3 } from "fs/promises";
115
116
  import { tmpdir } from "os";
116
- import path7 from "path";
117
+ import path6 from "path";
117
118
  import { Type as Type2 } from "typebox";
118
119
  import {
119
120
  HarnessCapabilityUnsupportedError as HarnessCapabilityUnsupportedError2
120
121
  } from "@ai-sdk/harness";
121
122
  import {
122
123
  getRestrictedSandboxSession,
123
- resolveSandboxHomeDir
124
+ resolveSandboxHomeDir,
125
+ writeSkills
124
126
  } from "@ai-sdk/harness/utils";
125
127
 
126
128
  // src/pi-auth.ts
@@ -134,7 +136,7 @@ import {
134
136
  import { access } from "fs/promises";
135
137
 
136
138
  // src/version.ts
137
- var VERSION = true ? "1.0.103" : "0.0.0-test";
139
+ var VERSION = true ? "1.0.105" : "0.0.0-test";
138
140
 
139
141
  // src/pi-auth.ts
140
142
  var DEFAULT_GATEWAY_BASE_URL = "https://ai-gateway.vercel.sh";
@@ -242,13 +244,15 @@ async function createPiModelRuntime({
242
244
  function createGatewayProviderConfig({
243
245
  apiKey,
244
246
  baseUrl,
245
- clientApp
247
+ clientApp,
248
+ headers
246
249
  }) {
247
250
  return {
248
251
  apiKey,
249
252
  baseUrl,
250
253
  authHeader: true,
251
254
  headers: {
255
+ ...headers,
252
256
  "User-Agent": clientApp,
253
257
  "x-client-app": clientApp
254
258
  }
@@ -335,7 +339,8 @@ async function registerPiProviders({
335
339
  options,
336
340
  resolvedEnv,
337
341
  registries,
338
- clientApp = HARNESS_CLIENT_APP
342
+ clientApp = HARNESS_CLIENT_APP,
343
+ headers
339
344
  }) {
340
345
  const suppliedEnvironment = isHarnessAuthenticationEnvironment(options);
341
346
  const authenticationEnvironment = suppliedEnvironment ? options : process.env;
@@ -346,7 +351,8 @@ async function registerPiProviders({
346
351
  await registerCustomProviders({
347
352
  customEnv: { ...pickOpenAIEnv(authenticationEnvironment), ...env },
348
353
  registries,
349
- clientApp
354
+ clientApp,
355
+ headers
350
356
  });
351
357
  return;
352
358
  }
@@ -355,7 +361,8 @@ async function registerPiProviders({
355
361
  await registerCustomProviders({
356
362
  customEnv: { ...pickAnthropicEnv(authenticationEnvironment), ...env },
357
363
  registries,
358
- clientApp
364
+ clientApp,
365
+ headers
359
366
  });
360
367
  return;
361
368
  }
@@ -364,7 +371,8 @@ async function registerPiProviders({
364
371
  await registerCustomProviders({
365
372
  customEnv: { ...pickProviderEnv(authenticationEnvironment), ...env },
366
373
  registries,
367
- clientApp
374
+ clientApp,
375
+ headers
368
376
  });
369
377
  return;
370
378
  }
@@ -382,7 +390,8 @@ async function registerPiProviders({
382
390
  config: createGatewayProviderConfig({
383
391
  apiKey: gatewayApiKey,
384
392
  baseUrl: gatewayBaseUrl,
385
- clientApp
393
+ clientApp,
394
+ headers
386
395
  })
387
396
  });
388
397
  return;
@@ -402,7 +411,8 @@ async function registerPiProviders({
402
411
  config: createGatewayProviderConfig({
403
412
  apiKey: gatewayApiKey,
404
413
  baseUrl: gatewayBaseUrl,
405
- clientApp
414
+ clientApp,
415
+ headers
406
416
  })
407
417
  });
408
418
  return;
@@ -411,7 +421,8 @@ async function registerPiProviders({
411
421
  await registerCustomProviders({
412
422
  customEnv: { ...pickProviderEnv(authenticationEnvironment), ...env },
413
423
  registries,
414
- clientApp
424
+ clientApp,
425
+ headers
415
426
  });
416
427
  return;
417
428
  }
@@ -444,7 +455,8 @@ function pickProviderEnv(env) {
444
455
  async function registerCustomProviders({
445
456
  customEnv,
446
457
  registries,
447
- clientApp
458
+ clientApp,
459
+ headers
448
460
  }) {
449
461
  const gatewayKey = customEnv.AI_GATEWAY_API_KEY;
450
462
  if (gatewayKey) {
@@ -456,7 +468,8 @@ async function registerCustomProviders({
456
468
  config: createGatewayProviderConfig({
457
469
  apiKey: gatewayKey,
458
470
  baseUrl,
459
- clientApp
471
+ clientApp,
472
+ headers
460
473
  })
461
474
  });
462
475
  }
@@ -469,7 +482,8 @@ async function registerCustomProviders({
469
482
  config: {
470
483
  apiKey: customEnv.OPENAI_API_KEY,
471
484
  baseUrl,
472
- authHeader: true
485
+ authHeader: true,
486
+ ...headers ? { headers } : {}
473
487
  }
474
488
  });
475
489
  }
@@ -482,9 +496,12 @@ async function registerCustomProviders({
482
496
  config: {
483
497
  apiKey: customEnv.ANTHROPIC_API_KEY,
484
498
  baseUrl,
485
- ...customEnv.ANTHROPIC_AUTH_TOKEN ? {
499
+ ...headers || customEnv.ANTHROPIC_AUTH_TOKEN ? {
486
500
  headers: {
487
- authorization: `Bearer ${customEnv.ANTHROPIC_AUTH_TOKEN}`
501
+ ...headers,
502
+ ...customEnv.ANTHROPIC_AUTH_TOKEN ? {
503
+ authorization: `Bearer ${customEnv.ANTHROPIC_AUTH_TOKEN}`
504
+ } : {}
488
505
  }
489
506
  } : {}
490
507
  }
@@ -510,7 +527,8 @@ async function registerCustomProviders({
510
527
  config: {
511
528
  apiKey,
512
529
  baseUrl,
513
- authHeader: true
530
+ authHeader: true,
531
+ ...headers ? { headers } : {}
514
532
  }
515
533
  });
516
534
  }
@@ -839,7 +857,7 @@ function createPiRemoteOps(options) {
839
857
  }
840
858
  return Buffer.from(bytes);
841
859
  };
842
- const writeFile3 = async (inputPath, content) => {
860
+ const writeFile4 = async (inputPath, content) => {
843
861
  const remotePath = options.paths.toSandboxPath(inputPath);
844
862
  const resolvedPath = await resolveWritableSandboxPath(
845
863
  remotePath,
@@ -865,7 +883,7 @@ function createPiRemoteOps(options) {
865
883
  const updated = `${current.slice(0, index)}${newText}${current.slice(
866
884
  index + oldText.length
867
885
  )}`;
868
- await writeFile3(inputPath, updated);
886
+ await writeFile4(inputPath, updated);
869
887
  return updated;
870
888
  };
871
889
  const listDirectory = async (inputPath = ".", limit = 500) => {
@@ -955,7 +973,7 @@ function createPiRemoteOps(options) {
955
973
  return {
956
974
  paths: options.paths,
957
975
  readBuffer,
958
- writeFile: writeFile3,
976
+ writeFile: writeFile4,
959
977
  editFile,
960
978
  listDirectory,
961
979
  findFiles,
@@ -986,20 +1004,6 @@ function createPiRemoteOps(options) {
986
1004
  };
987
1005
  }
988
1006
 
989
- // src/pi-skills.ts
990
- import path4 from "path";
991
- import { writeSkills } from "@ai-sdk/harness/utils";
992
- async function writePiSkills(args) {
993
- return writeSkills({
994
- sandbox: args.sandbox,
995
- rootDir: path4.posix.join(args.sandboxHomeDir, ".agents", "skills"),
996
- skills: args.skills,
997
- abortSignal: args.abortSignal,
998
- invalidSkillNameMessage: ({ name }) => `Invalid Pi skill name: ${name}`,
999
- invalidSkillFilePathMessage: ({ skillName, filePath }) => `Invalid Pi skill file path for ${skillName}: ${filePath}`
1000
- });
1001
- }
1002
-
1003
1007
  // src/pi-translate.ts
1004
1008
  import { randomBytes } from "crypto";
1005
1009
  import { secureJsonParse } from "@ai-sdk/provider-utils";
@@ -1362,7 +1366,7 @@ function toolSpecToTypeBoxParameters(jsonSchema) {
1362
1366
  // src/pi-workspace-vfs.ts
1363
1367
  import fs from "fs";
1364
1368
  import { syncBuiltinESMExports } from "module";
1365
- import path5 from "path";
1369
+ import path4 from "path";
1366
1370
  var mountedRoots = /* @__PURE__ */ new Map();
1367
1371
  var mutableFs = fs;
1368
1372
  var mutableFsPromises = fs.promises;
@@ -1392,11 +1396,11 @@ var originalFsPromises = {
1392
1396
  writeFile: fs.promises.writeFile.bind(fs.promises)
1393
1397
  };
1394
1398
  function isInsidePath2(parent, candidate) {
1395
- const relative = path5.relative(parent, candidate);
1396
- return relative === "" || !relative.startsWith("..") && !path5.isAbsolute(relative);
1399
+ const relative = path4.relative(parent, candidate);
1400
+ return relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative);
1397
1401
  }
1398
1402
  function resolveAbsolutePath(inputPath) {
1399
- return path5.isAbsolute(inputPath) ? path5.normalize(inputPath) : path5.resolve(inputPath);
1403
+ return path4.isAbsolute(inputPath) ? path4.normalize(inputPath) : path4.resolve(inputPath);
1400
1404
  }
1401
1405
  function findMountedRoot(inputPath) {
1402
1406
  const resolvedPath = resolveAbsolutePath(inputPath);
@@ -1417,26 +1421,26 @@ function mapToBackingPath(inputPath) {
1417
1421
  return null;
1418
1422
  }
1419
1423
  const resolvedPath = resolveAbsolutePath(inputPath);
1420
- const relativePath = path5.relative(mountedRoot.mountPoint, resolvedPath);
1421
- return relativePath ? path5.join(mountedRoot.backingRoot, relativePath) : mountedRoot.backingRoot;
1424
+ const relativePath = path4.relative(mountedRoot.mountPoint, resolvedPath);
1425
+ return relativePath ? path4.join(mountedRoot.backingRoot, relativePath) : mountedRoot.backingRoot;
1422
1426
  }
1423
1427
  function mapRealpathResult(inputPath, result) {
1424
1428
  const mountedRoot = findMountedRoot(inputPath);
1425
1429
  if (!mountedRoot) {
1426
1430
  return result;
1427
1431
  }
1428
- let normalizedBackingRoot = path5.resolve(mountedRoot.backingRoot);
1432
+ let normalizedBackingRoot = path4.resolve(mountedRoot.backingRoot);
1429
1433
  try {
1430
1434
  const realpath = originalFsSyncMethods.realpathSync.native ?? originalFsSyncMethods.realpathSync;
1431
- normalizedBackingRoot = path5.resolve(realpath(mountedRoot.backingRoot));
1435
+ normalizedBackingRoot = path4.resolve(realpath(mountedRoot.backingRoot));
1432
1436
  } catch {
1433
1437
  }
1434
- const normalizedResult = path5.resolve(result);
1438
+ const normalizedResult = path4.resolve(result);
1435
1439
  if (!isInsidePath2(normalizedBackingRoot, normalizedResult)) {
1436
1440
  return result;
1437
1441
  }
1438
- const relativePath = path5.relative(normalizedBackingRoot, normalizedResult);
1439
- return relativePath ? path5.join(mountedRoot.mountPoint, relativePath) : mountedRoot.mountPoint;
1442
+ const relativePath = path4.relative(normalizedBackingRoot, normalizedResult);
1443
+ return relativePath ? path4.join(mountedRoot.mountPoint, relativePath) : mountedRoot.mountPoint;
1440
1444
  }
1441
1445
  function mapRenamePaths(sourcePath, destinationPath) {
1442
1446
  const sourceRoot = findMountedRoot(sourcePath);
@@ -1631,8 +1635,8 @@ var PiWorkspaceVfs = class {
1631
1635
  if (this.backingRoot === backingRoot && this.mountPoint === mountPoint) {
1632
1636
  return;
1633
1637
  }
1634
- const resolvedBackingRoot = path5.resolve(backingRoot);
1635
- const resolvedMountPoint = path5.resolve(mountPoint);
1638
+ const resolvedBackingRoot = path4.resolve(backingRoot);
1639
+ const resolvedMountPoint = path4.resolve(mountPoint);
1636
1640
  if (this.mountPoint) {
1637
1641
  this.disposeMount();
1638
1642
  }
@@ -1661,7 +1665,7 @@ import {
1661
1665
  stat,
1662
1666
  writeFile as writeFile2
1663
1667
  } from "fs/promises";
1664
- import path6 from "path";
1668
+ import path5 from "path";
1665
1669
  import { gunzipSync } from "zlib";
1666
1670
  import { shellQuote as shellQuote2 } from "@ai-sdk/harness/utils";
1667
1671
  var PI_CONFIG_DIRS = [".pi", ".agents"];
@@ -1669,9 +1673,9 @@ var PI_CONTEXT_FILENAMES = ["AGENTS.md", "AGENTS.MD"];
1669
1673
  var ARCHIVE_BATCH_SIZE = 300;
1670
1674
  var TAR_BLOCK_SIZE = 512;
1671
1675
  function normalizeRelativePath(inputPath) {
1672
- const normalized = inputPath.split(path6.posix.sep).join(path6.sep);
1673
- const relative = path6.normalize(normalized);
1674
- if (relative === "" || relative === "." || path6.isAbsolute(relative) || relative === ".." || relative.startsWith(`..${path6.sep}`)) {
1676
+ const normalized = inputPath.split(path5.posix.sep).join(path5.sep);
1677
+ const relative = path5.normalize(normalized);
1678
+ if (relative === "" || relative === "." || path5.isAbsolute(relative) || relative === ".." || relative.startsWith(`..${path5.sep}`)) {
1675
1679
  throw new Error(
1676
1680
  `Sandbox workspace mirror received an invalid relative path: ${inputPath}`
1677
1681
  );
@@ -1694,7 +1698,7 @@ async function listRemoteWorkspaceEntries(sandbox, sandboxWorkDir) {
1694
1698
  ];
1695
1699
  const listCommand = [
1696
1700
  `pi_config_sources=(${scopedPaths.map(
1697
- (scopedPath) => shellQuote2(path6.posix.join(sandboxWorkDir, scopedPath.slice(2)))
1701
+ (scopedPath) => shellQuote2(path5.posix.join(sandboxWorkDir, scopedPath.slice(2)))
1698
1702
  ).join(" ")})`,
1699
1703
  `pi_config_relatives=(${scopedPaths.map((scopedPath) => shellQuote2(scopedPath.slice(2))).join(" ")})`,
1700
1704
  `pi_config_ancestors=(${scopedPaths.map(() => "''").join(" ")})`,
@@ -1887,8 +1891,8 @@ async function collectHostSubtree(rootDir, currentDir, directories, files) {
1887
1891
  const entries = await readdir(currentDir, { withFileTypes: true });
1888
1892
  for (const entry of entries) {
1889
1893
  if (entry.isSymbolicLink()) continue;
1890
- const absolutePath = path6.join(currentDir, entry.name);
1891
- const relativePath = path6.relative(rootDir, absolutePath);
1894
+ const absolutePath = path5.join(currentDir, entry.name);
1895
+ const relativePath = path5.relative(rootDir, absolutePath);
1892
1896
  if (entry.isDirectory()) {
1893
1897
  directories.push(relativePath);
1894
1898
  await collectHostSubtree(rootDir, absolutePath, directories, files);
@@ -1901,14 +1905,14 @@ async function collectHostScopedEntries(rootDir) {
1901
1905
  const directories = [];
1902
1906
  const files = [];
1903
1907
  for (const dir of PI_CONFIG_DIRS) {
1904
- const configDir = path6.join(rootDir, dir);
1908
+ const configDir = path5.join(rootDir, dir);
1905
1909
  if (await pathKind(configDir) === "directory") {
1906
1910
  directories.push(dir);
1907
1911
  await collectHostSubtree(rootDir, configDir, directories, files);
1908
1912
  }
1909
1913
  }
1910
1914
  for (const name of PI_CONTEXT_FILENAMES) {
1911
- if (await pathKind(path6.join(rootDir, name)) === "file") {
1915
+ if (await pathKind(path5.join(rootDir, name)) === "file") {
1912
1916
  files.push(name);
1913
1917
  }
1914
1918
  }
@@ -1920,10 +1924,10 @@ function buildRequiredDirectories(remoteDirectories, remoteFiles) {
1920
1924
  directories.add(normalizeRelativePath(directory));
1921
1925
  }
1922
1926
  for (const file of remoteFiles) {
1923
- let current = path6.dirname(normalizeRelativePath(file.relativePath));
1924
- while (current !== "." && current !== path6.sep && current.length > 0) {
1927
+ let current = path5.dirname(normalizeRelativePath(file.relativePath));
1928
+ while (current !== "." && current !== path5.sep && current.length > 0) {
1925
1929
  directories.add(current);
1926
- current = path6.dirname(current);
1930
+ current = path5.dirname(current);
1927
1931
  }
1928
1932
  }
1929
1933
  return directories;
@@ -1944,12 +1948,12 @@ async function syncHostWorkspaceFromSandbox(args) {
1944
1948
  );
1945
1949
  for (const relativePath of hostEntries.files) {
1946
1950
  if (!remoteFiles.has(relativePath)) {
1947
- await rm(path6.join(hostWorkDir, relativePath), { force: true });
1951
+ await rm(path5.join(hostWorkDir, relativePath), { force: true });
1948
1952
  }
1949
1953
  }
1950
1954
  const removableDirectories = [...hostEntries.directories].filter((p) => !requiredDirectories.has(p)).sort((a, b) => b.length - a.length);
1951
1955
  for (const relativePath of removableDirectories) {
1952
- await rm(path6.join(hostWorkDir, relativePath), {
1956
+ await rm(path5.join(hostWorkDir, relativePath), {
1953
1957
  recursive: true,
1954
1958
  force: true
1955
1959
  });
@@ -1957,7 +1961,7 @@ async function syncHostWorkspaceFromSandbox(args) {
1957
1961
  for (const relativePath of [...requiredDirectories].sort(
1958
1962
  (a, b) => a.length - b.length
1959
1963
  )) {
1960
- await mkdir2(path6.join(hostWorkDir, relativePath), { recursive: true });
1964
+ await mkdir2(path5.join(hostWorkDir, relativePath), { recursive: true });
1961
1965
  }
1962
1966
  for (let offset = 0; offset < remoteEntries.files.length; offset += ARCHIVE_BATCH_SIZE) {
1963
1967
  const batch = remoteEntries.files.slice(
@@ -1979,7 +1983,7 @@ async function syncHostWorkspaceFromSandbox(args) {
1979
1983
  }
1980
1984
  content = Buffer.from(bytes);
1981
1985
  }
1982
- const hostPath = path6.join(hostWorkDir, relativePath);
1986
+ const hostPath = path5.join(hostWorkDir, relativePath);
1983
1987
  let shouldWrite = true;
1984
1988
  try {
1985
1989
  const existing = await readFile2(hostPath);
@@ -1988,7 +1992,7 @@ async function syncHostWorkspaceFromSandbox(args) {
1988
1992
  shouldWrite = true;
1989
1993
  }
1990
1994
  if (shouldWrite) {
1991
- await mkdir2(path6.dirname(hostPath), { recursive: true });
1995
+ await mkdir2(path5.dirname(hostPath), { recursive: true });
1992
1996
  await writeFile2(hostPath, content);
1993
1997
  }
1994
1998
  }
@@ -2000,8 +2004,8 @@ var HARNESS_ID2 = "pi";
2000
2004
  var PI_MCP_ADAPTER_PACKAGE = "pi-mcp-adapter";
2001
2005
  var parkedPiSessions = /* @__PURE__ */ new Map();
2002
2006
  function isWithinDirectory(parent, child) {
2003
- const rel = path7.relative(parent, child);
2004
- return rel === "" || !rel.startsWith("..") && !path7.isAbsolute(rel);
2007
+ const rel = path6.relative(parent, child);
2008
+ return rel === "" || !rel.startsWith("..") && !path6.isAbsolute(rel);
2005
2009
  }
2006
2010
  function isWithinWorkspace(candidate, sessionWorkDir, hostWorkDir) {
2007
2011
  return isWithinDirectory(sessionWorkDir, candidate) || isWithinDirectory(hostWorkDir, candidate);
@@ -2012,8 +2016,8 @@ function createHarnessPiSkills({
2012
2016
  }) {
2013
2017
  return skills.map((skill) => {
2014
2018
  const name = safePiMetadataSegment(skill.name, "skill");
2015
- const baseDir = path7.posix.join(sandboxSkillRootDir, name);
2016
- const filePath = path7.posix.join(baseDir, "SKILL.md");
2019
+ const baseDir = path6.posix.join(sandboxSkillRootDir, name);
2020
+ const filePath = path6.posix.join(baseDir, "SKILL.md");
2017
2021
  return {
2018
2022
  name: skill.name,
2019
2023
  description: skill.description,
@@ -2071,6 +2075,26 @@ function resolveActivePiBuiltinNames(toolFiltering) {
2071
2075
  (native) => !toolFiltering.toolNames.includes(NATIVE_TO_COMMON[native] ?? native)
2072
2076
  );
2073
2077
  }
2078
+ async function isWorkspaceAvailableOnHost({
2079
+ sandbox,
2080
+ sessionWorkDir
2081
+ }) {
2082
+ const probePath = path6.join(
2083
+ sessionWorkDir,
2084
+ `.ai-sdk-harness-pi-${randomUUID()}`
2085
+ );
2086
+ const probeContent = randomUUID();
2087
+ try {
2088
+ await writeFile3(probePath, probeContent, { flag: "wx" });
2089
+ const sandboxContent = await sandbox.readBinaryFile({ path: probePath });
2090
+ return sandboxContent != null && Buffer.from(sandboxContent).equals(Buffer.from(probeContent));
2091
+ } catch {
2092
+ return false;
2093
+ } finally {
2094
+ await rm2(probePath, { force: true }).catch(() => {
2095
+ });
2096
+ }
2097
+ }
2074
2098
  async function createPiSession(input) {
2075
2099
  if (input.isResume) {
2076
2100
  const parkedSession = parkedPiSessions.get(input.sessionId);
@@ -2083,17 +2107,21 @@ async function createPiSession(input) {
2083
2107
  }
2084
2108
  }
2085
2109
  const safeSessionId = input.sessionId.replace(/[\\/: ]/g, "-");
2086
- const hostRoot = path7.join(tmpdir(), "ai-sdk-harness", "pi", safeSessionId);
2087
- const hostWorkDir = path7.join(hostRoot, "workspace");
2088
- const hostAgentDir = path7.join(hostRoot, "agent");
2089
- const hostSessionDir = path7.join(hostRoot, "sessions");
2110
+ const hostRoot = path6.join(tmpdir(), "ai-sdk-harness", "pi", safeSessionId);
2111
+ const hostAgentDir = path6.join(hostRoot, "agent");
2112
+ const hostSessionDir = path6.join(hostRoot, "sessions");
2113
+ const toolSafeSandboxSession = getRestrictedSandboxSession(
2114
+ input.sandboxSession
2115
+ );
2090
2116
  const sessionWorkDir = input.sessionWorkDir;
2117
+ const workspaceAvailableOnHost = await isWorkspaceAvailableOnHost({
2118
+ sandbox: toolSafeSandboxSession,
2119
+ sessionWorkDir
2120
+ });
2121
+ const hostWorkDir = workspaceAvailableOnHost ? path6.resolve(sessionWorkDir) : path6.join(hostRoot, "workspace");
2091
2122
  await mkdir3(hostWorkDir, { recursive: true });
2092
2123
  await mkdir3(hostAgentDir, { recursive: true });
2093
2124
  await mkdir3(hostSessionDir, { recursive: true });
2094
- const toolSafeSandboxSession = getRestrictedSandboxSession(
2095
- input.sandboxSession
2096
- );
2097
2125
  const sandboxHomeDir = await resolveSandboxHomeDir({
2098
2126
  sandbox: toolSafeSandboxSession,
2099
2127
  ...input.abortSignal ? { abortSignal: input.abortSignal } : {}
@@ -2104,7 +2132,7 @@ async function createPiSession(input) {
2104
2132
  sessionId: input.sessionId
2105
2133
  });
2106
2134
  const permissionMode = input.permissionMode ?? "allow-all";
2107
- const sandboxSkillRootDir = path7.posix.join(
2135
+ const sandboxSkillRootDir = path6.posix.join(
2108
2136
  sandboxHomeDir,
2109
2137
  ".agents",
2110
2138
  "skills"
@@ -2123,13 +2151,17 @@ async function createPiSession(input) {
2123
2151
  ...input.abortSignal ? { abortSignal: input.abortSignal } : {}
2124
2152
  });
2125
2153
  }
2126
- await syncHostWorkspaceFromSandbox({
2127
- sandbox: toolSafeSandboxSession,
2128
- sandboxWorkDir: input.sessionWorkDir,
2129
- hostWorkDir
2130
- });
2154
+ if (!workspaceAvailableOnHost) {
2155
+ await syncHostWorkspaceFromSandbox({
2156
+ sandbox: toolSafeSandboxSession,
2157
+ sandboxWorkDir: input.sessionWorkDir,
2158
+ hostWorkDir
2159
+ });
2160
+ }
2131
2161
  const workspaceVfs = new PiWorkspaceVfs();
2132
- workspaceVfs.mount(hostWorkDir, sessionWorkDir);
2162
+ if (!workspaceAvailableOnHost) {
2163
+ workspaceVfs.mount(hostWorkDir, sessionWorkDir);
2164
+ }
2133
2165
  const paths = createPiPathMapper({
2134
2166
  hostWorkDir,
2135
2167
  sandboxWorkDir: sessionWorkDir,
@@ -2138,8 +2170,8 @@ async function createPiSession(input) {
2138
2170
  const agentDir = input.agentDir ?? hostAgentDir;
2139
2171
  const modelRuntime = await createPiModelRuntime({
2140
2172
  auth: input.settings.auth,
2141
- authPath: path7.join(agentDir, "auth.json"),
2142
- modelsPath: path7.join(agentDir, "models.json")
2173
+ authPath: path6.join(agentDir, "auth.json"),
2174
+ modelsPath: path6.join(agentDir, "models.json")
2143
2175
  });
2144
2176
  const modelRegistry = new ModelRegistry(modelRuntime);
2145
2177
  const settingsManager = input.agentDir != null ? SettingsManager.create(hostWorkDir, agentDir) : SettingsManager.inMemory();
@@ -2154,7 +2186,8 @@ async function createPiSession(input) {
2154
2186
  modelRegistry,
2155
2187
  modelRuntime
2156
2188
  },
2157
- clientApp: input.clientApp
2189
+ clientApp: input.clientApp,
2190
+ headers: input.settings.headers
2158
2191
  });
2159
2192
  const resolveModel = createPiModelResolver({
2160
2193
  modelRegistry,
@@ -2363,7 +2396,7 @@ async function createPiSession(input) {
2363
2396
  deliveredDanglingResults.clear();
2364
2397
  if (!sessionFileName) {
2365
2398
  sessionFileName = safePiSessionFileName(
2366
- path7.basename(resumeSessionFilePath)
2399
+ path6.basename(resumeSessionFilePath)
2367
2400
  );
2368
2401
  }
2369
2402
  return true;
@@ -2563,7 +2596,7 @@ async function createPiSession(input) {
2563
2596
  settingsManager,
2564
2597
  resourceLoader,
2565
2598
  customTools,
2566
- ...hasMcpServers ? { noTools: "builtin" } : { tools: toolNames },
2599
+ ...hasExtensionFactories ? { noTools: "builtin" } : { tools: toolNames },
2567
2600
  ...input.settings.thinkingLevel ? { thinkingLevel: input.settings.thinkingLevel } : {},
2568
2601
  ...activeResolvedModel ? { model: activeResolvedModel } : {}
2569
2602
  });
@@ -2573,7 +2606,7 @@ async function createPiSession(input) {
2573
2606
  }
2574
2607
  const candidatePath = sessionManager.getSessionFile();
2575
2608
  if (candidatePath) {
2576
- sessionFileName = safePiSessionFileName(path7.basename(candidatePath));
2609
+ sessionFileName = safePiSessionFileName(path6.basename(candidatePath));
2577
2610
  }
2578
2611
  translatorState = createPiTranslatorState({
2579
2612
  builtinToolNames: builtinNames,
@@ -2598,11 +2631,14 @@ async function createPiSession(input) {
2598
2631
  if (stopped) {
2599
2632
  throw new Error("Pi session has been stopped.");
2600
2633
  }
2601
- const skillWriteResult = await writePiSkills({
2634
+ const skillWriteResult = await writeSkills({
2602
2635
  sandbox: toolSafeSandboxSession,
2603
- sandboxHomeDir,
2636
+ homePath: sandboxHomeDir,
2637
+ skillsDir: ".agents/skills",
2604
2638
  skills: turnOpts.skills,
2605
- abortSignal: turnOpts.abortSignal
2639
+ abortSignal: turnOpts.abortSignal,
2640
+ invalidSkillNameMessage: ({ name }) => `Invalid Pi skill name: ${name}`,
2641
+ invalidSkillFilePathMessage: ({ skillName, filePath }) => `Invalid Pi skill file path for ${skillName}: ${filePath}`
2606
2642
  });
2607
2643
  harnessSkills = createHarnessPiSkills({
2608
2644
  skills: turnOpts.skills,
@@ -2649,11 +2685,13 @@ async function createPiSession(input) {
2649
2685
  await reloadResourcesOnly();
2650
2686
  turnAbortController.signal.throwIfAborted();
2651
2687
  }
2652
- await syncHostWorkspaceFromSandbox({
2653
- sandbox: toolSafeSandboxSession,
2654
- sandboxWorkDir: input.sessionWorkDir,
2655
- hostWorkDir
2656
- });
2688
+ if (!workspaceAvailableOnHost) {
2689
+ await syncHostWorkspaceFromSandbox({
2690
+ sandbox: toolSafeSandboxSession,
2691
+ sandboxWorkDir: input.sessionWorkDir,
2692
+ hostWorkDir
2693
+ });
2694
+ }
2657
2695
  turnAbortController.signal.throwIfAborted();
2658
2696
  translatorState = createPiTranslatorState({
2659
2697
  builtinToolNames: [...PI_NATIVE_BUILTIN_NAMES],
@@ -3272,7 +3310,8 @@ function createPi(settings = {}) {
3272
3310
  ...settings.model == null ? {} : { model: settings.model },
3273
3311
  ...settings.thinkingLevel ? { thinkingLevel: settings.thinkingLevel } : {},
3274
3312
  ...settings.mcpServers ? { mcpServers: settings.mcpServers } : {},
3275
- ...settings.extensionFactories ? { extensionFactories: settings.extensionFactories } : {}
3313
+ ...settings.extensionFactories ? { extensionFactories: settings.extensionFactories } : {},
3314
+ ...startOpts.headers ? { headers: startOpts.headers } : {}
3276
3315
  },
3277
3316
  clientApp: PI_CLIENT_APP,
3278
3317
  isResume: lifecycleState != null,