@sheepbun/yips 0.1.1 → 0.1.47

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 (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +52 -0
  3. package/bin/yips.js +15 -0
  4. package/package.json +21 -128
  5. package/postinstall.js +52 -0
  6. package/dist/agent/commands/command-catalog.js +0 -243
  7. package/dist/agent/commands/commands.js +0 -418
  8. package/dist/agent/conductor.js +0 -118
  9. package/dist/agent/context/code-context.js +0 -68
  10. package/dist/agent/context/memory-store.js +0 -159
  11. package/dist/agent/context/session-store.js +0 -211
  12. package/dist/agent/protocol/tool-protocol.js +0 -160
  13. package/dist/agent/skills/skills.js +0 -327
  14. package/dist/agent/tools/tool-executor.js +0 -415
  15. package/dist/agent/tools/tool-safety.js +0 -52
  16. package/dist/app/index.js +0 -35
  17. package/dist/app/repl.js +0 -105
  18. package/dist/app/update-check.js +0 -132
  19. package/dist/app/version.js +0 -51
  20. package/dist/code-context.js +0 -68
  21. package/dist/colors.js +0 -204
  22. package/dist/command-catalog.js +0 -242
  23. package/dist/commands.js +0 -350
  24. package/dist/conductor.js +0 -94
  25. package/dist/config/config.js +0 -335
  26. package/dist/config/hooks.js +0 -187
  27. package/dist/config.js +0 -335
  28. package/dist/downloader-state.js +0 -302
  29. package/dist/downloader-ui.js +0 -289
  30. package/dist/gateway/adapters/discord.js +0 -108
  31. package/dist/gateway/adapters/formatting.js +0 -96
  32. package/dist/gateway/adapters/telegram.js +0 -106
  33. package/dist/gateway/adapters/types.js +0 -2
  34. package/dist/gateway/adapters/whatsapp.js +0 -124
  35. package/dist/gateway/auth-policy.js +0 -66
  36. package/dist/gateway/core.js +0 -87
  37. package/dist/gateway/headless-conductor.js +0 -328
  38. package/dist/gateway/message-router.js +0 -23
  39. package/dist/gateway/rate-limiter.js +0 -48
  40. package/dist/gateway/runtime/backend-policy.js +0 -18
  41. package/dist/gateway/runtime/discord-bot.js +0 -104
  42. package/dist/gateway/runtime/discord-main.js +0 -69
  43. package/dist/gateway/session-manager.js +0 -77
  44. package/dist/gateway/types.js +0 -2
  45. package/dist/hardware.js +0 -92
  46. package/dist/hooks.js +0 -187
  47. package/dist/index.js +0 -34
  48. package/dist/input-engine.js +0 -250
  49. package/dist/llama-client.js +0 -227
  50. package/dist/llama-server.js +0 -620
  51. package/dist/llm/llama-client.js +0 -227
  52. package/dist/llm/llama-server.js +0 -620
  53. package/dist/llm/token-counter.js +0 -47
  54. package/dist/memory-store.js +0 -159
  55. package/dist/messages.js +0 -59
  56. package/dist/model-downloader.js +0 -382
  57. package/dist/model-manager-state.js +0 -118
  58. package/dist/model-manager-ui.js +0 -194
  59. package/dist/model-manager.js +0 -190
  60. package/dist/models/hardware.js +0 -92
  61. package/dist/models/model-downloader.js +0 -382
  62. package/dist/models/model-manager.js +0 -190
  63. package/dist/prompt-box.js +0 -78
  64. package/dist/prompt-composer.js +0 -498
  65. package/dist/repl.js +0 -105
  66. package/dist/session-store.js +0 -211
  67. package/dist/spinner.js +0 -76
  68. package/dist/title-box.js +0 -388
  69. package/dist/token-counter.js +0 -47
  70. package/dist/tool-executor.js +0 -415
  71. package/dist/tool-protocol.js +0 -121
  72. package/dist/tool-safety.js +0 -52
  73. package/dist/tui/app.js +0 -2553
  74. package/dist/tui/startup.js +0 -56
  75. package/dist/tui-input-routing.js +0 -53
  76. package/dist/tui.js +0 -51
  77. package/dist/types/app-types.js +0 -2
  78. package/dist/types.js +0 -2
  79. package/dist/ui/colors.js +0 -204
  80. package/dist/ui/downloader/downloader-state.js +0 -302
  81. package/dist/ui/downloader/downloader-ui.js +0 -289
  82. package/dist/ui/input/input-engine.js +0 -250
  83. package/dist/ui/input/tui-input-routing.js +0 -53
  84. package/dist/ui/input/vt-session.js +0 -168
  85. package/dist/ui/messages.js +0 -59
  86. package/dist/ui/model-manager/model-manager-state.js +0 -118
  87. package/dist/ui/model-manager/model-manager-ui.js +0 -194
  88. package/dist/ui/prompt/prompt-box.js +0 -78
  89. package/dist/ui/prompt/prompt-composer.js +0 -498
  90. package/dist/ui/spinner.js +0 -76
  91. package/dist/ui/title-box.js +0 -388
  92. package/dist/ui/tui/app.js +0 -6
  93. package/dist/ui/tui/autocomplete.js +0 -85
  94. package/dist/ui/tui/constants.js +0 -18
  95. package/dist/ui/tui/history.js +0 -29
  96. package/dist/ui/tui/layout.js +0 -341
  97. package/dist/ui/tui/runtime-core.js +0 -2584
  98. package/dist/ui/tui/runtime-utils.js +0 -53
  99. package/dist/ui/tui/start-tui.js +0 -54
  100. package/dist/ui/tui/startup.js +0 -56
  101. package/dist/version.js +0 -51
  102. package/dist/vt-session.js +0 -168
  103. package/install.sh +0 -457
@@ -1,124 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WhatsAppAdapter = void 0;
4
- const formatting_1 = require("#gateway/adapters/formatting");
5
- const DEFAULT_API_BASE_URL = "https://graph.facebook.com";
6
- const DEFAULT_API_VERSION = "v21.0";
7
- const DEFAULT_MAX_MESSAGE_LENGTH = 4000;
8
- function isObject(value) {
9
- return typeof value === "object" && value !== null;
10
- }
11
- function parseTimestamp(value) {
12
- if (!value) {
13
- return undefined;
14
- }
15
- const seconds = Number.parseInt(value, 10);
16
- if (!Number.isFinite(seconds)) {
17
- return undefined;
18
- }
19
- return new Date(seconds * 1000);
20
- }
21
- function findContactByWaId(contacts, waId) {
22
- if (!contacts || contacts.length === 0) {
23
- return undefined;
24
- }
25
- return contacts.find((contact) => contact.wa_id === waId);
26
- }
27
- function toInboundMessage(message, value, entryId) {
28
- if (message.type !== "text") {
29
- return null;
30
- }
31
- const senderId = message.from;
32
- const text = message.text?.body;
33
- if (typeof senderId !== "string" || typeof text !== "string") {
34
- return null;
35
- }
36
- const contact = findContactByWaId(value.contacts, senderId);
37
- return {
38
- platform: "whatsapp",
39
- senderId,
40
- channelId: value.metadata?.phone_number_id,
41
- text,
42
- messageId: message.id,
43
- timestamp: parseTimestamp(message.timestamp),
44
- metadata: {
45
- waId: contact?.wa_id ?? senderId,
46
- profileName: contact?.profile?.name,
47
- displayPhoneNumber: value.metadata?.display_phone_number,
48
- phoneNumberId: value.metadata?.phone_number_id,
49
- entryId
50
- }
51
- };
52
- }
53
- function parseInboundMessages(payload) {
54
- if (!isObject(payload)) {
55
- return [];
56
- }
57
- const envelope = payload;
58
- if (!Array.isArray(envelope.entry)) {
59
- return [];
60
- }
61
- const messages = [];
62
- for (const entry of envelope.entry) {
63
- if (!Array.isArray(entry.changes)) {
64
- continue;
65
- }
66
- for (const change of entry.changes) {
67
- const value = change.value;
68
- if (!value || !Array.isArray(value.messages)) {
69
- continue;
70
- }
71
- for (const message of value.messages) {
72
- const inbound = toInboundMessage(message, value, entry.id);
73
- if (inbound) {
74
- messages.push(inbound);
75
- }
76
- }
77
- }
78
- }
79
- return messages;
80
- }
81
- class WhatsAppAdapter {
82
- platform = "whatsapp";
83
- accessToken;
84
- phoneNumberId;
85
- apiBaseUrl;
86
- apiVersion;
87
- maxMessageLength;
88
- constructor(options) {
89
- this.accessToken = options.accessToken.trim();
90
- this.phoneNumberId = options.phoneNumberId.trim();
91
- this.apiBaseUrl = (options.apiBaseUrl ?? DEFAULT_API_BASE_URL).trim().replace(/\/+$/, "");
92
- this.apiVersion = (options.apiVersion ?? DEFAULT_API_VERSION).trim();
93
- this.maxMessageLength = Math.max(1, Math.trunc(options.maxMessageLength ?? DEFAULT_MAX_MESSAGE_LENGTH));
94
- }
95
- parseInbound(payload) {
96
- return parseInboundMessages(payload);
97
- }
98
- formatOutbound(context, response) {
99
- const normalizedText = (0, formatting_1.normalizeOutboundText)(response.text);
100
- const chunks = (0, formatting_1.chunkOutboundText)(normalizedText, this.maxMessageLength);
101
- if (chunks.length === 0) {
102
- return null;
103
- }
104
- const requests = chunks.map((chunk) => ({
105
- method: "POST",
106
- endpoint: `${this.apiBaseUrl}/${this.apiVersion}/${this.phoneNumberId}/messages`,
107
- headers: {
108
- authorization: `Bearer ${this.accessToken}`,
109
- "content-type": "application/json"
110
- },
111
- body: {
112
- messaging_product: "whatsapp",
113
- recipient_type: "individual",
114
- to: context.message.senderId,
115
- type: "text",
116
- text: {
117
- body: chunk
118
- }
119
- }
120
- }));
121
- return requests.length === 1 ? (requests[0] ?? null) : requests;
122
- }
123
- }
124
- exports.WhatsAppAdapter = WhatsAppAdapter;
@@ -1,66 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GatewayAuthPolicy = void 0;
4
- function toSenderKey(message) {
5
- return `${message.platform}:${message.senderId}`;
6
- }
7
- function parseAuthCommand(text) {
8
- const trimmed = text.trim();
9
- if (!trimmed.startsWith("/auth")) {
10
- return null;
11
- }
12
- const parts = trimmed.split(/\s+/);
13
- if (parts[0] !== "/auth") {
14
- return null;
15
- }
16
- return parts[1] ?? "";
17
- }
18
- class GatewayAuthPolicy {
19
- allowedSenderIds;
20
- passphrase;
21
- authenticatedSenders = new Set();
22
- constructor(options) {
23
- this.allowedSenderIds =
24
- options.allowedSenderIds && options.allowedSenderIds.length > 0
25
- ? new Set(options.allowedSenderIds)
26
- : null;
27
- this.passphrase = options.passphrase?.trim() ? options.passphrase.trim() : null;
28
- }
29
- evaluate(message) {
30
- if (this.allowedSenderIds && !this.allowedSenderIds.has(message.senderId)) {
31
- return {
32
- type: "unauthorized",
33
- reason: "sender_not_allowed"
34
- };
35
- }
36
- if (!this.passphrase) {
37
- return {
38
- type: "authorized"
39
- };
40
- }
41
- const senderKey = toSenderKey(message);
42
- if (this.authenticatedSenders.has(senderKey)) {
43
- return {
44
- type: "authorized"
45
- };
46
- }
47
- const authAttempt = parseAuthCommand(message.text);
48
- if (authAttempt === null) {
49
- return {
50
- type: "unauthorized",
51
- reason: "passphrase_required"
52
- };
53
- }
54
- if (authAttempt !== this.passphrase) {
55
- return {
56
- type: "unauthorized",
57
- reason: "passphrase_invalid"
58
- };
59
- }
60
- this.authenticatedSenders.add(senderKey);
61
- return {
62
- type: "authenticated"
63
- };
64
- }
65
- }
66
- exports.GatewayAuthPolicy = GatewayAuthPolicy;
@@ -1,87 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GatewayCore = void 0;
4
- const auth_policy_1 = require("#gateway/auth-policy");
5
- const message_router_1 = require("#gateway/message-router");
6
- const rate_limiter_1 = require("#gateway/rate-limiter");
7
- const session_manager_1 = require("#gateway/session-manager");
8
- class GatewayCore {
9
- authPolicy;
10
- unauthorizedMessage;
11
- rateLimiter;
12
- sessionManager;
13
- handleMessageFn;
14
- constructor(options) {
15
- this.authPolicy = new auth_policy_1.GatewayAuthPolicy({
16
- allowedSenderIds: options.allowedSenderIds,
17
- passphrase: options.passphrase
18
- });
19
- this.unauthorizedMessage =
20
- options.unauthorizedMessage ??
21
- "Access denied. Authenticate with /auth <passphrase> or contact the administrator.";
22
- this.rateLimiter =
23
- options.rateLimiter ??
24
- new rate_limiter_1.GatewayRateLimiter({
25
- maxMessages: 20,
26
- windowMs: 60_000
27
- });
28
- this.sessionManager = options.sessionManager ?? new session_manager_1.GatewaySessionManager();
29
- this.handleMessageFn = options.handleMessage;
30
- }
31
- async dispatch(message) {
32
- const normalizedMessage = (0, message_router_1.normalizeIncomingMessage)(message);
33
- const validationError = (0, message_router_1.validateIncomingMessage)(normalizedMessage);
34
- if (validationError) {
35
- return {
36
- status: "invalid",
37
- reason: validationError
38
- };
39
- }
40
- const authDecision = this.authPolicy.evaluate(normalizedMessage);
41
- if (authDecision.type === "authenticated") {
42
- return {
43
- status: "authenticated",
44
- response: {
45
- text: "Authentication successful. You can now send messages."
46
- }
47
- };
48
- }
49
- if (authDecision.type === "unauthorized") {
50
- return {
51
- status: "unauthorized",
52
- reason: authDecision.reason,
53
- response: {
54
- text: this.unauthorizedMessage
55
- }
56
- };
57
- }
58
- const rate = this.rateLimiter.check(normalizedMessage.senderId);
59
- if (!rate.allowed) {
60
- return {
61
- status: "rate_limited",
62
- reason: "rate_limit_exceeded",
63
- retryAfterMs: rate.retryAfterMs
64
- };
65
- }
66
- const session = this.sessionManager.getOrCreateSession(normalizedMessage);
67
- const response = await this.handleMessageFn({
68
- message: normalizedMessage,
69
- session
70
- });
71
- return {
72
- status: "ok",
73
- sessionId: session.id,
74
- response
75
- };
76
- }
77
- listSessions() {
78
- return this.sessionManager.listSessions();
79
- }
80
- pruneIdleSessions(maxIdleMs) {
81
- return this.sessionManager.pruneIdleSessions(maxIdleMs);
82
- }
83
- pruneRateLimiterState() {
84
- return this.rateLimiter.pruneStaleCounters();
85
- }
86
- }
87
- exports.GatewayCore = GatewayCore;
@@ -1,328 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GatewayHeadlessConductor = void 0;
4
- exports.createGatewayHeadlessMessageHandler = createGatewayHeadlessMessageHandler;
5
- const conductor_1 = require("#agent/conductor");
6
- const code_context_1 = require("#agent/context/code-context");
7
- const session_store_1 = require("#agent/context/session-store");
8
- const skills_1 = require("#agent/skills/skills");
9
- const tool_executor_1 = require("#agent/tools/tool-executor");
10
- const tool_safety_1 = require("#agent/tools/tool-safety");
11
- const llama_server_1 = require("#llm/llama-server");
12
- const llama_client_1 = require("#llm/llama-client");
13
- const token_counter_1 = require("#llm/token-counter");
14
- const vt_session_1 = require("#ui/input/vt-session");
15
- const UNSUPPORTED_BACKEND_RESPONSE = "Gateway headless mode currently supports backend 'llamacpp' only.";
16
- const AUTO_DENY_OUTPUT = "Action denied by gateway safety policy.";
17
- function composeChatRequestMessages(history, codeContextMessage) {
18
- if (!codeContextMessage) {
19
- return history;
20
- }
21
- return [{ role: "system", content: codeContextMessage }, ...history];
22
- }
23
- function buildSubagentScopeMessage(call) {
24
- const lines = [
25
- "Subagent scope:",
26
- `Task: ${call.task}`,
27
- call.context ? `Context: ${call.context}` : null,
28
- call.allowedTools
29
- ? `Allowed tools: ${call.allowedTools.length > 0 ? call.allowedTools.join(", ") : "(none)"}`
30
- : "Allowed tools: all available tools",
31
- "Stay focused on the delegated scope and return concise findings."
32
- ].filter((line) => line !== null);
33
- return lines.join("\n");
34
- }
35
- function assessToolCallRisk(call, workingZone) {
36
- if (call.name === "run_command") {
37
- const command = typeof call.arguments["command"] === "string" ? call.arguments["command"] : "";
38
- const cwdArg = typeof call.arguments["cwd"] === "string" ? call.arguments["cwd"] : ".";
39
- const resolvedCwd = (0, tool_safety_1.resolveToolPath)(cwdArg, workingZone);
40
- return (0, tool_safety_1.assessCommandRisk)(command, resolvedCwd, workingZone);
41
- }
42
- const pathArg = typeof call.arguments["path"] === "string" ? call.arguments["path"] : ".";
43
- return (0, tool_safety_1.assessPathRisk)(pathArg, workingZone);
44
- }
45
- function toErrorMessage(error) {
46
- return error instanceof Error ? error.message : String(error);
47
- }
48
- const DEFAULT_DEPS = {
49
- createLlamaClient: (config) => new llama_client_1.LlamaClient({
50
- baseUrl: config.llamaBaseUrl,
51
- model: config.model
52
- }),
53
- ensureReady: llama_server_1.ensureLlamaReady,
54
- formatStartupFailure: llama_server_1.formatLlamaStartupFailure,
55
- runConductor: conductor_1.runConductorTurn,
56
- executeTool: tool_executor_1.executeToolCall,
57
- executeSkill: skills_1.executeSkillCall,
58
- createVtSession: () => new vt_session_1.VirtualTerminalSession(),
59
- loadCodeContext: code_context_1.loadCodeContext,
60
- toCodeContextSystemMessage: code_context_1.toCodeContextSystemMessage,
61
- createSessionFile: session_store_1.createSessionFileFromHistory,
62
- writeSessionFile: session_store_1.writeSessionFile,
63
- estimateCompletionTokens: (text) => (0, token_counter_1.estimateConversationTokens)([{ content: text }]),
64
- estimateHistoryTokens: (history) => (0, token_counter_1.estimateConversationTokens)(history)
65
- };
66
- class GatewayHeadlessConductor {
67
- config;
68
- gatewayBackend;
69
- username;
70
- workingDirectory;
71
- maxRounds;
72
- deps;
73
- sessionStates = new Map();
74
- llamaClient;
75
- vtSession;
76
- codeContextMessage = null;
77
- constructor(options, deps = {}) {
78
- this.config = options.config;
79
- this.gatewayBackend = options.gatewayBackend ?? this.config.backend;
80
- this.username = options.username ?? "Gateway User";
81
- this.workingDirectory = options.workingDirectory ?? process.cwd();
82
- this.maxRounds = options.maxRounds;
83
- this.deps = { ...DEFAULT_DEPS, ...deps };
84
- this.llamaClient = this.deps.createLlamaClient(this.config);
85
- this.vtSession = this.deps.createVtSession();
86
- }
87
- async initialize() {
88
- const loaded = await this.deps.loadCodeContext(this.workingDirectory);
89
- if (!loaded) {
90
- this.codeContextMessage = null;
91
- return;
92
- }
93
- this.codeContextMessage = this.deps.toCodeContextSystemMessage(loaded);
94
- }
95
- async handleMessage(context) {
96
- if (this.gatewayBackend !== "llamacpp") {
97
- return {
98
- text: `${UNSUPPORTED_BACKEND_RESPONSE} Configured backend: '${this.gatewayBackend}'.`
99
- };
100
- }
101
- const state = this.getOrCreateSessionState(context);
102
- state.history.push({ role: "user", content: context.message.text });
103
- let latestAssistantText = "";
104
- try {
105
- await this.deps.runConductor({
106
- history: state.history,
107
- requestAssistant: async () => {
108
- const readiness = await this.deps.ensureReady(this.config);
109
- if (!readiness.ready) {
110
- throw new Error(readiness.failure
111
- ? this.deps.formatStartupFailure(readiness.failure, this.config)
112
- : "llama.cpp is unavailable.");
113
- }
114
- this.llamaClient.setModel(this.config.model);
115
- const requestMessages = composeChatRequestMessages(state.history, this.codeContextMessage);
116
- const result = await this.llamaClient.chat(requestMessages, this.config.model);
117
- return {
118
- text: result.text,
119
- rendered: false,
120
- totalTokens: result.usage?.totalTokens,
121
- completionTokens: result.usage?.completionTokens ?? this.deps.estimateCompletionTokens(result.text)
122
- };
123
- },
124
- executeToolCalls: async (toolCalls) => {
125
- const results = [];
126
- for (const call of toolCalls) {
127
- const risk = assessToolCallRisk(call, this.workingDirectory);
128
- if (risk.requiresConfirmation) {
129
- results.push({
130
- callId: call.id,
131
- tool: call.name,
132
- status: "denied",
133
- output: AUTO_DENY_OUTPUT
134
- });
135
- continue;
136
- }
137
- const result = await this.deps.executeTool(call, {
138
- workingDirectory: this.workingDirectory,
139
- vtSession: this.vtSession
140
- });
141
- results.push(result);
142
- }
143
- return results;
144
- },
145
- executeSkillCalls: async (skillCalls) => {
146
- const results = [];
147
- for (const call of skillCalls) {
148
- const result = await this.deps.executeSkill(call, {
149
- workingDirectory: this.workingDirectory,
150
- vtSession: this.vtSession
151
- });
152
- results.push(result);
153
- }
154
- return results;
155
- },
156
- executeSubagentCalls: async (subagentCalls) => {
157
- const results = [];
158
- for (const subagentCall of subagentCalls) {
159
- const scopedHistory = [
160
- { role: "system", content: buildSubagentScopeMessage(subagentCall) },
161
- { role: "user", content: subagentCall.task }
162
- ];
163
- const allowedTools = subagentCall.allowedTools !== undefined ? new Set(subagentCall.allowedTools) : null;
164
- try {
165
- const turn = await this.deps.runConductor({
166
- history: scopedHistory,
167
- requestAssistant: async () => {
168
- const readiness = await this.deps.ensureReady(this.config);
169
- if (!readiness.ready) {
170
- throw new Error(readiness.failure
171
- ? this.deps.formatStartupFailure(readiness.failure, this.config)
172
- : "llama.cpp is unavailable.");
173
- }
174
- this.llamaClient.setModel(this.config.model);
175
- const requestMessages = composeChatRequestMessages(scopedHistory, null);
176
- const result = await this.llamaClient.chat(requestMessages, this.config.model);
177
- return {
178
- text: result.text,
179
- rendered: false,
180
- totalTokens: result.usage?.totalTokens,
181
- completionTokens: result.usage?.completionTokens ??
182
- this.deps.estimateCompletionTokens(result.text)
183
- };
184
- },
185
- executeToolCalls: async (toolCalls) => {
186
- const denied = [];
187
- const permitted = [];
188
- for (const call of toolCalls) {
189
- if (allowedTools && !allowedTools.has(call.name)) {
190
- denied.push({
191
- callId: call.id,
192
- tool: call.name,
193
- status: "denied",
194
- output: `Tool '${call.name}' is not allowed for subagent ${subagentCall.id}.`
195
- });
196
- continue;
197
- }
198
- const risk = assessToolCallRisk(call, this.workingDirectory);
199
- if (risk.requiresConfirmation) {
200
- denied.push({
201
- callId: call.id,
202
- tool: call.name,
203
- status: "denied",
204
- output: AUTO_DENY_OUTPUT
205
- });
206
- continue;
207
- }
208
- permitted.push(call);
209
- }
210
- const executed = [];
211
- for (const call of permitted) {
212
- const result = await this.deps.executeTool(call, {
213
- workingDirectory: this.workingDirectory,
214
- vtSession: this.vtSession
215
- });
216
- executed.push(result);
217
- }
218
- return [...denied, ...executed];
219
- },
220
- executeSkillCalls: async (skillCalls) => {
221
- const skillResults = [];
222
- for (const call of skillCalls) {
223
- const result = await this.deps.executeSkill(call, {
224
- workingDirectory: this.workingDirectory,
225
- vtSession: this.vtSession
226
- });
227
- skillResults.push(result);
228
- }
229
- return skillResults;
230
- },
231
- onAssistantText: () => {
232
- // Subagent responses are returned in metadata only.
233
- },
234
- onWarning: () => {
235
- // Warnings are intentionally omitted from outbound gateway text.
236
- },
237
- estimateCompletionTokens: this.deps.estimateCompletionTokens,
238
- estimateHistoryTokens: this.deps.estimateHistoryTokens,
239
- computeTokensPerSecond: () => null,
240
- maxRounds: subagentCall.maxRounds ?? 4
241
- });
242
- const latest = [...scopedHistory]
243
- .reverse()
244
- .find((entry) => entry.role === "assistant")?.content;
245
- results.push({
246
- callId: subagentCall.id,
247
- status: turn.finished ? "ok" : "timeout",
248
- output: latest ?? "Subagent completed without assistant output.",
249
- metadata: {
250
- rounds: turn.rounds
251
- }
252
- });
253
- }
254
- catch (error) {
255
- results.push({
256
- callId: subagentCall.id,
257
- status: "error",
258
- output: `Subagent failed: ${toErrorMessage(error)}`
259
- });
260
- }
261
- }
262
- return results;
263
- },
264
- onAssistantText: (assistantText) => {
265
- latestAssistantText = assistantText;
266
- },
267
- onWarning: () => {
268
- // Warnings are intentionally omitted from outbound gateway text.
269
- },
270
- estimateCompletionTokens: this.deps.estimateCompletionTokens,
271
- estimateHistoryTokens: this.deps.estimateHistoryTokens,
272
- computeTokensPerSecond: () => null,
273
- maxRounds: this.maxRounds
274
- });
275
- }
276
- catch (error) {
277
- const message = `Request failed: ${toErrorMessage(error)}`;
278
- state.history.push({ role: "assistant", content: message });
279
- await this.persistSession(state);
280
- return { text: message };
281
- }
282
- const finalText = latestAssistantText.trim().length > 0 ? latestAssistantText : "(no response)";
283
- await this.persistSession(state);
284
- return { text: finalText };
285
- }
286
- dispose() {
287
- this.vtSession.dispose();
288
- this.sessionStates.clear();
289
- }
290
- getOrCreateSessionState(context) {
291
- const key = `${context.session.id}:${context.session.createdAt.toISOString()}`;
292
- const existing = this.sessionStates.get(key);
293
- if (existing) {
294
- return existing;
295
- }
296
- const created = {
297
- history: [],
298
- sessionFilePath: null
299
- };
300
- this.sessionStates.set(key, created);
301
- return created;
302
- }
303
- async persistSession(state) {
304
- if (state.history.length === 0) {
305
- return;
306
- }
307
- if (!state.sessionFilePath) {
308
- const created = await this.deps.createSessionFile(state.history);
309
- state.sessionFilePath = created.path;
310
- }
311
- await this.deps.writeSessionFile({
312
- path: state.sessionFilePath,
313
- username: this.username,
314
- history: state.history
315
- });
316
- }
317
- }
318
- exports.GatewayHeadlessConductor = GatewayHeadlessConductor;
319
- async function createGatewayHeadlessMessageHandler(options, deps = {}) {
320
- const runtime = new GatewayHeadlessConductor(options, deps);
321
- await runtime.initialize();
322
- return {
323
- handleMessage: async (context) => await runtime.handleMessage(context),
324
- dispose: () => {
325
- runtime.dispose();
326
- }
327
- };
328
- }
@@ -1,23 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.normalizeIncomingMessage = normalizeIncomingMessage;
4
- exports.validateIncomingMessage = validateIncomingMessage;
5
- const MAX_TEXT_LENGTH = 4000;
6
- function normalizeIncomingMessage(message) {
7
- return {
8
- ...message,
9
- senderId: message.senderId.trim(),
10
- channelId: message.channelId?.trim(),
11
- text: message.text.trim().slice(0, MAX_TEXT_LENGTH),
12
- timestamp: message.timestamp ?? new Date()
13
- };
14
- }
15
- function validateIncomingMessage(message) {
16
- if (message.senderId.trim().length === 0) {
17
- return "sender_id_required";
18
- }
19
- if (message.text.trim().length === 0) {
20
- return "message_text_required";
21
- }
22
- return null;
23
- }
@@ -1,48 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GatewayRateLimiter = void 0;
4
- class GatewayRateLimiter {
5
- maxMessages;
6
- windowMs;
7
- counters = new Map();
8
- now;
9
- constructor(options, now = () => new Date()) {
10
- this.maxMessages = Math.max(1, Math.trunc(options.maxMessages));
11
- this.windowMs = Math.max(1000, Math.trunc(options.windowMs));
12
- this.now = now;
13
- }
14
- check(senderId) {
15
- const now = this.now().valueOf();
16
- const existing = this.counters.get(senderId);
17
- const active = existing && now - existing.startedAt < this.windowMs ? existing : { count: 0, startedAt: now };
18
- active.count += 1;
19
- this.counters.set(senderId, active);
20
- const resetAt = new Date(active.startedAt + this.windowMs);
21
- if (active.count <= this.maxMessages) {
22
- return {
23
- allowed: true,
24
- remaining: this.maxMessages - active.count,
25
- retryAfterMs: 0,
26
- resetAt
27
- };
28
- }
29
- return {
30
- allowed: false,
31
- remaining: 0,
32
- retryAfterMs: Math.max(1, active.startedAt + this.windowMs - now),
33
- resetAt
34
- };
35
- }
36
- pruneStaleCounters() {
37
- const now = this.now().valueOf();
38
- let removed = 0;
39
- for (const [senderId, window] of this.counters) {
40
- if (now - window.startedAt >= this.windowMs) {
41
- this.counters.delete(senderId);
42
- removed += 1;
43
- }
44
- }
45
- return removed;
46
- }
47
- }
48
- exports.GatewayRateLimiter = GatewayRateLimiter;