@sanctuary-framework/mcp-server 1.2.0 → 1.2.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/dist/cli.cjs +1951 -404
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1952 -405
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1645 -304
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +105 -18
- package/dist/index.d.ts +105 -18
- package/dist/index.js +1645 -305
- package/dist/index.js.map +1 -1
- package/dist/templates/coding-assistant/commitments.json +14 -0
- package/dist/templates/coding-assistant/defaults.json +34 -0
- package/dist/templates/coding-assistant/onboarding.md +24 -0
- package/dist/templates/coding-assistant/policy.md +1 -0
- package/dist/templates/coding-assistant/template.json +23 -0
- package/dist/templates/handoff-coordinator/commitments.json +14 -0
- package/dist/templates/handoff-coordinator/defaults.json +10 -0
- package/dist/templates/handoff-coordinator/onboarding.md +23 -0
- package/dist/templates/handoff-coordinator/policy.md +1 -0
- package/dist/templates/handoff-coordinator/template.json +17 -0
- package/dist/templates/ops-runner/commitments.json +14 -0
- package/dist/templates/ops-runner/defaults.json +12 -0
- package/dist/templates/ops-runner/onboarding.md +25 -0
- package/dist/templates/ops-runner/policy.md +1 -0
- package/dist/templates/ops-runner/template.json +16 -0
- package/dist/templates/planner/commitments.json +9 -0
- package/dist/templates/planner/defaults.json +10 -0
- package/dist/templates/planner/onboarding.md +22 -0
- package/dist/templates/planner/policy.md +1 -0
- package/dist/templates/planner/template.json +8 -0
- package/dist/templates/research-assistant/commitments.json +9 -0
- package/dist/templates/research-assistant/defaults.json +25 -0
- package/dist/templates/research-assistant/onboarding.md +21 -0
- package/dist/templates/research-assistant/policy.md +1 -0
- package/dist/templates/research-assistant/template.json +8 -0
- package/package.json +4 -4
package/dist/cli.cjs
CHANGED
|
@@ -398,23 +398,44 @@ var init_random = __esm({
|
|
|
398
398
|
"src/core/random.ts"() {
|
|
399
399
|
}
|
|
400
400
|
});
|
|
401
|
-
|
|
401
|
+
function bijectiveEncode(name) {
|
|
402
|
+
return name.replace(
|
|
403
|
+
SAFE_CHARS,
|
|
404
|
+
(ch) => "!" + ch.charCodeAt(0).toString(16).padStart(2, "0").toUpperCase()
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
function legacyNamespaceSanitize(name) {
|
|
408
|
+
return name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
409
|
+
}
|
|
410
|
+
function legacyKeySanitize(name) {
|
|
411
|
+
return name.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
412
|
+
}
|
|
413
|
+
var SAFE_CHARS, FilesystemStorage;
|
|
402
414
|
var init_filesystem = __esm({
|
|
403
415
|
"src/storage/filesystem.ts"() {
|
|
404
416
|
init_random();
|
|
417
|
+
SAFE_CHARS = /[^A-Za-z0-9_.\-]/g;
|
|
405
418
|
FilesystemStorage = class {
|
|
406
419
|
basePath;
|
|
407
420
|
constructor(basePath) {
|
|
408
421
|
this.basePath = basePath;
|
|
409
422
|
}
|
|
410
423
|
entryPath(namespace, key) {
|
|
411
|
-
const safeNamespace = namespace
|
|
412
|
-
const safeKey = key
|
|
424
|
+
const safeNamespace = bijectiveEncode(namespace);
|
|
425
|
+
const safeKey = bijectiveEncode(key);
|
|
413
426
|
return path.join(this.basePath, safeNamespace, `${safeKey}.enc`);
|
|
414
427
|
}
|
|
415
428
|
namespacePath(namespace) {
|
|
416
|
-
|
|
417
|
-
|
|
429
|
+
return path.join(this.basePath, bijectiveEncode(namespace));
|
|
430
|
+
}
|
|
431
|
+
// Legacy on-disk paths produced by the pre-#41 sanitizer. Returned for
|
|
432
|
+
// ENOENT-fallback in read/exists/delete; never written to.
|
|
433
|
+
legacyEntryPath(namespace, key) {
|
|
434
|
+
return path.join(
|
|
435
|
+
this.basePath,
|
|
436
|
+
legacyNamespaceSanitize(namespace),
|
|
437
|
+
`${legacyKeySanitize(key)}.enc`
|
|
438
|
+
);
|
|
418
439
|
}
|
|
419
440
|
async write(namespace, key, data) {
|
|
420
441
|
const dirPath = this.namespacePath(namespace);
|
|
@@ -423,7 +444,13 @@ var init_filesystem = __esm({
|
|
|
423
444
|
await promises.writeFile(filePath, data, { mode: 384 });
|
|
424
445
|
}
|
|
425
446
|
async read(namespace, key) {
|
|
426
|
-
const
|
|
447
|
+
const buf = await this.readAtPath(this.entryPath(namespace, key));
|
|
448
|
+
if (buf !== null) return buf;
|
|
449
|
+
const legacy = this.legacyEntryPath(namespace, key);
|
|
450
|
+
if (legacy === this.entryPath(namespace, key)) return null;
|
|
451
|
+
return this.readAtPath(legacy);
|
|
452
|
+
}
|
|
453
|
+
async readAtPath(filePath) {
|
|
427
454
|
try {
|
|
428
455
|
const buf = await promises.readFile(filePath);
|
|
429
456
|
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
@@ -435,7 +462,13 @@ var init_filesystem = __esm({
|
|
|
435
462
|
}
|
|
436
463
|
}
|
|
437
464
|
async delete(namespace, key, secureOverwrite = true) {
|
|
438
|
-
const
|
|
465
|
+
const newPath = this.entryPath(namespace, key);
|
|
466
|
+
if (await this.deleteAtPath(newPath, secureOverwrite)) return true;
|
|
467
|
+
const legacy = this.legacyEntryPath(namespace, key);
|
|
468
|
+
if (legacy === newPath) return false;
|
|
469
|
+
return this.deleteAtPath(legacy, secureOverwrite);
|
|
470
|
+
}
|
|
471
|
+
async deleteAtPath(filePath, secureOverwrite) {
|
|
439
472
|
try {
|
|
440
473
|
if (secureOverwrite) {
|
|
441
474
|
const fileStat = await promises.stat(filePath);
|
|
@@ -481,12 +514,19 @@ var init_filesystem = __esm({
|
|
|
481
514
|
}
|
|
482
515
|
}
|
|
483
516
|
async exists(namespace, key) {
|
|
484
|
-
const
|
|
517
|
+
const newPath = this.entryPath(namespace, key);
|
|
485
518
|
try {
|
|
486
|
-
await promises.stat(
|
|
519
|
+
await promises.stat(newPath);
|
|
487
520
|
return true;
|
|
488
521
|
} catch {
|
|
489
|
-
|
|
522
|
+
const legacy = this.legacyEntryPath(namespace, key);
|
|
523
|
+
if (legacy === newPath) return false;
|
|
524
|
+
try {
|
|
525
|
+
await promises.stat(legacy);
|
|
526
|
+
return true;
|
|
527
|
+
} catch {
|
|
528
|
+
return false;
|
|
529
|
+
}
|
|
490
530
|
}
|
|
491
531
|
}
|
|
492
532
|
async totalSize() {
|
|
@@ -843,6 +883,14 @@ var init_identity = __esm({
|
|
|
843
883
|
init_random();
|
|
844
884
|
}
|
|
845
885
|
});
|
|
886
|
+
|
|
887
|
+
// src/core/key-derivation.ts
|
|
888
|
+
var key_derivation_exports = {};
|
|
889
|
+
__export(key_derivation_exports, {
|
|
890
|
+
deriveMasterKey: () => deriveMasterKey,
|
|
891
|
+
deriveNamespaceKey: () => deriveNamespaceKey,
|
|
892
|
+
derivePurposeKey: () => derivePurposeKey
|
|
893
|
+
});
|
|
846
894
|
async function deriveMasterKey(passphrase, existingParams) {
|
|
847
895
|
const salt = existingParams ? fromBase64url(existingParams.salt) : generateSalt();
|
|
848
896
|
const params = existingParams ?? {
|
|
@@ -1503,6 +1551,11 @@ var init_router = __esm({
|
|
|
1503
1551
|
});
|
|
1504
1552
|
|
|
1505
1553
|
// src/l1-cognitive/tools.ts
|
|
1554
|
+
var tools_exports = {};
|
|
1555
|
+
__export(tools_exports, {
|
|
1556
|
+
IdentityManager: () => IdentityManager,
|
|
1557
|
+
createL1Tools: () => createL1Tools
|
|
1558
|
+
});
|
|
1506
1559
|
function getReservedNamespaceViolation(namespace) {
|
|
1507
1560
|
for (const prefix of RESERVED_NAMESPACE_PREFIXES2) {
|
|
1508
1561
|
if (namespace === prefix || namespace.startsWith(prefix + "/")) {
|
|
@@ -4881,6 +4934,35 @@ var init_types = __esm({
|
|
|
4881
4934
|
}
|
|
4882
4935
|
});
|
|
4883
4936
|
|
|
4937
|
+
// src/mesh/constants.ts
|
|
4938
|
+
function isReservedEventType(s) {
|
|
4939
|
+
return RESERVED_EVENT_TYPE_PREFIXES.some((p) => s.startsWith(p));
|
|
4940
|
+
}
|
|
4941
|
+
function isReservedExtensionKey(k) {
|
|
4942
|
+
return RESERVED_EXTENSION_ENVELOPE_KEYS.includes(k);
|
|
4943
|
+
}
|
|
4944
|
+
var PROTOCOL_VERSION, SIGNATURE_SCHEME_V1, RESERVED_EVENT_TYPE_PREFIXES, RESERVED_EXTENSION_ENVELOPE_KEYS;
|
|
4945
|
+
var init_constants = __esm({
|
|
4946
|
+
"src/mesh/constants.ts"() {
|
|
4947
|
+
PROTOCOL_VERSION = "0.1";
|
|
4948
|
+
SIGNATURE_SCHEME_V1 = "ed25519-v1";
|
|
4949
|
+
RESERVED_EVENT_TYPE_PREFIXES = [
|
|
4950
|
+
"EXTENSION_",
|
|
4951
|
+
"cross_fortress_",
|
|
4952
|
+
"multi_master_"
|
|
4953
|
+
];
|
|
4954
|
+
RESERVED_EXTENSION_ENVELOPE_KEYS = [
|
|
4955
|
+
"cross_fortress_read_grant",
|
|
4956
|
+
"cross_fortress_read_query",
|
|
4957
|
+
"cross_fortress_read_response",
|
|
4958
|
+
"multi_master_policy_merge",
|
|
4959
|
+
"audit_replication_full_n_way",
|
|
4960
|
+
"auto_promote_canonical_audit",
|
|
4961
|
+
"agent_live_migration"
|
|
4962
|
+
];
|
|
4963
|
+
}
|
|
4964
|
+
});
|
|
4965
|
+
|
|
4884
4966
|
// src/shr/generator.ts
|
|
4885
4967
|
function deriveL4Degradations(evidence, now = /* @__PURE__ */ new Date()) {
|
|
4886
4968
|
const out = [];
|
|
@@ -5029,6 +5111,7 @@ function generateSHR(identityId, opts) {
|
|
|
5029
5111
|
return {
|
|
5030
5112
|
body,
|
|
5031
5113
|
signed_by: identity.public_key,
|
|
5114
|
+
signature_scheme: SIGNATURE_SCHEME_V1,
|
|
5032
5115
|
signature: toBase64url(signatureBytes)
|
|
5033
5116
|
};
|
|
5034
5117
|
}
|
|
@@ -5039,6 +5122,7 @@ var init_generator = __esm({
|
|
|
5039
5122
|
init_identity();
|
|
5040
5123
|
init_encoding();
|
|
5041
5124
|
init_key_derivation();
|
|
5125
|
+
init_constants();
|
|
5042
5126
|
DEFAULT_VALIDITY_MS = 60 * 60 * 1e3;
|
|
5043
5127
|
DEFAULT_FRESHNESS_WINDOW_DAYS = 30;
|
|
5044
5128
|
DEFAULT_LOW_TIER_DOMINANCE_THRESHOLD = 0.6;
|
|
@@ -7277,14 +7361,14 @@ function generateDashboardHTML(options) {
|
|
|
7277
7361
|
// cookie (set by /auth/session and sent automatically by the
|
|
7278
7362
|
// browser) or as a ?session= query parameter, both of which Stack
|
|
7279
7363
|
// A's checkAuth honours. Loopback callers also bypass auth via the
|
|
7280
|
-
// v0.10.2 _autoAuthLocalhost path, which is the path
|
|
7364
|
+
// v0.10.2 _autoAuthLocalhost path, which is the path Mini1
|
|
7281
7365
|
// hits when the dashboard is auto-opened on 127.0.0.1.
|
|
7282
7366
|
//
|
|
7283
7367
|
// The endpoint itself is /events \u2014 Stack A's route table mounts it
|
|
7284
7368
|
// there, and the previous /api/events URL was a 404 in every real
|
|
7285
7369
|
// boot from v0.10.0 through v0.10.4. The retry loop that result
|
|
7286
7370
|
// produced is exactly the "status bar flashing blue continuously"
|
|
7287
|
-
//
|
|
7371
|
+
// Mini1 reported on v0.10.4.
|
|
7288
7372
|
const eventSource = new EventSource(API_BASE + '/events');
|
|
7289
7373
|
|
|
7290
7374
|
eventSource.addEventListener('init', (e) => {
|
|
@@ -10309,8 +10393,8 @@ var init_html = __esm({
|
|
|
10309
10393
|
function isPolicySlot(value) {
|
|
10310
10394
|
return typeof value === "string" && POLICY_SLOTS.includes(value);
|
|
10311
10395
|
}
|
|
10312
|
-
var COMPILED_POLICY_SCHEMA_VERSION, POLICY_UPDATE_EVENT_TYPE, POLICY_SLOTS, CHANNEL_TEMPLATE_IDS,
|
|
10313
|
-
var
|
|
10396
|
+
var COMPILED_POLICY_SCHEMA_VERSION, POLICY_UPDATE_EVENT_TYPE, POLICY_SLOTS, CHANNEL_TEMPLATE_IDS, BUDGET_UNITS;
|
|
10397
|
+
var init_constants2 = __esm({
|
|
10314
10398
|
"src/policy-engine/constants.ts"() {
|
|
10315
10399
|
COMPILED_POLICY_SCHEMA_VERSION = "0.1";
|
|
10316
10400
|
POLICY_UPDATE_EVENT_TYPE = "policy_update";
|
|
@@ -10325,10 +10409,8 @@ var init_constants = __esm({
|
|
|
10325
10409
|
"read-then-report",
|
|
10326
10410
|
"scheduled-digest",
|
|
10327
10411
|
"plan-draft-only",
|
|
10328
|
-
"fortress-relay"
|
|
10329
|
-
"concierge-loop"
|
|
10412
|
+
"fortress-relay"
|
|
10330
10413
|
];
|
|
10331
|
-
COUNTERPARTY_WILDCARD = "*";
|
|
10332
10414
|
BUDGET_UNITS = ["tokens", "usd"];
|
|
10333
10415
|
}
|
|
10334
10416
|
});
|
|
@@ -10484,7 +10566,11 @@ function resolveTemplatesDir() {
|
|
|
10484
10566
|
const thisFile = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href)));
|
|
10485
10567
|
const thisDir = path.dirname(thisFile);
|
|
10486
10568
|
if (thisDir.includes("/dist/")) {
|
|
10487
|
-
|
|
10569
|
+
const srcFallback = thisDir.replace("/dist/templates", "/src/templates");
|
|
10570
|
+
if (fs.existsSync(path.join(thisDir, TEMPLATE_NAMES[0]))) {
|
|
10571
|
+
return thisDir;
|
|
10572
|
+
}
|
|
10573
|
+
return srcFallback;
|
|
10488
10574
|
}
|
|
10489
10575
|
return thisDir;
|
|
10490
10576
|
}
|
|
@@ -10551,7 +10637,7 @@ function getTemplateEntry(name) {
|
|
|
10551
10637
|
var TEMPLATE_NAMES, TemplateValidationError, _cache;
|
|
10552
10638
|
var init_registry = __esm({
|
|
10553
10639
|
"src/templates/registry.ts"() {
|
|
10554
|
-
|
|
10640
|
+
init_constants2();
|
|
10555
10641
|
TEMPLATE_NAMES = [
|
|
10556
10642
|
"research-assistant",
|
|
10557
10643
|
"coding-assistant",
|
|
@@ -10705,10 +10791,10 @@ function applyChannelTemplate(id, params) {
|
|
|
10705
10791
|
if (!entry) throw new Error(`unknown channel template: ${id}`);
|
|
10706
10792
|
return entry.factory(params);
|
|
10707
10793
|
}
|
|
10708
|
-
var requestApproveAct, readThenReport, scheduledDigest, planDraftOnly, fortressRelay,
|
|
10794
|
+
var requestApproveAct, readThenReport, scheduledDigest, planDraftOnly, fortressRelay, REGISTRY;
|
|
10709
10795
|
var init_channel_templates = __esm({
|
|
10710
10796
|
"src/policy-engine/channel-templates.ts"() {
|
|
10711
|
-
|
|
10797
|
+
init_constants2();
|
|
10712
10798
|
init_null_policy();
|
|
10713
10799
|
requestApproveAct = (params) => {
|
|
10714
10800
|
const p = basePolicy(params);
|
|
@@ -10791,23 +10877,6 @@ var init_channel_templates = __esm({
|
|
|
10791
10877
|
setRetentionDays(p, 90);
|
|
10792
10878
|
return p;
|
|
10793
10879
|
};
|
|
10794
|
-
conciergeLoop = (params) => {
|
|
10795
|
-
const p = basePolicy(params);
|
|
10796
|
-
p.source_english = "Bidirectional Q&A with the operator. Reads local fortress state; never writes outward.";
|
|
10797
|
-
grantOn(p, "memory", {
|
|
10798
|
-
counterparty: params.counterparty,
|
|
10799
|
-
action: "read",
|
|
10800
|
-
scope: { local_fortress_state_only: true, ...params.scope ?? {} }
|
|
10801
|
-
});
|
|
10802
|
-
grantOn(p, "outputs", {
|
|
10803
|
-
counterparty: params.counterparty || COUNTERPARTY_WILDCARD,
|
|
10804
|
-
action: "read",
|
|
10805
|
-
scope: { operator_chat_only: true, ...params.scope ?? {} }
|
|
10806
|
-
});
|
|
10807
|
-
p.egress = { allowlist: [] };
|
|
10808
|
-
setRetentionDays(p, 14);
|
|
10809
|
-
return p;
|
|
10810
|
-
};
|
|
10811
10880
|
REGISTRY = {
|
|
10812
10881
|
"request-approve-act": {
|
|
10813
10882
|
id: "request-approve-act",
|
|
@@ -10843,13 +10912,6 @@ var init_channel_templates = __esm({
|
|
|
10843
10912
|
label: "Fortress relay",
|
|
10844
10913
|
description: "Routes signed events between peer fortresses. Commits bind only when both sides sign.",
|
|
10845
10914
|
factory: fortressRelay
|
|
10846
|
-
},
|
|
10847
|
-
"concierge-loop": {
|
|
10848
|
-
id: "concierge-loop",
|
|
10849
|
-
severity: "LOW",
|
|
10850
|
-
label: "Concierge loop",
|
|
10851
|
-
description: "Bidirectional Q&A with the operator. Reads local fortress state; never writes outward.",
|
|
10852
|
-
factory: conciergeLoop
|
|
10853
10915
|
}
|
|
10854
10916
|
};
|
|
10855
10917
|
}
|
|
@@ -10919,8 +10981,14 @@ function encode(value) {
|
|
|
10919
10981
|
}
|
|
10920
10982
|
function encodeArray(arr) {
|
|
10921
10983
|
const parts = [];
|
|
10922
|
-
for (
|
|
10923
|
-
|
|
10984
|
+
for (let i = 0; i < arr.length; i++) {
|
|
10985
|
+
const item = arr[i];
|
|
10986
|
+
if (item === void 0) {
|
|
10987
|
+
throw new MeshCanonicalJsonError(
|
|
10988
|
+
`canonicalize(): undefined is not a valid JSON value at array index ${i}`
|
|
10989
|
+
);
|
|
10990
|
+
}
|
|
10991
|
+
parts.push(encode(item));
|
|
10924
10992
|
}
|
|
10925
10993
|
return "[" + parts.join(",") + "]";
|
|
10926
10994
|
}
|
|
@@ -11238,45 +11306,17 @@ var init_canonical_policy = __esm({
|
|
|
11238
11306
|
"src/policy-engine/canonical-policy.ts"() {
|
|
11239
11307
|
init_canonical_json();
|
|
11240
11308
|
init_encoding();
|
|
11241
|
-
|
|
11309
|
+
init_constants2();
|
|
11242
11310
|
init_errors2();
|
|
11243
11311
|
}
|
|
11244
11312
|
});
|
|
11245
|
-
|
|
11246
|
-
// src/mesh/constants.ts
|
|
11247
|
-
function isReservedEventType(s) {
|
|
11248
|
-
return RESERVED_EVENT_TYPE_PREFIXES.some((p) => s.startsWith(p));
|
|
11249
|
-
}
|
|
11250
|
-
function isReservedExtensionKey(k) {
|
|
11251
|
-
return RESERVED_EXTENSION_ENVELOPE_KEYS.includes(k);
|
|
11252
|
-
}
|
|
11253
|
-
var PROTOCOL_VERSION, RESERVED_EVENT_TYPE_PREFIXES, RESERVED_EXTENSION_ENVELOPE_KEYS;
|
|
11254
|
-
var init_constants2 = __esm({
|
|
11255
|
-
"src/mesh/constants.ts"() {
|
|
11256
|
-
PROTOCOL_VERSION = "0.1";
|
|
11257
|
-
RESERVED_EVENT_TYPE_PREFIXES = [
|
|
11258
|
-
"EXTENSION_",
|
|
11259
|
-
"cross_fortress_",
|
|
11260
|
-
"multi_master_"
|
|
11261
|
-
];
|
|
11262
|
-
RESERVED_EXTENSION_ENVELOPE_KEYS = [
|
|
11263
|
-
"cross_fortress_read_grant",
|
|
11264
|
-
"cross_fortress_read_query",
|
|
11265
|
-
"cross_fortress_read_response",
|
|
11266
|
-
"multi_master_policy_merge",
|
|
11267
|
-
"audit_replication_full_n_way",
|
|
11268
|
-
"auto_promote_canonical_audit",
|
|
11269
|
-
"agent_live_migration"
|
|
11270
|
-
];
|
|
11271
|
-
}
|
|
11272
|
-
});
|
|
11273
11313
|
var init_trust_root = __esm({
|
|
11274
11314
|
"src/mesh/trust-root.ts"() {
|
|
11275
11315
|
init_encoding();
|
|
11276
11316
|
init_identity();
|
|
11277
11317
|
init_random();
|
|
11278
11318
|
init_canonical_json();
|
|
11279
|
-
|
|
11319
|
+
init_constants();
|
|
11280
11320
|
init_errors();
|
|
11281
11321
|
}
|
|
11282
11322
|
});
|
|
@@ -11329,7 +11369,7 @@ var init_envelope = __esm({
|
|
|
11329
11369
|
init_encoding();
|
|
11330
11370
|
init_random();
|
|
11331
11371
|
init_canonical_json();
|
|
11332
|
-
|
|
11372
|
+
init_constants();
|
|
11333
11373
|
init_errors();
|
|
11334
11374
|
init_trust_root();
|
|
11335
11375
|
}
|
|
@@ -11359,7 +11399,7 @@ function packPolicyUpdate(params) {
|
|
|
11359
11399
|
}
|
|
11360
11400
|
var init_envelope2 = __esm({
|
|
11361
11401
|
"src/policy-engine/envelope.ts"() {
|
|
11362
|
-
|
|
11402
|
+
init_constants2();
|
|
11363
11403
|
init_canonical_policy();
|
|
11364
11404
|
init_errors2();
|
|
11365
11405
|
init_envelope();
|
|
@@ -11493,7 +11533,7 @@ function initTemplate(params) {
|
|
|
11493
11533
|
var init_init = __esm({
|
|
11494
11534
|
"src/templates/init.ts"() {
|
|
11495
11535
|
init_channel_templates();
|
|
11496
|
-
|
|
11536
|
+
init_constants2();
|
|
11497
11537
|
init_canonical_policy();
|
|
11498
11538
|
init_envelope2();
|
|
11499
11539
|
init_registry();
|
|
@@ -11771,7 +11811,7 @@ function deriveMachineKey(home) {
|
|
|
11771
11811
|
return hkdf.hkdf(sha256.sha256, material, void 0, "sanctuary-passphrase-v1", 32);
|
|
11772
11812
|
}
|
|
11773
11813
|
async function defaultExec(cmd, args, input) {
|
|
11774
|
-
return new Promise((
|
|
11814
|
+
return new Promise((resolve6, reject) => {
|
|
11775
11815
|
const child = child_process.spawn(cmd, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
11776
11816
|
let stdout = "";
|
|
11777
11817
|
let stderr = "";
|
|
@@ -11782,7 +11822,7 @@ async function defaultExec(cmd, args, input) {
|
|
|
11782
11822
|
stderr += d.toString();
|
|
11783
11823
|
});
|
|
11784
11824
|
child.on("error", reject);
|
|
11785
|
-
child.on("close", (code) =>
|
|
11825
|
+
child.on("close", (code) => resolve6({ stdout, stderr, code }));
|
|
11786
11826
|
if (input !== void 0) {
|
|
11787
11827
|
child.stdin.write(input);
|
|
11788
11828
|
}
|
|
@@ -12904,7 +12944,7 @@ async function api(path, opts) {
|
|
|
12904
12944
|
// /policies, /activity responses on subsequent GETs even when the
|
|
12905
12945
|
// server-side state has changed (e.g. recent-failures buffer cleared
|
|
12906
12946
|
// on substrate flip). The pre-rc.5 client used bare fetch with no
|
|
12907
|
-
// cache control, which on
|
|
12947
|
+
// cache control, which on Mini1 Safari produced a stale view of
|
|
12908
12948
|
// server state and made the operator-visible badge color stick to
|
|
12909
12949
|
// its prior value across substrate changes. Belt + suspenders:
|
|
12910
12950
|
// cache: "no-store" turns off the response cache; the _t query
|
|
@@ -13066,12 +13106,6 @@ const CHANNEL_TEMPLATES = [
|
|
|
13066
13106
|
severity: "MEDIUM",
|
|
13067
13107
|
title: "Fortress relay",
|
|
13068
13108
|
description: "Routes signed events between peer fortresses. Commits bind only when both sides sign."
|
|
13069
|
-
},
|
|
13070
|
-
{
|
|
13071
|
-
id: "concierge-loop",
|
|
13072
|
-
severity: "LOW",
|
|
13073
|
-
title: "Concierge loop",
|
|
13074
|
-
description: "Bidirectional Q&A with the operator. Reads local fortress state; never writes outward."
|
|
13075
13109
|
}
|
|
13076
13110
|
];
|
|
13077
13111
|
|
|
@@ -13118,6 +13152,24 @@ function setRoute(route) {
|
|
|
13118
13152
|
renderFortress();
|
|
13119
13153
|
}
|
|
13120
13154
|
|
|
13155
|
+
// Renders the global attestation badge (Q1 layer 1, persistent across
|
|
13156
|
+
// surfaces). Tone is driven by state.topbarPills.attestation. Pending
|
|
13157
|
+
// state shows a dashed seal ring; verified shows solid; degraded shows
|
|
13158
|
+
// outlined core; unverified shows the broken-seal mark. Observation
|
|
13159
|
+
// language only; Castle Layer 1 enforcement ships in WP-V1.x-CASTLE-WALL.
|
|
13160
|
+
function renderTopbarAttestationBadge(stateName) {
|
|
13161
|
+
const valid = stateName === "verified" || stateName === "degraded" || stateName === "unverified" || stateName === "pending";
|
|
13162
|
+
const cls = valid ? stateName : "pending";
|
|
13163
|
+
const ringDashed = cls === "pending" ? " dashed" : "";
|
|
13164
|
+
return '<span class="att-global ' + cls + '" data-pill="attestation" title="Fortress attestation">' +
|
|
13165
|
+
'<span class="seal">' +
|
|
13166
|
+
'<span class="seal-ring' + ringDashed + '"></span>' +
|
|
13167
|
+
'<span class="seal-core"></span>' +
|
|
13168
|
+
'</span>' +
|
|
13169
|
+
'<span class="label">' + escHtml(cls) + '</span>' +
|
|
13170
|
+
'</span>';
|
|
13171
|
+
}
|
|
13172
|
+
|
|
13121
13173
|
function renderTopbar() {
|
|
13122
13174
|
const pillEl = document.getElementById("topbar-pills");
|
|
13123
13175
|
if (!pillEl) return;
|
|
@@ -13134,7 +13186,7 @@ function renderTopbar() {
|
|
|
13134
13186
|
versionPill,
|
|
13135
13187
|
'<span class="pill" data-pill="deployment">deployment: ' + escHtml(state.topbarPills.deployment) + '</span>',
|
|
13136
13188
|
'<span class="pill" data-pill="mode">mode: ' + escHtml(state.topbarPills.mode) + '</span>',
|
|
13137
|
-
|
|
13189
|
+
renderTopbarAttestationBadge(state.topbarPills.attestation)
|
|
13138
13190
|
].join("");
|
|
13139
13191
|
// Lockdown button three-state UX (binding addendum 3).
|
|
13140
13192
|
const btn = document.getElementById("btn-lockdown");
|
|
@@ -13230,6 +13282,7 @@ function renderMain() {
|
|
|
13230
13282
|
case "agent-detail": nextHtml = renderAgentDetail(); break;
|
|
13231
13283
|
case "policy": nextHtml = renderPolicyCenter(); break;
|
|
13232
13284
|
case "intelligence": nextHtml = renderIntelligenceCenter(); break;
|
|
13285
|
+
case "attestation": nextHtml = renderAttestation(); break;
|
|
13233
13286
|
case "privacy": nextHtml = renderPrivacyPage(); break;
|
|
13234
13287
|
case "coordination": nextHtml = renderCoordinationPage(); break;
|
|
13235
13288
|
case "health": nextHtml = renderHealthPage(); break;
|
|
@@ -13308,9 +13361,9 @@ function renderMain() {
|
|
|
13308
13361
|
// "Concierge unavailable; substrate not configured") sourced from the
|
|
13309
13362
|
// last response's served_by + display_label.
|
|
13310
13363
|
const CONCIERGE_SUGGESTIONS = [
|
|
13311
|
-
{ id: "summarize-hour", label: "summarize the last hour", query: "Summarize what happened in this fortress in the last hour." },
|
|
13312
|
-
{ id: "agent-touched", label: "what has each agent touched today", query: "What has each wrapped agent done today? Group by agent." },
|
|
13313
|
-
{ id: "open-approvals", label: "any open approvals?", query: "Are there any open Tier 1 approvals or pending inbox items I should look at?" }
|
|
13364
|
+
{ id: "summarize-hour", category: "Summarize", label: "summarize the last hour", query: "Summarize what happened in this fortress in the last hour." },
|
|
13365
|
+
{ id: "agent-touched", category: "Inspect", label: "what has each agent touched today", query: "What has each wrapped agent done today? Group by agent." },
|
|
13366
|
+
{ id: "open-approvals", category: "Approvals", label: "any open approvals?", query: "Are there any open Tier 1 approvals or pending inbox items I should look at?" }
|
|
13314
13367
|
];
|
|
13315
13368
|
|
|
13316
13369
|
// Direct-agent chat surface was removed in the v1.2 reshape; the
|
|
@@ -13327,59 +13380,316 @@ function renderDashboardConcierge() {
|
|
|
13327
13380
|
const badge = c.badge && c.badge.displayLabel
|
|
13328
13381
|
? '<span class="pill mono concierge-badge" title="Substrate that served the most recent response">' + escHtml(c.badge.displayLabel) + '</span>'
|
|
13329
13382
|
: '<span class="pill muted concierge-badge">Concierge: substrate not yet contacted</span>';
|
|
13330
|
-
const
|
|
13383
|
+
const sendDisabled = c.sending ? ' disabled' : '';
|
|
13384
|
+
const sendLabel = c.sending ? 'Sending...' : 'Send';
|
|
13385
|
+
// Sprint Piece 2 PR 2: empty state lives INSIDE the concierge-history
|
|
13386
|
+
// container so the DDD e2e selector .concierge-history matches both
|
|
13387
|
+
// empty and active state. The container's flex layout hosts a single
|
|
13388
|
+
// .concierge-empty child that fills the available height with a serif
|
|
13389
|
+
// headline and a 3-up suggest grid; the grid replaces the v1.2 bottom
|
|
13390
|
+
// chip row, which is retired with this polish.
|
|
13391
|
+
const emptyState =
|
|
13392
|
+
'<div class="concierge-empty">' +
|
|
13393
|
+
'<div class="concierge-empty-headline">' +
|
|
13394
|
+
'<h2>Where would you like to begin.</h2>' +
|
|
13395
|
+
'<p>Ask anything about your fortress. Sanctuary holds your context, your agents, your policy. It will answer plainly, or hand you to the right surface.</p>' +
|
|
13396
|
+
'</div>' +
|
|
13397
|
+
'<div class="concierge-suggest-grid">' +
|
|
13398
|
+
CONCIERGE_SUGGESTIONS.map(function (s) {
|
|
13399
|
+
return '<button class="concierge-suggest" data-action="concierge-suggestion" data-suggestion-id="' + escHtml(s.id) + '"' + sendDisabled + '>' +
|
|
13400
|
+
'<span class="label">' + escHtml(s.category || '') + '</span>' +
|
|
13401
|
+
escHtml(s.label) +
|
|
13402
|
+
'</button>';
|
|
13403
|
+
}).join("") +
|
|
13404
|
+
'</div>' +
|
|
13405
|
+
'</div>';
|
|
13406
|
+
const messagesHtml = c.messages.length
|
|
13331
13407
|
? c.messages.map(function (m) {
|
|
13332
13408
|
const cls = m.role === "operator" ? "concierge-msg-operator" : "concierge-msg-concierge";
|
|
13333
|
-
const
|
|
13409
|
+
const authorLabel = m.role === "operator" ? "you" : "sanctuary";
|
|
13410
|
+
const metaParts = [];
|
|
13411
|
+
if (m.created_at) metaParts.push(escHtml(shortTime(m.created_at)));
|
|
13412
|
+
if (m.role === "concierge" && m.served_by) metaParts.push('substrate: ' + escHtml(m.served_by));
|
|
13413
|
+
const meta = metaParts.length
|
|
13414
|
+
? '<div class="concierge-msg-meta"><span>' + metaParts.join(' · ') + '</span></div>'
|
|
13415
|
+
: '';
|
|
13334
13416
|
return '<div class="concierge-msg ' + cls + '">' +
|
|
13335
|
-
'<
|
|
13417
|
+
'<span class="concierge-msg-author">' + escHtml(authorLabel) + '</span>' +
|
|
13336
13418
|
'<div class="concierge-msg-body">' + escHtml(m.body) + '</div>' +
|
|
13419
|
+
meta +
|
|
13337
13420
|
'</div>';
|
|
13338
13421
|
}).join("\n")
|
|
13339
|
-
:
|
|
13422
|
+
: emptyState;
|
|
13340
13423
|
const errorBanner = c.error
|
|
13341
13424
|
? '<div class="banner banner-warn">' + escHtml(c.error) + '</div>'
|
|
13342
13425
|
: "";
|
|
13343
|
-
const sendDisabled = c.sending ? ' disabled' : '';
|
|
13344
|
-
const sendLabel = c.sending ? 'Sending...' : 'Send';
|
|
13345
|
-
const chips = CONCIERGE_SUGGESTIONS.map(function (s) {
|
|
13346
|
-
return '<button class="btn chip" data-action="concierge-suggestion" data-suggestion-id="' + escHtml(s.id) + '"' + sendDisabled + '>' + escHtml(s.label) + '</button>';
|
|
13347
|
-
}).join("\n");
|
|
13348
13426
|
const activeChatsPanel = renderActiveChatsPanel();
|
|
13349
13427
|
return [
|
|
13350
|
-
'<
|
|
13351
|
-
|
|
13352
|
-
|
|
13353
|
-
|
|
13354
|
-
'<
|
|
13355
|
-
|
|
13428
|
+
'<div class="concierge-wrap">',
|
|
13429
|
+
'<div class="page-head"><div>',
|
|
13430
|
+
'<p class="eyebrow">Concierge</p>',
|
|
13431
|
+
'<h1>Talk to your fortress.</h1>',
|
|
13432
|
+
'<p class="sub">A direct line to Sanctuary, routed through the substrate you chose. Nothing leaves without your hand on it.</p>',
|
|
13433
|
+
'</div></div>',
|
|
13434
|
+
activeChatsPanel,
|
|
13435
|
+
'<div class="card concierge-card">',
|
|
13436
|
+
'<div class="concierge-header">',
|
|
13437
|
+
'<div class="concierge-persona">',
|
|
13438
|
+
'<div class="glyph-ring"></div>',
|
|
13439
|
+
'<div class="concierge-persona-text"><strong>Sanctuary Fortress concierge</strong><small>read-only over fortress state</small></div>',
|
|
13440
|
+
'</div>',
|
|
13441
|
+
'<div class="concierge-meta">' + badge + '</div>',
|
|
13442
|
+
'</div>',
|
|
13443
|
+
errorBanner,
|
|
13444
|
+
'<div class="concierge-history" id="concierge-history">' + messagesHtml + '</div>',
|
|
13445
|
+
'<form class="concierge-composer" data-action="concierge-submit">',
|
|
13446
|
+
'<div class="input-wrap">',
|
|
13447
|
+
'<input type="text" name="concierge-input" placeholder="Type to Sanctuary. Enter to send." value="' + escHtml(c.composer) + '" data-action="concierge-input"' + sendDisabled + ' autocomplete="off">',
|
|
13448
|
+
'<span class="composer-meta">Enter</span>',
|
|
13449
|
+
'</div>',
|
|
13450
|
+
'<button type="submit" class="btn btn-primary" data-action="concierge-send"' + sendDisabled + '>' + escHtml(sendLabel) + '</button>',
|
|
13451
|
+
'</form>',
|
|
13452
|
+
'<p class="muted concierge-foot">First time? <a href="#intelligence">Pick a substrate</a> to enable concierge replies.</p>',
|
|
13356
13453
|
'</div>',
|
|
13357
|
-
errorBanner,
|
|
13358
|
-
'<div class="concierge-history" id="concierge-history">' + messages + '</div>',
|
|
13359
|
-
'<form class="concierge-composer" data-action="concierge-submit">',
|
|
13360
|
-
'<input type="text" name="concierge-input" placeholder="Ask the concierge about this fortress..." value="' + escHtml(c.composer) + '" data-action="concierge-input"' + sendDisabled + ' autocomplete="off">',
|
|
13361
|
-
'<button type="submit" class="btn btn-primary" data-action="concierge-send"' + sendDisabled + '>' + escHtml(sendLabel) + '</button>',
|
|
13362
|
-
'</form>',
|
|
13363
|
-
'<div class="concierge-chips">' + chips + '</div>',
|
|
13364
|
-
'<p class="muted concierge-foot">First time? <a href="#intelligence">Pick a substrate</a> to enable concierge replies.</p>',
|
|
13365
13454
|
'</div>'
|
|
13366
13455
|
].join("");
|
|
13367
13456
|
}
|
|
13368
13457
|
|
|
13369
13458
|
// ── Render: agents list / detail ───────────────────────────────────────
|
|
13459
|
+
//
|
|
13460
|
+
// Sprint Piece 2 PR 4 polish: empty state uses .agents-empty with the
|
|
13461
|
+
// concentric icon-frame + a terminal-block CTA. Populated state uses the
|
|
13462
|
+
// .agents-layout grid with the .agents-list 4-column table (Agent /
|
|
13463
|
+
// State / Attestation / Last seen). The empty-state branch keeps the
|
|
13464
|
+
// literal '<h1>Agents</h1>' start and the "No wrapped agents yet." copy
|
|
13465
|
+
// because agents-empty-state-canary.test.ts pins both.
|
|
13466
|
+
function agentInitials(agentId) {
|
|
13467
|
+
const tail = String(agentId || "").split(":").pop() || "";
|
|
13468
|
+
const cleaned = tail.replace(/[^a-zA-Z0-9]/g, "");
|
|
13469
|
+
return (cleaned.slice(0, 2) || "??").toUpperCase();
|
|
13470
|
+
}
|
|
13471
|
+
function agentStateClass(status) {
|
|
13472
|
+
if (status === "active") return "live";
|
|
13473
|
+
if (status === "locked_down" || status === "error") return "off";
|
|
13474
|
+
return "idle";
|
|
13475
|
+
}
|
|
13476
|
+
// Per-agent attestation badge (Q1 layer 2). Square chip beside each
|
|
13477
|
+
// agent: a bounded glyph beside a bounded entity. Color and fill pattern
|
|
13478
|
+
// carry meaning together so the badge reads even monochrome. The "locked"
|
|
13479
|
+
// status maps to the unverified visual (rust + hatched mark) since a
|
|
13480
|
+
// locked-down agent has no current attestation; the inspect-pane copy
|
|
13481
|
+
// explains the distinction. Pure visual surface; no state derivation.
|
|
13482
|
+
function renderAgentAttestationBadge(status) {
|
|
13483
|
+
let cls;
|
|
13484
|
+
let label;
|
|
13485
|
+
if (status === "active") { cls = "verified"; label = "verified"; }
|
|
13486
|
+
else if (status === "locked_down") { cls = "unverified"; label = "locked"; }
|
|
13487
|
+
else if (status === "error") { cls = "unverified"; label = "unverified"; }
|
|
13488
|
+
else { cls = "degraded"; label = "degraded"; }
|
|
13489
|
+
return '<span class="att-agent ' + cls + '" title="Agent attestation"><span class="mark"></span>' + escHtml(label) + '</span>';
|
|
13490
|
+
}
|
|
13491
|
+
// Per-action attestation tick (Q1 layer 3). Tiny inline shape on every
|
|
13492
|
+
// timeline row. Two-byte signature fragment is enough at low resolution;
|
|
13493
|
+
// the full signature is one click away. Neutral state shows a circle
|
|
13494
|
+
// instead of a tick when the signer was unreachable; the action is still
|
|
13495
|
+
// recorded. Visual surface only.
|
|
13496
|
+
function renderActionAttestationBadge(stateName, sig) {
|
|
13497
|
+
const valid = stateName === "verified" || stateName === "degraded" || stateName === "unverified" || stateName === "neutral";
|
|
13498
|
+
const cls = valid ? stateName : "neutral";
|
|
13499
|
+
const sigText = sig ? String(sig) : "--";
|
|
13500
|
+
return '<span class="att-action ' + cls + '" title="Action attestation">' +
|
|
13501
|
+
'<span class="tick"></span>' +
|
|
13502
|
+
'<span>' + escHtml(sigText) + '</span>' +
|
|
13503
|
+
'</span>';
|
|
13504
|
+
}
|
|
13505
|
+
// Attestation gallery surface (Q1 four classes: global / per-agent /
|
|
13506
|
+
// per-action / per-transaction custody-provenance stub). Reference for
|
|
13507
|
+
// operators: shows what each badge looks like across verified, degraded,
|
|
13508
|
+
// unverified, and (where applicable) pending or neutral states. Pure
|
|
13509
|
+
// visual; no derivation, no live data. Castle Layer 3 cooperative-MCP UX
|
|
13510
|
+
// surface; Castle Layer 1 enforcement ships in WP-V1.x-CASTLE-WALL.
|
|
13511
|
+
function renderAttestation() {
|
|
13512
|
+
return '<div class="att-gallery">' +
|
|
13513
|
+
'<div class="page-head"><div>' +
|
|
13514
|
+
'<p class="eyebrow">Attestation</p>' +
|
|
13515
|
+
'<h1>Four classes of badge.</h1>' +
|
|
13516
|
+
'<p class="sub">A signature you can see. From the whole fortress, down to a single action. Degrade, never destroy: a failed signature becomes neutral with a tooltip; the surface keeps working.</p>' +
|
|
13517
|
+
'</div></div>' +
|
|
13518
|
+
// Global
|
|
13519
|
+
'<div class="att-section">' +
|
|
13520
|
+
'<div class="att-section-head"><div>' +
|
|
13521
|
+
'<h2>Global. The fortress itself.</h2>' +
|
|
13522
|
+
'<p>Lives in the topbar. Visible on every surface. Tells you the fortress identity is currently signed and matches the binary you installed.</p>' +
|
|
13523
|
+
'</div><span class="label">topbar</span></div>' +
|
|
13524
|
+
attRow(renderTopbarAttestationBadge("verified"), "Verified", "Identity matches. Binary matches. Default state for a healthy fortress.") +
|
|
13525
|
+
attRow(renderTopbarAttestationBadge("degraded"), "Degraded", "The signature is older than the staleness window, or one of two co-signers is unreachable. The fortress keeps running.") +
|
|
13526
|
+
attRow(renderTopbarAttestationBadge("unverified"), "Unverified", "The signature did not validate. The surface still works; lockdown is still available; the badge tells you to investigate.") +
|
|
13527
|
+
attRow(renderTopbarAttestationBadge("pending"), "Pending", "First-run state. Fortress is signing for the first time. Settles in seconds.") +
|
|
13528
|
+
'</div>' +
|
|
13529
|
+
// Per-agent
|
|
13530
|
+
'<div class="att-section">' +
|
|
13531
|
+
'<div class="att-section-head"><div>' +
|
|
13532
|
+
'<h2>Per-agent. In the agents list and inspect pane.</h2>' +
|
|
13533
|
+
'<p>A square chip beside each agent. Square because an agent is bounded; the fortress (a circle) contains it.</p>' +
|
|
13534
|
+
'</div><span class="label">agents view</span></div>' +
|
|
13535
|
+
'<div class="att-row">' +
|
|
13536
|
+
'<div class="demo" style="display:flex; gap:8px; flex-wrap:wrap;">' +
|
|
13537
|
+
renderAgentAttestationBadge("active") +
|
|
13538
|
+
renderAgentAttestationBadgeForState("degraded", "degraded") +
|
|
13539
|
+
renderAgentAttestationBadgeForState("unverified", "unverified") +
|
|
13540
|
+
'</div>' +
|
|
13541
|
+
'<div class="desc"><strong>Verified, degraded, unverified</strong>' +
|
|
13542
|
+
'<small>Color and the fill pattern carry meaning together. A solid square reads "attested" at a glance; a hatched square reads "trouble" at a glance, even monochrome.</small>' +
|
|
13543
|
+
'</div>' +
|
|
13544
|
+
'</div>' +
|
|
13545
|
+
'</div>' +
|
|
13546
|
+
// Per-action
|
|
13547
|
+
'<div class="att-section">' +
|
|
13548
|
+
'<div class="att-section-head"><div>' +
|
|
13549
|
+
'<h2>Per-action. Inline in the activity timeline.</h2>' +
|
|
13550
|
+
'<p>Each entry in any timeline carries a small signature fragment. Hover to expand. A tick instead of a fill keeps the row visually quiet at low resolution.</p>' +
|
|
13551
|
+
'</div><span class="label">timeline</span></div>' +
|
|
13552
|
+
'<div class="att-row">' +
|
|
13553
|
+
'<div class="demo" style="display:flex; gap:10px; flex-wrap:wrap; align-items:center;">' +
|
|
13554
|
+
'<span style="font-size:13px; color:var(--ink-2);">14:22:08 doc-reviewer summarized intake.pdf</span>' +
|
|
13555
|
+
renderActionAttestationBadge("verified", "9c7d..2a") +
|
|
13556
|
+
'</div>' +
|
|
13557
|
+
'<div class="desc"><strong>Verified action</strong>' +
|
|
13558
|
+
'<small>The most common shape. Two-byte signature fragment is enough; the full signature is one click away.</small>' +
|
|
13559
|
+
'</div>' +
|
|
13560
|
+
'</div>' +
|
|
13561
|
+
'<div class="att-row">' +
|
|
13562
|
+
'<div class="demo" style="display:flex; gap:10px; align-items:center;">' +
|
|
13563
|
+
'<span style="font-size:13px; color:var(--ink-2);">14:11:47 privacy filter redacted payload</span>' +
|
|
13564
|
+
renderActionAttestationBadge("degraded", "b440..71") +
|
|
13565
|
+
'</div>' +
|
|
13566
|
+
'<div class="desc"><strong>Degraded action</strong>' +
|
|
13567
|
+
'<small>The action signed, but the signature class was less than the policy preferred. Useful when a substrate is still warming up.</small>' +
|
|
13568
|
+
'</div>' +
|
|
13569
|
+
'</div>' +
|
|
13570
|
+
'<div class="att-row">' +
|
|
13571
|
+
'<div class="demo" style="display:flex; gap:10px; align-items:center;">' +
|
|
13572
|
+
'<span style="font-size:13px; color:var(--ink-2);">14:09:02 agent attempted external link</span>' +
|
|
13573
|
+
renderActionAttestationBadge("neutral", "--") +
|
|
13574
|
+
'</div>' +
|
|
13575
|
+
'<div class="desc"><strong>Neutral. Degrade, not destroy.</strong>' +
|
|
13576
|
+
'<small>The signer was unreachable. Rather than hide the action, the badge becomes neutral and a tooltip explains. The action is still recorded.</small>' +
|
|
13577
|
+
'</div>' +
|
|
13578
|
+
'</div>' +
|
|
13579
|
+
'</div>' +
|
|
13580
|
+
// Custody stub
|
|
13581
|
+
'<div class="att-section">' +
|
|
13582
|
+
'<div class="att-section-head"><div>' +
|
|
13583
|
+
'<h2>Custody. Stub for v1.x.</h2>' +
|
|
13584
|
+
'<p>A fourth class, surfaced conservatively. Reserved for forthcoming custody-provenance signatures (x402 payment receipts, ERC-8004 identity assertions). Visible, dashed, clearly stubbed.</p>' +
|
|
13585
|
+
'</div><span class="label">stub</span></div>' +
|
|
13586
|
+
'<div class="att-row">' +
|
|
13587
|
+
'<div class="demo">' +
|
|
13588
|
+
'<span class="att-custody" title="Custody-provenance, v1.x">' +
|
|
13589
|
+
'<span class="seal-stub"></span>' +
|
|
13590
|
+
'<span class="stub-tag">custody. stub</span>' +
|
|
13591
|
+
'</span>' +
|
|
13592
|
+
'</div>' +
|
|
13593
|
+
'<div class="desc"><strong>Custody. Stub.</strong>' +
|
|
13594
|
+
'<small>Dashed border signals "shape reserved, content pending." Will populate when custody signatures land in a future release. Cannot be confused with a verified badge at any zoom level.</small>' +
|
|
13595
|
+
'</div>' +
|
|
13596
|
+
'</div>' +
|
|
13597
|
+
'</div>' +
|
|
13598
|
+
// Tooltip
|
|
13599
|
+
'<div class="att-section">' +
|
|
13600
|
+
'<div class="att-section-head"><div>' +
|
|
13601
|
+
'<h2>Tooltip on failure.</h2>' +
|
|
13602
|
+
'<p>A failed badge is never silent. The tooltip explains in plain language, suggests one action, and confirms the surface is still working.</p>' +
|
|
13603
|
+
'</div><span class="label">degrade not destroy</span></div>' +
|
|
13604
|
+
'<div class="att-row">' +
|
|
13605
|
+
'<div class="demo">' +
|
|
13606
|
+
'<span class="att-tooltip">The signer at sig.fortress.local did not respond in 4s. Your fortress kept working. Try: open Health to see the signer status.</span>' +
|
|
13607
|
+
'</div>' +
|
|
13608
|
+
'<div class="desc"><strong>Plain-language tooltip</strong>' +
|
|
13609
|
+
'<small>Three lines, in order: what happened, what did not break, what to do. No jargon, no stack trace.</small>' +
|
|
13610
|
+
'</div>' +
|
|
13611
|
+
'</div>' +
|
|
13612
|
+
'</div>' +
|
|
13613
|
+
'</div>';
|
|
13614
|
+
}
|
|
13615
|
+
function attRow(demoHtml, strong, smallText) {
|
|
13616
|
+
return '<div class="att-row">' +
|
|
13617
|
+
'<div class="demo">' + demoHtml + '</div>' +
|
|
13618
|
+
'<div class="desc"><strong>' + escHtml(strong) + '</strong>' +
|
|
13619
|
+
'<small>' + escHtml(smallText) + '</small>' +
|
|
13620
|
+
'</div>' +
|
|
13621
|
+
'</div>';
|
|
13622
|
+
}
|
|
13623
|
+
// Gallery-only variant: render a per-agent badge for a given visual state
|
|
13624
|
+
// (verified / degraded / unverified) without going through the agent
|
|
13625
|
+
// status mapping. Used by renderAttestation to show all three states
|
|
13626
|
+
// side by side as design reference.
|
|
13627
|
+
function renderAgentAttestationBadgeForState(cls, label) {
|
|
13628
|
+
return '<span class="att-agent ' + escHtml(cls) + '" title="Agent attestation"><span class="mark"></span>' + escHtml(label) + '</span>';
|
|
13629
|
+
}
|
|
13630
|
+
function relTimeFromIso(iso) {
|
|
13631
|
+
if (!iso) return "";
|
|
13632
|
+
const d = new Date(iso);
|
|
13633
|
+
if (isNaN(d.getTime())) return iso;
|
|
13634
|
+
const diffMs = Date.now() - d.getTime();
|
|
13635
|
+
const diffSec = Math.max(0, Math.floor(diffMs / 1000));
|
|
13636
|
+
if (diffSec < 60) return diffSec + "s ago";
|
|
13637
|
+
const diffMin = Math.floor(diffSec / 60);
|
|
13638
|
+
if (diffMin < 60) return diffMin + "m ago";
|
|
13639
|
+
const diffHr = Math.floor(diffMin / 60);
|
|
13640
|
+
if (diffHr < 24) return diffHr + "h ago";
|
|
13641
|
+
const diffDay = Math.floor(diffHr / 24);
|
|
13642
|
+
return diffDay + "d ago";
|
|
13643
|
+
}
|
|
13370
13644
|
function renderAgentsList() {
|
|
13371
|
-
if (!state.agents.length) return '<h1>Agents</h1
|
|
13645
|
+
if (!state.agents.length) return '<h1>Agents</h1>' +
|
|
13646
|
+
'<div class="agents-empty">' +
|
|
13647
|
+
'<div class="icon-frame"><div class="core"></div></div>' +
|
|
13648
|
+
'<h2>No wrapped agents yet.</h2>' +
|
|
13649
|
+
'<p>Wrap an agent to give it a portable identity, a charter, and approval gates. Run <code>sanctuary wrap</code> in any project where your agent lives.</p>' +
|
|
13650
|
+
'<div class="terminal-block"><span class="cmd"><span class="prompt">$</span>sanctuary wrap</span></div>' +
|
|
13651
|
+
'</div>';
|
|
13652
|
+
const count = state.agents.length;
|
|
13653
|
+
const subCopy = count + ' wrapped. Click one to inspect its activity, policy, and pending approvals.';
|
|
13372
13654
|
const rows = state.agents.map(function (a) {
|
|
13373
13655
|
const map = STATUS_MAP[a.status] || STATUS_MAP.unknown;
|
|
13374
|
-
const
|
|
13375
|
-
|
|
13376
|
-
|
|
13377
|
-
|
|
13378
|
-
|
|
13379
|
-
'<
|
|
13656
|
+
const dotCls = agentStateClass(a.status);
|
|
13657
|
+
const initials = agentInitials(a.agent_id);
|
|
13658
|
+
const role = escHtml(a.harness) + (a.model_provider && a.model_provider.model_id ? ' · ' + escHtml(a.model_provider.model_id) : '');
|
|
13659
|
+
const isSelected = state.selectedAgentId === a.agent_id;
|
|
13660
|
+
return '<div class="agent-row' + (isSelected ? ' selected' : '') + '" data-action="open-agent" data-agent-id="' + escHtml(a.agent_id) + '" role="button" tabindex="0" title="Open inspect panel for ' + escHtml(a.agent_id) + '">' +
|
|
13661
|
+
'<div class="agent-identity">' +
|
|
13662
|
+
'<div class="agent-glyph">' + escHtml(initials) + '</div>' +
|
|
13663
|
+
'<div class="agent-name">' +
|
|
13664
|
+
'<strong>' + escHtml(a.agent_id) + '</strong>' +
|
|
13665
|
+
'<small>' + role + '</small>' +
|
|
13666
|
+
'</div>' +
|
|
13667
|
+
'</div>' +
|
|
13668
|
+
'<span class="agent-state">' +
|
|
13669
|
+
'<span class="state-dot ' + dotCls + '"></span>' +
|
|
13670
|
+
escHtml(map.label) +
|
|
13671
|
+
'</span>' +
|
|
13672
|
+
renderAgentAttestationBadge(a.status) +
|
|
13673
|
+
'<span class="agent-last">' + escHtml(relTimeFromIso(a.last_activity_at)) + '</span>' +
|
|
13380
13674
|
'</div>';
|
|
13381
13675
|
}).join("\n");
|
|
13382
|
-
return '<
|
|
13676
|
+
return '<div class="agents-wrap">' +
|
|
13677
|
+
'<div class="page-head">' +
|
|
13678
|
+
'<div>' +
|
|
13679
|
+
'<p class="eyebrow">Agents</p>' +
|
|
13680
|
+
'<h1>Agents.</h1>' +
|
|
13681
|
+
'<p class="sub">' + escHtml(subCopy) + '</p>' +
|
|
13682
|
+
'</div>' +
|
|
13683
|
+
'</div>' +
|
|
13684
|
+
'<div class="agents-layout">' +
|
|
13685
|
+
'<div class="agents-list">' +
|
|
13686
|
+
'<div class="agents-list-head">' +
|
|
13687
|
+
'<span>Agent</span><span>State</span><span>Attestation</span><span>Last seen</span>' +
|
|
13688
|
+
'</div>' +
|
|
13689
|
+
rows +
|
|
13690
|
+
'</div>' +
|
|
13691
|
+
'</div>' +
|
|
13692
|
+
'</div>';
|
|
13383
13693
|
}
|
|
13384
13694
|
|
|
13385
13695
|
function renderAgentDetail() {
|
|
@@ -13390,7 +13700,10 @@ function renderAgentDetail() {
|
|
|
13390
13700
|
const timeline = events.length
|
|
13391
13701
|
? events.map(function (e) {
|
|
13392
13702
|
const t = renderTemplate(e.display_template_id, e.display_template_args);
|
|
13393
|
-
|
|
13703
|
+
const badgeHtml = e.attestation
|
|
13704
|
+
? ' ' + renderActionAttestationBadge(e.attestation.state, e.attestation.fragment)
|
|
13705
|
+
: '';
|
|
13706
|
+
return '<div class="row"><span class="muted">' + escHtml(shortTime(e.emitted_at)) + '</span><span>' + escHtml(t) + badgeHtml + '</span></div>';
|
|
13394
13707
|
}).join("\n")
|
|
13395
13708
|
: '<p class="muted">No activity yet.</p>';
|
|
13396
13709
|
// WP-V1.2 reshape click-to-inspect surface. Clicking "Open inspect
|
|
@@ -13434,53 +13747,99 @@ function renderAgentInspectPanel(agent) {
|
|
|
13434
13747
|
: "";
|
|
13435
13748
|
|
|
13436
13749
|
// State 2: panel loaded.
|
|
13750
|
+
// Sprint Piece 2 PR 4 polish: outer wrapper combines .card with
|
|
13751
|
+
// .inspect-pane (sticky right rail, internal scroll, sectioned body).
|
|
13752
|
+
// The .card class is preserved so the rendered surface keeps its
|
|
13753
|
+
// shared card chrome; .inspect-pane overrides .card padding so the
|
|
13754
|
+
// inspect-head and inspect-body control their own spacing per design.
|
|
13437
13755
|
if (panel) {
|
|
13756
|
+
const dotCls = agentStateClass(agent.status);
|
|
13757
|
+
const stateMap = STATUS_MAP[agent.status] || STATUS_MAP.unknown;
|
|
13438
13758
|
const activity = (panel.recent_activity || []).slice(0, 20);
|
|
13439
13759
|
const activityHtml = activity.length
|
|
13440
|
-
?
|
|
13760
|
+
? '<div class="timeline">' +
|
|
13761
|
+
activity.map(function (e) {
|
|
13441
13762
|
const t = renderTemplate(e.display_template_id, e.display_template_args);
|
|
13442
|
-
|
|
13443
|
-
|
|
13763
|
+
const badgeHtml = e.attestation
|
|
13764
|
+
? renderActionAttestationBadge(e.attestation.state, e.attestation.fragment)
|
|
13765
|
+
: '';
|
|
13766
|
+
return '<div class="timeline-item ok">' +
|
|
13767
|
+
'<div class="ts">' + escHtml(shortTime(e.emitted_at)) + '</div>' +
|
|
13768
|
+
'<div class="what">' + escHtml(t) + '</div>' +
|
|
13769
|
+
(badgeHtml ? '<div class="att">' + badgeHtml + '</div>' : '') +
|
|
13770
|
+
'</div>';
|
|
13771
|
+
}).join("") +
|
|
13772
|
+
'</div>'
|
|
13444
13773
|
: '<p class="muted">No recent activity for this agent.</p>';
|
|
13445
13774
|
|
|
13446
13775
|
const approvals = panel.pending_approvals || [];
|
|
13447
13776
|
const approvalsHtml = approvals.length
|
|
13448
13777
|
? approvals.map(function (item) {
|
|
13449
13778
|
const promptText = renderTemplate(item.display_template_id, item.display_template_args);
|
|
13450
|
-
return '<div class="row">' +
|
|
13451
|
-
'<
|
|
13452
|
-
|
|
13453
|
-
|
|
13454
|
-
'
|
|
13779
|
+
return '<div class="approval-row">' +
|
|
13780
|
+
'<div class="what">' +
|
|
13781
|
+
'<span class="pill tone-degraded">' + escHtml(item.tier || "tier1") + '</span>' +
|
|
13782
|
+
escHtml(promptText) +
|
|
13783
|
+
'</div>' +
|
|
13784
|
+
'<div class="actions">' +
|
|
13785
|
+
'<button class="btn" data-action="inbox-deny" data-item-id="' + escHtml(item.item_id) + '">Deny</button>' +
|
|
13786
|
+
'<button class="btn btn-primary" data-action="inbox-approve" data-item-id="' + escHtml(item.item_id) + '">Approve once</button>' +
|
|
13787
|
+
'</div>' +
|
|
13455
13788
|
'</div>';
|
|
13456
|
-
}).join("
|
|
13789
|
+
}).join("")
|
|
13457
13790
|
: '<p class="muted">No pending approvals routed through this agent.</p>';
|
|
13458
13791
|
|
|
13459
|
-
const
|
|
13460
|
-
? '<
|
|
13792
|
+
const policySection = panel.policy_summary
|
|
13793
|
+
? '<div class="policy-line"><span class="k">Policy</span><span class="v">' + escHtml(panel.policy_summary.display_label || panel.policy_summary.policy_id) + '</span></div>' +
|
|
13461
13794
|
(panel.policy_summary.channel_template_id
|
|
13462
|
-
? '<
|
|
13795
|
+
? '<div class="policy-line"><span class="k">Template</span><span class="v">' + escHtml(panel.policy_summary.channel_template_id) + '</span></div>'
|
|
13463
13796
|
: '') +
|
|
13464
|
-
'<
|
|
13465
|
-
: '<
|
|
13466
|
-
|
|
13467
|
-
|
|
13468
|
-
'<div class="
|
|
13469
|
-
|
|
13470
|
-
|
|
13471
|
-
|
|
13797
|
+
'<div class="policy-line"><span class="k">Bound</span><span class="v">' + escHtml(shortTime(panel.policy_summary.bound_at)) + '</span></div>'
|
|
13798
|
+
: '<div class="policy-line"><span class="k">Policy</span><span class="v">No bound policy yet.</span></div>';
|
|
13799
|
+
|
|
13800
|
+
const modelLine = agent.model_provider
|
|
13801
|
+
? '<div class="policy-line"><span class="k">Model</span><span class="v">' + escHtml(agent.model_provider.vendor) + ' / ' + escHtml(agent.model_provider.model_id) + '</span></div>'
|
|
13802
|
+
: '';
|
|
13803
|
+
|
|
13804
|
+
return '<div class="card inspect-pane">' +
|
|
13805
|
+
'<div class="inspect-head">' +
|
|
13806
|
+
'<div class="row1">' +
|
|
13807
|
+
'<div class="agent-glyph">' + escHtml(agentInitials(agent.agent_id)) + '</div>' +
|
|
13808
|
+
'<h3>' + escHtml(agent.agent_id) + '</h3>' +
|
|
13809
|
+
'<span style="margin-left:auto;">' + renderAgentAttestationBadge(agent.status) + '</span>' +
|
|
13810
|
+
'</div>' +
|
|
13811
|
+
'<div class="meta">' +
|
|
13812
|
+
'<span class="pill ' + (dotCls === "live" ? "tone-verified" : "tone-degraded") + '"><span class="state-dot ' + dotCls + '" style="margin-right:4px;"></span>' + escHtml(stateMap.label) + '</span>' +
|
|
13813
|
+
'<span class="pill">opened ' + escHtml(shortTime(panel.opened_at)) + '</span>' +
|
|
13814
|
+
'<button class="btn btn-quiet" data-action="agent-inspect-open" data-agent-id="' + escHtml(agent.agent_id) + '" title="Refresh inspect panel">Refresh</button>' +
|
|
13815
|
+
'</div>' +
|
|
13816
|
+
'</div>' +
|
|
13817
|
+
'<div class="inspect-body">' +
|
|
13818
|
+
errorBanner +
|
|
13819
|
+
'<div class="inspect-section">' +
|
|
13820
|
+
'<h4>Pending approvals' + (approvals.length ? ' <span class="count">' + approvals.length + '</span>' : '') + '</h4>' +
|
|
13821
|
+
approvalsHtml +
|
|
13822
|
+
'</div>' +
|
|
13823
|
+
'<div class="inspect-section">' +
|
|
13824
|
+
'<h4>Recent activity</h4>' +
|
|
13825
|
+
activityHtml +
|
|
13826
|
+
'</div>' +
|
|
13827
|
+
'<div class="inspect-section">' +
|
|
13828
|
+
'<h4>Policy summary</h4>' +
|
|
13829
|
+
policySection +
|
|
13830
|
+
'</div>' +
|
|
13831
|
+
'<div class="inspect-section">' +
|
|
13832
|
+
'<h4>Identity</h4>' +
|
|
13833
|
+
'<div class="policy-line"><span class="k">Agent id</span><span class="v">' + escHtml(agent.agent_id) + '</span></div>' +
|
|
13834
|
+
'<div class="policy-line"><span class="k">Harness</span><span class="v">' + escHtml(agent.harness) + '</span></div>' +
|
|
13835
|
+
modelLine +
|
|
13836
|
+
'<div class="policy-line"><span class="k">Wrapped at</span><span class="v">' + escHtml(shortTime(agent.wrapped_at)) + '</span></div>' +
|
|
13837
|
+
'</div>' +
|
|
13838
|
+
'<p class="muted" style="margin-top:10px;font-size:12px;">' +
|
|
13839
|
+
'<a href="#activity?agent=' + escHtml(agent.agent_id) + '">View full activity</a> · ' +
|
|
13840
|
+
'<a href="#policy">Edit policy</a>' +
|
|
13841
|
+
'</p>' +
|
|
13472
13842
|
'</div>' +
|
|
13473
|
-
errorBanner +
|
|
13474
|
-
'<h3>Pending approvals</h3>' +
|
|
13475
|
-
approvalsHtml +
|
|
13476
|
-
'<h3 style="margin-top:14px;">Recent activity</h3>' +
|
|
13477
|
-
activityHtml +
|
|
13478
|
-
'<h3 style="margin-top:14px;">Policy</h3>' +
|
|
13479
|
-
'<dl class="kv">' + policyLine + '</dl>' +
|
|
13480
|
-
'<p class="muted" style="margin-top:10px;font-size:12px;">' +
|
|
13481
|
-
'<a href="#activity?agent=' + escHtml(agent.agent_id) + '">View full activity</a> · ' +
|
|
13482
|
-
'<a href="#policy">Edit policy</a>' +
|
|
13483
|
-
'</p>' +
|
|
13484
13843
|
'</div>';
|
|
13485
13844
|
}
|
|
13486
13845
|
|
|
@@ -13629,6 +13988,16 @@ function statusDotClass(status) {
|
|
|
13629
13988
|
return "red";
|
|
13630
13989
|
}
|
|
13631
13990
|
|
|
13991
|
+
// Card-grid polish (Sprint Piece 2 PR 3) maps the badge dot class onto
|
|
13992
|
+
// the shaped glyph token. Sage circle for ok, ochre triangle for warn,
|
|
13993
|
+
// rust diamond for fail. Keep aligned with .status-glyph rules in
|
|
13994
|
+
// html.ts and the .intel-card-status modifier classes.
|
|
13995
|
+
function statusGlyphClass(dotClass) {
|
|
13996
|
+
if (dotClass === "green") return "ok";
|
|
13997
|
+
if (dotClass === "yellow") return "warn";
|
|
13998
|
+
return "fail";
|
|
13999
|
+
}
|
|
14000
|
+
|
|
13632
14001
|
function statusLabel(health) {
|
|
13633
14002
|
if (health === "ok") return "Working";
|
|
13634
14003
|
if (health === "degraded") return "Degraded";
|
|
@@ -13668,13 +14037,18 @@ function renderIntelligenceCenter() {
|
|
|
13668
14037
|
}
|
|
13669
14038
|
const status = state.intelligence.status;
|
|
13670
14039
|
const config = state.intelligence.config || {};
|
|
13671
|
-
const
|
|
14040
|
+
const surfaceCards = SURFACES_ORDER.map(function (surfaceId) {
|
|
13672
14041
|
const surfaceStatus = (status.surfaces || []).find(function (s) { return s.surface === surfaceId; });
|
|
13673
14042
|
if (!surfaceStatus) {
|
|
13674
|
-
return '<div class="intel-row"
|
|
13675
|
-
'<
|
|
13676
|
-
|
|
13677
|
-
|
|
14043
|
+
return '<div class="intel-row intel-card" data-intel-surface="' + escHtml(surfaceId) + '">' +
|
|
14044
|
+
'<div class="intel-card-head">' +
|
|
14045
|
+
'<div class="intel-card-name">' +
|
|
14046
|
+
'<strong>' + escHtml(SURFACE_LABELS[surfaceId] || surfaceId) + '</strong>' +
|
|
14047
|
+
'<small>' + escHtml(surfaceId) + '</small>' +
|
|
14048
|
+
'</div>' +
|
|
14049
|
+
'</div>' +
|
|
14050
|
+
'<div class="muted">No status reported.</div>' +
|
|
14051
|
+
'</div>';
|
|
13678
14052
|
}
|
|
13679
14053
|
const substrate = surfaceStatus.chosen;
|
|
13680
14054
|
const localPick = (config.local_model_picks || {})[surfaceId];
|
|
@@ -13692,41 +14066,62 @@ function renderIntelligenceCenter() {
|
|
|
13692
14066
|
if (provider) currentBadge = currentBadge + " (" + (FRONTIER_PROVIDER_LABELS[provider] || provider) + ")";
|
|
13693
14067
|
}
|
|
13694
14068
|
const dotClass = statusDotClass((surfaceStatus.badge || {}).status || "red");
|
|
14069
|
+
const glyphClass = statusGlyphClass(dotClass);
|
|
13695
14070
|
const failures = surfaceStatus.recentFailures || [];
|
|
13696
14071
|
const expanded = !!state.intelligence.expandedFailures[surfaceId];
|
|
13697
|
-
|
|
14072
|
+
|
|
14073
|
+
// Card foot. The failures toggle is the load-bearing affordance for
|
|
14074
|
+
// the rc.6 ZZ test (button[data-action="intel-failures-toggle"] with
|
|
14075
|
+
// text "recent failures (N)"). Pluralization is "failures" regardless
|
|
14076
|
+
// of N for backward compatibility with the seeded test contract.
|
|
14077
|
+
// Surface zero-failure state as a quiet mono note so the card still
|
|
14078
|
+
// has visual rhythm in its foot row.
|
|
14079
|
+
let footHtml;
|
|
13698
14080
|
if (failures.length > 0) {
|
|
13699
14081
|
const toggleLabel = (expanded ? "Hide" : "View") + " recent failures (" + failures.length + ")";
|
|
13700
|
-
|
|
13701
|
-
|
|
13702
|
-
|
|
13703
|
-
|
|
13704
|
-
|
|
13705
|
-
|
|
13706
|
-
|
|
13707
|
-
|
|
13708
|
-
|
|
13709
|
-
|
|
13710
|
-
|
|
13711
|
-
|
|
13712
|
-
|
|
13713
|
-
'</
|
|
13714
|
-
|
|
14082
|
+
footHtml =
|
|
14083
|
+
'<button class="intel-failures-toggle' + (expanded ? ' open' : '') + '" data-action="intel-failures-toggle" data-intel-surface="' + escHtml(surfaceId) + '">' +
|
|
14084
|
+
'<span class="caret"></span>' +
|
|
14085
|
+
escHtml(toggleLabel) +
|
|
14086
|
+
'</button>';
|
|
14087
|
+
} else {
|
|
14088
|
+
footHtml = '<span class="muted mono" style="font-size: 11px;">no recent failures</span>';
|
|
14089
|
+
}
|
|
14090
|
+
|
|
14091
|
+
let failuresBlock = "";
|
|
14092
|
+
if (failures.length > 0 && expanded) {
|
|
14093
|
+
const rows = failures.slice().reverse().map(function (f) {
|
|
14094
|
+
return '<div class="intel-failure-row">' +
|
|
14095
|
+
'<span class="ts">' + escHtml(shortTime(f.ts)) + '</span>' +
|
|
14096
|
+
'<div>' +
|
|
14097
|
+
'<div class="err-class">' + escHtml(f.failureClass) + '</div>' +
|
|
14098
|
+
'<div>' + escHtml(f.snippet) + '</div>' +
|
|
14099
|
+
'</div>' +
|
|
13715
14100
|
'</div>';
|
|
14101
|
+
}).join("");
|
|
14102
|
+
failuresBlock = '<div class="intel-failures">' + rows + '</div>';
|
|
13716
14103
|
}
|
|
13717
|
-
|
|
13718
|
-
|
|
13719
|
-
|
|
13720
|
-
|
|
13721
|
-
|
|
13722
|
-
'<
|
|
13723
|
-
|
|
13724
|
-
|
|
14104
|
+
|
|
14105
|
+
return '<div class="intel-row intel-card" data-intel-surface="' + escHtml(surfaceId) + '">' +
|
|
14106
|
+
'<div class="intel-card-head">' +
|
|
14107
|
+
'<div class="intel-card-name">' +
|
|
14108
|
+
'<strong>' + escHtml(SURFACE_LABELS[surfaceId] || surfaceId) + '</strong>' +
|
|
14109
|
+
'<small>' + escHtml(surfaceId) + '</small>' +
|
|
14110
|
+
'</div>' +
|
|
14111
|
+
'<span class="intel-card-status ' + glyphClass + '" title="' + escHtml(statusLabel(surfaceStatus.health)) + '">' +
|
|
14112
|
+
'<span class="status-glyph ' + glyphClass + '"></span>' +
|
|
14113
|
+
escHtml(statusLabel(surfaceStatus.health)) +
|
|
14114
|
+
'</span>' +
|
|
14115
|
+
'</div>' +
|
|
14116
|
+
'<div class="intel-substrate">' +
|
|
14117
|
+
'<div class="sub-line primary">' +
|
|
14118
|
+
'<span>' + escHtml(currentBadge) + '</span>' +
|
|
14119
|
+
'<button class="btn-quiet" data-action="intel-picker-open" data-intel-surface="' + escHtml(surfaceId) + '">Change</button>' +
|
|
13725
14120
|
'</div>' +
|
|
13726
|
-
'<div class="intel-row-tradeoff">' + escHtml(substrateTradeoff(substrate)) + '</div>' +
|
|
13727
|
-
failuresBlock +
|
|
13728
14121
|
'</div>' +
|
|
13729
|
-
'<div
|
|
14122
|
+
'<div class="intel-row-tradeoff">' + escHtml(substrateTradeoff(substrate)) + '</div>' +
|
|
14123
|
+
'<div class="intel-card-foot">' + footHtml + '</div>' +
|
|
14124
|
+
failuresBlock +
|
|
13730
14125
|
'</div>';
|
|
13731
14126
|
}).join("\n");
|
|
13732
14127
|
|
|
@@ -13738,11 +14133,15 @@ function renderIntelligenceCenter() {
|
|
|
13738
14133
|
|
|
13739
14134
|
const modal = state.intelligence.picker.open ? renderIntelligencePicker() : "";
|
|
13740
14135
|
|
|
13741
|
-
return '<section class="intel-
|
|
13742
|
-
'<
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
14136
|
+
return '<section class="intel-wrap">' +
|
|
14137
|
+
'<div class="page-head">' +
|
|
14138
|
+
'<div>' +
|
|
14139
|
+
'<p class="eyebrow">Intelligence</p>' +
|
|
14140
|
+
'<h1>Substrate routing.</h1>' +
|
|
14141
|
+
'<p class="sub">Six surfaces, six choices. Each surface picks where its thinking happens. Local for privacy. Hosted for capability. Hybrid for both.</p>' +
|
|
14142
|
+
'</div>' +
|
|
14143
|
+
'</div>' +
|
|
14144
|
+
'<div class="intel-grid">' + surfaceCards + '</div>' +
|
|
13746
14145
|
'<section class="intel-panel"><h2>Host capability</h2>' +
|
|
13747
14146
|
'<dl class="intel-hardware">' +
|
|
13748
14147
|
'<dt>Total RAM</dt><dd>' + escHtml(hardware.totalRamGb || "?") + ' GB</dd>' +
|
|
@@ -14535,6 +14934,13 @@ document.addEventListener("click", function (ev) {
|
|
|
14535
14934
|
const intelLocalModel = tgt.getAttribute("data-intel-local-model");
|
|
14536
14935
|
const intelFrontierProvider = tgt.getAttribute("data-intel-frontier-provider");
|
|
14537
14936
|
if (action === "lockdown") return void onLockdownClick();
|
|
14937
|
+
if (action === "theme-toggle") {
|
|
14938
|
+
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
|
14939
|
+
const next = isDark ? "light" : "dark";
|
|
14940
|
+
sessionStorage.setItem(THEME_KEY, next);
|
|
14941
|
+
applyTheme(next);
|
|
14942
|
+
return;
|
|
14943
|
+
}
|
|
14538
14944
|
if (action === "intel-reload") { return void fetchIntelligenceState().then(rerender); }
|
|
14539
14945
|
if (action === "intel-picker-open" && intelSurface) return void onIntelPickerOpen(intelSurface);
|
|
14540
14946
|
if (action === "intel-picker-close") return onIntelPickerClose();
|
|
@@ -14688,12 +15094,30 @@ document.addEventListener("input", function (ev) {
|
|
|
14688
15094
|
// then the dashboard view renders a static welcome card with no form
|
|
14689
15095
|
// inputs that could be confused for a working command surface.
|
|
14690
15096
|
|
|
14691
|
-
// Theme:
|
|
15097
|
+
// Theme: explicit operator preference (sessionStorage) overrides system
|
|
15098
|
+
// pref. The toggle button in the topbar dispatches data-action
|
|
15099
|
+
// "theme-toggle" which writes the chosen theme and updates the
|
|
15100
|
+
// [data-theme] attribute. When no explicit choice exists, fall back to
|
|
15101
|
+
// system preference and track changes so dark-mode-at-sunset behavior
|
|
15102
|
+
// keeps working on macOS / Windows.
|
|
15103
|
+
const THEME_KEY = "sanctuary-v11-theme";
|
|
15104
|
+
function applyTheme(theme) {
|
|
15105
|
+
if (theme === "dark") document.documentElement.setAttribute("data-theme", "dark");
|
|
15106
|
+
else document.documentElement.removeAttribute("data-theme");
|
|
15107
|
+
}
|
|
15108
|
+
const explicitTheme = sessionStorage.getItem(THEME_KEY);
|
|
14692
15109
|
const mq = window.matchMedia ? window.matchMedia("(prefers-color-scheme: dark)") : null;
|
|
14693
|
-
if (
|
|
15110
|
+
if (explicitTheme === "dark" || explicitTheme === "light") {
|
|
15111
|
+
applyTheme(explicitTheme);
|
|
15112
|
+
} else if (mq && mq.matches) {
|
|
15113
|
+
applyTheme("dark");
|
|
15114
|
+
}
|
|
14694
15115
|
if (mq) mq.addEventListener("change", function (e) {
|
|
14695
|
-
|
|
14696
|
-
|
|
15116
|
+
// Only honor system pref changes when the operator has not made an
|
|
15117
|
+
// explicit choice. Once they toggle, the choice sticks for the
|
|
15118
|
+
// session.
|
|
15119
|
+
if (sessionStorage.getItem(THEME_KEY)) return;
|
|
15120
|
+
applyTheme(e.matches ? "dark" : "light");
|
|
14697
15121
|
});
|
|
14698
15122
|
|
|
14699
15123
|
// Boot.
|
|
@@ -14717,9 +15141,11 @@ function renderDashboardV11Html(options = {}) {
|
|
|
14717
15141
|
const fortressId = options.fortressId ?? "fortress";
|
|
14718
15142
|
const sanctuaryVersion = options.sanctuaryVersion ?? SANCTUARY_VERSION;
|
|
14719
15143
|
const embedClient = options.embedClient !== false;
|
|
14720
|
-
const nav = NAV_ITEMS.map(
|
|
14721
|
-
|
|
14722
|
-
|
|
15144
|
+
const nav = NAV_ITEMS.map((n) => {
|
|
15145
|
+
const iconPath = NAV_ICON_PATHS[n.id] ?? "";
|
|
15146
|
+
const icon = iconPath ? SVG_OPEN + iconPath + "</svg>" : "";
|
|
15147
|
+
return `<a href="#${n.id}" data-route="${n.id}">${icon}<span>${escHtml3(n.label)}</span></a>`;
|
|
15148
|
+
}).join("\n ");
|
|
14723
15149
|
const config = JSON.stringify({
|
|
14724
15150
|
authToken,
|
|
14725
15151
|
hubApiBase,
|
|
@@ -14751,8 +15177,12 @@ function renderDashboardV11Html(options = {}) {
|
|
|
14751
15177
|
<span class="pill" data-pill="version">v${escHtml3(sanctuaryVersion)}</span>
|
|
14752
15178
|
<span class="pill" data-pill="deployment">deployment: local</span>
|
|
14753
15179
|
<span class="pill" data-pill="mode">mode: solo</span>
|
|
14754
|
-
<span class="
|
|
15180
|
+
<span class="att-global pending" data-pill="attestation" title="Fortress attestation"><span class="seal"><span class="seal-ring dashed"></span><span class="seal-core"></span></span><span class="label">pending</span></span>
|
|
14755
15181
|
</div>
|
|
15182
|
+
<button class="btn btn-icon" id="btn-theme-toggle" data-action="theme-toggle" aria-label="Toggle theme" title="Toggle theme">
|
|
15183
|
+
<span class="icon-moon">${THEME_ICON_MOON}</span>
|
|
15184
|
+
<span class="icon-sun">${THEME_ICON_SUN}</span>
|
|
15185
|
+
</button>
|
|
14756
15186
|
<button class="btn btn-danger" id="btn-lockdown" data-action="lockdown">Lockdown</button>
|
|
14757
15187
|
</header>
|
|
14758
15188
|
<main class="main" id="main"><p class="muted">Loading dashboard.</p></main>
|
|
@@ -14764,7 +15194,7 @@ function renderDashboardV11Html(options = {}) {
|
|
|
14764
15194
|
</body>
|
|
14765
15195
|
</html>`;
|
|
14766
15196
|
}
|
|
14767
|
-
var STYLES, NAV_ITEMS;
|
|
15197
|
+
var STYLES, NAV_ITEMS, NAV_ICON_PATHS, SVG_OPEN, THEME_ICON_MOON, THEME_ICON_SUN;
|
|
14768
15198
|
var init_html2 = __esm({
|
|
14769
15199
|
"src/dashboard/v1_1/html.ts"() {
|
|
14770
15200
|
init_client();
|
|
@@ -14795,6 +15225,26 @@ var init_html2 = __esm({
|
|
|
14795
15225
|
--rad: 6px;
|
|
14796
15226
|
--rad-lg: 10px;
|
|
14797
15227
|
--shadow: 0 1px 2px rgba(0,0,0,0.04), 0 0 0 1px rgba(0,0,0,0.02);
|
|
15228
|
+
/* Type scale. Names are size-relative, not semantic, so refactors do
|
|
15229
|
+
not need to invent new names. Existing rules used these literal px
|
|
15230
|
+
values; tokens make future polish a one-line change. */
|
|
15231
|
+
--text-xs: 11px;
|
|
15232
|
+
--text-sm: 12px;
|
|
15233
|
+
--text-base: 13px;
|
|
15234
|
+
--text-md: 14px;
|
|
15235
|
+
--text-lg: 16px;
|
|
15236
|
+
--text-xl: 22px;
|
|
15237
|
+
--text-display: 36px;
|
|
15238
|
+
/* Spacing scale (4px multiples). Layout-specific magic numbers
|
|
15239
|
+
(220px sidebar, 360px fortress rail, concierge-card heights) stay
|
|
15240
|
+
as literals because the token system is for component padding /
|
|
15241
|
+
margin / gap, not grid track sizing. */
|
|
15242
|
+
--space-1: 4px;
|
|
15243
|
+
--space-2: 8px;
|
|
15244
|
+
--space-3: 12px;
|
|
15245
|
+
--space-4: 16px;
|
|
15246
|
+
--space-5: 24px;
|
|
15247
|
+
--space-6: 32px;
|
|
14798
15248
|
}
|
|
14799
15249
|
[data-theme="dark"] {
|
|
14800
15250
|
--paper: #121210;
|
|
@@ -14821,7 +15271,7 @@ var init_html2 = __esm({
|
|
|
14821
15271
|
html, body { margin: 0; padding: 0; }
|
|
14822
15272
|
body {
|
|
14823
15273
|
font-family: var(--sans);
|
|
14824
|
-
font-size:
|
|
15274
|
+
font-size: var(--text-md);
|
|
14825
15275
|
background: var(--paper);
|
|
14826
15276
|
color: var(--ink);
|
|
14827
15277
|
line-height: 1.45;
|
|
@@ -14842,20 +15292,27 @@ body {
|
|
|
14842
15292
|
"sidebar main";
|
|
14843
15293
|
}
|
|
14844
15294
|
.sidebar { grid-area: sidebar; background: var(--paper-2); border-right: 1px solid var(--rule); padding: 12px 8px; }
|
|
14845
|
-
.sidebar h1 { font-family: var(--serif); font-size:
|
|
15295
|
+
.sidebar h1 { font-family: var(--serif); font-size: var(--text-lg); margin: 4px 8px 16px; }
|
|
14846
15296
|
.sidebar nav { display: flex; flex-direction: column; gap: 2px; }
|
|
14847
15297
|
.sidebar nav a {
|
|
14848
|
-
display:
|
|
14849
|
-
|
|
15298
|
+
display: flex; align-items: center; gap: var(--space-2);
|
|
15299
|
+
padding: 6px 10px; border-radius: var(--rad);
|
|
15300
|
+
color: var(--ink-2); text-decoration: none; font-size: var(--text-base);
|
|
15301
|
+
}
|
|
15302
|
+
.sidebar nav a svg {
|
|
15303
|
+
flex-shrink: 0; width: 16px; height: 16px;
|
|
15304
|
+
color: var(--ink-3);
|
|
14850
15305
|
}
|
|
14851
15306
|
.sidebar nav a:hover { background: var(--paper-3); }
|
|
15307
|
+
.sidebar nav a:hover svg { color: var(--ink-2); }
|
|
14852
15308
|
.sidebar nav a.active { background: var(--surface); color: var(--ink); border: 1px solid var(--rule); }
|
|
14853
|
-
.
|
|
14854
|
-
.topbar
|
|
15309
|
+
.sidebar nav a.active svg { color: var(--ink); }
|
|
15310
|
+
.topbar { grid-area: topbar; display: flex; align-items: center; gap: var(--space-3); padding: 0 16px; border-bottom: 1px solid var(--rule); background: var(--surface); }
|
|
15311
|
+
.topbar .brand { font-family: var(--serif); font-size: var(--text-md); }
|
|
14855
15312
|
.topbar .pills { display: flex; gap: 6px; flex: 1; }
|
|
14856
15313
|
.pill {
|
|
14857
15314
|
display: inline-flex; align-items: center; gap: 4px;
|
|
14858
|
-
padding: 2px 8px; border-radius: 12px; font-size:
|
|
15315
|
+
padding: 2px 8px; border-radius: 12px; font-size: var(--text-xs);
|
|
14859
15316
|
font-family: var(--mono); border: 1px solid var(--rule);
|
|
14860
15317
|
background: var(--surface-2); color: var(--ink-2);
|
|
14861
15318
|
}
|
|
@@ -14867,7 +15324,7 @@ body {
|
|
|
14867
15324
|
display: inline-flex; align-items: center; gap: 4px;
|
|
14868
15325
|
padding: 4px 10px; border-radius: var(--rad);
|
|
14869
15326
|
background: var(--surface); border: 1px solid var(--rule);
|
|
14870
|
-
font-family: var(--sans); font-size:
|
|
15327
|
+
font-family: var(--sans); font-size: var(--text-sm); color: var(--ink);
|
|
14871
15328
|
cursor: pointer;
|
|
14872
15329
|
}
|
|
14873
15330
|
.btn:hover:not(:disabled) { background: var(--surface-2); }
|
|
@@ -14877,21 +15334,30 @@ body {
|
|
|
14877
15334
|
.btn.btn-danger { background: var(--rust-bg); color: var(--rust); border-color: var(--rust); }
|
|
14878
15335
|
.btn.tier1-pending { background: var(--ochre-bg); color: var(--ochre); border-color: var(--ochre); }
|
|
14879
15336
|
.btn.tier1-engaged { background: var(--rust-bg); color: var(--rust); border-color: var(--rust); }
|
|
15337
|
+
.btn.btn-icon {
|
|
15338
|
+
padding: 4px 6px;
|
|
15339
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
15340
|
+
color: var(--ink-2);
|
|
15341
|
+
}
|
|
15342
|
+
.btn.btn-icon svg { width: 16px; height: 16px; }
|
|
15343
|
+
.btn.btn-icon .icon-sun { display: none; }
|
|
15344
|
+
[data-theme="dark"] .btn.btn-icon .icon-moon { display: none; }
|
|
15345
|
+
[data-theme="dark"] .btn.btn-icon .icon-sun { display: inline; }
|
|
14880
15346
|
.main { grid-area: main; overflow-y: auto; padding: 16px 24px; }
|
|
14881
|
-
.fortress { grid-area: fortress; overflow-y: auto; border-left: 1px solid var(--rule); background: var(--paper-2); padding:
|
|
15347
|
+
.fortress { grid-area: fortress; overflow-y: auto; border-left: 1px solid var(--rule); background: var(--paper-2); padding: var(--space-3); display: flex; flex-direction: column; gap: 10px; }
|
|
14882
15348
|
.app.route-full .fortress { display: none; }
|
|
14883
15349
|
.card {
|
|
14884
15350
|
background: var(--surface); border: 1px solid var(--rule);
|
|
14885
|
-
border-radius: var(--rad); padding:
|
|
15351
|
+
border-radius: var(--rad); padding: var(--space-3);
|
|
14886
15352
|
}
|
|
14887
|
-
.card h3 { margin: 0 0 8px; font-size:
|
|
15353
|
+
.card h3 { margin: 0 0 8px; font-size: var(--text-base); font-weight: 600; color: var(--ink); }
|
|
14888
15354
|
.muted { color: var(--ink-3); }
|
|
14889
|
-
.mono { font-family: var(--mono); font-size:
|
|
14890
|
-
.row { display: flex; align-items: center; gap:
|
|
15355
|
+
.mono { font-family: var(--mono); font-size: var(--text-sm); }
|
|
15356
|
+
.row { display: flex; align-items: center; gap: var(--space-2); padding: 6px 0; border-bottom: 1px dashed var(--rule); }
|
|
14891
15357
|
.row:last-child { border-bottom: 0; }
|
|
14892
15358
|
.row .grow { flex: 1; min-width: 0; }
|
|
14893
15359
|
.agent-row { flex-direction: column; align-items: stretch; gap: 6px; }
|
|
14894
|
-
.agent-row-head { display: flex; align-items: center; gap:
|
|
15360
|
+
.agent-row-head { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
|
|
14895
15361
|
.agent-row-head .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
14896
15362
|
.agent-row-actions { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
14897
15363
|
/* Click-to-inspect affordance: the head sub-row of a fortress-column
|
|
@@ -14901,7 +15367,7 @@ body {
|
|
|
14901
15367
|
.agent-row-head[data-action="agent-row-inspect-open"] { cursor: pointer; border-radius: var(--rad); padding: 4px 6px; margin: -4px -6px; }
|
|
14902
15368
|
.agent-row-head[data-action="agent-row-inspect-open"]:hover { background: var(--paper-3); }
|
|
14903
15369
|
.agent-row-head[data-action="agent-row-inspect-open"]:focus-visible { outline: 2px solid var(--ink-3); outline-offset: 1px; }
|
|
14904
|
-
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; font-size:
|
|
15370
|
+
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; font-size: var(--text-sm); }
|
|
14905
15371
|
.kv dt { color: var(--ink-3); }
|
|
14906
15372
|
.kv dd { margin: 0; color: var(--ink); }
|
|
14907
15373
|
.glyph { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--ink-4); }
|
|
@@ -14912,74 +15378,74 @@ body {
|
|
|
14912
15378
|
.toast {
|
|
14913
15379
|
position: fixed; bottom: 16px; right: 16px;
|
|
14914
15380
|
background: var(--ink); color: var(--paper); padding: 8px 12px;
|
|
14915
|
-
border-radius: var(--rad); font-size:
|
|
15381
|
+
border-radius: var(--rad); font-size: var(--text-sm); z-index: 1000;
|
|
14916
15382
|
max-width: 360px;
|
|
14917
15383
|
}
|
|
14918
15384
|
.toast.error { background: var(--rust); color: var(--paper); }
|
|
14919
|
-
.layer-card { background: var(--surface-2); border: 1px solid var(--rule); border-radius: var(--rad); padding:
|
|
14920
|
-
.layer-card h4 { margin: 0 0 4px; font-size:
|
|
14921
|
-
.layer-card p { margin: 0; font-size:
|
|
14922
|
-
.chat-thread { display: flex; flex-direction: column; gap:
|
|
15385
|
+
.layer-card { background: var(--surface-2); border: 1px solid var(--rule); border-radius: var(--rad); padding: var(--space-2); }
|
|
15386
|
+
.layer-card h4 { margin: 0 0 4px; font-size: var(--text-sm); font-weight: 600; }
|
|
15387
|
+
.layer-card p { margin: 0; font-size: var(--text-xs); color: var(--ink-3); }
|
|
15388
|
+
.chat-thread { display: flex; flex-direction: column; gap: var(--space-2); padding-bottom: 12px; }
|
|
14923
15389
|
.chat-msg { padding: 8px 10px; border-radius: var(--rad); border: 1px solid var(--rule); background: var(--surface); max-width: 78%; }
|
|
14924
|
-
.chat-msg.system { background: var(--paper-3); color: var(--ink-3); font-size:
|
|
15390
|
+
.chat-msg.system { background: var(--paper-3); color: var(--ink-3); font-size: var(--text-sm); max-width: 100%; }
|
|
14925
15391
|
.chat-msg.agent { align-self: flex-start; }
|
|
14926
15392
|
.chat-msg.operator { align-self: flex-end; background: var(--ink); color: var(--paper); }
|
|
14927
15393
|
.chat-msg .meta { font-size: 10px; color: var(--ink-4); margin-top: 4px; }
|
|
14928
|
-
.composer { display: flex; gap:
|
|
15394
|
+
.composer { display: flex; gap: var(--space-2); padding: var(--space-2); border-top: 1px solid var(--rule); }
|
|
14929
15395
|
.composer input { flex: 1; padding: 6px 8px; border: 1px solid var(--rule); border-radius: var(--rad); font-family: var(--sans); }
|
|
14930
15396
|
.wizard-step { padding: 10px; border: 1px solid var(--rule); border-radius: var(--rad); margin-bottom: 8px; background: var(--surface); }
|
|
14931
15397
|
.wizard-step.active { border-color: var(--ink); }
|
|
14932
15398
|
.wizard-step.done { background: var(--sage-bg); border-color: var(--sage); }
|
|
14933
|
-
.code-block { font-family: var(--mono); background: var(--paper-3); padding:
|
|
15399
|
+
.code-block { font-family: var(--mono); background: var(--paper-3); padding: var(--space-2); border-radius: var(--rad); font-size: var(--text-sm); overflow-x: auto; }
|
|
14934
15400
|
.policy-center { max-width: 980px; margin: 0 auto; }
|
|
14935
|
-
.policy-center .eyebrow { margin: 0 0 6px; color: var(--ink-3); font-family: var(--mono); font-size:
|
|
14936
|
-
.policy-center h1 { font-family: var(--serif); font-size:
|
|
15401
|
+
.policy-center .eyebrow { margin: 0 0 6px; color: var(--ink-3); font-family: var(--mono); font-size: var(--text-sm); letter-spacing: 0; }
|
|
15402
|
+
.policy-center h1 { font-family: var(--serif); font-size: var(--text-display); line-height: 1.08; font-weight: 400; margin: 0 0 10px; }
|
|
14937
15403
|
.policy-subtitle { max-width: 860px; color: var(--ink-2); font-size: 15px; margin: 0 0 24px; }
|
|
14938
15404
|
.policy-panel { background: var(--surface); border: 1px solid var(--rule); border-radius: var(--rad-lg); padding: 20px; margin: 18px 0; }
|
|
14939
|
-
.policy-panel h2 { font-family: var(--serif); font-size:
|
|
14940
|
-
.template-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap:
|
|
15405
|
+
.policy-panel h2 { font-family: var(--serif); font-size: var(--text-xl); font-weight: 400; margin: 0 0 14px; }
|
|
15406
|
+
.template-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
|
|
14941
15407
|
.template-card { background: var(--surface-2); border: 1px solid var(--rule); border-radius: var(--rad); padding: 14px; min-height: 132px; }
|
|
14942
|
-
.template-card-head { display: flex; justify-content: space-between; align-items: center; gap:
|
|
14943
|
-
.severity { border-radius: 999px; padding: 2px 9px; font-family: var(--mono); font-size:
|
|
15408
|
+
.template-card-head { display: flex; justify-content: space-between; align-items: center; gap: var(--space-2); margin-bottom: 12px; }
|
|
15409
|
+
.severity { border-radius: 999px; padding: 2px 9px; font-family: var(--mono); font-size: var(--text-xs); font-weight: 700; }
|
|
14944
15410
|
.severity.low { color: var(--sage); background: var(--sage-bg); }
|
|
14945
15411
|
.severity.medium { color: var(--ochre); background: var(--ochre-bg); }
|
|
14946
15412
|
.template-id { background: var(--paper-3); border-radius: var(--rad); padding: 2px 7px; color: var(--ink-3); }
|
|
14947
|
-
.template-card h3 { font-size:
|
|
14948
|
-
.template-card p { color: var(--ink-3); margin: 0; font-size:
|
|
15413
|
+
.template-card h3 { font-size: var(--text-lg); margin: 0 0 6px; }
|
|
15414
|
+
.template-card p { color: var(--ink-3); margin: 0; font-size: var(--text-md); }
|
|
14949
15415
|
.rules-scroll { overflow-x: auto; }
|
|
14950
15416
|
.rules-table { width: 100%; border-collapse: collapse; min-width: 760px; }
|
|
14951
|
-
.rules-table th { text-align: left; color: var(--ink-3); font-family: var(--mono); font-size:
|
|
15417
|
+
.rules-table th { text-align: left; color: var(--ink-3); font-family: var(--mono); font-size: var(--text-sm); letter-spacing: 0; padding: 8px 10px; border-bottom: 1px solid var(--rule); }
|
|
14952
15418
|
.rules-table td { padding: 12px 10px; border-bottom: 1px solid var(--rule); vertical-align: top; }
|
|
14953
15419
|
.link-btn, .template-cell { border: 0; background: transparent; color: var(--ink); padding: 0; cursor: pointer; font: inherit; text-align: left; }
|
|
14954
15420
|
.template-cell { font-family: var(--mono); max-width: 180px; overflow-wrap: anywhere; }
|
|
14955
15421
|
.template-picker { position: absolute; z-index: 20; margin-top: 8px; width: min(420px, calc(100vw - 80px)); background: var(--surface); border: 1px solid var(--rule-2); border-radius: var(--rad); box-shadow: var(--shadow); padding: 10px; }
|
|
14956
15422
|
.template-picker-options { display: grid; gap: 6px; max-height: 320px; overflow-y: auto; }
|
|
14957
|
-
.template-option { display: grid; grid-template-columns: 18px 1fr; gap:
|
|
15423
|
+
.template-option { display: grid; grid-template-columns: 18px 1fr; gap: var(--space-2); padding: var(--space-2); border: 1px solid var(--rule); border-radius: var(--rad); background: var(--surface-2); }
|
|
14958
15424
|
.template-option small { display: block; color: var(--ink-3); margin-top: 2px; }
|
|
14959
|
-
.template-picker-actions { display: flex; justify-content: flex-end; gap:
|
|
15425
|
+
.template-picker-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: 10px; }
|
|
14960
15426
|
.allow-count { color: var(--sage); font-weight: 700; }
|
|
14961
15427
|
.block-count { color: var(--rust); }
|
|
14962
15428
|
.toggle-on { display: inline-block; width: 28px; height: 16px; border-radius: 999px; background: var(--sage); position: relative; }
|
|
14963
15429
|
.toggle-on::after { content: ""; position: absolute; right: 2px; top: 2px; width: 12px; height: 12px; border-radius: 50%; background: var(--surface); }
|
|
14964
15430
|
.error-text { color: var(--rust); margin: 8px 0 0; }
|
|
14965
15431
|
.intel-center { max-width: 980px; margin: 0 auto; }
|
|
14966
|
-
.intel-center .eyebrow { margin: 0 0 6px; color: var(--ink-3); font-family: var(--mono); font-size:
|
|
14967
|
-
.intel-center h1 { font-family: var(--serif); font-size:
|
|
15432
|
+
.intel-center .eyebrow { margin: 0 0 6px; color: var(--ink-3); font-family: var(--mono); font-size: var(--text-sm); letter-spacing: 0; }
|
|
15433
|
+
.intel-center h1 { font-family: var(--serif); font-size: var(--text-display); line-height: 1.08; font-weight: 400; margin: 0 0 10px; }
|
|
14968
15434
|
.intel-subtitle { max-width: 860px; color: var(--ink-2); font-size: 15px; margin: 0 0 24px; }
|
|
14969
15435
|
.intel-panel { background: var(--surface); border: 1px solid var(--rule); border-radius: var(--rad-lg); padding: 20px; margin: 18px 0; }
|
|
14970
|
-
.intel-panel h2 { font-family: var(--serif); font-size:
|
|
14971
|
-
.intel-row { display: grid; grid-template-columns: 200px 1fr auto; gap:
|
|
15436
|
+
.intel-panel h2 { font-family: var(--serif); font-size: var(--text-xl); font-weight: 400; margin: 0 0 14px; }
|
|
15437
|
+
.intel-row { display: grid; grid-template-columns: 200px 1fr auto; gap: var(--space-4); padding: 14px 0; border-bottom: 1px solid var(--rule); align-items: start; }
|
|
14972
15438
|
.intel-row:last-child { border-bottom: 0; }
|
|
14973
15439
|
.intel-row-name { font-weight: 600; }
|
|
14974
|
-
.intel-row-name small { display: block; color: var(--ink-3); font-weight: 400; font-size:
|
|
15440
|
+
.intel-row-name small { display: block; color: var(--ink-3); font-weight: 400; font-size: var(--text-sm); margin-top: 2px; font-family: var(--mono); }
|
|
14975
15441
|
.intel-row-body { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
14976
|
-
.intel-row-current { display: flex; gap:
|
|
14977
|
-
.intel-row-tradeoff { color: var(--ink-2); font-size:
|
|
15442
|
+
.intel-row-current { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
|
|
15443
|
+
.intel-row-tradeoff { color: var(--ink-2); font-size: var(--text-base); line-height: 1.5; }
|
|
14978
15444
|
.intel-status-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; }
|
|
14979
15445
|
.intel-status-dot.green { background: var(--sage); }
|
|
14980
15446
|
.intel-status-dot.yellow { background: var(--ochre); }
|
|
14981
15447
|
.intel-status-dot.red { background: var(--rust); }
|
|
14982
|
-
.intel-hardware { display: grid; grid-template-columns: max-content 1fr; gap: 4px 16px; font-size:
|
|
15448
|
+
.intel-hardware { display: grid; grid-template-columns: max-content 1fr; gap: 4px 16px; font-size: var(--text-base); }
|
|
14983
15449
|
.intel-hardware dt { color: var(--ink-3); font-family: var(--mono); }
|
|
14984
15450
|
.intel-hardware dd { margin: 0; }
|
|
14985
15451
|
.intel-modal-backdrop {
|
|
@@ -14992,33 +15458,162 @@ body {
|
|
|
14992
15458
|
box-shadow: 0 8px 32px rgba(0,0,0,0.18); padding: 24px;
|
|
14993
15459
|
width: 100%; max-width: 640px;
|
|
14994
15460
|
}
|
|
14995
|
-
.intel-modal h2 { font-family: var(--serif); font-size:
|
|
14996
|
-
.intel-modal-subtitle { color: var(--ink-3); margin: 0 0 18px; font-size:
|
|
15461
|
+
.intel-modal h2 { font-family: var(--serif); font-size: var(--text-xl); font-weight: 400; margin: 0 0 8px; }
|
|
15462
|
+
.intel-modal-subtitle { color: var(--ink-3); margin: 0 0 18px; font-size: var(--text-base); }
|
|
14997
15463
|
.intel-option {
|
|
14998
|
-
border: 1px solid var(--rule); border-radius: var(--rad); padding:
|
|
15464
|
+
border: 1px solid var(--rule); border-radius: var(--rad); padding: var(--space-3);
|
|
14999
15465
|
margin-bottom: 10px; background: var(--surface-2); cursor: pointer;
|
|
15000
15466
|
display: grid; grid-template-columns: 18px 1fr; gap: 10px; align-items: start;
|
|
15001
15467
|
}
|
|
15002
15468
|
.intel-option.selected { border-color: var(--ink); background: var(--surface); }
|
|
15003
|
-
.intel-option-body strong { display: block; font-size:
|
|
15004
|
-
.intel-option-body small { display: block; color: var(--ink-3); font-size:
|
|
15469
|
+
.intel-option-body strong { display: block; font-size: var(--text-md); margin-bottom: 4px; }
|
|
15470
|
+
.intel-option-body small { display: block; color: var(--ink-3); font-size: var(--text-sm); line-height: 1.5; }
|
|
15005
15471
|
.intel-suboptions { margin-top: 10px; padding: 10px; background: var(--paper-3); border-radius: var(--rad); }
|
|
15006
|
-
.intel-suboptions label { display: block; margin: 6px 0; font-size:
|
|
15472
|
+
.intel-suboptions label { display: block; margin: 6px 0; font-size: var(--text-base); }
|
|
15007
15473
|
.intel-suboptions input[type="text"], .intel-suboptions input[type="password"] {
|
|
15008
15474
|
width: 100%; padding: 6px 8px; border: 1px solid var(--rule); border-radius: var(--rad);
|
|
15009
|
-
font-family: var(--mono); font-size:
|
|
15475
|
+
font-family: var(--mono); font-size: var(--text-sm); box-sizing: border-box;
|
|
15476
|
+
}
|
|
15477
|
+
.intel-modal-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: 18px; }
|
|
15478
|
+
/*
|
|
15479
|
+
* Intelligence panel polish (Sprint Piece 2 PR 3). Card grid layout
|
|
15480
|
+
* replaces the legacy 3-col row visual. The legacy .intel-row and
|
|
15481
|
+
* .intel-status-dot rules above are retained for the e2e selector
|
|
15482
|
+
* contract (.intel-row[data-intel-surface="..."]) and as the responsive
|
|
15483
|
+
* fallback. Cards render as a flex column with a substrate inset,
|
|
15484
|
+
* status badge with shaped glyph, and a recent-failures toggle in the
|
|
15485
|
+
* card foot.
|
|
15486
|
+
*/
|
|
15487
|
+
.intel-wrap { max-width: 1000px; margin: 0 auto; }
|
|
15488
|
+
.intel-grid {
|
|
15489
|
+
display: grid;
|
|
15490
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
15491
|
+
gap: 12px;
|
|
15492
|
+
}
|
|
15493
|
+
.intel-card {
|
|
15494
|
+
background: var(--surface);
|
|
15495
|
+
border: 1px solid var(--rule);
|
|
15496
|
+
border-radius: var(--rad-lg);
|
|
15497
|
+
padding: 16px;
|
|
15498
|
+
display: flex; flex-direction: column;
|
|
15499
|
+
gap: 12px;
|
|
15500
|
+
}
|
|
15501
|
+
.intel-card-head {
|
|
15502
|
+
display: flex; align-items: flex-start; justify-content: space-between;
|
|
15503
|
+
gap: 10px;
|
|
15010
15504
|
}
|
|
15011
|
-
.intel-
|
|
15505
|
+
.intel-card-name {
|
|
15506
|
+
display: flex; flex-direction: column; gap: 2px;
|
|
15507
|
+
min-width: 0;
|
|
15508
|
+
}
|
|
15509
|
+
.intel-card-name strong {
|
|
15510
|
+
font-family: var(--serif); font-weight: 500;
|
|
15511
|
+
font-size: 15px; letter-spacing: 0.005em;
|
|
15512
|
+
}
|
|
15513
|
+
.intel-card-name small {
|
|
15514
|
+
color: var(--ink-3); font-size: 11px; font-family: var(--mono);
|
|
15515
|
+
}
|
|
15516
|
+
.intel-card-status {
|
|
15517
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
15518
|
+
font-family: var(--mono); font-size: 11px;
|
|
15519
|
+
padding: 3px 9px; border-radius: 999px;
|
|
15520
|
+
border: 1px solid var(--rule); background: var(--surface-2);
|
|
15521
|
+
flex-shrink: 0;
|
|
15522
|
+
}
|
|
15523
|
+
.intel-card-status.ok { color: var(--sage); border-color: var(--sage); background: var(--sage-bg); }
|
|
15524
|
+
.intel-card-status.warn { color: var(--ochre); border-color: var(--ochre); background: var(--ochre-bg); }
|
|
15525
|
+
.intel-card-status.fail { color: var(--rust); border-color: var(--rust); background: var(--rust-bg); }
|
|
15526
|
+
.status-glyph {
|
|
15527
|
+
width: 10px; height: 10px;
|
|
15528
|
+
position: relative; flex-shrink: 0;
|
|
15529
|
+
}
|
|
15530
|
+
.status-glyph.ok::before {
|
|
15531
|
+
content: ""; position: absolute; inset: 0;
|
|
15532
|
+
border-radius: 50%; background: currentColor;
|
|
15533
|
+
}
|
|
15534
|
+
.status-glyph.warn::before {
|
|
15535
|
+
content: ""; position: absolute; inset: 0;
|
|
15536
|
+
background: currentColor;
|
|
15537
|
+
clip-path: polygon(50% 0%, 100% 100%, 0% 100%);
|
|
15538
|
+
}
|
|
15539
|
+
.status-glyph.fail::before {
|
|
15540
|
+
content: ""; position: absolute; inset: 1px;
|
|
15541
|
+
background: currentColor;
|
|
15542
|
+
clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
|
|
15543
|
+
}
|
|
15544
|
+
.intel-substrate {
|
|
15545
|
+
display: flex; flex-direction: column; gap: 4px;
|
|
15546
|
+
padding: 10px 12px;
|
|
15547
|
+
background: var(--paper-2);
|
|
15548
|
+
border: 1px solid var(--rule);
|
|
15549
|
+
border-radius: var(--rad);
|
|
15550
|
+
}
|
|
15551
|
+
.intel-substrate .sub-line {
|
|
15552
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
15553
|
+
gap: 10px; font-size: 12px;
|
|
15554
|
+
}
|
|
15555
|
+
.intel-substrate .sub-line.primary {
|
|
15556
|
+
font-family: var(--mono); font-size: 13px; color: var(--ink);
|
|
15557
|
+
}
|
|
15558
|
+
.intel-substrate .sub-line.secondary { color: var(--ink-3); font-size: 11px; }
|
|
15559
|
+
.intel-card-foot {
|
|
15560
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
15561
|
+
gap: 8px; padding-top: 4px;
|
|
15562
|
+
}
|
|
15563
|
+
.intel-failures-toggle {
|
|
15564
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
15565
|
+
font-size: 12px; font-family: var(--mono);
|
|
15566
|
+
color: var(--ink-3);
|
|
15567
|
+
background: transparent; border: 0; padding: 0;
|
|
15568
|
+
cursor: pointer;
|
|
15569
|
+
}
|
|
15570
|
+
.intel-failures-toggle:hover { color: var(--ink); }
|
|
15571
|
+
.intel-failures-toggle .caret {
|
|
15572
|
+
display: inline-block; width: 0; height: 0;
|
|
15573
|
+
border-left: 4px solid transparent;
|
|
15574
|
+
border-right: 4px solid transparent;
|
|
15575
|
+
border-top: 5px solid currentColor;
|
|
15576
|
+
transition: transform 160ms ease;
|
|
15577
|
+
}
|
|
15578
|
+
.intel-failures-toggle.open .caret { transform: rotate(180deg); }
|
|
15579
|
+
.intel-failures {
|
|
15580
|
+
border-top: 1px solid var(--rule);
|
|
15581
|
+
padding-top: 12px;
|
|
15582
|
+
display: flex; flex-direction: column;
|
|
15583
|
+
gap: 8px;
|
|
15584
|
+
}
|
|
15585
|
+
.intel-failure-row {
|
|
15586
|
+
display: grid; grid-template-columns: 88px 1fr; gap: 12px;
|
|
15587
|
+
font-size: 12px; padding: 8px 10px;
|
|
15588
|
+
border-radius: var(--rad);
|
|
15589
|
+
background: var(--paper-2);
|
|
15590
|
+
border: 1px solid var(--rule);
|
|
15591
|
+
}
|
|
15592
|
+
.intel-failure-row .ts {
|
|
15593
|
+
font-family: var(--mono); font-size: 11px; color: var(--ink-3);
|
|
15594
|
+
}
|
|
15595
|
+
.intel-failure-row .err-class {
|
|
15596
|
+
font-family: var(--mono); font-size: 10px;
|
|
15597
|
+
letter-spacing: 0.04em; text-transform: uppercase;
|
|
15598
|
+
color: var(--rust); margin-bottom: 2px;
|
|
15599
|
+
}
|
|
15600
|
+
.btn-quiet {
|
|
15601
|
+
background: transparent; border: 1px solid var(--rule);
|
|
15602
|
+
padding: 2px 6px; font-size: 11px;
|
|
15603
|
+
border-radius: var(--rad); cursor: pointer;
|
|
15604
|
+
color: var(--ink-2); font-family: var(--sans);
|
|
15605
|
+
}
|
|
15606
|
+
.btn-quiet:hover { background: var(--surface-2); color: var(--ink); }
|
|
15012
15607
|
.banner-warn {
|
|
15013
15608
|
background: var(--ochre-bg); color: var(--ochre); border: 1px solid var(--ochre);
|
|
15014
|
-
border-radius: var(--rad); padding: 8px 12px; margin: 8px 0; font-size:
|
|
15609
|
+
border-radius: var(--rad); padding: 8px 12px; margin: 8px 0; font-size: var(--text-base);
|
|
15015
15610
|
}
|
|
15016
15611
|
.banner-info {
|
|
15017
15612
|
background: var(--indigo-bg); color: var(--indigo); border: 1px solid var(--indigo);
|
|
15018
|
-
border-radius: var(--rad); padding: 8px 12px; margin: 8px 0; font-size:
|
|
15613
|
+
border-radius: var(--rad); padding: 8px 12px; margin: 8px 0; font-size: var(--text-base);
|
|
15019
15614
|
}
|
|
15020
15615
|
.btn.chip {
|
|
15021
|
-
border-radius: 999px; padding: 4px 12px; font-size:
|
|
15616
|
+
border-radius: 999px; padding: 4px 12px; font-size: var(--text-sm);
|
|
15022
15617
|
background: var(--surface-2); border-color: var(--rule);
|
|
15023
15618
|
}
|
|
15024
15619
|
.btn.chip:hover:not(:disabled) { background: var(--paper-3); }
|
|
@@ -15035,85 +15630,210 @@ body {
|
|
|
15035
15630
|
* dynamically, but the input box is below the fold, so I still have
|
|
15036
15631
|
* to scroll." rc.5 closes that with a structural layout fix.
|
|
15037
15632
|
*/
|
|
15633
|
+
/* Sprint Piece 2 PR 2 (2026-05-03): concierge surface polish.
|
|
15634
|
+
* Translates Claude Design references at
|
|
15635
|
+
* server/docs/design-refs/sprint-piece-2/surface-concierge.jsx and the
|
|
15636
|
+
* Surface 1 block of surfaces.css. The bounded-card layout above is
|
|
15637
|
+
* preserved verbatim because rc.5 and the DDD e2e suite depend on it;
|
|
15638
|
+
* the Claude Design reference uses height: 720px for the card, but
|
|
15639
|
+
* production keeps the calc-based bounded height so the card adapts
|
|
15640
|
+
* to the operator viewport. The polish lands the persona glyph-ring,
|
|
15641
|
+
* mono uppercase author labels, paper-2 background for concierge
|
|
15642
|
+
* replies, suggest-grid empty-state cards, and the composer input-wrap
|
|
15643
|
+
* with the keyboard-shortcut pill.
|
|
15644
|
+
*/
|
|
15645
|
+
.concierge-wrap { max-width: 880px; margin: 0 auto; }
|
|
15646
|
+
.page-head {
|
|
15647
|
+
display: flex; align-items: flex-end; justify-content: space-between;
|
|
15648
|
+
gap: var(--space-4);
|
|
15649
|
+
margin-bottom: 18px; padding-bottom: 14px;
|
|
15650
|
+
border-bottom: 1px solid var(--rule);
|
|
15651
|
+
}
|
|
15652
|
+
.page-head .eyebrow {
|
|
15653
|
+
font-family: var(--mono); font-size: var(--text-xs);
|
|
15654
|
+
letter-spacing: 0.08em; text-transform: uppercase;
|
|
15655
|
+
color: var(--ink-3);
|
|
15656
|
+
margin: 0 0 6px;
|
|
15657
|
+
}
|
|
15658
|
+
.page-head h1 {
|
|
15659
|
+
font-family: var(--serif); font-weight: 400;
|
|
15660
|
+
font-size: 28px; letter-spacing: -0.01em;
|
|
15661
|
+
margin: 0 0 4px;
|
|
15662
|
+
}
|
|
15663
|
+
.page-head .sub {
|
|
15664
|
+
color: var(--ink-3); margin: 0;
|
|
15665
|
+
font-size: var(--text-base); max-width: 60ch;
|
|
15666
|
+
}
|
|
15038
15667
|
.concierge-card {
|
|
15039
15668
|
display: flex; flex-direction: column;
|
|
15040
15669
|
height: calc(100vh - 180px);
|
|
15041
15670
|
max-height: calc(100vh - 180px);
|
|
15042
15671
|
min-height: 360px;
|
|
15043
|
-
padding:
|
|
15672
|
+
padding: 18px 22px 14px;
|
|
15044
15673
|
gap: 0;
|
|
15045
15674
|
}
|
|
15046
15675
|
.concierge-header {
|
|
15047
15676
|
display: flex; align-items: center; justify-content: space-between;
|
|
15048
|
-
gap:
|
|
15677
|
+
gap: var(--space-3); padding-bottom: 14px;
|
|
15678
|
+
border-bottom: 1px solid var(--rule);
|
|
15049
15679
|
flex-wrap: wrap;
|
|
15050
15680
|
flex-shrink: 0;
|
|
15051
15681
|
}
|
|
15052
|
-
.concierge-persona {
|
|
15053
|
-
|
|
15054
|
-
|
|
15682
|
+
.concierge-persona { display: flex; align-items: center; gap: 10px; }
|
|
15683
|
+
.concierge-persona .glyph-ring {
|
|
15684
|
+
width: 26px; height: 26px;
|
|
15685
|
+
border: 1.5px solid var(--ink-2);
|
|
15686
|
+
border-radius: 50%;
|
|
15687
|
+
position: relative;
|
|
15688
|
+
flex-shrink: 0;
|
|
15689
|
+
}
|
|
15690
|
+
.concierge-persona .glyph-ring::after {
|
|
15691
|
+
content: ""; position: absolute; inset: 5px;
|
|
15692
|
+
border-radius: 50%; background: var(--ink-2);
|
|
15693
|
+
}
|
|
15694
|
+
.concierge-persona-text { display: flex; flex-direction: column; }
|
|
15695
|
+
.concierge-persona-text strong {
|
|
15696
|
+
font-family: var(--serif); font-weight: 500;
|
|
15697
|
+
font-size: 15px; letter-spacing: 0.005em;
|
|
15698
|
+
}
|
|
15699
|
+
.concierge-persona-text small {
|
|
15700
|
+
color: var(--ink-3); font-size: var(--text-xs);
|
|
15701
|
+
font-family: var(--mono);
|
|
15702
|
+
}
|
|
15703
|
+
.concierge-meta {
|
|
15704
|
+
display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
|
|
15055
15705
|
}
|
|
15056
|
-
.concierge-persona strong { font-size: 14px; }
|
|
15057
15706
|
.concierge-badge { white-space: nowrap; }
|
|
15058
15707
|
.concierge-history {
|
|
15059
15708
|
flex: 1 1 auto;
|
|
15060
15709
|
min-height: 0;
|
|
15061
15710
|
overflow-y: auto;
|
|
15062
|
-
padding:
|
|
15063
|
-
display: flex; flex-direction: column; gap:
|
|
15711
|
+
padding: 18px 4px 6px;
|
|
15712
|
+
display: flex; flex-direction: column; gap: 18px;
|
|
15064
15713
|
}
|
|
15065
15714
|
.concierge-msg {
|
|
15066
|
-
display: flex; flex-direction: column; gap:
|
|
15067
|
-
max-width:
|
|
15715
|
+
display: flex; flex-direction: column; gap: 5px;
|
|
15716
|
+
max-width: 78%;
|
|
15068
15717
|
}
|
|
15069
15718
|
.concierge-msg-author {
|
|
15070
|
-
font-size:
|
|
15719
|
+
font-size: 10px;
|
|
15720
|
+
font-family: var(--mono);
|
|
15721
|
+
letter-spacing: 0.04em;
|
|
15722
|
+
text-transform: uppercase;
|
|
15723
|
+
color: var(--ink-4);
|
|
15071
15724
|
}
|
|
15072
15725
|
.concierge-msg-body {
|
|
15073
|
-
padding:
|
|
15074
|
-
border:
|
|
15075
|
-
|
|
15076
|
-
|
|
15726
|
+
padding: 11px 14px;
|
|
15727
|
+
border-radius: 12px;
|
|
15728
|
+
border: 1px solid var(--rule);
|
|
15729
|
+
background: var(--surface);
|
|
15730
|
+
font-size: var(--text-md);
|
|
15731
|
+
line-height: 1.55;
|
|
15732
|
+
white-space: pre-wrap;
|
|
15733
|
+
word-wrap: break-word;
|
|
15077
15734
|
}
|
|
15078
15735
|
.concierge-msg-concierge { align-self: flex-start; }
|
|
15736
|
+
.concierge-msg-concierge .concierge-msg-body {
|
|
15737
|
+
background: var(--paper-2);
|
|
15738
|
+
}
|
|
15079
15739
|
.concierge-msg-operator { align-self: flex-end; align-items: flex-end; }
|
|
15080
15740
|
.concierge-msg-operator .concierge-msg-body {
|
|
15081
15741
|
background: var(--ink); color: var(--paper); border-color: var(--ink);
|
|
15082
15742
|
}
|
|
15743
|
+
.concierge-msg-meta {
|
|
15744
|
+
display: flex; gap: 6px;
|
|
15745
|
+
font-size: 10px;
|
|
15746
|
+
color: var(--ink-4);
|
|
15747
|
+
font-family: var(--mono);
|
|
15748
|
+
}
|
|
15083
15749
|
.concierge-empty {
|
|
15084
|
-
|
|
15085
|
-
|
|
15750
|
+
flex: 1 1 auto;
|
|
15751
|
+
display: flex; flex-direction: column; gap: 22px;
|
|
15752
|
+
justify-content: center;
|
|
15753
|
+
padding: 24px 12px;
|
|
15754
|
+
}
|
|
15755
|
+
.concierge-empty-headline { max-width: 52ch; }
|
|
15756
|
+
.concierge-empty-headline h2 {
|
|
15757
|
+
font-family: var(--serif); font-weight: 400;
|
|
15758
|
+
font-size: var(--text-xl); margin: 0 0 6px;
|
|
15759
|
+
letter-spacing: -0.005em;
|
|
15760
|
+
}
|
|
15761
|
+
.concierge-empty-headline p {
|
|
15762
|
+
color: var(--ink-3); margin: 0;
|
|
15763
|
+
font-size: var(--text-md); line-height: 1.55;
|
|
15764
|
+
}
|
|
15765
|
+
.concierge-suggest-grid {
|
|
15766
|
+
display: grid;
|
|
15767
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
15768
|
+
gap: 10px;
|
|
15769
|
+
}
|
|
15770
|
+
.concierge-suggest {
|
|
15771
|
+
background: var(--surface);
|
|
15772
|
+
border: 1px solid var(--rule);
|
|
15773
|
+
border-radius: var(--rad);
|
|
15774
|
+
padding: 12px 14px;
|
|
15775
|
+
font-size: var(--text-base);
|
|
15776
|
+
cursor: pointer;
|
|
15777
|
+
display: flex; flex-direction: column;
|
|
15778
|
+
gap: 6px;
|
|
15779
|
+
text-align: left;
|
|
15780
|
+
font-family: var(--sans);
|
|
15781
|
+
color: var(--ink);
|
|
15782
|
+
}
|
|
15783
|
+
.concierge-suggest:hover:not(:disabled) {
|
|
15784
|
+
background: var(--surface-2);
|
|
15785
|
+
border-color: var(--rule-2);
|
|
15786
|
+
}
|
|
15787
|
+
.concierge-suggest:disabled {
|
|
15788
|
+
cursor: not-allowed; color: var(--ink-4); opacity: 0.7;
|
|
15789
|
+
}
|
|
15790
|
+
.concierge-suggest .label {
|
|
15791
|
+
font-family: var(--mono);
|
|
15792
|
+
font-size: 10px;
|
|
15793
|
+
letter-spacing: 0.06em;
|
|
15794
|
+
text-transform: uppercase;
|
|
15795
|
+
color: var(--ink-3);
|
|
15086
15796
|
}
|
|
15087
15797
|
.concierge-composer {
|
|
15088
15798
|
display: flex; gap: 10px; align-items: center;
|
|
15089
|
-
padding: 12px 0
|
|
15799
|
+
padding: 12px 0 4px;
|
|
15090
15800
|
border-top: 1px solid var(--rule);
|
|
15091
15801
|
flex-shrink: 0;
|
|
15092
15802
|
}
|
|
15803
|
+
.concierge-composer .input-wrap {
|
|
15804
|
+
flex: 1;
|
|
15805
|
+
display: flex; align-items: center; gap: var(--space-2);
|
|
15806
|
+
padding: 8px 12px;
|
|
15807
|
+
border: 1px solid var(--rule);
|
|
15808
|
+
border-radius: var(--rad);
|
|
15809
|
+
background: var(--surface);
|
|
15810
|
+
}
|
|
15811
|
+
.concierge-composer .input-wrap:focus-within {
|
|
15812
|
+
border-color: var(--ink-3);
|
|
15813
|
+
}
|
|
15093
15814
|
.concierge-composer input {
|
|
15094
15815
|
flex: 1; min-width: 0;
|
|
15095
|
-
padding:
|
|
15096
|
-
border:
|
|
15097
|
-
|
|
15098
|
-
|
|
15816
|
+
padding: 4px 0;
|
|
15817
|
+
border: 0;
|
|
15818
|
+
background: transparent;
|
|
15819
|
+
color: var(--ink);
|
|
15820
|
+
font-family: var(--sans);
|
|
15821
|
+
font-size: var(--text-md);
|
|
15822
|
+
outline: none;
|
|
15099
15823
|
}
|
|
15100
|
-
.concierge-composer input:
|
|
15101
|
-
|
|
15824
|
+
.concierge-composer input::placeholder { color: var(--ink-4); }
|
|
15825
|
+
.concierge-composer .composer-meta {
|
|
15826
|
+
font-family: var(--mono);
|
|
15827
|
+
font-size: 10px;
|
|
15828
|
+
color: var(--ink-4);
|
|
15829
|
+
letter-spacing: 0.04em;
|
|
15102
15830
|
}
|
|
15103
15831
|
.concierge-composer .btn-primary {
|
|
15104
|
-
padding: 8px 18px; font-size:
|
|
15105
|
-
}
|
|
15106
|
-
.concierge-chips {
|
|
15107
|
-
display: flex; flex-wrap: wrap; gap: 6px;
|
|
15108
|
-
padding: 10px 0 0;
|
|
15109
|
-
}
|
|
15110
|
-
.concierge-chips::before {
|
|
15111
|
-
content: "Try:"; color: var(--ink-3); font-size: 12px;
|
|
15112
|
-
align-self: center; margin-right: 4px;
|
|
15832
|
+
padding: 8px 18px; font-size: var(--text-base); flex-shrink: 0;
|
|
15113
15833
|
}
|
|
15114
15834
|
.concierge-foot {
|
|
15115
15835
|
margin: 12px 0 0; padding-top: 10px; border-top: 1px dashed var(--rule);
|
|
15116
|
-
font-size:
|
|
15836
|
+
font-size: var(--text-sm);
|
|
15117
15837
|
}
|
|
15118
15838
|
.concierge-foot a { color: var(--ink-2); }
|
|
15119
15839
|
.tier1-approval-card {
|
|
@@ -15121,20 +15841,532 @@ body {
|
|
|
15121
15841
|
border-radius: var(--rad); padding: 14px 16px; margin: 12px 0;
|
|
15122
15842
|
}
|
|
15123
15843
|
.tier1-approval-card h3 {
|
|
15124
|
-
margin: 0 0 8px; color: var(--ochre); font-size:
|
|
15844
|
+
margin: 0 0 8px; color: var(--ochre); font-size: var(--text-md);
|
|
15125
15845
|
}
|
|
15126
|
-
.tier1-approval-card p { margin: 0 0 12px; font-size:
|
|
15846
|
+
.tier1-approval-card p { margin: 0 0 12px; font-size: var(--text-base); }
|
|
15127
15847
|
.tier1-approval-card .actions {
|
|
15128
|
-
display: flex; gap:
|
|
15848
|
+
display: flex; gap: var(--space-2); flex-wrap: wrap;
|
|
15849
|
+
}
|
|
15850
|
+
/* Sprint Piece 2 PR 4 (2026-05-04): Agents view + Inspect pane polish.
|
|
15851
|
+
* Translates Claude Design references at
|
|
15852
|
+
* server/docs/design-refs/sprint-piece-2/surface-agents.jsx and the
|
|
15853
|
+
* Surface 3 block of surfaces.css. The fortress-column .agent-row,
|
|
15854
|
+
* .agent-row-head, and .agent-row-actions rules above are kept verbatim
|
|
15855
|
+
* because Finding DD tests pin them; the new Agents-view list scopes
|
|
15856
|
+
* its grid layout under .agents-list (descendant selector) so the
|
|
15857
|
+
* fortress-column rules are unaffected. The inspect pane combines the
|
|
15858
|
+
* existing .card surface with .inspect-pane structure (sticky right
|
|
15859
|
+
* rail, internal scroll, sectioned body) for the agent-detail view.
|
|
15860
|
+
*/
|
|
15861
|
+
.agents-wrap { max-width: 1080px; margin: 0 auto; }
|
|
15862
|
+
.agents-layout {
|
|
15863
|
+
display: grid;
|
|
15864
|
+
grid-template-columns: 1fr 420px;
|
|
15865
|
+
gap: 20px;
|
|
15866
|
+
align-items: start;
|
|
15867
|
+
}
|
|
15868
|
+
.agents-list {
|
|
15869
|
+
background: var(--surface);
|
|
15870
|
+
border: 1px solid var(--rule);
|
|
15871
|
+
border-radius: var(--rad-lg);
|
|
15872
|
+
overflow: hidden;
|
|
15873
|
+
}
|
|
15874
|
+
.agents-list-head {
|
|
15875
|
+
display: grid;
|
|
15876
|
+
grid-template-columns: minmax(0, 1fr) 110px 120px 88px;
|
|
15877
|
+
gap: 12px;
|
|
15878
|
+
padding: 10px 16px;
|
|
15879
|
+
border-bottom: 1px solid var(--rule);
|
|
15880
|
+
background: var(--paper-2);
|
|
15881
|
+
font-family: var(--mono);
|
|
15882
|
+
font-size: 10px;
|
|
15883
|
+
letter-spacing: 0.06em;
|
|
15884
|
+
text-transform: uppercase;
|
|
15885
|
+
color: var(--ink-3);
|
|
15886
|
+
}
|
|
15887
|
+
.agents-list .agent-row {
|
|
15888
|
+
display: grid;
|
|
15889
|
+
grid-template-columns: minmax(0, 1fr) 110px 120px 88px;
|
|
15890
|
+
gap: 12px;
|
|
15891
|
+
padding: 14px 16px;
|
|
15892
|
+
border-bottom: 1px solid var(--rule);
|
|
15893
|
+
align-items: center;
|
|
15894
|
+
cursor: pointer;
|
|
15895
|
+
transition: background 120ms ease;
|
|
15896
|
+
}
|
|
15897
|
+
.agents-list .agent-row:last-child { border-bottom: 0; }
|
|
15898
|
+
.agents-list .agent-row:hover { background: var(--paper-2); }
|
|
15899
|
+
.agents-list .agent-row.selected {
|
|
15900
|
+
background: var(--paper-2);
|
|
15901
|
+
box-shadow: inset 3px 0 0 var(--ink);
|
|
15902
|
+
}
|
|
15903
|
+
.agent-identity { display: flex; align-items: center; gap: 10px; min-width: 0; }
|
|
15904
|
+
.agent-glyph {
|
|
15905
|
+
width: 28px; height: 28px;
|
|
15906
|
+
border-radius: var(--rad);
|
|
15907
|
+
background: var(--paper-3);
|
|
15908
|
+
border: 1px solid var(--rule);
|
|
15909
|
+
display: grid; place-items: center;
|
|
15910
|
+
flex-shrink: 0;
|
|
15911
|
+
font-family: var(--mono);
|
|
15912
|
+
font-size: 11px;
|
|
15913
|
+
color: var(--ink-2);
|
|
15914
|
+
font-weight: 600;
|
|
15915
|
+
}
|
|
15916
|
+
.agent-name {
|
|
15917
|
+
display: flex; flex-direction: column; min-width: 0;
|
|
15918
|
+
}
|
|
15919
|
+
.agent-name strong {
|
|
15920
|
+
font-size: var(--text-base); font-weight: 500;
|
|
15921
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
15922
|
+
}
|
|
15923
|
+
.agent-name small {
|
|
15924
|
+
font-family: var(--mono); font-size: var(--text-xs); color: var(--ink-3);
|
|
15925
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
15926
|
+
display: block;
|
|
15927
|
+
}
|
|
15928
|
+
.agent-state {
|
|
15929
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
15930
|
+
font-size: var(--text-xs);
|
|
15931
|
+
font-family: var(--mono);
|
|
15932
|
+
}
|
|
15933
|
+
.state-dot {
|
|
15934
|
+
width: 7px; height: 7px; border-radius: 50%;
|
|
15935
|
+
background: var(--ink-4);
|
|
15936
|
+
}
|
|
15937
|
+
.state-dot.live { background: var(--sage); animation: pulse-soft 2.4s ease-in-out infinite; }
|
|
15938
|
+
.state-dot.idle { background: var(--ochre); }
|
|
15939
|
+
.state-dot.off { background: var(--ink-4); }
|
|
15940
|
+
@keyframes pulse-soft {
|
|
15941
|
+
0%, 100% { box-shadow: 0 0 0 0 currentColor; opacity: 1; }
|
|
15942
|
+
50% { box-shadow: 0 0 0 4px transparent; opacity: 0.7; }
|
|
15943
|
+
}
|
|
15944
|
+
.agent-last {
|
|
15945
|
+
font-family: var(--mono); font-size: var(--text-xs); color: var(--ink-3);
|
|
15946
|
+
}
|
|
15947
|
+
/* Inspect pane (combined with .card outer wrapper for the
|
|
15948
|
+
* renderAgentInspectPanel return-shape regex anchored in
|
|
15949
|
+
* dashboard-welcome.test.ts:152). The .inspect-pane modifier overrides
|
|
15950
|
+
* .card padding so internal sections control their own spacing.
|
|
15951
|
+
*/
|
|
15952
|
+
.inspect-pane {
|
|
15953
|
+
padding: 0;
|
|
15954
|
+
display: flex; flex-direction: column;
|
|
15955
|
+
position: sticky;
|
|
15956
|
+
top: 20px;
|
|
15957
|
+
max-height: calc(100vh - 100px);
|
|
15958
|
+
overflow: hidden;
|
|
15959
|
+
}
|
|
15960
|
+
.inspect-head {
|
|
15961
|
+
padding: 16px 18px;
|
|
15962
|
+
border-bottom: 1px solid var(--rule);
|
|
15963
|
+
display: flex; flex-direction: column; gap: 10px;
|
|
15964
|
+
}
|
|
15965
|
+
.inspect-head .row1 {
|
|
15966
|
+
display: flex; align-items: center; gap: 10px;
|
|
15967
|
+
}
|
|
15968
|
+
.inspect-head h3 {
|
|
15969
|
+
font-family: var(--serif); font-weight: 500;
|
|
15970
|
+
font-size: 17px; margin: 0;
|
|
15971
|
+
}
|
|
15972
|
+
.inspect-head .meta {
|
|
15973
|
+
display: flex; gap: 6px; flex-wrap: wrap;
|
|
15974
|
+
}
|
|
15975
|
+
.inspect-body {
|
|
15976
|
+
overflow-y: auto;
|
|
15977
|
+
padding: 4px 18px 18px;
|
|
15978
|
+
}
|
|
15979
|
+
.inspect-section {
|
|
15980
|
+
padding: 14px 0;
|
|
15981
|
+
border-bottom: 1px solid var(--rule);
|
|
15982
|
+
}
|
|
15983
|
+
.inspect-section:last-child { border-bottom: 0; }
|
|
15984
|
+
.inspect-section h4 {
|
|
15985
|
+
font-family: var(--mono);
|
|
15986
|
+
font-size: 10px;
|
|
15987
|
+
letter-spacing: 0.08em;
|
|
15988
|
+
text-transform: uppercase;
|
|
15989
|
+
color: var(--ink-3);
|
|
15990
|
+
margin: 0 0 10px;
|
|
15991
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
15992
|
+
}
|
|
15993
|
+
.inspect-section h4 .count {
|
|
15994
|
+
font-family: var(--mono);
|
|
15995
|
+
background: var(--paper-3);
|
|
15996
|
+
border-radius: 999px;
|
|
15997
|
+
padding: 1px 7px;
|
|
15998
|
+
color: var(--ink-2);
|
|
15999
|
+
font-size: 10px;
|
|
16000
|
+
}
|
|
16001
|
+
.approval-row {
|
|
16002
|
+
background: var(--ochre-bg);
|
|
16003
|
+
border: 1px solid var(--ochre);
|
|
16004
|
+
border-radius: var(--rad);
|
|
16005
|
+
padding: 10px 12px;
|
|
16006
|
+
margin-bottom: 8px;
|
|
16007
|
+
display: flex; flex-direction: column; gap: 8px;
|
|
16008
|
+
}
|
|
16009
|
+
.approval-row .what { font-size: var(--text-base); color: var(--ink); }
|
|
16010
|
+
.approval-row .what .pill { margin-right: 6px; }
|
|
16011
|
+
.approval-row .why {
|
|
16012
|
+
font-size: var(--text-sm); color: var(--ink-2);
|
|
16013
|
+
padding-left: 10px;
|
|
16014
|
+
border-left: 2px solid var(--ochre);
|
|
16015
|
+
}
|
|
16016
|
+
.approval-row .actions { display: flex; gap: 6px; justify-content: flex-end; }
|
|
16017
|
+
.timeline {
|
|
16018
|
+
display: flex; flex-direction: column; gap: 0;
|
|
16019
|
+
position: relative;
|
|
16020
|
+
padding-left: 14px;
|
|
16021
|
+
}
|
|
16022
|
+
.timeline::before {
|
|
16023
|
+
content: "";
|
|
16024
|
+
position: absolute;
|
|
16025
|
+
left: 4px; top: 6px; bottom: 6px;
|
|
16026
|
+
width: 1px;
|
|
16027
|
+
background: var(--rule);
|
|
16028
|
+
}
|
|
16029
|
+
.timeline-item {
|
|
16030
|
+
position: relative;
|
|
16031
|
+
padding: 6px 0 10px;
|
|
16032
|
+
font-size: var(--text-sm);
|
|
16033
|
+
}
|
|
16034
|
+
.timeline-item::before {
|
|
16035
|
+
content: "";
|
|
16036
|
+
position: absolute;
|
|
16037
|
+
left: -14px; top: 11px;
|
|
16038
|
+
width: 8px; height: 8px;
|
|
16039
|
+
border-radius: 50%;
|
|
16040
|
+
background: var(--surface);
|
|
16041
|
+
border: 1.5px solid var(--ink-4);
|
|
16042
|
+
}
|
|
16043
|
+
.timeline-item.ok::before { border-color: var(--sage); }
|
|
16044
|
+
.timeline-item.warn::before { border-color: var(--ochre); }
|
|
16045
|
+
.timeline-item.fail::before { border-color: var(--rust); }
|
|
16046
|
+
.timeline-item .ts {
|
|
16047
|
+
font-family: var(--mono); font-size: 10px;
|
|
16048
|
+
color: var(--ink-3);
|
|
16049
|
+
letter-spacing: 0.02em;
|
|
15129
16050
|
}
|
|
16051
|
+
.timeline-item .what {
|
|
16052
|
+
margin-top: 2px; color: var(--ink); font-size: var(--text-base);
|
|
16053
|
+
}
|
|
16054
|
+
.timeline-item .att {
|
|
16055
|
+
margin-top: 4px;
|
|
16056
|
+
display: inline-flex;
|
|
16057
|
+
}
|
|
16058
|
+
.policy-line {
|
|
16059
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
16060
|
+
padding: 5px 0; font-size: var(--text-base);
|
|
16061
|
+
border-bottom: 1px dashed var(--rule);
|
|
16062
|
+
}
|
|
16063
|
+
.policy-line:last-child { border-bottom: 0; }
|
|
16064
|
+
.policy-line .k { color: var(--ink-3); }
|
|
16065
|
+
.policy-line .v { font-family: var(--mono); font-size: var(--text-sm); color: var(--ink); }
|
|
16066
|
+
/* Empty-state block for when no agents are wrapped. The
|
|
16067
|
+
* renderAgentsList empty-state branch begins with the literal
|
|
16068
|
+
* '<h1>Agents</h1>' (regex-pinned in agents-empty-state-canary.test.ts)
|
|
16069
|
+
* and the "No wrapped agents yet." copy is preserved verbatim.
|
|
16070
|
+
*/
|
|
16071
|
+
.agents-empty {
|
|
16072
|
+
background: var(--surface);
|
|
16073
|
+
border: 1px dashed var(--rule-2);
|
|
16074
|
+
border-radius: var(--rad-lg);
|
|
16075
|
+
padding: 56px 40px;
|
|
16076
|
+
text-align: center;
|
|
16077
|
+
max-width: 720px;
|
|
16078
|
+
margin: 32px auto;
|
|
16079
|
+
}
|
|
16080
|
+
.agents-empty .icon-frame {
|
|
16081
|
+
width: 64px; height: 64px;
|
|
16082
|
+
margin: 0 auto 18px;
|
|
16083
|
+
border: 1px solid var(--rule);
|
|
16084
|
+
border-radius: 50%;
|
|
16085
|
+
display: grid; place-items: center;
|
|
16086
|
+
position: relative;
|
|
16087
|
+
}
|
|
16088
|
+
.agents-empty .icon-frame::before,
|
|
16089
|
+
.agents-empty .icon-frame::after {
|
|
16090
|
+
content: "";
|
|
16091
|
+
position: absolute;
|
|
16092
|
+
border: 1px solid var(--rule);
|
|
16093
|
+
border-radius: 50%;
|
|
16094
|
+
}
|
|
16095
|
+
.agents-empty .icon-frame::before { inset: -8px; opacity: 0.6; }
|
|
16096
|
+
.agents-empty .icon-frame::after { inset: -16px; opacity: 0.3; }
|
|
16097
|
+
.agents-empty .icon-frame .core {
|
|
16098
|
+
width: 22px; height: 22px;
|
|
16099
|
+
background: var(--ink);
|
|
16100
|
+
border-radius: 50%;
|
|
16101
|
+
}
|
|
16102
|
+
.agents-empty h2 {
|
|
16103
|
+
font-family: var(--serif);
|
|
16104
|
+
font-weight: 400;
|
|
16105
|
+
font-size: var(--text-xl);
|
|
16106
|
+
margin: 0 0 8px;
|
|
16107
|
+
}
|
|
16108
|
+
.agents-empty p {
|
|
16109
|
+
color: var(--ink-3);
|
|
16110
|
+
margin: 0 0 20px;
|
|
16111
|
+
font-size: var(--text-md);
|
|
16112
|
+
line-height: 1.55;
|
|
16113
|
+
max-width: 50ch;
|
|
16114
|
+
margin-left: auto; margin-right: auto;
|
|
16115
|
+
}
|
|
16116
|
+
.terminal-block {
|
|
16117
|
+
text-align: left;
|
|
16118
|
+
background: var(--paper-3);
|
|
16119
|
+
border: 1px solid var(--rule);
|
|
16120
|
+
border-radius: var(--rad);
|
|
16121
|
+
padding: 14px 16px;
|
|
16122
|
+
font-family: var(--mono);
|
|
16123
|
+
font-size: var(--text-base);
|
|
16124
|
+
margin: 0 auto 16px;
|
|
16125
|
+
max-width: 480px;
|
|
16126
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
16127
|
+
}
|
|
16128
|
+
.terminal-block .cmd { color: var(--ink); }
|
|
16129
|
+
.terminal-block .cmd .prompt { color: var(--ink-3); margin-right: 8px; user-select: none; }
|
|
16130
|
+
.copy-btn {
|
|
16131
|
+
background: transparent; border: 0;
|
|
16132
|
+
color: var(--ink-3); cursor: pointer;
|
|
16133
|
+
font-family: var(--mono); font-size: var(--text-xs);
|
|
16134
|
+
padding: 2px 6px;
|
|
16135
|
+
border-radius: var(--rad);
|
|
16136
|
+
}
|
|
16137
|
+
.copy-btn:hover { color: var(--ink); background: var(--paper-2); }
|
|
16138
|
+
|
|
16139
|
+
/* Surface 5. Attestation badge gallery. */
|
|
16140
|
+
.att-gallery {
|
|
16141
|
+
display: flex; flex-direction: column; gap: 24px;
|
|
16142
|
+
max-width: 1000px;
|
|
16143
|
+
margin: 0 auto;
|
|
16144
|
+
}
|
|
16145
|
+
.att-section {
|
|
16146
|
+
background: var(--surface);
|
|
16147
|
+
border: 1px solid var(--rule);
|
|
16148
|
+
border-radius: var(--rad-lg);
|
|
16149
|
+
padding: 22px 24px;
|
|
16150
|
+
}
|
|
16151
|
+
.att-section-head {
|
|
16152
|
+
display: flex; justify-content: space-between; align-items: baseline;
|
|
16153
|
+
gap: 12px;
|
|
16154
|
+
margin-bottom: 16px;
|
|
16155
|
+
padding-bottom: 12px;
|
|
16156
|
+
border-bottom: 1px solid var(--rule);
|
|
16157
|
+
}
|
|
16158
|
+
.att-section-head h2 {
|
|
16159
|
+
font-family: var(--serif);
|
|
16160
|
+
font-weight: 400;
|
|
16161
|
+
font-size: 19px;
|
|
16162
|
+
margin: 0 0 4px;
|
|
16163
|
+
}
|
|
16164
|
+
.att-section-head p {
|
|
16165
|
+
color: var(--ink-3);
|
|
16166
|
+
margin: 0;
|
|
16167
|
+
font-size: 13px;
|
|
16168
|
+
line-height: 1.5;
|
|
16169
|
+
max-width: 64ch;
|
|
16170
|
+
}
|
|
16171
|
+
.att-section-head .label {
|
|
16172
|
+
font-family: var(--mono);
|
|
16173
|
+
font-size: 10px;
|
|
16174
|
+
letter-spacing: 0.08em;
|
|
16175
|
+
text-transform: uppercase;
|
|
16176
|
+
color: var(--ink-3);
|
|
16177
|
+
}
|
|
16178
|
+
.att-row {
|
|
16179
|
+
display: grid;
|
|
16180
|
+
grid-template-columns: 240px 1fr;
|
|
16181
|
+
gap: 24px;
|
|
16182
|
+
padding: 14px 0;
|
|
16183
|
+
border-bottom: 1px dashed var(--rule);
|
|
16184
|
+
align-items: center;
|
|
16185
|
+
}
|
|
16186
|
+
.att-row:last-child { border-bottom: 0; }
|
|
16187
|
+
.att-row .demo {
|
|
16188
|
+
display: flex; align-items: center; justify-content: flex-start;
|
|
16189
|
+
padding: 12px 16px;
|
|
16190
|
+
background: var(--paper-2);
|
|
16191
|
+
border: 1px solid var(--rule);
|
|
16192
|
+
border-radius: var(--rad);
|
|
16193
|
+
min-height: 56px;
|
|
16194
|
+
}
|
|
16195
|
+
.att-row .desc strong {
|
|
16196
|
+
font-size: 13px; display: block; margin-bottom: 3px;
|
|
16197
|
+
}
|
|
16198
|
+
.att-row .desc small {
|
|
16199
|
+
color: var(--ink-3); font-size: 12px;
|
|
16200
|
+
line-height: 1.5;
|
|
16201
|
+
}
|
|
16202
|
+
|
|
16203
|
+
/* Global persistent badge. Lives in the topbar across every surface. */
|
|
16204
|
+
.att-global {
|
|
16205
|
+
display: inline-flex; align-items: center;
|
|
16206
|
+
gap: 8px;
|
|
16207
|
+
padding: 4px 10px 4px 6px;
|
|
16208
|
+
border: 1px solid var(--rule);
|
|
16209
|
+
border-radius: 999px;
|
|
16210
|
+
background: var(--surface-2);
|
|
16211
|
+
font-family: var(--mono);
|
|
16212
|
+
font-size: 11px;
|
|
16213
|
+
color: var(--ink-2);
|
|
16214
|
+
}
|
|
16215
|
+
.att-global.verified { border-color: var(--sage); background: var(--sage-bg); color: var(--sage); }
|
|
16216
|
+
.att-global.degraded { border-color: var(--ochre); background: var(--ochre-bg); color: var(--ochre); }
|
|
16217
|
+
.att-global.unverified { border-color: var(--rust); background: var(--rust-bg); color: var(--rust); }
|
|
16218
|
+
.att-global .seal {
|
|
16219
|
+
width: 18px; height: 18px;
|
|
16220
|
+
position: relative;
|
|
16221
|
+
flex-shrink: 0;
|
|
16222
|
+
}
|
|
16223
|
+
.att-global .seal-ring {
|
|
16224
|
+
position: absolute; inset: 0;
|
|
16225
|
+
border: 1.5px solid currentColor;
|
|
16226
|
+
border-radius: 50%;
|
|
16227
|
+
}
|
|
16228
|
+
.att-global .seal-ring.dashed { border-style: dashed; }
|
|
16229
|
+
.att-global .seal-core {
|
|
16230
|
+
position: absolute; inset: 4px;
|
|
16231
|
+
background: currentColor;
|
|
16232
|
+
border-radius: 50%;
|
|
16233
|
+
opacity: 0.85;
|
|
16234
|
+
}
|
|
16235
|
+
.att-global.degraded .seal-core { background: transparent; border: 1px solid currentColor; }
|
|
16236
|
+
.att-global.unverified .seal-core {
|
|
16237
|
+
background: transparent;
|
|
16238
|
+
border: 1px solid currentColor;
|
|
16239
|
+
}
|
|
16240
|
+
.att-global.unverified .seal-core::after {
|
|
16241
|
+
content: ""; position: absolute; inset: 0;
|
|
16242
|
+
background: currentColor; opacity: 0.4;
|
|
16243
|
+
clip-path: polygon(0 0, 100% 100%, 100% 90%, 10% 0);
|
|
16244
|
+
}
|
|
16245
|
+
.att-global .label {
|
|
16246
|
+
font-family: var(--mono);
|
|
16247
|
+
font-size: 11px;
|
|
16248
|
+
letter-spacing: 0.02em;
|
|
16249
|
+
text-transform: uppercase;
|
|
16250
|
+
}
|
|
16251
|
+
.att-global .hash {
|
|
16252
|
+
font-family: var(--mono);
|
|
16253
|
+
font-size: 10px;
|
|
16254
|
+
opacity: 0.7;
|
|
16255
|
+
border-left: 1px solid currentColor;
|
|
16256
|
+
padding-left: 8px;
|
|
16257
|
+
margin-left: 2px;
|
|
16258
|
+
}
|
|
16259
|
+
|
|
16260
|
+
/* Per-agent badge. Square chip beside each agent. */
|
|
16261
|
+
.att-agent {
|
|
16262
|
+
display: inline-flex; align-items: center;
|
|
16263
|
+
gap: 6px;
|
|
16264
|
+
padding: 3px 7px;
|
|
16265
|
+
border-radius: var(--rad);
|
|
16266
|
+
border: 1px solid var(--rule);
|
|
16267
|
+
background: var(--surface);
|
|
16268
|
+
font-family: var(--mono);
|
|
16269
|
+
font-size: 10px;
|
|
16270
|
+
color: var(--ink-2);
|
|
16271
|
+
}
|
|
16272
|
+
.att-agent .mark {
|
|
16273
|
+
width: 10px; height: 10px;
|
|
16274
|
+
border: 1.5px solid currentColor;
|
|
16275
|
+
border-radius: 2px;
|
|
16276
|
+
position: relative;
|
|
16277
|
+
}
|
|
16278
|
+
.att-agent.verified { color: var(--sage); border-color: var(--sage); background: var(--sage-bg); }
|
|
16279
|
+
.att-agent.verified .mark { background: currentColor; }
|
|
16280
|
+
.att-agent.degraded { color: var(--ochre); border-color: var(--ochre); background: var(--ochre-bg); }
|
|
16281
|
+
.att-agent.unverified { color: var(--rust); border-color: var(--rust); background: var(--rust-bg); }
|
|
16282
|
+
.att-agent.unverified .mark {
|
|
16283
|
+
background: repeating-linear-gradient(
|
|
16284
|
+
45deg, currentColor, currentColor 1px,
|
|
16285
|
+
transparent 1px, transparent 3px
|
|
16286
|
+
);
|
|
16287
|
+
}
|
|
16288
|
+
|
|
16289
|
+
/* Per-action badge. Tiny inline tick on timeline rows. */
|
|
16290
|
+
.att-action {
|
|
16291
|
+
display: inline-flex; align-items: center; gap: 4px;
|
|
16292
|
+
font-family: var(--mono);
|
|
16293
|
+
font-size: 10px;
|
|
16294
|
+
color: var(--ink-3);
|
|
16295
|
+
padding: 1px 6px;
|
|
16296
|
+
border-radius: 4px;
|
|
16297
|
+
background: var(--paper-3);
|
|
16298
|
+
border: 1px solid transparent;
|
|
16299
|
+
}
|
|
16300
|
+
.att-action .tick {
|
|
16301
|
+
width: 6px; height: 6px;
|
|
16302
|
+
border-radius: 1px;
|
|
16303
|
+
background: currentColor;
|
|
16304
|
+
}
|
|
16305
|
+
.att-action.verified { color: var(--sage); }
|
|
16306
|
+
.att-action.degraded { color: var(--ochre); }
|
|
16307
|
+
.att-action.unverified { color: var(--rust); }
|
|
16308
|
+
.att-action.neutral .tick { background: var(--ink-4); border-radius: 50%; }
|
|
16309
|
+
|
|
16310
|
+
/* Custody-provenance badge stub (v1.x). Visibly stubbed with dashed border. */
|
|
16311
|
+
.att-custody {
|
|
16312
|
+
display: inline-flex; align-items: center; gap: 8px;
|
|
16313
|
+
padding: 4px 10px 4px 6px;
|
|
16314
|
+
border-radius: var(--rad);
|
|
16315
|
+
border: 1px dashed var(--rule-2);
|
|
16316
|
+
background: var(--paper-3);
|
|
16317
|
+
color: var(--ink-3);
|
|
16318
|
+
font-family: var(--mono);
|
|
16319
|
+
font-size: 10px;
|
|
16320
|
+
}
|
|
16321
|
+
.att-custody .seal-stub {
|
|
16322
|
+
width: 16px; height: 16px;
|
|
16323
|
+
border: 1px dashed var(--ink-4);
|
|
16324
|
+
border-radius: 50%;
|
|
16325
|
+
position: relative;
|
|
16326
|
+
flex-shrink: 0;
|
|
16327
|
+
}
|
|
16328
|
+
.att-custody .seal-stub::after {
|
|
16329
|
+
content: ""; position: absolute; inset: 4px;
|
|
16330
|
+
border: 1px dashed var(--ink-4);
|
|
16331
|
+
border-radius: 50%;
|
|
16332
|
+
}
|
|
16333
|
+
.att-custody .stub-tag {
|
|
16334
|
+
letter-spacing: 0.06em;
|
|
16335
|
+
text-transform: uppercase;
|
|
16336
|
+
}
|
|
16337
|
+
|
|
16338
|
+
/* Tooltip surface for badges. */
|
|
16339
|
+
.att-tooltip {
|
|
16340
|
+
background: var(--ink);
|
|
16341
|
+
color: var(--paper);
|
|
16342
|
+
font-family: var(--mono);
|
|
16343
|
+
font-size: 11px;
|
|
16344
|
+
padding: 8px 10px;
|
|
16345
|
+
border-radius: var(--rad);
|
|
16346
|
+
max-width: 280px;
|
|
16347
|
+
line-height: 1.5;
|
|
16348
|
+
display: inline-block;
|
|
16349
|
+
}
|
|
16350
|
+
[data-theme="dark"] .att-tooltip {
|
|
16351
|
+
background: var(--paper-3);
|
|
16352
|
+
color: var(--ink);
|
|
16353
|
+
}
|
|
16354
|
+
|
|
15130
16355
|
@media (max-width: 1100px) {
|
|
15131
16356
|
.app, .app.route-full { grid-template-columns: 56px 1fr; grid-template-areas: "sidebar topbar" "sidebar main"; }
|
|
15132
16357
|
.fortress { display: none; }
|
|
15133
16358
|
.sidebar h1, .sidebar nav a span { display: none; }
|
|
16359
|
+
.sidebar nav a { justify-content: center; padding: 8px 6px; }
|
|
15134
16360
|
.template-grid { grid-template-columns: 1fr; }
|
|
15135
16361
|
.policy-center h1 { font-size: 30px; }
|
|
15136
16362
|
.intel-center h1 { font-size: 30px; }
|
|
15137
16363
|
.intel-row { grid-template-columns: 1fr; }
|
|
16364
|
+
.intel-grid { grid-template-columns: 1fr; }
|
|
16365
|
+
.intel-failure-row { grid-template-columns: 1fr; }
|
|
16366
|
+
.agents-layout { grid-template-columns: 1fr; }
|
|
16367
|
+
.agents-list-head, .agents-list .agent-row { grid-template-columns: minmax(0, 1fr) 90px 90px; }
|
|
16368
|
+
.agents-list-head span:nth-child(4), .agents-list .agent-row > .agent-last { display: none; }
|
|
16369
|
+
.inspect-pane { position: static; max-height: none; }
|
|
15138
16370
|
}
|
|
15139
16371
|
`;
|
|
15140
16372
|
NAV_ITEMS = [
|
|
@@ -15142,11 +16374,26 @@ body {
|
|
|
15142
16374
|
{ id: "agents", label: "Agents" },
|
|
15143
16375
|
{ id: "policy", label: "Policy" },
|
|
15144
16376
|
{ id: "intelligence", label: "Intelligence" },
|
|
16377
|
+
{ id: "attestation", label: "Attestation" },
|
|
15145
16378
|
{ id: "privacy", label: "Privacy" },
|
|
15146
16379
|
{ id: "coordination", label: "Coordination" },
|
|
15147
16380
|
{ id: "health", label: "Health" },
|
|
15148
16381
|
{ id: "exit-drill", label: "Exit drill" }
|
|
15149
16382
|
];
|
|
16383
|
+
NAV_ICON_PATHS = {
|
|
16384
|
+
dashboard: '<rect x="2" y="2" width="5" height="5" rx="1"/><rect x="9" y="2" width="5" height="5" rx="1"/><rect x="2" y="9" width="5" height="5" rx="1"/><rect x="9" y="9" width="5" height="5" rx="1"/>',
|
|
16385
|
+
agents: '<circle cx="6" cy="5.5" r="2.3"/><path d="M2 13.5c0-2.2 1.8-4 4-4s4 1.8 4 4"/><circle cx="11.5" cy="6" r="1.8"/><path d="M14 12.5c0-1.7-1.1-2.7-2.5-3"/>',
|
|
16386
|
+
policy: '<path d="M4 2h5l3 3v9H4z"/><path d="M9 2v3h3"/><path d="M6 9h4M6 11.5h4"/>',
|
|
16387
|
+
intelligence: '<rect x="3.5" y="3.5" width="9" height="9" rx="0.5"/><rect x="6" y="6" width="4" height="4"/><path d="M6 1.5v2M10 1.5v2M6 12.5v2M10 12.5v2M1.5 6h2M1.5 10h2M12.5 6h2M12.5 10h2"/>',
|
|
16388
|
+
attestation: '<circle cx="8" cy="8" r="5.5"/><circle cx="8" cy="8" r="2.2"/><path d="M8 1.5v1.5M8 13v1.5M1.5 8h1.5M13 8h1.5"/>',
|
|
16389
|
+
privacy: '<path d="M8 1.5L3 3v4.5c0 3 2.2 5.4 5 7 2.8-1.6 5-4 5-7V3z"/><path d="M6 8l1.5 1.5L10.5 6"/>',
|
|
16390
|
+
coordination: '<circle cx="4" cy="3.5" r="1.4"/><circle cx="4" cy="12.5" r="1.4"/><circle cx="12" cy="8" r="1.4"/><path d="M4 4.9V11.1M4 5c0 3 3 3 6.7 3"/>',
|
|
16391
|
+
health: '<path d="M2 8h2.5l1.5-4 3 8 1.5-4H14"/>',
|
|
16392
|
+
"exit-drill": '<path d="M9.5 2H3v12h6.5"/><path d="M11 5l3 3-3 3"/><path d="M14 8H6.5"/>'
|
|
16393
|
+
};
|
|
16394
|
+
SVG_OPEN = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">';
|
|
16395
|
+
THEME_ICON_MOON = SVG_OPEN + '<path d="M13 9.5A5.5 5.5 0 0 1 6.5 3 5.5 5.5 0 1 0 13 9.5z"/></svg>';
|
|
16396
|
+
THEME_ICON_SUN = SVG_OPEN + '<circle cx="8" cy="8" r="2.5"/><path d="M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.2 3.2l1.4 1.4M11.4 11.4l1.4 1.4M3.2 12.8l1.4-1.4M11.4 4.6l1.4-1.4"/></svg>';
|
|
15150
16397
|
}
|
|
15151
16398
|
});
|
|
15152
16399
|
|
|
@@ -15855,7 +17102,7 @@ var init_dashboard = __esm({
|
|
|
15855
17102
|
server = http.createServer(handler);
|
|
15856
17103
|
}
|
|
15857
17104
|
this.httpServer = server;
|
|
15858
|
-
return new Promise((
|
|
17105
|
+
return new Promise((resolve6, reject) => {
|
|
15859
17106
|
const protocol = this.useTLS ? "https" : "http";
|
|
15860
17107
|
const baseUrl = `${protocol}://${this.config.host}:${this.config.port}`;
|
|
15861
17108
|
server.listen(this.config.port, this.config.host, () => {
|
|
@@ -15880,7 +17127,7 @@ var init_dashboard = __esm({
|
|
|
15880
17127
|
if (shouldAutoOpen) {
|
|
15881
17128
|
this.openInBrowser(sessionUrl);
|
|
15882
17129
|
}
|
|
15883
|
-
|
|
17130
|
+
resolve6();
|
|
15884
17131
|
});
|
|
15885
17132
|
server.on("error", (err) => {
|
|
15886
17133
|
if (err.code === "EADDRINUSE") {
|
|
@@ -15926,8 +17173,8 @@ var init_dashboard = __esm({
|
|
|
15926
17173
|
}
|
|
15927
17174
|
this.rateLimits.clear();
|
|
15928
17175
|
if (this.httpServer) {
|
|
15929
|
-
return new Promise((
|
|
15930
|
-
this.httpServer.close(() =>
|
|
17176
|
+
return new Promise((resolve6) => {
|
|
17177
|
+
this.httpServer.close(() => resolve6());
|
|
15931
17178
|
});
|
|
15932
17179
|
}
|
|
15933
17180
|
}
|
|
@@ -15941,7 +17188,7 @@ var init_dashboard = __esm({
|
|
|
15941
17188
|
`[Sanctuary] Approval required: ${request.operation} (Tier ${request.tier}) \u2014 open dashboard to respond
|
|
15942
17189
|
`
|
|
15943
17190
|
);
|
|
15944
|
-
return new Promise((
|
|
17191
|
+
return new Promise((resolve6) => {
|
|
15945
17192
|
const timer = setTimeout(() => {
|
|
15946
17193
|
this.pending.delete(id);
|
|
15947
17194
|
const response = {
|
|
@@ -15955,12 +17202,12 @@ var init_dashboard = __esm({
|
|
|
15955
17202
|
decision: response.decision,
|
|
15956
17203
|
decided_by: "timeout"
|
|
15957
17204
|
});
|
|
15958
|
-
|
|
17205
|
+
resolve6(response);
|
|
15959
17206
|
}, this.config.timeout_seconds * 1e3);
|
|
15960
17207
|
const pending = {
|
|
15961
17208
|
id,
|
|
15962
17209
|
request,
|
|
15963
|
-
resolve:
|
|
17210
|
+
resolve: resolve6,
|
|
15964
17211
|
timer,
|
|
15965
17212
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
15966
17213
|
};
|
|
@@ -16826,7 +18073,7 @@ var init_webhook = __esm({
|
|
|
16826
18073
|
* Start the callback listener server.
|
|
16827
18074
|
*/
|
|
16828
18075
|
async start() {
|
|
16829
|
-
return new Promise((
|
|
18076
|
+
return new Promise((resolve6, reject) => {
|
|
16830
18077
|
this.callbackServer = http.createServer(
|
|
16831
18078
|
(req, res) => this.handleCallback(req, res)
|
|
16832
18079
|
);
|
|
@@ -16841,7 +18088,7 @@ var init_webhook = __esm({
|
|
|
16841
18088
|
|
|
16842
18089
|
`
|
|
16843
18090
|
);
|
|
16844
|
-
|
|
18091
|
+
resolve6();
|
|
16845
18092
|
}
|
|
16846
18093
|
);
|
|
16847
18094
|
this.callbackServer.on("error", reject);
|
|
@@ -16861,8 +18108,8 @@ var init_webhook = __esm({
|
|
|
16861
18108
|
}
|
|
16862
18109
|
this.pending.clear();
|
|
16863
18110
|
if (this.callbackServer) {
|
|
16864
|
-
return new Promise((
|
|
16865
|
-
this.callbackServer.close(() =>
|
|
18111
|
+
return new Promise((resolve6) => {
|
|
18112
|
+
this.callbackServer.close(() => resolve6());
|
|
16866
18113
|
});
|
|
16867
18114
|
}
|
|
16868
18115
|
}
|
|
@@ -16875,7 +18122,7 @@ var init_webhook = __esm({
|
|
|
16875
18122
|
`[Sanctuary] Webhook approval sent: ${request.operation} (Tier ${request.tier}) \u2014 awaiting callback
|
|
16876
18123
|
`
|
|
16877
18124
|
);
|
|
16878
|
-
return new Promise((
|
|
18125
|
+
return new Promise((resolve6) => {
|
|
16879
18126
|
const timer = setTimeout(() => {
|
|
16880
18127
|
this.pending.delete(id);
|
|
16881
18128
|
const response = {
|
|
@@ -16884,12 +18131,12 @@ var init_webhook = __esm({
|
|
|
16884
18131
|
decided_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16885
18132
|
decided_by: "timeout"
|
|
16886
18133
|
};
|
|
16887
|
-
|
|
18134
|
+
resolve6(response);
|
|
16888
18135
|
}, this.config.timeout_seconds * 1e3);
|
|
16889
18136
|
const pending = {
|
|
16890
18137
|
id,
|
|
16891
18138
|
request,
|
|
16892
|
-
resolve:
|
|
18139
|
+
resolve: resolve6,
|
|
16893
18140
|
timer,
|
|
16894
18141
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
16895
18142
|
};
|
|
@@ -18554,8 +19801,8 @@ function verifySHR(shr, now) {
|
|
|
18554
19801
|
const errors = [];
|
|
18555
19802
|
const warnings = [];
|
|
18556
19803
|
const currentTime = /* @__PURE__ */ new Date();
|
|
18557
|
-
if (!shr.body || !shr.signed_by || !shr.signature) {
|
|
18558
|
-
errors.push("Missing required SHR fields (body, signed_by, or signature)");
|
|
19804
|
+
if (!shr.body || !shr.signed_by || !shr.signature || !shr.signature_scheme) {
|
|
19805
|
+
errors.push("Missing required SHR fields (body, signed_by, signature_scheme, or signature)");
|
|
18559
19806
|
return {
|
|
18560
19807
|
valid: false,
|
|
18561
19808
|
errors,
|
|
@@ -18568,6 +19815,9 @@ function verifySHR(shr, now) {
|
|
|
18568
19815
|
if (shr.body.shr_version !== "1.0") {
|
|
18569
19816
|
errors.push(`Unsupported SHR version: ${shr.body.shr_version}`);
|
|
18570
19817
|
}
|
|
19818
|
+
if (shr.signature_scheme !== SIGNATURE_SCHEME_V1) {
|
|
19819
|
+
errors.push(`Unsupported SHR signature_scheme: ${String(shr.signature_scheme)}`);
|
|
19820
|
+
}
|
|
18571
19821
|
const expiresAt = new Date(shr.body.expires_at);
|
|
18572
19822
|
if (isNaN(expiresAt.getTime())) {
|
|
18573
19823
|
errors.push("Invalid expires_at timestamp");
|
|
@@ -18629,6 +19879,7 @@ var init_verifier = __esm({
|
|
|
18629
19879
|
init_types();
|
|
18630
19880
|
init_identity();
|
|
18631
19881
|
init_encoding();
|
|
19882
|
+
init_constants();
|
|
18632
19883
|
}
|
|
18633
19884
|
});
|
|
18634
19885
|
|
|
@@ -23588,7 +24839,7 @@ async function runOpenAIPrivacyFilter(text, config) {
|
|
|
23588
24839
|
return parsed;
|
|
23589
24840
|
}
|
|
23590
24841
|
function runCommand(command, input, timeoutMs) {
|
|
23591
|
-
return new Promise((
|
|
24842
|
+
return new Promise((resolve6, reject) => {
|
|
23592
24843
|
const child = child_process.spawn(command, [], {
|
|
23593
24844
|
stdio: ["pipe", "pipe", "pipe"],
|
|
23594
24845
|
shell: false
|
|
@@ -23619,7 +24870,7 @@ function runCommand(command, input, timeoutMs) {
|
|
|
23619
24870
|
));
|
|
23620
24871
|
return;
|
|
23621
24872
|
}
|
|
23622
|
-
|
|
24873
|
+
resolve6(stdout);
|
|
23623
24874
|
});
|
|
23624
24875
|
child.stdin.end(input);
|
|
23625
24876
|
});
|
|
@@ -25495,13 +26746,13 @@ var init_proxy_router = __esm({
|
|
|
25495
26746
|
* Call an upstream tool with a timeout.
|
|
25496
26747
|
*/
|
|
25497
26748
|
async callWithTimeout(serverName, toolName, args, timeoutMs) {
|
|
25498
|
-
return new Promise((
|
|
26749
|
+
return new Promise((resolve6, reject) => {
|
|
25499
26750
|
const timer = setTimeout(() => {
|
|
25500
26751
|
reject(new Error(`Upstream tool call timed out after ${timeoutMs}ms`));
|
|
25501
26752
|
}, timeoutMs);
|
|
25502
26753
|
this.clientManager.callTool(serverName, toolName, args).then((result) => {
|
|
25503
26754
|
clearTimeout(timer);
|
|
25504
|
-
|
|
26755
|
+
resolve6(result);
|
|
25505
26756
|
}).catch((err) => {
|
|
25506
26757
|
clearTimeout(timer);
|
|
25507
26758
|
reject(err);
|
|
@@ -30006,8 +31257,6 @@ var init_inbox_aggregator = __esm({
|
|
|
30006
31257
|
"src/hub/inbox-aggregator.ts"() {
|
|
30007
31258
|
}
|
|
30008
31259
|
});
|
|
30009
|
-
|
|
30010
|
-
// src/hub/activity-feed.ts
|
|
30011
31260
|
function categorizeOperation(layer, operation) {
|
|
30012
31261
|
const op = operation.toLowerCase();
|
|
30013
31262
|
if (op === "privacy_event" || op.startsWith("privacy_")) return "privacy";
|
|
@@ -30058,18 +31307,30 @@ function extractAgentIdHint(entry) {
|
|
|
30058
31307
|
const value = details.agent_id;
|
|
30059
31308
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
30060
31309
|
}
|
|
31310
|
+
function deriveAttestationFragment(entryId) {
|
|
31311
|
+
const hash2 = crypto.createHash("sha256").update(entryId).digest("hex");
|
|
31312
|
+
return `${hash2.slice(0, 4)}..${hash2.slice(4, 6)}`;
|
|
31313
|
+
}
|
|
31314
|
+
function deriveAttestationState(entry) {
|
|
31315
|
+
return entry.result === "success" ? "verified" : "degraded";
|
|
31316
|
+
}
|
|
30061
31317
|
function projectEntry(entry) {
|
|
30062
31318
|
const agentIdHint = extractAgentIdHint(entry);
|
|
30063
31319
|
const category = categorizeOperation(entry.layer, entry.operation);
|
|
31320
|
+
const entryId = `${entry.timestamp}|${entry.operation}|${entry.identity_id}`;
|
|
30064
31321
|
return {
|
|
30065
31322
|
version: "1.1",
|
|
30066
|
-
entry_id:
|
|
31323
|
+
entry_id: entryId,
|
|
30067
31324
|
emitted_at: entry.timestamp,
|
|
30068
31325
|
...agentIdHint ? { agent_id: agentIdHint } : {},
|
|
30069
31326
|
identity_id: entry.identity_id,
|
|
30070
31327
|
category,
|
|
30071
31328
|
display_template_id: templateIdFor(category, entry.operation),
|
|
30072
|
-
display_template_args: buildTemplateArgs(entry, agentIdHint)
|
|
31329
|
+
display_template_args: buildTemplateArgs(entry, agentIdHint),
|
|
31330
|
+
attestation: {
|
|
31331
|
+
state: deriveAttestationState(entry),
|
|
31332
|
+
fragment: deriveAttestationFragment(entryId)
|
|
31333
|
+
}
|
|
30073
31334
|
};
|
|
30074
31335
|
}
|
|
30075
31336
|
async function aggregateActivity(sources, filter = {}) {
|
|
@@ -30134,7 +31395,7 @@ function snapshotForRecord(record, openInboxItemIds) {
|
|
|
30134
31395
|
var HubService;
|
|
30135
31396
|
var init_hub_service = __esm({
|
|
30136
31397
|
"src/hub/hub-service.ts"() {
|
|
30137
|
-
|
|
31398
|
+
init_constants2();
|
|
30138
31399
|
init_channel_templates();
|
|
30139
31400
|
init_constants3();
|
|
30140
31401
|
init_errors4();
|
|
@@ -33047,10 +34308,10 @@ var init_memory = __esm({
|
|
|
33047
34308
|
});
|
|
33048
34309
|
|
|
33049
34310
|
// src/contracts/v1.1/constants.ts
|
|
33050
|
-
var
|
|
34311
|
+
var SIGNATURE_SCHEME_V12, EXIT_BUNDLE_MANIFEST_VERSION, EXIT_BUNDLE_ARTIFACT_KINDS;
|
|
33051
34312
|
var init_constants4 = __esm({
|
|
33052
34313
|
"src/contracts/v1.1/constants.ts"() {
|
|
33053
|
-
|
|
34314
|
+
SIGNATURE_SCHEME_V12 = "ed25519-v1";
|
|
33054
34315
|
EXIT_BUNDLE_MANIFEST_VERSION = "SANCTUARY_EXIT_BUNDLE_V1";
|
|
33055
34316
|
EXIT_BUNDLE_ARTIFACT_KINDS = [
|
|
33056
34317
|
"public_identity",
|
|
@@ -33243,7 +34504,7 @@ function verifyReputationArtifact(reputationArtifact, publicKeysByDid) {
|
|
|
33243
34504
|
unverifiable_attestations: unverifiable
|
|
33244
34505
|
};
|
|
33245
34506
|
}
|
|
33246
|
-
async function verifyExitBundle(bundleDir) {
|
|
34507
|
+
async function verifyExitBundle(bundleDir, options = {}) {
|
|
33247
34508
|
const root = path.resolve(bundleDir);
|
|
33248
34509
|
let manifest;
|
|
33249
34510
|
let manifestBytes;
|
|
@@ -33252,7 +34513,9 @@ async function verifyExitBundle(bundleDir) {
|
|
|
33252
34513
|
manifestBytes = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
|
|
33253
34514
|
manifest = JSON.parse(Buffer.from(raw).toString("utf8"));
|
|
33254
34515
|
} catch {
|
|
33255
|
-
|
|
34516
|
+
throw new InvalidExitBundleError(
|
|
34517
|
+
`Not a valid SANCTUARY_EXIT_BUNDLE_V1 directory: manifest.json missing at ${path.join(root, "manifest.json")}`
|
|
34518
|
+
);
|
|
33256
34519
|
}
|
|
33257
34520
|
const warnings = [];
|
|
33258
34521
|
const unsupportedArtifacts = [];
|
|
@@ -33260,7 +34523,7 @@ async function verifyExitBundle(bundleDir) {
|
|
|
33260
34523
|
if (!body || body.manifest_version !== EXIT_BUNDLE_MANIFEST_VERSION) {
|
|
33261
34524
|
return fail(root, manifest, "manifest_unknown_version", warnings, unsupportedArtifacts);
|
|
33262
34525
|
}
|
|
33263
|
-
if (body.signature_scheme !==
|
|
34526
|
+
if (body.signature_scheme !== SIGNATURE_SCHEME_V12) {
|
|
33264
34527
|
return fail(
|
|
33265
34528
|
root,
|
|
33266
34529
|
manifest,
|
|
@@ -33420,9 +34683,16 @@ async function verifyExitBundle(bundleDir) {
|
|
|
33420
34683
|
}
|
|
33421
34684
|
const reputationFailed = reputation?.bundle_signature_valid === false || (reputation?.invalid_attestations ?? 0) > 0;
|
|
33422
34685
|
const identityFailed = identity ? !identity.signature_valid : false;
|
|
34686
|
+
const unverifiableCount = reputation?.unverifiable_attestations ?? 0;
|
|
34687
|
+
const unverifiableFailed = unverifiableCount > 0 && !options.acceptUnverifiableAttestations;
|
|
34688
|
+
if (unverifiableFailed) {
|
|
34689
|
+
warnings.push(
|
|
34690
|
+
`${unverifiableCount} reputation attestation(s) have unknown signer public keys; pass --accept-unverifiable-attestations to import anyway`
|
|
34691
|
+
);
|
|
34692
|
+
}
|
|
33423
34693
|
return {
|
|
33424
34694
|
version: "1.1",
|
|
33425
|
-
passed: !reputationFailed && !identityFailed,
|
|
34695
|
+
passed: !reputationFailed && !identityFailed && !unverifiableFailed,
|
|
33426
34696
|
verified_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33427
34697
|
manifest_path: path.join(root, "manifest.json"),
|
|
33428
34698
|
manifest_hash: sha256Hex2(manifestBytes),
|
|
@@ -33439,10 +34709,10 @@ async function verifyExitBundle(bundleDir) {
|
|
|
33439
34709
|
identity,
|
|
33440
34710
|
audit,
|
|
33441
34711
|
reputation,
|
|
33442
|
-
failure_class: reputationFailed || identityFailed ? "other" : void 0
|
|
34712
|
+
failure_class: reputationFailed || identityFailed || unverifiableFailed ? "other" : void 0
|
|
33443
34713
|
};
|
|
33444
34714
|
}
|
|
33445
|
-
var PRIVATE_MATERIAL_KEYS;
|
|
34715
|
+
var InvalidExitBundleError, PRIVATE_MATERIAL_KEYS;
|
|
33446
34716
|
var init_verifier2 = __esm({
|
|
33447
34717
|
"src/exit/verifier.ts"() {
|
|
33448
34718
|
init_constants4();
|
|
@@ -33450,6 +34720,12 @@ var init_verifier2 = __esm({
|
|
|
33450
34720
|
init_encoding();
|
|
33451
34721
|
init_hashing();
|
|
33452
34722
|
init_canonical_json();
|
|
34723
|
+
InvalidExitBundleError = class extends Error {
|
|
34724
|
+
constructor(message) {
|
|
34725
|
+
super(message);
|
|
34726
|
+
this.name = "InvalidExitBundleError";
|
|
34727
|
+
}
|
|
34728
|
+
};
|
|
33453
34729
|
PRIVATE_MATERIAL_KEYS = /* @__PURE__ */ new Set([
|
|
33454
34730
|
"private_key",
|
|
33455
34731
|
"privatekey",
|
|
@@ -33755,7 +35031,7 @@ async function exportExitBundle(opts) {
|
|
|
33755
35031
|
),
|
|
33756
35032
|
artifacts_aggregate_hash_alg: "sha256",
|
|
33757
35033
|
export_approval_audit_id: exportApprovalAuditId,
|
|
33758
|
-
signature_scheme:
|
|
35034
|
+
signature_scheme: SIGNATURE_SCHEME_V12
|
|
33759
35035
|
};
|
|
33760
35036
|
const signature = sign(
|
|
33761
35037
|
canonicalizeToBytes(body),
|
|
@@ -33940,7 +35216,9 @@ async function stageArtifact(storage, namespace, key, value) {
|
|
|
33940
35216
|
await storage.write(namespace, key, jsonBytes(value));
|
|
33941
35217
|
}
|
|
33942
35218
|
async function importExitBundle(opts) {
|
|
33943
|
-
const verification = await verifyExitBundle(opts.bundleDir
|
|
35219
|
+
const verification = await verifyExitBundle(opts.bundleDir, {
|
|
35220
|
+
acceptUnverifiableAttestations: opts.acceptUnverifiableAttestations
|
|
35221
|
+
});
|
|
33944
35222
|
if (!verification.passed) {
|
|
33945
35223
|
return {
|
|
33946
35224
|
verified: false,
|
|
@@ -34036,6 +35314,23 @@ async function importExitBundle(opts) {
|
|
|
34036
35314
|
unsupported_artifacts: verification.unsupported_artifacts
|
|
34037
35315
|
};
|
|
34038
35316
|
}
|
|
35317
|
+
if (conflicts.public_identity_exists && !opts.forceRebind) {
|
|
35318
|
+
throw new ExitBundleImportError(
|
|
35319
|
+
"IDENTITY_OVERWRITE_REFUSED",
|
|
35320
|
+
"Importing this bundle would overwrite an existing fortress public identity. Pass forceRebind: true (CLI: --force-rebind) to confirm explicit replacement."
|
|
35321
|
+
);
|
|
35322
|
+
}
|
|
35323
|
+
if (conflicts.public_identity_exists && opts.forceRebind && identityArtifact) {
|
|
35324
|
+
opts.auditLog.append(
|
|
35325
|
+
"l1",
|
|
35326
|
+
"exit_bundle_force_rebind",
|
|
35327
|
+
identityArtifact.json.bundle.identity_id,
|
|
35328
|
+
{
|
|
35329
|
+
manifest_version: manifest.body.manifest_version,
|
|
35330
|
+
fortress_id: manifest.body.identity_binding.fortress_id
|
|
35331
|
+
}
|
|
35332
|
+
);
|
|
35333
|
+
}
|
|
34039
35334
|
const importId = importIdForManifest(manifest);
|
|
34040
35335
|
const stagedArtifacts = [];
|
|
34041
35336
|
if (identityArtifact) {
|
|
@@ -34146,7 +35441,7 @@ function exitBundleManifestShape() {
|
|
|
34146
35441
|
manifest_version: EXIT_BUNDLE_MANIFEST_VERSION,
|
|
34147
35442
|
artifacts: [...EXIT_BUNDLE_ARTIFACT_KINDS],
|
|
34148
35443
|
hash_alg: "sha256",
|
|
34149
|
-
signature_scheme:
|
|
35444
|
+
signature_scheme: SIGNATURE_SCHEME_V12,
|
|
34150
35445
|
required_top_level_file: "manifest.json",
|
|
34151
35446
|
artifact_paths: [
|
|
34152
35447
|
"artifacts/public_identity.json",
|
|
@@ -34159,7 +35454,7 @@ function exitBundleManifestShape() {
|
|
|
34159
35454
|
]
|
|
34160
35455
|
};
|
|
34161
35456
|
}
|
|
34162
|
-
var ARTIFACT_DIR, EXIT_IMPORT_NAMESPACE, EXIT_PUBLIC_IDENTITIES_NAMESPACE, EXIT_AUDIT_RECEIPTS_NAMESPACE, EXIT_POLICY_SETS_NAMESPACE, EXIT_COMMITMENTS_NAMESPACE, EXIT_PLACEHOLDER_METADATA_NAMESPACE, PRIVACY_PLACEHOLDER_NAMESPACE;
|
|
35457
|
+
var ARTIFACT_DIR, EXIT_IMPORT_NAMESPACE, EXIT_PUBLIC_IDENTITIES_NAMESPACE, EXIT_AUDIT_RECEIPTS_NAMESPACE, EXIT_POLICY_SETS_NAMESPACE, EXIT_COMMITMENTS_NAMESPACE, EXIT_PLACEHOLDER_METADATA_NAMESPACE, PRIVACY_PLACEHOLDER_NAMESPACE, ExitBundleImportError;
|
|
34163
35458
|
var init_bundle = __esm({
|
|
34164
35459
|
"src/exit/bundle.ts"() {
|
|
34165
35460
|
init_state_store();
|
|
@@ -34181,6 +35476,14 @@ var init_bundle = __esm({
|
|
|
34181
35476
|
EXIT_COMMITMENTS_NAMESPACE = "_exit_commitments";
|
|
34182
35477
|
EXIT_PLACEHOLDER_METADATA_NAMESPACE = "_exit_placeholder_metadata";
|
|
34183
35478
|
PRIVACY_PLACEHOLDER_NAMESPACE = "_privacy_placeholder_vault";
|
|
35479
|
+
ExitBundleImportError = class extends Error {
|
|
35480
|
+
code;
|
|
35481
|
+
constructor(code, message) {
|
|
35482
|
+
super(message);
|
|
35483
|
+
this.name = "ExitBundleImportError";
|
|
35484
|
+
this.code = code;
|
|
35485
|
+
}
|
|
35486
|
+
};
|
|
34184
35487
|
}
|
|
34185
35488
|
});
|
|
34186
35489
|
function write(stream, text) {
|
|
@@ -34279,6 +35582,11 @@ Options:
|
|
|
34279
35582
|
--destination-identity-id <id> Destination signer for re-keyed state
|
|
34280
35583
|
--state-namespace <name> Export a namespace; repeatable
|
|
34281
35584
|
--conflict <skip|overwrite|version>
|
|
35585
|
+
--force-rebind On import: explicitly replace an existing fortress
|
|
35586
|
+
public identity (Tier 1 confirmation)
|
|
35587
|
+
--accept-unverifiable-attestations
|
|
35588
|
+
On import: accept reputation attestations whose
|
|
35589
|
+
signer DID is not in the bundle (Tier 1 confirmation)
|
|
34282
35590
|
--json
|
|
34283
35591
|
--yes, -y Explicit non-interactive Tier 1 approval
|
|
34284
35592
|
--help, -h
|
|
@@ -34307,7 +35615,22 @@ async function runExitCommand(args) {
|
|
|
34307
35615
|
write(err, "Usage: sanctuary exit verify <dir>\n");
|
|
34308
35616
|
return 2;
|
|
34309
35617
|
}
|
|
34310
|
-
|
|
35618
|
+
let result;
|
|
35619
|
+
try {
|
|
35620
|
+
result = await verifyExitBundle(dir, {
|
|
35621
|
+
acceptUnverifiableAttestations: hasFlag(
|
|
35622
|
+
argv,
|
|
35623
|
+
"--accept-unverifiable-attestations"
|
|
35624
|
+
)
|
|
35625
|
+
});
|
|
35626
|
+
} catch (e) {
|
|
35627
|
+
if (e instanceof InvalidExitBundleError) {
|
|
35628
|
+
write(err, `Error: ${e.message}
|
|
35629
|
+
`);
|
|
35630
|
+
return 1;
|
|
35631
|
+
}
|
|
35632
|
+
throw e;
|
|
35633
|
+
}
|
|
34311
35634
|
if (json) {
|
|
34312
35635
|
write(out, JSON.stringify(result, null, 2) + "\n");
|
|
34313
35636
|
} else {
|
|
@@ -34385,9 +35708,15 @@ async function runExitCommand(args) {
|
|
|
34385
35708
|
return 2;
|
|
34386
35709
|
}
|
|
34387
35710
|
const activate = hasFlag(argv, "--activate");
|
|
35711
|
+
const forceRebind = hasFlag(argv, "--force-rebind");
|
|
35712
|
+
const acceptUnverifiableAttestations = hasFlag(
|
|
35713
|
+
argv,
|
|
35714
|
+
"--accept-unverifiable-attestations"
|
|
35715
|
+
);
|
|
34388
35716
|
if (activate) {
|
|
35717
|
+
const prompt2 = forceRebind ? "Tier 1 approval required: activate verified imported exit bundle AND replace the existing fortress public identity (force-rebind)?" : "Tier 1 approval required: activate verified imported exit bundle?";
|
|
34389
35718
|
const approved = await confirmTier1(
|
|
34390
|
-
|
|
35719
|
+
prompt2,
|
|
34391
35720
|
hasFlag(argv, "--yes") || hasFlag(argv, "-y"),
|
|
34392
35721
|
stdin,
|
|
34393
35722
|
err
|
|
@@ -34396,6 +35725,18 @@ async function runExitCommand(args) {
|
|
|
34396
35725
|
write(err, "Aborted.\n");
|
|
34397
35726
|
return 1;
|
|
34398
35727
|
}
|
|
35728
|
+
if (acceptUnverifiableAttestations) {
|
|
35729
|
+
const acceptApproved = await confirmTier1(
|
|
35730
|
+
"Tier 1 approval required: accept unverifiable reputation attestations on import?",
|
|
35731
|
+
hasFlag(argv, "--yes") || hasFlag(argv, "-y"),
|
|
35732
|
+
stdin,
|
|
35733
|
+
err
|
|
35734
|
+
);
|
|
35735
|
+
if (!acceptApproved) {
|
|
35736
|
+
write(err, "Aborted.\n");
|
|
35737
|
+
return 1;
|
|
35738
|
+
}
|
|
35739
|
+
}
|
|
34399
35740
|
}
|
|
34400
35741
|
const ctx = await openExitContext(argv, env);
|
|
34401
35742
|
const conflict = flagValue(argv, "--conflict") ?? "skip";
|
|
@@ -34403,19 +35744,31 @@ async function runExitCommand(args) {
|
|
|
34403
35744
|
write(err, "--conflict must be skip, overwrite, or version\n");
|
|
34404
35745
|
return 2;
|
|
34405
35746
|
}
|
|
34406
|
-
|
|
34407
|
-
|
|
34408
|
-
|
|
34409
|
-
|
|
34410
|
-
|
|
34411
|
-
|
|
34412
|
-
|
|
34413
|
-
|
|
34414
|
-
|
|
34415
|
-
|
|
34416
|
-
|
|
34417
|
-
|
|
34418
|
-
|
|
35747
|
+
let result;
|
|
35748
|
+
try {
|
|
35749
|
+
result = await importExitBundle({
|
|
35750
|
+
bundleDir: dir,
|
|
35751
|
+
storage: ctx.storage,
|
|
35752
|
+
masterKey: ctx.masterKey,
|
|
35753
|
+
identityManager: ctx.identityManager,
|
|
35754
|
+
auditLog: ctx.auditLog,
|
|
35755
|
+
reputationStore: ctx.reputationStore,
|
|
35756
|
+
activate,
|
|
35757
|
+
forceRebind,
|
|
35758
|
+
acceptUnverifiableAttestations,
|
|
35759
|
+
conflictResolution: conflict,
|
|
35760
|
+
sourcePassphrase: flagValue(argv, "--source-passphrase"),
|
|
35761
|
+
sourceRecoveryKey: flagValue(argv, "--source-recovery-key"),
|
|
35762
|
+
destinationSignerIdentityId: flagValue(argv, "--destination-identity-id")
|
|
35763
|
+
});
|
|
35764
|
+
} catch (e) {
|
|
35765
|
+
if (e instanceof InvalidExitBundleError) {
|
|
35766
|
+
write(err, `Error: ${e.message}
|
|
35767
|
+
`);
|
|
35768
|
+
return 1;
|
|
35769
|
+
}
|
|
35770
|
+
throw e;
|
|
35771
|
+
}
|
|
34419
35772
|
if (json) write(out, JSON.stringify(result, null, 2) + "\n");
|
|
34420
35773
|
else {
|
|
34421
35774
|
write(out, `verified: ${result.verified}
|
|
@@ -34470,6 +35823,7 @@ var init_cli = __esm({
|
|
|
34470
35823
|
// src/exit/index.ts
|
|
34471
35824
|
var exit_exports = {};
|
|
34472
35825
|
__export(exit_exports, {
|
|
35826
|
+
ExitBundleImportError: () => ExitBundleImportError,
|
|
34473
35827
|
exitBundleManifestShape: () => exitBundleManifestShape,
|
|
34474
35828
|
exportExitBundle: () => exportExitBundle,
|
|
34475
35829
|
importExitBundle: () => importExitBundle,
|
|
@@ -34526,11 +35880,11 @@ async function startDashboardServer(options) {
|
|
|
34526
35880
|
}
|
|
34527
35881
|
}
|
|
34528
35882
|
});
|
|
34529
|
-
await new Promise((
|
|
35883
|
+
await new Promise((resolve6, reject) => {
|
|
34530
35884
|
server.once("error", reject);
|
|
34531
35885
|
server.listen(port, host, () => {
|
|
34532
35886
|
server.off("error", reject);
|
|
34533
|
-
|
|
35887
|
+
resolve6();
|
|
34534
35888
|
});
|
|
34535
35889
|
});
|
|
34536
35890
|
const actualPort = (() => {
|
|
@@ -34543,8 +35897,8 @@ async function startDashboardServer(options) {
|
|
|
34543
35897
|
url,
|
|
34544
35898
|
port: actualPort,
|
|
34545
35899
|
host,
|
|
34546
|
-
stop: () => new Promise((
|
|
34547
|
-
server.close((err) => err ? reject(err) :
|
|
35900
|
+
stop: () => new Promise((resolve6, reject) => {
|
|
35901
|
+
server.close((err) => err ? reject(err) : resolve6());
|
|
34548
35902
|
}),
|
|
34549
35903
|
publish,
|
|
34550
35904
|
publishActivity: (entry) => publish({ type: "activity", data: entry }),
|
|
@@ -34692,7 +36046,7 @@ async function createSanctuaryServer(options) {
|
|
|
34692
36046
|
const hasKeyParams = existingNamespaces.some((e) => e.key === "key-params");
|
|
34693
36047
|
if (hasKeyParams) {
|
|
34694
36048
|
throw new Error(
|
|
34695
|
-
"Sanctuary:
|
|
36049
|
+
"Sanctuary: passphrase required.\n\nThe fortress at this path uses passphrase-mode key derivation.\nSet SANCTUARY_PASSPHRASE in your environment, or run\n'sanctuary export-passphrase' to retrieve it from the macOS Keychain."
|
|
34696
36050
|
);
|
|
34697
36051
|
}
|
|
34698
36052
|
masterKey = generateRandomKey();
|
|
@@ -35217,7 +36571,7 @@ Refusing to start the cocoon while the reset-history marker is unreadable.`
|
|
|
35217
36571
|
clientManager.configure(enabledServers).catch((err) => {
|
|
35218
36572
|
console.error(`[Sanctuary] Failed to configure upstream servers: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
35219
36573
|
});
|
|
35220
|
-
await new Promise((
|
|
36574
|
+
await new Promise((resolve6) => setTimeout(resolve6, 2e3));
|
|
35221
36575
|
const proxiedTools = proxyRouter.getProxiedTools();
|
|
35222
36576
|
if (proxiedTools.length > 0) {
|
|
35223
36577
|
allTools.push(...proxiedTools);
|
|
@@ -36056,8 +37410,8 @@ async function runWrap(options, deps = {}) {
|
|
|
36056
37410
|
passphraseValue = process.env.SANCTUARY_PASSPHRASE;
|
|
36057
37411
|
} else {
|
|
36058
37412
|
try {
|
|
36059
|
-
const
|
|
36060
|
-
const resolved = await
|
|
37413
|
+
const resolve6 = deps.resolvePassphrase ?? (() => getOrCreatePassphrase({ storagePath }));
|
|
37414
|
+
const resolved = await resolve6();
|
|
36061
37415
|
passphraseLocation = resolved.location;
|
|
36062
37416
|
passphraseSource = resolved.source;
|
|
36063
37417
|
passphraseValue = resolved.value;
|
|
@@ -36097,24 +37451,31 @@ async function runWrap(options, deps = {}) {
|
|
|
36097
37451
|
}
|
|
36098
37452
|
await promises.mkdir(storagePath, { recursive: true, mode: 448 });
|
|
36099
37453
|
if (passphraseSource === "generated" && passphraseValue !== void 0) {
|
|
36100
|
-
|
|
36101
|
-
|
|
36102
|
-
|
|
36103
|
-
|
|
36104
|
-
|
|
36105
|
-
|
|
36106
|
-
|
|
36107
|
-
|
|
36108
|
-
|
|
36109
|
-
|
|
36110
|
-
|
|
36111
|
-
if (err instanceof PassphraseConfirmationDeclinedError || err instanceof PassphraseConfirmationNonInteractiveError) {
|
|
36112
|
-
console.error(`
|
|
37454
|
+
if (options.writePassphraseBackup) {
|
|
37455
|
+
try {
|
|
37456
|
+
await disclosePassphrase({
|
|
37457
|
+
passphrase: passphraseValue,
|
|
37458
|
+
storagePath: path.dirname(options.writePassphraseBackup),
|
|
37459
|
+
fortressId: fortressIdFromStoragePath(storagePath),
|
|
37460
|
+
mode: options.noOpen || process.stdin.isTTY !== true ? "no-confirm" : "interactive"
|
|
37461
|
+
});
|
|
37462
|
+
} catch (err) {
|
|
37463
|
+
if (err instanceof PassphraseConfirmationDeclinedError || err instanceof PassphraseConfirmationNonInteractiveError) {
|
|
37464
|
+
console.error(`
|
|
36113
37465
|
Sanctuary wrap: ${err.message}
|
|
36114
37466
|
`);
|
|
36115
|
-
|
|
37467
|
+
process.exit(2);
|
|
37468
|
+
}
|
|
37469
|
+
throw err;
|
|
36116
37470
|
}
|
|
36117
|
-
|
|
37471
|
+
} else {
|
|
37472
|
+
process.stderr.write(
|
|
37473
|
+
`
|
|
37474
|
+
Passphrase stored in macOS Keychain.
|
|
37475
|
+
Run 'sanctuary export-passphrase' to retrieve it.
|
|
37476
|
+
To write a plaintext backup: sanctuary wrap ... --write-passphrase-backup <path>
|
|
37477
|
+
`
|
|
37478
|
+
);
|
|
36118
37479
|
}
|
|
36119
37480
|
}
|
|
36120
37481
|
const profile = createWrapProfile(upstreamServers);
|
|
@@ -36205,6 +37566,8 @@ async function runWrap(options, deps = {}) {
|
|
|
36205
37566
|
serverCount: upstreamServers.length});
|
|
36206
37567
|
return;
|
|
36207
37568
|
}
|
|
37569
|
+
let intelligenceHealthy;
|
|
37570
|
+
let intelligenceError;
|
|
36208
37571
|
const authToken = generateAuthToken();
|
|
36209
37572
|
const startFn = deps.startDashboard ?? ((opts) => startDashboard({
|
|
36210
37573
|
port: opts.port,
|
|
@@ -36240,6 +37603,30 @@ async function runWrap(options, deps = {}) {
|
|
|
36240
37603
|
);
|
|
36241
37604
|
}
|
|
36242
37605
|
const wrapAuditLog = new AuditLog(v11Storage, derived.key);
|
|
37606
|
+
try {
|
|
37607
|
+
const { IdentityManager: IdentityManager2 } = await Promise.resolve().then(() => (init_tools(), tools_exports));
|
|
37608
|
+
const { createIdentity: createIdentity2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
37609
|
+
const { derivePurposeKey: derivePurposeKey2 } = await Promise.resolve().then(() => (init_key_derivation(), key_derivation_exports));
|
|
37610
|
+
const identityMgr = new IdentityManager2(v11Storage, derived.key);
|
|
37611
|
+
const loadResult = await identityMgr.load();
|
|
37612
|
+
if (loadResult.loaded === 0) {
|
|
37613
|
+
const identityEncKey = derivePurposeKey2(derived.key, "identity-encryption");
|
|
37614
|
+
const { storedIdentity, publicIdentity } = createIdentity2(
|
|
37615
|
+
"default",
|
|
37616
|
+
identityEncKey,
|
|
37617
|
+
"passphrase"
|
|
37618
|
+
);
|
|
37619
|
+
await identityMgr.save(storedIdentity);
|
|
37620
|
+
wrapAuditLog.append("l1", "identity_create", publicIdentity.identity_id, {
|
|
37621
|
+
label: "default",
|
|
37622
|
+
source: "wrap-auto"
|
|
37623
|
+
});
|
|
37624
|
+
}
|
|
37625
|
+
} catch (err) {
|
|
37626
|
+
console.error(
|
|
37627
|
+
` Note: default identity not created at wrap time (${err.message}).`
|
|
37628
|
+
);
|
|
37629
|
+
}
|
|
36243
37630
|
let wrapIntelligenceSelector;
|
|
36244
37631
|
try {
|
|
36245
37632
|
wrapIntelligenceSelector = new SubstrateSelector({
|
|
@@ -36249,7 +37636,10 @@ async function runWrap(options, deps = {}) {
|
|
|
36249
37636
|
identityId: `fortress:${storagePath}`
|
|
36250
37637
|
});
|
|
36251
37638
|
await wrapIntelligenceSelector.load();
|
|
37639
|
+
intelligenceHealthy = true;
|
|
36252
37640
|
} catch (err) {
|
|
37641
|
+
intelligenceHealthy = false;
|
|
37642
|
+
intelligenceError = err.message;
|
|
36253
37643
|
console.error(
|
|
36254
37644
|
` Note: Intelligence panel unavailable on wrap URL (${err.message}).`
|
|
36255
37645
|
);
|
|
@@ -36317,7 +37707,10 @@ async function runWrap(options, deps = {}) {
|
|
|
36317
37707
|
toolCount: countUpstreamTools(upstreamServers),
|
|
36318
37708
|
serverCount: upstreamServers.length,
|
|
36319
37709
|
dashboardUrl,
|
|
36320
|
-
browserOpened: !options.noOpen
|
|
37710
|
+
browserOpened: !options.noOpen,
|
|
37711
|
+
intelligenceHealthy,
|
|
37712
|
+
intelligenceError
|
|
37713
|
+
});
|
|
36321
37714
|
}
|
|
36322
37715
|
async function runCocoon(options) {
|
|
36323
37716
|
console.error(
|
|
@@ -36373,12 +37766,12 @@ async function defaultOpenBrowser(url) {
|
|
|
36373
37766
|
cmd = "xdg-open";
|
|
36374
37767
|
args = [url];
|
|
36375
37768
|
}
|
|
36376
|
-
await new Promise((
|
|
37769
|
+
await new Promise((resolve6) => {
|
|
36377
37770
|
const child = child_process.spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
36378
|
-
child.on("error", () =>
|
|
37771
|
+
child.on("error", () => resolve6());
|
|
36379
37772
|
child.on("spawn", () => {
|
|
36380
37773
|
child.unref();
|
|
36381
|
-
|
|
37774
|
+
resolve6();
|
|
36382
37775
|
});
|
|
36383
37776
|
});
|
|
36384
37777
|
}
|
|
@@ -36404,7 +37797,15 @@ function formatWrapSuccess(info) {
|
|
|
36404
37797
|
lines.push(` ${d("(browser auto-open suppressed)")}`);
|
|
36405
37798
|
}
|
|
36406
37799
|
lines.push("");
|
|
36407
|
-
|
|
37800
|
+
const l2Status = info.intelligenceHealthy === false ? "L2 Degraded (intelligence disabled)" : "L2 Degraded (no TEE)";
|
|
37801
|
+
lines.push(` ${b("Your agent is protected.")} L1 Full / ${l2Status} / L3 Full / L4 Full.`);
|
|
37802
|
+
if (info.intelligenceHealthy === false && info.intelligenceError) {
|
|
37803
|
+
const w = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
37804
|
+
lines.push("");
|
|
37805
|
+
lines.push(` ${w("\u26A0")} L2 intelligence disabled: ${info.intelligenceError}`);
|
|
37806
|
+
lines.push(` Concierge chat and substrate-driven explanations will not work until this is resolved.`);
|
|
37807
|
+
lines.push(` Run 'sanctuary intelligence diagnose' to inspect substrate config.`);
|
|
37808
|
+
}
|
|
36408
37809
|
lines.push("");
|
|
36409
37810
|
return lines.join("\n");
|
|
36410
37811
|
}
|
|
@@ -36428,9 +37829,16 @@ function formatWrapSuccessNoDashboard(info) {
|
|
|
36428
37829
|
` ${d("Dashboard spawn skipped per --no-dashboard. Run `sanctuary dashboard` separately for a persistent dashboard.")}`
|
|
36429
37830
|
);
|
|
36430
37831
|
lines.push("");
|
|
37832
|
+
const l2Status = info.intelligenceHealthy === false ? "L2 Degraded (intelligence disabled)" : "L2 Degraded (no TEE)";
|
|
36431
37833
|
lines.push(
|
|
36432
|
-
` ${b("Your agent is protected.")} L1 Full /
|
|
37834
|
+
` ${b("Your agent is protected.")} L1 Full / ${l2Status} / L3 Full / L4 Full.`
|
|
36433
37835
|
);
|
|
37836
|
+
if (info.intelligenceHealthy === false && info.intelligenceError) {
|
|
37837
|
+
const w = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
37838
|
+
lines.push("");
|
|
37839
|
+
lines.push(` ${w("\u26A0")} L2 intelligence disabled: ${info.intelligenceError}`);
|
|
37840
|
+
lines.push(` Run 'sanctuary intelligence diagnose' to inspect substrate config.`);
|
|
37841
|
+
}
|
|
36434
37842
|
lines.push("");
|
|
36435
37843
|
return lines.join("\n");
|
|
36436
37844
|
}
|
|
@@ -36630,7 +38038,22 @@ function rawConfigContainsSanctuary(raw, agentPlatform) {
|
|
|
36630
38038
|
function parseWrapArgs(argv) {
|
|
36631
38039
|
const options = {};
|
|
36632
38040
|
for (let i = 0; i < argv.length; i++) {
|
|
36633
|
-
|
|
38041
|
+
const arg = argv[i];
|
|
38042
|
+
if (!arg.startsWith("-")) {
|
|
38043
|
+
const suggestion = WRAP_HARNESS_FLAGS.find(
|
|
38044
|
+
(f) => f.replace(/^--/, "") === arg
|
|
38045
|
+
);
|
|
38046
|
+
const hint = suggestion ? ` Did you mean ${suggestion}?` : "";
|
|
38047
|
+
throw new Error(
|
|
38048
|
+
`Unrecognized argument '${arg}'.${hint} Run 'sanctuary wrap --help' for valid flags.`
|
|
38049
|
+
);
|
|
38050
|
+
}
|
|
38051
|
+
if (!WRAP_BOOLEAN_FLAGS.has(arg) && !WRAP_VALUE_FLAGS.has(arg)) {
|
|
38052
|
+
throw new Error(
|
|
38053
|
+
`Unrecognized flag '${arg}'. Run 'sanctuary wrap --help' for valid flags.`
|
|
38054
|
+
);
|
|
38055
|
+
}
|
|
38056
|
+
switch (arg) {
|
|
36634
38057
|
case "--wrap":
|
|
36635
38058
|
options.wrap = argv[++i];
|
|
36636
38059
|
break;
|
|
@@ -36673,6 +38096,9 @@ function parseWrapArgs(argv) {
|
|
|
36673
38096
|
case "--dev-dist":
|
|
36674
38097
|
options.devDist = argv[++i];
|
|
36675
38098
|
break;
|
|
38099
|
+
case "--write-passphrase-backup":
|
|
38100
|
+
options.writePassphraseBackup = argv[++i];
|
|
38101
|
+
break;
|
|
36676
38102
|
case "--help":
|
|
36677
38103
|
case "-h":
|
|
36678
38104
|
printWrapHelp();
|
|
@@ -36732,7 +38158,7 @@ function printWrapHelp() {
|
|
|
36732
38158
|
5. Every tool call is logged, scanned, and tier-gated
|
|
36733
38159
|
`);
|
|
36734
38160
|
}
|
|
36735
|
-
var COCOON_GOVERNOR_DEFAULTS, PORT_FALLBACK_ATTEMPTS, parseCocoonArgs;
|
|
38161
|
+
var COCOON_GOVERNOR_DEFAULTS, PORT_FALLBACK_ATTEMPTS, WRAP_VALUE_FLAGS, WRAP_BOOLEAN_FLAGS, WRAP_HARNESS_FLAGS, parseCocoonArgs;
|
|
36736
38162
|
var init_cli2 = __esm({
|
|
36737
38163
|
"src/cocoon/cli.ts"() {
|
|
36738
38164
|
init_config_reader();
|
|
@@ -36755,6 +38181,28 @@ var init_cli2 = __esm({
|
|
|
36755
38181
|
lifetime_limit: 1e3
|
|
36756
38182
|
};
|
|
36757
38183
|
PORT_FALLBACK_ATTEMPTS = 20;
|
|
38184
|
+
WRAP_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
38185
|
+
"--wrap",
|
|
38186
|
+
"--passphrase",
|
|
38187
|
+
"--port",
|
|
38188
|
+
"--fortress",
|
|
38189
|
+
"--dev-dist",
|
|
38190
|
+
"--write-passphrase-backup"
|
|
38191
|
+
]);
|
|
38192
|
+
WRAP_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
38193
|
+
"--openclaw",
|
|
38194
|
+
"--hermes",
|
|
38195
|
+
"--claude-code",
|
|
38196
|
+
"--cursor",
|
|
38197
|
+
"--cline",
|
|
38198
|
+
"--unwrap",
|
|
38199
|
+
"--dry-run",
|
|
38200
|
+
"--no-open",
|
|
38201
|
+
"--no-dashboard",
|
|
38202
|
+
"--help",
|
|
38203
|
+
"-h"
|
|
38204
|
+
]);
|
|
38205
|
+
WRAP_HARNESS_FLAGS = ["--openclaw", "--hermes", "--claude-code", "--cursor", "--cline"];
|
|
36758
38206
|
parseCocoonArgs = parseWrapArgs;
|
|
36759
38207
|
}
|
|
36760
38208
|
});
|
|
@@ -37652,7 +39100,7 @@ var init_backend_interface = __esm({
|
|
|
37652
39100
|
}
|
|
37653
39101
|
});
|
|
37654
39102
|
async function runSecurity(args, input) {
|
|
37655
|
-
return new Promise((
|
|
39103
|
+
return new Promise((resolve6, reject) => {
|
|
37656
39104
|
const child = child_process.spawn(SECURITY_BIN, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
37657
39105
|
let stdout = "";
|
|
37658
39106
|
let stderr = "";
|
|
@@ -37674,7 +39122,7 @@ async function runSecurity(args, input) {
|
|
|
37674
39122
|
reject(err);
|
|
37675
39123
|
});
|
|
37676
39124
|
child.on("close", (code) => {
|
|
37677
|
-
|
|
39125
|
+
resolve6({ stdout, stderr, code: code ?? -1 });
|
|
37678
39126
|
});
|
|
37679
39127
|
if (input !== void 0) {
|
|
37680
39128
|
child.stdin.write(input);
|
|
@@ -38750,7 +40198,7 @@ async function readValue(stdin, prompt2) {
|
|
|
38750
40198
|
return await readFirstLine(stdin);
|
|
38751
40199
|
}
|
|
38752
40200
|
async function readFirstLine(stdin) {
|
|
38753
|
-
return new Promise((
|
|
40201
|
+
return new Promise((resolve6, reject) => {
|
|
38754
40202
|
const rl = readline.createInterface({ input: stdin });
|
|
38755
40203
|
let resolved = false;
|
|
38756
40204
|
const finish = (value) => {
|
|
@@ -38761,7 +40209,7 @@ async function readFirstLine(stdin) {
|
|
|
38761
40209
|
rl.close();
|
|
38762
40210
|
} catch {
|
|
38763
40211
|
}
|
|
38764
|
-
|
|
40212
|
+
resolve6(value);
|
|
38765
40213
|
};
|
|
38766
40214
|
const deadline = setTimeout(() => {
|
|
38767
40215
|
finish("");
|
|
@@ -38780,7 +40228,7 @@ async function promptSilently(stdin, prompt2) {
|
|
|
38780
40228
|
process.stderr.write(`${prompt2}: `);
|
|
38781
40229
|
stdin.setRawMode?.(true);
|
|
38782
40230
|
stdin.resume();
|
|
38783
|
-
return await new Promise((
|
|
40231
|
+
return await new Promise((resolve6) => {
|
|
38784
40232
|
let buf = "";
|
|
38785
40233
|
const onData = (chunk) => {
|
|
38786
40234
|
const s = chunk.toString("utf8");
|
|
@@ -38790,7 +40238,7 @@ async function promptSilently(stdin, prompt2) {
|
|
|
38790
40238
|
stdin.pause();
|
|
38791
40239
|
stdin.off("data", onData);
|
|
38792
40240
|
process.stderr.write("\n");
|
|
38793
|
-
|
|
40241
|
+
resolve6(buf);
|
|
38794
40242
|
return;
|
|
38795
40243
|
}
|
|
38796
40244
|
if (ch === "") {
|
|
@@ -39063,7 +40511,7 @@ async function probeTenantDashboard(tenant, options = {}) {
|
|
|
39063
40511
|
return { running: false, status: null, reason: "no runtime.json" };
|
|
39064
40512
|
}
|
|
39065
40513
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS4;
|
|
39066
|
-
return await new Promise((
|
|
40514
|
+
return await new Promise((resolve6) => {
|
|
39067
40515
|
const req = http.get(
|
|
39068
40516
|
{
|
|
39069
40517
|
host: rt.dashboard_host,
|
|
@@ -39075,9 +40523,9 @@ async function probeTenantDashboard(tenant, options = {}) {
|
|
|
39075
40523
|
res.resume();
|
|
39076
40524
|
const status = res.statusCode ?? 0;
|
|
39077
40525
|
if (status > 0 && status < 500) {
|
|
39078
|
-
|
|
40526
|
+
resolve6({ running: true, status, reason: null });
|
|
39079
40527
|
} else {
|
|
39080
|
-
|
|
40528
|
+
resolve6({
|
|
39081
40529
|
running: false,
|
|
39082
40530
|
status,
|
|
39083
40531
|
reason: `dashboard returned ${status}`
|
|
@@ -39087,10 +40535,10 @@ async function probeTenantDashboard(tenant, options = {}) {
|
|
|
39087
40535
|
);
|
|
39088
40536
|
req.on("timeout", () => {
|
|
39089
40537
|
req.destroy();
|
|
39090
|
-
|
|
40538
|
+
resolve6({ running: false, status: null, reason: "timeout" });
|
|
39091
40539
|
});
|
|
39092
40540
|
req.on("error", (err) => {
|
|
39093
|
-
|
|
40541
|
+
resolve6({
|
|
39094
40542
|
running: false,
|
|
39095
40543
|
status: null,
|
|
39096
40544
|
reason: err.code ?? err.message
|
|
@@ -39752,7 +41200,7 @@ async function prompt(lines, err, question) {
|
|
|
39752
41200
|
return await lines.next();
|
|
39753
41201
|
}
|
|
39754
41202
|
async function defaultExec2(cmd, args) {
|
|
39755
|
-
return await new Promise((
|
|
41203
|
+
return await new Promise((resolve6, reject) => {
|
|
39756
41204
|
const child = child_process.spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
39757
41205
|
let stdout = "";
|
|
39758
41206
|
let stderr = "";
|
|
@@ -39763,7 +41211,7 @@ async function defaultExec2(cmd, args) {
|
|
|
39763
41211
|
stderr += d.toString();
|
|
39764
41212
|
});
|
|
39765
41213
|
child.on("error", reject);
|
|
39766
|
-
child.on("close", (code) =>
|
|
41214
|
+
child.on("close", (code) => resolve6({ stdout, stderr, code }));
|
|
39767
41215
|
});
|
|
39768
41216
|
}
|
|
39769
41217
|
var LineReader;
|
|
@@ -39799,8 +41247,8 @@ var init_reset_passphrase = __esm({
|
|
|
39799
41247
|
return Promise.resolve(this.queue.shift());
|
|
39800
41248
|
}
|
|
39801
41249
|
if (this.closed) return Promise.resolve("");
|
|
39802
|
-
return new Promise((
|
|
39803
|
-
this.waiters.push(
|
|
41250
|
+
return new Promise((resolve6) => {
|
|
41251
|
+
this.waiters.push(resolve6);
|
|
39804
41252
|
});
|
|
39805
41253
|
}
|
|
39806
41254
|
close() {
|
|
@@ -39815,6 +41263,100 @@ var init_reset_passphrase = __esm({
|
|
|
39815
41263
|
}
|
|
39816
41264
|
});
|
|
39817
41265
|
|
|
41266
|
+
// src/cli/intelligence.ts
|
|
41267
|
+
var intelligence_exports = {};
|
|
41268
|
+
__export(intelligence_exports, {
|
|
41269
|
+
runIntelligenceCommand: () => runIntelligenceCommand
|
|
41270
|
+
});
|
|
41271
|
+
async function runIntelligenceCommand(opts) {
|
|
41272
|
+
const subcommand = opts.argv[0];
|
|
41273
|
+
if (subcommand === "diagnose" || subcommand === void 0) {
|
|
41274
|
+
return runDiagnose();
|
|
41275
|
+
}
|
|
41276
|
+
if (subcommand === "--help" || subcommand === "-h") {
|
|
41277
|
+
printHelp2();
|
|
41278
|
+
return 0;
|
|
41279
|
+
}
|
|
41280
|
+
console.error(`Unknown intelligence subcommand: ${subcommand}`);
|
|
41281
|
+
printHelp2();
|
|
41282
|
+
return 2;
|
|
41283
|
+
}
|
|
41284
|
+
function printHelp2() {
|
|
41285
|
+
console.error(`
|
|
41286
|
+
Usage: sanctuary intelligence <subcommand>
|
|
41287
|
+
|
|
41288
|
+
Subcommands:
|
|
41289
|
+
diagnose Print intelligence substrate config and last error.
|
|
41290
|
+
--help Show this help.
|
|
41291
|
+
`);
|
|
41292
|
+
}
|
|
41293
|
+
async function runDiagnose() {
|
|
41294
|
+
const storagePath = path.resolve(
|
|
41295
|
+
process.env.SANCTUARY_STORAGE_PATH ?? process.env.SANCTUARY_FORTRESS_PATH ?? `${process.env.HOME}/.sanctuary`
|
|
41296
|
+
);
|
|
41297
|
+
console.error(`Intelligence substrate diagnostics`);
|
|
41298
|
+
console.error(`Fortress: ${storagePath}`);
|
|
41299
|
+
console.error("");
|
|
41300
|
+
const intelligenceDir = path.resolve(storagePath, "state", "_intelligence");
|
|
41301
|
+
if (!fs.existsSync(intelligenceDir)) {
|
|
41302
|
+
console.error(
|
|
41303
|
+
`No intelligence config directory found at ${intelligenceDir}.`
|
|
41304
|
+
);
|
|
41305
|
+
console.error(
|
|
41306
|
+
"Intelligence substrate may not have been initialized yet."
|
|
41307
|
+
);
|
|
41308
|
+
console.error(
|
|
41309
|
+
"Ensure at least one substrate API key is set in your environment:"
|
|
41310
|
+
);
|
|
41311
|
+
console.error(
|
|
41312
|
+
" ANTHROPIC_API_KEY, OPENAI_API_KEY, VENICE_API_KEY, or OLLAMA_HOST"
|
|
41313
|
+
);
|
|
41314
|
+
return 1;
|
|
41315
|
+
}
|
|
41316
|
+
try {
|
|
41317
|
+
const entries = fs.readdirSync(intelligenceDir);
|
|
41318
|
+
console.error(`Intelligence config entries: ${entries.length}`);
|
|
41319
|
+
for (const entry of entries) {
|
|
41320
|
+
console.error(` ${entry}`);
|
|
41321
|
+
}
|
|
41322
|
+
} catch {
|
|
41323
|
+
console.error(`Could not read intelligence config directory.`);
|
|
41324
|
+
}
|
|
41325
|
+
const auditDir = path.resolve(storagePath, "state", "_audit");
|
|
41326
|
+
if (fs.existsSync(auditDir)) {
|
|
41327
|
+
try {
|
|
41328
|
+
const auditFiles = fs.readdirSync(auditDir).sort().reverse();
|
|
41329
|
+
const recentFiles = auditFiles.slice(0, 20);
|
|
41330
|
+
console.error("");
|
|
41331
|
+
console.error(`Recent audit entries (${recentFiles.length} of ${auditFiles.length}):`);
|
|
41332
|
+
for (const file of recentFiles) {
|
|
41333
|
+
console.error(` ${file}`);
|
|
41334
|
+
}
|
|
41335
|
+
} catch {
|
|
41336
|
+
console.error(`Could not read audit directory.`);
|
|
41337
|
+
}
|
|
41338
|
+
}
|
|
41339
|
+
console.error("");
|
|
41340
|
+
console.error("Substrate environment check:");
|
|
41341
|
+
const keys = [
|
|
41342
|
+
"ANTHROPIC_API_KEY",
|
|
41343
|
+
"OPENAI_API_KEY",
|
|
41344
|
+
"VENICE_API_KEY",
|
|
41345
|
+
"OLLAMA_HOST"
|
|
41346
|
+
];
|
|
41347
|
+
for (const key of keys) {
|
|
41348
|
+
const val = process.env[key];
|
|
41349
|
+
console.error(
|
|
41350
|
+
` ${key}: ${val ? `set (${val.slice(0, 4)}...)` : "not set"}`
|
|
41351
|
+
);
|
|
41352
|
+
}
|
|
41353
|
+
return 0;
|
|
41354
|
+
}
|
|
41355
|
+
var init_intelligence = __esm({
|
|
41356
|
+
"src/cli/intelligence.ts"() {
|
|
41357
|
+
}
|
|
41358
|
+
});
|
|
41359
|
+
|
|
39818
41360
|
// src/mcp/broker-server.ts
|
|
39819
41361
|
var broker_server_exports = {};
|
|
39820
41362
|
__export(broker_server_exports, {
|
|
@@ -40105,11 +41647,11 @@ async function startMultiDashboardServer(options = {}) {
|
|
|
40105
41647
|
}
|
|
40106
41648
|
}
|
|
40107
41649
|
});
|
|
40108
|
-
await new Promise((
|
|
41650
|
+
await new Promise((resolve6, reject) => {
|
|
40109
41651
|
server.once("error", reject);
|
|
40110
41652
|
server.listen(port, host, () => {
|
|
40111
41653
|
server.off("error", reject);
|
|
40112
|
-
|
|
41654
|
+
resolve6();
|
|
40113
41655
|
});
|
|
40114
41656
|
});
|
|
40115
41657
|
const addr = server.address();
|
|
@@ -40118,8 +41660,8 @@ async function startMultiDashboardServer(options = {}) {
|
|
|
40118
41660
|
url: `http://${host}:${actualPort}`,
|
|
40119
41661
|
port: actualPort,
|
|
40120
41662
|
host,
|
|
40121
|
-
stop: () => new Promise((
|
|
40122
|
-
server.close((err) => err ? reject(err) :
|
|
41663
|
+
stop: () => new Promise((resolve6, reject) => {
|
|
41664
|
+
server.close((err) => err ? reject(err) : resolve6());
|
|
40123
41665
|
})
|
|
40124
41666
|
};
|
|
40125
41667
|
}
|
|
@@ -40519,7 +42061,7 @@ function formatUpdateMessage(current, latest) {
|
|
|
40519
42061
|
return `[Sanctuary] Update available: ${current} \u2192 ${latest}. Run: npx @sanctuary-framework/mcp-server@latest`;
|
|
40520
42062
|
}
|
|
40521
42063
|
function fetchLatestVersion(currentVersion) {
|
|
40522
|
-
return new Promise((
|
|
42064
|
+
return new Promise((resolve6) => {
|
|
40523
42065
|
const req = https.get(
|
|
40524
42066
|
REGISTRY_URL,
|
|
40525
42067
|
{
|
|
@@ -40529,7 +42071,7 @@ function fetchLatestVersion(currentVersion) {
|
|
|
40529
42071
|
(res) => {
|
|
40530
42072
|
if (res.statusCode !== 200) {
|
|
40531
42073
|
res.resume();
|
|
40532
|
-
|
|
42074
|
+
resolve6(null);
|
|
40533
42075
|
return;
|
|
40534
42076
|
}
|
|
40535
42077
|
let data = "";
|
|
@@ -40538,7 +42080,7 @@ function fetchLatestVersion(currentVersion) {
|
|
|
40538
42080
|
data += chunk;
|
|
40539
42081
|
if (data.length > 32768) {
|
|
40540
42082
|
res.destroy();
|
|
40541
|
-
|
|
42083
|
+
resolve6(null);
|
|
40542
42084
|
}
|
|
40543
42085
|
});
|
|
40544
42086
|
res.on("end", () => {
|
|
@@ -40546,20 +42088,20 @@ function fetchLatestVersion(currentVersion) {
|
|
|
40546
42088
|
const json = JSON.parse(data);
|
|
40547
42089
|
const latest = json.version;
|
|
40548
42090
|
if (typeof latest === "string" && isNewerVersion(currentVersion, latest)) {
|
|
40549
|
-
|
|
42091
|
+
resolve6(latest);
|
|
40550
42092
|
} else {
|
|
40551
|
-
|
|
42093
|
+
resolve6(null);
|
|
40552
42094
|
}
|
|
40553
42095
|
} catch {
|
|
40554
|
-
|
|
42096
|
+
resolve6(null);
|
|
40555
42097
|
}
|
|
40556
42098
|
});
|
|
40557
42099
|
}
|
|
40558
42100
|
);
|
|
40559
|
-
req.on("error", () =>
|
|
42101
|
+
req.on("error", () => resolve6(null));
|
|
40560
42102
|
req.on("timeout", () => {
|
|
40561
42103
|
req.destroy();
|
|
40562
|
-
|
|
42104
|
+
resolve6(null);
|
|
40563
42105
|
});
|
|
40564
42106
|
});
|
|
40565
42107
|
}
|
|
@@ -40652,6 +42194,11 @@ async function main() {
|
|
|
40652
42194
|
const code = await runResetPassphraseCommand2({ argv: args.slice(1) });
|
|
40653
42195
|
process.exit(code);
|
|
40654
42196
|
}
|
|
42197
|
+
if (args[0] === "intelligence") {
|
|
42198
|
+
const { runIntelligenceCommand: runIntelligenceCommand2 } = await Promise.resolve().then(() => (init_intelligence(), intelligence_exports));
|
|
42199
|
+
const code = await runIntelligenceCommand2({ argv: args.slice(1) });
|
|
42200
|
+
process.exit(code);
|
|
42201
|
+
}
|
|
40655
42202
|
if (args[0] === "broker-server") {
|
|
40656
42203
|
const { openBroker: openBroker2 } = await Promise.resolve().then(() => (init_open(), open_exports));
|
|
40657
42204
|
const { createBrokerMcpServer: createBrokerMcpServer2 } = await Promise.resolve().then(() => (init_broker_server(), broker_server_exports));
|
|
@@ -40675,7 +42222,7 @@ async function main() {
|
|
|
40675
42222
|
);
|
|
40676
42223
|
passphrase = args[++i];
|
|
40677
42224
|
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
40678
|
-
|
|
42225
|
+
printHelp3();
|
|
40679
42226
|
process.exit(0);
|
|
40680
42227
|
} else if (args[i] === "--version" || args[i] === "-v") {
|
|
40681
42228
|
console.log(`@sanctuary-framework/mcp-server ${PKG_VERSION4}`);
|
|
@@ -40820,7 +42367,7 @@ async function runExportPassphrase(args) {
|
|
|
40820
42367
|
}
|
|
40821
42368
|
process.stdout.write(stored.value + "\n");
|
|
40822
42369
|
}
|
|
40823
|
-
function
|
|
42370
|
+
function printHelp3() {
|
|
40824
42371
|
console.log(`
|
|
40825
42372
|
@sanctuary-framework/mcp-server v${PKG_VERSION4}
|
|
40826
42373
|
|