@ctliz/agent-intercom-claude 0.12.0-connect.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/.claude-plugin/plugin.json +26 -0
  2. package/.mcp.json +10 -0
  3. package/CHANGELOG.md +62 -0
  4. package/COPYRIGHT +6 -0
  5. package/LICENSE +661 -0
  6. package/LICENSE_TRANSITION.md +25 -0
  7. package/README.md +506 -0
  8. package/THIRD_PARTY_NOTICES.md +25 -0
  9. package/broker/access-credential.ts +76 -0
  10. package/broker/access-registry.ts +460 -0
  11. package/broker/audit.ts +60 -0
  12. package/broker/authorization.ts +125 -0
  13. package/broker/boss-contracts.ts +267 -0
  14. package/broker/broker.ts +2028 -0
  15. package/broker/client.ts +1048 -0
  16. package/broker/framing.ts +87 -0
  17. package/broker/ownership.ts +61 -0
  18. package/broker/paths.ts +161 -0
  19. package/broker/spawn.ts +463 -0
  20. package/broker/validation.ts +132 -0
  21. package/claude/cci.ts +512 -0
  22. package/claude/ccim.ts +31 -0
  23. package/claude/cli-runner.ts +220 -0
  24. package/claude/contact.ts +33 -0
  25. package/claude/inbox-monitor.ts +49 -0
  26. package/claude/inbox.ts +77 -0
  27. package/claude/mcp-protocol.ts +253 -0
  28. package/claude/native-bridge.ts +219 -0
  29. package/claude/native-protocol.ts +346 -0
  30. package/claude/permission-policy.ts +287 -0
  31. package/claude/runtime.ts +510 -0
  32. package/claude/server.ts +69 -0
  33. package/claude/team.ts +348 -0
  34. package/claude/transport.ts +97 -0
  35. package/claude/worker-config.ts +198 -0
  36. package/claude/worker-daemon.ts +418 -0
  37. package/commands/intercom-id.md +9 -0
  38. package/commands/intercom.md +8 -0
  39. package/config.ts +195 -0
  40. package/dist/broker.mjs +2530 -0
  41. package/dist/cci.mjs +3607 -0
  42. package/dist/ccim.mjs +3621 -0
  43. package/dist/claude-server.mjs +2730 -0
  44. package/dist/inbox-monitor.mjs +68 -0
  45. package/dist/worker-daemon.mjs +2797 -0
  46. package/durable-json.ts +25 -0
  47. package/licenses/MIT-pi-claude-link.txt +21 -0
  48. package/licenses/MIT-pi-intercom.txt +21 -0
  49. package/monitors/monitors.json +8 -0
  50. package/outbound-outbox.ts +116 -0
  51. package/package.json +93 -0
  52. package/protocol-v4/contract.ts +45 -0
  53. package/provider/protected-service.ts +162 -0
  54. package/provider/provider.mjs +34 -0
  55. package/scripts/build.mjs +71 -0
  56. package/scripts/verify-core-provenance.mjs +64 -0
  57. package/scripts/verify-packed-runtime.mjs +140 -0
  58. package/skills/claude-intercom/SKILL.md +90 -0
  59. package/types.ts +212 -0
@@ -0,0 +1,25 @@
1
+ import { randomUUID } from "crypto";
2
+ import { closeSync, fsyncSync, openSync, renameSync, writeFileSync } from "fs";
3
+ import { dirname } from "path";
4
+ import { INTERCOM_RUNTIME_FILE_MODE, restrictIntercomRuntimeFile } from "./broker/paths.ts";
5
+
6
+ export function writeDurableJson(filePath: string, value: unknown): void {
7
+ const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
8
+ writeFileSync(temporaryPath, JSON.stringify(value), { encoding: "utf-8", mode: INTERCOM_RUNTIME_FILE_MODE });
9
+ const fileDescriptor = openSync(temporaryPath, "r");
10
+ try {
11
+ fsyncSync(fileDescriptor);
12
+ } finally {
13
+ closeSync(fileDescriptor);
14
+ }
15
+ renameSync(temporaryPath, filePath);
16
+ restrictIntercomRuntimeFile(filePath);
17
+ if (process.platform !== "win32") {
18
+ const directoryDescriptor = openSync(dirname(filePath), "r");
19
+ try {
20
+ fsyncSync(directoryDescriptor);
21
+ } finally {
22
+ closeSync(directoryDescriptor);
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 alonw0
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nico Bailon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,8 @@
1
+ [
2
+ {
3
+ "name": "intercom-inbox",
4
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/dist/inbox-monitor.mjs\"",
5
+ "description": "Inbound intercom messages addressed to this session",
6
+ "when": "always"
7
+ }
8
+ ]
@@ -0,0 +1,116 @@
1
+ import { createHash } from "crypto";
2
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync } from "fs";
3
+ import { join } from "path";
4
+ import { ensureIntercomRuntimeDir, getIntercomDirPath, INTERCOM_DIR_MODE, restrictIntercomRuntimeFile } from "./broker/paths.ts";
5
+ import { writeDurableJson } from "./durable-json.ts";
6
+ import type { Message } from "./types.ts";
7
+
8
+ const OUTBOX_STATE_VERSION = 1;
9
+ const MAX_OUTBOX_MESSAGES = 256;
10
+
11
+ export interface StoredOutboundMessage {
12
+ to: string;
13
+ message: Message;
14
+ queuedAt: number;
15
+ }
16
+
17
+ interface OutboxState {
18
+ version: typeof OUTBOX_STATE_VERSION;
19
+ entries: StoredOutboundMessage[];
20
+ }
21
+
22
+ function fingerprint(entry: Pick<StoredOutboundMessage, "to" | "message">): string {
23
+ return JSON.stringify({
24
+ to: entry.to,
25
+ replyTo: entry.message.replyTo,
26
+ expectsReply: entry.message.expectsReply,
27
+ content: entry.message.content,
28
+ });
29
+ }
30
+
31
+ function isStoredOutboundMessage(value: unknown): value is StoredOutboundMessage {
32
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
33
+ const entry = value as Record<string, unknown>;
34
+ if (typeof entry.to !== "string" || typeof entry.queuedAt !== "number") return false;
35
+ if (typeof entry.message !== "object" || entry.message === null || Array.isArray(entry.message)) return false;
36
+ const message = entry.message as Record<string, unknown>;
37
+ return typeof message.id === "string"
38
+ && typeof message.timestamp === "number"
39
+ && typeof message.content === "object"
40
+ && message.content !== null
41
+ && typeof (message.content as Record<string, unknown>).text === "string";
42
+ }
43
+
44
+ function fileName(sessionId: string): string {
45
+ return `${createHash("sha256").update(sessionId).digest("hex")}.json`;
46
+ }
47
+
48
+ export class PersistentOutboundOutbox {
49
+ private readonly directory: string;
50
+ private readonly filePath: string;
51
+ private state: OutboxState;
52
+
53
+ constructor(sessionId: string, intercomDir: string = getIntercomDirPath()) {
54
+ ensureIntercomRuntimeDir(intercomDir);
55
+ this.directory = join(intercomDir, "outbox");
56
+ mkdirSync(this.directory, { recursive: true, mode: INTERCOM_DIR_MODE });
57
+ if (process.platform !== "win32") chmodSync(this.directory, INTERCOM_DIR_MODE);
58
+ this.filePath = join(this.directory, fileName(sessionId));
59
+ this.state = this.load();
60
+ }
61
+
62
+ list(): StoredOutboundMessage[] {
63
+ return this.state.entries.map((entry) => ({ ...entry, message: { ...entry.message, content: { ...entry.message.content } } }));
64
+ }
65
+
66
+ enqueue(to: string, message: Message): "added" | "existing" {
67
+ const existing = this.state.entries.find((entry) => entry.message.id === message.id);
68
+ if (existing) {
69
+ if (fingerprint(existing) !== fingerprint({ to, message })) {
70
+ throw new Error(`Message ID ${message.id} is already queued with a different payload`);
71
+ }
72
+ return "existing";
73
+ }
74
+ if (this.state.entries.length >= MAX_OUTBOX_MESSAGES) {
75
+ throw new Error(`Durable outbox is full (${MAX_OUTBOX_MESSAGES} messages)`);
76
+ }
77
+ this.state.entries.push({ to, message, queuedAt: Date.now() });
78
+ this.persist();
79
+ return "added";
80
+ }
81
+
82
+ remove(messageId: string): void {
83
+ const remaining = this.state.entries.filter((entry) => entry.message.id !== messageId);
84
+ if (remaining.length === this.state.entries.length) return;
85
+ this.state.entries = remaining;
86
+ this.persist();
87
+ }
88
+
89
+ clear(): void {
90
+ if (this.state.entries.length === 0) return;
91
+ this.state.entries = [];
92
+ this.persist();
93
+ }
94
+
95
+ private load(): OutboxState {
96
+ if (!existsSync(this.filePath)) return { version: OUTBOX_STATE_VERSION, entries: [] };
97
+ try {
98
+ const parsed: unknown = JSON.parse(readFileSync(this.filePath, "utf-8"));
99
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("expected object");
100
+ const state = parsed as Record<string, unknown>;
101
+ if (state.version !== OUTBOX_STATE_VERSION || !Array.isArray(state.entries) || !state.entries.every(isStoredOutboundMessage)) {
102
+ throw new Error("invalid outbox state");
103
+ }
104
+ return { version: OUTBOX_STATE_VERSION, entries: state.entries };
105
+ } catch {
106
+ const corruptPath = `${this.filePath}.corrupt-${Date.now()}`;
107
+ renameSync(this.filePath, corruptPath);
108
+ restrictIntercomRuntimeFile(corruptPath);
109
+ return { version: OUTBOX_STATE_VERSION, entries: [] };
110
+ }
111
+ }
112
+
113
+ private persist(): void {
114
+ writeDurableJson(this.filePath, this.state);
115
+ }
116
+ }
package/package.json ADDED
@@ -0,0 +1,93 @@
1
+ {
2
+ "name": "@ctliz/agent-intercom-claude",
3
+ "version": "0.12.0-connect.3",
4
+ "description": "Claude Code MCP plugin and wakeable worker for direct local messaging with Pi, Codex, and Claude coding-agent sessions.",
5
+ "license": "AGPL-3.0-or-later",
6
+ "type": "module",
7
+ "main": "dist/claude-server.mjs",
8
+ "bin": {
9
+ "claude-intercom-mcp": "dist/claude-server.mjs",
10
+ "claude-intercom-worker": "dist/worker-daemon.mjs",
11
+ "cci": "dist/cci.mjs",
12
+ "ccim": "dist/ccim.mjs"
13
+ },
14
+ "files": [
15
+ "assets/**/*",
16
+ "dist/**/*",
17
+ "claude/**/*.ts",
18
+ "!claude/**/*.test.ts",
19
+ "broker/**/*.ts",
20
+ "!broker/**/*.test.ts",
21
+ "protocol-v4/**/*.ts",
22
+ "!protocol-v4/**/*.test.ts",
23
+ "provider/protected-service.ts",
24
+ "provider/provider.mjs",
25
+ "!provider/entry.ts",
26
+ "scripts/**/*",
27
+ "!scripts/build-protected-provider.mjs",
28
+ "skills/**/*",
29
+ "commands/**/*",
30
+ "monitors/**/*",
31
+ "types.ts",
32
+ "config.ts",
33
+ "durable-json.ts",
34
+ "outbound-outbox.ts",
35
+ ".claude-plugin/**/*",
36
+ ".mcp.json",
37
+ "README.md",
38
+ "CHANGELOG.md",
39
+ "LICENSE",
40
+ "THIRD_PARTY_NOTICES.md",
41
+ "licenses/**/*",
42
+ "COPYRIGHT",
43
+ "LICENSE_TRANSITION.md"
44
+ ],
45
+ "scripts": {
46
+ "build": "node scripts/build.mjs",
47
+ "build:protected-provider": "node scripts/build-protected-provider.mjs",
48
+ "prepack": "npm run build:protected-provider",
49
+ "prepare": "npm run build",
50
+ "pretest": "npm run build",
51
+ "claude:mcp": "tsx claude/server.ts",
52
+ "claude:worker": "tsx claude/worker-daemon.ts",
53
+ "cci": "tsx claude/cci.ts",
54
+ "ccim": "tsx claude/ccim.ts",
55
+ "typecheck": "tsc --noEmit",
56
+ "test": "node --import tsx --test protocol-v4/*.test.ts broker/*.test.ts outbound-outbox.test.ts claude/*.test.ts provider/*.test.ts test/*.test.ts",
57
+ "verify:core": "node scripts/verify-core-provenance.mjs",
58
+ "verify:pack": "node scripts/verify-packed-runtime.mjs"
59
+ },
60
+ "keywords": [
61
+ "claude",
62
+ "claude-code",
63
+ "mcp",
64
+ "intercom",
65
+ "multi-agent",
66
+ "pi",
67
+ "codex"
68
+ ],
69
+ "peerDependencies": {
70
+ "@ctliz/agent-intercom-core": "0.1.0"
71
+ },
72
+ "devDependencies": {
73
+ "@ctliz/agent-intercom-core": "git+https://github.com/ctliz/agent-intercom-core.git#37e074970e2a9de32a16fc325607c3b476b0bd45",
74
+ "@types/node": "^24.0.0",
75
+ "esbuild": "^0.28.1",
76
+ "tsx": "^4.20.0",
77
+ "typescript": "^6.0.3"
78
+ },
79
+ "publishConfig": {
80
+ "access": "public"
81
+ },
82
+ "engines": {
83
+ "node": ">=22.19.0"
84
+ },
85
+ "repository": {
86
+ "type": "git",
87
+ "url": "git+https://github.com/ctliz/agent-intercom-claude.git"
88
+ },
89
+ "homepage": "https://github.com/ctliz/agent-intercom-claude#readme",
90
+ "bugs": {
91
+ "url": "https://github.com/ctliz/agent-intercom-claude/issues"
92
+ }
93
+ }
@@ -0,0 +1,45 @@
1
+ import {
2
+ INTERCOM_PROTOCOL_NAME,
3
+ INTERCOM_PROTOCOL_V4_SEMANTICS_HASH,
4
+ INTERCOM_PROTOCOL_V4_VECTOR_SCHEMA_VERSION,
5
+ INTERCOM_PROTOCOL_V4_VECTORS,
6
+ INTERCOM_PROTOCOL_VERSION,
7
+ INTERCOM_SCOPE_ENV,
8
+ INTERCOM_SCOPE_ID_PATTERN,
9
+ INTERCOM_SCOPE_ID_PATTERN_SOURCE,
10
+ intercomScopeIdFromEnv,
11
+ parseIntercomScopeId,
12
+ sameIntercomScope,
13
+ } from "@ctliz/agent-intercom-core/protocol-v4";
14
+
15
+ export const INTERCOM_REGISTRATION_METADATA_ERROR = "Registration metadata is invalid" as const;
16
+
17
+ export function parseIntercomScopeIdForRegistration(value: unknown): string | undefined {
18
+ try {
19
+ return parseIntercomScopeId(value, "registrationMetadata");
20
+ } catch {
21
+ throw new Error(INTERCOM_REGISTRATION_METADATA_ERROR);
22
+ }
23
+ }
24
+
25
+ export function intercomScopeIdFromEnvForRegistration(env: NodeJS.ProcessEnv = process.env): string | undefined {
26
+ try {
27
+ return intercomScopeIdFromEnv(env);
28
+ } catch {
29
+ throw new Error(INTERCOM_REGISTRATION_METADATA_ERROR);
30
+ }
31
+ }
32
+
33
+ export {
34
+ INTERCOM_PROTOCOL_NAME,
35
+ INTERCOM_PROTOCOL_V4_SEMANTICS_HASH,
36
+ INTERCOM_PROTOCOL_V4_VECTOR_SCHEMA_VERSION,
37
+ INTERCOM_PROTOCOL_V4_VECTORS,
38
+ INTERCOM_PROTOCOL_VERSION,
39
+ INTERCOM_SCOPE_ENV,
40
+ INTERCOM_SCOPE_ID_PATTERN,
41
+ INTERCOM_SCOPE_ID_PATTERN_SOURCE,
42
+ intercomScopeIdFromEnv,
43
+ parseIntercomScopeId,
44
+ sameIntercomScope,
45
+ };
@@ -0,0 +1,162 @@
1
+ import { types as nodeUtilTypes } from "node:util";
2
+ import { BROKER_PROTECTED_PROVIDER_ROOT } from "@ctliz/agent-intercom-core/boss";
3
+
4
+ export const CLAUDE_BOSS_PROTECTED_PROVIDER_ID = "claude" as const;
5
+ export const CLAUDE_BOSS_PROTECTED_PROVIDER_PACKAGE = "@ctliz/agent-intercom-claude" as const;
6
+ export const CLAUDE_BOSS_PROTECTED_PROVIDER_ARTIFACT_PATH =
7
+ `${BROKER_PROTECTED_PROVIDER_ROOT}${CLAUDE_BOSS_PROTECTED_PROVIDER_ID}/provider.mjs` as const;
8
+ export const BOSS_PROTECTED_SERVICE_UNAVAILABLE = "BOSS_PROTECTED_SERVICE_UNAVAILABLE" as const;
9
+
10
+ const CLAUDE_BOSS_PROTECTED_PROVIDER_MODE = "0555" as const;
11
+ const CANONICAL_SEMANTIC_VERSION = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(?:(?:0|[1-9]\d*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
12
+ const CANDIDATE_KEYS = [
13
+ "adapterId",
14
+ "providerPackage",
15
+ "providerVersion",
16
+ "providerDigest",
17
+ "artifactPath",
18
+ "artifactOwnerUid",
19
+ "artifactOwnerGid",
20
+ "artifactMode",
21
+ ] as const;
22
+
23
+ export interface ClaudeBossProtectedProviderArtifactCandidate {
24
+ adapterId: typeof CLAUDE_BOSS_PROTECTED_PROVIDER_ID;
25
+ providerPackage: typeof CLAUDE_BOSS_PROTECTED_PROVIDER_PACKAGE;
26
+ providerVersion: string;
27
+ providerDigest: string;
28
+ artifactPath: typeof CLAUDE_BOSS_PROTECTED_PROVIDER_ARTIFACT_PATH;
29
+ artifactOwnerUid: 0;
30
+ artifactOwnerGid: 0;
31
+ artifactMode: typeof CLAUDE_BOSS_PROTECTED_PROVIDER_MODE;
32
+ }
33
+
34
+ export type ClaudeBossProtectedServiceErrorCode =
35
+ | "INVALID_CLAUDE_PROTECTED_PROVIDER_CANDIDATE"
36
+ | typeof BOSS_PROTECTED_SERVICE_UNAVAILABLE;
37
+
38
+ export class ClaudeBossProtectedServiceError extends Error {
39
+ readonly code: ClaudeBossProtectedServiceErrorCode;
40
+ readonly path: string;
41
+
42
+ constructor(code: ClaudeBossProtectedServiceErrorCode, path: string, message: string) {
43
+ super(`${path}: ${message}`);
44
+ this.name = "ClaudeBossProtectedServiceError";
45
+ this.code = code;
46
+ this.path = path;
47
+ }
48
+ }
49
+
50
+ function invalid(path: string, message: string): never {
51
+ throw new ClaudeBossProtectedServiceError(
52
+ "INVALID_CLAUDE_PROTECTED_PROVIDER_CANDIDATE",
53
+ path,
54
+ message,
55
+ );
56
+ }
57
+
58
+ function assertExactOwnDataCandidate(value: unknown): asserts value is Record<string, unknown> {
59
+ const path = "$candidate";
60
+ if (
61
+ typeof value !== "object"
62
+ || value === null
63
+ || nodeUtilTypes.isProxy(value)
64
+ || Array.isArray(value)
65
+ || Object.getPrototypeOf(value) !== Object.prototype
66
+ ) {
67
+ invalid(path, "must be a non-proxy plain data object");
68
+ }
69
+
70
+ const keys = Reflect.ownKeys(value);
71
+ if (
72
+ keys.length !== CANDIDATE_KEYS.length
73
+ || keys.some((key) => typeof key !== "string" || !CANDIDATE_KEYS.includes(key as typeof CANDIDATE_KEYS[number]))
74
+ ) {
75
+ invalid(path, "must contain exactly the canonical unsigned Claude provider candidate fields");
76
+ }
77
+
78
+ for (const key of CANDIDATE_KEYS) {
79
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
80
+ if (
81
+ descriptor === undefined
82
+ || !descriptor.enumerable
83
+ || !Object.hasOwn(descriptor, "value")
84
+ ) {
85
+ invalid(`${path}.${key}`, "must be an own enumerable data property");
86
+ }
87
+ }
88
+ }
89
+
90
+ function ownValue(value: Record<string, unknown>, key: typeof CANDIDATE_KEYS[number]): unknown {
91
+ return Object.getOwnPropertyDescriptor(value, key)!.value;
92
+ }
93
+
94
+ function readProviderVersion(value: unknown): string {
95
+ if (
96
+ typeof value !== "string"
97
+ || value.length > 128
98
+ || /[\r\n\u2028\u2029]/.test(value)
99
+ || !CANONICAL_SEMANTIC_VERSION.test(value)
100
+ ) {
101
+ invalid("$candidate.providerVersion", "must be a canonical semantic version");
102
+ }
103
+ return value;
104
+ }
105
+
106
+ function readProviderDigest(value: unknown): string {
107
+ if (typeof value !== "string" || value.length !== 64 || !/^[a-f0-9]{64}$/.test(value)) {
108
+ invalid("$candidate.providerDigest", "must be a lowercase SHA-256 digest");
109
+ }
110
+ return value;
111
+ }
112
+
113
+ /**
114
+ * Normalize an unsigned release candidate for the packaged Claude provider.
115
+ * This parser cannot verify installation, signatures, service identities, or
116
+ * authority. Its frozen result remains explicitly non-authoritative.
117
+ */
118
+ export function parseClaudeBossProtectedProviderArtifactCandidate(
119
+ value: unknown,
120
+ ): Readonly<ClaudeBossProtectedProviderArtifactCandidate> {
121
+ assertExactOwnDataCandidate(value);
122
+
123
+ if (ownValue(value, "adapterId") !== CLAUDE_BOSS_PROTECTED_PROVIDER_ID) {
124
+ invalid("$candidate.adapterId", "must identify the Claude provider");
125
+ }
126
+ if (ownValue(value, "providerPackage") !== CLAUDE_BOSS_PROTECTED_PROVIDER_PACKAGE) {
127
+ invalid("$candidate.providerPackage", "must identify the canonical Claude package");
128
+ }
129
+ if (ownValue(value, "artifactPath") !== CLAUDE_BOSS_PROTECTED_PROVIDER_ARTIFACT_PATH) {
130
+ invalid("$candidate.artifactPath", "must equal the canonical protected Claude provider path");
131
+ }
132
+ if (ownValue(value, "artifactOwnerUid") !== 0 || ownValue(value, "artifactOwnerGid") !== 0) {
133
+ invalid("$candidate.artifactOwnerUid", "must describe a root:root artifact");
134
+ }
135
+ if (ownValue(value, "artifactMode") !== CLAUDE_BOSS_PROTECTED_PROVIDER_MODE) {
136
+ invalid("$candidate.artifactMode", "must be read/execute-only mode 0555");
137
+ }
138
+
139
+ return Object.freeze({
140
+ adapterId: CLAUDE_BOSS_PROTECTED_PROVIDER_ID,
141
+ providerPackage: CLAUDE_BOSS_PROTECTED_PROVIDER_PACKAGE,
142
+ providerVersion: readProviderVersion(ownValue(value, "providerVersion")),
143
+ providerDigest: readProviderDigest(ownValue(value, "providerDigest")),
144
+ artifactPath: CLAUDE_BOSS_PROTECTED_PROVIDER_ARTIFACT_PATH,
145
+ artifactOwnerUid: 0,
146
+ artifactOwnerGid: 0,
147
+ artifactMode: CLAUDE_BOSS_PROTECTED_PROVIDER_MODE,
148
+ });
149
+ }
150
+
151
+ /**
152
+ * Production ensure is intentionally unavailable until a protected
153
+ * provisioner supplies release and service identity facts outside caller data.
154
+ * The request is never inspected while that provisioner is absent.
155
+ */
156
+ export function ensureClaudeBossProtectedService(_request: unknown): never {
157
+ throw new ClaudeBossProtectedServiceError(
158
+ BOSS_PROTECTED_SERVICE_UNAVAILABLE,
159
+ "$provisioner",
160
+ "the protected Claude broker service provisioner is not installed",
161
+ );
162
+ }
@@ -0,0 +1,34 @@
1
+ // Generated from provider/entry.ts by scripts/build-protected-provider.mjs.
2
+ // Do not edit this artifact directly.
3
+ const supportedBossFeature = Object.freeze({
4
+ version: "intercom.broker-feature.v1",
5
+ feature: "boss-run-v1",
6
+ featureVersion: 1,
7
+ semanticsHash: "8943eb60d29afa5264322b5cc7df3de245b01b1cf48a8cbf9cfb6188b02fcfa9",
8
+ controlEnvelopeVersion: 1,
9
+ capabilityDigest: "239bee8bb64cc8c149d49ac00c7396f33f375a89a1d6dea6bd86ff840d551a59",
10
+ });
11
+ /** Immutable build-time contract only; it is not an installed-provider claim. */
12
+ export const CLAUDE_BOSS_PROTECTED_PROVIDER_BUILD_IDENTITY = Object.freeze({
13
+ contractVersion: "claude.boss-protected-provider.v1",
14
+ adapterId: "claude",
15
+ providerPackage: "@ctliz/agent-intercom-claude",
16
+ supportedBaseProtocolVersions: Object.freeze([4]),
17
+ supportedFeatures: Object.freeze([supportedBossFeature]),
18
+ protocolFeatureContractHash: "dae30efe2c48d2de0fe72a7ebdfd107d3feaefc180d42056ba05df6088a94364",
19
+ authoritative: false,
20
+ providerStartAvailable: false,
21
+ bossAdvertisementEnabled: false,
22
+ });
23
+ const BOSS_PROTECTED_PROVIDER_START_UNAVAILABLE = "BOSS_PROTECTED_PROVIDER_START_UNAVAILABLE";
24
+ class ClaudeBossProtectedProviderStartUnavailableError extends Error {
25
+ code = BOSS_PROTECTED_PROVIDER_START_UNAVAILABLE;
26
+ constructor() {
27
+ super(`${BOSS_PROTECTED_PROVIDER_START_UNAVAILABLE}: protected provider execution is not installed`);
28
+ this.name = "ClaudeBossProtectedProviderStartUnavailableError";
29
+ }
30
+ }
31
+ /** A later protected service may replace this dormant entry after release gates. */
32
+ export function startClaudeBossProtectedProvider(_request) {
33
+ throw new ClaudeBossProtectedProviderStartUnavailableError();
34
+ }
@@ -0,0 +1,71 @@
1
+ import { chmod } from "node:fs/promises";
2
+ import { build } from "esbuild";
3
+
4
+ const common = {
5
+ bundle: true,
6
+ metafile: true,
7
+ platform: "node",
8
+ format: "esm",
9
+ target: "node20",
10
+ // Core is a runtime peer. Keep both its root entry point and every exported
11
+ // subpath as imports so all adapter bundles share the consumer's one Core
12
+ // module instance instead of embedding private copies.
13
+ external: [
14
+ "@ctliz/agent-intercom-core",
15
+ "@ctliz/agent-intercom-core/*",
16
+ ],
17
+ };
18
+
19
+ const buildResults = await Promise.all([
20
+ build({
21
+ ...common,
22
+ entryPoints: ["claude/server.ts"],
23
+ outfile: "dist/claude-server.mjs",
24
+ banner: { js: "#!/usr/bin/env node" },
25
+ }),
26
+ build({
27
+ ...common,
28
+ entryPoints: ["broker/broker.ts"],
29
+ outfile: "dist/broker.mjs",
30
+ }),
31
+ build({
32
+ ...common,
33
+ entryPoints: ["claude/worker-daemon.ts"],
34
+ outfile: "dist/worker-daemon.mjs",
35
+ banner: { js: "#!/usr/bin/env node" },
36
+ }),
37
+ build({
38
+ ...common,
39
+ entryPoints: ["claude/cci.ts"],
40
+ outfile: "dist/cci.mjs",
41
+ banner: { js: "#!/usr/bin/env node" },
42
+ }),
43
+ build({
44
+ ...common,
45
+ entryPoints: ["claude/ccim.ts"],
46
+ outfile: "dist/ccim.mjs",
47
+ banner: { js: "#!/usr/bin/env node" },
48
+ }),
49
+ build({
50
+ ...common,
51
+ entryPoints: ["claude/inbox-monitor.ts"],
52
+ outfile: "dist/inbox-monitor.mjs",
53
+ banner: { js: "#!/usr/bin/env node" },
54
+ }),
55
+ ]);
56
+
57
+ for (const result of buildResults) {
58
+ for (const input of Object.keys(result.metafile.inputs)) {
59
+ if (input.includes("node_modules/@ctliz/agent-intercom-core/")) {
60
+ throw new Error(`Core runtime was embedded in an adapter bundle through ${input}`);
61
+ }
62
+ }
63
+ }
64
+
65
+ await Promise.all([
66
+ chmod("dist/claude-server.mjs", 0o755),
67
+ chmod("dist/worker-daemon.mjs", 0o755),
68
+ chmod("dist/cci.mjs", 0o755),
69
+ chmod("dist/ccim.mjs", 0o755),
70
+ chmod("dist/inbox-monitor.mjs", 0o755),
71
+ ]);
@@ -0,0 +1,64 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { spawnSync } from "node:child_process";
5
+
6
+ export const APPROVED_CORE = Object.freeze({
7
+ name: "@ctliz/agent-intercom-core",
8
+ version: "0.1.0",
9
+ commit: "37e074970e2a9de32a16fc325607c3b476b0bd45",
10
+ integrity: "sha512-b5MXnfKh/RCjKoVor0tsFO+NKAqPROWgkgbHt7jiHxhdVbSh7gWvOdnVUtpSdPwJLYKSQrYXT+TJl7QocdJEPA==",
11
+ });
12
+
13
+ const repositoryRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
14
+ const devSpec = `git+https://github.com/ctliz/agent-intercom-core.git#${APPROVED_CORE.commit}`;
15
+
16
+ function readJson(path) {
17
+ return JSON.parse(readFileSync(path, "utf8"));
18
+ }
19
+
20
+ function requireEqual(actual, expected, field) {
21
+ if (actual !== expected) {
22
+ throw new Error(`${field} must be ${JSON.stringify(expected)}; received ${JSON.stringify(actual)}`);
23
+ }
24
+ }
25
+
26
+ function verifyLocalProvenance() {
27
+ const manifest = readJson(join(repositoryRoot, "package.json"));
28
+ const lock = readJson(join(repositoryRoot, "package-lock.json"));
29
+ const lockRoot = lock.packages?.[""];
30
+ const lockedCore = lock.packages?.[`node_modules/${APPROVED_CORE.name}`];
31
+
32
+ requireEqual(manifest.peerDependencies?.[APPROVED_CORE.name], APPROVED_CORE.version, "Core peer dependency");
33
+ requireEqual(manifest.dependencies?.[APPROVED_CORE.name], undefined, "Core production dependency");
34
+ requireEqual(manifest.peerDependenciesMeta?.[APPROVED_CORE.name]?.optional, undefined, "Core peer optionality");
35
+ requireEqual(manifest.devDependencies?.[APPROVED_CORE.name], devSpec, "Core development provenance");
36
+ requireEqual(lockRoot?.peerDependencies?.[APPROVED_CORE.name], APPROVED_CORE.version, "locked Core peer dependency");
37
+ requireEqual(lockRoot?.devDependencies?.[APPROVED_CORE.name], devSpec, "locked Core development provenance");
38
+ requireEqual(lockedCore?.version, APPROVED_CORE.version, "supplied Core version");
39
+ requireEqual(lockedCore?.resolved, devSpec, "supplied Core commit");
40
+ requireEqual(lockedCore?.integrity, APPROVED_CORE.integrity, "supplied Core artifact integrity");
41
+ requireEqual(lockedCore?.dev, true, "supplied Core dependency class");
42
+ }
43
+
44
+ function verifyPublishedProvenance() {
45
+ const result = spawnSync("npm", [
46
+ "view",
47
+ `${APPROVED_CORE.name}@${APPROVED_CORE.version}`,
48
+ "version",
49
+ "dist.integrity",
50
+ "gitHead",
51
+ "--json",
52
+ ], { encoding: "utf8" });
53
+ if (result.error) throw result.error;
54
+ if (result.status !== 0) {
55
+ throw new Error(`Unable to inspect published Core ${APPROVED_CORE.version}: ${(result.stderr || result.stdout).trim()}`);
56
+ }
57
+ const published = JSON.parse(result.stdout);
58
+ requireEqual(published.version, APPROVED_CORE.version, "published Core version");
59
+ requireEqual(published["dist.integrity"] ?? published.dist?.integrity, APPROVED_CORE.integrity, "published Core artifact integrity");
60
+ requireEqual(published.gitHead, APPROVED_CORE.commit, "published Core commit");
61
+ }
62
+
63
+ verifyLocalProvenance();
64
+ if (process.argv.includes("--published")) verifyPublishedProvenance();