instar 1.3.618 → 1.3.620
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/dashboard/index.html +16 -1
- package/dashboard/subscriptions.js +151 -6
- package/dist/coordination/AccountFollowMeOperatorOutbox.d.ts +42 -0
- package/dist/coordination/AccountFollowMeOperatorOutbox.d.ts.map +1 -0
- package/dist/coordination/AccountFollowMeOperatorOutbox.js +81 -0
- package/dist/coordination/AccountFollowMeOperatorOutbox.js.map +1 -0
- package/dist/coordination/AccountFollowMeSingleFlight.d.ts +68 -0
- package/dist/coordination/AccountFollowMeSingleFlight.d.ts.map +1 -0
- package/dist/coordination/AccountFollowMeSingleFlight.js +135 -0
- package/dist/coordination/AccountFollowMeSingleFlight.js.map +1 -0
- package/dist/coordination/driveFollowMeEnrollment.d.ts +80 -0
- package/dist/coordination/driveFollowMeEnrollment.d.ts.map +1 -0
- package/dist/coordination/driveFollowMeEnrollment.js +64 -0
- package/dist/coordination/driveFollowMeEnrollment.js.map +1 -0
- package/dist/coordination/enrollPointOfUseCheck.d.ts +48 -0
- package/dist/coordination/enrollPointOfUseCheck.d.ts.map +1 -0
- package/dist/coordination/enrollPointOfUseCheck.js +45 -0
- package/dist/coordination/enrollPointOfUseCheck.js.map +1 -0
- package/dist/coordination/followMeConsumerSweep.d.ts +32 -0
- package/dist/coordination/followMeConsumerSweep.d.ts.map +1 -0
- package/dist/coordination/followMeConsumerSweep.js +58 -0
- package/dist/coordination/followMeConsumerSweep.js.map +1 -0
- package/dist/core/rawTextRequestDetector.d.ts +27 -0
- package/dist/core/rawTextRequestDetector.d.ts.map +1 -0
- package/dist/core/rawTextRequestDetector.js +42 -0
- package/dist/core/rawTextRequestDetector.js.map +1 -0
- package/dist/monitoring/guardManifest.d.ts.map +1 -1
- package/dist/monitoring/guardManifest.js +1 -0
- package/dist/monitoring/guardManifest.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +70 -4
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/scripts/instar-dev-precommit.js +38 -1
- package/scripts/lib/operator-surface.mjs +63 -0
- package/src/data/builtin-manifest.json +46 -46
- package/upgrades/1.3.619.md +33 -0
- package/upgrades/1.3.620.md +22 -0
- package/upgrades/side-effects/ws52-operator-card-wiring.md +60 -0
- package/upgrades/side-effects/ws52-operator-tap-not-text.md +112 -0
package/dist/server/routes.js
CHANGED
|
@@ -999,6 +999,14 @@ export function createRoutes(ctx) {
|
|
|
999
999
|
void observePrincipalCoherence(text, options.topicId).catch(() => {
|
|
1000
1000
|
/* @silent-fallback-ok — observe-only; a detector error never affects delivery */
|
|
1001
1001
|
});
|
|
1002
|
+
// ── Operators-Act-in-Taps-Not-Text Signal (OBSERVE-ONLY, ws52 Part C arm 2) ──
|
|
1003
|
+
// Record — but NEVER act on — an outbound message that asks the OPERATOR to paste
|
|
1004
|
+
// raw/technical text (JSON/fingerprints/curl/multi-step). Backstop to the arm-1
|
|
1005
|
+
// build-time gate; per signal-vs-authority this is a SIGNAL, not a blocker. Same
|
|
1006
|
+
// fire-and-forget VOID contract — it cannot block, delay, or rewrite the message.
|
|
1007
|
+
void observeRawTextRequest(text, options.topicId).catch(() => {
|
|
1008
|
+
/* @silent-fallback-ok — observe-only; a detector error never affects delivery */
|
|
1009
|
+
});
|
|
1002
1010
|
const decision = await evaluateOutbound(text, channel, options);
|
|
1003
1011
|
if (!decision.ok) {
|
|
1004
1012
|
res.status(decision.status).json(decision.body);
|
|
@@ -1248,6 +1256,38 @@ export function createRoutes(ctx) {
|
|
|
1248
1256
|
// outbound message is unaffected.
|
|
1249
1257
|
}
|
|
1250
1258
|
}
|
|
1259
|
+
// ── Operators-Act-in-Taps-Not-Text Signal (OBSERVE-ONLY, ws52 Part C arm 2) ──
|
|
1260
|
+
// Backstop to the arm-1 build-time gate: record — never act on — an outbound
|
|
1261
|
+
// message that asks the OPERATOR to paste raw/technical text. Per signal-vs-authority
|
|
1262
|
+
// this is a SIGNAL (records the occurrence), NOT a blocker (asking-for-JSON is not
|
|
1263
|
+
// irreversible, so the safety-guard block exception does not apply, and a keyword
|
|
1264
|
+
// blocker on a user-visible channel would wedge real conversations). Dark on the
|
|
1265
|
+
// fleet, live on the development agent. Records the SIGNAL (reasons + topic), never
|
|
1266
|
+
// the raw message text. Fire-and-forget; never affects delivery.
|
|
1267
|
+
async function observeRawTextRequest(text, topicId) {
|
|
1268
|
+
try {
|
|
1269
|
+
if (typeof text !== 'string' || text.trim().length === 0)
|
|
1270
|
+
return;
|
|
1271
|
+
if (!resolveDevAgentGate(undefined, ctx.config))
|
|
1272
|
+
return;
|
|
1273
|
+
const { detectRawTextRequestToOperator } = await import('../core/rawTextRequestDetector.js');
|
|
1274
|
+
const sig = detectRawTextRequestToOperator(text);
|
|
1275
|
+
if (!sig.detected)
|
|
1276
|
+
return;
|
|
1277
|
+
const file = path.join(ctx.config.stateDir, 'raw-text-request-signals.jsonl');
|
|
1278
|
+
try {
|
|
1279
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
1280
|
+
fs.appendFileSync(file, JSON.stringify({ ts: new Date().toISOString(), topicId: topicId ?? null, reasons: sig.reasons }) + '\n', 'utf8');
|
|
1281
|
+
}
|
|
1282
|
+
catch {
|
|
1283
|
+
/* @silent-fallback-ok — recording must never throw */
|
|
1284
|
+
}
|
|
1285
|
+
console.log(`[operators-act-in-taps] outbound asked operator for raw text: ${sig.reasons.join('; ')}`);
|
|
1286
|
+
}
|
|
1287
|
+
catch {
|
|
1288
|
+
// @silent-fallback-ok — observe-only; any error silently no-ops and delivery is unaffected.
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1251
1291
|
/**
|
|
1252
1292
|
* Log a tone-gate decision. Structured output for the over-block audit tail.
|
|
1253
1293
|
* Writes to stderr so it's captured in the server log.
|
|
@@ -6536,10 +6576,21 @@ export function createRoutes(ctx) {
|
|
|
6536
6576
|
res.status(400).json({ error: 'agents must be a pair of two agent fingerprints' });
|
|
6537
6577
|
return;
|
|
6538
6578
|
}
|
|
6539
|
-
|
|
6540
|
-
|
|
6579
|
+
// FD1 (ws52-operator-tap-not-text): the one-tap card sends NO expiry — the server
|
|
6580
|
+
// defaults to 1 hour (a mandate only needs to authorize one re-mint enrollment, not
|
|
6581
|
+
// standing access). A caller MAY still pass an explicit future expiry; an invalid one
|
|
6582
|
+
// is rejected. Mandates always expire either way.
|
|
6583
|
+
let expiresAt;
|
|
6584
|
+
if (b.expiresAt === undefined || b.expiresAt === null || b.expiresAt === '') {
|
|
6585
|
+
expiresAt = new Date(Date.now() + 60 * 60 * 1000).toISOString();
|
|
6586
|
+
}
|
|
6587
|
+
else if (typeof b.expiresAt !== 'string' || isNaN(Date.parse(b.expiresAt)) || Date.parse(b.expiresAt) <= Date.now()) {
|
|
6588
|
+
res.status(400).json({ error: 'expiresAt, if provided, must be a future ISO timestamp' });
|
|
6541
6589
|
return;
|
|
6542
6590
|
}
|
|
6591
|
+
else {
|
|
6592
|
+
expiresAt = b.expiresAt;
|
|
6593
|
+
}
|
|
6543
6594
|
const { accountId, targetMachineId } = b;
|
|
6544
6595
|
const selfMachineId = ctx.meshSelfId ?? ctx.config.machineId ?? 'local';
|
|
6545
6596
|
// Issue locally (PIN-gated above) — exact-bounds account-follow-me / re-mint (R1).
|
|
@@ -6550,7 +6601,7 @@ export function createRoutes(ctx) {
|
|
|
6550
6601
|
{ action: 'account-follow-me', bounds: { accountId, targetMachineId, mechanism: 're-mint' } },
|
|
6551
6602
|
],
|
|
6552
6603
|
author: typeof b.author === 'string' && b.author ? b.author : 'justin',
|
|
6553
|
-
expiresAt
|
|
6604
|
+
expiresAt,
|
|
6554
6605
|
});
|
|
6555
6606
|
// A LOCAL target is the ordinary path — the local gate already sees this mandate; no delivery.
|
|
6556
6607
|
if (targetMachineId === selfMachineId) {
|
|
@@ -19282,7 +19333,22 @@ export function createRoutes(ctx) {
|
|
|
19282
19333
|
},
|
|
19283
19334
|
});
|
|
19284
19335
|
const { offered } = svc.scanAndOffer();
|
|
19285
|
-
|
|
19336
|
+
// Enrich each consent offer for the one-tap Approve card (ws52-operator-tap-not-text Part A +
|
|
19337
|
+
// FD2). The card reads machineNickname / accountLabel / expiryText and the FD2 `agents` pair —
|
|
19338
|
+
// ALL resolved SERVER-SIDE so the operator never types a fingerprint or a JSON authority. The
|
|
19339
|
+
// agents pair is this Echo identity's routing fingerprint (issuer === target identity: the same
|
|
19340
|
+
// agent following its own account onto another machine); the cross-machine enroll authorization
|
|
19341
|
+
// (R4a delivered-mandate path) gates on the mandate's exact bounds + issuance signature, not on
|
|
19342
|
+
// this pair, and issue-for-machine only requires a valid two-fingerprint pair.
|
|
19343
|
+
const localFp = resolveAgentFingerprint(ctx);
|
|
19344
|
+
const enriched = offered.map((o) => ({
|
|
19345
|
+
...o,
|
|
19346
|
+
machineNickname: o.targetMachineNickname,
|
|
19347
|
+
accountLabel: o.accountEmail || o.accountId,
|
|
19348
|
+
expiryText: 'Authorizes this one setup, then expires (1 hour).',
|
|
19349
|
+
agents: [localFp, localFp],
|
|
19350
|
+
}));
|
|
19351
|
+
res.json({ enabled: true, offered: enriched });
|
|
19286
19352
|
}
|
|
19287
19353
|
catch (err) {
|
|
19288
19354
|
res.status(500).json({ error: err instanceof Error ? err.message : 'follow-me scan failed' });
|