@dxos/plugin-daily-summary 0.8.3
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/LICENSE +8 -0
- package/dist/lib/browser/DailySummarySettings-RF6P6R5M.mjs +63 -0
- package/dist/lib/browser/DailySummarySettings-RF6P6R5M.mjs.map +7 -0
- package/dist/lib/browser/app-graph-builder-RHJEYETQ.mjs +39 -0
- package/dist/lib/browser/app-graph-builder-RHJEYETQ.mjs.map +7 -0
- package/dist/lib/browser/blueprint-definition-XOEDPUVU.mjs +53 -0
- package/dist/lib/browser/blueprint-definition-XOEDPUVU.mjs.map +7 -0
- package/dist/lib/browser/chunk-2TNDFGQB.mjs +21 -0
- package/dist/lib/browser/chunk-2TNDFGQB.mjs.map +7 -0
- package/dist/lib/browser/chunk-UYXGTD3T.mjs +40 -0
- package/dist/lib/browser/chunk-UYXGTD3T.mjs.map +7 -0
- package/dist/lib/browser/generate-FSSCIHAO.mjs +172 -0
- package/dist/lib/browser/generate-FSSCIHAO.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +48 -0
- package/dist/lib/browser/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -0
- package/dist/lib/browser/react-surface-WCGJVZCQ.mjs +36 -0
- package/dist/lib/browser/react-surface-WCGJVZCQ.mjs.map +7 -0
- package/dist/types/src/DailySummaryPlugin.d.ts +3 -0
- package/dist/types/src/DailySummaryPlugin.d.ts.map +1 -0
- package/dist/types/src/blueprints/daily-summary-blueprint.d.ts +7 -0
- package/dist/types/src/blueprints/daily-summary-blueprint.d.ts.map +1 -0
- package/dist/types/src/blueprints/functions/definitions.d.ts +18 -0
- package/dist/types/src/blueprints/functions/definitions.d.ts.map +1 -0
- package/dist/types/src/blueprints/functions/generate.d.ts +18 -0
- package/dist/types/src/blueprints/functions/generate.d.ts.map +1 -0
- package/dist/types/src/blueprints/functions/generate.test.d.ts +2 -0
- package/dist/types/src/blueprints/functions/generate.test.d.ts.map +1 -0
- package/dist/types/src/blueprints/functions/index.d.ts +4 -0
- package/dist/types/src/blueprints/functions/index.d.ts.map +1 -0
- package/dist/types/src/blueprints/index.d.ts +3 -0
- package/dist/types/src/blueprints/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +6 -0
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts +3 -0
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/blueprint-definition/blueprint-definition.d.ts +6 -0
- package/dist/types/src/capabilities/blueprint-definition/blueprint-definition.d.ts.map +1 -0
- package/dist/types/src/capabilities/blueprint-definition/index.d.ts +3 -0
- package/dist/types/src/capabilities/blueprint-definition/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +4 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface/index.d.ts +3 -0
- package/dist/types/src/capabilities/react-surface/index.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts +5 -0
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -0
- package/dist/types/src/containers/DailySummarySettings/DailySummarySettings.d.ts +7 -0
- package/dist/types/src/containers/DailySummarySettings/DailySummarySettings.d.ts.map +1 -0
- package/dist/types/src/containers/DailySummarySettings/index.d.ts +3 -0
- package/dist/types/src/containers/DailySummarySettings/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +3 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/meta.d.ts +3 -0
- package/dist/types/src/meta.d.ts.map +1 -0
- package/dist/types/src/meta.test.d.ts +2 -0
- package/dist/types/src/meta.test.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +11 -0
- package/dist/types/src/translations.d.ts.map +1 -0
- package/dist/types/src/translations.test.d.ts +2 -0
- package/dist/types/src/translations.test.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -0
- package/package.json +71 -0
- package/src/DailySummaryPlugin.tsx +18 -0
- package/src/blueprints/daily-summary-blueprint.ts +37 -0
- package/src/blueprints/functions/definitions.ts +43 -0
- package/src/blueprints/functions/generate.test.ts +143 -0
- package/src/blueprints/functions/generate.ts +182 -0
- package/src/blueprints/functions/index.ts +9 -0
- package/src/blueprints/index.ts +6 -0
- package/src/capabilities/app-graph-builder/app-graph-builder.ts +37 -0
- package/src/capabilities/app-graph-builder/index.ts +7 -0
- package/src/capabilities/blueprint-definition/blueprint-definition.ts +17 -0
- package/src/capabilities/blueprint-definition/index.ts +7 -0
- package/src/capabilities/index.ts +7 -0
- package/src/capabilities/react-surface/index.ts +7 -0
- package/src/capabilities/react-surface/react-surface.tsx +33 -0
- package/src/containers/DailySummarySettings/DailySummarySettings.tsx +68 -0
- package/src/containers/DailySummarySettings/index.ts +7 -0
- package/src/containers/index.ts +7 -0
- package/src/index.ts +6 -0
- package/src/meta.test.ts +21 -0
- package/src/meta.ts +19 -0
- package/src/translations.test.ts +20 -0
- package/src/translations.ts +20 -0
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dxos/plugin-daily-summary",
|
|
3
|
+
"version": "0.8.3",
|
|
4
|
+
"description": "Daily activity summary plugin",
|
|
5
|
+
"homepage": "https://dxos.org",
|
|
6
|
+
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"author": "DXOS.org",
|
|
13
|
+
"sideEffects": true,
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"source": "./src/index.ts",
|
|
18
|
+
"types": "./dist/types/src/index.d.ts",
|
|
19
|
+
"browser": "./dist/lib/browser/index.mjs"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"types": "dist/types/src/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@effect/ai": "0.33.2",
|
|
29
|
+
"effect": "3.20.0",
|
|
30
|
+
"@dxos/ai": "0.8.3",
|
|
31
|
+
"@dxos/app-toolkit": "0.8.3",
|
|
32
|
+
"@dxos/app-framework": "0.8.3",
|
|
33
|
+
"@dxos/client": "0.8.3",
|
|
34
|
+
"@dxos/assistant": "0.8.3",
|
|
35
|
+
"@dxos/client-protocol": "0.8.3",
|
|
36
|
+
"@dxos/functions": "0.8.3",
|
|
37
|
+
"@dxos/functions-runtime": "0.8.3",
|
|
38
|
+
"@dxos/effect": "0.8.3",
|
|
39
|
+
"@dxos/blueprints": "0.8.3",
|
|
40
|
+
"@dxos/echo": "0.8.3",
|
|
41
|
+
"@dxos/invariant": "0.8.3",
|
|
42
|
+
"@dxos/operation": "0.8.3",
|
|
43
|
+
"@dxos/keys": "0.8.3",
|
|
44
|
+
"@dxos/plugin-space": "0.8.3",
|
|
45
|
+
"@dxos/react-client": "0.8.3",
|
|
46
|
+
"@dxos/types": "0.8.3",
|
|
47
|
+
"@dxos/util": "0.8.3",
|
|
48
|
+
"@dxos/plugin-graph": "0.8.3",
|
|
49
|
+
"@dxos/schema": "0.8.3"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@storybook/react-vite": "^10.2.12",
|
|
53
|
+
"@types/react": "~19.2.7",
|
|
54
|
+
"@types/react-dom": "~19.2.3",
|
|
55
|
+
"react": "~19.2.3",
|
|
56
|
+
"react-dom": "~19.2.3",
|
|
57
|
+
"vite": "^7.1.11",
|
|
58
|
+
"@dxos/react-ui": "0.8.3",
|
|
59
|
+
"@dxos/storybook-utils": "0.8.3",
|
|
60
|
+
"@dxos/echo-db": "0.8.3"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"effect": "3.20.0",
|
|
64
|
+
"react": "~19.2.3",
|
|
65
|
+
"react-dom": "~19.2.3",
|
|
66
|
+
"@dxos/react-ui": "0.8.3"
|
|
67
|
+
},
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Plugin } from '@dxos/app-framework';
|
|
6
|
+
import { AppPlugin } from '@dxos/app-toolkit';
|
|
7
|
+
|
|
8
|
+
import { AppGraphBuilder, BlueprintDefinition, ReactSurface } from './capabilities';
|
|
9
|
+
import { meta } from './meta';
|
|
10
|
+
import { translations } from './translations';
|
|
11
|
+
|
|
12
|
+
export const DailySummaryPlugin = Plugin.define(meta).pipe(
|
|
13
|
+
AppPlugin.addAppGraphModule({ activate: AppGraphBuilder }),
|
|
14
|
+
AppPlugin.addBlueprintDefinitionModule({ activate: BlueprintDefinition }),
|
|
15
|
+
AppPlugin.addSurfaceModule({ activate: ReactSurface }),
|
|
16
|
+
AppPlugin.addTranslationsModule({ translations }),
|
|
17
|
+
Plugin.make,
|
|
18
|
+
);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type AppCapabilities } from '@dxos/app-toolkit';
|
|
6
|
+
import { Blueprint, Template } from '@dxos/blueprints';
|
|
7
|
+
import { trim } from '@dxos/util';
|
|
8
|
+
|
|
9
|
+
import { DailySummaryHandlers, GenerateSummary } from './functions';
|
|
10
|
+
|
|
11
|
+
export const BLUEPRINT_KEY = 'org.dxos.blueprint.daily-summary';
|
|
12
|
+
|
|
13
|
+
export { DailySummaryHandlers };
|
|
14
|
+
|
|
15
|
+
const make = () =>
|
|
16
|
+
Blueprint.make({
|
|
17
|
+
key: BLUEPRINT_KEY,
|
|
18
|
+
name: 'Daily Summary',
|
|
19
|
+
tools: Blueprint.toolDefinitions({ operations: [GenerateSummary] }),
|
|
20
|
+
instructions: Template.make({
|
|
21
|
+
source: trim`
|
|
22
|
+
{{! Daily Summary }}
|
|
23
|
+
|
|
24
|
+
You generate concise daily activity summaries as Markdown documents.
|
|
25
|
+
When asked to summarize, use the generate tool to query recent objects and produce a summary.
|
|
26
|
+
The tool creates a Markdown document in a "Summaries" collection.
|
|
27
|
+
If a summary for today already exists, it updates the existing document.
|
|
28
|
+
`,
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const blueprint: AppCapabilities.BlueprintDefinition = {
|
|
33
|
+
key: BLUEPRINT_KEY,
|
|
34
|
+
make,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default blueprint;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
|
|
7
|
+
import { AiService } from '@dxos/ai';
|
|
8
|
+
import { Database } from '@dxos/echo';
|
|
9
|
+
import { Operation } from '@dxos/operation';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Generates a daily summary of recently modified objects using AI.
|
|
13
|
+
* Queries the database for objects updated within a time window,
|
|
14
|
+
* sends them to an LLM for summarization, and creates/updates
|
|
15
|
+
* a Markdown document in a "Summaries" collection.
|
|
16
|
+
*/
|
|
17
|
+
export const GenerateSummary = Operation.make({
|
|
18
|
+
meta: {
|
|
19
|
+
key: 'org.dxos.function.daily-summary.generate',
|
|
20
|
+
name: 'Generate Daily Summary',
|
|
21
|
+
description: 'Queries objects modified in the last day and generates an AI-summarized markdown document.',
|
|
22
|
+
},
|
|
23
|
+
input: Schema.Struct({
|
|
24
|
+
previousSummary: Schema.optional(Schema.String).annotations({
|
|
25
|
+
description: 'The previous day summary text for context continuity.',
|
|
26
|
+
}),
|
|
27
|
+
lookbackHours: Schema.optional(Schema.Number).annotations({
|
|
28
|
+
description: 'Number of hours to look back for modified objects. Defaults to 24.',
|
|
29
|
+
}),
|
|
30
|
+
}),
|
|
31
|
+
output: Schema.Struct({
|
|
32
|
+
id: Schema.String.annotations({
|
|
33
|
+
description: 'DXN of the created or updated markdown document.',
|
|
34
|
+
}),
|
|
35
|
+
objectCount: Schema.Number.annotations({
|
|
36
|
+
description: 'Number of objects included in the summary.',
|
|
37
|
+
}),
|
|
38
|
+
date: Schema.String.annotations({
|
|
39
|
+
description: 'Date label for the summary (e.g., "March 20").',
|
|
40
|
+
}),
|
|
41
|
+
}),
|
|
42
|
+
services: [Database.Service, AiService.AiService],
|
|
43
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from '@effect/vitest';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
|
|
9
|
+
import { AssistantTestLayer } from '@dxos/assistant/testing';
|
|
10
|
+
import { Blueprint } from '@dxos/blueprints';
|
|
11
|
+
import { SpaceProperties } from '@dxos/client-protocol';
|
|
12
|
+
import { Collection, DXN, Database, Obj, Query, Ref, Type } from '@dxos/echo';
|
|
13
|
+
import { TestHelpers } from '@dxos/effect/testing';
|
|
14
|
+
import { FunctionInvocationService } from '@dxos/functions';
|
|
15
|
+
import { ObjectId } from '@dxos/keys';
|
|
16
|
+
import { Text } from '@dxos/schema';
|
|
17
|
+
import { HasSubject } from '@dxos/types';
|
|
18
|
+
|
|
19
|
+
import { DailySummaryHandlers } from '../daily-summary-blueprint';
|
|
20
|
+
|
|
21
|
+
import { GenerateSummary } from './definitions';
|
|
22
|
+
|
|
23
|
+
ObjectId.dangerouslyDisableRandomness();
|
|
24
|
+
|
|
25
|
+
const MarkdownDocument = Schema.Struct({
|
|
26
|
+
name: Schema.optional(Schema.String),
|
|
27
|
+
content: Ref.Ref(Text.Text),
|
|
28
|
+
}).pipe(Type.object({ typename: 'org.dxos.type.document', version: '0.1.0' }));
|
|
29
|
+
|
|
30
|
+
const WithProperties = <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R | Database.Service> =>
|
|
31
|
+
Effect.zipRight(
|
|
32
|
+
Effect.gen(function* () {
|
|
33
|
+
yield* Database.add(
|
|
34
|
+
Obj.make(SpaceProperties, {
|
|
35
|
+
[Collection.Collection.typename]: Ref.make(Collection.make()),
|
|
36
|
+
}) as any,
|
|
37
|
+
);
|
|
38
|
+
}),
|
|
39
|
+
effect,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const TestLayer = AssistantTestLayer({
|
|
43
|
+
operationHandlers: DailySummaryHandlers,
|
|
44
|
+
types: [SpaceProperties, Collection.Collection, Blueprint.Blueprint, MarkdownDocument, HasSubject.HasSubject],
|
|
45
|
+
tracing: 'pretty',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('GenerateSummary', () => {
|
|
49
|
+
it.effect(
|
|
50
|
+
'creates a Markdown document with summary content',
|
|
51
|
+
Effect.fnUntraced(
|
|
52
|
+
function* (_) {
|
|
53
|
+
const result = yield* FunctionInvocationService.invokeFunction(GenerateSummary, {
|
|
54
|
+
lookbackHours: 24,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(result.id).toBeTruthy();
|
|
58
|
+
expect(result.date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
|
59
|
+
expect(result.objectCount).toBeGreaterThanOrEqual(0);
|
|
60
|
+
|
|
61
|
+
const doc = yield* Database.resolve(DXN.parse(result.id), MarkdownDocument);
|
|
62
|
+
expect(doc.name).toContain('Daily Summary');
|
|
63
|
+
const text = yield* Database.load(doc.content);
|
|
64
|
+
expect(text.content).toContain('Daily Summary');
|
|
65
|
+
expect(text.content).toContain('objects were modified today');
|
|
66
|
+
},
|
|
67
|
+
WithProperties,
|
|
68
|
+
Effect.provide(TestLayer),
|
|
69
|
+
TestHelpers.provideTestContext,
|
|
70
|
+
),
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
it.effect(
|
|
74
|
+
'creates a "Summaries" collection if none exists',
|
|
75
|
+
Effect.fnUntraced(
|
|
76
|
+
function* (_) {
|
|
77
|
+
yield* FunctionInvocationService.invokeFunction(GenerateSummary, {});
|
|
78
|
+
|
|
79
|
+
const collections = yield* Database.runQuery(Query.type(Collection.Collection, { name: 'Summaries' }));
|
|
80
|
+
expect(collections.length).toBe(1);
|
|
81
|
+
expect(collections[0].objects.length).toBe(1);
|
|
82
|
+
},
|
|
83
|
+
WithProperties,
|
|
84
|
+
Effect.provide(TestLayer),
|
|
85
|
+
TestHelpers.provideTestContext,
|
|
86
|
+
),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
it.effect(
|
|
90
|
+
'reuses existing "Summaries" collection on subsequent calls',
|
|
91
|
+
Effect.fnUntraced(
|
|
92
|
+
function* (_) {
|
|
93
|
+
yield* FunctionInvocationService.invokeFunction(GenerateSummary, {});
|
|
94
|
+
yield* FunctionInvocationService.invokeFunction(GenerateSummary, {});
|
|
95
|
+
|
|
96
|
+
const collections = yield* Database.runQuery(Query.type(Collection.Collection, { name: 'Summaries' }));
|
|
97
|
+
expect(collections.length).toBe(1);
|
|
98
|
+
expect(collections[0].objects.length).toBe(2);
|
|
99
|
+
},
|
|
100
|
+
WithProperties,
|
|
101
|
+
Effect.provide(TestLayer),
|
|
102
|
+
TestHelpers.provideTestContext,
|
|
103
|
+
),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
it.effect(
|
|
107
|
+
'includes previous summary in content when provided',
|
|
108
|
+
Effect.fnUntraced(
|
|
109
|
+
function* (_) {
|
|
110
|
+
const previousSummary = 'Yesterday was productive. 10 objects edited.';
|
|
111
|
+
const result = yield* FunctionInvocationService.invokeFunction(GenerateSummary, {
|
|
112
|
+
previousSummary,
|
|
113
|
+
lookbackHours: 24,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const doc = yield* Database.resolve(DXN.parse(result.id), MarkdownDocument);
|
|
117
|
+
const text = yield* Database.load(doc.content);
|
|
118
|
+
expect(text.content).toContain('Previous Summary');
|
|
119
|
+
expect(text.content).toContain(previousSummary);
|
|
120
|
+
},
|
|
121
|
+
WithProperties,
|
|
122
|
+
Effect.provide(TestLayer),
|
|
123
|
+
TestHelpers.provideTestContext,
|
|
124
|
+
),
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
it.effect(
|
|
128
|
+
'names document with today date',
|
|
129
|
+
Effect.fnUntraced(
|
|
130
|
+
function* (_) {
|
|
131
|
+
const result = yield* FunctionInvocationService.invokeFunction(GenerateSummary, {});
|
|
132
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
133
|
+
expect(result.date).toBe(today);
|
|
134
|
+
|
|
135
|
+
const doc = yield* Database.resolve(DXN.parse(result.id), MarkdownDocument);
|
|
136
|
+
expect(doc.name).toContain(today);
|
|
137
|
+
},
|
|
138
|
+
WithProperties,
|
|
139
|
+
Effect.provide(TestLayer),
|
|
140
|
+
TestHelpers.provideTestContext,
|
|
141
|
+
),
|
|
142
|
+
);
|
|
143
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as LanguageModel from '@effect/ai/LanguageModel';
|
|
6
|
+
import * as Prompt from '@effect/ai/Prompt';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
import * as Layer from 'effect/Layer';
|
|
9
|
+
import * as Schema from 'effect/Schema';
|
|
10
|
+
|
|
11
|
+
import { AiService, ToolExecutionService, ToolResolverService } from '@dxos/ai';
|
|
12
|
+
import { Collection, Database, Filter, Obj, Query, Ref, Type } from '@dxos/echo';
|
|
13
|
+
import { FunctionInvocationService, TracingService } from '@dxos/functions';
|
|
14
|
+
import { Operation } from '@dxos/operation';
|
|
15
|
+
import { Text } from '@dxos/schema';
|
|
16
|
+
import { CollectionModel } from '@dxos/schema';
|
|
17
|
+
import { trim } from '@dxos/util';
|
|
18
|
+
|
|
19
|
+
import { GenerateSummary } from './definitions';
|
|
20
|
+
|
|
21
|
+
const DEFAULT_LOOKBACK_HOURS = 24;
|
|
22
|
+
const SUMMARIES_COLLECTION_NAME = 'Summaries';
|
|
23
|
+
const SUMMARY_TITLE_PREFIX = 'Daily Summary —';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Predefined structure for the AI-generated daily summary.
|
|
27
|
+
* Used as the system prompt for the summarization model.
|
|
28
|
+
*/
|
|
29
|
+
export const SUMMARY_STRUCTURE = trim`
|
|
30
|
+
Given a list of recently modified objects,
|
|
31
|
+
generate a concise daily summary in markdown,
|
|
32
|
+
try to pull insights from the changes and the context of the changes.
|
|
33
|
+
Focus on the meaningful changes and try to avoid changes in the system objects.
|
|
34
|
+
|
|
35
|
+
Follow this exact structure:
|
|
36
|
+
|
|
37
|
+
## Highlights
|
|
38
|
+
Bullet points capturing the most significant changes or accomplishments.
|
|
39
|
+
|
|
40
|
+
## TODOs
|
|
41
|
+
List of TODOs that is inferred from the changes and the content of the changes.
|
|
42
|
+
Make it short, do not force it, only strong call to action data should be included.
|
|
43
|
+
Use - [ ] syntax for the TODOs.
|
|
44
|
+
|
|
45
|
+
Rules:
|
|
46
|
+
- Do NOT include the heading "Daily Summary" — the document already has a title.
|
|
47
|
+
- Be concise. Each bullet should be one sentence.
|
|
48
|
+
- If a previous summary is provided, note what changed since then.
|
|
49
|
+
- Try to pull actionable points from the changes and the context of the changes.
|
|
50
|
+
- Output raw markdown only, no wrapping code fences.
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const MarkdownDocument = Schema.Struct({
|
|
54
|
+
name: Schema.optional(Schema.String),
|
|
55
|
+
content: Ref.Ref(Text.Text),
|
|
56
|
+
}).pipe(Type.object({ typename: 'org.dxos.type.document', version: '0.1.0' }));
|
|
57
|
+
|
|
58
|
+
type MarkdownDoc = Schema.Schema.Type<typeof MarkdownDocument>;
|
|
59
|
+
|
|
60
|
+
const makeMarkdownDoc = ({ name, content }: { name: string; content: string }) => {
|
|
61
|
+
const doc = Obj.make(MarkdownDocument, { name, content: Ref.make(Text.make(content)) });
|
|
62
|
+
Obj.setParent(doc.content.target!, doc);
|
|
63
|
+
return doc;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export default GenerateSummary.pipe(
|
|
67
|
+
Operation.withHandler(
|
|
68
|
+
Effect.fnUntraced(
|
|
69
|
+
function* ({ previousSummary, lookbackHours }) {
|
|
70
|
+
const hours = lookbackHours ?? DEFAULT_LOOKBACK_HOURS;
|
|
71
|
+
const cutoff = Date.now() - hours * 60 * 60 * 1000;
|
|
72
|
+
const now = new Date();
|
|
73
|
+
const dateLabel = now.toLocaleDateString('en-US', { month: 'long', day: 'numeric' });
|
|
74
|
+
const docName = `${SUMMARY_TITLE_PREFIX} ${dateLabel}`;
|
|
75
|
+
|
|
76
|
+
const recentObjects = yield* Database.runQuery(Filter.updated({ after: cutoff }));
|
|
77
|
+
|
|
78
|
+
const objectDescriptions = recentObjects.map((obj) => {
|
|
79
|
+
return JSON.stringify(Obj.toJSON(obj));
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const content = yield* summarizeWithAi({
|
|
83
|
+
dateLabel,
|
|
84
|
+
objectDescriptions,
|
|
85
|
+
previousSummary,
|
|
86
|
+
hours,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const existingDoc = yield* findExistingDaySummary(docName);
|
|
90
|
+
|
|
91
|
+
let doc;
|
|
92
|
+
if (existingDoc) {
|
|
93
|
+
yield* updateDocContent(existingDoc, content);
|
|
94
|
+
doc = existingDoc;
|
|
95
|
+
} else {
|
|
96
|
+
doc = makeMarkdownDoc({ name: docName, content });
|
|
97
|
+
const summariesCollection = yield* findOrCreateSummariesCollection();
|
|
98
|
+
yield* CollectionModel.add({ object: doc, target: summariesCollection });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
id: Obj.getDXN(doc).toString(),
|
|
103
|
+
objectCount: recentObjects.length,
|
|
104
|
+
date: dateLabel,
|
|
105
|
+
};
|
|
106
|
+
},
|
|
107
|
+
Effect.provide(
|
|
108
|
+
Layer.mergeAll(
|
|
109
|
+
AiService.model('@anthropic/claude-haiku-4-5'),
|
|
110
|
+
ToolResolverService.layerEmpty,
|
|
111
|
+
ToolExecutionService.layerEmpty,
|
|
112
|
+
TracingService.layerNoop,
|
|
113
|
+
FunctionInvocationService.layerNotAvailable,
|
|
114
|
+
),
|
|
115
|
+
),
|
|
116
|
+
),
|
|
117
|
+
),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const summarizeWithAi = Effect.fn(function* ({
|
|
121
|
+
dateLabel,
|
|
122
|
+
objectDescriptions,
|
|
123
|
+
previousSummary,
|
|
124
|
+
hours,
|
|
125
|
+
}: {
|
|
126
|
+
dateLabel: string;
|
|
127
|
+
objectDescriptions: string[];
|
|
128
|
+
previousSummary?: string;
|
|
129
|
+
hours: number;
|
|
130
|
+
}) {
|
|
131
|
+
const userContent = [
|
|
132
|
+
`Date: ${dateLabel}`,
|
|
133
|
+
`Time window: last ${hours} hours`,
|
|
134
|
+
`Total objects modified: ${objectDescriptions.length}`,
|
|
135
|
+
'',
|
|
136
|
+
'Modified objects:',
|
|
137
|
+
...objectDescriptions,
|
|
138
|
+
...(previousSummary ? ['', 'Previous summary for context:', previousSummary] : []),
|
|
139
|
+
].join('\n');
|
|
140
|
+
|
|
141
|
+
const { text } = yield* LanguageModel.generateText({
|
|
142
|
+
prompt: Prompt.fromMessages([
|
|
143
|
+
Prompt.systemMessage({ content: SUMMARY_STRUCTURE }),
|
|
144
|
+
Prompt.userMessage({ content: [Prompt.makePart('text', { text: userContent })] }),
|
|
145
|
+
]),
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return text;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const findExistingDaySummary = Effect.fn(function* (docName: string) {
|
|
152
|
+
const docs = yield* Database.runQuery(Query.type(MarkdownDocument, { name: docName }));
|
|
153
|
+
return docs.length > 0 ? docs[0] : null;
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const updateDocContent = Effect.fn(function* (doc: MarkdownDoc, newContent: string) {
|
|
157
|
+
const textRef = doc.content;
|
|
158
|
+
if (Ref.isRef(textRef)) {
|
|
159
|
+
const text: Text.Text | undefined = yield* Effect.promise(() => textRef.load());
|
|
160
|
+
if (text) {
|
|
161
|
+
Obj.change(text, (mutable) => {
|
|
162
|
+
mutable.content = newContent;
|
|
163
|
+
});
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
Obj.change(doc, (mutable) => {
|
|
168
|
+
mutable.content = Ref.make(Text.make(newContent));
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const findOrCreateSummariesCollection = Effect.fn(function* () {
|
|
173
|
+
const collections = yield* Database.runQuery(Query.type(Collection.Collection, { name: SUMMARIES_COLLECTION_NAME }));
|
|
174
|
+
|
|
175
|
+
if (collections.length > 0) {
|
|
176
|
+
return collections[0];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const collection = Collection.make({ name: SUMMARIES_COLLECTION_NAME });
|
|
180
|
+
yield* CollectionModel.add({ object: collection });
|
|
181
|
+
return collection;
|
|
182
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capability } from '@dxos/app-framework';
|
|
8
|
+
import { AppCapabilities } from '@dxos/app-toolkit';
|
|
9
|
+
import { GraphBuilder, NodeMatcher } from '@dxos/plugin-graph';
|
|
10
|
+
import { meta as spaceMeta } from '@dxos/plugin-space';
|
|
11
|
+
|
|
12
|
+
import { meta } from '../../meta';
|
|
13
|
+
|
|
14
|
+
export default Capability.makeModule(
|
|
15
|
+
Effect.fnUntraced(function* () {
|
|
16
|
+
const extensions = yield* Effect.all([
|
|
17
|
+
GraphBuilder.createExtension({
|
|
18
|
+
id: `${meta.id}.space-settings-daily-summary`,
|
|
19
|
+
match: NodeMatcher.whenNodeType(`${spaceMeta.id}.settings`),
|
|
20
|
+
connector: (node) =>
|
|
21
|
+
Effect.succeed([
|
|
22
|
+
{
|
|
23
|
+
id: `${meta.id}.daily-summary`,
|
|
24
|
+
type: `${meta.id}.space-settings-daily-summary`,
|
|
25
|
+
data: `${meta.id}.space-settings-daily-summary`,
|
|
26
|
+
properties: {
|
|
27
|
+
label: ['plugin name', { ns: meta.id }],
|
|
28
|
+
icon: 'ph--calendar-check--regular',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
]),
|
|
32
|
+
}),
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
return Capability.contributes(AppCapabilities.AppGraphBuilder, extensions);
|
|
36
|
+
}),
|
|
37
|
+
);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capability } from '@dxos/app-framework';
|
|
8
|
+
import { AppCapabilities } from '@dxos/app-toolkit';
|
|
9
|
+
|
|
10
|
+
import { DailySummaryBlueprint } from '../../blueprints';
|
|
11
|
+
|
|
12
|
+
const blueprintDefinition = Capability.makeModule<
|
|
13
|
+
[],
|
|
14
|
+
Capability.Capability<typeof AppCapabilities.BlueprintDefinition>[]
|
|
15
|
+
>(() => Effect.succeed([Capability.contributes(AppCapabilities.BlueprintDefinition, DailySummaryBlueprint)]));
|
|
16
|
+
|
|
17
|
+
export default blueprintDefinition;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
9
|
+
import { Surface } from '@dxos/app-framework/ui';
|
|
10
|
+
import { useActiveSpace } from '@dxos/plugin-space';
|
|
11
|
+
|
|
12
|
+
import { DailySummarySettings } from '../../containers';
|
|
13
|
+
import { meta } from '../../meta';
|
|
14
|
+
|
|
15
|
+
export default Capability.makeModule(() =>
|
|
16
|
+
Effect.succeed(
|
|
17
|
+
Capability.contributes(Capabilities.ReactSurface, [
|
|
18
|
+
Surface.create({
|
|
19
|
+
id: `${meta.id}.space-settings-daily-summary`,
|
|
20
|
+
role: 'article',
|
|
21
|
+
filter: (data): data is { subject: string } => data.subject === `${meta.id}.space-settings-daily-summary`,
|
|
22
|
+
component: () => {
|
|
23
|
+
const space = useActiveSpace();
|
|
24
|
+
if (!space) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return <DailySummarySettings space={space} />;
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
]),
|
|
32
|
+
),
|
|
33
|
+
);
|