atris 3.15.57 → 3.16.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/AGENTS.md +2 -2
- package/GETTING_STARTED.md +1 -1
- package/PERSONA.md +4 -4
- package/README.md +11 -11
- package/atris/skills/copy-editor/SKILL.md +30 -4
- package/atris/skills/improve/SKILL.md +18 -20
- package/atris/wiki/concepts/agent-activation-contract.md +5 -3
- package/atris/wiki/concepts/workspace-initialization-contract.md +4 -4
- package/atris/wiki/index.md +1 -0
- package/ax +522 -73
- package/bin/atris.js +31 -30
- package/commands/align.js +0 -14
- package/commands/apps.js +102 -1
- package/commands/autopilot.js +197 -22
- package/commands/brain.js +219 -34
- package/commands/brainstorm.js +0 -829
- package/commands/computer.js +0 -60
- package/commands/improve.js +501 -0
- package/commands/integrations.js +233 -71
- package/commands/lesson.js +44 -0
- package/commands/member.js +4498 -226
- package/commands/mission.js +302 -27
- package/commands/now.js +89 -1
- package/commands/radar.js +181 -56
- package/commands/soul.js +0 -4
- package/commands/task.js +5582 -517
- package/commands/terminal.js +14 -10
- package/commands/wiki.js +87 -1
- package/commands/workflow.js +288 -73
- package/commands/worktree.js +52 -15
- package/commands/xp.js +6 -65
- package/lib/auto-accept-certified.js +294 -0
- package/lib/file-ops.js +0 -184
- package/lib/member-alive.js +232 -0
- package/lib/policy-lessons.js +280 -0
- package/lib/receipt-evidence.js +64 -0
- package/lib/state-detection.js +34 -0
- package/lib/task-db.js +568 -16
- package/lib/task-proof.js +43 -0
- package/package.json +1 -1
- package/utils/auth.js +13 -4
- package/commands/research.js +0 -52
- package/lib/section-merge.js +0 -196
package/commands/xp.js
CHANGED
|
@@ -30,7 +30,11 @@ const EARNING_MODEL = {
|
|
|
30
30
|
schema: 'atris.agentxp_earning_model.v1',
|
|
31
31
|
score_name: AGENT_XP_LABEL,
|
|
32
32
|
primary_public_source: 'accepted_task_receipt',
|
|
33
|
-
public_rule: 'No accepted proof-backed task episodes means no AgentXP leaderboard movement.',
|
|
33
|
+
public_rule: 'No human-accepted proof-backed task episodes means no AgentXP leaderboard movement.',
|
|
34
|
+
policies: {
|
|
35
|
+
task_done_with_proof: 'Records proof for review and RL context; emits no Career XP receipt until human accept.',
|
|
36
|
+
task_accept: 'Human accept with proof and positive reward is the Career XP receipt boundary.',
|
|
37
|
+
},
|
|
34
38
|
weights: [
|
|
35
39
|
{
|
|
36
40
|
id: 'accepted_task_receipt',
|
|
@@ -674,6 +678,7 @@ function buildAtrisActionSignalFromRows(rows = []) {
|
|
|
674
678
|
included_in_total_agent_xp: false,
|
|
675
679
|
public_leaderboard: false,
|
|
676
680
|
role: 'rl_routing_only',
|
|
681
|
+
note: 'Task done/finish proof can count here as non-public action context; Career XP still requires human accept.',
|
|
677
682
|
};
|
|
678
683
|
}
|
|
679
684
|
|
|
@@ -1381,10 +1386,6 @@ function readTaskProjectionState(workspace) {
|
|
|
1381
1386
|
}
|
|
1382
1387
|
}
|
|
1383
1388
|
|
|
1384
|
-
function readTaskProjection(workspace) {
|
|
1385
|
-
return readTaskProjectionState(workspace).tasks;
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
1389
|
function taskRef(task) {
|
|
1389
1390
|
return task?.display_id || task?.legacy_ref || task?.id || 'task';
|
|
1390
1391
|
}
|
|
@@ -1715,36 +1716,6 @@ function buildCareerXpSessionCapsule(args = []) {
|
|
|
1715
1716
|
return capsule;
|
|
1716
1717
|
}
|
|
1717
1718
|
|
|
1718
|
-
function normalizeLocalScore(score, workspace) {
|
|
1719
|
-
const card = score.profile_card || score.player_card || {};
|
|
1720
|
-
const integrity = score.integrity || {};
|
|
1721
|
-
const careerXp = asNumber(card.agent_xp ?? card.career_xp);
|
|
1722
|
-
const leaderboardEligible = Boolean(
|
|
1723
|
-
card.leaderboard_eligible ?? integrity.leaderboard_eligible
|
|
1724
|
-
);
|
|
1725
|
-
|
|
1726
|
-
return {
|
|
1727
|
-
metric_label: AGENT_XP_LABEL,
|
|
1728
|
-
agent_xp: careerXp,
|
|
1729
|
-
career_xp: careerXp,
|
|
1730
|
-
level: levelFromXp(careerXp),
|
|
1731
|
-
operator: score.operator || null,
|
|
1732
|
-
leaderboard_eligible: leaderboardEligible,
|
|
1733
|
-
source: 'local_contribution_score',
|
|
1734
|
-
workspace_root: score.workspace_root || workspace,
|
|
1735
|
-
current_form_by_arena: {
|
|
1736
|
-
local_workspace: {
|
|
1737
|
-
ovr: asNumber(card.ovr || card.current_form),
|
|
1738
|
-
current_form: asNumber(card.current_form || card.ovr),
|
|
1739
|
-
visible_stats: Array.isArray(card.visible_stats) ? card.visible_stats : [],
|
|
1740
|
-
leaderboard_eligible: leaderboardEligible,
|
|
1741
|
-
integrity_status: score.label || integrity.status || 'unknown',
|
|
1742
|
-
},
|
|
1743
|
-
},
|
|
1744
|
-
contribution_graph: score.contribution_graph || {},
|
|
1745
|
-
};
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
1719
|
function renderContributionGraph(graph) {
|
|
1749
1720
|
if (!graph || !Array.isArray(graph.days)) return;
|
|
1750
1721
|
console.log('');
|
|
@@ -1762,36 +1733,6 @@ function renderLocalActivity(activity) {
|
|
|
1762
1733
|
);
|
|
1763
1734
|
}
|
|
1764
1735
|
|
|
1765
|
-
function loadLocalPayload(args) {
|
|
1766
|
-
const workspace = path.resolve(readFlag(args, '--workspace', process.cwd()));
|
|
1767
|
-
const operator = readFlag(
|
|
1768
|
-
args,
|
|
1769
|
-
'--operator',
|
|
1770
|
-
process.env.ATRIS_OPERATOR || process.env.USER || os.userInfo().username
|
|
1771
|
-
);
|
|
1772
|
-
const script = path.join(workspace, 'scripts', 'contribution_score.py');
|
|
1773
|
-
|
|
1774
|
-
if (!fs.existsSync(script)) {
|
|
1775
|
-
throw new Error(`No local contribution scorer found at ${path.relative(process.cwd(), script)}`);
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
|
-
const result = spawnSync(
|
|
1779
|
-
process.env.PYTHON || 'python3',
|
|
1780
|
-
[script, '--workspace', workspace, '--operator', operator, '--json'],
|
|
1781
|
-
{ cwd: workspace, encoding: 'utf8', maxBuffer: 20 * 1024 * 1024 }
|
|
1782
|
-
);
|
|
1783
|
-
|
|
1784
|
-
if (result.error) {
|
|
1785
|
-
throw result.error;
|
|
1786
|
-
}
|
|
1787
|
-
if (result.status !== 0) {
|
|
1788
|
-
const detail = (result.stderr || result.stdout || '').trim();
|
|
1789
|
-
throw new Error(detail || `Local scorer exited ${result.status}`);
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
return normalizeLocalScore(JSON.parse(result.stdout), workspace);
|
|
1793
|
-
}
|
|
1794
|
-
|
|
1795
1736
|
function earnedAgentXp(value) {
|
|
1796
1737
|
return Math.max(0, asNumber(value));
|
|
1797
1738
|
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('child_process');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { taskProofState } = require('./task-proof');
|
|
7
|
+
|
|
8
|
+
const AGENT_CERTIFICATION_REVIEW_PASSES = 2;
|
|
9
|
+
const AUTO_ACCEPT_HIGH_CONFIDENCE_PASSES = 3;
|
|
10
|
+
const DENIED_TAGS = new Set(['billing', 'deploy', 'feedback', 'voice', 'security', 'customer', 'external']);
|
|
11
|
+
|
|
12
|
+
const SIMPLE_VERIFY_TOKEN_RE = /^[a-zA-Z0-9_./:@=+-]+$/;
|
|
13
|
+
const GIT_WORKTREE_PATH_RE = /^[a-zA-Z0-9_./@=+-]+$/;
|
|
14
|
+
const GIT_REV_TOKEN_RE = /^[a-zA-Z0-9_./@=+~^-]+$/;
|
|
15
|
+
|
|
16
|
+
function hasUnsafePathSegment(token) {
|
|
17
|
+
const text = String(token || '');
|
|
18
|
+
return text.startsWith('/')
|
|
19
|
+
|| /^[a-zA-Z]:[\\/]/.test(text)
|
|
20
|
+
|| text.split(/[\\/]+/).includes('..');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function safeVerifyToken(token) {
|
|
24
|
+
return SIMPLE_VERIFY_TOKEN_RE.test(token) && !hasUnsafePathSegment(token);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function safeRelativePathToken(token) {
|
|
28
|
+
const text = String(token || '');
|
|
29
|
+
return Boolean(text)
|
|
30
|
+
&& !text.startsWith('-')
|
|
31
|
+
&& !text.includes(':')
|
|
32
|
+
&& safeVerifyToken(text);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function safeGitWorktreePathToken(token) {
|
|
36
|
+
const text = String(token || '');
|
|
37
|
+
return Boolean(text)
|
|
38
|
+
&& !text.startsWith('-')
|
|
39
|
+
&& !text.includes(':')
|
|
40
|
+
&& GIT_WORKTREE_PATH_RE.test(text)
|
|
41
|
+
&& !text.split(/[\\/]+/).includes('..');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function safeGitRevToken(token) {
|
|
45
|
+
const text = String(token || '');
|
|
46
|
+
return Boolean(text)
|
|
47
|
+
&& !text.startsWith('-')
|
|
48
|
+
&& !text.includes(':')
|
|
49
|
+
&& GIT_REV_TOKEN_RE.test(text)
|
|
50
|
+
&& !text.split(/[\\/]+/).includes('..');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function safeNodePathArgs(args) {
|
|
54
|
+
return args.every(token => safeRelativePathToken(token));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function safeGitDiffCheckArgs(args) {
|
|
58
|
+
return args.length === 0 || (args.length <= 2 && args.every(safeGitRevToken));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isAllowedGitDiffCheck(argv) {
|
|
62
|
+
const [bin, first, second, third, fourth] = argv;
|
|
63
|
+
if (bin !== 'git') return false;
|
|
64
|
+
if (first === 'diff' && second === '--check') {
|
|
65
|
+
return safeGitDiffCheckArgs(argv.slice(3));
|
|
66
|
+
}
|
|
67
|
+
if (first === '-C' && safeGitWorktreePathToken(second) && third === 'diff' && fourth === '--check') {
|
|
68
|
+
return safeGitDiffCheckArgs(argv.slice(5));
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isInsidePath(candidate, root) {
|
|
74
|
+
const relative = path.relative(root, candidate);
|
|
75
|
+
return relative === '' || (relative && !relative.startsWith('..') && !path.isAbsolute(relative));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function validateGitWorktreePath(argv, workspaceRoot) {
|
|
79
|
+
if (!(argv[0] === 'git' && argv[1] === '-C')) return { ok: true };
|
|
80
|
+
const workspace = path.resolve(workspaceRoot || process.cwd());
|
|
81
|
+
const target = path.resolve(workspace, argv[2]);
|
|
82
|
+
const allowedRoot = path.dirname(workspace);
|
|
83
|
+
if (!isInsidePath(target, workspace) && !isInsidePath(target, allowedRoot)) {
|
|
84
|
+
return { ok: false, reason: 'verify_command_not_allowed' };
|
|
85
|
+
}
|
|
86
|
+
if (!fs.existsSync(target)) return { ok: false, reason: 'verify_worktree_missing' };
|
|
87
|
+
return { ok: true };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function reviewPassCount(task) {
|
|
91
|
+
const metadata = task.metadata || {};
|
|
92
|
+
const review = task.review || {};
|
|
93
|
+
const count = Number(metadata.agent_review_pass_count ?? review.agent_review_pass_count);
|
|
94
|
+
return Number.isFinite(count) ? count : 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function isAgentCertified(task) {
|
|
98
|
+
const metadata = task.metadata || {};
|
|
99
|
+
const review = task.review || {};
|
|
100
|
+
const handoff = review.handoff && typeof review.handoff === 'object' ? review.handoff : {};
|
|
101
|
+
return metadata.agent_certified === true
|
|
102
|
+
|| review.agent_certified === true
|
|
103
|
+
|| handoff.native_goal_status === 'agent_certified'
|
|
104
|
+
|| reviewPassCount(task) >= AGENT_CERTIFICATION_REVIEW_PASSES;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function latestProof(task) {
|
|
108
|
+
const metadata = task.metadata || {};
|
|
109
|
+
const review = task.review || {};
|
|
110
|
+
return String(metadata.latest_agent_proof || review.proof || '').trim();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function proofSentences(proof) {
|
|
114
|
+
return String(proof || '')
|
|
115
|
+
.replace(/([.!?])\s+/g, '$1\n')
|
|
116
|
+
.split(/[\n\r]+/)
|
|
117
|
+
.map((sentence) => sentence.trim())
|
|
118
|
+
.filter(Boolean);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function sentenceHasPullRequestReference(sentence) {
|
|
122
|
+
return /\bPR\s*#?\d+\b/i.test(sentence)
|
|
123
|
+
|| /\bpull request\s*#?\d+\b/i.test(sentence)
|
|
124
|
+
|| /\bgithub\.com\/\S+\/pull\/\d+\b/i.test(sentence)
|
|
125
|
+
|| /#\d+\b/.test(sentence);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function sentenceHasUnmergedPullRequestBoundary(sentence) {
|
|
129
|
+
return sentenceHasPullRequestReference(sentence)
|
|
130
|
+
&& (/\bopen\s+draft\b/i.test(sentence)
|
|
131
|
+
|| /\bopen\/draft\b/i.test(sentence)
|
|
132
|
+
|| /\bopen\s+and\s+draft\s*=\s*true\b/i.test(sentence)
|
|
133
|
+
|| /\bdraft\s*=\s*true\b/i.test(sentence)
|
|
134
|
+
|| /\bisDraft\s*[:=]\s*true\b/i.test(sentence)
|
|
135
|
+
|| /\bmergedAt\s*[:=]\s*null\b/i.test(sentence)
|
|
136
|
+
|| /\bclosed\s+(?:with\s+)?mergedAt\s*[:=]\s*null\b/i.test(sentence));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function proofHasUnmergedPullRequestBoundary(proof) {
|
|
140
|
+
return proofSentences(proof).some(sentenceHasUnmergedPullRequestBoundary);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function unmergedPullRequestBoundaryResult(ref, proof) {
|
|
144
|
+
return {
|
|
145
|
+
eligible: false,
|
|
146
|
+
ref,
|
|
147
|
+
reason: 'proof_unmerged_or_draft_pr_boundary',
|
|
148
|
+
next_action: 'revise the task out of Review or narrow the proof to draft/local-proof only before auto-accept',
|
|
149
|
+
proof,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function distinctReviewActors(task) {
|
|
154
|
+
const actors = new Set();
|
|
155
|
+
for (const event of task.events || []) {
|
|
156
|
+
if (!['proof_ready', 'reviewed'].includes(event.event_type)) continue;
|
|
157
|
+
const actor = event.actor || event.payload?.actor;
|
|
158
|
+
if (actor) actors.add(String(actor));
|
|
159
|
+
}
|
|
160
|
+
return actors;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function parseVerifyCommand(verify) {
|
|
164
|
+
const cmd = String(verify || '').trim();
|
|
165
|
+
if (!cmd) return { ok: false, reason: 'no_verify_command' };
|
|
166
|
+
if (/[;&|`$<>\n\r]/.test(cmd)) return { ok: false, reason: 'verify_command_not_allowed' };
|
|
167
|
+
const argv = cmd.split(/\s+/).filter(Boolean);
|
|
168
|
+
if (!argv.length || argv.some((token, index) => {
|
|
169
|
+
if (argv[0] === 'git' && argv[1] === '-C' && index === 2) {
|
|
170
|
+
return !safeGitWorktreePathToken(token);
|
|
171
|
+
}
|
|
172
|
+
if (argv[0] === 'git' && ['diff', '-C'].includes(argv[1]) && index >= (argv[1] === '-C' ? 5 : 3)) {
|
|
173
|
+
return !safeGitRevToken(token);
|
|
174
|
+
}
|
|
175
|
+
return !safeVerifyToken(token);
|
|
176
|
+
})) {
|
|
177
|
+
return { ok: false, reason: 'verify_command_not_allowed' };
|
|
178
|
+
}
|
|
179
|
+
const [bin, first, second] = argv;
|
|
180
|
+
const allowed = (bin === 'npm' && (
|
|
181
|
+
(first === 'test' && argv.length === 2)
|
|
182
|
+
|| (first === 'run' && Boolean(second) && !second.startsWith('-') && argv.length === 3)
|
|
183
|
+
))
|
|
184
|
+
|| (bin === 'node' && (
|
|
185
|
+
(first === '--test' && safeNodePathArgs(argv.slice(2)))
|
|
186
|
+
|| (first === '--check' && argv.length === 3 && safeRelativePathToken(second))
|
|
187
|
+
|| (/^scripts\/[a-zA-Z0-9_./-]+$/.test(first || '') && safeNodePathArgs(argv.slice(1)))
|
|
188
|
+
))
|
|
189
|
+
|| (bin === 'tsc' && argv.length === 1)
|
|
190
|
+
|| isAllowedGitDiffCheck(argv);
|
|
191
|
+
if (!allowed) return { ok: false, reason: 'verify_command_not_allowed' };
|
|
192
|
+
return { ok: true, argv };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function runVerifyCommand(verify, workspaceRoot) {
|
|
196
|
+
const parsed = parseVerifyCommand(verify);
|
|
197
|
+
if (!parsed.ok) return parsed;
|
|
198
|
+
const gitPathCheck = validateGitWorktreePath(parsed.argv, workspaceRoot);
|
|
199
|
+
if (!gitPathCheck.ok) return gitPathCheck;
|
|
200
|
+
const result = spawnSync(parsed.argv[0], parsed.argv.slice(1), {
|
|
201
|
+
cwd: workspaceRoot,
|
|
202
|
+
shell: false,
|
|
203
|
+
encoding: 'utf8',
|
|
204
|
+
timeout: 120000,
|
|
205
|
+
});
|
|
206
|
+
return {
|
|
207
|
+
ok: result.status === 0,
|
|
208
|
+
reason: result.status === 0 ? 'verify_passed' : 'verify_failed',
|
|
209
|
+
status: result.status,
|
|
210
|
+
stderr: String(result.stderr || '').slice(0, 400),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function strictVerifyMissingResult(ref) {
|
|
215
|
+
return {
|
|
216
|
+
eligible: false,
|
|
217
|
+
ref,
|
|
218
|
+
reason: 'strict_verify_missing',
|
|
219
|
+
next_action: 'rerun the review verifier and record a safe metadata.verify command before strict auto-accept',
|
|
220
|
+
review_chat_command: `atris task review-chat ${ref} --as codex-review`,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function evaluateAutoAccept(task, options = {}) {
|
|
225
|
+
const { strictVerify = false, minPasses = AGENT_CERTIFICATION_REVIEW_PASSES } = options;
|
|
226
|
+
const ref = task.display_id || task.legacy_ref || task.id;
|
|
227
|
+
if (task.status !== 'review') return { eligible: false, ref, reason: 'not_in_review' };
|
|
228
|
+
const metadata = task.metadata || {};
|
|
229
|
+
const review = task.review || {};
|
|
230
|
+
const approval = String(review.approval_status || metadata.approval_status || 'pending').toLowerCase();
|
|
231
|
+
if (approval && approval !== 'pending' && approval !== 'agent_certified') {
|
|
232
|
+
return { eligible: false, ref, reason: `approval_${approval}` };
|
|
233
|
+
}
|
|
234
|
+
if (metadata.auto_accepted_at) return { eligible: false, ref, reason: 'already_auto_accepted' };
|
|
235
|
+
|
|
236
|
+
const tag = String(task.tag || '').toLowerCase();
|
|
237
|
+
if (DENIED_TAGS.has(tag)) return { eligible: false, ref, reason: `denied_tag_${tag}` };
|
|
238
|
+
|
|
239
|
+
if (!isAgentCertified(task)) return { eligible: false, ref, reason: 'not_agent_certified' };
|
|
240
|
+
|
|
241
|
+
const passes = reviewPassCount(task);
|
|
242
|
+
if (passes < minPasses) return { eligible: false, ref, reason: 'insufficient_review_passes', passes };
|
|
243
|
+
|
|
244
|
+
const proof = latestProof(task);
|
|
245
|
+
if (proofHasUnmergedPullRequestBoundary(proof)) {
|
|
246
|
+
return unmergedPullRequestBoundaryResult(ref, proof);
|
|
247
|
+
}
|
|
248
|
+
const proofCheck = taskProofState(proof);
|
|
249
|
+
if (!proofCheck.ok) return { eligible: false, ref, reason: proofCheck.reason, proof };
|
|
250
|
+
|
|
251
|
+
const actors = distinctReviewActors(task);
|
|
252
|
+
const multiActor = actors.size >= 2;
|
|
253
|
+
const highConfidence = passes >= AUTO_ACCEPT_HIGH_CONFIDENCE_PASSES;
|
|
254
|
+
if (!multiActor && !highConfidence) {
|
|
255
|
+
return {
|
|
256
|
+
eligible: false,
|
|
257
|
+
ref,
|
|
258
|
+
reason: 'needs_second_reviewer_or_third_pass',
|
|
259
|
+
passes,
|
|
260
|
+
actors: [...actors],
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (strictVerify) {
|
|
265
|
+
const verify = metadata.verify;
|
|
266
|
+
if (!verify) return strictVerifyMissingResult(ref);
|
|
267
|
+
const workspaceRoot = task.workspace_root || process.cwd();
|
|
268
|
+
const verifyResult = runVerifyCommand(verify, workspaceRoot);
|
|
269
|
+
if (!verifyResult.ok) {
|
|
270
|
+
return { eligible: false, ref, reason: verifyResult.reason, verify, ...verifyResult };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
eligible: true,
|
|
276
|
+
ref,
|
|
277
|
+
reason: strictVerify
|
|
278
|
+
? 'certified_strict_verify'
|
|
279
|
+
: (highConfidence ? 'certified_high_confidence' : 'certified_multi_actor'),
|
|
280
|
+
passes,
|
|
281
|
+
actors: [...actors],
|
|
282
|
+
proof,
|
|
283
|
+
policy: strictVerify ? 'strict_verify' : (highConfidence ? '3_passes' : '2_actors_2_passes'),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
module.exports = {
|
|
288
|
+
AGENT_CERTIFICATION_REVIEW_PASSES,
|
|
289
|
+
AUTO_ACCEPT_HIGH_CONFIDENCE_PASSES,
|
|
290
|
+
DENIED_TAGS,
|
|
291
|
+
evaluateAutoAccept,
|
|
292
|
+
parseVerifyCommand,
|
|
293
|
+
runVerifyCommand,
|
|
294
|
+
};
|
package/lib/file-ops.js
CHANGED
|
@@ -87,13 +87,6 @@ function createLogFile(logFile, dateFormatted) {
|
|
|
87
87
|
fs.writeFileSync(logFile, initialContent);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
function getTimeLabel() {
|
|
91
|
-
const now = new Date();
|
|
92
|
-
const hours = String(now.getHours()).padStart(2, '0');
|
|
93
|
-
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
94
|
-
return `${hours}:${minutes}`;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
90
|
// Inbox operations
|
|
98
91
|
function parseInboxItems(content) {
|
|
99
92
|
const match = content.match(/## Inbox\n([\s\S]*?)(?=\n##|\n---|$)/);
|
|
@@ -139,11 +132,6 @@ function addInboxItemToContent(content, id, summary) {
|
|
|
139
132
|
return replaceInboxSection(content, updatedItems);
|
|
140
133
|
}
|
|
141
134
|
|
|
142
|
-
function removeInboxItemFromContent(content, id) {
|
|
143
|
-
const items = parseInboxItems(content).filter((item) => item.id !== id);
|
|
144
|
-
return replaceInboxSection(content, items);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
135
|
function getNextInboxId(content) {
|
|
148
136
|
const items = parseInboxItems(content);
|
|
149
137
|
if (items.length === 0) return 1;
|
|
@@ -158,185 +146,13 @@ function addInboxIdea(logFile, summary) {
|
|
|
158
146
|
return nextId;
|
|
159
147
|
}
|
|
160
148
|
|
|
161
|
-
// Completion operations
|
|
162
|
-
function parseCompletionItems(content) {
|
|
163
|
-
const match = content.match(/## Completed ✅\n([\s\S]*?)(?=\n##|\n---|$)/);
|
|
164
|
-
if (!match) {
|
|
165
|
-
return [];
|
|
166
|
-
}
|
|
167
|
-
const body = match[1];
|
|
168
|
-
const lines = body.split('\n');
|
|
169
|
-
const items = [];
|
|
170
|
-
lines.forEach((line) => {
|
|
171
|
-
const trimmed = line.trim();
|
|
172
|
-
if (!trimmed) return;
|
|
173
|
-
if (trimmed.startsWith('(Empty')) return;
|
|
174
|
-
const parsed = trimmed.match(/^- \*\*C(\d+):\*\*\s*(.+)$|^- \*\*C(\d+):\s+(.+)$/);
|
|
175
|
-
if (parsed) {
|
|
176
|
-
const id = parseInt(parsed[1] || parsed[3], 10);
|
|
177
|
-
const text = parsed[2] || parsed[4];
|
|
178
|
-
items.push({ id, text, line: trimmed });
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
return items;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function replaceCompletedSection(content, items) {
|
|
185
|
-
const regex = /(## Completed ✅\n)([\s\S]*?)(\n---|\n##|$)/;
|
|
186
|
-
if (!regex.test(content)) {
|
|
187
|
-
const lines = items.length ? items.map((item) => item.line).join('\n') : '';
|
|
188
|
-
return `${content}\n\n## Completed ✅\n\n${lines}\n`;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return content.replace(regex, (match, header, body, suffix) => {
|
|
192
|
-
const inner = items.length
|
|
193
|
-
? `\n${items.map((item) => item.line).join('\n')}\n`
|
|
194
|
-
: '\n';
|
|
195
|
-
return `${header}${inner}${suffix}`;
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function addCompletionItemToContent(content, id, summary) {
|
|
200
|
-
const items = parseCompletionItems(content).filter((item) => item.id !== id);
|
|
201
|
-
const newItem = { id, text: summary, line: `- **C${id}:** ${summary}` };
|
|
202
|
-
const updatedItems = [...items, newItem];
|
|
203
|
-
return replaceCompletedSection(content, updatedItems);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function getNextCompletionId(content) {
|
|
207
|
-
const items = parseCompletionItems(content);
|
|
208
|
-
if (items.length === 0) return 1;
|
|
209
|
-
return items.reduce((max, item) => (item.id > max ? item.id : max), 0) + 1;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Notes operations
|
|
213
|
-
function insertIntoNotesSection(content, block) {
|
|
214
|
-
const regex = /(## Notes\n)([\s\S]*?)(\n---|\n##|$)/;
|
|
215
|
-
const match = content.match(regex);
|
|
216
|
-
if (!match) {
|
|
217
|
-
return `${content}\n\n## Notes\n\n${block}\n`;
|
|
218
|
-
}
|
|
219
|
-
const header = match[1];
|
|
220
|
-
const body = match[2];
|
|
221
|
-
const suffix = match[3];
|
|
222
|
-
const trimmedBody = body.replace(/\s*$/, '');
|
|
223
|
-
const newBody = trimmedBody
|
|
224
|
-
? `${trimmedBody}\n\n${block}\n`
|
|
225
|
-
: `\n${block}\n`;
|
|
226
|
-
return content.replace(regex, `${header}${newBody}${suffix}`);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function recordBrainstormSession(
|
|
230
|
-
logFile,
|
|
231
|
-
sourceLabel,
|
|
232
|
-
topic,
|
|
233
|
-
desiredOutcome,
|
|
234
|
-
keyQuestions,
|
|
235
|
-
focusAreas,
|
|
236
|
-
constraints,
|
|
237
|
-
references,
|
|
238
|
-
tonePreference,
|
|
239
|
-
nextSteps,
|
|
240
|
-
sessionSummary
|
|
241
|
-
) {
|
|
242
|
-
let content = fs.readFileSync(logFile, 'utf8');
|
|
243
|
-
const lines = [
|
|
244
|
-
`### Brainstorm Session — ${getTimeLabel()}`,
|
|
245
|
-
`**Source:** ${sourceLabel}`,
|
|
246
|
-
`**Topic:** ${topic}`,
|
|
247
|
-
];
|
|
248
|
-
if (desiredOutcome) {
|
|
249
|
-
lines.push(`**User Story / Desired Outcome:** ${desiredOutcome}`);
|
|
250
|
-
}
|
|
251
|
-
if (tonePreference) {
|
|
252
|
-
lines.push(`**Vibe / Feelings:** ${tonePreference}`);
|
|
253
|
-
}
|
|
254
|
-
if (keyQuestions && keyQuestions.length > 0) {
|
|
255
|
-
lines.push('**Key Questions:**');
|
|
256
|
-
keyQuestions.forEach((item) => lines.push(`- ${item}`));
|
|
257
|
-
}
|
|
258
|
-
if (focusAreas && focusAreas.length > 0) {
|
|
259
|
-
lines.push('**Focus Areas:**');
|
|
260
|
-
focusAreas.forEach((item) => lines.push(`- ${item}`));
|
|
261
|
-
}
|
|
262
|
-
if (constraints) {
|
|
263
|
-
lines.push(`**Constraints:** ${constraints}`);
|
|
264
|
-
}
|
|
265
|
-
if (references) {
|
|
266
|
-
lines.push(`**Context / References:** ${references}`);
|
|
267
|
-
}
|
|
268
|
-
if (sessionSummary) {
|
|
269
|
-
lines.push(`**Session Summary:** ${sessionSummary}`);
|
|
270
|
-
}
|
|
271
|
-
if (nextSteps && nextSteps.length > 0) {
|
|
272
|
-
lines.push('**Next Steps:**');
|
|
273
|
-
nextSteps.forEach((item) => lines.push(`- ${item}`));
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const block = lines.join('\n');
|
|
277
|
-
content = insertIntoNotesSection(content, block);
|
|
278
|
-
fs.writeFileSync(logFile, content);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function recordAutopilotVision(logFile, sourceLabel, summary, successCriteria, riskNotes) {
|
|
282
|
-
const content = fs.readFileSync(logFile, 'utf8');
|
|
283
|
-
const lines = [
|
|
284
|
-
`### Autopilot Vision — ${getTimeLabel()}`,
|
|
285
|
-
`**Source:** ${sourceLabel}`,
|
|
286
|
-
`**Summary:** ${summary}`,
|
|
287
|
-
'**Success Criteria:**',
|
|
288
|
-
...successCriteria.map((item) => `- ${item}`),
|
|
289
|
-
];
|
|
290
|
-
if (riskNotes && riskNotes.trim()) {
|
|
291
|
-
lines.push(`**Risks / Notes:** ${riskNotes}`);
|
|
292
|
-
}
|
|
293
|
-
const block = lines.join('\n');
|
|
294
|
-
const updated = insertIntoNotesSection(content, block);
|
|
295
|
-
fs.writeFileSync(logFile, updated);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function recordAutopilotIteration(logFile, iteration, result, notes) {
|
|
299
|
-
const content = fs.readFileSync(logFile, 'utf8');
|
|
300
|
-
const lines = [
|
|
301
|
-
`### Autopilot Iteration ${iteration} — ${getTimeLabel()}`,
|
|
302
|
-
`**Validator Result:** ${result}`,
|
|
303
|
-
];
|
|
304
|
-
if (notes && notes.trim()) {
|
|
305
|
-
lines.push(`**Notes:** ${notes}`);
|
|
306
|
-
}
|
|
307
|
-
const block = lines.join('\n');
|
|
308
|
-
const updated = insertIntoNotesSection(content, block);
|
|
309
|
-
fs.writeFileSync(logFile, updated);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
function recordAutopilotSuccess(logFile, inboxId, summary) {
|
|
313
|
-
let content = fs.readFileSync(logFile, 'utf8');
|
|
314
|
-
if (typeof inboxId === 'number' && !Number.isNaN(inboxId)) {
|
|
315
|
-
content = removeInboxItemFromContent(content, inboxId);
|
|
316
|
-
}
|
|
317
|
-
const nextId = getNextCompletionId(content);
|
|
318
|
-
content = addCompletionItemToContent(content, nextId, `Autopilot — ${summary}`);
|
|
319
|
-
fs.writeFileSync(logFile, content);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
149
|
module.exports = {
|
|
323
150
|
getLogPath,
|
|
324
151
|
ensureLogDirectory,
|
|
325
152
|
createLogFile,
|
|
326
|
-
getTimeLabel,
|
|
327
153
|
parseInboxItems,
|
|
328
154
|
replaceInboxSection,
|
|
329
155
|
addInboxItemToContent,
|
|
330
|
-
removeInboxItemFromContent,
|
|
331
156
|
getNextInboxId,
|
|
332
157
|
addInboxIdea,
|
|
333
|
-
parseCompletionItems,
|
|
334
|
-
replaceCompletedSection,
|
|
335
|
-
addCompletionItemToContent,
|
|
336
|
-
getNextCompletionId,
|
|
337
|
-
insertIntoNotesSection,
|
|
338
|
-
recordBrainstormSession,
|
|
339
|
-
recordAutopilotVision,
|
|
340
|
-
recordAutopilotIteration,
|
|
341
|
-
recordAutopilotSuccess,
|
|
342
158
|
};
|