@phneakngar/cli 0.0.1 → 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.
@@ -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(["unread", "read", "archived", "sent", "pending", "sending", "failed", "ambiguous"])
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 handleControlRequest(proc, event, enqueueStdinWrite) {
18170
- const requestId = event.request_id;
18171
- if (!requestId)
18172
- return;
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(approval);
19169
+ enqueueStdinWrite(gated.line);
18200
19170
  } else {
18201
19171
  try {
18202
- proc.stdin?.write(approval + `
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 asRecord(value) {
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 = asRecord(runtimeConfig);
21087
- const headroom = asRecord(runtime?.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 = asRecord(headroom?.upstream);
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 });