@vgai/sdk 0.4.0 → 0.4.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.
- package/package.json +4 -3
- package/src/errors.ts +19 -0
- package/src/index.ts +12 -0
- package/src/project/index.ts +1 -0
- package/src/project/provenance.ts +154 -0
- package/src/registry.ts +2 -2
- package/src/tools.ts +24 -0
- package/src/types.ts +24 -0
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@vgai/sdk",
|
|
3
3
|
"author": "Volter AI, Inc.",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
|
-
"version": "0.4.
|
|
5
|
+
"version": "0.4.1",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
],
|
|
18
18
|
"exports": {
|
|
19
19
|
".": "./src/index.ts",
|
|
20
|
-
"./registry": "./src/registry.ts"
|
|
20
|
+
"./registry": "./src/registry.ts",
|
|
21
|
+
"./tools": "./src/tools.ts"
|
|
21
22
|
},
|
|
22
23
|
"dependencies": {
|
|
23
|
-
"@vgai/engine": "0.4.
|
|
24
|
+
"@vgai/engine": "0.4.1",
|
|
24
25
|
"playwright": "^1.58.2",
|
|
25
26
|
"zod": "^4.3.6"
|
|
26
27
|
}
|
package/src/errors.ts
CHANGED
|
@@ -41,6 +41,13 @@ export interface StructuredOperationError {
|
|
|
41
41
|
data?: unknown;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* A global brand keeps expected errors recognizable across separately loaded
|
|
46
|
+
* copies of @vgai/sdk (for example, an editor host dispatching a standalone
|
|
47
|
+
* project's tool). `instanceof` alone cannot cross that package boundary.
|
|
48
|
+
*/
|
|
49
|
+
export const OPERATION_ERROR_BRAND = Symbol.for('@vgai/sdk.OperationError');
|
|
50
|
+
|
|
44
51
|
/**
|
|
45
52
|
* The only way an operation `impl` should signal an EXPECTED, contractual
|
|
46
53
|
* failure (as opposed to an unexpected bug/exception). `code` must be one
|
|
@@ -51,6 +58,7 @@ export interface StructuredOperationError {
|
|
|
51
58
|
* `normalizeThrown`).
|
|
52
59
|
*/
|
|
53
60
|
export class OperationError extends Error {
|
|
61
|
+
readonly [OPERATION_ERROR_BRAND] = true;
|
|
54
62
|
readonly code: string;
|
|
55
63
|
readonly data: unknown;
|
|
56
64
|
|
|
@@ -62,6 +70,17 @@ export class OperationError extends Error {
|
|
|
62
70
|
}
|
|
63
71
|
}
|
|
64
72
|
|
|
73
|
+
export function isOperationError(value: unknown): value is OperationError {
|
|
74
|
+
if (value instanceof OperationError) return true;
|
|
75
|
+
if (value === null || typeof value !== 'object') return false;
|
|
76
|
+
const candidate = value as Record<PropertyKey, unknown>;
|
|
77
|
+
return (
|
|
78
|
+
candidate[OPERATION_ERROR_BRAND] === true &&
|
|
79
|
+
typeof candidate['code'] === 'string' &&
|
|
80
|
+
typeof candidate['message'] === 'string'
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
65
84
|
/** Reduce a `ZodError`'s issues to the structured, serializable shape above. */
|
|
66
85
|
export function toStructuredIssues(issues: readonly z.ZodIssue[]): StructuredIssue[] {
|
|
67
86
|
return issues.map((issue) => ({
|
package/src/index.ts
CHANGED
|
@@ -27,6 +27,18 @@ export {
|
|
|
27
27
|
type OperationSummary,
|
|
28
28
|
} from './registry.js';
|
|
29
29
|
export * from './render/index.js';
|
|
30
|
+
export type {
|
|
31
|
+
ToolContext,
|
|
32
|
+
ToolDefinition,
|
|
33
|
+
ToolErrorDefinition,
|
|
34
|
+
ToolHost,
|
|
35
|
+
ToolOutcome,
|
|
36
|
+
ToolPermission,
|
|
37
|
+
ToolPermissionRisk,
|
|
38
|
+
ToolRequirements,
|
|
39
|
+
ToolSummary,
|
|
40
|
+
} from './tools.js';
|
|
41
|
+
export { defineTool, ToolError, ToolRegistry } from './tools.js';
|
|
30
42
|
export {
|
|
31
43
|
type ExecutionHost,
|
|
32
44
|
type ExecutionRequirements,
|
package/src/project/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from './discovery-operations.js';
|
|
|
13
13
|
export * from './entity-operations.js';
|
|
14
14
|
export * from './input-map-operations.js';
|
|
15
15
|
export * from './manifest-operations.js';
|
|
16
|
+
export * from './provenance.js';
|
|
16
17
|
export * from './scene-operations.js';
|
|
17
18
|
export {
|
|
18
19
|
BaseHashField,
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const PROJECT_PROVENANCE_PATH = '.vgai/provenance.json';
|
|
4
|
+
|
|
5
|
+
export const ProjectProvenanceOutputSchema = z.object({
|
|
6
|
+
path: z.string(),
|
|
7
|
+
bytes: z.number().int().nonnegative(),
|
|
8
|
+
sha256: z.string().regex(/^[a-f0-9]{64}$/),
|
|
9
|
+
mediaType: z.string().optional(),
|
|
10
|
+
role: z.enum(['asset', 'prefab', 'provenance', 'other']).optional(),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const ProjectProvenanceExecutionSchema = z.object({
|
|
14
|
+
mode: z.enum(['mock', 'direct', 'managed']),
|
|
15
|
+
provider: z.string(),
|
|
16
|
+
operation: z.string().optional(),
|
|
17
|
+
model: z.string().optional(),
|
|
18
|
+
requestId: z.string().optional(),
|
|
19
|
+
taskId: z.string().optional(),
|
|
20
|
+
managedJobId: z.string().optional(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const ProjectProvenanceOperationSchema = z.object({
|
|
24
|
+
createdAt: z.string().datetime(),
|
|
25
|
+
operation: z.object({
|
|
26
|
+
name: z.string(),
|
|
27
|
+
source: z.string().optional(),
|
|
28
|
+
}),
|
|
29
|
+
execution: ProjectProvenanceExecutionSchema.optional(),
|
|
30
|
+
executions: z.array(ProjectProvenanceExecutionSchema).min(2).optional(),
|
|
31
|
+
input: z.json().optional(),
|
|
32
|
+
outputs: z.array(ProjectProvenanceOutputSchema).min(1),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const ProjectProvenanceDocumentSchema = z.object({
|
|
36
|
+
version: z.literal(1),
|
|
37
|
+
operations: z.record(z.string(), ProjectProvenanceOperationSchema),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export type ProjectProvenanceDocument = z.infer<typeof ProjectProvenanceDocumentSchema>;
|
|
41
|
+
export type ProjectProvenanceOperation = z.infer<typeof ProjectProvenanceOperationSchema>;
|
|
42
|
+
export type ProjectProvenanceExecution = z.infer<typeof ProjectProvenanceExecutionSchema>;
|
|
43
|
+
|
|
44
|
+
export const ProjectAttributionEntrySchema = z.object({
|
|
45
|
+
key: z.string(),
|
|
46
|
+
name: z.string().optional(),
|
|
47
|
+
source: z.string().optional(),
|
|
48
|
+
sourceUrl: z.string().url().optional(),
|
|
49
|
+
author: z.string(),
|
|
50
|
+
license: z.string(),
|
|
51
|
+
text: z.string(),
|
|
52
|
+
operationIds: z.array(z.string()),
|
|
53
|
+
outputPaths: z.array(z.string()),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export const ProjectAttributionReportSchema = z.object({
|
|
57
|
+
version: z.literal(1),
|
|
58
|
+
entries: z.array(ProjectAttributionEntrySchema),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export type ProjectAttributionEntry = z.infer<typeof ProjectAttributionEntrySchema>;
|
|
62
|
+
export type ProjectAttributionReport = z.infer<typeof ProjectAttributionReportSchema>;
|
|
63
|
+
|
|
64
|
+
export function emptyProjectProvenanceDocument(): ProjectProvenanceDocument {
|
|
65
|
+
return { version: 1, operations: {} };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function objectRecord(value: unknown): Record<string, unknown> | null {
|
|
69
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
70
|
+
? (value as Record<string, unknown>)
|
|
71
|
+
: null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function stringField(record: Record<string, unknown>, key: string): string | undefined {
|
|
75
|
+
const value = record[key];
|
|
76
|
+
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function attributionEntry(
|
|
80
|
+
operationId: string,
|
|
81
|
+
operation: ProjectProvenanceOperation,
|
|
82
|
+
): ProjectAttributionEntry | null {
|
|
83
|
+
const input = objectRecord(operation.input);
|
|
84
|
+
if (!input) return null;
|
|
85
|
+
const nested = objectRecord(input['attribution']) ?? {};
|
|
86
|
+
const author = stringField(nested, 'author') ?? stringField(input, 'author');
|
|
87
|
+
const license = stringField(nested, 'license') ?? stringField(input, 'license');
|
|
88
|
+
if (!author || !license) return null;
|
|
89
|
+
const name = stringField(nested, 'name') ?? stringField(input, 'name');
|
|
90
|
+
const source = stringField(nested, 'source') ?? stringField(input, 'source');
|
|
91
|
+
const sourceUrl = stringField(nested, 'sourceUrl') ?? stringField(input, 'sourceUrl');
|
|
92
|
+
const text = stringField(nested, 'text') ?? `${name ? `${name} by ` : ''}${author} (${license})`;
|
|
93
|
+
const key = JSON.stringify([sourceUrl ?? '', author, license, text]);
|
|
94
|
+
return ProjectAttributionEntrySchema.parse({
|
|
95
|
+
key,
|
|
96
|
+
...(name ? { name } : {}),
|
|
97
|
+
...(source ? { source } : {}),
|
|
98
|
+
...(sourceUrl ? { sourceUrl } : {}),
|
|
99
|
+
author,
|
|
100
|
+
license,
|
|
101
|
+
text,
|
|
102
|
+
operationIds: [operationId],
|
|
103
|
+
outputPaths: operation.outputs
|
|
104
|
+
.filter((output) => output.role !== 'provenance')
|
|
105
|
+
.map((output) => output.path),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Project credits are a projection of central provenance, never another
|
|
111
|
+
* persisted ledger or a set of per-asset files.
|
|
112
|
+
*/
|
|
113
|
+
export function deriveProjectAttributionReport(
|
|
114
|
+
document: ProjectProvenanceDocument,
|
|
115
|
+
): ProjectAttributionReport {
|
|
116
|
+
const grouped = new Map<string, ProjectAttributionEntry>();
|
|
117
|
+
for (const [operationId, operation] of Object.entries(document.operations)) {
|
|
118
|
+
const entry = attributionEntry(operationId, operation);
|
|
119
|
+
if (!entry) continue;
|
|
120
|
+
const prior = grouped.get(entry.key);
|
|
121
|
+
if (!prior) {
|
|
122
|
+
grouped.set(entry.key, entry);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
prior.operationIds.push(...entry.operationIds);
|
|
126
|
+
prior.outputPaths.push(...entry.outputPaths);
|
|
127
|
+
}
|
|
128
|
+
const entries = [...grouped.values()]
|
|
129
|
+
.map((entry) => ({
|
|
130
|
+
...entry,
|
|
131
|
+
operationIds: [...new Set(entry.operationIds)].sort(),
|
|
132
|
+
outputPaths: [...new Set(entry.outputPaths)].sort(),
|
|
133
|
+
}))
|
|
134
|
+
.sort(
|
|
135
|
+
(left, right) => left.text.localeCompare(right.text) || left.key.localeCompare(right.key),
|
|
136
|
+
);
|
|
137
|
+
return ProjectAttributionReportSchema.parse({ version: 1, entries });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Deterministic human-readable projection of the machine report. */
|
|
141
|
+
export function formatProjectAttributionMarkdown(report: ProjectAttributionReport): string {
|
|
142
|
+
const sections = report.entries.map((entry) => {
|
|
143
|
+
const heading = entry.name ?? entry.text;
|
|
144
|
+
return [
|
|
145
|
+
`## ${heading}`,
|
|
146
|
+
'',
|
|
147
|
+
entry.text,
|
|
148
|
+
'',
|
|
149
|
+
`Author: ${entry.author} `,
|
|
150
|
+
`License: ${entry.license}${entry.sourceUrl ? ` \nSource: ${entry.sourceUrl}` : ''}`,
|
|
151
|
+
].join('\n');
|
|
152
|
+
});
|
|
153
|
+
return `# Asset attribution\n${sections.length > 0 ? `\n${sections.join('\n\n')}` : '\nNo attributed project assets are currently present.'}\n`;
|
|
154
|
+
}
|
package/src/registry.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { z } from 'zod';
|
|
2
2
|
import {
|
|
3
3
|
CORE_ERROR_CODES,
|
|
4
|
-
|
|
4
|
+
isOperationError,
|
|
5
5
|
type StructuredOperationError,
|
|
6
6
|
toStructuredIssues,
|
|
7
7
|
} from './errors.js';
|
|
@@ -141,7 +141,7 @@ export type OperationOutcome<TResult = unknown> =
|
|
|
141
141
|
* the identifying `code`.
|
|
142
142
|
*/
|
|
143
143
|
function normalizeThrown(def: OperationDefinition, err: unknown): StructuredOperationError {
|
|
144
|
-
if (err
|
|
144
|
+
if (isOperationError(err)) {
|
|
145
145
|
const declared = def.errors.find((e) => e.code === err.code);
|
|
146
146
|
if (!declared) {
|
|
147
147
|
return {
|
package/src/tools.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public project-tool contract.
|
|
3
|
+
*
|
|
4
|
+
* A tool is an ordinary registered JavaScript/TypeScript function. The SDK's
|
|
5
|
+
* operation registry remains the internal validated dispatcher used by every
|
|
6
|
+
* projection, but projects should not need a second public ontology merely
|
|
7
|
+
* because the host calls their function through a transport.
|
|
8
|
+
*/
|
|
9
|
+
export { OperationError as ToolError } from './errors.js';
|
|
10
|
+
export {
|
|
11
|
+
defineOperation as defineTool,
|
|
12
|
+
type ErrorDefinition as ToolErrorDefinition,
|
|
13
|
+
type OperationDefinition as ToolDefinition,
|
|
14
|
+
type OperationOutcome as ToolOutcome,
|
|
15
|
+
OperationRegistry as ToolRegistry,
|
|
16
|
+
type OperationSummary as ToolSummary,
|
|
17
|
+
} from './registry.js';
|
|
18
|
+
export type {
|
|
19
|
+
ExecutionHost as ToolHost,
|
|
20
|
+
ExecutionRequirements as ToolRequirements,
|
|
21
|
+
OperationContext as ToolContext,
|
|
22
|
+
PermissionMetadata as ToolPermission,
|
|
23
|
+
PermissionRisk as ToolPermissionRisk,
|
|
24
|
+
} from './types.js';
|
package/src/types.ts
CHANGED
|
@@ -68,6 +68,30 @@ export interface ProjectGeneratedOutput {
|
|
|
68
68
|
files: ProjectGeneratedOutputFile[];
|
|
69
69
|
totalBytes: number;
|
|
70
70
|
dryRun: boolean;
|
|
71
|
+
/** Host-created logical record in .vgai/provenance.json; absent for dry runs. */
|
|
72
|
+
provenanceOperationId?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ProjectProviderExecution {
|
|
76
|
+
mode: 'mock' | 'direct' | 'managed';
|
|
77
|
+
provider: string;
|
|
78
|
+
operation?: string;
|
|
79
|
+
model?: string;
|
|
80
|
+
requestId?: string;
|
|
81
|
+
taskId?: string;
|
|
82
|
+
managedJobId?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Facts the operation host already owns and stamps onto every committed batch. */
|
|
86
|
+
export interface ProjectOutputProvenanceContext {
|
|
87
|
+
operationName: string;
|
|
88
|
+
operationSource?: string;
|
|
89
|
+
/** Sanitized facts for a single provider execution. */
|
|
90
|
+
execution?: ProjectProviderExecution;
|
|
91
|
+
/** Ordered facts for a native multi-task pipeline that produced one output batch. */
|
|
92
|
+
executions?: readonly ProjectProviderExecution[];
|
|
93
|
+
/** Schema-validated, JSON-safe operation input. */
|
|
94
|
+
input?: unknown;
|
|
71
95
|
}
|
|
72
96
|
|
|
73
97
|
/**
|