instar 1.3.269 → 1.3.271

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 (109) hide show
  1. package/dashboard/index.html +73 -11
  2. package/dist/commands/server.d.ts.map +1 -1
  3. package/dist/commands/server.js +24 -3
  4. package/dist/commands/server.js.map +1 -1
  5. package/dist/coordination/MandateAudit.d.ts +48 -0
  6. package/dist/coordination/MandateAudit.d.ts.map +1 -0
  7. package/dist/coordination/MandateAudit.js +95 -0
  8. package/dist/coordination/MandateAudit.js.map +1 -0
  9. package/dist/coordination/MandateGate.d.ts +48 -0
  10. package/dist/coordination/MandateGate.d.ts.map +1 -0
  11. package/dist/coordination/MandateGate.js +104 -0
  12. package/dist/coordination/MandateGate.js.map +1 -0
  13. package/dist/coordination/MandateStore.d.ts +60 -0
  14. package/dist/coordination/MandateStore.d.ts.map +1 -0
  15. package/dist/coordination/MandateStore.js +106 -0
  16. package/dist/coordination/MandateStore.js.map +1 -0
  17. package/dist/coordination/ReviewExchange.d.ts +135 -0
  18. package/dist/coordination/ReviewExchange.d.ts.map +1 -0
  19. package/dist/coordination/ReviewExchange.js +192 -0
  20. package/dist/coordination/ReviewExchange.js.map +1 -0
  21. package/dist/coordination/conditions.d.ts +32 -0
  22. package/dist/coordination/conditions.d.ts.map +1 -0
  23. package/dist/coordination/conditions.js +54 -0
  24. package/dist/coordination/conditions.js.map +1 -0
  25. package/dist/coordination/types.d.ts +74 -0
  26. package/dist/coordination/types.d.ts.map +1 -0
  27. package/dist/coordination/types.js +17 -0
  28. package/dist/coordination/types.js.map +1 -0
  29. package/dist/core/ApprovalLedger.d.ts +132 -0
  30. package/dist/core/ApprovalLedger.d.ts.map +1 -0
  31. package/dist/core/ApprovalLedger.js +191 -0
  32. package/dist/core/ApprovalLedger.js.map +1 -0
  33. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  34. package/dist/core/PostUpdateMigrator.js +33 -1
  35. package/dist/core/PostUpdateMigrator.js.map +1 -1
  36. package/dist/core/SecretStore.d.ts.map +1 -1
  37. package/dist/core/SecretStore.js +10 -1
  38. package/dist/core/SecretStore.js.map +1 -1
  39. package/dist/core/SessionBuildContextStore.d.ts +33 -0
  40. package/dist/core/SessionBuildContextStore.d.ts.map +1 -0
  41. package/dist/core/SessionBuildContextStore.js +107 -0
  42. package/dist/core/SessionBuildContextStore.js.map +1 -0
  43. package/dist/core/SessionManager.d.ts +4 -0
  44. package/dist/core/SessionManager.d.ts.map +1 -1
  45. package/dist/core/SessionManager.js +44 -3
  46. package/dist/core/SessionManager.js.map +1 -1
  47. package/dist/core/TransferByNickname.d.ts +6 -0
  48. package/dist/core/TransferByNickname.d.ts.map +1 -1
  49. package/dist/core/TransferByNickname.js +18 -6
  50. package/dist/core/TransferByNickname.js.map +1 -1
  51. package/dist/core/seamlessnessConfig.d.ts.map +1 -1
  52. package/dist/core/seamlessnessConfig.js +12 -1
  53. package/dist/core/seamlessnessConfig.js.map +1 -1
  54. package/dist/core/types.d.ts +34 -0
  55. package/dist/core/types.d.ts.map +1 -1
  56. package/dist/core/types.js.map +1 -1
  57. package/dist/feedback-factory/migration/immutableGuard.d.ts +87 -0
  58. package/dist/feedback-factory/migration/immutableGuard.d.ts.map +1 -0
  59. package/dist/feedback-factory/migration/immutableGuard.js +163 -0
  60. package/dist/feedback-factory/migration/immutableGuard.js.map +1 -0
  61. package/dist/feedback-factory/migration/importIntegrity.d.ts +112 -0
  62. package/dist/feedback-factory/migration/importIntegrity.d.ts.map +1 -0
  63. package/dist/feedback-factory/migration/importIntegrity.js +202 -0
  64. package/dist/feedback-factory/migration/importIntegrity.js.map +1 -0
  65. package/dist/feedback-factory/monitor/parityMonitor.d.ts +75 -0
  66. package/dist/feedback-factory/monitor/parityMonitor.d.ts.map +1 -0
  67. package/dist/feedback-factory/monitor/parityMonitor.js +98 -0
  68. package/dist/feedback-factory/monitor/parityMonitor.js.map +1 -0
  69. package/dist/feedback-factory/monitor/parityMonitorStore.d.ts +44 -0
  70. package/dist/feedback-factory/monitor/parityMonitorStore.d.ts.map +1 -0
  71. package/dist/feedback-factory/monitor/parityMonitorStore.js +80 -0
  72. package/dist/feedback-factory/monitor/parityMonitorStore.js.map +1 -0
  73. package/dist/scaffold/templates.d.ts.map +1 -1
  74. package/dist/scaffold/templates.js +19 -1
  75. package/dist/scaffold/templates.js.map +1 -1
  76. package/dist/scheduler/MentorAutonomousGuardian.d.ts.map +1 -1
  77. package/dist/scheduler/MentorAutonomousGuardian.js +1 -0
  78. package/dist/scheduler/MentorAutonomousGuardian.js.map +1 -1
  79. package/dist/server/AgentServer.d.ts +11 -0
  80. package/dist/server/AgentServer.d.ts.map +1 -1
  81. package/dist/server/AgentServer.js +102 -1
  82. package/dist/server/AgentServer.js.map +1 -1
  83. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  84. package/dist/server/CapabilityIndex.js +45 -0
  85. package/dist/server/CapabilityIndex.js.map +1 -1
  86. package/dist/server/routes.d.ts +19 -0
  87. package/dist/server/routes.d.ts.map +1 -1
  88. package/dist/server/routes.js +482 -4
  89. package/dist/server/routes.js.map +1 -1
  90. package/package.json +1 -1
  91. package/src/data/builtin-manifest.json +65 -65
  92. package/src/scaffold/templates.ts +19 -1
  93. package/upgrades/1.3.270.md +106 -0
  94. package/upgrades/1.3.271.md +154 -0
  95. package/upgrades/approval-ledger-phase2.eli16.md +56 -0
  96. package/upgrades/autoloop-gate-compliance.eli16.md +11 -0
  97. package/upgrades/feedback-phase3-monitor.eli16.md +51 -0
  98. package/upgrades/feedback-phase4-integrity.eli16.md +54 -0
  99. package/upgrades/respawn-build-context-reestablishment.eli16.md +5 -0
  100. package/upgrades/side-effects/approval-ledger-phase2.md +82 -0
  101. package/upgrades/side-effects/autoloop-gate-compliance.md +43 -0
  102. package/upgrades/side-effects/coordination-mandate-enforcement.md +86 -0
  103. package/upgrades/side-effects/dashboard-pool-sessions.md +103 -0
  104. package/upgrades/side-effects/feedback-phase3-monitor.md +37 -0
  105. package/upgrades/side-effects/feedback-phase4-integrity.md +35 -0
  106. package/upgrades/side-effects/respawn-build-context-reestablishment.md +81 -0
  107. package/upgrades/side-effects/review-exchange-protocol.md +60 -0
  108. package/upgrades/side-effects/secret-drop-store-first.md +117 -0
  109. package/upgrades/side-effects/transfer-idempotency-exactly-once.md +105 -0
@@ -0,0 +1,48 @@
1
+ /**
2
+ * MandateAudit — append-only, hash-chained audit of every mandate gate decision.
3
+ *
4
+ * Every gate decision (ALLOW and DENY alike) is recorded. The hash chain (each
5
+ * entry embeds the hash of the previous one) makes a deletion or edit detectable
6
+ * (threat-model T8): break any link and `verifyChain()` reports the first broken
7
+ * index. The chain head is surfaced read-only for the operator. An external,
8
+ * out-of-process sink is a follow-on hardening, called out in the spec.
9
+ *
10
+ * Accountability after the fact is what makes ahead-of-time delegation safe: a
11
+ * misbehaving mandate is killable AND every action it authorized is reconstructable.
12
+ */
13
+ import type { MandateAuditEntry, MandateDecision } from './types.js';
14
+ /** Canonical bytes an entry's hash covers — field-ordered, hash EXCLUDED. */
15
+ export declare function canonicalAuditEntry(e: Omit<MandateAuditEntry, 'hash'>): string;
16
+ export interface MandateAuditDeps {
17
+ filePath: string;
18
+ now?: () => number;
19
+ }
20
+ export declare class MandateAudit {
21
+ private readonly d;
22
+ constructor(deps: MandateAuditDeps);
23
+ /** Append a decision, chaining it to the current head. Returns the new entry. */
24
+ record(input: {
25
+ mandateId: string;
26
+ agentFp: string;
27
+ action: string;
28
+ decision: MandateDecision;
29
+ reason: string;
30
+ conditionResult?: boolean | null;
31
+ }): MandateAuditEntry;
32
+ /** All entries (oldest→newest). Tolerant of a missing file / a torn trailing line. */
33
+ all(): MandateAuditEntry[];
34
+ /** The current chain head hash ('' when empty). */
35
+ headHash(): string;
36
+ /**
37
+ * Verify the hash chain end-to-end. Returns `{ ok: true }` when intact, else
38
+ * `{ ok: false, brokenAt }` with the index of the first entry whose recomputed
39
+ * hash (or prevHash linkage) does not match — the tamper signal.
40
+ */
41
+ verifyChain(): {
42
+ ok: true;
43
+ } | {
44
+ ok: false;
45
+ brokenAt: number;
46
+ };
47
+ }
48
+ //# sourceMappingURL=MandateAudit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MandateAudit.d.ts","sourceRoot":"","sources":["../../src/coordination/MandateAudit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAErE,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,GAAG,MAAM,CAK9E;AAMD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAmB;gBACzB,IAAI,EAAE,gBAAgB;IAIlC,iFAAiF;IACjF,MAAM,CAAC,KAAK,EAAE;QACZ,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,eAAe,CAAC;QAC1B,MAAM,EAAE,MAAM,CAAC;QACf,eAAe,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;KAClC,GAAG,iBAAiB;IAmBrB,sFAAsF;IACtF,GAAG,IAAI,iBAAiB,EAAE;IAW1B,mDAAmD;IACnD,QAAQ,IAAI,MAAM;IAKlB;;;;OAIG;IACH,WAAW,IAAI;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;CAY9D"}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * MandateAudit — append-only, hash-chained audit of every mandate gate decision.
3
+ *
4
+ * Every gate decision (ALLOW and DENY alike) is recorded. The hash chain (each
5
+ * entry embeds the hash of the previous one) makes a deletion or edit detectable
6
+ * (threat-model T8): break any link and `verifyChain()` reports the first broken
7
+ * index. The chain head is surfaced read-only for the operator. An external,
8
+ * out-of-process sink is a follow-on hardening, called out in the spec.
9
+ *
10
+ * Accountability after the fact is what makes ahead-of-time delegation safe: a
11
+ * misbehaving mandate is killable AND every action it authorized is reconstructable.
12
+ */
13
+ import fs from 'node:fs';
14
+ import path from 'node:path';
15
+ import { createHash } from 'node:crypto';
16
+ /** Canonical bytes an entry's hash covers — field-ordered, hash EXCLUDED. */
17
+ export function canonicalAuditEntry(e) {
18
+ return JSON.stringify([
19
+ e.ts, e.mandateId, e.agentFp, e.action, e.decision, e.reason,
20
+ e.conditionResult, e.prevHash,
21
+ ]);
22
+ }
23
+ function sha256(s) {
24
+ return createHash('sha256').update(s).digest('hex');
25
+ }
26
+ export class MandateAudit {
27
+ d;
28
+ constructor(deps) {
29
+ this.d = deps;
30
+ }
31
+ /** Append a decision, chaining it to the current head. Returns the new entry. */
32
+ record(input) {
33
+ const prevHash = this.headHash();
34
+ const ts = new Date(this.d.now ? this.d.now() : Date.now()).toISOString();
35
+ const unsigned = {
36
+ ts,
37
+ mandateId: input.mandateId,
38
+ agentFp: input.agentFp,
39
+ action: input.action,
40
+ decision: input.decision,
41
+ reason: input.reason,
42
+ conditionResult: input.conditionResult ?? null,
43
+ prevHash,
44
+ };
45
+ const entry = { ...unsigned, hash: sha256(prevHash + canonicalAuditEntry(unsigned)) };
46
+ fs.mkdirSync(path.dirname(this.d.filePath), { recursive: true });
47
+ fs.appendFileSync(this.d.filePath, JSON.stringify(entry) + '\n');
48
+ return entry;
49
+ }
50
+ /** All entries (oldest→newest). Tolerant of a missing file / a torn trailing line. */
51
+ all() {
52
+ let content;
53
+ try {
54
+ content = fs.readFileSync(this.d.filePath, 'utf8');
55
+ }
56
+ catch { /* @silent-fallback-ok — audit file may not exist yet; empty history is the natural default */
57
+ return [];
58
+ }
59
+ const out = [];
60
+ for (const line of content.split('\n')) {
61
+ if (!line.trim())
62
+ continue;
63
+ try {
64
+ out.push(JSON.parse(line));
65
+ }
66
+ catch { /* @silent-fallback-ok — torn-trailing-line tolerance; a crash mid-append must not poison reads */ }
67
+ }
68
+ return out;
69
+ }
70
+ /** The current chain head hash ('' when empty). */
71
+ headHash() {
72
+ const all = this.all();
73
+ return all.length ? all[all.length - 1].hash : '';
74
+ }
75
+ /**
76
+ * Verify the hash chain end-to-end. Returns `{ ok: true }` when intact, else
77
+ * `{ ok: false, brokenAt }` with the index of the first entry whose recomputed
78
+ * hash (or prevHash linkage) does not match — the tamper signal.
79
+ */
80
+ verifyChain() {
81
+ const all = this.all();
82
+ let prevHash = '';
83
+ for (let i = 0; i < all.length; i++) {
84
+ const { hash, ...unsigned } = all[i];
85
+ if (unsigned.prevHash !== prevHash)
86
+ return { ok: false, brokenAt: i };
87
+ const expected = sha256(prevHash + canonicalAuditEntry(unsigned));
88
+ if (expected !== hash)
89
+ return { ok: false, brokenAt: i };
90
+ prevHash = hash;
91
+ }
92
+ return { ok: true };
93
+ }
94
+ }
95
+ //# sourceMappingURL=MandateAudit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MandateAudit.js","sourceRoot":"","sources":["../../src/coordination/MandateAudit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * MandateGate — the deny-by-default enforcement point for autonomous A2A actions.
3
+ *
4
+ * `evaluate()` runs the spec's ordered checks (§4) and AUDITS every decision (allow
5
+ * AND deny). With NO valid mandate loaded the gate denies everything — so the system
6
+ * is inert (weakens nothing) until Justin authors a mandate through the PIN-gated path.
7
+ *
8
+ * Order (any miss → deny, audited):
9
+ * 1. mandate exists
10
+ * 2. authorship valid (T1/T2)
11
+ * 3. not expired (T4)
12
+ * 4. not revoked (T5)
13
+ * 5. caller is a named party (T6/T9)
14
+ * 6. an Authority matches `action` AND `params` are within `bounds`
15
+ * 7. if the Authority requires a condition, it evaluates true from REAL state (T7/T10)
16
+ * 8. → allow
17
+ *
18
+ * The agent's "judgment" never authorizes anything — the mandate (a human-authored
19
+ * policy) and the objective conditions do. This is `requester ≠ authorizer` preserved.
20
+ */
21
+ import type { MandateStore } from './MandateStore.js';
22
+ import type { ConditionsRegistry } from './conditions.js';
23
+ import type { MandateAudit } from './MandateAudit.js';
24
+ import type { MandateEvaluation, MandateAuditEntry } from './types.js';
25
+ export interface MandateGateResult {
26
+ decision: 'allow' | 'deny';
27
+ reason: string;
28
+ conditionResult: boolean | null;
29
+ audit: MandateAuditEntry;
30
+ }
31
+ /** Every key in `bounds` must be present in `params` with a deeply-equal value.
32
+ * `params` MAY carry extra keys — the bounds only constrain the keys they name. */
33
+ export declare function paramsSatisfyBounds(params: Record<string, unknown>, bounds: Record<string, unknown>): boolean;
34
+ export interface MandateGateDeps {
35
+ store: MandateStore;
36
+ conditions: ConditionsRegistry;
37
+ audit: MandateAudit;
38
+ now?: () => number;
39
+ }
40
+ export declare class MandateGate {
41
+ private readonly d;
42
+ constructor(deps: MandateGateDeps);
43
+ private nowMs;
44
+ private deny;
45
+ private allow;
46
+ evaluate(ev: MandateEvaluation): MandateGateResult;
47
+ }
48
+ //# sourceMappingURL=MandateGate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MandateGate.d.ts","sourceRoot":"","sources":["../../src/coordination/MandateGate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAa,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAElF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,iBAAiB,CAAC;CAC1B;AAUD;oFACoF;AACpF,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAM7G;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,KAAK,EAAE,YAAY,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAkB;gBACxB,IAAI,EAAE,eAAe;IAIjC,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,KAAK;IAQb,QAAQ,CAAC,EAAE,EAAE,iBAAiB,GAAG,iBAAiB;CAmDnD"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * MandateGate — the deny-by-default enforcement point for autonomous A2A actions.
3
+ *
4
+ * `evaluate()` runs the spec's ordered checks (§4) and AUDITS every decision (allow
5
+ * AND deny). With NO valid mandate loaded the gate denies everything — so the system
6
+ * is inert (weakens nothing) until Justin authors a mandate through the PIN-gated path.
7
+ *
8
+ * Order (any miss → deny, audited):
9
+ * 1. mandate exists
10
+ * 2. authorship valid (T1/T2)
11
+ * 3. not expired (T4)
12
+ * 4. not revoked (T5)
13
+ * 5. caller is a named party (T6/T9)
14
+ * 6. an Authority matches `action` AND `params` are within `bounds`
15
+ * 7. if the Authority requires a condition, it evaluates true from REAL state (T7/T10)
16
+ * 8. → allow
17
+ *
18
+ * The agent's "judgment" never authorizes anything — the mandate (a human-authored
19
+ * policy) and the objective conditions do. This is `requester ≠ authorizer` preserved.
20
+ */
21
+ /** Deterministic, key-sorted serialization for value comparison. */
22
+ function stableStringify(value) {
23
+ if (value === null || typeof value !== 'object')
24
+ return JSON.stringify(value);
25
+ if (Array.isArray(value))
26
+ return '[' + value.map(stableStringify).join(',') + ']';
27
+ const obj = value;
28
+ return '{' + Object.keys(obj).sort().map((k) => JSON.stringify(k) + ':' + stableStringify(obj[k])).join(',') + '}';
29
+ }
30
+ /** Every key in `bounds` must be present in `params` with a deeply-equal value.
31
+ * `params` MAY carry extra keys — the bounds only constrain the keys they name. */
32
+ export function paramsSatisfyBounds(params, bounds) {
33
+ for (const key of Object.keys(bounds)) {
34
+ if (!(key in params))
35
+ return false;
36
+ if (stableStringify(params[key]) !== stableStringify(bounds[key]))
37
+ return false;
38
+ }
39
+ return true;
40
+ }
41
+ export class MandateGate {
42
+ d;
43
+ constructor(deps) {
44
+ this.d = deps;
45
+ }
46
+ nowMs() {
47
+ return this.d.now ? this.d.now() : Date.now();
48
+ }
49
+ deny(ev, reason, conditionResult = null) {
50
+ const audit = this.d.audit.record({
51
+ mandateId: ev.mandateId, agentFp: ev.agentFp, action: ev.action,
52
+ decision: 'deny', reason, conditionResult,
53
+ });
54
+ return { decision: 'deny', reason, conditionResult, audit };
55
+ }
56
+ allow(ev, reason, conditionResult) {
57
+ const audit = this.d.audit.record({
58
+ mandateId: ev.mandateId, agentFp: ev.agentFp, action: ev.action,
59
+ decision: 'allow', reason, conditionResult,
60
+ });
61
+ return { decision: 'allow', reason, conditionResult, audit };
62
+ }
63
+ evaluate(ev) {
64
+ // 1. exists
65
+ const mandate = this.d.store.get(ev.mandateId);
66
+ if (!mandate)
67
+ return this.deny(ev, `mandate "${ev.mandateId}" not found`);
68
+ // 2. authorship valid
69
+ if (!this.d.store.verifyAuthorship(mandate)) {
70
+ return this.deny(ev, 'mandate authorship proof is invalid (forged or edited)');
71
+ }
72
+ // 3. not expired
73
+ if (this.nowMs() > Date.parse(mandate.expiresAt)) {
74
+ return this.deny(ev, `mandate expired at ${mandate.expiresAt}`);
75
+ }
76
+ // 4. not revoked
77
+ if (mandate.revoked) {
78
+ return this.deny(ev, `mandate revoked at ${mandate.revoked.at}: ${mandate.revoked.reason}`);
79
+ }
80
+ // 5. caller is a named party
81
+ if (!mandate.agents.includes(ev.agentFp)) {
82
+ return this.deny(ev, `agent ${ev.agentFp} is not a named party to this mandate`);
83
+ }
84
+ // 6. an authority matches action + params within bounds
85
+ const matching = mandate.authorities.find((a) => a.action === ev.action && paramsSatisfyBounds(ev.params, a.bounds));
86
+ if (!matching) {
87
+ const sameAction = mandate.authorities.some((a) => a.action === ev.action);
88
+ return this.deny(ev, sameAction
89
+ ? `params exceed the bounds of authority "${ev.action}"`
90
+ : `no authority for action "${ev.action}" in this mandate`);
91
+ }
92
+ // 7. condition (if any) evaluates true from real state
93
+ if (matching.requiresCondition) {
94
+ const ok = this.d.conditions.evaluate(matching.requiresCondition);
95
+ if (!ok) {
96
+ return this.deny(ev, `objective condition "${matching.requiresCondition}" not met`, false);
97
+ }
98
+ return this.allow(ev, `authority "${ev.action}" granted (condition "${matching.requiresCondition}" met)`, true);
99
+ }
100
+ // 8. allow (no condition)
101
+ return this.allow(ev, `authority "${ev.action}" granted`, null);
102
+ }
103
+ }
104
+ //# sourceMappingURL=MandateGate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MandateGate.js","sourceRoot":"","sources":["../../src/coordination/MandateGate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * MandateStore — persists Coordination Mandates and verifies their authorship.
3
+ *
4
+ * A mandate is valid only if its `authProof` verifies (threat-model T1/T2): the proof
5
+ * is produced ONLY by the PIN-gated issuance path (the human-authenticated surface,
6
+ * Justin's decision A on issuance), so an agent holding only its Bearer token cannot
7
+ * mint or widen a mandate. The proof covers the AUTHORED, immutable fields; `revoked`
8
+ * is a store-managed flag (a later mutation), so it is excluded from the proof and
9
+ * checked separately on every gate evaluation.
10
+ *
11
+ * The signer/verifier are INJECTED (like the other signed stores): tests use a
12
+ * deterministic stub; production uses an HMAC over the server's issuance secret.
13
+ *
14
+ * Trust boundary (stated, not hidden): integrity of the on-disk mandate + revocation
15
+ * flag against an attacker with LOCAL write access is the same trust root as today
16
+ * (threat-model T12, out of scope) — the proof stops a forged/edited AUTHORED mandate;
17
+ * local-file tamper of server-managed state is the baseline trust boundary.
18
+ */
19
+ import type { Authority, CoordinationMandate } from './types.js';
20
+ /** Canonical bytes the authProof covers — the AUTHORED fields only (proof + revoked excluded). */
21
+ export declare function canonicalMandate(m: Omit<CoordinationMandate, 'authProof' | 'revoked'>): string;
22
+ export interface MandateStoreDeps {
23
+ /** Absolute path to the mandates JSON file. */
24
+ filePath: string;
25
+ /** Sign the canonical mandate bytes (HMAC over the issuance secret in production). */
26
+ sign: (canonical: string) => string;
27
+ /** Verify a proof against the canonical mandate bytes. */
28
+ verifySig: (canonical: string, proof: string) => boolean;
29
+ now?: () => number;
30
+ /** Mandate id generator (default: random). Injected for deterministic tests. */
31
+ genId?: () => string;
32
+ }
33
+ export interface IssueMandateInput {
34
+ scope: string;
35
+ agents: [string, string];
36
+ authorities: Authority[];
37
+ author: string;
38
+ expiresAt: string;
39
+ id?: string;
40
+ createdAt?: string;
41
+ }
42
+ export declare class MandateStore {
43
+ private readonly d;
44
+ constructor(deps: MandateStoreDeps);
45
+ private nowIso;
46
+ private readAll;
47
+ private writeAll;
48
+ /**
49
+ * Issue (author) a mandate. ONLY the PIN-gated route should call this. Signs the
50
+ * authored bytes, persists, returns the mandate.
51
+ */
52
+ issue(input: IssueMandateInput): CoordinationMandate;
53
+ /** Verify a mandate's authorship proof (T1/T2). */
54
+ verifyAuthorship(m: CoordinationMandate): boolean;
55
+ get(id: string): CoordinationMandate | undefined;
56
+ list(): CoordinationMandate[];
57
+ /** Revoke a mandate (idempotent). Returns the updated mandate, or undefined if absent. */
58
+ revoke(id: string, reason: string): CoordinationMandate | undefined;
59
+ }
60
+ //# sourceMappingURL=MandateStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MandateStore.d.ts","sourceRoot":"","sources":["../../src/coordination/MandateStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAYjE,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,WAAW,GAAG,SAAS,CAAC,GAAG,MAAM,CAM9F;AAED,MAAM,WAAW,gBAAgB;IAC/B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,0DAA0D;IAC1D,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IACzD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzB,WAAW,EAAE,SAAS,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAmB;gBACzB,IAAI,EAAE,gBAAgB;IAIlC,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,QAAQ;IAKhB;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,iBAAiB,GAAG,mBAAmB;IAkBpD,mDAAmD;IACnD,gBAAgB,CAAC,CAAC,EAAE,mBAAmB,GAAG,OAAO;IAKjD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAIhD,IAAI,IAAI,mBAAmB,EAAE;IAI7B,0FAA0F;IAC1F,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;CAUpE"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * MandateStore — persists Coordination Mandates and verifies their authorship.
3
+ *
4
+ * A mandate is valid only if its `authProof` verifies (threat-model T1/T2): the proof
5
+ * is produced ONLY by the PIN-gated issuance path (the human-authenticated surface,
6
+ * Justin's decision A on issuance), so an agent holding only its Bearer token cannot
7
+ * mint or widen a mandate. The proof covers the AUTHORED, immutable fields; `revoked`
8
+ * is a store-managed flag (a later mutation), so it is excluded from the proof and
9
+ * checked separately on every gate evaluation.
10
+ *
11
+ * The signer/verifier are INJECTED (like the other signed stores): tests use a
12
+ * deterministic stub; production uses an HMAC over the server's issuance secret.
13
+ *
14
+ * Trust boundary (stated, not hidden): integrity of the on-disk mandate + revocation
15
+ * flag against an attacker with LOCAL write access is the same trust root as today
16
+ * (threat-model T12, out of scope) — the proof stops a forged/edited AUTHORED mandate;
17
+ * local-file tamper of server-managed state is the baseline trust boundary.
18
+ */
19
+ import fs from 'node:fs';
20
+ import path from 'node:path';
21
+ /** Deterministic, key-sorted serialization so the proof survives JSON round-trips
22
+ * and key reordering. */
23
+ function stableStringify(value) {
24
+ if (value === null || typeof value !== 'object')
25
+ return JSON.stringify(value);
26
+ if (Array.isArray(value))
27
+ return '[' + value.map(stableStringify).join(',') + ']';
28
+ const obj = value;
29
+ const keys = Object.keys(obj).sort();
30
+ return '{' + keys.map((k) => JSON.stringify(k) + ':' + stableStringify(obj[k])).join(',') + '}';
31
+ }
32
+ /** Canonical bytes the authProof covers — the AUTHORED fields only (proof + revoked excluded). */
33
+ export function canonicalMandate(m) {
34
+ return stableStringify([
35
+ m.id, m.scope, m.agents,
36
+ m.authorities.map((a) => [a.action, a.bounds, a.requiresCondition ?? '']),
37
+ m.author, m.createdAt, m.expiresAt,
38
+ ]);
39
+ }
40
+ export class MandateStore {
41
+ d;
42
+ constructor(deps) {
43
+ this.d = deps;
44
+ }
45
+ nowIso() {
46
+ return new Date(this.d.now ? this.d.now() : Date.now()).toISOString();
47
+ }
48
+ readAll() {
49
+ try {
50
+ const raw = JSON.parse(fs.readFileSync(this.d.filePath, 'utf8'));
51
+ return Array.isArray(raw) ? raw : [];
52
+ }
53
+ catch { /* @silent-fallback-ok — mandates file may not exist yet; an empty store is DENY-BY-DEFAULT (the safe state) */
54
+ return [];
55
+ }
56
+ }
57
+ writeAll(mandates) {
58
+ fs.mkdirSync(path.dirname(this.d.filePath), { recursive: true });
59
+ fs.writeFileSync(this.d.filePath, JSON.stringify(mandates, null, 2));
60
+ }
61
+ /**
62
+ * Issue (author) a mandate. ONLY the PIN-gated route should call this. Signs the
63
+ * authored bytes, persists, returns the mandate.
64
+ */
65
+ issue(input) {
66
+ const id = input.id ?? (this.d.genId ? this.d.genId() : `mandate-${Math.random().toString(36).slice(2, 10)}`);
67
+ const createdAt = input.createdAt ?? this.nowIso();
68
+ const authored = {
69
+ id, scope: input.scope, agents: input.agents, authorities: input.authorities,
70
+ author: input.author, createdAt, expiresAt: input.expiresAt,
71
+ };
72
+ const mandate = {
73
+ ...authored,
74
+ revoked: null,
75
+ authProof: this.d.sign(canonicalMandate(authored)),
76
+ };
77
+ const all = this.readAll().filter((m) => m.id !== id);
78
+ all.push(mandate);
79
+ this.writeAll(all);
80
+ return mandate;
81
+ }
82
+ /** Verify a mandate's authorship proof (T1/T2). */
83
+ verifyAuthorship(m) {
84
+ const { authProof, revoked, ...authored } = m;
85
+ return this.d.verifySig(canonicalMandate(authored), authProof);
86
+ }
87
+ get(id) {
88
+ return this.readAll().find((m) => m.id === id);
89
+ }
90
+ list() {
91
+ return this.readAll();
92
+ }
93
+ /** Revoke a mandate (idempotent). Returns the updated mandate, or undefined if absent. */
94
+ revoke(id, reason) {
95
+ const all = this.readAll();
96
+ const idx = all.findIndex((m) => m.id === id);
97
+ if (idx < 0)
98
+ return undefined;
99
+ if (!all[idx].revoked) {
100
+ all[idx].revoked = { at: this.nowIso(), reason };
101
+ this.writeAll(all);
102
+ }
103
+ return all[idx];
104
+ }
105
+ }
106
+ //# sourceMappingURL=MandateStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MandateStore.js","sourceRoot":"","sources":["../../src/coordination/MandateStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * ReviewExchange — the autonomous code-review protocol (spec §7 G2.3).
3
+ *
4
+ * A ReviewExchange is one mutual, mandate-gated sign-off of a review artifact
5
+ * between the two agents named in a Coordination Mandate. It is how Phase-1's
6
+ * "code-owner review" happens WITHOUT the human relaying: the owner agent delivers
7
+ * a review package over Threadline, the peer reviews and returns an authenticated
8
+ * verdict, and BOTH sign-offs are checked against the mandate's `sign-code-review`
9
+ * authority (bounds e.g. `{ artifact: 'migration-port', mutual: true }`) before
10
+ * they are accepted. Every accepted signature carries the audit hash of the gate
11
+ * decision that authorized it — the trail Justin reviews.
12
+ *
13
+ * Asymmetry, stated honestly: the peer's instance may not run this engine yet, so
14
+ * the peer's "signature" is their authenticated Threadline verdict message
15
+ * (Ed25519-authenticated sender → fingerprint), recorded verbatim by reference —
16
+ * kind 'authenticated-peer-verdict'. The local agent's signature goes through the
17
+ * local gate — kind 'mandate-gated-local'. BOTH are gate-evaluated here (named
18
+ * party, bounds, expiry, revocation), so a stranger's verdict or an out-of-bounds
19
+ * artifact is refused identically.
20
+ *
21
+ * State machine (linear, no skips):
22
+ * proposed → delivered → verdict-recorded → complete
23
+ * ↘ changes-requested (terminal: rework = a NEW exchange)
24
+ *
25
+ * The review package is content-addressed: `packageSha256` is fixed at creation
26
+ * and signatures bind to the record that carries it — a different package is a
27
+ * different exchange. Deny-by-default is inherited: with no mandate issued, every
28
+ * sign/verdict path denies.
29
+ */
30
+ import type { MandateGate } from './MandateGate.js';
31
+ export type ReviewExchangeState = 'proposed' | 'delivered' | 'verdict-recorded' | 'complete' | 'changes-requested';
32
+ export interface ReviewSignature {
33
+ /** The signing agent's routing fingerprint (a named party of the mandate). */
34
+ agentFp: string;
35
+ signedAt: string;
36
+ /** 'mandate-gated-local' = this server's own agent, allowed by the local gate;
37
+ * 'authenticated-peer-verdict' = the peer's sign-off, carried by their
38
+ * authenticated Threadline verdict message (recorded by reference). */
39
+ kind: 'mandate-gated-local' | 'authenticated-peer-verdict';
40
+ /** Hash of the audit entry for the gate decision that authorized this signature. */
41
+ auditHash: string;
42
+ /** Evidence reference — e.g. a Threadline message/thread ref for peer signatures. */
43
+ evidence: string | null;
44
+ }
45
+ export interface PeerVerdict {
46
+ verdict: 'approve' | 'request-changes';
47
+ summary: string;
48
+ /** Authenticated-source reference (Threadline message/thread id). */
49
+ evidence: string;
50
+ recordedAt: string;
51
+ }
52
+ export interface ReviewExchangeRecord {
53
+ id: string;
54
+ /** The mandate whose `sign-code-review` authority governs this exchange. */
55
+ mandateId: string;
56
+ /** The artifact under review — must match the authority's bounds (e.g. 'migration-port'). */
57
+ artifact: string;
58
+ /** Human-readable pointer to the review package (repo path or URL). */
59
+ packageRef: string;
60
+ /** sha256 of the package content — what the signatures bind to. Immutable. */
61
+ packageSha256: string;
62
+ /** [ownerFp, peerFp] — owner delivers, peer reviews. Both must be mandate parties. */
63
+ parties: [string, string];
64
+ state: ReviewExchangeState;
65
+ /** Delivery evidence (e.g. Threadline message id), set by markDelivered. */
66
+ deliveredEvidence: string | null;
67
+ peerVerdict: PeerVerdict | null;
68
+ signatures: ReviewSignature[];
69
+ createdAt: string;
70
+ updatedAt: string;
71
+ }
72
+ /** Uniform method result — gate denials and state errors come back as data, not throws. */
73
+ export type ExchangeResult = {
74
+ ok: true;
75
+ record: ReviewExchangeRecord;
76
+ } | {
77
+ ok: false;
78
+ reason: string;
79
+ };
80
+ export interface ReviewExchangeEngineDeps {
81
+ /** Absolute path to the exchanges JSON file. */
82
+ filePath: string;
83
+ /** The mandate gate — EVERY sign-off is evaluated through it. */
84
+ gate: MandateGate;
85
+ now?: () => number;
86
+ /** Exchange id generator (default: random). Injected for deterministic tests. */
87
+ genId?: () => string;
88
+ }
89
+ export interface CreateExchangeInput {
90
+ mandateId: string;
91
+ artifact: string;
92
+ packageRef: string;
93
+ packageSha256: string;
94
+ parties: [string, string];
95
+ id?: string;
96
+ }
97
+ export declare class ReviewExchangeEngine {
98
+ private readonly d;
99
+ constructor(deps: ReviewExchangeEngineDeps);
100
+ private nowIso;
101
+ private readAll;
102
+ private writeAll;
103
+ private save;
104
+ /** Evaluate `sign-code-review` for one party through the mandate gate. */
105
+ private gateSignOff;
106
+ /** Create a new exchange in `proposed`. Validation only — no gate call yet
107
+ * (creating a record delegates nothing; the sign-offs are what the mandate governs). */
108
+ create(input: CreateExchangeInput): ExchangeResult;
109
+ /** proposed → delivered. Records the delivery evidence (Threadline message ref). */
110
+ markDelivered(id: string, evidence: string): ExchangeResult;
111
+ /**
112
+ * delivered → verdict-recorded | changes-requested.
113
+ *
114
+ * Records the PEER's authenticated verdict. An 'approve' verdict IS the peer's
115
+ * sign-off, so it is MANDATE-GATED for the peer's fingerprint (named party,
116
+ * bounds, expiry, revocation — a deny refuses the verdict). A 'request-changes'
117
+ * verdict delegates nothing (it is a refusal), so it is recorded ungated — but
118
+ * only from the exchange's named peer.
119
+ */
120
+ recordPeerVerdict(id: string, input: {
121
+ verdict: 'approve' | 'request-changes';
122
+ summary: string;
123
+ evidence: string;
124
+ peerFp: string;
125
+ }): ExchangeResult;
126
+ /**
127
+ * verdict-recorded → complete. The OWNER's countersignature, mandate-gated for
128
+ * the owner's fingerprint. Completion requires the mutual pair: the peer's
129
+ * approve-signature already present + this one.
130
+ */
131
+ sign(id: string, agentFp: string): ExchangeResult;
132
+ get(id: string): ReviewExchangeRecord | undefined;
133
+ list(): ReviewExchangeRecord[];
134
+ }
135
+ //# sourceMappingURL=ReviewExchange.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReviewExchange.d.ts","sourceRoot":"","sources":["../../src/coordination/ReviewExchange.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,MAAM,mBAAmB,GAC3B,UAAU,GACV,WAAW,GACX,kBAAkB,GAClB,UAAU,GACV,mBAAmB,CAAC;AAExB,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB;;4EAEwE;IACxE,IAAI,EAAE,qBAAqB,GAAG,4BAA4B,CAAC;IAC3D,oFAAoF;IACpF,SAAS,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,SAAS,GAAG,iBAAiB,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,aAAa,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,KAAK,EAAE,mBAAmB,CAAC;IAC3B,4EAA4E;IAC5E,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,2FAA2F;AAC3F,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,oBAAoB,CAAA;CAAE,GAC1C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,MAAM,WAAW,wBAAwB;IACvC,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iFAAiF;IACjF,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAID,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAA2B;gBACjC,IAAI,EAAE,wBAAwB;IAI1C,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,IAAI;IAQZ,0EAA0E;IAC1E,OAAO,CAAC,WAAW;IASnB;6FACyF;IACzF,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,cAAc;IAuBlD,oFAAoF;IACpF,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc;IAY3D;;;;;;;;OAQG;IACH,iBAAiB,CACf,EAAE,EAAE,MAAM,EACV,KAAK,EAAE;QAAE,OAAO,EAAE,SAAS,GAAG,iBAAiB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GACnG,cAAc;IAoCjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc;IA0BjD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS;IAIjD,IAAI,IAAI,oBAAoB,EAAE;CAG/B"}