Skip to content

feat(memory): OCG-backed agent memory with auto-summarized save + hum… - #298

Open
NicholasDCole wants to merge 5 commits into
mainfrom
feature/add_memory
Open

feat(memory): OCG-backed agent memory with auto-summarized save + hum…#298
NicholasDCole wants to merge 5 commits into
mainfrom
feature/add_memory

Conversation

@NicholasDCole

Copy link
Copy Markdown
Contributor

…an feedback links

Add OCGMemoryStore (a synchronous MemoryStore HTTP adapter over the OCG BFF) and wire long-term memory into the agent runtime:

  • Pre-run: relevant agent/user-scoped memories are retrieved and injected into a copy of the agent's instructions (never mutating the shared agent).
  • Post-run: the conversation is distilled by an internal summarizer sub-agent (structured MemorySummary output; reuses the agent's model unless memory_summary_model is set) and saved as a 'conversation:' memory. Best-effort and recursion-guarded - never fails the primary run.
  • Feedback is human-only: the runtime hands a FeedbackEvent (distilled summary + signed good/bad capability URLs) to the agent's feedback_sink for out-of-band delivery (e.g. a Zendesk ticket). The URLs are never shown to the agent's LLM, so agents can only create and read memories.

Adds Agent params semantic_memory/memory_summary_model/feedback_sink, the OCGMemoryStore.feedback_links() mint call, example 118, and unit tests.

NicholasDCole and others added 2 commits June 30, 2026 12:10
…an feedback links

Add OCGMemoryStore (a synchronous MemoryStore HTTP adapter over the OCG BFF) and
wire long-term memory into the agent runtime:

- Pre-run: relevant agent/user-scoped memories are retrieved and injected into a
  copy of the agent's instructions (never mutating the shared agent).
- Post-run: the conversation is distilled by an internal summarizer sub-agent
  (structured MemorySummary output; reuses the agent's model unless
  memory_summary_model is set) and saved as a 'conversation:<session>' memory.
  Best-effort and recursion-guarded - never fails the primary run.
- Feedback is human-only: the runtime hands a FeedbackEvent (distilled summary +
  signed good/bad capability URLs) to the agent's feedback_sink for out-of-band
  delivery (e.g. a Zendesk ticket). The URLs are never shown to the agent's LLM,
  so agents can only create and read memories.

Adds Agent params semantic_memory/memory_summary_model/feedback_sink, the
OCGMemoryStore.feedback_links() mint call, example 118, and unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…path

Extend OCG-backed agent memory beyond the client-side run() wrapper so it
also works on the compiled (deployed/webhook) execution path.

Python SDK:
- Serialize semantic_memory to a longTermMemory config (OCG url, server-
  resolvable credential NAME, agent/user/scope, maxResults, summary model)
  plus a feedbackSink worker ref when a feedback_sink callable is set.
- OCGMemoryStore gains a `credential` name (default OCG_PUBLIC_KEY) for the
  compiled path, distinct from the raw client `token`.
- Register a best-effort feedback_sink SIMPLE worker that rebuilds a
  FeedbackEvent and delivers the good/bad capability links out-of-band.

Server compiler:
- New LongTermMemoryConfig DTO + AgentConfig.longTermMemory/feedbackSink.
- compileWithTools inlines pre-loop retrieval (search -> format -> set
  _ltm_context) and post-loop distill -> build value -> save -> feedback
  links -> feedback_sink. All tasks optional so memory never fails the run.
- Inject retrieved memory as an LLM system message (mirrors _human_feedback).
- Reuse host-mode-aware credential header rewrite via ToolCompiler.
- JavaScriptBuilder helpers to format search hits (folding good/bad signal)
  and parse the distiller JSON into the durable value string.
- Default inbound-webhook `media` to [] to avoid an upstream NPE.

Tests: config serializer coverage for longTermMemory + feedbackSink,
absent no-op, and summary-model fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NicholasDCole and others added 3 commits July 9, 2026 12:55
GET /agent/executions/search accepts topLevelOnly (default false). When
true, the service appends `parentWorkflowId = ""` to the query (AND-joined
when a query is already present) so only root executions are returned,
excluding sub-agent/sub-workflow rows. Backed by the queryable
parent_workflow_id column in orkes-conductor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts:
#	server/conductor-agentspan/src/main/java/dev/agentspan/runtime/compiler/ToolCompiler.java
#	server/conductor-agentspan/src/main/java/dev/agentspan/runtime/controller/AgentController.java
#	server/conductor-agentspan/src/main/java/dev/agentspan/runtime/service/AgentService.java
…er + topLevelOnly)

The main merge left the controller calling a boolean `topLevelOnly` variant while the service only
had main's `String classifier` variant — a compile mismatch that also risked dropping one feature.

Support both: AgentService gains a combined overload
`searchExecutionsRaw(..., String classifier, boolean topLevelOnly)` that folds the classifier filter
(via the existing withClassifierFilter — unchanged, tests intact) and ANDs in `parentWorkflowId = ""`
when topLevelOnly. The 5-arg and (…, classifier) overloads delegate to it. AgentController exposes
both `classifier` and `topLevelOnly` query params on /executions/search.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
NicholasDCole added a commit to conductor-oss/conductor that referenced this pull request Jul 14, 2026
Ports the server side of agentspan-ai/agentspan#298: agents configured
with longTermMemory get compiler-inlined memory steps — pre-loop
retrieval (search HTTP -> format INLINE -> _ltm_context variable
injected as an LLM system message) and post-loop distill/save/
feedback-links tasks, plus an optional feedback_sink SIMPLE worker
task. All memory tasks are optional=true so a memory outage never
fails the agent.

Adaptations for this repo: credential headers resolve via
${workflow.secrets.NAME} (this host has no CredentialAwareHttpTask, so
the inert #{NAME} form would never resolve), no __agentspan_ctx__
forwarding, and the distill model falls back to the agent's own model
when summaryModel is unset (ModelParser.parse(null) throws).

Also ports the topLevelOnly executions-search filter and the media
input-template default that fixes the inbound-webhook NPE.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant