@topolo/cli 0.13.6 → 0.13.7

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.
@@ -327,6 +327,9 @@ def parser():
327
327
  q.add_argument('operation',choices=['list','details','respond']);q.add_argument('key',nargs='?')
328
328
  q.add_argument('--intake');q.add_argument('--fingerprint');q.add_argument('--response')
329
329
  q.add_argument('--human-decision-recorded',action='store_true')
330
+ q=sub.add_parser('smoke',help='Start or read one closed, bound native acceptance case; never arbitrary prompts')
331
+ q.add_argument('operation',choices=['start','read']);q.add_argument('identity')
332
+ q.add_argument('--case',choices=sorted(live.SMOKE_CASES));q.add_argument('--operation-id')
330
333
  for command in ['connect','remote-context','remember','artifact','artifact-get','remote-recover']:
331
334
  q=sub.add_parser(command,help='Use public Topolo Agent continuity and Bytes artifact contracts')
332
335
  q.add_argument('name');q.add_argument('--environment',choices=['development','staging','production'],required=True)
@@ -380,7 +383,17 @@ def main(argv=None):
380
383
  emit(result)
381
384
  return 0
382
385
  with lib.lock():
383
- if args.command=='approvals':
386
+ if args.command=='smoke':
387
+ if args.operation=='start':
388
+ if not args.case or not args.operation_id:
389
+ raise PackError('Exact smoke case and operation ID required')
390
+ params={'key':args.identity,'caseId':args.case,'operationId':live.OwnedSmoke.operation_id(args.operation_id)}
391
+ else:
392
+ if args.case or args.operation_id:
393
+ raise PackError('Smoke read accepts only its existing operation ID')
394
+ params={'operationId':live.OwnedSmoke.operation_id(args.identity)}
395
+ result=live.request(lib,'smoke-'+args.operation,params)
396
+ elif args.command=='approvals':
384
397
  if args.operation!='list' and (not args.key or not args.intake or not args.fingerprint):
385
398
  raise PackError('Exact request key, --intake and --fingerprint required')
386
399
  params={'key':args.key,'intakeThreadId':args.intake,'fingerprint':args.fingerprint}
@@ -23,11 +23,240 @@ import time
23
23
  import urllib.request
24
24
  import urllib.parse
25
25
  import uuid
26
+ import datetime
26
27
 
27
28
  from .storage import Library,PackError,atomic_json,canonical,read_json
28
29
  from .execution import ExecutionGate, load_gate, prepare_runtime, qualify_protocol
29
30
  from .approvals import ApprovalRouter
30
31
 
32
+ SMOKE_PLAN = '6504d4e44c2456ae3e761de6c2e57b086b0e49229a7ee6a1ac782684ad4e40c6'
33
+ SMOKE_CASES = {'stg-context-memory-bytes': ('staging', 6), 'stg-peer-ack': ('staging', 6),
34
+ 'prod-role-context-ack': ('production', 140)}
35
+
36
+ class OwnedSmoke:
37
+ """One finite native verification operation. No caller text, paths or RPC.
38
+
39
+ Both native hosts use this store. A durable exclusive reservation covers
40
+ budget checks and dispatch; an uncertain send retains it across restart.
41
+ Binding/fixture references are supplied by the effect owner, never invented.
42
+ """
43
+ def __init__(self, lib, harness):
44
+ if harness not in {'codex', 'claude'}:
45
+ raise PackError('Unknown smoke harness')
46
+ self.lib=lib;self.harness=harness;self.root=lib.root/'smoke-operations'
47
+ self.root.mkdir(mode=0o700,exist_ok=True)
48
+ self.owned(self.root,directory=True)
49
+
50
+ @staticmethod
51
+ def owned(path,directory=False):
52
+ stat=path.lstat()
53
+ if path.is_symlink() or stat.st_uid!=os.getuid() or stat.st_mode&0o022 or (not directory and not path.is_file()):
54
+ raise PackError('Unowned smoke record')
55
+
56
+ @staticmethod
57
+ def operation_id(value):
58
+ try: valid=isinstance(value,str) and str(uuid.UUID(value))==value
59
+ except ValueError: valid=False
60
+ if not valid:
61
+ raise PackError('Exact smoke UUID required')
62
+ return value
63
+
64
+ def path(self,operation):
65
+ return self.root/(self.operation_id(operation)+'.json')
66
+
67
+ def read(self,operation):
68
+ path=self.path(operation);self.owned(path)
69
+ if path.stat().st_size>1048576:
70
+ raise PackError('Smoke record exceeds bounded transport')
71
+ record=read_json(path)
72
+ if record.get('operationId')!=operation or record.get('harness')!=self.harness or record.get('planSHA256')!=SMOKE_PLAN:
73
+ raise PackError('Smoke operation custody differs')
74
+ return record
75
+
76
+ def binding(self,key,case):
77
+ if case not in SMOKE_CASES or not isinstance(key,str) or not re.fullmatch(r'[a-z0-9-]+/[a-z0-9-]+/[a-z0-9-]+',key):
78
+ raise PackError('Finite owned smoke case and role required')
79
+ path=self.lib.root/'smoke-bindings'/(self.harness+'.json');self.owned(path.parent,directory=True);self.owned(path)
80
+ if path.stat().st_size>131072:
81
+ raise PackError('Smoke binding exceeds bounded inventory')
82
+ binding=read_json(path);now=time.time()
83
+ if (set(binding)!={'format','planSHA256','planId','issuedAt','expiresAt','roles'} or
84
+ binding['format']!='topolo-owned-native-smoke-v1' or binding['planSHA256']!=SMOKE_PLAN or
85
+ not isinstance(binding['issuedAt'],(int,float)) or not isinstance(binding['expiresAt'],(int,float)) or
86
+ not binding['issuedAt']<=now<binding['expiresAt']<=binding['issuedAt']+900):
87
+ raise PackError('Current bounded smoke binding required')
88
+ self.operation_id(binding['planId'])
89
+ rows=binding['roles']
90
+ if (not isinstance(rows,list) or not 1<=len(rows)<=70 or
91
+ len({r['key'] for r in rows})!=len(rows) or len({r['nativeId'] for r in rows})!=len(rows)):
92
+ raise PackError('Unique complete smoke roster required')
93
+ matches=[r for r in rows if r['key']==key]
94
+ if len(matches)!=1:
95
+ raise PackError('Smoke role is outside the owned binding')
96
+ row=matches[0]
97
+ fields={'key','nativeId','cwd','title','environment','contextKey','principalId','organizationId','instanceId',
98
+ 'logicalId','agentId','agentVersionId','agentWorkspaceId','bytesWorkspaceId',
99
+ 'contextMarkerId','contextMarkerHash','artifactMemoryId','artifactHash','fixtureExpiresAt'}
100
+ if set(row)!=fields or row['environment']!=SMOKE_CASES[case][0]:
101
+ raise PackError('Exact smoke descriptor or environment differs')
102
+ for field in fields-{'key','cwd','title','environment','contextMarkerHash','artifactHash','fixtureExpiresAt'}:
103
+ if not isinstance(row[field],str) or not re.fullmatch(r'[A-Za-z0-9_:/.-]{1,160}',row[field]):
104
+ raise PackError('Invalid smoke identity reference')
105
+ if (not pathlib.Path(row['cwd']).is_absolute() or not isinstance(row['title'],str) or not 1<=len(row['title'])<=200 or
106
+ any(not re.fullmatch(r'[a-f0-9]{64}',row[f]) for f in ['contextMarkerHash','artifactHash']) or
107
+ row['logicalId']!='/'.join(key.split('/')[1:]) or row['contextKey']!='org:'+row['organizationId']):
108
+ raise PackError('Smoke role/context references differ')
109
+ if case.startswith('stg-') and not now<row['fixtureExpiresAt']<=binding['expiresAt']:
110
+ raise PackError('Bounded staging fixture expiry required')
111
+ from . import topolo
112
+ pack=key.split('/')[0]
113
+ connection=read_json(topolo.binding_path(self.lib,pack,row['environment'],row['contextKey']))
114
+ role=[r for r in connection['input']['roles'] if r['logicalId']==row['logicalId']]
115
+ expected={'instanceId':row['instanceId'],'bytesWorkspaceId':row['bytesWorkspaceId']}
116
+ if (connection['identity']!={'userId':row['principalId'],'organizationId':row['organizationId']} or
117
+ connection['agentWorkspaceId']!=row['agentWorkspaceId'] or
118
+ any(connection['input'].get(k)!=v for k,v in expected.items()) or len(role)!=1 or
119
+ any(role[0].get(k)!=row[k] for k in ['agentId','agentVersionId'])):
120
+ raise PackError('Smoke hosted connection differs')
121
+ local=pathlib.Path(row['cwd'])/'topolo-connection.json';self.owned(local)
122
+ descriptor=read_json(local)
123
+ if any(descriptor.get(k)!=row[k] for k in ['environment','contextKey','instanceId','agentWorkspaceId','bytesWorkspaceId']):
124
+ raise PackError('Current native hosted descriptor differs')
125
+ return binding,row,hashlib.sha256(canonical(binding)).hexdigest()
126
+
127
+ def prepare(self,key,case,operation,native_identity):
128
+ path=self.path(operation)
129
+ if path.exists():
130
+ raise PackError('Smoke operation already exists; read without replay')
131
+ lock=self.root/'.reservation'
132
+ try:fd=os.open(lock,os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600)
133
+ except FileExistsError: raise PackError('Native smoke reservation remains; reconcile without replay')
134
+ with os.fdopen(fd,'wb') as stream:stream.write(canonical({'operationId':operation,'harness':self.harness}));stream.flush();os.fsync(stream.fileno())
135
+ try:
136
+ binding,row,digest=self.binding(key,case)
137
+ history=[]
138
+ for previous in self.root.glob('*.json'):
139
+ self.owned(previous);history.append(read_json(previous))
140
+ active=[r for r in history if r.get('state') not in {'completed','preflight-refused','native-refused'}]
141
+ if active:
142
+ raise PackError('Unsettled native smoke operation remains; no replay')
143
+ used=[r for r in history if r.get('planSHA256')==SMOKE_PLAN and r.get('caseId')==case and r.get('state')!='preflight-refused']
144
+ same_role=lambda r:r.get('harness')==self.harness and (r.get('key')==key or
145
+ (case.startswith('stg-') and r.get('key','').split('/')[1:2]==key.split('/')[1:2]))
146
+ if len(used)>=SMOKE_CASES[case][1] or any(same_role(r) for r in used):
147
+ raise PackError('Finite smoke case budget or role already consumed')
148
+ authority=self.authority(key,row)
149
+ if self.binding(key,case)[2]!=digest:
150
+ raise PackError('Smoke binding changed during qualification')
151
+ record={'operationId':operation,'harness':self.harness,'planSHA256':SMOKE_PLAN,'planId':binding['planId'],
152
+ 'caseId':case,'key':key,'nativeIdentity':native_identity,'bindingHash':digest,'row':row,
153
+ 'bindingExpiresAt':binding['expiresAt'],'authority':authority,
154
+ 'dispatchExpiresAt':min(binding['expiresAt'],row['fixtureExpiresAt']) if case.startswith('stg-') else binding['expiresAt'],
155
+ 'state':'prepared','atUTC':datetime.datetime.now(datetime.timezone.utc).isoformat(),
156
+ 'nativeId':None,'hostedUsageVerified':False,'operationEffectVerified':False}
157
+ context={};prefix={}
158
+ for member in binding['roles']:
159
+ charter=pathlib.Path(member['cwd'])/('AGENTS.md' if self.harness=='codex' else 'CLAUDE.md');self.owned(charter)
160
+ for file in pathlib.Path(member['cwd']).rglob('*.md'):
161
+ self.owned(file);context[str(file)]=hashlib.sha256(file.read_bytes()).hexdigest()
162
+ if self.harness=='codex':
163
+ runtime=pathlib.Path(self.lib.active()['runtime'])
164
+ files=[p for folder in ['sessions','archived_sessions'] for p in (runtime/'home'/folder).rglob('*.jsonl')]
165
+ else:
166
+ state=read_json(self.lib.root/'claude/state.json')
167
+ ids={r['nativeId'] for r in binding['roles']}
168
+ files=[pathlib.Path(a['transcript']) for a in state['agents'] if a.get('desktopSessionId') in ids]
169
+ for file in files:
170
+ self.owned(file);raw=file.read_bytes();prefix[str(file)]={'bytes':len(raw),'sha256':hashlib.sha256(raw).hexdigest()}
171
+ record.update(contextFiles=context,contextRoots=[r['cwd'] for r in binding['roles']],transcriptPrefixes=prefix)
172
+ atomic_json(path,record)
173
+ refs={k:row[k] for k in ['principalId','organizationId','instanceId','logicalId','agentId','agentVersionId',
174
+ 'agentWorkspaceId','bytesWorkspaceId','contextMarkerId','contextMarkerHash','artifactMemoryId','artifactHash','fixtureExpiresAt']}
175
+ task=('Perform the explicit expiring STG test-memory/Bytes roundtrip only; test provenance is this operation marker. '
176
+ if case=='stg-context-memory-bytes' else 'Read and acknowledge only the bound peer test-memory/Bytes references. '
177
+ if case=='stg-peer-ack' else 'Read only your hosted role/context and the bound verification memory/Bytes artifact. ')
178
+ text=('Topolo finite native acceptance case '+case+'. Operation '+operation+'. '+task+
179
+ 'Use the published Topolo Agent and Bytes tools in '+row['environment']+' / '+row['contextKey']+
180
+ ', for exactly '+key+'. Retrieve actual hosted context and the referenced Bytes content; local or CLI-proxy evidence is insufficient. '+
181
+ 'Exact references: '+canonical(refs).decode()+'. No business work, spawn, additional messages, permission changes or unrelated writes. '+
182
+ 'If any reference/tool is unavailable report the exact blocker. Return one concise JSON ACK with operationId, logicalId, agentVersionId, '+
183
+ 'contextMarkerHash, artifactHash and status; target at most 256 output tokens.')
184
+ return {'record':record,'text':text,'roster':[{k:r[k] for k in ['key','nativeId','cwd','title']} for r in binding['roles']]}
185
+ except BaseException:
186
+ lock.unlink();raise
187
+
188
+ def authority(self,key,row):
189
+ """Fresh public authority, with local consent re-read after blocking I/O."""
190
+ from . import topolo,permissions
191
+ pack=key.split('/')[0]
192
+ manifest,_=self.lib.load(pack);consent=permissions.load(self.lib,pack,manifest['agents'])
193
+ if not consent or consent.get('profile')!='full-access' or consent.get('contextKey')!=row['contextKey']:
194
+ raise PackError('Smoke needs the existing exact full-access consent')
195
+ original=canonical([manifest,consent])
196
+ client=topolo.Client(row['environment'],row['contextKey'])
197
+ if topolo.identity(client)!={'userId':row['principalId'],'organizationId':row['organizationId']}:
198
+ raise PackError('Fresh smoke principal differs')
199
+ ceiling,proof=topolo.organization_execution_policy(client,consent,manifest['agents'],row['agentWorkspaceId'])
200
+ permissions.authorize_native_scope(proof)
201
+ current,_=self.lib.load(pack)
202
+ if canonical([current,permissions.load(self.lib,pack,current['agents'])])!=original:
203
+ raise PackError('Smoke consent or manifest changed during authority qualification')
204
+ return {'localHash':hashlib.sha256(original).hexdigest(),'policyHash':hashlib.sha256(canonical([ceiling,proof])).hexdigest()}
205
+
206
+ def arm(self,operation,native_identity):
207
+ """Admit only the original prepared operation; never reset its budget."""
208
+ record=self.read(operation)
209
+ lock=self.root/'.reservation';self.owned(lock)
210
+ if (record['state']!='prepared' or record['nativeIdentity']!=native_identity or
211
+ read_json(lock)!={'operationId':operation,'harness':self.harness}):
212
+ raise PackError('Original prepared smoke reservation required; no replay')
213
+ binding,row,digest=self.binding(record['key'],record['caseId'])
214
+ if digest!=record['bindingHash'] or row!=record['row']:
215
+ raise PackError('Original smoke binding changed before dispatch')
216
+ if self.authority(record['key'],row)!=record['authority']:
217
+ raise PackError('Original smoke authority changed before dispatch')
218
+ # The public qualification can block. Re-read local binding/expiry,
219
+ # descriptors and original context after it, before the durable arm.
220
+ if self.binding(record['key'],record['caseId'])[2]!=record['bindingHash']:
221
+ raise PackError('Original smoke binding changed during final qualification')
222
+ self.verify(operation)
223
+ self.mark(operation,'start-may-have-effects')
224
+ return {'operationId':operation,'nativeIdentity':native_identity,'bindingHash':digest,
225
+ 'expiresAt':record['dispatchExpiresAt'],'state':'start-may-have-effects'}
226
+
227
+ def mark(self,operation,state,**evidence):
228
+ record=self.read(operation)
229
+ allowed={'prepared':{'prepared','resume-may-have-effects','start-may-have-effects','preflight-refused'},
230
+ 'resume-may-have-effects':{'start-may-have-effects','uncertain'},
231
+ 'start-may-have-effects':{'native-delivered','native-refused','uncertain'},
232
+ 'native-delivered':{'completed','uncertain'}}
233
+ if state not in allowed.get(record['state'],set()):
234
+ raise PackError('Smoke phase cannot replay or regress')
235
+ if set(evidence)-{'nativeId','nativeStatus','ack','historyPreserved','historyCount','historyHash'} or len(canonical(evidence))>8192:
236
+ raise PackError('Bounded native smoke evidence required')
237
+ record.update(state=state,**evidence);atomic_json(self.path(operation),record)
238
+ if state in {'native-delivered','native-refused','preflight-refused'}:
239
+ lock=self.root/'.reservation';self.owned(lock)
240
+ if read_json(lock).get('operationId')!=operation:
241
+ raise PackError('Native smoke reservation custody differs')
242
+ lock.unlink()
243
+ return record
244
+
245
+ def verify(self,operation):
246
+ record=self.read(operation)
247
+ if {str(p) for root in record['contextRoots'] for p in pathlib.Path(root).rglob('*.md')}!=set(record['contextFiles']):
248
+ raise PackError('Native smoke context inventory changed')
249
+ for filename,digest in record['contextFiles'].items():
250
+ path=pathlib.Path(filename);self.owned(path)
251
+ if hashlib.sha256(path.read_bytes()).hexdigest()!=digest:
252
+ raise PackError('Native smoke context changed')
253
+ for filename,before in record['transcriptPrefixes'].items():
254
+ path=pathlib.Path(filename);self.owned(path)
255
+ with path.open('rb') as stream: prefix=stream.read(before['bytes'])
256
+ if hashlib.sha256(prefix).hexdigest()!=before['sha256']:
257
+ raise PackError('Native smoke original transcript prefix changed')
258
+ return {'contextPreserved':True,'originalTranscriptPrefixesPreserved':True}
259
+
31
260
  def permission_settings(agent, current, consent):
32
261
  from .policy import settings
33
262
  wanted=settings(agent,'codex',consent)['nativeSettings']
@@ -46,7 +275,7 @@ def request(lib,operation,params=None):
46
275
  record=read_json(lib.root/'live-bridge.json',{})
47
276
  if not record: raise PackError('This desktop profile has no live bridge. Open it through topolo-agent-pack first.')
48
277
  with socket.socket(socket.AF_UNIX,socket.SOCK_STREAM) as connection:
49
- connection.settimeout(3 if operation=='status' else 60 if operation in {'permissions-configure','owned-roster-read'} else 20)
278
+ connection.settimeout(3 if operation=='status' else 60 if operation in {'permissions-configure','owned-roster-read','smoke-start'} else 20)
50
279
  try: connection.connect(record['socket'])
51
280
  except OSError as exc: raise PackError('Managed live bridge is unavailable: '+str(exc)) from exc
52
281
  connection.sendall(canonical({'token':record['token'],'operation':operation,**({'params':params} if params is not None else {})})+b'\n')
@@ -85,6 +314,7 @@ class BackendProxy:
85
314
  self.permission_configuration=False
86
315
  self.permission_batch=None
87
316
  self._permission_configuration_inputs=None
317
+ self.smoke_busy=False
88
318
 
89
319
  async def start_child(self):
90
320
  active=self.lib.active()
@@ -152,6 +382,9 @@ class BackendProxy:
152
382
  if self.output_task: await self.output_task
153
383
 
154
384
  async def forward(self,value):
385
+ if self.smoke_busy and value.get('method') in {'thread/resume','turn/start'}:
386
+ sys.stdout.buffer.write(canonical({'id':value.get('id'),'error':{'code':-32000,
387
+ 'message':'Owned native smoke dispatch is being qualified'}})+b'\n');sys.stdout.buffer.flush();return
155
388
  if self.permission_configuration and value.get('method') in {'thread/resume','turn/start'}:
156
389
  sys.stdout.buffer.write(canonical({'id':value.get('id'),'error':{'code':-32000,'message':'Owned permission configuration in progress'}})+b'\n')
157
390
  sys.stdout.buffer.flush();return
@@ -183,7 +416,7 @@ class BackendProxy:
183
416
  'params':{'includeHidden':True,**({'cursor':cursor} if cursor else {})}})+b'\n')
184
417
  await self.child.stdin.drain()
185
418
 
186
- async def call_native(self,method,params):
419
+ async def call_native(self,method,params,before_dispatch=None):
187
420
  identity='topolo-relay-'+uuid.uuid4().hex
188
421
  request={'id':identity,'method':method,'params':params}
189
422
  if method in {'thread/resume','turn/start'}:
@@ -196,6 +429,7 @@ class BackendProxy:
196
429
  raise PackError('No owned permission configuration snapshot; native settings were not sent')
197
430
  self.configuration_current(*self._permission_configuration_inputs)
198
431
  request=self.execution.prepare(request)
432
+ if before_dispatch is not None: before_dispatch()
199
433
  future=asyncio.get_running_loop().create_future();self.native_calls[identity]=future;self.pending.add(identity)
200
434
  try:
201
435
  self.child.stdin.write(canonical(request)+b'\n');await self.child.stdin.drain()
@@ -207,6 +441,111 @@ class BackendProxy:
207
441
  raise PackError('Native intake relay refused; original request remains pending')
208
442
  return reply['result']
209
443
 
444
+ def smoke_current(self,roles,resuming=None):
445
+ if (not self.execution or self.permission_configuration or self.quiesced or not self.initialized or self.closed or
446
+ not self.child or self.child.returncode is not None or self.active_turns or self.pending or self.notifications or
447
+ (self.approvals and self.approvals.pending)):
448
+ raise PackError('Smoke requires an idle current configured native connection')
449
+ current=self.existing_runtime_intent()['roles']
450
+ effective=[{k:r.get(k) for k in original} for original in current for r in [self.execution.roles.get(original['threadId'],{})]]
451
+ if canonical(current)!=canonical(roles) or canonical(current)!=canonical(effective):
452
+ raise PackError('Smoke execution/source roster changed')
453
+ verified=set(self.execution.roles)
454
+ if resuming is not None:
455
+ if list(self.execution.pending.values())!=[resuming]:
456
+ raise PackError('Owned smoke resume custody differs')
457
+ verified.discard(resuming)
458
+ if self.execution.verified!=verified:
459
+ raise PackError('Smoke requires all owned native permission/settings ACKs on this connection')
460
+ for role in roles:
461
+ spec=role['settings']
462
+ if (spec['sandbox_mode']!='danger-full-access' or spec['approval_policy']!='never' or
463
+ not role.get('installationConsent') or spec['model_reasoning_effort'] not in self.execution.catalog.get(spec['model'],set())):
464
+ raise PackError('Smoke native full-access policy or catalog is not qualified')
465
+
466
+ async def owned_smoke(self,operation,params):
467
+ wanted={'operationId'} if operation=='smoke-read' else {'operationId','key','caseId'}
468
+ if operation not in {'smoke-start','smoke-read'} or set(params)!=wanted:
469
+ raise PackError('Only closed smoke start/read fields are supported')
470
+ store=OwnedSmoke(self.lib,'codex');op=store.operation_id(params['operationId'])
471
+ if operation=='smoke-read':
472
+ record=store.read(op)
473
+ if record['state']!='native-delivered': return self.smoke_result(record)
474
+ if self.smoke_busy or not self.execution or not self.child or self.quiesced:
475
+ raise PackError('Existing native smoke connection unavailable; no replay')
476
+ identity=hashlib.sha256(canonical([self.runtime,self.child.pid,self.protocol])).hexdigest()
477
+ if record['nativeIdentity']!=identity: return {**self.smoke_result(record),'connectionCurrent':False}
478
+ row=record['row'];role=self.execution.roles.get(row['nativeId'])
479
+ if not role or role['key']!=record['key'] or role['cwd']!=row['cwd']:
480
+ raise PackError('Native smoke read ownership differs')
481
+ native=await self.call_native('thread/read',{'threadId':row['nativeId'],'includeTurns':True})
482
+ thread=native['thread'];turns=thread.get('turns',[])
483
+ matches=[t for t in turns if t.get('id')==record['nativeId']]
484
+ if thread['id']!=row['nativeId'] or thread['cwd']!=row['cwd'] or len(matches)!=1:
485
+ raise PackError('Exact native smoke turn is unavailable')
486
+ turn=matches[0]
487
+ if turn.get('status') not in {'completed','failed','interrupted'}: return self.smoke_result(record)
488
+ prefix=turns[:record['historyCount']]
489
+ preserved=hashlib.sha256(canonical(prefix)).hexdigest()==record['historyHash']
490
+ texts=[i.get('text','') for i in turn.get('items',[]) if i.get('type')=='agentMessage']
491
+ ack=None
492
+ if texts and len(texts[-1])<=2048:
493
+ try: ack=json.loads(texts[-1])
494
+ except ValueError: pass
495
+ expected={'operationId':op,'logicalId':row['logicalId'],'agentVersionId':row['agentVersionId'],
496
+ 'contextMarkerHash':row['contextMarkerHash'],'artifactHash':row['artifactHash']}
497
+ valid=(preserved and isinstance(ack,dict) and set(ack)==set(expected)|{'status'} and
498
+ all(ack[k]==v for k,v in expected.items()) and ack['status'] in {'ok','blocked'} and turn.get('status')=='completed')
499
+ store.verify(op)
500
+ if not valid:
501
+ record=store.mark(op,'uncertain',nativeStatus=turn.get('status'),historyPreserved=preserved)
502
+ else: record=store.mark(op,'completed',nativeStatus=turn.get('status'),ack=ack,historyPreserved=True)
503
+ return self.smoke_result(record)
504
+ if self.smoke_busy:
505
+ raise PackError('Native smoke dispatch already reserved')
506
+ self.smoke_busy=True;prepared=False
507
+ try:
508
+ roles=self.existing_runtime_intent()['roles'];self.smoke_current(roles)
509
+ self.execution.set_catalog(await self.read_catalog());self.smoke_current(roles)
510
+ binding,row,_=store.binding(params['key'],params['caseId'])
511
+ if {(r['key'],r['nativeId'],r['cwd']) for r in binding['roles']}!={(r['key'],r['threadId'],r['cwd']) for r in roles}:
512
+ raise PackError('Smoke binding must cover the complete existing roster')
513
+ identity=hashlib.sha256(canonical([self.runtime,self.child.pid,self.protocol])).hexdigest()
514
+ plan=await asyncio.to_thread(store.prepare,params['key'],params['caseId'],op,identity);prepared=True
515
+ self.smoke_current(roles)
516
+ native=await self.call_native('thread/read',{'threadId':row['nativeId'],'includeTurns':True})
517
+ thread=native['thread'];history=thread.get('turns',[])
518
+ if (thread.get('id')!=row['nativeId'] or thread.get('cwd')!=row['cwd'] or thread.get('name')!=row['title'] or
519
+ thread.get('status',{}).get('type') not in {'idle','notLoaded'} or not isinstance(history,list)):
520
+ raise PackError('Native smoke descriptor is not the exact idle role')
521
+ record=store.read(op);record.update(historyCount=len(history),historyHash=hashlib.sha256(canonical(history)).hexdigest())
522
+ atomic_json(store.path(op),record)
523
+ def guard(resuming=None):
524
+ self.smoke_current(roles,resuming)
525
+ store.verify(op)
526
+ if store.binding(params['key'],params['caseId'])[2]!=record['bindingHash']:
527
+ raise PackError('Smoke binding changed before native dispatch')
528
+ guard();store.mark(op,'resume-may-have-effects')
529
+ await self.call_native('thread/resume',{'threadId':row['nativeId']},before_dispatch=lambda:guard(row['nativeId']))
530
+ guard();store.mark(op,'start-may-have-effects')
531
+ result=await self.call_native('turn/start',{'threadId':row['nativeId'],
532
+ 'input':[{'type':'text','text':plan['text'],'text_elements':[]}]},before_dispatch=guard)
533
+ turn=result.get('turn',{})
534
+ if not isinstance(turn.get('id'),str) or not turn['id']:
535
+ raise PackError('Native smoke turn identity missing')
536
+ return self.smoke_result(store.mark(op,'native-delivered',nativeId=turn['id']))
537
+ except BaseException:
538
+ if prepared:
539
+ phase=store.read(op)['state']
540
+ store.mark(op,'preflight-refused' if phase=='prepared' else 'uncertain')
541
+ raise
542
+ finally:self.smoke_busy=False
543
+
544
+ @staticmethod
545
+ def smoke_result(record):
546
+ return {k:record[k] for k in ['operationId','harness','caseId','key','state','nativeId',
547
+ 'hostedUsageVerified','operationEffectVerified', 'nativeStatus','historyPreserved','ack'] if k in record}
548
+
210
549
  async def qualify_organization(self,thread_id):
211
550
  role=self.execution.roles.get(thread_id) if self.execution else None
212
551
  if role and not self.permission_configuration and (pathlib.Path(self.runtime)/'permissions-operation.json').exists():
@@ -473,6 +812,8 @@ class BackendProxy:
473
812
  if self.quiesced: raise PackError('Use the desktop reconnect action to resume a quiesced bridge')
474
813
  elif op=='owned-roster-read':
475
814
  result=await self.owned_roster_read(message.get('params') or {})
815
+ elif op in {'smoke-start','smoke-read'}:
816
+ result=await self.owned_smoke(op,message.get('params') or {})
476
817
  elif op=='permissions-configure':
477
818
  result=await self.configure_permissions((message.get('params') or {})['pack'])
478
819
  elif op in {'approval-list','approval-details','approval-respond'}:
@@ -498,7 +839,7 @@ class BackendProxy:
498
839
  self.child.stdin.write(canonical(reply)+b'\n');await self.child.stdin.drain();self.approvals.settled(reply)
499
840
  result={'state':'native-response-forwarded','operationEffectVerified':False}
500
841
  elif op!='status': raise PackError('Unknown live bridge operation')
501
- if op not in {'approval-list','approval-details','approval-respond','permissions-configure','owned-roster-read'}:result=self.status()
842
+ if op not in {'approval-list','approval-details','approval-respond','permissions-configure','owned-roster-read','smoke-start','smoke-read'}:result=self.status()
502
843
  except Exception as exc: result={'error':str(exc)}
503
844
  writer.write(canonical(result)+b'\n');await writer.drain();writer.close();await writer.wait_closed()
504
845
 
@@ -270,7 +270,8 @@ async function nativeClaudeApprovalControl(manager, descriptor, token, net, fs,
270
270
  if (Object.keys(request).sort().join(",") !== "identity,operation,pid,token" || request.token !== token || request.pid !== descriptor.pid || request.identity !== descriptor.identity)
271
271
  throw new Error("Native approval control authentication or process identity differs.");
272
272
  const op = request.operation;
273
- if (!op || !["list", "details", "respond"].includes(op.kind) || Object.keys(op).some((k) => !["kind", "key", "intake", "fingerprint", "decision", "humanDecisionRecorded"].includes(k)))
273
+ const fields = op?.kind === "smoke-start" ? ["kind", "key", "caseId", "operationId"] : op?.kind === "smoke-read" ? ["kind", "operationId"] : ["kind", "key", "intake", "fingerprint", "decision", "humanDecisionRecorded"];
274
+ if (!op || !["list", "details", "respond", "smoke-start", "smoke-read"].includes(op.kind) || Object.keys(op).some((k) => !fields.includes(k)))
274
275
  throw new Error("Unsupported native control operation.");
275
276
  const result = await invoke(op);
276
277
  const reply = JSON.stringify({ result }) + "\n";
@@ -315,6 +316,136 @@ async function nativeClaudeApprovalControl(manager, descriptor, token, net, fs,
315
316
  manager[key] = control;
316
317
  return { state: "native-approval-control-installed", ...descriptor };
317
318
  }
319
+ async function nativeClaudeOwnedSmokeOperation(manager, members, operation, helper, fingerprint, nativeIdentity, qualifyNative = () => {
320
+ }) {
321
+ const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
322
+ const fields = operation?.kind === "smoke-start" ? ["kind", "key", "caseId", "operationId"] : ["kind", "operationId"];
323
+ if (!operation || !["smoke-start", "smoke-read"].includes(operation.kind) || Object.keys(operation).sort().join() !== fields.sort().join() || !uuid.test(operation.operationId) || operation.kind === "smoke-start" && (!/^[a-z0-9-]+\/[a-z0-9-]+\/[a-z0-9-]+$/.test(operation.key) || !["stg-context-memory-bytes", "stg-peer-ack", "prod-role-context-ack"].includes(operation.caseId)))
324
+ throw new Error("Exact closed native smoke operation required.");
325
+ if (!members.length || members.length > 70 || new Set(members.map((m) => m.key)).size !== members.length || new Set(members.map((m) => m.sessionId)).size !== members.length) throw new Error("Unique native smoke roster required.");
326
+ const owner = /* @__PURE__ */ Symbol.for("topolo.agent-pack.owned-smoke.v1");
327
+ const rosterIdentity = fingerprint(members);
328
+ const summary = (record) => Object.fromEntries([
329
+ "operationId",
330
+ "harness",
331
+ "caseId",
332
+ "key",
333
+ "state",
334
+ "nativeId",
335
+ "hostedUsageVerified",
336
+ "operationEffectVerified",
337
+ "nativeStatus",
338
+ "historyPreserved",
339
+ "ack"
340
+ ].filter((k) => k in record).map((k) => [k, record[k]]));
341
+ const check = (member2, session) => {
342
+ if (!session || session.sessionId !== member2.sessionId || session.cwd !== member2.cwd || session.title !== member2.title || session.isArchived || session.backend?.kind !== "local" || session.backend.remoteTarget || session.worktreePath || session.worktreeLazy || session.spawnedFrom || session.isRunning || session.starting || session.pendingToolPermissions?.length || manager.hasLosableWork(member2.sessionId) || session.model !== member2.model || session.effort !== member2.effort || session.permissionMode !== member2.permissionMode || member2.permissionMode !== "bypassPermissions" || !member2.permissionConsent || !manager.getAvailableCodeModels().includes(member2.model)) throw new Error("Exact idle native smoke ownership/settings unavailable.");
343
+ };
344
+ const current = () => {
345
+ qualifyNative();
346
+ if (manager[/* @__PURE__ */ Symbol.for("topolo.agent-pack.native-operation.v1")] || fingerprint(members) !== rosterIdentity)
347
+ throw new Error("Unresolved native configuration or changed smoke roster remains.");
348
+ for (const member2 of members) check(member2, manager.getSession(member2.sessionId));
349
+ };
350
+ const ready = async () => {
351
+ current();
352
+ for (const member2 of members) {
353
+ const descriptor = await manager.getSessionWhenLoaded(member2.sessionId, "topolo-owned-smoke", { warnOnMiss: false });
354
+ check(member2, descriptor);
355
+ check(member2, manager.getSession(member2.sessionId));
356
+ }
357
+ current();
358
+ };
359
+ if (operation.kind === "smoke-read") {
360
+ const record = await helper("read", { operation: operation.operationId });
361
+ if (record.state !== "native-delivered") return summary(record);
362
+ if (record.nativeIdentity !== nativeIdentity) return { ...summary(record), connectionCurrent: false };
363
+ const member2 = members.find((m) => m.key === record.key && m.sessionId === record.row.nativeId && m.cwd === record.row.cwd);
364
+ if (!member2) throw new Error("Native smoke read custody differs.");
365
+ const session = await manager.getSession(member2.sessionId);
366
+ if (session?.isRunning || session?.starting || manager[owner]) return summary(record);
367
+ check(member2, session);
368
+ const history = await manager.getTranscript(member2.sessionId);
369
+ if (!Array.isArray(history)) throw new Error("Qualified native smoke transcript API unavailable.");
370
+ const same = fingerprint(history.slice(0, record.historyCount)) === record.historyHash;
371
+ const markers = history.map((m, index) => m.uuid === record.operationId ? index : -1).filter((i) => i >= 0);
372
+ const marker = markers[0] ?? -1;
373
+ const user = markers.length === 1 && marker === record.historyCount && history[marker]?.type === "user" ? marker : -1;
374
+ const nextUser = history.findIndex((m, index) => index > user && m.type === "user");
375
+ const assistants = user < 0 ? [] : history.slice(user + 1, nextUser < 0 ? history.length : nextUser).filter((m) => m.type === "assistant");
376
+ const text = assistants.at(-1)?.message?.content?.filter((b) => b.type === "text").map((b) => b.text).join("");
377
+ let ack;
378
+ if (typeof text === "string" && text.length <= 2048) {
379
+ try {
380
+ ack = JSON.parse(text);
381
+ } catch {
382
+ }
383
+ }
384
+ const expected = {
385
+ operationId: record.operationId,
386
+ logicalId: record.row.logicalId,
387
+ agentVersionId: record.row.agentVersionId,
388
+ contextMarkerHash: record.row.contextMarkerHash,
389
+ artifactHash: record.row.artifactHash
390
+ };
391
+ const valid = same && Number.isSafeInteger(record.historyCount) && record.historyCount >= 0 && ack && typeof ack === "object" && !Array.isArray(ack) && Object.keys(ack).sort().join() === [...Object.keys(expected), "status"].sort().join() && Object.entries(expected).every(([k, v]) => ack[k] === v) && ["ok", "blocked"].includes(ack.status);
392
+ await helper("verify", { operation: operation.operationId });
393
+ return summary(await helper("mark", {
394
+ operation: operation.operationId,
395
+ state: "uncertain",
396
+ nativeStatus: "terminal-evidence-unavailable",
397
+ historyPreserved: same,
398
+ ...valid ? { ack } : {}
399
+ }));
400
+ }
401
+ if (manager[owner]) throw new Error("Native smoke dispatch already reserved.");
402
+ const member = members.find((m) => m.key === operation.key);
403
+ if (!member) throw new Error("Smoke role is not natively owned.");
404
+ manager[owner] = operation.operationId;
405
+ let prepared = false;
406
+ try {
407
+ await ready();
408
+ const plan = await helper("prepare", {
409
+ key: operation.key,
410
+ case: operation.caseId,
411
+ operation: operation.operationId,
412
+ native_identity: nativeIdentity
413
+ });
414
+ prepared = true;
415
+ const actual = plan.roster.map((r) => [r.key, r.nativeId, r.cwd, r.title]).sort();
416
+ const wanted = members.map((m) => [m.key, m.sessionId, m.cwd, m.title]).sort();
417
+ if (JSON.stringify(actual) !== JSON.stringify(wanted)) throw new Error("Smoke binding must match the complete owned native roster.");
418
+ await ready();
419
+ await helper("verify", { operation: operation.operationId });
420
+ const history = await manager.getTranscript(member.sessionId);
421
+ if (!Array.isArray(history)) throw new Error("Qualified native smoke transcript API unavailable.");
422
+ await helper("mark", { operation: operation.operationId, state: "prepared", historyCount: history.length, historyHash: fingerprint(history) });
423
+ await ready();
424
+ const admission = await helper("arm", { operation: operation.operationId, native_identity: nativeIdentity });
425
+ current();
426
+ if (admission.operationId !== operation.operationId || admission.nativeIdentity !== nativeIdentity || admission.state !== "start-may-have-effects" || admission.bindingHash !== plan.record.bindingHash || !/^[a-f0-9]{64}$/.test(admission.bindingHash) || !Number.isFinite(admission.expiresAt) || admission.expiresAt !== plan.record.dispatchExpiresAt || Date.now() >= admission.expiresAt * 1e3)
427
+ throw new Error("Original smoke final dispatch admission expired or differs.");
428
+ const ack = await manager.sendMessage(
429
+ member.sessionId,
430
+ plan.text,
431
+ void 0,
432
+ { messageUuid: operation.operationId, initiator: "agent", origin: { kind: "task-notification" } }
433
+ );
434
+ return summary(await helper("mark", {
435
+ operation: operation.operationId,
436
+ state: ack?.delivered === true ? "native-delivered" : ack?.delivered === false ? "native-refused" : "uncertain",
437
+ nativeId: operation.operationId
438
+ }));
439
+ } catch (error) {
440
+ if (prepared) {
441
+ const record = await helper("read", { operation: operation.operationId });
442
+ await helper("mark", { operation: operation.operationId, state: record.state === "prepared" ? "preflight-refused" : "uncertain" });
443
+ }
444
+ throw error;
445
+ } finally {
446
+ delete manager[owner];
447
+ }
448
+ }
318
449
  async function nativeClaudeApprovalOperation(manager, routes, operation, fingerprint, checkpoint, history = []) {
319
450
  const symbol = /* @__PURE__ */ Symbol.for("topolo.agent-pack.approvals.v1");
320
451
  if (!routes.length || routes.length > 100 || new Set(routes.map((r) => r.sessionId)).size !== routes.length || new Set(routes.map((r) => r.key)).size !== routes.length) throw new Error("Unique owned approval roster required.");
@@ -496,13 +627,13 @@ async function nativeClaudeApprovalOperation(manager, routes, operation, fingerp
496
627
 
497
628
  // src/lib/claude-pack-backend.ts
498
629
  var CLAUDE_NATIVE_QUALIFIER = {
499
- version: "2.31226.1",
500
- manager: "index.chunk-BCHP6ll9.js",
501
- sidebar: "index.chunk-4-bSRKfE.js",
630
+ version: "2.37337.1",
631
+ manager: "index.chunk-BolytlRP.js",
632
+ sidebar: "index.chunk-BoQoqlAs.js",
502
633
  hashes: {
503
- "index.chunk-BCHP6ll9.js": "ec7c7cda3dd597a3cce034cc6eaea52e6e2b10d1b24f0eb9aee3964638afadf2",
504
- "index.chunk-4-bSRKfE.js": "594213a92b61bb64a3d61695d2370b624981af91f359c18832debb42a7814d86",
505
- "index.chunk-Cyt2WTko.js": "29738ec09f4bf44d511caae628b0f786e35956848e6a18c3159c9928d6ac1ad9"
634
+ "index.chunk-BolytlRP.js": "e387edfb06a34a59d99b54c04365f6e32d0108e8710124b4d1a628eea726d030",
635
+ "index.chunk-BoQoqlAs.js": "5c07a7e836d98dcd6afcfc2a62619e71d173a136ecb9af847716ea377b58b1df",
636
+ "index.chunk-BWk6pVUt.js": "f7d74e113f34761a675816fe2abf65b3ddc190327ee7623d9c822f7b59cceaca"
506
637
  }
507
638
  };
508
639
  function validateInspectorEndpoint(endpoint) {
@@ -615,6 +746,7 @@ function prepareClaudeNativePlan(store, pack, policyPath, mode = "configure") {
615
746
  contextInventories,
616
747
  approvalRoutes,
617
748
  installationConsent: policy.installationConsent,
749
+ libraryRoot: root,
618
750
  sourceRevision: policy.sourceRevision,
619
751
  mode,
620
752
  activeBefore: active
@@ -630,6 +762,9 @@ function qualifyPermissionConsent(policy, key, records, full) {
630
762
  function nativePackExpression(pid, plan, journal, apply) {
631
763
  if (!Number.isSafeInteger(pid) || pid < 1) throw new Error("Explicit Claude main-process PID required.");
632
764
  const qualifier = CLAUDE_NATIVE_QUALIFIER;
765
+ const here = dirname(fileURLToPath(import.meta.url));
766
+ const smokeSource = [resolve(here, "../../agent-packs/src"), resolve(here, "../agent-packs/src")].find(existsSync);
767
+ if (!smokeSource) throw new Error("Bundled owned smoke source unavailable.");
633
768
  return `(async () => {
634
769
  const expected = ${JSON.stringify({ pid, qualifier, journal, apply })};
635
770
  if (process.pid !== expected.pid || typeof process.getBuiltinModule !== 'function') throw new Error('Claude process identity or Node runtime differs.');
@@ -659,14 +794,24 @@ function nativePackExpression(pid, plan, journal, apply) {
659
794
  throw new Error('Context or transcript changed during configuration: ' + file);
660
795
  };
661
796
  const result = await (${nativeClaudePackOperation.toString()})(manager,
662
- value => transport.U(value, transport.V),
797
+ value => transport.P(value, transport.M),
663
798
  { members: ${JSON.stringify(plan.members)}, outgoing: ${JSON.stringify(plan.outgoing)},
664
799
  operationId: ${JSON.stringify(plan.operationId)}, mode: ${JSON.stringify(plan.recovery ? "recover" : plan.mode)},
665
800
  noEffects: ${JSON.stringify(plan.noEffects)},
666
801
  deadline: Date.now() + 45000 }, expected.apply, checkpoint, verifyContext);
667
802
  ${plan.approvalRoutes.length ? `const routes = ${JSON.stringify(plan.approvalRoutes)};
668
803
  const fingerprint = value => crypto.createHash('sha256').update(JSON.stringify(value)).digest('hex');
669
- const invoke = op => (${nativeClaudeApprovalOperation.toString()})(manager, routes, op, fingerprint,
804
+ const qualifyNative = () => {
805
+ if (process.pid !== expected.pid || load('electron').app.getVersion() !== expected.qualifier.version) throw new Error('Native process changed.');
806
+ for (const [file, hash] of Object.entries(expected.qualifier.hashes))
807
+ if (crypto.createHash('sha256').update(fs.readFileSync(base + file)).digest('hex') !== hash) throw new Error('Native module changed.');
808
+ };
809
+ const smokeIdentity = fingerprint({pid: expected.pid, qualifier: expected.qualifier, routes});
810
+ const invoke = op => ['smoke-start', 'smoke-read'].includes(op.kind) ?
811
+ (${nativeClaudeOwnedSmokeOperation.toString()})(manager, ${JSON.stringify(plan.members)}, op,
812
+ (action, payload) => (${runClaudeSmokeHelper.toString()})(${JSON.stringify(plan.libraryRoot)}, action, payload,
813
+ ${JSON.stringify(realpathSync(smokeSource))}, load('node:child_process').spawn), fingerprint, smokeIdentity, qualifyNative) :
814
+ (${nativeClaudeApprovalOperation.toString()})(manager, routes, op, fingerprint,
670
815
  row => checkpoint({kind: 'approval', ...row}),
671
816
  fs.readFileSync(expected.journal, 'utf8').split('\\n').filter(Boolean).map(line => JSON.parse(line)));
672
817
  if (expected.apply) {
@@ -676,11 +821,6 @@ function nativePackExpression(pid, plan, journal, apply) {
676
821
  const stat = fs.lstatSync(control.secretPath);
677
822
  if (!stat.isFile() || stat.isSymbolicLink() || stat.uid !== process.getuid() || (stat.mode & 63)) throw new Error('Unowned native control secret.');
678
823
  const secret = JSON.parse(fs.readFileSync(control.secretPath, 'utf8'));
679
- const qualifyNative = () => {
680
- if (process.pid !== expected.pid || load('electron').app.getVersion() !== expected.qualifier.version) throw new Error('Native process changed.');
681
- for (const [file, hash] of Object.entries(expected.qualifier.hashes))
682
- if (crypto.createHash('sha256').update(fs.readFileSync(base + file)).digest('hex') !== hash) throw new Error('Native module changed.');
683
- };
684
824
  result.approvalRouting.control = await (${nativeClaudeApprovalControl.toString()})(manager,
685
825
  {socket: control.socket, pid: expected.pid, identity: fingerprint(routes)}, secret.token,
686
826
  load('node:net'), fs, invoke, qualifyNative, checkpoint);
@@ -935,6 +1075,47 @@ async function configureClaudePack(options, evaluate = evaluateClaudeMain, quali
935
1075
  await release();
936
1076
  }
937
1077
  }
1078
+ function runClaudeSmokeHelper(root, action, payload, source, launch = spawn) {
1079
+ if (!["prepare", "arm", "mark", "read", "verify"].includes(action) || typeof root !== "string" || typeof source !== "string")
1080
+ return Promise.reject(new Error("Closed owned smoke helper required."));
1081
+ const script = 'import sys,json\nsys.path.insert(0,sys.argv[3])\nfrom topolo_agent_pack.storage import Library\nfrom topolo_agent_pack.live import OwnedSmoke\ns=OwnedSmoke(Library(sys.argv[1]),"claude")\np=json.loads(sys.stdin.read(16384))\na=sys.argv[2]\nr=s.prepare(**p) if a=="prepare" else s.arm(**p) if a=="arm" else s.mark(**p) if a=="mark" else s.verify(p["operation"]) if a=="verify" else s.read(p["operation"])\nprint(json.dumps(r))';
1082
+ return new Promise((done, fail) => {
1083
+ const child = launch("python3", ["-I", "-B", "-c", script, root, action, source], { stdio: ["pipe", "pipe", "pipe"] });
1084
+ let output = "";
1085
+ let settled = false;
1086
+ const finish = (error, value) => {
1087
+ if (settled) return;
1088
+ settled = true;
1089
+ clearTimeout(timer);
1090
+ error ? fail(error) : done(value);
1091
+ };
1092
+ const timer = setTimeout(() => {
1093
+ child.kill("SIGTERM");
1094
+ finish(new Error("Owned smoke helper timed out; reconcile without replay."));
1095
+ }, 6e4);
1096
+ child.stdout.setEncoding("utf8");
1097
+ child.stderr.resume();
1098
+ child.stdout.on("data", (data) => {
1099
+ output += data;
1100
+ if (Buffer.byteLength(output) > 1048576) {
1101
+ child.kill("SIGTERM");
1102
+ finish(new Error("Owned smoke evidence exceeds bounded transport."));
1103
+ }
1104
+ });
1105
+ child.on("error", () => finish(new Error("Bundled owned smoke helper unavailable.")));
1106
+ child.on("close", (code) => {
1107
+ if (settled) return;
1108
+ if (code !== 0) return finish(new Error("Owned smoke qualification refused; preserve its journal."));
1109
+ try {
1110
+ finish(void 0, JSON.parse(output));
1111
+ } catch {
1112
+ finish(new Error("Invalid owned smoke helper result."));
1113
+ }
1114
+ });
1115
+ child.stdin.on("error", () => finish(new Error("Owned smoke helper input failed; reconcile without replay.")));
1116
+ child.stdin.end(JSON.stringify(payload));
1117
+ });
1118
+ }
938
1119
  function requestClaudeApprovalControl(connection, operation, token, connect = createConnection) {
939
1120
  const parent = lstatSync(dirname(connection.socket));
940
1121
  const socket = lstatSync(connection.socket);
@@ -951,7 +1132,7 @@ function requestClaudeApprovalControl(connection, operation, token, connect = cr
951
1132
  error ? fail(error) : done(result);
952
1133
  };
953
1134
  client.setEncoding("utf8");
954
- client.setTimeout(1e4, () => finish(new Error("Native approval response timed out; reconcile without replay.")));
1135
+ client.setTimeout(operation.kind === "smoke-start" ? 6e4 : 1e4, () => finish(new Error("Native approval response timed out; reconcile without replay.")));
955
1136
  client.on("connect", () => client.write(JSON.stringify({ token, pid: connection.pid, identity: connection.identity, operation }) + "\n"));
956
1137
  client.on("data", (chunk) => {
957
1138
  reply += chunk;
@@ -985,6 +1166,15 @@ async function operateClaudeApprovals(options, request = requestClaudeApprovalCo
985
1166
  if (!secretPath.startsWith(realpathSync(privateDirectory) + sep) || !stat.isFile() || stat.isSymbolicLink() || stat.uid !== process.getuid?.() || stat.mode & 63) throw new Error("Unowned native control secret.");
986
1167
  const secret = JSON.parse(readFileSync(secretPath, "utf8"));
987
1168
  if (!/^[a-f0-9]{64}$/.test(secret.token) || !/^[a-f0-9]{64}$/.test(connection.identity)) throw new Error("Native control identity is invalid.");
1169
+ if (options.kind === "smoke-start" || options.kind === "smoke-read") return request(
1170
+ connection,
1171
+ {
1172
+ kind: options.kind,
1173
+ operationId: options.operationId,
1174
+ ...options.kind === "smoke-start" ? { key: options.key, caseId: options.caseId } : {}
1175
+ },
1176
+ secret.token
1177
+ );
988
1178
  return request(connection, {
989
1179
  kind: options.kind,
990
1180
  key: options.key,
@@ -994,6 +1184,12 @@ async function operateClaudeApprovals(options, request = requestClaudeApprovalCo
994
1184
  humanDecisionRecorded: options.humanDecisionRecorded
995
1185
  }, secret.token);
996
1186
  }
1187
+ async function operateClaudeSmoke(options, request = requestClaudeApprovalControl) {
1188
+ const fields = options.kind === "smoke-start" ? ["store", "kind", "operationId", "key", "caseId"] : ["store", "kind", "operationId"];
1189
+ if (!["smoke-start", "smoke-read"].includes(options.kind) || Object.keys(options).some((k) => !fields.includes(k)) || !/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(options.operationId) || options.kind === "smoke-start" && (!/^[a-z0-9-]+\/[a-z0-9-]+\/[a-z0-9-]+$/.test(options.key ?? "") || !["stg-context-memory-bytes", "stg-peer-ack", "prod-role-context-ack"].includes(options.caseId ?? "")))
1190
+ throw new Error("Exact closed native smoke fields required.");
1191
+ return operateClaudeApprovals(options, request);
1192
+ }
997
1193
  export {
998
1194
  CLAUDE_NATIVE_QUALIFIER,
999
1195
  configureClaudePack,
@@ -1001,8 +1197,10 @@ export {
1001
1197
  lockClaudeLibrary,
1002
1198
  nativePackExpression,
1003
1199
  operateClaudeApprovals,
1200
+ operateClaudeSmoke,
1004
1201
  prepareClaudeNativePlan,
1005
1202
  requestClaudeApprovalControl,
1203
+ runClaudeSmokeHelper,
1006
1204
  snapshotClaudeMarkdown,
1007
1205
  validateInspectorEndpoint,
1008
1206
  verifyClaudeOrganizationPolicy
package/dist/index.js CHANGED
@@ -8,9 +8,43 @@ import { fileURLToPath } from "url";
8
8
  import { homedir } from "os";
9
9
  function registerAgentPacksCommand(program2) {
10
10
  const packs = program2.command("agent-packs").description("Install and operate Topolo Agent Packs in supported local hosts").option("--store <path>", "Portable pack library (default ~/AgentPacks)");
11
+ packs.command("smoke").description("Start or read one closed, bound native acceptance case").argument("<operation>", "start or read").argument("<identity>", "Owned role key for start, existing operation UUID for read").option("--case <id>", "Finite compiled acceptance case").option("--operation-id <id>", "Unique native operation UUID for start").option("--harness <name>", "codex or claude", "codex").allowExcessArguments(false).action(async (operation, identity, options) => {
12
+ const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/;
13
+ if (!["start", "read"].includes(operation) || !["codex", "claude"].includes(options.harness) || (operation === "start" ? !/^[a-z0-9-]+\/[a-z0-9-]+\/[a-z0-9-]+$/.test(identity) || !["stg-context-memory-bytes", "stg-peer-ack", "prod-role-context-ack"].includes(options.case ?? "") || !uuid.test(options.operationId ?? "") : !uuid.test(identity) || options.case !== void 0 || options.operationId !== void 0))
14
+ throw new Error("Exact closed smoke operation, role/case and UUID required.");
15
+ const store = packs.opts().store ?? resolve(homedir(), "AgentPacks");
16
+ if (options.harness === "claude") {
17
+ const { operateClaudeSmoke } = await import("./claude-pack-backend-3YJNKMYD.js");
18
+ console.log(JSON.stringify(await operateClaudeSmoke({
19
+ store,
20
+ kind: "smoke-" + operation,
21
+ operationId: operation === "start" ? options.operationId : identity,
22
+ ...operation === "start" ? { key: identity, caseId: options.case } : {}
23
+ }), null, 2));
24
+ return;
25
+ }
26
+ const here = dirname(fileURLToPath(import.meta.url));
27
+ const adapter = [resolve(here, "../agent-packs/topolo-agent-pack"), resolve(here, "../../agent-packs/topolo-agent-pack")].find(existsSync);
28
+ if (!adapter) throw new Error("Installed bundled pack adapter unavailable.");
29
+ const result = spawnSync(
30
+ "python3",
31
+ [
32
+ adapter,
33
+ "--store",
34
+ store,
35
+ "smoke",
36
+ operation,
37
+ identity,
38
+ ...operation === "start" ? ["--case", options.case, "--operation-id", options.operationId] : []
39
+ ],
40
+ { stdio: "inherit", env: { ...process.env, PYTHONDONTWRITEBYTECODE: "1", TOPOLO_AGENT_PACK_PUBLIC_CLI: "1" } }
41
+ );
42
+ if (result.error) throw new Error("Python 3.11 or later is required for local packs.");
43
+ if (result.status !== 0) process.exitCode = result.status ?? 2;
44
+ });
11
45
  packs.command("configure").description("Qualify and configure an adopted Claude roster through its live native backend").argument("<pack>").option("--harness <name>", "Host adapter", "claude").requiredOption("--policy <path>", "Reviewed execution policy JSON").requiredOption("--endpoint <url>", "Explicit loopback main-process inspector endpoint, enabled separately with authorization").requiredOption("--pid <pid>", "Expected Claude main-process PID").option("--apply", "Apply native groups, pins, models, effort and permission modes; otherwise preflight only").action(async (pack, options) => {
12
46
  if (options.harness !== "claude") throw new Error("Native configure is currently qualified only for Claude Desktop.");
13
- const { configureClaudePack } = await import("./claude-pack-backend-AWCWJJA5.js");
47
+ const { configureClaudePack } = await import("./claude-pack-backend-3YJNKMYD.js");
14
48
  const result = await configureClaudePack({
15
49
  store: packs.opts().store ?? resolve(homedir(), "AgentPacks"),
16
50
  pack,
@@ -62,7 +96,7 @@ function registerAgentPacksCommand(program2) {
62
96
  throw new Error("Only a one-request once/deny response is supported.");
63
97
  decision = response.decision;
64
98
  }
65
- const { operateClaudeApprovals } = await import("./claude-pack-backend-AWCWJJA5.js");
99
+ const { operateClaudeApprovals } = await import("./claude-pack-backend-3YJNKMYD.js");
66
100
  console.log(JSON.stringify(await operateClaudeApprovals({
67
101
  store: packs.opts().store ?? resolve(homedir(), "AgentPacks"),
68
102
  kind: args[0],
@@ -77,7 +111,7 @@ function registerAgentPacksCommand(program2) {
77
111
  if (options.policy || options.endpoint || options.pid || options.apply) {
78
112
  if (options.harness !== "claude" || !options.policy || !options.endpoint || !options.pid || args.length !== 1)
79
113
  throw new Error("Native switch/recovery requires one Claude pack, --policy, --endpoint and --pid.");
80
- const { configureClaudePack } = await import("./claude-pack-backend-AWCWJJA5.js");
114
+ const { configureClaudePack } = await import("./claude-pack-backend-3YJNKMYD.js");
81
115
  const result2 = await configureClaudePack({
82
116
  store: packs.opts().store ?? resolve(homedir(), "AgentPacks"),
83
117
  pack: args[0],
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@topolo/cli",
3
3
  "packageManager": "pnpm@10.17.1",
4
- "version": "0.13.6",
5
- "topoloPublishedVersion": "0.13.6",
4
+ "version": "0.13.7",
5
+ "topoloPublishedVersion": "0.13.7",
6
6
  "description": "Public command-line interface for Topolo customer organizations, developers, and agents.",
7
7
  "license": "UNLICENSED",
8
8
  "author": "Topolo",