feat(weekly-report): bundled cron-triggered Feishu weekly-report plugin#2
Merged
Conversation
Direct 'git fetch origin main:refs/heads/main' fails when main is the
currently checked-out branch ('refusing to fetch into branch X checked
out at Y'). Update remote-tracking ref via plain 'git fetch origin main'
and push origin/main directly.
Signed-off-by: Chiliang Yao <[email protected]>
Co-Authored-By: Claude Opus 4.7 <[email protected]>
…ort flow Cron-triggered TaskFlow extension that drafts a weekly report from the agent's own session transcript, sends a Feishu confirmation card, loops on user supplements, and writes the result into a configured Feishu doc using invisible HTML-comment sentinels for idempotent splicing. v2 adds an optional git-activity fact source: a clone-on-demand, author-filtered `git log` runner exposed as `fetch_git_activity`, so completed bullets are anchored in real commits. SSH-only by default; URL allowlist, name regex, argv-only invocation, hooks/submodules/file-protocol disabled, per-op and overall timeout caps, concurrency-bounded parallel ops, and offline tests with local bare-repo fixtures. Five tools: submit_weekly_report_draft, respond_to_weekly_report_card, splice_weekly_report_doc, finalize_weekly_report, fetch_git_activity. CAS- idempotent timeout sweeper for reminders + fail-on-expiry. Card-action trust guards (controllerId / sessionKey / status / weekKey match before any flow mutation). 84 colocated vitest cases. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…w JSON reply
The submit_weekly_report_draft tool was returning a Feishu card JSON in its
response and instructing the agent to "send it via the appropriate Feishu card
delivery mechanism". With no concrete tool named, the agent dumped the card
JSON as a plain-text reply — Feishu rendered it as a code block instead of an
interactive card.
Fix: the tool now returns previewMarkdown + question shape + explicit
instructions to call `feishu_ask_user_question` (the @larksuite/openclaw-lark
plugin's purpose-built tool for interactive card prompts). The user's selection
arrives as a synthetic message via that tool's existing callback machinery, so
respond_to_weekly_report_card no longer needs to parse a card-action envelope.
Tool surface changes:
- submit_weekly_report_draft: drops `card` from response, adds previewMarkdown,
questionHeader, confirmLabel, supplementLabel, and explicit two-step
instructions naming feishu_ask_user_question.
- respond_to_weekly_report_card: parameters changed from {metadataJson,
supplement, sessionKey} to {flowId, weekKey, action, supplement?, sessionKey}
— the agent constructs these directly from its memory + the user's reply,
not from a card envelope.
- prompt.ts: drafting prompt rewritten to make the
submit_weekly_report_draft → feishu_ask_user_question → respond_to_weekly_report_card
chain explicit, and to forbid raw-text replies of the preview.
The card.ts envelope codec and card-action-handler.ts envelope parser remain
for the future raw-card path but are no longer in the critical flow.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…ect tool description Two related doc fixes that bit a real deployment: 1. README "Cron entry" now has a callout block warning that `payload.kind: "agentTurn"` (the alternative to systemEvent) triggers the cron runner's hardcoded 60 s isolated-agent setup watchdog. On hosts with several plugins, setup routinely exceeds that budget and the job fails with "cron: isolated agent setup timed out before runner start" before our tool ever runs. Also calls out the `payload.text` vs `payload.message` field-name mismatch between systemEvent and agentTurn, which is the obvious migration trap. 2. submit_weekly_report_draft tool description was still saying "Returns the card JSON to send to the user" — left over from the pre-fix shape. The agent reads this description first and sets its mental model from it, then ignores conflicting instructions in the response body. Rewritten to spell out the actual return shape and instruct the agent to call `feishu_ask_user_question` for delivery. Also brought the README Tools section up to date: now lists all five tools (was four), includes `fetch_git_activity`, drops the stale "returns card JSON" / "card JSON is returned to the agent" sentences. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…ing fact source
Adds `fetch_recent_group_messages` as a sixth tool. Together with the existing
`getSessionMessages` (DM transcript) and `fetch_git_activity` (commits), the
drafting agent now sees the user's contributions across Feishu group chats —
own messages + @-mentions + threads they participated in.
Why: real weekly reports also capture group-chat status updates / decisions /
announcements, which the chat-history-only collector misses.
How:
- New `runGroupActivity` runner enumerates the agent's sessions via
`runtime.agent.session.listSessionEntries` (precedent: extensions/active-memory),
filters for `:feishu:group:`, drops stale (lastInteractionAt /
updatedAt > groupStaleAfterDays), drops denylisted (full sessionKey or bare
chatId), optionally collapses Feishu topic-group sub-sessions by groupId.
- Per group: `runtime.subagent.getSessionMessages` (cross-session reads are
unguarded server-side per src/gateway/server-methods/sessions.ts:2169-2200),
message-shape introspection probes common field names (the gateway returns
`unknown[]`), time-window + author/mention/thread-participation classify.
- Partial-trace drop: messages with thread metadata but no user thread root
match are dropped conservatively (avoids false positives).
- userOpenId auto-derived from recipientSessionKey's `:direct:<openid>` suffix
with `^ou_[A-Za-z0-9_-]{8,}$` validation; explicit override via config.
Unresolved userOpenId → single ok:false entry, drafting continues without
group data (does not throw at parse).
- botOpenId optional config to exclude bot self-messages; no auto-derivation
(appId != open_id; would require a Feishu contacts API call we don't make).
- Bounded parallelism (groupMaxParallelOps=5), per-group budget enforcement,
overall deadline (groupOverallTimeoutMs=60s) with budget_exhausted reason,
safety cap (groupMaxGroupsScanned=50).
- Drafting prompt rewritten to enumerate THREE parallel data sources with
failure-transparency rule applied to all three.
Tests: 119 (was 86). Offline group-activity coverage spans empty, filter,
denylist, staleness, author/mention/thread classification, time window,
budget_exhausted, per-group failure isolation, includeReasons subset,
topicGroups=exclude dedupe, threadFilterAvailable propagation, derivation
edge cases.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…erson contract - Replace destructive larkcli 'doc update --mode overwrite' (cleared the whole doc) with block-level insert at the document head via official lark-cli 'docs +update' (doc-writer.ts): insert this week's section at the top, replace only the same-week section, preserve everything else. Inline content (lark-cli rejects --content @file and --format on +update); no move step (block_insert_after --block-id <page_id> already lands at the head). - Add begin_weekly_report tool returning a first-person, facts-only drafting contract owned by the plugin (drafting-contract.ts), so the cron is a one-line trigger and behaviour is shareable. - Hard-gate respond_to_weekly_report_card out of the supplement re-draft sub-session so a revision can only submit via submit_weekly_report_draft (types.ts isWeeklyReportSupplementSession + index.ts). - Add docIdentity (user/bot) config for the official lark-cli doc ops; retire doc-splicer/sentinels and the host-owned prompt; README rewritten; .oxfmtrc ignores the golden markdown fixture. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…r writing After the section write, read the doc title (drive +inspect) and, if it carries an '(updated at MMDD)' / '(updated at MMDD)' marker (full- or half-width parens), patch it to today via 'drive files patch'. Opt-in by marker (titles without it are left untouched, safe for other deployments) and best-effort (a rename hiccup never fails the report — surfaced as a titleNote /⚠️ line in the DM reply). Adds formatMMDD / nextDocTitle / parseInspectTitle helpers + tests; WriteWeeklySectionResult gains an optional titleNote; now-injectable date for testing. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Feishu's card callback has a ~3s ack deadline; the doc write (fetch outline → delete same-week section → insert at head → bump title) routinely exceeds it, so the gateway logged error 200530 'callback timeout' even though the write succeeded. Ack the callback synchronously with an info toast and run the write/finish/fail/reply inside a detached task so the deadline is always met. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the bundled weekly-report plugin (
extensions/weekly-report/): a cron-triggered Feishu/Lark weekly-report flow. A scheduledsystemEventfiresbegin_weekly_report, the agent drafts a first-person report from three fact sources (session messages, git activity, recent group messages), delivers it as a CardKit form card, and on confirm writes it non-destructively to a Lark doc.Key pieces:
src/doc-writer.ts) — officiallark-cli docs +updateblock ops. Fetches the outline, replaces the same-ISO-week## <week_title>H2 section in place (or inserts at the document head on first run), and cleans up legacy sentinel comments. Replaces the old destructivemode:overwritepath that clobbered images/comments on every write.(updated at MMDD)marker to today's date.src/drafting-contract.ts) — ships a first-person ("我"/I), facts-not-chat-recap drafting contract so a colleague can install the plugin and add a one-line cron trigger without hand-authoring a prompt.begin_weekly_reportkickoff tool +contracts.toolsmanifest entry; CardKit form-card delivery (src/card-sender.ts); managed-flow timeout sweeper.Verification
node scripts/run-vitest.mjs extensions/weekly-report— green (149 tests).pnpm tsgo:extensions && pnpm tsgo:extensions:test— clean.dist/index.js) rebuilt and committed.Real behavior proof
instance-1n7ut600-25(10.0.3.84), deployed viaopenclaw plugins install --force+systemctl --user restart.0524→0530); after the detached-write change the gateway no longer logs 200530, and[gateway] http server listening (… weekly-report)/readywithNRestarts=0, all 6 crons intact.--as userdoc identity (this node runs--as botwith the doc shared to the bot).🤖 Generated with Claude Code