Skip to content

Commit 3a163dc

Browse files
AlexsJonesclaude
andcommitted
feat: add shared workflow memory for cross-persona knowledge sharing
Introduces a pack-level shared memory pool that enables personas within a PersonaPack to share knowledge across runs. Each persona retains its private memory; the shared pool adds cross-persona context via three new agent tools (workflow_memory_search, workflow_memory_store, workflow_memory_list) with per-persona read-write/read-only access control. - CRD: SharedMemorySpec on PersonaPackSpec with access rules - Controller: provisions PVC, Deployment, Service per pack - AgentRun: injects WORKFLOW_MEMORY_SERVER_URL and wait-for init container - Agent runner: workflow memory tools with auto-context injection - API: PATCH support + shared-memory list proxy endpoint - Frontend: workflow tab card, canvas badges, TypeScript types - Cypress: 7 test suites (30+ cases) for API, UI, and edge cases - Docs: updated personapacks, persistent-memory, writing guide, architecture Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3a7ec53 commit 3a163dc

20 files changed

Lines changed: 2004 additions & 12 deletions

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,16 @@ Sympozium serves **two powerful use cases** on one Kubernetes-native platform:
8282

8383
| | |
8484
|---|---|
85-
| **PersonaPacks** | Helm-like bundles for AI agents — activate a pack and the controller stamps out a full agent team |
85+
| **PersonaPacks** | Helm-like bundles for AI agent teams — activate a pack and the controller stamps out instances, schedules, and memory |
86+
| **Agent Workflows** | Delegation, sequential pipelines, and supervision relationships between personas — visualised on an interactive canvas |
87+
| **Shared Workflow Memory** | Pack-level SQLite memory pool for cross-persona knowledge sharing with per-persona access control |
8688
| **Skill Sidecars** | Every skill runs in its own sidecar with ephemeral least-privilege RBAC, garbage-collected on completion |
8789
| **Multi-Channel** | Telegram, Slack, Discord, WhatsApp — each channel is a dedicated Deployment backed by NATS JetStream |
8890
| **Persistent Memory** | SQLite + FTS5 on a PersistentVolume — memories survive across ephemeral pod runs |
8991
| **Scheduled Heartbeats** | Cron-based recurring agent runs for health checks, alert triage, and resource right-sizing |
9092
| **Agent Sandbox** | Kernel-level isolation via [kubernetes-sigs/agent-sandbox](https://deploy.sympozium.ai/docs/concepts/agent-sandbox/) — gVisor or Kata with warm pools for instant starts |
9193
| **MCP Servers** | External tool providers via Model Context Protocol with auto-discovery and allow/deny filtering |
92-
| **TUI & Web UI** | Terminal and browser dashboards, or skip the UI entirely with Helm and kubectl |
94+
| **TUI & Web UI** | Terminal and browser dashboards with live workflow canvas, or skip the UI entirely with Helm and kubectl |
9395
| **Any AI Provider** | OpenAI, Anthropic, Azure, Ollama, or any compatible endpoint — no vendor lock-in |
9496

9597
---

api/v1alpha1/personapack_types.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ type PersonaPackSpec struct {
8282
// +optional
8383
AgentSandbox *AgentSandboxInstanceSpec `json:"agentSandbox,omitempty"`
8484

85+
// SharedMemory configures a shared memory pool accessible to all personas
86+
// in this pack. Each persona retains its private memory; the shared pool
87+
// provides cross-persona knowledge sharing within the workflow.
88+
// +optional
89+
SharedMemory *SharedMemorySpec `json:"sharedMemory,omitempty"`
90+
8591
// Relationships defines directed edges between personas in the pack,
8692
// enabling coordination patterns like delegation, sequential pipelines,
8793
// and supervision.
@@ -198,6 +204,35 @@ type PersonaMemory struct {
198204
Seeds []string `json:"seeds,omitempty"`
199205
}
200206

207+
// SharedMemorySpec configures a shared memory pool for all personas in a pack.
208+
type SharedMemorySpec struct {
209+
// Enabled activates the shared memory server for this pack.
210+
// +kubebuilder:default=false
211+
Enabled bool `json:"enabled"`
212+
213+
// StorageSize is the PVC storage request for the shared memory database.
214+
// Defaults to "1Gi".
215+
// +kubebuilder:default="1Gi"
216+
// +optional
217+
StorageSize string `json:"storageSize,omitempty"`
218+
219+
// AccessRules defines per-persona access controls for the shared memory.
220+
// If empty, all personas get read-write access.
221+
// +optional
222+
AccessRules []SharedMemoryAccessRule `json:"accessRules,omitempty"`
223+
}
224+
225+
// SharedMemoryAccessRule defines access control for a specific persona.
226+
type SharedMemoryAccessRule struct {
227+
// Persona is the persona name this rule applies to.
228+
Persona string `json:"persona"`
229+
230+
// Access is the access level: "read-write" or "read-only".
231+
// +kubebuilder:validation:Enum="read-write";"read-only"
232+
// +kubebuilder:default="read-write"
233+
Access string `json:"access"`
234+
}
235+
201236
// PersonaRelationship defines a directed edge between two personas in a pack.
202237
type PersonaRelationship struct {
203238
// Source is the persona name that initiates the interaction.
@@ -259,6 +294,10 @@ type PersonaPackStatus struct {
259294
// +optional
260295
InstalledPersonas []InstalledPersona `json:"installedPersonas,omitempty"`
261296

297+
// SharedMemoryReady indicates the shared memory infrastructure is provisioned.
298+
// +optional
299+
SharedMemoryReady bool `json:"sharedMemoryReady,omitempty"`
300+
262301
// Conditions represent the latest available observations.
263302
// +optional
264303
Conditions []metav1.Condition `json:"conditions,omitempty"`

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 40 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/sympozium/crds/sympozium.ai_personapacks.yaml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,51 @@ spec:
531531
- type
532532
type: object
533533
type: array
534+
sharedMemory:
535+
description: |-
536+
SharedMemory configures a shared memory pool accessible to all personas
537+
in this pack. Each persona retains its private memory; the shared pool
538+
provides cross-persona knowledge sharing within the workflow.
539+
properties:
540+
accessRules:
541+
description: |-
542+
AccessRules defines per-persona access controls for the shared memory.
543+
If empty, all personas get read-write access.
544+
items:
545+
description: SharedMemoryAccessRule defines access control for
546+
a specific persona.
547+
properties:
548+
access:
549+
default: read-write
550+
description: 'Access is the access level: "read-write" or
551+
"read-only".'
552+
enum:
553+
- read-write
554+
- read-only
555+
type: string
556+
persona:
557+
description: Persona is the persona name this rule applies
558+
to.
559+
type: string
560+
required:
561+
- access
562+
- persona
563+
type: object
564+
type: array
565+
enabled:
566+
default: false
567+
description: Enabled activates the shared memory server for this
568+
pack.
569+
type: boolean
570+
storageSize:
571+
default: 1Gi
572+
description: |-
573+
StorageSize is the PVC storage request for the shared memory database.
574+
Defaults to "1Gi".
575+
type: string
576+
required:
577+
- enabled
578+
type: object
534579
skillParams:
535580
additionalProperties:
536581
additionalProperties:
@@ -660,6 +705,10 @@ spec:
660705
phase:
661706
description: Phase is the current phase (Pending, Ready, Error).
662707
type: string
708+
sharedMemoryReady:
709+
description: SharedMemoryReady indicates the shared memory infrastructure
710+
is provisioned.
711+
type: boolean
663712
type: object
664713
type: object
665714
served: true

cmd/agent-runner/main.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,33 @@ func main() {
255255
}
256256
}
257257

258+
// Load shared workflow memory tools if configured (pack-level shared memory).
259+
if wfMemTools := initWorkflowMemoryTools(); len(wfMemTools) > 0 {
260+
tools = append(tools, wfMemTools...)
261+
262+
// Auto-inject shared team memory context alongside private memory.
263+
if wfMemCtx := queryWorkflowMemoryContext(task, 3); wfMemCtx != "" {
264+
systemPrompt += "\n\n## Team Knowledge (Shared Workflow Memory)\n\n" +
265+
"The following shared memories were contributed by other personas in your team:\n\n" +
266+
wfMemCtx + "\n\n" +
267+
"Use `workflow_memory_search` for additional team knowledge lookups.\n"
268+
log.Printf("auto-injected %d bytes of shared workflow memory context", len(wfMemCtx))
269+
}
270+
271+
systemPrompt += "\n\n## Shared Workflow Memory\n\n" +
272+
"You have access to shared team memory tools (`workflow_memory_search`, `workflow_memory_list`"
273+
if workflowMemoryAccess != "read-only" {
274+
systemPrompt += ", `workflow_memory_store`"
275+
}
276+
systemPrompt += ") that are shared across all personas in your team.\n"
277+
if workflowMemoryAccess != "read-only" {
278+
systemPrompt += "**After completing your task**, use `workflow_memory_store` to share key findings " +
279+
"with other team members. Your persona name is automatically attached for attribution.\n"
280+
}
281+
282+
log.Printf("workflow memory tools loaded: %d tool(s) (access: %s)", len(wfMemTools), workflowMemoryAccess)
283+
}
284+
258285
apiKey := firstNonEmpty(
259286
os.Getenv("API_KEY"),
260287
os.Getenv("OPENAI_API_KEY"),

0 commit comments

Comments
 (0)