@runpod/mcp-server 4.7.0 → 4.8.0

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,17 @@
1
1
  # @runpod/mcp-server
2
2
 
3
+ ## 4.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8f53070: Export the server profile module as `@runpod/mcp-server/profile` (`ServerProfile`, `DIRECTORY_PATH`, `DIRECTORY_TOOLS`, `profileForPath`, `directoryIncludes`, `directoryOmittedInputs`) so an HTTP host can resolve the directory profile from the package instead of reaching into `src/`.
8
+ - 4c51d2a: Resync the vendored v2 OpenAPI spec with production as of 2026-10-27 and regenerate the SDK schema and the tools. Adds `list-organization-users` and `get-organization-user`, documents the 413 response on list operations, and requires a non-empty mount `path`.
9
+
10
+ ### Patch Changes
11
+
12
+ - 2658966: Record the tool surface on each `runpod_mcp_tool_call` analytics event as `profile` (`directory` for `mcp.runpod.io/directory`, `default` otherwise), so directory traffic (the ChatGPT plugin and the Claude connector) can be told apart from custom connectors on `/`.
13
+ - f3efa2e: Avoid suggesting immediate retries when a timed-out or failed create may already have completed upstream.
14
+
3
15
  ## 4.7.0
4
16
 
5
17
  ### Minor Changes
package/README.md CHANGED
@@ -226,3 +226,6 @@ the new generated surface. Applications should connect to the server through
226
226
  MCP using the `runpod-mcp` CLI, or use `handleMcpRequest` from
227
227
  `@runpod/mcp-server/http` when embedding the HTTP handler. The HTTP entry point
228
228
  also no longer re-exports the legacy `registerTools` or `ToolContext`.
229
+
230
+ An HTTP host that serves the directory profile can import `DIRECTORY_PATH`
231
+ and `profileForPath` from `@runpod/mcp-server/profile`.
@@ -0,0 +1,135 @@
1
+ // src/specgen/profile.ts
2
+ var DIRECTORY_PATH = "/directory";
3
+ var IN = { include: true };
4
+ var CREDENTIAL_MANAGEMENT = "Manages stored credentials; OpenAI restricts plugins that handle access credentials, so the directory leaves the whole credential family out.";
5
+ var CREDENTIAL_INPUT = "Takes a secret or password as input; directories forbid collecting credentials.";
6
+ var ALP = "Collects conversation-derived data beyond the task; the directory profile never serves it.";
7
+ var DIRECTORY_TOOLS = {
8
+ "cancel-job": IN,
9
+ "create-cluster": IN,
10
+ "create-delegation": IN,
11
+ "create-endpoint": IN,
12
+ "create-global-volume": IN,
13
+ "create-network-volume": IN,
14
+ "create-pod": IN,
15
+ "create-template": IN,
16
+ "delete-cluster": IN,
17
+ "delete-endpoint": IN,
18
+ "delete-global-volume": IN,
19
+ "delete-network-volume": IN,
20
+ "delete-pod": IN,
21
+ "delete-template": IN,
22
+ "deploy-hub-repo": IN,
23
+ "endpoint-health": IN,
24
+ "get-capacity": IN,
25
+ "get-cluster": IN,
26
+ "get-cpu-type": IN,
27
+ "get-data-center": IN,
28
+ "get-endpoint": IN,
29
+ "get-endpoint-build": IN,
30
+ "get-global-volume": IN,
31
+ "get-gpu-type": IN,
32
+ "get-job-status": IN,
33
+ "get-network-volume": IN,
34
+ "get-organization": IN,
35
+ "get-organization-user": IN,
36
+ "get-pod": IN,
37
+ "get-ssh-keys": IN,
38
+ "get-template": IN,
39
+ "list-audit-logs": IN,
40
+ "list-billing": IN,
41
+ "list-cluster-billing": IN,
42
+ "list-cluster-pods": IN,
43
+ "list-clusters": IN,
44
+ "list-cpu-types": IN,
45
+ "list-data-centers": IN,
46
+ "list-delegations": IN,
47
+ "list-endpoint-billing": IN,
48
+ "list-endpoint-builds": IN,
49
+ "list-endpoint-releases": IN,
50
+ "list-endpoint-workers": IN,
51
+ "list-endpoints": IN,
52
+ "list-global-volumes": IN,
53
+ "list-gpu-types": IN,
54
+ "list-hub-repos": IN,
55
+ "list-network-volume-billing": IN,
56
+ "list-network-volumes": IN,
57
+ "list-organization-users": IN,
58
+ "list-pod-billing": IN,
59
+ "list-pods": IN,
60
+ "list-public-endpoints": IN,
61
+ "list-public-templates": IN,
62
+ "list-serverless-billing": IN,
63
+ "list-templates": IN,
64
+ "lookup-concept": IN,
65
+ "purge-endpoint-queue": IN,
66
+ "retry-job": IN,
67
+ "revoke-delegation": IN,
68
+ "search-concepts": IN,
69
+ "search-docs": IN,
70
+ "set-endpoint-gpus": IN,
71
+ "stream-job": IN,
72
+ "stream-pod-logs": IN,
73
+ "stream-worker-logs": IN,
74
+ "update-cluster": IN,
75
+ "update-endpoint": IN,
76
+ "update-global-volume": IN,
77
+ "update-network-volume": IN,
78
+ "update-pod": IN,
79
+ "update-ssh-keys": IN,
80
+ "update-template": IN,
81
+ "run-endpoint": { include: true, omitInputs: ["s3Config"] },
82
+ "runsync-endpoint": { include: true, omitInputs: ["s3Config"] },
83
+ "pod-action": {
84
+ include: false,
85
+ reason: "Takes the transition as an argument; OpenAI asks for one tool per operation, so the directory serves start-pod, stop-pod and restart-pod (terminate is delete-pod)."
86
+ },
87
+ "start-pod": IN,
88
+ "stop-pod": IN,
89
+ "restart-pod": IN,
90
+ "list-guides": {
91
+ include: false,
92
+ reason: "Serves playbooks that instruct the model how to behave."
93
+ },
94
+ "read-guide": {
95
+ include: false,
96
+ reason: "Serves playbooks that instruct the model how to behave."
97
+ },
98
+ "describe-tool": {
99
+ include: false,
100
+ reason: "Fetches schemas the directory already lists in full; directories reject schema fetching."
101
+ },
102
+ "create-secret": { include: false, reason: CREDENTIAL_INPUT },
103
+ "get-secret": { include: false, reason: CREDENTIAL_MANAGEMENT },
104
+ "list-secrets": { include: false, reason: CREDENTIAL_MANAGEMENT },
105
+ "delete-secret": { include: false, reason: CREDENTIAL_MANAGEMENT },
106
+ "get-registry": { include: false, reason: CREDENTIAL_MANAGEMENT },
107
+ "list-registries": { include: false, reason: CREDENTIAL_MANAGEMENT },
108
+ "delete-registry": { include: false, reason: CREDENTIAL_MANAGEMENT },
109
+ "update-secret": { include: false, reason: CREDENTIAL_INPUT },
110
+ "create-registry": { include: false, reason: CREDENTIAL_INPUT },
111
+ report_feedback: { include: false, reason: ALP },
112
+ report_how_user_found_runpod: { include: false, reason: ALP },
113
+ save_to_journal: { include: false, reason: ALP },
114
+ read_journal: { include: false, reason: ALP },
115
+ ask_question: { include: false, reason: ALP }
116
+ };
117
+ function directoryIncludes(name) {
118
+ return DIRECTORY_TOOLS[name]?.include === true;
119
+ }
120
+ function directoryOmittedInputs(name) {
121
+ const decision = DIRECTORY_TOOLS[name];
122
+ return decision?.include ? decision.omitInputs ?? [] : [];
123
+ }
124
+ function profileForPath(pathname) {
125
+ return pathname.replace(/\/+$/, "") === DIRECTORY_PATH ? "directory" : "default";
126
+ }
127
+
128
+ export {
129
+ DIRECTORY_PATH,
130
+ DIRECTORY_TOOLS,
131
+ directoryIncludes,
132
+ directoryOmittedInputs,
133
+ profileForPath
134
+ };
135
+ //# sourceMappingURL=chunk-6IETGSON.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/specgen/profile.ts"],"sourcesContent":["// Which tool surface a connection gets. `default` is the full surface every\n// client has always had. `directory` is what app directories (Anthropic's\n// connector directory, OpenAI's plugin directory) list: their policies forbid\n// tool descriptions that instruct the model, steer it toward other tools or\n// external instructions, or collect conversation data beyond the tool's job.\n// The directory profile is served only at DIRECTORY_PATH; nothing about the\n// default surface changes.\nexport type ServerProfile = 'default' | 'directory';\n\nexport const DIRECTORY_PATH = '/directory';\n\n/** Whether a tool is listed on the directory profile. Every tool the server\n * can serve must have an entry, so a new tool (a spec regeneration, a new\n * curated tool) fails tests/specgen-directory.test.ts until someone decides.\n * The rules for deciding are in specgen/DIRECTORY_POLICY.md.\n * `omitInputs` hides top-level arguments the directory must not ask for (a\n * credential, for example); a call that passes one is refused. */\nexport type DirectoryDecision =\n | { include: true; omitInputs?: readonly string[] }\n | { include: false; reason: string };\n\nconst IN = { include: true } as const;\nconst CREDENTIAL_MANAGEMENT =\n 'Manages stored credentials; OpenAI restricts plugins that handle access credentials, so the directory leaves the whole credential family out.';\nconst CREDENTIAL_INPUT =\n 'Takes a secret or password as input; directories forbid collecting credentials.';\nconst ALP =\n 'Collects conversation-derived data beyond the task; the directory profile never serves it.';\n\nexport const DIRECTORY_TOOLS: Readonly<Record<string, DirectoryDecision>> = {\n 'cancel-job': IN,\n 'create-cluster': IN,\n 'create-delegation': IN,\n 'create-endpoint': IN,\n 'create-global-volume': IN,\n 'create-network-volume': IN,\n 'create-pod': IN,\n 'create-template': IN,\n 'delete-cluster': IN,\n 'delete-endpoint': IN,\n 'delete-global-volume': IN,\n 'delete-network-volume': IN,\n 'delete-pod': IN,\n 'delete-template': IN,\n 'deploy-hub-repo': IN,\n 'endpoint-health': IN,\n 'get-capacity': IN,\n 'get-cluster': IN,\n 'get-cpu-type': IN,\n 'get-data-center': IN,\n 'get-endpoint': IN,\n 'get-endpoint-build': IN,\n 'get-global-volume': IN,\n 'get-gpu-type': IN,\n 'get-job-status': IN,\n 'get-network-volume': IN,\n 'get-organization': IN,\n 'get-organization-user': IN,\n 'get-pod': IN,\n 'get-ssh-keys': IN,\n 'get-template': IN,\n 'list-audit-logs': IN,\n 'list-billing': IN,\n 'list-cluster-billing': IN,\n 'list-cluster-pods': IN,\n 'list-clusters': IN,\n 'list-cpu-types': IN,\n 'list-data-centers': IN,\n 'list-delegations': IN,\n 'list-endpoint-billing': IN,\n 'list-endpoint-builds': IN,\n 'list-endpoint-releases': IN,\n 'list-endpoint-workers': IN,\n 'list-endpoints': IN,\n 'list-global-volumes': IN,\n 'list-gpu-types': IN,\n 'list-hub-repos': IN,\n 'list-network-volume-billing': IN,\n 'list-network-volumes': IN,\n 'list-organization-users': IN,\n 'list-pod-billing': IN,\n 'list-pods': IN,\n 'list-public-endpoints': IN,\n 'list-public-templates': IN,\n 'list-serverless-billing': IN,\n 'list-templates': IN,\n 'lookup-concept': IN,\n 'purge-endpoint-queue': IN,\n 'retry-job': IN,\n 'revoke-delegation': IN,\n 'search-concepts': IN,\n 'search-docs': IN,\n 'set-endpoint-gpus': IN,\n 'stream-job': IN,\n 'stream-pod-logs': IN,\n 'stream-worker-logs': IN,\n 'update-cluster': IN,\n 'update-endpoint': IN,\n 'update-global-volume': IN,\n 'update-network-volume': IN,\n 'update-pod': IN,\n 'update-ssh-keys': IN,\n 'update-template': IN,\n 'run-endpoint': { include: true, omitInputs: ['s3Config'] },\n 'runsync-endpoint': { include: true, omitInputs: ['s3Config'] },\n 'pod-action': {\n include: false,\n reason:\n 'Takes the transition as an argument; OpenAI asks for one tool per operation, so the directory serves start-pod, stop-pod and restart-pod (terminate is delete-pod).',\n },\n 'start-pod': IN,\n 'stop-pod': IN,\n 'restart-pod': IN,\n 'list-guides': {\n include: false,\n reason: 'Serves playbooks that instruct the model how to behave.',\n },\n 'read-guide': {\n include: false,\n reason: 'Serves playbooks that instruct the model how to behave.',\n },\n 'describe-tool': {\n include: false,\n reason:\n 'Fetches schemas the directory already lists in full; directories reject schema fetching.',\n },\n 'create-secret': { include: false, reason: CREDENTIAL_INPUT },\n 'get-secret': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'list-secrets': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'delete-secret': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'get-registry': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'list-registries': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'delete-registry': { include: false, reason: CREDENTIAL_MANAGEMENT },\n 'update-secret': { include: false, reason: CREDENTIAL_INPUT },\n 'create-registry': { include: false, reason: CREDENTIAL_INPUT },\n report_feedback: { include: false, reason: ALP },\n report_how_user_found_runpod: { include: false, reason: ALP },\n save_to_journal: { include: false, reason: ALP },\n read_journal: { include: false, reason: ALP },\n ask_question: { include: false, reason: ALP },\n};\n\n/** Served on the directory profile only with an explicit include. */\nexport function directoryIncludes(name: string): boolean {\n return DIRECTORY_TOOLS[name]?.include === true;\n}\n\nexport function directoryOmittedInputs(name: string): readonly string[] {\n const decision = DIRECTORY_TOOLS[name];\n return decision?.include ? (decision.omitInputs ?? []) : [];\n}\n\nexport function profileForPath(pathname: string): ServerProfile {\n return pathname.replace(/\\/+$/, '') === DIRECTORY_PATH\n ? 'directory'\n : 'default';\n}\n"],"mappings":"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","names":[]}
package/dist/http.cjs CHANGED
@@ -566,9 +566,36 @@ var generatedTools = [
566
566
  },
567
567
  ClusterMounts: {
568
568
  description: "Storage mounts for a cluster create: `persistent` or `network`, not both. Clusters cannot mount global volumes, so `global` is rejected.",
569
+ "x-go-type": "BaseMounts",
569
570
  allOf: [
570
571
  {
571
572
  $ref: "#/$defs/BaseMounts"
573
+ },
574
+ {
575
+ type: "object",
576
+ properties: {
577
+ persistent: {
578
+ type: "object",
579
+ properties: {
580
+ path: {
581
+ type: "string",
582
+ minLength: 1
583
+ }
584
+ }
585
+ },
586
+ network: {
587
+ type: "array",
588
+ items: {
589
+ type: "object",
590
+ properties: {
591
+ path: {
592
+ type: "string",
593
+ minLength: 1
594
+ }
595
+ }
596
+ }
597
+ }
598
+ }
572
599
  }
573
600
  ],
574
601
  unevaluatedProperties: false
@@ -2644,6 +2671,37 @@ var generatedTools = [
2644
2671
  properties: {}
2645
2672
  }
2646
2673
  },
2674
+ {
2675
+ name: "get-organization-user",
2676
+ title: "Get organization user",
2677
+ operationId: "getOrganizationUser",
2678
+ description: "Get an organization member. Returns one member of the authenticated caller's organization by user ID, with the role they hold. Any member can read it. A user who is not a member, including one from another organization, returns 404. Returns 404 when the caller does not belong to an organization, or 403 when organizations are unavailable to the caller.",
2679
+ method: "GET",
2680
+ path: "/v2/organization/users/{id}",
2681
+ params: [
2682
+ {
2683
+ name: "id",
2684
+ location: "path"
2685
+ }
2686
+ ],
2687
+ hasBody: false,
2688
+ annotations: {
2689
+ readOnlyHint: true,
2690
+ destructiveHint: false,
2691
+ idempotentHint: true,
2692
+ openWorldHint: true
2693
+ },
2694
+ inputSchema: {
2695
+ type: "object",
2696
+ properties: {
2697
+ id: {
2698
+ type: "string",
2699
+ description: "User identifier"
2700
+ }
2701
+ },
2702
+ required: ["id"]
2703
+ }
2704
+ },
2647
2705
  {
2648
2706
  name: "get-pod",
2649
2707
  title: "Get Pod",
@@ -3783,6 +3841,26 @@ var generatedTools = [
3783
3841
  }
3784
3842
  }
3785
3843
  },
3844
+ {
3845
+ name: "list-organization-users",
3846
+ title: "List organization users",
3847
+ operationId: "listOrganizationUsers",
3848
+ description: "List organization members. Returns every member of the authenticated caller's organization with the role each holds. Any member can read the roster. The list is complete and takes no pagination parameters. Returns 404 when the caller does not belong to an organization, or 403 when organizations are unavailable to the caller.",
3849
+ method: "GET",
3850
+ path: "/v2/organization/users",
3851
+ params: [],
3852
+ hasBody: false,
3853
+ annotations: {
3854
+ readOnlyHint: true,
3855
+ destructiveHint: false,
3856
+ idempotentHint: true,
3857
+ openWorldHint: true
3858
+ },
3859
+ inputSchema: {
3860
+ type: "object",
3861
+ properties: {}
3862
+ }
3863
+ },
3786
3864
  {
3787
3865
  name: "list-pod-billing",
3788
3866
  title: "List Pod billing",
@@ -5339,6 +5417,7 @@ function networkFailureResult(error) {
5339
5417
  return {
5340
5418
  ok: false,
5341
5419
  status: unreached ? 503 : 502,
5420
+ requestDelivery: unreached ? "not_sent" : "uncertain",
5342
5421
  payload: unreached ? {
5343
5422
  error: "Could not reach the Runpod API.",
5344
5423
  cause: code,
@@ -5365,6 +5444,7 @@ async function runTool(body) {
5365
5444
  return {
5366
5445
  ok: false,
5367
5446
  status: 504,
5447
+ requestDelivery: "uncertain",
5368
5448
  payload: {
5369
5449
  error: "The Runpod API did not respond before the request deadline.",
5370
5450
  hint: "Transient upstream stall: retry the same call once; if it persists, check https://uptime.runpod.io."
@@ -5457,6 +5537,7 @@ var DIRECTORY_TOOLS = {
5457
5537
  "get-job-status": IN,
5458
5538
  "get-network-volume": IN,
5459
5539
  "get-organization": IN,
5540
+ "get-organization-user": IN,
5460
5541
  "get-pod": IN,
5461
5542
  "get-ssh-keys": IN,
5462
5543
  "get-template": IN,
@@ -5478,6 +5559,7 @@ var DIRECTORY_TOOLS = {
5478
5559
  "list-hub-repos": IN,
5479
5560
  "list-network-volume-billing": IN,
5480
5561
  "list-network-volumes": IN,
5562
+ "list-organization-users": IN,
5481
5563
  "list-pod-billing": IN,
5482
5564
  "list-pods": IN,
5483
5565
  "list-public-endpoints": IN,
@@ -9097,7 +9179,7 @@ function words2(name) {
9097
9179
 
9098
9180
  // src/server.ts
9099
9181
  var SERVER_NAME = "Runpod API Server";
9100
- var SERVER_VERSION = true ? "4.7.0" : "dev";
9182
+ var SERVER_VERSION = true ? "4.8.0" : "dev";
9101
9183
 
9102
9184
  // src/specgen/analytics.ts
9103
9185
  var import_node_crypto = require("crypto");
@@ -9166,6 +9248,7 @@ function captureToolCall(apiKey, event) {
9166
9248
  ...event.clientName ? { client_name: event.clientName } : {},
9167
9249
  ...event.guideKind ? { guide_kind: event.guideKind } : {},
9168
9250
  ...event.blockedBy ? { blocked_by: event.blockedBy } : {},
9251
+ ...event.profile ? { profile: event.profile } : {},
9169
9252
  ...args
9170
9253
  });
9171
9254
  }
@@ -9798,6 +9881,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9798
9881
  status: 429,
9799
9882
  durationMs: 0,
9800
9883
  ...verdict.reason ? { blockedBy: verdict.reason } : {},
9884
+ profile,
9801
9885
  ...opts.analytics
9802
9886
  });
9803
9887
  }
@@ -9818,6 +9902,9 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9818
9902
  const curated = servedCuratedTools.find(
9819
9903
  (tool) => tool.name === request.params.name
9820
9904
  );
9905
+ const generated = servedGeneratedTools.find(
9906
+ (tool) => tool.name === request.params.name
9907
+ );
9821
9908
  const omitted = directory ? directoryOmittedInputs(request.params.name).filter((key) => key in args) : [];
9822
9909
  const rejected = omitted.length ? {
9823
9910
  ok: false,
@@ -9830,7 +9917,7 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9830
9917
  args
9831
9918
  ) : null;
9832
9919
  const result = rejected ?? await runTool(
9833
- () => curated ? curated.handler(ctx, args) : dispatchTool(request.params.name, args)
9920
+ () => curated ? curated.handler(ctx, args) : dispatchTool(generated, request.params.name, args)
9834
9921
  );
9835
9922
  logToolCall({
9836
9923
  tool: request.params.name,
@@ -9850,23 +9937,27 @@ function createSpecgenServer(ctx, version = "0.1.0", opts = {}) {
9850
9937
  args: recordedArgumentValues(curated?.recordedArguments, args),
9851
9938
  guideKind: curated?.recordedGuideKind?.(args)
9852
9939
  },
9940
+ profile,
9853
9941
  ...opts.analytics
9854
9942
  });
9855
9943
  }
9856
9944
  opts.onToolResult?.(request.params.name, result.status);
9857
9945
  if (!result.ok && result.status === 401) opts.onUnauthorized?.();
9946
+ const annotations = curated?.annotations ?? generated?.annotations;
9858
9947
  const payload = redactSecretEnv(
9859
- result.ok ? result.payload : withRecoveryHint(result, callShapeOnlyTools.has(request.params.name))
9948
+ result.ok ? result.payload : withRecoveryHint(
9949
+ result,
9950
+ annotations?.idempotentHint === true,
9951
+ request.params.name,
9952
+ callShapeOnlyTools.has(request.params.name)
9953
+ )
9860
9954
  );
9861
9955
  return {
9862
9956
  content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
9863
9957
  isError: !result.ok
9864
9958
  };
9865
9959
  });
9866
- async function dispatchTool(name, args) {
9867
- const tool = servedGeneratedTools.find(
9868
- (candidate) => candidate.name === name
9869
- );
9960
+ async function dispatchTool(tool, name, args) {
9870
9961
  if (!tool)
9871
9962
  return {
9872
9963
  ok: false,
@@ -9914,8 +10005,24 @@ function errorHint(status, detail) {
9914
10005
  return void 0;
9915
10006
  }
9916
10007
  var CALL_SHAPE_ONLY_HINT = `This tool lists its call shape only; ${DESCRIBE_TOOL_NAME} serves the full argument schema.`;
9917
- function withRecoveryHint(result, listsCallShapeOnly = false) {
10008
+ var OUTCOME_UNKNOWN_HINT = "Runpod may have completed this operation even though its response failed. Do not submit it again yet. Check the matching get- or list- tool or the Runpod console using the original request details and time. Lists can lag; if you cannot confirm the outcome, stop and ask the user to verify before retrying.";
10009
+ var JOB_OUTCOME_UNKNOWN_HINT = "Runpod may have submitted this Serverless job even though its response failed. Do not submit it again yet. No job-list tool is available, and get-job-status requires a job ID that this failed response may not have returned. Check the endpoint in the Runpod console or ask the user to verify the submission. If the outcome remains uncertain, stop instead of retrying.";
10010
+ var RETRY_JOB_OUTCOME_UNKNOWN_HINT = "Runpod may have requeued this Serverless job even though its response failed. Do not retry it again yet. Check get-job-status with the endpoint and job IDs you supplied, or check the Runpod console. If you cannot confirm the outcome, stop and ask the user to verify before retrying.";
10011
+ var JOB_SUBMISSION_TOOLS = /* @__PURE__ */ new Set(["run-endpoint", "runsync-endpoint"]);
10012
+ function withRecoveryHint(result, idempotent, toolName, listsCallShapeOnly = false) {
9918
10013
  const payload = asRecord(result.payload) ?? { error: result.payload };
10014
+ if (!idempotent && result.status >= 500 && result.requestDelivery !== "not_sent") {
10015
+ const detail = asRecord(payload.detail);
10016
+ const safeDetail = detail ? { ...detail } : void 0;
10017
+ if (safeDetail) delete safeDetail.hint;
10018
+ return {
10019
+ ...payload,
10020
+ ...safeDetail ? { detail: safeDetail } : {},
10021
+ ...payload.code !== void 0 ? { upstreamCode: payload.code } : {},
10022
+ code: "outcome_unknown",
10023
+ hint: toolName === "retry-job" ? RETRY_JOB_OUTCOME_UNKNOWN_HINT : JOB_SUBMISSION_TOOLS.has(toolName) ? JOB_OUTCOME_UNKNOWN_HINT : OUTCOME_UNKNOWN_HINT
10024
+ };
10025
+ }
9919
10026
  const hint = payload.hint ?? asRecord(payload.detail)?.hint ?? errorHint(result.status, detailText(payload));
9920
10027
  const shapeRejected = result.status === 400 || result.status === 422;
9921
10028
  return {