@theokit/sdk 2.20.0 → 2.21.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 (88) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/dist/a2a/index.cjs +592 -290
  3. package/dist/a2a/index.cjs.map +1 -1
  4. package/dist/a2a/index.js +593 -291
  5. package/dist/a2a/index.js.map +1 -1
  6. package/dist/a2a/types.d.cts +7 -0
  7. package/dist/a2a/types.d.ts +7 -0
  8. package/dist/agent-builder.d.ts +2 -1
  9. package/dist/agent-generate.d.ts +12 -0
  10. package/dist/client/index.cjs.map +1 -1
  11. package/dist/client/index.d.cts +5 -0
  12. package/dist/client/index.d.ts +5 -0
  13. package/dist/client/index.js.map +1 -1
  14. package/dist/client/theokit-client.d.cts +6 -0
  15. package/dist/client/theokit-client.d.ts +6 -0
  16. package/dist/client/types.d.cts +2 -0
  17. package/dist/client/types.d.ts +2 -0
  18. package/dist/{cron-D2yLOrk2.d.ts → cron-YrmsszEN.d.cts} +53 -3
  19. package/dist/{cron-B8fqui49.d.cts → cron-dpvtRoro.d.ts} +53 -3
  20. package/dist/cron.cjs +642 -387
  21. package/dist/cron.cjs.map +1 -1
  22. package/dist/cron.d.cts +3 -2
  23. package/dist/cron.d.ts +3 -2
  24. package/dist/cron.js +643 -388
  25. package/dist/cron.js.map +1 -1
  26. package/dist/{errors-5lj1EWgs.d.ts → errors-C4vZPqXf.d.ts} +2 -2
  27. package/dist/{errors-CE-lMBi2.d.cts → errors-DrcpYVfZ.d.cts} +2 -2
  28. package/dist/errors.d.cts +3 -2
  29. package/dist/eval.cjs +642 -387
  30. package/dist/eval.cjs.map +1 -1
  31. package/dist/eval.js +642 -387
  32. package/dist/eval.js.map +1 -1
  33. package/dist/generate-object.d.ts +4 -3
  34. package/dist/index.cjs +788 -390
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +259 -17
  37. package/dist/index.d.ts +259 -17
  38. package/dist/index.js +786 -392
  39. package/dist/index.js.map +1 -1
  40. package/dist/internal/agent-loop/loop-types.d.ts +2 -0
  41. package/dist/internal/agent-loop/tool-result-guard.d.ts +1 -0
  42. package/dist/internal/llm/tool-result-content.d.ts +40 -0
  43. package/dist/internal/llm/types.d.ts +6 -1
  44. package/dist/internal/persistence/conversation-storage-fs.d.cts +3 -1
  45. package/dist/internal/persistence/conversation-storage-fs.d.ts +3 -1
  46. package/dist/internal/persistence/conversation-storage-memory.d.cts +3 -1
  47. package/dist/internal/persistence/conversation-storage-memory.d.ts +3 -1
  48. package/dist/internal/persistence/session-meta.d.cts +13 -0
  49. package/dist/internal/persistence/session-meta.d.ts +13 -0
  50. package/dist/internal/runtime/model-selection.d.ts +14 -0
  51. package/dist/internal/structured-output-helpers.d.ts +1 -1
  52. package/dist/permission-engine.d.ts +32 -1
  53. package/dist/{run-BgfBWX-z.d.cts → run-BMo8yRwK.d.cts} +262 -19
  54. package/dist/{run-BgfBWX-z.d.ts → run-BMo8yRwK.d.ts} +262 -19
  55. package/dist/stream-object.d.ts +2 -1
  56. package/dist/tool-error.d.ts +30 -0
  57. package/dist/types/agent-prims.d.ts +10 -8
  58. package/dist/types/agent.d.ts +12 -1
  59. package/dist/types/content-blocks.d.ts +47 -0
  60. package/dist/types/conversation-storage.d.ts +38 -0
  61. package/dist/types/index.d.ts +1 -0
  62. package/dist/types/messages.d.ts +2 -9
  63. package/dist/types/run-events.d.ts +84 -0
  64. package/dist/types/run.d.ts +115 -1
  65. package/dist/types/session.d.ts +99 -0
  66. package/dist/types/workflow.d.ts +7 -0
  67. package/dist/workflow.cjs +7 -2
  68. package/dist/workflow.cjs.map +1 -1
  69. package/dist/workflow.d.cts +2 -0
  70. package/dist/workflow.d.ts +2 -0
  71. package/dist/workflow.js +7 -2
  72. package/dist/workflow.js.map +1 -1
  73. package/package.json +14 -34
  74. package/dist/internal/observability/context.d.cts +0 -23
  75. package/dist/internal/observability/context.d.ts +0 -23
  76. package/dist/internal/observability/index.cjs +0 -38
  77. package/dist/internal/observability/index.cjs.map +0 -1
  78. package/dist/internal/observability/index.d.cts +0 -8
  79. package/dist/internal/observability/index.d.ts +0 -8
  80. package/dist/internal/observability/index.js +0 -33
  81. package/dist/internal/observability/index.js.map +0 -1
  82. package/dist/internal/plugins/index.cjs +0 -363
  83. package/dist/internal/plugins/index.cjs.map +0 -1
  84. package/dist/internal/plugins/index.js +0 -357
  85. package/dist/internal/plugins/index.js.map +0 -1
  86. package/dist/internal/workflow/index.d.ts +0 -12
  87. package/dist/server/adapter/index.d.cts +0 -8
  88. 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
  }
@@ -3608,334 +3835,107 @@ async function submit(internal) {
3608
3835
  state.aborters.set(resolvedId, aborter);
3609
3836
  if (internal.signal !== void 0) {
3610
3837
  internal.signal.addEventListener("abort", () => aborter.abort(internal.signal?.reason), {
3611
- once: true
3612
- });
3613
- }
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
- };
3838
+ once: true
3839
+ });
3840
+ }
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)) {
@@ -11759,9 +11930,13 @@ async function runHandlerTool(kind, handler, call, signal, context) {
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 });
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
 
@@ -13047,10 +13282,10 @@ function stripVertexPrefix(modelId) {
13047
13282
  }
13048
13283
 
13049
13284
  // src/internal/providers/builtin/index.ts
13050
- var registered2 = false;
13285
+ var registered3 = false;
13051
13286
  function registerBuiltins() {
13052
- if (registered2) return;
13053
- registered2 = true;
13287
+ if (registered3) return;
13288
+ registered3 = true;
13054
13289
  registerProvider(ANTHROPIC);
13055
13290
  registerProvider(OPENAI);
13056
13291
  registerProvider(OPENROUTER);
@@ -13279,7 +13514,9 @@ function toAnthropicWireMessage(message) {
13279
13514
  return {
13280
13515
  type: "tool_result",
13281
13516
  tool_use_id: part.toolUseId,
13282
- content: part.content,
13517
+ // SE7 — this wire is block-capable: strings pass through, structured
13518
+ // text/image blocks are forwarded natively.
13519
+ content: toBlockToolResultContent(part.content),
13283
13520
  ...part.isError === true ? { is_error: true } : {}
13284
13521
  };
13285
13522
  });
@@ -14071,7 +14308,8 @@ function toOllamaMessages(message) {
14071
14308
  if (part.type === "tool_result") {
14072
14309
  out.push({
14073
14310
  role: "tool",
14074
- content: part.content,
14311
+ // SE7 — string-only tool role: text blocks flatten; image fails fast.
14312
+ content: toStringToolResultContent(part.content, "ollama"),
14075
14313
  tool_name: part.toolUseId
14076
14314
  });
14077
14315
  }
@@ -14608,7 +14846,9 @@ function userOrToolMessages(message) {
14608
14846
  out.push({
14609
14847
  role: "tool",
14610
14848
  tool_call_id: part.toolUseId,
14611
- content: part.content
14849
+ // SE7 — this wire's tool role is string-only: text blocks flatten; an
14850
+ // image block fails fast (ConfigurationError).
14851
+ content: toStringToolResultContent(part.content, "openai")
14612
14852
  });
14613
14853
  }
14614
14854
  }
@@ -15538,18 +15778,20 @@ function levenshtein(a, b) {
15538
15778
  function createRealLocalRun(options) {
15539
15779
  const { userText, id, startTime } = prepareRunContext(options.message);
15540
15780
  const supported = /* @__PURE__ */ new Set(["stream", "wait", "cancel", "conversation"]);
15541
- const unusedFixtureScript = {
15781
+ const runScript = {
15542
15782
  events: [],
15543
15783
  finalStatus: "running",
15544
15784
  cancellable: false,
15545
- conversation: []
15785
+ conversation: [],
15786
+ // SE3 — carry the turn's provenance so buildResult forwards it onto RunResult.origin.
15787
+ ...options.sendOptions.origin !== void 0 ? { origin: options.sendOptions.origin } : {}
15546
15788
  };
15547
15789
  const handle = new RealLocalRun(
15548
15790
  {
15549
15791
  id,
15550
15792
  agentId: options.agentId,
15551
15793
  model: options.model,
15552
- script: unusedFixtureScript,
15794
+ script: runScript,
15553
15795
  supportedOps: supported,
15554
15796
  startTime
15555
15797
  },
@@ -15661,6 +15903,8 @@ function buildLoopInputs(options, runId, userText) {
15661
15903
  // M7 — forward SendOptions.context to the loop so every tool handler receives
15662
15904
  // it on `ctx.context` (shared run config set once, e.g. projectRoot).
15663
15905
  ...options.sendOptions.context !== void 0 ? { context: options.sendOptions.context } : {},
15906
+ // SE2 — forward the opt-in typed runtime-event sink to the loop.
15907
+ ...options.sendOptions.onRunEvent !== void 0 ? { runEventSink: options.sendOptions.onRunEvent } : {},
15664
15908
  // #58 / #57 — forward the per-tool timeout + tool-result guard so a consumer
15665
15909
  // can enable them via SendOptions (not only internal AgentLoopInputs).
15666
15910
  ...options.sendOptions.perToolTimeoutMs !== void 0 ? { perToolTimeoutMs: options.sendOptions.perToolTimeoutMs } : {},
@@ -17405,7 +17649,7 @@ function wrapRunWithPostReplyHook(args) {
17405
17649
  async function executeSendLocked(inputs, message, options) {
17406
17650
  if (inputs.disposed) throw new AgentDisposedError(inputs.agentId);
17407
17651
  await consumePending(inputs.agentId, inputs.invalidationPending, inputs.clearInvalidation, inputs.reload);
17408
- inputs.applyModelOverride(options.model);
17652
+ inputs.applyModelOverride(normalizeModel(options.model));
17409
17653
  const userText = typeof message === "string" ? message : message.text;
17410
17654
  if (inputs.options.onBeforeSend !== void 0) {
17411
17655
  await inputs.options.onBeforeSend({
@@ -17557,7 +17801,7 @@ var LocalAgent = class {
17557
17801
  constructor(options) {
17558
17802
  this.agentId = options.agentId ?? generateLocalAgentId();
17559
17803
  this._telemetry = createTelemetry(options.telemetry);
17560
- this.model = options.model;
17804
+ this.model = normalizeModel(options.model);
17561
17805
  this.options = options;
17562
17806
  this.workspaceCwd = resolveCwd(options.local?.cwd);
17563
17807
  this.conversationStorage = options.conversationStorage;
@@ -17624,6 +17868,11 @@ var LocalAgent = class {
17624
17868
  hooks() {
17625
17869
  return this.hooksExecutor;
17626
17870
  }
17871
+ // SE9 — integrated structured output; delegates to the shared helper.
17872
+ generate(message, options) {
17873
+ const { apiKey, local } = this.options;
17874
+ return agentGenerate(this, this.model, apiKey, local, message, options);
17875
+ }
17627
17876
  async send(message, options = {}) {
17628
17877
  if (options.tools !== void 0 && options.tools.length > 0) {
17629
17878
  validateToolCatalog(options.tools);
@@ -17863,6 +18112,7 @@ function includesSetting(options, source) {
17863
18112
 
17864
18113
  // src/agent-helpers.ts
17865
18114
  async function runCreateUnderSpan(options, span) {
18115
+ options = { ...options, model: normalizeModel(options.model) };
17866
18116
  validateAgentOptions(options);
17867
18117
  validateCloudToolParity(options);
17868
18118
  await guardAgainstIdCollision(options);
@@ -17936,7 +18186,10 @@ async function createLocalAgent(options) {
17936
18186
  const willFlowToProvider = !isFixtureApiKey(apiKey) && !shouldUseRealLocalRuntime(apiKey);
17937
18187
  const shape = validateApiKeyShape(apiKey, {
17938
18188
  strict: willFlowToProvider,
17939
- ...willFlowToProvider ? { provider: providerFromModelId(options.model?.id) } : {}
18189
+ // `options.model` is already normalized by `runCreateUnderSpan`; `normalizeModel`
18190
+ // here is the idempotent narrowing of the widened public type (`{id}` passes
18191
+ // through by reference) — cleaner than an `as` cast.
18192
+ ...willFlowToProvider ? { provider: providerFromModelId(normalizeModel(options.model)?.id) } : {}
17940
18193
  });
17941
18194
  if (shape.malformed) {
17942
18195
  throw new AuthenticationError(shape.message, { code: "malformed_api_key" });
@@ -18002,6 +18255,10 @@ async function rehydrateExistingAgent(agentId, existing, options) {
18002
18255
  ...existing.options,
18003
18256
  ...options,
18004
18257
  ...mergedLocal !== void 0 ? { local: mergedLocal } : {},
18258
+ // SE8 — normalize a caller-supplied bare-string model override at this resume
18259
+ // boundary (Agent.resume never flows through runCreateUnderSpan). Cloud resume
18260
+ // honors it; local resume forces the persisted model below.
18261
+ model: normalizeModel(options.model ?? existing.options.model),
18005
18262
  mcpServers: void 0,
18006
18263
  agentId
18007
18264
  };
@@ -18368,6 +18625,7 @@ async function getOrCreateUncached(agentId, options) {
18368
18625
  }
18369
18626
  setAgentFacade({
18370
18627
  create: (options) => Agent.create(options),
18628
+ delete: (agentId) => Agent.delete(agentId),
18371
18629
  prompt: (message, options) => Agent.prompt(message, options),
18372
18630
  get: (agentId) => Agent.get(agentId),
18373
18631
  resume: (agentId, options) => Agent.resume(agentId, options),
@@ -19328,6 +19586,8 @@ init_generate_object();
19328
19586
  // src/internal/persistence/conversation-storage-memory.ts
19329
19587
  var InMemoryConversationStorage = class {
19330
19588
  #store = /* @__PURE__ */ new Map();
19589
+ // SE4 — session-level metadata (title/tag), kept beside the transcript.
19590
+ #meta = /* @__PURE__ */ new Map();
19331
19591
  async getMessages(conversationId, opts) {
19332
19592
  const existing = this.#store.get(conversationId);
19333
19593
  return existing === void 0 ? [] : paginate(existing.slice(), opts);
@@ -19356,15 +19616,20 @@ var InMemoryConversationStorage = class {
19356
19616
  }
19357
19617
  async deleteConversation(conversationId) {
19358
19618
  this.#store.delete(conversationId);
19619
+ this.#meta.delete(conversationId);
19359
19620
  }
19360
19621
  async deleteScope(prefix) {
19361
19622
  let n = 0;
19362
19623
  for (const id of [...this.#store.keys()]) {
19363
19624
  if (id.startsWith(prefix)) {
19364
19625
  this.#store.delete(id);
19626
+ this.#meta.delete(id);
19365
19627
  n += 1;
19366
19628
  }
19367
19629
  }
19630
+ for (const id of [...this.#meta.keys()]) {
19631
+ if (id.startsWith(prefix)) this.#meta.delete(id);
19632
+ }
19368
19633
  return n;
19369
19634
  }
19370
19635
  async listConversationIds(opts) {
@@ -19372,8 +19637,17 @@ var InMemoryConversationStorage = class {
19372
19637
  if (opts?.limit !== void 0) return all.slice(0, opts.limit);
19373
19638
  return all;
19374
19639
  }
19640
+ async getSessionMeta(conversationId) {
19641
+ const meta = this.#meta.get(conversationId);
19642
+ return meta === void 0 ? void 0 : { ...meta };
19643
+ }
19644
+ async setSessionMeta(conversationId, patch) {
19645
+ const current = this.#meta.get(conversationId) ?? {};
19646
+ this.#meta.set(conversationId, applyMetaPatch(current, patch));
19647
+ }
19375
19648
  async dispose() {
19376
19649
  this.#store.clear();
19650
+ this.#meta.clear();
19377
19651
  }
19378
19652
  };
19379
19653
 
@@ -20113,6 +20387,21 @@ async function migrateSqliteToLance2(options) {
20113
20387
  }
20114
20388
 
20115
20389
  // src/permission-engine.ts
20390
+ function applyMode(verdict, mode, explicit) {
20391
+ if (verdict === "deny") return "deny";
20392
+ switch (mode) {
20393
+ case "default":
20394
+ return verdict;
20395
+ case "plan":
20396
+ return verdict === "allow" ? "allow" : "deny";
20397
+ case "acceptEdits":
20398
+ if (verdict === "ask") return explicit ? "ask" : "allow";
20399
+ return verdict;
20400
+ // allow stays allow
20401
+ case "bypass":
20402
+ return "allow";
20403
+ }
20404
+ }
20116
20405
  function argMatches(matcher, value) {
20117
20406
  if (typeof matcher === "function") return matcher(value);
20118
20407
  if (value === void 0) return false;
@@ -20133,14 +20422,14 @@ var PermissionEngine = class {
20133
20422
  * argument values, so the same tool name can resolve to different actions
20134
20423
  * depending on what it is asked to do.
20135
20424
  */
20136
- evaluate(toolName, args) {
20425
+ evaluate(toolName, args, mode = "default") {
20137
20426
  for (const rule of this.rules) {
20138
20427
  const nameMatches = typeof rule.tool === "string" ? rule.tool === toolName : rule.tool.test(toolName);
20139
20428
  if (!nameMatches) continue;
20140
20429
  if (rule.args !== void 0 && !this.#argsMatch(rule.args, args)) continue;
20141
- return rule.action;
20430
+ return applyMode(rule.action, mode, true);
20142
20431
  }
20143
- return this.defaultAction;
20432
+ return applyMode(this.defaultAction, mode, false);
20144
20433
  }
20145
20434
  #argsMatch(matchers, args) {
20146
20435
  const call = args ?? {};
@@ -20152,21 +20441,32 @@ var PermissionEngine = class {
20152
20441
  };
20153
20442
 
20154
20443
  // src/permission-plugin.ts
20444
+ async function resolveAsk(opts, name, args, mode) {
20445
+ if (opts.canUseTool !== void 0) {
20446
+ let decision;
20447
+ try {
20448
+ decision = await opts.canUseTool(name, args, { toolName: name, mode });
20449
+ } catch {
20450
+ return { block: true, message: `permission gate error (fail-closed): ${name}` };
20451
+ }
20452
+ return decision.behavior === "deny" ? { block: true, message: decision.message ?? `denied: ${name}` } : void 0;
20453
+ }
20454
+ return opts.onAsk ? opts.onAsk(name) : { block: true, message: `requires approval: ${name}` };
20455
+ }
20155
20456
  function createPermissionPlugin(engine, opts = {}) {
20457
+ const mode = opts.mode ?? "default";
20156
20458
  return definePlugin({
20157
20459
  name: opts.name ?? "permission-engine",
20158
20460
  version: "1.0.0",
20159
20461
  kind: "general",
20160
20462
  register(ctx) {
20161
- ctx.on("pre_tool_call", (rawCtx) => {
20463
+ ctx.on("pre_tool_call", async (rawCtx) => {
20162
20464
  const { name, args } = rawCtx;
20163
- const action = engine.evaluate(name, args);
20465
+ const action = engine.evaluate(name, args, mode);
20164
20466
  if (action === "deny") {
20165
20467
  return { block: true, message: `denied by permission engine: ${name}` };
20166
20468
  }
20167
- if (action === "ask") {
20168
- return opts.onAsk ? opts.onAsk(name) : { block: true, message: `requires approval: ${name}` };
20169
- }
20469
+ if (action === "ask") return resolveAsk(opts, name, args, mode);
20170
20470
  return void 0;
20171
20471
  });
20172
20472
  }
@@ -20272,6 +20572,96 @@ var Security = class {
20272
20572
  }
20273
20573
  };
20274
20574
 
20575
+ // src/session-manager.ts
20576
+ var SUMMARY_PREVIEW_MAX = 80;
20577
+ function createSessionManager(storage2) {
20578
+ return {
20579
+ async listSessions(opts) {
20580
+ if (typeof storage2.listConversationIds !== "function") {
20581
+ return {
20582
+ supported: false,
20583
+ reason: "storage adapter does not support listing conversations (listConversationIds)"
20584
+ };
20585
+ }
20586
+ const ids = await storage2.listConversationIds();
20587
+ if (ids === void 0) {
20588
+ return {
20589
+ supported: false,
20590
+ reason: "storage adapter reported listing as unsupported (listConversationIds \u2192 undefined)"
20591
+ };
20592
+ }
20593
+ const windowed = windowIds(ids, opts);
20594
+ const summaries = [];
20595
+ for (const id of windowed) {
20596
+ summaries.push(await buildSummary(storage2, id));
20597
+ }
20598
+ return { supported: true, value: summaries };
20599
+ },
20600
+ getSessionMessages(id, opts) {
20601
+ return storage2.getMessages(id, opts);
20602
+ },
20603
+ async renameSession(id, title) {
20604
+ if (typeof storage2.setSessionMeta !== "function") {
20605
+ return unsupportedWrite();
20606
+ }
20607
+ await storage2.setSessionMeta(id, { title });
20608
+ return { supported: true, value: void 0 };
20609
+ },
20610
+ async tagSession(id, tag) {
20611
+ if (typeof storage2.setSessionMeta !== "function") {
20612
+ return unsupportedWrite();
20613
+ }
20614
+ await storage2.setSessionMeta(id, { tag });
20615
+ return { supported: true, value: void 0 };
20616
+ }
20617
+ };
20618
+ }
20619
+ function unsupportedWrite() {
20620
+ return {
20621
+ supported: false,
20622
+ reason: "storage adapter does not support writing session metadata (setSessionMeta)"
20623
+ };
20624
+ }
20625
+ function windowIds(ids, opts) {
20626
+ const start = opts?.offset !== void 0 && opts.offset > 0 ? opts.offset : 0;
20627
+ const rawLimit = opts?.limit;
20628
+ if (rawLimit !== void 0 && rawLimit <= 0) return [];
20629
+ const end = rawLimit !== void 0 ? start + rawLimit : void 0;
20630
+ return ids.slice(start, end);
20631
+ }
20632
+ async function buildSummary(storage2, id) {
20633
+ const messages = await storage2.getMessages(id);
20634
+ const firstUser = messages.find((m) => m.role === "user")?.content;
20635
+ const firstPrompt = firstUser !== void 0 && firstUser.length > 0 ? firstUser : void 0;
20636
+ const lastModified = deriveLastModified(messages);
20637
+ const meta = typeof storage2.getSessionMeta === "function" ? await storage2.getSessionMeta(id) : void 0;
20638
+ const title = meta?.title;
20639
+ const tag = meta?.tag;
20640
+ const summary = title ?? previewOf(firstPrompt);
20641
+ return {
20642
+ id,
20643
+ messageCount: messages.length,
20644
+ ...firstPrompt !== void 0 ? { firstPrompt } : {},
20645
+ ...lastModified !== void 0 ? { lastModified } : {},
20646
+ ...title !== void 0 ? { title } : {},
20647
+ ...tag !== void 0 ? { tag } : {},
20648
+ ...summary !== void 0 ? { summary } : {}
20649
+ };
20650
+ }
20651
+ function deriveLastModified(messages) {
20652
+ let last;
20653
+ for (const m of messages) {
20654
+ if (m.at !== void 0 && (last === void 0 || m.at > last)) last = m.at;
20655
+ }
20656
+ return last;
20657
+ }
20658
+ function previewOf(text) {
20659
+ if (text === void 0) return void 0;
20660
+ const oneLine = text.replace(/\s+/g, " ").trim();
20661
+ if (oneLine.length === 0) return void 0;
20662
+ return oneLine.length > SUMMARY_PREVIEW_MAX ? `${oneLine.slice(0, SUMMARY_PREVIEW_MAX - 1)}\u2026` : oneLine;
20663
+ }
20664
+
20275
20665
  // src/session-scope.ts
20276
20666
  function scopedConversationId(scope, id) {
20277
20667
  return `${scope}__${id}`;
@@ -20292,7 +20682,7 @@ var PersistenceSchema = z.object({
20292
20682
  // src/workflow.ts
20293
20683
  init_path_guard();
20294
20684
  init_workflow();
20295
- z.object({
20685
+ var RetryPolicySchema = z.object({
20296
20686
  // EC-3 absorbed: maxAttempts MUST be a finite int in [1, 20].
20297
20687
  maxAttempts: z.number().int("retry.maxAttempts must be integer").min(1, "retry.maxAttempts must be >= 1").max(20, "retry.maxAttempts must be <= 20"),
20298
20688
  initialBackoffMs: z.number().int().min(0).optional(),
@@ -20490,12 +20880,15 @@ var Workflow = class {
20490
20880
  };
20491
20881
  function agentStep(id, agent, promptTemplate, opts) {
20492
20882
  validateStepId(id);
20883
+ if (opts?.retry !== void 0) RetryPolicySchema.parse(opts.retry);
20493
20884
  return {
20494
20885
  kind: "agent",
20495
20886
  id,
20496
20887
  agent,
20497
20888
  promptTemplate,
20498
- ...{}
20889
+ ...opts?.retry !== void 0 ? { retry: opts.retry } : {},
20890
+ // SE3 — carry the turn's provenance down to `agent.send()` (metadata-only).
20891
+ ...opts?.origin !== void 0 ? { origin: opts.origin } : {}
20499
20892
  };
20500
20893
  }
20501
20894
  function validateStepId(id) {
@@ -20543,7 +20936,8 @@ function createSquad(options) {
20543
20936
  for (let i = 0; i < agents2.length; i++) {
20544
20937
  const agent = agents2[i];
20545
20938
  if (agent === void 0) continue;
20546
- builder = builder.then(agentStep(`agent-${i}`, agent, (prev) => String(prev)));
20939
+ const opts = i > 0 ? { origin: { kind: "peer", from: `agent-${i - 1}` } } : void 0;
20940
+ builder = builder.then(agentStep(`agent-${i}`, agent, (prev) => String(prev), opts));
20547
20941
  }
20548
20942
  const run = await builder.commit().run(input);
20549
20943
  return { result: run.output, status: run.status, steps: run.stepResults };
@@ -20997,6 +21391,6 @@ function safeStringify2(v) {
20997
21391
  }
20998
21392
  }
20999
21393
 
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 };
21394
+ 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
21395
  //# sourceMappingURL=index.js.map
21002
21396
  //# sourceMappingURL=index.js.map