instar 1.3.1337 → 1.3.1339

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +8 -0
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  5. package/dist/core/PostUpdateMigrator.js +13 -0
  6. package/dist/core/PostUpdateMigrator.js.map +1 -1
  7. package/dist/core/SessionRefresh.d.ts +21 -0
  8. package/dist/core/SessionRefresh.d.ts.map +1 -1
  9. package/dist/core/SessionRefresh.js +81 -30
  10. package/dist/core/SessionRefresh.js.map +1 -1
  11. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  12. package/dist/core/devGatedFeatures.js +6 -0
  13. package/dist/core/devGatedFeatures.js.map +1 -1
  14. package/dist/core/types.d.ts +12 -0
  15. package/dist/core/types.d.ts.map +1 -1
  16. package/dist/core/types.js.map +1 -1
  17. package/dist/data/standards-guard-index.json +1 -1
  18. package/dist/data/standards-guard-index.meta.json +2 -2
  19. package/dist/data/standards-registry.meta.json +1 -1
  20. package/dist/scaffold/templates.d.ts.map +1 -1
  21. package/dist/scaffold/templates.js +4 -0
  22. package/dist/scaffold/templates.js.map +1 -1
  23. package/dist/server/routes.d.ts.map +1 -1
  24. package/dist/server/routes.js +32 -0
  25. package/dist/server/routes.js.map +1 -1
  26. package/dist/threadline/AgentTrustManager.d.ts +40 -3
  27. package/dist/threadline/AgentTrustManager.d.ts.map +1 -1
  28. package/dist/threadline/AgentTrustManager.js +55 -7
  29. package/dist/threadline/AgentTrustManager.js.map +1 -1
  30. package/dist/threadline/ThreadlineBootstrap.d.ts +7 -0
  31. package/dist/threadline/ThreadlineBootstrap.d.ts.map +1 -1
  32. package/dist/threadline/ThreadlineBootstrap.js +34 -15
  33. package/dist/threadline/ThreadlineBootstrap.js.map +1 -1
  34. package/dist/threadline/inboundIdLedgerWiring.d.ts +2 -2
  35. package/dist/threadline/relayUnknownSenderTrust.d.ts +136 -0
  36. package/dist/threadline/relayUnknownSenderTrust.d.ts.map +1 -0
  37. package/dist/threadline/relayUnknownSenderTrust.js +208 -0
  38. package/dist/threadline/relayUnknownSenderTrust.js.map +1 -0
  39. package/package.json +1 -1
  40. package/src/data/builtin-manifest.json +63 -63
  41. package/src/data/standards-guard-index.json +1 -1
  42. package/src/data/standards-guard-index.meta.json +2 -2
  43. package/src/data/standards-registry.meta.json +1 -1
  44. package/src/scaffold/templates.ts +4 -0
  45. package/upgrades/1.3.1338.md +30 -0
  46. package/upgrades/1.3.1339.md +20 -0
  47. package/upgrades/a2a-relay-unknown-sender-trust.eli16.md +21 -0
  48. package/upgrades/side-effects/a2a-relay-unknown-sender-trust.md +129 -0
  49. package/upgrades/side-effects/sessions-refresh-prebind.md +113 -0
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Relay unknown-sender trust (docs/specs/a2a-relay-unknown-sender-trust.md, ACT-066).
3
+ *
4
+ * The relay emits `unknown-sender` for a message whose sender's keys this agent
5
+ * does not hold (it cannot decrypt it, so the payload is plaintext). Until this
6
+ * module, that path skipped InboundMessageGate entirely and handed every such
7
+ * message onward at trust level `verified` (reason `relay-authenticated`) — and
8
+ * recording the interaction created a fingerprint trust profile at `verified`,
9
+ * so the sender stayed `verified` for every later message on every path.
10
+ *
11
+ * The relay proves the sender holds the private key of its fingerprint. It does
12
+ * not prove who the sender is, and nobody granted that fingerprint anything.
13
+ *
14
+ * This module resolves the sender's trust from the SAME AgentTrustManager the
15
+ * relay gate reads and applies the SAME operation-permission table. The trust
16
+ * manager stays the single authority for level → operations; nothing here
17
+ * re-implements it. Dry-run (the default when on) logs and counts would-refuse
18
+ * verdicts and delivers exactly as before; only an explicit `dryRun: false`
19
+ * refuses, emits the held level, and stops new fingerprint profiles from being
20
+ * created at `verified`.
21
+ */
22
+ import type { AgentTrustLevel, AgentTrustManager } from './AgentTrustManager.js';
23
+ import type { ReceivedMessage } from './client/ThreadlineClient.js';
24
+ /** The slice of AgentTrustManager this module reads and writes. */
25
+ export type RelayUnknownSenderTrustManager = Pick<AgentTrustManager, 'getProfileByFingerprint' | 'getAllowedOperationsByFingerprint' | 'recordMessageReceivedByFingerprint' | 'getOrCreateProfileByFingerprint'>;
26
+ /** An operation name no trust level allows: a wire `type` that is present but unusable. */
27
+ export declare const INVALID_RELAY_OPERATION = "(invalid-type)";
28
+ /**
29
+ * The operation a plaintext relay payload asks for. The plaintext wire carries
30
+ * `chat` for a message and `ack` for a delivery ack (ThreadlineClient); neither
31
+ * name is in the level table, so `chat` (and a missing type) map to `message`.
32
+ * Any other string is taken as the operation name, exactly as the relay gate
33
+ * takes an E2E `type`; a non-string type maps to a name nothing allows.
34
+ */
35
+ export declare function classifyRelayPlaintextOperation(wireType: unknown): string;
36
+ export interface RelayUnknownSenderVerdict {
37
+ /** The level the trust manager holds for the fingerprint (`untrusted` with no profile). */
38
+ level: AgentTrustLevel;
39
+ operation: string;
40
+ /** May this level perform this operation, per the trust manager's table. */
41
+ allowed: boolean;
42
+ /** A trust profile counts for the fingerprint (a first-contact profile does not). */
43
+ hasProfile: boolean;
44
+ }
45
+ /**
46
+ * Resolve the sender's held trust level and whether it may perform the
47
+ * operation. Reads only.
48
+ *
49
+ * A profile this path itself wrote at first contact while observing
50
+ * (`relayFirstContact`, source still `setup-default`) counts as no profile:
51
+ * under enforcement it would never have been written at `verified`. Once the
52
+ * operator or a pairing decides (any other source), the profile counts.
53
+ */
54
+ export declare function resolveRelayUnknownSenderTrust(tm: Pick<RelayUnknownSenderTrustManager, 'getProfileByFingerprint' | 'getAllowedOperationsByFingerprint'>, fingerprint: string, wireType: unknown): RelayUnknownSenderVerdict;
55
+ export interface RelayUnknownSenderTrustMode {
56
+ /** Gate-resolved: `enabled` omitted ⇒ the developmentAgent gate decides. */
57
+ enabled: boolean;
58
+ /** Default TRUE: log + count would-refuse verdicts, deliver exactly as today. */
59
+ dryRun: boolean;
60
+ }
61
+ interface RelayUnknownSenderTrustConfigShape {
62
+ developmentAgent?: boolean;
63
+ threadline?: {
64
+ relayUnknownSenderTrust?: {
65
+ enabled?: boolean;
66
+ dryRun?: boolean;
67
+ };
68
+ };
69
+ }
70
+ /** The live mode (spec "Configuration"). Read per message. */
71
+ export declare function resolveRelayUnknownSenderTrustMode(live: {
72
+ enabled?: boolean;
73
+ dryRun?: boolean;
74
+ }, config: RelayUnknownSenderTrustConfigShape): RelayUnknownSenderTrustMode;
75
+ /** True only when the check is on AND enforcing. */
76
+ export declare function isRelayUnknownSenderTrustEnforcing(mode: RelayUnknownSenderTrustMode): boolean;
77
+ /**
78
+ * The level a NEW fingerprint trust profile is created at. Today `verified`;
79
+ * `untrusted` while the check is enforcing — a first contact is not a grant.
80
+ */
81
+ export declare function newFingerprintProfileLevel(mode: RelayUnknownSenderTrustMode): AgentTrustLevel;
82
+ /** In-memory counters on the authed /health (the rollout evidence). */
83
+ export interface RelayUnknownSenderTrustCounters {
84
+ /** Unknown-sender messages the check ran on. */
85
+ evaluated: number;
86
+ /** The held level may perform the operation. */
87
+ allowed: number;
88
+ /** Dry-run: would have been refused; delivered as today. */
89
+ wouldRefuse: number;
90
+ /** Enforcing: refused, not delivered. */
91
+ refused: number;
92
+ /** Dry-run: a first contact wrote a `verified` profile, marked `relayFirstContact` (enforcement would write none). */
93
+ firstContactProfiles: number;
94
+ /** The trust lookup threw. */
95
+ lookupErrors: number;
96
+ /** New fingerprint profiles created at `untrusted` because the check was enforcing. */
97
+ profilesCreatedUntrusted: number;
98
+ }
99
+ export declare function createRelayUnknownSenderTrustCounters(): RelayUnknownSenderTrustCounters;
100
+ /** The one server-log line per would-refuse / refuse verdict. */
101
+ export declare function relayUnknownSenderTrustLogLine(kind: 'would-refuse' | 'refuse', fingerprint: string, v: RelayUnknownSenderVerdict): string;
102
+ /** What `gate-passed` carries (the shape the server's relay consumer reads). */
103
+ export interface RelayUnknownSenderPassDecision {
104
+ action: 'pass';
105
+ reason: 'relay-authenticated' | 'probe';
106
+ trustLevel: AgentTrustLevel;
107
+ fingerprint: string;
108
+ message: ReceivedMessage;
109
+ }
110
+ export interface RelayUnknownSenderHandlerDeps {
111
+ trustManager: RelayUnknownSenderTrustManager;
112
+ mode: () => RelayUnknownSenderTrustMode;
113
+ counters: RelayUnknownSenderTrustCounters;
114
+ emit: (decision: RelayUnknownSenderPassDecision) => void;
115
+ log?: (line: string) => void;
116
+ }
117
+ export type RelayUnknownSenderOutcome = 'passed-legacy' | 'passed-dry-run' | 'would-refuse-delivered' | 'passed' | 'refused' | 'refused-lookup-error';
118
+ /**
119
+ * Handle one unknown-sender relay message after the payload-size check.
120
+ *
121
+ * - off: exactly today's behaviour (record, emit `verified`).
122
+ * - dry-run: today's delivery, plus the verdict counted and logged.
123
+ * - enforcing: refuse when the held level may not perform the operation;
124
+ * otherwise emit the held level. The interaction is recorded only for a
125
+ * sender that already has a profile, so a stranger's probe writes nothing.
126
+ */
127
+ export declare function handleRelayUnknownSender(deps: RelayUnknownSenderHandlerDeps, received: ReceivedMessage, wireType: unknown): RelayUnknownSenderOutcome;
128
+ /**
129
+ * Process-wide counters (one relay client per server process). The bootstrap
130
+ * writes them; the authed /health reads them.
131
+ */
132
+ export declare const relayUnknownSenderTrustCounters: RelayUnknownSenderTrustCounters;
133
+ /** Tests only: zero the process-wide counters. */
134
+ export declare function resetRelayUnknownSenderTrustCounters(): void;
135
+ export {};
136
+ //# sourceMappingURL=relayUnknownSenderTrust.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relayUnknownSenderTrust.d.ts","sourceRoot":"","sources":["../../src/threadline/relayUnknownSenderTrust.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Relay unknown-sender trust (docs/specs/a2a-relay-unknown-sender-trust.md, ACT-066).
3
+ *
4
+ * The relay emits `unknown-sender` for a message whose sender's keys this agent
5
+ * does not hold (it cannot decrypt it, so the payload is plaintext). Until this
6
+ * module, that path skipped InboundMessageGate entirely and handed every such
7
+ * message onward at trust level `verified` (reason `relay-authenticated`) — and
8
+ * recording the interaction created a fingerprint trust profile at `verified`,
9
+ * so the sender stayed `verified` for every later message on every path.
10
+ *
11
+ * The relay proves the sender holds the private key of its fingerprint. It does
12
+ * not prove who the sender is, and nobody granted that fingerprint anything.
13
+ *
14
+ * This module resolves the sender's trust from the SAME AgentTrustManager the
15
+ * relay gate reads and applies the SAME operation-permission table. The trust
16
+ * manager stays the single authority for level → operations; nothing here
17
+ * re-implements it. Dry-run (the default when on) logs and counts would-refuse
18
+ * verdicts and delivers exactly as before; only an explicit `dryRun: false`
19
+ * refuses, emits the held level, and stops new fingerprint profiles from being
20
+ * created at `verified`.
21
+ */
22
+ import { resolveDevAgentGate } from '../core/devAgentGate.js';
23
+ /** An operation name no trust level allows: a wire `type` that is present but unusable. */
24
+ export const INVALID_RELAY_OPERATION = '(invalid-type)';
25
+ /** Probes, as in InboundMessageGate: they pass with reason `probe`. */
26
+ const PROBE_OPS = new Set(['ping', 'health']);
27
+ /**
28
+ * Delivery acks are consumed by the ack stage before the inbox, the warrants
29
+ * gate and every router (docs/specs/a2a-ack-never-acked.md): they only record
30
+ * that our own earlier send arrived. Refusing them would make every peer we
31
+ * write to look unreachable, so every level may send one.
32
+ */
33
+ const ACK_OP = 'ack';
34
+ /**
35
+ * The operation a plaintext relay payload asks for. The plaintext wire carries
36
+ * `chat` for a message and `ack` for a delivery ack (ThreadlineClient); neither
37
+ * name is in the level table, so `chat` (and a missing type) map to `message`.
38
+ * Any other string is taken as the operation name, exactly as the relay gate
39
+ * takes an E2E `type`; a non-string type maps to a name nothing allows.
40
+ */
41
+ export function classifyRelayPlaintextOperation(wireType) {
42
+ if (wireType === undefined || wireType === null || wireType === 'chat')
43
+ return 'message';
44
+ if (typeof wireType === 'string' && wireType)
45
+ return wireType;
46
+ return INVALID_RELAY_OPERATION;
47
+ }
48
+ /** Credentials never travel the plaintext fallback (verified-pairing spec). */
49
+ const CREDENTIAL_SHARE_OP = 'credential-share';
50
+ /**
51
+ * Resolve the sender's held trust level and whether it may perform the
52
+ * operation. Reads only.
53
+ *
54
+ * A profile this path itself wrote at first contact while observing
55
+ * (`relayFirstContact`, source still `setup-default`) counts as no profile:
56
+ * under enforcement it would never have been written at `verified`. Once the
57
+ * operator or a pairing decides (any other source), the profile counts.
58
+ */
59
+ export function resolveRelayUnknownSenderTrust(tm, fingerprint, wireType) {
60
+ const operation = classifyRelayPlaintextOperation(wireType);
61
+ const found = tm.getProfileByFingerprint(fingerprint);
62
+ const profile = found && !(found.relayFirstContact && found.source === 'setup-default') ? found : null;
63
+ const level = profile?.level ?? 'untrusted';
64
+ let allowed;
65
+ if (operation === ACK_OP) {
66
+ allowed = true;
67
+ }
68
+ else if (operation === CREDENTIAL_SHARE_OP) {
69
+ allowed = false;
70
+ }
71
+ else if (level === 'untrusted') {
72
+ // One intended difference from the gate's table: an `untrusted` sender's
73
+ // probe is NOT passed on. The gate passes probes expecting them handled
74
+ // inline, but the server's `gate-passed` consumer routes every message it
75
+ // receives to a session — so passing a stranger's ping would hand it the
76
+ // very session this check exists to deny. Only its delivery acks pass.
77
+ allowed = false;
78
+ }
79
+ else {
80
+ allowed = tm.getAllowedOperationsByFingerprint(fingerprint).includes(operation);
81
+ }
82
+ return { level, operation, allowed, hasProfile: !!profile };
83
+ }
84
+ /** The live mode (spec "Configuration"). Read per message. */
85
+ export function resolveRelayUnknownSenderTrustMode(live, config) {
86
+ const block = config.threadline?.relayUnknownSenderTrust;
87
+ const enabled = resolveDevAgentGate(live.enabled ?? block?.enabled, config);
88
+ // Only an explicit `false` leaves dry-run; anything else observes.
89
+ const dryRun = (live.dryRun ?? block?.dryRun) !== false;
90
+ return { enabled, dryRun };
91
+ }
92
+ /** True only when the check is on AND enforcing. */
93
+ export function isRelayUnknownSenderTrustEnforcing(mode) {
94
+ return mode.enabled && !mode.dryRun;
95
+ }
96
+ /**
97
+ * The level a NEW fingerprint trust profile is created at. Today `verified`;
98
+ * `untrusted` while the check is enforcing — a first contact is not a grant.
99
+ */
100
+ export function newFingerprintProfileLevel(mode) {
101
+ return isRelayUnknownSenderTrustEnforcing(mode) ? 'untrusted' : 'verified';
102
+ }
103
+ export function createRelayUnknownSenderTrustCounters() {
104
+ return {
105
+ evaluated: 0, allowed: 0, wouldRefuse: 0, refused: 0,
106
+ firstContactProfiles: 0, lookupErrors: 0, profilesCreatedUntrusted: 0,
107
+ };
108
+ }
109
+ /** Peer-supplied text is never logged raw: printable ASCII only, bounded. */
110
+ function logSafe(value) {
111
+ return value.replace(/[^\x21-\x7e]/g, '?').slice(0, 48) || 'unknown';
112
+ }
113
+ /** The one server-log line per would-refuse / refuse verdict. */
114
+ export function relayUnknownSenderTrustLogLine(kind, fingerprint, v) {
115
+ return `[relay-unknown-sender-trust] ${kind} fp=${logSafe(fingerprint).slice(0, 12)} profile=${v.hasProfile ? 'yes' : 'no'} trust=${v.level} op=${logSafe(v.operation)}`;
116
+ }
117
+ /**
118
+ * Handle one unknown-sender relay message after the payload-size check.
119
+ *
120
+ * - off: exactly today's behaviour (record, emit `verified`).
121
+ * - dry-run: today's delivery, plus the verdict counted and logged.
122
+ * - enforcing: refuse when the held level may not perform the operation;
123
+ * otherwise emit the held level. The interaction is recorded only for a
124
+ * sender that already has a profile, so a stranger's probe writes nothing.
125
+ */
126
+ export function handleRelayUnknownSender(deps, received, wireType) {
127
+ const { trustManager: tm, counters } = deps;
128
+ const log = deps.log ?? ((line) => console.log(line));
129
+ const fingerprint = received.from;
130
+ const mode = deps.mode();
131
+ const legacyDeliver = () => {
132
+ // Evidence only: an unreadable profile counts as existing (no first-contact mark).
133
+ let existed = true;
134
+ try {
135
+ existed = !!tm.getProfileByFingerprint(fingerprint);
136
+ }
137
+ catch { /* @silent-fallback-ok — counter evidence only */ }
138
+ if (mode.enabled && !existed) {
139
+ // Dry-run keeps today's profile write, but the profile is created already
140
+ // marked (one atomic write — a crash cannot leave it unmarked), so neither
141
+ // later dry-run verdicts nor enforcement count it as a grant.
142
+ counters.firstContactProfiles++;
143
+ try {
144
+ tm.getOrCreateProfileByFingerprint(fingerprint, undefined, { relayFirstContact: true });
145
+ }
146
+ catch { /* @silent-fallback-ok — the record below writes today's profile */ }
147
+ }
148
+ tm.recordMessageReceivedByFingerprint(fingerprint);
149
+ deps.emit({ action: 'pass', reason: 'relay-authenticated', trustLevel: 'verified', fingerprint, message: received });
150
+ };
151
+ if (!mode.enabled) {
152
+ legacyDeliver();
153
+ return 'passed-legacy';
154
+ }
155
+ let verdict;
156
+ try {
157
+ verdict = resolveRelayUnknownSenderTrust(tm, fingerprint, wireType);
158
+ }
159
+ catch (err) {
160
+ counters.lookupErrors++;
161
+ log(`[relay-unknown-sender-trust] lookup failed: ${err instanceof Error ? err.message : String(err)}`);
162
+ if (mode.dryRun) {
163
+ legacyDeliver();
164
+ return 'passed-dry-run';
165
+ }
166
+ // Enforcing and the authority cannot answer: fail closed. The relay sender
167
+ // gets no answer either way, exactly as for a gate block.
168
+ return 'refused-lookup-error';
169
+ }
170
+ counters.evaluated++;
171
+ if (mode.dryRun) {
172
+ if (verdict.allowed) {
173
+ counters.allowed++;
174
+ legacyDeliver();
175
+ return 'passed-dry-run';
176
+ }
177
+ counters.wouldRefuse++;
178
+ log(relayUnknownSenderTrustLogLine('would-refuse', fingerprint, verdict));
179
+ legacyDeliver();
180
+ return 'would-refuse-delivered';
181
+ }
182
+ if (!verdict.allowed) {
183
+ counters.refused++;
184
+ log(relayUnknownSenderTrustLogLine('refuse', fingerprint, verdict));
185
+ return 'refused';
186
+ }
187
+ counters.allowed++;
188
+ if (verdict.hasProfile)
189
+ tm.recordMessageReceivedByFingerprint(fingerprint);
190
+ deps.emit({
191
+ action: 'pass',
192
+ reason: PROBE_OPS.has(verdict.operation) ? 'probe' : 'relay-authenticated',
193
+ trustLevel: verdict.level,
194
+ fingerprint,
195
+ message: received,
196
+ });
197
+ return 'passed';
198
+ }
199
+ /**
200
+ * Process-wide counters (one relay client per server process). The bootstrap
201
+ * writes them; the authed /health reads them.
202
+ */
203
+ export const relayUnknownSenderTrustCounters = createRelayUnknownSenderTrustCounters();
204
+ /** Tests only: zero the process-wide counters. */
205
+ export function resetRelayUnknownSenderTrustCounters() {
206
+ Object.assign(relayUnknownSenderTrustCounters, createRelayUnknownSenderTrustCounters());
207
+ }
208
+ //# sourceMappingURL=relayUnknownSenderTrust.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relayUnknownSenderTrust.js","sourceRoot":"","sources":["../../src/threadline/relayUnknownSenderTrust.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.1337",
3
+ "version": "1.3.1339",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",