@happycastle/oh-my-openclaw 0.12.4 → 0.12.5

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.
@@ -1,6 +1,5 @@
1
1
  import { getActivePersona, setActivePersona, resetPersonaState } from '../utils/persona-state.js';
2
2
  import { resolvePersonaId, listPersonas, DEFAULT_PERSONA_ID } from '../agents/persona-prompts.js';
3
- import { resetPersonaInjectorState } from '../hooks/persona-injector.js';
4
3
  function getDisplayName(personaId) {
5
4
  const persona = listPersonas().find((p) => p.id === personaId);
6
5
  return persona ? `${persona.emoji} ${persona.displayName}` : personaId;
@@ -16,7 +15,6 @@ export function registerPersonaCommands(api) {
16
15
  api.logger.info(`[omoc] Parsed args: "${args}" (length: ${args.length})`);
17
16
  if (!args) {
18
17
  const previousId = getActivePersona();
19
- resetPersonaInjectorState();
20
18
  setActivePersona(DEFAULT_PERSONA_ID);
21
19
  const name = getDisplayName(DEFAULT_PERSONA_ID);
22
20
  const switchNote = previousId && previousId !== DEFAULT_PERSONA_ID
@@ -30,7 +28,6 @@ export function registerPersonaCommands(api) {
30
28
  const wasActive = getActivePersona();
31
29
  const wasName = wasActive ? getDisplayName(wasActive) : null;
32
30
  resetPersonaState();
33
- resetPersonaInjectorState();
34
31
  return {
35
32
  text: wasName
36
33
  ? `# OmOC Mode: OFF\n\nPersona **${wasName}** deactivated. Applied immediately — your next message will use default behavior.`
@@ -67,7 +64,6 @@ export function registerPersonaCommands(api) {
67
64
  };
68
65
  }
69
66
  const previousId = getActivePersona();
70
- resetPersonaInjectorState();
71
67
  setActivePersona(resolvedId);
72
68
  const displayName = getDisplayName(resolvedId);
73
69
  const switched = listPersonas().find((p) => p.id === resolvedId);
@@ -1,21 +1,22 @@
1
1
  import { contextCollector } from '../features/context-collector.js';
2
2
  export function registerContextInjector(api) {
3
- api.registerHook('before_prompt_build', (event) => {
4
- const sessionKey = event.agentId || 'default';
3
+ // Use the typed hook system (api.on) instead of api.registerHook.
4
+ // api.registerHook registers into the internal hook system which does NOT
5
+ // trigger before_prompt_build — only hookRunner (typed hooks) does.
6
+ api.on('before_prompt_build', (_event, ctx) => {
7
+ const sessionKey = ctx.agentId || 'default';
5
8
  if (!contextCollector.hasEntries(sessionKey)) {
6
- return event;
9
+ return;
7
10
  }
8
11
  const entryCount = contextCollector.getEntries(sessionKey).length;
9
12
  const collectedContext = contextCollector.collectAsString(sessionKey);
10
13
  if (!collectedContext) {
11
- return event;
14
+ return;
12
15
  }
13
- const existing = event.prependContext || '';
14
- event.prependContext = existing ? `${existing}\n\n${collectedContext}` : collectedContext;
15
- api.logger.info(`[omoc] Context injected: ${entryCount} entries for ${sessionKey}`);
16
- return event;
17
- }, {
18
- name: 'oh-my-openclaw.context-injector',
19
- description: 'Unified context injection from ContextCollector into prependContext',
20
- });
16
+ api.logger.info(`[omoc] Context injected via before_prompt_build: ${entryCount} entries for ${sessionKey}`);
17
+ return {
18
+ prependContext: collectedContext,
19
+ };
20
+ }, { priority: 50 } // Lower priority than persona (100) — persona goes first
21
+ );
21
22
  }
@@ -1,7 +1,2 @@
1
1
  import { OmocPluginApi } from '../types.js';
2
- export declare function resetPersonaContextEntries(): void;
3
- export declare function resetPersonaInjectorState(): void;
4
- export declare function getPersonaInjectorState(): {
5
- lastInjectedPersonaId: string | null;
6
- };
7
2
  export declare function registerPersonaInjector(api: OmocPluginApi): void;
@@ -1,61 +1,54 @@
1
1
  import { getActivePersona } from '../utils/persona-state.js';
2
- import { readPersonaPromptSync } from '../agents/persona-prompts.js';
3
- import { contextCollector } from '../features/context-collector.js';
4
- let lastInjectedPersonaId = null;
5
- const personaSessionKeys = new Set();
6
- export function resetPersonaContextEntries() {
7
- for (const sessionKey of personaSessionKeys) {
8
- const entries = contextCollector.getEntries(sessionKey);
9
- for (const entry of entries) {
10
- if (entry.source === 'persona') {
11
- contextCollector.unregister(sessionKey, entry.id);
12
- }
13
- }
2
+ import { readPersonaPromptSync, resolvePersonaId } from '../agents/persona-prompts.js';
3
+ /**
4
+ * Resolve the effective persona ID.
5
+ *
6
+ * Priority:
7
+ * 1. Manually set persona via /omoc command (getActivePersona())
8
+ * 2. agentId from the hook context (set by OpenClaw core)
9
+ * 3. null no persona to inject
10
+ */
11
+ function resolveEffectivePersona(ctx) {
12
+ const manual = getActivePersona();
13
+ if (manual) {
14
+ const resolved = resolvePersonaId(manual);
15
+ if (resolved)
16
+ return { personaId: resolved, source: 'manual' };
14
17
  }
15
- personaSessionKeys.clear();
16
- }
17
- export function resetPersonaInjectorState() {
18
- lastInjectedPersonaId = null;
19
- resetPersonaContextEntries();
20
- }
21
- export function getPersonaInjectorState() {
22
- return { lastInjectedPersonaId };
18
+ const agentId = ctx.agentId;
19
+ if (!agentId)
20
+ return null;
21
+ const resolved = resolvePersonaId(agentId);
22
+ if (!resolved)
23
+ return null;
24
+ return { personaId: resolved, source: 'auto' };
23
25
  }
24
26
  export function registerPersonaInjector(api) {
25
- api.registerHook('agent:bootstrap', (event) => {
26
- const personaId = getActivePersona();
27
- const sessionKey = event.context.agentId || 'default';
28
- if (!personaId) {
29
- if (lastInjectedPersonaId) {
30
- contextCollector.unregister(sessionKey, `persona/${lastInjectedPersonaId}`);
31
- lastInjectedPersonaId = null;
32
- api.logger.info(`[omoc] Persona context cleared for ${sessionKey}`);
33
- }
34
- return;
35
- }
36
- if (lastInjectedPersonaId === personaId) {
27
+ // Use the typed hook system (api.on) for before_prompt_build.
28
+ // This directly injects into the system prompt via prependContext,
29
+ // which is more reliable than bootstrapFiles via agent:bootstrap.
30
+ //
31
+ // api.registerHook('before_prompt_build', ...) registers into the internal
32
+ // hook system which does NOT trigger before_prompt_build — only hookRunner
33
+ // (typed hooks via api.on) does.
34
+ api.on('before_prompt_build', (_event, ctx) => {
35
+ const result = resolveEffectivePersona(ctx);
36
+ if (!result) {
37
+ api.logger.info(`[omoc] Persona injector: no persona resolved (agentId=${ctx.agentId ?? 'none'}, manual=${getActivePersona() ?? 'none'})`);
37
38
  return;
38
39
  }
40
+ const { personaId, source } = result;
39
41
  try {
40
- if (lastInjectedPersonaId) {
41
- contextCollector.unregister(sessionKey, `persona/${lastInjectedPersonaId}`);
42
- }
43
42
  const content = readPersonaPromptSync(personaId);
44
- contextCollector.register(sessionKey, {
45
- id: `persona/${personaId}`,
46
- content,
47
- priority: 'high',
48
- source: 'persona',
49
- });
50
- personaSessionKeys.add(sessionKey);
51
- lastInjectedPersonaId = personaId;
52
- api.logger.info(`[omoc] Persona context registered: ${personaId}`);
43
+ api.logger.info(`[omoc] Persona injected via before_prompt_build: ${personaId} (${source}, agentId=${ctx.agentId ?? 'none'})`);
44
+ return {
45
+ prependContext: content,
46
+ };
53
47
  }
54
48
  catch (err) {
55
- api.logger.error(`[omoc] Failed to register persona context ${personaId}:`, err);
49
+ api.logger.error(`[omoc] Failed to inject persona ${personaId}:`, err);
50
+ return;
56
51
  }
57
- }, {
58
- name: 'oh-my-openclaw.persona-injector',
59
- description: 'Injects active persona prompt once per persona change',
60
- });
52
+ }, { priority: 100 } // High priority — persona prompt should be prepended first
53
+ );
61
54
  }
package/dist/types.d.ts CHANGED
@@ -122,4 +122,22 @@ export interface OmocPluginApi {
122
122
  }) => void | Promise<void>, opts?: {
123
123
  commands?: string[];
124
124
  }) => void;
125
+ on: <TEvent = unknown, TResult = unknown>(hookName: string, handler: (event: TEvent, ctx: TypedHookContext) => TResult | Promise<TResult> | void, opts?: {
126
+ priority?: number;
127
+ }) => void;
128
+ }
129
+ export interface TypedHookContext {
130
+ agentId?: string;
131
+ sessionKey?: string;
132
+ sessionId?: string;
133
+ workspaceDir?: string;
134
+ messageProvider?: unknown;
135
+ }
136
+ export interface BeforePromptBuildResult {
137
+ systemPrompt?: string;
138
+ prependContext?: string;
139
+ }
140
+ export interface BeforePromptBuildEvent {
141
+ prompt?: string;
142
+ messages?: unknown[];
125
143
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happycastle/oh-my-openclaw",
3
- "version": "0.12.4",
3
+ "version": "0.12.5",
4
4
  "description": "Oh-My-OpenClaw plugin — multi-agent orchestration, todo enforcer, ralph loop, and custom tools for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",