opencode-discord-notify 0.1.1 → 0.2.0

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/README-JP.md CHANGED
@@ -62,6 +62,7 @@ OpenCode を再起動してください。
62
62
  - `DISCORD_WEBHOOK_COMPLETE_MENTION`: `session.idle` / `session.error` の通知本文に付けるメンション(`@everyone` または `@here` のみ許容。Forum webhook の仕様上、ping は常に発生しない)
63
63
  - `DISCORD_WEBHOOK_PERMISSION_MENTION`: `permission.updated` の通知本文に付けるメンション(`DISCORD_WEBHOOK_COMPLETE_MENTION` へのフォールバックなし。`@everyone` または `@here` のみ許容。Forum webhook の仕様上、ping は常に発生しない)
64
64
  - `DISCORD_WEBHOOK_EXCLUDE_INPUT_CONTEXT`: `1` のとき input context(`<file>` から始まる user `text` part)を通知しない(デフォルト: `1` / `0` で無効化)
65
+ - `SEND_PARAMS`: embed の fields として送るキーをカンマ区切りで指定。指定可能キー: `sessionID`, `permissionID`, `type`, `pattern`, `messageID`, `callID`, `partID`, `role`, `directory`, `projectID`。未設定・空文字・空要素のみの場合は全て選択。`session.created` は `SEND_PARAMS` に関わらず `sessionID`, `projectID`, `directory` を必ず含みます。
65
66
 
66
67
  ## 仕様メモ
67
68
 
@@ -81,8 +82,10 @@ OpenCode を再起動してください。
81
82
  - `message.updated` は通知しません(role 判定用に追跡。role 未確定で保留した `text` part を後から通知することがあります)。
82
83
  - `message.part.updated` は以下の方針です。
83
84
  - `text`: user は即時通知。assistant は `part.time.end` がある確定時のみ通知(ストリーミング途中更新は通知しない)
85
+ - embed タイトルは `User says` / `Agent says` です
84
86
  - `tool`: 通知しない
85
87
  - `reasoning`: 通知しない(内部思考が含まれる可能性があるため)
88
+ - `SEND_PARAMS` の制御対象は embed の fields のみです(title/description/content/timestamp などは対象外)。また `share` は fields としては送りません(Session started の embed URL には `shareUrl` を使います)。
86
89
 
87
90
  ## 動作確認(手動)
88
91
 
package/README.md CHANGED
@@ -64,6 +64,7 @@ Optional:
64
64
  - `DISCORD_WEBHOOK_COMPLETE_MENTION`: mention to put in `session.idle` / `session.error` messages (only `@everyone` or `@here` supported; Forum webhooks may not actually ping due to Discord behavior)
65
65
  - `DISCORD_WEBHOOK_PERMISSION_MENTION`: mention to put in `permission.updated` messages (no fallback to `DISCORD_WEBHOOK_COMPLETE_MENTION`; only `@everyone` or `@here` supported; Forum webhooks may not actually ping due to Discord behavior)
66
66
  - `DISCORD_WEBHOOK_EXCLUDE_INPUT_CONTEXT`: when set to `1`, exclude "input context" (user `text` parts that start with `<file>`) from notifications (default: `1`; set to `0` to disable)
67
+ - `SEND_PARAMS`: comma-separated list of keys to include as embed fields. Allowed keys: `sessionID`, `permissionID`, `type`, `pattern`, `messageID`, `callID`, `partID`, `role`, `directory`, `projectID`. If unset, empty, or containing only empty elements, all keys are selected. `session.created` always includes `sessionID`, `projectID`, `directory` regardless.
67
68
 
68
69
  ## Notes / behavior
69
70
 
@@ -83,8 +84,10 @@ Optional:
83
84
  - `message.updated` is not posted (tracked for role inference; may post a previously-held text part later).
84
85
  - `message.part.updated` policy:
85
86
  - `text`: user is posted immediately; assistant is posted only when finalized (when `part.time.end` exists)
87
+ - Embed titles are `User says` / `Agent says`
86
88
  - `tool`: not posted
87
89
  - `reasoning`: not posted (to avoid exposing internal thoughts)
90
+ - `SEND_PARAMS` controls embed fields only (it does not affect title/description/content/timestamp). `share` is not an embed field (but Session started uses `shareUrl` as the embed URL).
88
91
 
89
92
  ## Manual test
90
93
 
package/dist/index.js CHANGED
@@ -1,4 +1,17 @@
1
1
  // src/index.ts
2
+ var SEND_PARAM_KEYS = [
3
+ "sessionID",
4
+ "permissionID",
5
+ "type",
6
+ "pattern",
7
+ "messageID",
8
+ "callID",
9
+ "partID",
10
+ "role",
11
+ "directory",
12
+ "projectID"
13
+ ];
14
+ var SEND_PARAM_KEY_SET = new Set(SEND_PARAM_KEYS);
2
15
  var COLORS = {
3
16
  info: 5793266,
4
17
  success: 5763719,
@@ -32,6 +45,23 @@ function buildFields(fields, inline = false) {
32
45
  }
33
46
  return result.length ? result : void 0;
34
47
  }
48
+ function isSendParamKey(value) {
49
+ return SEND_PARAM_KEY_SET.has(value);
50
+ }
51
+ function filterSendFields(fields, allowed) {
52
+ return fields.filter(([name]) => {
53
+ if (!isSendParamKey(name)) return false;
54
+ return allowed.has(name);
55
+ });
56
+ }
57
+ function getTextPartEmbedTitle(role) {
58
+ return role === "user" ? "User says" : "Agent says";
59
+ }
60
+ function withForcedSendParams(base, forced) {
61
+ const next = new Set(base);
62
+ for (const key of forced) next.add(key);
63
+ return next;
64
+ }
35
65
  function getEnv(name) {
36
66
  try {
37
67
  return process.env[name];
@@ -39,6 +69,17 @@ function getEnv(name) {
39
69
  return void 0;
40
70
  }
41
71
  }
72
+ function parseSendParams(raw) {
73
+ if (raw === void 0) return new Set(SEND_PARAM_KEYS);
74
+ const tokens = raw.split(",").map((v) => v.trim()).filter(Boolean);
75
+ if (!tokens.length) return new Set(SEND_PARAM_KEYS);
76
+ const result = /* @__PURE__ */ new Set();
77
+ for (const token of tokens) {
78
+ if (!SEND_PARAM_KEY_SET.has(token)) continue;
79
+ result.add(token);
80
+ }
81
+ return result;
82
+ }
42
83
  function withQuery(url, params) {
43
84
  const u = new URL(url);
44
85
  for (const [k, v] of Object.entries(params)) {
@@ -97,6 +138,7 @@ var plugin = async () => {
97
138
  const permissionMention = permissionMentionRaw || void 0;
98
139
  const excludeInputContextRaw = (getEnv("DISCORD_WEBHOOK_EXCLUDE_INPUT_CONTEXT") ?? "1").trim();
99
140
  const excludeInputContext = excludeInputContextRaw !== "0";
141
+ const sendParams = parseSendParams(getEnv("SEND_PARAMS"));
100
142
  const sessionToThread = /* @__PURE__ */ new Map();
101
143
  const threadCreateInFlight = /* @__PURE__ */ new Map();
102
144
  const pendingPostsBySession = /* @__PURE__ */ new Map();
@@ -321,12 +363,19 @@ var plugin = async () => {
321
363
  color: COLORS.info,
322
364
  timestamp: createdAt,
323
365
  fields: buildFields(
324
- [
325
- ["sessionID", sessionID],
326
- ["projectID", projectID],
327
- ["directory", directory],
328
- ["share", shareUrl]
329
- ],
366
+ filterSendFields(
367
+ [
368
+ ["sessionID", sessionID],
369
+ ["projectID", projectID],
370
+ ["directory", directory],
371
+ ["share", shareUrl]
372
+ ],
373
+ withForcedSendParams(sendParams, [
374
+ "sessionID",
375
+ "projectID",
376
+ "directory"
377
+ ])
378
+ ),
330
379
  false
331
380
  )
332
381
  };
@@ -345,14 +394,17 @@ var plugin = async () => {
345
394
  color: COLORS.warning,
346
395
  timestamp: toIsoTimestamp(p?.time?.created),
347
396
  fields: buildFields(
348
- [
349
- ["sessionID", sessionID],
350
- ["permissionID", p?.id],
351
- ["type", p?.type],
352
- ["pattern", p?.pattern],
353
- ["messageID", p?.messageID],
354
- ["callID", p?.callID]
355
- ],
397
+ filterSendFields(
398
+ [
399
+ ["sessionID", sessionID],
400
+ ["permissionID", p?.id],
401
+ ["type", p?.type],
402
+ ["pattern", p?.pattern],
403
+ ["messageID", p?.messageID],
404
+ ["callID", p?.callID]
405
+ ],
406
+ sendParams
407
+ ),
356
408
  false
357
409
  )
358
410
  };
@@ -371,7 +423,10 @@ var plugin = async () => {
371
423
  const embed = {
372
424
  title: "Session completed",
373
425
  color: COLORS.success,
374
- fields: buildFields([["sessionID", sessionID]], false)
426
+ fields: buildFields(
427
+ filterSendFields([["sessionID", sessionID]], sendParams),
428
+ false
429
+ )
375
430
  };
376
431
  const mention = buildCompleteMention();
377
432
  enqueueToThread(sessionID, {
@@ -390,7 +445,10 @@ var plugin = async () => {
390
445
  title: "Session error",
391
446
  color: COLORS.error,
392
447
  description: errorStr ? errorStr.length > 4096 ? errorStr.slice(0, 4093) + "..." : errorStr : void 0,
393
- fields: buildFields([["sessionID", sessionID]], false)
448
+ fields: buildFields(
449
+ filterSendFields([["sessionID", sessionID]], sendParams),
450
+ false
451
+ )
394
452
  };
395
453
  if (!sessionID) return;
396
454
  const mention = buildCompleteMention();
@@ -409,7 +467,10 @@ var plugin = async () => {
409
467
  const embed = {
410
468
  title: "Todo updated",
411
469
  color: COLORS.info,
412
- fields: buildFields([["sessionID", sessionID]], false),
470
+ fields: buildFields(
471
+ filterSendFields([["sessionID", sessionID]], sendParams),
472
+ false
473
+ ),
413
474
  description: buildTodoChecklist(p?.todos)
414
475
  };
415
476
  enqueueToThread(sessionID, { embeds: [embed] });
@@ -450,15 +511,18 @@ var plugin = async () => {
450
511
  firstUserTextBySession.set(sessionID, normalized);
451
512
  }
452
513
  const embed = {
453
- title: role === "user" ? "Message part updated: text (user)" : "Message part updated: text (assistant)",
514
+ title: getTextPartEmbedTitle(role),
454
515
  color: COLORS.info,
455
516
  fields: buildFields(
456
- [
457
- ["sessionID", sessionID],
458
- ["messageID", messageID],
459
- ["partID", partID],
460
- ["role", role]
461
- ],
517
+ filterSendFields(
518
+ [
519
+ ["sessionID", sessionID],
520
+ ["messageID", messageID],
521
+ ["partID", partID],
522
+ ["role", role]
523
+ ],
524
+ sendParams
525
+ ),
462
526
  false
463
527
  ),
464
528
  description: truncateText(text || "(empty)", 4096)
@@ -507,15 +571,18 @@ var plugin = async () => {
507
571
  firstUserTextBySession.set(sessionID, normalized);
508
572
  }
509
573
  const embed = {
510
- title: role === "user" ? "Message part updated: text (user)" : "Message part updated: text (assistant)",
574
+ title: getTextPartEmbedTitle(role),
511
575
  color: COLORS.info,
512
576
  fields: buildFields(
513
- [
514
- ["sessionID", sessionID],
515
- ["messageID", messageID],
516
- ["partID", partID],
517
- ["role", role]
518
- ],
577
+ filterSendFields(
578
+ [
579
+ ["sessionID", sessionID],
580
+ ["messageID", messageID],
581
+ ["partID", partID],
582
+ ["role", role]
583
+ ],
584
+ sendParams
585
+ ),
519
586
  false
520
587
  ),
521
588
  description: truncateText(text || "(empty)", 4096)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-discord-notify",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "A plugin that posts OpenCode events to a Discord webhook.",
5
5
  "license": "MIT",
6
6
  "type": "module",