thinkwork-cli 0.12.16 → 0.12.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +517 -10403
- package/dist/commands/enterprise/templates/deploy-repo/docs/runbook.md +3 -8
- package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +1 -184
- package/dist/drizzle/0050_brain_v0_entity_subtype.sql +2 -2
- package/dist/drizzle/0051_brain_v0_tenant_entity_pages.sql +6 -6
- package/dist/drizzle/0052_brain_v0_external_refs.sql +2 -2
- package/dist/drizzle/0053_brain_v0_unresolved_mentions_subtype.sql +2 -2
- package/dist/drizzle/0145_knowledge_graph_thread_ingest.sql +14 -14
- package/dist/drizzle/0146_knowledge_graph_source_scope.sql +1 -1
- package/dist/drizzle/0157_knowledge_graph_observations_source.sql +2 -2
- package/dist/drizzle/0158_wiki_tenant_scope.sql +1 -1
- package/dist/drizzle/0165_plugin_premium_entitlements.sql +2 -2
- package/dist/drizzle/{0166_company_brain_substrate_contract.sql → 0166_brain_substrate_contract.sql} +9 -9
- package/dist/drizzle/{0167_company_brain_artifact_manifest_runtime.sql → 0167_brain_artifact_manifest_runtime.sql} +5 -5
- package/dist/drizzle/0168_eval_skill_gate.sql +1 -1
- package/dist/drizzle/0183_okf_artifact_manifests.sql +1 -1
- package/dist/drizzle/0200_eval_results_execution_tier.sql +40 -0
- package/dist/drizzle/0201_brain_dream_runs.sql +72 -0
- package/dist/drizzle/0202_backfill_thread_participants_from_senders.sql +110 -0
- package/dist/drizzle/0203_threads_mode_override.sql +61 -0
- package/dist/drizzle/0204_thread_turns_triggering_message_id.sql +48 -0
- package/dist/drizzle/0205_routines_git_python.sql +185 -0
- package/dist/drizzle/0206_agent_loop_versions_routine_actions.sql +40 -0
- package/dist/drizzle/0207_tenant_credentials_github_repo_kind.sql +52 -0
- package/dist/drizzle/0208_knowledge_graph_neutral_identifiers.sql +253 -0
- package/dist/drizzle/0209_living_artifacts_schema.sql +109 -0
- package/dist/drizzle/0210_think137_automations_additive_schema.sql +113 -0
- package/dist/drizzle/0211_backfill_agent_loop_versions_target_spec.sql +120 -0
- package/dist/drizzle/0212_fold_webhooks_into_automations.sql +312 -0
- package/dist/drizzle/0213_agent_loop_versions_judge_evidence_nullable.sql +63 -0
- package/dist/drizzle/0214_think137_drop_judge_evidence_roi.sql +99 -0
- package/dist/drizzle/0215_agent_loop_versions_goal_worker_policy_nullable.sql +70 -0
- package/dist/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql +81 -0
- package/dist/drizzle/0217_agents_capability_folder_dispatch.sql +12 -0
- package/dist/drizzle/0218_document_plates.sql +30 -0
- package/dist/drizzle/0219_document_section_waivers.sql +29 -0
- package/dist/drizzle/0220_artifacts_created_by_user.sql +21 -0
- package/dist/drizzle/0221_artifact_shares.sql +29 -0
- package/dist/drizzle/0221_workflow_interpreter_thin_slice.sql +71 -0
- package/dist/drizzle/0222_compliance_drainer_select_audit_events.sql +21 -0
- package/dist/drizzle/0223_document_conformance_reports.sql +38 -0
- package/dist/drizzle/0224_artifact_refresh_state.sql +11 -0
- package/dist/drizzle/0225_think216_agent_loop_convergence.sql +21 -0
- package/dist/drizzle/0226_drop_n8n_agent_step_runs.sql +6 -0
- package/dist/drizzle/0227_analyst_reader_role.sql +1055 -0
- package/dist/drizzle/0228_tenant_credentials_rds_iam_kind.sql +62 -0
- package/dist/drizzle/0229_analyst_reader_rds_iam_grant.sql +79 -0
- package/dist/drizzle/0230_analyst_rls.sql +1613 -0
- package/dist/drizzle/0231_cost_events_cache_write_enforcement_exempt.sql +11 -0
- package/dist/plugins/catalog/package.json +0 -1
- package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +0 -1
- package/dist/plugins/catalog/src/__tests__/catalog.test.ts +16 -8
- package/dist/plugins/catalog/src/__tests__/contracts.test.ts +56 -0
- package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +0 -2
- package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -5
- package/dist/plugins/catalog/src/contracts.ts +112 -0
- package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -6
- package/dist/plugins/n8n/README.md +4 -36
- package/dist/plugins/n8n/src/deployment/managed-app.ts +0 -17
- package/dist/plugins/n8n/src/manifest.ts +0 -5
- package/dist/plugins/n8n/terraform/n8n/main.tf +0 -26
- package/dist/plugins/n8n/terraform/n8n/outputs.tf +0 -5
- package/dist/plugins/n8n/terraform/n8n/variables.tf +0 -6
- package/dist/plugins/n8n/test/manifest.test.ts +0 -1
- package/dist/plugins/twenty/package.json +5 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/load-records.test.ts +232 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/mappers.test.ts +489 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/members-ensure.test.ts +199 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/provision-members-db.test.ts +43 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/schema-ensure.test.ts +230 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/twenty-client.test.ts +207 -0
- package/dist/plugins/twenty/scripts/lib/lastmile-reader.ts +282 -0
- package/dist/plugins/twenty/scripts/lib/load-attachments.ts +220 -0
- package/dist/plugins/twenty/scripts/lib/load-records.ts +680 -0
- package/dist/plugins/twenty/scripts/lib/mappers.ts +559 -0
- package/dist/plugins/twenty/scripts/lib/members-ensure.ts +381 -0
- package/dist/plugins/twenty/scripts/lib/provision-members-db.ts +191 -0
- package/dist/plugins/twenty/scripts/lib/schema-ensure.ts +425 -0
- package/dist/plugins/twenty/scripts/lib/twenty-client.ts +246 -0
- package/dist/plugins/twenty/scripts/migrate-lastmile.ts +610 -0
- package/dist/plugins/twenty/scripts/provision-twenty-members.ts +214 -0
- package/dist/plugins/twenty/src/manifest.ts +99 -0
- package/dist/plugins/twenty/terraform/twenty/README.md +1 -1
- package/dist/plugins/twenty/test/manifest.test.ts +12 -2
- package/dist/terraform/examples/greenfield/main.tf +90 -380
- package/dist/terraform/examples/greenfield/terraform.tfvars.example +0 -1
- package/dist/terraform/modules/app/agentcore-pi/README.md +1 -3
- package/dist/terraform/modules/app/agentcore-pi/main.tf +29 -38
- package/dist/terraform/modules/app/agentcore-pi/variables.tf +23 -15
- package/dist/terraform/modules/app/agentcore-runtime/main.tf +3 -3
- package/dist/terraform/modules/app/customer-domain/main.tf +16 -2
- package/dist/terraform/modules/app/deployment-control-plane/main.tf +5 -0
- package/dist/terraform/modules/app/deployment-control-plane/runner.py +85 -207
- package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +56 -2
- package/dist/terraform/modules/app/hindsight-memory/main.tf +52 -4
- package/dist/terraform/modules/app/lambda-api/eval-fanout.tf +7 -0
- package/dist/terraform/modules/app/lambda-api/handlers.tf +493 -89
- package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +215 -96
- package/dist/terraform/modules/app/lambda-api/main.tf +24 -5
- package/dist/terraform/modules/app/lambda-api/outputs.tf +19 -11
- package/dist/terraform/modules/app/lambda-api/variables.tf +94 -67
- package/dist/terraform/modules/app/ses-email/main.tf +14 -2
- package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/main.tf +493 -0
- package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/outputs.tf +34 -0
- package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/variables.tf +32 -0
- package/dist/terraform/modules/data/aurora-postgres/main.tf +55 -0
- package/dist/terraform/modules/data/aurora-postgres/outputs.tf +15 -0
- package/dist/terraform/modules/data/s3-buckets/main.tf +26 -0
- package/dist/terraform/modules/thinkwork/README.md +0 -1
- package/dist/terraform/modules/thinkwork/main.tf +167 -446
- package/dist/terraform/modules/thinkwork/outputs.tf +2 -142
- package/dist/terraform/modules/thinkwork/variables.tf +55 -309
- package/dist/terraform/schema.graphql +4 -0
- package/dist/workspace-defaults/files/AGENTS.md +45 -31
- package/dist/workspace-defaults/files/CONTEXT.md +48 -4
- package/dist/workspace-defaults/files/GUARDRAILS.md +18 -0
- package/dist/workspace-defaults/files/MEMORY_GUIDE.md +32 -4
- package/dist/workspace-defaults/files/SPACE.md +34 -19
- package/dist/workspace-defaults/files/TOOLS.md +30 -0
- package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +154 -0
- package/dist/workspace-defaults/files/skills/document-composer/references/authoring-rules.md +101 -0
- package/package.json +1 -1
- package/dist/plugins/company-brain/README.md +0 -37
- package/dist/plugins/company-brain/package.json +0 -33
- package/dist/plugins/company-brain/runtime/cognee/Dockerfile +0 -11
- package/dist/plugins/company-brain/smoke/cognee-managed-app-smoke.mjs +0 -458
- package/dist/plugins/company-brain/smoke/cognee-memory-cutover-smoke.mjs +0 -539
- package/dist/plugins/company-brain/smoke/company-brain-context-engine-smoke.mjs +0 -312
- package/dist/plugins/company-brain/smoke/company-brain-operations-smoke.mjs +0 -449
- package/dist/plugins/company-brain/smoke/company-brain-plugin-smoke.mjs +0 -655
- package/dist/plugins/company-brain/smoke/hindsight-memory-isolation-smoke.mjs +0 -474
- package/dist/plugins/company-brain/src/api/cognee-client.ts +0 -896
- package/dist/plugins/company-brain/src/api/cognee-cluster-identity.ts +0 -49
- package/dist/plugins/company-brain/src/api/cognee-memory-scope.ts +0 -84
- package/dist/plugins/company-brain/src/api/context-engine-provider.ts +0 -888
- package/dist/plugins/company-brain/src/api/migration.ts +0 -665
- package/dist/plugins/company-brain/src/deployment/cognee-managed-app.ts +0 -396
- package/dist/plugins/company-brain/src/index.ts +0 -62
- package/dist/plugins/company-brain/src/manifest.ts +0 -112
- package/dist/plugins/company-brain/terraform/cognee/README.md +0 -263
- package/dist/plugins/company-brain/terraform/cognee/main.tf +0 -766
- package/dist/plugins/company-brain/terraform/cognee/outputs.tf +0 -144
- package/dist/plugins/company-brain/terraform/cognee/variables.tf +0 -402
- package/dist/plugins/company-brain/test/api/cognee-client.test.ts +0 -562
- package/dist/plugins/company-brain/test/api/cognee-memory-scope.test.ts +0 -74
- package/dist/plugins/company-brain/test/api/context-engine-provider.test.ts +0 -447
- package/dist/plugins/company-brain/test/api/migration.test.ts +0 -465
- package/dist/plugins/company-brain/test/manifest.test.ts +0 -95
- package/dist/plugins/company-brain/tsconfig.json +0 -8
|
@@ -1,896 +0,0 @@
|
|
|
1
|
-
export interface KnowledgeGraphOntologyExport {
|
|
2
|
-
mechanism: "cognee_owl_ontology" | "custom_prompt";
|
|
3
|
-
entityTypes: unknown[];
|
|
4
|
-
relationshipTypes: unknown[];
|
|
5
|
-
customPrompt: string;
|
|
6
|
-
ontologyKey: string | null;
|
|
7
|
-
ontologyOwlXml: string | null;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface CogneeGraphNode {
|
|
11
|
-
id: string;
|
|
12
|
-
label: string;
|
|
13
|
-
type?: string | null;
|
|
14
|
-
properties?: Record<string, unknown> | null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface CogneeGraphEdge {
|
|
18
|
-
id?: string | null;
|
|
19
|
-
source: string;
|
|
20
|
-
target: string;
|
|
21
|
-
label: string;
|
|
22
|
-
type?: string | null;
|
|
23
|
-
properties?: Record<string, unknown> | null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface CogneeGraphPayload {
|
|
27
|
-
nodes: CogneeGraphNode[];
|
|
28
|
-
edges: CogneeGraphEdge[];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface CogneeIngestResult {
|
|
32
|
-
datasetId: string | null;
|
|
33
|
-
datasetName: string;
|
|
34
|
-
mode: "remember" | "add_cognify";
|
|
35
|
-
pipelineRunId: string | null;
|
|
36
|
-
raw: unknown;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export type CogneeDatasetStatus =
|
|
40
|
-
| "completed"
|
|
41
|
-
| "errored"
|
|
42
|
-
| "running"
|
|
43
|
-
| "unknown";
|
|
44
|
-
|
|
45
|
-
export interface CogneeDatasetStatusSnapshot {
|
|
46
|
-
status: CogneeDatasetStatus;
|
|
47
|
-
rawStatus: string | null;
|
|
48
|
-
raw: unknown;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface CogneeDatasetWaitResult {
|
|
52
|
-
status: CogneeDatasetStatus;
|
|
53
|
-
rawStatus: string | null;
|
|
54
|
-
attempts: number;
|
|
55
|
-
elapsedMs: number;
|
|
56
|
-
samples: CogneeDatasetStatusSnapshot[];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export type CogneeDocumentSourceKind =
|
|
60
|
-
| "thread"
|
|
61
|
-
| "wiki"
|
|
62
|
-
| "brain"
|
|
63
|
-
| "observations"
|
|
64
|
-
| "user_memory"
|
|
65
|
-
| "space_memory";
|
|
66
|
-
|
|
67
|
-
export interface CogneeDocumentIngestArgs {
|
|
68
|
-
tenantId: string;
|
|
69
|
-
sourceKind: CogneeDocumentSourceKind;
|
|
70
|
-
sourceRef: string;
|
|
71
|
-
datasetName: string;
|
|
72
|
-
document: string;
|
|
73
|
-
filename: string;
|
|
74
|
-
ontology: KnowledgeGraphOntologyExport;
|
|
75
|
-
customPrompt?: string | null;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface CogneeSearchArgs {
|
|
79
|
-
query: string;
|
|
80
|
-
searchType?: string;
|
|
81
|
-
datasets?: string[];
|
|
82
|
-
datasetIds?: string[];
|
|
83
|
-
nodeNames?: string[];
|
|
84
|
-
nodeNameFilterOperator?: "AND" | "OR";
|
|
85
|
-
topK?: number;
|
|
86
|
-
onlyContext?: boolean;
|
|
87
|
-
includeReferences?: boolean;
|
|
88
|
-
systemPrompt?: string | null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export class CogneeClientError extends Error {
|
|
92
|
-
/** HTTP status when the error originated from a non-2xx Cognee response. */
|
|
93
|
-
readonly status?: number;
|
|
94
|
-
constructor(message: string, status?: number) {
|
|
95
|
-
super(message);
|
|
96
|
-
this.name = "CogneeClientError";
|
|
97
|
-
this.status = status;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export class CogneeClient {
|
|
102
|
-
private readonly endpoint: string;
|
|
103
|
-
private readonly token: string | null;
|
|
104
|
-
private readonly fetchFn: typeof fetch;
|
|
105
|
-
private readonly mode: "remember" | "add_cognify";
|
|
106
|
-
private readonly retryAttempts: number;
|
|
107
|
-
private readonly retryDelayMs: number;
|
|
108
|
-
private readonly indexPollMs: number;
|
|
109
|
-
private readonly indexTimeoutMs: number;
|
|
110
|
-
|
|
111
|
-
constructor(
|
|
112
|
-
opts: {
|
|
113
|
-
endpoint?: string | null;
|
|
114
|
-
token?: string | null;
|
|
115
|
-
fetchFn?: typeof fetch;
|
|
116
|
-
mode?: "remember" | "add_cognify" | null;
|
|
117
|
-
retryAttempts?: number;
|
|
118
|
-
retryDelayMs?: number;
|
|
119
|
-
indexPollMs?: number;
|
|
120
|
-
indexTimeoutMs?: number;
|
|
121
|
-
} = {},
|
|
122
|
-
) {
|
|
123
|
-
const endpoint = opts.endpoint ?? process.env.COGNEE_ENDPOINT;
|
|
124
|
-
if (!endpoint) {
|
|
125
|
-
throw new CogneeClientError("COGNEE_ENDPOINT is not configured");
|
|
126
|
-
}
|
|
127
|
-
this.endpoint = endpoint.replace(/\/+$/, "");
|
|
128
|
-
this.token = opts.token ?? process.env.COGNEE_API_KEY ?? null;
|
|
129
|
-
this.fetchFn = opts.fetchFn ?? fetch;
|
|
130
|
-
this.mode =
|
|
131
|
-
opts.mode ??
|
|
132
|
-
(process.env.COGNEE_INGEST_MODE === "add_cognify"
|
|
133
|
-
? "add_cognify"
|
|
134
|
-
: "remember");
|
|
135
|
-
this.retryAttempts =
|
|
136
|
-
opts.retryAttempts ?? readPositiveIntEnv("COGNEE_HTTP_RETRY_ATTEMPTS", 3);
|
|
137
|
-
this.retryDelayMs =
|
|
138
|
-
opts.retryDelayMs ??
|
|
139
|
-
readPositiveIntEnv("COGNEE_HTTP_RETRY_DELAY_MS", 250);
|
|
140
|
-
this.indexPollMs =
|
|
141
|
-
opts.indexPollMs ?? readPositiveIntEnv("COGNEE_INDEX_POLL_MS", 5_000);
|
|
142
|
-
this.indexTimeoutMs =
|
|
143
|
-
opts.indexTimeoutMs ??
|
|
144
|
-
readPositiveIntEnv("COGNEE_INDEX_TIMEOUT_MS", 240_000);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async ingestThread(args: {
|
|
148
|
-
tenantId: string;
|
|
149
|
-
threadId: string;
|
|
150
|
-
datasetName: string;
|
|
151
|
-
transcript: string;
|
|
152
|
-
ontology: KnowledgeGraphOntologyExport;
|
|
153
|
-
}): Promise<CogneeIngestResult> {
|
|
154
|
-
return this.ingestDocument({
|
|
155
|
-
tenantId: args.tenantId,
|
|
156
|
-
sourceKind: "thread",
|
|
157
|
-
sourceRef: args.threadId,
|
|
158
|
-
datasetName: args.datasetName,
|
|
159
|
-
document: args.transcript,
|
|
160
|
-
filename: "thinkwork-thread.md",
|
|
161
|
-
ontology: args.ontology,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async ingestDocument(
|
|
166
|
-
args: CogneeDocumentIngestArgs,
|
|
167
|
-
): Promise<CogneeIngestResult> {
|
|
168
|
-
await this.ensureOntology(args.ontology);
|
|
169
|
-
if (this.mode === "add_cognify") {
|
|
170
|
-
return this.addAndCognify(args);
|
|
171
|
-
}
|
|
172
|
-
try {
|
|
173
|
-
return await this.remember(args);
|
|
174
|
-
} catch (err) {
|
|
175
|
-
if (!isUnsupportedRememberError(err)) throw err;
|
|
176
|
-
return this.addAndCognify(args);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
async fetchDatasetGraph(datasetId: string): Promise<CogneeGraphPayload> {
|
|
181
|
-
const payload = await this.requestJson(
|
|
182
|
-
`/api/v1/datasets/${encodeURIComponent(datasetId)}/graph`,
|
|
183
|
-
{ method: "GET" },
|
|
184
|
-
{ retryTransient: true },
|
|
185
|
-
);
|
|
186
|
-
return parseGraphPayload(payload);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async search(args: CogneeSearchArgs): Promise<unknown> {
|
|
190
|
-
return this.requestJson(
|
|
191
|
-
"/api/v1/search",
|
|
192
|
-
{
|
|
193
|
-
method: "POST",
|
|
194
|
-
headers: { "content-type": "application/json" },
|
|
195
|
-
body: JSON.stringify({
|
|
196
|
-
query: args.query,
|
|
197
|
-
search_type: args.searchType ?? "GRAPH_COMPLETION",
|
|
198
|
-
...(args.datasets?.length ? { datasets: args.datasets } : {}),
|
|
199
|
-
...(args.datasetIds?.length ? { dataset_ids: args.datasetIds } : {}),
|
|
200
|
-
...(args.nodeNames?.length ? { node_name: args.nodeNames } : {}),
|
|
201
|
-
...(args.nodeNameFilterOperator
|
|
202
|
-
? { node_name_filter_operator: args.nodeNameFilterOperator }
|
|
203
|
-
: {}),
|
|
204
|
-
...(args.topK ? { top_k: args.topK } : {}),
|
|
205
|
-
...(args.onlyContext !== undefined
|
|
206
|
-
? { only_context: args.onlyContext }
|
|
207
|
-
: {}),
|
|
208
|
-
...(args.includeReferences !== undefined
|
|
209
|
-
? { include_references: args.includeReferences }
|
|
210
|
-
: {}),
|
|
211
|
-
...(args.systemPrompt ? { system_prompt: args.systemPrompt } : {}),
|
|
212
|
-
}),
|
|
213
|
-
},
|
|
214
|
-
{ retryTransient: true },
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/** List datasets currently known to Cognee (id + name pairs). */
|
|
219
|
-
async listDatasets(): Promise<Array<{ id: string; name: string }>> {
|
|
220
|
-
const payload = await this.requestJson(
|
|
221
|
-
"/api/v1/datasets",
|
|
222
|
-
{ method: "GET" },
|
|
223
|
-
{ retryTransient: true },
|
|
224
|
-
);
|
|
225
|
-
const rows = Array.isArray(payload)
|
|
226
|
-
? payload
|
|
227
|
-
: Array.isArray((payload as Record<string, unknown>)?.datasets)
|
|
228
|
-
? ((payload as Record<string, unknown>).datasets as unknown[])
|
|
229
|
-
: [];
|
|
230
|
-
return rows
|
|
231
|
-
.map((row) => row as Record<string, unknown>)
|
|
232
|
-
.filter(
|
|
233
|
-
(row) =>
|
|
234
|
-
row && (row.id ?? row.dataset_id) && (row.name ?? row.dataset_name),
|
|
235
|
-
)
|
|
236
|
-
.map((row) => ({
|
|
237
|
-
id: String(row.id ?? row.dataset_id),
|
|
238
|
-
name: String(row.name ?? row.dataset_name),
|
|
239
|
-
}));
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Delete every Cognee dataset whose name matches `datasetName`, so a
|
|
244
|
-
* fullRebuild starts from an empty graph for that source instead of
|
|
245
|
-
* appending to the accumulated one. Idempotent — a missing dataset is a
|
|
246
|
-
* no-op. Returns the number of datasets deleted.
|
|
247
|
-
*/
|
|
248
|
-
async deleteDatasetByName(datasetName: string): Promise<number> {
|
|
249
|
-
let datasets: Array<{ id: string; name: string }>;
|
|
250
|
-
try {
|
|
251
|
-
datasets = await this.listDatasets();
|
|
252
|
-
} catch {
|
|
253
|
-
return 0;
|
|
254
|
-
}
|
|
255
|
-
const matches = datasets.filter((d) => d.name === datasetName);
|
|
256
|
-
let deleted = 0;
|
|
257
|
-
for (const match of matches) {
|
|
258
|
-
await this.requestJson(
|
|
259
|
-
`/api/v1/datasets/${encodeURIComponent(match.id)}`,
|
|
260
|
-
{ method: "DELETE" },
|
|
261
|
-
{ retryTransient: true },
|
|
262
|
-
);
|
|
263
|
-
deleted += 1;
|
|
264
|
-
}
|
|
265
|
-
return deleted;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Nuclear clear of the entire Cognee store (all datasets + system graph).
|
|
270
|
-
* Only safe in single-tenant/dev contexts — callers gate this behind an
|
|
271
|
-
* explicit flag. Returns true on success.
|
|
272
|
-
*/
|
|
273
|
-
async pruneAll(): Promise<boolean> {
|
|
274
|
-
await this.requestJson(
|
|
275
|
-
"/api/v1/prune",
|
|
276
|
-
{
|
|
277
|
-
method: "POST",
|
|
278
|
-
headers: { "content-type": "application/json" },
|
|
279
|
-
body: "{}",
|
|
280
|
-
},
|
|
281
|
-
{ retryTransient: true },
|
|
282
|
-
);
|
|
283
|
-
return true;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
async waitForDatasetIndexing(
|
|
287
|
-
datasetId: string,
|
|
288
|
-
): Promise<CogneeDatasetWaitResult> {
|
|
289
|
-
const started = Date.now();
|
|
290
|
-
const samples: CogneeDatasetStatusSnapshot[] = [];
|
|
291
|
-
let attempts = 0;
|
|
292
|
-
let transientStatusErrors = 0;
|
|
293
|
-
|
|
294
|
-
for (;;) {
|
|
295
|
-
attempts += 1;
|
|
296
|
-
// The single dogfood Cognee task makes the /datasets/status endpoint
|
|
297
|
-
// briefly unavailable (502/503/504) while it writes the graph — but the
|
|
298
|
-
// cognify pipeline still completes. Treat a transient status error as
|
|
299
|
-
// "not ready yet, keep polling" within the index-timeout budget rather
|
|
300
|
-
// than failing the whole run on a flaky window.
|
|
301
|
-
let snapshot: CogneeDatasetStatusSnapshot;
|
|
302
|
-
try {
|
|
303
|
-
snapshot = await this.fetchDatasetStatus(datasetId);
|
|
304
|
-
} catch (err) {
|
|
305
|
-
const transient =
|
|
306
|
-
err instanceof CogneeClientError &&
|
|
307
|
-
(err.status === undefined || isTransientStatus(err.status));
|
|
308
|
-
if (!transient || Date.now() - started >= this.indexTimeoutMs) {
|
|
309
|
-
// Budget exhausted (or a non-transient error): before giving up,
|
|
310
|
-
// probe the graph directly — if cognify finished during a status
|
|
311
|
-
// outage, the graph has nodes and the run should proceed.
|
|
312
|
-
const probed = await this.probeGraphComplete(datasetId);
|
|
313
|
-
if (probed) {
|
|
314
|
-
return {
|
|
315
|
-
status: "completed",
|
|
316
|
-
rawStatus: "completed_via_graph_probe",
|
|
317
|
-
attempts,
|
|
318
|
-
elapsedMs: Date.now() - started,
|
|
319
|
-
samples: compactStatusSamples(samples),
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
throw err;
|
|
323
|
-
}
|
|
324
|
-
transientStatusErrors += 1;
|
|
325
|
-
await sleep(this.indexPollMs);
|
|
326
|
-
continue;
|
|
327
|
-
}
|
|
328
|
-
samples.push(snapshot);
|
|
329
|
-
|
|
330
|
-
if (snapshot.status === "completed") {
|
|
331
|
-
return {
|
|
332
|
-
status: "completed",
|
|
333
|
-
rawStatus: snapshot.rawStatus,
|
|
334
|
-
attempts,
|
|
335
|
-
elapsedMs: Date.now() - started,
|
|
336
|
-
samples: compactStatusSamples(samples),
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
if (snapshot.status === "errored") {
|
|
340
|
-
throw new CogneeClientError(
|
|
341
|
-
`Cognee dataset ${datasetId} indexing failed with status ${snapshot.rawStatus ?? "errored"}`,
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
if (Date.now() - started >= this.indexTimeoutMs) {
|
|
345
|
-
// Timed out waiting for "completed" — but a flaky status endpoint can
|
|
346
|
-
// report STARTED long after the pipeline actually finished. Probe the
|
|
347
|
-
// graph before failing.
|
|
348
|
-
const probed = await this.probeGraphComplete(datasetId);
|
|
349
|
-
if (probed) {
|
|
350
|
-
return {
|
|
351
|
-
status: "completed",
|
|
352
|
-
rawStatus: "completed_via_graph_probe",
|
|
353
|
-
attempts,
|
|
354
|
-
elapsedMs: Date.now() - started,
|
|
355
|
-
samples: compactStatusSamples(samples),
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
throw new CogneeClientError(
|
|
359
|
-
`Cognee dataset ${datasetId} indexing did not complete within ${this.indexTimeoutMs}ms; latest status ${snapshot.rawStatus ?? "unknown"} (transient status errors: ${transientStatusErrors})`,
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
await sleep(this.indexPollMs);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* Best-effort check that a dataset's graph is populated, used as a fallback
|
|
369
|
-
* when the status endpoint is flaky but the cognify pipeline may have
|
|
370
|
-
* finished. Returns true only when the graph fetch succeeds with >=1 node.
|
|
371
|
-
*/
|
|
372
|
-
private async probeGraphComplete(datasetId: string): Promise<boolean> {
|
|
373
|
-
try {
|
|
374
|
-
const graph = await this.fetchDatasetGraph(datasetId);
|
|
375
|
-
return graph.nodes.length > 0;
|
|
376
|
-
} catch {
|
|
377
|
-
return false;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
async fetchDatasetStatus(
|
|
382
|
-
datasetId: string,
|
|
383
|
-
): Promise<CogneeDatasetStatusSnapshot> {
|
|
384
|
-
const payload = await this.requestJson(
|
|
385
|
-
`/api/v1/datasets/status?dataset=${encodeURIComponent(datasetId)}`,
|
|
386
|
-
{ method: "GET" },
|
|
387
|
-
{ retryTransient: true },
|
|
388
|
-
);
|
|
389
|
-
return parseDatasetStatus(payload, datasetId);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
private async remember(args: {
|
|
393
|
-
tenantId: string;
|
|
394
|
-
sourceKind: CogneeDocumentSourceKind;
|
|
395
|
-
sourceRef: string;
|
|
396
|
-
datasetName: string;
|
|
397
|
-
document: string;
|
|
398
|
-
filename: string;
|
|
399
|
-
ontology: KnowledgeGraphOntologyExport;
|
|
400
|
-
customPrompt?: string | null;
|
|
401
|
-
}): Promise<CogneeIngestResult> {
|
|
402
|
-
const body = buildDocumentForm(args, { runInBackground: true });
|
|
403
|
-
const raw = await this.requestJson("/api/v1/remember", {
|
|
404
|
-
method: "POST",
|
|
405
|
-
body,
|
|
406
|
-
});
|
|
407
|
-
return {
|
|
408
|
-
datasetId: extractDatasetId(raw),
|
|
409
|
-
datasetName: args.datasetName,
|
|
410
|
-
mode: "remember",
|
|
411
|
-
pipelineRunId: extractPipelineRunId(raw),
|
|
412
|
-
raw,
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
private async addAndCognify(args: {
|
|
417
|
-
tenantId: string;
|
|
418
|
-
sourceKind: CogneeDocumentSourceKind;
|
|
419
|
-
sourceRef: string;
|
|
420
|
-
datasetName: string;
|
|
421
|
-
document: string;
|
|
422
|
-
filename: string;
|
|
423
|
-
ontology: KnowledgeGraphOntologyExport;
|
|
424
|
-
customPrompt?: string | null;
|
|
425
|
-
}): Promise<CogneeIngestResult> {
|
|
426
|
-
const addRaw = await this.requestJson("/api/v1/add", {
|
|
427
|
-
method: "POST",
|
|
428
|
-
body: buildDocumentForm(args, { runInBackground: false }),
|
|
429
|
-
});
|
|
430
|
-
const cognifyRaw = await this.requestJson("/api/v1/cognify", {
|
|
431
|
-
method: "POST",
|
|
432
|
-
headers: { "content-type": "application/json" },
|
|
433
|
-
body: JSON.stringify({
|
|
434
|
-
datasets: [args.datasetName],
|
|
435
|
-
run_in_background: true,
|
|
436
|
-
custom_prompt: buildCustomPrompt(args),
|
|
437
|
-
...(args.ontology.ontologyKey
|
|
438
|
-
? { ontology_key: [args.ontology.ontologyKey] }
|
|
439
|
-
: {}),
|
|
440
|
-
}),
|
|
441
|
-
});
|
|
442
|
-
return {
|
|
443
|
-
datasetId: extractDatasetId(cognifyRaw) ?? extractDatasetId(addRaw),
|
|
444
|
-
datasetName: args.datasetName,
|
|
445
|
-
mode: "add_cognify",
|
|
446
|
-
pipelineRunId: extractPipelineRunId(cognifyRaw),
|
|
447
|
-
raw: { add: addRaw, cognify: cognifyRaw },
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
private async ensureOntology(
|
|
452
|
-
ontology: KnowledgeGraphOntologyExport,
|
|
453
|
-
): Promise<void> {
|
|
454
|
-
if (!ontology.ontologyKey || !ontology.ontologyOwlXml) return;
|
|
455
|
-
const existing = await this.requestJson(
|
|
456
|
-
"/api/v1/ontologies",
|
|
457
|
-
{
|
|
458
|
-
method: "GET",
|
|
459
|
-
},
|
|
460
|
-
{ retryTransient: true },
|
|
461
|
-
);
|
|
462
|
-
if (hasOntologyKey(existing, ontology.ontologyKey)) return;
|
|
463
|
-
|
|
464
|
-
const form = new FormData();
|
|
465
|
-
form.append("ontology_key", ontology.ontologyKey);
|
|
466
|
-
form.append(
|
|
467
|
-
"ontology_file",
|
|
468
|
-
new Blob([ontology.ontologyOwlXml], { type: "application/xml" }),
|
|
469
|
-
`${ontology.ontologyKey}.owl`,
|
|
470
|
-
);
|
|
471
|
-
form.append(
|
|
472
|
-
"description",
|
|
473
|
-
"ThinkWork approved ontology export for thread graph extraction",
|
|
474
|
-
);
|
|
475
|
-
|
|
476
|
-
try {
|
|
477
|
-
await this.requestJson("/api/v1/ontologies", {
|
|
478
|
-
method: "POST",
|
|
479
|
-
body: form,
|
|
480
|
-
});
|
|
481
|
-
} catch (err) {
|
|
482
|
-
if (isDuplicateOntologyError(err, ontology.ontologyKey)) return;
|
|
483
|
-
throw err;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
private async requestJson(
|
|
488
|
-
path: string,
|
|
489
|
-
init: RequestInit,
|
|
490
|
-
opts: { retryTransient?: boolean } = {},
|
|
491
|
-
): Promise<unknown> {
|
|
492
|
-
let attempt = 0;
|
|
493
|
-
for (;;) {
|
|
494
|
-
try {
|
|
495
|
-
const response = await this.fetchFn(`${this.endpoint}${path}`, {
|
|
496
|
-
...init,
|
|
497
|
-
headers: {
|
|
498
|
-
...(this.token ? { authorization: `Bearer ${this.token}` } : {}),
|
|
499
|
-
...(init.headers ?? {}),
|
|
500
|
-
},
|
|
501
|
-
});
|
|
502
|
-
const text = await response.text();
|
|
503
|
-
const payload = text ? safeJson(text) : {};
|
|
504
|
-
if (response.ok) return payload;
|
|
505
|
-
const error = new CogneeClientError(
|
|
506
|
-
`Cognee ${path} failed with ${response.status}: ${summarizePayload(payload)}`,
|
|
507
|
-
response.status,
|
|
508
|
-
);
|
|
509
|
-
if (
|
|
510
|
-
!opts.retryTransient ||
|
|
511
|
-
!isTransientStatus(response.status) ||
|
|
512
|
-
attempt >= this.retryAttempts - 1
|
|
513
|
-
) {
|
|
514
|
-
throw error;
|
|
515
|
-
}
|
|
516
|
-
} catch (err) {
|
|
517
|
-
if (
|
|
518
|
-
!opts.retryTransient ||
|
|
519
|
-
err instanceof CogneeClientError ||
|
|
520
|
-
attempt >= this.retryAttempts - 1
|
|
521
|
-
) {
|
|
522
|
-
throw err;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
attempt += 1;
|
|
526
|
-
await sleep(this.retryDelayMs * attempt);
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
function buildDocumentForm(
|
|
532
|
-
args: {
|
|
533
|
-
tenantId: string;
|
|
534
|
-
sourceKind: CogneeDocumentSourceKind;
|
|
535
|
-
sourceRef: string;
|
|
536
|
-
datasetName: string;
|
|
537
|
-
document: string;
|
|
538
|
-
filename: string;
|
|
539
|
-
ontology: KnowledgeGraphOntologyExport;
|
|
540
|
-
customPrompt?: string | null;
|
|
541
|
-
},
|
|
542
|
-
opts: { runInBackground: boolean },
|
|
543
|
-
): FormData {
|
|
544
|
-
const form = new FormData();
|
|
545
|
-
form.append(
|
|
546
|
-
"data",
|
|
547
|
-
new Blob([args.document], { type: "text/markdown" }),
|
|
548
|
-
args.filename,
|
|
549
|
-
);
|
|
550
|
-
form.append("datasetName", args.datasetName);
|
|
551
|
-
form.append("run_in_background", opts.runInBackground ? "true" : "false");
|
|
552
|
-
for (const nodeSet of buildNodeSets(
|
|
553
|
-
args.tenantId,
|
|
554
|
-
args.sourceKind,
|
|
555
|
-
args.sourceRef,
|
|
556
|
-
)) {
|
|
557
|
-
form.append("node_set", nodeSet);
|
|
558
|
-
}
|
|
559
|
-
if (args.ontology.ontologyKey) {
|
|
560
|
-
form.append("ontology_key", args.ontology.ontologyKey);
|
|
561
|
-
}
|
|
562
|
-
form.append("custom_prompt", buildCustomPrompt(args));
|
|
563
|
-
return form;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
function buildNodeSets(
|
|
567
|
-
tenantId: string,
|
|
568
|
-
sourceKind: CogneeDocumentSourceKind,
|
|
569
|
-
sourceRef: string,
|
|
570
|
-
): string[] {
|
|
571
|
-
if (sourceKind === "user_memory" || sourceKind === "space_memory") {
|
|
572
|
-
const scopeKind = sourceKind === "user_memory" ? "user" : "space";
|
|
573
|
-
return [
|
|
574
|
-
"thinkwork_memory",
|
|
575
|
-
"thinkwork_memory_v1",
|
|
576
|
-
`thinkwork_${scopeKind}_memory`,
|
|
577
|
-
`tenant_${scopeToken(tenantId)}`,
|
|
578
|
-
`${scopeKind}_${scopeToken(sourceRef)}`,
|
|
579
|
-
];
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
return [
|
|
583
|
-
`thinkwork_${sourceKind}`,
|
|
584
|
-
`tenant_${scopeToken(tenantId)}`,
|
|
585
|
-
`${sourceKind}_${scopeToken(sourceRef)}`,
|
|
586
|
-
];
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
function buildCustomPrompt(args: {
|
|
590
|
-
sourceKind: CogneeDocumentSourceKind;
|
|
591
|
-
ontology: KnowledgeGraphOntologyExport;
|
|
592
|
-
customPrompt?: string | null;
|
|
593
|
-
}): string {
|
|
594
|
-
const sourcePrompt =
|
|
595
|
-
args.sourceKind === "thread"
|
|
596
|
-
? ""
|
|
597
|
-
: [
|
|
598
|
-
"",
|
|
599
|
-
"Source packet instructions:",
|
|
600
|
-
"- Prefer declared entity titles and ontology_type_slug fields over invented generic labels.",
|
|
601
|
-
"- Preserve source_packet, citation, page, and section identifiers in properties.",
|
|
602
|
-
"- Treat relationship_hint lines as candidate relationships only when the label matches the approved ontology.",
|
|
603
|
-
].join("\n");
|
|
604
|
-
return [args.ontology.customPrompt, args.customPrompt, sourcePrompt]
|
|
605
|
-
.filter(Boolean)
|
|
606
|
-
.join("\n\n");
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
function scopeToken(value: string): string {
|
|
610
|
-
return value
|
|
611
|
-
.trim()
|
|
612
|
-
.toLowerCase()
|
|
613
|
-
.replace(/[^a-z0-9]+/g, "_")
|
|
614
|
-
.replace(/^_+|_+$/g, "");
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
function parseGraphPayload(payload: unknown): CogneeGraphPayload {
|
|
618
|
-
const record = payload as Record<string, unknown>;
|
|
619
|
-
const nodes = Array.isArray(record.nodes) ? record.nodes : [];
|
|
620
|
-
const edges = Array.isArray(record.edges) ? record.edges : [];
|
|
621
|
-
return {
|
|
622
|
-
nodes: nodes
|
|
623
|
-
.map((node) => node as Record<string, unknown>)
|
|
624
|
-
.filter((node) => node.id && node.label)
|
|
625
|
-
.map((node) => {
|
|
626
|
-
const {
|
|
627
|
-
id: _id,
|
|
628
|
-
label: _label,
|
|
629
|
-
type: _type,
|
|
630
|
-
properties,
|
|
631
|
-
...rest
|
|
632
|
-
} = node;
|
|
633
|
-
return {
|
|
634
|
-
id: String(node.id),
|
|
635
|
-
label: String(node.label),
|
|
636
|
-
type: typeof node.type === "string" ? node.type : null,
|
|
637
|
-
properties: mergeProperties(rest, properties),
|
|
638
|
-
};
|
|
639
|
-
}),
|
|
640
|
-
edges: edges
|
|
641
|
-
.map((edge) => edge as Record<string, unknown>)
|
|
642
|
-
.filter((edge) => edge.source && edge.target && coerceEdgeLabel(edge))
|
|
643
|
-
.map((edge) => {
|
|
644
|
-
const {
|
|
645
|
-
id: _id,
|
|
646
|
-
source: _source,
|
|
647
|
-
target: _target,
|
|
648
|
-
label: _label,
|
|
649
|
-
type: _type,
|
|
650
|
-
properties,
|
|
651
|
-
...rest
|
|
652
|
-
} = edge;
|
|
653
|
-
const edgeLabel = coerceEdgeLabel(edge)!;
|
|
654
|
-
return {
|
|
655
|
-
id: edge.id ? String(edge.id) : null,
|
|
656
|
-
source: String(edge.source),
|
|
657
|
-
target: String(edge.target),
|
|
658
|
-
label: edgeLabel,
|
|
659
|
-
type:
|
|
660
|
-
typeof edge.type === "string"
|
|
661
|
-
? edge.type
|
|
662
|
-
: typeof edge.relationship_type === "string"
|
|
663
|
-
? edge.relationship_type
|
|
664
|
-
: null,
|
|
665
|
-
properties: mergeProperties(rest, properties),
|
|
666
|
-
};
|
|
667
|
-
}),
|
|
668
|
-
};
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
function coerceEdgeLabel(edge: Record<string, unknown>): string | null {
|
|
672
|
-
for (const key of ["label", "relationship_type", "type"]) {
|
|
673
|
-
if (typeof edge[key] === "string" && edge[key].trim()) {
|
|
674
|
-
return edge[key];
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
return null;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
function extractDatasetId(payload: unknown): string | null {
|
|
681
|
-
const seen = new Set<unknown>();
|
|
682
|
-
const visit = (value: unknown): string | null => {
|
|
683
|
-
if (!value || typeof value !== "object" || seen.has(value)) return null;
|
|
684
|
-
seen.add(value);
|
|
685
|
-
const record = value as Record<string, unknown>;
|
|
686
|
-
for (const key of ["dataset_id", "datasetId", "id"]) {
|
|
687
|
-
if (typeof record[key] === "string" && looksLikeUuid(record[key])) {
|
|
688
|
-
return record[key];
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
for (const nested of Object.values(record)) {
|
|
692
|
-
const found = visit(nested);
|
|
693
|
-
if (found) return found;
|
|
694
|
-
}
|
|
695
|
-
return null;
|
|
696
|
-
};
|
|
697
|
-
return visit(payload);
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
function extractPipelineRunId(payload: unknown): string | null {
|
|
701
|
-
const seen = new Set<unknown>();
|
|
702
|
-
const visit = (value: unknown): string | null => {
|
|
703
|
-
if (!value || typeof value !== "object" || seen.has(value)) return null;
|
|
704
|
-
seen.add(value);
|
|
705
|
-
const record = value as Record<string, unknown>;
|
|
706
|
-
for (const key of ["pipeline_run_id", "pipelineRunId", "run_id", "runId"]) {
|
|
707
|
-
if (typeof record[key] === "string" && looksLikeUuid(record[key])) {
|
|
708
|
-
return record[key];
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
for (const nested of Object.values(record)) {
|
|
712
|
-
const found = visit(nested);
|
|
713
|
-
if (found) return found;
|
|
714
|
-
}
|
|
715
|
-
return null;
|
|
716
|
-
};
|
|
717
|
-
return visit(payload);
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
function parseDatasetStatus(
|
|
721
|
-
payload: unknown,
|
|
722
|
-
datasetId: string,
|
|
723
|
-
): CogneeDatasetStatusSnapshot {
|
|
724
|
-
const rawStatus =
|
|
725
|
-
findDatasetStatusString(payload, datasetId) ?? findStatusString(payload);
|
|
726
|
-
return {
|
|
727
|
-
status: normalizeDatasetStatus(rawStatus),
|
|
728
|
-
rawStatus,
|
|
729
|
-
raw: payload,
|
|
730
|
-
};
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
function findDatasetStatusString(
|
|
734
|
-
payload: unknown,
|
|
735
|
-
datasetId: string,
|
|
736
|
-
): string | null {
|
|
737
|
-
if (!payload || typeof payload !== "object") return null;
|
|
738
|
-
const record = payload as Record<string, unknown>;
|
|
739
|
-
const datasetValue = record[datasetId];
|
|
740
|
-
if (typeof datasetValue === "string") return datasetValue;
|
|
741
|
-
if (datasetValue && typeof datasetValue === "object") {
|
|
742
|
-
return findStatusString(datasetValue);
|
|
743
|
-
}
|
|
744
|
-
return null;
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
function findStatusString(payload: unknown): string | null {
|
|
748
|
-
const seen = new Set<unknown>();
|
|
749
|
-
const visit = (value: unknown): string | null => {
|
|
750
|
-
if (!value || typeof value !== "object" || seen.has(value)) return null;
|
|
751
|
-
seen.add(value);
|
|
752
|
-
const record = value as Record<string, unknown>;
|
|
753
|
-
for (const key of ["status", "pipeline_status", "run_status"]) {
|
|
754
|
-
if (typeof record[key] === "string" && record[key].trim()) {
|
|
755
|
-
return record[key];
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
for (const nested of Object.values(record)) {
|
|
759
|
-
if (typeof nested === "string" && /PROCESSING|PIPELINE/i.test(nested)) {
|
|
760
|
-
return nested;
|
|
761
|
-
}
|
|
762
|
-
const found = visit(nested);
|
|
763
|
-
if (found) return found;
|
|
764
|
-
}
|
|
765
|
-
return null;
|
|
766
|
-
};
|
|
767
|
-
return typeof payload === "string" ? payload : visit(payload);
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
function normalizeDatasetStatus(status: string | null): CogneeDatasetStatus {
|
|
771
|
-
if (!status) return "unknown";
|
|
772
|
-
const normalized = status.toUpperCase();
|
|
773
|
-
if (/COMPLETED|ALREADY_COMPLETED|SUCCESS|SUCCEEDED/.test(normalized)) {
|
|
774
|
-
return "completed";
|
|
775
|
-
}
|
|
776
|
-
if (/ERRORED|ERROR|FAILED|FAILURE/.test(normalized)) return "errored";
|
|
777
|
-
if (/INITIATED|STARTED|RUNNING|PROCESSING|QUEUED|YIELD/.test(normalized)) {
|
|
778
|
-
return "running";
|
|
779
|
-
}
|
|
780
|
-
return "unknown";
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
function compactStatusSamples(
|
|
784
|
-
samples: CogneeDatasetStatusSnapshot[],
|
|
785
|
-
): CogneeDatasetStatusSnapshot[] {
|
|
786
|
-
const compacted: CogneeDatasetStatusSnapshot[] = [];
|
|
787
|
-
for (const sample of samples) {
|
|
788
|
-
const previous = compacted[compacted.length - 1];
|
|
789
|
-
if (
|
|
790
|
-
previous?.status === sample.status &&
|
|
791
|
-
previous.rawStatus === sample.rawStatus
|
|
792
|
-
) {
|
|
793
|
-
continue;
|
|
794
|
-
}
|
|
795
|
-
compacted.push(sample);
|
|
796
|
-
}
|
|
797
|
-
return compacted.slice(-8);
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
function isUnsupportedRememberError(err: unknown): boolean {
|
|
801
|
-
const message = (err as Error)?.message ?? "";
|
|
802
|
-
return /\b(404|405|501)\b/.test(message);
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
function isDuplicateOntologyError(err: unknown, ontologyKey: string): boolean {
|
|
806
|
-
const message = (err as Error)?.message ?? "";
|
|
807
|
-
return (
|
|
808
|
-
/\b400\b/.test(message) &&
|
|
809
|
-
message.includes("already exists") &&
|
|
810
|
-
message.includes(ontologyKey)
|
|
811
|
-
);
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
function hasOntologyKey(payload: unknown, ontologyKey: string): boolean {
|
|
815
|
-
return Boolean(
|
|
816
|
-
payload &&
|
|
817
|
-
typeof payload === "object" &&
|
|
818
|
-
Object.prototype.hasOwnProperty.call(payload, ontologyKey),
|
|
819
|
-
);
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
function looksLikeUuid(value: unknown): value is string {
|
|
823
|
-
return (
|
|
824
|
-
typeof value === "string" &&
|
|
825
|
-
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
|
|
826
|
-
value,
|
|
827
|
-
)
|
|
828
|
-
);
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
function asRecord(value: unknown): Record<string, unknown> {
|
|
832
|
-
return value && typeof value === "object" && !Array.isArray(value)
|
|
833
|
-
? (value as Record<string, unknown>)
|
|
834
|
-
: {};
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
function mergeProperties(
|
|
838
|
-
topLevel: Record<string, unknown>,
|
|
839
|
-
properties: unknown,
|
|
840
|
-
): Record<string, unknown> {
|
|
841
|
-
return { ...topLevel, ...asRecord(properties) };
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
function safeJson(text: string): unknown {
|
|
845
|
-
try {
|
|
846
|
-
return JSON.parse(text);
|
|
847
|
-
} catch {
|
|
848
|
-
return { text };
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
function summarizePayload(payload: unknown): string {
|
|
853
|
-
const rawText =
|
|
854
|
-
typeof payload === "string"
|
|
855
|
-
? payload
|
|
856
|
-
: payload &&
|
|
857
|
-
typeof payload === "object" &&
|
|
858
|
-
typeof (payload as Record<string, unknown>).text === "string"
|
|
859
|
-
? String((payload as Record<string, unknown>).text)
|
|
860
|
-
: null;
|
|
861
|
-
if (rawText) {
|
|
862
|
-
const htmlSummary = summarizeHtml(rawText);
|
|
863
|
-
return (htmlSummary ?? rawText).slice(0, 500);
|
|
864
|
-
}
|
|
865
|
-
return JSON.stringify(payload ?? {}).slice(0, 500);
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
function summarizeHtml(text: string): string | null {
|
|
869
|
-
if (!/<html[\s>]/i.test(text) && !/<body[\s>]/i.test(text)) return null;
|
|
870
|
-
const title =
|
|
871
|
-
text.match(/<title[^>]*>(.*?)<\/title>/is)?.[1] ??
|
|
872
|
-
text.match(/<h1[^>]*>(.*?)<\/h1>/is)?.[1] ??
|
|
873
|
-
null;
|
|
874
|
-
return title ? stripHtml(title).trim() : "HTML error response";
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
function stripHtml(value: string): string {
|
|
878
|
-
return value.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ");
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
function isTransientStatus(status: number): boolean {
|
|
882
|
-
return [502, 503, 504].includes(status);
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
function sleep(ms: number): Promise<void> {
|
|
886
|
-
return ms > 0
|
|
887
|
-
? new Promise((resolve) => setTimeout(resolve, ms))
|
|
888
|
-
: Promise.resolve();
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
function readPositiveIntEnv(name: string, fallback: number): number {
|
|
892
|
-
const raw = process.env[name];
|
|
893
|
-
if (!raw) return fallback;
|
|
894
|
-
const parsed = Number.parseInt(raw, 10);
|
|
895
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
896
|
-
}
|