papilot 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -4
- package/README.zh-CN.md +15 -3
- package/bin/doctor.mjs +239 -0
- package/bin/papilot.mjs +96 -87
- package/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/assets/index-CqhlgnOu.css +1 -0
- package/dist/assets/index-Czf1eCno.js +185 -0
- package/dist/assets/math-DEcVZfaU.css +1 -0
- package/dist/assets/math-Ebyrobf9.js +263 -0
- package/dist/assets/{pdf-view-DJo0X38w.js → pdf-view-BhJZr4qw.js} +18 -18
- package/dist/index.html +11 -2
- package/examples/papilot-paper/preamble.tex +17 -11
- package/examples/papilot-paper/sections/02-using.tex +3 -3
- package/package.json +12 -2
- package/scripts/service.ts +8 -2
- package/skills/check-math.md +12 -0
- package/skills/consistency.md +11 -0
- package/skills/latex-warnings.md +12 -0
- package/skills/polish.md +13 -0
- package/skills/proofread.md +12 -0
- package/skills/reviewer-response.md +11 -0
- package/skills/shorten.md +12 -0
- package/src/conversation.ts +37 -0
- package/src/notes.ts +5 -0
- package/src/server/agents.ts +4 -3
- package/src/server/codex.ts +2 -2
- package/src/server/main.ts +21 -7
- package/src/server/paths.ts +2 -1
- package/src/server/review.ts +1 -1
- package/src/server/service-environment.ts +22 -0
- package/src/server/skills.ts +57 -0
- package/src/server/workspace.ts +43 -19
- package/src/shared.ts +10 -2
- package/vendor/latex-review/dist/web/assets/index-Db7pq4Cj.css +1 -0
- package/vendor/latex-review/dist/web/assets/index-DqkrTw0t.js +19 -0
- package/vendor/latex-review/dist/web/index.html +2 -2
- package/vendor/latex-review/src/client.ts +10 -3
- package/vendor/latex-review/src/core/build.ts +53 -7
- package/vendor/latex-review/src/core/outline.ts +6 -3
- package/vendor/latex-review/src/core/publish.ts +8 -6
- package/vendor/latex-review/src/runtime.ts +14 -0
- package/vendor/latex-review/src/server.ts +2 -1
- package/vendor/latex-review/src/types.ts +16 -1
- package/vendor/latex-review/src/web/ChangeMap.tsx +21 -9
- package/vendor/latex-review/src/web/PdfStrip.tsx +5 -1
- package/vendor/latex-review/src/web/i18n.ts +48 -0
- package/vendor/latex-review/src/web/model.ts +23 -4
- package/vendor/latex-review/src/web/overview.tsx +25 -20
- package/vendor/latex-review/src/web/review.tsx +387 -143
- package/vendor/latex-review/src/web/style.css +106 -0
- package/vendor/latex-review/src/web/zh-CN.ts +186 -0
- package/dist/assets/index-BhQFCMTx.js +0 -131
- package/dist/assets/index-CAV3-87U.css +0 -1
- package/vendor/latex-review/dist/web/assets/index-DyzYnIGk.css +0 -1
- package/vendor/latex-review/dist/web/assets/index-HxBK-dls.js +0 -19
package/src/server/workspace.ts
CHANGED
|
@@ -8,7 +8,10 @@ import { Store } from './store.ts';
|
|
|
8
8
|
import { Codex, RpcResponseError } from './codex.ts';
|
|
9
9
|
import { AGENT_NAMES, AntigravityAgent, ClaudeAgent, type AgentRuntime } from './agents.ts';
|
|
10
10
|
import { reviewClient, taskRoute, confirmedReviewResponse, type ReviewClient } from './review.ts';
|
|
11
|
-
import
|
|
11
|
+
import { VERSION } from './paths.ts';
|
|
12
|
+
import { findSkill, type SkillFile } from './skills.ts';
|
|
13
|
+
import { reviewInstructions, skillPrompt, summarizeFeedback } from '../conversation.ts';
|
|
14
|
+
import type { Task, Run, TimelineItem, PendingRequest, RuntimeStatus, Snapshot, PendingReview, AgentModel, Usage, AgentId, MessageMeta, FeedbackSummary } from '../shared.ts';
|
|
12
15
|
|
|
13
16
|
const execute = promisify(execFile);
|
|
14
17
|
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
|
@@ -83,7 +86,7 @@ export class Workspace extends EventEmitter {
|
|
|
83
86
|
if (id === 'codex') this.loaded.clear();
|
|
84
87
|
this.emit('change');
|
|
85
88
|
}
|
|
86
|
-
snapshot(): Snapshot { return { version:
|
|
89
|
+
snapshot(): Snapshot { return { version: VERSION, projects: this.store.projects(), tasks: this.store.tasks(), requests: [...this.requests.values()].map(({ rpcId: _, agent: __, ...r }) => r), runtime: this.runtime, runtimes: this.runtimes }; }
|
|
87
90
|
async connect() {
|
|
88
91
|
if (this.connecting) return this.connecting;
|
|
89
92
|
this.connecting = Promise.all(AGENTS.map(id => this.connectAgent(id))).then(() => { this.emit('change'); this.schedule(); }).finally(() => { this.connecting = null; });
|
|
@@ -121,25 +124,30 @@ export class Workspace extends EventEmitter {
|
|
|
121
124
|
const project = { ...this.store.project(projectId), name };
|
|
122
125
|
this.store.saveProject(project); return project;
|
|
123
126
|
}
|
|
124
|
-
|
|
127
|
+
// A task can start from a skill; its instructions are then the author's own words, possibly none.
|
|
128
|
+
async createTask(input: { projectId: string; title: string; instructions: string; skill?: string; agent?: AgentId; model?: string; effort?: string; id: string }) {
|
|
125
129
|
if (!/^[\w-]{16,64}$/.test(input.id)) throw new Error('Invalid task ID.');
|
|
126
130
|
if (this.store.taskDeleted(input.id)) throw new Error('This task was deleted. Create a new task instead.');
|
|
127
131
|
const existing = this.store.tasks().find(t => t.id === input.id); if (existing) return existing;
|
|
128
132
|
if (this.preparing.has(input.id)) throw new Error('This task is being created. Refresh in a moment.');
|
|
129
133
|
this.preparing.add(input.id);
|
|
130
134
|
try {
|
|
131
|
-
const project = this.store.project(input.projectId),
|
|
135
|
+
const project = this.store.project(input.projectId), skill = input.skill ? findSkill(project.path, input.skill) : undefined;
|
|
136
|
+
if (!skill && !input.instructions.trim()) throw new Error('Describe the revision, or choose a skill.');
|
|
137
|
+
const client = await this.connectReview(project.path);
|
|
132
138
|
const name = 'p-' + input.id.toLowerCase().replace(/[^a-z0-9-]/g, '').slice(0, 48);
|
|
133
139
|
const info = await client.post('/api/tasks', { name, title: input.title });
|
|
134
|
-
const task: Task = { id: input.id, projectId: project.id, name, title: input.title, instructions: input.instructions,
|
|
140
|
+
const task: Task = { id: input.id, projectId: project.id, name, title: input.title, instructions: skill ? skillPrompt(skill, input.instructions) : input.instructions,
|
|
135
141
|
worktree: info.worktree, threadId: null, turnId: null, agent: input.agent || 'codex', model: input.model || null, effort: input.effort || null, status: 'idle',
|
|
136
142
|
createdAt: Date.now(), updatedAt: Date.now(), feedbackCursor: 0, error: null, review: null };
|
|
137
143
|
this.store.saveTask(task);
|
|
138
|
-
await this.enqueue(task.id, input.instructions, 'initial-' + task.id, false);
|
|
144
|
+
await this.enqueue(task.id, input.instructions, 'initial-' + task.id, false, { skill });
|
|
139
145
|
return this.store.task(task.id);
|
|
140
146
|
} finally { this.preparing.delete(input.id); }
|
|
141
147
|
}
|
|
142
|
-
|
|
148
|
+
// The author's message, a skill run with or without one, or the review
|
|
149
|
+
// feedback alone. The chat keeps what the author did; the agent gets the prompt.
|
|
150
|
+
async enqueue(taskId: string, text: string, requestKey: string, includeFeedback = true, options: { skill?: SkillFile; feedback?: boolean } = {}) {
|
|
143
151
|
const task = this.store.task(taskId);
|
|
144
152
|
if (task.status === 'deleting') throw new Error('This task is being deleted.');
|
|
145
153
|
const existing = this.store.runByKey(requestKey);
|
|
@@ -147,15 +155,18 @@ export class Workspace extends EventEmitter {
|
|
|
147
155
|
if (task.status === 'uncertain') throw new Error('Check the task status before sending more instructions.');
|
|
148
156
|
const pauseVersion = task.pauseVersion || 0;
|
|
149
157
|
if (this.store.runs().some(r => r.taskId === taskId && r.status === 'queued')) throw new Error('This task already has queued instructions. Wait or pause it first.');
|
|
150
|
-
let prompt = text.trim(), cursor = task.feedbackCursor;
|
|
158
|
+
let prompt = options.feedback ? reviewInstructions : options.skill ? skillPrompt(options.skill, text) : text.trim(), cursor = task.feedbackCursor;
|
|
159
|
+
let summary: FeedbackSummary | undefined;
|
|
151
160
|
if (includeFeedback) {
|
|
152
161
|
const client = await this.connectReview(this.store.project(task.projectId).path);
|
|
153
162
|
const feedback = await client.get(`${taskRoute(task.name)}/feedback?after=${task.feedbackCursor}`);
|
|
154
|
-
cursor = feedback.cursor;
|
|
163
|
+
cursor = feedback.cursor; summary = summarizeFeedback(feedback);
|
|
164
|
+
if (options.feedback && !summary) throw new Error('There is no new review feedback to send.');
|
|
155
165
|
if (feedback.feedback?.length || feedback.build_error) prompt += '\n\nPapilot review feedback (bound to the specified review round and source changes):\n' + JSON.stringify(feedback, null, 2);
|
|
156
166
|
if (task.error) prompt += '\n\nLatest task/review error:\n' + task.error + '\nThe latest accepted manuscript is at ' + this.store.project(task.projectId).path + '. You may read it to understand accepted changes, but must only edit your isolated task worktree. Prepare a revised proposal for another review; never write to or accept changes in the manuscript checkout.';
|
|
157
167
|
}
|
|
158
168
|
const run: Run = { id: randomUUID(), taskId, requestKey, prompt, feedbackCursor: cursor, status: 'queued', createdAt: Date.now(), finishedAt: null };
|
|
169
|
+
const meta: MessageMeta = { ...(options.skill ? { skill: { id: options.skill.id, name: options.skill.name } } : {}), ...(summary ? { feedback: summary } : {}), ...(options.feedback ? { sendsFeedback: true } : {}) };
|
|
159
170
|
const saved = this.store.transaction(() => {
|
|
160
171
|
// Feedback loading yields to other requests. Recheck identity, queue, and
|
|
161
172
|
// task status together before committing; none of the earlier reads own a lock.
|
|
@@ -171,7 +182,7 @@ export class Workspace extends EventEmitter {
|
|
|
171
182
|
if (current.status === 'uncertain') throw new Error('Check the task status before sending more instructions.');
|
|
172
183
|
this.store.saveRun(run);
|
|
173
184
|
if (!BUSY.has(current.status)) this.store.updateTask(taskId, { status: 'queued', error: null });
|
|
174
|
-
this.store.saveItem({ id: run.id, taskId, type: 'user', text: text.trim(), createdAt: run.createdAt });
|
|
185
|
+
this.store.saveItem({ id: run.id, taskId, type: 'user', text: options.feedback ? '' : text.trim(), createdAt: run.createdAt, ...(Object.keys(meta).length ? { meta } : {}) });
|
|
175
186
|
return run;
|
|
176
187
|
});
|
|
177
188
|
this.schedule();
|
|
@@ -199,7 +210,7 @@ export class Workspace extends EventEmitter {
|
|
|
199
210
|
Your task is already created. Your editable working directory is ${task.worktree}.
|
|
200
211
|
Original task: ${task.instructions}
|
|
201
212
|
Only edit this isolated task worktree. Never edit the manuscript checkout, other task worktrees, or Papilot's own source/state. Never commit, push, stash, or accept/reject/undo review decisions.
|
|
202
|
-
Papilot owns the LaTeX review lifecycle: it compiles and publishes automatically AFTER your turn finishes, and delivers review feedback in a new turn. Do not create another review task, invoke publish, or wait on feedback --wait. This application workflow replaces the standalone latex-review skill's publish-and-wait loop. End your turn after completing the requested edits, with a concise summary.
|
|
213
|
+
Papilot owns the LaTeX review lifecycle: it compiles and publishes automatically AFTER your turn finishes, and delivers review feedback in a new turn. Do not create another review task, invoke publish, or wait on feedback --wait. This application workflow replaces the standalone latex-review skill's publish-and-wait loop. End your turn after completing the requested edits, with a concise summary for the author of what you changed and why; do not describe Papilot's review workflow.
|
|
203
214
|
Preserve meaning and mathematical correctness. Read relevant project instructions. Treat reviewer comments as instructions tied to their round, file and changes. Rejected edits must not be reintroduced unless explicitly requested. If there is a conflict with accepted text, explain it and propose a revision for a new review round; never overwrite the accepted manuscript.
|
|
204
215
|
${this.agentOf(task) === 'antigravity' ? 'Shell commands are not available to you here and any attempt is refused, which ends your turn: read, search and edit files in the task worktree with your file tools only.' : 'You may run tools in the task worktree to inspect and validate your changes.'} If a decision requires the user, ask through the available user-input tool or explain it in your final response. Use English for your progress updates, questions, and final summaries unless the user explicitly requests another language. Preserve the manuscript language unless asked to translate it.`;
|
|
205
216
|
}
|
|
@@ -459,7 +470,7 @@ ${this.agentOf(task) === 'antigravity' ? 'Shell commands are not available to yo
|
|
|
459
470
|
if (this.closing) return;
|
|
460
471
|
const current = await client.get(`/api/jobs/${job.job}`);
|
|
461
472
|
const summary = await client.get(taskRoute(task.name));
|
|
462
|
-
this.store.updateTask(taskId, { review: { state: summary.state, round: summary.round, counts: summary.counts, job: current
|
|
473
|
+
this.store.updateTask(taskId, { review: { ...this.store.task(taskId).review, state: summary.state, round: summary.round, counts: summary.counts, job: current } });
|
|
463
474
|
if (current.status === 'done' || current.status === 'completed' || current.status === 'succeeded') break;
|
|
464
475
|
if (current.status === 'failed') throw new Error([current.error, ...(current.details || [])].filter(Boolean).join('\n') || 'PDF build failed.');
|
|
465
476
|
await delay(1500);
|
|
@@ -472,19 +483,22 @@ ${this.agentOf(task) === 'antigravity' ? 'Shell commands are not available to yo
|
|
|
472
483
|
try {
|
|
473
484
|
const task = this.store.task(taskId);
|
|
474
485
|
if (task.status === 'deleting') return;
|
|
475
|
-
const
|
|
486
|
+
const project = this.store.project(task.projectId), client = await this.connectReview(project.path);
|
|
476
487
|
const summary = await client.get(taskRoute(task.name));
|
|
477
|
-
const unsent = await this.
|
|
488
|
+
const { unsent, comments } = await this.unsentReviewFeedback(client, this.store.task(taskId));
|
|
489
|
+
const files = await acceptedFiles(project.path, task.name);
|
|
478
490
|
const current = this.store.task(taskId);
|
|
479
|
-
if (current.status !== 'deleting') this.store.updateTask(taskId, { review: { state: summary.state, round: summary.round, counts: summary.counts, job: current.review?.job, unsent } });
|
|
491
|
+
if (current.status !== 'deleting') this.store.updateTask(taskId, { review: { state: summary.state, round: summary.round, counts: summary.counts, job: current.review?.job, unsent, comments, files } });
|
|
480
492
|
} catch { /* The task remains visible even if its project is temporarily unavailable. */ }
|
|
481
493
|
}
|
|
482
|
-
// Review comments
|
|
483
|
-
//
|
|
484
|
-
|
|
494
|
+
// Review decisions, comments and undo updates the agent has not received yet.
|
|
495
|
+
// The engine excludes decisions undone before delivery. A queued or in-flight message
|
|
496
|
+
// already carries the feedback up to its own cursor. Comments and notes ask for a revision.
|
|
497
|
+
private async unsentReviewFeedback(client: ReviewClient, task: Task) {
|
|
485
498
|
const after = Math.max(task.feedbackCursor, ...this.store.runs().filter(r => r.taskId === task.id && ['queued', 'running', 'uncertain'].includes(r.status)).map(r => r.feedbackCursor || 0));
|
|
486
499
|
const feedback = await client.get<{ feedback?: { kind?: string }[] }>(`${taskRoute(task.name)}/feedback?after=${after}`);
|
|
487
|
-
|
|
500
|
+
const items = feedback.feedback || [];
|
|
501
|
+
return { unsent: items.length, comments: items.filter(item => item.kind === 'comment' || item.kind === 'note').length };
|
|
488
502
|
}
|
|
489
503
|
renameTask(taskId: string, title: string) {
|
|
490
504
|
if (this.store.task(taskId).status === 'deleting') throw new Error('This task is being deleted.');
|
|
@@ -527,3 +541,13 @@ ${this.agentOf(task) === 'antigravity' ? 'Shell commands are not available to yo
|
|
|
527
541
|
}
|
|
528
542
|
close() { this.closing = true; for (const timer of this.usageTimers.values()) clearTimeout(timer); this.flush(); for (const id of AGENTS) this.agents[id].close(); }
|
|
529
543
|
}
|
|
544
|
+
|
|
545
|
+
// The review engine records each task's accepted text as a commit beside the
|
|
546
|
+
// one it started from; their difference is what the task put in the
|
|
547
|
+
// manuscript. A rename counts as both of its paths.
|
|
548
|
+
export async function acceptedFiles(root: string, name: string) {
|
|
549
|
+
try {
|
|
550
|
+
const { stdout } = await execute('git', ['-C', root, 'diff', '--name-only', '--no-renames', '-z', `refs/latex-review/${name}/base`, `refs/latex-review/${name}/accepted`, '--']);
|
|
551
|
+
return stdout.split('\0').filter(Boolean);
|
|
552
|
+
} catch { return []; }
|
|
553
|
+
}
|
package/src/shared.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export type TaskStatus = 'idle' | 'queued' | 'running' | 'waiting_input' | 'building' | 'reviewing' | 'interrupted' | 'failed' | 'uncertain' | 'deleting';
|
|
2
2
|
export interface Project { id: string; name: string; path: string; createdAt: number }
|
|
3
|
-
|
|
3
|
+
// unsent counts review updates the agent has not received; comments are the
|
|
4
|
+
// ones that ask for a revision. files are the manuscript files this task's
|
|
5
|
+
// accepted changes touched, relative to the repository root.
|
|
6
|
+
export interface ReviewSummary { state: string; round: number | null; counts: Record<string, number>; unsent?: number; comments?: number; files?: string[]; next?: string; job?: { phase: string; status: string; error?: string; details?: string[] } | null }
|
|
4
7
|
export interface PendingReview { route: string; body: Record<string, unknown>; previousStatus: TaskStatus }
|
|
5
8
|
export interface Task {
|
|
6
9
|
id: string; projectId: string; name: string; title: string; instructions: string;
|
|
@@ -11,7 +14,12 @@ export interface Task {
|
|
|
11
14
|
pendingReview?: PendingReview | null;
|
|
12
15
|
}
|
|
13
16
|
export interface Run { id: string; taskId: string; requestKey: string; prompt: string; feedbackCursor?: number; status: 'queued' | 'running' | 'completed' | 'failed' | 'interrupted' | 'uncertain'; createdAt: number; finishedAt: number | null }
|
|
14
|
-
export interface TimelineItem { id: string; taskId: string; type: string; text: string; detail?: string; status?: string; createdAt: number }
|
|
17
|
+
export interface TimelineItem { id: string; taskId: string; type: string; text: string; detail?: string; status?: string; createdAt: number; meta?: MessageMeta }
|
|
18
|
+
// A user message can run a skill or carry review feedback; the chat shows
|
|
19
|
+
// those as what they are rather than as the prompt the agent receives.
|
|
20
|
+
export interface MessageMeta { skill?: { id: string; name: string }; feedback?: FeedbackSummary; sendsFeedback?: boolean }
|
|
21
|
+
export interface FeedbackSummary { round: number | null; accepted: number; rejected: number; undone: number; comments: { where: string; text: string }[]; buildError?: boolean }
|
|
22
|
+
export interface Skill { id: string; name: string; description: string; hint: string; source: 'builtin' | 'user' | 'paper' }
|
|
15
23
|
export interface PendingRequest { id: string; taskId: string; method: string; params: Record<string, any>; createdAt: number }
|
|
16
24
|
export type AgentId = 'codex' | 'claude' | 'antigravity';
|
|
17
25
|
export interface AgentModel { id: string; name: string; isDefault: boolean; efforts: string[]; defaultEffort: string | null }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@font-face{font-family:Review Serif;src:url(/assets/TeXGyrePagellaX-Regular-DQJjZE_L.otf)format("opentype");font-weight:400;font-display:swap}@font-face{font-family:Review Serif;src:url(/assets/TeXGyrePagellaX-Bold-CEtYdOXj.otf)format("opentype");font-weight:700;font-display:swap}:root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--serif:"Review Serif", Palatino, "Palatino Linotype", "Songti SC", Georgia, serif;--sans:-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Noto Sans", sans-serif;--frame:#fff;--bar:#fff;--ink:#171717;--muted:#666;--line:#e5e5e5;--paper:#fff;--accent:#356aa0;--accept:#1d7448;--reject:#b0313b;--comment:#356aa0;--before:#fbeeee;--before-edge:#d17c82;--after:#eef8f1;--after-edge:#68ab82;--head:#fafafa;--shadow:0 1px 2px #0000000f, 0 4px 18px #0000000d;--paper-top:24px;font:14px/1.5 var(--sans);color:var(--ink);background:var(--frame)}*{box-sizing:border-box}body{background:var(--frame);text-rendering:optimizelegibility;grid-template-rows:auto 1fr;height:100dvh;margin:0;display:grid;overflow:hidden}button,input,select,textarea{font:inherit;color:inherit}button{cursor:pointer}button:disabled{cursor:default;opacity:.38}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid var(--accent);outline-offset:3px}[hidden]{display:none!important}.muted{color:var(--muted)}a{color:var(--accent);text-underline-offset:.18em}h1,h2,.brand{font-family:var(--serif);font-weight:400;line-height:1.3}.brand{letter-spacing:.02em;color:var(--ink);font-size:20px;font-variant-caps:small-caps;text-decoration:none}kbd{border:1px solid var(--line);color:var(--muted);background:var(--frame);vertical-align:1px;border-radius:4px;margin-left:6px;padding:2px 4px;font:600 10px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.bar{background:var(--bar);border-bottom:1px solid var(--line);z-index:3;align-items:center;gap:18px;min-height:62px;padding:10px 24px;display:flex}.identity{flex:1;align-items:baseline;gap:10px;min-width:0;display:flex}.identity h1{letter-spacing:.02em;white-space:nowrap;text-overflow:ellipsis;margin:0;font-size:20px;font-variant-caps:small-caps;overflow:hidden}.identity span{white-space:nowrap;font-size:13px}.meter{color:var(--muted);white-space:nowrap;font-variant-numeric:tabular-nums;align-items:center;gap:10px;font-size:12px;display:flex}.track{background:var(--line);width:120px;height:2px;overflow:hidden}.track i{background:#999;width:0;height:100%;transition:width .3s;display:block}.bar-end{align-items:center;gap:8px;display:flex}.status:empty{display:none}.status{background:var(--head);color:var(--muted);white-space:nowrap;border-radius:999px;padding:3px 9px;font-size:12px}.status[data-state=build_failed],.status[data-state=offline]{color:var(--reject)}.ghost{color:var(--muted);background:0 0;border:1px solid #0000;border-radius:7px;padding:5px 9px;font-size:13px}.ghost:hover{border-color:var(--line);color:var(--ink);text-underline-offset:.18em;text-decoration:underline}.accept-all,.reject-all{color:var(--ink);border-color:var(--line);border-radius:999px;box-shadow:0 1px 3px #0000000d}.overview-link{color:var(--accent);flex:none;text-decoration:none}.ghost.round{border-color:var(--line);border-radius:50%;width:30px;height:30px;padding:0}#paper{padding:var(--paper-top) 24px 90px;overflow-anchor:none;outline:none;scroll-padding:70px 0 90px;overflow:auto}.document{max-width:980px;margin:0 auto}.empty{text-align:center;color:var(--muted);padding:60px 16px}.part+.part{margin-top:30px}.part-title{color:var(--muted);letter-spacing:.02em;margin:14px 0 18px;font-size:19px;font-variant-caps:small-caps;font-weight:400}.part-title .number{font-variant-numeric:tabular-nums;margin-right:8px}.sheet{background:var(--paper);box-shadow:var(--shadow);border-radius:3px;padding:10px 0}.context,.side{line-height:0}.document img{width:100%;height:auto;display:block}.fold{color:#9a9d98;letter-spacing:4px;background:repeating-linear-gradient(90deg,#0000 0 6px,#ececec 6px 12px) 50%/100% 1px no-repeat;justify-content:center;align-items:center;height:22px;margin:4px 0;font-size:16px;display:flex}.change{color:var(--ink);outline:none;scroll-margin:70px;position:relative}.change.standalone{background:var(--paper);box-shadow:var(--shadow);border-radius:3px;margin-bottom:26px}.change.inline{margin:6px 0}.change-head{background:var(--head);color:var(--muted);border-top:1px solid var(--line);border-bottom:1px solid var(--line);align-items:baseline;gap:10px;padding:8px 12px;font-size:12px;line-height:1.6;display:flex}.change-number{color:var(--ink);white-space:nowrap;font-weight:400}.change-where{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font:11px/1.8 ui-monospace,SFMono-Regular,Menlo,monospace;overflow:hidden}.change-kind{white-space:nowrap}.badge{white-space:nowrap;font-weight:600}.badge:empty{display:none}.link{color:var(--accent);background:0 0;border:0;padding:0 2px;font-size:12px}.link.active{font-weight:600;text-decoration:underline}.edits{white-space:nowrap}.side{border-left:2px solid #0000;position:relative}.side.before{background:var(--before);border-left-color:var(--before-edge)}.side.after{background:var(--after);border-left-color:var(--after-edge)}.side.after.identical{border-left-color:var(--line);background:0 0}.side img{mix-blend-mode:multiply}.side:after{font:10px/1 var(--sans);letter-spacing:.06em;text-transform:uppercase;color:var(--before-edge);opacity:.9;pointer-events:none;position:absolute;top:4px;right:8px}.side.before:after{content:"before"}.side.after:after{content:"after";color:var(--after-edge)}:where(:lang(zh-CN)) .side.before:after{content:"修改前"}:where(:lang(zh-CN)) .side.after:after{content:"修改后"}.side.after.identical:after,.change[data-resolved] .side:after{content:none}.caption{color:var(--muted);margin:0;padding:4px 12px 8px;font-size:12px}.change.current{box-shadow:0 0 0 2px var(--accent);z-index:1}.change.current .change-head{background:color-mix(in srgb, var(--accent) 8%, var(--head));color:var(--ink)}.change[data-state=accepted] .badge{color:var(--accept)}.change[data-state=rejected] .badge{color:var(--reject)}.change[data-state=commented] .badge,.change[data-state=mixed] .badge{color:var(--comment)}.change[data-resolved=after] .side.before,.change[data-resolved=before] .side.after:not(.identical){display:none}.change[data-resolved] .side{background:0 0;border-left-color:#0000}.change[data-resolved] .caption{display:none}.remark,.histories{padding:8px 14px;font-size:13px;line-height:1.55}.remark{background:color-mix(in srgb, var(--comment) 8%, #fff);border-top:1px solid var(--line);color:var(--ink)}.remark p,.histories p{white-space:pre-wrap;margin:0}.histories{border-bottom:1px solid var(--line);color:var(--muted);background:#f6f7fb}.source{background:var(--paper);border-top:1px solid var(--line);padding:10px 14px 12px;line-height:1.5}.source-unit+.source-unit{margin-top:12px}.source-title{color:#555;align-items:center;gap:8px;margin-bottom:6px;font:11px ui-monospace,SFMono-Regular,Menlo,monospace;display:flex}.unit-actions{font:13px var(--sans);align-items:center;gap:2px;margin-left:auto;display:flex}.unit-actions button{border:1px solid var(--line);background:var(--paper);border-radius:0;padding:1px 8px;font-weight:600}.unit-actions button:not(:disabled):hover{background:var(--frame)}.unit-actions .accept{color:var(--accept)}.unit-actions .reject{color:var(--reject)}.unit-actions .comment{color:var(--comment)}.unit-state{font-weight:600}.source-unit[data-state=accepted] .unit-state{color:var(--accept)}.source-unit[data-state=rejected] .unit-state{color:var(--reject)}.source-unit[data-state=commented] .unit-state{color:var(--comment)}.source-code{border:1px solid var(--line);white-space:pre-wrap;overflow-wrap:anywhere;color:#2b2f33;background:#fff;border-radius:5px;max-height:420px;margin:0;padding:10px 12px;font:12.5px/1.65 ui-monospace,SFMono-Regular,Menlo,monospace;overflow:auto}.source-code del{color:#8e1d28;background:#fadfe1;-webkit-text-decoration:line-through #c9636b;text-decoration:line-through #c9636b}.source-code ins{color:#11603a;background:#d6f0df;text-decoration:none}.banner{background:color-mix(in srgb, var(--accent) 6%, var(--paper));border:1px solid var(--line);border-radius:8px;max-width:980px;margin:0 auto 14px;padding:10px 14px;font-size:13px}.banner p{margin:3px 0}.banner .details{background:var(--bar);white-space:pre-wrap;border-radius:5px;max-height:220px;margin:6px 0 2px;padding:8px 10px;font:11.5px/1.5 ui-monospace,Menlo,monospace;overflow:auto}.banner summary{cursor:pointer;color:var(--muted)}.waiting{text-align:center;padding:90px 16px}.waiting p{margin:6px 0}.pulse{background:var(--accent);border-radius:50%;width:12px;height:12px;margin:0 auto 18px;animation:1.6s ease-in-out infinite pulse}@keyframes pulse{0%,to{opacity:.25;transform:scale(.8)}50%{opacity:1;transform:scale(1.15)}}.task-list{background:var(--bar);border:1px solid var(--line);box-shadow:var(--shadow);border-radius:8px;margin:0;padding:0;list-style:none}.task-list li+li{border-top:1px solid var(--line)}.index .document{max-width:880px}.overview-heading{margin:32px 0}.overview-heading h1{letter-spacing:.02em;margin:0 0 6px;font-size:34px;font-variant-caps:small-caps}.overview-heading p{margin:6px 0}.overview-summary{color:var(--muted);font-size:13px}.task-tools{gap:10px;margin-bottom:16px;display:flex}.task-tools input,.task-tools select{font:inherit;color:var(--ink);background:var(--bar);border:1px solid var(--line);border-radius:7px;padding:9px 12px}.task-tools input{flex:1;min-width:0}.task-tools input::placeholder{color:var(--muted)}.task-card{color:var(--ink);border-radius:7px;padding:24px 20px;text-decoration:none;display:block}.task-card:hover{background:var(--head)}.task-card:focus-visible,.overview-link:focus-visible,.task-tools :focus-visible{outline:2px solid var(--accent);outline-offset:2px}.task-heading{justify-content:space-between;align-items:baseline;gap:10px;display:flex}.task-heading h2{overflow-wrap:anywhere;margin:0;font-size:21px;font-weight:400}.task-meta{color:var(--muted);flex-wrap:wrap;gap:4px 14px;margin-top:5px;font-size:12px;display:flex}.task-name{overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.task-progress{color:var(--muted);align-items:center;gap:10px;margin-top:14px;font-size:12px;display:flex}.task-progress .track{width:140px}.task-footer{justify-content:space-between;align-items:baseline;gap:10px;margin-top:9px;font-size:12px;display:flex}.task-counts{color:var(--muted);flex-wrap:wrap;gap:4px 14px;display:flex}.task-counts [data-kind=pending]{color:var(--accent);font-weight:600}.task-counts [data-kind=commented]{color:var(--comment)}.task-open{color:var(--accent);flex:none;font-weight:600}.actions{z-index:4;background:var(--bar);border:1px solid var(--line);max-width:calc(100vw - 24px);box-shadow:var(--shadow);border-radius:12px;align-items:center;gap:2px;padding:5px;display:flex;position:fixed;bottom:22px;left:50%;transform:translate(-50%)}.actions button{white-space:nowrap;background:0 0;border:0;border-radius:8px;padding:9px 14px;font-size:14px}.actions button:not(:disabled):hover{background:var(--head)}.actions .nav{color:var(--muted);padding:6px 12px;font-size:20px;line-height:1}.actions .accept{color:var(--accept)}.actions .reject{color:var(--reject)}.actions .comment{color:var(--comment)}.actions .skip{font-weight:600}.actions .undo{color:var(--muted);border-left:1px solid var(--line);border-radius:0 8px 8px 0;margin-left:4px;font-size:13px}.actions .working{opacity:.55}.position{text-align:center;min-width:52px;color:var(--muted);font-variant-numeric:tabular-nums;font-size:12px}.complete{color:var(--muted);white-space:nowrap;padding:0 14px;font-size:13px}.change.current>.change-head{top:calc(-1 * var(--paper-top));z-index:4;position:sticky}.change-head .dock{flex:none;align-items:center;margin:0;display:flex}.change-head .actions{background:var(--bar);border:1px solid var(--line);border-radius:10px;gap:0;max-width:none;padding:3px;position:static;transform:none;box-shadow:0 1px 2px #00000014,0 6px 20px #0000001f}.change-head .actions button{padding:4px 8px;font-size:13px;line-height:1.5}.change-head .actions .nav{padding:2px 8px;font-size:17px}.change-head .actions .undo{margin-left:3px;padding:4px 10px;font-size:12px}.change-head .position{min-width:40px;font-size:11.5px}.change-head .complete{padding:0 8px;font-size:12px}.change-head .composer{width:min(380px,100vw - 24px);position:absolute;inset:calc(100% + 6px) 8px auto auto;transform:none}.composer{z-index:5;background:var(--bar);border:1px solid var(--line);border-radius:12px;width:min(540px,100vw - 24px);padding:12px;position:fixed;bottom:84px;left:50%;transform:translate(-50%);box-shadow:0 6px 24px #00000014}.composer label{color:var(--ink);margin-bottom:6px;font-size:17px;font-weight:400;display:block}.composer textarea{resize:vertical;border:1px solid var(--line);background:var(--paper);color:#1f2328;border-radius:7px;width:100%;min-height:84px;padding:8px 10px}.composer textarea:focus{outline:2px solid color-mix(in srgb, var(--comment) 45%, transparent);border-color:#0000}.composer-row{justify-content:space-between;align-items:center;margin-top:8px;font-size:12px;display:flex}.composer-row kbd{margin-left:0}.composer button{border:1px solid var(--line);background:var(--paper);color:var(--ink);border-radius:7px;padding:6px 16px}.toast{z-index:6;border:1px solid var(--line);background:var(--paper);max-width:min(640px,100vw - 24px);color:var(--ink);white-space:pre-wrap;padding:9px 14px;font-size:13px;position:fixed;top:74px;left:50%;transform:translate(-50%);box-shadow:0 3px 12px #0000000f}.toast.error{background:#7f2b31}dialog{border:1px solid var(--line);box-shadow:var(--shadow);background:var(--bar);color:var(--ink);border-radius:12px;max-width:min(460px,100vw - 32px);padding:20px 24px}dialog::backdrop{background:#00000040}dialog h2{margin:0 0 12px;font-size:23px}dialog dl{grid-template-columns:auto 1fr;gap:8px 16px;margin:0 0 12px;font-size:13px;display:grid}dialog dt{white-space:nowrap}dialog dt kbd{margin:0 2px 0 0}dialog dd{margin:0}dialog form{text-align:right}dialog button{border:1px solid var(--line);background:var(--frame);border-radius:7px;padding:5px 14px}@media (width<=720px){.bar{grid-template-columns:auto 1fr auto;gap:8px;padding:12px 16px;display:grid}.identity{grid-area:1/1/auto/-1}.bar .meter{justify-self:center;font-size:11px}.bar-end{gap:2px}.bar .ghost{padding:4px 2px;font-size:12px}.side{min-height:20px}.track{width:70px}.actions button{padding:9px}.change-head{flex-wrap:wrap}.change-head .dock{flex-basis:100%;order:9;justify-content:center;margin:4px 0 2px}.change-head .composer{width:calc(100vw - 24px);right:0}:root{--paper-top:10px}#paper{padding:var(--paper-top) 6px 90px}#paper.index{padding:10px 14px 40px}.overview-heading{margin-top:16px}.task-tools{flex-wrap:wrap}.task-tools input{flex-basis:100%}.task-tools select{width:100%}.task-card{padding:14px}.task-heading{flex-direction:column;gap:6px}}@media (prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important;animation:none!important}}body{display:block}#root{grid-template-rows:auto 1fr;height:100dvh;display:grid;overflow:hidden}.review-body{min-height:0;position:relative}.review-body>#paper{scrollbar-width:none;height:100%;padding-right:54px}.review-body>#paper::-webkit-scrollbar{display:none}.change-map{z-index:5;border:1px solid var(--line);touch-action:none;-webkit-user-select:none;user-select:none;background:#fafafa;border-radius:7px;width:22px;position:absolute;top:12px;bottom:12px;right:18px;box-shadow:0 1px 4px #0000000d}.change-map-viewport{cursor:grab;background:#6a6f741c;border:1px solid #c6c9cc;border-radius:5px;position:absolute;left:0;right:0}.change-map-viewport:active{cursor:grabbing}.change-map-mark{--mark:#b18a3c;background:var(--mark);border:0;border-radius:2px;width:calc(100% - 10px);min-height:0;padding:0;position:absolute;left:5px}.change-map-mark[data-state=accepted]{--mark:var(--accept)}.change-map-mark[data-state=rejected]{--mark:var(--reject)}.change-map-mark[data-state=commented]{--mark:var(--comment)}.change-map-mark[data-state=mixed]{--mark:#796195}.change-map-mark[aria-current]{outline:2px solid var(--ink);outline-offset:2px}.change-map-mark:hover,.change-map-mark:focus-visible{z-index:1;outline:2px solid var(--accent);outline-offset:2px}.change-map-tooltip{border:1px solid var(--line);background:var(--paper);width:max-content;max-width:min(360px,100vw - 64px);box-shadow:var(--shadow);color:var(--ink);font:12px/1.5 var(--sans);text-align:left;overflow-wrap:anywhere;pointer-events:none;visibility:hidden;border-radius:7px;padding:7px 10px;position:absolute;top:0;right:calc(100% + 12px)}.change-map-mark:hover .change-map-tooltip,.change-map-mark:focus-visible .change-map-tooltip{visibility:visible}@media (width<=720px){.review-body>#paper{padding-right:46px}.change-map{width:18px;right:16px}.change-map-mark{width:calc(100% - 6px);left:3px}.change-head .actions{flex-wrap:wrap;justify-content:center;max-width:100%}.change-head .actions button{padding:4px 6px;font-size:12px}.change-head .actions .nav{padding:2px 6px}.change-head .position{min-width:32px}}.pdf-strip{isolation:isolate;width:100%;position:relative;container-type:inline-size}.pdf-strip img{-webkit-user-select:none;user-select:none;pointer-events:none;position:absolute;inset:0}.pdf-text{color:#0000;-webkit-user-select:text;user-select:text;cursor:text;line-height:1;position:absolute;inset:0;overflow:hidden}.pdf-text-line{white-space:pre}.pdf-glyph{white-space:pre;transform-origin:0 0;color:#0000;font-family:Times New Roman,serif;font-weight:400;line-height:1;position:absolute}.pdf-glyph::selection{color:#0000;background:#437ee759}.copy-reference{white-space:nowrap}.change-head{flex-wrap:wrap}.change-head .change-where{min-width:160px}.document .source-unit button{line-height:1.5}.help-backdrop{z-index:8;background:#00000040;place-items:center;display:grid;position:fixed;inset:0}.help-backdrop dialog{margin:0;position:static}.composer{line-height:1.5}@media (width<=900px){.change-head .dock{flex-basis:100%;order:9;justify-content:center;margin:4px 0 2px}}#root:has(>.review-mode){grid-template-rows:auto auto minmax(0,1fr)}.reviewed-change,.change.reviewed-change.current{box-shadow:none;background:0 0;border:0}.reviewed-change>.change-head,.reviewed-change>.histories,.reviewed-change>.remark,.reviewed-change>.source,.reviewed-change>.caption,.mixed-reviewed>.side{display:none}.reviewed-placeholder{color:var(--muted);font:13px/1.5 var(--sans);text-align:center;margin:0;padding:10px 14px}.review-mode{border-bottom:1px solid var(--line);background:var(--paper);font:13px/1.4 var(--sans);flex-wrap:wrap;align-items:center;gap:8px;padding:6px 14px;display:flex}.review-mode>span,.review-mode>label{color:var(--muted);margin-right:auto}.review-mode label{align-items:center;gap:8px;display:flex}.review-mode select{border:1px solid var(--line);background:var(--paper);max-width:160px;font:inherit;color:var(--ink);border-radius:6px;padding:4px 8px}.review-mode .ghost{white-space:nowrap;padding:4px 8px;font-size:13px}.review-mode .undo{border:1px solid var(--line);border-radius:6px}.review-complete{max-width:var(--page-width);border:1px solid var(--line);background:var(--paper);font:14px/1.5 var(--sans);border-radius:10px;flex-wrap:wrap;align-items:baseline;gap:2px 8px;margin:0 auto 14px;padding:9px 14px;display:flex}.review-complete strong{font-weight:600}.review-complete span{color:var(--muted)}@media (width<=680px){.review-mode{padding:5px 8px}.review-mode .ghost{min-height:36px}.review-mode label{font-size:12px}.review-mode select{max-width:135px}}
|