@wingman-ai/gateway 0.2.5 → 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/.wingman/agents/coding/agent.md +5 -0
- package/.wingman/agents/coding-v2/agent.md +58 -0
- package/.wingman/agents/game-dev/agent.md +94 -0
- package/.wingman/agents/game-dev/art-generation.md +37 -0
- package/.wingman/agents/game-dev/asset-refinement.md +17 -0
- package/.wingman/agents/game-dev/planning-idea.md +17 -0
- package/.wingman/agents/game-dev/ui-specialist.md +17 -0
- package/.wingman/agents/main/agent.md +2 -0
- package/README.md +1 -0
- package/dist/agent/config/agentConfig.d.ts +4 -0
- package/dist/agent/config/mcpClientManager.cjs +44 -10
- package/dist/agent/config/mcpClientManager.d.ts +6 -2
- package/dist/agent/config/mcpClientManager.js +44 -10
- package/dist/agent/config/toolRegistry.cjs +3 -1
- package/dist/agent/config/toolRegistry.js +3 -1
- package/dist/agent/tests/mcpClientManager.test.cjs +124 -0
- package/dist/agent/tests/mcpClientManager.test.d.ts +1 -0
- package/dist/agent/tests/mcpClientManager.test.js +118 -0
- package/dist/agent/tools/command_execute.cjs +1 -1
- package/dist/agent/tools/command_execute.js +1 -1
- package/dist/cli/config/schema.d.ts +2 -0
- package/dist/cli/core/agentInvoker.cjs +55 -66
- package/dist/cli/core/agentInvoker.d.ts +10 -13
- package/dist/cli/core/agentInvoker.js +42 -62
- package/dist/cli/core/imagePersistence.cjs +125 -0
- package/dist/cli/core/imagePersistence.d.ts +24 -0
- package/dist/cli/core/imagePersistence.js +85 -0
- package/dist/cli/core/sessionManager.cjs +297 -40
- package/dist/cli/core/sessionManager.d.ts +9 -0
- package/dist/cli/core/sessionManager.js +297 -40
- package/dist/debug/terminalProbe.cjs +57 -0
- package/dist/debug/terminalProbe.d.ts +10 -0
- package/dist/debug/terminalProbe.js +20 -0
- package/dist/debug/terminalProbeAuth.cjs +140 -0
- package/dist/debug/terminalProbeAuth.d.ts +20 -0
- package/dist/debug/terminalProbeAuth.js +97 -0
- package/dist/gateway/http/fs.cjs +19 -0
- package/dist/gateway/http/fs.js +19 -0
- package/dist/gateway/http/sessions.cjs +25 -5
- package/dist/gateway/http/sessions.js +25 -5
- package/dist/gateway/server.cjs +112 -11
- package/dist/gateway/server.d.ts +2 -0
- package/dist/gateway/server.js +112 -11
- package/dist/tests/agentInvokerSummarization.test.cjs +56 -37
- package/dist/tests/agentInvokerSummarization.test.js +58 -39
- package/dist/tests/agentInvokerWorkdir.test.cjs +50 -0
- package/dist/tests/agentInvokerWorkdir.test.js +52 -2
- package/dist/tests/cli-init.test.cjs +36 -0
- package/dist/tests/cli-init.test.js +36 -0
- package/dist/tests/falRuntime.test.cjs +78 -0
- package/dist/tests/falRuntime.test.d.ts +1 -0
- package/dist/tests/falRuntime.test.js +72 -0
- package/dist/tests/falSummary.test.cjs +51 -0
- package/dist/tests/falSummary.test.d.ts +1 -0
- package/dist/tests/falSummary.test.js +45 -0
- package/dist/tests/gateway.test.cjs +109 -1
- package/dist/tests/gateway.test.js +109 -1
- package/dist/tests/imagePersistence.test.cjs +143 -0
- package/dist/tests/imagePersistence.test.d.ts +1 -0
- package/dist/tests/imagePersistence.test.js +137 -0
- package/dist/tests/sessionMessageAttachments.test.cjs +30 -0
- package/dist/tests/sessionMessageAttachments.test.js +30 -0
- package/dist/tests/sessionStateMessages.test.cjs +126 -0
- package/dist/tests/sessionStateMessages.test.js +126 -0
- package/dist/tests/sessions-api.test.cjs +117 -3
- package/dist/tests/sessions-api.test.js +118 -4
- package/dist/tests/terminalProbe.test.cjs +45 -0
- package/dist/tests/terminalProbe.test.d.ts +1 -0
- package/dist/tests/terminalProbe.test.js +39 -0
- package/dist/tests/terminalProbeAuth.test.cjs +85 -0
- package/dist/tests/terminalProbeAuth.test.d.ts +1 -0
- package/dist/tests/terminalProbeAuth.test.js +79 -0
- package/dist/tools/fal/runtime.cjs +103 -0
- package/dist/tools/fal/runtime.d.ts +10 -0
- package/dist/tools/fal/runtime.js +60 -0
- package/dist/tools/fal/summary.cjs +78 -0
- package/dist/tools/fal/summary.d.ts +22 -0
- package/dist/tools/fal/summary.js +41 -0
- package/dist/tools/mcp-fal-ai.cjs +1041 -0
- package/dist/tools/mcp-fal-ai.d.ts +1 -0
- package/dist/tools/mcp-fal-ai.js +1025 -0
- package/dist/types/mcp.cjs +2 -0
- package/dist/types/mcp.d.ts +8 -0
- package/dist/types/mcp.js +3 -1
- package/dist/webui/assets/index-0nUBsUUq.js +278 -0
- package/dist/webui/assets/index-kk7OrD-G.css +11 -0
- package/dist/webui/index.html +2 -2
- package/package.json +11 -8
- package/dist/webui/assets/index-C7EuTbnE.js +0 -270
- package/dist/webui/assets/index-DVWQluit.css +0 -11
package/dist/gateway/server.js
CHANGED
|
@@ -336,12 +336,29 @@ class GatewayServer {
|
|
|
336
336
|
const sessionManager = await this.getSessionManager(agentId);
|
|
337
337
|
const existingSession = sessionManager.getSession(sessionKey);
|
|
338
338
|
const session = existingSession || sessionManager.getOrCreateSession(sessionKey, agentId);
|
|
339
|
+
const requestId = msg.id || `req-${Date.now()}`;
|
|
339
340
|
const workdir = session.metadata?.workdir ?? null;
|
|
340
341
|
const defaultOutputDir = this.resolveDefaultOutputDir(agentId);
|
|
341
342
|
const preview = hasContent ? content.trim() : buildAttachmentPreview(attachments);
|
|
342
343
|
sessionManager.updateSession(session.id, {
|
|
344
|
+
messageCount: (session.messageCount ?? 0) + 1,
|
|
343
345
|
lastMessagePreview: preview.substring(0, 200)
|
|
344
346
|
});
|
|
347
|
+
try {
|
|
348
|
+
sessionManager.persistPendingMessage({
|
|
349
|
+
sessionId: sessionKey,
|
|
350
|
+
requestId,
|
|
351
|
+
message: {
|
|
352
|
+
id: `user-${requestId}`,
|
|
353
|
+
role: "user",
|
|
354
|
+
content,
|
|
355
|
+
attachments: attachments.length > 0 ? mapAttachmentsForPendingMessage(attachments) : void 0,
|
|
356
|
+
createdAt: Date.now()
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
} catch (error) {
|
|
360
|
+
this.logger.warn("Failed to persist pending user message", error);
|
|
361
|
+
}
|
|
345
362
|
if (!existingSession) this.internalHooks?.emit({
|
|
346
363
|
type: "session",
|
|
347
364
|
action: "start",
|
|
@@ -444,9 +461,15 @@ class GatewayServer {
|
|
|
444
461
|
this.activeSessionRequests.set(sessionQueueKey, msg.id);
|
|
445
462
|
const outputManager = new OutputManager("interactive");
|
|
446
463
|
let emittedAgentError = false;
|
|
464
|
+
let streamedCompletionResult;
|
|
447
465
|
const outputHandler = (event)=>{
|
|
448
466
|
const payloadWithSession = this.attachSessionContext(event, sessionKey, agentId);
|
|
449
|
-
|
|
467
|
+
const payloadType = payloadWithSession && "object" == typeof payloadWithSession && !Array.isArray(payloadWithSession) && "string" == typeof payloadWithSession.type ? payloadWithSession.type : "";
|
|
468
|
+
if ("agent-complete" === payloadType) {
|
|
469
|
+
if (payloadWithSession && "object" == typeof payloadWithSession && !Array.isArray(payloadWithSession)) streamedCompletionResult = payloadWithSession.result;
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if ("agent-error" === payloadType) emittedAgentError = true;
|
|
450
473
|
const baseMessage = {
|
|
451
474
|
type: "event:agent",
|
|
452
475
|
id: msg.id,
|
|
@@ -477,17 +500,43 @@ class GatewayServer {
|
|
|
477
500
|
abortController
|
|
478
501
|
});
|
|
479
502
|
try {
|
|
480
|
-
await invoker.invokeAgent(agentId, content, sessionKey, attachments, {
|
|
503
|
+
const invocationResult = await invoker.invokeAgent(agentId, content, sessionKey, attachments, {
|
|
481
504
|
signal: abortController.signal
|
|
482
505
|
});
|
|
483
|
-
|
|
484
|
-
if (
|
|
485
|
-
|
|
506
|
+
if (msg.id) sessionManager.clearPendingMessagesForRequest(sessionKey, msg.id);
|
|
507
|
+
if (emittedAgentError) return;
|
|
508
|
+
const invocationCancelled = abortController.signal.aborted || "object" == typeof invocationResult && null !== invocationResult && !Array.isArray(invocationResult) && true === invocationResult.cancelled;
|
|
509
|
+
if (invocationCancelled) return void this.sendAgentError(ws, msg.id, "Request cancelled", {
|
|
510
|
+
sessionId: sessionKey,
|
|
511
|
+
agentId,
|
|
512
|
+
broadcastToSession: true,
|
|
513
|
+
exclude: ws
|
|
514
|
+
});
|
|
515
|
+
const completionResult = void 0 === streamedCompletionResult ? invocationResult : streamedCompletionResult;
|
|
516
|
+
this.sendAgentComplete(ws, msg.id, completionResult, {
|
|
517
|
+
sessionId: sessionKey,
|
|
518
|
+
agentId,
|
|
519
|
+
broadcastToSession: true,
|
|
520
|
+
exclude: ws
|
|
486
521
|
});
|
|
487
522
|
} catch (error) {
|
|
488
523
|
this.logger.error("Agent invocation failed", error);
|
|
524
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
525
|
+
if (msg.id) try {
|
|
526
|
+
sessionManager.persistPendingMessage({
|
|
527
|
+
sessionId: sessionKey,
|
|
528
|
+
requestId: msg.id,
|
|
529
|
+
message: {
|
|
530
|
+
id: msg.id,
|
|
531
|
+
role: "assistant",
|
|
532
|
+
content: message,
|
|
533
|
+
createdAt: Date.now()
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
} catch (persistError) {
|
|
537
|
+
this.logger.warn("Failed to persist pending assistant error message", persistError);
|
|
538
|
+
}
|
|
489
539
|
if (!emittedAgentError) {
|
|
490
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
491
540
|
const stack = error instanceof Error ? error.stack : void 0;
|
|
492
541
|
this.sendAgentError(ws, msg.id, message, {
|
|
493
542
|
sessionId: sessionKey,
|
|
@@ -563,6 +612,12 @@ class GatewayServer {
|
|
|
563
612
|
},
|
|
564
613
|
timestamp: Date.now()
|
|
565
614
|
});
|
|
615
|
+
this.sendAgentError(ws, requestId, "Request cancelled", {
|
|
616
|
+
sessionId: queued.sessionKey,
|
|
617
|
+
agentId: queued.agentId,
|
|
618
|
+
broadcastToSession: true,
|
|
619
|
+
exclude: ws
|
|
620
|
+
});
|
|
566
621
|
return;
|
|
567
622
|
}
|
|
568
623
|
this.sendMessage(ws, {
|
|
@@ -737,11 +792,25 @@ class GatewayServer {
|
|
|
737
792
|
}
|
|
738
793
|
sendMessage(ws, message) {
|
|
739
794
|
try {
|
|
740
|
-
ws.send(JSON.stringify(message));
|
|
795
|
+
const result = ws.send(JSON.stringify(message));
|
|
796
|
+
if ("number" == typeof result && result <= 0) return false;
|
|
797
|
+
return true;
|
|
741
798
|
} catch (error) {
|
|
742
799
|
this.log("error", "Failed to send message", error);
|
|
800
|
+
return false;
|
|
743
801
|
}
|
|
744
802
|
}
|
|
803
|
+
sendMessageWithRetry(ws, message, attempt = 0) {
|
|
804
|
+
if (this.sendMessage(ws, message)) return;
|
|
805
|
+
if (attempt >= 2) return void this.log("warn", "Dropping websocket message after retry attempts", {
|
|
806
|
+
type: message.type,
|
|
807
|
+
id: message.id
|
|
808
|
+
});
|
|
809
|
+
const delayMs = 25 * (attempt + 1);
|
|
810
|
+
setTimeout(()=>{
|
|
811
|
+
this.sendMessageWithRetry(ws, message, attempt + 1);
|
|
812
|
+
}, delayMs);
|
|
813
|
+
}
|
|
745
814
|
sendError(ws, code, message) {
|
|
746
815
|
const errorPayload = {
|
|
747
816
|
code,
|
|
@@ -753,6 +822,25 @@ class GatewayServer {
|
|
|
753
822
|
timestamp: Date.now()
|
|
754
823
|
});
|
|
755
824
|
}
|
|
825
|
+
sendAgentComplete(ws, requestId, result, options) {
|
|
826
|
+
let payload = {
|
|
827
|
+
type: "agent-complete",
|
|
828
|
+
result: result ?? null,
|
|
829
|
+
timestamp: new Date().toISOString()
|
|
830
|
+
};
|
|
831
|
+
if (options?.sessionId && options?.agentId) payload = this.attachSessionContext(payload, options.sessionId, options.agentId);
|
|
832
|
+
const baseMessage = {
|
|
833
|
+
type: "event:agent",
|
|
834
|
+
id: requestId,
|
|
835
|
+
payload,
|
|
836
|
+
timestamp: Date.now()
|
|
837
|
+
};
|
|
838
|
+
this.sendMessageWithRetry(ws, {
|
|
839
|
+
...baseMessage,
|
|
840
|
+
clientId: ws.data.clientId
|
|
841
|
+
});
|
|
842
|
+
if (options?.broadcastToSession && options.sessionId) this.broadcastSessionEvent(options.sessionId, baseMessage, options.exclude, true);
|
|
843
|
+
}
|
|
756
844
|
sendAgentError(ws, requestId, message, options) {
|
|
757
845
|
let payload = {
|
|
758
846
|
type: "agent-error",
|
|
@@ -767,11 +855,11 @@ class GatewayServer {
|
|
|
767
855
|
payload,
|
|
768
856
|
timestamp: Date.now()
|
|
769
857
|
};
|
|
770
|
-
this.
|
|
858
|
+
this.sendMessageWithRetry(ws, {
|
|
771
859
|
...baseMessage,
|
|
772
860
|
clientId: ws.data.clientId
|
|
773
861
|
});
|
|
774
|
-
if (options?.broadcastToSession && options.sessionId) this.broadcastSessionEvent(options.sessionId, baseMessage, options.exclude);
|
|
862
|
+
if (options?.broadcastToSession && options.sessionId) this.broadcastSessionEvent(options.sessionId, baseMessage, options.exclude, true);
|
|
775
863
|
}
|
|
776
864
|
cancelSocketAgentRequests(ws) {
|
|
777
865
|
for (const [requestId, active] of this.activeAgentRequests)if (active.socket === ws) {
|
|
@@ -800,12 +888,13 @@ class GatewayServer {
|
|
|
800
888
|
agentId
|
|
801
889
|
};
|
|
802
890
|
}
|
|
803
|
-
broadcastSessionEvent(sessionId, message, exclude) {
|
|
891
|
+
broadcastSessionEvent(sessionId, message, exclude, reliable = false) {
|
|
804
892
|
const subscribers = this.sessionSubscriptions.get(sessionId);
|
|
805
893
|
if (!subscribers || 0 === subscribers.size) return 0;
|
|
806
894
|
let sent = 0;
|
|
807
895
|
for (const ws of subscribers)if (!exclude || ws !== exclude) {
|
|
808
|
-
this.
|
|
896
|
+
if (reliable) this.sendMessageWithRetry(ws, message);
|
|
897
|
+
else this.sendMessage(ws, message);
|
|
809
898
|
sent++;
|
|
810
899
|
}
|
|
811
900
|
return sent;
|
|
@@ -1379,6 +1468,18 @@ function buildAttachmentPreview(attachments) {
|
|
|
1379
1468
|
if (hasAudio) return count > 1 ? "Audio attachments" : "Audio attachment";
|
|
1380
1469
|
return count > 1 ? "Image attachments" : "Image attachment";
|
|
1381
1470
|
}
|
|
1471
|
+
function mapAttachmentsForPendingMessage(attachments) {
|
|
1472
|
+
return attachments.map((attachment)=>{
|
|
1473
|
+
const kind = isFileAttachment(attachment) ? "file" : isAudioAttachment(attachment) ? "audio" : "image";
|
|
1474
|
+
return {
|
|
1475
|
+
kind,
|
|
1476
|
+
dataUrl: attachment.dataUrl,
|
|
1477
|
+
name: attachment.name,
|
|
1478
|
+
mimeType: attachment.mimeType,
|
|
1479
|
+
size: attachment.size
|
|
1480
|
+
};
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1382
1483
|
function isAudioAttachment(attachment) {
|
|
1383
1484
|
if ("audio" === attachment.kind) return true;
|
|
1384
1485
|
if (attachment.mimeType?.startsWith("audio/")) return true;
|
|
@@ -348,42 +348,6 @@ const parseConfig = (input)=>{
|
|
|
348
348
|
})).toBeUndefined();
|
|
349
349
|
});
|
|
350
350
|
});
|
|
351
|
-
(0, external_vitest_namespaceObject.describe)("evaluateStreamingCompletion", ()=>{
|
|
352
|
-
(0, external_vitest_namespaceObject.it)("blocks with stream_error when no assistant text and stream error exists", ()=>{
|
|
353
|
-
const result = (0, agentInvoker_cjs_namespaceObject.evaluateStreamingCompletion)({
|
|
354
|
-
sawAssistantText: false,
|
|
355
|
-
fallbackText: void 0,
|
|
356
|
-
streamErrorMessage: "provider timeout"
|
|
357
|
-
});
|
|
358
|
-
(0, external_vitest_namespaceObject.expect)(result).toEqual({
|
|
359
|
-
status: "blocked",
|
|
360
|
-
reason: "stream_error",
|
|
361
|
-
message: "Model call failed: provider timeout"
|
|
362
|
-
});
|
|
363
|
-
});
|
|
364
|
-
(0, external_vitest_namespaceObject.it)("blocks with empty_stream_response when no text or fallback is present", ()=>{
|
|
365
|
-
const result = (0, agentInvoker_cjs_namespaceObject.evaluateStreamingCompletion)({
|
|
366
|
-
sawAssistantText: false,
|
|
367
|
-
fallbackText: void 0,
|
|
368
|
-
streamErrorMessage: void 0
|
|
369
|
-
});
|
|
370
|
-
(0, external_vitest_namespaceObject.expect)(result).toEqual({
|
|
371
|
-
status: "blocked",
|
|
372
|
-
reason: "empty_stream_response",
|
|
373
|
-
message: "Model completed without a response. Check provider logs for request errors."
|
|
374
|
-
});
|
|
375
|
-
});
|
|
376
|
-
(0, external_vitest_namespaceObject.it)("returns ok when assistant text exists", ()=>{
|
|
377
|
-
const result = (0, agentInvoker_cjs_namespaceObject.evaluateStreamingCompletion)({
|
|
378
|
-
sawAssistantText: true,
|
|
379
|
-
fallbackText: void 0,
|
|
380
|
-
streamErrorMessage: void 0
|
|
381
|
-
});
|
|
382
|
-
(0, external_vitest_namespaceObject.expect)(result).toEqual({
|
|
383
|
-
status: "ok"
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
});
|
|
387
351
|
(0, external_vitest_namespaceObject.describe)("LangGraph lifecycle termination", ()=>{
|
|
388
352
|
(0, external_vitest_namespaceObject.it)("tracks root LangGraph run id from parentless on_chain_start", ()=>{
|
|
389
353
|
const rootRunId = (0, agentInvoker_cjs_namespaceObject.trackRootLangGraphRunId)(void 0, {
|
|
@@ -441,12 +405,67 @@ const parseConfig = (input)=>{
|
|
|
441
405
|
run_id: "root-run",
|
|
442
406
|
parent_ids: []
|
|
443
407
|
}, "root-run")).toBe(false);
|
|
408
|
+
});
|
|
409
|
+
(0, external_vitest_namespaceObject.it)("treats root LangGraph on_chain_end as terminal even without tracked run id", ()=>{
|
|
444
410
|
(0, external_vitest_namespaceObject.expect)((0, agentInvoker_cjs_namespaceObject.isRootLangGraphTerminalEvent)({
|
|
445
411
|
event: "on_chain_end",
|
|
446
412
|
name: "LangGraph",
|
|
447
413
|
run_id: "root-run",
|
|
448
414
|
parent_ids: []
|
|
449
|
-
}, void 0)).toBe(
|
|
415
|
+
}, void 0)).toBe(true);
|
|
416
|
+
});
|
|
417
|
+
(0, external_vitest_namespaceObject.it)("treats root LangGraph on_chain_end as terminal when run id is missing", ()=>{
|
|
418
|
+
(0, external_vitest_namespaceObject.expect)((0, agentInvoker_cjs_namespaceObject.isRootLangGraphTerminalEvent)({
|
|
419
|
+
event: "on_chain_end",
|
|
420
|
+
name: "LangGraph",
|
|
421
|
+
parent_ids: []
|
|
422
|
+
}, "root-run")).toBe(true);
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
(0, external_vitest_namespaceObject.describe)("emitCompletionAndContinuePostProcessing", ()=>{
|
|
426
|
+
(0, external_vitest_namespaceObject.it)("emits completion before post-processing resolves", ()=>{
|
|
427
|
+
const callOrder = [];
|
|
428
|
+
let resolvePostProcess;
|
|
429
|
+
const postProcess = ()=>new Promise((resolve)=>{
|
|
430
|
+
callOrder.push("post-process-start");
|
|
431
|
+
resolvePostProcess = resolve;
|
|
432
|
+
});
|
|
433
|
+
(0, agentInvoker_cjs_namespaceObject.emitCompletionAndContinuePostProcessing)({
|
|
434
|
+
outputManager: {
|
|
435
|
+
emitAgentComplete: ()=>{
|
|
436
|
+
callOrder.push("emit-complete");
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
result: {
|
|
440
|
+
ok: true
|
|
441
|
+
},
|
|
442
|
+
postProcess
|
|
443
|
+
});
|
|
444
|
+
(0, external_vitest_namespaceObject.expect)(callOrder).toEqual([
|
|
445
|
+
"emit-complete",
|
|
446
|
+
"post-process-start"
|
|
447
|
+
]);
|
|
448
|
+
resolvePostProcess?.();
|
|
449
|
+
});
|
|
450
|
+
(0, external_vitest_namespaceObject.it)("logs and swallows post-processing failures", async ()=>{
|
|
451
|
+
const debug = external_vitest_namespaceObject.vi.fn();
|
|
452
|
+
(0, agentInvoker_cjs_namespaceObject.emitCompletionAndContinuePostProcessing)({
|
|
453
|
+
outputManager: {
|
|
454
|
+
emitAgentComplete: external_vitest_namespaceObject.vi.fn()
|
|
455
|
+
},
|
|
456
|
+
result: {
|
|
457
|
+
ok: true
|
|
458
|
+
},
|
|
459
|
+
postProcess: async ()=>{
|
|
460
|
+
throw new Error("materialization failed");
|
|
461
|
+
},
|
|
462
|
+
logger: {
|
|
463
|
+
debug
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
await Promise.resolve();
|
|
467
|
+
await Promise.resolve();
|
|
468
|
+
(0, external_vitest_namespaceObject.expect)(debug).toHaveBeenCalledWith("Failed post-completion processing for streamed agent response", external_vitest_namespaceObject.expect.any(Error));
|
|
450
469
|
});
|
|
451
470
|
});
|
|
452
471
|
for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { validateConfig } from "../cli/config/schema.js";
|
|
3
|
-
import { chunkHasAssistantText, configureDeepAgentSummarizationMiddleware, detectStreamErrorMessage, detectToolEventContext,
|
|
3
|
+
import { chunkHasAssistantText, configureDeepAgentSummarizationMiddleware, detectStreamErrorMessage, detectToolEventContext, emitCompletionAndContinuePostProcessing, isRootLangGraphTerminalEvent, resolveHumanInTheLoopSettings, resolveModelRetryMiddlewareSettings, resolveSummarizationMiddlewareSettings, resolveToolRetryMiddlewareSettings, selectStreamingFallbackText, trackRootLangGraphRunId } from "../cli/core/agentInvoker.js";
|
|
4
4
|
const parseConfig = (input)=>{
|
|
5
5
|
const result = validateConfig(input);
|
|
6
6
|
if (!result.success || !result.data) throw new Error(result.error || "Expected config validation to succeed");
|
|
@@ -346,42 +346,6 @@ describe("detectStreamErrorMessage", ()=>{
|
|
|
346
346
|
})).toBeUndefined();
|
|
347
347
|
});
|
|
348
348
|
});
|
|
349
|
-
describe("evaluateStreamingCompletion", ()=>{
|
|
350
|
-
it("blocks with stream_error when no assistant text and stream error exists", ()=>{
|
|
351
|
-
const result = evaluateStreamingCompletion({
|
|
352
|
-
sawAssistantText: false,
|
|
353
|
-
fallbackText: void 0,
|
|
354
|
-
streamErrorMessage: "provider timeout"
|
|
355
|
-
});
|
|
356
|
-
expect(result).toEqual({
|
|
357
|
-
status: "blocked",
|
|
358
|
-
reason: "stream_error",
|
|
359
|
-
message: "Model call failed: provider timeout"
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
it("blocks with empty_stream_response when no text or fallback is present", ()=>{
|
|
363
|
-
const result = evaluateStreamingCompletion({
|
|
364
|
-
sawAssistantText: false,
|
|
365
|
-
fallbackText: void 0,
|
|
366
|
-
streamErrorMessage: void 0
|
|
367
|
-
});
|
|
368
|
-
expect(result).toEqual({
|
|
369
|
-
status: "blocked",
|
|
370
|
-
reason: "empty_stream_response",
|
|
371
|
-
message: "Model completed without a response. Check provider logs for request errors."
|
|
372
|
-
});
|
|
373
|
-
});
|
|
374
|
-
it("returns ok when assistant text exists", ()=>{
|
|
375
|
-
const result = evaluateStreamingCompletion({
|
|
376
|
-
sawAssistantText: true,
|
|
377
|
-
fallbackText: void 0,
|
|
378
|
-
streamErrorMessage: void 0
|
|
379
|
-
});
|
|
380
|
-
expect(result).toEqual({
|
|
381
|
-
status: "ok"
|
|
382
|
-
});
|
|
383
|
-
});
|
|
384
|
-
});
|
|
385
349
|
describe("LangGraph lifecycle termination", ()=>{
|
|
386
350
|
it("tracks root LangGraph run id from parentless on_chain_start", ()=>{
|
|
387
351
|
const rootRunId = trackRootLangGraphRunId(void 0, {
|
|
@@ -439,11 +403,66 @@ describe("LangGraph lifecycle termination", ()=>{
|
|
|
439
403
|
run_id: "root-run",
|
|
440
404
|
parent_ids: []
|
|
441
405
|
}, "root-run")).toBe(false);
|
|
406
|
+
});
|
|
407
|
+
it("treats root LangGraph on_chain_end as terminal even without tracked run id", ()=>{
|
|
442
408
|
expect(isRootLangGraphTerminalEvent({
|
|
443
409
|
event: "on_chain_end",
|
|
444
410
|
name: "LangGraph",
|
|
445
411
|
run_id: "root-run",
|
|
446
412
|
parent_ids: []
|
|
447
|
-
}, void 0)).toBe(
|
|
413
|
+
}, void 0)).toBe(true);
|
|
414
|
+
});
|
|
415
|
+
it("treats root LangGraph on_chain_end as terminal when run id is missing", ()=>{
|
|
416
|
+
expect(isRootLangGraphTerminalEvent({
|
|
417
|
+
event: "on_chain_end",
|
|
418
|
+
name: "LangGraph",
|
|
419
|
+
parent_ids: []
|
|
420
|
+
}, "root-run")).toBe(true);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
describe("emitCompletionAndContinuePostProcessing", ()=>{
|
|
424
|
+
it("emits completion before post-processing resolves", ()=>{
|
|
425
|
+
const callOrder = [];
|
|
426
|
+
let resolvePostProcess;
|
|
427
|
+
const postProcess = ()=>new Promise((resolve)=>{
|
|
428
|
+
callOrder.push("post-process-start");
|
|
429
|
+
resolvePostProcess = resolve;
|
|
430
|
+
});
|
|
431
|
+
emitCompletionAndContinuePostProcessing({
|
|
432
|
+
outputManager: {
|
|
433
|
+
emitAgentComplete: ()=>{
|
|
434
|
+
callOrder.push("emit-complete");
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
result: {
|
|
438
|
+
ok: true
|
|
439
|
+
},
|
|
440
|
+
postProcess
|
|
441
|
+
});
|
|
442
|
+
expect(callOrder).toEqual([
|
|
443
|
+
"emit-complete",
|
|
444
|
+
"post-process-start"
|
|
445
|
+
]);
|
|
446
|
+
resolvePostProcess?.();
|
|
447
|
+
});
|
|
448
|
+
it("logs and swallows post-processing failures", async ()=>{
|
|
449
|
+
const debug = vi.fn();
|
|
450
|
+
emitCompletionAndContinuePostProcessing({
|
|
451
|
+
outputManager: {
|
|
452
|
+
emitAgentComplete: vi.fn()
|
|
453
|
+
},
|
|
454
|
+
result: {
|
|
455
|
+
ok: true
|
|
456
|
+
},
|
|
457
|
+
postProcess: async ()=>{
|
|
458
|
+
throw new Error("materialization failed");
|
|
459
|
+
},
|
|
460
|
+
logger: {
|
|
461
|
+
debug
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
await Promise.resolve();
|
|
465
|
+
await Promise.resolve();
|
|
466
|
+
expect(debug).toHaveBeenCalledWith("Failed post-completion processing for streamed agent response", expect.any(Error));
|
|
448
467
|
});
|
|
449
468
|
});
|
|
@@ -21,6 +21,8 @@ var __webpack_require__ = {};
|
|
|
21
21
|
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
22
22
|
})();
|
|
23
23
|
var __webpack_exports__ = {};
|
|
24
|
+
const external_node_fs_namespaceObject = require("node:fs");
|
|
25
|
+
const external_node_os_namespaceObject = require("node:os");
|
|
24
26
|
const external_node_path_namespaceObject = require("node:path");
|
|
25
27
|
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
26
28
|
const external_vitest_namespaceObject = require("vitest");
|
|
@@ -83,6 +85,21 @@ const agentInvoker_cjs_namespaceObject = require("../cli/core/agentInvoker.cjs")
|
|
|
83
85
|
(0, external_vitest_namespaceObject.expect)((0, agentInvoker_cjs_namespaceObject.resolveExecutionWorkspace)(workspace, absolute)).toBe(external_node_path_default().normalize(absolute));
|
|
84
86
|
});
|
|
85
87
|
});
|
|
88
|
+
(0, external_vitest_namespaceObject.describe)("resolveAgentExecutionWorkspace", ()=>{
|
|
89
|
+
const workspace = external_node_path_default().resolve("workspace");
|
|
90
|
+
(0, external_vitest_namespaceObject.it)("prefers explicit workdir over default output dir", ()=>{
|
|
91
|
+
const workdir = external_node_path_default().resolve("outside", "session-output");
|
|
92
|
+
const defaultOutputDir = external_node_path_default().resolve("outside", "default-output");
|
|
93
|
+
(0, external_vitest_namespaceObject.expect)((0, agentInvoker_cjs_namespaceObject.resolveAgentExecutionWorkspace)(workspace, workdir, defaultOutputDir)).toBe(external_node_path_default().normalize(workdir));
|
|
94
|
+
});
|
|
95
|
+
(0, external_vitest_namespaceObject.it)("uses default output dir when session workdir is unset", ()=>{
|
|
96
|
+
const defaultOutputDir = external_node_path_default().resolve("outside", "default-output");
|
|
97
|
+
(0, external_vitest_namespaceObject.expect)((0, agentInvoker_cjs_namespaceObject.resolveAgentExecutionWorkspace)(workspace, null, defaultOutputDir)).toBe(external_node_path_default().normalize(defaultOutputDir));
|
|
98
|
+
});
|
|
99
|
+
(0, external_vitest_namespaceObject.it)("falls back to workspace when neither workdir nor default output dir exist", ()=>{
|
|
100
|
+
(0, external_vitest_namespaceObject.expect)((0, agentInvoker_cjs_namespaceObject.resolveAgentExecutionWorkspace)(workspace, null, null)).toBe(external_node_path_default().normalize(workspace));
|
|
101
|
+
});
|
|
102
|
+
});
|
|
86
103
|
(0, external_vitest_namespaceObject.describe)("toWorkspaceAliasVirtualPath", ()=>{
|
|
87
104
|
(0, external_vitest_namespaceObject.it)("builds an alias path for absolute workspaces", ()=>{
|
|
88
105
|
const absolute = external_node_path_default().resolve("outside", "session-output");
|
|
@@ -94,6 +111,39 @@ const agentInvoker_cjs_namespaceObject = require("../cli/core/agentInvoker.cjs")
|
|
|
94
111
|
(0, external_vitest_namespaceObject.expect)((0, agentInvoker_cjs_namespaceObject.toWorkspaceAliasVirtualPath)("relative/workspace")).toBeNull();
|
|
95
112
|
});
|
|
96
113
|
});
|
|
114
|
+
(0, external_vitest_namespaceObject.describe)("resolveAgentMemorySources", ()=>{
|
|
115
|
+
const tempDirs = [];
|
|
116
|
+
(0, external_vitest_namespaceObject.afterEach)(()=>{
|
|
117
|
+
for (const dir of tempDirs)(0, external_node_fs_namespaceObject.rmSync)(dir, {
|
|
118
|
+
recursive: true,
|
|
119
|
+
force: true
|
|
120
|
+
});
|
|
121
|
+
tempDirs.length = 0;
|
|
122
|
+
});
|
|
123
|
+
(0, external_vitest_namespaceObject.it)("returns /AGENTS.md when present in execution workspace", ()=>{
|
|
124
|
+
const workspace = (0, external_node_fs_namespaceObject.mkdtempSync)(external_node_path_default().join((0, external_node_os_namespaceObject.tmpdir)(), "wingman-memory-"));
|
|
125
|
+
tempDirs.push(workspace);
|
|
126
|
+
(0, external_node_fs_namespaceObject.writeFileSync)(external_node_path_default().join(workspace, "AGENTS.md"), "# Agent Memory");
|
|
127
|
+
(0, external_vitest_namespaceObject.expect)((0, agentInvoker_cjs_namespaceObject.resolveAgentMemorySources)(workspace)).toEqual([
|
|
128
|
+
"/AGENTS.md"
|
|
129
|
+
]);
|
|
130
|
+
});
|
|
131
|
+
(0, external_vitest_namespaceObject.it)("returns no sources when AGENTS.md is absent", ()=>{
|
|
132
|
+
const workspace = (0, external_node_fs_namespaceObject.mkdtempSync)(external_node_path_default().join((0, external_node_os_namespaceObject.tmpdir)(), "wingman-memory-"));
|
|
133
|
+
tempDirs.push(workspace);
|
|
134
|
+
(0, external_vitest_namespaceObject.expect)((0, agentInvoker_cjs_namespaceObject.resolveAgentMemorySources)(workspace)).toEqual([]);
|
|
135
|
+
});
|
|
136
|
+
(0, external_vitest_namespaceObject.it)("ignores .deepagents/AGENTS.md when top-level AGENTS.md is missing", ()=>{
|
|
137
|
+
const workspace = (0, external_node_fs_namespaceObject.mkdtempSync)(external_node_path_default().join((0, external_node_os_namespaceObject.tmpdir)(), "wingman-memory-"));
|
|
138
|
+
tempDirs.push(workspace);
|
|
139
|
+
const deepagentsDir = external_node_path_default().join(workspace, ".deepagents");
|
|
140
|
+
(0, external_node_fs_namespaceObject.mkdirSync)(deepagentsDir, {
|
|
141
|
+
recursive: true
|
|
142
|
+
});
|
|
143
|
+
(0, external_node_fs_namespaceObject.writeFileSync)(external_node_path_default().join(deepagentsDir, "AGENTS.md"), "# Nested Memory");
|
|
144
|
+
(0, external_vitest_namespaceObject.expect)((0, agentInvoker_cjs_namespaceObject.resolveAgentMemorySources)(workspace)).toEqual([]);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
97
147
|
for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
98
148
|
Object.defineProperty(exports, '__esModule', {
|
|
99
149
|
value: true
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
1
3
|
import node_path from "node:path";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { OUTPUT_VIRTUAL_PATH, WORKDIR_VIRTUAL_PATH, resolveExecutionWorkspace, resolveExternalOutputMount, toWorkspaceAliasVirtualPath } from "../cli/core/agentInvoker.js";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { OUTPUT_VIRTUAL_PATH, WORKDIR_VIRTUAL_PATH, resolveAgentExecutionWorkspace, resolveAgentMemorySources, resolveExecutionWorkspace, resolveExternalOutputMount, toWorkspaceAliasVirtualPath } from "../cli/core/agentInvoker.js";
|
|
4
6
|
describe("resolveExternalOutputMount", ()=>{
|
|
5
7
|
const workspace = node_path.resolve("workspace");
|
|
6
8
|
it("mounts external workdir paths", ()=>{
|
|
@@ -59,6 +61,21 @@ describe("resolveExecutionWorkspace", ()=>{
|
|
|
59
61
|
expect(resolveExecutionWorkspace(workspace, absolute)).toBe(node_path.normalize(absolute));
|
|
60
62
|
});
|
|
61
63
|
});
|
|
64
|
+
describe("resolveAgentExecutionWorkspace", ()=>{
|
|
65
|
+
const workspace = node_path.resolve("workspace");
|
|
66
|
+
it("prefers explicit workdir over default output dir", ()=>{
|
|
67
|
+
const workdir = node_path.resolve("outside", "session-output");
|
|
68
|
+
const defaultOutputDir = node_path.resolve("outside", "default-output");
|
|
69
|
+
expect(resolveAgentExecutionWorkspace(workspace, workdir, defaultOutputDir)).toBe(node_path.normalize(workdir));
|
|
70
|
+
});
|
|
71
|
+
it("uses default output dir when session workdir is unset", ()=>{
|
|
72
|
+
const defaultOutputDir = node_path.resolve("outside", "default-output");
|
|
73
|
+
expect(resolveAgentExecutionWorkspace(workspace, null, defaultOutputDir)).toBe(node_path.normalize(defaultOutputDir));
|
|
74
|
+
});
|
|
75
|
+
it("falls back to workspace when neither workdir nor default output dir exist", ()=>{
|
|
76
|
+
expect(resolveAgentExecutionWorkspace(workspace, null, null)).toBe(node_path.normalize(workspace));
|
|
77
|
+
});
|
|
78
|
+
});
|
|
62
79
|
describe("toWorkspaceAliasVirtualPath", ()=>{
|
|
63
80
|
it("builds an alias path for absolute workspaces", ()=>{
|
|
64
81
|
const absolute = node_path.resolve("outside", "session-output");
|
|
@@ -70,3 +87,36 @@ describe("toWorkspaceAliasVirtualPath", ()=>{
|
|
|
70
87
|
expect(toWorkspaceAliasVirtualPath("relative/workspace")).toBeNull();
|
|
71
88
|
});
|
|
72
89
|
});
|
|
90
|
+
describe("resolveAgentMemorySources", ()=>{
|
|
91
|
+
const tempDirs = [];
|
|
92
|
+
afterEach(()=>{
|
|
93
|
+
for (const dir of tempDirs)rmSync(dir, {
|
|
94
|
+
recursive: true,
|
|
95
|
+
force: true
|
|
96
|
+
});
|
|
97
|
+
tempDirs.length = 0;
|
|
98
|
+
});
|
|
99
|
+
it("returns /AGENTS.md when present in execution workspace", ()=>{
|
|
100
|
+
const workspace = mkdtempSync(node_path.join(tmpdir(), "wingman-memory-"));
|
|
101
|
+
tempDirs.push(workspace);
|
|
102
|
+
writeFileSync(node_path.join(workspace, "AGENTS.md"), "# Agent Memory");
|
|
103
|
+
expect(resolveAgentMemorySources(workspace)).toEqual([
|
|
104
|
+
"/AGENTS.md"
|
|
105
|
+
]);
|
|
106
|
+
});
|
|
107
|
+
it("returns no sources when AGENTS.md is absent", ()=>{
|
|
108
|
+
const workspace = mkdtempSync(node_path.join(tmpdir(), "wingman-memory-"));
|
|
109
|
+
tempDirs.push(workspace);
|
|
110
|
+
expect(resolveAgentMemorySources(workspace)).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
it("ignores .deepagents/AGENTS.md when top-level AGENTS.md is missing", ()=>{
|
|
113
|
+
const workspace = mkdtempSync(node_path.join(tmpdir(), "wingman-memory-"));
|
|
114
|
+
tempDirs.push(workspace);
|
|
115
|
+
const deepagentsDir = node_path.join(workspace, ".deepagents");
|
|
116
|
+
mkdirSync(deepagentsDir, {
|
|
117
|
+
recursive: true
|
|
118
|
+
});
|
|
119
|
+
writeFileSync(node_path.join(deepagentsDir, "AGENTS.md"), "# Nested Memory");
|
|
120
|
+
expect(resolveAgentMemorySources(workspace)).toEqual([]);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -43,6 +43,7 @@ const init_cjs_namespaceObject = require("../cli/commands/init.cjs");
|
|
|
43
43
|
(0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(codingAgentPath)).toBe(true);
|
|
44
44
|
const codingPrompt = (0, external_node_fs_namespaceObject.readFileSync)(codingAgentPath, "utf-8");
|
|
45
45
|
(0, external_vitest_namespaceObject.expect)(codingPrompt).toContain("write_todos");
|
|
46
|
+
(0, external_vitest_namespaceObject.expect)(codingPrompt).toContain("read_todos");
|
|
46
47
|
(0, external_vitest_namespaceObject.expect)(codingPrompt).not.toContain("update_plan");
|
|
47
48
|
(0, external_vitest_namespaceObject.expect)(codingPrompt).not.toContain("subAgents:");
|
|
48
49
|
(0, external_vitest_namespaceObject.expect)(codingPrompt).toContain("Do not delegate coding work to subagents");
|
|
@@ -55,6 +56,7 @@ const init_cjs_namespaceObject = require("../cli/commands/init.cjs");
|
|
|
55
56
|
(0, external_vitest_namespaceObject.expect)(codingV2Prompt).toContain("promptFile: ./implementor.md");
|
|
56
57
|
(0, external_vitest_namespaceObject.expect)(codingV2Prompt).toContain("`task` tool");
|
|
57
58
|
(0, external_vitest_namespaceObject.expect)(codingV2Prompt).toContain("write_todos");
|
|
59
|
+
(0, external_vitest_namespaceObject.expect)(codingV2Prompt).toContain("read_todos");
|
|
58
60
|
const codingV2ImplementorPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "coding-v2", "implementor.md");
|
|
59
61
|
const codingV2PlannerPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "coding-v2", "planner.md");
|
|
60
62
|
const codingV2ReviewerPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "coding-v2", "reviewer.md");
|
|
@@ -63,6 +65,40 @@ const init_cjs_namespaceObject = require("../cli/commands/init.cjs");
|
|
|
63
65
|
(0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(codingV2PlannerPath)).toBe(false);
|
|
64
66
|
(0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(codingV2ReviewerPath)).toBe(false);
|
|
65
67
|
(0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(codingV2ResearcherPath)).toBe(false);
|
|
68
|
+
const gameDevAgentPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "game-dev", "agent.md");
|
|
69
|
+
(0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(gameDevAgentPath)).toBe(true);
|
|
70
|
+
const gameDevPrompt = (0, external_node_fs_namespaceObject.readFileSync)(gameDevAgentPath, "utf-8");
|
|
71
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: game-dev");
|
|
72
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("subAgents:");
|
|
73
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: art-generation");
|
|
74
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("promptFile: ./art-generation.md");
|
|
75
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: asset-refinement");
|
|
76
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("promptFile: ./asset-refinement.md");
|
|
77
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: planning-idea");
|
|
78
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("promptFile: ./planning-idea.md");
|
|
79
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("name: ui-specialist");
|
|
80
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("promptFile: ./ui-specialist.md");
|
|
81
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("write_todos");
|
|
82
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("read_todos");
|
|
83
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("UV-aware texture planning");
|
|
84
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("MeshStandardMaterial");
|
|
85
|
+
(0, external_vitest_namespaceObject.expect)(gameDevPrompt).toContain("uv`/`uv2");
|
|
86
|
+
const gameDevArtGenerationPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "game-dev", "art-generation.md");
|
|
87
|
+
const gameDevAssetRefinementPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "game-dev", "asset-refinement.md");
|
|
88
|
+
const gameDevPlanningIdeaPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "game-dev", "planning-idea.md");
|
|
89
|
+
const gameDevUiSpecialistPath = (0, external_node_path_namespaceObject.join)(workspace, ".wingman", "agents", "game-dev", "ui-specialist.md");
|
|
90
|
+
(0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(gameDevArtGenerationPath)).toBe(true);
|
|
91
|
+
(0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(gameDevAssetRefinementPath)).toBe(true);
|
|
92
|
+
(0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(gameDevPlanningIdeaPath)).toBe(true);
|
|
93
|
+
(0, external_vitest_namespaceObject.expect)((0, external_node_fs_namespaceObject.existsSync)(gameDevUiSpecialistPath)).toBe(true);
|
|
94
|
+
const gameDevArtGenerationPrompt = (0, external_node_fs_namespaceObject.readFileSync)(gameDevArtGenerationPath, "utf-8");
|
|
95
|
+
(0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("UV set(s) or UDIM tiles");
|
|
96
|
+
(0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("texel density targets");
|
|
97
|
+
(0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("Texture-to-geometry mapping notes");
|
|
98
|
+
(0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("material slot");
|
|
99
|
+
(0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("need `uv`, and `aoMap`");
|
|
100
|
+
(0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("flipY = false");
|
|
101
|
+
(0, external_vitest_namespaceObject.expect)(gameDevArtGenerationPrompt).toContain("RepeatWrapping");
|
|
66
102
|
});
|
|
67
103
|
(0, external_vitest_namespaceObject.it)("merges existing config when --merge is set", async ()=>{
|
|
68
104
|
const configDir = (0, external_node_path_namespaceObject.join)(workspace, ".wingman");
|