@theokit/sdk 2.20.0 → 2.22.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.
Files changed (92) hide show
  1. package/CHANGELOG.md +148 -0
  2. package/dist/a2a/index.cjs +673 -317
  3. package/dist/a2a/index.cjs.map +1 -1
  4. package/dist/a2a/index.d.cts +2 -1
  5. package/dist/a2a/index.d.ts +2 -1
  6. package/dist/a2a/index.js +674 -318
  7. package/dist/a2a/index.js.map +1 -1
  8. package/dist/a2a/subagent.d.cts +70 -1
  9. package/dist/a2a/subagent.d.ts +70 -1
  10. package/dist/a2a/types.d.cts +7 -0
  11. package/dist/a2a/types.d.ts +7 -0
  12. package/dist/agent-builder.d.ts +2 -1
  13. package/dist/agent-generate.d.ts +12 -0
  14. package/dist/client/index.cjs.map +1 -1
  15. package/dist/client/index.d.cts +5 -0
  16. package/dist/client/index.d.ts +5 -0
  17. package/dist/client/index.js.map +1 -1
  18. package/dist/client/theokit-client.d.cts +6 -0
  19. package/dist/client/theokit-client.d.ts +6 -0
  20. package/dist/client/types.d.cts +2 -0
  21. package/dist/client/types.d.ts +2 -0
  22. package/dist/{cron-B8fqui49.d.cts → cron-7CruUd_0.d.ts} +53 -3
  23. package/dist/{cron-D2yLOrk2.d.ts → cron-Bpt_1khA.d.cts} +53 -3
  24. package/dist/cron.cjs +654 -389
  25. package/dist/cron.cjs.map +1 -1
  26. package/dist/cron.d.cts +3 -2
  27. package/dist/cron.d.ts +3 -2
  28. package/dist/cron.js +655 -390
  29. package/dist/cron.js.map +1 -1
  30. package/dist/{errors-5lj1EWgs.d.ts → errors-BuwwkrAk.d.ts} +2 -2
  31. package/dist/{errors-CE-lMBi2.d.cts → errors-CkCaIqVP.d.cts} +2 -2
  32. package/dist/errors.d.cts +3 -2
  33. package/dist/eval.cjs +654 -389
  34. package/dist/eval.cjs.map +1 -1
  35. package/dist/eval.js +654 -389
  36. package/dist/eval.js.map +1 -1
  37. package/dist/generate-object.d.ts +4 -3
  38. package/dist/index.cjs +800 -392
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +259 -17
  41. package/dist/index.d.ts +259 -17
  42. package/dist/index.js +798 -394
  43. package/dist/index.js.map +1 -1
  44. package/dist/internal/agent-loop/loop-types.d.ts +2 -0
  45. package/dist/internal/agent-loop/tool-result-guard.d.ts +1 -0
  46. package/dist/internal/llm/tool-result-content.d.ts +40 -0
  47. package/dist/internal/llm/types.d.ts +6 -1
  48. package/dist/internal/persistence/conversation-storage-fs.d.cts +3 -1
  49. package/dist/internal/persistence/conversation-storage-fs.d.ts +3 -1
  50. package/dist/internal/persistence/conversation-storage-memory.d.cts +3 -1
  51. package/dist/internal/persistence/conversation-storage-memory.d.ts +3 -1
  52. package/dist/internal/persistence/session-meta.d.cts +13 -0
  53. package/dist/internal/persistence/session-meta.d.ts +13 -0
  54. package/dist/internal/runtime/model-selection.d.ts +14 -0
  55. package/dist/internal/structured-output-helpers.d.ts +1 -1
  56. package/dist/permission-engine.d.ts +32 -1
  57. package/dist/{run-BgfBWX-z.d.cts → run-CrIulPF7.d.cts} +282 -19
  58. package/dist/{run-BgfBWX-z.d.ts → run-CrIulPF7.d.ts} +282 -19
  59. package/dist/stream-object.d.ts +2 -1
  60. package/dist/tool-error.d.ts +30 -0
  61. package/dist/types/agent-prims.d.ts +30 -8
  62. package/dist/types/agent.d.ts +13 -2
  63. package/dist/types/content-blocks.d.ts +47 -0
  64. package/dist/types/conversation-storage.d.ts +38 -0
  65. package/dist/types/index.d.ts +1 -0
  66. package/dist/types/messages.d.ts +2 -9
  67. package/dist/types/run-events.d.ts +84 -0
  68. package/dist/types/run.d.ts +115 -1
  69. package/dist/types/session.d.ts +99 -0
  70. package/dist/types/workflow.d.ts +7 -0
  71. package/dist/workflow.cjs +7 -2
  72. package/dist/workflow.cjs.map +1 -1
  73. package/dist/workflow.d.cts +2 -0
  74. package/dist/workflow.d.ts +2 -0
  75. package/dist/workflow.js +7 -2
  76. package/dist/workflow.js.map +1 -1
  77. package/package.json +14 -34
  78. package/dist/internal/observability/context.d.cts +0 -23
  79. package/dist/internal/observability/context.d.ts +0 -23
  80. package/dist/internal/observability/index.cjs +0 -38
  81. package/dist/internal/observability/index.cjs.map +0 -1
  82. package/dist/internal/observability/index.d.cts +0 -8
  83. package/dist/internal/observability/index.d.ts +0 -8
  84. package/dist/internal/observability/index.js +0 -33
  85. package/dist/internal/observability/index.js.map +0 -1
  86. package/dist/internal/plugins/index.cjs +0 -363
  87. package/dist/internal/plugins/index.cjs.map +0 -1
  88. package/dist/internal/plugins/index.js +0 -357
  89. package/dist/internal/plugins/index.js.map +0 -1
  90. package/dist/internal/workflow/index.d.ts +0 -12
  91. package/dist/server/adapter/index.d.cts +0 -8
  92. package/dist/server/adapter/index.d.ts +0 -8
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
+ import { z, toJSONSchema } from 'zod';
2
+ import { createRequire } from 'module';
1
3
  import { createHash, randomUUID, randomBytes } from 'crypto';
2
4
  import { existsSync, rmSync, mkdirSync, renameSync, readFileSync, realpathSync, lstatSync, readlinkSync, readdirSync } from 'fs';
3
5
  import { join, dirname, relative, resolve, sep, isAbsolute } from 'path';
4
- import { readFile, stat, rm, readdir, mkdir, rename, open, unlink, statfs, access, appendFile } from 'fs/promises';
5
- import { z, toJSONSchema } from 'zod';
6
+ import { readFile, stat, rm, readdir, mkdir, appendFile, rename, open, unlink, statfs, access, writeFile } from 'fs/promises';
6
7
  import { AsyncLocalStorage } from 'async_hooks';
7
- import { createRequire } from 'module';
8
8
  import { homedir } from 'os';
9
9
  import { spawn } from 'child_process';
10
10
  import { fileURLToPath } from 'url';
@@ -516,6 +516,256 @@ var init_errors = __esm({
516
516
  };
517
517
  }
518
518
  });
519
+ function toJsonSchema(schema, options = { unrepresentable: "any" }) {
520
+ return toJSONSchema(schema, options);
521
+ }
522
+ var init_to_json_schema = __esm({
523
+ "src/internal/zod/to-json-schema.ts"() {
524
+ }
525
+ });
526
+ function requireZod() {
527
+ if (cachedZ !== void 0) return cachedZ;
528
+ const r = createRequire(import.meta.url);
529
+ let mod;
530
+ try {
531
+ mod = r("zod");
532
+ } catch (cause) {
533
+ throw new ConfigurationError(
534
+ 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
535
+ { code: "zod_not_installed", cause }
536
+ );
537
+ }
538
+ cachedZ = mod.z ?? mod;
539
+ return cachedZ;
540
+ }
541
+ function makeOutputTool(jsonSchema, onCapture) {
542
+ return {
543
+ name: "output",
544
+ description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
545
+ inputSchema: jsonSchema,
546
+ handler: (input) => {
547
+ const out = onCapture(input);
548
+ if (typeof out === "string") return out;
549
+ if (out instanceof Promise) return out;
550
+ return "";
551
+ }
552
+ };
553
+ }
554
+ function buildTransientAgentOptions(params) {
555
+ return {
556
+ model: params.model,
557
+ local: params.local,
558
+ tools: [params.outputTool],
559
+ systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
560
+ ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
561
+ ...params.providers !== void 0 ? { providers: params.providers } : {}
562
+ };
563
+ }
564
+ function extractUsage(result) {
565
+ const u = result.usage;
566
+ if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
567
+ return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
568
+ }
569
+ function buildToolPrompt(prompt) {
570
+ return `${prompt}
571
+
572
+ Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
573
+ }
574
+ function setupStructuredOutput(schema, maxRetries) {
575
+ const z8 = requireZod();
576
+ const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
577
+ return {
578
+ z: z8,
579
+ jsonSchema,
580
+ maxRetries: maxRetries ?? 1,
581
+ initialUsage: { inputTokens: 0, outputTokens: 0 }
582
+ };
583
+ }
584
+ async function disposeAndDeleteTransient(agent, deletor) {
585
+ await agent.dispose();
586
+ try {
587
+ await deletor(agent.agentId);
588
+ } catch {
589
+ }
590
+ }
591
+ var cachedZ;
592
+ var init_structured_output_helpers = __esm({
593
+ "src/internal/structured-output-helpers.ts"() {
594
+ init_errors();
595
+ init_to_json_schema();
596
+ }
597
+ });
598
+
599
+ // src/generate-object.ts
600
+ var generate_object_exports = {};
601
+ __export(generate_object_exports, {
602
+ GenerateObjectError: () => GenerateObjectError,
603
+ generateObjectImpl: () => generateObjectImpl
604
+ });
605
+ function salvagePartial(schema, raw) {
606
+ if (typeof raw !== "object" || raw === null) return raw;
607
+ const shape = schema.shape;
608
+ if (shape === void 0 || typeof shape !== "object") return raw;
609
+ const rawObj = raw;
610
+ const out = {};
611
+ for (const [key2, fieldSchema] of Object.entries(shape)) {
612
+ if (typeof fieldSchema?.safeParse !== "function") continue;
613
+ const parsed = fieldSchema.safeParse(rawObj[key2]);
614
+ if (parsed.success) out[key2] = parsed.data;
615
+ }
616
+ return out;
617
+ }
618
+ async function runReasoningPhase(options, deps) {
619
+ const reasoningOptions = {
620
+ model: options.model,
621
+ local: options.local,
622
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
623
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
624
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
625
+ };
626
+ const reasoningAgent = await deps.create(reasoningOptions);
627
+ try {
628
+ const run = await reasoningAgent.send(options.prompt);
629
+ const result = await run.wait();
630
+ const text = result.result;
631
+ return typeof text === "string" ? text : options.prompt;
632
+ } finally {
633
+ await disposeAndDeleteTransient(reasoningAgent, deps.delete);
634
+ }
635
+ }
636
+ async function generateObjectImpl(options, deps) {
637
+ const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
638
+ options.schema,
639
+ options.maxRetries
640
+ );
641
+ let capturedRaw;
642
+ let lastUsage = initialUsage;
643
+ const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
644
+ class CaptureSentinel extends Error {
645
+ constructor(captured) {
646
+ super("generate-object-capture");
647
+ this.captured = captured;
648
+ }
649
+ captured;
650
+ [sentinel] = true;
651
+ }
652
+ const outputTool = makeOutputTool(jsonSchema, (input) => {
653
+ if (capturedRaw === void 0) {
654
+ capturedRaw = input;
655
+ }
656
+ throw new CaptureSentinel(input);
657
+ });
658
+ const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
659
+ const structuringModel = options.structuringModel ?? options.model;
660
+ const structuringPrompt = reasoningText ?? options.prompt;
661
+ const agentOptions = buildTransientAgentOptions({
662
+ model: structuringModel,
663
+ local: options.local,
664
+ outputTool,
665
+ ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
666
+ ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
667
+ ...options.providers !== void 0 ? { providers: options.providers } : {}
668
+ });
669
+ const agent = await deps.create(agentOptions);
670
+ try {
671
+ const userMessage = buildToolPrompt(structuringPrompt);
672
+ let lastParseError;
673
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
674
+ capturedRaw = void 0;
675
+ const run = await agent.send(userMessage);
676
+ const result = await run.wait();
677
+ lastUsage = extractUsage(result);
678
+ if (capturedRaw === void 0) {
679
+ if (result.status === "error" && result.error !== void 0) {
680
+ throw new GenerateObjectError(
681
+ "no_tool_call",
682
+ `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
683
+ result.error
684
+ );
685
+ }
686
+ if (attempt === maxRetries) {
687
+ throw new GenerateObjectError(
688
+ "no_tool_call",
689
+ "The model returned text instead of calling the `output` tool."
690
+ );
691
+ }
692
+ continue;
693
+ }
694
+ const parsed = options.schema.safeParse(capturedRaw);
695
+ if (parsed.success) {
696
+ return {
697
+ object: parsed.data,
698
+ raw: capturedRaw,
699
+ usage: lastUsage,
700
+ finishReason: "tool_use"
701
+ };
702
+ }
703
+ lastParseError = parsed.error;
704
+ }
705
+ if (options.errorStrategy === "return-raw") {
706
+ return {
707
+ object: capturedRaw,
708
+ raw: capturedRaw,
709
+ usage: lastUsage,
710
+ finishReason: "tool_use"
711
+ };
712
+ }
713
+ if (options.errorStrategy === "return-partial") {
714
+ return {
715
+ object: salvagePartial(options.schema, capturedRaw),
716
+ raw: capturedRaw,
717
+ usage: lastUsage,
718
+ finishReason: "tool_use"
719
+ };
720
+ }
721
+ throw new GenerateObjectError(
722
+ "parse_failed",
723
+ "Schema parse failed after all retries.",
724
+ lastParseError
725
+ );
726
+ } finally {
727
+ await disposeAndDeleteTransient(agent, deps.delete);
728
+ }
729
+ }
730
+ var GenerateObjectError;
731
+ var init_generate_object = __esm({
732
+ "src/generate-object.ts"() {
733
+ init_structured_output_helpers();
734
+ GenerateObjectError = class extends Error {
735
+ name = "GenerateObjectError";
736
+ code;
737
+ cause;
738
+ constructor(code, message, cause) {
739
+ super(message);
740
+ this.code = code;
741
+ if (cause !== void 0) this.cause = cause;
742
+ }
743
+ };
744
+ }
745
+ });
746
+
747
+ // src/internal/runtime/registry/agent-factory-registry.ts
748
+ var agent_factory_registry_exports = {};
749
+ __export(agent_factory_registry_exports, {
750
+ getAgentFacade: () => getAgentFacade,
751
+ setAgentFacade: () => setAgentFacade
752
+ });
753
+ function setAgentFacade(facade) {
754
+ registered = facade;
755
+ }
756
+ function getAgentFacade() {
757
+ if (registered === void 0) {
758
+ throw new Error(
759
+ "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
760
+ );
761
+ }
762
+ return registered;
763
+ }
764
+ var registered;
765
+ var init_agent_factory_registry = __esm({
766
+ "src/internal/runtime/registry/agent-factory-registry.ts"() {
767
+ }
768
+ });
519
769
 
520
770
  // src/internal/persistence/cwd-mutex.ts
521
771
  function withCwdMutex(key2, fn) {
@@ -2786,29 +3036,6 @@ var init_judge_call = __esm({
2786
3036
  }
2787
3037
  });
2788
3038
 
2789
- // src/internal/runtime/registry/agent-factory-registry.ts
2790
- var agent_factory_registry_exports = {};
2791
- __export(agent_factory_registry_exports, {
2792
- getAgentFacade: () => getAgentFacade,
2793
- setAgentFacade: () => setAgentFacade
2794
- });
2795
- function setAgentFacade(facade) {
2796
- registered3 = facade;
2797
- }
2798
- function getAgentFacade() {
2799
- if (registered3 === void 0) {
2800
- throw new Error(
2801
- "internal: Agent facade not registered. The `agent.ts` module must be loaded before internal subsystems (LocalAgent.runUntil/fork, eval, scorers, cron) invoke it."
2802
- );
2803
- }
2804
- return registered3;
2805
- }
2806
- var registered3;
2807
- var init_agent_factory_registry = __esm({
2808
- "src/internal/runtime/registry/agent-factory-registry.ts"() {
2809
- }
2810
- });
2811
-
2812
3039
  // src/internal/runtime/lifecycle/run-to-completion.ts
2813
3040
  var run_to_completion_exports = {};
2814
3041
  __export(run_to_completion_exports, {
@@ -2973,14 +3200,14 @@ async function forkAgentImpl(parent, options, deps) {
2973
3200
  return {
2974
3201
  result: result.result,
2975
3202
  toolCalls: [],
2976
- usage: extractUsage(result)
3203
+ usage: extractUsage2(result)
2977
3204
  };
2978
3205
  });
2979
3206
  } finally {
2980
3207
  await fork.dispose();
2981
3208
  }
2982
3209
  }
2983
- function extractUsage(result) {
3210
+ function extractUsage2(result) {
2984
3211
  if (typeof result !== "object" || result === null) {
2985
3212
  return { inputTokens: 0, outputTokens: 0 };
2986
3213
  }
@@ -3611,331 +3838,104 @@ async function submit(internal) {
3611
3838
  once: true
3612
3839
  });
3613
3840
  }
3614
- const release = await acquireSlot();
3615
- void (async () => {
3616
- try {
3617
- await checkCancelRequestedAt(resolvedId, aborter);
3618
- if (aborter.signal.aborted) {
3619
- const cancelledAt = Date.now();
3620
- await transition(resolvedId, "cancelled", { cancelledAt });
3621
- emitToSubscribers(resolvedId, { type: "cancelled", taskId: resolvedId, cancelledAt });
3622
- return;
3623
- }
3624
- const startedAt = Date.now();
3625
- await transition(resolvedId, "running", { startedAt });
3626
- emitToSubscribers(resolvedId, { type: "started", taskId: resolvedId, startedAt });
3627
- await runWorkAndFinalize(resolvedId, aborter, internal.work);
3628
- } finally {
3629
- release();
3630
- state.aborters.delete(resolvedId);
3631
- }
3632
- })();
3633
- return queuedHandle;
3634
- }
3635
- async function list(filter = {}) {
3636
- return state.store.list(filter);
3637
- }
3638
- async function get(id) {
3639
- if (!isValidTaskId(id, true)) return void 0;
3640
- return state.store.get(id);
3641
- }
3642
- async function cancel(id, reason) {
3643
- if (!isValidTaskId(id, true)) return { cancelled: false, alreadyTerminal: false };
3644
- const handle = await state.store.get(id);
3645
- if (handle === void 0) return { cancelled: false, alreadyTerminal: false };
3646
- if (handle.state === "finished" || handle.state === "error" || handle.state === "cancelled") {
3647
- return { cancelled: false, alreadyTerminal: true };
3648
- }
3649
- const span = startTaskCancelSpan({
3650
- taskId: id,
3651
- ...reason !== void 0 ? { reason } : {},
3652
- via: "api"
3653
- });
3654
- try {
3655
- return await cancelInternal(id, handle.state, reason);
3656
- } finally {
3657
- span.end();
3658
- }
3659
- }
3660
- async function cancelInternal(id, currentState, reason) {
3661
- if (currentState === "queued") {
3662
- const cancelledAt = Date.now();
3663
- await transition(id, "cancelled", { cancelledAt });
3664
- emitToSubscribers(id, {
3665
- type: "cancelled",
3666
- taskId: id,
3667
- cancelledAt,
3668
- ...reason !== void 0 ? { reason } : {}
3669
- });
3670
- state.aborters.delete(id);
3671
- return { cancelled: true, alreadyTerminal: false };
3672
- }
3673
- const aborter = state.aborters.get(id);
3674
- if (aborter !== void 0) aborter.abort(reason ?? "cancelled");
3675
- return { cancelled: true, alreadyTerminal: false };
3676
- }
3677
- async function evictNow(now = Date.now()) {
3678
- return state.store.evictTerminalOlderThan(now - state.retentionMs);
3679
- }
3680
- var DEFAULT_CONCURRENCY, DEFAULT_RETENTION_MS, EVICTION_INTERVAL_MS, RING_CAP, reentryAls, state, subscribe;
3681
- var init_registry = __esm({
3682
- "src/internal/task/registry.ts"() {
3683
- init_errors();
3684
- init_task();
3685
- init_async_semaphore();
3686
- init_ring_buffer();
3687
- init_store();
3688
- init_subscribe();
3689
- init_telemetry();
3690
- DEFAULT_CONCURRENCY = 8;
3691
- DEFAULT_RETENTION_MS = 60 * 60 * 1e3;
3692
- EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
3693
- RING_CAP = 64;
3694
- reentryAls = new AsyncLocalStorage();
3695
- state = buildState({});
3696
- subscribe = buildSubscribe({
3697
- getBuffer: (id) => state.buffers.get(id),
3698
- getOrCreateSubscriberSet: (id) => {
3699
- let subs = state.subscribers.get(id);
3700
- if (subs === void 0) {
3701
- subs = /* @__PURE__ */ new Set();
3702
- state.subscribers.set(id, subs);
3703
- }
3704
- return subs;
3705
- },
3706
- removeSubscriber: (id, cb) => {
3707
- const set = state.subscribers.get(id);
3708
- set?.delete(cb);
3709
- if (set !== void 0 && set.size === 0) state.subscribers.delete(id);
3710
- }
3711
- });
3712
- }
3713
- });
3714
- function toJsonSchema(schema, options = { unrepresentable: "any" }) {
3715
- return toJSONSchema(schema, options);
3716
- }
3717
- var init_to_json_schema = __esm({
3718
- "src/internal/zod/to-json-schema.ts"() {
3719
- }
3720
- });
3721
- function requireZod() {
3722
- if (cachedZ !== void 0) return cachedZ;
3723
- const r = createRequire(import.meta.url);
3724
- let mod;
3725
- try {
3726
- mod = r("zod");
3727
- } catch (cause) {
3728
- throw new ConfigurationError(
3729
- 'Structured output requires the optional peer dependency `zod`. Add `"zod": "^3.25.0 || ^4.0.0"` to your package.json and reinstall.',
3730
- { code: "zod_not_installed", cause }
3731
- );
3732
- }
3733
- cachedZ = mod.z ?? mod;
3734
- return cachedZ;
3735
- }
3736
- function makeOutputTool(jsonSchema, onCapture) {
3737
- return {
3738
- name: "output",
3739
- description: "Call this tool with your final structured answer. Match the JSON schema exactly.",
3740
- inputSchema: jsonSchema,
3741
- handler: (input) => {
3742
- const out = onCapture(input);
3743
- if (typeof out === "string") return out;
3744
- if (out instanceof Promise) return out;
3745
- return "";
3746
- }
3747
- };
3748
- }
3749
- function buildTransientAgentOptions(params) {
3750
- return {
3751
- model: params.model,
3752
- local: params.local,
3753
- tools: [params.outputTool],
3754
- systemPrompt: params.systemPrompt ?? "You produce structured output by calling the `output` tool exactly once. Match the JSON schema.",
3755
- ...params.apiKey !== void 0 ? { apiKey: params.apiKey } : {},
3756
- ...params.providers !== void 0 ? { providers: params.providers } : {}
3757
- };
3758
- }
3759
- function extractUsage2(result) {
3760
- const u = result.usage;
3761
- if (u === void 0) return { inputTokens: 0, outputTokens: 0 };
3762
- return { inputTokens: u.inputTokens ?? 0, outputTokens: u.outputTokens ?? 0 };
3763
- }
3764
- function buildToolPrompt(prompt) {
3765
- return `${prompt}
3766
-
3767
- Respond by calling the \`output\` tool with the structured answer that matches the schema.`;
3768
- }
3769
- function setupStructuredOutput(schema, maxRetries) {
3770
- const z8 = requireZod();
3771
- const jsonSchema = toJsonSchema(schema, { unrepresentable: "any" });
3772
- return {
3773
- z: z8,
3774
- jsonSchema,
3775
- maxRetries: maxRetries ?? 1,
3776
- initialUsage: { inputTokens: 0, outputTokens: 0 }
3777
- };
3778
- }
3779
- async function disposeAndDeleteTransient(agent, deletor) {
3780
- await agent.dispose();
3781
- try {
3782
- await deletor(agent.agentId);
3783
- } catch {
3784
- }
3785
- }
3786
- var cachedZ;
3787
- var init_structured_output_helpers = __esm({
3788
- "src/internal/structured-output-helpers.ts"() {
3789
- init_errors();
3790
- init_to_json_schema();
3791
- }
3792
- });
3793
-
3794
- // src/generate-object.ts
3795
- var generate_object_exports = {};
3796
- __export(generate_object_exports, {
3797
- GenerateObjectError: () => GenerateObjectError,
3798
- generateObjectImpl: () => generateObjectImpl
3799
- });
3800
- function salvagePartial(schema, raw) {
3801
- if (typeof raw !== "object" || raw === null) return raw;
3802
- const shape = schema.shape;
3803
- if (shape === void 0 || typeof shape !== "object") return raw;
3804
- const rawObj = raw;
3805
- const out = {};
3806
- for (const [key2, fieldSchema] of Object.entries(shape)) {
3807
- if (typeof fieldSchema?.safeParse !== "function") continue;
3808
- const parsed = fieldSchema.safeParse(rawObj[key2]);
3809
- if (parsed.success) out[key2] = parsed.data;
3810
- }
3811
- return out;
3812
- }
3813
- async function runReasoningPhase(options, deps) {
3814
- const reasoningOptions = {
3815
- model: options.model,
3816
- local: options.local,
3817
- ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
3818
- ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
3819
- ...options.providers !== void 0 ? { providers: options.providers } : {}
3820
- };
3821
- const reasoningAgent = await deps.create(reasoningOptions);
3822
- try {
3823
- const run = await reasoningAgent.send(options.prompt);
3824
- const result = await run.wait();
3825
- const text = result.result;
3826
- return typeof text === "string" ? text : options.prompt;
3827
- } finally {
3828
- await disposeAndDeleteTransient(reasoningAgent, deps.delete);
3829
- }
3830
- }
3831
- async function generateObjectImpl(options, deps) {
3832
- const { jsonSchema, maxRetries, initialUsage } = setupStructuredOutput(
3833
- options.schema,
3834
- options.maxRetries
3835
- );
3836
- let capturedRaw;
3837
- let lastUsage = initialUsage;
3838
- const sentinel = /* @__PURE__ */ Symbol("generate-object-sentinel");
3839
- class CaptureSentinel extends Error {
3840
- constructor(captured) {
3841
- super("generate-object-capture");
3842
- this.captured = captured;
3843
- }
3844
- captured;
3845
- [sentinel] = true;
3846
- }
3847
- const outputTool = makeOutputTool(jsonSchema, (input) => {
3848
- if (capturedRaw === void 0) {
3849
- capturedRaw = input;
3850
- }
3851
- throw new CaptureSentinel(input);
3852
- });
3853
- const reasoningText = options.structuringModel !== void 0 ? await runReasoningPhase(options, deps) : void 0;
3854
- const structuringModel = options.structuringModel ?? options.model;
3855
- const structuringPrompt = reasoningText ?? options.prompt;
3856
- const agentOptions = buildTransientAgentOptions({
3857
- model: structuringModel,
3858
- local: options.local,
3859
- outputTool,
3860
- ...options.systemPrompt !== void 0 ? { systemPrompt: options.systemPrompt } : {},
3861
- ...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
3862
- ...options.providers !== void 0 ? { providers: options.providers } : {}
3863
- });
3864
- const agent = await deps.create(agentOptions);
3865
- try {
3866
- const userMessage = buildToolPrompt(structuringPrompt);
3867
- let lastParseError;
3868
- for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
3869
- capturedRaw = void 0;
3870
- const run = await agent.send(userMessage);
3871
- const result = await run.wait();
3872
- lastUsage = extractUsage2(result);
3873
- if (capturedRaw === void 0) {
3874
- if (result.status === "error" && result.error !== void 0) {
3875
- throw new GenerateObjectError(
3876
- "no_tool_call",
3877
- `Agent run failed before the model could reply: ${result.error.message ?? "unknown error"} [${result.error.code ?? "?"}]`,
3878
- result.error
3879
- );
3880
- }
3881
- if (attempt === maxRetries) {
3882
- throw new GenerateObjectError(
3883
- "no_tool_call",
3884
- "The model returned text instead of calling the `output` tool."
3885
- );
3886
- }
3887
- continue;
3888
- }
3889
- const parsed = options.schema.safeParse(capturedRaw);
3890
- if (parsed.success) {
3891
- return {
3892
- object: parsed.data,
3893
- raw: capturedRaw,
3894
- usage: lastUsage,
3895
- finishReason: "tool_use"
3896
- };
3841
+ const release = await acquireSlot();
3842
+ void (async () => {
3843
+ try {
3844
+ await checkCancelRequestedAt(resolvedId, aborter);
3845
+ if (aborter.signal.aborted) {
3846
+ const cancelledAt = Date.now();
3847
+ await transition(resolvedId, "cancelled", { cancelledAt });
3848
+ emitToSubscribers(resolvedId, { type: "cancelled", taskId: resolvedId, cancelledAt });
3849
+ return;
3897
3850
  }
3898
- lastParseError = parsed.error;
3899
- }
3900
- if (options.errorStrategy === "return-raw") {
3901
- return {
3902
- object: capturedRaw,
3903
- raw: capturedRaw,
3904
- usage: lastUsage,
3905
- finishReason: "tool_use"
3906
- };
3907
- }
3908
- if (options.errorStrategy === "return-partial") {
3909
- return {
3910
- object: salvagePartial(options.schema, capturedRaw),
3911
- raw: capturedRaw,
3912
- usage: lastUsage,
3913
- finishReason: "tool_use"
3914
- };
3851
+ const startedAt = Date.now();
3852
+ await transition(resolvedId, "running", { startedAt });
3853
+ emitToSubscribers(resolvedId, { type: "started", taskId: resolvedId, startedAt });
3854
+ await runWorkAndFinalize(resolvedId, aborter, internal.work);
3855
+ } finally {
3856
+ release();
3857
+ state.aborters.delete(resolvedId);
3915
3858
  }
3916
- throw new GenerateObjectError(
3917
- "parse_failed",
3918
- "Schema parse failed after all retries.",
3919
- lastParseError
3920
- );
3859
+ })();
3860
+ return queuedHandle;
3861
+ }
3862
+ async function list(filter = {}) {
3863
+ return state.store.list(filter);
3864
+ }
3865
+ async function get(id) {
3866
+ if (!isValidTaskId(id, true)) return void 0;
3867
+ return state.store.get(id);
3868
+ }
3869
+ async function cancel(id, reason) {
3870
+ if (!isValidTaskId(id, true)) return { cancelled: false, alreadyTerminal: false };
3871
+ const handle = await state.store.get(id);
3872
+ if (handle === void 0) return { cancelled: false, alreadyTerminal: false };
3873
+ if (handle.state === "finished" || handle.state === "error" || handle.state === "cancelled") {
3874
+ return { cancelled: false, alreadyTerminal: true };
3875
+ }
3876
+ const span = startTaskCancelSpan({
3877
+ taskId: id,
3878
+ ...reason !== void 0 ? { reason } : {},
3879
+ via: "api"
3880
+ });
3881
+ try {
3882
+ return await cancelInternal(id, handle.state, reason);
3921
3883
  } finally {
3922
- await disposeAndDeleteTransient(agent, deps.delete);
3884
+ span.end();
3923
3885
  }
3924
3886
  }
3925
- var GenerateObjectError;
3926
- var init_generate_object = __esm({
3927
- "src/generate-object.ts"() {
3928
- init_structured_output_helpers();
3929
- GenerateObjectError = class extends Error {
3930
- name = "GenerateObjectError";
3931
- code;
3932
- cause;
3933
- constructor(code, message, cause) {
3934
- super(message);
3935
- this.code = code;
3936
- if (cause !== void 0) this.cause = cause;
3887
+ async function cancelInternal(id, currentState, reason) {
3888
+ if (currentState === "queued") {
3889
+ const cancelledAt = Date.now();
3890
+ await transition(id, "cancelled", { cancelledAt });
3891
+ emitToSubscribers(id, {
3892
+ type: "cancelled",
3893
+ taskId: id,
3894
+ cancelledAt,
3895
+ ...reason !== void 0 ? { reason } : {}
3896
+ });
3897
+ state.aborters.delete(id);
3898
+ return { cancelled: true, alreadyTerminal: false };
3899
+ }
3900
+ const aborter = state.aborters.get(id);
3901
+ if (aborter !== void 0) aborter.abort(reason ?? "cancelled");
3902
+ return { cancelled: true, alreadyTerminal: false };
3903
+ }
3904
+ async function evictNow(now = Date.now()) {
3905
+ return state.store.evictTerminalOlderThan(now - state.retentionMs);
3906
+ }
3907
+ var DEFAULT_CONCURRENCY, DEFAULT_RETENTION_MS, EVICTION_INTERVAL_MS, RING_CAP, reentryAls, state, subscribe;
3908
+ var init_registry = __esm({
3909
+ "src/internal/task/registry.ts"() {
3910
+ init_errors();
3911
+ init_task();
3912
+ init_async_semaphore();
3913
+ init_ring_buffer();
3914
+ init_store();
3915
+ init_subscribe();
3916
+ init_telemetry();
3917
+ DEFAULT_CONCURRENCY = 8;
3918
+ DEFAULT_RETENTION_MS = 60 * 60 * 1e3;
3919
+ EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
3920
+ RING_CAP = 64;
3921
+ reentryAls = new AsyncLocalStorage();
3922
+ state = buildState({});
3923
+ subscribe = buildSubscribe({
3924
+ getBuffer: (id) => state.buffers.get(id),
3925
+ getOrCreateSubscriberSet: (id) => {
3926
+ let subs = state.subscribers.get(id);
3927
+ if (subs === void 0) {
3928
+ subs = /* @__PURE__ */ new Set();
3929
+ state.subscribers.set(id, subs);
3930
+ }
3931
+ return subs;
3932
+ },
3933
+ removeSubscriber: (id, cb) => {
3934
+ const set = state.subscribers.get(id);
3935
+ set?.delete(cb);
3936
+ if (set !== void 0 && set.size === 0) state.subscribers.delete(id);
3937
3937
  }
3938
- };
3938
+ });
3939
3939
  }
3940
3940
  });
3941
3941
 
@@ -3993,7 +3993,7 @@ async function* streamObjectImpl(options, deps) {
3993
3993
  }
3994
3994
  }
3995
3995
  const result = await run.wait();
3996
- lastUsage = extractUsage2(result);
3996
+ lastUsage = extractUsage(result);
3997
3997
  if (capturedRaw === void 0) {
3998
3998
  if (result.status === "error" && result.error !== void 0) {
3999
3999
  throw new StreamObjectError(
@@ -4745,7 +4745,10 @@ async function runAgentStep(step, input, ctx) {
4745
4745
  };
4746
4746
  }
4747
4747
  const exec = async () => {
4748
- const run = await step.agent.send(prompt);
4748
+ const run = await step.agent.send(
4749
+ prompt,
4750
+ step.origin !== void 0 ? { origin: step.origin } : void 0
4751
+ );
4749
4752
  const result = await run.wait();
4750
4753
  if (result.status === "finished") {
4751
4754
  return result.result ?? "";
@@ -5517,6 +5520,7 @@ var AgentBuilder = class {
5517
5520
  constructor(deps) {
5518
5521
  this.deps = deps;
5519
5522
  }
5523
+ /** SE8 — accepts a bare-string id shorthand (`"openai/gpt-4o-mini"`) or a {@link ModelSelection}. */
5520
5524
  model(m) {
5521
5525
  this.opts.model = m;
5522
5526
  return this;
@@ -5874,13 +5878,87 @@ function isLocalAgentId(id) {
5874
5878
  return id.startsWith(PREFIX.localAgent);
5875
5879
  }
5876
5880
 
5881
+ // src/internal/runtime/config/default-model.ts
5882
+ var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
5883
+
5884
+ // src/agent-generate.ts
5885
+ async function agentGenerate(agent, agentModel, agentApiKey, agentLocal, input, options) {
5886
+ const { output, maxRetries, errorStrategy, ...sendOptions } = options;
5887
+ const run = await agent.send(input, sendOptions);
5888
+ const result = await run.wait();
5889
+ const { generateObjectImpl: generateObjectImpl2, GenerateObjectError: GenerateObjectError2 } = await Promise.resolve().then(() => (init_generate_object(), generate_object_exports));
5890
+ const answer = structurableAnswerOrThrow(result, GenerateObjectError2);
5891
+ const { getAgentFacade: getAgentFacade2 } = await Promise.resolve().then(() => (init_agent_factory_registry(), agent_factory_registry_exports));
5892
+ const facade = getAgentFacade2();
5893
+ const structured = await generateObjectImpl2(
5894
+ {
5895
+ prompt: answer,
5896
+ schema: output,
5897
+ // `agentModel` arrives already normalized (`this.model` passed through
5898
+ // `normalizeModel` in the LocalAgent/CloudAgent constructor), so the widened
5899
+ // `string | ModelSelection` field only ever receives a `ModelSelection` here.
5900
+ model: agentModel ?? { id: DEFAULT_AGENTIC_MODEL_ID },
5901
+ // `local` is required to keep the transient structuring agent local-only;
5902
+ // default to the process cwd when the agent had no explicit local config.
5903
+ local: agentLocal ?? { cwd: process.cwd() },
5904
+ ...agentApiKey !== void 0 ? { apiKey: agentApiKey } : {},
5905
+ ...maxRetries !== void 0 ? { maxRetries } : {},
5906
+ ...errorStrategy !== void 0 ? { errorStrategy } : {}
5907
+ },
5908
+ {
5909
+ create: (opts) => facade.create(opts),
5910
+ delete: (agentId) => facade.delete(agentId)
5911
+ }
5912
+ );
5913
+ return {
5914
+ object: structured.object,
5915
+ result,
5916
+ raw: structured.raw,
5917
+ usage: structured.usage
5918
+ };
5919
+ }
5920
+ function structurableAnswerOrThrow(result, GenerateObjectError2) {
5921
+ if (result.status === "error") {
5922
+ throw new GenerateObjectError2(
5923
+ "no_tool_call",
5924
+ `Agent run failed before an answer could be structured: ${result.error?.message ?? "unknown error"} [${result.error?.code ?? "?"}]`,
5925
+ result.error
5926
+ );
5927
+ }
5928
+ if (result.status === "cancelled") {
5929
+ throw new GenerateObjectError2(
5930
+ "no_tool_call",
5931
+ "Agent run was cancelled before an answer could be structured."
5932
+ );
5933
+ }
5934
+ const answer = result.result;
5935
+ if (answer === void 0 || answer.length === 0) {
5936
+ throw new GenerateObjectError2(
5937
+ "no_tool_call",
5938
+ "The tool loop finished with no text answer (tool-only completion) \u2014 there is nothing to structure."
5939
+ );
5940
+ }
5941
+ return answer;
5942
+ }
5943
+
5877
5944
  // src/internal/runtime/cloud/cloud-agent.ts
5878
5945
  init_errors();
5879
5946
  init_cwd_mutex();
5880
5947
  init_path_guard();
5881
5948
 
5882
- // src/internal/runtime/config/default-model.ts
5883
- var DEFAULT_AGENTIC_MODEL_ID = "google/gemini-2.0-flash-001";
5949
+ // src/internal/runtime/model-selection.ts
5950
+ init_errors();
5951
+ function normalizeModel(m) {
5952
+ if (m === void 0 || typeof m !== "string") return m;
5953
+ const id = m.trim();
5954
+ if (id.length === 0) {
5955
+ throw new ConfigurationError(
5956
+ 'model must be a non-empty "provider/model" id string or a ModelSelection object',
5957
+ { code: "invalid_model_selection" }
5958
+ );
5959
+ }
5960
+ return { id };
5961
+ }
5884
5962
 
5885
5963
  // src/internal/runtime/registry/agent-registry-store.ts
5886
5964
  init_cwd_mutex();
@@ -5957,7 +6035,10 @@ var REGISTRY_RELATIVE_PATH = join(".theokit", "agents", "registry.json");
5957
6035
  function stripSecretsFromOptions(options) {
5958
6036
  return assignDefined({
5959
6037
  name: options.name,
5960
- model: options.model !== void 0 ? { id: options.model.id } : void 0,
6038
+ model: (() => {
6039
+ const m = normalizeModel(options.model);
6040
+ return m !== void 0 ? { id: m.id } : void 0;
6041
+ })(),
5961
6042
  systemPrompt: typeof options.systemPrompt === "string" ? options.systemPrompt : void 0,
5962
6043
  local: serializeLocal(options.local),
5963
6044
  cloud: serializeCloud(options.cloud),
@@ -6242,7 +6323,8 @@ function serializeCloudAgentConfig(options) {
6242
6323
  cloud: serializeCloud2(options.cloud)
6243
6324
  };
6244
6325
  if (typeof options.agentId === "string") payload.agentId = options.agentId;
6245
- if (options.model !== void 0) payload.model = { id: options.model.id };
6326
+ const cfgModel = normalizeModel(options.model);
6327
+ if (cfgModel !== void 0) payload.model = { id: cfgModel.id };
6246
6328
  if (typeof options.systemPrompt === "string") payload.systemPrompt = options.systemPrompt;
6247
6329
  const skills = serializeSkills(options.skills);
6248
6330
  if (skills !== void 0) payload.skills = skills;
@@ -7047,6 +7129,7 @@ function applyScriptMetrics(base, script) {
7047
7129
  if (script.cost !== void 0) base.cost = script.cost;
7048
7130
  if (script.stoppedAtIterationLimit === true) base.stoppedAtIterationLimit = true;
7049
7131
  if (script.stoppedByDoomLoop === true) base.stoppedByDoomLoop = true;
7132
+ if (script.origin !== void 0) base.origin = script.origin;
7050
7133
  }
7051
7134
 
7052
7135
  // src/internal/runtime/cloud/cloud-run.ts
@@ -7402,7 +7485,7 @@ var CloudAgent = class {
7402
7485
  disposed = false;
7403
7486
  constructor(options, providedAgentId) {
7404
7487
  this.agentId = providedAgentId ?? options.agentId ?? generateCloudAgentId();
7405
- this.model = options.model;
7488
+ this.model = normalizeModel(options.model);
7406
7489
  this.options = options;
7407
7490
  this.cloudPayload = serializeCloudAgentConfig({ ...options, agentId: this.agentId });
7408
7491
  const repoUrls = (options.cloud?.repos ?? []).map((repo) => repo.url);
@@ -7432,6 +7515,17 @@ var CloudAgent = class {
7432
7515
  const apiKey = resolveApiKey(this.options.apiKey);
7433
7516
  return isFixtureApiKey(apiKey) && getConfiguredBaseUrl() === void 0;
7434
7517
  }
7518
+ // SE9 — integrated structured output; delegates to the shared helper.
7519
+ generate(message, options) {
7520
+ return agentGenerate(
7521
+ this,
7522
+ this.model,
7523
+ this.options.apiKey,
7524
+ this.options.local,
7525
+ message,
7526
+ options
7527
+ );
7528
+ }
7435
7529
  async send(message, options = {}) {
7436
7530
  if (options.tools !== void 0 && options.tools.length > 0) {
7437
7531
  throw new ConfigurationError(
@@ -7462,7 +7556,7 @@ var CloudAgent = class {
7462
7556
  });
7463
7557
  }
7464
7558
  async sendLocked(message, options) {
7465
- const overrideModel = options.model;
7559
+ const overrideModel = normalizeModel(options.model);
7466
7560
  if (overrideModel !== void 0) {
7467
7561
  this.model = overrideModel;
7468
7562
  updateRegisteredAgent(this.agentId, { model: overrideModel });
@@ -8379,18 +8473,18 @@ var ALL_ADAPTERS = [
8379
8473
  arizeAdapter,
8380
8474
  braintrustAdapter
8381
8475
  ];
8382
- var registered = /* @__PURE__ */ new Set();
8476
+ var registered2 = /* @__PURE__ */ new Set();
8383
8477
  function tryAutoRegisterAdapters(settings) {
8384
8478
  if (settings?.enabled !== true) return;
8385
8479
  if (settings.autoDetect === false) return;
8386
8480
  const disabled = new Set(settings.disable ?? []);
8387
8481
  for (const adapter of ALL_ADAPTERS) {
8388
- if (registered.has(adapter.moduleName)) continue;
8482
+ if (registered2.has(adapter.moduleName)) continue;
8389
8483
  if (disabled.has(adapter.displayName.toLowerCase())) continue;
8390
8484
  if (!adapter.detect()) continue;
8391
8485
  try {
8392
8486
  adapter.register();
8393
- registered.add(adapter.moduleName);
8487
+ registered2.add(adapter.moduleName);
8394
8488
  process.stderr.write(`[theokit-sdk] telemetry: ${adapter.displayName} auto-instrumented.
8395
8489
  `);
8396
8490
  } catch (cause) {
@@ -8920,6 +9014,26 @@ function paginate(items, opts) {
8920
9014
  return items.slice(start, end);
8921
9015
  }
8922
9016
 
9017
+ // src/internal/persistence/session-meta.ts
9018
+ function applyMetaPatch(current, patch) {
9019
+ const next = {};
9020
+ if (current.title !== void 0) next.title = current.title;
9021
+ if (current.tag !== void 0) next.tag = current.tag;
9022
+ if (patch.title === null) delete next.title;
9023
+ else if (patch.title !== void 0) next.title = patch.title;
9024
+ if (patch.tag === null) delete next.tag;
9025
+ else if (patch.tag !== void 0) next.tag = patch.tag;
9026
+ return next;
9027
+ }
9028
+ function coerceSessionMeta(raw) {
9029
+ if (typeof raw !== "object" || raw === null) return void 0;
9030
+ const obj = raw;
9031
+ const meta = {};
9032
+ if (typeof obj.title === "string") meta.title = obj.title;
9033
+ if (typeof obj.tag === "string") meta.tag = obj.tag;
9034
+ return meta.title === void 0 && meta.tag === void 0 ? void 0 : meta;
9035
+ }
9036
+
8923
9037
  // src/internal/persistence/conversation-storage-fs.ts
8924
9038
  var FileSystemConversationStorage = class {
8925
9039
  #root;
@@ -8970,6 +9084,37 @@ var FileSystemConversationStorage = class {
8970
9084
  async compact(conversationId, maxTurns) {
8971
9085
  await compactSessionFile(this.#root, conversationId, maxTurns);
8972
9086
  }
9087
+ // SE4 — session metadata persisted as a per-conversation sidecar
9088
+ // `<root>/.theokit/agents/<safeId>/session.json` (same sanitized perimeter as
9089
+ // the transcript). Kept separate from messages.jsonl so a title/tag write does
9090
+ // not rewrite the append-only log.
9091
+ #metaPath(conversationId) {
9092
+ const safe2 = sanitizeIdentifier(conversationId, { maxLen: 128 });
9093
+ return safePathJoin(this.#root, ".theokit", "agents", safe2, "session.json");
9094
+ }
9095
+ async getSessionMeta(conversationId) {
9096
+ try {
9097
+ const raw = await readFile(this.#metaPath(conversationId), "utf8");
9098
+ return coerceSessionMeta(JSON.parse(raw));
9099
+ } catch (cause) {
9100
+ if (cause.code === "ENOENT") return void 0;
9101
+ throw cause;
9102
+ }
9103
+ }
9104
+ async setSessionMeta(conversationId, patch) {
9105
+ const metaPath = this.#metaPath(conversationId);
9106
+ await mkdir(dirname(metaPath), { recursive: true });
9107
+ await withFileLock(metaPath, async () => {
9108
+ let current = {};
9109
+ try {
9110
+ current = coerceSessionMeta(JSON.parse(await readFile(metaPath, "utf8"))) ?? {};
9111
+ } catch (cause) {
9112
+ if (cause.code !== "ENOENT") throw cause;
9113
+ }
9114
+ const next = applyMetaPatch(current, patch);
9115
+ await writeFile(metaPath, redactSecrets(JSON.stringify(next)), "utf8");
9116
+ });
9117
+ }
8973
9118
  async dispose() {
8974
9119
  }
8975
9120
  };
@@ -10891,7 +11036,7 @@ function bootstrapSubmanagers(args) {
10891
11036
  const providerCount = (args.options.providers?.routes?.length ?? 0) + enabledPluginNames(args.options.plugins).length;
10892
11037
  if (providerCount > 0 || args.options.providers !== void 0) {
10893
11038
  out.providers = new ProvidersManagerImpl(
10894
- args.options.model,
11039
+ normalizeModel(args.options.model),
10895
11040
  args.options.providers,
10896
11041
  args.options.plugins
10897
11042
  );
@@ -10961,6 +11106,7 @@ function createLocalRun(options) {
10961
11106
  projectMcpServers: options.projectMcpServers,
10962
11107
  ...options.persistMemoryFact !== void 0 ? { persistMemoryFact: options.persistMemoryFact } : {}
10963
11108
  });
11109
+ if (options.sendOptions.origin !== void 0) script.origin = options.sendOptions.origin;
10964
11110
  const handle = new LocalRun({
10965
11111
  id,
10966
11112
  agentId: options.agentId,
@@ -11609,6 +11755,15 @@ async function emitReasoningDeltaCallback(inputs, text) {
11609
11755
  );
11610
11756
  }
11611
11757
 
11758
+ // src/types/run-events.ts
11759
+ function emitRunEvent(sink, event) {
11760
+ if (sink === void 0) return;
11761
+ try {
11762
+ sink(event);
11763
+ } catch {
11764
+ }
11765
+ }
11766
+
11612
11767
  // src/internal/agent-loop/tool-dispatch.ts
11613
11768
  init_async_local_storage();
11614
11769
 
@@ -11706,6 +11861,22 @@ function coerceArgsToSchema(args, schema) {
11706
11861
  return { value: out, changed };
11707
11862
  }
11708
11863
 
11864
+ // src/tool-error.ts
11865
+ init_errors();
11866
+ var ToolError = class extends TheokitAgentError {
11867
+ name = "ToolError";
11868
+ /** The error content surfaced to the model: a string, or text/image blocks. */
11869
+ content;
11870
+ constructor(content, options = {}) {
11871
+ super(renderToolErrorMessage(content), { ...options, isRetryable: false });
11872
+ this.content = content;
11873
+ }
11874
+ };
11875
+ function renderToolErrorMessage(content) {
11876
+ if (typeof content === "string") return content;
11877
+ return content.map((block) => block.type === "text" ? block.text : `[${block.source.media_type} image]`).join("\n");
11878
+ }
11879
+
11709
11880
  // src/internal/runtime/tools/shell-tool.ts
11710
11881
  async function runShell(options) {
11711
11882
  if (options.sandbox === true && isObviouslyUnsafe(options.command)) {
@@ -11745,23 +11916,27 @@ async function executeTool(inputs, resolved, call) {
11745
11916
  if (resolved.origin === "shell") return runShellTool(inputs, call);
11746
11917
  if (resolved.origin === "memory") return runMemoryTool(resolved, call, inputs.context);
11747
11918
  if (resolved.origin === "custom")
11748
- return runCustomTool(resolved, call, inputs.signal, inputs.context);
11919
+ return runCustomTool(resolved, call, inputs.signal, inputs.context, inputs.messages);
11749
11920
  return runMcpTool(inputs, resolved, call);
11750
11921
  }
11751
11922
  async function runMemoryTool(resolved, call, context) {
11752
11923
  return runHandlerTool("memory", resolved.memoryHandler, call, void 0, context);
11753
11924
  }
11754
- async function runCustomTool(resolved, call, signal, context) {
11755
- return runHandlerTool("custom", resolved.customHandler, call, signal, context);
11925
+ async function runCustomTool(resolved, call, signal, context, messages) {
11926
+ return runHandlerTool("custom", resolved.customHandler, call, signal, context, messages);
11756
11927
  }
11757
- async function runHandlerTool(kind, handler, call, signal, context) {
11928
+ async function runHandlerTool(kind, handler, call, signal, context, messages) {
11758
11929
  if (handler === void 0) {
11759
11930
  return { stdout: "", stderr: `${kind} tool ${call.name} has no handler`, exitCode: 127 };
11760
11931
  }
11761
11932
  try {
11762
- const stdout = await handler(call.input, { signal, context });
11763
- return { stdout, stderr: "", exitCode: 0 };
11933
+ const out = await handler(call.input, { signal, context, messages });
11934
+ if (typeof out !== "string") return { stdout: "", stderr: "", exitCode: 0, content: out };
11935
+ return { stdout: out, stderr: "", exitCode: 0 };
11764
11936
  } catch (cause) {
11937
+ if (cause instanceof ToolError) {
11938
+ return { stdout: "", stderr: cause.message, exitCode: 1, content: cause.content };
11939
+ }
11765
11940
  const message = cause instanceof Error ? cause.message : String(cause);
11766
11941
  return { stdout: "", stderr: message, exitCode: 1 };
11767
11942
  }
@@ -11880,6 +12055,11 @@ async function dispatchSingleCall(inputs, tools, call, events, parentSpan) {
11880
12055
  toolSpan?.end();
11881
12056
  return fileVeto;
11882
12057
  }
12058
+ emitRunEvent(inputs.runEventSink, {
12059
+ type: "tool_progress",
12060
+ toolName: workingCall.name,
12061
+ toolCallId: callId
12062
+ });
11883
12063
  const result = await runToolWithLifecycle(inputs, resolved, workingCall, callId);
11884
12064
  await inputs.pluginManager?.runPostToolCallHooks({
11885
12065
  name: workingCall.name,
@@ -11906,6 +12086,13 @@ function applyRepairAndExtractCall(tools, call) {
11906
12086
  function vetoFromForkWhitelist(inputs, call, callId, events) {
11907
12087
  const whitelistDecision = checkToolWhitelist(call.name);
11908
12088
  if (whitelistDecision.allowed) return void 0;
12089
+ emitRunEvent(inputs.runEventSink, {
12090
+ type: "permission_denied",
12091
+ toolName: call.name,
12092
+ toolCallId: callId,
12093
+ source: "fork_whitelist",
12094
+ message: whitelistDecision.reason ?? "tool not available in fork"
12095
+ });
11909
12096
  events.push(buildToolUseRunning(inputs, callId, call));
11910
12097
  events.push(
11911
12098
  buildToolUseCompleted(inputs, callId, call, {
@@ -11942,6 +12129,13 @@ async function vetoFromPluginPreHook(inputs, call, callId, events) {
11942
12129
  runId: inputs.runId
11943
12130
  });
11944
12131
  if (pluginVeto === void 0) return void 0;
12132
+ emitRunEvent(inputs.runEventSink, {
12133
+ type: "permission_denied",
12134
+ toolName: call.name,
12135
+ toolCallId: callId,
12136
+ source: "plugin",
12137
+ message: pluginVeto.message
12138
+ });
11945
12139
  events.push(
11946
12140
  buildToolUseCompleted(inputs, callId, call, {
11947
12141
  stdout: "",
@@ -11964,6 +12158,13 @@ async function vetoFromFileHookPreDecision(inputs, call, callId, events) {
11964
12158
  runId: inputs.runId
11965
12159
  });
11966
12160
  if (!preDecision.blocked) return void 0;
12161
+ emitRunEvent(inputs.runEventSink, {
12162
+ type: "permission_denied",
12163
+ toolName: call.name,
12164
+ toolCallId: callId,
12165
+ source: "file_hook",
12166
+ message: preDecision.reason ?? "blocked by hook"
12167
+ });
11967
12168
  events.push(
11968
12169
  buildToolUseCompleted(inputs, callId, call, {
11969
12170
  stdout: "",
@@ -12007,7 +12208,9 @@ async function runToolWithLifecycle(inputs, resolved, call, callId) {
12007
12208
  await safeEmitToolHook(inputs.onToolEnd, {
12008
12209
  toolName: call.name,
12009
12210
  args: call.input,
12010
- result: result.stdout,
12211
+ // SE7 — a block-returning handler has empty `stdout`; surface its structured
12212
+ // content to the hook instead of an empty string.
12213
+ result: result.content !== void 0 ? result.content : result.stdout,
12011
12214
  conversationId: inputs.agentId,
12012
12215
  callId,
12013
12216
  durationMs
@@ -12029,7 +12232,10 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
12029
12232
  output: {
12030
12233
  stdout: result.stdout,
12031
12234
  stderr: result.stderr,
12032
- exitCode: result.exitCode ?? 0
12235
+ exitCode: result.exitCode ?? 0,
12236
+ // SE7 — structured content (text + image blocks) when the handler
12237
+ // returned/threw blocks; absent for the string path.
12238
+ ...result.content !== void 0 ? { content: result.content } : {}
12033
12239
  },
12034
12240
  agentId: inputs.agentId,
12035
12241
  runId: inputs.runId
@@ -12042,7 +12248,9 @@ function finalizeSpanAndPostHook(inputs, call, callId, result, events, toolSpan)
12042
12248
  return {
12043
12249
  type: "tool_result",
12044
12250
  toolUseId: call.id,
12045
- content: renderToolResult(result),
12251
+ // SE7 — structured content blocks (handler-returned or ToolError-carried)
12252
+ // are authoritative; otherwise the legacy string rendering.
12253
+ content: result.content !== void 0 ? result.content : renderToolResult(result),
12046
12254
  ...result.exitCode !== 0 && result.exitCode !== void 0 ? { isError: true } : {}
12047
12255
  };
12048
12256
  }
@@ -12091,6 +12299,29 @@ function buildToolUseCompleted(inputs, callId, call, result) {
12091
12299
  };
12092
12300
  }
12093
12301
 
12302
+ // src/internal/llm/tool-result-content.ts
12303
+ init_errors();
12304
+ function toBlockToolResultContent(content) {
12305
+ return content;
12306
+ }
12307
+ function toStringToolResultContent(content, providerName) {
12308
+ if (typeof content === "string") return content;
12309
+ if (content.some((block) => block.type === "image")) {
12310
+ throw new ConfigurationError(
12311
+ `provider "${providerName}" does not support image content in tool results`
12312
+ );
12313
+ }
12314
+ return content.filter(
12315
+ (block) => block.type === "text"
12316
+ ).map((block) => block.text).join("\n");
12317
+ }
12318
+ function renderToolResultContentText(content, fn) {
12319
+ if (typeof content === "string") return fn(content);
12320
+ return content.map(
12321
+ (block) => block.type === "text" ? { type: "text", text: fn(block.text) } : block
12322
+ );
12323
+ }
12324
+
12094
12325
  // src/internal/agent-loop/tool-result-guard.ts
12095
12326
  var OPEN = "<untrusted-tool-output>";
12096
12327
  var CLOSE = "</untrusted-tool-output>";
@@ -12116,7 +12347,11 @@ ${CLOSE}`;
12116
12347
  function applyToolResultGuard(parts, opts) {
12117
12348
  if (opts.delimit !== true && opts.redactPii !== true) return parts;
12118
12349
  return parts.map(
12119
- (p) => p.type === "tool_result" ? { ...p, content: guardText(p.content, opts) } : p
12350
+ (p) => (
12351
+ // SE7 — guard the TEXT of a tool result (string or the text blocks of a
12352
+ // structured content); image blocks pass through untouched.
12353
+ p.type === "tool_result" ? { ...p, content: renderToolResultContentText(p.content, (t) => guardText(t, opts)) } : p
12354
+ )
12120
12355
  );
12121
12356
  }
12122
12357
 
@@ -12455,6 +12690,14 @@ function computeUsageCost(inputs, usage) {
12455
12690
  }
12456
12691
 
12457
12692
  // src/internal/agent-loop/loop.ts
12693
+ function projectToolContextMessages(messages) {
12694
+ const projected = [];
12695
+ for (const m of messages) {
12696
+ const content = m.content.flatMap((p) => p.type === "text" ? [p.text] : []).join("");
12697
+ if (content !== "") projected.push({ role: m.role, content });
12698
+ }
12699
+ return projected;
12700
+ }
12458
12701
  var MAX_NUDGE_ATTEMPTS = 2;
12459
12702
  var MAX_STOP_FEEDBACK_ATTEMPTS = 2;
12460
12703
  async function runAgentLoop(inputs) {
@@ -12697,7 +12940,9 @@ async function continueOrTerminate(inputs, ctx, llmOutput) {
12697
12940
  const outText = await transformLlmOutputText(inputs, llmOutput.text, tCtx);
12698
12941
  ctx.messages.push(buildAssistantTurn(outText, llmOutput.toolCalls));
12699
12942
  const rawResults = await dispatchTools(
12700
- inputs,
12943
+ // SE12 — forward a read-only text projection of the transcript-so-far to tool
12944
+ // handlers via `ctx.messages` (consumed by defineSubAgent's messageFilter).
12945
+ { ...inputs, messages: projectToolContextMessages(ctx.messages) },
12701
12946
  ctx.tools,
12702
12947
  llmOutput.toolCalls,
12703
12948
  ctx.events,
@@ -13047,10 +13292,10 @@ function stripVertexPrefix(modelId) {
13047
13292
  }
13048
13293
 
13049
13294
  // src/internal/providers/builtin/index.ts
13050
- var registered2 = false;
13295
+ var registered3 = false;
13051
13296
  function registerBuiltins() {
13052
- if (registered2) return;
13053
- registered2 = true;
13297
+ if (registered3) return;
13298
+ registered3 = true;
13054
13299
  registerProvider(ANTHROPIC);
13055
13300
  registerProvider(OPENAI);
13056
13301
  registerProvider(OPENROUTER);
@@ -13279,7 +13524,9 @@ function toAnthropicWireMessage(message) {
13279
13524
  return {
13280
13525
  type: "tool_result",
13281
13526
  tool_use_id: part.toolUseId,
13282
- content: part.content,
13527
+ // SE7 — this wire is block-capable: strings pass through, structured
13528
+ // text/image blocks are forwarded natively.
13529
+ content: toBlockToolResultContent(part.content),
13283
13530
  ...part.isError === true ? { is_error: true } : {}
13284
13531
  };
13285
13532
  });
@@ -14071,7 +14318,8 @@ function toOllamaMessages(message) {
14071
14318
  if (part.type === "tool_result") {
14072
14319
  out.push({
14073
14320
  role: "tool",
14074
- content: part.content,
14321
+ // SE7 — string-only tool role: text blocks flatten; image fails fast.
14322
+ content: toStringToolResultContent(part.content, "ollama"),
14075
14323
  tool_name: part.toolUseId
14076
14324
  });
14077
14325
  }
@@ -14608,7 +14856,9 @@ function userOrToolMessages(message) {
14608
14856
  out.push({
14609
14857
  role: "tool",
14610
14858
  tool_call_id: part.toolUseId,
14611
- content: part.content
14859
+ // SE7 — this wire's tool role is string-only: text blocks flatten; an
14860
+ // image block fails fast (ConfigurationError).
14861
+ content: toStringToolResultContent(part.content, "openai")
14612
14862
  });
14613
14863
  }
14614
14864
  }
@@ -15538,18 +15788,20 @@ function levenshtein(a, b) {
15538
15788
  function createRealLocalRun(options) {
15539
15789
  const { userText, id, startTime } = prepareRunContext(options.message);
15540
15790
  const supported = /* @__PURE__ */ new Set(["stream", "wait", "cancel", "conversation"]);
15541
- const unusedFixtureScript = {
15791
+ const runScript = {
15542
15792
  events: [],
15543
15793
  finalStatus: "running",
15544
15794
  cancellable: false,
15545
- conversation: []
15795
+ conversation: [],
15796
+ // SE3 — carry the turn's provenance so buildResult forwards it onto RunResult.origin.
15797
+ ...options.sendOptions.origin !== void 0 ? { origin: options.sendOptions.origin } : {}
15546
15798
  };
15547
15799
  const handle = new RealLocalRun(
15548
15800
  {
15549
15801
  id,
15550
15802
  agentId: options.agentId,
15551
15803
  model: options.model,
15552
- script: unusedFixtureScript,
15804
+ script: runScript,
15553
15805
  supportedOps: supported,
15554
15806
  startTime
15555
15807
  },
@@ -15661,6 +15913,8 @@ function buildLoopInputs(options, runId, userText) {
15661
15913
  // M7 — forward SendOptions.context to the loop so every tool handler receives
15662
15914
  // it on `ctx.context` (shared run config set once, e.g. projectRoot).
15663
15915
  ...options.sendOptions.context !== void 0 ? { context: options.sendOptions.context } : {},
15916
+ // SE2 — forward the opt-in typed runtime-event sink to the loop.
15917
+ ...options.sendOptions.onRunEvent !== void 0 ? { runEventSink: options.sendOptions.onRunEvent } : {},
15664
15918
  // #58 / #57 — forward the per-tool timeout + tool-result guard so a consumer
15665
15919
  // can enable them via SendOptions (not only internal AgentLoopInputs).
15666
15920
  ...options.sendOptions.perToolTimeoutMs !== void 0 ? { perToolTimeoutMs: options.sendOptions.perToolTimeoutMs } : {},
@@ -17405,7 +17659,7 @@ function wrapRunWithPostReplyHook(args) {
17405
17659
  async function executeSendLocked(inputs, message, options) {
17406
17660
  if (inputs.disposed) throw new AgentDisposedError(inputs.agentId);
17407
17661
  await consumePending(inputs.agentId, inputs.invalidationPending, inputs.clearInvalidation, inputs.reload);
17408
- inputs.applyModelOverride(options.model);
17662
+ inputs.applyModelOverride(normalizeModel(options.model));
17409
17663
  const userText = typeof message === "string" ? message : message.text;
17410
17664
  if (inputs.options.onBeforeSend !== void 0) {
17411
17665
  await inputs.options.onBeforeSend({
@@ -17557,7 +17811,7 @@ var LocalAgent = class {
17557
17811
  constructor(options) {
17558
17812
  this.agentId = options.agentId ?? generateLocalAgentId();
17559
17813
  this._telemetry = createTelemetry(options.telemetry);
17560
- this.model = options.model;
17814
+ this.model = normalizeModel(options.model);
17561
17815
  this.options = options;
17562
17816
  this.workspaceCwd = resolveCwd(options.local?.cwd);
17563
17817
  this.conversationStorage = options.conversationStorage;
@@ -17624,6 +17878,11 @@ var LocalAgent = class {
17624
17878
  hooks() {
17625
17879
  return this.hooksExecutor;
17626
17880
  }
17881
+ // SE9 — integrated structured output; delegates to the shared helper.
17882
+ generate(message, options) {
17883
+ const { apiKey, local } = this.options;
17884
+ return agentGenerate(this, this.model, apiKey, local, message, options);
17885
+ }
17627
17886
  async send(message, options = {}) {
17628
17887
  if (options.tools !== void 0 && options.tools.length > 0) {
17629
17888
  validateToolCatalog(options.tools);
@@ -17863,6 +18122,7 @@ function includesSetting(options, source) {
17863
18122
 
17864
18123
  // src/agent-helpers.ts
17865
18124
  async function runCreateUnderSpan(options, span) {
18125
+ options = { ...options, model: normalizeModel(options.model) };
17866
18126
  validateAgentOptions(options);
17867
18127
  validateCloudToolParity(options);
17868
18128
  await guardAgainstIdCollision(options);
@@ -17936,7 +18196,10 @@ async function createLocalAgent(options) {
17936
18196
  const willFlowToProvider = !isFixtureApiKey(apiKey) && !shouldUseRealLocalRuntime(apiKey);
17937
18197
  const shape = validateApiKeyShape(apiKey, {
17938
18198
  strict: willFlowToProvider,
17939
- ...willFlowToProvider ? { provider: providerFromModelId(options.model?.id) } : {}
18199
+ // `options.model` is already normalized by `runCreateUnderSpan`; `normalizeModel`
18200
+ // here is the idempotent narrowing of the widened public type (`{id}` passes
18201
+ // through by reference) — cleaner than an `as` cast.
18202
+ ...willFlowToProvider ? { provider: providerFromModelId(normalizeModel(options.model)?.id) } : {}
17940
18203
  });
17941
18204
  if (shape.malformed) {
17942
18205
  throw new AuthenticationError(shape.message, { code: "malformed_api_key" });
@@ -18002,6 +18265,10 @@ async function rehydrateExistingAgent(agentId, existing, options) {
18002
18265
  ...existing.options,
18003
18266
  ...options,
18004
18267
  ...mergedLocal !== void 0 ? { local: mergedLocal } : {},
18268
+ // SE8 — normalize a caller-supplied bare-string model override at this resume
18269
+ // boundary (Agent.resume never flows through runCreateUnderSpan). Cloud resume
18270
+ // honors it; local resume forces the persisted model below.
18271
+ model: normalizeModel(options.model ?? existing.options.model),
18005
18272
  mcpServers: void 0,
18006
18273
  agentId
18007
18274
  };
@@ -18368,6 +18635,7 @@ async function getOrCreateUncached(agentId, options) {
18368
18635
  }
18369
18636
  setAgentFacade({
18370
18637
  create: (options) => Agent.create(options),
18638
+ delete: (agentId) => Agent.delete(agentId),
18371
18639
  prompt: (message, options) => Agent.prompt(message, options),
18372
18640
  get: (agentId) => Agent.get(agentId),
18373
18641
  resume: (agentId, options) => Agent.resume(agentId, options),
@@ -19328,6 +19596,8 @@ init_generate_object();
19328
19596
  // src/internal/persistence/conversation-storage-memory.ts
19329
19597
  var InMemoryConversationStorage = class {
19330
19598
  #store = /* @__PURE__ */ new Map();
19599
+ // SE4 — session-level metadata (title/tag), kept beside the transcript.
19600
+ #meta = /* @__PURE__ */ new Map();
19331
19601
  async getMessages(conversationId, opts) {
19332
19602
  const existing = this.#store.get(conversationId);
19333
19603
  return existing === void 0 ? [] : paginate(existing.slice(), opts);
@@ -19356,15 +19626,20 @@ var InMemoryConversationStorage = class {
19356
19626
  }
19357
19627
  async deleteConversation(conversationId) {
19358
19628
  this.#store.delete(conversationId);
19629
+ this.#meta.delete(conversationId);
19359
19630
  }
19360
19631
  async deleteScope(prefix) {
19361
19632
  let n = 0;
19362
19633
  for (const id of [...this.#store.keys()]) {
19363
19634
  if (id.startsWith(prefix)) {
19364
19635
  this.#store.delete(id);
19636
+ this.#meta.delete(id);
19365
19637
  n += 1;
19366
19638
  }
19367
19639
  }
19640
+ for (const id of [...this.#meta.keys()]) {
19641
+ if (id.startsWith(prefix)) this.#meta.delete(id);
19642
+ }
19368
19643
  return n;
19369
19644
  }
19370
19645
  async listConversationIds(opts) {
@@ -19372,8 +19647,17 @@ var InMemoryConversationStorage = class {
19372
19647
  if (opts?.limit !== void 0) return all.slice(0, opts.limit);
19373
19648
  return all;
19374
19649
  }
19650
+ async getSessionMeta(conversationId) {
19651
+ const meta = this.#meta.get(conversationId);
19652
+ return meta === void 0 ? void 0 : { ...meta };
19653
+ }
19654
+ async setSessionMeta(conversationId, patch) {
19655
+ const current = this.#meta.get(conversationId) ?? {};
19656
+ this.#meta.set(conversationId, applyMetaPatch(current, patch));
19657
+ }
19375
19658
  async dispose() {
19376
19659
  this.#store.clear();
19660
+ this.#meta.clear();
19377
19661
  }
19378
19662
  };
19379
19663
 
@@ -20113,6 +20397,21 @@ async function migrateSqliteToLance2(options) {
20113
20397
  }
20114
20398
 
20115
20399
  // src/permission-engine.ts
20400
+ function applyMode(verdict, mode, explicit) {
20401
+ if (verdict === "deny") return "deny";
20402
+ switch (mode) {
20403
+ case "default":
20404
+ return verdict;
20405
+ case "plan":
20406
+ return verdict === "allow" ? "allow" : "deny";
20407
+ case "acceptEdits":
20408
+ if (verdict === "ask") return explicit ? "ask" : "allow";
20409
+ return verdict;
20410
+ // allow stays allow
20411
+ case "bypass":
20412
+ return "allow";
20413
+ }
20414
+ }
20116
20415
  function argMatches(matcher, value) {
20117
20416
  if (typeof matcher === "function") return matcher(value);
20118
20417
  if (value === void 0) return false;
@@ -20133,14 +20432,14 @@ var PermissionEngine = class {
20133
20432
  * argument values, so the same tool name can resolve to different actions
20134
20433
  * depending on what it is asked to do.
20135
20434
  */
20136
- evaluate(toolName, args) {
20435
+ evaluate(toolName, args, mode = "default") {
20137
20436
  for (const rule of this.rules) {
20138
20437
  const nameMatches = typeof rule.tool === "string" ? rule.tool === toolName : rule.tool.test(toolName);
20139
20438
  if (!nameMatches) continue;
20140
20439
  if (rule.args !== void 0 && !this.#argsMatch(rule.args, args)) continue;
20141
- return rule.action;
20440
+ return applyMode(rule.action, mode, true);
20142
20441
  }
20143
- return this.defaultAction;
20442
+ return applyMode(this.defaultAction, mode, false);
20144
20443
  }
20145
20444
  #argsMatch(matchers, args) {
20146
20445
  const call = args ?? {};
@@ -20152,21 +20451,32 @@ var PermissionEngine = class {
20152
20451
  };
20153
20452
 
20154
20453
  // src/permission-plugin.ts
20454
+ async function resolveAsk(opts, name, args, mode) {
20455
+ if (opts.canUseTool !== void 0) {
20456
+ let decision;
20457
+ try {
20458
+ decision = await opts.canUseTool(name, args, { toolName: name, mode });
20459
+ } catch {
20460
+ return { block: true, message: `permission gate error (fail-closed): ${name}` };
20461
+ }
20462
+ return decision.behavior === "deny" ? { block: true, message: decision.message ?? `denied: ${name}` } : void 0;
20463
+ }
20464
+ return opts.onAsk ? opts.onAsk(name) : { block: true, message: `requires approval: ${name}` };
20465
+ }
20155
20466
  function createPermissionPlugin(engine, opts = {}) {
20467
+ const mode = opts.mode ?? "default";
20156
20468
  return definePlugin({
20157
20469
  name: opts.name ?? "permission-engine",
20158
20470
  version: "1.0.0",
20159
20471
  kind: "general",
20160
20472
  register(ctx) {
20161
- ctx.on("pre_tool_call", (rawCtx) => {
20473
+ ctx.on("pre_tool_call", async (rawCtx) => {
20162
20474
  const { name, args } = rawCtx;
20163
- const action = engine.evaluate(name, args);
20475
+ const action = engine.evaluate(name, args, mode);
20164
20476
  if (action === "deny") {
20165
20477
  return { block: true, message: `denied by permission engine: ${name}` };
20166
20478
  }
20167
- if (action === "ask") {
20168
- return opts.onAsk ? opts.onAsk(name) : { block: true, message: `requires approval: ${name}` };
20169
- }
20479
+ if (action === "ask") return resolveAsk(opts, name, args, mode);
20170
20480
  return void 0;
20171
20481
  });
20172
20482
  }
@@ -20272,6 +20582,96 @@ var Security = class {
20272
20582
  }
20273
20583
  };
20274
20584
 
20585
+ // src/session-manager.ts
20586
+ var SUMMARY_PREVIEW_MAX = 80;
20587
+ function createSessionManager(storage2) {
20588
+ return {
20589
+ async listSessions(opts) {
20590
+ if (typeof storage2.listConversationIds !== "function") {
20591
+ return {
20592
+ supported: false,
20593
+ reason: "storage adapter does not support listing conversations (listConversationIds)"
20594
+ };
20595
+ }
20596
+ const ids = await storage2.listConversationIds();
20597
+ if (ids === void 0) {
20598
+ return {
20599
+ supported: false,
20600
+ reason: "storage adapter reported listing as unsupported (listConversationIds \u2192 undefined)"
20601
+ };
20602
+ }
20603
+ const windowed = windowIds(ids, opts);
20604
+ const summaries = [];
20605
+ for (const id of windowed) {
20606
+ summaries.push(await buildSummary(storage2, id));
20607
+ }
20608
+ return { supported: true, value: summaries };
20609
+ },
20610
+ getSessionMessages(id, opts) {
20611
+ return storage2.getMessages(id, opts);
20612
+ },
20613
+ async renameSession(id, title) {
20614
+ if (typeof storage2.setSessionMeta !== "function") {
20615
+ return unsupportedWrite();
20616
+ }
20617
+ await storage2.setSessionMeta(id, { title });
20618
+ return { supported: true, value: void 0 };
20619
+ },
20620
+ async tagSession(id, tag) {
20621
+ if (typeof storage2.setSessionMeta !== "function") {
20622
+ return unsupportedWrite();
20623
+ }
20624
+ await storage2.setSessionMeta(id, { tag });
20625
+ return { supported: true, value: void 0 };
20626
+ }
20627
+ };
20628
+ }
20629
+ function unsupportedWrite() {
20630
+ return {
20631
+ supported: false,
20632
+ reason: "storage adapter does not support writing session metadata (setSessionMeta)"
20633
+ };
20634
+ }
20635
+ function windowIds(ids, opts) {
20636
+ const start = opts?.offset !== void 0 && opts.offset > 0 ? opts.offset : 0;
20637
+ const rawLimit = opts?.limit;
20638
+ if (rawLimit !== void 0 && rawLimit <= 0) return [];
20639
+ const end = rawLimit !== void 0 ? start + rawLimit : void 0;
20640
+ return ids.slice(start, end);
20641
+ }
20642
+ async function buildSummary(storage2, id) {
20643
+ const messages = await storage2.getMessages(id);
20644
+ const firstUser = messages.find((m) => m.role === "user")?.content;
20645
+ const firstPrompt = firstUser !== void 0 && firstUser.length > 0 ? firstUser : void 0;
20646
+ const lastModified = deriveLastModified(messages);
20647
+ const meta = typeof storage2.getSessionMeta === "function" ? await storage2.getSessionMeta(id) : void 0;
20648
+ const title = meta?.title;
20649
+ const tag = meta?.tag;
20650
+ const summary = title ?? previewOf(firstPrompt);
20651
+ return {
20652
+ id,
20653
+ messageCount: messages.length,
20654
+ ...firstPrompt !== void 0 ? { firstPrompt } : {},
20655
+ ...lastModified !== void 0 ? { lastModified } : {},
20656
+ ...title !== void 0 ? { title } : {},
20657
+ ...tag !== void 0 ? { tag } : {},
20658
+ ...summary !== void 0 ? { summary } : {}
20659
+ };
20660
+ }
20661
+ function deriveLastModified(messages) {
20662
+ let last;
20663
+ for (const m of messages) {
20664
+ if (m.at !== void 0 && (last === void 0 || m.at > last)) last = m.at;
20665
+ }
20666
+ return last;
20667
+ }
20668
+ function previewOf(text) {
20669
+ if (text === void 0) return void 0;
20670
+ const oneLine = text.replace(/\s+/g, " ").trim();
20671
+ if (oneLine.length === 0) return void 0;
20672
+ return oneLine.length > SUMMARY_PREVIEW_MAX ? `${oneLine.slice(0, SUMMARY_PREVIEW_MAX - 1)}\u2026` : oneLine;
20673
+ }
20674
+
20275
20675
  // src/session-scope.ts
20276
20676
  function scopedConversationId(scope, id) {
20277
20677
  return `${scope}__${id}`;
@@ -20292,7 +20692,7 @@ var PersistenceSchema = z.object({
20292
20692
  // src/workflow.ts
20293
20693
  init_path_guard();
20294
20694
  init_workflow();
20295
- z.object({
20695
+ var RetryPolicySchema = z.object({
20296
20696
  // EC-3 absorbed: maxAttempts MUST be a finite int in [1, 20].
20297
20697
  maxAttempts: z.number().int("retry.maxAttempts must be integer").min(1, "retry.maxAttempts must be >= 1").max(20, "retry.maxAttempts must be <= 20"),
20298
20698
  initialBackoffMs: z.number().int().min(0).optional(),
@@ -20490,12 +20890,15 @@ var Workflow = class {
20490
20890
  };
20491
20891
  function agentStep(id, agent, promptTemplate, opts) {
20492
20892
  validateStepId(id);
20893
+ if (opts?.retry !== void 0) RetryPolicySchema.parse(opts.retry);
20493
20894
  return {
20494
20895
  kind: "agent",
20495
20896
  id,
20496
20897
  agent,
20497
20898
  promptTemplate,
20498
- ...{}
20899
+ ...opts?.retry !== void 0 ? { retry: opts.retry } : {},
20900
+ // SE3 — carry the turn's provenance down to `agent.send()` (metadata-only).
20901
+ ...opts?.origin !== void 0 ? { origin: opts.origin } : {}
20499
20902
  };
20500
20903
  }
20501
20904
  function validateStepId(id) {
@@ -20543,7 +20946,8 @@ function createSquad(options) {
20543
20946
  for (let i = 0; i < agents2.length; i++) {
20544
20947
  const agent = agents2[i];
20545
20948
  if (agent === void 0) continue;
20546
- builder = builder.then(agentStep(`agent-${i}`, agent, (prev) => String(prev)));
20949
+ const opts = i > 0 ? { origin: { kind: "peer", from: `agent-${i - 1}` } } : void 0;
20950
+ builder = builder.then(agentStep(`agent-${i}`, agent, (prev) => String(prev), opts));
20547
20951
  }
20548
20952
  const run = await builder.commit().run(input);
20549
20953
  return { result: run.output, status: run.status, steps: run.stepResults };
@@ -20997,6 +21401,6 @@ function safeStringify2(v) {
20997
21401
  }
20998
21402
  }
20999
21403
 
21000
- export { Agent, AgentBuilder, AgentDisposedError, AgentRunError, AuthenticationError, Budget, BudgetExceededError, ConfigurationError, Cron, EventBus, FileSystemConversationStorage, GenerateObjectError, InMemoryConversationStorage, IntegrationNotConnectedError, InvalidTaskIdError, JobQueue, Memory, MemoryAdapterError, NetworkError, PermissionEngine, RateLimitError, Security, StreamObjectError, Task, TaskNotFoundError, Theokit, TheokitAgentError, UnknownAgentError, UnsupportedBudgetOperationError, UnsupportedRunOperationError, UnsupportedTaskOperationError, UsageAccumulator, buildReplayHistory, chargeAndCheckThresholds, computeCost, createAgentFactory, createCounterBudgetTracker, createNoopMemoryProvider, createPermissionPlugin, createSkill, createSquad, definePlugin, defineProvider, defineTool, extractRawId, getPricingEntry, inferApiMode, isTransientError, migrateSqliteToLance2 as migrateSqliteToLance, mkMemoryId, normalizeSchema, normalizeUsage, preflightCheck, scopedConversationId, sessionScopePrefix, toShareGptTrajectory, withCwdMutex };
21404
+ export { Agent, AgentBuilder, AgentDisposedError, AgentRunError, AuthenticationError, Budget, BudgetExceededError, ConfigurationError, Cron, EventBus, FileSystemConversationStorage, GenerateObjectError, InMemoryConversationStorage, IntegrationNotConnectedError, InvalidTaskIdError, JobQueue, Memory, MemoryAdapterError, NetworkError, PermissionEngine, RateLimitError, Security, StreamObjectError, Task, TaskNotFoundError, Theokit, TheokitAgentError, ToolError, UnknownAgentError, UnsupportedBudgetOperationError, UnsupportedRunOperationError, UnsupportedTaskOperationError, UsageAccumulator, applyMode, buildReplayHistory, chargeAndCheckThresholds, computeCost, createAgentFactory, createCounterBudgetTracker, createNoopMemoryProvider, createPermissionPlugin, createSessionManager, createSkill, createSquad, definePlugin, defineProvider, defineTool, emitRunEvent, extractRawId, getPricingEntry, inferApiMode, isTransientError, migrateSqliteToLance2 as migrateSqliteToLance, mkMemoryId, normalizeSchema, normalizeUsage, preflightCheck, scopedConversationId, sessionScopePrefix, toShareGptTrajectory, withCwdMutex };
21001
21405
  //# sourceMappingURL=index.js.map
21002
21406
  //# sourceMappingURL=index.js.map