@raindrop-ai/opencode-plugin 0.2.0 → 0.3.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.
package/dist/index.d.cts CHANGED
@@ -1,9 +1,17 @@
1
- import { I as IdentifyInput, A as AppGitOptions } from './index.d-vgBcEZbX.cjs';
1
+ import { I as IdentifyInput, A as AppGitOptions, S as SelfDiagnosticsOptions, P as PromptToolsOverride } from './self-diagnostics-CLW6h4zB.cjs';
2
+ export { a as SelfDiagnosticsSignalDefinition, b as SelfDiagnosticsSignalDefinitions } from './self-diagnostics-CLW6h4zB.cjs';
2
3
  import { Hooks, PluginInput } from '@opencode-ai/plugin';
3
4
 
4
5
  interface RaindropOpencodeOptions {
5
6
  /** Application Git identity override. false disables enrichment. */
6
7
  appGit?: AppGitOptions | false;
8
+ /** Built-in self-diagnostics: injects a `__raindrop_report` tool when enabled. */
9
+ selfDiagnostics?: SelfDiagnosticsOptions;
10
+ /**
11
+ * Overrides the tool catalog recorded as `ai.prompt.tools` on every model
12
+ * span; skips the OpenCode `client.tool.list` lookup entirely.
13
+ */
14
+ tools?: PromptToolsOverride;
7
15
  }
8
16
  /**
9
17
  * The Raindrop control handle exposed alongside the OpenCode hooks.
@@ -25,6 +33,8 @@ interface RaindropOpencodeHandle {
25
33
  */
26
34
  identify(users: IdentifyInput | IdentifyInput[]): Promise<void>;
27
35
  };
36
+ /** Raindrop eventId of the turn currently open for this OpenCode session, if any. */
37
+ currentEventId(sessionID: string): string | undefined;
28
38
  /** Drain the buffered event + trace shippers. */
29
39
  flush(): Promise<void>;
30
40
  /** Flush and tear down the shippers (also wired into session teardown). */
@@ -53,4 +63,4 @@ type RaindropOpencodeHooks = Hooks & {
53
63
  */
54
64
  declare function plugin(input: PluginInput, opts?: RaindropOpencodeOptions): Promise<RaindropOpencodeHooks>;
55
65
 
56
- export { IdentifyInput, type RaindropOpencodeHandle, type RaindropOpencodeHooks, type RaindropOpencodeOptions, plugin as default };
66
+ export { IdentifyInput, type RaindropOpencodeHandle, type RaindropOpencodeHooks, type RaindropOpencodeOptions, SelfDiagnosticsOptions, plugin as default };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,17 @@
1
- import { I as IdentifyInput, A as AppGitOptions } from './index.d-vgBcEZbX.js';
1
+ import { I as IdentifyInput, A as AppGitOptions, S as SelfDiagnosticsOptions, P as PromptToolsOverride } from './self-diagnostics-CLW6h4zB.js';
2
+ export { a as SelfDiagnosticsSignalDefinition, b as SelfDiagnosticsSignalDefinitions } from './self-diagnostics-CLW6h4zB.js';
2
3
  import { Hooks, PluginInput } from '@opencode-ai/plugin';
3
4
 
4
5
  interface RaindropOpencodeOptions {
5
6
  /** Application Git identity override. false disables enrichment. */
6
7
  appGit?: AppGitOptions | false;
8
+ /** Built-in self-diagnostics: injects a `__raindrop_report` tool when enabled. */
9
+ selfDiagnostics?: SelfDiagnosticsOptions;
10
+ /**
11
+ * Overrides the tool catalog recorded as `ai.prompt.tools` on every model
12
+ * span; skips the OpenCode `client.tool.list` lookup entirely.
13
+ */
14
+ tools?: PromptToolsOverride;
7
15
  }
8
16
  /**
9
17
  * The Raindrop control handle exposed alongside the OpenCode hooks.
@@ -25,6 +33,8 @@ interface RaindropOpencodeHandle {
25
33
  */
26
34
  identify(users: IdentifyInput | IdentifyInput[]): Promise<void>;
27
35
  };
36
+ /** Raindrop eventId of the turn currently open for this OpenCode session, if any. */
37
+ currentEventId(sessionID: string): string | undefined;
28
38
  /** Drain the buffered event + trace shippers. */
29
39
  flush(): Promise<void>;
30
40
  /** Flush and tear down the shippers (also wired into session teardown). */
@@ -53,4 +63,4 @@ type RaindropOpencodeHooks = Hooks & {
53
63
  */
54
64
  declare function plugin(input: PluginInput, opts?: RaindropOpencodeOptions): Promise<RaindropOpencodeHooks>;
55
65
 
56
- export { IdentifyInput, type RaindropOpencodeHandle, type RaindropOpencodeHooks, type RaindropOpencodeOptions, plugin as default };
66
+ export { IdentifyInput, type RaindropOpencodeHandle, type RaindropOpencodeHooks, type RaindropOpencodeOptions, SelfDiagnosticsOptions, plugin as default };
package/dist/index.js CHANGED
@@ -2,17 +2,25 @@ import {
2
2
  EventShipper,
3
3
  PLUGIN_NAME,
4
4
  PLUGIN_VERSION,
5
+ SELF_DIAGNOSTICS_ACK,
5
6
  TraceShipper,
6
7
  attrString,
7
8
  boundedStringify,
8
9
  buildLlmGenAiAttributes,
9
10
  capText,
10
11
  createAppGitContext,
12
+ createToolCatalog,
11
13
  generateId,
12
14
  loadConfig,
15
+ normalizeSelfDiagnosticsConfig,
13
16
  nowUnixNanoString,
14
- resolveLocalDebuggerBaseUrl
15
- } from "./chunk-4DAI6CYK.js";
17
+ promptToolsAttributes,
18
+ resolveLocalDebuggerBaseUrl,
19
+ trackSelfDiagnosticsSignal
20
+ } from "./chunk-ZG47FVK7.js";
21
+
22
+ // src/index.ts
23
+ import { tool } from "@opencode-ai/plugin";
16
24
 
17
25
  // src/tracing.ts
18
26
  import { hostname as osHostname } from "os";
@@ -129,7 +137,7 @@ function markTaskCallFinished(sessionID, callID) {
129
137
  running.delete(callID);
130
138
  if (running.size === 0) runningTaskCallsBySession.delete(sessionID);
131
139
  }
132
- function endPendingToolSpanWithError(state, callID, tool, toolCallArgs, errorMessage, traceShipper) {
140
+ function endPendingToolSpanWithError(state, callID, tool2, toolCallArgs, errorMessage, traceShipper) {
133
141
  const startInfo = state.toolSpanStarts.get(callID);
134
142
  if (!startInfo) return false;
135
143
  state.toolSpanStarts.delete(callID);
@@ -151,7 +159,7 @@ function endPendingToolSpanWithError(state, callID, tool, toolCallArgs, errorMes
151
159
  startTimeUnixNano: startInfo.startTimeUnixNano,
152
160
  attributes: [
153
161
  attrString("ai.operationId", "ai.toolCall"),
154
- attrString("ai.toolCall.name", tool),
162
+ attrString("ai.toolCall.name", tool2),
155
163
  attrString("ai.toolCall.id", callID)
156
164
  ]
157
165
  });
@@ -188,7 +196,7 @@ function getHostname() {
188
196
  return h;
189
197
  }
190
198
  }
191
- function createHooks(config, worktree, directory, eventShipper, traceShipper) {
199
+ function createTracing(config, worktree, directory, eventShipper, traceShipper, toolCatalog) {
192
200
  function log(msg, data) {
193
201
  if (!config.debug) return;
194
202
  const prefix = `[raindrop-ai/opencode-plugin] [info] ${msg}`;
@@ -214,7 +222,15 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
214
222
  callKey,
215
223
  log
216
224
  });
217
- return {
225
+ const hooks = {
226
+ // Fires before each model call with the model already resolved: start the
227
+ // catalog lookup now so the span attributes rarely wait on it later.
228
+ "chat.params": async (input) => {
229
+ void (toolCatalog == null ? void 0 : toolCatalog.resolve(input.sessionID, {
230
+ provider: input.model.providerID,
231
+ model: input.model.id
232
+ }));
233
+ },
218
234
  // ------------------------------------------------------------------
219
235
  // event — session lifecycle + streaming parts
220
236
  // ------------------------------------------------------------------
@@ -259,14 +275,14 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
259
275
  if (partObj["type"] === "text" && typeof partObj["text"] === "string") {
260
276
  state.outputParts.set(messageId, capText(partObj["text"]));
261
277
  } else if (partObj["type"] === "tool" && messageId) {
262
- const tool = partObj["tool"];
278
+ const tool2 = partObj["tool"];
263
279
  const callID = partObj["callID"];
264
280
  const partState = partObj["state"];
265
281
  if (callID == null || callID === "")
266
282
  throw new Error("message.part.updated: part.callID is required for tool part");
267
283
  if (partState == null || typeof partState !== "object")
268
284
  throw new Error("message.part.updated: part.state is required for tool part");
269
- if (tool === "task") {
285
+ if (tool2 === "task") {
270
286
  const metadata = partState["metadata"];
271
287
  const childSessionId = metadata != null && typeof metadata === "object" ? metadata["sessionId"] : void 0;
272
288
  if (childSessionId != null && childSessionId !== "") {
@@ -291,11 +307,11 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
291
307
  if (endPendingToolSpanWithError(
292
308
  state,
293
309
  callID,
294
- tool,
310
+ tool2,
295
311
  toolCallArgs,
296
312
  errorMessage,
297
313
  traceShipper
298
- ) && tool === "task") {
314
+ ) && tool2 === "task") {
299
315
  markTaskCallFinished(partSessionID, callID);
300
316
  }
301
317
  }
@@ -339,6 +355,9 @@ function createHooks(config, worktree, directory, eventShipper, traceShipper) {
339
355
  "message.updated: providerID and modelID are required non-empty strings"
340
356
  );
341
357
  const modelName = `${providerID}/${modelID}`;
358
+ const promptTools = promptToolsAttributes(
359
+ await (toolCatalog == null ? void 0 : toolCatalog.resolve(String(msgSessionID), { provider: providerID, model: modelID }))
360
+ );
342
361
  const finishReason = info2["finish"];
343
362
  const outputText = state.outputParts.get(messageId);
344
363
  const reasoningText = state.reasoningParts.get(messageId);
@@ -364,7 +383,8 @@ type: ${name != null ? name : "UnknownError"}`;
364
383
  const llmAttrs = [
365
384
  attrString("ai.operationId", "generateText"),
366
385
  attrString("ai.response.text", responseText),
367
- ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
386
+ ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj }),
387
+ ...promptTools
368
388
  ];
369
389
  if (state.currentSystemPrompt) {
370
390
  llmAttrs.push(
@@ -400,7 +420,8 @@ type: ${name != null ? name : "UnknownError"}`;
400
420
  buildPromptMessages(state.currentSystemPrompt, state.currentInput)
401
421
  ),
402
422
  attrString("ai.response.text", turnOutputText),
403
- ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
423
+ ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj }),
424
+ ...promptTools
404
425
  ];
405
426
  if (state.currentSystemPrompt) {
406
427
  llmAttrs.push(
@@ -445,7 +466,8 @@ type: ${name != null ? name : "UnknownError"}`;
445
466
  buildPromptMessages(state.currentSystemPrompt, state.currentInput)
446
467
  ),
447
468
  attrString("ai.response.text", turnOutputText),
448
- ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj })
469
+ ...buildLlmGenAiAttributes({ providerID, modelID, tokens: tokensObj }),
470
+ ...promptTools
449
471
  ];
450
472
  if (isCompaction) {
451
473
  rootAttrs.push(attrString("is_compaction", "true"));
@@ -511,6 +533,7 @@ type: ${name != null ? name : "UnknownError"}`;
511
533
  if (state.parentId) {
512
534
  cleanupSessionParentMap(String(sessionID));
513
535
  sessions.delete(String(sessionID));
536
+ toolCatalog == null ? void 0 : toolCatalog.forget(String(sessionID));
514
537
  } else {
515
538
  }
516
539
  } else if (event.type === "session.deleted") {
@@ -527,6 +550,7 @@ type: ${name != null ? name : "UnknownError"}`;
527
550
  cleanupSessionParentMap(String(sessionID));
528
551
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
529
552
  sessions.delete(String(sessionID));
553
+ toolCatalog == null ? void 0 : toolCatalog.forget(String(sessionID));
530
554
  } else if (event.type === "session.error") {
531
555
  const errorSessionID = (_h = props["sessionID"]) != null ? _h : sessionID;
532
556
  if (errorSessionID === void 0 || errorSessionID === "")
@@ -552,6 +576,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
552
576
  cleanupSessionParentMap(String(errorSessionID));
553
577
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
554
578
  sessions.delete(String(errorSessionID));
579
+ toolCatalog == null ? void 0 : toolCatalog.forget(String(errorSessionID));
555
580
  }
556
581
  } catch (err) {
557
582
  rateLimitedErrorLog(
@@ -672,20 +697,20 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
672
697
  "tool.execute.before": async (toolInput, _output) => {
673
698
  var _a, _b, _c;
674
699
  try {
675
- const { tool, sessionID, callID } = toolInput;
700
+ const { tool: tool2, sessionID, callID } = toolInput;
676
701
  const state = sessions.get(sessionID);
677
702
  if (!state || !state.currentEventId) return;
678
703
  const spanParent = state.parentId && state.parentTaskSpanIds ? state.parentTaskSpanIds : (_a = state.currentRootSpan) == null ? void 0 : _a.ids;
679
704
  if (!spanParent) return;
680
705
  const startTimeUnixNano = nowUnixNanoString();
681
- if (tool === "task") {
706
+ if (tool2 === "task") {
682
707
  const liveTaskSpan = traceShipper.startSpan({
683
708
  name: "ai.toolCall",
684
709
  parent: spanParent,
685
710
  eventId: state.currentEventId,
686
711
  attributes: [
687
712
  attrString("ai.operationId", "ai.toolCall"),
688
- attrString("ai.toolCall.name", tool),
713
+ attrString("ai.toolCall.name", tool2),
689
714
  attrString("ai.toolCall.id", callID)
690
715
  ]
691
716
  });
@@ -710,7 +735,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
710
735
  startTimeUnixNano,
711
736
  parent: spanParent,
712
737
  eventId: state.currentEventId,
713
- name: tool,
738
+ name: tool2,
714
739
  liveSpan: liveTaskSpan
715
740
  });
716
741
  } else {
@@ -718,7 +743,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
718
743
  startTimeUnixNano,
719
744
  parent: spanParent,
720
745
  eventId: state.currentEventId,
721
- name: tool
746
+ name: tool2
722
747
  });
723
748
  }
724
749
  } catch (err) {
@@ -733,7 +758,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
733
758
  // ------------------------------------------------------------------
734
759
  "tool.execute.after": async (toolInput, result) => {
735
760
  try {
736
- const { tool, sessionID, callID, args } = toolInput;
761
+ const { tool: tool2, sessionID, callID, args } = toolInput;
737
762
  const state = sessions.get(sessionID);
738
763
  if (!state || !state.currentEventId) return;
739
764
  if (args === void 0 || args === null)
@@ -743,7 +768,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
743
768
  state.toolSpanStarts.delete(callID);
744
769
  const resultMetadata = result.metadata;
745
770
  const childSessionId = resultMetadata == null ? void 0 : resultMetadata["sessionId"];
746
- if (tool === "task" && childSessionId) {
771
+ if (tool2 === "task" && childSessionId) {
747
772
  mapChildSessionToParent.set(childSessionId, { parentId: sessionID });
748
773
  attachChildSessionToParentTask(childSessionId, sessionID, callID, "tool.execute.after");
749
774
  }
@@ -768,7 +793,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
768
793
  endTimeUnixNano,
769
794
  attributes: [
770
795
  attrString("ai.operationId", "ai.toolCall"),
771
- attrString("ai.toolCall.name", tool),
796
+ attrString("ai.toolCall.name", tool2),
772
797
  attrString("ai.toolCall.id", callID),
773
798
  attrString("ai.toolCall.args", toolCallArgs),
774
799
  attrString("ai.toolCall.result", toolResult)
@@ -776,7 +801,7 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
776
801
  });
777
802
  }
778
803
  }
779
- if (tool === "task") {
804
+ if (tool2 === "task") {
780
805
  markTaskCallFinished(sessionID, callID);
781
806
  }
782
807
  } catch (err) {
@@ -829,16 +854,24 @@ type: ${errorName != null ? errorName : "UnknownError"}`;
829
854
  }
830
855
  }
831
856
  };
857
+ return {
858
+ hooks,
859
+ currentEventId(sessionID) {
860
+ var _a;
861
+ return (_a = sessions.get(sessionID)) == null ? void 0 : _a.currentEventId;
862
+ }
863
+ };
832
864
  }
833
865
 
834
866
  // src/index.ts
835
- function createRaindropHandle(eventShipper, traceShipper, appGitContext) {
867
+ function createRaindropHandle(eventShipper, traceShipper, appGitContext, currentEventId) {
836
868
  return {
837
869
  users: {
838
870
  async identify(users) {
839
871
  await eventShipper.identify(users);
840
872
  }
841
873
  },
874
+ currentEventId,
842
875
  async flush() {
843
876
  await Promise.all([eventShipper.flush(), traceShipper.flush()]);
844
877
  },
@@ -855,7 +888,7 @@ function createRaindropHandle(eventShipper, traceShipper, appGitContext) {
855
888
  };
856
889
  }
857
890
  async function plugin(input, opts = {}) {
858
- var _a, _b, _c;
891
+ var _a, _b, _c, _d, _e;
859
892
  const config = loadConfig(input.directory);
860
893
  function appLog(level, message) {
861
894
  console.log(`[raindrop-ai/opencode-plugin] [${level}] ${message}`);
@@ -899,14 +932,61 @@ async function plugin(input, opts = {}) {
899
932
  appGitContext
900
933
  });
901
934
  const worktree = (_c = input.worktree) != null ? _c : input.directory;
902
- const hooks = createHooks(
935
+ const toolCatalog = createToolCatalog({
936
+ override: (_d = opts.tools) != null ? _d : config.tools,
937
+ fetch: (query) => input.client.tool.list({ query }),
938
+ debug: config.debug
939
+ });
940
+ const tracing = createTracing(
903
941
  config,
904
942
  worktree,
905
943
  input.directory,
906
944
  eventShipper,
907
- traceShipper
945
+ traceShipper,
946
+ toolCatalog
947
+ );
948
+ const hooks = tracing.hooks;
949
+ const selfDiagnostics = normalizeSelfDiagnosticsConfig(
950
+ (_e = opts.selfDiagnostics) != null ? _e : config.selfDiagnostics
908
951
  );
909
- return { ...hooks, raindrop: createRaindropHandle(eventShipper, traceShipper, appGitContext) };
952
+ if (selfDiagnostics && !config.writeKey.trim()) {
953
+ appLog(
954
+ "warn",
955
+ `selfDiagnostics enabled but RAINDROP_WRITE_KEY is not set \u2014 the ${selfDiagnostics.toolName} tool is not registered (signals require the cloud destination).`
956
+ );
957
+ }
958
+ const [firstSignalKey, ...restSignalKeys] = config.writeKey.trim() && selfDiagnostics ? selfDiagnostics.signalKeys : [];
959
+ if (selfDiagnostics && firstSignalKey !== void 0) {
960
+ const cfg = selfDiagnostics;
961
+ hooks.tool = {
962
+ [cfg.toolName]: tool({
963
+ description: cfg.toolDescription,
964
+ args: {
965
+ category: tool.schema.enum([firstSignalKey, ...restSignalKeys]),
966
+ detail: tool.schema.string()
967
+ },
968
+ async execute(args, ctx) {
969
+ await trackSelfDiagnosticsSignal(
970
+ args,
971
+ cfg,
972
+ eventShipper,
973
+ tracing.currentEventId(ctx.sessionID),
974
+ config.debug
975
+ );
976
+ return SELF_DIAGNOSTICS_ACK;
977
+ }
978
+ })
979
+ };
980
+ }
981
+ return {
982
+ ...hooks,
983
+ raindrop: createRaindropHandle(
984
+ eventShipper,
985
+ traceShipper,
986
+ appGitContext,
987
+ tracing.currentEventId
988
+ )
989
+ };
910
990
  }
911
991
  export {
912
992
  plugin as default
@@ -85,5 +85,64 @@ declare global {
85
85
  var RAINDROP_CONTEXT_MANAGER: (new () => ContextManager) | undefined;
86
86
  var RAINDROP_ASYNC_LOCAL_STORAGE: (new <T>() => AsyncLocalStorageLike<T>) | undefined;
87
87
  }
88
+ type JsonSchema = Record<string, unknown>;
89
+ type FunctionToolDeclaration = {
90
+ type: "function";
91
+ name: string;
92
+ description?: string;
93
+ inputSchema?: JsonSchema;
94
+ };
95
+ type ProviderDefinedToolDeclaration = {
96
+ type: "provider-defined";
97
+ id?: string;
98
+ name: string;
99
+ args?: Record<string, unknown>;
100
+ };
101
+ type ToolDeclaration = FunctionToolDeclaration | ProviderDefinedToolDeclaration;
102
+ /**
103
+ * What callers hand to a `tools` override. The canonical `ToolDeclaration`
104
+ * shape is accepted as-is; so is any framework shape `normalizeToolDeclarations`
105
+ * recognizes (OpenAI, Anthropic, Bedrock, LangChain, Pi, Mastra, AI SDK).
106
+ */
107
+ type ToolDeclarationInput = ToolDeclaration | {
108
+ name: string;
109
+ description?: string;
110
+ inputSchema?: JsonSchema;
111
+ parameters?: JsonSchema;
112
+ };
113
+ type PromptToolsOverride = ReadonlyArray<ToolDeclarationInput | unknown>;
114
+
115
+ type SelfDiagnosticsSignalDefinition = {
116
+ /**
117
+ * Human-readable meaning of this signal.
118
+ * Used in the injected tool schema and stored in signal properties.
119
+ */
120
+ description: string;
121
+ /**
122
+ * Optional default sentiment for this signal key.
123
+ * This can help dashboards classify negative/positive programmatic signals.
124
+ */
125
+ sentiment?: "POSITIVE" | "NEGATIVE";
126
+ };
127
+ type SelfDiagnosticsSignalDefinitions = Record<string, SelfDiagnosticsSignalDefinition>;
128
+ type SelfDiagnosticsOptions = {
129
+ /** Enable automatic injection of the self diagnostics tool. Default: false */
130
+ enabled?: boolean;
131
+ /**
132
+ * Signal keys and descriptions exposed to the model.
133
+ * Defaults to a built-in set when omitted.
134
+ */
135
+ signals?: SelfDiagnosticsSignalDefinitions;
136
+ /**
137
+ * Optional extra guidance for when the model should emit self diagnostics.
138
+ * The SDK still generates a full tool prompt from `signals`.
139
+ */
140
+ guidance?: string;
141
+ /**
142
+ * Optional tool name override for the injected self diagnostics tool.
143
+ * Defaults to "__raindrop_report".
144
+ */
145
+ toolName?: string;
146
+ };
88
147
 
89
- export type { AppGitOptions as A, IdentifyInput as I, SignalInput as S };
148
+ export type { AppGitOptions as A, IdentifyInput as I, PromptToolsOverride as P, SelfDiagnosticsOptions as S, SelfDiagnosticsSignalDefinition as a, SelfDiagnosticsSignalDefinitions as b, SignalInput as c };
@@ -85,5 +85,64 @@ declare global {
85
85
  var RAINDROP_CONTEXT_MANAGER: (new () => ContextManager) | undefined;
86
86
  var RAINDROP_ASYNC_LOCAL_STORAGE: (new <T>() => AsyncLocalStorageLike<T>) | undefined;
87
87
  }
88
+ type JsonSchema = Record<string, unknown>;
89
+ type FunctionToolDeclaration = {
90
+ type: "function";
91
+ name: string;
92
+ description?: string;
93
+ inputSchema?: JsonSchema;
94
+ };
95
+ type ProviderDefinedToolDeclaration = {
96
+ type: "provider-defined";
97
+ id?: string;
98
+ name: string;
99
+ args?: Record<string, unknown>;
100
+ };
101
+ type ToolDeclaration = FunctionToolDeclaration | ProviderDefinedToolDeclaration;
102
+ /**
103
+ * What callers hand to a `tools` override. The canonical `ToolDeclaration`
104
+ * shape is accepted as-is; so is any framework shape `normalizeToolDeclarations`
105
+ * recognizes (OpenAI, Anthropic, Bedrock, LangChain, Pi, Mastra, AI SDK).
106
+ */
107
+ type ToolDeclarationInput = ToolDeclaration | {
108
+ name: string;
109
+ description?: string;
110
+ inputSchema?: JsonSchema;
111
+ parameters?: JsonSchema;
112
+ };
113
+ type PromptToolsOverride = ReadonlyArray<ToolDeclarationInput | unknown>;
114
+
115
+ type SelfDiagnosticsSignalDefinition = {
116
+ /**
117
+ * Human-readable meaning of this signal.
118
+ * Used in the injected tool schema and stored in signal properties.
119
+ */
120
+ description: string;
121
+ /**
122
+ * Optional default sentiment for this signal key.
123
+ * This can help dashboards classify negative/positive programmatic signals.
124
+ */
125
+ sentiment?: "POSITIVE" | "NEGATIVE";
126
+ };
127
+ type SelfDiagnosticsSignalDefinitions = Record<string, SelfDiagnosticsSignalDefinition>;
128
+ type SelfDiagnosticsOptions = {
129
+ /** Enable automatic injection of the self diagnostics tool. Default: false */
130
+ enabled?: boolean;
131
+ /**
132
+ * Signal keys and descriptions exposed to the model.
133
+ * Defaults to a built-in set when omitted.
134
+ */
135
+ signals?: SelfDiagnosticsSignalDefinitions;
136
+ /**
137
+ * Optional extra guidance for when the model should emit self diagnostics.
138
+ * The SDK still generates a full tool prompt from `signals`.
139
+ */
140
+ guidance?: string;
141
+ /**
142
+ * Optional tool name override for the injected self diagnostics tool.
143
+ * Defaults to "__raindrop_report".
144
+ */
145
+ toolName?: string;
146
+ };
88
147
 
89
- export type { AppGitOptions as A, IdentifyInput as I, SignalInput as S };
148
+ export type { AppGitOptions as A, IdentifyInput as I, PromptToolsOverride as P, SelfDiagnosticsOptions as S, SelfDiagnosticsSignalDefinition as a, SelfDiagnosticsSignalDefinitions as b, SignalInput as c };