instar 1.3.269 → 1.3.270
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/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +9 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/coordination/MandateAudit.d.ts +48 -0
- package/dist/coordination/MandateAudit.d.ts.map +1 -0
- package/dist/coordination/MandateAudit.js +95 -0
- package/dist/coordination/MandateAudit.js.map +1 -0
- package/dist/coordination/MandateGate.d.ts +48 -0
- package/dist/coordination/MandateGate.d.ts.map +1 -0
- package/dist/coordination/MandateGate.js +104 -0
- package/dist/coordination/MandateGate.js.map +1 -0
- package/dist/coordination/MandateStore.d.ts +60 -0
- package/dist/coordination/MandateStore.d.ts.map +1 -0
- package/dist/coordination/MandateStore.js +106 -0
- package/dist/coordination/MandateStore.js.map +1 -0
- package/dist/coordination/conditions.d.ts +32 -0
- package/dist/coordination/conditions.d.ts.map +1 -0
- package/dist/coordination/conditions.js +54 -0
- package/dist/coordination/conditions.js.map +1 -0
- package/dist/coordination/types.d.ts +74 -0
- package/dist/coordination/types.d.ts.map +1 -0
- package/dist/coordination/types.js +17 -0
- package/dist/coordination/types.js.map +1 -0
- package/dist/core/ApprovalLedger.d.ts +132 -0
- package/dist/core/ApprovalLedger.d.ts.map +1 -0
- package/dist/core/ApprovalLedger.js +191 -0
- package/dist/core/ApprovalLedger.js.map +1 -0
- package/dist/core/SessionBuildContextStore.d.ts +33 -0
- package/dist/core/SessionBuildContextStore.d.ts.map +1 -0
- package/dist/core/SessionBuildContextStore.js +107 -0
- package/dist/core/SessionBuildContextStore.js.map +1 -0
- package/dist/core/SessionManager.d.ts +4 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +44 -3
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/types.d.ts +11 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/feedback-factory/migration/immutableGuard.d.ts +87 -0
- package/dist/feedback-factory/migration/immutableGuard.d.ts.map +1 -0
- package/dist/feedback-factory/migration/immutableGuard.js +163 -0
- package/dist/feedback-factory/migration/immutableGuard.js.map +1 -0
- package/dist/feedback-factory/migration/importIntegrity.d.ts +112 -0
- package/dist/feedback-factory/migration/importIntegrity.d.ts.map +1 -0
- package/dist/feedback-factory/migration/importIntegrity.js +202 -0
- package/dist/feedback-factory/migration/importIntegrity.js.map +1 -0
- package/dist/feedback-factory/monitor/parityMonitor.d.ts +75 -0
- package/dist/feedback-factory/monitor/parityMonitor.d.ts.map +1 -0
- package/dist/feedback-factory/monitor/parityMonitor.js +98 -0
- package/dist/feedback-factory/monitor/parityMonitor.js.map +1 -0
- package/dist/feedback-factory/monitor/parityMonitorStore.d.ts +44 -0
- package/dist/feedback-factory/monitor/parityMonitorStore.d.ts.map +1 -0
- package/dist/feedback-factory/monitor/parityMonitorStore.js +80 -0
- package/dist/feedback-factory/monitor/parityMonitorStore.js.map +1 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +11 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +6 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +94 -1
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +29 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +11 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +255 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +48 -48
- package/src/scaffold/templates.ts +11 -0
- package/upgrades/1.3.270.md +106 -0
- package/upgrades/approval-ledger-phase2.eli16.md +56 -0
- package/upgrades/feedback-phase3-monitor.eli16.md +51 -0
- package/upgrades/feedback-phase4-integrity.eli16.md +54 -0
- package/upgrades/respawn-build-context-reestablishment.eli16.md +5 -0
- package/upgrades/side-effects/approval-ledger-phase2.md +82 -0
- package/upgrades/side-effects/coordination-mandate-enforcement.md +86 -0
- package/upgrades/side-effects/feedback-phase3-monitor.md +37 -0
- package/upgrades/side-effects/feedback-phase4-integrity.md +35 -0
- package/upgrades/side-effects/respawn-build-context-reestablishment.md +81 -0
package/dist/server/routes.js
CHANGED
|
@@ -4123,6 +4123,261 @@ export function createRoutes(ctx) {
|
|
|
4123
4123
|
samples: ctx.resourceLedger.recentSamples({ sinceMs, limit, source }),
|
|
4124
4124
|
});
|
|
4125
4125
|
});
|
|
4126
|
+
// ── Approval-as-Data (docs/specs/AUTONOMOUS-OPERATION-JUDGMENT-AND-APPROVAL-AS-DATA-SPEC.md, Part B / Phase 2) ──
|
|
4127
|
+
// Durable, signed record of every operator approval decision, and the per-class
|
|
4128
|
+
// agreement ratios computed from it. Tracks approvals WHEREVER they occur (a spec
|
|
4129
|
+
// sign-off, a decision approved in chat, …) — operator extension 2026-06-05.
|
|
4130
|
+
//
|
|
4131
|
+
// AUTHORITY INVARIANT: `mode` + `divergences` MUST reflect an EXPLICIT operator
|
|
4132
|
+
// statement (the approve control, or words like "go with your picks" = as-is vs
|
|
4133
|
+
// "change X because Y" = with-change). The agent must NOT infer/self-classify the
|
|
4134
|
+
// operator's intent; an ambiguous/silent decision is NOT recorded. Any row is
|
|
4135
|
+
// operator-correctable (append a row with `corrects` set). Read paths never gate.
|
|
4136
|
+
// Record an approval decision. The caller passes operator-sourced mode+divergences.
|
|
4137
|
+
router.post('/approvals', (req, res) => {
|
|
4138
|
+
if (!ctx.approvalLedger) {
|
|
4139
|
+
res.status(503).json({ error: 'approval ledger unavailable (no stateDir or init failed)' });
|
|
4140
|
+
return;
|
|
4141
|
+
}
|
|
4142
|
+
const b = req.body ?? {};
|
|
4143
|
+
if (typeof b.subject !== 'string' || !b.subject.trim()) {
|
|
4144
|
+
res.status(400).json({ error: 'subject is required' });
|
|
4145
|
+
return;
|
|
4146
|
+
}
|
|
4147
|
+
if (typeof b.decisionClass !== 'string' || !b.decisionClass.trim()) {
|
|
4148
|
+
res.status(400).json({ error: 'decisionClass is required' });
|
|
4149
|
+
return;
|
|
4150
|
+
}
|
|
4151
|
+
try {
|
|
4152
|
+
const row = ctx.approvalLedger.recordApproval({
|
|
4153
|
+
subject: b.subject,
|
|
4154
|
+
decisionClass: b.decisionClass,
|
|
4155
|
+
surface: b.surface ?? 'chat',
|
|
4156
|
+
approver: typeof b.approver === 'string' && b.approver ? b.approver : 'justin',
|
|
4157
|
+
mode: b.mode,
|
|
4158
|
+
divergences: Array.isArray(b.divergences) ? b.divergences : undefined,
|
|
4159
|
+
reviewIterations: typeof b.reviewIterations === 'number' ? b.reviewIterations : undefined,
|
|
4160
|
+
commitSha: typeof b.commitSha === 'string' ? b.commitSha : undefined,
|
|
4161
|
+
evidenceRef: typeof b.evidenceRef === 'string' ? b.evidenceRef : undefined,
|
|
4162
|
+
corrects: typeof b.corrects === 'string' ? b.corrects : undefined,
|
|
4163
|
+
decidedAt: typeof b.decidedAt === 'string' ? b.decidedAt : undefined,
|
|
4164
|
+
});
|
|
4165
|
+
res.status(201).json({ recorded: true, row });
|
|
4166
|
+
}
|
|
4167
|
+
catch (err) {
|
|
4168
|
+
// The store throws on an internally-inconsistent row (e.g. as-is WITH a
|
|
4169
|
+
// divergence, or a change WITHOUT one) — that's a 400, not a 500.
|
|
4170
|
+
res.status(400).json({ error: err instanceof Error ? err.message : String(err) });
|
|
4171
|
+
}
|
|
4172
|
+
});
|
|
4173
|
+
// Per-class agreement summary + an overall surface breakdown. Read-only; never gates.
|
|
4174
|
+
router.get('/approvals/summary', (_req, res) => {
|
|
4175
|
+
if (!ctx.approvalLedger) {
|
|
4176
|
+
res.status(503).json({ error: 'approval ledger unavailable (no stateDir or init failed)' });
|
|
4177
|
+
return;
|
|
4178
|
+
}
|
|
4179
|
+
const classes = ctx.approvalLedger.summarize();
|
|
4180
|
+
const rows = ctx.approvalLedger.all();
|
|
4181
|
+
const bySurface = {};
|
|
4182
|
+
for (const r of rows) {
|
|
4183
|
+
const s = (bySurface[r.surface] ??= { total: 0, approvedAsIs: 0 });
|
|
4184
|
+
s.total++;
|
|
4185
|
+
if (r.mode === 'approved-as-is')
|
|
4186
|
+
s.approvedAsIs++;
|
|
4187
|
+
}
|
|
4188
|
+
res.json({
|
|
4189
|
+
total: rows.length,
|
|
4190
|
+
classes,
|
|
4191
|
+
bySurface,
|
|
4192
|
+
note: 'Read-only. The ratio is a SIGNAL; auto-approval (Phase 3) routes through a full-context authority, not the arithmetic.',
|
|
4193
|
+
});
|
|
4194
|
+
});
|
|
4195
|
+
// List recorded rows, newest-first (paginated; ?limit= / ?decisionClass= / ?surface=).
|
|
4196
|
+
router.get('/approvals', (req, res) => {
|
|
4197
|
+
if (!ctx.approvalLedger) {
|
|
4198
|
+
res.status(503).json({ error: 'approval ledger unavailable (no stateDir or init failed)' });
|
|
4199
|
+
return;
|
|
4200
|
+
}
|
|
4201
|
+
let limit = 100;
|
|
4202
|
+
if (typeof req.query.limit === 'string' && /^\d+$/.test(req.query.limit)) {
|
|
4203
|
+
const n = Number(req.query.limit);
|
|
4204
|
+
if (n > 0 && n <= 1000)
|
|
4205
|
+
limit = n;
|
|
4206
|
+
}
|
|
4207
|
+
const decisionClass = typeof req.query.decisionClass === 'string' ? req.query.decisionClass : undefined;
|
|
4208
|
+
const surface = typeof req.query.surface === 'string' ? req.query.surface : undefined;
|
|
4209
|
+
let rows = ctx.approvalLedger.all();
|
|
4210
|
+
if (decisionClass)
|
|
4211
|
+
rows = rows.filter((r) => r.decisionClass === decisionClass);
|
|
4212
|
+
if (surface)
|
|
4213
|
+
rows = rows.filter((r) => r.surface === surface);
|
|
4214
|
+
const recent = rows.slice(-limit).reverse();
|
|
4215
|
+
res.json({ count: recent.length, total: rows.length, rows: recent });
|
|
4216
|
+
});
|
|
4217
|
+
// ── Coordination Mandate (docs/specs/coordination-mandate.md §4) ──
|
|
4218
|
+
// Deny-by-default enforcement for autonomous A2A authority. The mandate (a
|
|
4219
|
+
// human-authored, signed policy) is the AUTHORIZER — never the agent. Justin's
|
|
4220
|
+
// resolved decisions (A/A/B, 2026-06-05): issuance + revocation are PIN-gated
|
|
4221
|
+
// (the human-authenticated dashboard surface — an agent's Bearer token CANNOT
|
|
4222
|
+
// issue/revoke); the first mandate carries only exchange-read-credential +
|
|
4223
|
+
// sign-code-review; execute-cutover is not delegated (the flip stays human).
|
|
4224
|
+
//
|
|
4225
|
+
// PIN verification mirrors /dashboard/unlock: sha256 + timingSafeEqual + a
|
|
4226
|
+
// per-IP attempt limit. Read paths + /mandate/evaluate are Bearer-gated.
|
|
4227
|
+
const mandatePinAttempts = new Map();
|
|
4228
|
+
const MANDATE_PIN_MAX_ATTEMPTS = 5;
|
|
4229
|
+
const MANDATE_PIN_WINDOW_MS = 5 * 60 * 1000;
|
|
4230
|
+
/** Verify the operator PIN for mandate issuance/revocation. Returns an error
|
|
4231
|
+
* string (already sent) or null when the PIN is valid. */
|
|
4232
|
+
function checkMandatePin(req, res) {
|
|
4233
|
+
if (!ctx.config.dashboardPin) {
|
|
4234
|
+
res.status(503).json({ error: 'PIN authentication not available (no dashboardPin configured)' });
|
|
4235
|
+
return false;
|
|
4236
|
+
}
|
|
4237
|
+
const ip = req.ip || req.socket?.remoteAddress || 'unknown';
|
|
4238
|
+
const now = Date.now();
|
|
4239
|
+
let entry = mandatePinAttempts.get(ip);
|
|
4240
|
+
if (entry && now > entry.resetAt) {
|
|
4241
|
+
mandatePinAttempts.delete(ip);
|
|
4242
|
+
entry = undefined;
|
|
4243
|
+
}
|
|
4244
|
+
if (entry && entry.count >= MANDATE_PIN_MAX_ATTEMPTS) {
|
|
4245
|
+
res.status(429).json({ error: 'Too many attempts. Try again later.' });
|
|
4246
|
+
return false;
|
|
4247
|
+
}
|
|
4248
|
+
const pin = (req.body ?? {}).pin;
|
|
4249
|
+
if (!pin || typeof pin !== 'string') {
|
|
4250
|
+
res.status(403).json({ error: 'Operator PIN required — mandate issuance/revocation is a human action, not an agent action' });
|
|
4251
|
+
return false;
|
|
4252
|
+
}
|
|
4253
|
+
const ha = createHash('sha256').update(pin).digest();
|
|
4254
|
+
const hb = createHash('sha256').update(ctx.config.dashboardPin).digest();
|
|
4255
|
+
if (ha.length !== hb.length || !timingSafeEqual(ha, hb)) {
|
|
4256
|
+
if (!entry) {
|
|
4257
|
+
entry = { count: 0, resetAt: now + MANDATE_PIN_WINDOW_MS };
|
|
4258
|
+
mandatePinAttempts.set(ip, entry);
|
|
4259
|
+
}
|
|
4260
|
+
entry.count++;
|
|
4261
|
+
res.status(403).json({ error: 'Incorrect PIN', attemptsRemaining: MANDATE_PIN_MAX_ATTEMPTS - entry.count });
|
|
4262
|
+
return false;
|
|
4263
|
+
}
|
|
4264
|
+
return true;
|
|
4265
|
+
}
|
|
4266
|
+
// Evaluate an action against a mandate (the enforcement consumption point).
|
|
4267
|
+
// Every call — allow AND deny — lands in the hash-chained audit.
|
|
4268
|
+
router.post('/mandate/evaluate', (req, res) => {
|
|
4269
|
+
if (!ctx.coordination) {
|
|
4270
|
+
res.status(503).json({ error: 'coordination mandate engine unavailable (no stateDir or init failed)' });
|
|
4271
|
+
return;
|
|
4272
|
+
}
|
|
4273
|
+
const b = req.body ?? {};
|
|
4274
|
+
if (typeof b.action !== 'string' || !b.action || typeof b.agentFp !== 'string' || !b.agentFp || typeof b.mandateId !== 'string' || !b.mandateId) {
|
|
4275
|
+
res.status(400).json({ error: 'action, agentFp, and mandateId are required' });
|
|
4276
|
+
return;
|
|
4277
|
+
}
|
|
4278
|
+
const result = ctx.coordination.gate.evaluate({
|
|
4279
|
+
action: b.action,
|
|
4280
|
+
params: (b.params && typeof b.params === 'object') ? b.params : {},
|
|
4281
|
+
agentFp: b.agentFp,
|
|
4282
|
+
mandateId: b.mandateId,
|
|
4283
|
+
});
|
|
4284
|
+
res.json({ decision: result.decision, reason: result.reason, conditionResult: result.conditionResult });
|
|
4285
|
+
});
|
|
4286
|
+
// Read-only audit trail + chain integrity. MUST be registered before /mandate/:id.
|
|
4287
|
+
router.get('/mandate/audit', (req, res) => {
|
|
4288
|
+
if (!ctx.coordination) {
|
|
4289
|
+
res.status(503).json({ error: 'coordination mandate engine unavailable (no stateDir or init failed)' });
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
4292
|
+
let limit = 100;
|
|
4293
|
+
if (typeof req.query.limit === 'string' && /^\d+$/.test(req.query.limit)) {
|
|
4294
|
+
const n = Number(req.query.limit);
|
|
4295
|
+
if (n > 0 && n <= 1000)
|
|
4296
|
+
limit = n;
|
|
4297
|
+
}
|
|
4298
|
+
const all = ctx.coordination.audit.all();
|
|
4299
|
+
res.json({
|
|
4300
|
+
total: all.length,
|
|
4301
|
+
chain: ctx.coordination.audit.verifyChain(),
|
|
4302
|
+
headHash: ctx.coordination.audit.headHash(),
|
|
4303
|
+
entries: all.slice(-limit).reverse(),
|
|
4304
|
+
});
|
|
4305
|
+
});
|
|
4306
|
+
// List mandates (each with its live authorship-verification status). Read-only.
|
|
4307
|
+
router.get('/mandate', (_req, res) => {
|
|
4308
|
+
if (!ctx.coordination) {
|
|
4309
|
+
res.status(503).json({ error: 'coordination mandate engine unavailable (no stateDir or init failed)' });
|
|
4310
|
+
return;
|
|
4311
|
+
}
|
|
4312
|
+
const { store } = ctx.coordination;
|
|
4313
|
+
res.json({
|
|
4314
|
+
mandates: store.list().map((m) => ({ ...m, authorshipValid: store.verifyAuthorship(m) })),
|
|
4315
|
+
});
|
|
4316
|
+
});
|
|
4317
|
+
// One mandate + its verification status. Read-only.
|
|
4318
|
+
router.get('/mandate/:id', (req, res) => {
|
|
4319
|
+
if (!ctx.coordination) {
|
|
4320
|
+
res.status(503).json({ error: 'coordination mandate engine unavailable (no stateDir or init failed)' });
|
|
4321
|
+
return;
|
|
4322
|
+
}
|
|
4323
|
+
const m = ctx.coordination.store.get(req.params.id);
|
|
4324
|
+
if (!m) {
|
|
4325
|
+
res.status(404).json({ error: `mandate "${req.params.id}" not found` });
|
|
4326
|
+
return;
|
|
4327
|
+
}
|
|
4328
|
+
res.json({ mandate: { ...m, authorshipValid: ctx.coordination.store.verifyAuthorship(m) } });
|
|
4329
|
+
});
|
|
4330
|
+
// Issue a mandate — PIN-GATED (decision 2A: the human-authenticated surface).
|
|
4331
|
+
router.post('/mandate/issue', (req, res) => {
|
|
4332
|
+
if (!ctx.coordination) {
|
|
4333
|
+
res.status(503).json({ error: 'coordination mandate engine unavailable (no stateDir or init failed)' });
|
|
4334
|
+
return;
|
|
4335
|
+
}
|
|
4336
|
+
if (!checkMandatePin(req, res))
|
|
4337
|
+
return;
|
|
4338
|
+
const b = req.body ?? {};
|
|
4339
|
+
if (typeof b.scope !== 'string' || !b.scope.trim()) {
|
|
4340
|
+
res.status(400).json({ error: 'scope is required' });
|
|
4341
|
+
return;
|
|
4342
|
+
}
|
|
4343
|
+
if (!Array.isArray(b.agents) || b.agents.length !== 2 || b.agents.some((a) => typeof a !== 'string' || !a)) {
|
|
4344
|
+
res.status(400).json({ error: 'agents must be a pair of two agent fingerprints' });
|
|
4345
|
+
return;
|
|
4346
|
+
}
|
|
4347
|
+
if (!Array.isArray(b.authorities) || b.authorities.length === 0
|
|
4348
|
+
|| b.authorities.some((a) => typeof a?.action !== 'string' || !a.action || typeof a?.bounds !== 'object' || a.bounds === null)) {
|
|
4349
|
+
res.status(400).json({ error: 'authorities must be a non-empty array of { action, bounds, requiresCondition? }' });
|
|
4350
|
+
return;
|
|
4351
|
+
}
|
|
4352
|
+
if (typeof b.expiresAt !== 'string' || isNaN(Date.parse(b.expiresAt)) || Date.parse(b.expiresAt) <= Date.now()) {
|
|
4353
|
+
res.status(400).json({ error: 'expiresAt must be a future ISO timestamp (mandates always expire)' });
|
|
4354
|
+
return;
|
|
4355
|
+
}
|
|
4356
|
+
const mandate = ctx.coordination.store.issue({
|
|
4357
|
+
scope: b.scope,
|
|
4358
|
+
agents: [b.agents[0], b.agents[1]],
|
|
4359
|
+
authorities: b.authorities,
|
|
4360
|
+
author: typeof b.author === 'string' && b.author ? b.author : 'justin',
|
|
4361
|
+
expiresAt: b.expiresAt,
|
|
4362
|
+
});
|
|
4363
|
+
res.status(201).json({ issued: true, mandate });
|
|
4364
|
+
});
|
|
4365
|
+
// Revoke a mandate — PIN-GATED (the operator kill switch; checked on every action).
|
|
4366
|
+
router.post('/mandate/:id/revoke', (req, res) => {
|
|
4367
|
+
if (!ctx.coordination) {
|
|
4368
|
+
res.status(503).json({ error: 'coordination mandate engine unavailable (no stateDir or init failed)' });
|
|
4369
|
+
return;
|
|
4370
|
+
}
|
|
4371
|
+
if (!checkMandatePin(req, res))
|
|
4372
|
+
return;
|
|
4373
|
+
const reason = typeof (req.body ?? {}).reason === 'string' && req.body.reason ? req.body.reason : 'operator revocation';
|
|
4374
|
+
const m = ctx.coordination.store.revoke(req.params.id, reason);
|
|
4375
|
+
if (!m) {
|
|
4376
|
+
res.status(404).json({ error: `mandate "${req.params.id}" not found` });
|
|
4377
|
+
return;
|
|
4378
|
+
}
|
|
4379
|
+
res.json({ revoked: true, mandate: m });
|
|
4380
|
+
});
|
|
4126
4381
|
// ── Parallel-Work Awareness (docs/specs/parallel-activity-coherence.md, Phase A) ──
|
|
4127
4382
|
// Read-only cross-topic index: every topic with intent state + its current focus +
|
|
4128
4383
|
// high-specificity tags + whether a session is live on it. Signal-only; never gates.
|