create-windy 0.6.14 → 0.6.16

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 (162) hide show
  1. package/dist/cli.js +1008 -61
  2. package/package.json +1 -1
  3. package/template/.agents/skills/windy-business-module/SKILL.md +58 -8
  4. package/template/.windy-template.json +2 -2
  5. package/template/apps/server/src/agent/client-session/attempt-authority.test.ts +162 -0
  6. package/template/apps/server/src/agent/client-session/attempt-authority.ts +191 -0
  7. package/template/apps/server/src/agent/client-session/bootstrap.ts +27 -11
  8. package/template/apps/server/src/agent/client-session/completion.ts +75 -24
  9. package/template/apps/server/src/agent/client-session/contracts.ts +23 -0
  10. package/template/apps/server/src/agent/client-session/creation.ts +103 -0
  11. package/template/apps/server/src/agent/client-session/digest.ts +11 -1
  12. package/template/apps/server/src/agent/client-session/drizzle-records.ts +150 -0
  13. package/template/apps/server/src/agent/client-session/drizzle-repository.integration.test.ts +30 -3
  14. package/template/apps/server/src/agent/client-session/drizzle-repository.ts +23 -132
  15. package/template/apps/server/src/agent/client-session/finalization.ts +99 -0
  16. package/template/apps/server/src/agent/client-session/host-auth.ts +22 -10
  17. package/template/apps/server/src/agent/client-session/host-revocation-composition.test.ts +77 -0
  18. package/template/apps/server/src/agent/client-session/host-revocation-composition.ts +28 -0
  19. package/template/apps/server/src/agent/client-session/launch-authority.contract.test.ts +21 -0
  20. package/template/apps/server/src/agent/client-session/manifest-admission.ts +11 -0
  21. package/template/apps/server/src/agent/client-session/service.test-support.ts +22 -0
  22. package/template/apps/server/src/agent/client-session/service.ts +104 -134
  23. package/template/apps/server/src/agent/client-session/terminal-digest.test.ts +23 -0
  24. package/template/apps/server/src/agent/client-workflow-artifact/active-blob-references.ts +8 -0
  25. package/template/apps/server/src/agent/client-workflow-artifact/artifact-builder.ts +60 -0
  26. package/template/apps/server/src/agent/client-workflow-artifact/authority-current.ts +59 -0
  27. package/template/apps/server/src/agent/client-workflow-artifact/binding-registry.test.ts +189 -0
  28. package/template/apps/server/src/agent/client-workflow-artifact/binding-registry.ts +153 -0
  29. package/template/apps/server/src/agent/client-workflow-artifact/bootstrap.ts +77 -0
  30. package/template/apps/server/src/agent/client-workflow-artifact/cancellation.ts +47 -0
  31. package/template/apps/server/src/agent/client-workflow-artifact/completed-recovery.ts +31 -0
  32. package/template/apps/server/src/agent/client-workflow-artifact/contracts.ts +104 -0
  33. package/template/apps/server/src/agent/client-workflow-artifact/drizzle-locks.ts +30 -0
  34. package/template/apps/server/src/agent/client-workflow-artifact/drizzle-mapper.ts +138 -0
  35. package/template/apps/server/src/agent/client-workflow-artifact/drizzle-repository.ts +346 -0
  36. package/template/apps/server/src/agent/client-workflow-artifact/error.ts +21 -0
  37. package/template/apps/server/src/agent/client-workflow-artifact/handler.ts +167 -0
  38. package/template/apps/server/src/agent/client-workflow-artifact/host-revocation.ts +29 -0
  39. package/template/apps/server/src/agent/client-workflow-artifact/in-memory-repository.ts +232 -0
  40. package/template/apps/server/src/agent/client-workflow-artifact/job-port.ts +101 -0
  41. package/template/apps/server/src/agent/client-workflow-artifact/presentation.ts +41 -0
  42. package/template/apps/server/src/agent/client-workflow-artifact/recovery.contract.test.ts +253 -0
  43. package/template/apps/server/src/agent/client-workflow-artifact/release-binding.ts +85 -0
  44. package/template/apps/server/src/agent/client-workflow-artifact/repository.test.ts +204 -0
  45. package/template/apps/server/src/agent/client-workflow-artifact/repository.ts +97 -0
  46. package/template/apps/server/src/agent/client-workflow-artifact/routes-handler.contract.test.ts +201 -0
  47. package/template/apps/server/src/agent/client-workflow-artifact/routes.ts +161 -0
  48. package/template/apps/server/src/agent/client-workflow-artifact/runtime-composition.ts +101 -0
  49. package/template/apps/server/src/agent/client-workflow-artifact/scope-authority.ts +39 -0
  50. package/template/apps/server/src/agent/client-workflow-artifact/service-authorization.ts +117 -0
  51. package/template/apps/server/src/agent/client-workflow-artifact/service-support.ts +28 -0
  52. package/template/apps/server/src/agent/client-workflow-artifact/service.contract.test.ts +345 -0
  53. package/template/apps/server/src/agent/client-workflow-artifact/service.ts +350 -0
  54. package/template/apps/server/src/agent/client-workflow-artifact/session-boundary.ts +39 -0
  55. package/template/apps/server/src/agent/client-workflow-artifact/source-access.contract.test.ts +98 -0
  56. package/template/apps/server/src/agent/client-workflow-artifact/source-access.ts +80 -0
  57. package/template/apps/server/src/agent/client-workflow-artifact/takeover.ts +122 -0
  58. package/template/apps/server/src/agent/client-workflow-artifact/task-authority.contract.test.ts +97 -0
  59. package/template/apps/server/src/agent/client-workflow-artifact/task-authority.ts +147 -0
  60. package/template/apps/server/src/agent/client-workflow-artifact/terminal-authority.contract.test.ts +100 -0
  61. package/template/apps/server/src/agent/client-workflow-artifact/terminal-replay.ts +31 -0
  62. package/template/apps/server/src/agent/client-workflow-artifact/terminal-seal.ts +211 -0
  63. package/template/apps/server/src/agent/client-workflow-artifact/workflow-artifact.test-support.ts +173 -0
  64. package/template/apps/server/src/agent/client-workflow-artifact/workflow-loader.contract.test.ts +254 -0
  65. package/template/apps/server/src/agent/client-workflow-artifact/workflow-loader.ts +285 -0
  66. package/template/apps/server/src/agent/run-facade.test-support.ts +17 -4
  67. package/template/apps/server/src/agent/run-facade.test.ts +16 -0
  68. package/template/apps/server/src/agent/run-session-authorization.ts +12 -10
  69. package/template/apps/server/src/agent/runtime-bootstrap-input.ts +5 -1
  70. package/template/apps/server/src/agent/runtime-bootstrap.ts +33 -33
  71. package/template/apps/server/src/agent/runtime-public-ports.ts +40 -0
  72. package/template/apps/server/src/agent/session-invalidation/contracts.ts +25 -26
  73. package/template/apps/server/src/agent/session-projection-access.ts +31 -19
  74. package/template/apps/server/src/agent/unavailable-routes.ts +21 -0
  75. package/template/apps/server/src/agent/workflow-runtime-bootstrap.ts +2 -0
  76. package/template/apps/server/src/agent-workflow/artifact-source-authorization.ts +12 -6
  77. package/template/apps/server/src/agent-workflow/command-facade.ts +70 -11
  78. package/template/apps/server/src/agent-workflow/drizzle-artifact-port.ts +9 -9
  79. package/template/apps/server/src/agent-workflow/drizzle-delegation-completion.ts +1 -1
  80. package/template/apps/server/src/agent-workflow/platform-node-adapters.ts +14 -6
  81. package/template/apps/server/src/agent-workflow/project-launch-authority.contract.test.ts +53 -0
  82. package/template/apps/server/src/agent-workflow/project-launch-authority.ts +92 -0
  83. package/template/apps/server/src/agent-workflow/runtime-composition.ts +30 -14
  84. package/template/apps/server/src/app/agent-runtime-composition.ts +6 -1
  85. package/template/apps/server/src/app/client-agent-workflow-artifact-binding.ts +14 -0
  86. package/template/apps/server/src/application-services.ts +6 -0
  87. package/template/apps/server/src/data-access/scope-containment.test.ts +31 -0
  88. package/template/apps/server/src/data-access/scope-containment.ts +48 -0
  89. package/template/apps/server/src/jobs/artifact-publication.test.ts +192 -0
  90. package/template/apps/server/src/jobs/artifact-publication.ts +56 -0
  91. package/template/apps/server/src/jobs/artifact-publisher.ts +101 -0
  92. package/template/apps/server/src/jobs/drizzle-artifact.ts +73 -1
  93. package/template/apps/server/src/jobs/drizzle-execution-repository.ts +14 -1
  94. package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +65 -0
  95. package/template/apps/server/src/jobs/drizzle-repository.integration.test.ts +1 -0
  96. package/template/apps/server/src/jobs/execution-repository.ts +28 -0
  97. package/template/apps/server/src/jobs/execution.test.ts +3 -1
  98. package/template/apps/server/src/jobs/in-memory-execution.ts +16 -0
  99. package/template/apps/server/src/jobs/in-memory-lease.ts +45 -1
  100. package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +2 -0
  101. package/template/apps/server/src/jobs/postgres-cleanup-contract.test.ts +58 -0
  102. package/template/apps/server/src/jobs/worker.ts +8 -14
  103. package/template/apps/server/src/module-host/background-ai-unavailable.ts +18 -0
  104. package/template/apps/server/src/module-host/durable-context.ts +19 -22
  105. package/template/apps/server/src/module-host/implementation-completeness.test.ts +80 -1
  106. package/template/apps/server/src/module-host/implementation-completeness.ts +28 -1
  107. package/template/apps/server/src/module-host/request-context.ts +32 -8
  108. package/template/apps/server/src/module-host/storage-port.ts +3 -0
  109. package/template/apps/server/src/module-host/workflow-input-artifact-port.test.ts +104 -0
  110. package/template/apps/server/src/module-host/workflow-input-artifact-port.ts +229 -0
  111. package/template/apps/server/src/module-host/workflow-input-materialization.test.ts +163 -0
  112. package/template/apps/server/src/module-host/workflow-input-materialization.ts +161 -0
  113. package/template/apps/server/src/workflow/drizzle-repository.integration.test.ts +1 -0
  114. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.vue +2 -1
  115. package/template/apps/web/src/pages/system/components/ai-runtime/AiRuntimeFilters.webtest.ts +16 -0
  116. package/template/docs/architecture/agent-workflow.md +13 -2
  117. package/template/docs/architecture/durable-jobs.md +7 -0
  118. package/template/docs/operations/agent-session-memory.md +2 -1
  119. package/template/docs/operations/agent-session-troubleshooting.md +87 -2
  120. package/template/docs/operations/agent-workflow-recovery.md +25 -14
  121. package/template/docs/platform/client-agent-session.md +148 -2
  122. package/template/packages/ai-client/package.json +2 -1
  123. package/template/packages/ai-client/src/client-agent/index.ts +1 -0
  124. package/template/packages/ai-client/src/client-agent/workflow-artifact/contracts.ts +85 -0
  125. package/template/packages/ai-client/src/client-agent/workflow-artifact/http.test.ts +49 -0
  126. package/template/packages/ai-client/src/client-agent/workflow-artifact/http.ts +82 -0
  127. package/template/packages/ai-client/src/client-agent/workflow-artifact/index.ts +3 -0
  128. package/template/packages/ai-client/src/client-agent/workflow-artifact/validation.test.ts +154 -0
  129. package/template/packages/ai-client/src/client-agent/workflow-artifact/validation.ts +332 -0
  130. package/template/packages/database/drizzle/0062_client_agent_workflow_artifact.sql +63 -0
  131. package/template/packages/database/drizzle/meta/0062_snapshot.json +14839 -0
  132. package/template/packages/database/drizzle/meta/_journal.json +7 -0
  133. package/template/packages/database/package.json +1 -1
  134. package/template/packages/database/src/agent-runtime-schema.test.ts +177 -0
  135. package/template/packages/database/src/schema/client-agent-workflow-artifact.ts +158 -0
  136. package/template/packages/database/src/schema/index.ts +1 -0
  137. package/template/packages/jobs/package.json +1 -1
  138. package/template/packages/jobs/src/handlers.test.ts +10 -0
  139. package/template/packages/jobs/src/handlers.ts +28 -11
  140. package/template/packages/modules/package.json +1 -1
  141. package/template/packages/modules/src/ai-client-execution-policy.ts +6 -0
  142. package/template/packages/modules/src/manifest.ts +2 -0
  143. package/template/packages/modules/src/system-agent-audit-actions.ts +3 -0
  144. package/template/packages/modules/src/system-ai-api-bindings.ts +15 -0
  145. package/template/packages/modules/src/system-audit-actions.ts +15 -0
  146. package/template/packages/server-sdk/index.ts +52 -0
  147. package/template/packages/server-sdk/package.json +5 -1
  148. package/template/packages/server-sdk/src/client-agent-session.ts +13 -1
  149. package/template/packages/server-sdk/src/client-agent-workflow-artifact.contract.test.ts +113 -0
  150. package/template/packages/server-sdk/src/client-agent-workflow-artifact.ts +183 -0
  151. package/template/packages/server-sdk/src/durable-handler.ts +7 -0
  152. package/template/packages/server-sdk/src/request-context.ts +7 -0
  153. package/template/packages/server-sdk/src/workflow-input-artifact-port.ts +104 -0
  154. package/template/packages/server-sdk/src/workflow-input-artifact-validation.contract.test.ts +124 -0
  155. package/template/packages/server-sdk/src/workflow-input-artifact-validation.ts +347 -0
  156. package/template/packages/server-sdk/src/workflow-input-materialization-handler.ts +43 -0
  157. package/template/packages/storage/package.json +1 -1
  158. package/template/packages/storage/src/blob-store-put-once.test.ts +70 -0
  159. package/template/packages/storage/src/blob-store.ts +9 -0
  160. package/template/packages/storage/src/local-file-blob-put-once.ts +120 -0
  161. package/template/packages/storage/src/local-file-blob-store.ts +53 -1
  162. package/template/packages/storage/src/memory-blob-store.ts +73 -33
@@ -0,0 +1,153 @@
1
+ import {
2
+ CLIENT_AGENT_WORKFLOW_ARTIFACT_BINDING_LIMITS as limits,
3
+ CLIENT_AGENT_WORKFLOW_SCOPE_CONTAINMENT_CONTRACT_VERSION,
4
+ } from "@southwind-ai/server-sdk";
5
+ import { validDigest } from "../client-session/digest.js";
6
+ import type { ClientAgentWorkflowArtifactBinding } from "./contracts.js";
7
+
8
+ export class ClientAgentWorkflowArtifactBindingRegistry {
9
+ readonly #bindings: ReadonlyMap<string, ClientAgentWorkflowArtifactBinding>;
10
+
11
+ constructor(bindings: readonly ClientAgentWorkflowArtifactBinding[]) {
12
+ const values = new Map<string, ClientAgentWorkflowArtifactBinding>();
13
+ const workflowKeys = new Set<string>();
14
+ for (const binding of bindings) {
15
+ assertBinding(binding);
16
+ const key = coordinate(binding.workflowKey, binding.nodeId);
17
+ if (values.has(key) || workflowKeys.has(binding.workflowKey)) {
18
+ throw new Error("CLIENT_AGENT_WORKFLOW_BINDING_DUPLICATE");
19
+ }
20
+ workflowKeys.add(binding.workflowKey);
21
+ values.set(key, freezeBinding(binding));
22
+ }
23
+ this.#bindings = values;
24
+ }
25
+
26
+ require(workflowKey: string, nodeId: string) {
27
+ const binding = this.#bindings.get(coordinate(workflowKey, nodeId));
28
+ if (!binding) throw new Error("CLIENT_AGENT_WORKFLOW_BINDING_MISSING");
29
+ return binding;
30
+ }
31
+
32
+ findWorkflow(workflowKey: string) {
33
+ return [...this.#bindings.values()].find(
34
+ (binding) => binding.workflowKey === workflowKey,
35
+ );
36
+ }
37
+
38
+ get size() {
39
+ return this.#bindings.size;
40
+ }
41
+ }
42
+
43
+ function coordinate(workflowKey: string, nodeId: string) {
44
+ return `${workflowKey}\0${nodeId}`;
45
+ }
46
+
47
+ function assertBinding(binding: ClientAgentWorkflowArtifactBinding) {
48
+ if (
49
+ !bounded(binding.workflowKey, limits.workflowKey) ||
50
+ !bounded(binding.nodeId, limits.nodeId) ||
51
+ !bounded(binding.operationKey, limits.operationKey) ||
52
+ !bounded(binding.releaseDigest, limits.releaseDigest) ||
53
+ !validDigest(binding.releaseDigest) ||
54
+ binding.scopeContainmentContractVersion !==
55
+ CLIENT_AGENT_WORKFLOW_SCOPE_CONTAINMENT_CONTRACT_VERSION ||
56
+ !bounded(
57
+ binding.terminalResultAuthority.version,
58
+ limits.terminalAuthorityVersion,
59
+ ) ||
60
+ !bounded(
61
+ binding.terminalResultAuthority.digest,
62
+ limits.terminalAuthorityDigest,
63
+ ) ||
64
+ !validDigest(binding.terminalResultAuthority.digest) ||
65
+ typeof binding.terminalResultAuthority.canonicalize !== "function" ||
66
+ !bounded(
67
+ binding.clientTaskAuthority.version,
68
+ limits.terminalAuthorityVersion,
69
+ ) ||
70
+ !bounded(
71
+ binding.clientTaskAuthority.digest,
72
+ limits.terminalAuthorityDigest,
73
+ ) ||
74
+ !validDigest(binding.clientTaskAuthority.digest) ||
75
+ typeof binding.clientTaskAuthority.build !== "function" ||
76
+ !bounded(
77
+ binding.launchAuthority.version,
78
+ limits.terminalAuthorityVersion,
79
+ ) ||
80
+ !bounded(binding.launchAuthority.digest, limits.terminalAuthorityDigest) ||
81
+ !validDigest(binding.launchAuthority.digest) ||
82
+ typeof binding.launchAuthority.authorizeRelease !== "function" ||
83
+ typeof binding.launchAuthority.authorizeRun !== "function" ||
84
+ !bounded(binding.artifact.kind, limits.artifactKind) ||
85
+ !bounded(binding.artifact.contentType, limits.artifactContentType) ||
86
+ !bounded(binding.artifact.filename, limits.artifactFilename) ||
87
+ binding.artifact.filename.includes("/") ||
88
+ binding.artifact.filename.includes("\\") ||
89
+ !bounded(binding.artifact.retentionPolicyKey, limits.retentionPolicyKey) ||
90
+ binding.artifact.contentType !== "application/json" ||
91
+ !Number.isSafeInteger(binding.artifact.maxByteSize) ||
92
+ binding.artifact.maxByteSize < 1 ||
93
+ binding.artifact.maxByteSize > limits.maxByteSize
94
+ ) {
95
+ throw new Error("CLIENT_AGENT_WORKFLOW_BINDING_INVALID");
96
+ }
97
+ }
98
+
99
+ export function clientWorkflowBindingCommitment(
100
+ binding: ClientAgentWorkflowArtifactBinding,
101
+ ) {
102
+ return {
103
+ workflowKey: binding.workflowKey,
104
+ nodeId: binding.nodeId,
105
+ operationKey: binding.operationKey,
106
+ releaseDigest: binding.releaseDigest,
107
+ scopeContainmentContractVersion: binding.scopeContainmentContractVersion,
108
+ terminalResultAuthority: {
109
+ version: binding.terminalResultAuthority.version,
110
+ digest: binding.terminalResultAuthority.digest,
111
+ },
112
+ clientTaskAuthority: {
113
+ version: binding.clientTaskAuthority.version,
114
+ digest: binding.clientTaskAuthority.digest,
115
+ },
116
+ launchAuthority: {
117
+ version: binding.launchAuthority.version,
118
+ digest: binding.launchAuthority.digest,
119
+ },
120
+ artifact: { ...binding.artifact },
121
+ } as const;
122
+ }
123
+
124
+ function freezeBinding(
125
+ binding: ClientAgentWorkflowArtifactBinding,
126
+ ): ClientAgentWorkflowArtifactBinding {
127
+ return Object.freeze({
128
+ ...clientWorkflowBindingCommitment(binding),
129
+ terminalResultAuthority: Object.freeze({
130
+ ...clientWorkflowBindingCommitment(binding).terminalResultAuthority,
131
+ canonicalize: binding.terminalResultAuthority.canonicalize,
132
+ }),
133
+ clientTaskAuthority: Object.freeze({
134
+ ...clientWorkflowBindingCommitment(binding).clientTaskAuthority,
135
+ build: binding.clientTaskAuthority.build,
136
+ }),
137
+ launchAuthority: Object.freeze({
138
+ ...clientWorkflowBindingCommitment(binding).launchAuthority,
139
+ authorizeRelease: binding.launchAuthority.authorizeRelease,
140
+ authorizeRun: binding.launchAuthority.authorizeRun,
141
+ }),
142
+ artifact: Object.freeze({ ...binding.artifact }),
143
+ });
144
+ }
145
+
146
+ function bounded(value: string, maxLength: number) {
147
+ return (
148
+ value.length > 0 &&
149
+ value.length <= maxLength &&
150
+ value === value.trim() &&
151
+ !value.includes(String.fromCharCode(0))
152
+ );
153
+ }
@@ -0,0 +1,77 @@
1
+ import type { ModuleClientAgentWorkflowArtifactProjectBinding } from "@southwind-ai/server-sdk";
2
+ import type { NodePgDatabase } from "drizzle-orm/node-postgres";
3
+ import type { RouteApp } from "../../system/routes.js";
4
+ import type { DurableHandlerRegistry } from "@southwind-ai/jobs";
5
+ import type { BlobStore } from "@southwind-ai/storage";
6
+ import type { StaticRouteAuthorizer } from "../../module-host/static-route-authorization.js";
7
+ import { createManifestAuthorizedRouteApp } from "../../module-host/static-route-authorization.js";
8
+ import type { DurableJobService } from "../../jobs/service.js";
9
+ import type { GovernedClientAgentSessionService } from "../client-session/service.js";
10
+ import type { ClientSessionRepository } from "../client-session/repository.js";
11
+ import { DrizzleAgentWorkflowRepository } from "../../agent-workflow/drizzle-repository.js";
12
+ import { ClientAgentWorkflowArtifactBindingRegistry } from "./binding-registry.js";
13
+ import { DrizzleClientAgentWorkflowArtifactRepository } from "./drizzle-repository.js";
14
+ import { registerClientAgentWorkflowArtifactHandler } from "./handler.js";
15
+ import { registerClientAgentWorkflowArtifactRoutes } from "./routes.js";
16
+ import { GovernedClientAgentWorkflowArtifactService } from "./service.js";
17
+ import { ClientAgentWorkflowArtifactSourceAccess } from "./source-access.js";
18
+ import { ClientAgentWorkflowFactsLoader } from "./workflow-loader.js";
19
+ import { ClientAgentWorkflowTaskAuthority } from "./task-authority.js";
20
+
21
+ export function createClientAgentWorkflowArtifactRuntime(input: {
22
+ readonly db?: NodePgDatabase;
23
+ readonly hostApp?: RouteApp;
24
+ readonly routeAuthorizer: StaticRouteAuthorizer;
25
+ readonly bindings?: readonly ModuleClientAgentWorkflowArtifactProjectBinding[];
26
+ readonly sessions?: GovernedClientAgentSessionService;
27
+ readonly sessionRepository?: ClientSessionRepository;
28
+ readonly jobs: DurableJobService;
29
+ readonly jobRegistry: DurableHandlerRegistry;
30
+ readonly blobs: BlobStore;
31
+ }) {
32
+ if (
33
+ !input.db ||
34
+ !input.hostApp ||
35
+ !input.sessions ||
36
+ !input.sessionRepository ||
37
+ !input.bindings?.length
38
+ ) {
39
+ return undefined;
40
+ }
41
+ const bindings = new ClientAgentWorkflowArtifactBindingRegistry(
42
+ input.bindings,
43
+ );
44
+ const repository = new DrizzleClientAgentWorkflowArtifactRepository(input.db);
45
+ const loader = new ClientAgentWorkflowFactsLoader(
46
+ new DrizzleAgentWorkflowRepository(input.db),
47
+ input.jobs,
48
+ bindings,
49
+ );
50
+ const tasks = new ClientAgentWorkflowTaskAuthority();
51
+ const service = new GovernedClientAgentWorkflowArtifactService(
52
+ repository,
53
+ loader,
54
+ input.sessions,
55
+ input.sessionRepository,
56
+ input.blobs,
57
+ tasks,
58
+ );
59
+ registerClientAgentWorkflowArtifactHandler({
60
+ registry: input.jobRegistry,
61
+ service,
62
+ });
63
+ registerClientAgentWorkflowArtifactRoutes(
64
+ createManifestAuthorizedRouteApp(input.hostApp, input.routeAuthorizer),
65
+ service,
66
+ );
67
+ return {
68
+ service,
69
+ sourceAccess: new ClientAgentWorkflowArtifactSourceAccess(
70
+ repository,
71
+ input.jobs,
72
+ ),
73
+ bindTaskArtifacts: (
74
+ artifacts: import("../../agent-workflow/node-execution-ports.js").AgentWorkflowArtifactPort,
75
+ ) => tasks.bind(artifacts),
76
+ };
77
+ }
@@ -0,0 +1,47 @@
1
+ import type { GovernedClientAgentSessionService } from "../client-session/service.js";
2
+ import type { ClientAgentWorkflowArtifactRecord } from "./contracts.js";
3
+ import type { ClientAgentWorkflowArtifactRepository } from "./repository.js";
4
+ import { clientWorkflowSessionCall } from "./session-boundary.js";
5
+
6
+ export async function cancelClientWorkflowArtifact(input: {
7
+ record: ClientAgentWorkflowArtifactRecord;
8
+ repository: ClientAgentWorkflowArtifactRepository;
9
+ sessions: GovernedClientAgentSessionService;
10
+ cause: string;
11
+ now: number;
12
+ }) {
13
+ const requested = await input.repository.requestCancel({
14
+ workId: input.record.workId,
15
+ now: input.now,
16
+ });
17
+ if (!requested || requested.status === "completed") return undefined;
18
+ if (requested.status === "cancelled") return requested;
19
+ if (requested.clientSessionId) {
20
+ await clientWorkflowSessionCall(
21
+ input.sessions.revokeSession(requested.clientSessionId, input.cause),
22
+ "revoke",
23
+ );
24
+ }
25
+ return input.repository.finalizeCancel({
26
+ workId: requested.workId,
27
+ now: input.now,
28
+ });
29
+ }
30
+
31
+ export async function recoverClientWorkflowCancellations(input: {
32
+ repository: ClientAgentWorkflowArtifactRepository;
33
+ sessions: GovernedClientAgentSessionService;
34
+ now: () => number;
35
+ }) {
36
+ const records = await input.repository.listCancellationRequested();
37
+ for (const record of records) {
38
+ await cancelClientWorkflowArtifact({
39
+ record,
40
+ repository: input.repository,
41
+ sessions: input.sessions,
42
+ cause: "workflow-cancel-recovery",
43
+ now: input.now(),
44
+ });
45
+ }
46
+ return records.length;
47
+ }
@@ -0,0 +1,31 @@
1
+ import type { BlobStore } from "@southwind-ai/storage";
2
+ import type { ClientSessionRepository } from "../client-session/repository.js";
3
+ import type { ClientAgentWorkflowArtifactRecord } from "./contracts.js";
4
+ import type { ClientAgentWorkflowArtifactRepository } from "./repository.js";
5
+ import { workflowArtifactConflict as conflict } from "./service-support.js";
6
+ import { sealCompletedClientWorkflowSession } from "./terminal-seal.js";
7
+ import type { ClientAgentWorkflowFactsLoader } from "./workflow-loader.js";
8
+
9
+ export async function recoverCompletedClientWorkflowArtifact(input: {
10
+ current: ClientAgentWorkflowArtifactRecord;
11
+ signal: AbortSignal;
12
+ sessionRepository: ClientSessionRepository;
13
+ repository: ClientAgentWorkflowArtifactRepository;
14
+ loader: ClientAgentWorkflowFactsLoader;
15
+ blobs: BlobStore;
16
+ now: () => number;
17
+ }) {
18
+ const { current } = input;
19
+ if (current.status !== "claimed" || !current.clientSessionId) return current;
20
+ const session = await input.sessionRepository.find(current.clientSessionId);
21
+ if (!session || session.status !== "completed") return current;
22
+ if (!session.terminalDigest || !current.claimTokenHash) {
23
+ throw conflict("已完成 Client Agent Session 缺少稳定终态摘要");
24
+ }
25
+ return sealCompletedClientWorkflowSession({
26
+ ...input,
27
+ session,
28
+ terminalRequestDigest: session.terminalDigest,
29
+ claimTokenHash: current.claimTokenHash,
30
+ });
31
+ }
@@ -0,0 +1,104 @@
1
+ import type {
2
+ ModuleAiJsonValue,
3
+ ModuleClientAgentWorkflowArtifactProjectBinding,
4
+ ModuleClientAgentWorkflowExecutionTask,
5
+ } from "@southwind-ai/server-sdk";
6
+ import type { ArtifactRef } from "@southwind-ai/storage";
7
+ import type { AuthorizationScopeSnapshot } from "@southwind-ai/shared";
8
+
9
+ export const clientAgentWorkflowArtifactTaskKey =
10
+ "system.agent-workflow.client-agent-artifact";
11
+ export const clientAgentWorkflowArtifactSourceModule = "system.agent-workflow";
12
+ export const clientAgentWorkflowArtifactSourceKind = "client-agent-artifact";
13
+
14
+ export type ClientAgentWorkflowArtifactBinding =
15
+ ModuleClientAgentWorkflowArtifactProjectBinding;
16
+
17
+ export type ClientAgentWorkflowArtifactStatus =
18
+ | "pending"
19
+ | "claimed"
20
+ | "cancellation-requested"
21
+ | "completed"
22
+ | "cancelled";
23
+
24
+ export interface ClientAgentWorkflowArtifactRecord {
25
+ readonly workId: string;
26
+ readonly commandId: string;
27
+ readonly durableJobId: string;
28
+ readonly runId: string;
29
+ readonly nodeRunId: string;
30
+ readonly effectKind: "job";
31
+ readonly taskKey: typeof clientAgentWorkflowArtifactTaskKey;
32
+ readonly operationKey: string;
33
+ readonly bindingDigest: `sha256:${string}`;
34
+ readonly actorId: string;
35
+ readonly scopeDigest: `sha256:${string}`;
36
+ readonly authorizationSnapshotDigest: `sha256:${string}`;
37
+ readonly runPolicyDigest: `sha256:${string}`;
38
+ readonly requestDigest: `sha256:${string}`;
39
+ readonly inputDigest: `sha256:${string}`;
40
+ readonly effectGeneration: number;
41
+ readonly effectFenceTokenDigest: `sha256:${string}`;
42
+ readonly status: ClientAgentWorkflowArtifactStatus;
43
+ readonly claimGeneration: number;
44
+ readonly clientBindingDigest?: `sha256:${string}`;
45
+ readonly installationId?: string;
46
+ readonly hostSessionDigest?: `sha256:${string}`;
47
+ readonly clientSessionId?: string;
48
+ readonly claimTokenHash?: `sha256:${string}`;
49
+ readonly claimExpiresAt?: number;
50
+ readonly historySequence: number;
51
+ readonly historyHeadDigest?: `sha256:${string}`;
52
+ readonly terminalReceiptId?: string;
53
+ readonly terminalReceiptDigest?: `sha256:${string}`;
54
+ readonly terminalRequestDigest?: `sha256:${string}`;
55
+ readonly terminalResult?: Readonly<Record<string, unknown>>;
56
+ readonly artifact?: ClientAgentWorkflowStoredArtifact;
57
+ readonly cancelRequestedAt?: number;
58
+ readonly finishedAt?: number;
59
+ readonly version: number;
60
+ readonly createdAt: number;
61
+ readonly updatedAt: number;
62
+ }
63
+
64
+ export interface ClientAgentWorkflowStoredArtifact {
65
+ readonly artifactRef: ArtifactRef;
66
+ readonly filename: string;
67
+ readonly contentType: string;
68
+ readonly byteSize: number;
69
+ readonly sha256: string;
70
+ }
71
+
72
+ export interface ClientAgentWorkflowBeginFacts {
73
+ readonly record: ClientAgentWorkflowArtifactRecord;
74
+ readonly input: ModuleAiJsonValue;
75
+ readonly binding: ClientAgentWorkflowArtifactBinding;
76
+ /** Immutable authorization captured by the real Durable Job at Run launch. */
77
+ readonly frozenScope: AuthorizationScopeSnapshot;
78
+ readonly inputArtifacts: readonly import("@southwind-ai/shared").AgentWorkflowArtifactRef[];
79
+ }
80
+
81
+ export type ClientAgentWorkflowExecutionTask =
82
+ ModuleClientAgentWorkflowExecutionTask;
83
+
84
+ export interface ClientAgentWorkflowClaimBinding {
85
+ readonly clientBindingDigest: `sha256:${string}`;
86
+ readonly installationId: string;
87
+ readonly hostSessionDigest: `sha256:${string}`;
88
+ readonly claimTokenHash: `sha256:${string}`;
89
+ readonly claimExpiresAt: number;
90
+ readonly now: number;
91
+ }
92
+
93
+ export interface ClientAgentWorkflowTerminalInput {
94
+ readonly claimGeneration: number;
95
+ readonly clientSessionId: string;
96
+ readonly historySequence: number;
97
+ readonly historyHeadDigest: `sha256:${string}`;
98
+ readonly terminalReceiptId: string;
99
+ readonly terminalReceiptDigest: `sha256:${string}`;
100
+ readonly terminalRequestDigest: `sha256:${string}`;
101
+ readonly terminalResult: Readonly<Record<string, unknown>>;
102
+ readonly artifact: ClientAgentWorkflowStoredArtifact;
103
+ readonly now: number;
104
+ }
@@ -0,0 +1,30 @@
1
+ import { clientAgentWorkflowArtifacts } from "@southwind-ai/database";
2
+ import { eq } from "drizzle-orm";
3
+ import type { NodePgDatabase } from "drizzle-orm/node-postgres";
4
+ import { toClientWorkflowArtifact } from "./drizzle-mapper.js";
5
+
6
+ export async function lockClientWorkflowArtifact(
7
+ db: NodePgDatabase,
8
+ workId: string,
9
+ ) {
10
+ const [row] = await db
11
+ .select()
12
+ .from(clientAgentWorkflowArtifacts)
13
+ .where(eq(clientAgentWorkflowArtifacts.workId, workId))
14
+ .for("update")
15
+ .limit(1);
16
+ return row ? toClientWorkflowArtifact(row) : undefined;
17
+ }
18
+
19
+ export async function lockClientWorkflowArtifactBySession(
20
+ db: NodePgDatabase,
21
+ clientSessionId: string,
22
+ ) {
23
+ const [row] = await db
24
+ .select()
25
+ .from(clientAgentWorkflowArtifacts)
26
+ .where(eq(clientAgentWorkflowArtifacts.clientSessionId, clientSessionId))
27
+ .for("update")
28
+ .limit(1);
29
+ return row ? toClientWorkflowArtifact(row) : undefined;
30
+ }
@@ -0,0 +1,138 @@
1
+ import { clientAgentWorkflowArtifacts } from "@southwind-ai/database";
2
+ import type { ArtifactRef } from "@southwind-ai/storage";
3
+ import type { ClientAgentWorkflowArtifactRecord } from "./contracts.js";
4
+
5
+ type Row = typeof clientAgentWorkflowArtifacts.$inferSelect;
6
+
7
+ export function clientWorkflowArtifactRow(
8
+ value: ClientAgentWorkflowArtifactRecord,
9
+ ): typeof clientAgentWorkflowArtifacts.$inferInsert {
10
+ return {
11
+ workId: value.workId,
12
+ commandId: value.commandId,
13
+ durableJobId: value.durableJobId,
14
+ runId: value.runId,
15
+ nodeRunId: value.nodeRunId,
16
+ effectKind: value.effectKind,
17
+ taskKey: value.taskKey,
18
+ operationKey: value.operationKey,
19
+ bindingDigest: value.bindingDigest,
20
+ actorId: value.actorId,
21
+ scopeDigest: value.scopeDigest,
22
+ authorizationSnapshotDigest: value.authorizationSnapshotDigest,
23
+ runPolicyDigest: value.runPolicyDigest,
24
+ requestDigest: value.requestDigest,
25
+ inputDigest: value.inputDigest,
26
+ effectGeneration: value.effectGeneration,
27
+ effectFenceTokenDigest: value.effectFenceTokenDigest,
28
+ status: value.status,
29
+ claimGeneration: value.claimGeneration,
30
+ clientBindingDigest: value.clientBindingDigest ?? null,
31
+ installationId: value.installationId ?? null,
32
+ hostSessionDigest: value.hostSessionDigest ?? null,
33
+ clientSessionId: value.clientSessionId ?? null,
34
+ claimTokenHash: value.claimTokenHash ?? null,
35
+ claimExpiresAt: value.claimExpiresAt ?? null,
36
+ historySequence: value.historySequence,
37
+ historyHeadDigest: value.historyHeadDigest ?? null,
38
+ terminalReceiptId: value.terminalReceiptId ?? null,
39
+ terminalReceiptDigest: value.terminalReceiptDigest ?? null,
40
+ terminalRequestDigest: value.terminalRequestDigest ?? null,
41
+ terminalResultJson: value.terminalResult ?? null,
42
+ artifactRef: value.artifact?.artifactRef ?? null,
43
+ artifactFilename: value.artifact?.filename ?? null,
44
+ artifactContentType: value.artifact?.contentType ?? null,
45
+ artifactByteSize: value.artifact?.byteSize ?? null,
46
+ artifactSha256: value.artifact?.sha256 ?? null,
47
+ cancelRequestedAt: value.cancelRequestedAt ?? null,
48
+ finishedAt: value.finishedAt ?? null,
49
+ version: value.version,
50
+ createdAt: value.createdAt,
51
+ updatedAt: value.updatedAt,
52
+ };
53
+ }
54
+
55
+ export function toClientWorkflowArtifact(
56
+ row: Row,
57
+ ): ClientAgentWorkflowArtifactRecord {
58
+ const artifact =
59
+ row.artifactRef &&
60
+ row.artifactFilename &&
61
+ row.artifactContentType &&
62
+ row.artifactByteSize !== null &&
63
+ row.artifactSha256
64
+ ? {
65
+ artifactRef: row.artifactRef as ArtifactRef,
66
+ filename: row.artifactFilename,
67
+ contentType: row.artifactContentType,
68
+ byteSize: row.artifactByteSize,
69
+ sha256: row.artifactSha256,
70
+ }
71
+ : undefined;
72
+ return {
73
+ workId: row.workId,
74
+ commandId: row.commandId,
75
+ durableJobId: row.durableJobId,
76
+ runId: row.runId,
77
+ nodeRunId: row.nodeRunId,
78
+ effectKind: row.effectKind as "job",
79
+ taskKey: row.taskKey as ClientAgentWorkflowArtifactRecord["taskKey"],
80
+ operationKey: row.operationKey,
81
+ bindingDigest: row.bindingDigest as `sha256:${string}`,
82
+ actorId: row.actorId,
83
+ scopeDigest: row.scopeDigest as `sha256:${string}`,
84
+ authorizationSnapshotDigest:
85
+ row.authorizationSnapshotDigest as `sha256:${string}`,
86
+ runPolicyDigest: row.runPolicyDigest as `sha256:${string}`,
87
+ requestDigest: row.requestDigest as `sha256:${string}`,
88
+ inputDigest: row.inputDigest as `sha256:${string}`,
89
+ effectGeneration: row.effectGeneration,
90
+ effectFenceTokenDigest: row.effectFenceTokenDigest as `sha256:${string}`,
91
+ status: row.status,
92
+ claimGeneration: row.claimGeneration,
93
+ ...(row.clientBindingDigest
94
+ ? {
95
+ clientBindingDigest: row.clientBindingDigest as `sha256:${string}`,
96
+ }
97
+ : {}),
98
+ ...(row.installationId ? { installationId: row.installationId } : {}),
99
+ ...(row.hostSessionDigest
100
+ ? { hostSessionDigest: row.hostSessionDigest as `sha256:${string}` }
101
+ : {}),
102
+ ...(row.clientSessionId ? { clientSessionId: row.clientSessionId } : {}),
103
+ ...(row.claimTokenHash
104
+ ? { claimTokenHash: row.claimTokenHash as `sha256:${string}` }
105
+ : {}),
106
+ ...(row.claimExpiresAt ? { claimExpiresAt: row.claimExpiresAt } : {}),
107
+ historySequence: row.historySequence,
108
+ ...(row.historyHeadDigest
109
+ ? { historyHeadDigest: row.historyHeadDigest as `sha256:${string}` }
110
+ : {}),
111
+ ...(row.terminalReceiptId
112
+ ? { terminalReceiptId: row.terminalReceiptId }
113
+ : {}),
114
+ ...(row.terminalReceiptDigest
115
+ ? {
116
+ terminalReceiptDigest:
117
+ row.terminalReceiptDigest as `sha256:${string}`,
118
+ }
119
+ : {}),
120
+ ...(row.terminalRequestDigest
121
+ ? {
122
+ terminalRequestDigest:
123
+ row.terminalRequestDigest as `sha256:${string}`,
124
+ }
125
+ : {}),
126
+ ...(row.terminalResultJson
127
+ ? { terminalResult: row.terminalResultJson }
128
+ : {}),
129
+ ...(artifact ? { artifact } : {}),
130
+ ...(row.cancelRequestedAt
131
+ ? { cancelRequestedAt: row.cancelRequestedAt }
132
+ : {}),
133
+ ...(row.finishedAt ? { finishedAt: row.finishedAt } : {}),
134
+ version: row.version,
135
+ createdAt: row.createdAt,
136
+ updatedAt: row.updatedAt,
137
+ };
138
+ }