/** * Map an arbitrary id into a filesystem-safe path segment (DEC-062). * * V1 ids use `:` as a separator (e.g. `nego:codex`, `claude:proj-1`, * `sess:abc123`). On Windows, `:` is the drive-letter separator and is * forbidden inside path components — any `mkdir /negotiations/nego:codex` * ENOENTs. The same applies to `/`, `\`, `<`, `>`, `"`, `|`, `?`, `*` * which are reserved by Windows. * * The mapping is `[:\\/<>"|?*]+` → `__`. It is deterministic and lossy * (we never need to recover the original id from the path segment; * lookups go id → path, never path → id; on-disk artefacts always * carry the original id in their JSON body). * * Empty input is mapped to `_` so we never produce an empty path * segment. */ export declare function safePathSegment(id: string): string; export interface LocalStorePaths { root: string; registry: string; instances: string; keys: string; subagents: string; subagentAudit: string; offboard: string; orgMembership: string; contracts: string; policies: string; engagements: string; artifacts: string; negotiations: string; inbox: string; outbox: string; presence: string; drumbeat: string; blockage: string; escalation: string; } export declare function localStorePaths(root: string): LocalStorePaths; export declare function presenceFile(paths: LocalStorePaths, sessionId: string): string; export declare function negotiationDir(paths: LocalStorePaths, negotiationId: string): string; export declare function negotiationJournalFile(paths: LocalStorePaths, negotiationId: string): string; /** * Canonicalize an addressable instance/channel handle so addressing is * **case-insensitive** and **label-slug-stable**. * * The footgun this closes: `deriveInstanceId` builds `host:slugify(label):uuid` * (the label is lowercased + slugified), but inbox dirs were keyed on the RAW * handle via `safePathSegment`. So a sender addressing `claude:matchID` wrote to * `inbox/claude__matchID` while the agent (registered as `claude:matchid:…`) * read `inbox/claude__matchid` — the message was silently lost. Routing handles * through this first makes both forms resolve to the one canonical inbox. * * `host:label[:uuid…]` → `lower(host):slugify(label)[:lower(uuid…)]`. A bare * token with no `:` (not an instance handle) is returned unchanged. */ export declare function canonicalAddress(addr: string): string; /** * True iff `addr` is host-qualified (`host:label[:uuid…]`, optionally a * subagent `…~name`): a non-empty host segment AND a non-empty label segment. * A bare token with no ":" (e.g. "radar-immobilier"), ":label" (empty host) or * "host:" (empty label) is NOT addressable — it mis-routes to an orphan inbox. */ export declare function isHostQualifiedAddress(addr: string): boolean; /** * Throws a user-facing Error if `addr` is not host-qualified. Message names * the fix. Used to reject bare-label sends (e.g. "radar-immobilier" → must be * "claude:radar-immobilier"): the same label can exist on several hosts. */ export declare function assertHostQualifiedAddress(addr: string, role?: string): void; /** Inbox dir for an actor, keyed on its **canonical** handle (case-folded). */ export declare function inboxDir(paths: LocalStorePaths, actor: string): string; /** * Inbox dir keyed on the RAW (pre-canonicalization) handle — read-only fallback * so envelopes deposited before the case-fold fix are still recovered. Writes * always go to the canonical dir; these raw dirs drain as messages are popped. */ export declare function inboxDirRaw(paths: LocalStorePaths, actor: string): string; export declare function outboxDir(paths: LocalStorePaths, actor: string): string; /** Outbox dir keyed on the RAW handle — read-only fallback for pre-case-fold copies. */ export declare function outboxDirRaw(paths: LocalStorePaths, actor: string): string; /** * The outcome of resolving a send target BEFORE depositing (WP-2). * * - `deliver` — full id / subagent / an upstream-rejected bare label. * - `deliver-dormant` — bare alias, 0 live but ≥1 registered instance exists. * - `deliver-hint` — bare alias with exactly 1 live full-id match; deposit to bare * dir (UNCHANGED destination), surface the uuid to the caller. * - `deliver-resolved` — a unique live display name, addressed as its instance. * - `list` — a read found several live candidates. * - `refuse` — phantom (0 live, 0 registered) | ambiguous write | malformed. * * SAFETY: only a unique display name is translated to its recorded instance. * Existing instance aliases retain their original destination. */ export type RecipientResolution = { kind: "deliver"; reason?: string; } | { kind: "deliver-dormant"; reason: string; } | { kind: "deliver-hint"; liveCandidate: string; reason: string; } | { kind: "deliver-resolved"; recipient: string; reason: string; } | { kind: "list"; candidates: string[]; reason: string; } | { kind: "refuse"; reason: string; candidates?: string[]; }; /** A live presence can contribute both its stable instance and display name. */ export type LiveRecipient = string | { readonly instance: string; readonly name?: string; }; /** * Resolve how a SEND to `target` should be handled. * * Does NOT deliver — the caller delivers. A unique display name resolves to * the recorded full instance; existing instance aliases keep their destination. * The `liveInstances` list comes from `listPresence(root)` so display names are * available alongside their immutable instance ids. Strings remain accepted for * callers that only need instance-id resolution. * The `registeredInstances` list comes from `store.listInstances().map(i => i.instance ?? i.id)`. */ export declare function resolveRecipient(opts: { target: string; liveInstances: readonly LiveRecipient[]; registeredInstances: readonly string[]; operation?: "read" | "write"; }): RecipientResolution; /** * Gate for REACHING paths (drumbeat relance / wake-another / remote drive). * * Unlike the inbox-put path, liveness is NOT a blocker for reaching: we * intentionally wake dormant agents. Only a `refuse` outcome (phantom, * ambiguous, malformed) should block. Wraps `resolveRecipient` so all * addressing discipline lives in ONE place. * * This is the REACH-GUARD CHOKEPOINT — the future governance/conductor gate * (clearance, conductor-liveness) hooks HERE via the `DrumbeatTickOptions` * integration in `watch.ts`, so every reach path inherits it. */ export declare function reachGuard(opts: { target: string; liveInstances: readonly LiveRecipient[]; registeredInstances: readonly string[]; }): { ok: true; } | { ok: false; reason: string; kind: string; }; //# sourceMappingURL=paths.d.ts.map