Skip to content

feat(memory): Add passive turn extraction#628

Open
dcramer wants to merge 11 commits into
mainfrom
feat/memory-passive-extraction
Open

feat(memory): Add passive turn extraction#628
dcramer wants to merge 11 commits into
mainfrom
feat/memory-passive-extraction

Conversation

@dcramer

@dcramer dcramer commented Jun 22, 2026

Copy link
Copy Markdown
Member

Junior memory now learns from completed turns through the plugin observation hook, in addition to explicit memory tools. Core observes successful local replies, normal Slack replies, and resumed Slack replies; the memory plugin runs one structured internal agent over bounded user-authored context, ignores turns that already used memory-management tools, and stores accepted public/shareable facts using runtime-derived requester and source authority.

The plugin source context is now normalized before it reaches hooks and tools. Source carries platform, privacy type, conversation id, and platform coordinates, with helpers for construction, private-source checks, and stable source keys. Memory uses that context to allow local development and stable public sources while skipping private or unstable non-local sources before extraction.

Resumable Slack runs now persist the normalized source through auth, timeout, and continuation state. OAuth/MCP callbacks and agent continuation reuse the stored run source instead of reconstructing source privacy from channel ids, which keeps passive memory behavior consistent after resumed public-channel turns.

The memory specs and eval policy were tightened around public-only memory, agentic extraction, and eval boundaries. Memory evals now cover organic passive learning, automatic recall injection, and natural-language forget behavior while avoiding deterministic list/search/tool-mechanics checks.

Add a post-delivery observeTurn plugin hook and wire it through local and Slack turn delivery so trusted plugins can inspect successful turns without affecting user-visible delivery.

Teach the memory plugin to extract public, durable facts from user-authored turn text, skip memory-management tool turns to avoid duplicate writes, and store accepted memories through the existing context-bound memory store.

Update the memory specs and evals to cover organic passive extraction, canonical stored content, and follow-up recall.

Co-Authored-By: GPT-5 Codex <codex@openai.com>
@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
junior-docs Ready Ready Preview, Comment Jun 24, 2026 3:10pm

Request Review

Comment thread packages/junior-memory/src/observe.ts
Comment thread packages/junior-memory/src/observe.ts
Keep passive memory observation scoped to supported local and public Slack contexts, wire resumed Slack success through observation, and type observation contexts with platform-coupled runtime context.

Remove the telemetry mock from resume behavior tests and align memory specs with the explicit-review and passive-extraction split.

Co-Authored-By: GPT-5 Codex <codex@openai.com>
Comment thread packages/junior/src/chat/runtime/reply-executor.ts
Expose normalized plugin source helpers with compact pub/priv source typing so trusted plugins can rely on shared source visibility and stable source keys instead of rebuilding platform-specific checks. Update memory passive extraction, runtime callers, tests, specs, and source-mode package exports around that contract.

Co-Authored-By: GPT-5 Codex <codex@openai.com>
Comment thread packages/junior-plugin-api/src/schemas.ts Outdated
Keep memory extraction prompt examples neutral and make the model-facing output field explicitly canonical. Pass assistant responses as rejection context for passive extraction so follow-up and advice turns are less likely to create duplicate memories.

Fix eval assertions to read memories for the active test thread and remove the package development export condition that made CI load TypeScript from node_modules.

Co-Authored-By: GPT-5 Codex <codex@openai.com>
Comment thread packages/junior-plugin-api/src/context.ts Outdated
Pass successful tool results into post-turn observation so failed explicit memory attempts do not block passive extraction. Derive Slack memory source privacy from explicit runtime context instead of channel id prefixes, and observe the same queued text the agent answered.

Also keep requester memories in canonical stored-fact form and leave deterministic listMemories behavior in storage tests rather than evals.

Co-Authored-By: GPT-5 Codex <codex@openai.com>
Comment thread packages/junior/src/chat/runtime/slack-resume.ts Outdated
Persist the normalized run source through auth, timeout, and continuation state so resumed Slack turns reuse the original source instead of reconstructing one. Require source at Slack resume boundaries and tighten tests to prove stored sources are forwarded.

Remove deterministic memory tool mechanics from eval assertions so evals stay focused on model-facing behavior.

Co-Authored-By: GPT-5 Codex <codex@openai.com>
const pendingMessage = optionalString(value.pendingMessage);
if (pendingMessage && !source?.success) {
return undefined;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OAuth state rejects missing source

Medium Severity

OAuth state parsing now treats payloads with a pendingMessage but no normalized source as invalid, and pending-message resume also requires source. OAuth flows started before this change (or any path that stored pending text without source) can no longer be parsed or resumed after authorization.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 24882f3. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional hard cutover for this branch. OAuth pending-message state now requires a normalized source because source/destination context is the runtime boundary contract; we are not adding compatibility fallback for old in-flight state that was created before this schema change.

— Claude Code

Normalize old dispatch records that only stored destination by deriving a source through the plugin API helper. This keeps recovery and heartbeat paths able to read pre-source records without accepting malformed destinations.

Co-Authored-By: GPT-5 Codex <codex@openai.com>
Comment thread packages/junior/src/chat/runtime/reply-executor.ts
Pending-message OAuth state now requires a source so resumed Slack runs can reuse the original context. Update the unit fixture to match the runtime contract.

Co-Authored-By: GPT-5 Codex <codex@openai.com>
Keep passive extraction on a single canonical memory shape while preserving DB-backed eval coverage. Tighten source context handling, memory model selection, and eval harness behavior so the memory slice follows the trusted plugin and agentic-semantics policies.

Co-Authored-By: GPT-5 Codex <gpt-5-codex@openai.com>
Comment thread packages/junior-memory/src/observe.ts
errorMessage: "Stored Slack source missing for continuation",
});
return false;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resume fails without stored source

Medium Severity

Slack continuation and OAuth resume paths now require activeSessionRecord.source and fail the session when it is missing. Turn records created before this change only persisted destination metadata, so in-flight auth pauses, timeout continuations, or OAuth resumes can terminate with “Stored Slack source missing” instead of completing the user’s turn.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b069491. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional hard cutover for this branch. Resumed runs now require the stored source rather than reconstructing it from destination metadata; old in-flight records without source are not supported by this migration slice.

— Claude Code

Only explicit memory mutation tools should suppress passive extraction. Recall tools can run during an ordinary turn and still leave durable facts for the memory agent to extract afterward.

Co-Authored-By: GPT-5 Codex <gpt-5-codex@openai.com>
value: unknown,
): DispatchRecord | undefined {
const candidate =
value &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Legacy dispatch records unparsable

Medium Severity

Removing the parse-time backfill that copied destination into missing source means older persisted dispatch records without a source field no longer parse, so recovery and callbacks can lose in-flight plugin dispatches after deploy.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6f70be8. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional hard cutover for this branch. Plugin dispatch records now require a normalized source as part of the runtime boundary contract; we are not preserving parse-time fallback for older persisted dispatch records that predate source storage.

— Claude Code

Slack sources expose runtime-owned Slack address fields while the opaque Junior conversation id is passed separately on the plugin context. Update the hook assertion to match the strict source schema.

Co-Authored-By: GPT-5 Codex <gpt-5-codex@openai.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e3a5882. Configure here.

assistantText: reply.text,
toolCalls: reply.piMessages
? getSuccessfulToolCalls(reply.piMessages)
: reply.diagnostics.toolCalls,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turn tool history skips passive memory

High Severity

After a successful turn, passive memory observation builds the mutation-tool skip list from getSuccessfulToolCalls over the full reply.piMessages session, not just the current turn. An earlier successful createMemory or removeMemory in the same conversation makes later organic turns skip passive extraction even when this turn did not use those tools.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e3a5882. Configure here.

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