@volter/supercode-teams 0.2.20

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.
Files changed (79) hide show
  1. package/README.md +119 -0
  2. package/attention.mjs +191 -0
  3. package/bin/fleet.mjs +151 -0
  4. package/bin/teams.mjs +291 -0
  5. package/bin/workspace/args.mjs +66 -0
  6. package/bin/workspace/files.mjs +110 -0
  7. package/bin/workspace/lifecycle.mjs +113 -0
  8. package/bin/workspace/volter-device.mjs +42 -0
  9. package/bin/workspace/volter-login.mjs +185 -0
  10. package/bin/workspace.mjs +726 -0
  11. package/browser-attach.mjs +90 -0
  12. package/client.mjs +212 -0
  13. package/connector/effects.mjs +95 -0
  14. package/connector/index.d.ts +69 -0
  15. package/connector/index.mjs +174 -0
  16. package/connector/native-continuation.d.ts +8 -0
  17. package/connector/native-continuation.mjs +50 -0
  18. package/connector/native-effects.mjs +164 -0
  19. package/context.mjs +34 -0
  20. package/contracts/adapter-map.json +16 -0
  21. package/contracts/index.d.ts +299 -0
  22. package/contracts/index.mjs +37 -0
  23. package/execution.mjs +206 -0
  24. package/fleet.mjs +78 -0
  25. package/index.mjs +12 -0
  26. package/machine-door.mjs +36 -0
  27. package/machine.mjs +1398 -0
  28. package/package.json +80 -0
  29. package/porthole-hook.cjs +137 -0
  30. package/remote/job-exec.py +146 -0
  31. package/remote/wire.py +23 -0
  32. package/server/arc2-transactions.mjs +125 -0
  33. package/server/attachment-relay.mjs +149 -0
  34. package/server/catalog.mjs +7 -0
  35. package/server/continuations.mjs +56 -0
  36. package/server/correlation.d.ts +4 -0
  37. package/server/correlation.mjs +17 -0
  38. package/server/effects-transactions.mjs +119 -0
  39. package/server/effects.mjs +97 -0
  40. package/server/event-routes.mjs +208 -0
  41. package/server/exports.mjs +46 -0
  42. package/server/harness-routes.mjs +248 -0
  43. package/server/index.d.ts +31 -0
  44. package/server/index.mjs +52 -0
  45. package/server/ingest.mjs +24 -0
  46. package/server/node-upgrade.mjs +39 -0
  47. package/server/otlp/collector-config.d.ts +2 -0
  48. package/server/otlp/collector-config.mjs +26 -0
  49. package/server/otlp/decoders/index.d.ts +63 -0
  50. package/server/otlp/decoders/index.mjs +311 -0
  51. package/server/otlp/index.d.ts +4 -0
  52. package/server/otlp/index.mjs +4 -0
  53. package/server/otlp/projection.d.ts +5 -0
  54. package/server/otlp/projection.mjs +17 -0
  55. package/server/otlp/receiver.d.ts +3 -0
  56. package/server/otlp/receiver.mjs +36 -0
  57. package/server/otlp/schema/google/protobuf/any.proto +162 -0
  58. package/server/otlp/schema/google/rpc/status.proto +48 -0
  59. package/server/otlp/schema/manifest.json +22 -0
  60. package/server/otlp/schema/opentelemetry/proto/collector/logs/v1/logs_service.proto +77 -0
  61. package/server/otlp/schema/opentelemetry/proto/collector/metrics/v1/metrics_service.proto +77 -0
  62. package/server/otlp/schema/opentelemetry/proto/collector/trace/v1/trace_service.proto +77 -0
  63. package/server/otlp/schema/opentelemetry/proto/common/v1/common.proto +154 -0
  64. package/server/otlp/schema/opentelemetry/proto/logs/v1/logs.proto +226 -0
  65. package/server/otlp/schema/opentelemetry/proto/metrics/v1/metrics.proto +735 -0
  66. package/server/otlp/schema/opentelemetry/proto/resource/v1/resource.proto +45 -0
  67. package/server/otlp/schema/opentelemetry/proto/trace/v1/trace.proto +359 -0
  68. package/server/otlp/transport.d.ts +10 -0
  69. package/server/otlp/transport.mjs +424 -0
  70. package/server/otlp/wire-codec.mjs +153 -0
  71. package/server/store-core.mjs +139 -0
  72. package/server/store-worker.mjs +23 -0
  73. package/worker/fetch-adapter.mjs +49 -0
  74. package/worker/index.mjs +127 -0
  75. package/worker/websocket-duplex.mjs +29 -0
  76. package/worker/wrangler.toml +38 -0
  77. package/workspace-client.d.ts +75 -0
  78. package/workspace-client.mjs +171 -0
  79. package/workspace-move.mjs +38 -0
@@ -0,0 +1,149 @@
1
+ import {hashToken} from '@volter/teams/server/auth';
2
+
3
+ const LIMIT=1024*1024;
4
+ const decoder=new TextDecoder('utf-8',{fatal:true});
5
+ const TYPES=new Set(['output_delta','reasoning_delta','turn_started','turn_completed','message','tool','harness_notice','native','request','error','closed']);
6
+ const jsonBytes=value=>Buffer.byteLength(JSON.stringify(value));
7
+ const domain=(status,code,message)=>{const error=new Error(message);Object.assign(error,{status,code});throw error;};
8
+ const relays=api=>api.attachmentRelays??=new Map();
9
+
10
+ function validJson(value,seen=new Set()){
11
+ if(value===null||typeof value==='string'||typeof value==='boolean')return true;
12
+ if(typeof value==='number')return Number.isFinite(value);
13
+ if(typeof value!=='object'||seen.has(value))return false;seen.add(value);
14
+ const ok=Array.isArray(value)?value.every(item=>validJson(item,seen)):Object.keys(value).every(key=>validJson(value[key],seen));seen.delete(value);return ok;
15
+ }
16
+
17
+ export function validateAttachmentFrame(frame,{nativeSessionId,lastSequence}){
18
+ if(!frame||typeof frame!=='object'||Array.isArray(frame)||!validJson(frame))domain(400,'invalid_request','invalid runtime event JSON');
19
+ if(typeof frame.sessionId!=='string'||frame.sessionId!==nativeSessionId)domain(409,'conflict','runtime event session identity differs from binding');
20
+ if(!Number.isSafeInteger(frame.sequence)||frame.sequence<0||frame.sequence<=lastSequence)domain(409,'conflict','runtime event sequence must increase strictly');
21
+ if(!TYPES.has(frame.type)||typeof frame.kind!=='string'||!Object.hasOwn(frame,'payload')||!Object.hasOwn(frame,'raw')||!validJson(frame.payload)||!frame.raw||typeof frame.raw!=='object'||Array.isArray(frame.raw)||typeof frame.raw.kind!=='string'||!Object.hasOwn(frame.raw,'payload')||!validJson(frame.raw.payload))domain(400,'invalid_request','invalid ObservedRuntimeEvent');
22
+ const nullableString=value=>value===null||typeof value==='string',strings=value=>value&&typeof value==='object'&&!Array.isArray(value)&&Object.values(value).every(item=>typeof item==='string');
23
+ if(['output_delta','reasoning_delta'].includes(frame.type)&&!nullableString(frame.text)
24
+ ||frame.type==='turn_started'&&!nullableString(frame.turnId)
25
+ ||frame.type==='turn_completed'&&(!nullableString(frame.turnId)||!['completed','error'].includes(frame.outcome)||!nullableString(frame.message))
26
+ ||frame.type==='message'&&(!['user','assistant','system','tool'].includes(frame.role)||!nullableString(frame.text)||!strings(frame.metadata))
27
+ ||frame.type==='tool'&&(!nullableString(frame.callId)||!nullableString(frame.name)||!nullableString(frame.arguments)||typeof frame.resultText!=='string'||!['pending','completed','error'].includes(frame.status)||!strings(frame.metadata))
28
+ ||frame.type==='harness_notice'&&(!nullableString(frame.text)||frame.source!=='harness')
29
+ ||frame.type==='request'&&(!Object.hasOwn(frame,'requestId')||!validJson(frame.requestId)||typeof frame.requestKind!=='string'||!Array.isArray(frame.options)||!Object.hasOwn(frame,'cancelResponse')||!validJson(frame.cancelResponse)||!frame.options.every(option=>option&&typeof option==='object'&&typeof option.optionId==='string'&&typeof option.name==='string'&&['allow_once','allow_always','reject_once','reject_always','other'].includes(option.kind)&&validJson(option.response)))
30
+ ||frame.type==='error'&&(typeof frame.message!=='string'||typeof frame.terminal!=='boolean')
31
+ ||frame.type==='closed'&&typeof frame.message!=='string')domain(400,'invalid_request','runtime event does not match its event type');
32
+ if(jsonBytes(frame)>LIMIT)domain(413,'request_too_large','runtime event exceeds 1 MiB');
33
+ return frame;
34
+ }
35
+
36
+ async function snapshot(api,teamId,attachmentId){
37
+ const results=await api.store.snapshot([
38
+ {kind:'one',sql:'SELECT * FROM attachments WHERE team_id=? AND id=?',params:[teamId,attachmentId]},
39
+ {kind:'one',sql:`SELECT c.revoked_at credential_revoked,c.expires_at credential_expires,m.active,m.role
40
+ FROM attachments a JOIN credentials c ON c.id=a.issuer_credential_id
41
+ JOIN memberships m ON m.team_id=a.team_id AND m.principal_id=a.requester_id
42
+ WHERE a.team_id=? AND a.id=? AND c.principal_id=a.requester_id AND c.kind IN ('user','service') AND (c.team_id IS NULL OR c.team_id=a.team_id)`,params:[teamId,attachmentId]},
43
+ {kind:'one',sql:`SELECT m.revoked_at,m.last_seen_at,m.incarnation_id,b.incarnation_id binding_incarnation,b.descriptor_json
44
+ FROM attachments a JOIN machines m ON m.team_id=a.team_id AND m.id=a.machine_id
45
+ JOIN bindings b ON b.team_id=a.team_id AND b.id=a.binding_id AND b.machine_id=a.machine_id AND b.session_id=a.session_id
46
+ WHERE a.team_id=? AND a.id=?`,params:[teamId,attachmentId]},
47
+ {kind:'one',sql:"SELECT 1 ok FROM credentials c JOIN attachments a ON a.team_id=c.team_id AND a.machine_id=c.machine_id WHERE a.team_id=? AND a.id=? AND c.kind='machine' AND c.revoked_at IS NULL AND (c.expires_at IS NULL OR c.expires_at>?)",params:[teamId,attachmentId,api.clock.nowMs()]},
48
+ {kind:'one',sql:'SELECT * FROM live_grants WHERE team_id=? AND id=(SELECT grant_id FROM attachments WHERE team_id=? AND id=?)',params:[teamId,teamId,attachmentId]},
49
+ {kind:'query',sql:`WITH RECURSIVE ancestors(id,canonical_id) AS (
50
+ SELECT s.id,s.canonical_id FROM attachments a JOIN sessions s ON s.team_id=a.team_id AND s.id=a.session_id WHERE a.team_id=? AND a.id=?
51
+ UNION SELECT s.id,s.canonical_id FROM sessions s JOIN ancestors a ON s.id=a.canonical_id WHERE s.team_id=?
52
+ ), family(id) AS (
53
+ SELECT id FROM ancestors WHERE canonical_id IS NULL
54
+ UNION SELECT s.id FROM sessions s JOIN family f ON s.canonical_id=f.id WHERE s.team_id=?
55
+ ) SELECT s.* FROM sessions s JOIN family f ON f.id=s.id WHERE s.team_id=?`,params:[teamId,attachmentId,teamId,teamId,teamId]},
56
+ {kind:'query',sql:'SELECT * FROM session_grants WHERE team_id=? AND session_id IN (WITH RECURSIVE ancestors(id,canonical_id) AS (SELECT s.id,s.canonical_id FROM attachments a JOIN sessions s ON s.team_id=a.team_id AND s.id=a.session_id WHERE a.team_id=? AND a.id=? UNION SELECT s.id,s.canonical_id FROM sessions s JOIN ancestors a ON s.id=a.canonical_id WHERE s.team_id=?), family(id) AS (SELECT id FROM ancestors WHERE canonical_id IS NULL UNION SELECT s.id FROM sessions s JOIN family f ON s.canonical_id=f.id WHERE s.team_id=?) SELECT id FROM family)',params:[teamId,teamId,attachmentId,teamId,teamId]},
57
+ {kind:'query',sql:'SELECT group_id FROM group_members WHERE team_id=? AND principal_id=(SELECT requester_id FROM attachments WHERE team_id=? AND id=?)',params:[teamId,teamId,attachmentId]},
58
+ ]);
59
+ return{attachment:results[0],issuer:results[1],machine:results[2],machineCredential:results[3],grant:results[4],sessions:results[5],sessionGrants:results[6],groups:results[7].map(x=>x.group_id)};
60
+ }
61
+
62
+ function authorize(state,now,{requireActive=true}={}){
63
+ const {attachment:a,issuer,machine,machineCredential,grant,sessions,sessionGrants,groups}=state;
64
+ if(!a)domain(404,'not_found','attachment not found');
65
+ if(!['pending','active'].includes(a.state))domain(403,'forbidden','attachment is closed');
66
+ if(requireActive&&a.state!=='active')domain(409,'conflict','attachment is not active');
67
+ if(!issuer||issuer.credential_revoked!==null||issuer.credential_expires!==null&&issuer.credential_expires<=now||!issuer.active)domain(403,'forbidden','attachment issuer authorization changed');
68
+ if(!machine||!machineCredential||machine.revoked_at!==null||machine.last_seen_at===null||machine.last_seen_at+45000<=now||machine.incarnation_id!==a.incarnation_id||machine.binding_incarnation!==a.incarnation_id)domain(403,'forbidden','attachment machine or incarnation changed');
69
+ if(!grant||grant.machine_id!==a.machine_id||grant.session_id!==a.session_id||grant.binding_id!==a.binding_id||grant.subject_id!==a.requester_id||grant.expires_at<=now)domain(403,'forbidden','attachment live grant changed');
70
+ const actions=JSON.parse(grant.actions_json);if(!actions.includes('attach_observe')||a.mode==='control'&&!actions.includes('interact'))domain(403,'forbidden','attachment action permission changed');
71
+ if(!sessions.length||sessions.some(session=>session.deleted_at!==null||session.id===a.session_id&&session.access_revision!==a.access_revision||!(session.visibility==='team'||session.custodian_id===a.requester_id||sessionGrants.some(g=>g.session_id===session.id&&(g.expires_at===null||g.expires_at>now)&&(g.subject_id===a.requester_id||g.subject_kind==='group'&&groups.includes(g.subject_id))))))domain(403,'forbidden','attachment Reader authorization changed');
72
+ return state;
73
+ }
74
+
75
+ async function closePolicy(api,teamId,attachmentId,reason){
76
+ await api.store.exec("UPDATE attachments SET state='revoked',closed_at=? WHERE team_id=? AND id=? AND state IN ('pending','active')",[api.clock.nowMs(),teamId,attachmentId]);
77
+ const relay=relays(api).get(attachmentId);relay?.terminal(reason);relays(api).delete(attachmentId);
78
+ }
79
+
80
+ function sse(event,data){return `event:${event}\ndata:${JSON.stringify(data)}\n\n`;}
81
+ function makeRelay(api,teamId,attachmentId,res){
82
+ let closed=false,blocked=false,queued=[],queuedBytes=0,chain=Promise.resolve();
83
+ const cleanup=()=>{clearInterval(timer);relays(api).delete(attachmentId);};
84
+ const end=()=>{if(closed)return;closed=true;cleanup();res.end();};
85
+ const terminal=code=>{if(closed)return;const bytes=sse(code,{code});closed=true;queued=[];queuedBytes=0;cleanup();res.write(bytes);res.end();};
86
+ const fresh=async()=>authorize(await snapshot(api,teamId,attachmentId),api.clock.nowMs(),{requireActive:false});
87
+ const flush=()=>{chain=chain.then(async()=>{if(closed)return;blocked=false;queuedBytes=queued.reduce((sum,item)=>sum+item.bytes,0);while(queued.length&&!blocked){try{await fresh();}catch{await closePolicy(api,teamId,attachmentId,'access_revoked');return;}const item=queued.shift();queuedBytes-=item.bytes;blocked=!res.write(item.value);if(blocked)queuedBytes+=item.bytes;}if(closed&&!queued.length)res.end();});};
88
+ res.on?.('drain',flush);res.on?.('close',()=>{closed=true;cleanup();});
89
+ const timer=setInterval(()=>{chain=chain.then(async()=>{if(closed)return;try{await fresh();}catch{await closePolicy(api,teamId,attachmentId,'access_revoked');}});},1000);timer.unref?.();
90
+ return{
91
+ terminal,
92
+ publish(frame){chain=chain.then(async()=>{if(closed)return;try{authorize(await snapshot(api,teamId,attachmentId),api.clock.nowMs());}catch{await closePolicy(api,teamId,attachmentId,'access_revoked');return;}const value=sse('runtime',frame),bytes=Buffer.byteLength(value);if(blocked){if(queuedBytes+bytes>LIMIT){terminal('resync_required');return;}queued.push({value,bytes});queuedBytes+=bytes;}else if(!res.write(value)){blocked=true;queuedBytes=bytes;}});return chain;},
93
+ detach(){queued=[];queuedBytes=0;end();},
94
+ };
95
+ }
96
+
97
+ export async function attachmentStreamRoute({api,req,res,teamId,attachmentId,token,fail}){
98
+ const now=api.clock.nowMs(),digest=hashToken(token??'');
99
+ const guards=[
100
+ {sql:"SELECT 1 ok FROM attachments WHERE team_id=? AND id=? AND state IN ('pending','active') AND stream_token_hash=? AND stream_token_consumed_at IS NULL AND stream_token_expires_at>?",params:[teamId,attachmentId,digest,now],status:401,code:'authentication_required',message:'attachment stream credential is invalid or consumed'},
101
+ ];
102
+ try{await api.store.guardedTransaction(guards,[{sql:'UPDATE attachments SET stream_token_consumed_at=? WHERE team_id=? AND id=? AND stream_token_consumed_at IS NULL',params:[now,teamId,attachmentId]}]);authorize(await snapshot(api,teamId,attachmentId),now,{requireActive:false});}catch(error){fail(error.status??401,error.code??'authentication_required',error.message);}
103
+ if(relays(api).has(attachmentId))fail(409,'conflict','attachment already has an observer');
104
+ res.writeHead(200,{'Content-Type':'text/event-stream; charset=utf-8','Cache-Control':'no-store','Connection':'keep-alive'});res.write(': attached\n\n');relays(api).set(attachmentId,makeRelay(api,teamId,attachmentId,res));
105
+ }
106
+
107
+ export async function attachmentFramesRoute({api,req,res,auth,machineId,attachmentId,requestId,fail,send,result}){
108
+ if(auth.kind!=='machine'||auth.machine_id!==machineId)fail(403,'forbidden','exact machine credential required');
109
+ const incarnation=req.headers['x-teams-incarnation'];if(typeof incarnation!=='string'||!incarnation)fail(400,'invalid_request','X-Teams-Incarnation is required');
110
+ if(!/^application\/x-ndjson(?:\s*;|$)/i.test(req.headers['content-type']??''))fail(415,'invalid_request','application/x-ndjson is required');
111
+ let admission=await snapshot(api,auth.team_id,attachmentId);if(admission.attachment?.state==='pending')fail(409,'conflict','attachment is awaiting native activation');try{admission=authorize(admission,api.clock.nowMs());}catch(error){await closePolicy(api,auth.team_id,attachmentId,'access_revoked');throw error;}if(admission.attachment.machine_id!==machineId||admission.attachment.incarnation_id!==incarnation)fail(403,'forbidden','attachment belongs to another machine incarnation');const admissionCredential=await api.store.one("SELECT 1 ok FROM credentials WHERE id=? AND kind='machine' AND machine_id=? AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at>?)",[auth.credential_id,machineId,api.clock.nowMs()]);if(!admissionCredential)fail(403,'forbidden','machine credential changed');
112
+ const activeRequests=api.attachmentFrameRequests??=new Set();activeRequests.add(req);req.once?.('close',()=>activeRequests.delete(req));
113
+ let buffer=Buffer.alloc(0),accepted=0,last;
114
+ const consume=async line=>{if(!line.length)return;let frame;try{frame=JSON.parse(decoder.decode(line));}catch{fail(400,'invalid_request','invalid NDJSON runtime event');}let state=await snapshot(api,auth.team_id,attachmentId);if(state.attachment?.state==='pending')fail(409,'conflict','attachment is awaiting native activation');try{state=authorize(state,api.clock.nowMs());}catch(error){await closePolicy(api,auth.team_id,attachmentId,'access_revoked');throw error;}if(state.attachment.machine_id!==machineId||state.attachment.incarnation_id!==incarnation)fail(403,'forbidden','attachment belongs to another machine incarnation');
115
+ const credential=await api.store.one("SELECT 1 ok FROM credentials WHERE id=? AND kind='machine' AND machine_id=? AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at>?)",[auth.credential_id,machineId,api.clock.nowMs()]);if(!credential)fail(403,'forbidden','machine credential changed');
116
+ const descriptor=JSON.parse(state.machine.descriptor_json),nativeSessionId=descriptor.native_runtime_id;validateAttachmentFrame(frame,{nativeSessionId,lastSequence:state.attachment.last_sequence});
117
+ const now=api.clock.nowMs(),guards=[
118
+ {sql:"SELECT 1 ok FROM attachments WHERE team_id=? AND id=? AND state='active' AND machine_id=? AND incarnation_id=? AND last_sequence=?",params:[auth.team_id,attachmentId,machineId,incarnation,state.attachment.last_sequence],status:409,code:'conflict',message:'attachment sequence changed'},
119
+ {sql:"SELECT 1 ok FROM credentials WHERE id=? AND kind='machine' AND machine_id=? AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at>?)",params:[auth.credential_id,machineId,now],status:403,code:'forbidden',message:'machine credential changed'},
120
+ {sql:"SELECT 1 ok FROM credentials c JOIN memberships m ON m.team_id=? AND m.principal_id=c.principal_id WHERE c.id=? AND c.principal_id=? AND c.kind IN ('user','service') AND (c.team_id IS NULL OR c.team_id=?) AND c.revoked_at IS NULL AND (c.expires_at IS NULL OR c.expires_at>?) AND m.active=1",params:[auth.team_id,state.attachment.issuer_credential_id,state.attachment.requester_id,auth.team_id,now],status:403,code:'forbidden',message:'attachment issuer authorization changed'},
121
+ {sql:'SELECT 1 ok FROM machines WHERE team_id=? AND id=? AND revoked_at IS NULL AND incarnation_id=? AND last_seen_at+45000>?',params:[auth.team_id,machineId,incarnation,now],status:403,code:'forbidden',message:'attachment machine changed'},
122
+ {sql:'SELECT 1 ok FROM live_grants WHERE team_id=? AND id=? AND machine_id=? AND session_id=? AND binding_id=? AND subject_id=? AND revision=? AND expires_at>?',params:[auth.team_id,state.grant.id,machineId,state.attachment.session_id,state.attachment.binding_id,state.attachment.requester_id,state.grant.revision,now],status:403,code:'forbidden',message:'attachment live grant changed'},
123
+ ...state.sessions.map(session=>({sql:`SELECT 1 ok FROM sessions s WHERE s.team_id=? AND s.id=? AND s.deleted_at IS NULL AND (?=0 OR s.access_revision=?) AND (s.visibility='team' OR s.custodian_id=? OR EXISTS (SELECT 1 FROM session_grants g WHERE g.team_id=s.team_id AND g.session_id=s.id AND (g.expires_at IS NULL OR g.expires_at>?) AND (g.subject_kind='principal' AND g.subject_id=? OR g.subject_kind='group' AND EXISTS (SELECT 1 FROM group_members gm WHERE gm.team_id=s.team_id AND gm.group_id=g.subject_id AND gm.principal_id=?))))`,params:[auth.team_id,session.id,session.id===state.attachment.session_id?1:0,state.attachment.access_revision,state.attachment.requester_id,now,state.attachment.requester_id,state.attachment.requester_id],status:403,code:'forbidden',message:'attachment Reader authorization changed'})),
124
+ ];await api.store.guardedTransaction(guards,[{sql:'UPDATE attachments SET last_sequence=? WHERE team_id=? AND id=?',params:[frame.sequence,auth.team_id,attachmentId]}]);last=frame.sequence;accepted++;await relays(api).get(attachmentId)?.publish(frame);};
125
+ for await(const chunk of req){const bytes=Buffer.isBuffer(chunk)?chunk:Buffer.from(chunk);let offset=0;while(offset<bytes.length){const newline=bytes.indexOf(10,offset),end=newline===-1?bytes.length:newline,part=bytes.subarray(offset,end);if(buffer.length+part.length>LIMIT){await closePolicy(api,auth.team_id,attachmentId,'resync_required');fail(413,'request_too_large','runtime event exceeds 1 MiB');}if(part.length)buffer=buffer.length?Buffer.concat([buffer,part],buffer.length+part.length):part;if(newline===-1)break;await consume(buffer);buffer=Buffer.alloc(0);offset=newline+1;}}
126
+ if(buffer.length)await consume(buffer);return send(res,200,result(api,auth,{last_sequence:last??null,accepted}),requestId);
127
+ }
128
+
129
+ export function closeAttachmentRelays(api,reason='access_revoked'){for(const relay of relays(api).values())relay.terminal(reason);relays(api).clear();for(const request of api.attachmentFrameRequests??[])request.destroy?.();api.attachmentFrameRequests?.clear();}
130
+
131
+ export async function recheckAttachmentRelays(api){for(const [id,relay] of relays(api)){const row=await api.store.one('SELECT team_id FROM attachments WHERE id=?',[id]);if(!row){relay.terminal('access_revoked');relays(api).delete(id);continue;}try{authorize(await snapshot(api,row.team_id,id),api.clock.nowMs(),{requireActive:false});}catch{await closePolicy(api,row.team_id,id,'access_revoked');}}}
132
+
133
+ export async function detachAttachmentRoute({api,res,auth,attachmentId,requestId,fail,send,guardedMutation}){
134
+ const row=await api.store.one('SELECT * FROM attachments WHERE team_id=? AND id=? AND requester_id=?',[auth.team_id,attachmentId,auth.principal_id]);if(!row)fail(404,'not_found','attachment not found');
135
+ await guardedMutation(api,res,auth,[{sql:'SELECT 1 ok FROM attachments WHERE team_id=? AND id=? AND requester_id=?',params:[auth.team_id,attachmentId,auth.principal_id],status:404,code:'not_found',message:'attachment not found'}],[{sql:"UPDATE attachments SET state='detached',closed_at=? WHERE team_id=? AND id=? AND state IN ('pending','active')",params:[api.clock.nowMs(),auth.team_id,attachmentId]}],{status:204,body:null,targetId:attachmentId});
136
+ relays(api).get(attachmentId)?.detach();relays(api).delete(attachmentId);return send(res,204,null,requestId);
137
+ }
138
+
139
+ export async function closeMachineAttachmentRoute({api,req,res,auth,machineId,attachmentId,requestId,fail,send}){
140
+ if(auth.kind!=='machine'||auth.machine_id!==machineId)fail(403,'forbidden','exact machine credential required');
141
+ const incarnation=req.headers['x-teams-incarnation'];if(typeof incarnation!=='string'||!incarnation)fail(400,'invalid_request','X-Teams-Incarnation is required');
142
+ const row=await api.store.one('SELECT id,state FROM attachments WHERE team_id=? AND id=? AND machine_id=? AND incarnation_id=?',[auth.team_id,attachmentId,machineId,incarnation]);
143
+ if(!row)fail(404,'not_found','attachment not found');
144
+ if(['pending','active'].includes(row.state))await api.store.guardedTransaction([
145
+ {sql:"SELECT 1 ok FROM credentials WHERE id=? AND kind='machine' AND team_id=? AND machine_id=? AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at>?)",params:[auth.credential_id,auth.team_id,machineId,api.clock.nowMs()],status:403,code:'forbidden',message:'machine credential changed'},
146
+ {sql:"SELECT 1 ok FROM attachments WHERE team_id=? AND id=? AND machine_id=? AND incarnation_id=? AND state IN ('pending','active')",params:[auth.team_id,attachmentId,machineId,incarnation],status:409,code:'conflict',message:'attachment changed'},
147
+ ],[{sql:"UPDATE attachments SET state='closed',closed_at=? WHERE team_id=? AND id=?",params:[api.clock.nowMs(),auth.team_id,attachmentId]}]);
148
+ relays(api).get(attachmentId)?.detach();relays(api).delete(attachmentId);return send(res,204,null,requestId);
149
+ }
@@ -0,0 +1,7 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { canRead } from '@volter/teams/server/auth';
3
+ import { cursorToken, cursorHash } from '@volter/teams/server/wire';
4
+ export { cursorToken, cursorHash };
5
+ export const normalizeSearch = text => String(text).normalize('NFC').toLowerCase();
6
+ export function descriptor(row, auth, grants=[]) { const projection=JSON.parse(row.descriptor_json); projection.actions ??= {}; const read=canRead(auth,row,grants); for(const action of ['read','follow','export_text','export_native','manage','attach_observe','interact','approve','interrupt','terminate','continue']) projection.actions[action] ??={allowed:action==='read'||action==='follow'||action==='export_text'?read:false,reason:read?(action==='read'||action==='follow'||action==='export_text'?null:'unsupported'):'forbidden'}; return projection; }
7
+ export function textFromContent(content) { if(typeof content==='string')return content; if(!Array.isArray(content))return ''; return content.filter(x=>x&&typeof x==='object'&&x.type==='text'&&typeof x.text==='string').map(x=>x.text).join('\n'); }
@@ -0,0 +1,56 @@
1
+ import {createHash} from 'node:crypto';
2
+ const PLAN_TTL=15*60_000;
3
+ const STRENGTH={semantic:0,value_lossless:1,byte_lossless:2};
4
+ const fail=(status,code,message)=>{throw Object.assign(new Error(message),{status,code});};
5
+ const parse=value=>JSON.parse(value);
6
+ const operation=row=>{const value=parse(row.value_json);return{id:row.id,state:row.state,result:value.result??null,error:value.error??null};};
7
+ const publicContinuation=value=>({id:value.id,source_id:value.source_id,source_revision:value.source_revision,access_revision:value.access_revision,state:value.state,requester_id:value.requester_id,destination:value.destination,harness:value.harness,fidelity:value.fidelity,expires_at:value.expires_at,residue:value.residue,operation_id:value.operation_id,new_session_id:value.new_session_id,destination_authorization:value.destination_authorization});
8
+ const errorShape=value=>value&&typeof value==='object'&&!Array.isArray(value)&&typeof value.code==='string'&&typeof value.message==='string'&&typeof value.retryable==='boolean';
9
+
10
+ async function family(api,teamId,sessionId){return api.store.query(`WITH RECURSIVE ancestors(id,canonical_id) AS (SELECT id,canonical_id FROM sessions WHERE team_id=? AND id=? UNION SELECT s.id,s.canonical_id FROM sessions s JOIN ancestors a ON s.id=a.canonical_id WHERE s.team_id=?), family(id) AS (SELECT id FROM ancestors WHERE canonical_id IS NULL UNION SELECT s.id FROM sessions s JOIN family f ON s.canonical_id=f.id WHERE s.team_id=?) SELECT s.* FROM sessions s JOIN family f ON f.id=s.id WHERE s.team_id=?`,[teamId,sessionId,teamId,teamId,teamId]);}
11
+ async function readerGuards(api,auth,sessionId,accessRevision,now){const sessions=await family(api,auth.team_id,sessionId);if(!sessions.length)fail(404,'not_found','not found');return sessions.map(session=>({sql:`SELECT 1 ok FROM sessions s WHERE s.team_id=? AND s.id=? AND s.deleted_at IS NULL AND (?=0 OR s.access_revision=?) AND (s.visibility='team' OR s.custodian_id=? OR EXISTS (SELECT 1 FROM session_grants g WHERE g.team_id=s.team_id AND g.session_id=s.id AND (g.expires_at IS NULL OR g.expires_at>?) AND (g.subject_kind='principal' AND g.subject_id=? OR g.subject_kind='group' AND EXISTS (SELECT 1 FROM group_members gm WHERE gm.team_id=s.team_id AND gm.group_id=g.subject_id AND gm.principal_id=?))))`,params:[auth.team_id,session.id,session.id===sessionId?1:0,accessRevision,auth.principal_id,now,auth.principal_id,auth.principal_id],status:404,code:'not_found',message:'source session access changed'}));}
12
+ const credentialGuard=(auth,now)=>({sql:"SELECT 1 ok FROM credentials c JOIN memberships m ON m.team_id=? AND m.principal_id=c.principal_id WHERE c.id=? AND c.principal_id=? AND c.kind IN ('user','service') AND (c.team_id IS NULL OR c.team_id=?) AND c.revoked_at IS NULL AND (c.expires_at IS NULL OR c.expires_at>?) AND m.active=1",params:[auth.team_id,auth.credential_id,auth.principal_id,auth.team_id,now],status:401,code:'authentication_required',message:'requester authorization changed'});
13
+
14
+ async function capturedBundle(api,{teamId,sessionId,revision,harness,requested}){
15
+ const bundles=await api.store.query(`SELECT a.bundle_id,a.fidelity,a.residue_json,count(*) file_count,sum(CASE WHEN a.file_role='primary' THEN 1 ELSE 0 END) primary_count FROM session_artifacts a JOIN uploads u ON u.team_id=a.team_id AND u.id=a.upload_id AND u.committed=1 WHERE a.team_id=? AND a.session_id=? AND a.revision=? AND a.provenance='capture' AND a.format=? GROUP BY a.bundle_id,a.fidelity,a.residue_json ORDER BY CASE a.fidelity WHEN 'byte_lossless' THEN 2 WHEN 'value_lossless' THEN 1 ELSE 0 END DESC,a.bundle_id`,[teamId,sessionId,revision,harness]);
16
+ for(const bundle of bundles){if(bundle.primary_count!==1||STRENGTH[bundle.fidelity]<STRENGTH[requested])continue;const rows=await api.store.query(`SELECT a.file_role,u.id,u.manifest_json FROM session_artifacts a JOIN uploads u ON u.team_id=a.team_id AND u.id=a.upload_id WHERE a.team_id=? AND a.session_id=? AND a.revision=? AND a.provenance='capture' AND a.bundle_id=? AND u.committed=1 ORDER BY a.file_role,u.id`,[teamId,sessionId,revision,bundle.bundle_id]);if(rows.length!==bundle.file_count)continue;const files=rows.map(row=>{const manifest=parse(row.manifest_json);return{upload_id:row.id,relative_name:manifest.relative_name,media_type:manifest.media_type,byte_length:manifest.byte_length,sha256:manifest.sha256,role:row.file_role};});return{bundle_id:bundle.bundle_id,artifact:{kind:'export',source_session_id:sessionId,source_revision:revision,format:harness,fidelity:bundle.fidelity,residue:parse(bundle.residue_json),coverage:null,files}};}
17
+ return null;
18
+ }
19
+ function validateDestination(value,exact,routeFail){if(!value||typeof value!=='object'||Array.isArray(value))routeFail(400,'invalid_request','destination is required');exact(value,['machine_id','root_id','cwd'],'destination');if(typeof value.cwd!=='string'||!value.cwd||/[\0-\x1f\x7f]/.test(value.cwd)||typeof value.machine_id!=='string'||!value.machine_id)routeFail(400,'invalid_request','invalid continuation destination');const unixAbsolute=value.cwd.startsWith('/'),windowsAbsolute=/^[A-Za-z]:[\\/]/.test(value.cwd)||value.cwd.startsWith('\\\\');if(value.machine_id==='local'){if(value.root_id!==null||!unixAbsolute&&!windowsAbsolute)routeFail(400,'invalid_request','local continuation cwd must be absolute');}else{if(typeof value.root_id!=='string'||!value.root_id||unixAbsolute||windowsAbsolute||value.cwd.includes('\\')||value.cwd.split('/').some(part=>part==='..'||part===''))routeFail(400,'invalid_request','remote continuation cwd must be root-relative');}}
20
+ async function loadForRequester(api,auth,id){const row=await api.store.one('SELECT * FROM continuations WHERE team_id=? AND id=? AND requester_id=?',[auth.team_id,id,auth.principal_id]);if(!row)fail(404,'not_found','continuation not found');const value=parse(row.value_json);await api.store.authorizedSession({teamId:auth.team_id,principalId:auth.principal_id,credentialId:auth.credential_id,ref:row.source_session_id,now:api.clock.nowMs()});return{row,value};}
21
+ async function existingOperation(api,auth,value){const row=await api.store.one('SELECT * FROM operations WHERE team_id=? AND id=? AND requester_id=?',[auth.team_id,value.operation_id,auth.principal_id]);if(!row)fail(409,'conflict','consumed continuation operation is missing');return operation(row);}
22
+
23
+ export async function continuationRoute({api,req,res,auth,sessionId,id,action,requestId,parseJson,exact,fail:routeFail,send,result,guardedMutation}){
24
+ const now=api.clock.nowMs();
25
+ if(sessionId){
26
+ const body=await parseJson(req);exact(body,['source_revision','destination','harness','fidelity']);validateDestination(body.destination,exact,routeFail);const requested=body.fidelity??'value_lossless';if(!Number.isSafeInteger(body.source_revision)||body.source_revision<0||typeof body.harness!=='string'||!['byte_lossless','value_lossless'].includes(requested))routeFail(400,'invalid_request','invalid continuation');
27
+ const session=await api.store.authorizedSession({teamId:auth.team_id,principalId:auth.principal_id,credentialId:auth.credential_id,ref:sessionId,now});const version=await api.store.one('SELECT descriptor_json FROM session_versions WHERE team_id=? AND session_id=? AND revision=?',[auth.team_id,session.id,body.source_revision]);if(!version)routeFail(404,'not_found','source revision not found');const bundle=await capturedBundle(api,{teamId:auth.team_id,sessionId:session.id,revision:body.source_revision,harness:body.harness,requested});if(bundle)bundle.artifact.coverage=parse(version.descriptor_json).coverage;
28
+ const continuationId=api.ids.next('continuation'),value={id:continuationId,source_id:session.id,source_revision:body.source_revision,access_revision:session.access_revision,state:bundle?'ready':'refused',requester_id:auth.principal_id,destination:body.destination,harness:body.harness,fidelity:requested,expires_at:now+PLAN_TTL,residue:bundle?[...new Set([...bundle.artifact.residue,...(requested==='value_lossless'?['fresh_native_identity','destination_cwd']:[])])]:['native_bundle_unavailable_or_fidelity_insufficient'],operation_id:null,new_session_id:null,destination_authorization:null,issuer_credential_id:auth.credential_id,bundle_id:bundle?.bundle_id??null,artifact:bundle?.artifact??null,completion:null};const response=result(api,auth,publicContinuation(value));
29
+ await guardedMutation(api,res,auth,[credentialGuard(auth,now),...await readerGuards(api,auth,session.id,session.access_revision,now),{sql:'SELECT 1 ok FROM session_versions WHERE team_id=? AND session_id=? AND revision=?',params:[auth.team_id,session.id,body.source_revision],status:404,code:'not_found',message:'source revision changed'}],[{sql:'INSERT INTO continuations(team_id,id,requester_id,source_session_id,state,value_json,expires_at) VALUES (?,?,?,?,?,?,?)',params:[auth.team_id,continuationId,auth.principal_id,session.id,value.state,JSON.stringify(value),value.expires_at]}],{status:202,body:response,targetId:continuationId});return send(res,202,response,requestId);
30
+ }
31
+ if(action==='authorize-destination'){
32
+ const body=await parseJson(req);exact(body,['expires_at']);if(!Number.isSafeInteger(body.expires_at))routeFail(400,'invalid_request','expires_at is required');const row=await api.store.one('SELECT * FROM continuations WHERE team_id=? AND id=?',[auth.team_id,id]);if(!row)routeFail(404,'not_found','continuation not found');const value=parse(row.value_json),machineId=value.destination?.machine_id;if(machineId==='local'||value.state!=='ready'||value.operation_id!==null||body.expires_at<=now||body.expires_at>value.expires_at)routeFail(409,'conflict','continuation cannot be authorized');const machine=await api.store.one('SELECT * FROM machines WHERE team_id=? AND id=? AND custodian_id=? AND revoked_at IS NULL',[auth.team_id,machineId,auth.principal_id]);if(!machine)routeFail(404,'not_found','destination machine not found');const root=await api.store.one('SELECT value_json FROM machine_roots WHERE team_id=? AND machine_id=? AND id=?',[auth.team_id,machineId,value.destination.root_id]);if(!root||!parse(root.value_json).harnesses?.includes(value.harness))routeFail(403,'forbidden','destination root or harness is unavailable');value.destination_authorization={principal_id:value.requester_id,approved_by:auth.principal_id,expires_at:body.expires_at};const response=result(api,auth,publicContinuation(value));await guardedMutation(api,res,auth,[credentialGuard(auth,now),{sql:'SELECT 1 ok FROM machines WHERE team_id=? AND id=? AND custodian_id=? AND revoked_at IS NULL AND incarnation_id IS NOT NULL AND last_seen_at+45000>?',params:[auth.team_id,machineId,auth.principal_id,now],status:403,code:'forbidden',message:'destination machine changed'},{sql:'SELECT 1 ok FROM machine_roots WHERE team_id=? AND machine_id=? AND id=?',params:[auth.team_id,machineId,value.destination.root_id],status:403,code:'forbidden',message:'destination root changed'},{sql:'SELECT 1 ok FROM continuations WHERE team_id=? AND id=? AND state=\'ready\' AND expires_at>?',params:[auth.team_id,id,now],status:409,code:'conflict',message:'continuation changed'}],[{sql:'UPDATE continuations SET value_json=? WHERE team_id=? AND id=?',params:[JSON.stringify(value),auth.team_id,id]}],{status:200,body:response,targetId:id});return send(res,200,response,requestId);
33
+ }
34
+ const item=await loadForRequester(api,auth,id),value=item.value;
35
+ if(req.method==='GET'&&!action)return send(res,200,result(api,auth,publicContinuation(value.expires_at<=now&&value.operation_id===null?{...value,state:'expired'}:value)),requestId);
36
+ if(action==='launch'){
37
+ if(value.operation_id)return send(res,202,result(api,auth,await existingOperation(api,auth,value)),requestId);
38
+ if(value.state!=='ready')routeFail(409,'conflict','continuation is not ready');if(value.expires_at<=now)routeFail(409,'conflict','continuation expired');
39
+ const bundle=await capturedBundle(api,{teamId:auth.team_id,sessionId:value.source_id,revision:value.source_revision,harness:value.harness,requested:value.fidelity});if(!bundle||bundle.bundle_id!==value.bundle_id||JSON.stringify(bundle.artifact.files)!==JSON.stringify(value.artifact.files))routeFail(409,'conflict','prepared artifact changed');const operationId=api.ids.next('operation');let opResult,opState,statements=[],destinationGuards=[];
40
+ if(value.destination.machine_id==='local'){opState='running';opResult={kind:'local_launch',continuation_id:id,operation_id:operationId,harness:value.harness,fidelity:value.fidelity,artifact:value.artifact,destination:value.destination,expires_at:value.expires_at};value.state='running';}
41
+ else{const authorization=value.destination_authorization;if(!authorization||authorization.principal_id!==auth.principal_id||authorization.expires_at<=now)routeFail(403,'forbidden','destination authorization is missing or expired');const machine=await api.store.one('SELECT * FROM machines WHERE team_id=? AND id=? AND custodian_id=? AND revoked_at IS NULL',[auth.team_id,value.destination.machine_id,authorization.approved_by]);if(!machine?.incarnation_id||machine.last_seen_at+45000<=now)routeFail(403,'forbidden','destination authorization is missing or expired');const root=await api.store.one('SELECT value_json FROM machine_roots WHERE team_id=? AND machine_id=? AND id=?',[auth.team_id,machine.id,value.destination.root_id]);if(!root||!parse(root.value_json).harnesses?.includes(value.harness))routeFail(403,'forbidden','destination root or harness changed');destinationGuards=[{sql:'SELECT 1 ok FROM machines WHERE team_id=? AND id=? AND custodian_id=? AND revoked_at IS NULL AND incarnation_id=? AND last_seen_at+45000>?',params:[auth.team_id,machine.id,authorization.approved_by,machine.incarnation_id,now],status:403,code:'forbidden',message:'destination machine changed'},{sql:'SELECT 1 ok FROM machine_roots WHERE team_id=? AND machine_id=? AND id=?',params:[auth.team_id,machine.id,value.destination.root_id],status:403,code:'forbidden',message:'destination root changed'}];opState='pending';opResult=null;value.state='launching';const effectId=api.ids.next('event');statements.push({sql:'INSERT INTO native_effects(team_id,id,operation_id,principal_id,issuer_credential_id,machine_id,binding_id,incarnation_id,session_id,access_revision,grant_id,expires_at,kind,payload_json,state) VALUES (?,?,?,?,?,?,NULL,?,?,?,NULL,?,?,?,\'pending\')',params:[auth.team_id,effectId,operationId,auth.principal_id,auth.credential_id,machine.id,machine.incarnation_id,value.source_id,value.access_revision,Math.min(value.expires_at,authorization.expires_at),'continue',JSON.stringify({continuation_id:id})]});}
42
+ value.operation_id=operationId;const operationValue={result:opResult,error:null},op=operation({id:operationId,state:opState,value_json:JSON.stringify(operationValue)}),response=result(api,auth,op);statements.unshift({sql:'INSERT INTO operations(team_id,id,requester_id,session_id,state,value_json,created_at) VALUES (?,?,?,?,?,?,?)',params:[auth.team_id,operationId,auth.principal_id,value.source_id,opState,JSON.stringify(operationValue),now]});statements.push({sql:'UPDATE continuations SET state=?,value_json=? WHERE team_id=? AND id=? AND state=\'ready\'',params:[value.state,JSON.stringify(value),auth.team_id,id]});const guards=[credentialGuard(auth,now),...destinationGuards,...await readerGuards(api,auth,value.source_id,value.access_revision,now),{sql:'SELECT 1 ok FROM continuations WHERE team_id=? AND id=? AND requester_id=? AND state=\'ready\' AND expires_at>? AND json_extract(value_json,\'$.operation_id\') IS NULL',params:[auth.team_id,id,auth.principal_id,now],status:409,code:'conflict',message:'continuation was consumed'}];try{await guardedMutation(api,res,auth,guards,statements,{status:202,body:response,targetId:operationId});return send(res,202,response,requestId);}catch(error){if(error.code!=='conflict')throw error;const consumed=parse((await api.store.one('SELECT value_json FROM continuations WHERE team_id=? AND id=?',[auth.team_id,id])).value_json);if(!consumed.operation_id)throw error;return send(res,202,result(api,auth,await existingOperation(api,auth,consumed)),requestId);}
43
+ }
44
+ if(action==='complete-local'){
45
+ const body=await parseJson(req);exact(body,['operation_id','outcome','native_session_id','error']);if(body.operation_id!==value.operation_id||value.destination.machine_id!=='local'||!['running','failed','unknown'].includes(body.outcome)||body.outcome==='running'&&(typeof body.native_session_id!=='string'||!body.native_session_id||body.error!==null)||body.outcome==='failed'&&(body.native_session_id!==null||!errorShape(body.error))||body.outcome==='unknown'&&(body.native_session_id!==null||body.error!==null))routeFail(409,'conflict','invalid local completion');const existing=value.completion;if(existing){if(JSON.stringify(existing)!==JSON.stringify(body))routeFail(409,'conflict','local completion differs from prior report');return send(res,200,result(api,auth,await existingOperation(api,auth,value)),requestId);}const receipt={kind:'continuation',continuation_id:id,native_session_id:body.native_session_id,new_session_id:null,verification:'caller_reported'},opState=body.outcome==='running'?'succeeded':body.outcome,valueJson={result:body.outcome==='failed'?null:receipt,error:body.outcome==='failed'?body.error:null};value.completion=body;value.state=body.outcome;value.new_session_id=null;const op=operation({id:value.operation_id,state:opState,value_json:JSON.stringify(valueJson)}),response=result(api,auth,op);await guardedMutation(api,res,auth,[credentialGuard(auth,now),...await readerGuards(api,auth,value.source_id,value.access_revision,now),{sql:"SELECT 1 ok FROM operations WHERE team_id=? AND id=? AND requester_id=? AND state='running'",params:[auth.team_id,value.operation_id,auth.principal_id],status:409,code:'conflict',message:'local operation changed'}],[{sql:'UPDATE operations SET state=?,value_json=? WHERE team_id=? AND id=?',params:[opState,JSON.stringify(valueJson),auth.team_id,value.operation_id]},{sql:'UPDATE continuations SET state=?,value_json=? WHERE team_id=? AND id=?',params:[value.state,JSON.stringify(value),auth.team_id,id]}],{status:200,body:response,targetId:id});return send(res,200,response,requestId);
46
+ }
47
+ routeFail(405,'not_found','method not allowed');
48
+ }
49
+
50
+ async function authorizedRemote(api,auth,machineId,effectId,incarnation,routeFail){
51
+ if(auth.kind!=='machine'||auth.machine_id!==machineId||typeof incarnation!=='string'||!incarnation)routeFail(403,'forbidden','exact machine credential and incarnation required');const now=api.clock.nowMs(),effect=await api.store.one("SELECT * FROM native_effects WHERE team_id=? AND id=? AND machine_id=? AND kind='continue'",[auth.team_id,effectId,machineId]);if(!effect||effect.incarnation_id!==incarnation||effect.expires_at<=now)routeFail(404,'not_found','continuation effect not found');const value=parse((await api.store.one('SELECT value_json FROM continuations WHERE team_id=? AND id=?',[auth.team_id,parse(effect.payload_json).continuation_id]))?.value_json??'null');if(!value||value.operation_id!==effect.operation_id||value.destination.machine_id!==machineId||value.expires_at<=now||value.destination_authorization?.principal_id!==effect.principal_id||value.destination_authorization?.expires_at<=now)routeFail(403,'forbidden','destination authorization changed');const machine=await api.store.one('SELECT 1 ok FROM machines m JOIN credentials c ON c.team_id=m.team_id AND c.machine_id=m.id WHERE m.team_id=? AND m.id=? AND m.custodian_id=? AND m.incarnation_id=? AND m.revoked_at IS NULL AND m.last_seen_at+45000>? AND c.id=? AND c.kind=\'machine\' AND c.revoked_at IS NULL AND (c.expires_at IS NULL OR c.expires_at>?)',[auth.team_id,machineId,value.destination_authorization.approved_by,incarnation,now,auth.credential_id,now]);if(!machine)routeFail(403,'forbidden','machine authorization changed');await api.store.authorizedSession({teamId:auth.team_id,principalId:effect.principal_id,credentialId:effect.issuer_credential_id,ref:effect.session_id,now});const root=await api.store.one('SELECT value_json FROM machine_roots WHERE team_id=? AND machine_id=? AND id=?',[auth.team_id,machineId,value.destination.root_id]);if(!root||!parse(root.value_json).harnesses?.includes(value.harness))routeFail(403,'forbidden','destination root or harness changed');const bundle=await capturedBundle(api,{teamId:auth.team_id,sessionId:value.source_id,revision:value.source_revision,harness:value.harness,requested:value.fidelity});if(!bundle||bundle.bundle_id!==value.bundle_id||JSON.stringify(bundle.artifact.files)!==JSON.stringify(value.artifact.files))routeFail(409,'conflict','prepared artifact changed');return{effect,value};
52
+ }
53
+
54
+ export async function remoteContinuationRoute({api,req,res,auth,machineId,effectId,requestId,fail,send,result}){const {value}=await authorizedRemote(api,auth,machineId,effectId,req.headers['x-teams-incarnation'],fail);const ticket={kind:'remote_launch',continuation_id:value.id,operation_id:value.operation_id,harness:value.harness,fidelity:value.fidelity,artifact:value.artifact,destination:value.destination,expires_at:Math.min(value.expires_at,value.destination_authorization.expires_at)};return send(res,200,result(api,auth,ticket),requestId);}
55
+
56
+ export async function remoteContinuationArtifactRoute({api,req,res,auth,machineId,effectId,uploadId,requestId,fail}){const {value}=await authorizedRemote(api,auth,machineId,effectId,req.headers['x-teams-incarnation'],fail);const file=value.artifact.files.find(item=>item.upload_id===uploadId);if(!file)fail(404,'not_found','artifact is not in the continuation bundle');const rows=await api.store.query('SELECT bytes FROM artifact_chunks WHERE team_id=? AND upload_id=? ORDER BY chunk_index',[auth.team_id,uploadId]),bytes=Buffer.concat(rows.map(row=>row.bytes));if(bytes.length!==file.byte_length||createHash('sha256').update(bytes).digest('hex')!==file.sha256)fail(409,'conflict','artifact bytes changed');let start=0,end=bytes.length-1,status=200;const range=req.headers.range;if(range!==undefined){const match=/^bytes=(\d*)-(\d*)$/.exec(range);if(!match||(!match[1]&&!match[2]))fail(416,'invalid_request','invalid artifact range');if(match[1]){start=Number(match[1]);end=match[2]?Number(match[2]):end;}else{const suffix=Number(match[2]);start=Math.max(0,bytes.length-suffix);}if(!Number.isSafeInteger(start)||!Number.isSafeInteger(end)||start<0||end<start||start>=bytes.length)fail(416,'invalid_request','artifact range is unsatisfiable');end=Math.min(end,bytes.length-1);status=206;}const body=bytes.subarray(start,end+1);res.writeHead(status,{'Content-Type':file.media_type,'Content-Length':String(body.length),'Accept-Ranges':'bytes','Cache-Control':'no-store','X-Request-ID':requestId,...(status===206?{'Content-Range':`bytes ${start}-${end}/${bytes.length}`}:{})});res.end(body);}
@@ -0,0 +1,4 @@
1
+ export interface CorrelationProjection { id:string; created_at:number; source_id:string; descriptor_json:string|Record<string,unknown>; source_projection_json:string|Record<string,unknown> }
2
+ export declare function chooseCanonical<T extends Pick<CorrelationProjection,'id'|'created_at'>>(left:T,right:T):T;
3
+ export declare function projectionPriority(row:Pick<CorrelationProjection,'descriptor_json'|'source_projection_json'>):number;
4
+ export declare function choosePrimary<T extends CorrelationProjection>(left:T,right:T):T;
@@ -0,0 +1,17 @@
1
+ export function chooseCanonical(left,right){
2
+ return left.created_at<right.created_at?left:left.created_at>right.created_at?right:left.id.localeCompare(right.id)<=0?left:right;
3
+ }
4
+
5
+ export function projectionPriority(row){
6
+ const projection=typeof row.source_projection_json==='string'?JSON.parse(row.source_projection_json):row.source_projection_json;
7
+ const descriptor=typeof row.descriptor_json==='string'?JSON.parse(row.descriptor_json):row.descriptor_json;
8
+ const telemetry=projection?.kind==='telemetry';
9
+ const full=descriptor?.coverage?.completeness==='complete';
10
+ return telemetry?0:full?2:1;
11
+ }
12
+
13
+ export function choosePrimary(left,right){
14
+ const lp=projectionPriority(left),rp=projectionPriority(right);
15
+ if(lp!==rp)return lp>rp?left:right;
16
+ return String(left.source_id).localeCompare(String(right.source_id))<=0?left:right;
17
+ }
@@ -0,0 +1,119 @@
1
+ function fail(status, code, message) {
2
+ const error = new Error(message);
3
+ Object.assign(error, { status, code });
4
+ throw error;
5
+ }
6
+
7
+ const json = value => JSON.parse(value);
8
+ const operation = row => {
9
+ const value = json(row.value_json);
10
+ return { id: row.id, state: row.state, result: value.result ?? null, error: value.error ?? null };
11
+ };
12
+ const delivery = row => ({
13
+ effect: {
14
+ id: row.id, principal_id: row.principal_id, team_id: row.team_id, machine_id: row.machine_id,
15
+ binding_id: row.binding_id, incarnation_id: row.incarnation_id, session_id: row.session_id,
16
+ access_revision: row.access_revision, grant_id: row.grant_id, expires_at: row.expires_at,
17
+ kind: row.kind, payload: json(row.payload_json),
18
+ },
19
+ operation_id: row.operation_id,
20
+ state: row.state,
21
+ });
22
+
23
+ function currentMachine(db, { teamId, machineId, machineCredentialId, incarnationId, now }) {
24
+ const row = db.get(`SELECT m.* FROM machines m JOIN credentials c ON c.team_id=m.team_id AND c.machine_id=m.id
25
+ JOIN memberships mem ON mem.team_id=m.team_id AND mem.principal_id=m.custodian_id
26
+ WHERE m.team_id=? AND m.id=? AND m.revoked_at IS NULL AND m.incarnation_id=?
27
+ AND m.last_seen_at+45000>? AND c.id=? AND c.kind='machine' AND c.revoked_at IS NULL
28
+ AND (c.expires_at IS NULL OR c.expires_at>?) AND mem.active=1`,
29
+ [teamId,machineId,incarnationId,now,machineCredentialId,now]);
30
+ if(!row)fail(403,'forbidden','machine credential, incarnation, or enrollment changed');
31
+ return row;
32
+ }
33
+
34
+ function currentEffectAuthorization(db, row, now) {
35
+ const issuer=db.get(`SELECT 1 ok FROM credentials c JOIN memberships m ON m.team_id=? AND m.principal_id=c.principal_id
36
+ WHERE c.id=? AND c.principal_id=? AND c.kind IN ('user','service') AND (c.team_id IS NULL OR c.team_id=?)
37
+ AND c.revoked_at IS NULL AND (c.expires_at IS NULL OR c.expires_at>?) AND m.active=1`,
38
+ [row.team_id,row.issuer_credential_id,row.principal_id,row.team_id,now]);
39
+ if(!issuer||row.expires_at<=now)fail(403,'forbidden','effect requester authorization expired');
40
+ const session=db.get('SELECT * FROM sessions WHERE team_id=? AND id=?',[row.team_id,row.session_id]);
41
+ if(!session||session.deleted_at!=null||session.access_revision!==row.access_revision)fail(403,'forbidden','session authorization changed');
42
+ const groups=db.all('SELECT group_id FROM group_members WHERE team_id=? AND principal_id=?',[row.team_id,row.principal_id]).map(item=>item.group_id);
43
+ const readable=candidate=>{
44
+ if(candidate.deleted_at!=null)return false;
45
+ if(candidate.visibility==='team'||candidate.custodian_id===row.principal_id)return true;
46
+ return db.all('SELECT subject_kind,subject_id,expires_at FROM session_grants WHERE team_id=? AND session_id=?',[row.team_id,candidate.id]).some(grant=>(grant.subject_kind==='principal'&&grant.subject_id===row.principal_id||grant.subject_kind==='group'&&groups.includes(grant.subject_id))&&(grant.expires_at==null||grant.expires_at>now));
47
+ };
48
+ let root=session,seen=new Set();
49
+ while(root.canonical_id){if(seen.has(root.id))fail(409,'conflict','correlation cycle');seen.add(root.id);root=db.get('SELECT * FROM sessions WHERE team_id=? AND id=?',[row.team_id,root.canonical_id]);if(!root)fail(403,'forbidden','session authorization changed');}
50
+ const family=db.all('WITH RECURSIVE family(id) AS (SELECT ? UNION SELECT s.id FROM sessions s JOIN family f ON s.canonical_id=f.id WHERE s.team_id=?) SELECT s.* FROM sessions s JOIN family f ON f.id=s.id WHERE s.team_id=?',[root.id,row.team_id,row.team_id]);
51
+ if(!family.every(readable))fail(403,'forbidden','session Reader authorization changed');
52
+ if(row.kind!=='continue'){
53
+ const grant=db.get(`SELECT g.actions_json,b.session_id binding_session FROM live_grants g JOIN bindings b ON b.team_id=g.team_id AND b.id=g.binding_id
54
+ WHERE g.team_id=? AND g.id=? AND g.machine_id=? AND g.session_id=? AND g.binding_id=? AND g.subject_id=?
55
+ AND g.expires_at>? AND b.incarnation_id=?`,
56
+ [row.team_id,row.grant_id,row.machine_id,row.session_id,row.binding_id,row.principal_id,now,row.incarnation_id]);
57
+ if(!grant)fail(403,'forbidden','live grant changed');
58
+ const payload=json(row.payload_json),required=row.kind==='attach'?(payload.mode==='control'?'interact':'attach_observe'):row.kind==='input'?'interact':row.kind;
59
+ if(!json(grant.actions_json).includes(required)||grant.binding_session!==row.session_id)fail(403,'forbidden','live grant action changed');
60
+ if(row.kind!=='attach'){const attachment=db.get("SELECT 1 ok FROM attachments WHERE team_id=? AND id=? AND requester_id=? AND machine_id=? AND binding_id=? AND session_id=? AND incarnation_id=? AND mode='control' AND state='active'",[row.team_id,payload.attachment_id,row.principal_id,row.machine_id,row.binding_id,row.session_id,row.incarnation_id]);if(!attachment)fail(403,'forbidden','action attachment changed');}
61
+ }else{
62
+ const continuationId=json(row.payload_json).continuation_id,continuation=db.get('SELECT * FROM continuations WHERE team_id=? AND id=?',[row.team_id,continuationId]);if(!continuation)fail(403,'forbidden','continuation changed');const value=json(continuation.value_json),machine=db.get('SELECT custodian_id,revoked_at,incarnation_id,last_seen_at FROM machines WHERE team_id=? AND id=?',[row.team_id,row.machine_id]),authorization=value.destination_authorization,root=value.destination?.root_id?db.get('SELECT value_json FROM machine_roots WHERE team_id=? AND machine_id=? AND id=?',[row.team_id,row.machine_id,value.destination.root_id]):null;
63
+ if(value.operation_id!==row.operation_id||value.destination?.machine_id!==row.machine_id||value.expires_at<=now||authorization?.principal_id!==row.principal_id||authorization?.approved_by!==machine?.custodian_id||authorization?.expires_at<=now||machine?.revoked_at!=null||machine?.incarnation_id!==row.incarnation_id||machine.last_seen_at+45000<=now||!root||!json(root.value_json).harnesses?.includes(value.harness))fail(403,'forbidden','continuation destination authorization changed');
64
+ }
65
+ }
66
+
67
+ export function listEffects(db,args){
68
+ currentMachine(db,args);
69
+ const limit=Math.min(args.limit??100,100),rows=db.all(`SELECT * FROM native_effects WHERE team_id=? AND machine_id=? AND incarnation_id=? AND id>? AND state IN ('pending','running')
70
+ ORDER BY id LIMIT ?`,[args.teamId,args.machineId,args.incarnationId,args.after??'',limit]);
71
+ return rows.map(delivery);
72
+ }
73
+
74
+ export function getEffect(db,args){
75
+ currentMachine(db,args);
76
+ const row=db.get('SELECT * FROM native_effects WHERE team_id=? AND machine_id=? AND id=?',[args.teamId,args.machineId,args.effectId]);
77
+ if(!row)fail(404,'not_found','effect not found');
78
+ return delivery(row);
79
+ }
80
+
81
+ export function claimEffect(db,args){
82
+ currentMachine(db,args);
83
+ const row=db.get('SELECT * FROM native_effects WHERE team_id=? AND machine_id=? AND id=?',[args.teamId,args.machineId,args.effectId]);
84
+ if(!row)fail(404,'not_found','effect not found');
85
+ if(row.incarnation_id!==args.incarnationId)fail(403,'forbidden','effect belongs to another machine incarnation');
86
+ if(row.state!=='pending')return {delivery:delivery(row),execute:false};
87
+ currentEffectAuthorization(db,row,args.now);
88
+ db.run("UPDATE native_effects SET state='running',claimed_at=? WHERE team_id=? AND id=? AND state='pending'",[args.now,args.teamId,args.effectId]);
89
+ db.run("UPDATE operations SET state='running' WHERE team_id=? AND id=? AND state='pending'",[args.teamId,row.operation_id]);
90
+ return {delivery:delivery({...row,state:'running'}),execute:true};
91
+ }
92
+
93
+ export function reportEffect(db,args){
94
+ currentMachine(db,args);
95
+ const row=db.get('SELECT * FROM native_effects WHERE team_id=? AND machine_id=? AND id=?',[args.teamId,args.machineId,args.effectId]);
96
+ if(!row)fail(404,'not_found','effect not found');
97
+ if(row.incarnation_id!==args.incarnationId||row.state==='pending')fail(409,'conflict','effect was not claimed by this incarnation');
98
+ const encoded=JSON.stringify(args.result);
99
+ if(row.result_json!==null){if(row.result_json!==encoded)fail(409,'conflict','effect result differs from prior report');return operation(db.get('SELECT * FROM operations WHERE team_id=? AND id=?',[args.teamId,row.operation_id]));}
100
+ currentEffectAuthorization(db,row,args.now);
101
+ if(args.result.state==='succeeded'&&row.kind!=='continue'){
102
+ const binding=row.binding_id&&db.get('SELECT descriptor_json FROM bindings WHERE team_id=? AND id=? AND machine_id=? AND incarnation_id=?',[args.teamId,row.binding_id,row.machine_id,row.incarnation_id]),descriptor=binding&&json(binding.descriptor_json),receipt=args.result.receipt;
103
+ if(!descriptor||!receipt||receipt.native_session_id!==descriptor.native_session_id||receipt.runtime_id!==descriptor.native_runtime_id)fail(409,'conflict','effect receipt identity differs from binding');
104
+ if(row.kind!=='attach'){const attachmentId=json(row.payload_json).attachment_id,attachment=attachmentId&&db.get("SELECT 1 ok FROM attachments WHERE team_id=? AND id=? AND machine_id=? AND binding_id=? AND incarnation_id=? AND state='active'",[args.teamId,attachmentId,row.machine_id,row.binding_id,row.incarnation_id]);if(!attachment)fail(409,'conflict','effect attachment is not active');}
105
+ }
106
+ db.run('UPDATE native_effects SET state=?,result_json=? WHERE team_id=? AND id=?',[args.result.state,encoded,args.teamId,args.effectId]);
107
+ let value;if(row.kind==='continue'){
108
+ const continuationId=json(row.payload_json).continuation_id,continuation=db.get('SELECT * FROM continuations WHERE team_id=? AND id=?',[args.teamId,continuationId]),continuationValue=json(continuation.value_json),receipt={kind:'continuation',continuation_id:continuationId,native_session_id:args.result.state==='succeeded'?args.result.receipt.native_session_id:null,new_session_id:null,verification:'connector_reported'};value={result:args.result.state==='succeeded'?receipt:null,error:args.result.state==='failed'?args.result.error:null};continuationValue.state=args.result.state==='succeeded'?'running':args.result.state;continuationValue.new_session_id=null;continuationValue.remote_result=args.result;db.run('UPDATE continuations SET state=?,value_json=? WHERE team_id=? AND id=?',[continuationValue.state,JSON.stringify(continuationValue),args.teamId,continuationId]);
109
+ }else value={result:args.result.state==='succeeded'?args.result.receipt:null,error:args.result.state==='failed'?args.result.error:null};
110
+ db.run('UPDATE operations SET state=?,value_json=? WHERE team_id=? AND id=?',[args.result.state,JSON.stringify(value),args.teamId,row.operation_id]);
111
+ if(row.kind==='attach'){
112
+ const attachmentId=json(row.payload_json).attachment_id;
113
+ db.run("UPDATE attachments SET state=?,closed_at=? WHERE team_id=? AND id=? AND state='pending'",[args.result.state==='succeeded'?'active':'closed',args.result.state==='succeeded'?null:args.now,args.teamId,attachmentId]);
114
+ }
115
+ return operation({...db.get('SELECT * FROM operations WHERE team_id=? AND id=?',[args.teamId,row.operation_id]),state:args.result.state,value_json:JSON.stringify(value)});
116
+ }
117
+
118
+ export function effectJson(row){return delivery(row);}
119
+ export function operationJson(row){return operation(row);}
@@ -0,0 +1,97 @@
1
+ export async function machineEffectRoute({api,req,res,auth,machineId,effectId,action,url,requestId,parseJson,exact,fail,send,result}) {
2
+ if(auth.kind!=='machine'||auth.machine_id!==machineId)fail(403,'forbidden','exact machine credential required');
3
+ const incarnationId=req.headers['x-teams-incarnation'];
4
+ if(typeof incarnationId!=='string'||!incarnationId)fail(400,'invalid_request','X-Teams-Incarnation is required');
5
+ const common={teamId:auth.team_id,machineId,machineCredentialId:auth.credential_id,incarnationId,now:api.clock.nowMs()};
6
+ if(req.method==='GET'&&!effectId){
7
+ const limit=url.searchParams.has('limit')?Number(url.searchParams.get('limit')):100,after=url.searchParams.get('after')??'';
8
+ if([...url.searchParams.keys()].some(key=>!['after','limit'].includes(key))||!Number.isSafeInteger(limit)||limit<1||limit>100)fail(400,'invalid_request','invalid effects query');
9
+ const items=await api.store.listEffects({...common,after,limit});
10
+ return send(res,200,{version:1,context:{server_id:api.serverId,team_id:auth.team_id,principal_id:auth.principal_id},items,next_cursor:items.length===limit?items.at(-1).effect.id:null},requestId);
11
+ }
12
+ if(req.method==='GET'&&effectId&&!action)return send(res,200,result(api,auth,await api.store.getEffect({...common,effectId})),requestId);
13
+ if(req.method==='POST'&&effectId&&action==='claim'){
14
+ const body=await parseJson(req);exact(body,['incarnation_id']);
15
+ if(body.incarnation_id!==incarnationId)fail(400,'invalid_request','claim incarnation differs from header');
16
+ return send(res,200,result(api,auth,await api.store.claimEffect({...common,effectId})),requestId);
17
+ }
18
+ if(req.method==='POST'&&effectId&&action==='result'){
19
+ const body=await parseJson(req);validateEffectResult(body,exact,fail);
20
+ return send(res,200,result(api,auth,await api.store.reportEffect({...common,effectId,result:body})),requestId);
21
+ }
22
+ fail(405,'not_found','method not allowed');
23
+ }
24
+
25
+ function validateEffectResult(body,exact,fail){
26
+ exact(body,['state','receipt','error']);
27
+ if(!['succeeded','failed','unknown'].includes(body.state))fail(400,'invalid_request','invalid effect result state');
28
+ if(body.state==='succeeded'){
29
+ exact(body.receipt,['native_session_id','runtime_id'],'receipt');
30
+ for(const key of ['native_session_id','runtime_id'])if(body.receipt[key]!==null&&typeof body.receipt[key]!=='string')fail(400,'invalid_request',`receipt.${key} must be string or null`);
31
+ if(body.error!==undefined)fail(400,'invalid_request','successful result cannot include error');
32
+ }else if(body.state==='failed'){
33
+ if(!body.error||typeof body.error!=='object'||Array.isArray(body.error))fail(400,'invalid_request','failed result requires error');
34
+ if(body.receipt!==undefined)fail(400,'invalid_request','failed result cannot include receipt');
35
+ }else if(body.error!==null||body.receipt!==undefined)fail(400,'invalid_request','unknown result requires null error');
36
+ }
37
+ import { randomBytes } from 'node:crypto';
38
+ import { hashToken } from '@volter/teams/server/auth';
39
+
40
+ const grantJson=row=>({id:row.id,machine_id:row.machine_id,session_id:row.session_id,binding_id:row.binding_id,subject_id:row.subject_id,actions:JSON.parse(row.actions_json),expires_at:row.expires_at,revision:row.revision});
41
+
42
+ export async function liveGrantRoute({api,req,res,auth,machineId,grantId,url,requestId,parseJson,exact,fail,send,result,guardedMutation,requireRevision}){
43
+ if(!['user','service'].includes(auth.kind))fail(403,'forbidden','user credential required');
44
+ const machine=await api.store.one('SELECT * FROM machines WHERE team_id=? AND id=? AND custodian_id=? AND revoked_at IS NULL',[auth.team_id,machineId,auth.principal_id]);
45
+ if(!machine)fail(404,'not_found','machine not found');
46
+ if(req.method==='GET'){
47
+ if(grantId){const row=await api.store.one('SELECT * FROM live_grants WHERE team_id=? AND machine_id=? AND id=?',[auth.team_id,machineId,grantId]);if(!row)fail(404,'not_found','grant not found');return send(res,200,result(api,auth,grantJson(row)),requestId,{ETag:`"${row.revision}"`});}
48
+ if([...url.searchParams].length)fail(400,'invalid_request','grants do not accept query parameters');
49
+ const rows=await api.store.query('SELECT * FROM live_grants WHERE team_id=? AND machine_id=? AND expires_at>? ORDER BY id',[auth.team_id,machineId,api.clock.nowMs()]);
50
+ return send(res,200,{version:1,context:{server_id:api.serverId,team_id:auth.team_id,principal_id:auth.principal_id},items:rows.map(grantJson),next_cursor:null},requestId);
51
+ }
52
+ if(req.method==='POST'&&!grantId){
53
+ const body=await parseJson(req);exact(body,['machine_id','session_id','binding_id','subject_id','actions','expires_at']);
54
+ if(body.machine_id!==machineId||!Array.isArray(body.actions)||!body.actions.length||new Set(body.actions).size!==body.actions.length||!body.actions.every(x=>['attach_observe','interact','approve','interrupt','terminate'].includes(x))||!Number.isSafeInteger(body.expires_at))fail(400,'invalid_request','invalid live grant');
55
+ const binding=await api.store.one('SELECT * FROM bindings WHERE team_id=? AND id=? AND machine_id=? AND session_id=?',[auth.team_id,body.binding_id,machineId,body.session_id]);
56
+ if(!binding)fail(400,'invalid_request','binding does not match machine and session');
57
+ const descriptor=JSON.parse(binding.descriptor_json),supported=descriptor.native_capabilities??[];
58
+ if(!body.actions.every(action=>supported.includes(action)))fail(400,'unsupported','binding does not support requested actions');
59
+ const now=api.clock.nowMs();if(body.expires_at<=now||body.expires_at>now+3600000)fail(400,'invalid_request','live grant expiry must be within one hour');
60
+ const id=api.ids.next('grant'),row={id,machine_id:machineId,session_id:body.session_id,binding_id:body.binding_id,subject_id:body.subject_id,actions:body.actions,expires_at:body.expires_at,revision:1},response=result(api,auth,row);
61
+ const guards=[{sql:'SELECT 1 ok FROM machines WHERE team_id=? AND id=? AND custodian_id=? AND revoked_at IS NULL AND incarnation_id=?',params:[auth.team_id,machineId,auth.principal_id,binding.incarnation_id],code:'conflict',status:409,message:'machine incarnation changed'},{sql:'SELECT 1 ok FROM memberships WHERE team_id=? AND principal_id=? AND active=1',params:[auth.team_id,body.subject_id],code:'invalid_request',status:400,message:'grant subject is not active'},{sql:'SELECT 1 ok FROM credentials WHERE id=? AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at>?)',params:[auth.credential_id,now],code:'authentication_required',status:401,message:'credential changed'}];
62
+ await guardedMutation(api,res,auth,guards,[{sql:'INSERT INTO live_grants(team_id,id,machine_id,session_id,binding_id,subject_id,actions_json,expires_at) VALUES (?,?,?,?,?,?,?,?)',params:[auth.team_id,id,machineId,body.session_id,body.binding_id,body.subject_id,JSON.stringify(body.actions),body.expires_at]}],{status:201,body:response,targetId:id});
63
+ return send(res,201,response,requestId,{ETag:'"1"'});
64
+ }
65
+ if(req.method==='DELETE'&&grantId){
66
+ const row=await api.store.one('SELECT * FROM live_grants WHERE team_id=? AND machine_id=? AND id=?',[auth.team_id,machineId,grantId]);if(!row)fail(404,'not_found','grant not found');requireRevision(req,row.revision,'live grant');const now=api.clock.nowMs();
67
+ await guardedMutation(api,res,auth,[{sql:'SELECT 1 ok FROM machines WHERE team_id=? AND id=? AND custodian_id=? AND revoked_at IS NULL',params:[auth.team_id,machineId,auth.principal_id],code:'not_found',status:404,message:'machine not found'},{sql:'SELECT 1 ok FROM live_grants WHERE team_id=? AND id=? AND revision=?',params:[auth.team_id,grantId,row.revision],code:'revision_mismatch',status:409,message:'live grant changed'}],[{sql:"UPDATE attachments SET state='revoked',closed_at=? WHERE team_id=? AND grant_id=? AND state IN ('pending','active')",params:[now,auth.team_id,grantId]},{sql:'UPDATE live_grants SET expires_at=?,revision=revision+1 WHERE team_id=? AND id=?',params:[now,auth.team_id,grantId]}],{status:204,body:null,targetId:grantId});
68
+ return send(res,204,null,requestId);
69
+ }
70
+ fail(405,'not_found','method not allowed');
71
+ }
72
+
73
+ export async function createAttachment({api,req,res,auth,sessionId,requestId,parseJson,exact,fail,send,result,guardedMutation,resolve}){
74
+ const session=await resolve(api,auth,sessionId),body=await parseJson(req);exact(body,['binding_id','mode']);if(!['observe','control'].includes(body.mode))fail(400,'invalid_request','invalid attachment mode');
75
+ const now=api.clock.nowMs(),binding=await api.store.one('SELECT b.*,m.last_seen_at,m.incarnation_id machine_incarnation,m.revoked_at FROM bindings b JOIN machines m ON m.team_id=b.team_id AND m.id=b.machine_id WHERE b.team_id=? AND b.id=? AND b.session_id=?',[auth.team_id,body.binding_id,session.id]);
76
+ if(!binding)fail(404,'not_found','binding not found');if(binding.revoked_at!=null||binding.last_seen_at==null||binding.last_seen_at+45000<=now||binding.incarnation_id!==binding.machine_incarnation)fail(503,'machine_offline','machine is offline');
77
+ const grant=await api.store.one('SELECT * FROM live_grants WHERE team_id=? AND machine_id=? AND binding_id=? AND session_id=? AND subject_id=? AND expires_at>? ORDER BY expires_at DESC LIMIT 1',[auth.team_id,binding.machine_id,binding.id,session.id,auth.principal_id,now]);
78
+ if(!grant)fail(403,'forbidden','an exact live grant is required');const actions=JSON.parse(grant.actions_json),required=body.mode==='observe'?'attach_observe':'interact';if(!actions.includes('attach_observe')||body.mode==='control'&&!actions.includes(required))fail(403,'forbidden','live grant does not allow attachment mode');
79
+ const attachmentId=api.ids.next('attachment'),operationId=api.ids.next('operation'),effectId=api.ids.next('event'),secret=`sat_${attachmentId}.${randomBytes(32).toString('base64url')}`,expiresAt=Math.min(grant.expires_at,now+3600000),attachment={id:attachmentId,state:'pending',stream_format:'observed_runtime_event',binding_id:binding.id,expires_at:expiresAt,stream_url:`${api.publicOrigin??api.localOrigin}/api/v1/teams/${auth.team_id}/attachments/${attachmentId}/stream`,credential:secret,effective_actions:actions},response=result(api,auth,attachment),effect={attachment_id:attachmentId,mode:body.mode};
80
+ const guards=[{sql:'SELECT 1 ok FROM credentials WHERE id=? AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at>?)',params:[auth.credential_id,now],code:'authentication_required',status:401,message:'credential changed'},{sql:'SELECT 1 ok FROM sessions WHERE team_id=? AND id=? AND access_revision=? AND deleted_at IS NULL',params:[auth.team_id,session.id,session.access_revision],code:'forbidden',status:403,message:'session access changed'},{sql:'SELECT 1 ok FROM machines WHERE team_id=? AND id=? AND incarnation_id=? AND revoked_at IS NULL AND last_seen_at+45000>?',params:[auth.team_id,binding.machine_id,binding.incarnation_id,now],code:'machine_offline',status:503,message:'machine is offline'},{sql:'SELECT 1 ok FROM live_grants WHERE team_id=? AND id=? AND subject_id=? AND expires_at>?',params:[auth.team_id,grant.id,auth.principal_id,now],code:'forbidden',status:403,message:'live grant changed'}];
81
+ await guardedMutation(api,res,auth,guards,[{sql:'INSERT INTO operations(team_id,id,requester_id,session_id,state,value_json,created_at) VALUES (?,?,?,?,?,?,?)',params:[auth.team_id,operationId,auth.principal_id,session.id,'pending',JSON.stringify({result:null,error:null}),now]},{sql:'INSERT INTO attachments(team_id,id,requester_id,issuer_credential_id,machine_id,binding_id,session_id,incarnation_id,mode,access_revision,grant_id,effective_actions_json,stream_token_hash,stream_token_expires_at,grant_expires_at,state,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',params:[auth.team_id,attachmentId,auth.principal_id,auth.credential_id,binding.machine_id,binding.id,session.id,binding.incarnation_id,body.mode,session.access_revision,grant.id,JSON.stringify(actions),hashToken(secret),now+60000,grant.expires_at,'pending',now]},{sql:'INSERT INTO native_effects(team_id,id,operation_id,principal_id,issuer_credential_id,machine_id,binding_id,incarnation_id,session_id,access_revision,grant_id,expires_at,kind,payload_json,state) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',params:[auth.team_id,effectId,operationId,auth.principal_id,auth.credential_id,binding.machine_id,binding.id,binding.incarnation_id,session.id,session.access_revision,grant.id,expiresAt,'attach',JSON.stringify(effect),'pending']}],{status:201,body:response,targetId:attachmentId});
82
+ send(res,201,response,requestId);
83
+ }
84
+
85
+ export async function attachmentActionRoute({api,req,res,auth,attachmentId,requestId,parseJson,exact,fail,send,result,guardedMutation,resolve}){
86
+ if(!['user','service'].includes(auth.kind))fail(403,'forbidden','user credential required');
87
+ const body=await parseJson(req);if(!body||typeof body!=='object'||Array.isArray(body)||!['input','approve','interrupt','terminate'].includes(body.kind))fail(400,'invalid_request','invalid attachment action');
88
+ if(body.kind==='input'){exact(body,['kind','text']);if(typeof body.text!=='string'||!body.text||Buffer.byteLength(body.text)>65536)fail(400,'invalid_request','input text must be 1 to 65536 bytes');}
89
+ else if(body.kind==='approve'){exact(body,['kind','request_id','decision']);if(typeof body.request_id!=='string'||!body.request_id||Buffer.byteLength(body.request_id)>512||!['allow_once','deny'].includes(body.decision))fail(400,'invalid_request','invalid approval response');}
90
+ else exact(body,['kind']);
91
+ const now=api.clock.nowMs(),attachment=await api.store.one('SELECT * FROM attachments WHERE team_id=? AND id=? AND requester_id=?',[auth.team_id,attachmentId,auth.principal_id]);if(!attachment)fail(404,'not_found','attachment not found');if(attachment.state!=='active'||attachment.mode!=='control')fail(409,'conflict','active control attachment required');await resolve(api,auth,attachment.session_id);
92
+ const required=body.kind==='input'?'interact':body.kind,grant=await api.store.one('SELECT * FROM live_grants WHERE team_id=? AND id=? AND machine_id=? AND session_id=? AND binding_id=? AND subject_id=? AND expires_at>?',[auth.team_id,attachment.grant_id,attachment.machine_id,attachment.session_id,attachment.binding_id,auth.principal_id,now]);if(!grant||!JSON.parse(grant.actions_json).includes(required))fail(403,'forbidden','live grant does not allow action');
93
+ const binding=await api.store.one('SELECT b.*,m.last_seen_at,m.incarnation_id machine_incarnation,m.revoked_at FROM bindings b JOIN machines m ON m.team_id=b.team_id AND m.id=b.machine_id WHERE b.team_id=? AND b.id=? AND b.machine_id=? AND b.session_id=?',[auth.team_id,attachment.binding_id,attachment.machine_id,attachment.session_id]),capabilities=(binding&&JSON.parse(binding.descriptor_json).native_capabilities)??[];if(!binding||binding.revoked_at!=null||binding.incarnation_id!==attachment.incarnation_id||binding.machine_incarnation!==attachment.incarnation_id||binding.last_seen_at+45000<=now)fail(503,'machine_offline','machine is offline');if(!capabilities.includes(required))fail(400,'unsupported','binding does not support action');
94
+ const operationId=api.ids.next('operation'),effectId=api.ids.next('event'),expiresAt=Math.min(grant.expires_at,now+60000),operation={id:operationId,state:'pending',result:null,error:null},response=result(api,auth,operation),payload={attachment_id:attachmentId,...body};
95
+ const guards=[{sql:'SELECT 1 ok FROM credentials WHERE id=? AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at>?)',params:[auth.credential_id,now],status:401,code:'authentication_required',message:'credential changed'},{sql:"SELECT 1 ok FROM attachments WHERE team_id=? AND id=? AND requester_id=? AND state='active' AND mode='control' AND access_revision=? AND grant_id=? AND binding_id=? AND incarnation_id=?",params:[auth.team_id,attachmentId,auth.principal_id,attachment.access_revision,grant.id,binding.id,binding.incarnation_id],status:409,code:'conflict',message:'attachment changed'},{sql:'SELECT 1 ok FROM live_grants WHERE team_id=? AND id=? AND revision=? AND expires_at>?',params:[auth.team_id,grant.id,grant.revision,now],status:403,code:'forbidden',message:'live grant changed'},{sql:'SELECT 1 ok FROM machines WHERE team_id=? AND id=? AND incarnation_id=? AND revoked_at IS NULL AND last_seen_at+45000>?',params:[auth.team_id,attachment.machine_id,attachment.incarnation_id,now],status:503,code:'machine_offline',message:'machine is offline'}];
96
+ await guardedMutation(api,res,auth,guards,[{sql:'INSERT INTO operations(team_id,id,requester_id,session_id,state,value_json,created_at) VALUES (?,?,?,?,?,?,?)',params:[auth.team_id,operationId,auth.principal_id,attachment.session_id,'pending',JSON.stringify({result:null,error:null}),now]},{sql:'INSERT INTO native_effects(team_id,id,operation_id,principal_id,issuer_credential_id,machine_id,binding_id,incarnation_id,session_id,access_revision,grant_id,expires_at,kind,payload_json,state) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)',params:[auth.team_id,effectId,operationId,auth.principal_id,auth.credential_id,attachment.machine_id,attachment.binding_id,attachment.incarnation_id,attachment.session_id,attachment.access_revision,attachment.grant_id,expiresAt,body.kind,JSON.stringify(payload),'pending']}],{status:202,body:response,targetId:operationId});return send(res,202,response,requestId);
97
+ }