@bolt-foundry/gambit-core 0.8.6 → 1.0.0-rc.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.
@@ -35,13 +35,16 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.RunCanceledError = void 0;
37
37
  exports.isGambitEndSignal = isGambitEndSignal;
38
+ exports.stringifyResponseOutput = stringifyResponseOutput;
38
39
  exports.isRunCanceledError = isRunCanceledError;
39
40
  exports.runDeck = runDeck;
41
+ exports.runDeckResponses = runDeckResponses;
40
42
  // deno-lint-ignore-file gambit/no-unexplained-as-unknown
41
43
  const dntShim = __importStar(require("../_dnt.shims.js"));
42
44
  const path = __importStar(require("../deps/jsr.io/@std/path/1.1.4/mod.js"));
43
45
  const constants_js_1 = require("./constants.js");
44
46
  const loader_js_1 = require("./loader.js");
47
+ const text_js_1 = require("./text.js");
45
48
  const permissions_js_1 = require("./permissions.js");
46
49
  const runtime_exec_host_js_1 = require("./runtime_exec_host.js");
47
50
  const runtime_worker_host_js_1 = require("./runtime_worker_host.js");
@@ -57,6 +60,19 @@ function randomId(prefix) {
57
60
  // Keep IDs short enough for OpenAI/OpenRouter tool_call id limits (~40 chars).
58
61
  return `${prefix}-${suffix}`;
59
62
  }
63
+ function stringifyResponseOutput(output) {
64
+ const parts = [];
65
+ for (const item of output) {
66
+ if (item.type !== "message" || item.role !== "assistant")
67
+ continue;
68
+ for (const content of item.content) {
69
+ if (content.type === "output_text") {
70
+ parts.push(content.text);
71
+ }
72
+ }
73
+ }
74
+ return parts.join("");
75
+ }
60
76
  const WORKER_SANDBOX_ENV = "GAMBIT_DECK_WORKER_SANDBOX";
61
77
  const WORKER_TIMEOUT_MESSAGE = "Timeout exceeded";
62
78
  const RUN_CANCELED_MESSAGE = "Run canceled";
@@ -484,6 +500,7 @@ async function runDeck(opts) {
484
500
  initialUserMessage: opts.initialUserMessage,
485
501
  parentActionCallId: opts.parentActionCallId,
486
502
  modelProvider: opts.modelProvider,
503
+ modelResolver: opts.modelResolver,
487
504
  input: resolvedInput,
488
505
  defaultModel: opts.defaultModel,
489
506
  modelOverride: opts.modelOverride,
@@ -516,6 +533,7 @@ async function runDeck(opts) {
516
533
  runId,
517
534
  parentActionCallId: opts.parentActionCallId,
518
535
  modelProvider: opts.modelProvider,
536
+ modelResolver: opts.modelResolver,
519
537
  input: resolvedInput,
520
538
  inputProvided: opts.inputProvided ?? true,
521
539
  initialUserMessage: opts.initialUserMessage,
@@ -548,7 +566,7 @@ async function runDeck(opts) {
548
566
  const deck = opts.runtimeTools?.length
549
567
  ? {
550
568
  ...loadedDeck,
551
- tools: [...loadedDeck.tools, ...opts.runtimeTools],
569
+ tools: Array.from([...loadedDeck.tools, ...opts.runtimeTools].reduce((byName, tool) => byName.set(tool.name, tool), new Map()).values()),
552
570
  }
553
571
  : loadedDeck;
554
572
  const permissions = (0, permissions_js_1.resolveEffectivePermissions)({
@@ -609,6 +627,7 @@ async function runDeck(opts) {
609
627
  runId,
610
628
  parentActionCallId: opts.parentActionCallId,
611
629
  modelProvider: opts.modelProvider,
630
+ modelResolver: opts.modelResolver,
612
631
  input: validatedInput,
613
632
  inputProvided: opts.inputProvided ?? true,
614
633
  initialUserMessage: opts.initialUserMessage,
@@ -655,6 +674,7 @@ async function runDeck(opts) {
655
674
  runId,
656
675
  parentActionCallId: opts.parentActionCallId,
657
676
  modelProvider: opts.modelProvider,
677
+ modelResolver: opts.modelResolver,
658
678
  input: validatedInput,
659
679
  inputProvided: opts.inputProvided ?? true,
660
680
  initialUserMessage: opts.initialUserMessage,
@@ -692,6 +712,7 @@ async function runDeck(opts) {
692
712
  initialUserMessage: opts.initialUserMessage,
693
713
  parentActionCallId: opts.parentActionCallId,
694
714
  modelProvider: opts.modelProvider,
715
+ modelResolver: opts.modelResolver,
695
716
  input: validatedInput,
696
717
  defaultModel: opts.defaultModel,
697
718
  modelOverride: opts.modelOverride,
@@ -732,6 +753,57 @@ async function runDeck(opts) {
732
753
  }
733
754
  }
734
755
  }
756
+ async function runDeckResponses(opts) {
757
+ const runId = opts.runId ?? opts.state?.runId ?? randomId("run");
758
+ const traceEvents = [];
759
+ const responseEvents = [];
760
+ let output = [];
761
+ let state = opts.state;
762
+ let usage;
763
+ let finishReason;
764
+ const trace = (event) => {
765
+ traceEvents.push(event);
766
+ if (event.type === "model.result" &&
767
+ event.mode === "responses" &&
768
+ !event.parentActionCallId) {
769
+ output = event.responseItems ?? [];
770
+ usage = event.usage;
771
+ finishReason = event.finishReason;
772
+ }
773
+ if (event.type === "model.stream.event" ||
774
+ event.type.startsWith("response.")) {
775
+ responseEvents.push(event);
776
+ }
777
+ opts.trace?.(event);
778
+ };
779
+ const onStateUpdate = (nextState) => {
780
+ state = nextState;
781
+ opts.onStateUpdate?.(nextState);
782
+ };
783
+ const legacyResult = await runDeck({
784
+ ...opts,
785
+ runId,
786
+ responsesMode: true,
787
+ trace,
788
+ onStateUpdate,
789
+ });
790
+ const effects = traceEvents.filter((event) => event.type === "action.start" ||
791
+ event.type === "action.end" ||
792
+ event.type === "tool.call" ||
793
+ event.type === "tool.result");
794
+ return {
795
+ runId,
796
+ status: "completed",
797
+ output,
798
+ traceEvents,
799
+ responseEvents,
800
+ state,
801
+ usage,
802
+ finishReason,
803
+ effects,
804
+ legacyResult,
805
+ };
806
+ }
735
807
  function toProviderParams(params) {
736
808
  if (!params)
737
809
  return undefined;
@@ -758,7 +830,7 @@ function toProviderParams(params) {
758
830
  return Object.keys(out).length ? out : undefined;
759
831
  }
760
832
  async function resolveModelChoice(args) {
761
- const resolver = args.modelProvider.resolveModel;
833
+ const resolver = args.modelResolver?.resolveModel;
762
834
  if (resolver) {
763
835
  return await resolver({
764
836
  model: args.model,
@@ -778,11 +850,8 @@ async function resolveModelChoice(args) {
778
850
  }
779
851
  return { model: args.model, params: args.params };
780
852
  }
781
- function shouldExposeProviderToolArray(model) {
782
- const normalized = model.trim().toLowerCase();
783
- if (!normalized)
784
- return true;
785
- return normalized !== "codex-cli" && !normalized.startsWith("codex-cli/");
853
+ function shouldExposeProviderToolArray(_model) {
854
+ return true;
786
855
  }
787
856
  function resolveContextSchema(deck) {
788
857
  return deck.contextSchema ?? deck.inputSchema;
@@ -962,7 +1031,7 @@ function messagesFromResponseItems(items) {
962
1031
  const callNameById = new Map();
963
1032
  for (const item of items) {
964
1033
  if (item.type === "message") {
965
- const text = item.content.map((part) => part.text).join("");
1034
+ const text = (0, text_js_1.joinTextParts)(item.content.map((part) => part.text));
966
1035
  messages.push({
967
1036
  role: item.role,
968
1037
  content: text || null,
@@ -2015,7 +2084,7 @@ function mapResponseOutput(output) {
2015
2084
  return {
2016
2085
  message: {
2017
2086
  role: "assistant",
2018
- content: textParts.length ? textParts.join("") : null,
2087
+ content: textParts.length ? (0, text_js_1.joinTextParts)(textParts) : null,
2019
2088
  },
2020
2089
  toolCalls: toolCalls.length ? toolCalls : undefined,
2021
2090
  };
@@ -2079,6 +2148,7 @@ async function runComputeDeck(ctx) {
2079
2148
  initialUserMessage: ctx.initialUserMessage,
2080
2149
  parentActionCallId: ctx.parentActionCallId,
2081
2150
  modelProvider: ctx.modelProvider,
2151
+ modelResolver: ctx.modelResolver,
2082
2152
  input: ctx.input,
2083
2153
  defaultModel: ctx.defaultModel,
2084
2154
  modelOverride: ctx.modelOverride,
@@ -2835,54 +2905,11 @@ async function runLlmDeckInWorker(ctx) {
2835
2905
  ctx.onStreamText?.(msg.chunk);
2836
2906
  return;
2837
2907
  }
2838
- if (msg.type === "model.chat.request") {
2839
- (async () => {
2840
- const controller = new AbortController();
2841
- modelRequestControllers.set(msg.requestId, controller);
2842
- try {
2843
- const result = await ctx.modelProvider.chat({
2844
- ...msg.input,
2845
- signal: mergeAbortSignals(ctx.signal, controller.signal),
2846
- onStreamText: (chunk) => {
2847
- worker.postMessage({
2848
- type: "model.chat.stream",
2849
- requestId: msg.requestId,
2850
- chunk,
2851
- });
2852
- },
2853
- });
2854
- worker.postMessage({
2855
- type: "model.chat.result",
2856
- requestId: msg.requestId,
2857
- result,
2858
- });
2859
- }
2860
- catch (err) {
2861
- worker.postMessage({
2862
- type: "model.chat.error",
2863
- requestId: msg.requestId,
2864
- error: {
2865
- source: "model",
2866
- name: err instanceof Error ? err.name : undefined,
2867
- message: err instanceof Error ? err.message : String(err),
2868
- code: err?.code,
2869
- },
2870
- });
2871
- }
2872
- finally {
2873
- modelRequestControllers.delete(msg.requestId);
2874
- }
2875
- })();
2876
- return;
2877
- }
2878
2908
  if (msg.type === "model.responses.request") {
2879
2909
  (async () => {
2880
2910
  const controller = new AbortController();
2881
2911
  modelRequestControllers.set(msg.requestId, controller);
2882
2912
  try {
2883
- if (!ctx.modelProvider.responses) {
2884
- throw new Error("Responses API unavailable for current model provider");
2885
- }
2886
2913
  const result = await ctx.modelProvider.responses({
2887
2914
  ...msg.input,
2888
2915
  signal: mergeAbortSignals(ctx.signal, controller.signal),
@@ -2925,8 +2952,8 @@ async function runLlmDeckInWorker(ctx) {
2925
2952
  if (msg.type === "model.resolveModel.request") {
2926
2953
  (async () => {
2927
2954
  try {
2928
- const result = ctx.modelProvider.resolveModel
2929
- ? await ctx.modelProvider.resolveModel(msg.input)
2955
+ const result = ctx.modelResolver?.resolveModel
2956
+ ? await ctx.modelResolver.resolveModel(msg.input)
2930
2957
  : {
2931
2958
  model: Array.isArray(msg.input.model)
2932
2959
  ? msg.input.model[0]
@@ -3138,6 +3165,7 @@ async function runComputeDeckInWorker(ctx) {
3138
3165
  path: req.payload.path,
3139
3166
  input: req.payload.input,
3140
3167
  modelProvider: ctx.modelProvider,
3168
+ modelResolver: ctx.modelResolver,
3141
3169
  isRoot: false,
3142
3170
  guardrails: ctx.guardrails,
3143
3171
  depth: ctx.depth + 1,
@@ -3418,6 +3446,7 @@ async function runComputeDeckInProcess(ctx) {
3418
3446
  path: childPath,
3419
3447
  input: opts.input,
3420
3448
  modelProvider: ctx.modelProvider,
3449
+ modelResolver: ctx.modelResolver,
3421
3450
  isRoot: false,
3422
3451
  guardrails: ctx.guardrails,
3423
3452
  depth: ctx.depth + 1,
@@ -3481,8 +3510,7 @@ async function runLlmDeck(ctx) {
3481
3510
  const { deck, guardrails, depth, modelProvider, input, runId, inputProvided, initialUserMessage, } = ctx;
3482
3511
  const actionCallId = randomId("action");
3483
3512
  const start = performance.now();
3484
- const useResponses = Boolean(ctx.responsesMode) ||
3485
- ctx.state?.format === "responses";
3513
+ const useResponses = true;
3486
3514
  const validateResponseItemEmission = createResponseItemEmissionValidator(deck);
3487
3515
  const intermediateEmitter = ctx.parentActionCallId !== undefined
3488
3516
  ? createIntermediateChildResponseEmitter({
@@ -3637,6 +3665,7 @@ async function runLlmDeck(ctx) {
3637
3665
  model: modelCandidate,
3638
3666
  params: toProviderParams(deck.modelParams),
3639
3667
  modelProvider,
3668
+ modelResolver: ctx.modelResolver,
3640
3669
  deckPath: deck.path,
3641
3670
  });
3642
3671
  const model = resolved.model;
@@ -3665,144 +3694,93 @@ async function runLlmDeck(ctx) {
3665
3694
  parentActionCallId: ctx.parentActionCallId,
3666
3695
  });
3667
3696
  let responseOutputItems;
3668
- const responses = modelProvider.responses;
3669
3697
  const projectedToolCalls = new Set();
3670
3698
  const projectedToolResults = new Set();
3671
3699
  const projectedToolNames = new Map();
3672
3700
  const canonicalizeStreamEvent = createCanonicalStreamEventController();
3673
- const result = (useResponses && responses)
3674
- ? await (async () => {
3675
- const responseItems = responseItemsFromMessages(messages);
3676
- let sawDelta = false;
3677
- const response = await responses({
3678
- request: {
3679
- model,
3680
- input: responseItems,
3681
- tools: providerResponseTools,
3682
- text: responseTextConfig,
3683
- stream: ctx.stream,
3684
- params: providerParams,
3685
- },
3686
- state: ctx.state,
3687
- deckPath: deck.path,
3688
- signal: ctx.signal,
3689
- onStreamEvent: (ctx.trace || ctx.onStreamText || deck.handlers?.onIdle)
3690
- ? (event) => {
3691
- const normalizedEvent = validateResponseEventItems(event, validateResponseItemEmission);
3692
- const streamEvent = canonicalizeStreamEvent(normalizedEvent);
3693
- if (!streamEvent)
3694
- return;
3695
- if (ctx.trace) {
3696
- const handledAsResponse = traceOpenResponsesStreamEvent({
3697
- streamEvent,
3698
- runId,
3699
- actionCallId,
3700
- deckPath: deck.path,
3701
- model,
3702
- parentActionCallId: ctx.parentActionCallId,
3703
- trace: ctx.trace,
3704
- });
3705
- if (!handledAsResponse) {
3706
- ctx.trace({
3707
- type: "model.stream.event",
3708
- runId,
3709
- actionCallId,
3710
- deckPath: deck.path,
3711
- model,
3712
- event: streamEvent,
3713
- parentActionCallId: ctx.parentActionCallId,
3714
- });
3715
- }
3716
- projectStreamToolTraceEvents({
3717
- streamEvent,
3718
- runId,
3719
- parentActionCallId: actionCallId,
3720
- trace: ctx.trace,
3721
- emittedCalls: projectedToolCalls,
3722
- emittedResults: projectedToolResults,
3723
- toolNames: projectedToolNames,
3724
- });
3725
- }
3726
- const eventType = typeof streamEvent.type === "string"
3727
- ? streamEvent.type
3728
- : "";
3729
- if (eventType === "response.output_text.delta") {
3730
- sawDelta = true;
3731
- const delta = typeof streamEvent.delta === "string"
3732
- ? streamEvent.delta
3733
- : "";
3734
- if (delta)
3735
- wrappedOnStreamText(delta);
3736
- }
3737
- else if (eventType === "response.output_text.done" && !sawDelta) {
3738
- const text = typeof streamEvent.text === "string"
3739
- ? streamEvent.text
3740
- : "";
3741
- if (text)
3742
- wrappedOnStreamText(text);
3743
- }
3744
- }
3745
- : undefined,
3746
- });
3747
- responseOutputItems = validateResponseOutputItems(response.output ?? [], validateResponseItemEmission, "response.output");
3748
- const mapped = mapResponseOutput(responseOutputItems);
3749
- return {
3750
- message: mapped.message,
3751
- finishReason: mapped.toolCalls?.length ? "tool_calls" : "stop",
3752
- toolCalls: mapped.toolCalls,
3753
- usage: response.usage,
3754
- updatedState: response.updatedState,
3755
- };
3756
- })()
3757
- : await modelProvider.chat({
3758
- model,
3759
- messages,
3760
- tools,
3761
- stream: ctx.stream,
3701
+ const result = await (async () => {
3702
+ const responseItems = responseItemsFromMessages(messages);
3703
+ let sawDelta = false;
3704
+ const response = await modelProvider.responses({
3705
+ request: {
3706
+ model,
3707
+ input: responseItems,
3708
+ tools: providerResponseTools,
3709
+ text: responseTextConfig,
3710
+ stream: ctx.stream,
3711
+ params: providerParams,
3712
+ },
3762
3713
  state: ctx.state,
3763
3714
  deckPath: deck.path,
3764
3715
  signal: ctx.signal,
3765
- params: providerParams,
3766
- onStreamText: (ctx.onStreamText || deck.handlers?.onIdle)
3767
- ? wrappedOnStreamText
3768
- : undefined,
3769
- onStreamEvent: ctx.trace
3716
+ onStreamEvent: (ctx.trace || ctx.onStreamText || deck.handlers?.onIdle)
3770
3717
  ? (event) => {
3771
- const streamEvent = canonicalizeStreamEvent(event);
3718
+ const normalizedEvent = validateResponseEventItems(event, validateResponseItemEmission);
3719
+ const streamEvent = canonicalizeStreamEvent(normalizedEvent);
3772
3720
  if (!streamEvent)
3773
3721
  return;
3774
- const handledAsResponse = traceOpenResponsesStreamEvent({
3775
- streamEvent,
3776
- runId,
3777
- actionCallId,
3778
- deckPath: deck.path,
3779
- model,
3780
- parentActionCallId: ctx.parentActionCallId,
3781
- trace: ctx.trace,
3782
- });
3783
- if (!handledAsResponse) {
3784
- ctx.trace?.({
3785
- type: "model.stream.event",
3722
+ if (ctx.trace) {
3723
+ const handledAsResponse = traceOpenResponsesStreamEvent({
3724
+ streamEvent,
3786
3725
  runId,
3787
3726
  actionCallId,
3788
3727
  deckPath: deck.path,
3789
3728
  model,
3790
- event: streamEvent,
3791
3729
  parentActionCallId: ctx.parentActionCallId,
3730
+ trace: ctx.trace,
3731
+ });
3732
+ if (!handledAsResponse) {
3733
+ ctx.trace({
3734
+ type: "model.stream.event",
3735
+ runId,
3736
+ actionCallId,
3737
+ deckPath: deck.path,
3738
+ model,
3739
+ event: streamEvent,
3740
+ parentActionCallId: ctx.parentActionCallId,
3741
+ });
3742
+ }
3743
+ projectStreamToolTraceEvents({
3744
+ streamEvent,
3745
+ runId,
3746
+ parentActionCallId: actionCallId,
3747
+ trace: ctx.trace,
3748
+ emittedCalls: projectedToolCalls,
3749
+ emittedResults: projectedToolResults,
3750
+ toolNames: projectedToolNames,
3792
3751
  });
3793
3752
  }
3794
- projectStreamToolTraceEvents({
3795
- streamEvent,
3796
- runId,
3797
- parentActionCallId: actionCallId,
3798
- trace: ctx.trace,
3799
- emittedCalls: projectedToolCalls,
3800
- emittedResults: projectedToolResults,
3801
- toolNames: projectedToolNames,
3802
- });
3753
+ const eventType = typeof streamEvent.type === "string"
3754
+ ? streamEvent.type
3755
+ : "";
3756
+ if (eventType === "response.output_text.delta") {
3757
+ sawDelta = true;
3758
+ const delta = typeof streamEvent.delta === "string"
3759
+ ? streamEvent.delta
3760
+ : "";
3761
+ if (delta)
3762
+ wrappedOnStreamText(delta);
3763
+ }
3764
+ else if (eventType === "response.output_text.done" && !sawDelta) {
3765
+ const text = typeof streamEvent.text === "string"
3766
+ ? streamEvent.text
3767
+ : "";
3768
+ if (text)
3769
+ wrappedOnStreamText(text);
3770
+ }
3803
3771
  }
3804
3772
  : undefined,
3805
3773
  });
3774
+ responseOutputItems = validateResponseOutputItems(response.output ?? [], validateResponseItemEmission, "response.output");
3775
+ const mapped = mapResponseOutput(responseOutputItems);
3776
+ return {
3777
+ message: mapped.message,
3778
+ finishReason: mapped.toolCalls?.length ? "tool_calls" : "stop",
3779
+ toolCalls: mapped.toolCalls,
3780
+ usage: response.usage,
3781
+ updatedState: response.updatedState,
3782
+ };
3783
+ })();
3806
3784
  idleController.touch();
3807
3785
  let message = result.message;
3808
3786
  ctx.trace?.({
@@ -4705,6 +4683,7 @@ async function handleToolCall(call, ctx) {
4705
4683
  path: action.path,
4706
4684
  input: actionInput,
4707
4685
  modelProvider: ctx.modelProvider,
4686
+ modelResolver: ctx.modelResolver,
4708
4687
  isRoot: false,
4709
4688
  guardrails: ctx.guardrails,
4710
4689
  depth: ctx.depth + 1,
@@ -4804,6 +4783,7 @@ async function handleToolCall(call, ctx) {
4804
4783
  path: action.path,
4805
4784
  input: actionInput,
4806
4785
  modelProvider: ctx.modelProvider,
4786
+ modelResolver: ctx.modelResolver,
4807
4787
  isRoot: false,
4808
4788
  guardrails: ctx.guardrails,
4809
4789
  depth: ctx.depth + 1,
@@ -4853,6 +4833,7 @@ async function handleToolCall(call, ctx) {
4853
4833
  parentActionCallId: ctx.parentActionCallId,
4854
4834
  handlerPath: busyCfg.path,
4855
4835
  modelProvider: ctx.modelProvider,
4836
+ modelResolver: ctx.modelResolver,
4856
4837
  guardrails: ctx.guardrails,
4857
4838
  depth: ctx.depth,
4858
4839
  defaultModel: ctx.defaultModel,
@@ -4954,6 +4935,7 @@ async function handleToolCall(call, ctx) {
4954
4935
  parentActionCallId: ctx.parentActionCallId,
4955
4936
  handlerPath: busyCfg.path,
4956
4937
  modelProvider: ctx.modelProvider,
4938
+ modelResolver: ctx.modelResolver,
4957
4939
  guardrails: ctx.guardrails,
4958
4940
  depth: ctx.depth,
4959
4941
  defaultModel: ctx.defaultModel,
@@ -5043,6 +5025,7 @@ async function runBusyHandler(args) {
5043
5025
  path: args.handlerPath,
5044
5026
  input,
5045
5027
  modelProvider: args.modelProvider,
5028
+ modelResolver: args.modelResolver,
5046
5029
  isRoot: false,
5047
5030
  guardrails: args.guardrails,
5048
5031
  depth: args.depth + 1,
@@ -5136,6 +5119,7 @@ function createIdleController(args) {
5136
5119
  runId: args.runId,
5137
5120
  parentActionCallId: args.parentActionCallId,
5138
5121
  modelProvider: args.modelProvider,
5122
+ modelResolver: args.modelResolver,
5139
5123
  guardrails: args.guardrails,
5140
5124
  depth: args.depth,
5141
5125
  defaultModel: args.defaultModel,
@@ -5210,6 +5194,7 @@ async function runIdleHandler(args) {
5210
5194
  path: args.handlerPath,
5211
5195
  input,
5212
5196
  modelProvider: args.modelProvider,
5197
+ modelResolver: args.modelResolver,
5213
5198
  isRoot: false,
5214
5199
  guardrails: args.guardrails,
5215
5200
  depth: args.depth + 1,
@@ -5289,6 +5274,7 @@ async function maybeHandleError(args) {
5289
5274
  path: handlerPath,
5290
5275
  input: envelopeInput,
5291
5276
  modelProvider: args.ctx.modelProvider,
5277
+ modelResolver: args.ctx.modelResolver,
5292
5278
  isRoot: false,
5293
5279
  guardrails: args.ctx.guardrails,
5294
5280
  depth: args.ctx.depth + 1,
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/src/state.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC9B,KAAK,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC3B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,iBAAiB,CAAC,EAAE,aAAa,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3B,MAAM,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA2CF,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAsBlE;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,QAK5D"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/src/state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzE,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC9B,KAAK,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC3B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,iBAAiB,CAAC,EAAE,aAAa,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3B,MAAM,CAAC,EAAE,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA2CF,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAsBlE;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,QAK5D"}
@@ -37,6 +37,7 @@ exports.loadState = loadState;
37
37
  exports.saveState = saveState;
38
38
  const dntShim = __importStar(require("../_dnt.shims.js"));
39
39
  const path = __importStar(require("../deps/jsr.io/@std/path/1.1.4/mod.js"));
40
+ const text_js_1 = require("./text.js");
40
41
  function deriveMessagesFromItems(items) {
41
42
  const messages = [];
42
43
  const callNameById = new Map();
@@ -44,10 +45,10 @@ function deriveMessagesFromItems(items) {
44
45
  if (item.type === "message") {
45
46
  const text = item.content
46
47
  .map((part) => part.text)
47
- .join("");
48
+ .filter(Boolean);
48
49
  messages.push({
49
50
  role: item.role,
50
- content: text || null,
51
+ content: text.length ? (0, text_js_1.joinTextParts)(text) : null,
51
52
  });
52
53
  continue;
53
54
  }
@@ -0,0 +1,2 @@
1
+ export declare function joinTextParts(parts: Array<string>): string;
2
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/src/text.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,MAAM,CAE1D"}
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.joinTextParts = joinTextParts;
4
+ function joinTextParts(parts) {
5
+ return parts.filter(Boolean).join("");
6
+ }
@@ -119,6 +119,7 @@ export type BaseDefinition = {
119
119
  export type DeckDefinition<Input = unknown> = BaseDefinition & {
120
120
  kind: "gambit.deck";
121
121
  modelParams?: ModelParams;
122
+ workloop?: unknown;
122
123
  tools?: ReadonlyArray<ExternalToolDefinition>;
123
124
  responseItemExtensions?: ReadonlyArray<ResponseItemExtensionDefinition>;
124
125
  handlers?: HandlersConfig;
@@ -559,7 +560,7 @@ export type ResponseEvent = {
559
560
  payload: Record<string, JSONValue>;
560
561
  };
561
562
  export type ModelProvider = {
562
- responses?: (input: {
563
+ responses: (input: {
563
564
  request: CreateResponseRequest;
564
565
  state?: SavedState;
565
566
  deckPath?: string;
@@ -567,6 +568,8 @@ export type ModelProvider = {
567
568
  onStreamEvent?: (event: ResponseEvent) => void;
568
569
  onTraceEvent?: (event: ProviderTraceEvent) => void;
569
570
  }) => Promise<CreateResponseResponse>;
571
+ };
572
+ export type ModelResolver = {
570
573
  resolveModel?: (input: {
571
574
  model: string | Array<string>;
572
575
  params?: Record<string, unknown>;
@@ -575,38 +578,6 @@ export type ModelProvider = {
575
578
  model: string;
576
579
  params?: Record<string, unknown>;
577
580
  }>;
578
- chat: (input: {
579
- model: string;
580
- messages: Array<ModelMessage>;
581
- tools?: Array<ToolDefinition>;
582
- stream?: boolean;
583
- state?: SavedState;
584
- deckPath?: string;
585
- signal?: AbortSignal;
586
- onStreamText?: (chunk: string) => void;
587
- onStreamEvent?: (event: Record<string, JSONValue>) => void;
588
- onTraceEvent?: (event: ProviderTraceEvent) => void;
589
- /**
590
- * Provider-specific pass-through parameters (e.g. OpenAI chat completion
591
- * fields like temperature/max_tokens).
592
- */
593
- params?: Record<string, unknown>;
594
- }) => Promise<{
595
- message: ModelMessage;
596
- finishReason: "stop" | "tool_calls" | "length";
597
- toolCalls?: Array<{
598
- id: string;
599
- name: string;
600
- args: Record<string, JSONValue>;
601
- }>;
602
- updatedState?: SavedState;
603
- usage?: {
604
- promptTokens: number;
605
- completionTokens: number;
606
- totalTokens: number;
607
- reasoningTokens?: number;
608
- };
609
- }>;
610
581
  };
611
582
  export type ProviderTraceEvent = TraceEvent | (Omit<Extract<TraceEvent, {
612
583
  type: "tool.call";