@psnext/slingcli 2.5.20260602-4 → 2.5.20260607-2

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 (144) hide show
  1. package/node_modules/@aws-sdk/core/dist-cjs/index.js +12 -6
  2. package/node_modules/@aws-sdk/core/dist-cjs/submodules/httpAuthSchemes/index.js +12 -6
  3. package/node_modules/@aws-sdk/core/dist-es/submodules/httpAuthSchemes/aws_sdk/AwsSdkSigV4ASigner.js +1 -0
  4. package/node_modules/@aws-sdk/core/dist-es/submodules/httpAuthSchemes/aws_sdk/AwsSdkSigV4Signer.js +11 -6
  5. package/node_modules/@aws-sdk/core/package.json +7 -7
  6. package/node_modules/@aws-sdk/credential-provider-env/package.json +6 -6
  7. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-cjs/index.js +6 -4
  8. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js +7 -6
  9. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js +2 -1
  10. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-es/node-http2.js +2 -0
  11. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-es/node-https.js +2 -0
  12. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-types/node-http-handler.d.ts +2 -2
  13. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-types/node-http2-handler.d.ts +1 -1
  14. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-types/node-http2.d.ts +2 -0
  15. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-types/node-https.d.ts +2 -0
  16. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/package.json +1 -1
  17. package/node_modules/@aws-sdk/credential-provider-http/package.json +8 -8
  18. package/node_modules/@aws-sdk/credential-provider-ini/package.json +14 -14
  19. package/node_modules/@aws-sdk/credential-provider-login/package.json +7 -7
  20. package/node_modules/@aws-sdk/credential-provider-node/package.json +12 -12
  21. package/node_modules/@aws-sdk/credential-provider-process/package.json +6 -6
  22. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/package.json +7 -7
  23. package/node_modules/@aws-sdk/credential-provider-sso/package.json +8 -8
  24. package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +7 -7
  25. package/node_modules/@aws-sdk/eventstream-handler-node/package.json +5 -5
  26. package/node_modules/@aws-sdk/middleware-eventstream/package.json +5 -5
  27. package/node_modules/@aws-sdk/middleware-websocket/package.json +8 -8
  28. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +99 -106
  29. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +62 -120
  30. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +55 -86
  31. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +73 -104
  32. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +102 -185
  33. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/auth/httpAuthSchemeProvider.js +4 -5
  34. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/endpoint/bdd.js +21 -57
  35. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/runtimeConfig.browser.js +1 -2
  36. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/runtimeConfig.js +2 -4
  37. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/schemas/schemas_0.js +73 -41
  38. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/auth/httpAuthSchemeProvider.js +4 -5
  39. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/endpoint/bdd.js +20 -52
  40. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/runtimeConfig.browser.js +1 -2
  41. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/runtimeConfig.js +2 -4
  42. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/schemas/schemas_0.js +36 -59
  43. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/auth/httpAuthSchemeProvider.js +4 -5
  44. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/endpoint/bdd.js +17 -45
  45. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/runtimeConfig.browser.js +1 -2
  46. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/runtimeConfig.js +2 -4
  47. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/schemas/schemas_0.js +32 -32
  48. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/auth/httpAuthSchemeProvider.js +4 -5
  49. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/endpoint/bdd.js +17 -45
  50. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/runtimeConfig.browser.js +1 -2
  51. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/runtimeConfig.js +2 -4
  52. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/schemas/schemas_0.js +50 -50
  53. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/auth/httpAuthSchemeProvider.js +4 -5
  54. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/defaultRoleAssumers.js +1 -1
  55. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/endpoint/bdd.js +34 -96
  56. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/runtimeConfig.browser.js +1 -2
  57. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/runtimeConfig.js +3 -6
  58. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/schemas/schemas_0.js +63 -80
  59. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-cjs/index.js +6 -4
  60. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js +7 -6
  61. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js +2 -1
  62. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-es/node-http2.js +2 -0
  63. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-es/node-https.js +2 -0
  64. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-types/node-http-handler.d.ts +2 -2
  65. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-types/node-http2-handler.d.ts +1 -1
  66. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-types/node-http2.d.ts +2 -0
  67. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-types/node-https.d.ts +2 -0
  68. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/package.json +1 -1
  69. package/node_modules/@aws-sdk/nested-clients/package.json +9 -9
  70. package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +5 -5
  71. package/node_modules/@aws-sdk/types/package.json +3 -3
  72. package/node_modules/@aws-sdk/util-locate-window/package.json +2 -2
  73. package/node_modules/@aws-sdk/xml-builder/package.json +3 -3
  74. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  75. package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js +3 -0
  76. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +45 -0
  77. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +827 -140
  78. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +32 -12
  79. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js +13 -1
  80. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +4 -2
  81. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  82. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.js +15 -2
  83. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +1 -0
  84. package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.js +4 -3
  85. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/branch-summarization.js +9 -3
  86. package/node_modules/@earendil-works/pi-coding-agent/dist/core/export-html/template.js +19 -6
  87. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +3 -0
  88. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js +47 -13
  89. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.js +72 -0
  90. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-display-names.js +3 -0
  91. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js +28 -20
  92. package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.js +2 -30
  93. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +71 -30
  94. package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js +1 -0
  95. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.js +1 -1
  96. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.js +1 -1
  97. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.js +1 -1
  98. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js +1 -1
  99. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +1 -1
  100. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js +1 -1
  101. package/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js +130 -0
  102. package/node_modules/@earendil-works/pi-coding-agent/dist/index.js +1 -0
  103. package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +54 -2
  104. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +2 -2
  105. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js +1 -1
  106. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js +7 -0
  107. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/index.js +1 -0
  108. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +3 -12
  109. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +22 -0
  110. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/trust-selector.js +86 -0
  111. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +40 -0
  112. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js +55 -8
  113. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/git.js +54 -22
  114. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/open-browser.js +22 -0
  115. package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +12 -12
  116. package/node_modules/@earendil-works/pi-coding-agent/package.json +5 -4
  117. package/node_modules/@earendil-works/pi-tui/dist/utils.js +3 -0
  118. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  119. package/node_modules/@smithy/credential-provider-imds/dist-es/remoteProvider/httpRequest.js +2 -2
  120. package/node_modules/@smithy/credential-provider-imds/dist-es/remoteProvider/node-http.js +2 -0
  121. package/node_modules/@smithy/credential-provider-imds/dist-types/remoteProvider/httpRequest.d.ts +1 -1
  122. package/node_modules/@smithy/credential-provider-imds/dist-types/remoteProvider/node-http.d.ts +2 -0
  123. package/node_modules/@smithy/credential-provider-imds/package.json +1 -1
  124. package/node_modules/@types/node/README.md +1 -1
  125. package/node_modules/@types/node/http2.d.ts +3 -0
  126. package/node_modules/@types/node/package.json +2 -2
  127. package/node_modules/gaxios/build/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  128. package/node_modules/gaxios/build/esm/tsconfig.tsbuildinfo +1 -1
  129. package/node_modules/gaxios/package.json +7 -9
  130. package/node_modules/google-auth-library/build/src/auth/gdchclient.d.ts +47 -0
  131. package/node_modules/google-auth-library/build/src/auth/gdchclient.js +307 -0
  132. package/node_modules/google-auth-library/build/src/auth/googleauth.d.ts +5 -4
  133. package/node_modules/google-auth-library/build/src/auth/googleauth.js +18 -1
  134. package/node_modules/google-auth-library/build/src/index.d.ts +1 -0
  135. package/node_modules/google-auth-library/build/src/index.js +4 -1
  136. package/node_modules/google-auth-library/package.json +2 -4
  137. package/node_modules/semver/classes/semver.js +19 -2
  138. package/node_modules/semver/package.json +1 -1
  139. package/node_modules/undici/lib/global.js +1 -1
  140. package/node_modules/undici/lib/handler/unwrap-handler.js +6 -0
  141. package/node_modules/undici/package.json +1 -1
  142. package/node_modules/undici/types/dispatcher.d.ts +2 -0
  143. package/package.json +6 -6
  144. package/slingshot/index.js +275 -273
@@ -54,23 +54,25 @@ export function loadProjectContextFiles(options) {
54
54
  contextFiles.push(globalContext);
55
55
  seenPaths.add(globalContext.path);
56
56
  }
57
- const ancestorContextFiles = [];
58
- let currentDir = resolvedCwd;
59
- const root = resolve("/");
60
- while (true) {
61
- const contextFile = loadContextFileFromDir(currentDir);
62
- if (contextFile && !seenPaths.has(contextFile.path)) {
63
- ancestorContextFiles.unshift(contextFile);
64
- seenPaths.add(contextFile.path);
65
- }
66
- if (currentDir === root)
67
- break;
68
- const parentDir = resolve(currentDir, "..");
69
- if (parentDir === currentDir)
70
- break;
71
- currentDir = parentDir;
72
- }
73
- contextFiles.push(...ancestorContextFiles);
57
+ if (options.projectTrusted !== false) {
58
+ const ancestorContextFiles = [];
59
+ let currentDir = resolvedCwd;
60
+ const root = resolve("/");
61
+ while (true) {
62
+ const contextFile = loadContextFileFromDir(currentDir);
63
+ if (contextFile && !seenPaths.has(contextFile.path)) {
64
+ ancestorContextFiles.unshift(contextFile);
65
+ seenPaths.add(contextFile.path);
66
+ }
67
+ if (currentDir === root)
68
+ break;
69
+ const parentDir = resolve(currentDir, "..");
70
+ if (parentDir === currentDir)
71
+ break;
72
+ currentDir = parentDir;
73
+ }
74
+ contextFiles.push(...ancestorContextFiles);
75
+ }
74
76
  return contextFiles;
75
77
  }
76
78
  export class DefaultResourceLoader {
@@ -333,7 +335,13 @@ export class DefaultResourceLoader {
333
335
  }
334
336
  }
335
337
  const agentsFiles = {
336
- agentsFiles: this.noContextFiles ? [] : loadProjectContextFiles({ cwd: this.cwd, agentDir: this.agentDir }),
338
+ agentsFiles: this.noContextFiles
339
+ ? []
340
+ : loadProjectContextFiles({
341
+ cwd: this.cwd,
342
+ agentDir: this.agentDir,
343
+ projectTrusted: this.settingsManager.isProjectTrusted(),
344
+ }),
337
345
  };
338
346
  const resolvedAgentsFiles = this.agentsFilesOverride ? this.agentsFilesOverride(agentsFiles) : agentsFiles;
339
347
  this.agentsFiles = resolvedAgentsFiles.agentsFiles;
@@ -667,7 +675,7 @@ export class DefaultResourceLoader {
667
675
  }
668
676
  discoverSystemPromptFile() {
669
677
  const projectPath = join(this.cwd, CONFIG_DIR_NAME, "SYSTEM.md");
670
- if (existsSync(projectPath)) {
678
+ if (this.settingsManager.isProjectTrusted() && existsSync(projectPath)) {
671
679
  return projectPath;
672
680
  }
673
681
  const globalPath = join(this.agentDir, "SYSTEM.md");
@@ -678,7 +686,7 @@ export class DefaultResourceLoader {
678
686
  }
679
687
  discoverAppendSystemPromptFile() {
680
688
  const projectPath = join(this.cwd, CONFIG_DIR_NAME, "APPEND_SYSTEM.md");
681
- if (existsSync(projectPath)) {
689
+ if (this.settingsManager.isProjectTrusted() && existsSync(projectPath)) {
682
690
  return projectPath;
683
691
  }
684
692
  const globalPath = join(this.agentDir, "APPEND_SYSTEM.md");
@@ -10,10 +10,10 @@ import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
10
10
  import { convertToLlm } from "./messages.js";
11
11
  import { ModelRegistry } from "./model-registry.js";
12
12
  import { findInitialModel } from "./model-resolver.js";
13
+ import { mergeProviderAttributionHeaders } from "./provider-attribution.js";
13
14
  import { DefaultResourceLoader } from "./resource-loader.js";
14
15
  import { getDefaultSessionDir, SessionManager } from "./session-manager.js";
15
16
  import { SettingsManager } from "./settings-manager.js";
16
- import { isInstallTelemetryEnabled } from "./telemetry.js";
17
17
  import { time } from "./timings.js";
18
18
  import { createBashTool, createCodingTools, createEditTool, createFindTool, createGrepTool, createLsTool, createReadOnlyTools, createReadTool, createWriteTool, withFileMutationQueue, } from "./tools/index.js";
19
19
  // Re-exports
@@ -25,31 +25,6 @@ createCodingTools, createReadOnlyTools, createReadTool, createBashTool, createEd
25
25
  function getDefaultAgentDir() {
26
26
  return getAgentDir();
27
27
  }
28
- function getAttributionHeaders(model, settingsManager, sessionId) {
29
- if (sessionId &&
30
- (model.provider === "opencode" || model.provider === "opencode-go" || model.baseUrl.includes("opencode.ai"))) {
31
- return { "x-opencode-session": sessionId, "x-opencode-client": "pi" };
32
- }
33
- if (!isInstallTelemetryEnabled(settingsManager)) {
34
- return undefined;
35
- }
36
- if (model.provider === "openrouter" || model.baseUrl.includes("openrouter.ai")) {
37
- return {
38
- "HTTP-Referer": "https://pi.dev",
39
- "X-OpenRouter-Title": "pi",
40
- "X-OpenRouter-Categories": "cli-agent",
41
- };
42
- }
43
- if (model.provider === "cloudflare-workers-ai" ||
44
- model.provider === "cloudflare-ai-gateway" ||
45
- model.baseUrl.includes("api.cloudflare.com") ||
46
- model.baseUrl.includes("gateway.ai.cloudflare.com")) {
47
- return {
48
- "User-Agent": "pi-coding-agent",
49
- };
50
- }
51
- return undefined;
52
- }
53
28
  /**
54
29
  * Create an AgentSession with the specified options.
55
30
  *
@@ -210,7 +185,6 @@ export async function createAgentSession(options = {}) {
210
185
  const effectiveTimeoutMs = httpIdleTimeoutMs === 0 ? 2147483647 : httpIdleTimeoutMs;
211
186
  const timeoutMs = options?.timeoutMs ?? providerRetrySettings.timeoutMs ?? effectiveTimeoutMs;
212
187
  const websocketConnectTimeoutMs = options?.websocketConnectTimeoutMs ?? settingsManager.getWebSocketConnectTimeoutMs();
213
- const attributionHeaders = getAttributionHeaders(model, settingsManager, options?.sessionId);
214
188
  return streamSimple(model, context, {
215
189
  ...options,
216
190
  apiKey: auth.apiKey,
@@ -218,9 +192,7 @@ export async function createAgentSession(options = {}) {
218
192
  websocketConnectTimeoutMs,
219
193
  maxRetries: options?.maxRetries ?? providerRetrySettings.maxRetries,
220
194
  maxRetryDelayMs: options?.maxRetryDelayMs ?? providerRetrySettings.maxRetryDelayMs,
221
- headers: attributionHeaders || auth.headers || options?.headers
222
- ? { ...attributionHeaders, ...auth.headers, ...options?.headers }
223
- : undefined,
195
+ headers: mergeProviderAttributionHeaders(model, settingsManager, options?.sessionId, auth.headers, options?.headers),
224
196
  });
225
197
  },
226
198
  onPayload: async (payload, _model) => {
@@ -123,6 +123,7 @@ export class SettingsManager {
123
123
  globalSettings;
124
124
  projectSettings;
125
125
  settings;
126
+ projectTrusted;
126
127
  modifiedFields = new Set(); // Track global fields modified during session
127
128
  modifiedNestedFields = new Map(); // Track global nested field modifications
128
129
  modifiedProjectFields = new Set(); // Track project fields modified during session
@@ -131,24 +132,26 @@ export class SettingsManager {
131
132
  projectSettingsLoadError = null; // Track if project settings file had parse errors
132
133
  writeQueue = Promise.resolve();
133
134
  errors;
134
- constructor(storage, initialGlobal, initialProject, globalLoadError = null, projectLoadError = null, initialErrors = []) {
135
+ constructor(storage, initialGlobal, initialProject, globalLoadError = null, projectLoadError = null, initialErrors = [], projectTrusted = true) {
135
136
  this.storage = storage;
136
137
  this.globalSettings = initialGlobal;
137
138
  this.projectSettings = initialProject;
139
+ this.projectTrusted = projectTrusted;
138
140
  this.globalSettingsLoadError = globalLoadError;
139
141
  this.projectSettingsLoadError = projectLoadError;
140
142
  this.errors = [...initialErrors];
141
143
  this.settings = deepMergeSettings(this.globalSettings, this.projectSettings);
142
144
  }
143
145
  /** Create a SettingsManager that loads from files */
144
- static create(cwd, agentDir = getAgentDir()) {
146
+ static create(cwd, agentDir = getAgentDir(), options = {}) {
145
147
  const storage = new FileSettingsStorage(cwd, agentDir);
146
- return SettingsManager.fromStorage(storage);
148
+ return SettingsManager.fromStorage(storage, options);
147
149
  }
148
150
  /** Create a SettingsManager from an arbitrary storage backend */
149
- static fromStorage(storage) {
151
+ static fromStorage(storage, options = {}) {
152
+ const projectTrusted = options.projectTrusted ?? true;
150
153
  const globalLoad = SettingsManager.tryLoadFromStorage(storage, "global");
151
- const projectLoad = SettingsManager.tryLoadFromStorage(storage, "project");
154
+ const projectLoad = SettingsManager.tryLoadFromStorage(storage, "project", projectTrusted);
152
155
  const initialErrors = [];
153
156
  if (globalLoad.error) {
154
157
  initialErrors.push({ scope: "global", error: globalLoad.error });
@@ -156,7 +159,7 @@ export class SettingsManager {
156
159
  if (projectLoad.error) {
157
160
  initialErrors.push({ scope: "project", error: projectLoad.error });
158
161
  }
159
- return new SettingsManager(storage, globalLoad.settings, projectLoad.settings, globalLoad.error, projectLoad.error, initialErrors);
162
+ return new SettingsManager(storage, globalLoad.settings, projectLoad.settings, globalLoad.error, projectLoad.error, initialErrors, projectTrusted);
160
163
  }
161
164
  /** Create an in-memory SettingsManager (no file I/O) */
162
165
  static inMemory(settings = {}) {
@@ -165,7 +168,10 @@ export class SettingsManager {
165
168
  storage.withLock("global", () => JSON.stringify(initialSettings, null, 2));
166
169
  return SettingsManager.fromStorage(storage);
167
170
  }
168
- static loadFromStorage(storage, scope) {
171
+ static loadFromStorage(storage, scope, projectTrusted = true) {
172
+ if (scope === "project" && !projectTrusted) {
173
+ return {};
174
+ }
169
175
  let content;
170
176
  storage.withLock(scope, (current) => {
171
177
  content = current;
@@ -177,9 +183,9 @@ export class SettingsManager {
177
183
  const settings = JSON.parse(content);
178
184
  return SettingsManager.migrateSettings(settings);
179
185
  }
180
- static tryLoadFromStorage(storage, scope) {
186
+ static tryLoadFromStorage(storage, scope, projectTrusted = true) {
181
187
  try {
182
- return { settings: SettingsManager.loadFromStorage(storage, scope), error: null };
188
+ return { settings: SettingsManager.loadFromStorage(storage, scope, projectTrusted), error: null };
183
189
  }
184
190
  catch (error) {
185
191
  return { settings: {}, error: error };
@@ -239,6 +245,30 @@ export class SettingsManager {
239
245
  getProjectSettings() {
240
246
  return structuredClone(this.projectSettings);
241
247
  }
248
+ isProjectTrusted() {
249
+ return this.projectTrusted;
250
+ }
251
+ setProjectTrusted(trusted) {
252
+ if (this.projectTrusted === trusted) {
253
+ return;
254
+ }
255
+ this.projectTrusted = trusted;
256
+ this.modifiedProjectFields.clear();
257
+ this.modifiedProjectNestedFields.clear();
258
+ if (!trusted) {
259
+ this.projectSettings = {};
260
+ this.projectSettingsLoadError = null;
261
+ this.settings = deepMergeSettings(this.globalSettings, this.projectSettings);
262
+ return;
263
+ }
264
+ const projectLoad = SettingsManager.tryLoadFromStorage(this.storage, "project", trusted);
265
+ this.projectSettings = projectLoad.settings;
266
+ this.projectSettingsLoadError = projectLoad.error;
267
+ if (projectLoad.error) {
268
+ this.recordError("project", projectLoad.error);
269
+ }
270
+ this.settings = deepMergeSettings(this.globalSettings, this.projectSettings);
271
+ }
242
272
  async reload() {
243
273
  await this.writeQueue;
244
274
  const globalLoad = SettingsManager.tryLoadFromStorage(this.storage, "global");
@@ -254,7 +284,7 @@ export class SettingsManager {
254
284
  this.modifiedNestedFields.clear();
255
285
  this.modifiedProjectFields.clear();
256
286
  this.modifiedProjectNestedFields.clear();
257
- const projectLoad = SettingsManager.tryLoadFromStorage(this.storage, "project");
287
+ const projectLoad = SettingsManager.tryLoadFromStorage(this.storage, "project", this.projectTrusted);
258
288
  if (!projectLoad.error) {
259
289
  this.projectSettings = projectLoad.settings;
260
290
  this.projectSettingsLoadError = null;
@@ -289,6 +319,11 @@ export class SettingsManager {
289
319
  this.modifiedProjectNestedFields.get(field).add(nestedKey);
290
320
  }
291
321
  }
322
+ assertProjectTrustedForWrite() {
323
+ if (!this.projectTrusted) {
324
+ throw new Error("Project is not trusted; refusing to write project settings");
325
+ }
326
+ }
292
327
  recordError(scope, error) {
293
328
  const normalizedError = error instanceof Error ? error : new Error(String(error));
294
329
  this.errors.push({ scope, error: normalizedError });
@@ -305,6 +340,9 @@ export class SettingsManager {
305
340
  enqueueWrite(scope, task) {
306
341
  this.writeQueue = this.writeQueue
307
342
  .then(() => {
343
+ if (scope === "project") {
344
+ this.assertProjectTrustedForWrite();
345
+ }
308
346
  task();
309
347
  this.clearModifiedScope(scope);
310
348
  })
@@ -357,6 +395,7 @@ export class SettingsManager {
357
395
  });
358
396
  }
359
397
  saveProjectSettings(settings) {
398
+ this.assertProjectTrustedForWrite();
360
399
  this.projectSettings = structuredClone(settings);
361
400
  this.settings = deepMergeSettings(this.globalSettings, this.projectSettings);
362
401
  if (this.projectSettingsLoadError) {
@@ -369,6 +408,13 @@ export class SettingsManager {
369
408
  this.persistScopedSettings("project", snapshotProjectSettings, modifiedFields, modifiedNestedFields);
370
409
  });
371
410
  }
411
+ updateProjectSettings(field, update) {
412
+ this.assertProjectTrustedForWrite();
413
+ const projectSettings = structuredClone(this.projectSettings);
414
+ update(projectSettings);
415
+ this.markProjectModified(field);
416
+ this.saveProjectSettings(projectSettings);
417
+ }
372
418
  async flush() {
373
419
  await this.writeQueue;
374
420
  }
@@ -589,10 +635,9 @@ export class SettingsManager {
589
635
  this.save();
590
636
  }
591
637
  setProjectPackages(packages) {
592
- const projectSettings = structuredClone(this.projectSettings);
593
- projectSettings.packages = packages;
594
- this.markProjectModified("packages");
595
- this.saveProjectSettings(projectSettings);
638
+ this.updateProjectSettings("packages", (settings) => {
639
+ settings.packages = packages;
640
+ });
596
641
  }
597
642
  getExtensionPaths() {
598
643
  return [...(this.settings.extensions ?? [])];
@@ -603,10 +648,9 @@ export class SettingsManager {
603
648
  this.save();
604
649
  }
605
650
  setProjectExtensionPaths(paths) {
606
- const projectSettings = structuredClone(this.projectSettings);
607
- projectSettings.extensions = paths;
608
- this.markProjectModified("extensions");
609
- this.saveProjectSettings(projectSettings);
651
+ this.updateProjectSettings("extensions", (settings) => {
652
+ settings.extensions = paths;
653
+ });
610
654
  }
611
655
  getSkillPaths() {
612
656
  return [...(this.settings.skills ?? [])];
@@ -617,10 +661,9 @@ export class SettingsManager {
617
661
  this.save();
618
662
  }
619
663
  setProjectSkillPaths(paths) {
620
- const projectSettings = structuredClone(this.projectSettings);
621
- projectSettings.skills = paths;
622
- this.markProjectModified("skills");
623
- this.saveProjectSettings(projectSettings);
664
+ this.updateProjectSettings("skills", (settings) => {
665
+ settings.skills = paths;
666
+ });
624
667
  }
625
668
  getPromptTemplatePaths() {
626
669
  return [...(this.settings.prompts ?? [])];
@@ -631,10 +674,9 @@ export class SettingsManager {
631
674
  this.save();
632
675
  }
633
676
  setProjectPromptTemplatePaths(paths) {
634
- const projectSettings = structuredClone(this.projectSettings);
635
- projectSettings.prompts = paths;
636
- this.markProjectModified("prompts");
637
- this.saveProjectSettings(projectSettings);
677
+ this.updateProjectSettings("prompts", (settings) => {
678
+ settings.prompts = paths;
679
+ });
638
680
  }
639
681
  getThemePaths() {
640
682
  return [...(this.settings.themes ?? [])];
@@ -645,10 +687,9 @@ export class SettingsManager {
645
687
  this.save();
646
688
  }
647
689
  setProjectThemePaths(paths) {
648
- const projectSettings = structuredClone(this.projectSettings);
649
- projectSettings.themes = paths;
650
- this.markProjectModified("themes");
651
- this.saveProjectSettings(projectSettings);
690
+ this.updateProjectSettings("themes", (settings) => {
691
+ settings.themes = paths;
692
+ });
652
693
  }
653
694
  getEnableSkillCommands() {
654
695
  return this.settings.enableSkillCommands ?? true;
@@ -14,6 +14,7 @@ export const BUILTIN_SLASH_COMMANDS = [
14
14
  { name: "fork", description: "Create a new fork from a previous user message" },
15
15
  { name: "clone", description: "Duplicate the current session at the current position" },
16
16
  { name: "tree", description: "Navigate session tree (switch branches)" },
17
+ { name: "trust", description: "Save project trust decision for future sessions" },
17
18
  { name: "login", description: "Configure provider authentication" },
18
19
  { name: "logout", description: "Remove provider authentication" },
19
20
  { name: "new", description: "Start a new session" },
@@ -145,7 +145,7 @@ function rebuildBashResultRenderComponent(component, result, options, showImages
145
145
  }
146
146
  if (state.cachedSkipped && state.cachedSkipped > 0) {
147
147
  const hint = theme.fg("muted", `... (${state.cachedSkipped} earlier lines,`) +
148
- ` ${keyHint("app.tools.expand", "to expand")})`;
148
+ ` ${keyHint("app.tools.expand", "to expand")}${theme.fg("muted", ")")}`;
149
149
  return ["", truncateToWidth(hint, width, "..."), ...(state.cachedLines ?? [])];
150
150
  }
151
151
  return ["", ...(state.cachedLines ?? [])];
@@ -50,7 +50,7 @@ function formatFindResult(result, options, theme, showImages) {
50
50
  const remaining = lines.length - maxLines;
51
51
  text += `\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
52
52
  if (remaining > 0) {
53
- text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")})`;
53
+ text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")}${theme.fg("muted", ")")}`;
54
54
  }
55
55
  }
56
56
  const resultLimit = result.details?.resultLimitReached;
@@ -51,7 +51,7 @@ function formatGrepResult(result, options, theme, showImages) {
51
51
  const remaining = lines.length - maxLines;
52
52
  text += `\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
53
53
  if (remaining > 0) {
54
- text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")})`;
54
+ text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")}${theme.fg("muted", ")")}`;
55
55
  }
56
56
  }
57
57
  const matchLimit = result.details?.matchLimitReached;
@@ -36,7 +36,7 @@ function formatLsResult(result, options, theme, showImages) {
36
36
  const remaining = lines.length - maxLines;
37
37
  text += `\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`;
38
38
  if (remaining > 0) {
39
- text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")})`;
39
+ text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")}${theme.fg("muted", ")")}`;
40
40
  }
41
41
  }
42
42
  const entryLimit = result.details?.entryLimitReached;
@@ -111,7 +111,7 @@ function formatReadResult(args, result, options, theme, showImages, _cwd, isErro
111
111
  const remaining = lines.length - maxLines;
112
112
  let text = `\n${displayLines.map((line) => (lang ? replaceTabs(line) : theme.fg("toolOutput", replaceTabs(line)))).join("\n")}`;
113
113
  if (remaining > 0) {
114
- text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")})`;
114
+ text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("app.tools.expand", "to expand")}${theme.fg("muted", ")")}`;
115
115
  }
116
116
  const truncation = result.details?.truncation;
117
117
  if (truncation?.truncated) {
@@ -110,7 +110,7 @@ function formatWriteCall(args, options, theme, cache, cwd) {
110
110
  const remaining = lines.length - maxLines;
111
111
  text += `\n\n${displayLines.map((line) => (lang ? line : theme.fg("toolOutput", replaceTabs(line)))).join("\n")}`;
112
112
  if (remaining > 0) {
113
- text += `${theme.fg("muted", `\n... (${remaining} more lines, ${totalLines} total,`)} ${keyHint("app.tools.expand", "to expand")})`;
113
+ text += `${theme.fg("muted", `\n... (${remaining} more lines, ${totalLines} total,`)} ${keyHint("app.tools.expand", "to expand")}${theme.fg("muted", ")")}`;
114
114
  }
115
115
  }
116
116
  return text;
@@ -0,0 +1,130 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import lockfile from "proper-lockfile";
4
+ import { CONFIG_DIR_NAME } from "../config.js";
5
+ import { canonicalizePath, resolvePath } from "../utils/paths.js";
6
+ const CONTEXT_FILE_NAMES = ["AGENTS.md", "AGENTS.MD", "CLAUDE.md", "CLAUDE.MD"];
7
+ function normalizeCwd(cwd) {
8
+ return canonicalizePath(resolvePath(cwd));
9
+ }
10
+ function readTrustFile(path) {
11
+ if (!existsSync(path)) {
12
+ return {};
13
+ }
14
+ let parsed;
15
+ try {
16
+ parsed = JSON.parse(readFileSync(path, "utf-8"));
17
+ }
18
+ catch (error) {
19
+ const message = error instanceof Error ? error.message : String(error);
20
+ throw new Error(`Failed to read trust store ${path}: ${message}`);
21
+ }
22
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
23
+ throw new Error(`Invalid trust store ${path}: expected an object`);
24
+ }
25
+ const data = {};
26
+ for (const [key, value] of Object.entries(parsed)) {
27
+ if (value !== true && value !== false && value !== null) {
28
+ throw new Error(`Invalid trust store ${path}: value for ${JSON.stringify(key)} must be true, false, or null`);
29
+ }
30
+ data[key] = value;
31
+ }
32
+ return data;
33
+ }
34
+ function writeTrustFile(path, data) {
35
+ const sorted = {};
36
+ for (const key of Object.keys(data).sort()) {
37
+ const value = data[key];
38
+ if (value === true || value === false || value === null) {
39
+ sorted[key] = value;
40
+ }
41
+ }
42
+ mkdirSync(dirname(path), { recursive: true });
43
+ writeFileSync(path, `${JSON.stringify(sorted, null, 2)}\n`, "utf-8");
44
+ }
45
+ function acquireTrustLockSync(path) {
46
+ const trustDir = dirname(path);
47
+ mkdirSync(trustDir, { recursive: true });
48
+ const maxAttempts = 10;
49
+ const delayMs = 20;
50
+ let lastError;
51
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
52
+ try {
53
+ return lockfile.lockSync(trustDir, { realpath: false, lockfilePath: `${path}.lock` });
54
+ }
55
+ catch (error) {
56
+ const code = typeof error === "object" && error !== null && "code" in error
57
+ ? String(error.code)
58
+ : undefined;
59
+ if (code !== "ELOCKED" || attempt === maxAttempts) {
60
+ throw error;
61
+ }
62
+ lastError = error;
63
+ const start = Date.now();
64
+ while (Date.now() - start < delayMs) {
65
+ // Sleep synchronously to avoid changing trust store callers to async.
66
+ }
67
+ }
68
+ }
69
+ if (lastError instanceof Error) {
70
+ throw lastError;
71
+ }
72
+ throw new Error("Failed to acquire trust store lock");
73
+ }
74
+ function withTrustFileLock(path, fn) {
75
+ const release = acquireTrustLockSync(path);
76
+ try {
77
+ return fn();
78
+ }
79
+ finally {
80
+ release();
81
+ }
82
+ }
83
+ export function hasProjectTrustInputs(cwd) {
84
+ let currentDir = canonicalizePath(resolvePath(cwd));
85
+ if (existsSync(join(currentDir, CONFIG_DIR_NAME))) {
86
+ return true;
87
+ }
88
+ while (true) {
89
+ for (const filename of CONTEXT_FILE_NAMES) {
90
+ if (existsSync(join(currentDir, filename))) {
91
+ return true;
92
+ }
93
+ }
94
+ if (existsSync(join(currentDir, ".agents", "skills"))) {
95
+ return true;
96
+ }
97
+ const parentDir = dirname(currentDir);
98
+ if (parentDir === currentDir) {
99
+ return false;
100
+ }
101
+ currentDir = parentDir;
102
+ }
103
+ }
104
+ export class ProjectTrustStore {
105
+ trustPath;
106
+ constructor(agentDir) {
107
+ this.trustPath = join(resolvePath(agentDir), "trust.json");
108
+ }
109
+ get(cwd) {
110
+ return withTrustFileLock(this.trustPath, () => {
111
+ const data = readTrustFile(this.trustPath);
112
+ const value = data[normalizeCwd(cwd)];
113
+ return value === true || value === false ? value : null;
114
+ });
115
+ }
116
+ set(cwd, decision) {
117
+ withTrustFileLock(this.trustPath, () => {
118
+ const data = readTrustFile(this.trustPath);
119
+ const key = normalizeCwd(cwd);
120
+ if (decision === null) {
121
+ delete data[key];
122
+ }
123
+ else {
124
+ data[key] = decision;
125
+ }
126
+ writeTrustFile(this.trustPath, data);
127
+ });
128
+ }
129
+ }
130
+ //# sourceMappingURL=trust-manager.js.map
@@ -26,6 +26,7 @@ export { formatSkillsForPrompt, loadSkills, loadSkillsFromDir, } from "./core/sk
26
26
  export { createSyntheticSourceInfo } from "./core/source-info.js";
27
27
  // Tools
28
28
  export { createBashToolDefinition, createEditToolDefinition, createFindToolDefinition, createGrepToolDefinition, createLocalBashOperations, createLsToolDefinition, createReadToolDefinition, createWriteToolDefinition, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, truncateHead, truncateLine, truncateTail, withFileMutationQueue, } from "./core/tools/index.js";
29
+ export { hasProjectTrustInputs, ProjectTrustStore } from "./core/trust-manager.js";
29
30
  // Main entry point
30
31
  export { main } from "./main.js";
31
32
  // Run modes for programmatic SDK usage