@sema-agent/core
Version:
Stateless, task-oriented AI agent core
104 lines (103 loc) • 6.01 kB
TypeScript
/** Per-segment cap, measured AFTER encoding. A conservative portability policy adopted as contract
* (research (d) recommendation — NOT a verified third-party hard limit; uuidv7 = 36 chars fits). */
export declare const SCOPE_SEGMENT_MAX_ENCODED = 64;
/**
* Canonical injective encoding for a raw identifier entering a scope key: everything outside
* `[A-Za-z0-9._-]` becomes `%XX` (UTF-8 bytes), INCLUDING `%` itself (that self-encoding is what
* makes the map injective — no `__`-style collidable escapes). Throws when the encoded segment
* exceeds {@link SCOPE_SEGMENT_MAX_ENCODED} (an over-long principal is a deployment config error)
* or when the raw value is empty.
*/
export declare function encodeScopeSegment(raw: string): string;
/**
* Inverse of {@link encodeScopeSegment}, CANONICAL-FORM ONLY: the input must be
* exactly what the encoder produces — safe chars stay bare (so `%41` for `A` is refused: two
* spellings of one identity must not mint two keys), `%XX` sequences must decode as valid UTF-8
* (fatal decoder — no U+FFFD smoothing), and the 64-char cap holds here too (parse side enforces
* what the format side promised).
*/
export declare function decodeScopeSegment(encoded: string): string;
export type ParsedScopeKey = {
kind: "user";
principal: string;
} | {
kind: "org";
tenant: string;
} | {
kind: "proj";
tenant: string;
projectId: string;
} | {
kind: "userproj";
principal: string;
projectId: string;
}
/** No recognized v2 prefix: an opaque legacy scope (fully valid under the v2 contract too). */
| {
kind: "legacy";
raw: string;
};
export declare function formatUserScope(principal: string): string;
export declare function formatOrgScope(tenant: string): string;
export declare function formatProjScope(tenant: string, projectId: string): string;
export declare function formatUserProjScope(principal: string, projectId: string): string;
/**
* Parse a scope key under the v2 contract. A key carrying a recognized prefix MUST be well-formed
* (fail-loud — a malformed `proj:` key under an explicit opt-in is a config error, not an opaque
* scope); anything without a recognized prefix parses as `{ kind: "legacy" }` and keeps today's
* opaque behavior. NEVER call this on a deployment that has not opted in (see module header).
*/
export declare function parseScopeKey(key: string): ParsedScopeKey;
/** True when a v2-parsed key belongs to a PERSONAL plane (`user:` / `userproj:`). */
export declare function isPersonalScope(parsed: ParsedScopeKey): boolean;
/** True when the key's AUTHORITY is central (design/142 §2 表: user/userproj/org rows) — these keys
* must never project into an in-repo memory dir (they would leak to every collaborator AND enter
* commit history; extended the F9 gate from personal to org). `proj:`/legacy stay
* repo-planed. */
export declare function isCentralAuthorityScope(parsed: ParsedScopeKey): boolean;
/**
* design/142 §7 S1 hard gate (复审 F9): until S1b per-scope physical roots land, ALL scopes of a
* session materialize under ONE root — so a v2 session mixing `user:*`/`userproj:*` keys with an
* IN-REPO memory dir would project personal memory into the repo (the exact §2.7 leak shape:
* personal notes committed to a shared/open repo). Fail-loud, never fail-open: this throws a
* `config.`-coded error the prepare path must NOT swallow.
*/
export declare function assertScopeContractPlacement(args: {
parsedScopes: readonly ParsedScopeKey[];
memoryDir: string;
/** The repo/workspace root the session works in (undefined = no repo context, gate passes). */
repoRoot: string | undefined;
}): void;
/** Repo-root marker file: minted once (by scaffold/deployment, NEVER by the engine), committed, and
* from then on the project's identity survives clone/copy/rename/machine/cloud-sandbox. */
export declare const PROJECT_MARKER_PATH = ".sema/project";
/** projectId 格式单源:generic UUID,version nibble 不收紧——宽读严写(读面认外部/
* 历史 v4;铸造面 formatProjectMarker / center mint 统一产 uuidv7)。center 侧自持镜像+dev-only
* drift 锁对着这条(registry-core coreTypes 先例)。 */
export declare const PROJECT_ID_REGEX: RegExp;
/** Serialize a marker file body (scaffold-side helper; the engine itself never writes one). */
export declare function formatProjectMarker(projectId: string): string;
/** Parse a marker body: TOML-subset `key = value` lines, `#` comments and blank lines ignored,
* optional quotes around the value. Unknown keys are ignored (forward-compatible).
* ANY line that starts a `projectId` assignment but is not one canonical UUID assignment is a
* CORRUPT marker (fail-loud, `config.memory_project_marker`) — an empty value, an unclosed quote,
* a non-UUID, or a DUPLICATE projectId line must never degrade to "no marker" (that would be a
* silent identity change back to the path key). Returns null only when NO projectId line exists. */
export declare function parseProjectMarker(body: string): {
projectId: string;
} | null;
/**
* design/142 §1.1 — resolve a project's identity. READS ONLY THE MARKER (复审 F2: never derives an
* identity from a git remote; remote-URL normalization is a scaffold-side minting heuristic, not an
* identity source). v1 scope (复审 F10): ONE marker at the project root —
* - inside a git repo, the root is the REPO root; a marker at a nested directory that is not the
* repo root is fail-loud (`config.memory_project_marker_nested`) — monorepo sub-project identity
* is a future need-signal, not a silent second identity;
* - outside git, the given rootDir IS the project root (path-form local projects).
* Returns null when no marker exists (callers fall through the resolution ladder: explicit
* declaration → path fallback with a warning).
*/
export declare function resolveProjectId(rootDir: string): {
projectId: string;
markerDir: string;
} | null;