axstack 0.56.0 → 0.56.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axstack",
3
- "version": "0.56.0",
3
+ "version": "0.56.1",
4
4
  "description": "Axstack installer and setup CLI: installs owned chat skills and role data, configures supported harness settings, and checks T3 Code capabilities.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -25,7 +25,7 @@ Synthetic evidence can pass binding checks; helpers cannot authenticate it.
25
25
  3. Keep the model/effort/provider pins from the run; do not re-resolve to another
26
26
  model when quota/auth/timeout fails. Missing native `t3_thread_configuration`
27
27
  means unavailable effective fields stay unknown. Catalog runtimeModes advertise
28
- support; they do not prove effective access. Native task/thread facts supply
28
+ provider restrictions; they do not prove effective access. Native task/thread facts supply
29
29
  only the fields actually returned. A worker assertion is not native readback.
30
30
  4. Normalize current driver assignments and the pending batch as below. Sample
31
31
  telemetry with C2's bounded command, then recompute C1 immediately before each
@@ -71,7 +71,25 @@ excludes the forwarding environmentId, and evidence points to the preserved raw
71
71
  native call/result. environment/project/capabilities are read on the selected
72
72
  host; links are read on origin. The capabilities envelope uses `valueText` for
73
73
  exact raw catalog bytes instead of value. Project repositoryIdentity.canonicalKey
74
- must match the requested repositoryId. Unknown mappings hold rather than guessing.
74
+ must match the requested repositoryId. If project-read identity is null/omitted,
75
+ `projectList` may carry an ordered array of `t3_project_list` read envelopes on
76
+ that same environment. Collect from cursor 0 through native nextCursor:null,
77
+ recording actual cursor/limit arguments (native defaults: 0/20, limit at most 100).
78
+ Every page must join without gaps, extra arguments or duplicate project IDs.
79
+ The selected project's ID and workspaceRoot must equal project-read; every
80
+ available canonicalKey must equal repositoryId, including when both reads have
81
+ identity. Missing identity in both, incomplete pagination and any conflict hold.
82
+
83
+ For observed T3 0.1.5 native Codex/Claude drivers, omitted provider runtimeModes
84
+ means no provider restriction: native resolveRuntimeMode and RuntimePolicy
85
+ preserve the requested mode. Bind this only with catalog runtimeMode equal to
86
+ the request, exactly one selected provider, canRunChildTask:true and an explicit
87
+ empty constraints array. Explicit mode restrictions must include the requested
88
+ mode. Unknown version/driver omission semantics, missing caller authority,
89
+ unavailable providers or restrictions hold. This is preflight support, never
90
+ proof of a child's effective mode; terminal receipts still require actual native
91
+ readback. Preserve the exact catalog bytes/digest; do not synthesize runtimeModes
92
+ or repositoryIdentity in the saved raw reads. Unknown mappings hold rather than guessing.
75
93
  The pure `inspectDestination` API returns per-term pass/fail/unknown with evidence;
76
94
  `dispatch-plan` holds on a failed/unknown term. This is supplied binding proof only.
77
95
 
@@ -131,7 +149,51 @@ For isolated review include checkout with those fields, candidate/base and readb
131
149
  Never infer local file existence from a remote absolute path.
132
150
  Requested settings remain separate from effective native status.providerInstanceId,
133
151
  model/options and thread.providerInstanceId/model/runtimeMode. Missing effective
134
- options or mode is unknown and holds required verification.
152
+ options or mode is unknown and holds required verification, except for the bounded
153
+ T3 0.1.5 initial remote-launch join below.
154
+
155
+ ### T3 0.1.5 initial remote receipt
156
+
157
+ The remote `readRemoteTask` emitter omits options and returns null childRunId and
158
+ latestTerminalRunId. Preserve those raw omissions/nulls. A current family shell
159
+ is current thread metadata, not an immutable settings record for an older run.
160
+ Never use it to authorize a continuation from an origin summary lacking a run ID.
161
+
162
+ For the sole initial remote run, optional receipt `nativeFamily` can corroborate
163
+ settings and run identity. The driver acquires `orchestration.getThreadFamily`
164
+ with exact `{threadId}` on the actual destination using an authenticated native
165
+ RPC read with `orchestration:read` scope. This is metadata access, not agent
166
+ execution or an alternate worker launch path. Record host/environment identity,
167
+ authenticated acquisition provenance, exact request/result, and independently
168
+ observed server version. Do not execute an agent through SSH/CLI to obtain it.
169
+ Temporary read sessions require their authorized lifecycle and revocation; never
170
+ put tokens/tickets into evidence. This contract grants no auth/session authority.
171
+
172
+ The envelope is `{tool:"orchestration.getThreadFamily",transport:"t3-native-rpc",
173
+ environmentId,serverVersion:"0.1.5",args:{threadId},observedAt,evidence,value}`.
174
+ `value` is the unmodified native `{snapshotSequence,threads}` response. Retain
175
+ raw bytes separately, including any diagnostic/revocation trailer; parse only
176
+ the JSON response into a separate file. Never rewrite the raw response as JSON.
177
+ The version must also match selected destination versions; observe the family
178
+ after thread/wait and no later than receipt evaluation. Missing provenance holds.
179
+
180
+ Require a unique child with the selected project, exact title/current run,
181
+ provider/model/options/mode and delegatedFrom origin environment/thread. Every
182
+ returned family member must explicitly have idle/completed status, null active
183
+ run/request, zero pending guest input and an empty pendingBackgroundTasks array.
184
+ Unknown or live descendants hold even when remote status claims no pending child.
185
+
186
+ The fallback requires native thread runCount:1, exactly one recentRuns row at
187
+ ordinal:1, null local child IDs/childRunIds on status/submission, null origin
188
+ latestTerminalRunId, and identical origin/destination final text. Existing
189
+ current-run, completed terminal, marker, question and host/path checks still
190
+ apply. Explicit status options or run IDs cannot be overwritten by family facts.
191
+ T3 remote launch forwards the accepted explicit model selection and uses it for
192
+ both thread creation and the initial message; this is the source-backed scope
193
+ of the join. More runs or missing counts/ordinals hold. Later continuations need
194
+ actual result-run/per-run settings provenance; repeated matching markers or
195
+ current family settings are insufficient. Complete native statuses retain their
196
+ existing run-ID path. Saved inputs cannot authenticate their own provenance.
135
197
 
136
198
  `inspectFleetReceipt(receipt,{attempt,recommendation,recommendationJSON,now})`
137
199
  returns terms, never authority. Existing dispatch-plan --verify can additionally
@@ -168,6 +168,11 @@ Optional `nativeInventory` is normalized before counts under
168
168
  Unknown native totals are null; balance ranking uses the separately labelled
169
169
  pendingLowerBound. Counts also retain inventoryReasons/evidence; flags.countsUnknown follows normalized status.
170
170
  Optional `nativeDestination` carries observed native forwarded preflight reads.
171
+ Optional `nativeDestination.projectList` is the complete ordered native project-list
172
+ page envelopes used to join a cold project-read repository identity; preserve
173
+ raw reads and catalogs. Conflicting identities/workspaces or incomplete pages hold.
174
+ T3 0.1.5 native provider mode omissions require the documented caller/provider
175
+ authority evidence, never synthesized catalog fields.
171
176
  The same reference defines terminal receipt validation and its supplied-evidence
172
177
  limitation. Live drivers require these observed facts, not synthetic caller claims.
173
178
  Legacy normalized input remains unchanged. Remote coordinator remains disabled.
@@ -7,6 +7,9 @@ export function inspectDestination(observation, recommendation, capabilitiesText
7
7
  state: pass ? 'pass' : missing ? 'unknown' : 'fail', evidence: evidence ?? null });
8
8
  const s = recommendation.selected;
9
9
  const o = observation ?? {};
10
+ const observed = (read, tool, environmentId, args) => read?.tool === tool
11
+ && read?.environmentId === environmentId && canonical(read?.args) === canonical(args)
12
+ && time(read?.observedAt) && read.observedAt <= now && text(read?.evidence);
10
13
  for (const [name, tool, environmentId, args] of [
11
14
  ['environment', 't3_environment_read', s.environmentId, {}],
12
15
  ['project', 't3_project_read', s.environmentId, { projectId: s.projectId }],
@@ -14,22 +17,58 @@ export function inspectDestination(observation, recommendation, capabilitiesText
14
17
  ['links', 't3_environment_links', recommendation.originEnvironmentId, {}],
15
18
  ]) {
16
19
  const read = o[name];
17
- term('destination-observation', read?.tool === tool && read?.environmentId === environmentId
18
- && canonical(read?.args) === canonical(args) && time(read?.observedAt)
19
- && read.observedAt <= now && text(read?.evidence), read);
20
+ term('destination-observation', observed(read, tool, environmentId, args), read);
20
21
  }
21
22
  term('destination-identity', o.environment?.value?.environmentId === s.environmentId,
22
23
  o.environment?.value?.environmentId);
23
24
  term('destination-project', o.project?.value?.id === s.projectId && text(o.project?.value?.workspaceRoot),
24
25
  o.project?.value);
25
- term('destination-repository', o.project?.value?.repositoryIdentity?.canonicalKey === s.repositoryId,
26
- o.project?.value?.repositoryIdentity);
26
+ const project = o.project?.value;
27
+ let listed;
28
+ if (o.projectList !== undefined) {
29
+ const pages = o.projectList;
30
+ const projects = [];
31
+ let cursor = 0;
32
+ let complete = Array.isArray(pages) && pages.length > 0;
33
+ if (complete) for (const page of pages) {
34
+ const args = page?.args;
35
+ const limit = args?.limit ?? 20;
36
+ const rows = page?.value?.projects;
37
+ const next = page?.value?.nextCursor;
38
+ complete &&= cursor !== null && observed(page, 't3_project_list', s.environmentId, {
39
+ ...(args?.cursor === undefined ? {} : { cursor }),
40
+ ...(args?.limit === undefined ? {} : { limit }),
41
+ }) && (args?.cursor ?? 0) === cursor && Number.isInteger(limit) && limit >= 1 && limit <= 100
42
+ && Array.isArray(rows) && rows.length <= limit
43
+ && (next === null || (next === cursor + limit && rows.length === limit));
44
+ if (Array.isArray(rows)) projects.push(...rows);
45
+ cursor = next;
46
+ }
47
+ listed = projects.find((p) => p?.id === s.projectId);
48
+ term('destination-project-list', complete && cursor === null
49
+ && projects.every((p) => text(p?.id)) && new Set(projects.map((p) => p?.id)).size === projects.length
50
+ && listed?.id === s.projectId && listed.id === project?.id && listed?.workspaceRoot === project?.workspaceRoot, pages);
51
+ }
52
+ // ProjectService.listPage resolves the cold identity cache; getById need not.
53
+ const identities = [project?.repositoryIdentity, listed?.repositoryIdentity].filter((v) => v != null);
54
+ term('destination-repository', identities.length > 0
55
+ && identities.every((v) => v.canonicalKey === s.repositoryId), identities,
56
+ identities.length === 0);
27
57
  term('destination-catalog', o.capabilities?.valueText === capabilitiesText, o.capabilities?.valueText);
28
58
  const catalog = JSON.parse(capabilitiesText);
29
- const provider = catalog.providers?.find((v) => v.providerInstanceId === recommendation.request.providerInstanceId);
30
- term('destination-execution', provider?.canRunChildTask === true
31
- && provider?.runtimeModes?.includes(recommendation.request.runtimeMode), provider,
32
- provider?.canRunChildTask == null || !Array.isArray(provider?.runtimeModes));
59
+ const providers = catalog.providers?.filter((v) => v.providerInstanceId === recommendation.request.providerInstanceId);
60
+ const provider = providers?.length === 1 ? providers[0] : undefined;
61
+ const mode = recommendation.request.runtimeMode;
62
+ // T3 0.1.5 resolveRuntimeMode/RuntimePolicy preserve modes without restrictions.
63
+ // Limit omission semantics to the observed version and native drivers we checked.
64
+ const unrestricted = provider?.runtimeModes === undefined && o.environment?.value?.serverVersion === '0.1.5'
65
+ && ['codex', 'claudeAgent'].includes(provider?.driverKind);
66
+ const supported = unrestricted || (Array.isArray(provider?.runtimeModes) && provider.runtimeModes.includes(mode));
67
+ term('destination-execution', provider?.canRunChildTask === true && supported
68
+ && catalog.runtimeMode === mode && Array.isArray(provider?.constraints) && provider.constraints.length === 0,
69
+ { provider, runtimeMode: catalog.runtimeMode, serverVersion: o.environment?.value?.serverVersion },
70
+ providers?.length === 0 || provider?.canRunChildTask == null || catalog.runtimeMode == null
71
+ || provider?.constraints == null || (provider?.runtimeModes === undefined && !unrestricted));
33
72
  const local = s.environmentId === recommendation.originEnvironmentId;
34
73
  const link = o.links?.value?.links?.find((v) => v.environmentId === s.environmentId);
35
74
  term('destination-access', o.links?.value?.environmentId === recommendation.originEnvironmentId
@@ -0,0 +1,35 @@
1
+ // T3 0.1.5 remote status omits options and a result-run ID. Never synthesize them.
2
+ import { canonical, text, time } from './fleet-config.js';
3
+
4
+ export function inspectNativeFamily(receipt, expected) {
5
+ const r = receipt;
6
+ const n = r.nativeFamily;
7
+ const s = expected.recommendation.selected;
8
+ const a = expected.attempt;
9
+ const child = r.status?.value?.remoteChild?.threadId;
10
+ const current = r.thread?.value?.thread?.latestRunId;
11
+ const terms = [];
12
+ const term = (pass, evidence) => terms.push({ code: 'receipt-native-family',
13
+ state: pass ? 'pass' : evidence == null ? 'unknown' : 'fail', evidence: evidence ?? null });
14
+ term(n?.tool === 'orchestration.getThreadFamily' && n?.transport === 't3-native-rpc'
15
+ && n?.environmentId === s.environmentId && n?.serverVersion === '0.1.5'
16
+ && s.versions?.t3 === n.serverVersion && canonical(n?.args) === canonical({ threadId: child })
17
+ && time(n?.observedAt) && n.observedAt <= expected.now
18
+ && n.observedAt >= r.thread?.observedAt && n.observedAt >= r.terminal?.observedAt
19
+ && text(n?.evidence), n);
20
+ const rows = n?.value?.threads;
21
+ const matches = Array.isArray(rows) ? rows.filter((v) => v?.id === child) : [];
22
+ term(time(n?.value?.snapshotSequence) && Array.isArray(rows)
23
+ && rows.every((v) => text(v?.id)) && new Set(rows.map((v) => v.id)).size === rows.length
24
+ && matches.length === 1, n?.value);
25
+ const shell = matches[0];
26
+ term(shell?.id === child && shell?.projectId === s.projectId && shell?.title === a?.title
27
+ && shell?.latestRunId === current && text(current)
28
+ && shell?.delegatedFrom?.environmentId === a?.origin?.environmentId
29
+ && shell?.delegatedFrom?.threadId === a?.origin?.threadId, shell);
30
+ term(shell?.status === 'completed' && Array.isArray(rows)
31
+ && rows.every((v) => ['completed', 'idle'].includes(v?.status) && v?.activeRunId === null
32
+ && Array.isArray(v?.pendingBackgroundTasks) && v.pendingBackgroundTasks.length === 0
33
+ && v?.pendingRuntimeRequest === null && v?.pendingGuestMessageCount === 0), rows);
34
+ return { terms, shell };
35
+ }
@@ -1,5 +1,6 @@
1
1
  // Saved evidence validator. A pass binds supplied facts; it cannot authenticate them.
2
2
  import { canonical, record, text, time } from './fleet-config.js';
3
+ import { inspectNativeFamily } from './fleet-native-receipt.js';
3
4
 
4
5
  function options(value) {
5
6
  if (!Array.isArray(value)) return record(value) ? value : null;
@@ -57,12 +58,30 @@ export function inspectFleetReceipt(receipt, expected) {
57
58
  && target?.providerInstanceId === request.providerInstanceId && target?.model === request.model
58
59
  && same(options(target?.options), request.options) && r.requested?.runtimeMode === request.runtimeMode,
59
60
  r.requested);
61
+ const native = r.nativeFamily === undefined ? null : inspectNativeFamily(r, expected);
62
+ if (native) terms.push(...native.terms);
63
+ const shell = native?.shell;
64
+ const nativeValid = remote && native?.terms.every((t) => t.state === 'pass');
65
+ if (native) term('receipt-effective', shell?.providerInstanceId === request.providerInstanceId
66
+ && shell?.modelSelection?.instanceId === request.providerInstanceId
67
+ && shell?.modelSelection?.model === request.model
68
+ && Array.isArray(shell?.modelSelection?.options)
69
+ && same(options(shell?.modelSelection?.options), request.options)
70
+ && shell?.runtimeMode === request.runtimeMode, shell?.modelSelection);
71
+ const runs = r.thread?.value?.recentRuns;
72
+ // Without the origin result-run ID, only the sole initial run can own its summary.
73
+ const initial = nativeValid && status?.childThreadId === null && submitted?.childThreadId === null
74
+ && status?.childRunId === null && submitted?.childRunId === null
75
+ && status?.latestTerminalRunId === null && thread?.runCount === 1
76
+ && Array.isArray(runs) && runs.length === 1 && runs[0]?.ordinal === 1;
77
+ const effectiveOptions = status?.options === undefined && initial
78
+ ? shell?.modelSelection?.options : status?.options;
60
79
  term('receipt-effective', status?.providerInstanceId === request.providerInstanceId
61
80
  && status?.model === request.model && thread?.providerInstanceId === request.providerInstanceId
62
- && thread?.model === request.model && same(options(status?.options), request.options)
81
+ && thread?.model === request.model && same(options(effectiveOptions), request.options)
63
82
  && thread?.runtimeMode === request.runtimeMode, { provider: status?.providerInstanceId,
64
- model: status?.model, options: status?.options ?? null, runtimeMode: thread?.runtimeMode },
65
- status?.options == null || thread?.runtimeMode == null);
83
+ model: status?.model, options: effectiveOptions ?? null, runtimeMode: thread?.runtimeMode },
84
+ effectiveOptions == null || thread?.runtimeMode == null);
66
85
  const terminal = r.terminal?.value;
67
86
  term('receipt-terminal', status?.status === 'completed' && status?.latestTerminalStatus === 'completed'
68
87
  && status?.workState === 'result_available' && status?.hasPendingChildRuns === false
@@ -84,9 +103,8 @@ export function inspectFleetReceipt(receipt, expected) {
84
103
  && questions.value.requests.length === 0, questions,
85
104
  questions == null || questions.value?.requests === undefined);
86
105
  const current = thread?.latestRunId;
87
- const runs = r.thread?.value?.recentRuns;
88
106
  const latest = Array.isArray(runs) ? runs.find((v) => v.runId === current) : null;
89
- term('receipt-run', text(current) && status?.latestTerminalRunId === current
107
+ term('receipt-run', text(current) && (status?.latestTerminalRunId === current || initial)
90
108
  && terminal?.threadId === child(status) && terminal?.runId === current
91
109
  && (!terminal?.resultRun || (terminal.resultRun.runId === current && terminal.resultRun.status === 'completed'))
92
110
  && (!remote || (r.remoteChild?.environmentId === s.environmentId
@@ -94,7 +112,7 @@ export function inspectFleetReceipt(receipt, expected) {
94
112
  && latest?.status === 'completed' && Number.isSafeInteger(latest?.ordinal)
95
113
  && runs.every((v) => Number.isSafeInteger(v.ordinal) && v.ordinal <= latest.ordinal),
96
114
  { current, originTerminal: status?.latestTerminalRunId, terminal, runs },
97
- !current || !status?.latestTerminalRunId || !terminal?.runId || !Array.isArray(runs)
115
+ !current || (!status?.latestTerminalRunId && !initial) || !terminal?.runId || !Array.isArray(runs)
98
116
  || (remote && !r.remoteChild?.runId));
99
117
  const marker = `AXSTACK-DONE key=${a?.key} head=${a?.candidate} report=${r.evidence?.path}`;
100
118
  const finalMarker = (value) => {
@@ -110,7 +128,8 @@ export function inspectFleetReceipt(receipt, expected) {
110
128
  const items = r.thread?.value?.items;
111
129
  const final = Array.isArray(items) ? items.filter((v) => v.runId === current && v.createdBy === 'agent').at(-1) : null;
112
130
  term('receipt-marker', /^[a-f0-9]{40}$/.test(a?.candidate ?? '')
113
- && finalMarker(status?.latestTerminalSummary) === marker && finalMarker(final?.text) === marker,
131
+ && finalMarker(status?.latestTerminalSummary) === marker && finalMarker(final?.text) === marker
132
+ && (!initial || status?.latestTerminalSummary === final?.text),
114
133
  { origin: status?.latestTerminalSummary, destination: final?.text });
115
134
  term('receipt-host-path', r.evidence?.hostId === s.hostId && r.evidence?.environmentId === s.environmentId
116
135
  && absolute(r.evidence?.path) && finalMarker(status?.latestTerminalSummary)?.endsWith(` report=${r.evidence.path}`), r.evidence);