@oharness/server 0.9.3 → 0.11.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/analytics.d.ts +32 -29
- package/dist/analytics.js +225 -278
- package/dist/analytics.js.map +1 -1
- package/dist/i18n.js +500 -64
- package/dist/i18n.js.map +1 -1
- package/dist/icons.js +1 -0
- package/dist/icons.js.map +1 -1
- package/dist/main.js +4 -0
- package/dist/main.js.map +1 -1
- package/dist/model-order.js +1 -0
- package/dist/model-order.js.map +1 -1
- package/dist/page/client/account.js +570 -27
- package/dist/page/client/account.js.map +1 -1
- package/dist/page/client/base.js +55 -12
- package/dist/page/client/base.js.map +1 -1
- package/dist/page/client/browser.js +1 -1
- package/dist/page/client/composer.js +802 -59
- package/dist/page/client/composer.js.map +1 -1
- package/dist/page/client/computer.d.ts +27 -0
- package/dist/page/client/computer.js +358 -0
- package/dist/page/client/computer.js.map +1 -0
- package/dist/page/client/dialogs.js +168 -6
- package/dist/page/client/dialogs.js.map +1 -1
- package/dist/page/client/events.js +119 -9
- package/dist/page/client/events.js.map +1 -1
- package/dist/page/client/files.js +86 -1
- package/dist/page/client/files.js.map +1 -1
- package/dist/page/client/index.js +8 -0
- package/dist/page/client/index.js.map +1 -1
- package/dist/page/client/panels.js +14 -10
- package/dist/page/client/panels.js.map +1 -1
- package/dist/page/client/ripple.d.ts +13 -0
- package/dist/page/client/ripple.js +247 -0
- package/dist/page/client/ripple.js.map +1 -0
- package/dist/page/client/routines.js +432 -45
- package/dist/page/client/routines.js.map +1 -1
- package/dist/page/client/shell.js +386 -104
- package/dist/page/client/shell.js.map +1 -1
- package/dist/page/client/state.js +278 -19
- package/dist/page/client/state.js.map +1 -1
- package/dist/page/client/tasks.d.ts +8 -0
- package/dist/page/client/tasks.js +293 -0
- package/dist/page/client/tasks.js.map +1 -0
- package/dist/page/client/terminal.js +34 -7
- package/dist/page/client/terminal.js.map +1 -1
- package/dist/page/client/theme.d.ts +17 -0
- package/dist/page/client/theme.js +365 -0
- package/dist/page/client/theme.js.map +1 -0
- package/dist/page/client/transcript.js +196 -8
- package/dist/page/client/transcript.js.map +1 -1
- package/dist/page/client/usage.js +1 -11
- package/dist/page/client/usage.js.map +1 -1
- package/dist/page/style.js +745 -76
- package/dist/page/style.js.map +1 -1
- package/dist/page.js +94 -16
- package/dist/page.js.map +1 -1
- package/dist/prefs.d.ts +31 -0
- package/dist/prefs.js +13 -1
- package/dist/prefs.js.map +1 -1
- package/dist/remote-crypto.d.ts +78 -0
- package/dist/remote-crypto.js +216 -0
- package/dist/remote-crypto.js.map +1 -0
- package/dist/remote.d.ts +103 -0
- package/dist/remote.js +498 -0
- package/dist/remote.js.map +1 -0
- package/dist/server.d.ts +70 -0
- package/dist/server.js +1738 -73
- package/dist/server.js.map +1 -1
- package/dist/starters.d.ts +92 -0
- package/dist/starters.js +213 -0
- package/dist/starters.js.map +1 -0
- package/dist/suggest.d.ts +42 -0
- package/dist/suggest.js +215 -0
- package/dist/suggest.js.map +1 -0
- package/dist/terminal.d.ts +28 -2
- package/dist/terminal.js +45 -3
- package/dist/terminal.js.map +1 -1
- package/dist/theme-assets.d.ts +39 -0
- package/dist/theme-assets.js +109 -0
- package/dist/theme-assets.js.map +1 -0
- package/dist/theme-store.d.ts +50 -0
- package/dist/theme-store.js +124 -0
- package/dist/theme-store.js.map +1 -0
- package/dist/theme-tools.d.ts +25 -0
- package/dist/theme-tools.js +243 -0
- package/dist/theme-tools.js.map +1 -0
- package/dist/turn-shots.d.ts +43 -0
- package/dist/turn-shots.js +80 -0
- package/dist/turn-shots.js.map +1 -0
- package/dist/user-theme.d.ts +143 -0
- package/dist/user-theme.js +510 -0
- package/dist/user-theme.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { MemoryEntry } from "@oharness/engine/agent/memory.ts";
|
|
2
|
+
import type { Harness } from "@oharness/engine/harness.ts";
|
|
3
|
+
/**
|
|
4
|
+
* The offers under "What should I work on?".
|
|
5
|
+
*
|
|
6
|
+
* A project the agent has never worked in gets the same rotating set, and
|
|
7
|
+
* they are about the user's day rather than the directory: an empty folder has
|
|
8
|
+
* no code to profile and no README to fix, and guessing at one teaches a new
|
|
9
|
+
* person that this is a code tool and nothing else.
|
|
10
|
+
*
|
|
11
|
+
* Once there is history, the offers are written from it: what the recent
|
|
12
|
+
* conversations in this project were about, plus the project's notes when it
|
|
13
|
+
* has any. Conversations come first because nearly every project has them —
|
|
14
|
+
* notes mostly go to the user's own memory, not the project's, so a starter
|
|
15
|
+
* that waited for project notes was one most people never saw. Only titles and
|
|
16
|
+
* the opening request of each are read, never the whole transcript, and both
|
|
17
|
+
* pass through the same secret redaction memory uses.
|
|
18
|
+
*/
|
|
19
|
+
export interface Starter {
|
|
20
|
+
/** What fits on the chip. */
|
|
21
|
+
label: string;
|
|
22
|
+
/** The sentence the chip sends. */
|
|
23
|
+
prompt: string;
|
|
24
|
+
}
|
|
25
|
+
/** `pending`: a set is being written for this project, and will be pushed when it lands. */
|
|
26
|
+
export type StarterReply = {
|
|
27
|
+
kind: "fixed";
|
|
28
|
+
pending?: boolean;
|
|
29
|
+
} | {
|
|
30
|
+
kind: "written";
|
|
31
|
+
starters: Starter[];
|
|
32
|
+
};
|
|
33
|
+
/** One recent conversation, as the prompt sees it. */
|
|
34
|
+
export interface SessionDigest {
|
|
35
|
+
id: string;
|
|
36
|
+
title?: string;
|
|
37
|
+
/** What the user asked first, cut short and redacted. */
|
|
38
|
+
ask?: string;
|
|
39
|
+
updatedAt: number;
|
|
40
|
+
}
|
|
41
|
+
/** What the starters are written from. */
|
|
42
|
+
export interface ProjectContext {
|
|
43
|
+
sessions: SessionDigest[];
|
|
44
|
+
notes: MemoryEntry[];
|
|
45
|
+
}
|
|
46
|
+
/** Ids under "starter.<id>" in i18n: the rotating offers for a project with no memory. */
|
|
47
|
+
export declare const FIXED_STARTERS: readonly ["trip", "email", "research", "savings", "japanTrip"];
|
|
48
|
+
export declare const STARTERS_PROMPT = "You suggest what a user could ask an AI agent to do next in one of their projects. You are given what they have recently asked the agent in this project, and the agent's notes about it when there are any.\n\nWrite exactly three suggestions. Each one:\n- is grounded in what the user has actually been doing here \u2014 a thread they started and did not finish, a natural next step after something they did, a recurring chore \u2014 never invented;\n- is specific: name the file, app, document, topic or decision it is about;\n- differs in kind from the other two (for example: continue or fix something, check or measure something, write or plan something);\n- has a \"label\": an imperative phrase under 60 characters that fits on a button;\n- has a \"prompt\": one to three sentences the agent receives, saying what to do and what to hand back.\n\nDo not simply repeat a request the user already made; offer the step after it. Skip anything that looks finished. Never include secrets, keys or personal data.\nReply with only a JSON array: [{\"label\": \"...\", \"prompt\": \"...\"}, ...]";
|
|
49
|
+
/** Recent conversations as the prompt shows them: newest first, each short. */
|
|
50
|
+
export declare function sessionsText(sessions: readonly SessionDigest[]): string;
|
|
51
|
+
/** The notes as the prompt shows them: newest first, each cut short, the whole bounded. */
|
|
52
|
+
export declare function notesText(notes: readonly MemoryEntry[], budget?: number): string;
|
|
53
|
+
export declare function startersRequest(context: ProjectContext, locale: string): string;
|
|
54
|
+
/** How long one generation may take before the fixed three are shown instead. */
|
|
55
|
+
export declare const STARTERS_TIMEOUT_MS = 45000;
|
|
56
|
+
/**
|
|
57
|
+
* The project's recent conversations, digested.
|
|
58
|
+
*
|
|
59
|
+
* The open, empty one on screen is not history, and routine runs are the
|
|
60
|
+
* scheduler talking, not the user. Reading a transcript is the costly part,
|
|
61
|
+
* so only the dozen most recent are opened, and only their first user turn.
|
|
62
|
+
*/
|
|
63
|
+
export declare function recentSessions(harness: Harness, cwd: string): Promise<SessionDigest[]>;
|
|
64
|
+
/** Three usable starters out of a model reply, or nothing. */
|
|
65
|
+
export declare function parseStarters(reply: string): Starter[] | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* What a set of starters was written from, in which language.
|
|
68
|
+
*
|
|
69
|
+
* A new conversation, a renamed one, or a note edited or added is a project
|
|
70
|
+
* that has moved on, so the offers are rewritten. A turn inside an existing
|
|
71
|
+
* conversation is not: keying on its last activity would pay for a new set
|
|
72
|
+
* every time a message is sent. A restart with the same history pays nothing.
|
|
73
|
+
*/
|
|
74
|
+
export declare function startersKey(context: ProjectContext, locale: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* The starters for one project, as the page asks for them.
|
|
77
|
+
*
|
|
78
|
+
* Never waits on a model. With a set already written for this history it is
|
|
79
|
+
* returned; otherwise the answer is the fixed three at once, marked pending,
|
|
80
|
+
* and a set is written in the background — `onReady` is told when it lands,
|
|
81
|
+
* so the page can swap it in, and the next ask reads it from disk. Writing
|
|
82
|
+
* one takes a flash model twenty-odd seconds, which no new conversation
|
|
83
|
+
* should sit through.
|
|
84
|
+
*
|
|
85
|
+
* One generation per project and history at a time: the page asks on boot and
|
|
86
|
+
* on every project switch, and two tabs landing together must not pay twice.
|
|
87
|
+
*/
|
|
88
|
+
export type StartersFor = ((harness: Harness, cwd: string, locale: string, model?: string) => Promise<StarterReply>) & {
|
|
89
|
+
/** Every generation still running has settled. For tests and shutdown. */
|
|
90
|
+
idle(): Promise<void>;
|
|
91
|
+
};
|
|
92
|
+
export declare function createStarters(onReady?: (cwd: string, locale: string, starters: Starter[]) => void): StartersFor;
|
package/dist/starters.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { userPath } from "@oharness/engine/paths.ts";
|
|
5
|
+
import { parseJson } from "@oharness/engine/util/json.ts";
|
|
6
|
+
import { redactSecrets } from "@oharness/engine/agent/memory-redact.ts";
|
|
7
|
+
import { visibleText } from "@oharness/engine/core/types.ts";
|
|
8
|
+
/** Ids under "starter.<id>" in i18n: the rotating offers for a project with no memory. */
|
|
9
|
+
export const FIXED_STARTERS = ["trip", "email", "research", "savings", "japanTrip"];
|
|
10
|
+
const LANGUAGES = { en: "English", zh: "Simplified Chinese" };
|
|
11
|
+
export const STARTERS_PROMPT = `You suggest what a user could ask an AI agent to do next in one of their projects. You are given what they have recently asked the agent in this project, and the agent's notes about it when there are any.
|
|
12
|
+
|
|
13
|
+
Write exactly three suggestions. Each one:
|
|
14
|
+
- is grounded in what the user has actually been doing here — a thread they started and did not finish, a natural next step after something they did, a recurring chore — never invented;
|
|
15
|
+
- is specific: name the file, app, document, topic or decision it is about;
|
|
16
|
+
- differs in kind from the other two (for example: continue or fix something, check or measure something, write or plan something);
|
|
17
|
+
- has a "label": an imperative phrase under 60 characters that fits on a button;
|
|
18
|
+
- has a "prompt": one to three sentences the agent receives, saying what to do and what to hand back.
|
|
19
|
+
|
|
20
|
+
Do not simply repeat a request the user already made; offer the step after it. Skip anything that looks finished. Never include secrets, keys or personal data.
|
|
21
|
+
Reply with only a JSON array: [{"label": "...", "prompt": "..."}, ...]`;
|
|
22
|
+
/** Recent conversations as the prompt shows them: newest first, each short. */
|
|
23
|
+
export function sessionsText(sessions) {
|
|
24
|
+
return [...sessions]
|
|
25
|
+
.sort((a, b) => b.updatedAt - a.updatedAt)
|
|
26
|
+
.map((session) => {
|
|
27
|
+
const lines = [`<session updated="${new Date(session.updatedAt).toISOString().slice(0, 10)}">`];
|
|
28
|
+
if (session.title)
|
|
29
|
+
lines.push(`title: ${session.title}`);
|
|
30
|
+
if (session.ask)
|
|
31
|
+
lines.push(`first request: ${session.ask}`);
|
|
32
|
+
lines.push("</session>");
|
|
33
|
+
return lines.join("\n");
|
|
34
|
+
})
|
|
35
|
+
.join("\n\n");
|
|
36
|
+
}
|
|
37
|
+
/** The notes as the prompt shows them: newest first, each cut short, the whole bounded. */
|
|
38
|
+
export function notesText(notes, budget = 12_000) {
|
|
39
|
+
const sorted = [...notes].sort((a, b) => (b.updatedAt ?? b.createdAt) - (a.updatedAt ?? a.createdAt));
|
|
40
|
+
const out = [];
|
|
41
|
+
let used = 0;
|
|
42
|
+
for (const note of sorted) {
|
|
43
|
+
const body = note.body.length > 600 ? note.body.slice(0, 600) + "…" : note.body;
|
|
44
|
+
const block = `<note id="${note.id}"${note.type ? ` type="${note.type}"` : ""}>\n${note.summary}\n\n${body}\n</note>`;
|
|
45
|
+
if (used + block.length > budget)
|
|
46
|
+
break;
|
|
47
|
+
out.push(block);
|
|
48
|
+
used += block.length;
|
|
49
|
+
}
|
|
50
|
+
return out.join("\n\n");
|
|
51
|
+
}
|
|
52
|
+
export function startersRequest(context, locale) {
|
|
53
|
+
const language = LANGUAGES[locale] ?? LANGUAGES.en;
|
|
54
|
+
const parts = [];
|
|
55
|
+
if (context.sessions.length)
|
|
56
|
+
parts.push(`Recent conversations in this project:\n${sessionsText(context.sessions)}`);
|
|
57
|
+
if (context.notes.length)
|
|
58
|
+
parts.push(`Project notes:\n${notesText(context.notes)}`);
|
|
59
|
+
return `${parts.join("\n\n")}\n\nWrite the three suggestions in ${language}, as a JSON array.`;
|
|
60
|
+
}
|
|
61
|
+
/** How long one generation may take before the fixed three are shown instead. */
|
|
62
|
+
export const STARTERS_TIMEOUT_MS = 45_000;
|
|
63
|
+
function isTimeout(error) {
|
|
64
|
+
const name = error?.name;
|
|
65
|
+
return name === "TimeoutError" || name === "AbortError" || /abort|timed? ?out/i.test(String(error));
|
|
66
|
+
}
|
|
67
|
+
/** How many recent conversations are read, and how much of each. */
|
|
68
|
+
const SESSION_LIMIT = 12;
|
|
69
|
+
const ASK_LENGTH = 200;
|
|
70
|
+
/**
|
|
71
|
+
* The project's recent conversations, digested.
|
|
72
|
+
*
|
|
73
|
+
* The open, empty one on screen is not history, and routine runs are the
|
|
74
|
+
* scheduler talking, not the user. Reading a transcript is the costly part,
|
|
75
|
+
* so only the dozen most recent are opened, and only their first user turn.
|
|
76
|
+
*/
|
|
77
|
+
export async function recentSessions(harness, cwd) {
|
|
78
|
+
const metas = (await harness.listSessions(500).catch(() => []))
|
|
79
|
+
.filter((meta) => meta.cwd === cwd && !meta.routine && meta.turns > 0)
|
|
80
|
+
.slice(0, SESSION_LIMIT);
|
|
81
|
+
return Promise.all(metas.map(async (meta) => {
|
|
82
|
+
let ask;
|
|
83
|
+
try {
|
|
84
|
+
const first = (await harness.store.recordsOf(meta.id)).find((record) => record.message.role === "user");
|
|
85
|
+
const text = first ? visibleText(first.message).replace(/\s+/g, " ").trim() : "";
|
|
86
|
+
if (text)
|
|
87
|
+
ask = redactSecrets(text.length > ASK_LENGTH ? text.slice(0, ASK_LENGTH) + "…" : text).text;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// A transcript that will not read still has its title.
|
|
91
|
+
}
|
|
92
|
+
const title = meta.title ? redactSecrets(meta.title).text : undefined;
|
|
93
|
+
return { id: meta.id, updatedAt: meta.updatedAt, ...(title ? { title } : {}), ...(ask ? { ask } : {}) };
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
/** Three usable starters out of a model reply, or nothing. */
|
|
97
|
+
export function parseStarters(reply) {
|
|
98
|
+
const start = reply.indexOf("[");
|
|
99
|
+
const end = reply.lastIndexOf("]");
|
|
100
|
+
if (start < 0 || end <= start)
|
|
101
|
+
return undefined;
|
|
102
|
+
const parsed = parseJson(reply.slice(start, end + 1));
|
|
103
|
+
if (!parsed.ok || !Array.isArray(parsed.value))
|
|
104
|
+
return undefined;
|
|
105
|
+
const starters = [];
|
|
106
|
+
for (const item of parsed.value) {
|
|
107
|
+
if (!item || typeof item !== "object")
|
|
108
|
+
continue;
|
|
109
|
+
const { label, prompt } = item;
|
|
110
|
+
if (typeof label !== "string" || typeof prompt !== "string")
|
|
111
|
+
continue;
|
|
112
|
+
const cleanLabel = label.replace(/\s+/g, " ").trim();
|
|
113
|
+
const cleanPrompt = prompt.trim();
|
|
114
|
+
if (!cleanLabel || !cleanPrompt || cleanLabel.length > 120 || cleanPrompt.length > 1_200)
|
|
115
|
+
continue;
|
|
116
|
+
starters.push({ label: cleanLabel, prompt: cleanPrompt });
|
|
117
|
+
if (starters.length === 3)
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
return starters.length === 3 ? starters : undefined;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* What a set of starters was written from, in which language.
|
|
124
|
+
*
|
|
125
|
+
* A new conversation, a renamed one, or a note edited or added is a project
|
|
126
|
+
* that has moved on, so the offers are rewritten. A turn inside an existing
|
|
127
|
+
* conversation is not: keying on its last activity would pay for a new set
|
|
128
|
+
* every time a message is sent. A restart with the same history pays nothing.
|
|
129
|
+
*/
|
|
130
|
+
export function startersKey(context, locale) {
|
|
131
|
+
const notes = context.notes
|
|
132
|
+
.map((note) => [note.id, note.updatedAt ?? note.createdAt, note.summary.length, note.body.length])
|
|
133
|
+
.sort((a, b) => String(a[0]).localeCompare(String(b[0])));
|
|
134
|
+
const sessions = context.sessions
|
|
135
|
+
.map((session) => [session.id, session.title ?? ""])
|
|
136
|
+
.sort((a, b) => String(a[0]).localeCompare(String(b[0])));
|
|
137
|
+
return createHash("sha256").update(JSON.stringify([locale, notes, sessions])).digest("hex");
|
|
138
|
+
}
|
|
139
|
+
const cacheFile = (cwd) => userPath("starters", createHash("sha256").update(cwd).digest("hex").slice(0, 24) + ".json");
|
|
140
|
+
async function readCached(cwd, key) {
|
|
141
|
+
try {
|
|
142
|
+
const parsed = parseJson(await readFile(cacheFile(cwd), "utf8"));
|
|
143
|
+
if (!parsed.ok || parsed.value?.key !== key || !Array.isArray(parsed.value.starters))
|
|
144
|
+
return undefined;
|
|
145
|
+
return parseStarters(JSON.stringify(parsed.value.starters));
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function writeCached(cwd, key, starters) {
|
|
152
|
+
const file = cacheFile(cwd);
|
|
153
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
154
|
+
await writeFile(file, JSON.stringify({ key, starters }), "utf8");
|
|
155
|
+
}
|
|
156
|
+
export function createStarters(onReady) {
|
|
157
|
+
const pending = new Map();
|
|
158
|
+
const failed = new Set();
|
|
159
|
+
const get = async (harness, cwd, locale, model) => {
|
|
160
|
+
const [notes, sessions] = await Promise.all([
|
|
161
|
+
harness.memory ? harness.memory.load().catch(() => []) : Promise.resolve([]),
|
|
162
|
+
recentSessions(harness, cwd),
|
|
163
|
+
]);
|
|
164
|
+
if (!notes.length && !sessions.length)
|
|
165
|
+
return { kind: "fixed" };
|
|
166
|
+
const context = { notes, sessions };
|
|
167
|
+
const key = startersKey(context, locale);
|
|
168
|
+
const slot = cwd + "\0" + key;
|
|
169
|
+
if (failed.has(slot))
|
|
170
|
+
return { kind: "fixed" };
|
|
171
|
+
const cached = await readCached(cwd, key);
|
|
172
|
+
if (cached)
|
|
173
|
+
return { kind: "written", starters: cached };
|
|
174
|
+
if (!pending.has(slot)) {
|
|
175
|
+
const job = (async () => {
|
|
176
|
+
try {
|
|
177
|
+
// Generous: a flash model answering from a dozen conversations takes
|
|
178
|
+
// twenty-odd seconds, and nothing is waiting on it.
|
|
179
|
+
const reply = await harness.generateText(STARTERS_PROMPT, startersRequest(context, locale), AbortSignal.timeout(STARTERS_TIMEOUT_MS), 1_500, model);
|
|
180
|
+
const starters = parseStarters(reply);
|
|
181
|
+
if (!starters) {
|
|
182
|
+
harness.logger.warn("starters: the model's reply held no three usable starters", { reply: reply.slice(0, 500) });
|
|
183
|
+
failed.add(slot);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
await writeCached(cwd, key, starters).catch(() => { });
|
|
187
|
+
onReady?.(cwd, locale, starters);
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
harness.logger.warn("starters: could not write them", { error: String(error) });
|
|
191
|
+
// No model configured, offline, or too slow this time: the fixed
|
|
192
|
+
// three stay. A timeout is tried again on the next ask — a slow
|
|
193
|
+
// moment is not a model that cannot do this; anything else waits
|
|
194
|
+
// for the history to change.
|
|
195
|
+
if (!isTimeout(error))
|
|
196
|
+
failed.add(slot);
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
pending.delete(slot);
|
|
200
|
+
}
|
|
201
|
+
})();
|
|
202
|
+
pending.set(slot, job);
|
|
203
|
+
}
|
|
204
|
+
return { kind: "fixed", pending: true };
|
|
205
|
+
};
|
|
206
|
+
return Object.assign(get, {
|
|
207
|
+
idle: async () => {
|
|
208
|
+
while (pending.size)
|
|
209
|
+
await Promise.all([...pending.values()]);
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=starters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starters.js","sourceRoot":"","sources":["../src/starters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Message } from "@oharness/engine/core/types.ts";
|
|
2
|
+
import type { Harness } from "@oharness/engine/harness.ts";
|
|
3
|
+
/**
|
|
4
|
+
* The grey line in the composer: what the user is likely to type next.
|
|
5
|
+
*
|
|
6
|
+
* Written once a turn has ended on its own, from the last few exchanges — the
|
|
7
|
+
* requests, the replies' final answers and, for the latest, a line per tool
|
|
8
|
+
* call; never tool output — and offered for Tab to
|
|
9
|
+
* take. On the conversation's own model, the way Claude Code does it: the
|
|
10
|
+
* guess reads like the agent the user is talking to, and costs what that
|
|
11
|
+
* model costs, which the Settings switch is there to turn off.
|
|
12
|
+
*
|
|
13
|
+
* Absent whenever the model is unsure: an empty box is better than a wrong
|
|
14
|
+
* prediction sitting in it.
|
|
15
|
+
*/
|
|
16
|
+
export declare const SUGGEST_PROMPT = "You predict the next message a user will send to their AI agent, so it can be offered in their input box for them to accept with one key.\n\nYou are given the last few exchanges of the conversation, oldest first. For the latest one you also see, in <agent_actions>, the tools the agent used on the way to its answer: what it read, changed and ran, and what failed.\n\nWrite the single most likely next message, as the user would type it:\n- a short instruction, at most 12 words, in the same language the user wrote in;\n- grounded in the latest reply: approving what the agent proposed, answering a question it asked, or picking up something it said was left unfinished or failing;\n- specific to this conversation, never generic (\"thanks\", \"continue\", \"ok\");\n- never something the agent already did, and never a new task the conversation does not point to.\n\nIf the user has wrapped up (thanked the agent, said it is done) or the latest reply leaves nothing open, reply with exactly NONE.\nReply with only the message or NONE: no quotes, no preamble.";
|
|
17
|
+
/** One request, the answer it got, and what the agent did in between. */
|
|
18
|
+
export interface Exchange {
|
|
19
|
+
ask: string;
|
|
20
|
+
reply: string;
|
|
21
|
+
actions: string[];
|
|
22
|
+
}
|
|
23
|
+
/** Roughly what a tokenizer makes of it: a token per CJK character, four of anything else. */
|
|
24
|
+
export declare function estimateTokens(text: string): number;
|
|
25
|
+
/** Cut to `tokens`, from the start, the end, or both ends with the middle elided. */
|
|
26
|
+
export declare function cut(text: string, tokens: number, keep: "head" | "tail" | "ends"): string;
|
|
27
|
+
/**
|
|
28
|
+
* The last few exchanges, oldest first, cut to their budgets and redacted.
|
|
29
|
+
*
|
|
30
|
+
* A request is a user message with text that the harness did not write:
|
|
31
|
+
* tool results come back as user messages with none, and a compaction
|
|
32
|
+
* summary, a task notification or a goal nudge are marked synthetic. The
|
|
33
|
+
* reply is the agent's final answer — the text after its last tool call —
|
|
34
|
+
* because the narration on the way ("let me look at X") is not what the user
|
|
35
|
+
* answers; when a turn ends on a call, its last words are used instead.
|
|
36
|
+
*/
|
|
37
|
+
export declare function recentExchanges(messages: readonly Message[], count?: number): Exchange[];
|
|
38
|
+
/** The exchanges as the model reads them. */
|
|
39
|
+
export declare function suggestContext(messages: readonly Message[]): string | undefined;
|
|
40
|
+
/** One line fit for the box, or nothing: a long, multi-line or quoted answer is the model not following the brief. */
|
|
41
|
+
export declare function cleanSuggestion(raw: string): string | undefined;
|
|
42
|
+
export declare function suggestNext(harness: Harness, messages: readonly Message[], model: string, signal: AbortSignal): Promise<string | undefined>;
|
package/dist/suggest.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { visibleText } from "@oharness/engine/core/types.ts";
|
|
2
|
+
import { redactSecrets } from "@oharness/engine/agent/memory-redact.ts";
|
|
3
|
+
/**
|
|
4
|
+
* The grey line in the composer: what the user is likely to type next.
|
|
5
|
+
*
|
|
6
|
+
* Written once a turn has ended on its own, from the last few exchanges — the
|
|
7
|
+
* requests, the replies' final answers and, for the latest, a line per tool
|
|
8
|
+
* call; never tool output — and offered for Tab to
|
|
9
|
+
* take. On the conversation's own model, the way Claude Code does it: the
|
|
10
|
+
* guess reads like the agent the user is talking to, and costs what that
|
|
11
|
+
* model costs, which the Settings switch is there to turn off.
|
|
12
|
+
*
|
|
13
|
+
* Absent whenever the model is unsure: an empty box is better than a wrong
|
|
14
|
+
* prediction sitting in it.
|
|
15
|
+
*/
|
|
16
|
+
export const SUGGEST_PROMPT = `You predict the next message a user will send to their AI agent, so it can be offered in their input box for them to accept with one key.
|
|
17
|
+
|
|
18
|
+
You are given the last few exchanges of the conversation, oldest first. For the latest one you also see, in <agent_actions>, the tools the agent used on the way to its answer: what it read, changed and ran, and what failed.
|
|
19
|
+
|
|
20
|
+
Write the single most likely next message, as the user would type it:
|
|
21
|
+
- a short instruction, at most 12 words, in the same language the user wrote in;
|
|
22
|
+
- grounded in the latest reply: approving what the agent proposed, answering a question it asked, or picking up something it said was left unfinished or failing;
|
|
23
|
+
- specific to this conversation, never generic ("thanks", "continue", "ok");
|
|
24
|
+
- never something the agent already did, and never a new task the conversation does not point to.
|
|
25
|
+
|
|
26
|
+
If the user has wrapped up (thanked the agent, said it is done) or the latest reply leaves nothing open, reply with exactly NONE.
|
|
27
|
+
Reply with only the message or NONE: no quotes, no preamble.`;
|
|
28
|
+
/**
|
|
29
|
+
* What the guess reads, in estimated tokens rather than characters: a Chinese
|
|
30
|
+
* character is about a token and an English one about a quarter, so a budget
|
|
31
|
+
* in characters gave a Chinese conversation three times the context, and the
|
|
32
|
+
* bill to match.
|
|
33
|
+
*
|
|
34
|
+
* Most of it goes to the exchange that just ended — its request kept at both
|
|
35
|
+
* ends, where a pasted log's actual question sits, and its reply's final
|
|
36
|
+
* answer — and a little to each one before it, enough to say what the thread
|
|
37
|
+
* is about. With these caps three exchanges always fit.
|
|
38
|
+
*/
|
|
39
|
+
const EXCHANGES = 3;
|
|
40
|
+
const LATEST_ASK_TOKENS = 400;
|
|
41
|
+
const LATEST_REPLY_TOKENS = 450;
|
|
42
|
+
const EARLIER_ASK_TOKENS = 80;
|
|
43
|
+
const EARLIER_REPLY_TOKENS = 80;
|
|
44
|
+
/** The actions line: the latest turn's tool calls, most recent kept. */
|
|
45
|
+
const MAX_ACTIONS = 12;
|
|
46
|
+
const ARG_TOKENS = 20;
|
|
47
|
+
const MAX_LENGTH = 120;
|
|
48
|
+
const TIMEOUT_MS = 15_000;
|
|
49
|
+
const WIDE = /[\u1100-\u11ff\u2e80-\ua4cf\uac00-\ud7af\uf900-\ufaff\ufe30-\ufe4f\uff00-\uffef]/;
|
|
50
|
+
/** Roughly what a tokenizer makes of it: a token per CJK character, four of anything else. */
|
|
51
|
+
export function estimateTokens(text) {
|
|
52
|
+
let tokens = 0;
|
|
53
|
+
for (const char of text)
|
|
54
|
+
tokens += WIDE.test(char) ? 1 : 0.25;
|
|
55
|
+
return Math.ceil(tokens);
|
|
56
|
+
}
|
|
57
|
+
/** The longest start, or end, of `text` within `tokens`. */
|
|
58
|
+
function fit(text, tokens, from) {
|
|
59
|
+
const chars = [...text];
|
|
60
|
+
let used = 0;
|
|
61
|
+
let n = 0;
|
|
62
|
+
for (; n < chars.length; n++) {
|
|
63
|
+
const char = chars[from === "head" ? n : chars.length - 1 - n];
|
|
64
|
+
used += WIDE.test(char) ? 1 : 0.25;
|
|
65
|
+
if (used > tokens)
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
return from === "head" ? chars.slice(0, n).join("") : chars.slice(chars.length - n).join("");
|
|
69
|
+
}
|
|
70
|
+
/** Cut to `tokens`, from the start, the end, or both ends with the middle elided. */
|
|
71
|
+
export function cut(text, tokens, keep) {
|
|
72
|
+
if (estimateTokens(text) <= tokens)
|
|
73
|
+
return text;
|
|
74
|
+
if (keep === "head")
|
|
75
|
+
return fit(text, tokens, "head") + "…";
|
|
76
|
+
if (keep === "tail")
|
|
77
|
+
return "…" + fit(text, tokens, "tail");
|
|
78
|
+
return fit(text, tokens / 2, "head") + "\n…\n" + fit(text, tokens / 2, "tail");
|
|
79
|
+
}
|
|
80
|
+
/** The argument that says what a call was about: a path, a command, a pattern. */
|
|
81
|
+
const KEY_ARGS = ["path", "file_path", "command", "pattern", "url", "query", "description", "name"];
|
|
82
|
+
function describeCall(name, input, failed) {
|
|
83
|
+
let arg = "";
|
|
84
|
+
if (input && typeof input === "object") {
|
|
85
|
+
for (const key of KEY_ARGS) {
|
|
86
|
+
const value = input[key];
|
|
87
|
+
if (typeof value === "string" && value.trim()) {
|
|
88
|
+
arg = cut(redactSecrets(value.replace(/\s+/g, " ").trim()).text, ARG_TOKENS, "head");
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return `${name}${arg ? ` ${JSON.stringify(arg)}` : ""}${failed ? " → failed" : ""}`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A run of the same call is one line with a count: twenty reads of one file
|
|
97
|
+
* say less than "read ×20", and the room is for what else happened.
|
|
98
|
+
*/
|
|
99
|
+
function collapse(actions) {
|
|
100
|
+
const out = [];
|
|
101
|
+
for (const line of actions) {
|
|
102
|
+
const last = out.at(-1);
|
|
103
|
+
if (last && last.line === line)
|
|
104
|
+
last.times++;
|
|
105
|
+
else
|
|
106
|
+
out.push({ line, times: 1 });
|
|
107
|
+
}
|
|
108
|
+
const lines = out.map(({ line, times }) => (times > 1 ? `${line} ×${times}` : line));
|
|
109
|
+
if (lines.length <= MAX_ACTIONS)
|
|
110
|
+
return lines;
|
|
111
|
+
return [`(${lines.length - MAX_ACTIONS} earlier)`, ...lines.slice(-MAX_ACTIONS)];
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The last few exchanges, oldest first, cut to their budgets and redacted.
|
|
115
|
+
*
|
|
116
|
+
* A request is a user message with text that the harness did not write:
|
|
117
|
+
* tool results come back as user messages with none, and a compaction
|
|
118
|
+
* summary, a task notification or a goal nudge are marked synthetic. The
|
|
119
|
+
* reply is the agent's final answer — the text after its last tool call —
|
|
120
|
+
* because the narration on the way ("let me look at X") is not what the user
|
|
121
|
+
* answers; when a turn ends on a call, its last words are used instead.
|
|
122
|
+
*/
|
|
123
|
+
export function recentExchanges(messages, count = EXCHANGES) {
|
|
124
|
+
const exchanges = [];
|
|
125
|
+
let after = [];
|
|
126
|
+
for (let i = messages.length - 1; i >= 0 && exchanges.length < count; i--) {
|
|
127
|
+
const message = messages[i];
|
|
128
|
+
const text = message.role === "user" && !message.meta?.synthetic ? visibleText(message).trim() : "";
|
|
129
|
+
if (!text) {
|
|
130
|
+
after.unshift(message);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const turn = after;
|
|
134
|
+
after = [];
|
|
135
|
+
const latest = exchanges.length === 0;
|
|
136
|
+
const reply = finalAnswer(turn);
|
|
137
|
+
if (!reply)
|
|
138
|
+
continue;
|
|
139
|
+
exchanges.unshift({
|
|
140
|
+
// Redacted whole, then cut: a key cut in half no longer looks like one.
|
|
141
|
+
ask: cut(redactSecrets(text).text, latest ? LATEST_ASK_TOKENS : EARLIER_ASK_TOKENS, latest ? "ends" : "head"),
|
|
142
|
+
reply: cut(redactSecrets(reply).text, latest ? LATEST_REPLY_TOKENS : EARLIER_REPLY_TOKENS, "tail"),
|
|
143
|
+
actions: latest ? collapse(actionsOf(turn)) : [],
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return exchanges;
|
|
147
|
+
}
|
|
148
|
+
function finalAnswer(turn) {
|
|
149
|
+
let final = [];
|
|
150
|
+
let last = "";
|
|
151
|
+
for (const message of turn) {
|
|
152
|
+
if (message.role !== "assistant")
|
|
153
|
+
continue;
|
|
154
|
+
for (const block of message.content) {
|
|
155
|
+
if (block.type === "tool_call")
|
|
156
|
+
final = [];
|
|
157
|
+
else if (block.type === "text" && block.text.trim()) {
|
|
158
|
+
final.push(block.text.trim());
|
|
159
|
+
last = block.text.trim();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return final.length ? final.join("\n") : last;
|
|
164
|
+
}
|
|
165
|
+
function actionsOf(turn) {
|
|
166
|
+
const failed = new Set();
|
|
167
|
+
for (const message of turn) {
|
|
168
|
+
for (const block of message.content) {
|
|
169
|
+
if (block.type === "tool_result" && block.isError)
|
|
170
|
+
failed.add(block.toolCallId);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const actions = [];
|
|
174
|
+
for (const message of turn) {
|
|
175
|
+
if (message.role !== "assistant")
|
|
176
|
+
continue;
|
|
177
|
+
for (const block of message.content) {
|
|
178
|
+
if (block.type === "tool_call")
|
|
179
|
+
actions.push(describeCall(block.name, block.input, failed.has(block.id)));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return actions;
|
|
183
|
+
}
|
|
184
|
+
/** The exchanges as the model reads them. */
|
|
185
|
+
export function suggestContext(messages) {
|
|
186
|
+
const exchanges = recentExchanges(messages);
|
|
187
|
+
if (!exchanges.length)
|
|
188
|
+
return undefined;
|
|
189
|
+
return exchanges
|
|
190
|
+
.map((exchange) => {
|
|
191
|
+
const parts = [`<user>\n${exchange.ask}\n</user>`];
|
|
192
|
+
if (exchange.actions.length)
|
|
193
|
+
parts.push(`<agent_actions>\n${exchange.actions.map((line) => `- ${line}`).join("\n")}\n</agent_actions>`);
|
|
194
|
+
parts.push(`<agent>\n${exchange.reply}\n</agent>`);
|
|
195
|
+
return parts.join("\n");
|
|
196
|
+
})
|
|
197
|
+
.join("\n");
|
|
198
|
+
}
|
|
199
|
+
/** One line fit for the box, or nothing: a long, multi-line or quoted answer is the model not following the brief. */
|
|
200
|
+
export function cleanSuggestion(raw) {
|
|
201
|
+
const text = raw.trim().replace(/^["'“「]+|["'”」]+$/g, "").trim();
|
|
202
|
+
// The model's way of saying there is nothing to offer; an empty reply is
|
|
203
|
+
// the other, but small models will not give one.
|
|
204
|
+
if (!text || /^none\.?$/i.test(text) || text.includes("\n") || text.length > MAX_LENGTH)
|
|
205
|
+
return undefined;
|
|
206
|
+
return text;
|
|
207
|
+
}
|
|
208
|
+
export async function suggestNext(harness, messages, model, signal) {
|
|
209
|
+
const context = suggestContext(messages);
|
|
210
|
+
if (!context)
|
|
211
|
+
return undefined;
|
|
212
|
+
const reply = await harness.generateWith(model, SUGGEST_PROMPT, context, AbortSignal.any([signal, AbortSignal.timeout(TIMEOUT_MS)]), 200);
|
|
213
|
+
return cleanSuggestion(reply);
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=suggest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggest.js","sourceRoot":"","sources":["../src/suggest.ts"],"names":[],"mappings":"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"}
|
package/dist/terminal.d.ts
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
import type { ServerResponse } from "node:http";
|
|
2
|
+
/**
|
|
3
|
+
* Programs a new shell may be opened running, by name. A fixed list rather
|
|
4
|
+
* than a command from the request: the page asks for "claude", and what that
|
|
5
|
+
* means is decided here.
|
|
6
|
+
*
|
|
7
|
+
* `claude` is the hand-off to Claude Code. It is the user's own install,
|
|
8
|
+
* signed in to their own account through Claude Code's own login — OHarness
|
|
9
|
+
* starts it and does nothing else, which is the one way of using a Claude
|
|
10
|
+
* plan from another app that Anthropic's terms leave plainly open. It is
|
|
11
|
+
* typed into the shell rather than spawned in its place, so PATH is the
|
|
12
|
+
* login shell's and the shell is still there when Claude Code exits.
|
|
13
|
+
*/
|
|
14
|
+
export declare const LAUNCHERS: {
|
|
15
|
+
readonly claude: (platform: NodeJS.Platform, resume?: string) => string;
|
|
16
|
+
};
|
|
17
|
+
export type Launcher = keyof typeof LAUNCHERS;
|
|
18
|
+
export declare function isLauncher(value: unknown): value is Launcher;
|
|
19
|
+
/**
|
|
20
|
+
* An agent session id fit to put on a command line: letters, digits and
|
|
21
|
+
* dashes, the shape Claude Code's ids have. Anything else is refused rather
|
|
22
|
+
* than escaped — there is no legitimate id that needs escaping.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isAgentSessionId(value: unknown): value is string;
|
|
2
25
|
export declare class TerminalHub {
|
|
3
26
|
private readonly terminals;
|
|
4
27
|
private seq;
|
|
@@ -8,11 +31,14 @@ export declare class TerminalHub {
|
|
|
8
31
|
name: string;
|
|
9
32
|
exited: boolean;
|
|
10
33
|
}>;
|
|
11
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* A new shell in `cwd`; `replace` names one it takes the place of, and
|
|
36
|
+
* `run` a launcher typed into it as soon as it starts.
|
|
37
|
+
*/
|
|
12
38
|
open(cwd: string, size: {
|
|
13
39
|
cols?: unknown;
|
|
14
40
|
rows?: unknown;
|
|
15
|
-
}, replace?: string): Promise<{
|
|
41
|
+
}, replace?: string, run?: Launcher, resume?: string): Promise<{
|
|
16
42
|
id: string;
|
|
17
43
|
name: string;
|
|
18
44
|
} | {
|