@coffer-org/plugin-claude-agent 2.0.0 → 2.2.0

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/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { definePlugin } from '@coffer-org/sdk/plugin';
2
2
  import { defineSettings } from '@coffer-org/sdk/settings';
3
3
  import { field } from '@coffer-org/sdk/fields';
4
+ import { CLAUDE_MODELS, EFFORT_LEVELS } from "./models.js";
4
5
  export default definePlugin({
5
6
  id: 'claude-agent',
6
7
  version: '1.0.0',
@@ -8,31 +9,63 @@ export default definePlugin({
8
9
  settings: defineSettings({
9
10
  label: 'claude-agent.settings.label',
10
11
  fields: [
11
- field.select({
12
- key: 'auth_mode',
13
- label: 'claude-agent.settings.auth_mode',
14
- default: 'subscription',
15
- options: [
16
- { value: 'subscription', title: 'claude-agent.settings.auth_mode_subscription' },
17
- { value: 'api_key', title: 'claude-agent.settings.auth_mode_api_key' },
12
+ field.group({
13
+ label: 'claude-agent.settings.groups.runtime',
14
+ icon: 'lucide:bot',
15
+ fields: [
16
+ field.select({
17
+ key: 'auth_mode',
18
+ label: 'claude-agent.settings.auth_mode',
19
+ default: 'subscription',
20
+ options: [
21
+ { value: 'subscription', title: 'claude-agent.settings.auth_mode_subscription' },
22
+ { value: 'api_key', title: 'claude-agent.settings.auth_mode_api_key' },
23
+ ],
24
+ }),
25
+ field.password({
26
+ key: 'anthropic_api_key',
27
+ label: 'claude-agent.settings.anthropic_api_key',
28
+ view: { hidden: { auth_mode: { $ne: 'api_key' } } },
29
+ }),
30
+ field.select({
31
+ key: 'claude_model',
32
+ label: 'claude-agent.settings.claude_model',
33
+ default: 'haiku',
34
+ options: CLAUDE_MODELS.map((m) => ({ value: m.value, title: m.title })),
35
+ }),
36
+ field.select({
37
+ key: 'effort',
38
+ label: 'claude-agent.settings.effort',
39
+ default: '',
40
+ options: [
41
+ { value: '', title: 'claude-agent.settings.effort_default' },
42
+ ...EFFORT_LEVELS.map((v) => ({ value: v, title: v })),
43
+ ],
44
+ }),
45
+ field.boolean({ key: 'thinking_enabled', label: 'claude-agent.settings.thinking_enabled', default: false }),
46
+ field.boolean({ key: 'skip_permissions', label: 'claude-agent.settings.skip_permissions', default: true }),
47
+ field.int({ key: 'response_timeout', label: 'claude-agent.settings.response_timeout', default: 600 }),
18
48
  ],
19
49
  }),
20
- field.password({
21
- key: 'anthropic_api_key',
22
- label: 'claude-agent.settings.anthropic_api_key',
23
- view: { hidden: { auth_mode: { $ne: 'api_key' } } },
50
+ field.group({
51
+ label: 'claude-agent.settings.groups.rag',
52
+ icon: 'lucide:search',
53
+ fields: [
54
+ field.boolean({ key: 'rag_enabled', label: 'claude-agent.settings.rag_enabled', default: true }),
55
+ field.password({ key: 'openai_api_key', label: 'claude-agent.settings.openai_api_key' }),
56
+ field.int({ key: 'rag_top_k', label: 'claude-agent.settings.rag_top_k', default: 5, view: { hidden: true } }),
57
+ ],
58
+ }),
59
+ field.group({
60
+ label: 'claude-agent.settings.groups.langfuse',
61
+ icon: 'lucide:activity',
62
+ fields: [
63
+ field.boolean({ key: 'tracing_enabled', label: 'claude-agent.settings.tracing_enabled' }),
64
+ field.string({ key: 'langfuse_base_url', label: 'claude-agent.settings.langfuse_base_url' }),
65
+ field.password({ key: 'langfuse_public_key', label: 'claude-agent.settings.langfuse_public_key' }),
66
+ field.password({ key: 'langfuse_secret_key', label: 'claude-agent.settings.langfuse_secret_key' }),
67
+ ],
24
68
  }),
25
- field.string({ key: 'claude_model', label: 'claude-agent.settings.claude_model', default: 'haiku' }),
26
- field.boolean({ key: 'skip_permissions', label: 'claude-agent.settings.skip_permissions', default: true }),
27
- field.int({ key: 'response_timeout', label: 'claude-agent.settings.response_timeout', default: 600 }),
28
- field.boolean({ key: 'rag_enabled', label: 'claude-agent.settings.rag_enabled', default: true }),
29
- field.int({ key: 'rag_top_k', label: 'claude-agent.settings.rag_top_k', default: 5 }),
30
- field.boolean({ key: 'thinking_enabled', label: 'claude-agent.settings.thinking_enabled', default: false }),
31
- field.password({ key: 'openai_api_key', label: 'claude-agent.settings.openai_api_key' }),
32
- field.password({ key: 'langfuse_public_key', label: 'claude-agent.settings.langfuse_public_key' }),
33
- field.password({ key: 'langfuse_secret_key', label: 'claude-agent.settings.langfuse_secret_key' }),
34
- field.string({ key: 'langfuse_base_url', label: 'claude-agent.settings.langfuse_base_url' }),
35
- field.boolean({ key: 'tracing_enabled', label: 'claude-agent.settings.tracing_enabled' }),
36
69
  ],
37
70
  }),
38
71
  });
@@ -0,0 +1,23 @@
1
+ export declare const CLAUDE_MODELS: readonly [{
2
+ readonly value: "haiku";
3
+ readonly id: "claude-haiku-4-5";
4
+ readonly title: "Haiku 4.5 — найшвидша";
5
+ }, {
6
+ readonly value: "sonnet";
7
+ readonly id: "claude-sonnet-5";
8
+ readonly title: "Sonnet 5";
9
+ }, {
10
+ readonly value: "opus";
11
+ readonly id: "claude-opus-4-8";
12
+ readonly title: "Opus 4.8";
13
+ }, {
14
+ readonly value: "claude-opus-5";
15
+ readonly id: "claude-opus-5";
16
+ readonly title: "Opus 5";
17
+ }, {
18
+ readonly value: "claude-fable-5";
19
+ readonly id: "claude-fable-5";
20
+ readonly title: "Fable 5 — найдорожча";
21
+ }];
22
+ export declare const EFFORT_LEVELS: readonly ["low", "medium", "high", "xhigh", "max"];
23
+ export type Effort = (typeof EFFORT_LEVELS)[number];
package/dist/models.js ADDED
@@ -0,0 +1,8 @@
1
+ export const CLAUDE_MODELS = [
2
+ { value: 'haiku', id: 'claude-haiku-4-5', title: 'Haiku 4.5 — найшвидша' },
3
+ { value: 'sonnet', id: 'claude-sonnet-5', title: 'Sonnet 5' },
4
+ { value: 'opus', id: 'claude-opus-4-8', title: 'Opus 4.8' },
5
+ { value: 'claude-opus-5', id: 'claude-opus-5', title: 'Opus 5' },
6
+ { value: 'claude-fable-5', id: 'claude-fable-5', title: 'Fable 5 — найдорожча' },
7
+ ];
8
+ export const EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max'];
@@ -17,6 +17,8 @@ export { modelId } from './config.ts';
17
17
  export declare function makeClient(cfg: Pick<AgentConfig, 'authMode' | 'anthropicApiKey' | 'globalCredentials'>): Anthropic;
18
18
  export declare function esc(s: string): string;
19
19
  export declare function renderContent(m: ConvMessage): string;
20
+ export declare const MAX_TOOL_RESULT_CHARS = 100000;
21
+ export declare function capToolResult(text: string): string;
20
22
  export interface TurnParts {
21
23
  thinking: string;
22
24
  text: string;
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import Anthropic from '@anthropic-ai/sdk';
3
- import { loadAgentConfig, modelId } from "./config.js";
3
+ import { effortParam, loadAgentConfig, modelId } from "./config.js";
4
4
  import { makeAgentTools } from "./coffer.js";
5
5
  import { buildDomainSections } from '@coffer-org/server/mcp-tools';
6
6
  import { isTracing, flushTracing, langfuseFactory, TurnTracer } from "./tracing.js";
@@ -65,6 +65,13 @@ export function renderContent(m) {
65
65
  const author = m.sender ? `<author>${esc(m.sender)}</author>\n` : '';
66
66
  return `${author}<body>${esc(m.content)}</body>`;
67
67
  }
68
+ export const MAX_TOOL_RESULT_CHARS = 100_000;
69
+ export function capToolResult(text) {
70
+ if (text.length <= MAX_TOOL_RESULT_CHARS)
71
+ return text;
72
+ return (`${text.slice(0, MAX_TOOL_RESULT_CHARS)}\n\n[truncated: ${text.length} chars total. ` +
73
+ `Narrow the call — use limit/offset or filters, or count instead of listing.]`);
74
+ }
68
75
  export function collectReasoning(turns) {
69
76
  const parts = [];
70
77
  for (const t of turns) {
@@ -148,6 +155,7 @@ export async function runAgent(request, deps = {}) {
148
155
  }
149
156
  }
150
157
  const thinking = thinkingParam(cfg.thinkingEnabled);
158
+ const outputConfig = effortParam(cfg.effort, modelId(cfg.claudeModel));
151
159
  if (cfg.thinkingEnabled)
152
160
  log.info(`extended thinking ON (${modelId(cfg.claudeModel)})`);
153
161
  let tokensIn = null;
@@ -164,6 +172,7 @@ export async function runAgent(request, deps = {}) {
164
172
  messages,
165
173
  tools: tools,
166
174
  ...(thinking ? { thinking } : {}),
175
+ ...(outputConfig ? { output_config: outputConfig } : {}),
167
176
  }, { signal: ac.signal });
168
177
  stream.on('text', (delta) => {
169
178
  streamed = appendTextDelta(streamed, delta);
@@ -243,7 +252,10 @@ export async function runAgent(request, deps = {}) {
243
252
  log.warn(`tool ${b.name} threw: ${content}`);
244
253
  }
245
254
  }
246
- results.push({ type: 'tool_result', tool_use_id: b.id, content, is_error: isError });
255
+ const capped = capToolResult(content);
256
+ if (capped !== content)
257
+ log.warn(`tool ${b.name} returned ${content.length} chars — truncated`);
258
+ results.push({ type: 'tool_result', tool_use_id: b.id, content: capped, is_error: isError });
247
259
  }
248
260
  }
249
261
  traceMessage(tracer, {
@@ -1,8 +1,15 @@
1
1
  export declare const APP_ROOT: string;
2
2
  export declare const PLUGIN_ROOT: string;
3
+ import { CLAUDE_MODELS, EFFORT_LEVELS, type Effort } from '../models.ts';
4
+ export { CLAUDE_MODELS, EFFORT_LEVELS };
5
+ export type { Effort };
3
6
  export type AuthMode = 'subscription' | 'api_key';
4
7
  export declare function supportsAdaptiveThinking(model: string): boolean;
5
8
  export declare function modelId(alias: string): string;
9
+ export declare function supportsEffort(model: string): boolean;
10
+ export declare function effortParam(effort: string, model: string): {
11
+ effort: Effort;
12
+ } | undefined;
6
13
  export interface AgentConfig {
7
14
  authMode: AuthMode;
8
15
  anthropicApiKey: string;
@@ -12,6 +19,7 @@ export interface AgentConfig {
12
19
  ragEnabled: boolean;
13
20
  ragTopK: number;
14
21
  thinkingEnabled: boolean;
22
+ effort: string;
15
23
  embeddingApiKey: string;
16
24
  langfusePublicKey: string;
17
25
  langfuseSecretKey: string;
@@ -4,6 +4,8 @@ import { fileURLToPath } from 'node:url';
4
4
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
5
  export const APP_ROOT = path.resolve(__dirname, '../../../..');
6
6
  export const PLUGIN_ROOT = path.resolve(__dirname, '../..');
7
+ import { CLAUDE_MODELS, EFFORT_LEVELS } from "../models.js";
8
+ export { CLAUDE_MODELS, EFFORT_LEVELS };
7
9
  const ADAPTIVE_THINKING_MODELS = [
8
10
  'claude-opus-5', 'claude-opus-4-8', 'claude-opus-4-7', 'claude-opus-4-6',
9
11
  'claude-sonnet-5', 'claude-sonnet-4-6',
@@ -13,12 +15,18 @@ export function supportsAdaptiveThinking(model) {
13
15
  return ADAPTIVE_THINKING_MODELS.some((m) => model === m || model.startsWith(`${m}-`));
14
16
  }
15
17
  export function modelId(alias) {
16
- const map = {
17
- haiku: 'claude-haiku-4-5',
18
- sonnet: 'claude-sonnet-5',
19
- opus: 'claude-opus-4-8',
20
- };
21
- return map[alias] ?? (alias.startsWith('claude-') ? alias : 'claude-opus-4-8');
18
+ const known = CLAUDE_MODELS.find((m) => m.value === alias);
19
+ if (known)
20
+ return known.id;
21
+ return alias.startsWith('claude-') ? alias : 'claude-opus-4-8';
22
+ }
23
+ export function supportsEffort(model) {
24
+ return supportsAdaptiveThinking(model);
25
+ }
26
+ export function effortParam(effort, model) {
27
+ return EFFORT_LEVELS.includes(effort) && supportsEffort(model)
28
+ ? { effort: effort }
29
+ : undefined;
22
30
  }
23
31
  export function loadAgentConfig(opts = {}) {
24
32
  const env = opts.env ?? process.env;
@@ -32,9 +40,10 @@ export function loadAgentConfig(opts = {}) {
32
40
  skipPermissions: db.skip_permissions !== false,
33
41
  responseTimeout: Math.max(60, Number(env.AGENT_RESPONSE_TIMEOUT ?? db.response_timeout ?? 600) || 600),
34
42
  ragEnabled: db.rag_enabled !== false,
35
- ragTopK: Number(env.AGENT_RAG_TOP_K ?? db.rag_top_k ?? 5) || 5,
43
+ ragTopK: 5,
36
44
  thinkingEnabled: (env.AGENT_THINKING_ENABLED ?? String(db.thinking_enabled)) === 'true'
37
45
  && supportsAdaptiveThinking(modelId(claudeModel)),
46
+ effort: String(env.AGENT_EFFORT ?? db.effort ?? ''),
38
47
  embeddingApiKey: env.OPENAI_API_KEY ?? db.openai_api_key ?? '',
39
48
  langfusePublicKey: env.LANGFUSE_PUBLIC_KEY ?? db.langfuse_public_key ?? '',
40
49
  langfuseSecretKey: env.LANGFUSE_SECRET_KEY ?? db.langfuse_secret_key ?? '',
package/dist/schema.js CHANGED
@@ -7084,72 +7084,139 @@ var src_default = definePlugin({
7084
7084
  settings: defineSettings({
7085
7085
  label: "claude-agent.settings.label",
7086
7086
  fields: [
7087
- field.select({
7088
- key: "auth_mode",
7089
- label: "claude-agent.settings.auth_mode",
7090
- default: "subscription",
7091
- options: [{
7092
- value: "subscription",
7093
- title: "claude-agent.settings.auth_mode_subscription"
7094
- }, {
7095
- value: "api_key",
7096
- title: "claude-agent.settings.auth_mode_api_key"
7097
- }]
7098
- }),
7099
- field.password({
7100
- key: "anthropic_api_key",
7101
- label: "claude-agent.settings.anthropic_api_key",
7102
- view: { hidden: { auth_mode: { $ne: "api_key" } } }
7103
- }),
7104
- field.string({
7105
- key: "claude_model",
7106
- label: "claude-agent.settings.claude_model",
7107
- default: "haiku"
7108
- }),
7109
- field.boolean({
7110
- key: "skip_permissions",
7111
- label: "claude-agent.settings.skip_permissions",
7112
- default: true
7113
- }),
7114
- field.int({
7115
- key: "response_timeout",
7116
- label: "claude-agent.settings.response_timeout",
7117
- default: 600
7118
- }),
7119
- field.boolean({
7120
- key: "rag_enabled",
7121
- label: "claude-agent.settings.rag_enabled",
7122
- default: true
7123
- }),
7124
- field.int({
7125
- key: "rag_top_k",
7126
- label: "claude-agent.settings.rag_top_k",
7127
- default: 5
7128
- }),
7129
- field.boolean({
7130
- key: "thinking_enabled",
7131
- label: "claude-agent.settings.thinking_enabled",
7132
- default: false
7133
- }),
7134
- field.password({
7135
- key: "openai_api_key",
7136
- label: "claude-agent.settings.openai_api_key"
7137
- }),
7138
- field.password({
7139
- key: "langfuse_public_key",
7140
- label: "claude-agent.settings.langfuse_public_key"
7141
- }),
7142
- field.password({
7143
- key: "langfuse_secret_key",
7144
- label: "claude-agent.settings.langfuse_secret_key"
7087
+ field.group({
7088
+ label: "claude-agent.settings.groups.runtime",
7089
+ icon: "lucide:bot",
7090
+ fields: [
7091
+ field.select({
7092
+ key: "auth_mode",
7093
+ label: "claude-agent.settings.auth_mode",
7094
+ default: "subscription",
7095
+ options: [{
7096
+ value: "subscription",
7097
+ title: "claude-agent.settings.auth_mode_subscription"
7098
+ }, {
7099
+ value: "api_key",
7100
+ title: "claude-agent.settings.auth_mode_api_key"
7101
+ }]
7102
+ }),
7103
+ field.password({
7104
+ key: "anthropic_api_key",
7105
+ label: "claude-agent.settings.anthropic_api_key",
7106
+ view: { hidden: { auth_mode: { $ne: "api_key" } } }
7107
+ }),
7108
+ field.select({
7109
+ key: "claude_model",
7110
+ label: "claude-agent.settings.claude_model",
7111
+ default: "haiku",
7112
+ options: [
7113
+ {
7114
+ value: "haiku",
7115
+ id: "claude-haiku-4-5",
7116
+ title: "Haiku 4.5 — найшвидша"
7117
+ },
7118
+ {
7119
+ value: "sonnet",
7120
+ id: "claude-sonnet-5",
7121
+ title: "Sonnet 5"
7122
+ },
7123
+ {
7124
+ value: "opus",
7125
+ id: "claude-opus-4-8",
7126
+ title: "Opus 4.8"
7127
+ },
7128
+ {
7129
+ value: "claude-opus-5",
7130
+ id: "claude-opus-5",
7131
+ title: "Opus 5"
7132
+ },
7133
+ {
7134
+ value: "claude-fable-5",
7135
+ id: "claude-fable-5",
7136
+ title: "Fable 5 — найдорожча"
7137
+ }
7138
+ ].map((m) => ({
7139
+ value: m.value,
7140
+ title: m.title
7141
+ }))
7142
+ }),
7143
+ field.select({
7144
+ key: "effort",
7145
+ label: "claude-agent.settings.effort",
7146
+ default: "",
7147
+ options: [{
7148
+ value: "",
7149
+ title: "claude-agent.settings.effort_default"
7150
+ }, ...[
7151
+ "low",
7152
+ "medium",
7153
+ "high",
7154
+ "xhigh",
7155
+ "max"
7156
+ ].map((v) => ({
7157
+ value: v,
7158
+ title: v
7159
+ }))]
7160
+ }),
7161
+ field.boolean({
7162
+ key: "thinking_enabled",
7163
+ label: "claude-agent.settings.thinking_enabled",
7164
+ default: false
7165
+ }),
7166
+ field.boolean({
7167
+ key: "skip_permissions",
7168
+ label: "claude-agent.settings.skip_permissions",
7169
+ default: true
7170
+ }),
7171
+ field.int({
7172
+ key: "response_timeout",
7173
+ label: "claude-agent.settings.response_timeout",
7174
+ default: 600
7175
+ })
7176
+ ]
7145
7177
  }),
7146
- field.string({
7147
- key: "langfuse_base_url",
7148
- label: "claude-agent.settings.langfuse_base_url"
7178
+ field.group({
7179
+ label: "claude-agent.settings.groups.rag",
7180
+ icon: "lucide:search",
7181
+ fields: [
7182
+ field.boolean({
7183
+ key: "rag_enabled",
7184
+ label: "claude-agent.settings.rag_enabled",
7185
+ default: true
7186
+ }),
7187
+ field.password({
7188
+ key: "openai_api_key",
7189
+ label: "claude-agent.settings.openai_api_key"
7190
+ }),
7191
+ field.int({
7192
+ key: "rag_top_k",
7193
+ label: "claude-agent.settings.rag_top_k",
7194
+ default: 5,
7195
+ view: { hidden: true }
7196
+ })
7197
+ ]
7149
7198
  }),
7150
- field.boolean({
7151
- key: "tracing_enabled",
7152
- label: "claude-agent.settings.tracing_enabled"
7199
+ field.group({
7200
+ label: "claude-agent.settings.groups.langfuse",
7201
+ icon: "lucide:activity",
7202
+ fields: [
7203
+ field.boolean({
7204
+ key: "tracing_enabled",
7205
+ label: "claude-agent.settings.tracing_enabled"
7206
+ }),
7207
+ field.string({
7208
+ key: "langfuse_base_url",
7209
+ label: "claude-agent.settings.langfuse_base_url"
7210
+ }),
7211
+ field.password({
7212
+ key: "langfuse_public_key",
7213
+ label: "claude-agent.settings.langfuse_public_key"
7214
+ }),
7215
+ field.password({
7216
+ key: "langfuse_secret_key",
7217
+ label: "claude-agent.settings.langfuse_secret_key"
7218
+ })
7219
+ ]
7153
7220
  })
7154
7221
  ]
7155
7222
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffer-org/plugin-claude-agent",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"
@@ -26,9 +26,9 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@anthropic-ai/sdk": "^0.111.0",
29
- "@coffer-org/mcp": "^2.0.0",
29
+ "@coffer-org/mcp": "^2.2.0",
30
30
  "@coffer-org/sdk": "^2.0.0",
31
- "@coffer-org/server": "^2.0.1",
31
+ "@coffer-org/server": "^2.1.1",
32
32
  "@langfuse/otel": "^4.6.1",
33
33
  "@langfuse/tracing": "^4.6.1",
34
34
  "@opentelemetry/sdk-trace-node": "^2.8.0",