wendkeep 0.76.5 → 0.76.6
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 +11 -0
- package/README.en.md +5 -0
- package/README.md +5 -0
- package/docs/en/commands/operating-profiles.md +6 -0
- package/docs/pt-BR/commands/operating-profiles.md +6 -0
- package/hooks/active-context-store.mjs +5 -2
- package/hooks/change-nag.mjs +7 -3
- package/hooks/operating-profile-runtime.mjs +22 -1
- package/hooks/operating-profile-task-store.mjs +122 -18
- package/package.json +1 -1
- package/src/profile.mjs +21 -5
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ 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.76.6] — 2026-08-22
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Task lease causal.** `operating_profile_task` passa a pertencer ao `active_contexts` da
|
|
12
|
+
worktree/work session; rotas temporárias irmãs não se sobrescrevem nem ampliam autorização.
|
|
13
|
+
- **CLI e hooks escopados.** `profile route/status`, os hooks de operating profile e o consumo no
|
|
14
|
+
Stop resolvem a lease do chamador e preservam o sibling byte a byte por revision/CAS.
|
|
15
|
+
- **Fallback legado fail-closed.** Vault sem registry contextual conserva a lease na sessão;
|
|
16
|
+
depois da inicialização, uma lease global não é copiada nem aplicada sem identidade provada.
|
|
17
|
+
|
|
7
18
|
## [0.76.5] — 2026-08-22
|
|
8
19
|
|
|
9
20
|
### Added
|
package/README.en.md
CHANGED
|
@@ -343,6 +343,11 @@ selects the caller explicitly. `CURRENT_DELIVERY` is only a derived projection w
|
|
|
343
343
|
single, unambiguous context; hooks consult only the causal delivery, and an ID from another context
|
|
344
344
|
fails with `WENDKEEP_DELIVERY_CONTEXT_MISMATCH`.
|
|
345
345
|
|
|
346
|
+
`operating_profile_task` belongs to the causal work session's active context. `profile route` and
|
|
347
|
+
`profile status --session <id>`, hooks, and Stop read/consume only that lease; a temporary route
|
|
348
|
+
never crosses into a sibling. Legacy session fallback exists only without `active_contexts`;
|
|
349
|
+
an initialized registry never copies a global authorization without proven identity.
|
|
350
|
+
|
|
346
351
|
```bash
|
|
347
352
|
npx wendkeep delivery start release-0-74-0 --allow git:merge --allow git:push --allow publish --source-change <slug> --source-commit <sha> --session <id>
|
|
348
353
|
npx wendkeep delivery status release-0-74-0 --session <id>
|
package/README.md
CHANGED
|
@@ -343,6 +343,11 @@ selects the caller explicitly. `CURRENT_DELIVERY` is only a derived projection w
|
|
|
343
343
|
single, unambiguous context; hooks consult only the causal delivery, and an ID from another context
|
|
344
344
|
fails with `WENDKEEP_DELIVERY_CONTEXT_MISMATCH`.
|
|
345
345
|
|
|
346
|
+
`operating_profile_task` belongs to the causal work session's active context. `profile route` and
|
|
347
|
+
`profile status --session <id>`, hooks, and Stop read/consume only that lease; a temporary route
|
|
348
|
+
never crosses into a sibling. Legacy session fallback exists only without `active_contexts`;
|
|
349
|
+
an initialized registry never copies a global authorization without proven identity.
|
|
350
|
+
|
|
346
351
|
```bash
|
|
347
352
|
npx wendkeep delivery start release-0-74-0 --allow git:merge --allow git:push --allow publish --source-change <slug> --source-commit <sha> --session <id>
|
|
348
353
|
npx wendkeep delivery status release-0-74-0 --session <id>
|
|
@@ -130,6 +130,12 @@ ownership to the native LLM harness.
|
|
|
130
130
|
session identity.
|
|
131
131
|
- `profile route` requires `--session` and `--reason`, accepts only the four adaptive profiles,
|
|
132
132
|
and records lease id, reason, turn/sequence, and timestamp without touching persistent profiles.
|
|
133
|
+
- `operating_profile_task` belongs to the causal session/work session's active context. `profile route`
|
|
134
|
+
creates and `profile status --session <id>` reads only that lease; hooks use the same caller
|
|
135
|
+
context and an accepted Stop consumes it by `lease_id` with revision/CAS without changing a sibling.
|
|
136
|
+
- Legacy fallback of the lease in the session exists only without `active_contexts`. With an
|
|
137
|
+
initialized registry, a global lease absent from the context is neither copied nor applied;
|
|
138
|
+
missing, ambiguous, or stale context fails closed without a partial mutation.
|
|
133
139
|
- `.wendkeep.json` stays on `schemaVersion: 1`; the additive field is, for example,
|
|
134
140
|
`"harness": { "profile": "GOVERN" }`. A legacy binding without it also resolves to `GOVERN`.
|
|
135
141
|
- A corrupt binding never means `OFF`. When the payload or legacy integration identifies one
|
|
@@ -131,6 +131,12 @@ harness nativo da LLM.
|
|
|
131
131
|
projeto; com `--session`, grava override, origem e timestamp sem trocar a identidade da sessão.
|
|
132
132
|
- `profile route` exige `--session` e `--reason`, aceita somente os quatro perfis adaptativos e
|
|
133
133
|
grava `lease_id`, motivo, turno/sequência e timestamp sem tocar no perfil persistente.
|
|
134
|
+
- `operating_profile_task` pertence ao active context da work session/sessão causal. `profile route`
|
|
135
|
+
cria e `profile status --session <id>` consulta somente essa lease; os hooks usam o mesmo contexto
|
|
136
|
+
do chamador e o Stop aceito consome por `lease_id` com revision/CAS, sem alterar o sibling.
|
|
137
|
+
- O fallback legado de lease na sessão existe apenas sem `active_contexts`. Com o registry
|
|
138
|
+
inicializado, uma lease global ausente do contexto não é copiada nem aplicada; contexto
|
|
139
|
+
ausente, ambíguo ou stale falha fechado sem mutação parcial.
|
|
134
140
|
- `.wendkeep.json` continua em `schemaVersion: 1`; o campo aditivo usa, por exemplo,
|
|
135
141
|
`"harness": { "profile": "GOVERN" }`. Binding legado sem o campo também resolve `GOVERN`.
|
|
136
142
|
- Binding corrompido nunca equivale a `OFF`. Quando o payload ou a integração legada identifica
|
|
@@ -139,6 +139,7 @@ export function mutateActiveContext(vaultBase, identity, updater, {
|
|
|
139
139
|
expectedRevision,
|
|
140
140
|
now = new Date().toISOString(),
|
|
141
141
|
mutateRegistry = mutateSessionRegistry,
|
|
142
|
+
projectLegacy = true,
|
|
142
143
|
} = {}) {
|
|
143
144
|
const normalized = normalizeIdentity(identity);
|
|
144
145
|
const key = activeContextKey(normalized);
|
|
@@ -191,8 +192,10 @@ export function mutateActiveContext(vaultBase, identity, updater, {
|
|
|
191
192
|
registry.active_contexts = { ...contexts, [key]: next };
|
|
192
193
|
return { key, context: structuredClone(next), registryRevision: registry.active_contexts_revision };
|
|
193
194
|
});
|
|
194
|
-
|
|
195
|
-
|
|
195
|
+
if (projectLegacy) {
|
|
196
|
+
projectLegacyActiveChange(vaultBase);
|
|
197
|
+
projectLegacyActiveDelivery(vaultBase);
|
|
198
|
+
}
|
|
196
199
|
return result;
|
|
197
200
|
}
|
|
198
201
|
|
package/hooks/change-nag.mjs
CHANGED
|
@@ -15,11 +15,11 @@ import {
|
|
|
15
15
|
} from './operating-profile-runtime.mjs';
|
|
16
16
|
import { consumeSessionTaskOperatingProfile } from './operating-profile-task-store.mjs';
|
|
17
17
|
|
|
18
|
-
export function nagDecision(input, vaultBase, { profile = 'GOVERN' } = {}) {
|
|
18
|
+
export function nagDecision(input, vaultBase, { profile = 'GOVERN', context = null } = {}) {
|
|
19
19
|
if (input && input.stop_hook_active) return null; // anti-loop: sempre primeiro
|
|
20
20
|
const policy = hookProfilePolicy(profile);
|
|
21
21
|
if (!policy.harness) return null;
|
|
22
|
-
const gate = quickGateState(vaultBase);
|
|
22
|
+
const gate = quickGateState(vaultBase, { context });
|
|
23
23
|
if (!gate || !gate.openTasks) return null;
|
|
24
24
|
const sid = input?.session_id || input?.sessionId || '';
|
|
25
25
|
const sentinelId = profileSentinelId(sid, profile);
|
|
@@ -39,12 +39,16 @@ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
|
|
|
39
39
|
? null
|
|
40
40
|
: runtime.bindingError
|
|
41
41
|
? { decision: 'block', reason: profileRuntimeError(runtime.bindingError) }
|
|
42
|
-
: nagDecision(input, runtime.vaultBase, {
|
|
42
|
+
: nagDecision(input, runtime.vaultBase, {
|
|
43
|
+
profile: runtime.profile,
|
|
44
|
+
context: runtime.activeContext,
|
|
45
|
+
});
|
|
43
46
|
if (!decision && runtime.taskLease?.state === 'active') {
|
|
44
47
|
consumeSessionTaskOperatingProfile(
|
|
45
48
|
runtime.vaultBase,
|
|
46
49
|
runtime.identity?.canonicalConversationId || input?.session_id || input?.sessionId || '',
|
|
47
50
|
runtime.taskLease.lease_id,
|
|
51
|
+
{ context: runtime.activeContext },
|
|
48
52
|
);
|
|
49
53
|
}
|
|
50
54
|
writeHookOutput(decision || {});
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
import { findProjectBinding, resolveProjectVault } from '../src/project-vault.mjs';
|
|
12
12
|
import { readSessionRegistry } from './obsidian-common.mjs';
|
|
13
13
|
import { resolveSessionIdentity } from './session-identity.mjs';
|
|
14
|
+
import { resolveCommandActiveContext } from '../src/active-context-runtime.mjs';
|
|
15
|
+
import { sessionTaskOperatingProfile } from './operating-profile-task-store.mjs';
|
|
14
16
|
|
|
15
17
|
function bindingDiagnostic(error, configPath = '') {
|
|
16
18
|
return {
|
|
@@ -152,9 +154,26 @@ export function resolveHookOperatingProfile({
|
|
|
152
154
|
const entry = identity.state === 'resolved'
|
|
153
155
|
? readSessionRegistry(vaultResolution.base).sessions?.[identity.canonicalConversationId] || null
|
|
154
156
|
: null;
|
|
157
|
+
let activeContext = null;
|
|
158
|
+
let contextError = null;
|
|
159
|
+
if (!bindingError && identity.state === 'resolved') {
|
|
160
|
+
try {
|
|
161
|
+
activeContext = resolveCommandActiveContext({
|
|
162
|
+
vaultBase: vaultResolution.base,
|
|
163
|
+
projectRoot: input?.cwd || vaultResolution.projectRoot || process.cwd(),
|
|
164
|
+
sessionId: identity.canonicalConversationId,
|
|
165
|
+
});
|
|
166
|
+
} catch (error) {
|
|
167
|
+
contextError = bindingDiagnostic(error);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
155
170
|
const base = sessionOverride(entry) || project;
|
|
156
171
|
const taskLease = evaluateTaskOperatingProfileLease(
|
|
157
|
-
|
|
172
|
+
contextError ? null : sessionTaskOperatingProfile(
|
|
173
|
+
vaultResolution.base,
|
|
174
|
+
identity.canonicalConversationId || '',
|
|
175
|
+
{ context: activeContext },
|
|
176
|
+
),
|
|
158
177
|
taskLeaseContext(entry, input, identity.canonicalConversationId || ''),
|
|
159
178
|
);
|
|
160
179
|
const selected = taskLease.state === 'active'
|
|
@@ -176,6 +195,8 @@ export function resolveHookOperatingProfile({
|
|
|
176
195
|
baseProfile: base.profile,
|
|
177
196
|
baseSource: base.source,
|
|
178
197
|
taskLease,
|
|
198
|
+
activeContext,
|
|
199
|
+
...(contextError ? { contextError } : {}),
|
|
179
200
|
resolution: vaultResolution,
|
|
180
201
|
...(bindingError ? { bindingError } : {}),
|
|
181
202
|
};
|
|
@@ -3,7 +3,8 @@ import { randomUUID } from 'node:crypto';
|
|
|
3
3
|
import {
|
|
4
4
|
createTaskOperatingProfileLease,
|
|
5
5
|
} from '../src/operating-profile.mjs';
|
|
6
|
-
import { mutateSessionRegistry } from './obsidian-common.mjs';
|
|
6
|
+
import { mutateSessionRegistry, readSessionRegistry } from './obsidian-common.mjs';
|
|
7
|
+
import { mutateActiveContext, resolveActiveContext } from './active-context-store.mjs';
|
|
7
8
|
|
|
8
9
|
function isoTimestamp(now) {
|
|
9
10
|
if (typeof now === 'string') return now;
|
|
@@ -17,33 +18,93 @@ function missingSessionError(sessionId) {
|
|
|
17
18
|
return error;
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
function activeContextRequiredError() {
|
|
22
|
+
const error = new Error('active context causal é obrigatório quando o registry contextual está inicializado');
|
|
23
|
+
error.code = 'WENDKEEP_ACTIVE_CONTEXT_REQUIRED';
|
|
24
|
+
return error;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function hasActiveContextRegistry(registry) {
|
|
28
|
+
return Boolean(registry && (
|
|
29
|
+
Object.hasOwn(registry, 'active_contexts')
|
|
30
|
+
|| Object.hasOwn(registry, 'active_contexts_schema')
|
|
31
|
+
|| Object.hasOwn(registry, 'active_contexts_revision')
|
|
32
|
+
));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function leaseFromSession(current, profile, { reason, leaseId, issuedAt, sessionId }) {
|
|
36
|
+
const turnId = typeof current.last_prompt_turn_id === 'string'
|
|
37
|
+
? current.last_prompt_turn_id.trim()
|
|
38
|
+
: '';
|
|
39
|
+
const hasRegisteredTurn = Boolean(
|
|
40
|
+
turnId
|
|
41
|
+
&& current.turn_sequences
|
|
42
|
+
&& Object.hasOwn(current.turn_sequences, turnId)
|
|
43
|
+
&& current.turn_sequences[turnId] === current.last_turn_sequence
|
|
44
|
+
);
|
|
45
|
+
return createTaskOperatingProfileLease({
|
|
46
|
+
profile,
|
|
47
|
+
reason,
|
|
48
|
+
sessionId,
|
|
49
|
+
turnId,
|
|
50
|
+
turnSequence: hasRegisteredTurn ? current.last_turn_sequence : undefined,
|
|
51
|
+
leaseId,
|
|
52
|
+
issuedAt,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function sessionTaskOperatingProfile(vaultBase, sessionId, { context = null } = {}) {
|
|
57
|
+
if (context) {
|
|
58
|
+
try { return resolveActiveContext(vaultBase, context).operating_profile_task || null; }
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (error?.code === 'WENDKEEP_ACTIVE_CONTEXT_NOT_FOUND') {
|
|
61
|
+
const registry = readSessionRegistry(vaultBase);
|
|
62
|
+
if (hasActiveContextRegistry(registry)) return null;
|
|
63
|
+
return registry.sessions?.[sessionId]?.operating_profile_task || null;
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const registry = readSessionRegistry(vaultBase);
|
|
69
|
+
if (hasActiveContextRegistry(registry)) return null;
|
|
70
|
+
return registry.sessions?.[sessionId]?.operating_profile_task || null;
|
|
71
|
+
}
|
|
72
|
+
|
|
20
73
|
export function setSessionTaskOperatingProfile(vaultBase, sessionId, profile, {
|
|
21
74
|
reason,
|
|
22
75
|
leaseId = randomUUID(),
|
|
23
76
|
now,
|
|
77
|
+
context = null,
|
|
78
|
+
mutateContext = mutateActiveContext,
|
|
24
79
|
} = {}) {
|
|
25
80
|
const issuedAt = isoTimestamp(now);
|
|
81
|
+
if (context) {
|
|
82
|
+
let lease;
|
|
83
|
+
mutateContext(vaultBase, context, (active) => ({
|
|
84
|
+
...active,
|
|
85
|
+
operating_profile_task: lease,
|
|
86
|
+
state: 'active',
|
|
87
|
+
}), {
|
|
88
|
+
now: issuedAt,
|
|
89
|
+
projectLegacy: false,
|
|
90
|
+
mutateRegistry: (base, mutator) => mutateSessionRegistry(base, (registry) => {
|
|
91
|
+
const sessions = registry.sessions || (registry.sessions = {});
|
|
92
|
+
if (!Object.hasOwn(sessions, sessionId)) throw missingSessionError(sessionId);
|
|
93
|
+
lease = leaseFromSession(sessions[sessionId], profile, {
|
|
94
|
+
reason, leaseId, issuedAt, sessionId,
|
|
95
|
+
});
|
|
96
|
+
return mutator(registry);
|
|
97
|
+
}),
|
|
98
|
+
});
|
|
99
|
+
return lease;
|
|
100
|
+
}
|
|
26
101
|
return mutateSessionRegistry(vaultBase, (registry) => {
|
|
102
|
+
if (hasActiveContextRegistry(registry)) throw activeContextRequiredError();
|
|
27
103
|
const sessions = registry.sessions || (registry.sessions = {});
|
|
28
104
|
if (!Object.hasOwn(sessions, sessionId)) throw missingSessionError(sessionId);
|
|
29
105
|
const current = sessions[sessionId];
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
: '';
|
|
33
|
-
const hasRegisteredTurn = Boolean(
|
|
34
|
-
turnId
|
|
35
|
-
&& current.turn_sequences
|
|
36
|
-
&& Object.hasOwn(current.turn_sequences, turnId)
|
|
37
|
-
&& current.turn_sequences[turnId] === current.last_turn_sequence
|
|
38
|
-
);
|
|
39
|
-
const lease = createTaskOperatingProfileLease({
|
|
40
|
-
profile,
|
|
41
|
-
reason,
|
|
42
|
-
sessionId,
|
|
43
|
-
turnId,
|
|
44
|
-
turnSequence: hasRegisteredTurn ? current.last_turn_sequence : undefined,
|
|
45
|
-
leaseId,
|
|
46
|
-
issuedAt,
|
|
106
|
+
const lease = leaseFromSession(current, profile, {
|
|
107
|
+
reason, leaseId, issuedAt, sessionId,
|
|
47
108
|
});
|
|
48
109
|
sessions[sessionId] = {
|
|
49
110
|
...current,
|
|
@@ -56,10 +117,53 @@ export function setSessionTaskOperatingProfile(vaultBase, sessionId, profile, {
|
|
|
56
117
|
|
|
57
118
|
export function consumeSessionTaskOperatingProfile(vaultBase, sessionId, leaseId, {
|
|
58
119
|
now,
|
|
120
|
+
context = null,
|
|
121
|
+
mutateContext = mutateActiveContext,
|
|
59
122
|
} = {}) {
|
|
60
123
|
if (!sessionId || !leaseId) return false;
|
|
61
124
|
const consumedAt = isoTimestamp(now);
|
|
125
|
+
if (context) {
|
|
126
|
+
let current;
|
|
127
|
+
try { current = resolveActiveContext(vaultBase, context); }
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (error?.code !== 'WENDKEEP_ACTIVE_CONTEXT_NOT_FOUND') throw error;
|
|
130
|
+
const registry = readSessionRegistry(vaultBase);
|
|
131
|
+
if (hasActiveContextRegistry(registry)) return false;
|
|
132
|
+
return consumeSessionTaskOperatingProfile(vaultBase, sessionId, leaseId, {
|
|
133
|
+
now: consumedAt,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
const lease = current.operating_profile_task;
|
|
137
|
+
if (!lease || lease.state !== 'active' || lease.lease_id !== leaseId) return false;
|
|
138
|
+
let consumed = false;
|
|
139
|
+
try {
|
|
140
|
+
mutateContext(vaultBase, context, (active) => {
|
|
141
|
+
const activeLease = active.operating_profile_task;
|
|
142
|
+
if (!activeLease || activeLease.state !== 'active' || activeLease.lease_id !== leaseId) {
|
|
143
|
+
return active;
|
|
144
|
+
}
|
|
145
|
+
consumed = true;
|
|
146
|
+
return {
|
|
147
|
+
...active,
|
|
148
|
+
operating_profile_task: {
|
|
149
|
+
...activeLease,
|
|
150
|
+
state: 'consumed',
|
|
151
|
+
consumed_at: consumedAt,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}, {
|
|
155
|
+
expectedRevision: current.revision,
|
|
156
|
+
now: consumedAt,
|
|
157
|
+
projectLegacy: false,
|
|
158
|
+
});
|
|
159
|
+
} catch (error) {
|
|
160
|
+
if (error?.code === 'WENDKEEP_ACTIVE_CONTEXT_STALE') return false;
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
return consumed;
|
|
164
|
+
}
|
|
62
165
|
return mutateSessionRegistry(vaultBase, (registry) => {
|
|
166
|
+
if (hasActiveContextRegistry(registry)) throw activeContextRequiredError();
|
|
63
167
|
const current = registry.sessions?.[sessionId];
|
|
64
168
|
const lease = current?.operating_profile_task;
|
|
65
169
|
if (!lease || lease.state !== 'active' || lease.lease_id !== leaseId) return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wendkeep",
|
|
3
|
-
"version": "0.76.
|
|
3
|
+
"version": "0.76.6",
|
|
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
|
@@ -8,7 +8,11 @@ import {
|
|
|
8
8
|
resolveOperatingProfile,
|
|
9
9
|
setOperatingProfile,
|
|
10
10
|
} from './operating-profile.mjs';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
sessionTaskOperatingProfile,
|
|
13
|
+
setSessionTaskOperatingProfile,
|
|
14
|
+
} from '../hooks/operating-profile-task-store.mjs';
|
|
15
|
+
import { resolveCommandActiveContext } from './active-context-runtime.mjs';
|
|
12
16
|
import { resolve } from 'node:path';
|
|
13
17
|
import { findProjectBinding, resolveProjectVault, updateProjectBinding } from './project-vault.mjs';
|
|
14
18
|
|
|
@@ -163,12 +167,13 @@ function sessionProfile(vaultBase, sessionId, projectResolved) {
|
|
|
163
167
|
return sessionBaseProfile(sessions[sessionId], projectResolved);
|
|
164
168
|
}
|
|
165
169
|
|
|
166
|
-
function sessionProfileStatus(vaultBase, sessionId, projectResolved) {
|
|
170
|
+
function sessionProfileStatus(vaultBase, sessionId, projectResolved, { context = null } = {}) {
|
|
167
171
|
const sessions = readSessionRegistry(vaultBase).sessions || {};
|
|
168
172
|
if (!Object.hasOwn(sessions, sessionId)) throw new Error(`sessão não encontrada: ${sessionId}`);
|
|
169
173
|
const entry = sessions[sessionId];
|
|
170
174
|
const base = sessionBaseProfile(entry, projectResolved);
|
|
171
|
-
const taskLease = evaluateTaskOperatingProfileLease(
|
|
175
|
+
const taskLease = evaluateTaskOperatingProfileLease(
|
|
176
|
+
sessionTaskOperatingProfile(vaultBase, sessionId, { context }), {
|
|
172
177
|
sessionId,
|
|
173
178
|
turnId: entry.last_prompt_turn_id || '',
|
|
174
179
|
turnSequence: entry.last_turn_sequence,
|
|
@@ -183,6 +188,15 @@ function sessionProfileStatus(vaultBase, sessionId, projectResolved) {
|
|
|
183
188
|
};
|
|
184
189
|
}
|
|
185
190
|
|
|
191
|
+
function commandActiveContext(state, sessionId) {
|
|
192
|
+
if (!sessionId || state.resolved.bindingError) return null;
|
|
193
|
+
return resolveCommandActiveContext({
|
|
194
|
+
vaultBase: state.vaultBase,
|
|
195
|
+
projectRoot: state.resolved.projectRoot || process.cwd(),
|
|
196
|
+
sessionId,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
186
200
|
function sessionOutputPayload(effective, sessionId) {
|
|
187
201
|
return {
|
|
188
202
|
profile: effective.profile,
|
|
@@ -213,8 +227,9 @@ export function runProfile(argv = []) {
|
|
|
213
227
|
if (sub === 'status' || sub === 'show') {
|
|
214
228
|
if (args.length > 1) return fail(`${sub} não aceita argumentos posicionais adicionais`);
|
|
215
229
|
try {
|
|
230
|
+
const activeContext = sessionId ? commandActiveContext(state, sessionId) : null;
|
|
216
231
|
const effective = sessionId
|
|
217
|
-
? sessionProfileStatus(state.vaultBase, sessionId, projectResolved)
|
|
232
|
+
? sessionProfileStatus(state.vaultBase, sessionId, projectResolved, { context: activeContext })
|
|
218
233
|
: { profile: projectResolved.profile, source: projectResolved.source, scope: 'project' };
|
|
219
234
|
output({
|
|
220
235
|
...(sessionId ? sessionOutputPayload(effective, sessionId) : {
|
|
@@ -235,11 +250,12 @@ export function runProfile(argv = []) {
|
|
|
235
250
|
if (!reason) return fail('route requer --reason <text>');
|
|
236
251
|
try {
|
|
237
252
|
const base = sessionProfile(state.vaultBase, sessionId, projectResolved);
|
|
253
|
+
const activeContext = commandActiveContext(state, sessionId);
|
|
238
254
|
const lease = setSessionTaskOperatingProfile(
|
|
239
255
|
state.vaultBase,
|
|
240
256
|
sessionId,
|
|
241
257
|
args[1],
|
|
242
|
-
{ reason },
|
|
258
|
+
{ reason, context: activeContext },
|
|
243
259
|
);
|
|
244
260
|
output({
|
|
245
261
|
profile: lease.profile,
|