@rubytech/create-sitedesk-code 0.1.530 → 0.1.531
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/package.json +1 -1
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +4 -0
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/index.ts +4 -0
- package/payload/platform/lib/learning-constants/dist/index.d.ts +35 -0
- package/payload/platform/lib/learning-constants/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/learning-constants/dist/index.js +50 -0
- package/payload/platform/lib/learning-constants/dist/index.js.map +1 -0
- package/payload/platform/lib/learning-constants/package.json +7 -0
- package/payload/platform/lib/learning-constants/src/index.test.ts +47 -0
- package/payload/platform/lib/learning-constants/src/index.ts +57 -0
- package/payload/platform/lib/learning-constants/tsconfig.json +9 -0
- package/payload/platform/neo4j/schema.cypher +24 -0
- package/payload/platform/package.json +1 -1
- package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/insight/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +58 -10
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +7 -0
- package/payload/platform/plugins/docs/references/linkedin-extension.md +18 -9
- package/payload/platform/plugins/docs/references/session-retrospective.md +39 -0
- package/payload/platform/plugins/linkedin-extension/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/linkedin-extension/PLUGIN.md +7 -10
- package/payload/platform/plugins/linkedin-extension/extension/README.md +20 -17
- package/payload/platform/plugins/linkedin-extension/extension/background/sw.js +96 -13
- package/payload/platform/plugins/linkedin-extension/extension/manifest.json +3 -18
- package/payload/platform/plugins/linkedin-extension/extension/options/options.html +2 -2
- package/payload/platform/plugins/linkedin-extension/extension/options/options.js +1 -1
- package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +15 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +169 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/session-jsonl.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/session-jsonl.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/session-jsonl.test.js +95 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/session-jsonl.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/session-jsonl.d.ts +60 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/session-jsonl.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/session-jsonl.js +153 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/session-jsonl.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.js +6 -4
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.js +160 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.js +64 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-scan.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-scan.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-scan.test.js +78 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-scan.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-derive.d.ts +41 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-derive.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-derive.js +225 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-derive.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-recall.d.ts +31 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-recall.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-recall.js +67 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-recall.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-scan.d.ts +40 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-scan.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-scan.js +55 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/learnings-scan.js.map +1 -0
- package/payload/platform/plugins/memory/references/schema-base.md +3 -0
- package/payload/platform/plugins/memory/skills/learnings-recall/SKILL.md +27 -0
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +4 -4
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +3 -3
- package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +6 -2
- package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +5 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +3 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/learning-index.d.ts +28 -0
- package/payload/platform/services/claude-session-manager/dist/learning-index.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/learning-index.js +70 -0
- package/payload/platform/services/claude-session-manager/dist/learning-index.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +18 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +6 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +17 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
- package/payload/platform/templates/specialists/agents/librarian.md +1 -1
- package/payload/server/{chunk-2YMGNYA3.js → chunk-SVB5NZ64.js} +41 -30
- package/payload/server/{manager-XPRN5UF5.js → manager-4Q3DTLXO.js} +5 -1
- package/payload/server/server.js +65 -17
- package/payload/platform/plugins/linkedin-extension/extension/assets/pill.css +0 -52
- package/payload/platform/plugins/linkedin-extension/extension/content/profile.js +0 -100
- package/payload/platform/plugins/linkedin-extension/extension/content/thread.js +0 -102
|
@@ -13,7 +13,7 @@ insight surface, and it is called only when the operator asks for it.
|
|
|
13
13
|
|
|
14
14
|
Run the four passes now over the session so far, then keep going in this same session. Do NOT call `session-reset`, do NOT clear the context, do NOT end the session: the conversation continues afterward and the operator (or a parallel session) keeps working against the same live state. Use the tools you already have.
|
|
15
15
|
|
|
16
|
-
Pass 1 — Technical learnings. Walk this session for operator corrections and self-detected mistakes. For each one, delegate one graph write to `database-operator` via the Task tool, persisting a Learning
|
|
16
|
+
Pass 1 — Technical learnings. Walk this session for operator corrections and self-detected mistakes. For each one, delegate one graph write to `database-operator` via the Task tool, persisting a Learning node per the schema. Count what you write.
|
|
17
17
|
|
|
18
18
|
Pass 2 — Operator-relationship updates. Walk this session for tonal signals, rejected phrasings, and working-style preferences. For each one worth carrying forward, Write the observation into `agents/admin/SOUL.md`, the account-scoped identity path the spawn composer reads. That path is the only identity file; never write a bare `SOUL.md` at the account root, which the composer never reads and the schema guard blocks. Count what you write.
|
|
19
19
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: platform-architecture
|
|
3
3
|
description: Use when grounding any documented-surface claim about what SiteDesk ships — plugins, skills, specialists, install/deploy flows, internals. This is the install catalogue, not evidence of what is enabled on the current account. For install state on this account, call `capabilities-here`; for documented surface, cite the `Source:` URL inline.
|
|
4
|
-
content-hash: sha256:
|
|
4
|
+
content-hash: sha256:10987c045e406cec5ecac35344dbae665a350a08b55ef996d2691be4bff0605f
|
|
5
5
|
brand: sitedesk-code
|
|
6
6
|
product-name: SiteDesk
|
|
7
7
|
---
|
|
@@ -2855,13 +2855,16 @@ Capture a LinkedIn profile or DM thread to your SiteDesk graph with one click. T
|
|
|
2855
2855
|
6. Paste two values:
|
|
2856
2856
|
- **Admin host** — your tunnel URL, e.g. `https://your-tunnel.example.com`.
|
|
2857
2857
|
- **Ingest token** — ask your SiteDesk agent for the ingest token. It mints one on request and hands it to you in the chat. The same value keeps working: it survives restarts and new chat sessions, and it does not expire. This is not a conversation session key, and not the admin browser's `session_key`; the route accepts neither.
|
|
2858
|
-
7. Save.
|
|
2858
|
+
7. Save. Pin the **SiteDesk LinkedIn Ingest** button in the Chrome toolbar so it is one click away.
|
|
2859
2859
|
|
|
2860
2860
|
## Use
|
|
2861
2861
|
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
- **
|
|
2862
|
+
Everything happens from the toolbar button. There is no button on the LinkedIn page itself, and that is deliberate: an extension that changes the page before LinkedIn's React finishes loading makes React throw the whole page away and rebuild it. This one only reads, and only when you click it.
|
|
2863
|
+
|
|
2864
|
+
- **Profile** — open any `https://www.linkedin.com/in/<slug>/` page and click the toolbar button. Within a few seconds the badge shows a green tick: the profile is in your graph.
|
|
2865
|
+
- **DM thread** — open any `https://www.linkedin.com/messaging/thread/<id>/` conversation and click the same button. The full transcript is captured plus the participants and any explicit commitments (meetings booked, actions promised, prices discussed).
|
|
2866
|
+
- **Any other page** — the badge shows a grey `?` and nothing is sent. Open a profile or a thread first.
|
|
2867
|
+
- **Re-click** — capture is idempotent. Re-clicking the same URL refreshes the document body and regenerates the summary; identities and entities `MERGE` rather than duplicate.
|
|
2865
2868
|
|
|
2866
2869
|
## What lands in the graph
|
|
2867
2870
|
|
|
@@ -2877,14 +2880,20 @@ Soft signals ("interested in chatting", "would love to compare notes") stay in t
|
|
|
2877
2880
|
|
|
2878
2881
|
The plugin will never create `:Communication`, `:ConversationArchive` rows (i.e. KnowledgeDocument nodes that carry `conversationIdentity`), or `:Message` nodes — those shapes are reserved for other flows (live chat, archive ingest).
|
|
2879
2882
|
|
|
2880
|
-
## When the
|
|
2883
|
+
## When the badge turns amber
|
|
2884
|
+
|
|
2885
|
+
Hover the button for the full sentence.
|
|
2881
2886
|
|
|
2882
|
-
|
|
2887
|
+
- **"rejected the ingest token"** — the token is missing or was refused. Open the options page, ask your SiteDesk agent for the ingest token, paste it, and save. There is no sign-in step: the token is the whole credential, and the only way it stops working is if someone rotates it.
|
|
2888
|
+
- **"never granted"** — the admin host was never allowed, so the request never left the browser. Open options, Save, and allow the Chrome prompt.
|
|
2883
2889
|
|
|
2884
|
-
## When the
|
|
2890
|
+
## When the badge turns red
|
|
2885
2891
|
|
|
2886
|
-
- **
|
|
2887
|
-
- **
|
|
2892
|
+
- **"could not read this page"** — LinkedIn shipped a DOM change and the extractor cannot find a required field. The tooltip names the missing fields, and the service-worker console carries the same names on the `op=scrape` line. Drop a ticket pointing at the affected selector; the [`SKILL.md`](../../plugins/linkedin-extension/skills/linkedin-extension/SKILL.md) lists the selector table and the steps for adding a fallback.
|
|
2893
|
+
- **"did not answer"** — the host was allowed but nothing responded. Check the tunnel and the admin server.
|
|
2894
|
+
- **"unexpected status"** — the admin was reached but the request did not complete cleanly. Check the admin logs with `grep linkedin-ingest ~/.<brand>/logs/server.log`; the `[linkedin-ingest-auth]` and `[linkedin-ingest-route]` lines name the reason (`token-unknown`, `schema`, `dispatch-failed`).
|
|
2895
|
+
|
|
2896
|
+
To read the extension's own side, open `chrome://extensions`, find SiteDesk LinkedIn Ingest, and click **service worker**. The `op=scrape`, `op=permission`, `op=fetch` and `op=state` lines are one capture end to end.
|
|
2888
2897
|
|
|
2889
2898
|
## Related plugins
|
|
2890
2899
|
|
|
@@ -3055,6 +3064,45 @@ The pass is one or two messages from the agent, then a short summary of what was
|
|
|
3055
3064
|
|
|
3056
3065
|
You can tell the pass ran correctly by the final reply naming the five counts (learnings, tonal observations, graph updates, typed edges accepted, prose nodes scanned). The typed-edge pass is scoped by "what changed since the last completed insight pass," so two passes in a row never re-process the same nodes, and a session that never runs one simply defers its extraction to the next time you type `/insight`.
|
|
3057
3066
|
|
|
3067
|
+
# Learnings: how they accumulate, fade, and come back
|
|
3068
|
+
|
|
3069
|
+
An `/insight` pass writes learnings. Until now that is all that happened to
|
|
3070
|
+
them: they landed in the graph and nothing ever read them again. A learning is
|
|
3071
|
+
now a thing with a life.
|
|
3072
|
+
|
|
3073
|
+
**A learning carries a confidence.** It starts at 0.5. Every time a later
|
|
3074
|
+
session establishes the same thing again, the confidence moves most of the way
|
|
3075
|
+
toward certain without ever reaching it: `c + 0.15 × (1 − c)`, so 0.5 becomes
|
|
3076
|
+
0.575, then 0.64, and so on. When a session disputes it, 0.3 comes straight off
|
|
3077
|
+
and the learning is stamped with the date it was contradicted. If nobody
|
|
3078
|
+
mentions it for fourteen days it starts fading, at 0.05 a day, until it reaches
|
|
3079
|
+
zero.
|
|
3080
|
+
|
|
3081
|
+
That means the brain forgets on its own. Something established once and never
|
|
3082
|
+
seen again drops out of sight in about a month; something confirmed every week
|
|
3083
|
+
stays near the top forever. You do not have to prune anything by hand.
|
|
3084
|
+
|
|
3085
|
+
**Your session prompt carries titles, not text.** At the start of a session the
|
|
3086
|
+
agent is given the *titles* of the learnings currently above 0.4 confidence,
|
|
3087
|
+
capped at thirty, and nothing more. A title is a handle. When the conversation
|
|
3088
|
+
turns to something one of those titles names, the agent fetches the full text
|
|
3089
|
+
then, and only then. That is deliberate: injecting thirty full learnings into
|
|
3090
|
+
every session would cost a great deal of prompt for text that mostly goes
|
|
3091
|
+
unread.
|
|
3092
|
+
|
|
3093
|
+
**When a learning has been disputed, the agent says so.** The recall also
|
|
3094
|
+
returns how many separate sessions produced the learning and whether one of
|
|
3095
|
+
them later contradicted it, so the agent can tell you "we established this
|
|
3096
|
+
once, and then later decided otherwise" rather than repeating a stale
|
|
3097
|
+
conclusion as settled fact.
|
|
3098
|
+
|
|
3099
|
+
**What is not built yet.** Nothing runs this on a schedule. The extraction
|
|
3100
|
+
tools, the confidence curve, the fading and the session-start injection are all
|
|
3101
|
+
in place and work when driven by hand, but the overnight pass that walks every
|
|
3102
|
+
session closed since the previous night is a separate piece of work, filed as
|
|
3103
|
+
its own task and waiting on the routine-descriptions work to land first. Until
|
|
3104
|
+
then, `/insight` remains the way learnings get written.
|
|
3105
|
+
|
|
3058
3106
|
---
|
|
3059
3107
|
# Visitor Graph
|
|
3060
3108
|
Source: https://docs.getmaxy.com/visitor-graph.md
|
|
@@ -9,6 +9,13 @@ Invoked by the admin agent directly.
|
|
|
9
9
|
|
|
10
10
|
This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
|
|
11
11
|
|
|
12
|
+
## 2026-07-30 (0.1.531)
|
|
13
|
+
|
|
14
|
+
- Each WhatsApp connection is now recorded against the account it actually belongs to, rather than every connection being filed under the main account. A standing check reports any connection whose account has drifted, and an account created since the last restart is no longer mistaken for the main one.
|
|
15
|
+
- Setting up WhatsApp for a sub-account is now reachable. Asking about a connection without naming an account now means your own account rather than the main one, the status view is no longer limited to the main account, and a sub-account line can run alongside the main one.
|
|
16
|
+
- The assistant now learns from past sessions. It draws lessons out of earlier conversations, lets ones that stop being reconfirmed fade away, and brings only the relevant ones into a new conversation.
|
|
17
|
+
- Capturing a page from the LinkedIn extension now happens from the toolbar button, replacing the floating marker that could be wiped away when the page redrew.
|
|
18
|
+
|
|
12
19
|
## 2026-07-30 (0.1.530)
|
|
13
20
|
|
|
14
21
|
- Security fix: the WhatsApp connection library has been moved to a patched release, closing a critical flaw that allowed messages to be faked. Installs on the previous version should update.
|
|
@@ -12,13 +12,16 @@ Capture a LinkedIn profile or DM thread to your SiteDesk graph with one click. T
|
|
|
12
12
|
6. Paste two values:
|
|
13
13
|
- **Admin host** — your tunnel URL, e.g. `https://your-tunnel.example.com`.
|
|
14
14
|
- **Ingest token** — ask your SiteDesk agent for the ingest token. It mints one on request and hands it to you in the chat. The same value keeps working: it survives restarts and new chat sessions, and it does not expire. This is not a conversation session key, and not the admin browser's `session_key`; the route accepts neither.
|
|
15
|
-
7. Save.
|
|
15
|
+
7. Save. Pin the **SiteDesk LinkedIn Ingest** button in the Chrome toolbar so it is one click away.
|
|
16
16
|
|
|
17
17
|
## Use
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- **
|
|
19
|
+
Everything happens from the toolbar button. There is no button on the LinkedIn page itself, and that is deliberate: an extension that changes the page before LinkedIn's React finishes loading makes React throw the whole page away and rebuild it. This one only reads, and only when you click it.
|
|
20
|
+
|
|
21
|
+
- **Profile** — open any `https://www.linkedin.com/in/<slug>/` page and click the toolbar button. Within a few seconds the badge shows a green tick: the profile is in your graph.
|
|
22
|
+
- **DM thread** — open any `https://www.linkedin.com/messaging/thread/<id>/` conversation and click the same button. The full transcript is captured plus the participants and any explicit commitments (meetings booked, actions promised, prices discussed).
|
|
23
|
+
- **Any other page** — the badge shows a grey `?` and nothing is sent. Open a profile or a thread first.
|
|
24
|
+
- **Re-click** — capture is idempotent. Re-clicking the same URL refreshes the document body and regenerates the summary; identities and entities `MERGE` rather than duplicate.
|
|
22
25
|
|
|
23
26
|
## What lands in the graph
|
|
24
27
|
|
|
@@ -34,14 +37,20 @@ Soft signals ("interested in chatting", "would love to compare notes") stay in t
|
|
|
34
37
|
|
|
35
38
|
The plugin will never create `:Communication`, `:ConversationArchive` rows (i.e. KnowledgeDocument nodes that carry `conversationIdentity`), or `:Message` nodes — those shapes are reserved for other flows (live chat, archive ingest).
|
|
36
39
|
|
|
37
|
-
## When the
|
|
40
|
+
## When the badge turns amber
|
|
41
|
+
|
|
42
|
+
Hover the button for the full sentence.
|
|
43
|
+
|
|
44
|
+
- **"rejected the ingest token"** — the token is missing or was refused. Open the options page, ask your SiteDesk agent for the ingest token, paste it, and save. There is no sign-in step: the token is the whole credential, and the only way it stops working is if someone rotates it.
|
|
45
|
+
- **"never granted"** — the admin host was never allowed, so the request never left the browser. Open options, Save, and allow the Chrome prompt.
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
## When the badge turns red
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
- **"could not read this page"** — LinkedIn shipped a DOM change and the extractor cannot find a required field. The tooltip names the missing fields, and the service-worker console carries the same names on the `op=scrape` line. Drop a ticket pointing at the affected selector; the [`SKILL.md`](../../plugins/linkedin-extension/skills/linkedin-extension/SKILL.md) lists the selector table and the steps for adding a fallback.
|
|
50
|
+
- **"did not answer"** — the host was allowed but nothing responded. Check the tunnel and the admin server.
|
|
51
|
+
- **"unexpected status"** — the admin was reached but the request did not complete cleanly. Check the admin logs with `grep linkedin-ingest ~/.<brand>/logs/server.log`; the `[linkedin-ingest-auth]` and `[linkedin-ingest-route]` lines name the reason (`token-unknown`, `schema`, `dispatch-failed`).
|
|
42
52
|
|
|
43
|
-
|
|
44
|
-
- **Capture failed** — the admin reached, but the request did not complete cleanly. Check the admin logs (`journalctl -u maxy.service | grep linkedin-ingest`). The `[linkedin-ingest-route]` lines name the reason (`schema`, `dispatch-failed`).
|
|
53
|
+
To read the extension's own side, open `chrome://extensions`, find SiteDesk LinkedIn Ingest, and click **service worker**. The `op=scrape`, `op=permission`, `op=fetch` and `op=state` lines are one capture end to end.
|
|
45
54
|
|
|
46
55
|
## Related plugins
|
|
47
56
|
|
|
@@ -12,3 +12,42 @@ You can run it whenever you want the brain brought up to date — for example be
|
|
|
12
12
|
The pass is one or two messages from the agent, then a short summary of what was written. It runs inside the session you are already in: nothing happens in the background, no second session is spawned, and the session is not closed, reset, or cleared — the conversation continues straight afterward against the same live state. The typed-edge pass itself is delegated to the `database-operator` specialist so the writes land where graph writes are supposed to live.
|
|
13
13
|
|
|
14
14
|
You can tell the pass ran correctly by the final reply naming the five counts (learnings, tonal observations, graph updates, typed edges accepted, prose nodes scanned). The typed-edge pass is scoped by "what changed since the last completed insight pass," so two passes in a row never re-process the same nodes, and a session that never runs one simply defers its extraction to the next time you type `/insight`.
|
|
15
|
+
|
|
16
|
+
# Learnings: how they accumulate, fade, and come back
|
|
17
|
+
|
|
18
|
+
An `/insight` pass writes learnings. Until now that is all that happened to
|
|
19
|
+
them: they landed in the graph and nothing ever read them again. A learning is
|
|
20
|
+
now a thing with a life.
|
|
21
|
+
|
|
22
|
+
**A learning carries a confidence.** It starts at 0.5. Every time a later
|
|
23
|
+
session establishes the same thing again, the confidence moves most of the way
|
|
24
|
+
toward certain without ever reaching it: `c + 0.15 × (1 − c)`, so 0.5 becomes
|
|
25
|
+
0.575, then 0.64, and so on. When a session disputes it, 0.3 comes straight off
|
|
26
|
+
and the learning is stamped with the date it was contradicted. If nobody
|
|
27
|
+
mentions it for fourteen days it starts fading, at 0.05 a day, until it reaches
|
|
28
|
+
zero.
|
|
29
|
+
|
|
30
|
+
That means the brain forgets on its own. Something established once and never
|
|
31
|
+
seen again drops out of sight in about a month; something confirmed every week
|
|
32
|
+
stays near the top forever. You do not have to prune anything by hand.
|
|
33
|
+
|
|
34
|
+
**Your session prompt carries titles, not text.** At the start of a session the
|
|
35
|
+
agent is given the *titles* of the learnings currently above 0.4 confidence,
|
|
36
|
+
capped at thirty, and nothing more. A title is a handle. When the conversation
|
|
37
|
+
turns to something one of those titles names, the agent fetches the full text
|
|
38
|
+
then, and only then. That is deliberate: injecting thirty full learnings into
|
|
39
|
+
every session would cost a great deal of prompt for text that mostly goes
|
|
40
|
+
unread.
|
|
41
|
+
|
|
42
|
+
**When a learning has been disputed, the agent says so.** The recall also
|
|
43
|
+
returns how many separate sessions produced the learning and whether one of
|
|
44
|
+
them later contradicted it, so the agent can tell you "we established this
|
|
45
|
+
once, and then later decided otherwise" rather than repeating a stale
|
|
46
|
+
conclusion as settled fact.
|
|
47
|
+
|
|
48
|
+
**What is not built yet.** Nothing runs this on a schedule. The extraction
|
|
49
|
+
tools, the confidence curve, the fading and the session-start injection are all
|
|
50
|
+
in place and work when driven by hand, but the overnight pass that walks every
|
|
51
|
+
session closed since the previous night is a separate piece of work, filed as
|
|
52
|
+
its own task and waiting on the routine-descriptions work to land first. Until
|
|
53
|
+
then, `/insight` remains the way learnings get written.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linkedin-extension",
|
|
3
|
-
"description": "One-click LinkedIn ingest. Ships a manifest-v3 Chrome extension
|
|
3
|
+
"description": "One-click LinkedIn ingest. Ships a manifest-v3 Chrome extension whose toolbar button captures the current `linkedin.com/in/*` profile or `linkedin.com/messaging/thread/*` DM thread. A click injects the extractors into that tab under `activeTab`, reads the page DOM without writing to it, POSTs to the admin `linkedin-ingest` route, which dispatches `database-operator` against the generic `document-ingest` skill. The plugin is the **first consumer** of the generic communication-ingest path; every future communication surface (email, Telegram, WhatsApp, group chats, voice memos) plugs into the same backend. Opt-in per brand.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: linkedin-extension
|
|
3
|
-
description: "One-click LinkedIn ingest. Ships a manifest-v3 Chrome extension
|
|
3
|
+
description: "One-click LinkedIn ingest. Ships a manifest-v3 Chrome extension whose toolbar button captures the current `linkedin.com/in/*` profile or `linkedin.com/messaging/thread/*` DM thread. A click injects the extractors into that tab under `activeTab`, reads the page DOM without writing to it, POSTs to the admin `linkedin-ingest` route, which dispatches `database-operator` against the generic `document-ingest` skill. The plugin is the **first consumer** of the generic communication-ingest path; every future communication surface (email, Telegram, WhatsApp, group chats, voice memos) plugs into the same backend. Opt-in per brand."
|
|
4
4
|
tools: []
|
|
5
5
|
always: false
|
|
6
6
|
embed: false
|
|
@@ -26,10 +26,10 @@ Re-clicking the same URL refreshes the KD body and regenerates the summary. Pers
|
|
|
26
26
|
```
|
|
27
27
|
Chrome extension Hono admin Specialist
|
|
28
28
|
───────────────── ────────── ──────────
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
toolbar click /api/admin/linkedin-ingest ──> database-operator
|
|
30
|
+
inject extractors.js requireIngestToken document-ingest skill
|
|
31
|
+
read the page ──POST──> schema validate memory-ingest tools
|
|
32
|
+
badge + tooltip spawn database-operator graph writes
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
The route's job is auth + schema + dispatch. Extraction logic lives in `document-ingest`; ontology decisions live in the schema reference. The extension is the only LinkedIn-bespoke part.
|
|
@@ -37,11 +37,8 @@ The route's job is auth + schema + dispatch. Extraction logic lives in `document
|
|
|
37
37
|
## Files
|
|
38
38
|
|
|
39
39
|
- `extension/manifest.json` — manifest v3
|
|
40
|
-
- `extension/
|
|
41
|
-
- `extension/
|
|
42
|
-
- `extension/background/sw.js` — service worker handling POSTs
|
|
43
|
-
- `extension/options/options.{html,js}` — admin host + bearer token entry
|
|
44
|
-
- `extension/assets/pill.css` — pill styling
|
|
40
|
+
- `extension/background/sw.js` — service worker. Owns the capture: injects on toolbar click, reads, POSTs, reports via badge + tooltip
|
|
41
|
+
- `extension/options/options.{html,js}` — admin host + ingest token entry
|
|
45
42
|
- `extension/README.md` — developer install guide
|
|
46
43
|
- `extension/__tests__/{profile,thread}.test.ts` — jsdom extractor tests
|
|
47
44
|
- `extension/__tests__/fixtures/{profile,thread}.html` — frozen DOM fixtures captured 2026-05-22
|
|
@@ -9,35 +9,38 @@ Unpacked-extension dev build. No Chrome Web Store publication.
|
|
|
9
9
|
3. **Load unpacked** → select this `extension/` directory.
|
|
10
10
|
4. Click the new puzzle icon → open **SiteDesk LinkedIn Ingest** options.
|
|
11
11
|
5. Paste your admin host (e.g. `https://your-tunnel.example.com`) and your **ingest token**. Ask your SiteDesk agent for the ingest token; it mints one on request and gives it to you in the chat. The token is stable, so this is a one-time paste unless someone rotates it. Save.
|
|
12
|
-
6. Chrome asks you to allow the extension to talk to that host. **Allow it.**
|
|
12
|
+
6. Chrome asks you to allow the extension to talk to that host. **Allow it.** Captures cannot post without it, so declining saves nothing.
|
|
13
13
|
|
|
14
14
|
## Use
|
|
15
15
|
|
|
16
16
|
- Open any `https://www.linkedin.com/in/<slug>/` profile or `https://www.linkedin.com/messaging/thread/<id>/` thread.
|
|
17
|
-
- Click the **
|
|
18
|
-
- The
|
|
17
|
+
- Click the **SiteDesk LinkedIn Ingest** button in the Chrome toolbar. Pin it first if it is hidden behind the puzzle icon.
|
|
18
|
+
- The badge shows a green tick and SiteDesk writes one `:KnowledgeDocument` plus the entities the body assertively states.
|
|
19
19
|
|
|
20
|
-
The
|
|
20
|
+
There is no button on the page itself. The extension reads the page only when you click, and never changes it: an extension that alters the HTML before LinkedIn's React hydrates raises React error #418, which makes React throw the page away and rebuild it. That is what the old in-page pill did to every profile you opened.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
The badge says what happened; hover the button for the full sentence:
|
|
23
|
+
|
|
24
|
+
| Badge | Meaning | Fix |
|
|
23
25
|
|---|---|---|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
26
|
+
| `✓` green | captured | nothing to do |
|
|
27
|
+
| `…` grey | capture in progress | wait |
|
|
28
|
+
| `!` amber, "rejected the ingest token" | the token is missing or was refused | ask your SiteDesk agent for the ingest token, paste it in options |
|
|
29
|
+
| `!` amber, "never granted" | the admin host was never allowed, so the post never left the browser | open options, Save, allow the prompt |
|
|
30
|
+
| `!` red, "did not answer" | the host was allowed but did not answer | check the tunnel and the admin server |
|
|
31
|
+
| `!` red, "unexpected status" | the admin rejected the payload | read the service-worker console for the `op=fetch` line |
|
|
32
|
+
| `!` red, "could not read this page" | the extractors found no required field | LinkedIn's markup moved; the tooltip names the missing fields |
|
|
33
|
+
| `?` grey | the tab is not a profile or DM thread | open one first |
|
|
28
34
|
|
|
29
35
|
## File layout
|
|
30
36
|
|
|
31
37
|
| File | Role |
|
|
32
38
|
|---|---|
|
|
33
|
-
| `manifest.json` | MV3 manifest.
|
|
34
|
-
| `content/extractors.js` | Pure DOM extractor functions
|
|
35
|
-
| `
|
|
36
|
-
| `content/thread.js` | Thread pill + click handler. |
|
|
37
|
-
| `background/sw.js` | Service worker. Checks the admin-host grant, then POSTs envelopes to the admin route. |
|
|
39
|
+
| `manifest.json` | MV3 manifest. No content scripts and no standing host permission: `activeTab` plus `scripting` grant access to the tab you click. |
|
|
40
|
+
| `content/extractors.js` | Pure DOM extractor functions, injected on click. |
|
|
41
|
+
| `background/sw.js` | Service worker. Owns the whole capture: injects, reads, POSTs, and reports through the badge and tooltip. |
|
|
38
42
|
| `options/options.html` + `options/options.js` | Admin host + ingest-token entry. |
|
|
39
|
-
| `
|
|
40
|
-
| `__tests__/` | jsdom unit tests for the extractors, run via vitest from `platform/ui`. |
|
|
43
|
+
| `__tests__/` | jsdom fixtures for the extractors, run via vitest from `platform/ui`. |
|
|
41
44
|
|
|
42
45
|
## How the wire works
|
|
43
46
|
|
|
@@ -45,7 +48,7 @@ The extension does no extraction or graph-write logic — those live in `platfor
|
|
|
45
48
|
|
|
46
49
|
## Updating selectors
|
|
47
50
|
|
|
48
|
-
LinkedIn ships DOM changes from time to time. When
|
|
51
|
+
LinkedIn ships DOM changes from time to time. When a capture reports missing fields:
|
|
49
52
|
|
|
50
53
|
1. Save the current page as `__tests__/fixtures/<kind>.html` (overwrite).
|
|
51
54
|
2. Update the primary selector in `content/extractors.js`; keep the prior one as fallback if older clients may still serve it.
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
// Service worker.
|
|
2
|
-
//
|
|
1
|
+
// Service worker. Owns the whole capture: a toolbar click injects the
|
|
2
|
+
// extractors into the active tab, reads the page, and POSTs the envelope to
|
|
3
|
+
// the admin host with the ingest token stored in extension options.
|
|
4
|
+
//
|
|
5
|
+
// Task 2221 — capture moved here from an in-page pill. The pill was prepended
|
|
6
|
+
// into LinkedIn's `main` before React hydrated, which raised React #418 and
|
|
7
|
+
// had the tree regenerated, destroying the pill and degrading the page. The
|
|
8
|
+
// extension now injects only on an explicit click, under `activeTab`, so it
|
|
9
|
+
// holds no standing access to linkedin.com and never mutates the page.
|
|
3
10
|
//
|
|
4
11
|
// Task 2185 — the manifest's static host_permissions cover linkedin.com only,
|
|
5
12
|
// so the admin host has to be granted at runtime from the options page before
|
|
@@ -82,17 +89,93 @@ async function postEnvelope(envelope) {
|
|
|
82
89
|
return { ok: true }
|
|
83
90
|
}
|
|
84
91
|
|
|
85
|
-
chrome.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
92
|
+
// Injected into the page by chrome.scripting with `func`, so it is serialised
|
|
93
|
+
// and cannot close over anything in this file. Everything it needs is inline.
|
|
94
|
+
//
|
|
95
|
+
// It reads the DOM and returns; it never writes. That is the whole point of
|
|
96
|
+
// Task 2221: the previous in-page pill was prepended into `main` before React
|
|
97
|
+
// hydrated, which raised React #418 ("Hydration failed ... this tree will be
|
|
98
|
+
// regenerated on the client") and the regeneration took the pill with it. An
|
|
99
|
+
// extension that only reads cannot mismatch hydration.
|
|
100
|
+
function captureInPage() {
|
|
101
|
+
const ex = globalThis.MaxyLinkedinExtractors
|
|
102
|
+
if (!ex) return { ok: false, reason: 'extract', detail: 'extractors-missing' }
|
|
103
|
+
const path = location.pathname
|
|
104
|
+
let kind = null
|
|
105
|
+
if (/^\/in\/[^/]+/.test(path)) kind = 'profile'
|
|
106
|
+
else if (/^\/messaging\/thread\/[^/]+/.test(path)) kind = 'thread'
|
|
107
|
+
if (!kind) return { ok: false, reason: 'wrong-page', detail: path }
|
|
108
|
+
const result = kind === 'profile' ? ex.extractProfile(document) : ex.extractThread(document)
|
|
109
|
+
if (!result.ok) return { ok: false, reason: 'extract', detail: result.missing.join(',') }
|
|
110
|
+
return {
|
|
111
|
+
ok: true,
|
|
112
|
+
kind: kind,
|
|
113
|
+
pageUrl: location.href.split('?')[0].split('#')[0],
|
|
114
|
+
payload: result.payload,
|
|
89
115
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Badge carries the glyph, title carries the sentence. A toolbar button cannot
|
|
119
|
+
// both fire onClicked and open a popup — declaring `default_popup` suppresses
|
|
120
|
+
// onClicked — so the tooltip is where the reason lives. A badge alone cannot
|
|
121
|
+
// separate Task 2185's three reasons from Task 2208's token states.
|
|
122
|
+
const STATES = {
|
|
123
|
+
busy: { text: '…', color: '#46596d', title: 'Capturing…' },
|
|
124
|
+
ok: { text: '✓', color: '#2e7d32', title: 'Captured to SiteDesk' },
|
|
125
|
+
auth: { text: '!', color: '#b8860b', title: 'SiteDesk rejected the ingest token. Open options and paste a fresh one.' },
|
|
126
|
+
permission: { text: '!', color: '#b8860b', title: 'The SiteDesk admin host was never granted. Open options and save to grant it.' },
|
|
127
|
+
unreachable: { text: '!', color: '#b00020', title: 'The SiteDesk admin host did not answer. Check the tunnel and the admin server.' },
|
|
128
|
+
server: { text: '!', color: '#b00020', title: 'SiteDesk answered with an unexpected status.' },
|
|
129
|
+
extract: { text: '!', color: '#b00020', title: 'Could not read this page.' },
|
|
130
|
+
'wrong-page': { text: '?', color: '#46596d', title: 'Open a LinkedIn profile or DM thread first.' },
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function setState(tabId, state, detail) {
|
|
134
|
+
const s = STATES[state] || STATES.extract
|
|
135
|
+
const title = detail ? s.title + ' (' + detail + ')' : s.title
|
|
136
|
+
await chrome.action.setBadgeText({ tabId: tabId, text: s.text })
|
|
137
|
+
await chrome.action.setBadgeBackgroundColor({ tabId: tabId, color: s.color })
|
|
138
|
+
await chrome.action.setTitle({ tabId: tabId, title: title })
|
|
139
|
+
console.log(TAG + ' op=state tabId=' + tabId + ' state=' + state + (detail ? ' detail=' + detail : ''))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function runCapture(tab) {
|
|
143
|
+
const dispatchId = crypto.randomUUID()
|
|
144
|
+
await setState(tab.id, 'busy')
|
|
95
145
|
|
|
96
|
-
|
|
97
|
-
|
|
146
|
+
let injected
|
|
147
|
+
try {
|
|
148
|
+
await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['content/extractors.js'] })
|
|
149
|
+
injected = await chrome.scripting.executeScript({ target: { tabId: tab.id }, func: captureInPage })
|
|
150
|
+
} catch (err) {
|
|
151
|
+
// activeTab was not granted, or this is a page no extension may touch.
|
|
152
|
+
return setState(tab.id, 'wrong-page', err && err.message ? err.message : String(err))
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const scraped = injected && injected[0] ? injected[0].result : null
|
|
156
|
+
if (!scraped || !scraped.ok) {
|
|
157
|
+
const reason = scraped ? scraped.reason : 'extract'
|
|
158
|
+
console.error(TAG + ' op=scrape dispatchId=' + dispatchId + ' ok=false reason=' + reason)
|
|
159
|
+
return setState(tab.id, reason, scraped ? scraped.detail : 'no-result')
|
|
160
|
+
}
|
|
161
|
+
console.log(TAG + ' op=scrape dispatchId=' + dispatchId + ' kind=' + scraped.kind + ' ok=true')
|
|
162
|
+
|
|
163
|
+
const envelope = {
|
|
164
|
+
source: 'linkedin',
|
|
165
|
+
kind: scraped.kind,
|
|
166
|
+
capturedAt: new Date().toISOString(),
|
|
167
|
+
pageUrl: scraped.pageUrl,
|
|
168
|
+
dispatchId: dispatchId,
|
|
169
|
+
}
|
|
170
|
+
envelope[scraped.kind] = scraped.payload
|
|
171
|
+
|
|
172
|
+
const posted = await postEnvelope(envelope)
|
|
173
|
+
if (posted.ok) return setState(tab.id, 'ok')
|
|
174
|
+
return setState(tab.id, posted.reason, posted.detail)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
chrome.action.onClicked.addListener(function (tab) {
|
|
178
|
+
runCapture(tab).catch(function (err) {
|
|
179
|
+
setState(tab.id, 'extract', err && err.message ? err.message : String(err))
|
|
180
|
+
})
|
|
98
181
|
})
|
|
@@ -2,32 +2,17 @@
|
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "SiteDesk LinkedIn Ingest",
|
|
4
4
|
"version": "0.1.0",
|
|
5
|
-
"description": "
|
|
6
|
-
"permissions": ["storage", "activeTab"],
|
|
7
|
-
"host_permissions": ["https://www.linkedin.com/*"],
|
|
5
|
+
"description": "Click the toolbar button on a LinkedIn profile or DM thread to capture it into your SiteDesk admin.",
|
|
6
|
+
"permissions": ["storage", "activeTab", "scripting"],
|
|
8
7
|
"optional_host_permissions": ["http://*/*", "https://*/*"],
|
|
9
8
|
"background": {
|
|
10
9
|
"service_worker": "background/sw.js"
|
|
11
10
|
},
|
|
12
|
-
"content_scripts": [
|
|
13
|
-
{
|
|
14
|
-
"matches": ["https://www.linkedin.com/in/*"],
|
|
15
|
-
"js": ["content/extractors.js", "content/profile.js"],
|
|
16
|
-
"css": ["assets/pill.css"],
|
|
17
|
-
"run_at": "document_idle"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"matches": ["https://www.linkedin.com/messaging/thread/*"],
|
|
21
|
-
"js": ["content/extractors.js", "content/thread.js"],
|
|
22
|
-
"css": ["assets/pill.css"],
|
|
23
|
-
"run_at": "document_idle"
|
|
24
|
-
}
|
|
25
|
-
],
|
|
26
11
|
"options_ui": {
|
|
27
12
|
"page": "options/options.html",
|
|
28
13
|
"open_in_tab": true
|
|
29
14
|
},
|
|
30
15
|
"action": {
|
|
31
|
-
"default_title": "
|
|
16
|
+
"default_title": "Capture this LinkedIn page to SiteDesk"
|
|
32
17
|
}
|
|
33
18
|
}
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
</head>
|
|
18
18
|
<body>
|
|
19
19
|
<h1>SiteDesk LinkedIn Ingest</h1>
|
|
20
|
-
<p>Point the extension at your SiteDesk admin host and paste your ingest token. Ask your SiteDesk agent for the token and it will give you one. The
|
|
21
|
-
<p>On Save, Chrome asks you to allow this extension to talk to that host.
|
|
20
|
+
<p>Point the extension at your SiteDesk admin host and paste your ingest token. Ask your SiteDesk agent for the token and it will give you one. The toolbar button will then post one-click captures to your admin.</p>
|
|
21
|
+
<p>On Save, Chrome asks you to allow this extension to talk to that host. Captures cannot post without it, so declining leaves the extension unconfigured.</p>
|
|
22
22
|
|
|
23
23
|
<label for="admin-host">Admin host</label>
|
|
24
24
|
<input id="admin-host" type="url" placeholder="https://your-tunnel.example.com" />
|
|
@@ -96,7 +96,7 @@ async function save() {
|
|
|
96
96
|
console.log(TAG + ' op=permission origin=' + origin + ' granted=' + granted)
|
|
97
97
|
if (!granted) {
|
|
98
98
|
return setStatus(
|
|
99
|
-
'Access to ' + origin + ' was declined, so nothing was saved.
|
|
99
|
+
'Access to ' + origin + ' was declined, so nothing was saved. Captures cannot post without it.',
|
|
100
100
|
true,
|
|
101
101
|
)
|
|
102
102
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memory",
|
|
3
|
-
"description": "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-write, memory-update, memory-edge (create/delete typed directed edges between pre-existing nodes), memory-update-by-name (repair surface that resolves an elementId from a (label, name, accountId) tuple when search cannot return one), memory-lookup-by-name (deterministic read surface that returns nodes by case-insensitive exact `name` match — a Person, which carries no `name`, is matched on its composed `givenName` + `familyName` display name instead — optionally constrained by labels, bypassing the memory-search ranking stack so a present entity is never missed because a diluted query ranked it below the cut), and the :Report surface (memory-report-write / memory-report-read-latest / memory-report-list) for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses. Document ingestion goes through memory-ingest; the dispatched specialist produces typed-section JSON in-turn from the loaded ontology. Operator-solicited reclassify of an already-stored thread-shaped KD (today: email-thread KDs) is exposed as `kd-classify <attachmentId>` — runs a body-growth gate (proceed iff body grew ≥25% since the last classify, or first-ever classify), stages the body to a temp file under `$ACCOUNT_DIR/tmp/`, and returns a dispatch envelope for the `librarian`; on success `memory-ingest` replaces the prior :Section children and stamps `lastClassifiedAt` + `lastClassifiedBodyLength` on the parent KD. Two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :ConversationArchive (keyed on `conversationIdentity`) + :Section chunks; two parent labels, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 splits across two read-only tools: `conversation-archive-list-chunks` pages chunk bodies for the dispatched specialist to read in-turn; `conversation-archive-derive-insights` converts the specialist's per-chunk claims into operator-facing proposals with per-kind cypher. `conversation-archive-enrich-rejection` records (or undoes) durable per-row rejections so already-triaged claims do not re-surface on re-runs. Ships five skills: `conversational-memory`, `document-ingest`, `conversation-archive` (source-agnostic transcript ingest for WhatsApp, Telegram, Signal, LinkedIn DMs, Zoom, meeting minutes, iMessage, Slack), `conversation-archive-mcp` (operator-initiated MCP-driven ingest for Granola, Otter, Circleback meetings — sibling of `conversation-archive` sharing the same writer surface and identity formula), and `conversation-archive-enrich` (per-row operator-gated insight derivation over a named archive's chunks). Ships three thinking-tool skills: `challenge` (adversarial retrieval — strongest counter-evidence from the graph for a given claim), `connect` (bridge-finding between two topics via shared graph neighbors), and `emerge` (clusters uncategorised KnowledgeDocument and Section nodes into operator-approved Concept proposals).",
|
|
3
|
+
"description": "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-write, memory-update, memory-edge (create/delete typed directed edges between pre-existing nodes), memory-update-by-name (repair surface that resolves an elementId from a (label, name, accountId) tuple when search cannot return one), memory-lookup-by-name (deterministic read surface that returns nodes by case-insensitive exact `name` match — a Person, which carries no `name`, is matched on its composed `givenName` + `familyName` display name instead — optionally constrained by labels, bypassing the memory-search ranking stack so a present entity is never missed because a diluted query ranked it below the cut), and the :Report surface (memory-report-write / memory-report-read-latest / memory-report-list) for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses. Document ingestion goes through memory-ingest; the dispatched specialist produces typed-section JSON in-turn from the loaded ontology. Operator-solicited reclassify of an already-stored thread-shaped KD (today: email-thread KDs) is exposed as `kd-classify <attachmentId>` — runs a body-growth gate (proceed iff body grew ≥25% since the last classify, or first-ever classify), stages the body to a temp file under `$ACCOUNT_DIR/tmp/`, and returns a dispatch envelope for the `librarian`; on success `memory-ingest` replaces the prior :Section children and stamps `lastClassifiedAt` + `lastClassifiedBodyLength` on the parent KD. Two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :ConversationArchive (keyed on `conversationIdentity`) + :Section chunks; two parent labels, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 splits across two read-only tools: `conversation-archive-list-chunks` pages chunk bodies for the dispatched specialist to read in-turn; `conversation-archive-derive-insights` converts the specialist's per-chunk claims into operator-facing proposals with per-kind cypher. `conversation-archive-enrich-rejection` records (or undoes) durable per-row rejections so already-triaged claims do not re-surface on re-runs. Ships five skills: `conversational-memory`, `document-ingest`, `conversation-archive` (source-agnostic transcript ingest for WhatsApp, Telegram, Signal, LinkedIn DMs, Zoom, meeting minutes, iMessage, Slack), `conversation-archive-mcp` (operator-initiated MCP-driven ingest for Granola, Otter, Circleback meetings — sibling of `conversation-archive` sharing the same writer surface and identity formula), and `conversation-archive-enrich` (per-row operator-gated insight derivation over a named archive's chunks). Ships three thinking-tool skills: `challenge` (adversarial retrieval — strongest counter-evidence from the graph for a given claim), `connect` (bridge-finding between two topics via shared graph neighbors), and `emerge` (clusters uncategorised KnowledgeDocument and Section nodes into operator-approved Concept proposals). The learnings surface is three tools plus the `learnings-recall` skill: `learnings-scan` pages session transcripts off disk (requiring CLAUDE_CONFIG_DIR, refusing without it rather than guessing a projects root), `learnings-derive` reconciles the turn's claims into `:Learning` writes on a confidence curve (create at 0.5, reinforce as c + 0.15(1-c), contradict by 0.3 with contradictedAt stamped) and optionally runs the decay pass, and `learnings-recall` is the only path from a title injected into a system prompt to its full body.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: memory
|
|
3
|
-
description: "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-write, memory-update, memory-edge (create/delete typed directed edges between pre-existing nodes), memory-update-by-name (repair surface that resolves an elementId from a (label, name, accountId) tuple when search cannot return one), memory-lookup-by-name (deterministic read surface that returns nodes by case-insensitive exact `name` match — a Person, which carries no `name`, is matched on its composed `givenName` + `familyName` display name instead — optionally constrained by labels, bypassing the memory-search ranking stack so a present entity is never missed because a diluted query ranked it below the cut), and the :Report surface (memory-report-write / memory-report-read-latest / memory-report-list) for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses. Document ingestion goes through memory-ingest; the dispatched specialist produces typed-section JSON in-turn from the loaded ontology. Operator-solicited reclassify of an already-stored thread-shaped KD (today: email-thread KDs) is exposed as `kd-classify <attachmentId>` — runs a body-growth gate (proceed iff body grew ≥25% since the last classify, or first-ever classify), stages the body to a temp file under `$ACCOUNT_DIR/tmp/`, and returns a dispatch envelope for the `librarian`; on success `memory-ingest` replaces the prior :Section children and stamps `lastClassifiedAt` + `lastClassifiedBodyLength` on the parent KD. Two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :ConversationArchive (keyed on `conversationIdentity`) + :Section chunks; two parent labels, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 splits across two read-only tools: `conversation-archive-list-chunks` pages chunk bodies for the dispatched specialist to read in-turn; `conversation-archive-derive-insights` converts the specialist's per-chunk claims into operator-facing proposals with per-kind cypher. `conversation-archive-enrich-rejection` records (or undoes) durable per-row rejections so already-triaged claims do not re-surface on re-runs. Ships five skills: `conversational-memory`, `document-ingest`, `conversation-archive` (source-agnostic transcript ingest for WhatsApp, Telegram, Signal, LinkedIn DMs, Zoom, meeting minutes, iMessage, Slack), `conversation-archive-mcp` (operator-initiated MCP-driven ingest for Granola, Otter, Circleback meetings — sibling of `conversation-archive` sharing the same writer surface and identity formula), and `conversation-archive-enrich` (per-row operator-gated insight derivation over a named archive's chunks). Ships three thinking-tool skills: `challenge` (adversarial retrieval — strongest counter-evidence from the graph for a given claim), `connect` (bridge-finding between two topics via shared graph neighbors), and `emerge` (clusters uncategorised KnowledgeDocument and Section nodes into operator-approved Concept proposals)."
|
|
3
|
+
description: "Graph memory plugin. Provides memory-search (with optional `fields` projection for known-shape lookups), memory-write, memory-update, memory-edge (create/delete typed directed edges between pre-existing nodes), memory-update-by-name (repair surface that resolves an elementId from a (label, name, accountId) tuple when search cannot return one), memory-lookup-by-name (deterministic read surface that returns nodes by case-insensitive exact `name` match — a Person, which carries no `name`, is matched on its composed `givenName` + `familyName` display name instead — optionally constrained by labels, bypassing the memory-search ranking stack so a present entity is never missed because a diluted query ranked it below the cut), and the :Report surface (memory-report-write / memory-report-read-latest / memory-report-list) for reading from, writing to, and updating the Neo4j knowledge graph. Includes conversational memory — organic preference learning, evidence-backed recall, and transparent 'what do you know about me?' responses. Document ingestion goes through memory-ingest; the dispatched specialist produces typed-section JSON in-turn from the loaded ontology. Operator-solicited reclassify of an already-stored thread-shaped KD (today: email-thread KDs) is exposed as `kd-classify <attachmentId>` — runs a body-growth gate (proceed iff body grew ≥25% since the last classify, or first-ever classify), stages the body to a temp file under `$ACCOUNT_DIR/tmp/`, and returns a dispatch envelope for the `librarian`; on success `memory-ingest` replaces the prior :Section children and stamps `lastClassifiedAt` + `lastClassifiedBodyLength` on the parent KD. Two modes: `document` (default) for unstructured PDF/web content → :KnowledgeDocument (keyed on `attachmentId`) + :Section, and `chat` for conversation transcripts → :ConversationArchive (keyed on `conversationIdentity`) + :Section chunks; two parent labels, two writer paths chosen by which identity property is set. Conversation-archive Phase 2 splits across two read-only tools: `conversation-archive-list-chunks` pages chunk bodies for the dispatched specialist to read in-turn; `conversation-archive-derive-insights` converts the specialist's per-chunk claims into operator-facing proposals with per-kind cypher. `conversation-archive-enrich-rejection` records (or undoes) durable per-row rejections so already-triaged claims do not re-surface on re-runs. Ships five skills: `conversational-memory`, `document-ingest`, `conversation-archive` (source-agnostic transcript ingest for WhatsApp, Telegram, Signal, LinkedIn DMs, Zoom, meeting minutes, iMessage, Slack), `conversation-archive-mcp` (operator-initiated MCP-driven ingest for Granola, Otter, Circleback meetings — sibling of `conversation-archive` sharing the same writer surface and identity formula), and `conversation-archive-enrich` (per-row operator-gated insight derivation over a named archive's chunks). Ships three thinking-tool skills: `challenge` (adversarial retrieval — strongest counter-evidence from the graph for a given claim), `connect` (bridge-finding between two topics via shared graph neighbors), and `emerge` (clusters uncategorised KnowledgeDocument and Section nodes into operator-approved Concept proposals). The learnings surface is three tools plus the `learnings-recall` skill: `learnings-scan` pages session transcripts off disk (requiring CLAUDE_CONFIG_DIR, refusing without it rather than guessing a projects root), `learnings-derive` reconciles the turn's claims into `:Learning` writes on a confidence curve (create at 0.5, reinforce as c + 0.15(1-c), contradict by 0.3 with contradictedAt stamped) and optionally runs the decay pass, and `learnings-recall` is the only path from a title injected into a system prompt to its full body."
|
|
4
4
|
account-owned-dirs: [{"dir": "archive", "description": "Archived obsidian imports and their embedded attachments."}]
|
|
5
5
|
tools:
|
|
6
6
|
- name: memory-search
|
|
@@ -167,6 +167,18 @@ tools:
|
|
|
167
167
|
publicAllowlist: false
|
|
168
168
|
adminAllowlist: true
|
|
169
169
|
riskClass: read
|
|
170
|
+
- name: learnings-scan
|
|
171
|
+
publicAllowlist: false
|
|
172
|
+
adminAllowlist: true
|
|
173
|
+
riskClass: read
|
|
174
|
+
- name: learnings-derive
|
|
175
|
+
publicAllowlist: false
|
|
176
|
+
adminAllowlist: true
|
|
177
|
+
riskClass: write_local
|
|
178
|
+
- name: learnings-recall
|
|
179
|
+
publicAllowlist: false
|
|
180
|
+
adminAllowlist: true
|
|
181
|
+
riskClass: read
|
|
170
182
|
hidden:
|
|
171
183
|
- name: session-compact
|
|
172
184
|
riskClass: write_local
|
|
@@ -185,6 +197,7 @@ skills:
|
|
|
185
197
|
- skills/challenge/SKILL.md
|
|
186
198
|
- skills/connect/SKILL.md
|
|
187
199
|
- skills/emerge/SKILL.md
|
|
200
|
+
- skills/learnings-recall/SKILL.md
|
|
188
201
|
always: true
|
|
189
202
|
embed: false
|
|
190
203
|
mcp:
|
|
@@ -200,6 +213,7 @@ mcp:
|
|
|
200
213
|
SESSION_ID: ${SESSION_ID}
|
|
201
214
|
AGENT_SLUG: ${AGENT_SLUG}
|
|
202
215
|
AGENT_SLUG_SCOPE: ${AGENT_SLUG_SCOPE}
|
|
216
|
+
CLAUDE_CONFIG_DIR: ${CLAUDE_CONFIG_DIR}
|
|
203
217
|
mcp-manifest: auto
|
|
204
218
|
---
|
|
205
219
|
|