wendkeep 0.80.0 → 0.80.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.en.md +4 -1
- package/README.md +4 -1
- package/docs/en/commands/context.md +5 -0
- package/docs/en/commands/operating-profiles.md +4 -0
- package/docs/pt-BR/commands/context.md +6 -0
- package/docs/pt-BR/commands/operating-profiles.md +4 -0
- package/hooks/session-ensure.mjs +10 -22
- package/hooks/session-identity.mjs +20 -0
- package/hooks/session-start.mjs +8 -1
- package/package.json +1 -1
- package/src/profile.mjs +16 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,27 @@ All notable changes to **wendkeep** are documented here. Format based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project follows
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.80.2] — 2026-08-24
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Perfil persistente em worktrees vinculadas.** `profile use` e `profile status` agora leem e
|
|
12
|
+
escrevem o binding canônico da worktree principal quando o Vault é resolvido pelo registry Git
|
|
13
|
+
compartilhado, sem alterar o `.wendkeep.json` versionado da worktree vinculada. A identidade de
|
|
14
|
+
caminho também normaliza aliases Windows 8.3, impedindo que uma seleção humana `OFF` volte
|
|
15
|
+
imediatamente ao fallback `GOVERN`; README e guia de perfis foram atualizados em PT-BR/EN.
|
|
16
|
+
|
|
17
|
+
## [0.80.1] — 2026-08-24
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **Bootstrap causal das sessões Codex.** `session-start` e `session-ensure` agora derivam um
|
|
22
|
+
`work_session_id` estável do `session_id` canônico quando o host não envia essa identidade,
|
|
23
|
+
preservando primeiro handoff explícito e valor já registrado. Novas sessões deixam de falhar em
|
|
24
|
+
`profile route`, `change new` e active context por identidade ausente, sem relaxar as validações
|
|
25
|
+
de projeto, repositório, worktree ou branch; README e o guia de contexto foram atualizados em
|
|
26
|
+
PT-BR/EN.
|
|
27
|
+
|
|
7
28
|
## [0.80.0] — 2026-08-24
|
|
8
29
|
|
|
9
30
|
### Added
|
package/README.en.md
CHANGED
|
@@ -115,7 +115,7 @@ npx wendkeep init
|
|
|
115
115
|
|
|
116
116
|
1. Create the vault folder taxonomy and a templated `README.md` (default vault: `<project>/.<project-name>-vault`, e.g. `.MyApp-vault`; override with `--vault`).
|
|
117
117
|
2. Write a provider-neutral **`.wendkeep.json`** binding at the project root and a matching `.brain/PROJECT.json` marker in the vault, then merge the session hooks into **`.claude/settings.json`**. The binding is provider-neutral by design: any agent resolves the same vault from its session `cwd`, with no machine-global environment variable. Older registrations already in `.claude/settings.json` are adopted automatically.
|
|
118
|
-
3. Wire the Codex hooks in **`.codex/hooks.json`** — twelve compatible entries: `brain-inject` + `session-start` + `observer-publish` on `SessionStart`, `session-ensure` + `evidence-context` + `change-context` on `UserPromptSubmit`, `session-stop` + `observer-publish` + `change-nag` on `Stop`, `subagent-stop` + `observer-publish` on `SubagentStop`, and `change-guard` on `PreToolUse` for `Bash`, `exec_command`, `apply_patch`, and mutable MCP tools, always in the `npx wendkeep hook <name>` form. For the Observer, `SessionStart` only drains the outbox, `Stop` enqueues the changed session, and `SubagentStop` enqueues only the affected transcript; full scanning is explicit through `observer reconcile`. The guard accepts object, raw-string, and argv Codex payloads; before a mutation it compares the session with the project, Git root, remote, branch, and worktree, denying missing or divergent targets. A raw `git checkout/switch` branch transition is denied before it can strand the session; use `wendkeep context switch <branch> [--create]`, which moves Git and the causal scope together in the same worktree with an audited revision and rollback. If a divergence is already quarantined, `context status --session <id>` inventories sanitized `reserved`/`observed` candidates; `context recover --session <id> --select <reserved|observed> --revision <n> --reason <text>` requires an explicit choice, CAS, and current-checkout proof, failing closed before clearing the conflict if revalidation changes. `doctor` diagnoses orphaned active contexts, removed worktrees, and expired `request-stop` leases without writing; `context repair --key <key> --revision <n> --reason <text> --session <id>` revalidates under lock, closes only the ownerless/removed context or expires only its lease, while preserving the record and all historical memory. The change lifecycle uses `active_contexts`, identified by `repository_id` + `worktree_id` + `work_session_id`; two matching sessions fail with ambiguity instead of selecting silently, `CURRENT_CHANGE.md` is only a derived projection for one unambiguous context, and migration never invents a worktree or session identity. The other four stay out because Codex offers no equivalent payload, tool, or event: `change-warn` (no reliable `tool_input.file_path`), `plan-capture` (no `ExitPlanMode`), `decision-capture` (`AskUserQuestion` is Claude-only), and `task-log` (`TaskCompleted` is not in Codex's event enum). Codex scope blocks use `permissionDecision: "deny"`; `ask` is never emitted in `PreToolUse`. The merge remains non-destructive, preserves third-party hooks, and migrates legacy `timeout` to `timeoutSec`. **Codex enumerates every hook as untrusted and runs none until you approve the “Hooks need review” prompt at startup — `init` cannot pre-approve them**.
|
|
118
|
+
3. Wire the Codex hooks in **`.codex/hooks.json`** — twelve compatible entries: `brain-inject` + `session-start` + `observer-publish` on `SessionStart`, `session-ensure` + `evidence-context` + `change-context` on `UserPromptSubmit`, `session-stop` + `observer-publish` + `change-nag` on `Stop`, `subagent-stop` + `observer-publish` on `SubagentStop`, and `change-guard` on `PreToolUse` for `Bash`, `exec_command`, `apply_patch`, and mutable MCP tools, always in the `npx wendkeep hook <name>` form. For the Observer, `SessionStart` only drains the outbox, `Stop` enqueues the changed session, and `SubagentStop` enqueues only the affected transcript; full scanning is explicit through `observer reconcile`. When the host omits `work_session_id`, `session-start` and `session-ensure` derive it from the canonical `session_id`, preserving an explicit handoff and the already registered value first. The guard accepts object, raw-string, and argv Codex payloads; before a mutation it compares the session with the project, Git root, remote, branch, and worktree, denying missing or divergent targets. A raw `git checkout/switch` branch transition is denied before it can strand the session; use `wendkeep context switch <branch> [--create]`, which moves Git and the causal scope together in the same worktree with an audited revision and rollback. If a divergence is already quarantined, `context status --session <id>` inventories sanitized `reserved`/`observed` candidates; `context recover --session <id> --select <reserved|observed> --revision <n> --reason <text>` requires an explicit choice, CAS, and current-checkout proof, failing closed before clearing the conflict if revalidation changes. `doctor` diagnoses orphaned active contexts, removed worktrees, and expired `request-stop` leases without writing; `context repair --key <key> --revision <n> --reason <text> --session <id>` revalidates under lock, closes only the ownerless/removed context or expires only its lease, while preserving the record and all historical memory. The change lifecycle uses `active_contexts`, identified by `repository_id` + `worktree_id` + `work_session_id`; two matching sessions fail with ambiguity instead of selecting silently, `CURRENT_CHANGE.md` is only a derived projection for one unambiguous context, and migration never invents a worktree or session identity. The other four stay out because Codex offers no equivalent payload, tool, or event: `change-warn` (no reliable `tool_input.file_path`), `plan-capture` (no `ExitPlanMode`), `decision-capture` (`AskUserQuestion` is Claude-only), and `task-log` (`TaskCompleted` is not in Codex's event enum). Codex scope blocks use `permissionDecision: "deny"`; `ask` is never emitted in `PreToolUse`. The merge remains non-destructive, preserves third-party hooks, and migrates legacy `timeout` to `timeoutSec`. **Codex enumerates every hook as untrusted and runs none until you approve the “Hooks need review” prompt at startup — `init` cannot pre-approve them**.
|
|
119
119
|
Once `active_contexts` is initialized, `brain-inject` and `change-context` mark only the causal context's change as current; the backlog remains global, and an empty or ambiguous store never revives `CURRENT_CHANGE.md`.
|
|
120
120
|
4. Add the **`wendkeep-vault`** MCP server to `.mcp.json` so the agent can read/write the vault. Skip with `--no-mcp` — e.g. when the agent already has a vault MCP. (`--no-mcp` skips *only wendkeep's own* MCP; companion MCPs still follow `--companions`.)
|
|
121
121
|
5. Offer to pin **companion** plugins/MCP (multi-choice; **none** pre-checked — wendkeep is a neutral harness and presumes no third-party plugin). Each is wired the most agent-agnostic way it supports:
|
|
@@ -161,6 +161,9 @@ paths are also supported. Hooks search upward from the agent's `cwd`, so nested
|
|
|
161
161
|
the nearest binding. The vault carries the same identity in `.brain/PROJECT.json`; a mismatch
|
|
162
162
|
is rejected before any session is written. If no binding exists, hooks fail closed and never
|
|
163
163
|
create the historical `~/wendkeep-vault` fallback.
|
|
164
|
+
In linked worktrees, `profile use` and `profile status` resolve the main worktree's canonical
|
|
165
|
+
binding through the shared Git registry; the persistent selection applies project-wide without
|
|
166
|
+
rewriting the current worktree's versioned `.wendkeep.json`.
|
|
164
167
|
|
|
165
168
|
`OBSIDIAN_VAULT_PATH` remains only as legacy/manual CLI compatibility. It is not used to
|
|
166
169
|
route automatic Codex or Claude hooks and a project-local binding overrides an inherited
|
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@ npx wendkeep init
|
|
|
115
115
|
|
|
116
116
|
1. Create the vault folder taxonomy and a templated `README.md` (default vault: `<project>/.<project-name>-vault`, e.g. `.MyApp-vault`; override with `--vault`).
|
|
117
117
|
2. Write a provider-neutral **`.wendkeep.json`** binding at the project root and a matching `.brain/PROJECT.json` marker in the vault, then merge the session hooks into **`.claude/settings.json`**. The binding is provider-neutral by design: any agent resolves the same vault from its session `cwd`, with no machine-global environment variable. Older registrations already in `.claude/settings.json` are adopted automatically.
|
|
118
|
-
3. Wire the Codex hooks in **`.codex/hooks.json`** — twelve compatible entries: `brain-inject` + `session-start` + `observer-publish` on `SessionStart`, `session-ensure` + `evidence-context` + `change-context` on `UserPromptSubmit`, `session-stop` + `observer-publish` + `change-nag` on `Stop`, `subagent-stop` + `observer-publish` on `SubagentStop`, and `change-guard` on `PreToolUse` for `Bash`, `exec_command`, `apply_patch`, and mutable MCP tools, always in the `npx wendkeep hook <name>` form. For the Observer, `SessionStart` only drains the outbox, `Stop` enqueues the changed session, and `SubagentStop` enqueues only the affected transcript; full scanning is explicit through `observer reconcile`. The guard accepts object, raw-string, and argv Codex payloads; before a mutation it compares the session with the project, Git root, remote, branch, and worktree, denying missing or divergent targets. A raw `git checkout/switch` branch transition is denied before it can strand the session; use `wendkeep context switch <branch> [--create]`, which moves Git and the causal scope together in the same worktree with an audited revision and rollback. If a divergence is already quarantined, `context status --session <id>` inventories sanitized `reserved`/`observed` candidates; `context recover --session <id> --select <reserved|observed> --revision <n> --reason <text>` requires an explicit choice, CAS, and current-checkout proof, failing closed before clearing the conflict if revalidation changes. `doctor` diagnoses orphaned active contexts, removed worktrees, and expired `request-stop` leases without writing; `context repair --key <key> --revision <n> --reason <text> --session <id>` revalidates under lock, closes only the ownerless/removed context or expires only its lease, while preserving the record and all historical memory. The change lifecycle uses `active_contexts`, identified by `repository_id` + `worktree_id` + `work_session_id`; two matching sessions fail with ambiguity instead of selecting silently, `CURRENT_CHANGE.md` is only a derived projection for one unambiguous context, and migration never invents a worktree or session identity. The other four stay out because Codex offers no equivalent payload, tool, or event: `change-warn` (no reliable `tool_input.file_path`), `plan-capture` (no `ExitPlanMode`), `decision-capture` (`AskUserQuestion` is Claude-only), and `task-log` (`TaskCompleted` is not in Codex's event enum). Codex scope blocks use `permissionDecision: "deny"`; `ask` is never emitted in `PreToolUse`. The merge remains non-destructive, preserves third-party hooks, and migrates legacy `timeout` to `timeoutSec`. **Codex enumerates every hook as untrusted and runs none until you approve the “Hooks need review” prompt at startup — `init` cannot pre-approve them**.
|
|
118
|
+
3. Wire the Codex hooks in **`.codex/hooks.json`** — twelve compatible entries: `brain-inject` + `session-start` + `observer-publish` on `SessionStart`, `session-ensure` + `evidence-context` + `change-context` on `UserPromptSubmit`, `session-stop` + `observer-publish` + `change-nag` on `Stop`, `subagent-stop` + `observer-publish` on `SubagentStop`, and `change-guard` on `PreToolUse` for `Bash`, `exec_command`, `apply_patch`, and mutable MCP tools, always in the `npx wendkeep hook <name>` form. For the Observer, `SessionStart` only drains the outbox, `Stop` enqueues the changed session, and `SubagentStop` enqueues only the affected transcript; full scanning is explicit through `observer reconcile`. When the host omits `work_session_id`, `session-start` and `session-ensure` derive it from the canonical `session_id`, preserving an explicit handoff and the already registered value first. The guard accepts object, raw-string, and argv Codex payloads; before a mutation it compares the session with the project, Git root, remote, branch, and worktree, denying missing or divergent targets. A raw `git checkout/switch` branch transition is denied before it can strand the session; use `wendkeep context switch <branch> [--create]`, which moves Git and the causal scope together in the same worktree with an audited revision and rollback. If a divergence is already quarantined, `context status --session <id>` inventories sanitized `reserved`/`observed` candidates; `context recover --session <id> --select <reserved|observed> --revision <n> --reason <text>` requires an explicit choice, CAS, and current-checkout proof, failing closed before clearing the conflict if revalidation changes. `doctor` diagnoses orphaned active contexts, removed worktrees, and expired `request-stop` leases without writing; `context repair --key <key> --revision <n> --reason <text> --session <id>` revalidates under lock, closes only the ownerless/removed context or expires only its lease, while preserving the record and all historical memory. The change lifecycle uses `active_contexts`, identified by `repository_id` + `worktree_id` + `work_session_id`; two matching sessions fail with ambiguity instead of selecting silently, `CURRENT_CHANGE.md` is only a derived projection for one unambiguous context, and migration never invents a worktree or session identity. The other four stay out because Codex offers no equivalent payload, tool, or event: `change-warn` (no reliable `tool_input.file_path`), `plan-capture` (no `ExitPlanMode`), `decision-capture` (`AskUserQuestion` is Claude-only), and `task-log` (`TaskCompleted` is not in Codex's event enum). Codex scope blocks use `permissionDecision: "deny"`; `ask` is never emitted in `PreToolUse`. The merge remains non-destructive, preserves third-party hooks, and migrates legacy `timeout` to `timeoutSec`. **Codex enumerates every hook as untrusted and runs none until you approve the “Hooks need review” prompt at startup — `init` cannot pre-approve them**.
|
|
119
119
|
Once `active_contexts` is initialized, `brain-inject` and `change-context` mark only the causal context's change as current; the backlog remains global, and an empty or ambiguous store never revives `CURRENT_CHANGE.md`.
|
|
120
120
|
4. Add the **`wendkeep-vault`** MCP server to `.mcp.json` so the agent can read/write the vault. Skip with `--no-mcp` — e.g. when the agent already has a vault MCP. (`--no-mcp` skips *only wendkeep's own* MCP; companion MCPs still follow `--companions`.)
|
|
121
121
|
5. Offer to pin **companion** plugins/MCP (multi-choice; **none** pre-checked — wendkeep is a neutral harness and presumes no third-party plugin). Each is wired the most agent-agnostic way it supports:
|
|
@@ -161,6 +161,9 @@ paths are also supported. Hooks search upward from the agent's `cwd`, so nested
|
|
|
161
161
|
the nearest binding. The vault carries the same identity in `.brain/PROJECT.json`; a mismatch
|
|
162
162
|
is rejected before any session is written. If no binding exists, hooks fail closed and never
|
|
163
163
|
create the historical `~/wendkeep-vault` fallback.
|
|
164
|
+
In linked worktrees, `profile use` and `profile status` resolve the main worktree's canonical
|
|
165
|
+
binding through the shared Git registry; the persistent selection applies project-wide without
|
|
166
|
+
rewriting the current worktree's versioned `.wendkeep.json`.
|
|
164
167
|
|
|
165
168
|
`OBSIDIAN_VAULT_PATH` remains only as legacy/manual CLI compatibility. It is not used to
|
|
166
169
|
route automatic Codex or Claude hooks and a project-local binding overrides an inherited
|
|
@@ -108,6 +108,11 @@ identified by `repository_id` + `worktree_id` + `work_session_id`; branch, HEAD,
|
|
|
108
108
|
belong to that entry. Two worktrees can therefore select different changes without overwriting
|
|
109
109
|
each other's operational focus.
|
|
110
110
|
|
|
111
|
+
During the session lifecycle, `session-start` and `session-ensure` first preserve an explicit
|
|
112
|
+
handoff `work_session_id`, then the already registered value, and, when the host supplies neither,
|
|
113
|
+
derive the stable identity from the canonical `session_id`. Providers such as Codex can therefore
|
|
114
|
+
initialize an active context without weakening project, repository, worktree, or branch checks.
|
|
115
|
+
|
|
111
116
|
With an explicit causal session, change, spec, and verify resolve only the matching entry. Without
|
|
112
117
|
a session, only one active entry for the worktree is accepted; two sessions produce ambiguity and
|
|
113
118
|
the operation must fail closed without silently selecting a change.
|
|
@@ -138,6 +138,10 @@ ownership to the native LLM harness.
|
|
|
138
138
|
missing, ambiguous, or stale context fails closed without a partial mutation.
|
|
139
139
|
- `.wendkeep.json` stays on `schemaVersion: 1`; the additive field is, for example,
|
|
140
140
|
`"harness": { "profile": "GOVERN" }`. A legacy binding without it also resolves to `GOVERN`.
|
|
141
|
+
- In a linked worktree, `profile use` and `profile status` use the main worktree's canonical
|
|
142
|
+
binding discovered through the shared Git registry. The selection is persisted once for the
|
|
143
|
+
project and the linked worktree's versioned `.wendkeep.json` remains unchanged, including on
|
|
144
|
+
Windows when long paths and 8.3 aliases identify the same Vault.
|
|
141
145
|
- A corrupt binding never means `OFF`. When the payload or legacy integration identifies one
|
|
142
146
|
unambiguous Vault, Keep Core remains active under `GOVERN` and the hook exposes a diagnostic;
|
|
143
147
|
mutation guards fail closed until the binding is repaired. Invalid local configuration, a
|
|
@@ -105,6 +105,12 @@ identificada por `repository_id` + `worktree_id` + `work_session_id`; branch, HE
|
|
|
105
105
|
pertencem a essa entrada. Assim, duas worktrees podem selecionar changes diferentes sem
|
|
106
106
|
sobrescrever o foco uma da outra.
|
|
107
107
|
|
|
108
|
+
No lifecycle de sessão, `session-start` e `session-ensure` preservam primeiro um
|
|
109
|
+
`work_session_id` explícito do handoff, depois o valor já registrado e, quando o host não fornece
|
|
110
|
+
nenhum deles, derivam a identidade estável do `session_id` canônico. Assim, providers como o Codex
|
|
111
|
+
podem inicializar o active context sem enfraquecer as validações de projeto, repositório, worktree
|
|
112
|
+
ou branch.
|
|
113
|
+
|
|
108
114
|
Com sessão causal explícita, change, spec e verify resolvem somente a entrada correspondente. Sem
|
|
109
115
|
sessão, uma única entrada ativa da worktree pode ser usada; duas sessões compatíveis causam
|
|
110
116
|
ambiguidade e a operação falha fechada, sem escolher uma change em silêncio.
|
|
@@ -139,6 +139,10 @@ harness nativo da LLM.
|
|
|
139
139
|
ausente, ambíguo ou stale falha fechado sem mutação parcial.
|
|
140
140
|
- `.wendkeep.json` continua em `schemaVersion: 1`; o campo aditivo usa, por exemplo,
|
|
141
141
|
`"harness": { "profile": "GOVERN" }`. Binding legado sem o campo também resolve `GOVERN`.
|
|
142
|
+
- Em worktree vinculada, `profile use` e `profile status` usam o binding canônico da worktree
|
|
143
|
+
principal descoberto pelo registry Git compartilhado. A seleção é persistida uma vez para o
|
|
144
|
+
projeto e o `.wendkeep.json` versionado da worktree vinculada permanece intacto, inclusive no
|
|
145
|
+
Windows quando caminhos longos e aliases 8.3 identificam o mesmo Vault.
|
|
142
146
|
- Binding corrompido nunca equivale a `OFF`. Quando o payload ou a integração legada identifica
|
|
143
147
|
um Vault inequívoco, o Keep Core continua ativo sob `GOVERN` e o hook expõe um diagnóstico;
|
|
144
148
|
guards de mutação falham fechados até o binding ser reparado. Configuração local inválida,
|
package/hooks/session-ensure.mjs
CHANGED
|
@@ -32,31 +32,19 @@ import {
|
|
|
32
32
|
writeHookOutput,
|
|
33
33
|
yamlQuote,
|
|
34
34
|
} from './obsidian-common.mjs';
|
|
35
|
-
import { resolveSessionIdentity } from './session-identity.mjs';
|
|
35
|
+
import { resolveSessionIdentity, sessionWorkSessionPatch } from './session-identity.mjs';
|
|
36
36
|
import { readCodexRolloutMeta } from './codex-rollout-meta.mjs';
|
|
37
37
|
import { mutateSessionNote } from './session-note-io.mjs';
|
|
38
38
|
import { captureProjectScope, projectScopePatch } from './project-scope.mjs';
|
|
39
|
-
import { sanitizeMemoryText } from './memory-schema.mjs';
|
|
40
39
|
|
|
41
40
|
function sessionIdFromInput(input) {
|
|
42
41
|
return input.session_id || input.sessionId || input.codex_session_id || '';
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
function
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|| shared?.work_session_id
|
|
50
|
-
|| shared?.workSessionId
|
|
51
|
-
|| input.handoff?.work_session_id
|
|
52
|
-
|| input.handoff?.workSessionId
|
|
53
|
-
|| '';
|
|
54
|
-
return sanitizeMemoryText(value).trim();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function workSessionPatch(input = {}) {
|
|
58
|
-
const workSessionId = workSessionIdFromInput(input);
|
|
59
|
-
return workSessionId ? { work_session_id: workSessionId } : {};
|
|
44
|
+
function workSessionPatch(vaultBase, sessionId, input = {}) {
|
|
45
|
+
const existingWorkSessionId = readSessionRegistry(vaultBase)
|
|
46
|
+
.sessions?.[sessionId]?.work_session_id || '';
|
|
47
|
+
return sessionWorkSessionPatch({ input, sessionId, existingWorkSessionId });
|
|
60
48
|
}
|
|
61
49
|
|
|
62
50
|
function turnSequenceFromInput(input = {}) {
|
|
@@ -308,7 +296,7 @@ function activateExistingSession({ vaultBase, relPath, startedAt, sessionId, inp
|
|
|
308
296
|
transcript_path: identity.transcriptPath,
|
|
309
297
|
transcript_id: identity.transcriptId,
|
|
310
298
|
provider: identity.provider,
|
|
311
|
-
...workSessionPatch(input),
|
|
299
|
+
...workSessionPatch(vaultBase, sessionId, input),
|
|
312
300
|
...scopePatch,
|
|
313
301
|
...causalTurnPatch(input, now),
|
|
314
302
|
});
|
|
@@ -337,7 +325,7 @@ function createSession({ vaultBase, sessionId, input, now, identity, scopePatch
|
|
|
337
325
|
transcript_path: identity.transcriptPath,
|
|
338
326
|
transcript_id: identity.transcriptId,
|
|
339
327
|
provider: identity.provider,
|
|
340
|
-
...workSessionPatch(input),
|
|
328
|
+
...workSessionPatch(vaultBase, sessionId, input),
|
|
341
329
|
...scopePatch,
|
|
342
330
|
...causalTurnPatch(input, now),
|
|
343
331
|
});
|
|
@@ -381,7 +369,7 @@ function main() {
|
|
|
381
369
|
upsertSessionRegistry(vaultBase, sessionId, {
|
|
382
370
|
transcript_paths: [identity.transcriptPath],
|
|
383
371
|
provider: identity.provider,
|
|
384
|
-
...workSessionPatch(input),
|
|
372
|
+
...workSessionPatch(vaultBase, sessionId, input),
|
|
385
373
|
});
|
|
386
374
|
}
|
|
387
375
|
writeHookOutput({});
|
|
@@ -419,7 +407,7 @@ function main() {
|
|
|
419
407
|
transcript_path: identity.transcriptPath,
|
|
420
408
|
transcript_id: identity.transcriptId,
|
|
421
409
|
provider: identity.provider,
|
|
422
|
-
...workSessionPatch(input),
|
|
410
|
+
...workSessionPatch(vaultBase, sessionId, input),
|
|
423
411
|
...scopePatch,
|
|
424
412
|
...causalTurnPatch(input, now),
|
|
425
413
|
});
|
|
@@ -465,7 +453,7 @@ function main() {
|
|
|
465
453
|
transcript_path: identity.transcriptPath,
|
|
466
454
|
transcript_id: identity.transcriptId,
|
|
467
455
|
provider: identity.provider,
|
|
468
|
-
...workSessionPatch(input),
|
|
456
|
+
...workSessionPatch(vaultBase, sessionId || control.session_id, input),
|
|
469
457
|
...scopePatch,
|
|
470
458
|
...causalTurnPatch(input, now),
|
|
471
459
|
});
|
|
@@ -6,6 +6,26 @@ import {
|
|
|
6
6
|
resolveSessionIdentitySnapshot,
|
|
7
7
|
} from '../packages/integrations/src/session-identity.mjs';
|
|
8
8
|
import { readCodexRolloutMeta } from './codex-rollout-meta.mjs';
|
|
9
|
+
import { sanitizeMemoryText } from './memory-schema.mjs';
|
|
10
|
+
|
|
11
|
+
export function sessionWorkSessionPatch({
|
|
12
|
+
input = {},
|
|
13
|
+
sessionId = '',
|
|
14
|
+
existingWorkSessionId = '',
|
|
15
|
+
} = {}) {
|
|
16
|
+
const shared = input.shared || input.handoff?.shared;
|
|
17
|
+
const explicit = input.work_session_id
|
|
18
|
+
|| input.workSessionId
|
|
19
|
+
|| shared?.work_session_id
|
|
20
|
+
|| shared?.workSessionId
|
|
21
|
+
|| input.handoff?.work_session_id
|
|
22
|
+
|| input.handoff?.workSessionId
|
|
23
|
+
|| '';
|
|
24
|
+
const workSessionId = sanitizeMemoryText(
|
|
25
|
+
explicit || existingWorkSessionId || sessionId,
|
|
26
|
+
).trim();
|
|
27
|
+
return workSessionId ? { work_session_id: workSessionId } : {};
|
|
28
|
+
}
|
|
9
29
|
|
|
10
30
|
function unknownTranscriptIdentity() {
|
|
11
31
|
return {
|
package/hooks/session-start.mjs
CHANGED
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
writeHookOutput,
|
|
33
33
|
yamlQuote,
|
|
34
34
|
} from './obsidian-common.mjs';
|
|
35
|
-
import { resolveSessionIdentity } from './session-identity.mjs';
|
|
35
|
+
import { resolveSessionIdentity, sessionWorkSessionPatch } from './session-identity.mjs';
|
|
36
36
|
import { captureProjectScope, projectScopePatch } from './project-scope.mjs';
|
|
37
37
|
|
|
38
38
|
export function buildSessionContent({ relPath, now, summary = 'session', provider: providerId, sessionId = '' }) {
|
|
@@ -183,11 +183,18 @@ function main() {
|
|
|
183
183
|
readSessionRegistry(vaultBase).sessions?.[canonicalSessionId]?.project_scope,
|
|
184
184
|
{ ...initialScope, sessionId: canonicalSessionId },
|
|
185
185
|
);
|
|
186
|
+
const workSessionPatchFor = (canonicalSessionId) => sessionWorkSessionPatch({
|
|
187
|
+
input,
|
|
188
|
+
sessionId: canonicalSessionId,
|
|
189
|
+
existingWorkSessionId: readSessionRegistry(vaultBase)
|
|
190
|
+
.sessions?.[canonicalSessionId]?.work_session_id || '',
|
|
191
|
+
});
|
|
186
192
|
const control = readControl(vaultBase);
|
|
187
193
|
const activationId = input.activation_id || input.activationId || randomUUID();
|
|
188
194
|
const activationStartedAt = formatLocalIso(now);
|
|
189
195
|
const registerActivation = (canonicalSessionId, patch) => upsertSessionRegistry(vaultBase, canonicalSessionId, {
|
|
190
196
|
...patch,
|
|
197
|
+
...workSessionPatchFor(canonicalSessionId),
|
|
191
198
|
...scopePatchFor(canonicalSessionId),
|
|
192
199
|
activation_id: activationId,
|
|
193
200
|
activation_started_at: activationStartedAt,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wendkeep",
|
|
3
|
-
"version": "0.80.
|
|
3
|
+
"version": "0.80.2",
|
|
4
4
|
"description": "Vault-first persistent memory for AI coding agents, with an optional profile-aware governance runtime: OFF, FLOW, GUIDE, GOVERN, or ASSURE. Local-first and agent-agnostic (Claude Code, Codex, Cursor…).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
package/src/profile.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
setSessionTaskOperatingProfile,
|
|
14
14
|
} from '../hooks/operating-profile-task-store.mjs';
|
|
15
15
|
import { resolveCommandActiveContext } from './active-context-runtime.mjs';
|
|
16
|
+
import { realpathSync } from 'node:fs';
|
|
16
17
|
import { resolve } from 'node:path';
|
|
17
18
|
import { findProjectBinding, resolveProjectVault, updateProjectBinding } from './project-vault.mjs';
|
|
18
19
|
|
|
@@ -77,7 +78,11 @@ function validateArgv(argv) {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
function canonicalPath(value) {
|
|
80
|
-
const
|
|
81
|
+
const absolute = resolve(value);
|
|
82
|
+
let canonical = absolute;
|
|
83
|
+
try { canonical = realpathSync.native(absolute); }
|
|
84
|
+
catch { /* Preserve comparison support for valid paths that do not exist yet. */ }
|
|
85
|
+
const path = canonical.replaceAll('\\', '/');
|
|
81
86
|
return process.platform === 'win32' ? path.toLowerCase() : path;
|
|
82
87
|
}
|
|
83
88
|
|
|
@@ -114,8 +119,16 @@ function context(argv) {
|
|
|
114
119
|
catch (error) {
|
|
115
120
|
if (!explicitVault) throw error;
|
|
116
121
|
}
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
let canonicalBinding = null;
|
|
123
|
+
if (resolved.source === 'worktree-registry' && resolved.projectRoot) {
|
|
124
|
+
canonicalBinding = findProjectBinding(resolved.projectRoot);
|
|
125
|
+
}
|
|
126
|
+
const candidateBinding = canonicalBinding || binding;
|
|
127
|
+
const matchingBinding = candidateBinding
|
|
128
|
+
&& canonicalPath(candidateBinding.base) === canonicalPath(resolved.base)
|
|
129
|
+
? candidateBinding
|
|
130
|
+
: null;
|
|
131
|
+
const projectConfig = matchingBinding?.config || resolved.config || {};
|
|
119
132
|
return {
|
|
120
133
|
resolved: {
|
|
121
134
|
...resolved,
|