@opengeni/contracts 4.1.0 → 5.0.0
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/atlassian.d.ts +1 -0
- package/dist/atlassian.js +4 -4
- package/dist/bundled-skills.d.ts +5 -1
- package/dist/{chunk-HR6NB3CA.js → chunk-CM7E7PFV.js} +8341 -8206
- package/dist/chunk-CM7E7PFV.js.map +1 -0
- package/dist/{chunk-O5LEQHDM.js → chunk-ENJTPHKP.js} +2 -2
- package/dist/chunk-KUCZDFJ5.js +186 -0
- package/dist/chunk-KUCZDFJ5.js.map +1 -0
- package/dist/{chunk-5LYFSZ7O.js → chunk-YJEOWXOE.js} +8 -8
- package/dist/company-profile.d.ts +9 -6
- package/dist/connect.js +4 -4
- package/dist/connection-authority.d.ts +9 -317
- package/dist/connection-authority.js +7 -27
- package/dist/connector-tool-permissions.d.ts +73 -0
- package/dist/editable-artifact-codec-registry.js +2 -2
- package/dist/editable-artifact-live.js +2 -2
- package/dist/editable-artifact-serialized-commit.js +3 -3
- package/dist/editable-artifacts.js +13 -13
- package/dist/google-drive.d.ts +1 -0
- package/dist/google-drive.js +4 -4
- package/dist/host-mcp-bindings.d.ts +5 -0
- package/dist/host-mcp-bindings.js +34 -8
- package/dist/host-mcp-bindings.js.map +1 -1
- package/dist/index.d.ts +771 -3222
- package/dist/index.js +121 -75
- package/dist/knowledge-entries.d.ts +33 -0
- package/dist/knowledge-preparation.d.ts +207 -0
- package/dist/managed-sign-in-methods.d.ts +56 -0
- package/dist/managed-sign-in-methods.js +46 -0
- package/dist/managed-sign-in-methods.js.map +1 -0
- package/dist/mcp-oauth.d.ts +2 -2
- package/dist/organization-integration-policy.d.ts +27 -0
- package/dist/organization-usage.d.ts +102 -0
- package/dist/personal-github.d.ts +1 -0
- package/dist/personal-github.js +4 -4
- package/dist/remember.d.ts +6 -4
- package/dist/sandbox-provider-command.d.ts +91 -1
- package/dist/sandbox-recovery.d.ts +101 -0
- package/dist/session-goal-reports.d.ts +21 -0
- package/dist/session-message-search.d.ts +68 -0
- package/dist/skill-catalog-context.d.ts +7 -0
- package/dist/skills.d.ts +15 -0
- package/package.json +4 -4
- package/src/bundled-skills.ts +2 -0
- package/src/connection-authority.ts +25 -225
- package/src/connector-tool-permissions.ts +59 -0
- package/src/host-mcp-bindings.ts +37 -5
- package/src/index.ts +406 -755
- package/src/knowledge-entries.ts +4 -0
- package/src/knowledge-preparation.ts +50 -0
- package/src/managed-sign-in-methods.ts +37 -0
- package/src/mcp-oauth.ts +16 -18
- package/src/organization-integration-policy.ts +54 -0
- package/src/organization-usage.ts +45 -0
- package/src/sandbox-provider-command.ts +57 -1
- package/src/sandbox-recovery.ts +61 -0
- package/src/session-goal-reports.ts +45 -0
- package/src/session-message-search.ts +54 -0
- package/src/skill-catalog-context.ts +50 -0
- package/src/skills.ts +21 -6
- package/dist/chunk-HR6NB3CA.js.map +0 -1
- package/dist/chunk-J6QIYKO5.js +0 -348
- package/dist/chunk-J6QIYKO5.js.map +0 -1
- package/dist/host-mcp-resolvers.d.ts +0 -32
- package/dist/host-mcp-resolvers.js +0 -34
- package/dist/host-mcp-resolvers.js.map +0 -1
- package/src/host-mcp-resolvers.ts +0 -50
- /package/dist/{chunk-O5LEQHDM.js.map → chunk-ENJTPHKP.js.map} +0 -0
- /package/dist/{chunk-5LYFSZ7O.js.map → chunk-YJEOWXOE.js.map} +0 -0
package/src/knowledge-entries.ts
CHANGED
|
@@ -66,6 +66,8 @@ export const KnowledgeEntrySource = z
|
|
|
66
66
|
version: z.string().min(1).max(2048).optional(),
|
|
67
67
|
capturedAt: z.string().datetime({ offset: true }).optional(),
|
|
68
68
|
retention: z.enum(["full_text", "passages", "reference"]).optional(),
|
|
69
|
+
/** Supporting evidence stays retrievable by ID without entering default discovery. */
|
|
70
|
+
purpose: z.enum(["evidence", "reference"]).optional(),
|
|
69
71
|
})
|
|
70
72
|
.strict();
|
|
71
73
|
export type KnowledgeEntrySource = z.infer<typeof KnowledgeEntrySource>;
|
|
@@ -252,6 +254,8 @@ export const KnowledgeEntryListRequest = z
|
|
|
252
254
|
/** Top-level entries with no visible collection in the selected scope. */
|
|
253
255
|
rootOnly: z.boolean().optional(),
|
|
254
256
|
fileId: z.uuid().optional(),
|
|
257
|
+
/** Include supporting sources in published discovery; never changes access. */
|
|
258
|
+
includeEvidence: z.boolean().optional(),
|
|
255
259
|
kind: KnowledgeEntryKind.optional(),
|
|
256
260
|
view: z.enum(["published", "needs_review", "archived", "rejected"]).default("published"),
|
|
257
261
|
sessionId: z.uuid().optional(),
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { KnowledgeEntryListResponse, KnowledgeEntryScope } from "./knowledge-entries";
|
|
3
|
+
|
|
4
|
+
/** Read-only discovery before a model decides whether and where to save. */
|
|
5
|
+
export const KnowledgeSavePreparationRequest = z
|
|
6
|
+
.object({
|
|
7
|
+
query: z.string().trim().min(1).max(4096),
|
|
8
|
+
limit: z.number().int().positive().max(20).default(8),
|
|
9
|
+
collectionCursors: z
|
|
10
|
+
.object({
|
|
11
|
+
published: z.string().min(1).max(2048).nullable(),
|
|
12
|
+
needs_review: z.string().min(1).max(2048).nullable(),
|
|
13
|
+
})
|
|
14
|
+
.strict()
|
|
15
|
+
.optional(),
|
|
16
|
+
})
|
|
17
|
+
.strict();
|
|
18
|
+
export type KnowledgeSavePreparationRequest = z.input<typeof KnowledgeSavePreparationRequest>;
|
|
19
|
+
|
|
20
|
+
export const KnowledgeCollectionDescriptor = z
|
|
21
|
+
.object({
|
|
22
|
+
id: z.uuid(),
|
|
23
|
+
revisionId: z.uuid(),
|
|
24
|
+
version: z.number().int().positive(),
|
|
25
|
+
scope: KnowledgeEntryScope,
|
|
26
|
+
view: z.enum(["published", "needs_review"]),
|
|
27
|
+
title: z.string(),
|
|
28
|
+
description: z.string(),
|
|
29
|
+
descriptionTruncated: z.boolean(),
|
|
30
|
+
parentIds: z.array(z.uuid()),
|
|
31
|
+
})
|
|
32
|
+
.strict();
|
|
33
|
+
|
|
34
|
+
export const KnowledgeSavePreparationResponse = z
|
|
35
|
+
.object({
|
|
36
|
+
collections: z.object({
|
|
37
|
+
entries: z.array(KnowledgeCollectionDescriptor),
|
|
38
|
+
complete: z.boolean(),
|
|
39
|
+
nextCursors: z.object({
|
|
40
|
+
published: z.string().nullable(),
|
|
41
|
+
needs_review: z.string().nullable(),
|
|
42
|
+
}),
|
|
43
|
+
}),
|
|
44
|
+
matches: z.object({
|
|
45
|
+
published: KnowledgeEntryListResponse,
|
|
46
|
+
needs_review: KnowledgeEntryListResponse,
|
|
47
|
+
}),
|
|
48
|
+
})
|
|
49
|
+
.strict();
|
|
50
|
+
export type KnowledgeSavePreparationResponse = z.infer<typeof KnowledgeSavePreparationResponse>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const ManagedSignInProvider = z.enum(["google", "github"]);
|
|
4
|
+
export const ManagedSignInMethods = z.object({
|
|
5
|
+
identityId: z.string().uuid(),
|
|
6
|
+
email: z.string().email(),
|
|
7
|
+
emailVerified: z.boolean(),
|
|
8
|
+
identityRevision: z.number().int().positive(),
|
|
9
|
+
freshAuthenticationRequired: z.boolean(),
|
|
10
|
+
methods: z.array(
|
|
11
|
+
z.object({
|
|
12
|
+
provider: z.enum(["credential", "google", "github"]),
|
|
13
|
+
connected: z.boolean(),
|
|
14
|
+
available: z.boolean(),
|
|
15
|
+
canDisconnect: z.boolean(),
|
|
16
|
+
implicitRelinkingSuppressed: z.boolean(),
|
|
17
|
+
}),
|
|
18
|
+
),
|
|
19
|
+
});
|
|
20
|
+
export type ManagedSignInMethods = z.infer<typeof ManagedSignInMethods>;
|
|
21
|
+
export const ManagedSignInMutation = z.object({
|
|
22
|
+
expectedIdentityId: z.string().uuid(),
|
|
23
|
+
operationId: z.string().uuid(),
|
|
24
|
+
expectedIdentityRevision: z.number().int().positive(),
|
|
25
|
+
});
|
|
26
|
+
export const ManagedSignInProviderMutation = ManagedSignInMutation.extend({
|
|
27
|
+
provider: ManagedSignInProvider,
|
|
28
|
+
}).strict();
|
|
29
|
+
export const ManagedSignInPasswordMutation = ManagedSignInMutation.extend({
|
|
30
|
+
newPassword: z.string().min(8).max(128),
|
|
31
|
+
currentPassword: z.string().min(1).max(1024).optional(),
|
|
32
|
+
}).strict();
|
|
33
|
+
export const ManagedSignInMutationResponse = z.object({
|
|
34
|
+
reauthenticationRequired: z.literal(true),
|
|
35
|
+
notification: z.enum(["sent", "failed", "outcome_unknown"]),
|
|
36
|
+
});
|
|
37
|
+
export const ManagedSignInConnectResponse = z.object({ url: z.string().url() });
|
package/src/mcp-oauth.ts
CHANGED
|
@@ -8,24 +8,22 @@ export const MCP_OAUTH_CONSENT_TTL_SECONDS = 10 * 60;
|
|
|
8
8
|
|
|
9
9
|
const RedirectUri = z.string().url().max(2_048);
|
|
10
10
|
|
|
11
|
-
export const McpOAuthClientRegistrationRequest = z
|
|
12
|
-
.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
})
|
|
28
|
-
.strict();
|
|
11
|
+
export const McpOAuthClientRegistrationRequest = z.object({
|
|
12
|
+
redirect_uris: z.array(RedirectUri).min(1).max(16),
|
|
13
|
+
client_name: z.string().trim().min(1).max(200).optional(),
|
|
14
|
+
application_type: z.enum(["native", "web"]).optional(),
|
|
15
|
+
token_endpoint_auth_method: z.literal("none").default("none"),
|
|
16
|
+
scope: z.literal(MCP_OAUTH_SCOPE).optional(),
|
|
17
|
+
grant_types: z
|
|
18
|
+
.array(z.enum(["authorization_code", "refresh_token"]))
|
|
19
|
+
.min(1)
|
|
20
|
+
.max(2)
|
|
21
|
+
.refine((grantTypes) => grantTypes.includes("authorization_code"), {
|
|
22
|
+
message: "authorization_code grant is required",
|
|
23
|
+
})
|
|
24
|
+
.default(["authorization_code", "refresh_token"]),
|
|
25
|
+
response_types: z.array(z.literal("code")).min(1).max(1).default(["code"]),
|
|
26
|
+
});
|
|
29
27
|
export type McpOAuthClientRegistrationRequest = z.infer<typeof McpOAuthClientRegistrationRequest>;
|
|
30
28
|
|
|
31
29
|
export const McpOAuthClientRegistrationResponse = McpOAuthClientRegistrationRequest.extend({
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/** Catalog identities, never endpoint/domain matching. Custom transports are explicit categories. */
|
|
4
|
+
export const OrganizationIntegrationKey = z
|
|
5
|
+
.string()
|
|
6
|
+
.min(1)
|
|
7
|
+
.max(200)
|
|
8
|
+
.regex(/^[a-z0-9][a-z0-9._:-]*$/);
|
|
9
|
+
const keys = z
|
|
10
|
+
.array(OrganizationIntegrationKey)
|
|
11
|
+
.max(10000)
|
|
12
|
+
.refine((values) => new Set(values).size === values.length, "Duplicate integration identities");
|
|
13
|
+
export const OrganizationIntegrationPolicy = z
|
|
14
|
+
.object({
|
|
15
|
+
mode: z.enum(["unrestricted", "restricted"]).default("unrestricted"),
|
|
16
|
+
allowedIntegrationKeys: keys.default([]),
|
|
17
|
+
revision: z.number().int().nonnegative().safe().default(0),
|
|
18
|
+
})
|
|
19
|
+
.strict();
|
|
20
|
+
export type OrganizationIntegrationPolicy = z.infer<typeof OrganizationIntegrationPolicy>;
|
|
21
|
+
|
|
22
|
+
export const UpdateOrganizationIntegrationPolicyRequest = z
|
|
23
|
+
.object({
|
|
24
|
+
mode: z.enum(["unrestricted", "restricted"]),
|
|
25
|
+
allowedIntegrationKeys: keys,
|
|
26
|
+
expectedRevision: z
|
|
27
|
+
.number()
|
|
28
|
+
.int()
|
|
29
|
+
.nonnegative()
|
|
30
|
+
.max(Number.MAX_SAFE_INTEGER - 1),
|
|
31
|
+
operationId: z.string().uuid(),
|
|
32
|
+
})
|
|
33
|
+
.strict();
|
|
34
|
+
export type UpdateOrganizationIntegrationPolicyRequest = z.infer<
|
|
35
|
+
typeof UpdateOrganizationIntegrationPolicyRequest
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
export class OrganizationIntegrationDeniedError extends Error {
|
|
39
|
+
constructor() {
|
|
40
|
+
super("Integration acquisition is not allowed by organization policy");
|
|
41
|
+
this.name = "OrganizationIntegrationDeniedError";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** integrationKey must come from trusted classification, not client-supplied metadata. */
|
|
46
|
+
export function assertOrganizationIntegrationAllowed(
|
|
47
|
+
policy: OrganizationIntegrationPolicy,
|
|
48
|
+
integrationKey: string | null,
|
|
49
|
+
): void {
|
|
50
|
+
if (policy.mode === "unrestricted") return;
|
|
51
|
+
if (integrationKey === null || !policy.allowedIntegrationKeys.includes(integrationKey)) {
|
|
52
|
+
throw new OrganizationIntegrationDeniedError();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const OrganizationUsagePeriod = z.enum(["today", "week", "month", "ytd"]);
|
|
4
|
+
export type OrganizationUsagePeriod = z.infer<typeof OrganizationUsagePeriod>;
|
|
5
|
+
export const OrganizationUsageQuery = z.object({
|
|
6
|
+
period: OrganizationUsagePeriod.default("month"),
|
|
7
|
+
});
|
|
8
|
+
export const OrganizationUsageWorkspacePageQuery = OrganizationUsageQuery.extend({
|
|
9
|
+
until: z.string().datetime(),
|
|
10
|
+
afterWorkspaceId: z.string().uuid().optional(),
|
|
11
|
+
});
|
|
12
|
+
// PostgreSQL SUM(bigint) must never pass through a JavaScript number.
|
|
13
|
+
export const OrganizationUsageTotal = z.object({
|
|
14
|
+
eventType: z.string(),
|
|
15
|
+
unit: z.string(),
|
|
16
|
+
quantity: z.string().regex(/^-?\d+$/),
|
|
17
|
+
eventCount: z.string().regex(/^\d+$/),
|
|
18
|
+
});
|
|
19
|
+
export const OrganizationUsageSummary = z.object({
|
|
20
|
+
accountId: z.string().uuid(),
|
|
21
|
+
period: OrganizationUsagePeriod,
|
|
22
|
+
since: z.string().datetime(),
|
|
23
|
+
until: z.string().datetime(),
|
|
24
|
+
granularity: z.enum(["hour", "day"]),
|
|
25
|
+
totals: z.array(OrganizationUsageTotal),
|
|
26
|
+
buckets: z.array(z.object({ bucket: z.string(), totals: z.array(OrganizationUsageTotal) })),
|
|
27
|
+
workspaces: z
|
|
28
|
+
.array(
|
|
29
|
+
z.object({
|
|
30
|
+
workspaceId: z.string().uuid(),
|
|
31
|
+
name: z.string().nullable(),
|
|
32
|
+
totals: z.array(OrganizationUsageTotal),
|
|
33
|
+
}),
|
|
34
|
+
)
|
|
35
|
+
.max(50),
|
|
36
|
+
nextWorkspaceCursor: z.string().uuid().nullable(),
|
|
37
|
+
});
|
|
38
|
+
export type OrganizationUsageSummary = z.infer<typeof OrganizationUsageSummary>;
|
|
39
|
+
|
|
40
|
+
/** A page deliberately excludes totals and charts: those are not recomputed. */
|
|
41
|
+
export const OrganizationUsageWorkspacePage = OrganizationUsageSummary.omit({
|
|
42
|
+
totals: true,
|
|
43
|
+
buckets: true,
|
|
44
|
+
});
|
|
45
|
+
export type OrganizationUsageWorkspacePage = z.infer<typeof OrganizationUsageWorkspacePage>;
|
|
@@ -13,7 +13,7 @@ const ModalOutputCursor = z
|
|
|
13
13
|
|
|
14
14
|
/** Provider-owned execution identity and acknowledged output cursors. These
|
|
15
15
|
* values are retained by the control plane, never recovered from sandbox files. */
|
|
16
|
-
export const
|
|
16
|
+
export const ModalLegacyProviderCommand = z
|
|
17
17
|
.object({
|
|
18
18
|
kind: z.literal("modal-control-v1"),
|
|
19
19
|
sandboxId: z.string().min(1).max(200),
|
|
@@ -23,4 +23,60 @@ export const SandboxProviderCommand = z
|
|
|
23
23
|
streams: z.object({ stdout: ModalOutputCursor, stderr: ModalOutputCursor }).strict(),
|
|
24
24
|
})
|
|
25
25
|
.strict();
|
|
26
|
+
export type ModalLegacyProviderCommand = z.infer<typeof ModalLegacyProviderCommand>;
|
|
27
|
+
|
|
28
|
+
const ModalByteCursor = z
|
|
29
|
+
.object({
|
|
30
|
+
byteOffset: z.number().int().nonnegative().safe(),
|
|
31
|
+
utf8Remainder: z
|
|
32
|
+
.string()
|
|
33
|
+
.max(8)
|
|
34
|
+
.regex(/^[A-Za-z0-9+/]*={0,2}$/u),
|
|
35
|
+
eof: z.boolean(),
|
|
36
|
+
exitCode: z.number().int().nullable(),
|
|
37
|
+
})
|
|
38
|
+
.strict()
|
|
39
|
+
.refine((cursor) => cursor.exitCode === null || cursor.eof, {
|
|
40
|
+
message: "A terminal output cursor must have reached EOF",
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export const CommandSupervisionDescriptor = z
|
|
44
|
+
.object({
|
|
45
|
+
protocol: z.literal("native-subreaper-v1"),
|
|
46
|
+
invocationId: z.string().uuid(),
|
|
47
|
+
nonce: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
48
|
+
controlPath: z.string().regex(/^\/tmp\/opengeni-supervision\/[a-f0-9-]{36}\.sock$/u),
|
|
49
|
+
})
|
|
50
|
+
.strict();
|
|
51
|
+
export type CommandSupervisionDescriptor = z.infer<typeof CommandSupervisionDescriptor>;
|
|
52
|
+
|
|
53
|
+
export const CommandSupervisionReceipt = z
|
|
54
|
+
.object({
|
|
55
|
+
protocol: z.literal("native-subreaper-v1"),
|
|
56
|
+
invocationId: z.string().uuid(),
|
|
57
|
+
receiptId: z.string().uuid(),
|
|
58
|
+
leaderExitCode: z.number().int(),
|
|
59
|
+
})
|
|
60
|
+
.strict();
|
|
61
|
+
export type CommandSupervisionReceipt = z.infer<typeof CommandSupervisionReceipt>;
|
|
62
|
+
|
|
63
|
+
export const ModalRouterProviderCommand = z
|
|
64
|
+
.object({
|
|
65
|
+
kind: z.literal("modal-router-v1"),
|
|
66
|
+
sandboxId: z.string().min(1).max(200),
|
|
67
|
+
taskId: z.string().min(1).max(200),
|
|
68
|
+
execId: z.string().uuid(),
|
|
69
|
+
pty: z.boolean().optional(),
|
|
70
|
+
supervision: CommandSupervisionDescriptor.optional(),
|
|
71
|
+
streams: z.object({ stdout: ModalByteCursor, stderr: ModalByteCursor }).strict(),
|
|
72
|
+
})
|
|
73
|
+
.strict();
|
|
74
|
+
export type ModalRouterProviderCommand = z.infer<typeof ModalRouterProviderCommand>;
|
|
75
|
+
|
|
76
|
+
/** Legacy locators are read-only compatibility for already launched commands;
|
|
77
|
+
* new starts use authenticated task-router byte offsets, never batch indices. */
|
|
78
|
+
export const SandboxProviderCommand = z.discriminatedUnion("kind", [
|
|
79
|
+
ModalLegacyProviderCommand,
|
|
80
|
+
ModalRouterProviderCommand,
|
|
81
|
+
]);
|
|
26
82
|
export type SandboxProviderCommand = z.infer<typeof SandboxProviderCommand>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/** Public, content-free selection. Never include native provider bindings. */
|
|
4
|
+
export const SandboxRecoverySelection = z
|
|
5
|
+
.object({
|
|
6
|
+
version: z.literal(1),
|
|
7
|
+
sessionId: z.string().uuid(),
|
|
8
|
+
sandboxGroupId: z.string().uuid(),
|
|
9
|
+
leaseId: z.string().uuid(),
|
|
10
|
+
routeEpoch: z.number().int().nonnegative(),
|
|
11
|
+
authorityEpoch: z.number().int().positive(),
|
|
12
|
+
leaseEpoch: z.number().int().nonnegative(),
|
|
13
|
+
workspaceGeneration: z.number().int().nonnegative(),
|
|
14
|
+
archiveGeneration: z.number().int().nonnegative(),
|
|
15
|
+
artifactId: z.string().uuid(),
|
|
16
|
+
revision: z.string().min(1).max(512),
|
|
17
|
+
capturedAt: z.string().datetime(),
|
|
18
|
+
})
|
|
19
|
+
.strict();
|
|
20
|
+
export type SandboxRecoverySelection = z.infer<typeof SandboxRecoverySelection>;
|
|
21
|
+
|
|
22
|
+
export const SandboxRecoveryProjection = z
|
|
23
|
+
.object({
|
|
24
|
+
version: z.literal(1),
|
|
25
|
+
status: z.enum([
|
|
26
|
+
"unsupported",
|
|
27
|
+
"blocked",
|
|
28
|
+
"eligible",
|
|
29
|
+
"consent_accepted",
|
|
30
|
+
"restoring",
|
|
31
|
+
"restored",
|
|
32
|
+
]),
|
|
33
|
+
reason: z.string().max(128).nullable(),
|
|
34
|
+
checkpoint: SandboxRecoverySelection.nullable(),
|
|
35
|
+
operationId: z.string().uuid().nullable(),
|
|
36
|
+
})
|
|
37
|
+
.strict();
|
|
38
|
+
export type SandboxRecoveryProjection = z.infer<typeof SandboxRecoveryProjection>;
|
|
39
|
+
|
|
40
|
+
export const SandboxRecoveryRequest = z
|
|
41
|
+
.object({
|
|
42
|
+
operationId: z.string().uuid(),
|
|
43
|
+
acceptHistoricalCheckpoint: z.literal(true),
|
|
44
|
+
selection: SandboxRecoverySelection,
|
|
45
|
+
})
|
|
46
|
+
.strict();
|
|
47
|
+
export type SandboxRecoveryRequest = z.infer<typeof SandboxRecoveryRequest>;
|
|
48
|
+
|
|
49
|
+
export const SandboxRecoveryResponse = z
|
|
50
|
+
.object({
|
|
51
|
+
outcome: z.enum(["accepted", "replayed"]),
|
|
52
|
+
operationId: z.string().uuid(),
|
|
53
|
+
recovery: SandboxRecoveryProjection,
|
|
54
|
+
})
|
|
55
|
+
.strict();
|
|
56
|
+
export type SandboxRecoveryResponse = z.infer<typeof SandboxRecoveryResponse>;
|
|
57
|
+
|
|
58
|
+
/** Re-injected from durable receipts on every attempt, not lossy transcript state. */
|
|
59
|
+
export function sandboxRecoveryDiscontinuity(selection: SandboxRecoverySelection): string {
|
|
60
|
+
return `Filesystem discontinuity: the human explicitly consented to restoring this session's workspace from the checkpoint captured at ${selection.capturedAt} (archive generation ${selection.archiveGeneration}, pre-recovery workspace generation ${selection.workspaceGeneration}). Newer filesystem changes are unavailable. The generation gap is not a count of lost files or edits. Conversation and tool receipts remain historical evidence, not proof that their files still exist. External effects are not undone. Verify the current filesystem before relying on previous work. Never automatically replay prior commands or operations with unknown outcomes to rebuild missing files. Consent alone is not proof that restoration succeeded.`;
|
|
61
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const SessionGoalReportRequirement = z
|
|
4
|
+
.object({
|
|
5
|
+
id: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/),
|
|
6
|
+
title: z
|
|
7
|
+
.string()
|
|
8
|
+
.trim()
|
|
9
|
+
.min(1)
|
|
10
|
+
.refine(
|
|
11
|
+
(value) => new TextEncoder().encode(value).byteLength <= 512,
|
|
12
|
+
"Report title exceeds 512 UTF-8 bytes",
|
|
13
|
+
),
|
|
14
|
+
})
|
|
15
|
+
.strict();
|
|
16
|
+
export type SessionGoalReportRequirement = z.infer<typeof SessionGoalReportRequirement>;
|
|
17
|
+
|
|
18
|
+
export const SessionGoalReportRequirements = z
|
|
19
|
+
.array(SessionGoalReportRequirement)
|
|
20
|
+
.max(16)
|
|
21
|
+
.superRefine((requirements, context) => {
|
|
22
|
+
if (new Set(requirements.map((item) => item.id)).size !== requirements.length) {
|
|
23
|
+
context.addIssue({ code: "custom", message: "Duplicate report requirement IDs" });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const SessionGoalReportDelivery = z
|
|
28
|
+
.object({
|
|
29
|
+
requirementId: SessionGoalReportRequirement.shape.id,
|
|
30
|
+
artifactId: z
|
|
31
|
+
.string()
|
|
32
|
+
.regex(/^[0-9a-f]{32}$/)
|
|
33
|
+
.refine((value) => !/^0+$/.test(value)),
|
|
34
|
+
inspectionReceiptId: z.string().uuid(),
|
|
35
|
+
})
|
|
36
|
+
.strict();
|
|
37
|
+
export type SessionGoalReportDelivery = z.infer<typeof SessionGoalReportDelivery>;
|
|
38
|
+
export const SessionGoalReportDeliveries = z
|
|
39
|
+
.array(SessionGoalReportDelivery)
|
|
40
|
+
.max(16)
|
|
41
|
+
.superRefine((deliveries, context) => {
|
|
42
|
+
if (new Set(deliveries.map((item) => item.requirementId)).size !== deliveries.length) {
|
|
43
|
+
context.addIssue({ code: "custom", message: "Duplicate report delivery requirement IDs" });
|
|
44
|
+
}
|
|
45
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/** Literal Unicode case-insensitive search, without trimming or normalization. */
|
|
4
|
+
export const SessionMessageSearchRequest = z
|
|
5
|
+
.object({
|
|
6
|
+
query: z.string().min(1).max(200),
|
|
7
|
+
sessionId: z.string().uuid().optional(),
|
|
8
|
+
groupBy: z.literal("session").optional(),
|
|
9
|
+
archiveStatus: z.enum(["active", "archived", "all"]).optional(),
|
|
10
|
+
limit: z.number().int().min(1).max(50).optional(),
|
|
11
|
+
cursor: z.string().min(1).max(4096).optional(),
|
|
12
|
+
})
|
|
13
|
+
.strict()
|
|
14
|
+
.refine((request) => !request.groupBy || !request.sessionId, {
|
|
15
|
+
message: "groupBy=session is only supported for workspace search without sessionId",
|
|
16
|
+
path: ["groupBy"],
|
|
17
|
+
});
|
|
18
|
+
export type SessionMessageSearchRequest = z.infer<typeof SessionMessageSearchRequest>;
|
|
19
|
+
|
|
20
|
+
export const SessionMessageSearchMatch = z.object({
|
|
21
|
+
sessionId: z.string().uuid(),
|
|
22
|
+
sessionTitle: z.string().nullable(),
|
|
23
|
+
eventId: z.string().uuid(),
|
|
24
|
+
sequence: z.number().int().positive(),
|
|
25
|
+
turnId: z.string().uuid().nullable(),
|
|
26
|
+
role: z.enum(["user", "assistant"]),
|
|
27
|
+
messageId: z.string().nullable(),
|
|
28
|
+
/** All offsets are zero-based UTF-16 code units; ends are exclusive. */
|
|
29
|
+
messageMatchOffset: z.number().int().nonnegative(),
|
|
30
|
+
snippet: z.object({
|
|
31
|
+
text: z.string(),
|
|
32
|
+
matchStart: z.number().int().nonnegative(),
|
|
33
|
+
matchEnd: z.number().int().nonnegative(),
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
export type SessionMessageSearchMatch = z.infer<typeof SessionMessageSearchMatch>;
|
|
37
|
+
|
|
38
|
+
/** A bounded scan page, not necessarily a full page of matches. Continue even
|
|
39
|
+
* after an empty page while hasMore is true. Counts describe this live traversal,
|
|
40
|
+
* not a frozen snapshot; restart to reflect concurrent changes. All non-overlapping
|
|
41
|
+
* literal occurrences in user messages and completed assistant messages.
|
|
42
|
+
* With groupBy=session, matches and both matched counts describe one first-hit
|
|
43
|
+
* representative per matching session, not full message/occurrence totals.
|
|
44
|
+
*/
|
|
45
|
+
export const SessionMessageSearchResponse = z.object({
|
|
46
|
+
matches: z.array(SessionMessageSearchMatch).max(50),
|
|
47
|
+
nextCursor: z.string().nullable(),
|
|
48
|
+
hasMore: z.boolean(),
|
|
49
|
+
scannedMessages: z.number().int().nonnegative(),
|
|
50
|
+
matchedMessageCount: z.number().int().nonnegative(),
|
|
51
|
+
matchedOccurrenceCount: z.number().int().nonnegative(),
|
|
52
|
+
countIsExact: z.boolean(),
|
|
53
|
+
});
|
|
54
|
+
export type SessionMessageSearchResponse = z.infer<typeof SessionMessageSearchResponse>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** Durable, model-visible catalog context. Text markers survive SDK/provider replay. */
|
|
2
|
+
export const SKILL_CATALOG_CONTEXT_PREFIX = "<opengeni_skill_catalog>\n";
|
|
3
|
+
export const SKILL_CATALOG_CONTEXT_SUFFIX = "\n</opengeni_skill_catalog>";
|
|
4
|
+
const GUIDANCE =
|
|
5
|
+
"This is the current configured Skill index and replaces earlier configured Skill indexes. " +
|
|
6
|
+
"Entries describe discoverability, not permission or full instructions. " +
|
|
7
|
+
"Use skill_read for instructions and skill_search for current discovery; tool authorization remains authoritative.\n\n";
|
|
8
|
+
|
|
9
|
+
export function skillCatalogContextItem(catalog: string): Record<string, unknown> {
|
|
10
|
+
return {
|
|
11
|
+
type: "message",
|
|
12
|
+
role: "developer",
|
|
13
|
+
content: SKILL_CATALOG_CONTEXT_PREFIX + GUIDANCE + catalog + SKILL_CATALOG_CONTEXT_SUFFIX,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Accept the Chat Completions projection and SDK text-part normalization too. */
|
|
18
|
+
export function readSkillCatalogContext(item: Record<string, unknown>): string | null {
|
|
19
|
+
if (item.type === "unknown" && item.providerData && typeof item.providerData === "object") {
|
|
20
|
+
const data = item.providerData as Record<string, unknown>;
|
|
21
|
+
if (data.type === "message") return readSkillCatalogContext(data);
|
|
22
|
+
}
|
|
23
|
+
if (item.type !== "message" || (item.role !== "developer" && item.role !== "system")) return null;
|
|
24
|
+
const content =
|
|
25
|
+
typeof item.content === "string"
|
|
26
|
+
? item.content
|
|
27
|
+
: Array.isArray(item.content) &&
|
|
28
|
+
item.content.every(
|
|
29
|
+
(part) =>
|
|
30
|
+
part &&
|
|
31
|
+
(part.type === "input_text" || part.type === "output_text" || part.type === "text") &&
|
|
32
|
+
typeof part.text === "string",
|
|
33
|
+
)
|
|
34
|
+
? item.content.map((part) => part.text).join("")
|
|
35
|
+
: null;
|
|
36
|
+
const prefix = SKILL_CATALOG_CONTEXT_PREFIX + GUIDANCE;
|
|
37
|
+
return content?.startsWith(prefix) && content.endsWith(SKILL_CATALOG_CONTEXT_SUFFIX)
|
|
38
|
+
? content.slice(prefix.length, -SKILL_CATALOG_CONTEXT_SUFFIX.length)
|
|
39
|
+
: null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function latestSkillCatalogContext(
|
|
43
|
+
items: readonly Record<string, unknown>[],
|
|
44
|
+
): Record<string, unknown> | undefined {
|
|
45
|
+
for (let index = items.length - 1; index >= 0; index--) {
|
|
46
|
+
const catalog = readSkillCatalogContext(items[index]!);
|
|
47
|
+
if (catalog !== null) return skillCatalogContextItem(catalog);
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
package/src/skills.ts
CHANGED
|
@@ -38,6 +38,7 @@ export type SkillSaveInput = SkillWriteContext & {
|
|
|
38
38
|
reason: string;
|
|
39
39
|
};
|
|
40
40
|
export type SkillRevisionInput = SkillWriteContext & {
|
|
41
|
+
removalOperationId?: string | undefined;
|
|
41
42
|
operationId: string;
|
|
42
43
|
skillId: string;
|
|
43
44
|
revisionId: string;
|
|
@@ -45,12 +46,20 @@ export type SkillRevisionInput = SkillWriteContext & {
|
|
|
45
46
|
expectedScopeVersion: number;
|
|
46
47
|
reason: string;
|
|
47
48
|
};
|
|
49
|
+
export type SkillRemoveInput = SkillWriteContext & {
|
|
50
|
+
operationId: string;
|
|
51
|
+
skillId: string;
|
|
52
|
+
expectedRevisionId: string | null;
|
|
53
|
+
expectedScopeVersion: number;
|
|
54
|
+
reason: string;
|
|
55
|
+
};
|
|
48
56
|
export type SkillInstallInput = SkillWriteContext & {
|
|
49
57
|
operationId: string;
|
|
50
58
|
skillFacetId: string;
|
|
51
59
|
reason: string;
|
|
52
60
|
};
|
|
53
61
|
export const SkillReviewReference = z.object({
|
|
62
|
+
removalOperationId: z.uuid().optional(),
|
|
54
63
|
sourceOperationId: z.uuid(),
|
|
55
64
|
skillId: z.uuid(),
|
|
56
65
|
revisionId: z.uuid(),
|
|
@@ -59,18 +68,22 @@ export const SkillReviewReference = z.object({
|
|
|
59
68
|
});
|
|
60
69
|
export type SkillReviewReference = z.infer<typeof SkillReviewReference>;
|
|
61
70
|
export function skillReviewHumanInput(skillReview: SkillReviewReference) {
|
|
71
|
+
const removing = Boolean(skillReview.removalOperationId);
|
|
62
72
|
return {
|
|
63
73
|
questions: [
|
|
64
74
|
{
|
|
65
75
|
id: `skill:${skillReview.revisionId}`,
|
|
66
76
|
kind: "single_select" as const,
|
|
67
|
-
label: "Save this Skill?",
|
|
68
|
-
prompt:
|
|
69
|
-
|
|
70
|
-
"
|
|
77
|
+
label: removing ? "Permanently delete this Skill?" : "Save this Skill?",
|
|
78
|
+
prompt: removing
|
|
79
|
+
? "Permanently delete this Skill and all its stored revisions?"
|
|
80
|
+
: "Save this exact Skill revision for this workspace?",
|
|
81
|
+
helpText: removing
|
|
82
|
+
? "This cannot be undone. All stored Skill revisions will be deleted; conversations remain unchanged."
|
|
83
|
+
: "Review the complete files before saving. Saving activates this revision immediately.",
|
|
71
84
|
options: [
|
|
72
|
-
{ id: "save", label: "Save" },
|
|
73
|
-
{ id: "skip", label: "Don't save" },
|
|
85
|
+
{ id: "save", label: removing ? "Permanently delete" : "Save" },
|
|
86
|
+
{ id: "skip", label: removing ? "Keep Skill" : "Don't save" },
|
|
74
87
|
],
|
|
75
88
|
required: true,
|
|
76
89
|
allowOther: false,
|
|
@@ -80,6 +93,7 @@ export function skillReviewHumanInput(skillReview: SkillReviewReference) {
|
|
|
80
93
|
};
|
|
81
94
|
}
|
|
82
95
|
export const SkillWriteReceipt = z.object({
|
|
96
|
+
removed: z.boolean().optional(),
|
|
83
97
|
operationId: z.uuid(),
|
|
84
98
|
skillId: z.uuid(),
|
|
85
99
|
revisionId: z.uuid(),
|
|
@@ -104,6 +118,7 @@ export const SkillSourceReleaseReceipt = z.object({
|
|
|
104
118
|
});
|
|
105
119
|
export type SkillSourceReleaseReceipt = z.infer<typeof SkillSourceReleaseReceipt>;
|
|
106
120
|
export type SkillRecord = {
|
|
121
|
+
removalOperationId?: string | null;
|
|
107
122
|
activationMode: "workspace_managed" | "session_selected";
|
|
108
123
|
pendingRevisionIds: string[];
|
|
109
124
|
id: string;
|