@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.
@@ -3,6 +3,7 @@ import * as dntShim from "../_dnt.shims.js";
3
3
  import * as path from "../deps/jsr.io/@std/path/1.1.4/mod.js";
4
4
  import { DEFAULT_GUARDRAILS, DEFAULT_STATUS_DELAY_MS, GAMBIT_TOOL_CONTEXT, GAMBIT_TOOL_END, GAMBIT_TOOL_INIT, GAMBIT_TOOL_RESPOND, } from "./constants.js";
5
5
  import { loadDeck } from "./loader.js";
6
+ import { joinTextParts } from "./text.js";
6
7
  import { canReadPath, canRunCommand, canRunPath, canWritePath, intersectPermissions, resolveEffectivePermissions, } from "./permissions.js";
7
8
  import { ExecToolUnsupportedHostError, executeBuiltinCommand, } from "./runtime_exec_host.js";
8
9
  import { createWorkerSandboxBridge, isWorkerSandboxHostSupported, WorkerSandboxUnsupportedHostError, } from "./runtime_worker_host.js";
@@ -18,6 +19,19 @@ function randomId(prefix) {
18
19
  // Keep IDs short enough for OpenAI/OpenRouter tool_call id limits (~40 chars).
19
20
  return `${prefix}-${suffix}`;
20
21
  }
22
+ export function stringifyResponseOutput(output) {
23
+ const parts = [];
24
+ for (const item of output) {
25
+ if (item.type !== "message" || item.role !== "assistant")
26
+ continue;
27
+ for (const content of item.content) {
28
+ if (content.type === "output_text") {
29
+ parts.push(content.text);
30
+ }
31
+ }
32
+ }
33
+ return parts.join("");
34
+ }
21
35
  const WORKER_SANDBOX_ENV = "GAMBIT_DECK_WORKER_SANDBOX";
22
36
  const WORKER_TIMEOUT_MESSAGE = "Timeout exceeded";
23
37
  const RUN_CANCELED_MESSAGE = "Run canceled";
@@ -444,6 +458,7 @@ export async function runDeck(opts) {
444
458
  initialUserMessage: opts.initialUserMessage,
445
459
  parentActionCallId: opts.parentActionCallId,
446
460
  modelProvider: opts.modelProvider,
461
+ modelResolver: opts.modelResolver,
447
462
  input: resolvedInput,
448
463
  defaultModel: opts.defaultModel,
449
464
  modelOverride: opts.modelOverride,
@@ -476,6 +491,7 @@ export async function runDeck(opts) {
476
491
  runId,
477
492
  parentActionCallId: opts.parentActionCallId,
478
493
  modelProvider: opts.modelProvider,
494
+ modelResolver: opts.modelResolver,
479
495
  input: resolvedInput,
480
496
  inputProvided: opts.inputProvided ?? true,
481
497
  initialUserMessage: opts.initialUserMessage,
@@ -508,7 +524,7 @@ export async function runDeck(opts) {
508
524
  const deck = opts.runtimeTools?.length
509
525
  ? {
510
526
  ...loadedDeck,
511
- tools: [...loadedDeck.tools, ...opts.runtimeTools],
527
+ tools: Array.from([...loadedDeck.tools, ...opts.runtimeTools].reduce((byName, tool) => byName.set(tool.name, tool), new Map()).values()),
512
528
  }
513
529
  : loadedDeck;
514
530
  const permissions = resolveEffectivePermissions({
@@ -569,6 +585,7 @@ export async function runDeck(opts) {
569
585
  runId,
570
586
  parentActionCallId: opts.parentActionCallId,
571
587
  modelProvider: opts.modelProvider,
588
+ modelResolver: opts.modelResolver,
572
589
  input: validatedInput,
573
590
  inputProvided: opts.inputProvided ?? true,
574
591
  initialUserMessage: opts.initialUserMessage,
@@ -615,6 +632,7 @@ export async function runDeck(opts) {
615
632
  runId,
616
633
  parentActionCallId: opts.parentActionCallId,
617
634
  modelProvider: opts.modelProvider,
635
+ modelResolver: opts.modelResolver,
618
636
  input: validatedInput,
619
637
  inputProvided: opts.inputProvided ?? true,
620
638
  initialUserMessage: opts.initialUserMessage,
@@ -652,6 +670,7 @@ export async function runDeck(opts) {
652
670
  initialUserMessage: opts.initialUserMessage,
653
671
  parentActionCallId: opts.parentActionCallId,
654
672
  modelProvider: opts.modelProvider,
673
+ modelResolver: opts.modelResolver,
655
674
  input: validatedInput,
656
675
  defaultModel: opts.defaultModel,
657
676
  modelOverride: opts.modelOverride,
@@ -692,6 +711,57 @@ export async function runDeck(opts) {
692
711
  }
693
712
  }
694
713
  }
714
+ export async function runDeckResponses(opts) {
715
+ const runId = opts.runId ?? opts.state?.runId ?? randomId("run");
716
+ const traceEvents = [];
717
+ const responseEvents = [];
718
+ let output = [];
719
+ let state = opts.state;
720
+ let usage;
721
+ let finishReason;
722
+ const trace = (event) => {
723
+ traceEvents.push(event);
724
+ if (event.type === "model.result" &&
725
+ event.mode === "responses" &&
726
+ !event.parentActionCallId) {
727
+ output = event.responseItems ?? [];
728
+ usage = event.usage;
729
+ finishReason = event.finishReason;
730
+ }
731
+ if (event.type === "model.stream.event" ||
732
+ event.type.startsWith("response.")) {
733
+ responseEvents.push(event);
734
+ }
735
+ opts.trace?.(event);
736
+ };
737
+ const onStateUpdate = (nextState) => {
738
+ state = nextState;
739
+ opts.onStateUpdate?.(nextState);
740
+ };
741
+ const legacyResult = await runDeck({
742
+ ...opts,
743
+ runId,
744
+ responsesMode: true,
745
+ trace,
746
+ onStateUpdate,
747
+ });
748
+ const effects = traceEvents.filter((event) => event.type === "action.start" ||
749
+ event.type === "action.end" ||
750
+ event.type === "tool.call" ||
751
+ event.type === "tool.result");
752
+ return {
753
+ runId,
754
+ status: "completed",
755
+ output,
756
+ traceEvents,
757
+ responseEvents,
758
+ state,
759
+ usage,
760
+ finishReason,
761
+ effects,
762
+ legacyResult,
763
+ };
764
+ }
695
765
  function toProviderParams(params) {
696
766
  if (!params)
697
767
  return undefined;
@@ -718,7 +788,7 @@ function toProviderParams(params) {
718
788
  return Object.keys(out).length ? out : undefined;
719
789
  }
720
790
  async function resolveModelChoice(args) {
721
- const resolver = args.modelProvider.resolveModel;
791
+ const resolver = args.modelResolver?.resolveModel;
722
792
  if (resolver) {
723
793
  return await resolver({
724
794
  model: args.model,
@@ -738,11 +808,8 @@ async function resolveModelChoice(args) {
738
808
  }
739
809
  return { model: args.model, params: args.params };
740
810
  }
741
- function shouldExposeProviderToolArray(model) {
742
- const normalized = model.trim().toLowerCase();
743
- if (!normalized)
744
- return true;
745
- return normalized !== "codex-cli" && !normalized.startsWith("codex-cli/");
811
+ function shouldExposeProviderToolArray(_model) {
812
+ return true;
746
813
  }
747
814
  function resolveContextSchema(deck) {
748
815
  return deck.contextSchema ?? deck.inputSchema;
@@ -922,7 +989,7 @@ function messagesFromResponseItems(items) {
922
989
  const callNameById = new Map();
923
990
  for (const item of items) {
924
991
  if (item.type === "message") {
925
- const text = item.content.map((part) => part.text).join("");
992
+ const text = joinTextParts(item.content.map((part) => part.text));
926
993
  messages.push({
927
994
  role: item.role,
928
995
  content: text || null,
@@ -1975,7 +2042,7 @@ function mapResponseOutput(output) {
1975
2042
  return {
1976
2043
  message: {
1977
2044
  role: "assistant",
1978
- content: textParts.length ? textParts.join("") : null,
2045
+ content: textParts.length ? joinTextParts(textParts) : null,
1979
2046
  },
1980
2047
  toolCalls: toolCalls.length ? toolCalls : undefined,
1981
2048
  };
@@ -2039,6 +2106,7 @@ async function runComputeDeck(ctx) {
2039
2106
  initialUserMessage: ctx.initialUserMessage,
2040
2107
  parentActionCallId: ctx.parentActionCallId,
2041
2108
  modelProvider: ctx.modelProvider,
2109
+ modelResolver: ctx.modelResolver,
2042
2110
  input: ctx.input,
2043
2111
  defaultModel: ctx.defaultModel,
2044
2112
  modelOverride: ctx.modelOverride,
@@ -2795,54 +2863,11 @@ async function runLlmDeckInWorker(ctx) {
2795
2863
  ctx.onStreamText?.(msg.chunk);
2796
2864
  return;
2797
2865
  }
2798
- if (msg.type === "model.chat.request") {
2799
- (async () => {
2800
- const controller = new AbortController();
2801
- modelRequestControllers.set(msg.requestId, controller);
2802
- try {
2803
- const result = await ctx.modelProvider.chat({
2804
- ...msg.input,
2805
- signal: mergeAbortSignals(ctx.signal, controller.signal),
2806
- onStreamText: (chunk) => {
2807
- worker.postMessage({
2808
- type: "model.chat.stream",
2809
- requestId: msg.requestId,
2810
- chunk,
2811
- });
2812
- },
2813
- });
2814
- worker.postMessage({
2815
- type: "model.chat.result",
2816
- requestId: msg.requestId,
2817
- result,
2818
- });
2819
- }
2820
- catch (err) {
2821
- worker.postMessage({
2822
- type: "model.chat.error",
2823
- requestId: msg.requestId,
2824
- error: {
2825
- source: "model",
2826
- name: err instanceof Error ? err.name : undefined,
2827
- message: err instanceof Error ? err.message : String(err),
2828
- code: err?.code,
2829
- },
2830
- });
2831
- }
2832
- finally {
2833
- modelRequestControllers.delete(msg.requestId);
2834
- }
2835
- })();
2836
- return;
2837
- }
2838
2866
  if (msg.type === "model.responses.request") {
2839
2867
  (async () => {
2840
2868
  const controller = new AbortController();
2841
2869
  modelRequestControllers.set(msg.requestId, controller);
2842
2870
  try {
2843
- if (!ctx.modelProvider.responses) {
2844
- throw new Error("Responses API unavailable for current model provider");
2845
- }
2846
2871
  const result = await ctx.modelProvider.responses({
2847
2872
  ...msg.input,
2848
2873
  signal: mergeAbortSignals(ctx.signal, controller.signal),
@@ -2885,8 +2910,8 @@ async function runLlmDeckInWorker(ctx) {
2885
2910
  if (msg.type === "model.resolveModel.request") {
2886
2911
  (async () => {
2887
2912
  try {
2888
- const result = ctx.modelProvider.resolveModel
2889
- ? await ctx.modelProvider.resolveModel(msg.input)
2913
+ const result = ctx.modelResolver?.resolveModel
2914
+ ? await ctx.modelResolver.resolveModel(msg.input)
2890
2915
  : {
2891
2916
  model: Array.isArray(msg.input.model)
2892
2917
  ? msg.input.model[0]
@@ -3098,6 +3123,7 @@ async function runComputeDeckInWorker(ctx) {
3098
3123
  path: req.payload.path,
3099
3124
  input: req.payload.input,
3100
3125
  modelProvider: ctx.modelProvider,
3126
+ modelResolver: ctx.modelResolver,
3101
3127
  isRoot: false,
3102
3128
  guardrails: ctx.guardrails,
3103
3129
  depth: ctx.depth + 1,
@@ -3378,6 +3404,7 @@ async function runComputeDeckInProcess(ctx) {
3378
3404
  path: childPath,
3379
3405
  input: opts.input,
3380
3406
  modelProvider: ctx.modelProvider,
3407
+ modelResolver: ctx.modelResolver,
3381
3408
  isRoot: false,
3382
3409
  guardrails: ctx.guardrails,
3383
3410
  depth: ctx.depth + 1,
@@ -3441,8 +3468,7 @@ async function runLlmDeck(ctx) {
3441
3468
  const { deck, guardrails, depth, modelProvider, input, runId, inputProvided, initialUserMessage, } = ctx;
3442
3469
  const actionCallId = randomId("action");
3443
3470
  const start = performance.now();
3444
- const useResponses = Boolean(ctx.responsesMode) ||
3445
- ctx.state?.format === "responses";
3471
+ const useResponses = true;
3446
3472
  const validateResponseItemEmission = createResponseItemEmissionValidator(deck);
3447
3473
  const intermediateEmitter = ctx.parentActionCallId !== undefined
3448
3474
  ? createIntermediateChildResponseEmitter({
@@ -3597,6 +3623,7 @@ async function runLlmDeck(ctx) {
3597
3623
  model: modelCandidate,
3598
3624
  params: toProviderParams(deck.modelParams),
3599
3625
  modelProvider,
3626
+ modelResolver: ctx.modelResolver,
3600
3627
  deckPath: deck.path,
3601
3628
  });
3602
3629
  const model = resolved.model;
@@ -3625,144 +3652,93 @@ async function runLlmDeck(ctx) {
3625
3652
  parentActionCallId: ctx.parentActionCallId,
3626
3653
  });
3627
3654
  let responseOutputItems;
3628
- const responses = modelProvider.responses;
3629
3655
  const projectedToolCalls = new Set();
3630
3656
  const projectedToolResults = new Set();
3631
3657
  const projectedToolNames = new Map();
3632
3658
  const canonicalizeStreamEvent = createCanonicalStreamEventController();
3633
- const result = (useResponses && responses)
3634
- ? await (async () => {
3635
- const responseItems = responseItemsFromMessages(messages);
3636
- let sawDelta = false;
3637
- const response = await responses({
3638
- request: {
3639
- model,
3640
- input: responseItems,
3641
- tools: providerResponseTools,
3642
- text: responseTextConfig,
3643
- stream: ctx.stream,
3644
- params: providerParams,
3645
- },
3646
- state: ctx.state,
3647
- deckPath: deck.path,
3648
- signal: ctx.signal,
3649
- onStreamEvent: (ctx.trace || ctx.onStreamText || deck.handlers?.onIdle)
3650
- ? (event) => {
3651
- const normalizedEvent = validateResponseEventItems(event, validateResponseItemEmission);
3652
- const streamEvent = canonicalizeStreamEvent(normalizedEvent);
3653
- if (!streamEvent)
3654
- return;
3655
- if (ctx.trace) {
3656
- const handledAsResponse = traceOpenResponsesStreamEvent({
3657
- streamEvent,
3658
- runId,
3659
- actionCallId,
3660
- deckPath: deck.path,
3661
- model,
3662
- parentActionCallId: ctx.parentActionCallId,
3663
- trace: ctx.trace,
3664
- });
3665
- if (!handledAsResponse) {
3666
- ctx.trace({
3667
- type: "model.stream.event",
3668
- runId,
3669
- actionCallId,
3670
- deckPath: deck.path,
3671
- model,
3672
- event: streamEvent,
3673
- parentActionCallId: ctx.parentActionCallId,
3674
- });
3675
- }
3676
- projectStreamToolTraceEvents({
3677
- streamEvent,
3678
- runId,
3679
- parentActionCallId: actionCallId,
3680
- trace: ctx.trace,
3681
- emittedCalls: projectedToolCalls,
3682
- emittedResults: projectedToolResults,
3683
- toolNames: projectedToolNames,
3684
- });
3685
- }
3686
- const eventType = typeof streamEvent.type === "string"
3687
- ? streamEvent.type
3688
- : "";
3689
- if (eventType === "response.output_text.delta") {
3690
- sawDelta = true;
3691
- const delta = typeof streamEvent.delta === "string"
3692
- ? streamEvent.delta
3693
- : "";
3694
- if (delta)
3695
- wrappedOnStreamText(delta);
3696
- }
3697
- else if (eventType === "response.output_text.done" && !sawDelta) {
3698
- const text = typeof streamEvent.text === "string"
3699
- ? streamEvent.text
3700
- : "";
3701
- if (text)
3702
- wrappedOnStreamText(text);
3703
- }
3704
- }
3705
- : undefined,
3706
- });
3707
- responseOutputItems = validateResponseOutputItems(response.output ?? [], validateResponseItemEmission, "response.output");
3708
- const mapped = mapResponseOutput(responseOutputItems);
3709
- return {
3710
- message: mapped.message,
3711
- finishReason: mapped.toolCalls?.length ? "tool_calls" : "stop",
3712
- toolCalls: mapped.toolCalls,
3713
- usage: response.usage,
3714
- updatedState: response.updatedState,
3715
- };
3716
- })()
3717
- : await modelProvider.chat({
3718
- model,
3719
- messages,
3720
- tools,
3721
- stream: ctx.stream,
3659
+ const result = await (async () => {
3660
+ const responseItems = responseItemsFromMessages(messages);
3661
+ let sawDelta = false;
3662
+ const response = await modelProvider.responses({
3663
+ request: {
3664
+ model,
3665
+ input: responseItems,
3666
+ tools: providerResponseTools,
3667
+ text: responseTextConfig,
3668
+ stream: ctx.stream,
3669
+ params: providerParams,
3670
+ },
3722
3671
  state: ctx.state,
3723
3672
  deckPath: deck.path,
3724
3673
  signal: ctx.signal,
3725
- params: providerParams,
3726
- onStreamText: (ctx.onStreamText || deck.handlers?.onIdle)
3727
- ? wrappedOnStreamText
3728
- : undefined,
3729
- onStreamEvent: ctx.trace
3674
+ onStreamEvent: (ctx.trace || ctx.onStreamText || deck.handlers?.onIdle)
3730
3675
  ? (event) => {
3731
- const streamEvent = canonicalizeStreamEvent(event);
3676
+ const normalizedEvent = validateResponseEventItems(event, validateResponseItemEmission);
3677
+ const streamEvent = canonicalizeStreamEvent(normalizedEvent);
3732
3678
  if (!streamEvent)
3733
3679
  return;
3734
- const handledAsResponse = traceOpenResponsesStreamEvent({
3735
- streamEvent,
3736
- runId,
3737
- actionCallId,
3738
- deckPath: deck.path,
3739
- model,
3740
- parentActionCallId: ctx.parentActionCallId,
3741
- trace: ctx.trace,
3742
- });
3743
- if (!handledAsResponse) {
3744
- ctx.trace?.({
3745
- type: "model.stream.event",
3680
+ if (ctx.trace) {
3681
+ const handledAsResponse = traceOpenResponsesStreamEvent({
3682
+ streamEvent,
3746
3683
  runId,
3747
3684
  actionCallId,
3748
3685
  deckPath: deck.path,
3749
3686
  model,
3750
- event: streamEvent,
3751
3687
  parentActionCallId: ctx.parentActionCallId,
3688
+ trace: ctx.trace,
3689
+ });
3690
+ if (!handledAsResponse) {
3691
+ ctx.trace({
3692
+ type: "model.stream.event",
3693
+ runId,
3694
+ actionCallId,
3695
+ deckPath: deck.path,
3696
+ model,
3697
+ event: streamEvent,
3698
+ parentActionCallId: ctx.parentActionCallId,
3699
+ });
3700
+ }
3701
+ projectStreamToolTraceEvents({
3702
+ streamEvent,
3703
+ runId,
3704
+ parentActionCallId: actionCallId,
3705
+ trace: ctx.trace,
3706
+ emittedCalls: projectedToolCalls,
3707
+ emittedResults: projectedToolResults,
3708
+ toolNames: projectedToolNames,
3752
3709
  });
3753
3710
  }
3754
- projectStreamToolTraceEvents({
3755
- streamEvent,
3756
- runId,
3757
- parentActionCallId: actionCallId,
3758
- trace: ctx.trace,
3759
- emittedCalls: projectedToolCalls,
3760
- emittedResults: projectedToolResults,
3761
- toolNames: projectedToolNames,
3762
- });
3711
+ const eventType = typeof streamEvent.type === "string"
3712
+ ? streamEvent.type
3713
+ : "";
3714
+ if (eventType === "response.output_text.delta") {
3715
+ sawDelta = true;
3716
+ const delta = typeof streamEvent.delta === "string"
3717
+ ? streamEvent.delta
3718
+ : "";
3719
+ if (delta)
3720
+ wrappedOnStreamText(delta);
3721
+ }
3722
+ else if (eventType === "response.output_text.done" && !sawDelta) {
3723
+ const text = typeof streamEvent.text === "string"
3724
+ ? streamEvent.text
3725
+ : "";
3726
+ if (text)
3727
+ wrappedOnStreamText(text);
3728
+ }
3763
3729
  }
3764
3730
  : undefined,
3765
3731
  });
3732
+ responseOutputItems = validateResponseOutputItems(response.output ?? [], validateResponseItemEmission, "response.output");
3733
+ const mapped = mapResponseOutput(responseOutputItems);
3734
+ return {
3735
+ message: mapped.message,
3736
+ finishReason: mapped.toolCalls?.length ? "tool_calls" : "stop",
3737
+ toolCalls: mapped.toolCalls,
3738
+ usage: response.usage,
3739
+ updatedState: response.updatedState,
3740
+ };
3741
+ })();
3766
3742
  idleController.touch();
3767
3743
  let message = result.message;
3768
3744
  ctx.trace?.({
@@ -4665,6 +4641,7 @@ async function handleToolCall(call, ctx) {
4665
4641
  path: action.path,
4666
4642
  input: actionInput,
4667
4643
  modelProvider: ctx.modelProvider,
4644
+ modelResolver: ctx.modelResolver,
4668
4645
  isRoot: false,
4669
4646
  guardrails: ctx.guardrails,
4670
4647
  depth: ctx.depth + 1,
@@ -4764,6 +4741,7 @@ async function handleToolCall(call, ctx) {
4764
4741
  path: action.path,
4765
4742
  input: actionInput,
4766
4743
  modelProvider: ctx.modelProvider,
4744
+ modelResolver: ctx.modelResolver,
4767
4745
  isRoot: false,
4768
4746
  guardrails: ctx.guardrails,
4769
4747
  depth: ctx.depth + 1,
@@ -4813,6 +4791,7 @@ async function handleToolCall(call, ctx) {
4813
4791
  parentActionCallId: ctx.parentActionCallId,
4814
4792
  handlerPath: busyCfg.path,
4815
4793
  modelProvider: ctx.modelProvider,
4794
+ modelResolver: ctx.modelResolver,
4816
4795
  guardrails: ctx.guardrails,
4817
4796
  depth: ctx.depth,
4818
4797
  defaultModel: ctx.defaultModel,
@@ -4914,6 +4893,7 @@ async function handleToolCall(call, ctx) {
4914
4893
  parentActionCallId: ctx.parentActionCallId,
4915
4894
  handlerPath: busyCfg.path,
4916
4895
  modelProvider: ctx.modelProvider,
4896
+ modelResolver: ctx.modelResolver,
4917
4897
  guardrails: ctx.guardrails,
4918
4898
  depth: ctx.depth,
4919
4899
  defaultModel: ctx.defaultModel,
@@ -5003,6 +4983,7 @@ async function runBusyHandler(args) {
5003
4983
  path: args.handlerPath,
5004
4984
  input,
5005
4985
  modelProvider: args.modelProvider,
4986
+ modelResolver: args.modelResolver,
5006
4987
  isRoot: false,
5007
4988
  guardrails: args.guardrails,
5008
4989
  depth: args.depth + 1,
@@ -5096,6 +5077,7 @@ function createIdleController(args) {
5096
5077
  runId: args.runId,
5097
5078
  parentActionCallId: args.parentActionCallId,
5098
5079
  modelProvider: args.modelProvider,
5080
+ modelResolver: args.modelResolver,
5099
5081
  guardrails: args.guardrails,
5100
5082
  depth: args.depth,
5101
5083
  defaultModel: args.defaultModel,
@@ -5170,6 +5152,7 @@ async function runIdleHandler(args) {
5170
5152
  path: args.handlerPath,
5171
5153
  input,
5172
5154
  modelProvider: args.modelProvider,
5155
+ modelResolver: args.modelResolver,
5173
5156
  isRoot: false,
5174
5157
  guardrails: args.guardrails,
5175
5158
  depth: args.depth + 1,
@@ -5249,6 +5232,7 @@ async function maybeHandleError(args) {
5249
5232
  path: handlerPath,
5250
5233
  input: envelopeInput,
5251
5234
  modelProvider: args.ctx.modelProvider,
5235
+ modelResolver: args.ctx.modelResolver,
5252
5236
  isRoot: false,
5253
5237
  guardrails: args.ctx.guardrails,
5254
5238
  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"}
package/esm/src/state.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as dntShim from "../_dnt.shims.js";
2
2
  import * as path from "../deps/jsr.io/@std/path/1.1.4/mod.js";
3
+ import { joinTextParts } from "./text.js";
3
4
  function deriveMessagesFromItems(items) {
4
5
  const messages = [];
5
6
  const callNameById = new Map();
@@ -7,10 +8,10 @@ function deriveMessagesFromItems(items) {
7
8
  if (item.type === "message") {
8
9
  const text = item.content
9
10
  .map((part) => part.text)
10
- .join("");
11
+ .filter(Boolean);
11
12
  messages.push({
12
13
  role: item.role,
13
- content: text || null,
14
+ content: text.length ? joinTextParts(text) : null,
14
15
  });
15
16
  continue;
16
17
  }
@@ -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,3 @@
1
+ export function joinTextParts(parts) {
2
+ return parts.filter(Boolean).join("");
3
+ }
@@ -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";