@adhdev/daemon-core 0.9.82-rc.15 → 0.9.82-rc.151
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/boot/process-hardening.d.ts +50 -0
- package/dist/chat/source-machine.d.ts +166 -0
- package/dist/chat/source-resolver.d.ts +104 -0
- package/dist/chat/subscription-updates.d.ts +1 -0
- package/dist/cli-adapter-types.d.ts +5 -1
- package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +178 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +104 -63
- package/dist/cli-adapters/provider-cli-parse.d.ts +4 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +27 -0
- package/dist/commands/handler.d.ts +66 -0
- package/dist/commands/router.d.ts +22 -0
- package/dist/config/chat-history.d.ts +5 -0
- package/dist/config/config.d.ts +5 -0
- package/dist/config/mesh-config.d.ts +68 -1
- package/dist/git/git-commands.d.ts +5 -1
- package/dist/index.d.ts +20 -6
- package/dist/index.js +13153 -3031
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +13099 -3023
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/ipc/local-ipc-server.d.ts +91 -0
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/mesh/beads-db.d.ts +72 -0
- package/dist/mesh/contracts.d.ts +164 -0
- package/dist/mesh/coordinator-registry.d.ts +25 -0
- package/dist/mesh/mesh-active-work.d.ts +90 -0
- package/dist/mesh/mesh-events.d.ts +77 -5
- package/dist/mesh/mesh-fast-forward.d.ts +39 -0
- package/dist/mesh/mesh-host-ownership.d.ts +9 -0
- package/dist/mesh/mesh-ledger.d.ts +58 -1
- package/dist/mesh/mesh-refine-status.d.ts +26 -0
- package/dist/mesh/mesh-work-queue.d.ts +44 -5
- package/dist/mesh/preview-freshness.d.ts +18 -0
- package/dist/mesh/refine-config.d.ts +193 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +113 -0
- package/dist/providers/approval-utils.d.ts +9 -0
- package/dist/providers/cli-provider-instance.d.ts +6 -1
- package/dist/providers/contracts.d.ts +50 -5
- package/dist/providers/native-history/antigravity-cli-transcript.d.ts +100 -0
- package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
- package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
- package/dist/providers/native-history/index.d.ts +11 -0
- package/dist/providers/provider-loader.d.ts +19 -1
- package/dist/providers/read-chat-contract.d.ts +29 -0
- package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +85 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +64 -0
- package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
- package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
- package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
- package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
- package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
- package/dist/providers/sdk/v1/index.d.ts +30 -0
- package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
- package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
- package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
- package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
- package/dist/providers/sdk/v1/types/common/index.d.ts +169 -0
- package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
- package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
- package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
- package/dist/providers/transcript-v2.d.ts +176 -0
- package/dist/repo-mesh-types.d.ts +67 -0
- package/dist/shared-types.d.ts +12 -0
- package/dist/status/reporter.d.ts +2 -0
- package/dist/status/snapshot.d.ts +1 -0
- package/dist/types.d.ts +5 -0
- package/package.json +6 -2
- package/src/boot/daemon-lifecycle.ts +17 -10
- package/src/boot/process-hardening.ts +89 -0
- package/src/chat/source-machine.ts +534 -0
- package/src/chat/source-resolver.ts +0 -0
- package/src/chat/subscription-updates.ts +14 -1
- package/src/cli-adapter-types.d.ts +1 -0
- package/src/cli-adapter-types.ts +3 -1
- package/src/cli-adapters/cli-script-runner.ts +421 -0
- package/src/cli-adapters/cli-state-engine.ts +1086 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +664 -1137
- package/src/cli-adapters/provider-cli-parse.d.ts +1 -0
- package/src/cli-adapters/provider-cli-parse.ts +13 -0
- package/src/cli-adapters/provider-cli-runtime.ts +3 -1
- package/src/cli-adapters/provider-cli-shared.d.ts +2 -0
- package/src/cli-adapters/provider-cli-shared.ts +57 -11
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +17 -1
- package/src/cli-adapters/terminal-backends/xterm-backend.ts +8 -1
- package/src/commands/chat-commands.ts +1451 -52
- package/src/commands/cli-manager.ts +184 -3
- package/src/commands/handler.ts +547 -2
- package/src/commands/mesh-coordinator.ts +13 -143
- package/src/commands/router.ts +3273 -428
- package/src/config/chat-history.ts +79 -24
- package/src/config/config.ts +12 -0
- package/src/config/mesh-config.ts +249 -2
- package/src/config/recent-activity.ts +8 -2
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/detection/ide-detector.ts +26 -16
- package/src/git/git-commands.ts +17 -5
- package/src/git/git-worktree.ts +8 -1
- package/src/index.ts +72 -5
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/ipc/local-ipc-server.ts +278 -0
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/logger.ts +2 -1
- package/src/mesh/beads-db.ts +479 -0
- package/src/mesh/contracts.ts +329 -0
- package/src/mesh/coordinator-prompt.ts +40 -22
- package/src/mesh/coordinator-registry.ts +75 -0
- package/src/mesh/mesh-active-work.ts +437 -0
- package/src/mesh/mesh-events.ts +820 -61
- package/src/mesh/mesh-fast-forward.ts +430 -0
- package/src/mesh/mesh-host-ownership.ts +73 -0
- package/src/mesh/mesh-ledger.ts +457 -104
- package/src/mesh/mesh-refine-status.ts +144 -0
- package/src/mesh/mesh-work-queue.ts +216 -158
- package/src/mesh/preview-freshness.ts +118 -0
- package/src/mesh/refine-config.ts +366 -0
- package/src/mesh/worktree-bootstrap-config.ts +247 -0
- package/src/providers/approval-utils.ts +39 -5
- package/src/providers/chat-message-normalization.ts +4 -11
- package/src/providers/cli-provider-instance.ts +380 -44
- package/src/providers/contracts.ts +52 -5
- package/src/providers/ide-provider-instance.ts +17 -3
- package/src/providers/native-history/antigravity-cli-transcript.ts +643 -0
- package/src/providers/native-history/claude-cli-transcript.ts +396 -0
- package/src/providers/native-history/codex-cli-transcript.ts +419 -0
- package/src/providers/native-history/index.ts +23 -0
- package/src/providers/provider-loader.ts +294 -28
- package/src/providers/provider-schema.ts +31 -13
- package/src/providers/read-chat-contract.ts +76 -16
- package/src/providers/sdk/README.md +49 -0
- package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +262 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +245 -0
- package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
- package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
- package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
- package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
- package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
- package/src/providers/sdk/v1/index.ts +152 -0
- package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
- package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
- package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
- package/src/providers/sdk/v1/schemas/cli/provider.schema.json +444 -0
- package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
- package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +119 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
- package/src/providers/sdk/v1/types/cli/index.ts +365 -0
- package/src/providers/sdk/v1/types/common/index.ts +210 -0
- package/src/providers/sdk/v1/validators/index.ts +19 -0
- package/src/providers/sdk/v1/validators/manifest.ts +110 -0
- package/src/providers/sdk/v1/validators/taint.ts +309 -0
- package/src/providers/transcript-v2.ts +567 -0
- package/src/providers/version-archive.ts +38 -20
- package/src/repo-mesh-types.ts +77 -0
- package/src/shared-types.ts +9 -0
- package/src/status/builders.ts +23 -6
- package/src/status/reporter.ts +15 -0
- package/src/status/snapshot.ts +35 -11
- package/src/system/host-memory.ts +29 -12
- package/src/types.ts +5 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static taint analyzer for extended-tier override JS.
|
|
3
|
+
*
|
|
4
|
+
* Goal: classify the override JS shipped with an extended-tier provider into
|
|
5
|
+
* one of three risk tiers, so the marketplace + daemon trust prompt can
|
|
6
|
+
* surface accurate language to the operator instead of a single generic
|
|
7
|
+
* "this provider contains JS" warning.
|
|
8
|
+
*
|
|
9
|
+
* - clean : no flagged APIs at all. The override looks like pure string
|
|
10
|
+
* manipulation. Still extended-tier, but the warning copy is
|
|
11
|
+
* "this provider contains JS; nothing dangerous was detected".
|
|
12
|
+
* - elevated : touches process / dynamic codegen / dynamic require / eval
|
|
13
|
+
* / Function constructor / child_process. The warning copy is
|
|
14
|
+
* explicit ("this provider can run shell commands / dynamically
|
|
15
|
+
* load code").
|
|
16
|
+
* - hostile : combines elevated APIs with network egress, AND uses obvious
|
|
17
|
+
* obfuscation (base64-decoded strings fed into Function / eval).
|
|
18
|
+
* The publish endpoint should reject these.
|
|
19
|
+
*
|
|
20
|
+
* Implementation note: this is a *static* analyzer — string + light AST scan
|
|
21
|
+
* via Node's built-in parser. It is intentionally fail-loud: anything that
|
|
22
|
+
* looks suspicious is flagged. False positives are fine; this exists to gate
|
|
23
|
+
* the trust prompt, not to be a real sandbox. The real sandbox lives in
|
|
24
|
+
* Phase 5 (isolated-vm).
|
|
25
|
+
*
|
|
26
|
+
* Used by:
|
|
27
|
+
* - `adhdev provider validate` (CLI tier classification)
|
|
28
|
+
* - registry publish endpoint static-check
|
|
29
|
+
* - daemon at provider-load time, to size the warning
|
|
30
|
+
*/
|
|
31
|
+
export type TaintLevel = 'clean' | 'elevated' | 'hostile';
|
|
32
|
+
export interface TaintFinding {
|
|
33
|
+
/** What was found, e.g. "child_process.exec". */
|
|
34
|
+
api: string;
|
|
35
|
+
/** Coarse category. */
|
|
36
|
+
category: TaintCategory;
|
|
37
|
+
/** File the finding came from. */
|
|
38
|
+
file: string;
|
|
39
|
+
/** Line number (1-indexed) of the first character of the matched token. */
|
|
40
|
+
line: number;
|
|
41
|
+
/** The surrounding source excerpt, trimmed. */
|
|
42
|
+
excerpt: string;
|
|
43
|
+
}
|
|
44
|
+
export type TaintCategory = 'process-control' | 'shell-exec' | 'fs-write' | 'fs-read' | 'network' | 'dynamic-codegen' | 'dynamic-require' | 'obfuscation' | 'global-mutation';
|
|
45
|
+
export interface TaintResult {
|
|
46
|
+
level: TaintLevel;
|
|
47
|
+
findings: TaintFinding[];
|
|
48
|
+
files: string[];
|
|
49
|
+
}
|
|
50
|
+
export declare function analyzeOverrideTaint(manifestPath: string): TaintResult;
|
|
51
|
+
/** Format a result as a single CLI-friendly block. */
|
|
52
|
+
export declare function formatTaintResult(result: TaintResult): string;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Transcript Contract v2
|
|
3
|
+
*
|
|
4
|
+
* First-class message identity, monotonic sequence, strict enums. Designed to
|
|
5
|
+
* replace the v1 contract (contracts.ts ReadChatResult + types.ts ChatMessage)
|
|
6
|
+
* during the A2 big-bang. This module defines the contract; it does not yet
|
|
7
|
+
* change runtime behaviour. v1 keeps working until A2 flips the switch.
|
|
8
|
+
*
|
|
9
|
+
* Invariants v2 enforces (and v1 does not):
|
|
10
|
+
* 1. Every ChatMessageV2 has providerUnitKey, bubbleId, sequence — none optional.
|
|
11
|
+
* 2. sequence is a monotonic integer per (sessionId, source) tuple. Producers
|
|
12
|
+
* never reuse it; consumers MAY assume strict ordering.
|
|
13
|
+
* 3. providerUnitKey is stable across re-reads: the same logical message
|
|
14
|
+
* always yields the same key even if its index shifts or content is
|
|
15
|
+
* edited mid-stream. Producers derive it from provider-owned identifiers
|
|
16
|
+
* (turn id, native message id), not from content hashes or array index.
|
|
17
|
+
* 4. timestamp is producer-asserted wall-clock; orderingTimestamp is monotonic
|
|
18
|
+
* and used for snapshot diffs. 1ms fallback increments are forbidden — if
|
|
19
|
+
* a producer cannot supply a stable monotonic value, the daemon assigns
|
|
20
|
+
* sequence at ingest and orderingTimestamp = ingestedAt.
|
|
21
|
+
* 5. visibility / source / role / kind are strict unions. No `(string & {})`
|
|
22
|
+
* escape hatches.
|
|
23
|
+
* 6. Coverage is exactly one of full/tail/current-turn. Mixing it with
|
|
24
|
+
* partialReason or unavailableReason is a contract violation.
|
|
25
|
+
* 7. workspace / sessionId are explicit on every ReadChatResultV2; the daemon
|
|
26
|
+
* no longer infers them from message bodies.
|
|
27
|
+
*/
|
|
28
|
+
import type { MessagePart, ToolCallInfo } from './contracts.js';
|
|
29
|
+
export declare const CHAT_CONTRACT_VERSION_V1: "1.0";
|
|
30
|
+
export declare const CHAT_CONTRACT_VERSION_V2: "2.0";
|
|
31
|
+
export type ChatContractVersion = typeof CHAT_CONTRACT_VERSION_V1 | typeof CHAT_CONTRACT_VERSION_V2;
|
|
32
|
+
export declare const SUPPORTED_CHAT_CONTRACT_VERSIONS: readonly ChatContractVersion[];
|
|
33
|
+
export declare const CHAT_ROLES_V2: readonly ["user", "assistant", "system"];
|
|
34
|
+
export type ChatRoleV2 = typeof CHAT_ROLES_V2[number];
|
|
35
|
+
export declare const CHAT_MESSAGE_KINDS_V2: readonly ["standard", "thought", "tool", "terminal", "system"];
|
|
36
|
+
export type ChatMessageKindV2 = typeof CHAT_MESSAGE_KINDS_V2[number];
|
|
37
|
+
export declare const CHAT_VISIBILITIES_V2: readonly ["user", "debug", "internal", "hidden"];
|
|
38
|
+
export type ChatVisibilityV2 = typeof CHAT_VISIBILITIES_V2[number];
|
|
39
|
+
export declare const CHAT_AUDIENCES_V2: readonly ["chat", "debug", "trace", "internal"];
|
|
40
|
+
export type ChatAudienceV2 = typeof CHAT_AUDIENCES_V2[number];
|
|
41
|
+
export declare const CHAT_SOURCES_V2: readonly ["assistant_text", "tool_call", "terminal_command", "runtime_activity", "runtime_status", "provider_chrome", "control"];
|
|
42
|
+
export type ChatSourceV2 = typeof CHAT_SOURCES_V2[number];
|
|
43
|
+
export declare const CHAT_BUBBLE_STATES_V2: readonly ["draft", "streaming", "final", "removed"];
|
|
44
|
+
export type ChatBubbleStateV2 = typeof CHAT_BUBBLE_STATES_V2[number];
|
|
45
|
+
/**
|
|
46
|
+
* Stable identity of a single chat message. All fields are required in v2.
|
|
47
|
+
*
|
|
48
|
+
* - providerUnitKey: provider-owned canonical id. Must be derivable from
|
|
49
|
+
* provider primitives (native message id, turn id, bubble id), NOT from
|
|
50
|
+
* array index or content hash. Stable across re-reads of the same logical
|
|
51
|
+
* message even if neighbours change.
|
|
52
|
+
* - bubbleId: dashboard-owned bubble identity. May equal providerUnitKey when
|
|
53
|
+
* the provider already exposes a bubble-grained id; otherwise daemon
|
|
54
|
+
* derives it deterministically from providerUnitKey (e.g. `bubble:${key}`).
|
|
55
|
+
* - sequence: monotonic integer per (sessionId, source). Strictly increasing
|
|
56
|
+
* over the lifetime of the session. Used for snapshot diffs and dedup.
|
|
57
|
+
* - turnKey: stable identifier for the conversation turn this message belongs
|
|
58
|
+
* to. Required so consumers can group messages without re-deriving turns.
|
|
59
|
+
*/
|
|
60
|
+
export interface MessageIdentityV2 {
|
|
61
|
+
providerUnitKey: string;
|
|
62
|
+
bubbleId: string;
|
|
63
|
+
sequence: number;
|
|
64
|
+
turnKey: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Producer-asserted wall-clock time (ms since epoch). Forward jumps are
|
|
68
|
+
* permitted; consumers MUST NOT rely on this for ordering.
|
|
69
|
+
*/
|
|
70
|
+
export type ChatTimestampMs = number;
|
|
71
|
+
/**
|
|
72
|
+
* Monotonic ordering value (ms since epoch, but assigned by the daemon at
|
|
73
|
+
* ingest to guarantee monotonicity even if the producer's wall clock skews).
|
|
74
|
+
* Used by snapshot diffs in place of timestamp.
|
|
75
|
+
*/
|
|
76
|
+
export type ChatOrderingTimestampMs = number;
|
|
77
|
+
export interface ChatMessageV2 extends MessageIdentityV2 {
|
|
78
|
+
role: ChatRoleV2;
|
|
79
|
+
kind: ChatMessageKindV2;
|
|
80
|
+
content: string | MessagePart[];
|
|
81
|
+
bubbleState: ChatBubbleStateV2;
|
|
82
|
+
timestamp: ChatTimestampMs;
|
|
83
|
+
orderingTimestamp: ChatOrderingTimestampMs;
|
|
84
|
+
visibility: ChatVisibilityV2;
|
|
85
|
+
audience: ChatAudienceV2;
|
|
86
|
+
source: ChatSourceV2;
|
|
87
|
+
/** Sender display name (for shared sessions). Empty string when unused. */
|
|
88
|
+
senderName: string;
|
|
89
|
+
/** Tool calls associated with this message. Empty array when none. */
|
|
90
|
+
toolCalls: ToolCallInfo[];
|
|
91
|
+
/** Producer-defined metadata. Daemon never inspects this except for passthrough. */
|
|
92
|
+
meta: Readonly<Record<string, unknown>>;
|
|
93
|
+
}
|
|
94
|
+
export interface WorkspaceContextV2 {
|
|
95
|
+
/** Absolute realpath of the workspace the session is reading from. */
|
|
96
|
+
workspacePath: string;
|
|
97
|
+
/** Workspace the session was intended to operate on (may differ during cross-workspace ops). */
|
|
98
|
+
intendedWorkspacePath: string;
|
|
99
|
+
}
|
|
100
|
+
export interface SessionContextV2 {
|
|
101
|
+
/** Provider-owned session id (e.g. claude.jsonl uuid, codex session_meta.id). */
|
|
102
|
+
providerSessionId: string;
|
|
103
|
+
/** Optional daemon-side history alias used to disambiguate within a workspace. */
|
|
104
|
+
historySessionId?: string;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* v1 allowed `coverage='full'` to coexist with `partialReason`/`unavailableReason`,
|
|
108
|
+
* which is a contract lie. v2 makes the three states mutually exclusive.
|
|
109
|
+
*/
|
|
110
|
+
export type CoverageHintV2 = {
|
|
111
|
+
kind: 'full';
|
|
112
|
+
} | {
|
|
113
|
+
kind: 'tail';
|
|
114
|
+
tailMessageCount: number;
|
|
115
|
+
} | {
|
|
116
|
+
kind: 'current-turn';
|
|
117
|
+
turnKey: string;
|
|
118
|
+
} | {
|
|
119
|
+
kind: 'partial';
|
|
120
|
+
partialReason: string;
|
|
121
|
+
} | {
|
|
122
|
+
kind: 'unavailable';
|
|
123
|
+
unavailableReason: string;
|
|
124
|
+
};
|
|
125
|
+
export type CanonicalHistoryModeV2 = 'native-source' | 'disabled' | 'materialized-mirror';
|
|
126
|
+
export interface CanonicalHistoryDeclarationV2 {
|
|
127
|
+
contractVersion: ChatContractVersion;
|
|
128
|
+
format: string;
|
|
129
|
+
mode: CanonicalHistoryModeV2;
|
|
130
|
+
watchPath?: string;
|
|
131
|
+
scripts: {
|
|
132
|
+
readSession: string;
|
|
133
|
+
listSessions: string;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
export type ReadChatStatusV2 = 'idle' | 'generating' | 'waiting_approval' | 'error' | 'panel_hidden' | 'streaming';
|
|
137
|
+
export type ReadChatTurnStatusV2 = 'open' | 'waiting_approval' | 'complete' | 'error';
|
|
138
|
+
export interface ReadChatResultV2 {
|
|
139
|
+
contractVersion: typeof CHAT_CONTRACT_VERSION_V2;
|
|
140
|
+
workspace: WorkspaceContextV2;
|
|
141
|
+
session: SessionContextV2;
|
|
142
|
+
coverage: CoverageHintV2;
|
|
143
|
+
status: ReadChatStatusV2;
|
|
144
|
+
messages: ChatMessageV2[];
|
|
145
|
+
currentTurnId: string;
|
|
146
|
+
turnStatus: ReadChatTurnStatusV2;
|
|
147
|
+
transcriptAuthority: 'provider' | 'daemon';
|
|
148
|
+
}
|
|
149
|
+
export declare class ChatContractViolationError extends Error {
|
|
150
|
+
readonly violationPath: string;
|
|
151
|
+
readonly contractVersion: ChatContractVersion;
|
|
152
|
+
constructor(contractVersion: ChatContractVersion, violationPath: string, detail: string);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Validate a v2 read_chat payload. Throws ChatContractViolationError on the
|
|
156
|
+
* first violation. v2 invariants beyond per-field checks:
|
|
157
|
+
* - messages[*].sequence strictly increasing
|
|
158
|
+
* - messages[*].orderingTimestamp monotonic non-decreasing
|
|
159
|
+
* - coverage union is well-formed
|
|
160
|
+
*/
|
|
161
|
+
export declare function assertReadChatResultV2Payload(raw: unknown): ReadChatResultV2;
|
|
162
|
+
export declare function isSupportedChatContractVersion(value: unknown): value is ChatContractVersion;
|
|
163
|
+
/**
|
|
164
|
+
* Read a provider's declared chat contract version from its parsed
|
|
165
|
+
* provider.json `canonicalHistory.contractVersion` field.
|
|
166
|
+
*
|
|
167
|
+
* - Absent or unrecognized → treated as v1 (legacy behaviour preserved during
|
|
168
|
+
* the A1 transition). A2 will tighten this to reject unsupported versions
|
|
169
|
+
* at provider load time.
|
|
170
|
+
* - Present and supported → returned as-is.
|
|
171
|
+
*
|
|
172
|
+
* The returned version drives which validator (assertReadChatResultV2Payload
|
|
173
|
+
* vs the legacy validateReadChatResultPayload) processes the provider's
|
|
174
|
+
* read_chat output.
|
|
175
|
+
*/
|
|
176
|
+
export declare function readDeclaredChatContractVersion(canonicalHistory: unknown): ChatContractVersion;
|
|
@@ -19,10 +19,37 @@ export interface RepoMesh {
|
|
|
19
19
|
defaultBranch?: string;
|
|
20
20
|
policy: RepoMeshPolicy;
|
|
21
21
|
coordinator: RepoMeshCoordinatorConfig;
|
|
22
|
+
meshHost?: RepoMeshHostMetadata;
|
|
22
23
|
projectContext: ProjectContextSnapshot;
|
|
23
24
|
nodes: RepoMeshNode[];
|
|
24
25
|
status: 'active' | 'archived' | 'deleted';
|
|
25
26
|
}
|
|
27
|
+
export type RepoMeshDaemonRole = 'host' | 'member';
|
|
28
|
+
export interface RepoMeshHostPairingMetadata {
|
|
29
|
+
status: 'not_configured' | 'pairing' | 'paired' | 'rejected' | 'revoked';
|
|
30
|
+
tokenId?: string;
|
|
31
|
+
joinedAt?: string;
|
|
32
|
+
lastPairedAt?: string;
|
|
33
|
+
lastRejectedAt?: string;
|
|
34
|
+
expiresAt?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface RepoMeshHostMetadata {
|
|
37
|
+
/** Local daemon role for this mesh. Missing metadata defaults to host for standalone compatibility. */
|
|
38
|
+
role: RepoMeshDaemonRole;
|
|
39
|
+
/** Daemon that owns mesh truth/status/git/queue/session/ledger/coordinator ownership. */
|
|
40
|
+
hostDaemonId?: string;
|
|
41
|
+
/** Mesh node that represents the host daemon, when known. */
|
|
42
|
+
hostNodeId?: string;
|
|
43
|
+
/** Future standalone manual pairing endpoint entered by member daemons. */
|
|
44
|
+
hostAddress?: string;
|
|
45
|
+
/** Redacted pairing state only; raw join tokens must not be persisted here. */
|
|
46
|
+
pairing?: RepoMeshHostPairingMetadata;
|
|
47
|
+
}
|
|
48
|
+
export interface RepoMeshHostStatus extends RepoMeshHostMetadata {
|
|
49
|
+
canOwnCoordinator: boolean;
|
|
50
|
+
canOwnQueue: boolean;
|
|
51
|
+
defaulted: boolean;
|
|
52
|
+
}
|
|
26
53
|
export interface RepoMeshNode {
|
|
27
54
|
id: string;
|
|
28
55
|
daemonId: string;
|
|
@@ -37,6 +64,7 @@ export interface RepoMeshNode {
|
|
|
37
64
|
effectiveCapabilities: RepoMeshNodeCapabilities;
|
|
38
65
|
policy: RepoMeshNodePolicy;
|
|
39
66
|
health: RepoMeshNodeHealth;
|
|
67
|
+
role?: RepoMeshDaemonRole;
|
|
40
68
|
status: 'enabled' | 'disabled' | 'removed';
|
|
41
69
|
}
|
|
42
70
|
export type RepoMeshNodeHealth = 'online' | 'offline' | 'degraded' | 'dirty' | 'wrong_branch' | 'unknown';
|
|
@@ -46,6 +74,13 @@ export interface RepoMeshPolicy {
|
|
|
46
74
|
requirePreTaskCheckpoint: boolean;
|
|
47
75
|
requirePostTaskCheckpoint: boolean;
|
|
48
76
|
requireApprovalForPush: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Narrow Refinery opt-in: when validation and patch-equivalence have passed,
|
|
79
|
+
* allow Refinery to publish submodule gitlink commits to each submodule's
|
|
80
|
+
* configured remote main branch with a non-force push, then verify reachability.
|
|
81
|
+
* Defaults to false; root branch pushes/merges are not affected.
|
|
82
|
+
*/
|
|
83
|
+
allowAutoPublishSubmoduleMainCommits?: boolean;
|
|
49
84
|
requireApprovalForDestructiveGit: boolean;
|
|
50
85
|
dirtyWorkspaceBehavior: 'block' | 'warn' | 'checkpoint_then_continue';
|
|
51
86
|
maxParallelTasks: number;
|
|
@@ -185,6 +220,7 @@ export interface LocalMeshEntry {
|
|
|
185
220
|
defaultBranch?: string;
|
|
186
221
|
policy: RepoMeshPolicy;
|
|
187
222
|
coordinator: RepoMeshCoordinatorConfig;
|
|
223
|
+
meshHost?: RepoMeshHostMetadata;
|
|
188
224
|
nodes: LocalMeshNodeEntry[];
|
|
189
225
|
createdAt: string;
|
|
190
226
|
updatedAt: string;
|
|
@@ -204,8 +240,23 @@ export interface LocalMeshNodeEntry {
|
|
|
204
240
|
worktreeBranch?: string;
|
|
205
241
|
/** Node ID this worktree was cloned from */
|
|
206
242
|
clonedFromNodeId?: string;
|
|
243
|
+
/** Repo-local preparation result for ADHDev-created worktree nodes. */
|
|
244
|
+
worktreeBootstrap?: {
|
|
245
|
+
status: 'ready' | 'running' | 'failed' | 'not_configured' | 'disabled' | 'stale';
|
|
246
|
+
required?: boolean;
|
|
247
|
+
configSource?: string;
|
|
248
|
+
configSourceType?: string;
|
|
249
|
+
startedAt?: string;
|
|
250
|
+
completedAt?: string;
|
|
251
|
+
lastCommand?: string;
|
|
252
|
+
exitCode?: number | null;
|
|
253
|
+
error?: string;
|
|
254
|
+
commandsRun?: Array<Record<string, unknown>>;
|
|
255
|
+
staleInputs?: string[];
|
|
256
|
+
};
|
|
207
257
|
/** Optional associated/external repos configured as node metadata. */
|
|
208
258
|
relatedRepos?: RepoMeshRelatedRepo[];
|
|
259
|
+
role?: RepoMeshDaemonRole;
|
|
209
260
|
}
|
|
210
261
|
export interface RepoMeshStatus {
|
|
211
262
|
meshId: string;
|
|
@@ -213,6 +264,7 @@ export interface RepoMeshStatus {
|
|
|
213
264
|
repoIdentity: string;
|
|
214
265
|
defaultBranch?: string;
|
|
215
266
|
refreshedAt: string;
|
|
267
|
+
meshHost?: RepoMeshHostStatus;
|
|
216
268
|
nodes: RepoMeshNodeStatus[];
|
|
217
269
|
queue?: RepoMeshQueueStatus;
|
|
218
270
|
ledger?: RepoMeshLedgerStatus;
|
|
@@ -249,16 +301,26 @@ export interface RepoMeshNodeStatus {
|
|
|
249
301
|
repoRoot?: string;
|
|
250
302
|
daemonId?: string;
|
|
251
303
|
machineId?: string;
|
|
304
|
+
role?: RepoMeshDaemonRole;
|
|
252
305
|
machineStatus?: string;
|
|
253
306
|
isLocalWorktree?: boolean;
|
|
254
307
|
worktreeBranch?: string;
|
|
255
308
|
health: RepoMeshNodeHealth;
|
|
256
309
|
git?: GitRepoStatus;
|
|
310
|
+
/**
|
|
311
|
+
* True when the selected coordinator has evidence that a peer git probe is still
|
|
312
|
+
* in flight or just timed out during initial mesh handshake, so callers should
|
|
313
|
+
* treat missing git data as pending instead of authoritative absence.
|
|
314
|
+
*/
|
|
315
|
+
gitProbePending?: boolean;
|
|
257
316
|
providers: string[];
|
|
258
317
|
activeSessions: string[];
|
|
259
318
|
activeSessionDetails?: RepoMeshSessionStatus[];
|
|
260
319
|
providerPriority?: string[];
|
|
261
320
|
launchReady?: boolean;
|
|
321
|
+
worktreeBootstrap?: LocalMeshNodeEntry['worktreeBootstrap'];
|
|
322
|
+
launchBlockedReason?: string;
|
|
323
|
+
launchBlockedMessage?: string;
|
|
262
324
|
lastSeenAt?: string;
|
|
263
325
|
updatedAt?: string;
|
|
264
326
|
connection?: RepoMeshPeerConnectionStatus;
|
|
@@ -346,3 +408,8 @@ export interface RepoMeshLedgerStatus {
|
|
|
346
408
|
entries: RepoMeshLedgerEntryStatus[];
|
|
347
409
|
summary: RepoMeshLedgerSummaryStatus;
|
|
348
410
|
}
|
|
411
|
+
export interface MeshAsyncJobLifecycle {
|
|
412
|
+
startedAt?: string;
|
|
413
|
+
completedAt?: string;
|
|
414
|
+
error?: string;
|
|
415
|
+
}
|
package/dist/shared-types.d.ts
CHANGED
|
@@ -49,6 +49,13 @@ export interface ReadChatSyncResult {
|
|
|
49
49
|
message: string;
|
|
50
50
|
buttons: string[];
|
|
51
51
|
} | null;
|
|
52
|
+
/**
|
|
53
|
+
* Chat source provenance from ChatSourceMachine (A2). Carries the
|
|
54
|
+
* selected source, transition cause, lock state, and legacy
|
|
55
|
+
* fallbackReason — opaque to the daemon-core, consumed by web-core
|
|
56
|
+
* for the source debug badge and SourceTimeline (A3).
|
|
57
|
+
*/
|
|
58
|
+
messageSource?: Record<string, unknown>;
|
|
52
59
|
}
|
|
53
60
|
export interface ProviderSummaryItem {
|
|
54
61
|
id: string;
|
|
@@ -292,6 +299,11 @@ export interface SessionEntry {
|
|
|
292
299
|
seenCompletionMarker?: string;
|
|
293
300
|
surfaceHidden?: boolean;
|
|
294
301
|
settings?: Record<string, any>;
|
|
302
|
+
/** Set when this session is acting as a mesh coordinator for the given mesh. */
|
|
303
|
+
coordinator?: {
|
|
304
|
+
meshId: string;
|
|
305
|
+
role: 'coordinator';
|
|
306
|
+
};
|
|
295
307
|
meshQueueStats?: {
|
|
296
308
|
total?: number;
|
|
297
309
|
active?: number;
|
|
@@ -46,6 +46,8 @@ export declare class DaemonStatusReporter {
|
|
|
46
46
|
private lastStatusSentAt;
|
|
47
47
|
private statusPendingThrottle;
|
|
48
48
|
private lastP2PStatusHash;
|
|
49
|
+
private lastP2PStatusSentAt;
|
|
50
|
+
private p2pDebounceTimer;
|
|
49
51
|
private lastServerStatusHash;
|
|
50
52
|
private lastStatusSummary;
|
|
51
53
|
private statusTimer;
|
package/dist/types.d.ts
CHANGED
|
@@ -36,6 +36,11 @@ export interface ChatMessage {
|
|
|
36
36
|
index?: number;
|
|
37
37
|
timestamp?: number;
|
|
38
38
|
receivedAt?: number;
|
|
39
|
+
/** (A2.3) Monotonic sequence number per (session, source). Producer-asserted
|
|
40
|
+
* when emitted by v2-aware providers (transcript v2 schema); otherwise
|
|
41
|
+
* derived by the daemon from receivedAt/timestamp/index. ChatSourceMachine
|
|
42
|
+
* uses this as the native peak watermark for regression detection. */
|
|
43
|
+
sequence?: number;
|
|
39
44
|
_turnKey?: string;
|
|
40
45
|
/** Tool calls associated with this message */
|
|
41
46
|
toolCalls?: ToolCallInfo[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.151",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "tsup && npm run build:types",
|
|
29
29
|
"build:types": "tsc -p tsconfig.json --emitDeclarationOnly --declaration --declarationMap false",
|
|
30
|
-
"dev": "tsup --watch",
|
|
30
|
+
"dev": "tsup --watch --no-clean",
|
|
31
31
|
"test": "vitest run",
|
|
32
32
|
"test:watch": "vitest",
|
|
33
33
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
@@ -49,6 +49,9 @@
|
|
|
49
49
|
"@adhdev/session-host-core": "*",
|
|
50
50
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
51
51
|
"@xterm/xterm": "^6.0.0",
|
|
52
|
+
"ajv": "^8.20.0",
|
|
53
|
+
"ajv-formats": "^3.0.1",
|
|
54
|
+
"better-sqlite3": "^12.10.0",
|
|
52
55
|
"chalk": "^5.3.0",
|
|
53
56
|
"chokidar": "^4.0.3",
|
|
54
57
|
"conf": "^13.0.0",
|
|
@@ -60,6 +63,7 @@
|
|
|
60
63
|
"@adhdev/ghostty-vt-node": "*"
|
|
61
64
|
},
|
|
62
65
|
"devDependencies": {
|
|
66
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
63
67
|
"@types/js-yaml": "^4.0.9",
|
|
64
68
|
"@types/node": "^22.0.0",
|
|
65
69
|
"@types/ws": "^8.18.1",
|
|
@@ -37,6 +37,9 @@ import type { PtyTransportFactory } from '../cli-adapters/pty-transport.js';
|
|
|
37
37
|
import type { IdeProviderInstance } from '../providers/ide-provider-instance.js';
|
|
38
38
|
import { createDefaultGitCommandServices } from '../git/git-commands.js';
|
|
39
39
|
import { setupMeshEventForwarding } from '../mesh/mesh-events.js';
|
|
40
|
+
import { loadMeshCoordinatorRegistry } from '../mesh/coordinator-registry.js';
|
|
41
|
+
import { applyProcessHardening } from './process-hardening.js';
|
|
42
|
+
import { installProviderProcessShim } from '../providers/sdk/v1/sandbox/require-whitelist.js';
|
|
40
43
|
|
|
41
44
|
// ─── Init Config ───
|
|
42
45
|
|
|
@@ -129,8 +132,16 @@ export interface DaemonDevSupportOptions {
|
|
|
129
132
|
* 8. Start instance ticking
|
|
130
133
|
*/
|
|
131
134
|
export async function initDaemonComponents(config: DaemonInitConfig): Promise<DaemonComponents> {
|
|
135
|
+
// 0. Process-level hardening (must run before any provider script is loaded).
|
|
136
|
+
// Freezes built-in prototypes (Object/Array/Function/String/Number/Boolean/Promise)
|
|
137
|
+
// and shadows process.exit/kill/abort/binding/dlopen so provider-script callers
|
|
138
|
+
// throw instead of killing the daemon. See ./process-hardening.ts for details.
|
|
139
|
+
applyProcessHardening();
|
|
140
|
+
installProviderProcessShim();
|
|
141
|
+
|
|
132
142
|
// 1. Global log interceptor
|
|
133
143
|
installGlobalInterceptor();
|
|
144
|
+
loadMeshCoordinatorRegistry();
|
|
134
145
|
|
|
135
146
|
// 2. ProviderLoader (provider source mode from config.json)
|
|
136
147
|
const appConfig = loadConfig();
|
|
@@ -142,16 +153,11 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
142
153
|
userDir: appConfig.providerDir,
|
|
143
154
|
});
|
|
144
155
|
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
} catch (e: any) {
|
|
151
|
-
LOG.warn('Provider', `⚠ Failed to fetch providers: ${e?.message}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
156
|
+
// Boot-time auto-sync is intentionally disabled. The user picks which
|
|
157
|
+
// providers to install via the dashboard onboarding / Providers tab; the
|
|
158
|
+
// daemon ships empty and only contains what the user explicitly installs.
|
|
159
|
+
// Manual sync is still available via the install / check_provider_updates
|
|
160
|
+
// commands (and the REST endpoint at /api/v1/providers/updates).
|
|
155
161
|
providerLoader.loadAll();
|
|
156
162
|
providerLoader.registerToDetector();
|
|
157
163
|
|
|
@@ -309,6 +315,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
309
315
|
statusInstanceId: config.statusInstanceId,
|
|
310
316
|
statusVersion: config.statusVersion,
|
|
311
317
|
getMeshPeerConnectionStatus: config.getMeshPeerConnectionStatus,
|
|
318
|
+
dispatchMeshCommand: config.dispatchMeshCommand,
|
|
312
319
|
getCdpLogFn: config.getCdpLogFn || ((ideType: string) => LOG.forComponent(`CDP:${ideType}`).asLogFn()),
|
|
313
320
|
});
|
|
314
321
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-level hardening — boot-time mitigations against provider scripts.
|
|
3
|
+
*
|
|
4
|
+
* Provider scripts run in-process (see providers/sdk/v1/sandbox/README-design.ts).
|
|
5
|
+
* The require() whitelist (sandbox/require-whitelist.ts) blocks the obvious
|
|
6
|
+
* attack surfaces (no `child_process.exec`, no `fs.writeFile`, no `net`, etc.)
|
|
7
|
+
* but a malicious or buggy script can still:
|
|
8
|
+
*
|
|
9
|
+
* 1. Pollute Object.prototype to influence the daemon's downstream consumers
|
|
10
|
+
* (e.g. `Object.prototype.toString = () => '...'` makes every untagged
|
|
11
|
+
* object stringify to that value across the whole process).
|
|
12
|
+
* 2. Mutate Array/Function/String prototypes for similar effect.
|
|
13
|
+
* 3. Call `process.exit()` / `process.kill()` to kill the daemon.
|
|
14
|
+
*
|
|
15
|
+
* Sandbox isolation (isolated-vm) is the proper fix but has a real perf cost
|
|
16
|
+
* and a real marshalling-complexity cost. As a cheaper layer we just freeze
|
|
17
|
+
* the built-in prototypes at boot, which is irreversible and catches every
|
|
18
|
+
* prototype-pollution variant for the lifetime of the process.
|
|
19
|
+
*
|
|
20
|
+
* `applyProcessHardening()` is idempotent — calling it twice is a no-op the
|
|
21
|
+
* second time. The function intentionally swallows any "cannot redefine
|
|
22
|
+
* property" errors that would arise if some upstream code already froze
|
|
23
|
+
* the same prototype: the post-condition (prototype is frozen) is satisfied
|
|
24
|
+
* either way.
|
|
25
|
+
*
|
|
26
|
+
* Trade-offs:
|
|
27
|
+
* - Read-only access to prototype methods (`Object.prototype.hasOwnProperty.call`,
|
|
28
|
+
* `Array.prototype.slice.call`, etc.) is unaffected — only writes throw.
|
|
29
|
+
* - Some older npm libraries patch Array.prototype or Function.prototype at
|
|
30
|
+
* import time. If a daemon-core dep does this, the import will throw. The
|
|
31
|
+
* daemon-core vitest suite is the canonical regression check for this.
|
|
32
|
+
* - We deliberately do NOT freeze RegExp.prototype, Error.prototype, Map/Set
|
|
33
|
+
* prototypes, or Date.prototype because at least one of those tends to be
|
|
34
|
+
* mutated by mainstream test/runtime libs and the value of freezing them
|
|
35
|
+
* is low (no useful prototype-pollution attack surface).
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
let _hardened = false;
|
|
39
|
+
|
|
40
|
+
/** Prototypes that get frozen at boot. */
|
|
41
|
+
const HARDENED_PROTOS: Array<{ name: string; proto: object }> = [
|
|
42
|
+
{ name: 'Object', proto: Object.prototype },
|
|
43
|
+
{ name: 'Array', proto: Array.prototype },
|
|
44
|
+
{ name: 'Function', proto: Function.prototype },
|
|
45
|
+
{ name: 'String', proto: String.prototype },
|
|
46
|
+
{ name: 'Number', proto: Number.prototype },
|
|
47
|
+
{ name: 'Boolean', proto: Boolean.prototype },
|
|
48
|
+
{ name: 'Promise', proto: Promise.prototype },
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Freeze the canonical built-in prototypes so provider scripts (and anything
|
|
53
|
+
* else running in this process) cannot mutate them. Idempotent.
|
|
54
|
+
*
|
|
55
|
+
* Returns the list of prototype names that were newly frozen — primarily for
|
|
56
|
+
* tests / observability. Already-frozen prototypes are not re-reported.
|
|
57
|
+
*/
|
|
58
|
+
export function applyProcessHardening(): string[] {
|
|
59
|
+
if (_hardened) return [];
|
|
60
|
+
_hardened = true;
|
|
61
|
+
|
|
62
|
+
const newlyFrozen: string[] = [];
|
|
63
|
+
for (const entry of HARDENED_PROTOS) {
|
|
64
|
+
if (Object.isFrozen(entry.proto)) continue;
|
|
65
|
+
try {
|
|
66
|
+
Object.freeze(entry.proto);
|
|
67
|
+
newlyFrozen.push(entry.name);
|
|
68
|
+
} catch {
|
|
69
|
+
// Some host/runtime combinations refuse to freeze a built-in
|
|
70
|
+
// prototype (e.g. it has a non-configurable accessor). We treat
|
|
71
|
+
// this as best-effort — log nothing here (logger not guaranteed
|
|
72
|
+
// to be initialized this early) and move on. Subsequent reads
|
|
73
|
+
// of `Object.isFrozen(proto)` will reveal the gap.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return newlyFrozen;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** For tests only — clears the internal "already hardened" flag. The
|
|
80
|
+
* prototypes themselves cannot be un-frozen, so this is mostly useful for
|
|
81
|
+
* testing the idempotency guard. */
|
|
82
|
+
export function _resetProcessHardeningForTest(): void {
|
|
83
|
+
_hardened = false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** For tests only — peek at the hardened state. */
|
|
87
|
+
export function _isProcessHardened(): boolean {
|
|
88
|
+
return _hardened;
|
|
89
|
+
}
|