union-ark-web 1.0.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 (122) hide show
  1. package/PROJECT_OVERVIEW.md +405 -0
  2. package/README.md +177 -0
  3. package/ark-control/pom.xml +142 -0
  4. package/ark-control/src/local-mock/java/com/epcc/commons/securityproxy/api/SecurityResult.java +14 -0
  5. package/ark-control/src/local-mock/java/com/epcc/commons/securityproxy/api/SymmetricalSecurityService.java +11 -0
  6. package/ark-control/src/local-mock/java/com/epcc/dubbo/result/Result.java +29 -0
  7. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/annotation/EnDecryptField.java +16 -0
  8. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/annotation/EnDecryptFieldLong.java +17 -0
  9. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/annotation/EnDecryptFieldWithTag.java +16 -0
  10. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/exception/BaseDataErrorCode.java +26 -0
  11. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/exception/CheckException.java +33 -0
  12. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/exception/ErrorCode.java +9 -0
  13. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/redis/RedisCacheService.java +131 -0
  14. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/util/Constant.java +24 -0
  15. package/ark-control/src/local-mock/java/com/nucc/channel/ark/common/util/ResultUtil.java +9 -0
  16. package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalMockSensitiveProxy.java +40 -0
  17. package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalRedisCacheService.java +45 -0
  18. package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalSensitiveHostConfiguration.java +27 -0
  19. package/ark-control/src/local-mock/java/com/union/control/local/sensitive/LocalSensitiveProxyConfiguration.java +16 -0
  20. package/ark-control/src/main/java/com/union/control/ArkControlApplication.java +17 -0
  21. package/ark-control/src/main/java/com/union/control/dubbo/ProviderAccessLogFilter.java +45 -0
  22. package/ark-control/src/main/java/com/union/control/mapper/AgentExecutionMapper.java +35 -0
  23. package/ark-control/src/main/java/com/union/control/mapper/ConversationMapper.java +44 -0
  24. package/ark-control/src/main/java/com/union/control/mapper/MemoryStoreMapper.java +41 -0
  25. package/ark-control/src/main/java/com/union/control/mapper/ScheduledTaskMapper.java +96 -0
  26. package/ark-control/src/main/java/com/union/control/mapper/SensitiveAddressBookDemo.java +31 -0
  27. package/ark-control/src/main/java/com/union/control/mapper/SensitiveDataDemoMapper.java +24 -0
  28. package/ark-control/src/main/java/com/union/control/mapper/interceptor/AESInterceptor.java +265 -0
  29. package/ark-control/src/main/java/com/union/control/mapper/interceptor/AddressBookHandler.java +105 -0
  30. package/ark-control/src/main/java/com/union/control/schedule/ScheduledExecutionToken.java +55 -0
  31. package/ark-control/src/main/java/com/union/control/schedule/ScheduledTaskScheduler.java +206 -0
  32. package/ark-control/src/main/java/com/union/control/service/impl/AgentExecutionServiceImpl.java +212 -0
  33. package/ark-control/src/main/java/com/union/control/service/impl/AgentProxyServiceImpl.java +143 -0
  34. package/ark-control/src/main/java/com/union/control/service/impl/ConversationServiceImpl.java +245 -0
  35. package/ark-control/src/main/java/com/union/control/service/impl/MemoryStoreServiceImpl.java +252 -0
  36. package/ark-control/src/main/java/com/union/control/service/impl/RunningAnalysisMockServiceImpl.java +301 -0
  37. package/ark-control/src/main/java/com/union/control/service/impl/ScheduledTaskServiceImpl.java +576 -0
  38. package/ark-control/src/main/java/com/union/control/service/impl/SensitiveDataDemoServiceImpl.java +144 -0
  39. package/ark-control/src/main/java/com/union/control/service/sensitive/RedisRevealTokenStore.java +90 -0
  40. package/ark-control/src/main/java/com/union/control/service/sensitive/SensitiveRevealProcessor.java +319 -0
  41. package/ark-control/src/main/java/com/union/control/service/sensitive/SensitiveRevealServiceImpl.java +72 -0
  42. package/ark-control/src/main/java/com/union/control/utils/AgentSupport.java +103 -0
  43. package/ark-control/src/main/java/com/union/control/utils/security/SymmetricalSecurityUtils.java +160 -0
  44. package/ark-control/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter +1 -0
  45. package/ark-control/src/main/resources/application.properties +4 -0
  46. package/ark-control/src/main/resources/application.yml +32 -0
  47. package/ark-control/src/main/resources/dubbo-provider.xml +20 -0
  48. package/ark-control/src/main/resources/mapper/AgentExecutionMapper.xml +75 -0
  49. package/ark-control/src/main/resources/mapper/ConversationMapper.xml +110 -0
  50. package/ark-control/src/main/resources/mapper/MemoryStoreMapper.xml +74 -0
  51. package/ark-control/src/main/resources/mapper/ScheduledTaskMapper.xml +286 -0
  52. package/ark-control/src/main/resources/mapper/SensitiveDataDemoMapper.xml +55 -0
  53. package/ark-control/src/main/resources/schema.sql +146 -0
  54. package/ark-control/src/test/java/com/union/control/AgentExecutionServiceTest.java +170 -0
  55. package/ark-control/src/test/java/com/union/control/ArkControlStructureContractTest.java +107 -0
  56. package/ark-control/src/test/java/com/union/control/ConversationServiceTest.java +89 -0
  57. package/ark-control/src/test/java/com/union/control/MemoryStoreServiceTest.java +26 -0
  58. package/ark-control/src/test/java/com/union/control/PydanticAiControlContractTest.java +52 -0
  59. package/ark-control/src/test/java/com/union/control/TestJson.java +45 -0
  60. package/ark-control/src/test/java/com/union/control/dubbo/ProviderAccessLogFilterTest.java +53 -0
  61. package/ark-control/src/test/java/com/union/control/local/sensitive/LocalSensitiveProxyConfigurationTest.java +23 -0
  62. package/ark-control/src/test/java/com/union/control/mapper/interceptor/AESInterceptorCompatibilityTest.java +505 -0
  63. package/ark-control/src/test/java/com/union/control/schedule/ScheduledExecutionTokenTest.java +27 -0
  64. package/ark-control/src/test/java/com/union/control/schedule/ScheduledTaskSchedulerTest.java +51 -0
  65. package/ark-control/src/test/java/com/union/control/service/AgentProxyServiceTest.java +111 -0
  66. package/ark-control/src/test/java/com/union/control/service/ScheduledTaskServiceTest.java +483 -0
  67. package/ark-control/src/test/java/com/union/control/service/impl/SensitiveDataDemoServiceImplTest.java +104 -0
  68. package/ark-control/src/test/java/com/union/control/service/sensitive/RedisRevealTokenStoreTest.java +65 -0
  69. package/ark-control/src/test/java/com/union/control/service/sensitive/SensitiveRevealBoundaryTest.java +99 -0
  70. package/ark-control/src/test/java/com/union/control/service/sensitive/SensitiveRevealProcessorCodecTest.java +111 -0
  71. package/ark-control/src/test/java/com/union/control/service/sensitive/SensitiveRevealProcessorTest.java +111 -0
  72. package/ark-control/src/test/java/com/union/control/utils/security/SymmetricalSecurityUtilsCompatibilityTest.java +87 -0
  73. package/ark-control-facade/pom.xml +16 -0
  74. package/ark-control-facade/src/main/java/com/union/control/service/AgentExecutionService.java +8 -0
  75. package/ark-control-facade/src/main/java/com/union/control/service/AgentProxyService.java +7 -0
  76. package/ark-control-facade/src/main/java/com/union/control/service/AgentResponse.java +22 -0
  77. package/ark-control-facade/src/main/java/com/union/control/service/ConversationService.java +12 -0
  78. package/ark-control-facade/src/main/java/com/union/control/service/MemoryStoreService.java +12 -0
  79. package/ark-control-facade/src/main/java/com/union/control/service/RunningAnalysisMockService.java +10 -0
  80. package/ark-control-facade/src/main/java/com/union/control/service/ScheduledTaskService.java +40 -0
  81. package/ark-control-facade/src/main/java/com/union/control/service/SensitiveDataDemoService.java +13 -0
  82. package/ark-control-facade/src/main/java/com/union/control/service/sensitive/SensitiveRevealService.java +18 -0
  83. package/ark-web/README.md +80 -0
  84. package/ark-web/pom.xml +105 -0
  85. package/ark-web/src/local-mock/java/com/epcc/arkweb/mock/ArkAuthServiceMock.java +105 -0
  86. package/ark-web/src/local-mock/java/com/epcc/arkweb/mock/LocalKaptchaConfiguration.java +26 -0
  87. package/ark-web/src/main/java/com/epcc/arkweb/Application.java +13 -0
  88. package/ark-web/src/main/java/com/epcc/arkweb/config/InterceptorConfig.java +20 -0
  89. package/ark-web/src/main/java/com/epcc/arkweb/config/Realm.java +64 -0
  90. package/ark-web/src/main/java/com/epcc/arkweb/config/ShiroConfig.java +129 -0
  91. package/ark-web/src/main/java/com/epcc/arkweb/filter/LoginFormFilter.java +43 -0
  92. package/ark-web/src/main/java/com/epcc/arkweb/helper/AuthContextHolder.java +39 -0
  93. package/ark-web/src/main/java/com/epcc/arkweb/helper/AuthenticatedRequest.java +91 -0
  94. package/ark-web/src/main/java/com/epcc/arkweb/model/ShiroUser.java +21 -0
  95. package/ark-web/src/main/java/com/epcc/arkweb/utils/ResultMsg.java +19 -0
  96. package/ark-web/src/main/java/com/epcc/arkweb/vo/llm/ScheduledTaskCommandVO.java +30 -0
  97. package/ark-web/src/main/java/com/epcc/arkweb/vo/llm/ScheduledTaskQueryVO.java +20 -0
  98. package/ark-web/src/main/java/com/epcc/arkweb/web/CommonController.java +25 -0
  99. package/ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentAuthorizationInterceptor.java +229 -0
  100. package/ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentController.java +165 -0
  101. package/ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentPermission.java +13 -0
  102. package/ark-web/src/main/java/com/epcc/arkweb/web/llm/LlmController.java +209 -0
  103. package/ark-web/src/main/java/com/epcc/arkweb/web/llm/ScheduledTaskController.java +160 -0
  104. package/ark-web/src/main/java/com/epcc/arkweb/web/sensitive/reveal/SensitiveCaptchaService.java +146 -0
  105. package/ark-web/src/main/java/com/epcc/arkweb/web/sensitive/reveal/SensitiveRevealController.java +163 -0
  106. package/ark-web/src/main/resources/application.properties +1 -0
  107. package/ark-web/src/main/resources/application.yml +23 -0
  108. package/ark-web/src/main/resources/dubbo-consumer.xml +19 -0
  109. package/ark-web/src/production-overlay/java/com/epcc/arkweb/config/CasShiroConfig.java +229 -0
  110. package/ark-web/src/production-overlay/java/com/epcc/arkweb/config/InterceptorConfig.java +35 -0
  111. package/ark-web/src/production-overlay/java/com/epcc/arkweb/config/ShiroConfig.java +161 -0
  112. package/ark-web/src/test/java/com/epcc/arkweb/ArkWebStructureContractTest.java +290 -0
  113. package/ark-web/src/test/java/com/epcc/arkweb/ShiroTestSupport.java +32 -0
  114. package/ark-web/src/test/java/com/epcc/arkweb/config/LocalShiroIntegrationTest.java +121 -0
  115. package/ark-web/src/test/java/com/epcc/arkweb/helper/AuthenticatedRequestTest.java +131 -0
  116. package/ark-web/src/test/java/com/epcc/arkweb/local/LocalArkAuthServiceMockTest.java +17 -0
  117. package/ark-web/src/test/java/com/epcc/arkweb/web/llm/AgentAuthorizationInterceptorTest.java +134 -0
  118. package/ark-web/src/test/java/com/epcc/arkweb/web/llm/AgentControllerAuthorizationTest.java +97 -0
  119. package/ark-web/src/test/java/com/epcc/arkweb/web/llm/LlmControllerTest.java +113 -0
  120. package/ark-web/src/test/java/com/epcc/arkweb/web/sensitive/reveal/SensitiveRevealControllerTest.java +273 -0
  121. package/package.json +18 -0
  122. package/pom.xml +16 -0
@@ -0,0 +1,405 @@
1
+ # Project Overview
2
+
3
+ ## Mandatory Architecture Contract
4
+
5
+ This section is the highest-level implementation constraint for every future
6
+ feature iteration. New requirements must comply with it before lower sections,
7
+ existing scenario code, or tests are considered. Existing code that conflicts
8
+ with this contract is technical debt to remove, not a precedent to copy.
9
+
10
+ ### Web authentication and JSON-only service boundary
11
+
12
+ - `ark-web` is merged into the existing production Web application. Browser
13
+ routes reuse its existing Shiro/CAS implementation and authorization annotations.
14
+ The standalone build mirrors the production `ShiroConfig`, `Realm`,
15
+ `LoginFormFilter`, and `InterceptorConfig` names and lifecycle; only the
16
+ external `arkAuthService` bean is replaced under `src/local-mock/java`.
17
+ The production-overlay copies of both filter-chain configurations delegate
18
+ only `/agent/**` to the Agent guard with `anon` before their catch-all `authc`.
19
+ They replace the matching production files during integration; runtime source
20
+ must not define another Realm, Shiro configuration, CAS token, or authentication filter.
21
+ - `ark-control` is a trusted, independently deployed Dubbo provider. Business services must not
22
+ read Shiro subjects, cookies, thread-local authentication, or call helpers such
23
+ as `currentUserId()`. The module has no Shiro dependency.
24
+ - After Web authentication succeeds, `AuthenticatedRequest` overwrites any
25
+ client-supplied `userId`, `orgCode`, and `roleId` with the authenticated values
26
+ and serializes the complete backend input as one JSON string.
27
+ - User-facing business service methods accept that JSON string, parse and validate
28
+ it, then pass the parsed values to their mapper. Mapper ownership predicates
29
+ continue to use `userId`; the service trusts the Web boundary that supplied it.
30
+ - Background scheduler mechanics that create or consume execution credentials
31
+ live in `ark-control`. Backend-only scheduling state transitions may keep
32
+ typed internal method parameters because they are not public request inputs.
33
+
34
+ ### One browser-to-Agent gateway
35
+
36
+ - Domain controllers own browser and MySQL interactions for their domain. They
37
+ must not proxy requests to a model service or expose callbacks, context APIs,
38
+ or tool replicas for py-app. All browser-to-Agent traffic belongs to
39
+ `LlmController` and the shared `AgentProxyService` execution methods.
40
+ - A new business scenario may add its domain specialist Agent and domain tools
41
+ in py-app, plus its own CRUD/persistence services in control when required. It
42
+ must not add a scenario-specific LLM controller route, proxy method, bearer
43
+ credential, long-timeout HTTP client, tool forwarding endpoint, completion
44
+ callback, or alternate conversation persistence path.
45
+ - Scheduling, batching, and other orchestration are invocation mechanisms, not
46
+ Agent domains. They may decide when to invoke work and persist their own job
47
+ state, but they must call the same normal Agent execution plane as an
48
+ interactive caller.
49
+
50
+ ### Reuse stream or non-stream runs
51
+
52
+ - Use `/llm/chatMessage` when the caller needs AG-UI streaming and
53
+ `/llm/chatMessageSync` when it needs a final synchronous result. Both paths
54
+ must preserve the same CAS-authenticated identity and call the corresponding
55
+ shared py-app run endpoint.
56
+ - Background work such as a due scheduled occurrence must invoke the shared
57
+ non-stream application service behind `chatMessageSync`; it must not create a
58
+ second browser route just to forward to the model, nor call a scenario-specific
59
+ py-app route.
60
+ - Domain job state and final result payload may remain in domain tables. Normal
61
+ conversation, execution, and message records must be created through the
62
+ shared `ConversationService` and `AgentExecutionService` paths rather than
63
+ direct inserts from a domain mapper.
64
+
65
+ ### Preserve one identity and tool path
66
+
67
+ - The Web application authenticates interactive execution and forwards the
68
+ validated CAS cookie unchanged. Scheduled execution uses a separate
69
+ single-occurrence, short-lived, sessionless token; Control stores only its
70
+ SHA-256 hash and never constructs a Shiro identity for it.
71
+ - Scheduled traffic carries only `Authorization: Scheduled <token>`. It enters
72
+ py through `/agent/v1/runs/scheduled`, then calls
73
+ `/agent/scheduledTaskAuthorize`. Web resolves the active run through Control,
74
+ calls the production `arkAuthService.queryResource` bean with the
75
+ stored role ID, and requires `/assistantManager/page`. The response includes
76
+ a database-backed trusted context for subsequent Agent tools. Those tools must
77
+ use `X-Agent-Trusted-Context`; the raw scheduled bearer is not accepted on a
78
+ tool route. The non-Shiro guard re-resolves the run and live resources for
79
+ every tool call and never constructs a Subject.
80
+ - Each business tool has one control endpoint and one authorization
81
+ implementation. Scenario-prefixed clones are forbidden.
82
+
83
+ ### Layer and review gate
84
+
85
+ Transaction/control-plane packages contain reusable orchestration and runtime
86
+ mechanisms only; scenario business code belongs in its domain package. Before
87
+ implementation, every feature proposal must identify: domain persistence
88
+ ownership, the specialist and tools added or reused, stream versus non-stream
89
+ mode, the shared LLM entrypoint, and the full cookie identity path. Introducing
90
+ a second path in any category requires an explicit architecture decision
91
+ recorded in this section before code is written. Tests must enforce reuse of the
92
+ shared route and shared identity/tool path, and reviewers must reject violations
93
+ even when isolated feature tests pass.
94
+
95
+ Java sources follow the production project's responsibility-based packages:
96
+ MyBatis interfaces, application services, and timer entrypoints live in
97
+ `ark-control`; the Agent-only guard stays beside `AgentController`. Production
98
+ Shiro configuration, filters, Realm, and identity model are reused as-is.
99
+ Scheduled-task code must not introduce a parallel domain package containing its
100
+ own mapper, service, Web facade, production-authentication wrapper, or local mock.
101
+
102
+ ### Production integration boundary and review baseline
103
+
104
+ Confirmed by the project owner on 2026-09-08: this repository is a local
105
+ simulation and a source integration workspace. Its standalone JARs, POMs,
106
+ configuration files, and Dubbo XML are not deployed wholesale to production.
107
+ Selected business changes are integrated into the existing production projects.
108
+
109
+ - Production already has the correct, independently maintained database schema
110
+ and configuration. Local ignored `schema.sql` and `application.yml` files,
111
+ local schema initialization, and local scheduler defaults are not production
112
+ deployment inputs. Missing local files may affect clean-checkout testing,
113
+ but must not be reported as missing production schema/configuration or as
114
+ production scheduling being enabled without evidence from the integration.
115
+ - Production excludes all demo/mock implementations and their wiring. This
116
+ includes local sensitive crypto/auth substitutes, compatibility copies,
117
+ `SensitiveDataDemo*`, and `RunningAnalysisMockService*`. Production retains
118
+ its real crypto, authentication, and business services. Exclusion also covers
119
+ demo controller methods, constructor dependencies, mapper registrations, and
120
+ Dubbo references/exports; removing only the implementation files is insufficient.
121
+ - Production already has its own complete Dubbo logging. It does not import
122
+ `ProviderAccessLogFilter`, its SPI registration, or the local XML filter
123
+ setting. Findings in this local logger do not establish a production log leak.
124
+ Preserve the existing production logging implementation.
125
+ - Under this confirmed integration boundary, review findings F01, F03, F04,
126
+ F06, and F07 from the 2026-09-08 audit are not production release blockers.
127
+ Reopen them only if the actual integration starts importing the excluded
128
+ sources/configuration or contradicts these assumptions. Review the selected
129
+ production changeset, not a hypothetical deployment of this mock application.
130
+ - Implement new functionality against the production dependency versions;
131
+ do not downgrade the host to match the local build or upgrade the host just
132
+ to accommodate new code. Production reference sources are
133
+ `/Users/simon/code/restored/ark-web` and
134
+ `/Users/simon/code/restored/ark-control`; these are read-only reference inputs.
135
+ The Web parent POM specifies Boot 2.1.18.RELEASE, whose BOM manages Spring
136
+ 5.1.19.RELEASE. Its Jackson BOM override is 2.17.2; Shiro is 1.12.0 and Dubbo
137
+ is 2.6.9. The production `InterceptorConfig` already implements
138
+ `WebMvcConfigurer`; the overlay compiles against Spring 5.1.19. F02's
139
+ Spring-4-only compilation failure is not a production incompatibility.
140
+ - The restored Control snapshot currently contains only dao/common child
141
+ POMs, with versions inherited from an absent root `ark-control/pom.xml`.
142
+ Do not infer Control's Spring/MyBatis versions from the local simulation or
143
+ the Web parent. Obtain its root/effective POM before claiming full production
144
+ dependency compatibility. Host-specific dependency overrides take precedence
145
+ over a Boot version alone.
146
+
147
+ ### Cross-service timeout review
148
+
149
+ Inspect `/Users/simon/code/union-py-app` before assessing Agent timeout coverage.
150
+ Py owns Agent execution deadlines; Java transport timeouts are a separate
151
+ connection/resource safeguard, not another Agent lifecycle implementation.
152
+
153
+ `AGENT_MAX_RUN_SECONDS` is the shared execution duration, defaulting to 900
154
+ seconds. Set the same value in web, control, and py-app. Py's
155
+ `ExecutionCoordinator` applies it across streaming preparation and execution;
156
+ `sync_runs._sync_response` applies one `anyio.fail_after` scope across the shared
157
+ router and root Agent for both `/sync` and `/scheduled`, returning HTTP 504 with
158
+ `execution_timeout` on expiry. SSE comment keepalives remain every 15 seconds;
159
+ existing authentication, state, and tool HTTP timeouts remain unchanged.
160
+
161
+ Java Agent HTTP connect/read timeouts and the Agent proxy Dubbo reference use
162
+ this same duration (converted to milliseconds), instead of unbounded socket
163
+ waits or the historical 120-second RPC timeout. HTTP timeouts bound individual
164
+ socket waits; py remains responsible for the total execution deadline. No
165
+ second execution lifecycle is introduced. Preserve the scheduled stale-run
166
+ and token-expiry margins above that duration (defaults 930 and 960 seconds).
167
+ The F05 deadline gap is covered by sync/scheduled router, root, and combined
168
+ budget cancellation tests and Java silent-upstream tests.
169
+
170
+ ## Purpose
171
+
172
+ `ark-web` is the browser-facing control plane for the PydanticAI service;
173
+ `ark-control` is its trusted Dubbo provider, and `ark-control-facade` is their
174
+ shared serializable contract. Together they own authenticated
175
+ conversation/run state, standard AG-UI message
176
+ persistence, the official Memory store protocol adapter, and transparent AG-UI
177
+ SSE proxying.
178
+
179
+ ## Runtime
180
+
181
+ - Java 8
182
+ - Standalone simulation: Spring Boot 1.5.22.RELEASE / Spring 4.3.25.RELEASE
183
+ - Production Web: Spring Boot 2.1.18.RELEASE / Spring 5.1.19.RELEASE; use the
184
+ production POM overrides described above. Production Control version is
185
+ pending verification of its missing parent POM.
186
+ - MySQL 8
187
+ - Browser APIs remain under `/llm/**`
188
+ - Internal py-app APIs remain under `/agent/**`; they must not move under the
189
+ production-anonymous `/api/**` namespace. Interactive calls validate the CAS
190
+ session and scheduled calls use the dedicated token/context guard.
191
+
192
+ Only GET and POST endpoints are used in production.
193
+
194
+ Natural-language scheduled-task browser APIs also live under `/llm/**`.
195
+ They create `ONCE`, six-field Spring `CRON`, or fixed `INTERVAL` tasks and
196
+ expose task/run lists, unread results, and an explicit open-result action.
197
+ Draft generation is an ordinary browser call to `/llm/chatMessageSync`; there
198
+ is no scheduled-task-specific model endpoint.
199
+
200
+ ## Protocol
201
+
202
+ - `POST /llm/chatMessage` accepts the standard AG-UI `RunAgentInput`, creates
203
+ its persistence row, and transparently proxies AG-UI SSE.
204
+ - `GET /llm/conversationDetails` returns conversation metadata and a
205
+ `messages` array of standard AG-UI messages.
206
+ - `POST /llm/executionCancel` requires `conversationId` and `runId` and forwards
207
+ the owner-scoped cancellation request to py-app. Control does not maintain a
208
+ second cancellation state.
209
+ - `POST /llm/chatMessageSync` keeps the product `{content}` response.
210
+
211
+ There is no execution-current/recovery endpoint, legacy event translation,
212
+ SDK item API, heartbeat event, or dual-write path.
213
+
214
+ ## Persistence
215
+
216
+ `ai_conversation` owns only conversation metadata and status.
217
+
218
+ `ai_agent_execution` stores the root and child execution snapshots submitted by
219
+ py-app. Control does not enforce Agent concurrency or execution topology.
220
+
221
+ `ai_conversation_message` stores:
222
+
223
+ - message ID
224
+ - role and role-specific JSON payload
225
+ - an `agent_execution_id` foreign key
226
+ - a stable per-conversation sequence
227
+ - `delete_flag`
228
+
229
+ Browser history reconstructs each child execution as a standard AG-UI
230
+ `ActivityMessage`; model history contains root execution messages only.
231
+
232
+ Browser SSE disconnects forward cancellation to py-app. Py-app owns execution
233
+ timeouts, cancellation precedence, root/child lifecycle, and the final status;
234
+ control persists the final snapshot without maintaining a second execution
235
+ state machine.
236
+
237
+ `ai_memory_file` and `ai_memory_operation` implement PydanticAI Harness
238
+ `SearchableMemoryStore` semantics: bounded read/list/search, CAS versioning,
239
+ operation-ID idempotency, and operation fingerprint conflicts.
240
+
241
+ The local sensitive-data implementation mirrors the restored production
242
+ `AESInterceptor` and `SymmetricalSecurityUtils` contracts. Sensitive statements
243
+ route through general encryption, general decryption, or the AddressBook table
244
+ migration branch. The global reveal mode replaces sensitive plaintext fragments
245
+ with masks and short-lived Redis tokens. AddressBook result rows whose returned
246
+ `role` value is explicitly configured may remain plaintext; missing, unknown, or
247
+ invalid roles stay masked. The authenticated reveal API decrypts only the token's
248
+ fragment. The local simulation supplies a non-cryptographic `sensitiveProxy`
249
+ under the default or explicit `local-sensitive-mock` profile. This substitute
250
+ is excluded from production, which retains its existing real proxy.
251
+
252
+ `sensitive.reveal.strict_mode` defaults to false and must have the same value
253
+ on every Web and Control instance. When true, Control forces the existing
254
+ masking flow even if `sensitive.reveal.enabled` is false, and AddressBook ignores
255
+ its plaintext-role whitelist. Web requires a two-minute, user- and token-bound
256
+ image captcha before calling the unchanged reveal service. Captchas are generated
257
+ with the production Kaptcha `Producer` and stored only as an attribute of the
258
+ existing Shiro login session; no captcha Redis adapter, database table, or Dubbo
259
+ contract is added. A new image replaces the session's previous challenge and
260
+ each submission consumes it, including failed attempts. The local JVM lock does
261
+ not provide distributed atomic consumption: use session affinity for concurrent
262
+ requests, and do not claim cross-node exactly-once verification. The existing
263
+ production session DAO may itself use Redis; this feature does not replace it.
264
+ See `docs/sensitive-reveal-strict-mode.md` for the browser contract and rollout.
265
+
266
+ `agent_scheduled_task` and `agent_scheduled_task_run` persist task definitions
267
+ and individual outcomes. The task table owns the natural-language prompt,
268
+ schedule definition, state, timezone, and next due time. The run table owns one
269
+ scheduled occurrence, its execution state, stored result or safe error, unread
270
+ state, and optional materialized conversation ID. No additional token or
271
+ run-item table is involved.
272
+
273
+ The scheduled-task flow is:
274
+
275
+ 1. A MySQL-backed `@Scheduled` scanner locks due active tasks with
276
+ `FOR UPDATE SKIP LOCKED`, inserts a `PENDING` run, and advances the task's
277
+ next due time. The `(task_id, scheduled_at)` unique key prevents duplicate
278
+ occurrences across control instances. Claiming an `ONCE` task atomically
279
+ marks its definition `COMPLETED` while the run itself remains the source of
280
+ truth for `PENDING` / `RUNNING` / terminal progress, so pause/start cannot
281
+ enqueue the consumed occurrence again.
282
+ 2. Pending runs are moved to `RUNNING` and submitted to a bounded worker pool.
283
+ Control loads the prompt, owner, role, timezone, and effective occurrence
284
+ time, atomically issues a token, and calls py's scheduled execution endpoint
285
+ with `{}`. Control stores only the token hash and makes no permission
286
+ decision. Web's `/agent/scheduledTaskAuthorize` resolves the run, calls the
287
+ production `arkAuthService.queryResource` bean, and requires the
288
+ exact `/assistantManager/page` resource. Agent tools then use a non-Shiro
289
+ `@AgentPermission` guard with the returned trusted context; browser/CAS
290
+ requests continue to use the production Shiro Subject.
291
+ 3. Every terminal run is unread until opened. Successful py-app content is
292
+ stored as bounded JSON; failed runs retain a bounded, structured, user-safe
293
+ error code and message supplied by py-app. Neither
294
+ creates a conversation in the background.
295
+ 4. `scheduledTaskRunOpen` verifies the browser owner and locks the run. It
296
+ idempotently creates one normal AG-UI conversation with exactly two trusted
297
+ messages: the task prompt as the user message and the stored final content
298
+ as the assistant message. It marks the result read and returns the existing
299
+ conversation on repeated opens. Deleting that materialized conversation also
300
+ logically deletes its claimed run record, so run history never links to a
301
+ deleted conversation.
302
+
303
+ Only the successful result's `content` and `agentName` fields are persisted.
304
+ Provider messages and unknown response fields are discarded at the control
305
+ boundary. Failed callbacks accept only bounded error codes and single-line
306
+ user-safe messages produced by py-app; raw upstream bodies, stack traces, and
307
+ credentials are never persisted. Opening either terminal state creates the same standard
308
+ two-message conversation, so users can follow up on a result or a failure.
309
+
310
+ Every logical delete sets `delete_flag=0`; active rows use
311
+ `delete_flag=1`. The former message and personal-memory schemas are not
312
+ migrated or retained because the feature was not released.
313
+
314
+ ## Security
315
+
316
+ - `ark-web` authenticates browser callers before creating the JSON string passed
317
+ to `ark-control`; `ark-control` performs no authentication or permission
318
+ decision of its own, including while scheduling.
319
+ - Scheduled-task browser routes authenticate the caller's same-origin
320
+ `CASSESSIONID`; the controller never substitutes a synthetic browser identity.
321
+ Apache Shiro enforces `@RequiresPermissions("/assistantManager/page")` on every
322
+ CAS-protected API. The explicit `local-auth-mock` profile exercises the same
323
+ Filter/Realm/Subject path and mocks only the Realm's auth-service dependency;
324
+ production continues to use its unchanged Realm and Shiro configuration.
325
+ - Normal Agent, tool, history, completion, cancellation, sync, and Memory calls
326
+ forward and validate that `CASSESSIONID`.
327
+ - Scheduled execution has no browser session and must not derive or fabricate a
328
+ CAS Cookie from the task owner. Its token is checked against the active
329
+ RUNNING occurrence in Control; Agent tools use only the trusted context
330
+ returned by the dedicated authorization endpoint, recheck live resources, and
331
+ do not construct a Shiro Subject.
332
+ - The production Shiro Filter and Realm implementations remain unchanged. Both
333
+ SSO and non-SSO filter-chain maps place `/agent/** -> anon` before `/** -> authc`,
334
+ delegating this exact namespace to the fail-closed Agent guard. Missing
335
+ `@AgentPermission`, invalid credentials, mixed authentication, unavailable
336
+ Control, or unavailable role-resource lookup all deny the request. `/api/**`
337
+ anonymity is not used for Agent traffic.
338
+ - Conversation completion and cancellation validate user/conversation/run
339
+ ownership.
340
+ - Memory paths must begin with `<authenticated-user>/personal/`.
341
+ - Agent, Skill, memory namespace, model, provider, and frontend tools are not
342
+ accepted from browser configuration.
343
+
344
+ ## Scheduled-task configuration
345
+
346
+ - `SCHEDULED_TASK_ENABLED` controls claiming and execution in `ark-control` and
347
+ remains disabled until the database schema, dedicated Agent
348
+ authorization endpoint, production role-resource adapter, and py scheduled
349
+ adapter are deployed and cross-service security checks are complete.
350
+ - Worker tuning: `SCHEDULED_TASK_WORKER_THREADS` and
351
+ `SCHEDULED_TASK_MAX_RUN_SECONDS`.
352
+
353
+ The scanner runs every five seconds, claims at most 20 tasks per pass, queues
354
+ at most 32 worker submissions, and rejects schedules more frequent than once
355
+ per minute. These implementation limits are fixed in code. Configurable defaults
356
+ are defined locally in `ark-control/src/main/resources/application.yml`;
357
+ production uses its separately maintained configuration. Keep the
358
+ scheduler disabled while deploying or rolling back incompatible control and
359
+ py-app versions.
360
+
361
+ ## Scheduled-task deployment and rollback
362
+
363
+ Roll out in this order:
364
+
365
+ 1. Leave scheduling disabled and verify the existing production schema with
366
+ the DBA. Production already maintains the correct schema separately; the
367
+ local `ark-control/src/main/resources/schema.sql` is a simulation reference,
368
+ not a required production deployment input.
369
+ 2. Copy the three files from `ark-web/src/production-overlay/java/.../config`
370
+ over their matching production configurations. The two Shiro files differ
371
+ from the restored originals only by `/agent/** -> anon` before `/** -> authc`;
372
+ `InterceptorConfig` registers the Agent guard and excludes `/agent/**` from
373
+ the browser-only Referer, session-IP, and CSRF interceptors. Deploy the guard
374
+ using the existing `arkAuthService.queryResource` bean, then deploy the py
375
+ scheduled adapter while scheduling remains disabled.
376
+ 3. Deploy the web UI and verify draft, create, update, list, pause/start, and result
377
+ APIs while no background run can be claimed.
378
+ 4. Enable scheduling on control only after the cross-service contract and
379
+ database schema are verified.
380
+
381
+ Roll back in this order:
382
+
383
+ 1. Disable scheduling on every control instance and restart or drain them so
384
+ no new run is claimed; allow or explicitly terminate in-flight work.
385
+ 2. Roll back the web UI, then control, then py-app. The scheduled-task tables
386
+ may remain in place.
387
+ 3. If the tables must be removed, drop `agent_scheduled_task_run` first and
388
+ `agent_scheduled_task` second because of the foreign key.
389
+
390
+ ## Deployment boundary
391
+
392
+ Publish `ark-control-facade` first. `ark-control` registers provider services in
393
+ ZooKeeper, and `ark-web` consumes them. The SSE `chatMessage` path goes directly
394
+ from `ark-web` to py-app and immediately writes and flushes each upstream byte
395
+ chunk to the browser. Non-stream Agent operations remain synchronous Dubbo RPCs.
396
+
397
+ HTTP and Dubbo filters must not log Agent arguments or stream data. They include
398
+ the CAS cookie and model payload. During rollback, stop or
399
+ roll back `ark-web` consumers before rolling back `ark-control` or its facade.
400
+
401
+ ## Validation
402
+
403
+ ```bash
404
+ JAVA_HOME=/path/to/java8 mvn clean test
405
+ ```
package/README.md ADDED
@@ -0,0 +1,177 @@
1
+ # Ark Agent applications
2
+
3
+ This repository builds one headless application, one production Web integration
4
+ module, and one shared Dubbo contract artifact:
5
+
6
+ ```text
7
+ ark-control-facade/ serializable Dubbo interfaces shared by both applications
8
+ ark-control/ headless Dubbo provider, business services and database mappers
9
+ ark-web/ HTTP/SSE integration, Agent guard and Dubbo consumer
10
+ ```
11
+
12
+ `ark-web` authenticates HTTP callers and owns `HttpServletResponse`. Streaming
13
+ requests go directly from `ark-web` to py-app so SSE has no Dubbo hop.
14
+ `ark-control` owns persistence and provides the remaining synchronous RPCs.
15
+
16
+ The main scheduled-task paths are:
17
+
18
+ ```text
19
+ ark-control/src/main/java/com/union/control/mapper/ScheduledTaskMapper.java
20
+ ark-control/src/main/java/com/union/control/service/impl/ScheduledTaskServiceImpl.java
21
+ ark-control/src/main/java/com/union/control/schedule/ScheduledTaskScheduler.java
22
+ ark-control/src/main/java/com/union/control/schedule/ScheduledExecutionToken.java
23
+ ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentAuthorizationInterceptor.java
24
+ ark-web/src/main/java/com/epcc/arkweb/web/llm/AgentController.java
25
+ ```
26
+
27
+ Mapper, business service, token and scheduler code remain in `ark-control`;
28
+ browser authentication and the Agent-only permission guard remain in
29
+ `ark-web`.
30
+
31
+ The standalone module keeps the production class and bean names
32
+ `ShiroConfig`, `Realm`, `LoginFormFilter`, and `InterceptorConfig`, and follows
33
+ the same filter -> Realm -> Subject -> permission path. Its only local
34
+ replacement is the external `arkAuthService` bean under
35
+ `ark-web/src/local-mock/java`, inactive unless `local-auth-mock` is set.
36
+ Production keeps its existing Realm and filters.
37
+ The `ark-control` provider must not contain any Spring MVC controller.
38
+
39
+ Production Realm and Filter implementations are unchanged. The SSO and non-SSO
40
+ configuration copies under `ark-web/src/production-overlay/java` add
41
+ `/agent/** -> anon` before `/** -> authc`, delegating only that namespace to the
42
+ fail-closed Agent guard. Copy them over the matching production files; they are
43
+ not compiled as an additional local Shiro configuration. Browser/CAS requests still check
44
+ the existing Subject and permission; scheduled calls enter only
45
+ `/agent/scheduledTaskAuthorize`, and subsequent tools use `@AgentPermission`
46
+ with a Control-backed execution context. `/api/**` is not used as a workaround.
47
+
48
+ `ark-web` resolves the Shiro subject through `AuthContextHolder`, replaces
49
+ any client identity fields, and serializes the backend request through
50
+ `AuthenticatedRequest`. Every user-facing business service accepts that one JSON
51
+ string and never reads Shiro or a cookie. All browser/internal HTTP controllers, including
52
+ `LlmController`, `AgentController` and `ScheduledTaskController`, live in
53
+ `ark-web/src/main/java/com/epcc/arkweb/web/llm`. The scheduled controller
54
+ uses the shared Dubbo facade directly; no scheduled-package facade or local
55
+ adapter is introduced.
56
+
57
+ The applications share the following deployment configuration where relevant.
58
+
59
+ Configuration:
60
+
61
+ - `PY_APP_BASE_URL`
62
+ - `DUBBO_REGISTRY_ADDRESS`
63
+ - `DUBBO_PROTOCOL_PORT` (provider, default `20880`)
64
+ - `DUBBO_CONSUMER_CHECK` (consumer, default `false`)
65
+ - `DUBBO_DIRECT_URL` (consumer, optional; local direct-connect example:
66
+ `dubbo://ark-control.localhost:20880`)
67
+ - `DUBBO_CONSUMER_TIMEOUT_MS` (ordinary RPCs, default `10000`)
68
+ - `AGENT_MAX_RUN_SECONDS` (default `900`; set the same value on web, control,
69
+ and py-app; Agent HTTP connect/read and proxy RPC timeouts use this duration)
70
+ - `LOCAL_AUTHORIZED_ROLE_ID` (local `arkAuthService` mock only, default `role-1`)
71
+ - `SCHEDULED_TASK_ENABLED` (production keeps scheduling disabled until rollout
72
+ checks pass; the independently maintained local YAML may enable it)
73
+ - `SCHEDULED_TASK_WORKER_THREADS` (default `2`)
74
+ - `SCHEDULED_TASK_MAX_RUN_SECONDS` (default `930`)
75
+ - `SCHEDULED_TASK_TOKEN_TTL_SECONDS` (default `960`; must exceed the max run time)
76
+ - `MYSQL_URL`, `MYSQL_USER`, `MYSQL_PASSWORD` (`MYSQL_URL` must use a UTC connection timezone)
77
+ - `REDIS_HOST`, `REDIS_PORT`, `REDIS_PASSWORD`, `REDIS_TIMEOUT_MS`
78
+ - `SENSITIVE_REVEAL_ENABLED` (local application.properties defaults to `true`;
79
+ production supplies its own setting)
80
+ - `SENSITIVE_REVEAL_TTL_SECONDS` (default `300`)
81
+
82
+ For local Dubbo without ZooKeeper, set `DUBBO_REGISTRY_ADDRESS=N/A` on both
83
+ applications and set `DUBBO_DIRECT_URL=dubbo://ark-control.localhost:20880` on
84
+ `ark-web`. Do not use `127.0.0.1` in Dubbo 2.6.9 direct URLs: that version
85
+ rewrites loopback to the machine's preferred network address, which may be a
86
+ VPN interface. Remove any per-interface `-Dcom.union.control.service.*` direct
87
+ URL VM options because they override `DUBBO_DIRECT_URL`.
88
+
89
+ The unavailable production `sensitiveProxy` has an intentionally non-cryptographic
90
+ local stand-in. It is enabled for the default local profile and for the explicit
91
+ `local-sensitive-mock` profile. Production must activate its production profile
92
+ and keep the existing AES256 proxy binding.
93
+ The Maven `local-sensitive-compat` profile is active by default; when combining
94
+ it with another Maven profile, enable both explicitly with `-Pother,local-sensitive-compat`.
95
+
96
+ The unavailable production authentication service has one local substitute at
97
+ `ark-web/src/local-mock/java/com/epcc/arkweb/mock/ArkAuthServiceMock.java`,
98
+ enabled only by `--spring.profiles.active=local-auth-mock`. Local browser
99
+ requests still pass through Shiro's filter, Realm, Subject and permission
100
+ advisor. It automatically establishes a normal local Shiro Subject using
101
+ `user-1` / `local-only` (override with `LOCAL_USER_ID` and `LOCAL_PASSWORD`),
102
+ so no local login page is required. Scheduled headers skip this auto-login and
103
+ remain on the non-Shiro Agent guard. The mock grants `/assistantManager/page`
104
+ only to `LOCAL_AUTHORIZED_ROLE_ID`.
105
+
106
+ CAS browser routes reuse production Apache Shiro permission
107
+ `@RequiresPermissions("/assistantManager/page")`. Normal
108
+ control-to-py calls forward only the authenticated CAS session. If
109
+ `PY_APP_BASE_URL` is not loopback, use HTTPS with mTLS or an equivalent
110
+ authenticated service mesh.
111
+
112
+ Production already maintains its own correct schema and configuration.
113
+ `ark-control/src/main/resources/schema.sql` is the local AG-UI, Memory, and
114
+ scheduled-task simulation reference. These local files and standalone JARs are
115
+ not production deployment inputs; see PROJECT_OVERVIEW.md for the integration boundary.
116
+
117
+ Natural-language scheduled tasks use the two `agent_scheduled_task*` tables and
118
+ snapshot the authenticated CAS principal's `userId`, `orgCode`, and selected
119
+ Ark `roleId` on create and update.
120
+ The scheduler stores only the successful py-app result's `content` and
121
+ `agentName` on the run; opening an unread run atomically creates the normal
122
+ AG-UI conversation and messages.
123
+ Production uses its DBA-maintained schema and separately managed configuration.
124
+ Each occurrence receives a 256-bit, short-lived token while the database stores
125
+ only its SHA-256 hash. Control calls py `/agent/v1/runs/scheduled` with only that
126
+ token and `{}`; py calls `/agent/scheduledTaskAuthorize`, which resolves the
127
+ active run through Control and checks the role's real-time resources for the
128
+ exact `/assistantManager/page` URL. The response contains a database-backed
129
+ trusted context. Subsequent `/agent/*` tools must send that value in
130
+ `X-Agent-Trusted-Context`; the raw `Authorization: Scheduled ...` credential is
131
+ accepted only by the authorization endpoint. No scheduled Realm, synthetic
132
+ Subject, or tool endpoint copy is introduced.
133
+ Each user may keep at most 100 `ACTIVE` or `PAUSED` tasks.
134
+
135
+ The isolated sensitive-data demo mirrors the production database boundary.
136
+ `POST /api/sensitive/reveal/demo/insert` and
137
+ `GET /api/sensitive/reveal/demo/query` exercise field encryption on write and
138
+ masking on read. AddressBook demos use the `/demo/address-book/insert` and
139
+ `/demo/address-book/query` suffixes under `/api/sensitive/reveal`.
140
+ The reveal hook replaces plaintext fragments with masks and short-lived bearer
141
+ tokens. `POST /api/sensitive/reveal` requires the existing authenticated
142
+ permission and resolves the fragment token; this version does not bind tokens
143
+ to their owner's identity. Local authentication uses `local-auth-mock` and its
144
+ normal Shiro session, not a hard-coded CAS cookie. The crypto mock prefixes and
145
+ Base64-encodes data; it is not encryption. Production excludes the demo and mock
146
+ code and retains its own authentication, crypto, configuration, and Dubbo logs.
147
+
148
+ Set `sensitive.reveal.strict_mode=true` on both Web and Control to require a
149
+ Web-session image captcha before reveal and mask all AddressBook roles. Omission
150
+ or false preserves the original mode. The local environment variable is
151
+ `SENSITIVE_REVEAL_STRICT_MODE` in both applications. Browser clients read
152
+ `GET /api/sensitive/reveal/options`, fetch a PNG from
153
+ `POST /api/sensitive/reveal/captcha` with `{token}`, then submit
154
+ `{token, captchaCode}` to the existing reveal endpoint. See
155
+ [the strict-mode integration guide](docs/sensitive-reveal-strict-mode.md) for
156
+ error handling, session requirements, and production integration boundaries.
157
+
158
+ Streaming, sync, and scheduled py execution use `AGENT_MAX_RUN_SECONDS` (900
159
+ seconds by default). Sync and scheduled return HTTP 504 with `execution_timeout`
160
+ when the shared router-plus-Agent budget expires. Java uses the same setting
161
+ for finite HTTP connect/read waits and the Agent proxy Dubbo reference, replacing
162
+ the old fixed 120-second RPC wait. These are socket wait limits, not another
163
+ Agent lifecycle. Deploy the same value to all three processes; retain a larger
164
+ scheduled stale-run threshold and an even larger token TTL (defaults 930/960).
165
+
166
+ Run the Java 8 test suite with:
167
+
168
+ ```bash
169
+ mvn clean test
170
+ ```
171
+
172
+ Both applications are packaged independently with `mvn clean package`. Publish
173
+ `ark-control-facade` before deploying the provider and consumer. The facade
174
+ version used by `ark-control` and `ark-web` must match.
175
+
176
+ Do not install an HTTP or Dubbo filter that logs Agent arguments: they contain
177
+ the CAS cookie and model payload. Logs must also never contain SSE chunks.