@awebai/oats 0.30.0 → 0.30.1
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/docs/desktop-cli-api.md +3 -3
- package/docs/implementation.md +2 -1
- package/docs/integrations.md +1 -1
- package/docs/knowledge.md +4 -4
- package/docs/official-catalog.md +3 -3
- package/docs/packages.md +8 -8
- package/docs/plans/0.30-close-out.md +24 -2
- package/docs/release-lane.md +7 -2
- package/docs/release-notes/v0.30.1.md +123 -0
- package/docs/souls-and-instances.md +2 -2
- package/docs/workspaces.md +6 -1
- package/lib/packages.mjs +1 -1
- package/lib/resolve.mjs +1 -1
- package/package-catalog.json +2 -2
- package/package.json +1 -3
- package/skills/oats-getting-started/SKILL.md +1 -1
- package/capabilities/oats-authoring/LICENSE +0 -21
- package/capabilities/oats-authoring/oats-package.json +0 -11
- package/capabilities/oats-authoring/oats.json +0 -12
- package/capabilities/oats-authoring/skills/integration-authoring/SKILL.md +0 -84
- package/capabilities/oats-authoring/skills/skill-craft/SKILL.md +0 -109
- package/capabilities/oats-authoring/skills/soul-craft/SKILL.md +0 -116
- package/capabilities/oats-aweb/bin/oats-aweb-binding.mjs +0 -11
- package/capabilities/oats-aweb/bin/oats-aweb.mjs +0 -1672
- package/capabilities/oats-aweb/injects/aweb.md +0 -47
- package/capabilities/oats-aweb/lib/binding-wire.mjs +0 -365
- package/capabilities/oats-aweb/lib/captured-execution.mjs +0 -91
- package/capabilities/oats-aweb/lib/captured-native.mjs +0 -91
- package/capabilities/oats-aweb/lib/grant-custody.mjs +0 -38
- package/capabilities/oats-aweb/lib/invocation-shape.mjs +0 -135
- package/capabilities/oats-aweb/lib/portable-binding.mjs +0 -146
- package/capabilities/oats-aweb/lib/session-readiness.mjs +0 -56
- package/capabilities/oats-aweb/lib/wake-receive.mjs +0 -56
- package/capabilities/oats-aweb/oats.json +0 -201
- package/capabilities/oats-aweb/skills/LICENSE +0 -21
- package/capabilities/oats-aweb/skills/VENDORED.md +0 -31
- package/capabilities/oats-aweb/skills/aweb-identity/SKILL.md +0 -201
- package/capabilities/oats-aweb/skills/aweb-messaging/SKILL.md +0 -161
- package/capabilities/oats-aweb/skills/aweb-messaging/references/messaging-scenarios.md +0 -61
- package/capabilities/oats-aweb/skills/aweb-team-membership/SKILL.md +0 -116
- package/capabilities/oats-aweb/skills/aweb-team-membership/references/team-membership-reference.md +0 -74
- package/capabilities/oats-aweb/skills/oats-aweb/SKILL.md +0 -286
- package/capabilities/oats-code-review/injects/reviewer.md +0 -26
- package/capabilities/oats-code-review/oats.json +0 -16
- package/capabilities/oats-code-review/skills/adversarial-review/SKILL.md +0 -66
- package/capabilities/oats-code-review/skills/review-dev-docs/SKILL.md +0 -30
- package/capabilities/oats-code-review/skills/security-review/SKILL.md +0 -56
- package/capabilities/oats-code-review/skills/simplification-review/SKILL.md +0 -34
- package/capabilities/oats-developer/injects/developer.md +0 -38
- package/capabilities/oats-developer/oats.json +0 -17
- package/capabilities/oats-developer/skills/execution-strategy/SKILL.md +0 -43
- package/capabilities/oats-developer/skills/maintain-dev-docs/SKILL.md +0 -47
- package/capabilities/oats-developer/skills/run-the-review-loop/SKILL.md +0 -65
- package/capabilities/oats-developer/skills/understand-the-spec/SKILL.md +0 -37
- package/capabilities/oats-developer/skills/worktrees/SKILL.md +0 -36
- package/capabilities/oats-engineering-expert/injects/expert.md +0 -37
- package/capabilities/oats-engineering-expert/oats.json +0 -17
- package/capabilities/oats-engineering-expert/skills/coordinate-developers/SKILL.md +0 -37
- package/capabilities/oats-engineering-expert/skills/coordinate-experts/SKILL.md +0 -52
- package/capabilities/oats-engineering-expert/skills/land-your-prs/SKILL.md +0 -50
- package/capabilities/oats-engineering-expert/skills/plan-and-spec/SKILL.md +0 -53
- package/capabilities/oats-engineering-expert/skills/verify-developer-work/SKILL.md +0 -49
- package/capabilities/oats-jira/bin/oats-jira.mjs +0 -40
- package/capabilities/oats-jira/injects/jira.md +0 -10
- package/capabilities/oats-jira/oats.json +0 -22
- package/capabilities/oats-jira/skills/jira-tasks/SKILL.md +0 -179
- package/capabilities/oats-linear/bin/oats-linear-hook.mjs +0 -34
- package/capabilities/oats-linear/bin/oats-linear.mjs +0 -344
- package/capabilities/oats-linear/injects/linear.md +0 -8
- package/capabilities/oats-linear/oats.json +0 -24
- package/capabilities/oats-linear/skills/linear-tasks/SKILL.md +0 -223
- package/capabilities/oats-okf/bin/oats-okf-binding.mjs +0 -14
- package/capabilities/oats-okf/bin/oats-okf.mjs +0 -213
- package/capabilities/oats-okf/injects/okf.md +0 -42
- package/capabilities/oats-okf/lib/binding-wire.mjs +0 -380
- package/capabilities/oats-okf/lib/captured-worker.mjs +0 -109
- package/capabilities/oats-okf/lib/config.mjs +0 -124
- package/capabilities/oats-okf/lib/consult.mjs +0 -518
- package/capabilities/oats-okf/lib/harvest-status.mjs +0 -88
- package/capabilities/oats-okf/lib/harvest-switch.mjs +0 -94
- package/capabilities/oats-okf/lib/inspection.mjs +0 -138
- package/capabilities/oats-okf/lib/invocation-context.mjs +0 -111
- package/capabilities/oats-okf/lib/invocation-shape.mjs +0 -135
- package/capabilities/oats-okf/lib/io.mjs +0 -118
- package/capabilities/oats-okf/lib/migration.mjs +0 -137
- package/capabilities/oats-okf/lib/okf-validate.mjs +0 -123
- package/capabilities/oats-okf/lib/portable-binding.mjs +0 -199
- package/capabilities/oats-okf/lib/source-contract.mjs +0 -46
- package/capabilities/oats-okf/lib/sources.mjs +0 -438
- package/capabilities/oats-okf/lib/stores.mjs +0 -473
- package/capabilities/oats-okf/lib/worker.mjs +0 -486
- package/capabilities/oats-okf/oats.json +0 -151
- package/capabilities/oats-okf/schemas/okf-base.schema.json +0 -46
- package/capabilities/oats-okf/schemas/okf-bindings.schema.json +0 -112
- package/capabilities/oats-okf/schemas/okf-portable-declaration.schema.json +0 -87
- package/capabilities/oats-okf/schemas/okf-portable-payload.schema.json +0 -113
- package/capabilities/oats-okf/schemas/okf-soul.schema.json +0 -37
- package/capabilities/oats-okf/skills/okf-consultation/SKILL.md +0 -144
- package/capabilities/oats-okf/skills/okf-consultation/references/consult.md +0 -86
- package/capabilities/oats-okf/skills/okf-instance-knowledge/SKILL.md +0 -104
- package/capabilities/oats-okf-harvest/bin/okf-harvest.mjs +0 -140
- package/capabilities/oats-okf-harvest/injects/harvester.md +0 -12
- package/capabilities/oats-okf-harvest/oats.json +0 -26
- package/capabilities/oats-okf-harvest/skills/knowledge-harvest/SKILL.md +0 -168
- package/capabilities/oats-okf-harvest/skills/knowledge-theory/SKILL.md +0 -192
- package/capabilities/oats-okf-harvest/skills/okf-authoring/SKILL.md +0 -151
- package/capabilities/oats-okf-harvest/skills/okf-authoring/scripts/okf-validate.mjs +0 -123
- package/capabilities/oats-okf-maintenance/bin/okf-maintenance.mjs +0 -170
- package/capabilities/oats-okf-maintenance/injects/maintainer.md +0 -12
- package/capabilities/oats-okf-maintenance/lib/provenance.mjs +0 -50
- package/capabilities/oats-okf-maintenance/oats.json +0 -21
- package/capabilities/oats-okf-maintenance/skills/knowledge-review/SKILL.md +0 -159
- package/capabilities/oats-okf-maintenance/skills/knowledge-theory/SKILL.md +0 -192
- package/capabilities/oats-okf-maintenance/skills/okf-authoring/SKILL.md +0 -151
- package/capabilities/oats-okf-maintenance/skills/okf-authoring/scripts/okf-validate.mjs +0 -123
- package/capabilities/oats-okf-maintenance/skills/okf-trigger-setup/SKILL.md +0 -134
- package/capabilities/oats-workspace-experts/injects/oats-experts.md +0 -26
- package/capabilities/oats-workspace-experts/oats.json +0 -9
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
export function parseAwJson(text, what) {
|
|
2
|
-
const trimmed = String(text ?? '').trim();
|
|
3
|
-
if (!trimmed) throw new Error(`${what} returned no JSON result`);
|
|
4
|
-
try { return JSON.parse(trimmed); } catch { /* may have progress before JSON */ }
|
|
5
|
-
const lines = trimmed.split(/\r?\n/);
|
|
6
|
-
for (let i = 0; i < lines.length; i++) {
|
|
7
|
-
if (!lines[i].trimStart().startsWith('{')) continue;
|
|
8
|
-
try { return JSON.parse(lines.slice(i).join('\n')); } catch { /* keep looking */ }
|
|
9
|
-
}
|
|
10
|
-
throw new Error(`${what} returned no JSON result`);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function custodyPreflight({ custody, resident, team, e2eeRequired = true, runAw, fatalOnError = true, fatal }) {
|
|
14
|
-
const failNow = (message) => { if (fatalOnError && typeof fatal === 'function') fatal(message); throw new Error(message); };
|
|
15
|
-
let status;
|
|
16
|
-
try { status = parseAwJson(runAw(['aw', 'custody', 'status', '--json'], custody, { unsetEnv: ['AWEB_IDENTITY_HOME'] }), 'aw custody status'); }
|
|
17
|
-
catch (e) { failNow(`custody preflight failed for ${resident}: aw custody status --json could not run (${e.message || e}); start aw custody serve for ${resident}`); }
|
|
18
|
-
const state = String(status.status || 'unknown');
|
|
19
|
-
const firstError = Array.isArray(status.errors) && status.errors.length ? status.errors[0] : undefined;
|
|
20
|
-
const firstErrorCode = typeof firstError === 'string' ? firstError : firstError?.code;
|
|
21
|
-
const code = firstErrorCode ? ` error=${firstErrorCode}` : '';
|
|
22
|
-
const fail = (why) => failNow(`custody preflight failed for ${resident}: status=${state}${code}; ${why}; start aw custody serve for ${resident}`);
|
|
23
|
-
if (state !== 'running') fail('custody service is not running');
|
|
24
|
-
const teamRow = (Array.isArray(status.teams) ? status.teams : []).find((t) => t && (t.team_id || t.id) === team);
|
|
25
|
-
if (!teamRow) fail(`team ${team} is not present in custody status`);
|
|
26
|
-
if (teamRow.ready !== true) fail(`team ${team} is not ready in custody status`);
|
|
27
|
-
if (teamRow.certificate_present === false) fail(`team ${team} certificate is not present in custody status`);
|
|
28
|
-
if (teamRow.grant_status_endpoint_ready !== undefined && teamRow.grant_status_endpoint_ready !== true) fail(`the aweb server serving team ${team} does not provide grant status yet; hosted grants wait for that deployment`);
|
|
29
|
-
if (status.keys?.signing_ready !== true) fail('keys.signing_ready is false');
|
|
30
|
-
const ops = new Set(Array.isArray(status.ops) ? status.ops.map(String) : []);
|
|
31
|
-
const requiredOps = ['sign_plain_message.v1', ...(e2eeRequired ? ['unwrap_e2ee_message.v1', 'create_e2ee_envelope.v1'] : [])];
|
|
32
|
-
const missingOps = requiredOps.filter((op) => !ops.has(op));
|
|
33
|
-
if (missingOps.length) fail(`required custody operations are missing: ${missingOps.join(', ')}`);
|
|
34
|
-
if (e2eeRequired && status.keys?.encryption_ready !== true) fail('keys.encryption_ready is false');
|
|
35
|
-
const warnings = [];
|
|
36
|
-
if (!e2eeRequired && status.keys?.encryption_ready !== true) warnings.push('E2E encryption is disabled for this grant and custody encryption is not ready; encrypted mail/chat will not be available in this session.');
|
|
37
|
-
return { status, warnings };
|
|
38
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
// Provider-side consumption checks for the kernel-owned CapturedInvocationContext1.
|
|
2
|
-
// Decoded JSON only: no source/config parsing, artifact lookup or authorization.
|
|
3
|
-
// The kernel must verify retained authority before invoking provider code.
|
|
4
|
-
import { isAbsolute, join, resolve } from 'node:path';
|
|
5
|
-
export const INVOCATION_LIMITS=Object.freeze({bytes:512*1024,depth:32,entries:16384});
|
|
6
|
-
export const PRIOR_RECEIPT_LIMITS=Object.freeze({bytes:128*1024,depth:24,entries:8192});
|
|
7
|
-
const fail=()=>{throw Object.assign(new Error('invalid captured invocation'),{code:'invalid-binding'});};
|
|
8
|
-
const obj=value=>value!==null && typeof value==='object' && !Array.isArray(value);
|
|
9
|
-
const canonical=value=>value===null || typeof value!=='object'?JSON.stringify(value):Array.isArray(value)?`[${value.map(canonical).join(',')}]`:`{${Object.keys(value).sort().map(key=>`${JSON.stringify(key)}:${canonical(value[key])}`).join(',')}}`;
|
|
10
|
-
export const sameInvocationJson=(a,b)=>canonical(a)===canonical(b);
|
|
11
|
-
const same=sameInvocationJson;
|
|
12
|
-
function keys(value,allowed,required=allowed) {if(!obj(value)) fail();for(const key of Object.keys(value)) if(!allowed.includes(key)) fail();for(const key of required) if(!Object.hasOwn(value,key)) fail();}
|
|
13
|
-
function text(value,empty=false) {if(typeof value!=='string' || (!empty && !value.length) || value.includes('\0')) fail();}
|
|
14
|
-
function list(value) {if(!Array.isArray(value)) fail();return value;}
|
|
15
|
-
function absolute(value) {text(value);if(!isAbsolute(value) || resolve(value)!==value) fail();}
|
|
16
|
-
function path(value,root=false) {text(value);if(root && value==='.') return;if(value.startsWith('/') || value.includes('\\') || value.split('/').some(part=>!part || part==='.' || part==='..')) fail();}
|
|
17
|
-
function hash(value) {if(typeof value!=='string' || !/^sha256-[a-f0-9]{64}$/.test(value)) fail();}
|
|
18
|
-
function integrity(value,format) {keys(value,['format','value']);if(value.format!==format) fail();hash(value.value);}
|
|
19
|
-
function capability(value) {text(value);if(!/^(?:@?[a-z0-9][a-z0-9._-]*[./])[a-z0-9][a-z0-9._/-]*$/.test(value)) fail();}
|
|
20
|
-
function repository(value) {
|
|
21
|
-
if(value?.kind==='canonical-remote') {keys(value,['kind','remote']);text(value.remote);}
|
|
22
|
-
else if(value?.kind==='provider-repository') {keys(value,['kind','provider','host','id']);text(value.provider);text(value.host);text(value.id);}
|
|
23
|
-
else fail();
|
|
24
|
-
}
|
|
25
|
-
function soulIdentity(value) {
|
|
26
|
-
if(value?.kind==='git-soul') {keys(value,['kind','repository','exportPath']);repository(value.repository);path(value.exportPath);}
|
|
27
|
-
else if(value?.kind==='local-soul') {keys(value,['kind','source','exportPath']);text(value.source);if(!value.source.startsWith('path:')) fail();path(value.exportPath,true);}
|
|
28
|
-
else fail();
|
|
29
|
-
}
|
|
30
|
-
function artifact(value) {
|
|
31
|
-
if(value?.kind==='soul') {keys(value,['kind','identity','integrity']);soulIdentity(value.identity);}
|
|
32
|
-
else if(value?.kind==='capability') {keys(value,['kind','capability','integrity']);capability(value.capability);}
|
|
33
|
-
else if(value?.kind==='resource') keys(value,['kind','integrity']);
|
|
34
|
-
else fail();
|
|
35
|
-
integrity(value.integrity,'oats.tree-exec.v1');
|
|
36
|
-
}
|
|
37
|
-
function resolution(value) {keys(value,['schemaVersion','id']);if(value.schemaVersion!==1) fail();hash(value.id);}
|
|
38
|
-
function incarnation(value) {if(typeof value!=='string' || !/^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/.test(value)) fail();}
|
|
39
|
-
function intent(value,instance) {
|
|
40
|
-
if(value===null) return;
|
|
41
|
-
keys(value,['schemaVersion','executionId','incarnationId','attempt']);incarnation(value.incarnationId);
|
|
42
|
-
if(value.schemaVersion!==1 || typeof value.executionId!=='string' || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(value.executionId) || !Number.isSafeInteger(value.attempt) || value.attempt<1) fail();
|
|
43
|
-
if(instance===null || value.incarnationId!==instance.incarnationId) fail();
|
|
44
|
-
}
|
|
45
|
-
function origin(value) {
|
|
46
|
-
keys(value,['kind','document','pointer','span'],['kind','document','pointer']);
|
|
47
|
-
if(!['soul-requirement','soul-default','workspace-default','import-adoption','operator','package-dependency','work-target','migration-evidence','provider-binding','workspace-admission','member-backlink','source-export','manifest-default'].includes(value.kind)) fail();
|
|
48
|
-
text(value.pointer,true);if(!/^(?:\/(?:[^~]|~[01])*)*$/.test(value.pointer)) fail();
|
|
49
|
-
const doc=value.document;
|
|
50
|
-
if(doc?.kind==='source') {keys(doc,['kind','source','revision','path','integrity']);text(doc.source);text(doc.revision);path(doc.path);integrity(doc.integrity,'oats.bytes.v1');}
|
|
51
|
-
else if(doc?.kind==='deployment') {keys(doc,['kind','path','integrity']);path(doc.path);integrity(doc.integrity,'oats.bytes.v1');}
|
|
52
|
-
else if(doc?.kind==='operator') {keys(doc,['kind','id']);text(doc.id);}
|
|
53
|
-
else if(doc?.kind==='record') {keys(doc,['kind','ref']);resolution(doc.ref);}
|
|
54
|
-
else if(doc?.kind==='artifact') {keys(doc,['kind','owner','path','integrity']);artifact(doc.owner);path(doc.path);integrity(doc.integrity,'oats.bytes.v1');}
|
|
55
|
-
else fail();
|
|
56
|
-
if(Object.hasOwn(value,'span')) {keys(value.span,['start','end']);if(!Number.isSafeInteger(value.span.start) || !Number.isSafeInteger(value.span.end) || value.span.start<0 || value.span.end<value.span.start) fail();}
|
|
57
|
-
}
|
|
58
|
-
function origins(value) {list(value).forEach(origin);}
|
|
59
|
-
function subject(value) {
|
|
60
|
-
if(value?.kind==='helper') {
|
|
61
|
-
keys(value,['kind','provider','definition','name']);artifact(value.provider);
|
|
62
|
-
keys(value.definition,['owner','path','kind']);artifact(value.definition.owner);path(value.definition.path,true);
|
|
63
|
-
if(value.provider.kind!=='capability' || !same(value.provider,value.definition.owner) || !['file','directory','manifest','skill','runtime-package'].includes(value.definition.kind)) fail();
|
|
64
|
-
if(typeof value.name!=='string' || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value.name)) fail();return value.name;
|
|
65
|
-
}
|
|
66
|
-
if(value?.kind!=='persistent') fail();keys(value,['kind','soul']);const s=value.soul;
|
|
67
|
-
keys(s,['identity','revision','alias','sourceArtifact','definition','projection']);soulIdentity(s.identity);artifact(s.sourceArtifact);
|
|
68
|
-
if(s.sourceArtifact.kind!=='soul' || !same(s.identity,s.sourceArtifact.identity)) fail();
|
|
69
|
-
if(typeof s.alias!=='string' || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(s.alias)) fail();path(s.definition);
|
|
70
|
-
keys(s.projection,['roots']);list(s.projection.roots).forEach(value=>path(value,true));
|
|
71
|
-
if(!s.projection.roots.length || !s.projection.roots.some(root=>root==='.' || s.definition===root || s.definition.startsWith(`${root}/`))) fail();
|
|
72
|
-
for(let i=1;i<s.projection.roots.length;i++) if(Buffer.compare(Buffer.from(s.projection.roots[i-1]),Buffer.from(s.projection.roots[i]))>=0) fail();
|
|
73
|
-
if(s.identity.exportPath!=='.' && !s.definition.startsWith(`${s.identity.exportPath}/`)) fail();
|
|
74
|
-
const r=s.revision;
|
|
75
|
-
if(r?.kind==='local') {
|
|
76
|
-
keys(r,['kind','source','integrity','provenance','repository'],['kind','source','integrity','provenance']);text(r.source);if(!r.source.startsWith('path:')) fail();integrity(r.integrity,'oats.tree-exec.v1');
|
|
77
|
-
if(!same(r.integrity,s.sourceArtifact.integrity)) fail();
|
|
78
|
-
if(s.identity.kind==='local-soul') {if(r.source!==s.identity.source || Object.hasOwn(r,'repository')) fail();}
|
|
79
|
-
else {repository(r.repository);if(!same(r.repository,s.identity.repository)) fail();}
|
|
80
|
-
} else {
|
|
81
|
-
keys(r,['identity','remote','selector','commit','provenance']);repository(r.identity);text(r.remote);text(r.selector);
|
|
82
|
-
if(s.identity.kind!=='git-soul' || !same(r.identity,s.identity.repository) || !/^[a-f0-9]{40}$/.test(r.commit)) fail();
|
|
83
|
-
if(r.identity.kind==='canonical-remote' && r.identity.remote!==r.remote) fail();
|
|
84
|
-
}
|
|
85
|
-
origins(r.provenance);if(!r.provenance.length) fail();return s.alias;
|
|
86
|
-
}
|
|
87
|
-
function context(value) {
|
|
88
|
-
if(value?.kind==='workspace') {keys(value,['kind','identity','observation']);keys(value.identity,['repository','path']);repository(value.identity.repository);if(value.identity.path!=='oats-workspace.yaml') fail();origin(value.observation);return {kind:'workspace',identity:value.identity};}
|
|
89
|
-
if(value?.kind==='standalone') {keys(value,['kind','key']);if(value.key!==null) text(value.key);return value;}
|
|
90
|
-
fail();
|
|
91
|
-
}
|
|
92
|
-
function human(value) {if(value===null) return;keys(value,['provider','id']);text(value.provider);text(value.id);}
|
|
93
|
-
function messaging(value,selectedContext,responsibleHuman) {
|
|
94
|
-
if(value?.enabled===false) {keys(value,['schemaVersion','enabled']);if(responsibleHuman!==null) fail();}
|
|
95
|
-
else {
|
|
96
|
-
keys(value,['schemaVersion','enabled','privateKey','wider','provenance']);if(value.enabled!==true) fail();
|
|
97
|
-
keys(value.privateKey,['provider','human','context']);capability(value.privateKey.provider);human(value.privateKey.human);
|
|
98
|
-
if(value.privateKey.human===null || !same(value.privateKey.human,responsibleHuman) || !same(value.privateKey.context,selectedContext) || (selectedContext.kind==='standalone' && selectedContext.key===null)) fail();
|
|
99
|
-
list(value.wider).forEach(row=>{keys(row,['provider','id']);text(row.id);if(row.provider!==value.privateKey.provider) fail();});
|
|
100
|
-
for(let i=1;i<value.wider.length;i++) if(Buffer.compare(Buffer.from(canonical(value.wider[i-1])),Buffer.from(canonical(value.wider[i])))>=0) fail();
|
|
101
|
-
origins(value.provenance);
|
|
102
|
-
}
|
|
103
|
-
if(value.schemaVersion!==1) fail();
|
|
104
|
-
}
|
|
105
|
-
function action(value) {
|
|
106
|
-
if(!obj(value)) fail();
|
|
107
|
-
if(value.kind==='command') {keys(value,['kind','capability','namespace','name'],['kind','name']);if(Object.hasOwn(value,'capability')===Object.hasOwn(value,'namespace')) fail();if(value.capability!==undefined) capability(value.capability);else text(value.namespace);text(value.name);}
|
|
108
|
-
else if(value.kind==='hook') {keys(value,['kind','capability','name']);capability(value.capability);text(value.name);}
|
|
109
|
-
else if(value.kind==='operation') {keys(value,['kind','slot','name']);if(!['knowledge','messaging','tasks'].includes(value.slot)) fail();text(value.name);}
|
|
110
|
-
else if(['inspect','compose'].includes(value.kind)) keys(value,['kind']);
|
|
111
|
-
else fail();
|
|
112
|
-
}
|
|
113
|
-
export function assertInvocationBounds(value,limits=INVOCATION_LIMITS) {
|
|
114
|
-
let entries=0;const active=new Set();
|
|
115
|
-
const visit=(v,depth)=>{
|
|
116
|
-
if(v===null || typeof v==='string' || typeof v==='boolean') return;
|
|
117
|
-
if(typeof v==='number') {if(!Number.isFinite(v)) fail();return;}
|
|
118
|
-
if(typeof v!=='object' || depth>limits.depth || active.has(v)) fail();
|
|
119
|
-
if(!Array.isArray(v) && Object.getPrototypeOf(v)!==Object.prototype && Object.getPrototypeOf(v)!==null) fail();
|
|
120
|
-
active.add(v);for(const child of Object.values(v)) {if(++entries>limits.entries) fail();visit(child,depth+1);}active.delete(v);
|
|
121
|
-
};
|
|
122
|
-
visit(value,1);if(Buffer.byteLength(JSON.stringify(value),'utf8')>limits.bytes) fail();
|
|
123
|
-
}
|
|
124
|
-
export function validateInvocationShape(value,expected={}) {
|
|
125
|
-
assertInvocationBounds(value);keys(value,['schemaVersion','executionBinding','subject','instance','intent','context','responsibleHuman','messagingChoice','capability','action','priorReceipt']);
|
|
126
|
-
if(value.schemaVersion!==1) fail();keys(value.executionBinding,['schemaVersion','deployment','resolution']);if(value.executionBinding.schemaVersion!==1) fail();absolute(value.executionBinding.deployment);resolution(value.executionBinding.resolution);
|
|
127
|
-
const alias=subject(value.subject);
|
|
128
|
-
if(value.instance!==null) {keys(value.instance,['home','work','name','agent','incarnationId']);incarnation(value.instance.incarnationId);absolute(value.instance.home);absolute(value.instance.work);text(value.instance.name);text(value.instance.agent);if(value.instance.agent!==alias || value.instance.work!==join(value.instance.home,'work')) fail();}
|
|
129
|
-
intent(value.intent,value.instance);
|
|
130
|
-
const selectedContext=context(value.context);human(value.responsibleHuman);messaging(value.messagingChoice,selectedContext,value.responsibleHuman);capability(value.capability);action(value.action);assertInvocationBounds(value.priorReceipt,PRIOR_RECEIPT_LIMITS);
|
|
131
|
-
if(value.action.capability!==undefined && value.action.capability!==value.capability) fail();
|
|
132
|
-
if(value.action.kind==='hook' && value.instance===null) fail();
|
|
133
|
-
for(const field of ['capability','context','action']) if(Object.hasOwn(expected,field) && !same(value[field],expected[field])) fail();
|
|
134
|
-
return value;
|
|
135
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { validateInvocationShape, sameInvocationJson } from './invocation-shape.mjs';
|
|
2
|
-
export const MESSAGING_CONTRACT='oats.aweb.messaging';
|
|
3
|
-
export const MESSAGING_CONTRACT_VERSION=1;
|
|
4
|
-
export const RESPONSIBLE_HUMAN_KEY='/bindings/messaging/responsibleHuman';
|
|
5
|
-
export const PRIVATE_TEAM_KEY='/bindings/messaging/privateTeam';
|
|
6
|
-
export const WIDER_KEY='/bindings/messaging/wider';
|
|
7
|
-
const CAPABILITY='oats.aweb';
|
|
8
|
-
const identifierPattern=/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,95}$/;
|
|
9
|
-
const obj=value=>value!==null && typeof value==='object' && !Array.isArray(value);
|
|
10
|
-
const clone=value=>JSON.parse(JSON.stringify(value));
|
|
11
|
-
const canonical=value=>value===null || typeof value!=='object'?JSON.stringify(value):Array.isArray(value)?`[${value.map(canonical).join(',')}]`:`{${Object.keys(value).sort().map(key=>`${JSON.stringify(key)}:${canonical(value[key])}`).join(',')}}`;
|
|
12
|
-
const same=(a,b)=>canonical(a)===canonical(b);
|
|
13
|
-
const fail=(code,message)=>{throw Object.assign(new Error(message),{code});};
|
|
14
|
-
function keys(value,allowed,required,label) {
|
|
15
|
-
if(!obj(value)) fail('invalid-binding',`${label} must be an object`);
|
|
16
|
-
for(const key of Object.keys(value)) if(!allowed.includes(key)) fail('invalid-binding',`unknown ${label} property: ${key}`);
|
|
17
|
-
for(const key of required) if(!Object.hasOwn(value,key)) fail('invalid-binding',`${label} requires ${key}`);
|
|
18
|
-
return value;
|
|
19
|
-
}
|
|
20
|
-
function identifier(value,label='identity') {if(typeof value!=='string' || !identifierPattern.test(value) || ['constructor','__proto__','prototype','toString','valueOf'].includes(value)) fail('invalid-binding',`invalid ${label}`);return value;}
|
|
21
|
-
const pointerKey=value=>String(value).replace(/~/g,'~0').replace(/\//g,'~1');
|
|
22
|
-
export const teamChoiceKey=alias=>`/bindings/messaging/teams/${pointerKey(identifier(alias,'team alias'))}`;
|
|
23
|
-
function originAt(declaration,pointer,kind) {
|
|
24
|
-
const found=declaration.origins[pointer] ?? declaration.origin;
|
|
25
|
-
if(!obj(found)) fail('invalid-binding',`missing origin for ${pointer}`);
|
|
26
|
-
return clone({...found,kind});
|
|
27
|
-
}
|
|
28
|
-
function teamRef(value) {
|
|
29
|
-
keys(value,['provider','id'],['provider','id'],'aweb team reference');
|
|
30
|
-
if(value.provider!==CAPABILITY || typeof value.id!=='string' || !/^[^\s:]+:[^\s:]+$/.test(value.id)) fail('invalid-binding','aweb team reference requires provider oats.aweb and canonical team id');
|
|
31
|
-
return {provider:CAPABILITY,id:value.id};
|
|
32
|
-
}
|
|
33
|
-
function humanRef(value) {
|
|
34
|
-
keys(value,['provider','id'],['provider','id'],'responsible human');
|
|
35
|
-
if(value.provider!==CAPABILITY || typeof value.id!=='string' || !value.id.trim()) fail('invalid-binding','responsible human needs a provider-resolvable oats.aweb id');
|
|
36
|
-
return {provider:CAPABILITY,id:value.id.trim()};
|
|
37
|
-
}
|
|
38
|
-
function exactContext(value) {
|
|
39
|
-
if(value?.kind==='workspace') {keys(value,['kind','identity','observation'],['kind','identity','observation'],'workspace context');if(!obj(value.identity) || !obj(value.observation)) fail('invalid-binding','workspace context needs qualified identity and observation');return {kind:'workspace',identity:clone(value.identity)};}
|
|
40
|
-
if(value?.kind==='standalone') {keys(value,['kind','key'],['kind','key'],'standalone context');if(typeof value.key!=='string' || !value.key.trim()) fail('needs-configuration','messaging-enabled standalone preparation needs an explicit context key');return {kind:'standalone',key:value.key.trim()};}
|
|
41
|
-
fail('invalid-binding','unsupported messaging context');
|
|
42
|
-
}
|
|
43
|
-
function boundContext(value) {
|
|
44
|
-
if(value?.kind==='workspace') {keys(value,['kind','identity'],['kind','identity'],'bound workspace context');if(!obj(value.identity)) fail('invalid-binding','bound workspace context needs qualified identity');return clone(value);}
|
|
45
|
-
if(value?.kind==='standalone') {keys(value,['kind','key'],['kind','key'],'bound standalone context');if(typeof value.key!=='string' || !value.key.trim()) fail('invalid-binding','bound standalone context needs an explicit key');return {kind:'standalone',key:value.key.trim()};}
|
|
46
|
-
fail('invalid-binding','unsupported bound messaging context');
|
|
47
|
-
}
|
|
48
|
-
function declaration(value) {
|
|
49
|
-
keys(value,['kind','value','origin','origins'],['kind','value','origin','origins'],'messaging declaration');
|
|
50
|
-
if(!['soul','workspace','adoption','operator'].includes(value.kind) || !obj(value.value) || !obj(value.origin) || !obj(value.origins)) fail('invalid-binding','invalid messaging declaration');
|
|
51
|
-
return value;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Emit messaging provider fields for the kernel's single resolver. Team aliases
|
|
55
|
-
* are availability data; only the explicit operator wider list is consent. */
|
|
56
|
-
export function normalizeMessagingDeclarations({declarations,context}={}) {
|
|
57
|
-
if(!Array.isArray(declarations)) fail('invalid-binding','messaging declarations must be an array');
|
|
58
|
-
const exact=exactContext(context),contextOrigin=context?.kind==='workspace'?clone(context.observation):null,requirements=[],candidates=[],teams={},requested=[],requestedOrigins={},aliases={},aliasOrigins={};let soul=null,privatePolicy=false,privatePolicyOrigin=null;
|
|
59
|
-
for(const raw of declarations) {
|
|
60
|
-
const item=declaration(raw);
|
|
61
|
-
// The kernel supplies complete validated source/workspace/operator
|
|
62
|
-
// declarations, including other providers' fields. Consume only messaging
|
|
63
|
-
// fields here; do not impose a second closed schema on the whole document.
|
|
64
|
-
if(item.kind==='soul') {
|
|
65
|
-
if(soul) fail('requirement-conflict','multiple soul messaging declarations');soul=item;
|
|
66
|
-
const list=item.value.teams ?? [];
|
|
67
|
-
if(!Array.isArray(list) || new Set(list).size!==list.length) fail('invalid-binding','soul teams must be a unique array');
|
|
68
|
-
for(let index=0;index<list.length;index++) {const alias=identifier(list[index],'soul team alias');requested.push(alias);requestedOrigins[alias]=originAt(item,`/teams/${index}`,'soul-requirement');}
|
|
69
|
-
} else if(item.kind==='workspace') {
|
|
70
|
-
const value=item.value.teams;if(value===undefined) continue;if(!obj(value)) fail('invalid-binding','workspace teams must be an object');
|
|
71
|
-
for(const [alias,ref] of Object.entries(value)) {
|
|
72
|
-
if(alias==='private') {if(ref!=='per-human') fail('invalid-binding','workspace private team policy must be per-human');privatePolicy=true;privatePolicyOrigin=originAt(item,'/teams/private','workspace-default');continue;}
|
|
73
|
-
const key=teamChoiceKey(alias),origin=originAt(item,`/teams/${pointerKey(alias)}`,'workspace-default');
|
|
74
|
-
teams[alias]=key;candidates.push({key,kind:'workspace-default',value:teamRef(ref),origin});
|
|
75
|
-
}
|
|
76
|
-
} else if(item.kind==='adoption') {
|
|
77
|
-
const value=item.value.teamAliases;if(value===undefined) continue;if(!obj(value)) fail('invalid-binding','adoption teamAliases must be an object');
|
|
78
|
-
for(const [source,target] of Object.entries(value)) {
|
|
79
|
-
identifier(source,'source team alias');identifier(target,'workspace team alias');
|
|
80
|
-
if(Object.hasOwn(aliases,source) && aliases[source]!==target) fail('requirement-conflict','adoption team aliases have conflicting mappings');
|
|
81
|
-
aliases[source]=target;aliasOrigins[source]=originAt(item,`/teamAliases/${pointerKey(source)}`,'import-adoption');
|
|
82
|
-
}
|
|
83
|
-
} else {
|
|
84
|
-
const bindings=item.value.bindings;if(bindings===undefined) continue;
|
|
85
|
-
if(!obj(bindings)) fail('invalid-binding','operator bindings must be an object');
|
|
86
|
-
if(Object.hasOwn(bindings,'responsibleHuman')) candidates.push({key:RESPONSIBLE_HUMAN_KEY,kind:'operator',value:humanRef(bindings.responsibleHuman),origin:originAt(item,'/bindings/responsibleHuman','operator')});
|
|
87
|
-
if(Object.hasOwn(bindings,'privateTeam')) candidates.push({key:PRIVATE_TEAM_KEY,kind:'operator',value:teamRef(bindings.privateTeam),origin:originAt(item,'/bindings/privateTeam','operator')});
|
|
88
|
-
if(Object.hasOwn(bindings,'wider')) {
|
|
89
|
-
if(!Array.isArray(bindings.wider) || new Set(bindings.wider).size!==bindings.wider.length) fail('invalid-binding','operator wider consent must be a unique array');
|
|
90
|
-
candidates.push({key:WIDER_KEY,kind:'operator',value:bindings.wider.map(alias=>identifier(alias,'wider team alias')),origin:originAt(item,'/bindings/wider','operator')});
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if(!soul) fail('needs-configuration','messaging binding needs one soul declaration');
|
|
95
|
-
if(exact.kind==='workspace' && !privatePolicy) fail('needs-configuration','messaging workspace must declare private: per-human');
|
|
96
|
-
const rootOrigin=originAt(soul,'','soul-requirement');
|
|
97
|
-
requirements.push({key:RESPONSIBLE_HUMAN_KEY,kind:'required',origin:rootOrigin},{key:WIDER_KEY,kind:'required',origin:rootOrigin});
|
|
98
|
-
return {requirements,candidates,model:{contract:MESSAGING_CONTRACT,version:MESSAGING_CONTRACT_VERSION,context:exact,contextOrigin,requested,requestedOrigins,aliases,aliasOrigins,teams,privatePolicy,privatePolicyOrigin}};
|
|
99
|
-
}
|
|
100
|
-
function selected(choices,key,{optional=false}={}) {
|
|
101
|
-
const choice=choices?.[key];if(!obj(choice) || !Object.hasOwn(choice,'value') || choice.value===null) {if(optional)return null;fail('needs-configuration',key===RESPONSIBLE_HUMAN_KEY?'an explicit responsible-human binding is required':key===WIDER_KEY?'an explicit wider-membership consent list is required':'a selected wider-team binding is required');}return choice;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function bindMessagingDomain({model,choices}={}) {
|
|
105
|
-
if(!obj(model) || model.contract!==MESSAGING_CONTRACT || model.version!==MESSAGING_CONTRACT_VERSION) fail('invalid-binding','invalid normalized messaging model');
|
|
106
|
-
const humanChoice=selected(choices,RESPONSIBLE_HUMAN_KEY),widerChoice=selected(choices,WIDER_KEY),human=humanRef(humanChoice.value);
|
|
107
|
-
if(!Array.isArray(widerChoice.value) || new Set(widerChoice.value).size!==widerChoice.value.length) fail('invalid-binding','resolved wider consent must be a unique array');
|
|
108
|
-
const wider=[],provenance=[humanChoice.selectedBy,widerChoice.selectedBy,model.contextOrigin,model.privatePolicyOrigin].filter(obj).map(clone);
|
|
109
|
-
for(const sourceAlias of widerChoice.value) {
|
|
110
|
-
identifier(sourceAlias,'wider team alias');if(!model.requested.includes(sourceAlias)) fail('invalid-binding',`wider consent names undeclared soul alias: ${sourceAlias}`);
|
|
111
|
-
const workspaceAlias=model.aliases[sourceAlias] ?? sourceAlias,key=model.teams[workspaceAlias];if(!key) fail('needs-configuration','a selected wider alias needs an explicit workspace team mapping');
|
|
112
|
-
const choice=selected(choices,key),ref=teamRef(choice.value);wider.push(ref);if(obj(choice.selectedBy)) provenance.push(clone(choice.selectedBy));
|
|
113
|
-
if(obj(model.requestedOrigins[sourceAlias])) provenance.push(clone(model.requestedOrigins[sourceAlias]));
|
|
114
|
-
if(obj(model.aliasOrigins[sourceAlias])) provenance.push(clone(model.aliasOrigins[sourceAlias]));
|
|
115
|
-
}
|
|
116
|
-
wider.sort((a,b)=>Buffer.compare(Buffer.from(canonical(a)),Buffer.from(canonical(b))));
|
|
117
|
-
for(let index=1;index<wider.length;index++) if(same(wider[index-1],wider[index])) fail('invalid-binding','duplicate resolved wider team');
|
|
118
|
-
const privateChoice=selected(choices,PRIVATE_TEAM_KEY,{optional:true}),privateTeam=privateChoice?teamRef(privateChoice.value):null;if(privateTeam && wider.some(item=>same(item,privateTeam))) fail('invalid-binding','private team cannot also be a wider team');if(obj(privateChoice?.selectedBy)) provenance.push(clone(privateChoice.selectedBy));
|
|
119
|
-
const unique=[];for(const item of provenance) if(!unique.some(prior=>same(prior,item))) unique.push(item);
|
|
120
|
-
const privateKey={provider:CAPABILITY,human,context:clone(model.context)};
|
|
121
|
-
return {payloadContract:MESSAGING_CONTRACT,payloadVersion:MESSAGING_CONTRACT_VERSION,payload:{responsibleHuman:human,context:clone(model.context),privateTeam,wider},credentialRefs:{},provenance:unique,
|
|
122
|
-
messagingChoice:{schemaVersion:1,enabled:true,privateKey,wider:clone(wider),provenance:clone(unique)}};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export function validateAwebBinding(binding) {
|
|
126
|
-
keys(binding,['schemaVersion','capability','payloadContract','payloadVersion','payload','credentialRefs','provenance'],['schemaVersion','capability','payloadContract','payloadVersion','payload','credentialRefs','provenance'],'aweb provider binding');
|
|
127
|
-
if(binding.schemaVersion!==1 || binding.capability!==CAPABILITY || binding.payloadContract!==MESSAGING_CONTRACT || binding.payloadVersion!==MESSAGING_CONTRACT_VERSION || !obj(binding.payload) || !obj(binding.credentialRefs) || !Array.isArray(binding.provenance)) fail('invalid-binding','invalid aweb provider binding');
|
|
128
|
-
keys(binding.payload,['responsibleHuman','context','privateTeam','wider'],['responsibleHuman','context','privateTeam','wider'],'aweb binding payload');
|
|
129
|
-
humanRef(binding.payload.responsibleHuman);boundContext(binding.payload.context);if(!Array.isArray(binding.payload.wider)) fail('invalid-binding','aweb wider teams must be an array');
|
|
130
|
-
const wider=binding.payload.wider.map(teamRef);for(let index=1;index<wider.length;index++) {if(same(wider[index-1],wider[index])) fail('invalid-binding','duplicate aweb wider team');if(Buffer.compare(Buffer.from(canonical(wider[index-1])),Buffer.from(canonical(wider[index])))>0) fail('invalid-binding','aweb wider teams are not canonical');}
|
|
131
|
-
if(binding.payload.privateTeam!==null) {
|
|
132
|
-
const privateTeam=teamRef(binding.payload.privateTeam);
|
|
133
|
-
if(wider.some(team=>same(team,privateTeam))) fail('invalid-binding','private team cannot also be a wider team');
|
|
134
|
-
}
|
|
135
|
-
if(Object.keys(binding.credentialRefs).length) fail('invalid-binding','no credential reference shape is supported by this binding version');
|
|
136
|
-
return binding;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function validateAwebInvocationContext(value,binding,expected={}) {
|
|
140
|
-
validateAwebBinding(binding);
|
|
141
|
-
validateInvocationShape(value,{...expected,capability:CAPABILITY});
|
|
142
|
-
const selected=value.context.kind==='workspace'?{kind:'workspace',identity:value.context.identity}:value.context;
|
|
143
|
-
if(value.messagingChoice.enabled!==true || value.messagingChoice.privateKey.provider!==CAPABILITY || !sameInvocationJson(selected,binding.payload.context)
|
|
144
|
-
|| !sameInvocationJson(value.responsibleHuman,binding.payload.responsibleHuman) || !sameInvocationJson(value.messagingChoice.wider,binding.payload.wider)) fail('invalid-binding','invocation contradicts messaging binding');
|
|
145
|
-
return value;
|
|
146
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
// Capability-owned operational readiness for the existing HOME delivery route.
|
|
2
|
-
// This is not human-account delegation, private-grant proof or model completion.
|
|
3
|
-
import {execFileSync} from 'node:child_process';
|
|
4
|
-
import {isAbsolute,resolve} from 'node:path';
|
|
5
|
-
import {BINDING_WIRE_LIMITS,parseBindingJson} from './binding-wire.mjs';
|
|
6
|
-
import {sameInvocationJson as same} from './invocation-shape.mjs';
|
|
7
|
-
import {validateAwebBinding,validateAwebInvocationContext} from './portable-binding.mjs';
|
|
8
|
-
// 0.24.1 supplies HOME custody; 0.24.2 also ships the required public projection
|
|
9
|
-
// and same-kernel codec CLI locator. Source presence is not a released version.
|
|
10
|
-
export const HOME_ROUTE_KERNEL_FLOOR='0.24.2';
|
|
11
|
-
const obj=v=>v!==null&&typeof v==='object'&&!Array.isArray(v);
|
|
12
|
-
const missing=message=>({status:'needs-configuration',problems:[{code:'needs-configuration',message}]});
|
|
13
|
-
const error=()=>{throw Object.assign(new Error('selected kernel observation unavailable'),{code:'provider-unavailable'});};
|
|
14
|
-
/** Preserve native HOME/profile/auth/Git; remove only the invoking instance's
|
|
15
|
-
* OATS selectors so a read-only query cannot accidentally target its parent. */
|
|
16
|
-
export function kernelQueryEnvironment(inherited){
|
|
17
|
-
const env={...inherited};
|
|
18
|
-
for(const key of Object.keys(env))if(/^(OATS_(?!HOME_DIR$)|OAS_|PI_AGENT_)/.test(key)||key==='PI_AGENTS_ROOT')delete env[key];
|
|
19
|
-
return env;
|
|
20
|
-
}
|
|
21
|
-
export function kernelMeetsHomeRouteFloor(version){
|
|
22
|
-
if(typeof version!=='string'||!/^\d+\.\d+\.\d+$/.test(version))return false;
|
|
23
|
-
const parts=version.split('.').map(Number),floor=HOME_ROUTE_KERNEL_FLOOR.split('.').map(Number);if(!parts.every(Number.isSafeInteger))return false;
|
|
24
|
-
for(let i=0;i<3;i++)if(parts[i]!==floor[i])return parts[i]>floor[i];
|
|
25
|
-
return true;
|
|
26
|
-
}
|
|
27
|
-
/** Use the caller-owned CLI location, never search PATH or import private kernel
|
|
28
|
-
* indexes. Binding execution must receive the same OATS_CLI_BIN as hooks. */
|
|
29
|
-
export function querySelectedKernel(args,{env=process.env}={}){
|
|
30
|
-
const cli=env.OATS_CLI_BIN;
|
|
31
|
-
if(typeof cli!=='string'||!isAbsolute(cli)||resolve(cli)!==cli||cli.includes('\0'))error();
|
|
32
|
-
let bytes;
|
|
33
|
-
try{bytes=execFileSync(process.execPath,[cli,...args],{env:kernelQueryEnvironment(env),timeout:10000,maxBuffer:BINDING_WIRE_LIMITS.bytes,stdio:['ignore','pipe','pipe']});}catch{error();}
|
|
34
|
-
try{return parseBindingJson(bytes,BINDING_WIRE_LIMITS);}catch{error();}
|
|
35
|
-
}
|
|
36
|
-
/** Reads only public version/retained selection. A missing profile projection
|
|
37
|
-
* stays explicit; no source default, mutable home label or runtime guess. */
|
|
38
|
-
export function assessCapturedSessionReadiness({binding,invocation,settings},{query=querySelectedKernel,env=process.env}={}){
|
|
39
|
-
if(!binding||!invocation)return missing('selected binding and inline captured invocation are required');
|
|
40
|
-
validateAwebBinding(binding);validateAwebInvocationContext(invocation,binding);
|
|
41
|
-
if(!invocation.instance)return missing('an explicit captured instance home is required');
|
|
42
|
-
if(binding.payload.privateTeam===null)return missing('an explicit private-team binding is required');
|
|
43
|
-
if(settings.delivery!=='session')return missing('captured messaging requires explicit delivery: session');
|
|
44
|
-
if(binding.payload.wider.length)return missing('selected wider memberships need their explicitly qualified native setup; they were not omitted');
|
|
45
|
-
let version,observed;
|
|
46
|
-
try{version=query(['--version','--json'],{env});}catch{return missing('caller-owned OATS_CLI_BIN and readable kernel version are required');}
|
|
47
|
-
if(version?.schemaVersion!==1||version?.name!=='@awebai/oats'||!kernelMeetsHomeRouteFloor(version.version))return missing('oats >=0.24.2 is required for captured HOME custody and retained runtime inspection');
|
|
48
|
-
const b=invocation.executionBinding;
|
|
49
|
-
try{observed=query(['inspect','--deployment',b.deployment,'--resolution',b.resolution.id,'--json'],{env});}catch{return missing('the exact retained runtime profile must be readable');}
|
|
50
|
-
if(observed?.schemaVersion!==1||observed?.ok!==true||!same(observed.result?.resolution,b.resolution))return missing('the kernel must report the exact retained resolution');
|
|
51
|
-
const launch=observed.result.launchSelection;
|
|
52
|
-
if(!obj(launch)||Object.keys(launch).some(k=>!['runtime','model'].includes(k))||!Object.hasOwn(launch,'model')||(launch.model!==null&&(typeof launch.model!=='string'||!launch.model.trim())))return missing('a retained launchSelection runtime/model observation is required');
|
|
53
|
-
if(launch.runtime==='pi')return missing('Pi strict print does not support session input; retain messaging and configure an input-capable profile');
|
|
54
|
-
if(!['claude','codex'].includes(launch.runtime))return missing('a supported input-capable ordinary Claude/Codex profile is required');
|
|
55
|
-
return {status:'ready',problems:[]};
|
|
56
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
// Live receive for joined teams through the host wake broker (oats.aweb 1.15).
|
|
2
|
-
//
|
|
3
|
-
// aw >= 1.36.7 accepts one registration per instance home carrying several
|
|
4
|
-
// broker-owned receive identities (`aw wake register --registration-json -`):
|
|
5
|
-
// - external-session homes (delivery: session): the broker owns every identity;
|
|
6
|
-
// the primary keeps the runtime controls, joined homes add mail/chat streams;
|
|
7
|
-
// - native homes (delivery: channel): the Claude channel (native-channel) or the
|
|
8
|
-
// Pi extension (native-pi) keeps the primary identity, and the broker attaches
|
|
9
|
-
// only the joined homes, disjoint from the primary (aw's mixed mode).
|
|
10
|
-
// A runtime with no native surface (Codex, unknown) is not registered: its
|
|
11
|
-
// joined teams stay poll-only and readiness says so.
|
|
12
|
-
import {realpathSync} from 'node:fs';
|
|
13
|
-
import {resolve} from 'node:path';
|
|
14
|
-
|
|
15
|
-
const JOINED_EVENT_CLASSES = ['mail', 'chat'];
|
|
16
|
-
|
|
17
|
-
/** external-session | native-channel | native-pi | null (no broker receive). */
|
|
18
|
-
export function runtimeDeliveryFor({delivery, runtime}) {
|
|
19
|
-
if (delivery === 'session') return 'external-session';
|
|
20
|
-
if (runtime === 'claude') return 'native-channel';
|
|
21
|
-
if (runtime === 'pi') return 'native-pi';
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** The registration document for this home, or null when the home needs none
|
|
26
|
-
* (native home with no joined team, or a runtime the broker cannot type into).
|
|
27
|
-
* A session home with no joined team keeps the legacy one-identity register. */
|
|
28
|
-
export function wakeRegistration({home, primaryIdentityHome, delivery, runtime, joined = [], backend}) {
|
|
29
|
-
const rt = runtimeDeliveryFor({delivery, runtime});
|
|
30
|
-
if (!rt || !joined.length) return null;
|
|
31
|
-
const receive = joined.map((j) => ({identity_home: j.identityHome, label: j.label, event_classes: JOINED_EVENT_CLASSES}));
|
|
32
|
-
const doc = rt === 'external-session'
|
|
33
|
-
? {home, delivery: 'session', runtime_delivery: rt, identity_home: primaryIdentityHome, receive_identities: [{identity_home: primaryIdentityHome, label: 'default', controls: true}, ...receive]}
|
|
34
|
-
: {home, delivery: rt, runtime_delivery: rt, primary_identity_home: primaryIdentityHome, receive_identities: receive};
|
|
35
|
-
if (backend) doc.backend = backend;
|
|
36
|
-
return doc;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const canon = (p) => { try { return realpathSync(p); } catch { return resolve(String(p || '')); } };
|
|
40
|
-
|
|
41
|
-
/** Each joined team's actual receive mode from `aw wake status --json`:
|
|
42
|
-
* native only when the home is registered with that identity home AND the
|
|
43
|
-
* host daemon runs; otherwise poll with the reason. */
|
|
44
|
-
export function joinedReceiveModes(status, {home, joined = []}) {
|
|
45
|
-
const running = status?.daemon_running === true || status?.daemon_version_state === 'reported';
|
|
46
|
-
const want = canon(home);
|
|
47
|
-
const row = (Array.isArray(status?.instances) ? status.instances : []).find((i) => i && canon(i.home) === want);
|
|
48
|
-
const identities = Array.isArray(row?.receive_identities) ? row.receive_identities : [];
|
|
49
|
-
return joined.map((j) => {
|
|
50
|
-
const hit = identities.find((r) => r && canon(r.identity_home) === canon(j.identityHome));
|
|
51
|
-
if (!hit) return {label: j.label, receive: 'poll', reason: row ? 'not-registered-with-broker' : 'home-not-registered'};
|
|
52
|
-
if (!running) return {label: j.label, receive: 'poll', reason: 'wake-daemon-not-running'};
|
|
53
|
-
if (hit.stream_error || hit.stream_admitted === false) return {label: j.label, receive: 'poll', reason: 'stream-not-admitted', detail: String(hit.stream_error || hit.stream_phase || 'not admitted').slice(0, 160)};
|
|
54
|
-
return {label: j.label, receive: 'native', phase: hit.stream_phase || row.phase || 'unknown'};
|
|
55
|
-
});
|
|
56
|
-
}
|