create-windy 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/dist/cli.js +292 -125
  2. package/package.json +1 -1
  3. package/template/.windy-template.json +2 -2
  4. package/template/apps/agent-server/adk_web_agents/canonical_debug/agent.py +29 -9
  5. package/template/apps/agent-server/adk_web_agents/services.py +25 -0
  6. package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +40 -11
  7. package/template/apps/agent-server/src/southwind_agent_server/adk_debug_sessions.py +325 -0
  8. package/template/apps/agent-server/src/southwind_agent_server/adk_model.py +70 -6
  9. package/template/apps/agent-server/src/southwind_agent_server/adk_tools.py +1 -1
  10. package/template/apps/agent-server/src/southwind_agent_server/app.py +16 -4
  11. package/template/apps/agent-server/src/southwind_agent_server/approval_resolution.py +30 -0
  12. package/template/apps/agent-server/src/southwind_agent_server/contracts.py +9 -0
  13. package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +2 -1
  14. package/template/apps/agent-server/src/southwind_agent_server/engine.py +3 -1
  15. package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +95 -130
  16. package/template/apps/agent-server/src/southwind_agent_server/openai_engine_validation.py +133 -0
  17. package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +89 -20
  18. package/template/apps/agent-server/src/southwind_agent_server/postgres_report_insert.py +34 -0
  19. package/template/apps/agent-server/src/southwind_agent_server/postgres_report_queries.py +234 -0
  20. package/template/apps/agent-server/src/southwind_agent_server/postgres_report_replay.py +44 -0
  21. package/template/apps/agent-server/src/southwind_agent_server/postgres_report_status.py +83 -0
  22. package/template/apps/agent-server/src/southwind_agent_server/postgres_report_store.py +331 -0
  23. package/template/apps/agent-server/src/southwind_agent_server/postgres_report_summary.py +304 -0
  24. package/template/apps/agent-server/src/southwind_agent_server/report_lifecycle.py +217 -0
  25. package/template/apps/agent-server/src/southwind_agent_server/report_metrics.py +149 -0
  26. package/template/apps/agent-server/src/southwind_agent_server/report_presenter.py +280 -0
  27. package/template/apps/agent-server/src/southwind_agent_server/report_routes.py +126 -0
  28. package/template/apps/agent-server/src/southwind_agent_server/report_store.py +344 -0
  29. package/template/apps/agent-server/src/southwind_agent_server/report_types.py +111 -0
  30. package/template/apps/agent-server/src/southwind_agent_server/runtime_factory.py +31 -2
  31. package/template/apps/agent-server/src/southwind_agent_server/service.py +56 -51
  32. package/template/apps/agent-server/src/southwind_agent_server/signal_events.py +18 -1
  33. package/template/apps/agent-server/tests/test_adk_adapter.py +7 -9
  34. package/template/apps/agent-server/tests/test_adk_debug_sessions.py +215 -0
  35. package/template/apps/agent-server/tests/test_adk_web_canonical_debug.py +48 -1
  36. package/template/apps/agent-server/tests/test_openai_provider.py +58 -6
  37. package/template/apps/agent-server/tests/test_postgres_report_status.py +59 -0
  38. package/template/apps/agent-server/tests/test_postgres_report_summary.py +298 -0
  39. package/template/apps/agent-server/tests/test_provider_attempt_reporting.py +152 -0
  40. package/template/apps/agent-server/tests/test_report_attempt_lifecycle.py +126 -0
  41. package/template/apps/agent-server/tests/test_report_resilience.py +181 -0
  42. package/template/apps/agent-server/tests/test_report_store.py +286 -0
  43. package/template/apps/agent-server/tests/test_report_summary_parity.py +115 -0
  44. package/template/apps/agent-server/tests/test_report_terminal_sync.py +177 -0
  45. package/template/apps/server/src/agent/debug-session-facade-provider-failure.test.ts +269 -0
  46. package/template/apps/server/src/agent/debug-session-facade.test.ts +335 -0
  47. package/template/apps/server/src/agent/debug-session-facade.ts +302 -0
  48. package/template/apps/server/src/agent/debug-session-id.test.ts +37 -0
  49. package/template/apps/server/src/agent/debug-session-id.ts +37 -0
  50. package/template/apps/server/src/agent/debug-session-provider-reader.ts +54 -0
  51. package/template/apps/server/src/agent/debug-session-routes.test.ts +64 -0
  52. package/template/apps/server/src/agent/debug-session-routes.ts +73 -0
  53. package/template/apps/server/src/agent/debug-session-validation.ts +106 -0
  54. package/template/apps/server/src/agent/drizzle-run-binding-repository.ts +2 -0
  55. package/template/apps/server/src/agent/provider-input-governance.test.ts +18 -0
  56. package/template/apps/server/src/agent/provider-input-governance.ts +37 -1
  57. package/template/apps/server/src/agent/remote-runtime.test.ts +7 -0
  58. package/template/apps/server/src/agent/remote-runtime.ts +1 -0
  59. package/template/apps/server/src/agent/run-authorization.test.ts +98 -0
  60. package/template/apps/server/src/agent/run-authorization.ts +32 -1
  61. package/template/apps/server/src/agent/run-binding-repository.ts +1 -0
  62. package/template/apps/server/src/agent/run-contracts.ts +5 -0
  63. package/template/apps/server/src/agent/run-facade-adk-debug.test.ts +39 -0
  64. package/template/apps/server/src/agent/run-facade.test.ts +78 -0
  65. package/template/apps/server/src/agent/run-facade.ts +33 -3
  66. package/template/apps/server/src/agent/run-input-commitment.ts +50 -0
  67. package/template/apps/server/src/agent/run-input-verification.ts +41 -0
  68. package/template/apps/server/src/agent/runtime-bootstrap-report.test.ts +136 -0
  69. package/template/apps/server/src/agent/runtime-bootstrap.ts +66 -1
  70. package/template/apps/server/src/agent/tool-host.test.ts +1 -0
  71. package/template/apps/server/src/agent/unavailable-routes.ts +4 -0
  72. package/template/apps/server/src/ai-runtime-report/actor-directory.ts +94 -0
  73. package/template/apps/server/src/ai-runtime-report/actor-handle.test.ts +27 -0
  74. package/template/apps/server/src/ai-runtime-report/actor-handle.ts +81 -0
  75. package/template/apps/server/src/ai-runtime-report/contracts.ts +91 -0
  76. package/template/apps/server/src/ai-runtime-report/remote-parser.ts +212 -0
  77. package/template/apps/server/src/ai-runtime-report/remote-port.ts +138 -0
  78. package/template/apps/server/src/ai-runtime-report/routes.ts +172 -0
  79. package/template/apps/server/src/ai-runtime-report/scope.ts +63 -0
  80. package/template/apps/server/src/ai-runtime-report/service.test.ts +271 -0
  81. package/template/apps/server/src/ai-runtime-report/service.ts +206 -0
  82. package/template/apps/server/src/license/runtime-audit-reliability.test.ts +2 -0
  83. package/template/apps/server/src/license/runtime-service.test.ts +6 -0
  84. package/template/apps/server/src/module-host/ai-operation-port.test.ts +63 -0
  85. package/template/apps/server/src/module-host/ai-operation-port.ts +30 -5
  86. package/template/apps/server/src/module-host/debug-session-provider-registration.test.ts +162 -0
  87. package/template/apps/server/src/module-host/debug-session-provider-registration.ts +84 -0
  88. package/template/apps/server/src/module-host/initialize-contracts.ts +7 -0
  89. package/template/apps/server/src/module-host/initialize.ts +6 -197
  90. package/template/apps/server/src/module-host/request-context.ts +1 -0
  91. package/template/apps/server/src/module-host/scoped-host.ts +214 -0
  92. package/template/apps/server/src/module-host.ts +1 -0
  93. package/template/apps/server/src/runtime-adk-debug.test.ts +25 -1
  94. package/template/apps/server/src/runtime-adk-debug.ts +8 -2
  95. package/template/apps/server/src/system/operations.test.ts +54 -0
  96. package/template/apps/server/src/system/operations.ts +23 -1
  97. package/template/apps/server/src/system/seed.test.ts +4 -0
  98. package/template/apps/server/src/system/system-resource-monitoring.test.ts +82 -0
  99. package/template/apps/server/src/system/system-resource-monitoring.ts +300 -0
  100. package/template/apps/web/src/layout/AppSidebar.vue +6 -1
  101. package/template/apps/web/src/layout/AppSidebar.webtest.ts +2 -0
  102. package/template/apps/web/src/pages/auth/LoginPage.vue +6 -1
  103. package/template/apps/web/src/pages/settings/BrandingSettingsSection.vue +20 -3
  104. package/template/apps/web/src/pages/settings/BrandingSettingsSection.webtest.ts +80 -0
  105. package/template/apps/web/src/pages/system/AiRuntimePage.vue +129 -0
  106. package/template/apps/web/src/pages/system/SystemOperationsPage.vue +2 -1
  107. package/template/apps/web/src/pages/system/ai-runtime-presenter.ts +60 -0
  108. package/template/apps/web/src/pages/system/ai-runtime-presenter.webtest.ts +48 -0
  109. package/template/apps/web/src/pages/system/audit-time-range.ts +34 -2
  110. package/template/apps/web/src/pages/system/components/AuditLogFilters.vue +4 -3
  111. package/template/apps/web/src/pages/system/components/AuditLogFilters.webtest.ts +26 -1
  112. package/template/apps/web/src/pages/system/components/OperationGpuMonitor.vue +98 -0
  113. package/template/apps/web/src/pages/system/components/OperationGpuMonitor.webtest.ts +61 -0
  114. package/template/apps/web/src/pages/system/components/OperationServiceMonitor.vue +53 -0
  115. package/template/apps/web/src/pages/system/components/OperationServiceMonitor.webtest.ts +20 -1
  116. package/template/apps/web/src/pages/system/components/SystemResourcePage.webtest.ts +14 -1
  117. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeActivityChart.vue +85 -0
  118. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeCharts.webtest.ts +82 -0
  119. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.vue +98 -0
  120. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.webtest.ts +27 -0
  121. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunDetailDialog.vue +168 -0
  122. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeRunTable.vue +130 -0
  123. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeSummaryCards.vue +82 -0
  124. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeSummaryCards.webtest.ts +102 -0
  125. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeTokenByModelChart.vue +101 -0
  126. package/template/apps/web/src/pages/system/composables/useAiRuntimeReport.ts +185 -0
  127. package/template/apps/web/src/pages/system/composables/useAiRuntimeReport.webtest.ts +158 -0
  128. package/template/apps/web/src/pages/system/composables/useSystemResource.ts +21 -7
  129. package/template/apps/web/src/router/error-navigation.ts +22 -0
  130. package/template/apps/web/src/router/index.ts +12 -23
  131. package/template/apps/web/src/router/platform-admin-route-bindings.ts +4 -0
  132. package/template/apps/web/src/services/ai-runtime-api.ts +164 -0
  133. package/template/apps/web/src/services/ai-runtime-api.webtest.ts +111 -0
  134. package/template/apps/web/src/services/operations-api.ts +28 -0
  135. package/template/docker-compose.agent-debug.yml +1 -1
  136. package/template/docs/architecture/ai-runtime.md +2 -1
  137. package/template/docs/architecture/module-host.md +3 -0
  138. package/template/docs/platform/agent-run-attestation.md +65 -0
  139. package/template/docs/platform/agent-runtime-observability.md +135 -0
  140. package/template/docs/platform/agent-runtime.md +47 -3
  141. package/template/docs/platform/operations-overview.md +13 -0
  142. package/template/docs/platform/platform-authentication.md +4 -3
  143. package/template/packages/config/package.json +1 -1
  144. package/template/packages/database/drizzle/0045_tiny_cassandra_nova.sql +65 -0
  145. package/template/packages/database/drizzle/0046_tranquil_living_mummy.sql +6 -0
  146. package/template/packages/database/drizzle/0047_oval_luke_cage.sql +18 -0
  147. package/template/packages/database/drizzle/meta/0045_snapshot.json +8814 -0
  148. package/template/packages/database/drizzle/meta/0046_snapshot.json +8820 -0
  149. package/template/packages/database/drizzle/meta/0047_snapshot.json +8835 -0
  150. package/template/packages/database/drizzle/meta/_journal.json +21 -0
  151. package/template/packages/database/package.json +1 -1
  152. package/template/packages/database/src/agent-runtime-schema.test.ts +54 -0
  153. package/template/packages/database/src/schema/agent-runtime.ts +114 -0
  154. package/template/packages/jobs/package.json +1 -1
  155. package/template/packages/modules/package.json +1 -1
  156. package/template/packages/modules/src/system-admin-routes.ts +10 -0
  157. package/template/packages/modules/src/system-admin-ui-catalog.ts +1 -1
  158. package/template/packages/modules/src/system-api-control-plane.ts +17 -1
  159. package/template/packages/modules/src/system-audit-actions.ts +10 -0
  160. package/template/packages/modules/src/system-features.ts +8 -0
  161. package/template/packages/modules/src/system-module-catalog.test.ts +35 -0
  162. package/template/packages/modules/src/system-module-catalog.ts +15 -4
  163. package/template/packages/modules/src/system-modules-access.test.ts +69 -0
  164. package/template/packages/modules/src/system-modules.ts +13 -0
  165. package/template/packages/modules/src/system-permissions.ts +2 -0
  166. package/template/packages/server-sdk/index.ts +11 -0
  167. package/template/packages/server-sdk/package.json +2 -1
  168. package/template/packages/server-sdk/src/ai-operation-port.ts +5 -0
  169. package/template/packages/server-sdk/src/debug-session-provider.ts +63 -0
  170. package/template/packages/server-sdk/src/module-host.ts +5 -0
  171. package/template/packages/shared/index.ts +3 -0
  172. package/template/packages/shared/package.json +1 -1
  173. package/template/packages/shared/src/ai-runtime-report.ts +134 -0
  174. package/template/scripts/lib/postgres-regression.ts +201 -0
@@ -0,0 +1,177 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+
5
+ from southwind_agent_server.contracts import (
6
+ RunReportingContext,
7
+ StartRunRequest,
8
+ )
9
+ from southwind_agent_server.deterministic import DeterministicEngineAdapter
10
+ from southwind_agent_server.report_store import (
11
+ InMemoryAgentRuntimeReportStore,
12
+ )
13
+ from southwind_agent_server.service import AgentRunService
14
+ from southwind_agent_server.store import (
15
+ CanonicalEvent,
16
+ InMemoryRunStore,
17
+ RunRecord,
18
+ )
19
+
20
+
21
+ def request(text: str = "done") -> StartRunRequest:
22
+ return StartRunRequest(
23
+ operation_key="test.echo",
24
+ input={"text": text},
25
+ trace_id="trace_1",
26
+ reporting=RunReportingContext(
27
+ actor_id="actor_1",
28
+ actor_type="user",
29
+ ),
30
+ )
31
+
32
+
33
+ async def test_rewritten_event_terminal_drives_report_status() -> None:
34
+ canonical = EventLimitRunStore()
35
+ report = InMemoryAgentRuntimeReportStore()
36
+ service = AgentRunService(
37
+ DeterministicEngineAdapter(),
38
+ canonical,
39
+ report,
40
+ )
41
+
42
+ record = await service.start(request(), "event-limit")
43
+ final = await wait_for_terminal(service, record.run_id)
44
+ detail = await report.get_run(record.run_id)
45
+ await service.close()
46
+
47
+ assert final.status == "failed"
48
+ assert detail is not None
49
+ assert detail["status"] == "failed"
50
+ assert detail["safeErrorCode"] == "BUDGET_EXCEEDED"
51
+
52
+
53
+ async def test_output_limit_terminal_drives_report_status() -> None:
54
+ canonical = OutputLimitRunStore()
55
+ report = InMemoryAgentRuntimeReportStore()
56
+ service = AgentRunService(
57
+ DeterministicEngineAdapter(),
58
+ canonical,
59
+ report,
60
+ )
61
+
62
+ record = await service.start(request("too large"), "output-limit")
63
+ final = await wait_for_terminal(service, record.run_id)
64
+ detail = await report.get_run(record.run_id)
65
+ await service.close()
66
+
67
+ assert final.status == "failed"
68
+ assert detail is not None
69
+ assert detail["status"] == "failed"
70
+ assert detail["safeErrorCode"] == "BUDGET_EXCEEDED"
71
+
72
+
73
+ async def test_completion_wins_cancel_race_in_report() -> None:
74
+ canonical = CompleteWinsCancelRunStore()
75
+ report = InMemoryAgentRuntimeReportStore()
76
+ service = AgentRunService(
77
+ DeterministicEngineAdapter(),
78
+ canonical,
79
+ report,
80
+ )
81
+ invocation = request()
82
+ record = run_record(invocation, "race")
83
+ await canonical.create_or_get(record, invocation.model_dump(by_alias=True))
84
+ await report.ensure_run(record.run_id, invocation, 100.0)
85
+ await report.start_run(record.run_id, 101.0)
86
+
87
+ final = await service.cancel(record.run_id)
88
+ detail = await report.get_run(record.run_id)
89
+ await service.close()
90
+
91
+ assert final.status == "completed"
92
+ assert detail is not None
93
+ assert detail["status"] == "completed"
94
+
95
+
96
+ async def wait_for_terminal(
97
+ service: AgentRunService,
98
+ run_id: str,
99
+ ) -> RunRecord:
100
+ async def poll() -> RunRecord:
101
+ while True:
102
+ record = await service.get(run_id)
103
+ if record.terminal:
104
+ return record
105
+ await asyncio.sleep(0)
106
+
107
+ return await asyncio.wait_for(poll(), 1)
108
+
109
+
110
+ def run_record(invocation: StartRunRequest, suffix: str) -> RunRecord:
111
+ return RunRecord(
112
+ run_id=f"run_{suffix}",
113
+ operation_key=invocation.operation_key,
114
+ input=invocation.input,
115
+ idempotency_key=f"key_{suffix}",
116
+ fingerprint=f"fingerprint_{suffix}",
117
+ release=invocation.release.model_dump(by_alias=True),
118
+ trace_id=invocation.trace_id,
119
+ )
120
+
121
+
122
+ def failure_event(run_id: str, sequence: int) -> CanonicalEvent:
123
+ return {
124
+ "type": "run.failed",
125
+ "runId": run_id,
126
+ "sequence": sequence,
127
+ "error": {
128
+ "code": "BUDGET_EXCEEDED",
129
+ "message": "Agent 存储超过安全上限",
130
+ "requestId": f"request_{sequence}",
131
+ "retryable": False,
132
+ },
133
+ }
134
+
135
+
136
+ class EventLimitRunStore(InMemoryRunStore):
137
+ async def append(
138
+ self,
139
+ run_id: str,
140
+ event_type: str,
141
+ fields: dict[str, object] | None = None,
142
+ ) -> CanonicalEvent | None:
143
+ if event_type == "usage.reported":
144
+ record = await self.get(run_id)
145
+ assert record is not None
146
+ event = failure_event(run_id, len(record.events) + 1)
147
+ return await super().append(
148
+ run_id,
149
+ "run.failed",
150
+ {"error": event["error"]},
151
+ )
152
+ return await super().append(run_id, event_type, fields)
153
+
154
+
155
+ class OutputLimitRunStore(InMemoryRunStore):
156
+ async def complete(self, run_id: str, output: object) -> None:
157
+ del output
158
+ record = await self.get(run_id)
159
+ assert record is not None
160
+ event = failure_event(run_id, len(record.events) + 1)
161
+ await super().append(
162
+ run_id,
163
+ "run.failed",
164
+ {"error": event["error"]},
165
+ )
166
+
167
+
168
+ class CompleteWinsCancelRunStore(InMemoryRunStore):
169
+ async def append(
170
+ self,
171
+ run_id: str,
172
+ event_type: str,
173
+ fields: dict[str, object] | None = None,
174
+ ) -> CanonicalEvent | None:
175
+ if event_type == "run.cancelled":
176
+ await super().complete(run_id, {"winner": "complete"})
177
+ return await super().append(run_id, event_type, fields)
@@ -0,0 +1,269 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import type { ModuleManifest } from "@southwind-ai/modules";
3
+ import {
4
+ MODULE_DEBUG_SESSION_SCHEMA_VERSION,
5
+ type ModuleDebugSessionProviderV1,
6
+ } from "@southwind-ai/server-sdk";
7
+ import { featureKey } from "@southwind-ai/shared";
8
+ import {
9
+ bindModuleUserProjection,
10
+ createRequestGuardContext,
11
+ type RequestActor,
12
+ } from "../guards.js";
13
+ import type { InitializedModuleHosts } from "../module-host.js";
14
+ import { manifest } from "../module-host/test-fixtures.js";
15
+ import { createServerRuntime } from "../runtime.js";
16
+ import { DebugSessionFacadeV1 } from "./debug-session-facade.js";
17
+ import { DebugSessionIdCodecV1 } from "./debug-session-id.js";
18
+
19
+ const OPERATION_KEY = "known.copilot.global";
20
+ const SOURCE_ID = "conversation-sensitive-42";
21
+ const SECRET_FAILURE = `provider failed for ${SOURCE_ID}`;
22
+
23
+ describe("ADK 调试会话 Provider 故障隔离", () => {
24
+ test("一个 Provider 故障时 fail closed,避免把 partial 伪装成完整列表", async () => {
25
+ const failing = provider("known.failing-history", {
26
+ list: async () => {
27
+ throw new Error(SECRET_FAILURE);
28
+ },
29
+ get: async () => undefined,
30
+ });
31
+ const healthy = provider("known.healthy-history", {
32
+ list: async () => ({
33
+ sessions: [summary("conversation-healthy")],
34
+ truncated: false,
35
+ }),
36
+ get: async () => ({
37
+ ...summary("conversation-healthy"),
38
+ messages: [],
39
+ }),
40
+ });
41
+ const fixture = createFixture([failing, healthy]);
42
+
43
+ await expect(
44
+ fixture.facade.list(fixture.context, signal()),
45
+ ).rejects.toMatchObject({
46
+ code: "PROVIDER_UNAVAILABLE",
47
+ status: 503,
48
+ });
49
+ const failure = fixture.runtime.auditSink
50
+ .list()
51
+ .find(({ outcome }) => outcome === "failure");
52
+ expect(failure).toMatchObject({
53
+ action: "known.copilot.history.read",
54
+ outcome: "failure",
55
+ target: {
56
+ type: "agent-debug-session-provider",
57
+ id: "known.failing-history",
58
+ },
59
+ metadata: {
60
+ debugDataActorId: "user_dev_admin",
61
+ mode: "list",
62
+ phase: "list",
63
+ failureCode: "PROVIDER_UNAVAILABLE",
64
+ },
65
+ });
66
+ expect(JSON.stringify(failure)).not.toContain(SECRET_FAILURE);
67
+ });
68
+
69
+ test("存在 Provider 且全部失败时返回 503", async () => {
70
+ const fixture = createFixture([
71
+ failingProvider("known.first-history"),
72
+ failingProvider("known.second-history"),
73
+ ]);
74
+
75
+ await expect(
76
+ fixture.facade.list(fixture.context, signal()),
77
+ ).rejects.toMatchObject({
78
+ code: "PROVIDER_UNAVAILABLE",
79
+ status: 503,
80
+ });
81
+ expect(
82
+ fixture.runtime.auditSink
83
+ .list()
84
+ .filter(({ outcome }) => outcome === "failure"),
85
+ ).toHaveLength(2);
86
+ });
87
+
88
+ test("详情读取遇到 Provider 故障且最终未命中时返回 503", async () => {
89
+ let failDetail = false;
90
+ const unstable = provider("known.unstable-history", {
91
+ list: async () => ({
92
+ sessions: [summary(SOURCE_ID)],
93
+ truncated: false,
94
+ }),
95
+ get: async () => {
96
+ if (failDetail) throw new Error(SECRET_FAILURE);
97
+ return { ...summary(SOURCE_ID), messages: [] };
98
+ },
99
+ });
100
+ const empty = provider("known.empty-history", {
101
+ list: async () => ({ sessions: [], truncated: false }),
102
+ get: async () => undefined,
103
+ });
104
+ const fixture = createFixture([unstable, empty]);
105
+ const listed = await fixture.facade.list(fixture.context, signal());
106
+ failDetail = true;
107
+
108
+ await expect(
109
+ fixture.facade.get(
110
+ listed.sessions[0]?.id ?? "",
111
+ fixture.context,
112
+ signal(),
113
+ ),
114
+ ).rejects.toMatchObject({
115
+ code: "PROVIDER_UNAVAILABLE",
116
+ status: 503,
117
+ });
118
+ const failure = fixture.runtime.auditSink
119
+ .list()
120
+ .find(
121
+ ({ outcome, metadata }) =>
122
+ outcome === "failure" && metadata?.phase === "get",
123
+ );
124
+ expect(failure?.metadata).toMatchObject({
125
+ mode: "get",
126
+ phase: "get",
127
+ failureCode: "PROVIDER_UNAVAILABLE",
128
+ });
129
+ expect(JSON.stringify(failure)).not.toContain(SECRET_FAILURE);
130
+ });
131
+ });
132
+
133
+ function failingProvider(key: string): ModuleDebugSessionProviderV1 {
134
+ return provider(key, {
135
+ list: async () => {
136
+ throw new Error(SECRET_FAILURE);
137
+ },
138
+ get: async () => undefined,
139
+ });
140
+ }
141
+
142
+ function provider(
143
+ key: string,
144
+ methods: Pick<ModuleDebugSessionProviderV1, "list" | "get">,
145
+ ): ModuleDebugSessionProviderV1 {
146
+ return {
147
+ schemaVersion: MODULE_DEBUG_SESSION_SCHEMA_VERSION,
148
+ key,
149
+ operationKeys: [OPERATION_KEY],
150
+ featureKey: "known.copilot",
151
+ permissionKey: "known.copilot.execute",
152
+ auditAction: "known.copilot.history.read",
153
+ ...methods,
154
+ };
155
+ }
156
+
157
+ function createFixture(providers: readonly ModuleDebugSessionProviderV1[]) {
158
+ const module = debugManifest();
159
+ const hosts = {
160
+ debugSessionProviders: providers.map((provider) => ({
161
+ moduleName: "known",
162
+ provider,
163
+ })),
164
+ manifestOf(moduleName: string) {
165
+ if (moduleName !== "known") throw new Error("unknown module");
166
+ return module;
167
+ },
168
+ } as unknown as InitializedModuleHosts;
169
+ const base = createServerRuntime(
170
+ {},
171
+ {
172
+ featureCatalog: module.features,
173
+ licenseResolver: { resolve: () => ({ status: "valid" }) },
174
+ },
175
+ );
176
+ const runtime = {
177
+ ...base,
178
+ agentDebugOperationForActor: (actorId: string) =>
179
+ actorId === "service_adk_debug" ? OPERATION_KEY : undefined,
180
+ };
181
+ const context = createRequestGuardContext(
182
+ new Request("http://localhost/api/agent/debug/sessions/v1"),
183
+ {
184
+ resolveActor: () => bindModuleUserProjection(actor(), "user_dev_admin"),
185
+ resolveLicense: () => ({ status: "valid" }),
186
+ licenseEnforced: false,
187
+ },
188
+ );
189
+ return {
190
+ context,
191
+ facade: new DebugSessionFacadeV1(
192
+ hosts,
193
+ runtime,
194
+ new DebugSessionIdCodecV1("test-secret-012345678901234567890123456789"),
195
+ ),
196
+ runtime,
197
+ };
198
+ }
199
+
200
+ function actor(): RequestActor {
201
+ return {
202
+ id: "service_adk_debug",
203
+ type: "service",
204
+ name: "ADK Debug",
205
+ roleCodes: ["adk-debug"],
206
+ permissionKeys: ["known.copilot.execute"],
207
+ };
208
+ }
209
+
210
+ function summary(sourceId: string) {
211
+ return {
212
+ sourceId,
213
+ title: "历史研判",
214
+ scopeLabel: "测试范围",
215
+ updatedAt: "2026-07-31T01:00:01.000Z",
216
+ revision: "2",
217
+ };
218
+ }
219
+
220
+ function signal(): AbortSignal {
221
+ return new AbortController().signal;
222
+ }
223
+
224
+ function debugManifest(): ModuleManifest {
225
+ return manifest("known", {
226
+ features: [
227
+ {
228
+ key: featureKey("known.copilot"),
229
+ label: "Copilot",
230
+ module: "known",
231
+ visible: "visible",
232
+ enabled: true,
233
+ stage: "stable",
234
+ },
235
+ ],
236
+ permissions: [
237
+ {
238
+ key: "known.copilot.execute",
239
+ label: "使用 Copilot",
240
+ module: "known",
241
+ moduleLabel: "Known",
242
+ resource: "copilot",
243
+ resourceLabel: "Copilot",
244
+ action: "execute",
245
+ },
246
+ ],
247
+ aiOperations: [
248
+ {
249
+ key: OPERATION_KEY,
250
+ kind: "agent",
251
+ featureKey: "known.copilot",
252
+ permissionKey: "known.copilot.execute",
253
+ auditAction: "known.copilot.execute",
254
+ inputSchemaKey: "known.copilot.input.v1",
255
+ outputSchemaKey: "known.copilot.output.v1",
256
+ requiredCapabilities: ["text-generation"],
257
+ allowedToolKeys: [],
258
+ dataClassification: "internal",
259
+ execution: "streaming",
260
+ limits: {
261
+ maxDurationSeconds: 60,
262
+ maxInputBytes: 4096,
263
+ maxOutputBytes: 4096,
264
+ },
265
+ },
266
+ ],
267
+ auditActions: ["known.copilot.execute", "known.copilot.history.read"],
268
+ });
269
+ }