create-windy 0.6.14 → 0.6.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +215 -24
- package/package.json +1 -1
- package/template/.agents/skills/windy-business-module/SKILL.md +58 -8
- package/template/.windy-template.json +2 -2
- package/template/apps/server/src/agent/client-session/attempt-authority.test.ts +162 -0
- package/template/apps/server/src/agent/client-session/attempt-authority.ts +191 -0
- package/template/apps/server/src/agent/client-session/bootstrap.ts +27 -11
- package/template/apps/server/src/agent/client-session/completion.ts +75 -24
- package/template/apps/server/src/agent/client-session/contracts.ts +23 -0
- package/template/apps/server/src/agent/client-session/creation.ts +103 -0
- package/template/apps/server/src/agent/client-session/digest.ts +11 -1
- package/template/apps/server/src/agent/client-session/drizzle-records.ts +150 -0
- package/template/apps/server/src/agent/client-session/drizzle-repository.integration.test.ts +30 -3
- package/template/apps/server/src/agent/client-session/drizzle-repository.ts +23 -132
- package/template/apps/server/src/agent/client-session/finalization.ts +99 -0
- package/template/apps/server/src/agent/client-session/host-auth.ts +22 -10
- package/template/apps/server/src/agent/client-session/host-revocation-composition.test.ts +77 -0
- package/template/apps/server/src/agent/client-session/host-revocation-composition.ts +28 -0
- package/template/apps/server/src/agent/client-session/launch-authority.contract.test.ts +21 -0
- package/template/apps/server/src/agent/client-session/manifest-admission.ts +11 -0
- package/template/apps/server/src/agent/client-session/service.test-support.ts +22 -0
- package/template/apps/server/src/agent/client-session/service.ts +104 -134
- package/template/apps/server/src/agent/client-session/terminal-digest.test.ts +23 -0
- package/template/apps/server/src/agent/client-workflow-artifact/active-blob-references.ts +8 -0
- package/template/apps/server/src/agent/client-workflow-artifact/artifact-builder.ts +60 -0
- package/template/apps/server/src/agent/client-workflow-artifact/authority-current.ts +59 -0
- package/template/apps/server/src/agent/client-workflow-artifact/binding-registry.test.ts +189 -0
- package/template/apps/server/src/agent/client-workflow-artifact/binding-registry.ts +153 -0
- package/template/apps/server/src/agent/client-workflow-artifact/bootstrap.ts +77 -0
- package/template/apps/server/src/agent/client-workflow-artifact/cancellation.ts +47 -0
- package/template/apps/server/src/agent/client-workflow-artifact/completed-recovery.ts +31 -0
- package/template/apps/server/src/agent/client-workflow-artifact/contracts.ts +104 -0
- package/template/apps/server/src/agent/client-workflow-artifact/drizzle-locks.ts +30 -0
- package/template/apps/server/src/agent/client-workflow-artifact/drizzle-mapper.ts +138 -0
- package/template/apps/server/src/agent/client-workflow-artifact/drizzle-repository.ts +346 -0
- package/template/apps/server/src/agent/client-workflow-artifact/error.ts +21 -0
- package/template/apps/server/src/agent/client-workflow-artifact/handler.ts +167 -0
- package/template/apps/server/src/agent/client-workflow-artifact/host-revocation.ts +29 -0
- package/template/apps/server/src/agent/client-workflow-artifact/in-memory-repository.ts +232 -0
- package/template/apps/server/src/agent/client-workflow-artifact/job-port.ts +101 -0
- package/template/apps/server/src/agent/client-workflow-artifact/presentation.ts +41 -0
- package/template/apps/server/src/agent/client-workflow-artifact/recovery.contract.test.ts +253 -0
- package/template/apps/server/src/agent/client-workflow-artifact/release-binding.ts +85 -0
- package/template/apps/server/src/agent/client-workflow-artifact/repository.test.ts +204 -0
- package/template/apps/server/src/agent/client-workflow-artifact/repository.ts +97 -0
- package/template/apps/server/src/agent/client-workflow-artifact/routes-handler.contract.test.ts +201 -0
- package/template/apps/server/src/agent/client-workflow-artifact/routes.ts +161 -0
- package/template/apps/server/src/agent/client-workflow-artifact/runtime-composition.ts +101 -0
- package/template/apps/server/src/agent/client-workflow-artifact/scope-authority.ts +39 -0
- package/template/apps/server/src/agent/client-workflow-artifact/service-authorization.ts +117 -0
- package/template/apps/server/src/agent/client-workflow-artifact/service-support.ts +28 -0
- package/template/apps/server/src/agent/client-workflow-artifact/service.contract.test.ts +345 -0
- package/template/apps/server/src/agent/client-workflow-artifact/service.ts +350 -0
- package/template/apps/server/src/agent/client-workflow-artifact/session-boundary.ts +39 -0
- package/template/apps/server/src/agent/client-workflow-artifact/source-access.contract.test.ts +98 -0
- package/template/apps/server/src/agent/client-workflow-artifact/source-access.ts +80 -0
- package/template/apps/server/src/agent/client-workflow-artifact/takeover.ts +122 -0
- package/template/apps/server/src/agent/client-workflow-artifact/task-authority.contract.test.ts +97 -0
- package/template/apps/server/src/agent/client-workflow-artifact/task-authority.ts +147 -0
- package/template/apps/server/src/agent/client-workflow-artifact/terminal-authority.contract.test.ts +100 -0
- package/template/apps/server/src/agent/client-workflow-artifact/terminal-replay.ts +31 -0
- package/template/apps/server/src/agent/client-workflow-artifact/terminal-seal.ts +211 -0
- package/template/apps/server/src/agent/client-workflow-artifact/workflow-artifact.test-support.ts +173 -0
- package/template/apps/server/src/agent/client-workflow-artifact/workflow-loader.contract.test.ts +254 -0
- package/template/apps/server/src/agent/client-workflow-artifact/workflow-loader.ts +285 -0
- package/template/apps/server/src/agent/run-facade.test-support.ts +17 -4
- package/template/apps/server/src/agent/run-facade.test.ts +16 -0
- package/template/apps/server/src/agent/run-session-authorization.ts +12 -10
- package/template/apps/server/src/agent/runtime-bootstrap-input.ts +5 -1
- package/template/apps/server/src/agent/runtime-bootstrap.ts +33 -33
- package/template/apps/server/src/agent/runtime-public-ports.ts +40 -0
- package/template/apps/server/src/agent/session-invalidation/contracts.ts +25 -26
- package/template/apps/server/src/agent/session-projection-access.ts +31 -19
- package/template/apps/server/src/agent/unavailable-routes.ts +21 -0
- package/template/apps/server/src/agent/workflow-runtime-bootstrap.ts +2 -0
- package/template/apps/server/src/agent-workflow/artifact-source-authorization.ts +12 -6
- package/template/apps/server/src/agent-workflow/command-facade.ts +70 -11
- package/template/apps/server/src/agent-workflow/drizzle-artifact-port.ts +9 -9
- package/template/apps/server/src/agent-workflow/drizzle-delegation-completion.ts +1 -1
- package/template/apps/server/src/agent-workflow/platform-node-adapters.ts +14 -6
- package/template/apps/server/src/agent-workflow/project-launch-authority.contract.test.ts +53 -0
- package/template/apps/server/src/agent-workflow/project-launch-authority.ts +92 -0
- package/template/apps/server/src/agent-workflow/runtime-composition.ts +30 -14
- package/template/apps/server/src/app/agent-runtime-composition.ts +6 -1
- package/template/apps/server/src/app/client-agent-workflow-artifact-binding.ts +14 -0
- package/template/apps/server/src/application-services.ts +6 -0
- package/template/apps/server/src/data-access/scope-containment.test.ts +31 -0
- package/template/apps/server/src/data-access/scope-containment.ts +48 -0
- package/template/apps/server/src/jobs/artifact-publication.test.ts +192 -0
- package/template/apps/server/src/jobs/artifact-publication.ts +56 -0
- package/template/apps/server/src/jobs/artifact-publisher.ts +101 -0
- package/template/apps/server/src/jobs/drizzle-artifact.ts +73 -1
- package/template/apps/server/src/jobs/drizzle-execution-repository.ts +14 -1
- package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +65 -0
- package/template/apps/server/src/jobs/drizzle-repository.integration.test.ts +1 -0
- package/template/apps/server/src/jobs/execution-repository.ts +28 -0
- package/template/apps/server/src/jobs/execution.test.ts +3 -1
- package/template/apps/server/src/jobs/in-memory-execution.ts +16 -0
- package/template/apps/server/src/jobs/in-memory-lease.ts +45 -1
- package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +2 -0
- package/template/apps/server/src/jobs/postgres-cleanup-contract.test.ts +58 -0
- package/template/apps/server/src/jobs/worker.ts +8 -14
- package/template/apps/server/src/module-host/background-ai-unavailable.ts +18 -0
- package/template/apps/server/src/module-host/durable-context.ts +19 -22
- package/template/apps/server/src/module-host/implementation-completeness.test.ts +80 -1
- package/template/apps/server/src/module-host/implementation-completeness.ts +28 -1
- package/template/apps/server/src/module-host/request-context.ts +32 -8
- package/template/apps/server/src/module-host/storage-port.ts +3 -0
- package/template/apps/server/src/module-host/workflow-input-artifact-port.test.ts +104 -0
- package/template/apps/server/src/module-host/workflow-input-artifact-port.ts +229 -0
- package/template/apps/server/src/module-host/workflow-input-materialization.test.ts +163 -0
- package/template/apps/server/src/module-host/workflow-input-materialization.ts +161 -0
- package/template/apps/server/src/workflow/drizzle-repository.integration.test.ts +1 -0
- package/template/docs/architecture/agent-workflow.md +13 -2
- package/template/docs/architecture/durable-jobs.md +7 -0
- package/template/docs/operations/agent-session-memory.md +2 -1
- package/template/docs/operations/agent-session-troubleshooting.md +87 -2
- package/template/docs/operations/agent-workflow-recovery.md +25 -14
- package/template/docs/platform/client-agent-session.md +148 -2
- package/template/packages/ai-client/package.json +2 -1
- package/template/packages/ai-client/src/client-agent/index.ts +1 -0
- package/template/packages/ai-client/src/client-agent/workflow-artifact/contracts.ts +85 -0
- package/template/packages/ai-client/src/client-agent/workflow-artifact/http.test.ts +49 -0
- package/template/packages/ai-client/src/client-agent/workflow-artifact/http.ts +82 -0
- package/template/packages/ai-client/src/client-agent/workflow-artifact/index.ts +3 -0
- package/template/packages/ai-client/src/client-agent/workflow-artifact/validation.test.ts +154 -0
- package/template/packages/ai-client/src/client-agent/workflow-artifact/validation.ts +332 -0
- package/template/packages/database/drizzle/0062_client_agent_workflow_artifact.sql +63 -0
- package/template/packages/database/drizzle/meta/0062_snapshot.json +14839 -0
- package/template/packages/database/drizzle/meta/_journal.json +7 -0
- package/template/packages/database/package.json +1 -1
- package/template/packages/database/src/agent-runtime-schema.test.ts +177 -0
- package/template/packages/database/src/schema/client-agent-workflow-artifact.ts +158 -0
- package/template/packages/database/src/schema/index.ts +1 -0
- package/template/packages/jobs/package.json +1 -1
- package/template/packages/jobs/src/handlers.test.ts +10 -0
- package/template/packages/jobs/src/handlers.ts +28 -11
- package/template/packages/modules/package.json +1 -1
- package/template/packages/modules/src/ai-client-execution-policy.ts +6 -0
- package/template/packages/modules/src/manifest.ts +2 -0
- package/template/packages/modules/src/system-agent-audit-actions.ts +3 -0
- package/template/packages/modules/src/system-ai-api-bindings.ts +15 -0
- package/template/packages/modules/src/system-audit-actions.ts +15 -0
- package/template/packages/server-sdk/index.ts +52 -0
- package/template/packages/server-sdk/package.json +5 -1
- package/template/packages/server-sdk/src/client-agent-session.ts +13 -1
- package/template/packages/server-sdk/src/client-agent-workflow-artifact.contract.test.ts +113 -0
- package/template/packages/server-sdk/src/client-agent-workflow-artifact.ts +183 -0
- package/template/packages/server-sdk/src/durable-handler.ts +7 -0
- package/template/packages/server-sdk/src/request-context.ts +7 -0
- package/template/packages/server-sdk/src/workflow-input-artifact-port.ts +104 -0
- package/template/packages/server-sdk/src/workflow-input-artifact-validation.contract.test.ts +124 -0
- package/template/packages/server-sdk/src/workflow-input-artifact-validation.ts +347 -0
- package/template/packages/server-sdk/src/workflow-input-materialization-handler.ts +43 -0
- package/template/packages/storage/package.json +1 -1
- package/template/packages/storage/src/blob-store-put-once.test.ts +70 -0
- package/template/packages/storage/src/blob-store.ts +9 -0
- package/template/packages/storage/src/local-file-blob-put-once.ts +120 -0
- package/template/packages/storage/src/local-file-blob-store.ts +53 -1
- package/template/packages/storage/src/memory-blob-store.ts +73 -33
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
CLIENT_AGENT_WORKFLOW_ARTIFACT_BINDING_LIMITS,
|
|
4
|
+
CLIENT_AGENT_WORKFLOW_ARTIFACT_TASK_KEY,
|
|
5
|
+
CLIENT_AGENT_WORKFLOW_ARTIFACT_WORK_ID_PREFIX,
|
|
6
|
+
type ModuleClientAgentHostAuthAdapter,
|
|
7
|
+
type ModuleClientAgentWorkflowArtifactBeginSpec,
|
|
8
|
+
type ModuleClientAgentWorkflowArtifactPort,
|
|
9
|
+
type ModuleClientAgentWorkflowArtifactProjectBinding,
|
|
10
|
+
} from "../index.js";
|
|
11
|
+
|
|
12
|
+
type ForbiddenLifecycleCoordinate = Extract<
|
|
13
|
+
keyof ModuleClientAgentWorkflowArtifactBeginSpec,
|
|
14
|
+
| "actorId"
|
|
15
|
+
| "installationId"
|
|
16
|
+
| "sessionId"
|
|
17
|
+
| "workflowRunId"
|
|
18
|
+
| "nodeRunId"
|
|
19
|
+
| "commandId"
|
|
20
|
+
| "scope"
|
|
21
|
+
| "fence"
|
|
22
|
+
| "provider"
|
|
23
|
+
>;
|
|
24
|
+
|
|
25
|
+
const lifecycleHasNoCallerCoordinates: [ForbiddenLifecycleCoordinate] extends [
|
|
26
|
+
never,
|
|
27
|
+
]
|
|
28
|
+
? true
|
|
29
|
+
: false = true;
|
|
30
|
+
|
|
31
|
+
const projectBinding: ModuleClientAgentWorkflowArtifactProjectBinding = {
|
|
32
|
+
workflowKey: "analysis.cross-case.full-review",
|
|
33
|
+
nodeId: "page-agent-artifact",
|
|
34
|
+
operationKey: "analysis.cross-case.page-agent",
|
|
35
|
+
releaseDigest: `sha256:${"d".repeat(64)}`,
|
|
36
|
+
scopeContainmentContractVersion: 1,
|
|
37
|
+
terminalResultAuthority: {
|
|
38
|
+
version: "sharp.analysis-result.v1",
|
|
39
|
+
digest: `sha256:${"e".repeat(64)}`,
|
|
40
|
+
async canonicalize({ proposedResult }) {
|
|
41
|
+
return proposedResult;
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
clientTaskAuthority: {
|
|
45
|
+
version: "sharp.client-task.v1",
|
|
46
|
+
digest: `sha256:${"f".repeat(64)}`,
|
|
47
|
+
async build({ workflowInput }) {
|
|
48
|
+
return { taskKey: "sharp.full-review", input: workflowInput };
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
launchAuthority: {
|
|
52
|
+
version: "sharp.workflow-launch.v1",
|
|
53
|
+
digest: `sha256:${"a".repeat(64)}`,
|
|
54
|
+
async authorizeRelease() {},
|
|
55
|
+
async authorizeRun({ currentAuthorizationScope }) {
|
|
56
|
+
return {
|
|
57
|
+
authorizationScope: currentAuthorizationScope,
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
artifact: {
|
|
62
|
+
kind: "analysis-report",
|
|
63
|
+
contentType: "application/json",
|
|
64
|
+
filename: "analysis.json",
|
|
65
|
+
retentionPolicyKey: "analysis.case-artifact",
|
|
66
|
+
maxByteSize: 4 * 1024 * 1024,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const legacyHostAuth: ModuleClientAgentHostAuthAdapter = {
|
|
71
|
+
async ready() {
|
|
72
|
+
return true;
|
|
73
|
+
},
|
|
74
|
+
async authenticate() {
|
|
75
|
+
return undefined;
|
|
76
|
+
},
|
|
77
|
+
bindRevocations() {
|
|
78
|
+
return () => undefined;
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
test("公开 Workflow Artifact contract 只有三步 lifecycle", () => {
|
|
83
|
+
const methodNames: readonly (keyof ModuleClientAgentWorkflowArtifactPort)[] =
|
|
84
|
+
["begin", "claimOrAdvance", "reconcileOrCancel"];
|
|
85
|
+
|
|
86
|
+
expect(methodNames).toEqual(["begin", "claimOrAdvance", "reconcileOrCancel"]);
|
|
87
|
+
expect(CLIENT_AGENT_WORKFLOW_ARTIFACT_TASK_KEY).toBe(
|
|
88
|
+
"system.agent-workflow.client-agent-artifact",
|
|
89
|
+
);
|
|
90
|
+
expect(CLIENT_AGENT_WORKFLOW_ARTIFACT_WORK_ID_PREFIX).toBe("cawa_");
|
|
91
|
+
expect(CLIENT_AGENT_WORKFLOW_ARTIFACT_BINDING_LIMITS).toEqual({
|
|
92
|
+
workflowKey: 160,
|
|
93
|
+
nodeId: 96,
|
|
94
|
+
operationKey: 200,
|
|
95
|
+
releaseDigest: 71,
|
|
96
|
+
terminalAuthorityVersion: 80,
|
|
97
|
+
terminalAuthorityDigest: 71,
|
|
98
|
+
clientTaskKey: 160,
|
|
99
|
+
clientTaskMaxBytes: 256 * 1024,
|
|
100
|
+
clientTaskInputArtifactMaxBytes: 16 * 1024 * 1024,
|
|
101
|
+
artifactKind: 80,
|
|
102
|
+
artifactContentType: 120,
|
|
103
|
+
artifactFilename: 240,
|
|
104
|
+
retentionPolicyKey: 256,
|
|
105
|
+
maxByteSize: 16 * 1024 * 1024,
|
|
106
|
+
});
|
|
107
|
+
expect(lifecycleHasNoCallerCoordinates).toBe(true);
|
|
108
|
+
expect(projectBinding.artifact.maxByteSize).toBe(4 * 1024 * 1024);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("现有 Host Auth Adapter 不实现 Web Cookie seam 仍兼容", () => {
|
|
112
|
+
expect(legacyHostAuth.authenticateCurrentSession).toBeUndefined();
|
|
113
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import type { ModuleAiJsonValue } from "./ai-operation-registration.js";
|
|
2
|
+
import type {
|
|
3
|
+
AgentWorkflowArtifactRef,
|
|
4
|
+
AgentWorkflowDefinition,
|
|
5
|
+
AgentWorkflowReleaseCoordinates,
|
|
6
|
+
AuthorizationScopeSnapshot,
|
|
7
|
+
} from "@southwind-ai/shared";
|
|
8
|
+
|
|
9
|
+
/** Workflow 图中的 Job node 必须固定引用此 taskKey。 */
|
|
10
|
+
export const CLIENT_AGENT_WORKFLOW_ARTIFACT_TASK_KEY =
|
|
11
|
+
"system.agent-workflow.client-agent-artifact" as const;
|
|
12
|
+
/** Browser-facing opaque work handle 的唯一稳定前缀。 */
|
|
13
|
+
export const CLIENT_AGENT_WORKFLOW_ARTIFACT_WORK_ID_PREFIX = "cawa_" as const;
|
|
14
|
+
export const CLIENT_AGENT_WORKFLOW_SCOPE_CONTAINMENT_CONTRACT_VERSION =
|
|
15
|
+
1 as const;
|
|
16
|
+
/** Project binding 的公共 DTO 上限;Server Composition 必须在启动时 fail closed。 */
|
|
17
|
+
export const CLIENT_AGENT_WORKFLOW_ARTIFACT_BINDING_LIMITS = {
|
|
18
|
+
workflowKey: 160,
|
|
19
|
+
nodeId: 96,
|
|
20
|
+
operationKey: 200,
|
|
21
|
+
releaseDigest: 71,
|
|
22
|
+
terminalAuthorityVersion: 80,
|
|
23
|
+
terminalAuthorityDigest: 71,
|
|
24
|
+
clientTaskKey: 160,
|
|
25
|
+
clientTaskMaxBytes: 256 * 1024,
|
|
26
|
+
clientTaskInputArtifactMaxBytes: 16 * 1024 * 1024,
|
|
27
|
+
artifactKind: 80,
|
|
28
|
+
artifactContentType: 120,
|
|
29
|
+
artifactFilename: 240,
|
|
30
|
+
retentionPolicyKey: 256,
|
|
31
|
+
maxByteSize: 16 * 1024 * 1024,
|
|
32
|
+
} as const;
|
|
33
|
+
|
|
34
|
+
export interface ModuleClientAgentWorkflowTaskInputArtifact {
|
|
35
|
+
readonly artifact: AgentWorkflowArtifactRef;
|
|
36
|
+
readonly value: ModuleAiJsonValue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ModuleClientAgentWorkflowExecutionTask {
|
|
40
|
+
readonly schemaVersion: 1;
|
|
41
|
+
readonly taskKey: string;
|
|
42
|
+
readonly input: ModuleAiJsonValue;
|
|
43
|
+
readonly inputDigest: `sha256:${string}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ModuleClientAgentWorkflowClientTaskAuthority {
|
|
47
|
+
/** Project-owned canonical task projection contract version. */
|
|
48
|
+
readonly version: string;
|
|
49
|
+
/** Digest of the projection rules/configuration, never of the function body. */
|
|
50
|
+
readonly digest: `sha256:${string}`;
|
|
51
|
+
build(input: {
|
|
52
|
+
readonly workflowInput: ModuleAiJsonValue;
|
|
53
|
+
/** Host-opened JSON payloads after current durable/source reauthorization. */
|
|
54
|
+
readonly inputArtifacts: readonly ModuleClientAgentWorkflowTaskInputArtifact[];
|
|
55
|
+
readonly actorId: string;
|
|
56
|
+
readonly scopeDigest: `sha256:${string}`;
|
|
57
|
+
readonly signal: AbortSignal;
|
|
58
|
+
}): Promise<{ readonly taskKey: string; readonly input: ModuleAiJsonValue }>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ModuleClientAgentWorkflowArtifactTerminalResultAuthority {
|
|
62
|
+
/** Project-owned validator/canonicalizer contract version. */
|
|
63
|
+
readonly version: string;
|
|
64
|
+
/** Digest of the validator rules/configuration, never of the function body. */
|
|
65
|
+
readonly digest: `sha256:${string}`;
|
|
66
|
+
canonicalize(input: {
|
|
67
|
+
readonly workflowInput: ModuleAiJsonValue;
|
|
68
|
+
/** Host-opened canonical inputs after terminal-time reauthorization. */
|
|
69
|
+
readonly inputArtifacts: readonly ModuleClientAgentWorkflowTaskInputArtifact[];
|
|
70
|
+
readonly proposedResult: ModuleAiJsonValue;
|
|
71
|
+
readonly actorId: string;
|
|
72
|
+
readonly scopeDigest: `sha256:${string}`;
|
|
73
|
+
readonly signal: AbortSignal;
|
|
74
|
+
}): Promise<ModuleAiJsonValue>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ModuleClientAgentWorkflowProjectLaunchAuthority {
|
|
78
|
+
readonly version: string;
|
|
79
|
+
readonly digest: `sha256:${string}`;
|
|
80
|
+
authorizeRelease(input: {
|
|
81
|
+
readonly definition: AgentWorkflowDefinition;
|
|
82
|
+
readonly coordinates: AgentWorkflowReleaseCoordinates;
|
|
83
|
+
readonly actorId: string;
|
|
84
|
+
readonly signal: AbortSignal;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
authorizeRun(input: {
|
|
87
|
+
readonly releaseId: string;
|
|
88
|
+
readonly releaseDigest: string;
|
|
89
|
+
readonly workflowInput: ModuleAiJsonValue;
|
|
90
|
+
readonly inputArtifacts: readonly AgentWorkflowArtifactRef[];
|
|
91
|
+
readonly actorId: string;
|
|
92
|
+
/** Host-captured current scope; project may only return this scope or a narrowing. */
|
|
93
|
+
readonly currentAuthorizationScope: AuthorizationScopeSnapshot;
|
|
94
|
+
readonly signal: AbortSignal;
|
|
95
|
+
}): Promise<{ readonly authorizationScope: AuthorizationScopeSnapshot }>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface ModuleClientAgentWorkflowArtifactPolicy {
|
|
99
|
+
readonly kind: string;
|
|
100
|
+
readonly contentType: "application/json";
|
|
101
|
+
readonly filename: string;
|
|
102
|
+
readonly retentionPolicyKey: string;
|
|
103
|
+
readonly maxByteSize: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* project-owned Composition 对已审核 Workflow release 的静态绑定。
|
|
108
|
+
* 该绑定只供 Host 装配,不能作为 Module lifecycle 调用参数。
|
|
109
|
+
*/
|
|
110
|
+
export interface ModuleClientAgentWorkflowArtifactProjectBinding {
|
|
111
|
+
readonly workflowKey: string;
|
|
112
|
+
readonly nodeId: string;
|
|
113
|
+
readonly operationKey: string;
|
|
114
|
+
/** 审核时登记的完整 Immutable Release digest;新 Release 不自动继承。 */
|
|
115
|
+
readonly releaseDigest: `sha256:${string}`;
|
|
116
|
+
/** Composition readiness:Host 必须按持久 Run scope ⊆ 当前 Actor scope 复验。 */
|
|
117
|
+
readonly scopeContainmentContractVersion: typeof CLIENT_AGENT_WORKFLOW_SCOPE_CONTAINMENT_CONTRACT_VERSION;
|
|
118
|
+
readonly terminalResultAuthority: ModuleClientAgentWorkflowArtifactTerminalResultAuthority;
|
|
119
|
+
/** Claim response 的 canonical execution input;raw Artifact 永不下发。 */
|
|
120
|
+
readonly clientTaskAuthority: ModuleClientAgentWorkflowClientTaskAuthority;
|
|
121
|
+
/** 受保护 Workflow 的 Release/Run 只能经 project preset authority。 */
|
|
122
|
+
readonly launchAuthority: ModuleClientAgentWorkflowProjectLaunchAuthority;
|
|
123
|
+
readonly artifact: ModuleClientAgentWorkflowArtifactPolicy;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface ModuleClientAgentWorkflowArtifactBeginSpec {
|
|
127
|
+
/** 业务输入;Actor、Scope、Run、Node、Fence 与幂等坐标全部由 Host 当前 attempt 固定。 */
|
|
128
|
+
readonly input: ModuleAiJsonValue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Host 签发的不透明 lifecycle handle,调用方不得解析或持久化其内部坐标。 */
|
|
132
|
+
export interface ModuleClientAgentWorkflowArtifactCommand {
|
|
133
|
+
readonly value: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export type ModuleClientAgentWorkflowArtifactStatus =
|
|
137
|
+
| "pending"
|
|
138
|
+
| "running"
|
|
139
|
+
| "completed"
|
|
140
|
+
| "cancelled";
|
|
141
|
+
|
|
142
|
+
export type ModuleClientAgentWorkflowArtifactSafeCode =
|
|
143
|
+
| "HOST_UNAVAILABLE"
|
|
144
|
+
| "NO_PENDING_WORK"
|
|
145
|
+
| "CLAIM_CONFLICT"
|
|
146
|
+
| "WORK_NOT_FOUND"
|
|
147
|
+
| "WORK_EXPIRED"
|
|
148
|
+
| "WORK_CANCELLED"
|
|
149
|
+
| "RECEIPT_REJECTED"
|
|
150
|
+
| "ARTIFACT_REJECTED"
|
|
151
|
+
| "INTERNAL_ERROR";
|
|
152
|
+
|
|
153
|
+
export interface ModuleClientAgentWorkflowArtifactSummary {
|
|
154
|
+
readonly artifactId: string;
|
|
155
|
+
readonly kind: string;
|
|
156
|
+
readonly contentType: string;
|
|
157
|
+
readonly filename: string;
|
|
158
|
+
readonly byteSize: number;
|
|
159
|
+
readonly sha256: string;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface ModuleClientAgentWorkflowArtifactView {
|
|
163
|
+
readonly command: ModuleClientAgentWorkflowArtifactCommand;
|
|
164
|
+
readonly status: ModuleClientAgentWorkflowArtifactStatus;
|
|
165
|
+
readonly artifact?: ModuleClientAgentWorkflowArtifactSummary;
|
|
166
|
+
readonly safeCode?: ModuleClientAgentWorkflowArtifactSafeCode;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Durable Handler 的深 Port。实现从当前 attempt 捕获 Actor、installation/session、
|
|
171
|
+
* Workflow Run/Node/command、Scope、generation/effect fence 与 Artifact writer。
|
|
172
|
+
*/
|
|
173
|
+
export interface ModuleClientAgentWorkflowArtifactPort {
|
|
174
|
+
begin(
|
|
175
|
+
spec: ModuleClientAgentWorkflowArtifactBeginSpec,
|
|
176
|
+
): Promise<ModuleClientAgentWorkflowArtifactView>;
|
|
177
|
+
claimOrAdvance(
|
|
178
|
+
command: ModuleClientAgentWorkflowArtifactCommand,
|
|
179
|
+
): Promise<ModuleClientAgentWorkflowArtifactView>;
|
|
180
|
+
reconcileOrCancel(
|
|
181
|
+
command: ModuleClientAgentWorkflowArtifactCommand,
|
|
182
|
+
): Promise<ModuleClientAgentWorkflowArtifactView>;
|
|
183
|
+
}
|
|
@@ -9,6 +9,9 @@ import type {
|
|
|
9
9
|
ModuleResourceFileRequest,
|
|
10
10
|
ModuleStorageWritePortV1,
|
|
11
11
|
} from "./storage-port.js";
|
|
12
|
+
// @windy-module system.agent begin
|
|
13
|
+
import type { ModuleWorkflowInputArtifactPublicationPortV1 } from "./workflow-input-artifact-port.js";
|
|
14
|
+
// @windy-module system.agent end
|
|
12
15
|
|
|
13
16
|
export interface ModuleDurableStoragePort {
|
|
14
17
|
openResourceFile(
|
|
@@ -29,6 +32,10 @@ export interface ModuleDurableHandlerContext extends DurableHandlerContext {
|
|
|
29
32
|
readonly storageWriter?: ModuleStorageWritePortV1;
|
|
30
33
|
/** 每个 attempt 重新绑定 Actor、Scope 与当前授权。 */
|
|
31
34
|
readonly reportStaging?: ModuleReportStagingPortV1;
|
|
35
|
+
// @windy-module system.agent begin
|
|
36
|
+
/** 真实 Durable Job artifact publication;不会把 Storage fileId 当 artifactId。 */
|
|
37
|
+
readonly workflowInputArtifacts?: ModuleWorkflowInputArtifactPublicationPortV1;
|
|
38
|
+
// @windy-module system.agent end
|
|
32
39
|
readonly ai: ModuleBackgroundAiPort;
|
|
33
40
|
}
|
|
34
41
|
|
|
@@ -14,6 +14,9 @@ import type {
|
|
|
14
14
|
ModuleStorageWritePortV1,
|
|
15
15
|
} from "./storage-port.js";
|
|
16
16
|
import type { ModuleUserDirectoryPort } from "./user-directory-port.js";
|
|
17
|
+
// @windy-module system.agent begin
|
|
18
|
+
import type { ModuleWorkflowInputMaterializationPortV1 } from "./workflow-input-artifact-port.js";
|
|
19
|
+
// @windy-module system.agent end
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* 模块路由 Handler 的受限请求上下文。
|
|
@@ -44,6 +47,10 @@ export interface ModuleRequestContext {
|
|
|
44
47
|
/** Host-bound authoritative source lifecycle and authorization revocation outbox. */
|
|
45
48
|
readonly sourceInvalidation?: ModuleAuthoritativeSourceInvalidationPortV1;
|
|
46
49
|
readonly jobs: ModuleJobPort;
|
|
50
|
+
// @windy-module system.agent begin
|
|
51
|
+
/** 真实 Durable Job 两阶段物化;缺失时不得声明 Workflow input readiness。 */
|
|
52
|
+
readonly workflowInputMaterialization?: ModuleWorkflowInputMaterializationPortV1;
|
|
53
|
+
// @windy-module system.agent end
|
|
47
54
|
/** 大报告受治理暂存是版本化、可选的 additive seam。 */
|
|
48
55
|
readonly reportStaging?: ModuleReportStagingPortV1;
|
|
49
56
|
/**
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { AgentWorkflowArtifactRef, JsonValue } from "@southwind-ai/shared";
|
|
2
|
+
import type { ModuleResourceFileRequest } from "./storage-port.js";
|
|
3
|
+
|
|
4
|
+
export interface ModuleWorkflowInputArtifactSourceV1 {
|
|
5
|
+
/** Host 固定 moduleKey;调用方只声明模块内的权威对象坐标。 */
|
|
6
|
+
readonly kind: string;
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly version: string;
|
|
9
|
+
readonly sequence: number;
|
|
10
|
+
readonly digest?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ModuleWorkflowInputArtifactPublicationV1 {
|
|
14
|
+
/** 同一 Job stage 内的稳定幂等键。 */
|
|
15
|
+
readonly publicationKey: string;
|
|
16
|
+
readonly kind: string;
|
|
17
|
+
readonly filename: string;
|
|
18
|
+
readonly retentionPolicyKey: string;
|
|
19
|
+
readonly source: ModuleWorkflowInputArtifactSourceV1;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ModuleWorkflowInputResourceArtifactV1 extends ModuleWorkflowInputArtifactPublicationV1 {
|
|
23
|
+
/** 仍走当前模块注册的 resource-file policy 与对象授权。 */
|
|
24
|
+
readonly resource: ModuleResourceFileRequest;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ModuleWorkflowInputJsonArtifactV1 extends ModuleWorkflowInputArtifactPublicationV1 {
|
|
28
|
+
readonly value: JsonValue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 只在真实 Durable Handler attempt 中可用:复制受权源到 BlobStore,再发布正式
|
|
33
|
+
* Durable Job Artifact,并返回可直接作为 Workflow input 的真实 artifactId。
|
|
34
|
+
*/
|
|
35
|
+
export interface ModuleWorkflowInputArtifactPublicationPortV1 {
|
|
36
|
+
publishResource(
|
|
37
|
+
input: ModuleWorkflowInputResourceArtifactV1,
|
|
38
|
+
): Promise<AgentWorkflowArtifactRef>;
|
|
39
|
+
publishJson(
|
|
40
|
+
input: ModuleWorkflowInputJsonArtifactV1,
|
|
41
|
+
): Promise<AgentWorkflowArtifactRef>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type ModuleWorkflowInputMaterializationSourceV1 =
|
|
45
|
+
| ({ readonly mode: "resource" } & ModuleWorkflowInputResourceArtifactV1)
|
|
46
|
+
| ({ readonly mode: "json" } & ModuleWorkflowInputJsonArtifactV1);
|
|
47
|
+
|
|
48
|
+
export interface ModuleWorkflowInputMaterializationHandleV1 {
|
|
49
|
+
readonly materializationId: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type ModuleWorkflowInputMaterializationViewV1 =
|
|
53
|
+
| (ModuleWorkflowInputMaterializationHandleV1 & {
|
|
54
|
+
readonly status: "pending";
|
|
55
|
+
})
|
|
56
|
+
| (ModuleWorkflowInputMaterializationHandleV1 & {
|
|
57
|
+
readonly status: "ready";
|
|
58
|
+
readonly artifacts: readonly AgentWorkflowArtifactRef[];
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/** Route/Application 两阶段 seam:只有 ready 的正式 Artifact refs 才能进入 Workflow input。 */
|
|
62
|
+
export interface ModuleWorkflowInputMaterializationPortV1 {
|
|
63
|
+
begin(input: {
|
|
64
|
+
readonly idempotencyKey: string;
|
|
65
|
+
readonly sources: readonly ModuleWorkflowInputMaterializationSourceV1[];
|
|
66
|
+
}): Promise<ModuleWorkflowInputMaterializationHandleV1>;
|
|
67
|
+
resolve(
|
|
68
|
+
handle: ModuleWorkflowInputMaterializationHandleV1,
|
|
69
|
+
): Promise<ModuleWorkflowInputMaterializationViewV1>;
|
|
70
|
+
cancel(
|
|
71
|
+
handle: ModuleWorkflowInputMaterializationHandleV1 & {
|
|
72
|
+
readonly reason: string;
|
|
73
|
+
},
|
|
74
|
+
): Promise<void>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export class ModuleWorkflowInputMaterializationError extends Error {
|
|
78
|
+
constructor(
|
|
79
|
+
readonly code:
|
|
80
|
+
| "MATERIALIZATION_UNAVAILABLE"
|
|
81
|
+
| "MATERIALIZATION_FAILED"
|
|
82
|
+
| "MATERIALIZATION_RESULT_INVALID",
|
|
83
|
+
) {
|
|
84
|
+
super("Workflow input materialization 不可用");
|
|
85
|
+
this.name = "ModuleWorkflowInputMaterializationError";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function moduleWorkflowInputMaterializationHandlerKey(
|
|
90
|
+
moduleName: string,
|
|
91
|
+
) {
|
|
92
|
+
return `${moduleName}.workflow-input-materialize`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class ModuleWorkflowInputArtifactUnavailableError extends Error {
|
|
96
|
+
readonly code = "WORKFLOW_INPUT_ARTIFACT_UNAVAILABLE";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function requireModuleWorkflowInputArtifactPublicationPortV1(
|
|
100
|
+
port: ModuleWorkflowInputArtifactPublicationPortV1 | undefined,
|
|
101
|
+
) {
|
|
102
|
+
if (!port) throw new ModuleWorkflowInputArtifactUnavailableError();
|
|
103
|
+
return port;
|
|
104
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
MODULE_WORKFLOW_INPUT_MATERIALIZATION_LIMITS_V1 as limits,
|
|
4
|
+
parseModuleWorkflowInputJsonValueV1,
|
|
5
|
+
parseModuleWorkflowInputMaterializationBeginV1,
|
|
6
|
+
parseModuleWorkflowInputMaterializationPayloadV1,
|
|
7
|
+
parseModuleWorkflowInputMaterializationSourceV1,
|
|
8
|
+
} from "../index.js";
|
|
9
|
+
|
|
10
|
+
const publication = {
|
|
11
|
+
publicationKey: "case-1-v7",
|
|
12
|
+
kind: "case-snapshot",
|
|
13
|
+
filename: "case-1.json",
|
|
14
|
+
retentionPolicyKey: "case.analysis",
|
|
15
|
+
source: {
|
|
16
|
+
kind: "case",
|
|
17
|
+
id: "case-1",
|
|
18
|
+
version: "7",
|
|
19
|
+
sequence: 7,
|
|
20
|
+
digest: `sha256:${"a".repeat(64)}`,
|
|
21
|
+
},
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
test("materialization parser 按 mode 精确拒绝多余和互斥字段", () => {
|
|
25
|
+
expect(
|
|
26
|
+
parseModuleWorkflowInputMaterializationSourceV1({
|
|
27
|
+
mode: "resource",
|
|
28
|
+
...publication,
|
|
29
|
+
resource: {
|
|
30
|
+
fileId: "file-1",
|
|
31
|
+
purpose: "case-evidence",
|
|
32
|
+
resourceType: "case",
|
|
33
|
+
resourceId: "case-1",
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
).toMatchObject({ mode: "resource", resource: { fileId: "file-1" } });
|
|
37
|
+
expect(() =>
|
|
38
|
+
parseModuleWorkflowInputMaterializationSourceV1({
|
|
39
|
+
mode: "resource",
|
|
40
|
+
...publication,
|
|
41
|
+
resource: {
|
|
42
|
+
fileId: "file-1",
|
|
43
|
+
purpose: "case-evidence",
|
|
44
|
+
resourceType: "case",
|
|
45
|
+
resourceId: "case-1",
|
|
46
|
+
},
|
|
47
|
+
value: {},
|
|
48
|
+
}),
|
|
49
|
+
).toThrow("WORKFLOW_INPUT_MATERIALIZATION_PAYLOAD_INVALID");
|
|
50
|
+
expect(() =>
|
|
51
|
+
parseModuleWorkflowInputMaterializationSourceV1({
|
|
52
|
+
mode: "json",
|
|
53
|
+
...publication,
|
|
54
|
+
value: {},
|
|
55
|
+
unknown: true,
|
|
56
|
+
}),
|
|
57
|
+
).toThrow("WORKFLOW_INPUT_MATERIALIZATION_PAYLOAD_INVALID");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("begin、durable payload 与 JSON value 使用同一 fail-closed budgets", () => {
|
|
61
|
+
const source = { mode: "json" as const, ...publication, value: { ok: true } };
|
|
62
|
+
expect(
|
|
63
|
+
parseModuleWorkflowInputMaterializationBeginV1({
|
|
64
|
+
idempotencyKey: "case-1-v7",
|
|
65
|
+
sources: [source],
|
|
66
|
+
}),
|
|
67
|
+
).toMatchObject({ sources: [{ mode: "json" }] });
|
|
68
|
+
expect(
|
|
69
|
+
parseModuleWorkflowInputMaterializationPayloadV1({
|
|
70
|
+
schemaVersion: 1,
|
|
71
|
+
sources: [source],
|
|
72
|
+
}),
|
|
73
|
+
).toMatchObject({ schemaVersion: 1 });
|
|
74
|
+
expect(() =>
|
|
75
|
+
parseModuleWorkflowInputMaterializationBeginV1({
|
|
76
|
+
idempotencyKey: "case-1-v7",
|
|
77
|
+
sources: [source],
|
|
78
|
+
actorId: "browser-claim",
|
|
79
|
+
}),
|
|
80
|
+
).toThrow("WORKFLOW_INPUT_MATERIALIZATION_PAYLOAD_INVALID");
|
|
81
|
+
expect(() =>
|
|
82
|
+
parseModuleWorkflowInputMaterializationPayloadV1({
|
|
83
|
+
schemaVersion: 1,
|
|
84
|
+
sources: [source],
|
|
85
|
+
retry: true,
|
|
86
|
+
}),
|
|
87
|
+
).toThrow("WORKFLOW_INPUT_MATERIALIZATION_PAYLOAD_INVALID");
|
|
88
|
+
|
|
89
|
+
const dangerous = {} as Record<string, unknown>;
|
|
90
|
+
Object.defineProperty(dangerous, "__proto__", {
|
|
91
|
+
enumerable: true,
|
|
92
|
+
value: "pollute",
|
|
93
|
+
});
|
|
94
|
+
expect(() => parseModuleWorkflowInputJsonValueV1(dangerous)).toThrow(
|
|
95
|
+
"WORKFLOW_INPUT_MATERIALIZATION_PAYLOAD_INVALID",
|
|
96
|
+
);
|
|
97
|
+
expect(() => parseModuleWorkflowInputJsonValueV1(undefined)).toThrow();
|
|
98
|
+
expect(() => parseModuleWorkflowInputJsonValueV1(Number.NaN)).toThrow();
|
|
99
|
+
const sparse = Array<unknown>(2);
|
|
100
|
+
sparse[1] = "sparse";
|
|
101
|
+
expect(() => parseModuleWorkflowInputJsonValueV1(sparse)).toThrow();
|
|
102
|
+
expect(() =>
|
|
103
|
+
parseModuleWorkflowInputJsonValueV1(
|
|
104
|
+
Object.defineProperty({}, "lazy", {
|
|
105
|
+
enumerable: true,
|
|
106
|
+
get: () => "not-read",
|
|
107
|
+
}),
|
|
108
|
+
),
|
|
109
|
+
).toThrow();
|
|
110
|
+
|
|
111
|
+
let deep: unknown = null;
|
|
112
|
+
for (let index = 0; index <= limits.jsonDepth; index += 1) {
|
|
113
|
+
deep = { next: deep };
|
|
114
|
+
}
|
|
115
|
+
expect(() => parseModuleWorkflowInputJsonValueV1(deep)).toThrow();
|
|
116
|
+
expect(() =>
|
|
117
|
+
parseModuleWorkflowInputJsonValueV1(
|
|
118
|
+
Array.from({ length: limits.arrayItems + 1 }, () => null),
|
|
119
|
+
),
|
|
120
|
+
).toThrow();
|
|
121
|
+
expect(() =>
|
|
122
|
+
parseModuleWorkflowInputJsonValueV1("x".repeat(limits.stringBytes + 1)),
|
|
123
|
+
).toThrow();
|
|
124
|
+
});
|