@kenkaiiii/ggcoder 5.24.0 → 5.26.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.
Files changed (122) hide show
  1. package/dist/app-sidecar.js +330 -12
  2. package/dist/app-sidecar.js.map +1 -1
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +3 -0
  5. package/dist/cli.js.map +1 -1
  6. package/dist/config.d.ts +2 -0
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +3 -0
  9. package/dist/config.js.map +1 -1
  10. package/dist/config.test.js +12 -0
  11. package/dist/config.test.js.map +1 -1
  12. package/dist/core/agent-notifications.d.ts +62 -0
  13. package/dist/core/agent-notifications.d.ts.map +1 -0
  14. package/dist/core/agent-notifications.js +92 -0
  15. package/dist/core/agent-notifications.js.map +1 -0
  16. package/dist/core/agent-notifications.test.d.ts +2 -0
  17. package/dist/core/agent-notifications.test.d.ts.map +1 -0
  18. package/dist/core/agent-notifications.test.js +97 -0
  19. package/dist/core/agent-notifications.test.js.map +1 -0
  20. package/dist/core/agent-session-add-dir.test.js +16 -0
  21. package/dist/core/agent-session-add-dir.test.js.map +1 -1
  22. package/dist/core/agent-session-model-switch.test.d.ts +2 -0
  23. package/dist/core/agent-session-model-switch.test.d.ts.map +1 -0
  24. package/dist/core/agent-session-model-switch.test.js +161 -0
  25. package/dist/core/agent-session-model-switch.test.js.map +1 -0
  26. package/dist/core/agent-session-notifications.test.d.ts +2 -0
  27. package/dist/core/agent-session-notifications.test.d.ts.map +1 -0
  28. package/dist/core/agent-session-notifications.test.js +114 -0
  29. package/dist/core/agent-session-notifications.test.js.map +1 -0
  30. package/dist/core/agent-session.d.ts +76 -6
  31. package/dist/core/agent-session.d.ts.map +1 -1
  32. package/dist/core/agent-session.js +246 -25
  33. package/dist/core/agent-session.js.map +1 -1
  34. package/dist/core/event-bus.d.ts +6 -0
  35. package/dist/core/event-bus.d.ts.map +1 -1
  36. package/dist/core/event-bus.js +7 -0
  37. package/dist/core/event-bus.js.map +1 -1
  38. package/dist/core/local-endpoint-store.d.ts +50 -0
  39. package/dist/core/local-endpoint-store.d.ts.map +1 -0
  40. package/dist/core/local-endpoint-store.js +201 -0
  41. package/dist/core/local-endpoint-store.js.map +1 -0
  42. package/dist/core/local-endpoint-store.test.d.ts +2 -0
  43. package/dist/core/local-endpoint-store.test.d.ts.map +1 -0
  44. package/dist/core/local-endpoint-store.test.js +131 -0
  45. package/dist/core/local-endpoint-store.test.js.map +1 -0
  46. package/dist/core/memory/compaction-notes.d.ts +31 -0
  47. package/dist/core/memory/compaction-notes.d.ts.map +1 -0
  48. package/dist/core/memory/compaction-notes.js +162 -0
  49. package/dist/core/memory/compaction-notes.js.map +1 -0
  50. package/dist/core/memory/compaction-notes.test.d.ts +2 -0
  51. package/dist/core/memory/compaction-notes.test.d.ts.map +1 -0
  52. package/dist/core/memory/compaction-notes.test.js +110 -0
  53. package/dist/core/memory/compaction-notes.test.js.map +1 -0
  54. package/dist/core/memory/journal.d.ts +55 -0
  55. package/dist/core/memory/journal.d.ts.map +1 -0
  56. package/dist/core/memory/journal.js +212 -0
  57. package/dist/core/memory/journal.js.map +1 -0
  58. package/dist/core/memory/journal.test.d.ts +2 -0
  59. package/dist/core/memory/journal.test.d.ts.map +1 -0
  60. package/dist/core/memory/journal.test.js +184 -0
  61. package/dist/core/memory/journal.test.js.map +1 -0
  62. package/dist/core/process-manager-notifications.test.d.ts +2 -0
  63. package/dist/core/process-manager-notifications.test.d.ts.map +1 -0
  64. package/dist/core/process-manager-notifications.test.js +113 -0
  65. package/dist/core/process-manager-notifications.test.js.map +1 -0
  66. package/dist/core/process-manager.d.ts +29 -0
  67. package/dist/core/process-manager.d.ts.map +1 -1
  68. package/dist/core/process-manager.js +123 -0
  69. package/dist/core/process-manager.js.map +1 -1
  70. package/dist/core/session-manager.d.ts +3 -1
  71. package/dist/core/session-manager.d.ts.map +1 -1
  72. package/dist/core/session-manager.js +2 -1
  73. package/dist/core/session-manager.js.map +1 -1
  74. package/dist/core/settings-manager.d.ts +1 -0
  75. package/dist/core/settings-manager.d.ts.map +1 -1
  76. package/dist/core/settings-manager.js +7 -0
  77. package/dist/core/settings-manager.js.map +1 -1
  78. package/dist/core/slash-commands.d.ts +8 -0
  79. package/dist/core/slash-commands.d.ts.map +1 -1
  80. package/dist/core/slash-commands.js +17 -1
  81. package/dist/core/slash-commands.js.map +1 -1
  82. package/dist/core/slash-commands.test.js +11 -0
  83. package/dist/core/slash-commands.test.js.map +1 -1
  84. package/dist/core/steering.d.ts +12 -0
  85. package/dist/core/steering.d.ts.map +1 -1
  86. package/dist/core/steering.js +16 -0
  87. package/dist/core/steering.js.map +1 -1
  88. package/dist/core/subagent-manager.d.ts +13 -0
  89. package/dist/core/subagent-manager.d.ts.map +1 -1
  90. package/dist/core/subagent-manager.js +30 -0
  91. package/dist/core/subagent-manager.js.map +1 -1
  92. package/dist/core/subagent-manager.test.js +16 -0
  93. package/dist/core/subagent-manager.test.js.map +1 -1
  94. package/dist/modes/json-mode.d.ts.map +1 -1
  95. package/dist/modes/json-mode.js +5 -0
  96. package/dist/modes/json-mode.js.map +1 -1
  97. package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
  98. package/dist/modes/subagent-worker-mode.js +3 -1
  99. package/dist/modes/subagent-worker-mode.js.map +1 -1
  100. package/dist/tools/index.d.ts +8 -0
  101. package/dist/tools/index.d.ts.map +1 -1
  102. package/dist/tools/index.js +2 -1
  103. package/dist/tools/index.js.map +1 -1
  104. package/dist/tools/subagent-control.d.ts.map +1 -1
  105. package/dist/tools/subagent-control.js +6 -2
  106. package/dist/tools/subagent-control.js.map +1 -1
  107. package/dist/tools/subagent-shared.d.ts +6 -0
  108. package/dist/tools/subagent-shared.d.ts.map +1 -1
  109. package/dist/tools/subagent-shared.js +6 -0
  110. package/dist/tools/subagent-shared.js.map +1 -1
  111. package/dist/tools/task-output.d.ts.map +1 -1
  112. package/dist/tools/task-output.js +3 -1
  113. package/dist/tools/task-output.js.map +1 -1
  114. package/dist/ui/App.d.ts +2 -0
  115. package/dist/ui/App.d.ts.map +1 -1
  116. package/dist/ui/App.js +35 -1
  117. package/dist/ui/App.js.map +1 -1
  118. package/dist/ui/render.d.ts +2 -0
  119. package/dist/ui/render.d.ts.map +1 -1
  120. package/dist/ui/render.js +1 -0
  121. package/dist/ui/render.js.map +1 -1
  122. package/package.json +4 -4
@@ -0,0 +1,110 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildCompactionNotes, MAX_NOTES_PER_COMPACTION } from "./compaction-notes.js";
3
+ import { ENTRY_MAX_CHARS } from "./journal.js";
4
+ function edit(file) {
5
+ return {
6
+ role: "assistant",
7
+ content: [{ type: "tool_call", id: file, name: "edit", args: { file_path: file } }],
8
+ };
9
+ }
10
+ describe("buildCompactionNotes", () => {
11
+ it("captures the request, the changed files and the summary", () => {
12
+ const dropped = [
13
+ { role: "user", content: "add retry logic to the uploader" },
14
+ edit("/repo/src/upload.ts"),
15
+ edit("/repo/src/retry.ts"),
16
+ { role: "assistant", content: "done" },
17
+ ];
18
+ const notes = buildCompactionNotes(dropped, "[Previous conversation summary]\n\nAdded retries.");
19
+ expect(notes.map((n) => n.tag)).toEqual(["request", "files", "summary"]);
20
+ // Past tense throughout — an entry that reads as current state is the bug.
21
+ expect(notes[0].text).toBe("Was asked to: add retry logic to the uploader");
22
+ expect(notes[1].text).toContain("/repo/src/upload.ts");
23
+ expect(notes[1].text).toContain("/repo/src/retry.ts");
24
+ expect(notes[2].text).toBe("Added retries.");
25
+ });
26
+ it("ignores harness-injected user messages when picking the request", () => {
27
+ const dropped = [
28
+ { role: "user", content: "[Previous conversation summary]\n\nolder stuff" },
29
+ { role: "user", content: "[The user added this while you were working — ...]" },
30
+ { role: "user", content: "the actual ask" },
31
+ ];
32
+ expect(buildCompactionNotes(dropped, "")[0].text).toContain("the actual ask");
33
+ });
34
+ it("never writes more notes than the cap, each within one record", () => {
35
+ const dropped = [
36
+ { role: "user", content: "x".repeat(5000) },
37
+ ...Array.from({ length: 40 }, (_, i) => edit(`/repo/file-${i}.ts`)),
38
+ ];
39
+ const notes = buildCompactionNotes(dropped, "y".repeat(5000));
40
+ expect(notes.length).toBeLessThanOrEqual(MAX_NOTES_PER_COMPACTION);
41
+ for (const note of notes)
42
+ expect(note.text.length).toBeLessThanOrEqual(ENTRY_MAX_CHARS);
43
+ });
44
+ it("deduplicates repeatedly edited files", () => {
45
+ const dropped = [edit("/repo/a.ts"), edit("/repo/a.ts"), edit("/repo/b.ts")];
46
+ const files = buildCompactionNotes(dropped, "")[0];
47
+ expect(files.tag).toBe("files");
48
+ expect(files.text).toBe("Edited: /repo/a.ts, /repo/b.ts");
49
+ });
50
+ /** The compactor's real section structure (see compaction/compactor.ts). */
51
+ const FULL_SUMMARY = "[Previous conversation summary]\n\n" +
52
+ "### Primary Request and Intent\n" +
53
+ "Edit `src/slugify.ts` so the function replaces spaces with hyphens.\n\n" +
54
+ "### User Messages\n" +
55
+ '"Read src/slugify.ts, then edit it so the function also replaces spaces with hyphens."\n\n' +
56
+ "### What Was Done\n" +
57
+ "Replaced the naive lowercase call with a regex substitution.\n\n" +
58
+ "### Files Touched\n" +
59
+ "- `src/slugify.ts` — rewrote the transform\n\n" +
60
+ "### Errors and Fixes\n" +
61
+ "The first regex dropped unicode; fixed by using a character class.\n\n" +
62
+ "### Current Work\n" +
63
+ "About to add trimming of leading hyphens.\n\n" +
64
+ "### Next Step\n" +
65
+ "Run the test suite.";
66
+ it("keeps only the durable summary sections and drops the duplicated ones", () => {
67
+ const dropped = [
68
+ { role: "user", content: "edit slugify to replace spaces with hyphens" },
69
+ edit("/repo/src/slugify.ts"),
70
+ ];
71
+ const summaryNote = buildCompactionNotes(dropped, FULL_SUMMARY).find((n) => n.tag === "summary");
72
+ // Kept: pure history a future session cannot cheaply re-derive.
73
+ expect(summaryNote.text).toContain("Replaced the naive lowercase call");
74
+ expect(summaryNote.text).toContain("first regex dropped unicode");
75
+ // Dropped: already covered by the `request` and `files` entries.
76
+ expect(summaryNote.text).not.toContain("Primary Request and Intent");
77
+ expect(summaryNote.text).not.toContain("Read src/slugify.ts, then edit");
78
+ expect(summaryNote.text).not.toContain("rewrote the transform");
79
+ // Dropped: in-flight state that would be stale on the next read.
80
+ expect(summaryNote.text).not.toContain("About to add trimming");
81
+ expect(summaryNote.text).not.toContain("Run the test suite");
82
+ });
83
+ it("renders each entry as one clean markdown-free line", () => {
84
+ const note = buildCompactionNotes([{ role: "assistant", content: "ok" }], FULL_SUMMARY)[0];
85
+ expect(note.text).not.toContain("#");
86
+ expect(note.text).not.toContain("`");
87
+ expect(note.text).not.toContain("\n");
88
+ });
89
+ it("falls back to the whole summary when it has no section headings", () => {
90
+ // The compactor's extractive fallback path emits unstructured prose.
91
+ const note = buildCompactionNotes([{ role: "assistant", content: "ok" }], "[Previous conversation summary]\n\nModified two files and fixed a failing test.")[0];
92
+ expect(note.text).toBe("Modified two files and fixed a failing test.");
93
+ });
94
+ it("never emits an entry whose text is contained in another entry", () => {
95
+ const dropped = [{ role: "user", content: "do the thing" }];
96
+ const notes = buildCompactionNotes(dropped, "### What Was Done\nWas asked to: do the thing");
97
+ const texts = notes.map((n) => n.text);
98
+ // Every journal entry is re-read on every later turn, so a contained
99
+ // duplicate is recurring waste, not a cosmetic issue.
100
+ for (const [i, text] of texts.entries()) {
101
+ const others = texts.filter((_, j) => j !== i);
102
+ expect(others.some((other) => other.includes(text))).toBe(false);
103
+ }
104
+ });
105
+ it("writes nothing when there is nothing worth remembering", () => {
106
+ expect(buildCompactionNotes([], "")).toEqual([]);
107
+ expect(buildCompactionNotes([{ role: "assistant", content: "ok" }], "")).toEqual([]);
108
+ });
109
+ });
110
+ //# sourceMappingURL=compaction-notes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compaction-notes.test.js","sourceRoot":"","sources":["../../../src/core/memory/compaction-notes.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /** Project-relative location. In the repo, so it shows up in review. */
2
+ export declare const JOURNAL_RELATIVE_PATH: string;
3
+ /** Max characters of a single entry body. */
4
+ export declare const ENTRY_MAX_CHARS = 400;
5
+ /** Entries rendered into the prompt tail. Older ones stay in the file only. */
6
+ export declare const PROMPT_ENTRY_LIMIT = 8;
7
+ /** Hard cap on prompt-tail characters. */
8
+ export declare const PROMPT_CHAR_BUDGET = 1200;
9
+ export interface JournalEntry {
10
+ /** ISO date (day precision) the entry was recorded. */
11
+ date: string;
12
+ /** Short grouping label, e.g. "request", "files", "summary". */
13
+ tag: string;
14
+ /** Bounded, past-tense body. */
15
+ text: string;
16
+ }
17
+ /**
18
+ * Collapse whitespace and fit `text` into `maxChars`, cutting at the cleanest
19
+ * boundary available: sentence, then word, then a hard slice.
20
+ *
21
+ * A mid-word cut (`…read src/router.ts in fu…`) is what the live run produced,
22
+ * and it is worse than it looks. The tail of an entry is usually its most
23
+ * specific part — a file path, an identifier — so a fragment there invites a
24
+ * later reader to guess at what was severed, which is exactly the kind of
25
+ * confident guessing bench G showed the model will not go back and check.
26
+ *
27
+ * The result never exceeds `maxChars`, ellipsis included.
28
+ */
29
+ export declare function clampEntryText(text: string, maxChars: number): string;
30
+ export declare class ProjectJournal {
31
+ private readonly now;
32
+ readonly filePath: string;
33
+ constructor(cwd: string, now?: () => Date);
34
+ /** Entries in file order, oldest first. Hand-edits are read back as written. */
35
+ read(): JournalEntry[];
36
+ /**
37
+ * Append entries under a file lock, re-reading inside it. Two daemons sharing
38
+ * a project must not clobber each other, and an append is never a rewrite of
39
+ * anything a human edited above it.
40
+ */
41
+ append(entries: readonly Omit<JournalEntry, "date">[]): Promise<number>;
42
+ }
43
+ /**
44
+ * Recent journal entries for the uncached system-prompt tail, newest last.
45
+ * Returns "" when the journal is empty, so an unused journal costs zero tokens.
46
+ *
47
+ * Older entries are deliberately NOT summarized into the prompt — they stay in
48
+ * the file, and the pointer below tells the model how to read them. A summary
49
+ * would be one more unverifiable assertion in context.
50
+ */
51
+ export declare function buildJournalPromptTail(journal: ProjectJournal, options?: {
52
+ entryLimit?: number;
53
+ charBudget?: number;
54
+ }): string;
55
+ //# sourceMappingURL=journal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../../src/core/memory/journal.ts"],"names":[],"mappings":"AAoCA,wEAAwE;AACxE,eAAO,MAAM,qBAAqB,QAAgC,CAAC;AAEnE,6CAA6C;AAC7C,eAAO,MAAM,eAAe,MAAM,CAAC;AACnC,+EAA+E;AAC/E,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,0CAA0C;AAC1C,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAWvC,MAAM,WAAW,YAAY;IAC3B,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAC;IACZ,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAuBD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiBrE;AAmBD,qBAAa,cAAc;IAKvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAJtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAGxB,GAAG,EAAE,MAAM,EACM,GAAG,GAAE,MAAM,IAAuB;IAKrD,gFAAgF;IAChF,IAAI,IAAI,YAAY,EAAE;IAQtB;;;;OAIG;IACG,MAAM,CAAC,OAAO,EAAE,SAAS,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CAoC9E;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACzD,MAAM,CA6BR"}
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Project journal — durable, human-reviewable notes that survive compaction and
3
+ * session restarts.
4
+ *
5
+ * The design is dictated by a measured result, not by taste. Bench G
6
+ * (`bench/f-memory-staleness.mjs`) found that injected memory does not merely
7
+ * inform a model, it REPLACES the act of checking: with no memory the model
8
+ * verified against the repo 5/5 times; with any note present, 0/5 — and a false
9
+ * note was then asserted as fact 5/5. Showing note ages and an explicit
10
+ * "unverified, verify first" header fixed none of it. This is consistent with
11
+ * published work: prompt-level freshness instructions are not a reliable fix.
12
+ *
13
+ * Three consequences, each of which is a rule here:
14
+ *
15
+ * 1. **Only past-tense history is stored.** An entry about something that
16
+ * already happened ("chose X over Y because Z") cannot become false, so
17
+ * suppressing re-verification of it costs nothing. Claims about current
18
+ * state ("the test runner is jest") decay silently and are then asserted
19
+ * with full confidence. Nothing in this module can write the latter: the
20
+ * only writer is the compactor, recording what a past session did.
21
+ *
22
+ * 2. **The file is markdown, in the project, and git-diffable.** Since the
23
+ * model provably will not catch a bad entry, the defence has to be outside
24
+ * the model: a human reads `.gg/memory.md` in a diff. A binary log in
25
+ * `~/.gg` would be invisible to exactly the reviewer who can catch it.
26
+ *
27
+ * 3. **No bespoke retrieval layer.** Recent entries ride in the prompt; the
28
+ * whole file is readable with the ordinary `read` tool. A custom
29
+ * store/tree/zoom stack would be machinery whose only job is to re-derive
30
+ * what `read` already does.
31
+ */
32
+ import fs from "node:fs/promises";
33
+ import fsSync from "node:fs";
34
+ import path from "node:path";
35
+ import { withFileLock } from "@kenkaiiii/gg-core";
36
+ /** Project-relative location. In the repo, so it shows up in review. */
37
+ export const JOURNAL_RELATIVE_PATH = path.join(".gg", "memory.md");
38
+ /** Max characters of a single entry body. */
39
+ export const ENTRY_MAX_CHARS = 400;
40
+ /** Entries rendered into the prompt tail. Older ones stay in the file only. */
41
+ export const PROMPT_ENTRY_LIMIT = 8;
42
+ /** Hard cap on prompt-tail characters. */
43
+ export const PROMPT_CHAR_BUDGET = 1200;
44
+ const HEADER = [
45
+ "# Project memory",
46
+ "",
47
+ "Past-tense notes written automatically when a session's history is compacted.",
48
+ "Each entry records something that already happened, so it stays true.",
49
+ "Edit or delete entries freely — this file is meant to be reviewed by humans.",
50
+ "",
51
+ ].join("\n");
52
+ /**
53
+ * Fraction of the budget a boundary must retain to be worth cutting back to.
54
+ * Below this we would be discarding more content than the tidier ending is
55
+ * worth, so a plain word cut wins.
56
+ */
57
+ const MIN_BOUNDARY_RATIO = 0.6;
58
+ /** End of a sentence: terminal punctuation, optional closer, then a space. */
59
+ const SENTENCE_END = /[.!?]["')\]]?\s/g;
60
+ /** Index just past the last sentence ending inside `window`, or -1. */
61
+ function lastSentenceEnd(window) {
62
+ let index = -1;
63
+ SENTENCE_END.lastIndex = 0;
64
+ let match;
65
+ while ((match = SENTENCE_END.exec(window)) !== null) {
66
+ index = match.index + match[0].trimEnd().length;
67
+ }
68
+ return index;
69
+ }
70
+ /**
71
+ * Collapse whitespace and fit `text` into `maxChars`, cutting at the cleanest
72
+ * boundary available: sentence, then word, then a hard slice.
73
+ *
74
+ * A mid-word cut (`…read src/router.ts in fu…`) is what the live run produced,
75
+ * and it is worse than it looks. The tail of an entry is usually its most
76
+ * specific part — a file path, an identifier — so a fragment there invites a
77
+ * later reader to guess at what was severed, which is exactly the kind of
78
+ * confident guessing bench G showed the model will not go back and check.
79
+ *
80
+ * The result never exceeds `maxChars`, ellipsis included.
81
+ */
82
+ export function clampEntryText(text, maxChars) {
83
+ const collapsed = text.replace(/\s+/g, " ").trim();
84
+ if (collapsed.length <= maxChars)
85
+ return collapsed;
86
+ // Reserve room for the widest ellipsis form (" \u2026").
87
+ const budget = Math.max(1, maxChars - 2);
88
+ const window = collapsed.slice(0, budget);
89
+ const floor = Math.floor(budget * MIN_BOUNDARY_RATIO);
90
+ const sentence = lastSentenceEnd(window);
91
+ // Terminal punctuation is kept, so separate the marker rather than doubling up.
92
+ if (sentence >= floor)
93
+ return `${collapsed.slice(0, sentence)} \u2026`;
94
+ const word = window.lastIndexOf(" ");
95
+ if (word >= floor)
96
+ return `${collapsed.slice(0, word)}\u2026`;
97
+ return `${window}\u2026`;
98
+ }
99
+ /** `- 2026-07-27 [tag] body` — one entry, one line, trivially diffable. */
100
+ function formatEntry(entry) {
101
+ return `- ${entry.date} [${entry.tag}] ${entry.text}`;
102
+ }
103
+ const ENTRY_PATTERN = /^-\s+(\d{4}-\d{2}-\d{2})\s+\[([^\]]+)\]\s+(.*)$/;
104
+ function parseEntries(markdown) {
105
+ const entries = [];
106
+ for (const line of markdown.split("\n")) {
107
+ const match = ENTRY_PATTERN.exec(line.trim());
108
+ if (!match)
109
+ continue;
110
+ entries.push({ date: match[1], tag: match[2], text: match[3].trim() });
111
+ }
112
+ return entries;
113
+ }
114
+ export class ProjectJournal {
115
+ now;
116
+ filePath;
117
+ constructor(cwd, now = () => new Date()) {
118
+ this.now = now;
119
+ this.filePath = path.join(cwd, JOURNAL_RELATIVE_PATH);
120
+ }
121
+ /** Entries in file order, oldest first. Hand-edits are read back as written. */
122
+ read() {
123
+ try {
124
+ return parseEntries(fsSync.readFileSync(this.filePath, "utf-8"));
125
+ }
126
+ catch {
127
+ return [];
128
+ }
129
+ }
130
+ /**
131
+ * Append entries under a file lock, re-reading inside it. Two daemons sharing
132
+ * a project must not clobber each other, and an append is never a rewrite of
133
+ * anything a human edited above it.
134
+ */
135
+ async append(entries) {
136
+ const dated = entries
137
+ .map((entry) => ({
138
+ date: this.now().toISOString().slice(0, 10),
139
+ tag: entry.tag,
140
+ text: clampEntryText(entry.text, ENTRY_MAX_CHARS),
141
+ }))
142
+ .filter((entry) => entry.text.length > 0);
143
+ if (dated.length === 0)
144
+ return 0;
145
+ await fs.mkdir(path.dirname(this.filePath), { recursive: true });
146
+ return withFileLock(this.filePath, async () => {
147
+ let existing;
148
+ try {
149
+ existing = await fs.readFile(this.filePath, "utf-8");
150
+ }
151
+ catch {
152
+ existing = HEADER;
153
+ }
154
+ // Skip entries whose exact text is already recorded: repeated compactions
155
+ // of an unchanged span would otherwise stack identical lines.
156
+ const known = new Set(parseEntries(existing).map((entry) => entry.text));
157
+ const fresh = dated.filter((entry) => !known.has(entry.text));
158
+ if (fresh.length === 0)
159
+ return 0;
160
+ // Blank line between the prose header and the entry list, so the file is
161
+ // valid, readable markdown rather than a list glued to a paragraph.
162
+ const trimmed = existing.trimEnd();
163
+ const separator = /\n-\s/.test(trimmed) ? "\n" : "\n\n";
164
+ const body = `${trimmed}${separator}${fresh.map(formatEntry).join("\n")}\n`;
165
+ // Atomic replace: a crash mid-write leaves the previous file intact.
166
+ const tempPath = `${this.filePath}.tmp`;
167
+ await fs.writeFile(tempPath, body, "utf-8");
168
+ await fs.rename(tempPath, this.filePath);
169
+ return fresh.length;
170
+ });
171
+ }
172
+ }
173
+ /**
174
+ * Recent journal entries for the uncached system-prompt tail, newest last.
175
+ * Returns "" when the journal is empty, so an unused journal costs zero tokens.
176
+ *
177
+ * Older entries are deliberately NOT summarized into the prompt — they stay in
178
+ * the file, and the pointer below tells the model how to read them. A summary
179
+ * would be one more unverifiable assertion in context.
180
+ */
181
+ export function buildJournalPromptTail(journal, options = {}) {
182
+ const entryLimit = options.entryLimit ?? PROMPT_ENTRY_LIMIT;
183
+ const charBudget = options.charBudget ?? PROMPT_CHAR_BUDGET;
184
+ const all = journal.read();
185
+ if (all.length === 0)
186
+ return "";
187
+ const lines = [];
188
+ let chars = 0;
189
+ for (let i = all.length - 1; i >= 0 && lines.length < entryLimit; i--) {
190
+ const line = formatEntry(all[i]);
191
+ if (chars + line.length > charBudget)
192
+ break;
193
+ chars += line.length;
194
+ lines.push(line);
195
+ }
196
+ if (lines.length === 0)
197
+ return "";
198
+ lines.reverse();
199
+ const older = all.length - lines.length;
200
+ return [
201
+ "## Project memory",
202
+ `What earlier sessions did, oldest first. These are records of past events, not a ` +
203
+ `description of the project's current state — check the code before relying on any of it.`,
204
+ ...lines,
205
+ older > 0
206
+ ? `(${older} older ${older === 1 ? "entry" : "entries"} in ${JOURNAL_RELATIVE_PATH} — read that file if you need them.)`
207
+ : "",
208
+ ]
209
+ .filter(Boolean)
210
+ .join("\n");
211
+ }
212
+ //# sourceMappingURL=journal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journal.js","sourceRoot":"","sources":["../../../src/core/memory/journal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=journal.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journal.test.d.ts","sourceRoot":"","sources":["../../../src/core/memory/journal.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,184 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { ProjectJournal, buildJournalPromptTail, ENTRY_MAX_CHARS, JOURNAL_RELATIVE_PATH, PROMPT_ENTRY_LIMIT, } from "./journal.js";
6
+ const projects = [];
7
+ function project() {
8
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "gg-journal-"));
9
+ projects.push(dir);
10
+ return dir;
11
+ }
12
+ const FIXED_DATE = () => new Date("2026-07-27T10:00:00Z");
13
+ afterEach(() => {
14
+ for (const dir of projects.splice(0))
15
+ fs.rmSync(dir, { recursive: true, force: true });
16
+ });
17
+ describe("ProjectJournal", () => {
18
+ it("writes a reviewable markdown file inside the project", async () => {
19
+ const cwd = project();
20
+ const journal = new ProjectJournal(cwd, FIXED_DATE);
21
+ await journal.append([{ tag: "request", text: "Was asked to: add retry logic" }]);
22
+ const filePath = path.join(cwd, JOURNAL_RELATIVE_PATH);
23
+ expect(fs.existsSync(filePath)).toBe(true);
24
+ const body = fs.readFileSync(filePath, "utf-8");
25
+ // Human-facing: a header explaining the file, and one diffable line per entry.
26
+ expect(body).toContain("# Project memory");
27
+ expect(body).toContain("- 2026-07-27 [request] Was asked to: add retry logic");
28
+ });
29
+ it("round-trips entries it wrote", async () => {
30
+ const journal = new ProjectJournal(project(), FIXED_DATE);
31
+ await journal.append([
32
+ { tag: "request", text: "Was asked to: ship the parser" },
33
+ { tag: "files", text: "Edited: src/parse.ts" },
34
+ ]);
35
+ expect(journal.read()).toEqual([
36
+ { date: "2026-07-27", tag: "request", text: "Was asked to: ship the parser" },
37
+ { date: "2026-07-27", tag: "files", text: "Edited: src/parse.ts" },
38
+ ]);
39
+ });
40
+ it("reads back hand-edited entries and preserves human edits on append", async () => {
41
+ const cwd = project();
42
+ const journal = new ProjectJournal(cwd, FIXED_DATE);
43
+ await journal.append([{ tag: "summary", text: "Original machine entry" }]);
44
+ // A human corrects the file and adds a note of their own — the whole point
45
+ // of using markdown in the repo instead of a binary log in ~/.gg.
46
+ const filePath = path.join(cwd, JOURNAL_RELATIVE_PATH);
47
+ const edited = fs
48
+ .readFileSync(filePath, "utf-8")
49
+ .replace("Original machine entry", "Corrected by a human")
50
+ .concat("- 2026-07-26 [note] Hand-written by the reviewer\n");
51
+ fs.writeFileSync(filePath, edited);
52
+ await journal.append([{ tag: "files", text: "Edited: src/new.ts" }]);
53
+ const texts = journal.read().map((entry) => entry.text);
54
+ expect(texts).toEqual([
55
+ "Corrected by a human",
56
+ "Hand-written by the reviewer",
57
+ "Edited: src/new.ts",
58
+ ]);
59
+ expect(fs.readFileSync(filePath, "utf-8")).not.toContain("Original machine entry");
60
+ });
61
+ it("skips entries whose text is already recorded", async () => {
62
+ const journal = new ProjectJournal(project(), FIXED_DATE);
63
+ await journal.append([{ tag: "files", text: "Edited: src/a.ts" }]);
64
+ expect(await journal.append([{ tag: "files", text: "Edited: src/a.ts" }])).toBe(0);
65
+ expect(journal.read()).toHaveLength(1);
66
+ });
67
+ it("clamps entry text and drops empty entries", async () => {
68
+ const journal = new ProjectJournal(project(), FIXED_DATE);
69
+ const written = await journal.append([
70
+ { tag: "summary", text: "z".repeat(5000) },
71
+ { tag: "summary", text: " " },
72
+ ]);
73
+ expect(written).toBe(1);
74
+ const [entry] = journal.read();
75
+ expect(entry.text.length).toBeLessThanOrEqual(ENTRY_MAX_CHARS);
76
+ expect(entry.text.endsWith("\u2026")).toBe(true);
77
+ });
78
+ it("collapses newlines so one entry can never become two lines", async () => {
79
+ const journal = new ProjectJournal(project(), FIXED_DATE);
80
+ await journal.append([{ tag: "summary", text: "line one\nline two\n- fake entry" }]);
81
+ expect(journal.read()).toHaveLength(1);
82
+ expect(journal.read()[0].text).toBe("line one line two - fake entry");
83
+ });
84
+ it("cuts a long entry at a sentence boundary, not mid-word", async () => {
85
+ const journal = new ProjectJournal(project(), FIXED_DATE);
86
+ // Comfortably over ENTRY_MAX_CHARS so truncation actually engages.
87
+ const sentences = "Replaced the naive lowercase call in the slug helper with a regex substitution. " +
88
+ "The first attempt dropped unicode characters entirely and broke two fixtures. " +
89
+ "Fixed it by switching to an explicit character class. " +
90
+ "Then rewrote the helper so it also trims leading hyphens from the output value. " +
91
+ "Also updated the docblock to describe the new normalisation order in detail. " +
92
+ "Finally confirmed the behaviour against the existing suite of unit tests today.";
93
+ await journal.append([{ tag: "summary", text: sentences }]);
94
+ const text = journal.read()[0].text;
95
+ expect(text.length).toBeLessThanOrEqual(ENTRY_MAX_CHARS);
96
+ // Ends on a complete sentence, with the truncation still visible.
97
+ expect(text.endsWith(". \u2026")).toBe(true);
98
+ expect(text).toContain("explicit character class.");
99
+ // The severed sentence is absent rather than present as a fragment.
100
+ expect(text).not.toContain("Finally confirmed");
101
+ });
102
+ it("falls back to a word boundary when no sentence end fits", async () => {
103
+ const journal = new ProjectJournal(project(), FIXED_DATE);
104
+ // One long clause: no terminal punctuation to cut back to.
105
+ const words = Array.from({ length: 120 }, (_, i) => `token${i}`).join(" ");
106
+ await journal.append([{ tag: "summary", text: words }]);
107
+ const text = journal.read()[0].text;
108
+ expect(text.length).toBeLessThanOrEqual(ENTRY_MAX_CHARS);
109
+ // A whole token, never a severed one like "token4" cut from "token42".
110
+ const lastToken = text
111
+ .replace(/\u2026$/, "")
112
+ .trim()
113
+ .split(" ")
114
+ .pop();
115
+ expect(words.split(" ")).toContain(lastToken);
116
+ });
117
+ it("never severs a file path in the middle", async () => {
118
+ const journal = new ProjectJournal(project(), FIXED_DATE);
119
+ const paths = Array.from({ length: 40 }, (_, i) => `packages/ggcoder/src/module-${i}.ts`);
120
+ await journal.append([{ tag: "files", text: `Edited: ${paths.join(", ")}` }]);
121
+ const text = journal.read()[0].text;
122
+ const listed = text
123
+ .replace(/^Edited:\s*/, "")
124
+ .replace(/\u2026$/, "")
125
+ .split(",")
126
+ .map((entry) => entry.trim())
127
+ .filter(Boolean);
128
+ for (const entry of listed)
129
+ expect(paths).toContain(entry);
130
+ });
131
+ it("leaves text that already fits completely untouched", async () => {
132
+ const journal = new ProjectJournal(project(), FIXED_DATE);
133
+ await journal.append([{ tag: "summary", text: "Short and complete." }]);
134
+ expect(journal.read()[0].text).toBe("Short and complete.");
135
+ });
136
+ it("returns nothing for a project with no journal", () => {
137
+ expect(new ProjectJournal(project()).read()).toEqual([]);
138
+ });
139
+ it("survives concurrent appends without losing entries", async () => {
140
+ const cwd = project();
141
+ // Separate instances, as two daemons sharing one project would be.
142
+ const writers = Array.from({ length: 5 }, () => new ProjectJournal(cwd, FIXED_DATE));
143
+ await Promise.all(writers.map((w, i) => w.append([{ tag: "files", text: `Edited: f${i}.ts` }])));
144
+ expect(new ProjectJournal(cwd).read()).toHaveLength(5);
145
+ });
146
+ });
147
+ describe("buildJournalPromptTail", () => {
148
+ it("costs nothing when the journal is empty", () => {
149
+ expect(buildJournalPromptTail(new ProjectJournal(project()))).toBe("");
150
+ });
151
+ it("renders recent entries and points at the file for older ones", async () => {
152
+ const cwd = project();
153
+ const journal = new ProjectJournal(cwd, FIXED_DATE);
154
+ for (let i = 0; i < PROMPT_ENTRY_LIMIT + 5; i++) {
155
+ await journal.append([{ tag: "summary", text: `Entry number ${i}` }]);
156
+ }
157
+ const tail = buildJournalPromptTail(journal);
158
+ expect(tail).toContain("## Project memory");
159
+ // Newest entries are present, oldest are not.
160
+ expect(tail).toContain(`Entry number ${PROMPT_ENTRY_LIMIT + 4}`);
161
+ expect(tail).not.toContain("Entry number 0 ");
162
+ // Older material is reachable, not silently dropped.
163
+ expect(tail).toContain(JOURNAL_RELATIVE_PATH);
164
+ expect(tail).toContain("5 older entries");
165
+ });
166
+ it("frames entries as past events, not current state", async () => {
167
+ const journal = new ProjectJournal(project(), FIXED_DATE);
168
+ await journal.append([{ tag: "files", text: "Edited: src/a.ts" }]);
169
+ const tail = buildJournalPromptTail(journal);
170
+ // Bench G: the model will not verify on its own, so the framing must at
171
+ // least never present these as a description of how things are now.
172
+ expect(tail).toContain("records of past events");
173
+ expect(tail).toContain("check the code");
174
+ });
175
+ it("respects the char budget", async () => {
176
+ const journal = new ProjectJournal(project(), FIXED_DATE);
177
+ for (let i = 0; i < 8; i++) {
178
+ await journal.append([{ tag: "summary", text: `${i} ${"long body ".repeat(30)}` }]);
179
+ }
180
+ const tail = buildJournalPromptTail(journal, { charBudget: 300 });
181
+ expect(tail.length).toBeLessThan(700);
182
+ });
183
+ });
184
+ //# sourceMappingURL=journal.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journal.test.js","sourceRoot":"","sources":["../../../src/core/memory/journal.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=process-manager-notifications.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-manager-notifications.test.d.ts","sourceRoot":"","sources":["../../src/core/process-manager-notifications.test.ts"],"names":[],"mappings":""}