@topolo/cli 0.13.9 → 0.13.11

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.
@@ -29,6 +29,12 @@ MODELS = {'codex': {'reasoning': 'gpt-6-astra', 'workhorse': 'gpt-6.1-sol'},
29
29
  'claude': {'reasoning': 'claude-opus-5-5', 'workhorse': 'claude-sonnet-5-5'}}
30
30
 
31
31
 
32
+ def dynamic_task_settings(configuration, task, harness, catalog, constraints, *, override_authorized=False):
33
+ """Dynamic task intent only; preserve every fixed role's settings."""
34
+ from .task_models import resolve
35
+ return resolve(configuration, task, harness, catalog, constraints, override_authorized=override_authorized)
36
+
37
+
32
38
  def role_profile(agent):
33
39
  explicit = agent.get('executionProfile')
34
40
  if explicit is not None:
@@ -0,0 +1,200 @@
1
+ """Versioned task selection intersected with a current native account catalog.
2
+
3
+ This module selects intent only. It neither opens a host nor grants permissions.
4
+ An unavailable choice is a refusal, never a provider or parent-model fallback.
5
+ """
6
+ import copy
7
+ import hashlib
8
+
9
+ from .storage import PackError, canonical
10
+
11
+
12
+ def digest(value):
13
+ return hashlib.sha256(canonical(value)).hexdigest()
14
+
15
+
16
+ def default_configuration():
17
+ """Explicit template for a new pack configuration; existing roles are unchanged."""
18
+ return {
19
+ 'format': 'topolo-task-model-configuration', 'schemaVersion': 1, 'revision': '1',
20
+ 'rules': [
21
+ {'id': 'routine', 'complexity': ['routine'], 'risk': ['low'], 'profile': 'routine'},
22
+ {'id': 'deep', 'complexity': ['complex'], 'risk': ['low', 'normal', 'security-critical'], 'profile': 'deep'},
23
+ {'id': 'security', 'complexity': ['routine', 'standard'], 'risk': ['security-critical'], 'profile': 'deep'},
24
+ {'id': 'standard', 'complexity': ['standard'], 'risk': ['low', 'normal'], 'profile': 'standard'},
25
+ ],
26
+ 'harnesses': {
27
+ 'codex': {
28
+ 'tiers': {'standard': 'gpt-6.1-sol', 'deep': 'gpt-6-astra'},
29
+ 'profiles': {'routine': {'tier': 'standard', 'efforts': ['low', 'medium']},
30
+ 'standard': {'tier': 'standard', 'efforts': ['medium']},
31
+ 'deep': {'tier': 'deep', 'efforts': ['high']}},
32
+ },
33
+ 'claude': {
34
+ 'tiers': {'economy': 'claude-haiku-5-5', 'standard': 'claude-sonnet-5-5', 'deep': 'claude-opus-5-5'},
35
+ 'profiles': {'routine': {'tier': 'economy', 'efforts': ['low', 'medium']},
36
+ 'standard': {'tier': 'standard', 'efforts': ['medium']},
37
+ 'deep': {'tier': 'deep', 'efforts': ['high']}},
38
+ },
39
+ },
40
+ }
41
+
42
+
43
+ def _strings(values, label):
44
+ if (not isinstance(values, list) or not values or len(values) > 64 or
45
+ any(not isinstance(v, str) or not v or len(v) > 128 for v in values) or
46
+ len(set(values)) != len(values)):
47
+ raise PackError('Invalid exact ' + label)
48
+ return set(values)
49
+
50
+
51
+ def role_constraints(configuration, role_key):
52
+ """Intersect explicit pack, group and role ceilings; none is an implied grant."""
53
+ if (not isinstance(configuration,dict) or set(configuration)!={'pack','groups','roles'} or
54
+ not isinstance(role_key,str) or len(role_key.split('/'))!=3 or
55
+ not isinstance(configuration['groups'],dict) or not isinstance(configuration['roles'],dict)):
56
+ raise PackError('Explicit pack, group and role model constraints required')
57
+ group=role_key.split('/')[1]
58
+ rows=[configuration['pack'],configuration['groups'].get(group),configuration['roles'].get(role_key)]
59
+ models=None;efforts=None
60
+ for row in rows:
61
+ if not isinstance(row,dict) or set(row)!={'allowedModels','allowedEfforts'}:
62
+ raise PackError('Task model constraint layer is missing or unsupported')
63
+ m=_strings(row['allowedModels'],'constraint models');e=_strings(row['allowedEfforts'],'constraint efforts')
64
+ models=m if models is None else models&m;efforts=e if efforts is None else efforts&e
65
+ if not models or not efforts:
66
+ raise PackError('Task model constraints have no permitted intersection')
67
+ return {'allowedModels':sorted(models),'allowedEfforts':sorted(efforts)}
68
+
69
+
70
+ def _selection(configuration, task, harness, constraints, *, override_authorized=False):
71
+ """Resolve one bounded task against supplied, independently qualified host data.
72
+
73
+ The caller binds catalog/config digests to its current native connection and
74
+ checks them again before every effect. API-wide availability is insufficient.
75
+ """
76
+ if (not isinstance(configuration, dict) or set(configuration) != {'format', 'schemaVersion', 'revision', 'rules', 'harnesses'} or
77
+ configuration.get('format') != 'topolo-task-model-configuration' or
78
+ configuration.get('schemaVersion') != 1 or not configuration.get('revision')):
79
+ raise PackError('Task model configuration is missing or unsupported')
80
+ if (not isinstance(task, dict) or not {'type', 'complexity', 'risk'} <= set(task) or
81
+ set(task) - {'type', 'complexity', 'risk', 'override'} or
82
+ not isinstance(task['type'], str) or not task['type'] or len(task['type']) > 120 or
83
+ task['complexity'] not in {'routine', 'standard', 'complex'} or
84
+ task['risk'] not in {'low', 'normal', 'security-critical'}):
85
+ raise PackError('An intake must supply an exact task type, complexity and risk')
86
+ rules = configuration.get('rules')
87
+ if not isinstance(rules, list) or not 0 < len(rules) <= 32:
88
+ raise PackError('Task model rules are unavailable')
89
+ matches = []
90
+ for rule in rules:
91
+ if (not isinstance(rule, dict) or not {'id', 'complexity', 'risk', 'profile'} <= set(rule) or
92
+ not isinstance(rule['id'], str) or not rule['id'] or
93
+ not isinstance(rule['profile'], str) or not rule['profile'] or
94
+ set(rule) - {'id', 'complexity', 'risk', 'profile', 'types'}):
95
+ raise PackError('Invalid task model rule')
96
+ complexities = _strings(rule['complexity'], 'complexity rule')
97
+ risks = _strings(rule['risk'], 'risk rule')
98
+ types = _strings(rule['types'], 'task type rule') if 'types' in rule else None
99
+ if task['complexity'] in complexities and task['risk'] in risks and (types is None or task['type'] in types):
100
+ matches.append(rule)
101
+ if len(matches) != 1:
102
+ raise PackError('Task has no unique configured model profile')
103
+ rule = matches[0]
104
+ harnesses = configuration.get('harnesses')
105
+ host = harnesses.get(harness) if isinstance(harnesses, dict) else None
106
+ if (not isinstance(host, dict) or set(host) != {'tiers', 'profiles'} or
107
+ not isinstance(host['tiers'], dict) or not isinstance(host['profiles'], dict)):
108
+ raise PackError('Task model harness is not configured')
109
+ profile = host['profiles'].get(rule['profile'])
110
+ if (not isinstance(profile, dict) or set(profile) != {'tier', 'efforts'} or
111
+ not isinstance(profile['tier'], str)):
112
+ raise PackError('Task model profile is unavailable')
113
+ requested = {'model': host['tiers'].get(profile['tier']), 'efforts': profile['efforts']}
114
+ if not isinstance(requested['model'], str) or not requested['model'] or requested['model'].endswith('-latest'):
115
+ raise PackError('Configured model must be an exact versioned native model')
116
+ _strings(requested['efforts'], 'configured effort preference')
117
+ if not isinstance(constraints, dict) or set(constraints) != {'allowedModels', 'allowedEfforts'}:
118
+ raise PackError('Pack, group and role model constraints are required')
119
+ allowed_models = _strings(constraints['allowedModels'], 'allowed models')
120
+ allowed_efforts = _strings(constraints['allowedEfforts'], 'allowed efforts')
121
+ override = task.get('override')
122
+ if override is not None:
123
+ if (not override_authorized or not isinstance(override, dict) or set(override) != {'model', 'effort'}):
124
+ raise PackError('Task model override has no exact intake authorization')
125
+ model = override['model']; preferences = [override['effort']]
126
+ _strings(preferences, 'override effort')
127
+ else:
128
+ model = requested['model']; preferences = requested['efforts']
129
+ if not isinstance(model,str) or model not in allowed_models:
130
+ raise PackError('Requested model is outside the configured constraints; no fallback')
131
+ if not any(e in allowed_efforts for e in preferences):
132
+ raise PackError('Requested effort is outside the configured constraints; no fallback')
133
+ return {'rule':rule,'requested':requested,'override':override,'model':model,'preferences':preferences,
134
+ 'allowedModels':allowed_models,'allowedEfforts':allowed_efforts}
135
+
136
+
137
+ def resolve(configuration, task, harness, catalog, constraints, *, override_authorized=False):
138
+ selected=_selection(configuration,task,harness,constraints,override_authorized=override_authorized)
139
+ rule=selected['rule'];requested=selected['requested'];override=selected['override']
140
+ model=selected['model'];preferences=selected['preferences']
141
+ allowed_models=selected['allowedModels'];allowed_efforts=selected['allowedEfforts']
142
+ if (not isinstance(catalog, dict) or catalog.get('harness') != harness or
143
+ catalog.get('source') != 'native-account' or not catalog.get('connectionGeneration') or
144
+ not isinstance(catalog.get('models'), list) or not catalog['models']):
145
+ raise PackError('Current native account model and effort catalog is required')
146
+ available = {}
147
+ for row in catalog['models']:
148
+ if (not isinstance(row, dict) or set(row) != {'model', 'efforts'} or
149
+ not isinstance(row['model'], str) or not row['model'] or row['model'] in available):
150
+ raise PackError('Native catalog has duplicate or malformed model identities')
151
+ available[row['model']] = _strings(row['efforts'], 'native effort capabilities')
152
+ if not isinstance(model, str) or model not in allowed_models or model not in available:
153
+ raise PackError('Requested model is outside the configured or native account constraints; no fallback')
154
+ effort = next((v for v in preferences if v in allowed_efforts and v in available[model]), None)
155
+ if effort is None:
156
+ raise PackError('Requested effort is not supported for the exact model; no fallback')
157
+ return {
158
+ 'format': 'topolo-task-model-resolution', 'schemaVersion': 1,
159
+ 'task': copy.deepcopy(task), 'harness': harness, 'profile': rule['profile'],
160
+ 'rule': rule['id'], 'requested': copy.deepcopy(override or requested),
161
+ 'resolved': {'model': model, 'effort': effort},
162
+ 'reason': 'Exact configured task profile intersected with pack/group/role and native account constraints',
163
+ 'configurationRevision': configuration['revision'], 'configurationDigest': digest(configuration),
164
+ 'catalogDigest': digest(catalog), 'connectionGeneration': catalog['connectionGeneration'],
165
+ 'constraintsDigest': digest(constraints), 'nativeACK': None,
166
+ }
167
+
168
+
169
+ def resolve_intent(configuration, task, harness, models, constraints, *, override_authorized=False):
170
+ """Explicit Claude intent when native effort metadata is unavailable.
171
+
172
+ This does not fabricate effort capability rows. No task may run until the
173
+ exact fresh child's actual process settings acknowledge the selected tuple.
174
+ """
175
+ if (harness!='claude' or not isinstance(models,dict) or
176
+ set(models)!={'harness','source','connectionGeneration','models'} or models['harness']!=harness or
177
+ models['source']!='native-account-model-ids' or not models['connectionGeneration']):
178
+ raise PackError('Current native account model-ID observation required for staged Claude intent')
179
+ actual=_strings(models['models'],'native model IDs')
180
+ selected=_selection(configuration,task,harness,constraints,override_authorized=override_authorized)
181
+ if selected['model'] not in actual:
182
+ raise PackError('Exact task model is absent from the actual native account; no fallback')
183
+ effort=next(e for e in selected['preferences'] if e in selected['allowedEfforts'])
184
+ return {'format':'topolo-task-model-resolution','schemaVersion':1,'task':copy.deepcopy(task),'harness':harness,
185
+ 'profile':selected['rule']['profile'],'rule':selected['rule']['id'],
186
+ 'requested':copy.deepcopy(selected['override'] or selected['requested']),
187
+ 'resolved':{'model':selected['model'],'effort':effort},
188
+ 'reason':'Versioned task intent intersected with pack/group/role constraints and current native model IDs; effort support awaits actual process ACK',
189
+ 'configurationRevision':configuration['revision'],'configurationDigest':digest(configuration),
190
+ 'catalogDigest':digest(models),'connectionGeneration':models['connectionGeneration'],
191
+ 'constraintsDigest':digest(constraints),'nativeEffortSupport':'awaiting-native-process-settings-ACK','nativeACK':None}
192
+
193
+
194
+ def acknowledge(resolution, native, *, configuration, catalog, constraints):
195
+ if (resolution['configurationDigest'] != digest(configuration) or
196
+ resolution['catalogDigest'] != digest(catalog) or
197
+ resolution['constraintsDigest'] != digest(constraints) or
198
+ native != resolution['resolved']):
199
+ raise PackError('Task model inputs or actual native model/effort changed; execution remains blocked')
200
+ return {**copy.deepcopy(resolution), 'nativeACK': copy.deepcopy(native)}
@@ -1,26 +1,28 @@
1
1
  """Optional public Topolo continuity adapter. No credentials or transcript uploads."""
2
- import re,contextlib,fcntl,base64,hashlib,json,os,pathlib,shutil,subprocess,tempfile,uuid
2
+ import re,contextlib,fcntl,base64,hashlib,json,os,pathlib,shutil,stat,subprocess,tempfile,uuid
3
3
  from .storage import PackError,atomic_json,canonical,read_json,safe_id
4
4
 
5
5
  class ConnectionSourceRefused(PackError):
6
6
  pass
7
7
 
8
8
  class Client:
9
- def __init__(self,environment,context,binary=None):
9
+ def __init__(self,environment,context,binary=None,env=None,node=None):
10
10
  if environment not in {'development','staging','production'}:
11
11
  raise PackError('Explicit Topolo environment required')
12
12
  if not context or any(c in context for c in '\n\r'):
13
13
  raise PackError('Explicit Topolo context required')
14
- if any(os.environ.get(k) for k in ['TOPOLO_API_KEY','TOPOLO_ACCESS_TOKEN','TOPOLO_'+environment.upper()+'_API_KEY','TOPOLO_'+environment.upper()+'_ACCESS_TOKEN']):
14
+ selected=os.environ if env is None else env
15
+ if any(selected.get(k) for k in ['TOPOLO_API_KEY','TOPOLO_ACCESS_TOKEN','TOPOLO_'+environment.upper()+'_API_KEY','TOPOLO_'+environment.upper()+'_ACCESS_TOKEN']):
15
16
  raise PackError('Pinned connections require the CLI protected account context, not injected credentials')
16
17
  self.environment=environment;self.context=context
17
18
  self.binary=binary or shutil.which('topolo')
18
19
  if not self.binary:
19
20
  raise PackError('Install the public @topolo/cli and authenticate before connecting')
20
- self.env={**os.environ,'TOPOLO_CONTEXT':context}
21
- self.env['PATH']=str(pathlib.Path(self.binary).parent)+os.pathsep+self.env.get('PATH','')
21
+ self.env={**selected,'TOPOLO_CONTEXT':context}
22
+ self.command=[node,self.binary] if node else [self.binary]
23
+ self.env['PATH']=(str(pathlib.Path(node).parent)+os.pathsep+'/usr/bin:/bin') if node else str(pathlib.Path(self.binary).parent)+os.pathsep+self.env.get('PATH','')
22
24
  def run(self,args):
23
- p=subprocess.run([self.binary,'--env',self.environment,*args,'--json'],env=self.env,capture_output=True,text=True,timeout=90)
25
+ p=subprocess.run([*self.command,'--env',self.environment,*args,'--json'],env=self.env,capture_output=True,text=True,timeout=90)
24
26
  if p.returncode:
25
27
  raise PackError('Topolo command failed (exit '+str(p.returncode)+'); inspect the published action, account permissions and environment. No fallback was used.')
26
28
  try:value=json.loads(p.stdout)
@@ -100,7 +102,11 @@ def attach_runtime(lib,runtime):
100
102
  if not pointer.exists():continue
101
103
  selected=read_json(pointer);binding=read_json(binding_path(lib,pack,selected['environment'],selected['contextKey']))
102
104
  role=agent['set']+'/'+agent['id']
103
- if not any(r['logicalId']==role for r in binding['input']['roles']):continue
105
+ matches=[r for r in binding['input']['roles'] if r['logicalId']==role]
106
+ if not matches:continue
107
+ if len(matches)!=1:
108
+ raise PackError('Connected role binding is not unique')
109
+ reference=matches[0]
104
110
  import sqlite3,shlex
105
111
  con=sqlite3.connect(runtime/'home/state_5.sqlite')
106
112
  try:row=con.execute('SELECT cwd FROM threads WHERE id=?',(agent['threadId'],)).fetchone()
@@ -117,7 +123,9 @@ def attach_runtime(lib,runtime):
117
123
  # The descriptor contains identity references, never tokens or cached memory.
118
124
  atomic_json(directory/'topolo-connection.json',{'environment':selected['environment'],'contextKey':selected['contextKey'],
119
125
  'pack':pack,'role':role,'instanceId':binding['input']['instanceId'],'agentWorkspaceId':binding['agentWorkspaceId'],
120
- 'bytesWorkspaceId':binding['input']['bytesWorkspaceId']})
126
+ 'bytesWorkspaceId':binding['input']['bytesWorkspaceId'],
127
+ 'agentId':reference['agentId'],'agentVersionId':reference['agentVersionId'],
128
+ 'identity':{key:binding['identity'][key] for key in ['userId','organizationId']}})
121
129
  text=old+'\n<!-- topolo-continuity -->\n## Shared Topolo continuity\n'+\
122
130
  'Before starting assigned work, refresh your approved operating instructions and scoped memory using:\n\n'+command+'\n\n'+\
123
131
  'If retrieval fails, report the exact blocker and do not silently use stale shared memory. Read only relevant Bytes artifact references on demand. '+\
@@ -312,6 +320,28 @@ def qualify_connection_scope(scope):
312
320
  except (PackError,OSError,ValueError,TypeError) as error:
313
321
  raise ConnectionSourceRefused('Pack source or native context changed before cloud dispatch; no mutating call') from error
314
322
 
323
+ def private_profile_reference(client,args):
324
+ """Explicit selected-profile metadata only; never open credential contents."""
325
+ if not getattr(args,'private_profile',False):
326
+ return None
327
+ env=getattr(client,'env',None)
328
+ selected=env.get('TOPOLO_CONFIG_PATH') if isinstance(env,dict) else None
329
+ if (not isinstance(selected,str) or not selected or
330
+ getattr(client,'environment',None)!=args.environment or getattr(client,'context',None)!=args.context_key):
331
+ raise PackError('Explicit private profile and exact selected client environment/context required')
332
+ path=pathlib.Path(selected)
333
+ try:
334
+ leaf=path.lstat();parent=path.parent.lstat()
335
+ if (not path.is_absolute() or path.resolve()!=path or not stat.S_ISREG(leaf.st_mode) or
336
+ leaf.st_uid!=os.getuid() or leaf.st_nlink!=1 or stat.S_IMODE(leaf.st_mode)!=0o600 or
337
+ not stat.S_ISDIR(parent.st_mode) or parent.st_uid!=os.getuid() or stat.S_IMODE(parent.st_mode)!=0o700):
338
+ raise PackError('Private profile requires an owned single-link0600 leaf in an owned0700 directory')
339
+ except OSError as error:
340
+ raise PackError('Explicit private profile metadata is unavailable; no default profile fallback') from error
341
+ return {'format':'topolo-private-credential-profile','schemaVersion':1,'configPath':str(path),
342
+ 'environment':args.environment,'contextKey':args.context_key}
343
+
344
+
315
345
  def connect(lib,args,client):
316
346
  manifest,revision=lib.load(args.name)
317
347
  claude_plan=claude_context_plan(lib,args.name,manifest,revision) if getattr(args,'harness','codex')=='claude' else None
@@ -337,6 +367,9 @@ def connect(lib,args,client):
337
367
  if not getattr(args,'reconcile_local_context',False):
338
368
  raise PackError('Partial Claude context attachment requires explicit --reconcile-local-context; no replay')
339
369
  old=read_json(path,{})
370
+ if old.get('privateCredentialProfile') and not getattr(args,'private_profile',False):
371
+ raise PackError('Explicit private-profile selection required to requalify this existing private connection')
372
+ private_profile=private_profile_reference(client,args)
340
373
  journal=path.with_suffix('.pending.json')
341
374
  pending=read_json(journal,{})
342
375
  instance=args.instance or old.get('input',{}).get('instanceId') or pending.get('input',{}).get('instanceId') or 'pack_'+uuid.uuid4().hex
@@ -345,6 +378,8 @@ def connect(lib,args,client):
345
378
  'ownershipScope':args.ownership,'bytesWorkspaceId':args.bytes_workspace,'bytesScope':args.bytes_scope,'roles':roles}
346
379
  scope=(lib,args,dict(vars(args)),canonical(manifest),revision,roles_bytes,roles,claude_plan)
347
380
  owner=identity(client)
381
+ if private_profile is not None and args.context_key!='org:'+owner['organizationId']:
382
+ raise PackError('Private profile identity differs from the exact selected organization context')
348
383
  qualify_connection_scope(scope)
349
384
  journal=path.with_suffix('.pending.json')
350
385
  if journal.exists():
@@ -358,6 +393,8 @@ def connect(lib,args,client):
358
393
  reuse=bool(old and old.get('input')==payload and old.get('agentWorkspaceId')==args.agent_workspace and old.get('packRevision')==revision)
359
394
  proposed={'schemaVersion':1,'environment':args.environment,'contextKey':args.context_key,'identity':owner,
360
395
  'agentWorkspaceId':args.agent_workspace,'input':payload,'packRevision':revision}
396
+ if private_profile is not None:
397
+ proposed['privateCredentialProfile']={**private_profile,'identity':owner}
361
398
  if claude_plan is not None:
362
399
  claude_context_targets(lib,args,proposed,claude_plan)
363
400
  if local_pending:
@@ -384,6 +421,10 @@ def connect(lib,args,client):
384
421
  raise PackError('Pack connection read-back mismatch; preserve pending receipt')
385
422
  binding={'schemaVersion':1,'environment':args.environment,'contextKey':args.context_key,'identity':owner,
386
423
  'agentWorkspaceId':args.agent_workspace,'input':payload,'connection':readback,'packRevision':revision}
424
+ if private_profile is not None:
425
+ if private_profile_reference(client,args)!=private_profile:
426
+ raise PackError('Private profile selection changed during connection; preserve existing receipts')
427
+ binding['privateCredentialProfile']={**private_profile,'identity':owner}
387
428
  if local_pending:
388
429
  if binding!=old:
389
430
  raise PackError('Claude attachment verified remote binding changed; no local replay')
@@ -560,6 +601,8 @@ def connection_lock(lib,args):
560
601
  yield
561
602
 
562
603
  def dispatch(lib,args):
604
+ if args.command=='remote-context' and getattr(args,'continuity_input',None):
605
+ return native_cli_continuity_read(lib,args)
563
606
  client=Client(args.environment,args.context_key)
564
607
  if args.command=='connect':return connect(lib,args,client)
565
608
  if args.command=='remote-context':return remote_context(lib,args,client)[1]
@@ -571,3 +614,124 @@ def dispatch(lib,args):
571
614
  reference=read_json(args.reference)
572
615
  return read_artifact(client,binding,reference['key'],reference,args.destination)
573
616
  raise PackError('Unsupported Topolo command')
617
+
618
+
619
+
620
+ def native_cli_response(stdout,stderr,service):
621
+ """Preserve only canonical public response metadata; never echo raw stderr."""
622
+ from . import lanes
623
+ if len(stdout.encode())>262144 or len(stderr.encode())>65536:
624
+ raise PackError('native_cli_request_metadata_unavailable: bounded public response required')
625
+ try:
626
+ body=json.loads(stdout)
627
+ except ValueError as error:
628
+ raise PackError('native_cli_request_metadata_unavailable: invalid public JSON') from error
629
+ if not isinstance(body,dict) or body.get('error'):
630
+ raise PackError('native_cli_request_metadata_unavailable: unsuccessful public response')
631
+ events=[];seen=set()
632
+ pattern=r'^\[topolo\] ← (\d{3}) (GET|POST) (auth|agent) (/[^\s?#\\]+) \([0-9.]+ms, req=([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\)$'
633
+ for line in stderr.splitlines():
634
+ match=re.fullmatch(pattern,line)
635
+ if not match:
636
+ continue
637
+ status,method,target,path,request_id=match.groups()
638
+ if request_id=='unavailable' or request_id in seen or not 200<=int(status)<300:
639
+ raise PackError('native_cli_request_metadata_unavailable: ambiguous or unsuccessful response request IDs')
640
+ seen.add(request_id);events.append({'requestId':request_id,'method':method,'service':target,'path':path,'status':int(status)})
641
+ if not 1<=len(events)<=8 or service not in {event['service'] for event in events}:
642
+ raise PackError('native_cli_request_metadata_unavailable: actual canonical server response IDs required')
643
+ return body.get('data',body),events
644
+
645
+
646
+ def native_cli_command_response(client,args,service):
647
+ env={**client.env,'TOPOLO_DEBUG':'1'}
648
+ result=subprocess.run([*client.command,'--env',client.environment,*args,'--json'],env=env,
649
+ capture_output=True,text=True,timeout=90)
650
+ if result.returncode:
651
+ raise PackError('native_cli_request_metadata_unavailable: public command refused; no fallback or retry')
652
+ return native_cli_response(result.stdout,result.stderr,service)
653
+
654
+
655
+ def native_cli_selected_environment(value,binding,host_env):
656
+ from . import lanes
657
+ from types import SimpleNamespace
658
+ context=value['context'];profile=binding.get('privateCredentialProfile')
659
+ lanes.native_cli_closed(profile,{'format','schemaVersion','configPath','environment','contextKey','identity'},'private profile reference')
660
+ if (profile['format']!='topolo-private-credential-profile' or profile['schemaVersion']!=1 or
661
+ profile['environment']!=context['environment'] or profile['contextKey']!=context['contextKey'] or
662
+ profile['identity']!={'userId':context['principalId'],'organizationId':context['organizationId']}):
663
+ raise PackError('Exact native-cli approved profile/principal/context reference required')
664
+ selected=SimpleNamespace(env={'TOPOLO_CONFIG_PATH':profile['configPath']},environment=context['environment'],context=context['contextKey'])
665
+ args=SimpleNamespace(private_profile=True,environment=context['environment'],context_key=context['contextKey'])
666
+ private_profile_reference(selected,args)
667
+ for path,sha in [(value['cli']['nodeExecutable'],value['cli']['nodeDigest']),(value['cli']['entry'],value['cli']['entryDigest'])]:
668
+ p=pathlib.Path(path);info=p.lstat()
669
+ if (p.resolve()!=p or not stat.S_ISREG(info.st_mode) or info.st_uid not in {0,os.getuid()} or info.st_mode&0o022 or
670
+ not info.st_mode&0o111 or hashlib.sha256(p.read_bytes()).hexdigest()!=sha):
671
+ raise PackError('Native-cli executable source/provenance changed')
672
+ allowed={'HOME','USER','LOGNAME','PATH','LANG','LC_ALL','TMPDIR'}
673
+ env={k:v for k,v in host_env.items() if k in allowed and isinstance(v,str)}
674
+ env['PATH']=str(pathlib.Path(value['cli']['nodeExecutable']).parent)+os.pathsep+'/usr/bin:/bin'
675
+ env.update(TOPOLO_CONFIG_PATH=profile['configPath'],TOPOLO_ENV=context['environment'],TOPOLO_CONTEXT=context['contextKey'])
676
+ return env
677
+
678
+
679
+ def native_cli_continuity_read(lib,args,*,_admit=None,_client_factory=None,_response=None):
680
+ """Native command producer. No current runtime authority producer exists.
681
+
682
+ Injected callbacks are internal hermetic seams, never CLI/JSON options.
683
+ Receipt output is public response evidence, not native-query attribution.
684
+ """
685
+ from . import lanes
686
+ path=pathlib.Path(args.continuity_input);info=path.lstat()
687
+ if (path.is_symlink() or not stat.S_ISREG(info.st_mode) or info.st_uid!=os.getuid() or info.st_nlink!=1 or
688
+ info.st_mode&0o077 or info.st_size>65536):
689
+ raise PackError('Exact owned bounded native-cli input required')
690
+ value=lanes.native_cli_input(read_json(path))
691
+ (lanes.native_cli_admission if _admit is None else _admit)(value)
692
+ context=value['context']
693
+ if (args.environment!=context['environment'] or args.context_key!=context['contextKey'] or args.role!=context['logicalId'] or
694
+ value['sourceRoleKey']!=args.name+'/'+args.role):
695
+ raise PackError('Native-cli command differs from the exact source/operation')
696
+ file=binding_path(lib,args.name,args.environment,args.context_key);info=file.lstat()
697
+ if (file.is_symlink() or not stat.S_ISREG(info.st_mode) or info.st_uid!=os.getuid() or info.st_nlink!=1 or
698
+ hashlib.sha256(file.read_bytes()).hexdigest()!=value['source']['bindingDigest']):
699
+ raise PackError('Native-cli current owned binding source differs')
700
+ binding=read_json(file);roles=[r for r in binding['input']['roles'] if r['logicalId']==args.role]
701
+ if (len(roles)!=1 or binding['identity']!={'userId':context['principalId'],'organizationId':context['organizationId']} or
702
+ binding['packRevision']!=value['source']['packRevision'] or binding['input']['instanceId']!=context['installationId'] or
703
+ binding['agentWorkspaceId']!=context['agentWorkspaceId'] or binding['input']['bytesWorkspaceId']!=context['bytesWorkspaceId'] or
704
+ binding['input']['bytesScope']!=context['bytesScope'] or roles[0]['agentId']!=context['agentId'] or roles[0]['agentVersionId']!=context['agentVersionId']):
705
+ raise PackError('Native-cli source principal/Agent/Bytes binding differs')
706
+ env=native_cli_selected_environment(value,binding,os.environ)
707
+ client=(Client if _client_factory is None else _client_factory)(args.environment,args.context_key,
708
+ binary=value['cli']['entry'],env=env,node=value['cli']['nodeExecutable'])
709
+ call=native_cli_command_response if _response is None else _response
710
+ identity,identity_events=call(client,['whoami'],'auth')
711
+ if (identity.get('user',{}).get('id')!=context['principalId'] or (identity.get('organization')or{}).get('id')!=context['organizationId']):
712
+ raise PackError('Actual native-cli public identity differs')
713
+ with tempfile.TemporaryDirectory(prefix='topolo-native-cli-context-') as directory:
714
+ input_file=pathlib.Path(directory)/'input.json';atomic_json(input_file,{'instanceId':context['installationId'],'logicalId':context['logicalId']})
715
+ envelope,context_events=call(client,['actions','call','app_topolo_agent.agent_packs.context','--detail','--data','@'+str(input_file),
716
+ '--resource-type','workspace','--resource-id',context['agentWorkspaceId']],'agent')
717
+ result=envelope.get('data',envelope)
718
+ actual=result.get('context',{})
719
+ expected={'organizationId':context['organizationId'],'workspaceId':context['agentWorkspaceId'],'instanceId':context['installationId'],
720
+ 'logicalId':context['logicalId'],'agentId':context['agentId'],'agentVersionId':context['agentVersionId'],
721
+ 'artifacts':{'appId':'app_topolo_bytes','resourceType':'workspace','resourceId':context['bytesWorkspaceId'],'scope':context['bytesScope']}}
722
+ if envelope.get('success') is False or any(actual.get(k)!=v for k,v in expected.items()):
723
+ raise PackError('Actual native-cli public Agent/Bytes context differs')
724
+ # Recheck mutable public binding, binary and stat-only profile after awaited reads.
725
+ if hashlib.sha256(file.read_bytes()).hexdigest()!=value['source']['bindingDigest']:
726
+ raise PackError('Native-cli binding changed during response; no receipt ACK')
727
+ native_cli_selected_environment(value,binding,os.environ)
728
+ (lanes.native_cli_admission if _admit is None else _admit)(value)
729
+ responses={}
730
+ for kind,body,events in [('identity',{'userId':context['principalId'],'organizationId':context['organizationId']},identity_events),
731
+ ('context',expected,context_events)]:
732
+ responses[kind]={'requestEvents':events,'bodyProjectionDigest':lanes.digest(body),'actionId':None if kind=='identity' else 'app_topolo_agent.agent_packs.context',
733
+ 'resourceType':None if kind=='identity' else 'workspace','resourceId':None if kind=='identity' else context['agentWorkspaceId']}
734
+ receipt={k:value[k] for k in lanes.NATIVE_CLI_RECEIPT_FIELDS-{'format','schemaVersion','responses','claim'}}
735
+ receipt.update(format='topolo-native-cli-continuity-receipt',schemaVersion=1,responses=responses,
736
+ claim='PUBLIC_IDENTITY_CONTEXT_RESPONSE_ONLY_NATIVE_ATTRIBUTION_REQUIRED')
737
+ return lanes.native_cli_receipt(value,receipt)