@xingular/harness 1.0.0 → 1.1.1
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/CHANGELOG.md +33 -5
- package/README.md +70 -19
- package/dist/cli.js +10321 -5785
- package/dist/sdk/agents/builtin.d.ts +2 -0
- package/dist/sdk/agents/builtin.js +34 -0
- package/dist/sdk/agents/narrow.d.ts +13 -0
- package/dist/sdk/agents/narrow.js +29 -0
- package/dist/sdk/agents/profiles.d.ts +80 -0
- package/dist/sdk/agents/profiles.js +221 -0
- package/dist/sdk/annotations/bridge.d.ts +42 -0
- package/dist/sdk/annotations/bridge.js +264 -0
- package/dist/sdk/annotations/capture.d.ts +116 -0
- package/dist/sdk/annotations/capture.js +308 -0
- package/dist/sdk/annotations/delivery.d.ts +72 -0
- package/dist/sdk/annotations/delivery.js +130 -0
- package/dist/sdk/annotations/inspect.d.ts +10 -0
- package/dist/sdk/annotations/inspect.js +33 -0
- package/dist/sdk/annotations/isolated.d.ts +6 -0
- package/dist/sdk/annotations/isolated.js +51 -0
- package/dist/sdk/annotations/pageScript.d.ts +28 -0
- package/dist/sdk/annotations/pageScript.js +570 -0
- package/dist/sdk/annotations/part.d.ts +10 -0
- package/dist/sdk/annotations/part.js +144 -0
- package/dist/sdk/annotations/queue.d.ts +4 -0
- package/dist/sdk/annotations/queue.js +40 -0
- package/dist/sdk/annotations/serverMethods.d.ts +3 -0
- package/dist/sdk/annotations/serverMethods.js +62 -0
- package/dist/sdk/annotations/store.d.ts +160 -0
- package/dist/sdk/annotations/store.js +386 -0
- package/dist/sdk/browser/index.d.ts +14 -3
- package/dist/sdk/browser/index.js +11 -4
- package/dist/sdk/browser/serverMethods.d.ts +3 -0
- package/dist/sdk/browser/serverMethods.js +7 -0
- package/dist/sdk/browser/session.d.ts +1 -0
- package/dist/sdk/browser/session.js +45 -9
- package/dist/sdk/cli.js +19 -11
- package/dist/sdk/core/bus.js +1 -1
- package/dist/sdk/core/env.d.ts +1 -1
- package/dist/sdk/core/env.js +1 -1
- package/dist/sdk/doctor/serverMethods.d.ts +3 -0
- package/dist/sdk/doctor/serverMethods.js +8 -0
- package/dist/sdk/harness.d.ts +15 -0
- package/dist/sdk/harness.js +44 -5
- package/dist/sdk/history/recall.js +8 -8
- package/dist/sdk/history/serverMethods.d.ts +3 -0
- package/dist/sdk/history/serverMethods.js +8 -0
- package/dist/sdk/hub/cli.d.ts +1 -1
- package/dist/sdk/hub/cli.js +1 -1
- package/dist/sdk/hub/schema.d.ts +1 -1
- package/dist/sdk/hub/serverMethods.d.ts +3 -0
- package/dist/sdk/hub/serverMethods.js +11 -0
- package/dist/sdk/index.d.ts +10 -0
- package/dist/sdk/index.js +7 -0
- package/dist/sdk/mcp/connections.js +14 -7
- package/dist/sdk/mcp/serverMethods.d.ts +3 -0
- package/dist/sdk/mcp/serverMethods.js +22 -0
- package/dist/sdk/plugins/builtin.d.ts +1 -0
- package/dist/sdk/plugins/builtin.js +128 -33
- package/dist/sdk/plugins/builtinRegistry.d.ts +53 -0
- package/dist/sdk/plugins/builtinRegistry.js +49 -0
- package/dist/sdk/plugins/cli.d.ts +1 -1
- package/dist/sdk/plugins/cli.js +4 -3
- package/dist/sdk/prompt/compile.d.ts +5 -2
- package/dist/sdk/prompt/compile.js +25 -8
- package/dist/sdk/refine/cli.d.ts +1 -1
- package/dist/sdk/refine/cli.js +2 -2
- package/dist/sdk/refine/serverMethods.d.ts +3 -0
- package/dist/sdk/refine/serverMethods.js +20 -0
- package/dist/sdk/server/methods.js +9 -86
- package/dist/sdk/server/params.d.ts +4 -0
- package/dist/sdk/server/params.js +11 -0
- package/dist/sdk/sessions/contracts.d.ts +17 -0
- package/dist/sdk/sessions/contracts.js +42 -3
- package/dist/sdk/sessions/planMode.d.ts +5 -2
- package/dist/sdk/sessions/planMode.js +12 -3
- package/dist/sdk/sessions/reasoningSummaries.d.ts +2 -2
- package/dist/sdk/sessions/reasoningSummaries.js +3 -3
- package/dist/sdk/sessions/registry.d.ts +1 -0
- package/dist/sdk/sessions/registry.js +4 -1
- package/dist/sdk/sessions/runIndex.js +7 -7
- package/dist/sdk/sessions/runStore.js +3 -1
- package/dist/sdk/sessions/runner.js +2 -2
- package/dist/sdk/sessions/sessions.d.ts +17 -5
- package/dist/sdk/sessions/sessions.js +79 -12
- package/dist/sdk/sessions/sideQuestionServerMethods.d.ts +3 -0
- package/dist/sdk/sessions/sideQuestionServerMethods.js +19 -0
- package/dist/sdk/sessions/sideQuestions.d.ts +66 -0
- package/dist/sdk/sessions/sideQuestions.js +290 -0
- package/dist/sdk/sessions/subagentServerMethods.d.ts +3 -0
- package/dist/sdk/sessions/subagentServerMethods.js +41 -0
- package/dist/sdk/sessions/subagentService.d.ts +13 -2
- package/dist/sdk/sessions/subagentService.js +34 -16
- package/dist/sdk/sessions/subagents.d.ts +9 -1
- package/dist/sdk/sessions/subagents.js +19 -0
- package/dist/sdk/terminal/browser.d.ts +62 -0
- package/dist/sdk/terminal/browser.js +41 -0
- package/dist/sdk/terminal/browserControl.d.ts +29 -0
- package/dist/sdk/terminal/browserControl.js +186 -0
- package/dist/sdk/terminal/commandHelp.d.ts +17 -0
- package/dist/sdk/terminal/commandHelp.js +20 -0
- package/dist/sdk/terminal/controller.d.ts +7 -1
- package/dist/sdk/terminal/controller.js +34 -2
- package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +28 -0
- package/dist/sdk/terminal/opentui/subagentsWidget.js +52 -0
- package/dist/sdk/terminal/piExtension.js +33 -48
- package/dist/sdk/terminal/socket.d.ts +6 -3
- package/dist/sdk/terminal/socket.js +40 -9
- package/dist/sdk/terminal/standalone.d.ts +2 -0
- package/dist/sdk/terminal/standalone.js +50 -10
- package/dist/sdk/terminal/subagentMonitor.d.ts +38 -0
- package/dist/sdk/terminal/subagentMonitor.js +121 -0
- package/dist/sdk/terminal/terminals.d.ts +42 -6
- package/dist/sdk/terminal/terminals.js +359 -18
- package/dist/sdk/terminal/trajectory.js +2 -2
- package/dist/sdk/terminal/tui/annotateCommand.d.ts +3 -0
- package/dist/sdk/terminal/tui/annotateCommand.js +22 -0
- package/dist/sdk/terminal/tui/commandPages.d.ts +5 -1
- package/dist/sdk/terminal/tui/commandPages.js +54 -6
- package/dist/sdk/terminal/tui/commands.d.ts +8 -4
- package/dist/sdk/terminal/tui/commands.js +181 -58
- package/dist/sdk/terminal/tui/dialogGeometry.d.ts +2 -2
- package/dist/sdk/terminal/tui/dialogGeometry.js +4 -2
- package/dist/sdk/terminal/tui/dialogs.d.ts +6 -1
- package/dist/sdk/terminal/tui/dialogs.js +9 -2
- package/dist/sdk/terminal/tui/modelPicker.js +1 -6
- package/dist/sdk/terminal/tui/page.d.ts +3 -0
- package/dist/sdk/terminal/tui/pageOverlay.js +17 -6
- package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +26 -4
- package/dist/sdk/terminal/tui/pages/accountsPage.js +264 -36
- package/dist/sdk/terminal/tui/pages/agentsPage.d.ts +74 -0
- package/dist/sdk/terminal/tui/pages/agentsPage.js +190 -0
- package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +94 -0
- package/dist/sdk/terminal/tui/pages/annotatePage.js +341 -0
- package/dist/sdk/terminal/tui/pages/browserPage.d.ts +87 -0
- package/dist/sdk/terminal/tui/pages/browserPage.js +738 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +2 -0
- package/dist/sdk/terminal/tui/pages/doctorPage.js +103 -15
- package/dist/sdk/terminal/tui/pages/notesPage.d.ts +75 -0
- package/dist/sdk/terminal/tui/pages/notesPage.js +201 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +8 -0
- package/dist/sdk/terminal/tui/pages/settingsPage.js +16 -1
- package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +23 -0
- package/dist/sdk/terminal/tui/pages/subagentsPage.js +70 -0
- package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +12 -5
- package/dist/sdk/terminal/tui/pages/trajectoryPage.js +156 -83
- package/dist/sdk/terminal/tui/pages/worktreesPage.d.ts +72 -0
- package/dist/sdk/terminal/tui/pages/worktreesPage.js +203 -0
- package/dist/sdk/terminal/tui/planMode.d.ts +1 -1
- package/dist/sdk/terminal/tui/planMode.js +1 -1
- package/dist/sdk/terminal/tui/pluginCommands.d.ts +14 -0
- package/dist/sdk/terminal/tui/pluginCommands.js +27 -0
- package/dist/sdk/terminal/tui/questions.d.ts +2 -0
- package/dist/sdk/terminal/tui/runtime.js +4 -2
- package/dist/sdk/terminal/tui/settingsModel.d.ts +2 -0
- package/dist/sdk/terminal/tui/settingsModel.js +32 -2
- package/dist/sdk/terminal/tui/sideQuestion.d.ts +26 -26
- package/dist/sdk/terminal/tui/sideQuestion.js +131 -98
- package/dist/sdk/terminal/tui/ui.d.ts +2 -0
- package/dist/sdk/terminal/tui/ui.js +1 -0
- package/dist/sdk/tools/askUser.js +26 -8
- package/dist/sdk/tools/checklist.js +2 -2
- package/dist/sdk/tools/harnessTools.d.ts +2 -0
- package/dist/sdk/tools/harnessTools.js +1 -1
- package/dist/sdk/tools/spawnSubagent.js +13 -4
- package/dist/sdk/web/part.d.ts +10 -0
- package/dist/sdk/web/part.js +6 -0
- package/dist/sdk/web/serverMethods.d.ts +3 -0
- package/dist/sdk/web/serverMethods.js +7 -0
- package/dist/sdk/wiki/serverMethods.d.ts +3 -0
- package/dist/sdk/wiki/serverMethods.js +26 -0
- package/dist/sdk/worktrees/cli.d.ts +11 -0
- package/dist/sdk/worktrees/cli.js +146 -0
- package/dist/sdk/worktrees/copy.d.ts +9 -0
- package/dist/sdk/worktrees/copy.js +111 -0
- package/dist/sdk/worktrees/git.d.ts +16 -0
- package/dist/sdk/worktrees/git.js +81 -0
- package/dist/sdk/worktrees/serverMethods.d.ts +3 -0
- package/dist/sdk/worktrees/serverMethods.js +30 -0
- package/dist/sdk/worktrees/service.d.ts +73 -0
- package/dist/sdk/worktrees/service.js +483 -0
- package/dist/sdk/worktrees/setup.d.ts +23 -0
- package/dist/sdk/worktrees/setup.js +65 -0
- package/dist/sdk/worktrees/types.d.ts +91 -0
- package/dist/sdk/worktrees/types.js +1 -0
- package/dist/sdk/world/cli.d.ts +2 -2
- package/dist/sdk/world/cli.js +6 -6
- package/dist/sdk/world/world.d.ts +3 -1
- package/dist/sdk/world/world.js +8 -9
- package/dist/terminal-opentui.js +14540 -7374
- package/dist/terminal-pi.js +285 -666
- package/docs/README.md +22 -2
- package/docs/architecture/00-overview.md +28 -16
- package/docs/architecture/02-sessions-and-runs.md +72 -15
- package/docs/architecture/03-variants.md +10 -2
- package/docs/architecture/04-prompt.md +26 -6
- package/docs/architecture/06-plugins-and-capabilities.md +39 -3
- package/docs/architecture/07-tools-and-gate.md +21 -6
- package/docs/architecture/09-accounts-and-connectors.md +8 -0
- package/docs/architecture/10-server-protocol.md +111 -2
- package/docs/architecture/11-terminal.md +185 -5
- package/docs/architecture/12-storage.md +15 -1
- package/docs/architecture/13-world-tools.md +28 -6
- package/docs/architecture/14-surfaces-and-parity.md +89 -0
- package/docs/architecture/README.md +15 -4
- package/docs/architecture/proposals/PROP-0006-run-checklist.md +4 -2
- package/docs/architecture/proposals/PROP-0013-resource-browser.md +58 -0
- package/docs/architecture/proposals/PROP-0014-fullscreen-browser-tabs.md +58 -0
- package/docs/architecture/proposals/PROP-0015-one-capability-surface.md +88 -0
- package/docs/architecture/proposals/PROP-0016-managed-worktrees.md +156 -0
- package/docs/architecture/proposals/PROP-0017-page-annotations.md +127 -0
- package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +179 -0
- package/docs/architecture/proposals/PROP-0019-contextual-side-questions.md +122 -0
- package/docs/architecture/proposals/PROP-0020-builtin-feature-plugins.md +105 -0
- package/docs/architecture/proposals/PROP-0021-command-namespaces-and-duplicates.md +99 -0
- package/docs/architecture/proposals/PROP-0022-plan-only-questions.md +102 -0
- package/docs/architecture/proposals/PROP-0023-xagent-cli-name.md +61 -0
- package/docs/architecture/proposals/PROP-0024-question-sketch-previews.md +130 -0
- package/docs/architecture/proposals/PROP-0025-contextual-question-wireframes.md +49 -0
- package/docs/architecture/proposals/PROP-0026-connected-sketch-display.md +94 -0
- package/docs/architecture/proposals/PROP-0027-xingular-agent-product-identity.md +60 -0
- package/docs/architecture/proposals/PROP-0028-normalize-question-wireframes.md +55 -0
- package/docs/architecture/proposals/PROP-0029-agent-profiles.md +124 -0
- package/docs/architecture/proposals/PROP-0030-subagent-title.md +86 -0
- package/docs/architecture/proposals/README.md +25 -0
- package/docs/decisions/ADR-0009-xharness-command.md +6 -5
- package/docs/decisions/ADR-0014-public-release-baseline.md +3 -2
- package/docs/decisions/ADR-0015-resource-browser.md +28 -0
- package/docs/decisions/ADR-0016-fullscreen-browser-tabs.md +31 -0
- package/docs/decisions/ADR-0017-one-capability-surface.md +34 -0
- package/docs/decisions/ADR-0018-builtin-feature-plugins.md +35 -0
- package/docs/decisions/ADR-0019-managed-git-worktrees.md +31 -0
- package/docs/decisions/ADR-0020-page-annotations.md +37 -0
- package/docs/decisions/ADR-0021-required-command-namespaces.md +40 -0
- package/docs/decisions/ADR-0022-plan-only-question-tools.md +34 -0
- package/docs/decisions/ADR-0023-contextual-side-question-runs.md +35 -0
- package/docs/decisions/ADR-0024-xagent-command.md +40 -0
- package/docs/decisions/ADR-0025-question-choice-sketches.md +49 -0
- package/docs/decisions/ADR-0026-contextual-question-wireframes.md +58 -0
- package/docs/decisions/ADR-0027-connected-sketch-display.md +60 -0
- package/docs/decisions/ADR-0028-xingular-agent-product-identity.md +56 -0
- package/docs/decisions/ADR-0029-normalized-question-wireframe-borders.md +33 -0
- package/docs/decisions/ADR-0030-agent-profiles.md +43 -0
- package/docs/doctor.md +10 -4
- package/docs/features/README.md +26 -0
- package/docs/features/interfaces/README.md +1 -1
- package/docs/future-work.md +48 -0
- package/docs/parity.md +78 -0
- package/docs/releases/1.0.0.md +2 -2
- package/docs/releases/4.0.0.md +46 -0
- package/docs/releases/README.md +20 -15
- package/docs/study/00-index.md +5 -0
- package/docs/study/autonomous-engineering-sources.md +277 -0
- package/docs/study/autonomy-sources.md +147 -0
- package/docs/study/proactive-autonomy.md +408 -0
- package/docs/study/tui-resource-browser.md +78 -0
- package/docs/terminal-tui.md +325 -93
- package/package.json +3 -3
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { Bus } from "../core/bus.js";
|
|
2
|
+
import * as store from "./store.js";
|
|
3
|
+
export interface TerminalSender {
|
|
4
|
+
has(sessionId: string): boolean;
|
|
5
|
+
send(input: {
|
|
6
|
+
sessionId: string;
|
|
7
|
+
text: string;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
runId: string;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
export type SendResult = {
|
|
13
|
+
state: 'sent';
|
|
14
|
+
runId: string;
|
|
15
|
+
ids: string[];
|
|
16
|
+
} | {
|
|
17
|
+
state: 'busy';
|
|
18
|
+
ids: string[];
|
|
19
|
+
} | {
|
|
20
|
+
state: 'host';
|
|
21
|
+
text: string;
|
|
22
|
+
ids: string[];
|
|
23
|
+
} | {
|
|
24
|
+
state: 'empty';
|
|
25
|
+
ids: [];
|
|
26
|
+
};
|
|
27
|
+
export type NoteAction = 'save' | 'queue' | 'send';
|
|
28
|
+
export interface NoteDeliveryOptions {
|
|
29
|
+
root: () => string;
|
|
30
|
+
bus: Bus;
|
|
31
|
+
terminals: () => TerminalSender | null;
|
|
32
|
+
}
|
|
33
|
+
export declare class NoteDeliveries {
|
|
34
|
+
private readonly options;
|
|
35
|
+
private readonly sending;
|
|
36
|
+
constructor(options: NoteDeliveryOptions);
|
|
37
|
+
private changed;
|
|
38
|
+
queue(id: string, expectedVersion: number | undefined, sessionId: string, options?: {
|
|
39
|
+
autoSend?: boolean;
|
|
40
|
+
confirm?: boolean;
|
|
41
|
+
}): store.Note;
|
|
42
|
+
dequeue(id: string, expectedVersion: number | undefined): store.Note;
|
|
43
|
+
/**
|
|
44
|
+
* Creates a note and acts on it in one step: what the popover's three buttons do. `trusted` is
|
|
45
|
+
* false when the comment came from a script the page could reach; a send then waits in the queue
|
|
46
|
+
* for the person to confirm it.
|
|
47
|
+
*/
|
|
48
|
+
createAndAct(input: store.NoteInput, action: NoteAction, sessionId: string | null, trusted: boolean): Promise<{
|
|
49
|
+
note: store.Note;
|
|
50
|
+
send: SendResult | null;
|
|
51
|
+
}>;
|
|
52
|
+
/** The person confirms a note the page could have forged; it is then sent with the next send. */
|
|
53
|
+
confirm(id: string, expectedVersion: number | undefined): store.Note;
|
|
54
|
+
/** The instruction for the session's queued notes (or the given ones), for a host that runs it itself. */
|
|
55
|
+
compose(sessionId: string, ids?: string[] | null): {
|
|
56
|
+
text: string;
|
|
57
|
+
ids: string[];
|
|
58
|
+
};
|
|
59
|
+
/** A host started `runId` with composed notes. */
|
|
60
|
+
delivered(ids: string[], sessionId: string, runId: string): store.Note[];
|
|
61
|
+
/** Sends the session's queued notes (or the given ones) as one run. */
|
|
62
|
+
send(sessionId: string, filter?: {
|
|
63
|
+
ids?: string[] | null;
|
|
64
|
+
auto?: boolean;
|
|
65
|
+
}): Promise<SendResult>;
|
|
66
|
+
/** A run of the session ended: notes waiting for it to be idle go out now. */
|
|
67
|
+
runEnded(sessionId: string | null | undefined): Promise<void>;
|
|
68
|
+
markFixed(id: string, caller: {
|
|
69
|
+
sessionId?: string | null;
|
|
70
|
+
agentId?: string | null;
|
|
71
|
+
}, summary: string): store.Note;
|
|
72
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { HarnessError, invalid } from "../core/errors.js";
|
|
2
|
+
import { compose, MAX_SEND } from "./queue.js";
|
|
3
|
+
import * as store from "./store.js";
|
|
4
|
+
export class NoteDeliveries {
|
|
5
|
+
options;
|
|
6
|
+
sending = new Set();
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
}
|
|
10
|
+
changed(sessionId, ids) {
|
|
11
|
+
this.options.bus.emit({ type: 'annotations.changed', sessionId, ids });
|
|
12
|
+
}
|
|
13
|
+
queue(id, expectedVersion, sessionId, options = {}) {
|
|
14
|
+
const note = store.queue(this.options.root(), id, expectedVersion, sessionId, options);
|
|
15
|
+
this.changed(sessionId, [id]);
|
|
16
|
+
return note;
|
|
17
|
+
}
|
|
18
|
+
dequeue(id, expectedVersion) {
|
|
19
|
+
const note = store.dequeue(this.options.root(), id, expectedVersion);
|
|
20
|
+
this.changed(null, [id]);
|
|
21
|
+
return note;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Creates a note and acts on it in one step: what the popover's three buttons do. `trusted` is
|
|
25
|
+
* false when the comment came from a script the page could reach; a send then waits in the queue
|
|
26
|
+
* for the person to confirm it.
|
|
27
|
+
*/
|
|
28
|
+
async createAndAct(input, action, sessionId, trusted) {
|
|
29
|
+
if (!['save', 'queue', 'send'].includes(action))
|
|
30
|
+
throw invalid('action is save, queue or send.');
|
|
31
|
+
if (action !== 'save' && !sessionId)
|
|
32
|
+
throw invalid('Queueing or sending a note needs a session.');
|
|
33
|
+
const root = this.options.root();
|
|
34
|
+
let note = store.create(root, input);
|
|
35
|
+
if (action === 'save') {
|
|
36
|
+
this.changed(null, [note.id]);
|
|
37
|
+
return { note, send: null };
|
|
38
|
+
}
|
|
39
|
+
const deliveryOptions = {
|
|
40
|
+
...(action === 'send' ? { autoSend: true } : {}),
|
|
41
|
+
...(!trusted ? { confirm: true } : {})
|
|
42
|
+
};
|
|
43
|
+
note = store.queue(root, note.id, undefined, sessionId, deliveryOptions);
|
|
44
|
+
this.changed(sessionId, [note.id]);
|
|
45
|
+
if (action === 'queue' || !trusted)
|
|
46
|
+
return { note, send: null };
|
|
47
|
+
const send = await this.send(sessionId, { ids: [note.id] });
|
|
48
|
+
return { note: store.read(root, note.id), send };
|
|
49
|
+
}
|
|
50
|
+
/** The person confirms a note the page could have forged; it is then sent with the next send. */
|
|
51
|
+
confirm(id, expectedVersion) {
|
|
52
|
+
const root = this.options.root();
|
|
53
|
+
const note = store.read(root, id);
|
|
54
|
+
if (note.delivery?.state !== 'queued')
|
|
55
|
+
throw invalid('This note is not waiting to be sent.');
|
|
56
|
+
const confirmed = store.queue(root, id, expectedVersion, note.delivery.sessionId, { autoSend: note.delivery.autoSend === true, confirmed: true });
|
|
57
|
+
this.changed(confirmed.delivery.sessionId, [id]);
|
|
58
|
+
return confirmed;
|
|
59
|
+
}
|
|
60
|
+
/** The instruction for the session's queued notes (or the given ones), for a host that runs it itself. */
|
|
61
|
+
compose(sessionId, ids) {
|
|
62
|
+
const waiting = store.queued(this.options.root(), sessionId, { ids: ids ?? null });
|
|
63
|
+
if (ids != null && waiting.some((note) => note.delivery?.confirm))
|
|
64
|
+
throw invalid('Confirm page-origin notes before composing them.');
|
|
65
|
+
const notes = waiting.filter((note) => note.delivery?.confirm !== true).slice(0, MAX_SEND);
|
|
66
|
+
if (!notes.length)
|
|
67
|
+
throw invalid('No confirmed notes are waiting for this session.');
|
|
68
|
+
return { text: compose(notes), ids: notes.map((note) => note.id) };
|
|
69
|
+
}
|
|
70
|
+
/** A host started `runId` with composed notes. */
|
|
71
|
+
delivered(ids, sessionId, runId) {
|
|
72
|
+
const sent = store.markSent(this.options.root(), ids, sessionId, runId);
|
|
73
|
+
this.changed(sessionId, ids);
|
|
74
|
+
return sent;
|
|
75
|
+
}
|
|
76
|
+
/** Sends the session's queued notes (or the given ones) as one run. */
|
|
77
|
+
async send(sessionId, filter = {}) {
|
|
78
|
+
const root = this.options.root();
|
|
79
|
+
const waiting = store.queued(root, sessionId, { ids: filter.ids ?? null, auto: filter.auto === true });
|
|
80
|
+
if (filter.ids != null && waiting.some((note) => note.delivery?.confirm))
|
|
81
|
+
throw invalid('Confirm page-origin notes before sending them.');
|
|
82
|
+
const notes = waiting.filter((note) => note.delivery?.confirm !== true).slice(0, MAX_SEND);
|
|
83
|
+
if (!notes.length)
|
|
84
|
+
return { state: 'empty', ids: [] };
|
|
85
|
+
const ids = notes.map((note) => note.id);
|
|
86
|
+
const terminals = this.options.terminals();
|
|
87
|
+
if (!terminals?.has(sessionId)) {
|
|
88
|
+
// A host's session: the host starts the run and reports it with `delivered`.
|
|
89
|
+
if (filter.auto)
|
|
90
|
+
this.options.bus.emit({ type: 'annotations.ready', sessionId, ids });
|
|
91
|
+
return { state: 'host', text: compose(notes), ids };
|
|
92
|
+
}
|
|
93
|
+
if (this.sending.has(sessionId))
|
|
94
|
+
return { state: 'busy', ids };
|
|
95
|
+
this.sending.add(sessionId);
|
|
96
|
+
try {
|
|
97
|
+
const { runId } = await terminals.send({ sessionId, text: compose(notes) });
|
|
98
|
+
store.markSent(root, ids, sessionId, runId);
|
|
99
|
+
this.changed(sessionId, ids);
|
|
100
|
+
return { state: 'sent', runId, ids };
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (error instanceof HarnessError && error.code === 'session_busy') {
|
|
104
|
+
// Send it when the run ends.
|
|
105
|
+
for (const note of notes)
|
|
106
|
+
if (!note.delivery?.autoSend)
|
|
107
|
+
store.queue(root, note.id, undefined, sessionId, { autoSend: true });
|
|
108
|
+
this.changed(sessionId, ids);
|
|
109
|
+
return { state: 'busy', ids };
|
|
110
|
+
}
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
this.sending.delete(sessionId);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** A run of the session ended: notes waiting for it to be idle go out now. */
|
|
118
|
+
async runEnded(sessionId) {
|
|
119
|
+
if (!sessionId)
|
|
120
|
+
return;
|
|
121
|
+
if (!store.queued(this.options.root(), sessionId, { auto: true }).length)
|
|
122
|
+
return;
|
|
123
|
+
await this.send(sessionId, { auto: true });
|
|
124
|
+
}
|
|
125
|
+
markFixed(id, caller, summary) {
|
|
126
|
+
const note = store.markFixed(this.options.root(), id, caller, summary);
|
|
127
|
+
this.changed(note.delivery?.sessionId ?? null, [id]);
|
|
128
|
+
return note;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AnnotationCapture, InspectState } from "./capture.js";
|
|
2
|
+
import type { NoteDeliveries, SendResult } from "./delivery.js";
|
|
3
|
+
import type { Note } from "./store.js";
|
|
4
|
+
export interface InspectStep extends Omit<InspectState, 'result'> {
|
|
5
|
+
note: Note | null;
|
|
6
|
+
send: SendResult | null;
|
|
7
|
+
/** What the person was told in the page. */
|
|
8
|
+
message: string | null;
|
|
9
|
+
}
|
|
10
|
+
export declare function inspectStep(capture: AnnotationCapture, notes: NoteDeliveries, key: string, sessionId: string | null, surface: string): Promise<InspectStep>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// One step of the inspector loop (PROP-0018), shared by the terminal and a host: read the next
|
|
2
|
+
// comment from the capture target, keep it as a note, act on the button the person pressed, and
|
|
3
|
+
// tell them in the page what happened. Whether a send goes straight to the agent depends on where
|
|
4
|
+
// the comment came from, never on what the page or a client claims.
|
|
5
|
+
import { now } from "../core/fs.js";
|
|
6
|
+
function message(action, note, send, sessionId) {
|
|
7
|
+
if (action === 'save' || !sessionId)
|
|
8
|
+
return 'Annotation saved';
|
|
9
|
+
if (action === 'queue')
|
|
10
|
+
return 'Added to the agent\'s queue';
|
|
11
|
+
if (note.delivery?.confirm)
|
|
12
|
+
return 'Queued — confirm sending it in the panel';
|
|
13
|
+
if (send?.state === 'sent')
|
|
14
|
+
return 'Sent to the agent';
|
|
15
|
+
if (send?.state === 'busy')
|
|
16
|
+
return 'Queued — it goes to the agent when it finishes';
|
|
17
|
+
return 'Sending to the agent';
|
|
18
|
+
}
|
|
19
|
+
export async function inspectStep(capture, notes, key, sessionId, surface) {
|
|
20
|
+
const state = await capture.next(key);
|
|
21
|
+
const { result, ...rest } = state;
|
|
22
|
+
if (!result)
|
|
23
|
+
return { ...rest, note: null, send: null, message: null };
|
|
24
|
+
const action = sessionId ? result.action : 'save';
|
|
25
|
+
const { note, send } = await notes.createAndAct({
|
|
26
|
+
source: { url: result.url, title: result.title, capturedAt: now(), surface, representationHint: result.documentId, ...(state.app ? { app: state.app } : {}) },
|
|
27
|
+
target: result.target,
|
|
28
|
+
body: result.body
|
|
29
|
+
}, action, sessionId, state.trusted);
|
|
30
|
+
const text = message(action, note, send, sessionId);
|
|
31
|
+
await capture.toast(key, text).catch(() => undefined);
|
|
32
|
+
return { ...rest, note, send, message: text };
|
|
33
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Page } from 'playwright-core';
|
|
2
|
+
/** Evaluates `expression` (which yields a JSON string) and says whether the page could have reached it. */
|
|
3
|
+
export declare function evaluateIsolated(page: Page, expression: string): Promise<{
|
|
4
|
+
reply: unknown;
|
|
5
|
+
trusted: boolean;
|
|
6
|
+
}>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const WORLD = 'harness-page-notes';
|
|
2
|
+
const worlds = new WeakMap();
|
|
3
|
+
const watched = new WeakSet();
|
|
4
|
+
async function createWorld(page) {
|
|
5
|
+
const cdp = await page.context().newCDPSession(page);
|
|
6
|
+
const { frameTree } = await cdp.send('Page.getFrameTree');
|
|
7
|
+
const { executionContextId } = await cdp.send('Page.createIsolatedWorld', { frameId: frameTree.frame.id, worldName: WORLD, grantUniveralAccess: false });
|
|
8
|
+
const world = { cdp, contextId: executionContextId };
|
|
9
|
+
worlds.set(page, world);
|
|
10
|
+
if (!watched.has(page)) {
|
|
11
|
+
watched.add(page);
|
|
12
|
+
// A new document destroys the world; the next call makes another.
|
|
13
|
+
page.on('framenavigated', (frame) => {
|
|
14
|
+
if (frame !== page.mainFrame())
|
|
15
|
+
return;
|
|
16
|
+
const old = worlds.get(page);
|
|
17
|
+
worlds.delete(page);
|
|
18
|
+
void old?.cdp.detach().catch(() => undefined);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return world;
|
|
22
|
+
}
|
|
23
|
+
/** Evaluates `expression` (which yields a JSON string) and says whether the page could have reached it. */
|
|
24
|
+
export async function evaluateIsolated(page, expression) {
|
|
25
|
+
if (typeof page.context !== 'function' || typeof page.context().newCDPSession !== 'function') {
|
|
26
|
+
return { reply: await page.evaluate(expression), trusted: false };
|
|
27
|
+
}
|
|
28
|
+
let lastError = null;
|
|
29
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
30
|
+
let world = worlds.get(page) ?? null;
|
|
31
|
+
try {
|
|
32
|
+
world ??= await createWorld(page);
|
|
33
|
+
const evaluated = await world.cdp.send('Runtime.evaluate', { expression, contextId: world.contextId, returnByValue: true, awaitPromise: false });
|
|
34
|
+
if (evaluated.exceptionDetails)
|
|
35
|
+
throw new Error(evaluated.exceptionDetails.text || 'The page helper failed.');
|
|
36
|
+
return { reply: evaluated.result?.value, trusted: true };
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
lastError = error;
|
|
40
|
+
worlds.delete(page);
|
|
41
|
+
void world?.cdp.detach().catch(() => undefined);
|
|
42
|
+
// A context that went away with a navigation is made again once; anything else falls back.
|
|
43
|
+
if (attempt === 0 && /context|target closed|detached/i.test(String(error)))
|
|
44
|
+
continue;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (/closed/i.test(String(lastError)))
|
|
49
|
+
throw lastError;
|
|
50
|
+
return { reply: await page.evaluate(expression), trusted: false };
|
|
51
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** The helper's source. `__ARGS__` is replaced with one JSON operation; the expression evaluates to a JSON string. */
|
|
2
|
+
export declare const PAGE_SCRIPT: string;
|
|
3
|
+
export type PageOperation = {
|
|
4
|
+
operation: 'identity';
|
|
5
|
+
} | {
|
|
6
|
+
operation: 'begin';
|
|
7
|
+
kind: 'text' | 'element' | 'inspect';
|
|
8
|
+
captureId: string;
|
|
9
|
+
} | {
|
|
10
|
+
operation: 'capture';
|
|
11
|
+
captureId: string;
|
|
12
|
+
} | {
|
|
13
|
+
operation: 'next';
|
|
14
|
+
captureId: string;
|
|
15
|
+
} | {
|
|
16
|
+
operation: 'toast';
|
|
17
|
+
text: string;
|
|
18
|
+
} | {
|
|
19
|
+
operation: 'cancel';
|
|
20
|
+
captureId?: string;
|
|
21
|
+
} | {
|
|
22
|
+
operation: 'resolve' | 'reveal';
|
|
23
|
+
target: unknown;
|
|
24
|
+
};
|
|
25
|
+
/** The expression to evaluate in the page for one operation. */
|
|
26
|
+
export declare function pageCall(operation: PageOperation): string;
|
|
27
|
+
/** Reads the helper's reply, throwing its error. */
|
|
28
|
+
export declare function pageReply<T>(reply: unknown): T;
|