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.
- package/dist/lib/agent-context-contract.d.ts +21 -0
- package/dist/lib/agent-context-contract.js +169 -0
- package/dist/lib/agent-context-contract.js.map +1 -0
- package/dist/lib/event-dispatcher.d.ts +1 -0
- package/dist/lib/event-dispatcher.js +95 -11
- package/dist/lib/event-dispatcher.js.map +1 -1
- package/dist/lib/managed-agent-store.d.ts +1 -0
- package/dist/lib/managed-agent-store.js +13 -0
- package/dist/lib/managed-agent-store.js.map +1 -1
- package/dist/lib/prompts.js +17 -1
- package/dist/lib/prompts.js.map +1 -1
- package/dist/lib/rest.d.ts +8 -0
- package/dist/lib/rest.js +9 -6
- package/dist/lib/rest.js.map +1 -1
- package/dist/lib/ticket-session-manager.d.ts +1 -0
- package/dist/lib/ticket-session-manager.js +32 -23
- package/dist/lib/ticket-session-manager.js.map +1 -1
- package/dist/lib/worktree-manager.d.ts +7 -0
- package/dist/lib/worktree-manager.js +3 -0
- package/dist/lib/worktree-manager.js.map +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
|
@@ -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,
|
|
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
|
-
|
|
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
|
|
1039
|
-
|
|
1040
|
-
|
|
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 ? {
|
|
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
|
}
|