gsd-pi 2.67.0-dev.4fb8afe → 2.67.0-dev.509bd95
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/resources/extensions/gsd/commands/handlers/core.js +38 -24
- package/dist/resources/extensions/gsd/commands/index.js +8 -1
- package/dist/resources/extensions/gsd/workflow-logger.js +18 -3
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +52 -25
- package/src/resources/extensions/gsd/commands/index.ts +7 -1
- package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +101 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +16 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +19 -3
- /package/dist/web/standalone/.next/static/{IBTC_HlEpTBAa4HXMoV_A → mHJZ3Z8yGRzZ32BmQs-I7}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{IBTC_HlEpTBAa4HXMoV_A → mHJZ3Z8yGRzZ32BmQs-I7}/_ssgManifest.js +0 -0
|
@@ -74,3 +74,104 @@ test("model command resolves and persists exact provider-qualified selection", a
|
|
|
74
74
|
assert.deepEqual(applied, selectedModel);
|
|
75
75
|
assert.match(notices[0]!.message, /openai\/gpt-5\.4/);
|
|
76
76
|
});
|
|
77
|
+
|
|
78
|
+
test("interactive model picker chooses provider first, then model", async () => {
|
|
79
|
+
const selectedModel = { provider: "openai", id: "gpt-5.4" };
|
|
80
|
+
let applied: typeof selectedModel | null = null;
|
|
81
|
+
const selects: Array<{ title: string; options: string[] }> = [];
|
|
82
|
+
const notices: Array<{ message: string; type?: string }> = [];
|
|
83
|
+
|
|
84
|
+
const ctx = {
|
|
85
|
+
hasUI: true,
|
|
86
|
+
model: { provider: "anthropic", id: "claude-sonnet-4-6" },
|
|
87
|
+
modelRegistry: {
|
|
88
|
+
getAvailable: () => [
|
|
89
|
+
{ provider: "openai", id: "gpt-5.4" },
|
|
90
|
+
{ provider: "anthropic", id: "claude-opus-4-6" },
|
|
91
|
+
{ provider: "openai", id: "gpt-5.3-mini" },
|
|
92
|
+
{ provider: "anthropic", id: "claude-sonnet-4-6" },
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
ui: {
|
|
96
|
+
select: async (title: string, options: string[]) => {
|
|
97
|
+
selects.push({ title, options });
|
|
98
|
+
return selects.length === 1 ? "openai (2 models)" : "gpt-5.4";
|
|
99
|
+
},
|
|
100
|
+
notify: (message: string, type?: string) => {
|
|
101
|
+
notices.push({ message, type });
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
} as any;
|
|
105
|
+
|
|
106
|
+
const pi = {
|
|
107
|
+
setModel: async (model: typeof selectedModel) => {
|
|
108
|
+
applied = model;
|
|
109
|
+
return true;
|
|
110
|
+
},
|
|
111
|
+
} as any;
|
|
112
|
+
|
|
113
|
+
const handled = await handleCoreCommand("model", ctx, pi);
|
|
114
|
+
assert.equal(handled, true);
|
|
115
|
+
assert.deepEqual(selects, [
|
|
116
|
+
{
|
|
117
|
+
title: "Select session model: — choose provider:",
|
|
118
|
+
options: ["anthropic (2 models)", "openai (2 models)", "(cancel)"],
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
title: "Select session model: — openai:",
|
|
122
|
+
options: ["gpt-5.3-mini", "gpt-5.4", "(cancel)"],
|
|
123
|
+
},
|
|
124
|
+
]);
|
|
125
|
+
assert.deepEqual(applied, selectedModel);
|
|
126
|
+
assert.match(notices[0]!.message, /openai\/gpt-5\.4/);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("ambiguous typed model selection chooses provider first, then model", async () => {
|
|
130
|
+
const selectedModel = { provider: "github-copilot", id: "gpt-5" };
|
|
131
|
+
let applied: typeof selectedModel | null = null;
|
|
132
|
+
const selects: Array<{ title: string; options: string[] }> = [];
|
|
133
|
+
const notices: Array<{ message: string; type?: string }> = [];
|
|
134
|
+
|
|
135
|
+
const ctx = {
|
|
136
|
+
hasUI: true,
|
|
137
|
+
model: { provider: "anthropic", id: "claude-sonnet-4-6" },
|
|
138
|
+
modelRegistry: {
|
|
139
|
+
getAvailable: () => [
|
|
140
|
+
{ provider: "openai", id: "gpt-5" },
|
|
141
|
+
{ provider: "github-copilot", id: "gpt-5" },
|
|
142
|
+
{ provider: "openai", id: "gpt-5-mini" },
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
ui: {
|
|
146
|
+
select: async (title: string, options: string[]) => {
|
|
147
|
+
selects.push({ title, options });
|
|
148
|
+
return selects.length === 1 ? "github-copilot (1 model)" : "gpt-5";
|
|
149
|
+
},
|
|
150
|
+
notify: (message: string, type?: string) => {
|
|
151
|
+
notices.push({ message, type });
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
} as any;
|
|
155
|
+
|
|
156
|
+
const pi = {
|
|
157
|
+
setModel: async (model: typeof selectedModel) => {
|
|
158
|
+
applied = model;
|
|
159
|
+
return true;
|
|
160
|
+
},
|
|
161
|
+
} as any;
|
|
162
|
+
|
|
163
|
+
const handled = await handleCoreCommand("model gpt", ctx, pi);
|
|
164
|
+
assert.equal(handled, true);
|
|
165
|
+
assert.deepEqual(selects, [
|
|
166
|
+
{
|
|
167
|
+
title: "Multiple models match \"gpt\" — choose provider:",
|
|
168
|
+
options: ["github-copilot (1 model)", "openai (2 models)", "(cancel)"],
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
title: "Multiple models match \"gpt\" — github-copilot:",
|
|
172
|
+
options: ["gpt-5", "(cancel)"],
|
|
173
|
+
},
|
|
174
|
+
]);
|
|
175
|
+
assert.deepEqual(applied, selectedModel);
|
|
176
|
+
assert.match(notices[0]!.message, /github-copilot\/gpt-5/);
|
|
177
|
+
});
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
summarizeLogs,
|
|
19
19
|
formatForNotification,
|
|
20
20
|
setLogBasePath,
|
|
21
|
+
setStderrLoggingEnabled,
|
|
21
22
|
_resetLogs,
|
|
22
23
|
} from "../workflow-logger.ts";
|
|
23
24
|
|
|
@@ -375,5 +376,20 @@ describe("workflow-logger", () => {
|
|
|
375
376
|
logError("tool", "failed", { cmd: "complete_task" });
|
|
376
377
|
assert.ok(written[0].includes('"cmd":"complete_task"'));
|
|
377
378
|
});
|
|
379
|
+
|
|
380
|
+
test("suppresses stderr when disabled", (t) => {
|
|
381
|
+
const written: string[] = [];
|
|
382
|
+
const orig = process.stderr.write.bind(process.stderr);
|
|
383
|
+
const previous = setStderrLoggingEnabled(false);
|
|
384
|
+
// @ts-ignore — patching for test
|
|
385
|
+
process.stderr.write = (chunk: string) => { written.push(chunk); return true; };
|
|
386
|
+
t.after(() => {
|
|
387
|
+
process.stderr.write = orig;
|
|
388
|
+
setStderrLoggingEnabled(previous);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
logWarning("engine", "hidden warning");
|
|
392
|
+
assert.deepEqual(written, []);
|
|
393
|
+
});
|
|
378
394
|
});
|
|
379
395
|
});
|
|
@@ -67,6 +67,7 @@ export interface LogEntry {
|
|
|
67
67
|
const MAX_BUFFER = 100;
|
|
68
68
|
let _buffer: LogEntry[] = [];
|
|
69
69
|
let _auditBasePath: string | null = null;
|
|
70
|
+
let _stderrEnabled = true;
|
|
70
71
|
|
|
71
72
|
/**
|
|
72
73
|
* Set the base path for persistent audit log writes.
|
|
@@ -77,6 +78,16 @@ export function setLogBasePath(basePath: string): void {
|
|
|
77
78
|
_auditBasePath = basePath;
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Enable or disable immediate stderr writes for workflow logs.
|
|
83
|
+
* Returns the previous setting so callers can restore it.
|
|
84
|
+
*/
|
|
85
|
+
export function setStderrLoggingEnabled(enabled: boolean): boolean {
|
|
86
|
+
const previous = _stderrEnabled;
|
|
87
|
+
_stderrEnabled = enabled;
|
|
88
|
+
return previous;
|
|
89
|
+
}
|
|
90
|
+
|
|
80
91
|
// ─── Public API ─────────────────────────────────────────────────────────
|
|
81
92
|
|
|
82
93
|
/**
|
|
@@ -245,7 +256,7 @@ function _push(
|
|
|
245
256
|
// Always forward to stderr so terminal watchers see it (see module header for policy)
|
|
246
257
|
const prefix = severity === "error" ? "ERROR" : "WARN";
|
|
247
258
|
const ctxStr = context ? ` ${JSON.stringify(context)}` : "";
|
|
248
|
-
|
|
259
|
+
_writeStderr(`[gsd:${component}] ${prefix}: ${message}${ctxStr}\n`);
|
|
249
260
|
|
|
250
261
|
// Persist to notification store (both warnings and errors)
|
|
251
262
|
try {
|
|
@@ -255,7 +266,7 @@ function _push(
|
|
|
255
266
|
"workflow-logger",
|
|
256
267
|
);
|
|
257
268
|
} catch (notifErr) {
|
|
258
|
-
|
|
269
|
+
_writeStderr(`[gsd:workflow-logger] notification-store append failed: ${(notifErr as Error).message}\n`);
|
|
259
270
|
}
|
|
260
271
|
|
|
261
272
|
// Buffer for auto-loop to drain
|
|
@@ -275,11 +286,16 @@ function _push(
|
|
|
275
286
|
appendFileSync(join(auditDir, "audit-log.jsonl"), JSON.stringify(sanitized) + "\n", "utf-8");
|
|
276
287
|
} catch (auditErr) {
|
|
277
288
|
// Best-effort — never let audit write failures bubble up
|
|
278
|
-
|
|
289
|
+
_writeStderr(`[gsd:audit] failed to persist log entry: ${(auditErr as Error).message}\n`);
|
|
279
290
|
}
|
|
280
291
|
}
|
|
281
292
|
}
|
|
282
293
|
|
|
294
|
+
function _writeStderr(message: string): void {
|
|
295
|
+
if (!_stderrEnabled) return;
|
|
296
|
+
process.stderr.write(message);
|
|
297
|
+
}
|
|
298
|
+
|
|
283
299
|
/**
|
|
284
300
|
* Sanitize a log entry before persisting to the audit JSONL file.
|
|
285
301
|
* Strips potentially sensitive context (raw paths, cwd, full error text)
|
|
File without changes
|
|
File without changes
|