@mastra/factory 0.8.0-alpha.8 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +660 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +42 -4
- package/dist/factory.js.map +1 -1
- package/dist/integrations/base.d.ts +8 -0
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/app-identity.d.ts +43 -0
- package/dist/integrations/github/app-identity.d.ts.map +1 -0
- package/dist/integrations/github/app-identity.js +62 -0
- package/dist/integrations/github/app-identity.js.map +1 -0
- package/dist/integrations/github/integration.d.ts +9 -0
- package/dist/integrations/github/integration.d.ts.map +1 -1
- package/dist/integrations/github/integration.js +9 -1
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/reconcile-worker.d.ts +5 -0
- package/dist/integrations/github/reconcile-worker.d.ts.map +1 -1
- package/dist/integrations/github/reconcile-worker.js +7 -0
- package/dist/integrations/github/reconcile-worker.js.map +1 -1
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +11 -6
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/rules.d.ts +8 -0
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +30 -8
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +39 -6
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +48 -14
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/github/webhook.d.ts +39 -0
- package/dist/integrations/github/webhook.d.ts.map +1 -1
- package/dist/integrations/github/webhook.js +74 -13
- package/dist/integrations/github/webhook.js.map +1 -1
- package/dist/integrations/linear/integration.d.ts.map +1 -1
- package/dist/integrations/linear/integration.js +1 -0
- package/dist/integrations/linear/integration.js.map +1 -1
- package/dist/integrations/linear/routes.d.ts +10 -0
- package/dist/integrations/linear/routes.d.ts.map +1 -1
- package/dist/integrations/linear/routes.js +35 -1
- package/dist/integrations/linear/routes.js.map +1 -1
- package/dist/integrations/linear/rules.d.ts.map +1 -1
- package/dist/integrations/linear/rules.js +2 -1
- package/dist/integrations/linear/rules.js.map +1 -1
- package/dist/integrations/platform/github/event-worker.d.ts +11 -0
- package/dist/integrations/platform/github/event-worker.d.ts.map +1 -1
- package/dist/integrations/platform/github/event-worker.js +64 -16
- package/dist/integrations/platform/github/event-worker.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts +12 -0
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +47 -4
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
- package/dist/integrations/platform/linear/integration.js +1 -0
- package/dist/integrations/platform/linear/integration.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +2 -1
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/routes/config.d.ts +12 -6
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +97 -56
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/fs.d.ts.map +1 -1
- package/dist/routes/fs.js +2 -0
- package/dist/routes/fs.js.map +1 -1
- package/dist/routes/intake.d.ts +15 -0
- package/dist/routes/intake.d.ts.map +1 -1
- package/dist/routes/intake.js +65 -2
- package/dist/routes/intake.js.map +1 -1
- package/dist/routes/knowledge.d.ts +152 -0
- package/dist/routes/knowledge.d.ts.map +1 -0
- package/dist/routes/knowledge.js +442 -0
- package/dist/routes/knowledge.js.map +1 -0
- package/dist/routes/projects.d.ts +9 -1
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +8 -0
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/surface.d.ts +6 -0
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +10 -0
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +3 -1
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/binding-context.d.ts.map +1 -1
- package/dist/rules/binding-context.js +4 -1
- package/dist/rules/binding-context.js.map +1 -1
- package/dist/rules/defaults.d.ts.map +1 -1
- package/dist/rules/defaults.js +87 -4
- package/dist/rules/defaults.js.map +1 -1
- package/dist/rules/dispatcher.d.ts +6 -0
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +118 -16
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +20 -7
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/resolve.d.ts +1 -0
- package/dist/rules/resolve.d.ts.map +1 -1
- package/dist/rules/resolve.js +3 -2
- package/dist/rules/resolve.js.map +1 -1
- package/dist/rules/start-coordinator.d.ts +2 -0
- package/dist/rules/start-coordinator.d.ts.map +1 -1
- package/dist/rules/start-coordinator.js +3 -1
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/rules/terminal-cleanup.d.ts +23 -0
- package/dist/rules/terminal-cleanup.d.ts.map +1 -0
- package/dist/rules/terminal-cleanup.js +43 -0
- package/dist/rules/terminal-cleanup.js.map +1 -0
- package/dist/rules/tools.d.ts.map +1 -1
- package/dist/rules/tools.js +24 -2
- package/dist/rules/tools.js.map +1 -1
- package/dist/rules/transition-service.d.ts +3 -0
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +10 -6
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +32 -3
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js +2 -0
- package/dist/rules/types.js.map +1 -1
- package/dist/rules/validation.d.ts.map +1 -1
- package/dist/rules/validation.js +8 -3
- package/dist/rules/validation.js.map +1 -1
- package/dist/sandbox/base-checkpoint-triggers.d.ts +77 -0
- package/dist/sandbox/base-checkpoint-triggers.d.ts.map +1 -0
- package/dist/sandbox/base-checkpoint-triggers.js +137 -0
- package/dist/sandbox/base-checkpoint-triggers.js.map +1 -0
- package/dist/sandbox/base-checkpoint.d.ts +59 -0
- package/dist/sandbox/base-checkpoint.d.ts.map +1 -0
- package/dist/sandbox/base-checkpoint.js +145 -0
- package/dist/sandbox/base-checkpoint.js.map +1 -0
- package/dist/sandbox/fleet.d.ts +32 -0
- package/dist/sandbox/fleet.d.ts.map +1 -1
- package/dist/sandbox/fleet.js +39 -10
- package/dist/sandbox/fleet.js.map +1 -1
- package/dist/session/checkpoint-capture.d.ts +3 -2
- package/dist/session/checkpoint-capture.d.ts.map +1 -1
- package/dist/session/checkpoint-capture.js +3 -3
- package/dist/session/checkpoint-capture.js.map +1 -1
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +2 -1
- package/dist/session/factory-session.js.map +1 -1
- package/dist/session/filesystem-capture.d.ts +7 -0
- package/dist/session/filesystem-capture.d.ts.map +1 -1
- package/dist/session/filesystem-capture.js +41 -4
- package/dist/session/filesystem-capture.js.map +1 -1
- package/dist/session/model-pack-hydration.d.ts +55 -0
- package/dist/session/model-pack-hydration.d.ts.map +1 -0
- package/dist/session/model-pack-hydration.js +56 -0
- package/dist/session/model-pack-hydration.js.map +1 -0
- package/dist/skills/service.d.ts +9 -0
- package/dist/skills/service.d.ts.map +1 -1
- package/dist/skills/service.js +10 -1
- package/dist/skills/service.js.map +1 -1
- package/dist/storage/domains/intake/base.d.ts +36 -0
- package/dist/storage/domains/intake/base.d.ts.map +1 -1
- package/dist/storage/domains/intake/base.js +91 -2
- package/dist/storage/domains/intake/base.js.map +1 -1
- package/dist/storage/domains/model-packs/base.d.ts +26 -0
- package/dist/storage/domains/model-packs/base.d.ts.map +1 -1
- package/dist/storage/domains/model-packs/base.js +102 -13
- package/dist/storage/domains/model-packs/base.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +47 -1
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +78 -19
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +11 -3
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +10 -1
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +67 -1
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +183 -12
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/workspace.d.ts +31 -2
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +225 -54
- package/dist/workspace.js.map +1 -1
- package/factory-skills/factory-plan/SKILL.md +1 -1
- package/factory-skills/factory-rereview/SKILL.md +3 -3
- package/factory-skills/factory-review/SKILL.md +3 -3
- package/factory-skills/factory-triage/SKILL.md +2 -0
- package/package.json +8 -8
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only Mastra `apiRoutes` exposing the factory project's knowledge graph.
|
|
3
|
+
*
|
|
4
|
+
* Serves the Knowledge page in factory-ui: a polling graph snapshot (nodes
|
|
5
|
+
* as nodes, wikilink edges derived from record text), a node flyout payload
|
|
6
|
+
* with per-record provenance, and the recent activity feed. Every endpoint is a
|
|
7
|
+
* GET — this module never writes knowledge.
|
|
8
|
+
*
|
|
9
|
+
* Scoping is fail-closed: the org and resource rungs are derived server-side
|
|
10
|
+
* from the authenticated caller and the validated `:id` project. The DEFAULT
|
|
11
|
+
* view queries `[org:<orgId>, resource:<projectId>]` (org + project records).
|
|
12
|
+
* Thread-scoped records are reachable ONLY via an explicit, server-validated
|
|
13
|
+
* `threadId` query parameter (the drill-down view), which appends the thread
|
|
14
|
+
* rung to the query scope. A thread is drillable iff it produced knowledge
|
|
15
|
+
* visible under the caller's org/project prefix; unknown or cross-org threads
|
|
16
|
+
* 404 — never a silent fallback to the default view.
|
|
17
|
+
*/
|
|
18
|
+
import type { ApiRoute } from '@mastra/core/server';
|
|
19
|
+
import type { KnowledgeScope, KnowledgeStorage } from '@mastra/core/storage';
|
|
20
|
+
import type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';
|
|
21
|
+
import type { RouteDependencies } from './route.js';
|
|
22
|
+
import { Route } from './route.js';
|
|
23
|
+
/** Window caps. Injectable at construction only — never per-request. */
|
|
24
|
+
export interface KnowledgeRouteLimits {
|
|
25
|
+
/** Max nodes in a graph snapshot (newest-first). */
|
|
26
|
+
maxNodes: number;
|
|
27
|
+
/** Max records parsed for edges per snapshot (newest-first). */
|
|
28
|
+
maxRecords: number;
|
|
29
|
+
/** Max fallback `resolveNode` store lookups per request (deduped per unique name+scope). */
|
|
30
|
+
maxFallbackLookups: number;
|
|
31
|
+
}
|
|
32
|
+
export interface KnowledgeRoutesDeps extends RouteDependencies {
|
|
33
|
+
/** Factory projects domain — validates the `:id` project belongs to the caller's org. */
|
|
34
|
+
projects: FactoryProjectsStorage;
|
|
35
|
+
/** Lazy handle to the knowledge storage domain; endpoints 503 when absent. */
|
|
36
|
+
knowledge: () => Promise<KnowledgeStorage | undefined>;
|
|
37
|
+
limits?: Partial<KnowledgeRouteLimits>;
|
|
38
|
+
}
|
|
39
|
+
/** A graph node. `recordCount` is window-derived (records inside the snapshot window only). */
|
|
40
|
+
export interface KnowledgeGraphNode {
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
kind: string;
|
|
44
|
+
scope: KnowledgeScope;
|
|
45
|
+
/** Deepest rung of the record's scope: org | resource | thread. */
|
|
46
|
+
rung: 'org' | 'resource' | 'thread';
|
|
47
|
+
/**
|
|
48
|
+
* True when a non-deleted pinned record's wikilinks reference ONLY this
|
|
49
|
+
* node (A9: multi-target pins mark their edges instead — the pin is
|
|
50
|
+
* about the relationship; a single-target pin has no edge to carry it).
|
|
51
|
+
*/
|
|
52
|
+
pinned: boolean;
|
|
53
|
+
/** Records owned by this node INSIDE the snapshot window (not a total). */
|
|
54
|
+
recordCount: number;
|
|
55
|
+
createdAt: string;
|
|
56
|
+
updatedAt: string;
|
|
57
|
+
}
|
|
58
|
+
export interface KnowledgeGraphEdge {
|
|
59
|
+
id: string;
|
|
60
|
+
/** The owning node of the record (its `node`). */
|
|
61
|
+
source: string;
|
|
62
|
+
/** The wikilink-resolved node. */
|
|
63
|
+
target: string;
|
|
64
|
+
/**
|
|
65
|
+
* Always 'wikilink': the record's `node` is the edge SOURCE, so the
|
|
66
|
+
* plan's "parent link" collapses into the wikilink edge — nodes carry no
|
|
67
|
+
* separate parent field to derive a second edge type from.
|
|
68
|
+
*/
|
|
69
|
+
type: 'wikilink';
|
|
70
|
+
/** The record whose text produced the edge. */
|
|
71
|
+
recordId: string;
|
|
72
|
+
/**
|
|
73
|
+
* True when the edge is derived from a PINNED record linking two nodes —
|
|
74
|
+
* the pin marks the relationship, so the accent lives on the edge (A9).
|
|
75
|
+
*/
|
|
76
|
+
pinned?: boolean;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A knowledge record as a first-class graph element (A11): every record in the window,
|
|
80
|
+
* with the in-window nodes it touches. The client renders by arity —
|
|
81
|
+
* 1 node: a small dot linked to it; 2: the connecting line; 3+: a midpoint
|
|
82
|
+
* junction splitting to each node. Pin records have their hidden reserved
|
|
83
|
+
* owner omitted, so their arity comes purely from wikilink targets.
|
|
84
|
+
*/
|
|
85
|
+
export interface KnowledgeGraphRecord {
|
|
86
|
+
/** The record id. */
|
|
87
|
+
id: string;
|
|
88
|
+
/** Owner node first (omitted for pins), then resolved wikilink targets. */
|
|
89
|
+
nodeIds: string[];
|
|
90
|
+
pinned: boolean;
|
|
91
|
+
/** Record text, truncated for hover cards. */
|
|
92
|
+
text: string;
|
|
93
|
+
}
|
|
94
|
+
export interface KnowledgeGraphPayload {
|
|
95
|
+
view: 'project' | 'thread';
|
|
96
|
+
threadId?: string;
|
|
97
|
+
nodes: KnowledgeGraphNode[];
|
|
98
|
+
edges: KnowledgeGraphEdge[];
|
|
99
|
+
records: KnowledgeGraphRecord[];
|
|
100
|
+
/** True when the node or record window cap was hit (newest-first window). */
|
|
101
|
+
truncated: boolean;
|
|
102
|
+
/** Wikilink targets that resolved in the store but fell outside the node window. */
|
|
103
|
+
outOfWindow: Array<{
|
|
104
|
+
id: string;
|
|
105
|
+
name: string;
|
|
106
|
+
}>;
|
|
107
|
+
/** Unique unknown names skipped once the fallback-lookup cap was hit. */
|
|
108
|
+
unresolvedCapped: {
|
|
109
|
+
count: number;
|
|
110
|
+
names: string[];
|
|
111
|
+
};
|
|
112
|
+
/** Pin counts per rung of the active view (thread is null in the default view). */
|
|
113
|
+
pinCensus: {
|
|
114
|
+
resource: number;
|
|
115
|
+
thread: number | null;
|
|
116
|
+
};
|
|
117
|
+
/** Change hint: newest knowledge activity id (per-process monotonic — hint only). */
|
|
118
|
+
version: string | null;
|
|
119
|
+
}
|
|
120
|
+
export interface KnowledgeNodeRecordPayload {
|
|
121
|
+
id: string;
|
|
122
|
+
node: string;
|
|
123
|
+
/** 'owned' when the node is the record's parent, 'mentions' when it only wikilinks it. */
|
|
124
|
+
relation: 'owned' | 'mentions';
|
|
125
|
+
text: string;
|
|
126
|
+
scope: KnowledgeScope;
|
|
127
|
+
rung: 'org' | 'resource' | 'thread';
|
|
128
|
+
sourceThreadId: string;
|
|
129
|
+
capturedAt: string;
|
|
130
|
+
when?: string;
|
|
131
|
+
pinned: boolean;
|
|
132
|
+
metadata?: Record<string, unknown>;
|
|
133
|
+
}
|
|
134
|
+
export interface KnowledgeNodePayload {
|
|
135
|
+
node: {
|
|
136
|
+
id: string;
|
|
137
|
+
name: string;
|
|
138
|
+
kind: string;
|
|
139
|
+
content: string;
|
|
140
|
+
scope: KnowledgeScope;
|
|
141
|
+
rung: 'org' | 'resource' | 'thread';
|
|
142
|
+
createdAt: string;
|
|
143
|
+
updatedAt: string;
|
|
144
|
+
};
|
|
145
|
+
records: KnowledgeNodeRecordPayload[];
|
|
146
|
+
}
|
|
147
|
+
export declare class KnowledgeRoutes extends Route<KnowledgeRoutesDeps> {
|
|
148
|
+
#private;
|
|
149
|
+
constructor(deps: KnowledgeRoutesDeps);
|
|
150
|
+
routes(): ApiRoute[];
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=knowledge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge.d.ts","sourceRoot":"","sources":["../../src/routes/knowledge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAkC,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAS7G,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAcnC,wEAAwE;AACxE,MAAM,WAAW,oBAAoB;IACnC,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,4FAA4F;IAC5F,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAID,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC5D,yFAAyF;IACzF,QAAQ,EAAE,sBAAsB,CAAC;IACjC,8EAA8E;IAC9E,SAAS,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;IACvD,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACxC;AAED,+FAA+F;AAC/F,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,cAAc,CAAC;IACtB,mEAAmE;IACnE,IAAI,EAAE,KAAK,GAAG,UAAU,GAAG,QAAQ,CAAC;IACpC;;;;OAIG;IACH,MAAM,EAAE,OAAO,CAAC;IAChB,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,IAAI,EAAE,UAAU,CAAC;IACjB,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,qBAAqB;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,2EAA2E;IAC3E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,6EAA6E;IAC7E,SAAS,EAAE,OAAO,CAAC;IACnB,oFAAoF;IACpF,WAAW,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,yEAAyE;IACzE,gBAAgB,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACrD,mFAAmF;IACnF,SAAS,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACvD,qFAAqF;IACrF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,0FAA0F;IAC1F,QAAQ,EAAE,OAAO,GAAG,UAAU,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,cAAc,CAAC;IACtB,IAAI,EAAE,KAAK,GAAG,UAAU,GAAG,QAAQ,CAAC;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,cAAc,CAAC;QACtB,IAAI,EAAE,KAAK,GAAG,UAAU,GAAG,QAAQ,CAAC;QACpC,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,OAAO,EAAE,0BAA0B,EAAE,CAAC;CACvC;AA+GD,qBAAa,eAAgB,SAAQ,KAAK,CAAC,mBAAmB,CAAC;;gBAGjD,IAAI,EAAE,mBAAmB;IAqHrC,MAAM,IAAI,QAAQ,EAAE;CAiOrB"}
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { Route } from "./route.js";
|
|
2
|
+
import { registerApiRoute } from "@mastra/core/server";
|
|
3
|
+
import { canonicalizeKnowledgeScope, isKnowledgeScopeVisible, knowledgeScopeKey, parseKnowledgeWikilinks } from "@mastra/core/storage";
|
|
4
|
+
//#region src/routes/knowledge.ts
|
|
5
|
+
/** Reserved node that anchors pinned records (see subconscious/pinned.ts). */
|
|
6
|
+
const PINNED_NODE_NAME = "pinned";
|
|
7
|
+
/** Hover-card budget for record text shipped in the graph payload. */
|
|
8
|
+
const RECORD_TEXT_LIMIT = 240;
|
|
9
|
+
function truncateRecordText(text) {
|
|
10
|
+
return text.length > RECORD_TEXT_LIMIT ? `${text.slice(0, RECORD_TEXT_LIMIT - 1)}…` : text;
|
|
11
|
+
}
|
|
12
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
13
|
+
const DEFAULT_LIMITS = {
|
|
14
|
+
maxNodes: 500,
|
|
15
|
+
maxRecords: 2e3,
|
|
16
|
+
maxFallbackLookups: 100
|
|
17
|
+
};
|
|
18
|
+
function loose(c) {
|
|
19
|
+
return c;
|
|
20
|
+
}
|
|
21
|
+
function deepestRung(scope) {
|
|
22
|
+
let rung = "org";
|
|
23
|
+
for (const entry of scope) {
|
|
24
|
+
const ns = entry.slice(0, entry.indexOf(":"));
|
|
25
|
+
if (ns === "thread") return "thread";
|
|
26
|
+
if (ns === "resource") rung = "resource";
|
|
27
|
+
}
|
|
28
|
+
return rung;
|
|
29
|
+
}
|
|
30
|
+
function boundedThreadId(raw) {
|
|
31
|
+
if (!raw) return void 0;
|
|
32
|
+
const trimmed = raw.trim();
|
|
33
|
+
return trimmed.length > 0 && trimmed.length <= 512 ? trimmed : void 0;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* In-record + capped-fallback wikilink resolver, shared by both endpoints.
|
|
37
|
+
* Resolution uses the store's own algorithm: a descending-prefix walk over the
|
|
38
|
+
* record's canonical scope matching canonical name + exact scope key at each
|
|
39
|
+
* prefix, so an edge never depends on whether the target landed in the window.
|
|
40
|
+
*/
|
|
41
|
+
var WikilinkResolver = class {
|
|
42
|
+
/** exact `${scopeKey}\u0000${lowerName}` → node, from the fetched window. */
|
|
43
|
+
#inWindow = /* @__PURE__ */ new Map();
|
|
44
|
+
#windowIds = /* @__PURE__ */ new Set();
|
|
45
|
+
/** `${recordScopeKey}\u0000${lowerName}` → fallback result (null = dangling). */
|
|
46
|
+
#fallbackCache = /* @__PURE__ */ new Map();
|
|
47
|
+
#fallbackLookups = 0;
|
|
48
|
+
#store;
|
|
49
|
+
#maxFallbackLookups;
|
|
50
|
+
outOfWindow = /* @__PURE__ */ new Map();
|
|
51
|
+
cappedNames = [];
|
|
52
|
+
#cappedSeen = /* @__PURE__ */ new Set();
|
|
53
|
+
constructor(store, nodes, maxFallbackLookups) {
|
|
54
|
+
this.#store = store;
|
|
55
|
+
this.#maxFallbackLookups = maxFallbackLookups;
|
|
56
|
+
for (const node of nodes) {
|
|
57
|
+
this.#inWindow.set(`${knowledgeScopeKey(node.scope)}\u0000${node.name.trim().toLocaleLowerCase()}`, node);
|
|
58
|
+
this.#windowIds.add(node.id);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
inWindowId(id) {
|
|
62
|
+
return this.#windowIds.has(id);
|
|
63
|
+
}
|
|
64
|
+
/** Resolve a wikilink name from a knowledge record's scope. Returns the node or null (dangling/capped). */
|
|
65
|
+
async resolve(name, recordScope) {
|
|
66
|
+
const canonical = canonicalizeKnowledgeScope(recordScope);
|
|
67
|
+
const lower = name.trim().toLocaleLowerCase();
|
|
68
|
+
for (let length = canonical.length; length > 0; length--) {
|
|
69
|
+
const hit = this.#inWindow.get(`${knowledgeScopeKey(canonical.slice(0, length))}\u0000${lower}`);
|
|
70
|
+
if (hit) return hit;
|
|
71
|
+
}
|
|
72
|
+
const cacheKey = `${knowledgeScopeKey(canonical)}\u0000${lower}`;
|
|
73
|
+
if (this.#fallbackCache.has(cacheKey)) return this.#trackOutOfWindow(this.#fallbackCache.get(cacheKey) ?? null);
|
|
74
|
+
if (this.#fallbackLookups >= this.#maxFallbackLookups) {
|
|
75
|
+
if (!this.#cappedSeen.has(lower) && this.cappedNames.length < 100) {
|
|
76
|
+
this.#cappedSeen.add(lower);
|
|
77
|
+
this.cappedNames.push(name.trim());
|
|
78
|
+
} else if (!this.#cappedSeen.has(lower)) this.#cappedSeen.add(lower);
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
this.#fallbackLookups += 1;
|
|
82
|
+
let resolved = null;
|
|
83
|
+
try {
|
|
84
|
+
resolved = await this.#store.resolveNode({
|
|
85
|
+
name,
|
|
86
|
+
scope: canonical
|
|
87
|
+
});
|
|
88
|
+
} catch {
|
|
89
|
+
resolved = null;
|
|
90
|
+
}
|
|
91
|
+
this.#fallbackCache.set(cacheKey, resolved);
|
|
92
|
+
return this.#trackOutOfWindow(resolved);
|
|
93
|
+
}
|
|
94
|
+
#trackOutOfWindow(node) {
|
|
95
|
+
if (node && !this.#windowIds.has(node.id)) this.outOfWindow.set(node.id, {
|
|
96
|
+
id: node.id,
|
|
97
|
+
name: node.name
|
|
98
|
+
});
|
|
99
|
+
return node;
|
|
100
|
+
}
|
|
101
|
+
get cappedCount() {
|
|
102
|
+
return this.#cappedSeen.size;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
var KnowledgeRoutes = class extends Route {
|
|
106
|
+
#limits;
|
|
107
|
+
constructor(deps) {
|
|
108
|
+
super(deps);
|
|
109
|
+
this.#limits = {
|
|
110
|
+
...DEFAULT_LIMITS,
|
|
111
|
+
...deps.limits
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** Resolve the `(orgId, userId)` tenant or a ready-to-return error response. */
|
|
115
|
+
async #resolveTenant(c) {
|
|
116
|
+
await this.deps.auth.ensureUser(c);
|
|
117
|
+
const tenant = this.deps.auth.tenant(c);
|
|
118
|
+
if (!tenant) return { response: c.json({ error: "unauthorized" }, 401) };
|
|
119
|
+
if (!tenant.orgId) return { response: c.json({
|
|
120
|
+
error: "organization_required",
|
|
121
|
+
message: "The knowledge graph requires an organization."
|
|
122
|
+
}, 403) };
|
|
123
|
+
return {
|
|
124
|
+
orgId: tenant.orgId,
|
|
125
|
+
userId: tenant.userId
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Resolve tenant + org-owned project + knowledge store + the active view's
|
|
130
|
+
* query scope. The ONE seam both endpoints share, so the default/thread view
|
|
131
|
+
* scope and the pin rungs cannot drift between them.
|
|
132
|
+
*
|
|
133
|
+
* threadId validation runs a single `listRecordsBySource` lookup with
|
|
134
|
+
* `limit: 1` AT THE CANDIDATE SCOPE `[org, resource, thread:<id>]` — the
|
|
135
|
+
* store's own visibility predicate is the authorization: the thread's own
|
|
136
|
+
* records (equal scope key) and its project/org captures (prefix) match, while
|
|
137
|
+
* a cross-org thread's records match nothing → zero rows → 404.
|
|
138
|
+
*/
|
|
139
|
+
async #resolveView(c) {
|
|
140
|
+
const tenant = await this.#resolveTenant(c);
|
|
141
|
+
if ("response" in tenant) return tenant;
|
|
142
|
+
const projectId = c.req.param("id");
|
|
143
|
+
if (!projectId || !UUID_RE.test(projectId)) return { response: c.json({ error: "Project not found" }, 404) };
|
|
144
|
+
const { projects } = this.deps;
|
|
145
|
+
await projects.ensureReady();
|
|
146
|
+
if (!await projects.get({
|
|
147
|
+
orgId: tenant.orgId,
|
|
148
|
+
id: projectId
|
|
149
|
+
})) return { response: c.json({ error: "Project not found" }, 404) };
|
|
150
|
+
const store = await this.deps.knowledge();
|
|
151
|
+
if (!store) return { response: c.json({
|
|
152
|
+
error: "knowledge_unavailable",
|
|
153
|
+
message: "The knowledge storage domain is not configured."
|
|
154
|
+
}, 503) };
|
|
155
|
+
const defaultScope = [`org:${tenant.orgId}`, `resource:${projectId}`];
|
|
156
|
+
const resourceRungScope = defaultScope;
|
|
157
|
+
const threadId = boundedThreadId(c.req.query("threadId"));
|
|
158
|
+
if (c.req.query("threadId") !== void 0 && !threadId) return { response: c.json({ error: "thread_not_found" }, 404) };
|
|
159
|
+
if (!threadId) return {
|
|
160
|
+
...tenant,
|
|
161
|
+
factoryProjectId: projectId,
|
|
162
|
+
store,
|
|
163
|
+
view: "project",
|
|
164
|
+
scope: defaultScope,
|
|
165
|
+
pinRungs: [{
|
|
166
|
+
rung: "resource",
|
|
167
|
+
scope: resourceRungScope
|
|
168
|
+
}]
|
|
169
|
+
};
|
|
170
|
+
const candidateScope = [...defaultScope, `thread:${threadId}`];
|
|
171
|
+
if ((await store.knowledgeBySource({
|
|
172
|
+
sourceThreadId: threadId,
|
|
173
|
+
scope: candidateScope,
|
|
174
|
+
limit: 1
|
|
175
|
+
})).records.length === 0) return { response: c.json({ error: "thread_not_found" }, 404) };
|
|
176
|
+
return {
|
|
177
|
+
...tenant,
|
|
178
|
+
factoryProjectId: projectId,
|
|
179
|
+
store,
|
|
180
|
+
view: "thread",
|
|
181
|
+
threadId,
|
|
182
|
+
scope: candidateScope,
|
|
183
|
+
pinRungs: [{
|
|
184
|
+
rung: "resource",
|
|
185
|
+
scope: resourceRungScope
|
|
186
|
+
}, {
|
|
187
|
+
rung: "thread",
|
|
188
|
+
scope: candidateScope
|
|
189
|
+
}]
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/** Reserved `pinned` node ids at the active view's rungs (one exact-scope lookup per rung). */
|
|
193
|
+
async #pinnedNodeIds(view) {
|
|
194
|
+
const out = [];
|
|
195
|
+
for (const { rung, scope } of view.pinRungs) {
|
|
196
|
+
const node = await view.store.getNodeByName({
|
|
197
|
+
name: PINNED_NODE_NAME,
|
|
198
|
+
scope
|
|
199
|
+
});
|
|
200
|
+
if (node && !node.mergedInto) out.push({
|
|
201
|
+
rung,
|
|
202
|
+
id: node.id
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
/** Non-deleted pinned records for the given pinned-node ids, visible in the view. */
|
|
208
|
+
async #pinnedRecords(view, pinnedNodeIds) {
|
|
209
|
+
const out = [];
|
|
210
|
+
for (const { rung, id } of pinnedNodeIds) {
|
|
211
|
+
const { records } = await view.store.listKnowledgeAbout({
|
|
212
|
+
node: id,
|
|
213
|
+
scope: view.scope,
|
|
214
|
+
limit: 200
|
|
215
|
+
});
|
|
216
|
+
for (const record of records) out.push({
|
|
217
|
+
rung,
|
|
218
|
+
record
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
222
|
+
}
|
|
223
|
+
routes() {
|
|
224
|
+
return [
|
|
225
|
+
registerApiRoute("/web/factory/projects/:id/knowledge/graph", {
|
|
226
|
+
method: "GET",
|
|
227
|
+
requiresAuth: false,
|
|
228
|
+
handler: async (c) => {
|
|
229
|
+
const view = await this.#resolveView(loose(c));
|
|
230
|
+
if ("response" in view) return view.response;
|
|
231
|
+
const { store, scope } = view;
|
|
232
|
+
const limits = this.#limits;
|
|
233
|
+
const fetched = await store.listNodes({
|
|
234
|
+
scope,
|
|
235
|
+
limit: limits.maxNodes + 1
|
|
236
|
+
});
|
|
237
|
+
let truncated = fetched.length > limits.maxNodes;
|
|
238
|
+
const pinnedNodeIds = await this.#pinnedNodeIds(view);
|
|
239
|
+
const pinnedNodeIdSet = new Set(pinnedNodeIds.map((p) => p.id));
|
|
240
|
+
const nodes = fetched.slice(0, limits.maxNodes).filter((node) => !pinnedNodeIdSet.has(node.id));
|
|
241
|
+
const recordWindow = [];
|
|
242
|
+
for (const node of nodes) {
|
|
243
|
+
if (recordWindow.length > limits.maxRecords) break;
|
|
244
|
+
const { records } = await store.listKnowledgeAbout({
|
|
245
|
+
node: node.id,
|
|
246
|
+
scope,
|
|
247
|
+
limit: limits.maxRecords + 1 - recordWindow.length
|
|
248
|
+
});
|
|
249
|
+
recordWindow.push(...records);
|
|
250
|
+
}
|
|
251
|
+
recordWindow.sort((a, b) => b.id.localeCompare(a.id));
|
|
252
|
+
if (recordWindow.length > limits.maxRecords) {
|
|
253
|
+
truncated = true;
|
|
254
|
+
recordWindow.length = limits.maxRecords;
|
|
255
|
+
}
|
|
256
|
+
const resolver = new WikilinkResolver(store, nodes, limits.maxFallbackLookups);
|
|
257
|
+
const edges = [];
|
|
258
|
+
const graphRecords = [];
|
|
259
|
+
const edgeSeen = /* @__PURE__ */ new Set();
|
|
260
|
+
const recordCounts = /* @__PURE__ */ new Map();
|
|
261
|
+
for (const record of recordWindow) {
|
|
262
|
+
recordCounts.set(record.node, (recordCounts.get(record.node) ?? 0) + 1);
|
|
263
|
+
const nodeIds = [record.node];
|
|
264
|
+
for (const name of parseKnowledgeWikilinks(record.text)) {
|
|
265
|
+
const target = await resolver.resolve(name, record.scope);
|
|
266
|
+
if (!target) continue;
|
|
267
|
+
if (target.id === record.node) continue;
|
|
268
|
+
if (!resolver.inWindowId(target.id)) continue;
|
|
269
|
+
if (!nodeIds.includes(target.id)) nodeIds.push(target.id);
|
|
270
|
+
const key = `${record.node}\u0000${target.id}`;
|
|
271
|
+
if (edgeSeen.has(key)) continue;
|
|
272
|
+
edgeSeen.add(key);
|
|
273
|
+
edges.push({
|
|
274
|
+
id: `wikilink:${record.node}:${target.id}`,
|
|
275
|
+
source: record.node,
|
|
276
|
+
target: target.id,
|
|
277
|
+
type: "wikilink",
|
|
278
|
+
recordId: record.id
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
graphRecords.push({
|
|
282
|
+
id: record.id,
|
|
283
|
+
nodeIds,
|
|
284
|
+
pinned: false,
|
|
285
|
+
text: truncateRecordText(record.text)
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
const pinnedRecords = await this.#pinnedRecords(view, pinnedNodeIds);
|
|
289
|
+
const accented = /* @__PURE__ */ new Set();
|
|
290
|
+
for (const { record } of pinnedRecords) {
|
|
291
|
+
const targets = [];
|
|
292
|
+
for (const name of parseKnowledgeWikilinks(record.text)) {
|
|
293
|
+
const target = await resolver.resolve(name, record.scope);
|
|
294
|
+
if (target && resolver.inWindowId(target.id) && !targets.includes(target.id)) targets.push(target.id);
|
|
295
|
+
}
|
|
296
|
+
graphRecords.push({
|
|
297
|
+
id: record.id,
|
|
298
|
+
nodeIds: targets,
|
|
299
|
+
pinned: true,
|
|
300
|
+
text: truncateRecordText(record.text)
|
|
301
|
+
});
|
|
302
|
+
if (targets.length === 1) {
|
|
303
|
+
accented.add(targets[0]);
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
for (let a = 0; a < targets.length; a += 1) for (let b = a + 1; b < targets.length; b += 1) {
|
|
307
|
+
const key = `${targets[a]}\u0000${targets[b]}\u0000pin`;
|
|
308
|
+
if (edgeSeen.has(key)) continue;
|
|
309
|
+
edgeSeen.add(key);
|
|
310
|
+
edges.push({
|
|
311
|
+
id: `pin:${record.id}:${targets[a]}:${targets[b]}`,
|
|
312
|
+
source: targets[a],
|
|
313
|
+
target: targets[b],
|
|
314
|
+
type: "wikilink",
|
|
315
|
+
recordId: record.id,
|
|
316
|
+
pinned: true
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const pinCensus = {
|
|
321
|
+
resource: pinnedRecords.filter((p) => p.rung === "resource").length,
|
|
322
|
+
thread: view.view === "thread" ? pinnedRecords.filter((p) => p.rung === "thread").length : null
|
|
323
|
+
};
|
|
324
|
+
const activity = await store.listActivity({
|
|
325
|
+
scope,
|
|
326
|
+
limit: 1
|
|
327
|
+
});
|
|
328
|
+
const payload = {
|
|
329
|
+
view: view.view,
|
|
330
|
+
...view.threadId ? { threadId: view.threadId } : {},
|
|
331
|
+
nodes: nodes.map((node) => ({
|
|
332
|
+
id: node.id,
|
|
333
|
+
name: node.name,
|
|
334
|
+
kind: node.kind,
|
|
335
|
+
scope: node.scope,
|
|
336
|
+
rung: deepestRung(node.scope),
|
|
337
|
+
pinned: accented.has(node.id),
|
|
338
|
+
recordCount: recordCounts.get(node.id) ?? 0,
|
|
339
|
+
createdAt: node.createdAt.toISOString(),
|
|
340
|
+
updatedAt: node.updatedAt.toISOString()
|
|
341
|
+
})),
|
|
342
|
+
edges,
|
|
343
|
+
records: graphRecords,
|
|
344
|
+
truncated,
|
|
345
|
+
outOfWindow: [...resolver.outOfWindow.values()],
|
|
346
|
+
unresolvedCapped: {
|
|
347
|
+
count: resolver.cappedCount,
|
|
348
|
+
names: resolver.cappedNames
|
|
349
|
+
},
|
|
350
|
+
pinCensus,
|
|
351
|
+
version: activity[0]?.id ?? null
|
|
352
|
+
};
|
|
353
|
+
return c.json(payload);
|
|
354
|
+
}
|
|
355
|
+
}),
|
|
356
|
+
registerApiRoute("/web/factory/projects/:id/knowledge/nodes/:nodeId", {
|
|
357
|
+
method: "GET",
|
|
358
|
+
requiresAuth: false,
|
|
359
|
+
handler: async (c) => {
|
|
360
|
+
const view = await this.#resolveView(loose(c));
|
|
361
|
+
if ("response" in view) return view.response;
|
|
362
|
+
const { store, scope } = view;
|
|
363
|
+
const nodeId = loose(c).req.param("nodeId");
|
|
364
|
+
if (!nodeId || nodeId.length > 512) return c.json({ error: "node_not_found" }, 404);
|
|
365
|
+
const node = await store.getNode(nodeId);
|
|
366
|
+
if (!node || !isKnowledgeScopeVisible(node.scope, scope)) return c.json({ error: "node_not_found" }, 404);
|
|
367
|
+
const pinnedNodeIds = await this.#pinnedNodeIds(view);
|
|
368
|
+
const pinnedNodeIdSet = new Set(pinnedNodeIds.map((p) => p.id));
|
|
369
|
+
const [owned, mentioning] = await Promise.all([store.listKnowledgeAbout({
|
|
370
|
+
node: node.id,
|
|
371
|
+
scope,
|
|
372
|
+
limit: 200
|
|
373
|
+
}), store.listKnowledgeMentioning({
|
|
374
|
+
node: node.id,
|
|
375
|
+
scope,
|
|
376
|
+
limit: 200
|
|
377
|
+
})]);
|
|
378
|
+
const seen = /* @__PURE__ */ new Set();
|
|
379
|
+
const records = [];
|
|
380
|
+
const push = (record, relation) => {
|
|
381
|
+
if (seen.has(record.id)) return;
|
|
382
|
+
seen.add(record.id);
|
|
383
|
+
records.push({
|
|
384
|
+
id: record.id,
|
|
385
|
+
node: record.node,
|
|
386
|
+
relation,
|
|
387
|
+
text: record.text,
|
|
388
|
+
scope: record.scope,
|
|
389
|
+
rung: deepestRung(record.scope),
|
|
390
|
+
sourceThreadId: record.sourceThreadId,
|
|
391
|
+
capturedAt: record.capturedAt.toISOString(),
|
|
392
|
+
...record.when ? { when: record.when.toISOString() } : {},
|
|
393
|
+
pinned: pinnedNodeIdSet.has(record.node),
|
|
394
|
+
...record.metadata ? { metadata: record.metadata } : {}
|
|
395
|
+
});
|
|
396
|
+
};
|
|
397
|
+
for (const record of [...owned.records].sort((a, b) => b.id.localeCompare(a.id))) push(record, "owned");
|
|
398
|
+
for (const record of [...mentioning.records].sort((a, b) => b.id.localeCompare(a.id))) push(record, "mentions");
|
|
399
|
+
const payload = {
|
|
400
|
+
node: {
|
|
401
|
+
id: node.id,
|
|
402
|
+
name: node.name,
|
|
403
|
+
kind: node.kind,
|
|
404
|
+
content: node.content ?? "",
|
|
405
|
+
scope: node.scope,
|
|
406
|
+
rung: deepestRung(node.scope),
|
|
407
|
+
createdAt: node.createdAt.toISOString(),
|
|
408
|
+
updatedAt: node.updatedAt.toISOString()
|
|
409
|
+
},
|
|
410
|
+
records
|
|
411
|
+
};
|
|
412
|
+
return c.json(payload);
|
|
413
|
+
}
|
|
414
|
+
}),
|
|
415
|
+
registerApiRoute("/web/factory/projects/:id/knowledge/activity", {
|
|
416
|
+
method: "GET",
|
|
417
|
+
requiresAuth: false,
|
|
418
|
+
handler: async (c) => {
|
|
419
|
+
const view = await this.#resolveView(loose(c));
|
|
420
|
+
if ("response" in view) return view.response;
|
|
421
|
+
const events = await view.store.listActivity({
|
|
422
|
+
scope: view.scope,
|
|
423
|
+
limit: 100
|
|
424
|
+
});
|
|
425
|
+
return c.json({ events: events.map((event) => ({
|
|
426
|
+
id: event.id,
|
|
427
|
+
action: event.action,
|
|
428
|
+
recordType: event.recordType,
|
|
429
|
+
recordId: event.recordId,
|
|
430
|
+
scope: event.scope,
|
|
431
|
+
...event.sourceThreadId ? { sourceThreadId: event.sourceThreadId } : {},
|
|
432
|
+
createdAt: event.createdAt.toISOString()
|
|
433
|
+
})) });
|
|
434
|
+
}
|
|
435
|
+
})
|
|
436
|
+
];
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
//#endregion
|
|
440
|
+
export { KnowledgeRoutes };
|
|
441
|
+
|
|
442
|
+
//# sourceMappingURL=knowledge.js.map
|