@jungjaehoon/mama-core 4.0.0 → 4.1.1

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/README.md CHANGED
@@ -4,7 +4,7 @@ The shared engine for storage, records and revisions, evidence links, memory, se
4
4
  runtime drivers. Consumers supply their own database, principals, source access and product
5
5
  vocabulary through public exports. Core does not require the MAMA OS daemon.
6
6
 
7
- Version **4.0.0**; Node.js 22.13+. This README describes the current rebuild checkout.
7
+ Version **4.1.1**; Node.js 22.13+. This README describes the current rebuild checkout.
8
8
  See [architecture](../../docs/explanation/architecture.md) and
9
9
  [the shared-engine goal](../../INTENT.md).
10
10
 
@@ -0,0 +1,2 @@
1
+ INSERT OR IGNORE INTO schema_version (version, description)
2
+ VALUES (99, 'Commitment revision graph links');
@@ -55,7 +55,7 @@ const refSchema = {
55
55
  kind: {
56
56
  type: 'string',
57
57
  enum: twin_edge_types_js_1.TWIN_REF_KINDS,
58
- description: 'Referenced graph kind, e.g. "work".',
58
+ description: 'Referenced graph kind, e.g. "memory" (a work item\'s revision record) or "registry".',
59
59
  },
60
60
  id: {
61
61
  type: 'string',
@@ -145,7 +145,7 @@ const workGraphQuerySchema = {
145
145
  },
146
146
  seeds: {
147
147
  type: 'array',
148
- description: 'Starting graph references, e.g. [{"kind":"work","id":"work_123"}].',
148
+ description: 'Starting graph references, e.g. [{"kind":"memory","id":"judgment_123"}].',
149
149
  items: refSchema,
150
150
  },
151
151
  search: {
@@ -161,7 +161,7 @@ const workGraphQuerySchema = {
161
161
  },
162
162
  kinds: {
163
163
  type: 'array',
164
- description: 'Kinds to restrict the search, e.g. ["work"].',
164
+ description: 'Kinds to restrict the search, e.g. ["registry"].',
165
165
  items: { type: 'string', enum: twin_edge_types_js_1.TWIN_REF_KINDS },
166
166
  },
167
167
  },
@@ -183,7 +183,7 @@ const workGraphQuerySchema = {
183
183
  },
184
184
  from: {
185
185
  ...refSchema,
186
- description: 'Path/timeline start reference, e.g. {"kind":"work","id":"work_123"}.',
186
+ description: 'Path/timeline start reference, e.g. {"kind":"memory","id":"judgment_123"}.',
187
187
  },
188
188
  to: {
189
189
  ...refSchema,
@@ -614,7 +614,7 @@ function coreActionRegistrations(knowledge, adapter, deps) {
614
614
  {
615
615
  contract: {
616
616
  name: 'graph.query',
617
- summary: 'Read the work graph: overview roots, neighbors, paths, timelines, and hydrated details under the caller authority. search resolves a name spelling to its registered node or alias — an empty page with coverage.search_no_match means nothing is registered under that spelling, the signal to graph.node.put it.',
617
+ summary: 'Read the work graph: overview roots, neighbors, paths, timelines, and hydrated details under the caller authority. Each view takes its own inputs: browse pages every visible edge and accepts only history, relations, asOf, limit and cursor; paths requires from and to; neighbors, timeline and detail need seeds or a search. search resolves a name spelling to its registered node or alias; an empty page with coverage.search_no_match means nothing is registered under that spelling. Ref kinds are memory, case, report, edge, raw, registry and observation; a work item is reached through its memory (revision) records.',
618
618
  inputSchema: workGraphQuerySchema,
619
619
  examples: [
620
620
  {
@@ -272,7 +272,11 @@ function validateInput(schema, value, path) {
272
272
  if (schema.oneOf !== undefined) {
273
273
  const matches = schema.oneOf.filter((sub) => validateInput(sub, value, path) === null).length;
274
274
  if (matches !== 1) {
275
- return `${path} must match exactly one allowed shape (${matches} matched).`;
275
+ // A refusal names what is allowed: "(0 matched)" alone once left a caller dropping the
276
+ // field and the write with it.
277
+ const shapes = schema.oneOf.map(shapeName).join(', ');
278
+ const described = schema.description === undefined ? '' : ` ${schema.description}`;
279
+ return `${path} must match exactly one of: ${shapes} (${matches} matched).${described}`;
276
280
  }
277
281
  }
278
282
  if (typeof value === 'number') {
@@ -349,6 +353,13 @@ function validateInput(schema, value, path) {
349
353
  }
350
354
  return null;
351
355
  }
356
+ function shapeName(schema) {
357
+ if (schema.const !== undefined)
358
+ return JSON.stringify(schema.const);
359
+ if (schema.enum !== undefined)
360
+ return schema.enum.map((value) => JSON.stringify(value)).join(' | ');
361
+ return schema.type ?? 'a described shape';
362
+ }
352
363
  function typeMismatch(type, value) {
353
364
  switch (type) {
354
365
  case 'object':
@@ -14,6 +14,7 @@ const fs_1 = __importDefault(require("fs"));
14
14
  const node_sqlite_statement_js_1 = require("./node-sqlite-statement.js");
15
15
  const debug_logger_js_1 = require("../debug-logger.js");
16
16
  const embedder_js_1 = require("../embedding/embedder.js");
17
+ const commitment_revision_migration_js_1 = require("../knowledge/commitment-revision-migration.js");
17
18
  const SQLITE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
18
19
  // Migration 071 origin invariant, kept in ONE place so the DDL the rebuild
19
20
  // writes and the shape verification that admits a table cannot drift. A row is
@@ -674,6 +675,22 @@ class NodeSQLiteAdapter {
674
675
  if (version <= currentVersion) {
675
676
  continue;
676
677
  }
678
+ if (isCore && version === 99) {
679
+ try {
680
+ this.exec('BEGIN TRANSACTION');
681
+ (0, commitment_revision_migration_js_1.backfillCommitmentRevisionGraph)(this);
682
+ this.exec(fs_1.default.readFileSync(path_1.default.join(migrationsDir, file), 'utf8'));
683
+ this.stampMigration(sourceName, version);
684
+ this.exec('COMMIT');
685
+ (0, debug_logger_js_1.info)(`[node-sqlite-adapter] Migration ${file} applied successfully`);
686
+ }
687
+ catch (err) {
688
+ this.exec('ROLLBACK');
689
+ const message = err instanceof Error ? err.message : String(err);
690
+ throw new Error(`Migration ${file} failed: ${message}`);
691
+ }
692
+ continue;
693
+ }
677
694
  if (isCore && version === 98) {
678
695
  const decisionColumns = this.tableColumns('decisions');
679
696
  if (!this.tableExists('decisions_fts') ||
@@ -0,0 +1,4 @@
1
+ import type { DatabaseAdapter } from '../db-manager.js';
2
+ /** Link every stored commitment revision to the revision it follows. */
3
+ export declare function backfillCommitmentRevisionGraph(adapter: DatabaseAdapter): void;
4
+ //# sourceMappingURL=commitment-revision-migration.d.ts.map
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.backfillCommitmentRevisionGraph = backfillCommitmentRevisionGraph;
4
+ const judgment_edge_js_1 = require("./judgment-edge.js");
5
+ /** Link every stored commitment revision to the revision it follows. */
6
+ function backfillCommitmentRevisionGraph(adapter) {
7
+ const assignments = adapter
8
+ .prepare(`SELECT assignment.commitment_id, assignment.revision, assignment.record_id, assignment.operation,
9
+ assignment.agent_id, assignment.model_run_id, assignment.created_at
10
+ FROM commitment_assignments AS assignment
11
+ JOIN commitments AS commitment ON commitment.commitment_id = assignment.commitment_id
12
+ ORDER BY assignment.commitment_id, assignment.revision`)
13
+ .all();
14
+ if (assignments.length === 0)
15
+ return;
16
+ const byCommitment = new Map();
17
+ for (const assignment of assignments) {
18
+ const rows = byCommitment.get(assignment.commitment_id) ?? [];
19
+ rows.push(assignment);
20
+ byCommitment.set(assignment.commitment_id, rows);
21
+ }
22
+ const hasEdge = adapter.prepare(`SELECT 1 FROM twin_edges
23
+ WHERE edge_type = 'builds_on'
24
+ AND subject_kind = 'memory' AND subject_id = ?
25
+ AND object_kind = 'memory' AND object_id = ?`);
26
+ const insertEdge = adapter.prepare(`INSERT OR IGNORE INTO twin_edges (
27
+ edge_id, edge_type, subject_kind, subject_id, object_kind, object_id,
28
+ relation_attrs_json, confidence, source, agent_id, model_run_id, content_hash, created_at
29
+ ) VALUES (?, 'builds_on', 'memory', ?, 'memory', ?, '{}', 1.0, 'code', ?, ?, ?, ?)`);
30
+ for (const [commitmentId, revisions] of byCommitment) {
31
+ for (let index = 0; index < revisions.length; index += 1) {
32
+ const current = revisions[index];
33
+ if (current.operation === 'create')
34
+ continue;
35
+ const previous = revisions[index - 1];
36
+ if (!previous) {
37
+ throw new Error(`Commitment ${commitmentId} revision ${current.revision} has no predecessor`);
38
+ }
39
+ if (hasEdge.get(current.record_id, previous.record_id))
40
+ continue;
41
+ const link = {
42
+ relation: 'builds_on',
43
+ target: { kind: 'memory', id: previous.record_id },
44
+ };
45
+ const id = (0, judgment_edge_js_1.judgmentEdgeId)(`migration:099:${commitmentId}:${current.revision}`, 0, link.relation, link.target);
46
+ insertEdge.run(id, current.record_id, previous.record_id, current.agent_id, current.model_run_id, (0, judgment_edge_js_1.judgmentEdgeContentHash)(id, current.record_id, link), current.created_at);
47
+ }
48
+ }
49
+ }
50
+ //# sourceMappingURL=commitment-revision-migration.js.map
@@ -102,8 +102,6 @@ export interface WorkCommand {
102
102
  commandId: string;
103
103
  /** Native run that authored this work, when it was written during a model turn. */
104
104
  modelRunId?: string | null;
105
- /** What this commitment is about; becomes the record topic. */
106
- topic: string;
107
105
  /** What the caller is committing to, in its own words. */
108
106
  summary: string;
109
107
  reasoning?: string;
@@ -131,6 +129,8 @@ export interface WorkCommand {
131
129
  event?: JudgmentEventMeta;
132
130
  }
133
131
  export interface CreateWorkCommand extends WorkCommand {
132
+ /** What this new commitment is about; becomes the record topic. */
133
+ topic: string;
134
134
  set: OwnerWorkPatch;
135
135
  /**
136
136
  * Import vocabulary: the identity this work already had in a predecessor
@@ -147,12 +147,16 @@ export interface CreateWorkCommand extends WorkCommand {
147
147
  }
148
148
  export interface ReviseWorkCommand extends WorkCommand {
149
149
  commitmentId: string;
150
+ /** The revision record's topic; the work item's topic when omitted. */
151
+ topic?: string;
150
152
  expectedRevision?: number;
151
153
  set?: OwnerWorkPatch;
152
154
  clear?: Array<keyof OwnerWorkPatch>;
153
155
  }
154
156
  export interface WithdrawWorkCommand extends WorkCommand {
155
157
  commitmentId: string;
158
+ /** The withdrawal record's topic; the work item's topic when omitted. */
159
+ topic?: string;
156
160
  expectedRevision: number;
157
161
  }
158
162
  export interface WorkWriteResult {
@@ -331,8 +331,9 @@ async function reviseWork(command, access, options) {
331
331
  if (readWork(options.adapter, { commitmentId: command.commitmentId }, access).items.length === 0) {
332
332
  throw new judgments_js_1.JudgmentError('REFERENCE_NOT_FOUND', `Commitment is unavailable: ${command.commitmentId}`);
333
333
  }
334
+ const topic = command.topic ?? workTopic(options.adapter, command.commitmentId);
334
335
  const receipt = await (0, judgments_js_1.appendJudgment)({
335
- ...recordFields(command),
336
+ ...recordFields({ ...command, topic }),
336
337
  work: {
337
338
  operation: 'revise',
338
339
  commitmentId: command.commitmentId,
@@ -357,8 +358,9 @@ async function withdrawWork(command, access, options) {
357
358
  if (readWork(options.adapter, { commitmentId: command.commitmentId }, access).items.length === 0) {
358
359
  throw new judgments_js_1.JudgmentError('REFERENCE_NOT_FOUND', `Commitment is unavailable: ${command.commitmentId}`);
359
360
  }
361
+ const topic = command.topic ?? workTopic(options.adapter, command.commitmentId);
360
362
  const receipt = await (0, judgments_js_1.appendJudgment)({
361
- ...recordFields(command),
363
+ ...recordFields({ ...command, topic }),
362
364
  work: {
363
365
  operation: 'withdraw',
364
366
  commitmentId: command.commitmentId,
@@ -367,4 +369,18 @@ async function withdrawWork(command, access, options) {
367
369
  }, access, options);
368
370
  return requireWorkReceipt(receipt);
369
371
  }
372
+ function workTopic(adapter, commitmentId) {
373
+ const row = adapter
374
+ .prepare(`SELECT decision.topic
375
+ FROM commitment_assignments AS assignment
376
+ JOIN decisions AS decision ON decision.id = assignment.record_id
377
+ WHERE assignment.commitment_id = ? AND assignment.operation = 'create'
378
+ ORDER BY assignment.revision
379
+ LIMIT 1`)
380
+ .get(commitmentId);
381
+ if (!row) {
382
+ throw new judgments_js_1.JudgmentError('REFERENCE_NOT_FOUND', `Commitment is unavailable: ${commitmentId}`);
383
+ }
384
+ return row.topic;
385
+ }
370
386
  //# sourceMappingURL=commitments.js.map
@@ -0,0 +1,6 @@
1
+ import type { RecordLink, WorkReference } from '../memory/judgment-types.js';
2
+ /** The stable id of the index-th link a judgment command writes. */
3
+ export declare function judgmentEdgeId(commandId: string, index: number, relation: string, target: WorkReference): string;
4
+ /** The content hash stored on a judgment link's twin edge. */
5
+ export declare function judgmentEdgeContentHash(id: string, recordId: string, link: RecordLink): Buffer;
6
+ //# sourceMappingURL=judgment-edge.d.ts.map
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.judgmentEdgeId = judgmentEdgeId;
7
+ exports.judgmentEdgeContentHash = judgmentEdgeContentHash;
8
+ const node_crypto_1 = __importDefault(require("node:crypto"));
9
+ const canonicalize_js_1 = require("../canonicalize.js");
10
+ /** The stable id of the index-th link a judgment command writes. */
11
+ function judgmentEdgeId(commandId, index, relation, target) {
12
+ return `edge_${node_crypto_1.default
13
+ .createHash('sha256')
14
+ .update((0, canonicalize_js_1.canonicalizeJSON)({ commandId, index, relation, target }))
15
+ .digest('hex')
16
+ .slice(0, 24)}`;
17
+ }
18
+ /** The content hash stored on a judgment link's twin edge. */
19
+ function judgmentEdgeContentHash(id, recordId, link) {
20
+ return node_crypto_1.default.createHash('sha256').update((0, canonicalize_js_1.canonicalizeJSON)({ id, recordId, link })).digest();
21
+ }
22
+ //# sourceMappingURL=judgment-edge.js.map
@@ -16,6 +16,7 @@ exports.listTwinEdgesForRefs = listTwinEdgesForRefs;
16
16
  const node_crypto_1 = __importDefault(require("node:crypto"));
17
17
  const db_manager_js_1 = require("../db-manager.js");
18
18
  const canonicalize_js_1 = require("../canonicalize.js");
19
+ const judgment_edge_js_1 = require("./judgment-edge.js");
19
20
  const twin_edge_types_js_1 = require("./twin-edge-types.js");
20
21
  const event_store_js_1 = require("../memory/event-store.js");
21
22
  const record_identity_js_1 = require("../registry/record-identity.js");
@@ -323,20 +324,10 @@ function assertReplay(adapter, command, access, hash) {
323
324
  }
324
325
  return parseReceipt(row.receipt_json);
325
326
  }
326
- function edgeId(commandId, index, relation, target) {
327
- return `edge_${node_crypto_1.default
328
- .createHash('sha256')
329
- .update((0, canonicalize_js_1.canonicalizeJSON)({ commandId, index, relation, target }))
330
- .digest('hex')
331
- .slice(0, 24)}`;
332
- }
333
327
  function insertLink(adapter, command, recordId, link, index, access, now) {
334
- const id = edgeId(command.commandId, index, link.relation, link.target);
328
+ const id = (0, judgment_edge_js_1.judgmentEdgeId)(command.commandId, index, link.relation, link.target);
335
329
  const attrs = link.attrs ?? {};
336
- const contentHash = node_crypto_1.default
337
- .createHash('sha256')
338
- .update((0, canonicalize_js_1.canonicalizeJSON)({ id, recordId, link }))
339
- .digest();
330
+ const contentHash = (0, judgment_edge_js_1.judgmentEdgeContentHash)(id, recordId, link);
340
331
  insertTwinEdge(adapter, {
341
332
  edge_id: id,
342
333
  edge_type: link.relation,
@@ -537,7 +528,7 @@ async function appendJudgmentOnAdapter(adapter, command, access, embedder) {
537
528
  }
538
529
  else {
539
530
  const current = adapter
540
- .prepare('SELECT current_revision, withdrawn FROM commitments WHERE commitment_id = ?')
531
+ .prepare('SELECT current_revision, head_record_id, withdrawn FROM commitments WHERE commitment_id = ?')
541
532
  .get(work.commitmentId);
542
533
  if (!current) {
543
534
  throw new JudgmentError('REFERENCE_NOT_FOUND', `Commitment is unavailable: ${work.commitmentId}`);
@@ -556,6 +547,9 @@ async function appendJudgmentOnAdapter(adapter, command, access, embedder) {
556
547
  (commitment_id, revision, record_id, operation, set_json, clear_json, applies_from, applies_until, created_at, agent_id, model_run_id)
557
548
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
558
549
  .run(work.commitmentId, revision, recordId, work.operation, (0, canonicalize_js_1.canonicalizeJSON)(workPatch(work)), (0, canonicalize_js_1.canonicalizeJSON)(work.clear ?? []), command.eventDatetime ?? null, command.appliesUntil ?? null, domainNow, access.agentId, command.modelRunId ?? null);
550
+ edgeIds.push(insertLink(adapter, command, recordId, { relation: 'builds_on', target: { kind: 'memory', id: current.head_record_id } }, command.links?.length ?? 0,
551
+ // The host links a revision to the record it revises; the agent did not judge it.
552
+ { ...access, edgeSource: 'code' }, now));
559
553
  adapter
560
554
  .prepare('UPDATE commitments SET current_revision = ?, head_record_id = ?, withdrawn = ?, updated_at = ?, agent_id = ?, model_run_id = ? WHERE commitment_id = ?')
561
555
  .run(revision, recordId, work.operation === 'withdraw' ? 1 : 0, domainNow, access.agentId, command.modelRunId ?? null, work.commitmentId);
@@ -21,6 +21,7 @@
21
21
  * - ⚠️ Tool integration via MCP (future work)
22
22
  */
23
23
  import type { PromptCallbacks, PromptResult } from './types.js';
24
+ import { type ThinkingEffort } from './claude-effort.js';
24
25
  export interface ClaudeCLIWrapperOptions {
25
26
  /** The isolated workspace this CLI runs in; the product states where. */
26
27
  /**
@@ -29,12 +30,8 @@ export interface ClaudeCLIWrapperOptions {
29
30
  */
30
31
  workspaceDir: string;
31
32
  model?: string;
32
- /**
33
- * Effort level for Claude 4.6 adaptive thinking
34
- * Applies to claude-opus-4-6 and claude-sonnet-4-6
35
- * 'max' is only available on Opus 4.6
36
- */
37
- effort?: 'low' | 'medium' | 'high' | 'max';
33
+ /** Thinking effort passed as --effort on models that accept it. */
34
+ effort?: ThinkingEffort;
38
35
  sessionId?: string;
39
36
  systemPrompt?: string;
40
37
  mcpConfigPath?: string;
@@ -66,21 +66,9 @@ const path_1 = __importDefault(require("path"));
66
66
  const debugLogger = __importStar(require("@jungjaehoon/mama-core/debug-logger"));
67
67
  const cli_secret_redaction_js_1 = require("./cli-secret-redaction.js");
68
68
  const cli_arg_redaction_js_1 = require("./cli-arg-redaction.js");
69
+ const claude_effort_js_1 = require("./claude-effort.js");
69
70
  const { DebugLogger } = debugLogger;
70
71
  const logger = new DebugLogger('ClaudeCLI');
71
- function supportsThinkingEffortModel(model) {
72
- if (!model) {
73
- return false;
74
- }
75
- // Adaptive thinking effort: Claude 4.6 and every Claude 5 family model accept --effort.
76
- return /^claude-(opus|sonnet|haiku|fable)-(4-6|5)(\b|-)/.test(model);
77
- }
78
- function normalizeThinkingEffort(model, effort) {
79
- if (effort === 'max' && !(model && /^claude-(opus-4-6|opus-5|fable-5)(\b|-)/.test(model))) {
80
- return 'high';
81
- }
82
- return effort;
83
- }
84
72
  /**
85
73
  * ClaudeCLIWrapper - Wraps Claude CLI for programmatic use
86
74
  *
@@ -144,12 +132,7 @@ class ClaudeCLIWrapper {
144
132
  if (model) {
145
133
  args.push('--model', model);
146
134
  }
147
- // Add effort level for Claude 4.6 adaptive thinking.
148
- if (this.options.effort && supportsThinkingEffortModel(model)) {
149
- const effort = normalizeThinkingEffort(model, this.options.effort);
150
- args.push('--effort', effort);
151
- logger.debug('Effort level:', effort);
152
- }
135
+ args.push(...(0, claude_effort_js_1.claudeEffortArgs)(model, this.options.effort));
153
136
  // System prompt: first turn only (session persistence keeps it across turns)
154
137
  if (this.options.systemPrompt && this.turnCount === 0) {
155
138
  args.push('--system-prompt', this.options.systemPrompt);
@@ -0,0 +1,4 @@
1
+ export type ThinkingEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
2
+ /** The `--effort` arguments for a Claude model; none when the model takes no effort setting. */
3
+ export declare function claudeEffortArgs(model: string | undefined, effort: ThinkingEffort | undefined): string[];
4
+ //# sourceMappingURL=claude-effort.d.ts.map
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.claudeEffortArgs = claudeEffortArgs;
4
+ /** The `--effort` arguments for a Claude model; none when the model takes no effort setting. */
5
+ function claudeEffortArgs(model, effort) {
6
+ // --effort: Opus/Sonnet 4.6, Opus 4.7/4.8, and the Claude 5 family (Opus, Sonnet, Fable, Mythos).
7
+ if (!model ||
8
+ !effort ||
9
+ !/^claude-(?:(?:opus|sonnet)-4-6|opus-4-[78]|(?:opus|sonnet|fable|mythos)-5)(?:\b|-)/.test(model)) {
10
+ return [];
11
+ }
12
+ // xhigh arrived with Opus 4.7; the 4.6 models take low through max.
13
+ const accepted = effort === 'xhigh' && /^claude-(?:opus|sonnet)-4-6(?:\b|-)/.test(model) ? 'high' : effort;
14
+ return ['--effort', accepted];
15
+ }
16
+ //# sourceMappingURL=claude-effort.js.map
@@ -26,6 +26,7 @@
26
26
  import { EventEmitter } from 'events';
27
27
  import { type SessionPolicyStatus } from './types.js';
28
28
  import { type PromptCallbacks, type PromptResult, type TokenUsageRecord } from './types.js';
29
+ import { type ThinkingEffort } from './claude-effort.js';
29
30
  export interface PersistentProcessOptions {
30
31
  sessionId: string;
31
32
  /**
@@ -93,8 +94,8 @@ export interface PersistentProcessOptions {
93
94
  channelKey?: string;
94
95
  /** Agent ID for token usage tracking */
95
96
  agentId?: string;
96
- /** Effort level for Claude 4.6 adaptive thinking */
97
- effort?: 'low' | 'medium' | 'high' | 'max';
97
+ /** Thinking effort passed as --effort on models that accept it. */
98
+ effort?: ThinkingEffort;
98
99
  }
99
100
  export interface PersistentProcessAcquireResult {
100
101
  process: PersistentClaudeProcess;
@@ -69,22 +69,10 @@ const types_js_1 = require("./types.js");
69
69
  const debugLogger = __importStar(require("@jungjaehoon/mama-core/debug-logger"));
70
70
  const cli_secret_redaction_js_1 = require("./cli-secret-redaction.js");
71
71
  const cli_arg_redaction_js_1 = require("./cli-arg-redaction.js");
72
+ const claude_effort_js_1 = require("./claude-effort.js");
72
73
  const { DebugLogger } = debugLogger;
73
74
  const persistentLogger = new DebugLogger('PersistentCLI');
74
75
  const poolLogger = new DebugLogger('ProcessPool');
75
- function supportsThinkingEffortModel(model) {
76
- if (!model) {
77
- return false;
78
- }
79
- // Adaptive thinking effort: Claude 4.6 and every Claude 5 family model accept --effort.
80
- return /^claude-(opus|sonnet|haiku|fable)-(4-6|5)(\b|-)/.test(model);
81
- }
82
- function normalizeThinkingEffort(model, effort) {
83
- if (effort === 'max' && !(model && /^claude-(opus-4-6|opus-5|fable-5)(\b|-)/.test(model))) {
84
- return 'high';
85
- }
86
- return effort;
87
- }
88
76
  /**
89
77
  * Regex to strip lone Unicode surrogates that cause API 400 errors.
90
78
  * Matches high surrogates not followed by a low surrogate, and
@@ -351,9 +339,7 @@ class PersistentClaudeProcess extends events_1.EventEmitter {
351
339
  if (this.options.useGatewayTools) {
352
340
  persistentLogger.info('Gateway Tools mode enabled');
353
341
  }
354
- if (this.options.effort && supportsThinkingEffortModel(this.options.model)) {
355
- args.push('--effort', normalizeThinkingEffort(this.options.model, this.options.effort));
356
- }
342
+ args.push(...(0, claude_effort_js_1.claudeEffortArgs)(this.options.model, this.options.effort));
357
343
  if (this.options.permissionMode) {
358
344
  args.push('--permission-mode', this.options.permissionMode);
359
345
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jungjaehoon/mama-core",
3
- "version": "4.0.0",
3
+ "version": "4.1.1",
4
4
  "description": "MAMA Core - Shared modules for Memory-Augmented MCP Assistant",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",