feat: disable subagent summaries, label subagent observations (#2073)
* feat: disable subagent summaries and label subagent observations Detect Claude Code subagent hook context via `agent_id`/`agent_type` on stdin, short-circuit the Stop-hook summary path when present, and thread the subagent identity end-to-end onto observation rows (new `agent_type` and `agent_id` columns, migration 010 at version 27). Main-session rows remain NULL; content-hash dedup is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: address PR #2073 review feedback - Narrow summarize subagent guard to agentId only so --agent-started main sessions still own their summary (agentType alone is main-session). - Remove now-dead agentId/agentType spreads from the summarize POST body. - Always overwrite pendingAgentId/pendingAgentType in SDK/Gemini/OpenRouter agents (clears stale subagent identity on main-session messages after a subagent message in the same batch). - Add idx_observations_agent_id index in migration 010 + the mirror migration in SessionStore + the runner. - Replace console.log in migration010 with logger.debug. - Update summarize test: agentType alone no longer short-circuits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: address CodeRabbit + claude-review iteration 4 feedback - SessionRoutes.handleSummarizeByClaudeId: narrow worker-side guard to agentId only (matches hook-side). agentType alone = --agent main session, which still owns its summary. - ResponseProcessor: wrap storeObservations in try/finally so pendingAgentId/Type clear even if storage throws. Prevents stale subagent identity from leaking into the next batch on error. - SessionStore.importObservation + bulk.importObservation: persist agent_type/agent_id so backup/import round-trips preserve subagent attribution. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * polish: claude-review iteration 5 cleanup - Use ?? not || for nullable subagent fields in PendingMessageStore (prevents treating empty string as null). - Simplify observation.ts body spread — include fields unconditionally; JSON.stringify drops undefined anyway. - Narrow any[] to Array<{ name: string }> in migration010 column checks. - Add trailing newline to migrations.ts. - Document in observations/store.ts why the dedup hash intentionally excludes agent fields. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * polish: claude-review iteration 7 feedback - claude-code adapter: add 128-char safety cap on agent_id/agent_type so a malformed Claude Code payload cannot balloon DB rows. Empty strings now also treated as absent. - migration010: state-aware debug log lists only columns actually added; idempotent re-runs log "already present; ensured indexes". - Add 3 adapter tests covering the length cap boundary and empty-string rejection. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * perf: skip subagent summary before worker bootstrap Move the agentId short-circuit above ensureWorkerRunning() so a Stop hook fired inside a subagent does not trigger worker startup just to return early. Addresses CodeRabbit nit on summarize.ts:36-47. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { Database } from 'bun:sqlite';
|
||||
import { Migration } from './Database.js';
|
||||
import { logger } from '../../utils/logger.js';
|
||||
|
||||
// Re-export MigrationRunner for SessionStore migration extraction
|
||||
export { MigrationRunner } from './migrations/runner.js';
|
||||
@@ -572,6 +573,61 @@ export const migration009: Migration = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Migration 010: Label observations (and their queue rows) with the subagent identity.
|
||||
*
|
||||
* Claude Code hooks that fire inside a subagent carry agent_id and agent_type on the
|
||||
* stdin payload. These flow hook → worker → pending_messages → SDK storage so that
|
||||
* observation rows can be attributed to the originating subagent. Main-session rows
|
||||
* keep NULL for both columns.
|
||||
*/
|
||||
export const migration010: Migration = {
|
||||
version: 27,
|
||||
up: (db: Database) => {
|
||||
const added: string[] = [];
|
||||
|
||||
const obsColumns = db.prepare('PRAGMA table_info(observations)').all() as Array<{ name: string }>;
|
||||
const obsHasAgentType = obsColumns.some(c => c.name === 'agent_type');
|
||||
const obsHasAgentId = obsColumns.some(c => c.name === 'agent_id');
|
||||
if (!obsHasAgentType) {
|
||||
db.run('ALTER TABLE observations ADD COLUMN agent_type TEXT');
|
||||
added.push('observations.agent_type');
|
||||
}
|
||||
if (!obsHasAgentId) {
|
||||
db.run('ALTER TABLE observations ADD COLUMN agent_id TEXT');
|
||||
added.push('observations.agent_id');
|
||||
}
|
||||
db.run('CREATE INDEX IF NOT EXISTS idx_observations_agent_type ON observations(agent_type)');
|
||||
db.run('CREATE INDEX IF NOT EXISTS idx_observations_agent_id ON observations(agent_id)');
|
||||
|
||||
// Also thread the same fields through the pending_messages queue so the label
|
||||
// survives worker restarts between enqueue and SDK-agent processing.
|
||||
const pendingColumns = db.prepare('PRAGMA table_info(pending_messages)').all() as Array<{ name: string }>;
|
||||
if (pendingColumns.length > 0) {
|
||||
const pendingHasAgentType = pendingColumns.some(c => c.name === 'agent_type');
|
||||
const pendingHasAgentId = pendingColumns.some(c => c.name === 'agent_id');
|
||||
if (!pendingHasAgentType) {
|
||||
db.run('ALTER TABLE pending_messages ADD COLUMN agent_type TEXT');
|
||||
added.push('pending_messages.agent_type');
|
||||
}
|
||||
if (!pendingHasAgentId) {
|
||||
db.run('ALTER TABLE pending_messages ADD COLUMN agent_id TEXT');
|
||||
added.push('pending_messages.agent_id');
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
'DB',
|
||||
added.length > 0
|
||||
? `[migration010] Added columns: ${added.join(', ')}`
|
||||
: '[migration010] Subagent identity columns already present; ensured indexes'
|
||||
);
|
||||
},
|
||||
down: (_db: Database) => {
|
||||
// SQLite DROP COLUMN not fully supported; no-op
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* All migrations in order
|
||||
*/
|
||||
@@ -584,5 +640,6 @@ export const migrations: Migration[] = [
|
||||
migration006,
|
||||
migration007,
|
||||
migration008,
|
||||
migration009
|
||||
];
|
||||
migration009,
|
||||
migration010
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user