@inneranimalmedia/agentsam-sdk 2.5.0 → 2.6.1

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 (211) hide show
  1. package/AGENTSAM.md +55 -0
  2. package/README.md +12 -8
  3. package/bin/agentsam +2 -0
  4. package/docs/AGENTSAM_ASTRA_OPENAI_INTEGRATION.md +1363 -0
  5. package/docs/CLI_SHELL.md +163 -53
  6. package/docs/PLATFORM_RUNTIME_EVENTS.md +48 -0
  7. package/docs/RELEASES.md +16 -7
  8. package/docs/SOURCE_ARCHITECTURE.md +58 -0
  9. package/docs/TEST_TIERS.md +26 -0
  10. package/migrations/runtime/0001_cli_runtime.sql +298 -0
  11. package/package.json +45 -12
  12. package/packages/agentsam-repository/README.md +15 -0
  13. package/packages/agentsam-repository/package.json +25 -0
  14. package/packages/agentsam-repository/src/contracts.js +113 -0
  15. package/packages/agentsam-repository/src/index.js +3 -0
  16. package/{src/lib → packages/agentsam-repository/src}/merkle/cloudflare-persistence.js +14 -24
  17. package/{src/lib → packages/agentsam-repository/src}/merkle/index.js +1 -0
  18. package/{src/lib → packages/agentsam-repository/src}/merkle/persistence.js +6 -4
  19. package/{src/lib → packages/agentsam-repository/src}/merkle/policy.js +1 -0
  20. package/packages/agentsam-repository/test/contracts.test.mjs +40 -0
  21. package/packages/agentsam-repository/test/git-context.test.mjs +24 -0
  22. package/{test/merkle.test.mjs → packages/agentsam-repository/test/merkle-core.test.mjs} +2 -32
  23. package/{test → packages/agentsam-repository/test}/merkle-persistence.test.mjs +11 -6
  24. package/packages/connectors/cloudflare/package.json +10 -0
  25. package/packages/connectors/cloudflare/src/index.js +127 -0
  26. package/packages/connectors/cloudflare/src/owner.js +76 -0
  27. package/packages/connectors/cloudflare/src/routes.js +223 -0
  28. package/packages/connectors/cloudflare/src/vault.js +80 -0
  29. package/packages/connectors/cloudflare/tests/connector.test.mjs +44 -0
  30. package/packages/identity/package.json +2 -2
  31. package/packages/identity/src/contracts/auth-config.js +18 -7
  32. package/packages/identity/tests/auth-config.test.mjs +9 -5
  33. package/packages/identity/tests/oauth-credentials.test.mjs +4 -4
  34. package/protocol/COMPANY_REPOSITORY_GRAPH_V1.md +91 -0
  35. package/protocol/MERKLE_PERSISTENCE_V1.md +2 -0
  36. package/protocol/MERKLE_PERSISTENCE_V2.md +40 -0
  37. package/protocol/README.md +1 -0
  38. package/protocol/capabilities/cloudflare-cpu-audit-input.schema.json +19 -0
  39. package/protocol/capabilities/cloudflare-cpu-profile-input.schema.json +13 -0
  40. package/protocol/capabilities/cloudflare-wrangler-native-input.schema.json +19 -0
  41. package/protocol/capabilities/manifest.json +47 -0
  42. package/protocol/context/context-budget.schema.json +10 -15
  43. package/protocol/context/context-item.schema.json +4 -5
  44. package/protocol/context/resolved-context-pack.schema.json +19 -14
  45. package/protocol/models/README.md +373 -0
  46. package/protocol/models/model-inventory-v2.schema.json +212 -0
  47. package/protocol/repository/repository-contract.schema.json +24 -0
  48. package/protocol/repository/repository-dependency.schema.json +24 -0
  49. package/protocol/repository/repository-identity.schema.json +17 -0
  50. package/protocol/rpc/v1/common.proto +16 -0
  51. package/protocol/rpc/v1/errors.proto +35 -0
  52. package/protocol/rpc/v1/knowledge.proto +77 -0
  53. package/services/knowledge/package-lock.json +333 -0
  54. package/services/knowledge/package.json +5 -1
  55. package/skills/agentsam-cloudflare-workers/SKILL.md +53 -0
  56. package/skills/agentsam-cloudflare-workers/references/cpu-profiling.md +16 -0
  57. package/skills/agentsam-cloudflare-workers/references/errors-and-observability.md +29 -0
  58. package/skills/agentsam-cloudflare-workers/references/wrangler-native-map.md +28 -0
  59. package/skills/catalog.json +18 -0
  60. package/src/agent/capability-adapter.js +25 -13
  61. package/src/agent/index.js +1 -0
  62. package/src/agent/responses-runner.js +353 -0
  63. package/src/capabilities/repository-snapshot.js +3 -3
  64. package/src/cli.js +118 -31
  65. package/src/cloudflare/cpu-profile.js +115 -0
  66. package/src/cloudflare/index.js +14 -0
  67. package/src/cloudflare/wrangler.js +132 -0
  68. package/src/commands/account-auth.js +47 -0
  69. package/src/commands/cloudflare.js +58 -0
  70. package/src/commands/connections.js +93 -0
  71. package/src/commands/context-economics.js +129 -0
  72. package/src/commands/context.js +1 -1
  73. package/src/commands/db.js +20 -3
  74. package/src/commands/deploy.js +39 -3
  75. package/src/commands/env.js +90 -0
  76. package/src/commands/eval.js +63 -0
  77. package/src/commands/interactive.js +2 -5
  78. package/src/commands/knowledge.js +12 -4
  79. package/src/commands/merkle-persist.js +30 -11
  80. package/src/commands/merkle.js +1 -1
  81. package/src/commands/models.js +149 -46
  82. package/src/commands/ollama.js +26 -0
  83. package/src/commands/preferences.js +130 -61
  84. package/src/commands/resume.js +67 -0
  85. package/src/commands/security.js +5 -3
  86. package/src/commands/shell.js +568 -119
  87. package/src/commands/tunnel.js +2 -2
  88. package/src/commands/whoami.js +86 -0
  89. package/src/context/budget.js +68 -6
  90. package/src/context/index.js +3 -1
  91. package/src/context/rehydrate.js +35 -0
  92. package/src/context/resolve.js +44 -12
  93. package/src/errors/contract.js +236 -0
  94. package/src/errors/diagnostic.js +160 -0
  95. package/src/errors/index.js +23 -0
  96. package/src/eval/context.js +191 -0
  97. package/src/eval/index.js +1 -0
  98. package/src/index.js +68 -2
  99. package/src/knowledge/service/auth.js +13 -0
  100. package/src/knowledge/service/grpc-client.js +115 -0
  101. package/src/knowledge/service/grpc-codec.js +237 -0
  102. package/src/knowledge/service/grpc-server.js +83 -0
  103. package/src/knowledge/service/job-engine.js +248 -0
  104. package/src/knowledge/service/server.js +87 -135
  105. package/src/knowledge/source.js +1 -1
  106. package/src/lib/account-session.js +98 -0
  107. package/src/lib/agent-instructions.js +73 -0
  108. package/src/lib/auth.js +4 -0
  109. package/src/lib/cli-preferences.js +55 -24
  110. package/src/lib/deploy/git-guard.js +69 -0
  111. package/src/lib/deploy/health.js +57 -0
  112. package/src/lib/deploy/local-studio.js +283 -0
  113. package/src/lib/deploy/secret-scan.js +65 -0
  114. package/src/lib/deploy-receipt/index.js +2 -2
  115. package/src/lib/detect-context.js +2 -2
  116. package/src/lib/execution-approvals.js +59 -0
  117. package/src/lib/knowledge-docker.js +6 -3
  118. package/src/lib/local-sessions.js +148 -0
  119. package/src/lib/local-status.js +1 -1
  120. package/src/lib/project-config.js +1 -1
  121. package/src/lib/provider-credentials.js +183 -0
  122. package/src/lib/scaffold/templates/worker-api/index.js +101 -20
  123. package/src/lib/scaffold/wizards/worker-api.js +27 -11
  124. package/src/lib/slash-commands.js +23 -16
  125. package/src/local/migrations.js +93 -0
  126. package/src/local/runtime-store.js +141 -0
  127. package/src/local/sqlite.js +2 -0
  128. package/src/local-pty/server.js +113 -51
  129. package/src/models/catalog.js +135 -0
  130. package/src/models/discovery.js +292 -0
  131. package/src/models/index.js +7 -0
  132. package/src/providers/anthropic-messages.js +192 -0
  133. package/src/providers/cloudflare-chat.js +183 -0
  134. package/src/providers/factory.js +69 -0
  135. package/src/providers/gemini-generate-content.js +208 -0
  136. package/src/providers/index.js +10 -0
  137. package/src/providers/ollama-chat.js +148 -0
  138. package/src/providers/openai-responses.js +426 -0
  139. package/src/repository/index.js +14 -2
  140. package/src/rpc/generated/common_grpc_pb.js +1 -0
  141. package/src/rpc/generated/common_pb.js +536 -0
  142. package/src/rpc/generated/errors_grpc_pb.js +1 -0
  143. package/src/rpc/generated/errors_pb.js +482 -0
  144. package/src/rpc/generated/knowledge_grpc_pb.js +135 -0
  145. package/src/rpc/generated/knowledge_pb.js +2168 -0
  146. package/src/rpc/generated/package.json +3 -0
  147. package/src/security/process.js +35 -9
  148. package/src/security/trust-boundary.js +2 -2
  149. package/src/telemetry/contracts.js +203 -0
  150. package/src/telemetry/events.js +51 -0
  151. package/src/telemetry/index.js +8 -0
  152. package/src/tools/hydrate.js +35 -0
  153. package/src/tools/index.js +1 -0
  154. package/src/ui/boot.js +15 -17
  155. package/src/ui/cli/activity.js +76 -0
  156. package/src/ui/cli/compaction.js +15 -0
  157. package/src/ui/cli/footer.js +39 -0
  158. package/src/ui/cli/help.js +192 -0
  159. package/src/ui/cli/plan.js +20 -0
  160. package/src/ui/cli/runtime-events.js +110 -0
  161. package/src/ui/cli/waiting.js +16 -0
  162. package/src/ui/merkle/render.js +1 -1
  163. package/test/account-session.test.mjs +36 -0
  164. package/test/cli/preferences-runtime.test.mjs +11 -0
  165. package/test/cli/runtime-ui.test.mjs +74 -0
  166. package/test/cli-preferences.test.mjs +26 -5
  167. package/test/cloudflare-connector.test.mjs +96 -0
  168. package/test/cloudflare-runtime.test.mjs +75 -0
  169. package/test/context.test.mjs +61 -12
  170. package/test/deploy-health-scan.test.mjs +67 -0
  171. package/test/error-diagnostics.test.mjs +115 -0
  172. package/test/eval-context.test.mjs +37 -0
  173. package/test/execution-approvals.test.mjs +27 -0
  174. package/test/fixtures/knowledge-rpc-worker.mjs +16 -0
  175. package/test/integration/cli-help.test.mjs +37 -0
  176. package/test/integration/knowledge-rpc.test.mjs +112 -0
  177. package/test/integration/merkle-cli.test.mjs +61 -0
  178. package/test/integration/merkle-persistence-identity.test.mjs +48 -0
  179. package/test/integration/provider-env-cli.test.mjs +49 -0
  180. package/test/integration/provider-factory.test.mjs +197 -0
  181. package/test/integration/repository-company-graph.test.mjs +90 -0
  182. package/test/integration/runtime-migrations.test.mjs +82 -0
  183. package/test/knowledge-service.test.mjs +5 -0
  184. package/test/knowledge.test.mjs +16 -0
  185. package/test/live/terminal-transport.live.test.mjs +24 -0
  186. package/test/local-sessions.test.mjs +48 -0
  187. package/test/local-studio-deploy.test.mjs +83 -0
  188. package/test/model-catalog.test.mjs +43 -0
  189. package/test/models.test.mjs +127 -16
  190. package/test/npm10-lock.test.mjs +29 -0
  191. package/test/ollama.test.mjs +21 -0
  192. package/test/openai-responses.test.mjs +95 -0
  193. package/test/portable-context.test.mjs +1 -1
  194. package/test/provider-credentials.test.mjs +96 -0
  195. package/test/rehydrate.test.mjs +25 -0
  196. package/test/release-hygiene.test.mjs +13 -5
  197. package/test/responses-runner.test.mjs +150 -0
  198. package/test/shell.test.mjs +92 -23
  199. package/test/smoke.mjs +4 -1
  200. package/test/telemetry.test.mjs +79 -0
  201. package/test/terminal/local-pty.mock.test.mjs +151 -0
  202. package/test/tools-search.test.mjs +14 -1
  203. package/test/whoami-resume.test.mjs +56 -0
  204. /package/{src/lib → packages/agentsam-repository/src}/git-context.js +0 -0
  205. /package/{src/lib → packages/agentsam-repository/src}/merkle/diff.js +0 -0
  206. /package/{src/lib → packages/agentsam-repository/src}/merkle/filemeta.js +0 -0
  207. /package/{src/lib → packages/agentsam-repository/src}/merkle/git-ignore.js +0 -0
  208. /package/{src/lib → packages/agentsam-repository/src}/merkle/hash.js +0 -0
  209. /package/{src/lib → packages/agentsam-repository/src}/merkle/semantic.js +0 -0
  210. /package/{src/lib → packages/agentsam-repository/src}/merkle/snapshot.js +0 -0
  211. /package/{src/lib → packages/agentsam-repository/src}/merkle/tree.js +0 -0
@@ -7,7 +7,7 @@
7
7
  import { spawn, spawnSync } from 'node:child_process';
8
8
  import { authenticateViaBrowser } from '../lib/auth.js';
9
9
  import { postJson } from '../lib/core-client.js';
10
- import { resolveSdkKey } from '../../packages/identity/src/contracts/auth-config.js';
10
+ import { resolveAccountSdkKey } from '../lib/account-session.js';
11
11
 
12
12
  const DEFAULT_PORT = 3099;
13
13
 
@@ -51,7 +51,7 @@ function ensureCloudflared() {
51
51
  }
52
52
 
53
53
  async function resolveToken() {
54
- const existing = resolveSdkKey(process.env);
54
+ const existing = resolveAccountSdkKey({ env: process.env }).value;
55
55
  if (existing.startsWith('sdk_')) return existing;
56
56
  const session = await authenticateViaBrowser();
57
57
  const tok = String(session?.access_token || '').trim();
@@ -0,0 +1,86 @@
1
+ import { getJson } from '../lib/core-client.js';
2
+ import { resolveAccountSdkKey } from '../lib/account-session.js';
3
+ import { listProviderCredentialStatus } from '../lib/provider-credentials.js';
4
+
5
+ function writeLine(write, value = '') { write(`${value}\n`); }
6
+
7
+ export async function collectWhoami(options = {}) {
8
+ const env = options.env || process.env;
9
+ const sdk = resolveAccountSdkKey({ env, explicit: options.token || '', home: options.home });
10
+ const token = sdk.value;
11
+ const credentials = listProviderCredentialStatus({ env, home: options.home });
12
+ if (!token) return {
13
+ schema_version: 1,
14
+ authenticated: false,
15
+ authority: 'iam',
16
+ identity: null,
17
+ sdk_credential: { configured: false, source: null },
18
+ provider_credentials: credentials,
19
+ };
20
+ try {
21
+ const loader = options.contextLoader || ((sdkKey) => getJson('/api/sdk/context', sdkKey));
22
+ const context = await loader(token);
23
+ return {
24
+ schema_version: 1,
25
+ authenticated: true,
26
+ authority: 'iam',
27
+ identity: {
28
+ user_id: context?.user_id || null,
29
+ account_id: context?.account_id || null,
30
+ email: context?.email || context?.user?.email || null,
31
+ },
32
+ sdk_credential: { configured: true, source: sdk.source, valid: true },
33
+ provider_credentials: credentials,
34
+ cloudflare_connected: context?.cloudflare?.ok === true,
35
+ byok: context?.byok && typeof context.byok === 'object'
36
+ ? Object.fromEntries(Object.entries(context.byok).map(([key, value]) => [key, { configured: value?.configured === true }]))
37
+ : {},
38
+ };
39
+ } catch (error) {
40
+ return {
41
+ schema_version: 1,
42
+ authenticated: false,
43
+ authority: 'iam',
44
+ identity: null,
45
+ sdk_credential: { configured: true, source: sdk.source, valid: false, error: error?.message || String(error) },
46
+ provider_credentials: credentials,
47
+ };
48
+ }
49
+ }
50
+
51
+ export function renderWhoami(status) {
52
+ const lines = ['', ' Agent Sam · whoami', ''];
53
+ if (status.authenticated) {
54
+ lines.push(' authenticated yes');
55
+ lines.push(` authority ${status.authority}`);
56
+ if (status.identity?.email) lines.push(` email ${status.identity.email}`);
57
+ if (status.identity?.account_id) lines.push(` account ${status.identity.account_id}`);
58
+ if (status.identity?.user_id) lines.push(` user ${status.identity.user_id}`);
59
+ } else {
60
+ lines.push(' authenticated no');
61
+ lines.push(` SDK key ${status.sdk_credential?.configured ? 'present but not validated' : 'not configured'}`);
62
+ if (status.sdk_credential?.error) lines.push(` error ${status.sdk_credential.error}`);
63
+ }
64
+ lines.push('');
65
+ lines.push(' Provider credentials');
66
+ for (const row of status.provider_credentials || []) {
67
+ const state = row.configured ? 'available' : row.error ? `blocked (${row.error})` : 'not configured';
68
+ const source = row.source ? ` · ${row.source}` : '';
69
+ lines.push(` ${String(row.provider).padEnd(12)} ${state}${source}`);
70
+ }
71
+ lines.push('');
72
+ lines.push(' Secret values are never printed by whoami.');
73
+ lines.push('');
74
+ return lines.join('\n');
75
+ }
76
+
77
+ export async function runWhoami(argv = [], options = {}) {
78
+ const allowed = new Set(['--json']);
79
+ const unknown = argv.filter((arg) => !allowed.has(arg));
80
+ if (unknown.length) throw new Error(`unknown whoami option: ${unknown[0]}`);
81
+ const status = await collectWhoami(options);
82
+ const write = options.write || ((text) => process.stdout.write(text));
83
+ if (argv.includes('--json')) writeLine(write, JSON.stringify(status, null, 2));
84
+ else write(renderWhoami(status));
85
+ return status;
86
+ }
@@ -19,18 +19,51 @@ function positiveInteger(value, label) {
19
19
  return value;
20
20
  }
21
21
 
22
+ function nullablePositiveInteger(value, label) {
23
+ if (value == null) return null;
24
+ return positiveInteger(value, label);
25
+ }
26
+
22
27
  export function createContextBudget(options = {}) {
23
28
  const windowTokens = positiveInteger(options.windowTokens, 'windowTokens');
24
29
  const charsPerToken = positiveNumber(options.charsPerToken ?? DEFAULT_CONTEXT_RATIOS.charsPerToken, 'charsPerToken');
25
- const targetInputRatio = ratio(options.targetInputRatio ?? DEFAULT_CONTEXT_RATIOS.targetInputRatio, 'targetInputRatio');
26
- const hardInputRatio = ratio(options.hardInputRatio ?? DEFAULT_CONTEXT_RATIOS.hardInputRatio, 'hardInputRatio');
30
+ const targetInputRatio = ratio(
31
+ options.targetInputRatio ?? (options.targetInputTokens ? options.targetInputTokens / windowTokens : DEFAULT_CONTEXT_RATIOS.targetInputRatio),
32
+ 'targetInputRatio',
33
+ );
34
+ const hardInputRatio = ratio(
35
+ options.hardInputRatio ?? options.hardWindowRatio ?? (options.hardInputTokens ? options.hardInputTokens / windowTokens : DEFAULT_CONTEXT_RATIOS.hardInputRatio),
36
+ 'hardInputRatio',
37
+ );
27
38
  if (targetInputRatio >= hardInputRatio) throw new RangeError('targetInputRatio must be lower than hardInputRatio');
28
39
 
29
40
  const windowChars = Math.floor(windowTokens * charsPerToken);
30
- const targetInputTokens = Math.floor(windowTokens * targetInputRatio);
31
- const hardInputTokens = Math.floor(windowTokens * hardInputRatio);
32
- const targetInputChars = Math.floor(windowChars * targetInputRatio);
33
- const hardInputChars = Math.floor(windowChars * hardInputRatio);
41
+ const targetInputTokens = positiveInteger(options.targetInputTokens ?? Math.floor(windowTokens * targetInputRatio), 'targetInputTokens');
42
+ const hardInputTokens = positiveInteger(options.hardInputTokens ?? Math.floor(windowTokens * hardInputRatio), 'hardInputTokens');
43
+ if (targetInputTokens >= hardInputTokens) throw new RangeError('targetInputTokens must be lower than hardInputTokens');
44
+
45
+ const maxNormalInputTokens = positiveInteger(options.maxNormalInputTokens ?? hardInputTokens, 'maxNormalInputTokens');
46
+ if (maxNormalInputTokens > hardInputTokens) throw new RangeError('maxNormalInputTokens must not exceed hardInputTokens');
47
+ const compactAtTokens = positiveInteger(
48
+ options.compactAtTokens ?? Math.floor(targetInputTokens + ((maxNormalInputTokens - targetInputTokens) * 0.4)),
49
+ 'compactAtTokens',
50
+ );
51
+ const interveneAtTokens = positiveInteger(
52
+ options.interveneAtTokens ?? Math.floor(targetInputTokens + ((maxNormalInputTokens - targetInputTokens) * 0.72)),
53
+ 'interveneAtTokens',
54
+ );
55
+ if (!(targetInputTokens <= compactAtTokens && compactAtTokens <= interveneAtTokens && interveneAtTokens <= maxNormalInputTokens)) {
56
+ throw new RangeError('context policy must satisfy target <= compactAt <= interveneAt <= maxNormal');
57
+ }
58
+
59
+ const pricingThresholdTokens = nullablePositiveInteger(options.pricingThresholdTokens, 'pricingThresholdTokens');
60
+ const safetyMarginTokens = Number.isInteger(options.safetyMarginTokens) && options.safetyMarginTokens >= 0
61
+ ? options.safetyMarginTokens
62
+ : pricingThresholdTokens && pricingThresholdTokens > maxNormalInputTokens
63
+ ? pricingThresholdTokens - maxNormalInputTokens
64
+ : 0;
65
+ const targetInputChars = Math.floor(targetInputTokens * charsPerToken);
66
+ const hardInputChars = Math.floor(hardInputTokens * charsPerToken);
34
67
 
35
68
  return Object.freeze({
36
69
  windowTokens,
@@ -38,8 +71,14 @@ export function createContextBudget(options = {}) {
38
71
  charsPerToken,
39
72
  targetInputRatio,
40
73
  hardInputRatio,
74
+ hardWindowRatio: hardInputRatio,
41
75
  targetInputTokens,
76
+ compactAtTokens,
77
+ interveneAtTokens,
78
+ maxNormalInputTokens,
42
79
  hardInputTokens,
80
+ pricingThresholdTokens,
81
+ safetyMarginTokens,
43
82
  targetInputChars,
44
83
  hardInputChars,
45
84
  maxSystemChars: positiveInteger(options.maxSystemChars ?? Math.max(4_000, Math.min(60_000, Math.floor(windowChars * 0.05))), 'maxSystemChars'),
@@ -56,3 +95,26 @@ export function estimateContextTokens(chars, charsPerToken = DEFAULT_CONTEXT_RAT
56
95
  if (!Number.isFinite(charsPerToken) || charsPerToken <= 0) throw new RangeError('charsPerToken must be positive');
57
96
  return Math.ceil(chars / charsPerToken);
58
97
  }
98
+
99
+ export function assessContextUsage(tokens, budget) {
100
+ const inputTokens = Number(tokens);
101
+ if (!Number.isFinite(inputTokens) || inputTokens < 0) throw new RangeError('tokens must be a non-negative number');
102
+ if (!budget?.windowTokens) throw new TypeError('context budget is required');
103
+ let stage = 'normal';
104
+ if (inputTokens >= budget.hardInputTokens) stage = 'hard';
105
+ else if (inputTokens >= budget.maxNormalInputTokens) stage = 'max-normal';
106
+ else if (inputTokens >= budget.interveneAtTokens) stage = 'intervene';
107
+ else if (inputTokens >= budget.compactAtTokens) stage = 'compact';
108
+ else if (inputTokens >= budget.targetInputTokens) stage = 'target';
109
+ const pricingThresholdTokens = budget.pricingThresholdTokens ?? null;
110
+ return Object.freeze({
111
+ inputTokens: Math.ceil(inputTokens),
112
+ stage,
113
+ utilizationRatio: inputTokens / budget.windowTokens,
114
+ pricingThresholdTokens,
115
+ tokensUntilPricingThreshold: pricingThresholdTokens == null ? null : pricingThresholdTokens - Math.ceil(inputTokens),
116
+ pricingThresholdCrossed: pricingThresholdTokens == null ? false : inputTokens > pricingThresholdTokens,
117
+ shouldCompact: inputTokens >= budget.compactAtTokens,
118
+ shouldIntervene: inputTokens >= budget.interveneAtTokens,
119
+ });
120
+ }
@@ -1,5 +1,7 @@
1
- export { DEFAULT_CONTEXT_RATIOS, createContextBudget, estimateContextTokens } from './budget.js';
1
+ export { DEFAULT_CONTEXT_RATIOS, createContextBudget, estimateContextTokens, assessContextUsage } from './budget.js';
2
2
  export { DEFAULT_RESULT_POLICY, RESULT_DETAIL_ORDER, normalizeResultPolicy, truncateResultText, boundResultItems } from './result-policy.js';
3
3
  export { CONTEXT_ITEM_KINDS, normalizeContextItem, resolveContext, resolveProjectContext } from './resolve.js';
4
4
  export { PROJECT_RULES_FILENAME, DEFAULT_PROJECT_RULES_MAX_CHARS, defaultProjectRules, ensureProjectRules, findProjectRules, loadProjectRules } from '../lib/project-rules.js';
5
+ export { AGENT_RUNTIME_FILENAME, AGENT_INSTRUCTION_PRECEDENCE, findAgentRuntimeContract, compileAgentInstructions } from '../lib/agent-instructions.js';
5
6
  export { DEFAULT_CONSUMED_CONTEXT_CHARS, compactContextItem, compactConsumedToolResult } from './compact.js';
7
+ export { rehydrateContextRef } from './rehydrate.js';
@@ -0,0 +1,35 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { normalizeContextItem } from './resolve.js';
3
+
4
+ function sha256(value) { return `sha256:${createHash('sha256').update(String(value)).digest('hex')}`; }
5
+
6
+ export async function rehydrateContextRef(ref, adapter, options = {}) {
7
+ const sourceRef = String(ref || '').trim();
8
+ if (!sourceRef) throw new TypeError('rehydrate ref is required');
9
+ const read = typeof adapter === 'function' ? adapter : adapter?.read?.bind(adapter);
10
+ if (typeof read !== 'function') throw new TypeError('rehydration adapter must provide read(ref, options)');
11
+ const maxChars = Number(options.maxChars ?? 64_000);
12
+ if (!Number.isInteger(maxChars) || maxChars < 1) throw new RangeError('rehydration maxChars must be a positive integer');
13
+
14
+ const raw = await read(sourceRef, { maxChars, signal: options.signal });
15
+ if (raw == null) throw new Error(`rehydration_source_not_found:${sourceRef}`);
16
+ const value = typeof raw === 'string' ? { content: raw } : raw;
17
+ const content = String(value.content ?? '');
18
+ const computedHash = sha256(content);
19
+ const expectedHash = String(options.expectedHash || value.hash || '').trim();
20
+ if (expectedHash && expectedHash.startsWith('sha256:') && expectedHash !== computedHash) {
21
+ throw new Error(`rehydration_hash_mismatch:${sourceRef}`);
22
+ }
23
+ const bounded = content.length > maxChars ? `${content.slice(0, Math.max(0, maxChars - 1))}…` : content;
24
+ const item = normalizeContextItem({
25
+ ref: sourceRef,
26
+ kind: options.kind || value.kind || 'artifact',
27
+ hash: expectedHash || computedHash,
28
+ priority: Number.isFinite(options.priority) ? options.priority : Number(value.priority || 0),
29
+ content: bounded,
30
+ chars: bounded.length,
31
+ truncated: bounded.length < content.length,
32
+ source_chars: content.length > bounded.length ? content.length : undefined,
33
+ });
34
+ return Object.freeze({ ...item, truncated: bounded.length < content.length });
35
+ }
@@ -1,9 +1,10 @@
1
- import { createContextBudget, estimateContextTokens } from './budget.js';
2
- import { loadProjectRules } from '../lib/project-rules.js';
1
+ import { assessContextUsage, createContextBudget, estimateContextTokens } from './budget.js';
2
+ import { compileAgentInstructions } from '../lib/agent-instructions.js';
3
3
 
4
4
  export const CONTEXT_ITEM_KINDS = Object.freeze(['file', 'symbol', 'memory', 'tool_result', 'repo', 'artifact']);
5
5
 
6
6
  function clean(value) { return value == null ? '' : String(value).trim(); }
7
+ function nonNegativeInteger(value) { return Number.isInteger(value) && value >= 0 ? value : 0; }
7
8
 
8
9
  export function normalizeContextItem(value = {}) {
9
10
  const ref = clean(value.ref);
@@ -13,7 +14,17 @@ export function normalizeContextItem(value = {}) {
13
14
  const content = value.content == null ? '' : String(value.content);
14
15
  const chars = Number.isInteger(value.chars) && value.chars >= 0 ? value.chars : content.length;
15
16
  const priority = Number.isFinite(value.priority) ? Number(value.priority) : 0;
16
- return Object.freeze({ ref, kind, chars, hash: clean(value.hash) || undefined, priority, content });
17
+ return Object.freeze({
18
+ ref,
19
+ kind,
20
+ chars,
21
+ hash: clean(value.hash) || undefined,
22
+ priority,
23
+ content,
24
+ ...(value.truncated === true ? { truncated: true } : {}),
25
+ ...(value.compacted === true ? { compacted: true } : {}),
26
+ ...(Number.isInteger(value.source_chars) && value.source_chars >= chars ? { source_chars: value.source_chars } : {}),
27
+ });
17
28
  }
18
29
 
19
30
  function capForKind(item, budget) {
@@ -35,29 +46,38 @@ export function resolveContext(options = {}) {
35
46
  const candidates = (options.items || []).map((item, index) => ({ item: normalizeContextItem(item), index }));
36
47
  candidates.sort((a, b) => (b.item.priority - a.item.priority) || (a.index - b.index));
37
48
 
38
- let evidenceChars = 0;
49
+ let selectedChars = 0;
39
50
  let fileChars = 0;
40
51
  const included = [];
41
52
  const deferred = [];
42
53
 
43
54
  for (const row of candidates) {
44
55
  const item = row.item;
45
- const remainingEvidence = budget.maxEvidenceChars - evidenceChars;
56
+ const remainingEvidence = budget.maxEvidenceChars - selectedChars;
46
57
  if (remainingEvidence <= 0) { deferred.push(item.ref); continue; }
47
58
  const kindCap = capForKind(item, budget);
48
59
  const remainingFile = item.kind === 'file' ? budget.maxFileCharsPerTurn - fileChars : Number.POSITIVE_INFINITY;
49
60
  const allowed = Math.floor(Math.min(kindCap, remainingEvidence, remainingFile));
50
61
  if (allowed <= 0) { deferred.push(item.ref); continue; }
51
62
  const clipped = truncate(item.content, allowed);
52
- const next = Object.freeze({ ...item, content: clipped.content, chars: clipped.content.length, truncated: clipped.truncated || item.chars > clipped.content.length });
63
+ const next = Object.freeze({ ...item, content: clipped.content, chars: clipped.content.length, truncated: clipped.truncated || item.truncated === true || item.chars > clipped.content.length });
53
64
  included.push(next);
54
- evidenceChars += next.chars;
65
+ selectedChars += next.chars;
55
66
  if (item.kind === 'file') fileChars += next.chars;
56
67
  if (clipped.truncated) deferred.push(`${item.ref}#remainder`);
57
68
  }
58
69
 
59
- const systemChars = options.rules?.content ? String(options.rules.content).length : 0;
60
- const totalChars = systemChars + evidenceChars;
70
+ const ruleChars = options.rules?.content ? String(options.rules.content).length : 0;
71
+ const instructionChars = ruleChars + nonNegativeInteger(options.instructionChars);
72
+ const toolSchemaChars = nonNegativeInteger(options.toolSchemaChars);
73
+ const historyChars = nonNegativeInteger(options.historyChars);
74
+ const toolResultChars = included.filter((item) => item.kind === 'tool_result').reduce((sum, item) => sum + item.chars, 0);
75
+ const evidenceChars = selectedChars - toolResultChars;
76
+ const totalChars = instructionChars + toolSchemaChars + historyChars + evidenceChars + toolResultChars;
77
+ const estimatedInputTokens = estimateContextTokens(totalChars, budget.charsPerToken);
78
+ const pressure = assessContextUsage(estimatedInputTokens, budget);
79
+ const rehydratable = [...new Set(included.filter((item) => item.ref).map((item) => item.ref))];
80
+
61
81
  return Object.freeze({
62
82
  objective,
63
83
  refs: Object.freeze(refs),
@@ -66,19 +86,31 @@ export function resolveContext(options = {}) {
66
86
  budget,
67
87
  receipt: Object.freeze({
68
88
  chars: totalChars,
89
+ system_chars: instructionChars,
90
+ instruction_chars: instructionChars,
91
+ tool_schema_chars: toolSchemaChars,
92
+ history_chars: historyChars,
69
93
  evidence_chars: evidenceChars,
70
- system_chars: systemChars,
71
- estimated_tokens: estimateContextTokens(totalChars, budget.charsPerToken),
94
+ tool_result_chars: toolResultChars,
95
+ estimated_tokens: estimatedInputTokens,
96
+ estimated_input_tokens: estimatedInputTokens,
97
+ window_tokens: budget.windowTokens,
98
+ utilization_ratio: pressure.utilizationRatio,
99
+ pricing_threshold_tokens: pressure.pricingThresholdTokens,
100
+ tokens_until_pricing_threshold: pressure.tokensUntilPricingThreshold,
101
+ pressure: pressure.stage,
102
+ estimate_kind: 'local',
72
103
  sources_considered: candidates.length,
73
104
  sources_included: included.length,
74
105
  sources_deferred: deferred.length,
75
106
  deferred_refs: Object.freeze(deferred),
107
+ rehydratable_refs: Object.freeze(rehydratable),
76
108
  }),
77
109
  });
78
110
  }
79
111
 
80
112
  export function resolveProjectContext(options = {}) {
81
113
  const budget = options.budget?.windowTokens ? options.budget : createContextBudget(options.budget || {});
82
- const rules = loadProjectRules(options.cwd || process.cwd(), { maxChars: Math.min(24_000, budget.maxSystemChars) });
114
+ const rules = compileAgentInstructions(options.cwd || process.cwd(), { maxChars: Math.min(24_000, budget.maxSystemChars) });
83
115
  return resolveContext({ ...options, budget, rules });
84
116
  }
@@ -0,0 +1,236 @@
1
+ export const ERROR_SCHEMA_VERSION = 1;
2
+
3
+ // Mirrors google.rpc.Code / gRPC canonical status names without importing a Node gRPC runtime.
4
+ export const ERROR_CODE = Object.freeze({
5
+ OK: 'OK',
6
+ CANCELLED: 'CANCELLED',
7
+ UNKNOWN: 'UNKNOWN',
8
+ INVALID_ARGUMENT: 'INVALID_ARGUMENT',
9
+ DEADLINE_EXCEEDED: 'DEADLINE_EXCEEDED',
10
+ NOT_FOUND: 'NOT_FOUND',
11
+ ALREADY_EXISTS: 'ALREADY_EXISTS',
12
+ PERMISSION_DENIED: 'PERMISSION_DENIED',
13
+ RESOURCE_EXHAUSTED: 'RESOURCE_EXHAUSTED',
14
+ FAILED_PRECONDITION: 'FAILED_PRECONDITION',
15
+ ABORTED: 'ABORTED',
16
+ OUT_OF_RANGE: 'OUT_OF_RANGE',
17
+ UNIMPLEMENTED: 'UNIMPLEMENTED',
18
+ INTERNAL: 'INTERNAL',
19
+ UNAVAILABLE: 'UNAVAILABLE',
20
+ DATA_LOSS: 'DATA_LOSS',
21
+ UNAUTHENTICATED: 'UNAUTHENTICATED',
22
+ });
23
+
24
+ export const GRPC_STATUS = Object.freeze({
25
+ OK: 0,
26
+ CANCELLED: 1,
27
+ UNKNOWN: 2,
28
+ INVALID_ARGUMENT: 3,
29
+ DEADLINE_EXCEEDED: 4,
30
+ NOT_FOUND: 5,
31
+ ALREADY_EXISTS: 6,
32
+ PERMISSION_DENIED: 7,
33
+ RESOURCE_EXHAUSTED: 8,
34
+ FAILED_PRECONDITION: 9,
35
+ ABORTED: 10,
36
+ OUT_OF_RANGE: 11,
37
+ UNIMPLEMENTED: 12,
38
+ INTERNAL: 13,
39
+ UNAVAILABLE: 14,
40
+ DATA_LOSS: 15,
41
+ UNAUTHENTICATED: 16,
42
+ });
43
+
44
+ // Stable AgentSam-specific reasons. Add reasons deliberately; callers should not branch on messages.
45
+ export const ERROR_REASON = Object.freeze({
46
+ UNKNOWN: 'unknown',
47
+
48
+ INPUT_INVALID: 'input_invalid',
49
+ INPUT_OUT_OF_RANGE: 'input_out_of_range',
50
+ PRECONDITION_FAILED: 'precondition_failed',
51
+ UNSUPPORTED_OPERATION: 'unsupported_operation',
52
+
53
+ AUTH_MISSING: 'auth_missing',
54
+ AUTH_INVALID: 'auth_invalid',
55
+ IDENTITY_MISSING: 'identity_missing',
56
+ PERMISSION_DENIED: 'permission_denied',
57
+
58
+ TARGET_NOT_FOUND: 'target_not_found',
59
+ ALREADY_EXISTS: 'already_exists',
60
+ CONFLICT: 'conflict',
61
+ STALE_VERSION: 'stale_version',
62
+
63
+ RATE_LIMITED: 'rate_limited',
64
+ QUOTA_EXHAUSTED: 'quota_exhausted',
65
+ CAPACITY_EXHAUSTED: 'capacity_exhausted',
66
+
67
+ TRANSPORT_UNREACHABLE: 'transport_unreachable',
68
+ TRANSPORT_REFUSED: 'transport_refused',
69
+ TRANSPORT_TIMEOUT: 'transport_timeout',
70
+ UPSTREAM_INVALID_RESPONSE: 'upstream_invalid_response',
71
+ DNS_UNRESOLVED: 'dns_unresolved',
72
+ TLS_HANDSHAKE_FAILED: 'tls_handshake_failed',
73
+ TLS_CERTIFICATE_INVALID: 'tls_certificate_invalid',
74
+ TUNNEL_CONNECTOR_UNAVAILABLE: 'tunnel_connector_unavailable',
75
+
76
+ CANCELLED: 'cancelled',
77
+ DEADLINE_EXCEEDED: 'deadline_exceeded',
78
+ EXECUTION_TIMEOUT: 'execution_timeout',
79
+ EXECUTION_FAILED: 'execution_failed',
80
+
81
+ PROVIDER_UNAVAILABLE: 'provider_unavailable',
82
+ MODEL_NOT_FOUND: 'model_not_found',
83
+ POLICY_BLOCKED: 'policy_blocked',
84
+ COPYRIGHT_BLOCKED: 'copyright_blocked',
85
+
86
+ PERSISTENCE_FAILED: 'persistence_failed',
87
+ DATA_CORRUPT: 'data_corrupt',
88
+ DATA_LOSS: 'data_loss',
89
+
90
+ REPOSITORY_SCOPE_VIOLATION: 'repository_scope_violation',
91
+ REPOSITORY_CHANGED: 'repository_changed',
92
+ INDEX_STALE: 'index_stale',
93
+ INDEX_FAILED: 'index_failed',
94
+ EMBEDDING_FAILED: 'embedding_failed',
95
+ VECTOR_STORE_FAILED: 'vector_store_failed',
96
+
97
+ CAD_CONSTRAINT_UNSATISFIED: 'cad_constraint_unsatisfied',
98
+ CAD_GEOMETRY_INVALID: 'cad_geometry_invalid',
99
+ CAD_KERNEL_FAILED: 'cad_kernel_failed',
100
+ CAD_EXPORT_FAILED: 'cad_export_failed',
101
+ CAD_RENDER_FAILED: 'cad_render_failed',
102
+
103
+ INTERNAL: 'internal',
104
+ });
105
+
106
+ const DEFAULT_HTTP_STATUS = Object.freeze({
107
+ [ERROR_CODE.CANCELLED]: 499,
108
+ [ERROR_CODE.UNKNOWN]: 500,
109
+ [ERROR_CODE.INVALID_ARGUMENT]: 400,
110
+ [ERROR_CODE.DEADLINE_EXCEEDED]: 504,
111
+ [ERROR_CODE.NOT_FOUND]: 404,
112
+ [ERROR_CODE.ALREADY_EXISTS]: 409,
113
+ [ERROR_CODE.PERMISSION_DENIED]: 403,
114
+ [ERROR_CODE.RESOURCE_EXHAUSTED]: 429,
115
+ [ERROR_CODE.FAILED_PRECONDITION]: 400,
116
+ [ERROR_CODE.ABORTED]: 409,
117
+ [ERROR_CODE.OUT_OF_RANGE]: 400,
118
+ [ERROR_CODE.UNIMPLEMENTED]: 501,
119
+ [ERROR_CODE.INTERNAL]: 500,
120
+ [ERROR_CODE.UNAVAILABLE]: 503,
121
+ [ERROR_CODE.DATA_LOSS]: 500,
122
+ [ERROR_CODE.UNAUTHENTICATED]: 401,
123
+ });
124
+
125
+ const HTTP_CODE_OVERRIDES = Object.freeze({
126
+ 400: ERROR_CODE.INVALID_ARGUMENT,
127
+ 401: ERROR_CODE.UNAUTHENTICATED,
128
+ 403: ERROR_CODE.PERMISSION_DENIED,
129
+ 404: ERROR_CODE.NOT_FOUND,
130
+ 408: ERROR_CODE.DEADLINE_EXCEEDED,
131
+ 409: ERROR_CODE.ABORTED,
132
+ 412: ERROR_CODE.FAILED_PRECONDITION,
133
+ 413: ERROR_CODE.RESOURCE_EXHAUSTED,
134
+ 415: ERROR_CODE.INVALID_ARGUMENT,
135
+ 422: ERROR_CODE.INVALID_ARGUMENT,
136
+ 429: ERROR_CODE.RESOURCE_EXHAUSTED,
137
+ 499: ERROR_CODE.CANCELLED,
138
+ 500: ERROR_CODE.INTERNAL,
139
+ 501: ERROR_CODE.UNIMPLEMENTED,
140
+ 502: ERROR_CODE.UNAVAILABLE,
141
+ 503: ERROR_CODE.UNAVAILABLE,
142
+ 504: ERROR_CODE.DEADLINE_EXCEEDED,
143
+ 520: ERROR_CODE.UNAVAILABLE,
144
+ 521: ERROR_CODE.UNAVAILABLE,
145
+ 522: ERROR_CODE.UNAVAILABLE,
146
+ 523: ERROR_CODE.UNAVAILABLE,
147
+ 524: ERROR_CODE.DEADLINE_EXCEEDED,
148
+ 525: ERROR_CODE.UNAVAILABLE,
149
+ 526: ERROR_CODE.UNAVAILABLE,
150
+ 530: ERROR_CODE.UNAVAILABLE,
151
+ });
152
+
153
+ const GRPC_CODE_BY_NUMBER = new Map(Object.entries(GRPC_STATUS).map(([name, value]) => [value, ERROR_CODE[name]]));
154
+
155
+ export function canonicalCodeFromHttpStatus(status) {
156
+ return HTTP_CODE_OVERRIDES[Number(status)] || (Number(status) >= 500 ? ERROR_CODE.INTERNAL : ERROR_CODE.UNKNOWN);
157
+ }
158
+
159
+ export function canonicalCodeFromGrpcStatus(status) {
160
+ return GRPC_CODE_BY_NUMBER.get(Number(status)) || ERROR_CODE.UNKNOWN;
161
+ }
162
+
163
+ export function defaultHttpStatusForCode(code) {
164
+ return DEFAULT_HTTP_STATUS[code] || 500;
165
+ }
166
+
167
+ export function grpcStatusForCode(code) {
168
+ return GRPC_STATUS[code] ?? GRPC_STATUS.UNKNOWN;
169
+ }
170
+
171
+ export function classifyCloudflareFailure({ httpStatus, cloudflareCode } = {}) {
172
+ const status = Number(httpStatus || 0) || null;
173
+ const cf = Number(cloudflareCode || 0) || null;
174
+ if (cf === 1033) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.TUNNEL_CONNECTOR_UNAVAILABLE, retryable: true };
175
+ if (status === 521) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.TRANSPORT_REFUSED, retryable: true };
176
+ if (status === 522 || status === 523) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.TRANSPORT_UNREACHABLE, retryable: true };
177
+ if (status === 524) return { code: ERROR_CODE.DEADLINE_EXCEEDED, reason: ERROR_REASON.TRANSPORT_TIMEOUT, retryable: true };
178
+ if (status === 525) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.TLS_HANDSHAKE_FAILED, retryable: false };
179
+ if (status === 526) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.TLS_CERTIFICATE_INVALID, retryable: false };
180
+ if (status === 520 || status === 502) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.UPSTREAM_INVALID_RESPONSE, retryable: true };
181
+ if (status === 530) return { code: ERROR_CODE.UNAVAILABLE, reason: ERROR_REASON.TRANSPORT_UNREACHABLE, retryable: true };
182
+ return { code: canonicalCodeFromHttpStatus(status), reason: ERROR_REASON.UNKNOWN, retryable: status != null && status >= 500 };
183
+ }
184
+
185
+ export function classifyOAuthFailure(error) {
186
+ switch (String(error || '').toLowerCase()) {
187
+ case 'invalid_client': return { code: ERROR_CODE.UNAUTHENTICATED, reason: ERROR_REASON.AUTH_INVALID, retryable: false };
188
+ case 'invalid_grant': return { code: ERROR_CODE.UNAUTHENTICATED, reason: ERROR_REASON.AUTH_INVALID, retryable: false };
189
+ case 'unauthorized_client':
190
+ case 'access_denied': return { code: ERROR_CODE.PERMISSION_DENIED, reason: ERROR_REASON.PERMISSION_DENIED, retryable: false };
191
+ case 'invalid_scope':
192
+ case 'invalid_request':
193
+ case 'unsupported_grant_type': return { code: ERROR_CODE.INVALID_ARGUMENT, reason: ERROR_REASON.INPUT_INVALID, retryable: false };
194
+ default: return { code: ERROR_CODE.UNKNOWN, reason: ERROR_REASON.UNKNOWN, retryable: false };
195
+ }
196
+ }
197
+
198
+ export function createErrorEnvelope({
199
+ code = ERROR_CODE.UNKNOWN,
200
+ reason = ERROR_REASON.UNKNOWN,
201
+ message = 'Operation failed',
202
+ retryable = false,
203
+ retry_after_ms = null,
204
+ http_status = null,
205
+ grpc_status = null,
206
+ provider = null,
207
+ provider_code = null,
208
+ transport = null,
209
+ request_id = null,
210
+ trace_id = null,
211
+ resource = null,
212
+ details = null,
213
+ } = {}) {
214
+ if (!Object.values(ERROR_CODE).includes(code)) throw new TypeError(`Unknown AgentSam error code: ${code}`);
215
+ if (!Object.values(ERROR_REASON).includes(reason)) throw new TypeError(`Unknown AgentSam error reason: ${reason}`);
216
+ const effectiveHttp = http_status == null ? defaultHttpStatusForCode(code) : Number(http_status);
217
+ const effectiveGrpc = grpc_status == null ? grpcStatusForCode(code) : Number(grpc_status);
218
+ return Object.freeze({
219
+ ok: false,
220
+ schema_version: ERROR_SCHEMA_VERSION,
221
+ code,
222
+ reason,
223
+ message: String(message || 'Operation failed'),
224
+ retryable: Boolean(retryable),
225
+ retry_after_ms: retry_after_ms == null ? null : Math.max(0, Number(retry_after_ms)),
226
+ http_status: Number.isInteger(effectiveHttp) ? effectiveHttp : null,
227
+ grpc_status: Number.isInteger(effectiveGrpc) ? effectiveGrpc : null,
228
+ provider: provider || null,
229
+ provider_code: provider_code || null,
230
+ transport: transport || null,
231
+ request_id: request_id || null,
232
+ trace_id: trace_id || null,
233
+ resource: resource || null,
234
+ details: details || null,
235
+ });
236
+ }