@ottocode/server 0.1.173

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.
Files changed (111) hide show
  1. package/package.json +42 -0
  2. package/src/events/bus.ts +43 -0
  3. package/src/events/types.ts +32 -0
  4. package/src/index.ts +281 -0
  5. package/src/openapi/helpers.ts +64 -0
  6. package/src/openapi/paths/ask.ts +70 -0
  7. package/src/openapi/paths/config.ts +218 -0
  8. package/src/openapi/paths/files.ts +72 -0
  9. package/src/openapi/paths/git.ts +457 -0
  10. package/src/openapi/paths/messages.ts +92 -0
  11. package/src/openapi/paths/sessions.ts +90 -0
  12. package/src/openapi/paths/setu.ts +154 -0
  13. package/src/openapi/paths/stream.ts +26 -0
  14. package/src/openapi/paths/terminals.ts +226 -0
  15. package/src/openapi/schemas.ts +345 -0
  16. package/src/openapi/spec.ts +49 -0
  17. package/src/presets.ts +85 -0
  18. package/src/routes/ask.ts +113 -0
  19. package/src/routes/auth.ts +592 -0
  20. package/src/routes/branch.ts +106 -0
  21. package/src/routes/config/agents.ts +44 -0
  22. package/src/routes/config/cwd.ts +21 -0
  23. package/src/routes/config/defaults.ts +45 -0
  24. package/src/routes/config/index.ts +16 -0
  25. package/src/routes/config/main.ts +73 -0
  26. package/src/routes/config/models.ts +139 -0
  27. package/src/routes/config/providers.ts +46 -0
  28. package/src/routes/config/utils.ts +120 -0
  29. package/src/routes/files.ts +218 -0
  30. package/src/routes/git/branch.ts +75 -0
  31. package/src/routes/git/commit.ts +209 -0
  32. package/src/routes/git/diff.ts +137 -0
  33. package/src/routes/git/index.ts +18 -0
  34. package/src/routes/git/push.ts +160 -0
  35. package/src/routes/git/schemas.ts +48 -0
  36. package/src/routes/git/staging.ts +208 -0
  37. package/src/routes/git/status.ts +83 -0
  38. package/src/routes/git/types.ts +31 -0
  39. package/src/routes/git/utils.ts +249 -0
  40. package/src/routes/openapi.ts +6 -0
  41. package/src/routes/research.ts +392 -0
  42. package/src/routes/root.ts +5 -0
  43. package/src/routes/session-approval.ts +63 -0
  44. package/src/routes/session-files.ts +387 -0
  45. package/src/routes/session-messages.ts +170 -0
  46. package/src/routes/session-stream.ts +61 -0
  47. package/src/routes/sessions.ts +814 -0
  48. package/src/routes/setu.ts +346 -0
  49. package/src/routes/terminals.ts +227 -0
  50. package/src/runtime/agent/registry.ts +351 -0
  51. package/src/runtime/agent/runner-reasoning.ts +108 -0
  52. package/src/runtime/agent/runner-setup.ts +257 -0
  53. package/src/runtime/agent/runner.ts +375 -0
  54. package/src/runtime/agent-registry.ts +6 -0
  55. package/src/runtime/ask/service.ts +369 -0
  56. package/src/runtime/context/environment.ts +202 -0
  57. package/src/runtime/debug/index.ts +117 -0
  58. package/src/runtime/debug/state.ts +140 -0
  59. package/src/runtime/errors/api-error.ts +192 -0
  60. package/src/runtime/errors/handling.ts +199 -0
  61. package/src/runtime/message/compaction-auto.ts +154 -0
  62. package/src/runtime/message/compaction-context.ts +101 -0
  63. package/src/runtime/message/compaction-detect.ts +26 -0
  64. package/src/runtime/message/compaction-limits.ts +37 -0
  65. package/src/runtime/message/compaction-mark.ts +111 -0
  66. package/src/runtime/message/compaction-prune.ts +75 -0
  67. package/src/runtime/message/compaction.ts +21 -0
  68. package/src/runtime/message/history-builder.ts +266 -0
  69. package/src/runtime/message/service.ts +468 -0
  70. package/src/runtime/message/tool-history-tracker.ts +204 -0
  71. package/src/runtime/prompt/builder.ts +167 -0
  72. package/src/runtime/provider/anthropic.ts +50 -0
  73. package/src/runtime/provider/copilot.ts +12 -0
  74. package/src/runtime/provider/google.ts +8 -0
  75. package/src/runtime/provider/index.ts +60 -0
  76. package/src/runtime/provider/moonshot.ts +8 -0
  77. package/src/runtime/provider/oauth-adapter.ts +237 -0
  78. package/src/runtime/provider/openai.ts +18 -0
  79. package/src/runtime/provider/opencode.ts +7 -0
  80. package/src/runtime/provider/openrouter.ts +7 -0
  81. package/src/runtime/provider/selection.ts +118 -0
  82. package/src/runtime/provider/setu.ts +126 -0
  83. package/src/runtime/provider/zai.ts +16 -0
  84. package/src/runtime/session/branch.ts +280 -0
  85. package/src/runtime/session/db-operations.ts +285 -0
  86. package/src/runtime/session/manager.ts +99 -0
  87. package/src/runtime/session/queue.ts +243 -0
  88. package/src/runtime/stream/abort-handler.ts +65 -0
  89. package/src/runtime/stream/error-handler.ts +371 -0
  90. package/src/runtime/stream/finish-handler.ts +101 -0
  91. package/src/runtime/stream/handlers.ts +5 -0
  92. package/src/runtime/stream/step-finish.ts +93 -0
  93. package/src/runtime/stream/types.ts +25 -0
  94. package/src/runtime/tools/approval.ts +180 -0
  95. package/src/runtime/tools/context.ts +83 -0
  96. package/src/runtime/tools/mapping.ts +154 -0
  97. package/src/runtime/tools/setup.ts +44 -0
  98. package/src/runtime/topup/manager.ts +110 -0
  99. package/src/runtime/utils/cwd.ts +69 -0
  100. package/src/runtime/utils/token.ts +35 -0
  101. package/src/tools/adapter.ts +634 -0
  102. package/src/tools/database/get-parent-session.ts +183 -0
  103. package/src/tools/database/get-session-context.ts +161 -0
  104. package/src/tools/database/index.ts +42 -0
  105. package/src/tools/database/present-session-links.ts +47 -0
  106. package/src/tools/database/query-messages.ts +160 -0
  107. package/src/tools/database/query-sessions.ts +126 -0
  108. package/src/tools/database/search-history.ts +135 -0
  109. package/src/types/sql-imports.d.ts +5 -0
  110. package/sst-env.d.ts +8 -0
  111. package/tsconfig.json +7 -0
@@ -0,0 +1,126 @@
1
+ import { tool } from 'ai';
2
+ import { z } from 'zod/v3';
3
+ import { getDb } from '@ottocode/database';
4
+ import { sessions, messages } from '@ottocode/database/schema';
5
+ import { eq, desc, asc, gte, lte, and, count } from 'drizzle-orm';
6
+
7
+ const inputSchema = z.object({
8
+ limit: z
9
+ .number()
10
+ .min(1)
11
+ .max(100)
12
+ .default(20)
13
+ .describe('Max sessions to return'),
14
+ offset: z.number().min(0).default(0).describe('Offset for pagination'),
15
+ agent: z.string().optional().describe('Filter by agent type'),
16
+ startDate: z
17
+ .string()
18
+ .optional()
19
+ .describe('Filter sessions created after this ISO date'),
20
+ endDate: z
21
+ .string()
22
+ .optional()
23
+ .describe('Filter sessions created before this ISO date'),
24
+ orderBy: z
25
+ .enum(['created_at', 'last_active_at', 'total_tokens'])
26
+ .default('last_active_at')
27
+ .describe('Sort field'),
28
+ orderDir: z.enum(['asc', 'desc']).default('desc').describe('Sort direction'),
29
+ sessionType: z
30
+ .enum(['main', 'research', 'all'])
31
+ .default('main')
32
+ .describe('Filter by session type'),
33
+ });
34
+
35
+ export function buildQuerySessionsTool(projectRoot: string) {
36
+ return {
37
+ name: 'query_sessions',
38
+ tool: tool({
39
+ description:
40
+ 'Search and list sessions from the local database. Use to find past conversations, check what work was done, or locate specific sessions.',
41
+ inputSchema,
42
+ async execute(input) {
43
+ const db = await getDb(projectRoot);
44
+
45
+ const conditions = [eq(sessions.projectPath, projectRoot)];
46
+
47
+ if (input.sessionType !== 'all') {
48
+ conditions.push(eq(sessions.sessionType, input.sessionType));
49
+ }
50
+
51
+ if (input.agent) {
52
+ conditions.push(eq(sessions.agent, input.agent));
53
+ }
54
+
55
+ if (input.startDate) {
56
+ const startTs = new Date(input.startDate).getTime();
57
+ conditions.push(gte(sessions.createdAt, startTs));
58
+ }
59
+
60
+ if (input.endDate) {
61
+ const endTs = new Date(input.endDate).getTime();
62
+ conditions.push(lte(sessions.createdAt, endTs));
63
+ }
64
+
65
+ const orderField =
66
+ input.orderBy === 'created_at'
67
+ ? sessions.createdAt
68
+ : input.orderBy === 'total_tokens'
69
+ ? sessions.totalInputTokens
70
+ : sessions.lastActiveAt;
71
+
72
+ const orderFn = input.orderDir === 'asc' ? asc : desc;
73
+
74
+ const rows = await db
75
+ .select({
76
+ id: sessions.id,
77
+ title: sessions.title,
78
+ agent: sessions.agent,
79
+ provider: sessions.provider,
80
+ model: sessions.model,
81
+ createdAt: sessions.createdAt,
82
+ lastActiveAt: sessions.lastActiveAt,
83
+ totalInputTokens: sessions.totalInputTokens,
84
+ totalOutputTokens: sessions.totalOutputTokens,
85
+ totalCachedTokens: sessions.totalCachedTokens,
86
+ totalCacheCreationTokens: sessions.totalCacheCreationTokens,
87
+ sessionType: sessions.sessionType,
88
+ parentSessionId: sessions.parentSessionId,
89
+ })
90
+ .from(sessions)
91
+ .where(and(...conditions))
92
+ .orderBy(orderFn(orderField), desc(sessions.createdAt))
93
+ .limit(input.limit)
94
+ .offset(input.offset);
95
+
96
+ const countResult = await db
97
+ .select({ total: count() })
98
+ .from(sessions)
99
+ .where(and(...conditions));
100
+
101
+ const total = countResult[0]?.total ?? 0;
102
+
103
+ const sessionsWithCounts = await Promise.all(
104
+ rows.map(async (row) => {
105
+ const msgCount = await db
106
+ .select({ count: count() })
107
+ .from(messages)
108
+ .where(eq(messages.sessionId, row.id));
109
+ return {
110
+ ...row,
111
+ messageCount: msgCount[0]?.count ?? 0,
112
+ };
113
+ }),
114
+ );
115
+
116
+ return {
117
+ ok: true,
118
+ sessions: sessionsWithCounts,
119
+ total,
120
+ limit: input.limit,
121
+ offset: input.offset,
122
+ };
123
+ },
124
+ }),
125
+ };
126
+ }
@@ -0,0 +1,135 @@
1
+ import { tool } from 'ai';
2
+ import { z } from 'zod/v3';
3
+ import { getDb } from '@ottocode/database';
4
+ import { sessions, messages, messageParts } from '@ottocode/database/schema';
5
+ import { eq, like, and } from 'drizzle-orm';
6
+
7
+ const inputSchema = z.object({
8
+ query: z.string().min(1).describe('Search term to find in message content'),
9
+ limit: z
10
+ .number()
11
+ .min(1)
12
+ .max(50)
13
+ .default(20)
14
+ .describe('Max results to return'),
15
+ });
16
+
17
+ export function buildSearchHistoryTool(projectRoot: string) {
18
+ return {
19
+ name: 'search_history',
20
+ tool: tool({
21
+ description:
22
+ 'Full-text search across all message content in session history. Find past conversations, solutions, or discussions about specific topics.',
23
+ inputSchema,
24
+ async execute(input) {
25
+ const db = await getDb(projectRoot);
26
+
27
+ const projectSessionIds = await db
28
+ .select({ id: sessions.id })
29
+ .from(sessions)
30
+ .where(eq(sessions.projectPath, projectRoot));
31
+
32
+ const sessionIdSet = new Set(projectSessionIds.map((s) => s.id));
33
+
34
+ if (sessionIdSet.size === 0) {
35
+ return {
36
+ ok: true,
37
+ results: [],
38
+ total: 0,
39
+ };
40
+ }
41
+
42
+ const searchPattern = `%${input.query}%`;
43
+
44
+ const matchingParts = await db
45
+ .select({
46
+ id: messageParts.id,
47
+ messageId: messageParts.messageId,
48
+ content: messageParts.content,
49
+ type: messageParts.type,
50
+ })
51
+ .from(messageParts)
52
+ .where(
53
+ and(
54
+ eq(messageParts.type, 'text'),
55
+ like(messageParts.content, searchPattern),
56
+ ),
57
+ )
58
+ .limit(input.limit * 3);
59
+
60
+ const results: Array<{
61
+ sessionId: string;
62
+ sessionTitle: string | null;
63
+ messageId: string;
64
+ role: string;
65
+ matchedContent: string;
66
+ createdAt: number;
67
+ }> = [];
68
+
69
+ for (const part of matchingParts) {
70
+ if (results.length >= input.limit) break;
71
+
72
+ const msgRows = await db
73
+ .select({
74
+ id: messages.id,
75
+ sessionId: messages.sessionId,
76
+ role: messages.role,
77
+ createdAt: messages.createdAt,
78
+ })
79
+ .from(messages)
80
+ .where(eq(messages.id, part.messageId))
81
+ .limit(1);
82
+
83
+ if (msgRows.length === 0) continue;
84
+
85
+ const msg = msgRows[0];
86
+
87
+ if (!sessionIdSet.has(msg.sessionId)) continue;
88
+
89
+ const sessionRows = await db
90
+ .select({ title: sessions.title })
91
+ .from(sessions)
92
+ .where(eq(sessions.id, msg.sessionId))
93
+ .limit(1);
94
+
95
+ const content = part.content ?? '';
96
+ const queryLower = input.query.toLowerCase();
97
+ const contentLower = content.toLowerCase();
98
+ const matchIndex = contentLower.indexOf(queryLower);
99
+
100
+ let matchedContent: string;
101
+ if (matchIndex >= 0) {
102
+ const start = Math.max(0, matchIndex - 50);
103
+ const end = Math.min(
104
+ content.length,
105
+ matchIndex + input.query.length + 50,
106
+ );
107
+ const prefix = start > 0 ? '...' : '';
108
+ const suffix = end < content.length ? '...' : '';
109
+ matchedContent = prefix + content.slice(start, end) + suffix;
110
+ } else {
111
+ matchedContent =
112
+ content.slice(0, 150) + (content.length > 150 ? '...' : '');
113
+ }
114
+
115
+ results.push({
116
+ sessionId: msg.sessionId,
117
+ sessionTitle: sessionRows[0]?.title ?? null,
118
+ messageId: msg.id,
119
+ role: msg.role,
120
+ matchedContent,
121
+ createdAt: msg.createdAt,
122
+ });
123
+ }
124
+
125
+ results.sort((a, b) => b.createdAt - a.createdAt);
126
+
127
+ return {
128
+ ok: true,
129
+ results,
130
+ total: results.length,
131
+ };
132
+ },
133
+ }),
134
+ };
135
+ }
@@ -0,0 +1,5 @@
1
+ // Type declarations for .sql file imports (Bun-specific)
2
+ declare module '*.sql' {
3
+ const content: string;
4
+ export default content;
5
+ }
package/sst-env.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /* This file is auto-generated by SST. Do not edit. */
2
+ /* tslint:disable */
3
+ /* eslint-disable */
4
+ /* deno-fmt-ignore-file */
5
+
6
+ /// <reference path="../../sst-env.d.ts" />
7
+
8
+ import 'sst';
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist"
5
+ },
6
+ "include": ["src/**/*"]
7
+ }