@sanctuary-framework/mcp-server 1.2.0 → 1.2.2
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 +1952 -405
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1953 -406
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1646 -305
- 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 +1646 -306
- 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.js
CHANGED
|
@@ -12,7 +12,7 @@ import { argon2id } from 'hash-wasm';
|
|
|
12
12
|
import { hkdf } from '@noble/hashes/hkdf';
|
|
13
13
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
14
14
|
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
15
|
-
import { existsSync, readFileSync, statSync, mkdirSync, writeFileSync, renameSync, chmodSync } from 'fs';
|
|
15
|
+
import { existsSync, readFileSync, statSync, mkdirSync, writeFileSync, renameSync, chmodSync, readdirSync } from 'fs';
|
|
16
16
|
import { fileURLToPath } from 'url';
|
|
17
17
|
import { exec, execSync, spawn } from 'child_process';
|
|
18
18
|
import { createServer as createServer$2, get as get$1 } from 'http';
|
|
@@ -391,23 +391,44 @@ var init_random = __esm({
|
|
|
391
391
|
"src/core/random.ts"() {
|
|
392
392
|
}
|
|
393
393
|
});
|
|
394
|
-
|
|
394
|
+
function bijectiveEncode(name) {
|
|
395
|
+
return name.replace(
|
|
396
|
+
SAFE_CHARS,
|
|
397
|
+
(ch) => "!" + ch.charCodeAt(0).toString(16).padStart(2, "0").toUpperCase()
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
function legacyNamespaceSanitize(name) {
|
|
401
|
+
return name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
402
|
+
}
|
|
403
|
+
function legacyKeySanitize(name) {
|
|
404
|
+
return name.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
405
|
+
}
|
|
406
|
+
var SAFE_CHARS, FilesystemStorage;
|
|
395
407
|
var init_filesystem = __esm({
|
|
396
408
|
"src/storage/filesystem.ts"() {
|
|
397
409
|
init_random();
|
|
410
|
+
SAFE_CHARS = /[^A-Za-z0-9_.\-]/g;
|
|
398
411
|
FilesystemStorage = class {
|
|
399
412
|
basePath;
|
|
400
413
|
constructor(basePath) {
|
|
401
414
|
this.basePath = basePath;
|
|
402
415
|
}
|
|
403
416
|
entryPath(namespace, key) {
|
|
404
|
-
const safeNamespace = namespace
|
|
405
|
-
const safeKey = key
|
|
417
|
+
const safeNamespace = bijectiveEncode(namespace);
|
|
418
|
+
const safeKey = bijectiveEncode(key);
|
|
406
419
|
return join(this.basePath, safeNamespace, `${safeKey}.enc`);
|
|
407
420
|
}
|
|
408
421
|
namespacePath(namespace) {
|
|
409
|
-
|
|
410
|
-
|
|
422
|
+
return join(this.basePath, bijectiveEncode(namespace));
|
|
423
|
+
}
|
|
424
|
+
// Legacy on-disk paths produced by the pre-#41 sanitizer. Returned for
|
|
425
|
+
// ENOENT-fallback in read/exists/delete; never written to.
|
|
426
|
+
legacyEntryPath(namespace, key) {
|
|
427
|
+
return join(
|
|
428
|
+
this.basePath,
|
|
429
|
+
legacyNamespaceSanitize(namespace),
|
|
430
|
+
`${legacyKeySanitize(key)}.enc`
|
|
431
|
+
);
|
|
411
432
|
}
|
|
412
433
|
async write(namespace, key, data) {
|
|
413
434
|
const dirPath = this.namespacePath(namespace);
|
|
@@ -416,7 +437,13 @@ var init_filesystem = __esm({
|
|
|
416
437
|
await writeFile(filePath, data, { mode: 384 });
|
|
417
438
|
}
|
|
418
439
|
async read(namespace, key) {
|
|
419
|
-
const
|
|
440
|
+
const buf = await this.readAtPath(this.entryPath(namespace, key));
|
|
441
|
+
if (buf !== null) return buf;
|
|
442
|
+
const legacy = this.legacyEntryPath(namespace, key);
|
|
443
|
+
if (legacy === this.entryPath(namespace, key)) return null;
|
|
444
|
+
return this.readAtPath(legacy);
|
|
445
|
+
}
|
|
446
|
+
async readAtPath(filePath) {
|
|
420
447
|
try {
|
|
421
448
|
const buf = await readFile(filePath);
|
|
422
449
|
return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
@@ -428,7 +455,13 @@ var init_filesystem = __esm({
|
|
|
428
455
|
}
|
|
429
456
|
}
|
|
430
457
|
async delete(namespace, key, secureOverwrite = true) {
|
|
431
|
-
const
|
|
458
|
+
const newPath = this.entryPath(namespace, key);
|
|
459
|
+
if (await this.deleteAtPath(newPath, secureOverwrite)) return true;
|
|
460
|
+
const legacy = this.legacyEntryPath(namespace, key);
|
|
461
|
+
if (legacy === newPath) return false;
|
|
462
|
+
return this.deleteAtPath(legacy, secureOverwrite);
|
|
463
|
+
}
|
|
464
|
+
async deleteAtPath(filePath, secureOverwrite) {
|
|
432
465
|
try {
|
|
433
466
|
if (secureOverwrite) {
|
|
434
467
|
const fileStat = await stat(filePath);
|
|
@@ -474,12 +507,19 @@ var init_filesystem = __esm({
|
|
|
474
507
|
}
|
|
475
508
|
}
|
|
476
509
|
async exists(namespace, key) {
|
|
477
|
-
const
|
|
510
|
+
const newPath = this.entryPath(namespace, key);
|
|
478
511
|
try {
|
|
479
|
-
await stat(
|
|
512
|
+
await stat(newPath);
|
|
480
513
|
return true;
|
|
481
514
|
} catch {
|
|
482
|
-
|
|
515
|
+
const legacy = this.legacyEntryPath(namespace, key);
|
|
516
|
+
if (legacy === newPath) return false;
|
|
517
|
+
try {
|
|
518
|
+
await stat(legacy);
|
|
519
|
+
return true;
|
|
520
|
+
} catch {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
483
523
|
}
|
|
484
524
|
}
|
|
485
525
|
async totalSize() {
|
|
@@ -836,6 +876,14 @@ var init_identity = __esm({
|
|
|
836
876
|
init_random();
|
|
837
877
|
}
|
|
838
878
|
});
|
|
879
|
+
|
|
880
|
+
// src/core/key-derivation.ts
|
|
881
|
+
var key_derivation_exports = {};
|
|
882
|
+
__export(key_derivation_exports, {
|
|
883
|
+
deriveMasterKey: () => deriveMasterKey,
|
|
884
|
+
deriveNamespaceKey: () => deriveNamespaceKey,
|
|
885
|
+
derivePurposeKey: () => derivePurposeKey
|
|
886
|
+
});
|
|
839
887
|
async function deriveMasterKey(passphrase, existingParams) {
|
|
840
888
|
const salt = existingParams ? fromBase64url(existingParams.salt) : generateSalt();
|
|
841
889
|
const params = existingParams ?? {
|
|
@@ -1496,6 +1544,11 @@ var init_router = __esm({
|
|
|
1496
1544
|
});
|
|
1497
1545
|
|
|
1498
1546
|
// src/l1-cognitive/tools.ts
|
|
1547
|
+
var tools_exports = {};
|
|
1548
|
+
__export(tools_exports, {
|
|
1549
|
+
IdentityManager: () => IdentityManager,
|
|
1550
|
+
createL1Tools: () => createL1Tools
|
|
1551
|
+
});
|
|
1499
1552
|
function getReservedNamespaceViolation(namespace) {
|
|
1500
1553
|
for (const prefix of RESERVED_NAMESPACE_PREFIXES2) {
|
|
1501
1554
|
if (namespace === prefix || namespace.startsWith(prefix + "/")) {
|
|
@@ -4874,6 +4927,35 @@ var init_types = __esm({
|
|
|
4874
4927
|
}
|
|
4875
4928
|
});
|
|
4876
4929
|
|
|
4930
|
+
// src/mesh/constants.ts
|
|
4931
|
+
function isReservedEventType(s) {
|
|
4932
|
+
return RESERVED_EVENT_TYPE_PREFIXES.some((p) => s.startsWith(p));
|
|
4933
|
+
}
|
|
4934
|
+
function isReservedExtensionKey(k) {
|
|
4935
|
+
return RESERVED_EXTENSION_ENVELOPE_KEYS.includes(k);
|
|
4936
|
+
}
|
|
4937
|
+
var PROTOCOL_VERSION, SIGNATURE_SCHEME_V1, RESERVED_EVENT_TYPE_PREFIXES, RESERVED_EXTENSION_ENVELOPE_KEYS;
|
|
4938
|
+
var init_constants = __esm({
|
|
4939
|
+
"src/mesh/constants.ts"() {
|
|
4940
|
+
PROTOCOL_VERSION = "0.1";
|
|
4941
|
+
SIGNATURE_SCHEME_V1 = "ed25519-v1";
|
|
4942
|
+
RESERVED_EVENT_TYPE_PREFIXES = [
|
|
4943
|
+
"EXTENSION_",
|
|
4944
|
+
"cross_fortress_",
|
|
4945
|
+
"multi_master_"
|
|
4946
|
+
];
|
|
4947
|
+
RESERVED_EXTENSION_ENVELOPE_KEYS = [
|
|
4948
|
+
"cross_fortress_read_grant",
|
|
4949
|
+
"cross_fortress_read_query",
|
|
4950
|
+
"cross_fortress_read_response",
|
|
4951
|
+
"multi_master_policy_merge",
|
|
4952
|
+
"audit_replication_full_n_way",
|
|
4953
|
+
"auto_promote_canonical_audit",
|
|
4954
|
+
"agent_live_migration"
|
|
4955
|
+
];
|
|
4956
|
+
}
|
|
4957
|
+
});
|
|
4958
|
+
|
|
4877
4959
|
// src/shr/generator.ts
|
|
4878
4960
|
function deriveL4Degradations(evidence, now = /* @__PURE__ */ new Date()) {
|
|
4879
4961
|
const out = [];
|
|
@@ -5022,6 +5104,7 @@ function generateSHR(identityId, opts) {
|
|
|
5022
5104
|
return {
|
|
5023
5105
|
body,
|
|
5024
5106
|
signed_by: identity.public_key,
|
|
5107
|
+
signature_scheme: SIGNATURE_SCHEME_V1,
|
|
5025
5108
|
signature: toBase64url(signatureBytes)
|
|
5026
5109
|
};
|
|
5027
5110
|
}
|
|
@@ -5032,6 +5115,7 @@ var init_generator = __esm({
|
|
|
5032
5115
|
init_identity();
|
|
5033
5116
|
init_encoding();
|
|
5034
5117
|
init_key_derivation();
|
|
5118
|
+
init_constants();
|
|
5035
5119
|
DEFAULT_VALIDITY_MS = 60 * 60 * 1e3;
|
|
5036
5120
|
DEFAULT_FRESHNESS_WINDOW_DAYS = 30;
|
|
5037
5121
|
DEFAULT_LOW_TIER_DOMINANCE_THRESHOLD = 0.6;
|
|
@@ -7270,14 +7354,14 @@ function generateDashboardHTML(options) {
|
|
|
7270
7354
|
// cookie (set by /auth/session and sent automatically by the
|
|
7271
7355
|
// browser) or as a ?session= query parameter, both of which Stack
|
|
7272
7356
|
// A's checkAuth honours. Loopback callers also bypass auth via the
|
|
7273
|
-
// v0.10.2 _autoAuthLocalhost path, which is the path
|
|
7357
|
+
// v0.10.2 _autoAuthLocalhost path, which is the path Mini1
|
|
7274
7358
|
// hits when the dashboard is auto-opened on 127.0.0.1.
|
|
7275
7359
|
//
|
|
7276
7360
|
// The endpoint itself is /events \u2014 Stack A's route table mounts it
|
|
7277
7361
|
// there, and the previous /api/events URL was a 404 in every real
|
|
7278
7362
|
// boot from v0.10.0 through v0.10.4. The retry loop that result
|
|
7279
7363
|
// produced is exactly the "status bar flashing blue continuously"
|
|
7280
|
-
//
|
|
7364
|
+
// Mini1 reported on v0.10.4.
|
|
7281
7365
|
const eventSource = new EventSource(API_BASE + '/events');
|
|
7282
7366
|
|
|
7283
7367
|
eventSource.addEventListener('init', (e) => {
|
|
@@ -10302,8 +10386,8 @@ var init_html = __esm({
|
|
|
10302
10386
|
function isPolicySlot(value) {
|
|
10303
10387
|
return typeof value === "string" && POLICY_SLOTS.includes(value);
|
|
10304
10388
|
}
|
|
10305
|
-
var COMPILED_POLICY_SCHEMA_VERSION, POLICY_UPDATE_EVENT_TYPE, POLICY_SLOTS, CHANNEL_TEMPLATE_IDS,
|
|
10306
|
-
var
|
|
10389
|
+
var COMPILED_POLICY_SCHEMA_VERSION, POLICY_UPDATE_EVENT_TYPE, POLICY_SLOTS, CHANNEL_TEMPLATE_IDS, BUDGET_UNITS;
|
|
10390
|
+
var init_constants2 = __esm({
|
|
10307
10391
|
"src/policy-engine/constants.ts"() {
|
|
10308
10392
|
COMPILED_POLICY_SCHEMA_VERSION = "0.1";
|
|
10309
10393
|
POLICY_UPDATE_EVENT_TYPE = "policy_update";
|
|
@@ -10318,10 +10402,8 @@ var init_constants = __esm({
|
|
|
10318
10402
|
"read-then-report",
|
|
10319
10403
|
"scheduled-digest",
|
|
10320
10404
|
"plan-draft-only",
|
|
10321
|
-
"fortress-relay"
|
|
10322
|
-
"concierge-loop"
|
|
10405
|
+
"fortress-relay"
|
|
10323
10406
|
];
|
|
10324
|
-
COUNTERPARTY_WILDCARD = "*";
|
|
10325
10407
|
BUDGET_UNITS = ["tokens", "usd"];
|
|
10326
10408
|
}
|
|
10327
10409
|
});
|
|
@@ -10476,8 +10558,12 @@ function lintOnboarding(_name, content) {
|
|
|
10476
10558
|
function resolveTemplatesDir() {
|
|
10477
10559
|
const thisFile = fileURLToPath(import.meta.url);
|
|
10478
10560
|
const thisDir = dirname(thisFile);
|
|
10479
|
-
if (thisDir.includes("/dist
|
|
10480
|
-
|
|
10561
|
+
if (thisDir.includes("/dist")) {
|
|
10562
|
+
const templatesSubdir = join(thisDir, "templates");
|
|
10563
|
+
const candidateDir = existsSync(join(thisDir, TEMPLATE_NAMES[0])) ? thisDir : existsSync(join(templatesSubdir, TEMPLATE_NAMES[0])) ? templatesSubdir : null;
|
|
10564
|
+
if (candidateDir) return candidateDir;
|
|
10565
|
+
const srcFallback = thisDir.endsWith("/templates") ? thisDir.replace("/dist/templates", "/src/templates") : join(thisDir.replace("/dist", "/src"), "templates");
|
|
10566
|
+
return srcFallback;
|
|
10481
10567
|
}
|
|
10482
10568
|
return thisDir;
|
|
10483
10569
|
}
|
|
@@ -10544,7 +10630,7 @@ function getTemplateEntry(name) {
|
|
|
10544
10630
|
var TEMPLATE_NAMES, TemplateValidationError, _cache;
|
|
10545
10631
|
var init_registry = __esm({
|
|
10546
10632
|
"src/templates/registry.ts"() {
|
|
10547
|
-
|
|
10633
|
+
init_constants2();
|
|
10548
10634
|
TEMPLATE_NAMES = [
|
|
10549
10635
|
"research-assistant",
|
|
10550
10636
|
"coding-assistant",
|
|
@@ -10698,10 +10784,10 @@ function applyChannelTemplate(id, params) {
|
|
|
10698
10784
|
if (!entry) throw new Error(`unknown channel template: ${id}`);
|
|
10699
10785
|
return entry.factory(params);
|
|
10700
10786
|
}
|
|
10701
|
-
var requestApproveAct, readThenReport, scheduledDigest, planDraftOnly, fortressRelay,
|
|
10787
|
+
var requestApproveAct, readThenReport, scheduledDigest, planDraftOnly, fortressRelay, REGISTRY;
|
|
10702
10788
|
var init_channel_templates = __esm({
|
|
10703
10789
|
"src/policy-engine/channel-templates.ts"() {
|
|
10704
|
-
|
|
10790
|
+
init_constants2();
|
|
10705
10791
|
init_null_policy();
|
|
10706
10792
|
requestApproveAct = (params) => {
|
|
10707
10793
|
const p = basePolicy(params);
|
|
@@ -10784,23 +10870,6 @@ var init_channel_templates = __esm({
|
|
|
10784
10870
|
setRetentionDays(p, 90);
|
|
10785
10871
|
return p;
|
|
10786
10872
|
};
|
|
10787
|
-
conciergeLoop = (params) => {
|
|
10788
|
-
const p = basePolicy(params);
|
|
10789
|
-
p.source_english = "Bidirectional Q&A with the operator. Reads local fortress state; never writes outward.";
|
|
10790
|
-
grantOn(p, "memory", {
|
|
10791
|
-
counterparty: params.counterparty,
|
|
10792
|
-
action: "read",
|
|
10793
|
-
scope: { local_fortress_state_only: true, ...params.scope ?? {} }
|
|
10794
|
-
});
|
|
10795
|
-
grantOn(p, "outputs", {
|
|
10796
|
-
counterparty: params.counterparty || COUNTERPARTY_WILDCARD,
|
|
10797
|
-
action: "read",
|
|
10798
|
-
scope: { operator_chat_only: true, ...params.scope ?? {} }
|
|
10799
|
-
});
|
|
10800
|
-
p.egress = { allowlist: [] };
|
|
10801
|
-
setRetentionDays(p, 14);
|
|
10802
|
-
return p;
|
|
10803
|
-
};
|
|
10804
10873
|
REGISTRY = {
|
|
10805
10874
|
"request-approve-act": {
|
|
10806
10875
|
id: "request-approve-act",
|
|
@@ -10836,13 +10905,6 @@ var init_channel_templates = __esm({
|
|
|
10836
10905
|
label: "Fortress relay",
|
|
10837
10906
|
description: "Routes signed events between peer fortresses. Commits bind only when both sides sign.",
|
|
10838
10907
|
factory: fortressRelay
|
|
10839
|
-
},
|
|
10840
|
-
"concierge-loop": {
|
|
10841
|
-
id: "concierge-loop",
|
|
10842
|
-
severity: "LOW",
|
|
10843
|
-
label: "Concierge loop",
|
|
10844
|
-
description: "Bidirectional Q&A with the operator. Reads local fortress state; never writes outward.",
|
|
10845
|
-
factory: conciergeLoop
|
|
10846
10908
|
}
|
|
10847
10909
|
};
|
|
10848
10910
|
}
|
|
@@ -10912,8 +10974,14 @@ function encode(value) {
|
|
|
10912
10974
|
}
|
|
10913
10975
|
function encodeArray(arr) {
|
|
10914
10976
|
const parts = [];
|
|
10915
|
-
for (
|
|
10916
|
-
|
|
10977
|
+
for (let i = 0; i < arr.length; i++) {
|
|
10978
|
+
const item = arr[i];
|
|
10979
|
+
if (item === void 0) {
|
|
10980
|
+
throw new MeshCanonicalJsonError(
|
|
10981
|
+
`canonicalize(): undefined is not a valid JSON value at array index ${i}`
|
|
10982
|
+
);
|
|
10983
|
+
}
|
|
10984
|
+
parts.push(encode(item));
|
|
10917
10985
|
}
|
|
10918
10986
|
return "[" + parts.join(",") + "]";
|
|
10919
10987
|
}
|
|
@@ -11231,45 +11299,17 @@ var init_canonical_policy = __esm({
|
|
|
11231
11299
|
"src/policy-engine/canonical-policy.ts"() {
|
|
11232
11300
|
init_canonical_json();
|
|
11233
11301
|
init_encoding();
|
|
11234
|
-
|
|
11302
|
+
init_constants2();
|
|
11235
11303
|
init_errors2();
|
|
11236
11304
|
}
|
|
11237
11305
|
});
|
|
11238
|
-
|
|
11239
|
-
// src/mesh/constants.ts
|
|
11240
|
-
function isReservedEventType(s) {
|
|
11241
|
-
return RESERVED_EVENT_TYPE_PREFIXES.some((p) => s.startsWith(p));
|
|
11242
|
-
}
|
|
11243
|
-
function isReservedExtensionKey(k) {
|
|
11244
|
-
return RESERVED_EXTENSION_ENVELOPE_KEYS.includes(k);
|
|
11245
|
-
}
|
|
11246
|
-
var PROTOCOL_VERSION, RESERVED_EVENT_TYPE_PREFIXES, RESERVED_EXTENSION_ENVELOPE_KEYS;
|
|
11247
|
-
var init_constants2 = __esm({
|
|
11248
|
-
"src/mesh/constants.ts"() {
|
|
11249
|
-
PROTOCOL_VERSION = "0.1";
|
|
11250
|
-
RESERVED_EVENT_TYPE_PREFIXES = [
|
|
11251
|
-
"EXTENSION_",
|
|
11252
|
-
"cross_fortress_",
|
|
11253
|
-
"multi_master_"
|
|
11254
|
-
];
|
|
11255
|
-
RESERVED_EXTENSION_ENVELOPE_KEYS = [
|
|
11256
|
-
"cross_fortress_read_grant",
|
|
11257
|
-
"cross_fortress_read_query",
|
|
11258
|
-
"cross_fortress_read_response",
|
|
11259
|
-
"multi_master_policy_merge",
|
|
11260
|
-
"audit_replication_full_n_way",
|
|
11261
|
-
"auto_promote_canonical_audit",
|
|
11262
|
-
"agent_live_migration"
|
|
11263
|
-
];
|
|
11264
|
-
}
|
|
11265
|
-
});
|
|
11266
11306
|
var init_trust_root = __esm({
|
|
11267
11307
|
"src/mesh/trust-root.ts"() {
|
|
11268
11308
|
init_encoding();
|
|
11269
11309
|
init_identity();
|
|
11270
11310
|
init_random();
|
|
11271
11311
|
init_canonical_json();
|
|
11272
|
-
|
|
11312
|
+
init_constants();
|
|
11273
11313
|
init_errors();
|
|
11274
11314
|
}
|
|
11275
11315
|
});
|
|
@@ -11322,7 +11362,7 @@ var init_envelope = __esm({
|
|
|
11322
11362
|
init_encoding();
|
|
11323
11363
|
init_random();
|
|
11324
11364
|
init_canonical_json();
|
|
11325
|
-
|
|
11365
|
+
init_constants();
|
|
11326
11366
|
init_errors();
|
|
11327
11367
|
init_trust_root();
|
|
11328
11368
|
}
|
|
@@ -11352,7 +11392,7 @@ function packPolicyUpdate(params) {
|
|
|
11352
11392
|
}
|
|
11353
11393
|
var init_envelope2 = __esm({
|
|
11354
11394
|
"src/policy-engine/envelope.ts"() {
|
|
11355
|
-
|
|
11395
|
+
init_constants2();
|
|
11356
11396
|
init_canonical_policy();
|
|
11357
11397
|
init_errors2();
|
|
11358
11398
|
init_envelope();
|
|
@@ -11486,7 +11526,7 @@ function initTemplate(params) {
|
|
|
11486
11526
|
var init_init = __esm({
|
|
11487
11527
|
"src/templates/init.ts"() {
|
|
11488
11528
|
init_channel_templates();
|
|
11489
|
-
|
|
11529
|
+
init_constants2();
|
|
11490
11530
|
init_canonical_policy();
|
|
11491
11531
|
init_envelope2();
|
|
11492
11532
|
init_registry();
|
|
@@ -11764,7 +11804,7 @@ function deriveMachineKey(home) {
|
|
|
11764
11804
|
return hkdf(sha256, material, void 0, "sanctuary-passphrase-v1", 32);
|
|
11765
11805
|
}
|
|
11766
11806
|
async function defaultExec(cmd, args, input) {
|
|
11767
|
-
return new Promise((
|
|
11807
|
+
return new Promise((resolve6, reject) => {
|
|
11768
11808
|
const child = spawn(cmd, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
11769
11809
|
let stdout = "";
|
|
11770
11810
|
let stderr = "";
|
|
@@ -11775,7 +11815,7 @@ async function defaultExec(cmd, args, input) {
|
|
|
11775
11815
|
stderr += d.toString();
|
|
11776
11816
|
});
|
|
11777
11817
|
child.on("error", reject);
|
|
11778
|
-
child.on("close", (code) =>
|
|
11818
|
+
child.on("close", (code) => resolve6({ stdout, stderr, code }));
|
|
11779
11819
|
if (input !== void 0) {
|
|
11780
11820
|
child.stdin.write(input);
|
|
11781
11821
|
}
|
|
@@ -12897,7 +12937,7 @@ async function api(path, opts) {
|
|
|
12897
12937
|
// /policies, /activity responses on subsequent GETs even when the
|
|
12898
12938
|
// server-side state has changed (e.g. recent-failures buffer cleared
|
|
12899
12939
|
// on substrate flip). The pre-rc.5 client used bare fetch with no
|
|
12900
|
-
// cache control, which on
|
|
12940
|
+
// cache control, which on Mini1 Safari produced a stale view of
|
|
12901
12941
|
// server state and made the operator-visible badge color stick to
|
|
12902
12942
|
// its prior value across substrate changes. Belt + suspenders:
|
|
12903
12943
|
// cache: "no-store" turns off the response cache; the _t query
|
|
@@ -13059,12 +13099,6 @@ const CHANNEL_TEMPLATES = [
|
|
|
13059
13099
|
severity: "MEDIUM",
|
|
13060
13100
|
title: "Fortress relay",
|
|
13061
13101
|
description: "Routes signed events between peer fortresses. Commits bind only when both sides sign."
|
|
13062
|
-
},
|
|
13063
|
-
{
|
|
13064
|
-
id: "concierge-loop",
|
|
13065
|
-
severity: "LOW",
|
|
13066
|
-
title: "Concierge loop",
|
|
13067
|
-
description: "Bidirectional Q&A with the operator. Reads local fortress state; never writes outward."
|
|
13068
13102
|
}
|
|
13069
13103
|
];
|
|
13070
13104
|
|
|
@@ -13111,6 +13145,24 @@ function setRoute(route) {
|
|
|
13111
13145
|
renderFortress();
|
|
13112
13146
|
}
|
|
13113
13147
|
|
|
13148
|
+
// Renders the global attestation badge (Q1 layer 1, persistent across
|
|
13149
|
+
// surfaces). Tone is driven by state.topbarPills.attestation. Pending
|
|
13150
|
+
// state shows a dashed seal ring; verified shows solid; degraded shows
|
|
13151
|
+
// outlined core; unverified shows the broken-seal mark. Observation
|
|
13152
|
+
// language only; Castle Layer 1 enforcement ships in WP-V1.x-CASTLE-WALL.
|
|
13153
|
+
function renderTopbarAttestationBadge(stateName) {
|
|
13154
|
+
const valid = stateName === "verified" || stateName === "degraded" || stateName === "unverified" || stateName === "pending";
|
|
13155
|
+
const cls = valid ? stateName : "pending";
|
|
13156
|
+
const ringDashed = cls === "pending" ? " dashed" : "";
|
|
13157
|
+
return '<span class="att-global ' + cls + '" data-pill="attestation" title="Fortress attestation">' +
|
|
13158
|
+
'<span class="seal">' +
|
|
13159
|
+
'<span class="seal-ring' + ringDashed + '"></span>' +
|
|
13160
|
+
'<span class="seal-core"></span>' +
|
|
13161
|
+
'</span>' +
|
|
13162
|
+
'<span class="label">' + escHtml(cls) + '</span>' +
|
|
13163
|
+
'</span>';
|
|
13164
|
+
}
|
|
13165
|
+
|
|
13114
13166
|
function renderTopbar() {
|
|
13115
13167
|
const pillEl = document.getElementById("topbar-pills");
|
|
13116
13168
|
if (!pillEl) return;
|
|
@@ -13127,7 +13179,7 @@ function renderTopbar() {
|
|
|
13127
13179
|
versionPill,
|
|
13128
13180
|
'<span class="pill" data-pill="deployment">deployment: ' + escHtml(state.topbarPills.deployment) + '</span>',
|
|
13129
13181
|
'<span class="pill" data-pill="mode">mode: ' + escHtml(state.topbarPills.mode) + '</span>',
|
|
13130
|
-
|
|
13182
|
+
renderTopbarAttestationBadge(state.topbarPills.attestation)
|
|
13131
13183
|
].join("");
|
|
13132
13184
|
// Lockdown button three-state UX (binding addendum 3).
|
|
13133
13185
|
const btn = document.getElementById("btn-lockdown");
|
|
@@ -13223,6 +13275,7 @@ function renderMain() {
|
|
|
13223
13275
|
case "agent-detail": nextHtml = renderAgentDetail(); break;
|
|
13224
13276
|
case "policy": nextHtml = renderPolicyCenter(); break;
|
|
13225
13277
|
case "intelligence": nextHtml = renderIntelligenceCenter(); break;
|
|
13278
|
+
case "attestation": nextHtml = renderAttestation(); break;
|
|
13226
13279
|
case "privacy": nextHtml = renderPrivacyPage(); break;
|
|
13227
13280
|
case "coordination": nextHtml = renderCoordinationPage(); break;
|
|
13228
13281
|
case "health": nextHtml = renderHealthPage(); break;
|
|
@@ -13301,9 +13354,9 @@ function renderMain() {
|
|
|
13301
13354
|
// "Concierge unavailable; substrate not configured") sourced from the
|
|
13302
13355
|
// last response's served_by + display_label.
|
|
13303
13356
|
const CONCIERGE_SUGGESTIONS = [
|
|
13304
|
-
{ id: "summarize-hour", label: "summarize the last hour", query: "Summarize what happened in this fortress in the last hour." },
|
|
13305
|
-
{ id: "agent-touched", label: "what has each agent touched today", query: "What has each wrapped agent done today? Group by agent." },
|
|
13306
|
-
{ id: "open-approvals", label: "any open approvals?", query: "Are there any open Tier 1 approvals or pending inbox items I should look at?" }
|
|
13357
|
+
{ id: "summarize-hour", category: "Summarize", label: "summarize the last hour", query: "Summarize what happened in this fortress in the last hour." },
|
|
13358
|
+
{ id: "agent-touched", category: "Inspect", label: "what has each agent touched today", query: "What has each wrapped agent done today? Group by agent." },
|
|
13359
|
+
{ 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?" }
|
|
13307
13360
|
];
|
|
13308
13361
|
|
|
13309
13362
|
// Direct-agent chat surface was removed in the v1.2 reshape; the
|
|
@@ -13320,59 +13373,316 @@ function renderDashboardConcierge() {
|
|
|
13320
13373
|
const badge = c.badge && c.badge.displayLabel
|
|
13321
13374
|
? '<span class="pill mono concierge-badge" title="Substrate that served the most recent response">' + escHtml(c.badge.displayLabel) + '</span>'
|
|
13322
13375
|
: '<span class="pill muted concierge-badge">Concierge: substrate not yet contacted</span>';
|
|
13323
|
-
const
|
|
13376
|
+
const sendDisabled = c.sending ? ' disabled' : '';
|
|
13377
|
+
const sendLabel = c.sending ? 'Sending...' : 'Send';
|
|
13378
|
+
// Sprint Piece 2 PR 2: empty state lives INSIDE the concierge-history
|
|
13379
|
+
// container so the DDD e2e selector .concierge-history matches both
|
|
13380
|
+
// empty and active state. The container's flex layout hosts a single
|
|
13381
|
+
// .concierge-empty child that fills the available height with a serif
|
|
13382
|
+
// headline and a 3-up suggest grid; the grid replaces the v1.2 bottom
|
|
13383
|
+
// chip row, which is retired with this polish.
|
|
13384
|
+
const emptyState =
|
|
13385
|
+
'<div class="concierge-empty">' +
|
|
13386
|
+
'<div class="concierge-empty-headline">' +
|
|
13387
|
+
'<h2>Where would you like to begin.</h2>' +
|
|
13388
|
+
'<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>' +
|
|
13389
|
+
'</div>' +
|
|
13390
|
+
'<div class="concierge-suggest-grid">' +
|
|
13391
|
+
CONCIERGE_SUGGESTIONS.map(function (s) {
|
|
13392
|
+
return '<button class="concierge-suggest" data-action="concierge-suggestion" data-suggestion-id="' + escHtml(s.id) + '"' + sendDisabled + '>' +
|
|
13393
|
+
'<span class="label">' + escHtml(s.category || '') + '</span>' +
|
|
13394
|
+
escHtml(s.label) +
|
|
13395
|
+
'</button>';
|
|
13396
|
+
}).join("") +
|
|
13397
|
+
'</div>' +
|
|
13398
|
+
'</div>';
|
|
13399
|
+
const messagesHtml = c.messages.length
|
|
13324
13400
|
? c.messages.map(function (m) {
|
|
13325
13401
|
const cls = m.role === "operator" ? "concierge-msg-operator" : "concierge-msg-concierge";
|
|
13326
|
-
const
|
|
13402
|
+
const authorLabel = m.role === "operator" ? "you" : "sanctuary";
|
|
13403
|
+
const metaParts = [];
|
|
13404
|
+
if (m.created_at) metaParts.push(escHtml(shortTime(m.created_at)));
|
|
13405
|
+
if (m.role === "concierge" && m.served_by) metaParts.push('substrate: ' + escHtml(m.served_by));
|
|
13406
|
+
const meta = metaParts.length
|
|
13407
|
+
? '<div class="concierge-msg-meta"><span>' + metaParts.join(' · ') + '</span></div>'
|
|
13408
|
+
: '';
|
|
13327
13409
|
return '<div class="concierge-msg ' + cls + '">' +
|
|
13328
|
-
'<
|
|
13410
|
+
'<span class="concierge-msg-author">' + escHtml(authorLabel) + '</span>' +
|
|
13329
13411
|
'<div class="concierge-msg-body">' + escHtml(m.body) + '</div>' +
|
|
13412
|
+
meta +
|
|
13330
13413
|
'</div>';
|
|
13331
13414
|
}).join("\n")
|
|
13332
|
-
:
|
|
13415
|
+
: emptyState;
|
|
13333
13416
|
const errorBanner = c.error
|
|
13334
13417
|
? '<div class="banner banner-warn">' + escHtml(c.error) + '</div>'
|
|
13335
13418
|
: "";
|
|
13336
|
-
const sendDisabled = c.sending ? ' disabled' : '';
|
|
13337
|
-
const sendLabel = c.sending ? 'Sending...' : 'Send';
|
|
13338
|
-
const chips = CONCIERGE_SUGGESTIONS.map(function (s) {
|
|
13339
|
-
return '<button class="btn chip" data-action="concierge-suggestion" data-suggestion-id="' + escHtml(s.id) + '"' + sendDisabled + '>' + escHtml(s.label) + '</button>';
|
|
13340
|
-
}).join("\n");
|
|
13341
13419
|
const activeChatsPanel = renderActiveChatsPanel();
|
|
13342
13420
|
return [
|
|
13343
|
-
'<
|
|
13344
|
-
|
|
13345
|
-
|
|
13346
|
-
|
|
13347
|
-
'<
|
|
13348
|
-
|
|
13421
|
+
'<div class="concierge-wrap">',
|
|
13422
|
+
'<div class="page-head"><div>',
|
|
13423
|
+
'<p class="eyebrow">Concierge</p>',
|
|
13424
|
+
'<h1>Talk to your fortress.</h1>',
|
|
13425
|
+
'<p class="sub">A direct line to Sanctuary, routed through the substrate you chose. Nothing leaves without your hand on it.</p>',
|
|
13426
|
+
'</div></div>',
|
|
13427
|
+
activeChatsPanel,
|
|
13428
|
+
'<div class="card concierge-card">',
|
|
13429
|
+
'<div class="concierge-header">',
|
|
13430
|
+
'<div class="concierge-persona">',
|
|
13431
|
+
'<div class="glyph-ring"></div>',
|
|
13432
|
+
'<div class="concierge-persona-text"><strong>Sanctuary Fortress concierge</strong><small>read-only over fortress state</small></div>',
|
|
13433
|
+
'</div>',
|
|
13434
|
+
'<div class="concierge-meta">' + badge + '</div>',
|
|
13435
|
+
'</div>',
|
|
13436
|
+
errorBanner,
|
|
13437
|
+
'<div class="concierge-history" id="concierge-history">' + messagesHtml + '</div>',
|
|
13438
|
+
'<form class="concierge-composer" data-action="concierge-submit">',
|
|
13439
|
+
'<div class="input-wrap">',
|
|
13440
|
+
'<input type="text" name="concierge-input" placeholder="Type to Sanctuary. Enter to send." value="' + escHtml(c.composer) + '" data-action="concierge-input"' + sendDisabled + ' autocomplete="off">',
|
|
13441
|
+
'<span class="composer-meta">Enter</span>',
|
|
13442
|
+
'</div>',
|
|
13443
|
+
'<button type="submit" class="btn btn-primary" data-action="concierge-send"' + sendDisabled + '>' + escHtml(sendLabel) + '</button>',
|
|
13444
|
+
'</form>',
|
|
13445
|
+
'<p class="muted concierge-foot">First time? <a href="#intelligence">Pick a substrate</a> to enable concierge replies.</p>',
|
|
13349
13446
|
'</div>',
|
|
13350
|
-
errorBanner,
|
|
13351
|
-
'<div class="concierge-history" id="concierge-history">' + messages + '</div>',
|
|
13352
|
-
'<form class="concierge-composer" data-action="concierge-submit">',
|
|
13353
|
-
'<input type="text" name="concierge-input" placeholder="Ask the concierge about this fortress..." value="' + escHtml(c.composer) + '" data-action="concierge-input"' + sendDisabled + ' autocomplete="off">',
|
|
13354
|
-
'<button type="submit" class="btn btn-primary" data-action="concierge-send"' + sendDisabled + '>' + escHtml(sendLabel) + '</button>',
|
|
13355
|
-
'</form>',
|
|
13356
|
-
'<div class="concierge-chips">' + chips + '</div>',
|
|
13357
|
-
'<p class="muted concierge-foot">First time? <a href="#intelligence">Pick a substrate</a> to enable concierge replies.</p>',
|
|
13358
13447
|
'</div>'
|
|
13359
13448
|
].join("");
|
|
13360
13449
|
}
|
|
13361
13450
|
|
|
13362
13451
|
// ── Render: agents list / detail ───────────────────────────────────────
|
|
13452
|
+
//
|
|
13453
|
+
// Sprint Piece 2 PR 4 polish: empty state uses .agents-empty with the
|
|
13454
|
+
// concentric icon-frame + a terminal-block CTA. Populated state uses the
|
|
13455
|
+
// .agents-layout grid with the .agents-list 4-column table (Agent /
|
|
13456
|
+
// State / Attestation / Last seen). The empty-state branch keeps the
|
|
13457
|
+
// literal '<h1>Agents</h1>' start and the "No wrapped agents yet." copy
|
|
13458
|
+
// because agents-empty-state-canary.test.ts pins both.
|
|
13459
|
+
function agentInitials(agentId) {
|
|
13460
|
+
const tail = String(agentId || "").split(":").pop() || "";
|
|
13461
|
+
const cleaned = tail.replace(/[^a-zA-Z0-9]/g, "");
|
|
13462
|
+
return (cleaned.slice(0, 2) || "??").toUpperCase();
|
|
13463
|
+
}
|
|
13464
|
+
function agentStateClass(status) {
|
|
13465
|
+
if (status === "active") return "live";
|
|
13466
|
+
if (status === "locked_down" || status === "error") return "off";
|
|
13467
|
+
return "idle";
|
|
13468
|
+
}
|
|
13469
|
+
// Per-agent attestation badge (Q1 layer 2). Square chip beside each
|
|
13470
|
+
// agent: a bounded glyph beside a bounded entity. Color and fill pattern
|
|
13471
|
+
// carry meaning together so the badge reads even monochrome. The "locked"
|
|
13472
|
+
// status maps to the unverified visual (rust + hatched mark) since a
|
|
13473
|
+
// locked-down agent has no current attestation; the inspect-pane copy
|
|
13474
|
+
// explains the distinction. Pure visual surface; no state derivation.
|
|
13475
|
+
function renderAgentAttestationBadge(status) {
|
|
13476
|
+
let cls;
|
|
13477
|
+
let label;
|
|
13478
|
+
if (status === "active") { cls = "verified"; label = "verified"; }
|
|
13479
|
+
else if (status === "locked_down") { cls = "unverified"; label = "locked"; }
|
|
13480
|
+
else if (status === "error") { cls = "unverified"; label = "unverified"; }
|
|
13481
|
+
else { cls = "degraded"; label = "degraded"; }
|
|
13482
|
+
return '<span class="att-agent ' + cls + '" title="Agent attestation"><span class="mark"></span>' + escHtml(label) + '</span>';
|
|
13483
|
+
}
|
|
13484
|
+
// Per-action attestation tick (Q1 layer 3). Tiny inline shape on every
|
|
13485
|
+
// timeline row. Two-byte signature fragment is enough at low resolution;
|
|
13486
|
+
// the full signature is one click away. Neutral state shows a circle
|
|
13487
|
+
// instead of a tick when the signer was unreachable; the action is still
|
|
13488
|
+
// recorded. Visual surface only.
|
|
13489
|
+
function renderActionAttestationBadge(stateName, sig) {
|
|
13490
|
+
const valid = stateName === "verified" || stateName === "degraded" || stateName === "unverified" || stateName === "neutral";
|
|
13491
|
+
const cls = valid ? stateName : "neutral";
|
|
13492
|
+
const sigText = sig ? String(sig) : "--";
|
|
13493
|
+
return '<span class="att-action ' + cls + '" title="Action attestation">' +
|
|
13494
|
+
'<span class="tick"></span>' +
|
|
13495
|
+
'<span>' + escHtml(sigText) + '</span>' +
|
|
13496
|
+
'</span>';
|
|
13497
|
+
}
|
|
13498
|
+
// Attestation gallery surface (Q1 four classes: global / per-agent /
|
|
13499
|
+
// per-action / per-transaction custody-provenance stub). Reference for
|
|
13500
|
+
// operators: shows what each badge looks like across verified, degraded,
|
|
13501
|
+
// unverified, and (where applicable) pending or neutral states. Pure
|
|
13502
|
+
// visual; no derivation, no live data. Castle Layer 3 cooperative-MCP UX
|
|
13503
|
+
// surface; Castle Layer 1 enforcement ships in WP-V1.x-CASTLE-WALL.
|
|
13504
|
+
function renderAttestation() {
|
|
13505
|
+
return '<div class="att-gallery">' +
|
|
13506
|
+
'<div class="page-head"><div>' +
|
|
13507
|
+
'<p class="eyebrow">Attestation</p>' +
|
|
13508
|
+
'<h1>Four classes of badge.</h1>' +
|
|
13509
|
+
'<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>' +
|
|
13510
|
+
'</div></div>' +
|
|
13511
|
+
// Global
|
|
13512
|
+
'<div class="att-section">' +
|
|
13513
|
+
'<div class="att-section-head"><div>' +
|
|
13514
|
+
'<h2>Global. The fortress itself.</h2>' +
|
|
13515
|
+
'<p>Lives in the topbar. Visible on every surface. Tells you the fortress identity is currently signed and matches the binary you installed.</p>' +
|
|
13516
|
+
'</div><span class="label">topbar</span></div>' +
|
|
13517
|
+
attRow(renderTopbarAttestationBadge("verified"), "Verified", "Identity matches. Binary matches. Default state for a healthy fortress.") +
|
|
13518
|
+
attRow(renderTopbarAttestationBadge("degraded"), "Degraded", "The signature is older than the staleness window, or one of two co-signers is unreachable. The fortress keeps running.") +
|
|
13519
|
+
attRow(renderTopbarAttestationBadge("unverified"), "Unverified", "The signature did not validate. The surface still works; lockdown is still available; the badge tells you to investigate.") +
|
|
13520
|
+
attRow(renderTopbarAttestationBadge("pending"), "Pending", "First-run state. Fortress is signing for the first time. Settles in seconds.") +
|
|
13521
|
+
'</div>' +
|
|
13522
|
+
// Per-agent
|
|
13523
|
+
'<div class="att-section">' +
|
|
13524
|
+
'<div class="att-section-head"><div>' +
|
|
13525
|
+
'<h2>Per-agent. In the agents list and inspect pane.</h2>' +
|
|
13526
|
+
'<p>A square chip beside each agent. Square because an agent is bounded; the fortress (a circle) contains it.</p>' +
|
|
13527
|
+
'</div><span class="label">agents view</span></div>' +
|
|
13528
|
+
'<div class="att-row">' +
|
|
13529
|
+
'<div class="demo" style="display:flex; gap:8px; flex-wrap:wrap;">' +
|
|
13530
|
+
renderAgentAttestationBadge("active") +
|
|
13531
|
+
renderAgentAttestationBadgeForState("degraded", "degraded") +
|
|
13532
|
+
renderAgentAttestationBadgeForState("unverified", "unverified") +
|
|
13533
|
+
'</div>' +
|
|
13534
|
+
'<div class="desc"><strong>Verified, degraded, unverified</strong>' +
|
|
13535
|
+
'<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>' +
|
|
13536
|
+
'</div>' +
|
|
13537
|
+
'</div>' +
|
|
13538
|
+
'</div>' +
|
|
13539
|
+
// Per-action
|
|
13540
|
+
'<div class="att-section">' +
|
|
13541
|
+
'<div class="att-section-head"><div>' +
|
|
13542
|
+
'<h2>Per-action. Inline in the activity timeline.</h2>' +
|
|
13543
|
+
'<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>' +
|
|
13544
|
+
'</div><span class="label">timeline</span></div>' +
|
|
13545
|
+
'<div class="att-row">' +
|
|
13546
|
+
'<div class="demo" style="display:flex; gap:10px; flex-wrap:wrap; align-items:center;">' +
|
|
13547
|
+
'<span style="font-size:13px; color:var(--ink-2);">14:22:08 doc-reviewer summarized intake.pdf</span>' +
|
|
13548
|
+
renderActionAttestationBadge("verified", "9c7d..2a") +
|
|
13549
|
+
'</div>' +
|
|
13550
|
+
'<div class="desc"><strong>Verified action</strong>' +
|
|
13551
|
+
'<small>The most common shape. Two-byte signature fragment is enough; the full signature is one click away.</small>' +
|
|
13552
|
+
'</div>' +
|
|
13553
|
+
'</div>' +
|
|
13554
|
+
'<div class="att-row">' +
|
|
13555
|
+
'<div class="demo" style="display:flex; gap:10px; align-items:center;">' +
|
|
13556
|
+
'<span style="font-size:13px; color:var(--ink-2);">14:11:47 privacy filter redacted payload</span>' +
|
|
13557
|
+
renderActionAttestationBadge("degraded", "b440..71") +
|
|
13558
|
+
'</div>' +
|
|
13559
|
+
'<div class="desc"><strong>Degraded action</strong>' +
|
|
13560
|
+
'<small>The action signed, but the signature class was less than the policy preferred. Useful when a substrate is still warming up.</small>' +
|
|
13561
|
+
'</div>' +
|
|
13562
|
+
'</div>' +
|
|
13563
|
+
'<div class="att-row">' +
|
|
13564
|
+
'<div class="demo" style="display:flex; gap:10px; align-items:center;">' +
|
|
13565
|
+
'<span style="font-size:13px; color:var(--ink-2);">14:09:02 agent attempted external link</span>' +
|
|
13566
|
+
renderActionAttestationBadge("neutral", "--") +
|
|
13567
|
+
'</div>' +
|
|
13568
|
+
'<div class="desc"><strong>Neutral. Degrade, not destroy.</strong>' +
|
|
13569
|
+
'<small>The signer was unreachable. Rather than hide the action, the badge becomes neutral and a tooltip explains. The action is still recorded.</small>' +
|
|
13570
|
+
'</div>' +
|
|
13571
|
+
'</div>' +
|
|
13572
|
+
'</div>' +
|
|
13573
|
+
// Custody stub
|
|
13574
|
+
'<div class="att-section">' +
|
|
13575
|
+
'<div class="att-section-head"><div>' +
|
|
13576
|
+
'<h2>Custody. Stub for v1.x.</h2>' +
|
|
13577
|
+
'<p>A fourth class, surfaced conservatively. Reserved for forthcoming custody-provenance signatures (x402 payment receipts, ERC-8004 identity assertions). Visible, dashed, clearly stubbed.</p>' +
|
|
13578
|
+
'</div><span class="label">stub</span></div>' +
|
|
13579
|
+
'<div class="att-row">' +
|
|
13580
|
+
'<div class="demo">' +
|
|
13581
|
+
'<span class="att-custody" title="Custody-provenance, v1.x">' +
|
|
13582
|
+
'<span class="seal-stub"></span>' +
|
|
13583
|
+
'<span class="stub-tag">custody. stub</span>' +
|
|
13584
|
+
'</span>' +
|
|
13585
|
+
'</div>' +
|
|
13586
|
+
'<div class="desc"><strong>Custody. Stub.</strong>' +
|
|
13587
|
+
'<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>' +
|
|
13588
|
+
'</div>' +
|
|
13589
|
+
'</div>' +
|
|
13590
|
+
'</div>' +
|
|
13591
|
+
// Tooltip
|
|
13592
|
+
'<div class="att-section">' +
|
|
13593
|
+
'<div class="att-section-head"><div>' +
|
|
13594
|
+
'<h2>Tooltip on failure.</h2>' +
|
|
13595
|
+
'<p>A failed badge is never silent. The tooltip explains in plain language, suggests one action, and confirms the surface is still working.</p>' +
|
|
13596
|
+
'</div><span class="label">degrade not destroy</span></div>' +
|
|
13597
|
+
'<div class="att-row">' +
|
|
13598
|
+
'<div class="demo">' +
|
|
13599
|
+
'<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>' +
|
|
13600
|
+
'</div>' +
|
|
13601
|
+
'<div class="desc"><strong>Plain-language tooltip</strong>' +
|
|
13602
|
+
'<small>Three lines, in order: what happened, what did not break, what to do. No jargon, no stack trace.</small>' +
|
|
13603
|
+
'</div>' +
|
|
13604
|
+
'</div>' +
|
|
13605
|
+
'</div>' +
|
|
13606
|
+
'</div>';
|
|
13607
|
+
}
|
|
13608
|
+
function attRow(demoHtml, strong, smallText) {
|
|
13609
|
+
return '<div class="att-row">' +
|
|
13610
|
+
'<div class="demo">' + demoHtml + '</div>' +
|
|
13611
|
+
'<div class="desc"><strong>' + escHtml(strong) + '</strong>' +
|
|
13612
|
+
'<small>' + escHtml(smallText) + '</small>' +
|
|
13613
|
+
'</div>' +
|
|
13614
|
+
'</div>';
|
|
13615
|
+
}
|
|
13616
|
+
// Gallery-only variant: render a per-agent badge for a given visual state
|
|
13617
|
+
// (verified / degraded / unverified) without going through the agent
|
|
13618
|
+
// status mapping. Used by renderAttestation to show all three states
|
|
13619
|
+
// side by side as design reference.
|
|
13620
|
+
function renderAgentAttestationBadgeForState(cls, label) {
|
|
13621
|
+
return '<span class="att-agent ' + escHtml(cls) + '" title="Agent attestation"><span class="mark"></span>' + escHtml(label) + '</span>';
|
|
13622
|
+
}
|
|
13623
|
+
function relTimeFromIso(iso) {
|
|
13624
|
+
if (!iso) return "";
|
|
13625
|
+
const d = new Date(iso);
|
|
13626
|
+
if (isNaN(d.getTime())) return iso;
|
|
13627
|
+
const diffMs = Date.now() - d.getTime();
|
|
13628
|
+
const diffSec = Math.max(0, Math.floor(diffMs / 1000));
|
|
13629
|
+
if (diffSec < 60) return diffSec + "s ago";
|
|
13630
|
+
const diffMin = Math.floor(diffSec / 60);
|
|
13631
|
+
if (diffMin < 60) return diffMin + "m ago";
|
|
13632
|
+
const diffHr = Math.floor(diffMin / 60);
|
|
13633
|
+
if (diffHr < 24) return diffHr + "h ago";
|
|
13634
|
+
const diffDay = Math.floor(diffHr / 24);
|
|
13635
|
+
return diffDay + "d ago";
|
|
13636
|
+
}
|
|
13363
13637
|
function renderAgentsList() {
|
|
13364
|
-
if (!state.agents.length) return '<h1>Agents</h1
|
|
13638
|
+
if (!state.agents.length) return '<h1>Agents</h1>' +
|
|
13639
|
+
'<div class="agents-empty">' +
|
|
13640
|
+
'<div class="icon-frame"><div class="core"></div></div>' +
|
|
13641
|
+
'<h2>No wrapped agents yet.</h2>' +
|
|
13642
|
+
'<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>' +
|
|
13643
|
+
'<div class="terminal-block"><span class="cmd"><span class="prompt">$</span>sanctuary wrap</span></div>' +
|
|
13644
|
+
'</div>';
|
|
13645
|
+
const count = state.agents.length;
|
|
13646
|
+
const subCopy = count + ' wrapped. Click one to inspect its activity, policy, and pending approvals.';
|
|
13365
13647
|
const rows = state.agents.map(function (a) {
|
|
13366
13648
|
const map = STATUS_MAP[a.status] || STATUS_MAP.unknown;
|
|
13367
|
-
const
|
|
13368
|
-
|
|
13369
|
-
|
|
13370
|
-
|
|
13371
|
-
|
|
13372
|
-
'<
|
|
13649
|
+
const dotCls = agentStateClass(a.status);
|
|
13650
|
+
const initials = agentInitials(a.agent_id);
|
|
13651
|
+
const role = escHtml(a.harness) + (a.model_provider && a.model_provider.model_id ? ' · ' + escHtml(a.model_provider.model_id) : '');
|
|
13652
|
+
const isSelected = state.selectedAgentId === a.agent_id;
|
|
13653
|
+
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) + '">' +
|
|
13654
|
+
'<div class="agent-identity">' +
|
|
13655
|
+
'<div class="agent-glyph">' + escHtml(initials) + '</div>' +
|
|
13656
|
+
'<div class="agent-name">' +
|
|
13657
|
+
'<strong>' + escHtml(a.agent_id) + '</strong>' +
|
|
13658
|
+
'<small>' + role + '</small>' +
|
|
13659
|
+
'</div>' +
|
|
13660
|
+
'</div>' +
|
|
13661
|
+
'<span class="agent-state">' +
|
|
13662
|
+
'<span class="state-dot ' + dotCls + '"></span>' +
|
|
13663
|
+
escHtml(map.label) +
|
|
13664
|
+
'</span>' +
|
|
13665
|
+
renderAgentAttestationBadge(a.status) +
|
|
13666
|
+
'<span class="agent-last">' + escHtml(relTimeFromIso(a.last_activity_at)) + '</span>' +
|
|
13373
13667
|
'</div>';
|
|
13374
13668
|
}).join("\n");
|
|
13375
|
-
return '<
|
|
13669
|
+
return '<div class="agents-wrap">' +
|
|
13670
|
+
'<div class="page-head">' +
|
|
13671
|
+
'<div>' +
|
|
13672
|
+
'<p class="eyebrow">Agents</p>' +
|
|
13673
|
+
'<h1>Agents.</h1>' +
|
|
13674
|
+
'<p class="sub">' + escHtml(subCopy) + '</p>' +
|
|
13675
|
+
'</div>' +
|
|
13676
|
+
'</div>' +
|
|
13677
|
+
'<div class="agents-layout">' +
|
|
13678
|
+
'<div class="agents-list">' +
|
|
13679
|
+
'<div class="agents-list-head">' +
|
|
13680
|
+
'<span>Agent</span><span>State</span><span>Attestation</span><span>Last seen</span>' +
|
|
13681
|
+
'</div>' +
|
|
13682
|
+
rows +
|
|
13683
|
+
'</div>' +
|
|
13684
|
+
'</div>' +
|
|
13685
|
+
'</div>';
|
|
13376
13686
|
}
|
|
13377
13687
|
|
|
13378
13688
|
function renderAgentDetail() {
|
|
@@ -13383,7 +13693,10 @@ function renderAgentDetail() {
|
|
|
13383
13693
|
const timeline = events.length
|
|
13384
13694
|
? events.map(function (e) {
|
|
13385
13695
|
const t = renderTemplate(e.display_template_id, e.display_template_args);
|
|
13386
|
-
|
|
13696
|
+
const badgeHtml = e.attestation
|
|
13697
|
+
? ' ' + renderActionAttestationBadge(e.attestation.state, e.attestation.fragment)
|
|
13698
|
+
: '';
|
|
13699
|
+
return '<div class="row"><span class="muted">' + escHtml(shortTime(e.emitted_at)) + '</span><span>' + escHtml(t) + badgeHtml + '</span></div>';
|
|
13387
13700
|
}).join("\n")
|
|
13388
13701
|
: '<p class="muted">No activity yet.</p>';
|
|
13389
13702
|
// WP-V1.2 reshape click-to-inspect surface. Clicking "Open inspect
|
|
@@ -13427,53 +13740,99 @@ function renderAgentInspectPanel(agent) {
|
|
|
13427
13740
|
: "";
|
|
13428
13741
|
|
|
13429
13742
|
// State 2: panel loaded.
|
|
13743
|
+
// Sprint Piece 2 PR 4 polish: outer wrapper combines .card with
|
|
13744
|
+
// .inspect-pane (sticky right rail, internal scroll, sectioned body).
|
|
13745
|
+
// The .card class is preserved so the rendered surface keeps its
|
|
13746
|
+
// shared card chrome; .inspect-pane overrides .card padding so the
|
|
13747
|
+
// inspect-head and inspect-body control their own spacing per design.
|
|
13430
13748
|
if (panel) {
|
|
13749
|
+
const dotCls = agentStateClass(agent.status);
|
|
13750
|
+
const stateMap = STATUS_MAP[agent.status] || STATUS_MAP.unknown;
|
|
13431
13751
|
const activity = (panel.recent_activity || []).slice(0, 20);
|
|
13432
13752
|
const activityHtml = activity.length
|
|
13433
|
-
?
|
|
13753
|
+
? '<div class="timeline">' +
|
|
13754
|
+
activity.map(function (e) {
|
|
13434
13755
|
const t = renderTemplate(e.display_template_id, e.display_template_args);
|
|
13435
|
-
|
|
13436
|
-
|
|
13756
|
+
const badgeHtml = e.attestation
|
|
13757
|
+
? renderActionAttestationBadge(e.attestation.state, e.attestation.fragment)
|
|
13758
|
+
: '';
|
|
13759
|
+
return '<div class="timeline-item ok">' +
|
|
13760
|
+
'<div class="ts">' + escHtml(shortTime(e.emitted_at)) + '</div>' +
|
|
13761
|
+
'<div class="what">' + escHtml(t) + '</div>' +
|
|
13762
|
+
(badgeHtml ? '<div class="att">' + badgeHtml + '</div>' : '') +
|
|
13763
|
+
'</div>';
|
|
13764
|
+
}).join("") +
|
|
13765
|
+
'</div>'
|
|
13437
13766
|
: '<p class="muted">No recent activity for this agent.</p>';
|
|
13438
13767
|
|
|
13439
13768
|
const approvals = panel.pending_approvals || [];
|
|
13440
13769
|
const approvalsHtml = approvals.length
|
|
13441
13770
|
? approvals.map(function (item) {
|
|
13442
13771
|
const promptText = renderTemplate(item.display_template_id, item.display_template_args);
|
|
13443
|
-
return '<div class="row">' +
|
|
13444
|
-
'<
|
|
13445
|
-
|
|
13446
|
-
|
|
13447
|
-
'
|
|
13772
|
+
return '<div class="approval-row">' +
|
|
13773
|
+
'<div class="what">' +
|
|
13774
|
+
'<span class="pill tone-degraded">' + escHtml(item.tier || "tier1") + '</span>' +
|
|
13775
|
+
escHtml(promptText) +
|
|
13776
|
+
'</div>' +
|
|
13777
|
+
'<div class="actions">' +
|
|
13778
|
+
'<button class="btn" data-action="inbox-deny" data-item-id="' + escHtml(item.item_id) + '">Deny</button>' +
|
|
13779
|
+
'<button class="btn btn-primary" data-action="inbox-approve" data-item-id="' + escHtml(item.item_id) + '">Approve once</button>' +
|
|
13780
|
+
'</div>' +
|
|
13448
13781
|
'</div>';
|
|
13449
|
-
}).join("
|
|
13782
|
+
}).join("")
|
|
13450
13783
|
: '<p class="muted">No pending approvals routed through this agent.</p>';
|
|
13451
13784
|
|
|
13452
|
-
const
|
|
13453
|
-
? '<
|
|
13785
|
+
const policySection = panel.policy_summary
|
|
13786
|
+
? '<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>' +
|
|
13454
13787
|
(panel.policy_summary.channel_template_id
|
|
13455
|
-
? '<
|
|
13788
|
+
? '<div class="policy-line"><span class="k">Template</span><span class="v">' + escHtml(panel.policy_summary.channel_template_id) + '</span></div>'
|
|
13456
13789
|
: '') +
|
|
13457
|
-
'<
|
|
13458
|
-
: '<
|
|
13459
|
-
|
|
13460
|
-
|
|
13461
|
-
'<div class="
|
|
13462
|
-
|
|
13463
|
-
|
|
13464
|
-
|
|
13790
|
+
'<div class="policy-line"><span class="k">Bound</span><span class="v">' + escHtml(shortTime(panel.policy_summary.bound_at)) + '</span></div>'
|
|
13791
|
+
: '<div class="policy-line"><span class="k">Policy</span><span class="v">No bound policy yet.</span></div>';
|
|
13792
|
+
|
|
13793
|
+
const modelLine = agent.model_provider
|
|
13794
|
+
? '<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>'
|
|
13795
|
+
: '';
|
|
13796
|
+
|
|
13797
|
+
return '<div class="card inspect-pane">' +
|
|
13798
|
+
'<div class="inspect-head">' +
|
|
13799
|
+
'<div class="row1">' +
|
|
13800
|
+
'<div class="agent-glyph">' + escHtml(agentInitials(agent.agent_id)) + '</div>' +
|
|
13801
|
+
'<h3>' + escHtml(agent.agent_id) + '</h3>' +
|
|
13802
|
+
'<span style="margin-left:auto;">' + renderAgentAttestationBadge(agent.status) + '</span>' +
|
|
13803
|
+
'</div>' +
|
|
13804
|
+
'<div class="meta">' +
|
|
13805
|
+
'<span class="pill ' + (dotCls === "live" ? "tone-verified" : "tone-degraded") + '"><span class="state-dot ' + dotCls + '" style="margin-right:4px;"></span>' + escHtml(stateMap.label) + '</span>' +
|
|
13806
|
+
'<span class="pill">opened ' + escHtml(shortTime(panel.opened_at)) + '</span>' +
|
|
13807
|
+
'<button class="btn btn-quiet" data-action="agent-inspect-open" data-agent-id="' + escHtml(agent.agent_id) + '" title="Refresh inspect panel">Refresh</button>' +
|
|
13808
|
+
'</div>' +
|
|
13809
|
+
'</div>' +
|
|
13810
|
+
'<div class="inspect-body">' +
|
|
13811
|
+
errorBanner +
|
|
13812
|
+
'<div class="inspect-section">' +
|
|
13813
|
+
'<h4>Pending approvals' + (approvals.length ? ' <span class="count">' + approvals.length + '</span>' : '') + '</h4>' +
|
|
13814
|
+
approvalsHtml +
|
|
13815
|
+
'</div>' +
|
|
13816
|
+
'<div class="inspect-section">' +
|
|
13817
|
+
'<h4>Recent activity</h4>' +
|
|
13818
|
+
activityHtml +
|
|
13819
|
+
'</div>' +
|
|
13820
|
+
'<div class="inspect-section">' +
|
|
13821
|
+
'<h4>Policy summary</h4>' +
|
|
13822
|
+
policySection +
|
|
13823
|
+
'</div>' +
|
|
13824
|
+
'<div class="inspect-section">' +
|
|
13825
|
+
'<h4>Identity</h4>' +
|
|
13826
|
+
'<div class="policy-line"><span class="k">Agent id</span><span class="v">' + escHtml(agent.agent_id) + '</span></div>' +
|
|
13827
|
+
'<div class="policy-line"><span class="k">Harness</span><span class="v">' + escHtml(agent.harness) + '</span></div>' +
|
|
13828
|
+
modelLine +
|
|
13829
|
+
'<div class="policy-line"><span class="k">Wrapped at</span><span class="v">' + escHtml(shortTime(agent.wrapped_at)) + '</span></div>' +
|
|
13830
|
+
'</div>' +
|
|
13831
|
+
'<p class="muted" style="margin-top:10px;font-size:12px;">' +
|
|
13832
|
+
'<a href="#activity?agent=' + escHtml(agent.agent_id) + '">View full activity</a> · ' +
|
|
13833
|
+
'<a href="#policy">Edit policy</a>' +
|
|
13834
|
+
'</p>' +
|
|
13465
13835
|
'</div>' +
|
|
13466
|
-
errorBanner +
|
|
13467
|
-
'<h3>Pending approvals</h3>' +
|
|
13468
|
-
approvalsHtml +
|
|
13469
|
-
'<h3 style="margin-top:14px;">Recent activity</h3>' +
|
|
13470
|
-
activityHtml +
|
|
13471
|
-
'<h3 style="margin-top:14px;">Policy</h3>' +
|
|
13472
|
-
'<dl class="kv">' + policyLine + '</dl>' +
|
|
13473
|
-
'<p class="muted" style="margin-top:10px;font-size:12px;">' +
|
|
13474
|
-
'<a href="#activity?agent=' + escHtml(agent.agent_id) + '">View full activity</a> · ' +
|
|
13475
|
-
'<a href="#policy">Edit policy</a>' +
|
|
13476
|
-
'</p>' +
|
|
13477
13836
|
'</div>';
|
|
13478
13837
|
}
|
|
13479
13838
|
|
|
@@ -13622,6 +13981,16 @@ function statusDotClass(status) {
|
|
|
13622
13981
|
return "red";
|
|
13623
13982
|
}
|
|
13624
13983
|
|
|
13984
|
+
// Card-grid polish (Sprint Piece 2 PR 3) maps the badge dot class onto
|
|
13985
|
+
// the shaped glyph token. Sage circle for ok, ochre triangle for warn,
|
|
13986
|
+
// rust diamond for fail. Keep aligned with .status-glyph rules in
|
|
13987
|
+
// html.ts and the .intel-card-status modifier classes.
|
|
13988
|
+
function statusGlyphClass(dotClass) {
|
|
13989
|
+
if (dotClass === "green") return "ok";
|
|
13990
|
+
if (dotClass === "yellow") return "warn";
|
|
13991
|
+
return "fail";
|
|
13992
|
+
}
|
|
13993
|
+
|
|
13625
13994
|
function statusLabel(health) {
|
|
13626
13995
|
if (health === "ok") return "Working";
|
|
13627
13996
|
if (health === "degraded") return "Degraded";
|
|
@@ -13661,13 +14030,18 @@ function renderIntelligenceCenter() {
|
|
|
13661
14030
|
}
|
|
13662
14031
|
const status = state.intelligence.status;
|
|
13663
14032
|
const config = state.intelligence.config || {};
|
|
13664
|
-
const
|
|
14033
|
+
const surfaceCards = SURFACES_ORDER.map(function (surfaceId) {
|
|
13665
14034
|
const surfaceStatus = (status.surfaces || []).find(function (s) { return s.surface === surfaceId; });
|
|
13666
14035
|
if (!surfaceStatus) {
|
|
13667
|
-
return '<div class="intel-row"
|
|
13668
|
-
'<
|
|
13669
|
-
|
|
13670
|
-
|
|
14036
|
+
return '<div class="intel-row intel-card" data-intel-surface="' + escHtml(surfaceId) + '">' +
|
|
14037
|
+
'<div class="intel-card-head">' +
|
|
14038
|
+
'<div class="intel-card-name">' +
|
|
14039
|
+
'<strong>' + escHtml(SURFACE_LABELS[surfaceId] || surfaceId) + '</strong>' +
|
|
14040
|
+
'<small>' + escHtml(surfaceId) + '</small>' +
|
|
14041
|
+
'</div>' +
|
|
14042
|
+
'</div>' +
|
|
14043
|
+
'<div class="muted">No status reported.</div>' +
|
|
14044
|
+
'</div>';
|
|
13671
14045
|
}
|
|
13672
14046
|
const substrate = surfaceStatus.chosen;
|
|
13673
14047
|
const localPick = (config.local_model_picks || {})[surfaceId];
|
|
@@ -13685,41 +14059,62 @@ function renderIntelligenceCenter() {
|
|
|
13685
14059
|
if (provider) currentBadge = currentBadge + " (" + (FRONTIER_PROVIDER_LABELS[provider] || provider) + ")";
|
|
13686
14060
|
}
|
|
13687
14061
|
const dotClass = statusDotClass((surfaceStatus.badge || {}).status || "red");
|
|
14062
|
+
const glyphClass = statusGlyphClass(dotClass);
|
|
13688
14063
|
const failures = surfaceStatus.recentFailures || [];
|
|
13689
14064
|
const expanded = !!state.intelligence.expandedFailures[surfaceId];
|
|
13690
|
-
|
|
14065
|
+
|
|
14066
|
+
// Card foot. The failures toggle is the load-bearing affordance for
|
|
14067
|
+
// the rc.6 ZZ test (button[data-action="intel-failures-toggle"] with
|
|
14068
|
+
// text "recent failures (N)"). Pluralization is "failures" regardless
|
|
14069
|
+
// of N for backward compatibility with the seeded test contract.
|
|
14070
|
+
// Surface zero-failure state as a quiet mono note so the card still
|
|
14071
|
+
// has visual rhythm in its foot row.
|
|
14072
|
+
let footHtml;
|
|
13691
14073
|
if (failures.length > 0) {
|
|
13692
14074
|
const toggleLabel = (expanded ? "Hide" : "View") + " recent failures (" + failures.length + ")";
|
|
13693
|
-
|
|
13694
|
-
|
|
13695
|
-
|
|
13696
|
-
|
|
13697
|
-
|
|
13698
|
-
|
|
13699
|
-
|
|
13700
|
-
|
|
13701
|
-
|
|
13702
|
-
|
|
13703
|
-
|
|
13704
|
-
|
|
13705
|
-
|
|
13706
|
-
'</
|
|
13707
|
-
|
|
14075
|
+
footHtml =
|
|
14076
|
+
'<button class="intel-failures-toggle' + (expanded ? ' open' : '') + '" data-action="intel-failures-toggle" data-intel-surface="' + escHtml(surfaceId) + '">' +
|
|
14077
|
+
'<span class="caret"></span>' +
|
|
14078
|
+
escHtml(toggleLabel) +
|
|
14079
|
+
'</button>';
|
|
14080
|
+
} else {
|
|
14081
|
+
footHtml = '<span class="muted mono" style="font-size: 11px;">no recent failures</span>';
|
|
14082
|
+
}
|
|
14083
|
+
|
|
14084
|
+
let failuresBlock = "";
|
|
14085
|
+
if (failures.length > 0 && expanded) {
|
|
14086
|
+
const rows = failures.slice().reverse().map(function (f) {
|
|
14087
|
+
return '<div class="intel-failure-row">' +
|
|
14088
|
+
'<span class="ts">' + escHtml(shortTime(f.ts)) + '</span>' +
|
|
14089
|
+
'<div>' +
|
|
14090
|
+
'<div class="err-class">' + escHtml(f.failureClass) + '</div>' +
|
|
14091
|
+
'<div>' + escHtml(f.snippet) + '</div>' +
|
|
14092
|
+
'</div>' +
|
|
13708
14093
|
'</div>';
|
|
14094
|
+
}).join("");
|
|
14095
|
+
failuresBlock = '<div class="intel-failures">' + rows + '</div>';
|
|
13709
14096
|
}
|
|
13710
|
-
|
|
13711
|
-
|
|
13712
|
-
|
|
13713
|
-
|
|
13714
|
-
|
|
13715
|
-
'<
|
|
13716
|
-
'<span class="pill">' + escHtml(currentBadge) + '</span>' +
|
|
13717
|
-
'<span class="muted mono">' + escHtml(statusLabel(surfaceStatus.health)) + '</span>' +
|
|
14097
|
+
|
|
14098
|
+
return '<div class="intel-row intel-card" data-intel-surface="' + escHtml(surfaceId) + '">' +
|
|
14099
|
+
'<div class="intel-card-head">' +
|
|
14100
|
+
'<div class="intel-card-name">' +
|
|
14101
|
+
'<strong>' + escHtml(SURFACE_LABELS[surfaceId] || surfaceId) + '</strong>' +
|
|
14102
|
+
'<small>' + escHtml(surfaceId) + '</small>' +
|
|
13718
14103
|
'</div>' +
|
|
13719
|
-
'<
|
|
13720
|
-
|
|
14104
|
+
'<span class="intel-card-status ' + glyphClass + '" title="' + escHtml(statusLabel(surfaceStatus.health)) + '">' +
|
|
14105
|
+
'<span class="status-glyph ' + glyphClass + '"></span>' +
|
|
14106
|
+
escHtml(statusLabel(surfaceStatus.health)) +
|
|
14107
|
+
'</span>' +
|
|
13721
14108
|
'</div>' +
|
|
13722
|
-
'<div
|
|
14109
|
+
'<div class="intel-substrate">' +
|
|
14110
|
+
'<div class="sub-line primary">' +
|
|
14111
|
+
'<span>' + escHtml(currentBadge) + '</span>' +
|
|
14112
|
+
'<button class="btn-quiet" data-action="intel-picker-open" data-intel-surface="' + escHtml(surfaceId) + '">Change</button>' +
|
|
14113
|
+
'</div>' +
|
|
14114
|
+
'</div>' +
|
|
14115
|
+
'<div class="intel-row-tradeoff">' + escHtml(substrateTradeoff(substrate)) + '</div>' +
|
|
14116
|
+
'<div class="intel-card-foot">' + footHtml + '</div>' +
|
|
14117
|
+
failuresBlock +
|
|
13723
14118
|
'</div>';
|
|
13724
14119
|
}).join("\n");
|
|
13725
14120
|
|
|
@@ -13731,11 +14126,15 @@ function renderIntelligenceCenter() {
|
|
|
13731
14126
|
|
|
13732
14127
|
const modal = state.intelligence.picker.open ? renderIntelligencePicker() : "";
|
|
13733
14128
|
|
|
13734
|
-
return '<section class="intel-
|
|
13735
|
-
'<
|
|
13736
|
-
|
|
13737
|
-
|
|
13738
|
-
|
|
14129
|
+
return '<section class="intel-wrap">' +
|
|
14130
|
+
'<div class="page-head">' +
|
|
14131
|
+
'<div>' +
|
|
14132
|
+
'<p class="eyebrow">Intelligence</p>' +
|
|
14133
|
+
'<h1>Substrate routing.</h1>' +
|
|
14134
|
+
'<p class="sub">Six surfaces, six choices. Each surface picks where its thinking happens. Local for privacy. Hosted for capability. Hybrid for both.</p>' +
|
|
14135
|
+
'</div>' +
|
|
14136
|
+
'</div>' +
|
|
14137
|
+
'<div class="intel-grid">' + surfaceCards + '</div>' +
|
|
13739
14138
|
'<section class="intel-panel"><h2>Host capability</h2>' +
|
|
13740
14139
|
'<dl class="intel-hardware">' +
|
|
13741
14140
|
'<dt>Total RAM</dt><dd>' + escHtml(hardware.totalRamGb || "?") + ' GB</dd>' +
|
|
@@ -14528,6 +14927,13 @@ document.addEventListener("click", function (ev) {
|
|
|
14528
14927
|
const intelLocalModel = tgt.getAttribute("data-intel-local-model");
|
|
14529
14928
|
const intelFrontierProvider = tgt.getAttribute("data-intel-frontier-provider");
|
|
14530
14929
|
if (action === "lockdown") return void onLockdownClick();
|
|
14930
|
+
if (action === "theme-toggle") {
|
|
14931
|
+
const isDark = document.documentElement.getAttribute("data-theme") === "dark";
|
|
14932
|
+
const next = isDark ? "light" : "dark";
|
|
14933
|
+
sessionStorage.setItem(THEME_KEY, next);
|
|
14934
|
+
applyTheme(next);
|
|
14935
|
+
return;
|
|
14936
|
+
}
|
|
14531
14937
|
if (action === "intel-reload") { return void fetchIntelligenceState().then(rerender); }
|
|
14532
14938
|
if (action === "intel-picker-open" && intelSurface) return void onIntelPickerOpen(intelSurface);
|
|
14533
14939
|
if (action === "intel-picker-close") return onIntelPickerClose();
|
|
@@ -14681,12 +15087,30 @@ document.addEventListener("input", function (ev) {
|
|
|
14681
15087
|
// then the dashboard view renders a static welcome card with no form
|
|
14682
15088
|
// inputs that could be confused for a working command surface.
|
|
14683
15089
|
|
|
14684
|
-
// Theme:
|
|
15090
|
+
// Theme: explicit operator preference (sessionStorage) overrides system
|
|
15091
|
+
// pref. The toggle button in the topbar dispatches data-action
|
|
15092
|
+
// "theme-toggle" which writes the chosen theme and updates the
|
|
15093
|
+
// [data-theme] attribute. When no explicit choice exists, fall back to
|
|
15094
|
+
// system preference and track changes so dark-mode-at-sunset behavior
|
|
15095
|
+
// keeps working on macOS / Windows.
|
|
15096
|
+
const THEME_KEY = "sanctuary-v11-theme";
|
|
15097
|
+
function applyTheme(theme) {
|
|
15098
|
+
if (theme === "dark") document.documentElement.setAttribute("data-theme", "dark");
|
|
15099
|
+
else document.documentElement.removeAttribute("data-theme");
|
|
15100
|
+
}
|
|
15101
|
+
const explicitTheme = sessionStorage.getItem(THEME_KEY);
|
|
14685
15102
|
const mq = window.matchMedia ? window.matchMedia("(prefers-color-scheme: dark)") : null;
|
|
14686
|
-
if (
|
|
15103
|
+
if (explicitTheme === "dark" || explicitTheme === "light") {
|
|
15104
|
+
applyTheme(explicitTheme);
|
|
15105
|
+
} else if (mq && mq.matches) {
|
|
15106
|
+
applyTheme("dark");
|
|
15107
|
+
}
|
|
14687
15108
|
if (mq) mq.addEventListener("change", function (e) {
|
|
14688
|
-
|
|
14689
|
-
|
|
15109
|
+
// Only honor system pref changes when the operator has not made an
|
|
15110
|
+
// explicit choice. Once they toggle, the choice sticks for the
|
|
15111
|
+
// session.
|
|
15112
|
+
if (sessionStorage.getItem(THEME_KEY)) return;
|
|
15113
|
+
applyTheme(e.matches ? "dark" : "light");
|
|
14690
15114
|
});
|
|
14691
15115
|
|
|
14692
15116
|
// Boot.
|
|
@@ -14710,9 +15134,11 @@ function renderDashboardV11Html(options = {}) {
|
|
|
14710
15134
|
const fortressId = options.fortressId ?? "fortress";
|
|
14711
15135
|
const sanctuaryVersion = options.sanctuaryVersion ?? SANCTUARY_VERSION;
|
|
14712
15136
|
const embedClient = options.embedClient !== false;
|
|
14713
|
-
const nav = NAV_ITEMS.map(
|
|
14714
|
-
|
|
14715
|
-
|
|
15137
|
+
const nav = NAV_ITEMS.map((n) => {
|
|
15138
|
+
const iconPath = NAV_ICON_PATHS[n.id] ?? "";
|
|
15139
|
+
const icon = iconPath ? SVG_OPEN + iconPath + "</svg>" : "";
|
|
15140
|
+
return `<a href="#${n.id}" data-route="${n.id}">${icon}<span>${escHtml3(n.label)}</span></a>`;
|
|
15141
|
+
}).join("\n ");
|
|
14716
15142
|
const config = JSON.stringify({
|
|
14717
15143
|
authToken,
|
|
14718
15144
|
hubApiBase,
|
|
@@ -14744,8 +15170,12 @@ function renderDashboardV11Html(options = {}) {
|
|
|
14744
15170
|
<span class="pill" data-pill="version">v${escHtml3(sanctuaryVersion)}</span>
|
|
14745
15171
|
<span class="pill" data-pill="deployment">deployment: local</span>
|
|
14746
15172
|
<span class="pill" data-pill="mode">mode: solo</span>
|
|
14747
|
-
<span class="
|
|
15173
|
+
<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>
|
|
14748
15174
|
</div>
|
|
15175
|
+
<button class="btn btn-icon" id="btn-theme-toggle" data-action="theme-toggle" aria-label="Toggle theme" title="Toggle theme">
|
|
15176
|
+
<span class="icon-moon">${THEME_ICON_MOON}</span>
|
|
15177
|
+
<span class="icon-sun">${THEME_ICON_SUN}</span>
|
|
15178
|
+
</button>
|
|
14749
15179
|
<button class="btn btn-danger" id="btn-lockdown" data-action="lockdown">Lockdown</button>
|
|
14750
15180
|
</header>
|
|
14751
15181
|
<main class="main" id="main"><p class="muted">Loading dashboard.</p></main>
|
|
@@ -14757,7 +15187,7 @@ function renderDashboardV11Html(options = {}) {
|
|
|
14757
15187
|
</body>
|
|
14758
15188
|
</html>`;
|
|
14759
15189
|
}
|
|
14760
|
-
var STYLES, NAV_ITEMS;
|
|
15190
|
+
var STYLES, NAV_ITEMS, NAV_ICON_PATHS, SVG_OPEN, THEME_ICON_MOON, THEME_ICON_SUN;
|
|
14761
15191
|
var init_html2 = __esm({
|
|
14762
15192
|
"src/dashboard/v1_1/html.ts"() {
|
|
14763
15193
|
init_client();
|
|
@@ -14788,6 +15218,26 @@ var init_html2 = __esm({
|
|
|
14788
15218
|
--rad: 6px;
|
|
14789
15219
|
--rad-lg: 10px;
|
|
14790
15220
|
--shadow: 0 1px 2px rgba(0,0,0,0.04), 0 0 0 1px rgba(0,0,0,0.02);
|
|
15221
|
+
/* Type scale. Names are size-relative, not semantic, so refactors do
|
|
15222
|
+
not need to invent new names. Existing rules used these literal px
|
|
15223
|
+
values; tokens make future polish a one-line change. */
|
|
15224
|
+
--text-xs: 11px;
|
|
15225
|
+
--text-sm: 12px;
|
|
15226
|
+
--text-base: 13px;
|
|
15227
|
+
--text-md: 14px;
|
|
15228
|
+
--text-lg: 16px;
|
|
15229
|
+
--text-xl: 22px;
|
|
15230
|
+
--text-display: 36px;
|
|
15231
|
+
/* Spacing scale (4px multiples). Layout-specific magic numbers
|
|
15232
|
+
(220px sidebar, 360px fortress rail, concierge-card heights) stay
|
|
15233
|
+
as literals because the token system is for component padding /
|
|
15234
|
+
margin / gap, not grid track sizing. */
|
|
15235
|
+
--space-1: 4px;
|
|
15236
|
+
--space-2: 8px;
|
|
15237
|
+
--space-3: 12px;
|
|
15238
|
+
--space-4: 16px;
|
|
15239
|
+
--space-5: 24px;
|
|
15240
|
+
--space-6: 32px;
|
|
14791
15241
|
}
|
|
14792
15242
|
[data-theme="dark"] {
|
|
14793
15243
|
--paper: #121210;
|
|
@@ -14814,7 +15264,7 @@ var init_html2 = __esm({
|
|
|
14814
15264
|
html, body { margin: 0; padding: 0; }
|
|
14815
15265
|
body {
|
|
14816
15266
|
font-family: var(--sans);
|
|
14817
|
-
font-size:
|
|
15267
|
+
font-size: var(--text-md);
|
|
14818
15268
|
background: var(--paper);
|
|
14819
15269
|
color: var(--ink);
|
|
14820
15270
|
line-height: 1.45;
|
|
@@ -14835,20 +15285,27 @@ body {
|
|
|
14835
15285
|
"sidebar main";
|
|
14836
15286
|
}
|
|
14837
15287
|
.sidebar { grid-area: sidebar; background: var(--paper-2); border-right: 1px solid var(--rule); padding: 12px 8px; }
|
|
14838
|
-
.sidebar h1 { font-family: var(--serif); font-size:
|
|
15288
|
+
.sidebar h1 { font-family: var(--serif); font-size: var(--text-lg); margin: 4px 8px 16px; }
|
|
14839
15289
|
.sidebar nav { display: flex; flex-direction: column; gap: 2px; }
|
|
14840
15290
|
.sidebar nav a {
|
|
14841
|
-
display:
|
|
14842
|
-
|
|
15291
|
+
display: flex; align-items: center; gap: var(--space-2);
|
|
15292
|
+
padding: 6px 10px; border-radius: var(--rad);
|
|
15293
|
+
color: var(--ink-2); text-decoration: none; font-size: var(--text-base);
|
|
15294
|
+
}
|
|
15295
|
+
.sidebar nav a svg {
|
|
15296
|
+
flex-shrink: 0; width: 16px; height: 16px;
|
|
15297
|
+
color: var(--ink-3);
|
|
14843
15298
|
}
|
|
14844
15299
|
.sidebar nav a:hover { background: var(--paper-3); }
|
|
15300
|
+
.sidebar nav a:hover svg { color: var(--ink-2); }
|
|
14845
15301
|
.sidebar nav a.active { background: var(--surface); color: var(--ink); border: 1px solid var(--rule); }
|
|
14846
|
-
.
|
|
14847
|
-
.topbar
|
|
15302
|
+
.sidebar nav a.active svg { color: var(--ink); }
|
|
15303
|
+
.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); }
|
|
15304
|
+
.topbar .brand { font-family: var(--serif); font-size: var(--text-md); }
|
|
14848
15305
|
.topbar .pills { display: flex; gap: 6px; flex: 1; }
|
|
14849
15306
|
.pill {
|
|
14850
15307
|
display: inline-flex; align-items: center; gap: 4px;
|
|
14851
|
-
padding: 2px 8px; border-radius: 12px; font-size:
|
|
15308
|
+
padding: 2px 8px; border-radius: 12px; font-size: var(--text-xs);
|
|
14852
15309
|
font-family: var(--mono); border: 1px solid var(--rule);
|
|
14853
15310
|
background: var(--surface-2); color: var(--ink-2);
|
|
14854
15311
|
}
|
|
@@ -14860,7 +15317,7 @@ body {
|
|
|
14860
15317
|
display: inline-flex; align-items: center; gap: 4px;
|
|
14861
15318
|
padding: 4px 10px; border-radius: var(--rad);
|
|
14862
15319
|
background: var(--surface); border: 1px solid var(--rule);
|
|
14863
|
-
font-family: var(--sans); font-size:
|
|
15320
|
+
font-family: var(--sans); font-size: var(--text-sm); color: var(--ink);
|
|
14864
15321
|
cursor: pointer;
|
|
14865
15322
|
}
|
|
14866
15323
|
.btn:hover:not(:disabled) { background: var(--surface-2); }
|
|
@@ -14870,21 +15327,30 @@ body {
|
|
|
14870
15327
|
.btn.btn-danger { background: var(--rust-bg); color: var(--rust); border-color: var(--rust); }
|
|
14871
15328
|
.btn.tier1-pending { background: var(--ochre-bg); color: var(--ochre); border-color: var(--ochre); }
|
|
14872
15329
|
.btn.tier1-engaged { background: var(--rust-bg); color: var(--rust); border-color: var(--rust); }
|
|
15330
|
+
.btn.btn-icon {
|
|
15331
|
+
padding: 4px 6px;
|
|
15332
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
15333
|
+
color: var(--ink-2);
|
|
15334
|
+
}
|
|
15335
|
+
.btn.btn-icon svg { width: 16px; height: 16px; }
|
|
15336
|
+
.btn.btn-icon .icon-sun { display: none; }
|
|
15337
|
+
[data-theme="dark"] .btn.btn-icon .icon-moon { display: none; }
|
|
15338
|
+
[data-theme="dark"] .btn.btn-icon .icon-sun { display: inline; }
|
|
14873
15339
|
.main { grid-area: main; overflow-y: auto; padding: 16px 24px; }
|
|
14874
|
-
.fortress { grid-area: fortress; overflow-y: auto; border-left: 1px solid var(--rule); background: var(--paper-2); padding:
|
|
15340
|
+
.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; }
|
|
14875
15341
|
.app.route-full .fortress { display: none; }
|
|
14876
15342
|
.card {
|
|
14877
15343
|
background: var(--surface); border: 1px solid var(--rule);
|
|
14878
|
-
border-radius: var(--rad); padding:
|
|
15344
|
+
border-radius: var(--rad); padding: var(--space-3);
|
|
14879
15345
|
}
|
|
14880
|
-
.card h3 { margin: 0 0 8px; font-size:
|
|
15346
|
+
.card h3 { margin: 0 0 8px; font-size: var(--text-base); font-weight: 600; color: var(--ink); }
|
|
14881
15347
|
.muted { color: var(--ink-3); }
|
|
14882
|
-
.mono { font-family: var(--mono); font-size:
|
|
14883
|
-
.row { display: flex; align-items: center; gap:
|
|
15348
|
+
.mono { font-family: var(--mono); font-size: var(--text-sm); }
|
|
15349
|
+
.row { display: flex; align-items: center; gap: var(--space-2); padding: 6px 0; border-bottom: 1px dashed var(--rule); }
|
|
14884
15350
|
.row:last-child { border-bottom: 0; }
|
|
14885
15351
|
.row .grow { flex: 1; min-width: 0; }
|
|
14886
15352
|
.agent-row { flex-direction: column; align-items: stretch; gap: 6px; }
|
|
14887
|
-
.agent-row-head { display: flex; align-items: center; gap:
|
|
15353
|
+
.agent-row-head { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
|
|
14888
15354
|
.agent-row-head .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
14889
15355
|
.agent-row-actions { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
14890
15356
|
/* Click-to-inspect affordance: the head sub-row of a fortress-column
|
|
@@ -14894,7 +15360,7 @@ body {
|
|
|
14894
15360
|
.agent-row-head[data-action="agent-row-inspect-open"] { cursor: pointer; border-radius: var(--rad); padding: 4px 6px; margin: -4px -6px; }
|
|
14895
15361
|
.agent-row-head[data-action="agent-row-inspect-open"]:hover { background: var(--paper-3); }
|
|
14896
15362
|
.agent-row-head[data-action="agent-row-inspect-open"]:focus-visible { outline: 2px solid var(--ink-3); outline-offset: 1px; }
|
|
14897
|
-
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; font-size:
|
|
15363
|
+
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; font-size: var(--text-sm); }
|
|
14898
15364
|
.kv dt { color: var(--ink-3); }
|
|
14899
15365
|
.kv dd { margin: 0; color: var(--ink); }
|
|
14900
15366
|
.glyph { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--ink-4); }
|
|
@@ -14905,74 +15371,74 @@ body {
|
|
|
14905
15371
|
.toast {
|
|
14906
15372
|
position: fixed; bottom: 16px; right: 16px;
|
|
14907
15373
|
background: var(--ink); color: var(--paper); padding: 8px 12px;
|
|
14908
|
-
border-radius: var(--rad); font-size:
|
|
15374
|
+
border-radius: var(--rad); font-size: var(--text-sm); z-index: 1000;
|
|
14909
15375
|
max-width: 360px;
|
|
14910
15376
|
}
|
|
14911
15377
|
.toast.error { background: var(--rust); color: var(--paper); }
|
|
14912
|
-
.layer-card { background: var(--surface-2); border: 1px solid var(--rule); border-radius: var(--rad); padding:
|
|
14913
|
-
.layer-card h4 { margin: 0 0 4px; font-size:
|
|
14914
|
-
.layer-card p { margin: 0; font-size:
|
|
14915
|
-
.chat-thread { display: flex; flex-direction: column; gap:
|
|
15378
|
+
.layer-card { background: var(--surface-2); border: 1px solid var(--rule); border-radius: var(--rad); padding: var(--space-2); }
|
|
15379
|
+
.layer-card h4 { margin: 0 0 4px; font-size: var(--text-sm); font-weight: 600; }
|
|
15380
|
+
.layer-card p { margin: 0; font-size: var(--text-xs); color: var(--ink-3); }
|
|
15381
|
+
.chat-thread { display: flex; flex-direction: column; gap: var(--space-2); padding-bottom: 12px; }
|
|
14916
15382
|
.chat-msg { padding: 8px 10px; border-radius: var(--rad); border: 1px solid var(--rule); background: var(--surface); max-width: 78%; }
|
|
14917
|
-
.chat-msg.system { background: var(--paper-3); color: var(--ink-3); font-size:
|
|
15383
|
+
.chat-msg.system { background: var(--paper-3); color: var(--ink-3); font-size: var(--text-sm); max-width: 100%; }
|
|
14918
15384
|
.chat-msg.agent { align-self: flex-start; }
|
|
14919
15385
|
.chat-msg.operator { align-self: flex-end; background: var(--ink); color: var(--paper); }
|
|
14920
15386
|
.chat-msg .meta { font-size: 10px; color: var(--ink-4); margin-top: 4px; }
|
|
14921
|
-
.composer { display: flex; gap:
|
|
15387
|
+
.composer { display: flex; gap: var(--space-2); padding: var(--space-2); border-top: 1px solid var(--rule); }
|
|
14922
15388
|
.composer input { flex: 1; padding: 6px 8px; border: 1px solid var(--rule); border-radius: var(--rad); font-family: var(--sans); }
|
|
14923
15389
|
.wizard-step { padding: 10px; border: 1px solid var(--rule); border-radius: var(--rad); margin-bottom: 8px; background: var(--surface); }
|
|
14924
15390
|
.wizard-step.active { border-color: var(--ink); }
|
|
14925
15391
|
.wizard-step.done { background: var(--sage-bg); border-color: var(--sage); }
|
|
14926
|
-
.code-block { font-family: var(--mono); background: var(--paper-3); padding:
|
|
15392
|
+
.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; }
|
|
14927
15393
|
.policy-center { max-width: 980px; margin: 0 auto; }
|
|
14928
|
-
.policy-center .eyebrow { margin: 0 0 6px; color: var(--ink-3); font-family: var(--mono); font-size:
|
|
14929
|
-
.policy-center h1 { font-family: var(--serif); font-size:
|
|
15394
|
+
.policy-center .eyebrow { margin: 0 0 6px; color: var(--ink-3); font-family: var(--mono); font-size: var(--text-sm); letter-spacing: 0; }
|
|
15395
|
+
.policy-center h1 { font-family: var(--serif); font-size: var(--text-display); line-height: 1.08; font-weight: 400; margin: 0 0 10px; }
|
|
14930
15396
|
.policy-subtitle { max-width: 860px; color: var(--ink-2); font-size: 15px; margin: 0 0 24px; }
|
|
14931
15397
|
.policy-panel { background: var(--surface); border: 1px solid var(--rule); border-radius: var(--rad-lg); padding: 20px; margin: 18px 0; }
|
|
14932
|
-
.policy-panel h2 { font-family: var(--serif); font-size:
|
|
14933
|
-
.template-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap:
|
|
15398
|
+
.policy-panel h2 { font-family: var(--serif); font-size: var(--text-xl); font-weight: 400; margin: 0 0 14px; }
|
|
15399
|
+
.template-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-3); }
|
|
14934
15400
|
.template-card { background: var(--surface-2); border: 1px solid var(--rule); border-radius: var(--rad); padding: 14px; min-height: 132px; }
|
|
14935
|
-
.template-card-head { display: flex; justify-content: space-between; align-items: center; gap:
|
|
14936
|
-
.severity { border-radius: 999px; padding: 2px 9px; font-family: var(--mono); font-size:
|
|
15401
|
+
.template-card-head { display: flex; justify-content: space-between; align-items: center; gap: var(--space-2); margin-bottom: 12px; }
|
|
15402
|
+
.severity { border-radius: 999px; padding: 2px 9px; font-family: var(--mono); font-size: var(--text-xs); font-weight: 700; }
|
|
14937
15403
|
.severity.low { color: var(--sage); background: var(--sage-bg); }
|
|
14938
15404
|
.severity.medium { color: var(--ochre); background: var(--ochre-bg); }
|
|
14939
15405
|
.template-id { background: var(--paper-3); border-radius: var(--rad); padding: 2px 7px; color: var(--ink-3); }
|
|
14940
|
-
.template-card h3 { font-size:
|
|
14941
|
-
.template-card p { color: var(--ink-3); margin: 0; font-size:
|
|
15406
|
+
.template-card h3 { font-size: var(--text-lg); margin: 0 0 6px; }
|
|
15407
|
+
.template-card p { color: var(--ink-3); margin: 0; font-size: var(--text-md); }
|
|
14942
15408
|
.rules-scroll { overflow-x: auto; }
|
|
14943
15409
|
.rules-table { width: 100%; border-collapse: collapse; min-width: 760px; }
|
|
14944
|
-
.rules-table th { text-align: left; color: var(--ink-3); font-family: var(--mono); font-size:
|
|
15410
|
+
.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); }
|
|
14945
15411
|
.rules-table td { padding: 12px 10px; border-bottom: 1px solid var(--rule); vertical-align: top; }
|
|
14946
15412
|
.link-btn, .template-cell { border: 0; background: transparent; color: var(--ink); padding: 0; cursor: pointer; font: inherit; text-align: left; }
|
|
14947
15413
|
.template-cell { font-family: var(--mono); max-width: 180px; overflow-wrap: anywhere; }
|
|
14948
15414
|
.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; }
|
|
14949
15415
|
.template-picker-options { display: grid; gap: 6px; max-height: 320px; overflow-y: auto; }
|
|
14950
|
-
.template-option { display: grid; grid-template-columns: 18px 1fr; gap:
|
|
15416
|
+
.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); }
|
|
14951
15417
|
.template-option small { display: block; color: var(--ink-3); margin-top: 2px; }
|
|
14952
|
-
.template-picker-actions { display: flex; justify-content: flex-end; gap:
|
|
15418
|
+
.template-picker-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: 10px; }
|
|
14953
15419
|
.allow-count { color: var(--sage); font-weight: 700; }
|
|
14954
15420
|
.block-count { color: var(--rust); }
|
|
14955
15421
|
.toggle-on { display: inline-block; width: 28px; height: 16px; border-radius: 999px; background: var(--sage); position: relative; }
|
|
14956
15422
|
.toggle-on::after { content: ""; position: absolute; right: 2px; top: 2px; width: 12px; height: 12px; border-radius: 50%; background: var(--surface); }
|
|
14957
15423
|
.error-text { color: var(--rust); margin: 8px 0 0; }
|
|
14958
15424
|
.intel-center { max-width: 980px; margin: 0 auto; }
|
|
14959
|
-
.intel-center .eyebrow { margin: 0 0 6px; color: var(--ink-3); font-family: var(--mono); font-size:
|
|
14960
|
-
.intel-center h1 { font-family: var(--serif); font-size:
|
|
15425
|
+
.intel-center .eyebrow { margin: 0 0 6px; color: var(--ink-3); font-family: var(--mono); font-size: var(--text-sm); letter-spacing: 0; }
|
|
15426
|
+
.intel-center h1 { font-family: var(--serif); font-size: var(--text-display); line-height: 1.08; font-weight: 400; margin: 0 0 10px; }
|
|
14961
15427
|
.intel-subtitle { max-width: 860px; color: var(--ink-2); font-size: 15px; margin: 0 0 24px; }
|
|
14962
15428
|
.intel-panel { background: var(--surface); border: 1px solid var(--rule); border-radius: var(--rad-lg); padding: 20px; margin: 18px 0; }
|
|
14963
|
-
.intel-panel h2 { font-family: var(--serif); font-size:
|
|
14964
|
-
.intel-row { display: grid; grid-template-columns: 200px 1fr auto; gap:
|
|
15429
|
+
.intel-panel h2 { font-family: var(--serif); font-size: var(--text-xl); font-weight: 400; margin: 0 0 14px; }
|
|
15430
|
+
.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; }
|
|
14965
15431
|
.intel-row:last-child { border-bottom: 0; }
|
|
14966
15432
|
.intel-row-name { font-weight: 600; }
|
|
14967
|
-
.intel-row-name small { display: block; color: var(--ink-3); font-weight: 400; font-size:
|
|
15433
|
+
.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); }
|
|
14968
15434
|
.intel-row-body { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
14969
|
-
.intel-row-current { display: flex; gap:
|
|
14970
|
-
.intel-row-tradeoff { color: var(--ink-2); font-size:
|
|
15435
|
+
.intel-row-current { display: flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
|
|
15436
|
+
.intel-row-tradeoff { color: var(--ink-2); font-size: var(--text-base); line-height: 1.5; }
|
|
14971
15437
|
.intel-status-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; }
|
|
14972
15438
|
.intel-status-dot.green { background: var(--sage); }
|
|
14973
15439
|
.intel-status-dot.yellow { background: var(--ochre); }
|
|
14974
15440
|
.intel-status-dot.red { background: var(--rust); }
|
|
14975
|
-
.intel-hardware { display: grid; grid-template-columns: max-content 1fr; gap: 4px 16px; font-size:
|
|
15441
|
+
.intel-hardware { display: grid; grid-template-columns: max-content 1fr; gap: 4px 16px; font-size: var(--text-base); }
|
|
14976
15442
|
.intel-hardware dt { color: var(--ink-3); font-family: var(--mono); }
|
|
14977
15443
|
.intel-hardware dd { margin: 0; }
|
|
14978
15444
|
.intel-modal-backdrop {
|
|
@@ -14985,33 +15451,162 @@ body {
|
|
|
14985
15451
|
box-shadow: 0 8px 32px rgba(0,0,0,0.18); padding: 24px;
|
|
14986
15452
|
width: 100%; max-width: 640px;
|
|
14987
15453
|
}
|
|
14988
|
-
.intel-modal h2 { font-family: var(--serif); font-size:
|
|
14989
|
-
.intel-modal-subtitle { color: var(--ink-3); margin: 0 0 18px; font-size:
|
|
15454
|
+
.intel-modal h2 { font-family: var(--serif); font-size: var(--text-xl); font-weight: 400; margin: 0 0 8px; }
|
|
15455
|
+
.intel-modal-subtitle { color: var(--ink-3); margin: 0 0 18px; font-size: var(--text-base); }
|
|
14990
15456
|
.intel-option {
|
|
14991
|
-
border: 1px solid var(--rule); border-radius: var(--rad); padding:
|
|
15457
|
+
border: 1px solid var(--rule); border-radius: var(--rad); padding: var(--space-3);
|
|
14992
15458
|
margin-bottom: 10px; background: var(--surface-2); cursor: pointer;
|
|
14993
15459
|
display: grid; grid-template-columns: 18px 1fr; gap: 10px; align-items: start;
|
|
14994
15460
|
}
|
|
14995
15461
|
.intel-option.selected { border-color: var(--ink); background: var(--surface); }
|
|
14996
|
-
.intel-option-body strong { display: block; font-size:
|
|
14997
|
-
.intel-option-body small { display: block; color: var(--ink-3); font-size:
|
|
15462
|
+
.intel-option-body strong { display: block; font-size: var(--text-md); margin-bottom: 4px; }
|
|
15463
|
+
.intel-option-body small { display: block; color: var(--ink-3); font-size: var(--text-sm); line-height: 1.5; }
|
|
14998
15464
|
.intel-suboptions { margin-top: 10px; padding: 10px; background: var(--paper-3); border-radius: var(--rad); }
|
|
14999
|
-
.intel-suboptions label { display: block; margin: 6px 0; font-size:
|
|
15465
|
+
.intel-suboptions label { display: block; margin: 6px 0; font-size: var(--text-base); }
|
|
15000
15466
|
.intel-suboptions input[type="text"], .intel-suboptions input[type="password"] {
|
|
15001
15467
|
width: 100%; padding: 6px 8px; border: 1px solid var(--rule); border-radius: var(--rad);
|
|
15002
|
-
font-family: var(--mono); font-size:
|
|
15468
|
+
font-family: var(--mono); font-size: var(--text-sm); box-sizing: border-box;
|
|
15469
|
+
}
|
|
15470
|
+
.intel-modal-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: 18px; }
|
|
15471
|
+
/*
|
|
15472
|
+
* Intelligence panel polish (Sprint Piece 2 PR 3). Card grid layout
|
|
15473
|
+
* replaces the legacy 3-col row visual. The legacy .intel-row and
|
|
15474
|
+
* .intel-status-dot rules above are retained for the e2e selector
|
|
15475
|
+
* contract (.intel-row[data-intel-surface="..."]) and as the responsive
|
|
15476
|
+
* fallback. Cards render as a flex column with a substrate inset,
|
|
15477
|
+
* status badge with shaped glyph, and a recent-failures toggle in the
|
|
15478
|
+
* card foot.
|
|
15479
|
+
*/
|
|
15480
|
+
.intel-wrap { max-width: 1000px; margin: 0 auto; }
|
|
15481
|
+
.intel-grid {
|
|
15482
|
+
display: grid;
|
|
15483
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
15484
|
+
gap: 12px;
|
|
15485
|
+
}
|
|
15486
|
+
.intel-card {
|
|
15487
|
+
background: var(--surface);
|
|
15488
|
+
border: 1px solid var(--rule);
|
|
15489
|
+
border-radius: var(--rad-lg);
|
|
15490
|
+
padding: 16px;
|
|
15491
|
+
display: flex; flex-direction: column;
|
|
15492
|
+
gap: 12px;
|
|
15493
|
+
}
|
|
15494
|
+
.intel-card-head {
|
|
15495
|
+
display: flex; align-items: flex-start; justify-content: space-between;
|
|
15496
|
+
gap: 10px;
|
|
15497
|
+
}
|
|
15498
|
+
.intel-card-name {
|
|
15499
|
+
display: flex; flex-direction: column; gap: 2px;
|
|
15500
|
+
min-width: 0;
|
|
15003
15501
|
}
|
|
15004
|
-
.intel-
|
|
15502
|
+
.intel-card-name strong {
|
|
15503
|
+
font-family: var(--serif); font-weight: 500;
|
|
15504
|
+
font-size: 15px; letter-spacing: 0.005em;
|
|
15505
|
+
}
|
|
15506
|
+
.intel-card-name small {
|
|
15507
|
+
color: var(--ink-3); font-size: 11px; font-family: var(--mono);
|
|
15508
|
+
}
|
|
15509
|
+
.intel-card-status {
|
|
15510
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
15511
|
+
font-family: var(--mono); font-size: 11px;
|
|
15512
|
+
padding: 3px 9px; border-radius: 999px;
|
|
15513
|
+
border: 1px solid var(--rule); background: var(--surface-2);
|
|
15514
|
+
flex-shrink: 0;
|
|
15515
|
+
}
|
|
15516
|
+
.intel-card-status.ok { color: var(--sage); border-color: var(--sage); background: var(--sage-bg); }
|
|
15517
|
+
.intel-card-status.warn { color: var(--ochre); border-color: var(--ochre); background: var(--ochre-bg); }
|
|
15518
|
+
.intel-card-status.fail { color: var(--rust); border-color: var(--rust); background: var(--rust-bg); }
|
|
15519
|
+
.status-glyph {
|
|
15520
|
+
width: 10px; height: 10px;
|
|
15521
|
+
position: relative; flex-shrink: 0;
|
|
15522
|
+
}
|
|
15523
|
+
.status-glyph.ok::before {
|
|
15524
|
+
content: ""; position: absolute; inset: 0;
|
|
15525
|
+
border-radius: 50%; background: currentColor;
|
|
15526
|
+
}
|
|
15527
|
+
.status-glyph.warn::before {
|
|
15528
|
+
content: ""; position: absolute; inset: 0;
|
|
15529
|
+
background: currentColor;
|
|
15530
|
+
clip-path: polygon(50% 0%, 100% 100%, 0% 100%);
|
|
15531
|
+
}
|
|
15532
|
+
.status-glyph.fail::before {
|
|
15533
|
+
content: ""; position: absolute; inset: 1px;
|
|
15534
|
+
background: currentColor;
|
|
15535
|
+
clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
|
|
15536
|
+
}
|
|
15537
|
+
.intel-substrate {
|
|
15538
|
+
display: flex; flex-direction: column; gap: 4px;
|
|
15539
|
+
padding: 10px 12px;
|
|
15540
|
+
background: var(--paper-2);
|
|
15541
|
+
border: 1px solid var(--rule);
|
|
15542
|
+
border-radius: var(--rad);
|
|
15543
|
+
}
|
|
15544
|
+
.intel-substrate .sub-line {
|
|
15545
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
15546
|
+
gap: 10px; font-size: 12px;
|
|
15547
|
+
}
|
|
15548
|
+
.intel-substrate .sub-line.primary {
|
|
15549
|
+
font-family: var(--mono); font-size: 13px; color: var(--ink);
|
|
15550
|
+
}
|
|
15551
|
+
.intel-substrate .sub-line.secondary { color: var(--ink-3); font-size: 11px; }
|
|
15552
|
+
.intel-card-foot {
|
|
15553
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
15554
|
+
gap: 8px; padding-top: 4px;
|
|
15555
|
+
}
|
|
15556
|
+
.intel-failures-toggle {
|
|
15557
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
15558
|
+
font-size: 12px; font-family: var(--mono);
|
|
15559
|
+
color: var(--ink-3);
|
|
15560
|
+
background: transparent; border: 0; padding: 0;
|
|
15561
|
+
cursor: pointer;
|
|
15562
|
+
}
|
|
15563
|
+
.intel-failures-toggle:hover { color: var(--ink); }
|
|
15564
|
+
.intel-failures-toggle .caret {
|
|
15565
|
+
display: inline-block; width: 0; height: 0;
|
|
15566
|
+
border-left: 4px solid transparent;
|
|
15567
|
+
border-right: 4px solid transparent;
|
|
15568
|
+
border-top: 5px solid currentColor;
|
|
15569
|
+
transition: transform 160ms ease;
|
|
15570
|
+
}
|
|
15571
|
+
.intel-failures-toggle.open .caret { transform: rotate(180deg); }
|
|
15572
|
+
.intel-failures {
|
|
15573
|
+
border-top: 1px solid var(--rule);
|
|
15574
|
+
padding-top: 12px;
|
|
15575
|
+
display: flex; flex-direction: column;
|
|
15576
|
+
gap: 8px;
|
|
15577
|
+
}
|
|
15578
|
+
.intel-failure-row {
|
|
15579
|
+
display: grid; grid-template-columns: 88px 1fr; gap: 12px;
|
|
15580
|
+
font-size: 12px; padding: 8px 10px;
|
|
15581
|
+
border-radius: var(--rad);
|
|
15582
|
+
background: var(--paper-2);
|
|
15583
|
+
border: 1px solid var(--rule);
|
|
15584
|
+
}
|
|
15585
|
+
.intel-failure-row .ts {
|
|
15586
|
+
font-family: var(--mono); font-size: 11px; color: var(--ink-3);
|
|
15587
|
+
}
|
|
15588
|
+
.intel-failure-row .err-class {
|
|
15589
|
+
font-family: var(--mono); font-size: 10px;
|
|
15590
|
+
letter-spacing: 0.04em; text-transform: uppercase;
|
|
15591
|
+
color: var(--rust); margin-bottom: 2px;
|
|
15592
|
+
}
|
|
15593
|
+
.btn-quiet {
|
|
15594
|
+
background: transparent; border: 1px solid var(--rule);
|
|
15595
|
+
padding: 2px 6px; font-size: 11px;
|
|
15596
|
+
border-radius: var(--rad); cursor: pointer;
|
|
15597
|
+
color: var(--ink-2); font-family: var(--sans);
|
|
15598
|
+
}
|
|
15599
|
+
.btn-quiet:hover { background: var(--surface-2); color: var(--ink); }
|
|
15005
15600
|
.banner-warn {
|
|
15006
15601
|
background: var(--ochre-bg); color: var(--ochre); border: 1px solid var(--ochre);
|
|
15007
|
-
border-radius: var(--rad); padding: 8px 12px; margin: 8px 0; font-size:
|
|
15602
|
+
border-radius: var(--rad); padding: 8px 12px; margin: 8px 0; font-size: var(--text-base);
|
|
15008
15603
|
}
|
|
15009
15604
|
.banner-info {
|
|
15010
15605
|
background: var(--indigo-bg); color: var(--indigo); border: 1px solid var(--indigo);
|
|
15011
|
-
border-radius: var(--rad); padding: 8px 12px; margin: 8px 0; font-size:
|
|
15606
|
+
border-radius: var(--rad); padding: 8px 12px; margin: 8px 0; font-size: var(--text-base);
|
|
15012
15607
|
}
|
|
15013
15608
|
.btn.chip {
|
|
15014
|
-
border-radius: 999px; padding: 4px 12px; font-size:
|
|
15609
|
+
border-radius: 999px; padding: 4px 12px; font-size: var(--text-sm);
|
|
15015
15610
|
background: var(--surface-2); border-color: var(--rule);
|
|
15016
15611
|
}
|
|
15017
15612
|
.btn.chip:hover:not(:disabled) { background: var(--paper-3); }
|
|
@@ -15028,85 +15623,210 @@ body {
|
|
|
15028
15623
|
* dynamically, but the input box is below the fold, so I still have
|
|
15029
15624
|
* to scroll." rc.5 closes that with a structural layout fix.
|
|
15030
15625
|
*/
|
|
15626
|
+
/* Sprint Piece 2 PR 2 (2026-05-03): concierge surface polish.
|
|
15627
|
+
* Translates Claude Design references at
|
|
15628
|
+
* server/docs/design-refs/sprint-piece-2/surface-concierge.jsx and the
|
|
15629
|
+
* Surface 1 block of surfaces.css. The bounded-card layout above is
|
|
15630
|
+
* preserved verbatim because rc.5 and the DDD e2e suite depend on it;
|
|
15631
|
+
* the Claude Design reference uses height: 720px for the card, but
|
|
15632
|
+
* production keeps the calc-based bounded height so the card adapts
|
|
15633
|
+
* to the operator viewport. The polish lands the persona glyph-ring,
|
|
15634
|
+
* mono uppercase author labels, paper-2 background for concierge
|
|
15635
|
+
* replies, suggest-grid empty-state cards, and the composer input-wrap
|
|
15636
|
+
* with the keyboard-shortcut pill.
|
|
15637
|
+
*/
|
|
15638
|
+
.concierge-wrap { max-width: 880px; margin: 0 auto; }
|
|
15639
|
+
.page-head {
|
|
15640
|
+
display: flex; align-items: flex-end; justify-content: space-between;
|
|
15641
|
+
gap: var(--space-4);
|
|
15642
|
+
margin-bottom: 18px; padding-bottom: 14px;
|
|
15643
|
+
border-bottom: 1px solid var(--rule);
|
|
15644
|
+
}
|
|
15645
|
+
.page-head .eyebrow {
|
|
15646
|
+
font-family: var(--mono); font-size: var(--text-xs);
|
|
15647
|
+
letter-spacing: 0.08em; text-transform: uppercase;
|
|
15648
|
+
color: var(--ink-3);
|
|
15649
|
+
margin: 0 0 6px;
|
|
15650
|
+
}
|
|
15651
|
+
.page-head h1 {
|
|
15652
|
+
font-family: var(--serif); font-weight: 400;
|
|
15653
|
+
font-size: 28px; letter-spacing: -0.01em;
|
|
15654
|
+
margin: 0 0 4px;
|
|
15655
|
+
}
|
|
15656
|
+
.page-head .sub {
|
|
15657
|
+
color: var(--ink-3); margin: 0;
|
|
15658
|
+
font-size: var(--text-base); max-width: 60ch;
|
|
15659
|
+
}
|
|
15031
15660
|
.concierge-card {
|
|
15032
15661
|
display: flex; flex-direction: column;
|
|
15033
15662
|
height: calc(100vh - 180px);
|
|
15034
15663
|
max-height: calc(100vh - 180px);
|
|
15035
15664
|
min-height: 360px;
|
|
15036
|
-
padding:
|
|
15665
|
+
padding: 18px 22px 14px;
|
|
15037
15666
|
gap: 0;
|
|
15038
15667
|
}
|
|
15039
15668
|
.concierge-header {
|
|
15040
15669
|
display: flex; align-items: center; justify-content: space-between;
|
|
15041
|
-
gap:
|
|
15670
|
+
gap: var(--space-3); padding-bottom: 14px;
|
|
15671
|
+
border-bottom: 1px solid var(--rule);
|
|
15042
15672
|
flex-wrap: wrap;
|
|
15043
15673
|
flex-shrink: 0;
|
|
15044
15674
|
}
|
|
15045
|
-
.concierge-persona {
|
|
15046
|
-
|
|
15047
|
-
|
|
15675
|
+
.concierge-persona { display: flex; align-items: center; gap: 10px; }
|
|
15676
|
+
.concierge-persona .glyph-ring {
|
|
15677
|
+
width: 26px; height: 26px;
|
|
15678
|
+
border: 1.5px solid var(--ink-2);
|
|
15679
|
+
border-radius: 50%;
|
|
15680
|
+
position: relative;
|
|
15681
|
+
flex-shrink: 0;
|
|
15682
|
+
}
|
|
15683
|
+
.concierge-persona .glyph-ring::after {
|
|
15684
|
+
content: ""; position: absolute; inset: 5px;
|
|
15685
|
+
border-radius: 50%; background: var(--ink-2);
|
|
15686
|
+
}
|
|
15687
|
+
.concierge-persona-text { display: flex; flex-direction: column; }
|
|
15688
|
+
.concierge-persona-text strong {
|
|
15689
|
+
font-family: var(--serif); font-weight: 500;
|
|
15690
|
+
font-size: 15px; letter-spacing: 0.005em;
|
|
15691
|
+
}
|
|
15692
|
+
.concierge-persona-text small {
|
|
15693
|
+
color: var(--ink-3); font-size: var(--text-xs);
|
|
15694
|
+
font-family: var(--mono);
|
|
15695
|
+
}
|
|
15696
|
+
.concierge-meta {
|
|
15697
|
+
display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
|
|
15048
15698
|
}
|
|
15049
|
-
.concierge-persona strong { font-size: 14px; }
|
|
15050
15699
|
.concierge-badge { white-space: nowrap; }
|
|
15051
15700
|
.concierge-history {
|
|
15052
15701
|
flex: 1 1 auto;
|
|
15053
15702
|
min-height: 0;
|
|
15054
15703
|
overflow-y: auto;
|
|
15055
|
-
padding:
|
|
15056
|
-
display: flex; flex-direction: column; gap:
|
|
15704
|
+
padding: 18px 4px 6px;
|
|
15705
|
+
display: flex; flex-direction: column; gap: 18px;
|
|
15057
15706
|
}
|
|
15058
15707
|
.concierge-msg {
|
|
15059
|
-
display: flex; flex-direction: column; gap:
|
|
15060
|
-
max-width:
|
|
15708
|
+
display: flex; flex-direction: column; gap: 5px;
|
|
15709
|
+
max-width: 78%;
|
|
15061
15710
|
}
|
|
15062
15711
|
.concierge-msg-author {
|
|
15063
|
-
font-size:
|
|
15712
|
+
font-size: 10px;
|
|
15713
|
+
font-family: var(--mono);
|
|
15714
|
+
letter-spacing: 0.04em;
|
|
15715
|
+
text-transform: uppercase;
|
|
15716
|
+
color: var(--ink-4);
|
|
15064
15717
|
}
|
|
15065
15718
|
.concierge-msg-body {
|
|
15066
|
-
padding:
|
|
15067
|
-
border:
|
|
15068
|
-
|
|
15069
|
-
|
|
15719
|
+
padding: 11px 14px;
|
|
15720
|
+
border-radius: 12px;
|
|
15721
|
+
border: 1px solid var(--rule);
|
|
15722
|
+
background: var(--surface);
|
|
15723
|
+
font-size: var(--text-md);
|
|
15724
|
+
line-height: 1.55;
|
|
15725
|
+
white-space: pre-wrap;
|
|
15726
|
+
word-wrap: break-word;
|
|
15070
15727
|
}
|
|
15071
15728
|
.concierge-msg-concierge { align-self: flex-start; }
|
|
15729
|
+
.concierge-msg-concierge .concierge-msg-body {
|
|
15730
|
+
background: var(--paper-2);
|
|
15731
|
+
}
|
|
15072
15732
|
.concierge-msg-operator { align-self: flex-end; align-items: flex-end; }
|
|
15073
15733
|
.concierge-msg-operator .concierge-msg-body {
|
|
15074
15734
|
background: var(--ink); color: var(--paper); border-color: var(--ink);
|
|
15075
15735
|
}
|
|
15736
|
+
.concierge-msg-meta {
|
|
15737
|
+
display: flex; gap: 6px;
|
|
15738
|
+
font-size: 10px;
|
|
15739
|
+
color: var(--ink-4);
|
|
15740
|
+
font-family: var(--mono);
|
|
15741
|
+
}
|
|
15076
15742
|
.concierge-empty {
|
|
15077
|
-
|
|
15078
|
-
|
|
15743
|
+
flex: 1 1 auto;
|
|
15744
|
+
display: flex; flex-direction: column; gap: 22px;
|
|
15745
|
+
justify-content: center;
|
|
15746
|
+
padding: 24px 12px;
|
|
15747
|
+
}
|
|
15748
|
+
.concierge-empty-headline { max-width: 52ch; }
|
|
15749
|
+
.concierge-empty-headline h2 {
|
|
15750
|
+
font-family: var(--serif); font-weight: 400;
|
|
15751
|
+
font-size: var(--text-xl); margin: 0 0 6px;
|
|
15752
|
+
letter-spacing: -0.005em;
|
|
15753
|
+
}
|
|
15754
|
+
.concierge-empty-headline p {
|
|
15755
|
+
color: var(--ink-3); margin: 0;
|
|
15756
|
+
font-size: var(--text-md); line-height: 1.55;
|
|
15757
|
+
}
|
|
15758
|
+
.concierge-suggest-grid {
|
|
15759
|
+
display: grid;
|
|
15760
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
15761
|
+
gap: 10px;
|
|
15762
|
+
}
|
|
15763
|
+
.concierge-suggest {
|
|
15764
|
+
background: var(--surface);
|
|
15765
|
+
border: 1px solid var(--rule);
|
|
15766
|
+
border-radius: var(--rad);
|
|
15767
|
+
padding: 12px 14px;
|
|
15768
|
+
font-size: var(--text-base);
|
|
15769
|
+
cursor: pointer;
|
|
15770
|
+
display: flex; flex-direction: column;
|
|
15771
|
+
gap: 6px;
|
|
15772
|
+
text-align: left;
|
|
15773
|
+
font-family: var(--sans);
|
|
15774
|
+
color: var(--ink);
|
|
15775
|
+
}
|
|
15776
|
+
.concierge-suggest:hover:not(:disabled) {
|
|
15777
|
+
background: var(--surface-2);
|
|
15778
|
+
border-color: var(--rule-2);
|
|
15779
|
+
}
|
|
15780
|
+
.concierge-suggest:disabled {
|
|
15781
|
+
cursor: not-allowed; color: var(--ink-4); opacity: 0.7;
|
|
15782
|
+
}
|
|
15783
|
+
.concierge-suggest .label {
|
|
15784
|
+
font-family: var(--mono);
|
|
15785
|
+
font-size: 10px;
|
|
15786
|
+
letter-spacing: 0.06em;
|
|
15787
|
+
text-transform: uppercase;
|
|
15788
|
+
color: var(--ink-3);
|
|
15079
15789
|
}
|
|
15080
15790
|
.concierge-composer {
|
|
15081
15791
|
display: flex; gap: 10px; align-items: center;
|
|
15082
|
-
padding: 12px 0
|
|
15792
|
+
padding: 12px 0 4px;
|
|
15083
15793
|
border-top: 1px solid var(--rule);
|
|
15084
15794
|
flex-shrink: 0;
|
|
15085
15795
|
}
|
|
15796
|
+
.concierge-composer .input-wrap {
|
|
15797
|
+
flex: 1;
|
|
15798
|
+
display: flex; align-items: center; gap: var(--space-2);
|
|
15799
|
+
padding: 8px 12px;
|
|
15800
|
+
border: 1px solid var(--rule);
|
|
15801
|
+
border-radius: var(--rad);
|
|
15802
|
+
background: var(--surface);
|
|
15803
|
+
}
|
|
15804
|
+
.concierge-composer .input-wrap:focus-within {
|
|
15805
|
+
border-color: var(--ink-3);
|
|
15806
|
+
}
|
|
15086
15807
|
.concierge-composer input {
|
|
15087
15808
|
flex: 1; min-width: 0;
|
|
15088
|
-
padding:
|
|
15089
|
-
border:
|
|
15090
|
-
|
|
15091
|
-
|
|
15809
|
+
padding: 4px 0;
|
|
15810
|
+
border: 0;
|
|
15811
|
+
background: transparent;
|
|
15812
|
+
color: var(--ink);
|
|
15813
|
+
font-family: var(--sans);
|
|
15814
|
+
font-size: var(--text-md);
|
|
15815
|
+
outline: none;
|
|
15092
15816
|
}
|
|
15093
|
-
.concierge-composer input:
|
|
15094
|
-
|
|
15817
|
+
.concierge-composer input::placeholder { color: var(--ink-4); }
|
|
15818
|
+
.concierge-composer .composer-meta {
|
|
15819
|
+
font-family: var(--mono);
|
|
15820
|
+
font-size: 10px;
|
|
15821
|
+
color: var(--ink-4);
|
|
15822
|
+
letter-spacing: 0.04em;
|
|
15095
15823
|
}
|
|
15096
15824
|
.concierge-composer .btn-primary {
|
|
15097
|
-
padding: 8px 18px; font-size:
|
|
15098
|
-
}
|
|
15099
|
-
.concierge-chips {
|
|
15100
|
-
display: flex; flex-wrap: wrap; gap: 6px;
|
|
15101
|
-
padding: 10px 0 0;
|
|
15102
|
-
}
|
|
15103
|
-
.concierge-chips::before {
|
|
15104
|
-
content: "Try:"; color: var(--ink-3); font-size: 12px;
|
|
15105
|
-
align-self: center; margin-right: 4px;
|
|
15825
|
+
padding: 8px 18px; font-size: var(--text-base); flex-shrink: 0;
|
|
15106
15826
|
}
|
|
15107
15827
|
.concierge-foot {
|
|
15108
15828
|
margin: 12px 0 0; padding-top: 10px; border-top: 1px dashed var(--rule);
|
|
15109
|
-
font-size:
|
|
15829
|
+
font-size: var(--text-sm);
|
|
15110
15830
|
}
|
|
15111
15831
|
.concierge-foot a { color: var(--ink-2); }
|
|
15112
15832
|
.tier1-approval-card {
|
|
@@ -15114,20 +15834,532 @@ body {
|
|
|
15114
15834
|
border-radius: var(--rad); padding: 14px 16px; margin: 12px 0;
|
|
15115
15835
|
}
|
|
15116
15836
|
.tier1-approval-card h3 {
|
|
15117
|
-
margin: 0 0 8px; color: var(--ochre); font-size:
|
|
15837
|
+
margin: 0 0 8px; color: var(--ochre); font-size: var(--text-md);
|
|
15118
15838
|
}
|
|
15119
|
-
.tier1-approval-card p { margin: 0 0 12px; font-size:
|
|
15839
|
+
.tier1-approval-card p { margin: 0 0 12px; font-size: var(--text-base); }
|
|
15120
15840
|
.tier1-approval-card .actions {
|
|
15121
|
-
display: flex; gap:
|
|
15841
|
+
display: flex; gap: var(--space-2); flex-wrap: wrap;
|
|
15842
|
+
}
|
|
15843
|
+
/* Sprint Piece 2 PR 4 (2026-05-04): Agents view + Inspect pane polish.
|
|
15844
|
+
* Translates Claude Design references at
|
|
15845
|
+
* server/docs/design-refs/sprint-piece-2/surface-agents.jsx and the
|
|
15846
|
+
* Surface 3 block of surfaces.css. The fortress-column .agent-row,
|
|
15847
|
+
* .agent-row-head, and .agent-row-actions rules above are kept verbatim
|
|
15848
|
+
* because Finding DD tests pin them; the new Agents-view list scopes
|
|
15849
|
+
* its grid layout under .agents-list (descendant selector) so the
|
|
15850
|
+
* fortress-column rules are unaffected. The inspect pane combines the
|
|
15851
|
+
* existing .card surface with .inspect-pane structure (sticky right
|
|
15852
|
+
* rail, internal scroll, sectioned body) for the agent-detail view.
|
|
15853
|
+
*/
|
|
15854
|
+
.agents-wrap { max-width: 1080px; margin: 0 auto; }
|
|
15855
|
+
.agents-layout {
|
|
15856
|
+
display: grid;
|
|
15857
|
+
grid-template-columns: 1fr 420px;
|
|
15858
|
+
gap: 20px;
|
|
15859
|
+
align-items: start;
|
|
15860
|
+
}
|
|
15861
|
+
.agents-list {
|
|
15862
|
+
background: var(--surface);
|
|
15863
|
+
border: 1px solid var(--rule);
|
|
15864
|
+
border-radius: var(--rad-lg);
|
|
15865
|
+
overflow: hidden;
|
|
15866
|
+
}
|
|
15867
|
+
.agents-list-head {
|
|
15868
|
+
display: grid;
|
|
15869
|
+
grid-template-columns: minmax(0, 1fr) 110px 120px 88px;
|
|
15870
|
+
gap: 12px;
|
|
15871
|
+
padding: 10px 16px;
|
|
15872
|
+
border-bottom: 1px solid var(--rule);
|
|
15873
|
+
background: var(--paper-2);
|
|
15874
|
+
font-family: var(--mono);
|
|
15875
|
+
font-size: 10px;
|
|
15876
|
+
letter-spacing: 0.06em;
|
|
15877
|
+
text-transform: uppercase;
|
|
15878
|
+
color: var(--ink-3);
|
|
15879
|
+
}
|
|
15880
|
+
.agents-list .agent-row {
|
|
15881
|
+
display: grid;
|
|
15882
|
+
grid-template-columns: minmax(0, 1fr) 110px 120px 88px;
|
|
15883
|
+
gap: 12px;
|
|
15884
|
+
padding: 14px 16px;
|
|
15885
|
+
border-bottom: 1px solid var(--rule);
|
|
15886
|
+
align-items: center;
|
|
15887
|
+
cursor: pointer;
|
|
15888
|
+
transition: background 120ms ease;
|
|
15889
|
+
}
|
|
15890
|
+
.agents-list .agent-row:last-child { border-bottom: 0; }
|
|
15891
|
+
.agents-list .agent-row:hover { background: var(--paper-2); }
|
|
15892
|
+
.agents-list .agent-row.selected {
|
|
15893
|
+
background: var(--paper-2);
|
|
15894
|
+
box-shadow: inset 3px 0 0 var(--ink);
|
|
15895
|
+
}
|
|
15896
|
+
.agent-identity { display: flex; align-items: center; gap: 10px; min-width: 0; }
|
|
15897
|
+
.agent-glyph {
|
|
15898
|
+
width: 28px; height: 28px;
|
|
15899
|
+
border-radius: var(--rad);
|
|
15900
|
+
background: var(--paper-3);
|
|
15901
|
+
border: 1px solid var(--rule);
|
|
15902
|
+
display: grid; place-items: center;
|
|
15903
|
+
flex-shrink: 0;
|
|
15904
|
+
font-family: var(--mono);
|
|
15905
|
+
font-size: 11px;
|
|
15906
|
+
color: var(--ink-2);
|
|
15907
|
+
font-weight: 600;
|
|
15908
|
+
}
|
|
15909
|
+
.agent-name {
|
|
15910
|
+
display: flex; flex-direction: column; min-width: 0;
|
|
15911
|
+
}
|
|
15912
|
+
.agent-name strong {
|
|
15913
|
+
font-size: var(--text-base); font-weight: 500;
|
|
15914
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
15915
|
+
}
|
|
15916
|
+
.agent-name small {
|
|
15917
|
+
font-family: var(--mono); font-size: var(--text-xs); color: var(--ink-3);
|
|
15918
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
15919
|
+
display: block;
|
|
15920
|
+
}
|
|
15921
|
+
.agent-state {
|
|
15922
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
15923
|
+
font-size: var(--text-xs);
|
|
15924
|
+
font-family: var(--mono);
|
|
15925
|
+
}
|
|
15926
|
+
.state-dot {
|
|
15927
|
+
width: 7px; height: 7px; border-radius: 50%;
|
|
15928
|
+
background: var(--ink-4);
|
|
15929
|
+
}
|
|
15930
|
+
.state-dot.live { background: var(--sage); animation: pulse-soft 2.4s ease-in-out infinite; }
|
|
15931
|
+
.state-dot.idle { background: var(--ochre); }
|
|
15932
|
+
.state-dot.off { background: var(--ink-4); }
|
|
15933
|
+
@keyframes pulse-soft {
|
|
15934
|
+
0%, 100% { box-shadow: 0 0 0 0 currentColor; opacity: 1; }
|
|
15935
|
+
50% { box-shadow: 0 0 0 4px transparent; opacity: 0.7; }
|
|
15936
|
+
}
|
|
15937
|
+
.agent-last {
|
|
15938
|
+
font-family: var(--mono); font-size: var(--text-xs); color: var(--ink-3);
|
|
15939
|
+
}
|
|
15940
|
+
/* Inspect pane (combined with .card outer wrapper for the
|
|
15941
|
+
* renderAgentInspectPanel return-shape regex anchored in
|
|
15942
|
+
* dashboard-welcome.test.ts:152). The .inspect-pane modifier overrides
|
|
15943
|
+
* .card padding so internal sections control their own spacing.
|
|
15944
|
+
*/
|
|
15945
|
+
.inspect-pane {
|
|
15946
|
+
padding: 0;
|
|
15947
|
+
display: flex; flex-direction: column;
|
|
15948
|
+
position: sticky;
|
|
15949
|
+
top: 20px;
|
|
15950
|
+
max-height: calc(100vh - 100px);
|
|
15951
|
+
overflow: hidden;
|
|
15952
|
+
}
|
|
15953
|
+
.inspect-head {
|
|
15954
|
+
padding: 16px 18px;
|
|
15955
|
+
border-bottom: 1px solid var(--rule);
|
|
15956
|
+
display: flex; flex-direction: column; gap: 10px;
|
|
15957
|
+
}
|
|
15958
|
+
.inspect-head .row1 {
|
|
15959
|
+
display: flex; align-items: center; gap: 10px;
|
|
15960
|
+
}
|
|
15961
|
+
.inspect-head h3 {
|
|
15962
|
+
font-family: var(--serif); font-weight: 500;
|
|
15963
|
+
font-size: 17px; margin: 0;
|
|
15964
|
+
}
|
|
15965
|
+
.inspect-head .meta {
|
|
15966
|
+
display: flex; gap: 6px; flex-wrap: wrap;
|
|
15967
|
+
}
|
|
15968
|
+
.inspect-body {
|
|
15969
|
+
overflow-y: auto;
|
|
15970
|
+
padding: 4px 18px 18px;
|
|
15971
|
+
}
|
|
15972
|
+
.inspect-section {
|
|
15973
|
+
padding: 14px 0;
|
|
15974
|
+
border-bottom: 1px solid var(--rule);
|
|
15975
|
+
}
|
|
15976
|
+
.inspect-section:last-child { border-bottom: 0; }
|
|
15977
|
+
.inspect-section h4 {
|
|
15978
|
+
font-family: var(--mono);
|
|
15979
|
+
font-size: 10px;
|
|
15980
|
+
letter-spacing: 0.08em;
|
|
15981
|
+
text-transform: uppercase;
|
|
15982
|
+
color: var(--ink-3);
|
|
15983
|
+
margin: 0 0 10px;
|
|
15984
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
15985
|
+
}
|
|
15986
|
+
.inspect-section h4 .count {
|
|
15987
|
+
font-family: var(--mono);
|
|
15988
|
+
background: var(--paper-3);
|
|
15989
|
+
border-radius: 999px;
|
|
15990
|
+
padding: 1px 7px;
|
|
15991
|
+
color: var(--ink-2);
|
|
15992
|
+
font-size: 10px;
|
|
15993
|
+
}
|
|
15994
|
+
.approval-row {
|
|
15995
|
+
background: var(--ochre-bg);
|
|
15996
|
+
border: 1px solid var(--ochre);
|
|
15997
|
+
border-radius: var(--rad);
|
|
15998
|
+
padding: 10px 12px;
|
|
15999
|
+
margin-bottom: 8px;
|
|
16000
|
+
display: flex; flex-direction: column; gap: 8px;
|
|
16001
|
+
}
|
|
16002
|
+
.approval-row .what { font-size: var(--text-base); color: var(--ink); }
|
|
16003
|
+
.approval-row .what .pill { margin-right: 6px; }
|
|
16004
|
+
.approval-row .why {
|
|
16005
|
+
font-size: var(--text-sm); color: var(--ink-2);
|
|
16006
|
+
padding-left: 10px;
|
|
16007
|
+
border-left: 2px solid var(--ochre);
|
|
16008
|
+
}
|
|
16009
|
+
.approval-row .actions { display: flex; gap: 6px; justify-content: flex-end; }
|
|
16010
|
+
.timeline {
|
|
16011
|
+
display: flex; flex-direction: column; gap: 0;
|
|
16012
|
+
position: relative;
|
|
16013
|
+
padding-left: 14px;
|
|
16014
|
+
}
|
|
16015
|
+
.timeline::before {
|
|
16016
|
+
content: "";
|
|
16017
|
+
position: absolute;
|
|
16018
|
+
left: 4px; top: 6px; bottom: 6px;
|
|
16019
|
+
width: 1px;
|
|
16020
|
+
background: var(--rule);
|
|
16021
|
+
}
|
|
16022
|
+
.timeline-item {
|
|
16023
|
+
position: relative;
|
|
16024
|
+
padding: 6px 0 10px;
|
|
16025
|
+
font-size: var(--text-sm);
|
|
16026
|
+
}
|
|
16027
|
+
.timeline-item::before {
|
|
16028
|
+
content: "";
|
|
16029
|
+
position: absolute;
|
|
16030
|
+
left: -14px; top: 11px;
|
|
16031
|
+
width: 8px; height: 8px;
|
|
16032
|
+
border-radius: 50%;
|
|
16033
|
+
background: var(--surface);
|
|
16034
|
+
border: 1.5px solid var(--ink-4);
|
|
16035
|
+
}
|
|
16036
|
+
.timeline-item.ok::before { border-color: var(--sage); }
|
|
16037
|
+
.timeline-item.warn::before { border-color: var(--ochre); }
|
|
16038
|
+
.timeline-item.fail::before { border-color: var(--rust); }
|
|
16039
|
+
.timeline-item .ts {
|
|
16040
|
+
font-family: var(--mono); font-size: 10px;
|
|
16041
|
+
color: var(--ink-3);
|
|
16042
|
+
letter-spacing: 0.02em;
|
|
16043
|
+
}
|
|
16044
|
+
.timeline-item .what {
|
|
16045
|
+
margin-top: 2px; color: var(--ink); font-size: var(--text-base);
|
|
16046
|
+
}
|
|
16047
|
+
.timeline-item .att {
|
|
16048
|
+
margin-top: 4px;
|
|
16049
|
+
display: inline-flex;
|
|
16050
|
+
}
|
|
16051
|
+
.policy-line {
|
|
16052
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
16053
|
+
padding: 5px 0; font-size: var(--text-base);
|
|
16054
|
+
border-bottom: 1px dashed var(--rule);
|
|
16055
|
+
}
|
|
16056
|
+
.policy-line:last-child { border-bottom: 0; }
|
|
16057
|
+
.policy-line .k { color: var(--ink-3); }
|
|
16058
|
+
.policy-line .v { font-family: var(--mono); font-size: var(--text-sm); color: var(--ink); }
|
|
16059
|
+
/* Empty-state block for when no agents are wrapped. The
|
|
16060
|
+
* renderAgentsList empty-state branch begins with the literal
|
|
16061
|
+
* '<h1>Agents</h1>' (regex-pinned in agents-empty-state-canary.test.ts)
|
|
16062
|
+
* and the "No wrapped agents yet." copy is preserved verbatim.
|
|
16063
|
+
*/
|
|
16064
|
+
.agents-empty {
|
|
16065
|
+
background: var(--surface);
|
|
16066
|
+
border: 1px dashed var(--rule-2);
|
|
16067
|
+
border-radius: var(--rad-lg);
|
|
16068
|
+
padding: 56px 40px;
|
|
16069
|
+
text-align: center;
|
|
16070
|
+
max-width: 720px;
|
|
16071
|
+
margin: 32px auto;
|
|
16072
|
+
}
|
|
16073
|
+
.agents-empty .icon-frame {
|
|
16074
|
+
width: 64px; height: 64px;
|
|
16075
|
+
margin: 0 auto 18px;
|
|
16076
|
+
border: 1px solid var(--rule);
|
|
16077
|
+
border-radius: 50%;
|
|
16078
|
+
display: grid; place-items: center;
|
|
16079
|
+
position: relative;
|
|
16080
|
+
}
|
|
16081
|
+
.agents-empty .icon-frame::before,
|
|
16082
|
+
.agents-empty .icon-frame::after {
|
|
16083
|
+
content: "";
|
|
16084
|
+
position: absolute;
|
|
16085
|
+
border: 1px solid var(--rule);
|
|
16086
|
+
border-radius: 50%;
|
|
16087
|
+
}
|
|
16088
|
+
.agents-empty .icon-frame::before { inset: -8px; opacity: 0.6; }
|
|
16089
|
+
.agents-empty .icon-frame::after { inset: -16px; opacity: 0.3; }
|
|
16090
|
+
.agents-empty .icon-frame .core {
|
|
16091
|
+
width: 22px; height: 22px;
|
|
16092
|
+
background: var(--ink);
|
|
16093
|
+
border-radius: 50%;
|
|
16094
|
+
}
|
|
16095
|
+
.agents-empty h2 {
|
|
16096
|
+
font-family: var(--serif);
|
|
16097
|
+
font-weight: 400;
|
|
16098
|
+
font-size: var(--text-xl);
|
|
16099
|
+
margin: 0 0 8px;
|
|
16100
|
+
}
|
|
16101
|
+
.agents-empty p {
|
|
16102
|
+
color: var(--ink-3);
|
|
16103
|
+
margin: 0 0 20px;
|
|
16104
|
+
font-size: var(--text-md);
|
|
16105
|
+
line-height: 1.55;
|
|
16106
|
+
max-width: 50ch;
|
|
16107
|
+
margin-left: auto; margin-right: auto;
|
|
16108
|
+
}
|
|
16109
|
+
.terminal-block {
|
|
16110
|
+
text-align: left;
|
|
16111
|
+
background: var(--paper-3);
|
|
16112
|
+
border: 1px solid var(--rule);
|
|
16113
|
+
border-radius: var(--rad);
|
|
16114
|
+
padding: 14px 16px;
|
|
16115
|
+
font-family: var(--mono);
|
|
16116
|
+
font-size: var(--text-base);
|
|
16117
|
+
margin: 0 auto 16px;
|
|
16118
|
+
max-width: 480px;
|
|
16119
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
16120
|
+
}
|
|
16121
|
+
.terminal-block .cmd { color: var(--ink); }
|
|
16122
|
+
.terminal-block .cmd .prompt { color: var(--ink-3); margin-right: 8px; user-select: none; }
|
|
16123
|
+
.copy-btn {
|
|
16124
|
+
background: transparent; border: 0;
|
|
16125
|
+
color: var(--ink-3); cursor: pointer;
|
|
16126
|
+
font-family: var(--mono); font-size: var(--text-xs);
|
|
16127
|
+
padding: 2px 6px;
|
|
16128
|
+
border-radius: var(--rad);
|
|
16129
|
+
}
|
|
16130
|
+
.copy-btn:hover { color: var(--ink); background: var(--paper-2); }
|
|
16131
|
+
|
|
16132
|
+
/* Surface 5. Attestation badge gallery. */
|
|
16133
|
+
.att-gallery {
|
|
16134
|
+
display: flex; flex-direction: column; gap: 24px;
|
|
16135
|
+
max-width: 1000px;
|
|
16136
|
+
margin: 0 auto;
|
|
16137
|
+
}
|
|
16138
|
+
.att-section {
|
|
16139
|
+
background: var(--surface);
|
|
16140
|
+
border: 1px solid var(--rule);
|
|
16141
|
+
border-radius: var(--rad-lg);
|
|
16142
|
+
padding: 22px 24px;
|
|
16143
|
+
}
|
|
16144
|
+
.att-section-head {
|
|
16145
|
+
display: flex; justify-content: space-between; align-items: baseline;
|
|
16146
|
+
gap: 12px;
|
|
16147
|
+
margin-bottom: 16px;
|
|
16148
|
+
padding-bottom: 12px;
|
|
16149
|
+
border-bottom: 1px solid var(--rule);
|
|
16150
|
+
}
|
|
16151
|
+
.att-section-head h2 {
|
|
16152
|
+
font-family: var(--serif);
|
|
16153
|
+
font-weight: 400;
|
|
16154
|
+
font-size: 19px;
|
|
16155
|
+
margin: 0 0 4px;
|
|
16156
|
+
}
|
|
16157
|
+
.att-section-head p {
|
|
16158
|
+
color: var(--ink-3);
|
|
16159
|
+
margin: 0;
|
|
16160
|
+
font-size: 13px;
|
|
16161
|
+
line-height: 1.5;
|
|
16162
|
+
max-width: 64ch;
|
|
16163
|
+
}
|
|
16164
|
+
.att-section-head .label {
|
|
16165
|
+
font-family: var(--mono);
|
|
16166
|
+
font-size: 10px;
|
|
16167
|
+
letter-spacing: 0.08em;
|
|
16168
|
+
text-transform: uppercase;
|
|
16169
|
+
color: var(--ink-3);
|
|
16170
|
+
}
|
|
16171
|
+
.att-row {
|
|
16172
|
+
display: grid;
|
|
16173
|
+
grid-template-columns: 240px 1fr;
|
|
16174
|
+
gap: 24px;
|
|
16175
|
+
padding: 14px 0;
|
|
16176
|
+
border-bottom: 1px dashed var(--rule);
|
|
16177
|
+
align-items: center;
|
|
16178
|
+
}
|
|
16179
|
+
.att-row:last-child { border-bottom: 0; }
|
|
16180
|
+
.att-row .demo {
|
|
16181
|
+
display: flex; align-items: center; justify-content: flex-start;
|
|
16182
|
+
padding: 12px 16px;
|
|
16183
|
+
background: var(--paper-2);
|
|
16184
|
+
border: 1px solid var(--rule);
|
|
16185
|
+
border-radius: var(--rad);
|
|
16186
|
+
min-height: 56px;
|
|
16187
|
+
}
|
|
16188
|
+
.att-row .desc strong {
|
|
16189
|
+
font-size: 13px; display: block; margin-bottom: 3px;
|
|
16190
|
+
}
|
|
16191
|
+
.att-row .desc small {
|
|
16192
|
+
color: var(--ink-3); font-size: 12px;
|
|
16193
|
+
line-height: 1.5;
|
|
16194
|
+
}
|
|
16195
|
+
|
|
16196
|
+
/* Global persistent badge. Lives in the topbar across every surface. */
|
|
16197
|
+
.att-global {
|
|
16198
|
+
display: inline-flex; align-items: center;
|
|
16199
|
+
gap: 8px;
|
|
16200
|
+
padding: 4px 10px 4px 6px;
|
|
16201
|
+
border: 1px solid var(--rule);
|
|
16202
|
+
border-radius: 999px;
|
|
16203
|
+
background: var(--surface-2);
|
|
16204
|
+
font-family: var(--mono);
|
|
16205
|
+
font-size: 11px;
|
|
16206
|
+
color: var(--ink-2);
|
|
16207
|
+
}
|
|
16208
|
+
.att-global.verified { border-color: var(--sage); background: var(--sage-bg); color: var(--sage); }
|
|
16209
|
+
.att-global.degraded { border-color: var(--ochre); background: var(--ochre-bg); color: var(--ochre); }
|
|
16210
|
+
.att-global.unverified { border-color: var(--rust); background: var(--rust-bg); color: var(--rust); }
|
|
16211
|
+
.att-global .seal {
|
|
16212
|
+
width: 18px; height: 18px;
|
|
16213
|
+
position: relative;
|
|
16214
|
+
flex-shrink: 0;
|
|
16215
|
+
}
|
|
16216
|
+
.att-global .seal-ring {
|
|
16217
|
+
position: absolute; inset: 0;
|
|
16218
|
+
border: 1.5px solid currentColor;
|
|
16219
|
+
border-radius: 50%;
|
|
16220
|
+
}
|
|
16221
|
+
.att-global .seal-ring.dashed { border-style: dashed; }
|
|
16222
|
+
.att-global .seal-core {
|
|
16223
|
+
position: absolute; inset: 4px;
|
|
16224
|
+
background: currentColor;
|
|
16225
|
+
border-radius: 50%;
|
|
16226
|
+
opacity: 0.85;
|
|
16227
|
+
}
|
|
16228
|
+
.att-global.degraded .seal-core { background: transparent; border: 1px solid currentColor; }
|
|
16229
|
+
.att-global.unverified .seal-core {
|
|
16230
|
+
background: transparent;
|
|
16231
|
+
border: 1px solid currentColor;
|
|
15122
16232
|
}
|
|
16233
|
+
.att-global.unverified .seal-core::after {
|
|
16234
|
+
content: ""; position: absolute; inset: 0;
|
|
16235
|
+
background: currentColor; opacity: 0.4;
|
|
16236
|
+
clip-path: polygon(0 0, 100% 100%, 100% 90%, 10% 0);
|
|
16237
|
+
}
|
|
16238
|
+
.att-global .label {
|
|
16239
|
+
font-family: var(--mono);
|
|
16240
|
+
font-size: 11px;
|
|
16241
|
+
letter-spacing: 0.02em;
|
|
16242
|
+
text-transform: uppercase;
|
|
16243
|
+
}
|
|
16244
|
+
.att-global .hash {
|
|
16245
|
+
font-family: var(--mono);
|
|
16246
|
+
font-size: 10px;
|
|
16247
|
+
opacity: 0.7;
|
|
16248
|
+
border-left: 1px solid currentColor;
|
|
16249
|
+
padding-left: 8px;
|
|
16250
|
+
margin-left: 2px;
|
|
16251
|
+
}
|
|
16252
|
+
|
|
16253
|
+
/* Per-agent badge. Square chip beside each agent. */
|
|
16254
|
+
.att-agent {
|
|
16255
|
+
display: inline-flex; align-items: center;
|
|
16256
|
+
gap: 6px;
|
|
16257
|
+
padding: 3px 7px;
|
|
16258
|
+
border-radius: var(--rad);
|
|
16259
|
+
border: 1px solid var(--rule);
|
|
16260
|
+
background: var(--surface);
|
|
16261
|
+
font-family: var(--mono);
|
|
16262
|
+
font-size: 10px;
|
|
16263
|
+
color: var(--ink-2);
|
|
16264
|
+
}
|
|
16265
|
+
.att-agent .mark {
|
|
16266
|
+
width: 10px; height: 10px;
|
|
16267
|
+
border: 1.5px solid currentColor;
|
|
16268
|
+
border-radius: 2px;
|
|
16269
|
+
position: relative;
|
|
16270
|
+
}
|
|
16271
|
+
.att-agent.verified { color: var(--sage); border-color: var(--sage); background: var(--sage-bg); }
|
|
16272
|
+
.att-agent.verified .mark { background: currentColor; }
|
|
16273
|
+
.att-agent.degraded { color: var(--ochre); border-color: var(--ochre); background: var(--ochre-bg); }
|
|
16274
|
+
.att-agent.unverified { color: var(--rust); border-color: var(--rust); background: var(--rust-bg); }
|
|
16275
|
+
.att-agent.unverified .mark {
|
|
16276
|
+
background: repeating-linear-gradient(
|
|
16277
|
+
45deg, currentColor, currentColor 1px,
|
|
16278
|
+
transparent 1px, transparent 3px
|
|
16279
|
+
);
|
|
16280
|
+
}
|
|
16281
|
+
|
|
16282
|
+
/* Per-action badge. Tiny inline tick on timeline rows. */
|
|
16283
|
+
.att-action {
|
|
16284
|
+
display: inline-flex; align-items: center; gap: 4px;
|
|
16285
|
+
font-family: var(--mono);
|
|
16286
|
+
font-size: 10px;
|
|
16287
|
+
color: var(--ink-3);
|
|
16288
|
+
padding: 1px 6px;
|
|
16289
|
+
border-radius: 4px;
|
|
16290
|
+
background: var(--paper-3);
|
|
16291
|
+
border: 1px solid transparent;
|
|
16292
|
+
}
|
|
16293
|
+
.att-action .tick {
|
|
16294
|
+
width: 6px; height: 6px;
|
|
16295
|
+
border-radius: 1px;
|
|
16296
|
+
background: currentColor;
|
|
16297
|
+
}
|
|
16298
|
+
.att-action.verified { color: var(--sage); }
|
|
16299
|
+
.att-action.degraded { color: var(--ochre); }
|
|
16300
|
+
.att-action.unverified { color: var(--rust); }
|
|
16301
|
+
.att-action.neutral .tick { background: var(--ink-4); border-radius: 50%; }
|
|
16302
|
+
|
|
16303
|
+
/* Custody-provenance badge stub (v1.x). Visibly stubbed with dashed border. */
|
|
16304
|
+
.att-custody {
|
|
16305
|
+
display: inline-flex; align-items: center; gap: 8px;
|
|
16306
|
+
padding: 4px 10px 4px 6px;
|
|
16307
|
+
border-radius: var(--rad);
|
|
16308
|
+
border: 1px dashed var(--rule-2);
|
|
16309
|
+
background: var(--paper-3);
|
|
16310
|
+
color: var(--ink-3);
|
|
16311
|
+
font-family: var(--mono);
|
|
16312
|
+
font-size: 10px;
|
|
16313
|
+
}
|
|
16314
|
+
.att-custody .seal-stub {
|
|
16315
|
+
width: 16px; height: 16px;
|
|
16316
|
+
border: 1px dashed var(--ink-4);
|
|
16317
|
+
border-radius: 50%;
|
|
16318
|
+
position: relative;
|
|
16319
|
+
flex-shrink: 0;
|
|
16320
|
+
}
|
|
16321
|
+
.att-custody .seal-stub::after {
|
|
16322
|
+
content: ""; position: absolute; inset: 4px;
|
|
16323
|
+
border: 1px dashed var(--ink-4);
|
|
16324
|
+
border-radius: 50%;
|
|
16325
|
+
}
|
|
16326
|
+
.att-custody .stub-tag {
|
|
16327
|
+
letter-spacing: 0.06em;
|
|
16328
|
+
text-transform: uppercase;
|
|
16329
|
+
}
|
|
16330
|
+
|
|
16331
|
+
/* Tooltip surface for badges. */
|
|
16332
|
+
.att-tooltip {
|
|
16333
|
+
background: var(--ink);
|
|
16334
|
+
color: var(--paper);
|
|
16335
|
+
font-family: var(--mono);
|
|
16336
|
+
font-size: 11px;
|
|
16337
|
+
padding: 8px 10px;
|
|
16338
|
+
border-radius: var(--rad);
|
|
16339
|
+
max-width: 280px;
|
|
16340
|
+
line-height: 1.5;
|
|
16341
|
+
display: inline-block;
|
|
16342
|
+
}
|
|
16343
|
+
[data-theme="dark"] .att-tooltip {
|
|
16344
|
+
background: var(--paper-3);
|
|
16345
|
+
color: var(--ink);
|
|
16346
|
+
}
|
|
16347
|
+
|
|
15123
16348
|
@media (max-width: 1100px) {
|
|
15124
16349
|
.app, .app.route-full { grid-template-columns: 56px 1fr; grid-template-areas: "sidebar topbar" "sidebar main"; }
|
|
15125
16350
|
.fortress { display: none; }
|
|
15126
16351
|
.sidebar h1, .sidebar nav a span { display: none; }
|
|
16352
|
+
.sidebar nav a { justify-content: center; padding: 8px 6px; }
|
|
15127
16353
|
.template-grid { grid-template-columns: 1fr; }
|
|
15128
16354
|
.policy-center h1 { font-size: 30px; }
|
|
15129
16355
|
.intel-center h1 { font-size: 30px; }
|
|
15130
16356
|
.intel-row { grid-template-columns: 1fr; }
|
|
16357
|
+
.intel-grid { grid-template-columns: 1fr; }
|
|
16358
|
+
.intel-failure-row { grid-template-columns: 1fr; }
|
|
16359
|
+
.agents-layout { grid-template-columns: 1fr; }
|
|
16360
|
+
.agents-list-head, .agents-list .agent-row { grid-template-columns: minmax(0, 1fr) 90px 90px; }
|
|
16361
|
+
.agents-list-head span:nth-child(4), .agents-list .agent-row > .agent-last { display: none; }
|
|
16362
|
+
.inspect-pane { position: static; max-height: none; }
|
|
15131
16363
|
}
|
|
15132
16364
|
`;
|
|
15133
16365
|
NAV_ITEMS = [
|
|
@@ -15135,11 +16367,26 @@ body {
|
|
|
15135
16367
|
{ id: "agents", label: "Agents" },
|
|
15136
16368
|
{ id: "policy", label: "Policy" },
|
|
15137
16369
|
{ id: "intelligence", label: "Intelligence" },
|
|
16370
|
+
{ id: "attestation", label: "Attestation" },
|
|
15138
16371
|
{ id: "privacy", label: "Privacy" },
|
|
15139
16372
|
{ id: "coordination", label: "Coordination" },
|
|
15140
16373
|
{ id: "health", label: "Health" },
|
|
15141
16374
|
{ id: "exit-drill", label: "Exit drill" }
|
|
15142
16375
|
];
|
|
16376
|
+
NAV_ICON_PATHS = {
|
|
16377
|
+
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"/>',
|
|
16378
|
+
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"/>',
|
|
16379
|
+
policy: '<path d="M4 2h5l3 3v9H4z"/><path d="M9 2v3h3"/><path d="M6 9h4M6 11.5h4"/>',
|
|
16380
|
+
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"/>',
|
|
16381
|
+
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"/>',
|
|
16382
|
+
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"/>',
|
|
16383
|
+
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"/>',
|
|
16384
|
+
health: '<path d="M2 8h2.5l1.5-4 3 8 1.5-4H14"/>',
|
|
16385
|
+
"exit-drill": '<path d="M9.5 2H3v12h6.5"/><path d="M11 5l3 3-3 3"/><path d="M14 8H6.5"/>'
|
|
16386
|
+
};
|
|
16387
|
+
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">';
|
|
16388
|
+
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>';
|
|
16389
|
+
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>';
|
|
15143
16390
|
}
|
|
15144
16391
|
});
|
|
15145
16392
|
|
|
@@ -15848,7 +17095,7 @@ var init_dashboard = __esm({
|
|
|
15848
17095
|
server = createServer$2(handler);
|
|
15849
17096
|
}
|
|
15850
17097
|
this.httpServer = server;
|
|
15851
|
-
return new Promise((
|
|
17098
|
+
return new Promise((resolve6, reject) => {
|
|
15852
17099
|
const protocol = this.useTLS ? "https" : "http";
|
|
15853
17100
|
const baseUrl = `${protocol}://${this.config.host}:${this.config.port}`;
|
|
15854
17101
|
server.listen(this.config.port, this.config.host, () => {
|
|
@@ -15873,7 +17120,7 @@ var init_dashboard = __esm({
|
|
|
15873
17120
|
if (shouldAutoOpen) {
|
|
15874
17121
|
this.openInBrowser(sessionUrl);
|
|
15875
17122
|
}
|
|
15876
|
-
|
|
17123
|
+
resolve6();
|
|
15877
17124
|
});
|
|
15878
17125
|
server.on("error", (err) => {
|
|
15879
17126
|
if (err.code === "EADDRINUSE") {
|
|
@@ -15919,8 +17166,8 @@ var init_dashboard = __esm({
|
|
|
15919
17166
|
}
|
|
15920
17167
|
this.rateLimits.clear();
|
|
15921
17168
|
if (this.httpServer) {
|
|
15922
|
-
return new Promise((
|
|
15923
|
-
this.httpServer.close(() =>
|
|
17169
|
+
return new Promise((resolve6) => {
|
|
17170
|
+
this.httpServer.close(() => resolve6());
|
|
15924
17171
|
});
|
|
15925
17172
|
}
|
|
15926
17173
|
}
|
|
@@ -15934,7 +17181,7 @@ var init_dashboard = __esm({
|
|
|
15934
17181
|
`[Sanctuary] Approval required: ${request.operation} (Tier ${request.tier}) \u2014 open dashboard to respond
|
|
15935
17182
|
`
|
|
15936
17183
|
);
|
|
15937
|
-
return new Promise((
|
|
17184
|
+
return new Promise((resolve6) => {
|
|
15938
17185
|
const timer = setTimeout(() => {
|
|
15939
17186
|
this.pending.delete(id);
|
|
15940
17187
|
const response = {
|
|
@@ -15948,12 +17195,12 @@ var init_dashboard = __esm({
|
|
|
15948
17195
|
decision: response.decision,
|
|
15949
17196
|
decided_by: "timeout"
|
|
15950
17197
|
});
|
|
15951
|
-
|
|
17198
|
+
resolve6(response);
|
|
15952
17199
|
}, this.config.timeout_seconds * 1e3);
|
|
15953
17200
|
const pending = {
|
|
15954
17201
|
id,
|
|
15955
17202
|
request,
|
|
15956
|
-
resolve:
|
|
17203
|
+
resolve: resolve6,
|
|
15957
17204
|
timer,
|
|
15958
17205
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
15959
17206
|
};
|
|
@@ -16819,7 +18066,7 @@ var init_webhook = __esm({
|
|
|
16819
18066
|
* Start the callback listener server.
|
|
16820
18067
|
*/
|
|
16821
18068
|
async start() {
|
|
16822
|
-
return new Promise((
|
|
18069
|
+
return new Promise((resolve6, reject) => {
|
|
16823
18070
|
this.callbackServer = createServer$2(
|
|
16824
18071
|
(req, res) => this.handleCallback(req, res)
|
|
16825
18072
|
);
|
|
@@ -16834,7 +18081,7 @@ var init_webhook = __esm({
|
|
|
16834
18081
|
|
|
16835
18082
|
`
|
|
16836
18083
|
);
|
|
16837
|
-
|
|
18084
|
+
resolve6();
|
|
16838
18085
|
}
|
|
16839
18086
|
);
|
|
16840
18087
|
this.callbackServer.on("error", reject);
|
|
@@ -16854,8 +18101,8 @@ var init_webhook = __esm({
|
|
|
16854
18101
|
}
|
|
16855
18102
|
this.pending.clear();
|
|
16856
18103
|
if (this.callbackServer) {
|
|
16857
|
-
return new Promise((
|
|
16858
|
-
this.callbackServer.close(() =>
|
|
18104
|
+
return new Promise((resolve6) => {
|
|
18105
|
+
this.callbackServer.close(() => resolve6());
|
|
16859
18106
|
});
|
|
16860
18107
|
}
|
|
16861
18108
|
}
|
|
@@ -16868,7 +18115,7 @@ var init_webhook = __esm({
|
|
|
16868
18115
|
`[Sanctuary] Webhook approval sent: ${request.operation} (Tier ${request.tier}) \u2014 awaiting callback
|
|
16869
18116
|
`
|
|
16870
18117
|
);
|
|
16871
|
-
return new Promise((
|
|
18118
|
+
return new Promise((resolve6) => {
|
|
16872
18119
|
const timer = setTimeout(() => {
|
|
16873
18120
|
this.pending.delete(id);
|
|
16874
18121
|
const response = {
|
|
@@ -16877,12 +18124,12 @@ var init_webhook = __esm({
|
|
|
16877
18124
|
decided_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16878
18125
|
decided_by: "timeout"
|
|
16879
18126
|
};
|
|
16880
|
-
|
|
18127
|
+
resolve6(response);
|
|
16881
18128
|
}, this.config.timeout_seconds * 1e3);
|
|
16882
18129
|
const pending = {
|
|
16883
18130
|
id,
|
|
16884
18131
|
request,
|
|
16885
|
-
resolve:
|
|
18132
|
+
resolve: resolve6,
|
|
16886
18133
|
timer,
|
|
16887
18134
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
16888
18135
|
};
|
|
@@ -18547,8 +19794,8 @@ function verifySHR(shr, now) {
|
|
|
18547
19794
|
const errors = [];
|
|
18548
19795
|
const warnings = [];
|
|
18549
19796
|
const currentTime = /* @__PURE__ */ new Date();
|
|
18550
|
-
if (!shr.body || !shr.signed_by || !shr.signature) {
|
|
18551
|
-
errors.push("Missing required SHR fields (body, signed_by, or signature)");
|
|
19797
|
+
if (!shr.body || !shr.signed_by || !shr.signature || !shr.signature_scheme) {
|
|
19798
|
+
errors.push("Missing required SHR fields (body, signed_by, signature_scheme, or signature)");
|
|
18552
19799
|
return {
|
|
18553
19800
|
valid: false,
|
|
18554
19801
|
errors,
|
|
@@ -18561,6 +19808,9 @@ function verifySHR(shr, now) {
|
|
|
18561
19808
|
if (shr.body.shr_version !== "1.0") {
|
|
18562
19809
|
errors.push(`Unsupported SHR version: ${shr.body.shr_version}`);
|
|
18563
19810
|
}
|
|
19811
|
+
if (shr.signature_scheme !== SIGNATURE_SCHEME_V1) {
|
|
19812
|
+
errors.push(`Unsupported SHR signature_scheme: ${String(shr.signature_scheme)}`);
|
|
19813
|
+
}
|
|
18564
19814
|
const expiresAt = new Date(shr.body.expires_at);
|
|
18565
19815
|
if (isNaN(expiresAt.getTime())) {
|
|
18566
19816
|
errors.push("Invalid expires_at timestamp");
|
|
@@ -18622,6 +19872,7 @@ var init_verifier = __esm({
|
|
|
18622
19872
|
init_types();
|
|
18623
19873
|
init_identity();
|
|
18624
19874
|
init_encoding();
|
|
19875
|
+
init_constants();
|
|
18625
19876
|
}
|
|
18626
19877
|
});
|
|
18627
19878
|
|
|
@@ -23581,7 +24832,7 @@ async function runOpenAIPrivacyFilter(text, config) {
|
|
|
23581
24832
|
return parsed;
|
|
23582
24833
|
}
|
|
23583
24834
|
function runCommand(command, input, timeoutMs) {
|
|
23584
|
-
return new Promise((
|
|
24835
|
+
return new Promise((resolve6, reject) => {
|
|
23585
24836
|
const child = spawn(command, [], {
|
|
23586
24837
|
stdio: ["pipe", "pipe", "pipe"],
|
|
23587
24838
|
shell: false
|
|
@@ -23612,7 +24863,7 @@ function runCommand(command, input, timeoutMs) {
|
|
|
23612
24863
|
));
|
|
23613
24864
|
return;
|
|
23614
24865
|
}
|
|
23615
|
-
|
|
24866
|
+
resolve6(stdout);
|
|
23616
24867
|
});
|
|
23617
24868
|
child.stdin.end(input);
|
|
23618
24869
|
});
|
|
@@ -25488,13 +26739,13 @@ var init_proxy_router = __esm({
|
|
|
25488
26739
|
* Call an upstream tool with a timeout.
|
|
25489
26740
|
*/
|
|
25490
26741
|
async callWithTimeout(serverName, toolName, args, timeoutMs) {
|
|
25491
|
-
return new Promise((
|
|
26742
|
+
return new Promise((resolve6, reject) => {
|
|
25492
26743
|
const timer = setTimeout(() => {
|
|
25493
26744
|
reject(new Error(`Upstream tool call timed out after ${timeoutMs}ms`));
|
|
25494
26745
|
}, timeoutMs);
|
|
25495
26746
|
this.clientManager.callTool(serverName, toolName, args).then((result) => {
|
|
25496
26747
|
clearTimeout(timer);
|
|
25497
|
-
|
|
26748
|
+
resolve6(result);
|
|
25498
26749
|
}).catch((err) => {
|
|
25499
26750
|
clearTimeout(timer);
|
|
25500
26751
|
reject(err);
|
|
@@ -29999,8 +31250,6 @@ var init_inbox_aggregator = __esm({
|
|
|
29999
31250
|
"src/hub/inbox-aggregator.ts"() {
|
|
30000
31251
|
}
|
|
30001
31252
|
});
|
|
30002
|
-
|
|
30003
|
-
// src/hub/activity-feed.ts
|
|
30004
31253
|
function categorizeOperation(layer, operation) {
|
|
30005
31254
|
const op = operation.toLowerCase();
|
|
30006
31255
|
if (op === "privacy_event" || op.startsWith("privacy_")) return "privacy";
|
|
@@ -30051,18 +31300,30 @@ function extractAgentIdHint(entry) {
|
|
|
30051
31300
|
const value = details.agent_id;
|
|
30052
31301
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
30053
31302
|
}
|
|
31303
|
+
function deriveAttestationFragment(entryId) {
|
|
31304
|
+
const hash2 = createHash("sha256").update(entryId).digest("hex");
|
|
31305
|
+
return `${hash2.slice(0, 4)}..${hash2.slice(4, 6)}`;
|
|
31306
|
+
}
|
|
31307
|
+
function deriveAttestationState(entry) {
|
|
31308
|
+
return entry.result === "success" ? "verified" : "degraded";
|
|
31309
|
+
}
|
|
30054
31310
|
function projectEntry(entry) {
|
|
30055
31311
|
const agentIdHint = extractAgentIdHint(entry);
|
|
30056
31312
|
const category = categorizeOperation(entry.layer, entry.operation);
|
|
31313
|
+
const entryId = `${entry.timestamp}|${entry.operation}|${entry.identity_id}`;
|
|
30057
31314
|
return {
|
|
30058
31315
|
version: "1.1",
|
|
30059
|
-
entry_id:
|
|
31316
|
+
entry_id: entryId,
|
|
30060
31317
|
emitted_at: entry.timestamp,
|
|
30061
31318
|
...agentIdHint ? { agent_id: agentIdHint } : {},
|
|
30062
31319
|
identity_id: entry.identity_id,
|
|
30063
31320
|
category,
|
|
30064
31321
|
display_template_id: templateIdFor(category, entry.operation),
|
|
30065
|
-
display_template_args: buildTemplateArgs(entry, agentIdHint)
|
|
31322
|
+
display_template_args: buildTemplateArgs(entry, agentIdHint),
|
|
31323
|
+
attestation: {
|
|
31324
|
+
state: deriveAttestationState(entry),
|
|
31325
|
+
fragment: deriveAttestationFragment(entryId)
|
|
31326
|
+
}
|
|
30066
31327
|
};
|
|
30067
31328
|
}
|
|
30068
31329
|
async function aggregateActivity(sources, filter = {}) {
|
|
@@ -30127,7 +31388,7 @@ function snapshotForRecord(record, openInboxItemIds) {
|
|
|
30127
31388
|
var HubService;
|
|
30128
31389
|
var init_hub_service = __esm({
|
|
30129
31390
|
"src/hub/hub-service.ts"() {
|
|
30130
|
-
|
|
31391
|
+
init_constants2();
|
|
30131
31392
|
init_channel_templates();
|
|
30132
31393
|
init_constants3();
|
|
30133
31394
|
init_errors4();
|
|
@@ -33040,10 +34301,10 @@ var init_memory = __esm({
|
|
|
33040
34301
|
});
|
|
33041
34302
|
|
|
33042
34303
|
// src/contracts/v1.1/constants.ts
|
|
33043
|
-
var
|
|
34304
|
+
var SIGNATURE_SCHEME_V12, EXIT_BUNDLE_MANIFEST_VERSION, EXIT_BUNDLE_ARTIFACT_KINDS;
|
|
33044
34305
|
var init_constants4 = __esm({
|
|
33045
34306
|
"src/contracts/v1.1/constants.ts"() {
|
|
33046
|
-
|
|
34307
|
+
SIGNATURE_SCHEME_V12 = "ed25519-v1";
|
|
33047
34308
|
EXIT_BUNDLE_MANIFEST_VERSION = "SANCTUARY_EXIT_BUNDLE_V1";
|
|
33048
34309
|
EXIT_BUNDLE_ARTIFACT_KINDS = [
|
|
33049
34310
|
"public_identity",
|
|
@@ -33236,7 +34497,7 @@ function verifyReputationArtifact(reputationArtifact, publicKeysByDid) {
|
|
|
33236
34497
|
unverifiable_attestations: unverifiable
|
|
33237
34498
|
};
|
|
33238
34499
|
}
|
|
33239
|
-
async function verifyExitBundle(bundleDir) {
|
|
34500
|
+
async function verifyExitBundle(bundleDir, options = {}) {
|
|
33240
34501
|
const root = resolve(bundleDir);
|
|
33241
34502
|
let manifest;
|
|
33242
34503
|
let manifestBytes;
|
|
@@ -33245,7 +34506,9 @@ async function verifyExitBundle(bundleDir) {
|
|
|
33245
34506
|
manifestBytes = new Uint8Array(raw.buffer, raw.byteOffset, raw.byteLength);
|
|
33246
34507
|
manifest = JSON.parse(Buffer.from(raw).toString("utf8"));
|
|
33247
34508
|
} catch {
|
|
33248
|
-
|
|
34509
|
+
throw new InvalidExitBundleError(
|
|
34510
|
+
`Not a valid SANCTUARY_EXIT_BUNDLE_V1 directory: manifest.json missing at ${join(root, "manifest.json")}`
|
|
34511
|
+
);
|
|
33249
34512
|
}
|
|
33250
34513
|
const warnings = [];
|
|
33251
34514
|
const unsupportedArtifacts = [];
|
|
@@ -33253,7 +34516,7 @@ async function verifyExitBundle(bundleDir) {
|
|
|
33253
34516
|
if (!body || body.manifest_version !== EXIT_BUNDLE_MANIFEST_VERSION) {
|
|
33254
34517
|
return fail(root, manifest, "manifest_unknown_version", warnings, unsupportedArtifacts);
|
|
33255
34518
|
}
|
|
33256
|
-
if (body.signature_scheme !==
|
|
34519
|
+
if (body.signature_scheme !== SIGNATURE_SCHEME_V12) {
|
|
33257
34520
|
return fail(
|
|
33258
34521
|
root,
|
|
33259
34522
|
manifest,
|
|
@@ -33413,9 +34676,16 @@ async function verifyExitBundle(bundleDir) {
|
|
|
33413
34676
|
}
|
|
33414
34677
|
const reputationFailed = reputation?.bundle_signature_valid === false || (reputation?.invalid_attestations ?? 0) > 0;
|
|
33415
34678
|
const identityFailed = identity ? !identity.signature_valid : false;
|
|
34679
|
+
const unverifiableCount = reputation?.unverifiable_attestations ?? 0;
|
|
34680
|
+
const unverifiableFailed = unverifiableCount > 0 && !options.acceptUnverifiableAttestations;
|
|
34681
|
+
if (unverifiableFailed) {
|
|
34682
|
+
warnings.push(
|
|
34683
|
+
`${unverifiableCount} reputation attestation(s) have unknown signer public keys; pass --accept-unverifiable-attestations to import anyway`
|
|
34684
|
+
);
|
|
34685
|
+
}
|
|
33416
34686
|
return {
|
|
33417
34687
|
version: "1.1",
|
|
33418
|
-
passed: !reputationFailed && !identityFailed,
|
|
34688
|
+
passed: !reputationFailed && !identityFailed && !unverifiableFailed,
|
|
33419
34689
|
verified_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33420
34690
|
manifest_path: join(root, "manifest.json"),
|
|
33421
34691
|
manifest_hash: sha256Hex2(manifestBytes),
|
|
@@ -33432,10 +34702,10 @@ async function verifyExitBundle(bundleDir) {
|
|
|
33432
34702
|
identity,
|
|
33433
34703
|
audit,
|
|
33434
34704
|
reputation,
|
|
33435
|
-
failure_class: reputationFailed || identityFailed ? "other" : void 0
|
|
34705
|
+
failure_class: reputationFailed || identityFailed || unverifiableFailed ? "other" : void 0
|
|
33436
34706
|
};
|
|
33437
34707
|
}
|
|
33438
|
-
var PRIVATE_MATERIAL_KEYS;
|
|
34708
|
+
var InvalidExitBundleError, PRIVATE_MATERIAL_KEYS;
|
|
33439
34709
|
var init_verifier2 = __esm({
|
|
33440
34710
|
"src/exit/verifier.ts"() {
|
|
33441
34711
|
init_constants4();
|
|
@@ -33443,6 +34713,12 @@ var init_verifier2 = __esm({
|
|
|
33443
34713
|
init_encoding();
|
|
33444
34714
|
init_hashing();
|
|
33445
34715
|
init_canonical_json();
|
|
34716
|
+
InvalidExitBundleError = class extends Error {
|
|
34717
|
+
constructor(message) {
|
|
34718
|
+
super(message);
|
|
34719
|
+
this.name = "InvalidExitBundleError";
|
|
34720
|
+
}
|
|
34721
|
+
};
|
|
33446
34722
|
PRIVATE_MATERIAL_KEYS = /* @__PURE__ */ new Set([
|
|
33447
34723
|
"private_key",
|
|
33448
34724
|
"privatekey",
|
|
@@ -33748,7 +35024,7 @@ async function exportExitBundle(opts) {
|
|
|
33748
35024
|
),
|
|
33749
35025
|
artifacts_aggregate_hash_alg: "sha256",
|
|
33750
35026
|
export_approval_audit_id: exportApprovalAuditId,
|
|
33751
|
-
signature_scheme:
|
|
35027
|
+
signature_scheme: SIGNATURE_SCHEME_V12
|
|
33752
35028
|
};
|
|
33753
35029
|
const signature = sign(
|
|
33754
35030
|
canonicalizeToBytes(body),
|
|
@@ -33933,7 +35209,9 @@ async function stageArtifact(storage, namespace, key, value) {
|
|
|
33933
35209
|
await storage.write(namespace, key, jsonBytes(value));
|
|
33934
35210
|
}
|
|
33935
35211
|
async function importExitBundle(opts) {
|
|
33936
|
-
const verification = await verifyExitBundle(opts.bundleDir
|
|
35212
|
+
const verification = await verifyExitBundle(opts.bundleDir, {
|
|
35213
|
+
acceptUnverifiableAttestations: opts.acceptUnverifiableAttestations
|
|
35214
|
+
});
|
|
33937
35215
|
if (!verification.passed) {
|
|
33938
35216
|
return {
|
|
33939
35217
|
verified: false,
|
|
@@ -34029,6 +35307,23 @@ async function importExitBundle(opts) {
|
|
|
34029
35307
|
unsupported_artifacts: verification.unsupported_artifacts
|
|
34030
35308
|
};
|
|
34031
35309
|
}
|
|
35310
|
+
if (conflicts.public_identity_exists && !opts.forceRebind) {
|
|
35311
|
+
throw new ExitBundleImportError(
|
|
35312
|
+
"IDENTITY_OVERWRITE_REFUSED",
|
|
35313
|
+
"Importing this bundle would overwrite an existing fortress public identity. Pass forceRebind: true (CLI: --force-rebind) to confirm explicit replacement."
|
|
35314
|
+
);
|
|
35315
|
+
}
|
|
35316
|
+
if (conflicts.public_identity_exists && opts.forceRebind && identityArtifact) {
|
|
35317
|
+
opts.auditLog.append(
|
|
35318
|
+
"l1",
|
|
35319
|
+
"exit_bundle_force_rebind",
|
|
35320
|
+
identityArtifact.json.bundle.identity_id,
|
|
35321
|
+
{
|
|
35322
|
+
manifest_version: manifest.body.manifest_version,
|
|
35323
|
+
fortress_id: manifest.body.identity_binding.fortress_id
|
|
35324
|
+
}
|
|
35325
|
+
);
|
|
35326
|
+
}
|
|
34032
35327
|
const importId = importIdForManifest(manifest);
|
|
34033
35328
|
const stagedArtifacts = [];
|
|
34034
35329
|
if (identityArtifact) {
|
|
@@ -34139,7 +35434,7 @@ function exitBundleManifestShape() {
|
|
|
34139
35434
|
manifest_version: EXIT_BUNDLE_MANIFEST_VERSION,
|
|
34140
35435
|
artifacts: [...EXIT_BUNDLE_ARTIFACT_KINDS],
|
|
34141
35436
|
hash_alg: "sha256",
|
|
34142
|
-
signature_scheme:
|
|
35437
|
+
signature_scheme: SIGNATURE_SCHEME_V12,
|
|
34143
35438
|
required_top_level_file: "manifest.json",
|
|
34144
35439
|
artifact_paths: [
|
|
34145
35440
|
"artifacts/public_identity.json",
|
|
@@ -34152,7 +35447,7 @@ function exitBundleManifestShape() {
|
|
|
34152
35447
|
]
|
|
34153
35448
|
};
|
|
34154
35449
|
}
|
|
34155
|
-
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;
|
|
35450
|
+
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;
|
|
34156
35451
|
var init_bundle = __esm({
|
|
34157
35452
|
"src/exit/bundle.ts"() {
|
|
34158
35453
|
init_state_store();
|
|
@@ -34174,6 +35469,14 @@ var init_bundle = __esm({
|
|
|
34174
35469
|
EXIT_COMMITMENTS_NAMESPACE = "_exit_commitments";
|
|
34175
35470
|
EXIT_PLACEHOLDER_METADATA_NAMESPACE = "_exit_placeholder_metadata";
|
|
34176
35471
|
PRIVACY_PLACEHOLDER_NAMESPACE = "_privacy_placeholder_vault";
|
|
35472
|
+
ExitBundleImportError = class extends Error {
|
|
35473
|
+
code;
|
|
35474
|
+
constructor(code, message) {
|
|
35475
|
+
super(message);
|
|
35476
|
+
this.name = "ExitBundleImportError";
|
|
35477
|
+
this.code = code;
|
|
35478
|
+
}
|
|
35479
|
+
};
|
|
34177
35480
|
}
|
|
34178
35481
|
});
|
|
34179
35482
|
function write(stream, text) {
|
|
@@ -34272,6 +35575,11 @@ Options:
|
|
|
34272
35575
|
--destination-identity-id <id> Destination signer for re-keyed state
|
|
34273
35576
|
--state-namespace <name> Export a namespace; repeatable
|
|
34274
35577
|
--conflict <skip|overwrite|version>
|
|
35578
|
+
--force-rebind On import: explicitly replace an existing fortress
|
|
35579
|
+
public identity (Tier 1 confirmation)
|
|
35580
|
+
--accept-unverifiable-attestations
|
|
35581
|
+
On import: accept reputation attestations whose
|
|
35582
|
+
signer DID is not in the bundle (Tier 1 confirmation)
|
|
34275
35583
|
--json
|
|
34276
35584
|
--yes, -y Explicit non-interactive Tier 1 approval
|
|
34277
35585
|
--help, -h
|
|
@@ -34300,7 +35608,22 @@ async function runExitCommand(args) {
|
|
|
34300
35608
|
write(err, "Usage: sanctuary exit verify <dir>\n");
|
|
34301
35609
|
return 2;
|
|
34302
35610
|
}
|
|
34303
|
-
|
|
35611
|
+
let result;
|
|
35612
|
+
try {
|
|
35613
|
+
result = await verifyExitBundle(dir, {
|
|
35614
|
+
acceptUnverifiableAttestations: hasFlag(
|
|
35615
|
+
argv,
|
|
35616
|
+
"--accept-unverifiable-attestations"
|
|
35617
|
+
)
|
|
35618
|
+
});
|
|
35619
|
+
} catch (e) {
|
|
35620
|
+
if (e instanceof InvalidExitBundleError) {
|
|
35621
|
+
write(err, `Error: ${e.message}
|
|
35622
|
+
`);
|
|
35623
|
+
return 1;
|
|
35624
|
+
}
|
|
35625
|
+
throw e;
|
|
35626
|
+
}
|
|
34304
35627
|
if (json) {
|
|
34305
35628
|
write(out, JSON.stringify(result, null, 2) + "\n");
|
|
34306
35629
|
} else {
|
|
@@ -34378,9 +35701,15 @@ async function runExitCommand(args) {
|
|
|
34378
35701
|
return 2;
|
|
34379
35702
|
}
|
|
34380
35703
|
const activate = hasFlag(argv, "--activate");
|
|
35704
|
+
const forceRebind = hasFlag(argv, "--force-rebind");
|
|
35705
|
+
const acceptUnverifiableAttestations = hasFlag(
|
|
35706
|
+
argv,
|
|
35707
|
+
"--accept-unverifiable-attestations"
|
|
35708
|
+
);
|
|
34381
35709
|
if (activate) {
|
|
35710
|
+
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?";
|
|
34382
35711
|
const approved = await confirmTier1(
|
|
34383
|
-
|
|
35712
|
+
prompt2,
|
|
34384
35713
|
hasFlag(argv, "--yes") || hasFlag(argv, "-y"),
|
|
34385
35714
|
stdin,
|
|
34386
35715
|
err
|
|
@@ -34389,6 +35718,18 @@ async function runExitCommand(args) {
|
|
|
34389
35718
|
write(err, "Aborted.\n");
|
|
34390
35719
|
return 1;
|
|
34391
35720
|
}
|
|
35721
|
+
if (acceptUnverifiableAttestations) {
|
|
35722
|
+
const acceptApproved = await confirmTier1(
|
|
35723
|
+
"Tier 1 approval required: accept unverifiable reputation attestations on import?",
|
|
35724
|
+
hasFlag(argv, "--yes") || hasFlag(argv, "-y"),
|
|
35725
|
+
stdin,
|
|
35726
|
+
err
|
|
35727
|
+
);
|
|
35728
|
+
if (!acceptApproved) {
|
|
35729
|
+
write(err, "Aborted.\n");
|
|
35730
|
+
return 1;
|
|
35731
|
+
}
|
|
35732
|
+
}
|
|
34392
35733
|
}
|
|
34393
35734
|
const ctx = await openExitContext(argv, env);
|
|
34394
35735
|
const conflict = flagValue(argv, "--conflict") ?? "skip";
|
|
@@ -34396,19 +35737,31 @@ async function runExitCommand(args) {
|
|
|
34396
35737
|
write(err, "--conflict must be skip, overwrite, or version\n");
|
|
34397
35738
|
return 2;
|
|
34398
35739
|
}
|
|
34399
|
-
|
|
34400
|
-
|
|
34401
|
-
|
|
34402
|
-
|
|
34403
|
-
|
|
34404
|
-
|
|
34405
|
-
|
|
34406
|
-
|
|
34407
|
-
|
|
34408
|
-
|
|
34409
|
-
|
|
34410
|
-
|
|
34411
|
-
|
|
35740
|
+
let result;
|
|
35741
|
+
try {
|
|
35742
|
+
result = await importExitBundle({
|
|
35743
|
+
bundleDir: dir,
|
|
35744
|
+
storage: ctx.storage,
|
|
35745
|
+
masterKey: ctx.masterKey,
|
|
35746
|
+
identityManager: ctx.identityManager,
|
|
35747
|
+
auditLog: ctx.auditLog,
|
|
35748
|
+
reputationStore: ctx.reputationStore,
|
|
35749
|
+
activate,
|
|
35750
|
+
forceRebind,
|
|
35751
|
+
acceptUnverifiableAttestations,
|
|
35752
|
+
conflictResolution: conflict,
|
|
35753
|
+
sourcePassphrase: flagValue(argv, "--source-passphrase"),
|
|
35754
|
+
sourceRecoveryKey: flagValue(argv, "--source-recovery-key"),
|
|
35755
|
+
destinationSignerIdentityId: flagValue(argv, "--destination-identity-id")
|
|
35756
|
+
});
|
|
35757
|
+
} catch (e) {
|
|
35758
|
+
if (e instanceof InvalidExitBundleError) {
|
|
35759
|
+
write(err, `Error: ${e.message}
|
|
35760
|
+
`);
|
|
35761
|
+
return 1;
|
|
35762
|
+
}
|
|
35763
|
+
throw e;
|
|
35764
|
+
}
|
|
34412
35765
|
if (json) write(out, JSON.stringify(result, null, 2) + "\n");
|
|
34413
35766
|
else {
|
|
34414
35767
|
write(out, `verified: ${result.verified}
|
|
@@ -34463,6 +35816,7 @@ var init_cli = __esm({
|
|
|
34463
35816
|
// src/exit/index.ts
|
|
34464
35817
|
var exit_exports = {};
|
|
34465
35818
|
__export(exit_exports, {
|
|
35819
|
+
ExitBundleImportError: () => ExitBundleImportError,
|
|
34466
35820
|
exitBundleManifestShape: () => exitBundleManifestShape,
|
|
34467
35821
|
exportExitBundle: () => exportExitBundle,
|
|
34468
35822
|
importExitBundle: () => importExitBundle,
|
|
@@ -34519,11 +35873,11 @@ async function startDashboardServer(options) {
|
|
|
34519
35873
|
}
|
|
34520
35874
|
}
|
|
34521
35875
|
});
|
|
34522
|
-
await new Promise((
|
|
35876
|
+
await new Promise((resolve6, reject) => {
|
|
34523
35877
|
server.once("error", reject);
|
|
34524
35878
|
server.listen(port, host, () => {
|
|
34525
35879
|
server.off("error", reject);
|
|
34526
|
-
|
|
35880
|
+
resolve6();
|
|
34527
35881
|
});
|
|
34528
35882
|
});
|
|
34529
35883
|
const actualPort = (() => {
|
|
@@ -34536,8 +35890,8 @@ async function startDashboardServer(options) {
|
|
|
34536
35890
|
url,
|
|
34537
35891
|
port: actualPort,
|
|
34538
35892
|
host,
|
|
34539
|
-
stop: () => new Promise((
|
|
34540
|
-
server.close((err) => err ? reject(err) :
|
|
35893
|
+
stop: () => new Promise((resolve6, reject) => {
|
|
35894
|
+
server.close((err) => err ? reject(err) : resolve6());
|
|
34541
35895
|
}),
|
|
34542
35896
|
publish,
|
|
34543
35897
|
publishActivity: (entry) => publish({ type: "activity", data: entry }),
|
|
@@ -34685,7 +36039,7 @@ async function createSanctuaryServer(options) {
|
|
|
34685
36039
|
const hasKeyParams = existingNamespaces.some((e) => e.key === "key-params");
|
|
34686
36040
|
if (hasKeyParams) {
|
|
34687
36041
|
throw new Error(
|
|
34688
|
-
"Sanctuary:
|
|
36042
|
+
"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."
|
|
34689
36043
|
);
|
|
34690
36044
|
}
|
|
34691
36045
|
masterKey = generateRandomKey();
|
|
@@ -35210,7 +36564,7 @@ Refusing to start the cocoon while the reset-history marker is unreadable.`
|
|
|
35210
36564
|
clientManager.configure(enabledServers).catch((err) => {
|
|
35211
36565
|
console.error(`[Sanctuary] Failed to configure upstream servers: ${err instanceof Error ? err.message : "unknown error"}`);
|
|
35212
36566
|
});
|
|
35213
|
-
await new Promise((
|
|
36567
|
+
await new Promise((resolve6) => setTimeout(resolve6, 2e3));
|
|
35214
36568
|
const proxiedTools = proxyRouter.getProxiedTools();
|
|
35215
36569
|
if (proxiedTools.length > 0) {
|
|
35216
36570
|
allTools.push(...proxiedTools);
|
|
@@ -36049,8 +37403,8 @@ async function runWrap(options, deps = {}) {
|
|
|
36049
37403
|
passphraseValue = process.env.SANCTUARY_PASSPHRASE;
|
|
36050
37404
|
} else {
|
|
36051
37405
|
try {
|
|
36052
|
-
const
|
|
36053
|
-
const resolved = await
|
|
37406
|
+
const resolve6 = deps.resolvePassphrase ?? (() => getOrCreatePassphrase({ storagePath }));
|
|
37407
|
+
const resolved = await resolve6();
|
|
36054
37408
|
passphraseLocation = resolved.location;
|
|
36055
37409
|
passphraseSource = resolved.source;
|
|
36056
37410
|
passphraseValue = resolved.value;
|
|
@@ -36090,24 +37444,31 @@ async function runWrap(options, deps = {}) {
|
|
|
36090
37444
|
}
|
|
36091
37445
|
await mkdir(storagePath, { recursive: true, mode: 448 });
|
|
36092
37446
|
if (passphraseSource === "generated" && passphraseValue !== void 0) {
|
|
36093
|
-
|
|
36094
|
-
|
|
36095
|
-
|
|
36096
|
-
|
|
36097
|
-
|
|
36098
|
-
|
|
36099
|
-
|
|
36100
|
-
|
|
36101
|
-
|
|
36102
|
-
|
|
36103
|
-
|
|
36104
|
-
if (err instanceof PassphraseConfirmationDeclinedError || err instanceof PassphraseConfirmationNonInteractiveError) {
|
|
36105
|
-
console.error(`
|
|
37447
|
+
if (options.writePassphraseBackup) {
|
|
37448
|
+
try {
|
|
37449
|
+
await disclosePassphrase({
|
|
37450
|
+
passphrase: passphraseValue,
|
|
37451
|
+
storagePath: dirname(options.writePassphraseBackup),
|
|
37452
|
+
fortressId: fortressIdFromStoragePath(storagePath),
|
|
37453
|
+
mode: options.noOpen || process.stdin.isTTY !== true ? "no-confirm" : "interactive"
|
|
37454
|
+
});
|
|
37455
|
+
} catch (err) {
|
|
37456
|
+
if (err instanceof PassphraseConfirmationDeclinedError || err instanceof PassphraseConfirmationNonInteractiveError) {
|
|
37457
|
+
console.error(`
|
|
36106
37458
|
Sanctuary wrap: ${err.message}
|
|
36107
37459
|
`);
|
|
36108
|
-
|
|
37460
|
+
process.exit(2);
|
|
37461
|
+
}
|
|
37462
|
+
throw err;
|
|
36109
37463
|
}
|
|
36110
|
-
|
|
37464
|
+
} else {
|
|
37465
|
+
process.stderr.write(
|
|
37466
|
+
`
|
|
37467
|
+
Passphrase stored in macOS Keychain.
|
|
37468
|
+
Run 'sanctuary export-passphrase' to retrieve it.
|
|
37469
|
+
To write a plaintext backup: sanctuary wrap ... --write-passphrase-backup <path>
|
|
37470
|
+
`
|
|
37471
|
+
);
|
|
36111
37472
|
}
|
|
36112
37473
|
}
|
|
36113
37474
|
const profile = createWrapProfile(upstreamServers);
|
|
@@ -36198,6 +37559,8 @@ async function runWrap(options, deps = {}) {
|
|
|
36198
37559
|
serverCount: upstreamServers.length});
|
|
36199
37560
|
return;
|
|
36200
37561
|
}
|
|
37562
|
+
let intelligenceHealthy;
|
|
37563
|
+
let intelligenceError;
|
|
36201
37564
|
const authToken = generateAuthToken();
|
|
36202
37565
|
const startFn = deps.startDashboard ?? ((opts) => startDashboard({
|
|
36203
37566
|
port: opts.port,
|
|
@@ -36233,6 +37596,30 @@ async function runWrap(options, deps = {}) {
|
|
|
36233
37596
|
);
|
|
36234
37597
|
}
|
|
36235
37598
|
const wrapAuditLog = new AuditLog(v11Storage, derived.key);
|
|
37599
|
+
try {
|
|
37600
|
+
const { IdentityManager: IdentityManager2 } = await Promise.resolve().then(() => (init_tools(), tools_exports));
|
|
37601
|
+
const { createIdentity: createIdentity2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
37602
|
+
const { derivePurposeKey: derivePurposeKey2 } = await Promise.resolve().then(() => (init_key_derivation(), key_derivation_exports));
|
|
37603
|
+
const identityMgr = new IdentityManager2(v11Storage, derived.key);
|
|
37604
|
+
const loadResult = await identityMgr.load();
|
|
37605
|
+
if (loadResult.loaded === 0) {
|
|
37606
|
+
const identityEncKey = derivePurposeKey2(derived.key, "identity-encryption");
|
|
37607
|
+
const { storedIdentity, publicIdentity } = createIdentity2(
|
|
37608
|
+
"default",
|
|
37609
|
+
identityEncKey,
|
|
37610
|
+
"passphrase"
|
|
37611
|
+
);
|
|
37612
|
+
await identityMgr.save(storedIdentity);
|
|
37613
|
+
wrapAuditLog.append("l1", "identity_create", publicIdentity.identity_id, {
|
|
37614
|
+
label: "default",
|
|
37615
|
+
source: "wrap-auto"
|
|
37616
|
+
});
|
|
37617
|
+
}
|
|
37618
|
+
} catch (err) {
|
|
37619
|
+
console.error(
|
|
37620
|
+
` Note: default identity not created at wrap time (${err.message}).`
|
|
37621
|
+
);
|
|
37622
|
+
}
|
|
36236
37623
|
let wrapIntelligenceSelector;
|
|
36237
37624
|
try {
|
|
36238
37625
|
wrapIntelligenceSelector = new SubstrateSelector({
|
|
@@ -36242,7 +37629,10 @@ async function runWrap(options, deps = {}) {
|
|
|
36242
37629
|
identityId: `fortress:${storagePath}`
|
|
36243
37630
|
});
|
|
36244
37631
|
await wrapIntelligenceSelector.load();
|
|
37632
|
+
intelligenceHealthy = true;
|
|
36245
37633
|
} catch (err) {
|
|
37634
|
+
intelligenceHealthy = false;
|
|
37635
|
+
intelligenceError = err.message;
|
|
36246
37636
|
console.error(
|
|
36247
37637
|
` Note: Intelligence panel unavailable on wrap URL (${err.message}).`
|
|
36248
37638
|
);
|
|
@@ -36310,7 +37700,10 @@ async function runWrap(options, deps = {}) {
|
|
|
36310
37700
|
toolCount: countUpstreamTools(upstreamServers),
|
|
36311
37701
|
serverCount: upstreamServers.length,
|
|
36312
37702
|
dashboardUrl,
|
|
36313
|
-
browserOpened: !options.noOpen
|
|
37703
|
+
browserOpened: !options.noOpen,
|
|
37704
|
+
intelligenceHealthy,
|
|
37705
|
+
intelligenceError
|
|
37706
|
+
});
|
|
36314
37707
|
}
|
|
36315
37708
|
async function runCocoon(options) {
|
|
36316
37709
|
console.error(
|
|
@@ -36366,12 +37759,12 @@ async function defaultOpenBrowser(url) {
|
|
|
36366
37759
|
cmd = "xdg-open";
|
|
36367
37760
|
args = [url];
|
|
36368
37761
|
}
|
|
36369
|
-
await new Promise((
|
|
37762
|
+
await new Promise((resolve6) => {
|
|
36370
37763
|
const child = spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
36371
|
-
child.on("error", () =>
|
|
37764
|
+
child.on("error", () => resolve6());
|
|
36372
37765
|
child.on("spawn", () => {
|
|
36373
37766
|
child.unref();
|
|
36374
|
-
|
|
37767
|
+
resolve6();
|
|
36375
37768
|
});
|
|
36376
37769
|
});
|
|
36377
37770
|
}
|
|
@@ -36397,7 +37790,15 @@ function formatWrapSuccess(info) {
|
|
|
36397
37790
|
lines.push(` ${d("(browser auto-open suppressed)")}`);
|
|
36398
37791
|
}
|
|
36399
37792
|
lines.push("");
|
|
36400
|
-
|
|
37793
|
+
const l2Status = info.intelligenceHealthy === false ? "L2 Degraded (intelligence disabled)" : "L2 Degraded (no TEE)";
|
|
37794
|
+
lines.push(` ${b("Your agent is protected.")} L1 Full / ${l2Status} / L3 Full / L4 Full.`);
|
|
37795
|
+
if (info.intelligenceHealthy === false && info.intelligenceError) {
|
|
37796
|
+
const w = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
37797
|
+
lines.push("");
|
|
37798
|
+
lines.push(` ${w("\u26A0")} L2 intelligence disabled: ${info.intelligenceError}`);
|
|
37799
|
+
lines.push(` Concierge chat and substrate-driven explanations will not work until this is resolved.`);
|
|
37800
|
+
lines.push(` Run 'sanctuary intelligence diagnose' to inspect substrate config.`);
|
|
37801
|
+
}
|
|
36401
37802
|
lines.push("");
|
|
36402
37803
|
return lines.join("\n");
|
|
36403
37804
|
}
|
|
@@ -36421,9 +37822,16 @@ function formatWrapSuccessNoDashboard(info) {
|
|
|
36421
37822
|
` ${d("Dashboard spawn skipped per --no-dashboard. Run `sanctuary dashboard` separately for a persistent dashboard.")}`
|
|
36422
37823
|
);
|
|
36423
37824
|
lines.push("");
|
|
37825
|
+
const l2Status = info.intelligenceHealthy === false ? "L2 Degraded (intelligence disabled)" : "L2 Degraded (no TEE)";
|
|
36424
37826
|
lines.push(
|
|
36425
|
-
` ${b("Your agent is protected.")} L1 Full /
|
|
37827
|
+
` ${b("Your agent is protected.")} L1 Full / ${l2Status} / L3 Full / L4 Full.`
|
|
36426
37828
|
);
|
|
37829
|
+
if (info.intelligenceHealthy === false && info.intelligenceError) {
|
|
37830
|
+
const w = (s) => `\x1B[33m${s}\x1B[0m`;
|
|
37831
|
+
lines.push("");
|
|
37832
|
+
lines.push(` ${w("\u26A0")} L2 intelligence disabled: ${info.intelligenceError}`);
|
|
37833
|
+
lines.push(` Run 'sanctuary intelligence diagnose' to inspect substrate config.`);
|
|
37834
|
+
}
|
|
36427
37835
|
lines.push("");
|
|
36428
37836
|
return lines.join("\n");
|
|
36429
37837
|
}
|
|
@@ -36623,7 +38031,22 @@ function rawConfigContainsSanctuary(raw, agentPlatform) {
|
|
|
36623
38031
|
function parseWrapArgs(argv) {
|
|
36624
38032
|
const options = {};
|
|
36625
38033
|
for (let i = 0; i < argv.length; i++) {
|
|
36626
|
-
|
|
38034
|
+
const arg = argv[i];
|
|
38035
|
+
if (!arg.startsWith("-")) {
|
|
38036
|
+
const suggestion = WRAP_HARNESS_FLAGS.find(
|
|
38037
|
+
(f) => f.replace(/^--/, "") === arg
|
|
38038
|
+
);
|
|
38039
|
+
const hint = suggestion ? ` Did you mean ${suggestion}?` : "";
|
|
38040
|
+
throw new Error(
|
|
38041
|
+
`Unrecognized argument '${arg}'.${hint} Run 'sanctuary wrap --help' for valid flags.`
|
|
38042
|
+
);
|
|
38043
|
+
}
|
|
38044
|
+
if (!WRAP_BOOLEAN_FLAGS.has(arg) && !WRAP_VALUE_FLAGS.has(arg)) {
|
|
38045
|
+
throw new Error(
|
|
38046
|
+
`Unrecognized flag '${arg}'. Run 'sanctuary wrap --help' for valid flags.`
|
|
38047
|
+
);
|
|
38048
|
+
}
|
|
38049
|
+
switch (arg) {
|
|
36627
38050
|
case "--wrap":
|
|
36628
38051
|
options.wrap = argv[++i];
|
|
36629
38052
|
break;
|
|
@@ -36666,6 +38089,9 @@ function parseWrapArgs(argv) {
|
|
|
36666
38089
|
case "--dev-dist":
|
|
36667
38090
|
options.devDist = argv[++i];
|
|
36668
38091
|
break;
|
|
38092
|
+
case "--write-passphrase-backup":
|
|
38093
|
+
options.writePassphraseBackup = argv[++i];
|
|
38094
|
+
break;
|
|
36669
38095
|
case "--help":
|
|
36670
38096
|
case "-h":
|
|
36671
38097
|
printWrapHelp();
|
|
@@ -36725,7 +38151,7 @@ function printWrapHelp() {
|
|
|
36725
38151
|
5. Every tool call is logged, scanned, and tier-gated
|
|
36726
38152
|
`);
|
|
36727
38153
|
}
|
|
36728
|
-
var COCOON_GOVERNOR_DEFAULTS, PORT_FALLBACK_ATTEMPTS, parseCocoonArgs;
|
|
38154
|
+
var COCOON_GOVERNOR_DEFAULTS, PORT_FALLBACK_ATTEMPTS, WRAP_VALUE_FLAGS, WRAP_BOOLEAN_FLAGS, WRAP_HARNESS_FLAGS, parseCocoonArgs;
|
|
36729
38155
|
var init_cli2 = __esm({
|
|
36730
38156
|
"src/cocoon/cli.ts"() {
|
|
36731
38157
|
init_config_reader();
|
|
@@ -36748,6 +38174,28 @@ var init_cli2 = __esm({
|
|
|
36748
38174
|
lifetime_limit: 1e3
|
|
36749
38175
|
};
|
|
36750
38176
|
PORT_FALLBACK_ATTEMPTS = 20;
|
|
38177
|
+
WRAP_VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
38178
|
+
"--wrap",
|
|
38179
|
+
"--passphrase",
|
|
38180
|
+
"--port",
|
|
38181
|
+
"--fortress",
|
|
38182
|
+
"--dev-dist",
|
|
38183
|
+
"--write-passphrase-backup"
|
|
38184
|
+
]);
|
|
38185
|
+
WRAP_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
38186
|
+
"--openclaw",
|
|
38187
|
+
"--hermes",
|
|
38188
|
+
"--claude-code",
|
|
38189
|
+
"--cursor",
|
|
38190
|
+
"--cline",
|
|
38191
|
+
"--unwrap",
|
|
38192
|
+
"--dry-run",
|
|
38193
|
+
"--no-open",
|
|
38194
|
+
"--no-dashboard",
|
|
38195
|
+
"--help",
|
|
38196
|
+
"-h"
|
|
38197
|
+
]);
|
|
38198
|
+
WRAP_HARNESS_FLAGS = ["--openclaw", "--hermes", "--claude-code", "--cursor", "--cline"];
|
|
36751
38199
|
parseCocoonArgs = parseWrapArgs;
|
|
36752
38200
|
}
|
|
36753
38201
|
});
|
|
@@ -37645,7 +39093,7 @@ var init_backend_interface = __esm({
|
|
|
37645
39093
|
}
|
|
37646
39094
|
});
|
|
37647
39095
|
async function runSecurity(args, input) {
|
|
37648
|
-
return new Promise((
|
|
39096
|
+
return new Promise((resolve6, reject) => {
|
|
37649
39097
|
const child = spawn(SECURITY_BIN, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
37650
39098
|
let stdout = "";
|
|
37651
39099
|
let stderr = "";
|
|
@@ -37667,7 +39115,7 @@ async function runSecurity(args, input) {
|
|
|
37667
39115
|
reject(err);
|
|
37668
39116
|
});
|
|
37669
39117
|
child.on("close", (code) => {
|
|
37670
|
-
|
|
39118
|
+
resolve6({ stdout, stderr, code: code ?? -1 });
|
|
37671
39119
|
});
|
|
37672
39120
|
if (input !== void 0) {
|
|
37673
39121
|
child.stdin.write(input);
|
|
@@ -38743,7 +40191,7 @@ async function readValue(stdin, prompt2) {
|
|
|
38743
40191
|
return await readFirstLine(stdin);
|
|
38744
40192
|
}
|
|
38745
40193
|
async function readFirstLine(stdin) {
|
|
38746
|
-
return new Promise((
|
|
40194
|
+
return new Promise((resolve6, reject) => {
|
|
38747
40195
|
const rl = createInterface$1({ input: stdin });
|
|
38748
40196
|
let resolved = false;
|
|
38749
40197
|
const finish = (value) => {
|
|
@@ -38754,7 +40202,7 @@ async function readFirstLine(stdin) {
|
|
|
38754
40202
|
rl.close();
|
|
38755
40203
|
} catch {
|
|
38756
40204
|
}
|
|
38757
|
-
|
|
40205
|
+
resolve6(value);
|
|
38758
40206
|
};
|
|
38759
40207
|
const deadline = setTimeout(() => {
|
|
38760
40208
|
finish("");
|
|
@@ -38773,7 +40221,7 @@ async function promptSilently(stdin, prompt2) {
|
|
|
38773
40221
|
process.stderr.write(`${prompt2}: `);
|
|
38774
40222
|
stdin.setRawMode?.(true);
|
|
38775
40223
|
stdin.resume();
|
|
38776
|
-
return await new Promise((
|
|
40224
|
+
return await new Promise((resolve6) => {
|
|
38777
40225
|
let buf = "";
|
|
38778
40226
|
const onData = (chunk) => {
|
|
38779
40227
|
const s = chunk.toString("utf8");
|
|
@@ -38783,7 +40231,7 @@ async function promptSilently(stdin, prompt2) {
|
|
|
38783
40231
|
stdin.pause();
|
|
38784
40232
|
stdin.off("data", onData);
|
|
38785
40233
|
process.stderr.write("\n");
|
|
38786
|
-
|
|
40234
|
+
resolve6(buf);
|
|
38787
40235
|
return;
|
|
38788
40236
|
}
|
|
38789
40237
|
if (ch === "") {
|
|
@@ -39056,7 +40504,7 @@ async function probeTenantDashboard(tenant, options = {}) {
|
|
|
39056
40504
|
return { running: false, status: null, reason: "no runtime.json" };
|
|
39057
40505
|
}
|
|
39058
40506
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS4;
|
|
39059
|
-
return await new Promise((
|
|
40507
|
+
return await new Promise((resolve6) => {
|
|
39060
40508
|
const req = get$1(
|
|
39061
40509
|
{
|
|
39062
40510
|
host: rt.dashboard_host,
|
|
@@ -39068,9 +40516,9 @@ async function probeTenantDashboard(tenant, options = {}) {
|
|
|
39068
40516
|
res.resume();
|
|
39069
40517
|
const status = res.statusCode ?? 0;
|
|
39070
40518
|
if (status > 0 && status < 500) {
|
|
39071
|
-
|
|
40519
|
+
resolve6({ running: true, status, reason: null });
|
|
39072
40520
|
} else {
|
|
39073
|
-
|
|
40521
|
+
resolve6({
|
|
39074
40522
|
running: false,
|
|
39075
40523
|
status,
|
|
39076
40524
|
reason: `dashboard returned ${status}`
|
|
@@ -39080,10 +40528,10 @@ async function probeTenantDashboard(tenant, options = {}) {
|
|
|
39080
40528
|
);
|
|
39081
40529
|
req.on("timeout", () => {
|
|
39082
40530
|
req.destroy();
|
|
39083
|
-
|
|
40531
|
+
resolve6({ running: false, status: null, reason: "timeout" });
|
|
39084
40532
|
});
|
|
39085
40533
|
req.on("error", (err) => {
|
|
39086
|
-
|
|
40534
|
+
resolve6({
|
|
39087
40535
|
running: false,
|
|
39088
40536
|
status: null,
|
|
39089
40537
|
reason: err.code ?? err.message
|
|
@@ -39745,7 +41193,7 @@ async function prompt(lines, err, question) {
|
|
|
39745
41193
|
return await lines.next();
|
|
39746
41194
|
}
|
|
39747
41195
|
async function defaultExec2(cmd, args) {
|
|
39748
|
-
return await new Promise((
|
|
41196
|
+
return await new Promise((resolve6, reject) => {
|
|
39749
41197
|
const child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
39750
41198
|
let stdout = "";
|
|
39751
41199
|
let stderr = "";
|
|
@@ -39756,7 +41204,7 @@ async function defaultExec2(cmd, args) {
|
|
|
39756
41204
|
stderr += d.toString();
|
|
39757
41205
|
});
|
|
39758
41206
|
child.on("error", reject);
|
|
39759
|
-
child.on("close", (code) =>
|
|
41207
|
+
child.on("close", (code) => resolve6({ stdout, stderr, code }));
|
|
39760
41208
|
});
|
|
39761
41209
|
}
|
|
39762
41210
|
var LineReader;
|
|
@@ -39792,8 +41240,8 @@ var init_reset_passphrase = __esm({
|
|
|
39792
41240
|
return Promise.resolve(this.queue.shift());
|
|
39793
41241
|
}
|
|
39794
41242
|
if (this.closed) return Promise.resolve("");
|
|
39795
|
-
return new Promise((
|
|
39796
|
-
this.waiters.push(
|
|
41243
|
+
return new Promise((resolve6) => {
|
|
41244
|
+
this.waiters.push(resolve6);
|
|
39797
41245
|
});
|
|
39798
41246
|
}
|
|
39799
41247
|
close() {
|
|
@@ -39808,6 +41256,100 @@ var init_reset_passphrase = __esm({
|
|
|
39808
41256
|
}
|
|
39809
41257
|
});
|
|
39810
41258
|
|
|
41259
|
+
// src/cli/intelligence.ts
|
|
41260
|
+
var intelligence_exports = {};
|
|
41261
|
+
__export(intelligence_exports, {
|
|
41262
|
+
runIntelligenceCommand: () => runIntelligenceCommand
|
|
41263
|
+
});
|
|
41264
|
+
async function runIntelligenceCommand(opts) {
|
|
41265
|
+
const subcommand = opts.argv[0];
|
|
41266
|
+
if (subcommand === "diagnose" || subcommand === void 0) {
|
|
41267
|
+
return runDiagnose();
|
|
41268
|
+
}
|
|
41269
|
+
if (subcommand === "--help" || subcommand === "-h") {
|
|
41270
|
+
printHelp2();
|
|
41271
|
+
return 0;
|
|
41272
|
+
}
|
|
41273
|
+
console.error(`Unknown intelligence subcommand: ${subcommand}`);
|
|
41274
|
+
printHelp2();
|
|
41275
|
+
return 2;
|
|
41276
|
+
}
|
|
41277
|
+
function printHelp2() {
|
|
41278
|
+
console.error(`
|
|
41279
|
+
Usage: sanctuary intelligence <subcommand>
|
|
41280
|
+
|
|
41281
|
+
Subcommands:
|
|
41282
|
+
diagnose Print intelligence substrate config and last error.
|
|
41283
|
+
--help Show this help.
|
|
41284
|
+
`);
|
|
41285
|
+
}
|
|
41286
|
+
async function runDiagnose() {
|
|
41287
|
+
const storagePath = resolve(
|
|
41288
|
+
process.env.SANCTUARY_STORAGE_PATH ?? process.env.SANCTUARY_FORTRESS_PATH ?? `${process.env.HOME}/.sanctuary`
|
|
41289
|
+
);
|
|
41290
|
+
console.error(`Intelligence substrate diagnostics`);
|
|
41291
|
+
console.error(`Fortress: ${storagePath}`);
|
|
41292
|
+
console.error("");
|
|
41293
|
+
const intelligenceDir = resolve(storagePath, "state", "_intelligence");
|
|
41294
|
+
if (!existsSync(intelligenceDir)) {
|
|
41295
|
+
console.error(
|
|
41296
|
+
`No intelligence config directory found at ${intelligenceDir}.`
|
|
41297
|
+
);
|
|
41298
|
+
console.error(
|
|
41299
|
+
"Intelligence substrate may not have been initialized yet."
|
|
41300
|
+
);
|
|
41301
|
+
console.error(
|
|
41302
|
+
"Ensure at least one substrate API key is set in your environment:"
|
|
41303
|
+
);
|
|
41304
|
+
console.error(
|
|
41305
|
+
" ANTHROPIC_API_KEY, OPENAI_API_KEY, VENICE_API_KEY, or OLLAMA_HOST"
|
|
41306
|
+
);
|
|
41307
|
+
return 1;
|
|
41308
|
+
}
|
|
41309
|
+
try {
|
|
41310
|
+
const entries = readdirSync(intelligenceDir);
|
|
41311
|
+
console.error(`Intelligence config entries: ${entries.length}`);
|
|
41312
|
+
for (const entry of entries) {
|
|
41313
|
+
console.error(` ${entry}`);
|
|
41314
|
+
}
|
|
41315
|
+
} catch {
|
|
41316
|
+
console.error(`Could not read intelligence config directory.`);
|
|
41317
|
+
}
|
|
41318
|
+
const auditDir = resolve(storagePath, "state", "_audit");
|
|
41319
|
+
if (existsSync(auditDir)) {
|
|
41320
|
+
try {
|
|
41321
|
+
const auditFiles = readdirSync(auditDir).sort().reverse();
|
|
41322
|
+
const recentFiles = auditFiles.slice(0, 20);
|
|
41323
|
+
console.error("");
|
|
41324
|
+
console.error(`Recent audit entries (${recentFiles.length} of ${auditFiles.length}):`);
|
|
41325
|
+
for (const file of recentFiles) {
|
|
41326
|
+
console.error(` ${file}`);
|
|
41327
|
+
}
|
|
41328
|
+
} catch {
|
|
41329
|
+
console.error(`Could not read audit directory.`);
|
|
41330
|
+
}
|
|
41331
|
+
}
|
|
41332
|
+
console.error("");
|
|
41333
|
+
console.error("Substrate environment check:");
|
|
41334
|
+
const keys = [
|
|
41335
|
+
"ANTHROPIC_API_KEY",
|
|
41336
|
+
"OPENAI_API_KEY",
|
|
41337
|
+
"VENICE_API_KEY",
|
|
41338
|
+
"OLLAMA_HOST"
|
|
41339
|
+
];
|
|
41340
|
+
for (const key of keys) {
|
|
41341
|
+
const val = process.env[key];
|
|
41342
|
+
console.error(
|
|
41343
|
+
` ${key}: ${val ? `set (${val.slice(0, 4)}...)` : "not set"}`
|
|
41344
|
+
);
|
|
41345
|
+
}
|
|
41346
|
+
return 0;
|
|
41347
|
+
}
|
|
41348
|
+
var init_intelligence = __esm({
|
|
41349
|
+
"src/cli/intelligence.ts"() {
|
|
41350
|
+
}
|
|
41351
|
+
});
|
|
41352
|
+
|
|
39811
41353
|
// src/mcp/broker-server.ts
|
|
39812
41354
|
var broker_server_exports = {};
|
|
39813
41355
|
__export(broker_server_exports, {
|
|
@@ -40098,11 +41640,11 @@ async function startMultiDashboardServer(options = {}) {
|
|
|
40098
41640
|
}
|
|
40099
41641
|
}
|
|
40100
41642
|
});
|
|
40101
|
-
await new Promise((
|
|
41643
|
+
await new Promise((resolve6, reject) => {
|
|
40102
41644
|
server.once("error", reject);
|
|
40103
41645
|
server.listen(port, host, () => {
|
|
40104
41646
|
server.off("error", reject);
|
|
40105
|
-
|
|
41647
|
+
resolve6();
|
|
40106
41648
|
});
|
|
40107
41649
|
});
|
|
40108
41650
|
const addr = server.address();
|
|
@@ -40111,8 +41653,8 @@ async function startMultiDashboardServer(options = {}) {
|
|
|
40111
41653
|
url: `http://${host}:${actualPort}`,
|
|
40112
41654
|
port: actualPort,
|
|
40113
41655
|
host,
|
|
40114
|
-
stop: () => new Promise((
|
|
40115
|
-
server.close((err) => err ? reject(err) :
|
|
41656
|
+
stop: () => new Promise((resolve6, reject) => {
|
|
41657
|
+
server.close((err) => err ? reject(err) : resolve6());
|
|
40116
41658
|
})
|
|
40117
41659
|
};
|
|
40118
41660
|
}
|
|
@@ -40512,7 +42054,7 @@ function formatUpdateMessage(current, latest) {
|
|
|
40512
42054
|
return `[Sanctuary] Update available: ${current} \u2192 ${latest}. Run: npx @sanctuary-framework/mcp-server@latest`;
|
|
40513
42055
|
}
|
|
40514
42056
|
function fetchLatestVersion(currentVersion) {
|
|
40515
|
-
return new Promise((
|
|
42057
|
+
return new Promise((resolve6) => {
|
|
40516
42058
|
const req = get(
|
|
40517
42059
|
REGISTRY_URL,
|
|
40518
42060
|
{
|
|
@@ -40522,7 +42064,7 @@ function fetchLatestVersion(currentVersion) {
|
|
|
40522
42064
|
(res) => {
|
|
40523
42065
|
if (res.statusCode !== 200) {
|
|
40524
42066
|
res.resume();
|
|
40525
|
-
|
|
42067
|
+
resolve6(null);
|
|
40526
42068
|
return;
|
|
40527
42069
|
}
|
|
40528
42070
|
let data = "";
|
|
@@ -40531,7 +42073,7 @@ function fetchLatestVersion(currentVersion) {
|
|
|
40531
42073
|
data += chunk;
|
|
40532
42074
|
if (data.length > 32768) {
|
|
40533
42075
|
res.destroy();
|
|
40534
|
-
|
|
42076
|
+
resolve6(null);
|
|
40535
42077
|
}
|
|
40536
42078
|
});
|
|
40537
42079
|
res.on("end", () => {
|
|
@@ -40539,20 +42081,20 @@ function fetchLatestVersion(currentVersion) {
|
|
|
40539
42081
|
const json = JSON.parse(data);
|
|
40540
42082
|
const latest = json.version;
|
|
40541
42083
|
if (typeof latest === "string" && isNewerVersion(currentVersion, latest)) {
|
|
40542
|
-
|
|
42084
|
+
resolve6(latest);
|
|
40543
42085
|
} else {
|
|
40544
|
-
|
|
42086
|
+
resolve6(null);
|
|
40545
42087
|
}
|
|
40546
42088
|
} catch {
|
|
40547
|
-
|
|
42089
|
+
resolve6(null);
|
|
40548
42090
|
}
|
|
40549
42091
|
});
|
|
40550
42092
|
}
|
|
40551
42093
|
);
|
|
40552
|
-
req.on("error", () =>
|
|
42094
|
+
req.on("error", () => resolve6(null));
|
|
40553
42095
|
req.on("timeout", () => {
|
|
40554
42096
|
req.destroy();
|
|
40555
|
-
|
|
42097
|
+
resolve6(null);
|
|
40556
42098
|
});
|
|
40557
42099
|
});
|
|
40558
42100
|
}
|
|
@@ -40645,6 +42187,11 @@ async function main() {
|
|
|
40645
42187
|
const code = await runResetPassphraseCommand2({ argv: args.slice(1) });
|
|
40646
42188
|
process.exit(code);
|
|
40647
42189
|
}
|
|
42190
|
+
if (args[0] === "intelligence") {
|
|
42191
|
+
const { runIntelligenceCommand: runIntelligenceCommand2 } = await Promise.resolve().then(() => (init_intelligence(), intelligence_exports));
|
|
42192
|
+
const code = await runIntelligenceCommand2({ argv: args.slice(1) });
|
|
42193
|
+
process.exit(code);
|
|
42194
|
+
}
|
|
40648
42195
|
if (args[0] === "broker-server") {
|
|
40649
42196
|
const { openBroker: openBroker2 } = await Promise.resolve().then(() => (init_open(), open_exports));
|
|
40650
42197
|
const { createBrokerMcpServer: createBrokerMcpServer2 } = await Promise.resolve().then(() => (init_broker_server(), broker_server_exports));
|
|
@@ -40668,7 +42215,7 @@ async function main() {
|
|
|
40668
42215
|
);
|
|
40669
42216
|
passphrase = args[++i];
|
|
40670
42217
|
} else if (args[i] === "--help" || args[i] === "-h") {
|
|
40671
|
-
|
|
42218
|
+
printHelp3();
|
|
40672
42219
|
process.exit(0);
|
|
40673
42220
|
} else if (args[i] === "--version" || args[i] === "-v") {
|
|
40674
42221
|
console.log(`@sanctuary-framework/mcp-server ${PKG_VERSION4}`);
|
|
@@ -40813,7 +42360,7 @@ async function runExportPassphrase(args) {
|
|
|
40813
42360
|
}
|
|
40814
42361
|
process.stdout.write(stored.value + "\n");
|
|
40815
42362
|
}
|
|
40816
|
-
function
|
|
42363
|
+
function printHelp3() {
|
|
40817
42364
|
console.log(`
|
|
40818
42365
|
@sanctuary-framework/mcp-server v${PKG_VERSION4}
|
|
40819
42366
|
|