Skip to content

Support multiple graph epochs per root Session with cross-graph result inputs #2588

Description

@likun666661

Problem

An agent graph currently has the same lifetime as its root Session:

  • agentGraphIdForRootSession(rootSessionId) deterministically maps one root Session to one graph ID.
  • A finish schedule update permanently closes that graph.
  • Later schedule updates for the same root Session fail with Agent graph schedule is already finished.
  • There is no normal graph epoch/generation or "begin next graph" path.

This makes sense if a root Session represents one workflow execution, but Maka Sessions are long-lived conversations. A common flow is:

  1. Ask the supervisor to use swarm for a research task.
  2. The supervisor finishes the graph and selects its final result records.
  3. Continue the same conversation with a follow-up question.
  4. Ask for another swarm pass.

Step 4 cannot schedule new work because the root Session still resolves to the already-finished graph. We reproduced this in a normal conversation: after completing and finishing a six-item architecture-reading swarm, a follow-up add_work failed because the graph schedule was closed.

Relevant implementation points:

  • packages/runtime/src/stream-graph-coordinator.ts: agentGraphIdForRootSession() hashes only rootSessionId.
  • packages/storage/src/sqlite-session-metadata-store.ts: commitAgentGraphScheduleUpdate() rejects every update after hasClosedAgentGraphSchedule(graphId).
  • packages/runtime/src/stream-graph-supervisor-tools.ts: schedule projection rejects updates after finish.

The last two invariants are valuable: finish should remain a durable, irreversible admission boundary. The questionable part is binding that one graph's lifetime to the entire conversational Session.

There is a related data-continuity issue. A new graph should be able to consume immutable results published by a finished graph without reopening or mutating the old graph. Otherwise follow-up work must copy large outputs back into prompts, rely on lossy supervisor summaries, or repeat completed work.

Desired outcome

A root Session can run multiple sequential graph epochs while preserving the meaning of finish for each individual graph.

For example:

Root Session
  |- Graph epoch 1 (finished)
  |- Graph epoch 2 (finished)
  `- Graph epoch 3 (active)

Desired semantics:

  • finish permanently closes the current graph epoch; it does not reopen later.
  • A later supervisor turn can atomically create or bind a new graph epoch for the same root Session.
  • Only one graph epoch is active for a root Session at a time, unless explicit parallel-root semantics are introduced later.
  • Historical graphs remain readable and retain their selected final results.
  • A new graph may explicitly consume committed outputs from an older graph.
  • Cross-graph data references do not grant cross-graph control: the new graph cannot stop old activations, continue old claims, mutate old schedules, or implicitly reuse old operators.

One possible model is an explicit root-to-graph binding with an epoch:

graph_id = hash(rootSessionId, graphEpoch)

agent_graph_roots(
  graph_id,
  root_session_id,
  epoch,
  status,
  created_at,
  finished_at
)

Coordinator APIs would resolve the active graph rather than deriving a single permanent graph ID from the root Session. If no active graph exists, a new epoch could be created atomically, either automatically on the next graph operation or through an explicit begin_agent_graph operation.

For cross-graph inputs, a typed reference would make provenance and authorization clearer than a bare string:

type GraphInputRef =
  | { kind: 'graph_result'; graphId: string; resultId: string }
  | { kind: 'graph_record'; graphId: string; recordId: string }
  | { kind: 'artifact'; artifactId: string; fingerprint: string };

At minimum, cross-graph inputs should be:

  • immutable and committed;
  • authorized for the current root Session/workspace;
  • pinned against garbage collection while referenced;
  • recorded as explicit lineage;
  • resolved to a stable source identity/fingerprint at admission time.

It may be preferable to expose only results selected by the old graph's finish.resultIds as its public cross-graph interface, rather than allowing arbitrary internal records by default.

Most existing storage structures are already keyed by graph_id, so this appears primarily to require a lifecycle/binding change in the coordinator and recovery paths rather than a redesign of schedule, claim, wake, or projection semantics.

Alternatives or workarounds

  • Start a new root Session for every swarm. This loses conversational continuity and makes follow-up research awkward.
  • Never call finish. This weakens the graph's terminal/admission invariant and leaves lifecycle management ambiguous.
  • Reopen a finished graph. This would invalidate the current meaning of finish, complicate wake recovery, and allow schedule revisions after a declared terminal boundary.
  • Copy old outputs into the next prompt. This is expensive, lossy, and loses durable provenance.
  • Reset/delete graph operational state. This is destructive and is not a normal lifecycle operation.

The proposed direction is therefore new graph epochs with immutable cross-epoch result references, not reopening an old graph.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions