@kenkaiiii/ggcoder 4.3.235 → 4.3.237
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/command-routing.d.ts +1 -1
- package/dist/cli/command-routing.d.ts.map +1 -1
- package/dist/cli/command-routing.js +2 -0
- package/dist/cli/command-routing.js.map +1 -1
- package/dist/cli/command-routing.test.js +1 -0
- package/dist/cli/command-routing.test.js.map +1 -1
- package/dist/cli/mcp.d.ts +2 -0
- package/dist/cli/mcp.d.ts.map +1 -0
- package/dist/cli/mcp.js +309 -0
- package/dist/cli/mcp.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -2
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/core/loop-breaker.d.ts +46 -0
- package/dist/core/loop-breaker.d.ts.map +1 -0
- package/dist/core/loop-breaker.js +79 -0
- package/dist/core/loop-breaker.js.map +1 -0
- package/dist/core/loop-breaker.test.d.ts +2 -0
- package/dist/core/loop-breaker.test.d.ts.map +1 -0
- package/dist/core/loop-breaker.test.js +110 -0
- package/dist/core/loop-breaker.test.js.map +1 -0
- package/dist/core/mcp/client.d.ts +20 -0
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +55 -11
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/defaults.d.ts +7 -0
- package/dist/core/mcp/defaults.d.ts.map +1 -1
- package/dist/core/mcp/defaults.js +14 -0
- package/dist/core/mcp/defaults.js.map +1 -1
- package/dist/core/mcp/index.d.ts +6 -1
- package/dist/core/mcp/index.d.ts.map +1 -1
- package/dist/core/mcp/index.js +3 -1
- package/dist/core/mcp/index.js.map +1 -1
- package/dist/core/mcp/parse-add-command.d.ts +25 -0
- package/dist/core/mcp/parse-add-command.d.ts.map +1 -0
- package/dist/core/mcp/parse-add-command.js +220 -0
- package/dist/core/mcp/parse-add-command.js.map +1 -0
- package/dist/core/mcp/parse-add-command.test.d.ts +2 -0
- package/dist/core/mcp/parse-add-command.test.d.ts.map +1 -0
- package/dist/core/mcp/parse-add-command.test.js +75 -0
- package/dist/core/mcp/parse-add-command.test.js.map +1 -0
- package/dist/core/mcp/store.d.ts +80 -0
- package/dist/core/mcp/store.d.ts.map +1 -0
- package/dist/core/mcp/store.js +157 -0
- package/dist/core/mcp/store.js.map +1 -0
- package/dist/core/mcp/store.test.d.ts +2 -0
- package/dist/core/mcp/store.test.d.ts.map +1 -0
- package/dist/core/mcp/store.test.js +101 -0
- package/dist/core/mcp/store.test.js.map +1 -0
- package/dist/core/regrounding.d.ts +23 -0
- package/dist/core/regrounding.d.ts.map +1 -0
- package/dist/core/regrounding.js +21 -0
- package/dist/core/regrounding.js.map +1 -0
- package/dist/core/regrounding.test.d.ts +2 -0
- package/dist/core/regrounding.test.d.ts.map +1 -0
- package/dist/core/regrounding.test.js +38 -0
- package/dist/core/regrounding.test.js.map +1 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +29 -2
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/app-items.d.ts +20 -3
- package/dist/ui/app-items.d.ts.map +1 -1
- package/dist/ui/app-items.js +11 -0
- package/dist/ui/app-items.js.map +1 -1
- package/dist/ui/components/ChatInputStack.js +1 -1
- package/dist/ui/components/ChatInputStack.js.map +1 -1
- package/dist/ui/components/IdealHookMessage.d.ts +3 -1
- package/dist/ui/components/IdealHookMessage.d.ts.map +1 -1
- package/dist/ui/components/IdealHookMessage.js +4 -2
- package/dist/ui/components/IdealHookMessage.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts +7 -0
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +108 -9
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/hooks/useChatLayoutMeasurements.d.ts.map +1 -1
- package/dist/ui/hooks/useChatLayoutMeasurements.js +6 -7
- package/dist/ui/hooks/useChatLayoutMeasurements.js.map +1 -1
- package/dist/ui/mcp.d.ts +62 -0
- package/dist/ui/mcp.d.ts.map +1 -0
- package/dist/ui/mcp.js +302 -0
- package/dist/ui/mcp.js.map +1 -0
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +6 -0
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/terminal-history.js +9 -7
- package/dist/ui/terminal-history.js.map +1 -1
- package/dist/ui/transcript/TranscriptRenderer.d.ts.map +1 -1
- package/dist/ui/transcript/TranscriptRenderer.js +1 -1
- package/dist/ui/transcript/TranscriptRenderer.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { MCPServerConfig } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Where a server config lives. We collapse Claude Code's three scopes
|
|
5
|
+
* (local/project/user) into two:
|
|
6
|
+
* - "global" → ~/.gg/mcp.json (all GG Coder sessions)
|
|
7
|
+
* - "project" → ./.gg/mcp.json (the current project root)
|
|
8
|
+
*/
|
|
9
|
+
export type MCPScope = "global" | "project";
|
|
10
|
+
/**
|
|
11
|
+
* On-disk entry shape. Accepts both Claude's `.mcp.json` fields
|
|
12
|
+
* (`type`, `url`, `headers`, `command`, `args`, `env`, `timeout`) and our
|
|
13
|
+
* extra (`enabled`) so configs are portable in both directions.
|
|
14
|
+
*/
|
|
15
|
+
declare const StoredServerEntrySchema: z.ZodObject<{
|
|
16
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
17
|
+
stdio: "stdio";
|
|
18
|
+
http: "http";
|
|
19
|
+
"streamable-http": "streamable-http";
|
|
20
|
+
sse: "sse";
|
|
21
|
+
}>>;
|
|
22
|
+
url: z.ZodOptional<z.ZodString>;
|
|
23
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
24
|
+
command: z.ZodOptional<z.ZodString>;
|
|
25
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
26
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
27
|
+
timeout: z.ZodOptional<z.ZodNumber>;
|
|
28
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
29
|
+
}, z.core.$loose>;
|
|
30
|
+
export type StoredServerEntry = z.infer<typeof StoredServerEntrySchema>;
|
|
31
|
+
declare const McpFileSchema: z.ZodObject<{
|
|
32
|
+
mcpServers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
33
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
34
|
+
stdio: "stdio";
|
|
35
|
+
http: "http";
|
|
36
|
+
"streamable-http": "streamable-http";
|
|
37
|
+
sse: "sse";
|
|
38
|
+
}>>;
|
|
39
|
+
url: z.ZodOptional<z.ZodString>;
|
|
40
|
+
headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
41
|
+
command: z.ZodOptional<z.ZodString>;
|
|
42
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
43
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
44
|
+
timeout: z.ZodOptional<z.ZodNumber>;
|
|
45
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
46
|
+
}, z.core.$loose>>>;
|
|
47
|
+
}, z.core.$strip>;
|
|
48
|
+
export type McpFile = z.infer<typeof McpFileSchema>;
|
|
49
|
+
/** A loaded server config paired with the scope it came from. */
|
|
50
|
+
export interface ScopedServer {
|
|
51
|
+
config: MCPServerConfig;
|
|
52
|
+
scope: MCPScope;
|
|
53
|
+
}
|
|
54
|
+
export declare function globalMcpPath(): string;
|
|
55
|
+
export declare function projectMcpPath(cwd: string): string;
|
|
56
|
+
/** Map an on-disk entry to a runtime MCPServerConfig. */
|
|
57
|
+
export declare function fromStoredEntry(name: string, entry: StoredServerEntry): MCPServerConfig;
|
|
58
|
+
/** Map a runtime MCPServerConfig to the on-disk (Claude-compatible) entry. */
|
|
59
|
+
export declare function toStoredEntry(config: MCPServerConfig): StoredServerEntry;
|
|
60
|
+
/**
|
|
61
|
+
* Load servers from both scopes. Project wins on name collision (global entries
|
|
62
|
+
* whose name also exists in project are dropped). Each result carries its scope.
|
|
63
|
+
*/
|
|
64
|
+
export declare function loadServers(cwd: string): Promise<ScopedServer[]>;
|
|
65
|
+
/**
|
|
66
|
+
* Add (or overwrite) a server in the target scope's file. Rejects a duplicate
|
|
67
|
+
* name within the same scope unless `overwrite` is true.
|
|
68
|
+
*/
|
|
69
|
+
export declare function addServer(entry: MCPServerConfig, scope: MCPScope, cwd: string, overwrite?: boolean): Promise<{
|
|
70
|
+
ok: true;
|
|
71
|
+
} | {
|
|
72
|
+
ok: false;
|
|
73
|
+
error: string;
|
|
74
|
+
}>;
|
|
75
|
+
/** Remove a server from a scope. Returns true if it existed. */
|
|
76
|
+
export declare function removeServer(name: string, scope: MCPScope, cwd: string): Promise<boolean>;
|
|
77
|
+
/** Look up a single server across both scopes (project wins). */
|
|
78
|
+
export declare function getServer(name: string, cwd: string): Promise<ScopedServer | undefined>;
|
|
79
|
+
export {};
|
|
80
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE5C;;;;GAIG;AACH,QAAA,MAAM,uBAAuB;;;;;;;;;;;;;;iBAWb,CAAC;AAEjB,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,QAAA,MAAM,aAAa;;;;;;;;;;;;;;;;iBAEjB,CAAC;AAEH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,eAAe,CAAC;IACxB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAElD;AA6BD,yDAAyD;AACzD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,eAAe,CAcvF;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,GAAG,iBAAiB,CAexE;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAiBtE;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE,QAAQ,EACf,GAAG,EAAE,MAAM,EACX,SAAS,UAAQ,GAChB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAYtD;AAED,gEAAgE;AAChE,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/F;AAED,iEAAiE;AACjE,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAG5F"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { getAppPaths } from "../../config.js";
|
|
5
|
+
import { log } from "../logger.js";
|
|
6
|
+
/**
|
|
7
|
+
* On-disk entry shape. Accepts both Claude's `.mcp.json` fields
|
|
8
|
+
* (`type`, `url`, `headers`, `command`, `args`, `env`, `timeout`) and our
|
|
9
|
+
* extra (`enabled`) so configs are portable in both directions.
|
|
10
|
+
*/
|
|
11
|
+
const StoredServerEntrySchema = z
|
|
12
|
+
.object({
|
|
13
|
+
type: z.enum(["stdio", "http", "streamable-http", "sse"]).optional(),
|
|
14
|
+
url: z.string().optional(),
|
|
15
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
16
|
+
command: z.string().optional(),
|
|
17
|
+
args: z.array(z.string()).optional(),
|
|
18
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
19
|
+
timeout: z.number().optional(),
|
|
20
|
+
enabled: z.boolean().optional(),
|
|
21
|
+
})
|
|
22
|
+
.passthrough();
|
|
23
|
+
const McpFileSchema = z.object({
|
|
24
|
+
mcpServers: z.record(z.string(), StoredServerEntrySchema).default({}),
|
|
25
|
+
});
|
|
26
|
+
export function globalMcpPath() {
|
|
27
|
+
return getAppPaths().mcpFile;
|
|
28
|
+
}
|
|
29
|
+
export function projectMcpPath(cwd) {
|
|
30
|
+
return path.join(cwd, ".gg", "mcp.json");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Read + validate one mcp.json file. On missing file → empty. On malformed
|
|
34
|
+
* JSON/schema → log + treat as empty (don't crash), matching SettingsManager.
|
|
35
|
+
*/
|
|
36
|
+
async function readMcpFile(filePath) {
|
|
37
|
+
let content;
|
|
38
|
+
try {
|
|
39
|
+
content = await fs.readFile(filePath, "utf-8");
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return { mcpServers: {} };
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const raw = JSON.parse(content);
|
|
46
|
+
return McpFileSchema.parse(raw);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
log("WARN", "mcp", `Ignoring malformed MCP config at ${filePath}`, {
|
|
50
|
+
error: err instanceof Error ? err.message : String(err),
|
|
51
|
+
});
|
|
52
|
+
return { mcpServers: {} };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function writeMcpFile(filePath, file) {
|
|
56
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
57
|
+
await fs.writeFile(filePath, JSON.stringify(file, null, 2) + "\n", "utf-8");
|
|
58
|
+
}
|
|
59
|
+
/** Map an on-disk entry to a runtime MCPServerConfig. */
|
|
60
|
+
export function fromStoredEntry(name, entry) {
|
|
61
|
+
const config = { name };
|
|
62
|
+
// URL transports: explicit url, or type marks it as http/sse.
|
|
63
|
+
if (entry.url) {
|
|
64
|
+
config.url = entry.url;
|
|
65
|
+
if (entry.headers)
|
|
66
|
+
config.headers = entry.headers;
|
|
67
|
+
}
|
|
68
|
+
else if (entry.command) {
|
|
69
|
+
config.command = entry.command;
|
|
70
|
+
if (entry.args)
|
|
71
|
+
config.args = entry.args;
|
|
72
|
+
if (entry.env)
|
|
73
|
+
config.env = entry.env;
|
|
74
|
+
}
|
|
75
|
+
if (typeof entry.timeout === "number")
|
|
76
|
+
config.timeout = entry.timeout;
|
|
77
|
+
if (typeof entry.enabled === "boolean")
|
|
78
|
+
config.enabled = entry.enabled;
|
|
79
|
+
return config;
|
|
80
|
+
}
|
|
81
|
+
/** Map a runtime MCPServerConfig to the on-disk (Claude-compatible) entry. */
|
|
82
|
+
export function toStoredEntry(config) {
|
|
83
|
+
const entry = {};
|
|
84
|
+
if (config.url) {
|
|
85
|
+
entry.type = "http";
|
|
86
|
+
entry.url = config.url;
|
|
87
|
+
if (config.headers)
|
|
88
|
+
entry.headers = config.headers;
|
|
89
|
+
}
|
|
90
|
+
else if (config.command) {
|
|
91
|
+
entry.type = "stdio";
|
|
92
|
+
entry.command = config.command;
|
|
93
|
+
if (config.args)
|
|
94
|
+
entry.args = config.args;
|
|
95
|
+
if (config.env)
|
|
96
|
+
entry.env = config.env;
|
|
97
|
+
}
|
|
98
|
+
if (typeof config.timeout === "number")
|
|
99
|
+
entry.timeout = config.timeout;
|
|
100
|
+
if (typeof config.enabled === "boolean")
|
|
101
|
+
entry.enabled = config.enabled;
|
|
102
|
+
return entry;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Load servers from both scopes. Project wins on name collision (global entries
|
|
106
|
+
* whose name also exists in project are dropped). Each result carries its scope.
|
|
107
|
+
*/
|
|
108
|
+
export async function loadServers(cwd) {
|
|
109
|
+
const [globalFile, projectFile] = await Promise.all([
|
|
110
|
+
readMcpFile(globalMcpPath()),
|
|
111
|
+
readMcpFile(projectMcpPath(cwd)),
|
|
112
|
+
]);
|
|
113
|
+
const projectNames = new Set(Object.keys(projectFile.mcpServers));
|
|
114
|
+
const result = [];
|
|
115
|
+
for (const [name, entry] of Object.entries(globalFile.mcpServers)) {
|
|
116
|
+
if (projectNames.has(name))
|
|
117
|
+
continue; // project overrides global
|
|
118
|
+
result.push({ config: fromStoredEntry(name, entry), scope: "global" });
|
|
119
|
+
}
|
|
120
|
+
for (const [name, entry] of Object.entries(projectFile.mcpServers)) {
|
|
121
|
+
result.push({ config: fromStoredEntry(name, entry), scope: "project" });
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Add (or overwrite) a server in the target scope's file. Rejects a duplicate
|
|
127
|
+
* name within the same scope unless `overwrite` is true.
|
|
128
|
+
*/
|
|
129
|
+
export async function addServer(entry, scope, cwd, overwrite = false) {
|
|
130
|
+
const filePath = scope === "global" ? globalMcpPath() : projectMcpPath(cwd);
|
|
131
|
+
const file = await readMcpFile(filePath);
|
|
132
|
+
if (file.mcpServers[entry.name] && !overwrite) {
|
|
133
|
+
return {
|
|
134
|
+
ok: false,
|
|
135
|
+
error: `A "${entry.name}" server already exists in ${scope} scope. Remove it first or use a different name.`,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
file.mcpServers[entry.name] = toStoredEntry(entry);
|
|
139
|
+
await writeMcpFile(filePath, file);
|
|
140
|
+
return { ok: true };
|
|
141
|
+
}
|
|
142
|
+
/** Remove a server from a scope. Returns true if it existed. */
|
|
143
|
+
export async function removeServer(name, scope, cwd) {
|
|
144
|
+
const filePath = scope === "global" ? globalMcpPath() : projectMcpPath(cwd);
|
|
145
|
+
const file = await readMcpFile(filePath);
|
|
146
|
+
if (!file.mcpServers[name])
|
|
147
|
+
return false;
|
|
148
|
+
delete file.mcpServers[name];
|
|
149
|
+
await writeMcpFile(filePath, file);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
/** Look up a single server across both scopes (project wins). */
|
|
153
|
+
export async function getServer(name, cwd) {
|
|
154
|
+
const servers = await loadServers(cwd);
|
|
155
|
+
return servers.find((s) => s.config.name === name);
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/core/mcp/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/store.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
// Point the global mcp file at a temp home so tests don't touch the real ~/.gg.
|
|
6
|
+
let tmpHome;
|
|
7
|
+
let tmpProject;
|
|
8
|
+
vi.mock("../../config.js", async () => {
|
|
9
|
+
const actual = await vi.importActual("../../config.js");
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
getAppPaths: () => ({
|
|
13
|
+
...actual.getAppPaths(),
|
|
14
|
+
mcpFile: path.join(process.env.GG_TEST_HOME, ".gg", "mcp.json"),
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
import { addServer, loadServers, removeServer, getServer, fromStoredEntry, globalMcpPath, projectMcpPath, } from "./store.js";
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
tmpHome = await fs.mkdtemp(path.join(os.tmpdir(), "gg-mcp-home-"));
|
|
21
|
+
tmpProject = await fs.mkdtemp(path.join(os.tmpdir(), "gg-mcp-proj-"));
|
|
22
|
+
process.env.GG_TEST_HOME = tmpHome;
|
|
23
|
+
});
|
|
24
|
+
afterEach(async () => {
|
|
25
|
+
await fs.rm(tmpHome, { recursive: true, force: true });
|
|
26
|
+
await fs.rm(tmpProject, { recursive: true, force: true });
|
|
27
|
+
delete process.env.GG_TEST_HOME;
|
|
28
|
+
});
|
|
29
|
+
describe("mcp store", () => {
|
|
30
|
+
it("round-trips an http server in global scope", async () => {
|
|
31
|
+
const res = await addServer({ name: "notion", url: "https://mcp.notion.com/mcp" }, "global", tmpProject);
|
|
32
|
+
expect(res.ok).toBe(true);
|
|
33
|
+
const servers = await loadServers(tmpProject);
|
|
34
|
+
expect(servers).toEqual([
|
|
35
|
+
{ config: { name: "notion", url: "https://mcp.notion.com/mcp" }, scope: "global" },
|
|
36
|
+
]);
|
|
37
|
+
// On-disk shape uses Claude's `type` field.
|
|
38
|
+
const raw = JSON.parse(await fs.readFile(globalMcpPath(), "utf-8"));
|
|
39
|
+
expect(raw.mcpServers.notion.type).toBe("http");
|
|
40
|
+
});
|
|
41
|
+
it("round-trips a stdio server with env in project scope", async () => {
|
|
42
|
+
await addServer({
|
|
43
|
+
name: "airtable",
|
|
44
|
+
command: "npx",
|
|
45
|
+
args: ["-y", "airtable-mcp-server"],
|
|
46
|
+
env: { AIRTABLE_API_KEY: "key" },
|
|
47
|
+
}, "project", tmpProject);
|
|
48
|
+
const servers = await loadServers(tmpProject);
|
|
49
|
+
expect(servers[0]).toEqual({
|
|
50
|
+
config: {
|
|
51
|
+
name: "airtable",
|
|
52
|
+
command: "npx",
|
|
53
|
+
args: ["-y", "airtable-mcp-server"],
|
|
54
|
+
env: { AIRTABLE_API_KEY: "key" },
|
|
55
|
+
},
|
|
56
|
+
scope: "project",
|
|
57
|
+
});
|
|
58
|
+
// Project file lives at ./.gg/mcp.json.
|
|
59
|
+
await fs.access(projectMcpPath(tmpProject));
|
|
60
|
+
});
|
|
61
|
+
it("project overrides global on name collision", async () => {
|
|
62
|
+
await addServer({ name: "dup", url: "https://global.example/mcp" }, "global", tmpProject);
|
|
63
|
+
await addServer({ name: "dup", url: "https://project.example/mcp" }, "project", tmpProject);
|
|
64
|
+
const servers = await loadServers(tmpProject);
|
|
65
|
+
expect(servers).toHaveLength(1);
|
|
66
|
+
expect(servers[0].scope).toBe("project");
|
|
67
|
+
expect(servers[0].config.url).toBe("https://project.example/mcp");
|
|
68
|
+
});
|
|
69
|
+
it("rejects duplicate name in the same scope unless overwrite", async () => {
|
|
70
|
+
await addServer({ name: "x", url: "https://a.example/mcp" }, "global", tmpProject);
|
|
71
|
+
const dup = await addServer({ name: "x", url: "https://b.example/mcp" }, "global", tmpProject);
|
|
72
|
+
expect(dup.ok).toBe(false);
|
|
73
|
+
const ow = await addServer({ name: "x", url: "https://b.example/mcp" }, "global", tmpProject, true);
|
|
74
|
+
expect(ow.ok).toBe(true);
|
|
75
|
+
const found = await getServer("x", tmpProject);
|
|
76
|
+
expect(found?.config.url).toBe("https://b.example/mcp");
|
|
77
|
+
});
|
|
78
|
+
it("removes a server", async () => {
|
|
79
|
+
await addServer({ name: "gone", url: "https://x.example/mcp" }, "global", tmpProject);
|
|
80
|
+
expect(await removeServer("gone", "global", tmpProject)).toBe(true);
|
|
81
|
+
expect(await removeServer("gone", "global", tmpProject)).toBe(false);
|
|
82
|
+
expect(await loadServers(tmpProject)).toEqual([]);
|
|
83
|
+
});
|
|
84
|
+
it("tolerates a malformed config file", async () => {
|
|
85
|
+
await fs.mkdir(path.dirname(globalMcpPath()), { recursive: true });
|
|
86
|
+
await fs.writeFile(globalMcpPath(), "{ not valid json", "utf-8");
|
|
87
|
+
expect(await loadServers(tmpProject)).toEqual([]);
|
|
88
|
+
});
|
|
89
|
+
it("parses Claude's .mcp.json type field for http and sse", () => {
|
|
90
|
+
expect(fromStoredEntry("n", { type: "http", url: "https://x/mcp" })).toEqual({
|
|
91
|
+
name: "n",
|
|
92
|
+
url: "https://x/mcp",
|
|
93
|
+
});
|
|
94
|
+
expect(fromStoredEntry("s", {
|
|
95
|
+
type: "sse",
|
|
96
|
+
url: "https://x/sse",
|
|
97
|
+
headers: { Authorization: "Bearer t" },
|
|
98
|
+
})).toEqual({ name: "s", url: "https://x/sse", headers: { Authorization: "Bearer t" } });
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
//# sourceMappingURL=store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.test.js","sourceRoot":"","sources":["../../../src/core/mcp/store.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Message } from "@kenkaiiii/gg-ai";
|
|
2
|
+
/**
|
|
3
|
+
* Post-compaction re-grounding hook.
|
|
4
|
+
*
|
|
5
|
+
* Compaction summarizes earlier context, which is a known drift point: the
|
|
6
|
+
* model can quietly lose the verbatim original ask or a subtle constraint.
|
|
7
|
+
* After a compaction this run, inject the original request verbatim (the one
|
|
8
|
+
* piece of ground truth the summary may have lossily rewritten) so the model
|
|
9
|
+
* re-anchors before its next step. Fires at most once per compaction event.
|
|
10
|
+
*
|
|
11
|
+
* Per the "introspection alone fails" research, this injects concrete
|
|
12
|
+
* evidence (the literal original request) rather than asking the model to
|
|
13
|
+
* "think harder".
|
|
14
|
+
*/
|
|
15
|
+
export interface RegroundingState {
|
|
16
|
+
/** A compaction/overflow-compaction happened during this run. */
|
|
17
|
+
compactionOccurred: boolean;
|
|
18
|
+
/** A re-grounding message was already injected for this compaction. */
|
|
19
|
+
alreadyInjected: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function shouldReground(state: RegroundingState): boolean;
|
|
22
|
+
export declare function buildRegroundingMessage(originalRequest: string): Message;
|
|
23
|
+
//# sourceMappingURL=regrounding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regrounding.d.ts","sourceRoot":"","sources":["../../src/core/regrounding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEhD;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,kBAAkB,EAAE,OAAO,CAAC;IAC5B,uEAAuE;IACvE,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAE/D;AAED,wBAAgB,uBAAuB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAkBxE"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function shouldReground(state) {
|
|
2
|
+
return state.compactionOccurred && !state.alreadyInjected;
|
|
3
|
+
}
|
|
4
|
+
export function buildRegroundingMessage(originalRequest) {
|
|
5
|
+
const trimmed = originalRequest.trim();
|
|
6
|
+
const pin = trimmed.length > 0
|
|
7
|
+
? `\n\nThe user's original request was:\n\n ${trimmed}\n\n`
|
|
8
|
+
: " The original request is in the earlier messages that remain. ";
|
|
9
|
+
return {
|
|
10
|
+
role: "user",
|
|
11
|
+
content: "Re-ground. The conversation was just compacted, so earlier detail is now a summary and " +
|
|
12
|
+
"easy to drift from." +
|
|
13
|
+
pin +
|
|
14
|
+
"Before continuing, re-anchor on two things: that original request, and the specific " +
|
|
15
|
+
"objective you're working on right now. Verify your next step still serves the original " +
|
|
16
|
+
"ask \u2014 not a reshaped or narrowed version of it. If the summary dropped a constraint, " +
|
|
17
|
+
"file, or requirement you were tracking, recover it from the messages that remain before " +
|
|
18
|
+
"acting. Then continue. Do not restate this note; just proceed correctly.",
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=regrounding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regrounding.js","sourceRoot":"","sources":["../../src/core/regrounding.ts"],"names":[],"mappings":"AAuBA,MAAM,UAAU,cAAc,CAAC,KAAuB;IACpD,OAAO,KAAK,CAAC,kBAAkB,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,eAAuB;IAC7D,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,CAAC;IACvC,MAAM,GAAG,GACP,OAAO,CAAC,MAAM,GAAG,CAAC;QAChB,CAAC,CAAC,6CAA6C,OAAO,MAAM;QAC5D,CAAC,CAAC,gEAAgE,CAAC;IACvE,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,OAAO,EACL,yFAAyF;YACzF,qBAAqB;YACrB,GAAG;YACH,sFAAsF;YACtF,yFAAyF;YACzF,4FAA4F;YAC5F,0FAA0F;YAC1F,0EAA0E;KAC7E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regrounding.test.d.ts","sourceRoot":"","sources":["../../src/core/regrounding.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { buildRegroundingMessage, shouldReground } from "./regrounding.js";
|
|
3
|
+
describe("shouldReground", () => {
|
|
4
|
+
it("fires once after a compaction occurs", () => {
|
|
5
|
+
expect(shouldReground({ compactionOccurred: true, alreadyInjected: false })).toBe(true);
|
|
6
|
+
});
|
|
7
|
+
it("does not fire when no compaction has happened", () => {
|
|
8
|
+
expect(shouldReground({ compactionOccurred: false, alreadyInjected: false })).toBe(false);
|
|
9
|
+
});
|
|
10
|
+
it("does not fire twice for the same compaction", () => {
|
|
11
|
+
expect(shouldReground({ compactionOccurred: true, alreadyInjected: true })).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
describe("buildRegroundingMessage", () => {
|
|
15
|
+
it("pins the original request verbatim", () => {
|
|
16
|
+
const original = "Rename the qty field to quantity everywhere in inventory.ts";
|
|
17
|
+
const message = buildRegroundingMessage(original);
|
|
18
|
+
expect(message.role).toBe("user");
|
|
19
|
+
expect(message.content).toContain("Re-ground");
|
|
20
|
+
expect(message.content).toContain(original);
|
|
21
|
+
});
|
|
22
|
+
it("explains that context was compacted and warns against drift", () => {
|
|
23
|
+
const content = buildRegroundingMessage("do the thing").content;
|
|
24
|
+
expect(content).toContain("compacted");
|
|
25
|
+
expect(content.toLowerCase()).toContain("drift");
|
|
26
|
+
});
|
|
27
|
+
it("does not instruct the model to narrate the note", () => {
|
|
28
|
+
const content = buildRegroundingMessage("do the thing").content;
|
|
29
|
+
expect(content.toLowerCase()).toContain("do not restate");
|
|
30
|
+
});
|
|
31
|
+
it("trims whitespace and tolerates an empty original request", () => {
|
|
32
|
+
const message = buildRegroundingMessage(" ");
|
|
33
|
+
expect(message.role).toBe("user");
|
|
34
|
+
expect(message.content).toContain("Re-ground");
|
|
35
|
+
expect(message.content).not.toContain('""');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
//# sourceMappingURL=regrounding.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regrounding.test.js","sourceRoot":"","sources":["../../src/core/regrounding.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE3E,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,cAAc,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,cAAc,CAAC,EAAE,kBAAkB,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,cAAc,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,QAAQ,GAAG,6DAA6D,CAAC;QAC/E,MAAM,OAAO,GAAG,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QAClD,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAC/C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,OAAO,GAAG,uBAAuB,CAAC,cAAc,CAAC,CAAC,OAAiB,CAAC;QAC1E,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACvC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,OAAO,GAAG,uBAAuB,CAAC,cAAc,CAAC,CAAC,OAAiB,CAAC;QAC1E,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,OAAO,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;QAC/C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAC/C,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/ui/App.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":"AAqBA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,aAAa,EAEnB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAcrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAwBjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAkB,eAAe,EAAe,MAAM,6BAA6B,CAAC;AAEhG,OAAO,EAML,KAAK,QAAQ,EACd,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAS3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":"AAqBA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,aAAa,EAEnB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAcrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAwBjE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAkB,eAAe,EAAe,MAAM,6BAA6B,CAAC;AAEhG,OAAO,EAML,KAAK,QAAQ,EACd,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAS3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAQpE,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,uBAAuB,CAAC;AAS/B,OAAO,EAOL,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAY9B,OAAO,KAAK,EACV,aAAa,EAYd,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAOnE,YAAY,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,+BAA+B,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC/F,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,EACpB,8BAA8B,EAC9B,mBAAmB,EACnB,gCAAgC,EAChC,qBAAqB,EACrB,+BAA+B,EAC/B,mCAAmC,EACnC,kCAAkC,EAClC,uCAAuC,EACvC,wCAAwC,EACxC,gCAAgC,GACjC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,sBAAsB,EACtB,YAAY,EACZ,kBAAkB,EAClB,kCAAkC,GACnC,MAAM,mBAAmB,CAAC;AAiB3B,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC3D,iBAAiB,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAC5B,MAAM,EACN;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAClF,CAAC;IACF,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IACnC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IACjB,qEAAqE;IACrE,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;IAClD,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACpD,aAAa,CAAC,EAAE;QACd,WAAW,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACxD,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;KACpD,CAAC;IACF,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAChD;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE;QACnB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;QACrB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;QAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE;YACd,MAAM,EAAE,MAAM,CAAC;YACf,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,SAAS,CAAC,EAAE;gBAAE,KAAK,EAAE,UAAU,GAAG,UAAU,GAAG,WAAW,CAAC;gBAAC,MAAM,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;SAC/E,CAAC;KACH,KAAK,IAAI,CAAC;IACX;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE;QAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,QAAQ,CAAC;QACpB,QAAQ,CAAC,EAAE,aAAa,CAAC;KAC1B,KAAK,IAAI,CAAC;IACX;;;;;OAKG;IACH,YAAY,CAAC,EAAE;QACb,QAAQ,EAAE,OAAO,EAAE,CAAC;QACpB,OAAO,EAAE,aAAa,EAAE,CAAC;QACzB,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;QAC5B,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;QAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,SAAS,EAAE,QAAQ,EAAE,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,qBAAqB,EAAE,OAAO,CAAC;QAC/B,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,IAAI,CAAC;QACjE,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,aAAa,CAAC,EAAE;YACd,MAAM,EAAE,MAAM,CAAC;YACf,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,SAAS,CAAC,EAAE;gBAAE,KAAK,EAAE,UAAU,GAAG,UAAU,GAAG,WAAW,CAAC;gBAAC,MAAM,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;SAC/E,CAAC;QACF,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,YAAY,CAAC,EAAE,YAAY,CAAC;QAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAC;KAC9B,CAAC;CACH;AAuBD,wBAAgB,GAAG,CAAC,KAAK,EAAE,QAAQ,2CA0nFlC"}
|
package/dist/ui/App.js
CHANGED
|
@@ -44,6 +44,8 @@ import { buildUserContentWithAttachments } from "./prompt-routing.js";
|
|
|
44
44
|
import { submitPromptCommand } from "./submit-prompt-command.js";
|
|
45
45
|
import { handleUiSlashCommand } from "./submit-slash-commands.js";
|
|
46
46
|
import { buildIdealReviewMessage, evaluateIdealReview } from "../core/ideal-review.js";
|
|
47
|
+
import { buildLoopBreakMessage, evaluateLoopBreak } from "../core/loop-breaker.js";
|
|
48
|
+
import { buildRegroundingMessage } from "../core/regrounding.js";
|
|
47
49
|
import { getNextThinkingLevel, isThinkingLevelSupported } from "./thinking-level.js";
|
|
48
50
|
import { getDoneFlushDecision, shouldTopSpaceAfterPrintedAgentBoundary, shouldTopSpaceStreamingAssistant, } from "./layout-decisions.js";
|
|
49
51
|
import { isTranscriptSpacingItem } from "./transcript/spacing.js";
|
|
@@ -56,7 +58,7 @@ import { pickDurationVerb } from "./duration-summary.js";
|
|
|
56
58
|
import { toErrorItem } from "./error-item.js";
|
|
57
59
|
import { addLinesChanged, buildSessionSummary, createSessionStats, recordServerToolCall, recordToolEnd, recordTurnEnd, } from "./session-summary.js";
|
|
58
60
|
import { compactHistory, getNextGeneratedItemId, isActiveItem, isSameAssistantText, normalizeAssistantText, partitionCompleted, pinStreamingTextBeforeToolBoundary, removeItemsWithIds, uniqueItemsById, } from "./item-helpers.js";
|
|
59
|
-
import { IDEAL_HOOK_NOTICE_TEXT, lastVisibleTranscriptItem } from "./app-items.js";
|
|
61
|
+
import { IDEAL_HOOK_NOTICE_TEXT, LOOP_BREAK_NOTICE_TEXT, REGROUNDING_NOTICE_TEXT, lastVisibleTranscriptItem, } from "./app-items.js";
|
|
60
62
|
export { buildUserContentWithAttachments, routePromptCommandInput } from "./prompt-routing.js";
|
|
61
63
|
export { getNextThinkingLevel } from "./thinking-level.js";
|
|
62
64
|
export { getChatControlsLayoutDecision, getDoneFlushDecision, getScrollStabilizationDecision, getStaticHistoryKey, hasParagraphBreakLiveUserMessage, isTallLiveUserMessage, shouldHideHistoryForOverlayView, shouldHideStaticItemsForOverlayView, shouldStabilizeOverlayPaneRerender, shouldTopSpaceAfterPrintedAgentBoundary, shouldTopSpaceAssistantAfterToolBoundary, shouldTopSpaceStreamingAssistant, } from "./layout-decisions.js";
|
|
@@ -580,10 +582,35 @@ export function App(props) {
|
|
|
580
582
|
});
|
|
581
583
|
setLiveItems((prev) => [
|
|
582
584
|
...prev,
|
|
583
|
-
{ kind: "ideal_hook", text: IDEAL_HOOK_NOTICE_TEXT, id: getId() },
|
|
585
|
+
{ kind: "ideal_hook", text: IDEAL_HOOK_NOTICE_TEXT, tone: "review", id: getId() },
|
|
584
586
|
]);
|
|
585
587
|
return buildIdealReviewMessage(decision.reasons);
|
|
586
588
|
},
|
|
589
|
+
getLoopBreakMessage: (stats) => {
|
|
590
|
+
if (!idealReviewEnabledRef.current)
|
|
591
|
+
return null;
|
|
592
|
+
const decision = evaluateLoopBreak(stats);
|
|
593
|
+
if (!decision.shouldBreak)
|
|
594
|
+
return null;
|
|
595
|
+
log("INFO", "loop-break", "Injecting loop-break nudge", {
|
|
596
|
+
reasons: decision.reasons.join(", "),
|
|
597
|
+
});
|
|
598
|
+
setLiveItems((prev) => [
|
|
599
|
+
...prev,
|
|
600
|
+
{ kind: "ideal_hook", text: LOOP_BREAK_NOTICE_TEXT, tone: "warning", id: getId() },
|
|
601
|
+
]);
|
|
602
|
+
return buildLoopBreakMessage(decision.reasons);
|
|
603
|
+
},
|
|
604
|
+
getRegroundingMessage: (originalRequest) => {
|
|
605
|
+
if (!idealReviewEnabledRef.current)
|
|
606
|
+
return null;
|
|
607
|
+
log("INFO", "reground", "Injecting re-grounding after compaction", {});
|
|
608
|
+
setLiveItems((prev) => [
|
|
609
|
+
...prev,
|
|
610
|
+
{ kind: "ideal_hook", text: REGROUNDING_NOTICE_TEXT, tone: "info", id: getId() },
|
|
611
|
+
]);
|
|
612
|
+
return buildRegroundingMessage(originalRequest);
|
|
613
|
+
},
|
|
587
614
|
}, {
|
|
588
615
|
onComplete: useCallback(() => {
|
|
589
616
|
persistNewMessages();
|