summaryrefslogtreecommitdiff
path: root/libpanto/src
diff options
context:
space:
mode:
Diffstat (limited to 'libpanto/src')
-rw-r--r--libpanto/src/agent.zig25
1 files changed, 25 insertions, 0 deletions
diff --git a/libpanto/src/agent.zig b/libpanto/src/agent.zig
index 3616ab6..09b0994 100644
--- a/libpanto/src/agent.zig
+++ b/libpanto/src/agent.zig
@@ -270,6 +270,13 @@ pub const Agent = struct {
/// rewound by `rewriteWithSummary` so a compaction re-persists its summary
/// + restated suffix.
_persisted_through: usize = 0,
+ /// User-message blocks queued by an embedder seam (the CLI's
+ /// `panto.ext.agent:submit`) for the host front end to open the next
+ /// turn with, so the turn runs on the host's own driver (rendering,
+ /// interrupts) rather than inside the queuing callback. Blocks and the
+ /// list storage are owned by `conversation.allocator` — the same
+ /// ownership `run` adopts.
+ _pending_submission: std.ArrayList(conversation.ContentBlock) = .empty,
/// Construct an agent.
///
@@ -311,6 +318,8 @@ pub const Agent = struct {
// self-heap-pinned (`init` allocated it), so it frees itself last.
const allocator = self._allocator;
self._registry.deinit();
+ for (self._pending_submission.items) |*b| b.deinit(self.conversation.allocator);
+ self._pending_submission.deinit(self.conversation.allocator);
self.conversation.deinit();
self._session.info.deinit(allocator);
allocator.destroy(self);
@@ -454,6 +463,22 @@ pub const Agent = struct {
/// The agent re-reads its `config` snapshot at the top of each provider
/// response inside the stream, so a mid-conversation `setConfig` takes
/// effect at the next response boundary, never mid-stream.
+ /// Append blocks to the pending submission (see `_pending_submission`).
+ /// Adopts the block *contents*; the `blocks` slice itself stays the
+ /// caller's. Multiple calls accumulate into one pending user message.
+ pub fn queueSubmission(self: *Agent, blocks: []const conversation.ContentBlock) !void {
+ try self._pending_submission.appendSlice(self.conversation.allocator, blocks);
+ }
+
+ /// Take the pending submission, or null when none is queued. The caller
+ /// owns the returned slice: typically hand it to `run` (which adopts the
+ /// block contents) and free the slice itself with
+ /// `conversation.allocator`.
+ pub fn takeSubmission(self: *Agent) !?[]conversation.ContentBlock {
+ if (self._pending_submission.items.len == 0) return null;
+ return try self._pending_submission.toOwnedSlice(self.conversation.allocator);
+ }
+
pub fn run(self: *Agent, message: UserMessage) !*Stream {
self._auto_compacted = false;