@sena-ai/hooks 0.0.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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fileContext.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileContext.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fileContext.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,60 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { fileContext } from '../fileContext.js';
3
+ import { join } from 'node:path';
4
+ const fixturesDir = join(import.meta.dirname, 'fixtures');
5
+ function mockContext(overrides) {
6
+ return {
7
+ turnId: 'turn-1',
8
+ agentName: 'test',
9
+ trigger: 'programmatic',
10
+ input: 'hello',
11
+ sessionId: null,
12
+ metadata: {},
13
+ ...overrides,
14
+ };
15
+ }
16
+ describe('fileContext', () => {
17
+ it('loads a single file', async () => {
18
+ const hook = fileContext({
19
+ path: join(fixturesDir, 'soul.md'),
20
+ as: 'system',
21
+ });
22
+ const fragments = await hook.execute(mockContext());
23
+ expect(fragments).toHaveLength(1);
24
+ expect(fragments[0].role).toBe('system');
25
+ expect(fragments[0].content).toContain('테스트 에이전트');
26
+ expect(fragments[0].source).toContain('soul.md');
27
+ });
28
+ it('loads directory with glob filter', async () => {
29
+ const hook = fileContext({
30
+ path: join(fixturesDir, 'memory'),
31
+ as: 'context',
32
+ glob: '*.md',
33
+ });
34
+ const fragments = await hook.execute(mockContext());
35
+ expect(fragments).toHaveLength(2);
36
+ expect(fragments.every(f => f.role === 'context')).toBe(true);
37
+ expect(fragments.every(f => !f.content.includes('무시'))).toBe(true);
38
+ });
39
+ it('respects when condition', async () => {
40
+ const hook = fileContext({
41
+ path: join(fixturesDir, 'soul.md'),
42
+ as: 'system',
43
+ when: (ctx) => ctx.trigger === 'schedule',
44
+ });
45
+ const fragments = await hook.execute(mockContext({ trigger: 'programmatic' }));
46
+ expect(fragments).toHaveLength(0);
47
+ const fragments2 = await hook.execute(mockContext({ trigger: 'schedule' }));
48
+ expect(fragments2).toHaveLength(1);
49
+ });
50
+ it('respects maxLength', async () => {
51
+ const hook = fileContext({
52
+ path: join(fixturesDir, 'soul.md'),
53
+ as: 'system',
54
+ maxLength: 10,
55
+ });
56
+ const fragments = await hook.execute(mockContext());
57
+ expect(fragments[0].content.length).toBeLessThanOrEqual(10);
58
+ });
59
+ });
60
+ //# sourceMappingURL=fileContext.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileContext.test.js","sourceRoot":"","sources":["../../src/__tests__/fileContext.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schedule.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/schedule.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,34 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { cronSchedule } from '../cronSchedule.js';
3
+ import { heartbeat } from '../heartbeat.js';
4
+ describe('cronSchedule', () => {
5
+ it('creates a cron schedule', () => {
6
+ const schedule = cronSchedule('0 * * * *', {
7
+ name: 'hourly-check',
8
+ prompt: 'Do the hourly check',
9
+ });
10
+ expect(schedule.name).toBe('hourly-check');
11
+ expect(schedule.type).toBe('cron');
12
+ expect(schedule.expression).toBe('0 * * * *');
13
+ expect(schedule.prompt).toBe('Do the hourly check');
14
+ });
15
+ });
16
+ describe('heartbeat', () => {
17
+ it('creates a heartbeat schedule', () => {
18
+ const schedule = heartbeat('15m', {
19
+ prompt: 'Check heartbeat',
20
+ });
21
+ expect(schedule.name).toBe('heartbeat:15m');
22
+ expect(schedule.type).toBe('heartbeat');
23
+ expect(schedule.expression).toBe('15m');
24
+ expect(schedule.prompt).toBe('Check heartbeat');
25
+ });
26
+ it('allows custom name', () => {
27
+ const schedule = heartbeat('30s', {
28
+ name: 'fast-poll',
29
+ prompt: 'Poll fast',
30
+ });
31
+ expect(schedule.name).toBe('fast-poll');
32
+ });
33
+ });
34
+ //# sourceMappingURL=schedule.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule.test.js","sourceRoot":"","sources":["../../src/__tests__/schedule.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAE3C,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,EAAE;YACzC,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,qBAAqB;SAC9B,CAAC,CAAA;QAEF,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;QAC1C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAClC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAC7C,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,EAAE;YAChC,MAAM,EAAE,iBAAiB;SAC1B,CAAC,CAAA;QAEF,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QAC3C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QACvC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACvC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;IACjD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,EAAE;YAChC,IAAI,EAAE,WAAW;YACjB,MAAM,EAAE,WAAW;SACpB,CAAC,CAAA;QAEF,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IACzC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=traceLogger.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traceLogger.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/traceLogger.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,41 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { traceLogger } from '../traceLogger.js';
3
+ import { readFile, rm, mkdir, readdir } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ import { tmpdir } from 'node:os';
6
+ const testDir = join(tmpdir(), 'sena-trace-test-' + Date.now());
7
+ function mockContext() {
8
+ return {
9
+ turnId: 'turn-123',
10
+ agentName: 'test',
11
+ trigger: 'programmatic',
12
+ input: 'hello',
13
+ sessionId: null,
14
+ metadata: {},
15
+ };
16
+ }
17
+ function mockResult() {
18
+ return {
19
+ text: 'response',
20
+ sessionId: 'sess-1',
21
+ durationMs: 100,
22
+ toolCalls: [],
23
+ };
24
+ }
25
+ describe('traceLogger', () => {
26
+ afterEach(async () => {
27
+ await rm(testDir, { recursive: true, force: true });
28
+ });
29
+ it('writes trace as JSON file', async () => {
30
+ await mkdir(testDir, { recursive: true });
31
+ const hook = traceLogger({ dir: testDir });
32
+ await hook.execute(mockContext(), mockResult());
33
+ const entries = await readdir(testDir);
34
+ expect(entries).toHaveLength(1);
35
+ expect(entries[0]).toMatch(/turn-123.*\.json$/);
36
+ const content = JSON.parse(await readFile(join(testDir, entries[0]), 'utf-8'));
37
+ expect(content.turnId).toBe('turn-123');
38
+ expect(content.result.text).toBe('response');
39
+ });
40
+ });
41
+ //# sourceMappingURL=traceLogger.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traceLogger.test.js","sourceRoot":"","sources":["../../src/__tests__/traceLogger.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAGhC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;AAE/D,SAAS,WAAW;IAClB,OAAO;QACL,MAAM,EAAE,UAAU;QAClB,SAAS,EAAE,MAAM;QACjB,OAAO,EAAE,cAAc;QACvB,KAAK,EAAE,OAAO;QACd,SAAS,EAAE,IAAI;QACf,QAAQ,EAAE,EAAE;KACb,CAAA;AACH,CAAC;AAED,SAAS,UAAU;IACjB,OAAO;QACL,IAAI,EAAE,UAAU;QAChB,SAAS,EAAE,QAAQ;QACnB,UAAU,EAAE,GAAG;QACf,SAAS,EAAE,EAAE;KACd,CAAA;AACH,CAAC;AAED,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IACrD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACzC,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAA;QAE1C,MAAM,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,CAAC,CAAA;QAE/C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;QACtC,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;QAC/B,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAA;QAE/C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAA;QAC9E,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACvC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,12 @@
1
+ import type { Schedule } from '@sena-ai/core';
2
+ export type CronScheduleOptions = {
3
+ name: string;
4
+ prompt: string;
5
+ };
6
+ /**
7
+ * Creates a cron-based schedule.
8
+ * Expression uses 5-field cron format (minute hour day month weekday).
9
+ * Timezone defaults to Asia/Seoul.
10
+ */
11
+ export declare function cronSchedule(expression: string, options: CronScheduleOptions): Schedule;
12
+ //# sourceMappingURL=cronSchedule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cronSchedule.d.ts","sourceRoot":"","sources":["../src/cronSchedule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,GAAG,QAAQ,CAOvF"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Creates a cron-based schedule.
3
+ * Expression uses 5-field cron format (minute hour day month weekday).
4
+ * Timezone defaults to Asia/Seoul.
5
+ */
6
+ export function cronSchedule(expression, options) {
7
+ return {
8
+ name: options.name,
9
+ type: 'cron',
10
+ expression,
11
+ prompt: options.prompt,
12
+ };
13
+ }
14
+ //# sourceMappingURL=cronSchedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cronSchedule.js","sourceRoot":"","sources":["../src/cronSchedule.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB,EAAE,OAA4B;IAC3E,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,MAAM;QACZ,UAAU;QACV,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAA;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { TurnStartHook, TurnContext } from '@sena-ai/core';
2
+ export type FileContextOptions = {
3
+ path: string;
4
+ as: 'system' | 'context';
5
+ glob?: string;
6
+ when?: (ctx: TurnContext) => boolean;
7
+ maxLength?: number;
8
+ };
9
+ export declare function fileContext(options: FileContextOptions): TurnStartHook;
10
+ //# sourceMappingURL=fileContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileContext.d.ts","sourceRoot":"","sources":["../src/fileContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAmB,MAAM,eAAe,CAAA;AAIhF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,QAAQ,GAAG,SAAS,CAAA;IACxB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,WAAW,KAAK,OAAO,CAAA;IACpC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,aAAa,CAmCtE"}
@@ -0,0 +1,49 @@
1
+ import { readFile, readdir, stat } from 'node:fs/promises';
2
+ import { join, basename } from 'node:path';
3
+ export function fileContext(options) {
4
+ const { path, as: role, glob, when, maxLength } = options;
5
+ return {
6
+ name: `fileContext:${path}`,
7
+ async execute(ctx) {
8
+ if (when && !when(ctx))
9
+ return [];
10
+ const info = await stat(path);
11
+ if (info.isFile()) {
12
+ const content = await readFile(path, 'utf-8');
13
+ return [makeFragment(path, role, content, maxLength)];
14
+ }
15
+ if (info.isDirectory()) {
16
+ const entries = await readdir(path);
17
+ const filtered = glob
18
+ ? entries.filter(e => matchGlob(e, glob))
19
+ : entries;
20
+ const fragments = [];
21
+ for (const entry of filtered.sort()) {
22
+ const filePath = join(path, entry);
23
+ const fileStat = await stat(filePath);
24
+ if (!fileStat.isFile())
25
+ continue;
26
+ const content = await readFile(filePath, 'utf-8');
27
+ fragments.push(makeFragment(filePath, role, content, maxLength));
28
+ }
29
+ return fragments;
30
+ }
31
+ return [];
32
+ },
33
+ };
34
+ }
35
+ function makeFragment(filePath, role, content, maxLength) {
36
+ const trimmed = maxLength ? content.slice(0, maxLength) : content;
37
+ return {
38
+ source: `file:${basename(filePath)}`,
39
+ role,
40
+ content: trimmed,
41
+ };
42
+ }
43
+ function matchGlob(filename, pattern) {
44
+ if (pattern.startsWith('*')) {
45
+ return filename.endsWith(pattern.slice(1));
46
+ }
47
+ return filename === pattern;
48
+ }
49
+ //# sourceMappingURL=fileContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileContext.js","sourceRoot":"","sources":["../src/fileContext.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAU1C,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,OAAO,CAAA;IAEzD,OAAO;QACL,IAAI,EAAE,eAAe,IAAI,EAAE;QAC3B,KAAK,CAAC,OAAO,CAAC,GAAgB;YAC5B,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,OAAO,EAAE,CAAA;YAEjC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAA;YAE7B,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;gBAClB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;gBAC7C,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAA;YACvD,CAAC;YAED,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACvB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;gBACnC,MAAM,QAAQ,GAAG,IAAI;oBACnB,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;oBACzC,CAAC,CAAC,OAAO,CAAA;gBAEX,MAAM,SAAS,GAAsB,EAAE,CAAA;gBACvC,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;oBACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;oBAClC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAA;oBACrC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;wBAAE,SAAQ;oBAChC,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;oBACjD,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAA;gBAClE,CAAC;gBACD,OAAO,SAAS,CAAA;YAClB,CAAC;YAED,OAAO,EAAE,CAAA;QACX,CAAC;KACF,CAAA;AACH,CAAC;AAED,SAAS,YAAY,CACnB,QAAgB,EAChB,IAA0B,EAC1B,OAAe,EACf,SAAkB;IAElB,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;IACjE,OAAO;QACL,MAAM,EAAE,QAAQ,QAAQ,CAAC,QAAQ,CAAC,EAAE;QACpC,IAAI;QACJ,OAAO,EAAE,OAAO;KACjB,CAAA;AACH,CAAC;AAED,SAAS,SAAS,CAAC,QAAgB,EAAE,OAAe;IAClD,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5C,CAAC;IACD,OAAO,QAAQ,KAAK,OAAO,CAAA;AAC7B,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Schedule } from '@sena-ai/core';
2
+ export type HeartbeatOptions = {
3
+ prompt: string;
4
+ name?: string;
5
+ };
6
+ /**
7
+ * Creates a fixed-interval heartbeat schedule.
8
+ * Interval format: '15m', '1h', '30s', etc.
9
+ */
10
+ export declare function heartbeat(interval: string, options: HeartbeatOptions): Schedule;
11
+ //# sourceMappingURL=heartbeat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../src/heartbeat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,QAAQ,CAO/E"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Creates a fixed-interval heartbeat schedule.
3
+ * Interval format: '15m', '1h', '30s', etc.
4
+ */
5
+ export function heartbeat(interval, options) {
6
+ return {
7
+ name: options.name ?? `heartbeat:${interval}`,
8
+ type: 'heartbeat',
9
+ expression: interval,
10
+ prompt: options.prompt,
11
+ };
12
+ }
13
+ //# sourceMappingURL=heartbeat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../src/heartbeat.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB,EAAE,OAAyB;IACnE,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,aAAa,QAAQ,EAAE;QAC7C,IAAI,EAAE,WAAW;QACjB,UAAU,EAAE,QAAQ;QACpB,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAA;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ export declare const VERSION = "0.0.1";
2
+ export { fileContext } from './fileContext.js';
3
+ export type { FileContextOptions } from './fileContext.js';
4
+ export { traceLogger } from './traceLogger.js';
5
+ export type { TraceLoggerOptions } from './traceLogger.js';
6
+ export { cronSchedule } from './cronSchedule.js';
7
+ export type { CronScheduleOptions } from './cronSchedule.js';
8
+ export { heartbeat } from './heartbeat.js';
9
+ export type { HeartbeatOptions } from './heartbeat.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAA;AAE9B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export const VERSION = '0.0.1';
2
+ export { fileContext } from './fileContext.js';
3
+ export { traceLogger } from './traceLogger.js';
4
+ export { cronSchedule } from './cronSchedule.js';
5
+ export { heartbeat } from './heartbeat.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAA;AAE9B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,7 @@
1
+ import type { TurnEndHook } from '@sena-ai/core';
2
+ export type TraceLoggerOptions = {
3
+ dir: string;
4
+ format?: 'json';
5
+ };
6
+ export declare function traceLogger(options: TraceLoggerOptions): TurnEndHook;
7
+ //# sourceMappingURL=traceLogger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traceLogger.d.ts","sourceRoot":"","sources":["../src/traceLogger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAA2B,MAAM,eAAe,CAAA;AAIzE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAqBpE"}
@@ -0,0 +1,22 @@
1
+ import { writeFile, mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ export function traceLogger(options) {
4
+ const { dir } = options;
5
+ return {
6
+ name: 'traceLogger',
7
+ async execute(context, result) {
8
+ await mkdir(dir, { recursive: true });
9
+ const filename = `${context.turnId}-${Date.now()}.json`;
10
+ const trace = {
11
+ turnId: context.turnId,
12
+ agentName: context.agentName,
13
+ trigger: context.trigger,
14
+ input: context.input,
15
+ timestamp: new Date().toISOString(),
16
+ result,
17
+ };
18
+ await writeFile(join(dir, filename), JSON.stringify(trace, null, 2), 'utf-8');
19
+ },
20
+ };
21
+ }
22
+ //# sourceMappingURL=traceLogger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traceLogger.js","sourceRoot":"","sources":["../src/traceLogger.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAOhC,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAA;IAEvB,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,KAAK,CAAC,OAAO,CAAC,OAAoB,EAAE,MAAkB;YACpD,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YAErC,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,OAAO,CAAA;YACvD,MAAM,KAAK,GAAG;gBACZ,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,MAAM;aACP,CAAA;YAED,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;QAC/E,CAAC;KACF,CAAA;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@sena-ai/hooks",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js"
9
+ }
10
+ },
11
+ "files": ["dist"],
12
+ "scripts": {
13
+ "build": "tsc -b",
14
+ "dev": "tsc -b --watch"
15
+ },
16
+ "dependencies": {
17
+ "@sena-ai/core": "workspace:*"
18
+ },
19
+ "devDependencies": {
20
+ "typescript": "^5.8.0"
21
+ }
22
+ }