ftown-bridge 0.19.67 → 0.20.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/ask-question-hook.d.ts +46 -0
- package/dist/ask-question-hook.js +192 -0
- package/dist/ask-question-hook.js.map +1 -0
- package/dist/attention-events.d.ts +38 -2
- package/dist/attention-events.js +70 -0
- package/dist/attention-events.js.map +1 -1
- package/dist/attention-prompt-protocol.d.ts +92 -0
- package/dist/attention-prompt-protocol.js +28 -0
- package/dist/attention-prompt-protocol.js.map +1 -0
- package/dist/attention-prompt.d.ts +12 -0
- package/dist/attention-prompt.js +333 -0
- package/dist/attention-prompt.js.map +1 -0
- package/dist/attention-store.d.ts +29 -1
- package/dist/attention-store.js +218 -18
- package/dist/attention-store.js.map +1 -1
- package/dist/browser-access.js +7 -1
- package/dist/browser-access.js.map +1 -1
- package/dist/command-rpc.d.ts +8 -0
- package/dist/command-rpc.js +76 -18
- package/dist/command-rpc.js.map +1 -1
- package/dist/harness-commands.js +7 -0
- package/dist/harness-commands.js.map +1 -1
- package/dist/hook-installer.d.ts +2 -0
- package/dist/hook-installer.js +83 -9
- package/dist/hook-installer.js.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/local-api-server.d.ts +4 -1
- package/dist/local-api-server.js +148 -11
- package/dist/local-api-server.js.map +1 -1
- package/dist/mcp/fleet.d.ts +1 -1
- package/dist/mcp/fleet.js +2 -2
- package/dist/mcp/fleet.js.map +1 -1
- package/dist/mcp/security-events.js +3 -3
- package/dist/mcp/security-events.js.map +1 -1
- package/dist/mcp/server.js +59 -9
- package/dist/mcp/server.js.map +1 -1
- package/dist/preview-chat-protocol.d.ts +50 -0
- package/dist/preview-chat-protocol.js +9 -0
- package/dist/preview-chat-protocol.js.map +1 -0
- package/dist/preview-chat.d.ts +18 -0
- package/dist/preview-chat.js +155 -0
- package/dist/preview-chat.js.map +1 -0
- package/dist/security/bridge/account-state.d.ts +4 -4
- package/dist/security/bridge/audit.js +2 -2
- package/dist/security/bridge/audit.js.map +1 -1
- package/dist/security/bridge/identity.d.ts +3 -3
- package/dist/security/bridge/metadata.d.ts +2 -2
- package/dist/security/bridge/permissions.js +17 -4
- package/dist/security/bridge/permissions.js.map +1 -1
- package/dist/session-controller.d.ts +32 -0
- package/dist/session-controller.js +49 -0
- package/dist/session-controller.js.map +1 -1
- package/dist/sessions-commands.d.ts +5 -0
- package/dist/sessions-commands.js +218 -5
- package/dist/sessions-commands.js.map +1 -1
- package/dist/types.d.ts +4 -2
- package/package.json +2 -2
- package/skills/ftown/SKILL.md +35 -6
- package/skills/ftown/references/sessions.md +80 -5
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code PreToolUse hook for AskUserQuestion (`ftown-harness ask-hook`).
|
|
3
|
+
* Records the question with the bridge, blocks until the human answers in the
|
|
4
|
+
* dashboard, then returns the answer so Claude Code skips the terminal picker.
|
|
5
|
+
*
|
|
6
|
+
* Installed globally, so it runs for every Claude Code session on the machine:
|
|
7
|
+
* outside ftown sessions it must exit immediately and silently. It always exits
|
|
8
|
+
* 0 and writes to stdout only when it has an answer; any other outcome lets the
|
|
9
|
+
* normal terminal picker run.
|
|
10
|
+
*/
|
|
11
|
+
import { type AttentionAnswerEntry } from './attention-prompt-protocol.js';
|
|
12
|
+
export interface AskUserQuestionHookOutput {
|
|
13
|
+
hookSpecificOutput: {
|
|
14
|
+
hookEventName: 'PreToolUse';
|
|
15
|
+
permissionDecision: 'allow';
|
|
16
|
+
updatedInput: Record<string, unknown> & {
|
|
17
|
+
answers: Record<string, string>;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Claude Code's documented updatedInput for AskUserQuestion: the full original
|
|
23
|
+
* tool input plus `answers` keyed by the exact question text. Returns undefined
|
|
24
|
+
* when the answers do not line up with the original questions.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildAskUserQuestionHookOutput(toolInput: Record<string, unknown>, answers: AttentionAnswerEntry[]): AskUserQuestionHookOutput | undefined;
|
|
27
|
+
export interface AskHookResponse {
|
|
28
|
+
status: number;
|
|
29
|
+
body: unknown;
|
|
30
|
+
}
|
|
31
|
+
export interface AskHookDeps {
|
|
32
|
+
readStdin: () => Promise<string>;
|
|
33
|
+
env: Record<string, string | undefined>;
|
|
34
|
+
/** Local bridge API. Rejects when the bridge is unreachable. */
|
|
35
|
+
request: (method: 'GET' | 'POST', path: string, body?: unknown, signal?: AbortSignal) => Promise<AskHookResponse>;
|
|
36
|
+
now: () => number;
|
|
37
|
+
sleep: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
38
|
+
writeStdout: (text: string) => void;
|
|
39
|
+
writeStderr: (text: string) => void;
|
|
40
|
+
signal?: AbortSignal;
|
|
41
|
+
deadlineMs?: number;
|
|
42
|
+
}
|
|
43
|
+
/** Always resolves 0. Writes stdout only when there is an answer to hand back. */
|
|
44
|
+
export declare function runAskHook(deps: AskHookDeps): Promise<0>;
|
|
45
|
+
/** Wires real stdin/stdout, the bridge local API (same pointer + auth as the other harness commands) and signals. */
|
|
46
|
+
export declare function runAskHookCli(): Promise<number>;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code PreToolUse hook for AskUserQuestion (`ftown-harness ask-hook`).
|
|
3
|
+
* Records the question with the bridge, blocks until the human answers in the
|
|
4
|
+
* dashboard, then returns the answer so Claude Code skips the terminal picker.
|
|
5
|
+
*
|
|
6
|
+
* Installed globally, so it runs for every Claude Code session on the machine:
|
|
7
|
+
* outside ftown sessions it must exit immediately and silently. It always exits
|
|
8
|
+
* 0 and writes to stdout only when it has an answer; any other outcome lets the
|
|
9
|
+
* normal terminal picker run.
|
|
10
|
+
*/
|
|
11
|
+
import { ASK_HOOK_DEADLINE_MS, ATTENTION_ITEM_WAIT_MAX_SECONDS, } from './attention-prompt-protocol.js';
|
|
12
|
+
import { apiResponse, loadPointer } from './cli-client.js';
|
|
13
|
+
const SESSION_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
14
|
+
const RETRY_DELAY_MS = 2_000;
|
|
15
|
+
const OUTAGE_LIMIT_MS = 60_000;
|
|
16
|
+
const STDIN_TIMEOUT_MS = 10_000;
|
|
17
|
+
function isRecord(value) {
|
|
18
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Claude Code's documented updatedInput for AskUserQuestion: the full original
|
|
22
|
+
* tool input plus `answers` keyed by the exact question text. Returns undefined
|
|
23
|
+
* when the answers do not line up with the original questions.
|
|
24
|
+
*/
|
|
25
|
+
export function buildAskUserQuestionHookOutput(toolInput, answers) {
|
|
26
|
+
const questions = toolInput.questions;
|
|
27
|
+
if (!Array.isArray(questions) || questions.length !== answers.length)
|
|
28
|
+
return undefined;
|
|
29
|
+
// Claude Code keys answers by question text, so duplicate question texts collide (last one wins).
|
|
30
|
+
const byQuestion = {};
|
|
31
|
+
for (let i = 0; i < questions.length; i += 1) {
|
|
32
|
+
const question = questions[i];
|
|
33
|
+
const entry = answers[i];
|
|
34
|
+
if (!isRecord(question) || typeof question.question !== 'string' || !entry || !Array.isArray(entry.selected))
|
|
35
|
+
return undefined;
|
|
36
|
+
const options = Array.isArray(question.options) ? question.options : [];
|
|
37
|
+
const parts = [];
|
|
38
|
+
for (const index of entry.selected) {
|
|
39
|
+
const option = Number.isInteger(index) ? options[index] : undefined;
|
|
40
|
+
if (!isRecord(option) || typeof option.label !== 'string')
|
|
41
|
+
return undefined;
|
|
42
|
+
parts.push(option.label);
|
|
43
|
+
}
|
|
44
|
+
if (typeof entry.text === 'string' && entry.text.length > 0)
|
|
45
|
+
parts.push(entry.text);
|
|
46
|
+
byQuestion[question.question] = parts.join(', ');
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
hookSpecificOutput: {
|
|
50
|
+
hookEventName: 'PreToolUse',
|
|
51
|
+
permissionDecision: 'allow',
|
|
52
|
+
updatedInput: { ...toolInput, answers: byQuestion },
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function ok(response) {
|
|
57
|
+
return response.status >= 200 && response.status < 300;
|
|
58
|
+
}
|
|
59
|
+
/** Always resolves 0. Writes stdout only when there is an answer to hand back. */
|
|
60
|
+
export async function runAskHook(deps) {
|
|
61
|
+
const { signal } = deps;
|
|
62
|
+
const aborted = () => signal?.aborted === true;
|
|
63
|
+
try {
|
|
64
|
+
const sessionId = deps.env.FTOWN_SESSION_ID;
|
|
65
|
+
if (!sessionId || !SESSION_ID_PATTERN.test(sessionId))
|
|
66
|
+
return 0;
|
|
67
|
+
const input = JSON.parse(await deps.readStdin());
|
|
68
|
+
if (!isRecord(input) || input.tool_name !== 'AskUserQuestion' || !isRecord(input.tool_input) || typeof input.tool_use_id !== 'string')
|
|
69
|
+
return 0;
|
|
70
|
+
const toolInput = input.tool_input;
|
|
71
|
+
if (aborted())
|
|
72
|
+
return 0;
|
|
73
|
+
const base = `/api/sessions/${sessionId}`;
|
|
74
|
+
const session = await deps.request('GET', base, undefined, signal);
|
|
75
|
+
if (!ok(session) || !isRecord(session.body))
|
|
76
|
+
return 0;
|
|
77
|
+
const capabilities = session.body.capabilities;
|
|
78
|
+
const attention = isRecord(capabilities) ? capabilities.attention : undefined;
|
|
79
|
+
if (!isRecord(attention) || attention.prompt !== true)
|
|
80
|
+
return 0;
|
|
81
|
+
const askBody = { toolName: 'AskUserQuestion', toolUseId: input.tool_use_id, toolInput };
|
|
82
|
+
let asked;
|
|
83
|
+
try {
|
|
84
|
+
asked = await deps.request('POST', `${base}/attention/ask`, askBody, signal);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
if (aborted())
|
|
88
|
+
return 0;
|
|
89
|
+
// Transport failure: retry once. Safe because the store dedups unresolved items by tool_use_id.
|
|
90
|
+
await deps.sleep(RETRY_DELAY_MS, signal);
|
|
91
|
+
if (aborted())
|
|
92
|
+
return 0;
|
|
93
|
+
asked = await deps.request('POST', `${base}/attention/ask`, askBody, signal);
|
|
94
|
+
}
|
|
95
|
+
if (!ok(asked) || !isRecord(asked.body) || !isRecord(asked.body.event) || typeof asked.body.event.id !== 'string')
|
|
96
|
+
return 0;
|
|
97
|
+
const itemPath = `${base}/attention/${encodeURIComponent(asked.body.event.id)}`;
|
|
98
|
+
const deadlineAt = deps.now() + (deps.deadlineMs ?? ASK_HOOK_DEADLINE_MS);
|
|
99
|
+
let lastSuccess = deps.now();
|
|
100
|
+
while (!aborted()) {
|
|
101
|
+
const remaining = deadlineAt - deps.now();
|
|
102
|
+
if (remaining <= 0)
|
|
103
|
+
return 0;
|
|
104
|
+
const waitSeconds = Math.max(1, Math.min(ATTENTION_ITEM_WAIT_MAX_SECONDS, Math.ceil(remaining / 1000)));
|
|
105
|
+
let response;
|
|
106
|
+
try {
|
|
107
|
+
response = await deps.request('GET', `${itemPath}?waitSeconds=${waitSeconds}`, undefined, signal);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
if (aborted())
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
if (response && response.status === 404)
|
|
114
|
+
return 0;
|
|
115
|
+
if (response && ok(response) && isRecord(response.body) && isRecord(response.body.event)) {
|
|
116
|
+
lastSuccess = deps.now();
|
|
117
|
+
const item = response.body.event;
|
|
118
|
+
if (item.resolution === 'answered' && isRecord(item.answer) && Array.isArray(item.answer.answers)) {
|
|
119
|
+
const output = buildAskUserQuestionHookOutput(toolInput, item.answer.answers);
|
|
120
|
+
if (output && !aborted())
|
|
121
|
+
deps.writeStdout(JSON.stringify(output));
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
if (item.resolution !== undefined || item.resolvedAt !== undefined)
|
|
125
|
+
return 0;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (deps.now() - lastSuccess > OUTAGE_LIMIT_MS)
|
|
129
|
+
return 0;
|
|
130
|
+
await deps.sleep(RETRY_DELAY_MS, signal);
|
|
131
|
+
}
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (!aborted())
|
|
136
|
+
deps.writeStderr(`ftown ask-hook: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
137
|
+
return 0;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async function readStdinText() {
|
|
141
|
+
if (process.stdin.isTTY)
|
|
142
|
+
return '';
|
|
143
|
+
const chunks = [];
|
|
144
|
+
const killer = setTimeout(() => { process.stdin.destroy(); }, STDIN_TIMEOUT_MS);
|
|
145
|
+
killer.unref();
|
|
146
|
+
try {
|
|
147
|
+
for await (const chunk of process.stdin)
|
|
148
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
clearTimeout(killer);
|
|
152
|
+
}
|
|
153
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
154
|
+
}
|
|
155
|
+
function abortableSleep(ms, signal) {
|
|
156
|
+
return new Promise((resolve) => {
|
|
157
|
+
if (signal?.aborted) {
|
|
158
|
+
resolve();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const done = () => { clearTimeout(timer); signal?.removeEventListener('abort', done); resolve(); };
|
|
162
|
+
const timer = setTimeout(done, ms);
|
|
163
|
+
signal?.addEventListener('abort', done, { once: true });
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/** Wires real stdin/stdout, the bridge local API (same pointer + auth as the other harness commands) and signals. */
|
|
167
|
+
export async function runAskHookCli() {
|
|
168
|
+
const controller = new AbortController();
|
|
169
|
+
const stop = () => controller.abort();
|
|
170
|
+
process.once('SIGINT', stop);
|
|
171
|
+
process.once('SIGTERM', stop);
|
|
172
|
+
try {
|
|
173
|
+
return await runAskHook({
|
|
174
|
+
readStdin: readStdinText,
|
|
175
|
+
env: process.env,
|
|
176
|
+
request: async (method, path, body, requestSignal) => {
|
|
177
|
+
const result = await apiResponse(loadPointer(), method, path, body, undefined, requestSignal);
|
|
178
|
+
return { status: result.status, body: result.body };
|
|
179
|
+
},
|
|
180
|
+
now: () => Date.now(),
|
|
181
|
+
sleep: abortableSleep,
|
|
182
|
+
writeStdout: (text) => { process.stdout.write(text); },
|
|
183
|
+
writeStderr: (text) => { process.stderr.write(text); },
|
|
184
|
+
signal: controller.signal,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
process.off('SIGINT', stop);
|
|
189
|
+
process.off('SIGTERM', stop);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=ask-question-hook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask-question-hook.js","sourceRoot":"","sources":["../src/ask-question-hook.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type AttentionAnswer, type AttentionPrompt } from './attention-prompt-protocol.js';
|
|
1
3
|
export type AttentionType = 'permission_prompt' | 'elicitation_dialog' | 'agent_needs_input' | 'ask_user';
|
|
2
|
-
export type Resolution = 'manual' | 'user_prompt' | 'elicitation_complete' | 'session_end';
|
|
4
|
+
export type Resolution = 'manual' | 'user_prompt' | 'elicitation_complete' | 'session_end' | 'answered' | 'tool_complete';
|
|
3
5
|
export interface Activity {
|
|
4
6
|
state: 'thinking' | 'tool_use' | 'idle' | 'unknown';
|
|
5
7
|
lastActivityAt?: string;
|
|
@@ -16,6 +18,8 @@ export interface AttentionItem {
|
|
|
16
18
|
acknowledgedAt?: string;
|
|
17
19
|
resolvedAt?: string;
|
|
18
20
|
resolution?: Resolution;
|
|
21
|
+
prompt?: AttentionPrompt;
|
|
22
|
+
answer?: AttentionAnswer;
|
|
19
23
|
}
|
|
20
24
|
export interface AttentionObservation {
|
|
21
25
|
activity?: Activity['state'];
|
|
@@ -24,9 +28,41 @@ export interface AttentionObservation {
|
|
|
24
28
|
title: string;
|
|
25
29
|
message: string;
|
|
26
30
|
sourceId?: string;
|
|
31
|
+
prompt?: AttentionPrompt;
|
|
27
32
|
};
|
|
28
|
-
resolve?: Exclude<Resolution, 'manual'>;
|
|
33
|
+
resolve?: Exclude<Resolution, 'manual' | 'answered' | 'tool_complete'>;
|
|
34
|
+
/** Raw tool_use_id of an ask tool that finished; resolves the matching ask_user item. */
|
|
35
|
+
toolComplete?: string;
|
|
29
36
|
}
|
|
37
|
+
export declare const promptSchema: z.ZodObject<{
|
|
38
|
+
channel: z.ZodEnum<{
|
|
39
|
+
mail: "mail";
|
|
40
|
+
hook: "hook";
|
|
41
|
+
}>;
|
|
42
|
+
questions: z.ZodArray<z.ZodObject<{
|
|
43
|
+
question: z.ZodString;
|
|
44
|
+
header: z.ZodOptional<z.ZodString>;
|
|
45
|
+
options: z.ZodArray<z.ZodObject<{
|
|
46
|
+
label: z.ZodString;
|
|
47
|
+
description: z.ZodOptional<z.ZodString>;
|
|
48
|
+
}, z.core.$strict>>;
|
|
49
|
+
multiSelect: z.ZodBoolean;
|
|
50
|
+
allowText: z.ZodBoolean;
|
|
51
|
+
}, z.core.$strict>>;
|
|
52
|
+
}, z.core.$strict>;
|
|
53
|
+
/** Secret redaction only (no cleaning, truncation or excerpting). */
|
|
54
|
+
export declare function redactAttentionSecrets(value: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* Redacts every prompt string, then re-checks limits, unique labels and the free-text rule.
|
|
57
|
+
* Never throws; the error names what to fix.
|
|
58
|
+
*/
|
|
59
|
+
export declare function normalizeAttentionPrompt(input: unknown): {
|
|
60
|
+
ok: true;
|
|
61
|
+
value: AttentionPrompt;
|
|
62
|
+
} | {
|
|
63
|
+
ok: false;
|
|
64
|
+
error: string;
|
|
65
|
+
};
|
|
30
66
|
/** Only the selected human-facing excerpt crosses the API; never the hook payload. */
|
|
31
67
|
export declare function sanitizeAttentionText(value: string, maxLength: number): string;
|
|
32
68
|
export declare function classifyAttentionHook(eventName: string, data: Record<string, unknown>): AttentionObservation;
|
package/dist/attention-events.js
CHANGED
|
@@ -1,3 +1,64 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ATTENTION_PROMPT_LIMITS as L } from './attention-prompt-protocol.js';
|
|
3
|
+
import { promptFromAskUserQuestionInput } from './attention-prompt.js';
|
|
4
|
+
export const promptSchema = z.object({
|
|
5
|
+
channel: z.enum(['mail', 'hook']),
|
|
6
|
+
questions: z.array(z.object({
|
|
7
|
+
question: z.string().min(1).max(L.questionChars),
|
|
8
|
+
header: z.string().max(L.headerChars).optional(),
|
|
9
|
+
options: z.array(z.object({
|
|
10
|
+
label: z.string().min(1).max(L.optionLabelChars),
|
|
11
|
+
description: z.string().max(L.optionDescriptionChars).optional(),
|
|
12
|
+
}).strict()).max(L.options),
|
|
13
|
+
multiSelect: z.boolean(),
|
|
14
|
+
allowText: z.boolean(),
|
|
15
|
+
}).strict()).min(1).max(L.questions),
|
|
16
|
+
}).strict();
|
|
17
|
+
/** Secret redaction only (no cleaning, truncation or excerpting). */
|
|
18
|
+
export function redactAttentionSecrets(value) {
|
|
19
|
+
return value
|
|
20
|
+
.replace(/\b(Bearer\s+)[A-Za-z0-9._~+\/-]+=*/gi, '$1[redacted]')
|
|
21
|
+
.replace(/\b((?:[A-Z_]*(?:API_KEY|AUTH_TOKEN|ACCESS_TOKEN|SECRET|PASSWORD)[A-Z_]*|authorization)\s*[:=]\s*)[^\s,;]+/gi, '$1[redacted]')
|
|
22
|
+
.replace(/\b(?:sk-(?:ant-)?[A-Za-z0-9_-]{12,}|gh[pousr]_[A-Za-z0-9]{20,})\b/g, '[redacted]');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Redacts every prompt string, then re-checks limits, unique labels and the free-text rule.
|
|
26
|
+
* Never throws; the error names what to fix.
|
|
27
|
+
*/
|
|
28
|
+
export function normalizeAttentionPrompt(input) {
|
|
29
|
+
const shape = promptSchema.safeParse(input);
|
|
30
|
+
if (!shape.success) {
|
|
31
|
+
const issue = shape.error.issues[0];
|
|
32
|
+
return { ok: false, error: `Invalid prompt${issue ? ` at ${issue.path.join('.') || 'prompt'}: ${issue.message}` : ''}` };
|
|
33
|
+
}
|
|
34
|
+
const prompt = shape.data;
|
|
35
|
+
const redacted = {
|
|
36
|
+
channel: prompt.channel,
|
|
37
|
+
questions: prompt.questions.map((q) => ({
|
|
38
|
+
question: redactAttentionSecrets(q.question),
|
|
39
|
+
...(q.header !== undefined ? { header: redactAttentionSecrets(q.header) } : {}),
|
|
40
|
+
options: q.options.map((o) => ({
|
|
41
|
+
label: redactAttentionSecrets(o.label),
|
|
42
|
+
...(o.description !== undefined ? { description: redactAttentionSecrets(o.description) } : {}),
|
|
43
|
+
})),
|
|
44
|
+
multiSelect: q.multiSelect,
|
|
45
|
+
allowText: q.allowText,
|
|
46
|
+
})),
|
|
47
|
+
};
|
|
48
|
+
const parsed = promptSchema.safeParse(redacted);
|
|
49
|
+
if (!parsed.success) {
|
|
50
|
+
const issue = parsed.error.issues[0];
|
|
51
|
+
return { ok: false, error: `Invalid prompt${issue ? ` at ${issue.path.join('.') || 'prompt'}: ${issue.message}` : ''}` };
|
|
52
|
+
}
|
|
53
|
+
for (const [i, q] of parsed.data.questions.entries()) {
|
|
54
|
+
const labels = new Set(q.options.map((o) => o.label.toLowerCase()));
|
|
55
|
+
if (labels.size !== q.options.length)
|
|
56
|
+
return { ok: false, error: `Invalid prompt at questions.${i}.options: labels must be unique` };
|
|
57
|
+
if (!q.options.length && (!q.allowText || q.multiSelect))
|
|
58
|
+
return { ok: false, error: `Invalid prompt at questions.${i}: a question without options must allow text and not be multi-select` };
|
|
59
|
+
}
|
|
60
|
+
return { ok: true, value: parsed.data };
|
|
61
|
+
}
|
|
1
62
|
/** Only the selected human-facing excerpt crosses the API; never the hook payload. */
|
|
2
63
|
export function sanitizeAttentionText(value, maxLength) {
|
|
3
64
|
return value
|
|
@@ -61,6 +122,15 @@ export function classifyAttentionHook(eventName, data) {
|
|
|
61
122
|
message: sanitizeAttentionText(question(data) ?? 'Open the session to respond.', 2000),
|
|
62
123
|
...(sourceId ? { sourceId: sourceId.slice(0, 200) } : {}),
|
|
63
124
|
};
|
|
125
|
+
if (tool === 'askuserquestion') {
|
|
126
|
+
const raw = promptFromAskUserQuestionInput(data.tool_input ?? data.toolInput ?? data.input);
|
|
127
|
+
const checked = raw ? normalizeAttentionPrompt(raw) : undefined;
|
|
128
|
+
if (checked?.ok)
|
|
129
|
+
observation.attention.prompt = checked.value;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (['PostToolUse', 'postToolUse'].includes(eventName) && tool && askTools.has(tool) && sourceId) {
|
|
133
|
+
observation.toolComplete = sourceId.slice(0, 200);
|
|
64
134
|
}
|
|
65
135
|
return observation;
|
|
66
136
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attention-events.js","sourceRoot":"","sources":["../src/attention-events.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"attention-events.js","sourceRoot":"","sources":["../src/attention-events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured questions an attention request can carry, and the human's answer.
|
|
3
|
+
* Shared by the bridge, the ftown CLI, MCP, the harness hook and the UI, so it
|
|
4
|
+
* must stay free of Node built-ins and runtime dependencies.
|
|
5
|
+
*/
|
|
6
|
+
export declare const ATTENTION_PROMPT_LIMITS: {
|
|
7
|
+
readonly questions: 4;
|
|
8
|
+
readonly questionChars: 500;
|
|
9
|
+
readonly headerChars: 40;
|
|
10
|
+
readonly options: 8;
|
|
11
|
+
readonly optionLabelChars: 80;
|
|
12
|
+
readonly optionDescriptionChars: 200;
|
|
13
|
+
readonly answerTextChars: 4000;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* A waiter counts as live while a long-poll is active and this long after its
|
|
17
|
+
* latest long-poll ended normally (an aborted poll ends liveness at once). Kept
|
|
18
|
+
* short: a waiter that died between polls must not swallow an answer that
|
|
19
|
+
* should have fallen back to mail.
|
|
20
|
+
*/
|
|
21
|
+
export declare const ATTENTION_WAITER_TTL_MS = 10000;
|
|
22
|
+
/** Longest single long-poll on one attention item, in seconds. */
|
|
23
|
+
export declare const ATTENTION_ITEM_WAIT_MAX_SECONDS = 25;
|
|
24
|
+
/** Claude Code `timeout` for the AskUserQuestion hook, in seconds. */
|
|
25
|
+
export declare const ASK_HOOK_TIMEOUT_SECONDS = 3600;
|
|
26
|
+
/** The hook gives up before Claude Code kills it, so the terminal picker can take over. */
|
|
27
|
+
export declare const ASK_HOOK_DEADLINE_MS = 3540000;
|
|
28
|
+
export interface AttentionPromptOption {
|
|
29
|
+
label: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface AttentionPromptQuestion {
|
|
33
|
+
question: string;
|
|
34
|
+
header?: string;
|
|
35
|
+
/** May be empty only when `allowText` is true (a free-text question). */
|
|
36
|
+
options: AttentionPromptOption[];
|
|
37
|
+
multiSelect: boolean;
|
|
38
|
+
/** The human may type their own answer or note ("manual input"). */
|
|
39
|
+
allowText: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* `mail`: an agent asked through ftown; a live waiter receives the answer,
|
|
43
|
+
* otherwise it is mailed to the session.
|
|
44
|
+
* `hook`: a blocking harness hook (Claude Code AskUserQuestion) receives it;
|
|
45
|
+
* there is no mail fallback.
|
|
46
|
+
*/
|
|
47
|
+
export type AttentionAnswerChannel = 'mail' | 'hook';
|
|
48
|
+
export interface AttentionPrompt {
|
|
49
|
+
channel: AttentionAnswerChannel;
|
|
50
|
+
questions: AttentionPromptQuestion[];
|
|
51
|
+
}
|
|
52
|
+
/** Answer to `prompt.questions[i]` is `answers[i]`. */
|
|
53
|
+
export interface AttentionAnswerEntry {
|
|
54
|
+
/** Indexes into the question's `options`, unique, ascending. */
|
|
55
|
+
selected: number[];
|
|
56
|
+
text?: string;
|
|
57
|
+
}
|
|
58
|
+
export interface AttentionAnswer {
|
|
59
|
+
answers: AttentionAnswerEntry[];
|
|
60
|
+
answeredAt: string;
|
|
61
|
+
}
|
|
62
|
+
/** What callers send as `questions` (local API, MCP, CLI --questions-json). */
|
|
63
|
+
export interface AttentionPromptQuestionInput {
|
|
64
|
+
question: string;
|
|
65
|
+
header?: string;
|
|
66
|
+
options?: Array<string | AttentionPromptOption>;
|
|
67
|
+
multiSelect?: boolean;
|
|
68
|
+
allowText?: boolean;
|
|
69
|
+
}
|
|
70
|
+
/** Body of POST /api/sessions/:id/attention/ask (harness hook only). */
|
|
71
|
+
export interface AttentionAskRequest {
|
|
72
|
+
toolName: string;
|
|
73
|
+
toolUseId: string;
|
|
74
|
+
toolInput: unknown;
|
|
75
|
+
}
|
|
76
|
+
/** Browser -> bridge E2EE command `answer_session_attention` (control permission). */
|
|
77
|
+
export interface AnswerSessionAttentionPayload {
|
|
78
|
+
bridgeId: string;
|
|
79
|
+
sessionId: string;
|
|
80
|
+
eventId: string;
|
|
81
|
+
answers: AttentionAnswerEntry[];
|
|
82
|
+
}
|
|
83
|
+
/** `waiter`: a live long-poll took the answer. `mail`: mailed to the session. */
|
|
84
|
+
export type AttentionAnswerDelivery = 'waiter' | 'mail' | 'mail_failed';
|
|
85
|
+
export type AttentionAnswerErrorCode = 'attention_not_found' | 'attention_not_answerable' | 'attention_already_resolved' | 'invalid_answer' | 'answer_waiter_gone';
|
|
86
|
+
export type AttentionParseResult<T> = {
|
|
87
|
+
ok: true;
|
|
88
|
+
value: T;
|
|
89
|
+
} | {
|
|
90
|
+
ok: false;
|
|
91
|
+
error: string;
|
|
92
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured questions an attention request can carry, and the human's answer.
|
|
3
|
+
* Shared by the bridge, the ftown CLI, MCP, the harness hook and the UI, so it
|
|
4
|
+
* must stay free of Node built-ins and runtime dependencies.
|
|
5
|
+
*/
|
|
6
|
+
export const ATTENTION_PROMPT_LIMITS = {
|
|
7
|
+
questions: 4,
|
|
8
|
+
questionChars: 500,
|
|
9
|
+
headerChars: 40,
|
|
10
|
+
options: 8,
|
|
11
|
+
optionLabelChars: 80,
|
|
12
|
+
optionDescriptionChars: 200,
|
|
13
|
+
answerTextChars: 4000,
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* A waiter counts as live while a long-poll is active and this long after its
|
|
17
|
+
* latest long-poll ended normally (an aborted poll ends liveness at once). Kept
|
|
18
|
+
* short: a waiter that died between polls must not swallow an answer that
|
|
19
|
+
* should have fallen back to mail.
|
|
20
|
+
*/
|
|
21
|
+
export const ATTENTION_WAITER_TTL_MS = 10_000;
|
|
22
|
+
/** Longest single long-poll on one attention item, in seconds. */
|
|
23
|
+
export const ATTENTION_ITEM_WAIT_MAX_SECONDS = 25;
|
|
24
|
+
/** Claude Code `timeout` for the AskUserQuestion hook, in seconds. */
|
|
25
|
+
export const ASK_HOOK_TIMEOUT_SECONDS = 3600;
|
|
26
|
+
/** The hook gives up before Claude Code kills it, so the terminal picker can take over. */
|
|
27
|
+
export const ASK_HOOK_DEADLINE_MS = 3_540_000;
|
|
28
|
+
//# sourceMappingURL=attention-prompt-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention-prompt-protocol.js","sourceRoot":"","sources":["../src/attention-prompt-protocol.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,SAAS,EAAE,CAAC;IACZ,aAAa,EAAE,GAAG;IAClB,WAAW,EAAE,EAAE;IACf,OAAO,EAAE,CAAC;IACV,gBAAgB,EAAE,EAAE;IACpB,sBAAsB,EAAE,GAAG;IAC3B,eAAe,EAAE,IAAI;CACb,CAAC;AAEX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAC9C,kEAAkE;AAClE,MAAM,CAAC,MAAM,+BAA+B,GAAG,EAAE,CAAC;AAClD,sEAAsE;AACtE,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAC7C,2FAA2F;AAC3F,MAAM,CAAC,MAAM,oBAAoB,GAAG,SAAS,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type AttentionAnswerChannel, type AttentionAnswerEntry, type AttentionParseResult, type AttentionPrompt, type AttentionPromptQuestion } from './attention-prompt-protocol.js';
|
|
2
|
+
export declare function parseAttentionPrompt(questions: unknown, channel: AttentionAnswerChannel): AttentionParseResult<AttentionPrompt>;
|
|
3
|
+
export declare function promptFromAskUserQuestionInput(toolInput: unknown): AttentionPrompt | undefined;
|
|
4
|
+
export declare function validateAttentionAnswer(prompt: AttentionPrompt, answers: unknown): AttentionParseResult<AttentionAnswerEntry[]>;
|
|
5
|
+
export declare function answerLabels(question: AttentionPromptQuestion, entry: AttentionAnswerEntry): string[];
|
|
6
|
+
export declare function formatAttentionAnswerText(prompt: AttentionPrompt, answers: AttentionAnswerEntry[]): string;
|
|
7
|
+
export declare function formatAttentionAnswerMail(input: {
|
|
8
|
+
attentionId: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
prompt: AttentionPrompt;
|
|
11
|
+
answers: AttentionAnswerEntry[];
|
|
12
|
+
}): string;
|