@superdurable/dex-agents 0.4.0 → 0.5.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 (132) hide show
  1. package/README.md +506 -52
  2. package/dist/src/agent-context-compaction.d.ts +1 -1
  3. package/dist/src/agent-framework-runner.d.ts +122 -11
  4. package/dist/src/agent-framework-runner.js +4 -0
  5. package/dist/src/agent-framework-runner.js.map +1 -1
  6. package/dist/src/agent-local-directory-media-store.d.ts +111 -0
  7. package/dist/src/agent-local-directory-media-store.js +417 -0
  8. package/dist/src/agent-local-directory-media-store.js.map +1 -0
  9. package/dist/src/agent-media.d.ts +279 -0
  10. package/dist/src/agent-media.js +457 -0
  11. package/dist/src/agent-media.js.map +1 -0
  12. package/dist/src/agent-session-codecs.d.ts +3 -1
  13. package/dist/src/agent-session-codecs.js +45 -4
  14. package/dist/src/agent-session-codecs.js.map +1 -1
  15. package/dist/src/agent-session-failure.d.ts +4 -2
  16. package/dist/src/agent-session-failure.js +6 -2
  17. package/dist/src/agent-session-failure.js.map +1 -1
  18. package/dist/src/agent-session-flow-graph.d.ts +46 -0
  19. package/dist/src/agent-session-flow-graph.js +51 -0
  20. package/dist/src/agent-session-flow-graph.js.map +1 -1
  21. package/dist/src/agent-session-flow.d.ts +5 -3
  22. package/dist/src/agent-session-flow.js +10 -1
  23. package/dist/src/agent-session-flow.js.map +1 -1
  24. package/dist/src/agent-session-start.d.ts +1 -1
  25. package/dist/src/agent-session-start.js +2 -2
  26. package/dist/src/agent-session-start.js.map +1 -1
  27. package/dist/src/agent-session-steps.d.ts +2 -0
  28. package/dist/src/agent-session-steps.js +52 -3
  29. package/dist/src/agent-session-steps.js.map +1 -1
  30. package/dist/src/agent-session-tracer.js +1 -1
  31. package/dist/src/agent-session-tracer.js.map +1 -1
  32. package/dist/src/agent-session-transitions.d.ts +2 -0
  33. package/dist/src/agent-session-transitions.js +21 -1
  34. package/dist/src/agent-session-transitions.js.map +1 -1
  35. package/dist/src/agent-session-values.d.ts +76 -1
  36. package/dist/src/agent-session-values.js.map +1 -1
  37. package/dist/src/agent-session-wait-steps.js +14 -2
  38. package/dist/src/agent-session-wait-steps.js.map +1 -1
  39. package/dist/src/ai-sdk/ai-sdk-model-client.d.ts +15 -3
  40. package/dist/src/ai-sdk/ai-sdk-model-client.js +36 -7
  41. package/dist/src/ai-sdk/ai-sdk-model-client.js.map +1 -1
  42. package/dist/src/ai-sdk/index.d.ts +1 -1
  43. package/dist/src/ai-sdk/index.js +1 -1
  44. package/dist/src/ai-sdk/index.js.map +1 -1
  45. package/dist/src/{stepwise → builtin-loop}/agent-definition.d.ts +120 -5
  46. package/dist/src/builtin-loop/agent-definition.js.map +1 -0
  47. package/dist/src/builtin-loop/builtin-agent-session-flow.d.ts +69 -0
  48. package/dist/src/{stepwise/stepwise-agent-session-flow.js → builtin-loop/builtin-agent-session-flow.js} +15 -9
  49. package/dist/src/builtin-loop/builtin-agent-session-flow.js.map +1 -0
  50. package/dist/src/{stepwise/stepwise-agent-runner.d.ts → builtin-loop/builtin-loop-agent-runner.d.ts} +72 -14
  51. package/dist/src/{stepwise/stepwise-agent-runner.js → builtin-loop/builtin-loop-agent-runner.js} +284 -41
  52. package/dist/src/builtin-loop/builtin-loop-agent-runner.js.map +1 -0
  53. package/dist/src/{stepwise/stepwise-input-items.d.ts → builtin-loop/builtin-loop-input-items.d.ts} +7 -3
  54. package/dist/src/{stepwise/stepwise-input-items.js → builtin-loop/builtin-loop-input-items.js} +57 -10
  55. package/dist/src/builtin-loop/builtin-loop-input-items.js.map +1 -0
  56. package/dist/src/builtin-loop/function-tool.d.ts +282 -0
  57. package/dist/src/{stepwise → builtin-loop}/function-tool.js +63 -3
  58. package/dist/src/builtin-loop/function-tool.js.map +1 -0
  59. package/dist/src/builtin-loop/index.d.ts +8 -0
  60. package/dist/src/builtin-loop/index.js +13 -0
  61. package/dist/src/builtin-loop/index.js.map +1 -0
  62. package/dist/src/builtin-loop/json-schema-validation.js.map +1 -0
  63. package/dist/src/{stepwise → builtin-loop}/model-client.d.ts +36 -7
  64. package/dist/src/builtin-loop/model-client.js.map +1 -0
  65. package/dist/src/{stepwise → builtin-loop}/scripted-model-client.d.ts +8 -5
  66. package/dist/src/{stepwise → builtin-loop}/scripted-model-client.js +8 -5
  67. package/dist/src/builtin-loop/scripted-model-client.js.map +1 -0
  68. package/dist/src/{stepwise → builtin-loop}/tool-source.d.ts +2 -2
  69. package/dist/src/builtin-loop/tool-source.js.map +1 -0
  70. package/dist/src/connectors/connector-input-schema.js +1 -1
  71. package/dist/src/connectors/connector-input-schema.js.map +1 -1
  72. package/dist/src/connectors/connector-step-context.d.ts +19 -2
  73. package/dist/src/connectors/connector-step-context.js +61 -5
  74. package/dist/src/connectors/connector-step-context.js.map +1 -1
  75. package/dist/src/connectors/connector-tool-source.d.ts +61 -13
  76. package/dist/src/connectors/connector-tool-source.js +111 -24
  77. package/dist/src/connectors/connector-tool-source.js.map +1 -1
  78. package/dist/src/index.d.ts +6 -4
  79. package/dist/src/index.js +3 -1
  80. package/dist/src/index.js.map +1 -1
  81. package/dist/src/mcp/index.d.ts +1 -0
  82. package/dist/src/mcp/index.js +1 -0
  83. package/dist/src/mcp/index.js.map +1 -1
  84. package/dist/src/mcp/mcp-dex-conventions.d.ts +57 -0
  85. package/dist/src/mcp/mcp-dex-conventions.js +90 -0
  86. package/dist/src/mcp/mcp-dex-conventions.js.map +1 -0
  87. package/dist/src/mcp/mcp-image-content.d.ts +20 -0
  88. package/dist/src/mcp/mcp-image-content.js +82 -0
  89. package/dist/src/mcp/mcp-image-content.js.map +1 -0
  90. package/dist/src/mcp/mcp-tool-source.d.ts +178 -17
  91. package/dist/src/mcp/mcp-tool-source.js +237 -34
  92. package/dist/src/mcp/mcp-tool-source.js.map +1 -1
  93. package/dist/src/s3/index.d.ts +1 -0
  94. package/dist/src/s3/index.js +9 -0
  95. package/dist/src/s3/index.js.map +1 -0
  96. package/dist/src/s3/s3-media-store.d.ts +138 -0
  97. package/dist/src/s3/s3-media-store.js +255 -0
  98. package/dist/src/s3/s3-media-store.js.map +1 -0
  99. package/dist/src/stepwise/index.d.ts +41 -8
  100. package/dist/src/stepwise/index.js +35 -5
  101. package/dist/src/stepwise/index.js.map +1 -1
  102. package/dist/src/testing/agent-session-test-client.d.ts +6 -3
  103. package/dist/src/testing/agent-session-test-client.js +10 -3
  104. package/dist/src/testing/agent-session-test-client.js.map +1 -1
  105. package/dist/src/testing/scripted-model-client.d.ts +1 -1
  106. package/dist/src/testing/scripted-model-client.js +2 -2
  107. package/dist/src/testing/scripted-model-client.js.map +1 -1
  108. package/dist/src/tool-source-router.d.ts +3 -3
  109. package/dist/src/tool-source-router.js +2 -2
  110. package/dist/src/ui-stream/agent-event-json.d.ts +2 -1
  111. package/dist/src/ui-stream/agent-event-json.js +3 -1
  112. package/dist/src/ui-stream/agent-event-json.js.map +1 -1
  113. package/dist/src/ui-stream/agent-ui-messages.d.ts +11 -3
  114. package/dist/src/ui-stream/agent-ui-messages.js +29 -5
  115. package/dist/src/ui-stream/agent-ui-messages.js.map +1 -1
  116. package/package.json +16 -3
  117. package/dist/src/stepwise/agent-definition.js.map +0 -1
  118. package/dist/src/stepwise/function-tool.d.ts +0 -130
  119. package/dist/src/stepwise/function-tool.js.map +0 -1
  120. package/dist/src/stepwise/json-schema-validation.js.map +0 -1
  121. package/dist/src/stepwise/model-client.js.map +0 -1
  122. package/dist/src/stepwise/scripted-model-client.js.map +0 -1
  123. package/dist/src/stepwise/stepwise-agent-runner.js.map +0 -1
  124. package/dist/src/stepwise/stepwise-agent-session-flow.d.ts +0 -35
  125. package/dist/src/stepwise/stepwise-agent-session-flow.js.map +0 -1
  126. package/dist/src/stepwise/stepwise-input-items.js.map +0 -1
  127. package/dist/src/stepwise/tool-source.js.map +0 -1
  128. /package/dist/src/{stepwise → builtin-loop}/agent-definition.js +0 -0
  129. /package/dist/src/{stepwise → builtin-loop}/json-schema-validation.d.ts +0 -0
  130. /package/dist/src/{stepwise → builtin-loop}/json-schema-validation.js +0 -0
  131. /package/dist/src/{stepwise → builtin-loop}/model-client.js +0 -0
  132. /package/dist/src/{stepwise → builtin-loop}/tool-source.js +0 -0
@@ -2,7 +2,7 @@ import type { AgentEventWriter, AgentFrameworkRunner, AgentTurnContext, Deferred
2
2
  import type { AgentMessage, AgentUsage, ContextSummary, SessionSettings } from "./agent-session-values.js";
3
3
  /**
4
4
  * When a session summarizes its earlier messages so the model input stays under the context window, passed
5
- * as {@link AgentSessionOptions.compaction} or as `AgentDefinition.compaction` for a Stepwise agent.
5
+ * as {@link AgentSessionOptions.compaction} or as `AgentDefinition.compaction` for a built-in loop agent.
6
6
  *
7
7
  * Before each model call, `RunModel` asks the runner for the size of the next model input. When it reaches
8
8
  * {@link triggerRatio} of {@link contextWindowTokens}, `RunModel` makes no model call and moves to
@@ -1,7 +1,7 @@
1
1
  import type { AsyncContext, RetryPolicy } from "@superdurable/dex";
2
2
  import type { BuiltInTool } from "./agent-built-in-tools.js";
3
3
  import type { ContextSummaryRequest, ContextSummaryResult } from "./agent-context-compaction.js";
4
- import type { AgentMessage, AgentPlan, AgentToolCall, AgentToolCallRecord, AgentToolCallStatus, AgentUsage, ContextSummary, SessionSettings, FrameworkMessage, InteractionMode, QuestionAnswer, ToolEffect } from "./agent-session-values.js";
4
+ import type { AgentImageReference, AgentMessage, AgentPlan, AgentToolCall, AgentToolCallRecord, AgentToolCallStatus, AgentUsage, ContextSummary, SessionSettings, FrameworkMessage, InteractionMode, QuestionAnswer, ToolEffect } from "./agent-session-values.js";
5
5
  /**
6
6
  * Per-tool override of the effect and approval defaults.
7
7
  *
@@ -135,11 +135,14 @@ export interface AgentTurnContext {
135
135
  */
136
136
  readonly messages: readonly AgentMessage[];
137
137
  /**
138
- * Framework run state from this turn's previous {@link ToolCallsRequested} outcome, or `null` for
139
- * the turn's first model call.
138
+ * Framework run state from this turn's previous {@link ToolCallsRequested} or {@link TurnContinued} outcome,
139
+ * or `null` for the turn's first model call.
140
140
  */
141
141
  readonly frameworkRunState: string | null;
142
- /** Whether this model call continues the turn after tool calls, that is, `frameworkRunState` is set. */
142
+ /**
143
+ * Whether this model call continues the turn after tool calls or after a {@link TurnContinued} outcome, that
144
+ * is, `frameworkRunState` is set.
145
+ */
143
146
  readonly isResuming: boolean;
144
147
  /**
145
148
  * Tool call records of this turn, in deferral order; every record is final. Give the framework the
@@ -307,6 +310,62 @@ export interface TurnCompleted {
307
310
  readonly usage: AgentUsage;
308
311
  /** Model and response of this model call, for the `chat` span. When omitted, the span is named `chat`. */
309
312
  readonly modelCallMetadata?: ModelCallMetadata;
313
+ /**
314
+ * Error text that ends the turn as failed instead of completed.
315
+ *
316
+ * When it is set, the session still stores {@link newMessages} and records {@link usage} in the same commit.
317
+ * It then sets the turn's `lastTurnError` to this text and writes a `turn_failed` event instead of
318
+ * `turn_completed`. No retry follows, so use it only for a deliberate stop that a retry would repeat, such as a
319
+ * limit the runner enforces. Throw instead for a failure that a retry may fix. Omit it, the default, to complete
320
+ * the turn. Snapshots and the UI stream show the text, so keep secrets out of it.
321
+ *
322
+ * @since 0.5.0
323
+ */
324
+ readonly turnError?: string;
325
+ }
326
+ /**
327
+ * Outcome of a model call that answered without tool calls, after which the turn continues with another model
328
+ * call.
329
+ *
330
+ * The session stores {@link newMessages}, records {@link usage}, keeps {@link frameworkRunState}, and moves to
331
+ * `RunModel` again, all in one commit, within the same turn. That next `RunModel` is its own Step, so a retry there
332
+ * repeats only its own model call. Its {@link AgentTurnContext.messages} include these messages. End them with a
333
+ * user message when the next request must end with one, as Anthropic models from Claude 4.6 on require. The
334
+ * built-in loop returns this outcome when `AgentDefinition.decideTurnContinuation` returns a continuation, with its
335
+ * message as the last of {@link newMessages}.
336
+ *
337
+ * A runner that returns it with messages must implement {@link AgentFrameworkRunner.buildUnfinishedTurnMessages},
338
+ * as for {@link ToolCallsRequested.newMessages}; the session fails the turn otherwise. A runner that never returns
339
+ * it needs no change.
340
+ *
341
+ * @example
342
+ * ```ts
343
+ * return {
344
+ * kind: "turn_continued",
345
+ * frameworkRunState: JSON.stringify({ model_calls: modelCalls + 1 }),
346
+ * newMessages: [...previousToolResults, ...thisResponse, continuationUserMessage],
347
+ * usage,
348
+ * };
349
+ * ```
350
+ * @since 0.5.0
351
+ */
352
+ export interface TurnContinued {
353
+ /** Discriminator. */
354
+ readonly kind: "turn_continued";
355
+ /**
356
+ * Non-empty serialized framework state from which the next `RunModel` continues the turn. The session keeps it
357
+ * opaquely in `FrameworkRunState`, as it keeps {@link ToolCallsRequested.frameworkRunState}.
358
+ */
359
+ readonly frameworkRunState: string;
360
+ /**
361
+ * Messages of the turn to store now: the previous model response's unstored tool results, if any, this model
362
+ * response, and any user message for the next call. The next {@link AgentTurnContext.messages} include them.
363
+ */
364
+ readonly newMessages: readonly FrameworkMessage[];
365
+ /** Usage of this model call only. */
366
+ readonly usage: AgentUsage;
367
+ /** Model and response of this model call, for the `chat` span. When omitted, the span is named `chat`. */
368
+ readonly modelCallMetadata?: ModelCallMetadata;
310
369
  }
311
370
  /** Outcome of a model call that requested tool calls. */
312
371
  export interface ToolCallsRequested {
@@ -339,8 +398,12 @@ export interface ToolCallsRequested {
339
398
  /** Model and response of this model call, for the `chat` span. When omitted, the span is named `chat`. */
340
399
  readonly modelCallMetadata?: ModelCallMetadata;
341
400
  }
342
- /** Result of {@link AgentFrameworkRunner.runModelStep}. */
343
- export type AgentTurnOutcome = TurnCompleted | ToolCallsRequested;
401
+ /**
402
+ * Result of {@link AgentFrameworkRunner.runModelStep}.
403
+ *
404
+ * Changed in 0.5.0: {@link TurnContinued} joined the union, so code that handles every kind handles it too.
405
+ */
406
+ export type AgentTurnOutcome = TurnCompleted | ToolCallsRequested | TurnContinued;
344
407
  /** One tool call that a tool Step asks the runner to run. */
345
408
  export interface DeferredToolExecution {
346
409
  /** Flow ID, which is the session ID. */
@@ -417,6 +480,24 @@ export interface ToolExecutionResult {
417
480
  * @since 0.4.0
418
481
  */
419
482
  readonly truncatedOutputBytes?: number;
483
+ /**
484
+ * Images the tool returns with {@link output}, uploaded with `MediaStore.putImage` inside the call, at most
485
+ * `maxImagesPerMessage`. The session stores them as {@link AgentToolCall.images}; a reference that
486
+ * `validateImageReference` refuses fails the attempt. Upload before returning: a retried attempt uploads the same
487
+ * bytes to the same object. Omit it for none.
488
+ * @since 0.5.0
489
+ */
490
+ readonly images?: readonly AgentImageReference[];
491
+ /**
492
+ * Opaque text the runner keeps with the call's result for its own later model calls, such as the framework
493
+ * state that the tool changed. The session stores it as {@link AgentToolCall.runnerToolState} of the
494
+ * `succeeded` record, and `runModelStep` reads it from `AgentTurnContext.toolCalls`. The session never interprets
495
+ * it and never shows it to the model, in the browser snapshot from `agentSnapshotToJson`, or in trace attributes.
496
+ * Only the attempt that commits the result stores it, so a retried attempt never adds a second copy. Omit it to
497
+ * store nothing.
498
+ * @since 0.5.0
499
+ */
500
+ readonly runnerToolState?: string;
420
501
  }
421
502
  /**
422
503
  * Timeout and retry policy of one tool call's Step, returned by
@@ -604,6 +685,10 @@ export type ToolTaskPollOutcome = ToolExecutionResult | ToolInputRequested | Too
604
685
  * `error`, does not retry, and the model sees {@link outcomeUnknownToolResult}. A later ordered write of
605
686
  * the turn is skipped.
606
687
  *
688
+ * {@link AgentFrameworkRunner.pollToolTask} may throw it too, for a task that ended without a known
689
+ * outcome. The session then records `outcome_unknown` the same way and stops polling. Changed in 0.5.0:
690
+ * `pollToolTask` may throw it.
691
+ *
607
692
  * @example
608
693
  * ```ts
609
694
  * throw new ToolOutcomeUnknownError("the provider timed out after receiving the refund");
@@ -699,12 +784,35 @@ export interface AgentFrameworkRunner<ToolOutcome extends DeferredToolOutcome =
699
784
  * @returns The framework message, with `role` `"user"`.
700
785
  */
701
786
  buildUserMessage(text: string): FrameworkMessage;
787
+ /**
788
+ * Converts a user message with images into the framework's user message. Optional.
789
+ *
790
+ * Implement it when the runner can show images to its model. `send_user_message` accepts images only for a runner
791
+ * with this method, and the session calls it instead of {@link buildUserMessage} for a message with images. Put
792
+ * the references, never the bytes, in `frameworkItem`; read the bytes with `loadImages` when the model call
793
+ * builds its input. The session sets the stored message's `images` itself. It must be synchronous,
794
+ * deterministic, and free of side effects.
795
+ *
796
+ * @example
797
+ * ```ts
798
+ * public buildUserMessageWithImages(text: string, images: readonly AgentImageReference[]): FrameworkMessage {
799
+ * return { role: "user", text, toolCallId: null, toolName: null, toolArguments: null, toolOutput: null,
800
+ * frameworkItem: JSON.stringify({ role: "user", content: text, images }) };
801
+ * }
802
+ * ```
803
+ * @param text - User message text, never blank.
804
+ * @param images - The message's images, at least one, each valid for `validateImageReference`.
805
+ * @returns The framework message, with `role` `"user"`.
806
+ * @since 0.5.0
807
+ */
808
+ buildUserMessageWithImages?(text: string, images: readonly AgentImageReference[]): FrameworkMessage;
702
809
  /**
703
810
  * Makes the turn's next model call, at most one.
704
811
  *
705
812
  * @param turn - Turn context; see {@link AgentTurnContext}.
706
- * @returns {@link TurnCompleted} with every new message of the turn, or {@link ToolCallsRequested}
707
- * with the framework run state and every tool call of the model response.
813
+ * @returns {@link TurnCompleted} with every new message of the turn, {@link ToolCallsRequested}
814
+ * with the framework run state and every tool call of the model response, or {@link TurnContinued} to make
815
+ * another model call within the same turn.
708
816
  * @throws Any error to retry the model call; after the last attempt the turn fails.
709
817
  */
710
818
  runModelStep(turn: AgentTurnContext): Promise<AgentTurnOutcome>;
@@ -741,8 +849,8 @@ export interface AgentFrameworkRunner<ToolOutcome extends DeferredToolOutcome =
741
849
  * Such a runner reads {@link AgentTurnContext.interactionMode} in every `runModelStep`. In a `planning`
742
850
  * turn it offers only the `write_todos` definition from {@link builtInToolDefinitions}. It may force
743
851
  * `write_todos` on the turn's first model call while the plan is not a draft yet, where the model accepts a
744
- * forced tool choice; Stepwise does with `planningToolChoice: "required"`. In every
745
- * turn it shows the plan with {@link buildPlanNote} as user text before the turn's messages. The Stepwise
852
+ * forced tool choice; the built-in loop does with `planningToolChoice: "required"`. In every
853
+ * turn it shows the plan with {@link buildPlanNote} as user text before the turn's messages. The built-in loop
746
854
  * runner implements it; a framework adapter that cannot change the tools it offers between turns leaves
747
855
  * plan mode off.
748
856
  */
@@ -771,6 +879,9 @@ export interface AgentFrameworkRunner<ToolOutcome extends DeferredToolOutcome =
771
879
  * the call's result.
772
880
  * @throws {ToolExecutionError} When the task failed in a way the model should see; the call is
773
881
  * recorded as `failed`.
882
+ * @throws {ToolOutcomeUnknownError} When the task ended and may or may not have taken effect; the call is
883
+ * recorded as `outcome_unknown` and not polled again. Changed in 0.5.0: this error is recorded instead of
884
+ * failing the poll attempt.
774
885
  * @throws Any other error to fail the poll attempt; after three attempts the record becomes `failed`
775
886
  * for a `read` call and `outcome_unknown` otherwise, because the task may still run.
776
887
  */
@@ -800,7 +911,7 @@ export interface AgentFrameworkRunner<ToolOutcome extends DeferredToolOutcome =
800
911
  /**
801
912
  * Returns the timeout and retry policy of one call's tool Step. Optional.
802
913
  *
803
- * Implement it when some tools need options other than the tool Steps' defaults, as the Stepwise runner
914
+ * Implement it when some tools need options other than the tool Steps' defaults, as the built-in loop runner
804
915
  * does for a tool source that defines `toolCallStepOptions`. The session calls it whenever it starts a
805
916
  * call's tool Step, including after an approval, so it must answer from the record alone. A method that
806
917
  * throws leaves the defaults.
@@ -38,6 +38,10 @@ export function resolveToolPolicy(toolName, toolPolicies, defaultToolEffect, fra
38
38
  * `error`, does not retry, and the model sees {@link outcomeUnknownToolResult}. A later ordered write of
39
39
  * the turn is skipped.
40
40
  *
41
+ * {@link AgentFrameworkRunner.pollToolTask} may throw it too, for a task that ended without a known
42
+ * outcome. The session then records `outcome_unknown` the same way and stops polling. Changed in 0.5.0:
43
+ * `pollToolTask` may throw it.
44
+ *
41
45
  * @example
42
46
  * ```ts
43
47
  * throw new ToolOutcomeUnknownError("the provider timed out after receiving the refund");
@@ -1 +1 @@
1
- {"version":3,"file":"agent-framework-runner.js","sourceRoot":"","sources":["../../src/agent-framework-runner.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,kDAAkD;AAClD,mEAAmE;AACnE,+CAA+C;AAC/C,EAAE;AACF,0DAA0D;AAqD1D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAgB,EAChB,YAAkD,EAClD,iBAA6B,EAC7B,eAA4B;IAE5B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1F,MAAM,MAAM,GAAG,MAAM,EAAE,MAAM,IAAI,eAAe,IAAI,iBAAiB,CAAC;IACtE,MAAM,gBAAgB,GACpB,MAAM,EAAE,gBAAgB,IAAI,CAAC,MAAM,KAAK,sBAAsB,IAAI,MAAM,KAAK,cAAc,CAAC,CAAC;IAC/F,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;AACtC,CAAC;AAmjBD;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD;;;;OAIG;IACH,YAAmB,OAAe,EAAE,OAAsB;QACxD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C;;;;OAIG;IACH,YAAmB,OAAe,EAAE,OAAsB;QACxD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAkPD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,gCAAgC,CAC9C,WAA+B;IAE/B,IAAI,WAAW,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,WAAW,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;QACxC,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"agent-framework-runner.js","sourceRoot":"","sources":["../../src/agent-framework-runner.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,kDAAkD;AAClD,mEAAmE;AACnE,+CAA+C;AAC/C,EAAE;AACF,0DAA0D;AAsD1D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAgB,EAChB,YAAkD,EAClD,iBAA6B,EAC7B,eAA4B;IAE5B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1F,MAAM,MAAM,GAAG,MAAM,EAAE,MAAM,IAAI,eAAe,IAAI,iBAAiB,CAAC;IACtE,MAAM,gBAAgB,GACpB,MAAM,EAAE,gBAAgB,IAAI,CAAC,MAAM,KAAK,sBAAsB,IAAI,MAAM,KAAK,cAAc,CAAC,CAAC;IAC/F,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;AACtC,CAAC;AAqoBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD;;;;OAIG;IACH,YAAmB,OAAe,EAAE,OAAsB;QACxD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C;;;;OAIG;IACH,YAAmB,OAAe,EAAE,OAAsB;QACxD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AA4QD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,gCAAgC,CAC9C,WAA+B;IAE/B,IAAI,WAAW,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,WAAW,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;QACxC,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,111 @@
1
+ import { type MediaStore } from "./agent-media.js";
2
+ import type { AgentImageReference } from "./agent-session-values.js";
3
+ /**
4
+ * Keeps image bytes in a local directory, one file per distinct image.
5
+ *
6
+ * Use it when every client and Worker of a session runs on one machine, such as a command-line tool in local mode;
7
+ * use `S3MediaStore` from `@superdurable/dex-agents/s3` when they run on different machines.
8
+ *
9
+ * The file of an image is `<root>/sha256/<sha256>`, and a reference's `uri` is `file://<root>/sha256/<sha256>`, where
10
+ * `<root>` is the absolute root with every UTF-8 byte other than ASCII letters, digits, `-`, `.`, `_`, `~`, and `/`
11
+ * written as `%XX` in uppercase hex. The root is made absolute as text: `.` and `..` segments and repeated or trailing
12
+ * `/` characters are resolved without reading the file system, symbolic links are not resolved, and Unicode is not
13
+ * normalized. A client and a Worker that pass the same root therefore build the same references, in every core.
14
+ *
15
+ * `putImage` copies the bytes into a temporary file in the same directory, syncs it, and renames it into place, so a
16
+ * reader never sees a partial image. An existing file with the same bytes is success, so retried and concurrent uploads
17
+ * of the same bytes leave one file; an existing file with other bytes is replaced. Directories the store creates get
18
+ * mode `0700` and image files mode `0600`, narrowed further by the process umask. When `putImage` writes the image, it
19
+ * syncs the file before the rename. Before every `putImage` returns, including when an identical file already exists
20
+ * and nothing is written, it syncs the `sha256` directory, the root, and the root's parent. It also syncs the parent of
21
+ * each directory it creates. Directories above the root's parent must already be durable: a concurrent creator of a
22
+ * deeper missing ancestor is out of scope.
23
+ *
24
+ * The store refuses a `sha256` directory or an image file that is a symbolic link or not a regular file, and an image
25
+ * file with more than one hard link, with `MediaStoreError`, on both upload and read. The root itself may be a symbolic
26
+ * link. Node.js has no `openat`, so this store opens files by path and checks before and after each operation: it
27
+ * checks the `sha256` directory with `lstat` and opens the file with `O_NOFOLLOW`, then confirms that the real path of
28
+ * the `sha256` directory is still the root's and that the path still names the opened or written file. A read that
29
+ * fails the check returns nothing, and a write that fails it removes the file it wrote when it can still find it. A
30
+ * process of the same user that can write the `0700` root can still race a directory swap between these checks; the
31
+ * Python core opens every path relative to an open directory and has no such window. `getImage` reads only
32
+ * references under this store's root, refuses a file whose size differs from the reference before reading it, reads at
33
+ * most one byte more than `sizeBytes`, and checks the bytes with `verifyImageBytes`. It needs a POSIX file system; on
34
+ * Windows the constructor throws.
35
+ *
36
+ * Session deletion does not delete files. A runner reads a session's images again whenever a model call replays them,
37
+ * so keep every file at least as long as any session that references it can still run a turn or replay its history. A
38
+ * missing file makes those turns fail with `MediaStoreError`.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * import { LocalDirectoryMediaStore } from "@superdurable/dex-agents";
43
+ *
44
+ * const store = new LocalDirectoryMediaStore("/var/lib/my-agent/media");
45
+ * const reference = await store.putImage(screenshotPng);
46
+ * // reference.uri === "file:///var/lib/my-agent/media/sha256/9f2c..."
47
+ * ```
48
+ * @since 0.5.0
49
+ */
50
+ export declare class LocalDirectoryMediaStore implements MediaStore {
51
+ /** The absolute root directory, made absolute as text, without a trailing `/`. */
52
+ readonly root: string;
53
+ /** The location that every reference of this store starts with: `"file://<root>"`, with the root percent-encoded. */
54
+ readonly storeUri: string;
55
+ /**
56
+ * Creates a store; it touches no file until the first upload or read.
57
+ *
58
+ * @param root - Absolute path of the directory that holds the images, such as `"/var/lib/my-agent/media"`. The first
59
+ * upload creates it and any missing parent directory with mode `0700`.
60
+ * @throws {TypeError} When `root` is not absolute, is not well-formed Unicode, contains NUL, or is the file system
61
+ * root.
62
+ * @throws {RangeError} When `root` makes image URIs longer than `maxImageUriLength`.
63
+ * @throws {Error} On a system without POSIX file access, such as Windows.
64
+ */
65
+ constructor(root: string);
66
+ /**
67
+ * Returns the path of the file of the image with this SHA-256.
68
+ *
69
+ * @param sha256 - SHA-256 of the bytes as 64 lowercase hex digits.
70
+ * @returns `"<root>/sha256/<sha256>"`.
71
+ */
72
+ imagePath(sha256: string): string;
73
+ /**
74
+ * Stores one image unless the directory already has it, and returns its reference.
75
+ *
76
+ * @param data - PNG, JPEG, GIF, or WebP bytes, at most `maxImageBytes`.
77
+ * @returns The reference.
78
+ * @throws {ImageValidationError} When the bytes are not a valid image for `buildImageReference`. Nothing is written.
79
+ * @throws {MediaStoreError} When the `sha256` directory or the image's file is a symbolic link or not a directory or
80
+ * regular file, the image's file has more than one hard link, or the `sha256` directory changed during the write.
81
+ * @throws A Node.js system error (`NodeJS.ErrnoException`) when the file system refuses a write, such as for missing
82
+ * permissions or a full disk. Retrying is safe.
83
+ */
84
+ putImage(data: Uint8Array): Promise<AgentImageReference>;
85
+ /**
86
+ * Returns the bytes of one image in this store.
87
+ *
88
+ * @param reference - A reference under this store's root.
89
+ * @returns The exact bytes, checked with `verifyImageBytes`.
90
+ * @throws {ImageValidationError} When the reference's fields are invalid.
91
+ * @throws {MediaStoreError} When the reference is outside this store's root, the file is missing, is a symbolic link
92
+ * or not a regular file, has more than one hard link, moved during the read, or its bytes do not match.
93
+ * @throws A Node.js system error (`NodeJS.ErrnoException`) when the file system refuses the read for another reason.
94
+ */
95
+ getImage(reference: AgentImageReference): Promise<Uint8Array>;
96
+ private imageDirectory;
97
+ /** Creates the root and its missing parents with mode `0700`, syncing the parent of each. */
98
+ private makeRoot;
99
+ /** Throws `MediaStoreError` unless the `sha256` directory is a real directory, not a symbolic link. */
100
+ private requireImageDirectory;
101
+ /** Returns whether the image's file exists and holds exactly `data`. */
102
+ private hasImageFile;
103
+ /** Opens the reference's image file, refusing symbolic links, special files, hard links, and moved files. */
104
+ private openImageFile;
105
+ /** Returns the open file, or `undefined` when a rename replaced it after the open. */
106
+ private openImageFileOnce;
107
+ /** Returns whether the `sha256` directory is a real directory whose real path is the root's. */
108
+ private isImageDirectoryInStore;
109
+ private directoryChanged;
110
+ private replaceImageFile;
111
+ }