@aiwg/cockpit 2026.8.5 → 2026.8.8
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/README.md +6 -2
- package/bridge/contracts/activity-event-v1.schema.json +70 -0
- package/bridge/contracts/activity-operational-evidence-v1.schema.json +88 -0
- package/bridge/src/activity-contract.mjs +101 -0
- package/bridge/src/server.mjs +13 -15
- package/package.json +1 -1
- package/web/dist/assets/{index-C5N6qGZf.js → index-DhWeIKvN.js} +120 -120
- package/web/dist/index.html +1 -1
package/README.md
CHANGED
|
@@ -411,7 +411,11 @@ safe version/build fields in the report. When the executor build does not expose
|
|
|
411
411
|
that metadata, set `AIWG_COCKPIT_EXECUTOR_VERSION=<tag-or-commit>` so release
|
|
412
412
|
evidence still names the tested agentic-sandbox build. A manual run against
|
|
413
413
|
agentic-sandbox `v2026.6.15` or newer should attach its markdown/JSON result to
|
|
414
|
-
epic roctinam/aiwg#1588 before the epic is considered done-done.
|
|
414
|
+
epic roctinam/aiwg#1588 before the epic is considered done-done. The latest
|
|
415
|
+
immutable fleet/activity qualification is
|
|
416
|
+
[Agentic Sandbox v2026.8.3](../../docs/cockpit/qualifications/agentic-sandbox-v2026.8.3.md);
|
|
417
|
+
older v2026.7.x entries below are historical feature-floor evidence, not the
|
|
418
|
+
latest whole-integration claim.
|
|
415
419
|
|
|
416
420
|
The executable release-validation procedure is
|
|
417
421
|
`.aiwg/testing/cockpit-real-integration-uat-runbook.md`. Use that runbook for
|
|
@@ -575,7 +579,7 @@ agentic-sandbox executor through `AIWG_COCKPIT_EXECUTOR_URL`. The host target
|
|
|
575
579
|
(agentic-sandbox#461) have landed upstream; the Bridge seam is now the
|
|
576
580
|
AIWG-side integration point for #1589. Runtime-tier provisioning and
|
|
577
581
|
host-daemon surfacing (roctinam/aiwg#1615) and transport-trust visibility (#1618)
|
|
578
|
-
**landed
|
|
582
|
+
**landed with a compatibility floor verified against `v2026.7.4`** — transport posture and
|
|
579
583
|
host-daemon now render per instance (a host-daemon *detail-status* payload
|
|
580
584
|
remains a residual under #1615). Direct/managed PTY negotiation (#1616) and the
|
|
581
585
|
live real-sandbox gate (#1617) continue. Secure transport details map back to agentic-sandbox#409/#410/#412; local
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/jmagly/agentic-sandbox/blob/main/docs/schemas/activity-event-v1.schema.json",
|
|
4
|
+
"title": "Agentic Sandbox Activity Event v1",
|
|
5
|
+
"description": "Stable metadata-first, loss-aware activity event envelope.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["schema_version", "event_id", "event_name", "plane", "occurred_at", "observed_at", "source", "correlation", "sensitivity", "retention_class", "payload", "integrity"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"schema_version": {"const": "activity.event/v1"},
|
|
11
|
+
"event_id": {"type": "string", "format": "uuid", "description": "Source-generated UUIDv7."},
|
|
12
|
+
"event_name": {"type": "string", "pattern": "^[a-z][a-z0-9_]*(\\.[a-z][a-z0-9_]*)+$"},
|
|
13
|
+
"plane": {"enum": ["session", "action", "network", "runtime", "system", "integrity"]},
|
|
14
|
+
"occurred_at": {"type": "string", "format": "date-time"},
|
|
15
|
+
"observed_at": {"type": "string", "format": "date-time"},
|
|
16
|
+
"source": {"$ref": "#/$defs/source"},
|
|
17
|
+
"correlation": {"$ref": "#/$defs/correlation"},
|
|
18
|
+
"actor": {"$ref": "#/$defs/entity"},
|
|
19
|
+
"target": {"$ref": "#/$defs/entity"},
|
|
20
|
+
"outcome": {"$ref": "#/$defs/outcome"},
|
|
21
|
+
"sensitivity": {"enum": ["metadata", "restricted-content", "secret-prohibited"]},
|
|
22
|
+
"retention_class": {"enum": ["standard", "security", "forensic-hold", "ephemeral"]},
|
|
23
|
+
"payload": {"type": "object", "description": "Event-specific allowlisted metadata after source filtering."},
|
|
24
|
+
"integrity": {"$ref": "#/$defs/integrity"}
|
|
25
|
+
},
|
|
26
|
+
"$defs": {
|
|
27
|
+
"source": {
|
|
28
|
+
"type": "object", "additionalProperties": false,
|
|
29
|
+
"required": ["collector", "layer", "runtime", "trust"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"collector": {"type": "string", "minLength": 1},
|
|
32
|
+
"layer": {"enum": ["guest", "runtime", "host", "control-plane", "provider"]},
|
|
33
|
+
"runtime": {"enum": ["qemu-kvm", "cloud-hypervisor", "docker", "host", "unknown"]},
|
|
34
|
+
"trust": {"enum": ["observed", "attested", "self-reported", "derived"]},
|
|
35
|
+
"clock_id": {"type": "string"},
|
|
36
|
+
"clock_error_ms": {"type": "number", "minimum": 0}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"correlation": {
|
|
40
|
+
"type": "object", "additionalProperties": false,
|
|
41
|
+
"required": ["tenant_id", "host_id", "instance_id", "agent_id"],
|
|
42
|
+
"properties": {
|
|
43
|
+
"tenant_id": {"type": "string", "minLength": 1}, "host_id": {"type": "string", "minLength": 1},
|
|
44
|
+
"instance_id": {"type": "string", "minLength": 1}, "agent_id": {"type": "string", "minLength": 1},
|
|
45
|
+
"session_id": {"type": "string"}, "mission_id": {"type": "string"}, "task_id": {"type": "string"},
|
|
46
|
+
"tool_call_id": {"type": "string"}, "command_id": {"type": "string"}, "process_id": {"type": "string"},
|
|
47
|
+
"parent_event_id": {"type": "string", "format": "uuid"},
|
|
48
|
+
"trace_id": {"type": "string", "pattern": "^[0-9a-f]{32}$"},
|
|
49
|
+
"span_id": {"type": "string", "pattern": "^[0-9a-f]{16}$"}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"entity": {
|
|
53
|
+
"type": "object", "additionalProperties": false, "required": ["type", "id"],
|
|
54
|
+
"properties": {"type": {"type": "string"}, "id": {"type": "string"}, "uid": {"type": "integer", "minimum": 0}, "gid": {"type": "integer", "minimum": 0}, "pid": {"type": "integer", "minimum": 0}}
|
|
55
|
+
},
|
|
56
|
+
"outcome": {
|
|
57
|
+
"type": "object", "additionalProperties": false, "required": ["status"],
|
|
58
|
+
"properties": {"status": {"enum": ["started", "success", "failure", "allowed", "denied", "degraded", "unknown"]}, "exit_code": {"type": "integer"}, "reason": {"type": "string"}}
|
|
59
|
+
},
|
|
60
|
+
"integrity": {
|
|
61
|
+
"type": "object", "additionalProperties": false, "required": ["collector_sequence"],
|
|
62
|
+
"properties": {
|
|
63
|
+
"collector_sequence": {"type": "integer", "minimum": 1},
|
|
64
|
+
"source_hash": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, "source_previous_hash": {"type": "string", "pattern": "^[0-9a-f]{64}$"},
|
|
65
|
+
"timeline_hash": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, "timeline_previous_hash": {"type": "string", "pattern": "^[0-9a-f]{64}$"},
|
|
66
|
+
"signature": {"type": "string"}, "key_id": {"type": "string"}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://agentic-sandbox.local/schemas/activity-operational-evidence-v1.schema.json",
|
|
4
|
+
"title": "Agentic Sandbox daily operational evidence record v1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schema_version",
|
|
9
|
+
"record_date",
|
|
10
|
+
"sealed_at",
|
|
11
|
+
"window",
|
|
12
|
+
"identity_digests",
|
|
13
|
+
"input_digest",
|
|
14
|
+
"previous_record_digest",
|
|
15
|
+
"summary",
|
|
16
|
+
"record_digest"
|
|
17
|
+
],
|
|
18
|
+
"properties": {
|
|
19
|
+
"schema_version": {
|
|
20
|
+
"const": "agentic.activity-operational-evidence/v1"
|
|
21
|
+
},
|
|
22
|
+
"record_date": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"format": "date"
|
|
25
|
+
},
|
|
26
|
+
"sealed_at": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"format": "date-time"
|
|
29
|
+
},
|
|
30
|
+
"window": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"additionalProperties": false,
|
|
33
|
+
"required": ["start", "end", "actual_span_seconds"],
|
|
34
|
+
"properties": {
|
|
35
|
+
"start": {"type": "string", "format": "date-time"},
|
|
36
|
+
"end": {"type": "string", "format": "date-time"},
|
|
37
|
+
"actual_span_seconds": {"type": "number", "minimum": 0}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"identity_digests": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"items": {"type": "string", "pattern": "^[0-9a-f]{64}$"},
|
|
43
|
+
"minItems": 1,
|
|
44
|
+
"uniqueItems": true
|
|
45
|
+
},
|
|
46
|
+
"input_digest": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
49
|
+
},
|
|
50
|
+
"previous_record_digest": {
|
|
51
|
+
"oneOf": [
|
|
52
|
+
{"type": "null"},
|
|
53
|
+
{"type": "string", "pattern": "^[0-9a-f]{64}$"}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"summary": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"required": [
|
|
59
|
+
"sample_count",
|
|
60
|
+
"evidence_class_counts",
|
|
61
|
+
"source_availability",
|
|
62
|
+
"latency_ms",
|
|
63
|
+
"metrics"
|
|
64
|
+
],
|
|
65
|
+
"properties": {
|
|
66
|
+
"sample_count": {"type": "integer", "minimum": 1},
|
|
67
|
+
"evidence_class_counts": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"additionalProperties": false,
|
|
70
|
+
"required": ["organic", "canary", "drill"],
|
|
71
|
+
"properties": {
|
|
72
|
+
"organic": {"type": "integer", "minimum": 0},
|
|
73
|
+
"canary": {"type": "integer", "minimum": 0},
|
|
74
|
+
"drill": {"type": "integer", "minimum": 0}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"source_availability": {"type": "object"},
|
|
78
|
+
"latency_ms": {"type": "object"},
|
|
79
|
+
"metrics": {"type": "object"}
|
|
80
|
+
},
|
|
81
|
+
"additionalProperties": false
|
|
82
|
+
},
|
|
83
|
+
"record_digest": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
|
|
3
|
+
const EVENT_SCHEMA = JSON.parse(readFileSync(
|
|
4
|
+
new URL('../contracts/activity-event-v1.schema.json', import.meta.url),
|
|
5
|
+
'utf8',
|
|
6
|
+
));
|
|
7
|
+
|
|
8
|
+
const RESTRICTED_KEY = /(?:^|_)(?:content|terminal|prompt|environment|env|credential|secret|password|authorization|bearer|token|private_key|certificate|restricted_(?:url|uri|link))(?:$|_)/i;
|
|
9
|
+
|
|
10
|
+
function resolveRef(root, ref) {
|
|
11
|
+
if (!ref.startsWith('#/')) throw new Error(`unsupported external schema reference: ${ref}`);
|
|
12
|
+
return ref.slice(2).split('/').reduce((value, segment) => value?.[segment.replaceAll('~1', '/').replaceAll('~0', '~')], root);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function typeMatches(value, type) {
|
|
16
|
+
if (type === 'null') return value === null;
|
|
17
|
+
if (type === 'array') return Array.isArray(value);
|
|
18
|
+
if (type === 'object') return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
19
|
+
if (type === 'integer') return Number.isInteger(value);
|
|
20
|
+
if (type === 'number') return typeof value === 'number' && Number.isFinite(value);
|
|
21
|
+
return typeof value === type;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function formatMatches(value, format) {
|
|
25
|
+
if (format === 'uuid') return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
26
|
+
if (format === 'date') return /^\d{4}-\d{2}-\d{2}$/.test(value) && !Number.isNaN(Date.parse(`${value}T00:00:00Z`));
|
|
27
|
+
if (format === 'date-time') return !Number.isNaN(Date.parse(value));
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function validateNode(value, schema, root, path, errors) {
|
|
32
|
+
if (schema.$ref) return validateNode(value, resolveRef(root, schema.$ref), root, path, errors);
|
|
33
|
+
if (schema.oneOf) {
|
|
34
|
+
const matches = schema.oneOf.filter((candidate) => {
|
|
35
|
+
const candidateErrors = [];
|
|
36
|
+
validateNode(value, candidate, root, path, candidateErrors);
|
|
37
|
+
return candidateErrors.length === 0;
|
|
38
|
+
});
|
|
39
|
+
if (matches.length !== 1) errors.push(`${path} must match exactly one schema`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (schema.const !== undefined && value !== schema.const) errors.push(`${path} must equal ${JSON.stringify(schema.const)}`);
|
|
43
|
+
if (schema.enum && !schema.enum.includes(value)) errors.push(`${path} is not an allowed value`);
|
|
44
|
+
if (schema.type && !typeMatches(value, schema.type)) {
|
|
45
|
+
errors.push(`${path} must be ${schema.type}`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (typeof value === 'string') {
|
|
49
|
+
if (schema.minLength !== undefined && value.length < schema.minLength) errors.push(`${path} is too short`);
|
|
50
|
+
if (schema.pattern && !new RegExp(schema.pattern, 'u').test(value)) errors.push(`${path} has invalid format`);
|
|
51
|
+
if (schema.format && !formatMatches(value, schema.format)) errors.push(`${path} has invalid ${schema.format} format`);
|
|
52
|
+
}
|
|
53
|
+
if (typeof value === 'number' && schema.minimum !== undefined && value < schema.minimum) errors.push(`${path} is below minimum`);
|
|
54
|
+
if (Array.isArray(value)) {
|
|
55
|
+
if (schema.minItems !== undefined && value.length < schema.minItems) errors.push(`${path} has too few items`);
|
|
56
|
+
if (schema.uniqueItems && new Set(value.map((item) => JSON.stringify(item))).size !== value.length) errors.push(`${path} items must be unique`);
|
|
57
|
+
if (schema.items) value.forEach((item, index) => validateNode(item, schema.items, root, `${path}/${index}`, errors));
|
|
58
|
+
}
|
|
59
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
60
|
+
for (const key of schema.required ?? []) if (!(key in value)) errors.push(`${path}/${key} is required`);
|
|
61
|
+
if (schema.additionalProperties === false) {
|
|
62
|
+
for (const key of Object.keys(value)) if (!(key in (schema.properties ?? {}))) errors.push(`${path}/${key} is not allowed`);
|
|
63
|
+
}
|
|
64
|
+
for (const [key, childSchema] of Object.entries(schema.properties ?? {})) {
|
|
65
|
+
if (key in value) validateNode(value[key], childSchema, root, `${path}/${key}`, errors);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function containsRestrictedField(value) {
|
|
71
|
+
if (Array.isArray(value)) return value.some(containsRestrictedField);
|
|
72
|
+
if (!value || typeof value !== 'object') return false;
|
|
73
|
+
return Object.entries(value).some(([key, child]) => RESTRICTED_KEY.test(key) || containsRestrictedField(child));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function validateActivityEvent(event, expectedScope = undefined) {
|
|
77
|
+
const errors = [];
|
|
78
|
+
validateNode(event, EVENT_SCHEMA, EVENT_SCHEMA, '$', errors);
|
|
79
|
+
if (event?.sensitivity !== 'metadata') errors.push('$/sensitivity must be metadata at the Cockpit boundary');
|
|
80
|
+
if (containsRestrictedField(event)) errors.push('$ contains a restricted field');
|
|
81
|
+
if (expectedScope) {
|
|
82
|
+
for (const [key, value] of Object.entries(expectedScope)) {
|
|
83
|
+
if (event?.correlation?.[key] !== value) errors.push(`$/correlation/${key} scope mismatch`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { valid: errors.length === 0, errors };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function assertActivityEvent(event, expectedScope = undefined) {
|
|
90
|
+
const result = validateActivityEvent(event, expectedScope);
|
|
91
|
+
if (!result.valid) {
|
|
92
|
+
const scopeMismatch = result.errors.some((error) => error.includes('scope mismatch'));
|
|
93
|
+
const restricted = result.errors.some((error) => error.includes('restricted') || error.includes('sensitivity'));
|
|
94
|
+
const error = new Error(result.errors.join('; '));
|
|
95
|
+
error.code = scopeMismatch ? 'activity_scope_mismatch' : restricted ? 'activity_restricted_data' : 'activity_malformed_envelope';
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
return event;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export { EVENT_SCHEMA };
|
package/bridge/src/server.mjs
CHANGED
|
@@ -16,6 +16,7 @@ import { homedir } from 'node:os';
|
|
|
16
16
|
import { fileURLToPath } from 'node:url';
|
|
17
17
|
import { dirname, join, basename, extname, resolve, sep } from 'node:path';
|
|
18
18
|
import { storeCockpitToken } from '../../shell-core/keychain.mjs';
|
|
19
|
+
import { assertActivityEvent } from './activity-contract.mjs';
|
|
19
20
|
|
|
20
21
|
const __dir = dirname(fileURLToPath(import.meta.url));
|
|
21
22
|
// Primary seam for roctinam/aiwg#1589: Cockpit talks to a real agentic-sandbox
|
|
@@ -1606,7 +1607,6 @@ const ACTIVITY_SCOPE_HEADERS = {
|
|
|
1606
1607
|
instance_id: 'x-agentic-instance-id', agent_id: 'x-agentic-agent-id',
|
|
1607
1608
|
};
|
|
1608
1609
|
const ACTIVITY_FILTERS = new Set(['event_name', 'collector', 'trust', 'plane', 'outcome', 'session_id', 'mission_id', 'task_id', 'tool_call_id', 'command_id', 'process_id', 'trace_id', 'since', 'until', 'limit']);
|
|
1609
|
-
const RESTRICTED_ACTIVITY_KEY = /(?:^|_)(?:content|terminal|prompt|environment|env|credential|secret|password|authorization|bearer|token|private_key|certificate|restricted_(?:url|uri|link))(?:$|_)/i;
|
|
1610
1610
|
|
|
1611
1611
|
export function activityRequest(input = {}) {
|
|
1612
1612
|
if (!input || typeof input !== 'object' || Array.isArray(input)) throw Object.assign(new Error('activity request must be an object'), { code: 'activity_invalid_request' });
|
|
@@ -1630,12 +1630,6 @@ export function activityRequest(input = {}) {
|
|
|
1630
1630
|
return { headers, scope, filter };
|
|
1631
1631
|
}
|
|
1632
1632
|
|
|
1633
|
-
function hasRestrictedActivityField(value) {
|
|
1634
|
-
if (Array.isArray(value)) return value.some(hasRestrictedActivityField);
|
|
1635
|
-
if (!value || typeof value !== 'object') return false;
|
|
1636
|
-
return Object.entries(value).some(([key, child]) => RESTRICTED_ACTIVITY_KEY.test(key) || hasRestrictedActivityField(child));
|
|
1637
|
-
}
|
|
1638
|
-
|
|
1639
1633
|
export function validateActivityEnvelope(body, expectedScope, { includeEvents = false, exportEnvelope = false } = {}) {
|
|
1640
1634
|
if (!body || typeof body !== 'object' || Array.isArray(body)) throw Object.assign(new Error('malformed activity envelope'), { code: 'activity_malformed_envelope' });
|
|
1641
1635
|
if (!exportEnvelope && body.schema_version !== 'activity.event/v1') throw Object.assign(new Error('unsupported activity schema'), { code: 'activity_malformed_envelope' });
|
|
@@ -1664,14 +1658,18 @@ export function validateActivityEnvelope(body, expectedScope, { includeEvents =
|
|
|
1664
1658
|
throw Object.assign(new Error('activity envelope has malformed collector coverage'), { code: 'activity_malformed_envelope' });
|
|
1665
1659
|
}
|
|
1666
1660
|
for (const event of events) {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1661
|
+
assertActivityEvent(event, expectedScope);
|
|
1662
|
+
}
|
|
1663
|
+
const manifest = body.manifest;
|
|
1664
|
+
if (exportEnvelope && (!manifest
|
|
1665
|
+
|| typeof manifest.batch_id !== 'string' || !manifest.batch_id
|
|
1666
|
+
|| manifest.tenant_id !== expectedScope.tenant_id
|
|
1667
|
+
|| typeof manifest.collector_id !== 'string' || !manifest.collector_id
|
|
1668
|
+
|| !Number.isInteger(manifest.event_count) || manifest.event_count < 0
|
|
1669
|
+
|| !/^[0-9a-f]{64}$/.test(manifest.merkle_root ?? '')
|
|
1670
|
+
|| typeof manifest.key_id !== 'string' || !manifest.key_id
|
|
1671
|
+
|| typeof manifest.signature !== 'string' || !manifest.signature
|
|
1672
|
+
|| (manifest.previous_root !== null && manifest.previous_root !== undefined && !/^[0-9a-f]{64}$/.test(manifest.previous_root)))) {
|
|
1675
1673
|
throw Object.assign(new Error('signed activity export has no valid manifest'), { code: 'activity_malformed_export' });
|
|
1676
1674
|
}
|
|
1677
1675
|
return body;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiwg/cockpit",
|
|
3
|
-
"version": "2026.8.
|
|
3
|
+
"version": "2026.8.8",
|
|
4
4
|
"description": "AIWG Cockpit — UX-first control plane over AIWG + multi-stack agentic sessions. Opt-in, separately published; NOT shipped in the base aiwg npm package (guarded by test/smoke/cockpit-base-footprint.test.js).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|