clay-server 3.8.1 → 4.0.0-beta.10
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/lib/ask-user-mcp-server.js +5 -119
- package/lib/background-task-timing.js +105 -0
- package/lib/builtin-mates.js +6 -6
- package/lib/claude-hook-installer.js +15 -0
- package/lib/clay-history-mcp-server.js +62 -297
- package/lib/daemon.js +23 -1
- package/lib/debate-model-selection.js +119 -0
- package/lib/home-debate-tool-policy.js +164 -0
- package/lib/home-response-notifications.js +26 -0
- package/lib/knowledge-import.js +436 -0
- package/lib/knowledge-record-store.js +188 -0
- package/lib/knowledge-search.js +224 -0
- package/lib/mate-creation-mcp-server.js +55 -0
- package/lib/mate-knowledge-mcp-server.js +151 -0
- package/lib/mate-knowledge-migration.js +283 -0
- package/lib/mate-knowledge-service.js +488 -0
- package/lib/mate-knowledge-sync.js +482 -0
- package/lib/mate-ready-creation.js +95 -0
- package/lib/mates.js +2 -0
- package/lib/notes.js +2 -1
- package/lib/project-ask-user.js +64 -0
- package/lib/project-capsule-catalog.js +101 -0
- package/lib/project-connection.js +17 -0
- package/lib/project-debate-proposal.js +133 -17
- package/lib/project-debate.js +214 -110
- package/lib/project-delegated-follow-up.js +84 -0
- package/lib/project-delegated-session.js +53 -0
- package/lib/project-filesystem.js +7 -9
- package/lib/project-knowledge.js +23 -0
- package/lib/project-logs-comments.js +137 -0
- package/lib/project-logs-mcp-server.js +290 -0
- package/lib/project-logs-query.js +167 -0
- package/lib/project-logs-root.js +67 -0
- package/lib/project-logs-schema.js +259 -0
- package/lib/project-logs-service.js +357 -0
- package/lib/project-logs-snapshot.js +202 -0
- package/lib/project-logs-store.js +498 -0
- package/lib/project-logs-versioning.js +128 -0
- package/lib/project-logs.js +347 -0
- package/lib/project-mate-creation-proposal.js +120 -0
- package/lib/project-mate-interaction.js +14 -0
- package/lib/project-mate-knowledge.js +110 -0
- package/lib/project-memory.js +36 -7
- package/lib/project-models.js +157 -32
- package/lib/project-session-notes.js +2 -2
- package/lib/project-session-pair.js +8 -8
- package/lib/project-sessions.js +66 -51
- package/lib/project-vendor-login.js +414 -0
- package/lib/project-worker-proposal.js +25 -25
- package/lib/project-workspace-query.js +144 -0
- package/lib/project.js +433 -102
- package/lib/public/app.js +109 -48
- package/lib/public/css/avatar-imprints.css +0 -1
- package/lib/public/css/base.css +4 -0
- package/lib/public/css/capsule-ui.css +389 -0
- package/lib/public/css/command-palette.css +306 -6
- package/lib/public/css/home-capsule-library.css +243 -0
- package/lib/public/css/home-capsule-source.css +192 -0
- package/lib/public/css/home-debate-live.css +146 -0
- package/lib/public/css/home-debate-planning.css +125 -0
- package/lib/public/css/home-debates-archive.css +281 -0
- package/lib/public/css/home-hub.css +896 -405
- package/lib/public/css/home-mate-creation.css +34 -0
- package/lib/public/css/home-mate-model-picker.css +131 -0
- package/lib/public/css/home-mate-settings.css +344 -0
- package/lib/public/css/home-session-actions.css +265 -0
- package/lib/public/css/home-sidebar.css +667 -0
- package/lib/public/css/icon-strip.css +1 -2
- package/lib/public/css/input.css +79 -25
- package/lib/public/css/mates.css +7 -34
- package/lib/public/css/pane.css +1 -1
- package/lib/public/css/playbook.css +0 -80
- package/lib/public/css/project-logs.css +234 -0
- package/lib/public/css/scheduler.css +1 -1
- package/lib/public/css/sticky-notes.css +53 -117
- package/lib/public/css/tui-attention.css +0 -44
- package/lib/public/css/workspace-assignment.css +45 -0
- package/lib/public/fonts/source-serif-4/LICENSE.md +93 -0
- package/lib/public/fonts/source-serif-4/SourceSerif4Caption-Semibold.ttf.woff2 +0 -0
- package/lib/public/index.html +96 -74
- package/lib/public/modules/app-connection.js +100 -5
- package/lib/public/modules/app-dm.js +43 -160
- package/lib/public/modules/app-home-hub.js +328 -585
- package/lib/public/modules/app-message-cards.js +242 -0
- package/lib/public/modules/app-message-router.js +84 -0
- package/lib/public/modules/app-messages.js +124 -38
- package/lib/public/modules/app-notifications.js +8 -119
- package/lib/public/modules/app-panels.js +43 -2
- package/lib/public/modules/app-projects.js +9 -1
- package/lib/public/modules/app-rendering.js +21 -602
- package/lib/public/modules/avatar-imprint.js +11 -8
- package/lib/public/modules/background-tasks-ui.js +139 -16
- package/lib/public/modules/chat-bubble-renderer.js +152 -0
- package/lib/public/modules/chat-render-runtime.js +198 -0
- package/lib/public/modules/command-palette.js +225 -436
- package/lib/public/modules/dm-render.js +82 -0
- package/lib/public/modules/home-capsule-creation-intent.js +31 -0
- package/lib/public/modules/home-capsule-library.js +146 -0
- package/lib/public/modules/home-capsule-source.js +383 -0
- package/lib/public/modules/home-chat-identity.js +35 -0
- package/lib/public/modules/home-chat-scroll.js +77 -0
- package/lib/public/modules/home-chat-stream-state.js +34 -0
- package/lib/public/modules/home-composer-focus.js +25 -0
- package/lib/public/modules/home-conversations-sheet.js +179 -0
- package/lib/public/modules/home-debate-activity.js +48 -0
- package/lib/public/modules/home-debate-controls.js +295 -0
- package/lib/public/modules/home-debate-launch.js +41 -0
- package/lib/public/modules/home-debate-live.js +284 -0
- package/lib/public/modules/home-debate-models.js +73 -0
- package/lib/public/modules/home-debate-planning.js +335 -0
- package/lib/public/modules/home-debates-archive.js +279 -0
- package/lib/public/modules/home-dock-resize.js +74 -0
- package/lib/public/modules/home-dock.js +446 -0
- package/lib/public/modules/home-mate-chat.js +496 -0
- package/lib/public/modules/home-mate-creation.js +109 -0
- package/lib/public/modules/home-mate-model-picker.js +281 -0
- package/lib/public/modules/home-mate-selection.js +38 -0
- package/lib/public/modules/home-mate-settings-menu.js +131 -0
- package/lib/public/modules/home-mate-settings.js +383 -0
- package/lib/public/modules/home-session-actions.js +373 -0
- package/lib/public/modules/home-session-model-confirmation.js +13 -0
- package/lib/public/modules/home-shell.js +9 -0
- package/lib/public/modules/home-sidebar-chat-list.js +63 -0
- package/lib/public/modules/home-sidebar.js +165 -0
- package/lib/public/modules/home-sub-surface.js +19 -0
- package/lib/public/modules/home-surface-boot.js +68 -0
- package/lib/public/modules/home-surface.js +141 -0
- package/lib/public/modules/home-tools.js +271 -0
- package/lib/public/modules/markdown.js +50 -2
- package/lib/public/modules/mate-management.js +86 -0
- package/lib/public/modules/mate-sidebar.js +0 -8
- package/lib/public/modules/mate-wizard.js +0 -7
- package/lib/public/modules/mention.js +17 -19
- package/lib/public/modules/pane-bridge.js +9 -0
- package/lib/public/modules/profile.js +4 -5
- package/lib/public/modules/project-logs-ambient.js +238 -0
- package/lib/public/modules/project-logs-render.js +359 -0
- package/lib/public/modules/project-logs.js +328 -0
- package/lib/public/modules/project-settings.js +34 -28
- package/lib/public/modules/project-switcher.js +11 -5
- package/lib/public/modules/project-workspace-assignment.js +23 -0
- package/lib/public/modules/scheduler.js +17 -3
- package/lib/public/modules/search-clay-chat.js +402 -0
- package/lib/public/modules/server-settings.js +14 -12
- package/lib/public/modules/session-actions.js +1 -1
- package/lib/public/modules/sidebar-mates.js +56 -552
- package/lib/public/modules/sidebar-mobile.js +12 -73
- package/lib/public/modules/sidebar-presence.js +37 -0
- package/lib/public/modules/sidebar-sessions.js +25 -1
- package/lib/public/modules/split-pair-ui.js +13 -13
- package/lib/public/modules/split-view.js +7 -0
- package/lib/public/modules/sticky-notes.js +106 -53
- package/lib/public/modules/tool-input-composition.js +48 -0
- package/lib/public/modules/tool-llm-status.js +167 -0
- package/lib/public/modules/tool-palette.js +76 -4
- package/lib/public/modules/tool-renderer-advanced.js +285 -0
- package/lib/public/modules/tool-renderer-chart.js +158 -0
- package/lib/public/modules/tool-renderer-semantics.js +35 -0
- package/lib/public/modules/tool-renderer.js +464 -0
- package/lib/public/modules/tool-runtime.js +309 -0
- package/lib/public/modules/tool-ui-evaluator.js +97 -0
- package/lib/public/modules/tool-ui-tree.js +21 -0
- package/lib/public/modules/tui-attention.js +10 -0
- package/lib/public/modules/user-settings.js +16 -23
- package/lib/public/modules/vendor-login.js +287 -0
- package/lib/public/modules/worker-proposal.js +2 -2
- package/lib/public/modules/workspace-assignment-card.js +149 -0
- package/lib/public/modules/workspace-assignment-routing.js +4 -0
- package/lib/public/style.css +14 -2
- package/lib/runtime-env.js +65 -0
- package/lib/sdk-bridge.js +120 -32
- package/lib/sdk-message-processor.js +34 -13
- package/lib/server-home-capsule-creation.js +36 -0
- package/lib/server-home-chat-events.js +287 -0
- package/lib/server-home-chat.js +518 -0
- package/lib/server-home-clay-entry.js +65 -0
- package/lib/server-home-clay-session-links.js +26 -0
- package/lib/server-home-debate-planning.js +214 -0
- package/lib/server-home-debates.js +137 -0
- package/lib/server-home-mate-creation.js +261 -0
- package/lib/server-home-models.js +241 -0
- package/lib/server-home-preferences.js +61 -0
- package/lib/server-mates.js +37 -0
- package/lib/server-tools.js +377 -0
- package/lib/server.js +166 -59
- package/lib/session-pair-mcp-server.js +6 -6
- package/lib/sessions.js +22 -4
- package/lib/tool-capsule-source.js +142 -0
- package/lib/tool-control-mcp-server.js +126 -0
- package/lib/tool-llm.js +48 -0
- package/lib/tool-storage.js +77 -0
- package/lib/tool-ui-spec-advanced.js +82 -0
- package/lib/tool-ui-spec.js +261 -0
- package/lib/tools-registry.js +286 -0
- package/lib/users-home-dock-preferences.js +77 -0
- package/lib/users-home-surface-preferences.js +99 -0
- package/lib/users-preferences.js +4 -7
- package/lib/users.js +12 -0
- package/lib/whats-new-content.js +3 -3
- package/lib/workspace-assignment-service.js +420 -0
- package/lib/workspace-query-mcp-server.js +154 -0
- package/lib/workspace-query-service.js +492 -0
- package/lib/ws-schema.js +107 -12
- package/lib/yoke/adapters/acp.js +2 -1
- package/lib/yoke/adapters/antigravity.js +2 -1
- package/lib/yoke/adapters/claude-worker.js +11 -4
- package/lib/yoke/adapters/claude.js +141 -46
- package/lib/yoke/adapters/codex.js +79 -11
- package/lib/yoke/adapters/kiro.js +2 -1
- package/lib/yoke/claude-user-input.js +38 -0
- package/lib/yoke/codex-app-server.js +20 -2
- package/lib/yoke/codex-background-tasks.js +8 -2
- package/lib/yoke/codex-user-input.js +64 -0
- package/lib/yoke/complete-once.js +164 -0
- package/lib/yoke/index.js +5 -1
- package/lib/yoke/interface.js +19 -0
- package/lib/yoke/mcp-bridge-server.js +2 -6
- package/lib/yoke/user-input.js +254 -0
- package/package.json +4 -3
- package/lib/public/css/home-chat.css +0 -380
- package/lib/public/modules/home-chat.js +0 -438
- package/lib/server-clay-home.js +0 -245
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// Comment review and revision control for Project Logs.
|
|
2
|
+
//
|
|
3
|
+
// Split from project-logs-store.js so the store stays inside the module size
|
|
4
|
+
// limit. Everything here is Project Driver territory: reviewing a user comment
|
|
5
|
+
// and moving the canonical record between revisions. The authorization layer
|
|
6
|
+
// decides who may call these; this module only guarantees the record shape.
|
|
7
|
+
|
|
8
|
+
var logsSchema = require("./project-logs-schema");
|
|
9
|
+
var logsSnapshot = require("./project-logs-snapshot");
|
|
10
|
+
var logsComments = require("./project-logs-comments");
|
|
11
|
+
var recordStore = require("./knowledge-record-store");
|
|
12
|
+
|
|
13
|
+
function attachVersioning(ctx) {
|
|
14
|
+
var read = ctx.read;
|
|
15
|
+
var requireLive = ctx.requireLive;
|
|
16
|
+
var findChain = ctx.findChain;
|
|
17
|
+
var appendRevision = ctx.appendRevision;
|
|
18
|
+
var nextSnapshot = ctx.nextSnapshot;
|
|
19
|
+
var cleanText = ctx.cleanText;
|
|
20
|
+
var MAX_LINKS = ctx.maxLinks;
|
|
21
|
+
var MAX_RESPONSE_CHARS = ctx.maxResponseChars;
|
|
22
|
+
var scopeId = ctx.scopeId;
|
|
23
|
+
var store = ctx.store;
|
|
24
|
+
var normalizeAuthor = ctx.normalizeAuthor;
|
|
25
|
+
|
|
26
|
+
// Review a user comment. `clarify` and `decline` resolve the comment and
|
|
27
|
+
// create no canonical revision. `incorporate` appends exactly one record that
|
|
28
|
+
// both resolves the comment and carries the new canonical snapshot, so the
|
|
29
|
+
// two can never drift apart.
|
|
30
|
+
function review(ref, input, author) {
|
|
31
|
+
var located = requireLive(ref);
|
|
32
|
+
var data = input || {};
|
|
33
|
+
if (!logsComments.isAction(data.action)) {
|
|
34
|
+
throw new Error("A review action is required: " + logsComments.ACTIONS.join(", "));
|
|
35
|
+
}
|
|
36
|
+
var entry = read(ref, false);
|
|
37
|
+
var target = null;
|
|
38
|
+
for (var i = 0; i < entry.comments.length; i++) {
|
|
39
|
+
if (entry.comments[i].id === data.commentId) { target = entry.comments[i]; break; }
|
|
40
|
+
}
|
|
41
|
+
if (!target) throw new Error("Comment not found on this entry.");
|
|
42
|
+
if (target.review) throw new Error("That comment has already been reviewed.");
|
|
43
|
+
|
|
44
|
+
var response = cleanText(data.response || "", MAX_RESPONSE_CHARS);
|
|
45
|
+
if (data.action !== "incorporate" && !response) {
|
|
46
|
+
throw new Error("A " + data.action + " review requires a response explaining the decision.");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (data.action !== "incorporate") {
|
|
50
|
+
store.append({
|
|
51
|
+
id: recordStore.newRecordId(),
|
|
52
|
+
rootId: located.rootId,
|
|
53
|
+
op: "review",
|
|
54
|
+
scope: scopeId,
|
|
55
|
+
action: data.action,
|
|
56
|
+
commentId: data.commentId,
|
|
57
|
+
response: response,
|
|
58
|
+
author: normalizeAuthor(author),
|
|
59
|
+
at: Date.now(),
|
|
60
|
+
});
|
|
61
|
+
return read(ref, false);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
var validated = logsSchema.validateRevision(data);
|
|
65
|
+
var current = logsSnapshot.cloneSnapshot(entry);
|
|
66
|
+
var snapshot = nextSnapshot(current, validated, data);
|
|
67
|
+
if (logsSnapshot.sameSnapshot(current, snapshot)) {
|
|
68
|
+
throw new Error("Incorporating a comment requires an actual canonical change.");
|
|
69
|
+
}
|
|
70
|
+
appendRevision(located.rootId, "review", snapshot, author, {
|
|
71
|
+
action: "incorporate",
|
|
72
|
+
commentId: data.commentId,
|
|
73
|
+
response: response,
|
|
74
|
+
});
|
|
75
|
+
return read(ref, false);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Restore an earlier revision by appending a new one. Later history is never
|
|
79
|
+
// erased: a revert is itself a revision, with the source recorded.
|
|
80
|
+
function revert(ref, revision, reason, author) {
|
|
81
|
+
var located = requireLive(ref);
|
|
82
|
+
var target = Number(revision);
|
|
83
|
+
if (!Number.isInteger(target) || target < 1) throw new Error("A revision number is required.");
|
|
84
|
+
var trimmedReason = cleanText(reason || "", MAX_RESPONSE_CHARS);
|
|
85
|
+
if (!trimmedReason) throw new Error("A revert requires a reason.");
|
|
86
|
+
|
|
87
|
+
var history = logsSnapshot.revisions(located.chain, MAX_LINKS);
|
|
88
|
+
if (target > history.revisions.length) {
|
|
89
|
+
throw new Error("This entry has only " + history.revisions.length + " revisions.");
|
|
90
|
+
}
|
|
91
|
+
var source = history.revisions[target - 1];
|
|
92
|
+
var current = history.revisions[history.revisions.length - 1];
|
|
93
|
+
if (logsSnapshot.sameSnapshot(current.snapshot, source.snapshot)) {
|
|
94
|
+
throw new Error("Revision " + target + " is identical to the current one.");
|
|
95
|
+
}
|
|
96
|
+
appendRevision(located.rootId, "revert", logsSnapshot.cloneSnapshot(source.snapshot), author, {
|
|
97
|
+
revertedFrom: target,
|
|
98
|
+
reason: trimmedReason,
|
|
99
|
+
});
|
|
100
|
+
return read(ref, false);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// The exact reconstructed snapshot as of one revision.
|
|
104
|
+
function readRevision(ref, revision) {
|
|
105
|
+
var located = findChain(ref);
|
|
106
|
+
if (!located) throw new Error("Log entry not found.");
|
|
107
|
+
var target = Number(revision);
|
|
108
|
+
if (!Number.isInteger(target) || target < 1) throw new Error("A revision number is required.");
|
|
109
|
+
var history = logsSnapshot.revisions(located.chain, MAX_LINKS);
|
|
110
|
+
if (target > history.revisions.length) {
|
|
111
|
+
throw new Error("This entry has only " + history.revisions.length + " revisions.");
|
|
112
|
+
}
|
|
113
|
+
var found = history.revisions[target - 1];
|
|
114
|
+
var described = logsSnapshot.describeRevision(found);
|
|
115
|
+
described.ref = ref;
|
|
116
|
+
described.snapshot = found.snapshot;
|
|
117
|
+
return described;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
review: review,
|
|
123
|
+
revert: revert,
|
|
124
|
+
readRevision: readRevision,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = { attachVersioning: attachVersioning };
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
// Project Logs message handling and session-bound MCP wiring.
|
|
2
|
+
//
|
|
3
|
+
// Every capability here is derived server-side. The WebSocket path binds from
|
|
4
|
+
// ws._clayUser plus this project's own slug, and the MCP path binds from the
|
|
5
|
+
// exact live session object. Neither accepts identity or project scope from
|
|
6
|
+
// the message, so a client cannot widen its own reach by asking.
|
|
7
|
+
//
|
|
8
|
+
// Authorship is split deliberately. Canonical entries are created and revised
|
|
9
|
+
// only by this project's agent sessions through `clay-logs`; a connected human
|
|
10
|
+
// reads the ledger and appends attributed comments. That keeps the log an
|
|
11
|
+
// honest record of the work rather than a shared wiki.
|
|
12
|
+
|
|
13
|
+
var logsMcp = require("./project-logs-mcp-server");
|
|
14
|
+
|
|
15
|
+
var LIST_LIMIT = 50;
|
|
16
|
+
var MAX_NOTICE_TITLE_CHARS = 160;
|
|
17
|
+
var MAX_NOTICE_SUMMARY_CHARS = 240;
|
|
18
|
+
|
|
19
|
+
// Canonical writes that move the ledger forward. A plain comment, a
|
|
20
|
+
// clarification, and a decline are participation, not revisions, so they never
|
|
21
|
+
// notify.
|
|
22
|
+
var NOTIFYING_OPS = ["create", "update", "link", "incorporate", "revert"];
|
|
23
|
+
|
|
24
|
+
function clip(value, max) {
|
|
25
|
+
if (typeof value !== "string") return "";
|
|
26
|
+
return value.length > max ? value.substring(0, max) : value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Bounded ledger metadata only. No body, no comment text, no author identity
|
|
30
|
+
// beyond the vendor that wrote it.
|
|
31
|
+
function updateNotice(entry, op) {
|
|
32
|
+
return {
|
|
33
|
+
type: "project_log_updated",
|
|
34
|
+
ref: entry.ref,
|
|
35
|
+
revision: entry.revisions,
|
|
36
|
+
op: op,
|
|
37
|
+
category: entry.category,
|
|
38
|
+
priority: entry.priority,
|
|
39
|
+
title: clip(entry.title, MAX_NOTICE_TITLE_CHARS),
|
|
40
|
+
summary: clip(entry.summary, MAX_NOTICE_SUMMARY_CHARS),
|
|
41
|
+
at: entry.updatedAt,
|
|
42
|
+
vendor: (entry.updatedBy && entry.updatedBy.vendor) || null,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
var SYSTEM_PROMPT_LABEL = "--- Project Logs (durable project record; manage via clay-logs tools) ---";
|
|
47
|
+
|
|
48
|
+
function parseJsonArray(value) {
|
|
49
|
+
if (Array.isArray(value)) return value;
|
|
50
|
+
if (typeof value !== "string" || !value.trim()) return undefined;
|
|
51
|
+
var parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = JSON.parse(value);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
throw new Error("Expected a JSON array.");
|
|
56
|
+
}
|
|
57
|
+
if (!Array.isArray(parsed)) throw new Error("Expected a JSON array.");
|
|
58
|
+
return parsed;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The tool surface accepts JSON-encoded arrays because MCP shapes are flat
|
|
62
|
+
// scalars. The service and store only ever see real arrays.
|
|
63
|
+
function coerceToolArgs(args) {
|
|
64
|
+
var out = Object.assign({}, args || {});
|
|
65
|
+
if (out.tags !== undefined) out.tags = parseJsonArray(out.tags);
|
|
66
|
+
if (out.links !== undefined) out.links = parseJsonArray(out.links);
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var HANDLED_TYPES = [
|
|
71
|
+
"project_logs_list",
|
|
72
|
+
"project_log_read",
|
|
73
|
+
"project_log_comment",
|
|
74
|
+
// Retired human mutations. Still claimed so an older client gets an
|
|
75
|
+
// explicit refusal rather than silence.
|
|
76
|
+
"project_log_create",
|
|
77
|
+
"project_log_update",
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
var RETIRED_TYPES = ["project_log_create", "project_log_update"];
|
|
81
|
+
|
|
82
|
+
function attachProjectLogs(ctx) {
|
|
83
|
+
var service = ctx.service;
|
|
84
|
+
var sm = ctx.sm;
|
|
85
|
+
var slug = ctx.projectSlug;
|
|
86
|
+
var isMate = ctx.isMate === true;
|
|
87
|
+
var mateId = ctx.mateId || null;
|
|
88
|
+
var sendTo = ctx.sendTo;
|
|
89
|
+
var getClients = ctx.getClients || function () { return []; };
|
|
90
|
+
var getProjectOwnerId = ctx.getProjectOwnerId || function () { return null; };
|
|
91
|
+
|
|
92
|
+
// Every socket in this project's client set passed the project access check
|
|
93
|
+
// at WebSocket upgrade, so a broadcast here is already scoped to authorized
|
|
94
|
+
// human users of this project. Nothing is read from any payload. Pane
|
|
95
|
+
// connections are excluded, matching how sticky notes broadcast.
|
|
96
|
+
function broadcastUpdate(entry, op) {
|
|
97
|
+
if (isMate || !entry) return;
|
|
98
|
+
if (NOTIFYING_OPS.indexOf(op) === -1) return;
|
|
99
|
+
var notice = updateNotice(entry, op);
|
|
100
|
+
var clients = getClients();
|
|
101
|
+
for (var client of clients) {
|
|
102
|
+
if (!client || client.readyState !== 1 || client._clayPane) continue;
|
|
103
|
+
sendTo(client, notice);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Wrap a canonical write so a successful one notifies. A throw never reaches
|
|
108
|
+
// here, so a failed or no-op write is silent.
|
|
109
|
+
function notifying(op, run) {
|
|
110
|
+
return function (args) {
|
|
111
|
+
var result = run(args);
|
|
112
|
+
broadcastUpdate(result, op);
|
|
113
|
+
return result;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// --- Bindings --------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
function sessionBinding(session) {
|
|
120
|
+
if (!service) return null;
|
|
121
|
+
if (isMate) {
|
|
122
|
+
if (!mateId) return null;
|
|
123
|
+
if (session && (!sm || !sm.sessions || sm.sessions.get(session.localId) !== session)) return null;
|
|
124
|
+
return service.bindMate({
|
|
125
|
+
projectSlug: slug,
|
|
126
|
+
projectOwnerId: getProjectOwnerId(),
|
|
127
|
+
isMate: true,
|
|
128
|
+
mateId: mateId,
|
|
129
|
+
session: session || null,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
if (!session) return null;
|
|
133
|
+
return service.bindProjectSession({ projectSlug: slug, session: session });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function userBinding(ws) {
|
|
137
|
+
if (!service || isMate) return null;
|
|
138
|
+
return service.bindUser({ projectSlug: slug, user: (ws && ws._clayUser) || null });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// --- WebSocket surface -----------------------------------------------
|
|
142
|
+
|
|
143
|
+
function fail(ws, requestId, message) {
|
|
144
|
+
sendTo(ws, { type: "project_logs_error", requestId: requestId || null, message: message });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Search already returns the ledger row shape, so a query needs no per-hit
|
|
148
|
+
// read and neither path dumps a record body into the list.
|
|
149
|
+
// Returns both the rows and this project's live category vocabulary, so the
|
|
150
|
+
// filter is populated solely from what this project actually uses.
|
|
151
|
+
function listEntries(bound, query, category) {
|
|
152
|
+
if (query) {
|
|
153
|
+
var hits = bound.searchLogs({ query: query, kind: category, limit: LIST_LIMIT });
|
|
154
|
+
return { entries: hits.results, categories: hits.categories || [] };
|
|
155
|
+
}
|
|
156
|
+
var listed = bound.listLogs({ kind: category, limit: LIST_LIMIT });
|
|
157
|
+
return { entries: listed.entries, categories: listed.categories || [] };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function handleLogsMessage(ws, msg) {
|
|
161
|
+
if (!msg || typeof msg.type !== "string") return false;
|
|
162
|
+
if (HANDLED_TYPES.indexOf(msg.type) === -1) return false;
|
|
163
|
+
|
|
164
|
+
var requestId = typeof msg.requestId === "string" ? msg.requestId : null;
|
|
165
|
+
if (isMate) {
|
|
166
|
+
fail(ws, requestId, "Project Logs are available in projects, not in Mate conversations.");
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Canonical authorship belongs to the project's agent sessions. A human
|
|
171
|
+
// asking to create or revise is refused before any binding is attempted,
|
|
172
|
+
// so no privilege level can reach the canonical writers.
|
|
173
|
+
if (RETIRED_TYPES.indexOf(msg.type) !== -1) {
|
|
174
|
+
fail(ws, requestId, "Project Logs are written by this project's agent sessions. Add a comment instead.");
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
var bound = userBinding(ws);
|
|
179
|
+
if (!bound) {
|
|
180
|
+
fail(ws, requestId, "Project Logs are not available for this project.");
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
if (msg.type === "project_logs_list") {
|
|
186
|
+
var query = typeof msg.query === "string" ? msg.query.trim() : "";
|
|
187
|
+
// A view preference, not an authorization input: the store validates it.
|
|
188
|
+
var category = typeof msg.category === "string" && msg.category ? msg.category : null;
|
|
189
|
+
var listing = listEntries(bound, query, category);
|
|
190
|
+
sendTo(ws, {
|
|
191
|
+
type: "project_logs_state",
|
|
192
|
+
requestId: requestId,
|
|
193
|
+
entries: listing.entries,
|
|
194
|
+
categories: listing.categories,
|
|
195
|
+
});
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (msg.type === "project_log_read") {
|
|
200
|
+
sendTo(ws, { type: "project_log_entry", requestId: requestId, entry: bound.readLog({ ref: msg.ref }) });
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (msg.type === "project_log_comment") {
|
|
205
|
+
var commented = bound.commentLog({ ref: msg.ref, body: msg.body });
|
|
206
|
+
sendTo(ws, { type: "project_log_commented", requestId: requestId, entry: commented });
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
} catch (e) {
|
|
210
|
+
fail(ws, requestId, (e && e.message) || "Project Logs could not complete the request.");
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// --- MCP surface ------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
function toolHandlers(bound) {
|
|
220
|
+
if (!bound) return null;
|
|
221
|
+
return {
|
|
222
|
+
listLogs: function (args) { return bound.listLogs(args || {}); },
|
|
223
|
+
searchLogs: function (args) { return bound.searchLogs(args || {}); },
|
|
224
|
+
readLog: function (args) { return bound.readLog(args || {}); },
|
|
225
|
+
logHistory: function (args) { return bound.logHistory(args || {}); },
|
|
226
|
+
readLogRevision: function (args) { return bound.readLogRevision(args || {}); },
|
|
227
|
+
listLogFeedback: function (args) { return bound.listLogFeedback(args || {}); },
|
|
228
|
+
reviewLogComment: function (args) {
|
|
229
|
+
var request = coerceToolArgs(args);
|
|
230
|
+
var result = bound.reviewLogComment(request);
|
|
231
|
+
// Only an incorporation is a canonical revision; clarify and decline
|
|
232
|
+
// resolve a comment and change nothing.
|
|
233
|
+
if (request && request.action === "incorporate") broadcastUpdate(result, "incorporate");
|
|
234
|
+
return result;
|
|
235
|
+
},
|
|
236
|
+
revertLog: notifying("revert", function (args) { return bound.revertLog(args || {}); }),
|
|
237
|
+
createLog: notifying("create", function (args) { return bound.createLog(coerceToolArgs(args)); }),
|
|
238
|
+
updateLog: notifying("update", function (args) { return bound.updateLog(coerceToolArgs(args)); }),
|
|
239
|
+
linkLog: notifying("link", function (args) { return bound.linkLog(coerceToolArgs(args)); }),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// A Mate project advertises tools only when the registry confirms
|
|
244
|
+
// authoritative builtin Clay, and then only the read-only cross-project set.
|
|
245
|
+
function includeGlobal() {
|
|
246
|
+
if (!isMate) return false;
|
|
247
|
+
var projectBound = sessionBinding(null);
|
|
248
|
+
return !!(projectBound && projectBound.isClay === true);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function getToolDefs(session) {
|
|
252
|
+
if (!service) return [];
|
|
253
|
+
if (isMate && !includeGlobal()) return [];
|
|
254
|
+
var bound = session ? sessionBinding(session) : null;
|
|
255
|
+
if (session && !bound) return [];
|
|
256
|
+
return logsMcp.getToolDefs(toolHandlers(bound), isMate);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function createMcpServer(adapter, session) {
|
|
260
|
+
if (!service) return null;
|
|
261
|
+
if (isMate && !includeGlobal()) return null;
|
|
262
|
+
if (!isMate && session && !sessionBinding(session)) {
|
|
263
|
+
// A session that cannot bind still gets the fail-closed descriptor
|
|
264
|
+
// rather than a silently missing server.
|
|
265
|
+
return logsMcp.createMcpServer(adapter, null, false);
|
|
266
|
+
}
|
|
267
|
+
var bound = session ? sessionBinding(session) : null;
|
|
268
|
+
return logsMcp.createMcpServer(adapter, toolHandlers(bound), isMate);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function getDynamicToolDefs(session) {
|
|
272
|
+
return getToolDefs(session).map(function (tool) {
|
|
273
|
+
tool.permissionName = "mcp__clay-logs__" + tool.name;
|
|
274
|
+
return tool;
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function getBridgeTools(session, normalizeSchema) {
|
|
279
|
+
if (!session) return [];
|
|
280
|
+
var defs = getToolDefs(session);
|
|
281
|
+
var tools = [];
|
|
282
|
+
for (var i = 0; i < defs.length; i++) {
|
|
283
|
+
tools.push({
|
|
284
|
+
server: "clay-logs",
|
|
285
|
+
name: defs[i].name,
|
|
286
|
+
description: defs[i].description || defs[i].name,
|
|
287
|
+
inputSchema: normalizeSchema(defs[i].inputSchema),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
return tools;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function callBridgeTool(session, toolName, args) {
|
|
294
|
+
if (!session) return Promise.reject(new Error("Project Logs require a valid session."));
|
|
295
|
+
var defs = getToolDefs(session);
|
|
296
|
+
for (var i = 0; i < defs.length; i++) {
|
|
297
|
+
if (defs[i].name === toolName) return Promise.resolve(defs[i].handler(args || {}));
|
|
298
|
+
}
|
|
299
|
+
return Promise.reject(new Error("Project Logs tool not found: " + toolName));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// A count only. The comment bodies stay in the tool, so a pending queue is
|
|
303
|
+
// discoverable on the next turn without dumping the discussion into context
|
|
304
|
+
// and without starting a turn of its own.
|
|
305
|
+
function pendingFeedbackSignal(session) {
|
|
306
|
+
if (!session) return "";
|
|
307
|
+
var bound = sessionBinding(session);
|
|
308
|
+
if (!bound || typeof bound.listLogFeedback !== "function") return "";
|
|
309
|
+
var total = 0;
|
|
310
|
+
try {
|
|
311
|
+
total = bound.listLogFeedback({ limit: 1 }).total || 0;
|
|
312
|
+
} catch (e) {
|
|
313
|
+
return "";
|
|
314
|
+
}
|
|
315
|
+
if (!total) return "";
|
|
316
|
+
return "\n" + total + (total === 1 ? " comment awaits" : " comments await") +
|
|
317
|
+
" your review. Call list_log_feedback to see them.";
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function getSystemPrompt(session) {
|
|
321
|
+
if (isMate || !service) return "";
|
|
322
|
+
if (session && !sessionBinding(session)) return "";
|
|
323
|
+
return SYSTEM_PROMPT_LABEL + "\n" + logsMcp.LOGS_CONTRACT +
|
|
324
|
+
"\n" + logsMcp.LEARNING_CONTRACT +
|
|
325
|
+
"\n" + logsMcp.REVIEW_CONTRACT + pendingFeedbackSignal(session);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
handleLogsMessage: handleLogsMessage,
|
|
330
|
+
createMcpServer: createMcpServer,
|
|
331
|
+
getToolDefs: getToolDefs,
|
|
332
|
+
getDynamicToolDefs: getDynamicToolDefs,
|
|
333
|
+
getBridgeTools: getBridgeTools,
|
|
334
|
+
callBridgeTool: callBridgeTool,
|
|
335
|
+
getSystemPrompt: getSystemPrompt,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
module.exports = {
|
|
340
|
+
SYSTEM_PROMPT_LABEL: SYSTEM_PROMPT_LABEL,
|
|
341
|
+
NOTIFYING_OPS: NOTIFYING_OPS,
|
|
342
|
+
updateNotice: updateNotice,
|
|
343
|
+
HANDLED_TYPES: HANDLED_TYPES,
|
|
344
|
+
RETIRED_TYPES: RETIRED_TYPES,
|
|
345
|
+
attachProjectLogs: attachProjectLogs,
|
|
346
|
+
coerceToolArgs: coerceToolArgs,
|
|
347
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
var mateCreationMcp = require("./mate-creation-mcp-server");
|
|
2
|
+
var selectCatalogModel = require("./project-models").selectCatalogModel;
|
|
3
|
+
|
|
4
|
+
function attachMateCreationProposal(ctx) {
|
|
5
|
+
var pending = {};
|
|
6
|
+
|
|
7
|
+
function clean(value, maxLength) {
|
|
8
|
+
if (typeof value !== "string") return "";
|
|
9
|
+
return value.replace(/\u0000/g, "").trim().slice(0, maxLength);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function publicProposal(source) {
|
|
13
|
+
return {
|
|
14
|
+
proposalId: source.proposalId,
|
|
15
|
+
name: clean(source.name, 80),
|
|
16
|
+
bio: clean(source.bio, 280),
|
|
17
|
+
relationship: clean(source.relationship, 120),
|
|
18
|
+
activities: Array.isArray(source.activities) ? source.activities.slice(0, 12) : [],
|
|
19
|
+
communicationStyle: Array.isArray(source.communicationStyle) ? source.communicationStyle.slice(0, 12) : [],
|
|
20
|
+
autonomy: clean(source.autonomy, 500),
|
|
21
|
+
identityMarkdown: clean(source.identityMarkdown, 30000),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function record(session, event) {
|
|
26
|
+
if (typeof ctx.recordSessionEvent === "function") ctx.recordSessionEvent(session, event);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function validateBoundSession(session) {
|
|
30
|
+
var projectOwnerId = typeof ctx.getProjectOwnerId === "function" ? ctx.getProjectOwnerId() : null;
|
|
31
|
+
var ownerValid = typeof ctx.isMultiUser === "function" && ctx.isMultiUser() ? !!(session && session.ownerId && (!projectOwnerId || session.ownerId === projectOwnerId)) : !!(session && !session.ownerId);
|
|
32
|
+
return !!(session && ctx.isMate === true && ctx.isHostAgent === true && session.mateCreationMode === true
|
|
33
|
+
&& ownerValid);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function hasInterviewAnswer(session) {
|
|
37
|
+
var history = session && Array.isArray(session.history) ? session.history : [];
|
|
38
|
+
for (var i = history.length - 1; i >= 0; i--) if (history[i] && history[i].type === "ask_user_answered") return true;
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getToolDefs(boundSession) {
|
|
43
|
+
if (!boundSession || boundSession.mateCreationMode !== true) return [];
|
|
44
|
+
return mateCreationMcp.getToolDefs(function (definition) {
|
|
45
|
+
if (!validateBoundSession(boundSession)) return Promise.resolve({ action: "error", error: "Mate proposals require an owned Clay creation interview." });
|
|
46
|
+
if (!hasInterviewAnswer(boundSession)) return Promise.resolve({ action: "error", error: "Interview the user before proposing a Mate." });
|
|
47
|
+
return new Promise(function (resolve) {
|
|
48
|
+
var proposalId = "mp_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8);
|
|
49
|
+
definition.proposalId = proposalId;
|
|
50
|
+
pending[proposalId] = { resolve: resolve, definition: definition, session: boundSession };
|
|
51
|
+
record(boundSession, { type: "mate_creation_proposal", proposal: publicProposal(definition) });
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function createMcpServer(adapter, boundSession) {
|
|
57
|
+
return adapter.createToolServer({ name: "clay-mate-creation", version: "1.0.0", tools: getToolDefs(boundSession) });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getBridgeTools(boundSession, normalizeSchema) {
|
|
61
|
+
if (!boundSession) return [];
|
|
62
|
+
return getToolDefs(boundSession).map(function (tool) {
|
|
63
|
+
return { server: "clay-mate-creation", name: tool.name, description: tool.description || tool.name, inputSchema: normalizeSchema(tool.inputSchema) };
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function callBridgeTool(boundSession, toolName, args) {
|
|
68
|
+
var tools = getToolDefs(boundSession);
|
|
69
|
+
for (var i = 0; i < tools.length; i++) if (tools[i].name === toolName) return Promise.resolve(tools[i].handler(args || {}));
|
|
70
|
+
return Promise.reject(new Error("Session tool not found: clay-mate-creation/" + toolName));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function handleHomeMessage(ws, msg, session) {
|
|
74
|
+
var entry = msg && msg.proposalId ? pending[msg.proposalId] : null;
|
|
75
|
+
if (!entry || entry.session !== session || !validateBoundSession(session)) return false;
|
|
76
|
+
delete pending[msg.proposalId];
|
|
77
|
+
if (msg.action !== "create") {
|
|
78
|
+
record(session, { type: "mate_creation_proposal_resolved", proposalId: msg.proposalId, action: "cancel" });
|
|
79
|
+
entry.resolve({ action: "cancel" });
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (typeof ctx.createReadyMate !== "function") {
|
|
83
|
+
var unavailable = "Mate creation is unavailable.";
|
|
84
|
+
record(session, { type: "mate_creation_proposal_resolved", proposalId: msg.proposalId, action: "error", error: unavailable });
|
|
85
|
+
entry.resolve({ action: "error", error: unavailable });
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
var vendor = session.vendor || "claude";
|
|
89
|
+
if (typeof ctx.getVendorModelCatalog !== "function") {
|
|
90
|
+
var catalogUnavailable = "The Mate model catalog is unavailable.";
|
|
91
|
+
record(session, { type: "mate_creation_proposal_resolved", proposalId: msg.proposalId, action: "error", error: catalogUnavailable });
|
|
92
|
+
entry.resolve({ action: "error", error: catalogUnavailable });
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
Promise.resolve(ctx.getVendorModelCatalog(ws, vendor)).then(function (catalog) {
|
|
96
|
+
var selected = catalog && catalog.status === "ready" ? selectCatalogModel(catalog.models || [], catalog.defaultModel, "deep") : null;
|
|
97
|
+
if (!selected) throw new Error((catalog && catalog.error) || "No high-capability Mate model is available for " + vendor + ".");
|
|
98
|
+
return ctx.createReadyMate(ws, session.ownerId || null, Object.assign({}, entry.definition, { vendor: vendor, model: selected.value }));
|
|
99
|
+
}).then(function (mate) {
|
|
100
|
+
session.homeMateCreationPhase = "created";
|
|
101
|
+
record(session, { type: "mate_creation_proposal_resolved", proposalId: msg.proposalId, action: "create", mateId: mate.id, mateName: (mate.profile && mate.profile.displayName) || mate.name || "Mate" });
|
|
102
|
+
entry.resolve({ action: "create", mateId: mate.id });
|
|
103
|
+
}).catch(function (error) {
|
|
104
|
+
var text = error && error.message ? error.message : "The Mate could not be created.";
|
|
105
|
+
record(session, { type: "mate_creation_proposal_resolved", proposalId: msg.proposalId, action: "error", error: text });
|
|
106
|
+
entry.resolve({ action: "error", error: text });
|
|
107
|
+
});
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
createMcpServer: createMcpServer,
|
|
113
|
+
getToolDefs: getToolDefs,
|
|
114
|
+
getBridgeTools: getBridgeTools,
|
|
115
|
+
callBridgeTool: callBridgeTool,
|
|
116
|
+
handleHomeMessage: handleHomeMessage,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = { attachMateCreationProposal: attachMateCreationProposal };
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
var fs = require("fs");
|
|
2
2
|
var path = require("path");
|
|
3
3
|
var crypto = require("crypto");
|
|
4
|
+
var mateSync = require("./mate-knowledge-sync");
|
|
5
|
+
|
|
6
|
+
// Mirror a Mate-owned journal into the Clay-wide Knowledge backend after the
|
|
7
|
+
// legacy append has already succeeded. Reconciling the whole journal keeps the
|
|
8
|
+
// new scope current whether the write added, rewrote, or removed lines.
|
|
9
|
+
function mirrorMateJournal(mateDir, fileName, job) {
|
|
10
|
+
mateSync.syncQuietly({
|
|
11
|
+
mateDir: mateDir,
|
|
12
|
+
fileName: fileName,
|
|
13
|
+
actor: { type: "agent", vendor: (job && job.vendor) || null, sessionKey: (job && job.sessionKey) || null },
|
|
14
|
+
});
|
|
15
|
+
}
|
|
4
16
|
|
|
5
17
|
function attachMateInteraction(ctx) {
|
|
6
18
|
var cwd = ctx.cwd;
|
|
@@ -278,6 +290,7 @@ function attachMateInteraction(ctx) {
|
|
|
278
290
|
fs.mkdirSync(knowledgeDir, { recursive: true });
|
|
279
291
|
var digestFile = path.join(knowledgeDir, "session-digests.jsonl");
|
|
280
292
|
fs.appendFileSync(digestFile, JSON.stringify(digestObj) + "\n");
|
|
293
|
+
mirrorMateJournal(mateDir, "session-digests.jsonl", job);
|
|
281
294
|
} catch (e) {
|
|
282
295
|
console.error("[digest-worker] Write failed for " + job.mateId + ":", e.message);
|
|
283
296
|
}
|
|
@@ -302,6 +315,7 @@ function attachMateInteraction(ctx) {
|
|
|
302
315
|
}));
|
|
303
316
|
}
|
|
304
317
|
fs.appendFileSync(obsFile, obsLines.join("\n") + "\n");
|
|
318
|
+
mirrorMateJournal(mateDir, "user-observations.jsonl", job);
|
|
305
319
|
} catch (e) {
|
|
306
320
|
console.error("[digest-worker] Observations write failed for " + job.mateId + ":", e.message);
|
|
307
321
|
}
|