@bradheitmann/odin-sentinel 0.4.5 → 0.4.7
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/AGENTS.md +64 -0
- package/CLAUDE.md +43 -0
- package/README.md +113 -302
- package/dist/src/mcp/server.js +43 -12
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/protocol/schemas.d.ts +2529 -4
- package/dist/src/protocol/schemas.js +214 -18
- package/dist/src/protocol/schemas.js.map +1 -1
- package/dist/src/protocol/service.d.ts +96 -2
- package/dist/src/protocol/service.js +516 -4
- package/dist/src/protocol/service.js.map +1 -1
- package/dist/src/protocol/surface-layout.d.ts +40 -1
- package/dist/src/protocol/surface-layout.js +98 -1
- package/dist/src/protocol/surface-layout.js.map +1 -1
- package/dist/src/protocol/validators.d.ts +3 -0
- package/dist/src/protocol/validators.js +28 -0
- package/dist/src/protocol/validators.js.map +1 -1
- package/dist/src/protocol/version.d.ts +3 -0
- package/dist/src/protocol/version.js +3 -0
- package/dist/src/protocol/version.js.map +1 -1
- package/dist/src/telemetry/config.d.ts +8 -0
- package/dist/src/telemetry/config.js +24 -0
- package/dist/src/telemetry/config.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +5 -5
- package/dist/src/telemetry/index.js +3 -3
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/redactor.js +25 -7
- package/dist/src/telemetry/redactor.js.map +1 -1
- package/dist/src/telemetry/report.d.ts +108 -0
- package/dist/src/telemetry/report.js +83 -3
- package/dist/src/telemetry/report.js.map +1 -1
- package/dist/src/telemetry/submit.d.ts +2 -0
- package/dist/src/telemetry/submit.js +79 -6
- package/dist/src/telemetry/submit.js.map +1 -1
- package/docs/guides/quick-start.md +112 -44
- package/docs/guides/quickstart-prompts.md +46 -113
- package/docs/guides/recommended-starter-team.md +45 -27
- package/docs/reference/client-compatibility.md +20 -43
- package/docs/reference/cost-and-privacy.md +26 -23
- package/docs/reference/distribution.md +40 -55
- package/docs/reference/public-surface-audit.md +35 -114
- package/package.json +22 -6
- package/protocol/SCP.md +8 -1
- package/protocol/bootstrap-skill.md +16 -11
- package/protocol/closeout.yaml +7 -1
- package/protocol/delegation.yaml +1 -1
- package/protocol/model-profiles.yaml +55 -1
- package/protocol/receipts/boot-receipt.yaml +42 -0
- package/protocol/receipts/team-manifest.yaml +41 -0
- package/protocol/roles.yaml +69 -1
- package/protocol/topology.yaml +78 -36
- package/scripts/audit/public-surface.mjs +47 -19
- package/scripts/audit/verify-pack.mjs +293 -27
- package/templates/dev-slice-template.md +56 -0
- package/templates/pm-role-template.md +61 -0
- package/templates/qa-slice-template.md +46 -0
- package/templates/team-manifest-template.yaml +163 -0
|
@@ -12,6 +12,109 @@ export type ModelSignal = {
|
|
|
12
12
|
model: string;
|
|
13
13
|
violations: number;
|
|
14
14
|
};
|
|
15
|
+
export type DiagnosticStatus = "verified" | "missing" | "blocked" | "unknown" | "not_applicable";
|
|
16
|
+
export type DiagnosticCheck = {
|
|
17
|
+
status: DiagnosticStatus;
|
|
18
|
+
detail?: string;
|
|
19
|
+
evidence?: string;
|
|
20
|
+
};
|
|
21
|
+
export type PromptType = "full_protocol" | "short_boot_prompt" | "handoff" | "unknown";
|
|
22
|
+
export type ReadinessStatus = "ready" | "degraded" | "blocked" | "unknown";
|
|
23
|
+
export type ReceiptStatus = "emitted" | "missing" | "invalid" | "unknown" | "not_applicable";
|
|
24
|
+
export type WatchStatus = "active" | "passive" | "idle" | "blocked" | "unknown" | "not_applicable";
|
|
25
|
+
export type BlockerClassification = "none" | "stale_mcp_version" | "missing_skill" | "short_prompt" | "passive_odin" | "split_workspace" | "tab_heavy_layout" | "auth_blocker" | "permission_blocker" | "local_inference_stall" | "role_refusal" | "missing_protocol_text" | "operator_authority_confusion" | "receipt_missing" | "runbook_improvisation" | "readiness_gate_skipped";
|
|
26
|
+
export type RoleSlotLocator = {
|
|
27
|
+
workspace?: string;
|
|
28
|
+
pane?: string;
|
|
29
|
+
surface?: string;
|
|
30
|
+
};
|
|
31
|
+
export type RoleSlotDiagnostic = {
|
|
32
|
+
role: string;
|
|
33
|
+
team: string;
|
|
34
|
+
harness: string;
|
|
35
|
+
model: string;
|
|
36
|
+
locator: RoleSlotLocator;
|
|
37
|
+
mcpAccess: DiagnosticCheck;
|
|
38
|
+
mcpVersion?: string;
|
|
39
|
+
minimumCompatibleMcpVersion?: string;
|
|
40
|
+
skillAccess: DiagnosticCheck;
|
|
41
|
+
fullProtocolTextReceived: DiagnosticCheck;
|
|
42
|
+
promptType: PromptType;
|
|
43
|
+
operatorAuthorityRecognized: DiagnosticCheck;
|
|
44
|
+
readinessStatus: ReadinessStatus;
|
|
45
|
+
bootReceiptStatus: ReceiptStatus;
|
|
46
|
+
activeWatchStatus: WatchStatus;
|
|
47
|
+
blockerClassification: BlockerClassification[];
|
|
48
|
+
childContextText?: string;
|
|
49
|
+
notes?: string[];
|
|
50
|
+
};
|
|
51
|
+
export type HarnessFailureDiagnostics = {
|
|
52
|
+
openHandsAuthApiCredentials: DiagnosticCheck;
|
|
53
|
+
kiloCodeLogin: DiagnosticCheck;
|
|
54
|
+
gooseLocalInferenceStall: DiagnosticCheck;
|
|
55
|
+
crushPermissionPrompt: DiagnosticCheck;
|
|
56
|
+
piRoleRefusal: DiagnosticCheck;
|
|
57
|
+
staleMcpVersion: DiagnosticCheck;
|
|
58
|
+
};
|
|
59
|
+
export type LayoutDiagnostics = {
|
|
60
|
+
sameWorkspace: boolean | "unknown";
|
|
61
|
+
splitWorkspace: boolean;
|
|
62
|
+
spatialPodLayout: boolean | "unknown";
|
|
63
|
+
tabHeavyLayout: boolean;
|
|
64
|
+
cmuxAvailable: boolean | "unknown";
|
|
65
|
+
degradedLayoutWarnings: string[];
|
|
66
|
+
};
|
|
67
|
+
export type LaunchRunbookDiagnostics = {
|
|
68
|
+
deterministicRunbookFollowed: boolean | "unknown";
|
|
69
|
+
firstImprovisationPoint?: string;
|
|
70
|
+
missingPrompts: string[];
|
|
71
|
+
missingTools: string[];
|
|
72
|
+
missingSchemas: string[];
|
|
73
|
+
};
|
|
74
|
+
export type ReadinessGateDiagnostics = {
|
|
75
|
+
childMcpVerified: DiagnosticCheck;
|
|
76
|
+
skillVerified: DiagnosticCheck;
|
|
77
|
+
authVerified: DiagnosticCheck;
|
|
78
|
+
firstRunPromptVerified: DiagnosticCheck;
|
|
79
|
+
modelSmokeVerified: DiagnosticCheck;
|
|
80
|
+
roleCompatibilityVerified: DiagnosticCheck;
|
|
81
|
+
};
|
|
82
|
+
export type ReleaseVersionDiagnostics = {
|
|
83
|
+
currentServerVersion: string;
|
|
84
|
+
minimumCompatibleVersion: string;
|
|
85
|
+
childReportedMcpVersions: Array<{
|
|
86
|
+
role: string;
|
|
87
|
+
version: string;
|
|
88
|
+
}>;
|
|
89
|
+
driftWarnings: string[];
|
|
90
|
+
};
|
|
91
|
+
export type ArtifactDiagnostics = {
|
|
92
|
+
planningArtifactsLocalOnly: boolean | "unknown";
|
|
93
|
+
publicTemplatesSanitized: boolean | "unknown";
|
|
94
|
+
privateArtifactWarnings: string[];
|
|
95
|
+
};
|
|
96
|
+
export type ChildContextWindow = {
|
|
97
|
+
role: string;
|
|
98
|
+
text: string;
|
|
99
|
+
};
|
|
100
|
+
export type AxDiagnostics = {
|
|
101
|
+
roleSlots: RoleSlotDiagnostic[];
|
|
102
|
+
childAgentQuestions: string[];
|
|
103
|
+
odinQuestions: string[];
|
|
104
|
+
harnessFailures: HarnessFailureDiagnostics;
|
|
105
|
+
layout: LayoutDiagnostics;
|
|
106
|
+
launchRunbook: LaunchRunbookDiagnostics;
|
|
107
|
+
readinessGates: ReadinessGateDiagnostics;
|
|
108
|
+
versions: ReleaseVersionDiagnostics;
|
|
109
|
+
artifacts: ArtifactDiagnostics;
|
|
110
|
+
pastedChildContextWindows?: ChildContextWindow[];
|
|
111
|
+
};
|
|
112
|
+
export type AxSummary = {
|
|
113
|
+
roleSlotCount: number;
|
|
114
|
+
blockerClassifications: BlockerClassification[];
|
|
115
|
+
degradedLayout: boolean;
|
|
116
|
+
driftWarningCount: number;
|
|
117
|
+
};
|
|
15
118
|
export type SessionReportInput = {
|
|
16
119
|
teamCount: number;
|
|
17
120
|
violations: ViolationEntry[];
|
|
@@ -20,11 +123,16 @@ export type SessionReportInput = {
|
|
|
20
123
|
peakContextPct: number;
|
|
21
124
|
closeoutClean: boolean;
|
|
22
125
|
modelSignals: ModelSignal[];
|
|
126
|
+
ax?: Partial<AxDiagnostics>;
|
|
23
127
|
};
|
|
24
128
|
export type SessionReport = SessionReportInput & {
|
|
25
129
|
version: string;
|
|
26
130
|
compiledAt: string;
|
|
27
131
|
violationCount: number;
|
|
28
132
|
haltCount: number;
|
|
133
|
+
axSummary?: AxSummary;
|
|
29
134
|
};
|
|
135
|
+
export declare const CHILD_AGENT_DIAGNOSTIC_QUESTIONS: readonly ["What role slot, team, harness, model, workspace, pane, and surface were you assigned?", "Did you have ODIN Sentinel MCP access, and what MCP version did you see?", "Did you have native skill access for the requested role, or was a required skill missing?", "Did you receive the full protocol text, a handoff, or only a short boot prompt?", "Did you recognize A/EXEC-PM operator authority and your own implementation/QA limits?", "Did you emit the required boot receipt or explain why receipt emission was blocked?", "What blocker or confusion stopped progress, using the closest standard classification?", "What exact prompt, tool, schema, layout, auth, or runbook change would make launch smoother next time?"];
|
|
136
|
+
export declare const ODIN_DIAGNOSTIC_QUESTIONS: readonly ["Were you explicitly instructed to poll actively, or did you interpret the role as passive observation?", "Which workspaces, panes, surfaces, agents, files, queues, or receipts were you watching?", "Did plan mode, read-only mode, permission prompts, or role boundaries conflict with active intervention?", "What exact polling prompt or cadence would have made the watch loop unambiguous?", "Which terminal states did you detect: idle, blocked, still thinking, input-bar-only, delivered-no-ack, or done?", "What intervention would you have issued if active polling authority had been clear?"];
|
|
137
|
+
export declare function getAxDiagnosticQuestions(scope?: "child" | "odin" | "all"): string[];
|
|
30
138
|
export declare function compileSessionReport(input: SessionReportInput, version: string): SessionReport;
|
|
@@ -1,10 +1,90 @@
|
|
|
1
|
+
import { redactPayload } from "./redactor.js";
|
|
2
|
+
export const CHILD_AGENT_DIAGNOSTIC_QUESTIONS = [
|
|
3
|
+
"What role slot, team, harness, model, workspace, pane, and surface were you assigned?",
|
|
4
|
+
"Did you have ODIN Sentinel MCP access, and what MCP version did you see?",
|
|
5
|
+
"Did you have native skill access for the requested role, or was a required skill missing?",
|
|
6
|
+
"Did you receive the full protocol text, a handoff, or only a short boot prompt?",
|
|
7
|
+
"Did you recognize A/EXEC-PM operator authority and your own implementation/QA limits?",
|
|
8
|
+
"Did you emit the required boot receipt or explain why receipt emission was blocked?",
|
|
9
|
+
"What blocker or confusion stopped progress, using the closest standard classification?",
|
|
10
|
+
"What exact prompt, tool, schema, layout, auth, or runbook change would make launch smoother next time?"
|
|
11
|
+
];
|
|
12
|
+
export const ODIN_DIAGNOSTIC_QUESTIONS = [
|
|
13
|
+
"Were you explicitly instructed to poll actively, or did you interpret the role as passive observation?",
|
|
14
|
+
"Which workspaces, panes, surfaces, agents, files, queues, or receipts were you watching?",
|
|
15
|
+
"Did plan mode, read-only mode, permission prompts, or role boundaries conflict with active intervention?",
|
|
16
|
+
"What exact polling prompt or cadence would have made the watch loop unambiguous?",
|
|
17
|
+
"Which terminal states did you detect: idle, blocked, still thinking, input-bar-only, delivered-no-ack, or done?",
|
|
18
|
+
"What intervention would you have issued if active polling authority had been clear?"
|
|
19
|
+
];
|
|
20
|
+
const HARNESS_BLOCKER_MAP = [
|
|
21
|
+
["openHandsAuthApiCredentials", "auth_blocker"],
|
|
22
|
+
["kiloCodeLogin", "auth_blocker"],
|
|
23
|
+
["gooseLocalInferenceStall", "local_inference_stall"],
|
|
24
|
+
["crushPermissionPrompt", "permission_blocker"],
|
|
25
|
+
["piRoleRefusal", "role_refusal"],
|
|
26
|
+
["staleMcpVersion", "stale_mcp_version"]
|
|
27
|
+
];
|
|
28
|
+
function uniq(values) {
|
|
29
|
+
return [...new Set(values)];
|
|
30
|
+
}
|
|
31
|
+
function isBlocking(status) {
|
|
32
|
+
return status === "missing" || status === "blocked";
|
|
33
|
+
}
|
|
34
|
+
function summarizeAx(ax) {
|
|
35
|
+
if (!ax)
|
|
36
|
+
return undefined;
|
|
37
|
+
const blockers = [];
|
|
38
|
+
for (const roleSlot of ax.roleSlots ?? []) {
|
|
39
|
+
blockers.push(...roleSlot.blockerClassification.filter((entry) => entry !== "none"));
|
|
40
|
+
if (roleSlot.promptType === "short_boot_prompt")
|
|
41
|
+
blockers.push("short_prompt");
|
|
42
|
+
if (roleSlot.fullProtocolTextReceived.status === "missing")
|
|
43
|
+
blockers.push("missing_protocol_text");
|
|
44
|
+
if (roleSlot.skillAccess.status === "missing")
|
|
45
|
+
blockers.push("missing_skill");
|
|
46
|
+
if (roleSlot.activeWatchStatus === "passive" || roleSlot.activeWatchStatus === "idle")
|
|
47
|
+
blockers.push("passive_odin");
|
|
48
|
+
if (roleSlot.bootReceiptStatus === "missing")
|
|
49
|
+
blockers.push("receipt_missing");
|
|
50
|
+
}
|
|
51
|
+
if (ax.harnessFailures) {
|
|
52
|
+
for (const [key, classification] of HARNESS_BLOCKER_MAP) {
|
|
53
|
+
if (isBlocking(ax.harnessFailures[key].status))
|
|
54
|
+
blockers.push(classification);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (ax.layout?.splitWorkspace)
|
|
58
|
+
blockers.push("split_workspace");
|
|
59
|
+
if (ax.layout?.tabHeavyLayout)
|
|
60
|
+
blockers.push("tab_heavy_layout");
|
|
61
|
+
if (ax.launchRunbook?.deterministicRunbookFollowed === false)
|
|
62
|
+
blockers.push("runbook_improvisation");
|
|
63
|
+
if ((ax.versions?.driftWarnings.length ?? 0) > 0)
|
|
64
|
+
blockers.push("stale_mcp_version");
|
|
65
|
+
return {
|
|
66
|
+
roleSlotCount: ax.roleSlots?.length ?? 0,
|
|
67
|
+
blockerClassifications: uniq(blockers),
|
|
68
|
+
degradedLayout: Boolean(ax.layout?.splitWorkspace || ax.layout?.tabHeavyLayout || (ax.layout?.degradedLayoutWarnings.length ?? 0) > 0),
|
|
69
|
+
driftWarningCount: ax.versions?.driftWarnings.length ?? 0
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export function getAxDiagnosticQuestions(scope = "all") {
|
|
73
|
+
if (scope === "child")
|
|
74
|
+
return [...CHILD_AGENT_DIAGNOSTIC_QUESTIONS];
|
|
75
|
+
if (scope === "odin")
|
|
76
|
+
return [...ODIN_DIAGNOSTIC_QUESTIONS];
|
|
77
|
+
return [...CHILD_AGENT_DIAGNOSTIC_QUESTIONS, ...ODIN_DIAGNOSTIC_QUESTIONS];
|
|
78
|
+
}
|
|
1
79
|
export function compileSessionReport(input, version) {
|
|
80
|
+
const redacted = redactPayload(input);
|
|
2
81
|
return {
|
|
3
|
-
...
|
|
82
|
+
...redacted,
|
|
4
83
|
version,
|
|
5
84
|
compiledAt: new Date().toISOString(),
|
|
6
|
-
violationCount:
|
|
7
|
-
haltCount:
|
|
85
|
+
violationCount: redacted.violations.length,
|
|
86
|
+
haltCount: redacted.halts.length,
|
|
87
|
+
axSummary: summarizeAx(redacted.ax)
|
|
8
88
|
};
|
|
9
89
|
}
|
|
10
90
|
//# sourceMappingURL=report.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/telemetry/report.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/telemetry/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,5 +10,7 @@ export type SubmitOptions = {
|
|
|
10
10
|
config?: TelemetryConfig;
|
|
11
11
|
fetchImpl?: typeof fetch;
|
|
12
12
|
timeoutMs?: number;
|
|
13
|
+
userConsentConfirmed?: boolean;
|
|
13
14
|
};
|
|
15
|
+
export declare function toTelemetryPayload(report: Record<string, unknown>): Record<string, unknown>;
|
|
14
16
|
export declare function submitSessionReport(report: Record<string, unknown>, options?: SubmitOptions): Promise<SubmitResult>;
|
|
@@ -1,7 +1,80 @@
|
|
|
1
|
-
import { readTelemetryConfig } from "./config.js";
|
|
2
|
-
import { redactPayload } from "./redactor.js";
|
|
1
|
+
import { readTelemetryConfig, redactTelemetryEndpoint } from "./config.js";
|
|
2
|
+
import { redactPayload, redactString } from "./redactor.js";
|
|
3
3
|
const DEFAULT_TIMEOUT_MS = 8000;
|
|
4
|
+
function isRecord(value) {
|
|
5
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
6
|
+
}
|
|
7
|
+
function safeString(value, maxLen = 96) {
|
|
8
|
+
if (typeof value !== "string")
|
|
9
|
+
return "";
|
|
10
|
+
const redacted = redactString(value).trim();
|
|
11
|
+
return redacted.length > maxLen ? redacted.slice(0, maxLen) : redacted;
|
|
12
|
+
}
|
|
13
|
+
function safeInt(value, fallback, min, max) {
|
|
14
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
15
|
+
return fallback;
|
|
16
|
+
const integer = Math.trunc(value);
|
|
17
|
+
if (integer < min)
|
|
18
|
+
return min;
|
|
19
|
+
if (integer > max)
|
|
20
|
+
return max;
|
|
21
|
+
return integer;
|
|
22
|
+
}
|
|
23
|
+
function safeStringArray(value) {
|
|
24
|
+
if (!Array.isArray(value))
|
|
25
|
+
return [];
|
|
26
|
+
return [...new Set(value.map((entry) => safeString(entry)).filter(Boolean))];
|
|
27
|
+
}
|
|
28
|
+
function violationClasses(report) {
|
|
29
|
+
const direct = safeStringArray(report.violationClasses);
|
|
30
|
+
if (direct.length > 0)
|
|
31
|
+
return direct;
|
|
32
|
+
if (!Array.isArray(report.violations))
|
|
33
|
+
return [];
|
|
34
|
+
return [...new Set(report.violations.flatMap((entry) => isRecord(entry) ? [safeString(entry.class)] : []).filter(Boolean))];
|
|
35
|
+
}
|
|
36
|
+
function blockerClassifications(report) {
|
|
37
|
+
const axSummary = isRecord(report.axSummary) ? report.axSummary : {};
|
|
38
|
+
return safeStringArray(axSummary.blockerClassifications);
|
|
39
|
+
}
|
|
40
|
+
function modelSignals(report) {
|
|
41
|
+
if (!Array.isArray(report.modelSignals))
|
|
42
|
+
return [];
|
|
43
|
+
return report.modelSignals.flatMap((entry) => {
|
|
44
|
+
if (!isRecord(entry))
|
|
45
|
+
return [];
|
|
46
|
+
return [{
|
|
47
|
+
role: safeString(entry.role),
|
|
48
|
+
model: safeString(entry.model),
|
|
49
|
+
violations: safeInt(entry.violations, 0, 0, 10_000)
|
|
50
|
+
}];
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export function toTelemetryPayload(report) {
|
|
54
|
+
const axSummary = isRecord(report.axSummary) ? report.axSummary : {};
|
|
55
|
+
return {
|
|
56
|
+
version: safeString(report.version, 32),
|
|
57
|
+
teamCount: safeInt(report.teamCount, 0, 0, 26),
|
|
58
|
+
violationCount: safeInt(report.violationCount, Array.isArray(report.violations) ? report.violations.length : 0, 0, 10_000),
|
|
59
|
+
haltCount: safeInt(report.haltCount, Array.isArray(report.halts) ? report.halts.length : 0, 0, 10_000),
|
|
60
|
+
layoutDriftEvents: safeInt(report.layoutDriftEvents, 0, 0, 10_000),
|
|
61
|
+
peakContextPct: safeInt(report.peakContextPct, 0, 0, 100),
|
|
62
|
+
closeoutClean: report.closeoutClean === true,
|
|
63
|
+
modelSignals: modelSignals(report),
|
|
64
|
+
violationClasses: violationClasses(report),
|
|
65
|
+
blockerClassifications: blockerClassifications(report),
|
|
66
|
+
roleSlotCount: safeInt(axSummary.roleSlotCount, 0, 0, 10_000),
|
|
67
|
+
driftWarningCount: safeInt(axSummary.driftWarningCount, 0, 0, 10_000),
|
|
68
|
+
degradedLayout: axSummary.degradedLayout === true
|
|
69
|
+
};
|
|
70
|
+
}
|
|
4
71
|
export async function submitSessionReport(report, options = {}) {
|
|
72
|
+
if (options.userConsentConfirmed !== true) {
|
|
73
|
+
return {
|
|
74
|
+
submitted: false,
|
|
75
|
+
reason: "explicit user consent required for telemetry submission"
|
|
76
|
+
};
|
|
77
|
+
}
|
|
5
78
|
const config = options.config ?? readTelemetryConfig();
|
|
6
79
|
if (!config.enabled || !config.endpoint) {
|
|
7
80
|
return {
|
|
@@ -9,7 +82,7 @@ export async function submitSessionReport(report, options = {}) {
|
|
|
9
82
|
reason: "telemetry not configured (set ODIN_TELEMETRY_ENDPOINT to opt in)"
|
|
10
83
|
};
|
|
11
84
|
}
|
|
12
|
-
const redacted = redactPayload(report);
|
|
85
|
+
const redacted = redactPayload(toTelemetryPayload(report));
|
|
13
86
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
14
87
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
15
88
|
const controller = new AbortController();
|
|
@@ -30,7 +103,7 @@ export async function submitSessionReport(report, options = {}) {
|
|
|
30
103
|
}
|
|
31
104
|
return {
|
|
32
105
|
submitted: response.ok,
|
|
33
|
-
endpoint: config.endpoint,
|
|
106
|
+
endpoint: redactTelemetryEndpoint(config.endpoint),
|
|
34
107
|
status: response.status,
|
|
35
108
|
id: typeof parsed.id === "string" ? parsed.id : undefined,
|
|
36
109
|
reason: response.ok ? undefined : `HTTP ${response.status}`
|
|
@@ -39,8 +112,8 @@ export async function submitSessionReport(report, options = {}) {
|
|
|
39
112
|
catch (error) {
|
|
40
113
|
return {
|
|
41
114
|
submitted: false,
|
|
42
|
-
endpoint: config.endpoint,
|
|
43
|
-
reason: error instanceof Error ? error.message : "network error"
|
|
115
|
+
endpoint: redactTelemetryEndpoint(config.endpoint),
|
|
116
|
+
reason: error instanceof Error ? redactString(error.message) : "network error"
|
|
44
117
|
};
|
|
45
118
|
}
|
|
46
119
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"submit.js","sourceRoot":"","sources":["../../../src/telemetry/submit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAwB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"submit.js","sourceRoot":"","sources":["../../../src/telemetry/submit.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,74 +1,142 @@
|
|
|
1
1
|
# Quick Start
|
|
2
2
|
|
|
3
|
-
ODIN Sentinel runs as a local MCP server over stdio.
|
|
3
|
+
ODIN Sentinel runs as a local MCP server over stdio. It returns protocol
|
|
4
|
+
resources, startup packets, validation results, delegation envelopes, closeout
|
|
5
|
+
checklists, and optional telemetry redaction helpers.
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
Plain version: install the package, connect it to an agent app, run one smoke
|
|
8
|
+
test, then check that each selected agent can sign in, use its model, and follow
|
|
9
|
+
the role instructions. If any step blocks, stop there and choose the safest next
|
|
10
|
+
step. You are still in control.
|
|
7
11
|
|
|
8
|
-
##
|
|
12
|
+
## Which Path Should I Choose?
|
|
13
|
+
|
|
14
|
+
- New to this: use the global install path, then run the global smoke test.
|
|
15
|
+
- Avoid global installs: use the zero-install `npx` command everywhere.
|
|
16
|
+
- Using Claude Code plugin support: use the plugin README, then come back here
|
|
17
|
+
for readiness checks.
|
|
18
|
+
- Local model users: run the normal smoke test and a separate local inference
|
|
19
|
+
smoke test before launching agents.
|
|
20
|
+
|
|
21
|
+
## 1. Prerequisites
|
|
22
|
+
|
|
23
|
+
- Node.js `>=22.13.0`
|
|
24
|
+
- npm or pnpm
|
|
25
|
+
- at least one MCP-capable harness
|
|
26
|
+
- CMUX for governed team mode
|
|
27
|
+
- account/auth readiness for each selected harness
|
|
28
|
+
- local model endpoint readiness if you plan to use local inference
|
|
29
|
+
|
|
30
|
+
## 2. Install
|
|
9
31
|
|
|
10
32
|
```bash
|
|
11
|
-
|
|
12
|
-
pnpm run build
|
|
13
|
-
node dist/src/bin/index.js
|
|
33
|
+
npm i -g @bradheitmann/odin-sentinel
|
|
14
34
|
```
|
|
15
35
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Use the built server as a stdio command:
|
|
36
|
+
Or use zero-install from MCP configs:
|
|
19
37
|
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
"mcpServers": {
|
|
23
|
-
"odin-sentinel": {
|
|
24
|
-
"command": "node",
|
|
25
|
-
"args": ["/absolute/path/to/odin-sentinel/dist/src/bin/index.js"]
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
38
|
+
```bash
|
|
39
|
+
npx -y -p @bradheitmann/odin-sentinel odin-sentinel-mcp
|
|
29
40
|
```
|
|
30
41
|
|
|
31
|
-
|
|
42
|
+
## 3. Configure MCP
|
|
43
|
+
|
|
44
|
+
Point each selected harness at the `odin-sentinel-mcp` stdio command. Restart the
|
|
45
|
+
harness after editing config.
|
|
32
46
|
|
|
33
47
|
```json
|
|
34
48
|
{
|
|
35
49
|
"mcpServers": {
|
|
36
50
|
"odin-sentinel": {
|
|
37
|
-
"command": "
|
|
38
|
-
"args": ["
|
|
51
|
+
"command": "npx",
|
|
52
|
+
"args": ["-y", "-p", "@bradheitmann/odin-sentinel", "odin-sentinel-mcp"]
|
|
39
53
|
}
|
|
40
54
|
}
|
|
41
55
|
}
|
|
42
56
|
```
|
|
43
57
|
|
|
44
|
-
##
|
|
45
|
-
|
|
46
|
-
Ask the client to call:
|
|
58
|
+
## 4. Install Skill Or Fallback Context
|
|
47
59
|
|
|
48
|
-
|
|
49
|
-
2. `odin.get_runtime_notice`
|
|
50
|
-
3. `odin.get_startup_packet`
|
|
60
|
+
Use the best available context path for each harness:
|
|
51
61
|
|
|
52
|
-
|
|
62
|
+
1. Native skill, if the harness supports it.
|
|
63
|
+
2. Plugin/install path that bundles the skill and MCP server, if available.
|
|
64
|
+
3. Full prompt injection from `odin.get_bootstrap_skill`, if skill/plugin support
|
|
65
|
+
is unavailable.
|
|
66
|
+
4. Protocol snapshot from `odin.export_protocol_snapshot` for non-MCP or bridge
|
|
67
|
+
clients.
|
|
53
68
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
3. `odin://protocol/topology`
|
|
57
|
-
4. `odin://protocol/delegation`
|
|
69
|
+
MCP supplies tools/resources. Native skill context improves automatic invocation.
|
|
70
|
+
Prompt injection is fallback only.
|
|
58
71
|
|
|
59
|
-
|
|
60
|
-
start cleanly.
|
|
72
|
+
## 5. Smoke Test
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
[recommended-starter-team.md](recommended-starter-team.md).
|
|
74
|
+
Global install smoke test:
|
|
64
75
|
|
|
65
|
-
|
|
76
|
+
```bash
|
|
77
|
+
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}\n' \
|
|
78
|
+
| odin-sentinel-mcp
|
|
79
|
+
```
|
|
66
80
|
|
|
67
|
-
|
|
81
|
+
Zero-install smoke test:
|
|
68
82
|
|
|
69
|
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
83
|
+
```bash
|
|
84
|
+
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}\n' \
|
|
85
|
+
| npx -y -p @bradheitmann/odin-sentinel odin-sentinel-mcp
|
|
86
|
+
```
|
|
72
87
|
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
Expect `serverInfo.name = odin-sentinel` and `serverInfo.version = 0.4.7`.
|
|
89
|
+
Minimum compatible child MCP version: `0.4.5`.
|
|
90
|
+
|
|
91
|
+
## 6. Auth And Provider Readiness
|
|
92
|
+
|
|
93
|
+
Ask whether selected harnesses are already provisioned. Do not ask users to paste
|
|
94
|
+
API keys into prompts or docs. Verify provider status without printing secret
|
|
95
|
+
values. Common secret/provider paths include Doppler, 1Password CLI, environment
|
|
96
|
+
variables, direnv, mise, and dotenv-style files.
|
|
97
|
+
|
|
98
|
+
If those names are unfamiliar, use this safer question instead: "Is this agent
|
|
99
|
+
already signed in or configured outside this chat?" If the answer is no, pause
|
|
100
|
+
launch and either sign in through the tool's normal UI, ask someone to configure
|
|
101
|
+
the provider outside chat, choose another harness, or keep the role slot empty.
|
|
102
|
+
|
|
103
|
+
For local inference, endpoint reachability is not enough. The selected model must
|
|
104
|
+
return visible content within the timeout and must not return only
|
|
105
|
+
`reasoning_content`.
|
|
106
|
+
|
|
107
|
+
## 7. CMUX Governed-Team Launch
|
|
108
|
+
|
|
109
|
+
Governed team mode requires CMUX. Keep EXEC PM in the same CMUX workspace as the
|
|
110
|
+
rest of the team by default. Use spatial/pod organization, not tab-only layouts,
|
|
111
|
+
for the canonical operator surface.
|
|
112
|
+
|
|
113
|
+
Slot provisioning may happen before readiness is complete. Do not launch role
|
|
114
|
+
occupants until readiness passes or EXEC PM records a waiver/substitution.
|
|
115
|
+
|
|
116
|
+
## 8. Deterministic Bootstrap Order
|
|
117
|
+
|
|
118
|
+
1. Confirm Node.js and package version.
|
|
119
|
+
2. Configure and smoke-test ODIN MCP.
|
|
120
|
+
3. Install native skill or prepare full prompt fallback.
|
|
121
|
+
4. Open CMUX and compute the target layout.
|
|
122
|
+
5. Select harnesses and check auth/account readiness.
|
|
123
|
+
6. Smoke-test local inference if used.
|
|
124
|
+
7. Run role-compatibility smoke tests.
|
|
125
|
+
8. Launch EXEC PM in the CMUX workspace.
|
|
126
|
+
9. Stage role slots and collect boot receipts.
|
|
127
|
+
10. Activate work only after the manifest validates.
|
|
128
|
+
|
|
129
|
+
## Troubleshooting
|
|
130
|
+
|
|
131
|
+
| Symptom | Action |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| MCP unavailable | Restart the host, re-check config path, and run the stdio smoke test. |
|
|
134
|
+
| Skill missing | Install the native skill where supported or use `odin.get_bootstrap_skill` as full prompt fallback. |
|
|
135
|
+
| Auth/login required | Ask the user to provision the provider; verify status without printing secrets. |
|
|
136
|
+
| Permission prompt | Stop and route to PM/user; do not bypass silently. |
|
|
137
|
+
| Stale MCP version | Reinstall with `@latest`, restart host, and confirm `0.4.7`. |
|
|
138
|
+
| Local inference stall | Require visible content within timeout; endpoint-only success is insufficient. |
|
|
139
|
+
| Role refusal | Mark the role `NON_GOVERNED_ONE_SHOT_ONLY` unless MCP/skill/full protocol proof is established. |
|
|
140
|
+
|
|
141
|
+
`NON_GOVERNED_ONE_SHOT_ONLY` means the agent may help with one bounded task, but
|
|
142
|
+
is not suitable for a persistent governed role.
|