@orion-agents/orion-code 0.1.0 → 0.1.2
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 +107 -67
- package/README.zh-CN.md +192 -217
- package/dist/cli.js +23 -21
- package/dist/cli.js.map +1 -1
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +397 -212
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/target-command.d.ts.map +1 -1
- package/dist/commands/target-command.js +43 -9
- package/dist/commands/target-command.js.map +1 -1
- package/dist/commands/types.d.ts +33 -0
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js +5 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +2 -2
- package/dist/core/agent.js.map +1 -1
- package/dist/framework/external-assertion.d.ts +65 -0
- package/dist/framework/external-assertion.d.ts.map +1 -0
- package/dist/framework/external-assertion.js +571 -0
- package/dist/framework/external-assertion.js.map +1 -0
- package/dist/framework/index.d.ts +6 -4
- package/dist/framework/index.d.ts.map +1 -1
- package/dist/framework/index.js +4 -1
- package/dist/framework/index.js.map +1 -1
- package/dist/framework/query.d.ts +18 -1
- package/dist/framework/query.d.ts.map +1 -1
- package/dist/framework/query.js +65 -28
- package/dist/framework/query.js.map +1 -1
- package/dist/framework/tool-scheduler.d.ts +2 -0
- package/dist/framework/tool-scheduler.d.ts.map +1 -1
- package/dist/framework/tool-scheduler.js +23 -9
- package/dist/framework/tool-scheduler.js.map +1 -1
- package/dist/framework/tool-serializer.d.ts.map +1 -1
- package/dist/framework/tool-serializer.js +20 -4
- package/dist/framework/tool-serializer.js.map +1 -1
- package/dist/framework/tool.d.ts +11 -5
- package/dist/framework/tool.d.ts.map +1 -1
- package/dist/framework/tool.js.map +1 -1
- package/dist/harness/context-harness.d.ts.map +1 -1
- package/dist/harness/context-harness.js +4 -3
- package/dist/harness/context-harness.js.map +1 -1
- package/dist/harness/drift-guard.d.ts.map +1 -1
- package/dist/harness/drift-guard.js +14 -5
- package/dist/harness/drift-guard.js.map +1 -1
- package/dist/harness/safety.d.ts.map +1 -1
- package/dist/harness/safety.js +19 -3
- package/dist/harness/safety.js.map +1 -1
- package/dist/init.d.ts +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +115 -34
- package/dist/init.js.map +1 -1
- package/dist/ink-ui/screens/ReplScreen.d.ts.map +1 -1
- package/dist/ink-ui/screens/ReplScreen.js +4 -1
- package/dist/ink-ui/screens/ReplScreen.js.map +1 -1
- package/dist/memory/storage.d.ts.map +1 -1
- package/dist/memory/storage.js +1 -0
- package/dist/memory/storage.js.map +1 -1
- package/dist/memory/store.d.ts.map +1 -1
- package/dist/memory/store.js +11 -5
- package/dist/memory/store.js.map +1 -1
- package/dist/migration/command.d.ts.map +1 -1
- package/dist/migration/command.js +6 -1
- package/dist/migration/command.js.map +1 -1
- package/dist/migration/migrate.d.ts.map +1 -1
- package/dist/migration/migrate.js +4 -1
- package/dist/migration/migrate.js.map +1 -1
- package/dist/print-ui/launch.d.ts +4 -0
- package/dist/print-ui/launch.d.ts.map +1 -1
- package/dist/print-ui/launch.js +102 -9
- package/dist/print-ui/launch.js.map +1 -1
- package/dist/product/identity.d.ts +2 -2
- package/dist/product/identity.d.ts.map +1 -1
- package/dist/product/identity.js +2 -2
- package/dist/product/identity.js.map +1 -1
- package/dist/product/index.d.ts +2 -1
- package/dist/product/index.d.ts.map +1 -1
- package/dist/product/index.js +5 -1
- package/dist/product/index.js.map +1 -1
- package/dist/product/version.d.ts +12 -0
- package/dist/product/version.d.ts.map +1 -0
- package/dist/product/version.js +31 -0
- package/dist/product/version.js.map +1 -0
- package/dist/runtime/agent-runtime-controller.d.ts +49 -0
- package/dist/runtime/agent-runtime-controller.d.ts.map +1 -1
- package/dist/runtime/agent-runtime-controller.js +1048 -81
- package/dist/runtime/agent-runtime-controller.js.map +1 -1
- package/dist/runtime/agent-runtime-protocol.d.ts +14 -2
- package/dist/runtime/agent-runtime-protocol.d.ts.map +1 -1
- package/dist/runtime/agent-runtime-protocol.js +18 -0
- package/dist/runtime/agent-runtime-protocol.js.map +1 -1
- package/dist/runtime/chat-controller.d.ts +6 -0
- package/dist/runtime/chat-controller.d.ts.map +1 -1
- package/dist/runtime/chat-controller.js +226 -20
- package/dist/runtime/chat-controller.js.map +1 -1
- package/dist/runtime/goals/accounting.d.ts +28 -0
- package/dist/runtime/goals/accounting.d.ts.map +1 -1
- package/dist/runtime/goals/accounting.js +69 -0
- package/dist/runtime/goals/accounting.js.map +1 -1
- package/dist/runtime/goals/completion-audit.d.ts +13 -8
- package/dist/runtime/goals/completion-audit.d.ts.map +1 -1
- package/dist/runtime/goals/completion-audit.js +598 -16
- package/dist/runtime/goals/completion-audit.js.map +1 -1
- package/dist/runtime/goals/coordinator.d.ts +44 -5
- package/dist/runtime/goals/coordinator.d.ts.map +1 -1
- package/dist/runtime/goals/coordinator.js +1124 -58
- package/dist/runtime/goals/coordinator.js.map +1 -1
- package/dist/runtime/goals/evidence.d.ts +37 -0
- package/dist/runtime/goals/evidence.d.ts.map +1 -0
- package/dist/runtime/goals/evidence.js +300 -0
- package/dist/runtime/goals/evidence.js.map +1 -0
- package/dist/runtime/goals/presentation.d.ts +4 -0
- package/dist/runtime/goals/presentation.d.ts.map +1 -0
- package/dist/runtime/goals/presentation.js +50 -0
- package/dist/runtime/goals/presentation.js.map +1 -0
- package/dist/runtime/goals/prompt.d.ts.map +1 -1
- package/dist/runtime/goals/prompt.js +38 -8
- package/dist/runtime/goals/prompt.js.map +1 -1
- package/dist/runtime/goals/tools.d.ts +18 -1
- package/dist/runtime/goals/tools.d.ts.map +1 -1
- package/dist/runtime/goals/tools.js +308 -28
- package/dist/runtime/goals/tools.js.map +1 -1
- package/dist/runtime/goals/types.d.ts +241 -7
- package/dist/runtime/goals/types.d.ts.map +1 -1
- package/dist/runtime/goals/types.js +8 -2
- package/dist/runtime/goals/types.js.map +1 -1
- package/dist/runtime/subagents/child-executor-guard.d.ts +12 -4
- package/dist/runtime/subagents/child-executor-guard.d.ts.map +1 -1
- package/dist/runtime/subagents/child-executor-guard.js +397 -61
- package/dist/runtime/subagents/child-executor-guard.js.map +1 -1
- package/dist/runtime/subagents/presets.d.ts +4 -5
- package/dist/runtime/subagents/presets.d.ts.map +1 -1
- package/dist/runtime/subagents/presets.js +18 -33
- package/dist/runtime/subagents/presets.js.map +1 -1
- package/dist/runtime/subagents/production.d.ts +3 -1
- package/dist/runtime/subagents/production.d.ts.map +1 -1
- package/dist/runtime/subagents/production.js +37 -7
- package/dist/runtime/subagents/production.js.map +1 -1
- package/dist/runtime/subagents/runner.d.ts.map +1 -1
- package/dist/runtime/subagents/runner.js +24 -3
- package/dist/runtime/subagents/runner.js.map +1 -1
- package/dist/runtime/subagents/runtime-integration.d.ts +3 -0
- package/dist/runtime/subagents/runtime-integration.d.ts.map +1 -1
- package/dist/runtime/subagents/runtime-integration.js +18 -7
- package/dist/runtime/subagents/runtime-integration.js.map +1 -1
- package/dist/runtime/subagents/supervisor.d.ts +4 -4
- package/dist/runtime/subagents/supervisor.d.ts.map +1 -1
- package/dist/runtime/subagents/supervisor.js +39 -19
- package/dist/runtime/subagents/supervisor.js.map +1 -1
- package/dist/runtime/subagents/types.d.ts +7 -0
- package/dist/runtime/subagents/types.d.ts.map +1 -1
- package/dist/runtime/subagents/types.js +12 -1
- package/dist/runtime/subagents/types.js.map +1 -1
- package/dist/runtime/ui-events.d.ts +10 -0
- package/dist/runtime/ui-events.d.ts.map +1 -1
- package/dist/runtime/ui-events.js +5 -5
- package/dist/runtime/ui-events.js.map +1 -1
- package/dist/runtime/ui-view-model.d.ts +3 -3
- package/dist/runtime/ui-view-model.d.ts.map +1 -1
- package/dist/runtime/ui-view-model.js +11 -4
- package/dist/runtime/ui-view-model.js.map +1 -1
- package/dist/services/config.d.ts +18 -7
- package/dist/services/config.d.ts.map +1 -1
- package/dist/services/config.js +79 -57
- package/dist/services/config.js.map +1 -1
- package/dist/services/doctor.d.ts.map +1 -1
- package/dist/services/doctor.js +164 -18
- package/dist/services/doctor.js.map +1 -1
- package/dist/services/global-config.d.ts +1 -1
- package/dist/services/global-config.d.ts.map +1 -1
- package/dist/services/goal-storage.d.ts +9 -4
- package/dist/services/goal-storage.d.ts.map +1 -1
- package/dist/services/goal-storage.js +1113 -75
- package/dist/services/goal-storage.js.map +1 -1
- package/dist/services/llm.d.ts +19 -0
- package/dist/services/llm.d.ts.map +1 -1
- package/dist/services/llm.js +153 -20
- package/dist/services/llm.js.map +1 -1
- package/dist/services/model-registry.d.ts +1 -1
- package/dist/services/model-registry.d.ts.map +1 -1
- package/dist/services/model-registry.js +69 -19
- package/dist/services/model-registry.js.map +1 -1
- package/dist/services/provider-resilience/coordinator.d.ts +2 -1
- package/dist/services/provider-resilience/coordinator.d.ts.map +1 -1
- package/dist/services/provider-resilience/coordinator.js +7 -6
- package/dist/services/provider-resilience/coordinator.js.map +1 -1
- package/dist/services/provider-resilience/error-classifier.d.ts.map +1 -1
- package/dist/services/provider-resilience/error-classifier.js +67 -17
- package/dist/services/provider-resilience/error-classifier.js.map +1 -1
- package/dist/services/provider-resilience/types.d.ts +1 -1
- package/dist/services/provider-resilience/types.d.ts.map +1 -1
- package/dist/services/provider-resilience/types.js.map +1 -1
- package/dist/services/redaction.d.ts.map +1 -1
- package/dist/services/redaction.js +9 -1
- package/dist/services/redaction.js.map +1 -1
- package/dist/services/session-storage.d.ts +12 -1
- package/dist/services/session-storage.d.ts.map +1 -1
- package/dist/services/session-storage.js +23 -7
- package/dist/services/session-storage.js.map +1 -1
- package/dist/services/storage-maintenance.d.ts +73 -3
- package/dist/services/storage-maintenance.d.ts.map +1 -1
- package/dist/services/storage-maintenance.js +771 -104
- package/dist/services/storage-maintenance.js.map +1 -1
- package/dist/services/task-manager.d.ts.map +1 -1
- package/dist/services/task-manager.js +2 -2
- package/dist/services/task-manager.js.map +1 -1
- package/dist/services/web-search-adapters.d.ts.map +1 -1
- package/dist/services/web-search-adapters.js +89 -49
- package/dist/services/web-search-adapters.js.map +1 -1
- package/dist/services/web-search-mcp.d.ts.map +1 -1
- package/dist/services/web-search-mcp.js +34 -37
- package/dist/services/web-search-mcp.js.map +1 -1
- package/dist/services/workspace-state.d.ts +4 -0
- package/dist/services/workspace-state.d.ts.map +1 -1
- package/dist/services/workspace-state.js +30 -9
- package/dist/services/workspace-state.js.map +1 -1
- package/dist/terminal-ui/completion.js +1 -1
- package/dist/terminal-ui/completion.js.map +1 -1
- package/dist/terminal-ui/launch.d.ts +3 -0
- package/dist/terminal-ui/launch.d.ts.map +1 -1
- package/dist/terminal-ui/launch.js +26 -98
- package/dist/terminal-ui/launch.js.map +1 -1
- package/dist/tools/git.d.ts.map +1 -1
- package/dist/tools/git.js +275 -27
- package/dist/tools/git.js.map +1 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +29 -10
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/mcp.js +20 -12
- package/dist/tools/mcp.js.map +1 -1
- package/dist/tools/web.d.ts.map +1 -1
- package/dist/tools/web.js +38 -14
- package/dist/tools/web.js.map +1 -1
- package/dist/tui-ui/layout.d.ts.map +1 -1
- package/dist/tui-ui/layout.js +36 -23
- package/dist/tui-ui/layout.js.map +1 -1
- package/dist/tui-ui/pickers.d.ts.map +1 -1
- package/dist/tui-ui/pickers.js +5 -4
- package/dist/tui-ui/pickers.js.map +1 -1
- package/dist/tui-ui/runner.d.ts +1 -0
- package/dist/tui-ui/runner.d.ts.map +1 -1
- package/dist/tui-ui/runner.js +15 -17
- package/dist/tui-ui/runner.js.map +1 -1
- package/dist/tui-ui/state.d.ts +6 -0
- package/dist/tui-ui/state.d.ts.map +1 -1
- package/dist/tui-ui/state.js +61 -30
- package/dist/tui-ui/state.js.map +1 -1
- package/package.json +14 -9
|
@@ -1,28 +1,609 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* v0.2.24 — Goal Completion & Blocked Audit.
|
|
4
|
-
*
|
|
5
|
-
* Two-phase terminal-state audit. Model requests complete/blocked via
|
|
6
|
-
* update_goal tool; Coordinator runs audit after turn persistence.
|
|
7
|
-
*/
|
|
2
|
+
/** Goal completion and blocked audits. Terminal state changes are runtime-owned. */
|
|
8
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.hasUnnegatedActionMatch = hasUnnegatedActionMatch;
|
|
5
|
+
exports.criterionRequiresExternalCompletionEvidence = criterionRequiresExternalCompletionEvidence;
|
|
9
6
|
exports.auditCompletion = auditCompletion;
|
|
10
7
|
exports.auditBlocked = auditBlocked;
|
|
11
8
|
exports.blockerFingerprint = blockerFingerprint;
|
|
12
9
|
exports.blockersMatch = blockersMatch;
|
|
10
|
+
const evidence_1 = require("./evidence");
|
|
11
|
+
const WORKSPACE_BOUND_EVIDENCE = new Set(['test', 'build', 'lint', 'file', 'runtime']);
|
|
12
|
+
const MAX_EVIDENCE_TRUNCATION_COUNT = 1000000000;
|
|
13
|
+
function evidenceLedgerTruncationRequirement(truncation, objectiveRevision) {
|
|
14
|
+
if (truncation === undefined)
|
|
15
|
+
return null;
|
|
16
|
+
const fields = Object.keys(truncation);
|
|
17
|
+
const counts = [
|
|
18
|
+
truncation.droppedPassed,
|
|
19
|
+
truncation.droppedFailed,
|
|
20
|
+
truncation.droppedInconclusive,
|
|
21
|
+
];
|
|
22
|
+
const valid = fields.length === 4 &&
|
|
23
|
+
fields.every(field => ['objectiveRevision', 'droppedPassed', 'droppedFailed', 'droppedInconclusive'].includes(field)) &&
|
|
24
|
+
Number.isSafeInteger(truncation.objectiveRevision) &&
|
|
25
|
+
truncation.objectiveRevision === objectiveRevision &&
|
|
26
|
+
counts.every(count => Number.isSafeInteger(count) && count >= 0 && count <= MAX_EVIDENCE_TRUNCATION_COUNT) &&
|
|
27
|
+
counts.some(count => count > 0);
|
|
28
|
+
if (!valid) {
|
|
29
|
+
return ('Completion is fail-closed because evidence-ledger truncation metadata is invalid or ' +
|
|
30
|
+
'does not belong to the current objective revision. Use /target edit to start a new ' +
|
|
31
|
+
'objective epoch, then rerun every criterion-specific verification.');
|
|
32
|
+
}
|
|
33
|
+
if (truncation.droppedFailed === 0 && truncation.droppedInconclusive === 0)
|
|
34
|
+
return null;
|
|
35
|
+
return (`Completion is fail-closed because the bounded evidence ledger discarded ` +
|
|
36
|
+
`${truncation.droppedFailed} failed and ${truncation.droppedInconclusive} ` +
|
|
37
|
+
`inconclusive record(s) for objective revision ${truncation.objectiveRevision}. ` +
|
|
38
|
+
'Use /target edit to start a new objective epoch, then rerun every criterion-specific verification.');
|
|
39
|
+
}
|
|
40
|
+
const SEMANTIC_STOP_WORDS = new Set([
|
|
41
|
+
'a',
|
|
42
|
+
'an',
|
|
43
|
+
'and',
|
|
44
|
+
'are',
|
|
45
|
+
'be',
|
|
46
|
+
'been',
|
|
47
|
+
'complete',
|
|
48
|
+
'completed',
|
|
49
|
+
'criterion',
|
|
50
|
+
'derived',
|
|
51
|
+
'evidence',
|
|
52
|
+
'file',
|
|
53
|
+
'for',
|
|
54
|
+
'from',
|
|
55
|
+
'goal',
|
|
56
|
+
'in',
|
|
57
|
+
'is',
|
|
58
|
+
'of',
|
|
59
|
+
'on',
|
|
60
|
+
'or',
|
|
61
|
+
'pass',
|
|
62
|
+
'passed',
|
|
63
|
+
'passes',
|
|
64
|
+
'primary',
|
|
65
|
+
'requirement',
|
|
66
|
+
'result',
|
|
67
|
+
'runtime',
|
|
68
|
+
'suite',
|
|
69
|
+
'test',
|
|
70
|
+
'the',
|
|
71
|
+
'to',
|
|
72
|
+
'verify',
|
|
73
|
+
'verified',
|
|
74
|
+
'verification',
|
|
75
|
+
'with',
|
|
76
|
+
]);
|
|
77
|
+
function canonicalToken(value) {
|
|
78
|
+
const lower = value.toLowerCase();
|
|
79
|
+
const actionStem = lower.match(/^(publish|release|merge|create|open|submit|raise|push)(?:s|es|ed|d|ing)?$/u);
|
|
80
|
+
if (actionStem)
|
|
81
|
+
return actionStem[1];
|
|
82
|
+
if (/^[a-z0-9_-]+$/u.test(lower) && lower.length > 4 && lower.endsWith('s')) {
|
|
83
|
+
return lower.slice(0, -1);
|
|
84
|
+
}
|
|
85
|
+
return lower;
|
|
86
|
+
}
|
|
87
|
+
function semanticTokens(value) {
|
|
88
|
+
const result = new Set();
|
|
89
|
+
for (const raw of value.match(/[\p{L}\p{N}][\p{L}\p{N}_-]*/gu) ?? []) {
|
|
90
|
+
const token = canonicalToken(raw);
|
|
91
|
+
if (token.length >= 3 && !SEMANTIC_STOP_WORDS.has(token))
|
|
92
|
+
result.add(token);
|
|
93
|
+
if (/\p{Script=Han}/u.test(raw)) {
|
|
94
|
+
const characters = Array.from(raw);
|
|
95
|
+
for (let index = 0; index < characters.length - 1; index += 1) {
|
|
96
|
+
result.add(`${characters[index]}${characters[index + 1]}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
const ACTION_NEGATION_PATTERN = /\b(?:do\s+not|don't|must\s+not|should\s+not|cannot|can't|never|not\b(?!\s+only\b)|without)\b|不要|禁止|不得|不允许|尚未|不(?:发布|上线|创建|提交|打开|合并|部署|删除|推送|上传)|未(?:发布|上线|创建|提交|打开|合并|部署|删除|推送|上传)/iu;
|
|
103
|
+
// A contrast or a strong sentence boundary ends the scope of an earlier
|
|
104
|
+
// negation. Ordinary conjunctions deliberately do not: "do not publish and
|
|
105
|
+
// merge" negates both actions, while "do not merge, but publish" resets at
|
|
106
|
+
// "but" and preserves the positive publish action.
|
|
107
|
+
const NEGATION_SCOPE_RESET_PATTERN = /[.;;。\n]+|\b(?:but|however|yet|then)\b|(?:但是|但|不过|然而|然后)/giu;
|
|
108
|
+
function negationScopeStart(value, actionIndex) {
|
|
109
|
+
const matcher = new RegExp(NEGATION_SCOPE_RESET_PATTERN.source, NEGATION_SCOPE_RESET_PATTERN.flags);
|
|
110
|
+
let start = 0;
|
|
111
|
+
let match;
|
|
112
|
+
while ((match = matcher.exec(value)) && match.index < actionIndex) {
|
|
113
|
+
start = matcher.lastIndex;
|
|
114
|
+
}
|
|
115
|
+
return start;
|
|
116
|
+
}
|
|
117
|
+
/** Return true when at least one pattern match is outside a local negation scope. */
|
|
118
|
+
function hasUnnegatedActionMatch(value, pattern) {
|
|
119
|
+
const flags = `${pattern.flags.replace(/[gy]/gu, '')}g`;
|
|
120
|
+
const matcher = new RegExp(pattern.source, flags);
|
|
121
|
+
let match;
|
|
122
|
+
while ((match = matcher.exec(value))) {
|
|
123
|
+
const scope = value.slice(negationScopeStart(value, match.index), matcher.lastIndex);
|
|
124
|
+
if (!ACTION_NEGATION_PATTERN.test(scope))
|
|
125
|
+
return true;
|
|
126
|
+
if (match[0].length === 0)
|
|
127
|
+
matcher.lastIndex += 1;
|
|
128
|
+
}
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
function criterionCompletionActions(value) {
|
|
132
|
+
return evidence_1.EXTERNAL_COMPLETION_ACTION_RULES.filter(rule => hasUnnegatedActionMatch(value, rule.criterionPattern)).map(rule => rule.action);
|
|
133
|
+
}
|
|
134
|
+
function completedEvidenceActions(evidence, allowConfirmedAction) {
|
|
135
|
+
if (evidence.externalAssertion?.status === 'passed') {
|
|
136
|
+
return [evidence.externalAssertion.action];
|
|
137
|
+
}
|
|
138
|
+
const evidenceText = `${evidence.subject} ${evidence.sourceRef}`;
|
|
139
|
+
return evidence_1.EXTERNAL_COMPLETION_ACTION_RULES.filter(rule => hasUnnegatedActionMatch(evidenceText, allowConfirmedAction ? rule.criterionPattern : rule.completedEvidencePattern)).map(rule => rule.action);
|
|
140
|
+
}
|
|
141
|
+
function normalizeVersion(value) {
|
|
142
|
+
return value.toLowerCase().replace(/^v/u, '');
|
|
143
|
+
}
|
|
144
|
+
function exactRequiredVersion(statement) {
|
|
145
|
+
const packageVersion = statement.match(/(?:^|[\s("'`])(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+@(v?\d+\.\d+\.\d+(?:[-+][0-9a-z.-]+)?)/iu)?.[1];
|
|
146
|
+
if (packageVersion)
|
|
147
|
+
return normalizeVersion(packageVersion);
|
|
148
|
+
const versions = statement.match(/\bv?\d+\.\d+\.\d+(?:[-+][0-9a-z.-]+)?\b/giu) ?? [];
|
|
149
|
+
return versions.length > 0 ? normalizeVersion(versions[versions.length - 1]) : undefined;
|
|
150
|
+
}
|
|
151
|
+
function exactRequiredVersions(statement) {
|
|
152
|
+
return [
|
|
153
|
+
...new Set((statement.match(/\bv?\d+\.\d+\.\d+(?:[-+][0-9a-z.-]+)?\b/giu) ?? []).map(normalizeVersion)),
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
function exactRequiredPackages(statement) {
|
|
157
|
+
const packages = new Set();
|
|
158
|
+
for (const match of statement.matchAll(/@[a-z0-9._-]+\/[a-z0-9._-]+/giu)) {
|
|
159
|
+
packages.add(match[0].toLowerCase());
|
|
160
|
+
}
|
|
161
|
+
for (const match of statement.matchAll(/(?:^|[\s("'`])((?:@[a-z0-9._-]+\/)?[a-z0-9._-]+)@v?\d+\.\d+\.\d+(?:[-+][0-9a-z.-]+)?/giu)) {
|
|
162
|
+
packages.add(match[1].toLowerCase());
|
|
163
|
+
}
|
|
164
|
+
// A release criterion commonly names unversioned packages as a conjunction,
|
|
165
|
+
// for example "Publish orion-code and orion-sdk to npm". Those names do not
|
|
166
|
+
// carry the scoped-package or @version signals above, but they are still
|
|
167
|
+
// exact targets and must not be collapsed into one generic npm action.
|
|
168
|
+
for (const match of statement.matchAll(/\b(?:publish|release)(?:es|ed|d|ing)?\s+(.+?)\s+(?:to|on)\s+(?:the\s+)?npm\b/giu)) {
|
|
169
|
+
const clause = match[1].replace(/\b(?:both|the)\b/giu, ' ').replace(/\bpackages?\b/giu, ' ');
|
|
170
|
+
for (const rawCandidate of clause.split(/\s*(?:,|\band\b|&)\s*/iu)) {
|
|
171
|
+
const candidate = rawCandidate.trim().replace(/^[`'"]+|[`'".]+$/gu, '');
|
|
172
|
+
const packageMatch = candidate.match(/^((?:@[a-z0-9._-]+\/)?[a-z0-9][a-z0-9._-]*)(?:@v?\d+\.\d+\.\d+(?:[-+][0-9a-z.-]+)?)?$/iu);
|
|
173
|
+
if (packageMatch)
|
|
174
|
+
packages.add(packageMatch[1].toLowerCase());
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return [...packages];
|
|
178
|
+
}
|
|
179
|
+
function exactRequiredNpmTargets(statement) {
|
|
180
|
+
const targets = new Map();
|
|
181
|
+
for (const match of statement.matchAll(/(?:^|[\s("'`])((?:@[a-z0-9._-]+\/)?[a-z0-9._-]+)@(v?\d+\.\d+\.\d+(?:[-+][0-9a-z.-]+)?)/giu)) {
|
|
182
|
+
const packageName = match[1].toLowerCase();
|
|
183
|
+
targets.set(packageName, { packageName, version: normalizeVersion(match[2]) });
|
|
184
|
+
}
|
|
185
|
+
const sharedVersion = exactRequiredVersion(statement);
|
|
186
|
+
for (const packageName of exactRequiredPackages(statement)) {
|
|
187
|
+
if (!targets.has(packageName)) {
|
|
188
|
+
targets.set(packageName, { packageName, version: sharedVersion });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return [...targets.values()];
|
|
192
|
+
}
|
|
193
|
+
function formatRequiredNpmTarget(target) {
|
|
194
|
+
return target.version ? `${target.packageName}@${target.version}` : target.packageName;
|
|
195
|
+
}
|
|
196
|
+
function exactRequiredGithubPullRequests(statement) {
|
|
197
|
+
const targets = new Map();
|
|
198
|
+
const sharedRepository = exactRequiredGithubRepository(statement);
|
|
199
|
+
const targetToken = String.raw `(?:[a-z0-9_.-]+\/[a-z0-9_.-]+)?#?\d+`;
|
|
200
|
+
const targetList = String.raw `${targetToken}(?:\s*(?:,\s*(?:and\s+)?|and\s+|&\s*)${targetToken})*`;
|
|
201
|
+
const pullRequestPattern = /\b(?:prs?|pull[\s_-]?requests?)\b/giu;
|
|
202
|
+
for (const pullRequestMatch of statement.matchAll(pullRequestPattern)) {
|
|
203
|
+
const matchIndex = pullRequestMatch.index ?? 0;
|
|
204
|
+
const before = statement.slice(0, matchIndex);
|
|
205
|
+
const after = statement.slice(matchIndex + pullRequestMatch[0].length);
|
|
206
|
+
const adjacentLists = [
|
|
207
|
+
after.match(new RegExp(`^\\s*(${targetList})`, 'iu'))?.[1],
|
|
208
|
+
before.match(new RegExp(`(${targetList})\\s*$`, 'iu'))?.[1],
|
|
209
|
+
];
|
|
210
|
+
for (const list of adjacentLists) {
|
|
211
|
+
if (!list)
|
|
212
|
+
continue;
|
|
213
|
+
for (const match of list.matchAll(/(?:([a-z0-9_.-]+\/[a-z0-9_.-]+))?#?(\d+)\b/giu)) {
|
|
214
|
+
const repository = match[1]?.toLowerCase() ?? sharedRepository;
|
|
215
|
+
const prNumber = Number(match[2]);
|
|
216
|
+
targets.set(`${repository ?? '*'}#${prNumber}`, { repository, prNumber });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return [...targets.values()];
|
|
221
|
+
}
|
|
222
|
+
function exactRequiredGithubReleaseRepositories(statement) {
|
|
223
|
+
const repositories = new Set();
|
|
224
|
+
const statementWithoutGithubUrls = statement.replace(/(?:https?:\/\/)?github\.com\/([a-z0-9_.-]+\/[a-z0-9_.-]+)/giu, (_url, repository) => {
|
|
225
|
+
repositories.add(repository.toLowerCase());
|
|
226
|
+
return ' ';
|
|
227
|
+
});
|
|
228
|
+
for (const match of statementWithoutGithubUrls.matchAll(/\b([a-z0-9_.-]+\/[a-z0-9_.-]+)\b/giu)) {
|
|
229
|
+
repositories.add(match[1].toLowerCase());
|
|
230
|
+
}
|
|
231
|
+
return [...repositories];
|
|
232
|
+
}
|
|
233
|
+
function exactRequiredGithubRepository(statement) {
|
|
234
|
+
return (statement.match(/github\.com\/([a-z0-9_.-]+\/[a-z0-9_.-]+)/iu)?.[1] ??
|
|
235
|
+
statement.match(/\b(?:repo(?:sitory)?|github)\s+[`'"]?([a-z0-9_.-]+\/[a-z0-9_.-]+)/iu)?.[1] ??
|
|
236
|
+
statement.match(/\b([a-z0-9_.-]+\/[a-z0-9_.-]+)#\d+\b/iu)?.[1])?.toLowerCase();
|
|
237
|
+
}
|
|
238
|
+
function exactRequiredBranch(statement) {
|
|
239
|
+
return (statement.match(/\bbranch\s+[`'"]?([a-z0-9._\/-]+)/iu)?.[1] ??
|
|
240
|
+
statement.match(/\bpush(?:ed|ing)?\s+(?:the\s+)?[`'"]?([a-z0-9._\/-]+)\s+branch\b/iu)?.[1]);
|
|
241
|
+
}
|
|
242
|
+
function exactRequiredRemote(statement) {
|
|
243
|
+
return (statement.match(/\bremote\s+[`'"]?([a-z0-9._\/-]+)/iu)?.[1] ??
|
|
244
|
+
statement.match(/\bpush(?:ed|ing)?\s+to\s+[`'"]?([a-z0-9._\/-]+)/iu)?.[1]);
|
|
245
|
+
}
|
|
246
|
+
function externalAssertionMatchesCriterion(assertion, statement) {
|
|
247
|
+
if (assertion.status !== 'passed' || !assertion.details)
|
|
248
|
+
return false;
|
|
249
|
+
const statementLower = statement.toLowerCase();
|
|
250
|
+
if (assertion.details.kind === 'npm') {
|
|
251
|
+
const npmDetails = assertion.details;
|
|
252
|
+
if (assertion.provider !== 'npm')
|
|
253
|
+
return false;
|
|
254
|
+
if (assertion.action === 'publish' && npmDetails.field !== 'publish')
|
|
255
|
+
return false;
|
|
256
|
+
if (assertion.action === 'registry' && npmDetails.field === 'publish')
|
|
257
|
+
return false;
|
|
258
|
+
const requiredTargets = exactRequiredNpmTargets(statement);
|
|
259
|
+
if (requiredTargets.length > 0 &&
|
|
260
|
+
!requiredTargets.some(target => npmDetails.packageName.toLowerCase() === target.packageName &&
|
|
261
|
+
(!target.version || normalizeVersion(npmDetails.version ?? '') === target.version))) {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
if (assertion.details.kind === 'github_pr') {
|
|
267
|
+
if (assertion.provider !== 'github')
|
|
268
|
+
return false;
|
|
269
|
+
if (assertion.action === 'merge' && assertion.details.state !== 'MERGED')
|
|
270
|
+
return false;
|
|
271
|
+
if (assertion.action === 'pull_request' && assertion.details.state !== 'OPEN')
|
|
272
|
+
return false;
|
|
273
|
+
const prDetails = assertion.details;
|
|
274
|
+
const requiredPullRequests = exactRequiredGithubPullRequests(statement);
|
|
275
|
+
if (requiredPullRequests.length > 0 &&
|
|
276
|
+
!requiredPullRequests.some(target => target.prNumber === prDetails.prNumber &&
|
|
277
|
+
(!target.repository || prDetails.repository?.toLowerCase() === target.repository))) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
return !/\bnpm\b/iu.test(statement);
|
|
281
|
+
}
|
|
282
|
+
if (assertion.details.kind === 'github_release') {
|
|
283
|
+
if (assertion.provider !== 'github' ||
|
|
284
|
+
assertion.action !== 'publish' ||
|
|
285
|
+
assertion.details.isDraft !== false ||
|
|
286
|
+
!assertion.details.publishedAt) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
if (/\bnpm\b/iu.test(statement))
|
|
290
|
+
return false;
|
|
291
|
+
const requiredRepositories = exactRequiredGithubReleaseRepositories(statement);
|
|
292
|
+
const requiredVersions = exactRequiredVersions(statement);
|
|
293
|
+
// Pairing several repositories with several tags is ambiguous without an
|
|
294
|
+
// explicit structured contract. Fail closed and require separate atomic
|
|
295
|
+
// criteria instead of accepting a cross-paired set of assertions.
|
|
296
|
+
if (requiredRepositories.length > 1 && requiredVersions.length > 1)
|
|
297
|
+
return false;
|
|
298
|
+
if (requiredRepositories.length > 0 &&
|
|
299
|
+
!requiredRepositories.includes(assertion.details.repository?.toLowerCase() ?? '')) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
if (requiredVersions.length > 0 &&
|
|
303
|
+
!requiredVersions.includes(normalizeVersion(assertion.details.tagName ?? ''))) {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
if (assertion.details.kind === 'git_push') {
|
|
309
|
+
if (assertion.provider !== 'git' || assertion.action !== 'push')
|
|
310
|
+
return false;
|
|
311
|
+
const requiredBranch = exactRequiredBranch(statement);
|
|
312
|
+
if (requiredBranch && assertion.details.branch !== requiredBranch)
|
|
313
|
+
return false;
|
|
314
|
+
const requiredRemote = exactRequiredRemote(statement);
|
|
315
|
+
if (requiredRemote && assertion.details.remote !== requiredRemote)
|
|
316
|
+
return false;
|
|
317
|
+
return !statementLower.includes('npm');
|
|
318
|
+
}
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
function criterionRequiresExternalCompletionEvidence(statement) {
|
|
322
|
+
return criterionCompletionActions(statement).length > 0;
|
|
323
|
+
}
|
|
324
|
+
function completionActionLabel(action) {
|
|
325
|
+
return evidence_1.EXTERNAL_COMPLETION_ACTION_RULES.find(rule => rule.action === action)?.label ?? action;
|
|
326
|
+
}
|
|
327
|
+
function evidenceMatchesCriterion(evidence, criterion, allCriteria) {
|
|
328
|
+
const requiredActions = criterionCompletionActions(criterion.statement);
|
|
329
|
+
const requiresExternalCompletion = requiredActions.length > 0;
|
|
330
|
+
const allowedKind = requiresExternalCompletion
|
|
331
|
+
? evidence.kind === 'external' || evidence.kind === 'user'
|
|
332
|
+
: criterion.requiredEvidenceKinds.includes(evidence.kind);
|
|
333
|
+
if (!allowedKind) {
|
|
334
|
+
return { matched: false, reason: 'kind_mismatch', sharedTokens: [], coveredActions: [] };
|
|
335
|
+
}
|
|
336
|
+
if (!evidence.sourceRef.trim() || !evidence.subject.trim()) {
|
|
337
|
+
return { matched: false, reason: 'missing_metadata', sharedTokens: [], coveredActions: [] };
|
|
338
|
+
}
|
|
339
|
+
if (evidence.kind === 'user') {
|
|
340
|
+
const matched = evidence.sourceRef === 'user:/target-confirm' && evidence.subject.includes(criterion.id);
|
|
341
|
+
const coveredActions = completedEvidenceActions(evidence, true).filter(action => requiredActions.includes(action));
|
|
342
|
+
const coversRequiredAction = requiredActions.length === 0 || coveredActions.length > 0;
|
|
343
|
+
return {
|
|
344
|
+
matched: matched && coversRequiredAction,
|
|
345
|
+
reason: matched && !coversRequiredAction
|
|
346
|
+
? 'missing_completion_action'
|
|
347
|
+
: matched
|
|
348
|
+
? 'matched_criterion_id'
|
|
349
|
+
: 'user_confirmation_mismatch',
|
|
350
|
+
sharedTokens: [],
|
|
351
|
+
coveredActions,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
if (requiresExternalCompletion &&
|
|
355
|
+
evidence.kind === 'external' &&
|
|
356
|
+
evidence.sourceRef.startsWith('tool:') &&
|
|
357
|
+
!evidence.externalAssertion) {
|
|
358
|
+
return {
|
|
359
|
+
matched: false,
|
|
360
|
+
reason: 'untrusted_completion_source',
|
|
361
|
+
sharedTokens: [],
|
|
362
|
+
coveredActions: [],
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
if (requiresExternalCompletion &&
|
|
366
|
+
evidence.externalAssertion &&
|
|
367
|
+
!externalAssertionMatchesCriterion(evidence.externalAssertion, criterion.statement)) {
|
|
368
|
+
return {
|
|
369
|
+
matched: false,
|
|
370
|
+
reason: 'external_assertion_mismatch',
|
|
371
|
+
sharedTokens: [],
|
|
372
|
+
coveredActions: [],
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
const coveredActions = completedEvidenceActions(evidence, false).filter(action => requiredActions.includes(action));
|
|
376
|
+
if (requiresExternalCompletion && coveredActions.length === 0) {
|
|
377
|
+
return {
|
|
378
|
+
matched: false,
|
|
379
|
+
reason: 'missing_completion_action',
|
|
380
|
+
sharedTokens: [],
|
|
381
|
+
coveredActions: [],
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
if (evidence.subject.toLowerCase().includes(criterion.id.toLowerCase()) ||
|
|
385
|
+
evidence.sourceRef.toLowerCase().includes(criterion.id.toLowerCase())) {
|
|
386
|
+
return { matched: true, reason: 'matched_criterion_id', sharedTokens: [], coveredActions };
|
|
387
|
+
}
|
|
388
|
+
const criterionTokens = semanticTokens(criterion.statement);
|
|
389
|
+
if (criterionTokens.size === 0) {
|
|
390
|
+
return { matched: false, reason: 'no_semantic_overlap', sharedTokens: [], coveredActions };
|
|
391
|
+
}
|
|
392
|
+
const evidenceTokens = semanticTokens(evidence.subject);
|
|
393
|
+
const sharedTokens = [...criterionTokens].filter(token => evidenceTokens.has(token));
|
|
394
|
+
if (sharedTokens.length === 0) {
|
|
395
|
+
return { matched: false, reason: 'no_semantic_overlap', sharedTokens: [], coveredActions };
|
|
396
|
+
}
|
|
397
|
+
// Terms repeated across criteria identify the overall goal topic, not the
|
|
398
|
+
// individual requirement. For example, "package smoke" cannot distinguish
|
|
399
|
+
// a registry-entry check from a binary-start check. At least one overlapping
|
|
400
|
+
// term must be unique to this criterion within the current contract.
|
|
401
|
+
const otherCriterionTokens = new Set(allCriteria
|
|
402
|
+
.filter(item => item.id !== criterion.id)
|
|
403
|
+
.flatMap(item => [...semanticTokens(item.statement)]));
|
|
404
|
+
const discriminativeTokens = sharedTokens.filter(token => !otherCriterionTokens.has(token));
|
|
405
|
+
if (discriminativeTokens.length > 0) {
|
|
406
|
+
return {
|
|
407
|
+
matched: true,
|
|
408
|
+
reason: 'matched_discriminative_token',
|
|
409
|
+
sharedTokens: discriminativeTokens,
|
|
410
|
+
coveredActions,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
return { matched: false, reason: 'ambiguous_shared_tokens', sharedTokens, coveredActions };
|
|
414
|
+
}
|
|
415
|
+
function evidenceIsFresh(evidence, input, now, requireExternalExpiry = false) {
|
|
416
|
+
if (evidence.goalId !== input.goalId)
|
|
417
|
+
return false;
|
|
418
|
+
if (evidence.goalRevision > input.goalRevision)
|
|
419
|
+
return false;
|
|
420
|
+
if (evidence.objectiveRevision !== input.contract.objectiveRevision)
|
|
421
|
+
return false;
|
|
422
|
+
if (evidence.capturedAt > now)
|
|
423
|
+
return false;
|
|
424
|
+
if (requireExternalExpiry &&
|
|
425
|
+
evidence.kind === 'external' &&
|
|
426
|
+
evidence.expiresAt === undefined &&
|
|
427
|
+
// Legacy registry adapters emit a typed registry source instead of an
|
|
428
|
+
// explicit expiry. Keep those records only when they were captured for
|
|
429
|
+
// this completion request; generic external/tool claims remain fail-closed.
|
|
430
|
+
!(evidence.sourceRef.startsWith('registry:') && evidence.capturedAt >= input.requestedAt))
|
|
431
|
+
return false;
|
|
432
|
+
if (evidence.expiresAt !== undefined && evidence.expiresAt <= now)
|
|
433
|
+
return false;
|
|
434
|
+
if (evidence.externalAssertion) {
|
|
435
|
+
const observedAt = evidence.externalAssertion.observedAt;
|
|
436
|
+
// Bind freshness to the time of the external observation, not merely to
|
|
437
|
+
// when a ledger record was created. This prevents a stale assertion from
|
|
438
|
+
// being copied into a fresh-looking evidence envelope.
|
|
439
|
+
if (observedAt > evidence.capturedAt || observedAt > now)
|
|
440
|
+
return false;
|
|
441
|
+
if (requireExternalExpiry && now - observedAt >= 5 * 60000)
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
if (WORKSPACE_BOUND_EVIDENCE.has(evidence.kind)) {
|
|
445
|
+
if (!input.workspaceFingerprint || !evidence.workspaceFingerprint)
|
|
446
|
+
return false;
|
|
447
|
+
if (evidence.workspaceFingerprint !== input.workspaceFingerprint)
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
if (input.workspaceFingerprint &&
|
|
451
|
+
evidence.workspaceFingerprint &&
|
|
452
|
+
evidence.workspaceFingerprint !== input.workspaceFingerprint)
|
|
453
|
+
return false;
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
13
456
|
function auditCompletion(input) {
|
|
14
|
-
const now = Date.now();
|
|
15
|
-
const
|
|
457
|
+
const now = input.now ?? Date.now();
|
|
458
|
+
const evidenceById = new Map(input.evidenceLedger.map(record => [record.id, record]));
|
|
459
|
+
const criterionResults = input.contract.successCriteria.map(criterion => {
|
|
460
|
+
const records = criterion.evidenceRefs
|
|
461
|
+
.map(ref => evidenceById.get(ref))
|
|
462
|
+
.filter((record) => Boolean(record));
|
|
463
|
+
const mappedMatches = records.map(record => ({
|
|
464
|
+
record,
|
|
465
|
+
match: evidenceMatchesCriterion(record, criterion, input.contract.successCriteria),
|
|
466
|
+
}));
|
|
467
|
+
const mappedRelevant = mappedMatches.filter(item => item.match.matched);
|
|
468
|
+
const requiredActions = criterionCompletionActions(criterion.statement);
|
|
469
|
+
const requireExternalExpiry = requiredActions.length > 0;
|
|
470
|
+
const relevantMatches = mappedRelevant.filter(item => item.record.result === 'passed' &&
|
|
471
|
+
evidenceIsFresh(item.record, input, now, requireExternalExpiry));
|
|
472
|
+
const relevant = relevantMatches.map(item => item.record);
|
|
473
|
+
const hasTrustedUserConfirmation = relevant.some(record => record.kind === 'user');
|
|
474
|
+
// Audit the whole ledger, not only model-selected refs. Otherwise a model
|
|
475
|
+
// can omit a relevant failed record and cherry-pick a passing record.
|
|
476
|
+
const failedRecords = input.evidenceLedger.filter(record => record.result === 'failed' &&
|
|
477
|
+
evidenceMatchesCriterion(record, criterion, input.contract.successCriteria).matched &&
|
|
478
|
+
evidenceIsFresh(record, input, now, requireExternalExpiry));
|
|
479
|
+
const failed = failedRecords.length > 0;
|
|
480
|
+
const coveredActions = new Set(relevantMatches.flatMap(item => item.match.coveredActions));
|
|
481
|
+
const missingActions = requiredActions.filter(action => !coveredActions.has(action));
|
|
482
|
+
const requiredNpmTargets = exactRequiredNpmTargets(criterion.statement);
|
|
483
|
+
const npmAssertions = relevantMatches
|
|
484
|
+
.map(item => item.record.externalAssertion)
|
|
485
|
+
.filter((assertion) => assertion?.details?.kind === 'npm');
|
|
486
|
+
// A structured npm assertion proves exactly one package target. When a
|
|
487
|
+
// criterion names several packages, require a fresh matching assertion
|
|
488
|
+
// for every target instead of letting any one package close the whole
|
|
489
|
+
// conjunction.
|
|
490
|
+
const missingNpmTargets = !hasTrustedUserConfirmation &&
|
|
491
|
+
requiredNpmTargets.length > 0 &&
|
|
492
|
+
(npmAssertions.length > 0 || requiredNpmTargets.length > 1)
|
|
493
|
+
? requiredNpmTargets.filter(target => !npmAssertions.some(assertion => assertion.details.packageName.toLowerCase() === target.packageName &&
|
|
494
|
+
(!target.version ||
|
|
495
|
+
normalizeVersion(assertion.details.version ?? '') === target.version)))
|
|
496
|
+
: [];
|
|
497
|
+
const requiredPullRequests = exactRequiredGithubPullRequests(criterion.statement);
|
|
498
|
+
const pullRequestAssertions = relevantMatches
|
|
499
|
+
.map(item => item.record.externalAssertion)
|
|
500
|
+
.filter((assertion) => assertion?.details?.kind === 'github_pr');
|
|
501
|
+
const missingPullRequests = !hasTrustedUserConfirmation &&
|
|
502
|
+
requiredPullRequests.length > 0 &&
|
|
503
|
+
(pullRequestAssertions.length > 0 || requiredPullRequests.length > 1)
|
|
504
|
+
? requiredPullRequests.filter(target => !pullRequestAssertions.some(assertion => assertion.details.prNumber === target.prNumber &&
|
|
505
|
+
(!target.repository ||
|
|
506
|
+
assertion.details.repository?.toLowerCase() === target.repository)))
|
|
507
|
+
: [];
|
|
508
|
+
const requiresGithubReleaseTargets = requiredActions.includes('publish') &&
|
|
509
|
+
requiredNpmTargets.length === 0 &&
|
|
510
|
+
!/\bnpm\b/iu.test(criterion.statement);
|
|
511
|
+
const requiredReleaseRepositories = requiresGithubReleaseTargets
|
|
512
|
+
? exactRequiredGithubReleaseRepositories(criterion.statement)
|
|
513
|
+
: [];
|
|
514
|
+
const releaseAssertions = relevantMatches
|
|
515
|
+
.map(item => item.record.externalAssertion)
|
|
516
|
+
.filter((assertion) => assertion?.details?.kind === 'github_release');
|
|
517
|
+
const missingReleaseRepositories = !hasTrustedUserConfirmation &&
|
|
518
|
+
requiredReleaseRepositories.length > 0 &&
|
|
519
|
+
(releaseAssertions.length > 0 || requiredReleaseRepositories.length > 1)
|
|
520
|
+
? requiredReleaseRepositories.filter(repository => !releaseAssertions.some(assertion => assertion.details.repository?.toLowerCase() === repository))
|
|
521
|
+
: [];
|
|
522
|
+
const requiredReleaseVersions = requiresGithubReleaseTargets
|
|
523
|
+
? exactRequiredVersions(criterion.statement)
|
|
524
|
+
: [];
|
|
525
|
+
const missingReleaseVersions = !hasTrustedUserConfirmation &&
|
|
526
|
+
requiredReleaseVersions.length > 0 &&
|
|
527
|
+
(releaseAssertions.length > 0 || requiredReleaseVersions.length > 1)
|
|
528
|
+
? requiredReleaseVersions.filter(version => !releaseAssertions.some(assertion => normalizeVersion(assertion.details.tagName ?? '') === version))
|
|
529
|
+
: [];
|
|
530
|
+
const missingExternalTargets = [
|
|
531
|
+
...missingNpmTargets.map(formatRequiredNpmTarget),
|
|
532
|
+
...missingPullRequests.map(target => `${target.repository ?? 'requested repository'}#${target.prNumber}`),
|
|
533
|
+
...missingReleaseRepositories.map(repository => `GitHub release ${repository}`),
|
|
534
|
+
...missingReleaseVersions.map(version => `GitHub release tag ${version}`),
|
|
535
|
+
];
|
|
536
|
+
const passed = relevant.length > 0 &&
|
|
537
|
+
missingActions.length === 0 &&
|
|
538
|
+
missingExternalTargets.length === 0 &&
|
|
539
|
+
!failed;
|
|
540
|
+
const stale = mappedRelevant.some(item => !evidenceIsFresh(item.record, input, now, requireExternalExpiry));
|
|
541
|
+
const irrelevant = records.length > 0 && mappedRelevant.length === 0;
|
|
542
|
+
const ambiguousTokens = [
|
|
543
|
+
...new Set(mappedMatches
|
|
544
|
+
.filter(item => item.match.reason === 'ambiguous_shared_tokens')
|
|
545
|
+
.flatMap(item => item.match.sharedTokens)),
|
|
546
|
+
];
|
|
547
|
+
const status = passed
|
|
548
|
+
? 'passed'
|
|
549
|
+
: failed
|
|
550
|
+
? 'failed'
|
|
551
|
+
: stale
|
|
552
|
+
? 'stale'
|
|
553
|
+
: 'pending';
|
|
554
|
+
let reason;
|
|
555
|
+
if (!passed) {
|
|
556
|
+
reason =
|
|
557
|
+
status === 'failed'
|
|
558
|
+
? `[${criterion.id}] ${criterion.statement}: relevant verification failed (${failedRecords
|
|
559
|
+
.slice(0, 3)
|
|
560
|
+
.map(record => record.id)
|
|
561
|
+
.join(', ')}). Rerun the criterion-specific verification after fixing the failure.`
|
|
562
|
+
: status === 'stale'
|
|
563
|
+
? `[${criterion.id}] ${criterion.statement}: referenced evidence is stale or lacks the required workspace fingerprint. Rerun a criterion-specific ${criterion.requiredEvidenceKinds.join('/')} check.`
|
|
564
|
+
: missingExternalTargets.length > 0
|
|
565
|
+
? `[${criterion.id}] ${criterion.statement}: fresh external evidence is missing for required target(s): ${missingExternalTargets.join(', ')}.`
|
|
566
|
+
: missingActions.length > 0
|
|
567
|
+
? `[${criterion.id}] ${criterion.statement}: completion requires fresh external or user evidence covering the completed action/status (${missingActions.map(completionActionLabel).join(', ')}). Local runtime, build, file, or version output cannot prove this external state.`
|
|
568
|
+
: irrelevant && ambiguousTokens.length > 0
|
|
569
|
+
? `[${criterion.id}] ${criterion.statement}: mapped evidence overlaps only on terms shared by multiple criteria (${ambiguousTokens.slice(0, 4).join(', ')}). Include a criterion-specific term or the exact criterion id in the evidence subject.`
|
|
570
|
+
: irrelevant
|
|
571
|
+
? `[${criterion.id}] ${criterion.statement}: mapped evidence is not semantically related to this criterion. Run a specifically named ${criterion.requiredEvidenceKinds.join('/')} check.`
|
|
572
|
+
: `[${criterion.id}] ${criterion.statement}: fresh relevant evidence is required. Run a criterion-specific ${criterion.requiredEvidenceKinds.join('/')} check.`;
|
|
573
|
+
}
|
|
574
|
+
return {
|
|
575
|
+
criterionId: criterion.id,
|
|
576
|
+
passed,
|
|
577
|
+
status,
|
|
578
|
+
evidenceRefs: relevant.map(record => record.id),
|
|
579
|
+
reason,
|
|
580
|
+
};
|
|
581
|
+
});
|
|
582
|
+
const remainingRequirements = criterionResults
|
|
583
|
+
.filter(result => !result.passed)
|
|
584
|
+
.map(result => result.reason ?? `Criterion ${result.criterionId} is not verified.`);
|
|
585
|
+
const truncationRequirement = evidenceLedgerTruncationRequirement(input.evidenceLedgerTruncation, input.contract.objectiveRevision);
|
|
586
|
+
if (truncationRequirement)
|
|
587
|
+
remainingRequirements.push(truncationRequirement);
|
|
588
|
+
const passed = criterionResults.length > 0 && remainingRequirements.length === 0;
|
|
16
589
|
return {
|
|
17
|
-
requestedAt:
|
|
590
|
+
requestedAt: input.requestedAt,
|
|
18
591
|
auditedAt: now,
|
|
19
592
|
passed,
|
|
20
|
-
verificationSummary: input.verificationSummary ||
|
|
21
|
-
|
|
22
|
-
|
|
593
|
+
verificationSummary: input.verificationSummary.trim() ||
|
|
594
|
+
(passed ? 'All success criteria have fresh runtime evidence.' : 'Completion audit failed.'),
|
|
595
|
+
remainingRequirements,
|
|
596
|
+
evidenceRefs: [...new Set(criterionResults.flatMap(result => result.evidenceRefs))],
|
|
597
|
+
criterionResults,
|
|
23
598
|
};
|
|
24
599
|
}
|
|
25
600
|
function auditBlocked(input) {
|
|
601
|
+
if (!['user_input', 'permission', 'external_state'].includes(input.blocker.category)) {
|
|
602
|
+
return { allowed: false, reason: 'Blocker category is not eligible for terminal blocked.' };
|
|
603
|
+
}
|
|
604
|
+
if (input.blocker.retryable !== false) {
|
|
605
|
+
return { allowed: false, reason: 'Retryable blockers cannot become terminal blocked.' };
|
|
606
|
+
}
|
|
26
607
|
if (input.blocker.consecutiveTurns < 3) {
|
|
27
608
|
return {
|
|
28
609
|
allowed: false,
|
|
@@ -35,14 +616,15 @@ function auditBlocked(input) {
|
|
|
35
616
|
reason: 'Progress was made in recent turns; blocking not justified.',
|
|
36
617
|
};
|
|
37
618
|
}
|
|
38
|
-
return {
|
|
619
|
+
return {
|
|
620
|
+
allowed: true,
|
|
621
|
+
reason: `Blocker persisted for ${input.blocker.consecutiveTurns} consecutive turns with no progress.`,
|
|
622
|
+
};
|
|
39
623
|
}
|
|
40
624
|
function blockerFingerprint(category, resource, reason) {
|
|
41
625
|
return `${category}:${resource}:${reason}`;
|
|
42
626
|
}
|
|
43
627
|
function blockersMatch(a, fingerprint) {
|
|
44
|
-
|
|
45
|
-
return false;
|
|
46
|
-
return a.fingerprint === fingerprint;
|
|
628
|
+
return Boolean(a && a.fingerprint === fingerprint);
|
|
47
629
|
}
|
|
48
630
|
//# sourceMappingURL=completion-audit.js.map
|