@opengeni/core 0.17.1 → 0.18.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.
@@ -0,0 +1,107 @@
1
+ import { type KnowledgeMemoryKind, type KnowledgeMemoryStatus } from "@opengeni/contracts";
2
+ export declare const MEMORY_SLACK_PROJECTION_VERSION = 1;
3
+ export declare const MEMORY_SLACK_SUMMARY_MAX_UTF8_BYTES = 512;
4
+ export declare const MEMORY_SLACK_NAMESPACE_MAX_UTF8_BYTES = 128;
5
+ export declare const MEMORY_SLACK_LABEL_MAX_UTF8_BYTES = 64;
6
+ export declare const MEMORY_SLACK_LABEL_MAX_COUNT = 8;
7
+ export declare const MEMORY_SLACK_OWNER_MAX_UTF8_BYTES = 96;
8
+ export declare const MEMORY_SLACK_PROJECTION_MAX_UTF8_BYTES = 4096;
9
+ export type MemorySlackImportance = "major" | "normal" | "minor";
10
+ export type MemorySlackRequestedMode = "auto" | "review" | "never";
11
+ export type MemorySlackDeliveryMode = Exclude<MemorySlackRequestedMode, "never">;
12
+ export type MemorySlackChangeKind = "created" | "corrected" | "superseded";
13
+ export type MemorySlackOrigin = "native" | "slack_derived";
14
+ export type MemorySlackAudience = "workspace" | "restricted";
15
+ export type MemorySlackPublicationPolicy = {
16
+ enabled: boolean;
17
+ autoImportances: readonly MemorySlackImportance[];
18
+ reviewImportances: readonly MemorySlackImportance[];
19
+ };
20
+ /**
21
+ * Nothing publishes without an explicit workspace-admin enablement. When
22
+ * enabled without tuning, major changes may publish automatically, normal
23
+ * changes require review, and minor changes stay quiet.
24
+ */
25
+ export declare const DEFAULT_MEMORY_SLACK_PUBLICATION_POLICY: {
26
+ readonly enabled: false;
27
+ readonly autoImportances: readonly ["major"];
28
+ readonly reviewImportances: readonly ["normal"];
29
+ };
30
+ export type MemorySlackPublicationInput = {
31
+ context: {
32
+ accountId: string;
33
+ workspaceId: string;
34
+ now: string;
35
+ };
36
+ policy?: MemorySlackPublicationPolicy | undefined;
37
+ memory: {
38
+ sourceType: "workspace_memory";
39
+ accountId: string;
40
+ workspaceId: string;
41
+ id: string;
42
+ version: number;
43
+ status: KnowledgeMemoryStatus;
44
+ kind: KnowledgeMemoryKind;
45
+ scopeType: "workspace" | "user" | "role" | "session" | "ephemeral" | "legacy";
46
+ namespace: string;
47
+ labels: readonly string[];
48
+ validFrom: string;
49
+ validUntil: string | null;
50
+ supersedesId: string | null;
51
+ supersededById: string | null;
52
+ };
53
+ change: {
54
+ kind: MemorySlackChangeKind;
55
+ relatedMemoryId: string | null;
56
+ occurredAt: string;
57
+ origin: MemorySlackOrigin;
58
+ ownerLabel?: string | null | undefined;
59
+ };
60
+ distribution: {
61
+ importance: MemorySlackImportance;
62
+ audience: MemorySlackAudience;
63
+ slackMode: MemorySlackRequestedMode;
64
+ shareSummary: string;
65
+ };
66
+ };
67
+ export type MemorySlackPublicationDenialReason = "disabled" | "invalid_input" | "unsupported_source" | "tenant_mismatch" | "not_decision" | "status_ineligible" | "restricted_scope" | "restricted_audience" | "not_yet_valid" | "expired" | "slack_origin_loop" | "mode_never" | "below_noise_policy" | "missing_summary" | "invalid_change_lineage";
68
+ export type MemorySlackPublicationProjection = {
69
+ version: typeof MEMORY_SLACK_PROJECTION_VERSION;
70
+ workspaceId: string;
71
+ memoryId: string;
72
+ memoryVersion: number;
73
+ changeKind: MemorySlackChangeKind;
74
+ relatedMemoryId: string | null;
75
+ occurredAt: string;
76
+ importance: MemorySlackImportance;
77
+ deliveryMode: MemorySlackDeliveryMode;
78
+ summary: string;
79
+ summaryRedacted: boolean;
80
+ summaryTruncated: boolean;
81
+ namespace: string;
82
+ labels: string[];
83
+ labelsTruncated: boolean;
84
+ ownerLabel: string | null;
85
+ ownerLabelRedacted: boolean;
86
+ ownerLabelTruncated: boolean;
87
+ authoritativeRecord: {
88
+ workspaceId: string;
89
+ memoryId: string;
90
+ };
91
+ };
92
+ export type MemorySlackPublicationDecision = {
93
+ eligible: false;
94
+ reason: MemorySlackPublicationDenialReason;
95
+ } | {
96
+ eligible: true;
97
+ deliveryMode: MemorySlackDeliveryMode;
98
+ idempotencyKey: string;
99
+ projection: MemorySlackPublicationProjection;
100
+ };
101
+ /**
102
+ * Produce the only data shape a later delivery slice may persist or
103
+ * render. This is a deterministic policy/security boundary, not a persistence
104
+ * hook: callers must durably bind the returned projection and key in a later
105
+ * transaction/outbox slice.
106
+ */
107
+ export declare function evaluateMemorySlackPublication(input: MemorySlackPublicationInput): MemorySlackPublicationDecision;
package/dist/index.d.ts CHANGED
@@ -18,6 +18,7 @@ export * from "./domain/session-tool-policy";
18
18
  export * from "./domain/scheduled-tasks";
19
19
  export * from "./domain/sessions";
20
20
  export * from "./domain/insights";
21
+ export * from "./domain/memory-slack-publication";
21
22
  export * from "./domain/slack-bot";
22
23
  export * from "./domain/workspace-members";
23
24
  export * from "./application/new-session-drafts";
package/dist/index.js CHANGED
@@ -2356,6 +2356,19 @@ async function requireCatalogItem(db, workspaceId, settings, capabilityId) {
2356
2356
  return item;
2357
2357
  }
2358
2358
  function packCatalogItem(pack, source) {
2359
+ const customMetadata = { ...pack.metadata };
2360
+ for (const key of [
2361
+ "packId",
2362
+ "version",
2363
+ "connectors",
2364
+ "knowledge",
2365
+ "scheduledTaskTemplates",
2366
+ "sandboxImage",
2367
+ "sandboxProviderImages",
2368
+ "skills"
2369
+ ]) {
2370
+ delete customMetadata[key];
2371
+ }
2359
2372
  return CapabilityCatalogItem.parse({
2360
2373
  id: `pack:${pack.id}`,
2361
2374
  kind: "pack",
@@ -2370,6 +2383,7 @@ function packCatalogItem(pack, source) {
2370
2383
  notes: "Enables role-scoped tools, connectors, knowledge, and scheduled-task templates."
2371
2384
  },
2372
2385
  metadata: {
2386
+ ...customMetadata,
2373
2387
  packId: pack.id,
2374
2388
  version: pack.version,
2375
2389
  connectors: pack.connectors,
@@ -2377,8 +2391,8 @@ function packCatalogItem(pack, source) {
2377
2391
  scheduledTaskTemplates: pack.scheduledTaskTemplates,
2378
2392
  // Runtime composition surface only: skill names, never file content.
2379
2393
  ...pack.sandboxImage ? { sandboxImage: pack.sandboxImage } : {},
2380
- ...pack.skills.length > 0 ? { skills: pack.skills.map((skill) => skill.name) } : {},
2381
- ...pack.metadata
2394
+ ...pack.sandboxProviderImages ? { sandboxProviderImages: pack.sandboxProviderImages } : {},
2395
+ ...pack.skills.length > 0 ? { skills: pack.skills.map((skill) => skill.name) } : {}
2382
2396
  }
2383
2397
  });
2384
2398
  }
@@ -5965,6 +5979,236 @@ async function getWorkspaceInsights(db, settings, input) {
5965
5979
  return { snapshot };
5966
5980
  }
5967
5981
 
5982
+ // src/domain/memory-slack-publication.ts
5983
+ import { createHash } from "crypto";
5984
+ import {
5985
+ redactSensitiveText,
5986
+ stableJson as stableJson3
5987
+ } from "@opengeni/contracts";
5988
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
5989
+ var SELECTOR_SEGMENT_PATTERN = /^[a-z0-9](?:[a-z0-9._-]{0,62}[a-z0-9])?$/;
5990
+ var encoder = new TextEncoder();
5991
+ var decoder = new TextDecoder();
5992
+ var MEMORY_SLACK_PROJECTION_VERSION = 1;
5993
+ var MEMORY_SLACK_SUMMARY_MAX_UTF8_BYTES = 512;
5994
+ var MEMORY_SLACK_NAMESPACE_MAX_UTF8_BYTES = 128;
5995
+ var MEMORY_SLACK_LABEL_MAX_UTF8_BYTES = 64;
5996
+ var MEMORY_SLACK_LABEL_MAX_COUNT = 8;
5997
+ var MEMORY_SLACK_OWNER_MAX_UTF8_BYTES = 96;
5998
+ var MEMORY_SLACK_PROJECTION_MAX_UTF8_BYTES = 4096;
5999
+ var DEFAULT_MEMORY_SLACK_PUBLICATION_POLICY = {
6000
+ enabled: false,
6001
+ autoImportances: ["major"],
6002
+ reviewImportances: ["normal"]
6003
+ };
6004
+ var IMPORTANCES = /* @__PURE__ */ new Set(["major", "normal", "minor"]);
6005
+ var REQUESTED_MODES = /* @__PURE__ */ new Set(["auto", "review", "never"]);
6006
+ var CHANGE_KINDS = /* @__PURE__ */ new Set(["created", "corrected", "superseded"]);
6007
+ var ORIGINS = /* @__PURE__ */ new Set(["native", "slack_derived"]);
6008
+ var AUDIENCES = /* @__PURE__ */ new Set(["workspace", "restricted"]);
6009
+ var SCOPE_TYPES = /* @__PURE__ */ new Set([
6010
+ "workspace",
6011
+ "user",
6012
+ "role",
6013
+ "session",
6014
+ "ephemeral",
6015
+ "legacy"
6016
+ ]);
6017
+ var MEMORY_STATUSES = /* @__PURE__ */ new Set([
6018
+ "proposed",
6019
+ "approved",
6020
+ "rejected",
6021
+ "active",
6022
+ "superseded",
6023
+ "archived"
6024
+ ]);
6025
+ var MEMORY_KINDS = /* @__PURE__ */ new Set([
6026
+ "semantic",
6027
+ "episodic",
6028
+ "procedural",
6029
+ "decision",
6030
+ "preference"
6031
+ ]);
6032
+ var VISIBLE_STATUSES = /* @__PURE__ */ new Set(["active", "approved"]);
6033
+ function evaluateMemorySlackPublication(input) {
6034
+ const policy = input.policy ?? DEFAULT_MEMORY_SLACK_PUBLICATION_POLICY;
6035
+ if (!validPolicy(policy)) return denied("invalid_input");
6036
+ if (!policy.enabled) return denied("disabled");
6037
+ if (input.memory.sourceType !== "workspace_memory") return denied("unsupported_source");
6038
+ if (!validIdentity(input)) return denied("invalid_input");
6039
+ if (input.memory.accountId !== input.context.accountId || input.memory.workspaceId !== input.context.workspaceId) {
6040
+ return denied("tenant_mismatch");
6041
+ }
6042
+ if (input.memory.kind !== "decision") return denied("not_decision");
6043
+ if (input.memory.scopeType !== "workspace") return denied("restricted_scope");
6044
+ if (input.distribution.audience !== "workspace") return denied("restricted_audience");
6045
+ if (input.change.origin === "slack_derived") return denied("slack_origin_loop");
6046
+ if (input.distribution.slackMode === "never") return denied("mode_never");
6047
+ const timeDecision = evaluateValidity(input);
6048
+ if (timeDecision) return denied(timeDecision);
6049
+ if (!statusEligibleForChange(input)) return denied("status_ineligible");
6050
+ if (!validChangeLineage(input)) return denied("invalid_change_lineage");
6051
+ const deliveryMode = effectiveDeliveryMode(policy, input.distribution);
6052
+ if (!deliveryMode) return denied("below_noise_policy");
6053
+ const collapsedSummary = collapseText(input.distribution.shareSummary);
6054
+ const redactedSummary = redactSensitiveText(collapsedSummary);
6055
+ if (!redactedSummary) return denied("missing_summary");
6056
+ const summary = truncateUtf8(redactedSummary, MEMORY_SLACK_SUMMARY_MAX_UTF8_BYTES);
6057
+ const namespace = normalizeNamespace(input.memory.namespace);
6058
+ const labels = normalizeLabels(input.memory.labels);
6059
+ if (!namespace || !labels) return denied("invalid_input");
6060
+ const owner = boundedOptionalText(input.change.ownerLabel, MEMORY_SLACK_OWNER_MAX_UTF8_BYTES);
6061
+ const projection = {
6062
+ version: MEMORY_SLACK_PROJECTION_VERSION,
6063
+ workspaceId: input.context.workspaceId,
6064
+ memoryId: input.memory.id,
6065
+ memoryVersion: input.memory.version,
6066
+ changeKind: input.change.kind,
6067
+ relatedMemoryId: input.change.relatedMemoryId,
6068
+ occurredAt: new Date(input.change.occurredAt).toISOString(),
6069
+ importance: input.distribution.importance,
6070
+ deliveryMode,
6071
+ summary: summary.value,
6072
+ summaryRedacted: redactedSummary !== collapsedSummary,
6073
+ summaryTruncated: summary.truncated,
6074
+ namespace,
6075
+ labels: labels.values,
6076
+ labelsTruncated: labels.truncated,
6077
+ ownerLabel: owner.value,
6078
+ ownerLabelRedacted: owner.redacted,
6079
+ ownerLabelTruncated: owner.truncated,
6080
+ authoritativeRecord: {
6081
+ workspaceId: input.context.workspaceId,
6082
+ memoryId: input.memory.id
6083
+ }
6084
+ };
6085
+ if (utf8Bytes(stableJson3(projection)) > MEMORY_SLACK_PROJECTION_MAX_UTF8_BYTES) {
6086
+ return denied("invalid_input");
6087
+ }
6088
+ const digest = createHash("sha256").update(stableJson3(projection), "utf8").digest("hex");
6089
+ return {
6090
+ eligible: true,
6091
+ deliveryMode,
6092
+ idempotencyKey: `memory-slack:v${MEMORY_SLACK_PROJECTION_VERSION}:${digest}`,
6093
+ projection
6094
+ };
6095
+ }
6096
+ function validPolicy(policy) {
6097
+ return typeof policy.enabled === "boolean" && Array.isArray(policy.autoImportances) && Array.isArray(policy.reviewImportances) && policy.autoImportances.every((importance) => IMPORTANCES.has(importance)) && policy.reviewImportances.every((importance) => IMPORTANCES.has(importance));
6098
+ }
6099
+ function validIdentity(input) {
6100
+ const uuids = [
6101
+ input.context.accountId,
6102
+ input.context.workspaceId,
6103
+ input.memory.accountId,
6104
+ input.memory.workspaceId,
6105
+ input.memory.id,
6106
+ input.memory.supersedesId,
6107
+ input.memory.supersededById,
6108
+ input.change.relatedMemoryId
6109
+ ].filter((value) => value !== null);
6110
+ return uuids.every((value) => UUID_PATTERN.test(value)) && Number.isSafeInteger(input.memory.version) && input.memory.version > 0 && typeof input.memory.namespace === "string" && Array.isArray(input.memory.labels) && input.memory.labels.every((label) => typeof label === "string") && (input.change.ownerLabel === void 0 || input.change.ownerLabel === null || typeof input.change.ownerLabel === "string") && typeof input.distribution.shareSummary === "string" && IMPORTANCES.has(input.distribution.importance) && REQUESTED_MODES.has(input.distribution.slackMode) && CHANGE_KINDS.has(input.change.kind) && ORIGINS.has(input.change.origin) && AUDIENCES.has(input.distribution.audience) && SCOPE_TYPES.has(input.memory.scopeType) && MEMORY_STATUSES.has(input.memory.status) && MEMORY_KINDS.has(input.memory.kind);
6111
+ }
6112
+ function evaluateValidity(input) {
6113
+ const now = Date.parse(input.context.now);
6114
+ const occurredAt = Date.parse(input.change.occurredAt);
6115
+ const validFrom = Date.parse(input.memory.validFrom);
6116
+ const validUntil = input.memory.validUntil === null ? null : Date.parse(input.memory.validUntil);
6117
+ if (!Number.isFinite(now) || !Number.isFinite(occurredAt) || !Number.isFinite(validFrom) || validUntil !== null && !Number.isFinite(validUntil) || validUntil !== null && validUntil <= validFrom) {
6118
+ return "invalid_input";
6119
+ }
6120
+ if (now < validFrom) return "not_yet_valid";
6121
+ if (validUntil !== null && now >= validUntil) return "expired";
6122
+ return null;
6123
+ }
6124
+ function validChangeLineage(input) {
6125
+ if (input.memory.supersedesId === input.memory.id || input.memory.supersededById === input.memory.id || input.change.relatedMemoryId === input.memory.id) {
6126
+ return false;
6127
+ }
6128
+ switch (input.change.kind) {
6129
+ case "created":
6130
+ return input.change.relatedMemoryId === null && input.memory.supersedesId === null && input.memory.supersededById === null;
6131
+ case "corrected":
6132
+ return input.change.relatedMemoryId !== null && input.change.relatedMemoryId !== input.memory.id && input.memory.supersedesId === input.change.relatedMemoryId && input.memory.supersededById === null;
6133
+ case "superseded":
6134
+ return input.change.relatedMemoryId !== null && input.change.relatedMemoryId !== input.memory.id && input.memory.supersededById === input.change.relatedMemoryId;
6135
+ }
6136
+ }
6137
+ function statusEligibleForChange(input) {
6138
+ return input.change.kind === "superseded" ? input.memory.status === "superseded" : VISIBLE_STATUSES.has(input.memory.status);
6139
+ }
6140
+ function effectiveDeliveryMode(policy, distribution) {
6141
+ const auto = new Set(policy.autoImportances);
6142
+ const review = new Set(policy.reviewImportances);
6143
+ if (distribution.slackMode === "review") {
6144
+ return auto.has(distribution.importance) || review.has(distribution.importance) ? "review" : null;
6145
+ }
6146
+ if (auto.has(distribution.importance)) return "auto";
6147
+ if (review.has(distribution.importance)) return "review";
6148
+ return null;
6149
+ }
6150
+ function normalizeNamespace(value) {
6151
+ const trimmed = value.trim();
6152
+ if (redactSensitiveText(trimmed) !== trimmed) return null;
6153
+ const namespace = trimmed.toLowerCase();
6154
+ if (!namespace || utf8Bytes(namespace) > MEMORY_SLACK_NAMESPACE_MAX_UTF8_BYTES) return null;
6155
+ if (redactSensitiveText(namespace) !== namespace) return null;
6156
+ const segments = namespace.split("/");
6157
+ if (segments.some((segment) => !SELECTOR_SEGMENT_PATTERN.test(segment))) return null;
6158
+ return segments.join("/");
6159
+ }
6160
+ function normalizeLabels(values) {
6161
+ if (!Array.isArray(values)) return null;
6162
+ const labels = /* @__PURE__ */ new Set();
6163
+ for (const value of values) {
6164
+ if (typeof value !== "string") return null;
6165
+ const trimmed = value.trim();
6166
+ if (redactSensitiveText(trimmed) !== trimmed) return null;
6167
+ const label = trimmed.toLowerCase();
6168
+ if (redactSensitiveText(label) !== label || !SELECTOR_SEGMENT_PATTERN.test(label) || utf8Bytes(label) > MEMORY_SLACK_LABEL_MAX_UTF8_BYTES) {
6169
+ return null;
6170
+ }
6171
+ labels.add(label);
6172
+ }
6173
+ const sorted = [...labels].sort();
6174
+ return {
6175
+ values: sorted.slice(0, MEMORY_SLACK_LABEL_MAX_COUNT),
6176
+ truncated: sorted.length > MEMORY_SLACK_LABEL_MAX_COUNT
6177
+ };
6178
+ }
6179
+ function boundedOptionalText(value, maxBytes) {
6180
+ const collapsed = collapseText(value ?? "");
6181
+ if (!collapsed) return { value: null, redacted: false, truncated: false };
6182
+ const redacted = redactSensitiveText(collapsed);
6183
+ const bounded = truncateUtf8(redacted, maxBytes);
6184
+ return {
6185
+ value: bounded.value || null,
6186
+ redacted: redacted !== collapsed,
6187
+ truncated: bounded.truncated
6188
+ };
6189
+ }
6190
+ function collapseText(value) {
6191
+ return value.replace(/[\u0000-\u001F\u007F-\u009F]/g, " ").replace(/\s+/g, " ").trim();
6192
+ }
6193
+ function truncateUtf8(value, maxBytes) {
6194
+ const bytes = encoder.encode(value);
6195
+ if (bytes.byteLength <= maxBytes) return { value, truncated: false };
6196
+ const marker = "\u2026";
6197
+ const markerBytes = encoder.encode(marker);
6198
+ let end = Math.max(0, maxBytes - markerBytes.byteLength);
6199
+ while (end > 0 && (bytes[end] & 192) === 128) end -= 1;
6200
+ return {
6201
+ value: `${decoder.decode(bytes.subarray(0, end)).trimEnd()}${marker}`,
6202
+ truncated: true
6203
+ };
6204
+ }
6205
+ function utf8Bytes(value) {
6206
+ return encoder.encode(value).byteLength;
6207
+ }
6208
+ function denied(reason) {
6209
+ return { eligible: false, reason };
6210
+ }
6211
+
5968
6212
  // src/domain/workspace-members.ts
5969
6213
  import { HTTPException as HTTPException12 } from "hono/http-exception";
5970
6214
  var MEMBER_ADMIN_PERMISSIONS = ["workspace:admin", "members:manage"];
@@ -6678,6 +6922,7 @@ async function saveHumanComposerDraft(deps, context, input) {
6678
6922
  export {
6679
6923
  CODEX_COMPACTION_V2_PROVIDER_LOCKED,
6680
6924
  CodexCompactionV2ProviderLockedError,
6925
+ DEFAULT_MEMORY_SLACK_PUBLICATION_POLICY,
6681
6926
  MARKETING_SOCIAL_PACK_ID,
6682
6927
  MAX_CHECKS_PER_RIG,
6683
6928
  MAX_CREDENTIAL_HOOKS_PER_RIG,
@@ -6685,6 +6930,13 @@ export {
6685
6930
  MAX_ENVIRONMENTS_PER_WORKSPACE,
6686
6931
  MAX_RIGS_PER_WORKSPACE,
6687
6932
  MAX_VARIABLES_PER_ENVIRONMENT,
6933
+ MEMORY_SLACK_LABEL_MAX_COUNT,
6934
+ MEMORY_SLACK_LABEL_MAX_UTF8_BYTES,
6935
+ MEMORY_SLACK_NAMESPACE_MAX_UTF8_BYTES,
6936
+ MEMORY_SLACK_OWNER_MAX_UTF8_BYTES,
6937
+ MEMORY_SLACK_PROJECTION_MAX_UTF8_BYTES,
6938
+ MEMORY_SLACK_PROJECTION_VERSION,
6939
+ MEMORY_SLACK_SUMMARY_MAX_UTF8_BYTES,
6688
6940
  SESSION_AUTHORIZATION_DEFAULT_REAUTHORIZE_MS,
6689
6941
  SESSION_WORKFLOW_WAKE_DISPATCHER_PERIOD_MS,
6690
6942
  SESSION_WORKFLOW_WAKE_DISPATCHER_SCHEDULE_ID,
@@ -6734,6 +6986,7 @@ export {
6734
6986
  editHumanQueuePrompt,
6735
6987
  enableCapability,
6736
6988
  enabledCapabilityMcpToolRefs,
6989
+ evaluateMemorySlackPublication,
6737
6990
  executeRunOnSelfhostedMachine,
6738
6991
  filenameForMimeType,
6739
6992
  freezePersonalConnectionDelegations,