@primusclaw/personal-agent 0.1.0 → 0.2.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/index.js +7874 -6791
- package/dist/types/assistant/chat.d.ts +2 -2
- package/dist/types/assistant/events.d.ts +6 -1
- package/dist/types/capabilities/capability.d.ts +6 -0
- package/dist/types/capabilities/natural.d.ts +1 -1
- package/dist/types/capabilities/phrases.d.ts +5 -0
- package/dist/types/commands/names.d.ts +1 -1
- package/dist/types/config/settings.d.ts +6 -0
- package/dist/types/feedback/reporting.d.ts +10 -0
- package/dist/types/group-context/buffer.d.ts +66 -0
- package/dist/types/group-context/command.d.ts +11 -0
- package/dist/types/group-context/index.d.ts +3 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/locales/assistant-chat.d.ts +4 -4
- package/dist/types/locales/connector-reasons.d.ts +1 -0
- package/dist/types/locales/group-context.d.ts +44 -0
- package/dist/types/locales/remote-agents.d.ts +12 -1
- package/dist/types/locales/routing.d.ts +9 -2
- package/dist/types/locales/terminal-approvals.d.ts +44 -0
- package/dist/types/ports/claw/remote-agents.d.ts +4 -0
- package/dist/types/ports/store/group-context.d.ts +10 -0
- package/dist/types/ports/store/index.d.ts +3 -0
- package/dist/types/ports/store/remote.d.ts +12 -1
- package/dist/types/proactive/deliver.d.ts +11 -0
- package/dist/types/protocol/claw-api/assistant-events.d.ts +30 -1
- package/dist/types/protocol/remote-agents.d.ts +34 -0
- package/dist/types/remote/approvals-command.d.ts +8 -0
- package/dist/types/remote/chats.d.ts +40 -0
- package/dist/types/remote/events.d.ts +4 -1
- package/dist/types/remote/follow/control.d.ts +10 -0
- package/dist/types/remote/prompt.d.ts +14 -2
- package/dist/types/remote/state.d.ts +4 -1
- package/dist/types/remote/terminal-asks.d.ts +9 -0
- package/dist/types/remote/turn-state.d.ts +16 -1
- package/dist/types/remote/turns/chat-turn.d.ts +12 -1
- package/dist/types/remote/turns/display.d.ts +2 -0
- package/dist/types/remote/turns/runner.d.ts +15 -0
- package/dist/types/remote/turns/window-turn.d.ts +5 -1
- package/dist/types/remote-view/index.d.ts +3 -0
- package/dist/types/remote-view/terminal-ask-view.d.ts +68 -0
- package/dist/types/routing/commands-session.d.ts +3 -1
- package/dist/types/routing/deps.d.ts +3 -1
- package/dist/types/routing/features.d.ts +12 -2
- package/dist/types/routing/group-router.d.ts +2 -0
- package/dist/types/routing/turn-registry.d.ts +23 -6
- package/dist/types/routing/turn-start.d.ts +13 -5
- package/package.json +1 -1
|
@@ -158,9 +158,9 @@ export declare class AssistantChat {
|
|
|
158
158
|
/**
|
|
159
159
|
* `/stop`: interrupts what the assistant is doing this moment; a task is stopped by name. `onlyMessage`: interrupt only while
|
|
160
160
|
* Claw's running turn answers that message (a Stop card's turn, read just before the interrupt goes); false, with nothing said
|
|
161
|
-
* or interrupted, when it does not.
|
|
161
|
+
* or interrupted, when it does not. Said in `lang`, the chat's.
|
|
162
162
|
*/
|
|
163
|
-
stop(_userId: string, credential: string, conversation: Conversation, onlyMessage?: string): Promise<boolean>;
|
|
163
|
+
stop(_userId: string, credential: string, conversation: Conversation, onlyMessage?: string, lang?: Lang): Promise<boolean>;
|
|
164
164
|
}
|
|
165
165
|
/** The session a chat is attached to as the assistant is told it: its text and yes/no fields, each text clipped. */
|
|
166
166
|
export declare function attachedOf(attached: JsonObject | null): JsonObject | null;
|
|
@@ -42,11 +42,16 @@ export declare function confirmWriteOf(event: Event): ConfirmWrite | undefined;
|
|
|
42
42
|
*/
|
|
43
43
|
export declare function suggestedRepliesOf(event: Event, text: string, styleOn: boolean, lang?: Lang): SuggestedReply[];
|
|
44
44
|
export interface RemoteNotice {
|
|
45
|
-
|
|
45
|
+
/** `resolved`: a question a terminal asked no longer waits here (answered there, or given up); `resolved` says which. */
|
|
46
|
+
readonly kind: "done" | "needs_you" | "error" | "resolved";
|
|
46
47
|
readonly sessionId: string;
|
|
47
48
|
readonly ask: Event | undefined;
|
|
48
49
|
readonly machine: string;
|
|
49
50
|
readonly agent: string;
|
|
51
|
+
readonly resolved?: {
|
|
52
|
+
readonly actionId: string;
|
|
53
|
+
readonly by: string;
|
|
54
|
+
};
|
|
50
55
|
}
|
|
51
56
|
/** What an `assistant.say` says of an agent session on the person's machine, or undefined for any other message. */
|
|
52
57
|
export declare function remoteNoticeOf(event: Event): RemoteNotice | undefined;
|
|
@@ -21,6 +21,12 @@ export interface Capability {
|
|
|
21
21
|
/** What it is, in a few words. */
|
|
22
22
|
readonly zh: string;
|
|
23
23
|
readonly en: string;
|
|
24
|
+
/**
|
|
25
|
+
* What it is in a handful of words, for the line of a group's help that says what else a 1:1 chat with the bot does ("" : not
|
|
26
|
+
* named there). Only a capability a group does not show has one.
|
|
27
|
+
*/
|
|
28
|
+
readonly dmZh: string;
|
|
29
|
+
readonly dmEn: string;
|
|
24
30
|
/** One sentence a person would type for it ("" : it is done with buttons). */
|
|
25
31
|
readonly sayZh: string;
|
|
26
32
|
readonly sayEn: string;
|
|
@@ -12,7 +12,7 @@ export declare const NATURAL_PATTERNS: readonly (readonly [string, readonly RegE
|
|
|
12
12
|
/**
|
|
13
13
|
* `text` as the command it means when it is one of the plain sentences help teaches (or a close way of saying it), else `text`
|
|
14
14
|
* itself. Only the whole message counts. `group`: a group chat reads only what a group may do (help, stop, a new session, which
|
|
15
|
-
* account, feedback). `off`: what this chat cannot do right now, as a command word (`board` without the assistant, `github` without
|
|
15
|
+
* account, feedback), and asking it to introduce itself, read as help. `off`: what this chat cannot do right now, as a command word (`board` without the assistant, `github` without
|
|
16
16
|
* the review service, `agents` and `agent` with remote agents off) or a command and its verb (`agent detach` with no agent
|
|
17
17
|
* attached): a sentence that would mean one of them stays the words it is, for Claw.
|
|
18
18
|
*/
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
* message is looked up as a phrase: lower case, no spaces between the words, no punctuation at either end (`phraseKey`).
|
|
4
4
|
*/
|
|
5
5
|
export declare const HELP: string[];
|
|
6
|
+
/**
|
|
7
|
+
* Asking the bot to introduce itself, read as help in a group only: there the answer is said to everyone, and help is what knows
|
|
8
|
+
* what the bot does (a group's session is shared and knows nothing of it). In a 1:1 chat the words stay the person's, as before.
|
|
9
|
+
*/
|
|
10
|
+
export declare const INTRODUCE: string[];
|
|
6
11
|
export declare const STOP: string[];
|
|
7
12
|
/**
|
|
8
13
|
* "Stop it" said to a chat attached to an agent's session: that agent's turn is stopped (`/stop`). With the assistant the same words
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
export declare const COMMANDS: readonly ["help", "new", "stop", "unlink", "whoami", "resume", "memory", "sessions"];
|
|
7
7
|
/** The only commands a group chat accepts. */
|
|
8
8
|
export declare const GROUP_COMMANDS: readonly ["help", "new", "stop", "whoami", "feedback"];
|
|
9
|
-
export declare const PERSONAL_ONLY_COMMANDS: readonly ["link", "unlink", "resume", "timezone", "quiet", "pause", "brief", "repo", "sessions", "agents", "agent", "lang"];
|
|
9
|
+
export declare const PERSONAL_ONLY_COMMANDS: readonly ["link", "unlink", "resume", "timezone", "quiet", "pause", "brief", "repo", "sessions", "agents", "agent", "lang", "approvals"];
|
|
10
10
|
/**
|
|
11
11
|
* `/feedback [TEXT]` takes an argument, so it is read with `commandArgs`, never `parseCommand`; it is listed here (not in
|
|
12
12
|
* COMMANDS) only so that a group chat accepts it and an unknown `/word` is still plain text.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { GroupContextSettings } from "../group-context/buffer.js";
|
|
1
2
|
import type { SecretsSourceName } from "../ports/secret-source.js";
|
|
2
3
|
import type { LinkLimits } from "../ports/store/link.js";
|
|
3
4
|
/**
|
|
@@ -36,6 +37,11 @@ export interface Settings {
|
|
|
36
37
|
readonly onBehalfUsers: ReadonlySet<string>;
|
|
37
38
|
/** TEAMS_OBSERVE_USERS (lower-cased): who may turn on observing a group chat. */
|
|
38
39
|
readonly observeUsers: ReadonlySet<string>;
|
|
40
|
+
/**
|
|
41
|
+
* TEAMS_GROUP_CONTEXT_MESSAGES (30; 0 off), TEAMS_GROUP_CONTEXT_HOURS (4), TEAMS_GROUP_CONTEXT_CHARS (500): the recent
|
|
42
|
+
* messages of a group chat kept in memory as background for an @mention.
|
|
43
|
+
*/
|
|
44
|
+
readonly groupContext: GroupContextSettings;
|
|
39
45
|
/** TEAMS_LIMIT_<OPERATION>: the per-person limits of operations that act with someone's authority. */
|
|
40
46
|
readonly sensitiveLimits: ReadonlyMap<string, Limit>;
|
|
41
47
|
/** TEAMS_LINK_MAX_FAILURES_PER_USER (5) / TEAMS_LINK_MAX_FAILURES_GLOBAL (100) / TEAMS_LINK_ATTEMPT_WINDOW_SECONDS (600). */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IdentityLookup } from "../plugins/identity-lookup.js";
|
|
2
|
+
import type { ClawAssistantPort } from "../ports/claw/assistant.js";
|
|
2
3
|
import type { Clock } from "../ports/clock.js";
|
|
3
4
|
import type { Logger } from "../ports/logger.js";
|
|
4
5
|
import type { RouteBase } from "../ports/routing.js";
|
|
@@ -8,6 +9,9 @@ import type { FeedbackService, ReportResult } from "./service.js";
|
|
|
8
9
|
export interface ReportingDeps {
|
|
9
10
|
readonly service: FeedbackService;
|
|
10
11
|
readonly sessions: Pick<SessionRepository, "getSession">;
|
|
12
|
+
readonly assistant: Pick<ClawAssistantPort, "session">;
|
|
13
|
+
/** A 1:1 chat that talks to the person's assistant: its turns are in the assistant's session, which the report's scene is then. */
|
|
14
|
+
readonly assistantOn: (userId: string) => boolean;
|
|
11
15
|
/** The person's Claw credential if they have one; never asks them for it (`undefined`: not linked). */
|
|
12
16
|
readonly credential: (sender: UserRef) => Promise<string | undefined>;
|
|
13
17
|
readonly identity: IdentityLookup;
|
|
@@ -19,6 +23,12 @@ export declare class FeedbackReporting {
|
|
|
19
23
|
private readonly deps;
|
|
20
24
|
constructor(deps: ReportingDeps);
|
|
21
25
|
file(route: RouteBase, text: string): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* The session whose turns the report shows: the one the chat talks to. A 1:1 chat with the assistant on talks to the assistant's
|
|
28
|
+
* session (as a thumb's vote is cast there, `thumbs.ts`), so its turns are the ones reported, not the plain chat session's. When the
|
|
29
|
+
* assistant's session cannot be read, the report goes without turns rather than with another session's.
|
|
30
|
+
*/
|
|
31
|
+
private sessionOf;
|
|
22
32
|
/** What the channel gave, and for what is missing, what the key's platform says; a slow or failed lookup is skipped. */
|
|
23
33
|
private reporterOf;
|
|
24
34
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The recent messages of a group chat, as background for the next time someone @mentions the bot there.
|
|
3
|
+
*
|
|
4
|
+
* A channel hands the bot a group message that does not @mention it only where the chat granted that (Teams: the
|
|
5
|
+
* resource-specific permission ChatMessage.Read.Chat). Without it nothing reaches `record` and the bot works exactly as
|
|
6
|
+
* before. With it, each such message is kept in this worker's memory only: never answered, never stored, never logged,
|
|
7
|
+
* masked as an outbound message is before it is kept, cut to `messageChars`, the sender's display name and the words
|
|
8
|
+
* alone (no files). A chat keeps its newest `messages`, none older than `maxAgeSeconds`; the whole chat is dropped when
|
|
9
|
+
* the bot is removed from it, when it says `/context off`, or once its newest message is that old. When someone
|
|
10
|
+
* @mentions the bot, `background` gives what is kept, marked as untrusted history, for the turn to send before what they
|
|
11
|
+
* wrote.
|
|
12
|
+
*/
|
|
13
|
+
import type { Clock } from "../ports/clock.js";
|
|
14
|
+
import type { Logger } from "../ports/logger.js";
|
|
15
|
+
import type { GroupContextRepository } from "../ports/store/group-context.js";
|
|
16
|
+
import type { InboundMessage } from "../ports/surface.js";
|
|
17
|
+
/** How much a group's context keeps (the deployment's TEAMS_GROUP_CONTEXT_* settings). */
|
|
18
|
+
export interface GroupContextSettings {
|
|
19
|
+
/** The most messages kept per chat; 0 turns the feature off (nothing is kept). */
|
|
20
|
+
readonly messages: number;
|
|
21
|
+
/** How old a kept message may get. */
|
|
22
|
+
readonly maxAgeSeconds: number;
|
|
23
|
+
/** The longest text kept of one message (code points). */
|
|
24
|
+
readonly messageChars: number;
|
|
25
|
+
}
|
|
26
|
+
export declare const DEFAULT_GROUP_CONTEXT: GroupContextSettings;
|
|
27
|
+
/** The most chats kept at once: past it, the chat that heard nothing for longest is dropped. */
|
|
28
|
+
export declare const MAX_CHATS = 2000;
|
|
29
|
+
export interface GroupContextDeps {
|
|
30
|
+
readonly settings: GroupContextSettings;
|
|
31
|
+
readonly clock: Clock;
|
|
32
|
+
/** Credentials and the like masked out, as in an outbound message. */
|
|
33
|
+
readonly mask: (text: string) => string;
|
|
34
|
+
readonly off: GroupContextRepository;
|
|
35
|
+
readonly log: Logger;
|
|
36
|
+
}
|
|
37
|
+
export declare class GroupContext {
|
|
38
|
+
private readonly deps;
|
|
39
|
+
/** Per chat, oldest first; a Map keeps chats in the order they last heard something (oldest first). */
|
|
40
|
+
private readonly chats;
|
|
41
|
+
constructor(deps: GroupContextDeps);
|
|
42
|
+
get enabled(): boolean;
|
|
43
|
+
/** A group message not addressed to the bot: kept, never answered. */
|
|
44
|
+
record(message: InboundMessage): void;
|
|
45
|
+
/** The person edited a kept message: the new words replace the old (nothing when it is not kept). */
|
|
46
|
+
edit(chatId: string, messageId: string, newText: string): void;
|
|
47
|
+
/** A kept message was deleted: it is no longer shown. */
|
|
48
|
+
remove(chatId: string, messageId: string): void;
|
|
49
|
+
/** The bot was removed from the chat, or the chat turned the context off: all of it is dropped. */
|
|
50
|
+
forget(chatId: string): void;
|
|
51
|
+
/** Drops what has grown too old in every chat (a timer runs it, so a quiet chat does not keep its words). */
|
|
52
|
+
sweep(): void;
|
|
53
|
+
/** `/context off`: kept off for the chat (stored), and what was kept is dropped at once. */
|
|
54
|
+
turnOff(chatId: string): void;
|
|
55
|
+
/** `/context on`. */
|
|
56
|
+
turnOn(chatId: string): void;
|
|
57
|
+
isOff(chatId: string): boolean;
|
|
58
|
+
/** How many messages the chat has kept now (never what). */
|
|
59
|
+
count(chatId: string): number;
|
|
60
|
+
/** How many chats have something kept (for a test or a gauge; never what). */
|
|
61
|
+
get size(): number;
|
|
62
|
+
/** The chat's kept messages as background for a turn ("" none, or the chat turned it off). */
|
|
63
|
+
background(chatId: string): string;
|
|
64
|
+
private cleaned;
|
|
65
|
+
private fresh;
|
|
66
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MessageInterceptor } from "../ports/routing.js";
|
|
2
|
+
import type { GroupContext } from "./buffer.js";
|
|
3
|
+
/** `/context on | off | status`, said to the bot in a group; with 开/关/状态 as well. */
|
|
4
|
+
export type ContextVerb = "on" | "off" | "status" | "usage";
|
|
5
|
+
/** The command's verb, `usage` for a malformed one, undefined when the text is not this command. */
|
|
6
|
+
export declare function parseContext(text: string): ContextVerb | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* `/context on|off|status` in a group, read before the account check: anybody in the chat may turn it off or on (it is
|
|
9
|
+
* the chat's, not one person's), and off drops what was kept at once.
|
|
10
|
+
*/
|
|
11
|
+
export declare function contextInterceptor(context: GroupContext): MessageInterceptor;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export { INTENT_PROMPT, MODE_PROMPT } from "./locales/review-github.js";
|
|
|
17
17
|
export * from "./typed-answer/index.js";
|
|
18
18
|
export * from "./understanding/index.js";
|
|
19
19
|
export * from "./locales/index.js";
|
|
20
|
+
export * from "./group-context/index.js";
|
|
20
21
|
export * from "./observe/index.js";
|
|
21
22
|
export * from "./plugins/index.js";
|
|
22
23
|
export * from "./ports/crypto.js";
|
|
@@ -66,6 +67,7 @@ export * from "./review/index.js";
|
|
|
66
67
|
export * as remoteDomain from "./remote/index.js";
|
|
67
68
|
export * as remoteView from "./remote-view/index.js";
|
|
68
69
|
export { RemoteAgents } from "./remote/remote-agents.js";
|
|
70
|
+
export { TerminalApprovalsCommand } from "./remote/approvals-command.js";
|
|
69
71
|
export type { RemoteContext, RemoteErrors, RemoteGuard, RemoteSettings } from "./remote/context.js";
|
|
70
72
|
export { ConnectorSetup, setupFrom } from "./remote/connector/setup.js";
|
|
71
73
|
export { CredentialUnavailable as FollowCredentialUnavailable } from "./remote/follow/follower.js";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
export declare const assistantChatText: import("./catalog.js").Catalog<{
|
|
2
2
|
unavailable: string;
|
|
3
3
|
rejected: string;
|
|
4
|
-
stopFailed: string;
|
|
5
|
-
stopped: string;
|
|
6
|
-
notBusy: string;
|
|
7
|
-
stopHint: string;
|
|
8
4
|
notTaken: string;
|
|
9
5
|
clawRefused: string;
|
|
10
6
|
anImage: string;
|
|
11
7
|
notUnderstood: string;
|
|
12
8
|
filesOnly: string;
|
|
9
|
+
stopFailed: string;
|
|
10
|
+
stopped: string;
|
|
11
|
+
notBusy: string;
|
|
12
|
+
stopHint: string;
|
|
13
13
|
}>;
|
|
@@ -53,6 +53,7 @@ export declare const connectorReasonText: import("./catalog.js").Catalog<{
|
|
|
53
53
|
terminal_gone: string;
|
|
54
54
|
terminal_switched: string;
|
|
55
55
|
terminal_input_failed: string;
|
|
56
|
+
terminal_input_unseen: string;
|
|
56
57
|
terminal_input_unconfirmed: string;
|
|
57
58
|
terminal_input_unavailable: string;
|
|
58
59
|
empty_prompt: string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare const groupContextText: import("./catalog.js").Catalog<{
|
|
2
|
+
turnedOff: (p: {
|
|
3
|
+
bot: string;
|
|
4
|
+
}) => string;
|
|
5
|
+
turnedOn: (p: {
|
|
6
|
+
bot: string;
|
|
7
|
+
}) => string;
|
|
8
|
+
isOn: (p: {
|
|
9
|
+
bot: string;
|
|
10
|
+
count: string;
|
|
11
|
+
}) => string;
|
|
12
|
+
isOnEmpty: (p: {
|
|
13
|
+
bot: string;
|
|
14
|
+
}) => string;
|
|
15
|
+
isOff: (p: {
|
|
16
|
+
bot: string;
|
|
17
|
+
}) => string;
|
|
18
|
+
unavailable: string;
|
|
19
|
+
usage: (p: {
|
|
20
|
+
bot: string;
|
|
21
|
+
}) => string;
|
|
22
|
+
}>;
|
|
23
|
+
export declare const GROUP_CONTEXT_TEXT: {
|
|
24
|
+
turnedOff: (p: {
|
|
25
|
+
bot: string;
|
|
26
|
+
}) => string;
|
|
27
|
+
turnedOn: (p: {
|
|
28
|
+
bot: string;
|
|
29
|
+
}) => string;
|
|
30
|
+
isOn: (p: {
|
|
31
|
+
bot: string;
|
|
32
|
+
count: string;
|
|
33
|
+
}) => string;
|
|
34
|
+
isOnEmpty: (p: {
|
|
35
|
+
bot: string;
|
|
36
|
+
}) => string;
|
|
37
|
+
isOff: (p: {
|
|
38
|
+
bot: string;
|
|
39
|
+
}) => string;
|
|
40
|
+
unavailable: string;
|
|
41
|
+
usage: (p: {
|
|
42
|
+
bot: string;
|
|
43
|
+
}) => string;
|
|
44
|
+
};
|
|
@@ -7,7 +7,18 @@ export declare const remoteAgentText: import("./catalog.js").Catalog<{
|
|
|
7
7
|
notEnabled: string;
|
|
8
8
|
offline: string;
|
|
9
9
|
outdated: string;
|
|
10
|
-
|
|
10
|
+
queuedBehind: (p: {
|
|
11
|
+
n: number;
|
|
12
|
+
}) => string;
|
|
13
|
+
queueFull: (p: {
|
|
14
|
+
n: number;
|
|
15
|
+
}) => string;
|
|
16
|
+
queueDropped: (p: {
|
|
17
|
+
n: number;
|
|
18
|
+
}) => string;
|
|
19
|
+
queuedRefused: (p: {
|
|
20
|
+
reason: string;
|
|
21
|
+
}) => string;
|
|
11
22
|
stopFirst: string;
|
|
12
23
|
notAttached: string;
|
|
13
24
|
approvalInactive: string;
|
|
@@ -3,8 +3,15 @@ export declare const routingText: import("./catalog.js").Catalog<{
|
|
|
3
3
|
notConnected: string;
|
|
4
4
|
onlyChats: string;
|
|
5
5
|
stillWorking: string;
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
queuedBehind: (p: {
|
|
7
|
+
n: number;
|
|
8
|
+
}) => string;
|
|
9
|
+
queueFull: (p: {
|
|
10
|
+
n: number;
|
|
11
|
+
}) => string;
|
|
12
|
+
queueDropped: (p: {
|
|
13
|
+
n: number;
|
|
14
|
+
}) => string;
|
|
8
15
|
unlinkAsk: string;
|
|
9
16
|
nothingRunning: string;
|
|
10
17
|
noSessions: string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export declare const terminalApprovalText: import("./catalog.js").Catalog<{
|
|
2
|
+
title: string;
|
|
3
|
+
who: (p: {
|
|
4
|
+
agent: string;
|
|
5
|
+
host: string;
|
|
6
|
+
project: string;
|
|
7
|
+
}) => string;
|
|
8
|
+
asksRun: string;
|
|
9
|
+
asksEdit: (p: {
|
|
10
|
+
path: string;
|
|
11
|
+
}) => string;
|
|
12
|
+
asksPlan: string;
|
|
13
|
+
asksQuestion: string;
|
|
14
|
+
asksTool: (p: {
|
|
15
|
+
tool: string;
|
|
16
|
+
}) => string;
|
|
17
|
+
oldText: string;
|
|
18
|
+
newText: string;
|
|
19
|
+
alwaysOption: (p: {
|
|
20
|
+
name: string;
|
|
21
|
+
}) => string;
|
|
22
|
+
firstAnswerWins: string;
|
|
23
|
+
tooLong: string;
|
|
24
|
+
questionOf: (p: {
|
|
25
|
+
n: number;
|
|
26
|
+
of: number;
|
|
27
|
+
}) => string;
|
|
28
|
+
optionYes: string;
|
|
29
|
+
optionSession: string;
|
|
30
|
+
optionNo: string;
|
|
31
|
+
optionNoStop: string;
|
|
32
|
+
optionPlanApprove: string;
|
|
33
|
+
optionPlanKeep: string;
|
|
34
|
+
answeredInTerminal: string;
|
|
35
|
+
noLongerWaiting: string;
|
|
36
|
+
answeredInTerminalLine: string;
|
|
37
|
+
noLongerWaitingLine: string;
|
|
38
|
+
approvalsOn: string;
|
|
39
|
+
approvalsOff: string;
|
|
40
|
+
approvalsTurnedOn: string;
|
|
41
|
+
approvalsTurnedOff: string;
|
|
42
|
+
approvalsUsage: string;
|
|
43
|
+
approvalsUnavailable: string;
|
|
44
|
+
}>;
|
|
@@ -39,6 +39,10 @@ export interface ClawRemoteAgentsPort {
|
|
|
39
39
|
/** A one-time pairing code inside the command line that uses it. */
|
|
40
40
|
pair(key: ClawCredential): Promise<RemotePairing>;
|
|
41
41
|
revoke(key: ClawCredential, connectorId: string): Promise<void>;
|
|
42
|
+
/** Whether the questions the caller's terminals ask come to them (`GET /approvals`; on unless they turned it off). */
|
|
43
|
+
approvals(key: ClawCredential): Promise<boolean>;
|
|
44
|
+
/** Turn them on or off (`PUT /approvals`); answers the setting as Claw stored it. */
|
|
45
|
+
setApprovals(key: ClawCredential, enabled: boolean): Promise<boolean>;
|
|
42
46
|
createSession(key: ClawCredential, session: NewRemoteSession): Promise<RemoteSession>;
|
|
43
47
|
sessions(key: ClawCredential): Promise<RemoteSession[]>;
|
|
44
48
|
session(key: ClawCredential, sessionId: string): Promise<RemoteSession>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The group chats that turned the recent-messages context off (`/context off`). Only the switch is kept: the messages
|
|
3
|
+
* themselves are never stored (they live in the worker's memory, `group-context/buffer.ts`).
|
|
4
|
+
*/
|
|
5
|
+
export interface GroupContextRepository {
|
|
6
|
+
/** True when the chat turned the context off. */
|
|
7
|
+
contextOff(conversationId: string): boolean;
|
|
8
|
+
/** Turns it off (`off` true) or back on. */
|
|
9
|
+
setContextOff(conversationId: string, off: boolean): void;
|
|
10
|
+
}
|
|
@@ -13,6 +13,7 @@ import type { ApprovalRepository } from "./approvals.js";
|
|
|
13
13
|
import type { AssistantButtonRepository, AssistantCardRepository, AssistantMessageRepository } from "./assistant.js";
|
|
14
14
|
import type { FeedbackRepository } from "./feedback.js";
|
|
15
15
|
import type { GithubConfirmationRepository, ReviewDelegationRepository } from "./github.js";
|
|
16
|
+
import type { GroupContextRepository } from "./group-context.js";
|
|
16
17
|
import type { IdempotencyRepository } from "./idempotency.js";
|
|
17
18
|
import type { LinkRepository } from "./link.js";
|
|
18
19
|
import type { NotifySubscriptionRepository } from "./notify.js";
|
|
@@ -31,6 +32,7 @@ export interface StorePort {
|
|
|
31
32
|
readonly notifySubs: NotifySubscriptionRepository;
|
|
32
33
|
readonly approvals: ApprovalRepository;
|
|
33
34
|
readonly observe: ObserveRepository;
|
|
35
|
+
readonly groupContext: GroupContextRepository;
|
|
34
36
|
readonly assistantCards: AssistantCardRepository;
|
|
35
37
|
readonly assistantButtons: AssistantButtonRepository;
|
|
36
38
|
readonly assistantMessages: AssistantMessageRepository;
|
|
@@ -52,6 +54,7 @@ export type * from "./approvals.js";
|
|
|
52
54
|
export type * from "./assistant.js";
|
|
53
55
|
export type * from "./feedback.js";
|
|
54
56
|
export type * from "./github.js";
|
|
57
|
+
export type * from "./group-context.js";
|
|
55
58
|
export type * from "./idempotency.js";
|
|
56
59
|
export type * from "./link.js";
|
|
57
60
|
export type * from "./notify.js";
|
|
@@ -91,6 +91,10 @@ export interface RemotePermission {
|
|
|
91
91
|
readonly options: readonly unknown[];
|
|
92
92
|
readonly expiresAt: number;
|
|
93
93
|
readonly state: RemotePermissionState;
|
|
94
|
+
/** The message the card went out as, when the channel said its id ("" none): a card a terminal's answer ends is edited there. */
|
|
95
|
+
readonly messageId: string;
|
|
96
|
+
/** For a question a terminal asks: the whole `remote.ask`, so the card can be drawn again with its content (null otherwise). */
|
|
97
|
+
readonly ask: Readonly<Record<string, unknown>> | null;
|
|
94
98
|
}
|
|
95
99
|
export interface NewRemotePermission {
|
|
96
100
|
readonly userId: string;
|
|
@@ -99,6 +103,8 @@ export interface NewRemotePermission {
|
|
|
99
103
|
readonly tool: string;
|
|
100
104
|
readonly options: readonly unknown[];
|
|
101
105
|
readonly expiresAt: number;
|
|
106
|
+
/** For a question a terminal asks: the whole `remote.ask` (see {@link RemotePermission.ask}). */
|
|
107
|
+
readonly ask?: Readonly<Record<string, unknown>>;
|
|
102
108
|
}
|
|
103
109
|
export interface RemotePermissionRepository {
|
|
104
110
|
/** Recorded before it is shown; false when this request already has a row. */
|
|
@@ -108,7 +114,12 @@ export interface RemotePermissionRepository {
|
|
|
108
114
|
pendingRemotePermissions(userId: string, sessionId: string): RemotePermission[];
|
|
109
115
|
waitingRemoteSessions(userId: string): Set<string>;
|
|
110
116
|
forgetRemotePermission(sessionId: string, actionId: string): void;
|
|
111
|
-
|
|
117
|
+
/** Record the message the card went out as. */
|
|
118
|
+
setRemotePermissionMessage(sessionId: string, actionId: string, messageId: string): void;
|
|
119
|
+
closeRemotePermission(sessionId: string, actionId: string, state: RemotePermissionState,
|
|
120
|
+
/** `onlyIfState`: only from that state (instead of only from pending). */
|
|
121
|
+
options?: {
|
|
112
122
|
readonly onlyIfPending?: boolean;
|
|
123
|
+
readonly onlyIfState?: RemotePermissionState;
|
|
113
124
|
}): boolean;
|
|
114
125
|
}
|
|
@@ -12,6 +12,11 @@ export declare class Deliveries {
|
|
|
12
12
|
send(userId: string, reference: JsonObject, event: JsonObject): Promise<boolean>;
|
|
13
13
|
/** An approval's card, recorded BEFORE it goes out (atomically: the turn may show the same approval) and WITH its envelope. */
|
|
14
14
|
private sendApproval;
|
|
15
|
+
/**
|
|
16
|
+
* A development environment session's own card: a request that waits on the person (its card), or the end of a terminal's
|
|
17
|
+
* question (its card edited). Undefined when the message goes on as words.
|
|
18
|
+
*/
|
|
19
|
+
private remoteCard;
|
|
15
20
|
/** What the assistant said (or a work item that needs the person and was not said). */
|
|
16
21
|
private sendSaid;
|
|
17
22
|
/** The client locale of the message being handled ("" for a push or a sweep: Python CLIENT_LOCALE's default). */
|
|
@@ -39,6 +44,12 @@ export declare class Deliveries {
|
|
|
39
44
|
* or it ran out while held), undefined when it offers nothing that can be answered (its words go as they are).
|
|
40
45
|
*/
|
|
41
46
|
private sendRemoteAsk;
|
|
47
|
+
/**
|
|
48
|
+
* A question a terminal asked no longer waits here (`resolved`): answered at the terminal, or given up. Its card is edited in
|
|
49
|
+
* place to say so, without its buttons, and a click on it is told the same. True when that is done (or there is nothing to
|
|
50
|
+
* do: answered here already); false when the card cannot be found or edited, and the notice's own line is said instead.
|
|
51
|
+
*/
|
|
52
|
+
private endTerminalAsk;
|
|
42
53
|
/** A run started by a chat turn is named by what the person asked there (Claw names it by its session's first message). */
|
|
43
54
|
namedByPrompt(event: JsonObject): JsonObject;
|
|
44
55
|
/**
|
|
@@ -92,11 +92,21 @@ export interface AssistantFile {
|
|
|
92
92
|
* `agent` are what the connector reported, and `ask`'s words what the agent wrote: untrusted text, shown as text only.
|
|
93
93
|
*/
|
|
94
94
|
export interface AssistantRemoteNotice {
|
|
95
|
-
|
|
95
|
+
/**
|
|
96
|
+
* `resolved` (only for a terminal's request, `ask.origin` "terminal"): a request this channel was sent as a card was answered, or stopped
|
|
97
|
+
* waiting, somewhere else (`resolved`), so a channel that still shows that card marks it so in place; the text says the same in one line
|
|
98
|
+
* for a channel that does not.
|
|
99
|
+
*/
|
|
100
|
+
kind: "done" | "needs_you" | "error" | "resolved";
|
|
96
101
|
session_id: string;
|
|
97
102
|
machine: string;
|
|
98
103
|
agent: string;
|
|
99
104
|
ask?: AssistantRemoteAsk;
|
|
105
|
+
/** With `kind: "resolved"`: which request, and who ended it (`terminal`, or `timeout`, `session_end`, `connector_restart` when nobody did). */
|
|
106
|
+
resolved?: {
|
|
107
|
+
action_id: string;
|
|
108
|
+
by: string;
|
|
109
|
+
};
|
|
100
110
|
}
|
|
101
111
|
/** A permission request a development environment's agent waits on (`permissionRequest`), as its connector posted it. */
|
|
102
112
|
export interface AssistantRemoteAsk {
|
|
@@ -110,6 +120,25 @@ export interface AssistantRemoteAsk {
|
|
|
110
120
|
}>;
|
|
111
121
|
/** When the connector stops waiting for an answer (it refuses the request then); absent when it did not say. */
|
|
112
122
|
expires_at?: string;
|
|
123
|
+
/**
|
|
124
|
+
* `terminal`: asked in a terminal on the machine (a conversation its person started there), whose own prompt stays open: whichever
|
|
125
|
+
* answer comes first is taken. Absent for a session opened through Claw.
|
|
126
|
+
*/
|
|
127
|
+
origin?: "terminal";
|
|
128
|
+
/** A terminal's request: what kind of prompt it is (`execute`, `edit`, `question`, `switch_mode` for a plan, `mcp`, `other`). */
|
|
129
|
+
kind?: string;
|
|
130
|
+
/**
|
|
131
|
+
* A terminal's request: what it would do, whole, as its connector posted it (the command, the edit, the plan, the question): a channel
|
|
132
|
+
* shows all of it, or offers only the options that refuse. Absent when it was too large to carry whole; the options are then only
|
|
133
|
+
* those that refuse.
|
|
134
|
+
*/
|
|
135
|
+
input?: unknown;
|
|
136
|
+
/** A terminal's request: the conversation's title (its project or session name) and the folder it runs in; plain one-line text. */
|
|
137
|
+
title?: string;
|
|
138
|
+
cwd?: string;
|
|
139
|
+
/** A terminal's question asked as several cards: which this is (from 0), and how many. */
|
|
140
|
+
question_index?: number;
|
|
141
|
+
question_count?: number;
|
|
113
142
|
}
|
|
114
143
|
/** Something the assistant chose to say to the user. */
|
|
115
144
|
export interface AssistantSayPayload extends AssistantEventBase {
|
|
@@ -155,6 +155,40 @@ export interface RemoteSessionNew {
|
|
|
155
155
|
takeover: boolean;
|
|
156
156
|
}
|
|
157
157
|
export declare function parseSessionNew(body: unknown): Parsed<RemoteSessionNew>;
|
|
158
|
+
/**
|
|
159
|
+
* Approvals a terminal asks for, carried to the phone (`POST /v1/remote-agents/connectors/:cid/terminal-sessions`).
|
|
160
|
+
*
|
|
161
|
+
* A coding agent its person started in their own terminal asks there before it runs a command, edits a file, leaves plan mode or
|
|
162
|
+
* puts a multiple-choice question. A connector whose hook sees such a prompt opens (or finds) one session per terminal conversation
|
|
163
|
+
* (`ref`, the agent's own conversation id) and posts each prompt on it as a `permissionRequest`, with
|
|
164
|
+
* `data.origin` {@link REMOTE_ORIGIN_TERMINAL_HOOK}; the owner answers it as any other, with a `decision`. The terminal keeps its own
|
|
165
|
+
* prompt: whichever answer comes first is the one the agent takes, and the connector says which (`permission_resolved.by`).
|
|
166
|
+
*
|
|
167
|
+
* Such a session carries nothing else: no prompt, no turn, no mode. It is not one of the sessions a user opens, so it neither counts
|
|
168
|
+
* against {@link REMOTE_AGENT_LIMITS.maxActiveSessionsPerUser} nor is closed to make room for one.
|
|
169
|
+
*/
|
|
170
|
+
export declare const REMOTE_ORIGIN_TERMINAL_HOOK = "terminal_hook";
|
|
171
|
+
/** The only commands a terminal-hook session takes: an answer, and closing it. */
|
|
172
|
+
export declare const TERMINAL_HOOK_COMMANDS: readonly RemoteCommandType[];
|
|
173
|
+
export declare const TERMINAL_SESSION_LIMITS: {
|
|
174
|
+
/** Terminal-hook sessions one user may open per minute, across their machines (a new terminal conversation opens one). */
|
|
175
|
+
readonly opensPerMinute: 30;
|
|
176
|
+
/** Characters of a session's title (the conversation's name, or the start of its first prompt). */
|
|
177
|
+
readonly maxTitleChars: 80;
|
|
178
|
+
};
|
|
179
|
+
export interface RemoteTerminalSessionOpen {
|
|
180
|
+
agent_id: string;
|
|
181
|
+
/** The agent's own id of the conversation open in the terminal. */
|
|
182
|
+
ref: string;
|
|
183
|
+
cwd: string;
|
|
184
|
+
/** The conversation's name, or the start of its first prompt; empty when the connector has neither. */
|
|
185
|
+
title: string;
|
|
186
|
+
}
|
|
187
|
+
export declare function parseTerminalSessionOpen(body: unknown): Parsed<RemoteTerminalSessionOpen>;
|
|
188
|
+
/** The owner's switch for approvals from terminals (`GET`/`PUT /v1/remote-agents/approvals`). On unless they turned it off. */
|
|
189
|
+
export declare function parseApprovalsSetting(body: unknown): Parsed<{
|
|
190
|
+
enabled: boolean;
|
|
191
|
+
}>;
|
|
158
192
|
/**
|
|
159
193
|
* Hiding conversations of a machine from its owner's lists (`POST /v1/remote-agents/connectors/:cid/hidden`), and the
|
|
160
194
|
* listing that leaves them out (`GET /v1/remote-agents/connectors`, `?hidden=include` to list them too). Nothing on the
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ClawRemoteAgentsPort } from "../ports/claw/remote-agents.js";
|
|
2
|
+
import type { CommandHandler, CommandRoute } from "../ports/routing.js";
|
|
3
|
+
export declare class TerminalApprovalsCommand implements CommandHandler {
|
|
4
|
+
private readonly claw;
|
|
5
|
+
readonly name = "approvals";
|
|
6
|
+
constructor(claw: Pick<ClawRemoteAgentsPort, "approvals" | "setApprovals">);
|
|
7
|
+
run(route: CommandRoute, args: string): Promise<boolean>;
|
|
8
|
+
}
|