chesspal 0.2.3 → 0.3.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 +11 -12
- package/dist/cli/cli.js +83 -3
- package/dist/cli/command-line.js +26 -9
- package/dist/cli/icon.d.ts +25 -0
- package/dist/cli/icon.js +80 -0
- package/dist/commands/agents.d.ts +19 -2
- package/dist/commands/agents.js +41 -14
- package/dist/commands/ai-say.d.ts +2 -1
- package/dist/commands/ai-say.js +3 -4
- package/dist/commands/analyse-game.d.ts +6 -0
- package/dist/commands/analyse-game.js +51 -15
- package/dist/commands/annotate-game.d.ts +4 -0
- package/dist/commands/annotate-game.js +60 -20
- package/dist/commands/answer-lines.d.ts +10 -4
- package/dist/commands/answer-lines.js +30 -4
- package/dist/commands/coach.js +6 -4
- package/dist/commands/context.d.ts +11 -1
- package/dist/commands/context.js +5 -0
- package/dist/commands/delete-review.js +9 -4
- package/dist/commands/game-summary.d.ts +9 -2
- package/dist/commands/game-summary.js +11 -0
- package/dist/commands/index.d.ts +4 -3
- package/dist/commands/index.js +8 -5
- package/dist/commands/init.js +2 -4
- package/dist/commands/instructions.d.ts +9 -0
- package/dist/commands/instructions.js +30 -0
- package/dist/commands/lead-input.d.ts +1 -1
- package/dist/commands/lead-input.js +7 -4
- package/dist/commands/look.d.ts +9 -5
- package/dist/commands/look.js +19 -9
- package/dist/commands/page-link.js +6 -5
- package/dist/commands/past-games.d.ts +1 -1
- package/dist/commands/past-games.js +12 -5
- package/dist/commands/player-move.js +2 -3
- package/dist/commands/ponder.js +1 -1
- package/dist/commands/position.js +2 -2
- package/dist/commands/profiles.d.ts +23 -9
- package/dist/commands/profiles.js +76 -35
- package/dist/commands/ratings.d.ts +8 -0
- package/dist/commands/ratings.js +45 -0
- package/dist/commands/registry.d.ts +1 -1
- package/dist/commands/registry.js +3 -2
- package/dist/commands/say.d.ts +11 -6
- package/dist/commands/say.js +21 -13
- package/dist/commands/status.d.ts +6 -2
- package/dist/commands/status.js +70 -26
- package/dist/commands/study.d.ts +11 -1
- package/dist/commands/study.js +39 -7
- package/dist/commands/view.d.ts +17 -5
- package/dist/commands/view.js +67 -10
- package/dist/core/facts.d.ts +14 -2
- package/dist/core/facts.js +32 -7
- package/dist/core/game.d.ts +5 -0
- package/dist/core/game.js +9 -0
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +1 -1
- package/dist/core/placement.d.ts +63 -0
- package/dist/core/placement.js +259 -0
- package/dist/core/possibilities.d.ts +38 -0
- package/dist/core/possibilities.js +254 -0
- package/dist/core/review.d.ts +6 -1
- package/dist/core/review.js +21 -6
- package/dist/games/ai-model.d.ts +4 -2
- package/dist/games/ai-model.js +10 -1
- package/dist/games/analysis.d.ts +31 -4
- package/dist/games/analysis.js +44 -5
- package/dist/games/archive.d.ts +6 -3
- package/dist/games/archive.js +13 -5
- package/dist/games/current-game.d.ts +26 -2
- package/dist/games/current-game.js +57 -21
- package/dist/games/files.d.ts +7 -0
- package/dist/games/files.js +48 -1
- package/dist/games/index.d.ts +4 -4
- package/dist/games/index.js +4 -4
- package/dist/games/table-talk.d.ts +2 -0
- package/dist/games/table-talk.js +5 -1
- package/dist/games/talk.d.ts +4 -3
- package/dist/games/talk.js +3 -3
- package/dist/home/data-folder.d.ts +2 -0
- package/dist/home/data-folder.js +5 -1
- package/dist/home/index.d.ts +1 -1
- package/dist/home/index.js +1 -1
- package/dist/host-files/agents.d.ts +2 -2
- package/dist/host-files/agents.js +12 -6
- package/dist/host-files/host-files.d.ts +5 -3
- package/dist/host-files/host-files.js +15 -8
- package/dist/host-files/index.d.ts +2 -2
- package/dist/host-files/index.js +2 -2
- package/dist/page/app/app.js +59 -22
- package/dist/page/app/keys.js +16 -2
- package/dist/page/app/served.js +2 -2
- package/dist/page/app/title.js +17 -0
- package/dist/page/avatars/torretta.js +4 -4
- package/dist/page/board/board.js +2 -1
- package/dist/page/board/marks.js +4 -24
- package/dist/page/board/signs.js +23 -0
- package/dist/page/dialogs/analyses.js +4 -4
- package/dist/page/dialogs/dialog.js +4 -1
- package/dist/page/dialogs/dialogs.css +84 -10
- package/dist/page/dialogs/profiles.js +95 -42
- package/dist/page/game/column.js +6 -2
- package/dist/page/game/game.css +12 -0
- package/dist/page/game/move-list.js +2 -2
- package/dist/page/game/plies.js +11 -0
- package/dist/page/guide/blocks.js +67 -0
- package/dist/page/guide/en.md +66 -0
- package/dist/page/guide/foot.js +18 -0
- package/dist/page/guide/guide.css +200 -0
- package/dist/page/guide/guide.js +89 -0
- package/dist/page/guide/it.md +66 -0
- package/dist/page/guide/markdown.js +67 -0
- package/dist/page/helps/helps.css +10 -11
- package/dist/page/helps/helps.js +15 -15
- package/dist/page/layout/favicon.svg +13 -0
- package/dist/page/layout/header.js +3 -3
- package/dist/page/layout/icon.icns +0 -0
- package/dist/page/layout/icon.ico +0 -0
- package/dist/page/layout/icon.png +0 -0
- package/dist/page/layout/layout.css +140 -9
- package/dist/page/layout/window.js +45 -0
- package/dist/page/legend/legend.css +203 -0
- package/dist/page/legend/legend.js +169 -0
- package/dist/page/legend/view.js +54 -0
- package/dist/page/main.js +4 -1
- package/dist/page/players/card.js +1 -1
- package/dist/page/profile/profile.js +14 -9
- package/dist/page/profile/settings.js +9 -0
- package/dist/page/style.css +4 -0
- package/dist/page/table/opener.css +65 -0
- package/dist/page/table/opener.js +59 -0
- package/dist/page/talk/chat.css +97 -0
- package/dist/page/talk/talk.css +38 -26
- package/dist/page/talk/talk.js +31 -7
- package/dist/page/text.js +120 -18
- package/dist/page/theme.css +7 -0
- package/dist/profiles/index.d.ts +2 -1
- package/dist/profiles/index.js +2 -1
- package/dist/profiles/profiles.d.ts +10 -15
- package/dist/profiles/profiles.js +30 -15
- package/dist/profiles/ratings.d.ts +35 -0
- package/dist/profiles/ratings.js +61 -0
- package/dist/server/bell.d.ts +24 -3
- package/dist/server/bell.js +84 -10
- package/dist/server/caller.d.ts +2 -1
- package/dist/server/caller.js +3 -2
- package/dist/server/mcp.d.ts +7 -3
- package/dist/server/mcp.js +106 -56
- package/dist/server/page/analyses.d.ts +2 -0
- package/dist/server/page/analyses.js +20 -5
- package/dist/server/page/attach.d.ts +4 -0
- package/dist/server/page/attach.js +112 -0
- package/dist/server/page/chat.d.ts +60 -0
- package/dist/server/page/chat.js +79 -0
- package/dist/server/page/events.d.ts +7 -0
- package/dist/server/page/events.js +20 -0
- package/dist/server/page/files.js +10 -1
- package/dist/server/page/game.js +62 -29
- package/dist/server/page/page.d.ts +47 -7
- package/dist/server/page/page.js +74 -51
- package/dist/server/page/profiles.d.ts +5 -0
- package/dist/server/page/profiles.js +26 -10
- package/dist/server/page/table.d.ts +39 -0
- package/dist/server/page/table.js +102 -0
- package/dist/server/table/browser.d.ts +6 -0
- package/dist/server/table/browser.js +48 -0
- package/dist/server/table/hosts/chats.d.ts +7 -0
- package/dist/server/table/hosts/chats.js +31 -0
- package/dist/server/table/hosts/find.d.ts +2 -0
- package/dist/server/table/hosts/find.js +20 -0
- package/dist/server/table/hosts/hosts.d.ts +34 -0
- package/dist/server/table/hosts/hosts.js +49 -0
- package/dist/server/table/hosts/index.d.ts +3 -0
- package/dist/server/table/hosts/index.js +4 -0
- package/dist/server/table/hosts/play.d.ts +25 -0
- package/dist/server/table/hosts/play.js +152 -0
- package/dist/server/table/hosts/seat.d.ts +25 -0
- package/dist/server/table/hosts/seat.js +74 -0
- package/dist/server/table/hosts/terminal.d.ts +6 -0
- package/dist/server/table/hosts/terminal.js +63 -0
- package/dist/server/table/icon/index.d.ts +32 -0
- package/dist/server/table/icon/index.js +83 -0
- package/dist/server/table/icon/linux.d.ts +5 -0
- package/dist/server/table/icon/linux.js +47 -0
- package/dist/server/table/icon/macos.d.ts +3 -0
- package/dist/server/table/icon/macos.js +38 -0
- package/dist/server/table/icon/windows.d.ts +3 -0
- package/dist/server/table/icon/windows.js +48 -0
- package/dist/server/table/index.d.ts +6 -0
- package/dist/server/table/index.js +7 -0
- package/dist/server/table/launch.d.ts +19 -0
- package/dist/server/table/launch.js +78 -0
- package/dist/server/table/link.d.ts +10 -0
- package/dist/server/table/link.js +230 -0
- package/dist/server/table/table.d.ts +35 -0
- package/dist/server/table/table.js +94 -0
- package/dist/sites/chesscom.d.ts +4 -0
- package/dist/sites/chesscom.js +25 -0
- package/dist/sites/choice.d.ts +17 -0
- package/dist/sites/choice.js +13 -0
- package/dist/sites/index.d.ts +2 -0
- package/dist/sites/index.js +4 -0
- package/dist/sites/lichess.d.ts +4 -0
- package/dist/sites/lichess.js +32 -0
- package/dist/sites/request.d.ts +11 -0
- package/dist/sites/request.js +15 -0
- package/dist/sites/sites.d.ts +6 -0
- package/dist/sites/sites.js +33 -0
- package/instructions/analysis.md +1 -1
- package/instructions/analyst-agent.md +1 -1
- package/instructions/coach.md +2 -1
- package/instructions/opponent.md +6 -6
- package/instructions/ponder.md +1 -0
- package/instructions/start.md +1 -0
- package/package.json +1 -1
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
// The analyst's tool (D-21, D-68): a game over, move by move in one call, facts only and never a judgment, with the analyst's instructions once in a chat.
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { clockOfComment, clockText, closingOf, Game, gameReviewOf, isReviewed, leadOf, leadText, levelOf, materialOf, readPgn, reviewed, reviewOf, timeControlOfTag, timeControlText, withGameReview, withLead, } from "../core/index.js";
|
|
4
|
-
import { isGameName, keepToStudy, lastChatStart, planOf, rewriteGame, startAnalysis } from "../games/index.js";
|
|
5
|
-
import { analystAgent,
|
|
4
|
+
import { gameInAnalysis, isGameName, keepToStudy, lastChatStart, planOf, rewriteGame, startAnalysis } from "../games/index.js";
|
|
5
|
+
import { analystAgent, instructionsFile } from "../host-files/index.js";
|
|
6
6
|
import { activeProfile, gameFolder } from "../profiles/index.js";
|
|
7
|
+
import { analystOf, anotherAnalyst } from "./agents.js";
|
|
7
8
|
import { bookOpening, bookReaches } from "./opening-book.js";
|
|
9
|
+
import { playerLine } from "./profiles.js";
|
|
8
10
|
import { defineCommand } from "./registry.js";
|
|
9
11
|
import { speechLine, speechOf } from "./speech.js";
|
|
10
12
|
import { gameToStudy, knownTag, moveNumbers, namedGame, noGameGoingOn } from "./study.js";
|
|
11
|
-
import { balanceText, colourName, riskLine } from "./view.js";
|
|
12
|
-
// The analyst's instructions, read at the first analysis.
|
|
13
|
-
let analystText;
|
|
13
|
+
import { balanceText, colourName, riskLine, tacticsText } from "./view.js";
|
|
14
14
|
/** A game over move by move, for the analyst (D-68): the current one, one of the archive or kept to study, or one the player brings as a PGN, kept to study. */
|
|
15
15
|
export const analyseGame = defineCommand({
|
|
16
16
|
name: "analyse_game",
|
|
17
17
|
description: "Gives a game that is over move by move, to comment it with the player: each move with what it took and the time it took, the material "
|
|
18
|
-
+ "when it changes, the pieces at risk
|
|
18
|
+
+ "when it changes, the pieces at risk, the only legal move when there is one alone, the mates in one and what the next moves would do after it (forks, double attacks, discovered checks, "
|
|
19
|
+
+ "promotions, stalemates and stalemate traps), where the game left the opening book, and your comments and plans. Facts "
|
|
19
20
|
+ "only: the judgment is yours. Without game or pgn, the game just over, or else the last one played; game: a name as past_games or study_games "
|
|
20
21
|
+ "writes it; pgn: a game the player brings from elsewhere, kept to study in the profile. Never while a game goes on. The first time in a chat "
|
|
21
22
|
+ "it also says how to analyse. annotate_game then keeps your badges and notes for the board page. Example: analyse_game {}.",
|
|
@@ -31,19 +32,20 @@ export const analyseGame = defineCommand({
|
|
|
31
32
|
// A game's name given as pgn, as an AI often gives it, is that game (Priya).
|
|
32
33
|
const named = pgn !== undefined && isGameName(pgn) ? pgn.trim() : game;
|
|
33
34
|
const studied = pgn === undefined || named !== game ? gameToStudy(context, named) : importedGame(context, pgn);
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
throw new Error("Stop now: this game has its review already. End with one line for the AI that started you: go over that review with the player.");
|
|
35
|
+
if (context.agent === analystAgent) {
|
|
36
|
+
oneAnalyst(context, studied);
|
|
37
37
|
}
|
|
38
38
|
const analysis = analysisOf(studied, activeProfile(context.home()).name);
|
|
39
39
|
if (studied.name !== undefined) {
|
|
40
40
|
markBook(context, { record: studied.record, name: studied.name }, analysis.moves);
|
|
41
41
|
startAnalysis(gameFolder(context.home()), studied.name, context.now());
|
|
42
42
|
}
|
|
43
|
-
// The analyst's agent has its instructions in its own file, and the player's language in its prompt or, when Chesspal knows it, here (D-89)
|
|
43
|
+
// The analyst's agent has its instructions in its own file, and the player's language in its prompt or, when Chesspal knows it, here (D-89);
|
|
44
|
+
// who the player is, which the chat reads in status, here too, so that it writes for their level.
|
|
44
45
|
if (context.agent === analystAgent) {
|
|
45
46
|
const speech = speechOf(gameFolder(context.home()), context.language);
|
|
46
|
-
|
|
47
|
+
const profile = playerLine(context);
|
|
48
|
+
return { ...analysis, ...(speech === undefined ? {} : { speech: speechLine(speech) }), ...(profile === undefined ? {} : { profile }) };
|
|
47
49
|
}
|
|
48
50
|
const told = analystInstructions(context);
|
|
49
51
|
return told === undefined ? analysis : { instructions: told, ...analysis };
|
|
@@ -51,10 +53,36 @@ export const analyseGame = defineCommand({
|
|
|
51
53
|
format: (analysis) => [
|
|
52
54
|
...(analysis.instructions === undefined ? [] : [analysis.instructions, ""]),
|
|
53
55
|
...(analysis.speech === undefined ? [] : [analysis.speech]),
|
|
56
|
+
...(analysis.profile === undefined ? [] : [analysis.profile]),
|
|
54
57
|
...analysisHead(analysis),
|
|
55
58
|
...analysis.moves.map(moveLine),
|
|
56
59
|
].join("\n"),
|
|
57
60
|
});
|
|
61
|
+
/**
|
|
62
|
+
* One analyst a game: one started on a game reviewed already, or that another analyst has taken, stops, and the chat goes over that review (Stefano); the
|
|
63
|
+
* analyst that took it may call again, its review half written. The game is taken at once, before the long reading of it, and read again: of two analysts
|
|
64
|
+
* started together in two processes, as Codex runs them, one goes on (Federico).
|
|
65
|
+
*/
|
|
66
|
+
function oneAnalyst(context, studied) {
|
|
67
|
+
const { name } = studied;
|
|
68
|
+
const holder = name === undefined ? undefined : analystOf(context, name);
|
|
69
|
+
if (holder !== undefined && holder === context.agentRun) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (isReviewed(studied.record)) {
|
|
73
|
+
throw new Error("Stop now: this game has its review already. End with one line for the AI that started you: go over that review with the player.");
|
|
74
|
+
}
|
|
75
|
+
if (holder !== undefined) {
|
|
76
|
+
throw new Error(anotherAnalyst);
|
|
77
|
+
}
|
|
78
|
+
if (name !== undefined && context.agentRun !== undefined) {
|
|
79
|
+
const folder = gameFolder(context.home());
|
|
80
|
+
startAnalysis(folder, name, context.now(), { analyst: { game: name, run: context.agentRun, since: context.now().toISOString() } });
|
|
81
|
+
if (gameInAnalysis(folder)?.analyst?.run !== context.agentRun) {
|
|
82
|
+
throw new Error(anotherAnalyst);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
58
86
|
/** A game the player brings as a PGN, checked move by move and kept to study with its clock and Chesspal's own review: no other comment, no engine's evaluation (D-6). */
|
|
59
87
|
function importedGame(context, pgn) {
|
|
60
88
|
noGameGoingOn(context);
|
|
@@ -145,7 +173,7 @@ function movesOf(record, ai) {
|
|
|
145
173
|
let inBook = record.tags.FEN === undefined && bookReaches(game.position.fen);
|
|
146
174
|
let before = game.facts;
|
|
147
175
|
let balance = materialOf(game.position.fen).balance;
|
|
148
|
-
const start = { atRisk: riskLine(before), mates: matesOf(before) };
|
|
176
|
+
const start = { atRisk: riskLine(before), oneLegal: oneLegalMove(before), mates: matesOf(before), tactics: tacticsText(before, game.possibilities, true) };
|
|
149
177
|
// What was at risk on the line before: a move tells it only when it changed.
|
|
150
178
|
let risk = start.atRisk;
|
|
151
179
|
const moves = record.moves.map(({ san, comment }, ply) => {
|
|
@@ -169,7 +197,9 @@ function movesOf(record, ai) {
|
|
|
169
197
|
left: clock,
|
|
170
198
|
material: now === balance ? undefined : balanceText(now),
|
|
171
199
|
atRisk: over || risks === risk ? undefined : (risks ?? "none"),
|
|
200
|
+
oneLegal: over ? undefined : oneLegalMove(facts),
|
|
172
201
|
mates: over ? undefined : matesOf(facts),
|
|
202
|
+
tactics: over ? undefined : tacticsText(facts, game.possibilities, true),
|
|
173
203
|
outOfBook: outOfBook ? true : undefined,
|
|
174
204
|
drawOffer: comment?.includes("(=)") === true ? true : undefined,
|
|
175
205
|
said: side === ai ? saidIn(comment) : undefined,
|
|
@@ -206,7 +236,7 @@ function analysisHead(analysis) {
|
|
|
206
236
|
const review = analysis.review === undefined ? [] : [`Your review: ${analysis.review}`];
|
|
207
237
|
return [head, ...opening, ...review, `${name}.`, ...start];
|
|
208
238
|
}
|
|
209
|
-
/** 12...Nxe5: took P; spent 0:12, 4:32 left; material White +1; at risk: undefended Black Nf6; mate in one: Qh7#; out of book; you said "…"; your plan: …; ahead -1.5 */
|
|
239
|
+
/** 12...Nxe5: took P; spent 0:12, 4:32 left; material White +1; at risk: undefended Black Nf6; mate in one: Qh7#; tactics: fork Nc7+ (Ke8 Ra8); out of book; you said "…"; your plan: …; ahead -1.5 */
|
|
210
240
|
function moveLine(move) {
|
|
211
241
|
const left = move.left === undefined ? undefined : `${clockText(move.left)} left`;
|
|
212
242
|
const parts = [
|
|
@@ -214,7 +244,9 @@ function moveLine(move) {
|
|
|
214
244
|
move.spent === undefined ? left : `spent ${clockText(move.spent)}, ${left}`,
|
|
215
245
|
move.material === undefined ? undefined : `material ${move.material}`,
|
|
216
246
|
move.atRisk === undefined ? undefined : `at risk: ${move.atRisk}`,
|
|
247
|
+
move.oneLegal === undefined ? undefined : `one legal move: ${move.oneLegal}`,
|
|
217
248
|
move.mates === undefined ? undefined : `mate in one: ${move.mates.join(" ")}`,
|
|
249
|
+
move.tactics === undefined ? undefined : `tactics: ${move.tactics}`,
|
|
218
250
|
move.outOfBook ? "out of book" : undefined,
|
|
219
251
|
move.drawOffer ? "offers a draw" : undefined,
|
|
220
252
|
move.said === undefined ? undefined : `you said "${move.said}"`,
|
|
@@ -235,14 +267,18 @@ function analystInstructions(context) {
|
|
|
235
267
|
return undefined;
|
|
236
268
|
}
|
|
237
269
|
context.told.add(key);
|
|
238
|
-
|
|
239
|
-
return analystText;
|
|
270
|
+
return `${instructionsFile("analyst")}\n${instructionsFile("analysis")}`;
|
|
240
271
|
}
|
|
241
272
|
/** Who is ahead after a move, as the AI estimated it with annotate_game or the coach during the game (D-90). */
|
|
242
273
|
function estimateOf(comment) {
|
|
243
274
|
const lead = leadOf(comment);
|
|
244
275
|
return lead === undefined ? undefined : leadText(lead);
|
|
245
276
|
}
|
|
277
|
+
/** The only legal move of the side to move, when it has one alone (D-103). */
|
|
278
|
+
function oneLegalMove(facts) {
|
|
279
|
+
const moves = facts.pieces.flatMap((piece) => piece.moves ?? []);
|
|
280
|
+
return moves.length === 1 ? moves[0] : undefined;
|
|
281
|
+
}
|
|
246
282
|
/** The mates in one the side to move has; none when it has none. */
|
|
247
283
|
function matesOf(facts) {
|
|
248
284
|
const mates = facts.checks.filter((check) => check.endsWith("#"));
|
|
@@ -10,6 +10,10 @@ interface Annotated {
|
|
|
10
10
|
/** How many moves took who is ahead after them, and those still without it, as analyse_game numbers them (D-90). */
|
|
11
11
|
estimated: number;
|
|
12
12
|
unestimated: string[];
|
|
13
|
+
/** The moves whose note the chat corrected with the player, told the analyst at each call: its notes leave them as they are (D-101). */
|
|
14
|
+
left: string[];
|
|
15
|
+
/** The chat corrected a note of a review that already has its word on the game. */
|
|
16
|
+
summaryStands: boolean;
|
|
13
17
|
}
|
|
14
18
|
/** The AI's badges and notes on a game over, and its word on the whole game, written into the game's PGN (D-69). */
|
|
15
19
|
export declare const annotateGame: import("./registry.ts").Command<z.ZodObject<{
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// The AI's review of a game over (D-69): a badge on each move that matters, as chess.com marks them, with a note when it needs one, and its word on the
|
|
2
2
|
// whole game, kept in the game's PGN for the board page. The judgment is the AI's alone: Chesspal gives no criteria (D-6, D-43).
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
import { annotatorOf, badges, Game, leadOf, reviewed, reviewOf, withAnnotator, withGameReview, withLead } from "../core/index.js";
|
|
4
|
+
import { annotatorOf, badges, Game, gameReviewOf, leadOf, reviewed, reviewOf, withAnnotator, withGameReview, withLead } from "../core/index.js";
|
|
5
5
|
import { gameInAnalysis, rewriteGame, startAnalysis } from "../games/index.js";
|
|
6
6
|
import { analystAgent } from "../host-files/index.js";
|
|
7
7
|
import { gameFolder } from "../profiles/index.js";
|
|
8
|
+
import { analystOf, analystRuns, anotherAnalyst } from "./agents.js";
|
|
8
9
|
import { arrowsInput, drawingsOf, noteInput, squaresInput } from "./drawings.js";
|
|
9
|
-
import { leadFrom
|
|
10
|
+
import { leadFrom } from "./lead-input.js";
|
|
10
11
|
import { defineCommand } from "./registry.js";
|
|
11
12
|
import { moveIndex, moveNumbers, namedGame, noGameGoingOn } from "./study.js";
|
|
12
13
|
const summaryLimit = 1500;
|
|
@@ -30,9 +31,10 @@ export const annotateGame = defineCommand({
|
|
|
30
31
|
arrows: arrowsInput,
|
|
31
32
|
squares: squaresInput,
|
|
32
33
|
})).optional().describe("the moves that matter, each with its badge, its note, or both"),
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
+ "
|
|
34
|
+
// Checked in run, which leaves out a figure after the move that ended the game on the board.
|
|
35
|
+
ahead: z.record(z.string(), z.union([z.number(), z.string()])).optional().describe("who is ahead after each move, by the move as analyse_game numbers it: pawns from White's side as chess.com shows it, 1.5 for White, -3 for Black, 0 for "
|
|
36
|
+
+ 'even, or a mate you see in moves of the side that mates, "#3" for White\'s, "#-3" for Black\'s; every move but the last when it mates or draws '
|
|
37
|
+
+ "on the board, which says it, in one call or a few; a move given again takes its new figure"),
|
|
36
38
|
summary: z.string().max(summaryLimit).optional().describe("your word on the whole game: a short paragraph, with a few points as lines starting with - if it helps; the moves have their own notes"),
|
|
37
39
|
closing: z.string().max(closingLimit).optional().describe("your last word on how the game ended, its win or loss, in a sentence or two: the page shows it at the end"),
|
|
38
40
|
}),
|
|
@@ -48,6 +50,21 @@ export const annotateGame = defineCommand({
|
|
|
48
50
|
// would be lost (Federico).
|
|
49
51
|
const caller = await context.caller?.();
|
|
50
52
|
const studied = game === undefined ? gameAnalysed(context) : namedGame(context, game);
|
|
53
|
+
// An analyst whose game another has taken since, the review started again, writes no more (Priya).
|
|
54
|
+
const holder = context.agent === analystAgent ? analystOf(context, studied.name) : undefined;
|
|
55
|
+
if (holder !== undefined && holder !== context.agentRun) {
|
|
56
|
+
throw new Error(`${anotherAnalyst} Nothing changed.`);
|
|
57
|
+
}
|
|
58
|
+
const folder = gameFolder(context.home());
|
|
59
|
+
const numbers = moveNumbers(studied.record);
|
|
60
|
+
// Where the analyst reviews the games, the chat's notes are corrections it made with the player: the analyst, at work then or later, leaves those notes
|
|
61
|
+
// and is told of them at each call, and the chat is not asked for the analyst's work (D-101, Priya). Only a call the host ties to the chat is its own.
|
|
62
|
+
const correction = context.chat === true && analystRuns(context) && notes.length > 0 && summary === undefined && closing === undefined;
|
|
63
|
+
const analysis = gameInAnalysis(folder);
|
|
64
|
+
const corrected = context.chat !== true && analysis?.game === studied.name ? analysis.corrected ?? [] : [];
|
|
65
|
+
let skipped = 0;
|
|
66
|
+
// The moves noted, as analyse_game numbers them: the chat's corrections.
|
|
67
|
+
const noted = [];
|
|
51
68
|
// Every move is checked before the game is written: all or nothing.
|
|
52
69
|
const moves = [...studied.record.moves];
|
|
53
70
|
for (const { move, badge, note, arrows = [], squares = [] } of notes) {
|
|
@@ -55,36 +72,45 @@ export const annotateGame = defineCommand({
|
|
|
55
72
|
throw new Error(`Give ${move} a badge, a note, or both: arrows and squares go with them. Nothing changed.`);
|
|
56
73
|
}
|
|
57
74
|
const index = indexIn(studied.record, move, "notes");
|
|
75
|
+
noted.push(numbers[index]);
|
|
76
|
+
if (corrected.includes(numbers[index])) {
|
|
77
|
+
skipped += 1;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
58
80
|
// A note alone on a move of the book keeps its book badge: a fact Chesspal put there, not the AI's judgment.
|
|
59
81
|
const kept = badge ?? (reviewOf(moves[index].comment)?.badge === "book" ? "book" : undefined);
|
|
60
82
|
moves[index] = reviewed(moves[index], { ...(kept === undefined ? {} : { badge: kept }), ...(note === undefined ? {} : { note }), ...drawingsOf(arrows, squares) });
|
|
61
83
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
84
|
+
// After the move that ended the game on the board, which says it, a figure is left out rather than refused, "#0" too (Priya).
|
|
85
|
+
const over = new Game(studied.record).outcome.over;
|
|
86
|
+
const leads = estimates.map(([move, lead]) => ({ index: indexIn(studied.record, move, "ahead"), move, lead })).filter(({ index }) => !over || index < moves.length - 1);
|
|
87
|
+
for (const { index, move, lead } of leads) {
|
|
88
|
+
moves[index] = withLead(moves[index], leadAfter(move, lead));
|
|
65
89
|
}
|
|
66
90
|
// Who reviewed it, as far as the host says it for certain (D-81): on the command line, or by a host that names no model over one that did, as it was.
|
|
67
91
|
const kept = caller === undefined || (caller.hostOnly === true && annotatorOf(studied.record) !== undefined);
|
|
68
92
|
const marked = { ...(kept ? studied.record : withAnnotator(studied.record, caller)), moves };
|
|
69
|
-
rewriteGame(
|
|
70
|
-
startAnalysis(
|
|
71
|
-
const numbers = moveNumbers(marked);
|
|
93
|
+
rewriteGame(folder, studied.name, summary === undefined && closing === undefined ? marked : withGameReview(marked, { ...(summary === undefined ? {} : { summary }), ...(closing === undefined ? {} : { closing }) }));
|
|
94
|
+
startAnalysis(folder, studied.name, context.now(), { corrected: correction ? noted : [] });
|
|
72
95
|
const bookUnnoted = moves.flatMap((move, index) => {
|
|
73
96
|
const review = reviewOf(move.comment);
|
|
74
97
|
return review?.badge === "book" && review.note === undefined ? [numbers[index]] : [];
|
|
75
98
|
});
|
|
76
99
|
return {
|
|
77
100
|
game: studied.name,
|
|
78
|
-
marked: notes.length,
|
|
101
|
+
marked: notes.length - skipped,
|
|
79
102
|
summary: summary !== undefined,
|
|
80
103
|
closing: closing !== undefined,
|
|
81
|
-
bookUnnoted,
|
|
82
|
-
estimated:
|
|
104
|
+
bookUnnoted: correction ? [] : bookUnnoted,
|
|
105
|
+
estimated: leads.length,
|
|
83
106
|
// Asked every time, of an older review too: an analyst whose instructions predate the estimates learns of them here.
|
|
84
|
-
unestimated: unestimatedMoves(marked, numbers),
|
|
107
|
+
unestimated: correction ? [] : unestimatedMoves(marked, numbers, over),
|
|
108
|
+
left: [...corrected],
|
|
109
|
+
// A correction after the review's word on the game: that word may now say otherwise (Priya).
|
|
110
|
+
summaryStands: correction && gameReviewOf(studied.record) !== undefined,
|
|
85
111
|
};
|
|
86
112
|
},
|
|
87
|
-
format: ({ game, marked, summary, closing, bookUnnoted, estimated, unestimated }) => {
|
|
113
|
+
format: ({ game, marked, summary, closing, bookUnnoted, estimated, unestimated, left, summaryStands }) => {
|
|
88
114
|
const kept = [
|
|
89
115
|
marked === 0 ? undefined : `${marked} ${marked === 1 ? "move" : "moves"} marked`,
|
|
90
116
|
estimated === 0 ? undefined : `who is ahead after ${estimated} ${estimated === 1 ? "move" : "moves"}`,
|
|
@@ -100,13 +126,27 @@ export const annotateGame = defineCommand({
|
|
|
100
126
|
const shown = unestimated.slice(0, unestimatedShown).join(" ");
|
|
101
127
|
const more = unestimated.length > unestimatedShown ? ` and ${unestimated.length - unestimatedShown} more` : "";
|
|
102
128
|
const ahead = unestimated.length === 0 ? [] : [`Who is ahead is still missing after ${shown}${more}: give it with ahead, for the page's bar and chart.`];
|
|
103
|
-
|
|
129
|
+
const one = left.length === 1;
|
|
130
|
+
const corrected = left.length === 0
|
|
131
|
+
? []
|
|
132
|
+
: [`${left.join(" ")} ${one ? "keeps the note" : "keep the notes"} the AI wrote with the player: leave ${one ? "it" : "them"}, and don't contradict ${one ? "it" : "them"} in your summary${summary ? "; if this one does, give it again" : ""}.`];
|
|
133
|
+
const head = kept.length === 0 ? `Nothing new kept in ${game}.` : `Kept in ${game}: ${[kept.slice(0, -1).join(", "), kept.at(-1)].filter((part) => part !== "").join(" and ")}. The board page shows them.`;
|
|
134
|
+
const stands = summaryStands ? ["The review's word on the game stays as written: if it says otherwise, give summary again."] : [];
|
|
135
|
+
return [head, ...stands, ...corrected, ...book, ...ahead].join("\n");
|
|
104
136
|
},
|
|
105
137
|
});
|
|
106
138
|
/** The moves without who is ahead after them, as analyse_game numbers them: all but the last when the board ended the game there, which says it. */
|
|
107
|
-
function unestimatedMoves(record, numbers) {
|
|
108
|
-
|
|
109
|
-
|
|
139
|
+
function unestimatedMoves(record, numbers, over) {
|
|
140
|
+
return record.moves.flatMap((move, index) => leadOf(move.comment) !== undefined || (over && index === record.moves.length - 1) ? [] : [numbers[index]]);
|
|
141
|
+
}
|
|
142
|
+
/** Who is ahead after a move given in ahead, refused with the move it was given for. */
|
|
143
|
+
function leadAfter(move, value) {
|
|
144
|
+
try {
|
|
145
|
+
return leadFrom(value);
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
throw new Error(`In ahead, after ${move}, ${error.message}`);
|
|
149
|
+
}
|
|
110
150
|
}
|
|
111
151
|
/** A move given in notes or in ahead, as analyse_game numbers it, refused with where it was given (Priya). */
|
|
112
152
|
function indexIn(record, move, field) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Side } from "../core/index.ts";
|
|
2
2
|
import { type GameSummary, type LastMove, type MovePlayed } from "./game-summary.ts";
|
|
3
|
+
import type { Written } from "./say.ts";
|
|
3
4
|
/** Where the game stands: the AI's position to decide on, with its last move unless the lines before named it; the player's turn; or how it ended. */
|
|
4
5
|
export declare function positionLines(game: GameSummary, withLastMove?: boolean): string[];
|
|
5
6
|
/** The head of a view: where the game stands, the last move unless the lines before named it, the opening, the material as the player sees it, and the FEN. */
|
|
@@ -11,17 +12,22 @@ export declare function lastMoveText({ move, from, to }: LastMove): string;
|
|
|
11
12
|
* (Priya, test of D-86): 4.Ng5 is 4.♞g5.
|
|
12
13
|
*/
|
|
13
14
|
export declare function shownMove(move: string): string;
|
|
15
|
+
/** Numbered moves as the player is shown them, with the pieces' figurines (D-86): 4...g6 5.Nxe5 Bxd1 is 4...g6 5.♞xe5 ♝xd1. */
|
|
16
|
+
export declare function shownMoves(moves: string): string;
|
|
14
17
|
/** The lines after a move, which the line before named: its note, where the game stands, a warning. closingLines end them. */
|
|
15
18
|
export declare function moveLines({ note, game, warning }: MovePlayed): string[];
|
|
16
19
|
/** The last lines of a tool's answer: the player's words from the board page, then the next step, which stays the last line. */
|
|
17
|
-
export declare function closingLines(game: GameSummary | undefined, words?: readonly
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
+
export declare function closingLines(game: GameSummary | undefined, words?: readonly Written[], pageClosed?: boolean): string[];
|
|
21
|
+
/**
|
|
22
|
+
* The player's words from the board page, one line each, in analysis with the move the page showed (D-101): lines they broke are joined, so no line of
|
|
23
|
+
* theirs reads as one of Chesspal's.
|
|
24
|
+
*/
|
|
25
|
+
export declare function wordLines(words: readonly Written[]): string[];
|
|
20
26
|
/**
|
|
21
27
|
* What the AI does next: the one line every host reads, even where the server's instructions are not shown. Words of the player's ask for an answer. With the
|
|
22
28
|
* board page open in no browser, a new game starts there, never in the chat.
|
|
23
29
|
*/
|
|
24
|
-
export declare function nextStep(game: GameSummary | undefined, words?: readonly
|
|
30
|
+
export declare function nextStep(game: GameSummary | undefined, words?: readonly Written[], pageClosed?: boolean): string;
|
|
25
31
|
/** How a new game starts: the player chooses it in the board page's window, which new_game without choices opens (D-85). */
|
|
26
32
|
export declare const newGameStep = "call new_game without choices: it opens the board page's New game window, where the player chooses the colour, the clock and the level.";
|
|
27
33
|
/** Who plays what, from the AI's side. */
|
|
@@ -4,7 +4,7 @@ import { talkLimits } from "../games/index.js";
|
|
|
4
4
|
import { onQuickModel } from "../host-files/index.js";
|
|
5
5
|
import { analystLine, reviewedLine } from "./agents.js";
|
|
6
6
|
import { clockLine } from "./clock.js";
|
|
7
|
-
import { endText } from "./game-summary.js";
|
|
7
|
+
import { endText, unplayed } from "./game-summary.js";
|
|
8
8
|
import { boardView, colourName, compactView, materialText } from "./view.js";
|
|
9
9
|
/** Where the game stands: the AI's position to decide on, with its last move unless the lines before named it; the player's turn; or how it ended. */
|
|
10
10
|
export function positionLines(game, withLastMove = true) {
|
|
@@ -13,7 +13,7 @@ export function positionLines(game, withLastMove = true) {
|
|
|
13
13
|
}
|
|
14
14
|
return [
|
|
15
15
|
...headLines(game, withLastMove),
|
|
16
|
-
...(game.facts === undefined ? [] : (
|
|
16
|
+
...(game.facts === undefined ? [] : viewLines(game.facts, game)),
|
|
17
17
|
...(game.book === undefined ? [] : [`Book moves, most named lines first: ${game.book.join(", ")}`]),
|
|
18
18
|
...(game.plan === undefined ? [] : [`Plan: ${game.plan}`]),
|
|
19
19
|
...(game.thought === undefined ? [] : [thoughtLine(game.thought, game.level)]),
|
|
@@ -21,6 +21,14 @@ export function positionLines(game, withLastMove = true) {
|
|
|
21
21
|
...(game.helps === "coached" ? [`${helpsLine} ${coachLine}`] : game.helps === "on" ? [helpsLine] : game.helps === "off now" ? [helpsOffLine] : []),
|
|
22
22
|
];
|
|
23
23
|
}
|
|
24
|
+
/** The view of the AI's position as its level sees it (D-60, D-100): the tactical facts from the club level, the other side's chances from the strong level. */
|
|
25
|
+
function viewLines(facts, game) {
|
|
26
|
+
if (!sees(game.level, "tactics")) {
|
|
27
|
+
return boardView(facts, game.turn);
|
|
28
|
+
}
|
|
29
|
+
const possible = game.possibilities === undefined ? undefined : { possibilities: game.possibilities, threats: game.threats, strong: sees(game.level, "strategy") };
|
|
30
|
+
return compactView(facts, game.turn, possible);
|
|
31
|
+
}
|
|
24
32
|
/** The helps turned on: the AI may teach as the analyst does (D-62, D-78), never playing for the player. */
|
|
25
33
|
const helpsLine = "Helps on: the player wants to learn. Tell your intentions, never their best move; draw arrows and squares to show what you say, and write up to "
|
|
26
34
|
+ `${talkLimits.aiHelps} characters.`;
|
|
@@ -55,6 +63,10 @@ export function shownMove(move) {
|
|
|
55
63
|
const [, number = "", san = move] = /^(\d+\.+)(.+)$/.exec(move) ?? [];
|
|
56
64
|
return `${number}${figurinesOf(san)}`;
|
|
57
65
|
}
|
|
66
|
+
/** Numbered moves as the player is shown them, with the pieces' figurines (D-86): 4...g6 5.Nxe5 Bxd1 is 4...g6 5.♞xe5 ♝xd1. */
|
|
67
|
+
export function shownMoves(moves) {
|
|
68
|
+
return moves.split(" ").map(shownMove).join(" ");
|
|
69
|
+
}
|
|
58
70
|
/** The lines after a move, which the line before named: its note, where the game stands, a warning. closingLines end them. */
|
|
59
71
|
export function moveLines({ note, game, warning }) {
|
|
60
72
|
return [...(note === undefined ? [] : [`Note: ${note}`]), ...positionLines(game, false), ...(warning === undefined ? [] : [warning])];
|
|
@@ -63,9 +75,12 @@ export function moveLines({ note, game, warning }) {
|
|
|
63
75
|
export function closingLines(game, words = [], pageClosed = false) {
|
|
64
76
|
return [...wordLines(words), nextStep(game, words, pageClosed)];
|
|
65
77
|
}
|
|
66
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* The player's words from the board page, one line each, in analysis with the move the page showed (D-101): lines they broke are joined, so no line of
|
|
80
|
+
* theirs reads as one of Chesspal's.
|
|
81
|
+
*/
|
|
67
82
|
export function wordLines(words) {
|
|
68
|
-
return words.map((text) => `Player wrote on the board page: «${text.replace(/\s+/g, " ")}»`);
|
|
83
|
+
return words.map(({ text, place }) => `Player wrote on the board page${place === undefined ? "" : `, at ${place}`}: «${text.replace(/\s+/g, " ")}»`);
|
|
69
84
|
}
|
|
70
85
|
/**
|
|
71
86
|
* What the AI does next: the one line every host reads, even where the server's instructions are not shown. Words of the player's ask for an answer. With the
|
|
@@ -73,9 +88,20 @@ export function wordLines(words) {
|
|
|
73
88
|
*/
|
|
74
89
|
export function nextStep(game, words = [], pageClosed = false) {
|
|
75
90
|
const step = pageClosed ? closedPageGameStep : newGameStep;
|
|
91
|
+
// Words written on a game in analysis: the chat answers them there, and corrects the review where they show it wrong (D-101).
|
|
92
|
+
if (words.some((word) => word.place !== undefined) && (game === undefined || game.end !== undefined)) {
|
|
93
|
+
return "Next: answer the player with ai_say, about the move they were looking at. When they show you something the review missed, first correct that move's "
|
|
94
|
+
+ "note with annotate_game, that move only.";
|
|
95
|
+
}
|
|
76
96
|
if (game === undefined) {
|
|
77
97
|
return words.length === 0 ? `Next: when the player wants a game, ${step}` : `Next: answer the player with ai_say. When they want a game, ${step}`;
|
|
78
98
|
}
|
|
99
|
+
if (game.end !== undefined && unplayed(game)) {
|
|
100
|
+
return words.length === 0
|
|
101
|
+
? "Next: tell the player how the game ended, with ai_say and how you feel as its mood if they play on the board page; then offer them, in words, another "
|
|
102
|
+
+ `game. For another, ${step}`
|
|
103
|
+
: `Next: answer the player with ai_say. When they want another game, ${step}`;
|
|
104
|
+
}
|
|
79
105
|
if (game.end !== undefined) {
|
|
80
106
|
const analysis = game.end.analyst === "offer"
|
|
81
107
|
? ` To go over it, on their yes, ${analystLine(game.end.archive)}`
|
package/dist/commands/coach.js
CHANGED
|
@@ -10,6 +10,7 @@ import { aheadWords, leadFrom, leadInput } from "./lead-input.js";
|
|
|
10
10
|
import { knownMoves, openingLine } from "./opening-book.js";
|
|
11
11
|
import { waitForPlayer, wordsOf } from "./ponder.js";
|
|
12
12
|
import { profileLines } from "./position.js";
|
|
13
|
+
import { ratingsText } from "./profiles.js";
|
|
13
14
|
import { defineCommand } from "./registry.js";
|
|
14
15
|
import { speechLine, speechOf } from "./speech.js";
|
|
15
16
|
import { colourName, compactView, materialText, riskLine } from "./view.js";
|
|
@@ -202,7 +203,7 @@ function judgedLines({ game }, ply, player) {
|
|
|
202
203
|
`Their move to judge: ${lastMoves(played, 1)}. The position they played it in:`,
|
|
203
204
|
`FEN: ${before.position.fen}`,
|
|
204
205
|
`Material: ${materialText(materialOf(before.position.fen))}`,
|
|
205
|
-
...compactView(before.facts, player),
|
|
206
|
+
...compactView(before.facts, player, { possibilities: before.possibilities, threats: before.threats, strong: true }),
|
|
206
207
|
...(book.length === 0 ? [] : [`Book moves there, most named lines first: ${book.join(", ")}`]),
|
|
207
208
|
];
|
|
208
209
|
if (ply === record.moves.length) {
|
|
@@ -229,7 +230,7 @@ function nowLines(current, folder, player) {
|
|
|
229
230
|
return [
|
|
230
231
|
"Now, for who is ahead and your hint:",
|
|
231
232
|
`FEN: ${game.position.fen}`,
|
|
232
|
-
...compactView(game.facts, player),
|
|
233
|
+
...compactView(game.facts, player, { possibilities: game.possibilities, threats: game.threats, strong: true }),
|
|
233
234
|
...profileLines(game.profile),
|
|
234
235
|
...(lead === undefined ? [] : [`Who you saw ahead on their turn before: ${leadText(lead)}, ${aheadWords(lead)}`]),
|
|
235
236
|
...(hint === undefined || hint.position !== plies - 2 ? [] : [`Your hint on their turn before: «${hint.text}»`]),
|
|
@@ -237,8 +238,9 @@ function nowLines(current, folder, player) {
|
|
|
237
238
|
}
|
|
238
239
|
/** How the player says they play, and their rating, when their profile says so. */
|
|
239
240
|
function playerLines(context) {
|
|
240
|
-
const
|
|
241
|
-
const
|
|
241
|
+
const profile = activeProfile(context.home());
|
|
242
|
+
const ratings = ratingsText(profile);
|
|
243
|
+
const parts = [profile.level === undefined ? undefined : `plays at the ${profile.level} level`, ratings === undefined ? undefined : `is rated ${ratings}`];
|
|
242
244
|
const said = parts.filter((part) => part !== undefined);
|
|
243
245
|
return said.length === 0 ? [] : [`The player ${said.join(" and ")}.`];
|
|
244
246
|
}
|
|
@@ -20,6 +20,8 @@ export interface Context {
|
|
|
20
20
|
pageOpen?: () => boolean;
|
|
21
21
|
/** Opens a folder in the system's file manager: the data folder, from the board page; whether it could; none in the tests. */
|
|
22
22
|
openFolder?: (folder: string) => Promise<boolean>;
|
|
23
|
+
/** Asks the sites the player plays on for their ratings (D-105): only the page and the command line, never a tool of the AI's (D-106); none in the tests. */
|
|
24
|
+
fetch?: typeof globalThis.fetch;
|
|
23
25
|
/** Opens the New game window on the board pages open now, where the player chooses the game: whether one was open. */
|
|
24
26
|
askNewGame?: () => boolean;
|
|
25
27
|
/** The AI host, as it names itself to the MCP server: claude-code, which notes how each chat starts (D-57); none on the command line. */
|
|
@@ -29,7 +31,9 @@ export interface Context {
|
|
|
29
31
|
/** Told when the board page has news that no file holds: words the AI was given, which it shows read (D-53), and the AI's model (D-82). */
|
|
30
32
|
heard?: () => void;
|
|
31
33
|
/** Told when the chat calls a tool, before it runs: the board page hears the chat listens, and the AI's clock, held while nobody did, starts. */
|
|
32
|
-
calls?: () => void
|
|
34
|
+
calls?: () => void | Promise<void>;
|
|
35
|
+
/** Told when a tool of the chat's has ended: the table the chat is attached to gets a copy of what it saw, else the chat looks for one (D-104). */
|
|
36
|
+
toolEnded?: () => void;
|
|
33
37
|
/** The game the chat saw when its last tool ended, its folder and its stamp: the doorbell wakes it only for a game changed since. */
|
|
34
38
|
seen?: string | undefined;
|
|
35
39
|
/**
|
|
@@ -43,6 +47,10 @@ export interface Context {
|
|
|
43
47
|
caller?: () => Promise<Caller | undefined>;
|
|
44
48
|
/** The agent that makes this call of a tool it shares with the chat, as the analyst (D-89); none for the chat, or when it cannot be told. */
|
|
45
49
|
agent?: string;
|
|
50
|
+
/** Which run of that agent makes the call, as the host tells it: two analysts started on one game are two runs. */
|
|
51
|
+
agentRun?: string;
|
|
52
|
+
/** The chat makes this call of a tool it shares with an agent, as the host tells it: a caller that cannot be told is not the chat here (Federico). */
|
|
53
|
+
chat?: true;
|
|
46
54
|
}
|
|
47
55
|
/** Who made a tool call: the model, else the host's name, never what the AI says of itself; the effort only when the host gives it. */
|
|
48
56
|
export interface Caller {
|
|
@@ -61,5 +69,7 @@ export interface SeenPosition {
|
|
|
61
69
|
}
|
|
62
70
|
/** The context of a process started in the player's folder: the data folder is found at the first command that needs it, once. */
|
|
63
71
|
export declare function defaultContext(): Context;
|
|
72
|
+
/** Whether the player's language is Italian: the command line speaks Italian to them then, English otherwise (D-32). */
|
|
73
|
+
export declare function speaksItalian(language: string | undefined): boolean;
|
|
64
74
|
/** A data folder with a profile in it, the first made when there is none, named after the system's account (D-71). */
|
|
65
75
|
export declare function withProfile(folder: string, account: string): string;
|
package/dist/commands/context.js
CHANGED
|
@@ -15,8 +15,13 @@ export function defaultContext() {
|
|
|
15
15
|
language: systemLanguage(),
|
|
16
16
|
given: new Set(),
|
|
17
17
|
openFolder: (folder) => openFolder(folder, process.platform),
|
|
18
|
+
fetch: globalThis.fetch,
|
|
18
19
|
};
|
|
19
20
|
}
|
|
21
|
+
/** Whether the player's language is Italian: the command line speaks Italian to them then, English otherwise (D-32). */
|
|
22
|
+
export function speaksItalian(language) {
|
|
23
|
+
return language?.slice(0, 2).toLowerCase() === "it";
|
|
24
|
+
}
|
|
20
25
|
/** A data folder with a profile in it, the first made when there is none, named after the system's account (D-71). */
|
|
21
26
|
export function withProfile(folder, account) {
|
|
22
27
|
ensureProfile(folder, account);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// delete, never the AI.
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { isReviewed, unreviewed } from "../core/index.js";
|
|
5
|
-
import { forgetCoach, gameInAnalysis, isOverOnTable, rewriteGame, startAnalysis } from "../games/index.js";
|
|
5
|
+
import { forgetAnalyst, forgetCoach, gameInAnalysis, isOverOnTable, rewriteGame, startAnalysis } from "../games/index.js";
|
|
6
6
|
import { gameFolder } from "../profiles/index.js";
|
|
7
7
|
import { defineCommand } from "./registry.js";
|
|
8
8
|
import { namedGame } from "./study.js";
|
|
@@ -24,9 +24,14 @@ export const deleteReview = defineCommand({
|
|
|
24
24
|
if (isOverOnTable(folder, name)) {
|
|
25
25
|
forgetCoach(folder);
|
|
26
26
|
}
|
|
27
|
-
// The page showing the game hears of it, and shows it without its review
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
// The page showing the game hears of it, and shows it without its review; the chat's corrections and the analyst that took it go with it, the
|
|
28
|
+
// analyst also while the page shows another game.
|
|
29
|
+
const analysis = gameInAnalysis(folder);
|
|
30
|
+
if (analysis?.game === name) {
|
|
31
|
+
startAnalysis(folder, name, context.now(), { afresh: true });
|
|
32
|
+
}
|
|
33
|
+
else if (analysis?.analyst?.game === name) {
|
|
34
|
+
forgetAnalyst(folder);
|
|
30
35
|
}
|
|
31
36
|
return { game: name, reviewed: isReviewed(record) };
|
|
32
37
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { type AiLevel, type Facts, type GameRecord, type MoodNow, type Side, type Square } from "../core/index.ts";
|
|
1
|
+
import { type AiLevel, type Chances, type Facts, type GameRecord, type MoodNow, type Possibilities, type Side, type Square } from "../core/index.ts";
|
|
2
2
|
import { type CurrentGame, type Speaker, type Thought } from "../games/index.ts";
|
|
3
3
|
import { type ClockSummary } from "./clock.ts";
|
|
4
|
+
import type { Written } from "./say.ts";
|
|
4
5
|
/** The current game, as the commands give it. */
|
|
5
6
|
export interface GameSummary {
|
|
6
7
|
ai: Side;
|
|
@@ -17,6 +18,10 @@ export interface GameSummary {
|
|
|
17
18
|
opening?: string;
|
|
18
19
|
/** When the AI is to move: the facts of the position (D-26); below the club level, only the check and the legal moves of each piece are shown. */
|
|
19
20
|
facts?: Facts;
|
|
21
|
+
/** When the AI is to move, from the club level up: what the moves would do (D-100). */
|
|
22
|
+
possibilities?: Possibilities;
|
|
23
|
+
/** With the possibilities, out of check: what the player's moves would do if they were to move. */
|
|
24
|
+
threats?: Chances;
|
|
20
25
|
/** When the AI is to move at the master level, in the book: the moves it knows from the position (D-15, D-60). */
|
|
21
26
|
book?: readonly string[];
|
|
22
27
|
/** When the AI is to move: the plan it wrote last. */
|
|
@@ -58,7 +63,7 @@ export interface MovePlayed {
|
|
|
58
63
|
/** What went wrong after the move was saved, such as the archive not written. */
|
|
59
64
|
warning?: string;
|
|
60
65
|
/** What the player wrote on the board page that the AI has not been given yet. */
|
|
61
|
-
words?:
|
|
66
|
+
words?: Written[];
|
|
62
67
|
}
|
|
63
68
|
/** The game as the commands tell it; a game over with the name it has in the archive, when it is there. */
|
|
64
69
|
export declare function summaryOf(current: CurrentGame, now: Date, archive?: string): GameSummary;
|
|
@@ -69,6 +74,8 @@ export declare function summaryOf(current: CurrentGame, now: Date, archive?: str
|
|
|
69
74
|
export declare function withThought(summary: GameSummary, folder: string, current: CurrentGame, coached: boolean): GameSummary;
|
|
70
75
|
/** The current game `now`, refused when there is none or when it is over, on time too. */
|
|
71
76
|
export declare function gameGoingOn(folder: string, now: Date): CurrentGame;
|
|
77
|
+
/** Whether a game has no move yet, or was closed before any: nothing to go over. */
|
|
78
|
+
export declare function unplayed(summary: GameSummary): boolean;
|
|
72
79
|
/** How a game ended, in words: its result, and how it came when known. */
|
|
73
80
|
export declare function endText({ result, termination }: {
|
|
74
81
|
result: string;
|
|
@@ -62,6 +62,10 @@ export function gameGoingOn(folder, now) {
|
|
|
62
62
|
}
|
|
63
63
|
return current;
|
|
64
64
|
}
|
|
65
|
+
/** Whether a game has no move yet, or was closed before any: nothing to go over. */
|
|
66
|
+
export function unplayed(summary) {
|
|
67
|
+
return summary.lastMoves === "";
|
|
68
|
+
}
|
|
65
69
|
/** How a game ended, in words: its result, and how it came when known. */
|
|
66
70
|
export function endText({ result, termination }) {
|
|
67
71
|
return termination === undefined ? result : `${result}, ${termination}`;
|
|
@@ -90,6 +94,13 @@ function judgedBefore(current) {
|
|
|
90
94
|
function addAiFacts(summary, { game }) {
|
|
91
95
|
const plan = currentPlan(game.record);
|
|
92
96
|
summary.facts = game.facts;
|
|
97
|
+
if (sees(summary.level, "tactics")) {
|
|
98
|
+
summary.possibilities = game.possibilities;
|
|
99
|
+
const { threats } = game;
|
|
100
|
+
if (threats !== undefined) {
|
|
101
|
+
summary.threats = threats;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
93
104
|
if (sees(summary.level, "theory")) {
|
|
94
105
|
const book = knownMoves(summary.fen);
|
|
95
106
|
if (book.length > 0) {
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AnyCommand } from "./registry.ts";
|
|
|
2
2
|
export { aiMove } from "./ai-move.ts";
|
|
3
3
|
export { analyseGame } from "./analyse-game.ts";
|
|
4
4
|
export { gameAt } from "./clock.ts";
|
|
5
|
-
export { type Caller, type Context, defaultContext, type SeenPosition } from "./context.ts";
|
|
5
|
+
export { type Caller, type Context, defaultContext, type SeenPosition, speaksItalian } from "./context.ts";
|
|
6
6
|
export { deleteGame } from "./delete-game.ts";
|
|
7
7
|
export { deleteReview } from "./delete-review.ts";
|
|
8
8
|
export { answerDraw, DrawRefused, offerDraw } from "./draw-offer.ts";
|
|
@@ -10,9 +10,10 @@ export { endGame } from "./end-game.ts";
|
|
|
10
10
|
export { ChatNotClear, newGame, startNewGame } from "./new-game.ts";
|
|
11
11
|
export { bookOpening, bookPlies } from "./opening-book.ts";
|
|
12
12
|
export { playMove, PositionChanged, positionIn, samePosition } from "./play-move.ts";
|
|
13
|
-
export { chooseDrawnAvatar, GameGoingOn, newProfile,
|
|
13
|
+
export { chooseDrawnAvatar, editProfile, GameGoingOn, newProfile, useProfile } from "./profiles.ts";
|
|
14
|
+
export { ratingsAge, refreshRatings } from "./ratings.ts";
|
|
14
15
|
export { type AnyCommand, baseType, type Command, commandLineFieldProblem, inputProblems, isBoolean, isList, takesNumbers } from "./registry.ts";
|
|
15
16
|
export { say } from "./say.ts";
|
|
16
|
-
export { gameToStudy, namedGame } from "./study.ts";
|
|
17
|
+
export { gameToStudy, namedGame, shownPosition } from "./study.ts";
|
|
17
18
|
/** Every command of Chesspal, in the order the command line lists them and the MCP server offers them. */
|
|
18
19
|
export declare const commands: AnyCommand[];
|