@phneakngar/cli 0.0.2 → 0.0.3
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/bin-mcp.js +2991 -0
- package/dist/index.js +4908 -186
- package/dist/session-runner.js +1088 -42
- package/dist/web-brain-mcp.js +2991 -0
- package/package.json +3 -2
package/dist/session-runner.js
CHANGED
|
@@ -39,12 +39,14 @@ var TASK_TYPES = {
|
|
|
39
39
|
EMAIL_NOTIFICATION: "email_notification",
|
|
40
40
|
CALENDAR_EVENT: "calendar_event",
|
|
41
41
|
ISSUE_EVENT: "issue_event",
|
|
42
|
+
AUTOMATION_EVENT: "automation_event",
|
|
42
43
|
KILL_TASK: "kill_task"
|
|
43
44
|
};
|
|
44
45
|
var IssueStatus = {
|
|
45
46
|
TODO: "todo",
|
|
46
47
|
IN_PROGRESS: "in_progress",
|
|
47
48
|
REVIEW: "review",
|
|
49
|
+
BLOCKED: "blocked",
|
|
48
50
|
DONE: "done",
|
|
49
51
|
CLOSED: "closed",
|
|
50
52
|
CANCELED: "canceled",
|
|
@@ -53,7 +55,8 @@ var IssueStatus = {
|
|
|
53
55
|
var ACTIVE_ISSUE_STATUSES = [
|
|
54
56
|
IssueStatus.TODO,
|
|
55
57
|
IssueStatus.IN_PROGRESS,
|
|
56
|
-
IssueStatus.REVIEW
|
|
58
|
+
IssueStatus.REVIEW,
|
|
59
|
+
IssueStatus.BLOCKED
|
|
57
60
|
];
|
|
58
61
|
var TERMINAL_ISSUE_STATUSES = [
|
|
59
62
|
IssueStatus.DONE,
|
|
@@ -80,6 +83,25 @@ var TERMINAL_MEETING_STATUSES = [
|
|
|
80
83
|
var DEV_WEB_URL = process.env.PHNEAKNGAR_SERVER_URL || "http://localhost:3000";
|
|
81
84
|
var DEV_WS_DO_URL = process.env.DEV_WS_DO_URL || "http://localhost:8789";
|
|
82
85
|
var DEV_EMAIL_WORKER_URL = process.env.DEV_EMAIL_WORKER_URL || "http://localhost:8787";
|
|
86
|
+
var ApprovalKind = {
|
|
87
|
+
OUTBOUND_EMAIL: "outbound_email",
|
|
88
|
+
TOOL_ACTION: "tool_action",
|
|
89
|
+
SKILL_INSTALL: "skill_install",
|
|
90
|
+
AUTOMATION_PROMOTE: "automation_promote"
|
|
91
|
+
};
|
|
92
|
+
var MemoryKind = {
|
|
93
|
+
PREFERENCE: "preference",
|
|
94
|
+
DECISION: "decision",
|
|
95
|
+
FACT: "fact",
|
|
96
|
+
ROLE: "role",
|
|
97
|
+
SUMMARY: "summary"
|
|
98
|
+
};
|
|
99
|
+
var DeliveryArtifactKind = {
|
|
100
|
+
DELIVERY: "delivery",
|
|
101
|
+
DIGEST: "digest",
|
|
102
|
+
DRAFT: "draft",
|
|
103
|
+
REPORT: "report"
|
|
104
|
+
};
|
|
83
105
|
// ../shared/src/locale.ts
|
|
84
106
|
var Locale = {
|
|
85
107
|
EN: "en",
|
|
@@ -122,6 +144,10 @@ var coreEntityLabels = {
|
|
|
122
144
|
[Locale.EN]: "Agent",
|
|
123
145
|
[Locale.KM]: "ភ្នាក់ងារ"
|
|
124
146
|
},
|
|
147
|
+
teammate: {
|
|
148
|
+
[Locale.EN]: "Teammate",
|
|
149
|
+
[Locale.KM]: "មិត្តរួមការងារ"
|
|
150
|
+
},
|
|
125
151
|
runtime: {
|
|
126
152
|
[Locale.EN]: "Runtime",
|
|
127
153
|
[Locale.KM]: "បរិស្ថានដំណើរការ"
|
|
@@ -130,6 +156,18 @@ var coreEntityLabels = {
|
|
|
130
156
|
[Locale.EN]: "Memory",
|
|
131
157
|
[Locale.KM]: "សតិចងចាំ"
|
|
132
158
|
},
|
|
159
|
+
automation: {
|
|
160
|
+
[Locale.EN]: "Automation",
|
|
161
|
+
[Locale.KM]: "ស្វ័យប្រវត្តិកម្ម"
|
|
162
|
+
},
|
|
163
|
+
approval: {
|
|
164
|
+
[Locale.EN]: "Approval",
|
|
165
|
+
[Locale.KM]: "ការអនុម័ត"
|
|
166
|
+
},
|
|
167
|
+
routine: {
|
|
168
|
+
[Locale.EN]: "Routine",
|
|
169
|
+
[Locale.KM]: "ទម្លាប់ការងារ"
|
|
170
|
+
},
|
|
133
171
|
template: {
|
|
134
172
|
[Locale.EN]: "Template",
|
|
135
173
|
[Locale.KM]: "គំរូ"
|
|
@@ -278,6 +316,10 @@ var taskTypeLabels = {
|
|
|
278
316
|
[Locale.EN]: "Issue event",
|
|
279
317
|
[Locale.KM]: "ព្រឹត្តិការណ៍បញ្ហា"
|
|
280
318
|
},
|
|
319
|
+
automation_event: {
|
|
320
|
+
[Locale.EN]: "Automation",
|
|
321
|
+
[Locale.KM]: "ស្វ័យប្រវត្តិកម្ម"
|
|
322
|
+
},
|
|
281
323
|
kill_task: {
|
|
282
324
|
[Locale.EN]: "Stop task",
|
|
283
325
|
[Locale.KM]: "បញ្ឈប់ភារកិច្ច"
|
|
@@ -296,6 +338,10 @@ var issueStatusLabels = {
|
|
|
296
338
|
[Locale.EN]: "Review",
|
|
297
339
|
[Locale.KM]: "រង់ចាំពិនិត្យ"
|
|
298
340
|
},
|
|
341
|
+
blocked: {
|
|
342
|
+
[Locale.EN]: "Blocked",
|
|
343
|
+
[Locale.KM]: "ជាប់គាំង"
|
|
344
|
+
},
|
|
299
345
|
done: {
|
|
300
346
|
[Locale.EN]: "Done",
|
|
301
347
|
[Locale.KM]: "រួចរាល់"
|
|
@@ -14987,6 +15033,7 @@ var CalendarEventApiSchema = exports_external.object({
|
|
|
14987
15033
|
var IssueStatusSchema = exports_external.enum([
|
|
14988
15034
|
IssueStatus.TODO,
|
|
14989
15035
|
IssueStatus.IN_PROGRESS,
|
|
15036
|
+
IssueStatus.BLOCKED,
|
|
14990
15037
|
IssueStatus.REVIEW,
|
|
14991
15038
|
IssueStatus.DONE,
|
|
14992
15039
|
IssueStatus.CLOSED,
|
|
@@ -15004,6 +15051,12 @@ var UpdateIssueRequestSchema = exports_external.object({
|
|
|
15004
15051
|
status: IssueStatusSchema.optional(),
|
|
15005
15052
|
agent_id: exports_external.string().min(1).optional()
|
|
15006
15053
|
}).refine((v) => v.title !== undefined || v.description !== undefined || v.status !== undefined || v.agent_id !== undefined, { message: "at least one field is required" });
|
|
15054
|
+
var ClaimIssueRequestSchema = exports_external.object({
|
|
15055
|
+
agent_id: exports_external.string().min(1, "agent_id is required")
|
|
15056
|
+
});
|
|
15057
|
+
var HandBackIssueRequestSchema = exports_external.object({
|
|
15058
|
+
agent_id: exports_external.string().min(1).optional()
|
|
15059
|
+
});
|
|
15007
15060
|
var CreateIssueCommentBodySchema = exports_external.object({
|
|
15008
15061
|
content: exports_external.string().min(1, "content is required").max(20000)
|
|
15009
15062
|
});
|
|
@@ -15023,6 +15076,8 @@ var IssueApiSchema = exports_external.object({
|
|
|
15023
15076
|
creator_user_id: exports_external.string(),
|
|
15024
15077
|
conversation_id: exports_external.string().nullable(),
|
|
15025
15078
|
latest_task_id: exports_external.string().nullable(),
|
|
15079
|
+
claimed_by_agent_id: exports_external.string().nullable().optional(),
|
|
15080
|
+
claimed_at: exports_external.string().nullable().optional(),
|
|
15026
15081
|
title: exports_external.string(),
|
|
15027
15082
|
description: exports_external.string(),
|
|
15028
15083
|
status: IssueStatusSchema,
|
|
@@ -15062,6 +15117,8 @@ var CreateAgentRequestSchema = exports_external.object({
|
|
|
15062
15117
|
name: exports_external.string().min(1, "name is required"),
|
|
15063
15118
|
description: exports_external.string().optional().default(""),
|
|
15064
15119
|
instructions: exports_external.string().optional().default(""),
|
|
15120
|
+
role_title: exports_external.string().max(120).optional().default(""),
|
|
15121
|
+
responsibility: exports_external.string().max(2000).optional().default(""),
|
|
15065
15122
|
runtime_id: exports_external.string().min(1, "runtime_id is required"),
|
|
15066
15123
|
runtime_config: RuntimeConfigSchema,
|
|
15067
15124
|
max_concurrent_tasks: exports_external.number().int().optional(),
|
|
@@ -15072,11 +15129,108 @@ var UpdateAgentRequestSchema = exports_external.object({
|
|
|
15072
15129
|
name: exports_external.string().min(1).optional(),
|
|
15073
15130
|
description: exports_external.string().optional(),
|
|
15074
15131
|
instructions: exports_external.string().optional(),
|
|
15132
|
+
role_title: exports_external.string().max(120).optional(),
|
|
15133
|
+
responsibility: exports_external.string().max(2000).optional(),
|
|
15075
15134
|
runtime_id: exports_external.string().min(1).optional(),
|
|
15076
15135
|
runtime_config: RuntimeConfigSchema,
|
|
15077
15136
|
visibility: exports_external.enum(["public", "private"]).optional(),
|
|
15078
15137
|
avatar_url: exports_external.string().max(2000).nullable().optional()
|
|
15079
|
-
}).refine((v) => v.name !== undefined || v.description !== undefined || v.instructions !== undefined || v.runtime_id !== undefined || v.runtime_config !== undefined || v.visibility !== undefined || v.avatar_url !== undefined, { message: "at least one field is required" });
|
|
15138
|
+
}).refine((v) => v.name !== undefined || v.description !== undefined || v.instructions !== undefined || v.role_title !== undefined || v.responsibility !== undefined || v.runtime_id !== undefined || v.runtime_config !== undefined || v.visibility !== undefined || v.avatar_url !== undefined, { message: "at least one field is required" });
|
|
15139
|
+
var AutomationDeliveryModeSchema = exports_external.enum([
|
|
15140
|
+
"channel",
|
|
15141
|
+
"dm",
|
|
15142
|
+
"email_draft",
|
|
15143
|
+
"issue"
|
|
15144
|
+
]);
|
|
15145
|
+
var CreateAutomationRequestSchema = exports_external.object({
|
|
15146
|
+
agent_id: exports_external.string().min(1, "agent_id is required"),
|
|
15147
|
+
title: exports_external.string().min(1).max(200),
|
|
15148
|
+
sop_markdown: exports_external.string().max(50000).optional().default(""),
|
|
15149
|
+
schedule: exports_external.string().min(1).max(200),
|
|
15150
|
+
next_run_at: exports_external.string().min(1),
|
|
15151
|
+
delivery_mode: AutomationDeliveryModeSchema.optional().default("channel"),
|
|
15152
|
+
delivery_channel_id: exports_external.string().min(1).nullable().optional(),
|
|
15153
|
+
skill_name: exports_external.string().max(200).nullable().optional(),
|
|
15154
|
+
enabled: exports_external.boolean().optional().default(true)
|
|
15155
|
+
});
|
|
15156
|
+
var UpdateAutomationRequestSchema = exports_external.object({
|
|
15157
|
+
title: exports_external.string().min(1).max(200).optional(),
|
|
15158
|
+
sop_markdown: exports_external.string().max(50000).optional(),
|
|
15159
|
+
schedule: exports_external.string().min(1).max(200).optional(),
|
|
15160
|
+
next_run_at: exports_external.string().min(1).optional(),
|
|
15161
|
+
delivery_mode: AutomationDeliveryModeSchema.optional(),
|
|
15162
|
+
delivery_channel_id: exports_external.string().min(1).nullable().optional(),
|
|
15163
|
+
skill_name: exports_external.string().max(200).nullable().optional(),
|
|
15164
|
+
enabled: exports_external.boolean().optional()
|
|
15165
|
+
}).refine((v) => v.title !== undefined || v.sop_markdown !== undefined || v.schedule !== undefined || v.next_run_at !== undefined || v.delivery_mode !== undefined || v.delivery_channel_id !== undefined || v.skill_name !== undefined || v.enabled !== undefined, { message: "at least one field is required" });
|
|
15166
|
+
var MemoryKindSchema = exports_external.enum(["preference", "decision", "fact", "role"]);
|
|
15167
|
+
var CreateMemoryRequestSchema = exports_external.object({
|
|
15168
|
+
agent_id: exports_external.string().min(1).nullable().optional(),
|
|
15169
|
+
kind: MemoryKindSchema,
|
|
15170
|
+
content: exports_external.string().min(1).max(1e4),
|
|
15171
|
+
source_task_id: exports_external.string().min(1).nullable().optional()
|
|
15172
|
+
});
|
|
15173
|
+
var UpdateMemoryRequestSchema = exports_external.object({
|
|
15174
|
+
kind: MemoryKindSchema.optional(),
|
|
15175
|
+
content: exports_external.string().min(1).max(1e4).optional()
|
|
15176
|
+
}).refine((v) => v.kind !== undefined || v.content !== undefined, {
|
|
15177
|
+
message: "at least one field is required"
|
|
15178
|
+
});
|
|
15179
|
+
var CompactMemoryRequestSchema = exports_external.object({
|
|
15180
|
+
agent_id: exports_external.string().min(1).nullable().optional(),
|
|
15181
|
+
min_notes: exports_external.number().int().min(1).max(500).optional(),
|
|
15182
|
+
max_notes: exports_external.number().int().min(1).max(500).optional(),
|
|
15183
|
+
max_length: exports_external.number().int().min(0).max(50000).optional(),
|
|
15184
|
+
dry_run: exports_external.boolean().optional()
|
|
15185
|
+
});
|
|
15186
|
+
var DecideApprovalRequestSchema = exports_external.object({
|
|
15187
|
+
decision: exports_external.enum(["approved", "rejected"])
|
|
15188
|
+
});
|
|
15189
|
+
var ProposeSkillFromTaskRequestSchema = exports_external.object({
|
|
15190
|
+
task_id: exports_external.string().min(1),
|
|
15191
|
+
agent_id: exports_external.string().min(1).optional(),
|
|
15192
|
+
runtime: exports_external.enum(["claude", "codex", "opencode", "grok"]).optional()
|
|
15193
|
+
});
|
|
15194
|
+
var CreateIntegrationRequestSchema = exports_external.object({
|
|
15195
|
+
provider: exports_external.string().min(1).max(80),
|
|
15196
|
+
status: exports_external.enum(["active", "disabled", "error"]).optional().default("active"),
|
|
15197
|
+
config: exports_external.unknown().optional(),
|
|
15198
|
+
secret_ref: exports_external.string().max(500).nullable().optional()
|
|
15199
|
+
});
|
|
15200
|
+
var GatewayProviderSchema = exports_external.enum([
|
|
15201
|
+
"slack",
|
|
15202
|
+
"discord",
|
|
15203
|
+
"telegram",
|
|
15204
|
+
"lark",
|
|
15205
|
+
"teams"
|
|
15206
|
+
]);
|
|
15207
|
+
var CreateGatewayBindingRequestSchema = exports_external.object({
|
|
15208
|
+
provider: GatewayProviderSchema,
|
|
15209
|
+
external_team_id: exports_external.string().min(1).max(200),
|
|
15210
|
+
external_account_id: exports_external.string().min(1).max(200).nullable().optional(),
|
|
15211
|
+
agent_id: exports_external.string().min(1),
|
|
15212
|
+
user_id: exports_external.string().min(1).optional(),
|
|
15213
|
+
status: exports_external.enum(["active", "disabled"]).optional().default("active"),
|
|
15214
|
+
dm_policy: exports_external.enum(["open", "allowlist", "pairing"]).optional().default("open"),
|
|
15215
|
+
outbound_mode: exports_external.enum(["live", "preview"]).optional().default("preview"),
|
|
15216
|
+
secret_ref: exports_external.string().min(1).max(4000).nullable().optional()
|
|
15217
|
+
});
|
|
15218
|
+
var UpdateGatewayBindingRequestSchema = exports_external.object({
|
|
15219
|
+
status: exports_external.enum(["active", "disabled"]).optional(),
|
|
15220
|
+
dm_policy: exports_external.enum(["open", "allowlist", "pairing"]).optional(),
|
|
15221
|
+
outbound_mode: exports_external.enum(["live", "preview"]).optional(),
|
|
15222
|
+
agent_id: exports_external.string().min(1).optional(),
|
|
15223
|
+
user_id: exports_external.string().min(1).optional(),
|
|
15224
|
+
secret_ref: exports_external.string().min(1).max(4000).nullable().optional()
|
|
15225
|
+
}).refine((v) => v.status !== undefined || v.dm_policy !== undefined || v.outbound_mode !== undefined || v.agent_id !== undefined || v.user_id !== undefined || v.secret_ref !== undefined, { message: "at least one field is required" });
|
|
15226
|
+
var GatewayPeerAllowlistRequestSchema = exports_external.object({
|
|
15227
|
+
peer_id: exports_external.string().min(1).max(200),
|
|
15228
|
+
status: exports_external.enum(["allow", "deny", "paired"]).optional().default("allow")
|
|
15229
|
+
});
|
|
15230
|
+
var ChannelMemberRequestSchema = exports_external.object({
|
|
15231
|
+
member_type: exports_external.enum(["user", "agent"]),
|
|
15232
|
+
member_id: exports_external.string().min(1)
|
|
15233
|
+
});
|
|
15080
15234
|
var CreateConversationRequestSchema = exports_external.object({
|
|
15081
15235
|
agent_id: exports_external.string().min(1, "agent_id is required"),
|
|
15082
15236
|
channel: exports_external.string().optional()
|
|
@@ -15108,10 +15262,15 @@ var SendEmailRequestSchema = exports_external.object({
|
|
|
15108
15262
|
conversationId: exports_external.string().optional(),
|
|
15109
15263
|
traceId: exports_external.string().optional(),
|
|
15110
15264
|
sourceTaskId: exports_external.string().optional(),
|
|
15111
|
-
idempotencyKey: exports_external.string().min(1).max(128).optional()
|
|
15265
|
+
idempotencyKey: exports_external.string().min(1).max(128).optional(),
|
|
15266
|
+
requiresApproval: exports_external.boolean().optional().default(false)
|
|
15112
15267
|
});
|
|
15113
15268
|
var UpdateEmailStatusRequestSchema = exports_external.object({
|
|
15114
|
-
status: exports_external.enum([
|
|
15269
|
+
status: exports_external.enum([
|
|
15270
|
+
"unread",
|
|
15271
|
+
"read",
|
|
15272
|
+
"archived"
|
|
15273
|
+
])
|
|
15115
15274
|
});
|
|
15116
15275
|
var MeetingInfoSchema = exports_external.object({
|
|
15117
15276
|
title: exports_external.string(),
|
|
@@ -16797,6 +16956,8 @@ var agent = sqliteTable("agent", {
|
|
|
16797
16956
|
name: text("name").notNull(),
|
|
16798
16957
|
description: text("description").notNull().default(""),
|
|
16799
16958
|
instructions: text("instructions").notNull().default(""),
|
|
16959
|
+
roleTitle: text("role_title").notNull().default(""),
|
|
16960
|
+
responsibility: text("responsibility").notNull().default(""),
|
|
16800
16961
|
avatarUrl: text("avatar_url"),
|
|
16801
16962
|
runtimeId: text("runtime_id").references(() => agentRuntime.id),
|
|
16802
16963
|
runtimeMode: text("runtime_mode").notNull().default("local"),
|
|
@@ -16925,12 +17086,15 @@ var issue2 = sqliteTable("issue", {
|
|
|
16925
17086
|
title: text("title").notNull(),
|
|
16926
17087
|
description: text("description").notNull().default(""),
|
|
16927
17088
|
status: text("status").notNull().default("todo"),
|
|
17089
|
+
claimedByAgentId: text("claimed_by_agent_id"),
|
|
17090
|
+
claimedAt: text("claimed_at"),
|
|
16928
17091
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
16929
17092
|
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
16930
17093
|
completedAt: text("completed_at")
|
|
16931
17094
|
}, (t) => [
|
|
16932
17095
|
index("idx_issue_workspace_status_agent").on(t.workspaceId, t.status, t.agentId),
|
|
16933
17096
|
index("idx_issue_workspace_updated").on(t.workspaceId, t.updatedAt),
|
|
17097
|
+
index("idx_issue_workspace_claimed").on(t.workspaceId, t.claimedByAgentId),
|
|
16934
17098
|
unique("issue_conversation_unique").on(t.conversationId),
|
|
16935
17099
|
foreignKey({
|
|
16936
17100
|
columns: [t.agentId, t.workspaceId],
|
|
@@ -17023,6 +17187,7 @@ var artifact = sqliteTable("artifact", {
|
|
|
17023
17187
|
conversationId: text("conversation_id").notNull().references(() => conversation.id, { onDelete: "cascade" }),
|
|
17024
17188
|
agentId: text("agent_id").notNull(),
|
|
17025
17189
|
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
17190
|
+
taskId: text("task_id").references(() => agentTaskQueue.id, { onDelete: "set null" }),
|
|
17026
17191
|
filename: text("filename").notNull(),
|
|
17027
17192
|
contentType: text("content_type").notNull().default("application/octet-stream"),
|
|
17028
17193
|
size: integer2("size").notNull(),
|
|
@@ -17032,6 +17197,8 @@ var artifact = sqliteTable("artifact", {
|
|
|
17032
17197
|
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
17033
17198
|
}, (t) => [
|
|
17034
17199
|
index("idx_artifact_conversation").on(t.conversationId),
|
|
17200
|
+
index("idx_artifact_task").on(t.workspaceId, t.taskId),
|
|
17201
|
+
index("idx_artifact_ws_source").on(t.workspaceId, t.source, t.createdAt),
|
|
17035
17202
|
foreignKey({
|
|
17036
17203
|
columns: [t.agentId, t.workspaceId],
|
|
17037
17204
|
foreignColumns: [agent.id, agent.workspaceId]
|
|
@@ -17205,6 +17372,173 @@ var inboxUnread = sqliteTable("inbox_unread", {
|
|
|
17205
17372
|
unique("inbox_unread_conv_user").on(t.conversationId, t.userId),
|
|
17206
17373
|
index("idx_inbox_unread_user_ws").on(t.userId, t.workspaceId, t.taskType, t.completedAt)
|
|
17207
17374
|
]);
|
|
17375
|
+
var automation = sqliteTable("automation", {
|
|
17376
|
+
id: text("id").primaryKey().$defaultFn(() => "au_" + nanoid3()),
|
|
17377
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
17378
|
+
agentId: text("agent_id").notNull(),
|
|
17379
|
+
title: text("title").notNull(),
|
|
17380
|
+
sopMarkdown: text("sop_markdown").notNull().default(""),
|
|
17381
|
+
schedule: text("schedule").notNull(),
|
|
17382
|
+
nextRunAt: text("next_run_at").notNull(),
|
|
17383
|
+
deliveryMode: text("delivery_mode").notNull().default("channel"),
|
|
17384
|
+
deliveryChannelId: text("delivery_channel_id"),
|
|
17385
|
+
skillName: text("skill_name"),
|
|
17386
|
+
enabled: integer2("enabled", { mode: "boolean" }).notNull().default(true),
|
|
17387
|
+
lastRunAt: text("last_run_at"),
|
|
17388
|
+
lastTaskId: text("last_task_id"),
|
|
17389
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
17390
|
+
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
17391
|
+
}, (t) => [
|
|
17392
|
+
index("idx_automation_ws_next").on(t.workspaceId, t.enabled, t.nextRunAt),
|
|
17393
|
+
index("idx_automation_ws_agent").on(t.workspaceId, t.agentId),
|
|
17394
|
+
foreignKey({
|
|
17395
|
+
columns: [t.agentId, t.workspaceId],
|
|
17396
|
+
foreignColumns: [agent.id, agent.workspaceId]
|
|
17397
|
+
}).onDelete("cascade")
|
|
17398
|
+
]);
|
|
17399
|
+
var agentMemory = sqliteTable("agent_memory", {
|
|
17400
|
+
id: text("id").primaryKey().$defaultFn(() => "mem_" + nanoid3()),
|
|
17401
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
17402
|
+
agentId: text("agent_id"),
|
|
17403
|
+
kind: text("kind").notNull().default("fact"),
|
|
17404
|
+
content: text("content").notNull(),
|
|
17405
|
+
sourceTaskId: text("source_task_id"),
|
|
17406
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
17407
|
+
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
17408
|
+
}, (t) => [
|
|
17409
|
+
index("idx_agent_memory_ws_agent").on(t.workspaceId, t.agentId, t.kind),
|
|
17410
|
+
foreignKey({
|
|
17411
|
+
columns: [t.agentId, t.workspaceId],
|
|
17412
|
+
foreignColumns: [agent.id, agent.workspaceId]
|
|
17413
|
+
}).onDelete("cascade")
|
|
17414
|
+
]);
|
|
17415
|
+
var approval = sqliteTable("approval", {
|
|
17416
|
+
id: text("id").primaryKey().$defaultFn(() => "ap_" + nanoid3()),
|
|
17417
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
17418
|
+
agentId: text("agent_id"),
|
|
17419
|
+
kind: text("kind").notNull(),
|
|
17420
|
+
status: text("status").notNull().default("pending"),
|
|
17421
|
+
title: text("title").notNull().default(""),
|
|
17422
|
+
summary: text("summary").notNull().default(""),
|
|
17423
|
+
payload: text("payload", { mode: "json" }),
|
|
17424
|
+
decidedByUserId: text("decided_by_user_id").references(() => user.id, {
|
|
17425
|
+
onDelete: "set null"
|
|
17426
|
+
}),
|
|
17427
|
+
decidedAt: text("decided_at"),
|
|
17428
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
17429
|
+
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
17430
|
+
}, (t) => [
|
|
17431
|
+
index("idx_approval_ws_status").on(t.workspaceId, t.status, t.createdAt),
|
|
17432
|
+
index("idx_approval_ws_agent").on(t.workspaceId, t.agentId),
|
|
17433
|
+
foreignKey({
|
|
17434
|
+
columns: [t.agentId, t.workspaceId],
|
|
17435
|
+
foreignColumns: [agent.id, agent.workspaceId]
|
|
17436
|
+
}).onDelete("cascade")
|
|
17437
|
+
]);
|
|
17438
|
+
var agentIntegration = sqliteTable("agent_integration", {
|
|
17439
|
+
id: text("id").primaryKey().$defaultFn(() => "ai_" + nanoid3()),
|
|
17440
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
17441
|
+
agentId: text("agent_id").notNull(),
|
|
17442
|
+
provider: text("provider").notNull(),
|
|
17443
|
+
status: text("status").notNull().default("active"),
|
|
17444
|
+
config: text("config", { mode: "json" }),
|
|
17445
|
+
secretRef: text("secret_ref"),
|
|
17446
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
17447
|
+
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
17448
|
+
}, (t) => [
|
|
17449
|
+
unique("agent_integration_ws_agent_provider").on(t.workspaceId, t.agentId, t.provider),
|
|
17450
|
+
index("idx_agent_integration_ws_agent").on(t.workspaceId, t.agentId),
|
|
17451
|
+
foreignKey({
|
|
17452
|
+
columns: [t.agentId, t.workspaceId],
|
|
17453
|
+
foreignColumns: [agent.id, agent.workspaceId]
|
|
17454
|
+
}).onDelete("cascade")
|
|
17455
|
+
]);
|
|
17456
|
+
var channelMember = sqliteTable("channel_member", {
|
|
17457
|
+
id: text("id").primaryKey().$defaultFn(() => "cm_" + nanoid3()),
|
|
17458
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
17459
|
+
channelId: text("channel_id").notNull().references(() => channel.id, { onDelete: "cascade" }),
|
|
17460
|
+
memberType: text("member_type").notNull(),
|
|
17461
|
+
memberId: text("member_id").notNull(),
|
|
17462
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
17463
|
+
}, (t) => [
|
|
17464
|
+
unique("channel_member_unique").on(t.channelId, t.memberType, t.memberId),
|
|
17465
|
+
index("idx_channel_member_ws").on(t.workspaceId, t.channelId),
|
|
17466
|
+
index("idx_channel_member_member").on(t.workspaceId, t.memberType, t.memberId)
|
|
17467
|
+
]);
|
|
17468
|
+
var conversationMember = sqliteTable("conversation_member", {
|
|
17469
|
+
id: text("id").primaryKey().$defaultFn(() => "cvm_" + nanoid3()),
|
|
17470
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
17471
|
+
conversationId: text("conversation_id").notNull().references(() => conversation.id, { onDelete: "cascade" }),
|
|
17472
|
+
memberType: text("member_type").notNull(),
|
|
17473
|
+
memberId: text("member_id").notNull(),
|
|
17474
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
17475
|
+
}, (t) => [
|
|
17476
|
+
unique("conversation_member_unique").on(t.conversationId, t.memberType, t.memberId),
|
|
17477
|
+
index("idx_conversation_member_ws").on(t.workspaceId, t.conversationId),
|
|
17478
|
+
index("idx_conversation_member_member").on(t.workspaceId, t.memberType, t.memberId)
|
|
17479
|
+
]);
|
|
17480
|
+
var gatewayBinding = sqliteTable("gateway_binding", {
|
|
17481
|
+
id: text("id").primaryKey().$defaultFn(() => "gb_" + nanoid3()),
|
|
17482
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
17483
|
+
provider: text("provider").notNull(),
|
|
17484
|
+
externalTeamId: text("external_team_id").notNull(),
|
|
17485
|
+
externalAccountId: text("external_account_id"),
|
|
17486
|
+
agentId: text("agent_id").notNull(),
|
|
17487
|
+
userId: text("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
|
|
17488
|
+
status: text("status").notNull().default("active"),
|
|
17489
|
+
dmPolicy: text("dm_policy").notNull().default("open"),
|
|
17490
|
+
outboundMode: text("outbound_mode").notNull().default("preview"),
|
|
17491
|
+
secretRef: text("secret_ref"),
|
|
17492
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString()),
|
|
17493
|
+
updatedAt: text("updated_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
17494
|
+
}, (t) => [
|
|
17495
|
+
unique("gateway_binding_provider_team_account").on(t.provider, t.externalTeamId, t.externalAccountId),
|
|
17496
|
+
index("idx_gateway_binding_ws").on(t.workspaceId, t.provider, t.status),
|
|
17497
|
+
index("idx_gateway_binding_lookup").on(t.provider, t.externalTeamId, t.status),
|
|
17498
|
+
foreignKey({
|
|
17499
|
+
columns: [t.agentId, t.workspaceId],
|
|
17500
|
+
foreignColumns: [agent.id, agent.workspaceId]
|
|
17501
|
+
}).onDelete("cascade")
|
|
17502
|
+
]);
|
|
17503
|
+
var activityEvent = sqliteTable("activity_event", {
|
|
17504
|
+
id: text("id").primaryKey().$defaultFn(() => "ae_" + nanoid3()),
|
|
17505
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
17506
|
+
kind: text("kind").notNull(),
|
|
17507
|
+
actorType: text("actor_type"),
|
|
17508
|
+
actorId: text("actor_id"),
|
|
17509
|
+
subjectType: text("subject_type"),
|
|
17510
|
+
subjectId: text("subject_id"),
|
|
17511
|
+
summary: text("summary").notNull(),
|
|
17512
|
+
payloadJson: text("payload_json"),
|
|
17513
|
+
dedupeKey: text("dedupe_key"),
|
|
17514
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
17515
|
+
}, (t) => [
|
|
17516
|
+
index("idx_activity_event_ws_created").on(t.workspaceId, t.createdAt),
|
|
17517
|
+
unique("activity_event_ws_dedupe").on(t.workspaceId, t.dedupeKey)
|
|
17518
|
+
]);
|
|
17519
|
+
var gatewayPeerAllowlist = sqliteTable("gateway_peer_allowlist", {
|
|
17520
|
+
id: text("id").primaryKey().$defaultFn(() => "gpa_" + nanoid3()),
|
|
17521
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
17522
|
+
bindingId: text("binding_id").notNull().references(() => gatewayBinding.id, { onDelete: "cascade" }),
|
|
17523
|
+
peerId: text("peer_id").notNull(),
|
|
17524
|
+
status: text("status").notNull().default("allow"),
|
|
17525
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
17526
|
+
}, (t) => [
|
|
17527
|
+
unique("gateway_peer_allowlist_unique").on(t.bindingId, t.peerId),
|
|
17528
|
+
index("idx_gateway_peer_ws").on(t.workspaceId, t.bindingId)
|
|
17529
|
+
]);
|
|
17530
|
+
var gatewayIngressDedupe = sqliteTable("gateway_ingress_dedupe", {
|
|
17531
|
+
id: text("id").primaryKey().$defaultFn(() => "gid_" + nanoid3()),
|
|
17532
|
+
workspaceId: text("workspace_id").notNull().references(() => workspace.id, { onDelete: "cascade" }),
|
|
17533
|
+
provider: text("provider").notNull(),
|
|
17534
|
+
externalMessageId: text("external_message_id").notNull(),
|
|
17535
|
+
conversationId: text("conversation_id"),
|
|
17536
|
+
messageId: text("message_id"),
|
|
17537
|
+
createdAt: text("created_at").notNull().$defaultFn(() => new Date().toISOString())
|
|
17538
|
+
}, (t) => [
|
|
17539
|
+
unique("gateway_ingress_dedupe_unique").on(t.provider, t.externalMessageId),
|
|
17540
|
+
index("idx_gateway_ingress_dedupe_ws").on(t.workspaceId, t.provider)
|
|
17541
|
+
]);
|
|
17208
17542
|
// ../shared/src/utils/title.ts
|
|
17209
17543
|
var graphemeSegmenter = typeof Intl !== "undefined" && typeof Intl.Segmenter === "function" ? new Intl.Segmenter(undefined, { granularity: "grapheme" }) : null;
|
|
17210
17544
|
// ../shared/src/db/queries/task.ts
|
|
@@ -17328,6 +17662,479 @@ var RESERVED_HANDLES = new Set([
|
|
|
17328
17662
|
"system",
|
|
17329
17663
|
"phneakngar"
|
|
17330
17664
|
]);
|
|
17665
|
+
// ../shared/src/db/queries/issue.ts
|
|
17666
|
+
var ACTIVE_STATUSES = [...ACTIVE_ISSUE_STATUSES];
|
|
17667
|
+
// ../shared/src/db/queries/conversation-member.ts
|
|
17668
|
+
var conversationMemberUniqueTarget = [
|
|
17669
|
+
conversationMember.conversationId,
|
|
17670
|
+
conversationMember.memberType,
|
|
17671
|
+
conversationMember.memberId
|
|
17672
|
+
];
|
|
17673
|
+
// ../shared/src/lib/memory-compact.ts
|
|
17674
|
+
var MEMORY_SUMMARY_KIND = MemoryKind.SUMMARY;
|
|
17675
|
+
// ../shared/src/lib/approval-policy.ts
|
|
17676
|
+
var ToolClass = {
|
|
17677
|
+
READ: "read",
|
|
17678
|
+
SEARCH: "search",
|
|
17679
|
+
WRITE: "write",
|
|
17680
|
+
SHELL: "shell",
|
|
17681
|
+
NETWORK: "network",
|
|
17682
|
+
OUTBOUND_EMAIL: "outbound_email",
|
|
17683
|
+
TOOL_WRITEBACK: "tool_writeback",
|
|
17684
|
+
SKILL_INSTALL: "skill_install",
|
|
17685
|
+
AUTOMATION_PROMOTE: "automation_promote",
|
|
17686
|
+
UNKNOWN: "unknown"
|
|
17687
|
+
};
|
|
17688
|
+
var HIGH_STAKES_TOOL_CLASSES = new Set([
|
|
17689
|
+
ToolClass.WRITE,
|
|
17690
|
+
ToolClass.SHELL,
|
|
17691
|
+
ToolClass.NETWORK,
|
|
17692
|
+
ToolClass.OUTBOUND_EMAIL,
|
|
17693
|
+
ToolClass.TOOL_WRITEBACK,
|
|
17694
|
+
ToolClass.SKILL_INSTALL,
|
|
17695
|
+
ToolClass.AUTOMATION_PROMOTE
|
|
17696
|
+
]);
|
|
17697
|
+
var LOW_STAKES_TOOL_CLASSES = new Set([
|
|
17698
|
+
ToolClass.READ,
|
|
17699
|
+
ToolClass.SEARCH
|
|
17700
|
+
]);
|
|
17701
|
+
var TOOL_CLASS_VALUES = new Set(Object.values(ToolClass));
|
|
17702
|
+
var TOOL_NAME_TO_CLASS = {
|
|
17703
|
+
read: ToolClass.READ,
|
|
17704
|
+
read_file: ToolClass.READ,
|
|
17705
|
+
readdir: ToolClass.READ,
|
|
17706
|
+
cat: ToolClass.READ,
|
|
17707
|
+
ls: ToolClass.READ,
|
|
17708
|
+
list: ToolClass.READ,
|
|
17709
|
+
glob: ToolClass.SEARCH,
|
|
17710
|
+
grep: ToolClass.SEARCH,
|
|
17711
|
+
search: ToolClass.SEARCH,
|
|
17712
|
+
websearch: ToolClass.SEARCH,
|
|
17713
|
+
web_search: ToolClass.SEARCH,
|
|
17714
|
+
webfetch: ToolClass.READ,
|
|
17715
|
+
web_fetch: ToolClass.READ,
|
|
17716
|
+
web_cache: ToolClass.READ,
|
|
17717
|
+
web_cache_search: ToolClass.SEARCH,
|
|
17718
|
+
web_extract: ToolClass.READ,
|
|
17719
|
+
web_crawl: ToolClass.SEARCH,
|
|
17720
|
+
web_diff: ToolClass.READ,
|
|
17721
|
+
mcp_web_search: ToolClass.SEARCH,
|
|
17722
|
+
mcp_web_fetch: ToolClass.READ,
|
|
17723
|
+
mcp_web_extract: ToolClass.READ,
|
|
17724
|
+
mcp_web_crawl: ToolClass.SEARCH,
|
|
17725
|
+
mcp_web_diff: ToolClass.READ,
|
|
17726
|
+
phneakngar_web_search: ToolClass.SEARCH,
|
|
17727
|
+
phneakngar_web_fetch: ToolClass.READ,
|
|
17728
|
+
phneakngar_web_extract: ToolClass.READ,
|
|
17729
|
+
phneakngar_web_crawl: ToolClass.SEARCH,
|
|
17730
|
+
phneakngar_web_diff: ToolClass.READ,
|
|
17731
|
+
get: ToolClass.READ,
|
|
17732
|
+
fetch: ToolClass.READ,
|
|
17733
|
+
write: ToolClass.WRITE,
|
|
17734
|
+
write_file: ToolClass.WRITE,
|
|
17735
|
+
edit: ToolClass.WRITE,
|
|
17736
|
+
edit_file: ToolClass.WRITE,
|
|
17737
|
+
multiedit: ToolClass.WRITE,
|
|
17738
|
+
delete: ToolClass.WRITE,
|
|
17739
|
+
delete_file: ToolClass.WRITE,
|
|
17740
|
+
apply_patch: ToolClass.WRITE,
|
|
17741
|
+
bash: ToolClass.SHELL,
|
|
17742
|
+
shell: ToolClass.SHELL,
|
|
17743
|
+
terminal: ToolClass.SHELL,
|
|
17744
|
+
run_terminal_cmd: ToolClass.SHELL,
|
|
17745
|
+
execute: ToolClass.SHELL,
|
|
17746
|
+
http: ToolClass.NETWORK,
|
|
17747
|
+
http_request: ToolClass.NETWORK,
|
|
17748
|
+
curl: ToolClass.NETWORK,
|
|
17749
|
+
fetch_post: ToolClass.NETWORK,
|
|
17750
|
+
send_email: ToolClass.OUTBOUND_EMAIL,
|
|
17751
|
+
outbound_email: ToolClass.OUTBOUND_EMAIL,
|
|
17752
|
+
email_send: ToolClass.OUTBOUND_EMAIL,
|
|
17753
|
+
github_create_issue: ToolClass.TOOL_WRITEBACK,
|
|
17754
|
+
github_comment: ToolClass.TOOL_WRITEBACK,
|
|
17755
|
+
github_create_pr: ToolClass.TOOL_WRITEBACK,
|
|
17756
|
+
linear_create_issue: ToolClass.TOOL_WRITEBACK,
|
|
17757
|
+
linear_comment: ToolClass.TOOL_WRITEBACK,
|
|
17758
|
+
tool_writeback: ToolClass.TOOL_WRITEBACK,
|
|
17759
|
+
skill_install: ToolClass.SKILL_INSTALL,
|
|
17760
|
+
install_skill: ToolClass.SKILL_INSTALL,
|
|
17761
|
+
automation_promote: ToolClass.AUTOMATION_PROMOTE,
|
|
17762
|
+
promote_automation: ToolClass.AUTOMATION_PROMOTE
|
|
17763
|
+
};
|
|
17764
|
+
var TOOL_CLASS_TO_APPROVAL_KIND = {
|
|
17765
|
+
[ToolClass.OUTBOUND_EMAIL]: ApprovalKind.OUTBOUND_EMAIL,
|
|
17766
|
+
[ToolClass.TOOL_WRITEBACK]: ApprovalKind.TOOL_ACTION,
|
|
17767
|
+
[ToolClass.WRITE]: ApprovalKind.TOOL_ACTION,
|
|
17768
|
+
[ToolClass.SHELL]: ApprovalKind.TOOL_ACTION,
|
|
17769
|
+
[ToolClass.NETWORK]: ApprovalKind.TOOL_ACTION,
|
|
17770
|
+
[ToolClass.SKILL_INSTALL]: ApprovalKind.SKILL_INSTALL,
|
|
17771
|
+
[ToolClass.AUTOMATION_PROMOTE]: ApprovalKind.AUTOMATION_PROMOTE
|
|
17772
|
+
};
|
|
17773
|
+
var READ_ONLY_SHELL_HEAD = /^(?:ls|pwd|cat|head|tail|echo|which|type|env|printenv|date|whoami|id|uname|file|stat|wc|true|false|basename|dirname|realpath|readlink)\b/;
|
|
17774
|
+
function normalizeName(value) {
|
|
17775
|
+
return (value ?? "").trim().toLowerCase();
|
|
17776
|
+
}
|
|
17777
|
+
function asRecord(value) {
|
|
17778
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
17779
|
+
return null;
|
|
17780
|
+
return value;
|
|
17781
|
+
}
|
|
17782
|
+
function extractCommandFromInput(input) {
|
|
17783
|
+
if (typeof input === "string") {
|
|
17784
|
+
const t = input.trim();
|
|
17785
|
+
return t || null;
|
|
17786
|
+
}
|
|
17787
|
+
const rec = asRecord(input);
|
|
17788
|
+
if (!rec)
|
|
17789
|
+
return null;
|
|
17790
|
+
for (const key of ["command", "cmd", "script", "code"]) {
|
|
17791
|
+
const v = rec[key];
|
|
17792
|
+
if (typeof v === "string" && v.trim())
|
|
17793
|
+
return v.trim();
|
|
17794
|
+
}
|
|
17795
|
+
return null;
|
|
17796
|
+
}
|
|
17797
|
+
function classifyToolName(toolName) {
|
|
17798
|
+
const key = normalizeName(toolName);
|
|
17799
|
+
if (!key)
|
|
17800
|
+
return ToolClass.UNKNOWN;
|
|
17801
|
+
if (TOOL_NAME_TO_CLASS[key])
|
|
17802
|
+
return TOOL_NAME_TO_CLASS[key];
|
|
17803
|
+
if (/(^|[_-])(send[_-]?email|outbound[_-]?email)([_-]|$)/.test(key)) {
|
|
17804
|
+
return ToolClass.OUTBOUND_EMAIL;
|
|
17805
|
+
}
|
|
17806
|
+
if (/(github|linear|jira|notion|slack[_-]?post)/.test(key) && /(create|write|comment|update|delete|post|merge)/.test(key)) {
|
|
17807
|
+
return ToolClass.TOOL_WRITEBACK;
|
|
17808
|
+
}
|
|
17809
|
+
if (/(skill).*(install)|(^|[_-])install[_-]?skill([_-]|$)/.test(key)) {
|
|
17810
|
+
return ToolClass.SKILL_INSTALL;
|
|
17811
|
+
}
|
|
17812
|
+
if (/(^|[_-])(bash|shell|terminal)([_-]|$)/.test(key)) {
|
|
17813
|
+
return ToolClass.SHELL;
|
|
17814
|
+
}
|
|
17815
|
+
if (/(^|[_-])(write|edit|delete|apply[_-]?patch)([_-]|$)/.test(key)) {
|
|
17816
|
+
return ToolClass.WRITE;
|
|
17817
|
+
}
|
|
17818
|
+
if (/(^|[_-])(read|cat|ls|glob|grep|search)([_-]|$)/.test(key)) {
|
|
17819
|
+
return key.includes("search") || key.includes("grep") || key.includes("glob") ? ToolClass.SEARCH : ToolClass.READ;
|
|
17820
|
+
}
|
|
17821
|
+
if (/(web[_-]?(search|fetch|cache|extract|crawl|diff)|wigolo__(search|fetch|crawl|extract|diff))/.test(key)) {
|
|
17822
|
+
return key.includes("search") || key.includes("crawl") ? ToolClass.SEARCH : ToolClass.READ;
|
|
17823
|
+
}
|
|
17824
|
+
return ToolClass.UNKNOWN;
|
|
17825
|
+
}
|
|
17826
|
+
function normalizeToolClass(toolClass) {
|
|
17827
|
+
const key = normalizeName(toolClass);
|
|
17828
|
+
if (TOOL_CLASS_VALUES.has(key))
|
|
17829
|
+
return key;
|
|
17830
|
+
return ToolClass.UNKNOWN;
|
|
17831
|
+
}
|
|
17832
|
+
function mapToolClassToApprovalKind(toolClass) {
|
|
17833
|
+
return TOOL_CLASS_TO_APPROVAL_KIND[toolClass] ?? null;
|
|
17834
|
+
}
|
|
17835
|
+
function isHighStakesToolClass(toolClass) {
|
|
17836
|
+
return HIGH_STAKES_TOOL_CLASSES.has(toolClass);
|
|
17837
|
+
}
|
|
17838
|
+
function isToolAllowListed(toolName, allowList) {
|
|
17839
|
+
if (!allowList || allowList.length === 0)
|
|
17840
|
+
return false;
|
|
17841
|
+
const key = normalizeName(toolName);
|
|
17842
|
+
if (!key)
|
|
17843
|
+
return false;
|
|
17844
|
+
return allowList.some((entry) => normalizeName(entry) === key);
|
|
17845
|
+
}
|
|
17846
|
+
function maybeDowngradeShellClass(toolClass, input) {
|
|
17847
|
+
if (toolClass !== ToolClass.SHELL)
|
|
17848
|
+
return toolClass;
|
|
17849
|
+
const cmd = extractCommandFromInput(input);
|
|
17850
|
+
if (!cmd)
|
|
17851
|
+
return toolClass;
|
|
17852
|
+
if (/[|;&><`$]/.test(cmd) || /\n/.test(cmd))
|
|
17853
|
+
return toolClass;
|
|
17854
|
+
if (READ_ONLY_SHELL_HEAD.test(cmd))
|
|
17855
|
+
return ToolClass.READ;
|
|
17856
|
+
return toolClass;
|
|
17857
|
+
}
|
|
17858
|
+
function classFromKind(kind) {
|
|
17859
|
+
const k = normalizeName(kind);
|
|
17860
|
+
if (!k)
|
|
17861
|
+
return null;
|
|
17862
|
+
if (k === ApprovalKind.OUTBOUND_EMAIL)
|
|
17863
|
+
return ToolClass.OUTBOUND_EMAIL;
|
|
17864
|
+
if (k === ApprovalKind.SKILL_INSTALL)
|
|
17865
|
+
return ToolClass.SKILL_INSTALL;
|
|
17866
|
+
if (k === ApprovalKind.AUTOMATION_PROMOTE)
|
|
17867
|
+
return ToolClass.AUTOMATION_PROMOTE;
|
|
17868
|
+
if (k === ApprovalKind.TOOL_ACTION)
|
|
17869
|
+
return ToolClass.TOOL_WRITEBACK;
|
|
17870
|
+
return null;
|
|
17871
|
+
}
|
|
17872
|
+
function evaluateApprovalPolicy(input = {}) {
|
|
17873
|
+
let toolClass = ToolClass.UNKNOWN;
|
|
17874
|
+
if (input.toolClass) {
|
|
17875
|
+
const normalized = normalizeToolClass(input.toolClass);
|
|
17876
|
+
toolClass = normalized !== ToolClass.UNKNOWN ? normalized : classifyToolName(input.toolClass);
|
|
17877
|
+
} else if (input.kind) {
|
|
17878
|
+
toolClass = classFromKind(input.kind) ?? ToolClass.UNKNOWN;
|
|
17879
|
+
} else if (input.toolName) {
|
|
17880
|
+
toolClass = classifyToolName(input.toolName);
|
|
17881
|
+
}
|
|
17882
|
+
toolClass = maybeDowngradeShellClass(toolClass, input.input);
|
|
17883
|
+
const allowListed = isToolAllowListed(input.toolName, input.allowList);
|
|
17884
|
+
const lowStakes = LOW_STAKES_TOOL_CLASSES.has(toolClass);
|
|
17885
|
+
const approvalKind = mapToolClassToApprovalKind(toolClass);
|
|
17886
|
+
if (input.forceAllow) {
|
|
17887
|
+
return {
|
|
17888
|
+
requiresApproval: false,
|
|
17889
|
+
toolClass,
|
|
17890
|
+
approvalKind: null,
|
|
17891
|
+
reason: "forceAllow",
|
|
17892
|
+
lowStakes,
|
|
17893
|
+
allowListed
|
|
17894
|
+
};
|
|
17895
|
+
}
|
|
17896
|
+
if (input.forceRequire) {
|
|
17897
|
+
return {
|
|
17898
|
+
requiresApproval: true,
|
|
17899
|
+
toolClass,
|
|
17900
|
+
approvalKind: approvalKind ?? ApprovalKind.TOOL_ACTION,
|
|
17901
|
+
reason: "forceRequire",
|
|
17902
|
+
lowStakes,
|
|
17903
|
+
allowListed
|
|
17904
|
+
};
|
|
17905
|
+
}
|
|
17906
|
+
if (allowListed) {
|
|
17907
|
+
return {
|
|
17908
|
+
requiresApproval: false,
|
|
17909
|
+
toolClass,
|
|
17910
|
+
approvalKind: null,
|
|
17911
|
+
reason: "allowList",
|
|
17912
|
+
lowStakes,
|
|
17913
|
+
allowListed: true
|
|
17914
|
+
};
|
|
17915
|
+
}
|
|
17916
|
+
if (isHighStakesToolClass(toolClass)) {
|
|
17917
|
+
return {
|
|
17918
|
+
requiresApproval: true,
|
|
17919
|
+
toolClass,
|
|
17920
|
+
approvalKind: approvalKind ?? ApprovalKind.TOOL_ACTION,
|
|
17921
|
+
reason: `high_stakes:${toolClass}`,
|
|
17922
|
+
lowStakes: false,
|
|
17923
|
+
allowListed: false
|
|
17924
|
+
};
|
|
17925
|
+
}
|
|
17926
|
+
return {
|
|
17927
|
+
requiresApproval: false,
|
|
17928
|
+
toolClass,
|
|
17929
|
+
approvalKind: null,
|
|
17930
|
+
reason: lowStakes ? `low_stakes:${toolClass}` : `default_allow:${toolClass}`,
|
|
17931
|
+
lowStakes,
|
|
17932
|
+
allowListed: false
|
|
17933
|
+
};
|
|
17934
|
+
}
|
|
17935
|
+
function extractToolPermissionRequest(payload) {
|
|
17936
|
+
const rec = asRecord(payload);
|
|
17937
|
+
if (!rec)
|
|
17938
|
+
return { toolName: null, input: undefined };
|
|
17939
|
+
const toolNameRaw = rec.tool_name ?? rec.toolName ?? rec.name ?? rec.tool ?? null;
|
|
17940
|
+
const toolName = typeof toolNameRaw === "string" && toolNameRaw.trim() ? toolNameRaw.trim() : null;
|
|
17941
|
+
let input = rec.input ?? rec.tool_input ?? rec.args ?? undefined;
|
|
17942
|
+
if (typeof input === "string") {
|
|
17943
|
+
try {
|
|
17944
|
+
input = JSON.parse(input);
|
|
17945
|
+
} catch {}
|
|
17946
|
+
}
|
|
17947
|
+
return { toolName, input };
|
|
17948
|
+
}
|
|
17949
|
+
function gateToolPermission(payload, opts) {
|
|
17950
|
+
const { toolName, input } = extractToolPermissionRequest(payload);
|
|
17951
|
+
const policy = evaluateApprovalPolicy({
|
|
17952
|
+
toolName,
|
|
17953
|
+
input,
|
|
17954
|
+
allowList: opts?.allowList,
|
|
17955
|
+
forceRequire: opts?.forceRequire,
|
|
17956
|
+
forceAllow: opts?.forceAllow,
|
|
17957
|
+
toolClass: opts?.toolClass,
|
|
17958
|
+
kind: opts?.kind
|
|
17959
|
+
});
|
|
17960
|
+
return {
|
|
17961
|
+
behavior: policy.requiresApproval ? "deny" : "allow",
|
|
17962
|
+
requiresApproval: policy.requiresApproval,
|
|
17963
|
+
policy,
|
|
17964
|
+
toolName
|
|
17965
|
+
};
|
|
17966
|
+
}
|
|
17967
|
+
// ../shared/src/lib/delivery-artifact.ts
|
|
17968
|
+
var KIND_VALUES = new Set(Object.values(DeliveryArtifactKind));
|
|
17969
|
+
// ../shared/src/lib/judgment-policy.ts
|
|
17970
|
+
var DEFAULT_JUDGMENT_POLICY = {
|
|
17971
|
+
ambiguousToIssue: false
|
|
17972
|
+
};
|
|
17973
|
+
var MAX_TITLE = 200;
|
|
17974
|
+
var MAX_DESCRIPTION = 20000;
|
|
17975
|
+
function asRecord2(value) {
|
|
17976
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
17977
|
+
}
|
|
17978
|
+
function firstLine(text2) {
|
|
17979
|
+
return text2.split(/\r?\n/)[0]?.trim() ?? "";
|
|
17980
|
+
}
|
|
17981
|
+
function clamp(text2, max) {
|
|
17982
|
+
if (text2.length <= max)
|
|
17983
|
+
return text2;
|
|
17984
|
+
return `${text2.slice(0, max - 1).trimEnd()}…`;
|
|
17985
|
+
}
|
|
17986
|
+
function readJudgmentPolicy(runtimeConfig) {
|
|
17987
|
+
const config2 = asRecord2(runtimeConfig);
|
|
17988
|
+
const judgment = asRecord2(config2?.judgment);
|
|
17989
|
+
return {
|
|
17990
|
+
ambiguousToIssue: judgment?.ambiguousToIssue === true || judgment?.ambiguous_to_issue === true
|
|
17991
|
+
};
|
|
17992
|
+
}
|
|
17993
|
+
var CONCRETE_SIGNAL = /\b(create|implement|deploy|refactor|rename|merge|delete|schedule|send|email|file|open pr|pull request|pr\s*#?\d+|issue\s*#?\d+|fix\s+\S+|write\s+\S+|add\s+\S+\s+to)\b/i;
|
|
17994
|
+
var PATH_OR_CODE = /`[^`]+`|\b[\w.-]+\.[a-z]{1,5}\b|\/[\w./-]+/i;
|
|
17995
|
+
var AMBIGUOUS_PHRASES = /\b(not sure|unclear|ambiguous|someone|anyone|whoever|whoever owns|figure (it|this) out|look into (this|it)|handle this|can you help|help me(?: with this)?|what (should|do) we|what do you think|idk|tbd|maybe|or whatever)\b/i;
|
|
17996
|
+
var VAGUE_SHORT = /^(?:pls|please)?\s*(?:help|fix|check|review|look(?:\s+at)?|handle|do)\s*(?:this|it|please)?[.!?…]*$/i;
|
|
17997
|
+
function isAmbiguousRequest(prompt) {
|
|
17998
|
+
const text2 = prompt.replace(/\s+/g, " ").trim();
|
|
17999
|
+
if (!text2)
|
|
18000
|
+
return true;
|
|
18001
|
+
const hasConcrete = CONCRETE_SIGNAL.test(text2) || PATH_OR_CODE.test(text2);
|
|
18002
|
+
const hasAmbiguousPhrase = AMBIGUOUS_PHRASES.test(text2);
|
|
18003
|
+
const endsWithQuestion = /\?\s*$/.test(text2);
|
|
18004
|
+
const isVagueShort = text2.length <= 48 && VAGUE_SHORT.test(text2);
|
|
18005
|
+
if (hasConcrete && !hasAmbiguousPhrase)
|
|
18006
|
+
return false;
|
|
18007
|
+
if (hasAmbiguousPhrase)
|
|
18008
|
+
return true;
|
|
18009
|
+
if (isVagueShort)
|
|
18010
|
+
return true;
|
|
18011
|
+
if (endsWithQuestion && text2.length < 100 && !hasConcrete)
|
|
18012
|
+
return true;
|
|
18013
|
+
if (text2.length < 28 && !hasConcrete)
|
|
18014
|
+
return true;
|
|
18015
|
+
return false;
|
|
18016
|
+
}
|
|
18017
|
+
function buildAmbiguousIssueDraft(input) {
|
|
18018
|
+
const headline = firstLine(input.prompt) || input.prompt.replace(/\s+/g, " ").trim() || "(empty request)";
|
|
18019
|
+
const prompt = input.prompt.replace(/\s+/g, " ").trim() || "(empty request)";
|
|
18020
|
+
const title = clamp(/^clarify:/i.test(headline) ? headline : `Clarify: ${headline}`, MAX_TITLE);
|
|
18021
|
+
const parts = [
|
|
18022
|
+
"Escalated from an ambiguous DM under judgment policy (ambiguous → create issue).",
|
|
18023
|
+
input.senderName ? `Requester: ${input.senderName}` : null,
|
|
18024
|
+
input.conversationId ? `Conversation: ${input.conversationId}` : null,
|
|
18025
|
+
"",
|
|
18026
|
+
"Original request:",
|
|
18027
|
+
prompt,
|
|
18028
|
+
"",
|
|
18029
|
+
"Next step: clarify owner, desired outcome, and acceptance criteria before executing."
|
|
18030
|
+
].filter((p) => p !== null);
|
|
18031
|
+
return {
|
|
18032
|
+
agent_id: input.agentId,
|
|
18033
|
+
title,
|
|
18034
|
+
description: clamp(parts.join(`
|
|
18035
|
+
`), MAX_DESCRIPTION)
|
|
18036
|
+
};
|
|
18037
|
+
}
|
|
18038
|
+
function resolveAmbiguousDmJudgment(input) {
|
|
18039
|
+
const policy = input.policy ?? (input.runtimeConfig !== undefined ? readJudgmentPolicy(input.runtimeConfig) : DEFAULT_JUDGMENT_POLICY);
|
|
18040
|
+
if (!policy.ambiguousToIssue) {
|
|
18041
|
+
return {
|
|
18042
|
+
action: "continue",
|
|
18043
|
+
reason: "judgment policy ambiguousToIssue is disabled"
|
|
18044
|
+
};
|
|
18045
|
+
}
|
|
18046
|
+
if (!input.agentId.trim()) {
|
|
18047
|
+
return {
|
|
18048
|
+
action: "continue",
|
|
18049
|
+
reason: "missing agent id for issue ownership"
|
|
18050
|
+
};
|
|
18051
|
+
}
|
|
18052
|
+
if (!isAmbiguousRequest(input.prompt)) {
|
|
18053
|
+
return {
|
|
18054
|
+
action: "continue",
|
|
18055
|
+
reason: "request has a clear enough outcome or target"
|
|
18056
|
+
};
|
|
18057
|
+
}
|
|
18058
|
+
return {
|
|
18059
|
+
action: "create_issue",
|
|
18060
|
+
issue: buildAmbiguousIssueDraft({
|
|
18061
|
+
prompt: input.prompt,
|
|
18062
|
+
agentId: input.agentId,
|
|
18063
|
+
senderName: input.senderName,
|
|
18064
|
+
conversationId: input.conversationId
|
|
18065
|
+
}),
|
|
18066
|
+
reason: "request is ambiguous; create owned issue instead of freeform chat only"
|
|
18067
|
+
};
|
|
18068
|
+
}
|
|
18069
|
+
function buildJudgmentPolicyNotice(settings) {
|
|
18070
|
+
if (!settings.ambiguousToIssue)
|
|
18071
|
+
return null;
|
|
18072
|
+
return "Judgment policy ENABLED: ambiguous → create issue. " + "If the user's request lacks a clear owner, deliverable, or outcome, " + "do NOT spin freeform chat. Create an owned issue with " + '`phneakngar issue create --title "…" --description "…"` ' + "(defaults to you as the agent owner), then `phneakngar sync send-dm` " + "to tell the user you filed it and what you need clarified. " + "When the request is already clear, execute normally.";
|
|
18073
|
+
}
|
|
18074
|
+
function buildJudgmentPolicyContextBlock(settings, cmdPrefix = "phneakngar") {
|
|
18075
|
+
if (!settings.ambiguousToIssue)
|
|
18076
|
+
return null;
|
|
18077
|
+
return `### Judgment policy (ambiguous → issue)
|
|
18078
|
+
This agent has **ambiguous → create issue** enabled.
|
|
18079
|
+
|
|
18080
|
+
When a user DM is vague, missing an owner, or has no clear outcome:
|
|
18081
|
+
1. Create an owned issue: \`${cmdPrefix} issue create --title "<short clarify title>" --description "<original request + what is unclear>"\`
|
|
18082
|
+
2. You are the default owner — track it with \`${cmdPrefix} issue update\` / \`${cmdPrefix} issue comment\` as work progresses.
|
|
18083
|
+
3. Tell the user via \`${cmdPrefix} sync send-dm\` that you filed the issue and what you need from them.
|
|
18084
|
+
4. Prefer one owned issue over a long freeform chat loop when the ask is ambiguous.
|
|
18085
|
+
|
|
18086
|
+
When the request already names a clear deliverable or target, skip issue creation and execute normally.
|
|
18087
|
+
`;
|
|
18088
|
+
}
|
|
18089
|
+
// ../shared/src/lib/approval-hold-policy.ts
|
|
18090
|
+
var DEFAULT_APPROVAL_HOLD = {
|
|
18091
|
+
enabled: true
|
|
18092
|
+
};
|
|
18093
|
+
function asRecord3(value) {
|
|
18094
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
18095
|
+
}
|
|
18096
|
+
function truthyFlag(v) {
|
|
18097
|
+
return v === true || v === 1 || v === "1" || v === "true" || v === "yes" || v === "on";
|
|
18098
|
+
}
|
|
18099
|
+
function falsyFlag(v) {
|
|
18100
|
+
return v === false || v === 0 || v === "0" || v === "false" || v === "no" || v === "off";
|
|
18101
|
+
}
|
|
18102
|
+
function readApprovalHoldPolicy(runtimeConfig) {
|
|
18103
|
+
const config2 = asRecord3(runtimeConfig);
|
|
18104
|
+
const hold = asRecord3(config2?.approvalHold) ?? asRecord3(config2?.approval_hold) ?? null;
|
|
18105
|
+
if (!hold)
|
|
18106
|
+
return { ...DEFAULT_APPROVAL_HOLD };
|
|
18107
|
+
if (falsyFlag(hold.enabled))
|
|
18108
|
+
return { enabled: false };
|
|
18109
|
+
if (truthyFlag(hold.enabled))
|
|
18110
|
+
return { enabled: true };
|
|
18111
|
+
return { ...DEFAULT_APPROVAL_HOLD };
|
|
18112
|
+
}
|
|
18113
|
+
function resolveApprovalHoldEnabled(input = {}) {
|
|
18114
|
+
const env = input.env ?? {};
|
|
18115
|
+
const raw = (env.CHHLAT_APPROVAL_HOLD ?? env.PHNEAKNGAR_APPROVAL_HOLD ?? "").toString().trim().toLowerCase();
|
|
18116
|
+
if (raw === "0" || raw === "false" || raw === "no" || raw === "off") {
|
|
18117
|
+
return false;
|
|
18118
|
+
}
|
|
18119
|
+
if (raw === "1" || raw === "true" || raw === "yes" || raw === "on") {
|
|
18120
|
+
return true;
|
|
18121
|
+
}
|
|
18122
|
+
return readApprovalHoldPolicy(input.runtimeConfig).enabled;
|
|
18123
|
+
}
|
|
18124
|
+
// ../shared/src/utils/memory-prompt.ts
|
|
18125
|
+
var DEFAULT_AGENT_MEMORY_PROMPT_LIMIT = 12;
|
|
18126
|
+
function formatMemoryForPrompt(memories, opts) {
|
|
18127
|
+
const limit = opts?.limit ?? DEFAULT_AGENT_MEMORY_PROMPT_LIMIT;
|
|
18128
|
+
const items = memories.filter((m) => typeof m.content === "string" && m.content.trim().length > 0).slice(0, Math.max(0, limit));
|
|
18129
|
+
if (items.length === 0)
|
|
18130
|
+
return "";
|
|
18131
|
+
const lines = items.map((m) => {
|
|
18132
|
+
const kind = (m.kind || "fact").trim() || "fact";
|
|
18133
|
+
return `- [${kind}] ${m.content.trim()}`;
|
|
18134
|
+
});
|
|
18135
|
+
return ["Agent memory (apply when relevant):", ...lines].join(`
|
|
18136
|
+
`);
|
|
18137
|
+
}
|
|
17331
18138
|
// ../shared/src/ws-ticket.ts
|
|
17332
18139
|
var encoder = new TextEncoder;
|
|
17333
18140
|
// ../shared/src/mode.ts
|
|
@@ -17535,6 +18342,171 @@ class ChhlatClient {
|
|
|
17535
18342
|
syncSkills(token, body) {
|
|
17536
18343
|
return this.request("POST", "/api/chhlat/skills/sync", token, body);
|
|
17537
18344
|
}
|
|
18345
|
+
createToolApproval(token, body) {
|
|
18346
|
+
return this.request("POST", "/api/chhlat/approvals", token, body);
|
|
18347
|
+
}
|
|
18348
|
+
getToolApproval(token, approvalId) {
|
|
18349
|
+
return this.request("GET", `/api/chhlat/approvals/${encodeURIComponent(approvalId)}`, token);
|
|
18350
|
+
}
|
|
18351
|
+
}
|
|
18352
|
+
function makeClientToolActionApprovalCreator(opts) {
|
|
18353
|
+
return async (input) => {
|
|
18354
|
+
const res = await opts.client.createToolApproval(opts.token, {
|
|
18355
|
+
chhlat_id: opts.chhlatId,
|
|
18356
|
+
agent_id: opts.agentId ?? null,
|
|
18357
|
+
tool_name: input.toolName,
|
|
18358
|
+
tool_class: input.toolClass,
|
|
18359
|
+
request_id: input.requestId,
|
|
18360
|
+
title: input.toolName ? `Tool: ${input.toolName}` : `Tool class: ${input.toolClass}`,
|
|
18361
|
+
summary: input.policyReason,
|
|
18362
|
+
input: input.input,
|
|
18363
|
+
policy_reason: input.policyReason,
|
|
18364
|
+
kind: "tool_action"
|
|
18365
|
+
});
|
|
18366
|
+
const id = res?.approval?.id;
|
|
18367
|
+
if (typeof id === "string" && id.trim()) {
|
|
18368
|
+
return { approvalId: id.trim() };
|
|
18369
|
+
}
|
|
18370
|
+
return null;
|
|
18371
|
+
};
|
|
18372
|
+
}
|
|
18373
|
+
|
|
18374
|
+
// chhlat/tool-gate.ts
|
|
18375
|
+
var toolActionApprovalCreator = null;
|
|
18376
|
+
function setToolActionApprovalCreator(creator) {
|
|
18377
|
+
toolActionApprovalCreator = creator;
|
|
18378
|
+
}
|
|
18379
|
+
function decideToolGate(payload, opts = {}) {
|
|
18380
|
+
const { toolName } = extractToolPermissionRequest(payload);
|
|
18381
|
+
const toolClass = toolName != null ? undefined : opts.defaultToolClass ?? ToolClass.SHELL;
|
|
18382
|
+
return gateToolPermission(payload, {
|
|
18383
|
+
allowList: opts.allowList,
|
|
18384
|
+
forceRequire: opts.forceRequire,
|
|
18385
|
+
forceAllow: opts.forceAllow,
|
|
18386
|
+
toolClass
|
|
18387
|
+
});
|
|
18388
|
+
}
|
|
18389
|
+
function buildControlResponseLine(requestId, behavior, updatedInput, message2, extras) {
|
|
18390
|
+
const responseBody = behavior === "allow" ? {
|
|
18391
|
+
behavior: "allow",
|
|
18392
|
+
updatedInput
|
|
18393
|
+
} : {
|
|
18394
|
+
behavior: "deny",
|
|
18395
|
+
message: message2 ?? "This tool class requires human approval before it can run."
|
|
18396
|
+
};
|
|
18397
|
+
if (behavior === "deny" && extras?.approvalId) {
|
|
18398
|
+
responseBody.approval_id = extras.approvalId;
|
|
18399
|
+
}
|
|
18400
|
+
return JSON.stringify({
|
|
18401
|
+
type: "control_response",
|
|
18402
|
+
response: {
|
|
18403
|
+
subtype: "success",
|
|
18404
|
+
request_id: requestId,
|
|
18405
|
+
response: responseBody
|
|
18406
|
+
}
|
|
18407
|
+
});
|
|
18408
|
+
}
|
|
18409
|
+
function resolveUpdatedInput(payload) {
|
|
18410
|
+
const rec = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : null;
|
|
18411
|
+
if (!rec)
|
|
18412
|
+
return;
|
|
18413
|
+
const input = rec.input;
|
|
18414
|
+
if (typeof input === "string") {
|
|
18415
|
+
try {
|
|
18416
|
+
return JSON.parse(input);
|
|
18417
|
+
} catch {
|
|
18418
|
+
return input;
|
|
18419
|
+
}
|
|
18420
|
+
}
|
|
18421
|
+
if (input !== undefined)
|
|
18422
|
+
return input;
|
|
18423
|
+
return;
|
|
18424
|
+
}
|
|
18425
|
+
function buildRequiresApprovalDenyMessage(decision, approvalId) {
|
|
18426
|
+
const base = `Blocked by approval policy (${decision.policy.reason}). High-stakes tool class "${decision.policy.toolClass}" requires human approval.`;
|
|
18427
|
+
if (approvalId) {
|
|
18428
|
+
return `${base} Approval id: ${approvalId}.`;
|
|
18429
|
+
}
|
|
18430
|
+
return base;
|
|
18431
|
+
}
|
|
18432
|
+
function buildToolActionApprovalRequest(event, decision) {
|
|
18433
|
+
if (!decision.requiresApproval)
|
|
18434
|
+
return null;
|
|
18435
|
+
const requestId = event.request_id;
|
|
18436
|
+
if (typeof requestId !== "string" || !requestId.trim())
|
|
18437
|
+
return null;
|
|
18438
|
+
const { input } = extractToolPermissionRequest(event.payload);
|
|
18439
|
+
return {
|
|
18440
|
+
toolName: decision.toolName,
|
|
18441
|
+
toolClass: decision.policy.toolClass,
|
|
18442
|
+
requestId: requestId.trim(),
|
|
18443
|
+
input,
|
|
18444
|
+
policyReason: decision.policy.reason,
|
|
18445
|
+
approvalKind: ApprovalKind.TOOL_ACTION
|
|
18446
|
+
};
|
|
18447
|
+
}
|
|
18448
|
+
async function waitForApprovalDecision(approvalId, hold) {
|
|
18449
|
+
const timeoutMs = hold.timeoutMs ?? 120000;
|
|
18450
|
+
const intervalMs = hold.intervalMs ?? 2000;
|
|
18451
|
+
const sleep = hold.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
18452
|
+
const now = hold.now ?? (() => Date.now());
|
|
18453
|
+
const deadline = now() + timeoutMs;
|
|
18454
|
+
while (now() < deadline) {
|
|
18455
|
+
try {
|
|
18456
|
+
const row = await hold.poll(approvalId);
|
|
18457
|
+
const status = (row?.status ?? "").trim().toLowerCase();
|
|
18458
|
+
if (status === "approved")
|
|
18459
|
+
return "approved";
|
|
18460
|
+
if (status === "denied" || status === "rejected") {
|
|
18461
|
+
return status === "rejected" ? "rejected" : "denied";
|
|
18462
|
+
}
|
|
18463
|
+
} catch {}
|
|
18464
|
+
const remaining = deadline - now();
|
|
18465
|
+
if (remaining <= 0)
|
|
18466
|
+
break;
|
|
18467
|
+
await sleep(Math.min(intervalMs, remaining));
|
|
18468
|
+
}
|
|
18469
|
+
return "timeout";
|
|
18470
|
+
}
|
|
18471
|
+
async function handleToolControlRequestAsync(event, opts = {}, createApproval = toolActionApprovalCreator) {
|
|
18472
|
+
const requestId = event.request_id;
|
|
18473
|
+
if (!requestId)
|
|
18474
|
+
return null;
|
|
18475
|
+
const payload = event.payload;
|
|
18476
|
+
const decision = decideToolGate(payload, opts);
|
|
18477
|
+
const updatedInput = resolveUpdatedInput(payload);
|
|
18478
|
+
let approvalId = null;
|
|
18479
|
+
if (decision.requiresApproval && createApproval) {
|
|
18480
|
+
const draft = buildToolActionApprovalRequest(event, decision);
|
|
18481
|
+
if (draft) {
|
|
18482
|
+
try {
|
|
18483
|
+
const created = await createApproval(draft);
|
|
18484
|
+
if (created?.approvalId && typeof created.approvalId === "string") {
|
|
18485
|
+
approvalId = created.approvalId;
|
|
18486
|
+
}
|
|
18487
|
+
} catch {
|
|
18488
|
+
approvalId = null;
|
|
18489
|
+
}
|
|
18490
|
+
}
|
|
18491
|
+
}
|
|
18492
|
+
if (decision.requiresApproval && approvalId && opts.hold?.enabled) {
|
|
18493
|
+
const outcome = await waitForApprovalDecision(approvalId, opts.hold);
|
|
18494
|
+
if (outcome === "approved") {
|
|
18495
|
+
const allowLine = buildControlResponseLine(requestId, "allow", updatedInput);
|
|
18496
|
+
return {
|
|
18497
|
+
line: allowLine,
|
|
18498
|
+
decision: { ...decision, behavior: "allow", requiresApproval: false },
|
|
18499
|
+
approvalId
|
|
18500
|
+
};
|
|
18501
|
+
}
|
|
18502
|
+
const reason = outcome === "timeout" ? `Approval timed out (${opts.hold.timeoutMs ?? 120000}ms).` : `Approval ${outcome}.`;
|
|
18503
|
+
const message3 = `${buildRequiresApprovalDenyMessage(decision, approvalId)} ${reason}`;
|
|
18504
|
+
const line2 = buildControlResponseLine(requestId, "deny", updatedInput, message3, { approvalId });
|
|
18505
|
+
return { line: line2, decision, approvalId };
|
|
18506
|
+
}
|
|
18507
|
+
const message2 = decision.requiresApproval ? buildRequiresApprovalDenyMessage(decision, approvalId) : undefined;
|
|
18508
|
+
const line = buildControlResponseLine(requestId, decision.behavior, updatedInput, message2, { approvalId });
|
|
18509
|
+
return { line, decision, approvalId };
|
|
17538
18510
|
}
|
|
17539
18511
|
|
|
17540
18512
|
// chhlat/agent/claude.ts
|
|
@@ -17725,6 +18697,14 @@ async function killProcessTree(pid, opts) {
|
|
|
17725
18697
|
}
|
|
17726
18698
|
|
|
17727
18699
|
// chhlat/agent/claude.ts
|
|
18700
|
+
var approvalHoldPoller = null;
|
|
18701
|
+
var approvalHoldRuntimeConfig = undefined;
|
|
18702
|
+
function setApprovalHoldRuntimeConfig(runtimeConfig) {
|
|
18703
|
+
approvalHoldRuntimeConfig = runtimeConfig;
|
|
18704
|
+
}
|
|
18705
|
+
function setApprovalHoldPoller(poller) {
|
|
18706
|
+
approvalHoldPoller = poller;
|
|
18707
|
+
}
|
|
17728
18708
|
class ClaudeBackend {
|
|
17729
18709
|
cliPath;
|
|
17730
18710
|
name = "claude";
|
|
@@ -18166,40 +19146,30 @@ class ClaudeBackend {
|
|
|
18166
19146
|
return { pid: proc.pid, messages, parsedEvents, sessionId: sessionIdPromise, result: resultPromise, send, closeStdin, descriptor };
|
|
18167
19147
|
}
|
|
18168
19148
|
}
|
|
18169
|
-
function
|
|
18170
|
-
|
|
18171
|
-
|
|
18172
|
-
|
|
18173
|
-
let updatedInput = undefined;
|
|
18174
|
-
const payload = event.payload;
|
|
18175
|
-
if (payload) {
|
|
18176
|
-
const input = payload.input;
|
|
18177
|
-
if (typeof input === "string") {
|
|
18178
|
-
try {
|
|
18179
|
-
updatedInput = JSON.parse(input);
|
|
18180
|
-
} catch {
|
|
18181
|
-
updatedInput = input;
|
|
18182
|
-
}
|
|
18183
|
-
} else if (input !== undefined) {
|
|
18184
|
-
updatedInput = input;
|
|
18185
|
-
}
|
|
18186
|
-
}
|
|
18187
|
-
const approval = JSON.stringify({
|
|
18188
|
-
type: "control_response",
|
|
18189
|
-
response: {
|
|
18190
|
-
subtype: "success",
|
|
18191
|
-
request_id: requestId,
|
|
18192
|
-
response: {
|
|
18193
|
-
behavior: "allow",
|
|
18194
|
-
updatedInput
|
|
18195
|
-
}
|
|
18196
|
-
}
|
|
19149
|
+
function approvalHoldEnabledFromEnv(env = process.env) {
|
|
19150
|
+
return resolveApprovalHoldEnabled({
|
|
19151
|
+
runtimeConfig: approvalHoldRuntimeConfig,
|
|
19152
|
+
env
|
|
18197
19153
|
});
|
|
19154
|
+
}
|
|
19155
|
+
async function handleControlRequest(proc, event, enqueueStdinWrite) {
|
|
19156
|
+
const holdEnabled = approvalHoldEnabledFromEnv();
|
|
19157
|
+
const poller = approvalHoldPoller;
|
|
19158
|
+
const gated = await handleToolControlRequestAsync(event, {
|
|
19159
|
+
hold: holdEnabled && poller ? {
|
|
19160
|
+
enabled: true,
|
|
19161
|
+
poll: poller,
|
|
19162
|
+
timeoutMs: Number(process.env.CHHLAT_APPROVAL_HOLD_MS ?? 120000) || 120000,
|
|
19163
|
+
intervalMs: Number(process.env.CHHLAT_APPROVAL_HOLD_INTERVAL_MS ?? 2000) || 2000
|
|
19164
|
+
} : null
|
|
19165
|
+
});
|
|
19166
|
+
if (!gated)
|
|
19167
|
+
return;
|
|
18198
19168
|
if (enqueueStdinWrite) {
|
|
18199
|
-
enqueueStdinWrite(
|
|
19169
|
+
enqueueStdinWrite(gated.line);
|
|
18200
19170
|
} else {
|
|
18201
19171
|
try {
|
|
18202
|
-
proc.stdin?.write(
|
|
19172
|
+
proc.stdin?.write(gated.line + `
|
|
18203
19173
|
`);
|
|
18204
19174
|
} catch {}
|
|
18205
19175
|
}
|
|
@@ -19933,12 +20903,18 @@ The CLI auto-detects your identity from the environment. No need to pass \`--age
|
|
|
19933
20903
|
| Capability | Command |
|
|
19934
20904
|
|---|---|
|
|
19935
20905
|
| Send a message to the user | \`${cmdPrefix()} sync send-dm\` |
|
|
20906
|
+
| Create / update / claim issues | \`${cmdPrefix()} issue create\` (also list, show, update, comment, claim, handback) |
|
|
19936
20907
|
| Schedule / list / edit tasks | \`${cmdPrefix()} calendar set\` (also list, show, update, delete) |
|
|
19937
20908
|
| Upload a file for your owner | \`${cmdPrefix()} sync upload-artifact\` |
|
|
19938
20909
|
| Recruit a colleague agent | \`${cmdPrefix()} agent recruit\` |
|
|
19939
20910
|
|
|
19940
20911
|
Detailed usage for each capability follows below.
|
|
19941
20912
|
`;
|
|
20913
|
+
const judgmentBlock = buildJudgmentPolicyContextBlock(readJudgmentPolicy(task.agent?.runtimeConfig), cmdPrefix());
|
|
20914
|
+
if (judgmentBlock) {
|
|
20915
|
+
content += `
|
|
20916
|
+
${judgmentBlock}`;
|
|
20917
|
+
}
|
|
19942
20918
|
const emailLines = [];
|
|
19943
20919
|
if (phneakngarAddr)
|
|
19944
20920
|
emailLines.push(`- '${phneakngarAddr}' (default, ភ្នាក់ងារ platform address)`);
|
|
@@ -21056,7 +22032,7 @@ function configDir() {
|
|
|
21056
22032
|
var DEFAULT_PORT = 8799;
|
|
21057
22033
|
var MIN_PORT = 1024;
|
|
21058
22034
|
var MAX_PORT = 65535;
|
|
21059
|
-
function
|
|
22035
|
+
function asRecord4(value) {
|
|
21060
22036
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
21061
22037
|
}
|
|
21062
22038
|
function boolValue(value) {
|
|
@@ -21083,13 +22059,13 @@ function parsePort(value) {
|
|
|
21083
22059
|
return parsed;
|
|
21084
22060
|
}
|
|
21085
22061
|
function normalizeHeadroomRuntimeConfig(runtimeConfig, env = process.env) {
|
|
21086
|
-
const runtime =
|
|
21087
|
-
const headroom =
|
|
22062
|
+
const runtime = asRecord4(runtimeConfig);
|
|
22063
|
+
const headroom = asRecord4(runtime?.headroom);
|
|
21088
22064
|
const envOverride = envBoolOverride(env.PHNEAKNGAR_HEADROOM_ENABLED);
|
|
21089
22065
|
const enabled = envOverride ?? boolValue(headroom?.enabled);
|
|
21090
22066
|
const port = parsePort(env.PHNEAKNGAR_HEADROOM_PORT) ?? parsePort(headroom?.port) ?? parsePort(env.HEADROOM_PORT) ?? DEFAULT_PORT;
|
|
21091
22067
|
const upstream = enabled ? (() => {
|
|
21092
|
-
const upstreamRaw =
|
|
22068
|
+
const upstreamRaw = asRecord4(headroom?.upstream);
|
|
21093
22069
|
return upstreamRaw ? {
|
|
21094
22070
|
claude: typeof upstreamRaw.claude === "string" ? upstreamRaw.claude : undefined,
|
|
21095
22071
|
openai: typeof upstreamRaw.openai === "string" ? upstreamRaw.openai : undefined
|
|
@@ -21328,6 +22304,7 @@ async function prepareHeadroomForTask(task, provider, deps = {}) {
|
|
|
21328
22304
|
var DM_RESPONSE_NOTICE = "Reply with `phneakngar sync send-dm` — that's the only thing the user sees; your task output and reasoning are not shown." + " Talk to them at milestones like a colleague would, and don't end your turn without sending what they need." + " If this task will take more than 30 seconds, send a quick ack first so the user knows you're on it." + " IMPORTANT: If you were working on a previous task before this message arrived, do NOT silently drop it. After handling this message, return to any prior unfinished work and report the result to the user.";
|
|
21329
22305
|
var EMAIL_NOTICE = "This task was triggered by an incoming email. Reply to the sender via email — use the email sending tool to respond." + " If you need more information or confirmation, email them and then exit." + " Do not wait — when they reply, a new task will be triggered automatically and you will be woken up with their response." + " IMPORTANT: Do not let this email interrupt any task you were previously working on. After handling this email, return to your original task and make sure it reaches completion.";
|
|
21330
22306
|
var CALENDAR_NOTICE = "This task was triggered by a scheduled calendar event." + " If you need to communicate with someone, send an email using the email sending tool." + " If you need more information or confirmation, email them and then exit." + " Do not wait — when they reply, a new task will be triggered automatically and you will be woken up with their response.";
|
|
22307
|
+
var WEB_BRAIN_NOTICE = " Live web tools are available via the lean web brain:" + " prefer MCP tools web_search, web_fetch, web_extract, web_crawl, web_diff, web_cache when wired" + " (`phneakngar web wire-mcp`); else CLI `phneakngar web search|fetch|extract|crawl|diff`." + " Prefer real fetch for URLs; cite sources; do not invent page content." + " On blocked/SSRF/http errors, report the structured error and stop retry-spamming.";
|
|
21331
22308
|
var ISSUE_NOTICE = "This task was triggered by an assigned issue. The issue_id is provided in this message." + " Use `phneakngar issue show --issue_id <issue_id>` to read full context." + " Use `phneakngar issue update --issue_id <issue_id> --status <status>` to change status." + " Use `phneakngar issue comment --issue_id <issue_id> --body <text>` to leave a comment." + " CRITICAL — You MUST manage the issue status correctly. This is NOT optional:" + " 1. Set status to 'in_progress' when you start working." + " 2. If you complete the work yourself: leave a summary comment, then set status to 'review' as your last action. 'review' means there is actual completed work (code, artifact, result) ready for the owner to look at." + " 3. If you delegated work to colleagues and are waiting for their response: KEEP status as 'in_progress' and exit. This is expected — you will be woken up when they reply. Set 'review' only after all delegated work is confirmed complete." + " 4. NEVER set 'review' unless there is concrete completed work for the owner to review. Sending a plan to a colleague is NOT completed work." + " NEVER exit without doing at least one of: updating the status, or leaving a comment explaining what you did and what you're waiting for.";
|
|
21332
22309
|
function languagePolicyFor(task) {
|
|
21333
22310
|
return task.languagePolicy ?? buildAgentPromptLanguagePolicy({ taskLocaleOverride: task.localeOverride });
|
|
@@ -21342,10 +22319,23 @@ function buildTaskObject(task, attachments) {
|
|
|
21342
22319
|
type: task.type,
|
|
21343
22320
|
received_at: receivedAt,
|
|
21344
22321
|
instruction: task.prompt,
|
|
21345
|
-
language_policy: languagePolicyFor(task)
|
|
22322
|
+
language_policy: languagePolicyFor(task),
|
|
22323
|
+
web_brain: {
|
|
22324
|
+
available: true,
|
|
22325
|
+
provider: "phneakngar",
|
|
22326
|
+
tools: [
|
|
22327
|
+
"web_search",
|
|
22328
|
+
"web_fetch",
|
|
22329
|
+
"web_cache",
|
|
22330
|
+
"web_extract",
|
|
22331
|
+
"web_crawl",
|
|
22332
|
+
"web_diff"
|
|
22333
|
+
],
|
|
22334
|
+
notice: WEB_BRAIN_NOTICE.trim()
|
|
22335
|
+
}
|
|
21346
22336
|
};
|
|
21347
22337
|
if (task.type === "user_dm_message") {
|
|
21348
|
-
obj.notice = DM_RESPONSE_NOTICE;
|
|
22338
|
+
obj.notice = DM_RESPONSE_NOTICE + WEB_BRAIN_NOTICE;
|
|
21349
22339
|
const ctx = task.context;
|
|
21350
22340
|
if (ctx?.message_id) {
|
|
21351
22341
|
obj.message_id = ctx.message_id;
|
|
@@ -21360,14 +22350,35 @@ function buildTaskObject(task, attachments) {
|
|
|
21360
22350
|
...ctx.conversation_history ? { history: ctx.conversation_history } : {}
|
|
21361
22351
|
};
|
|
21362
22352
|
}
|
|
22353
|
+
const judgment = readJudgmentPolicy(task.agent?.runtimeConfig);
|
|
22354
|
+
const judgmentNotice = buildJudgmentPolicyNotice(judgment);
|
|
22355
|
+
if (judgmentNotice) {
|
|
22356
|
+
const decision = resolveAmbiguousDmJudgment({
|
|
22357
|
+
policy: judgment,
|
|
22358
|
+
prompt: task.prompt,
|
|
22359
|
+
agentId: task.agentId,
|
|
22360
|
+
senderName: task.sender?.name ?? null,
|
|
22361
|
+
conversationId: task.conversationId
|
|
22362
|
+
});
|
|
22363
|
+
obj.judgment_policy = {
|
|
22364
|
+
ambiguous_to_issue: true,
|
|
22365
|
+
guidance: judgmentNotice,
|
|
22366
|
+
recommended_action: decision.action,
|
|
22367
|
+
reason: decision.reason,
|
|
22368
|
+
...decision.action === "create_issue" ? {
|
|
22369
|
+
issue_draft: decision.issue
|
|
22370
|
+
} : {}
|
|
22371
|
+
};
|
|
22372
|
+
obj.notice = `${DM_RESPONSE_NOTICE}${WEB_BRAIN_NOTICE} ${judgmentNotice}`;
|
|
22373
|
+
}
|
|
21363
22374
|
}
|
|
21364
22375
|
if (task.type === "email_notification") {
|
|
21365
22376
|
const ctx = task.context;
|
|
21366
22377
|
const dmUser = ctx?.dmUser;
|
|
21367
22378
|
if (ctx?.conversationType === "user_dm_message" && dmUser) {
|
|
21368
|
-
obj.notice = buildEmailDmNotice(dmUser.name, dmUser.email);
|
|
22379
|
+
obj.notice = buildEmailDmNotice(dmUser.name, dmUser.email) + WEB_BRAIN_NOTICE;
|
|
21369
22380
|
} else {
|
|
21370
|
-
obj.notice = EMAIL_NOTICE;
|
|
22381
|
+
obj.notice = EMAIL_NOTICE + WEB_BRAIN_NOTICE;
|
|
21371
22382
|
}
|
|
21372
22383
|
if (ctx?.emailId != null) {
|
|
21373
22384
|
obj.email_id = ctx.emailId;
|
|
@@ -21402,6 +22413,29 @@ function buildTaskObject(task, attachments) {
|
|
|
21402
22413
|
obj.issue_id = ctx.issue_id;
|
|
21403
22414
|
}
|
|
21404
22415
|
}
|
|
22416
|
+
if (task.type === "automation_event") {
|
|
22417
|
+
const ctx = task.context;
|
|
22418
|
+
if (ctx?.automation_id != null) {
|
|
22419
|
+
obj.automation_id = ctx.automation_id;
|
|
22420
|
+
}
|
|
22421
|
+
if (ctx?.delivery_mode != null) {
|
|
22422
|
+
obj.delivery_mode = ctx.delivery_mode;
|
|
22423
|
+
}
|
|
22424
|
+
if (ctx?.skill_name != null) {
|
|
22425
|
+
obj.skill_name = ctx.skill_name;
|
|
22426
|
+
}
|
|
22427
|
+
}
|
|
22428
|
+
const memoryCtx = task.context;
|
|
22429
|
+
if (memoryCtx) {
|
|
22430
|
+
if (typeof memoryCtx.memory_prompt === "string" && memoryCtx.memory_prompt.trim()) {
|
|
22431
|
+
obj.memory = memoryCtx.memory_prompt;
|
|
22432
|
+
} else if (Array.isArray(memoryCtx.memories) && memoryCtx.memories.length > 0) {
|
|
22433
|
+
const items = memoryCtx.memories;
|
|
22434
|
+
const formatted = formatMemoryForPrompt(items);
|
|
22435
|
+
if (formatted)
|
|
22436
|
+
obj.memory = formatted;
|
|
22437
|
+
}
|
|
22438
|
+
}
|
|
21405
22439
|
if (task.sender) {
|
|
21406
22440
|
obj.sender = {
|
|
21407
22441
|
name: task.sender.name,
|
|
@@ -21500,6 +22534,18 @@ async function runSession(input) {
|
|
|
21500
22534
|
log6.info(`starting (task=${task.id}, type=${task.type}, agent=${task.agentId}, provider=${provider}, model=${model || "default"})`);
|
|
21501
22535
|
const client = new ChhlatClient(serverURL);
|
|
21502
22536
|
const backend = createBackend(provider, cliPath);
|
|
22537
|
+
setToolActionApprovalCreator(makeClientToolActionApprovalCreator({
|
|
22538
|
+
client,
|
|
22539
|
+
token,
|
|
22540
|
+
chhlatId: task.runtimeId ?? "",
|
|
22541
|
+
agentId: task.agentId
|
|
22542
|
+
}));
|
|
22543
|
+
setApprovalHoldRuntimeConfig(task.agent?.runtimeConfig);
|
|
22544
|
+
setApprovalHoldPoller(async (approvalId) => {
|
|
22545
|
+
const res = await client.getToolApproval(token, approvalId);
|
|
22546
|
+
const status = res?.approval?.status;
|
|
22547
|
+
return typeof status === "string" ? { status } : null;
|
|
22548
|
+
});
|
|
21503
22549
|
const agentBaseDir = path.join(workspacesRoot, task.workspaceId, task.agentId, "workdir");
|
|
21504
22550
|
const timelineDir = path.join(agentBaseDir, ".context_timeline").replace(/\\/g, "/");
|
|
21505
22551
|
mkdirSync7(timelineDir, { recursive: true });
|