@opengeni/core 3.0.1 → 3.1.2
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/README.md +4 -4
- package/dist/access/index.d.ts +131 -7
- package/dist/access/service-initiator.d.ts +3 -0
- package/dist/application/archived-session-imports.d.ts +6 -0
- package/dist/application/composer-submit.d.ts +1 -1
- package/dist/application/connect-operation.d.ts +6 -0
- package/dist/application/external-workspace-members.d.ts +13 -1
- package/dist/application/modal-native-live-declaration-join.d.ts +37 -0
- package/dist/application/modal-native-original-configuration.d.ts +79 -0
- package/dist/application/modal-native-worker-host-transport.d.ts +297 -0
- package/dist/application/new-session-drafts.d.ts +29 -2
- package/dist/application/organization-privacy.d.ts +57 -0
- package/dist/application/prepared-mcp-connection.d.ts +31 -0
- package/dist/application/sandbox-recovery.d.ts +58 -0
- package/dist/application/session-commands.d.ts +3 -0
- package/dist/application/session-tenancy.d.ts +13 -4
- package/dist/billing/agent-run-admission.d.ts +21 -0
- package/dist/billing/limits.d.ts +27 -1
- package/dist/billing/model-call-admission.d.ts +34 -0
- package/dist/billing/model-usage-settlement.d.ts +112 -0
- package/dist/canonical-human-identities.js +1 -1
- package/dist/{chunk-VCZFFEPB.js → chunk-7T2SZ7BI.js} +16 -2
- package/dist/chunk-7T2SZ7BI.js.map +1 -0
- package/dist/{chunk-ZYCNLYGH.js → chunk-OMJFIIWK.js} +178 -12
- package/dist/chunk-OMJFIIWK.js.map +1 -0
- package/dist/{chunk-H7X52RI2.js → chunk-QI2M2B2K.js} +83 -8
- package/dist/chunk-QI2M2B2K.js.map +1 -0
- package/dist/{chunk-OF65T3PM.js → chunk-TTWTXQB2.js} +94 -6
- package/dist/chunk-TTWTXQB2.js.map +1 -0
- package/dist/codex-model-availability.d.ts +55 -0
- package/dist/default-session-model.d.ts +192 -0
- package/dist/dependencies.d.ts +41 -7
- package/dist/developer-setup-credential-ceiling.d.ts +10 -0
- package/dist/domain/agent-config-resolution.d.ts +49 -0
- package/dist/domain/automations.d.ts +8 -1
- package/dist/domain/capabilities.d.ts +112 -8
- package/dist/domain/connection-account-selection-error.d.ts +8 -0
- package/dist/domain/connector-tool-permissions.d.ts +62 -0
- package/dist/domain/direct-model-provider.d.ts +3 -0
- package/dist/domain/editable-artifacts/agent-application.d.ts +10 -1
- package/dist/domain/editable-artifacts/durable-export.d.ts +22 -1
- package/dist/domain/editable-artifacts/ports.d.ts +10 -0
- package/dist/domain/editable-artifacts/service.d.ts +3 -1
- package/dist/domain/editable-artifacts/types.d.ts +1 -1
- package/dist/domain/encryption-key.d.ts +12 -0
- package/dist/domain/insights-timing.d.ts +16 -0
- package/dist/domain/insights-usage.d.ts +27 -0
- package/dist/domain/insights.d.ts +21 -3
- package/dist/domain/knowledge-files.d.ts +1 -1
- package/dist/domain/knowledge-preparation.d.ts +10 -0
- package/dist/domain/knowledge-preparation.test.d.ts +1 -0
- package/dist/domain/knowledge-search.d.ts +15 -76
- package/dist/domain/mcp-account-bindings.d.ts +17 -0
- package/dist/domain/mcp-account-routes.d.ts +21 -0
- package/dist/domain/native-mcp-connection-admission.d.ts +3 -0
- package/dist/domain/organization-integration-catalog.d.ts +15 -0
- package/dist/domain/personal-connection-delegations.d.ts +73 -12
- package/dist/domain/pr-review.d.ts +121 -6
- package/dist/domain/realtime-voice-billing.d.ts +103 -0
- package/dist/domain/resources.d.ts +17 -1
- package/dist/domain/scheduled-task-access.d.ts +265 -0
- package/dist/domain/scheduled-task-account-audit.d.ts +27 -0
- package/dist/domain/scheduled-task-audience.d.ts +70 -0
- package/dist/domain/scheduled-tasks.d.ts +61 -18
- package/dist/domain/session-connection-accounts.d.ts +53 -0
- package/dist/domain/session-file-access.d.ts +4 -0
- package/dist/domain/session-native-connection-replacement.d.ts +14 -0
- package/dist/domain/session-start-timing.d.ts +7 -0
- package/dist/domain/session-tool-policy.d.ts +102 -4
- package/dist/domain/sessions.d.ts +179 -15
- package/dist/domain/site-audience.d.ts +52 -0
- package/dist/domain/skills.d.ts +29 -1
- package/dist/domain/slack-bot.d.ts +46 -1
- package/dist/domain/slack-publication-secret-safety.d.ts +1 -1
- package/dist/domain/vaults/index.d.ts +7 -0
- package/dist/domain/vaults/mapping.d.ts +25 -0
- package/dist/domain/vaults/provider-registry.d.ts +72 -0
- package/dist/domain/vaults/provider-sync.d.ts +176 -0
- package/dist/domain/vaults/provider-writes.d.ts +57 -0
- package/dist/domain/vaults/secret-resolver.d.ts +34 -0
- package/dist/domain/vaults/types.d.ts +24 -0
- package/dist/domain/vaults/vault-service.d.ts +35 -0
- package/dist/domain/voice-input-billing.d.ts +77 -0
- package/dist/domain/web-search-billing.d.ts +51 -0
- package/dist/domain/workspace-credits.d.ts +7 -0
- package/dist/editable-artifact-live/application.d.ts +2 -1
- package/dist/editable-artifact-live/server.d.ts +5 -1
- package/dist/editable-artifact-live/ticket.d.ts +2 -1
- package/dist/editable-artifact-live/types.d.ts +9 -0
- package/dist/editable-artifact-live.js +2 -2
- package/dist/editable-artifacts.js +10 -2
- package/dist/goal-admission.d.ts +627 -0
- package/dist/host-session-usage-limit.d.ts +15 -0
- package/dist/index.d.ts +43 -8
- package/dist/index.js +22682 -11743
- package/dist/index.js.map +1 -1
- package/dist/managed-session.d.ts +110 -7
- package/dist/model-call.d.ts +58 -0
- package/dist/model-catalog.d.ts +29 -3
- package/dist/model-provider-credentials.d.ts +43 -0
- package/dist/product-usage-metrics.d.ts +30 -0
- package/dist/rigs/index.d.ts +1 -13
- package/dist/sandbox/fleet.d.ts +58 -4
- package/dist/sandbox/routing.d.ts +42 -2
- package/dist/sandbox/runtime-settings.d.ts +27 -17
- package/dist/session-authorization.d.ts +10 -0
- package/dist/session-usage-limit-metrics.d.ts +60 -0
- package/dist/session-usage-limits.d.ts +89 -0
- package/dist/site-agent-clamp.d.ts +71 -0
- package/dist/site-session-origin.d.ts +39 -2
- package/dist/transcription.d.ts +120 -3
- package/dist/turn-surface.d.ts +20 -0
- package/dist/user-presence.d.ts +32 -0
- package/dist/workflow-wake-contract.d.ts +13 -1
- package/package.json +15 -22
- package/src/access/index.ts +961 -38
- package/src/access/service-initiator.ts +50 -0
- package/src/application/api-integration-servers.ts +9 -0
- package/src/application/archived-session-imports.ts +161 -0
- package/src/application/composer-submit.ts +3 -2
- package/src/application/connect-authority.ts +3 -3
- package/src/application/connect-operation.ts +37 -3
- package/src/application/external-continuation.ts +20 -3
- package/src/application/external-link-work-admission.ts +3 -0
- package/src/application/external-workspace-members.ts +51 -3
- package/src/application/modal-native-live-declaration-join.ts +196 -0
- package/src/application/modal-native-original-configuration.ts +261 -0
- package/src/application/modal-native-worker-host-transport.ts +263 -0
- package/src/application/new-session-drafts.ts +172 -55
- package/src/application/organization-privacy.ts +161 -0
- package/src/application/prepared-mcp-connection.ts +133 -0
- package/src/application/sandbox-recovery.ts +78 -0
- package/src/application/session-commands.ts +73 -1
- package/src/application/session-mcp-credential-rotation.ts +61 -18
- package/src/application/session-tenancy.ts +262 -44
- package/src/application/user-resource-grants.ts +1 -29
- package/src/billing/agent-run-admission.ts +117 -0
- package/src/billing/limits.ts +138 -14
- package/src/billing/model-call-admission.ts +118 -0
- package/src/billing/model-usage-settlement.ts +472 -0
- package/src/codex-model-availability.ts +264 -0
- package/src/default-session-model.ts +692 -0
- package/src/dependencies.ts +48 -6
- package/src/developer-setup-credential-ceiling.ts +60 -0
- package/src/domain/agent-config-resolution.ts +148 -0
- package/src/domain/automations.ts +76 -2
- package/src/domain/capabilities.ts +830 -181
- package/src/domain/connection-account-selection-error.ts +19 -0
- package/src/domain/connector-tool-permissions.ts +572 -0
- package/src/domain/direct-model-provider.ts +62 -0
- package/src/domain/editable-artifacts/agent-application.ts +44 -6
- package/src/domain/editable-artifacts/durable-export.ts +100 -5
- package/src/domain/editable-artifacts/in-memory.ts +6 -0
- package/src/domain/editable-artifacts/ports.ts +11 -0
- package/src/domain/editable-artifacts/service.ts +20 -0
- package/src/domain/editable-artifacts/types.ts +1 -1
- package/src/domain/encryption-key.ts +26 -0
- package/src/domain/file-owner.ts +16 -1
- package/src/domain/github-action-policies.ts +1 -1
- package/src/domain/github-skill-source.ts +3 -1
- package/src/domain/insights-timing.ts +95 -0
- package/src/domain/insights-usage.ts +67 -0
- package/src/domain/insights.ts +167 -43
- package/src/domain/knowledge-files.ts +10 -3
- package/src/domain/knowledge-messages.ts +1 -0
- package/src/domain/knowledge-preparation.test.ts +276 -0
- package/src/domain/knowledge-preparation.ts +147 -0
- package/src/domain/knowledge-search.test.ts +617 -5
- package/src/domain/knowledge-search.ts +240 -23
- package/src/domain/mcp-account-bindings.ts +169 -0
- package/src/domain/mcp-account-routes.ts +152 -0
- package/src/domain/native-mcp-connection-admission.ts +14 -0
- package/src/domain/organization-integration-catalog.ts +88 -0
- package/src/domain/personal-connection-delegations.ts +399 -257
- package/src/domain/pr-review.ts +24 -8
- package/src/domain/realtime-voice-billing.ts +425 -0
- package/src/domain/resources.ts +50 -4
- package/src/domain/scheduled-task-access.ts +1089 -0
- package/src/domain/scheduled-task-account-audit.ts +90 -0
- package/src/domain/scheduled-task-audience.ts +249 -0
- package/src/domain/scheduled-tasks.ts +1313 -374
- package/src/domain/session-connection-accounts.ts +239 -0
- package/src/domain/session-file-access.ts +47 -0
- package/src/domain/session-native-connection-replacement.ts +64 -0
- package/src/domain/session-start-timing.ts +60 -0
- package/src/domain/session-tool-policy.ts +489 -14
- package/src/domain/sessions.ts +2254 -717
- package/src/domain/site-audience.ts +130 -0
- package/src/domain/skills.ts +9 -0
- package/src/domain/slack-bot.ts +141 -4
- package/src/domain/slack-publication-secret-safety.ts +1 -1
- package/src/domain/vaults/index.ts +19 -0
- package/src/domain/vaults/mapping.ts +190 -0
- package/src/domain/vaults/provider-registry.ts +201 -0
- package/src/domain/vaults/provider-sync.ts +531 -0
- package/src/domain/vaults/provider-writes.ts +288 -0
- package/src/domain/vaults/secret-resolver.ts +464 -0
- package/src/domain/vaults/types.ts +26 -0
- package/src/domain/vaults/vault-service.ts +1316 -0
- package/src/domain/voice-input-billing.ts +330 -0
- package/src/domain/web-search-billing.ts +169 -0
- package/src/domain/workspace-credits.ts +13 -0
- package/src/editable-artifact-live/application.ts +8 -1
- package/src/editable-artifact-live/server.ts +154 -6
- package/src/editable-artifact-live/ticket.ts +86 -1
- package/src/editable-artifact-live/types.ts +10 -0
- package/src/goal-admission.ts +220 -0
- package/src/host-session-usage-limit.ts +25 -0
- package/src/index.ts +75 -11
- package/src/managed-session.ts +137 -6
- package/src/model-call.ts +79 -0
- package/src/model-catalog.ts +345 -91
- package/src/model-provider-credentials.ts +394 -0
- package/src/product-usage-metrics.ts +101 -0
- package/src/rigs/index.ts +23 -111
- package/src/sandbox/fleet.ts +253 -28
- package/src/sandbox/routing.ts +204 -43
- package/src/sandbox/runtime-settings.ts +159 -85
- package/src/session-authorization.ts +172 -23
- package/src/session-usage-limit-metrics.ts +95 -0
- package/src/session-usage-limits.ts +415 -0
- package/src/site-agent-clamp.ts +226 -0
- package/src/site-session-origin.ts +84 -5
- package/src/transcription.ts +123 -4
- package/src/turn-surface.ts +47 -0
- package/src/user-presence.ts +134 -0
- package/src/workflow-wake-contract.ts +14 -1
- package/dist/application/host-mcp-owner.d.ts +0 -6
- package/dist/application/host-mcp-resolvers.d.ts +0 -13
- package/dist/chunk-5WZYFL45.js +0 -266
- package/dist/chunk-5WZYFL45.js.map +0 -1
- package/dist/chunk-H7X52RI2.js.map +0 -1
- package/dist/chunk-OF65T3PM.js.map +0 -1
- package/dist/chunk-VCZFFEPB.js.map +0 -1
- package/dist/chunk-ZYCNLYGH.js.map +0 -1
- package/dist/domain/connection-authority.d.ts +0 -97
- package/dist/domain/connection-authority.js +0 -169
- package/dist/domain/connection-authority.js.map +0 -1
- package/dist/domain/environments.d.ts +0 -33
- package/dist/domain/host-mcp-authority-source-admission.d.ts +0 -10
- package/dist/domain/host-mcp-task-admission.d.ts +0 -18
- package/dist/domain/packs.d.ts +0 -59
- package/dist/domain/product-integration-pack.d.ts +0 -44
- package/dist/domain/product-integration-skill.gen.d.ts +0 -5
- package/dist/remote-mcp-credentials.d.ts +0 -23
- package/dist/remote-mcp-credentials.js +0 -11
- package/dist/remote-mcp-credentials.js.map +0 -1
- package/src/application/host-mcp-owner.ts +0 -41
- package/src/application/host-mcp-resolvers.ts +0 -84
- package/src/domain/connection-authority.ts +0 -329
- package/src/domain/environments.ts +0 -120
- package/src/domain/host-mcp-authority-source-admission.ts +0 -25
- package/src/domain/host-mcp-task-admission.ts +0 -121
- package/src/domain/packs.ts +0 -699
- package/src/domain/product-integration-pack.ts +0 -42
- package/src/domain/product-integration-skill.gen.ts +0 -52
- package/src/remote-mcp-credentials.ts +0 -361
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { AccessGrant, ScheduledTaskAgentConfig } from "@opengeni/contracts";
|
|
2
|
+
import { recordAuditEvent, type Database } from "@opengeni/db";
|
|
3
|
+
import { isDeepStrictEqual } from "node:util";
|
|
4
|
+
|
|
5
|
+
/** The audit action written whenever a schedule's account or connector choices change. */
|
|
6
|
+
export const SCHEDULED_TASK_ACCOUNT_CHOICES_AUDIT_ACTION = "scheduled_task.account_choices_changed";
|
|
7
|
+
|
|
8
|
+
type Choices = {
|
|
9
|
+
pinnedAccounts: { serverId: string; connectionId: string }[];
|
|
10
|
+
pinnedAccountsRequired: boolean;
|
|
11
|
+
connectors: {
|
|
12
|
+
followsWorkspaceDefaults: boolean;
|
|
13
|
+
excludedMcpServerIds: string[];
|
|
14
|
+
tools: string[];
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function choicesOf(config: ScheduledTaskAgentConfig | null): Choices {
|
|
19
|
+
return {
|
|
20
|
+
pinnedAccounts: [...(config?.pinnedAccounts ?? [])]
|
|
21
|
+
.map(({ serverId, connectionId }) => ({ serverId, connectionId }))
|
|
22
|
+
.sort((a, b) =>
|
|
23
|
+
a.serverId === b.serverId
|
|
24
|
+
? a.connectionId.localeCompare(b.connectionId)
|
|
25
|
+
: a.serverId.localeCompare(b.serverId),
|
|
26
|
+
),
|
|
27
|
+
pinnedAccountsRequired: config?.pinnedAccountsRequired === true,
|
|
28
|
+
connectors: {
|
|
29
|
+
followsWorkspaceDefaults: config?.toolPolicy !== undefined,
|
|
30
|
+
excludedMcpServerIds: [...(config?.toolPolicy?.excludedMcpServerIds ?? [])].sort(),
|
|
31
|
+
tools: (config?.tools ?? [])
|
|
32
|
+
.filter((tool) => tool.kind === "mcp")
|
|
33
|
+
.map((tool) => tool.id)
|
|
34
|
+
.sort(),
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* What changed in a schedule's account and connector choices, as audit
|
|
41
|
+
* metadata: each changed choice with its value before and after. Only the
|
|
42
|
+
* choices are recorded (server and connection ids), never a credential or an
|
|
43
|
+
* account label. Returns null when nothing changed; a new schedule without
|
|
44
|
+
* any choice records nothing.
|
|
45
|
+
*/
|
|
46
|
+
export function scheduledTaskAccountChoiceChanges(
|
|
47
|
+
before: ScheduledTaskAgentConfig | null,
|
|
48
|
+
after: ScheduledTaskAgentConfig,
|
|
49
|
+
): Record<string, { before: unknown; after: unknown }> | null {
|
|
50
|
+
const prior = choicesOf(before);
|
|
51
|
+
const next = choicesOf(after);
|
|
52
|
+
const changes: Record<string, { before: unknown; after: unknown }> = {};
|
|
53
|
+
for (const key of ["pinnedAccounts", "pinnedAccountsRequired", "connectors"] as const) {
|
|
54
|
+
if (!isDeepStrictEqual(prior[key], next[key]))
|
|
55
|
+
changes[key] = { before: prior[key], after: next[key] };
|
|
56
|
+
}
|
|
57
|
+
return Object.keys(changes).length > 0 ? changes : null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Record a change to a schedule's account or connector choices in the same
|
|
62
|
+
* transaction as the change, with who made it and through which kind of
|
|
63
|
+
* caller (a person, an agent, an API key or a service).
|
|
64
|
+
*/
|
|
65
|
+
export async function recordScheduledTaskAccountChoiceChanges(
|
|
66
|
+
tx: Database,
|
|
67
|
+
input: {
|
|
68
|
+
grant: AccessGrant;
|
|
69
|
+
taskId: string;
|
|
70
|
+
operation: "create" | "update";
|
|
71
|
+
before: ScheduledTaskAgentConfig | null;
|
|
72
|
+
after: ScheduledTaskAgentConfig;
|
|
73
|
+
},
|
|
74
|
+
): Promise<void> {
|
|
75
|
+
const changes = scheduledTaskAccountChoiceChanges(input.before, input.after);
|
|
76
|
+
if (!changes) return;
|
|
77
|
+
await recordAuditEvent(tx, {
|
|
78
|
+
accountId: input.grant.accountId,
|
|
79
|
+
workspaceId: input.grant.workspaceId,
|
|
80
|
+
subjectId: input.grant.subjectId,
|
|
81
|
+
action: SCHEDULED_TASK_ACCOUNT_CHOICES_AUDIT_ACTION,
|
|
82
|
+
targetType: "scheduled_task",
|
|
83
|
+
targetId: input.taskId,
|
|
84
|
+
metadata: {
|
|
85
|
+
operation: input.operation,
|
|
86
|
+
principalKind: input.grant.principalKind ?? null,
|
|
87
|
+
changes,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AccessGrant,
|
|
3
|
+
PrivateScheduledTaskSummary,
|
|
4
|
+
ScheduledTask,
|
|
5
|
+
SessionVisibility,
|
|
6
|
+
} from "@opengeni/contracts";
|
|
7
|
+
import {
|
|
8
|
+
getPrivateSessionCreatePolicy,
|
|
9
|
+
getScheduledTask,
|
|
10
|
+
getVisibleSessionTenancyVisibility,
|
|
11
|
+
listPrivateScheduledTaskSummaries,
|
|
12
|
+
pausePrivateScheduledTask,
|
|
13
|
+
scheduledTaskViewerSubject,
|
|
14
|
+
type Database,
|
|
15
|
+
} from "@opengeni/db";
|
|
16
|
+
import { HTTPException } from "hono/http-exception";
|
|
17
|
+
import { hasPermission } from "../access";
|
|
18
|
+
import { assertNotSiteClampedAgent } from "../site-agent-clamp";
|
|
19
|
+
import {
|
|
20
|
+
privacyDeploymentLimits,
|
|
21
|
+
resolveOrganizationPrivacy,
|
|
22
|
+
type PrivacySettings,
|
|
23
|
+
} from "../application/organization-privacy";
|
|
24
|
+
import { creationInitiatorForGrant } from "./sessions";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Who is reading schedules: the person (or the person an agent acts for),
|
|
28
|
+
* whether they administer the workspace, and whether, as an admin, they see
|
|
29
|
+
* that other people's Only me schedules exist. The organization can turn that
|
|
30
|
+
* last part off; a deployment can require it.
|
|
31
|
+
*/
|
|
32
|
+
export type ScheduledTaskViewer = {
|
|
33
|
+
subjectId: string | null;
|
|
34
|
+
admin: boolean;
|
|
35
|
+
seesPrivateSummaries: boolean;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* `full`: everything. `summary`: an admin's view of someone else's Only me
|
|
40
|
+
* schedule (owner, cadence, cost) and pause. `none`: indistinguishable from a
|
|
41
|
+
* schedule that does not exist.
|
|
42
|
+
*/
|
|
43
|
+
export type ScheduledTaskAccess = "full" | "summary" | "none";
|
|
44
|
+
|
|
45
|
+
export async function scheduledTaskViewerForGrant(
|
|
46
|
+
db: Database,
|
|
47
|
+
grant: AccessGrant,
|
|
48
|
+
settings: PrivacySettings,
|
|
49
|
+
): Promise<ScheduledTaskViewer> {
|
|
50
|
+
const { subjectId } = await scheduledTaskFullViewerForGrant(db, grant);
|
|
51
|
+
const admin = hasPermission(grant.permissions, "workspace:admin", grant.permissionMode);
|
|
52
|
+
return {
|
|
53
|
+
subjectId,
|
|
54
|
+
admin,
|
|
55
|
+
seesPrivateSummaries:
|
|
56
|
+
admin &&
|
|
57
|
+
(await resolveOrganizationPrivacy({ db, settings }, grant.accountId))
|
|
58
|
+
.adminsSeePrivateSchedules,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The viewer for a check that only asks "may they see it in full?". Summary
|
|
64
|
+
* access never matters there, so it needs no organization setting.
|
|
65
|
+
*/
|
|
66
|
+
async function scheduledTaskFullViewerForGrant(
|
|
67
|
+
db: Database,
|
|
68
|
+
grant: AccessGrant,
|
|
69
|
+
): Promise<ScheduledTaskViewer> {
|
|
70
|
+
const creator = creationInitiatorForGrant(grant);
|
|
71
|
+
const subjectId = await scheduledTaskViewerSubject(db, {
|
|
72
|
+
workspaceId: grant.workspaceId,
|
|
73
|
+
...(creator.initiator ? { createdBy: creator.initiator } : {}),
|
|
74
|
+
...(creator.context ? { createdByContext: creator.context } : {}),
|
|
75
|
+
createdByActor: creator.actor ?? null,
|
|
76
|
+
});
|
|
77
|
+
return { subjectId, admin: false, seesPrivateSummaries: false };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function scheduledTaskAccess(
|
|
81
|
+
viewer: ScheduledTaskViewer,
|
|
82
|
+
task: Pick<ScheduledTask, "visibility" | "ownerSubjectId">,
|
|
83
|
+
): ScheduledTaskAccess {
|
|
84
|
+
if (task.visibility !== "private") return "full";
|
|
85
|
+
if (viewer.subjectId !== null && task.ownerSubjectId === viewer.subjectId) return "full";
|
|
86
|
+
return viewer.seesPrivateSummaries ? "summary" : "none";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function scheduledTaskNotFound(): HTTPException {
|
|
90
|
+
return new HTTPException(404, { message: "scheduled task not found" });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** A schedule this caller may see in full; anything else is a plain 404. */
|
|
94
|
+
export async function requireVisibleScheduledTask(
|
|
95
|
+
db: Database,
|
|
96
|
+
grant: AccessGrant,
|
|
97
|
+
taskId: string,
|
|
98
|
+
viewer?: ScheduledTaskViewer,
|
|
99
|
+
): Promise<ScheduledTask> {
|
|
100
|
+
const task = await getScheduledTask(db, grant.workspaceId, taskId);
|
|
101
|
+
if (!task) throw scheduledTaskNotFound();
|
|
102
|
+
const resolved = viewer ?? (await scheduledTaskFullViewerForGrant(db, grant));
|
|
103
|
+
if (scheduledTaskAccess(resolved, task) !== "full") throw scheduledTaskNotFound();
|
|
104
|
+
return task;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
108
|
+
|
|
109
|
+
/** An admin's summary of someone else's Only me schedule, or null. */
|
|
110
|
+
export async function privateScheduledTaskSummaryForAdmin(
|
|
111
|
+
db: Database,
|
|
112
|
+
grant: AccessGrant,
|
|
113
|
+
taskId: string,
|
|
114
|
+
viewer: ScheduledTaskViewer,
|
|
115
|
+
): Promise<PrivateScheduledTaskSummary | null> {
|
|
116
|
+
if (!viewer.seesPrivateSummaries || !UUID_PATTERN.test(taskId)) return null;
|
|
117
|
+
const [summary] = await listPrivateScheduledTaskSummaries(db, {
|
|
118
|
+
workspaceId: grant.workspaceId,
|
|
119
|
+
excludeOwnerSubjectId: viewer.subjectId,
|
|
120
|
+
taskId,
|
|
121
|
+
});
|
|
122
|
+
return summary ?? null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* A schedule this caller may pause: one they see in full, or, for an admin,
|
|
127
|
+
* someone else's Only me schedule, which they only ever see as a summary. That
|
|
128
|
+
* schedule often posts into a chat the admin cannot see, so it is read through
|
|
129
|
+
* the summary and never loaded in full.
|
|
130
|
+
*/
|
|
131
|
+
export async function requirePausableScheduledTask(
|
|
132
|
+
db: Database,
|
|
133
|
+
grant: AccessGrant,
|
|
134
|
+
taskId: string,
|
|
135
|
+
viewer: ScheduledTaskViewer,
|
|
136
|
+
): Promise<
|
|
137
|
+
| { access: "full"; task: ScheduledTask }
|
|
138
|
+
| { access: "summary"; summary: PrivateScheduledTaskSummary }
|
|
139
|
+
> {
|
|
140
|
+
const resolved = viewer;
|
|
141
|
+
const task = await getScheduledTask(db, grant.workspaceId, taskId);
|
|
142
|
+
if (task && scheduledTaskAccess(resolved, task) === "full") return { access: "full", task };
|
|
143
|
+
const summary = await privateScheduledTaskSummaryForAdmin(db, grant, taskId, resolved);
|
|
144
|
+
if (!summary) throw scheduledTaskNotFound();
|
|
145
|
+
return { access: "summary", summary };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Pause someone else's Only me schedule as a workspace admin and return its
|
|
150
|
+
* refreshed summary. Only its owner can change, run or resume it.
|
|
151
|
+
*/
|
|
152
|
+
export async function pausePrivateScheduledTaskAsAdmin(
|
|
153
|
+
db: Database,
|
|
154
|
+
grant: AccessGrant,
|
|
155
|
+
taskId: string,
|
|
156
|
+
viewer: ScheduledTaskViewer,
|
|
157
|
+
): Promise<PrivateScheduledTaskSummary> {
|
|
158
|
+
await assertNotSiteClampedAgent(db, grant, "schedules");
|
|
159
|
+
if (!viewer.seesPrivateSummaries) throw scheduledTaskNotFound();
|
|
160
|
+
const paused = await pausePrivateScheduledTask(db, {
|
|
161
|
+
workspaceId: grant.workspaceId,
|
|
162
|
+
taskId,
|
|
163
|
+
excludeOwnerSubjectId: viewer.subjectId,
|
|
164
|
+
});
|
|
165
|
+
const summary = paused
|
|
166
|
+
? await privateScheduledTaskSummaryForAdmin(db, grant, taskId, viewer)
|
|
167
|
+
: null;
|
|
168
|
+
if (!summary) throw scheduledTaskNotFound();
|
|
169
|
+
return summary;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export const SCHEDULED_TASK_PRIVATE_TARGET_MESSAGE =
|
|
173
|
+
"This schedule posts into an Only me chat, so it is Only me too.";
|
|
174
|
+
export const SCHEDULED_TASK_PRIVATE_OWNER_MESSAGE = "Only a person can have an Only me schedule.";
|
|
175
|
+
export const SCHEDULED_TASK_PRIVATE_DISABLED_MESSAGE =
|
|
176
|
+
"Only me schedules are turned off for this organization.";
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Whether this person may start something new as Only me in this workspace: a
|
|
180
|
+
* chat, a schedule or a Site. In their Personal workspace, whenever the
|
|
181
|
+
* platform offers private sessions; elsewhere, when the organization allows
|
|
182
|
+
* Only me and the deployment does not turn it off.
|
|
183
|
+
*/
|
|
184
|
+
export async function mayStartOnlyMe(
|
|
185
|
+
db: Database,
|
|
186
|
+
settings: PrivacySettings,
|
|
187
|
+
workspaceId: string,
|
|
188
|
+
subjectId: string,
|
|
189
|
+
): Promise<boolean> {
|
|
190
|
+
const policy = await getPrivateSessionCreatePolicy(db, {
|
|
191
|
+
workspaceId,
|
|
192
|
+
actorSubjectId: subjectId,
|
|
193
|
+
}).catch(() => null);
|
|
194
|
+
return policy
|
|
195
|
+
? policy.personalWorkspace
|
|
196
|
+
? policy.platformAvailable
|
|
197
|
+
: policy.organizationEnabled && privacyDeploymentLimits(settings).onlyMeAllowed
|
|
198
|
+
: false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* The audience to store for a created or edited schedule. A schedule that
|
|
203
|
+
* posts into an Only me chat, or writes to its owner's personal knowledge, is
|
|
204
|
+
* always Only me. Choosing Only me needs a human owner who may create Only me
|
|
205
|
+
* chats in this workspace.
|
|
206
|
+
*/
|
|
207
|
+
export async function resolveScheduledTaskVisibility(input: {
|
|
208
|
+
db: Database;
|
|
209
|
+
settings: PrivacySettings;
|
|
210
|
+
grant: AccessGrant;
|
|
211
|
+
requested: SessionVisibility | undefined;
|
|
212
|
+
current: Pick<ScheduledTask, "visibility"> | null;
|
|
213
|
+
targetSessionId: string | null;
|
|
214
|
+
personalKnowledge: boolean;
|
|
215
|
+
ownerSubjectId: string | null;
|
|
216
|
+
}): Promise<"user_private" | "workspace_shared"> {
|
|
217
|
+
const targetPrivate =
|
|
218
|
+
input.targetSessionId !== null &&
|
|
219
|
+
(await getVisibleSessionTenancyVisibility(
|
|
220
|
+
input.db,
|
|
221
|
+
input.grant.workspaceId,
|
|
222
|
+
input.targetSessionId,
|
|
223
|
+
)) === "user_private";
|
|
224
|
+
const forced = targetPrivate || input.personalKnowledge;
|
|
225
|
+
if (forced && input.requested === "workspace")
|
|
226
|
+
throw new HTTPException(422, {
|
|
227
|
+
message: targetPrivate
|
|
228
|
+
? SCHEDULED_TASK_PRIVATE_TARGET_MESSAGE
|
|
229
|
+
: "This schedule saves to your personal knowledge, so it is Only me.",
|
|
230
|
+
});
|
|
231
|
+
const wanted = forced ? "private" : (input.requested ?? input.current?.visibility ?? "workspace");
|
|
232
|
+
if (wanted === "workspace") return "workspace_shared";
|
|
233
|
+
if (!input.ownerSubjectId)
|
|
234
|
+
throw new HTTPException(422, { message: SCHEDULED_TASK_PRIVATE_OWNER_MESSAGE });
|
|
235
|
+
// Narrowing an existing Only me schedule never needs a fresh permission; a
|
|
236
|
+
// newly private one needs the organization to allow Only me.
|
|
237
|
+
if (input.current?.visibility !== "private" && !forced) {
|
|
238
|
+
if (
|
|
239
|
+
!(await mayStartOnlyMe(
|
|
240
|
+
input.db,
|
|
241
|
+
input.settings,
|
|
242
|
+
input.grant.workspaceId,
|
|
243
|
+
input.ownerSubjectId,
|
|
244
|
+
))
|
|
245
|
+
)
|
|
246
|
+
throw new HTTPException(403, { message: SCHEDULED_TASK_PRIVATE_DISABLED_MESSAGE });
|
|
247
|
+
}
|
|
248
|
+
return "user_private";
|
|
249
|
+
}
|