@ornexus/neocortex 4.60.55 → 4.60.57
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/dist/sbom.cdx.json +23 -23
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/package.json +5 -2
- package/packages/client/dist/commands/invoke.d.ts +32 -0
- package/packages/client/dist/commands/invoke.js +69 -60
- package/packages/client/dist/git/direct-yolo-checkpoint.d.ts +6 -0
- package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -4
- package/packages/client/dist/private-execution/clean-baseline.d.ts +12 -0
- package/packages/client/dist/private-execution/clean-baseline.js +2 -0
- package/packages/client/dist/private-execution/codex-executor.d.ts +65 -0
- package/packages/client/dist/private-execution/codex-executor.js +2 -0
- package/packages/client/dist/private-execution/delivery.d.ts +74 -0
- package/packages/client/dist/private-execution/delivery.js +3 -0
- package/packages/client/dist/private-execution/isolated-admission.d.ts +13 -0
- package/packages/client/dist/private-execution/isolated-admission.js +2 -0
- package/packages/client/dist/private-execution/isolated-session.d.ts +41 -0
- package/packages/client/dist/private-execution/isolated-session.js +2 -0
- package/packages/client/dist/private-execution/isolated-workspace.d.ts +13 -0
- package/packages/client/dist/private-execution/isolated-workspace.js +1 -0
- package/packages/client/dist/private-execution/root-checkpoint.d.ts +15 -0
- package/packages/client/dist/private-execution/root-checkpoint.js +1 -0
- package/packages/client/dist/resolvers/remote-resolver.js +1 -1
- package/packages/client/dist/yolo/change-evidence-collector.d.ts +6 -0
- package/packages/client/dist/yolo/change-evidence-collector.js +1 -0
- package/packages/client/dist/yolo/spec-envelope-collector.d.ts +11 -0
- package/packages/client/dist/yolo/spec-envelope-collector.js +5 -0
- package/packages/shared/dist/frontmatter-context-contract.js +326 -0
- package/packages/shared/dist/yolo/change-evidence.js +44 -0
- package/packages/shared/dist/yolo/spec-envelope.js +161 -0
- package/targets-stubs/antigravity/gemini.md +1 -1
- package/targets-stubs/antigravity/skill/SKILL.md +1 -1
- package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex-root.md +2 -2
- package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
- package/targets-stubs/claude-code/neocortex.md +2 -2
- package/targets-stubs/codex/AGENTS.md +1 -1
- package/targets-stubs/cursor/agent.md +2 -2
- package/targets-stubs/gemini-cli/agent.md +2 -2
- package/targets-stubs/opencode/neocortex-root.md +1 -1
- package/targets-stubs/vscode/neocortex.agent.md +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
import{ResolverMode as y,NoOpCache as d}from"../types/index.js";import{InMemoryAssetCache as m}from"../cache/in-memory-asset-cache.js";const
|
|
1
|
+
import{ResolverMode as y,NoOpCache as d}from"../types/index.js";import{InMemoryAssetCache as m}from"../cache/in-memory-asset-cache.js";import{CLIENT_VERSION as C}from"../version.js";const f=3e4,w=3,p=1e3,T=1e4;class n extends Error{statusCode;endpoint;constructor(t,e,r){super(t),this.statusCode=e,this.endpoint=r,this.name="RemoteResolverError"}}class b{mode=y.REMOTE;serverUrl;licenseKey;timeout;retryCount;cache;assetCache;licenseClient;constructor(t){this.serverUrl=t.serverUrl.replace(/\/+$/,""),this.licenseKey=t.licenseKey,this.timeout=t.timeout??f,this.retryCount=t.retryCount??w,this.cache=t.cacheProvider??new d,this.licenseClient=t.licenseClient??null,this.assetCache=new m}async resolveStep(t,e){const r=`step:${t}`;return this.fetchWithInMemoryCache(r,{method:"GET",path:`/api/v1/steps/${encodeURIComponent(t)}`})}async resolveSkill(t,e){const r=`skill:${t}`;return this.fetchWithInMemoryCache(r,{method:"GET",path:`/api/v1/skills/${encodeURIComponent(t)}`})}async resolveStandard(t){const e=`standard:${t}`;return this.fetchWithInMemoryCache(e,{method:"GET",path:`/api/v1/standards/${encodeURIComponent(t)}`})}async resolveRegistry(){return this.fetchWithCacheFallback("registry",{method:"GET",path:"/api/v1/registry"})}async assemblePrompt(t,e){return this.fetchWithRetry({method:"POST",path:"/api/v1/prompts/assemble",body:{stepId:t,context:e}})}async isAvailable(){try{const t=new AbortController,e=setTimeout(()=>t.abort(),5e3),r=await fetch(`${this.serverUrl}/api/v1/health`,{method:"GET",headers:await this.buildHeaders(),signal:t.signal});return clearTimeout(e),r.ok}catch{return!1}}async dispose(){await this.cache.clear(),await this.assetCache.clear()}async buildHeaders(){const t={"Content-Type":"application/json","X-Client-Version":C};if(this.licenseClient){const e=await this.licenseClient.getToken();e&&(t.Authorization=`Bearer ${e}`)}return t}async fetchWithRetry(t){let e,r=!1;for(let o=0;o<=this.retryCount;o++)try{const a=new AbortController,c=setTimeout(()=>a.abort(),this.timeout),i=`${this.serverUrl}${t.path}`,h={method:t.method,headers:await this.buildHeaders(),signal:a.signal};t.body&&(h.body=JSON.stringify(t.body));const s=await fetch(i,h);if(clearTimeout(c),!s.ok){const l=await s.text().catch(()=>"Unknown error");if(s.status===401&&!r&&this.licenseClient){if(r=!0,await this.licenseClient.forceRefresh()){o--;continue}throw new n(`API error: ${s.status} ${s.statusText} - ${l}`,s.status,t.path)}throw s.status>=400&&s.status<500&&s.status!==429?new n(`API error: ${s.status} ${s.statusText} - ${l}`,s.status,t.path):new n(`Server error: ${s.status} ${s.statusText}`,s.status,t.path)}return await s.json()}catch(a){if(e=a instanceof Error?a:new Error(String(a)),a instanceof n&&a.statusCode&&a.statusCode>=400&&a.statusCode<500&&a.statusCode!==429)throw a;if(o<this.retryCount){const c=Math.min(p*Math.pow(2,o),T),i=Math.random()*c*.25;await this.sleep(c+i)}}throw e??new n("All retry attempts failed",void 0,t.path)}async fetchWithCacheFallback(t,e){try{const r=await this.fetchWithRetry(e);return this.cache.set(t,JSON.stringify(r)).catch(()=>{}),r}catch(r){const o=await this.cache.get(t);if(o!==null)try{return JSON.parse(o)}catch{}throw r}}async fetchWithInMemoryCache(t,e){try{const r=await this.fetchWithRetry(e);return this.assetCache.set(t,JSON.stringify(r)).catch(()=>{}),r}catch(r){const o=await this.assetCache.get(t);if(o!==null)try{return JSON.parse(o)}catch{}throw r}}sleep(t){return new Promise(e=>setTimeout(e,t))}}export{b as RemoteResolver,n as RemoteResolverError};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type YoloChangeEvidenceV1 } from '../../../shared/dist/yolo/change-evidence.js';
|
|
2
|
+
export declare function collectYoloChangeEvidence(options: {
|
|
3
|
+
readonly projectRoot: string;
|
|
4
|
+
readonly defaultBranch: string;
|
|
5
|
+
readonly expectedHeadSha: string;
|
|
6
|
+
}): YoloChangeEvidenceV1;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{execFileSync as h}from"node:child_process";import{realpathSync as c}from"node:fs";import{createYoloChangeEvidenceV1 as d}from"../../../shared/dist/yolo/change-evidence.js";function r(e,o){try{return h("git",["-C",e,...o],{encoding:"utf8",timeout:3e3,maxBuffer:1024*1024,stdio:["ignore","pipe","ignore"]})}catch{throw new Error("yolo-change-evidence-unavailable")}}function g(e){if(!/^[A-Za-z0-9][A-Za-z0-9_./-]{0,119}$/.test(e.defaultBranch)||e.defaultBranch.includes(".."))throw new Error("yolo-change-evidence-unavailable");const o=r(e.projectRoot,["rev-parse","--show-toplevel"]).trim();if(c(o)!==c(e.projectRoot))throw new Error("yolo-change-evidence-unavailable");const a=r(e.projectRoot,["rev-parse","HEAD"]).trim();if(a!==e.expectedHeadSha)throw new Error("yolo-change-evidence-stale");let t;try{t=r(e.projectRoot,["merge-base","HEAD",e.defaultBranch]).trim()}catch{t=r(e.projectRoot,["merge-base","HEAD",`refs/remotes/origin/${e.defaultBranch}`]).trim()}const n=r(e.projectRoot,["diff","--name-only","-z",t,"--"]).split("\0").filter(Boolean),l=r(e.projectRoot,["ls-files","--others","--exclude-standard","-z"]).split("\0").filter(Boolean),i=d({baseSha:t,headSha:a,changedPaths:[...n,...l]});if(r(e.projectRoot,["rev-parse","HEAD"]).trim()!==a)throw new Error("yolo-change-evidence-stale");return i}export{g as collectYoloChangeEvidence};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type YoloSpecEnvelopeV1 } from '../../../shared/dist/yolo/spec-envelope.js';
|
|
2
|
+
export interface CollectYoloSpecEnvelopeOptions {
|
|
3
|
+
readonly projectRoot: string;
|
|
4
|
+
readonly storyId: string;
|
|
5
|
+
readonly epicId: string;
|
|
6
|
+
readonly inputSha256: string;
|
|
7
|
+
/** Test seam; production callers omit it and read Git HEAD physically. */
|
|
8
|
+
readonly headSha?: string;
|
|
9
|
+
readonly collectedAt?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function collectYoloSpecEnvelope(options: CollectYoloSpecEnvelopeOptions): YoloSpecEnvelopeV1;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{execFileSync as h}from"node:child_process";import{existsSync as w,readFileSync as S,realpathSync as d,statSync as m}from"node:fs";import{join as f,sep as E}from"node:path";import{createYoloSpecEnvelopeV1 as R,YOLO_SPEC_ARTIFACT_LIMIT_BYTES as v}from"../../../shared/dist/yolo/spec-envelope.js";import{inspectFrontmatterContext as g}from"../../../shared/dist/frontmatter-context-contract.js";const j=["core/templates/specs/feature-spec-template.md","core/templates/specs/architecture-spec-template.md","core/templates/specs/contract-spec-template.md","core/templates/specs/story-spec-template.md","core/templates/specs/traceability-matrix-template.md"],x=/(?:^|[\s([`'";])((?:docs\/(?:stories|epics|specs|architecture|examples)\/|core\/templates\/specs\/)[A-Za-z0-9_./-]+\.(?:md|mdx|feature))(?:$|[\s)\]`'",;])/gm;function A(e){return e.startsWith("core/templates/")?"template":e.startsWith("docs/epics/")?"epic":e.startsWith("docs/stories/")&&e.endsWith(".story.md")?"story":e.toLowerCase().includes("adr")?"adr":e.toLowerCase().includes("contract")?"contract":e.toLowerCase().includes("nfr")?"nfr":e.endsWith(".feature")?"bdd":e.toLowerCase().includes("traceability")?"traceability":e.startsWith("docs/examples/")?"example":"spec"}function I(e){return[...new Set([...e.matchAll(x)].map(t=>t[1]).filter(t=>!!t))].sort()}function n(e,t){const c=d(e);let r;try{r=d(f(c,t))}catch{throw new Error("yolo-spec-envelope-required")}if(!r.startsWith(`${c}${E}`))throw new Error("yolo-spec-envelope-path-out-of-scope");if(!m(r).isFile())throw new Error("yolo-spec-envelope-unsafe");if(m(r).size>v)throw new Error("yolo-spec-envelope-too-large");return S(r,"utf8")}function a(e,t){const r=g(e,{context:t==="story"?"story-target":"epic-target",explicitlySelected:!0});if(r.status==="malformed"||r.status==="oversized")throw new Error("yolo-spec-envelope-unsafe");if(r.status==="absent")return e;const s=e.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n/);if(!s)throw new Error("yolo-spec-envelope-unsafe");return`---
|
|
2
|
+
${Object.entries(r.metadata).map(([i,p])=>`${i}: ${JSON.stringify(p)}`).join(`
|
|
3
|
+
`)}
|
|
4
|
+
---
|
|
5
|
+
${e.slice(s[0].length)}`}function u(e){try{return h("git",["-C",e,"rev-parse","HEAD"],{encoding:"utf8",timeout:2e3,stdio:["ignore","pipe","ignore"]}).trim()}catch{throw new Error("yolo-spec-envelope-stale")}}function L(e){const t=`docs/stories/${e.storyId}.story.md`,c=`docs/epics/epic-${e.epicId}.md`,r=e.headSha??u(e.projectRoot),s=a(n(e.projectRoot,t),"story"),l=a(n(e.projectRoot,c),"epic"),i=[{kind:"story",path:t,content:s},{kind:"epic",path:c,content:l}];for(const o of new Set([...I(s),...j]))o===t||o===c||!w(f(e.projectRoot,o))||i.push({kind:A(o),path:o,content:n(e.projectRoot,o)});const p=R({storyId:e.storyId,epicId:e.epicId,targetPath:t,inputSha256:e.inputSha256,headSha:r,collectedAt:e.collectedAt??new Date().toISOString(),artifacts:i}),y=e.headSha??u(e.projectRoot);if(r!==y||a(n(e.projectRoot,t),"story")!==s||a(n(e.projectRoot,c),"epic")!==l||i.some(o=>o.kind!=="story"&&o.kind!=="epic"&&n(e.projectRoot,o.path)!==o.content))throw new Error("yolo-spec-envelope-stale");return p}export{L as collectYoloSpecEnvelope};
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
export const FRONTMATTER_CONTEXTS = [
|
|
3
|
+
'wrapper',
|
|
4
|
+
'delegated-prompt',
|
|
5
|
+
'epic-target',
|
|
6
|
+
'story-target',
|
|
7
|
+
'memory-wrapper',
|
|
8
|
+
];
|
|
9
|
+
export const FRONTMATTER_STATUSES = [
|
|
10
|
+
'absent',
|
|
11
|
+
'valid',
|
|
12
|
+
'malformed',
|
|
13
|
+
'oversized',
|
|
14
|
+
];
|
|
15
|
+
export const FRONTMATTER_REASON_CODES = [
|
|
16
|
+
'frontmatter-absent',
|
|
17
|
+
'frontmatter-valid',
|
|
18
|
+
'frontmatter-malformed',
|
|
19
|
+
'frontmatter-oversized',
|
|
20
|
+
'frontmatter-preserved',
|
|
21
|
+
'frontmatter-projection-withheld',
|
|
22
|
+
'frontmatter-explicit-selection-required',
|
|
23
|
+
'frontmatter-metadata-projected',
|
|
24
|
+
'frontmatter-unknown-fields-omitted',
|
|
25
|
+
'frontmatter-unsupported-value',
|
|
26
|
+
];
|
|
27
|
+
export const FRONTMATTER_LIMITS = Object.freeze({
|
|
28
|
+
maxBytes: 8 * 1024,
|
|
29
|
+
maxLines: 128,
|
|
30
|
+
maxFields: 32,
|
|
31
|
+
maxListItems: 32,
|
|
32
|
+
maxValueChars: 512,
|
|
33
|
+
});
|
|
34
|
+
const EPIC_TARGET_FIELDS = Object.freeze([
|
|
35
|
+
'id',
|
|
36
|
+
'title',
|
|
37
|
+
'status',
|
|
38
|
+
'source',
|
|
39
|
+
'depends_on',
|
|
40
|
+
'related_epics',
|
|
41
|
+
'total_stories',
|
|
42
|
+
'completed_stories',
|
|
43
|
+
]);
|
|
44
|
+
const STORY_TARGET_FIELDS = Object.freeze([
|
|
45
|
+
'id',
|
|
46
|
+
'epic_id',
|
|
47
|
+
'title',
|
|
48
|
+
'status',
|
|
49
|
+
'priority',
|
|
50
|
+
'depends_on',
|
|
51
|
+
'files_to_modify',
|
|
52
|
+
]);
|
|
53
|
+
const LIST_FIELDS = new Set([
|
|
54
|
+
'depends_on',
|
|
55
|
+
'related_epics',
|
|
56
|
+
'files_to_modify',
|
|
57
|
+
]);
|
|
58
|
+
const NUMBER_FIELDS = new Set([
|
|
59
|
+
'total_stories',
|
|
60
|
+
'completed_stories',
|
|
61
|
+
]);
|
|
62
|
+
export const FRONTMATTER_CONTEXT_POLICY = Object.freeze({
|
|
63
|
+
wrapper: Object.freeze({
|
|
64
|
+
preserveSource: true,
|
|
65
|
+
metadataProjection: 'withheld',
|
|
66
|
+
bodyProjection: 'explicit-command-only',
|
|
67
|
+
allowedFields: Object.freeze([]),
|
|
68
|
+
}),
|
|
69
|
+
'delegated-prompt': Object.freeze({
|
|
70
|
+
preserveSource: true,
|
|
71
|
+
metadataProjection: 'withheld',
|
|
72
|
+
bodyProjection: 'server-authorized-command-only',
|
|
73
|
+
allowedFields: Object.freeze([]),
|
|
74
|
+
}),
|
|
75
|
+
'epic-target': Object.freeze({
|
|
76
|
+
preserveSource: true,
|
|
77
|
+
metadataProjection: 'explicit-target-only',
|
|
78
|
+
bodyProjection: 'none',
|
|
79
|
+
allowedFields: EPIC_TARGET_FIELDS,
|
|
80
|
+
}),
|
|
81
|
+
'story-target': Object.freeze({
|
|
82
|
+
preserveSource: true,
|
|
83
|
+
metadataProjection: 'explicit-target-only',
|
|
84
|
+
bodyProjection: 'none',
|
|
85
|
+
allowedFields: STORY_TARGET_FIELDS,
|
|
86
|
+
}),
|
|
87
|
+
'memory-wrapper': Object.freeze({
|
|
88
|
+
preserveSource: true,
|
|
89
|
+
metadataProjection: 'withheld',
|
|
90
|
+
bodyProjection: 'none',
|
|
91
|
+
allowedFields: Object.freeze([]),
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
function byteLength(value) {
|
|
95
|
+
return Buffer.byteLength(value, 'utf8');
|
|
96
|
+
}
|
|
97
|
+
function scanFrontmatter(source) {
|
|
98
|
+
if (typeof source !== 'string')
|
|
99
|
+
return { status: 'malformed' };
|
|
100
|
+
if (!source.startsWith('---'))
|
|
101
|
+
return { status: 'absent' };
|
|
102
|
+
const opening = source.match(/^---(?:\r\n|\n)/);
|
|
103
|
+
if (!opening)
|
|
104
|
+
return { status: 'malformed' };
|
|
105
|
+
const contentStart = opening[0].length;
|
|
106
|
+
let cursor = contentStart;
|
|
107
|
+
let lineCount = 1;
|
|
108
|
+
while (cursor <= source.length) {
|
|
109
|
+
const newlineIndex = source.indexOf('\n', cursor);
|
|
110
|
+
const lineEnd = newlineIndex === -1 ? source.length : newlineIndex;
|
|
111
|
+
const rawLine = source.slice(cursor, lineEnd);
|
|
112
|
+
const line = rawLine.endsWith('\r') ? rawLine.slice(0, -1) : rawLine;
|
|
113
|
+
lineCount += 1;
|
|
114
|
+
const inspectedEnd = newlineIndex === -1 ? lineEnd : newlineIndex + 1;
|
|
115
|
+
if (lineCount > FRONTMATTER_LIMITS.maxLines ||
|
|
116
|
+
byteLength(source.slice(0, inspectedEnd)) > FRONTMATTER_LIMITS.maxBytes) {
|
|
117
|
+
return { status: 'oversized' };
|
|
118
|
+
}
|
|
119
|
+
if (line === '---') {
|
|
120
|
+
return {
|
|
121
|
+
status: 'valid',
|
|
122
|
+
block: source.slice(contentStart, cursor),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (newlineIndex === -1)
|
|
126
|
+
break;
|
|
127
|
+
cursor = newlineIndex + 1;
|
|
128
|
+
}
|
|
129
|
+
return { status: 'malformed' };
|
|
130
|
+
}
|
|
131
|
+
function unquote(value) {
|
|
132
|
+
if (value.length > FRONTMATTER_LIMITS.maxValueChars)
|
|
133
|
+
return null;
|
|
134
|
+
const first = value[0];
|
|
135
|
+
const last = value[value.length - 1];
|
|
136
|
+
const startsQuoted = first === '"' || first === "'";
|
|
137
|
+
const endsQuoted = last === '"' || last === "'";
|
|
138
|
+
if (startsQuoted !== endsQuoted || (startsQuoted && first !== last))
|
|
139
|
+
return null;
|
|
140
|
+
if (startsQuoted && value.length >= 2) {
|
|
141
|
+
return value.slice(1, -1);
|
|
142
|
+
}
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
145
|
+
function parseListItem(value) {
|
|
146
|
+
const parsed = unquote(value.trim());
|
|
147
|
+
if (parsed === null || parsed.length === 0)
|
|
148
|
+
return null;
|
|
149
|
+
return parsed;
|
|
150
|
+
}
|
|
151
|
+
function parseInlineValue(value) {
|
|
152
|
+
const trimmed = value.trim();
|
|
153
|
+
if (trimmed.length === 0)
|
|
154
|
+
return null;
|
|
155
|
+
if (trimmed.startsWith('[') || trimmed.endsWith(']')) {
|
|
156
|
+
if (!trimmed.startsWith('[') || !trimmed.endsWith(']'))
|
|
157
|
+
return null;
|
|
158
|
+
const body = trimmed.slice(1, -1).trim();
|
|
159
|
+
if (!body)
|
|
160
|
+
return Object.freeze([]);
|
|
161
|
+
const rawItems = body.split(',');
|
|
162
|
+
if (rawItems.length > FRONTMATTER_LIMITS.maxListItems)
|
|
163
|
+
return null;
|
|
164
|
+
const items = rawItems.map(parseListItem);
|
|
165
|
+
if (items.some((item) => item === null))
|
|
166
|
+
return null;
|
|
167
|
+
return Object.freeze(items);
|
|
168
|
+
}
|
|
169
|
+
const unquoted = unquote(trimmed);
|
|
170
|
+
if (unquoted === null)
|
|
171
|
+
return null;
|
|
172
|
+
const quoted = (trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
173
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"));
|
|
174
|
+
if (quoted)
|
|
175
|
+
return unquoted;
|
|
176
|
+
if (unquoted === 'true')
|
|
177
|
+
return true;
|
|
178
|
+
if (unquoted === 'false')
|
|
179
|
+
return false;
|
|
180
|
+
if (/^-?(?:0|[1-9]\d*)$/.test(unquoted)) {
|
|
181
|
+
const numberValue = Number(unquoted);
|
|
182
|
+
if (!Number.isFinite(numberValue) || !Number.isSafeInteger(numberValue))
|
|
183
|
+
return null;
|
|
184
|
+
return numberValue;
|
|
185
|
+
}
|
|
186
|
+
return unquoted;
|
|
187
|
+
}
|
|
188
|
+
function parseFrontmatterBlock(block) {
|
|
189
|
+
const fields = {};
|
|
190
|
+
let fieldCount = 0;
|
|
191
|
+
let pendingListKey = null;
|
|
192
|
+
let pendingList = [];
|
|
193
|
+
const commitPendingList = () => {
|
|
194
|
+
if (!pendingListKey)
|
|
195
|
+
return true;
|
|
196
|
+
if (pendingList.length > FRONTMATTER_LIMITS.maxListItems)
|
|
197
|
+
return false;
|
|
198
|
+
fields[pendingListKey] = Object.freeze([...pendingList]);
|
|
199
|
+
pendingListKey = null;
|
|
200
|
+
pendingList = [];
|
|
201
|
+
return true;
|
|
202
|
+
};
|
|
203
|
+
for (const rawLine of block.split(/\r?\n/)) {
|
|
204
|
+
if (!rawLine.trim() || rawLine.trimStart().startsWith('#'))
|
|
205
|
+
continue;
|
|
206
|
+
const listMatch = rawLine.match(/^\s+-\s+(.+?)\s*$/);
|
|
207
|
+
if (listMatch) {
|
|
208
|
+
if (!pendingListKey)
|
|
209
|
+
return { status: 'malformed', unsupportedValue: true };
|
|
210
|
+
if (listMatch[1].trim().length > FRONTMATTER_LIMITS.maxValueChars) {
|
|
211
|
+
return { status: 'oversized' };
|
|
212
|
+
}
|
|
213
|
+
const item = parseListItem(listMatch[1]);
|
|
214
|
+
if (item === null)
|
|
215
|
+
return { status: 'malformed', unsupportedValue: true };
|
|
216
|
+
pendingList.push(item);
|
|
217
|
+
if (pendingList.length > FRONTMATTER_LIMITS.maxListItems)
|
|
218
|
+
return { status: 'oversized' };
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (!commitPendingList())
|
|
222
|
+
return { status: 'oversized' };
|
|
223
|
+
const fieldMatch = rawLine.match(/^([A-Za-z_][A-Za-z0-9_-]{0,63}):(?:\s*(.*))?$/);
|
|
224
|
+
if (!fieldMatch)
|
|
225
|
+
return { status: 'malformed', unsupportedValue: true };
|
|
226
|
+
const key = fieldMatch[1];
|
|
227
|
+
if (Object.prototype.hasOwnProperty.call(fields, key)) {
|
|
228
|
+
return { status: 'malformed', unsupportedValue: true };
|
|
229
|
+
}
|
|
230
|
+
fieldCount += 1;
|
|
231
|
+
if (fieldCount > FRONTMATTER_LIMITS.maxFields)
|
|
232
|
+
return { status: 'oversized' };
|
|
233
|
+
const rawValue = fieldMatch[2] ?? '';
|
|
234
|
+
if (!rawValue.trim()) {
|
|
235
|
+
pendingListKey = key;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const value = parseInlineValue(rawValue);
|
|
239
|
+
if (value === null) {
|
|
240
|
+
return rawValue.length > FRONTMATTER_LIMITS.maxValueChars
|
|
241
|
+
? { status: 'oversized' }
|
|
242
|
+
: { status: 'malformed', unsupportedValue: true };
|
|
243
|
+
}
|
|
244
|
+
fields[key] = value;
|
|
245
|
+
}
|
|
246
|
+
if (!commitPendingList())
|
|
247
|
+
return { status: 'oversized' };
|
|
248
|
+
return { status: 'valid', fields: Object.freeze(fields) };
|
|
249
|
+
}
|
|
250
|
+
function result(context, status, reasonCodes, metadata = Object.freeze({})) {
|
|
251
|
+
return Object.freeze({
|
|
252
|
+
context,
|
|
253
|
+
status,
|
|
254
|
+
reasonCodes: Object.freeze([...new Set(reasonCodes)]),
|
|
255
|
+
metadata: Object.freeze({ ...metadata }),
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
function isValidAllowedFieldValue(key, value) {
|
|
259
|
+
if (LIST_FIELDS.has(key)) {
|
|
260
|
+
return Array.isArray(value) && value.every((item) => typeof item === 'string');
|
|
261
|
+
}
|
|
262
|
+
if (NUMBER_FIELDS.has(key)) {
|
|
263
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
|
|
264
|
+
}
|
|
265
|
+
return typeof value === 'string';
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Classify bounded frontmatter without returning source text, body content, or
|
|
269
|
+
* rejected values. The caller supplies the context; file content never grants
|
|
270
|
+
* execution authority or selects its own interpretation mode.
|
|
271
|
+
*/
|
|
272
|
+
export function inspectFrontmatterContext(source, options) {
|
|
273
|
+
const structural = scanFrontmatter(source);
|
|
274
|
+
if (structural.status === 'absent') {
|
|
275
|
+
return result(options.context, 'absent', ['frontmatter-absent']);
|
|
276
|
+
}
|
|
277
|
+
if (structural.status === 'malformed') {
|
|
278
|
+
return result(options.context, 'malformed', ['frontmatter-malformed']);
|
|
279
|
+
}
|
|
280
|
+
if (structural.status === 'oversized') {
|
|
281
|
+
return result(options.context, 'oversized', ['frontmatter-oversized']);
|
|
282
|
+
}
|
|
283
|
+
const parsed = parseFrontmatterBlock(structural.block);
|
|
284
|
+
if (parsed.status === 'malformed') {
|
|
285
|
+
return result(options.context, 'malformed', [
|
|
286
|
+
'frontmatter-malformed',
|
|
287
|
+
...(parsed.unsupportedValue ? ['frontmatter-unsupported-value'] : []),
|
|
288
|
+
]);
|
|
289
|
+
}
|
|
290
|
+
if (parsed.status === 'oversized') {
|
|
291
|
+
return result(options.context, 'oversized', ['frontmatter-oversized']);
|
|
292
|
+
}
|
|
293
|
+
const policy = FRONTMATTER_CONTEXT_POLICY[options.context];
|
|
294
|
+
const baseReasons = [
|
|
295
|
+
'frontmatter-valid',
|
|
296
|
+
'frontmatter-preserved',
|
|
297
|
+
];
|
|
298
|
+
if (policy.metadataProjection === 'withheld') {
|
|
299
|
+
baseReasons.push('frontmatter-projection-withheld');
|
|
300
|
+
return result(options.context, 'valid', baseReasons);
|
|
301
|
+
}
|
|
302
|
+
if (options.explicitlySelected !== true) {
|
|
303
|
+
baseReasons.push('frontmatter-explicit-selection-required', 'frontmatter-projection-withheld');
|
|
304
|
+
return result(options.context, 'valid', baseReasons);
|
|
305
|
+
}
|
|
306
|
+
const allowedFields = new Set(policy.allowedFields);
|
|
307
|
+
const metadata = {};
|
|
308
|
+
let omittedUnknownField = false;
|
|
309
|
+
for (const [key, value] of Object.entries(parsed.fields)) {
|
|
310
|
+
if (!allowedFields.has(key)) {
|
|
311
|
+
omittedUnknownField = true;
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
if (!isValidAllowedFieldValue(key, value)) {
|
|
315
|
+
return result(options.context, 'malformed', [
|
|
316
|
+
'frontmatter-malformed',
|
|
317
|
+
'frontmatter-unsupported-value',
|
|
318
|
+
]);
|
|
319
|
+
}
|
|
320
|
+
metadata[key] = value;
|
|
321
|
+
}
|
|
322
|
+
baseReasons.push('frontmatter-metadata-projected');
|
|
323
|
+
if (omittedUnknownField)
|
|
324
|
+
baseReasons.push('frontmatter-unknown-fields-omitted');
|
|
325
|
+
return result(options.context, 'valid', baseReasons, metadata);
|
|
326
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { posix } from 'node:path';
|
|
3
|
+
const SHA = /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/;
|
|
4
|
+
const PATH = /^[A-Za-z0-9_.-][A-Za-z0-9_./ -]{0,239}$/;
|
|
5
|
+
const UNSAFE = /(^|\/)(?:\.env(?:\.|$)|secrets?|credentials?|private-prompts?|customer-data)(?:\/|$)|(?:token|password|license[_-]?key|secret)[=:]/i;
|
|
6
|
+
function digest(paths) {
|
|
7
|
+
return createHash('sha256').update(JSON.stringify(paths)).digest('hex');
|
|
8
|
+
}
|
|
9
|
+
function fail() { throw new Error('yolo-change-evidence-unsafe'); }
|
|
10
|
+
function validPath(path) {
|
|
11
|
+
return typeof path === 'string' && PATH.test(path) && !path.includes('\\')
|
|
12
|
+
&& !path.split('/').includes('..') && posix.normalize(path) === path && !UNSAFE.test(path);
|
|
13
|
+
}
|
|
14
|
+
export function createYoloChangeEvidenceV1(input) {
|
|
15
|
+
if (!SHA.test(input.baseSha) || !SHA.test(input.headSha) || !Array.isArray(input.changedPaths))
|
|
16
|
+
fail();
|
|
17
|
+
const paths = [...new Set(input.changedPaths)].sort();
|
|
18
|
+
if (paths.length > 256 || paths.some((path) => !validPath(path)))
|
|
19
|
+
fail();
|
|
20
|
+
return {
|
|
21
|
+
schemaVersion: 1,
|
|
22
|
+
source: 'git-diff-base-and-worktree',
|
|
23
|
+
baseSha: input.baseSha,
|
|
24
|
+
headSha: input.headSha,
|
|
25
|
+
changedPaths: paths,
|
|
26
|
+
pathsSha256: digest(paths),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function assertYoloChangeEvidenceV1(value, expectedHeadSha) {
|
|
30
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
31
|
+
fail();
|
|
32
|
+
const record = value;
|
|
33
|
+
const keys = Object.keys(record).sort();
|
|
34
|
+
const expected = ['schemaVersion', 'source', 'baseSha', 'headSha', 'changedPaths', 'pathsSha256'].sort();
|
|
35
|
+
if (keys.length !== expected.length || keys.some((key, index) => key !== expected[index])
|
|
36
|
+
|| record.schemaVersion !== 1 || record.source !== 'git-diff-base-and-worktree'
|
|
37
|
+
|| typeof record.baseSha !== 'string' || !SHA.test(record.baseSha)
|
|
38
|
+
|| record.headSha !== expectedHeadSha || typeof record.headSha !== 'string' || !SHA.test(record.headSha)
|
|
39
|
+
|| !Array.isArray(record.changedPaths) || record.changedPaths.length > 256
|
|
40
|
+
|| record.changedPaths.some((path) => !validPath(path))
|
|
41
|
+
|| JSON.stringify([...new Set(record.changedPaths)].sort()) !== JSON.stringify(record.changedPaths)
|
|
42
|
+
|| record.pathsSha256 !== digest(record.changedPaths))
|
|
43
|
+
fail();
|
|
44
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { posix } from 'node:path';
|
|
3
|
+
import { inspectFrontmatterContext } from '../frontmatter-context-contract.js';
|
|
4
|
+
export const YOLO_SPEC_ARTIFACT_LIMIT_BYTES = 64 * 1024;
|
|
5
|
+
export const YOLO_SPEC_TOTAL_LIMIT_BYTES = 128 * 1024;
|
|
6
|
+
export const YOLO_SPEC_MAX_ARTIFACTS = 12;
|
|
7
|
+
export const YOLO_SPEC_MAX_AGE_MS = 120_000;
|
|
8
|
+
export const YOLO_SPEC_FUTURE_SKEW_MS = 30_000;
|
|
9
|
+
const HASH = /^[a-f0-9]{64}$/;
|
|
10
|
+
const HEAD = /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/;
|
|
11
|
+
const STORY_ID = /^[A-Za-z0-9]+(?:\.[A-Za-z0-9]+)+$/;
|
|
12
|
+
const EPIC_ID = /^[A-Za-z0-9]+$/;
|
|
13
|
+
const KINDS = ['story', 'epic', 'spec', 'contract', 'adr', 'nfr', 'example', 'bdd', 'traceability', 'template'];
|
|
14
|
+
const ALLOWED_PATH = /^(?:docs\/(?:stories|epics|specs|architecture|examples)\/|core\/templates\/specs\/)[A-Za-z0-9_./-]+$/;
|
|
15
|
+
const PROHIBITED_CONTENT = /(?:-----BEGIN [A-Z ]*PRIVATE KEY-----|\b(?:bearer|token|password|secret|api[_-]?key)\s*[:=]\s*\S+|\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b|\b(?:sk-|ghp_|github_pat_|npm_)[A-Za-z0-9_-]{12,}\b|(?:^|[\s"'(])\/(?:home|media|root|etc|var|Users)\/)/im;
|
|
16
|
+
function deny(code) {
|
|
17
|
+
throw new Error(code);
|
|
18
|
+
}
|
|
19
|
+
function isRecord(value) {
|
|
20
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
21
|
+
}
|
|
22
|
+
function hasExactKeys(value, keys) {
|
|
23
|
+
const actual = Object.keys(value).sort();
|
|
24
|
+
const expected = [...keys].sort();
|
|
25
|
+
return actual.length === expected.length && actual.every((key, index) => key === expected[index]);
|
|
26
|
+
}
|
|
27
|
+
function sha256(value) {
|
|
28
|
+
return createHash('sha256').update(value, 'utf8').digest('hex');
|
|
29
|
+
}
|
|
30
|
+
function validPath(value) {
|
|
31
|
+
return typeof value === 'string'
|
|
32
|
+
&& value.length > 0 && value.length <= 240
|
|
33
|
+
&& ALLOWED_PATH.test(value)
|
|
34
|
+
&& !value.includes('\\') && !value.split('/').includes('..')
|
|
35
|
+
&& posix.normalize(value) === value;
|
|
36
|
+
}
|
|
37
|
+
function normalizeContent(content) {
|
|
38
|
+
if (content.includes('\0') || /\r(?!\n)/.test(content))
|
|
39
|
+
deny('yolo-spec-envelope-unsafe');
|
|
40
|
+
const normalized = content.replaceAll('\r\n', '\n');
|
|
41
|
+
if (PROHIBITED_CONTENT.test(normalized))
|
|
42
|
+
deny('yolo-spec-envelope-unsafe');
|
|
43
|
+
return normalized;
|
|
44
|
+
}
|
|
45
|
+
function artifactManifestSha256(artifacts) {
|
|
46
|
+
return sha256(JSON.stringify(artifacts.map(({ kind, path, byteLength, sha256: digest }) => [kind, path, byteLength, digest])));
|
|
47
|
+
}
|
|
48
|
+
function unsignedEnvelopeSha256(value) {
|
|
49
|
+
return sha256(JSON.stringify(value));
|
|
50
|
+
}
|
|
51
|
+
function checkArtifact(value) {
|
|
52
|
+
if (!isRecord(value) || !hasExactKeys(value, ['kind', 'path', 'content', 'byteLength', 'sha256'])
|
|
53
|
+
|| !KINDS.includes(String(value.kind)) || !validPath(value.path))
|
|
54
|
+
deny('yolo-spec-envelope-path-out-of-scope');
|
|
55
|
+
if (typeof value.content !== 'string' || !Number.isSafeInteger(value.byteLength) || !HASH.test(String(value.sha256))) {
|
|
56
|
+
deny('yolo-spec-envelope-unsafe');
|
|
57
|
+
}
|
|
58
|
+
const content = normalizeContent(value.content);
|
|
59
|
+
const bytes = Buffer.byteLength(content, 'utf8');
|
|
60
|
+
if (bytes > YOLO_SPEC_ARTIFACT_LIMIT_BYTES)
|
|
61
|
+
deny('yolo-spec-envelope-too-large');
|
|
62
|
+
if (content !== value.content || bytes !== value.byteLength || sha256(content) !== value.sha256) {
|
|
63
|
+
deny('yolo-spec-envelope-digest-mismatch');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function createYoloSpecEnvelopeV1(input) {
|
|
67
|
+
if (!Array.isArray(input.artifacts) || input.artifacts.length > YOLO_SPEC_MAX_ARTIFACTS)
|
|
68
|
+
deny('yolo-spec-envelope-too-large');
|
|
69
|
+
const artifacts = input.artifacts.map((artifact) => {
|
|
70
|
+
if (!isRecord(artifact) || !KINDS.includes(String(artifact.kind)) || !validPath(artifact.path)
|
|
71
|
+
|| typeof artifact.content !== 'string')
|
|
72
|
+
deny('yolo-spec-envelope-path-out-of-scope');
|
|
73
|
+
const content = normalizeContent(artifact.content);
|
|
74
|
+
return {
|
|
75
|
+
kind: artifact.kind,
|
|
76
|
+
path: artifact.path,
|
|
77
|
+
content,
|
|
78
|
+
byteLength: Buffer.byteLength(content, 'utf8'),
|
|
79
|
+
sha256: sha256(content),
|
|
80
|
+
};
|
|
81
|
+
}).sort((left, right) => left.kind.localeCompare(right.kind) || left.path.localeCompare(right.path));
|
|
82
|
+
const unsigned = {
|
|
83
|
+
schemaVersion: 1,
|
|
84
|
+
storyId: input.storyId,
|
|
85
|
+
epicId: input.epicId,
|
|
86
|
+
targetPath: input.targetPath,
|
|
87
|
+
inputSha256: input.inputSha256,
|
|
88
|
+
sourceRevision: { headSha: input.headSha, manifestSha256: artifactManifestSha256(artifacts) },
|
|
89
|
+
collectedAt: input.collectedAt,
|
|
90
|
+
artifacts,
|
|
91
|
+
};
|
|
92
|
+
const envelope = { ...unsigned, envelopeSha256: unsignedEnvelopeSha256(unsigned) };
|
|
93
|
+
assertYoloSpecEnvelopeV1(envelope, {
|
|
94
|
+
storyId: input.storyId, epicId: input.epicId, targetPath: input.targetPath,
|
|
95
|
+
inputSha256: input.inputSha256, nowMs: Date.parse(input.collectedAt),
|
|
96
|
+
});
|
|
97
|
+
return envelope;
|
|
98
|
+
}
|
|
99
|
+
export function assertYoloSpecEnvelopeV1(value, context) {
|
|
100
|
+
if (!isRecord(value) || !hasExactKeys(value, ['schemaVersion', 'storyId', 'epicId', 'targetPath', 'inputSha256', 'sourceRevision', 'collectedAt', 'artifacts', 'envelopeSha256'])
|
|
101
|
+
|| value.schemaVersion !== 1 || typeof value.storyId !== 'string' || !STORY_ID.test(value.storyId)
|
|
102
|
+
|| typeof value.epicId !== 'string' || !EPIC_ID.test(value.epicId)
|
|
103
|
+
|| !validPath(value.targetPath) || !HASH.test(String(value.inputSha256))
|
|
104
|
+
|| !HASH.test(String(value.envelopeSha256)))
|
|
105
|
+
deny('yolo-spec-envelope-unsafe');
|
|
106
|
+
if (value.storyId !== context.storyId || value.epicId !== context.epicId
|
|
107
|
+
|| value.targetPath !== context.targetPath || value.inputSha256 !== context.inputSha256
|
|
108
|
+
|| value.targetPath !== `docs/stories/${value.storyId}.story.md`)
|
|
109
|
+
deny('yolo-spec-envelope-target-mismatch');
|
|
110
|
+
const source = value.sourceRevision;
|
|
111
|
+
if (!isRecord(source) || !hasExactKeys(source, ['headSha', 'manifestSha256'])
|
|
112
|
+
|| !HEAD.test(String(source.headSha)) || !HASH.test(String(source.manifestSha256)))
|
|
113
|
+
deny('yolo-spec-envelope-unsafe');
|
|
114
|
+
if (typeof value.collectedAt !== 'string' || !/^\d{4}-\d{2}-\d{2}T.*Z$/.test(value.collectedAt))
|
|
115
|
+
deny('yolo-spec-envelope-stale');
|
|
116
|
+
const collectedMs = Date.parse(value.collectedAt);
|
|
117
|
+
const nowMs = context.nowMs ?? Date.now();
|
|
118
|
+
if (!Number.isFinite(collectedMs) || !Number.isFinite(nowMs)
|
|
119
|
+
|| nowMs - collectedMs > YOLO_SPEC_MAX_AGE_MS || collectedMs - nowMs > YOLO_SPEC_FUTURE_SKEW_MS)
|
|
120
|
+
deny('yolo-spec-envelope-stale');
|
|
121
|
+
if (!Array.isArray(value.artifacts) || value.artifacts.length < 1 || value.artifacts.length > YOLO_SPEC_MAX_ARTIFACTS)
|
|
122
|
+
deny('yolo-spec-envelope-too-large');
|
|
123
|
+
const artifacts = value.artifacts;
|
|
124
|
+
for (const artifact of artifacts)
|
|
125
|
+
checkArtifact(artifact);
|
|
126
|
+
const typedArtifacts = artifacts;
|
|
127
|
+
if (typedArtifacts.reduce((sum, artifact) => sum + artifact.byteLength, 0) > YOLO_SPEC_TOTAL_LIMIT_BYTES)
|
|
128
|
+
deny('yolo-spec-envelope-too-large');
|
|
129
|
+
const keys = typedArtifacts.map((artifact) => `${artifact.kind}:${artifact.path}`);
|
|
130
|
+
if (new Set(keys).size !== keys.length || keys.some((key, index) => index > 0 && key < keys[index - 1]))
|
|
131
|
+
deny('yolo-spec-envelope-unsafe');
|
|
132
|
+
if (typedArtifacts.filter((artifact) => artifact.kind === 'story' && artifact.path === value.targetPath).length !== 1
|
|
133
|
+
|| typedArtifacts.filter((artifact) => artifact.kind === 'epic' && artifact.path === `docs/epics/epic-${value.epicId}.md`).length !== 1
|
|
134
|
+
|| typedArtifacts.some((artifact) => artifact.kind === 'story' && artifact.path !== value.targetPath)
|
|
135
|
+
|| typedArtifacts.some((artifact) => artifact.kind === 'epic' && artifact.path !== `docs/epics/epic-${value.epicId}.md`)) {
|
|
136
|
+
deny('yolo-spec-envelope-target-mismatch');
|
|
137
|
+
}
|
|
138
|
+
const story = typedArtifacts.find((artifact) => artifact.kind === 'story');
|
|
139
|
+
const epic = typedArtifacts.find((artifact) => artifact.kind === 'epic');
|
|
140
|
+
const storyContext = inspectFrontmatterContext(story.content, { context: 'story-target', explicitlySelected: true });
|
|
141
|
+
const epicContext = inspectFrontmatterContext(epic.content, { context: 'epic-target', explicitlySelected: true });
|
|
142
|
+
if (storyContext.status !== 'valid' || storyContext.metadata.id !== value.storyId
|
|
143
|
+
|| storyContext.metadata.epic_id !== value.epicId
|
|
144
|
+
|| epicContext.status !== 'valid' || epicContext.metadata.id !== value.epicId) {
|
|
145
|
+
deny('yolo-spec-envelope-target-mismatch');
|
|
146
|
+
}
|
|
147
|
+
if (artifactManifestSha256(typedArtifacts) !== source.manifestSha256)
|
|
148
|
+
deny('yolo-spec-envelope-digest-mismatch');
|
|
149
|
+
const unsigned = {
|
|
150
|
+
schemaVersion: value.schemaVersion,
|
|
151
|
+
storyId: value.storyId,
|
|
152
|
+
epicId: value.epicId,
|
|
153
|
+
targetPath: value.targetPath,
|
|
154
|
+
inputSha256: value.inputSha256,
|
|
155
|
+
sourceRevision: value.sourceRevision,
|
|
156
|
+
collectedAt: value.collectedAt,
|
|
157
|
+
artifacts: value.artifacts,
|
|
158
|
+
};
|
|
159
|
+
if (unsignedEnvelopeSha256(unsigned) !== value.envelopeSha256)
|
|
160
|
+
deny('yolo-spec-envelope-digest-mismatch');
|
|
161
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: neocortex-root
|
|
3
|
-
description: "🧠 Neocortex Root Agent v4.60.
|
|
3
|
+
description: "🧠 Neocortex Root Agent v4.60.57 | OrNexus Team"
|
|
4
4
|
model: opus
|
|
5
5
|
color: blue
|
|
6
6
|
tools:
|
|
@@ -108,7 +108,7 @@ SEMPRE que este agente for invocado, imprima o banner abaixo como PRIMEIRO outpu
|
|
|
108
108
|
┌────────────────────────────────────────────────────────────┐
|
|
109
109
|
│ │
|
|
110
110
|
│ ####### N E O C O R T E X │
|
|
111
|
-
│ ### ######## v4.60.
|
|
111
|
+
│ ### ######## v4.60.57 │
|
|
112
112
|
│ ######### ##### │
|
|
113
113
|
│ ## ############## Development Orchestrator │
|
|
114
114
|
│ ## ### ###### ## OrNexus Team │
|