@dxos/assistant 0.14.0 → 0.16.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/dist/lib/ExecutionGraph.mjs.map +1 -1
- package/dist/lib/chunk-Agent.mjs +202 -0
- package/dist/lib/chunk-Agent.mjs.map +1 -0
- package/dist/lib/chunk-AiContext.mjs +462 -0
- package/dist/lib/chunk-AiContext.mjs.map +1 -0
- package/dist/lib/chunk-AiRequest.mjs.map +1 -1
- package/dist/lib/chunk-AiSession.mjs +2 -2
- package/dist/lib/chunk-AiSession.mjs.map +1 -1
- package/dist/lib/chunk-Alarm.mjs +37 -0
- package/dist/lib/chunk-Alarm.mjs.map +1 -0
- package/dist/lib/chunk-Chat.mjs +258 -0
- package/dist/lib/chunk-Chat.mjs.map +1 -0
- package/dist/lib/chunk-Harness.mjs +4 -4
- package/dist/lib/chunk-Harness.mjs.map +1 -1
- package/dist/lib/chunk-SessionLink.mjs +20 -0
- package/dist/lib/chunk-SessionLink.mjs.map +1 -0
- package/dist/lib/chunk-SkillHooks.mjs +47 -0
- package/dist/lib/chunk-SkillHooks.mjs.map +1 -0
- package/dist/lib/extraction.mjs.map +1 -1
- package/dist/lib/index.mjs +6 -6
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/request/AiRequest.mjs +1 -2
- package/dist/lib/session/AiContext.mjs +1 -462
- package/dist/lib/session/AiSession.mjs +1 -2
- package/dist/lib/session/Alarm.mjs +1 -37
- package/dist/lib/session/Harness.mjs +1 -2
- package/dist/lib/session/SessionLink.mjs +1 -20
- package/dist/lib/session/SkillHooks.mjs +1 -47
- package/dist/lib/types/Agent.mjs +1 -202
- package/dist/lib/types/Chat.mjs +1 -258
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -23
- package/src/request/AiRequest.test.ts +1 -1
- package/src/types/Agent.ts +1 -1
- package/dist/lib/session/AiContext.mjs.map +0 -1
- package/dist/lib/session/Alarm.mjs.map +0 -1
- package/dist/lib/session/SessionLink.mjs.map +0 -1
- package/dist/lib/session/SkillHooks.mjs.map +0 -1
- package/dist/lib/types/Agent.mjs.map +0 -1
- package/dist/lib/types/Chat.mjs.map +0 -1
|
@@ -1,47 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import * as Effect from "effect/Effect";
|
|
3
|
-
import * as Operation from "@dxos/compute/Operation";
|
|
4
|
-
import { Database, Obj } from "@dxos/echo";
|
|
5
|
-
import { invariant } from "@dxos/invariant";
|
|
6
|
-
import { log } from "@dxos/log";
|
|
7
|
-
//#region src/session/SkillHooks.ts
|
|
8
|
-
var SkillHooks_exports = /* @__PURE__ */ __exportAll({ runHooks: () => runHooks });
|
|
9
|
-
var __dxlog_file = "/__w/dxos/dxos/packages/core/compute/assistant/src/session/SkillHooks.ts";
|
|
10
|
-
/**
|
|
11
|
-
* Runs every {@link Skill.Hook} on `skills` whose spec matches `phase`. Each hook resolves its
|
|
12
|
-
* `function` ref to a {@link Operation.PersistentOperation}, deserializes it to a definition, and
|
|
13
|
-
* invokes it with the templated `input` via `invoke`. Hooks without a function ref are skipped.
|
|
14
|
-
*
|
|
15
|
-
* Hook failures are isolated: a throwing hook logs and is swallowed so one skill's hook cannot
|
|
16
|
-
* abort the request lifecycle (or the sibling hooks).
|
|
17
|
-
*/
|
|
18
|
-
var runHooks = ({ skills, phase, invoke }) => Effect.gen(function* () {
|
|
19
|
-
for (const skill of skills) for (const hook of skill.hooks ?? []) {
|
|
20
|
-
if (hook.spec._tag !== phase) continue;
|
|
21
|
-
if (!hook.function) continue;
|
|
22
|
-
yield* Effect.gen(function* () {
|
|
23
|
-
const record = yield* Database.load(hook.function);
|
|
24
|
-
invariant(Obj.instanceOf(Operation.PersistentOperation, record), "Hook function must be an operation.", {
|
|
25
|
-
"~LogMeta": "~LogMeta",
|
|
26
|
-
F: __dxlog_file,
|
|
27
|
-
L: 61,
|
|
28
|
-
S: this,
|
|
29
|
-
A: ["Obj.instanceOf(Operation.PersistentOperation, record)", "'Hook function must be an operation.'"]
|
|
30
|
-
});
|
|
31
|
-
yield* invoke(Operation.deserialize(record), { ...hook.input }, hook);
|
|
32
|
-
}).pipe(Effect.catchCause((cause) => Effect.sync(() => log.warn("skill hook failed", {
|
|
33
|
-
phase,
|
|
34
|
-
skill: Obj.getMeta(skill).key,
|
|
35
|
-
cause
|
|
36
|
-
}, {
|
|
37
|
-
"~LogMeta": "~LogMeta",
|
|
38
|
-
F: __dxlog_file,
|
|
39
|
-
L: 70,
|
|
40
|
-
S: this
|
|
41
|
-
}))));
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
//#endregion
|
|
45
|
-
export { runHooks, SkillHooks_exports as t };
|
|
46
|
-
|
|
47
|
-
//# sourceMappingURL=SkillHooks.mjs.map
|
|
1
|
+
export { n as runHooks } from "../chunk-SkillHooks.mjs";
|
package/dist/lib/types/Agent.mjs
CHANGED
|
@@ -1,202 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { r as HarnessContextError } from "../chunk-errors.mjs";
|
|
3
|
-
import { Chat, linkCompanion, make } from "./Chat.mjs";
|
|
4
|
-
import * as Effect from "effect/Effect";
|
|
5
|
-
import * as Schema from "effect/Schema";
|
|
6
|
-
import { Annotation, DXN, Database, Feed, Filter, Obj, Ref, Type } from "@dxos/echo";
|
|
7
|
-
import { IdentityDid } from "@dxos/keys";
|
|
8
|
-
import * as Skill from "@dxos/compute/Skill";
|
|
9
|
-
import * as EffectEx from "@dxos/effect/EffectEx";
|
|
10
|
-
import * as Instructions from "@dxos/compute/Instructions";
|
|
11
|
-
//#region src/types/Agent.ts
|
|
12
|
-
var Agent_exports = /* @__PURE__ */ __exportAll({
|
|
13
|
-
Agent: () => Agent,
|
|
14
|
-
chatLocation: () => chatLocation,
|
|
15
|
-
getFromChatContext: () => getFromChatContext,
|
|
16
|
-
isAgentChat: () => isAgentChat,
|
|
17
|
-
loadChat: () => loadChat,
|
|
18
|
-
loadForChat: () => loadForChat,
|
|
19
|
-
loadInstructions: () => loadInstructions,
|
|
20
|
-
makeInitialized: () => makeInitialized,
|
|
21
|
-
resetChatHistory: () => resetChatHistory
|
|
22
|
-
});
|
|
23
|
-
/**
|
|
24
|
-
* An agent identity: a personality (attribution DID) plus its preset payload (instructions with
|
|
25
|
-
* text, skills, objects, and commands). Owns no conversation state — a chat is linked to the agent
|
|
26
|
-
* it runs as by the ECHO parent edge (the agent parents its chats), and durable work products live on a Project
|
|
27
|
-
* (plugin-projects DESIGN.md, "Agent ↔ Project convergence").
|
|
28
|
-
*/
|
|
29
|
-
/**
|
|
30
|
-
* The AI session runtime (MCP SDK, Anthropic client, ~280 KB) is loaded lazily: this module holds
|
|
31
|
-
* the Agent *schema*, which core plugins reference for operation definitions, so the runtime loads
|
|
32
|
-
* only when an agent operation actually runs.
|
|
33
|
-
*/
|
|
34
|
-
var aiContextRuntime = () => import("../session/AiContext.mjs");
|
|
35
|
-
var harnessRuntime = () => import("../session/Harness.mjs");
|
|
36
|
-
var Agent = class extends Type.makeObject(DXN.make("org.dxos.type.agent", "0.2.0"))(Schema.Struct({
|
|
37
|
-
name: Schema.optional(Schema.String),
|
|
38
|
-
/**
|
|
39
|
-
* Stable identity DID for the agent, used to attribute content it authors (e.g. suggestion
|
|
40
|
-
* branches) as its own author — distinct from any human or other agent. Provisioned
|
|
41
|
-
* externally, never minted by {@link makeInitialized} (see the note there); the slot a real
|
|
42
|
-
* HALO identity DID takes once agents get first-class identities. Optional because nothing
|
|
43
|
-
* populates it yet.
|
|
44
|
-
*/
|
|
45
|
-
did: Schema.optional(IdentityDid.annotate({
|
|
46
|
-
title: "DID",
|
|
47
|
-
description: "The agent's identity DID; attributes content the agent authors."
|
|
48
|
-
})).annotate({
|
|
49
|
-
title: "DID",
|
|
50
|
-
description: "The agent's DID."
|
|
51
|
-
}),
|
|
52
|
-
/**
|
|
53
|
-
* Master switch for the agent's automation (propagated onto its compiled routine triggers).
|
|
54
|
-
*/
|
|
55
|
-
enabled: Schema.optional(Schema.Boolean).annotate({
|
|
56
|
-
title: "Enabled",
|
|
57
|
-
description: "Master switch for agent automation; propagated to all triggers on sync."
|
|
58
|
-
}),
|
|
59
|
-
/**
|
|
60
|
-
* Instructions for the agent — the preset payload (text, skills, objects, commands) a chat
|
|
61
|
-
* receives when the agent is applied to it. Owned: `SetParent` cascades it with the agent.
|
|
62
|
-
*/
|
|
63
|
-
instructions: Ref.Ref(Instructions.Instructions).pipe(Annotation.SetParent.set(), Annotation.FormInlineAnnotation.set(true), Schema.annotate({ title: "Instructions" }))
|
|
64
|
-
}).pipe(Annotation.LabelAnnotation.set(["name"]), Annotation.IconAnnotation.set({
|
|
65
|
-
icon: "ph--drone--regular",
|
|
66
|
-
hue: "amber"
|
|
67
|
-
}), Annotation.UserType.set())) {};
|
|
68
|
-
/**
|
|
69
|
-
* Resolves the agent's instructions and their markdown text.
|
|
70
|
-
*/
|
|
71
|
-
var loadInstructions = (agent) => Effect.gen(function* () {
|
|
72
|
-
const instructions = yield* Database.load(agent.instructions);
|
|
73
|
-
return {
|
|
74
|
-
text: yield* Database.load(instructions.text).pipe(Effect.map((doc) => doc.content), Effect.catchTag("EntityNotFoundError", () => Effect.succeed(""))),
|
|
75
|
-
instructions
|
|
76
|
-
};
|
|
77
|
-
});
|
|
78
|
-
/**
|
|
79
|
-
* Resolves the agent's primary chat (the agent holds no chat ref): the latest chat parented to the
|
|
80
|
-
* agent. Entity ids are ULIDs, so the id order is creation order — `resetChatHistory` makes the
|
|
81
|
-
* replacement chat primary simply by creating it later.
|
|
82
|
-
*/
|
|
83
|
-
var loadChat = (agent) => Effect.gen(function* () {
|
|
84
|
-
return (yield* Database.query(Filter.and(Filter.type(Chat), Filter.childOf(agent))).run).filter((chat) => Obj.getMeta(chat).keys.length === 0).sort((left, right) => left.id.localeCompare(right.id)).at(-1);
|
|
85
|
-
}).pipe(Effect.orDie);
|
|
86
|
-
/**
|
|
87
|
-
* Resolves the agent a chat runs as, if any. Plain (agentless) chats yield `undefined`.
|
|
88
|
-
* The parent is loaded with the object, so this is synchronous — no query.
|
|
89
|
-
*/
|
|
90
|
-
var loadForChat = (chat) => Effect.sync(() => {
|
|
91
|
-
const parent = Obj.getParent(chat);
|
|
92
|
-
return parent && Obj.instanceOf(Agent, parent) ? parent : void 0;
|
|
93
|
-
});
|
|
94
|
-
/**
|
|
95
|
-
* Where a chat's turns run: as its `remote` flag says, else on EDGE for an agent's chat, since its chats share
|
|
96
|
-
* the agent's one brain, which only EDGE holds. An explicit `remote: false` keeps an agent's chats local, for
|
|
97
|
-
* a runtime with no EDGE to reach (a story or test), where every chat shares the client's brain instead.
|
|
98
|
-
*/
|
|
99
|
-
var chatLocation = (chat) => chat.remote ?? isAgentChat(chat) ? "edge" : "local";
|
|
100
|
-
/** Whether the chat runs as an agent (the agent parents it); synchronous, like {@link loadForChat}. */
|
|
101
|
-
var isAgentChat = (chat) => {
|
|
102
|
-
const parent = Obj.getParent(chat);
|
|
103
|
-
return parent !== void 0 && Obj.instanceOf(Agent, parent);
|
|
104
|
-
};
|
|
105
|
-
/**
|
|
106
|
-
* Creates a fully initialized Agent with its first chat and context bindings.
|
|
107
|
-
*
|
|
108
|
-
* @param props - Agent properties including spec, skills, and context objects.
|
|
109
|
-
* @param skill - The skill to use for the agent context: a skill object is cloned into the space, a ref
|
|
110
|
-
* (e.g. a registry URI) is bound as-is so the agent follows the compiled skill until customized.
|
|
111
|
-
* @returns An Effect that yields the initialized Agent.
|
|
112
|
-
*/
|
|
113
|
-
var makeInitialized = (props, skill) => Effect.gen(function* () {
|
|
114
|
-
const { skills: propsSkills, contextObjects, ...agentProps } = props;
|
|
115
|
-
const AiContextRuntime = yield* Effect.promise(aiContextRuntime);
|
|
116
|
-
const persistedPropsSkills = yield* Effect.all((propsSkills ?? []).map((ref) => {
|
|
117
|
-
const target = ref.peek();
|
|
118
|
-
return target !== void 0 ? Database.add(target).pipe(Effect.map((persisted) => Ref.make(persisted))) : Effect.succeed(ref);
|
|
119
|
-
}));
|
|
120
|
-
const instructions = yield* Database.add(Instructions.make({
|
|
121
|
-
text: props.instructions,
|
|
122
|
-
skills: persistedPropsSkills
|
|
123
|
-
}));
|
|
124
|
-
const agent = yield* Database.add(Obj.make(Agent, {
|
|
125
|
-
...agentProps,
|
|
126
|
-
instructions: Ref.make(instructions),
|
|
127
|
-
enabled: props.enabled ?? true
|
|
128
|
-
}));
|
|
129
|
-
const feed = yield* Database.add(Feed.make());
|
|
130
|
-
const runtime = yield* Effect.context();
|
|
131
|
-
const contextBinder = yield* EffectEx.acquireReleaseResource(() => new AiContextRuntime.Binder({
|
|
132
|
-
feed,
|
|
133
|
-
runtime
|
|
134
|
-
}));
|
|
135
|
-
const agentSkill = Ref.isRef(skill) ? skill : Ref.make(yield* Database.add(Obj.clone(skill, { deep: "all" })));
|
|
136
|
-
const chat = yield* Database.add(make({
|
|
137
|
-
feed: Ref.make(feed),
|
|
138
|
-
instructions: Ref.make(instructions)
|
|
139
|
-
}));
|
|
140
|
-
linkCompanion({
|
|
141
|
-
chat,
|
|
142
|
-
subject: agent
|
|
143
|
-
});
|
|
144
|
-
yield* Effect.promise(() => contextBinder.bind({
|
|
145
|
-
skills: [agentSkill, ...persistedPropsSkills],
|
|
146
|
-
objects: [
|
|
147
|
-
Ref.make(agent),
|
|
148
|
-
Ref.make(chat),
|
|
149
|
-
...contextObjects ?? []
|
|
150
|
-
]
|
|
151
|
-
}));
|
|
152
|
-
return agent;
|
|
153
|
-
}).pipe(Effect.scoped);
|
|
154
|
-
/**
|
|
155
|
-
* Resets the agent's chat history by rebuilding the chat context.
|
|
156
|
-
* Preserves the existing skills and objects from the current chat context.
|
|
157
|
-
*
|
|
158
|
-
* @param agent - The agent whose chat history should be reset. Must have an existing chat.
|
|
159
|
-
* @returns An Effect that resets the chat history.
|
|
160
|
-
*/
|
|
161
|
-
var resetChatHistory = (agent) => Effect.gen(function* () {
|
|
162
|
-
const existingChat = yield* loadChat(agent);
|
|
163
|
-
if (!existingChat) return yield* Effect.die(/* @__PURE__ */ new Error("Agent must have an existing chat to reset."));
|
|
164
|
-
const existingFeed = yield* Database.load(existingChat.feed);
|
|
165
|
-
const runtime = yield* Effect.context();
|
|
166
|
-
const AiContextRuntime = yield* Effect.promise(aiContextRuntime);
|
|
167
|
-
const existingContextBinder = yield* EffectEx.acquireReleaseResource(() => new AiContextRuntime.Binder({
|
|
168
|
-
feed: existingFeed,
|
|
169
|
-
runtime
|
|
170
|
-
}));
|
|
171
|
-
const skills = existingContextBinder.getSkills().map(Skill.makeRef);
|
|
172
|
-
const objects = existingContextBinder.getObjects().filter((object) => !Obj.instanceOf(Chat, object)).map((object) => Ref.make(object));
|
|
173
|
-
const feed = yield* Database.add(Feed.make());
|
|
174
|
-
const contextBinder = yield* EffectEx.acquireReleaseResource(() => new AiContextRuntime.Binder({
|
|
175
|
-
feed,
|
|
176
|
-
runtime
|
|
177
|
-
}));
|
|
178
|
-
const chat = yield* Database.add(make({
|
|
179
|
-
feed: Ref.make(feed),
|
|
180
|
-
instructions: agent.instructions
|
|
181
|
-
}));
|
|
182
|
-
linkCompanion({
|
|
183
|
-
chat,
|
|
184
|
-
subject: agent
|
|
185
|
-
});
|
|
186
|
-
yield* Effect.promise(() => contextBinder.bind({
|
|
187
|
-
skills,
|
|
188
|
-
objects: [...objects, Ref.make(chat)]
|
|
189
|
-
}));
|
|
190
|
-
}).pipe(Effect.scoped);
|
|
191
|
-
var getFromChatContext = Effect.gen(function* () {
|
|
192
|
-
const agents = yield* (yield* Effect.promise(harnessRuntime)).queryContext(Filter.type(Agent));
|
|
193
|
-
if (agents.length !== 1) return yield* Effect.fail(new HarnessContextError({
|
|
194
|
-
type: "agent",
|
|
195
|
-
count: agents.length
|
|
196
|
-
}));
|
|
197
|
-
return agents[0];
|
|
198
|
-
});
|
|
199
|
-
//#endregion
|
|
200
|
-
export { Agent, chatLocation, getFromChatContext, isAgentChat, loadChat, loadForChat, loadInstructions, makeInitialized, resetChatHistory, Agent_exports as t };
|
|
201
|
-
|
|
202
|
-
//# sourceMappingURL=Agent.mjs.map
|
|
1
|
+
export { t as Agent, r as chatLocation, i as getFromChatContext, a as isAgentChat, o as loadChat, s as loadForChat, c as loadInstructions, l as makeInitialized, u as resetChatHistory } from "../chunk-Agent.mjs";
|
package/dist/lib/types/Chat.mjs
CHANGED
|
@@ -1,258 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import * as Effect from "effect/Effect";
|
|
3
|
-
import * as Option from "effect/Option";
|
|
4
|
-
import * as Schema from "effect/Schema";
|
|
5
|
-
import { SessionConfig } from "@dxos/ai";
|
|
6
|
-
import { Annotation, DXN, Database, Feed, Filter, Obj, Ref, Type } from "@dxos/echo";
|
|
7
|
-
import { log } from "@dxos/log";
|
|
8
|
-
import { Task } from "@dxos/types";
|
|
9
|
-
import * as Cause from "effect/Cause";
|
|
10
|
-
import * as Instructions from "@dxos/compute/Instructions";
|
|
11
|
-
import * as Project from "@dxos/compute/Project";
|
|
12
|
-
import { ArchivableAnnotation } from "@dxos/schema";
|
|
13
|
-
//#region src/types/Chat.ts
|
|
14
|
-
var Chat_exports = /* @__PURE__ */ __exportAll({
|
|
15
|
-
Chat: () => Chat,
|
|
16
|
-
CompanionChatAnnotation: () => CompanionChatAnnotation,
|
|
17
|
-
addTask: () => addTask,
|
|
18
|
-
assignTasks: () => assignTasks,
|
|
19
|
-
deleteTask: () => deleteTask,
|
|
20
|
-
feedEntityId: () => feedEntityId,
|
|
21
|
-
fields: () => fields,
|
|
22
|
-
formatChecklist: () => formatChecklist,
|
|
23
|
-
isOpenTask: () => isOpenTask,
|
|
24
|
-
linkCompanion: () => linkCompanion,
|
|
25
|
-
loadForFeed: () => loadForFeed,
|
|
26
|
-
loadTasks: () => loadTasks,
|
|
27
|
-
make: () => make,
|
|
28
|
-
peekProject: () => peekProject,
|
|
29
|
-
resolveTasks: () => resolveTasks,
|
|
30
|
-
seedSession: () => seedSession,
|
|
31
|
-
unassignTasks: () => unassignTasks
|
|
32
|
-
});
|
|
33
|
-
var __dxlog_file = "/__w/dxos/dxos/packages/core/compute/assistant/src/types/Chat.ts";
|
|
34
|
-
/**
|
|
35
|
-
* AI chat session.
|
|
36
|
-
*/
|
|
37
|
-
var Chat = class extends Type.makeObject(DXN.make("org.dxos.type.assistant.chat", "0.2.0"))(Schema.Struct({
|
|
38
|
-
name: Schema.String.pipe(Schema.optional),
|
|
39
|
-
viewType: Schema.String.pipe(Schema.optional),
|
|
40
|
-
/**
|
|
41
|
-
* Runs this conversation's agent on the edge rather than locally, mirroring a trigger's own
|
|
42
|
-
* `remote` flag. When unset, the agent runs locally on the client.
|
|
43
|
-
*/
|
|
44
|
-
remote: Schema.Boolean.pipe(Schema.annotate({ title: "Remote" }), Schema.optional),
|
|
45
|
-
/** Message feed, owned by the chat so `SetParent` cascades it. */
|
|
46
|
-
feed: Ref.Ref(Feed.Feed).pipe(Annotation.SetParent.set(), Annotation.FormInputAnnotation.set(false)),
|
|
47
|
-
/**
|
|
48
|
-
* Instructions steering this conversation, rendered into the system prompt at request time.
|
|
49
|
-
* Held by reference (never copied), so a project's chats follow edits to its instructions.
|
|
50
|
-
*/
|
|
51
|
-
instructions: Schema.optional(Ref.Ref(Instructions.Instructions).pipe(Annotation.FormInputAnnotation.set(false))),
|
|
52
|
-
/**
|
|
53
|
-
* How this conversation runs (its model), chosen in the chat rather than globally so it survives
|
|
54
|
-
* a remount and travels with the chat. Unset fields fall back to the project's, then the agent's.
|
|
55
|
-
*/
|
|
56
|
-
session: Schema.optional(SessionConfig.SessionConfig.pipe(Annotation.FormInputAnnotation.set(false))),
|
|
57
|
-
/**
|
|
58
|
-
* The working checklist, flat and ordered. Deliberately NOT an owning (`SetParent`) field: a
|
|
59
|
-
* chat may work on a task that belongs somewhere else — a project's task set delegates one here
|
|
60
|
-
* — and an owning field re-parents every resolved member on each update of the chat, which
|
|
61
|
-
* would silently move that task out of the set that owns it. Tasks the chat itself creates are
|
|
62
|
-
* parented to it explicitly; see {@link addTask}.
|
|
63
|
-
*/
|
|
64
|
-
tasks: Schema.Array(Ref.Ref(Task.Task)).pipe(Annotation.FormInputAnnotation.set(false))
|
|
65
|
-
}).pipe(Annotation.LabelAnnotation.set(["name"]), Annotation.IconAnnotation.set({
|
|
66
|
-
icon: "ph--sparkle--regular",
|
|
67
|
-
hue: "amber"
|
|
68
|
-
}), Annotation.UserType.set(), ArchivableAnnotation.set(true))) {};
|
|
69
|
-
/** Module-level alias so callers importing the namespace avoid the doubled `Chat.Chat.fields`. */
|
|
70
|
-
var fields = Chat.fields;
|
|
71
|
-
var make = (props) => Obj.make(Chat, {
|
|
72
|
-
...props,
|
|
73
|
-
tasks: props.tasks ?? []
|
|
74
|
-
});
|
|
75
|
-
/**
|
|
76
|
-
* Refs to the chats accompanying an object, stored on the subject itself (replaces the former
|
|
77
|
-
* `CompanionTo` relation): the annotation is deleted with the subject, and its refs make the
|
|
78
|
-
* parented chats real children — referenced and cascaded — rather than ref-less parent-edge
|
|
79
|
-
* orphans reachable only by index query.
|
|
80
|
-
*/
|
|
81
|
-
var CompanionChatAnnotation = Annotation.make({
|
|
82
|
-
id: "org.dxos.assistant.companionChats",
|
|
83
|
-
schema: Schema.Array(Ref.Ref(Chat))
|
|
84
|
-
});
|
|
85
|
-
/**
|
|
86
|
-
* Links a chat to the subject it accompanies (a Project, an Agent, a document, ...): a ref on the
|
|
87
|
-
* subject via {@link CompanionChatAnnotation} plus the ECHO parent edge. Idempotent per chat.
|
|
88
|
-
*/
|
|
89
|
-
var linkCompanion = ({ chat, subject }) => {
|
|
90
|
-
const existing = Annotation.get(subject, CompanionChatAnnotation);
|
|
91
|
-
if (!existing.pipe(Option.getOrElse(() => [])).some((ref) => ref.uri === Ref.make(chat).uri)) {
|
|
92
|
-
if (Option.isNone(existing)) Obj.update(subject, (subject) => {
|
|
93
|
-
Annotation.set(subject, CompanionChatAnnotation, [Ref.make(chat)]);
|
|
94
|
-
});
|
|
95
|
-
else Annotation.update(subject, CompanionChatAnnotation, (chats) => {
|
|
96
|
-
chats.push(Ref.make(chat));
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
Obj.setParent(chat, subject);
|
|
100
|
-
};
|
|
101
|
-
/**
|
|
102
|
-
* Starts a chat on a default session config — a project's, say — copied rather than referenced, so
|
|
103
|
-
* a model later picked in the chat is the chat's own. A chat already carrying a config keeps it.
|
|
104
|
-
*/
|
|
105
|
-
var seedSession = (chat, session) => {
|
|
106
|
-
if (!session || chat.session) return;
|
|
107
|
-
Obj.update(chat, (chat) => {
|
|
108
|
-
chat.session = { ...session };
|
|
109
|
-
});
|
|
110
|
-
};
|
|
111
|
-
/** Creates a task the chat owns and appends it to the checklist. */
|
|
112
|
-
var addTask = (db, chat, title, props = {}) => {
|
|
113
|
-
const task = db.add(Task.make({
|
|
114
|
-
title: title.trim(),
|
|
115
|
-
status: "todo",
|
|
116
|
-
...props,
|
|
117
|
-
[Obj.Parent]: chat
|
|
118
|
-
}));
|
|
119
|
-
Obj.update(chat, (chat) => {
|
|
120
|
-
chat.tasks.push(Ref.make(task));
|
|
121
|
-
});
|
|
122
|
-
return task;
|
|
123
|
-
};
|
|
124
|
-
/**
|
|
125
|
-
* Appends existing tasks to the checklist. Ownership is deliberately untouched — the task keeps the
|
|
126
|
-
* parent it arrived with (contrast {@link addTask}, which parents what the chat creates), so a task
|
|
127
|
-
* borrowed from a project's set stays in that set. Refs already on the checklist are skipped.
|
|
128
|
-
* Returns the refs actually appended.
|
|
129
|
-
*/
|
|
130
|
-
var assignTasks = (chat, tasks) => {
|
|
131
|
-
const added = [];
|
|
132
|
-
Obj.update(chat, (chat) => {
|
|
133
|
-
const present = new Set(chat.tasks.map((ref) => Task.refEntityId(ref)).filter((id) => id !== void 0));
|
|
134
|
-
for (const ref of tasks) {
|
|
135
|
-
const id = Task.refEntityId(ref);
|
|
136
|
-
if (id === void 0 || present.has(id)) continue;
|
|
137
|
-
present.add(id);
|
|
138
|
-
added.push(ref);
|
|
139
|
-
}
|
|
140
|
-
chat.tasks.push(...added);
|
|
141
|
-
});
|
|
142
|
-
return added;
|
|
143
|
-
};
|
|
144
|
-
/**
|
|
145
|
-
* Takes tasks off the checklist without destroying them — the inverse of {@link assignTasks}, and
|
|
146
|
-
* distinct from {@link deleteTask}, which also removes the members the chat owns. Returns the refs
|
|
147
|
-
* actually removed.
|
|
148
|
-
*/
|
|
149
|
-
var unassignTasks = (chat, tasks) => {
|
|
150
|
-
const ids = new Set(tasks.map((ref) => Task.refEntityId(ref)).filter((id) => id !== void 0));
|
|
151
|
-
const removed = chat.tasks.filter((ref) => {
|
|
152
|
-
const id = Task.refEntityId(ref);
|
|
153
|
-
return id !== void 0 && ids.has(id);
|
|
154
|
-
});
|
|
155
|
-
if (removed.length > 0) Obj.update(chat, (chat) => {
|
|
156
|
-
chat.tasks = chat.tasks.filter((ref) => {
|
|
157
|
-
const id = Task.refEntityId(ref);
|
|
158
|
-
return id === void 0 || !ids.has(id);
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
return [...removed];
|
|
162
|
-
};
|
|
163
|
-
/**
|
|
164
|
-
* Remove a task and its sub-tasks from `tasks`, the chat's checklist loaded in full (see
|
|
165
|
-
* {@link loadTasks}) — an unloaded child is invisible to the walk and would be left orphaned in
|
|
166
|
-
* the array. Returns everything dropped from the checklist.
|
|
167
|
-
*
|
|
168
|
-
* Only members the chat owns are destroyed: a delegated task belongs to the set that parents it,
|
|
169
|
-
* so taking it off this checklist must not delete it from there.
|
|
170
|
-
*/
|
|
171
|
-
var deleteTask = (db, chat, tasks, task) => {
|
|
172
|
-
const subtree = Task.subtree(tasks, task);
|
|
173
|
-
const ids = new Set(subtree.map((member) => member.id));
|
|
174
|
-
Obj.update(chat, (chat) => {
|
|
175
|
-
chat.tasks = chat.tasks.filter((ref) => {
|
|
176
|
-
const id = Task.refEntityId(ref);
|
|
177
|
-
return id === void 0 || !ids.has(id);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
for (const member of subtree) if (Obj.getParent(member)?.id === chat.id) db.remove(member);
|
|
181
|
-
return subtree;
|
|
182
|
-
};
|
|
183
|
-
/** The chat's feed entity id, readable without resolving the ref. */
|
|
184
|
-
var feedEntityId = (chat) => Task.refEntityId(chat.feed);
|
|
185
|
-
/**
|
|
186
|
-
* The chat that owns `feed`. The chat owns its feed (`SetParent`), so the parent edge answers this
|
|
187
|
-
* without a query; the scan is the fallback for a feed whose parent was never stamped.
|
|
188
|
-
*/
|
|
189
|
-
var loadForFeed = (feed) => Effect.gen(function* () {
|
|
190
|
-
const parent = Obj.getParent(feed);
|
|
191
|
-
if (parent && Obj.instanceOf(Chat, parent)) return parent;
|
|
192
|
-
return (yield* Database.query(Filter.type(Chat)).run).find((chat) => feedEntityId(chat) === feed.id);
|
|
193
|
-
}).pipe(Effect.catchCause((cause) => Effect.sync(() => {
|
|
194
|
-
log.warn("chat lookup failed; treating feed as chat-less", {
|
|
195
|
-
feed: feed.id,
|
|
196
|
-
cause: Cause.pretty(cause)
|
|
197
|
-
}, {
|
|
198
|
-
"~LogMeta": "~LogMeta",
|
|
199
|
-
F: __dxlog_file,
|
|
200
|
-
L: 238,
|
|
201
|
-
S: void 0
|
|
202
|
-
});
|
|
203
|
-
})));
|
|
204
|
-
/** Bound on the parent walk below; a conversation sits one or two edges under its project. */
|
|
205
|
-
var MAX_OWNER_DEPTH = 8;
|
|
206
|
-
/**
|
|
207
|
-
* The project a conversation belongs to, if any.
|
|
208
|
-
*
|
|
209
|
-
* Walks the parent chain rather than reading the immediate parent, because a chat reaches its project
|
|
210
|
-
* through whatever owns it — directly for a project chat, through the agent for an agent's chat.
|
|
211
|
-
*/
|
|
212
|
-
var peekProject = (chat) => {
|
|
213
|
-
let owner = Obj.getParent(chat);
|
|
214
|
-
for (let depth = 0; owner && depth < MAX_OWNER_DEPTH; depth++) {
|
|
215
|
-
if (Obj.instanceOf(Project.Project, owner)) return owner;
|
|
216
|
-
owner = Obj.getParent(owner);
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
/** Loads the checklist in order, de-duplicated: a concurrent merge can land the same ref twice. */
|
|
220
|
-
var loadTasks = (chat) => Effect.gen(function* () {
|
|
221
|
-
const tasks = yield* Effect.forEach(chat.tasks, (task) => Database.load(task).pipe(Effect.orElseSucceed(() => void 0)));
|
|
222
|
-
return Task.dedupeById(tasks);
|
|
223
|
-
});
|
|
224
|
-
/** Sync twin of {@link loadTasks}; an unresolved ref contributes nothing rather than throwing. */
|
|
225
|
-
var resolveTasks = (chat) => Task.dedupeById(chat.tasks.filter((ref) => ref.isAvailable).map((ref) => ref.target));
|
|
226
|
-
/** A task is open until it reaches a terminal status. */
|
|
227
|
-
var isOpenTask = (task) => (task.status ?? "todo") === "todo" || task.status === "started";
|
|
228
|
-
/**
|
|
229
|
-
* The conversation's tasks rendered as a numbered checklist (the format the planning prompts
|
|
230
|
-
* speak), or a placeholder when none exist. Ordinals match the task list UI, and non-default
|
|
231
|
-
* status/dependencies are noted so the model can reason about readiness.
|
|
232
|
-
*/
|
|
233
|
-
var formatChecklist = (chat) => Effect.gen(function* () {
|
|
234
|
-
const tasks = yield* loadTasks(chat);
|
|
235
|
-
if (tasks.length === 0) return "No checklist found.";
|
|
236
|
-
return formatTasks(tasks);
|
|
237
|
-
});
|
|
238
|
-
/**
|
|
239
|
-
* Renders tasks as `1. [ ] Title` lines, ordinals in checklist order, each followed by an indented
|
|
240
|
-
* note line carrying the task's ref and any status/dependency notes — appended to the title, models
|
|
241
|
-
* paste the notes back into it. The ref renders as `[MNEMONIC](uri)` so the mnemonic the user reads
|
|
242
|
-
* and the handle the tools take travel together and get pasted back verbatim.
|
|
243
|
-
*/
|
|
244
|
-
var formatTasks = (tasks) => {
|
|
245
|
-
const ordinals = new Map(tasks.map((task, index) => [task.id, index + 1]));
|
|
246
|
-
return tasks.map((task, index) => {
|
|
247
|
-
const line = `${index + 1}. [${task.status === "done" ? "x" : " "}] ${task.title}`;
|
|
248
|
-
const notes = [`ref: [${Obj.getMnemonic(task)}](${Obj.getURI(task)})`];
|
|
249
|
-
if (task.status && task.status !== "todo" && task.status !== "done") notes.push(task.status);
|
|
250
|
-
const deps = (task.dependsOn ?? []).map((ref) => ref.target).filter((target) => target !== void 0).map((target) => ordinals.get(target.id) ?? target.title);
|
|
251
|
-
if (deps.length > 0) notes.push(`depends on ${deps.join(", ")}`);
|
|
252
|
-
return `${line}\n (${notes.join("; ")})`;
|
|
253
|
-
}).join("\n");
|
|
254
|
-
};
|
|
255
|
-
//#endregion
|
|
256
|
-
export { Chat, CompanionChatAnnotation, addTask, assignTasks, deleteTask, feedEntityId, fields, formatChecklist, isOpenTask, linkCompanion, loadForFeed, loadTasks, make, peekProject, resolveTasks, seedSession, Chat_exports as t, unassignTasks };
|
|
257
|
-
|
|
258
|
-
//# sourceMappingURL=Chat.mjs.map
|
|
1
|
+
export { t as Chat, r as CompanionChatAnnotation, i as addTask, a as assignTasks, o as deleteTask, s as feedEntityId, c as fields, l as formatChecklist, u as isOpenTask, d as linkCompanion, f as loadForFeed, p as loadTasks, m as make, h as peekProject, g as resolveTasks, _ as seedSession, v as unassignTasks } from "../chunk-Chat.mjs";
|