@theokit/sdk 3.2.2 → 3.3.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/dist/index.cjs CHANGED
@@ -5173,12 +5173,23 @@ var init_objective_coerce = __esm({
5173
5173
  // src/internal/persistence/pagination.ts
5174
5174
  function paginate(items, opts) {
5175
5175
  if (opts === void 0 || opts.offset === void 0 && opts.limit === void 0) return items;
5176
- const start = Math.max(0, opts.offset ?? 0);
5177
- const end = opts.limit === void 0 ? items.length : start + Math.max(0, opts.limit);
5176
+ const start = opts.offset === void 0 ? 0 : requireNonNegativeInt(opts.offset, "offset");
5177
+ const limit = opts.limit === void 0 ? void 0 : requireNonNegativeInt(opts.limit, "limit");
5178
+ const end = limit === void 0 ? items.length : start + limit;
5178
5179
  return items.slice(start, end);
5179
5180
  }
5181
+ function requireNonNegativeInt(value, field) {
5182
+ if (!Number.isInteger(value) || value < 0) {
5183
+ throw new exports.ConfigurationError(
5184
+ `Invalid pagination ${field}: expected a non-negative integer, got ${value}`,
5185
+ { code: "pagination_invalid" }
5186
+ );
5187
+ }
5188
+ return value;
5189
+ }
5180
5190
  var init_pagination = __esm({
5181
5191
  "src/internal/persistence/pagination.ts"() {
5192
+ init_errors();
5182
5193
  }
5183
5194
  });
5184
5195
 
@@ -9005,7 +9016,9 @@ async function vetoFromPluginPreHook(inputs, call, callId, events) {
9005
9016
  name: call.name,
9006
9017
  args: call.input,
9007
9018
  agentId: inputs.agentId,
9008
- runId: inputs.runId
9019
+ runId: inputs.runId,
9020
+ // SE1 — thread the run's permission mode so a PermissionPlugin gates per-run.
9021
+ ...inputs.permissionMode !== void 0 ? { permissionMode: inputs.permissionMode } : {}
9009
9022
  });
9010
9023
  if (pluginVeto === void 0) return void 0;
9011
9024
  emitRunEvent(inputs.runEventSink, {
@@ -10350,6 +10363,8 @@ function parseRetryAfter(headers) {
10350
10363
  if (raw === null) return void 0;
10351
10364
  const n = Number(raw);
10352
10365
  if (Number.isFinite(n) && n >= 0) return Math.ceil(n);
10366
+ const dateMs = Date.parse(raw);
10367
+ if (Number.isFinite(dateMs)) return Math.max(0, Math.ceil((dateMs - Date.now()) / 1e3));
10353
10368
  return void 0;
10354
10369
  }
10355
10370
  function truncateRaw(body) {
@@ -10670,6 +10685,7 @@ function buildAnthropicBody(request) {
10670
10685
  var AnthropicClient, AnthropicStreamAccumulator;
10671
10686
  var init_anthropic3 = __esm({
10672
10687
  "src/internal/llm/anthropic.ts"() {
10688
+ init_errors();
10673
10689
  init_anthropic2();
10674
10690
  init_anthropic_shared();
10675
10691
  init_finish();
@@ -10723,12 +10739,23 @@ var init_anthropic3 = __esm({
10723
10739
  const events = accumulator.consume(parsed);
10724
10740
  for (const event of events) yield event;
10725
10741
  }
10742
+ if (!accumulator.finishReasonSeen) {
10743
+ throw new exports.NetworkError("Anthropic SSE stream truncated (no stop_reason)", {
10744
+ code: "stream_truncated"
10745
+ });
10746
+ }
10726
10747
  return accumulator.finish();
10727
10748
  }
10728
10749
  };
10729
10750
  AnthropicStreamAccumulator = class {
10730
10751
  text = "";
10731
10752
  stopReason = "end_turn";
10753
+ /**
10754
+ * M2 #61 — whether a `message_delta` carrying a real `stop_reason` was seen.
10755
+ * A stream that closes before it is a truncation (server FIN / proxy hiccup),
10756
+ * not a clean `end_turn` — the caller throws `stream_truncated` on `false`.
10757
+ */
10758
+ sawStopReason = false;
10732
10759
  inputTokens;
10733
10760
  outputTokens;
10734
10761
  cacheReadTokens;
@@ -10775,6 +10802,9 @@ var init_anthropic3 = __esm({
10775
10802
  * spinning the SSE parser.
10776
10803
  */
10777
10804
  handleMessageDelta(md) {
10805
+ if (md.delta.stop_reason !== void 0 && md.delta.stop_reason !== null) {
10806
+ this.sawStopReason = true;
10807
+ }
10778
10808
  this.stopReason = mapAnthropicStopReason(md.delta.stop_reason);
10779
10809
  if (md.usage?.input_tokens !== void 0) this.inputTokens = md.usage.input_tokens;
10780
10810
  if (md.usage?.output_tokens !== void 0) this.outputTokens = md.usage.output_tokens;
@@ -10785,6 +10815,10 @@ var init_anthropic3 = __esm({
10785
10815
  this.cacheReadTokens = md.usage.cache_read_input_tokens;
10786
10816
  }
10787
10817
  }
10818
+ /** M2 #61 — whether the terminal `message_delta` (stop_reason) was seen. */
10819
+ get finishReasonSeen() {
10820
+ return this.sawStopReason;
10821
+ }
10788
10822
  finish() {
10789
10823
  const toolCalls = [];
10790
10824
  for (const [index, tool] of this.toolCalls.entries()) {
@@ -13018,7 +13052,6 @@ var init_client = __esm({
13018
13052
  // concurrent request so parallel tool dispatch after a drop awaits one handshake
13019
13053
  // instead of racing (or spuriously failing with mcp_not_init).
13020
13054
  dropped = false;
13021
- reconnectAttempts = 0;
13022
13055
  reconnectPromise;
13023
13056
  get timeoutMs() {
13024
13057
  return this.config.requestTimeoutMs ?? DEFAULT_MCP_TIMEOUT_MS;
@@ -13071,15 +13104,22 @@ var init_client = __esm({
13071
13104
  return this.reconnectPromise;
13072
13105
  }
13073
13106
  async reconnect() {
13074
- if (this.reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
13075
- throw new exports.NetworkError(`MCP ${this.name} reconnect exhausted`, { code: "mcp_disconnected" });
13107
+ let lastErr;
13108
+ for (let attempt = 0; attempt < MAX_RECONNECT_ATTEMPTS; attempt += 1) {
13109
+ await reconnectDelay(attempt);
13110
+ this.spawnChild();
13111
+ try {
13112
+ await super.initialize();
13113
+ this.dropped = false;
13114
+ return;
13115
+ } catch (err) {
13116
+ lastErr = err;
13117
+ }
13076
13118
  }
13077
- await reconnectDelay(this.reconnectAttempts);
13078
- this.reconnectAttempts += 1;
13079
- this.spawnChild();
13080
- await super.initialize();
13081
- this.dropped = false;
13082
- this.reconnectAttempts = 0;
13119
+ throw new exports.NetworkError(`MCP ${this.name} reconnect exhausted`, {
13120
+ code: "mcp_disconnected",
13121
+ ...lastErr instanceof Error ? { cause: lastErr } : {}
13122
+ });
13083
13123
  }
13084
13124
  async close() {
13085
13125
  this.rejectAllPending(new exports.NetworkError(`MCP ${this.name} closed`, { code: "mcp_closed" }));
@@ -13403,6 +13443,10 @@ function buildLoopInputs(options, runId, userText) {
13403
13443
  ...options.onDelta !== void 0 ? { onDelta: options.onDelta } : {},
13404
13444
  ...options.sendOptions.toolChoice !== void 0 ? { toolChoice: options.sendOptions.toolChoice } : {},
13405
13445
  ...options.sendOptions.activeTools !== void 0 ? { activeTools: options.sendOptions.activeTools } : {},
13446
+ // SE1 — resolve the run's permission mode: per-send wins over creation-time.
13447
+ ...(options.sendOptions.permissionMode ?? options.agentOptions.permissionMode) !== void 0 ? {
13448
+ permissionMode: options.sendOptions.permissionMode ?? options.agentOptions.permissionMode
13449
+ } : {},
13406
13450
  ...options.priorMessages !== void 0 ? { priorMessages: options.priorMessages } : {},
13407
13451
  ...options.memoryTools !== void 0 && options.memoryTools.length > 0 ? { memoryTools: options.memoryTools } : {},
13408
13452
  ...buildCustomToolsInput(
@@ -22808,13 +22852,17 @@ function applyMode(verdict, mode, explicit) {
22808
22852
  return verdict;
22809
22853
  // allow stays allow
22810
22854
  case "bypass":
22855
+ case "bypassPermissions":
22811
22856
  return "allow";
22812
22857
  }
22813
22858
  }
22814
22859
  function argMatches(matcher, value) {
22815
22860
  if (typeof matcher === "function") return matcher(value);
22816
22861
  if (value === void 0) return false;
22817
- if (matcher instanceof RegExp) return matcher.test(String(value));
22862
+ if (matcher instanceof RegExp) {
22863
+ matcher.lastIndex = 0;
22864
+ return matcher.test(String(value));
22865
+ }
22818
22866
  return matcher === value;
22819
22867
  }
22820
22868
  var PermissionEngine = class {
@@ -22858,19 +22906,19 @@ async function resolveAsk(opts, name, args, mode) {
22858
22906
  } catch {
22859
22907
  return { block: true, message: `permission gate error (fail-closed): ${name}` };
22860
22908
  }
22861
- return decision.behavior === "deny" ? { block: true, message: decision.message ?? `denied: ${name}` } : void 0;
22909
+ return decision?.behavior === "allow" ? void 0 : { block: true, message: decision?.message ?? `denied: ${name}` };
22862
22910
  }
22863
22911
  return opts.onAsk ? opts.onAsk(name) : { block: true, message: `requires approval: ${name}` };
22864
22912
  }
22865
22913
  function createPermissionPlugin(engine, opts = {}) {
22866
- const mode = opts.mode ?? "default";
22867
22914
  return definePlugin({
22868
22915
  name: opts.name ?? "permission-engine",
22869
22916
  version: "1.0.0",
22870
22917
  kind: "general",
22871
22918
  register(ctx) {
22872
22919
  ctx.on("pre_tool_call", async (rawCtx) => {
22873
- const { name, args } = rawCtx;
22920
+ const { name, args, permissionMode } = rawCtx;
22921
+ const mode = permissionMode ?? opts.mode ?? "default";
22874
22922
  const action = engine.evaluate(name, args, mode);
22875
22923
  if (action === "deny") {
22876
22924
  return { block: true, message: `denied by permission engine: ${name}` };