baxian 2.0.6 → 2.0.7
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/README.md +2 -3
- package/README.zh-CN.md +2 -3
- package/dist/agent/bootstrap-poller.d.ts +2 -2
- package/dist/agent/bootstrap-poller.d.ts.map +1 -1
- package/dist/agent/bootstrap-poller.js.map +1 -1
- package/dist/agent/bootstrap.d.ts +1 -1
- package/dist/agent/bootstrap.d.ts.map +1 -1
- package/dist/agent/bootstrap.js +24 -48
- package/dist/agent/bootstrap.js.map +1 -1
- package/dist/agent/manager.d.ts +9 -35
- package/dist/agent/manager.d.ts.map +1 -1
- package/dist/agent/manager.js +86 -335
- package/dist/agent/manager.js.map +1 -1
- package/dist/agent/net-exec.d.ts +0 -1
- package/dist/agent/net-exec.d.ts.map +1 -1
- package/dist/agent/net-exec.js +0 -1
- package/dist/agent/net-exec.js.map +1 -1
- package/dist/agent/pane-streamer-manager.d.ts.map +1 -1
- package/dist/agent/pane-streamer-manager.js +1 -3
- package/dist/agent/pane-streamer-manager.js.map +1 -1
- package/dist/agent/preflight.d.ts +1 -20
- package/dist/agent/preflight.d.ts.map +1 -1
- package/dist/agent/preflight.js +39 -122
- package/dist/agent/preflight.js.map +1 -1
- package/dist/agent/prompt.d.ts +6 -16
- package/dist/agent/prompt.d.ts.map +1 -1
- package/dist/agent/prompt.js +128 -79
- package/dist/agent/prompt.js.map +1 -1
- package/dist/agent/repo-store.d.ts +1 -5
- package/dist/agent/repo-store.d.ts.map +1 -1
- package/dist/agent/repo-store.js +8 -33
- package/dist/agent/repo-store.js.map +1 -1
- package/dist/agent/runner.d.ts +1 -1
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +1 -3
- package/dist/agent/runner.js.map +1 -1
- package/dist/api/config.d.ts +1 -2
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +5 -22
- package/dist/api/config.js.map +1 -1
- package/dist/api/platform-guard.d.ts.map +1 -1
- package/dist/api/platform-guard.js +5 -15
- package/dist/api/platform-guard.js.map +1 -1
- package/dist/api/projects.d.ts.map +1 -1
- package/dist/api/projects.js +18 -25
- package/dist/api/projects.js.map +1 -1
- package/dist/api/tasks.js +2 -2
- package/dist/api/tasks.js.map +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/config/hot-reload.d.ts +3 -2
- package/dist/config/hot-reload.d.ts.map +1 -1
- package/dist/config/hot-reload.js +6 -16
- package/dist/config/hot-reload.js.map +1 -1
- package/dist/config/loader.d.ts +1 -5
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +31 -31
- package/dist/config/loader.js.map +1 -1
- package/dist/config/validator.d.ts +2 -8
- package/dist/config/validator.d.ts.map +1 -1
- package/dist/config/validator.js +42 -120
- package/dist/config/validator.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -29
- package/dist/index.js.map +1 -1
- package/dist/platform/driver-host.d.ts +4 -3
- package/dist/platform/driver-host.d.ts.map +1 -1
- package/dist/platform/driver-host.js +12 -11
- package/dist/platform/driver-host.js.map +1 -1
- package/dist/platform/feedback.d.ts +5 -6
- package/dist/platform/feedback.d.ts.map +1 -1
- package/dist/platform/feedback.js +4 -7
- package/dist/platform/feedback.js.map +1 -1
- package/dist/platform/github-driver.d.ts +36 -0
- package/dist/platform/github-driver.d.ts.map +1 -0
- package/dist/platform/github-driver.js +374 -0
- package/dist/platform/github-driver.js.map +1 -0
- package/dist/platform/markers.d.ts +1 -4
- package/dist/platform/markers.d.ts.map +1 -1
- package/dist/platform/markers.js +0 -8
- package/dist/platform/markers.js.map +1 -1
- package/dist/platform/platform-poller.d.ts +2 -17
- package/dist/platform/platform-poller.d.ts.map +1 -1
- package/dist/platform/platform-poller.js +9 -15
- package/dist/platform/platform-poller.js.map +1 -1
- package/dist/platform/response-parser.d.ts.map +1 -1
- package/dist/platform/response-parser.js +6 -63
- package/dist/platform/response-parser.js.map +1 -1
- package/dist/platform/review-timeline.d.ts +6 -8
- package/dist/platform/review-timeline.d.ts.map +1 -1
- package/dist/platform/review-timeline.js +4 -6
- package/dist/platform/review-timeline.js.map +1 -1
- package/dist/platform/row-schema.d.ts +1 -2
- package/dist/platform/row-schema.d.ts.map +1 -1
- package/dist/platform/row-schema.js +13 -1
- package/dist/platform/row-schema.js.map +1 -1
- package/dist/platform/startup.d.ts +6 -34
- package/dist/platform/startup.d.ts.map +1 -1
- package/dist/platform/startup.js +10 -109
- package/dist/platform/startup.js.map +1 -1
- package/dist/platform/types.d.ts +61 -54
- package/dist/platform/types.d.ts.map +1 -1
- package/dist/platform/types.js +38 -26
- package/dist/platform/types.js.map +1 -1
- package/dist/platform/verdict-engine.d.ts +1 -1
- package/dist/platform/verdict-engine.d.ts.map +1 -1
- package/dist/platform/verdict-engine.js +1 -1
- package/dist/platform/verdict-engine.js.map +1 -1
- package/dist/shared/constants.d.ts +2 -6
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/constants.js +2 -17
- package/dist/shared/constants.js.map +1 -1
- package/dist/shared/git-url.d.ts +0 -14
- package/dist/shared/git-url.d.ts.map +1 -1
- package/dist/shared/git-url.js +4 -57
- package/dist/shared/git-url.js.map +1 -1
- package/dist/shared/types.d.ts +2 -10
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.js.map +1 -1
- package/dist/state/agent-store.js +0 -3
- package/dist/state/agent-store.js.map +1 -1
- package/dist/state/task-store.js +2 -2
- package/dist/state/task-store.js.map +1 -1
- package/dist/web/assets/index-KH_4rYrI.js +15 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/config/normalizer.d.ts +0 -2
- package/dist/config/normalizer.d.ts.map +0 -1
- package/dist/config/normalizer.js +0 -46
- package/dist/config/normalizer.js.map +0 -1
- package/dist/platform/command-renderer.d.ts +0 -29
- package/dist/platform/command-renderer.d.ts.map +0 -1
- package/dist/platform/command-renderer.js +0 -113
- package/dist/platform/command-renderer.js.map +0 -1
- package/dist/platform/driver-spec.d.ts +0 -7
- package/dist/platform/driver-spec.d.ts.map +0 -1
- package/dist/platform/driver-spec.js +0 -506
- package/dist/platform/driver-spec.js.map +0 -1
- package/dist/platform/field-mapper.d.ts +0 -7
- package/dist/platform/field-mapper.d.ts.map +0 -1
- package/dist/platform/field-mapper.js +0 -109
- package/dist/platform/field-mapper.js.map +0 -1
- package/dist/platform/git-driver.d.ts +0 -64
- package/dist/platform/git-driver.d.ts.map +0 -1
- package/dist/platform/git-driver.js +0 -230
- package/dist/platform/git-driver.js.map +0 -1
- package/dist/platform/manifest.d.ts +0 -11
- package/dist/platform/manifest.d.ts.map +0 -1
- package/dist/platform/manifest.js +0 -42
- package/dist/platform/manifest.js.map +0 -1
- package/dist/platform/plugin-registry.d.ts +0 -31
- package/dist/platform/plugin-registry.d.ts.map +0 -1
- package/dist/platform/plugin-registry.js +0 -239
- package/dist/platform/plugin-registry.js.map +0 -1
- package/dist/platform/plugin-roots.d.ts +0 -3
- package/dist/platform/plugin-roots.d.ts.map +0 -1
- package/dist/platform/plugin-roots.js +0 -9
- package/dist/platform/plugin-roots.js.map +0 -1
- package/dist/platform/plugins/github/baxian-plugin.json +0 -8
- package/dist/platform/plugins/github/driver.json +0 -153
- package/dist/platform/plugins/github/skills/baxian-cli-gh/SKILL.md +0 -115
- package/dist/platform/plugins/github/skills/baxian-cli-gh/agents/openai.yaml +0 -2
- package/dist/platform/preflight-exec.d.ts +0 -21
- package/dist/platform/preflight-exec.d.ts.map +0 -1
- package/dist/platform/preflight-exec.js +0 -53
- package/dist/platform/preflight-exec.js.map +0 -1
- package/dist/skill/registry.d.ts +0 -37
- package/dist/skill/registry.d.ts.map +0 -1
- package/dist/skill/registry.js +0 -234
- package/dist/skill/registry.js.map +0 -1
- package/dist/skills/baxian-greeting/SKILL.md +0 -15
- package/dist/skills/baxian-greeting/agents/openai.yaml +0 -2
- package/dist/skills/baxian-pr-feedback/SKILL.md +0 -41
- package/dist/skills/baxian-pr-feedback/agents/openai.yaml +0 -2
- package/dist/skills/baxian-pr-recheck/SKILL.md +0 -27
- package/dist/skills/baxian-pr-recheck/agents/openai.yaml +0 -2
- package/dist/skills/baxian-pr-review/SKILL.md +0 -33
- package/dist/skills/baxian-pr-review/agents/openai.yaml +0 -2
- package/dist/skills/baxian-signals/SKILL.md +0 -86
- package/dist/skills/baxian-signals/agents/openai.yaml +0 -2
- package/dist/skills/baxian-task-check/SKILL.md +0 -52
- package/dist/skills/baxian-task-check/agents/openai.yaml +0 -2
- package/dist/web/assets/index-CF2C4Fq-.js +0 -15
package/dist/agent/manager.js
CHANGED
|
@@ -6,8 +6,8 @@ import { tmpdir } from 'node:os';
|
|
|
6
6
|
import { createSignalToken, PASSIVE_VERDICT_WATCH } from './phase-signal.js';
|
|
7
7
|
import { tmuxInstallHint } from './preflight.js';
|
|
8
8
|
import { BRANCH_PREFIX, isValidBranchName, PHASE_EXPECTED_STATUS, PHASE_REQUIRES_AGENT_BOUND_TO_TASK, TASK_TERMINAL_STATUSES as TERMINAL_STATUSES, TASK_ACTIVE_STATUS_SET as ACTIVE_TASK_STATUSES, TASK_OWNER_ROLES, effectiveTaskReviewRound, isSpecStagePhase, isTaskOpen, repoIdentityKey, taskAttentionGeneration, taskGenerationGuard, taskReviewRound, taskMatchesGeneration, } from '../shared/index.js';
|
|
9
|
-
import { buildProjectDriver, makeDriverExec } from '../platform/driver-host.js';
|
|
10
|
-
import { DriverOpError,
|
|
9
|
+
import { buildProjectDriver, buildProjectPromptContext, makeDriverExec, } from '../platform/driver-host.js';
|
|
10
|
+
import { DriverOpError, safeDriverErrorText, validateCommentBody, } from '../platform/types.js';
|
|
11
11
|
import { versionTimeOf } from '../platform/row-schema.js';
|
|
12
12
|
import { checkOpenPrBinding, checkPrBinding } from '../platform/pr-binding.js';
|
|
13
13
|
import { collectPendingFeedback, scanCommentSourcesOnce } from '../platform/feedback.js';
|
|
@@ -15,21 +15,18 @@ import { prReviewCacheRevision } from '../platform/pr-conversation-cache.js';
|
|
|
15
15
|
import { deadTokens, recheckPassProvenance } from '../platform/verdict-engine.js';
|
|
16
16
|
import { platformBindingMismatch, taskNeedsPlatformBindingAudit, } from '../platform/startup.js';
|
|
17
17
|
import { AGENT_STORE_NOOP } from '../state/agent-store.js';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { execOutcomeUnknown, isTransientNetworkFailure } from './net-exec.js';
|
|
18
|
+
import { createRunner, LocalRunner, shellQuote, resolveAgentHost, workdirHostGroupKey, } from './runner.js';
|
|
19
|
+
import { isTransientNetworkFailure } from './net-exec.js';
|
|
21
20
|
import { imageFilename, agentHostPath, writeImageToHost } from './image-input.js';
|
|
22
21
|
import { TmuxManager, ReplNotReadyError, detectStartupDialog, detectRuntimeMenu, runtimeBusyCheck, hasRuntimeReadyView, hasReplProcTitle, hasOscTitleWorking, hasOscTitleIdle, screenAllowsTitleIdle, PaneGoneError, } from './tmux.js';
|
|
23
22
|
import { BranchManager, DirtyWorkdirError, ReviewHeadMismatchError, isAutoDeletableTaskBranch } from './branch.js';
|
|
24
|
-
import { RepoStore, createRepoStoreCache
|
|
23
|
+
import { RepoStore, createRepoStoreCache } from './repo-store.js';
|
|
25
24
|
import { PhaseSignalWatcher, } from './phase-signal-watcher.js';
|
|
26
|
-
import { buildPromptInline, buildGreetingPrompt, PromptSizeError,
|
|
25
|
+
import { buildPromptInline, buildGreetingPrompt, PromptSizeError, MAX_PROMPT_BYTES_ROUTE_LIMIT, } from './prompt.js';
|
|
27
26
|
import { ApiError } from '../errors.js';
|
|
28
27
|
import { prepareConfig } from '../config/loader.js';
|
|
29
|
-
import { resolveProjectTool } from '../config/validator.js';
|
|
30
28
|
import { SHA_HEX_SOURCE } from '../platform/types.js';
|
|
31
29
|
import { bodyDigest } from '../platform/body-digest.js';
|
|
32
|
-
import { validateCommentBody } from '../platform/command-renderer.js';
|
|
33
30
|
import { buildReviewTokenLine, extractReviewTokens, rowTokens } from '../platform/markers.js';
|
|
34
31
|
const BENIGN_DISPATCH_CONFLICT_CODES = new Set(['dispatch-superseded', 'dispatch-in-flight']);
|
|
35
32
|
export function isBenignDispatchConflict(err) {
|
|
@@ -127,6 +124,7 @@ const REPL_EXIT_COMMAND = {
|
|
|
127
124
|
qodercli: '/quit',
|
|
128
125
|
};
|
|
129
126
|
const WORKDIR_SESSION_OPTION = '@baxian-workdir';
|
|
127
|
+
const TASK_CONTEXT_SESSION_OPTION = '@baxian-context-task-id';
|
|
130
128
|
function normalizedDir(left) {
|
|
131
129
|
if (!left)
|
|
132
130
|
return null;
|
|
@@ -147,17 +145,6 @@ async function sameDirOnHost(runner, left, right) {
|
|
|
147
145
|
};
|
|
148
146
|
return await physical(left) === await physical(right);
|
|
149
147
|
}
|
|
150
|
-
export function skillSubdirFor(runtime) {
|
|
151
|
-
switch (runtime) {
|
|
152
|
-
case 'codex':
|
|
153
|
-
case 'opencode':
|
|
154
|
-
return '.agents/skills';
|
|
155
|
-
case 'qodercli':
|
|
156
|
-
return '.qoder/skills';
|
|
157
|
-
default:
|
|
158
|
-
return '.claude/skills';
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
148
|
const DEFAULT_DISPATCH_ACK_TIMEOUT_MS = 30_000;
|
|
162
149
|
const DEFAULT_NEED_INPUT_RETRY_INTERVAL_MS = 5_000;
|
|
163
150
|
const DEFAULT_DISPATCH_SETTLE_TIMEOUT_MS = 3_000;
|
|
@@ -184,7 +171,7 @@ const RUNTIME_LIVENESS_SAMPLES = 3;
|
|
|
184
171
|
export function canDispatchWithBinding(binding) {
|
|
185
172
|
return !binding?.taskId && !binding?.creationToken && binding?.status !== 'awaiting_human';
|
|
186
173
|
}
|
|
187
|
-
|
|
174
|
+
const RECOVERABLE_QA_DISPATCH_HOLD_PHASES = new Set([
|
|
188
175
|
'checkout-preparation-failed',
|
|
189
176
|
'dirty-workdir',
|
|
190
177
|
]);
|
|
@@ -233,12 +220,10 @@ export class AgentManager {
|
|
|
233
220
|
taskStore;
|
|
234
221
|
lockManager;
|
|
235
222
|
eventBus;
|
|
236
|
-
skillRegistry;
|
|
237
223
|
runnerFactory;
|
|
238
224
|
repoStoreFactory;
|
|
239
225
|
repoCache;
|
|
240
226
|
paneStreamerManager;
|
|
241
|
-
pluginRegistry;
|
|
242
227
|
platformDefaultBranchOf;
|
|
243
228
|
prConversationCache;
|
|
244
229
|
platformDriverExec;
|
|
@@ -277,7 +262,7 @@ export class AgentManager {
|
|
|
277
262
|
divergedAgents = new Set();
|
|
278
263
|
deletionGeneration = new Map();
|
|
279
264
|
compactInFlight = new Set();
|
|
280
|
-
|
|
265
|
+
platformBindingInterventionKeys = new Set();
|
|
281
266
|
constructor(deps) {
|
|
282
267
|
const config = prepareConfig(deps.config);
|
|
283
268
|
this.config = config;
|
|
@@ -285,11 +270,9 @@ export class AgentManager {
|
|
|
285
270
|
this.taskStore = deps.taskStore;
|
|
286
271
|
this.lockManager = deps.lockManager;
|
|
287
272
|
this.eventBus = deps.eventBus;
|
|
288
|
-
this.skillRegistry = deps.skillRegistry ?? new SkillRegistry();
|
|
289
273
|
this.runnerFactory = deps.runnerFactory;
|
|
290
274
|
this.repoStoreFactory = deps.repoStoreFactory;
|
|
291
275
|
this.paneStreamerManager = deps.paneStreamerManager;
|
|
292
|
-
this.pluginRegistry = deps.pluginRegistry;
|
|
293
276
|
this.platformDefaultBranchOf = deps.platformDefaultBranchOf;
|
|
294
277
|
this.prConversationCache = deps.prConversationCache;
|
|
295
278
|
this.errorRecordStore = deps.errorRecordStore;
|
|
@@ -415,30 +398,19 @@ export class AgentManager {
|
|
|
415
398
|
data,
|
|
416
399
|
});
|
|
417
400
|
}
|
|
418
|
-
async emitConfigIntervention(projectId, data) {
|
|
419
|
-
const key = this.configInterventionKey(projectId, data);
|
|
420
|
-
if (this.configInterventionKeys.has(key))
|
|
421
|
-
return;
|
|
422
|
-
if (await this.emitIntervention(projectId, undefined, undefined, data)) {
|
|
423
|
-
this.configInterventionKeys.add(key);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
configInterventionKey(projectId, data) {
|
|
427
|
-
return `config:${projectId}:${JSON.stringify(data)}`;
|
|
428
|
-
}
|
|
429
401
|
platformBindingInterventionKey(task, mismatch) {
|
|
430
402
|
return `binding:${task.id}:${mismatch.reason}:${mismatch.differences.join(',')}:` +
|
|
431
403
|
`${JSON.stringify(mismatch.binding ?? null)}:${JSON.stringify(mismatch.live ?? null)}`;
|
|
432
404
|
}
|
|
433
|
-
|
|
434
|
-
for (const key of this.
|
|
405
|
+
retainPlatformBindingInterventionKeys(keys) {
|
|
406
|
+
for (const key of this.platformBindingInterventionKeys) {
|
|
435
407
|
if (!keys.has(key))
|
|
436
|
-
this.
|
|
408
|
+
this.platformBindingInterventionKeys.delete(key);
|
|
437
409
|
}
|
|
438
410
|
}
|
|
439
411
|
async emitPlatformBindingIntervention(task, mismatch) {
|
|
440
412
|
const key = this.platformBindingInterventionKey(task, mismatch);
|
|
441
|
-
if (this.
|
|
413
|
+
if (this.platformBindingInterventionKeys.has(key))
|
|
442
414
|
return;
|
|
443
415
|
const emitted = await this.emitIntervention(task.projectId, task.agentId || undefined, task.id, {
|
|
444
416
|
phase: 'platform-binding-mismatch',
|
|
@@ -448,7 +420,7 @@ export class AgentManager {
|
|
|
448
420
|
...(mismatch.live !== undefined ? { live: mismatch.live } : {}),
|
|
449
421
|
});
|
|
450
422
|
if (emitted)
|
|
451
|
-
this.
|
|
423
|
+
this.platformBindingInterventionKeys.add(key);
|
|
452
424
|
}
|
|
453
425
|
async recordError(input) {
|
|
454
426
|
if (!this.errorRecordStore)
|
|
@@ -606,13 +578,6 @@ export class AgentManager {
|
|
|
606
578
|
return { ...existing, workdir, updatedAt: new Date().toISOString() };
|
|
607
579
|
});
|
|
608
580
|
assertGenerationOpen();
|
|
609
|
-
try {
|
|
610
|
-
await this.provisionRepoSkills(runner, agent, workdir);
|
|
611
|
-
}
|
|
612
|
-
catch (err) {
|
|
613
|
-
throw new EnsureSessionError({ createdSession: false, agentId }, `skill provisioning failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
614
|
-
}
|
|
615
|
-
assertGenerationOpen();
|
|
616
581
|
let snapshot;
|
|
617
582
|
try {
|
|
618
583
|
snapshot = await tmux.getSessionSnapshot(agentId);
|
|
@@ -641,31 +606,6 @@ export class AgentManager {
|
|
|
641
606
|
}
|
|
642
607
|
return this.adoptOrRestartSessionLocked(tmux, runner, agent, agentId, workdir, workdir, snapshot);
|
|
643
608
|
}
|
|
644
|
-
agentSkillScope(agentId) {
|
|
645
|
-
const projectId = this.getAgentConfig(agentId)?.projectId;
|
|
646
|
-
if (projectId === undefined)
|
|
647
|
-
return { pluginTools: [] };
|
|
648
|
-
const project = this.getProjectConfig(projectId);
|
|
649
|
-
const tool = project === undefined ? undefined : resolveProjectTool(project);
|
|
650
|
-
return { pluginTools: tool === undefined ? [] : [tool] };
|
|
651
|
-
}
|
|
652
|
-
async provisionRepoSkills(runner, agent, workdir) {
|
|
653
|
-
if (this.skillRegistry.names().length === 0)
|
|
654
|
-
return;
|
|
655
|
-
const scope = this.agentSkillScope(agent.id);
|
|
656
|
-
const subdir = skillSubdirFor(agent.runtime);
|
|
657
|
-
const destRoot = `${workdir}/${subdir}`;
|
|
658
|
-
await this.excludeInjectedSkills(runner, workdir, subdir);
|
|
659
|
-
// Batch inside the swept tree itself: a symlinked skills dir may live on another filesystem, where mv degrades to copy+unlink.
|
|
660
|
-
const trashDir = trashBatchDirUnder(`${destRoot}/.baxian-trash`, 'skills');
|
|
661
|
-
await this.runUnderSkillDirLock(this.skillDirLockKey(agent, workdir), async () => {
|
|
662
|
-
await this.ensureSkillDirSafe(runner, workdir, subdir, [
|
|
663
|
-
...this.skillRegistry.names(),
|
|
664
|
-
...this.skillRegistry.pluginSkillNames(scope),
|
|
665
|
-
], trashDir);
|
|
666
|
-
await this.skillRegistry.materialize((path, content) => this.atomicWriteFile(runner, path, content, trashDir), destRoot, scope);
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
609
|
sessionLifecycleChain = new Map();
|
|
670
610
|
adoptGeneration = new Map();
|
|
671
611
|
runUnderSessionLifecycle(agentId, fn) {
|
|
@@ -733,109 +673,6 @@ export class AgentManager {
|
|
|
733
673
|
return;
|
|
734
674
|
await this.rollbackCreatedSession(agentId, err.partial, reason);
|
|
735
675
|
}
|
|
736
|
-
skillDirChain = new Map();
|
|
737
|
-
skillDirLockKey(agent, workdir) {
|
|
738
|
-
const host = hostGroupKey(agent.mode, resolveAgentHost(this.config.host, agent.host));
|
|
739
|
-
const subdir = skillSubdirFor(agent.runtime);
|
|
740
|
-
const dir = workdir.replace(/\/+$/, '');
|
|
741
|
-
return `${host}:${dir}:${subdir}`;
|
|
742
|
-
}
|
|
743
|
-
runUnderSkillDirLock(key, fn) {
|
|
744
|
-
const prev = this.skillDirChain.get(key) ?? Promise.resolve();
|
|
745
|
-
const run = prev.then(fn, fn);
|
|
746
|
-
const settled = run.then(() => undefined, () => undefined);
|
|
747
|
-
this.skillDirChain.set(key, settled);
|
|
748
|
-
void settled.finally(() => {
|
|
749
|
-
if (this.skillDirChain.get(key) === settled)
|
|
750
|
-
this.skillDirChain.delete(key);
|
|
751
|
-
});
|
|
752
|
-
return run;
|
|
753
|
-
}
|
|
754
|
-
async atomicWriteFile(runner, finalPath, content, trashDir) {
|
|
755
|
-
const tmp = `${finalPath}.baxian-tmp-${randomBytes(6).toString('hex')}`;
|
|
756
|
-
try {
|
|
757
|
-
await stageFile(runner, tmp, content, { trashDir });
|
|
758
|
-
await moveFileIntoPlace(runner, tmp, finalPath, { trashDir });
|
|
759
|
-
}
|
|
760
|
-
catch (err) {
|
|
761
|
-
throw new Error(`atomic skill write failed (${finalPath}): ${err instanceof Error ? err.message : String(err)}`);
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
async ensureSkillDirSafe(runner, workdir, subdir, keepNames, trashDir) {
|
|
765
|
-
const SKILLS_SWEPT = 'BX_SKILLS_SWEPT';
|
|
766
|
-
const keep = keepNames.map((n) => `! -name ${shellQuote(n)}`).join(' ');
|
|
767
|
-
const batch = shellQuote(trashDir);
|
|
768
|
-
// Stale directories go first so the later link/file passes only see what stayed behind in kept dirs. -H follows a symlinked skills dir given as the start path; -exec … + propagates a mover failure into find's exit code (\; would swallow it); pruning .baxian-trash keeps the never-emptied trash out of the walk (it only grows, and one unreadable old batch would fail every sweep).
|
|
769
|
-
const prune = `-name .baxian-trash -prune -o`;
|
|
770
|
-
const findStaleDirs = `find -H ${subdir} -maxdepth 1 ${prune} -name 'baxian-*' ${keep}`;
|
|
771
|
-
const findStaleLinks = `find -H ${subdir} ${prune} -path '${subdir}/baxian-*' -type l`;
|
|
772
|
-
const findStaleFiles = `find -H ${subdir} ${prune} -path '${subdir}/baxian-*/*' -type f ! -name 'SKILL.md'`;
|
|
773
|
-
const mover = `dest="$0"; for f in "$@"; do ` +
|
|
774
|
-
`mkdir -p "$dest" || exit 1; ` +
|
|
775
|
-
`base=$(basename -- "$f"); t="$dest/$base"; n=0; ` +
|
|
776
|
-
`while [ -e "$t" ] || [ -L "$t" ]; do n=$((n+1)); t="$dest/$base.$n"; done; ` +
|
|
777
|
-
`mv -- "$f" "$t" || exit 1; ` +
|
|
778
|
-
`done`;
|
|
779
|
-
const moveStale = (find) => `${find} -exec sh -c '${mover}' ${batch} {} +`;
|
|
780
|
-
const inner = `cd ${shellQuote(workdir)} && ` +
|
|
781
|
-
`mkdir -p ${subdir} && ` +
|
|
782
|
-
`${moveStale(findStaleDirs)} && ` +
|
|
783
|
-
`${moveStale(findStaleLinks)} && ` +
|
|
784
|
-
`${moveStale(findStaleFiles)} && ` +
|
|
785
|
-
`printf '%s' ${SKILLS_SWEPT}`;
|
|
786
|
-
const res = await runner.exec(`sh -c ${shellQuote(inner)}`);
|
|
787
|
-
if (execOutcomeUnknown(res)) {
|
|
788
|
-
throw new Error(`skill sweep outcome unknown in ${workdir}/${subdir} (exit ${res.exitCode}): ${res.stderr.trim() || res.stdout.trim() || 'no output'}`);
|
|
789
|
-
}
|
|
790
|
-
if (res.exitCode !== 0 || !res.stdout.includes(SKILLS_SWEPT)) {
|
|
791
|
-
throw new Error(`failed to prepare ${subdir} in ${workdir}: ${res.stderr.trim() || `exit ${res.exitCode}, no completion marker`}`);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
async tagSessionSkillsVersion(tmux, agentId, ref) {
|
|
795
|
-
if (this.skillRegistry.names().length === 0)
|
|
796
|
-
return;
|
|
797
|
-
await this.setSessionOptions(tmux, agentId, ref, [
|
|
798
|
-
['@baxian-skills-version', this.skillRegistry.contentHash(this.agentSkillScope(agentId))],
|
|
799
|
-
]);
|
|
800
|
-
}
|
|
801
|
-
async replSkillsStale(tmux, agentId, ref) {
|
|
802
|
-
if (this.skillRegistry.names().length === 0)
|
|
803
|
-
return false;
|
|
804
|
-
const tagged = await tmux.getSessionOptionByRef(ref, agentId, '@baxian-skills-version');
|
|
805
|
-
return tagged !== this.skillRegistry.contentHash(this.agentSkillScope(agentId));
|
|
806
|
-
}
|
|
807
|
-
async excludeInjectedSkills(runner, workdir, subdir) {
|
|
808
|
-
// cd -P first: tracked/exclude/probe all run against the skills dir's PHYSICAL owning repo, so a symlink pointing back inside a worktree keeps the tracked-content protection while one pointing outside degrades to the plain non-git path.
|
|
809
|
-
const inner = `cd ${shellQuote(workdir)} && ` +
|
|
810
|
-
`mkdir -p ${subdir} && cd -P ${subdir} && ` +
|
|
811
|
-
`if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then echo BX_SKILLS_NON_GIT; exit 0; fi; ` +
|
|
812
|
-
`tracked="$(git ls-files -- 'baxian-*' 2>/dev/null | head -5)"; ` +
|
|
813
|
-
`if [ -n "$tracked" ]; then printf 'BX_SKILLS_TRACKED %s\\n' "$tracked" | head -1; exit 0; fi; ` +
|
|
814
|
-
`p="$(git rev-parse --git-path info/exclude)" || { echo BX_SKILLS_EXCLUDE_WRITE_FAILED; exit 0; }; ` +
|
|
815
|
-
// show-prefix is a literal path but lands in a gitignore pattern: escape fnmatch metacharacters or a dir like skill[dir] never matches its own rule.
|
|
816
|
-
`pre="$(git rev-parse --show-prefix 2>/dev/null | sed 's/[][\\*?]/\\\\&/g')"; rule="/\${pre}baxian-*"; trashrule="/\${pre}.baxian-trash"; ` +
|
|
817
|
-
`{ mkdir -p "$(dirname "$p")" && { grep -qxF "$rule" "$p" 2>/dev/null || printf '%s\\n' "$rule" >> "$p"; } ` +
|
|
818
|
-
`&& { grep -qxF "$trashrule" "$p" 2>/dev/null || printf '%s\\n' "$trashrule" >> "$p"; }; } ` +
|
|
819
|
-
`|| { echo BX_SKILLS_EXCLUDE_WRITE_FAILED; exit 0; }; ` +
|
|
820
|
-
`if git check-ignore -q -- 'baxian-__probe__/SKILL.md' ` +
|
|
821
|
-
`&& git check-ignore -q -- '.baxian-trash/__probe__'; then echo BX_SKILLS_OK; ` +
|
|
822
|
-
`else echo BX_SKILLS_EXCLUDE_INEFFECTIVE; fi`;
|
|
823
|
-
const res = await runner.exec(`sh -c ${shellQuote(inner)}`);
|
|
824
|
-
if (execOutcomeUnknown(res)) {
|
|
825
|
-
throw new Error(`skill exclusion probe outcome unknown in ${workdir} (exit ${res.exitCode}): ${res.stderr.trim() || res.stdout.trim() || 'no output'}`);
|
|
826
|
-
}
|
|
827
|
-
if (res.exitCode !== 0) {
|
|
828
|
-
throw new Error(`skill exclusion probe failed in ${workdir}: ${res.stderr.trim() || `exit ${res.exitCode}`}`);
|
|
829
|
-
}
|
|
830
|
-
const verdict = res.stdout.trim().split('\n').pop() ?? '';
|
|
831
|
-
if (verdict === 'BX_SKILLS_NON_GIT' || verdict === 'BX_SKILLS_OK')
|
|
832
|
-
return;
|
|
833
|
-
if (verdict.startsWith('BX_SKILLS_TRACKED')) {
|
|
834
|
-
throw new Error(`refusing to inject baxian skills into ${workdir}: project tracks ` +
|
|
835
|
-
`${verdict.replace('BX_SKILLS_TRACKED ', '')}`);
|
|
836
|
-
}
|
|
837
|
-
throw new Error(`cannot make injected skills invisible to Git in ${workdir}: ${verdict || 'no probe output'}`);
|
|
838
|
-
}
|
|
839
676
|
runtimePinEntries() {
|
|
840
677
|
return [
|
|
841
678
|
['prefix', 'C-b'],
|
|
@@ -849,6 +686,22 @@ export class AgentManager {
|
|
|
849
686
|
throw new Error(`tmux session ${ref.sessionId} (${agentId}) vanished before its options could be applied`);
|
|
850
687
|
}
|
|
851
688
|
}
|
|
689
|
+
async taskContextForSession(tmux, agentId, ensure) {
|
|
690
|
+
if (ensure.freshRuntime) {
|
|
691
|
+
await this.setSessionOptions(tmux, agentId, ensure.sessionRef, [[TASK_CONTEXT_SESSION_OPTION, '']]);
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
return tmux.getSessionOptionByRef(ensure.sessionRef, agentId, TASK_CONTEXT_SESSION_OPTION);
|
|
695
|
+
}
|
|
696
|
+
async rememberTaskContext(tmux, agentId, ref, taskId) {
|
|
697
|
+
try {
|
|
698
|
+
await this.setSessionOptions(tmux, agentId, ref, [[TASK_CONTEXT_SESSION_OPTION, taskId]]);
|
|
699
|
+
}
|
|
700
|
+
catch (err) {
|
|
701
|
+
console.warn(`[AgentManager] prompt for ${agentId}/${taskId} was delivered, but its context marker could not be saved; ` +
|
|
702
|
+
`the next phase will clear once and resend the full task:`, err);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
852
705
|
async resolveClaimedPane(tmux, agentId, expectedPaneId) {
|
|
853
706
|
const snapshot = await tmux.getSessionSnapshot(agentId);
|
|
854
707
|
if (!snapshot)
|
|
@@ -863,7 +716,7 @@ export class AgentManager {
|
|
|
863
716
|
return pane;
|
|
864
717
|
}
|
|
865
718
|
async buildFreshSession(tmux, agent, agentId, workdir, sessionDir = workdir) {
|
|
866
|
-
return this.
|
|
719
|
+
return this.buildFreshSessionLocked(tmux, agent, agentId, workdir, sessionDir);
|
|
867
720
|
}
|
|
868
721
|
async buildFreshSessionLocked(tmux, agent, agentId, workdir, sessionDir) {
|
|
869
722
|
let createdSession = false;
|
|
@@ -895,7 +748,6 @@ export class AgentManager {
|
|
|
895
748
|
timeoutMs: this.bootstrapTimeoutsMs.waitReplReady,
|
|
896
749
|
scrollback: 0,
|
|
897
750
|
});
|
|
898
|
-
await this.tagSessionSkillsVersion(tmux, agentId, createdRef);
|
|
899
751
|
return { ok: true, createdSession: true, freshRuntime: true, paneId: pane.paneId, pane, workdir, sessionRef: createdRef };
|
|
900
752
|
}
|
|
901
753
|
catch (err) {
|
|
@@ -988,7 +840,7 @@ export class AgentManager {
|
|
|
988
840
|
for (let attempt = 1; attempt <= this.greetingMaxAttempts; attempt++) {
|
|
989
841
|
const token = createSignalToken();
|
|
990
842
|
try {
|
|
991
|
-
await this.injectAndAwaitAckSteps(tmux, pane, buildGreetingPrompt(token
|
|
843
|
+
await this.injectAndAwaitAckSteps(tmux, pane, buildGreetingPrompt(token), agentId, agent.runtime);
|
|
992
844
|
}
|
|
993
845
|
catch (err) {
|
|
994
846
|
console.warn(`[bootstrap] greeting inject failed for ${agentId} (attempt ${attempt}):`, err);
|
|
@@ -1021,8 +873,8 @@ export class AgentManager {
|
|
|
1021
873
|
return;
|
|
1022
874
|
const now = new Date().toISOString();
|
|
1023
875
|
const reason = 'Greeting capability check failed: the agent did not echo a valid [bx:greeting] signal ' +
|
|
1024
|
-
'
|
|
1025
|
-
'
|
|
876
|
+
'from the inline startup instruction within the timeout. Its runtime/model may not meet baxian ' +
|
|
877
|
+
'pane signalling requirements. Fix the runtime, then restart-repl or ' +
|
|
1026
878
|
'retry the agent to re-run the check (Resume will not clear it — capability must be re-proven).';
|
|
1027
879
|
let wrote = false;
|
|
1028
880
|
await this.agentStore.update(agentId, (fresh) => {
|
|
@@ -1519,28 +1371,8 @@ export class AgentManager {
|
|
|
1519
1371
|
}
|
|
1520
1372
|
await this.setSessionOptions(tmux, agentId, sessionRef, [[WORKDIR_SESSION_OPTION, sessionDir]]);
|
|
1521
1373
|
switch (state.kind) {
|
|
1522
|
-
case 'live-runtime':
|
|
1523
|
-
let stale;
|
|
1524
|
-
try {
|
|
1525
|
-
stale = await this.replSkillsStale(tmux, agentId, sessionRef);
|
|
1526
|
-
}
|
|
1527
|
-
catch (err) {
|
|
1528
|
-
throw new EnsureSessionError({ createdSession: false, agentId }, `skills-version probe failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1529
|
-
}
|
|
1530
|
-
if (stale) {
|
|
1531
|
-
return {
|
|
1532
|
-
ok: true,
|
|
1533
|
-
createdSession: false,
|
|
1534
|
-
freshRuntime: false,
|
|
1535
|
-
skillsStale: true,
|
|
1536
|
-
paneId,
|
|
1537
|
-
pane,
|
|
1538
|
-
workdir,
|
|
1539
|
-
sessionRef,
|
|
1540
|
-
};
|
|
1541
|
-
}
|
|
1374
|
+
case 'live-runtime':
|
|
1542
1375
|
return { ok: true, createdSession: false, freshRuntime: false, paneId, pane, workdir, sessionRef };
|
|
1543
|
-
}
|
|
1544
1376
|
case 'startup-dialog':
|
|
1545
1377
|
throw new EnsureSessionError({
|
|
1546
1378
|
createdSession: false,
|
|
@@ -1561,7 +1393,6 @@ export class AgentManager {
|
|
|
1561
1393
|
timeoutMs: this.bootstrapTimeoutsMs.waitReplReady,
|
|
1562
1394
|
scrollback: 0,
|
|
1563
1395
|
});
|
|
1564
|
-
await this.tagSessionSkillsVersion(tmux, agentId, sessionRef);
|
|
1565
1396
|
return { ok: true, createdSession: false, freshRuntime: true, paneId, pane, workdir, sessionRef };
|
|
1566
1397
|
}
|
|
1567
1398
|
catch (trustErr) {
|
|
@@ -1589,7 +1420,6 @@ export class AgentManager {
|
|
|
1589
1420
|
timeoutMs: this.bootstrapTimeoutsMs.waitReplReady,
|
|
1590
1421
|
scrollback: 0,
|
|
1591
1422
|
});
|
|
1592
|
-
await this.tagSessionSkillsVersion(tmux, agentId, sessionRef);
|
|
1593
1423
|
return { ok: true, createdSession: false, freshRuntime: true, paneId, pane, workdir, sessionRef };
|
|
1594
1424
|
}
|
|
1595
1425
|
catch (relErr) {
|
|
@@ -3436,7 +3266,6 @@ export class AgentManager {
|
|
|
3436
3266
|
if (!this.deletionGateOpen(agentId, genAtEntry)) {
|
|
3437
3267
|
throw new Error(`restart-repl: agent ${agentId} is being deleted or was recreated; aborting`);
|
|
3438
3268
|
}
|
|
3439
|
-
await this.provisionRepoSkills(runner, cfg, workdir);
|
|
3440
3269
|
await this.setSessionOptions(tmux, agentId, snapshot.ref, [[WORKDIR_SESSION_OPTION, workdir]]);
|
|
3441
3270
|
const runtime = agentRuntimeKindFor(cfg);
|
|
3442
3271
|
const relaunch = async () => {
|
|
@@ -3449,9 +3278,9 @@ export class AgentManager {
|
|
|
3449
3278
|
timeoutMs: this.bootstrapTimeoutsMs.waitReplReady,
|
|
3450
3279
|
scrollback: 0,
|
|
3451
3280
|
});
|
|
3452
|
-
await this.
|
|
3281
|
+
await this.setSessionOptions(tmux, agentId, snapshot.ref, [[TASK_CONTEXT_SESSION_OPTION, '']]);
|
|
3453
3282
|
};
|
|
3454
|
-
await
|
|
3283
|
+
await relaunch();
|
|
3455
3284
|
await this.agentStore.update(agentId, (state) => {
|
|
3456
3285
|
if (!state || !this.deletionGateOpen(agentId, genAtEntry))
|
|
3457
3286
|
return AGENT_STORE_NOOP;
|
|
@@ -3959,9 +3788,8 @@ export class AgentManager {
|
|
|
3959
3788
|
phase: 'develop',
|
|
3960
3789
|
agent: cfg,
|
|
3961
3790
|
workdir: workdirGuess,
|
|
3962
|
-
skillRegistry: this.skillRegistry,
|
|
3963
3791
|
signalToken: 'preview-signal-token',
|
|
3964
|
-
|
|
3792
|
+
platform: this.platformPromptContextOf(fakeTask),
|
|
3965
3793
|
});
|
|
3966
3794
|
return Buffer.byteLength(fullPrompt, 'utf8');
|
|
3967
3795
|
}
|
|
@@ -3972,12 +3800,10 @@ export class AgentManager {
|
|
|
3972
3800
|
return this.config.project.find(p => p.id === projectId);
|
|
3973
3801
|
}
|
|
3974
3802
|
platformDriverFor(projectId) {
|
|
3975
|
-
if (!this.pluginRegistry)
|
|
3976
|
-
return undefined;
|
|
3977
3803
|
const project = this.getProjectConfig(projectId);
|
|
3978
3804
|
if (!project)
|
|
3979
3805
|
return undefined;
|
|
3980
|
-
return buildProjectDriver(project, this.
|
|
3806
|
+
return buildProjectDriver(project, this.platformDriverExec);
|
|
3981
3807
|
}
|
|
3982
3808
|
findAgentTeam(anchorAgentId) {
|
|
3983
3809
|
for (const project of this.config.project) {
|
|
@@ -4484,25 +4310,6 @@ export class AgentManager {
|
|
|
4484
4310
|
headSha: completion.approvedHeadSha,
|
|
4485
4311
|
});
|
|
4486
4312
|
}
|
|
4487
|
-
async ensurePluginSkillPools(next) {
|
|
4488
|
-
if (!this.pluginRegistry)
|
|
4489
|
-
return;
|
|
4490
|
-
for (const project of next.project) {
|
|
4491
|
-
const tool = resolveProjectTool(project);
|
|
4492
|
-
if (tool === undefined)
|
|
4493
|
-
continue;
|
|
4494
|
-
const plugin = this.pluginRegistry.resolveTool(tool);
|
|
4495
|
-
if (plugin === undefined) {
|
|
4496
|
-
throw new Error(`project '${project.id}': no git-driver plugin provides tool '${tool}' — install it under <home>/plugins/ first`);
|
|
4497
|
-
}
|
|
4498
|
-
if (this.skillRegistry.pluginSkillNames({ pluginTools: [tool] }).length > 0)
|
|
4499
|
-
continue;
|
|
4500
|
-
await this.skillRegistry.scanPluginSkills(tool, join(plugin.pluginPath, 'skills'));
|
|
4501
|
-
if (this.skillRegistry.pluginSkillNames({ pluginTools: [tool] }).length === 0) {
|
|
4502
|
-
throw new Error(`plugin '${plugin.manifest.name}' provides no materializable skills for tool '${tool}'`);
|
|
4503
|
-
}
|
|
4504
|
-
}
|
|
4505
|
-
}
|
|
4506
4313
|
async guardGitConfigCommit(current, next, scan, commit) {
|
|
4507
4314
|
return this.withTaskLock(async () => {
|
|
4508
4315
|
const blockers = await scan(this, current, next);
|
|
@@ -5227,51 +5034,16 @@ export class AgentManager {
|
|
|
5227
5034
|
const project = this.getProjectConfig(projectId);
|
|
5228
5035
|
if (!project)
|
|
5229
5036
|
throw new Error(`project ${projectId} not found while capturing platform binding`);
|
|
5230
|
-
const tool = resolveProjectTool(project);
|
|
5231
|
-
if (tool === undefined)
|
|
5232
|
-
throw new Error(`project ${projectId} has no resolved platform tool`);
|
|
5233
5037
|
return {
|
|
5234
|
-
mode: 'git',
|
|
5235
5038
|
repoKey: repoIdentityKey(project.repo),
|
|
5236
|
-
tool,
|
|
5237
5039
|
};
|
|
5238
5040
|
}
|
|
5239
|
-
|
|
5240
|
-
const project = this.getProjectConfig(projectId);
|
|
5241
|
-
if (!project || !this.pluginRegistry)
|
|
5242
|
-
return undefined;
|
|
5243
|
-
const tool = resolveProjectTool(project);
|
|
5244
|
-
if (tool === undefined)
|
|
5245
|
-
return undefined;
|
|
5246
|
-
const plugin = this.pluginRegistry.resolveTool(tool);
|
|
5247
|
-
if (plugin === undefined)
|
|
5248
|
-
return undefined;
|
|
5249
|
-
const renderCtx = buildDriverRunContext(project.repo, tool);
|
|
5250
|
-
return {
|
|
5251
|
-
tool,
|
|
5252
|
-
minToolVersion: plugin.manifest.minToolVersion,
|
|
5253
|
-
steps: plugin.spec.preflight,
|
|
5254
|
-
agentCommands: plugin.spec.agentCommands,
|
|
5255
|
-
renderCtx,
|
|
5256
|
-
driverFor: (exec) => new GitDriver(plugin, renderCtx, exec),
|
|
5257
|
-
};
|
|
5258
|
-
}
|
|
5259
|
-
platformCliContextOf(task) {
|
|
5041
|
+
platformPromptContextOf(task) {
|
|
5260
5042
|
this.assertPlatformBinding(task);
|
|
5261
5043
|
const project = this.getProjectConfig(task.projectId);
|
|
5262
5044
|
if (!project)
|
|
5263
5045
|
return undefined;
|
|
5264
|
-
|
|
5265
|
-
if (tool === undefined)
|
|
5266
|
-
return undefined;
|
|
5267
|
-
const ctx = buildDriverRunContext(project.repo, tool);
|
|
5268
|
-
return {
|
|
5269
|
-
tool,
|
|
5270
|
-
host: ctx.host,
|
|
5271
|
-
repo: ctx.repoPath,
|
|
5272
|
-
repoEncoded: encodeURIComponent(ctx.repoPath),
|
|
5273
|
-
...(project.gitCli?.notes !== undefined ? { notes: project.gitCli.notes } : {}),
|
|
5274
|
-
};
|
|
5046
|
+
return buildProjectPromptContext(project);
|
|
5275
5047
|
}
|
|
5276
5048
|
async ensureGitBaseSnapshot(task, phase) {
|
|
5277
5049
|
if (phase !== 'develop' && phase !== 'code')
|
|
@@ -5340,8 +5112,7 @@ export class AgentManager {
|
|
|
5340
5112
|
const { task, driver } = await this.platformTaskAndDriver(taskId);
|
|
5341
5113
|
if (task.prNumber === undefined)
|
|
5342
5114
|
throw new Error(`task ${taskId} has no bound PR`);
|
|
5343
|
-
|
|
5344
|
-
return row;
|
|
5115
|
+
return driver.prView(task.prNumber);
|
|
5345
5116
|
}
|
|
5346
5117
|
async platformFetchPrHeadSha(taskId) {
|
|
5347
5118
|
const headSha = String((await this.platformFetchPrView(taskId)).headSha);
|
|
@@ -5352,11 +5123,11 @@ export class AgentManager {
|
|
|
5352
5123
|
}
|
|
5353
5124
|
async platformVerifyPrBinding(taskId, prNumber, opts = {}) {
|
|
5354
5125
|
const { task, driver } = await this.platformTaskAndDriver(taskId);
|
|
5355
|
-
const
|
|
5126
|
+
const row = await driver.prView(prNumber);
|
|
5356
5127
|
let expectedBase = task.baseBranch;
|
|
5357
5128
|
if (expectedBase === undefined) {
|
|
5358
|
-
const
|
|
5359
|
-
if (typeof project
|
|
5129
|
+
const project = await driver.projectView();
|
|
5130
|
+
if (typeof project.defaultBranch === 'string')
|
|
5360
5131
|
expectedBase = project.defaultBranch;
|
|
5361
5132
|
}
|
|
5362
5133
|
const check = checkOpenPrBinding(row, { branch: opts.branchOverride ?? task.branch, expectedBase });
|
|
@@ -5473,8 +5244,8 @@ export class AgentManager {
|
|
|
5473
5244
|
let pr;
|
|
5474
5245
|
let project;
|
|
5475
5246
|
try {
|
|
5476
|
-
|
|
5477
|
-
|
|
5247
|
+
pr = await driver.prView(intent.prNumber);
|
|
5248
|
+
project = await driver.projectView();
|
|
5478
5249
|
}
|
|
5479
5250
|
catch (err) {
|
|
5480
5251
|
if (err instanceof DriverOpError
|
|
@@ -5520,7 +5291,7 @@ export class AgentManager {
|
|
|
5520
5291
|
task = credentialed;
|
|
5521
5292
|
intent = credentialed.remoteCleanup;
|
|
5522
5293
|
try {
|
|
5523
|
-
await driver.
|
|
5294
|
+
await driver.closePr(intent.prNumber);
|
|
5524
5295
|
}
|
|
5525
5296
|
catch (err) {
|
|
5526
5297
|
if (err instanceof DriverOpError
|
|
@@ -5582,21 +5353,14 @@ export class AgentManager {
|
|
|
5582
5353
|
return;
|
|
5583
5354
|
let mutationError;
|
|
5584
5355
|
try {
|
|
5585
|
-
await driver.
|
|
5586
|
-
branch: intent.branch,
|
|
5587
|
-
expectedHeadSha: intent.expectedHeadSha,
|
|
5588
|
-
remoteProjectId: intent.remoteProjectId,
|
|
5589
|
-
});
|
|
5356
|
+
await driver.deleteBranch(intent.remoteProjectId, intent.branch, intent.expectedHeadSha);
|
|
5590
5357
|
}
|
|
5591
5358
|
catch (err) {
|
|
5592
5359
|
mutationError = err;
|
|
5593
5360
|
}
|
|
5594
5361
|
let branch;
|
|
5595
5362
|
try {
|
|
5596
|
-
|
|
5597
|
-
branch: intent.branch,
|
|
5598
|
-
remoteProjectId: intent.remoteProjectId,
|
|
5599
|
-
});
|
|
5363
|
+
branch = await driver.branchView(intent.remoteProjectId, intent.branch);
|
|
5600
5364
|
}
|
|
5601
5365
|
catch (err) {
|
|
5602
5366
|
const mutation = mutationError === undefined ? '' : `; delete attempt: ${safeDriverErrorText(mutationError)}`;
|
|
@@ -5700,7 +5464,7 @@ export class AgentManager {
|
|
|
5700
5464
|
if (task.prNumber === undefined)
|
|
5701
5465
|
throw new Error(`task ${taskId} has no bound PR`);
|
|
5702
5466
|
const prNumber = task.prNumber;
|
|
5703
|
-
const
|
|
5467
|
+
const row = await driver.prView(prNumber);
|
|
5704
5468
|
const binding = checkOpenPrBinding(row, { branch: task.branch, expectedBase: task.baseBranch });
|
|
5705
5469
|
if (!binding.ok)
|
|
5706
5470
|
throw new Error(`merge blocked: binding ${binding.reason}`);
|
|
@@ -5747,13 +5511,13 @@ export class AgentManager {
|
|
|
5747
5511
|
throw new Error(`merge blocked: task left its merge gate (status=${fresh?.status ?? 'gone'})`);
|
|
5748
5512
|
}
|
|
5749
5513
|
try {
|
|
5750
|
-
await driver.
|
|
5514
|
+
await driver.mergePr(prNumber, opts.expectedHeadSha);
|
|
5751
5515
|
}
|
|
5752
5516
|
catch (err) {
|
|
5753
5517
|
if (err instanceof DriverOpError && err.info.errorClass === 'MERGE_BLOCKED') {
|
|
5754
5518
|
let mergeStatus;
|
|
5755
5519
|
try {
|
|
5756
|
-
const
|
|
5520
|
+
const fresh = await driver.prView(prNumber);
|
|
5757
5521
|
if (typeof fresh?.detailedMergeStatus === 'string')
|
|
5758
5522
|
mergeStatus = fresh.detailedMergeStatus;
|
|
5759
5523
|
}
|
|
@@ -5774,9 +5538,7 @@ export class AgentManager {
|
|
|
5774
5538
|
taskBelongsToPlatformEntry(task, entryBinding) {
|
|
5775
5539
|
const binding = task.platformBinding;
|
|
5776
5540
|
return binding !== undefined
|
|
5777
|
-
&& binding.
|
|
5778
|
-
&& binding.repoKey === entryBinding.repoKey
|
|
5779
|
-
&& binding.tool === entryBinding.tool;
|
|
5541
|
+
&& binding.repoKey === entryBinding.repoKey;
|
|
5780
5542
|
}
|
|
5781
5543
|
async listTasksForPlatformEntry(projectId) {
|
|
5782
5544
|
const project = this.getProjectConfig(projectId);
|
|
@@ -5784,9 +5546,7 @@ export class AgentManager {
|
|
|
5784
5546
|
return [];
|
|
5785
5547
|
const key = repoIdentityKey(project.repo);
|
|
5786
5548
|
const entryBinding = {
|
|
5787
|
-
mode: 'git',
|
|
5788
5549
|
repoKey: key,
|
|
5789
|
-
tool: resolveProjectTool(project) ?? '',
|
|
5790
5550
|
};
|
|
5791
5551
|
const ids = new Set(this.config.project.filter(p => repoIdentityKey(p.repo) === key).map(p => p.id));
|
|
5792
5552
|
const tasks = await this.taskStore.listStrict();
|
|
@@ -5973,8 +5733,7 @@ export class AgentManager {
|
|
|
5973
5733
|
if (this.repoStoreFactory) {
|
|
5974
5734
|
return this.repoStoreFactory(runner, project.repo, agent.mode, host, this.repoCache, agent.id, agent.workdir);
|
|
5975
5735
|
}
|
|
5976
|
-
|
|
5977
|
-
return new RepoStore(runner, project.repo, agent.mode, host, this.repoCache, agent.id, agent.workdir, cloneViaGh);
|
|
5736
|
+
return new RepoStore(runner, project.repo, agent.mode, host, this.repoCache, agent.id, agent.workdir);
|
|
5978
5737
|
}
|
|
5979
5738
|
async ensureWorkdir(agent, project, runner) {
|
|
5980
5739
|
const repoStore = this.createRepoStore(agent, project, runner);
|
|
@@ -6444,6 +6203,9 @@ export class AgentManager {
|
|
|
6444
6203
|
await tmux.clearComposerDraft(pane);
|
|
6445
6204
|
await waitReady();
|
|
6446
6205
|
await assertSessionUnchanged();
|
|
6206
|
+
if (command === '/clear') {
|
|
6207
|
+
await this.setSessionOptions(tmux, agentId, pane.session, [[TASK_CONTEXT_SESSION_OPTION, '']]);
|
|
6208
|
+
}
|
|
6447
6209
|
await tmux.sendKeysLiteral(pane, command);
|
|
6448
6210
|
await tmux.sendEnter(pane);
|
|
6449
6211
|
guardHandedOff = true;
|
|
@@ -6544,7 +6306,7 @@ export class AgentManager {
|
|
|
6544
6306
|
`${phase} queued for redispatch when idle (${(err.message.split('\n')[0])})`);
|
|
6545
6307
|
return new EnsureSessionError({ createdSession, agentId, handled: true, busyPending: true }, `QA REPL busy; ${phase} dispatch for task ${taskId} queued for redispatch when idle`);
|
|
6546
6308
|
}
|
|
6547
|
-
async clearRuntimeForDispatchBoundary(tmux, pane, agentId, runtime, revalidate) {
|
|
6309
|
+
async clearRuntimeForDispatchBoundary(tmux, pane, agentId, runtime, sessionRef, revalidate) {
|
|
6548
6310
|
await this.acquireCompactGuard(agentId);
|
|
6549
6311
|
try {
|
|
6550
6312
|
await this.waitForReplPromptReady(tmux, pane, runtime, this.cleanComposerWaitMs, { stableIdle: true });
|
|
@@ -6554,6 +6316,7 @@ export class AgentManager {
|
|
|
6554
6316
|
timeoutMs: this.dispatchSettleTimeoutMs,
|
|
6555
6317
|
});
|
|
6556
6318
|
const baselineTitle = await tmux.readPaneTitle(pane);
|
|
6319
|
+
await this.setSessionOptions(tmux, agentId, sessionRef, [[TASK_CONTEXT_SESSION_OPTION, '']]);
|
|
6557
6320
|
await tmux.sendKeysLiteral(pane, '/clear');
|
|
6558
6321
|
await tmux.sendEnter(pane);
|
|
6559
6322
|
await tmux.waitSubmitAck(pane, baseline, runtime, {
|
|
@@ -6952,6 +6715,7 @@ export class AgentManager {
|
|
|
6952
6715
|
if (!lockState || lockState.taskId !== taskId || lockState.lockToken !== lockToken)
|
|
6953
6716
|
return false;
|
|
6954
6717
|
await assertOwner();
|
|
6718
|
+
const reuseTaskContext = (await this.taskContextForSession(tmux, agentId, ensure)) === taskId;
|
|
6955
6719
|
try {
|
|
6956
6720
|
await this.waitForReplPromptReady(tmux, pane, agent.runtime, this.cleanComposerWaitMs, { stableIdle: true });
|
|
6957
6721
|
}
|
|
@@ -6978,10 +6742,8 @@ export class AgentManager {
|
|
|
6978
6742
|
await this.clearBranchLocalCleaned(taskId);
|
|
6979
6743
|
}
|
|
6980
6744
|
await assertOwner();
|
|
6981
|
-
if (!ensure.freshRuntime) {
|
|
6982
|
-
await this.clearRuntimeForDispatchBoundary(tmux, pane, agentId, agent.runtime, assertOwner);
|
|
6983
|
-
if (ensure.skillsStale)
|
|
6984
|
-
await this.tagSessionSkillsVersion(tmux, agentId, ensure.sessionRef);
|
|
6745
|
+
if (!ensure.freshRuntime && !reuseTaskContext) {
|
|
6746
|
+
await this.clearRuntimeForDispatchBoundary(tmux, pane, agentId, agent.runtime, ensure.sessionRef, assertOwner);
|
|
6985
6747
|
}
|
|
6986
6748
|
}
|
|
6987
6749
|
catch (err) {
|
|
@@ -7039,14 +6801,14 @@ export class AgentManager {
|
|
|
7039
6801
|
try {
|
|
7040
6802
|
const imagePaths = await this.imagePathsForDispatch(runner, task, phase);
|
|
7041
6803
|
const taskForPrompt = await this.ensureGitBaseSnapshot(task, phase);
|
|
7042
|
-
const
|
|
6804
|
+
const platform = this.platformPromptContextOf(taskForPrompt);
|
|
7043
6805
|
prompt = buildPromptInline({
|
|
7044
6806
|
task: taskForPrompt,
|
|
7045
6807
|
phase,
|
|
7046
6808
|
agent,
|
|
7047
6809
|
workdir: dispatchWorkdir,
|
|
7048
|
-
|
|
7049
|
-
...(
|
|
6810
|
+
includeTaskContext: ensure.freshRuntime || !reuseTaskContext,
|
|
6811
|
+
...(platform ? { platform } : {}),
|
|
7050
6812
|
...(promptSignalToken ? { signalToken: promptSignalToken } : {}),
|
|
7051
6813
|
...(imagePaths.length ? { imagePaths } : {}),
|
|
7052
6814
|
});
|
|
@@ -7056,9 +6818,6 @@ export class AgentManager {
|
|
|
7056
6818
|
if (err instanceof PromptSizeError) {
|
|
7057
6819
|
throw new DispatchTerminalError('prompt_too_large', err.message);
|
|
7058
6820
|
}
|
|
7059
|
-
if (err instanceof RequiredSkillsMissingError) {
|
|
7060
|
-
throw new DispatchTerminalError('required_skills_missing', err.message);
|
|
7061
|
-
}
|
|
7062
6821
|
throw err;
|
|
7063
6822
|
}
|
|
7064
6823
|
const taskFresh = await this.taskStore.get(taskId);
|
|
@@ -7159,6 +6918,9 @@ export class AgentManager {
|
|
|
7159
6918
|
console.warn(`[AgentManager] startSession[${phase}]: injection fenced off (pass superseded) for task ${taskId}`);
|
|
7160
6919
|
return false;
|
|
7161
6920
|
}
|
|
6921
|
+
if (!reuseTaskContext) {
|
|
6922
|
+
await this.rememberTaskContext(tmux, agentId, ensure.sessionRef, taskId);
|
|
6923
|
+
}
|
|
7162
6924
|
if (opts.dispatchPassToken !== undefined) {
|
|
7163
6925
|
this.clearPendingDispatchRetryIfMatches(taskId, { agentId, signalToken: opts.dispatchPassToken });
|
|
7164
6926
|
}
|
|
@@ -7522,6 +7284,7 @@ export class AgentManager {
|
|
|
7522
7284
|
return false;
|
|
7523
7285
|
}
|
|
7524
7286
|
await this.assertTaskLockOwner(agentId, taskId, lockToken);
|
|
7287
|
+
const reuseTaskContext = (await this.taskContextForSession(tmux, agentId, ensure)) === taskId;
|
|
7525
7288
|
const verifiedWorkdir = stateAfterEnsure.workdir;
|
|
7526
7289
|
if (!verifiedWorkdir
|
|
7527
7290
|
|| verifiedWorkdir !== ensure.workdir
|
|
@@ -7565,14 +7328,14 @@ export class AgentManager {
|
|
|
7565
7328
|
try {
|
|
7566
7329
|
const imagePaths = await this.imagePathsForDispatch(runner, task, phase);
|
|
7567
7330
|
const taskForPrompt = await this.ensureGitBaseSnapshot(task, phase);
|
|
7568
|
-
const
|
|
7331
|
+
const platform = this.platformPromptContextOf(taskForPrompt);
|
|
7569
7332
|
prompt = buildPromptInline({
|
|
7570
7333
|
task: taskForPrompt,
|
|
7571
7334
|
phase,
|
|
7572
7335
|
agent,
|
|
7573
7336
|
workdir: verifiedWorkdir,
|
|
7574
|
-
|
|
7575
|
-
...(
|
|
7337
|
+
includeTaskContext: ensure.freshRuntime || !reuseTaskContext,
|
|
7338
|
+
...(platform ? { platform } : {}),
|
|
7576
7339
|
...(signalToken ? { signalToken } : {}),
|
|
7577
7340
|
...(imagePaths.length ? { imagePaths } : {}),
|
|
7578
7341
|
});
|
|
@@ -7581,9 +7344,6 @@ export class AgentManager {
|
|
|
7581
7344
|
if (err instanceof PromptSizeError) {
|
|
7582
7345
|
throw new DispatchTerminalError('prompt_too_large', err.message);
|
|
7583
7346
|
}
|
|
7584
|
-
if (err instanceof RequiredSkillsMissingError) {
|
|
7585
|
-
throw new DispatchTerminalError('required_skills_missing', err.message);
|
|
7586
|
-
}
|
|
7587
7347
|
throw err;
|
|
7588
7348
|
}
|
|
7589
7349
|
const taskFresh = await this.taskStore.get(taskId);
|
|
@@ -7641,8 +7401,8 @@ export class AgentManager {
|
|
|
7641
7401
|
return false;
|
|
7642
7402
|
}
|
|
7643
7403
|
const guardBeforeInject = opts.guardBeforeInject ?? (phase === 'post-approve' ? postApprovePassStillLive : undefined);
|
|
7644
|
-
if (!ensure.freshRuntime &&
|
|
7645
|
-
const
|
|
7404
|
+
if (!ensure.freshRuntime && !reuseTaskContext) {
|
|
7405
|
+
const revalidateContextReset = async () => {
|
|
7646
7406
|
const [currentTask, currentAgent] = await Promise.all([
|
|
7647
7407
|
this.taskStore.get(taskId),
|
|
7648
7408
|
this.agentStore.get(agentId),
|
|
@@ -7657,15 +7417,14 @@ export class AgentManager {
|
|
|
7657
7417
|
|| !bindingStillDispatchable
|
|
7658
7418
|
|| currentAgent?.lockToken !== lockToken
|
|
7659
7419
|
|| currentAgent.workdir !== verifiedWorkdir) {
|
|
7660
|
-
throw new Error(`Task or agent ownership changed while
|
|
7420
|
+
throw new Error(`Task or agent ownership changed while resetting context for ${phase} dispatch`);
|
|
7661
7421
|
}
|
|
7662
7422
|
await this.assertTaskLockOwner(agentId, taskId, lockToken);
|
|
7663
7423
|
if (guardBeforeInject && !(await guardBeforeInject())) {
|
|
7664
|
-
throw new Error(`Task generation changed while
|
|
7424
|
+
throw new Error(`Task generation changed while resetting context for ${phase} dispatch`);
|
|
7665
7425
|
}
|
|
7666
7426
|
};
|
|
7667
|
-
await this.clearRuntimeForDispatchBoundary(tmux, pane, agentId, agent.runtime,
|
|
7668
|
-
await this.tagSessionSkillsVersion(tmux, agentId, ensure.sessionRef);
|
|
7427
|
+
await this.clearRuntimeForDispatchBoundary(tmux, pane, agentId, agent.runtime, ensure.sessionRef, revalidateContextReset);
|
|
7669
7428
|
}
|
|
7670
7429
|
if (opts.armBeforeInject && !(await opts.armBeforeInject())) {
|
|
7671
7430
|
return false;
|
|
@@ -7708,6 +7467,9 @@ export class AgentManager {
|
|
|
7708
7467
|
console.warn(`[AgentManager] continueSession[${phase}]: pre-paste guard rejected the inject for task ${taskId}; skipping`);
|
|
7709
7468
|
return false;
|
|
7710
7469
|
}
|
|
7470
|
+
if (!reuseTaskContext) {
|
|
7471
|
+
await this.rememberTaskContext(tmux, agentId, ensure.sessionRef, taskId);
|
|
7472
|
+
}
|
|
7711
7473
|
return true;
|
|
7712
7474
|
}
|
|
7713
7475
|
async rollbackUndeliveredBootstrap(state) {
|
|
@@ -8256,14 +8018,11 @@ export class AgentManager {
|
|
|
8256
8018
|
previewBytes = this.previewPromptBytesForTaskInput(projectId, input);
|
|
8257
8019
|
}
|
|
8258
8020
|
catch (err) {
|
|
8259
|
-
if (err instanceof RequiredSkillsMissingError) {
|
|
8260
|
-
throw new ApiError(500, err.message);
|
|
8261
|
-
}
|
|
8262
8021
|
throw new ApiError(400, err instanceof Error ? err.message : String(err));
|
|
8263
8022
|
}
|
|
8264
8023
|
if (previewBytes > MAX_PROMPT_BYTES_ROUTE_LIMIT) {
|
|
8265
8024
|
throw new ApiError(400, `estimated prompt size ${previewBytes} bytes exceeds ${MAX_PROMPT_BYTES_ROUTE_LIMIT} limit; ` +
|
|
8266
|
-
`reduce task description or
|
|
8025
|
+
`reduce the task description or platform workflow instructions`);
|
|
8267
8026
|
}
|
|
8268
8027
|
}
|
|
8269
8028
|
gitReviewClaimMatches(task, expected) {
|
|
@@ -9669,7 +9428,7 @@ export class AgentManager {
|
|
|
9669
9428
|
let opposite = false;
|
|
9670
9429
|
for (const scan of scans) {
|
|
9671
9430
|
for (const row of scan.rows) {
|
|
9672
|
-
if (
|
|
9431
|
+
if (versionTimeOf(row) === undefined)
|
|
9673
9432
|
continue;
|
|
9674
9433
|
for (const marker of rowTokens(row)) {
|
|
9675
9434
|
if (dead.has(marker.token))
|
|
@@ -9694,9 +9453,7 @@ export class AgentManager {
|
|
|
9694
9453
|
return 'unknown';
|
|
9695
9454
|
if (deadTokens(scans).has(entry.data.token))
|
|
9696
9455
|
return 'dead';
|
|
9697
|
-
if (scans.some(scan => scan.rows.some(row => row
|
|
9698
|
-
&& versionTimeOf(row) !== undefined
|
|
9699
|
-
&& row.bodyDigest === expectedDigest)))
|
|
9456
|
+
if (scans.some(scan => scan.rows.some(row => versionTimeOf(row) !== undefined && row.bodyDigest === expectedDigest)))
|
|
9700
9457
|
return 'present';
|
|
9701
9458
|
return 'absent';
|
|
9702
9459
|
}
|
|
@@ -9753,10 +9510,7 @@ export class AgentManager {
|
|
|
9753
9510
|
entry = claimedEntry;
|
|
9754
9511
|
let writeFailure;
|
|
9755
9512
|
try {
|
|
9756
|
-
await driver.
|
|
9757
|
-
prNumber: entry.data.prNumber,
|
|
9758
|
-
body: this.codeVerdictCommentBody(entry),
|
|
9759
|
-
});
|
|
9513
|
+
await driver.postComment(entry.data.prNumber, this.codeVerdictCommentBody(entry));
|
|
9760
9514
|
}
|
|
9761
9515
|
catch (error) {
|
|
9762
9516
|
writeFailure = { error };
|
|
@@ -9956,10 +9710,7 @@ export class AgentManager {
|
|
|
9956
9710
|
entry = claimedEntry;
|
|
9957
9711
|
let writeFailure;
|
|
9958
9712
|
try {
|
|
9959
|
-
await driver.
|
|
9960
|
-
prNumber: entry.data.prNumber,
|
|
9961
|
-
body: this.specVerdictCommentBody(entry),
|
|
9962
|
-
});
|
|
9713
|
+
await driver.postComment(entry.data.prNumber, this.specVerdictCommentBody(entry));
|
|
9963
9714
|
}
|
|
9964
9715
|
catch (error) {
|
|
9965
9716
|
writeFailure = { error };
|