@cr1992/agentkit 1.0.0
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/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.en.md +107 -0
- package/README.md +103 -0
- package/bin/agentkit.mjs +4 -0
- package/bin/cli.mjs +273 -0
- package/core/atomic-fs.mjs +23 -0
- package/core/cli-help.mjs +54 -0
- package/core/content-digest.mjs +66 -0
- package/core/digest.mjs +67 -0
- package/core/json-schema-lite.mjs +60 -0
- package/core/legacy-entry.mjs +37 -0
- package/core/reflection.mjs +142 -0
- package/core/runtime-bundle.mjs +101 -0
- package/docs/loop/embedded-review-adapter.md +41 -0
- package/docs/loop/loop-state-machine.md +43 -0
- package/docs/loop/recovery-and-fuses.md +34 -0
- package/docs/orchestrate/dispatch-contract.md +92 -0
- package/docs/orchestrate/failure-routing-and-recovery.md +46 -0
- package/docs/orchestrate/host-capability-cache.md +170 -0
- package/docs/orchestrate/isolation-fallback.md +18 -0
- package/docs/orchestrate/model-routing-config.md +186 -0
- package/docs/orchestrate/orchestration-runtime.md +261 -0
- package/docs/orchestrate/review-budget.md +90 -0
- package/docs/orchestrate/task-playbooks.md +85 -0
- package/docs/orchestrate/user-facing-reporting.md +14 -0
- package/docs/verify/evidence-schema.md +167 -0
- package/docs/verify/input-preparation.md +44 -0
- package/docs/verify/verification-protocol.md +76 -0
- package/docs/worktree/batch-integration.md +176 -0
- package/docs/worktree/delivery-identity.md +41 -0
- package/docs/worktree/profile.md +107 -0
- package/docs/worktree/reclaim-and-watch.md +96 -0
- package/docs/worktree/review-lifecycle.md +92 -0
- package/docs/worktree/spawn-and-stack.md +74 -0
- package/domains/loop/loop-runtime.mjs +1056 -0
- package/domains/orchestrate/contract-tool.mjs +169 -0
- package/domains/orchestrate/host_capability_cache.mjs +437 -0
- package/domains/orchestrate/orchestration-ledger.mjs +332 -0
- package/domains/orchestrate/orchestration-metadata.mjs +4 -0
- package/domains/orchestrate/orchestration-reflection.mjs +119 -0
- package/domains/orchestrate/resolve_model_policy.mjs +311 -0
- package/domains/orchestrate/review-budget.mjs +162 -0
- package/domains/orchestrate/worker-capability-preflight.mjs +227 -0
- package/domains/verify/verification-runtime.mjs +1638 -0
- package/domains/worktree/worktree-archive.mjs +135 -0
- package/domains/worktree/worktree-artifact.mjs +123 -0
- package/domains/worktree/worktree-batch-integrate.mjs +713 -0
- package/domains/worktree/worktree-batch-plan.mjs +198 -0
- package/domains/worktree/worktree-batch-result.mjs +241 -0
- package/domains/worktree/worktree-core.mjs +908 -0
- package/domains/worktree/worktree-doctor.mjs +493 -0
- package/domains/worktree/worktree-history.mjs +377 -0
- package/domains/worktree/worktree-learning.mjs +110 -0
- package/domains/worktree/worktree-lifecycle.mjs +786 -0
- package/domains/worktree/worktree-merge-preview.mjs +409 -0
- package/domains/worktree/worktree-mgr.mjs +261 -0
- package/domains/worktree/worktree-process.mjs +55 -0
- package/domains/worktree/worktree-profile.mjs +800 -0
- package/domains/worktree/worktree-provider-gitlab.mjs +59 -0
- package/domains/worktree/worktree-reclaim.mjs +683 -0
- package/domains/worktree/worktree-review-refresh.mjs +574 -0
- package/domains/worktree/worktree-review-watch.mjs +661 -0
- package/domains/worktree/worktree-scan.mjs +510 -0
- package/domains/worktree/worktree-trace-test-worker.mjs +23 -0
- package/domains/worktree/worktree-trace.mjs +478 -0
- package/manage-worktrees/SKILL.md +87 -0
- package/manage-worktrees/agents/openai.yaml +4 -0
- package/manage-worktrees/scripts/worktree-mgr.mjs +10 -0
- package/manage-worktrees/scripts/worktree-scan.mjs +10 -0
- package/orchestrate-subagents/SKILL.md +173 -0
- package/orchestrate-subagents/agents/openai.yaml +4 -0
- package/orchestrate-subagents/scripts/contract-tool.mjs +10 -0
- package/orchestrate-subagents/scripts/host_capability_cache.mjs +10 -0
- package/orchestrate-subagents/scripts/orchestration-ledger.mjs +10 -0
- package/orchestrate-subagents/scripts/orchestration-reflection.mjs +10 -0
- package/orchestrate-subagents/scripts/resolve_model_policy.mjs +10 -0
- package/orchestrate-subagents/scripts/review-budget.mjs +10 -0
- package/orchestrate-subagents/scripts/worker-capability-preflight.mjs +10 -0
- package/package.json +48 -0
- package/run-agent-verify-loop/SKILL.md +127 -0
- package/run-agent-verify-loop/agents/openai.yaml +4 -0
- package/run-agent-verify-loop/scripts/loop-runtime.mjs +10 -0
- package/schemas/artifact-ref-v1.schema.json +23 -0
- package/schemas/batch-result-v1.schema.json +138 -0
- package/schemas/controller-recheck-record-v1.schema.json +22 -0
- package/schemas/convergence-report-v1.schema.json +9 -0
- package/schemas/effective-worker-capability-v1.schema.json +36 -0
- package/schemas/embedded-verification-record-v1.schema.json +32 -0
- package/schemas/evidence-package-v1.schema.json +41 -0
- package/schemas/improvement-proposal-v1.schema.json +18 -0
- package/schemas/loop-state-v1.schema.json +34 -0
- package/schemas/model-policy-resolution-v1.schema.json +41 -0
- package/schemas/orchestration-ledger-v1.schema.json +110 -0
- package/schemas/reflection-record-v1.schema.json +24 -0
- package/schemas/review-result-v1.schema.json +37 -0
- package/schemas/task-contract-v1.schema.json +83 -0
- package/schemas/verification-profile-v1.schema.json +60 -0
- package/schemas/worker-capability-requirements-v1.schema.json +21 -0
- package/schemas/worktree-binding-v1.schema.json +14 -0
- package/shell-manifest.json +79 -0
- package/verify-agent-output/SKILL.md +119 -0
- package/verify-agent-output/agents/openai.yaml +4 -0
- package/verify-agent-output/scripts/verification-runtime.mjs +10 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Domain command factory. Dependencies are injected once by the thin CLI composition root,
|
|
5
|
+
* keeping this module acyclic and independently reviewable.
|
|
6
|
+
*
|
|
7
|
+
* `archive` 只处理"目录已经不存在、分支已经安全"的历史 record:它只追加一条 `archived`
|
|
8
|
+
* trace event,用来让 doctor/list 停止对已经确认安全的历史 record 重复报警。它与 `reclaim`
|
|
9
|
+
* 完全不同——不删除分支、不删除目录、不改任何 ref、不 prune,因为它的前置条件已经要求目录
|
|
10
|
+
* 不存在(不然本来就该走 reclaim),archive 只是把"已经确认过的历史"标记出来,纯粹是 trace
|
|
11
|
+
* 元数据操作。
|
|
12
|
+
* @param {Record<string, any>} deps
|
|
13
|
+
*/
|
|
14
|
+
export function createCommands(deps) {
|
|
15
|
+
const {
|
|
16
|
+
loadRepositoryProfile,
|
|
17
|
+
loadRecords,
|
|
18
|
+
selectRecord,
|
|
19
|
+
liveGitSnapshot,
|
|
20
|
+
updateRecord,
|
|
21
|
+
localBranchExists,
|
|
22
|
+
resolveBaseRef,
|
|
23
|
+
isAncestor,
|
|
24
|
+
assertHistoryOperationIdle,
|
|
25
|
+
git,
|
|
26
|
+
gitTry,
|
|
27
|
+
log,
|
|
28
|
+
die,
|
|
29
|
+
flag,
|
|
30
|
+
rejectUnknownFlags,
|
|
31
|
+
oneLine,
|
|
32
|
+
} = deps;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 三项前置条件全部满足才允许归档:
|
|
36
|
+
* 1. worktree 目录已经不存在(否则应该走 reclaim,或先确认再手工处理);
|
|
37
|
+
* 2. 分支已经不存在,或分支 tip 可达于登记的 base ref / 当前 remote HEAD;
|
|
38
|
+
* 3.(由调用方在此之前检查)worktree 不处于 ready_for_review 的武装监听状态。
|
|
39
|
+
* 任一条件不满足都返回 `{ ok:false, reason }`,由调用方按 KEEP 惯例处理——不 throw、不
|
|
40
|
+
* exit,保持和 reclaim 的 KEEP 语义一致。
|
|
41
|
+
* @param {ReturnType<typeof loadRepositoryProfile>} loaded
|
|
42
|
+
* @param {Record<string,any>} record
|
|
43
|
+
*/
|
|
44
|
+
function evaluateArchivePreconditions(loaded, record) {
|
|
45
|
+
const snapshot = liveGitSnapshot(record);
|
|
46
|
+
if (snapshot.present) {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
reason: 'worktree 目录仍然存在;archive 只接受目录已经不存在的历史 record(目录还在时请走 reclaim,或先确认后再手工清理)。',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const branchExists = localBranchExists(loaded, record);
|
|
53
|
+
if (!branchExists) {
|
|
54
|
+
return { ok: true, basis: 'branch_absent', branchTipSha: record.last_head ?? null, matchedRef: null };
|
|
55
|
+
}
|
|
56
|
+
const branchTipSha = git(['rev-parse', `refs/heads/${record.branch}`], loaded.context.current_worktree);
|
|
57
|
+
const candidateRefs = [];
|
|
58
|
+
if (record.base_ref) candidateRefs.push(record.base_ref);
|
|
59
|
+
const remoteHead = resolveBaseRef(loaded.context.current_worktree, loaded.profile.default_base ?? null, null);
|
|
60
|
+
if (remoteHead?.ref && !candidateRefs.includes(remoteHead.ref)) candidateRefs.push(remoteHead.ref);
|
|
61
|
+
for (const ref of candidateRefs) {
|
|
62
|
+
const resolved = gitTry(['rev-parse', '--verify', '--quiet', `${ref}^{commit}`], loaded.context.current_worktree);
|
|
63
|
+
if (resolved.ok && isAncestor(loaded.context.current_worktree, branchTipSha, resolved.out)) {
|
|
64
|
+
return { ok: true, basis: 'branch_merged', branchTipSha, matchedRef: ref };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
ok: false,
|
|
69
|
+
reason: candidateRefs.length > 0
|
|
70
|
+
? `分支 ${record.branch} 仍然存在,且未合入已知 base(${candidateRefs.join(', ')});请先确认已合入或删除分支后再归档。`
|
|
71
|
+
: `分支 ${record.branch} 仍然存在,且没有可用于判断合入状态的 base ref;请先补登记 base 或删除分支后再归档。`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function cmdArchive(args) {
|
|
76
|
+
rejectUnknownFlags(args.flags, ['reason', 'id', 'config']);
|
|
77
|
+
const reasonInput = flag(args.flags, 'reason');
|
|
78
|
+
if (!reasonInput) die('archive 需要 --reason "<text>"。', 2);
|
|
79
|
+
const reason = oneLine(reasonInput, '--reason', 500);
|
|
80
|
+
const loaded = loadRepositoryProfile({ explicitConfigPath: flag(args.flags, 'config') });
|
|
81
|
+
const records = loadRecords(loaded.context.common_dir);
|
|
82
|
+
const record = selectRecord(records, args.positionals[0] ?? null, flag(args.flags, 'id'));
|
|
83
|
+
|
|
84
|
+
if (record.worktree_state === 'archived') {
|
|
85
|
+
log(`KEEP ${record.path}: record 已经归档,拒绝重复归档。`);
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (record.worktree_state === 'reclaimed') {
|
|
90
|
+
log(`KEEP ${record.path}: record 已经 reclaimed,无需归档。`);
|
|
91
|
+
process.exitCode = 1;
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (record.worktree_state === 'reclaim_ready') {
|
|
95
|
+
log(`KEEP ${record.path}: reclaim 流程被中断(reclaim_ready);请先修复并完成 reclaim,不能用 archive 绕过。`);
|
|
96
|
+
process.exitCode = 1;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
assertHistoryOperationIdle(record, 'archive');
|
|
100
|
+
|
|
101
|
+
const activeWatch = record.auto_reclaim && !['disarmed', 'reclaimed'].includes(record.auto_reclaim.state)
|
|
102
|
+
? record.auto_reclaim
|
|
103
|
+
: null;
|
|
104
|
+
if (activeWatch) {
|
|
105
|
+
log(`KEEP ${record.path}: worktree 仍处于 ready_for_review 武装监听状态(state=${activeWatch.state});请先 unwatch ${record.task} 再归档。`);
|
|
106
|
+
process.exitCode = 1;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const evaluation = evaluateArchivePreconditions(loaded, record);
|
|
111
|
+
if (!evaluation.ok) {
|
|
112
|
+
log(`KEEP ${record.path}: ${evaluation.reason}`);
|
|
113
|
+
process.exitCode = 1;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const now = new Date().toISOString();
|
|
118
|
+
const details = {
|
|
119
|
+
reason,
|
|
120
|
+
basis: evaluation.basis,
|
|
121
|
+
branch: record.branch ?? null,
|
|
122
|
+
branch_tip_sha: evaluation.branchTipSha,
|
|
123
|
+
matched_base_ref: evaluation.matchedRef,
|
|
124
|
+
};
|
|
125
|
+
const updated = updateRecord(record, 'archived', (next) => {
|
|
126
|
+
next.worktree_state = 'archived';
|
|
127
|
+
next.archived_at = now;
|
|
128
|
+
next.archive = { ...details };
|
|
129
|
+
}, details, loaded.context.common_dir);
|
|
130
|
+
|
|
131
|
+
log(`已归档 ${updated.worktree_id.slice(0, 8)} task=${updated.task}(basis=${evaluation.basis}${evaluation.matchedRef ? `, base=${evaluation.matchedRef}` : ''});不删除分支、不删除目录、不改 refs。`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return { cmdArchive };
|
|
135
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Domain command factory. Dependencies are injected once by the thin CLI composition root,
|
|
5
|
+
* keeping this module acyclic and independently reviewable.
|
|
6
|
+
* @param {Record<string, any>} deps
|
|
7
|
+
*/
|
|
8
|
+
export function createCommands(deps) {
|
|
9
|
+
const {
|
|
10
|
+
resolve,
|
|
11
|
+
ensureRepositoryIdentity,
|
|
12
|
+
loadRepositoryProfile,
|
|
13
|
+
readRepositoryIdentity,
|
|
14
|
+
git,
|
|
15
|
+
gitTry,
|
|
16
|
+
die,
|
|
17
|
+
flag,
|
|
18
|
+
readJsonFileOrDie,
|
|
19
|
+
rejectUnknownFlags,
|
|
20
|
+
loadRecords,
|
|
21
|
+
selectRecord,
|
|
22
|
+
liveGitSnapshot,
|
|
23
|
+
assertHistoryOperationIdle,
|
|
24
|
+
} = deps;
|
|
25
|
+
|
|
26
|
+
function worktreeEnvelopeContext(args) {
|
|
27
|
+
const loaded = loadRepositoryProfile({ explicitConfigPath: flag(args.flags, 'config') });
|
|
28
|
+
const record = selectRecord(loadRecords(loaded.context.common_dir), args.positionals[0] ?? null, flag(args.flags, 'id'));
|
|
29
|
+
const identity = ensureRepositoryIdentity(loaded.context);
|
|
30
|
+
const snapshot = liveGitSnapshot(record);
|
|
31
|
+
return { loaded, record, identity, snapshot };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildBinding(record, identity, snapshot) {
|
|
35
|
+
return {
|
|
36
|
+
schema_version: 1,
|
|
37
|
+
provider: 'manage-worktrees',
|
|
38
|
+
repository_id: identity.repository_id,
|
|
39
|
+
worktree_id: record.worktree_id,
|
|
40
|
+
workdir: record.path,
|
|
41
|
+
branch: record.branch,
|
|
42
|
+
owner: { agent: record.agent.host, agent_id: record.agent.id, epoch: record.ownership_epochs?.length ?? 0 },
|
|
43
|
+
base_sha: record.base_sha,
|
|
44
|
+
head_sha: snapshot.head ?? record.last_head,
|
|
45
|
+
task_status: record.task_status,
|
|
46
|
+
worktree_state: record.worktree_state,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function buildArtifact(record, identity, snapshot, cwd) {
|
|
51
|
+
if (!snapshot.present) die('Artifact 要求 worktree 当前存在。', 2);
|
|
52
|
+
if (snapshot.dirty !== false) die('Artifact 要求 worktree clean。', 2);
|
|
53
|
+
if (!snapshot.head) die('Artifact 无法冻结 HEAD。', 2);
|
|
54
|
+
const objectFormat = git(['rev-parse', '--show-object-format'], cwd);
|
|
55
|
+
return {
|
|
56
|
+
schema_version: 1,
|
|
57
|
+
provider: 'manage-worktrees',
|
|
58
|
+
repository_id: identity.repository_id,
|
|
59
|
+
object_format: objectFormat,
|
|
60
|
+
base_sha: record.base_sha,
|
|
61
|
+
artifact_sha: snapshot.head,
|
|
62
|
+
branch_hint: record.branch ?? undefined,
|
|
63
|
+
worktree_id: record.worktree_id,
|
|
64
|
+
ownership_epoch: record.ownership_epochs?.length ?? 0,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function cmdBinding(args) {
|
|
69
|
+
rejectUnknownFlags(args.flags, ['json', 'id', 'config']);
|
|
70
|
+
const { record, identity, snapshot } = worktreeEnvelopeContext(args);
|
|
71
|
+
console.log(JSON.stringify(buildBinding(record, identity, snapshot), null, 2));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function cmdArtifact(args) {
|
|
75
|
+
rejectUnknownFlags(args.flags, ['json', 'id', 'config']);
|
|
76
|
+
const { loaded, record, identity, snapshot } = worktreeEnvelopeContext(args);
|
|
77
|
+
assertHistoryOperationIdle(record, 'artifact');
|
|
78
|
+
console.log(JSON.stringify(buildArtifact(record, identity, snapshot, loaded.context.current_worktree), null, 2));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function verifyArtifactEnvelope(artifact, loaded, records = loadRecords(loaded.context.common_dir)) {
|
|
82
|
+
if (!artifact || artifact.schema_version !== 1 || artifact.provider !== 'manage-worktrees') throw new Error('Artifact schema/provider 无效');
|
|
83
|
+
if (typeof artifact.worktree_id !== 'string' || !artifact.worktree_id) throw new Error('Artifact worktree_id 缺失');
|
|
84
|
+
if (!Number.isInteger(artifact.ownership_epoch) || artifact.ownership_epoch < 1) throw new Error('Artifact ownership_epoch 缺失或无效');
|
|
85
|
+
const identity = readRepositoryIdentity(loaded.context);
|
|
86
|
+
if (!identity || artifact.repository_id !== identity.repository_id) throw new Error('Artifact repository_id 不匹配');
|
|
87
|
+
const objectFormat = git(['rev-parse', '--show-object-format'], loaded.context.current_worktree);
|
|
88
|
+
if (artifact.object_format !== objectFormat) throw new Error('Artifact object_format 不匹配');
|
|
89
|
+
const length = objectFormat === 'sha256' ? 64 : objectFormat === 'sha1' ? 40 : 0;
|
|
90
|
+
for (const field of ['base_sha', 'artifact_sha']) {
|
|
91
|
+
if (!new RegExp(`^[0-9a-f]{${length}}$`, 'u').test(String(artifact[field] ?? ''))) throw new Error(`${field} 不是完整 object id`);
|
|
92
|
+
if (!gitTry(['cat-file', '-e', `${artifact[field]}^{commit}`], loaded.context.current_worktree).ok) throw new Error(`${field} 不是可达 commit`);
|
|
93
|
+
}
|
|
94
|
+
if (!gitTry(['merge-base', '--is-ancestor', artifact.base_sha, artifact.artifact_sha], loaded.context.current_worktree).ok) throw new Error('Artifact base 不是 artifact ancestor');
|
|
95
|
+
const record = records.find((candidate) => candidate.worktree_id === artifact.worktree_id);
|
|
96
|
+
if (!record) throw new Error('Artifact worktree_id 不存在');
|
|
97
|
+
if (record.base_sha !== artifact.base_sha) throw new Error('Artifact base_sha 与 worktree record 不匹配');
|
|
98
|
+
if (artifact.ownership_epoch !== (record.ownership_epochs?.length ?? 0)) throw new Error('Artifact ownership_epoch 已 stale');
|
|
99
|
+
const live = liveGitSnapshot(record);
|
|
100
|
+
if (!live.present) throw new Error('Artifact worktree 已不存在');
|
|
101
|
+
if (live.head !== artifact.artifact_sha) throw new Error('Artifact SHA 与 live HEAD 不一致');
|
|
102
|
+
if (live.dirty !== false) throw new Error('Artifact worktree 已变脏');
|
|
103
|
+
return { valid: true, repository_id: artifact.repository_id, artifact_sha: artifact.artifact_sha, object_format: artifact.object_format };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function cmdVerifyArtifact(args) {
|
|
107
|
+
rejectUnknownFlags(args.flags, ['json', 'config']);
|
|
108
|
+
const path = args.positionals[0];
|
|
109
|
+
if (!path) die('verify-artifact 需要 Artifact JSON 文件。', 2);
|
|
110
|
+
const loaded = loadRepositoryProfile({ explicitConfigPath: flag(args.flags, 'config') });
|
|
111
|
+
const artifact = readJsonFileOrDie(resolve(path), 'Artifact 文件');
|
|
112
|
+
console.log(JSON.stringify(verifyArtifactEnvelope(artifact, loaded), null, 2));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
worktreeEnvelopeContext,
|
|
118
|
+
verifyArtifactEnvelope,
|
|
119
|
+
cmdBinding,
|
|
120
|
+
cmdArtifact,
|
|
121
|
+
cmdVerifyArtifact,
|
|
122
|
+
};
|
|
123
|
+
}
|