@theokit/agents 0.5.0 → 0.6.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/bridge.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ExecutionContext } from '@theokit/http';
2
- import { A as AgentOptions, T as ToolOptions, e as MainLoopMeta, a as ApprovalOptions, B as BudgetOptions, d as GatewayOptions, j as MemoryOptions, q as SkillsOptions, c as ContextWindowOptions, o as ProjectContextOptions, h as McpServersMap } from './skills-DnfvEUSg.js';
2
+ import { A as AgentOptions, T as ToolOptions, M as MainLoopMeta, a as ApprovalOptions, B as BudgetOptions, d as GatewayOptions, j as MemoryOptions, q as SkillsOptions, c as ContextWindowOptions, o as ProjectContextOptions, h as McpServersMap } from './skills-DmN1HGUb.js';
3
3
  import { SkillsSettings, ContextSettings, SystemPromptResolver } from '@theokit/sdk';
4
4
  import 'zod';
5
5
 
@@ -407,25 +407,14 @@ interface SdkMessage {
407
407
  declare function translateSdkEvent(msg: SdkMessage, runId: string): StreamEvent[];
408
408
 
409
409
  /**
410
- * Multi-agent orchestration runtime.
410
+ * Shared delegation value types + typed errors.
411
411
  *
412
- * Provides `delegate()` a function that lets a parent agent invoke a
413
- * sub-agent and receive its result. Handles budget clamping (D4),
414
- * tool sharing, and toolbox auto-instantiation (EC-1).
412
+ * Extracted from `agent-orchestrator.ts` so BOTH the orchestrator (`delegate`)
413
+ * and the loop driver (`loop/run-reflective-loop.ts`) can import them WITHOUT a
414
+ * cycle (orchestrator loop → delegation-types; orchestrator → delegation-types;
415
+ * delegation-types depends on nothing — Acyclic Dependencies Principle, G1).
416
+ * `agent-orchestrator.ts` re-exports these for backward compatibility.
415
417
  */
416
-
417
- interface DelegateOptions {
418
- /** Max USD for this sub-agent call. */
419
- budget?: number;
420
- /** Parent's remaining budget (for clamping). */
421
- parentBudgetRemaining?: number;
422
- /** Parent's tools (for sharing — sub-agent inherits these). */
423
- parentTools?: CompiledTool[];
424
- /** LLM API key (inherited from parent). */
425
- apiKey?: string;
426
- /** Session ID override (default: crypto.randomUUID for isolation). */
427
- sessionId?: string;
428
- }
429
418
  interface DelegationResult {
430
419
  response: string;
431
420
  toolCalls: {
@@ -435,6 +424,8 @@ interface DelegationResult {
435
424
  }[];
436
425
  cost: number;
437
426
  tokens: number;
427
+ /** Rounds the reflective loop ran (set by `runReflectiveLoop`; absent for the single-shot path). */
428
+ rounds?: number;
438
429
  }
439
430
  declare class BudgetExceededError extends Error {
440
431
  readonly agentName: string;
@@ -447,6 +438,21 @@ declare class DelegationError extends Error {
447
438
  readonly cause: unknown;
448
439
  constructor(agentName: string, cause: unknown);
449
440
  }
441
+
442
+ interface DelegateOptions {
443
+ /** Max USD for this sub-agent call. */
444
+ budget?: number;
445
+ /** Parent's remaining budget (for clamping). */
446
+ parentBudgetRemaining?: number;
447
+ /** Parent's tools (for sharing — sub-agent inherits these). */
448
+ parentTools?: CompiledTool[];
449
+ /** LLM API key (inherited from parent). */
450
+ apiKey?: string;
451
+ /** Session ID override (default: crypto.randomUUID for isolation). */
452
+ sessionId?: string;
453
+ /** Cancellation — aborts stop the reflective loop from re-entering. */
454
+ signal?: AbortSignal;
455
+ }
450
456
  /**
451
457
  * Delegate a task to a sub-agent and collect its result.
452
458
  *
@@ -454,6 +460,10 @@ declare class DelegationError extends Error {
454
460
  * - Tool sharing: parent tools merged with sub-agent tools (sub wins on collision)
455
461
  * - Toolbox auto-instantiation: sub-agent toolboxes instantiated without DI (EC-1)
456
462
  * - Session isolation: each delegation gets a unique session ID (EC-4)
463
+ * - `@MainLoop` strategy runtime: routes through `runReflectiveLoop` (the same loop
464
+ * `AgentRunner.run` uses — one runtime for both on-ramps, ADR D4). The runtime
465
+ * metric (`THEO_AGENT_MAINLOOP_RUNTIME_APPLIED`) + typed-error + cumulative budget
466
+ * all live in the shared driver, so they fire identically on both paths.
457
467
  */
458
468
  declare function delegate(SubAgentClass: Function, message: string, opts?: DelegateOptions): Promise<DelegationResult>;
459
469
 
package/dist/bridge.js CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  translateSdkEvent,
26
26
  validateUniqueRoutes,
27
27
  walkAgentMetadata
28
- } from "./chunk-2MI27XCA.js";
28
+ } from "./chunk-KTYBQ7HY.js";
29
29
  import "./chunk-MVEY7HEY.js";
30
30
  import "./chunk-7QVYU63E.js";
31
31
  export {
@@ -477,7 +477,8 @@ function translateSystemEvent(msg, runId) {
477
477
  __name(translateSystemEvent, "translateSystemEvent");
478
478
  function translateAssistantEvent(msg) {
479
479
  const events = [];
480
- const content = msg.content;
480
+ const message = msg.message;
481
+ const content = message?.content;
481
482
  if (!Array.isArray(content)) return events;
482
483
  for (const block of content) {
483
484
  const b = block;
@@ -501,12 +502,14 @@ function translateAssistantEvent(msg) {
501
502
  __name(translateAssistantEvent, "translateAssistantEvent");
502
503
  function translateToolCallEvent(msg) {
503
504
  const status = msg.status;
505
+ const callId = asString(msg.call_id, `tc-${Date.now()}`);
506
+ const toolName = asString(msg.name, "unknown");
504
507
  if (status === "completed") {
505
508
  return [
506
509
  {
507
510
  type: "tool_result",
508
- callId: asString(msg.id, `tc-${Date.now()}`),
509
- toolName: asString(msg.name, "unknown"),
511
+ callId,
512
+ toolName,
510
513
  output: asString(msg.result, ""),
511
514
  durationMs: 0,
512
515
  isError: false
@@ -517,9 +520,9 @@ function translateToolCallEvent(msg) {
517
520
  return [
518
521
  {
519
522
  type: "tool_result",
520
- callId: asString(msg.id, `tc-${Date.now()}`),
521
- toolName: asString(msg.name, "unknown"),
522
- output: asString(msg.error, "Tool failed"),
523
+ callId,
524
+ toolName,
525
+ output: asString(msg.result, "Tool failed"),
523
526
  durationMs: 0,
524
527
  isError: true
525
528
  }
@@ -530,7 +533,7 @@ function translateToolCallEvent(msg) {
530
533
  __name(translateToolCallEvent, "translateToolCallEvent");
531
534
  function translateStatusEvent(msg) {
532
535
  const s = msg.status;
533
- if (s === "done" || s === "completed") {
536
+ if (s === "FINISHED" || s === "CANCELLED") {
534
537
  return [
535
538
  {
536
539
  type: "done",
@@ -545,12 +548,12 @@ function translateStatusEvent(msg) {
545
548
  }
546
549
  ];
547
550
  }
548
- if (s === "error") {
551
+ if (s === "ERROR" || s === "EXPIRED") {
549
552
  return [
550
553
  {
551
554
  type: "error",
552
555
  code: "AGENT_ERROR",
553
- message: asString(msg.error, "Agent error"),
556
+ message: asString(msg.message, "Agent error"),
554
557
  retryable: false
555
558
  }
556
559
  ];
@@ -570,7 +573,7 @@ function translateSdkEvent(msg, runId) {
570
573
  return [
571
574
  {
572
575
  type: "thinking",
573
- content: asString(msg.content, "")
576
+ content: asString(msg.text, "")
574
577
  }
575
578
  ];
576
579
  case "status":
@@ -656,23 +659,27 @@ function createSdkAgentStream(compiled, compiledTools, apiKey, envModel) {
656
659
  ...m8
657
660
  });
658
661
  const run = await agent.send(message);
662
+ let sawTerminal = false;
659
663
  for await (const sdkEvent of run.stream()) {
660
664
  const translated = translateSdkEvent(sdkEvent, runId);
661
665
  for (const event of translated) {
666
+ if (event.type === "done" || event.type === "error") sawTerminal = true;
662
667
  yield event;
663
668
  }
664
669
  }
665
- yield {
666
- type: "done",
667
- result: "",
668
- usage: {
669
- inputTokens: 0,
670
- outputTokens: 0,
671
- totalTokens: 0
672
- },
673
- durationMs: Date.now() - t0,
674
- cost: 0
675
- };
670
+ if (!sawTerminal) {
671
+ yield {
672
+ type: "done",
673
+ result: "",
674
+ usage: {
675
+ inputTokens: 0,
676
+ outputTokens: 0,
677
+ totalTokens: 0
678
+ },
679
+ durationMs: Date.now() - t0,
680
+ cost: 0
681
+ };
682
+ }
676
683
  await agent.dispose();
677
684
  } catch (err) {
678
685
  yield {
@@ -687,7 +694,66 @@ function createSdkAgentStream(compiled, compiledTools, apiKey, envModel) {
687
694
  }
688
695
  __name(createSdkAgentStream, "createSdkAgentStream");
689
696
 
690
- // src/bridge/agent-orchestrator.ts
697
+ // src/loop/loop-strategy.ts
698
+ import { z } from "zod";
699
+ var DEFAULT_MAX_ITERATIONS = 8;
700
+ var loopStrategyConfigSchema = z.object({
701
+ name: z.enum([
702
+ "simple-chat",
703
+ "plan-act-reflect",
704
+ "react"
705
+ ]),
706
+ maxIterations: z.number().int().min(1)
707
+ });
708
+ function resolveLoopStrategy(strategy, maxIterations = DEFAULT_MAX_ITERATIONS) {
709
+ const cfg = loopStrategyConfigSchema.parse({
710
+ name: strategy,
711
+ maxIterations
712
+ });
713
+ if (cfg.name === "simple-chat") {
714
+ return {
715
+ name: cfg.name,
716
+ maxIterations: cfg.maxIterations,
717
+ shouldContinue: /* @__PURE__ */ __name(() => false, "shouldContinue")
718
+ };
719
+ }
720
+ return {
721
+ name: cfg.name,
722
+ maxIterations: cfg.maxIterations,
723
+ shouldContinue: /* @__PURE__ */ __name((outcome) => outcome.finishReason === "tool-calls" && outcome.round < cfg.maxIterations, "shouldContinue")
724
+ };
725
+ }
726
+ __name(resolveLoopStrategy, "resolveLoopStrategy");
727
+
728
+ // src/loop/reflection-strategy.ts
729
+ import { z as z2 } from "zod";
730
+ var reflectionStrategyConfigSchema = z2.object({
731
+ name: z2.string().min(1)
732
+ });
733
+ var ladderReflectionStrategy = {
734
+ name: "ladder",
735
+ reflect(outcome) {
736
+ if (outcome.finishReason === "tool-calls") {
737
+ return {
738
+ feedback: `Tool results received (round ${outcome.round}). Reflect on whether the goal is met; if not, refine the approach and continue.`,
739
+ continue: true
740
+ };
741
+ }
742
+ return {
743
+ continue: false
744
+ };
745
+ }
746
+ };
747
+ var noopReflectionStrategy = {
748
+ name: "noop",
749
+ reflect() {
750
+ return {
751
+ continue: true
752
+ };
753
+ }
754
+ };
755
+
756
+ // src/bridge/delegation-types.ts
691
757
  var BudgetExceededError = class extends Error {
692
758
  static {
693
759
  __name(this, "BudgetExceededError");
@@ -711,16 +777,185 @@ var DelegationError = class extends Error {
711
777
  this.name = "DelegationError";
712
778
  }
713
779
  };
780
+
781
+ // src/loop/run-reflective-loop.ts
714
782
  function asString2(value, fallback) {
715
- if (typeof value === "string") return value;
716
- return fallback;
783
+ return typeof value === "string" ? value : fallback;
717
784
  }
718
785
  __name(asString2, "asString");
719
786
  function asNumber(value, fallback) {
720
- if (typeof value === "number") return value;
721
- return fallback;
787
+ return typeof value === "number" ? value : fallback;
722
788
  }
723
789
  __name(asNumber, "asNumber");
790
+ function deriveFinishReason(signals) {
791
+ if (signals.sawError) return "error";
792
+ if (signals.sawDone && signals.doneFinishReason === "tool-calls") return "tool-calls";
793
+ if (signals.sawToolResult) return "tool-calls";
794
+ return "stop";
795
+ }
796
+ __name(deriveFinishReason, "deriveFinishReason");
797
+ async function consumeOneRound(factory, prompt, sessionId, signal) {
798
+ const r = {
799
+ responseText: "",
800
+ toolCalls: [],
801
+ cost: 0,
802
+ tokens: 0,
803
+ finishReason: "stop",
804
+ errorMessage: ""
805
+ };
806
+ const signals = {
807
+ sawError: false,
808
+ sawDone: false,
809
+ doneFinishReason: "",
810
+ sawToolResult: false
811
+ };
812
+ for await (const event of factory(prompt, sessionId)) {
813
+ if (signal?.aborted) break;
814
+ if (event.type === "text_delta" && typeof event.content === "string") {
815
+ r.responseText += event.content;
816
+ } else if (event.type === "tool_result") {
817
+ signals.sawToolResult = true;
818
+ r.toolCalls.push({
819
+ name: asString2(event.toolName, "unknown"),
820
+ input: event.input ?? {},
821
+ output: asString2(event.output, "")
822
+ });
823
+ } else if (event.type === "done") {
824
+ signals.sawDone = true;
825
+ signals.doneFinishReason = asString2(event.finishReason, "");
826
+ r.cost = asNumber(event.cost, 0);
827
+ r.tokens = event.usage?.totalTokens ?? 0;
828
+ } else if (event.type === "error") {
829
+ signals.sawError = true;
830
+ r.errorMessage = asString2(event.message, "Unknown agent error");
831
+ }
832
+ }
833
+ r.finishReason = deriveFinishReason(signals);
834
+ return r;
835
+ }
836
+ __name(consumeOneRound, "consumeOneRound");
837
+ async function runReflectiveLoop(factory, message, sessionId, config) {
838
+ const { loop, reflection, budget = Number.POSITIVE_INFINITY, signal, agentName = loop.name } = config;
839
+ const acc = {
840
+ response: "",
841
+ toolCalls: [],
842
+ cost: 0,
843
+ tokens: 0,
844
+ rounds: 0
845
+ };
846
+ let round = 1;
847
+ let feedback;
848
+ while (!signal?.aborted) {
849
+ const prompt = round === 1 || !feedback ? message : `${message}
850
+
851
+ [reflection] ${feedback}`;
852
+ let r;
853
+ try {
854
+ r = await consumeOneRound(factory, prompt, sessionId, signal);
855
+ } catch (err) {
856
+ if (err instanceof BudgetExceededError || err instanceof DelegationError) throw err;
857
+ throw new DelegationError(agentName, err);
858
+ }
859
+ acc.response += r.responseText;
860
+ acc.toolCalls.push(...r.toolCalls);
861
+ acc.cost += r.cost;
862
+ acc.tokens += r.tokens;
863
+ if (r.finishReason === "error") throw new DelegationError(agentName, r.errorMessage);
864
+ if (Number.isFinite(budget) && acc.cost > budget) {
865
+ throw new BudgetExceededError(agentName, acc.cost, budget);
866
+ }
867
+ const outcome = {
868
+ finishReason: r.finishReason,
869
+ round,
870
+ toolCalls: r.toolCalls,
871
+ responseText: r.responseText
872
+ };
873
+ const reflectionResult = reflection.reflect(outcome);
874
+ if (!(reflectionResult.continue && loop.shouldContinue(outcome))) {
875
+ acc.rounds = round;
876
+ console.debug("[THEO_AGENT_MAINLOOP_RUNTIME_APPLIED]", {
877
+ strategy: loop.name,
878
+ rounds: round
879
+ });
880
+ return acc;
881
+ }
882
+ feedback = reflectionResult.feedback;
883
+ round += 1;
884
+ }
885
+ acc.rounds = round - 1;
886
+ return acc;
887
+ }
888
+ __name(runReflectiveLoop, "runReflectiveLoop");
889
+
890
+ // src/loop/agent-runner.ts
891
+ var AgentRunner = class {
892
+ static {
893
+ __name(this, "AgentRunner");
894
+ }
895
+ compiled;
896
+ agentName;
897
+ loopStrategy;
898
+ reflectionStrategy;
899
+ streamEnabled;
900
+ constructor(compiled, agentName, loopStrategy, reflectionStrategy, streamEnabled) {
901
+ this.compiled = compiled;
902
+ this.agentName = agentName;
903
+ this.loopStrategy = loopStrategy;
904
+ this.reflectionStrategy = reflectionStrategy;
905
+ this.streamEnabled = streamEnabled;
906
+ }
907
+ /** Start a fluent builder for `AgentClass`. */
908
+ static builder(AgentClass) {
909
+ return new AgentRunnerBuilder(AgentClass);
910
+ }
911
+ /** Run the agent to a terminal result via the shared reflective loop. */
912
+ run(message, opts) {
913
+ const streamFactory = createSdkAgentStream(this.compiled, this.compiled.tools, opts.apiKey, this.compiled.model);
914
+ const sessionId = opts.sessionId ?? `runner-${crypto.randomUUID()}`;
915
+ return runReflectiveLoop(streamFactory, message, sessionId, {
916
+ loop: this.loopStrategy,
917
+ reflection: this.reflectionStrategy,
918
+ budget: opts.budget,
919
+ agentName: this.agentName,
920
+ signal: opts.signal
921
+ });
922
+ }
923
+ };
924
+ var AgentRunnerBuilder = class {
925
+ static {
926
+ __name(this, "AgentRunnerBuilder");
927
+ }
928
+ AgentClass;
929
+ reflectionOverride;
930
+ streamEnabled = true;
931
+ constructor(AgentClass) {
932
+ this.AgentClass = AgentClass;
933
+ }
934
+ /** Override the default reflection strategy (OCP — plan Drawback #2). No arg ⇒ keep default. */
935
+ reflection(strategy) {
936
+ if (strategy) this.reflectionOverride = strategy;
937
+ return this;
938
+ }
939
+ /** Record the streaming preference (see {@link AgentRunner.streamEnabled}). */
940
+ stream(enabled = true) {
941
+ this.streamEnabled = enabled;
942
+ return this;
943
+ }
944
+ /** Walk + compile + resolve strategies — the compile→execute boundary (no I/O). */
945
+ build() {
946
+ const walk = walkAgentMetadata(this.AgentClass, []);
947
+ const toolboxInstances = new Map(walk.toolboxes.map((tb) => [
948
+ tb.class,
949
+ new tb.class()
950
+ ]));
951
+ const compiled = compileAgent(walk, toolboxInstances);
952
+ const loopStrategy = resolveLoopStrategy(walk.mainLoop.strategy, walk.mainLoop.maxIterations);
953
+ const reflectionStrategy = this.reflectionOverride ?? (walk.mainLoop.strategy === "plan-act-reflect" ? ladderReflectionStrategy : noopReflectionStrategy);
954
+ return new AgentRunner(compiled, walk.agentConfig.name, loopStrategy, reflectionStrategy, this.streamEnabled);
955
+ }
956
+ };
957
+
958
+ // src/bridge/agent-orchestrator.ts
724
959
  function requireApiKey(opts, agentName) {
725
960
  const apiKey = opts.apiKey ?? "";
726
961
  if (!apiKey) {
@@ -738,33 +973,6 @@ function mergeTools(parentTools, subTools) {
738
973
  ];
739
974
  }
740
975
  __name(mergeTools, "mergeTools");
741
- function processStreamEvent(event, acc, budget, agentName) {
742
- if (event.type === "text_delta" && typeof event.content === "string") {
743
- acc.response += event.content;
744
- return;
745
- }
746
- if (event.type === "tool_result") {
747
- acc.toolCalls.push({
748
- name: asString2(event.toolName, "unknown"),
749
- input: event.input ?? {},
750
- output: asString2(event.output, "")
751
- });
752
- return;
753
- }
754
- if (event.type === "done") {
755
- acc.cost = asNumber(event.cost, 0);
756
- const usage = event.usage;
757
- acc.tokens = usage?.totalTokens ?? 0;
758
- if (Number.isFinite(budget) && acc.cost > budget) {
759
- throw new BudgetExceededError(agentName, acc.cost, budget);
760
- }
761
- return;
762
- }
763
- if (event.type === "error") {
764
- throw new DelegationError(agentName, asString2(event.message, "Unknown agent error"));
765
- }
766
- }
767
- __name(processStreamEvent, "processStreamEvent");
768
976
  async function delegate(SubAgentClass, message, opts = {}) {
769
977
  const apiKey = requireApiKey(opts, SubAgentClass.name);
770
978
  const walk = walkAgentMetadata(SubAgentClass, []);
@@ -777,24 +985,15 @@ async function delegate(SubAgentClass, message, opts = {}) {
777
985
  const budget = Math.min(opts.budget ?? Infinity, opts.parentBudgetRemaining ?? Infinity);
778
986
  const streamFactory = createSdkAgentStream(compiled, allTools, apiKey, walk.agentConfig.model);
779
987
  const sessionId = opts.sessionId ?? `sub-${crypto.randomUUID()}`;
780
- const acc = {
781
- response: "",
782
- toolCalls: [],
783
- cost: 0,
784
- tokens: 0
785
- };
786
- try {
787
- for await (const event of streamFactory(message, sessionId)) {
788
- processStreamEvent(event, acc, budget, SubAgentClass.name);
789
- }
790
- } catch (err) {
791
- if (err instanceof BudgetExceededError || err instanceof DelegationError) throw err;
792
- throw new DelegationError(SubAgentClass.name, err);
793
- }
794
- if (Number.isFinite(budget) && acc.cost > budget) {
795
- throw new BudgetExceededError(SubAgentClass.name, acc.cost, budget);
796
- }
797
- return acc;
988
+ const loopStrategy = resolveLoopStrategy(walk.mainLoop.strategy, walk.mainLoop.maxIterations);
989
+ const reflection = loopStrategy.name === "plan-act-reflect" ? ladderReflectionStrategy : noopReflectionStrategy;
990
+ return runReflectiveLoop(streamFactory, message, sessionId, {
991
+ loop: loopStrategy,
992
+ reflection,
993
+ budget,
994
+ agentName: SubAgentClass.name,
995
+ signal: opts.signal
996
+ });
798
997
  }
799
998
  __name(delegate, "delegate");
800
999
 
@@ -944,10 +1143,18 @@ export {
944
1143
  generateAgentRoutes,
945
1144
  translateSdkEvent,
946
1145
  createSdkAgentStream,
1146
+ DEFAULT_MAX_ITERATIONS,
1147
+ loopStrategyConfigSchema,
1148
+ resolveLoopStrategy,
1149
+ reflectionStrategyConfigSchema,
1150
+ ladderReflectionStrategy,
1151
+ noopReflectionStrategy,
947
1152
  BudgetExceededError,
948
1153
  DelegationError,
1154
+ AgentRunner,
1155
+ AgentRunnerBuilder,
949
1156
  delegate,
950
1157
  generateAgentManifest,
951
1158
  agentsPlugin
952
1159
  };
953
- //# sourceMappingURL=chunk-2MI27XCA.js.map
1160
+ //# sourceMappingURL=chunk-KTYBQ7HY.js.map