@fitlab-ai/agent-infra 0.9.7 → 0.9.9
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/bin/internal-cli.js +11 -1
- package/dist/lib/agent-clients/adapters/claude-code.js +3 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +60 -22
- package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +8 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +175 -11
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +147 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +14 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +86 -69
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +2 -1
- package/dist/lib/decide.js +68 -14
- package/dist/lib/git/workflow.js +10 -1
- package/dist/lib/init.js +1 -1
- package/dist/lib/internal/codex-lifecycle.js +19 -5
- package/dist/lib/internal/codex-sandbox-controller.js +6 -13
- package/dist/lib/internal/git-workflow.js +5 -7
- package/dist/lib/internal/platform-pr.js +20 -7
- package/dist/lib/internal/release-workflow.js +3 -4
- package/dist/lib/internal/sandbox-control.js +79 -2
- package/dist/lib/internal/task-activity.js +57 -7
- package/dist/lib/internal/task-event.js +57 -4
- package/dist/lib/internal/task-finalization.js +54 -0
- package/dist/lib/internal/task-ledger.js +59 -5
- package/dist/lib/internal/task-lifecycle.js +59 -8
- package/dist/lib/internal/task-orchestration.js +161 -288
- package/dist/lib/internal/task-override.js +131 -0
- package/dist/lib/internal/task-review.js +64 -6
- package/dist/lib/internal/task-verify.js +65 -3
- package/dist/lib/internal/task-warning.js +57 -4
- package/dist/lib/platform/pr-summary.js +53 -13
- package/dist/lib/platform/pull-requests.js +205 -41
- package/dist/lib/runtime/agent-runtime.js +56 -0
- package/dist/lib/sandbox/commands/create.js +389 -311
- package/dist/lib/sandbox/commands/rm.js +316 -55
- package/dist/lib/sandbox/control/client.js +190 -6
- package/dist/lib/sandbox/control/container-identity.js +1 -1
- package/dist/lib/sandbox/control/controller-registration.js +273 -0
- package/dist/lib/sandbox/control/executor.js +145 -5
- package/dist/lib/sandbox/control/lifecycle.js +395 -73
- package/dist/lib/sandbox/control/protocol.js +67 -6
- package/dist/lib/sandbox/control/state.js +27 -11
- package/dist/lib/sandbox/engine.js +14 -7
- package/dist/lib/sandbox/index.js +5 -3
- package/dist/lib/sandbox/managed-fs.js +5 -0
- package/dist/lib/sandbox/mounts.js +20 -5
- package/dist/lib/sandbox/recovery.js +78 -10
- package/dist/lib/sandbox/task-resolver.js +50 -6
- package/dist/lib/sandbox/workspace-identity.js +27 -0
- package/dist/lib/sandbox/workspace-view.js +62 -40
- package/dist/lib/sandbox/worktree-safety.js +215 -2
- package/dist/lib/server/process-state.js +51 -1
- package/dist/lib/task/activity-intent.js +4 -1
- package/dist/lib/task/codex-orchestration.js +158 -33
- package/dist/lib/task/commands/status.js +59 -1
- package/dist/lib/task/commit-operation.js +335 -0
- package/dist/lib/task/commit-policy.js +18 -0
- package/dist/lib/task/decision-intents.js +4 -2
- package/dist/lib/task/delegation-receipts.js +279 -17
- package/dist/lib/task/events.js +9 -5
- package/dist/lib/task/finalization.js +567 -0
- package/dist/lib/task/guard-override.js +31 -0
- package/dist/lib/task/human-override.js +1240 -0
- package/dist/lib/task/ledger-intents.js +6 -3
- package/dist/lib/task/lifecycle.js +69 -14
- package/dist/lib/task/operation-outcome.js +33 -0
- package/dist/lib/task/orchestration.js +199 -719
- package/dist/lib/task/review-finalization.js +3 -2
- package/dist/lib/task/short-id.js +59 -1
- package/dist/lib/task/task-execution-lock.js +6 -1
- package/dist/lib/task/verification-engine.js +138 -59
- package/dist/lib/task/verification.js +15 -2
- package/dist/lib/task/workflow-warning-intents.js +54 -3
- package/dist/lib/task/write.js +2 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/claude-code.ts +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +63 -20
- package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +12 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +278 -13
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +180 -0
- package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +19 -0
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +9 -1
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +112 -93
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +2 -1
- package/lib/decide.ts +59 -14
- package/lib/git/workflow.ts +11 -2
- package/lib/init.ts +1 -1
- package/lib/internal/codex-lifecycle.ts +18 -5
- package/lib/internal/codex-sandbox-controller.ts +6 -12
- package/lib/internal/git-workflow.ts +5 -6
- package/lib/internal/platform-pr.ts +14 -7
- package/lib/internal/release-workflow.ts +3 -4
- package/lib/internal/sandbox-control.ts +92 -2
- package/lib/internal/task-activity.ts +46 -6
- package/lib/internal/task-event.ts +53 -4
- package/lib/internal/task-finalization.ts +68 -0
- package/lib/internal/task-ledger.ts +48 -4
- package/lib/internal/task-lifecycle.ts +56 -12
- package/lib/internal/task-orchestration.ts +32 -107
- package/lib/internal/task-override.ts +138 -0
- package/lib/internal/task-review.ts +51 -6
- package/lib/internal/task-verify.ts +52 -3
- package/lib/internal/task-warning.ts +44 -3
- package/lib/platform/pr-summary.ts +65 -14
- package/lib/platform/pull-requests.ts +211 -41
- package/lib/runtime/agent-runtime.ts +76 -0
- package/lib/sandbox/commands/create.ts +88 -4
- package/lib/sandbox/commands/rm.ts +413 -61
- package/lib/sandbox/config.ts +2 -1
- package/lib/sandbox/control/client.ts +255 -6
- package/lib/sandbox/control/container-identity.ts +3 -3
- package/lib/sandbox/control/controller-registration.ts +347 -0
- package/lib/sandbox/control/executor.ts +172 -6
- package/lib/sandbox/control/lifecycle.ts +465 -77
- package/lib/sandbox/control/protocol.ts +85 -9
- package/lib/sandbox/control/state.ts +35 -18
- package/lib/sandbox/engine.ts +20 -9
- package/lib/sandbox/index.ts +5 -3
- package/lib/sandbox/managed-fs.ts +5 -0
- package/lib/sandbox/mounts.ts +23 -5
- package/lib/sandbox/recovery.ts +90 -9
- package/lib/sandbox/task-resolver.ts +58 -7
- package/lib/sandbox/workspace-identity.ts +50 -2
- package/lib/sandbox/workspace-view.ts +73 -43
- package/lib/sandbox/worktree-safety.ts +236 -2
- package/lib/server/process-state.ts +48 -1
- package/lib/task/activity-intent.ts +4 -2
- package/lib/task/codex-orchestration.ts +167 -33
- package/lib/task/commands/status.ts +78 -1
- package/lib/task/commit-operation.ts +430 -0
- package/lib/task/commit-policy.ts +30 -0
- package/lib/task/decision-intents.ts +4 -2
- package/lib/task/delegation-receipts.ts +304 -19
- package/lib/task/events.ts +10 -5
- package/lib/task/finalization.ts +719 -0
- package/lib/task/guard-override.ts +50 -0
- package/lib/task/human-override.ts +1503 -0
- package/lib/task/ledger-intents.ts +6 -3
- package/lib/task/lifecycle.ts +71 -14
- package/lib/task/operation-outcome.ts +48 -0
- package/lib/task/orchestration.ts +239 -922
- package/lib/task/review-finalization.ts +6 -2
- package/lib/task/short-id.ts +65 -1
- package/lib/task/task-execution-lock.ts +10 -1
- package/lib/task/verification-engine.ts +145 -64
- package/lib/task/verification.ts +18 -3
- package/lib/task/workflow-warning-intents.ts +61 -4
- package/lib/task/write.ts +4 -1
- package/package.json +2 -2
- package/templates/.agents/QUICKSTART.en.md +2 -2
- package/templates/.agents/QUICKSTART.zh-CN.md +2 -2
- package/templates/.agents/hooks/lifecycle-delegation.js +9 -24
- package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -3
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -3
- package/templates/.agents/rules/task-management.en.md +2 -2
- package/templates/.agents/rules/task-management.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -4
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -4
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -2
- package/templates/.agents/skills/commit/SKILL.en.md +41 -125
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +52 -123
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +14 -43
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +14 -49
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -35
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -35
- package/templates/.agents/skills/complete-task/SKILL.en.md +31 -38
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +31 -38
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +6 -4
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +2 -2
- package/templates/.agents/skills/run-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +3 -1
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +3 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +1 -1
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +1 -1
- package/templates/.codex/hooks.json +4 -4
- package/dist/lib/task/commit-finalization.js +0 -305
- package/dist/lib/task/commit-intent.js +0 -165
- package/lib/task/commit-finalization.ts +0 -419
- package/lib/task/commit-intent.ts +0 -230
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { commitExplicitPaths, inspectGitWorkflow, pushGitRefs } from '../git/workflow.js';
|
|
4
|
+
import { parseTaskFrontmatter } from './frontmatter.js';
|
|
5
|
+
import { resolveTaskContext, resolveTaskRef } from './resolve-ref.js';
|
|
6
|
+
import { commitPushDecision } from './commit-policy.js';
|
|
7
|
+
import { mergeOperationWarnings } from './operation-outcome.js';
|
|
8
|
+
import { TaskExecutionLockError, withRepositoryMutationLock, withTaskExecutionLock } from './task-execution-lock.js';
|
|
9
|
+
import { appendActivityEntry, locateActivityLog } from './activity-log.js';
|
|
10
|
+
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
11
|
+
import { commitOrchestrationStageCompletion, planOrchestrationStageCompletion } from './orchestration.js';
|
|
12
|
+
const HEAD_REF = /^refs\/heads\/(.+)$/;
|
|
13
|
+
function isCommitExecutionMode(value) {
|
|
14
|
+
return value === 'direct' || value === 'orchestrated';
|
|
15
|
+
}
|
|
16
|
+
function gitText(repoRoot, args) {
|
|
17
|
+
return execFileSync('git', [...args], {
|
|
18
|
+
cwd: repoRoot,
|
|
19
|
+
encoding: 'utf8',
|
|
20
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
21
|
+
}).trim();
|
|
22
|
+
}
|
|
23
|
+
function canonicalRepositoryRoot(cwd) {
|
|
24
|
+
return gitText(cwd, ['rev-parse', '--show-toplevel']);
|
|
25
|
+
}
|
|
26
|
+
function failure(input, mode, taskId, code, message, status = 'failed', snapshot = null) {
|
|
27
|
+
return {
|
|
28
|
+
status,
|
|
29
|
+
changed: false,
|
|
30
|
+
result: status,
|
|
31
|
+
taskId,
|
|
32
|
+
mode,
|
|
33
|
+
warnings: [],
|
|
34
|
+
snapshot: snapshot ?? inspectGitWorkflow(input.cwd).snapshot,
|
|
35
|
+
operations: [],
|
|
36
|
+
error: { code, message }
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function warning(code, message, retryable, step, target, severity) {
|
|
40
|
+
return { code, message, retryable, step, target, severity };
|
|
41
|
+
}
|
|
42
|
+
function resolveBoundTask(input) {
|
|
43
|
+
const mode = input.mode ?? 'direct';
|
|
44
|
+
if (input.taskRef !== undefined) {
|
|
45
|
+
const resolved = resolveTaskRef(input.taskRef, { repoRoot: input.cwd });
|
|
46
|
+
if (!resolved.ok)
|
|
47
|
+
return {
|
|
48
|
+
kind: 'error',
|
|
49
|
+
result: failure(input, mode, resolved.taskId, resolved.code, resolved.message)
|
|
50
|
+
};
|
|
51
|
+
return boundTask(input, resolved.taskId, resolved.taskDir, resolved.taskMdPath, resolved.repoRoot, resolved.state);
|
|
52
|
+
}
|
|
53
|
+
const context = resolveTaskContext(undefined, { repoRoot: input.cwd });
|
|
54
|
+
if (context.ok)
|
|
55
|
+
return boundTask(input, context.taskId, context.taskDir, context.taskMdPath, context.repoRoot, context.state);
|
|
56
|
+
if (context.code === 'TASK_CONTEXT_NOT_FOUND')
|
|
57
|
+
return { kind: 'none' };
|
|
58
|
+
return {
|
|
59
|
+
kind: 'error',
|
|
60
|
+
result: failure(input, mode, context.taskId, context.code, context.message)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function boundTask(input, taskId, taskDir, taskMdPath, repoRoot, state) {
|
|
64
|
+
let branch;
|
|
65
|
+
try {
|
|
66
|
+
const frontmatter = parseTaskFrontmatter(fs.readFileSync(taskMdPath, 'utf8'));
|
|
67
|
+
branch = frontmatter.branch ?? '';
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
kind: 'error',
|
|
72
|
+
result: failure(input, input.mode ?? 'direct', taskId, 'TASK_CONTEXT_UNREADABLE', error instanceof Error ? error.message : String(error))
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (!branch)
|
|
76
|
+
return {
|
|
77
|
+
kind: 'error',
|
|
78
|
+
result: failure(input, input.mode ?? 'direct', taskId, 'TASK_CONTEXT_UNREADABLE', 'task branch is missing')
|
|
79
|
+
};
|
|
80
|
+
return { kind: 'bound', task: { repoRoot, taskId, taskDir, taskMdPath, state, branch } };
|
|
81
|
+
}
|
|
82
|
+
function validateCommon(input, branch) {
|
|
83
|
+
if (!input.message.trim())
|
|
84
|
+
return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Commit message is required' };
|
|
85
|
+
if (input.paths.length === 0 && !input.push)
|
|
86
|
+
return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'At least one explicit path is required unless this is a push-only retry' };
|
|
87
|
+
if (!/^[a-f0-9]{40,64}$/.test(input.expectedHead))
|
|
88
|
+
return { code: 'GIT_HEAD_EXPECTATION_REQUIRED', message: 'A valid expected HEAD is required' };
|
|
89
|
+
if (!/^[a-f0-9]{40,64}$/.test(input.expectedTree))
|
|
90
|
+
return { code: 'GIT_TREE_EXPECTATION_REQUIRED', message: 'A valid expected tree is required' };
|
|
91
|
+
if (input.paths.some((candidate) => !candidate || candidate.startsWith('/') || candidate.split(/[\\/]/).includes('..'))) {
|
|
92
|
+
return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Commit paths must stay inside the repository' };
|
|
93
|
+
}
|
|
94
|
+
if (input.paths.some((candidate) => /(^|\/)(?:\.env|credentials\.json|id_rsa)(?:$|\/)/i.test(candidate))) {
|
|
95
|
+
return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Sensitive paths cannot be committed' };
|
|
96
|
+
}
|
|
97
|
+
if (branch !== null && branch === '')
|
|
98
|
+
return { code: 'GIT_BRANCH_INVALID', message: 'A named branch is required' };
|
|
99
|
+
if (input.push) {
|
|
100
|
+
if (!input.push.remote || input.push.refs.length !== 1)
|
|
101
|
+
return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push requires one remote and one ref' };
|
|
102
|
+
const ref = input.push.refs[0] ?? '';
|
|
103
|
+
if (!HEAD_REF.test(ref) || HEAD_REF.exec(ref)?.[1] !== branch) {
|
|
104
|
+
return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push ref must be the current full heads ref' };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
function validateOrchestrated(input, task) {
|
|
110
|
+
if (!input.agent)
|
|
111
|
+
return { error: { code: 'ORCHESTRATION_PROVENANCE_MISMATCH', message: 'orchestrated commit requires an agent' } };
|
|
112
|
+
const planned = planOrchestrationStageCompletion(task.taskId, {
|
|
113
|
+
stage: 'commit',
|
|
114
|
+
round: 1,
|
|
115
|
+
artifact: 'commit',
|
|
116
|
+
role: 'executor',
|
|
117
|
+
agent: input.agent
|
|
118
|
+
}, { repoRoot: task.repoRoot });
|
|
119
|
+
const run = planned.result.run;
|
|
120
|
+
if (!planned.plan
|
|
121
|
+
|| !run
|
|
122
|
+
|| run.status !== 'running'
|
|
123
|
+
|| !run.commitAuthorization.issuedAt
|
|
124
|
+
|| run.commitAuthorization.consumedAt)
|
|
125
|
+
return {
|
|
126
|
+
error: {
|
|
127
|
+
code: planned.result.error?.code ?? 'ORCHESTRATION_PROVENANCE_MISMATCH',
|
|
128
|
+
message: planned.result.error?.message ?? 'orchestrated commit requires one matching activated commit delegation'
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
return { plan: planned.plan };
|
|
132
|
+
}
|
|
133
|
+
function syncTaskCommit(input, task, committedHead) {
|
|
134
|
+
let content;
|
|
135
|
+
try {
|
|
136
|
+
content = fs.readFileSync(task.taskMdPath, 'utf8');
|
|
137
|
+
const activity = locateActivityLog(content);
|
|
138
|
+
if (!activity)
|
|
139
|
+
return warning('TASK_STATUS_SYNC_FAILED', 'task has no unique Activity Log section', true, 'task', task.taskId, 'ACTION_REQUIRED');
|
|
140
|
+
const metadata = captureTaskWriteMetadata();
|
|
141
|
+
const commitNote = gitText(task.repoRoot, ['show', '-s', '--format=%h %s', committedHead]);
|
|
142
|
+
const alreadyLogged = activity.entries.some((entry) => entry.step === 'Commit' && entry.note === commitNote);
|
|
143
|
+
const mutations = [];
|
|
144
|
+
if (!alreadyLogged)
|
|
145
|
+
mutations.push({
|
|
146
|
+
kind: 'section',
|
|
147
|
+
aliases: ['活动日志', 'Activity Log'],
|
|
148
|
+
heading: activity.heading,
|
|
149
|
+
body: appendActivityEntry(activity, { time: metadata.timestamp, step: 'Commit', agent: input.agent, note: commitNote })
|
|
150
|
+
});
|
|
151
|
+
const frontmatter = { assigned_to: input.agent };
|
|
152
|
+
mutations.push({ kind: 'frontmatter', set: frontmatter });
|
|
153
|
+
if (mutations.length === 0)
|
|
154
|
+
return null;
|
|
155
|
+
const written = writeTask({
|
|
156
|
+
taskRef: task.taskId,
|
|
157
|
+
expectedState: 'active',
|
|
158
|
+
mutations
|
|
159
|
+
}, { repoRoot: task.repoRoot, metadataProvider: () => metadata });
|
|
160
|
+
if (written.status === 'failed')
|
|
161
|
+
return warning('TASK_STATUS_SYNC_FAILED', written.error.message, true, 'task', task.taskId, 'ACTION_REQUIRED');
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
return warning('TASK_STATUS_SYNC_FAILED', error instanceof Error ? error.message : String(error), true, 'task', task.taskId, 'ACTION_REQUIRED');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function executeUnlocked(input, task, mode) {
|
|
169
|
+
const inspected = inspectGitWorkflow(input.cwd);
|
|
170
|
+
if (!inspected.snapshot)
|
|
171
|
+
return failure(input, mode, task?.taskId ?? null, 'GIT_INSPECT_FAILED', 'Unable to inspect Git repository');
|
|
172
|
+
const branch = inspected.snapshot.branch;
|
|
173
|
+
const commonError = validateCommon(input, branch);
|
|
174
|
+
if (commonError)
|
|
175
|
+
return failure(input, mode, task?.taskId ?? null, commonError.code, commonError.message, 'blocked', inspected.snapshot);
|
|
176
|
+
if (task && task.state !== 'active')
|
|
177
|
+
return failure(input, mode, task.taskId, 'TASK_STATE_INVALID', 'task-bound commit requires an active task', 'blocked', inspected.snapshot);
|
|
178
|
+
if (task && task.branch !== branch)
|
|
179
|
+
return failure(input, mode, task.taskId, 'GIT_BRANCH_MISMATCH', `Task branch '${task.branch}' does not match current branch '${branch}'`, 'blocked', inspected.snapshot);
|
|
180
|
+
if (task && !input.agent)
|
|
181
|
+
return failure(input, mode, task.taskId, 'COMMIT_AGENT_REQUIRED', 'task-bound commit requires an agent', 'blocked', inspected.snapshot);
|
|
182
|
+
const orchestrationValidation = mode === 'orchestrated'
|
|
183
|
+
? (task
|
|
184
|
+
? validateOrchestrated(input, task)
|
|
185
|
+
: { error: { code: 'ORCHESTRATION_TASK_REQUIRED', message: 'orchestrated commit requires a task context' } })
|
|
186
|
+
: null;
|
|
187
|
+
if (orchestrationValidation && 'error' in orchestrationValidation) {
|
|
188
|
+
const error = orchestrationValidation.error;
|
|
189
|
+
return failure(input, mode, task?.taskId ?? null, error.code, error.message, 'blocked', inspected.snapshot);
|
|
190
|
+
}
|
|
191
|
+
const orchestrationCompletion = orchestrationValidation && 'plan' in orchestrationValidation
|
|
192
|
+
? orchestrationValidation.plan
|
|
193
|
+
: null;
|
|
194
|
+
const committed = input.paths.length === 0
|
|
195
|
+
? (() => {
|
|
196
|
+
if (inspected.snapshot.worktree.length > 0 || inspected.snapshot.staged.length > 0)
|
|
197
|
+
return {
|
|
198
|
+
status: 'failed',
|
|
199
|
+
changed: false,
|
|
200
|
+
snapshot: inspected.snapshot,
|
|
201
|
+
operations: [],
|
|
202
|
+
error: { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Push-only retry requires a clean working tree' }
|
|
203
|
+
};
|
|
204
|
+
if (inspected.snapshot.head !== input.expectedHead)
|
|
205
|
+
return {
|
|
206
|
+
status: 'failed',
|
|
207
|
+
changed: false,
|
|
208
|
+
snapshot: inspected.snapshot,
|
|
209
|
+
operations: [],
|
|
210
|
+
error: { code: 'GIT_HEAD_MISMATCH', message: `Expected HEAD ${input.expectedHead}, received ${inspected.snapshot.head}` }
|
|
211
|
+
};
|
|
212
|
+
const currentTree = gitText(input.cwd, ['rev-parse', 'HEAD^{tree}']);
|
|
213
|
+
if (currentTree !== input.expectedTree)
|
|
214
|
+
return {
|
|
215
|
+
status: 'failed',
|
|
216
|
+
changed: false,
|
|
217
|
+
snapshot: inspected.snapshot,
|
|
218
|
+
operations: [],
|
|
219
|
+
error: { code: 'GIT_TREE_MISMATCH', message: `Expected current tree ${input.expectedTree}, received ${currentTree}` }
|
|
220
|
+
};
|
|
221
|
+
return {
|
|
222
|
+
status: 'no-op',
|
|
223
|
+
changed: false,
|
|
224
|
+
snapshot: inspected.snapshot,
|
|
225
|
+
operations: [{ name: 'commit', status: 'no-op' }],
|
|
226
|
+
error: null
|
|
227
|
+
};
|
|
228
|
+
})()
|
|
229
|
+
: commitExplicitPaths({
|
|
230
|
+
cwd: input.cwd,
|
|
231
|
+
paths: input.paths,
|
|
232
|
+
message: input.message,
|
|
233
|
+
expectedHead: input.expectedHead,
|
|
234
|
+
expectedTree: input.expectedTree
|
|
235
|
+
});
|
|
236
|
+
if (committed.status === 'failed')
|
|
237
|
+
return {
|
|
238
|
+
...committed,
|
|
239
|
+
result: 'failed',
|
|
240
|
+
taskId: task?.taskId ?? null,
|
|
241
|
+
mode,
|
|
242
|
+
warnings: [],
|
|
243
|
+
snapshot: committed.snapshot
|
|
244
|
+
};
|
|
245
|
+
const warnings = [];
|
|
246
|
+
if (orchestrationCompletion) {
|
|
247
|
+
try {
|
|
248
|
+
commitOrchestrationStageCompletion(orchestrationCompletion);
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
warnings.push(warning('ORCHESTRATION_COMPLETION_FAILED', error instanceof Error ? error.message : String(error), true, 'orchestration', task?.taskId ?? 'orchestration', 'ACTION_REQUIRED'));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
let status = committed.status;
|
|
255
|
+
let result = committed.status === 'applied' ? 'committed' : 'no_op';
|
|
256
|
+
if (input.push) {
|
|
257
|
+
const target = `${input.push.remote}:${input.push.refs[0]}`;
|
|
258
|
+
const decision = commitPushDecision({ branch, automatic: input.push.automatic }, target);
|
|
259
|
+
if (!decision.shouldPush) {
|
|
260
|
+
warnings.push(decision.warning);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
const pushed = pushGitRefs({
|
|
264
|
+
cwd: input.cwd,
|
|
265
|
+
remote: input.push.remote,
|
|
266
|
+
refs: input.push.refs,
|
|
267
|
+
expectedSha: committed.snapshot?.head ?? gitText(input.cwd, ['rev-parse', 'HEAD'])
|
|
268
|
+
});
|
|
269
|
+
if (pushed.status !== 'applied')
|
|
270
|
+
warnings.push(warning('COMMIT_PUSH_FAILED', pushed.error?.message ?? 'Git push failed', true, 'push', target, 'ACTION_REQUIRED'));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (warnings.length > 0) {
|
|
274
|
+
status = 'applied';
|
|
275
|
+
result = 'committed_with_warnings';
|
|
276
|
+
}
|
|
277
|
+
if (task && (committed.status === 'applied' || committed.status === 'no-op')) {
|
|
278
|
+
const taskWarning = syncTaskCommit(input, task, committed.snapshot?.head ?? gitText(task.repoRoot, ['rev-parse', 'HEAD']));
|
|
279
|
+
if (taskWarning) {
|
|
280
|
+
warnings.push(taskWarning);
|
|
281
|
+
status = 'applied';
|
|
282
|
+
result = 'committed_with_warnings';
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return {
|
|
286
|
+
status,
|
|
287
|
+
changed: committed.changed,
|
|
288
|
+
result,
|
|
289
|
+
taskId: task?.taskId ?? null,
|
|
290
|
+
mode,
|
|
291
|
+
warnings: mergeOperationWarnings(warnings),
|
|
292
|
+
snapshot: committed.snapshot,
|
|
293
|
+
operations: committed.operations,
|
|
294
|
+
error: null
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function executeCommitOperation(input) {
|
|
298
|
+
if (input.mode !== undefined && !isCommitExecutionMode(input.mode))
|
|
299
|
+
return {
|
|
300
|
+
status: 'blocked',
|
|
301
|
+
changed: false,
|
|
302
|
+
result: 'blocked',
|
|
303
|
+
taskId: null,
|
|
304
|
+
mode: 'direct',
|
|
305
|
+
warnings: [],
|
|
306
|
+
snapshot: null,
|
|
307
|
+
operations: [],
|
|
308
|
+
error: { code: 'COMMIT_MODE_INVALID', message: 'commit mode must be direct or orchestrated' }
|
|
309
|
+
};
|
|
310
|
+
const mode = input.mode ?? 'direct';
|
|
311
|
+
let normalizedInput;
|
|
312
|
+
try {
|
|
313
|
+
normalizedInput = { ...input, cwd: canonicalRepositoryRoot(input.cwd) };
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
return failure(input, mode, null, 'GIT_INSPECT_FAILED', error instanceof Error ? error.message : 'Unable to resolve Git repository root', 'blocked');
|
|
317
|
+
}
|
|
318
|
+
const resolved = resolveBoundTask(normalizedInput);
|
|
319
|
+
if (resolved.kind === 'error')
|
|
320
|
+
return resolved.result;
|
|
321
|
+
const task = resolved.kind === 'bound' ? resolved.task : null;
|
|
322
|
+
if (mode === 'orchestrated' && !task)
|
|
323
|
+
return failure(normalizedInput, mode, null, 'ORCHESTRATION_TASK_REQUIRED', 'orchestrated commit requires a task context', 'blocked');
|
|
324
|
+
try {
|
|
325
|
+
return task
|
|
326
|
+
? withRepositoryMutationLock(normalizedInput.cwd, () => withTaskExecutionLock(normalizedInput.cwd, task.taskId, 'commit-operation', () => executeUnlocked(normalizedInput, task, mode)))
|
|
327
|
+
: withRepositoryMutationLock(normalizedInput.cwd, () => executeUnlocked(normalizedInput, null, mode));
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
const code = error instanceof TaskExecutionLockError ? error.code : 'COMMIT_OPERATION_FAILED';
|
|
331
|
+
return failure(normalizedInput, mode, task?.taskId ?? null, code, error instanceof Error ? error.message : String(error), 'blocked');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
export { executeCommitOperation };
|
|
335
|
+
//# sourceMappingURL=commit-operation.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function commitPushDecision(policy, target) {
|
|
2
|
+
const branch = policy.branch.trim();
|
|
3
|
+
if (policy.automatic && (branch === 'main' || branch === 'master'))
|
|
4
|
+
return {
|
|
5
|
+
shouldPush: false,
|
|
6
|
+
warning: {
|
|
7
|
+
code: 'COMMIT_AUTOPUSH_PROTECTED_BRANCH',
|
|
8
|
+
message: `Automatic commit push is disabled for protected branch '${branch}'`,
|
|
9
|
+
retryable: false,
|
|
10
|
+
step: 'push',
|
|
11
|
+
target,
|
|
12
|
+
severity: 'IMPORTANT'
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
return { shouldPush: true, warning: null };
|
|
16
|
+
}
|
|
17
|
+
export { commitPushDecision };
|
|
18
|
+
//# sourceMappingURL=commit-policy.js.map
|
|
@@ -6,6 +6,7 @@ import { LEDGER_COLUMNS, LEDGER_HEADINGS, parseLedger } from './ledger.js';
|
|
|
6
6
|
import { resolveTaskContext } from './resolve-ref.js';
|
|
7
7
|
import { extractSection, extractSubSection, findSectionHeading } from './sections.js';
|
|
8
8
|
import { captureTaskWriteMetadata, writeTask } from './write.js';
|
|
9
|
+
import { allowsManualOverride } from './guard-override.js';
|
|
9
10
|
const DECISION_ALIASES = ['人工裁决', 'Human Rulings', 'Human Decisions', 'Human Decision'];
|
|
10
11
|
const ACTIVITY_ALIASES = ['活动日志', 'Activity Log'];
|
|
11
12
|
function failed(code, message, taskId = null, ledgerId = null) {
|
|
@@ -56,7 +57,8 @@ function applyHumanDecision(request, options = {}) {
|
|
|
56
57
|
const resolved = resolveTaskContext(request.taskRef, { repoRoot: options.repoRoot });
|
|
57
58
|
if (!resolved.ok)
|
|
58
59
|
return failed(resolved.code, resolved.message, resolved.taskId);
|
|
59
|
-
|
|
60
|
+
const stateOverride = allowsManualOverride(options.manualOverride, 'decision-intent', 'TASK_STATE_MISMATCH');
|
|
61
|
+
if (resolved.state !== 'active' && !stateOverride)
|
|
60
62
|
return failed('TASK_STATE_MISMATCH', `task ${resolved.taskId} is ${resolved.state}, expected active`, resolved.taskId);
|
|
61
63
|
let content;
|
|
62
64
|
let rows;
|
|
@@ -155,7 +157,7 @@ function applyHumanDecision(request, options = {}) {
|
|
|
155
157
|
kind: 'section', aliases: ACTIVITY_ALIASES, heading: activity.heading,
|
|
156
158
|
body: appendActivityEntry(activity, { time: metadata.timestamp, step: 'Human Decision', agent: 'human', note: `${row.id} decided → ${recordId}` })
|
|
157
159
|
});
|
|
158
|
-
const writeResult = writeTask({ taskRef: resolved.taskId, expectedState: 'active', mutations, dryRun: request.dryRun }, {
|
|
160
|
+
const writeResult = writeTask({ taskRef: resolved.taskId, expectedState: stateOverride ? resolved.state : 'active', mutations, dryRun: request.dryRun }, {
|
|
159
161
|
...options,
|
|
160
162
|
taskLocation: { repoRoot: resolved.repoRoot, taskId: resolved.taskId, taskMdPath: resolved.taskMdPath, state: resolved.state },
|
|
161
163
|
metadataProvider: () => metadata
|