awb-agent-manager 1.6.171 → 1.6.172

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ import type { HarnessSpec, RuntimeProfileSpec } from './cli-adapters/base.js';
2
+ import type { TicketRepositoryContext } from './worktree-manager.js';
3
+ export declare const AGENT_CONTEXT_VERSION = "1.2";
4
+ export declare const AGENT_CONTEXT_MAX_CHARS = 16000;
5
+ export interface AgentContextContractInput {
6
+ ticket: any;
7
+ role: string;
8
+ repository?: TicketRepositoryContext;
9
+ harness?: HarnessSpec | null;
10
+ runtimeProfile?: RuntimeProfileSpec | null;
11
+ sessionMode?: 'persistent' | 'stateless' | 'hermes';
12
+ mcpAvailable?: boolean;
13
+ effort?: string | null;
14
+ }
15
+ export declare class AgentContextPreflightError extends Error {
16
+ readonly category: 'ticket' | 'column' | 'repository';
17
+ constructor(category: 'ticket' | 'column' | 'repository', message: string);
18
+ }
19
+ export declare function redactAgentContextText(value: string): string;
20
+ export declare function buildAgentContextContract(input: AgentContextContractInput): any;
21
+ export declare function renderAgentContextContract(contract: ReturnType<typeof buildAgentContextContract>): string;
@@ -0,0 +1,169 @@
1
+ export const AGENT_CONTEXT_VERSION = '1.2';
2
+ export const AGENT_CONTEXT_MAX_CHARS = 16_000;
3
+ const CONTRACT_JSON_MAX_CHARS = 15_900;
4
+ const COLLECTION_MAX_ITEMS = 20;
5
+ const COLLECTION_ITEM_MAX_CHARS = 800;
6
+ const REDACTED = '[REDACTED]';
7
+ const SENSITIVE_KEY_NAMES = new Set([
8
+ 'authorization',
9
+ 'password',
10
+ 'passwd',
11
+ 'pwd',
12
+ 'token',
13
+ 'accesstoken',
14
+ 'refreshtoken',
15
+ 'apikey',
16
+ 'secret',
17
+ 'clientsecret',
18
+ 'credential',
19
+ 'credentialref',
20
+ 'privatekey',
21
+ ]);
22
+ export class AgentContextPreflightError extends Error {
23
+ category;
24
+ constructor(category, message) {
25
+ super(message);
26
+ this.category = category;
27
+ this.name = 'AgentContextPreflightError';
28
+ }
29
+ }
30
+ function compact(value, limit) {
31
+ return redactAgentContextText(String(value ?? '').replace(/\s+/g, ' ').trim()).slice(0, limit);
32
+ }
33
+ export function redactAgentContextText(value) {
34
+ return value
35
+ .replace(/\b(Bearer\s+)[A-Za-z0-9._~+\/-]+=*/gi, `$1${REDACTED}`)
36
+ .replace(/(["']?(?:authorization|password|passwd|pwd|token|access[_-]?token|refresh[_-]?token|api[_-]?key|secret|client[_-]?secret|credential(?:_ref)?|private[_-]?key)["']?\s*[:=]\s*)(["'])(.*?)(\2)/gi, `$1$2${REDACTED}$4`)
37
+ .replace(/\b((?:authorization|password|passwd|pwd|token|access[_-]?token|refresh[_-]?token|api[_-]?key|secret|client[_-]?secret|credential(?:_ref)?|private[_-]?key)\s*[:=]\s*)[^\s,;]+/gi, `$1${REDACTED}`)
38
+ .replace(/\b(?:gh[pousr]_[A-Za-z0-9]{20,}|sk-[A-Za-z0-9_-]{16,})\b/g, REDACTED);
39
+ }
40
+ function isSensitiveKey(key) {
41
+ return SENSITIVE_KEY_NAMES.has(key.replace(/[^a-z0-9]/gi, '').toLowerCase());
42
+ }
43
+ function boundedCollection(value) {
44
+ if (!Array.isArray(value))
45
+ return [];
46
+ return value.slice(-COLLECTION_MAX_ITEMS).map((item) => compact(typeof item === 'string' ? item : JSON.stringify(redactContractValue(item)), COLLECTION_ITEM_MAX_CHARS));
47
+ }
48
+ function redactContractValue(value) {
49
+ if (typeof value === 'string')
50
+ return redactAgentContextText(value).slice(0, 2048);
51
+ if (Array.isArray(value))
52
+ return value.map(redactContractValue);
53
+ if (value && typeof value === 'object') {
54
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
55
+ key,
56
+ isSensitiveKey(key) ? REDACTED : redactContractValue(item),
57
+ ]));
58
+ }
59
+ return value;
60
+ }
61
+ function enforceContextBudget(contract) {
62
+ contract = redactContractValue(contract);
63
+ const order = ['priorProgress', 'relatedTickets', 'recentDecisions', 'unresolvedQuestions', 'verificationCommands'];
64
+ for (const field of order) {
65
+ while (JSON.stringify(contract, null, 2).length > CONTRACT_JSON_MAX_CHARS && contract[field].length > 0) {
66
+ contract[field].shift();
67
+ }
68
+ }
69
+ if (JSON.stringify(contract, null, 2).length > CONTRACT_JSON_MAX_CHARS) {
70
+ throw new AgentContextPreflightError('ticket', '필수 Agent Context가 전체 크기 제한을 초과했습니다');
71
+ }
72
+ return contract;
73
+ }
74
+ function redactRemoteUrl(value) {
75
+ const raw = compact(value, 2048);
76
+ if (!raw)
77
+ return null;
78
+ try {
79
+ const parsed = new URL(raw);
80
+ parsed.username = '';
81
+ parsed.password = '';
82
+ parsed.search = '';
83
+ parsed.hash = '';
84
+ return parsed.toString();
85
+ }
86
+ catch {
87
+ return raw.replace(/:\/\/[^/@]+@/, '://').replace(/[?#].*$/, '');
88
+ }
89
+ }
90
+ export function buildAgentContextContract(input) {
91
+ const ticket = input.ticket;
92
+ if (!ticket?.id)
93
+ throw new AgentContextPreflightError('ticket', 'ticket.id가 없습니다');
94
+ if (!ticket.current_column_id || !ticket.current_column_name) {
95
+ throw new AgentContextPreflightError('column', '현재 column 확정값이 없습니다');
96
+ }
97
+ const repositoryComplete = Boolean(input.repository?.resourceId
98
+ && input.repository.cwd
99
+ && input.repository.baseBranch
100
+ && input.repository.baseSha);
101
+ if (ticket.__awb_require_repository_context && !repositoryComplete) {
102
+ throw new AgentContextPreflightError('repository', '연결된 저장소의 준비 결과가 없습니다');
103
+ }
104
+ const comments = Array.isArray(ticket.comments) ? ticket.comments.slice(-5) : [];
105
+ const metadata = ticket.__awb_context_metadata || {};
106
+ return enforceContextBudget({
107
+ version: AGENT_CONTEXT_VERSION,
108
+ authority: ['system_policy', 'role_instructions', 'project_instructions', 'task', 'prior_progress'],
109
+ assignment: {
110
+ workspaceId: compact(ticket.workspace_id, 256),
111
+ boardId: compact(ticket.board_id || ticket.board?.id, 256),
112
+ ticketId: compact(ticket.id, 256),
113
+ role: compact(input.role, 120),
114
+ column: {
115
+ id: ticket.current_column_id,
116
+ name: ticket.current_column_name,
117
+ kind: ticket.current_column_kind || '',
118
+ },
119
+ },
120
+ repository: input.repository ? {
121
+ resourceId: input.repository.resourceId,
122
+ cwd: input.repository.cwd,
123
+ remoteUrl: redactRemoteUrl(input.repository.remoteUrl ?? metadata.remoteUrl),
124
+ defaultBranch: input.repository.defaultBranch ?? metadata.defaultBranch ?? null,
125
+ baseBranch: input.repository.baseBranch,
126
+ baseSha: input.repository.baseSha,
127
+ fetchedSha: input.repository.fetchedSha ?? input.repository.baseSha,
128
+ currentSha: input.repository.currentSha || null,
129
+ currentShaFailure: input.repository.currentShaFailure ?? null,
130
+ workingBranch: input.repository.workingBranch,
131
+ dirty: input.repository.dirty,
132
+ ahead: input.repository.ahead,
133
+ behind: input.repository.behind,
134
+ resumed: input.repository.resumed,
135
+ } : null,
136
+ execution: {
137
+ model: input.harness?.model || input.runtimeProfile?.model || '',
138
+ effort: input.effort ?? metadata.effort ?? null,
139
+ permissionMode: input.harness?.permission_mode || 'managed-default',
140
+ sandbox: metadata.sandbox ?? input.harness?.permission_mode ?? 'managed-default',
141
+ mcpAvailable: input.mcpAvailable !== false,
142
+ mcpServers: Array.isArray(metadata.mcpServers) ? metadata.mcpServers : [],
143
+ sessionMode: input.sessionMode || 'stateless',
144
+ credentialAvailable: metadata.credentialAvailable ?? input.repository?.credentialAvailable ?? null,
145
+ credentialFailure: metadata.credentialFailure ?? input.repository?.credentialFailure ?? null,
146
+ requestedGitOperation: metadata.requestedGitOperation ?? null,
147
+ },
148
+ relatedTickets: boundedCollection(metadata.relatedTickets),
149
+ recentDecisions: boundedCollection(metadata.recentDecisions),
150
+ unresolvedQuestions: boundedCollection(metadata.unresolvedQuestions),
151
+ verificationCommands: boundedCollection(metadata.verificationCommands),
152
+ priorProgress: comments.map((comment) => ({
153
+ at: comment.created_at || '',
154
+ author: compact(comment.author_name || comment.agent_name || comment.author || 'unknown', 120),
155
+ content: compact(comment.body || comment.content, 1200),
156
+ })),
157
+ });
158
+ }
159
+ export function renderAgentContextContract(contract) {
160
+ const rendered = [
161
+ `AWB Agent Context Contract v${contract.version} (provider-neutral, redacted):`,
162
+ JSON.stringify(contract, null, 2),
163
+ ].join('\n');
164
+ if (rendered.length > AGENT_CONTEXT_MAX_CHARS) {
165
+ throw new AgentContextPreflightError('ticket', '직렬화된 Agent Context가 전체 크기 제한을 초과했습니다');
166
+ }
167
+ return rendered;
168
+ }
169
+ //# sourceMappingURL=agent-context-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-context-contract.js","sourceRoot":"","sources":["../../src/lib/agent-context-contract.ts"],"names":[],"mappings":"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"}
@@ -35,6 +35,7 @@ export declare function resolveBootstrapRepository(baseRepo: unknown, baseBranch
35
35
  resourceId: string;
36
36
  url: string;
37
37
  branch: string;
38
+ defaultBranch: string | null;
38
39
  } | null;
39
40
  export declare function parseWorktreeMode(raw: unknown): WorktreeMode | undefined;
40
41
  export declare function buildDispatchEnvVars(boardEnv: Record<string, string> | null | undefined, cwd: string | undefined, worktreeMode: WorktreeMode | undefined, ticketId: unknown): Record<string, string>;
@@ -2,11 +2,12 @@ var _a;
2
2
  import { log } from './logging.js';
3
3
  import { loadAgentInfo } from './config.js';
4
4
  import { spawnFailureTracker } from './spawn-failure-tracker.js';
5
- import { fetchTicketContext, fetchChatRoomHistory, fetchOrdinaryWorkBoardCandidates, fetchAgentRecord, fetchRepositoryCredential, hasNewAgentComment, postFsResponse, postChatRoomMessage, postDispatchAck, provisionManagedAgentApiKey, } from './rest.js';
6
- import { readApiKey, writeApiKey, writeMcpConfig } from './managed-agent-store.js';
5
+ import { fetchTicketContext, fetchChatRoomHistory, fetchOrdinaryWorkBoardCandidates, fetchAgentRecord, fetchRepositoryCredentialStatus, hasNewAgentComment, postFsResponse, postChatRoomMessage, postDispatchAck, provisionManagedAgentApiKey, } from './rest.js';
6
+ import { readApiKey, readMcpConfigServerNames, writeApiKey, writeMcpConfig } from './managed-agent-store.js';
7
7
  import { recordEvent } from './event-log-recorder.js';
8
8
  import { prepareChatAttachments } from './chat-attachment-prep.js';
9
9
  import { injectWorkFolder, repositoryContextInstructions, worktreeInstructionsFor } from './prompts.js';
10
+ import { AGENT_CONTEXT_VERSION, AgentContextPreflightError } from './agent-context-contract.js';
10
11
  import { DispatchBlockerTracker, DispatchBlockTracker, InflightDispatchTracker, PendingDispatchRetry, RoleSpawnSuppressor, classifyWorktreeOutcome, decideCliAuthReadiness, decideCliTrustReadiness, isSafeTicketProvisioningFallback, managedWorktreePath, provisioningPendReason } from './dispatch-preflight.js';
11
12
  import { SessionLimitDeferStore } from './session-limit-defer.js';
12
13
  import { createAdapter, ADAPTER_CAPABILITIES } from './cli-adapters/index.js';
@@ -207,10 +208,13 @@ export function resolveBootstrapRepository(baseRepo, baseBranch, environment) {
207
208
  if (ticketUrl) {
208
209
  const branch = (typeof baseBranch === 'string' ? baseBranch.trim() : '')
209
210
  || (typeof repo?.default_branch === 'string' ? repo.default_branch.trim() : '');
210
- return { resourceId: typeof repo?.id === 'string' ? repo.id : '', url: ticketUrl, branch };
211
+ const defaultBranch = typeof repo?.default_branch === 'string' && repo.default_branch.trim()
212
+ ? repo.default_branch.trim()
213
+ : null;
214
+ return { resourceId: typeof repo?.id === 'string' ? repo.id : '', url: ticketUrl, branch, defaultBranch };
211
215
  }
212
216
  const boardRepo = environment?.repositories[0];
213
- return boardRepo ? { resourceId: boardRepo.resource_id || '', url: boardRepo.url, branch: boardRepo.branch } : null;
217
+ return boardRepo ? { resourceId: boardRepo.resource_id || '', url: boardRepo.url, branch: boardRepo.branch, defaultBranch: null } : null;
214
218
  }
215
219
  export function parseWorktreeMode(raw) {
216
220
  return raw === 'per_ticket' || raw === 'shared' ? raw : undefined;
@@ -1035,13 +1039,20 @@ export class EventDispatcher {
1035
1039
  return;
1036
1040
  }
1037
1041
  const selectedRepo = resolveBootstrapRepository(ev.base_repo, ev.base_branch, envConfig);
1038
- const repoCredential = selectedRepo?.resourceId && agentContext?.agent_id
1039
- ? await fetchRepositoryCredential(this.#config, selectedRepo.resourceId, agentContext.agent_id, ev.workspace_id)
1040
- : null;
1042
+ const repositoryContextRequired = Boolean(selectedRepo || ev.base_repo || ev.repository_context_required === true || envConfig?.repositories.length);
1043
+ const repoCredentialStatus = selectedRepo?.resourceId && agentContext?.agent_id
1044
+ ? await fetchRepositoryCredentialStatus(this.#config, selectedRepo.resourceId, agentContext.agent_id, ev.workspace_id)
1045
+ : { credential: null, failure: selectedRepo ? 'credential_lookup_not_applicable' : null };
1046
+ const repoCredential = repoCredentialStatus.credential;
1041
1047
  const worktreeMode = parseWorktreeMode(ev.worktree_mode);
1042
1048
  const applyWorktree = () => this.#applyWorktreeCwd(agentContext, ev.ticket_id, ev.action, worktreeMode, typeof ev.max_concurrent_tickets_per_agent === 'number'
1043
1049
  ? ev.max_concurrent_tickets_per_agent
1044
- : undefined, selectedRepo ? { ...selectedRepo, credential: repoCredential } : null);
1050
+ : undefined, selectedRepo ? {
1051
+ resourceId: selectedRepo.resourceId,
1052
+ url: selectedRepo.url,
1053
+ branch: selectedRepo.branch,
1054
+ credential: repoCredential,
1055
+ } : null);
1045
1056
  let worktreeProvision = await applyWorktree();
1046
1057
  if (!worktreeProvision.ok && worktreeProvision.reason === 'pool_exhausted') {
1047
1058
  this.#dispatchBlockTracker.record(worktreeProvision.blockerKind || 'worktree:pool_exhausted');
@@ -1224,9 +1235,67 @@ export class EventDispatcher {
1224
1235
  repositoryContextInstructions(worktreeProvision.repositoryContext),
1225
1236
  worktreeProvision.recoveryInstructions || '',
1226
1237
  ].filter(Boolean).join('\n\n');
1238
+ const activeMcpServers = agentContext?.cli === 'hermes'
1239
+ ? ['awb']
1240
+ : await readMcpConfigServerNames(agentContext?.mcp_config_path || '');
1241
+ const attachContextContract = (ticket, sessionMode) => {
1242
+ if (!ticket) {
1243
+ throw new AgentContextPreflightError('ticket', 'ticket context 조회 결과가 없습니다');
1244
+ }
1245
+ ticket.id = ticket.id || ev.ticket_id || '';
1246
+ ticket.current_column_id = ev.current_column_id || ticket.current_column_id || '';
1247
+ ticket.current_column_name = ev.current_column_name || ticket.current_column_name || '';
1248
+ ticket.current_column_kind = ev.current_column_kind || ticket.current_column_kind || '';
1249
+ ticket.__awb_role = ev.action || '';
1250
+ ticket.__awb_enforce_context_contract = true;
1251
+ ticket.__awb_repository_context = worktreeProvision.repositoryContext;
1252
+ const requirePreparedRepository = repositoryContextRequired && !worktreeProvision.recoveryInstructions;
1253
+ ticket.__awb_require_repository_context = requirePreparedRepository;
1254
+ if (requirePreparedRepository && !worktreeProvision.repositoryContext) {
1255
+ throw new AgentContextPreflightError('repository', '연결된 저장소의 준비 결과가 없습니다');
1256
+ }
1257
+ ticket.__awb_harness = harness;
1258
+ ticket.__awb_runtime_profile = runtimeProfile;
1259
+ ticket.__awb_session_mode = sessionMode;
1260
+ ticket.__awb_effort = effortPreset?.id ?? null;
1261
+ ticket.__awb_context_metadata = {
1262
+ remoteUrl: selectedRepo?.url ?? null,
1263
+ defaultBranch: selectedRepo?.defaultBranch ?? worktreeProvision.repositoryContext?.defaultBranch ?? null,
1264
+ credentialAvailable: selectedRepo ? Boolean(repoCredential) : null,
1265
+ credentialFailure: selectedRepo ? repoCredentialStatus.failure : null,
1266
+ sandbox: harness?.permission_mode ?? 'managed-default',
1267
+ requestedGitOperation: ev.action === 'assignee' ? 'commit_and_push' : 'read_only',
1268
+ mcpServers: activeMcpServers,
1269
+ relatedTickets: ticket.related_tickets ?? [],
1270
+ recentDecisions: ticket.recent_decisions ?? [],
1271
+ unresolvedQuestions: ticket.unresolved_questions ?? [],
1272
+ verificationCommands: ticket.verification_commands ?? [],
1273
+ };
1274
+ if (ticket.__awb_repository_context) {
1275
+ Object.assign(ticket.__awb_repository_context, {
1276
+ remoteUrl: selectedRepo?.url,
1277
+ defaultBranch: selectedRepo?.defaultBranch ?? ticket.__awb_repository_context.defaultBranch,
1278
+ fetchedSha: ticket.__awb_repository_context.baseSha,
1279
+ credentialAvailable: selectedRepo ? Boolean(repoCredential) : null,
1280
+ credentialFailure: selectedRepo ? repoCredentialStatus.failure : null,
1281
+ });
1282
+ }
1283
+ log(`Agent context v${AGENT_CONTEXT_VERSION}: ticket=${ev.ticket_id} role=${ev.action || ''} ` +
1284
+ `cwd=${worktreeProvision.repositoryContext?.cwd || agentContext?.cwd || ''} ` +
1285
+ `resource=${worktreeProvision.repositoryContext?.resourceId || ''} ` +
1286
+ `branch=${worktreeProvision.repositoryContext?.workingBranch || ''} ` +
1287
+ `base=${worktreeProvision.repositoryContext?.baseSha || ''} ` +
1288
+ `head=${worktreeProvision.repositoryContext?.currentSha || ''} ` +
1289
+ `dirty=${worktreeProvision.repositoryContext?.dirty ?? false} ` +
1290
+ `repositoryRequired=${repositoryContextRequired} ` +
1291
+ `model=${harness?.model || runtimeProfile?.model || agentContext?.model || ''} ` +
1292
+ `permission=${harness?.permission_mode || 'managed-default'} ` +
1293
+ `mcp=${activeMcpServers.join(',') || 'none'} session=${sessionMode}`);
1294
+ return ticket;
1295
+ };
1227
1296
  if (agentContext?.cli === 'hermes') {
1228
1297
  try {
1229
- const ticket = await fetchTicketContext(this.#config, ev.ticket_id);
1298
+ const ticket = attachContextContract(await fetchTicketContext(this.#config, ev.ticket_id), 'hermes');
1230
1299
  if (ticket) {
1231
1300
  ticket.current_column_id = ev.current_column_id || ticket.current_column_id || '';
1232
1301
  ticket.current_column_name = ev.current_column_name || ticket.current_column_name || '';
@@ -1267,6 +1336,11 @@ export class EventDispatcher {
1267
1336
  }
1268
1337
  }
1269
1338
  catch (err) {
1339
+ if (err instanceof AgentContextPreflightError) {
1340
+ log(`Agent context preflight failed: category=${err.category} ticket=${ev.ticket_id} ${err.message}`);
1341
+ this.#ackDispatch(ev, 'nack', `agent_context_${err.category}_missing`);
1342
+ return;
1343
+ }
1270
1344
  log(`Hermes trigger dispatch failed closed: ${err?.code || ''} ${err?.message ?? err}`);
1271
1345
  this.#ackDispatch(ev, 'nack', err?.code || 'runtime_protocol_error');
1272
1346
  }
@@ -1279,7 +1353,7 @@ export class EventDispatcher {
1279
1353
  let dispatchTriggerInvoked = false;
1280
1354
  if (delegationEnabled && persistentTicket && this.#ticketSessionManager) {
1281
1355
  try {
1282
- const ticket = await fetchTicketContext(this.#config, ev.ticket_id);
1356
+ const ticket = attachContextContract(await fetchTicketContext(this.#config, ev.ticket_id), 'persistent');
1283
1357
  if (ticket) {
1284
1358
  ticket.current_column_id = ev.current_column_id || ticket.current_column_id || '';
1285
1359
  ticket.current_column_name = ev.current_column_name || ticket.current_column_name || '';
@@ -1333,6 +1407,11 @@ export class EventDispatcher {
1333
1407
  log(`Ticket session dispatch declined (${result.reason}), falling back to one-shot subagent`);
1334
1408
  }
1335
1409
  catch (err) {
1410
+ if (err instanceof AgentContextPreflightError) {
1411
+ log(`Agent context preflight failed: category=${err.category} ticket=${ev.ticket_id} ${err.message}`);
1412
+ this.#ackDispatch(ev, 'nack', `agent_context_${err.category}_missing`);
1413
+ return;
1414
+ }
1336
1415
  circuitBreakerClearedByDispatchTrigger = dispatchTriggerInvoked;
1337
1416
  log(`Ticket session path failed: ${err?.message ?? err}, falling back to one-shot subagent`);
1338
1417
  }
@@ -1340,7 +1419,7 @@ export class EventDispatcher {
1340
1419
  const canDelegate = delegationEnabled && this.#subagentManager && this.#subagentManager.canSpawn();
1341
1420
  if (canDelegate && this.#subagentManager) {
1342
1421
  try {
1343
- const ticket = await fetchTicketContext(this.#config, ev.ticket_id);
1422
+ const ticket = attachContextContract(await fetchTicketContext(this.#config, ev.ticket_id), 'stateless');
1344
1423
  if (ticket && selectedRepo) {
1345
1424
  ticket.base_repo = { id: selectedRepo.resourceId, name: '', url: selectedRepo.url, default_branch: selectedRepo.branch };
1346
1425
  ticket.base_branch = selectedRepo.branch;
@@ -1417,6 +1496,11 @@ export class EventDispatcher {
1417
1496
  log(`Subagent spawn declined (${result.reason}); no further fallback in standalone mode`);
1418
1497
  }
1419
1498
  catch (err) {
1499
+ if (err instanceof AgentContextPreflightError) {
1500
+ log(`Agent context preflight failed: category=${err.category} ticket=${ev.ticket_id} ${err.message}`);
1501
+ this.#ackDispatch(ev, 'nack', `agent_context_${err.category}_missing`);
1502
+ return;
1503
+ }
1420
1504
  log(`Delegation path failed: ${err?.message ?? err}; dropping`);
1421
1505
  }
1422
1506
  }