@primusclaw/personal-agent-runtime 0.0.0-stage → 0.1.0
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/LICENSE +37 -0
- package/dist/index.js +5734 -0
- package/dist/sqlite/schema.sql +122 -0
- package/dist/types/claw/answers.d.ts +11 -0
- package/dist/types/claw/assistant.d.ts +27 -0
- package/dist/types/claw/client.d.ts +23 -0
- package/dist/types/claw/http.d.ts +64 -0
- package/dist/types/claw/identity-lookup.d.ts +15 -0
- package/dist/types/claw/index.d.ts +5 -0
- package/dist/types/claw/key-revoker.d.ts +22 -0
- package/dist/types/claw/remote-agents.d.ts +23 -0
- package/dist/types/claw/rule.d.ts +20 -0
- package/dist/types/claw/sessions.d.ts +56 -0
- package/dist/types/claw/settings.d.ts +46 -0
- package/dist/types/claw/sse-stream.d.ts +14 -0
- package/dist/types/claw/work-items.d.ts +22 -0
- package/dist/types/claw/zip.d.ts +15 -0
- package/dist/types/config/env.d.ts +29 -0
- package/dist/types/config/index.d.ts +3 -0
- package/dist/types/config/secrets.d.ts +21 -0
- package/dist/types/config/settings.d.ts +13 -0
- package/dist/types/context/als-request-scope.d.ts +3 -0
- package/dist/types/crypto/fernet.d.ts +37 -0
- package/dist/types/crypto/link-code.d.ts +6 -0
- package/dist/types/crypto/node-crypto.d.ts +2 -0
- package/dist/types/crypto/py-base64.d.ts +8 -0
- package/dist/types/crypto-random.d.ts +3 -0
- package/dist/types/exchange/index.d.ts +2 -0
- package/dist/types/exchange/serial.d.ts +8 -0
- package/dist/types/exchange/token-exchange.d.ts +56 -0
- package/dist/types/github/api.d.ts +44 -0
- package/dist/types/github/app-token.d.ts +46 -0
- package/dist/types/github/credential-client.d.ts +37 -0
- package/dist/types/github/device-flow.d.ts +28 -0
- package/dist/types/github/feedback-config.d.ts +26 -0
- package/dist/types/github/feedback-issues.d.ts +23 -0
- package/dist/types/github/feedback-worker.d.ts +11 -0
- package/dist/types/github/index.d.ts +7 -0
- package/dist/types/github/intent-reader.d.ts +28 -0
- package/dist/types/github/pull-reader.d.ts +22 -0
- package/dist/types/http/claw-events.d.ts +27 -0
- package/dist/types/http/link.d.ts +14 -0
- package/dist/types/http/routes.d.ts +37 -0
- package/dist/types/http/server.d.ts +24 -0
- package/dist/types/index.d.ts +29 -0
- package/dist/types/line-logger.d.ts +15 -0
- package/dist/types/logging/chat-log.d.ts +26 -0
- package/dist/types/metrics/exposition.d.ts +17 -0
- package/dist/types/metrics/index.d.ts +2 -0
- package/dist/types/metrics/prometheus-metrics.d.ts +46 -0
- package/dist/types/sqlite/approval-repository.d.ts +19 -0
- package/dist/types/sqlite/assistant-repository.d.ts +42 -0
- package/dist/types/sqlite/context.d.ts +38 -0
- package/dist/types/sqlite/database.d.ts +37 -0
- package/dist/types/sqlite/feedback-repository.d.ts +27 -0
- package/dist/types/sqlite/forget.d.ts +6 -0
- package/dist/types/sqlite/github-repository.d.ts +36 -0
- package/dist/types/sqlite/idempotency-repository.d.ts +14 -0
- package/dist/types/sqlite/link-repository.d.ts +27 -0
- package/dist/types/sqlite/member-repository.d.ts +15 -0
- package/dist/types/sqlite/migrations.d.ts +12 -0
- package/dist/types/sqlite/notify-repository.d.ts +17 -0
- package/dist/types/sqlite/observe-repository.d.ts +19 -0
- package/dist/types/sqlite/onboarding-repository.d.ts +20 -0
- package/dist/types/sqlite/open.d.ts +25 -0
- package/dist/types/sqlite/people-repository.d.ts +33 -0
- package/dist/types/sqlite/py-json.d.ts +16 -0
- package/dist/types/sqlite/reencrypt.d.ts +13 -0
- package/dist/types/sqlite/remote-repository.d.ts +24 -0
- package/dist/types/sqlite/remote-state-repository.d.ts +20 -0
- package/dist/types/sqlite/session-repository.d.ts +31 -0
- package/dist/types/sqlite/store.d.ts +67 -0
- package/dist/types/sqlite/turn-repository.d.ts +24 -0
- package/dist/types/system-clock.d.ts +3 -0
- package/package.json +34 -4
- package/README.md +0 -3
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** ApprovalRepository on SQLite (table `approvals`). Which cards are being sent right now is kept in this process only;
|
|
2
|
+
* what outlives a crash is the `card_sent` column: a card recorded and not marked sent is sent by the next process. */
|
|
3
|
+
import { type Approval, type ApprovalRepository, type ApprovalState, type NewApproval, type UnsentCard } from "@primusclaw/personal-agent";
|
|
4
|
+
import type { StoreContext } from "./context.js";
|
|
5
|
+
export declare class SqliteApprovalRepository implements ApprovalRepository {
|
|
6
|
+
#private;
|
|
7
|
+
private readonly ctx;
|
|
8
|
+
constructor(ctx: StoreContext);
|
|
9
|
+
saveApproval(approval: NewApproval): boolean;
|
|
10
|
+
approvalSent(sessionId: string, toolUseId: string): void;
|
|
11
|
+
approvalInFlight(sessionId: string, toolUseId: string): boolean;
|
|
12
|
+
forgetApproval(sessionId: string, toolUseId: string): void;
|
|
13
|
+
claimUnsentApprovals(): UnsentCard[];
|
|
14
|
+
approvalSendFailed(sessionId: string, toolUseId: string): void;
|
|
15
|
+
getApproval(sessionId: string, toolUseId: string): Approval | null;
|
|
16
|
+
closeApproval(sessionId: string, toolUseId: string, state: ApprovalState, options?: {
|
|
17
|
+
readonly onlyIfPending?: boolean;
|
|
18
|
+
}): boolean;
|
|
19
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** The assistant's cards and messages on SQLite (tables `assistant_cards`, `auto_replies`, `assistant_action_cards`,
|
|
2
|
+
* `assistant_confirm_cards`, `assistant_messages`). */
|
|
3
|
+
import { type ActionCard, type AssistantButtonRepository, type AssistantCard, type AssistantCardRepository, type AssistantMessage, type AssistantMessageRepository, type CardState, type ConfirmCard, type JsonObject, type MoveOptions, type PlacedCard } from "@primusclaw/personal-agent";
|
|
4
|
+
import { type StoreContext } from "./context.js";
|
|
5
|
+
export declare class SqliteAssistantCardRepository implements AssistantCardRepository {
|
|
6
|
+
private readonly ctx;
|
|
7
|
+
constructor(ctx: StoreContext);
|
|
8
|
+
addAssistantCard(userId: string, itemId: string, version: number, canAccept: boolean): string;
|
|
9
|
+
getAssistantCard(cardId: string): AssistantCard | null;
|
|
10
|
+
endAssistantCard(cardId: string, ended: string): boolean;
|
|
11
|
+
placeAssistantCard(cardId: string, placement: {
|
|
12
|
+
readonly activityId: string;
|
|
13
|
+
readonly reference: JsonObject;
|
|
14
|
+
readonly render: JsonObject;
|
|
15
|
+
}): void;
|
|
16
|
+
placedAssistantCards(filter: {
|
|
17
|
+
readonly itemId: string;
|
|
18
|
+
} | {
|
|
19
|
+
readonly since?: number;
|
|
20
|
+
}): PlacedCard[];
|
|
21
|
+
moveAssistantCard(cardId: string, state: CardState, options?: MoveOptions): boolean;
|
|
22
|
+
setAutoReply(userId: string, itemId: string, pull: string): void;
|
|
23
|
+
autoReply(userId: string, itemId: string): string | null;
|
|
24
|
+
clearAutoReply(userId: string, itemId: string): void;
|
|
25
|
+
}
|
|
26
|
+
export declare class SqliteAssistantButtonRepository implements AssistantButtonRepository {
|
|
27
|
+
private readonly ctx;
|
|
28
|
+
constructor(ctx: StoreContext);
|
|
29
|
+
addActionCard(userId: string, channelMessageId: string, name: string, args: string): string;
|
|
30
|
+
getActionCard(cardId: string): ActionCard | null;
|
|
31
|
+
moveActionCard(cardId: string, state: CardState, options?: MoveOptions): boolean;
|
|
32
|
+
addConfirmCard(userId: string, confirmationId: string, kind: string, label?: string): string;
|
|
33
|
+
getConfirmCard(cardId: string): ConfirmCard | null;
|
|
34
|
+
moveConfirmCard(cardId: string, state: CardState, options?: MoveOptions): boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare class SqliteAssistantMessageRepository implements AssistantMessageRepository {
|
|
37
|
+
private readonly ctx;
|
|
38
|
+
constructor(ctx: StoreContext);
|
|
39
|
+
noteAssistantMessage(channelMessageId: string, userId: string, actions: readonly string[], people?: readonly string[]): void;
|
|
40
|
+
assistantMessagePeople(channelMessageId: string): ReadonlySet<string>;
|
|
41
|
+
assistantMessage(channelMessageId: string): AssistantMessage | null;
|
|
42
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** What every repository of the SQLite store shares: the connection, the clock, randomness and the stored-secret keys. */
|
|
2
|
+
import type { Random } from "@primusclaw/personal-agent";
|
|
3
|
+
import { Fernet, MultiFernet } from "../crypto/fernet.js";
|
|
4
|
+
import type { Database } from "./database.js";
|
|
5
|
+
/** The fields a security event of the store carries (`link_attempt`, `link_lockout`); the sink filters and logs them. */
|
|
6
|
+
export type SecurityEventFields = Readonly<Record<string, string | number | undefined>>;
|
|
7
|
+
export type SecurityEventSink = (event: "link_attempt" | "link_lockout", fields: SecurityEventFields) => void;
|
|
8
|
+
/**
|
|
9
|
+
* The keys of TEAMS_ENCRYPTION_KEYS: the first encrypts and hashes new link codes, every one decrypts and finds a code
|
|
10
|
+
* (adding a key at the head does not void the codes of the last 10 minutes).
|
|
11
|
+
*/
|
|
12
|
+
export declare class StoreSecrets {
|
|
13
|
+
#private;
|
|
14
|
+
readonly primary: Fernet;
|
|
15
|
+
readonly all: MultiFernet;
|
|
16
|
+
constructor(keys: readonly string[], nowSeconds?: () => number);
|
|
17
|
+
/** A Fernet token as the store keeps it (a BLOB of the token's ASCII). */
|
|
18
|
+
encrypt(text: string): Uint8Array;
|
|
19
|
+
/** The text, or null when no configured key reads it. */
|
|
20
|
+
decrypt(token: Uint8Array): string | null;
|
|
21
|
+
/** The code's stored hash under the first key. */
|
|
22
|
+
primaryHash(code: string): string;
|
|
23
|
+
/** The code's hash under every key. */
|
|
24
|
+
hashes(code: string): string[];
|
|
25
|
+
}
|
|
26
|
+
export interface StoreContext {
|
|
27
|
+
readonly db: Database;
|
|
28
|
+
/** Epoch seconds. */
|
|
29
|
+
readonly now: () => number;
|
|
30
|
+
readonly random: Random;
|
|
31
|
+
readonly secrets: StoreSecrets;
|
|
32
|
+
}
|
|
33
|
+
/** Python's `secrets.token_urlsafe(n)`: n random bytes, base64url without padding. */
|
|
34
|
+
export declare function tokenUrlsafe(random: Random, n: number): string;
|
|
35
|
+
/** Python's `secrets.token_hex(n)`. */
|
|
36
|
+
export declare function tokenHex(random: Random, n: number): string;
|
|
37
|
+
/** `n` characters drawn uniformly from `alphabet` (Python's `secrets.choice` per character). */
|
|
38
|
+
export declare function drawCode(random: Random, alphabet: string, n: number): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one SQLite connection a store works on, with typed statement helpers. Opened as the Python worker opens it:
|
|
3
|
+
* autocommit (`isolation_level=None`), WAL, and a 5 s busy timeout (`sqlite3.connect`'s default).
|
|
4
|
+
*/
|
|
5
|
+
import { type SQLInputValue } from "node:sqlite";
|
|
6
|
+
export type SqlValue = SQLInputValue;
|
|
7
|
+
export type Row = Readonly<Record<string, SqlValue>>;
|
|
8
|
+
/** Prepared statements are cached per SQL text; the store's statements are a closed set. */
|
|
9
|
+
export declare class Database {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(path: string);
|
|
12
|
+
/** Several statements, no parameters (DDL). */
|
|
13
|
+
exec(sql: string): void;
|
|
14
|
+
/** Rows changed. */
|
|
15
|
+
run(sql: string, ...params: readonly SqlValue[]): number;
|
|
16
|
+
/** The rowid of the row an INSERT made. */
|
|
17
|
+
insert(sql: string, ...params: readonly SqlValue[]): number;
|
|
18
|
+
get(sql: string, ...params: readonly SqlValue[]): Row | undefined;
|
|
19
|
+
all(sql: string, ...params: readonly SqlValue[]): Row[];
|
|
20
|
+
/** The column names of `table` (`PRAGMA table_info`). */
|
|
21
|
+
columns(table: string): Set<string>;
|
|
22
|
+
/** Idempotent. */
|
|
23
|
+
close(): void;
|
|
24
|
+
private statement;
|
|
25
|
+
}
|
|
26
|
+
/** Typed reads of a column; a value of another type is a corrupt row, not a case to handle. */
|
|
27
|
+
export declare const col: {
|
|
28
|
+
str: (row: Row, name: string) => string;
|
|
29
|
+
strOrNull: (row: Row, name: string) => string | null;
|
|
30
|
+
num: (row: Row, name: string) => number;
|
|
31
|
+
numOrNull: (row: Row, name: string) => number | null;
|
|
32
|
+
/** Python's truthiness of the value. */
|
|
33
|
+
bool(row: Row, name: string): boolean;
|
|
34
|
+
bytes(row: Row, name: string): Uint8Array;
|
|
35
|
+
};
|
|
36
|
+
/** SQLite's integer for a flag. */
|
|
37
|
+
export declare const flag: (value: boolean) => number;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** FeedbackRepository on SQLite (tables `feedback_reports`, `feedback_asks`, `feedback_offers`, `feedback_prompts`). */
|
|
2
|
+
import { type FeedbackOffer, type FeedbackOfferState, type FeedbackPrompt, type FeedbackReport, type FeedbackRepository, type IssueRef, type NewFeedbackReport } from "@primusclaw/personal-agent";
|
|
3
|
+
import { type StoreContext } from "./context.js";
|
|
4
|
+
import { type Database } from "./database.js";
|
|
5
|
+
/** Delivered reports past FEEDBACK_KEEP_SECONDS go (undelivered ones are never dropped). */
|
|
6
|
+
export declare function pruneFiledReports(db: Database, now: number): void;
|
|
7
|
+
export declare class SqliteFeedbackRepository implements FeedbackRepository {
|
|
8
|
+
private readonly ctx;
|
|
9
|
+
constructor(ctx: StoreContext);
|
|
10
|
+
addFeedbackReport(r: NewFeedbackReport): number;
|
|
11
|
+
getFeedbackReport(reportId: number): FeedbackReport | null;
|
|
12
|
+
feedbackTarget(r: FeedbackReport): IssueRef | null;
|
|
13
|
+
markFeedbackFiled(reportId: number, issue: IssueRef, asComment: boolean): void;
|
|
14
|
+
markFeedbackFailed(reportId: number, error: string, retryAt: number): void;
|
|
15
|
+
dueFeedback(limit: number): FeedbackReport[];
|
|
16
|
+
pendingFeedbackCount(): number;
|
|
17
|
+
openFeedbackAsk(userId: string, scope: string): void;
|
|
18
|
+
takeFeedbackAsk(userId: string, scope: string): boolean;
|
|
19
|
+
hasFeedbackAsk(userId: string, scope: string): boolean;
|
|
20
|
+
addFeedbackOffer(userId: string, issueNumber: number, sessionId: string, turnId: string): string;
|
|
21
|
+
getFeedbackOffer(offerId: string): FeedbackOffer | null;
|
|
22
|
+
moveFeedbackOffer(offerId: string, state: FeedbackOfferState, options?: {
|
|
23
|
+
readonly onlyFrom?: FeedbackOfferState | null;
|
|
24
|
+
}): boolean;
|
|
25
|
+
addFeedbackPrompt(userId: string, scope: string): string;
|
|
26
|
+
getFeedbackPrompt(promptId: string): FeedbackPrompt | null;
|
|
27
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Deletions that more than one repository performs (a re-link, an unlink): one definition each. */
|
|
2
|
+
import type { Database } from "./database.js";
|
|
3
|
+
/** The person's notification subscription and its attempt record. */
|
|
4
|
+
export declare function forgetNotifySub(db: Database, userId: string): void;
|
|
5
|
+
/** Everything /repo and /sessions remember about the person. */
|
|
6
|
+
export declare function forgetRepos(db: Database, userId: string): void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** GitHub review delegation on SQLite (tables `github_confirmations`, `acting_sessions`, `review_notices`,
|
|
2
|
+
* `asker_follows`, `github_notices`). */
|
|
3
|
+
import { type AskerFollow, type ConfirmationState, type GithubConfirmation, type GithubConfirmationRepository, type JsonObject, type NewGithubConfirmation, type ReviewDelegationRepository, type ReviewNotice } from "@primusclaw/personal-agent";
|
|
4
|
+
import type { StoreContext } from "./context.js";
|
|
5
|
+
export declare class SqliteGithubConfirmationRepository implements GithubConfirmationRepository {
|
|
6
|
+
private readonly ctx;
|
|
7
|
+
constructor(ctx: StoreContext);
|
|
8
|
+
addGithubConfirmation(c: NewGithubConfirmation): boolean;
|
|
9
|
+
getGithubConfirmation(confirmationId: string): GithubConfirmation | null;
|
|
10
|
+
pendingGithubConfirmations(actorId: string): GithubConfirmation[];
|
|
11
|
+
moveGithubConfirmation(confirmationId: string, state: ConfirmationState, options?: {
|
|
12
|
+
readonly onlyFrom?: ConfirmationState;
|
|
13
|
+
}): boolean;
|
|
14
|
+
forgetGithubConfirmation(confirmationId: string): void;
|
|
15
|
+
}
|
|
16
|
+
export declare class SqliteReviewDelegationRepository implements ReviewDelegationRepository {
|
|
17
|
+
private readonly ctx;
|
|
18
|
+
constructor(ctx: StoreContext);
|
|
19
|
+
noteActingSession(sessionId: string, requesterId: string, actorId: string): void;
|
|
20
|
+
isActingSession(sessionId: string): boolean;
|
|
21
|
+
noteReviewNotice(n: ReviewNotice): void;
|
|
22
|
+
reviewNotices(): ReviewNotice[];
|
|
23
|
+
addAskerFollow(f: {
|
|
24
|
+
readonly itemId: string;
|
|
25
|
+
readonly batchId: string;
|
|
26
|
+
readonly review: JsonObject;
|
|
27
|
+
readonly reference: JsonObject;
|
|
28
|
+
readonly deadline: number;
|
|
29
|
+
}): void;
|
|
30
|
+
askerFollows(): AskerFollow[];
|
|
31
|
+
askerFollowSaid(itemId: string, said: string): void;
|
|
32
|
+
askerFollowReviewerTold(itemId: string): void;
|
|
33
|
+
dropAskerFollow(itemId: string): void;
|
|
34
|
+
claimGithubNotice(userId: string, kind: string, interval: number): boolean;
|
|
35
|
+
releaseGithubNotice(userId: string, kind: string): void;
|
|
36
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** IdempotencyRepository on SQLite (tables `processed`, `event_claims`). A claim is one statement on the store, so of
|
|
2
|
+
* any number of copies, in this process or another on the same file, one is sent. */
|
|
3
|
+
import { type EventClaim, type IdempotencyRepository } from "@primusclaw/personal-agent";
|
|
4
|
+
import { type StoreContext } from "./context.js";
|
|
5
|
+
export declare class SqliteIdempotencyRepository implements IdempotencyRepository {
|
|
6
|
+
private readonly ctx;
|
|
7
|
+
constructor(ctx: StoreContext);
|
|
8
|
+
claimProcessed(activityId: string): boolean;
|
|
9
|
+
claimEvent(key: string): EventClaim;
|
|
10
|
+
finishEvent(key: string): void;
|
|
11
|
+
releaseEvent(key: string, holder: string): void;
|
|
12
|
+
seen(activityId: string): boolean;
|
|
13
|
+
markProcessed(activityId: string): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** LinkRepository on SQLite (tables `links`, `pending_keys`, `link_attempts`). */
|
|
2
|
+
import { type LinkLimits, type LinkRepository, type RedeemOutcome } from "@primusclaw/personal-agent";
|
|
3
|
+
import { type StoreContext, type SecurityEventSink } from "./context.js";
|
|
4
|
+
export declare class SqliteLinkRepository implements LinkRepository {
|
|
5
|
+
private readonly ctx;
|
|
6
|
+
private readonly limits;
|
|
7
|
+
private readonly securityEvent;
|
|
8
|
+
constructor(ctx: StoreContext, limits: LinkLimits, securityEvent: SecurityEventSink);
|
|
9
|
+
get linkWindowSeconds(): number;
|
|
10
|
+
stashPending(key: string): string;
|
|
11
|
+
redeemPending(userId: string, name: string, code: string, activityId?: string): RedeemOutcome;
|
|
12
|
+
/** The check, the spend and the count, as one step: two requests racing for one code bind it once. */
|
|
13
|
+
private judge;
|
|
14
|
+
private spend;
|
|
15
|
+
/** A re-link may be another Claw account: its subscription, sessions and repositories must be its own. The wrong
|
|
16
|
+
* codes stay counted until the window ends, or minting a code after every few guesses would make the limit none. */
|
|
17
|
+
private bind;
|
|
18
|
+
takeExpiredPending(): string[];
|
|
19
|
+
getKey(userId: string): string | null;
|
|
20
|
+
unlink(userId: string): void;
|
|
21
|
+
setClawUserId(userId: string, clawUserId: string): void;
|
|
22
|
+
needsClawUserId(userId: string): boolean;
|
|
23
|
+
clawUserIdOf(userId: string): string | null;
|
|
24
|
+
linkedUsers(): string[];
|
|
25
|
+
userForClawUser(clawUserId: string): string | null;
|
|
26
|
+
usersForClawUser(clawUserId: string): string[];
|
|
27
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** MemberDirectoryRepository on SQLite (tables `teams_users`, `personal_clicks`). */
|
|
2
|
+
import type { KnownMember, MemberDirectoryRepository, MemberListEntry } from "@primusclaw/personal-agent";
|
|
3
|
+
import type { StoreContext } from "./context.js";
|
|
4
|
+
export declare class SqliteMemberDirectoryRepository implements MemberDirectoryRepository {
|
|
5
|
+
private readonly ctx;
|
|
6
|
+
constructor(ctx: StoreContext);
|
|
7
|
+
rememberMember(channelUserId: string, userId: string, name: string, email?: string): void;
|
|
8
|
+
rememberMemberEmails(members: readonly MemberListEntry[]): void;
|
|
9
|
+
knownMembers(limit?: number): KnownMember[];
|
|
10
|
+
userForChannelId(channelUserId: string): string | null;
|
|
11
|
+
channelIdOf(userId: string): string | null;
|
|
12
|
+
memberName(userId: string): string | null;
|
|
13
|
+
notePersonalClick(userId: string): void;
|
|
14
|
+
clickedPersonally(userId: string): boolean;
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Database } from "./database.js";
|
|
2
|
+
/** The DDL, as Python's `_SCHEMA`. */
|
|
3
|
+
export declare const SCHEMA_SQL: string;
|
|
4
|
+
/** table -> [column, declaration], in the order the Python worker adds them. */
|
|
5
|
+
export declare const ADDED_COLUMNS: readonly (readonly [string, readonly (readonly [string, string])[]])[];
|
|
6
|
+
/**
|
|
7
|
+
* The sessions table used to be keyed by the person alone. It is now keyed by (person, scope): scope '' is the 1:1
|
|
8
|
+
* chat. Existing rows become the 1:1 session; plugin_id '' means created without one.
|
|
9
|
+
*/
|
|
10
|
+
export declare function migrateSessions(db: Database): void;
|
|
11
|
+
/** The whole start-up migration; idempotent. */
|
|
12
|
+
export declare function migrate(db: Database): void;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** NotifySubscriptionRepository on SQLite (tables `notify_subs`, `notify_sub_attempts`). */
|
|
2
|
+
import type { NotifyRoute, NotifySubscription, NotifySubscriptionRepository } from "@primusclaw/personal-agent";
|
|
3
|
+
import type { StoreContext } from "./context.js";
|
|
4
|
+
export declare class SqliteNotifySubscriptionRepository implements NotifySubscriptionRepository {
|
|
5
|
+
private readonly ctx;
|
|
6
|
+
constructor(ctx: StoreContext);
|
|
7
|
+
saveNotifySub(userId: string, sub: NotifySubscription & {
|
|
8
|
+
readonly secret: string;
|
|
9
|
+
readonly typesVersion?: number;
|
|
10
|
+
}): void;
|
|
11
|
+
notifyTypesVersion(userId: string): number | null;
|
|
12
|
+
setNotifyTypesVersion(userId: string, typesVersion: number): void;
|
|
13
|
+
getNotifySub(userId: string): NotifySubscription | null;
|
|
14
|
+
notifySubByRoute(route: string): NotifyRoute | null;
|
|
15
|
+
forgetNotifySub(userId: string): void;
|
|
16
|
+
claimNotifyAttempt(userId: string, minInterval: number): boolean;
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** ObserveRepository on SQLite (tables `observed_groups`, `observe_cards`). */
|
|
2
|
+
import { type GroupObserver, type JsonObject, type NewObserveCard, type ObserveCard, type ObserveCardState, type ObserveRepository, type ObservedGroup } from "@primusclaw/personal-agent";
|
|
3
|
+
import { type StoreContext } from "./context.js";
|
|
4
|
+
export declare class SqliteObserveRepository implements ObserveRepository {
|
|
5
|
+
private readonly ctx;
|
|
6
|
+
constructor(ctx: StoreContext);
|
|
7
|
+
observeGroup(conversationId: string, observer: GroupObserver & {
|
|
8
|
+
readonly reference: JsonObject;
|
|
9
|
+
readonly name?: string;
|
|
10
|
+
}): void;
|
|
11
|
+
unobserveGroup(conversationId: string): string | null;
|
|
12
|
+
groupObserver(conversationId: string): GroupObserver | null;
|
|
13
|
+
observedBy(userId: string): ObservedGroup[];
|
|
14
|
+
observedReference(conversationId: string): JsonObject | null;
|
|
15
|
+
addObserveCard(card: NewObserveCard): string;
|
|
16
|
+
getObserveCard(cardId: string): ObserveCard | null;
|
|
17
|
+
observeSuggestionSettled(userId: string, runId: string, seq: number): boolean;
|
|
18
|
+
moveObserveCard(cardId: string, state: ObserveCardState, onlyFrom?: ObserveCardState): boolean;
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** OnboardingRepository on SQLite (tables `onboarding_cards`, `onboarding_dms`, `pending_requests`). */
|
|
2
|
+
import { type NewPendingRequest, type OnboardingCard, type OnboardingRepository, type PendingRequest } from "@primusclaw/personal-agent";
|
|
3
|
+
import { type StoreContext } from "./context.js";
|
|
4
|
+
import { type Database } from "./database.js";
|
|
5
|
+
/** Requests of people who did not link in time go. */
|
|
6
|
+
export declare function pruneExpiredRequests(db: Database, now: number): void;
|
|
7
|
+
export declare class SqliteOnboardingRepository implements OnboardingRepository {
|
|
8
|
+
private readonly ctx;
|
|
9
|
+
constructor(ctx: StoreContext);
|
|
10
|
+
claimOnboardingCard(userId: string, scope: string, member: {
|
|
11
|
+
readonly channelUserId: string;
|
|
12
|
+
readonly name: string;
|
|
13
|
+
}): string | null;
|
|
14
|
+
releaseOnboardingCard(promptId: string): void;
|
|
15
|
+
getOnboardingCard(promptId: string): OnboardingCard | null;
|
|
16
|
+
claimOnboardingDm(userId: string): boolean;
|
|
17
|
+
releaseOnboardingDm(userId: string): void;
|
|
18
|
+
savePendingRequest(r: NewPendingRequest): void;
|
|
19
|
+
takePendingRequest(userId: string): PendingRequest | null;
|
|
20
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How the worker opens its store at start: the SQLite store on the configured file, with the security-event log wired
|
|
3
|
+
* in, and every stored secret moved to the first encryption key before anything is served (README, key rotation).
|
|
4
|
+
*/
|
|
5
|
+
import type { Clock, LinkLimits, Logger, Random } from "@primusclaw/personal-agent";
|
|
6
|
+
import type { SecurityEventSink } from "./context.js";
|
|
7
|
+
import { SqliteStore } from "./store.js";
|
|
8
|
+
/** The settings the store is opened with; the configuration's own fields (TEAMS_DB_PATH, TEAMS_ENCRYPTION_KEYS, the
|
|
9
|
+
* TEAMS_LINK_* limits), so a worker's settings object is passed as it is. */
|
|
10
|
+
export interface WorkerStoreSettings {
|
|
11
|
+
readonly dbPath: string;
|
|
12
|
+
readonly encryptionKeys: readonly string[];
|
|
13
|
+
readonly linkLimits: LinkLimits;
|
|
14
|
+
}
|
|
15
|
+
export interface WorkerStoreDeps {
|
|
16
|
+
/** The security-event log (`link_attempt`, `link_lockout`). */
|
|
17
|
+
readonly securityEvent: SecurityEventSink;
|
|
18
|
+
/** Where the re-encryption counts are logged. */
|
|
19
|
+
readonly logger: Logger;
|
|
20
|
+
/** Default: the system clock. */
|
|
21
|
+
readonly clock?: Clock;
|
|
22
|
+
/** Default: `node:crypto`. */
|
|
23
|
+
readonly random?: Random;
|
|
24
|
+
}
|
|
25
|
+
export declare function openWorkerStore(settings: WorkerStoreSettings, deps: WorkerStoreDeps): SqliteStore;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Conversation references, preferences and held notices on SQLite (tables `conversation_refs`, `user_prefs`,
|
|
2
|
+
* `held_events`). */
|
|
3
|
+
import { type ConversationRefRepository, type HeldEventRepository, type JsonObject, type Prefs, type PrefsRepository } from "@primusclaw/personal-agent";
|
|
4
|
+
import type { StoreContext } from "./context.js";
|
|
5
|
+
export declare class SqliteConversationRefRepository implements ConversationRefRepository {
|
|
6
|
+
private readonly ctx;
|
|
7
|
+
constructor(ctx: StoreContext);
|
|
8
|
+
saveReference(userId: string, reference: JsonObject): void;
|
|
9
|
+
getReference(userId: string): JsonObject | null;
|
|
10
|
+
lastInboundAt(userId: string): number | null;
|
|
11
|
+
}
|
|
12
|
+
export declare class SqlitePrefsRepository implements PrefsRepository {
|
|
13
|
+
private readonly ctx;
|
|
14
|
+
constructor(ctx: StoreContext);
|
|
15
|
+
getPrefs(userId: string): Prefs;
|
|
16
|
+
getTimezone(userId: string): string;
|
|
17
|
+
setTimezone(userId: string, timezone: string): void;
|
|
18
|
+
setQuiet(userId: string, window: readonly [string, string] | null): void;
|
|
19
|
+
setPausedUntil(userId: string, until: number | null): void;
|
|
20
|
+
setRemoteWindowPref(userId: string, window: string): void;
|
|
21
|
+
setBriefTrigger(userId: string, triggerId: string | null): void;
|
|
22
|
+
setLang(userId: string, lang: string, said?: boolean): void;
|
|
23
|
+
learnLang(userId: string, lang: string): void;
|
|
24
|
+
/** Upsert one preference; the others keep their value (or their default for a new row). */
|
|
25
|
+
private setPref;
|
|
26
|
+
}
|
|
27
|
+
export declare class SqliteHeldEventRepository implements HeldEventRepository {
|
|
28
|
+
private readonly ctx;
|
|
29
|
+
constructor(ctx: StoreContext);
|
|
30
|
+
holdEvent(userId: string, event: JsonObject): void;
|
|
31
|
+
usersWithHeld(): string[];
|
|
32
|
+
takeHeld(userId: string): JsonObject[];
|
|
33
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON text as Python's `json.dumps` writes it by default (`", "` and `": "` separators, non-ASCII as `\uXXXX`), so a
|
|
3
|
+
* `*_json` column holds the same text whichever worker wrote it. Reading back is plain `JSON.parse`.
|
|
4
|
+
*/
|
|
5
|
+
import { type JsonObject } from "@primusclaw/personal-agent";
|
|
6
|
+
export declare function pyJsonDumps(value: unknown): string;
|
|
7
|
+
/** The parsed value, or `undefined` when the text is not JSON (Python's `ValueError`). */
|
|
8
|
+
export declare function parseJson(text: string | null): unknown;
|
|
9
|
+
/** A stored list; anything else reads as an empty one (Python's `_json_list`). */
|
|
10
|
+
export declare function jsonList(text: string | null): unknown[];
|
|
11
|
+
/** A stored list of strings, other entries dropped. */
|
|
12
|
+
export declare function jsonStrings(text: string | null): string[];
|
|
13
|
+
/** The stored mode id -> flag map; anything that is not one reads as nothing said (Python's `_json_flags`). */
|
|
14
|
+
export declare function jsonFlags(text: string | null): Record<string, boolean>;
|
|
15
|
+
/** A stored object, or null when the text is not one. */
|
|
16
|
+
export declare function storedObject(text: string | null): JsonObject | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { StoreContext } from "./context.js";
|
|
2
|
+
/** Every column that holds a Fernet token, as (table, key column, encrypted column). A new one must be listed here, or
|
|
3
|
+
* it is never moved to a new key and is lost when the old key goes. */
|
|
4
|
+
export declare const ENCRYPTED_COLUMNS: readonly (readonly [string, string, string])[];
|
|
5
|
+
export interface ReencryptCounts {
|
|
6
|
+
/** Re-encrypted now. */
|
|
7
|
+
rotated: number;
|
|
8
|
+
/** Already under the first key. */
|
|
9
|
+
current: number;
|
|
10
|
+
/** Under none of the configured keys; left as they are. */
|
|
11
|
+
undecryptable: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function reencrypt(ctx: StoreContext): ReencryptCounts;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Remote session names and permission cards on SQLite (tables `remote_session_names`, `remote_permissions`). */
|
|
2
|
+
import { type NewRemotePermission, type RemotePermission, type RemotePermissionRepository, type RemotePermissionState, type RemoteSessionName, type RemoteSessionNameRepository } from "@primusclaw/personal-agent";
|
|
3
|
+
import type { StoreContext } from "./context.js";
|
|
4
|
+
export declare class SqliteRemoteSessionNameRepository implements RemoteSessionNameRepository {
|
|
5
|
+
private readonly ctx;
|
|
6
|
+
constructor(ctx: StoreContext);
|
|
7
|
+
nameRemoteSession(userId: string, sessionId: string, name?: Partial<RemoteSessionName>): void;
|
|
8
|
+
titleRemoteSession(sessionId: string, title: string): void;
|
|
9
|
+
setRemoteSessionAcp(sessionId: string, acpSessionId: string): void;
|
|
10
|
+
remoteSessionAcps(userId: string): Map<string, string>;
|
|
11
|
+
remoteSessionNames(userId: string): Map<string, RemoteSessionName>;
|
|
12
|
+
}
|
|
13
|
+
export declare class SqliteRemotePermissionRepository implements RemotePermissionRepository {
|
|
14
|
+
private readonly ctx;
|
|
15
|
+
constructor(ctx: StoreContext);
|
|
16
|
+
saveRemotePermission(p: NewRemotePermission): boolean;
|
|
17
|
+
getRemotePermission(sessionId: string, actionId: string): RemotePermission | null;
|
|
18
|
+
pendingRemotePermissions(userId: string, sessionId: string): RemotePermission[];
|
|
19
|
+
waitingRemoteSessions(userId: string): Set<string>;
|
|
20
|
+
forgetRemotePermission(sessionId: string, actionId: string): void;
|
|
21
|
+
closeRemotePermission(sessionId: string, actionId: string, state: RemotePermissionState, options?: {
|
|
22
|
+
readonly onlyIfPending?: boolean;
|
|
23
|
+
}): boolean;
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Where a chat is attached on a remote agent, on SQLite (table `remote_state`). The key is the person's id for a 1:1
|
|
2
|
+
* chat, or `<userId>#<conversation>` for a group chat bound to a session. */
|
|
3
|
+
import { type RemoteAttachment, type RemoteModes, type RemoteState, type RemoteStateRepository } from "@primusclaw/personal-agent";
|
|
4
|
+
import type { StoreContext } from "./context.js";
|
|
5
|
+
export declare class SqliteRemoteStateRepository implements RemoteStateRepository {
|
|
6
|
+
private readonly ctx;
|
|
7
|
+
constructor(ctx: StoreContext);
|
|
8
|
+
getRemote(chatKey: string): RemoteState | null;
|
|
9
|
+
remoteAttachedUsers(): string[];
|
|
10
|
+
attachedRemote(chatKey: string): RemoteState | null;
|
|
11
|
+
attachRemote(chatKey: string, a: RemoteAttachment): void;
|
|
12
|
+
setRemoteWindow(chatKey: string, window: string): void;
|
|
13
|
+
noteWindowCard(chatKey: string, cardId: string, kept?: number): void;
|
|
14
|
+
detachRemote(chatKey: string): void;
|
|
15
|
+
setRemoteCursor(chatKey: string, sessionId: string, cursor: string): void;
|
|
16
|
+
setRemoteTurn(chatKey: string, sessionId: string, isOpen: boolean): void;
|
|
17
|
+
setRemoteModes(chatKey: string, sessionId: string, modes: RemoteModes): void;
|
|
18
|
+
setRemoteStatusCard(chatKey: string, cardId: string): void;
|
|
19
|
+
setRemoteListing(chatKey: string, sessionIds: readonly string[]): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Which session a chat uses, and what /repo and /sessions remember, on SQLite (tables `sessions`, `recent_repos`,
|
|
2
|
+
* `pending_repo`, `session_history`). Order is by a per-person counter, not the clock, so two picks in one second keep
|
|
3
|
+
* their order. */
|
|
4
|
+
import { type ChatSession, type HistorySession, type NotedSession, type RepoChoice, type RepoHistoryRepository, type SessionRepository } from "@primusclaw/personal-agent";
|
|
5
|
+
import type { StoreContext } from "./context.js";
|
|
6
|
+
export declare class SqliteSessionRepository implements SessionRepository {
|
|
7
|
+
private readonly ctx;
|
|
8
|
+
constructor(ctx: StoreContext);
|
|
9
|
+
getSession(userId: string, scope?: string): string | null;
|
|
10
|
+
getSessionPlugin(userId: string, scope?: string): string;
|
|
11
|
+
getSessionModel(userId: string, scope?: string): string;
|
|
12
|
+
getSessionShared(userId: string, scope?: string): boolean;
|
|
13
|
+
setSession(userId: string, session: Partial<ChatSession> & {
|
|
14
|
+
readonly sessionId: string;
|
|
15
|
+
}, scope?: string): void;
|
|
16
|
+
clearSession(userId: string, scope?: string): void;
|
|
17
|
+
}
|
|
18
|
+
export declare class SqliteRepoHistoryRepository implements RepoHistoryRepository {
|
|
19
|
+
private readonly ctx;
|
|
20
|
+
constructor(ctx: StoreContext);
|
|
21
|
+
touchRecentRepo(userId: string, choice: RepoChoice): void;
|
|
22
|
+
recentRepos(userId: string): RepoChoice[];
|
|
23
|
+
setPendingRepo(userId: string, choice: RepoChoice): void;
|
|
24
|
+
getPendingRepo(userId: string): RepoChoice | null;
|
|
25
|
+
clearPendingRepo(userId: string): void;
|
|
26
|
+
noteSession(userId: string, sessionId: string, seen?: NotedSession): void;
|
|
27
|
+
sessionHistory(userId: string, limit?: number): HistorySession[];
|
|
28
|
+
forgetSession(userId: string, sessionId: string): void;
|
|
29
|
+
forgetRepos(userId: string): void;
|
|
30
|
+
private nextSeq;
|
|
31
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The StorePort on one SQLite file (`/data/state.db`), table for table and column for column the Python worker's, so
|
|
3
|
+
* either worker opens what the other wrote. Single process by design: the deployment runs one replica (Recreate).
|
|
4
|
+
*/
|
|
5
|
+
import { type Clock, type LinkLimits, type Random, type StorePort } from "@primusclaw/personal-agent";
|
|
6
|
+
import { SqliteApprovalRepository } from "./approval-repository.js";
|
|
7
|
+
import { SqliteAssistantButtonRepository, SqliteAssistantCardRepository, SqliteAssistantMessageRepository } from "./assistant-repository.js";
|
|
8
|
+
import { type SecurityEventSink } from "./context.js";
|
|
9
|
+
import { SqliteFeedbackRepository } from "./feedback-repository.js";
|
|
10
|
+
import { SqliteGithubConfirmationRepository, SqliteReviewDelegationRepository } from "./github-repository.js";
|
|
11
|
+
import { SqliteIdempotencyRepository } from "./idempotency-repository.js";
|
|
12
|
+
import { SqliteLinkRepository } from "./link-repository.js";
|
|
13
|
+
import { SqliteMemberDirectoryRepository } from "./member-repository.js";
|
|
14
|
+
import { SqliteNotifySubscriptionRepository } from "./notify-repository.js";
|
|
15
|
+
import { SqliteObserveRepository } from "./observe-repository.js";
|
|
16
|
+
import { SqliteOnboardingRepository } from "./onboarding-repository.js";
|
|
17
|
+
import { SqliteConversationRefRepository, SqliteHeldEventRepository, SqlitePrefsRepository } from "./people-repository.js";
|
|
18
|
+
import { type ReencryptCounts } from "./reencrypt.js";
|
|
19
|
+
import { SqliteRemotePermissionRepository, SqliteRemoteSessionNameRepository } from "./remote-repository.js";
|
|
20
|
+
import { SqliteRemoteStateRepository } from "./remote-state-repository.js";
|
|
21
|
+
import { SqliteRepoHistoryRepository, SqliteSessionRepository } from "./session-repository.js";
|
|
22
|
+
import { SqliteTurnAnswerRepository } from "./turn-repository.js";
|
|
23
|
+
export declare const DEFAULT_LINK_LIMITS: LinkLimits;
|
|
24
|
+
export interface SqliteStoreOptions {
|
|
25
|
+
/** The database file (`TEAMS_DB_PATH`), or ":memory:". */
|
|
26
|
+
readonly path: string;
|
|
27
|
+
/** TEAMS_ENCRYPTION_KEYS: the first encrypts, all decrypt. At least one. */
|
|
28
|
+
readonly encryptionKeys: readonly string[];
|
|
29
|
+
readonly clock?: Clock;
|
|
30
|
+
readonly random?: Random;
|
|
31
|
+
readonly linkLimits?: LinkLimits;
|
|
32
|
+
/**
|
|
33
|
+
* Where `link_attempt` / `link_lockout` go: the security-event log, which filters the fields it logs. Required, so a
|
|
34
|
+
* worker cannot be assembled that links people without an audit trail (the Python store always logs them).
|
|
35
|
+
*/
|
|
36
|
+
readonly securityEvent: SecurityEventSink;
|
|
37
|
+
}
|
|
38
|
+
export declare class SqliteStore implements StorePort {
|
|
39
|
+
#private;
|
|
40
|
+
readonly links: SqliteLinkRepository;
|
|
41
|
+
readonly references: SqliteConversationRefRepository;
|
|
42
|
+
readonly prefs: SqlitePrefsRepository;
|
|
43
|
+
readonly held: SqliteHeldEventRepository;
|
|
44
|
+
readonly members: SqliteMemberDirectoryRepository;
|
|
45
|
+
readonly notifySubs: SqliteNotifySubscriptionRepository;
|
|
46
|
+
readonly approvals: SqliteApprovalRepository;
|
|
47
|
+
readonly observe: SqliteObserveRepository;
|
|
48
|
+
readonly assistantCards: SqliteAssistantCardRepository;
|
|
49
|
+
readonly assistantButtons: SqliteAssistantButtonRepository;
|
|
50
|
+
readonly assistantMessages: SqliteAssistantMessageRepository;
|
|
51
|
+
readonly remoteState: SqliteRemoteStateRepository;
|
|
52
|
+
readonly remoteSessionNames: SqliteRemoteSessionNameRepository;
|
|
53
|
+
readonly remotePermissions: SqliteRemotePermissionRepository;
|
|
54
|
+
readonly sessions: SqliteSessionRepository;
|
|
55
|
+
readonly repoHistory: SqliteRepoHistoryRepository;
|
|
56
|
+
readonly turns: SqliteTurnAnswerRepository;
|
|
57
|
+
readonly feedback: SqliteFeedbackRepository;
|
|
58
|
+
readonly onboarding: SqliteOnboardingRepository;
|
|
59
|
+
readonly githubConfirmations: SqliteGithubConfirmationRepository;
|
|
60
|
+
readonly reviewDelegation: SqliteReviewDelegationRepository;
|
|
61
|
+
readonly idempotency: SqliteIdempotencyRepository;
|
|
62
|
+
constructor(options: SqliteStoreOptions);
|
|
63
|
+
now(): number;
|
|
64
|
+
/** Move every stored secret to the first key (run at start, before serving). */
|
|
65
|
+
reencrypt(): ReencryptCounts;
|
|
66
|
+
close(): void;
|
|
67
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Chat turns that answered in the chat, on SQLite (tables `turn_answers`, `answered_messages`). A turn row carries the
|
|
2
|
+
* id of the process that started it: a turn of another process died with it. */
|
|
3
|
+
import { type AnsweredInChat, type TurnAnswerRepository, type FinishedRun } from "@primusclaw/personal-agent";
|
|
4
|
+
import type { StoreContext } from "./context.js";
|
|
5
|
+
export declare class SqliteTurnAnswerRepository implements TurnAnswerRepository {
|
|
6
|
+
private readonly ctx;
|
|
7
|
+
private readonly bootId;
|
|
8
|
+
/** @param bootId this process's id (Python's `secrets.token_hex(8)` at start). */
|
|
9
|
+
constructor(ctx: StoreContext, bootId: string);
|
|
10
|
+
turnStarted(sessionId: string, turn?: {
|
|
11
|
+
readonly prompt?: string;
|
|
12
|
+
readonly messageId?: string;
|
|
13
|
+
readonly turnId?: string;
|
|
14
|
+
}): void;
|
|
15
|
+
turnDelivered(sessionId: string, turn?: {
|
|
16
|
+
readonly messageId?: string;
|
|
17
|
+
readonly turnId?: string;
|
|
18
|
+
}): void;
|
|
19
|
+
turnNotDelivered(sessionId: string, turnId?: string): void;
|
|
20
|
+
answeredInChat(sessionId: string, run: FinishedRun): AnsweredInChat | null;
|
|
21
|
+
/** The two message ids decide when both are known; else the clock does (see the port's description). */
|
|
22
|
+
private judge;
|
|
23
|
+
turnPrompt(sessionId: string, finishedAt: number): string;
|
|
24
|
+
}
|